diff --git a/Applications/CoreApp/icons/CoreApp.rc b/Applications/CoreApp/icons/CoreApp.rc new file mode 100644 index 0000000000..bb10963fb3 --- /dev/null +++ b/Applications/CoreApp/icons/CoreApp.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icon.ico" \ No newline at end of file diff --git a/Applications/CoreApp/icons/icon.icns b/Applications/CoreApp/icons/icon.icns new file mode 100644 index 0000000000..5afc942282 Binary files /dev/null and b/Applications/CoreApp/icons/icon.icns differ diff --git a/Applications/CoreApp/icons/icon.ico b/Applications/CoreApp/icons/icon.ico new file mode 100644 index 0000000000..27d4c9d1b9 Binary files /dev/null and b/Applications/CoreApp/icons/icon.ico differ diff --git a/Applications/CoreApp/icons/icon.png b/Applications/CoreApp/icons/icon.png new file mode 100644 index 0000000000..857e61aef3 Binary files /dev/null and b/Applications/CoreApp/icons/icon.png differ diff --git a/Applications/ExtApp/icons/ExtApp.rc b/Applications/ExtApp/icons/ExtApp.rc new file mode 100644 index 0000000000..bb10963fb3 --- /dev/null +++ b/Applications/ExtApp/icons/ExtApp.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icon.ico" \ No newline at end of file diff --git a/Applications/ExtApp/icons/icon.icns b/Applications/ExtApp/icons/icon.icns new file mode 100644 index 0000000000..5afc942282 Binary files /dev/null and b/Applications/ExtApp/icons/icon.icns differ diff --git a/Applications/ExtApp/icons/icon.ico b/Applications/ExtApp/icons/icon.ico new file mode 100644 index 0000000000..27d4c9d1b9 Binary files /dev/null and b/Applications/ExtApp/icons/icon.ico differ diff --git a/Applications/ExtApp/icons/icon.png b/Applications/ExtApp/icons/icon.png new file mode 100644 index 0000000000..857e61aef3 Binary files /dev/null and b/Applications/ExtApp/icons/icon.png differ diff --git a/Applications/mitkDiffusion/CMakeLists.txt b/Applications/mitkDiffusion/CMakeLists.txt index 4a0a2467a4..8e414314ab 100644 --- a/Applications/mitkDiffusion/CMakeLists.txt +++ b/Applications/mitkDiffusion/CMakeLists.txt @@ -1,77 +1,74 @@ project(mitkDiffusion) set(DIFFUSIONAPP_NAME mitkDiffusion) set(_app_options) if(MITK_SHOW_CONSOLE_WINDOW) list(APPEND _app_options SHOW_CONSOLE) endif() MITK_USE_MODULE(qtsingleapplication) include_directories(${ALL_INCLUDE_DIRECTORIES}) # Create a cache entry for the provisioning file which is used to export # the file name in the MITKConfig.cmake file. This will keep external projects # which rely on this file happy. set(DIFFUSIONIMAGINGAPP_PROVISIONING_FILE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${DIFFUSIONAPP_NAME}.provisioning" CACHE INTERNAL "${DIFFUSIONAPP_NAME} provisioning file" FORCE) set(_plugins org.commontk.configadmin org.commontk.eventadmin org.blueberry.osgi org.blueberry.compat org.blueberry.core.runtime org.blueberry.core.expressions org.blueberry.solstice.common org.blueberry.core.commands - org.blueberry.core.jobs org.blueberry.ui org.blueberry.ui.qt org.blueberry.ui.qt.help - org.blueberry.ui.qt.log - org.blueberry.ui.qt.objectinspector org.mitk.core.services org.mitk.gui.common org.mitk.planarfigure org.mitk.core.ext org.mitk.diffusionimaging org.mitk.gui.qt.application org.mitk.gui.qt.ext org.mitk.gui.qt.diffusionimagingapp org.mitk.gui.qt.common org.mitk.gui.qt.stdmultiwidgeteditor org.mitk.gui.qt.common.legacy org.mitk.gui.qt.datamanager org.mitk.gui.qt.measurementtoolbox org.mitk.gui.qt.segmentation org.mitk.gui.qt.volumevisualization org.mitk.gui.qt.diffusionimaging org.mitk.gui.qt.imagenavigator org.mitk.gui.qt.moviemaker ) # Plug-ins listed below will not be # - added as a build-time dependency to the executable # - listed in the provisioning file for the executable # - installed if they are external plug-ins set(_exclude_plugins org.blueberry.test org.blueberry.uitest org.mitk.gui.qt.coreapplication org.mitk.gui.qt.extapplication ) FunctionCreateBlueBerryApplication( NAME ${DIFFUSIONAPP_NAME} DESCRIPTION "MITK Diffusion" PLUGINS ${_plugins} EXCLUDE_PLUGINS ${_exclude_plugins} LINK_LIBRARIES ${ALL_LIBRARIES} ${_app_options} ) # Add a build time dependency to legacy BlueBerry bundles. if(MITK_MODULES_ENABLED_PLUGINS) add_dependencies(${DIFFUSIONAPP_NAME} ${MITK_MODULES_ENABLED_PLUGINS}) endif() \ No newline at end of file diff --git a/Applications/mitkDiffusion/icons/icon.icns b/Applications/mitkDiffusion/icons/icon.icns new file mode 100644 index 0000000000..a113602951 Binary files /dev/null and b/Applications/mitkDiffusion/icons/icon.icns differ diff --git a/Applications/mitkDiffusion/icons/icon.ico b/Applications/mitkDiffusion/icons/icon.ico new file mode 100644 index 0000000000..77a2f03ee8 Binary files /dev/null and b/Applications/mitkDiffusion/icons/icon.ico differ diff --git a/Applications/mitkDiffusion/icons/icon.png b/Applications/mitkDiffusion/icons/icon.png new file mode 100644 index 0000000000..48a79b5bfc Binary files /dev/null and b/Applications/mitkDiffusion/icons/icon.png differ diff --git a/Applications/mitkDiffusion/icons/mitkDiffusion.rc b/Applications/mitkDiffusion/icons/mitkDiffusion.rc new file mode 100644 index 0000000000..bb10963fb3 --- /dev/null +++ b/Applications/mitkDiffusion/icons/mitkDiffusion.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icon.ico" \ No newline at end of file diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.help/src/internal/berryHelpWebView.cpp b/BlueBerry/Bundles/org.blueberry.ui.qt.help/src/internal/berryHelpWebView.cpp index 93925c4e44..b8d7995cbf 100644 --- a/BlueBerry/Bundles/org.blueberry.ui.qt.help/src/internal/berryHelpWebView.cpp +++ b/BlueBerry/Bundles/org.blueberry.ui.qt.help/src/internal/berryHelpWebView.cpp @@ -1,520 +1,528 @@ /*========================================================================= Program: BlueBerry Platform Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "berryHelpWebView.h" #include "berryHelpPluginActivator.h" #include "berryHelpEditor.h" #include "berryHelpEditorInput.h" #include "berryQHelpEngineWrapper.h" #include #include #include #include #include #include #include #include #include #include #include namespace berry { struct ExtensionMap { const char *extension; const char *mimeType; } extensionMap[] = { { ".bmp", "image/bmp" }, { ".css", "text/css" }, { ".gif", "image/gif" }, { ".html", "text/html" }, { ".htm", "text/html" }, { ".ico", "image/x-icon" }, { ".jpeg", "image/jpeg" }, { ".jpg", "image/jpeg" }, { ".js", "application/x-javascript" }, { ".mng", "video/x-mng" }, { ".pbm", "image/x-portable-bitmap" }, { ".pgm", "image/x-portable-graymap" }, { ".pdf", "application/pdf" }, { ".png", "image/png" }, { ".ppm", "image/x-portable-pixmap" }, { ".rss", "application/rss+xml" }, { ".svg", "image/svg+xml" }, { ".svgz", "image/svg+xml" }, { ".text", "text/plain" }, { ".tif", "image/tiff" }, { ".tiff", "image/tiff" }, { ".txt", "text/plain" }, { ".xbm", "image/x-xbitmap" }, { ".xml", "text/xml" }, { ".xpm", "image/x-xpm" }, { ".xsl", "text/xsl" }, { ".xhtml", "application/xhtml+xml" }, { ".wml", "text/vnd.wap.wml" }, { ".wmlc", "application/vnd.wap.wmlc" }, { "about:blank", 0 }, { 0, 0 } }; const QString HelpWebView::m_PageNotFoundMessage = QCoreApplication::translate("org.blueberry.ui.qt.help", "Error 404...


The page could not be found


'%1'" "

"); class HelpNetworkReply : public QNetworkReply { public: HelpNetworkReply(const QNetworkRequest &request, const QByteArray &fileData, const QString &mimeType); virtual void abort(); virtual qint64 bytesAvailable() const { return data.length() + QNetworkReply::bytesAvailable(); } protected: virtual qint64 readData(char *data, qint64 maxlen); private: QByteArray data; qint64 origLen; }; HelpNetworkReply::HelpNetworkReply(const QNetworkRequest &request, const QByteArray &fileData, const QString& mimeType) : data(fileData), origLen(fileData.length()) { setRequest(request); setOpenMode(QIODevice::ReadOnly); setHeader(QNetworkRequest::ContentTypeHeader, mimeType); setHeader(QNetworkRequest::ContentLengthHeader, QByteArray::number(origLen)); QTimer::singleShot(0, this, SIGNAL(metaDataChanged())); QTimer::singleShot(0, this, SIGNAL(readyRead())); } void HelpNetworkReply::abort() { } qint64 HelpNetworkReply::readData(char *buffer, qint64 maxlen) { qint64 len = qMin(qint64(data.length()), maxlen); if (len) { memcpy(buffer, data.constData(), len); data.remove(0, len); } if (!data.length()) QTimer::singleShot(0, this, SIGNAL(finished())); return len; } class HelpNetworkAccessManager : public QNetworkAccessManager { public: HelpNetworkAccessManager(QObject *parent); protected: virtual QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData = 0); }; HelpNetworkAccessManager::HelpNetworkAccessManager(QObject *parent) : QNetworkAccessManager(parent) { } QNetworkReply *HelpNetworkAccessManager::createRequest(Operation /*op*/, const QNetworkRequest &request, QIODevice* /*outgoingData*/) { QString url = request.url().toString(); QHelpEngine& helpEngine = HelpPluginActivator::getInstance()->getQHelpEngine(); // TODO: For some reason the url to load is already wrong (passed from webkit) // though the css file and the references inside should work that way. One // possible problem might be that the css is loaded at the same level as the // html, thus a path inside the css like (../images/foo.png) might cd out of // the virtual folder // if (!helpEngine.findFile(url).isValid()) { // if (url.startsWith(AbstractHelpWebView::DocPath)) { // QUrl newUrl = request.url(); // if (!newUrl.path().startsWith(QLatin1String("/qdoc/"))) { // newUrl.setPath(QLatin1String("qdoc") + newUrl.path()); // url = newUrl.toString(); // } // } // } const QString &mimeType = HelpWebView::mimeFromUrl(url); const QByteArray &data = helpEngine.findFile(url).isValid() ? helpEngine.fileData(url) : HelpWebView::m_PageNotFoundMessage.arg(url).toUtf8(); return new HelpNetworkReply(request, data, mimeType.isEmpty() ? QLatin1String("application/octet-stream") : mimeType); } class HelpPage : public QWebPage { public: HelpPage(IEditorSite::Pointer editorSite, QObject *parent); protected: virtual QWebPage *createWindow(QWebPage::WebWindowType); virtual void triggerAction(WebAction action, bool checked = false); virtual bool acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type); private: IEditorSite::Pointer m_EditorSite; bool m_CloseNewTabIfNeeded; friend class HelpWebView; QUrl m_loadingUrl; Qt::MouseButtons m_pressedButtons; Qt::KeyboardModifiers m_keyboardModifiers; }; HelpPage::HelpPage(IEditorSite::Pointer editorSite, QObject *parent) : QWebPage(parent) , m_EditorSite(editorSite) , m_CloseNewTabIfNeeded(false) , m_pressedButtons(Qt::NoButton) , m_keyboardModifiers(Qt::NoModifier) { } QWebPage *HelpPage::createWindow(QWebPage::WebWindowType type) { IEditorInput::Pointer input(new HelpEditorInput(QUrl())); IEditorPart::Pointer editorPart = m_EditorSite->GetPage()->OpenEditor(input, HelpEditor::EDITOR_ID); HelpEditor::Pointer helpEditor = editorPart.Cast(); HelpPage* newPage = static_cast(helpEditor->GetQWebPage()); if (newPage) newPage->m_CloseNewTabIfNeeded = m_CloseNewTabIfNeeded; m_CloseNewTabIfNeeded = false; return newPage; } void HelpPage::triggerAction(WebAction action, bool checked) { switch (action) { case OpenLinkInNewWindow: m_CloseNewTabIfNeeded = true; default: // fall through QWebPage::triggerAction(action, checked); break; } } bool HelpPage::acceptNavigationRequest(QWebFrame *, const QNetworkRequest &request, QWebPage::NavigationType type) { const bool closeNewTab = m_CloseNewTabIfNeeded; m_CloseNewTabIfNeeded = false; + // open in an external browser if a http link + const QUrl &url = request.url(); + if (url.scheme() == QLatin1String("http")) + { + QDesktopServices::openUrl(url); + return false; + } + // const QUrl &url = request.url(); // if (AbstractHelpWebView::launchWithExternalApp(url)) // { // if (closeNewTab) // QMetaObject::invokeMethod(centralWidget, "closeTab"); // return false; // } // if (type == QWebPage::NavigationTypeLinkClicked // && (m_keyboardModifiers & Qt::ControlModifier // || m_pressedButtons == Qt::MidButton)) // { // if (centralWidget->newEmptyTab()) // centralWidget->setSource(url); // m_pressedButtons = Qt::NoButton; // m_keyboardModifiers = Qt::NoModifier; // return false; // } // m_loadingUrl = url; // because of async page loading, we will hit some kind // of race condition while using a remote command, like a combination of // SetSource; SyncContent. SetSource would be called and SyncContents shortly // afterwards, but the page might not have finished loading and the old url // would be returned. return true; } // -- HelpWebView HelpWebView::HelpWebView(IEditorSite::Pointer editorSite, QWidget *parent, qreal zoom) : QWebView(parent) //, parentWidget(parent) , m_LoadFinished(false) , m_HelpEngine(HelpPluginActivator::getInstance()->getQHelpEngine()) { setAcceptDrops(false); setPage(new HelpPage(editorSite, parent)); page()->setNetworkAccessManager(new HelpNetworkAccessManager(this)); QAction* action = pageAction(QWebPage::OpenLinkInNewWindow); action->setText(tr("Open Link in New Tab")); if (!parent) action->setVisible(false); pageAction(QWebPage::DownloadLinkToDisk)->setVisible(false); pageAction(QWebPage::DownloadImageToDisk)->setVisible(false); pageAction(QWebPage::OpenImageInNewWindow)->setVisible(false); connect(pageAction(QWebPage::Copy), SIGNAL(changed()), this, SLOT(actionChanged())); connect(pageAction(QWebPage::Back), SIGNAL(changed()), this, SLOT(actionChanged())); connect(pageAction(QWebPage::Forward), SIGNAL(changed()), this, SLOT(actionChanged())); connect(page(), SIGNAL(linkHovered(QString,QString,QString)), this, SIGNAL(highlighted(QString))); connect(this, SIGNAL(urlChanged(QUrl)), this, SIGNAL(sourceChanged(QUrl))); connect(this, SIGNAL(loadStarted()), this, SLOT(setLoadStarted())); connect(this, SIGNAL(loadFinished(bool)), this, SLOT(setLoadFinished(bool))); connect(page(), SIGNAL(printRequested(QWebFrame*)), this, SIGNAL(printRequested())); setFont(viewerFont()); setTextSizeMultiplier(zoom == 0.0 ? 1.0 : zoom); } HelpWebView::~HelpWebView() { } QFont HelpWebView::viewerFont() const { //if (m_HelpEngine.usesBrowserFont()) // return m_HelpEngine.browserFont(); QWebSettings *webSettings = QWebSettings::globalSettings(); return QFont(webSettings->fontFamily(QWebSettings::StandardFont), webSettings->fontSize(QWebSettings::DefaultFontSize)); } void HelpWebView::setViewerFont(const QFont &font) { QWebSettings *webSettings = settings(); webSettings->setFontFamily(QWebSettings::StandardFont, font.family()); webSettings->setFontSize(QWebSettings::DefaultFontSize, font.pointSize()); } void HelpWebView::scaleUp() { setTextSizeMultiplier(textSizeMultiplier() + 0.1); } void HelpWebView::scaleDown() { setTextSizeMultiplier(qMax(0.0, textSizeMultiplier() - 0.1)); } void HelpWebView::resetScale() { setTextSizeMultiplier(1.0); } bool HelpWebView::handleForwardBackwardMouseButtons(QMouseEvent *e) { if (e->button() == Qt::XButton1) { triggerPageAction(QWebPage::Back); return true; } if (e->button() == Qt::XButton2) { triggerPageAction(QWebPage::Forward); return true; } return false; } QUrl HelpWebView::source() const { HelpPage *currentPage = static_cast (page()); if (currentPage && !hasLoadFinished()) { // see HelpPage::acceptNavigationRequest(...) return currentPage->m_loadingUrl; } return url(); } void HelpWebView::setSource(const QUrl &url) { if (m_HelpEngine.findFile(url).isValid()) load(url); else setHtml(m_PageNotFoundMessage.arg(url.toString())); } void HelpWebView::wheelEvent(QWheelEvent *e) { if (e->modifiers()& Qt::ControlModifier) { e->accept(); e->delta() > 0 ? scaleUp() : scaleDown(); } else { QWebView::wheelEvent(e); } } void HelpWebView::mouseReleaseEvent(QMouseEvent *e) { #ifndef Q_OS_LINUX if (handleForwardBackwardMouseButtons(e)) return; #endif QWebView::mouseReleaseEvent(e); } void HelpWebView::actionChanged() { QAction *a = qobject_cast(sender()); if (a == pageAction(QWebPage::Copy)) emit copyAvailable(a->isEnabled()); else if (a == pageAction(QWebPage::Back)) emit backwardAvailable(a->isEnabled()); else if (a == pageAction(QWebPage::Forward)) emit forwardAvailable(a->isEnabled()); } void HelpWebView::mousePressEvent(QMouseEvent *event) { #ifdef Q_OS_LINUX if (handleForwardBackwardMouseButtons(event)) return; #endif HelpPage *currentPage = static_cast(page()); if (currentPage) { currentPage->m_pressedButtons = event->buttons(); currentPage->m_keyboardModifiers = event->modifiers(); } QWebView::mousePressEvent(event); } void HelpWebView::setLoadStarted() { m_LoadFinished = false; } void HelpWebView::setLoadFinished(bool ok) { m_LoadFinished = ok; emit sourceChanged(url()); } QString HelpWebView::mimeFromUrl(const QUrl &url) { const QString &path = url.path(); const int index = path.lastIndexOf(QLatin1Char('.')); const QByteArray &ext = path.mid(index).toUtf8().toLower(); const ExtensionMap *e = extensionMap; while (e->extension) { if (ext == e->extension) return QLatin1String(e->mimeType); ++e; } return QLatin1String(""); } bool HelpWebView::canOpenPage(const QString &url) { return !mimeFromUrl(url).isEmpty(); } bool HelpWebView::isLocalUrl(const QUrl &url) { const QString &scheme = url.scheme(); return scheme.isEmpty() || scheme == QLatin1String("file") || scheme == QLatin1String("qrc") || scheme == QLatin1String("data") || scheme == QLatin1String("qthelp") || scheme == QLatin1String("about"); } bool HelpWebView::launchWithExternalApp(const QUrl &url) { if (isLocalUrl(url)) { const QHelpEngine& helpEngine = HelpPluginActivator::getInstance()->getQHelpEngine(); const QUrl &resolvedUrl = helpEngine.findFile(url); if (!resolvedUrl.isValid()) return false; const QString& path = resolvedUrl.path(); if (!canOpenPage(path)) { QTemporaryFile tmpTmpFile; if (!tmpTmpFile.open()) return false; const QString &extension = QFileInfo(path).completeSuffix(); QFile actualTmpFile(tmpTmpFile.fileName() % QLatin1String(".") % extension); if (!actualTmpFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) return false; actualTmpFile.write(helpEngine.fileData(resolvedUrl)); actualTmpFile.close(); return QDesktopServices::openUrl(QUrl(actualTmpFile.fileName())); } } else if (url.scheme() == QLatin1String("http")) { return QDesktopServices::openUrl(url); } return false; } void HelpWebView::home() { setSource(m_HelpEngine.homePage()); } } diff --git a/BlueBerry/CMake/FunctionCreateBlueBerryApplication.cmake b/BlueBerry/CMake/FunctionCreateBlueBerryApplication.cmake index 8759656417..a906a89e8a 100644 --- a/BlueBerry/CMake/FunctionCreateBlueBerryApplication.cmake +++ b/BlueBerry/CMake/FunctionCreateBlueBerryApplication.cmake @@ -1,167 +1,188 @@ #! #! Create a BlueBerry application. #! #! \brif This function will create a BlueBerry application together with all #! necessary provisioning and configuration data and install support. #! #! \param NAME (required) The name of the executable. #! \param DESCRIPTION (optional) A human-readable description of your application. #! The usage depends on the CPack generator (on Windows, this is a descriptive #! text for the created shortcuts). #! \param SOURCES (optional) A list of source files to compile into your executable. Defaults #! to .cpp. #! \param PLUGINS (optional) A list of required plug-ins. Defaults to all known plug-ins. #! \param EXCLUDE_PLUGINS (optional) A list of plug-ins which should not be used. Mainly #! useful if PLUGINS was not used. #! \param LINK_LIBRARIES A list of libraries to be linked with the executable. #! \param SHOW_CONSOLE (option) Show the console output window (on Windows). #! \param NO_PROVISIONING (option) Do not create provisioning files. #! #! Assuming that there exists a file called MyApp.cpp, an example call looks like: #! \code #! FunctionCreateBlueBerryApplication( #! NAME MyApp #! DESCRIPTION "MyApp - New ways to explore medical data" #! EXCLUDE_PLUGINS org.mitk.gui.qt.extapplication #! SHOW_CONSOLE #! ) #! \endcode #! function(FunctionCreateBlueBerryApplication) macro_parse_arguments(_APP "NAME;DESCRIPTION;SOURCES;PLUGINS;EXCLUDE_PLUGINS;LINK_LIBRARIES" "SHOW_CONSOLE;NO_PROVISIONING" ${ARGN}) if(NOT _APP_NAME) message(FATAL_ERROR "NAME argument cannot be empty.") endif() if(NOT _APP_SOURCES) set(_APP_SOURCES ${_APP_NAME}.cpp) endif() if(NOT _APP_PLUGINS) ctkFunctionGetAllPluginTargets(_APP_PLUGINS) else() set(_plugins ${_APP_PLUGINS}) set(_APP_PLUGINS) foreach(_plugin ${_plugins}) string(REPLACE "." "_" _plugin_target ${_plugin}) list(APPEND _APP_PLUGINS ${_plugin_target}) endforeach() # get all plug-in dependencies ctkFunctionGetPluginDependencies(_plugin_deps PLUGINS ${_APP_PLUGINS} ALL) # add the dependencies to the list of application plug-ins list(APPEND _APP_PLUGINS ${_plugin_deps}) endif() #------------------------------------------------------------------------ # Prerequesites #------------------------------------------------------------------------ find_package(MITK REQUIRED) # ----------------------------------------------------------------------- # Set up include and link dirs for the executable # ----------------------------------------------------------------------- include(${QT_USE_FILE}) include_directories( ${org_blueberry_osgi_INCLUDE_DIRS} ${Poco_INCLUDE_DIRS} ${mbilog_INCLUDE_DIRS} ) link_directories(${MITK_LINK_DIRECTORIES}) +# ----------------------------------------------------------------------- +# Add executable icon (Windows) +# ----------------------------------------------------------------------- +set(WINDOWS_ICON_RESOURCE_FILE "") +if(WIN32) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/icons/${_APP_NAME}.rc") + set(WINDOWS_ICON_RESOURCE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/icons/${_APP_NAME}.rc") + endif() +endif() + # ----------------------------------------------------------------------- # Create the executable and link libraries # ----------------------------------------------------------------------- if(_APP_SHOW_CONSOLE) - add_executable(${_APP_NAME} MACOSX_BUNDLE ${_APP_SOURCES}) + add_executable(${_APP_NAME} MACOSX_BUNDLE ${_APP_SOURCES} ${WINDOWS_ICON_RESOURCE_FILE}) else() - add_executable(${_APP_NAME} MACOSX_BUNDLE WIN32 ${_APP_SOURCES}) + add_executable(${_APP_NAME} MACOSX_BUNDLE WIN32 ${_APP_SOURCES} ${WINDOWS_ICON_RESOURCE_FILE}) endif() target_link_libraries(${_APP_NAME} org_blueberry_osgi ${_APP_LINK_LIBRARIES}) if(WIN32) target_link_libraries(${_APP_NAME} ${QT_QTCORE_LIBRARY} ${QT_QTMAIN_LIBRARY}) endif() +# ----------------------------------------------------------------------- +# Add executable icon (Mac) +# ----------------------------------------------------------------------- +if(APPLE) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/icons/icon.icns") + set_target_properties(${_APP_NAME} PROPERTIES MACOSX_BUNDLE_ICON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/icons/icon.icns") + file(COPY ${MACOSX_BUNDLE_ICON_FILE} DESTINATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${_APP_NAME}.app/Contents/Resources/") + file(INSTALL ${MACOSX_BUNDLE_ICON_FILE} DESTINATION "${_APP_NAME}.app/Contents/Resources/") + endif() +endif() + # ----------------------------------------------------------------------- # Set build time dependencies # ----------------------------------------------------------------------- # This ensures that all enabled plug-ins are up-to-date when the # executable is build. if(_APP_PLUGINS) ctkMacroGetAllProjectTargetLibraries("${_APP_PLUGINS}" _project_plugins) if(_APP_EXCLUDE_PLUGINS) set(_exclude_targets) foreach(_exclude_plugin ${_APP_EXCLUDE_PLUGINS}) string(REPLACE "." "_" _exclude_target ${_exclude_plugin}) list(APPEND _exclude_targets ${_exclude_target}) endforeach() list(REMOVE_ITEM _project_plugins ${_exclude_targets}) endif() if(_project_plugins) add_dependencies(${_APP_NAME} ${_project_plugins}) endif() endif() # ----------------------------------------------------------------------- # Additional files needed for the executable # ----------------------------------------------------------------------- if(NOT _APP_NO_PROVISIONING) # Create a provisioning file, listing all plug-ins set(_prov_file "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${_APP_NAME}.provisioning") FunctionCreateProvisioningFile(FILE ${_prov_file} PLUGINS ${_APP_PLUGINS} EXCLUDE_PLUGINS ${_APP_EXCLUDE_PLUGINS} ) endif() # Create a .ini file for initial parameters if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_APP_NAME}.ini") configure_file(${_APP_NAME}.ini ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${_APP_NAME}.ini) endif() # Create batch files for Windows platforms if(WIN32) foreach(BUILD_TYPE debug release) mitkFunctionCreateWindowsBatchScript(start${_APP_NAME}.bat.in ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/start${_APP_NAME}_${BUILD_TYPE}.bat ${BUILD_TYPE}) endforeach() endif(WIN32) # ----------------------------------------------------------------------- # Install support # ----------------------------------------------------------------------- # This installs all third-party CTK plug-ins FunctionInstallThirdPartyCTKPlugins(${_APP_PLUGINS} EXCLUDE ${_APP_EXCLUDE_PLUGINS}) # Install the executable MITK_INSTALL_TARGETS(EXECUTABLES ${_APP_NAME} GLOB_PLUGINS ) if(NOT _APP_NO_PROVISIONING) # Install the provisioning file mitkFunctionInstallProvisioningFiles(${_prov_file}) endif() # On Linux, create a shell script to start a relocatable application if(UNIX AND NOT APPLE) install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME ${_APP_NAME}.sh) endif() # Tell cpack the executables that you want in the start menu as links set(MITK_CPACK_PACKAGE_EXECUTABLES ${MITK_CPACK_PACKAGE_EXECUTABLES} "${_APP_NAME};${_APP_DESCRIPTION}" CACHE INTERNAL "Collecting windows shortcuts to executables") endfunction() diff --git a/CMake/RunInstalledApp.sh b/CMake/RunInstalledApp.sh index 36f721e68f..b859e80340 100644 --- a/CMake/RunInstalledApp.sh +++ b/CMake/RunInstalledApp.sh @@ -1,6 +1,6 @@ #!/bin/sh -binpath=$(dirname "$(readlink -f $0)") -appname=$(basename $0 .sh) +binpath=$(dirname "$(readlink -f "$0")") +appname=$(basename "$0" .sh) export LD_LIBRARY_PATH="$binpath/bin":"$binpath/bin/plugins":$LD_LIBRARY_PATH cd "$binpath/bin" ./$appname $* diff --git a/CMakeExternals/CTK.cmake b/CMakeExternals/CTK.cmake index 66f047558c..2c4faee1de 100644 --- a/CMakeExternals/CTK.cmake +++ b/CMakeExternals/CTK.cmake @@ -1,67 +1,79 @@ #----------------------------------------------------------------------------- # CTK #----------------------------------------------------------------------------- if(MITK_USE_CTK) # Sanity checks if(DEFINED CTK_DIR AND NOT EXISTS ${CTK_DIR}) message(FATAL_ERROR "CTK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj CTK) set(proj_DEPENDENCIES ) set(CTK_DEPENDS ${proj}) if(NOT DEFINED CTK_DIR) - set(revision_tag 6925794b) - #if(${proj}_REVISION_TAG) - # set(revision_tag ${${proj}_REVISION_TAG}) - #endif() + set(revision_tag 70c0a8d3) + #IF(${proj}_REVISION_TAG) + # SET(revision_tag ${${proj}_REVISION_TAG}) + #ENDIF() set(ctk_optional_cache_args ) if(MITK_USE_Python) list(APPEND ctk_optional_cache_args -DCTK_LIB_Scripting/Python/Widgets:BOOL=ON ) endif() - foreach(type RUNTIME ARCHIVE LIBRARY) - if(DEFINED CTK_PLUGIN_${type}_OUTPUT_DIRECTORY) - list(APPEND mitk_optional_cache_args -DCTK_PLUGIN_${type}_OUTPUT_DIRECTORY:PATH=${CTK_PLUGIN_${type}_OUTPUT_DIRECTORY}) - endif() - endforeach() + + if(MITK_USE_DCMTK) + list(APPEND ctk_optional_cache_args + -DDCMTK_DIR:PATH=${DCMTK_DIR} + ) + list(APPEND proj_DEPENDENCIES DCMTK) + else() + list(APPEND ctk_optional_cache_args + -DDCMTK_URL:STRING=${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/CTK_DCMTK_085525e6.tar.gz + ) + endif() + + FOREACH(type RUNTIME ARCHIVE LIBRARY) + IF(DEFINED CTK_PLUGIN_${type}_OUTPUT_DIRECTORY) + LIST(APPEND mitk_optional_cache_args -DCTK_PLUGIN_${type}_OUTPUT_DIRECTORY:PATH=${CTK_PLUGIN_${type}_OUTPUT_DIRECTORY}) + ENDIF() + ENDFOREACH() ExternalProject_Add(${proj} SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj}-src BINARY_DIR ${proj}-build PREFIX ${proj}-cmake URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/CTK_${revision_tag}.tar.gz - URL_MD5 43430cee2dfec2519cbe33cbfebc3eaf + URL_MD5 a3130b2c3e7a1d320740938f61b65840 UPDATE_COMMAND "" INSTALL_COMMAND "" CMAKE_GENERATOR ${gen} CMAKE_ARGS ${ep_common_args} ${ctk_optional_cache_args} -DDESIRED_QT_VERSION:STRING=4 -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE} -DGit_EXECUTABLE:FILEPATH=${GIT_EXECUTABLE} -DGIT_EXECUTABLE:FILEPATH=${GIT_EXECUTABLE} -DCTK_LIB_PluginFramework:BOOL=ON -DCTK_LIB_DICOM/Widgets:BOOL=ON -DCTK_PLUGIN_org.commontk.eventadmin:BOOL=ON -DCTK_PLUGIN_org.commontk.configadmin:BOOL=ON -DCTK_USE_GIT_PROTOCOL:BOOL=OFF -DDCMTK_URL:STRING=${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/CTK_DCMTK_085525e6.tar.gz DEPENDS ${proj_DEPENDENCIES} ) set(CTK_DIR ${CMAKE_CURRENT_BINARY_DIR}/${proj}-build) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/DCMTK.cmake b/CMakeExternals/DCMTK.cmake index 7673a2066e..79661aa4c2 100644 --- a/CMakeExternals/DCMTK.cmake +++ b/CMakeExternals/DCMTK.cmake @@ -1,56 +1,56 @@ #----------------------------------------------------------------------------- # DCMTK #----------------------------------------------------------------------------- if(MITK_USE_DCMTK) # Sanity checks if(DEFINED DCMTK_DIR AND NOT EXISTS ${DCMTK_DIR}) message(FATAL_ERROR "DCMTK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj DCMTK) set(proj_DEPENDENCIES ) set(DCMTK_DEPENDS ${proj}) if(NOT DEFINED DCMTK_DIR) if(UNIX) set(DCMTK_CXX_FLAGS "-fPIC") set(DCMTK_C_FLAGS "-fPIC") endif(UNIX) if(DCMTK_DICOM_ROOT_ID) set(DCMTK_CXX_FLAGS "${DCMTK_CXX_FLAGS} -DSITE_UID_ROOT=\\\"${DCMTK_DICOM_ROOT_ID}\\\"") set(DCMTK_C_FLAGS "${DCMTK_CXX_FLAGS} -DSITE_UID_ROOT=\\\"${DCMTK_DICOM_ROOT_ID}\\\"") endif() ExternalProject_Add(${proj} SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj}-src BINARY_DIR ${proj}-build PREFIX ${proj}-cmake - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/dcmtk-3.6.0.tar.gz - URL_MD5 19409e039e29a330893caea98715390e + URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/dcmtk-3.6.1_20120222.tar.gz + URL_MD5 86fa9e0f91e4e0c6b44d513ea48391d6 INSTALL_DIR ${proj}-install - PATCH_COMMAND ${CMAKE_COMMAND} -DTEMPLATE_FILE:FILEPATH=${MITK_SOURCE_DIR}/CMakeExternals/EmptyFileForPatching.dummy -P ${MITK_SOURCE_DIR}/CMakeExternals/PatchDCMTK-3.6.cmake CMAKE_GENERATOR ${gen} CMAKE_ARGS ${ep_common_args} -DDCMTK_OVERWRITE_WIN32_COMPILER_FLAGS:BOOL=OFF -DBUILD_SHARED_LIBS:BOOL=OFF "-DCMAKE_CXX_FLAGS:STRING=${ep_common_CXX_FLAGS} ${DCMTK_CXX_FLAGS}" "-DCMAKE_C_FLAGS:STRING=${ep_common_C_FLAGS} ${DCMTK_C_FLAGS}" -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/${proj}-install + -DDCMTK_WITH_DOXYGEN:BOOL=OFF -DDCMTK_WITH_ZLIB:BOOL=OFF # see bug #9894 -DDCMTK_WITH_OPENSSL:BOOL=OFF # see bug #9894 -DDCMTK_WITH_PNG:BOOL=OFF # see bug #9894 -DDCMTK_WITH_TIFF:BOOL=OFF # see bug #9894 -DDCMTK_WITH_XML:BOOL=OFF # see bug #9894 -DDCMTK_FORCE_FPIC_ON_UNIX:BOOL=ON DEPENDS ${proj_DEPENDENCIES} ) set(DCMTK_DIR ${CMAKE_CURRENT_BINARY_DIR}/${proj}-install) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/OpenCV.cmake b/CMakeExternals/OpenCV.cmake index 3ac3824748..3b9251b052 100644 --- a/CMakeExternals/OpenCV.cmake +++ b/CMakeExternals/OpenCV.cmake @@ -1,54 +1,63 @@ #----------------------------------------------------------------------------- # OpenCV #----------------------------------------------------------------------------- if(MITK_USE_OpenCV) # Sanity checks if(DEFINED OpenCV_DIR AND NOT EXISTS ${OpenCV_DIR}) message(FATAL_ERROR "OpenCV_DIR variable is defined but corresponds to non-existing directory") endif() set(proj OpenCV) set(proj_DEPENDENCIES) set(OpenCV_DEPENDS ${proj}) if(NOT DEFINED OpenCV_DIR) set(additional_cmake_args ) if(MITK_USE_Python) list(APPEND additional_cmake_args -DBUILD_NEW_PYTHON_SUPPORT:BOOL=ON ) endif() + + # 12-05-02, muellerm, added QT usage by OpenCV if QT is used in MITK + if(MITK_USE_QT) + list(APPEND additional_cmake_args + -DWITH_QT:BOOL=ON + -DWITH_QT_OPENGL:BOOL=OFF + -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE} + ) + endif() set(opencv_url ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/OpenCV-2.3.0.tar.bz2) set(opencv_url_md5 4e353dfb04b53bea37407f397aabf069) ExternalProject_Add(${proj} SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj}-src BINARY_DIR ${proj}-build PREFIX ${proj}-cmake URL ${opencv_url} URL_MD5 ${opencv_url_md5} INSTALL_COMMAND "" CMAKE_GENERATOR ${gen} CMAKE_ARGS ${ep_common_args} -DBUILD_DOCS:BOOL=OFF -DBUILD_TESTS:BOOL=OFF -DBUILD_EXAMPLES:BOOL=OFF -DBUILD_DOXYGEN_DOCS:BOOL=OFF ${additional_cmake_args} DEPENDS ${proj_DEPENDENCIES} ) set(OpenCV_DIR ${CMAKE_CURRENT_BINARY_DIR}/${proj}-build) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/PatchDCMTK-3.6.cmake b/CMakeExternals/PatchDCMTK-3.6.cmake deleted file mode 100644 index 775251978f..0000000000 --- a/CMakeExternals/PatchDCMTK-3.6.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# Called by DCMTK.cmake (ExternalProject_Add) as a patch for DCMTK. -# Makes pdf2dcm use "DOC" as modality for Encapsulated PDFs - -#message("Patching dcmdata/apps/pdf2dcm.cc to use modality 'DOC'. Using template ${TEMPLATE_FILE}") - -# read whole file -file(STRINGS dcmdata/apps/pdf2dcm.cc sourceCode NEWLINE_CONSUME) - -# substitute "OT" for "DOC" (only single occurence where modality tag is set) -string(REGEX REPLACE "\"OT\"" "\"DOC\"" sourceCode ${sourceCode}) - -# set variable CONTENTS, which is substituted in TEMPLATE_FILE -set(CONTENTS ${sourceCode}) -configure_file(${TEMPLATE_FILE} dcmdata/apps/pdf2dcm.cc @ONLY) - diff --git a/CMakeExternals/PatchITK-3.20.cmake b/CMakeExternals/PatchITK-3.20.cmake index e0359a2c60..7dd459d922 100644 --- a/CMakeExternals/PatchITK-3.20.cmake +++ b/CMakeExternals/PatchITK-3.20.cmake @@ -1,25 +1,36 @@ # Called by ITK.cmake (ExternalProject_Add) as a patch for ITK to work witk GDCM 2.0.18 # First patch # updates library dependencies # read whole file CMakeLists.txt file(STRINGS CMakeLists.txt sourceCode NEWLINE_CONSUME) # substitute dependency to gdcmMSFF by dependencies for more libraries string(REGEX REPLACE "gdcmMSFF" "gdcmMSFF gdcmDICT gdcmCommon gdcmDSED" sourceCode ${sourceCode}) # set variable CONTENTS, which is substituted in TEMPLATE_FILE set(CONTENTS ${sourceCode}) configure_file(${TEMPLATE_FILE} CMakeLists.txt @ONLY) # second patch # read whole file file(STRINGS Code/Common/itkLandmarkBasedTransformInitializer.h sourceCode2 NEWLINE_CONSUME) -# substitute dependency to gdcmMSFF by dependencies for more libraries +# backported fix from ITK4 string(REGEX REPLACE "typedef typename ParametersType::ValueType +ParameterValueType;" "typedef typename TransformType::ScalarType ParameterValueType;" sourceCode2 ${sourceCode2}) # set variable CONTENTS, which is substituted in TEMPLATE_FILE set(CONTENTS ${sourceCode2}) configure_file(${TEMPLATE_FILE} Code/Common/itkLandmarkBasedTransformInitializer.h @ONLY) +# third patch +# read whole file +file(STRINGS Code/Common/itkImageSource.h sourceCode3 NEWLINE_CONSUME) + +# remove ITK_NO_RETURN since the method sometimes returns which makes clang based builds crash +string(REGEX REPLACE "ITK_NO_RETURN" "" sourceCode3 ${sourceCode3}) + +# set variable CONTENTS, which is substituted in TEMPLATE_FILE +set(CONTENTS ${sourceCode3}) +configure_file(${TEMPLATE_FILE} Code/Common/itkImageSource.h @ONLY) + diff --git a/CMakeExternals/VTK.cmake b/CMakeExternals/VTK.cmake index 0853cf384f..3f905d67b3 100644 --- a/CMakeExternals/VTK.cmake +++ b/CMakeExternals/VTK.cmake @@ -1,87 +1,93 @@ #----------------------------------------------------------------------------- # VTK #----------------------------------------------------------------------------- if(WIN32) option(VTK_USE_SYSTEM_FREETYPE OFF) else(WIN32) option(VTK_USE_SYSTEM_FREETYPE ON) endif(WIN32) # Sanity checks if(DEFINED VTK_DIR AND NOT EXISTS ${VTK_DIR}) message(FATAL_ERROR "VTK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj VTK) set(proj_DEPENDENCIES ) set(VTK_DEPENDS ${proj}) if(NOT DEFINED VTK_DIR) set(additional_cmake_args ) if(MINGW) set(additional_cmake_args -DCMAKE_USE_WIN32_THREADS:BOOL=ON -DCMAKE_USE_PTHREADS:BOOL=OFF -DVTK_USE_VIDEO4WINDOWS:BOOL=OFF # no header files provided by MinGW ) endif() if(MITK_USE_Python) list(APPEND additional_cmake_args -DVTK_WRAP_PYTHON:BOOL=ON -DVTK_USE_TK:BOOL=OFF -DVTK_WINDOWS_PYTHON_DEBUGGABLE:BOOL=OFF ) endif() if(MITK_USE_QT) list(APPEND additional_cmake_args -DDESIRED_QT_VERSION:STRING=4 -DVTK_USE_GUISUPPORT:BOOL=ON -DVTK_USE_QVTK_QTOPENGL:BOOL=ON -DVTK_USE_QT:BOOL=ON -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE} ) endif() option(MITK_USE_VTK_5_8_IN_SUPERBUILD OFF) + if(CMAKE_CXX_COMPILER MATCHES clang) + if(NOT MITK_USE_VTK_5_8_IN_SUPERBUILD) + MESSAGE(STATUS "Forcing VTK 5.8 since we're using clang") + endif() + SET(MITK_USE_VTK_5_8_IN_SUPERBUILD ON CACHE BOOL "Use VTK 5.8 in MITK superbuild" FORCE) + endif() if(MITK_USE_VTK_5_8_IN_SUPERBUILD) set(VTK_URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/vtk-5.8.0.tar.gz) set(VTK_URL_MD5 37b7297d02d647cc6ca95b38174cb41f) else() set(VTK_URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/vtk-5.6.1.tar.gz) set(VTK_URL_MD5 b80a76435207c5d0f74dfcab15b75181) endif() ExternalProject_Add(${proj} SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj}-src BINARY_DIR ${proj}-build PREFIX ${proj}-cmake URL ${VTK_URL} URL_MD5 ${VTK_URL_MD5} INSTALL_COMMAND "" CMAKE_GENERATOR ${gen} CMAKE_ARGS ${ep_common_args} -DVTK_WRAP_TCL:BOOL=OFF -DVTK_WRAP_PYTHON:BOOL=OFF -DVTK_WRAP_JAVA:BOOL=OFF -DBUILD_SHARED_LIBS:BOOL=ON -DVTK_USE_PARALLEL:BOOL=ON -DVTK_USE_CHARTS:BOOL=OFF -DVTK_USE_QTCHARTS:BOOL=ON -DVTK_USE_GEOVIS:BOOL=OFF -DVTK_USE_SYSTEM_FREETYPE:BOOL=${VTK_USE_SYSTEM_FREETYPE} -DVTK_USE_QVTK_QTOPENGL:BOOL=OFF ${additional_cmake_args} DEPENDS ${proj_DEPENDENCIES} ) set(VTK_DIR ${CMAKE_CURRENT_BINARY_DIR}/${proj}-build) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a384b8aec..2b2529e3ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,867 +1,885 @@ -cmake_minimum_required(VERSION 2.8.4) +if(APPLE) + # With XCode 4.3, the SDK location changed. Older CMake + # versions are not able to find it. + cmake_minimum_required(VERSION 2.8.8) +else() + cmake_minimum_required(VERSION 2.8.4) +endif() #----------------------------------------------------------------------------- # Set a default build type if none was specified #----------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() #----------------------------------------------------------------------------- # Superbuild Option - Enabled by default #----------------------------------------------------------------------------- option(MITK_USE_SUPERBUILD "Build MITK and the projects it depends on via SuperBuild.cmake." ON) if(MITK_USE_SUPERBUILD) project(MITK-superbuild) set(MITK_SOURCE_DIR ${PROJECT_SOURCE_DIR}) set(MITK_BINARY_DIR ${PROJECT_BINARY_DIR}) else() project(MITK) endif() #----------------------------------------------------------------------------- # Warn if source or build path is too long #----------------------------------------------------------------------------- if(WIN32) set(_src_dir_length_max 50) set(_bin_dir_length_max 50) if(MITK_USE_SUPERBUILD) set(_src_dir_length_max 43) # _src_dir_length_max - strlen(ITK-src) set(_bin_dir_length_max 40) # _bin_dir_length_max - strlen(MITK-build) endif() string(LENGTH "${MITK_SOURCE_DIR}" _src_n) string(LENGTH "${MITK_BINARY_DIR}" _bin_n) # The warnings should be converted to errors if(_src_n GREATER _src_dir_length_max) message(WARNING "MITK source code directory path length is too long (${_src_n} > ${_src_dir_length_max})." "Please move the MITK source code directory to a directory with a shorter path." ) endif() if(_bin_n GREATER _bin_dir_length_max) message(WARNING "MITK build directory path length is too long (${_bin_n} > ${_bin_dir_length_max})." "Please move the MITK build directory to a directory with a shorter path." ) endif() endif() #----------------------------------------------------------------------------- # See http://cmake.org/cmake/help/cmake-2-8-docs.html#section_Policies for details #----------------------------------------------------------------------------- set(project_policies CMP0001 # NEW: CMAKE_BACKWARDS_COMPATIBILITY should no longer be used. CMP0002 # NEW: Logical target names must be globally unique. CMP0003 # NEW: Libraries linked via full path no longer produce linker search paths. CMP0004 # NEW: Libraries linked may NOT have leading or trailing whitespace. CMP0005 # NEW: Preprocessor definition values are now escaped automatically. CMP0006 # NEW: Installing MACOSX_BUNDLE targets requires a BUNDLE DESTINATION. CMP0007 # NEW: List command no longer ignores empty elements. CMP0008 # NEW: Libraries linked by full-path must have a valid library file name. CMP0009 # NEW: FILE GLOB_RECURSE calls should not follow symlinks by default. CMP0010 # NEW: Bad variable reference syntax is an error. CMP0011 # NEW: Included scripts do automatic cmake_policy PUSH and POP. CMP0012 # NEW: if() recognizes numbers and boolean constants. CMP0013 # NEW: Duplicate binary directories are not allowed. CMP0014 # NEW: Input directories must have CMakeLists.txt ) foreach(policy ${project_policies}) if(POLICY ${policy}) cmake_policy(SET ${policy} NEW) endif() endforeach() #----------------------------------------------------------------------------- # Update CMake module path #------------------------------------------------------------------------------ set(CMAKE_MODULE_PATH ${MITK_SOURCE_DIR}/CMake ${CMAKE_MODULE_PATH} ) #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(mitkMacroEmptyExternalProject) include(mitkFunctionGenerateProjectXml) include(mitkFunctionSuppressWarnings) SUPPRESS_VC_DEPRECATED_WARNINGS() #----------------------------------------------------------------------------- # Output directories. #----------------------------------------------------------------------------- foreach(type LIBRARY RUNTIME ARCHIVE) # Make sure the directory exists if(DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY AND NOT EXISTS ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) message("Creating directory MITK_CMAKE_${type}_OUTPUT_DIRECTORY: ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") file(MAKE_DIRECTORY "${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") endif() if(MITK_USE_SUPERBUILD) set(output_dir ${MITK_BINARY_DIR}/bin) if(NOT DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_BINARY_DIR}/MITK-build/bin) endif() else() if(NOT DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(output_dir ${MITK_BINARY_DIR}/bin) else() set(output_dir ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) endif() endif() set(CMAKE_${type}_OUTPUT_DIRECTORY ${output_dir} CACHE INTERNAL "Single output directory for building all libraries.") mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY) endforeach() #----------------------------------------------------------------------------- # Additional MITK Options (also shown during superbuild) #----------------------------------------------------------------------------- option(BUILD_SHARED_LIBS "Build MITK with shared libraries" ON) option(WITH_COVERAGE "Enable/Disable coverage" OFF) option(BUILD_TESTING "Test the project" ON) option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF) option(MITK_BUILD_TUTORIAL "Build the MITK tutorial" ON) option(MITK_USE_Boost "Use the Boost C++ library" OFF) option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) option(MITK_USE_CTK "Use CTK in MITK" ${MITK_USE_BLUEBERRY}) option(MITK_USE_QT "Use Nokia's Qt library" ${MITK_USE_CTK}) -option(MITK_USE_DCMTK "EXEPERIMENTAL, superbuild only: Use DCMTK in MITK" OFF) +option(MITK_USE_DCMTK "EXEPERIMENTAL, superbuild only: Use DCMTK in MITK" ${MITK_USE_CTK}) option(MITK_USE_OpenCV "Use Intel's OpenCV library" OFF) option(MITK_USE_Python "Use Python wrapping in MITK" OFF) set(MITK_USE_CableSwig ${MITK_USE_Python}) mark_as_advanced(MITK_BUILD_ALL_APPS MITK_USE_CTK MITK_USE_DCMTK ) if(MITK_USE_Boost) option(MITK_USE_SYSTEM_Boost "Use the system Boost" OFF) set(MITK_USE_Boost_LIBRARIES "" CACHE STRING "A semi-colon separated list of required Boost libraries") endif() if(MITK_USE_BLUEBERRY) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) mark_as_advanced(MITK_BUILD_ALL_PLUGINS) if(NOT MITK_USE_CTK) message("Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY") set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE) endif() endif() -if(MITK_USE_CTK AND NOT MITK_USE_QT) - message("Forcing MITK_USE_QT to ON because of MITK_USE_CTK") - set(MITK_USE_QT ON CACHE BOOL "Use Nokia's Qt library in MITK" FORCE) +if(MITK_USE_CTK) + if(NOT MITK_USE_QT) + message("Forcing MITK_USE_QT to ON because of MITK_USE_CTK") + set(MITK_USE_QT ON CACHE BOOL "Use Nokia's Qt library in MITK" FORCE) + endif() + if(NOT MITK_USE_DCMTK) + message("Setting MITK_USE_DCMTK to ON because DCMTK needs to be build for CTK") + set(MITK_USE_DCMTK ON CACHE BOOL "Use DCMTK in MITK" FORCE) + endif() endif() if(MITK_USE_QT) # find the package at the very beginning, so that QT4_FOUND is available find_package(Qt4 4.6.2 REQUIRED) endif() # Customize the default pixel types for multiplex macros set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") mark_as_advanced(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES MITK_ACCESSBYITK_DIMENSIONS ) # consistency checks if(NOT MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES) set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES) set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_DIMENSIONS) set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") endif() #----------------------------------------------------------------------------- # Additional CXX/C Flags #----------------------------------------------------------------------------- set(ADDITIONAL_C_FLAGS "" CACHE STRING "Additional C Flags") mark_as_advanced(ADDITIONAL_C_FLAGS) set(ADDITIONAL_CXX_FLAGS "" CACHE STRING "Additional CXX Flags") mark_as_advanced(ADDITIONAL_CXX_FLAGS) #----------------------------------------------------------------------------- # Project.xml #----------------------------------------------------------------------------- # A list of topologically ordered targets set(CTEST_PROJECT_SUBPROJECTS) if(MITK_USE_BLUEBERRY) list(APPEND CTEST_PROJECT_SUBPROJECTS BlueBerry) endif() list(APPEND CTEST_PROJECT_SUBPROJECTS MITK-Core MITK-CoreUI MITK-IGT MITK-ToF MITK-DTI MITK-Registration MITK-Modules # all modules not contained in a specific subproject MITK-Plugins # all plugins not contained in a specific subproject Unlabeled # special "subproject" catching all unlabeled targets and tests ) # Configure CTestConfigSubProject.cmake that could be used by CTest scripts configure_file(${MITK_SOURCE_DIR}/CTestConfigSubProject.cmake.in ${MITK_BINARY_DIR}/CTestConfigSubProject.cmake) if(CTEST_PROJECT_ADDITIONAL_TARGETS) # those targets will be executed at the end of the ctest driver script # and they also get their own subproject label set(subproject_list "${CTEST_PROJECT_SUBPROJECTS};${CTEST_PROJECT_ADDITIONAL_TARGETS}") else() set(subproject_list "${CTEST_PROJECT_SUBPROJECTS}") endif() # Generate Project.xml file expected by the CTest driver script mitkFunctionGenerateProjectXml(${MITK_BINARY_DIR} MITK "${subproject_list}" ${MITK_USE_SUPERBUILD}) #----------------------------------------------------------------------------- # Superbuild script #----------------------------------------------------------------------------- if(MITK_USE_SUPERBUILD) include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake") return() endif() #***************************************************************************** #**************************** END OF SUPERBUILD **************************** #***************************************************************************** #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(CheckCXXSourceCompiles) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionGetGccVersion) include(MacroParseArguments) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkFunctionOrganizeSources) include(mitkFunctionGetVersion) include(mitkFunctionCreateWindowsBatchScript) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionCompileSnippets) include(mitkMacroCreateModuleConf) include(mitkMacroCreateModule) include(mitkMacroCheckModule) include(mitkMacroCreateModuleTests) include(mitkFunctionAddCustomModuleTest) include(mitkMacroUseModule) include(mitkMacroMultiplexPicType) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetLinuxDistribution) #----------------------------------------------------------------------------- # Prerequesites #----------------------------------------------------------------------------- find_package(ITK REQUIRED) find_package(VTK REQUIRED) if(ITK_USE_SYSTEM_GDCM) find_package(GDCM PATHS ${ITK_GDCM_DIR} REQUIRED) endif() #----------------------------------------------------------------------------- # Set MITK specific options and variables (NOT available during superbuild) #----------------------------------------------------------------------------- # ASK THE USER TO SHOW THE CONSOLE WINDOW FOR CoreApp and ExtApp option(MITK_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting MITK GUI Applications" ON) mark_as_advanced(MITK_SHOW_CONSOLE_WINDOW) # TODO: check if necessary option(USE_ITKZLIB "Use the ITK zlib for pic compression." ON) mark_as_advanced(USE_ITKZLIB) if(NOT MITK_FAST_TESTING) if(DEFINED MITK_CTEST_SCRIPT_MODE AND (MITK_CTEST_SCRIPT_MODE STREQUAL "continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "experimental") ) set(MITK_FAST_TESTING 1) endif() endif() #----------------------------------------------------------------------------- # Get MITK version info #----------------------------------------------------------------------------- mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK) #----------------------------------------------------------------------------- # Installation preparation # # These should be set before any MITK install macros are used #----------------------------------------------------------------------------- # on Mac OSX all BlueBerry plugins get copied into every # application bundle (.app directory) specified here -if(APPLE) - if(MITK_BUILD_org.mitk.gui.qt.extapplication) - set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} ExtApp) - endif() - if(MITK_BUILD_org.mitk.gui.qt.application) - set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} CoreApp) - endif() - if(MITK_BUILD_org.mitk.gui.qt.diffusionimagingapp) - set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} mitkDiffusion) - endif() -endif(APPLE) +if(MITK_USE_BLUEBERRY AND APPLE) + + include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake") + + foreach(mitk_app ${MITK_APPS}) + # extract option_name + string(REPLACE "^^" "\\;" target_info ${mitk_app}) + set(target_info_list ${target_info}) + list(GET target_info_list 1 option_name) + list(GET target_info_list 0 app_name) + # check if the application is enabled + if(${option_name}) + set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} ${app_name}) + endif() + endforeach() + +endif() #----------------------------------------------------------------------------- # Set symbol visibility Flags #----------------------------------------------------------------------------- # MinGW does not export all symbols automatically, so no need to set flags if(CMAKE_COMPILER_IS_GNUCXX AND NOT MINGW) set(VISIBILITY_CXX_FLAGS ) #"-fvisibility=hidden -fvisibility-inlines-hidden") endif() #----------------------------------------------------------------------------- # Set coverage Flags #----------------------------------------------------------------------------- if(WITH_COVERAGE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG") set(COVERAGE_CXX_FLAGS ${coverage_flags}) set(COVERAGE_C_FLAGS ${coverage_flags}) endif() endif() #----------------------------------------------------------------------------- # MITK C/CXX Flags #----------------------------------------------------------------------------- set(MITK_C_FLAGS "${COVERAGE_C_FLAGS} ${ADDITIONAL_C_FLAGS}") set(MITK_CXX_FLAGS "${VISIBILITY_CXX_FLAGS} ${COVERAGE_CXX_FLAGS} ${ADDITIONAL_CXX_FLAGS}") include(mitkSetupC++0xVariables) set(cflags ) if(WIN32) set(cflags "${cflags} -DPOCO_NO_UNWINDOWS -DWIN32_LEAN_AND_MEAN") endif() if(CMAKE_COMPILER_IS_GNUCXX) set(cflags "${cflags} -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align -Wwrite-strings -D_FORTIFY_SOURCE=2") mitkFunctionCheckCompilerFlags("-fdiagnostics-show-option" cflags) mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" cflags) mitkFunctionCheckCompilerFlags("-Wl,--as-needed" cflags) if(MITK_USE_C++0x) mitkFunctionCheckCompilerFlags("-std=c++0x" MITK_CXX_FLAGS) endif() mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) # With older version of gcc supporting the flag -fstack-protector-all, an extra dependency to libssp.so # is introduced. If gcc is smaller than 4.4.0 and the build type is Release let's not include the flag. # Doing so should allow to build package made for distribution using older linux distro. if(${GCC_VERSION} VERSION_GREATER "4.4.0" OR (CMAKE_BUILD_TYPE STREQUAL "Debug" AND ${GCC_VERSION} VERSION_LESS "4.4.0")) mitkFunctionCheckCompilerFlags("-fstack-protector-all" cflags) endif() if(MINGW) # suppress warnings about auto imported symbols set(MITK_CXX_FLAGS "-Wl,--enable-auto-import ${MITK_CXX_FLAGS}") # we need to define a Windows version set(MITK_CXX_FLAGS "-D_WIN32_WINNT=0x0500 ${MITK_CXX_FLAGS}") endif() #set(MITK_CXX_FLAGS "-Woverloaded-virtual -Wold-style-cast -Wstrict-null-sentinel -Wsign-promo ${MITK_CXX_FLAGS}") set(MITK_CXX_FLAGS "-Woverloaded-virtual -Wstrict-null-sentinel ${MITK_CXX_FLAGS}") endif() set(MITK_C_FLAGS "${cflags} ${MITK_C_FLAGS}") set(MITK_CXX_FLAGS "${cflags} ${MITK_CXX_FLAGS}") #----------------------------------------------------------------------------- # MITK Packages #----------------------------------------------------------------------------- set(MITK_MODULES_PACKAGE_DEPENDS_DIR ${MITK_SOURCE_DIR}/CMake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- if(BUILD_TESTING) enable_testing() include(CTest) mark_as_advanced(TCL_TCLSH DART_ROOT) option(MITK_ENABLE_GUI_TESTING OFF "Enable the MITK GUI tests") # Setup file for setting custom ctest vars configure_file( CMake/CTestCustom.cmake.in ${MITK_BINARY_DIR}/CTestCustom.cmake @ONLY ) # Configuration for the CMake-generated test driver set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ") set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " try {") set(CMAKE_TESTDRIVER_AFTER_TESTMAIN " } catch( std::exception & excp ) { fprintf(stderr,\"%s\\n\",excp.what()); return EXIT_FAILURE; } catch( ... ) { printf(\"Exception caught in the test driver\\n\"); return EXIT_FAILURE; } ") set(MITK_TEST_OUTPUT_DIR "${MITK_BINARY_DIR}/test_output") if(NOT EXISTS ${MITK_TEST_OUTPUT_DIR}) file(MAKE_DIRECTORY ${MITK_TEST_OUTPUT_DIR}) endif() # Test the external project template if(MITK_USE_BLUEBERRY) include(mitkTestProjectTemplate) endif() # Test the package target include(mitkPackageTest) endif() configure_file(mitkTestingConfig.h.in ${MITK_BINARY_DIR}/mitkTestingConfig.h) #----------------------------------------------------------------------------- # MITK_SUPERBUILD_BINARY_DIR #----------------------------------------------------------------------------- # If MITK_SUPERBUILD_BINARY_DIR isn't defined, it means MITK is *NOT* build using Superbuild. # In that specific case, MITK_SUPERBUILD_BINARY_DIR should default to MITK_BINARY_DIR if(NOT DEFINED MITK_SUPERBUILD_BINARY_DIR) set(MITK_SUPERBUILD_BINARY_DIR ${MITK_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # Compile Utilities and set-up MITK variables #----------------------------------------------------------------------------- include(mitkSetupVariables) #----------------------------------------------------------------------------- # Cleanup #----------------------------------------------------------------------------- file(GLOB _MODULES_CONF_FILES ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME}/*.cmake) if(_MODULES_CONF_FILES) file(REMOVE ${_MODULES_CONF_FILES}) endif() add_subdirectory(Utilities) if(MITK_USE_BLUEBERRY) # We need to hack a little bit because MITK applications may need # to enable certain BlueBerry plug-ins. However, these plug-ins # are validated separately from the MITK plug-ins and know nothing # about potential MITK plug-in dependencies of the applications. Hence # we cannot pass the MITK application list to the BlueBerry # ctkMacroSetupPlugins call but need to extract the BlueBerry dependencies # from the applications and set them explicitly. include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) # check if the application is enabled and if target_libraries.cmake exists if(${option_name} AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/target_libraries.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/target_libraries.cmake") foreach(_target_dep ${target_libraries}) if(_target_dep MATCHES org_blueberry_) string(REPLACE _ . _app_bb_dep ${_target_dep}) # explicitly set the build option for the BlueBerry plug-in set(BLUEBERRY_BUILD_${_app_bb_dep} ON CACHE BOOL "Build the ${_app_bb_dep} plug-in") endif() endforeach() endif() endforeach() set(mbilog_DIR "${mbilog_BINARY_DIR}") if(MITK_BUILD_ALL_PLUGINS) set(BLUEBERRY_BUILD_ALL_PLUGINS ON) endif() add_subdirectory(BlueBerry) set(BlueBerry_DIR ${CMAKE_CURRENT_BINARY_DIR}/BlueBerry CACHE PATH "The directory containing a CMake configuration file for BlueBerry" FORCE) include(mitkMacroCreateCTKPlugin) endif() #----------------------------------------------------------------------------- # Set C/CXX Flags for MITK code #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MITK_CXX_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MITK_C_FLAGS}") if(MITK_USE_QT) add_definitions(-DQWT_DLL) endif() #----------------------------------------------------------------------------- # Add custom targets representing CDash subprojects #----------------------------------------------------------------------------- foreach(subproject ${CTEST_PROJECT_SUBPROJECTS}) if(NOT TARGET ${subproject} AND NOT subproject MATCHES "Unlabeled") add_custom_target(${subproject}) endif() endforeach() #----------------------------------------------------------------------------- # Add subdirectories #----------------------------------------------------------------------------- link_directories(${MITK_LINK_DIRECTORIES}) add_subdirectory(Core) add_subdirectory(Modules) if(MITK_USE_BLUEBERRY) find_package(BlueBerry REQUIRED) set(MITK_DEFAULT_SUBPROJECTS MITK-Plugins) # Plug-in testing (needs some work to be enabled again) if(BUILD_TESTING) include(berryTestingHelpers) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp") get_target_property(_is_macosx_bundle CoreApp MACOSX_BUNDLE) if(APPLE AND _is_macosx_bundle) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp.app/Contents/MacOS/CoreApp") endif() set(BLUEBERRY_TEST_APP_ID "org.mitk.qt.coreapplication") endif() include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake") set(mitk_plugins_fullpath ) foreach(mitk_plugin ${MITK_EXT_PLUGINS}) list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin}) endforeach() if(EXISTS ${MITK_PRIVATE_MODULES}/PluginList.cmake) include(${MITK_PRIVATE_MODULES}/PluginList.cmake) foreach(mitk_plugin ${MITK_PRIVATE_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PRIVATE_MODULES}/${mitk_plugin}) endforeach() endif() # Specify which plug-ins belong to this project macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin_mitk "^org_mitk_[a-zA-Z0-9_]+$") set(re_ctkplugin_bb "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin_mitk re_ctkplugin_bb OUTPUT_VARIABLE ${varname}) endmacro() # Get infos about application directories and build options include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake") set(mitk_apps_fullpath ) foreach(mitk_app ${MITK_APPS}) list(APPEND mitk_apps_fullpath "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${mitk_app}") endforeach() ctkMacroSetupPlugins(${mitk_plugins_fullpath} BUILD_OPTION_PREFIX MITK_BUILD_ APPS ${mitk_apps_fullpath} BUILD_ALL ${MITK_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE}) else() file(REMOVE ${MITK_PLUGIN_USE_FILE}) set(MITK_PLUGIN_USE_FILE ) endif() endif() # Construct a list of paths containing runtime directories # for MITK applications on Windows set(MITK_RUNTIME_PATH "${VTK_LIBRARY_DIRS}/%VS_BUILD_TYPE%;${ITK_LIBRARY_DIRS}/%VS_BUILD_TYPE%;${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/%VS_BUILD_TYPE%" ) if(QT4_FOUND) set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${QT_LIBRARY_DIR}/../bin") endif() if(MITK_USE_BLUEBERRY) set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${CTK_RUNTIME_LIBRARY_DIRS}/%VS_BUILD_TYPE%") if(DEFINED CTK_PLUGIN_RUNTIME_OUTPUT_DIRECTORY) if(IS_ABSOLUTE "${CTK_PLUGIN_RUNTIME_OUTPUT_DIRECTORY}") set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${CTK_PLUGIN_RUNTIME_OUTPUT_DIRECTORY}/%VS_BUILD_TYPE%") else() set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CTK_PLUGIN_RUNTIME_OUTPUT_DIRECTORY}/%VS_BUILD_TYPE%") endif() else() set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/plugins/%VS_BUILD_TYPE%") endif() endif() if(GDCM_DIR) set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${GDCM_DIR}/bin/%VS_BUILD_TYPE%") endif() if(OpenCV_DIR) set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${OpenCV_DIR}/bin/%VS_BUILD_TYPE%") endif() # DCMTK is statically build #if(DCMTK_DIR) # set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${DCMTK_DIR}/bin/%VS_BUILD_TYPE%") #endif() if(MITK_USE_Boost AND MITK_USE_Boost_LIBRARIES AND NOT MITK_USE_SYSTEM_Boost) set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${Boost_LIBRARY_DIRS}") endif() #----------------------------------------------------------------------------- # Python Wrapping #----------------------------------------------------------------------------- set(MITK_WRAPPING_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Wrapping) set(MITK_WRAPPING_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/Wrapping) option(MITK_USE_Python "Build cswig Python wrapper support (requires CableSwig)." OFF) if(MITK_USE_Python) add_subdirectory(Wrapping) endif() #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- add_subdirectory(Documentation) #----------------------------------------------------------------------------- # Installation #----------------------------------------------------------------------------- # set MITK cpack variables # These are the default variables, which can be overwritten ( see below ) include(mitkSetupCPack) set(use_default_config ON) # MITK_APPS is set in Applications/AppList.cmake (included somewhere above # if MITK_USE_BLUEBERRY is set to ON). if(MITK_APPS) set(activated_apps_no 0) list(LENGTH MITK_APPS app_count) # Check how many apps have been enabled # If more than one app has been activated, the we use the # default CPack configuration. Otherwise that apps configuration # will be used, if present. foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) # check if the application is enabled if(${option_name}) MATH(EXPR activated_apps_no "${activated_apps_no} + 1") endif() endforeach() if(app_count EQUAL 1 AND (activated_apps_no EQUAL 1 OR MITK_BUILD_ALL_APPS)) # Corner case if there is only one app in total set(use_project_cpack ON) elseif(activated_apps_no EQUAL 1 AND NOT MITK_BUILD_ALL_APPS) # Only one app is enabled (no "build all" flag set) set(use_project_cpack ON) else() # Less or more then one app is enabled set(use_project_cpack OFF) endif() foreach(mitk_app ${MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) # check if the application is enabled if(${option_name}) # check whether application specific configuration files will be used if(use_project_cpack) # use files if they exist if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/CPackOptions.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/CPackOptions.cmake") endif() if(EXISTS "${PROJECT_SOURCE_DIR}/Applications/${target_dir}/CPackConfig.cmake.in") set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/Applications/${target_dir}/CPackConfig.cmake") configure_file(${PROJECT_SOURCE_DIR}/Applications/${target_dir}/CPackConfig.cmake.in ${CPACK_PROJECT_CONFIG_FILE} @ONLY) set(use_default_config OFF) endif() endif() # add link to the list list(APPEND CPACK_CREATE_DESKTOP_LINKS "${target_dir}") endif() endforeach() endif() # if no application specific configuration file was used, use default if(use_default_config) configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in ${MITK_BINARY_DIR}/MITKCPackOptions.cmake @ONLY) set(CPACK_PROJECT_CONFIG_FILE "${MITK_BINARY_DIR}/MITKCPackOptions.cmake") endif() # include CPack model once all variables are set include(CPack) # Additional installation rules include(mitkInstallRules) #----------------------------------------------------------------------------- # Last configuration steps #----------------------------------------------------------------------------- set(MITK_EXPORTS_FILE "${MITK_BINARY_DIR}/MitkExports.cmake") file(REMOVE ${MITK_EXPORTS_FILE}) if(MITK_USE_BLUEBERRY) # This is for installation support of external projects depending on # MITK plugins. The export file should not be used for linking to MITK # libraries without using LINK_DIRECTORIES, since the exports are incomplete # yet(depending libraries are not exported). if(MITK_PLUGIN_LIBRARIES) export(TARGETS ${MITK_PLUGIN_LIBRARIES} APPEND FILE ${MITK_EXPORTS_FILE}) endif() endif() configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) configure_file(mitkVersion.h.in ${MITK_BINARY_DIR}/mitkVersion.h) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) set(VECMATH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/vecmath) set(IPFUNC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipFunc) set(UTILITIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities) file(GLOB _MODULES_CONF_FILES RELATIVE ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME} ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME}/*.cmake) set(MITK_MODULE_NAMES) foreach(_module ${_MODULES_CONF_FILES}) string(REPLACE Config.cmake "" _module_name ${_module}) list(APPEND MITK_MODULE_NAMES ${_module_name}) endforeach() configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY) # If we are under Windows, create two batch files which correctly # set up the environment for the application and for Visual Studio if(WIN32) include(mitkFunctionCreateWindowsBatchScript) set(VS_SOLUTION_FILE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.sln") foreach(VS_BUILD_TYPE debug release) mitkFunctionCreateWindowsBatchScript("${MITK_SOURCE_DIR}/CMake/StartVS.bat.in" ${PROJECT_BINARY_DIR}/StartVS_${VS_BUILD_TYPE}.bat ${VS_BUILD_TYPE}) endforeach() endif(WIN32) #----------------------------------------------------------------------------- # MITK Applications #----------------------------------------------------------------------------- # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Applications) diff --git a/Core/Code/Algorithms/itkImportMitkImageContainer.txx b/Core/Code/Algorithms/itkImportMitkImageContainer.txx index e3d85cb1aa..d6c7acb16c 100644 --- a/Core/Code/Algorithms/itkImportMitkImageContainer.txx +++ b/Core/Code/Algorithms/itkImportMitkImageContainer.txx @@ -1,65 +1,65 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 13129 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _itkImportMitkImageContainer_txx #define _itkImportMitkImageContainer_txx #include "itkImportMitkImageContainer.h" namespace itk { template ImportMitkImageContainer ::ImportMitkImageContainer() { } template ImportMitkImageContainer< TElementIdentifier , TElement > ::~ImportMitkImageContainer() { m_ImageDataItem = NULL; } template void ImportMitkImageContainer< TElementIdentifier , TElement > ::SetImageDataItem(mitk::ImageDataItem* imageDataItem) { m_ImageDataItem = imageDataItem; - SetImportPointer( (TElement*) m_ImageDataItem->GetData(), m_ImageDataItem->GetSize()/sizeof(Element), false); + this->SetImportPointer( (TElement*) m_ImageDataItem->GetData(), m_ImageDataItem->GetSize()/sizeof(Element), false); this->Modified(); } template void ImportMitkImageContainer< TElementIdentifier , TElement > ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os,indent); os << indent << "ImageDataItem: " << m_ImageDataItem << std::endl; } } // end namespace itk #endif diff --git a/Core/Code/DataManagement/mitkGeometry3D.cpp b/Core/Code/DataManagement/mitkGeometry3D.cpp index 59ce86d746..c98095438a 100644 --- a/Core/Code/DataManagement/mitkGeometry3D.cpp +++ b/Core/Code/DataManagement/mitkGeometry3D.cpp @@ -1,741 +1,741 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ +#include + #include "mitkGeometry3D.h" #include "mitkMatrixConvert.h" #include "mitkRotationOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkPointOperation.h" #include "mitkInteractionConst.h" //#include "mitkStatusBar.h" #include #include // Standard constructor for the New() macro. Sets the geometry to 3 dimensions mitk::Geometry3D::Geometry3D() : m_ParametricBoundingBox(NULL), m_ImageGeometry(false), m_Valid(true), m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0) { FillVector3D(m_FloatSpacing, 1,1,1); m_VtkMatrix = vtkMatrix4x4::New(); m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New(); m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix); Initialize(); } mitk::Geometry3D::Geometry3D(const Geometry3D& other) : Superclass(), m_ParametricBoundingBox(other.m_ParametricBoundingBox),m_TimeBounds(other.m_TimeBounds), m_ImageGeometry(other.m_ImageGeometry), m_Valid(other.m_Valid), m_FrameOfReferenceID(other.m_FrameOfReferenceID), m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified), m_RotationQuaternion( other.m_RotationQuaternion ) , m_Origin(other.m_Origin) { // AffineGeometryFrame SetBounds(other.GetBounds()); //SetIndexToObjectTransform(other.GetIndexToObjectTransform()); //SetObjectToNodeTransform(other.GetObjectToNodeTransform()); //SetIndexToWorldTransform(other.GetIndexToWorldTransform()); // this is not used in AffineGeometryFrame of ITK, thus there are not Get and Set methods // m_IndexToNodeTransform = other.m_IndexToNodeTransform; // m_InvertedTransform = TransformType::New(); // m_InvertedTransform = TransformType::New(); // m_InvertedTransform->DeepCopy(other.m_InvertedTransform); m_VtkMatrix = vtkMatrix4x4::New(); m_VtkMatrix->DeepCopy(other.m_VtkMatrix); if (other.m_ParametricBoundingBox.IsNotNull()) { m_ParametricBoundingBox = other.m_ParametricBoundingBox->DeepCopy(); } FillVector3D(m_FloatSpacing,other.m_FloatSpacing[0],other.m_FloatSpacing[1],other.m_FloatSpacing[2]); m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New(); m_VtkIndexToWorldTransform->DeepCopy(other.m_VtkIndexToWorldTransform); m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix); other.InitializeGeometry(this); } mitk::Geometry3D::~Geometry3D() { m_VtkMatrix->Delete(); m_VtkIndexToWorldTransform->Delete(); } static void CopySpacingFromTransform(mitk::AffineTransform3D* transform, mitk::Vector3D& spacing, float floatSpacing[3]) { mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = transform->GetMatrix().GetVnlMatrix(); spacing[0]=vnlmatrix.get_column(0).magnitude(); spacing[1]=vnlmatrix.get_column(1).magnitude(); spacing[2]=vnlmatrix.get_column(2).magnitude(); floatSpacing[0]=spacing[0]; floatSpacing[1]=spacing[1]; floatSpacing[2]=spacing[2]; } void mitk::Geometry3D::Initialize() { float b[6] = {0,1,0,1,0,1}; SetFloatBounds(b); m_IndexToObjectTransform = TransformType::New(); m_ObjectToNodeTransform = TransformType::New(); if(m_IndexToWorldTransform.IsNull()) m_IndexToWorldTransform = TransformType::New(); else m_IndexToWorldTransform->SetIdentity(); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); m_VtkMatrix->Identity(); m_TimeBounds[0]=ScalarTypeNumericTraits::NonpositiveMin(); m_TimeBounds[1]=ScalarTypeNumericTraits::max(); m_FrameOfReferenceID = 0; m_ImageGeometry = false; } void mitk::Geometry3D::TransferItkToVtkTransform() { // copy m_IndexToWorldTransform into m_VtkIndexToWorldTransform TransferItkTransformToVtkMatrix(m_IndexToWorldTransform.GetPointer(), m_VtkMatrix); m_VtkIndexToWorldTransform->Modified(); } void mitk::Geometry3D::TransferVtkToItkTransform() { TransferVtkMatrixToItkTransform(m_VtkMatrix, m_IndexToWorldTransform.GetPointer()); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); } void mitk::Geometry3D::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4* vtkmatrix) { m_VtkMatrix->DeepCopy(vtkmatrix); TransferVtkToItkTransform(); } void mitk::Geometry3D::SetTimeBounds(const TimeBounds& timebounds) { if(m_TimeBounds != timebounds) { m_TimeBounds = timebounds; Modified(); } } void mitk::Geometry3D::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++; SetBoundsArray(b, m_BoundingBox); } void mitk::Geometry3D::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++; SetBoundsArray(b, m_BoundingBox); } void mitk::Geometry3D::SetParametricBounds(const BoundingBox::BoundsArrayType& bounds) { SetBoundsArray(bounds, m_ParametricBoundingBox); } void mitk::Geometry3D::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const { BackTransform(pt_mm, pt_units); } void mitk::Geometry3D::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const { pt_mm = m_IndexToWorldTransform->TransformPoint(pt_units); } void mitk::Geometry3D::WorldToIndex(const mitk::Point3D & /*atPt3d_mm*/, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { MITK_WARN<<"Warning! Call of the deprecated function Geometry3D::WorldToIndex(point, vec, vec). Use Geometry3D::WorldToIndex(vec, vec) instead!"; //BackTransform(atPt3d_mm, vec_mm, vec_units); this->WorldToIndex(vec_mm, vec_units); } void mitk::Geometry3D::WorldToIndex( const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { BackTransform( vec_mm, vec_units); } void mitk::Geometry3D::IndexToWorld(const mitk::Point3D &/*atPt3d_units*/, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { MITK_WARN<<"Warning! Call of the deprecated function Geometry3D::IndexToWorld(point, vec, vec). Use Geometry3D::IndexToWorld(vec, vec) instead!"; //vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); this->IndexToWorld(vec_units, vec_mm); } void mitk::Geometry3D::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); } void mitk::Geometry3D::SetIndexToWorldTransform(mitk::AffineTransform3D* transform) { if(m_IndexToWorldTransform.GetPointer() != transform) { Superclass::SetIndexToWorldTransform(transform); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); TransferItkToVtkTransform(); Modified(); } } mitk::AffineGeometryFrame3D::Pointer mitk::Geometry3D::Clone() const { Self::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } /* void mitk::Geometry3D::InitializeGeometry(Geometry3D * newGeometry) const { Superclass::InitializeGeometry(newGeometry); newGeometry->SetTimeBounds(m_TimeBounds); //newGeometry->GetVtkTransform()->SetMatrix(m_VtkIndexToWorldTransform->GetMatrix()); IW //newGeometry->TransferVtkToItkTransform(); //MH newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID()); newGeometry->m_ImageGeometry = m_ImageGeometry; } */ void mitk::Geometry3D::SetExtentInMM(int direction, ScalarType extentInMM) { ScalarType len = GetExtentInMM(direction); if(fabs(len - extentInMM)>=mitk::eps) { AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_IndexToWorldTransform->GetMatrix().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_IndexToWorldTransform->SetMatrix(matrix); Modified(); } } mitk::BoundingBox::Pointer mitk::Geometry3D::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!=NULL) { 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; } #include void mitk::Geometry3D::ExecuteOperation(Operation* operation) { vtkTransform *vtktransform = vtkTransform::New(); vtktransform->SetMatrix(m_VtkMatrix); switch (operation->GetOperationType()) { case OpNOTHING: break; case OpMOVE: { mitk::PointOperation *pointOp = dynamic_cast(operation); if (pointOp == NULL) { //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); 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: { mitk::PointOperation *pointOp = dynamic_cast(operation); if (pointOp == NULL) { //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); return; } mitk::Point3D newScale = pointOp->GetPoint(); ScalarType data[3]; /* calculate new scale: newscale = oldscale * (oldscale + scaletoadd)/oldscale */ data[0] = 1 + (newScale[0] / GetMatrixColumn(0).magnitude()); data[1] = 1 + (newScale[1] / GetMatrixColumn(1).magnitude()); data[2] = 1 + (newScale[2] / GetMatrixColumn(2).magnitude()); mitk::Point3D center = const_cast(m_BoundingBox.GetPointer())->GetCenter(); ScalarType pos[3]; vtktransform->GetPosition(pos); vtktransform->PostMultiply(); vtktransform->Translate(-pos[0], -pos[1], -pos[2]); vtktransform->Translate(-center[0], -center[1], -center[2]); vtktransform->PreMultiply(); vtktransform->Scale(data[0], data[1], data[2]); vtktransform->PostMultiply(); vtktransform->Translate(+center[0], +center[1], +center[2]); vtktransform->Translate(pos[0], pos[1], pos[2]); vtktransform->PreMultiply(); break; } case OpROTATE: { mitk::RotationOperation *rotateOp = dynamic_cast(operation); if (rotateOp == NULL) { //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); 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); break; } default: vtktransform->Delete(); return; } m_VtkMatrix->DeepCopy(vtktransform->GetMatrix()); TransferVtkToItkTransform(); Modified(); vtktransform->Delete(); } void mitk::Geometry3D::BackTransform(const mitk::Point3D &in, mitk::Point3D& out) const { ScalarType temp[3]; unsigned int i, j; const TransformType::OffsetType& offset = m_IndexToWorldTransform->GetOffset(); // Remove offset for (j = 0; j < 3; j++) { temp[j] = in[j] - offset[j]; } // Get WorldToIndex transform if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) { m_InvertedTransform = TransformType::New(); if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) { itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); } m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->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 << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl << inverse ); } // Transform point for (i = 0; i < 3; i++) { out[i] = 0.0; for (j = 0; j < 3; j++) { out[i] += inverse[i][j]*temp[j]; } } } void mitk::Geometry3D::BackTransform(const mitk::Point3D &/*at*/, const mitk::Vector3D &in, mitk::Vector3D& out) const { MITK_INFO<<"Warning! Call of the deprecated function Geometry3D::BackTransform(point, vec, vec). Use Geometry3D::BackTransform(vec, vec) instead!"; //// Get WorldToIndex transform //if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) //{ // m_InvertedTransform = TransformType::New(); // if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) // { // itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); // } // m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->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 // << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl // << inverse ); //} //// Transform vector //for (unsigned int i = 0; i < 3; i++) //{ // out[i] = 0.0; // for (unsigned int j = 0; j < 3; j++) // { // out[i] += inverse[i][j]*in[j]; // } //} this->BackTransform(in, out); } void mitk::Geometry3D::BackTransform(const mitk::Vector3D& in, mitk::Vector3D& out) const { // Get WorldToIndex transform if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) { m_InvertedTransform = TransformType::New(); if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) { itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); } m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->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 << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl << inverse ); } // Transform vector for (unsigned int i = 0; i < 3; i++) { out[i] = 0.0; for (unsigned int j = 0; j < 3; j++) { out[i] += inverse[i][j]*in[j]; } } } const float* mitk::Geometry3D::GetFloatSpacing() const { return m_FloatSpacing; } void mitk::Geometry3D::SetSpacing(const mitk::Vector3D& aSpacing) { if(mitk::Equal(m_Spacing, aSpacing) == false) { assert(aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0); m_Spacing = aSpacing; AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); mitk::VnlVector col; col = vnlmatrix.get_column(0); col.normalize(); col*=aSpacing[0]; vnlmatrix.set_column(0, col); col = vnlmatrix.get_column(1); col.normalize(); col*=aSpacing[1]; vnlmatrix.set_column(1, col); col = vnlmatrix.get_column(2); col.normalize(); col*=aSpacing[2]; vnlmatrix.set_column(2, col); Matrix3D matrix; matrix = vnlmatrix; AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetMatrix(matrix); transform->SetOffset(m_IndexToWorldTransform->GetOffset()); SetIndexToWorldTransform(transform.GetPointer()); itk2vtk(m_Spacing, m_FloatSpacing); } } void mitk::Geometry3D::SetOrigin(const Point3D & origin) { if(origin!=GetOrigin()) { m_Origin = origin; m_IndexToWorldTransform->SetOffset(m_Origin.GetVectorFromOrigin()); Modified(); TransferItkToVtkTransform(); } } void mitk::Geometry3D::Translate(const Vector3D & vector) { if((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) { m_IndexToWorldTransform->SetOffset(m_IndexToWorldTransform->GetOffset()+vector); TransferItkToVtkTransform(); Modified(); } } void mitk::Geometry3D::SetIdentity() { m_IndexToWorldTransform->SetIdentity(); m_Origin.Fill(0); Modified(); TransferItkToVtkTransform(); } void mitk::Geometry3D::Compose( const mitk::AffineGeometryFrame3D::TransformType * other, bool pre ) { m_IndexToWorldTransform->Compose(other, pre); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); Modified(); TransferItkToVtkTransform(); } void mitk::Geometry3D::Compose( const vtkMatrix4x4 * vtkmatrix, bool pre ) { mitk::AffineGeometryFrame3D::TransformType::Pointer itkTransform = mitk::AffineGeometryFrame3D::TransformType::New(); TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); Compose(itkTransform, pre); } -const char* mitk::Geometry3D::GetTransformAsString( TransformType* transformType ) +const std::string mitk::Geometry3D::GetTransformAsString( TransformType* transformType ) { - static char buffer[255]; - for ( int j=0; j<255; j++) buffer[j] = '\0'; - ostrstream out( buffer, 255 ); + 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 buffer; + return out.str(); } void mitk::Geometry3D::PrintSelf(std::ostream& os, itk::Indent indent) const { os << indent << " IndexToWorldTransform: "; if(m_IndexToWorldTransform.IsNull()) os << "NULL" << 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 << m_IndexToWorldTransform->GetMatrix()[i][j] << " "; } os << std::endl; } os << indent << "Offset: " << m_IndexToWorldTransform->GetOffset() << std::endl; os << indent << "Center: " << m_IndexToWorldTransform->GetCenter() << std::endl; os << indent << "Translation: " << m_IndexToWorldTransform->GetTranslation() << std::endl; os << indent << "Inverse: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << m_IndexToWorldTransform->GetInverseMatrix()[i][j] << " "; } os << std::endl; } // from itk::ScalableAffineTransform os << indent << "Scale : "; for (i = 0; i < 3; i++) { os << m_IndexToWorldTransform->GetScale()[i] << " "; } os << std::endl; } os << indent << " BoundingBox: "; if(m_BoundingBox.IsNull()) os << "NULL" << std::endl; else { os << indent << "( "; for (unsigned int i=0; i<3; i++) { os << m_BoundingBox->GetBounds()[2*i] << "," << m_BoundingBox->GetBounds()[2*i+1] << " "; } os << " )" << std::endl; } os << indent << " Origin: " << m_Origin << std::endl; os << indent << " ImageGeometry: " << m_ImageGeometry << std::endl; os << indent << " Spacing: " << m_Spacing << std::endl; os << indent << " TimeBounds: " << m_TimeBounds << std::endl; } mitk::Point3D mitk::Geometry3D::GetCornerPoint(int id) const { assert(id >= 0); assert(m_BoundingBox.IsNotNull()); BoundingBox::BoundsArrayType bounds = m_BoundingBox->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."); return NULL; } } 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 m_IndexToWorldTransform->TransformPoint(cornerpoint); } mitk::Point3D mitk::Geometry3D::GetCornerPoint(bool xFront, bool yFront, bool zFront) const { assert(m_BoundingBox.IsNotNull()); BoundingBox::BoundsArrayType bounds = m_BoundingBox->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 m_IndexToWorldTransform->TransformPoint(cornerpoint); } void mitk::Geometry3D::ResetSubTransforms() { } void mitk::Geometry3D::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); } diff --git a/Core/Code/DataManagement/mitkGeometry3D.h b/Core/Code/DataManagement/mitkGeometry3D.h index 46528d5ff1..c7c7decbce 100644 --- a/Core/Code/DataManagement/mitkGeometry3D.h +++ b/Core/Code/DataManagement/mitkGeometry3D.h @@ -1,663 +1,663 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef GEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #define GEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #include #include #include "mitkVector.h" #include "mitkOperationActor.h" #include #include #include #include class vtkLinearTransform; class vtkMatrixToLinearTransform; class vtkMatrix4x4; 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; typedef itk::AffineGeometryFrame AffineGeometryFrame3D; //##Documentation //## @brief Describes the geometry of a data object //## //## At least, it can return the bounding box of the 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 a life span, i.e. a bounding box in time in ms (with //## start and end time), to be accessed by GetTimeBounds(). //## The default is minus infinity to plus infinity. //## //## Geometry3D and its sub-classes allow converting between //## intrinsic coordinates (called index or unit coordinates) //## and world-coordinates (called world or mm coordinates), //## e.g. WorldToIndex. //## In case you need integer index coordinates, provide an //## mitk::Index3D (or itk::Index) as target variable to //## WorldToIndex, otherwise you will get a continuous index //## (floating point values). //## //## An important sub-class is SlicedGeometry3D, which descibes //## 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. //## //## Geometry3D instances referring to an Image need a slightly //## different definition of corners, see SetImageGeometry. This //## is usualy automatically called by Image. //## //## Geometry3D 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(). //## //## Rule: everything is in mm (ms) if not stated otherwise. //## @ingroup Geometry class MITK_CORE_EXPORT Geometry3D : public AffineGeometryFrame3D, public OperationActor { public: mitkClassMacro(Geometry3D, AffineGeometryFrame3D); typedef itk::QuaternionRigidTransform< ScalarType > QuaternionTransformType; typedef QuaternionTransformType::VnlQuaternionType VnlQuaternionType; /** Method for creation through the object factory. */ itkNewMacro(Self); // a bit of a misuse, but we want only doxygen to see the following: #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates itkGetObjectMacro(IndexToWorldTransform, AffineTransform3D); #endif //## @brief Set the transformation used to convert from index //## to world coordinates virtual void SetIndexToWorldTransform(mitk::AffineTransform3D* transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4 //## \sa SetIndexToWorldTransform virtual void SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4* vtkmatrix); #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 { assert(m_BoundingBox.IsNotNull()); return m_BoundingBox->GetBounds(); } //##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. virtual void SetBounds(const BoundsArrayType& bounds); #endif //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a float array virtual void SetFloatBounds(const float bounds[6]); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a double array virtual void SetFloatBounds(const double bounds[6]); //##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 Get the time bounds (in ms) itkGetConstReferenceMacro(TimeBounds, TimeBounds); //##Documentation //## @brief Set the time bounds (in ms) virtual void SetTimeBounds(const TimeBounds& timebounds); //##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 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 { Vector3D frontToBack; frontToBack.Set_vnl_vector(m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction)); frontToBack *= GetExtent(direction); return frontToBack; } //##Documentation //## @brief Get the center of the bounding-box in mm //## Point3D GetCenter() const { assert(m_BoundingBox.IsNotNull()); return m_IndexToWorldTransform->TransformPoint(m_BoundingBox->GetCenter()); } //##Documentation //## @brief Get the squared length of the diagonal of the bounding-box in mm //## double GetDiagonalLength2() const { Vector3D diagonalvector = GetCornerPoint()-GetCornerPoint(false, false, false); return diagonalvector.GetSquaredNorm(); } //##Documentation //## @brief Get the length of the diagonal of the bounding-box in mm //## double GetDiagonalLength() const { return sqrt(GetDiagonalLength2()); } //##Documentation //## @brief Get a VnlVector along bounding-box in the specified //## @a direction, length is spacing //## //## \sa GetAxisVector VnlVector GetMatrixColumn(unsigned int direction) const { return m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction); } #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 //##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 { return m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction).magnitude()*GetExtent(direction); } //##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! virtual void SetExtentInMM(int direction, ScalarType extentInMM); //##Documentation //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform vtkLinearTransform* GetVtkTransform() const { return (vtkLinearTransform*)m_VtkIndexToWorldTransform; } //##Documentation //## @brief Set the origin, i.e. the upper-left corner of the plane //## virtual void SetOrigin(const Point3D& origin); //##Documentation //## @brief Translate the origin by a vector //## virtual void Translate(const Vector3D& vector); //##Documentation //## @brief Set the transform to identity //## virtual void SetIdentity(); //##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. virtual void Compose( const AffineGeometryFrame3D::TransformType * other, bool pre = 0 ); //##Documentation //## @brief Compose new IndexToWorldTransform with a given vtkMatrix4x4. //## //## Converts the vtkMatrix4x4 into a itk-transform and calls the previous method. virtual void Compose( const vtkMatrix4x4 * vtkmatrix, bool pre = 0 ); //##Documentation //## @brief Get the origin, e.g. the upper-left corner of the plane const Point3D& GetOrigin() const { return m_Origin; } //##Documentation //## @brief Get the origin as VnlVector //## //## \sa GetOrigin VnlVector GetOriginVnl() const { return const_cast(this)->m_Origin.Get_vnl_vector(); } //##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 (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 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 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 (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 (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 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( pt_units[i] ); index[i]=itk::Math::RoundHalfIntegerUp( pt_units[i] ); } } //##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 Geometry3D 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); } //##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 Initialize the Geometry3D virtual void Initialize(); //##Documentation //## @brief Is this an ImageGeometry? //## //## For more information, see SetImageGeometry itkGetConstMacro(ImageGeometry, bool); //##Documentation //## @brief Define that this Geometry3D is refering 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 Geometry3D is referring to an Image. itkSetMacro(ImageGeometry, bool); itkBooleanMacro(ImageGeometry); //##Documentation //## @brief Is this Geometry3D in a state that is valid? virtual bool IsValid() const { return m_Valid; } //##Documentation //## @brief Test whether the point \a p (world coordinates in mm) is //## inside the bounding box bool IsInside(const mitk::Point3D& p) const { mitk::Point3D index; WorldToIndex(p, index); return IsIndexInside(index); } //##Documentation //## @brief Test whether the point \a p ((continous!)index coordinates in units) is //## inside the bounding box bool 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 = m_BoundingBox->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 = m_BoundingBox->GetBounds(); if((discretIndex[0] == bounds[1]) || (discretIndex[1] == bounds[3]) || (discretIndex[2] == bounds[5])) inside = false; } } else inside = m_BoundingBox->IsInside(index); return inside; } //##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); } //##Documentation //## @brief Get the spacing (size of a pixel). //## itkGetConstReferenceMacro(Spacing, mitk::Vector3D); //##Documentation //## @brief Get the spacing as a float[3] array. const float* GetFloatSpacing() const; //##Documentation //## @brief Set the spacing (m_Spacing) virtual void SetSpacing(const mitk::Vector3D& aSpacing); //##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); //##Documentation //## @brief Copy the ITK transform //## (m_IndexToWorldTransform) to the VTK transform //## \sa SetIndexToWorldTransform void TransferItkToVtkTransform(); //##Documentation //## @brief Copy the VTK transform //## to the ITK transform (m_IndexToWorldTransform) //## \sa SetIndexToWorldTransform void TransferVtkToItkTransform(); //##Documentation //## @brief Get the parametric bounding-box //## //## See AbstractTransformGeometry for an example usage of this. itkGetConstObjectMacro(ParametricBoundingBox, BoundingBox); //##Documentation //## @brief Get the parametric bounds //## //## See AbstractTransformGeometry for an example usage of this. const BoundingBox::BoundsArrayType& GetParametricBounds() const { assert(m_ParametricBoundingBox.IsNotNull()); return m_ParametricBoundingBox->GetBounds(); } //##Documentation //## @brief Get the parametric extent //## //## See AbstractTransformGeometry for an example usage of this. mitk::ScalarType GetParametricExtent(int direction) const { assert(direction>=0 && direction<3); assert(m_ParametricBoundingBox.IsNotNull()); BoundingBoxType::BoundsArrayType bounds = m_ParametricBoundingBox->GetBounds(); return bounds[direction*2+1]-bounds[direction*2]; } //##Documentation //## @brief Get the parametric extent in mm //## //## See AbstractTransformGeometry for an example usage of this. virtual mitk::ScalarType GetParametricExtentInMM(int direction) const { return GetExtentInMM(direction); } //##Documentation //## @brief Get the parametric transform //## //## See AbstractTransformGeometry for an example usage of this. virtual const Transform3D* GetParametricTransform() const { return m_IndexToWorldTransform; } //##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 clones the geometry //## //## Overwrite in all sub-classes. //## Normally looks like: //## \code //## Self::Pointer newGeometry = new Self(*this); //## newGeometry->UnRegister(); //## return newGeometry.GetPointer(); //## \endcode virtual AffineGeometryFrame3D::Pointer Clone() const; //##Documentation //##@brief executes affine operations (translate, rotate, scale) virtual void ExecuteOperation(Operation* operation); protected: Geometry3D(); Geometry3D(const Geometry3D& other); - static const char* GetTransformAsString( TransformType* transformType ); + static const std::string GetTransformAsString( TransformType* transformType ); virtual ~Geometry3D(); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; virtual void BackTransform(const mitk::Point3D& in, mitk::Point3D& out) const; //##Documentation //## @brief Deprecated virtual void BackTransform(const mitk::Point3D& at, const mitk::Vector3D& in, mitk::Vector3D& out) const; //Without redundant parameter Point3D virtual void BackTransform(const mitk::Vector3D& in, mitk::Vector3D& out) const; //##Documentation //## @brief Set the parametric bounds //## //## Protected in this class, made public in some sub-classes, e.g., //## ExternAbstractTransformGeometry. virtual void SetParametricBounds(const BoundingBox::BoundsArrayType& bounds); /** Resets sub-transforms that compose m_IndexToWorldTransform, by using * the current value of m_IndexToWorldTransform and setting the rotation * component to zero. */ virtual void ResetSubTransforms(); mutable mitk::BoundingBox::Pointer m_ParametricBoundingBox; mutable mitk::TimeBounds m_TimeBounds; vtkMatrix4x4* m_VtkMatrix; bool m_ImageGeometry; //##Documentation //## @brief Spacing of the data. Only significant if the geometry describes //## an Image (m_ImageGeometry==true). mitk::Vector3D m_Spacing; bool m_Valid; unsigned int m_FrameOfReferenceID; static const std::string INDEX_TO_OBJECT_TRANSFORM; static const std::string OBJECT_TO_NODE_TRANSFORM; static const std::string INDEX_TO_NODE_TRANSFORM; static const std::string INDEX_TO_WORLD_TRANSFORM; private: mutable TransformType::Pointer m_InvertedTransform; mutable unsigned long m_IndexToWorldTransformLastModified; VnlQuaternionType m_RotationQuaternion; float m_FloatSpacing[3]; vtkMatrixToLinearTransform* m_VtkIndexToWorldTransform; //##Documentation //## @brief Origin, i.e. upper-left corner of the plane //## Point3D m_Origin; }; } // namespace mitk #endif /* GEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Core/Code/DataManagement/mitkImage.cpp b/Core/Code/DataManagement/mitkImage.cpp index 6c052b74df..c4e1929abb 100644 --- a/Core/Code/DataManagement/mitkImage.cpp +++ b/Core/Code/DataManagement/mitkImage.cpp @@ -1,1275 +1,1279 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkImage.h" #include "mitkImageStatisticsHolder.h" #include "mitkPixelTypeMultiplex.h" #include #define FILL_C_ARRAY( _arr, _size, _value) for(unsigned int i=0u; i<_size; i++) \ { _arr[i] = _value; } mitk::Image::Image() : m_Dimension(0), m_Dimensions(NULL), m_ImageDescriptor(NULL), m_OffsetTable(NULL), m_CompleteData(NULL), m_ImageStatistics(NULL) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY( m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); m_Initialized = false; } mitk::Image::Image(const Image &other) : SlicedData(other), m_Dimension(0), m_Dimensions(NULL), m_ImageDescriptor(NULL), m_OffsetTable(NULL), m_CompleteData(NULL), m_ImageStatistics(NULL) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY( m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); this->Initialize( other.GetPixelType(), other.GetDimension(), other.GetDimensions()); + //Since the above called "Initialize" method doesn't take the geometry into account we need to set it + //here manually + this->SetGeometry(dynamic_cast(other.GetGeometry()->Clone().GetPointer())); + if (this->GetDimension() > 3) { const unsigned int time_steps = this->GetDimension(3); for (unsigned int i = 0u; i < time_steps; ++i) { ImageDataItemPointer volume = const_cast(other).GetVolumeData(i); this->SetVolume(volume->GetData(), i); } } else { ImageDataItemPointer volume = const_cast(other).GetVolumeData(0); this->SetVolume(volume->GetData(), 0); } } mitk::Image::~Image() { Clear(); m_ReferenceCountLock.Lock(); m_ReferenceCount = 3; m_ReferenceCountLock.Unlock(); m_ReferenceCountLock.Lock(); m_ReferenceCount = 0; m_ReferenceCountLock.Unlock(); if(m_OffsetTable != NULL) delete [] m_OffsetTable; if(m_ImageStatistics != NULL) delete m_ImageStatistics; } const mitk::PixelType mitk::Image::GetPixelType(int n) const { return this->m_ImageDescriptor->GetChannelTypeById(n); } unsigned int mitk::Image::GetDimension() const { return m_Dimension; } unsigned int mitk::Image::GetDimension(int i) const { if((i>=0) && (i<(int)m_Dimension)) return m_Dimensions[i]; return 1; } void* mitk::Image::GetData() { if(m_Initialized==false) { if(GetSource().IsNull()) return NULL; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } m_CompleteData=GetChannelData(); // update channel's data // if data was not available at creation point, the m_Data of channel descriptor is NULL // if data present, it won't be overwritten m_ImageDescriptor->GetChannelDescriptor(0).SetData(m_CompleteData->GetData()); return m_CompleteData->GetData(); } template void AccessPixel( const mitk::PixelType ptype, void* data, const unsigned int offset, double& value ) { value = 0.0; if( data == NULL ) return; if(ptype.GetBpe() != 24) { value = (double) (((T*) data)[ offset ]); } else { const unsigned int rgboffset = 3 * offset; double returnvalue = (((T*) data)[rgboffset ]); returnvalue += (((T*) data)[rgboffset + 1]); returnvalue += (((T*) data)[rgboffset + 2]); value = returnvalue; } } double mitk::Image::GetPixelValueByIndex(const mitk::Index3D &position, unsigned int timestep) { double value = 0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } value = 0.0; const unsigned int* imageDims = this->m_ImageDescriptor->GetDimensions(); const mitk::PixelType ptype = this->m_ImageDescriptor->GetChannelTypeById(0); // Comparison ?>=0 not needed since all position[i] and timestep are unsigned int // (position[0]>=0 && position[1] >=0 && position[2]>=0 && timestep>=0) // && if ( (unsigned int)position[0] < imageDims[0] && (unsigned int)position[1] < imageDims[1] && ( (imageDims[2] == 0) || (unsigned int)position[2] < imageDims[2]) // in case a 2D Image passed in, the third dimension could be set to 0 causing the if() to fail /*&& (unsigned int)timestep < imageDims[3]*/ ) { const unsigned int offset = position[0] + position[1]*imageDims[0] + position[2]*imageDims[0]*imageDims[1] + timestep*imageDims[0]*imageDims[1]*imageDims[2]; mitkPixelTypeMultiplex3( AccessPixel, ptype, this->GetData(), offset, value ); } return value; } double mitk::Image::GetPixelValueByWorldCoordinate(const mitk::Point3D& position, unsigned int timestep) { double value = 0.0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } Index3D itkIndex; this->GetGeometry()->WorldToIndex(position, itkIndex); const unsigned int* imageDims = this->m_ImageDescriptor->GetDimensions(); const mitk::PixelType ptype = this->m_ImageDescriptor->GetChannelTypeById(0); //if ( (itkIndex[0]>=0 && itkIndex[1] >=0 && itkIndex[2]>=0 && timestep>=0) // && // lines above taken from comparison since always true due to unsigned type - if((unsigned int)itkIndex[0] < imageDims[0] && + if (((unsigned int)itkIndex[0] < imageDims[0] && (unsigned int)itkIndex[1] < imageDims[1] && - (imageDims[2] == 0) || ((unsigned int)itkIndex[2] < imageDims[2])) // in case a 2D Image passed in, the third dimension could be set to 0 causing the if() to fail + (imageDims[2] == 0) ) || ((unsigned int)itkIndex[2] < imageDims[2])) // in case a 2D Image passed in, the third dimension could be set to 0 causing the if() to fail { const unsigned int offset = itkIndex[0] + itkIndex[1]*imageDims[0] + itkIndex[2]*imageDims[0]*imageDims[1] + timestep*imageDims[0]*imageDims[1]*imageDims[2]; mitkPixelTypeMultiplex3( AccessPixel, ptype, this->GetData(), offset, value ); } return value; } vtkImageData* mitk::Image::GetVtkImageData(int t, int n) { if(m_Initialized==false) { if(GetSource().IsNull()) return NULL; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume=GetVolumeData(t, n); if(volume.GetPointer()==NULL || volume->GetVtkImageData() == NULL) return NULL; float *fspacing = const_cast(GetSlicedGeometry(t)->GetFloatSpacing()); double dspacing[3] = {fspacing[0],fspacing[1],fspacing[2]}; volume->GetVtkImageData()->SetSpacing( dspacing ); return volume->GetVtkImageData(); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidSlice(s,t,n)==false) return NULL; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // slice directly available? int pos=GetSliceIndex(s,t,n); if(m_Slices[pos].GetPointer()!=NULL) return m_Slices[pos]; // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) { sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // is slice available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) { sl=new ImageDataItem(*ch, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, (((size_t) s)*m_OffsetTable[2]+((size_t) t)*m_OffsetTable[3])*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // slice is unavailable. Can we calculate it? if((GetSource().IsNotNull()) && (GetSource()->Updating()==false)) { // ... wir mussen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, s); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, 1); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); if(IsSliceSet(s,t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetSliceData(s,t,n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateSliceData(s,t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidVolume(t,n)==false) return NULL; ImageDataItemPointer ch, vol; // volume directly available? int pos=GetVolumeIndex(t,n); vol=m_Volumes[pos]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return vol; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) { vol=new ImageDataItem(*ch, m_ImageDescriptor, 3, data, importMemoryManagement == ManageMemory, (((size_t) t)*m_OffsetTable[3])*(ptypeSize)); vol->SetComplete(true); return m_Volumes[pos]=vol; } // let's see if all slices of the volume are set, so that we can (could) combine them to a volume bool complete=true; unsigned int s; for(s=0;sSetComplete(true); } else { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); vol=m_Volumes[pos]; // ok, let's combine the slices! if(vol.GetPointer()==NULL) vol=new ImageDataItem( chPixelType, 3, m_Dimensions, NULL, true); vol->SetComplete(true); size_t size=m_OffsetTable[2]*(ptypeSize); for(s=0;sGetParent()!=vol) { // copy data of slices in volume size_t offset = ((size_t) s)*size; std::memcpy(static_cast(vol->GetData())+offset, sl->GetData(), size); // FIXME mitkIpPicDescriptor * pic = sl->GetPicDescriptor(); // replace old slice with reference to volume sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*size); sl->SetComplete(true); //mitkIpFuncCopyTags(sl->GetPicDescriptor(), pic); m_Slices[posSl]=sl; } } //if(vol->GetPicDescriptor()->info->tags_head==NULL) // mitkIpFuncCopyTags(vol->GetPicDescriptor(), m_Slices[GetSliceIndex(0,t,n)]->GetPicDescriptor()); } return m_Volumes[pos]=vol; } // volume is unavailable. Can we calculate it? if((GetSource().IsNotNull()) && (GetSource()->Updating()==false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); if(IsVolumeSet(t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetVolumeData(t,n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateVolumeData(t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidChannel(n)==false) return NULL; ImageDataItemPointer ch, vol; ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return ch; // let's see if all volumes are set, so that we can (could) combine them to a channel if(IsChannelSet(n)) { // if there is only one time frame we do not need to combine anything if(m_Dimensions[3]<=1) { vol=GetVolumeData(0,n,data,importMemoryManagement); ch=new ImageDataItem(*vol, m_ImageDescriptor, m_ImageDescriptor->GetNumberOfDimensions(), data, importMemoryManagement == ManageMemory); ch->SetComplete(true); } else { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch=m_Channels[n]; // ok, let's combine the volumes! if(ch.GetPointer()==NULL) ch=new ImageDataItem(this->m_ImageDescriptor, NULL, true); ch->SetComplete(true); size_t size=m_OffsetTable[m_Dimension-1]*(ptypeSize); unsigned int t; ImageDataItemPointerArray::iterator slicesIt = m_Slices.begin()+n*m_Dimensions[2]*m_Dimensions[3]; for(t=0;tGetParent()!=ch) { // copy data of volume in channel size_t offset = ((size_t) t)*m_OffsetTable[3]*(ptypeSize); std::memcpy(static_cast(ch->GetData())+offset, vol->GetData(), size); // REVEIW FIX mitkIpPicDescriptor * pic = vol->GetPicDescriptor(); // replace old volume with reference to channel vol=new ImageDataItem(*ch, m_ImageDescriptor, 3, data, importMemoryManagement == ManageMemory, offset); vol->SetComplete(true); //mitkIpFuncCopyTags(vol->GetPicDescriptor(), pic); m_Volumes[posVol]=vol; // get rid of slices - they may point to old volume ImageDataItemPointer dnull=NULL; for(unsigned int i = 0; i < m_Dimensions[2]; ++i, ++slicesIt) { assert(slicesIt != m_Slices.end()); *slicesIt = dnull; } } } // REVIEW FIX // if(ch->GetPicDescriptor()->info->tags_head==NULL) // mitkIpFuncCopyTags(ch->GetPicDescriptor(), m_Volumes[GetVolumeIndex(0,n)]->GetPicDescriptor()); } return m_Channels[n]=ch; } // channel is unavailable. Can we calculate it? if((GetSource().IsNotNull()) && (GetSource()->Updating()==false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, 0); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, m_Dimensions[3]); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); // did it work? if(IsChannelSet(n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetChannelData(n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateChannelData(n,data,importMemoryManagement); item->SetComplete(true); return item; } } bool mitk::Image::IsSliceSet(int s, int t, int n) const { if(IsValidSlice(s,t,n)==false) return false; if(m_Slices[GetSliceIndex(s,t,n)].GetPointer()!=NULL) return true; ImageDataItemPointer ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return true; ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return true; return false; } bool mitk::Image::IsVolumeSet(int t, int n) const { if(IsValidVolume(t,n)==false) return false; ImageDataItemPointer ch, vol; // volume directly available? vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return true; // is volume available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return true; // let's see if all slices of the volume are set, so that we can (could) combine them to a volume unsigned int s; for(s=0;sIsComplete())) return true; // let's see if all volumes are set, so that we can (could) combine them to a channel unsigned int t; for(t=0;t(data), s, t, n, CopyMemory); } bool mitk::Image::SetVolume(const void *data, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportVolume(const_cast(data), t, n, CopyMemory); } bool mitk::Image::SetChannel(const void *data, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportChannel(const_cast(data), n, CopyMemory); } bool mitk::Image::SetImportSlice(void *data, int s, int t, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidSlice(s,t,n)==false) return false; ImageDataItemPointer sl; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); if(IsSliceSet(s,t,n)) { sl=GetSliceData(s,t,n,data,importMemoryManagement); if(sl->GetManageMemory()==false) { sl=AllocateSliceData(s,t,n,data,importMemoryManagement); if(sl.GetPointer()==NULL) return false; } if ( sl->GetData() != data ) std::memcpy(sl->GetData(), data, m_OffsetTable[2]*(ptypeSize)); sl->Modified(); //we have changed the data: call Modified()! Modified(); } else { sl=AllocateSliceData(s,t,n,data,importMemoryManagement); if(sl.GetPointer()==NULL) return false; if ( sl->GetData() != data ) std::memcpy(sl->GetData(), data, m_OffsetTable[2]*(ptypeSize)); //we just added a missing slice, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportVolume(void *data, int t, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidVolume(t,n)==false) return false; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer vol; if(IsVolumeSet(t,n)) { vol=GetVolumeData(t,n,data,importMemoryManagement); if(vol->GetManageMemory()==false) { vol=AllocateVolumeData(t,n,data,importMemoryManagement); if(vol.GetPointer()==NULL) return false; } if ( vol->GetData() != data ) std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); vol->Modified(); vol->SetComplete(true); //we have changed the data: call Modified()! Modified(); } else { vol=AllocateVolumeData(t,n,data,importMemoryManagement); if(vol.GetPointer()==NULL) return false; if ( vol->GetData() != data ) { std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); } vol->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData( vol->GetData() ); //we just added a missing Volume, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportChannel(void *data, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidChannel(n)==false) return false; // channel descriptor const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer ch; if(IsChannelSet(n)) { ch=GetChannelData(n,data,importMemoryManagement); if(ch->GetManageMemory()==false) { ch=AllocateChannelData(n,data,importMemoryManagement); if(ch.GetPointer()==NULL) return false; } if ( ch->GetData() != data ) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); ch->Modified(); ch->SetComplete(true); //we have changed the data: call Modified()! Modified(); } else { ch=AllocateChannelData(n,data,importMemoryManagement); if(ch.GetPointer()==NULL) return false; if ( ch->GetData() != data ) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); ch->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData( ch->GetData() ); //we just added a missing Channel, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } void mitk::Image::Initialize() { ImageDataItemPointerArray::iterator it, end; for( it=m_Slices.begin(), end=m_Slices.end(); it!=end; ++it ) { (*it)=NULL; } for( it=m_Volumes.begin(), end=m_Volumes.end(); it!=end; ++it ) { (*it)=NULL; } for( it=m_Channels.begin(), end=m_Channels.end(); it!=end; ++it ) { (*it)=NULL; } m_CompleteData = NULL; if( m_ImageStatistics == NULL) { m_ImageStatistics = new mitk::ImageStatisticsHolder( this ); } SetRequestedRegionToLargestPossibleRegion(); } void mitk::Image::Initialize(const mitk::ImageDescriptor::Pointer inDesc) { // store the descriptor this->m_ImageDescriptor = inDesc; // initialize image this->Initialize( inDesc->GetChannelDescriptor(0).GetPixelType(), inDesc->GetNumberOfDimensions(), inDesc->GetDimensions(), 1 ); } void mitk::Image::Initialize(const mitk::PixelType& type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels) { Clear(); m_Dimension=dimension; if(!dimensions) itkExceptionMacro(<< "invalid zero dimension image"); unsigned int i; for(i=0;im_ImageDescriptor = mitk::ImageDescriptor::New(); this->m_ImageDescriptor->Initialize( this->m_Dimensions, this->m_Dimension ); for(i=0;i<4;++i) { m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize (i, m_Dimensions[i]); } m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, channels); if(m_LargestPossibleRegion.GetNumberOfPixels()==0) { delete [] m_Dimensions; m_Dimensions = NULL; return; } for( unsigned int i=0u; im_ImageDescriptor->AddNewChannel( type ); } PlaneGeometry::Pointer planegeometry = PlaneGeometry::New(); planegeometry->InitializeStandardPlane(m_Dimensions[0], m_Dimensions[1]); SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(planegeometry, m_Dimensions[2]); if(dimension>=4) { TimeBounds timebounds; timebounds[0] = 0.0; timebounds[1] = 1.0; slicedGeometry->SetTimeBounds(timebounds); } TimeSlicedGeometry::Pointer timeSliceGeometry = TimeSlicedGeometry::New(); timeSliceGeometry->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); timeSliceGeometry->ImageGeometryOn(); SetGeometry(timeSliceGeometry); ImageDataItemPointer dnull=NULL; m_Channels.assign(GetNumberOfChannels(), dnull); m_Volumes.assign(GetNumberOfChannels()*m_Dimensions[3], dnull); m_Slices.assign(GetNumberOfChannels()*m_Dimensions[3]*m_Dimensions[2], dnull); ComputeOffsetTable(); Initialize(); m_Initialized = true; } void mitk::Image::Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int channels, int tDim ) { unsigned int dimensions[5]; dimensions[0] = (unsigned int)(geometry.GetExtent(0)+0.5); dimensions[1] = (unsigned int)(geometry.GetExtent(1)+0.5); dimensions[2] = (unsigned int)(geometry.GetExtent(2)+0.5); dimensions[3] = 0; dimensions[4] = 0; unsigned int dimension = 2; if ( dimensions[2] > 1 ) dimension = 3; if ( tDim > 0) { dimensions[3] = tDim; } else { const mitk::TimeSlicedGeometry* timeGeometry = dynamic_cast(&geometry); if ( timeGeometry != NULL ) { dimensions[3] = timeGeometry->GetTimeSteps(); } } if ( dimensions[3] > 1 ) dimension = 4; Initialize( type, dimension, dimensions, channels ); SetGeometry(static_cast(geometry.Clone().GetPointer())); mitk::BoundingBox::BoundsArrayType bounds = geometry.GetBoundingBox()->GetBounds(); if( (bounds[0] != 0.0) || (bounds[2] != 0.0) || (bounds[4] != 0.0) ) { SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); mitk::Point3D origin; origin.Fill(0.0); slicedGeometry->IndexToWorld(origin, origin); bounds[1]-=bounds[0]; bounds[3]-=bounds[2]; bounds[5]-=bounds[4]; bounds[0] = 0.0; bounds[2] = 0.0; bounds[4] = 0.0; this->m_ImageDescriptor->Initialize( this->m_Dimensions, this->m_Dimension ); slicedGeometry->SetBounds(bounds); slicedGeometry->GetIndexToWorldTransform()->SetOffset(origin.Get_vnl_vector().data_block()); GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); } } void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped, unsigned int channels, int tDim ) { SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(static_cast(geometry2d.Clone().GetPointer()), sDim, flipped); Initialize(type, *slicedGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::Image* image) { Initialize(image->GetPixelType(), *image->GetTimeSlicedGeometry()); } void mitk::Image::Initialize(vtkImageData* vtkimagedata, int channels, int tDim, int sDim) { if(vtkimagedata==NULL) return; m_Dimension=vtkimagedata->GetDataDimension(); unsigned int i, *tmpDimensions=new unsigned int[m_Dimension>4?m_Dimension:4]; for(i=0;iGetDimensions()[i]; if(m_Dimension<4) { unsigned int *p; for(i=0,p=tmpDimensions+m_Dimension;i<4-m_Dimension;++i, ++p) *p=1; } if(sDim>=0) { tmpDimensions[2]=sDim; if(m_Dimension < 3) m_Dimension = 3; } if(tDim>=0) { tmpDimensions[3]=tDim; if(m_Dimension < 4) m_Dimension = 4; } switch ( vtkimagedata->GetScalarType() ) { case VTK_BIT: case VTK_CHAR: //pixelType.Initialize(typeid(char), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_CHAR: //pixelType.Initialize(typeid(unsigned char), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_SHORT: //pixelType.Initialize(typeid(short), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_SHORT: //pixelType.Initialize(typeid(unsigned short), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_INT: //pixelType.Initialize(typeid(int), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_INT: //pixelType.Initialize(typeid(unsigned int), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_LONG: //pixelType.Initialize(typeid(long), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_LONG: //pixelType.Initialize(typeid(unsigned long), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_FLOAT: //pixelType.Initialize(typeid(float), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_DOUBLE: //pixelType.Initialize(typeid(double), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; default: break; } /* Initialize(pixelType, m_Dimension, tmpDimensions, channels); */ const double *spacinglist = vtkimagedata->GetSpacing(); Vector3D spacing; FillVector3D(spacing, spacinglist[0], 1.0, 1.0); if(m_Dimension>=2) spacing[1]=spacinglist[1]; if(m_Dimension>=3) spacing[2]=spacinglist[2]; // access origin of vtkImage Point3D origin; vtkFloatingPointType vtkorigin[3]; vtkimagedata->GetOrigin(vtkorigin); FillVector3D(origin, vtkorigin[0], 0.0, 0.0); if(m_Dimension>=2) origin[1]=vtkorigin[1]; if(m_Dimension>=3) origin[2]=vtkorigin[2]; SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast(slicedGeometry->GetGeometry2D(0)); planeGeometry->SetOrigin(origin); // re-initialize SlicedGeometry3D slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); delete [] tmpDimensions; } bool mitk::Image::IsValidSlice(int s, int t, int n) const { if(m_Initialized) return ((s>=0) && (s<(int)m_Dimensions[2]) && (t>=0) && (t< (int) m_Dimensions[3]) && (n>=0) && (n< (int)GetNumberOfChannels())); else return false; } bool mitk::Image::IsValidVolume(int t, int n) const { if(m_Initialized) return IsValidSlice(0, t, n); else return false; } bool mitk::Image::IsValidChannel(int n) const { if(m_Initialized) return IsValidSlice(0, 0, n); else return false; } void mitk::Image::ComputeOffsetTable() { if(m_OffsetTable!=NULL) delete [] m_OffsetTable; m_OffsetTable=new size_t[m_Dimension>4 ? m_Dimension+1 : 4+1]; unsigned int i; size_t num=1; m_OffsetTable[0] = 1; for (i=0; i < m_Dimension; ++i) { num *= m_Dimensions[i]; m_OffsetTable[i+1] = num; } for (;i < 4; ++i) m_OffsetTable[i+1] = num; } bool mitk::Image::IsValidTimeStep(int t) const { return ( ( m_Dimension >= 4 && t <= (int)m_Dimensions[3] && t > 0 ) || (t == 0) ); } void mitk::Image::Expand(unsigned int timeSteps) { if(timeSteps < 1) itkExceptionMacro(<< "Invalid timestep in Image!"); Superclass::Expand(timeSteps); } int mitk::Image::GetSliceIndex(int s, int t, int n) const { if(IsValidSlice(s,t,n)==false) return false; return ((size_t)s)+((size_t) t)*m_Dimensions[2]+((size_t) n)*m_Dimensions[3]*m_Dimensions[2]; //?? } int mitk::Image::GetVolumeIndex(int t, int n) const { if(IsValidVolume(t,n)==false) return false; return ((size_t)t)+((size_t) n)*m_Dimensions[3]; //?? } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { int pos; pos=GetSliceIndex(s,t,n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if(vol.GetPointer()!=NULL) { sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // is slice available as part of a channel that is available? ch=m_Channels[n]; if(ch.GetPointer()!=NULL) { sl=new ImageDataItem(*ch, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, (((size_t) s)*m_OffsetTable[2]+((size_t) t)*m_OffsetTable[3])*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // allocate new volume (instead of a single slice to keep data together!) m_Volumes[GetVolumeIndex(t,n)]=vol=AllocateVolumeData(t,n,NULL,importMemoryManagement); sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; ////ALTERNATIVE: //// allocate new slice //sl=new ImageDataItem(*m_PixelType, 2, m_Dimensions); //m_Slices[pos]=sl; //return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { int pos; pos=GetVolumeIndex(t,n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ImageDataItemPointer ch, vol; ch=m_Channels[n]; if(ch.GetPointer()!=NULL) { vol=new ImageDataItem(*ch, m_ImageDescriptor, 3, data,importMemoryManagement == ManageMemory, (((size_t) t)*m_OffsetTable[3])*(ptypeSize)); return m_Volumes[pos]=vol; } mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); // allocate new volume if(importMemoryManagement == CopyMemory) { vol=new ImageDataItem( chPixelType, 3, m_Dimensions, NULL, true); if(data != NULL) std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); } else { vol=new ImageDataItem( chPixelType, 3, m_Dimensions, data, importMemoryManagement == ManageMemory); } m_Volumes[pos]=vol; return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) { ImageDataItemPointer ch; // allocate new channel if(importMemoryManagement == CopyMemory) { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch=new ImageDataItem(this->m_ImageDescriptor, NULL, true); if(data != NULL) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); } else { ch=new ImageDataItem(this->m_ImageDescriptor, data, importMemoryManagement == ManageMemory); } m_Channels[n]=ch; return ch; } unsigned int* mitk::Image::GetDimensions() const { return m_Dimensions; } void mitk::Image::Clear() { Superclass::Clear(); delete [] m_Dimensions; m_Dimensions = NULL; } void mitk::Image::SetGeometry(Geometry3D* aGeometry3D) { // Please be aware of the 0.5 offset/pixel-center issue! See Geometry documentation for further information if(aGeometry3D->GetImageGeometry()==false) { MITK_INFO << "WARNING: Applied a non-image geometry onto an image. Please be SURE that this geometry is pixel-center-based! If it is not, you need to call Geometry3D->ChangeImageGeometryConsideringOriginOffset(true) before calling image->setGeometry(..)\n"; } Superclass::SetGeometry(aGeometry3D); GetTimeSlicedGeometry()->ImageGeometryOn(); } void mitk::Image::PrintSelf(std::ostream& os, itk::Indent indent) const { unsigned char i; if(m_Initialized) { os << indent << " Dimension: " << m_Dimension << std::endl; os << indent << " Dimensions: "; for(i=0; i < m_Dimension; ++i) os << GetDimension(i) << " "; os << std::endl; for(unsigned int ch=0; ch < this->m_ImageDescriptor->GetNumberOfChannels(); ch++) { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(ch); os << indent << " Channel: " << this->m_ImageDescriptor->GetChannelName(ch) << std::endl; os << indent << " PixelType: " << chPixelType.GetTypeId().name() << std::endl; os << indent << " BitsPerElement: " << chPixelType.GetSize() << std::endl; os << indent << " NumberOfComponents: " << chPixelType.GetNumberOfComponents() << std::endl; os << indent << " BitsPerComponent: " << chPixelType.GetBitsPerComponent() << std::endl; } } else { os << indent << " Image not initialized: m_Initialized: false" << std::endl; } Superclass::PrintSelf(os,indent); } bool mitk::Image::IsRotated() const { const mitk::Geometry3D* geo = this->GetGeometry(); bool ret = false; if(geo) { const vnl_matrix_fixed & mx = geo->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); float ref = 0; for(short k = 0; k < 3; ++k) ref += mx[k][k]; ref/=1000; // Arbitrary value; if a non-diagonal (nd) element is bigger then this, matrix is considered nd. for(short i = 0; i < 3; ++i) { for(short j = 0; j < 3; ++j) { if(i != j) { if(abs(mx[i][j]) > ref) // matrix is nd ret = true; } } } } return ret; } #include "mitkImageStatisticsHolder.h" //##Documentation mitk::ScalarType mitk::Image::GetScalarValueMin(int t) const { return m_ImageStatistics->GetScalarValueMin(t); } //##Documentation //## \brief Get the maximum for scalar images mitk::ScalarType mitk::Image::GetScalarValueMax(int t) const { return m_ImageStatistics->GetScalarValueMax(t); } //##Documentation //## \brief Get the second smallest value for scalar images mitk::ScalarType mitk::Image::GetScalarValue2ndMin(int t) const { return m_ImageStatistics->GetScalarValue2ndMin(t); } mitk::ScalarType mitk::Image::GetScalarValueMinNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValueMinNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMinNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValue2ndMinNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMax(int t) const { return m_ImageStatistics->GetScalarValue2ndMax(t); } mitk::ScalarType mitk::Image::GetScalarValueMaxNoRecompute( unsigned int t) const { return m_ImageStatistics->GetScalarValueMaxNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMaxNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValue2ndMaxNoRecompute(t); } mitk::ScalarType mitk::Image::GetCountOfMinValuedVoxels(int t ) const { return m_ImageStatistics->GetCountOfMinValuedVoxels(t); } mitk::ScalarType mitk::Image::GetCountOfMaxValuedVoxels(int t) const { return m_ImageStatistics->GetCountOfMaxValuedVoxels(t); } unsigned int mitk::Image::GetCountOfMaxValuedVoxelsNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetCountOfMaxValuedVoxelsNoRecompute(t); } unsigned int mitk::Image::GetCountOfMinValuedVoxelsNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetCountOfMinValuedVoxelsNoRecompute(t); } diff --git a/Core/Code/DataManagement/mitkPointSet.cpp b/Core/Code/DataManagement/mitkPointSet.cpp index 2a3828844b..8abce42872 100755 --- a/Core/Code/DataManagement/mitkPointSet.cpp +++ b/Core/Code/DataManagement/mitkPointSet.cpp @@ -1,780 +1,787 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkPointSet.h" #include "mitkPointOperation.h" #include "mitkInteractionConst.h" mitk::PointSet::PointSet() { this->InitializeEmpty(); } -mitk::PointSet::PointSet(const PointSet &other): BaseData(other), -m_PointSetSeries(other.m_PointSetSeries), -m_CalculateBoundingBox(other.m_CalculateBoundingBox) +mitk::PointSet::PointSet(const PointSet &other): BaseData(other) { + this->Expand(other.GetTimeSteps()); + for (int t=0; t < other.GetTimeSteps(); t++) + { + for (int i=0; i< other.GetSize(t); i++) + { + this->InsertPoint(i, other.GetPoint(i,t), t); + } + } + this->SetGeometry(other.GetGeometry()); } mitk::PointSet::~PointSet() { this->ClearData(); } void mitk::PointSet::ClearData() { m_PointSetSeries.clear(); Superclass::ClearData(); } void mitk::PointSet::InitializeEmpty() { m_PointSetSeries.resize( 1 ); m_PointSetSeries[0] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[0]->SetPointData( pointData ); m_CalculateBoundingBox = false; Superclass::InitializeTimeSlicedGeometry(1); m_Initialized = true; } bool mitk::PointSet::IsEmptyTimeStep(unsigned int t) const { return IsInitialized() && (GetSize(t) == 0); } void mitk::PointSet::Expand( unsigned int timeSteps ) { // Check if the vector is long enough to contain the new element // at the given position. If not, expand it with sufficient pre-initialized // elements. // // NOTE: This method will never REDUCE the vector size; it should only // be used to make sure that the vector has enough elements to include the // specified time step. unsigned int oldSize = m_PointSetSeries.size(); if ( timeSteps > oldSize ) { Superclass::Expand( timeSteps ); m_PointSetSeries.resize( timeSteps ); for ( unsigned int i = oldSize; i < timeSteps; ++i ) { m_PointSetSeries[i] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[i]->SetPointData( pointData ); } //if the size changes, then compute the bounding box m_CalculateBoundingBox = true; this->InvokeEvent( PointSetExtendTimeRangeEvent() ); } } unsigned int mitk::PointSet::GetPointSetSeriesSize() const { return m_PointSetSeries.size(); } int mitk::PointSet::GetSize( unsigned int t ) const { if ( t < m_PointSetSeries.size() ) { return m_PointSetSeries[t]->GetNumberOfPoints(); } else { return 0; } } mitk::PointSet::DataType::Pointer mitk::PointSet::GetPointSet( int t ) const { if ( t < (int)m_PointSetSeries.size() ) { return m_PointSetSeries[t]; } else { return NULL; } } int mitk::PointSet::SearchPoint( Point3D point, float distance, int t ) const { if ( t >= (int)m_PointSetSeries.size() ) { return -1; } // Out is the point which is checked to be the searched point PointType out; out.Fill( 0 ); PointType indexPoint; this->GetGeometry( t )->WorldToIndex(point, indexPoint); // Searching the first point in the Set, that is +- distance far away fro // the given point unsigned int i; PointsContainer::Iterator it, end; end = m_PointSetSeries[t]->GetPoints()->End(); int bestIndex = -1; distance = distance * distance; // To correct errors from converting index to world and world to index if (distance == 0.0) { distance = 0.000001; } ScalarType bestDist = distance; ScalarType dist, tmp; for ( it = m_PointSetSeries[t]->GetPoints()->Begin(), i = 0; it != end; ++it, ++i ) { bool ok = m_PointSetSeries[t]->GetPoints() ->GetElementIfIndexExists( it->Index(), &out ); if ( !ok ) { return -1; } else if ( indexPoint == out ) //if totally equal { return it->Index(); } //distance calculation tmp = out[0] - indexPoint[0]; dist = tmp * tmp; tmp = out[1] - indexPoint[1]; dist += tmp * tmp; tmp = out[2] - indexPoint[2]; dist += tmp * tmp; if ( dist < bestDist ) { bestIndex = it->Index(); bestDist = dist; } } return bestIndex; } mitk::PointSet::PointType mitk::PointSet::GetPoint( PointIdentifier id, int t ) const { PointType out; out.Fill(0); if ( (unsigned int) t >= m_PointSetSeries.size() ) { return out; } if ( m_PointSetSeries[t]->GetPoints()->IndexExists(id) ) { m_PointSetSeries[t]->GetPoint( id, &out ); this->GetGeometry(t)->IndexToWorld( out, out ); return out; } else { return out; } } bool mitk::PointSet ::GetPointIfExists( PointIdentifier id, PointType* point, int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return false; } if ( m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(id, point) ) { this->GetGeometry( t )->IndexToWorld( *point, *point ); return true; } else { return false; } } void mitk::PointSet::SetPoint( PointIdentifier id, PointType point, int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); mitk::Point3D indexPoint; this->GetGeometry( t )->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->SetPoint( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->SetPointData( id, defaultPointData ); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::SetPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); mitk::Point3D indexPoint; this->GetGeometry( t )->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->SetPoint( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->SetPointData( id, defaultPointData ); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::InsertPoint( PointIdentifier id, PointType point, int t ) { if ( (unsigned int) t < m_PointSetSeries.size() ) { mitk::Point3D indexPoint; mitk::Geometry3D* tempGeometry = this->GetGeometry( t ); if (tempGeometry == NULL) { MITK_INFO<< __FILE__ << ", l." << __LINE__ << ": GetGeometry of "<< t <<" returned NULL!" << std::endl; return; } tempGeometry->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->GetPoints()->InsertElement( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->GetPointData()->InsertElement(id, defaultPointData); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } } void mitk::PointSet::InsertPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t ) { if ( (unsigned int) t < m_PointSetSeries.size() ) { mitk::Point3D indexPoint; mitk::Geometry3D* tempGeometry = this->GetGeometry( t ); if (tempGeometry == NULL) { MITK_INFO<< __FILE__ << ", l." << __LINE__ << ": GetGeometry of "<< t <<" returned NULL!" << std::endl; return; } tempGeometry->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->GetPoints()->InsertElement( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->GetPointData()->InsertElement(id, defaultPointData); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } } bool mitk::PointSet::SwapPointPosition( PointIdentifier id, bool moveUpwards, int t ) { if(IndexExists(id, t) ) { PointType point = GetPoint(id,t); if(moveUpwards) {//up if(IndexExists(id-1,t)) { InsertPoint(id, GetPoint(id - 1, t), t); InsertPoint(id-1,point,t); this->Modified(); return true; } } else {//down if(IndexExists(id+1,t)) { InsertPoint(id, GetPoint(id + 1, t), t); InsertPoint(id+1,point,t); this->Modified(); return true; } } } return false; } bool mitk::PointSet::IndexExists( int position, int t ) const { if ( (unsigned int) t < m_PointSetSeries.size() ) { return m_PointSetSeries[t]->GetPoints()->IndexExists( position ); } else { return false; } } bool mitk::PointSet::GetSelectInfo( int position, int t ) const { if ( this->IndexExists( position, t ) ) { PointDataType pointData = { 0, false, PTUNDEFINED }; m_PointSetSeries[t]->GetPointData( position, &pointData ); return pointData.selected; } else { return false; } } void mitk::PointSet::SetSelectInfo( int position, bool selected, int t ) { if ( this->IndexExists( position, t ) ) { // timeStep to ms ScalarType timeInMS = this->GetTimeSlicedGeometry()->TimeStepToMS( t ); // point Point3D point = this->GetPoint( position, t ); PointOperation* op; if (selected) { op = new mitk::PointOperation(OpSELECTPOINT, timeInMS, point, position ); } else { op = new mitk::PointOperation(OpDESELECTPOINT, timeInMS, point, position ); } this->ExecuteOperation( op ); } } mitk::PointSpecificationType mitk::PointSet::GetSpecificationTypeInfo( int position, int t ) const { if ( this->IndexExists( position, t ) ) { PointDataType pointData = { 0, false, PTUNDEFINED }; m_PointSetSeries[t]->GetPointData( position, &pointData ); return pointData.pointSpec; } else { return PTUNDEFINED; } } int mitk::PointSet::GetNumberOfSelected( int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return 0; } int numberOfSelected = 0; PointDataIterator it; for ( it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++ ) { if (it->Value().selected == true) { ++numberOfSelected; } } return numberOfSelected; } int mitk::PointSet::SearchSelectedPoint( int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return -1; } PointDataIterator it; for ( it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++ ) { if ( it->Value().selected == true ) { return it->Index(); } } return -1; } void mitk::PointSet::ExecuteOperation( Operation* operation ) { int timeStep = -1; mitkCheckOperationTypeMacro(PointOperation, operation, pointOp); if ( pointOp ) { timeStep = this->GetTimeSlicedGeometry() ->MSToTimeStep( pointOp->GetTimeInMS() ); } if ( timeStep < 0 ) { MITK_ERROR << "Time step (" << timeStep << ") outside of PointSet time bounds" << std::endl; return; } switch (operation->GetOperationType()) { case OpNOTHING: break; case OpINSERT://inserts the point at the given position and selects it. { int position = pointOp->GetIndex(); PointType pt; pt.CastFrom(pointOp->GetPoint()); //transfer from world to index coordinates mitk::Geometry3D* geometry = this->GetGeometry( timeStep ); if (geometry == NULL) { MITK_INFO<<"GetGeometry returned NULL!\n"; return; } geometry->WorldToIndex(pt, pt); m_PointSetSeries[timeStep]->GetPoints()->InsertElement(position, pt); PointDataType pointData = { pointOp->GetIndex(), pointOp->GetSelected(), pointOp->GetPointType() }; m_PointSetSeries[timeStep]->GetPointData() ->InsertElement(position, pointData); this->Modified(); //boundingbox has to be computed m_CalculateBoundingBox = true; this->InvokeEvent( PointSetAddEvent() ); this->OnPointSetChange(); } break; case OpMOVE://moves the point given by index { PointType pt; pt.CastFrom(pointOp->GetPoint()); //transfer from world to index coordinates this->GetGeometry( timeStep )->WorldToIndex(pt, pt); // Copy new point into container m_PointSetSeries[timeStep]->SetPoint(pointOp->GetIndex(), pt); // Insert a default point data object to keep the containers in sync // (if no point data object exists yet) PointDataType pointData; if ( !m_PointSetSeries[timeStep]->GetPointData( pointOp->GetIndex(), &pointData ) ) { m_PointSetSeries[timeStep]->SetPointData( pointOp->GetIndex(), pointData ); } this->OnPointSetChange(); this->Modified(); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent( PointSetMoveEvent() ); } break; case OpREMOVE://removes the point at given by position { m_PointSetSeries[timeStep]->GetPoints()->DeleteIndex((unsigned)pointOp->GetIndex()); m_PointSetSeries[timeStep]->GetPointData()->DeleteIndex((unsigned)pointOp->GetIndex()); this->OnPointSetChange(); this->Modified(); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent( PointSetRemoveEvent() ); } break; case OpSELECTPOINT://select the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = true; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpDESELECTPOINT://unselect the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = false; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpSETPOINTTYPE: { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.pointSpec = pointOp->GetPointType(); m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpMOVEPOINTUP: // swap content of point with ID pointOp->GetIndex() with the point preceding it in the container // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that precedes this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); PointsContainer::STLContainerType::iterator it = points.find(currentID); if (it == points.end()) // ID not found break; if (it == points.begin()) // we are at the first element, there is no previous element break; /* get and cache current point & pointdata and previous point & pointdata */ --it; PointIdentifier prevID = it->first; if (this->SwapPointContents(prevID, currentID, timeStep) == true) this->Modified(); } break; case OpMOVEPOINTDOWN: // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that succeeds this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); PointsContainer::STLContainerType::iterator it = points.find(currentID); if (it == points.end()) // ID not found break; ++it; if (it == points.end()) // ID is already the last element, there is no succeeding element break; /* get and cache current point & pointdata and previous point & pointdata */ PointIdentifier nextID = it->first; if (this->SwapPointContents(nextID, currentID, timeStep) == true) this->Modified(); } break; default: itkWarningMacro("mitkPointSet could not understrand the operation. Please check!"); break; } //to tell the mappers, that the data is modified and has to be updated //only call modified if anything is done, so call in cases //this->Modified(); mitk::OperationEndEvent endevent(operation); ((const itk::Object*)this)->InvokeEvent(endevent); //*todo has to be done here, cause of update-pipeline not working yet // As discussed lately, don't mess with the rendering from inside data structures //mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PointSet::UpdateOutputInformation() { if ( this->GetSource( ) ) { this->GetSource( )->UpdateOutputInformation( ); } // // first make sure, that the associated time sliced geometry has // the same number of geometry 3d's as PointSets are present // mitk::TimeSlicedGeometry* timeGeometry = GetTimeSlicedGeometry(); if ( timeGeometry->GetTimeSteps() != m_PointSetSeries.size() ) { itkExceptionMacro(<<"timeGeometry->GetTimeSteps() != m_PointSetSeries.size() -- use Initialize(timeSteps) with correct number of timeSteps!"); } // This is needed to detect zero objects mitk::ScalarType nullpoint[]={0,0,0,0,0,0}; BoundingBox::BoundsArrayType itkBoundsNull(nullpoint); // // Iterate over the PointSets and update the Geometry // information of each of the items. // if (m_CalculateBoundingBox) { for ( unsigned int i = 0 ; i < m_PointSetSeries.size() ; ++i ) { const DataType::BoundingBoxType *bb = m_PointSetSeries[i]->GetBoundingBox(); BoundingBox::BoundsArrayType itkBounds = bb->GetBounds(); if ( m_PointSetSeries[i].IsNull() || (m_PointSetSeries[i]->GetNumberOfPoints() == 0) || (itkBounds == itkBoundsNull) ) { itkBounds = itkBoundsNull; continue; } // Ensure minimal bounds of 1.0 in each dimension for ( unsigned int j = 0; j < 3; ++j ) { if ( itkBounds[j*2+1] - itkBounds[j*2] < 1.0 ) { BoundingBox::CoordRepType center = (itkBounds[j*2] + itkBounds[j*2+1]) / 2.0; itkBounds[j*2] = center - 0.5; itkBounds[j*2+1] = center + 0.5; } } this->GetGeometry(i)->SetBounds(itkBounds); } m_CalculateBoundingBox = false; } this->GetTimeSlicedGeometry()->UpdateInformation(); } void mitk::PointSet::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::PointSet::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::PointSet::VerifyRequestedRegion() { return true; } void mitk::PointSet::SetRequestedRegion( itk::DataObject * ) { } void mitk::PointSet::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os, indent); os << indent << "Number timesteps: " << m_PointSetSeries.size() << "\n"; unsigned int i = 0; for (PointSetSeries::const_iterator it = m_PointSetSeries.begin(); it != m_PointSetSeries.end(); ++it) { os << indent << "Timestep " << i++ << ": \n"; MeshType::Pointer ps = *it; itk::Indent nextIndent = indent.GetNextIndent(); ps->Print(os, nextIndent); MeshType::PointsContainer* points = ps->GetPoints(); MeshType::PointDataContainer* datas = ps->GetPointData(); MeshType::PointDataContainer::Iterator dataIterator = datas->Begin(); for (MeshType::PointsContainer::Iterator pointIterator = points->Begin(); pointIterator != points->End(); ++pointIterator, ++dataIterator) { os << nextIndent << "Point " << pointIterator->Index() << ": ["; os << pointIterator->Value().GetElement(0); for (unsigned int i = 1; i < PointType::GetPointDimension(); ++i) { os << ", " << pointIterator->Value().GetElement(i); } os << "]"; os << ", selected: " << dataIterator->Value().selected << ", point spec: " << dataIterator->Value().pointSpec << "\n"; } } } bool mitk::PointSet::SwapPointContents(PointIdentifier id1, PointIdentifier id2, int timeStep) { /* search and cache contents */ PointType p1; if (m_PointSetSeries[timeStep]->GetPoint(id1, &p1) == false) return false; PointDataType data1; if (m_PointSetSeries[timeStep]->GetPointData(id1, &data1) == false) return false; PointType p2; if (m_PointSetSeries[timeStep]->GetPoint(id2, &p2) == false) return false; PointDataType data2; if (m_PointSetSeries[timeStep]->GetPointData(id2, &data2) == false) return false; /* now swap contents */ m_PointSetSeries[timeStep]->SetPoint(id1, p2); m_PointSetSeries[timeStep]->SetPointData(id1, data2); m_PointSetSeries[timeStep]->SetPoint(id2, p1); m_PointSetSeries[timeStep]->SetPointData(id2, data1); return true; } diff --git a/Core/Code/DataManagement/mitkVector.h b/Core/Code/DataManagement/mitkVector.h index e09c49ee0b..c4e3fde5de 100644 --- a/Core/Code/DataManagement/mitkVector.h +++ b/Core/Code/DataManagement/mitkVector.h @@ -1,436 +1,440 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITKVECTOR_H_HEADER_INCLUDED_C1EBD0AD #define MITKVECTOR_H_HEADER_INCLUDED_C1EBD0AD +// this is needed for memcopy in ITK +// can be removed when fixed in ITK +#include + #include #include #include #include #include #include #include #include #include #ifndef DOXYGEN_SKIP namespace mitk { typedef float ScalarType; typedef itk::Matrix Matrix3D; typedef itk::Matrix Matrix4D; typedef vnl_matrix_fixed VnlMatrix3D; typedef itk::Transform Transform3D; typedef vnl_vector VnlVector; typedef vnl_vector_ref VnlVectorRef; typedef itk::Point Point2D; typedef itk::Point Point3D; typedef itk::Point Point4D; typedef itk::Point Point2I; typedef itk::Point Point3I; typedef itk::Point Point4I; typedef itk::Vector Vector2D; typedef itk::Vector Vector3D; typedef itk::Index<3> Index3D; typedef itk::ContinuousIndex ContinuousIndex3D; typedef vnl_quaternion Quaternion; //##Documentation //##@brief enumeration of the type a point can be enum PointSpecificationType { PTUNDEFINED = 0, PTSTART, PTCORNER, PTEDGE, PTEND }; typedef itk::NumericTraits ScalarTypeNumericTraits; MITK_CORE_EXPORT extern const ScalarType eps; MITK_CORE_EXPORT extern const ScalarType sqrteps; MITK_CORE_EXPORT extern const double large; template class VectorTraits { public: typedef T ValueType; }; template <> class VectorTraits { public: typedef ScalarType ValueType; }; template<> class VectorTraits { public: typedef double ValueType; }; template<> class VectorTraits< itk::Index<5> > { public: typedef itk::Index<5>::IndexValueType ValueType; }; template<> class VectorTraits< itk::Index<3> > { public: typedef itk::Index<3>::IndexValueType ValueType; }; template<> class VectorTraits< long int [3]> { public: typedef long int ValueType; }; template<> class VectorTraits< float [3]> { public: typedef float ValueType; }; template<> class VectorTraits< double [3]> { public: typedef double ValueType; }; template<> class VectorTraits< vnl_vector_fixed > { public: typedef ScalarType ValueType; }; template<> class VectorTraits< long unsigned int[3]> { public: typedef long unsigned int ValueType; }; template<> class VectorTraits< unsigned int *> { public: typedef unsigned int ValueType; }; template<> class VectorTraits< ScalarType[4] > { public: typedef ScalarType ValueType; }; template<> class VectorTraits< itk::Vector > { public: typedef float ValueType; }; template<> class VectorTraits< itk::Point > { public: typedef float ValueType; }; template<> class VectorTraits< itk::Point > { public: typedef float ValueType; }; template<> class VectorTraits< itk::Vector > { public: typedef double ValueType; }; template<> class VectorTraits< itk::Point > { public: typedef double ValueType; }; template<> class VectorTraits< itk::Vector > { public: typedef int ValueType; }; template<> class VectorTraits< itk::Point > { public: typedef int ValueType; }; template inline void itk2vtk(const Tin& in, Tout& out) { out[0]=(typename VectorTraits::ValueType)(in[0]); out[1]=(typename VectorTraits::ValueType)(in[1]); out[2]=(typename VectorTraits::ValueType)(in[2]); } template inline void vtk2itk(const Tin& in, Tout& out) { out[0]=(typename VectorTraits::ValueType)(in[0]); out[1]=(typename VectorTraits::ValueType)(in[1]); out[2]=(typename VectorTraits::ValueType)(in[2]); } template inline void FillVector3D(Tout& out, ScalarType x, ScalarType y, ScalarType z) { out[0] = (typename VectorTraits::ValueType)x; out[1] = (typename VectorTraits::ValueType)y; out[2] = (typename VectorTraits::ValueType)z; } template inline void FillVector4D(Tout& out, ScalarType x, ScalarType y, ScalarType z, ScalarType t) { out[0] = (typename VectorTraits::ValueType)x; out[1] = (typename VectorTraits::ValueType)y; out[2] = (typename VectorTraits::ValueType)z; out[3] = (typename VectorTraits::ValueType)t; } template inline void vnl2vtk(const vnl_vector& in, Tout *out) { unsigned int i; for(i=0; i inline void vtk2vnl(const Tin *in, vnl_vector& out) { unsigned int i; for(i=0; i inline void vtk2vnlref(const Tin *in, vnl_vector_ref& out) { unsigned int i; for(i=0; i inline void vnl2vtk(const vnl_vector_fixed& in, Tout *out) { unsigned int i; for(i=0; i inline void vtk2vnl(const Tin *in, vnl_vector_fixed& out) { unsigned int i; for(i=0; i itk::Vector operator+(const itk::Vector &vector, const itk::Point &point) { itk::Vector sub; for( unsigned int i=0; i inline itk::Vector& operator+=(itk::Vector &vector, const itk::Point &point) { for( unsigned int i=0; i itk::Vector operator-(const itk::Vector &vector, const itk::Point &point) { itk::Vector sub; for( unsigned int i=0; i inline itk::Vector& operator-=(itk::Vector &vector, const itk::Point &point) { for( unsigned int i=0; i inline bool MatrixEqualRMS(const vnl_matrix_fixed& matrix1,const vnl_matrix_fixed& matrix2,mitk::ScalarType epsilon=mitk::eps) { if ( (matrix1.rows() == matrix2.rows()) && (matrix1.cols() == matrix2.cols()) ) { vnl_matrix_fixed differenceMatrix = matrix1-matrix2; if (differenceMatrix.rms() inline bool MatrixEqualRMS(const itk::Matrix& matrix1,const itk::Matrix& matrix2,mitk::ScalarType epsilon=mitk::eps) { return mitk::MatrixEqualRMS(matrix1.GetVnlMatrix(),matrix2.GetVnlMatrix(),epsilon); } /*! \brief Check for element-wise matrix equality with a user defined accuracy. \param matrix1 first vnl matrix \param matrix2 second vnl matrix \epsilon user defined accuracy bounds */ template inline bool MatrixEqualElementWise(const vnl_matrix_fixed& matrix1,const vnl_matrix_fixed& matrix2,mitk::ScalarType epsilon=mitk::eps) { if ( (matrix1.rows() == matrix2.rows()) && (matrix1.cols() == matrix2.cols()) ) { for( unsigned int r=0; repsilon) { return false; } } } return true; } else { return false; } } /*! \brief Check for element-wise matrix equality with a user defined accuracy. \param matrix1 first itk matrix \param matrix2 second itk matrix \epsilon user defined accuracy bounds */ template inline bool MatrixEqualElementWise(const itk::Matrix& matrix1,const itk::Matrix& matrix2,mitk::ScalarType epsilon=mitk::eps) { return mitk::MatrixEqualElementWise(matrix1.GetVnlMatrix(),matrix2.GetVnlMatrix(),epsilon); } template inline bool Equal(const itk::Vector& vector1, const itk::Vector& vector2) { typename itk::Vector::VectorType diff = vector1-vector2; return diff.GetSquaredNorm() < mitk::eps; } template inline bool Equal(const itk::Point& vector1, const itk::Point& vector2) { typename itk::Point::VectorType diff = vector1-vector2; return diff.GetSquaredNorm() < mitk::eps; } inline bool Equal(const mitk::VnlVector& vector1, const mitk::VnlVector& vector2) { mitk::VnlVector diff = vector1-vector2; return diff.squared_magnitude() < mitk::eps; } inline bool Equal(double scalar1, double scalar2) { return fabs(scalar1-scalar2) < mitk::eps; } template inline bool Equal(const vnl_vector_fixed & vector1, const vnl_vector_fixed& vector2) { vnl_vector_fixed diff = vector1-vector2; return diff.squared_magnitude() < mitk::eps; } template inline void TransferMatrix(const itk::Matrix& in, itk::Matrix& out) { for (unsigned int i = 0; i < in.RowDimensions; ++i) for (unsigned int j = 0; j < in.ColumnDimensions; ++j) out[i][j] = in[i][j]; } } // namespace mitk #endif //DOXYGEN_SKIP /* * This part of the code has been shifted here to avoid compiler clashes * caused by including before the declaration of * the Equal() methods above. This problem occurs when using MSVC and is * probably related to a compiler bug. */ #include namespace mitk { typedef itk::AffineGeometryFrame::TransformType AffineTransform3D; } #define mitkSetConstReferenceMacro(name,type) \ virtual void Set##name (const type & _arg) \ { \ itkDebugMacro("setting " << #name " to " << _arg ); \ if (this->m_##name != _arg) \ { \ this->m_##name = _arg; \ this->Modified(); \ } \ } #define mitkSetVectorMacro(name,type) \ mitkSetConstReferenceMacro(name,type) #define mitkGetVectorMacro(name,type) \ itkGetConstReferenceMacro(name,type) #endif /* MITKVECTOR_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Core/Code/Interactions/mitkStateMachineFactory.cpp b/Core/Code/Interactions/mitkStateMachineFactory.cpp index d528829f9e..324474cf03 100755 --- a/Core/Code/Interactions/mitkStateMachineFactory.cpp +++ b/Core/Code/Interactions/mitkStateMachineFactory.cpp @@ -1,462 +1,462 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkStateMachineFactory.h" #include "mitkGlobalInteraction.h" #include #include #include #include #include /** * @brief This class builds up all the necessary structures for a statemachine. * and stores one start-state for all built statemachines. **/ //mitk::StateMachineFactory::StartStateMap mitk::StateMachineFactory::m_StartStates; //mitk::StateMachineFactory::AllStateMachineMapType mitk::StateMachineFactory::m_AllStateMachineMap; //std::string mitk::StateMachineFactory::s_LastLoadedBehavior; //XML StateMachine const std::string STYLE = "STYLE"; const std::string NAME = "NAME"; const std::string ID = "ID"; const std::string START_STATE = "START_STATE"; const std::string NEXT_STATE_ID = "NEXT_STATE_ID"; const std::string EVENT_ID = "EVENT_ID"; const std::string SIDE_EFFECT_ID = "SIDE_EFFECT_ID"; const std::string ISTRUE = "TRUE"; const std::string ISFALSE = "FALSE"; const std::string STATE_MACHINE = "stateMachine"; const std::string STATE = "state"; const std::string TRANSITION = "transition"; const std::string STATE_MACHINE_NAME = "stateMachine"; const std::string ACTION = "action"; const std::string BOOL_PARAMETER = "boolParameter"; const std::string INT_PARAMETER = "intParameter"; const std::string FLOAT_PARAMETER = "floatParameter"; const std::string DOUBLE_PARAMETER = "doubleParameter"; const std::string STRING_PARAMETER = "stringParameter"; const std::string VALUE = "VALUE"; #include namespace mitk { vtkStandardNewMacro(StateMachineFactory); } mitk::StateMachineFactory::StateMachineFactory() : m_AktStateMachineName(""), m_SkipStateMachine(false) {} mitk::StateMachineFactory::~StateMachineFactory() { //free memory while (!m_AllStateMachineMap.empty()) { StateMachineMapType* temp = m_AllStateMachineMap.begin()->second; m_AllStateMachineMap.erase(m_AllStateMachineMap.begin()); delete temp; } //should not be necessary due to SmartPointers m_StartStates.clear(); //delete WeakPointer if (m_AktTransition) delete m_AktTransition; } /** * @brief Returns NULL if no entry with string type is found. **/ mitk::State* mitk::StateMachineFactory::GetStartState(const char * type) { StartStateMapIter tempState = m_StartStates.find(type); if( tempState != m_StartStates.end() ) return (tempState)->second.GetPointer(); MITK_ERROR << "Error in StateMachineFactory: StartState for pattern \""<< type<< "\"not found! StateMachine might not work!\n"; return NULL; } /** * @brief Loads the xml file filename and generates the necessary instances. **/ bool mitk::StateMachineFactory::LoadBehavior(std::string fileName) { if ( fileName.empty() ) return false; m_LastLoadedBehavior = fileName; this->SetFileName(fileName.c_str()); return this->Parse(); } /** * @brief Loads the xml string and generates the necessary instances. **/ bool mitk::StateMachineFactory::LoadBehaviorString(std::string xmlString) { if ( xmlString.empty() ) return false; m_LastLoadedBehavior = "String"; return ( this->Parse(xmlString.c_str(), xmlString.length()) ); } bool mitk::StateMachineFactory::LoadStandardBehavior() { std::string xmlFileName( mitk::StandardFileLocations::GetInstance()->FindFile("StateMachine.xml", "Core/Code/Interactions") ); if (!xmlFileName.empty()) return this->LoadBehavior(xmlFileName); else return false; } /** * @brief Recursive method, that parses this brand of * the stateMachine; if the history has the same * size at the end, then the StateMachine is correct **/ bool mitk::StateMachineFactory::RParse(mitk::State::StateMap* states, mitk::State::StateMapIter thisState, HistorySet *history) { history->insert((thisState->second)->GetId());//log our path //or thisState->first. but this seems safer std::set nextStatesSet = (thisState->second)->GetAllNextStates(); //remove loops in nextStatesSet; //nether do we have to go there, nor will it clear a deadlock std::set::iterator position = nextStatesSet.find((thisState->second)->GetId());//look for the same state in nextStateSet if (position != nextStatesSet.end()) {//found the same state we are in! nextStatesSet.erase(position);//delete it, cause, we don't have to go there a second time! } //nextStatesSet is empty, so deadlock! if ( nextStatesSet.empty() ) { MITK_INFO<::iterator i = nextStatesSet.begin(); i != nextStatesSet.end(); i++) { if ( history->find(*i) == history->end() )//if we haven't been in this nextstate { mitk::State::StateMapIter nextState = states->find(*i);//search the iterator for our nextState if (nextState == states->end()) { MITK_INFO<size() > 1)//only one state; don't have to be parsed for deadlocks! { //parse all the given states an check for deadlock or not connected states HistorySet *history = new HistorySet; mitk::State::StateMapIter firstState = states->begin(); //parse through all the given states, log the parsed elements in history bool ok = RParse( states, firstState, history); if ( (states->size() == history->size()) && ok ) { delete history; } else //ether !ok or sizeA!=sizeB { delete history; MITK_INFO<begin(); tempState != states->end(); tempState++) { //searched through the States and Connects all Transitions bool tempbool = ( ( tempState->second )->ConnectTransitions( states ) ); if ( tempbool == false ) { MITK_INFO< ok = m_AllStatesOfOneStateMachine.insert(mitk::State::StateMap::value_type(id , m_AktState)); if ( ok.second == false ) { MITK_INFO<AddTransition( m_AktTransition ); } else if ( name == ACTION ) { int actionId = ReadXMLIntegerAttribut( ID, atts ); m_AktAction = Action::New( actionId ); m_AktTransition->AddAction( m_AktAction ); } else if ( name == BOOL_PARAMETER ) { if ( !m_AktAction ) return; bool value = ReadXMLBooleanAttribut( VALUE, atts ); std::string name = ReadXMLStringAttribut( NAME, atts ); m_AktAction->AddProperty( name.c_str(), BoolProperty::New( value ) ); } else if ( name == INT_PARAMETER ) { if ( !m_AktAction ) return; int value = ReadXMLIntegerAttribut( VALUE, atts ); std::string name = ReadXMLStringAttribut( NAME, atts ); m_AktAction->AddProperty( name.c_str(), IntProperty::New( value ) ); } else if ( name == FLOAT_PARAMETER ) { if ( !m_AktAction ) return; float value = ReadXMLIntegerAttribut( VALUE, atts ); std::string name = ReadXMLStringAttribut( NAME, atts ); m_AktAction->AddProperty( name.c_str(), FloatProperty::New( value ) ); } else if ( name == DOUBLE_PARAMETER ) { if ( !m_AktAction ) return; double value = ReadXMLDoubleAttribut( VALUE, atts ); std::string name = ReadXMLStringAttribut( NAME, atts ); m_AktAction->AddProperty( name.c_str(), DoubleProperty::New( value ) ); } else if ( name == STRING_PARAMETER ) { if ( !m_AktAction ) return; std::string value = ReadXMLStringAttribut( VALUE, atts ); std::string name = ReadXMLStringAttribut( NAME, atts ); m_AktAction->AddProperty( name.c_str(), StringProperty::New( value ) ); } } void mitk::StateMachineFactory::EndElement (const char* elementName) { //bool ok = true; std::string name(elementName); //skip the state machine pattern because the name was not unique! if (m_SkipStateMachine && (name != STATE_MACHINE) ) return; if ( name == STATE_MACHINE_NAME ) { if (m_SkipStateMachine) { m_SkipStateMachine = false; return; } /*ok =*/ ConnectStates(&m_AllStatesOfOneStateMachine); m_AllStatesOfOneStateMachine.clear(); } else if ( name == STATE_MACHINE ) { //doesn't have to be done } else if ( name == TRANSITION ) { m_AktTransition = NULL; //pointer stored in its state. memory will be freed in destructor of class state } else if ( name == ACTION ) { m_AktAction = NULL; } else if ( name == STATE ) { m_AktState = NULL; } } std::string mitk::StateMachineFactory::ReadXMLStringAttribut( std::string name, const char** atts ) { if(atts) { const char** attsIter = atts; while(*attsIter) { if ( name == *attsIter ) { attsIter++; return *attsIter; } attsIter++; attsIter++; } } return std::string(); } int mitk::StateMachineFactory::ReadXMLIntegerAttribut( std::string name, const char** atts ) { std::string s = ReadXMLStringAttribut( name, atts ); return atoi( s.c_str() ); } float mitk::StateMachineFactory::ReadXMLFloatAttribut( std::string name, const char** atts ) { std::string s = ReadXMLStringAttribut( name, atts ); return (float) atof( s.c_str() ); } double mitk::StateMachineFactory::ReadXMLDoubleAttribut( std::string name, const char** atts ) { std::string s = ReadXMLStringAttribut( name, atts ); return atof( s.c_str() ); } bool mitk::StateMachineFactory::ReadXMLBooleanAttribut( std::string name, const char** atts ) { std::string s = ReadXMLStringAttribut( name, atts ); if ( s == ISTRUE ) return true; else return false; } mitk::State* mitk::StateMachineFactory::GetState( const char * type, int StateId ) { //check if the state exists AllStateMachineMapType::iterator i = m_AllStateMachineMap.find( type ); if ( i == m_AllStateMachineMap.end() ) - return false; + return NULL; //get the statemachine of the state StateMachineMapType* sm = m_AllStateMachineMap[type]; //get the state from its statemachine if ( sm != NULL ) return (*sm)[StateId].GetPointer(); else return NULL; } bool mitk::StateMachineFactory::AddStateMachinePattern(const char * type, mitk::State* startState, mitk::StateMachineFactory::StateMachineMapType* allStatesOfStateMachine) { if (startState == NULL || allStatesOfStateMachine == NULL) return false; //check if the pattern has already been added StartStateMapIter tempState = m_StartStates.find(type); if( tempState != m_StartStates.end() ) { MITK_WARN << "Pattern " << type << " has already been added!\n"; return false; } //add the start state m_StartStates.insert(StartStateMap::value_type(type, startState)); //add all states of the new pattern to hold their references m_AllStateMachineMap.insert(AllStateMachineMapType::value_type(type, allStatesOfStateMachine)); return true; } diff --git a/Core/Code/Testing/mitkImageTest.cpp b/Core/Code/Testing/mitkImageTest.cpp index 6be4c0b921..3b7fca625f 100644 --- a/Core/Code/Testing/mitkImageTest.cpp +++ b/Core/Code/Testing/mitkImageTest.cpp @@ -1,336 +1,342 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ // mitk includes #include #include #include #include "mitkItkImageFileReader.h" #include #include // itk includes #include #include // stl includes #include // vtk includes #include int mitkImageTest(int argc, char* argv[]) { MITK_TEST_BEGIN(mitkImageTest); //Create Image out of nowhere mitk::Image::Pointer imgMem = mitk::Image::New(); mitk::PixelType pt = mitk::MakeScalarPixelType(); unsigned int dim[]={100,100,20}; MITK_TEST_CONDITION_REQUIRED( imgMem.IsNotNull(), "An image was created. "); // Initialize image imgMem->Initialize( pt, 3, dim); MITK_TEST_CONDITION_REQUIRED( imgMem->IsInitialized(), "Image::IsInitialized() ?"); MITK_TEST_CONDITION_REQUIRED( imgMem->GetPixelType() == pt, "PixelType was set correctly."); int *p = (int*)imgMem->GetData(); MITK_TEST_CONDITION( p != NULL, "GetData() returned not-NULL pointer."); // FIXME: this is directly changing the image data // filling image const unsigned int size = dim[0]*dim[1]*dim[2]; for(unsigned int i=0; iGetData(); MITK_TEST_CONDITION( p2 != NULL, "GetData() returned not-NULL pointer."); bool isEqual = true; for(unsigned int i=0; iGetSliceData(dim[2]/2)->GetData(); MITK_TEST_CONDITION_REQUIRED( p2 != NULL, "Valid slice data returned"); unsigned int xy_size = dim[0]*dim[1]; unsigned int start_mid_slice = (dim[2]/2)*xy_size; isEqual = true; for(unsigned int i=0; i(); imgMem->Initialize( pType , 3, dim); MITK_TEST_CONDITION_REQUIRED(imgMem->GetDimension()== 3, "Testing initialization parameter dimension!"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetPixelType() == pType, "Testing initialization parameter pixeltype!"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetDimension(0) == dim[0] && imgMem->GetDimension(1)== dim[1] && imgMem->GetDimension(2)== dim[2], "Testing initialization of dimensions!"); MITK_TEST_CONDITION( imgMem->IsInitialized(), "Image is initialized."); // Setting volume again: imgMem->SetVolume(imgMem->GetData()); //----------------- // geometry information for image mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::Vector3D spacing; mitk::FillVector3D(origin, 17.0, 19.92, 7.83); mitk::FillVector3D(right, 1.0, 2.0, 3.0); mitk::FillVector3D(bottom, 0.0, -3.0, 2.0); mitk::FillVector3D(spacing, 0.78, 0.91, 2.23); //InitializeStandardPlane(rightVector, downVector, spacing) mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing); planegeometry->SetOrigin(origin); // Testing Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int slices) with PlaneGeometry and GetData(): "; imgMem->Initialize( mitk::MakePixelType(), *planegeometry); MITK_TEST_CONDITION_REQUIRED( imgMem->GetGeometry()->GetOrigin() == static_cast(planegeometry)->GetOrigin(), "Testing correct setting of geometry via initialize!"); p = (int*)imgMem->GetData(); MITK_TEST_CONDITION_REQUIRED( p!=NULL, "GetData() returned valid pointer."); // Testing Initialize(const mitk::PixelType& type, int sDim, const mitk::PlaneGeometry& geometry) and GetData(): "; imgMem->Initialize( mitk::MakePixelType() , 40, *planegeometry); p = (int*)imgMem->GetData(); MITK_TEST_CONDITION_REQUIRED( p!=NULL, "GetData() returned valid pointer."); //----------------- // testing origin information and methods MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetOrigin(), origin), "Testing correctness of origin via GetGeometry()->GetOrigin(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetTimeSlicedGeometry()->GetOrigin(), origin), "Testing correctness of origin via GetTimeSlicedGeometry()->GetOrigin(): "); // Setting origin via SetOrigin(origin): "; mitk::FillVector3D(origin, 37.0, 17.92, 27.83); imgMem->SetOrigin(origin); // Test origin MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetOrigin(), origin), "Testing correctness of changed origin via GetGeometry()->GetOrigin(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetTimeSlicedGeometry()->GetOrigin(), origin), "Testing correctness of changed origin via GetTimeSlicedGeometry()->GetOrigin(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetGeometry2D(0)->GetOrigin(), origin), "Testing correctness of changed origin via GetSlicedGeometry()->GetGeometry2D(0)->GetOrigin(): "); //----------------- // testing spacing information and methods MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correct spacing from Geometry3D!"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetTimeSlicedGeometry()->GetSpacing(), spacing), "Testing correctspacing from TimeSlicedGeometry!"); mitk::FillVector3D(spacing, 7.0, 0.92, 1.83); imgMem->SetSpacing(spacing); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correctness of changed spacing via GetGeometry()->GetSpacing(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetTimeSlicedGeometry()->GetSpacing(), spacing), "Testing correctness of changed spacing via GetTimeSlicedGeometry()->GetSpacing(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetGeometry2D(0)->GetSpacing(), spacing), "Testing correctness of changed spacing via GetSlicedGeometry()->GetGeometry2D(0)->GetSpacing(): "); mitk::Image::Pointer vecImg = mitk::Image::New(); vecImg->Initialize( imgMem->GetPixelType(), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/ ); vecImg->SetImportChannel(imgMem->GetData(), 0, mitk::Image::CopyMemory ); vecImg->SetImportChannel(imgMem->GetData(), 1, mitk::Image::CopyMemory ); MITK_TEST_CONDITION_REQUIRED(vecImg->GetChannelData(0)->GetData() != NULL && vecImg->GetChannelData(1)->GetData() != NULL, "Testing set and return of channel data!"); MITK_TEST_CONDITION_REQUIRED( vecImg->IsValidSlice(0,0,1) , ""); MITK_TEST_OUTPUT(<< " Testing whether CopyMemory worked"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetData() != vecImg->GetData(), ""); MITK_TEST_OUTPUT(<< " Testing destruction after SetImportChannel"); vecImg = NULL; MITK_TEST_CONDITION_REQUIRED(vecImg.IsNull() , "testing destruction!"); //----------------- MITK_TEST_OUTPUT(<< "Testing initialization via vtkImageData"); MITK_TEST_OUTPUT(<< " Setting up vtkImageData"); vtkImageData* vtkimage = vtkImageData::New(); vtkimage->Initialize(); vtkimage->SetDimensions( 2, 3, 4); double vtkorigin[] = {-350,-358.203, -1363.5}; vtkimage->SetOrigin(vtkorigin); mitk::Point3D vtkoriginAsMitkPoint; mitk::vtk2itk(vtkorigin, vtkoriginAsMitkPoint); double vtkspacing[] = {1.367, 1.367, 2}; vtkimage->SetSpacing(vtkspacing); vtkimage->SetScalarType( VTK_SHORT ); vtkimage->AllocateScalars(); std::cout<<"[PASSED]"<Initialize(vtkimage); MITK_TEST_CONDITION_REQUIRED(mitkByVtkImage->IsInitialized(), ""); vtkimage->Delete(); MITK_TEST_OUTPUT(<< " Testing whether spacing has been correctly initialized from vtkImageData"); mitk::Vector3D spacing2 = mitkByVtkImage->GetGeometry()->GetSpacing(); mitk::Vector3D vtkspacingAsMitkVector; mitk::vtk2itk(vtkspacing, vtkspacingAsMitkVector); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(spacing2,vtkspacingAsMitkVector), ""); MITK_TEST_OUTPUT(<< " Testing whether GetSlicedGeometry(0)->GetOrigin() has been correctly initialized from vtkImageData"); mitk::Point3D origin2 = mitkByVtkImage->GetSlicedGeometry(0)->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2,vtkoriginAsMitkPoint), ""); MITK_TEST_OUTPUT(<< " Testing whether GetGeometry()->GetOrigin() has been correctly initialized from vtkImageData"); origin2 = mitkByVtkImage->GetGeometry()->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2,vtkoriginAsMitkPoint), ""); MITK_TEST_OUTPUT(<< " Testing whether GetTimeSlicedGeometry()->GetOrigin() has been correctly initialized from vtkImageData"); origin2 = mitkByVtkImage->GetTimeSlicedGeometry()->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2,vtkoriginAsMitkPoint), ""); // TODO test the following initializers on channel-incorporation // void mitk::Image::Initialize(const mitk::PixelType& type, unsigned int dimension, unsigned int *dimensions, unsigned int channels) // void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped, unsigned int channels, int tDim ) // void mitk::Image::Initialize(const mitk::Image* image) // void mitk::Image::Initialize(const mitkIpPicDescriptor* pic, int channels, int tDim, int sDim) //mitk::Image::Pointer vecImg = mitk::Image::New(); //vecImg->Initialize(PixelType(typeid(float), 6, itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/, false /*shiftBoundingBoxMinimumToZero*/ ); //vecImg->Initialize(PixelType(typeid(itk::Vector)), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/, false /*shiftBoundingBoxMinimumToZero*/ ); // testing access by index coordinates and by world coordinates MITK_TEST_CONDITION_REQUIRED(argc == 2, "Check if test image is accessible!"); const std::string filename = std::string(argv[1]); mitk::ItkImageFileReader::Pointer imageReader = mitk::ItkImageFileReader::New(); try { imageReader->SetFileName(filename); imageReader->Update(); } catch(...) { MITK_TEST_FAILED_MSG(<< "Could not read file for testing: " << filename); return 0; } mitk::Image::Pointer image = imageReader->GetOutput(); // generate a random point in world coordinates mitk::Point3D xMax, yMax, zMax, xMaxIndex, yMaxIndex, zMaxIndex; xMaxIndex.Fill(0.0f); yMaxIndex.Fill(0.0f); zMaxIndex.Fill(0.0f); xMaxIndex[0] = image->GetLargestPossibleRegion().GetSize()[0]; yMaxIndex[1] = image->GetLargestPossibleRegion().GetSize()[1]; zMaxIndex[2] = image->GetLargestPossibleRegion().GetSize()[2]; image->GetGeometry()->IndexToWorld(xMaxIndex, xMax); image->GetGeometry()->IndexToWorld(yMaxIndex, yMax); image->GetGeometry()->IndexToWorld(zMaxIndex, zMax); MITK_INFO << "Origin " << image->GetGeometry()->GetOrigin()[0] << " "<< image->GetGeometry()->GetOrigin()[1] << " "<< image->GetGeometry()->GetOrigin()[2] << ""; MITK_INFO << "MaxExtend " << xMax[0] << " "<< yMax[1] << " "<< zMax[2] << ""; mitk::Point3D point; itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randomGenerator = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randomGenerator->Initialize( std::rand() ); // initialize with random value, to get sensible random points for the image point[0] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[0], xMax[0]); point[1] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[1], yMax[1]); point[2] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[2], zMax[2]); MITK_INFO << "RandomPoint " << point[0] << " "<< point[1] << " "<< point[2] << ""; // test values and max/min mitk::ScalarType imageMin = image->GetStatistics()->GetScalarValueMin(); mitk::ScalarType imageMax = image->GetStatistics()->GetScalarValueMax(); mitk::ScalarType value = image->GetPixelValueByWorldCoordinate(point); MITK_INFO << imageMin << " "<< imageMax << " "<< value << ""; MITK_TEST_CONDITION( (value >= imageMin && value <= imageMax), "Value returned is between max/min"); + // testing the clone method of mitk::Image mitk::Image::Pointer cloneImage = image->Clone(); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension() == image->GetDimension(), "Clone (testing dimension)"); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetPixelType() == image->GetPixelType(), "Clone (testing pixel type)"); + // After cloning an image the geometry of both images should be equal too + MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetOrigin() == image->GetGeometry()->GetOrigin(), "Clone (testing origin)"); + MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetSpacing() == image->GetGeometry()->GetSpacing(), "Clone (testing spacing)"); + MITK_TEST_CONDITION_REQUIRED(mitk::MatrixEqualElementWise(cloneImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(), image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix()), + "Clone (testing transformation matrix)"); for (unsigned int i = 0u; i < cloneImage->GetDimension(); ++i) { MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension(i) == image->GetDimension(i), "Clone (testing dimension " << i << ")"); } //access via itk if(image->GetDimension()> 3) // CastToItk only works with 3d images so we need to check for 4d images { mitk::ImageTimeSelector::Pointer selector = mitk::ImageTimeSelector::New(); selector->SetTimeNr(0); selector->SetInput(image); selector->Update(); image = selector->GetOutput(); } if(image->GetDimension()==3) { typedef itk::Image ItkFloatImage3D; ItkFloatImage3D::Pointer itkimage; mitk::CastToItkImage(image, itkimage); MITK_TEST_CONDITION_REQUIRED(itkimage.IsNotNull(), "Test conversion to itk::Image!"); mitk::Point3D itkPhysicalPoint; image->GetGeometry()->WorldToItkPhysicalPoint(point, itkPhysicalPoint); MITK_INFO << "ITKPoint " << itkPhysicalPoint[0] << " "<< itkPhysicalPoint[1] << " "<< itkPhysicalPoint[2] << ""; mitk::Point3D backTransformedPoint; image->GetGeometry()->ItkPhysicalPointToWorld(itkPhysicalPoint, backTransformedPoint); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(point,backTransformedPoint), "Testing world->itk-physical->world consistency"); itk::Index<3> idx; bool status = itkimage->TransformPhysicalPointToIndex(itkPhysicalPoint, idx); MITK_INFO << "ITK Index " << idx[0] << " "<< idx[1] << " "<< idx[2] << ""; if(status) { float valByItk = itkimage->GetPixel(idx); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(valByItk, value), "Compare value of pixel returned by mitk in comparison to itk"); } else { MITK_WARN<< "Index is out buffered region!"; } } else { MITK_INFO << "Image does not contain three dimensions, some test cases are skipped!"; } // clone generated 3D image with one slice in z direction (cf. bug 11058) unsigned int* threeDdim = new unsigned int[3]; threeDdim[0] = 100; threeDdim[1] = 200; threeDdim[2] = 1; mitk::Image::Pointer threeDImage = mitk::Image::New(); threeDImage->Initialize(mitk::MakeScalarPixelType(), 3, threeDdim); mitk::Image::Pointer cloneThreeDImage = threeDImage->Clone(); // check that the clone image has the same dimensionality as the source image MITK_TEST_CONDITION_REQUIRED( cloneThreeDImage->GetDimension() == 3, "Testing if the clone image initializes with 3D!"); MITK_TEST_END(); -} \ No newline at end of file +} diff --git a/Core/Code/Testing/mitkPointSetTest.cpp b/Core/Code/Testing/mitkPointSetTest.cpp index d812b356d5..7e8a6cec4f 100644 --- a/Core/Code/Testing/mitkPointSetTest.cpp +++ b/Core/Code/Testing/mitkPointSetTest.cpp @@ -1,610 +1,621 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkTestingMacros.h" #include #include #include #include #include class mitkPointSetTestClass { public: static void TestGetITKPointSet(mitk::PointSet *pointSet) { //try to get the itkPointSet mitk::PointSet::DataType::Pointer itkdata = NULL; itkdata = pointSet->GetPointSet(); MITK_TEST_CONDITION( itkdata.IsNotNull(), "try to get the itkPointSet from a newly created PointSet" ) } static void TestGetSizeIsZero(mitk::PointSet *pointSet) { //fresh PointSet has to be empty! MITK_TEST_CONDITION( pointSet->GetSize() == 0, "check if the PointSet size is 0 " ) } static void TestIsEmpty(mitk::PointSet *pointSet) { MITK_TEST_CONDITION(pointSet->IsEmptyTimeStep(0), "check if the PointSet is empty" ) } static void TestCreateOperationAndAddPoint(mitk::PointSet *pointSet) { int id = 0; mitk::Point3D point; point.Fill(1); mitk::PointOperation* doOp = new mitk::PointOperation(mitk::OpINSERT, point, id); pointSet->ExecuteOperation(doOp); MITK_TEST_CONDITION( pointSet->GetSize()==1 && pointSet->IndexExists(id), "check if added points exists" ) delete doOp; mitk::Point3D tempPoint; tempPoint.Fill(0); tempPoint = pointSet->GetPoint(id); MITK_TEST_CONDITION( point == tempPoint, "check if added point contains real value" ) } static void TestAddSecondPoint(mitk::PointSet *pointSet) { //add a point directly int id=0; mitk::Point3D point; mitk::FillVector3D(point, 1.0, 2.0, 3.0); ++id; pointSet->GetPointSet()->GetPoints()->InsertElement(id, point); MITK_TEST_CONDITION( pointSet->GetSize()==2 ||pointSet->IndexExists(id), "check if added points exists" ) mitk::Point3D tempPoint; tempPoint.Fill(0); tempPoint = pointSet->GetPoint(id); MITK_TEST_CONDITION( point == tempPoint, "check if added point contains real value" ) } static void TestIsNotEmpty(mitk::PointSet *pointSet) { //PointSet can not be empty! MITK_TEST_CONDITION( !pointSet->IsEmptyTimeStep(0), "check if the PointSet is not empty " ) /* std::cout << "check if the PointSet is not empty "; if (pointSet->IsEmpty(0)) { std::cout<<"[FAILED]"<GetPoint(1); pointSet->SwapPointPosition(1, true); tempPoint = pointSet->GetPoint(0); MITK_TEST_CONDITION( point == tempPoint, "check SwapPointPosition upwards" ) /* if(point != tempPoint) { std::cout<<"[FAILED]"<SwapPointPosition(0, true)==false, "check SwapPointPosition upwards not possible" ) /* if(pointSet->SwapPointPosition(0, true)) { std::cout<<"[FAILED]"<GetPoint(0); pointSet->SwapPointPosition(0, false); tempPoint = pointSet->GetPoint(1); MITK_TEST_CONDITION( point == tempPoint, "check SwapPointPosition down" ) /* if(point != tempPoint) { std::cout<<"[FAILED]"<SetPoint(id, point); //Check SwapPointPosition downwards not possible MITK_TEST_CONDITION(!pointSet2->SwapPointPosition(id, false), "check SwapPointPosition downwards not possible" ) /* if(pointSet->SwapPointPosition(1, false)) { std::cout<<"[FAILED]"<ExecuteOperation(doOp); tempPoint = pointSet->GetPoint(id); MITK_TEST_CONDITION(tempPoint == point1 , "check PointOperation OpMove " ) delete doOp; /* if (tempPoint != point1) { std::cout<<"[FAILED]"<GetPoint(id); mitk::PointOperation* doOp = new mitk::PointOperation(mitk::OpREMOVE, point, id); pointSet->ExecuteOperation(doOp); tempPoint = pointSet->GetPoint(id); MITK_TEST_CONDITION(!pointSet->IndexExists(id) , "check PointOperation OpREMOVE " ) delete doOp; /* if(pointSet->IndexExists(id)) { std::cout<<"[FAILED]"<ExecuteOperation(doOp); MITK_TEST_CONDITION(pointSet->GetSelectInfo(4) , "check PointOperation OpSELECTPOINT " ) delete doOp; /* if (!pointSet->GetSelectInfo(4)) { std::cout<<"[FAILED]"<GetNumberOfSelected() == 1 , "check GetNumeberOfSelected " ) /* if(pointSet->GetNumberOfSelected() != 1) { std::cout<<"[FAILED]"<SearchSelectedPoint() == 4 , "check SearchSelectedPoint " ) /* if( pointSet->SearchSelectedPoint() != 4) { std::cout<<"[FAILED]"<ExecuteOperation(doOp); MITK_TEST_CONDITION(!pointSet->GetSelectInfo(4) , "check PointOperation OpDESELECTPOINT " ) MITK_TEST_CONDITION(pointSet->GetNumberOfSelected() == 0 , "check GetNumeberOfSelected " ) delete doOp; /* if (pointSet->GetSelectInfo(4)) { std::cout<<"[FAILED]"<GetNumberOfSelected() != 0) { std::cout<<"[FAILED]"<GetPoint(id); mitk::PointOperation* doOp = new mitk::PointOperation(mitk::OpMOVEPOINTUP, point4, id); pointSet->ExecuteOperation(doOp); tempPoint = pointSet->GetPoint(id-1); MITK_TEST_CONDITION(tempPoint == point , "check PointOperation OpMOVEPOINTUP " ) delete doOp; /* if (tempPoint != point) { std::cout<<"[FAILED]"<GetPoint(id); mitk::PointOperation* doOp = new mitk::PointOperation(mitk::OpMOVEPOINTDOWN, point2, id); pointSet->ExecuteOperation(doOp); tempPoint = pointSet->GetPoint(id+1); MITK_TEST_CONDITION(tempPoint == point , "check PointOperation OpMOVEPOINTDOWN " ) delete doOp; /* if (tempPoint != point) { std::cout<<"[FAILED]"<SetSelectInfo(2, true); MITK_TEST_CONDITION(pointSet->GetSelectInfo(2) , "check SetSelectInfo" ) /* if (!pointSet->GetSelectInfo(2)) { std::cout<<"[FAILED]"<SetPoint(5, point5, mitk::PTEDGE ); tempPoint = pointSet->GetPoint(5); MITK_TEST_CONDITION(tempPoint == point5, "check InsertPoint with PointSpecification" ) /* if (tempPoint != point5) { std::cout<<"[FAILED]"<GetPointIfExists(5, &tmpPoint); MITK_TEST_CONDITION(tmpPoint == point5, "check GetPointIfExists: " ) /* if (tmpPoint != point5) { std::cout<<"[FAILED]"<InsertPoint(10, p10); pointSet->InsertPoint(11, p11); pointSet->InsertPoint(12, p12); MITK_TEST_CONDITION((pointSet->IndexExists(10) == true) || (pointSet->IndexExists(11) == true) || (pointSet->IndexExists(12) == true), "add points with id 10, 11, 12: " ) //check OpREMOVE ExecuteOperation int id = 11; mitk::PointOperation* doOp = new mitk::PointOperation(mitk::OpREMOVE, point, id); pointSet->ExecuteOperation(doOp); MITK_TEST_CONDITION(!pointSet->IndexExists(id), "remove point id 11: ") /* if(pointSet->IndexExists(id)) { std::cout<<"[FAILED]"<ExecuteOperation(doOp); delete doOp; //check OpMOVEPOINTUP ExecuteOperation doOp = new mitk::PointOperation(mitk::OpMOVEPOINTUP, p12, 12); pointSet->ExecuteOperation(doOp); delete doOp; mitk::PointSet::PointType newP10 = pointSet->GetPoint(10); mitk::PointSet::PointType newP12 = pointSet->GetPoint(12); MITK_TEST_CONDITION(((newP10 == p12) && (newP12 == p10)) == true, "check PointOperation OpMOVEPOINTUP for point id 12:" ) //check OpMOVEPOINTDOWN ExecuteOperation doOp = new mitk::PointOperation(mitk::OpMOVEPOINTDOWN, p10, 10); pointSet->ExecuteOperation(doOp); delete doOp; newP10 = pointSet->GetPoint(10); newP12 = pointSet->GetPoint(12); MITK_TEST_CONDITION(((newP10 == p10) && (newP12 == p12)) == true, "check PointOperation OpMOVEPOINTDOWN for point id 10: ") } static void TestOpMovePointUpOnFirstPoint(mitk::PointSet *pointSet) { //check OpMOVEPOINTUP on first point ExecuteOperation mitk::PointSet::PointType p1 = pointSet->GetPoint(1); mitk::PointSet::PointType p2 = pointSet->GetPoint(2); mitk::PointOperation* doOp = new mitk::PointOperation(mitk::OpMOVEPOINTUP, p1, 1); pointSet->ExecuteOperation(doOp); delete doOp; mitk::PointSet::PointType newP1 = pointSet->GetPoint(1); mitk::PointSet::PointType newP2 = pointSet->GetPoint(2); MITK_TEST_CONDITION(((newP1 == p1) && (newP2 == p2)) == true, "check PointOperation OpMOVEPOINTUP for point id 1: ") /* if (((newP1 == p1) && (newP2 == p2)) == false) { std::cout<<"[FAILED]"<GetPointSet()->GetPoints(); mitk::PointSet::PointDataContainer* pd = ps->GetPointSet()->GetPointData(); MITK_TEST_CONDITION_REQUIRED(pc->Size() == pd->Size(), "PointContainer and PointDataContainer have same size"); mitk::PointSet::PointsContainer::ConstIterator pIt = pc->Begin(); mitk::PointSet::PointDataContainer::ConstIterator dIt = pd->Begin(); bool failed = false; for (; pIt != pc->End(); ++pIt, ++dIt) if (pIt->Index() != dIt->Index()) { failed = true; break; } MITK_TEST_CONDITION(failed == false, "Indices in PointContainer and PointDataContainer are equal"); } }; int mitkPointSetTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN("PointSet") //Create PointSet mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); MITK_TEST_CONDITION_REQUIRED(pointSet.IsNotNull(),"Testing instantiation") mitkPointSetTestClass::TestGetITKPointSet(pointSet); mitkPointSetTestClass::TestGetSizeIsZero(pointSet); mitkPointSetTestClass::TestIsEmpty(pointSet); mitkPointSetTestClass::TestCreateOperationAndAddPoint(pointSet); mitk::Point3D point2, point3, point4; point2.Fill(3); point3.Fill(4); point4.Fill(5); pointSet->InsertPoint(2,point2); pointSet->InsertPoint(3,point3); pointSet->InsertPoint(4,point4); mitkPointSetTestClass::TestAddSecondPoint(pointSet); mitkPointSetTestClass::TestIsNotEmpty(pointSet); mitkPointSetTestClass::TestSwapPointPositionUpwards(pointSet); mitkPointSetTestClass::TestSwapPointPositionUpwardsNotPossible(pointSet); mitkPointSetTestClass::TestSwapPointPositionDownwards(pointSet); mitkPointSetTestClass::TestSwapPointPositionDownwardsNotPossible(pointSet); mitkPointSetTestClass::TestPointOperationOpMove(pointSet); mitkPointSetTestClass::TestPointOperationOpRemove(pointSet); mitkPointSetTestClass::TestPointOperationOpSelectPoint(pointSet); mitkPointSetTestClass::TestGetNumberOfSelected(pointSet); mitkPointSetTestClass::TestSearchSelectedPoint(pointSet); mitkPointSetTestClass::TestOpDeselectPoint(pointSet); mitkPointSetTestClass::TestOpMovePointUp(pointSet); mitkPointSetTestClass::TestOpMovePointDown(pointSet); mitkPointSetTestClass::TestSetSelectInfo(pointSet); mitkPointSetTestClass::TestInsertPointWithPointSpecification(pointSet); mitkPointSetTestClass::TestGetPointIfExists(pointSet); mitkPointSetTestClass::TestCreateHoleInThePointIDs(pointSet); mitkPointSetTestClass::TestOpMovePointUpOnFirstPoint(pointSet); MITK_TEST_OUTPUT(<< "Test InsertPoint(), SetPoint() and SwapPointPosition()"); mitk::PointSet::PointType point; mitk::FillVector3D(point, 2.2, 3.3, -4.4); /* call everything that might modify PointContainer and PointDataContainer */ pointSet->InsertPoint(17, point); pointSet->SetPoint(4, point); pointSet->SetPoint(7, point); pointSet->SetPoint(2, point); pointSet->SwapPointPosition(7, true); pointSet->SwapPointPosition(3, true); pointSet->SwapPointPosition(2, false); mitkPointSetTestClass::TestPointContainerPointDataContainer(pointSet); MITK_TEST_OUTPUT(<< "Test OpREMOVE"); mitk::PointOperation op1(mitk::OpREMOVE, mitk::Point3D(), 2); // existing index pointSet->ExecuteOperation(&op1); mitk::PointOperation op1b(mitk::OpREMOVE, mitk::Point3D(), 112); // non existing index pointSet->ExecuteOperation(&op1b); mitkPointSetTestClass::TestPointContainerPointDataContainer(pointSet); MITK_TEST_OUTPUT(<< "Test OpMove"); mitk::PointOperation op2(mitk::OpMOVE, mitk::Point3D(), 4); // existing index pointSet->ExecuteOperation(&op2); mitk::PointOperation op3(mitk::OpMOVE, mitk::Point3D(), 34); // non existing index pointSet->ExecuteOperation(&op3); mitkPointSetTestClass::TestPointContainerPointDataContainer(pointSet); MITK_TEST_OUTPUT(<< "Test OpINSERT"); mitk::PointOperation op4(mitk::OpINSERT, mitk::Point3D(), 38); // non existing index pointSet->ExecuteOperation(&op4); mitk::PointOperation op5(mitk::OpINSERT, mitk::Point3D(), 17); // existing index pointSet->ExecuteOperation(&op5); mitkPointSetTestClass::TestPointContainerPointDataContainer(pointSet); mitk::PointSet::Pointer clonePS = pointSet->Clone(); mitkPointSetTestClass::TestIsNotEmpty(clonePS); MITK_TEST_CONDITION_REQUIRED(clonePS->GetPointSetSeriesSize() == pointSet->GetPointSetSeriesSize(), "Testing cloned point set's size!"); MITK_TEST_CONDITION_REQUIRED(clonePS.GetPointer() != pointSet.GetPointer(), "Testing that the clone is not the source PS!"); MITK_TEST_CONDITION_REQUIRED(clonePS->GetGeometry()->GetCenter() == pointSet->GetGeometry()->GetCenter() , "Testing if the geometry is cloned correctly!"); MITK_TEST_CONDITION_REQUIRED(clonePS->GetPropertyList()->GetMap()->size() == pointSet->GetPropertyList()->GetMap()->size() , "Testing if the property list is cloned correctly!"); + // Also testing, that clone is independent from original + mitk::Point3D p, p2; + p.Fill(42); + p2.Fill(84); + clonePS->InsertPoint(0,p); + pointSet->InsertPoint(0,p2); + p = clonePS->GetPoint(0); + p2 = pointSet->GetPoint(0); + MITK_TEST_CONDITION_REQUIRED(p != p2, "Testing that the clone is independent from source!"); + + MITK_TEST_END(); } diff --git a/Modules/CMakeLists.txt b/Modules/CMakeLists.txt index c9ac643b13..808d527c13 100644 --- a/Modules/CMakeLists.txt +++ b/Modules/CMakeLists.txt @@ -1,52 +1,53 @@ set(LIBPOSTFIX "Ext") # Modules must be listed according to their dependencies set(module_dirs SceneSerializationBase PlanarFigure ImageExtraction ImageStatistics LegacyAdaptors IpPicSupport MitkExt SceneSerialization + Segmentation Qmitk QmitkExt GraphAlgorithms DiffusionImaging GPGPU IGT CameraCalibration IGTUI RigidRegistration RigidRegistrationUI DeformableRegistration DeformableRegistrationUI OpenCVVideoSupport Overlays InputDevices ToFHardware ToFProcessing ToFUI ClippingTools ) set(MITK_DEFAULT_SUBPROJECTS MITK-Modules) foreach(module_dir ${module_dirs}) add_subdirectory(${module_dir}) endforeach() if(MITK_PRIVATE_MODULES) file(GLOB all_subdirs RELATIVE ${MITK_PRIVATE_MODULES} ${MITK_PRIVATE_MODULES}/*) foreach(subdir ${all_subdirs}) string(FIND ${subdir} "." _result) if(_result EQUAL -1) if(EXISTS ${MITK_PRIVATE_MODULES}/${subdir}/CMakeLists.txt) message(STATUS "Found private module ${subdir}") add_subdirectory(${MITK_PRIVATE_MODULES}/${subdir} private_modules/${subdir}) endif() endif() endforeach() endif(MITK_PRIVATE_MODULES) diff --git a/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsHistogramCache.cpp b/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsHistogramCache.cpp index 183b3b5358..89512a4aef 100644 --- a/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsHistogramCache.cpp +++ b/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsHistogramCache.cpp @@ -1,84 +1,96 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkConnectomicsHistogramCache.h" mitk::ConnectomicsHistogramCache::ConnectomicsHistogramCache() { } mitk::ConnectomicsHistogramCache::~ConnectomicsHistogramCache() { } mitk::ConnectomicsHistogramsContainer * mitk::ConnectomicsHistogramCache::operator[]( mitk::ConnectomicsNetwork::Pointer sp_NetworkData ) { BaseData *p_BaseData = dynamic_cast< BaseData* >( sp_NetworkData.GetPointer() ); if(!p_BaseData) { MITK_WARN << "ConnectomicsHistogramCache::operator[] with null connectomics network data called"; return 0; } ConnectomicsHistogramsCacheElement *elementToUpdate = 0; - bool first = true; + bool first( true ); + + bool found( false ); for(CacheContainer::iterator iter = cache.begin(); iter != cache.end(); iter++) { ConnectomicsHistogramsCacheElement *e = dynamic_cast(*iter); BaseData *p_tmp = e->baseData.GetPointer(); if(p_tmp == p_BaseData) { if(!first) { cache.erase(iter); cache.push_front(e); } if( p_BaseData->GetMTime() > e->m_LastUpdateTime.GetMTime()) - goto recomputeElement; - + { + // found but needs an update + found = true; + elementToUpdate = e; + break; + } + // found but no update needed return dynamic_cast( e->GetHistograms() ); } first = false; } - if (dynamic_cast(p_BaseData)) + if( !found ) { - elementToUpdate = new ConnectomicsHistogramsCacheElement(); + if (dynamic_cast(p_BaseData)) + { + elementToUpdate = new ConnectomicsHistogramsCacheElement(); + } + else + { + MITK_WARN << "not supported: " << p_BaseData->GetNameOfClass(); + return NULL; + } + + elementToUpdate->baseData = p_BaseData; + cache.push_front(elementToUpdate); + TrimCache(); } - else + + if(elementToUpdate) { - MITK_WARN << "not supported: " << p_BaseData->GetNameOfClass(); - return NULL; + elementToUpdate->ComputeFromBaseData(p_BaseData); + elementToUpdate->m_LastUpdateTime.Modified(); + return dynamic_cast( elementToUpdate->GetHistograms() ); } - - elementToUpdate->baseData = p_BaseData; - cache.push_front(elementToUpdate); - TrimCache(); - - recomputeElement: - - elementToUpdate->ComputeFromBaseData(p_BaseData); - elementToUpdate->m_LastUpdateTime.Modified(); - return dynamic_cast( elementToUpdate->GetHistograms() ); + return NULL; } diff --git a/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.cpp b/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.cpp index d71742d711..133cf33333 100644 --- a/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.cpp +++ b/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.cpp @@ -1,352 +1,364 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkConnectomicsSyntheticNetworkGenerator.h" #include #include #include "mitkConnectomicsConstantsManager.h" #include //for random number generation #include "vxl/core/vnl/vnl_random.h" #include "vxl/core/vnl/vnl_math.h" mitk::ConnectomicsSyntheticNetworkGenerator::ConnectomicsSyntheticNetworkGenerator() +: m_LastGenerationWasSuccess( false ) { } mitk::ConnectomicsSyntheticNetworkGenerator::~ConnectomicsSyntheticNetworkGenerator() { } mitk::ConnectomicsNetwork::Pointer mitk::ConnectomicsSyntheticNetworkGenerator::CreateSyntheticNetwork(int networkTypeId, int paramterOne, double parameterTwo) { mitk::ConnectomicsNetwork::Pointer network = mitk::ConnectomicsNetwork::New(); + m_LastGenerationWasSuccess = false; // give the network an artificial geometry network->SetGeometry( this->GenerateDefaultGeometry() ); switch (networkTypeId) { case 0: GenerateSyntheticCubeNetwork( network, paramterOne, parameterTwo ); break; case 1: GenerateSyntheticCenterToSurfaceNetwork( network, paramterOne, parameterTwo ); break; case 2: GenerateSyntheticRandomNetwork( network, paramterOne, parameterTwo ); break; case 3: //GenerateSyntheticScaleFreeNetwork( network, 1000 ); break; case 4: //GenerateSyntheticSmallWorldNetwork( network, 1000 ); break; default: MBI_ERROR << "Unrecognized Network ID"; } network->UpdateBounds(); return network; } mitk::Geometry3D::Pointer mitk::ConnectomicsSyntheticNetworkGenerator::GenerateDefaultGeometry() { mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); double zero( 0.0 ); double one( 1.0 ); // origin = {0,0,0} mitk::Point3D origin; origin[0] = zero; origin[1] = zero; origin[2] = zero; geometry->SetOrigin(origin); // spacing = {1,1,1} float spacing[3]; spacing[0] = one; spacing[1] = one; spacing[2] = one; geometry->SetSpacing(spacing); // transform vtkMatrix4x4* transformMatrix = vtkMatrix4x4::New(); transformMatrix->SetElement(0,0,one); transformMatrix->SetElement(1,0,zero); transformMatrix->SetElement(2,0,zero); transformMatrix->SetElement(0,1,zero); transformMatrix->SetElement(1,1,one); transformMatrix->SetElement(2,1,zero); transformMatrix->SetElement(0,2,zero); transformMatrix->SetElement(1,2,zero); transformMatrix->SetElement(2,2,one); transformMatrix->SetElement(0,3,origin[0]); transformMatrix->SetElement(1,3,origin[1]); transformMatrix->SetElement(2,3,origin[2]); transformMatrix->SetElement(3,3,1); geometry->SetIndexToWorldTransformByVtkMatrix( transformMatrix ); geometry->SetImageGeometry(true); return geometry; } void mitk::ConnectomicsSyntheticNetworkGenerator::GenerateSyntheticCubeNetwork( mitk::ConnectomicsNetwork::Pointer network, int cubeExtent, double distance ) { // map for storing the conversion from indices to vertex descriptor std::map< int, mitk::ConnectomicsNetwork::VertexDescriptorType > idToVertexMap; int vertexID(0); for( int loopX( 0 ); loopX < cubeExtent; loopX++ ) { for( int loopY( 0 ); loopY < cubeExtent; loopY++ ) { for( int loopZ( 0 ); loopZ < cubeExtent; loopZ++ ) { std::vector< float > position; std::string label; std::stringstream labelStream; labelStream << vertexID; label = labelStream.str(); position.push_back( loopX * distance ); position.push_back( loopY * distance ); position.push_back( loopZ * distance ); mitk::ConnectomicsNetwork::VertexDescriptorType newVertex = network->AddVertex( vertexID ); network->SetLabel( newVertex, label ); network->SetCoordinates( newVertex, position ); if ( idToVertexMap.count( vertexID ) > 0 ) { MITK_ERROR << "Aborting network creation, duplicate vertex ID generated."; + m_LastGenerationWasSuccess = false; return; } idToVertexMap.insert( std::pair< int, mitk::ConnectomicsNetwork::VertexDescriptorType >( vertexID, newVertex) ); vertexID++; } } } int edgeID(0), edgeSourceID(0), edgeTargetID(0); // uniform weight of one int edgeWeight(1); mitk::ConnectomicsNetwork::VertexDescriptorType source; mitk::ConnectomicsNetwork::VertexDescriptorType target; for( int loopX( 0 ); loopX < cubeExtent; loopX++ ) { for( int loopY( 0 ); loopY < cubeExtent; loopY++ ) { for( int loopZ( 0 ); loopZ < cubeExtent; loopZ++ ) { // to avoid creating an edge twice (this being an undirected graph) we only generate // edges in three directions, the others will be supplied by the corresponding nodes if( loopX != 0 ) { edgeTargetID = edgeSourceID - cubeExtent * cubeExtent; source = idToVertexMap.find( edgeSourceID )->second; target = idToVertexMap.find( edgeTargetID )->second; network->AddEdge( source, target, edgeSourceID, edgeTargetID, edgeWeight); edgeID++; } if( loopY != 0 ) { edgeTargetID = edgeSourceID - cubeExtent; source = idToVertexMap.find( edgeSourceID )->second; target = idToVertexMap.find( edgeTargetID )->second; network->AddEdge( source, target, edgeSourceID, edgeTargetID, edgeWeight); edgeID++; } if( loopZ != 0 ) { edgeTargetID = edgeSourceID - 1; source = idToVertexMap.find( edgeSourceID )->second; target = idToVertexMap.find( edgeTargetID )->second; network->AddEdge( source, target, edgeSourceID, edgeTargetID, edgeWeight); edgeID++; } edgeSourceID++; } // end for( int loopZ( 0 ); loopZ < cubeExtent; loopZ++ ) } // end for( int loopY( 0 ); loopY < cubeExtent; loopY++ ) } // end for( int loopX( 0 ); loopX < cubeExtent; loopX++ ) + m_LastGenerationWasSuccess = true; } void mitk::ConnectomicsSyntheticNetworkGenerator::GenerateSyntheticCenterToSurfaceNetwork( mitk::ConnectomicsNetwork::Pointer network, int numberOfPoints, double radius ) { //the random number generators unsigned int randomOne = (unsigned int) rand(); unsigned int randomTwo = (unsigned int) rand(); vnl_random rng( (unsigned int) rand() ); vnl_random rng2( (unsigned int) rand() ); mitk::ConnectomicsNetwork::VertexDescriptorType centerVertex; int vertexID(0); { //add center vertex std::vector< float > position; std::string label; std::stringstream labelStream; labelStream << vertexID; label = labelStream.str(); position.push_back( 0 ); position.push_back( 0 ); position.push_back( 0 ); centerVertex = network->AddVertex( vertexID ); network->SetLabel( centerVertex, label ); network->SetCoordinates( centerVertex, position ); }//end add center vertex // uniform weight of one int edgeWeight(1); mitk::ConnectomicsNetwork::VertexDescriptorType source; mitk::ConnectomicsNetwork::VertexDescriptorType target; //add vertices on sphere surface for( int loopID( 1 ); loopID < numberOfPoints; loopID++ ) { std::vector< float > position; std::string label; std::stringstream labelStream; labelStream << loopID; label = labelStream.str(); //generate random, uniformly distributed points on a sphere surface const double uVariable = rng.drand64( 0.0 , 1.0); const double vVariable = rng.drand64( 0.0 , 1.0); const double phi = 2 * vnl_math::pi * uVariable; const double theta = std::acos( 2 * vVariable - 1 ); double xpos = radius * std::cos( phi ) * std::sin( theta ); double ypos = radius * std::sin( phi ) * std::sin( theta ); double zpos = radius * std::cos( theta ); position.push_back( xpos ); position.push_back( ypos ); position.push_back( zpos ); mitk::ConnectomicsNetwork::VertexDescriptorType newVertex = network->AddVertex( loopID ); network->SetLabel( newVertex, label ); network->SetCoordinates( newVertex, position ); network->AddEdge( newVertex, centerVertex, loopID, 0, edgeWeight); } + m_LastGenerationWasSuccess = true; } void mitk::ConnectomicsSyntheticNetworkGenerator::GenerateSyntheticRandomNetwork( mitk::ConnectomicsNetwork::Pointer network, int numberOfPoints, double threshold ) { // as the surface is proportional to the square of the radius the density stays the same double radius = 5 * std::sqrt( (float) numberOfPoints ); //the random number generators unsigned int randomOne = (unsigned int) rand(); unsigned int randomTwo = (unsigned int) rand(); vnl_random rng( (unsigned int) rand() ); vnl_random rng2( (unsigned int) rand() ); // map for storing the conversion from indices to vertex descriptor std::map< int, mitk::ConnectomicsNetwork::VertexDescriptorType > idToVertexMap; //add vertices on sphere surface for( int loopID( 0 ); loopID < numberOfPoints; loopID++ ) { std::vector< float > position; std::string label; std::stringstream labelStream; labelStream << loopID; label = labelStream.str(); //generate random, uniformly distributed points on a sphere surface const double uVariable = rng.drand64( 0.0 , 1.0); const double vVariable = rng.drand64( 0.0 , 1.0); const double phi = 2 * vnl_math::pi * uVariable; const double theta = std::acos( 2 * vVariable - 1 ); double xpos = radius * std::cos( phi ) * std::sin( theta ); double ypos = radius * std::sin( phi ) * std::sin( theta ); double zpos = radius * std::cos( theta ); position.push_back( xpos ); position.push_back( ypos ); position.push_back( zpos ); mitk::ConnectomicsNetwork::VertexDescriptorType newVertex = network->AddVertex( loopID ); network->SetLabel( newVertex, label ); network->SetCoordinates( newVertex, position ); if ( idToVertexMap.count( loopID ) > 0 ) { MITK_ERROR << "Aborting network creation, duplicate vertex ID generated."; + m_LastGenerationWasSuccess = false; return; } idToVertexMap.insert( std::pair< int, mitk::ConnectomicsNetwork::VertexDescriptorType >( loopID, newVertex) ); } int edgeID(0); // uniform weight of one int edgeWeight(1); mitk::ConnectomicsNetwork::VertexDescriptorType source; mitk::ConnectomicsNetwork::VertexDescriptorType target; for( int loopID( 0 ); loopID < numberOfPoints; loopID++ ) { // to avoid creating an edge twice (this being an undirected graph) we only // potentially generate edges with all nodes with a bigger ID for( int innerLoopID( loopID ); innerLoopID < numberOfPoints; innerLoopID++ ) { if( rng.drand64( 0.0 , 1.0) > threshold) { // do nothing } else { source = idToVertexMap.find( loopID )->second; target = idToVertexMap.find( innerLoopID )->second; network->AddEdge( source, target, loopID, innerLoopID, edgeWeight); edgeID++; } } // end for( int innerLoopID( loopID ); innerLoopID < numberOfPoints; innerLoopID++ ) } // end for( int loopID( 0 ); loopID < numberOfPoints; loopID++ ) + m_LastGenerationWasSuccess = true; +} + +bool mitk::ConnectomicsSyntheticNetworkGenerator::WasGenerationSuccessfull() +{ + return m_LastGenerationWasSuccess; } \ No newline at end of file diff --git a/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.h b/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.h index dca9005c48..c19aa02155 100644 --- a/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.h +++ b/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.h @@ -1,83 +1,88 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkConnectomicsSyntheticNetworkGenerator_h #define mitkConnectomicsSyntheticNetworkGenerator_h #include #include #include #include "mitkCommon.h" #include "mitkImage.h" #include "mitkConnectomicsNetwork.h" #include "MitkDiffusionImagingExports.h" namespace mitk { /** * \brief A class to generate synthetic networks */ class MitkDiffusionImaging_EXPORT ConnectomicsSyntheticNetworkGenerator : public itk::Object { public: /** Standard class typedefs. */ /** Method for creation through the object factory. */ mitkClassMacro(ConnectomicsSyntheticNetworkGenerator, itk::Object); itkNewMacro(Self); /** Create Synthetic Networks */ mitk::ConnectomicsNetwork::Pointer CreateSyntheticNetwork(int networkTypeId, int paramterOne, double parameterTwo); + /** Return whether the last attempted network generation was a success*/ + bool WasGenerationSuccessfull(); + protected: //////////////////// Functions /////////////////////// ConnectomicsSyntheticNetworkGenerator(); ~ConnectomicsSyntheticNetworkGenerator(); /** Generate a default geometry for synthetic images */ mitk::Geometry3D::Pointer GenerateDefaultGeometry(); /** Generate a synthetic cube (regular lattice) network */ void GenerateSyntheticCubeNetwork( mitk::ConnectomicsNetwork::Pointer network, int cubeExtent, double distance ); /** Generate a highly heterogenic network * * This is achieved by generating a center vertex and vertices on * a sphere surface, which are all only connected to the center * vertex. */ void GenerateSyntheticCenterToSurfaceNetwork( mitk::ConnectomicsNetwork::Pointer network, int numberOfPoints, double radius ); /** Generate a random network without specific characteristics * * This is achieved by generating vertices and then deciding whether to * specific vertices are connected by comparing a random number to the threshold */ void GenerateSyntheticRandomNetwork( mitk::ConnectomicsNetwork::Pointer network, int numberOfPoints, double threshold ); /////////////////////// Variables //////////////////////// + /** Store whether the network generated last was generated properly */ + bool m_LastGenerationWasSuccess; }; }// end namespace mitk #endif // _mitkConnectomicsSyntheticNetworkGenerator_H_INCLUDED \ No newline at end of file diff --git a/Modules/DiffusionImaging/Algorithms/itkExtractChannelFromRgbaImageFilter.cpp b/Modules/DiffusionImaging/Algorithms/itkExtractChannelFromRgbaImageFilter.cpp index b3d0bc068a..8461b0afd6 100644 --- a/Modules/DiffusionImaging/Algorithms/itkExtractChannelFromRgbaImageFilter.cpp +++ b/Modules/DiffusionImaging/Algorithms/itkExtractChannelFromRgbaImageFilter.cpp @@ -1,159 +1,156 @@ #include "itkExtractChannelFromRgbaImageFilter.h" // VTK #include #include #include // misc #include namespace itk{ - template< class OutputImageType > - ExtractChannelFromRgbaImageFilter< OutputImageType >::ExtractChannelFromRgbaImageFilter(): + template< class ReferenceImageType, class OutputImageType > + ExtractChannelFromRgbaImageFilter< ReferenceImageType, OutputImageType >::ExtractChannelFromRgbaImageFilter(): m_Channel(RED) { } - template< class OutputImageType > - ExtractChannelFromRgbaImageFilter< OutputImageType >::~ExtractChannelFromRgbaImageFilter() + template< class ReferenceImageType, class OutputImageType > + ExtractChannelFromRgbaImageFilter< ReferenceImageType, OutputImageType >::~ExtractChannelFromRgbaImageFilter() { } - template< class OutputImageType > - void ExtractChannelFromRgbaImageFilter< OutputImageType >::GenerateData() + template< class ReferenceImageType, class OutputImageType > + void ExtractChannelFromRgbaImageFilter< ReferenceImageType, OutputImageType >::GenerateData() { typename InputImageType::Pointer rgbaImage = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); typename InputImageType::RegionType region = rgbaImage->GetLargestPossibleRegion(); outputImage->SetSpacing( m_ReferenceImage->GetSpacing() ); // Set the image spacing outputImage->SetOrigin( m_ReferenceImage->GetOrigin() ); // Set the image origin outputImage->SetDirection( m_ReferenceImage->GetDirection() ); // Set the image direction outputImage->SetRegions( m_ReferenceImage->GetLargestPossibleRegion()); outputImage->Allocate(); outputImage->FillBuffer(0); float* outImageBufferPointer = outputImage->GetBufferPointer(); itk::Image< short, 3 >::Pointer counterImage = itk::Image< short, 3 >::New(); counterImage->SetSpacing( m_ReferenceImage->GetSpacing() ); // Set the image spacing counterImage->SetOrigin( m_ReferenceImage->GetOrigin() ); // Set the image origin counterImage->SetDirection( m_ReferenceImage->GetDirection() ); // Set the image direction counterImage->SetRegions( m_ReferenceImage->GetLargestPossibleRegion()); counterImage->Allocate(); counterImage->FillBuffer(0); short* counterImageBufferPointer = counterImage->GetBufferPointer(); int w = m_ReferenceImage->GetLargestPossibleRegion().GetSize().GetElement(0); int h = m_ReferenceImage->GetLargestPossibleRegion().GetSize().GetElement(1); int d = m_ReferenceImage->GetLargestPossibleRegion().GetSize().GetElement(2); typedef ImageRegionConstIterator< InputImageType > InImageIteratorType; InImageIteratorType rgbaIt(rgbaImage, region); rgbaIt.GoToBegin(); while(!rgbaIt.IsAtEnd()){ InPixelType x = rgbaIt.Get(); + ++rgbaIt; itk::Point vertex; itk::Index<3> index = rgbaIt.GetIndex(); rgbaImage->TransformIndexToPhysicalPoint(index, vertex); outputImage->TransformPhysicalPointToIndex(vertex, index); itk::ContinuousIndex contIndex; outputImage->TransformPhysicalPointToContinuousIndex(vertex, contIndex); float frac_x = contIndex[0] - index[0]; float frac_y = contIndex[1] - index[1]; float frac_z = contIndex[2] - index[2]; int px = index[0]; if (frac_x<0) { px -= 1; frac_x += 1; } int py = index[1]; if (frac_y<0) { py -= 1; frac_y += 1; } int pz = index[2]; if (frac_z<0) { pz -= 1; frac_z += 1; } frac_x = 1-frac_x; frac_y = 1-frac_y; frac_z = 1-frac_z; // int coordinates inside image? if (px < 0 || px >= w-1) continue; if (py < 0 || py >= h-1) continue; if (pz < 0 || pz >= d-1) continue; OutPixelType out; switch (m_Channel) { case RED: out = (float)x.GetRed()/255; break; case GREEN: out = (float)x.GetGreen()/255; break; case BLUE: out = (float)x.GetBlue()/255; break; case ALPHA: out = (float)x.GetAlpha()/255; } outImageBufferPointer[( px + w*(py + h*pz ))] += out*( frac_x)*( frac_y)*( frac_z); outImageBufferPointer[( px + w*(py+1+ h*pz ))] += out*( frac_x)*(1-frac_y)*( frac_z); outImageBufferPointer[( px + w*(py + h*pz+h))] += out*( frac_x)*( frac_y)*(1-frac_z); outImageBufferPointer[( px + w*(py+1+ h*pz+h))] += out*( frac_x)*(1-frac_y)*(1-frac_z); outImageBufferPointer[( px+1 + w*(py + h*pz ))] += out*(1-frac_x)*( frac_y)*( frac_z); outImageBufferPointer[( px+1 + w*(py + h*pz+h))] += out*(1-frac_x)*( frac_y)*(1-frac_z); outImageBufferPointer[( px+1 + w*(py+1+ h*pz ))] += out*(1-frac_x)*(1-frac_y)*( frac_z); outImageBufferPointer[( px+1 + w*(py+1+ h*pz+h))] += out*(1-frac_x)*(1-frac_y)*(1-frac_z); counterImageBufferPointer[( px + w*(py + h*pz ))] += 1; counterImageBufferPointer[( px + w*(py+1+ h*pz ))] += 1; counterImageBufferPointer[( px + w*(py + h*pz+h))] += 1; counterImageBufferPointer[( px + w*(py+1+ h*pz+h))] += 1; counterImageBufferPointer[( px+1 + w*(py + h*pz ))] += 1; counterImageBufferPointer[( px+1 + w*(py + h*pz+h))] += 1; counterImageBufferPointer[( px+1 + w*(py+1+ h*pz ))] += 1; counterImageBufferPointer[( px+1 + w*(py+1+ h*pz+h))] += 1; -// outputImage->SetPixel(index, outputImage->GetPixel(index)+out); -// counterImage->SetPixel(index, counterImage->GetPixel(index)+1); - - ++rgbaIt; } typedef ImageRegionIterator< OutputImageType > OutImageIteratorType; OutImageIteratorType outIt(outputImage, outputImage->GetLargestPossibleRegion()); outIt.GoToBegin(); typedef ImageRegionConstIterator< itk::Image< short, 3 > > CountImageIteratorType; CountImageIteratorType counterIt(counterImage, counterImage->GetLargestPossibleRegion()); counterIt.GoToBegin(); while(!outIt.IsAtEnd() && !counterIt.IsAtEnd()){ if (counterIt.Value()>0) outIt.Set(outIt.Value()/counterIt.Value()); ++outIt; ++counterIt; } } } diff --git a/Modules/DiffusionImaging/Algorithms/itkExtractChannelFromRgbaImageFilter.h b/Modules/DiffusionImaging/Algorithms/itkExtractChannelFromRgbaImageFilter.h index e9fdc6e6f4..571b6d8760 100644 --- a/Modules/DiffusionImaging/Algorithms/itkExtractChannelFromRgbaImageFilter.h +++ b/Modules/DiffusionImaging/Algorithms/itkExtractChannelFromRgbaImageFilter.h @@ -1,59 +1,57 @@ #ifndef __itkExtractChannelFromRgbaImageFilter_h__ #define __itkExtractChannelFromRgbaImageFilter_h__ #include #include #include #include namespace itk{ -template< class OutputImageType > +template< class ReferenceImageType, class OutputImageType > class ExtractChannelFromRgbaImageFilter : public ImageToImageFilter< itk::Image,3>, OutputImageType > { public: enum Channel { RED, GREEN, BLUE, ALPHA }; typedef ExtractChannelFromRgbaImageFilter Self; typedef ProcessObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; typedef itk::Image,3> InputImageType; typedef typename OutputImageType::PixelType OutPixelType; typedef typename InputImageType::PixelType InPixelType; - typedef itk::VectorImage< short int, 3 > ReferenceImageType; - itkNewMacro(Self); itkTypeMacro( ExtractChannelFromRgbaImageFilter, ImageSource ); /** Upsampling factor **/ void GenerateData(); itkSetMacro(Channel, Channel) - itkSetMacro(ReferenceImage, ReferenceImageType::Pointer) + itkSetMacro(ReferenceImage, typename ReferenceImageType::Pointer) protected: ExtractChannelFromRgbaImageFilter(); virtual ~ExtractChannelFromRgbaImageFilter(); Channel m_Channel; - ReferenceImageType::Pointer m_ReferenceImage; + typename ReferenceImageType::Pointer m_ReferenceImage; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkExtractChannelFromRgbaImageFilter.cpp" #endif #endif // __itkExtractChannelFromRgbaImageFilter_h__ diff --git a/Modules/DiffusionImaging/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.h b/Modules/DiffusionImaging/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.h new file mode 100644 index 0000000000..9ac637a7ba --- /dev/null +++ b/Modules/DiffusionImaging/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.h @@ -0,0 +1,237 @@ +/*========================================================================= + +Program: Tensor ToolKit - TTK +Module: $URL: svn://scm.gforge.inria.fr/svn/ttk/trunk/Algorithms/itkTensorImageToDiffusionImageFilter.h $ +Language: C++ +Date: $Date: 2010-06-07 13:39:13 +0200 (Mo, 07 Jun 2010) $ +Version: $Revision: 68 $ + +Copyright (c) INRIA 2010. All rights reserved. +See LICENSE.txt for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef _itk_TensorReconstructionWithEigenvalueCorrectionFilter_h_ +#define _itk_TensorReconstructionWithEigenvalueCorrectionFilter_h_ + +#include "itkImageToImageFilter.h" +#include +#include +#include + +#include +#include + + +typedef itk::VectorImage ImageType; + + + + +namespace itk +{ + + template + class TensorReconstructionWithEigenvalueCorrectionFilter + : public ImageToImageFilter< itk::Image< TDiffusionPixelType, 3 >, itk::Image,3> > + { + + public: + + typedef TensorReconstructionWithEigenvalueCorrectionFilter Self; + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + typedef ImageToImageFilter< Image< TDiffusionPixelType, 3>, + Image< DiffusionTensor3D< TTensorPixelType >, 3 > > + Superclass; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Runtime information support. */ + itkTypeMacro(TensorReconstructionWithEigenvalueCorrectionFilter, ImageToImageFilter); + + typedef TDiffusionPixelType ReferencePixelType; + typedef TDiffusionPixelType GradientPixelType; + typedef DiffusionTensor3D< TTensorPixelType > TensorPixelType; + + + /** Reference image data, This image is aquired in the absence + * of a diffusion sensitizing field gradient */ + typedef typename Superclass::InputImageType ReferenceImageType; + typedef Image< TensorPixelType, 3 > TensorImageType; + typedef TensorImageType OutputImageType; + typedef typename Superclass::OutputImageRegionType + OutputImageRegionType; + + /** Typedef defining one (of the many) gradient images. */ + typedef Image< GradientPixelType, 3 > GradientImageType; + + /** An alternative typedef defining one (of the many) gradient images. + * It will be assumed that the vectorImage has the same dimension as the + * Reference image and a vector length parameter of \c n (number of + * gradient directions) */ + typedef VectorImage< GradientPixelType, 3 > GradientImagesType; + + + + /* + /** Holds the tensor basis coefficients G_k + typedef vnl_matrix_fixed< double, 6, 6 > TensorBasisMatrixType; + + typedef vnl_matrix< double > CoefficientMatrixType; + */ + + /** Holds each magnetic field gradient used to acquire one DWImage */ + typedef vnl_vector_fixed< double, 3 > GradientDirectionType; + + /** Container to hold gradient directions of the 'n' DW measurements */ + typedef VectorContainer< unsigned int, + GradientDirectionType > GradientDirectionContainerType; + + + + /** Another set method to add a gradient directions and its corresponding + * image. The image here is a VectorImage. The user is expected to pass the + * gradient directions in a container. The ith element of the container + * corresponds to the gradient direction of the ith component image the + * VectorImage. For the baseline image, a vector of all zeros + * should be set. */ + void SetGradientImage( GradientDirectionContainerType *, + const GradientImagesType *image); + + /** Set method to set the reference image. */ + void SetReferenceImage( ReferenceImageType *referenceImage ) + { + if( m_GradientImageTypeEnumeration == GradientIsInASingleImage) + { + itkExceptionMacro( << "Cannot call both methods:" + << "AddGradientImage and SetGradientImage. Please call only one of them."); + } + + this->ProcessObject::SetNthInput( 0, referenceImage ); + + m_GradientImageTypeEnumeration = GradientIsInManyImages; + } + + /** Get reference image */ + virtual ReferenceImageType * GetReferenceImage() + { return ( static_cast< ReferenceImageType *>(this->ProcessObject::GetInput(0)) ); } + + /** Return the gradient direction. idx is 0 based */ + virtual GradientDirectionType GetGradientDirection( unsigned int idx) const + { + if( idx >= m_NumberOfGradientDirections ) + { + itkExceptionMacro( << "Gradient direction " << idx << "does not exist" ); + } + return m_GradientDirectionContainer->ElementAt( idx+1 ); + } + + itkSetMacro( BValue, TTensorPixelType); + itkSetMacro( B0Threshold, float); + + itkGetMacro(PseudoInverse, vnl_matrix); + itkGetMacro(H, vnl_matrix); + itkGetMacro(BVec, vnl_vector); + itkGetMacro(B0Mask, vnl_vector); + itkGetMacro(Voxdim, vnl_vector); + + mitk::DiffusionImage::Pointer GetOutputDiffusionImage() + { + return m_OutputDiffusionImage; + } + + ImageType::Pointer GetVectorImage() + { + return m_VectorImage; + } + + itk::Image::Pointer GetMask() + { + return m_MaskImage; + } + + + //itkGetMacro(OutputDiffusionImage, mitk::DiffusionImage) + + //itkGetMacro( GradientDirectionContainer, GradientDirectionContainerType::Pointer); + + protected: + + TensorReconstructionWithEigenvalueCorrectionFilter(); + ~TensorReconstructionWithEigenvalueCorrectionFilter() {}; + + + void GenerateData(); + + + typedef enum + { + GradientIsInASingleImage = 1, + GradientIsInManyImages, + Else + } GradientImageTypeEnumeration; + + + + private: + + void check_the_neighbors(int x, int y, int z,int f, itk::Size<3> size,itk::Index<3> ix, + typename GradientImagesType::Pointer gradientImagePointer,ImageType::IndexType pixelIndex,ImageType::Pointer corrected_diffusion, double &temp_pixel); + + void calculate_attenuation(vnl_vector org_data,vnl_vector< double > b0index, vnl_vector &atten, double mean_b,int nof,int numberb0); + + + void calculate_tensor(vnl_matrix pseudoInverse,vnl_vector atten, vnl_vector &tensor,int nof,int numberb0); + + /** Gradient image was specified in a single image or in multiple images */ + GradientImageTypeEnumeration m_GradientImageTypeEnumeration; + + /** Number of gradient measurements */ + unsigned int m_NumberOfGradientDirections; + + /** container to hold gradient directions */ + GradientDirectionContainerType::Pointer m_GradientDirectionContainer; + + + /** b-value */ + TTensorPixelType m_BValue; + + /** Number of baseline images */ + unsigned int m_NumberOfBaselineImages; + + mitk::DiffusionImage::Pointer m_OutputDiffusionImage; + + ImageType::Pointer m_VectorImage; + + float m_B0Threshold; + + + + itk::Image::Pointer m_MaskImage; + vnl_matrix m_PseudoInverse; + vnl_matrix m_H; + vnl_vector m_BVec; + vnl_vector m_B0Mask; + vnl_vector m_Voxdim; + + }; + + + + + + +} // end of namespace + + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkTensorReconstructionWithEigenvalueCorrectionFilter.txx" +#endif + + +#endif diff --git a/Modules/DiffusionImaging/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.txx b/Modules/DiffusionImaging/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.txx new file mode 100644 index 0000000000..3b2db6abc6 --- /dev/null +++ b/Modules/DiffusionImaging/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.txx @@ -0,0 +1,632 @@ +/*========================================================================= + +Program: Tensor ToolKit - TTK +Module: $URL: svn://scm.gforge.inria.fr/svn/ttk/trunk/Algorithms/itkTensorImageToDiffusionImageFilter.txx $ +Language: C++ +Date: $Date: 2010-06-07 13:39:13 +0200 (Mo, 07 Jun 2010) $ +Version: $Revision: 68 $ + +Copyright (c) INRIA 2010. All rights reserved. +See LICENSE.txt for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef _itk_TensorReconstructionWithEigenvalueCorrectionFilter_txx_ +#define _itk_TensorReconstructionWithEigenvalueCorrectioFiltern_txx_ +#endif +#include "itkImageRegionConstIterator.h" +#include +#include + +namespace itk +{ + + + template + TensorReconstructionWithEigenvalueCorrectionFilter + ::TensorReconstructionWithEigenvalueCorrectionFilter() + { + m_B0Threshold = 50.0; + } + + template + void + TensorReconstructionWithEigenvalueCorrectionFilter + ::GenerateData ( ) + { + + typedef ImageRegionConstIterator< GradientImagesType > GradientIteratorType; + typedef typename GradientImagesType::PixelType GradientVectorType; + typename GradientImagesType::Pointer gradientImagePointer = NULL; + + // Would have liked a dynamic_cast here, but seems SGI doesn't like it + // The enum will ensure that an inappropriate cast is not done + gradientImagePointer = static_cast< GradientImagesType * >( + this->ProcessObject::GetInput(0) ); + + // Size of the complete image + typename GradientImagesType::SizeType size = gradientImagePointer->GetLargestPossibleRegion().GetSize(); + + /// First filter + int nof = m_GradientDirectionContainer->Size(); + int numberb0=0; + vnl_vector< double > b0index(nof); + int cnt=0; + + + + for(int i=0; i vec = m_GradientDirectionContainer->ElementAt(i); + + if(vec[0]<0.0001 && vec[1]<0.0001 && vec[2]<0.0001 && vec[0]>-0.001&& vec[1]>-0.001 && vec[2]>-0.001) + { + numberb0++; + } + } + + + itk::Vector spacing_term = gradientImagePointer->GetSpacing(); + itk::Matrix direction_term = gradientImagePointer->GetDirection(); + + vnl_vector spacing_vnl(3); + vnl_matrix dir_vnl (3,3); + + + + for (int i=0;i<3;i++) + { + + spacing_vnl[i]=spacing_term[i]; + + for(int j=0;j<3;j++) + { + dir_vnl[i][j]=direction_term[i][j]; + + } + } + + vnl_matrix vox_dim_step (3,3); + + + for (int i=0;i<3;i++) + { + + for(int j=0;j<3;j++) + { + vox_dim_step[i][j]=spacing_vnl[i]*dir_vnl[i][j]; + + } + } + + vnl_symmetric_eigensystem eigen_spacing(vox_dim_step); + + vnl_vector vox_dim (3); + + vox_dim[0]=eigen_spacing.get_eigenvalue(0); + vox_dim[1]=eigen_spacing.get_eigenvalue(1); + vox_dim[2]=eigen_spacing.get_eigenvalue(2); + + vox_dim=vox_dim/(vox_dim.min_value()); + + + //vnl_matrix directions(nof-numberb0,3); + vnl_matrix directions(nof-numberb0,3); + + for(int i=0; i vec = m_GradientDirectionContainer->ElementAt(i); + + if(vec[0]<0.0001 && vec[1]<0.0001 && vec[2]<0.0001 && vec[0]>-0.001&& vec[1]>-0.001 && vec[2]>-0.001) + { + b0index[i]=5; + } + else + { + b0index[i]=-1; + directions[cnt][0] = vec[0]; + directions[cnt][1] = vec[1]; + directions[cnt][2] = vec[2]; + cnt++; + } + + } + + vnl_matrix dirsTimesDirsTrans = directions*directions.transpose(); + + vnl_vector< double> diagonal(nof-numberb0); + vnl_vector< double> b_vec(nof-numberb0); + vnl_vector< double> temporary(3); + + for (int i=0;i H(nof-numberb0, 6); + vnl_matrix H_org(nof-numberb0, 6); + vnl_matrix temp_3_3(3,3); + vnl_vector pre_tensor(9); + + int etbt[6] = { 0, 4, 8, 1, 5, 2 }; + + for (int i = 0; i < nof-numberb0; i++) + { + for (int j = 0; j < 3; j++) + { + temporary[j] = -directions[i][j]; + } + for (int j = 0; j < 3; j++) + { + for (int k = 0; k < 3; k++) + { + pre_tensor[k + 3 * j] = temporary[k] * directions[i][j]; + } + } + for (int j = 0; j < 6; j++) + { + H[i][j] = pre_tensor[etbt[j]]; + } + for (int j = 0; j < 3; j++) + { + H[i][3 + j] *= 2.0; + } + } + + H_org=H; + + vnl_matrix inputtopseudoinverse=H.transpose()*H; + vnl_symmetric_eigensystem eig( inputtopseudoinverse); + vnl_matrix pseudoInverse = eig.pinverse()*H.transpose(); + + pseudoInverse.print(std::cout); + std::cout << std::endl; + + + double temp_pixel; + + itk::Index<3> ix; + + // declaration of corrected diffusion image/// + ImageType::IndexType start; + start.Fill(0); + + ImageType::RegionType region(start,size); + ImageType::Pointer corrected_diffusion = ImageType::New(); + corrected_diffusion->SetRegions(region); + corrected_diffusion->SetSpacing(gradientImagePointer->GetSpacing()); + corrected_diffusion->SetOrigin(gradientImagePointer->GetOrigin()); + corrected_diffusion->SetVectorLength(nof); + corrected_diffusion->Allocate(); + ImageType::IndexType pixelIndex; + typedef itk::VariableLengthVector VariableVectorType; + VariableVectorType variableLengthVector; + variableLengthVector.SetSize(nof); + + // end of the biggest data structure for this filter + // begining of removing of negative values from the data set + + for ( int x=0;xGetPixel(ix); + for( int f=0;fSetPixel(pixelIndex, variableLengthVector); + } + } + } + + vnl_vector org_data(nof); + vnl_vector atten(nof-numberb0); + double mean_b=0.0; + double pixel=0.0; + vnl_vector tensor (6); + // Declaration of mask-1-good voxel , 0-bad one, initaialy good voxels indicated by 3 + typedef itk::Image ImageType_mask; + ImageType_mask::IndexType start_mask; + start_mask.Fill(0); + ImageType_mask::RegionType region_mask(start_mask,size); + ImageType_mask::Pointer mask = ImageType_mask::New(); + mask->SetRegions(region_mask); + mask->SetSpacing(gradientImagePointer->GetSpacing()); + mask->SetOrigin(gradientImagePointer->GetOrigin()); + mask->Allocate(); + ImageType_mask::IndexType pixelIndex_mask; + + // + int mask_cnt=0; + + // + + // end of mask declaration + // start of filling mask + for(int x=0;xGetPixel(ix); + for (int i=0;i0) + { + mean_b = mean_b + pixel4[i]; + } + } + mean_b=mean_b/numberb0; + if(mean_b>m_B0Threshold) + { + pixel = 3.0; + // + mask_cnt++; + // + } + else + { + pixel=0.0; + } + mask->SetPixel(pixelIndex_mask, pixel); + } + } + } + + // + std::cout << "Number of voxels in mask: " << mask_cnt << std::endl; + // + + // Declaration of tensor image- output of the filter + typedef itk::Image< itk::DiffusionTensor3D, 3 > TensorImageType; + TensorImageType::IndexType start_tensorImg; + start_tensorImg.Fill(0); + TensorImageType::RegionType region_tensorImg(start_tensorImg,size); + TensorImageType::Pointer tensorImg = TensorImageType::New(); + tensorImg->SetRegions(region_tensorImg); + tensorImg->SetSpacing(gradientImagePointer->GetSpacing()); + tensorImg->SetOrigin(gradientImagePointer->GetOrigin()); + tensorImg->Allocate(); + itk::Index<3> pixelIndex_tensorImg; + // end of tensor declaration + // start of the main loop for removing of voxels with negative eigen values tensors + //some important variables + vnl_matrix temp_tensor(3,3); + vnl_vector eigen_vals(3); +// double l1; + // double l2; + //double l3; + int number_of_bads=0; + int old_number_of_bads=10000000000000000; + int diff=1; + + vnl_vector< double> pixel_max(nof); + vnl_vector< double> pixel_min(nof); + + for (int i=1;i0) + { + for (int x=0;xGetPixel(ix); + itk::DiffusionTensor3D ten; + + pixelIndex_tensorImg[0]=x; + pixelIndex_tensorImg[1]=y; + pixelIndex_tensorImg[2]=z; + + if(pixel>2.0) + { + ix[0] = x; + ix[1] = y; + ix[2] = z; + GradientVectorType pt = corrected_diffusion->GetPixel(ix); + mean_b=0.0; + + + for (int i=0;i eigen_tensor(temp_tensor); + + eigen_vals[0]=eigen_tensor.get_eigenvalue(0);eigen_vals[1]=eigen_tensor.get_eigenvalue(1);eigen_vals[2]=eigen_tensor.get_eigenvalue(2); + + + if( eigen_vals[0]>0.0 && eigen_vals[1]>0.0 && eigen_vals[2]>0.0) + { + + + ten(0,0) = tensor[0]; + ten(0,1) = tensor[3]; + ten(0,2) = tensor[5]; + ten(1,1) = tensor[1]; + ten(1,2) = tensor[4]; + ten(2,2) = tensor[2]; + + + if(x==47 && y==69 && z==28) + { + std::cout <<"Joe tensor"<< "\n"; + + std::cout <SetPixel(pixelIndex_tensorImg, ten); + mask->SetPixel(ix,1.0); + }//end of if eigenvalues + else + { + number_of_bads++; + ten.Fill(0.0); + tensorImg->SetPixel(pixelIndex_tensorImg, ten); + + for (int f=0;fpixel_max[f] || pt[f]SetPixel(ix,3.0); + corrected_diffusion->SetPixel(ix, variableLengthVector); + } + }// end of if + + else if(pixel<1.0) + { + ten.Fill(0.0); + tensorImg->SetPixel(pixelIndex_tensorImg, ten); + } + + + }// end of for 3 + } + + }// end of for 1 + diff=old_number_of_bads-number_of_bads; + old_number_of_bads=number_of_bads; + std::cout << "bad voxels: " << number_of_bads << std::endl; + number_of_bads=0; + + + } //while + + this->SetNthOutput(0, tensorImg); + m_VectorImage = corrected_diffusion; + m_MaskImage = mask; + m_PseudoInverse = pseudoInverse; + m_H = H_org; + m_BVec=b_vec; + m_B0Mask=b0index; + m_Voxdim = vox_dim; + + } + + + + template + void + TensorReconstructionWithEigenvalueCorrectionFilter + ::SetGradientImage( GradientDirectionContainerType *gradientDirection, + const GradientImagesType *gradientImage ) + { + // Make sure crazy users did not call both AddGradientImage and + // SetGradientImage + if( m_GradientImageTypeEnumeration == GradientIsInManyImages ) + { + itkExceptionMacro( << "Cannot call both methods:" + << "AddGradientImage and SetGradientImage. Please call only one of them."); + } + + this->m_GradientDirectionContainer = gradientDirection; + + + unsigned int numImages = gradientDirection->Size(); + this->m_NumberOfBaselineImages = 0; + + this->m_NumberOfGradientDirections = numImages - this->m_NumberOfBaselineImages; + + // ensure that the gradient image we received has as many components as + // the number of gradient directions + if( gradientImage->GetVectorLength() != this->m_NumberOfBaselineImages + this->m_NumberOfGradientDirections ) + { + itkExceptionMacro( << this->m_NumberOfGradientDirections << " gradients + " << this->m_NumberOfBaselineImages + << "baselines = " << this->m_NumberOfGradientDirections + this->m_NumberOfBaselineImages + << " directions specified but image has " << gradientImage->GetVectorLength() + << " components."); + } + + this->ProcessObject::SetNthInput( 0, + const_cast< GradientImagesType* >(gradientImage) ); + m_GradientImageTypeEnumeration = GradientIsInASingleImage; + } + + template + void + TensorReconstructionWithEigenvalueCorrectionFilter + ::check_the_neighbors(int x, int y, int z,int f, itk::Size<3> size,itk::Index<3> ix, + typename GradientImagesType::Pointer gradientImagePointer,ImageType::IndexType pixelIndex, + ImageType::Pointer corrected_diffusion,double &temp_pixel) + { + typedef typename GradientImagesType::PixelType GradientVectorType; + + int init_i;int init_j;int init_c;int limit_i;int limit_j;int limit_c; + double tempsum=0.0; + double temp_number=0.0; + + + if(x==0){init_i=x;limit_i=x+2;} + else if(x==size[0]-1){init_i=x-1;limit_i=x+1;} + else{init_i=x-1;limit_i=x+2;} + + for (int i=init_i;iGetPixel(ix); + + if(p[f]<=0.0) + { + tempsum=tempsum + 0; + } + else + { + tempsum=tempsum+p[f]; + + temp_number=temp_number+1; + + } + } + } + }// end of for 1 + if (temp_number==0.0) + { + tempsum=0.0; + } + else + { + tempsum=tempsum/temp_number; + } + + temp_pixel = (short)tempsum; + } + + + template + void + TensorReconstructionWithEigenvalueCorrectionFilter + ::calculate_attenuation(vnl_vector org_data,vnl_vector< double > b0index,vnl_vector &atten, double mean_b,int nof, int numberb0) + { + mean_b=0.0; + + + for (int i=0;i0) + { + mean_b=mean_b+org_data[i]; + } + + } + mean_b=mean_b/numberb0; + int cnt=0; + for (int i=0;i + void + TensorReconstructionWithEigenvalueCorrectionFilter + ::calculate_tensor(vnl_matrix pseudoInverse,vnl_vector atten,vnl_vector &tensor, int nof,int numberb0) + { + for (int i=0;i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const char* mitk::FiberBundleX::COLORCODING_ORIENTATION_BASED = "Color_Orient"; //const char* mitk::FiberBundleX::COLORCODING_FA_AS_OPACITY = "Color_Orient_FA_Opacity"; const char* mitk::FiberBundleX::COLORCODING_FA_BASED = "FA_Values"; const char* mitk::FiberBundleX::COLORCODING_CUSTOM = "custom"; const char* mitk::FiberBundleX::FIBER_ID_ARRAY = "Fiber_IDs"; mitk::FiberBundleX::FiberBundleX( vtkPolyData* fiberPolyData ) : m_CurrentColorCoding(NULL) , m_NumFibers(0) { m_FiberPolyData = vtkSmartPointer::New(); if (fiberPolyData != NULL) { vtkSmartPointer cleaner = vtkSmartPointer::New(); cleaner->SetInput(fiberPolyData); cleaner->Update(); fiberPolyData = cleaner->GetOutput(); m_FiberPolyData->DeepCopy(fiberPolyData); this->DoColorCodingOrientationBased(); } if(m_FiberPolyData->GetPointData()->HasArray(COLORCODING_ORIENTATION_BASED)) MITK_DEBUG << "ok"; vtkUnsignedCharArray* tmpColors = (vtkUnsignedCharArray*) m_FiberPolyData->GetPointData()->GetArray(COLORCODING_ORIENTATION_BASED); if (tmpColors!=NULL) { int tmpColorss = tmpColors->GetNumberOfTuples(); int tmpColorc = tmpColors->GetNumberOfComponents(); } m_NumFibers = m_FiberPolyData->GetNumberOfLines(); this->UpdateFiberGeometry(); this->SetColorCoding(COLORCODING_ORIENTATION_BASED); this->GenerateFiberIds(); } mitk::FiberBundleX::~FiberBundleX() { } mitk::FiberBundleX::Pointer mitk::FiberBundleX::GetDeepCopy() { mitk::FiberBundleX::Pointer newFib = mitk::FiberBundleX::New(m_FiberPolyData); if(m_FiberPolyData->GetPointData()->HasArray(COLORCODING_ORIENTATION_BASED)) MITK_DEBUG << "ok"; vtkUnsignedCharArray* tmpColors = (vtkUnsignedCharArray*) m_FiberPolyData->GetPointData()->GetArray(COLORCODING_ORIENTATION_BASED); int tmpColorss = tmpColors->GetNumberOfTuples(); int tmpColorc = tmpColors->GetNumberOfComponents(); newFib->SetColorCoding(m_CurrentColorCoding); return newFib; } vtkSmartPointer mitk::FiberBundleX::GeneratePolyDataByIds(std::vector fiberIds) { MITK_DEBUG << "\n=====FINAL RESULT: fib_id ======\n"; MITK_DEBUG << "Number of new Fibers: " << fiberIds.size(); // iterate through the vectorcontainer hosting all desired fiber Ids vtkSmartPointer newFiberPolyData = vtkSmartPointer::New(); vtkSmartPointer newLineSet = vtkSmartPointer::New(); vtkSmartPointer newPointSet = vtkSmartPointer::New(); // if FA array available, initialize fa double array // if color orient array is available init color array vtkSmartPointer faValueArray; vtkSmartPointer colorsT; //colors and alpha value for each single point, RGBA = 4 components unsigned char rgba[4] = {0,0,0,0}; int componentSize = sizeof(rgba); if (m_FiberIdDataSet->GetPointData()->HasArray(COLORCODING_FA_BASED)){ MITK_DEBUG << "FA VALUES AVAILABLE, init array for new fiberbundle"; faValueArray = vtkSmartPointer::New(); } if (m_FiberIdDataSet->GetPointData()->HasArray(COLORCODING_ORIENTATION_BASED)){ MITK_DEBUG << "colorValues available, init array for new fiberbundle"; colorsT = vtkUnsignedCharArray::New(); colorsT->SetNumberOfComponents(componentSize); colorsT->SetName(COLORCODING_ORIENTATION_BASED); } std::vector::iterator finIt = fiberIds.begin(); while ( finIt != fiberIds.end() ) { if (*finIt < 0 || *finIt>GetNumFibers()){ MITK_INFO << "FiberID can not be negative or >NumFibers!!! check id Extraction!" << *finIt; break; } vtkSmartPointer fiber = m_FiberIdDataSet->GetCell(*finIt);//->DeepCopy(fiber); vtkSmartPointer fibPoints = fiber->GetPoints(); vtkSmartPointer newFiber = vtkSmartPointer::New(); newFiber->GetPointIds()->SetNumberOfIds( fibPoints->GetNumberOfPoints() ); for(int i=0; iGetNumberOfPoints(); i++) { // MITK_DEBUG << "id: " << fiber->GetPointId(i); // MITK_DEBUG << fibPoints->GetPoint(i)[0] << " | " << fibPoints->GetPoint(i)[1] << " | " << fibPoints->GetPoint(i)[2]; newFiber->GetPointIds()->SetId(i, newPointSet->GetNumberOfPoints()); newPointSet->InsertNextPoint(fibPoints->GetPoint(i)[0], fibPoints->GetPoint(i)[1], fibPoints->GetPoint(i)[2]); if (m_FiberIdDataSet->GetPointData()->HasArray(COLORCODING_FA_BASED)){ // MITK_DEBUG << m_FiberIdDataSet->GetPointData()->GetArray(FA_VALUE_ARRAY)->GetTuple(fiber->GetPointId(i)); } if (m_FiberIdDataSet->GetPointData()->HasArray(COLORCODING_ORIENTATION_BASED)){ // MITK_DEBUG << "ColorValue: " << m_FiberIdDataSet->GetPointData()->GetArray(COLORCODING_ORIENTATION_BASED)->GetTuple(fiber->GetPointId(i))[0]; } } newLineSet->InsertNextCell(newFiber); ++finIt; } newFiberPolyData->SetPoints(newPointSet); newFiberPolyData->SetLines(newLineSet); MITK_DEBUG << "new fiberbundle polydata points: " << newFiberPolyData->GetNumberOfPoints(); MITK_DEBUG << "new fiberbundle polydata lines: " << newFiberPolyData->GetNumberOfLines(); MITK_DEBUG << "=====================\n"; // mitk::FiberBundleX::Pointer newFib = mitk::FiberBundleX::New(newFiberPolyData); return newFiberPolyData; } // merge two fiber bundles mitk::FiberBundleX::Pointer mitk::FiberBundleX::AddBundle(mitk::FiberBundleX* fib) { vtkSmartPointer vNewPolyData = vtkSmartPointer::New(); vtkSmartPointer vNewLines = vtkSmartPointer::New(); vtkSmartPointer vNewPoints = vtkSmartPointer::New(); vtkSmartPointer vLines = m_FiberPolyData->GetLines(); vLines->InitTraversal(); // add current fiber bundle int numFibers = GetNumFibers(); for( int i=0; iGetNextCell ( numPoints, points ); vtkSmartPointer container = vtkSmartPointer::New(); for( int j=0; jInsertNextPoint(m_FiberPolyData->GetPoint(points[j])); container->GetPointIds()->InsertNextId(id); } vNewLines->InsertNextCell(container); } vLines = fib->m_FiberPolyData->GetLines(); vLines->InitTraversal(); // add new fiber bundle numFibers = fib->GetNumFibers(); for( int i=0; iGetNextCell ( numPoints, points ); vtkSmartPointer container = vtkSmartPointer::New(); for( int j=0; jInsertNextPoint(fib->m_FiberPolyData->GetPoint(points[j])); container->GetPointIds()->InsertNextId(id); } vNewLines->InsertNextCell(container); } // initialize polydata vNewPolyData->SetPoints(vNewPoints); vNewPolyData->SetLines(vNewLines); // initialize fiber bundle mitk::FiberBundleX::Pointer newFib = mitk::FiberBundleX::New(vNewPolyData); return newFib; } // subtract two fiber bundles mitk::FiberBundleX::Pointer mitk::FiberBundleX::SubtractBundle(mitk::FiberBundleX* fib) { vtkSmartPointer vNewPolyData = vtkSmartPointer::New(); vtkSmartPointer vNewLines = vtkSmartPointer::New(); vtkSmartPointer vNewPoints = vtkSmartPointer::New(); vtkSmartPointer vLines = m_FiberPolyData->GetLines(); vLines->InitTraversal(); // iterate over current fibers int numFibers = GetNumFibers(); for( int i=0; iGetNextCell ( numPoints, points ); vtkSmartPointer vLines2 = fib->m_FiberPolyData->GetLines(); vLines2->InitTraversal(); int numFibers2 = fib->GetNumFibers(); bool contained = false; for( int i2=0; i2GetNextCell ( numPoints2, points2 ); // check endpoints itk::Point point_start = GetItkPoint(m_FiberPolyData->GetPoint(points[0])); itk::Point point_end = GetItkPoint(m_FiberPolyData->GetPoint(points[numPoints-1])); itk::Point point2_start = GetItkPoint(fib->m_FiberPolyData->GetPoint(points2[0])); itk::Point point2_end = GetItkPoint(fib->m_FiberPolyData->GetPoint(points2[numPoints2-1])); if (point_start.SquaredEuclideanDistanceTo(point2_start)<=mitk::eps && point_end.SquaredEuclideanDistanceTo(point2_end)<=mitk::eps || point_start.SquaredEuclideanDistanceTo(point2_end)<=mitk::eps && point_end.SquaredEuclideanDistanceTo(point2_start)<=mitk::eps) { // further checking ??? contained = true; } } // add to result because fiber is not subtracted if (!contained) { vtkSmartPointer container = vtkSmartPointer::New(); for( int j=0; jInsertNextPoint(m_FiberPolyData->GetPoint(points[j])); container->GetPointIds()->InsertNextId(id); } vNewLines->InsertNextCell(container); } } if(vNewLines->GetNumberOfCells()==0) return NULL; // initialize polydata vNewPolyData->SetPoints(vNewPoints); vNewPolyData->SetLines(vNewLines); // initialize fiber bundle mitk::FiberBundleX::Pointer newFib = mitk::FiberBundleX::New(vNewPolyData); return newFib; } itk::Point mitk::FiberBundleX::GetItkPoint(double point[3]) { itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; return itkPoint; } /* * set polydata (additional flag to recompute fiber geometry, default = true) */ void mitk::FiberBundleX::SetFiberPolyData(vtkSmartPointer fiberPD, bool updateGeometry) { if (fiberPD == NULL) this->m_FiberPolyData = vtkSmartPointer::New(); else { m_FiberPolyData->DeepCopy(fiberPD); DoColorCodingOrientationBased(); } m_NumFibers = m_FiberPolyData->GetNumberOfLines(); if (updateGeometry) UpdateFiberGeometry(); SetColorCoding(COLORCODING_ORIENTATION_BASED); GenerateFiberIds(); } /* * return vtkPolyData */ vtkSmartPointer mitk::FiberBundleX::GetFiberPolyData() { return m_FiberPolyData; } void mitk::FiberBundleX::DoColorCodingOrientationBased() { //===== FOR WRITING A TEST ======================== // colorT size == tupelComponents * tupelElements // compare color results // to cover this code 100% also polydata needed, where colorarray already exists // + one fiber with exactly 1 point // + one fiber with 0 points //================================================= /* make sure that processing colorcoding is only called when necessary */ if ( m_FiberPolyData->GetPointData()->HasArray(COLORCODING_ORIENTATION_BASED) && m_FiberPolyData->GetNumberOfPoints() == m_FiberPolyData->GetPointData()->GetArray(COLORCODING_ORIENTATION_BASED)->GetNumberOfTuples() ) { // fiberstructure is already colorcoded MITK_DEBUG << " NO NEED TO REGENERATE COLORCODING! " ; this->ResetFiberOpacity(); this->SetColorCoding(COLORCODING_ORIENTATION_BASED); return; } /* Finally, execute color calculation */ vtkPoints* extrPoints = NULL; extrPoints = m_FiberPolyData->GetPoints(); int numOfPoints = 0; if (extrPoints!=NULL) numOfPoints = extrPoints->GetNumberOfPoints(); //colors and alpha value for each single point, RGBA = 4 components unsigned char rgba[4] = {0,0,0,0}; // int componentSize = sizeof(rgba); int componentSize = 4; vtkSmartPointer colorsT = vtkUnsignedCharArray::New(); colorsT->Allocate(numOfPoints * componentSize); colorsT->SetNumberOfComponents(componentSize); colorsT->SetName(COLORCODING_ORIENTATION_BASED); /* checkpoint: does polydata contain any fibers */ int numOfFibers = m_FiberPolyData->GetNumberOfLines(); if (numOfFibers < 1) { MITK_DEBUG << "\n ========= Number of Fibers is 0 and below ========= \n"; return; } /* extract single fibers of fiberBundle */ vtkCellArray* fiberList = m_FiberPolyData->GetLines(); fiberList->InitTraversal(); for (int fi=0; fiGetNextCell(pointsPerFiber, idList); // MITK_DEBUG << "Fib#: " << fi << " of " << numOfFibers << " pnts in fiber: " << pointsPerFiber ; /* single fiber checkpoints: is number of points valid */ if (pointsPerFiber > 1) { /* operate on points of single fiber */ for (int i=0; i 0) { /* The color value of the current point is influenced by the previous point and next point. */ vnl_vector_fixed< double, 3 > currentPntvtk(extrPoints->GetPoint(idList[i])[0], extrPoints->GetPoint(idList[i])[1],extrPoints->GetPoint(idList[i])[2]); vnl_vector_fixed< double, 3 > nextPntvtk(extrPoints->GetPoint(idList[i+1])[0], extrPoints->GetPoint(idList[i+1])[1], extrPoints->GetPoint(idList[i+1])[2]); vnl_vector_fixed< double, 3 > prevPntvtk(extrPoints->GetPoint(idList[i-1])[0], extrPoints->GetPoint(idList[i-1])[1], extrPoints->GetPoint(idList[i-1])[2]); vnl_vector_fixed< double, 3 > diff1; diff1 = currentPntvtk - nextPntvtk; vnl_vector_fixed< double, 3 > diff2; diff2 = currentPntvtk - prevPntvtk; vnl_vector_fixed< double, 3 > diff; diff = (diff1 - diff2) / 2.0; diff.normalize(); rgba[0] = (unsigned char) (255.0 * std::fabs(diff[0])); rgba[1] = (unsigned char) (255.0 * std::fabs(diff[1])); rgba[2] = (unsigned char) (255.0 * std::fabs(diff[2])); rgba[3] = (unsigned char) (255.0); } else if (i==0) { /* First point has no previous point, therefore only diff1 is taken */ vnl_vector_fixed< double, 3 > currentPntvtk(extrPoints->GetPoint(idList[i])[0], extrPoints->GetPoint(idList[i])[1],extrPoints->GetPoint(idList[i])[2]); vnl_vector_fixed< double, 3 > nextPntvtk(extrPoints->GetPoint(idList[i+1])[0], extrPoints->GetPoint(idList[i+1])[1], extrPoints->GetPoint(idList[i+1])[2]); vnl_vector_fixed< double, 3 > diff1; diff1 = currentPntvtk - nextPntvtk; diff1.normalize(); rgba[0] = (unsigned char) (255.0 * std::fabs(diff1[0])); rgba[1] = (unsigned char) (255.0 * std::fabs(diff1[1])); rgba[2] = (unsigned char) (255.0 * std::fabs(diff1[2])); rgba[3] = (unsigned char) (255.0); } else if (i==pointsPerFiber-1) { /* Last point has no next point, therefore only diff2 is taken */ vnl_vector_fixed< double, 3 > currentPntvtk(extrPoints->GetPoint(idList[i])[0], extrPoints->GetPoint(idList[i])[1],extrPoints->GetPoint(idList[i])[2]); vnl_vector_fixed< double, 3 > prevPntvtk(extrPoints->GetPoint(idList[i-1])[0], extrPoints->GetPoint(idList[i-1])[1], extrPoints->GetPoint(idList[i-1])[2]); vnl_vector_fixed< double, 3 > diff2; diff2 = currentPntvtk - prevPntvtk; diff2.normalize(); rgba[0] = (unsigned char) (255.0 * std::fabs(diff2[0])); rgba[1] = (unsigned char) (255.0 * std::fabs(diff2[1])); rgba[2] = (unsigned char) (255.0 * std::fabs(diff2[2])); rgba[3] = (unsigned char) (255.0); } colorsT->InsertTupleValue(idList[i], rgba); } //end for loop } else if (pointsPerFiber == 1) { /* a single point does not define a fiber (use vertex mechanisms instead */ continue; // colorsT->InsertTupleValue(0, rgba); } else { MITK_DEBUG << "Fiber with 0 points detected... please check your tractography algorithm!" ; continue; } }//end for loop m_FiberPolyData->GetPointData()->AddArray(colorsT); /*========================= - this is more relevant for renderer than for fiberbundleX datastructure - think about sourcing this to a explicit method which coordinates colorcoding */ this->SetColorCoding(COLORCODING_ORIENTATION_BASED); // =========================== //mini test, shall be ported to MITK TESTINGS! if (colorsT->GetSize() != numOfPoints*componentSize) MITK_DEBUG << "ALLOCATION ERROR IN INITIATING COLOR ARRAY"; } void mitk::FiberBundleX::DoColorCodingFaBased() { if(m_FiberPolyData->GetPointData()->HasArray(COLORCODING_FA_BASED) != 1 ) return; this->SetColorCoding(COLORCODING_FA_BASED); MITK_DEBUG << "FBX: done CC FA based"; this->GenerateFiberIds(); } void mitk::FiberBundleX::DoUseFaFiberOpacity() { if(m_FiberPolyData->GetPointData()->HasArray(COLORCODING_FA_BASED) != 1 ) return; if(m_FiberPolyData->GetPointData()->HasArray(COLORCODING_ORIENTATION_BASED) != 1 ) return; vtkDoubleArray* FAValArray = (vtkDoubleArray*) m_FiberPolyData->GetPointData()->GetArray(COLORCODING_FA_BASED); vtkUnsignedCharArray* ColorArray = dynamic_cast (m_FiberPolyData->GetPointData()->GetArray(COLORCODING_ORIENTATION_BASED)); for(long i=0; iGetNumberOfTuples(); i++) { double faValue = FAValArray->GetValue(i); faValue = faValue * 255.0; ColorArray->SetComponent(i,3, (unsigned char) faValue ); } this->SetColorCoding(COLORCODING_ORIENTATION_BASED); MITK_DEBUG << "FBX: done CC OPACITY"; this->GenerateFiberIds(); } void mitk::FiberBundleX::ResetFiberOpacity() { vtkUnsignedCharArray* ColorArray = dynamic_cast (m_FiberPolyData->GetPointData()->GetArray(COLORCODING_ORIENTATION_BASED)); for(long i=0; iGetNumberOfTuples(); i++) { ColorArray->SetComponent(i,3, 255.0 ); } } void mitk::FiberBundleX::SetFAMap(mitk::Image::Pointer FAimage) { MITK_DEBUG << "SetFAMap"; vtkSmartPointer faValues = vtkDoubleArray::New(); faValues->SetName(COLORCODING_FA_BASED); faValues->Allocate(m_FiberPolyData->GetNumberOfPoints()); // MITK_DEBUG << faValues->GetNumberOfTuples(); // MITK_DEBUG << faValues->GetSize(); faValues->SetNumberOfValues(m_FiberPolyData->GetNumberOfPoints()); // MITK_DEBUG << faValues->GetNumberOfTuples(); // MITK_DEBUG << faValues->GetSize(); vtkPoints* pointSet = m_FiberPolyData->GetPoints(); for(long i=0; iGetNumberOfPoints(); ++i) { Point3D px; px[0] = pointSet->GetPoint(i)[0]; px[1] = pointSet->GetPoint(i)[1]; px[2] = pointSet->GetPoint(i)[2]; double faPixelValue = 1-FAimage->GetPixelValueByWorldCoordinate(px); // faValues->InsertNextTuple1(faPixelValue); faValues->InsertValue(i, faPixelValue); // MITK_DEBUG << faPixelValue; // MITK_DEBUG << faValues->GetValue(i); } m_FiberPolyData->GetPointData()->AddArray(faValues); this->GenerateFiberIds(); if(m_FiberPolyData->GetPointData()->HasArray(COLORCODING_FA_BASED)) MITK_DEBUG << "FA VALUE ARRAY SET"; // vtkDoubleArray* valueArray = (vtkDoubleArray*) m_FiberPolyData->GetPointData()->GetArray(FA_VALUE_ARRAY); // for(long i=0; iGetNumberOfPoints(); i++) // { // MITK_DEBUG << "value at pos "<< i << ": " << valueArray->GetValue(i); // } } void mitk::FiberBundleX::GenerateFiberIds() { if (m_FiberPolyData == NULL) return; vtkSmartPointer idFiberFilter = vtkSmartPointer::New(); idFiberFilter->SetInput(m_FiberPolyData); idFiberFilter->CellIdsOn(); // idFiberFilter->PointIdsOn(); // point id's are not needed idFiberFilter->SetIdsArrayName(FIBER_ID_ARRAY); idFiberFilter->FieldDataOn(); idFiberFilter->Update(); m_FiberIdDataSet = idFiberFilter->GetOutput(); MITK_DEBUG << "Generating Fiber Ids...[done] | " << m_FiberIdDataSet->GetNumberOfCells(); } mitk::FiberBundleX::Pointer mitk::FiberBundleX::ExtractFiberSubset(mitk::PlanarFigure::Pointer pf) { std::vector tmp = ExtractFiberIdSubset(pf); if (tmp.size()<=0) return mitk::FiberBundleX::New(); vtkSmartPointer pTmp = GeneratePolyDataByIds(tmp); return mitk::FiberBundleX::New(pTmp); } std::vector mitk::FiberBundleX::ExtractFiberIdSubset(mitk::PlanarFigure::Pointer pf) { MITK_DEBUG << "Extracting fibers!"; // vector which is returned, contains all extracted FiberIds std::vector FibersInROI; /* Handle type of planarfigure */ // if incoming pf is a pfc mitk::PlanarFigureComposite::Pointer pfcomp= dynamic_cast(pf.GetPointer()); if (!pfcomp.IsNull()) { // process requested boolean operation of PFC switch (pfcomp->getOperationType()) { case 0: { MITK_DEBUG << "AND PROCESSING"; //AND //temporarly store results of the child in this vector, we need that to accumulate the std::vector childResults = this->ExtractFiberIdSubset(pfcomp->getChildAt(0)); MITK_DEBUG << "first roi got fibers in ROI: " << childResults.size(); MITK_DEBUG << "sorting..."; std::sort(childResults.begin(), childResults.end()); MITK_DEBUG << "sorting done"; std::vector AND_Assamblage(childResults.size()); //std::vector AND_Assamblage; fill(AND_Assamblage.begin(), AND_Assamblage.end(), -1); //AND_Assamblage.reserve(childResults.size()); //max size AND can reach anyway std::vector::iterator it; for (int i=1; igetNumberOfChildren(); ++i) { std::vector tmpChild = this->ExtractFiberIdSubset(pfcomp->getChildAt(i)); MITK_DEBUG << "ROI " << i << " has fibers in ROI: " << tmpChild.size(); sort(tmpChild.begin(), tmpChild.end()); it = std::set_intersection(childResults.begin(), childResults.end(), tmpChild.begin(), tmpChild.end(), AND_Assamblage.begin() ); } MITK_DEBUG << "resize Vector"; long i=0; while (i < AND_Assamblage.size() && AND_Assamblage[i] != -1){ //-1 represents a placeholder in the array ++i; } AND_Assamblage.resize(i); MITK_DEBUG << "returning AND vector, size: " << AND_Assamblage.size(); return AND_Assamblage; // break; } case 1: { //OR std::vector OR_Assamblage = this->ExtractFiberIdSubset(pfcomp->getChildAt(0)); std::vector::iterator it; MITK_DEBUG << OR_Assamblage.size(); for (int i=1; igetNumberOfChildren(); ++i) { it = OR_Assamblage.end(); std::vector tmpChild = this->ExtractFiberIdSubset(pfcomp->getChildAt(i)); OR_Assamblage.insert(it, tmpChild.begin(), tmpChild.end()); MITK_DEBUG << "ROI " << i << " has fibers in ROI: " << tmpChild.size() << " OR Assamblage: " << OR_Assamblage.size(); } sort(OR_Assamblage.begin(), OR_Assamblage.end()); it = unique(OR_Assamblage.begin(), OR_Assamblage.end()); OR_Assamblage.resize( it - OR_Assamblage.begin() ); MITK_DEBUG << "returning OR vector, size: " << OR_Assamblage.size(); return OR_Assamblage; } case 2: { //NOT //get IDs of all fibers std::vector childResults; childResults.reserve(this->GetNumFibers()); vtkSmartPointer idSet = m_FiberIdDataSet->GetCellData()->GetArray(FIBER_ID_ARRAY); MITK_DEBUG << "m_NumOfFib: " << this->GetNumFibers() << " cellIdNum: " << idSet->GetNumberOfTuples(); for(long i=0; iGetNumFibers(); i++) { MITK_DEBUG << "i: " << i << " idset: " << idSet->GetTuple(i)[0]; childResults.push_back(idSet->GetTuple(i)[0]); } std::sort(childResults.begin(), childResults.end()); std::vector NOT_Assamblage(childResults.size()); //fill it with -1, otherwise 0 will be stored and 0 can also be an ID of fiber! fill(NOT_Assamblage.begin(), NOT_Assamblage.end(), -1); std::vector::iterator it; for (long i=0; igetNumberOfChildren(); ++i) { std::vector tmpChild = ExtractFiberIdSubset(pfcomp->getChildAt(i)); sort(tmpChild.begin(), tmpChild.end()); it = std::set_difference(childResults.begin(), childResults.end(), tmpChild.begin(), tmpChild.end(), NOT_Assamblage.begin() ); } MITK_DEBUG << "resize Vector"; long i=0; while (NOT_Assamblage[i] != -1){ //-1 represents a placeholder in the array ++i; } NOT_Assamblage.resize(i); return NOT_Assamblage; } default: MITK_DEBUG << "we have an UNDEFINED composition... ERROR" ; break; } } else { mitk::Geometry2D::ConstPointer pfgeometry = pf->GetGeometry2D(); const mitk::PlaneGeometry* planeGeometry = dynamic_cast (pfgeometry.GetPointer()); Vector3D planeNormal = planeGeometry->GetNormal(); planeNormal.Normalize(); Point3D planeOrigin = planeGeometry->GetOrigin(); MITK_DEBUG << "planeOrigin: " << planeOrigin[0] << " | " << planeOrigin[1] << " | " << planeOrigin[2] << endl; MITK_DEBUG << "planeNormal: " << planeNormal[0] << " | " << planeNormal[1] << " | " << planeNormal[2] << endl; std::vector PointsOnPlane; // contains all pointIds which are crossing the cutting plane std::vector PointsInROI; // based on PointsOnPlane, all ROI relevant point IDs are stored here /* Define cutting plane by ROI (PlanarFigure) */ vtkSmartPointer plane = vtkSmartPointer::New(); plane->SetOrigin(planeOrigin[0],planeOrigin[1],planeOrigin[2]); plane->SetNormal(planeNormal[0],planeNormal[1],planeNormal[2]); //same plane but opposite normal direction. so point cloud will be reduced -> better performance // vtkSmartPointer planeR = vtkSmartPointer::New(); //define new origin along the normal but close to the original one // OriginNew = OriginOld + 1*Normal // Vector3D extendedNormal; // int multiplyFactor = 1; // extendedNormal[0] = planeNormal[0] * multiplyFactor; // extendedNormal[1] = planeNormal[1] * multiplyFactor; // extendedNormal[2] = planeNormal[2] * multiplyFactor; // Point3D RplaneOrigin = planeOrigin - extendedNormal; // planeR->SetOrigin(RplaneOrigin[0],RplaneOrigin[1],RplaneOrigin[2]); // planeR->SetNormal(-planeNormal[0],-planeNormal[1],-planeNormal[2]); // MITK_DEBUG << "RPlaneOrigin: " << RplaneOrigin[0] << " | " << RplaneOrigin[1] // << " | " << RplaneOrigin[2]; /* get all points/fibers cutting the plane */ MITK_DEBUG << "start clipping"; vtkSmartPointer clipper = vtkSmartPointer::New(); clipper->SetInput(m_FiberIdDataSet); clipper->SetClipFunction(plane); clipper->GenerateClipScalarsOn(); clipper->GenerateClippedOutputOn(); vtkSmartPointer clipperout = clipper->GetClippedOutput(); MITK_DEBUG << "end clipping"; /* for some reason clipperoutput is not initialized for futher processing * so far only writing out clipped polydata provides requested */ // MITK_DEBUG << "writing clipper output"; // vtkSmartPointer writerC = vtkSmartPointer::New(); // writerC->SetInput(clipperout1); // writerC->SetFileName("/vtkOutput/Clipping.vtk"); // writerC->SetFileTypeToASCII(); // writerC->Write(); // MITK_DEBUG << "writing done"; MITK_DEBUG << "init and update clipperoutput"; clipperout->GetPointData()->Initialize(); clipperout->Update(); MITK_DEBUG << "init and update clipperoutput completed"; // MITK_DEBUG << "start clippingRecursive"; // vtkSmartPointer Rclipper = vtkSmartPointer::New(); // Rclipper->SetInput(clipperout1); // Rclipper->SetClipFunction(planeR); // Rclipper->GenerateClipScalarsOn(); // Rclipper->GenerateClippedOutputOn(); // vtkSmartPointer clipperout = Rclipper->GetClippedOutput(); // MITK_DEBUG << "end clipping recursive"; // MITK_DEBUG << "writing clipper output 2"; // vtkSmartPointer writerC1 = vtkSmartPointer::New(); // writerC1->SetInput(clipperout); // writerC1->SetFileName("/vtkOutput/RClipping.vtk"); // writerC1->SetFileTypeToASCII(); // writerC1->Write(); // MITK_DEBUG << "init and update clipperoutput"; // clipperout->GetPointData()->Initialize(); // clipperout->Update(); // MITK_DEBUG << "init and update clipperoutput completed"; MITK_DEBUG << "STEP 1: find all points which have distance 0 to the given plane"; /*======STEP 1====== * extract all points, which are crossing the plane */ // Scalar values describe the distance between each remaining point to the given plane. Values sorted by point index vtkSmartPointer distanceList = clipperout->GetPointData()->GetScalars(); vtkIdType sizeOfList = distanceList->GetNumberOfTuples(); PointsOnPlane.reserve(sizeOfList); /* use reserve for high-performant push_back, no hidden copy procedures are processed then! * size of list can be optimized by reducing allocation, but be aware of iterator and vector size*/ for (int i=0; iGetTuple(i); // check if point is on plane. // 0.01 due to some approximation errors when calculating distance if (distance[0] >= -0.01 && distance[0] <= 0.01) PointsOnPlane.push_back(i); } // DEBUG print out all interesting points, stop where array starts with value -1. after -1 no more interesting idx are set! // std::vector::iterator rit = PointsOnPlane.begin(); // while (rit != PointsOnPlane.end() ) { // std::cout << "interesting point: " << *rit << " coord: " << clipperout->GetPoint(*rit)[0] << " | " << clipperout->GetPoint(*rit)[1] << " | " << clipperout->GetPoint(*rit)[2] << endl; // rit++; // } MITK_DEBUG << "Num Of points on plane: " << PointsOnPlane.size(); MITK_DEBUG << "Step 2: extract Interesting points with respect to given extraction planarFigure"; PointsInROI.reserve(PointsOnPlane.size()); /*=======STEP 2===== * extract ROI relevant pointIds */ mitk::PlanarCircle::Pointer circleName = mitk::PlanarCircle::New(); mitk::PlanarPolygon::Pointer polyName = mitk::PlanarPolygon::New(); if ( pf->GetNameOfClass() == circleName->GetNameOfClass() ) { //calculate circle radius mitk::Point3D V1w = pf->GetWorldControlPoint(0); //centerPoint mitk::Point3D V2w = pf->GetWorldControlPoint(1); //radiusPoint double distPF = V1w.EuclideanDistanceTo(V2w); for (int i=0; iGetPoint(PointsOnPlane[i])[0] - V1w[0]) * (clipperout->GetPoint(PointsOnPlane[i])[0] - V1w[0]) + (clipperout->GetPoint(PointsOnPlane[i])[1] - V1w[1]) * (clipperout->GetPoint(PointsOnPlane[i])[1] - V1w[1]) + (clipperout->GetPoint(PointsOnPlane[i])[2] - V1w[2]) * (clipperout->GetPoint(PointsOnPlane[i])[2] - V1w[2])) ; if( XdistPnt <= distPF) PointsInROI.push_back(PointsOnPlane[i]); } } else if ( pf->GetNameOfClass() == polyName->GetNameOfClass() ) { //create vtkPolygon using controlpoints from planarFigure polygon vtkSmartPointer polygonVtk = vtkPolygon::New(); //get the control points from pf and insert them to vtkPolygon unsigned int nrCtrlPnts = pf->GetNumberOfControlPoints(); for (int i=0; iGetPoints()->InsertNextPoint((double)pf->GetWorldControlPoint(i)[0], (double)pf->GetWorldControlPoint(i)[1], (double)pf->GetWorldControlPoint(i)[2] ); } //prepare everything for using pointInPolygon function double n[3]; polygonVtk->ComputeNormal(polygonVtk->GetPoints()->GetNumberOfPoints(), static_cast(polygonVtk->GetPoints()->GetData()->GetVoidPointer(0)), n); double bounds[6]; polygonVtk->GetPoints()->GetBounds(bounds); for (int i=0; iGetPoint(PointsOnPlane[i])[0], clipperout->GetPoint(PointsOnPlane[i])[1], clipperout->GetPoint(PointsOnPlane[i])[2]}; int isInPolygon = polygonVtk->PointInPolygon(checkIn, polygonVtk->GetPoints()->GetNumberOfPoints() , static_cast(polygonVtk->GetPoints()->GetData()->GetVoidPointer(0)), bounds, n); if( isInPolygon ) PointsInROI.push_back(PointsOnPlane[i]); } } MITK_DEBUG << "Step3: Identify fibers"; // we need to access the fiberId Array, so make sure that this array is available if (!clipperout->GetCellData()->HasArray(FIBER_ID_ARRAY)) { MITK_DEBUG << "ERROR: FiberID array does not exist, no correlation between points and fiberIds possible! Make sure calling GenerateFiberIds()"; return FibersInROI; // FibersInRoi is empty then } if (PointsInROI.size()<=0) return FibersInROI; // prepare a structure where each point id is represented as an indexId. // vector looks like: | pntId | fiberIdx | std::vector< long > pointindexFiberMap; // walk through the whole subline section and create an vector sorted by point index vtkCellArray *clipperlines = clipperout->GetLines(); clipperlines->InitTraversal(); long numOfLineCells = clipperlines->GetNumberOfCells(); long numofClippedPoints = clipperout->GetNumberOfPoints(); - pointindexFiberMap.reserve(numofClippedPoints); + pointindexFiberMap.resize(numofClippedPoints); //prepare resulting vector FibersInROI.reserve(PointsInROI.size()); MITK_DEBUG << "\n===== Pointindex based structure initialized ======\n"; // go through resulting "sub"lines which are stored as cells, "i" corresponds to current line id. for (int i=0, ic=0 ; iGetCell(ic, npts, pts); // go through point ids in hosting subline, "j" corresponds to current pointindex in current line i. eg. idx[0]=45; idx[1]=46 for (long j=0; jGetCellData()->GetArray(FIBER_ID_ARRAY)->GetTuple(i)[0] << " to pointId: " << pts[j]; pointindexFiberMap[ pts[j] ] = clipperout->GetCellData()->GetArray(FIBER_ID_ARRAY)->GetTuple(i)[0]; // MITK_DEBUG << "in array: " << pointindexFiberMap[ pts[j] ]; } } MITK_DEBUG << "\n===== Pointindex based structure finalized ======\n"; // get all Points in ROI with according fiberID for (long k = 0; k < PointsInROI.size(); k++) { //MITK_DEBUG << "point " << PointsInROI[k] << " belongs to fiber " << pointindexFiberMap[ PointsInROI[k] ]; if (pointindexFiberMap[ PointsInROI[k] ]<=GetNumFibers() && pointindexFiberMap[ PointsInROI[k] ]>=0) FibersInROI.push_back(pointindexFiberMap[ PointsInROI[k] ]); else MITK_INFO << "ERROR in ExtractFiberIdSubset; impossible fiber id detected"; } } // detecting fiberId duplicates MITK_DEBUG << "check for duplicates"; sort(FibersInROI.begin(), FibersInROI.end()); bool hasDuplicats = false; for(long i=0; i::iterator it; it = unique (FibersInROI.begin(), FibersInROI.end()); FibersInROI.resize( it - FibersInROI.begin() ); } return FibersInROI; } void mitk::FiberBundleX::UpdateFiberGeometry() { - if (m_NumFibers<=0) + if (m_NumFibers<=0) // no fibers present; apply default geometry { mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->SetImageGeometry(true); float b[] = {0, 1, 0, 1, 0, 1}; geometry->SetFloatBounds(b); SetGeometry(geometry); return; } - float min = itk::NumericTraits::min(); + float min = itk::NumericTraits::NonpositiveMin(); float max = itk::NumericTraits::max(); float b[] = {max, min, max, min, max, min}; vtkCellArray* cells = m_FiberPolyData->GetLines(); cells->InitTraversal(); for (int i=0; iGetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); int p = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; jGetPoint(j, p); if (p[0]b[1]) b[1]=p[0]; if (p[1]b[3]) b[3]=p[1]; if (p[2]b[5]) b[5]=p[2]; } } - // provide some border margin for(int i=0; i<=4; i+=2) b[i] -=10; for(int i=1; i<=5; i+=2) b[i] +=10; mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); - geometry->SetImageGeometry(true); geometry->SetFloatBounds(b); this->SetGeometry(geometry); } QStringList mitk::FiberBundleX::GetAvailableColorCodings() { QStringList availableColorCodings; int numColors = m_FiberPolyData->GetPointData()->GetNumberOfArrays(); for(int i=0; iGetPointData()->GetArrayName(i)); } //this controlstructure shall be implemented by the calling method if (availableColorCodings.isEmpty()) MITK_DEBUG << "no colorcodings available in fiberbundleX"; // for(int i=0; im_CurrentColorCoding = (char*) COLORCODING_ORIENTATION_BASED; } else if( strcmp (COLORCODING_FA_BASED,requestedColorCoding) == 0 ) { this->m_CurrentColorCoding = (char*) COLORCODING_FA_BASED; } else if( strcmp (COLORCODING_CUSTOM,requestedColorCoding) == 0 ) { this->m_CurrentColorCoding = (char*) COLORCODING_CUSTOM; } else { MITK_DEBUG << "FIBERBUNDLE X: UNKNOWN COLORCODING in FIBERBUNDLEX Datastructure"; this->m_CurrentColorCoding = (char*) COLORCODING_CUSTOM; //will cause blank colorcoding of fibers } } void mitk::FiberBundleX::DoFiberSmoothing(int pointsPerCm) { vtkSmartPointer vtkSmoothPoints = vtkPoints::New(); //in smoothpoints the interpolated points representing a fiber are stored. //in vtkcells all polylines are stored, actually all id's of them are stored vtkSmartPointer vtkSmoothCells = vtkCellArray::New(); //cellcontainer for smoothed lines vtkSmartPointer vLines = m_FiberPolyData->GetLines(); vLines->InitTraversal(); vtkIdType pointHelperCnt = 0; for (int i=0; iGetNextCell ( numPoints, pointIds ); vtkSmartPointer points = vtkSmartPointer::New(); float length = 0; itk::Point lastP; for (int j=0; jGetPoint(pointIds[j]); points->InsertNextPoint(p); if (j>0) length += sqrt(pow(p[0]-lastP[0], 2)+pow(p[1]-lastP[1], 2)+pow(p[2]-lastP[2], 2)); lastP[0] = p[0]; lastP[1] = p[1]; lastP[2] = p[2]; } length /=10; int sampling = pointsPerCm*length; /////PROCESS POLYLINE SMOOTHING///// vtkSmartPointer xSpline = vtkKochanekSpline::New(); vtkSmartPointer ySpline = vtkKochanekSpline::New(); vtkSmartPointer zSpline = vtkKochanekSpline::New(); vtkSmartPointer spline = vtkParametricSpline::New(); spline->SetXSpline(xSpline); spline->SetYSpline(ySpline); spline->SetZSpline(zSpline); spline->SetPoints(points); vtkSmartPointer functionSource = vtkParametricFunctionSource::New(); functionSource->SetParametricFunction(spline); functionSource->SetUResolution(sampling); functionSource->SetVResolution(sampling); functionSource->SetWResolution(sampling); functionSource->Update(); vtkPolyData* outputFunction = functionSource->GetOutput(); vtkPoints* tmpSmoothPnts = outputFunction->GetPoints(); //smoothPoints of current fiber vtkSmartPointer smoothLine = vtkPolyLine::New(); smoothLine->GetPointIds()->SetNumberOfIds(tmpSmoothPnts->GetNumberOfPoints()); for (int j=0; jGetNumberOfPoints(); j++) { smoothLine->GetPointIds()->SetId(j, j+pointHelperCnt); vtkSmoothPoints->InsertNextPoint(tmpSmoothPnts->GetPoint(j)); } vtkSmoothCells->InsertNextCell(smoothLine); pointHelperCnt += tmpSmoothPnts->GetNumberOfPoints(); } m_FiberPolyData = vtkSmartPointer::New(); m_FiberPolyData->SetPoints(vtkSmoothPoints); m_FiberPolyData->SetLines(vtkSmoothCells); UpdateColorCoding(); UpdateFiberGeometry(); } // Resample fiber to get equidistant points void mitk::FiberBundleX::ResampleFibers(float pointDistance) { vtkSmartPointer newPoly = vtkSmartPointer::New(); vtkSmartPointer newCellArray = vtkSmartPointer::New(); vtkSmartPointer newPoints = vtkSmartPointer::New(); vtkSmartPointer vLines = m_FiberPolyData->GetLines(); vLines->InitTraversal(); int numberOfLines = m_NumFibers; for (int i=0; iGetNextCell ( numPoints, points ); vtkSmartPointer container = vtkSmartPointer::New(); double* point = m_FiberPolyData->GetPoint(points[0]); vtkIdType pointId = newPoints->InsertNextPoint(point); container->GetPointIds()->InsertNextId(pointId); float dtau = 0; int cur_p = 1; itk::Vector dR; float normdR = 0; for (;;) { while (dtau <= pointDistance && cur_p < numPoints) { itk::Vector v1; point = m_FiberPolyData->GetPoint(points[cur_p-1]); v1[0] = point[0]; v1[1] = point[1]; v1[2] = point[2]; itk::Vector v2; point = m_FiberPolyData->GetPoint(points[cur_p]); v2[0] = point[0]; v2[1] = point[1]; v2[2] = point[2]; dR = v2 - v1; normdR = std::sqrt(dR.GetSquaredNorm()); dtau += normdR; cur_p++; } if (dtau >= pointDistance) { itk::Vector v1; point = m_FiberPolyData->GetPoint(points[cur_p-1]); v1[0] = point[0]; v1[1] = point[1]; v1[2] = point[2]; itk::Vector v2 = v1 - dR*( (dtau-pointDistance)/normdR ); pointId = newPoints->InsertNextPoint(v2.GetDataPointer()); container->GetPointIds()->InsertNextId(pointId); } else { point = m_FiberPolyData->GetPoint(points[numPoints-1]); pointId = newPoints->InsertNextPoint(point); container->GetPointIds()->InsertNextId(pointId); break; } dtau = dtau-pointDistance; } newCellArray->InsertNextCell(container); } newPoly->SetPoints(newPoints); newPoly->SetLines(newCellArray); m_FiberPolyData = newPoly; UpdateFiberGeometry(); UpdateColorCoding(); } // reapply selected colorcoding in case polydata structure has changed void mitk::FiberBundleX::UpdateColorCoding() { char* cc = GetCurrentColorCoding(); if( strcmp (COLORCODING_ORIENTATION_BASED,cc) == 0 ) DoColorCodingOrientationBased(); else if( strcmp (COLORCODING_FA_BASED,cc) == 0 ) DoColorCodingFaBased(); } /* ESSENTIAL IMPLEMENTATION OF SUPERCLASS METHODS */ void mitk::FiberBundleX::UpdateOutputInformation() { } void mitk::FiberBundleX::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::FiberBundleX::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::FiberBundleX::VerifyRequestedRegion() { return true; } void mitk::FiberBundleX::SetRequestedRegion( itk::DataObject *data ) { } diff --git a/Modules/DiffusionImaging/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.h b/Modules/DiffusionImaging/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.h index 3a4cf2dcf9..c6546fd746 100644 --- a/Modules/DiffusionImaging/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.h +++ b/Modules/DiffusionImaging/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.h @@ -1,305 +1,305 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2009-07-14 19:11:20 +0200 (Tue, 14 Jul 2009) $ Version: $Revision: 18127 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef __itkAnalyticalDiffusionQballReconstructionImageFilter_h_ #define __itkAnalyticalDiffusionQballReconstructionImageFilter_h_ #include "itkImageToImageFilter.h" #include "vnl/vnl_vector_fixed.h" #include "vnl/vnl_matrix.h" #include "vnl/algo/vnl_svd.h" #include "itkVectorContainer.h" #include "itkVectorImage.h" namespace itk{ /** \class AnalyticalDiffusionQballReconstructionImageFilter * \brief This class takes as input one or more reference image (acquired in the * absence of diffusion sensitizing gradients) and 'n' diffusion * weighted images and their gradient directions and computes an image of * orientation distribution function coefficients in a spherical harmonic basis. * * \par Inputs and Usage * \par * When you have the 'n' gradient and one or more reference images in a single * multi-component image (VectorImage), you can specify the images as * \code * filter->SetGradientImage( directionsContainer, vectorImage ); * \endcode * Note that this method is used to specify both the reference and gradient images. * This is convenient when the DWI images are read in using the * NRRD * format. Like the Nrrd format, the reference images are those components of the * vectorImage whose gradient direction is (0,0,0). If more than one reference image * is present, they are averaged prior to the reconstruction. * * \par Outputs * The output image is an image of vectors that must be understood as ODFs: * \code * Image< Vector< TPixelType, OdfNrDirections >, 3 > * \endcode * * \par Parameters * \li Threshold - Threshold on the reference image data. The output ODF will * be a null pdf for pixels in the reference image that have a value less * than this. * \li BValue - See the documentation of SetBValue(). * \li At least 6 gradient images must be specified for the filter to be able * to run. If the input gradient directions g_i are majorly sampled on one half * of the sqhere, then each input image I_i will be duplicated and assign -g_i * in order to guarantee stability of the algorithm. * \li OdfDirections - directions of resulting orientation distribution function * \li EquatorNrSamplingPoints - number of sampling points on equator when * performing Funk Radeon Transform (FRT) * \li BasisFunctionCenters - the centers of the basis functions are used for * the sRBF (spherical radial basis functions interpolation). If not set, they * will be defaulted to equal m_EquatorNrSamplingPoints * * \par Template parameters * The class is templated over * \li the pixel type of the reference and gradient images * (expected to be scalar data types) * \li the internal representation of the ODF pixels (double, float etc). * \li the number of OdfDirections * \li the number of basis function centers for the sRBF * * \par References: * \li[1] * Tuch DS, * "Q-ball imaging", Magn Reson Med. 2004 Dec;52(6):1358-72. * */ template< class TReferenceImagePixelType, class TGradientImagePixelType, class TOdfPixelType, int NOrderL, int NrOdfDirections> class AnalyticalDiffusionQballReconstructionImageFilter : public ImageToImageFilter< Image< TReferenceImagePixelType, 3 >, Image< Vector< TOdfPixelType, NrOdfDirections >, 3 > > { public: enum Normalization { QBAR_STANDARD, QBAR_B_ZERO_B_VALUE, QBAR_B_ZERO, QBAR_NONE, QBAR_ADC_ONLY, QBAR_RAW_SIGNAL, QBAR_SOLID_ANGLE, QBAR_NONNEG_SOLID_ANGLE }; typedef AnalyticalDiffusionQballReconstructionImageFilter Self; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef ImageToImageFilter< Image< TReferenceImagePixelType, 3>, Image< Vector< TOdfPixelType, NrOdfDirections >, 3 > > Superclass; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro(AnalyticalDiffusionQballReconstructionImageFilter, ImageToImageFilter); typedef TReferenceImagePixelType ReferencePixelType; typedef TGradientImagePixelType GradientPixelType; typedef Vector< TOdfPixelType, NrOdfDirections > OdfPixelType; typedef TOdfPixelType BZeroPixelType; /** Reference image data, This image is aquired in the absence * of a diffusion sensitizing field gradient */ typedef typename Superclass::InputImageType ReferenceImageType; typedef Image< OdfPixelType, 3 > OdfImageType; typedef OdfImageType OutputImageType; - typedef Image< Vector< TOdfPixelType, (unsigned int)((NOrderL*NOrderL + NOrderL + 2.0)/2.0 + NOrderL) >, 3 > CoefficientImageType; + typedef Image< Vector< TOdfPixelType, (NOrderL*NOrderL + NOrderL + 2)/2 + NOrderL >, 3 > CoefficientImageType; typedef Image< BZeroPixelType, 3 > BZeroImageType; typedef typename Superclass::OutputImageRegionType OutputImageRegionType; /** Typedef defining one (of the many) gradient images. */ typedef Image< GradientPixelType, 3 > GradientImageType; /** An alternative typedef defining one (of the many) gradient images. * It will be assumed that the vectorImage has the same dimension as the * Reference image and a vector length parameter of \c n (number of * gradient directions)*/ typedef VectorImage< GradientPixelType, 3 > GradientImagesType; /** Holds the ODF reconstruction matrix */ typedef vnl_matrix< TOdfPixelType >* OdfReconstructionMatrixType; typedef vnl_matrix< double > CoefficientMatrixType; /** Holds each magnetic field gradient used to acquire one DWImage */ typedef vnl_vector_fixed< double, 3 > GradientDirectionType; /** Container to hold gradient directions of the 'n' DW measurements */ typedef VectorContainer< unsigned int, GradientDirectionType > GradientDirectionContainerType; /** set method to add gradient directions and its corresponding * image. The image here is a VectorImage. The user is expected to pass the * gradient directions in a container. The ith element of the container * corresponds to the gradient direction of the ith component image the * VectorImage. For the baseline image, a vector of all zeros * should be set.*/ void SetGradientImage( GradientDirectionContainerType *, const GradientImagesType *image); /** Get reference image */ virtual ReferenceImageType * GetReferenceImage() { return ( static_cast< ReferenceImageType *>(this->ProcessObject::GetInput(0)) ); } /** Return the gradient direction. idx is 0 based */ virtual GradientDirectionType GetGradientDirection( unsigned int idx) const { if( idx >= m_NumberOfGradientDirections ) { itkExceptionMacro( << "Gradient direction " << idx << "does not exist" ); } return m_GradientDirectionContainer->ElementAt( idx+1 ); } static void tofile2(vnl_matrix *A, std::string fname); static double factorial(int number); static void Cart2Sph(double x, double y, double z, double* cart); static double legendre0(int l); static double spherical_harmonic(int m,int l,double theta,double phi, bool complexPart); static double Yj(int m, int k, double theta, double phi); OdfPixelType Normalize(OdfPixelType odf, typename NumericTraits::AccumulateType b0 ); vnl_vector PreNormalize( vnl_vector vec, typename NumericTraits::AccumulateType b0 ); /** Threshold on the reference image data. The output ODF will be a null * pdf for pixels in the reference image that have a value less than this * threshold. */ itkSetMacro( Threshold, ReferencePixelType ); itkGetMacro( Threshold, ReferencePixelType ); itkSetMacro( NormalizationMethod, Normalization); itkGetMacro( NormalizationMethod, Normalization ); typedef Image FloatImageType; itkGetMacro( BZeroImage, typename BZeroImageType::Pointer); itkGetMacro( ODFSumImage, typename FloatImageType::Pointer); itkGetMacro( CoefficientImage, typename CoefficientImageType::Pointer); itkSetMacro( BValue, TOdfPixelType); #ifdef GetBValue #undef GetBValue #endif itkGetConstReferenceMacro( BValue, TOdfPixelType); itkSetMacro( Lambda, double ); itkGetMacro( Lambda, double ); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(ReferenceEqualityComparableCheck, (Concept::EqualityComparable)); itkConceptMacro(TensorEqualityComparableCheck, (Concept::EqualityComparable)); itkConceptMacro(GradientConvertibleToDoubleCheck, (Concept::Convertible)); itkConceptMacro(DoubleConvertibleToTensorCheck, (Concept::Convertible)); itkConceptMacro(GradientReferenceAdditiveOperatorsCheck, (Concept::AdditiveOperators)); itkConceptMacro(ReferenceOStreamWritableCheck, (Concept::OStreamWritable)); itkConceptMacro(TensorOStreamWritableCheck, (Concept::OStreamWritable)); /** End concept checking */ #endif protected: AnalyticalDiffusionQballReconstructionImageFilter(); ~AnalyticalDiffusionQballReconstructionImageFilter() {}; void PrintSelf(std::ostream& os, Indent indent) const; void ComputeReconstructionMatrix(); void BeforeThreadedGenerateData(); void ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, int); private: OdfReconstructionMatrixType m_ReconstructionMatrix; OdfReconstructionMatrixType m_CoeffReconstructionMatrix; OdfReconstructionMatrixType m_SphericalHarmonicBasisMatrix; /** container to hold gradient directions */ GradientDirectionContainerType::Pointer m_GradientDirectionContainer; /** Number of gradient measurements */ unsigned int m_NumberOfGradientDirections; /** Number of baseline images */ unsigned int m_NumberOfBaselineImages; /** Threshold on the reference image data */ ReferencePixelType m_Threshold; /** LeBihan's b-value for normalizing tensors */ TOdfPixelType m_BValue; typename BZeroImageType::Pointer m_BZeroImage; double m_Lambda; bool m_DirectionsDuplicated; Normalization m_NormalizationMethod; int m_NumberCoefficients; vnl_matrix* m_B_t; vnl_vector* m_LP; FloatImageType::Pointer m_ODFSumImage; typename CoefficientImageType::Pointer m_CoefficientImage; TOdfPixelType m_Delta1; TOdfPixelType m_Delta2; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkAnalyticalDiffusionQballReconstructionImageFilter.cpp" #endif #endif //__itkAnalyticalDiffusionQballReconstructionImageFilter_h_ diff --git a/Modules/DiffusionImaging/Rendering/mitkFiberBundleXMapper2D.cpp b/Modules/DiffusionImaging/Rendering/mitkFiberBundleXMapper2D.cpp index 6d3115e871..5ac6db6694 100644 --- a/Modules/DiffusionImaging/Rendering/mitkFiberBundleXMapper2D.cpp +++ b/Modules/DiffusionImaging/Rendering/mitkFiberBundleXMapper2D.cpp @@ -1,321 +1,321 @@ /* * mitkFiberBundleMapper2D.cpp * mitk-all * * Created by HAL9000 on 1/17/11. * Copyright 2011 __MyCompanyName__. All rights reserved. * */ #include "mitkFiberBundleXMapper2D.h" #include #include #include #include #include //#include //#include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include mitk::FiberBundleXMapper2D::FiberBundleXMapper2D() { m_lut = vtkLookupTable::New(); m_lut->Build(); } mitk::FiberBundleXMapper2D::~FiberBundleXMapper2D() { } mitk::FiberBundleX* mitk::FiberBundleXMapper2D::GetInput() { return dynamic_cast< mitk::FiberBundleX * > ( GetData() ); } void mitk::FiberBundleXMapper2D::Update(mitk::BaseRenderer * renderer) { if ( !this->IsVisible( renderer ) ) { return; } - MITK_INFO << "MapperFBX 2D update: "; + MITK_DEBUG << "MapperFBX 2D update: "; // Calculate time step of the input data for the specified renderer (integer value) // this method is implemented in mitkMapper this->CalculateTimeStep( renderer ); //check if updates occured in the node or on the display FBXLocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); const DataNode *node = this->GetDataNode(); if ( (localStorage->m_LastUpdateTime < node->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) //was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ) { // MITK_INFO << "UPDATE NEEDED FOR _ " << renderer->GetName(); this->GenerateDataForRenderer( renderer ); } if ((localStorage->m_LastUpdateTime < renderer->GetDisplayGeometry()->GetMTime()) ) //was the display geometry modified? e.g. zooming, panning) { this->UpdateShaderParameter(renderer); } } void mitk::FiberBundleXMapper2D::UpdateShaderParameter(mitk::BaseRenderer * renderer) { FBXLocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); //get information about current position of views mitk::SliceNavigationController::Pointer sliceContr = renderer->GetSliceNavigationController(); mitk::PlaneGeometry::ConstPointer planeGeo = sliceContr->GetCurrentPlaneGeometry(); //generate according cutting planes based on the view position float sliceN[3], planeOrigin[3]; // since shader uses camera coordinates, transform origin and normal from worldcoordinates to cameracoordinates planeOrigin[0] = (float) planeGeo->GetOrigin()[0]; planeOrigin[1] = (float) planeGeo->GetOrigin()[1]; planeOrigin[2] = (float) planeGeo->GetOrigin()[2]; sliceN[0] = planeGeo->GetNormal()[0]; sliceN[1] = planeGeo->GetNormal()[1]; sliceN[2] = planeGeo->GetNormal()[2]; float tmp1 = planeOrigin[0] * sliceN[0]; float tmp2 = planeOrigin[1] * sliceN[1]; float tmp3 = planeOrigin[2] * sliceN[2]; float d1 = tmp1 + tmp2 + tmp3; //attention, correct normalvector float plane1[4]; plane1[0] = sliceN[0]; plane1[1] = sliceN[1]; plane1[2] = sliceN[2]; plane1[3] = d1; float thickness = 2.0; if(!this->GetDataNode()->GetPropertyValue("Fiber2DSliceThickness",thickness)) MITK_INFO << "FIBER2D SLICE THICKNESS PROPERTY ERROR"; bool fiberfading = false; if(!this->GetDataNode()->GetPropertyValue("Fiber2DfadeEFX",fiberfading)) MITK_INFO << "FIBER2D SLICE FADE EFX PROPERTY ERROR"; int fiberfading_i = 1; if (!fiberfading) fiberfading_i = 0; // set Opacity float fiberOpacity; this->GetDataNode()->GetOpacity(fiberOpacity, NULL); localStorage->m_PointActor->GetProperty()->AddShaderVariable("slicingPlane",4, plane1); localStorage->m_PointActor->GetProperty()->AddShaderVariable("fiberThickness",1, &thickness); localStorage->m_PointActor->GetProperty()->AddShaderVariable("fiberFadingON",1, &fiberfading_i); localStorage->m_PointActor->GetProperty()->AddShaderVariable("fiberOpacity", 1, &fiberOpacity); } // ALL RAW DATA FOR VISUALIZATION IS GENERATED HERE. // vtkActors and Mappers are feeded here void mitk::FiberBundleXMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { //the handler of local storage gets feeded in this method with requested data for related renderwindow FBXLocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); //this procedure is depricated, //not needed after initializaton anymore mitk::DataNode* node = this->GetDataNode(); if ( node == NULL ) { MITK_INFO << "check DATANODE: ....[Fail] "; return; } /////////////////////////////////// ///THIS GET INPUT mitk::FiberBundleX* fbx = this->GetInput(); localStorage->m_PointMapper->ScalarVisibilityOn(); localStorage->m_PointMapper->SetScalarModeToUsePointFieldData(); localStorage->m_PointMapper->SetLookupTable(m_lut); //apply the properties after the slice was set localStorage->m_PointActor->GetProperty()->SetOpacity(0.999); // set color if (fbx->GetCurrentColorCoding() != NULL){ // localStorage->m_PointMapper->SelectColorArray(""); localStorage->m_PointMapper->SelectColorArray(fbx->GetCurrentColorCoding()); MITK_DEBUG << "MapperFBX 2D: " << fbx->GetCurrentColorCoding(); if(fbx->GetCurrentColorCoding() == fbx->COLORCODING_CUSTOM){ float temprgb[3]; this->GetDataNode()->GetColor( temprgb, NULL ); double trgb[3] = { (double) temprgb[0], (double) temprgb[1], (double) temprgb[2] }; localStorage->m_PointActor->GetProperty()->SetColor(trgb); } } localStorage->m_PointMapper->SetInput(fbx->GetFiberPolyData()); localStorage->m_PointActor->SetMapper(localStorage->m_PointMapper); localStorage->m_PointActor->GetProperty()->ShadingOn(); // Applying shading properties { mitk::ShaderRepository::GetGlobalShaderRepository()->ApplyProperties(this->GetDataNode(),localStorage->m_PointActor,renderer, localStorage->m_LastUpdateTime); } this->UpdateShaderParameter(renderer); // We have been modified => save this for next Update() localStorage->m_LastUpdateTime.Modified(); } vtkProp* mitk::FiberBundleXMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { //MITK_INFO << "FiberBundleMapper2D GetVtkProp(renderer)"; this->Update(renderer); return m_LSH.GetLocalStorage(renderer)->m_PointActor; } void mitk::FiberBundleXMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { //add shader to datano //####### load shader from file ######### QString applicationDir = QCoreApplication::applicationDirPath(); if (applicationDir.endsWith("bin")) applicationDir.append("/"); else if (applicationDir.endsWith("MacOS")) { //on osx, check if path for installer or MITK development is needed applicationDir.append("/"); QFile f( applicationDir+"FiberTrackingLUTBaryCoords.bin" ); if( !f.exists() ) // if file does not exist, then look in MITK development build directory applicationDir.append("../../../"); }else applicationDir.append("\\..\\"); mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch( applicationDir.toStdString().c_str(), false ); mitk::ShaderRepository::Pointer shaderRepository = mitk::ShaderRepository::GetGlobalShaderRepository(); shaderRepository->LoadShader(mitk::StandardFileLocations::GetInstance()->FindFile("mitkShaderFiberClipping.xml")); //#################################################################### node->SetProperty("shader",mitk::ShaderProperty::New("mitkShaderFiberClipping")); mitk::ShaderRepository::GetGlobalShaderRepository()->AddDefaultProperties(node,renderer,overwrite); //add other parameters to propertylist node->AddProperty( "Fiber2DSliceThickness", mitk::FloatProperty::New(2.0f), renderer, overwrite ); node->AddProperty( "Fiber2DfadeEFX", mitk::BoolProperty::New(true), renderer, overwrite ); Superclass::SetDefaultProperties(node, renderer, overwrite); } // following methods are essential, they actually call the GetVtkProp() method // which returns the desired actors void mitk::FiberBundleXMapper2D::MitkRenderOverlay(BaseRenderer* renderer) { // MITK_INFO << "FiberBundleMapper2D MitkRenderOVerlay(renderer)"; if ( this->IsVisible(renderer)==false ) return; if ( this->GetVtkProp(renderer)->GetVisibility() ) { this->GetVtkProp(renderer)->RenderOverlay(renderer->GetVtkRenderer()); } } void mitk::FiberBundleXMapper2D::MitkRenderOpaqueGeometry(BaseRenderer* renderer) { // MITK_INFO << "FiberBundleMapper2D MitkRenderOpaqueGeometry(renderer)"; if ( this->IsVisible( renderer )==false ) return; if ( this->GetVtkProp(renderer)->GetVisibility() ) this->GetVtkProp(renderer)->RenderOpaqueGeometry( renderer->GetVtkRenderer() ); } void mitk::FiberBundleXMapper2D::MitkRenderTranslucentGeometry(BaseRenderer* renderer) { // MITK_INFO << "FiberBundleMapper2D MitkRenderTranslucentGeometry(renderer)"; if ( this->IsVisible(renderer)==false ) return; //TODO is it possible to have a visible BaseRenderer AND an invisible VtkRenderer??? if ( this->GetVtkProp(renderer)->GetVisibility() ) this->GetVtkProp(renderer)->RenderTranslucentPolygonalGeometry(renderer->GetVtkRenderer()); } void mitk::FiberBundleXMapper2D::MitkRenderVolumetricGeometry(BaseRenderer* renderer) { // MITK_INFO << "FiberBundleMapper2D MitkRenderVolumentricGeometry(renderer)"; if(IsVisible(renderer)==false) return; //TODO is it possible to have a visible BaseRenderer AND an invisible VtkRenderer??? if ( GetVtkProp(renderer)->GetVisibility() ) this->GetVtkProp(renderer)->RenderVolumetricGeometry(renderer->GetVtkRenderer()); } mitk::FiberBundleXMapper2D::FBXLocalStorage::FBXLocalStorage() { m_PointActor = vtkSmartPointer::New(); m_PointMapper = vtkSmartPointer::New(); } diff --git a/Modules/DiffusionImaging/files.cmake b/Modules/DiffusionImaging/files.cmake index 59d5212562..ed47a8eb41 100644 --- a/Modules/DiffusionImaging/files.cmake +++ b/Modules/DiffusionImaging/files.cmake @@ -1,216 +1,218 @@ set(CPP_FILES # DicomImport DicomImport/mitkDicomDiffusionImageReader.cpp DicomImport/mitkGroupDiffusionHeadersFilter.cpp DicomImport/mitkDicomDiffusionImageHeaderReader.cpp DicomImport/mitkGEDicomDiffusionImageHeaderReader.cpp DicomImport/mitkPhilipsDicomDiffusionImageHeaderReader.cpp DicomImport/mitkSiemensDicomDiffusionImageHeaderReader.cpp DicomImport/mitkSiemensMosaicDicomDiffusionImageHeaderReader.cpp # DataStructures IODataStructures/mitkDiffusionImagingObjectFactory.cpp # DataStructures -> DWI IODataStructures/DiffusionWeightedImages/mitkDiffusionImageHeaderInformation.cpp IODataStructures/DiffusionWeightedImages/mitkDiffusionImageSource.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageReader.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageWriter.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageIOFactory.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageWriterFactory.cpp IODataStructures/DiffusionWeightedImages/mitkDiffusionImageSerializer.cpp # DataStructures -> QBall IODataStructures/QBallImages/mitkQBallImageSource.cpp IODataStructures/QBallImages/mitkNrrdQBallImageReader.cpp IODataStructures/QBallImages/mitkNrrdQBallImageWriter.cpp IODataStructures/QBallImages/mitkNrrdQBallImageIOFactory.cpp IODataStructures/QBallImages/mitkNrrdQBallImageWriterFactory.cpp IODataStructures/QBallImages/mitkQBallImage.cpp IODataStructures/QBallImages/mitkQBallImageSerializer.cpp # DataStructures -> Tensor IODataStructures/TensorImages/mitkTensorImageSource.cpp IODataStructures/TensorImages/mitkNrrdTensorImageReader.cpp IODataStructures/TensorImages/mitkNrrdTensorImageWriter.cpp IODataStructures/TensorImages/mitkNrrdTensorImageIOFactory.cpp IODataStructures/TensorImages/mitkNrrdTensorImageWriterFactory.cpp IODataStructures/TensorImages/mitkTensorImage.cpp IODataStructures/TensorImages/mitkTensorImageSerializer.cpp # DataStructures -> FiberBundleX IODataStructures/FiberBundleX/mitkFiberBundleX.cpp IODataStructures/FiberBundleX/mitkFiberBundleXWriter.cpp IODataStructures/FiberBundleX/mitkFiberBundleXReader.cpp IODataStructures/FiberBundleX/mitkFiberBundleXIOFactory.cpp IODataStructures/FiberBundleX/mitkFiberBundleXWriterFactory.cpp IODataStructures/FiberBundleX/mitkFiberBundleXSerializer.cpp IODataStructures/FiberBundleX/mitkFiberBundleXThreadMonitor.cpp # DataStructures -> PlanarFigureComposite IODataStructures/PlanarFigureComposite/mitkPlanarFigureComposite.cpp # DataStructures -> Tbss IODataStructures/TbssImages/mitkTbssImageSource.cpp IODataStructures/TbssImages/mitkTbssRoiImageSource.cpp IODataStructures/TbssImages/mitkNrrdTbssImageReader.cpp IODataStructures/TbssImages/mitkNrrdTbssImageIOFactory.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageReader.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageIOFactory.cpp IODataStructures/TbssImages/mitkTbssImage.cpp IODataStructures/TbssImages/mitkTbssRoiImage.cpp IODataStructures/TbssImages/mitkNrrdTbssImageWriter.cpp IODataStructures/TbssImages/mitkNrrdTbssImageWriterFactory.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageWriter.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageWriterFactory.cpp IODataStructures/TbssImages/mitkTbssImporter.cpp # DataStructures Connectomics IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkReader.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkIOFactory.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkSerializer.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkWriter.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkWriterFactory.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkDefinitions.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsConstantsManager.cpp # Rendering Rendering/vtkMaskedProgrammableGlyphFilter.cpp Rendering/mitkCompositeMapper.cpp Rendering/mitkVectorImageVtkGlyphMapper3D.cpp Rendering/vtkOdfSource.cxx Rendering/vtkThickPlane.cxx Rendering/mitkOdfNormalizationMethodProperty.cpp Rendering/mitkOdfScaleByProperty.cpp Rendering/mitkFiberBundleXMapper2D.cpp Rendering/mitkFiberBundleXMapper3D.cpp Rendering/mitkFiberBundleXThreadMonitorMapper3D.cpp Rendering/mitkTbssImageMapper.cpp Rendering/mitkPlanarCircleMapper3D.cpp Rendering/mitkPlanarPolygonMapper3D.cpp Rendering/mitkConnectomicsNetworkMapper3D.cpp # Interactions Interactions/mitkFiberBundleInteractor.cpp # Algorithms Algorithms/mitkPartialVolumeAnalysisHistogramCalculator.cpp Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.cpp Algorithms/mitkTractAnalyzer.cpp # Algorithms Connectomics Algorithms/Connectomics/mitkConnectomicsNetworkCreator.cpp Algorithms/Connectomics/mitkConnectomicsHistogramBase.cpp Algorithms/Connectomics/mitkConnectomicsDegreeHistogram.cpp Algorithms/Connectomics/mitkConnectomicsShortestPathHistogram.cpp Algorithms/Connectomics/mitkConnectomicsBetweennessHistogram.cpp Algorithms/Connectomics/mitkConnectomicsHistogramCache.cpp Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.cpp Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingPermutationBase.cpp Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingPermutationModularity.cpp Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingManager.cpp Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingCostFunctionBase.cpp Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingCostFunctionModularity.cpp # Tractography Tractography/itkStochasticTractographyFilter.h ) set(H_FILES # Rendering Rendering/mitkDiffusionImageMapper.h Rendering/mitkTbssImageMapper.h Rendering/mitkOdfVtkMapper2D.h Rendering/mitkFiberBundleXMapper3D.h Rendering/mitkFiberBundleXMapper2D.h Rendering/mitkFiberBundleXThreadMonitorMapper3D.h Rendering/mitkPlanarCircleMapper3D.h Rendering/mitkPlanarPolygonMapper3D.h Rendering/mitkConnectomicsNetworkMapper3D.h # Reconstruction Reconstruction/itkDiffusionQballReconstructionImageFilter.h Reconstruction/mitkTeemDiffusionTensor3DReconstructionImageFilter.h Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.h Reconstruction/itkPointShell.h Reconstruction/itkOrientationDistributionFunction.h Reconstruction/itkDiffusionIntravoxelIncoherentMotionReconstructionImageFilter.h Reconstruction/itkRegularizedIVIMLocalVariationImageFilter.h Reconstruction/itkRegularizedIVIMReconstructionFilter.h Reconstruction/itkRegularizedIVIMReconstructionSingleIteration.h # IO Datastructures IODataStructures/DiffusionWeightedImages/mitkDiffusionImage.h IODataStructures/TbssImages/mitkTbssImporter.h # DataStructures -> FiberBundleX IODataStructures/FiberBundleX/mitkFiberBundleX.h IODataStructures/FiberBundleX/mitkFiberBundleXWriter.h IODataStructures/FiberBundleX/mitkFiberBundleXReader.h IODataStructures/FiberBundleX/mitkFiberBundleXIOFactory.h IODataStructures/FiberBundleX/mitkFiberBundleXWriterFactory.h IODataStructures/FiberBundleX/mitkFiberBundleXSerializer.h IODataStructures/FiberBundleX/mitkFiberBundleXThreadMonitor.h # Datastructures Connectomics IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkReader.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkIOFactory.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkSerializer.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkWriter.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkWriterFactory.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkDefinitions.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsConstantsManager.h # Tractography Tractography/itkGibbsTrackingFilter.h Tractography/itkStochasticTractographyFilter.h # Algorithms Algorithms/itkDiffusionQballGeneralizedFaImageFilter.h Algorithms/itkDiffusionQballPrepareVisualizationImageFilter.h Algorithms/itkTensorDerivedMeasurementsFilter.h Algorithms/itkBrainMaskExtractionImageFilter.h Algorithms/itkB0ImageExtractionImageFilter.h Algorithms/itkTensorImageToDiffusionImageFilter.h Algorithms/itkTensorToL2NormImageFilter.h Algorithms/itkTractDensityImageFilter.h Algorithms/itkTractsToFiberEndingsImageFilter.h Algorithms/itkTractsToRgbaImageFilter.h Algorithms/itkGaussianInterpolateImageFunction.h Algorithms/mitkPartialVolumeAnalysisHistogramCalculator.h Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.h Algorithms/itkDiffusionTensorPrincipleDirectionImageFilter.h Algorithms/itkCartesianToPolarVectorImageFilter.h Algorithms/itkPolarToCartesianVectorImageFilter.h Algorithms/itkDistanceMapFilter.h Algorithms/itkProjectionFilter.h Algorithms/itkSkeletonizationFilter.h Algorithms/itkReduceDirectionGradientsFilter.h Algorithms/itkResidualImageFilter.h Algorithms/itkExtractChannelFromRgbaImageFilter.h # Algorithms Connectomics Algorithms/Connectomics/mitkConnectomicsNetworkCreator.h Algorithms/Connectomics/mitkConnectomicsHistogramBase.h Algorithms/Connectomics/mitkConnectomicsDegreeHistogram.h Algorithms/Connectomics/mitkConnectomicsShortestPathHistogram.h Algorithms/Connectomics/mitkConnectomicsBetweennessHistogram.h Algorithms/Connectomics/mitkConnectomicsHistogramCache.h Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.h Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingPermutationBase.h Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingPermutationModularity.h Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingManager.h Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingCostFunctionBase.h Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingCostFunctionModularity.h + Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.h + ) set( TOOL_FILES ) if(WIN32) endif(WIN32) #MITK_MULTIPLEX_PICTYPE( Algorithms/mitkImageRegistrationMethod-TYPE.cpp ) diff --git a/Modules/DiffusionImaging/files.cmake~ b/Modules/DiffusionImaging/files.cmake~ index 961349c228..eeb2a72a09 100644 --- a/Modules/DiffusionImaging/files.cmake~ +++ b/Modules/DiffusionImaging/files.cmake~ @@ -1,180 +1,219 @@ -SET(CPP_FILES +set(CPP_FILES # DicomImport DicomImport/mitkDicomDiffusionImageReader.cpp DicomImport/mitkGroupDiffusionHeadersFilter.cpp DicomImport/mitkDicomDiffusionImageHeaderReader.cpp DicomImport/mitkGEDicomDiffusionImageHeaderReader.cpp DicomImport/mitkPhilipsDicomDiffusionImageHeaderReader.cpp DicomImport/mitkSiemensDicomDiffusionImageHeaderReader.cpp DicomImport/mitkSiemensMosaicDicomDiffusionImageHeaderReader.cpp # DataStructures IODataStructures/mitkDiffusionImagingObjectFactory.cpp # DataStructures -> DWI IODataStructures/DiffusionWeightedImages/mitkDiffusionImageHeaderInformation.cpp IODataStructures/DiffusionWeightedImages/mitkDiffusionImageSource.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageReader.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageWriter.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageIOFactory.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageWriterFactory.cpp IODataStructures/DiffusionWeightedImages/mitkDiffusionImageSerializer.cpp # DataStructures -> QBall IODataStructures/QBallImages/mitkQBallImageSource.cpp IODataStructures/QBallImages/mitkNrrdQBallImageReader.cpp IODataStructures/QBallImages/mitkNrrdQBallImageWriter.cpp IODataStructures/QBallImages/mitkNrrdQBallImageIOFactory.cpp IODataStructures/QBallImages/mitkNrrdQBallImageWriterFactory.cpp IODataStructures/QBallImages/mitkQBallImage.cpp IODataStructures/QBallImages/mitkQBallImageSerializer.cpp # DataStructures -> Tensor IODataStructures/TensorImages/mitkTensorImageSource.cpp IODataStructures/TensorImages/mitkNrrdTensorImageReader.cpp IODataStructures/TensorImages/mitkNrrdTensorImageWriter.cpp IODataStructures/TensorImages/mitkNrrdTensorImageIOFactory.cpp IODataStructures/TensorImages/mitkNrrdTensorImageWriterFactory.cpp IODataStructures/TensorImages/mitkTensorImage.cpp IODataStructures/TensorImages/mitkTensorImageSerializer.cpp - # DataStructures -> FiberBundle - IODataStructures/FiberBundle/mitkFiberBundle.cpp - IODataStructures/FiberBundle/mitkFiberBundleWriter.cpp - IODataStructures/FiberBundle/mitkFiberBundleReader.cpp - IODataStructures/FiberBundle/mitkFiberBundleIOFactory.cpp - IODataStructures/FiberBundle/mitkFiberBundleWriterFactory.cpp - IODataStructures/FiberBundle/mitkFiberBundleSerializer.cpp - IODataStructures/FiberBundle/mitkParticle.cpp - IODataStructures/FiberBundle/mitkParticleGrid.cpp - # DataStructures -> FiberBundleX IODataStructures/FiberBundleX/mitkFiberBundleX.cpp IODataStructures/FiberBundleX/mitkFiberBundleXWriter.cpp IODataStructures/FiberBundleX/mitkFiberBundleXReader.cpp IODataStructures/FiberBundleX/mitkFiberBundleXIOFactory.cpp IODataStructures/FiberBundleX/mitkFiberBundleXWriterFactory.cpp IODataStructures/FiberBundleX/mitkFiberBundleXSerializer.cpp IODataStructures/FiberBundleX/mitkFiberBundleXThreadMonitor.cpp - # DataStructures -> PlanarFigureComposite IODataStructures/PlanarFigureComposite/mitkPlanarFigureComposite.cpp - # DataStructures -> Tbss IODataStructures/TbssImages/mitkTbssImageSource.cpp IODataStructures/TbssImages/mitkTbssRoiImageSource.cpp IODataStructures/TbssImages/mitkNrrdTbssImageReader.cpp IODataStructures/TbssImages/mitkNrrdTbssImageIOFactory.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageReader.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageIOFactory.cpp IODataStructures/TbssImages/mitkTbssImage.cpp IODataStructures/TbssImages/mitkTbssRoiImage.cpp IODataStructures/TbssImages/mitkNrrdTbssImageWriter.cpp - IODataStructures/TbssImages/mitkNrrdTbssImageWriterFactory.cpp + IODataStructures/TbssImages/mitkNrrdTbssImageWriterFactory.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageWriter.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageWriterFactory.cpp IODataStructures/TbssImages/mitkTbssImporter.cpp - + + # DataStructures Connectomics + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.cpp + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkReader.cpp + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkIOFactory.cpp + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkSerializer.cpp + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkWriter.cpp + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkWriterFactory.cpp + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkDefinitions.cpp + IODataStructures/ConnectomicsNetwork/mitkConnectomicsConstantsManager.cpp # Rendering Rendering/vtkMaskedProgrammableGlyphFilter.cpp Rendering/mitkCompositeMapper.cpp Rendering/mitkVectorImageVtkGlyphMapper3D.cpp Rendering/vtkOdfSource.cxx Rendering/vtkThickPlane.cxx Rendering/mitkOdfNormalizationMethodProperty.cpp Rendering/mitkOdfScaleByProperty.cpp - Rendering/mitkFiberBundleMapper2D.cpp - Rendering/mitkFiberBundleMapper3D.cpp Rendering/mitkFiberBundleXMapper2D.cpp Rendering/mitkFiberBundleXMapper3D.cpp Rendering/mitkFiberBundleXThreadMonitorMapper3D.cpp - Rendering/mitkTbssImageMapper.cpp + Rendering/mitkTbssImageMapper.cpp Rendering/mitkPlanarCircleMapper3D.cpp Rendering/mitkPlanarPolygonMapper3D.cpp - + Rendering/mitkConnectomicsNetworkMapper3D.cpp + # Interactions Interactions/mitkFiberBundleInteractor.cpp # Algorithms Algorithms/mitkPartialVolumeAnalysisHistogramCalculator.cpp Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.cpp + Algorithms/mitkTractAnalyzer.cpp + + # Algorithms Connectomics + Algorithms/Connectomics/mitkConnectomicsNetworkCreator.cpp + Algorithms/Connectomics/mitkConnectomicsHistogramBase.cpp + Algorithms/Connectomics/mitkConnectomicsDegreeHistogram.cpp + Algorithms/Connectomics/mitkConnectomicsShortestPathHistogram.cpp + Algorithms/Connectomics/mitkConnectomicsBetweennessHistogram.cpp + Algorithms/Connectomics/mitkConnectomicsHistogramCache.cpp + Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.cpp + Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingPermutationBase.cpp + Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingPermutationModularity.cpp + Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingManager.cpp + Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingCostFunctionBase.cpp + Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingCostFunctionModularity.cpp # Tractography Tractography/itkStochasticTractographyFilter.h ) -SET(H_FILES +set(H_FILES # Rendering Rendering/mitkDiffusionImageMapper.h Rendering/mitkTbssImageMapper.h Rendering/mitkOdfVtkMapper2D.h - Rendering/mitkFiberBundleMapper2D.h - Rendering/mitkFiberBundleMapper3D.h Rendering/mitkFiberBundleXMapper3D.h Rendering/mitkFiberBundleXMapper2D.h Rendering/mitkFiberBundleXThreadMonitorMapper3D.h Rendering/mitkPlanarCircleMapper3D.h Rendering/mitkPlanarPolygonMapper3D.h + Rendering/mitkConnectomicsNetworkMapper3D.h # Reconstruction Reconstruction/itkDiffusionQballReconstructionImageFilter.h Reconstruction/mitkTeemDiffusionTensor3DReconstructionImageFilter.h Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.h Reconstruction/itkPointShell.h Reconstruction/itkOrientationDistributionFunction.h Reconstruction/itkDiffusionIntravoxelIncoherentMotionReconstructionImageFilter.h Reconstruction/itkRegularizedIVIMLocalVariationImageFilter.h Reconstruction/itkRegularizedIVIMReconstructionFilter.h Reconstruction/itkRegularizedIVIMReconstructionSingleIteration.h # IO Datastructures IODataStructures/DiffusionWeightedImages/mitkDiffusionImage.h - IODataStructures/FiberBundle/itkSlowPolyLineParametricPath.h + IODataStructures/TbssImages/mitkTbssImporter.h # DataStructures -> FiberBundleX IODataStructures/FiberBundleX/mitkFiberBundleX.h IODataStructures/FiberBundleX/mitkFiberBundleXWriter.h IODataStructures/FiberBundleX/mitkFiberBundleXReader.h IODataStructures/FiberBundleX/mitkFiberBundleXIOFactory.h IODataStructures/FiberBundleX/mitkFiberBundleXWriterFactory.h IODataStructures/FiberBundleX/mitkFiberBundleXSerializer.h IODataStructures/FiberBundleX/mitkFiberBundleXThreadMonitor.h - - + + # Datastructures Connectomics + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.h + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkReader.h + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkIOFactory.h + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkSerializer.h + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkWriter.h + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkWriterFactory.h + IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkDefinitions.h + IODataStructures/ConnectomicsNetwork/mitkConnectomicsConstantsManager.h # Tractography Tractography/itkGibbsTrackingFilter.h Tractography/itkStochasticTractographyFilter.h # Algorithms Algorithms/itkDiffusionQballGeneralizedFaImageFilter.h Algorithms/itkDiffusionQballPrepareVisualizationImageFilter.h Algorithms/itkTensorDerivedMeasurementsFilter.h Algorithms/itkBrainMaskExtractionImageFilter.h Algorithms/itkB0ImageExtractionImageFilter.h Algorithms/itkTensorImageToDiffusionImageFilter.h Algorithms/itkTensorToL2NormImageFilter.h - Algorithms/itkTractsToProbabilityImageFilter.h + Algorithms/itkTractDensityImageFilter.h Algorithms/itkTractsToFiberEndingsImageFilter.h + Algorithms/itkTractsToRgbaImageFilter.h Algorithms/itkGaussianInterpolateImageFunction.h Algorithms/mitkPartialVolumeAnalysisHistogramCalculator.h Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.h Algorithms/itkDiffusionTensorPrincipleDirectionImageFilter.h Algorithms/itkCartesianToPolarVectorImageFilter.h Algorithms/itkPolarToCartesianVectorImageFilter.h Algorithms/itkDistanceMapFilter.h Algorithms/itkProjectionFilter.h Algorithms/itkSkeletonizationFilter.h +<<<<<<< HEAD + Algorithms/itkReduceDirectionGradientsFilter.h + Algorithms/itkResidualImageFilter.h + + # Algorithms Connectomics + Algorithms/Connectomics/mitkConnectomicsNetworkCreator.h + Algorithms/Connectomics/mitkConnectomicsHistogramBase.h + Algorithms/Connectomics/mitkConnectomicsDegreeHistogram.h + Algorithms/Connectomics/mitkConnectomicsShortestPathHistogram.h + Algorithms/Connectomics/mitkConnectomicsBetweennessHistogram.h + Algorithms/Connectomics/mitkConnectomicsHistogramCache.h + Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.h + Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingPermutationBase.h + Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingPermutationModularity.h + Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingManager.h + Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingCostFunctionBase.h + Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingCostFunctionModularity.h +======= + Algorithms/itkFreeWaterEliminationFilter.h +>>>>>>> origin/bug-10827-fwe ) -SET( TOOL_FILES +set( TOOL_FILES ) -IF(WIN32) -ENDIF(WIN32) +if(WIN32) +endif(WIN32) #MITK_MULTIPLEX_PICTYPE( Algorithms/mitkImageRegistrationMethod-TYPE.cpp ) diff --git a/Modules/MitkExt/Algorithms/mitkCoreExtObjectFactory.cpp b/Modules/MitkExt/Algorithms/mitkCoreExtObjectFactory.cpp index 4e7c473c4c..a2ee5baa58 100644 --- a/Modules/MitkExt/Algorithms/mitkCoreExtObjectFactory.cpp +++ b/Modules/MitkExt/Algorithms/mitkCoreExtObjectFactory.cpp @@ -1,215 +1,193 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 16916 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkCoreExtObjectFactory.h" #include "mitkProperties.h" #include "mitkBaseRenderer.h" #include "mitkDataNode.h" #include "mitkCoreObjectFactory.h" #include "mitkParRecFileIOFactory.h" #include "mitkObjFileIOFactory.h" #include "mitkVtkUnstructuredGridIOFactory.h" #include "mitkStlVolumeTimeSeriesIOFactory.h" #include "mitkVtkVolumeTimeSeriesIOFactory.h" #include "mitkUnstructuredGridVtkWriterFactory.h" #include "mitkUnstructuredGridVtkWriter.h" #include #include #include #include "mitkCone.h" -#include "mitkContour.h" -#include "mitkContourMapper2D.h" -#include "mitkContourSetMapper2D.h" -#include "mitkContourSetVtkMapper3D.h" -#include "mitkContourVtkMapper3D.h" #include "mitkCuboid.h" #include "mitkCylinder.h" #include "mitkEllipsoid.h" #include "mitkMeshMapper2D.h" #include "mitkMeshVtkMapper3D.h" #include "mitkUnstructuredGridMapper2D.h" #include "mitkEnhancedPointSetVtkMapper3D.h" #include "mitkSeedsImage.h" #include "mitkUnstructuredGrid.h" #include "mitkUnstructuredGridVtkMapper3D.h" #include "mitkPolyDataGLMapper2D.h" #include "mitkGPUVolumeMapper3D.h" #include "mitkVolumeDataVtkMapper3D.h" mitk::CoreExtObjectFactory::CoreExtObjectFactory() :CoreObjectFactoryBase() { static bool alreadyDone = false; if (!alreadyDone) { MITK_DEBUG << "CoreExtObjectFactory c'tor" << std::endl; RegisterIOFactories(); itk::ObjectFactoryBase::RegisterFactory( ParRecFileIOFactory::New() ); itk::ObjectFactoryBase::RegisterFactory( ObjFileIOFactory::New() ); itk::ObjectFactoryBase::RegisterFactory( VtkUnstructuredGridIOFactory::New() ); itk::ObjectFactoryBase::RegisterFactory( StlVolumeTimeSeriesIOFactory::New() ); itk::ObjectFactoryBase::RegisterFactory( VtkVolumeTimeSeriesIOFactory::New() ); mitk::UnstructuredGridVtkWriterFactory::RegisterOneFactory(); m_FileWriters.push_back(mitk::UnstructuredGridVtkWriter::New().GetPointer()); m_FileWriters.push_back(mitk::UnstructuredGridVtkWriter::New().GetPointer()); m_FileWriters.push_back(mitk::UnstructuredGridVtkWriter::New().GetPointer()); CreateFileExtensionsMap(); alreadyDone = true; } } mitk::Mapper::Pointer mitk::CoreExtObjectFactory::CreateMapper(mitk::DataNode* node, MapperSlotId id) { mitk::Mapper::Pointer newMapper=NULL; mitk::BaseData *data = node->GetData(); if ( id == mitk::BaseRenderer::Standard2D ) { if((dynamic_cast(data)!=NULL)) { newMapper = mitk::MeshMapper2D::New(); newMapper->SetDataNode(node); } - else if((dynamic_cast(data)!=NULL)) - { - newMapper = mitk::ContourMapper2D::New(); - newMapper->SetDataNode(node); - } - else if((dynamic_cast(data)!=NULL)) - { - newMapper = mitk::ContourSetMapper2D::New(); - newMapper->SetDataNode(node); - } else if((dynamic_cast(data)!=NULL)) { newMapper = mitk::UnstructuredGridMapper2D::New(); newMapper->SetDataNode(node); } } else if ( id == mitk::BaseRenderer::Standard3D ) { if((dynamic_cast(data) != NULL)) { newMapper = mitk::GPUVolumeMapper3D::New(); newMapper->SetDataNode(node); } else if((dynamic_cast(data)!=NULL)) { newMapper = mitk::MeshVtkMapper3D::New(); newMapper->SetDataNode(node); } - else if((dynamic_cast(data)!=NULL)) - { - newMapper = mitk::ContourVtkMapper3D::New(); - newMapper->SetDataNode(node); - } - else if((dynamic_cast(data)!=NULL)) - { - newMapper = mitk::ContourSetVtkMapper3D::New(); - newMapper->SetDataNode(node); - } else if((dynamic_cast(data)!=NULL)) { newMapper = mitk::UnstructuredGridVtkMapper3D::New(); newMapper->SetDataNode(node); } } return newMapper; } void mitk::CoreExtObjectFactory::SetDefaultProperties(mitk::DataNode* node) { if(node==NULL) return; mitk::DataNode::Pointer nodePointer = node; mitk::Image::Pointer image = dynamic_cast(node->GetData()); if(image.IsNotNull() && image->IsInitialized()) { mitk::GPUVolumeMapper3D::SetDefaultProperties(node); } if (dynamic_cast(node->GetData())) { mitk::UnstructuredGridVtkMapper3D::SetDefaultProperties(node); } } const char* mitk::CoreExtObjectFactory::GetFileExtensions() { std::string fileExtension; this->CreateFileExtensions(m_FileExtensionsMap, fileExtension); return fileExtension.c_str(); }; mitk::CoreObjectFactoryBase::MultimapType mitk::CoreExtObjectFactory::GetFileExtensionsMap() { return m_FileExtensionsMap; } mitk::CoreObjectFactoryBase::MultimapType mitk::CoreExtObjectFactory::GetSaveFileExtensionsMap() { return m_SaveFileExtensionsMap; } void mitk::CoreExtObjectFactory::CreateFileExtensionsMap() { + m_FileExtensionsMap.insert(std::pair("*.mitk", "MITK scene files")); //a better place to add this file ending might be the scene serialization class + //at the moment this is not done because there is a plan to restructure the + //ObjectFactories. When this is done we have to check where we want to add this file ending. m_FileExtensionsMap.insert(std::pair("*.vtu", "VTK Unstructured Grid")); m_FileExtensionsMap.insert(std::pair("*.vtk", "VTK Unstructured Grid")); m_FileExtensionsMap.insert(std::pair("*.pvtu", "VTK Unstructured Grid")); m_SaveFileExtensionsMap.insert(std::pair("*.pvtu", "VTK Parallel XML Unstructured Grid")); m_SaveFileExtensionsMap.insert(std::pair("*.vtu", "VTK XML Unstructured Grid")); m_SaveFileExtensionsMap.insert(std::pair("*.vtk", "VTK Legacy Unstructured Grid")); } const char* mitk::CoreExtObjectFactory::GetSaveFileExtensions() { std::string fileExtension; this->CreateFileExtensions(m_SaveFileExtensionsMap, fileExtension); return fileExtension.c_str(); } void mitk::CoreExtObjectFactory::RegisterIOFactories() { } void RegisterCoreExtObjectFactory() { static bool oneCoreExtObjectFactoryRegistered = false; if ( ! oneCoreExtObjectFactoryRegistered ) { MITK_DEBUG << "Registering CoreExtObjectFactory..." << std::endl; mitk::CoreObjectFactory::GetInstance()->RegisterExtraFactory(mitk::CoreExtObjectFactory::New()); oneCoreExtObjectFactoryRegistered = true; } } diff --git a/Modules/MitkExt/Algorithms/mitkExtractDirectedPlaneImageFilter.cpp.orig b/Modules/MitkExt/Algorithms/mitkExtractDirectedPlaneImageFilter.cpp.orig deleted file mode 100644 index fdc10a7bf1..0000000000 --- a/Modules/MitkExt/Algorithms/mitkExtractDirectedPlaneImageFilter.cpp.orig +++ /dev/null @@ -1,504 +0,0 @@ -/*========================================================================= - -Program: Medical Imaging & Interaction Toolkit -Language: C++ -Date: $Date$ -Version: $Revision: $ - -Copyright (c) German Cancer Research Center, Division of Medical and -Biological Informatics. All rights reserved. -See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. - -This software is distributed WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the above copyright notices for more information. - -=========================================================================*/ - - -#include "mitkExtractDirectedPlaneImageFilter.h" -#include "mitkAbstractTransformGeometry.h" - -#include -#include -#include -#include -#include - -#include "pic2vtk.h" -#include "picimage.h" - - - -mitk::ExtractDirectedPlaneImageFilter::ExtractDirectedPlaneImageFilter() -: m_WorldGeometry(NULL) -{ - m_Reslicer = vtkImageReslice::New(); - m_TargetTimestep = 0; - m_InPlaneResampleExtentByGeometry = false; -} - -mitk::ExtractDirectedPlaneImageFilter::~ExtractDirectedPlaneImageFilter() -{ - m_WorldGeometry = NULL; - m_Reslicer->Delete(); -} - -void mitk::ExtractDirectedPlaneImageFilter::GenerateData() -{ - // A world geometry must be set... - if ( m_WorldGeometry == NULL ) - { - itkWarningMacro(<<"No world geometry has been set. Returning."); - return; - } - - Image *input = const_cast< ImageToImageFilter::InputImageType* >( this->GetInput() ); - input->Update(); - - if ( input == NULL ) - { - itkWarningMacro(<<"No input set."); - return; - } - - const TimeSlicedGeometry *inputTimeGeometry = input->GetTimeSlicedGeometry(); - if ( ( inputTimeGeometry == NULL ) - || ( inputTimeGeometry->GetTimeSteps() == 0 ) ) - { - itkWarningMacro(<<"Error reading input image geometry."); - return; - } - - // Get the target timestep; if none is set, use the lowest given. - unsigned int timestep = 0; - if ( ! m_TargetTimestep ) - { - ScalarType time = m_WorldGeometry->GetTimeBounds()[0]; - if ( time > ScalarTypeNumericTraits::NonpositiveMin() ) - { - timestep = inputTimeGeometry->MSToTimeStep( time ); - } - } - else timestep = m_TargetTimestep; - - if ( inputTimeGeometry->IsValidTime( timestep ) == false ) - { - itkWarningMacro(<<"This is not a valid timestep: "<IsVolumeSet( timestep ) ) - { - itkWarningMacro(<<"No volume data existent at given timestep "<GetLargestPossibleRegion(); - requestedRegion.SetIndex( 3, timestep ); - requestedRegion.SetSize( 3, 1 ); - requestedRegion.SetSize( 4, 1 ); - input->SetRequestedRegion( &requestedRegion ); - input->Update(); - - vtkImageData* inputData = input->GetVtkImageData( timestep ); - - if ( inputData == NULL ) - { - itkWarningMacro(<<"Could not extract vtk image data for given timestep"<GetSpacing( spacing ); - - // how big the area is in physical coordinates: widthInMM x heightInMM pixels - mitk::ScalarType widthInMM, heightInMM; - - // where we want to sample - Point3D origin; - Vector3D right, bottom, normal; - Vector3D rightInIndex, bottomInIndex; - - assert( input->GetTimeSlicedGeometry() == inputTimeGeometry ); - - // take transform of input image into account - Geometry3D* inputGeometry = inputTimeGeometry->GetGeometry3D( timestep ); - if ( inputGeometry == NULL ) - { - itkWarningMacro(<<"There is no Geometry3D at given timestep "<( m_WorldGeometry ) != NULL ) - { - const PlaneGeometry *planeGeometry = - static_cast< const PlaneGeometry * >( m_WorldGeometry ); - origin = planeGeometry->GetOrigin(); - right = planeGeometry->GetAxisVector( 0 ); - bottom = planeGeometry->GetAxisVector( 1 ); - normal = planeGeometry->GetNormal(); - - if ( m_InPlaneResampleExtentByGeometry ) - { - // Resampling grid corresponds to the current world geometry. This - // means that the spacing of the output 2D image depends on the - // currently selected world geometry, and *not* on the image itself. - - extent[0] = m_WorldGeometry->GetExtent( 0 ); - extent[1] = m_WorldGeometry->GetExtent( 1 ); - } - else - { - // Resampling grid corresponds to the input geometry. This means that - // the spacing of the output 2D image is directly derived from the - // associated input image, regardless of the currently selected world - // geometry. - inputGeometry->WorldToIndex( origin, right, rightInIndex ); - inputGeometry->WorldToIndex( origin, bottom, bottomInIndex ); - extent[0] = rightInIndex.GetNorm(); - extent[1] = bottomInIndex.GetNorm(); - } - - // Get the extent of the current world geometry and calculate resampling - // spacing therefrom. - widthInMM = m_WorldGeometry->GetExtentInMM( 0 ); - heightInMM = m_WorldGeometry->GetExtentInMM( 1 ); - - mmPerPixel[0] = widthInMM / extent[0]; - mmPerPixel[1] = heightInMM / extent[1]; - - right.Normalize(); - bottom.Normalize(); - normal.Normalize(); - - //origin += right * ( mmPerPixel[0] * 0.5 ); - //origin += bottom * ( mmPerPixel[1] * 0.5 ); - - //widthInMM -= mmPerPixel[0]; - //heightInMM -= mmPerPixel[1]; - - // Use inverse transform of the input geometry for reslicing the 3D image - m_Reslicer->SetResliceTransform( - inputGeometry->GetVtkTransform()->GetLinearInverse() ); - - // Set background level to TRANSLUCENT (see Geometry2DDataVtkMapper3D) - m_Reslicer->SetBackgroundLevel( -32768 ); - - // Check if a reference geometry does exist (as would usually be the case for - // PlaneGeometry). - // Note: this is currently not strictly required, but could facilitate - // correct plane clipping. - if ( m_WorldGeometry->GetReferenceGeometry() ) - { - // Calculate the actual bounds of the transformed plane clipped by the - // dataset bounding box; this is required for drawing the texture at the - // correct position during 3D mapping. - boundsInitialized = this->CalculateClippedPlaneBounds( - m_WorldGeometry->GetReferenceGeometry(), planeGeometry, bounds ); - } - } - - // Do we have an AbstractTransformGeometry? - else if ( dynamic_cast< const AbstractTransformGeometry * >( m_WorldGeometry ) ) - { - const mitk::AbstractTransformGeometry* abstractGeometry = - dynamic_cast< const AbstractTransformGeometry * >(m_WorldGeometry); - - extent[0] = abstractGeometry->GetParametricExtent(0); - extent[1] = abstractGeometry->GetParametricExtent(1); - - widthInMM = abstractGeometry->GetParametricExtentInMM(0); - heightInMM = abstractGeometry->GetParametricExtentInMM(1); - - mmPerPixel[0] = widthInMM / extent[0]; - mmPerPixel[1] = heightInMM / extent[1]; - - origin = abstractGeometry->GetPlane()->GetOrigin(); - - right = abstractGeometry->GetPlane()->GetAxisVector(0); - right.Normalize(); - - bottom = abstractGeometry->GetPlane()->GetAxisVector(1); - bottom.Normalize(); - - normal = abstractGeometry->GetPlane()->GetNormal(); - normal.Normalize(); - - // Use a combination of the InputGeometry *and* the possible non-rigid - // AbstractTransformGeometry for reslicing the 3D Image - vtkGeneralTransform *composedResliceTransform = vtkGeneralTransform::New(); - composedResliceTransform->Identity(); - composedResliceTransform->Concatenate( - inputGeometry->GetVtkTransform()->GetLinearInverse() ); - composedResliceTransform->Concatenate( - abstractGeometry->GetVtkAbstractTransform() - ); - - m_Reslicer->SetResliceTransform( composedResliceTransform ); - - // Set background level to BLACK instead of translucent, to avoid - // boundary artifacts (see Geometry2DDataVtkMapper3D) - m_Reslicer->SetBackgroundLevel( -1023 ); - composedResliceTransform->Delete(); - } - else - { - itkWarningMacro(<<"World Geometry has to be a PlaneGeometry or an AbstractTransformGeometry."); - return; - } - - // Make sure that the image to be resliced has a certain minimum size. - if ( (extent[0] <= 2) && (extent[1] <= 2) ) - { - itkWarningMacro(<<"Image is too small to be resliced..."); - return; - } - - vtkImageChangeInformation * unitSpacingImageFilter = vtkImageChangeInformation::New() ; - unitSpacingImageFilter->SetOutputSpacing( 1.0, 1.0, 1.0 ); - unitSpacingImageFilter->SetInput( inputData ); - - m_Reslicer->SetInput( unitSpacingImageFilter->GetOutput() ); - unitSpacingImageFilter->Delete(); - - //m_Reslicer->SetInput( inputData ); - - m_Reslicer->SetOutputDimensionality( 2 ); - m_Reslicer->SetOutputOrigin( 0.0, 0.0, 0.0 ); - - Vector2D pixelsPerMM; - pixelsPerMM[0] = 1.0 / mmPerPixel[0]; - pixelsPerMM[1] = 1.0 / mmPerPixel[1]; - - //calulate the originArray and the orientations for the reslice-filter - double originArray[3]; - itk2vtk( origin, originArray ); - - m_Reslicer->SetResliceAxesOrigin( originArray ); - - double cosines[9]; - - // direction of the X-axis of the sampled result - vnl2vtk( right.Get_vnl_vector(), cosines ); - - // direction of the Y-axis of the sampled result - vnl2vtk( bottom.Get_vnl_vector(), cosines + 3 ); - - // normal of the plane - vnl2vtk( normal.Get_vnl_vector(), cosines + 6 ); - - m_Reslicer->SetResliceAxesDirectionCosines( cosines ); - - // Determine output extent for reslicing - ScalarType size[2]; - size[0] = (bounds[1] - bounds[0]) / mmPerPixel[0]; - size[1] = (bounds[3] - bounds[2]) / mmPerPixel[1]; - - int xMin, xMax, yMin, yMax; - if ( boundsInitialized ) - { - xMin = static_cast< int >( bounds[0] / mmPerPixel[0] );//+ 0.5 ); - xMax = static_cast< int >( bounds[1] / mmPerPixel[0] );//+ 0.5 ); - yMin = static_cast< int >( bounds[2] / mmPerPixel[1] );//+ 0.5); - yMax = static_cast< int >( bounds[3] / mmPerPixel[1] );//+ 0.5 ); - } - else - { - // If no reference geometry is available, we also don't know about the - // maximum plane size; so the overlap is just ignored - - xMin = yMin = 0; - xMax = static_cast< int >( extent[0] - pixelsPerMM[0] );//+ 0.5 ); - yMax = static_cast< int >( extent[1] - pixelsPerMM[1] );//+ 0.5 ); - } - - m_Reslicer->SetOutputSpacing( mmPerPixel[0], mmPerPixel[1], 1.0 ); - // xMax and yMax are meant exclusive until now, whereas - // SetOutputExtent wants an inclusive bound. Thus, we need - // to subtract 1. - m_Reslicer->SetOutputExtent( xMin, xMax-1, yMin, yMax-1, 0, 1 ); - - // Do the reslicing. Modified() is called to make sure that the reslicer is - // executed even though the input geometry information did not change; this - // is necessary when the input /em data, but not the /em geometry changes. - m_Reslicer->Modified(); - m_Reslicer->ReleaseDataFlagOn(); - - m_Reslicer->Update(); - - // 1. Check the result - vtkImageData* reslicedImage = m_Reslicer->GetOutput(); - - mitkIpPicDescriptor *pic = Pic2vtk::convert( reslicedImage ); - - if((reslicedImage == NULL) || (reslicedImage->GetDataDimension() < 1)) - { - itkWarningMacro(<<"Reslicer returned empty image"); - return; - } - - unsigned int dimensions[2]; - dimensions[0] = (unsigned int)extent[0]; dimensions[1] = (unsigned int)extent[1]; - Vector3D spacingVector; - FillVector3D(spacingVector, mmPerPixel[0], mmPerPixel[1], 1.0); - - mitk::Image::Pointer resultImage = this->GetOutput(); - resultImage->Initialize( pic ); - resultImage->SetSpacing( spacingVector ); - resultImage->SetPicVolume( pic ); - - mitkIpPicFree(pic); - - /*unsigned int dimensions[2]; - dimensions[0] = (unsigned int)extent[0]; dimensions[1] = (unsigned int)extent[1]; - Vector3D spacingVector; - FillVector3D(spacingVector, mmPerPixel[0], mmPerPixel[1], 1.0); - - mitk::Image::Pointer resultImage = this->GetOutput(); - resultImage->Initialize(m_Reslicer->GetOutput()); - resultImage->Initialize(inputImage->GetPixelType(), 2, dimensions); - resultImage->SetSpacing(spacingVector); - resultImage->SetSlice(m_Reslicer->GetOutput());*/ -} - - -void mitk::ExtractDirectedPlaneImageFilter::GenerateOutputInformation() -{ - Superclass::GenerateOutputInformation(); -} - - -bool mitk::ExtractDirectedPlaneImageFilter -::CalculateClippedPlaneBounds( const Geometry3D *boundingGeometry, - const PlaneGeometry *planeGeometry, vtkFloatingPointType *bounds ) -{ - // Clip the plane with the bounding geometry. To do so, the corner points - // of the bounding box are transformed by the inverse transformation - // matrix, and the transformed bounding box edges derived therefrom are - // clipped with the plane z=0. The resulting min/max values are taken as - // bounds for the image reslicer. - const BoundingBox *boundingBox = boundingGeometry->GetBoundingBox(); - - BoundingBox::PointType bbMin = boundingBox->GetMinimum(); - BoundingBox::PointType bbMax = boundingBox->GetMaximum(); - BoundingBox::PointType bbCenter = boundingBox->GetCenter(); - - vtkPoints *points = vtkPoints::New(); - if(boundingGeometry->GetImageGeometry()) - { - points->InsertPoint( 0, bbMin[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); - points->InsertPoint( 1, bbMin[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); - points->InsertPoint( 2, bbMin[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); - points->InsertPoint( 3, bbMin[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); - points->InsertPoint( 4, bbMax[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); - points->InsertPoint( 5, bbMax[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); - points->InsertPoint( 6, bbMax[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); - points->InsertPoint( 7, bbMax[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); - } - else - { - points->InsertPoint( 0, bbMin[0], bbMin[1], bbMin[2] ); - points->InsertPoint( 1, bbMin[0], bbMin[1], bbMax[2] ); - points->InsertPoint( 2, bbMin[0], bbMax[1], bbMax[2] ); - points->InsertPoint( 3, bbMin[0], bbMax[1], bbMin[2] ); - points->InsertPoint( 4, bbMax[0], bbMin[1], bbMin[2] ); - points->InsertPoint( 5, bbMax[0], bbMin[1], bbMax[2] ); - points->InsertPoint( 6, bbMax[0], bbMax[1], bbMax[2] ); - points->InsertPoint( 7, bbMax[0], bbMax[1], bbMin[2] ); - } - - vtkPoints *newPoints = vtkPoints::New(); - - vtkTransform *transform = vtkTransform::New(); - transform->Identity(); - transform->Concatenate( - planeGeometry->GetVtkTransform()->GetLinearInverse() - ); - - transform->Concatenate( boundingGeometry->GetVtkTransform() ); - - transform->TransformPoints( points, newPoints ); - transform->Delete(); - - bounds[0] = bounds[2] = 10000000.0; - bounds[1] = bounds[3] = -10000000.0; - bounds[4] = bounds[5] = 0.0; - - this->LineIntersectZero( newPoints, 0, 1, bounds ); - this->LineIntersectZero( newPoints, 1, 2, bounds ); - this->LineIntersectZero( newPoints, 2, 3, bounds ); - this->LineIntersectZero( newPoints, 3, 0, bounds ); - this->LineIntersectZero( newPoints, 0, 4, bounds ); - this->LineIntersectZero( newPoints, 1, 5, bounds ); - this->LineIntersectZero( newPoints, 2, 6, bounds ); - this->LineIntersectZero( newPoints, 3, 7, bounds ); - this->LineIntersectZero( newPoints, 4, 5, bounds ); - this->LineIntersectZero( newPoints, 5, 6, bounds ); - this->LineIntersectZero( newPoints, 6, 7, bounds ); - this->LineIntersectZero( newPoints, 7, 4, bounds ); - - // clean up vtk data - points->Delete(); - newPoints->Delete(); - - if ( (bounds[0] > 9999999.0) || (bounds[2] > 9999999.0) - || (bounds[1] < -9999999.0) || (bounds[3] < -9999999.0) ) - { - return false; - } - else - { - // The resulting bounds must be adjusted by the plane spacing, since we - // we have so far dealt with index coordinates - const float *planeSpacing = planeGeometry->GetFloatSpacing(); - bounds[0] *= planeSpacing[0]; - bounds[1] *= planeSpacing[0]; - bounds[2] *= planeSpacing[1]; - bounds[3] *= planeSpacing[1]; - bounds[4] *= planeSpacing[2]; - bounds[5] *= planeSpacing[2]; - return true; - } -} - -bool mitk::ExtractDirectedPlaneImageFilter -::LineIntersectZero( vtkPoints *points, int p1, int p2, - vtkFloatingPointType *bounds ) -{ - vtkFloatingPointType point1[3]; - vtkFloatingPointType point2[3]; - points->GetPoint( p1, point1 ); - points->GetPoint( p2, point2 ); - - if ( (point1[2] * point2[2] <= 0.0) && (point1[2] != point2[2]) ) - { - double x, y; - x = ( point1[0] * point2[2] - point1[2] * point2[0] ) / ( point2[2] - point1[2] ); - y = ( point1[1] * point2[2] - point1[2] * point2[1] ) / ( point2[2] - point1[2] ); - - if ( x < bounds[0] ) { bounds[0] = x; } - if ( x > bounds[1] ) { bounds[1] = x; } - if ( y < bounds[2] ) { bounds[2] = y; } - if ( y > bounds[3] ) { bounds[3] = y; } - bounds[4] = bounds[5] = 0.0; - return true; - } - return false; -} diff --git a/Modules/MitkExt/Rendering/vtkMitkOpenGLVolumeTextureMapper3D.cpp b/Modules/MitkExt/Rendering/vtkMitkOpenGLVolumeTextureMapper3D.cpp index 13e0609f70..51830c1628 100644 --- a/Modules/MitkExt/Rendering/vtkMitkOpenGLVolumeTextureMapper3D.cpp +++ b/Modules/MitkExt/Rendering/vtkMitkOpenGLVolumeTextureMapper3D.cpp @@ -1,2425 +1,2426 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2009-07-14 19:11:20 +0200 (Tue, 14 Jul 2009) $ Version: $Revision: 18127 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifdef _OPENMP #include #endif #include "vtkWindows.h" #include "vtkMitkOpenGLVolumeTextureMapper3D.h" #include "mitkCommon.h" #define GPU_INFO MITK_INFO("mapper.vr") #define GPU_WARN MITK_WARN("mapper.vr") #include "vtkImageData.h" #include "vtkMatrix4x4.h" +#include "vtkDataArray.h" #include "vtkObjectFactory.h" #include "vtkPlane.h" #include "vtkPlaneCollection.h" #include "vtkPointData.h" #include "vtkRenderWindow.h" #include "vtkRenderer.h" #include "vtkTimerLog.h" #include "vtkVolumeProperty.h" #include "vtkTransform.h" #include "vtkLightCollection.h" #include "vtkLight.h" #include "vtkCamera.h" #include "vtkMath.h" #include "vtkOpenGLExtensionManager.h" #include "vtkgl.h" #include "vtkOpenGLRenderWindow.h" #define myGL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 #define myGL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 #define myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 const char *vtkMitkVolumeTextureMapper3D_FourDependentShadeFP = "!!ARBfp1.0\n" //# We need some temporary variables "TEMP index, normal, finalColor;\n" "TEMP temp,temp1, temp2, temp3,temp4; \n" "TEMP sampleColor;\n" "TEMP ndotl, ndoth, ndotv; \n" "TEMP lightInfo, lightResult;\n" //# We are going to use the first //# texture coordinate "ATTRIB tex0 = fragment.texcoord[0];\n" //# This is the lighting information "PARAM lightDirection = program.local[0];\n" "PARAM halfwayVector = program.local[1];\n" "PARAM coefficient = program.local[2];\n" "PARAM lightDiffColor = program.local[3]; \n" "PARAM lightSpecColor = program.local[4]; \n" "PARAM viewVector = program.local[5];\n" "PARAM constants = program.local[6];\n" //# This is our output color "OUTPUT out = result.color;\n" //# Look up the gradient direction //# in the third volume "TEX temp2, tex0, texture[0], 3D;\n" //# This normal is stored 0 to 1, change to -1 to 1 //# by multiplying by 2.0 then adding -1.0. "MAD normal, temp2, constants.x, constants.y;\n" "DP3 temp4, normal, normal;\n" "RSQ temp, temp4.x;\n" "MUL normal, normal, temp;\n" //"RCP temp4,temp.x;\n" //"MUL temp2.w,temp2.w,temp4.x;\n" //"MUL_SAT temp2.w,temp2.w,6.0;\n" "TEX sampleColor, tex0, texture[1], 3D;\n" //# Take the dot product of the light //# direction and the normal "DP3 ndotl, normal, lightDirection;\n" //# Take the dot product of the halfway //# vector and the normal "DP3 ndoth, normal, halfwayVector;\n" "DP3 ndotv, normal, viewVector;\n" //# flip if necessary for two sided lighting "MUL temp3, ndotl, constants.y; \n" "CMP ndotl, ndotv, ndotl, temp3;\n" "MUL temp3, ndoth, constants.y; \n" "CMP ndoth, ndotv, ndoth, temp3;\n" //# put the pieces together for a LIT operation "MOV lightInfo.x, ndotl.x; \n" "MOV lightInfo.y, ndoth.x; \n" "MOV lightInfo.w, coefficient.w; \n" //# compute the lighting "LIT lightResult, lightInfo;\n" //# COLOR FIX "MUL lightResult, lightResult, 4.0;\n" //# This is the ambient contribution "MUL finalColor, coefficient.x, sampleColor;\n" //# This is the diffuse contribution "MUL temp3, lightDiffColor, sampleColor;\n" "MUL temp3, temp3, lightResult.y;\n" "ADD finalColor, finalColor, temp3;\n" //# This is th specular contribution "MUL temp3, lightSpecColor, lightResult.z; \n" //# Add specular into result so far, and replace //# with the original alpha. "ADD out, finalColor, temp3;\n" "MOV out.w, temp2.w;\n" "END\n"; const char *vtkMitkVolumeTextureMapper3D_OneComponentShadeFP = "!!ARBfp1.0\n" //# This is the fragment program for one //# component data with shading //# We need some temporary variables "TEMP index, normal, finalColor;\n" "TEMP temp,temp1, temp2, temp3,temp4; \n" "TEMP sampleColor;\n" "TEMP ndotl, ndoth, ndotv; \n" "TEMP lightInfo, lightResult;\n" //# We are going to use the first //# texture coordinate "ATTRIB tex0 = fragment.texcoord[0];\n" //# This is the lighting information "PARAM lightDirection = program.local[0];\n" "PARAM halfwayVector = program.local[1];\n" "PARAM coefficient = program.local[2];\n" "PARAM lightDiffColor = program.local[3]; \n" "PARAM lightSpecColor = program.local[4]; \n" "PARAM viewVector = program.local[5];\n" "PARAM constants = program.local[6];\n" //# This is our output color "OUTPUT out = result.color;\n" //# Look up the gradient direction //# in the third volume "TEX temp2, tex0, texture[0], 3D;\n" // Gradient Compution //# Look up the scalar value / gradient //# magnitude in the first volume //"TEX temp1, tex0, texture[0], 3D;\n" /* "ADD temp3,tex0,{-0.005,0,0};\n" "TEX temp2,temp3, texture[0], 3D;\n" //"ADD temp3,tex0,{ 0.005,0,0};\n" //"TEX temp1,temp3, texture[0], 3D;\n" "SUB normal.x,temp2.y,temp1.y;\n" "ADD temp3,tex0,{0,-0.005,0};\n" "TEX temp2,temp3, texture[0], 3D;\n" //"ADD temp3,tex0,{0, 0.005,0};\n" //"TEX temp1,temp3, texture[0], 3D;\n" "SUB normal.y,temp2.y,temp1.y;\n" "ADD temp3,tex0,{0,0,-0.005};\n" "TEX temp2,temp3, texture[0], 3D;\n" //"ADD temp3,tex0,{0,0, 0.005};\n" //"TEX temp1,temp3, texture[0], 3D;\n" "SUB normal.z,temp2.y,temp1.y;\n" */ //"MOV normal,{1,1,1};\n" "MOV index.x,temp2.a;\n" //# This normal is stored 0 to 1, change to -1 to 1 //# by multiplying by 2.0 then adding -1.0. "MAD normal, temp2, constants.x, constants.y;\n" //# Swizzle this to use (a,r) as texture //# coordinates //"SWZ index, temp1, a, r, 1, 1;\n" //# Use this coordinate to look up a //# final color in the third texture //# (this is a 2D texture) "DP3 temp4, normal, normal;\n" "RSQ temp, temp4.x;\n" "RCP temp4,temp.x;\n" "MUL normal, normal, temp;\n" "MOV index.y, temp4.x;\n" "TEX sampleColor, index, texture[1], 2D;\n" //"MUL sampleColor.w,sampleColor.w,temp4.x;\n" //# Take the dot product of the light //# direction and the normal "DP3 ndotl, normal, lightDirection;\n" //# Take the dot product of the halfway //# vector and the normal "DP3 ndoth, normal, halfwayVector;\n" "DP3 ndotv, normal, viewVector;\n" //# flip if necessary for two sided lighting "MUL temp3, ndotl, constants.y; \n" "CMP ndotl, ndotv, ndotl, temp3;\n" "MUL temp3, ndoth, constants.y; \n" "CMP ndoth, ndotv, ndoth, temp3;\n" //# put the pieces together for a LIT operation "MOV lightInfo.x, ndotl.x; \n" "MOV lightInfo.y, ndoth.x; \n" "MOV lightInfo.w, coefficient.w; \n" //# compute the lighting "LIT lightResult, lightInfo;\n" //# COLOR FIX "MUL lightResult, lightResult, 4.0;\n" //# This is the ambient contribution "MUL finalColor, coefficient.x, sampleColor;\n" //# This is the diffuse contribution "MUL temp3, lightDiffColor, sampleColor;\n" "MUL temp3, temp3, lightResult.y;\n" "ADD finalColor, finalColor, temp3;\n" //# This is th specular contribution "MUL temp3, lightSpecColor, lightResult.z; \n" //# Add specular into result so far, and replace //# with the original alpha. "ADD out, finalColor, temp3;\n" "MOV out.w, sampleColor.w;\n" "END\n"; //#ifndef VTK_IMPLEMENT_MESA_CXX vtkCxxRevisionMacro(vtkMitkOpenGLVolumeTextureMapper3D, "$Revision: 1.21 $"); vtkStandardNewMacro(vtkMitkOpenGLVolumeTextureMapper3D); //#endif vtkMitkOpenGLVolumeTextureMapper3D::vtkMitkOpenGLVolumeTextureMapper3D() { //GPU_INFO << "vtkMitkOpenGLVolumeTextureMapper3D"; this->Initialized = 0; this->Volume1Index = 0; this->Volume2Index = 0; this->Volume3Index = 0; this->ColorLookupIndex = 0; this->AlphaLookupIndex = 0; this->RenderWindow = NULL; this->SupportsCompressedTexture = false; prgOneComponentShade = 0; prgRGBAShade = 0; } vtkMitkOpenGLVolumeTextureMapper3D::~vtkMitkOpenGLVolumeTextureMapper3D() { //GPU_INFO << "~vtkMitkOpenGLVolumeTextureMapper3D"; if(prgOneComponentShade) vtkgl::DeleteProgramsARB( 1, &prgOneComponentShade ); if(prgRGBAShade) vtkgl::DeleteProgramsARB( 1, &prgRGBAShade ); } // Release the graphics resources used by this texture. void vtkMitkOpenGLVolumeTextureMapper3D::ReleaseGraphicsResources(vtkWindow *renWin) { //GPU_INFO << "ReleaseGraphicsResources"; if (( this->Volume1Index || this->Volume2Index || this->Volume3Index || this->ColorLookupIndex) && renWin) { static_cast(renWin)->MakeCurrent(); #ifdef GL_VERSION_1_1 // free any textures this->DeleteTextureIndex( &this->Volume1Index ); this->DeleteTextureIndex( &this->Volume2Index ); this->DeleteTextureIndex( &this->Volume3Index ); this->DeleteTextureIndex( &this->ColorLookupIndex ); this->DeleteTextureIndex( &this->AlphaLookupIndex ); #endif } this->Volume1Index = 0; this->Volume2Index = 0; this->Volume3Index = 0; this->ColorLookupIndex = 0; this->RenderWindow = NULL; this->SupportsCompressedTexture=false; this->SupportsNonPowerOfTwoTextures=false; this->Modified(); } void vtkMitkOpenGLVolumeTextureMapper3D::Render(vtkRenderer *ren, vtkVolume *vol) { //GPU_INFO << "Render"; ren->GetRenderWindow()->MakeCurrent(); if ( !this->Initialized ) { //this->Initialize(); this->Initialize(ren); } if ( !this->RenderPossible ) { vtkErrorMacro( "required extensions not supported" ); return; } vtkMatrix4x4 *matrix = vtkMatrix4x4::New(); vtkPlaneCollection *clipPlanes; vtkPlane *plane; int numClipPlanes = 0; double planeEquation[4]; // build transformation vol->GetMatrix(matrix); matrix->Transpose(); glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT | GL_TEXTURE_BIT); int i; // Use the OpenGL clip planes clipPlanes = this->ClippingPlanes; if ( clipPlanes ) { numClipPlanes = clipPlanes->GetNumberOfItems(); if (numClipPlanes > 6) { vtkErrorMacro(<< "OpenGL guarantees only 6 additional clipping planes"); } for (i = 0; i < numClipPlanes; i++) { glEnable(static_cast(GL_CLIP_PLANE0+i)); plane = static_cast(clipPlanes->GetItemAsObject(i)); planeEquation[0] = plane->GetNormal()[0]; planeEquation[1] = plane->GetNormal()[1]; planeEquation[2] = plane->GetNormal()[2]; planeEquation[3] = -(planeEquation[0]*plane->GetOrigin()[0]+ planeEquation[1]*plane->GetOrigin()[1]+ planeEquation[2]*plane->GetOrigin()[2]); glClipPlane(static_cast(GL_CLIP_PLANE0+i),planeEquation); } } // insert model transformation glMatrixMode( GL_MODELVIEW ); glPushMatrix(); glMultMatrixd(matrix->Element[0]); glColor4f( 1.0, 1.0, 1.0, 1.0 ); // Turn lighting off - the polygon textures already have illumination glDisable( GL_LIGHTING ); vtkGraphicErrorMacro(ren->GetRenderWindow(),"Before actual render method"); this->RenderFP(ren,vol); // pop transformation matrix glMatrixMode( GL_MODELVIEW ); glPopMatrix(); matrix->Delete(); glPopAttrib(); } void vtkMitkOpenGLVolumeTextureMapper3D::RenderFP(vtkRenderer *ren, vtkVolume *vol) { //GPU_INFO << "RenderFP"; /* glAlphaFunc (GL_GREATER, static_cast(1.0/255.0)); glEnable (GL_ALPHA_TEST); */ glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); int components = this->GetInput()->GetNumberOfScalarComponents(); switch ( components ) { case 1: this->RenderOneIndependentShadeFP(ren,vol); break; case 4: this->RenderRGBAShadeFP(ren,vol); break; } vtkgl::ActiveTexture( vtkgl::TEXTURE2); glDisable( GL_TEXTURE_2D ); glDisable( vtkgl::TEXTURE_3D ); vtkgl::ActiveTexture( vtkgl::TEXTURE1); glDisable( GL_TEXTURE_2D ); glDisable( vtkgl::TEXTURE_3D ); vtkgl::ActiveTexture( vtkgl::TEXTURE0); glDisable( GL_TEXTURE_2D ); glDisable( vtkgl::TEXTURE_3D ); glDisable( GL_BLEND ); } void vtkMitkOpenGLVolumeTextureMapper3D::DeleteTextureIndex( GLuint *index ) { //GPU_INFO << "DeleteTextureIndex"; if (glIsTexture(*index)) { GLuint tempIndex; tempIndex = *index; glDeleteTextures(1, &tempIndex); *index = 0; } } void vtkMitkOpenGLVolumeTextureMapper3D::CreateTextureIndex( GLuint *index ) { //GPU_INFO << "CreateTextureIndex"; GLuint tempIndex=0; glGenTextures(1, &tempIndex); *index = static_cast(tempIndex); } void vtkMitkOpenGLVolumeTextureMapper3D::RenderPolygons( vtkRenderer *ren, vtkVolume *vol, int stages[4] ) { //GPU_INFO << "RenderPolygons"; vtkRenderWindow *renWin = ren->GetRenderWindow(); if ( renWin->CheckAbortStatus() ) { return; } double bounds[27][6]; float distance2[27]; int numIterations; int i, j, k; // No cropping case - render the whole thing if ( !this->Cropping ) { // Use the input data bounds - we'll take care of the volume's // matrix during rendering this->GetInput()->GetBounds(bounds[0]); numIterations = 1; } // Simple cropping case - render the subvolume else if ( this->CroppingRegionFlags == 0x2000 ) { this->GetCroppingRegionPlanes(bounds[0]); numIterations = 1; } // Complex cropping case - render each region in back-to-front order else { // Get the camera position double camPos[4]; ren->GetActiveCamera()->GetPosition(camPos); double volBounds[6]; this->GetInput()->GetBounds(volBounds); // Pass camera through inverse volume matrix // so that we are in the same coordinate system vtkMatrix4x4 *volMatrix = vtkMatrix4x4::New(); vol->GetMatrix( volMatrix ); camPos[3] = 1.0; volMatrix->Invert(); volMatrix->MultiplyPoint( camPos, camPos ); volMatrix->Delete(); if ( camPos[3] ) { camPos[0] /= camPos[3]; camPos[1] /= camPos[3]; camPos[2] /= camPos[3]; } // These are the region limits for x (first four), y (next four) and // z (last four). The first region limit is the lower bound for // that axis, the next two are the region planes along that axis, and // the final one in the upper bound for that axis. float limit[12]; for ( i = 0; i < 3; i++ ) { limit[i*4 ] = volBounds[i*2]; limit[i*4+1] = this->CroppingRegionPlanes[i*2]; limit[i*4+2] = this->CroppingRegionPlanes[i*2+1]; limit[i*4+3] = volBounds[i*2+1]; } // For each of the 27 possible regions, find out if it is enabled, // and if so, compute the bounds and the distance from the camera // to the center of the region. int numRegions = 0; int region; for ( region = 0; region < 27; region++ ) { int regionFlag = 1<CroppingRegionFlags & regionFlag ) { // what is the coordinate in the 3x3x3 grid int loc[3]; loc[0] = region%3; loc[1] = (region/3)%3; loc[2] = (region/9)%3; // compute the bounds and center float center[3]; for ( i = 0; i < 3; i++ ) { bounds[numRegions][i*2 ] = limit[4*i+loc[i]]; bounds[numRegions][i*2+1] = limit[4*i+loc[i]+1]; center[i] = (bounds[numRegions][i*2 ] + bounds[numRegions][i*2+1])/2.0; } // compute the distance squared to the center distance2[numRegions] = (camPos[0]-center[0])*(camPos[0]-center[0]) + (camPos[1]-center[1])*(camPos[1]-center[1]) + (camPos[2]-center[2])*(camPos[2]-center[2]); // we've added one region numRegions++; } } // Do a quick bubble sort on distance for ( i = 1; i < numRegions; i++ ) { for ( j = i; j > 0 && distance2[j] > distance2[j-1]; j-- ) { float tmpBounds[6]; float tmpDistance2; for ( k = 0; k < 6; k++ ) { tmpBounds[k] = bounds[j][k]; } tmpDistance2 = distance2[j]; for ( k = 0; k < 6; k++ ) { bounds[j][k] = bounds[j-1][k]; } distance2[j] = distance2[j-1]; for ( k = 0; k < 6; k++ ) { bounds[j-1][k] = tmpBounds[k]; } distance2[j-1] = tmpDistance2; } } numIterations = numRegions; } // loop over all regions we need to render for ( int loop = 0; loop < numIterations; loop++ ) { // Compute the set of polygons for this region // according to the bounds this->ComputePolygons( ren, vol, bounds[loop] ); // Loop over the polygons for ( i = 0; i < this->NumberOfPolygons; i++ ) { if ( renWin->CheckAbortStatus() ) { return; } float *ptr = this->PolygonBuffer + 36*i; glBegin( GL_TRIANGLE_FAN ); for ( j = 0; j < 6; j++ ) { if ( ptr[0] < 0.0 ) { break; } for ( k = 0; k < 4; k++ ) { if ( stages[k] ) { vtkgl::MultiTexCoord3fv( vtkgl::TEXTURE0 + k, ptr ); } } glVertex3fv( ptr+3 ); ptr += 6; } glEnd(); } } } // This method moves the scalars from the input volume into volume1 (and // possibly volume2) which are the 3D texture maps used for rendering. // // In the case where our volume is a power of two, the copy is done // directly. If we need to resample, then trilinear interpolation is used. // // A shift/scale is applied to the input scalar value to produce an 8 bit // value for the texture volume. // // When the input data is one component, the scalar value is placed in the // second component of the two component volume1. The first component is // filled in later with the gradient magnitude. // // When the input data is two component non-independent, the first component // of the input data is placed in the first component of volume1, and the // second component of the input data is placed in the third component of // volume1. Volume1 has three components - the second is filled in later with // the gradient magnitude. // // When the input data is four component non-independent, the first three // components of the input data are placed in volume1 (which has three // components), and the fourth component is placed in the second component // of volume2. The first component of volume2 is later filled in with the // gradient magnitude. template class ScalarGradientCompute { T *dataPtr; unsigned char *tmpPtr; unsigned char *tmpPtr2; int sizeX; int sizeY; int sizeZ; int sizeXY; int sizeXm1; int sizeYm1; int sizeZm1; int fullX; int fullY; int fullZ; int fullXY; int currentChunkStart; int currentChunkEnd; int offZ; float offset; float scale; public: ScalarGradientCompute( T *_dataPtr,unsigned char *_tmpPtr,unsigned char *_tmpPtr2,int _sizeX,int _sizeY,int _sizeZ,int _fullX,int _fullY,int _fullZ,float _offset,float _scale) { dataPtr=_dataPtr; tmpPtr=_tmpPtr; tmpPtr2=_tmpPtr2; sizeX=_sizeX; sizeY=_sizeY; sizeZ=_sizeZ; fullX=_fullX; fullY=_fullY; fullZ=_fullZ; offset=_offset; scale=_scale; sizeXY=sizeX*sizeY; sizeXm1=sizeX-1; sizeYm1=sizeY-1; sizeZm1=sizeZ-1; fullXY=fullX*fullY; } inline float sample(int x,int y,int z) { return float(dataPtr[ x + y * sizeX + z * sizeXY ]); } inline void fill(int x,int y,int z) { int doff = x + y * fullX + (z-offZ) * fullXY; tmpPtr[doff*4+0]= 0; tmpPtr[doff*4+1]= 0; tmpPtr[doff*4+2]= 0; tmpPtr[doff*4+3]= 0; /* tmpPtr2[doff*3+0]= 0; tmpPtr2[doff*3+1]= 0; tmpPtr2[doff*3+2]= 0; */ } inline int clamp(int x) { if(x<0) x=0; else if(x>255) x=255; return x; } inline void write(int x,int y,int z,float grayValue,float gx,float gy,float gz) { /* gx /= aspect[0]; gy /= aspect[1]; gz /= aspect[2]; */ // Compute the gradient magnitude int iGrayValue = static_cast( (grayValue + offset) * scale + 0.5f ); gx *= scale; gy *= scale; gz *= scale; float t = sqrtf( gx*gx + gy*gy + gz*gz ); if ( t > 0.01f ) { if( t < 2.0f ) { float fac = 2.0f/t; gx *= fac; gy *= fac; gz *= fac; } else if( t > 255.0f) { float fac = 255.0f/t; gx *= fac; gy *= fac; gz *= fac; } } else { gx=gy=gz=0.0f; } int nx = static_cast(0.5f*gx+127.5f); int ny = static_cast(0.5f*gy+127.5f); int nz = static_cast(0.5f*gz+127.5f); int doff = x + y * fullX + (z-offZ) * fullXY; //tmpPtr[doff*2+0]= 0; tmpPtr[doff*4+0]= clamp(nx); tmpPtr[doff*4+1]= clamp(ny); tmpPtr[doff*4+2]= clamp(nz); tmpPtr[doff*4+3]= clamp(iGrayValue); /* if( z == fullZ/2 ) if( y == fullY/2 ) MITK_INFO << x << " " << y << " " << z << " : " << iGrayValue << " : " << iGradient; */ } inline void compute(int x,int y,int z) { float grayValue = sample(x,y,z); float gx,gy,gz; gx = sample(x+1,y,z) - sample(x-1,y,z); gy = sample(x,y+1,z) - sample(x,y-1,z); gz = sample(x,y,z+1) - sample(x,y,z-1); write( x, y, z, grayValue, gx, gy, gz ); } inline void computeClamp(int x,int y,int z) { float grayValue = sample(x,y,z); float gx,gy,gz; if(x==0) gx = 2.0f * ( sample(x+1,y,z) - grayValue ); else if(x==sizeXm1) gx = 2.0f * ( grayValue - sample(x-1,y,z) ); else gx = sample(x+1,y,z) - sample(x-1,y,z); if(y==0) gy = 2.0f * ( sample(x,y+1,z) - grayValue ); else if(y==sizeYm1) gy = 2.0f * ( grayValue - sample(x,y-1,z) ); else gy = sample(x,y+1,z) - sample(x,y-1,z); if(z==0) gz = 2.0f * ( sample(x,y,z+1) - grayValue ); else if(z==sizeZm1) gz = 2.0f * ( grayValue - sample(x,y,z-1) ); else gz = sample(x,y,z+1) - sample(x,y,z-1); write( x, y, z, grayValue, gx, gy, gz ); } inline void compute1D(int y,int z) { int x; x=0; computeClamp(x,y,z); x++; while(x=sizeZ) fill2D(z); else compute2D(z); } } }; template void vtkVolumeTextureMapper3DComputeScalars( T *dataPtr, vtkMitkVolumeTextureMapper3D *me, float offset, float scale, GLuint volume1, GLuint /*volume2*/) { T *inPtr; // unsigned char *outPtr, *outPtr2; // int i, j, k; // int idx; int inputDimensions[3]; double inputSpacing[3]; vtkImageData *input = me->GetInput(); input->GetDimensions( inputDimensions ); input->GetSpacing( inputSpacing ); int outputDimensions[3]; float outputSpacing[3]; me->GetVolumeDimensions( outputDimensions ); me->GetVolumeSpacing( outputSpacing ); // int components = input->GetNumberOfScalarComponents(); // double wx, wy, wz; // double fx, fy, fz; // int x, y, z; double sampleRate[3]; sampleRate[0] = outputSpacing[0] / static_cast(inputSpacing[0]); sampleRate[1] = outputSpacing[1] / static_cast(inputSpacing[1]); sampleRate[2] = outputSpacing[2] / static_cast(inputSpacing[2]); int fullX = outputDimensions[0]; int fullY = outputDimensions[1]; int fullZ = outputDimensions[2]; int sizeX = inputDimensions[0]; int sizeY = inputDimensions[1]; int sizeZ = inputDimensions[2]; int chunkSize = 64; if(fullZ < chunkSize) chunkSize=fullZ; int numChunks = ( fullZ + (chunkSize-1) ) / chunkSize; inPtr = dataPtr; unsigned char *tmpPtr = new unsigned char[fullX*fullY*chunkSize*4]; unsigned char *tmpPtr2 = 0;//new unsigned char[fullX*fullY*chunkSize*3]; // For each Chunk { ScalarGradientCompute sgc(dataPtr,tmpPtr,tmpPtr2,sizeX,sizeY,sizeZ,fullX,fullY,fullZ,offset,scale); int currentChunk = 0; while(currentChunk < numChunks) { // MITK_INFO << "processing chunk " << currentChunk; int currentChunkStart = currentChunk * chunkSize; int currentChunkEnd = currentChunkStart + chunkSize - 1 ; if( currentChunkEnd > (fullZ-1) ) currentChunkEnd = (fullZ-1); int currentChunkSize = currentChunkEnd - currentChunkStart + 1; sgc.fillSlices( currentChunkStart , currentChunkEnd ); glBindTexture(vtkgl::TEXTURE_3D, volume1); vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D,0,0,0,currentChunkStart,fullX,fullY,currentChunkSize,GL_RGBA,GL_UNSIGNED_BYTE,tmpPtr); /* glBindTexture(vtkgl::TEXTURE_3D, volume2); vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D,0,0,0,currentChunkStart,fullX,fullY,currentChunkSize,GL_RGB,GL_UNSIGNED_BYTE,tmpPtr2); */ currentChunk ++; } } delete tmpPtr; // delete tmpPtr2; } class RGBACompute { unsigned char *dataPtr; unsigned char *tmpPtr; unsigned char *tmpPtr2; int sizeX; int sizeY; int sizeZ; int sizeXY; int sizeXm1; int sizeYm1; int sizeZm1; int fullX; int fullY; int fullZ; int fullXY; int currentChunkStart; int currentChunkEnd; int offZ; public: RGBACompute( unsigned char *_dataPtr,unsigned char *_tmpPtr,unsigned char *_tmpPtr2,int _sizeX,int _sizeY,int _sizeZ,int _fullX,int _fullY,int _fullZ) { dataPtr=_dataPtr; tmpPtr=_tmpPtr; tmpPtr2=_tmpPtr2; sizeX=_sizeX; sizeY=_sizeY; sizeZ=_sizeZ; fullX=_fullX; fullY=_fullY; fullZ=_fullZ; sizeXY=sizeX*sizeY; sizeXm1=sizeX-1; sizeYm1=sizeY-1; sizeZm1=sizeZ-1; fullXY=fullX*fullY; } inline int sample(int x,int y,int z) { return dataPtr[ ( x + y * sizeX + z * sizeXY ) * 4 +3 ]; } inline void fill(int x,int y,int z) { int doff = x + y * fullX + (z-offZ) * fullXY; tmpPtr[doff*4+0]= 0; tmpPtr[doff*4+1]= 0; tmpPtr[doff*4+2]= 0; tmpPtr[doff*4+3]= 0; tmpPtr2[doff*3+0]= 0; tmpPtr2[doff*3+1]= 0; tmpPtr2[doff*3+2]= 0; } inline int clamp(int x) { if(x<0) x=0; else if(x>255) x=255; return x; } inline void write(int x,int y,int z,int iGrayValue,int gx,int gy,int gz) { /* gx /= aspect[0]; gy /= aspect[1]; gz /= aspect[2]; */ int nx = static_cast(0.5f*gx+127.5f); int ny = static_cast(0.5f*gy+127.5f); int nz = static_cast(0.5f*gz+127.5f); int doff = x + y * fullX + (z-offZ) * fullXY; //tmpPtr[doff*2+0]= 0; tmpPtr[doff*4+0]= clamp(nx); tmpPtr[doff*4+1]= clamp(ny); tmpPtr[doff*4+2]= clamp(nz); tmpPtr[doff*4+3]= clamp(iGrayValue); int soff = x + y * sizeX + z * sizeXY; tmpPtr2[doff*3+0]= dataPtr[soff*4+0]; tmpPtr2[doff*3+1]= dataPtr[soff*4+1]; tmpPtr2[doff*3+2]= dataPtr[soff*4+2]; /* if( z == fullZ/2 ) if( y == fullY/2 ) MITK_INFO << x << " " << y << " " << z << " : " << iGrayValue << " : " << iGradient; */ } inline void compute(int x,int y,int z) { int grayValue = sample(x,y,z); int gx,gy,gz; gx = sample(x+1,y,z) - sample(x-1,y,z); gy = sample(x,y+1,z) - sample(x,y-1,z); gz = sample(x,y,z+1) - sample(x,y,z-1); write( x, y, z, grayValue, gx, gy, gz ); } inline void computeClamp(int x,int y,int z) { int grayValue = sample(x,y,z); int gx,gy,gz; if(x==0) gx = 2 * ( sample(x+1,y,z) - grayValue ); else if(x==sizeXm1) gx = 2 * ( grayValue - sample(x-1,y,z) ); else gx = sample(x+1,y,z) - sample(x-1,y,z); if(y==0) gy = 2 * ( sample(x,y+1,z) - grayValue ); else if(y==sizeYm1) gy = 2 * ( grayValue - sample(x,y-1,z) ); else gy = sample(x,y+1,z) - sample(x,y-1,z); if(z==0) gz = 2 * ( sample(x,y,z+1) - grayValue ); else if(z==sizeZm1) gz = 2 * ( grayValue - sample(x,y,z-1) ); else gz = sample(x,y,z+1) - sample(x,y,z-1); write( x, y, z, grayValue, gx, gy, gz ); } inline void compute1D(int y,int z) { int x=0; computeClamp(x,y,z); x++; while(x=sizeZ) fill2D(z); else compute2D(z); } } }; void vtkVolumeTextureMapper3DComputeRGBA( unsigned char *dataPtr, vtkMitkVolumeTextureMapper3D *me, GLuint volume1, GLuint volume2) { unsigned char *inPtr; // unsigned char *outPtr, *outPtr2; // int i, j, k; // int idx; int inputDimensions[3]; double inputSpacing[3]; vtkImageData *input = me->GetInput(); input->GetDimensions( inputDimensions ); input->GetSpacing( inputSpacing ); int outputDimensions[3]; float outputSpacing[3]; me->GetVolumeDimensions( outputDimensions ); me->GetVolumeSpacing( outputSpacing ); int components = input->GetNumberOfScalarComponents(); MITK_INFO << "components are " << components; // double wx, wy, wz; // double fx, fy, fz; // int x, y, z; double sampleRate[3]; sampleRate[0] = outputSpacing[0] / static_cast(inputSpacing[0]); sampleRate[1] = outputSpacing[1] / static_cast(inputSpacing[1]); sampleRate[2] = outputSpacing[2] / static_cast(inputSpacing[2]); int fullX = outputDimensions[0]; int fullY = outputDimensions[1]; int fullZ = outputDimensions[2]; int sizeX = inputDimensions[0]; int sizeY = inputDimensions[1]; int sizeZ = inputDimensions[2]; int chunkSize = 64; if(fullZ < chunkSize) chunkSize=fullZ; int numChunks = ( fullZ + (chunkSize-1) ) / chunkSize; inPtr = dataPtr; unsigned char *tmpPtr = new unsigned char[fullX*fullY*chunkSize*4]; unsigned char *tmpPtr2 = new unsigned char[fullX*fullY*chunkSize*3]; // For each Chunk { RGBACompute sgc(dataPtr,tmpPtr,tmpPtr2,sizeX,sizeY,sizeZ,fullX,fullY,fullZ); int currentChunk = 0; while(currentChunk < numChunks) { // MITK_INFO << "processing chunk " << currentChunk; int currentChunkStart = currentChunk * chunkSize; int currentChunkEnd = currentChunkStart + chunkSize - 1 ; if( currentChunkEnd > (fullZ-1) ) currentChunkEnd = (fullZ-1); int currentChunkSize = currentChunkEnd - currentChunkStart + 1; sgc.fillSlices( currentChunkStart , currentChunkEnd ); glBindTexture(vtkgl::TEXTURE_3D, volume1); vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D,0,0,0,currentChunkStart,fullX,fullY,currentChunkSize,GL_RGBA,GL_UNSIGNED_BYTE,tmpPtr); glBindTexture(vtkgl::TEXTURE_3D, volume2); vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D,0,0,0,currentChunkStart,fullX,fullY,currentChunkSize,GL_RGB,GL_UNSIGNED_BYTE,tmpPtr2); currentChunk ++; } } delete tmpPtr; delete tmpPtr2; } //----------------------------------------------------------------------------- void vtkMitkOpenGLVolumeTextureMapper3D::ComputeVolumeDimensions() { // Get the image data vtkImageData *input = this->GetInput(); // How big does the Volume need to be? int dim[3]; input->GetDimensions(dim); int powerOfTwoDim[3]; if(this->SupportsNonPowerOfTwoTextures) { for ( int i = 0; i < 3; i++ ) powerOfTwoDim[i]=(dim[i]+1)&~1; // MITK_INFO << "using non-power-two even textures (" << (1.0-double(dim[0]*dim[1]*dim[2])/double(powerOfTwoDim[0]*powerOfTwoDim[1]*powerOfTwoDim[2])) * 100.0 << "% memory wasted)"; } else { for ( int i = 0; i < 3; i++ ) { powerOfTwoDim[i] = 4; while ( powerOfTwoDim[i] < dim[i] ) powerOfTwoDim[i] *= 2; } MITK_WARN << "using power-two textures (" << (1.0-double(dim[0]*dim[1]*dim[2])/double(powerOfTwoDim[0]*powerOfTwoDim[1]*powerOfTwoDim[2])) * 100.0 << "% memory wasted)"; } // Save the volume size this->VolumeDimensions[0] = powerOfTwoDim[0]; this->VolumeDimensions[1] = powerOfTwoDim[1]; this->VolumeDimensions[2] = powerOfTwoDim[2]; // What is the spacing? double spacing[3]; input->GetSpacing(spacing); // Compute the new spacing this->VolumeSpacing[0] = ( dim[0] -1.01)*spacing[0] / static_cast(this->VolumeDimensions[0]-1); this->VolumeSpacing[1] = ( dim[1] -1.01)*spacing[1] / static_cast(this->VolumeDimensions[1]-1); this->VolumeSpacing[2] = ((dim[2])-1.01)*spacing[2] / static_cast(this->VolumeDimensions[2]-1); } //----------------------------------------------------------------------------- bool vtkMitkOpenGLVolumeTextureMapper3D::UpdateVolumes(vtkVolume *vtkNotUsed(vol)) { // Get the image data vtkImageData *input = this->GetInput(); input->Update(); bool needUpdate = false; // Has the volume changed in some way? if ( this->SavedTextureInput != input || this->SavedTextureMTime.GetMTime() < input->GetMTime() ) needUpdate = true; // Do we have any volume on the gpu already? if(!this->Volume1Index) needUpdate = true; if(!needUpdate) return true; ComputeVolumeDimensions(); int components = input->GetNumberOfScalarComponents(); // Find the scalar range double scalarRange[2]; input->GetPointData()->GetScalars()->GetRange(scalarRange, components-1); // Is the difference between max and min less than 4096? If so, and if // the data is not of float or double type, use a simple offset mapping. // If the difference between max and min is 4096 or greater, or the data // is of type float or double, we must use an offset / scaling mapping. // In this case, the array size will be 4096 - we need to figure out the // offset and scale factor. float offset; float scale; int arraySizeNeeded; int scalarType = input->GetScalarType(); if ( scalarType == VTK_FLOAT || scalarType == VTK_DOUBLE || scalarRange[1] - scalarRange[0] > 255 ) { arraySizeNeeded = 256; offset = -scalarRange[0]; scale = 255.0 / (scalarRange[1] - scalarRange[0]); } else { arraySizeNeeded = static_cast(scalarRange[1] - scalarRange[0] + 1); offset = -scalarRange[0]; scale = 1.0; } this->ColorTableSize = arraySizeNeeded; this->ColorTableOffset = offset; this->ColorTableScale = scale; // Allocating volume on gpu { // Deleting old textures this->DeleteTextureIndex(&this->Volume1Index); this->DeleteTextureIndex(&this->Volume2Index); this->DeleteTextureIndex(&this->Volume3Index); this->CreateTextureIndex(&this->Volume1Index); //this->CreateTextureIndex(&this->Volume2Index); int dim[3]; this->GetVolumeDimensions(dim); vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); MITK_INFO << "allocating volume on gpu"; GLint gradientScalarTextureFormat = GL_RGBA8; if(this->UseCompressedTexture && SupportsCompressedTexture) gradientScalarTextureFormat = myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT; glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,gradientScalarTextureFormat,dim[0],dim[1],dim[2],0,GL_RGBA,GL_UNSIGNED_BYTE,0); this->Setup3DTextureParameters( true ); } // Transfer the input volume to the RGBA volume void *dataPtr = input->GetScalarPointer(); switch ( scalarType ) { vtkTemplateMacro( vtkVolumeTextureMapper3DComputeScalars( static_cast(dataPtr), this, offset, scale, this->Volume1Index, this->Volume2Index)); } this->SavedTextureInput = input; this->SavedTextureMTime.Modified(); return true; } //----------------------------------------------------------------------------- bool vtkMitkOpenGLVolumeTextureMapper3D::UpdateVolumesRGBA(vtkVolume *vtkNotUsed(vol)) { // Get the image data vtkImageData *input = this->GetInput(); input->Update(); bool needUpdate = false; // Has the volume changed in some way? if ( this->SavedTextureInput != input || this->SavedTextureMTime.GetMTime() < input->GetMTime() ) needUpdate = true; // Do we have any volume on the gpu already? if(!this->Volume1Index) needUpdate = true; if(!needUpdate) return true; MITK_INFO << "updating rgba volume"; ComputeVolumeDimensions(); // Allocating volume on gpu { // Deleting old textures this->DeleteTextureIndex(&this->Volume1Index); this->DeleteTextureIndex(&this->Volume2Index); this->DeleteTextureIndex(&this->Volume3Index); this->CreateTextureIndex(&this->Volume1Index); this->CreateTextureIndex(&this->Volume2Index); int dim[3]; this->GetVolumeDimensions(dim); MITK_INFO << "allocating volume on gpu"; GLint gradientScalarTextureFormat = GL_RGBA8; GLint colorTextureFormat = GL_RGB8; if(this->UseCompressedTexture && SupportsCompressedTexture) { gradientScalarTextureFormat = myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT; colorTextureFormat = myGL_COMPRESSED_RGB_S3TC_DXT1_EXT; } vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,gradientScalarTextureFormat,dim[0],dim[1],dim[2],0,GL_RGBA,GL_UNSIGNED_BYTE,0); this->Setup3DTextureParameters( true ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,colorTextureFormat,dim[0],dim[1],dim[2],0,GL_RGB,GL_UNSIGNED_BYTE,0); this->Setup3DTextureParameters( true ); } // Transfer the input volume to the RGBA volume unsigned char *dataPtr = (unsigned char*)input->GetScalarPointer(); vtkVolumeTextureMapper3DComputeRGBA( dataPtr, this, this->Volume1Index, this->Volume2Index); this->SavedTextureInput = input; this->SavedTextureMTime.Modified(); return true; } void vtkMitkOpenGLVolumeTextureMapper3D::Setup3DTextureParameters( bool linear ) { //GPU_INFO << "Setup3DTextureParameters"; if( linear ) { glTexParameterf( vtkgl::TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameterf( vtkgl::TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); } else { glTexParameterf( vtkgl::TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameterf( vtkgl::TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); } glTexParameterf( vtkgl::TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP ); glTexParameterf( vtkgl::TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP ); } void vtkMitkOpenGLVolumeTextureMapper3D::SetupOneIndependentTextures( vtkRenderer *vtkNotUsed(ren), vtkVolume *vol ) { // Update the volume containing the 2 byte scalar / gradient magnitude this->UpdateVolumes( vol ); // Update the dependent 2D color table mapping scalar value and // gradient magnitude to RGBA if ( this->UpdateColorLookup( vol ) || !this->ColorLookupIndex ) { this->DeleteTextureIndex( &this->ColorLookupIndex ); this->DeleteTextureIndex( &this->AlphaLookupIndex ); this->CreateTextureIndex( &this->ColorLookupIndex ); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glBindTexture(GL_TEXTURE_2D, this->ColorLookupIndex); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); //MITK_INFO << "uploading transferfunction"; GLint colorLookupTextureFormat = GL_RGBA8; if(this->UseCompressedTexture && SupportsCompressedTexture) colorLookupTextureFormat = myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT; glTexImage2D( GL_TEXTURE_2D, 0,colorLookupTextureFormat, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->ColorLookup ); } } void vtkMitkOpenGLVolumeTextureMapper3D::SetupRGBATextures( vtkRenderer *vtkNotUsed(ren), vtkVolume *vol ) { MITK_INFO << "SetupFourDependentTextures"; this->UpdateVolumesRGBA(vol); /* vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); // Update the volume containing the 3 byte scalars / gradient magnitude if ( this->UpdateVolumes( vol ) || !this->Volume1Index || !this->Volume2Index || !this->Volume3Index ) { int dim[3]; this->GetVolumeDimensions(dim); vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glBindTexture(vtkgl::TEXTURE_3D,0); this->DeleteTextureIndex(&this->Volume1Index); this->CreateTextureIndex(&this->Volume1Index); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,this->InternalRGB,dim[0],dim[1], dim[2],0,GL_RGB,GL_UNSIGNED_BYTE,this->Volume1); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glBindTexture(vtkgl::TEXTURE_3D,0); this->DeleteTextureIndex(&this->Volume2Index); this->CreateTextureIndex(&this->Volume2Index); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,this->InternalLA,dim[0],dim[1], dim[2],0,GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE, this->Volume2); vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); glBindTexture(vtkgl::TEXTURE_3D,0); this->DeleteTextureIndex(&this->Volume3Index); this->CreateTextureIndex(&this->Volume3Index); glBindTexture(vtkgl::TEXTURE_3D, this->Volume3Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,this->InternalRGB,dim[0],dim[1], dim[2],0,GL_RGB,GL_UNSIGNED_BYTE,this->Volume3); } vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); this->Setup3DTextureParameters( true ); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); this->Setup3DTextureParameters( true ); vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); glBindTexture(vtkgl::TEXTURE_3D_EXT, this->Volume3Index); this->Setup3DTextureParameters( true ); vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); glEnable( GL_TEXTURE_2D ); glDisable( vtkgl::TEXTURE_3D ); // Update the dependent 2D table mapping scalar value and // gradient magnitude to opacity if ( this->UpdateColorLookup( vol ) || !this->AlphaLookupIndex ) { this->DeleteTextureIndex(&this->ColorLookupIndex); vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); glBindTexture(GL_TEXTURE_2D,0); this->DeleteTextureIndex(&this->AlphaLookupIndex); this->CreateTextureIndex(&this->AlphaLookupIndex); glBindTexture(GL_TEXTURE_2D, this->AlphaLookupIndex); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); //MITK_INFO << "uploading transferfunction"; glTexImage2D(GL_TEXTURE_2D,0,this->InternalAlpha, 256, 256, 0, GL_ALPHA, GL_UNSIGNED_BYTE, this->AlphaLookup ); } vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); glBindTexture(GL_TEXTURE_2D, this->AlphaLookupIndex); */ } void vtkMitkOpenGLVolumeTextureMapper3D::RenderOneIndependentShadeFP( vtkRenderer *ren, vtkVolume *vol ) { //GPU_INFO << "RenderOneIndependentShadeFP"; this->SetupOneIndependentTextures( ren, vol ); glEnable( vtkgl::FRAGMENT_PROGRAM_ARB ); vtkgl::BindProgramARB( vtkgl::FRAGMENT_PROGRAM_ARB, prgOneComponentShade ); this->SetupProgramLocalsForShadingFP( ren, vol ); // Bind Textures { vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glEnable( GL_TEXTURE_2D ); glDisable( vtkgl::TEXTURE_3D ); glBindTexture(GL_TEXTURE_2D, this->ColorLookupIndex); vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); } int stages[4] = {1,1,1,0}; this->RenderPolygons( ren, vol, stages ); glDisable( vtkgl::FRAGMENT_PROGRAM_ARB ); } void vtkMitkOpenGLVolumeTextureMapper3D::RenderRGBAShadeFP( vtkRenderer *ren, vtkVolume *vol ) { this->SetupRGBATextures(ren, vol); glEnable( vtkgl::FRAGMENT_PROGRAM_ARB ); vtkgl::BindProgramARB( vtkgl::FRAGMENT_PROGRAM_ARB, prgRGBAShade ); this->SetupProgramLocalsForShadingFP( ren, vol ); // Bind Textures { vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); } int stages[4] = {1,1,1,0}; this->RenderPolygons( ren, vol, stages ); glDisable( vtkgl::FRAGMENT_PROGRAM_ARB ); } void vtkMitkOpenGLVolumeTextureMapper3D::GetLightInformation( vtkRenderer *ren, vtkVolume *vol, GLfloat lightDirection[2][4], GLfloat lightDiffuseColor[2][4], GLfloat lightSpecularColor[2][4], GLfloat halfwayVector[2][4], GLfloat ambientColor[4] ) { //GPU_INFO << "GetLightInformation"; float ambient = vol->GetProperty()->GetAmbient(); float diffuse = vol->GetProperty()->GetDiffuse(); float specular = vol->GetProperty()->GetSpecular(); vtkTransform *volumeTransform = vtkTransform::New(); volumeTransform->SetMatrix( vol->GetMatrix() ); volumeTransform->Inverse(); vtkLightCollection *lights = ren->GetLights(); lights->InitTraversal(); vtkLight *light[2]; light[0] = lights->GetNextItem(); light[1] = lights->GetNextItem(); int lightIndex = 0; double cameraPosition[3]; double cameraFocalPoint[3]; ren->GetActiveCamera()->GetPosition( cameraPosition ); ren->GetActiveCamera()->GetFocalPoint( cameraFocalPoint ); double viewDirection[3]; volumeTransform->TransformPoint( cameraPosition, cameraPosition ); volumeTransform->TransformPoint( cameraFocalPoint, cameraFocalPoint ); viewDirection[0] = cameraFocalPoint[0] - cameraPosition[0]; viewDirection[1] = cameraFocalPoint[1] - cameraPosition[1]; viewDirection[2] = cameraFocalPoint[2] - cameraPosition[2]; vtkMath::Normalize( viewDirection ); ambientColor[0] = 0.0; ambientColor[1] = 0.0; ambientColor[2] = 0.0; ambientColor[3] = 0.0; for ( lightIndex = 0; lightIndex < 2; lightIndex++ ) { float dir[3] = {0,0,0}; float half[3] = {0,0,0}; if ( light[lightIndex] == NULL || light[lightIndex]->GetSwitch() == 0 ) { lightDiffuseColor[lightIndex][0] = 0.0; lightDiffuseColor[lightIndex][1] = 0.0; lightDiffuseColor[lightIndex][2] = 0.0; lightDiffuseColor[lightIndex][3] = 0.0; lightSpecularColor[lightIndex][0] = 0.0; lightSpecularColor[lightIndex][1] = 0.0; lightSpecularColor[lightIndex][2] = 0.0; lightSpecularColor[lightIndex][3] = 0.0; } else { float lightIntensity = light[lightIndex]->GetIntensity(); double lightColor[3]; light[lightIndex]->GetDiffuseColor( lightColor ); double lightPosition[3]; double lightFocalPoint[3]; light[lightIndex]->GetTransformedPosition( lightPosition ); light[lightIndex]->GetTransformedFocalPoint( lightFocalPoint ); volumeTransform->TransformPoint( lightPosition, lightPosition ); volumeTransform->TransformPoint( lightFocalPoint, lightFocalPoint ); dir[0] = lightPosition[0] - lightFocalPoint[0]; dir[1] = lightPosition[1] - lightFocalPoint[1]; dir[2] = lightPosition[2] - lightFocalPoint[2]; vtkMath::Normalize( dir ); lightDiffuseColor[lightIndex][0] = lightColor[0]*diffuse*lightIntensity; lightDiffuseColor[lightIndex][1] = lightColor[1]*diffuse*lightIntensity; lightDiffuseColor[lightIndex][2] = lightColor[2]*diffuse*lightIntensity; lightDiffuseColor[lightIndex][3] = 1.0; lightSpecularColor[lightIndex][0]= lightColor[0]*specular*lightIntensity; lightSpecularColor[lightIndex][1]= lightColor[1]*specular*lightIntensity; lightSpecularColor[lightIndex][2]= lightColor[2]*specular*lightIntensity; lightSpecularColor[lightIndex][3] = 0.0; half[0] = dir[0] - viewDirection[0]; half[1] = dir[1] - viewDirection[1]; half[2] = dir[2] - viewDirection[2]; vtkMath::Normalize( half ); ambientColor[0] += ambient*lightColor[0]; ambientColor[1] += ambient*lightColor[1]; ambientColor[2] += ambient*lightColor[2]; } lightDirection[lightIndex][0] = (dir[0]+1.0)/2.0; lightDirection[lightIndex][1] = (dir[1]+1.0)/2.0; lightDirection[lightIndex][2] = (dir[2]+1.0)/2.0; lightDirection[lightIndex][3] = 0.0; halfwayVector[lightIndex][0] = (half[0]+1.0)/2.0; halfwayVector[lightIndex][1] = (half[1]+1.0)/2.0; halfwayVector[lightIndex][2] = (half[2]+1.0)/2.0; halfwayVector[lightIndex][3] = 0.0; } volumeTransform->Delete(); } void vtkMitkOpenGLVolumeTextureMapper3D::SetupProgramLocalsForShadingFP( vtkRenderer *ren, vtkVolume *vol ) { //GPU_INFO << "SetupProgramLocalsForShadingFP"; GLfloat lightDirection[2][4]; GLfloat lightDiffuseColor[2][4]; GLfloat lightSpecularColor[2][4]; GLfloat halfwayVector[2][4]; GLfloat ambientColor[4]; float ambient = vol->GetProperty()->GetAmbient(); float diffuse = vol->GetProperty()->GetDiffuse(); float specular = vol->GetProperty()->GetSpecular(); float specularPower = vol->GetProperty()->GetSpecularPower(); vtkTransform *volumeTransform = vtkTransform::New(); volumeTransform->SetMatrix( vol->GetMatrix() ); volumeTransform->Inverse(); vtkLightCollection *lights = ren->GetLights(); lights->InitTraversal(); vtkLight *light[2]; light[0] = lights->GetNextItem(); light[1] = lights->GetNextItem(); int lightIndex = 0; double cameraPosition[3]; double cameraFocalPoint[3]; ren->GetActiveCamera()->GetPosition( cameraPosition ); ren->GetActiveCamera()->GetFocalPoint( cameraFocalPoint ); volumeTransform->TransformPoint( cameraPosition, cameraPosition ); volumeTransform->TransformPoint( cameraFocalPoint, cameraFocalPoint ); double viewDirection[4]; viewDirection[0] = cameraFocalPoint[0] - cameraPosition[0]; viewDirection[1] = cameraFocalPoint[1] - cameraPosition[1]; viewDirection[2] = cameraFocalPoint[2] - cameraPosition[2]; viewDirection[3] = 0.0; vtkMath::Normalize( viewDirection ); ambientColor[0] = 0.0; ambientColor[1] = 0.0; ambientColor[2] = 0.0; ambientColor[3] = 0.0; for ( lightIndex = 0; lightIndex < 2; lightIndex++ ) { float dir[3] = {0,0,0}; float half[3] = {0,0,0}; if ( light[lightIndex] == NULL || light[lightIndex]->GetSwitch() == 0 ) { lightDiffuseColor[lightIndex][0] = 0.0; lightDiffuseColor[lightIndex][1] = 0.0; lightDiffuseColor[lightIndex][2] = 0.0; lightDiffuseColor[lightIndex][3] = 0.0; lightSpecularColor[lightIndex][0] = 0.0; lightSpecularColor[lightIndex][1] = 0.0; lightSpecularColor[lightIndex][2] = 0.0; lightSpecularColor[lightIndex][3] = 0.0; } else { float lightIntensity = light[lightIndex]->GetIntensity(); double lightColor[3]; light[lightIndex]->GetDiffuseColor( lightColor ); double lightPosition[3]; double lightFocalPoint[3]; light[lightIndex]->GetTransformedPosition( lightPosition ); light[lightIndex]->GetTransformedFocalPoint( lightFocalPoint ); volumeTransform->TransformPoint( lightPosition, lightPosition ); volumeTransform->TransformPoint( lightFocalPoint, lightFocalPoint ); dir[0] = lightPosition[0] - lightFocalPoint[0]; dir[1] = lightPosition[1] - lightFocalPoint[1]; dir[2] = lightPosition[2] - lightFocalPoint[2]; vtkMath::Normalize( dir ); lightDiffuseColor[lightIndex][0] = lightColor[0]*diffuse*lightIntensity; lightDiffuseColor[lightIndex][1] = lightColor[1]*diffuse*lightIntensity; lightDiffuseColor[lightIndex][2] = lightColor[2]*diffuse*lightIntensity; lightDiffuseColor[lightIndex][3] = 0.0; lightSpecularColor[lightIndex][0]= lightColor[0]*specular*lightIntensity; lightSpecularColor[lightIndex][1]= lightColor[1]*specular*lightIntensity; lightSpecularColor[lightIndex][2]= lightColor[2]*specular*lightIntensity; lightSpecularColor[lightIndex][3] = 0.0; half[0] = dir[0] - viewDirection[0]; half[1] = dir[1] - viewDirection[1]; half[2] = dir[2] - viewDirection[2]; vtkMath::Normalize( half ); ambientColor[0] += ambient*lightColor[0]; ambientColor[1] += ambient*lightColor[1]; ambientColor[2] += ambient*lightColor[2]; } lightDirection[lightIndex][0] = dir[0]; lightDirection[lightIndex][1] = dir[1]; lightDirection[lightIndex][2] = dir[2]; lightDirection[lightIndex][3] = 0.0; halfwayVector[lightIndex][0] = half[0]; halfwayVector[lightIndex][1] = half[1]; halfwayVector[lightIndex][2] = half[2]; halfwayVector[lightIndex][3] = 0.0; } volumeTransform->Delete(); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 0, lightDirection[0][0], lightDirection[0][1], lightDirection[0][2], lightDirection[0][3] ); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 1, halfwayVector[0][0], halfwayVector[0][1], halfwayVector[0][2], halfwayVector[0][3] ); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 2, ambient, diffuse, specular, specularPower ); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 3, lightDiffuseColor[0][0], lightDiffuseColor[0][1], lightDiffuseColor[0][2], lightDiffuseColor[0][3] ); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 4, lightSpecularColor[0][0], lightSpecularColor[0][1], lightSpecularColor[0][2], lightSpecularColor[0][3] ); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 5, viewDirection[0], viewDirection[1], viewDirection[2], viewDirection[3] ); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 6, 2.0, -1.0, 0.0, 0.0 ); } int vtkMitkOpenGLVolumeTextureMapper3D::IsRenderSupported( vtkRenderer *renderer, vtkVolumeProperty *property ) { //GPU_INFO << "IsRenderSupported"; if ( !this->Initialized ) { //this->Initialize(); this->Initialize(renderer); } if ( !this->RenderPossible ) { return 0; } if ( !this->GetInput() ) { return 0; } if ( this->GetInput()->GetNumberOfScalarComponents() > 1 && property->GetIndependentComponents() ) { return 0; } return 1; } void vtkMitkOpenGLVolumeTextureMapper3D::Initialize(vtkRenderer *renderer) { //GPU_INFO << "Initialize"; this->Initialized = 1; // vtkOpenGLExtensionManager * extensions = vtkOpenGLExtensionManager::New(); //extensions->SetRenderWindow(NULL); // set render window to the current one. vtkOpenGLExtensionManager *extensions=static_cast(renderer->GetRenderWindow())->GetExtensionManager(); int supports_texture3D=extensions->ExtensionSupported( "GL_VERSION_1_2" ); if(supports_texture3D) { extensions->LoadExtension("GL_VERSION_1_2"); } else { supports_texture3D=extensions->ExtensionSupported( "GL_EXT_texture3D" ); if(supports_texture3D) { extensions->LoadCorePromotedExtension("GL_EXT_texture3D"); } } int supports_multitexture=extensions->ExtensionSupported( "GL_VERSION_1_3" ); if(supports_multitexture) { extensions->LoadExtension("GL_VERSION_1_3"); } else { supports_multitexture= extensions->ExtensionSupported("GL_ARB_multitexture"); if(supports_multitexture) { extensions->LoadCorePromotedExtension("GL_ARB_multitexture"); } } this->SupportsCompressedTexture=extensions->ExtensionSupported("GL_VERSION_1_3")==1; if(!this->SupportsCompressedTexture) { this->SupportsCompressedTexture= extensions->ExtensionSupported("GL_ARB_texture_compression")==1; if(this->SupportsCompressedTexture) { extensions->LoadCorePromotedExtension("GL_ARB_texture_compression"); } } //GPU_INFO(this->SupportsCompressedTexture) << "supporting compressed textures"; this->SupportsNonPowerOfTwoTextures= extensions->ExtensionSupported("GL_VERSION_2_0") || extensions->ExtensionSupported("GL_ARB_texture_non_power_of_two"); //GPU_INFO << "np2: " << (this->SupportsNonPowerOfTwoTextures?1:0); int supports_GL_ARB_fragment_program = extensions->ExtensionSupported( "GL_ARB_fragment_program" ); if(supports_GL_ARB_fragment_program) { extensions->LoadExtension( "GL_ARB_fragment_program" ); } int supports_GL_ARB_vertex_program = extensions->ExtensionSupported( "GL_ARB_vertex_program" ); if(supports_GL_ARB_vertex_program) { extensions->LoadExtension( "GL_ARB_vertex_program" ); } RenderPossible = 0; if ( supports_texture3D && supports_multitexture && supports_GL_ARB_fragment_program && supports_GL_ARB_vertex_program && vtkgl::TexImage3D && vtkgl::ActiveTexture && vtkgl::MultiTexCoord3fv && vtkgl::GenProgramsARB && vtkgl::DeleteProgramsARB && vtkgl::BindProgramARB && vtkgl::ProgramStringARB && vtkgl::ProgramLocalParameter4fARB ) { RenderPossible = 1; } else { std::string errString = "no gpu-acceleration possible cause following extensions/methods are missing or unsupported:"; if(!supports_texture3D) errString += " EXT_TEXTURE3D"; if(!supports_multitexture) errString += " EXT_MULTITEXTURE"; if(!supports_GL_ARB_fragment_program) errString += " ARB_FRAGMENT_PROGRAM"; if(!supports_GL_ARB_vertex_program) errString += " ARB_VERTEX_PROGRAM"; if(!vtkgl::TexImage3D) errString += " glTexImage3D"; if(!vtkgl::ActiveTexture) errString += " glActiveTexture"; if(!vtkgl::MultiTexCoord3fv) errString += " glMultiTexCoord3fv"; if(!vtkgl::GenProgramsARB) errString += " glGenProgramsARB"; if(!vtkgl::DeleteProgramsARB) errString += " glDeleteProgramsARB"; if(!vtkgl::BindProgramARB) errString += " glBindProgramARB"; if(!vtkgl::ProgramStringARB) errString += " glProgramStringARB"; if(!vtkgl::ProgramLocalParameter4fARB) errString += " glProgramLocalParameter4fARB"; GPU_WARN << errString; }; if(RenderPossible) { vtkgl::GenProgramsARB( 1, &prgOneComponentShade ); vtkgl::BindProgramARB( vtkgl::FRAGMENT_PROGRAM_ARB, prgOneComponentShade ); vtkgl::ProgramStringARB( vtkgl::FRAGMENT_PROGRAM_ARB, vtkgl::PROGRAM_FORMAT_ASCII_ARB, static_cast(strlen(vtkMitkVolumeTextureMapper3D_OneComponentShadeFP)), vtkMitkVolumeTextureMapper3D_OneComponentShadeFP ); vtkgl::GenProgramsARB( 1, &prgRGBAShade ); vtkgl::BindProgramARB( vtkgl::FRAGMENT_PROGRAM_ARB, prgRGBAShade ); vtkgl::ProgramStringARB( vtkgl::FRAGMENT_PROGRAM_ARB, vtkgl::PROGRAM_FORMAT_ASCII_ARB, static_cast(strlen(vtkMitkVolumeTextureMapper3D_FourDependentShadeFP)), vtkMitkVolumeTextureMapper3D_FourDependentShadeFP ); } } // ---------------------------------------------------------------------------- // Print the vtkMitkOpenGLVolumeTextureMapper3D void vtkMitkOpenGLVolumeTextureMapper3D::PrintSelf(ostream& os, vtkIndent indent) { // vtkOpenGLExtensionManager * extensions = vtkOpenGLExtensionManager::New(); // extensions->SetRenderWindow(NULL); // set render window to current render window os << indent << "Initialized " << this->Initialized << endl; /* if ( this->Initialized ) { os << indent << "Supports GL_VERSION_1_2:" << extensions->ExtensionSupported( "GL_VERSION_1_2" ) << endl; os << indent << "Supports GL_EXT_texture3D:" << extensions->ExtensionSupported( "GL_EXT_texture3D" ) << endl; os << indent << "Supports GL_VERSION_1_3:" << extensions->ExtensionSupported( "GL_VERSION_1_3" ) << endl; os << indent << "Supports GL_ARB_multitexture: " << extensions->ExtensionSupported( "GL_ARB_multitexture" ) << endl; os << indent << "Supports GL_NV_texture_shader2: " << extensions->ExtensionSupported( "GL_NV_texture_shader2" ) << endl; os << indent << "Supports GL_NV_register_combiners2: " << extensions->ExtensionSupported( "GL_NV_register_combiners2" ) << endl; os << indent << "Supports GL_ATI_fragment_shader: " << extensions->ExtensionSupported( "GL_ATI_fragment_shader" ) << endl; os << indent << "Supports GL_ARB_fragment_program: " << extensions->ExtensionSupported( "GL_ARB_fragment_program" ) << endl; os << indent << "Supports GL_ARB_texture_compression: " << extensions->ExtensionSupported( "GL_ARB_texture_compression" ) << endl; os << indent << "Supports GL_VERSION_2_0:" << extensions->ExtensionSupported( "GL_VERSION_2_0" ) << endl; os << indent << "Supports GL_ARB_texture_non_power_of_two:" << extensions->ExtensionSupported( "GL_ARB_texture_non_power_of_two" ) << endl; } extensions->Delete(); */ if(this->RenderWindow!=0) { vtkOpenGLExtensionManager *extensions= static_cast(this->RenderWindow)->GetExtensionManager(); if ( this->Initialized ) { os << indent << "Supports GL_VERSION_1_2:" << extensions->ExtensionSupported( "GL_VERSION_1_2" ) << endl; os << indent << "Supports GL_EXT_texture3D:" << extensions->ExtensionSupported( "GL_EXT_texture3D" ) << endl; os << indent << "Supports GL_VERSION_1_3:" << extensions->ExtensionSupported( "GL_VERSION_1_3" ) << endl; os << indent << "Supports GL_ARB_multitexture: " << extensions->ExtensionSupported( "GL_ARB_multitexture" ) << endl; os << indent << "Supports GL_NV_texture_shader2: " << extensions->ExtensionSupported( "GL_NV_texture_shader2" ) << endl; os << indent << "Supports GL_NV_register_combiners2: " << extensions->ExtensionSupported( "GL_NV_register_combiners2" ) << endl; os << indent << "Supports GL_ATI_fragment_shader: " << extensions->ExtensionSupported( "GL_ATI_fragment_shader" ) << endl; os << indent << "Supports GL_ARB_fragment_program: " << extensions->ExtensionSupported( "GL_ARB_fragment_program" ) << endl; os << indent << "Supports GL_ARB_texture_compression: " << extensions->ExtensionSupported( "GL_ARB_texture_compression" ) << endl; os << indent << "Supports GL_VERSION_2_0:" << extensions->ExtensionSupported( "GL_VERSION_2_0" ) << endl; os << indent << "Supports GL_ARB_texture_non_power_of_two:" << extensions->ExtensionSupported( "GL_ARB_texture_non_power_of_two" ) << endl; } } this->Superclass::PrintSelf(os,indent); } diff --git a/Modules/MitkExt/Testing/files.cmake b/Modules/MitkExt/Testing/files.cmake index 529128a720..604b7b6f67 100644 --- a/Modules/MitkExt/Testing/files.cmake +++ b/Modules/MitkExt/Testing/files.cmake @@ -1,41 +1,37 @@ set(MODULE_TESTS mitkAutoCropImageFilterTest.cpp mitkBoundingObjectCutterTest.cpp - mitkContourMapper2DTest.cpp - mitkContourTest.cpp mitkCoreExtObjectFactoryTest mitkDataNodeExtTest.cpp mitkExternalToolsTest.cpp mitkMeshTest.cpp mitkMultiStepperTest.cpp mitkOrganTypePropertyTest.cpp mitkPipelineSmartPointerCorrectnessTest.cpp mitkPlaneFitTest.cpp mitkPointLocatorTest.cpp # mitkSegmentationInterpolationTest.cpp # mitkTestTemplate.cpp - mitkToolManagerTest.cpp mitkUnstructuredGridTest.cpp mitkSimpleHistogramTest.cpp + mitkToolManagerTest.cpp ) set(MODULE_IMAGE_TESTS mitkUnstructuredGridVtkWriterTest.cpp mitkCompressedImageContainerTest.cpp mitkCylindricToCartesianFilterTest.cpp #mitkExtractImageFilterTest.cpp - mitkManualSegmentationToSurfaceFilterTest.cpp - mitkOverwriteSliceImageFilterTest.cpp mitkSurfaceToImageFilterTest.cpp ) set(MODULE_CUSTOM_TESTS mitkLabeledImageToSurfaceFilterTest.cpp ) set(MODULE_TESTIMAGES US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png binary.stl ball.stl ) diff --git a/Modules/MitkExt/Testing/mitkDataNodeExtTest.cpp b/Modules/MitkExt/Testing/mitkDataNodeExtTest.cpp index 4faeb11b65..72c6289ee3 100644 --- a/Modules/MitkExt/Testing/mitkDataNodeExtTest.cpp +++ b/Modules/MitkExt/Testing/mitkDataNodeExtTest.cpp @@ -1,257 +1,214 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2008-02-25 17:27:17 +0100 (Mo, 25 Feb 2008) $ Version: $Revision: 7837 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkDataNode.h" #include #include "mitkVtkPropRenderer.h" #include "mitkTestingMacros.h" #include "mitkGlobalInteraction.h" #include //Basedata Test -#include -#include #include #include #include #include #include //Mapper Test #include #include #include -#include -#include #include #include #include #include -#include -#include #include #include #include #include //Interactors #include -#include -#include #include #include #include #include //Propertylist Test #include #include #include #include #include #include #include #include #include #include #include /** * Extended test for mitk::DataNode. A number of tests from the core test * mitkDataNodeTest are assumed to pass! */ class mitkDataNodeExtTestClass { public: static void TestDataSetting(mitk::DataNode::Pointer dataNode) { mitk::BaseData::Pointer baseData; //NULL pointer Test dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a NULL pointer was set correctly" ) - baseData = mitk::Contour::New(); - dataNode->SetData(baseData); - MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a Contour object was set correctly" ) - - baseData = mitk::ContourSet::New(); - dataNode->SetData(baseData); - MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a ContourSet object was set correctly" ) - baseData = mitk::ItkBaseDataAdapter::New(); dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a ItkBaseDataAdapter object was set correctly" ) baseData = mitk::Mesh::New(); dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a Mesh object was set correctly" ) baseData = mitk::SeedsImage::New(); dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a SeedsImage object was set correctly" ) baseData = mitk::BoundingObject::New(); dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a BoundingObject object was set correctly" ) baseData = mitk::UnstructuredGrid::New(); dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a UnstructuredGrid object was set correctly" ) } static void TestMapperSetting(mitk::DataNode::Pointer dataNode) { //tests the SetMapper() method //in dataNode is a mapper vector which can be accessed by index //in this test method we use only slot 0 (filled with null) and slot 1 //so we also test the destructor of the mapper classes mitk::Mapper::Pointer mapper; dataNode->SetMapper(0,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(0), "Testing if a NULL pointer was set correctly" ) - mapper = mitk::ContourMapper2D::New(); - dataNode->SetMapper(1,mapper); - MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourMapper2D was set correctly" ) - MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - - mapper = mitk::ContourSetMapper2D::New(); - dataNode->SetMapper(1,mapper); - MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourSetMapper2D was set correctly" ) - MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - mapper = mitk::MeshMapper2D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a MeshMapper2D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) mapper = mitk::UnstructuredGridMapper2D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a UnstructuredGridMapper2D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) mapper = mitk::LineMapper2D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a LineMapper2D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) mapper = mitk::SplineMapper2D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a SplineMapper2D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - //3D Mappers - mapper = mitk::ContourSetVtkMapper3D::New(); - dataNode->SetMapper(1,mapper); - MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourSetVtkMapper3D was set correctly" ) - MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - - mapper = mitk::ContourVtkMapper3D::New(); - dataNode->SetMapper(1,mapper); - MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourVtkMapper3D was set correctly" ) - MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - mapper = mitk::MeshVtkMapper3D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a MeshVtkMapper3D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) mapper = mitk::UnstructuredGridVtkMapper3D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a UnstructuredGridVtkMapper3D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) //linker error //mapper = mitk::LineVtkMapper3D::New(); //dataNode->SetMapper(1,mapper); //MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a LineVtkMapper3D was set correctly" ) //MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) mapper = mitk::SplineVtkMapper3D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a SplineVtkMapper3D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) } static void TestInteractorSetting(mitk::DataNode::Pointer dataNode) { //this method tests the SetInteractor() and GetInteractor methods //the Interactor base class calls the DataNode->SetInteractor method mitk::Interactor::Pointer interactor; MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a NULL pointer was set correctly (Interactor)" ) interactor = mitk::ConnectPointsInteractor::New("AffineInteractions click to select", dataNode); MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a ConnectPointsInteractor was set correctly" ) - interactor = mitk::ContourInteractor::New("AffineInteractions click to select", dataNode); - MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a ContourInteractor was set correctly" ) - - interactor = mitk::ExtrudedContourInteractor::New("AffineInteractions click to select", dataNode); - MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a ExtrudedContourInteractor was set correctly" ) - interactor = mitk::PointInteractor::New("AffineInteractions click to select", dataNode); MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a PointInteractor was set correctly" ) interactor = mitk::PointSelectorInteractor::New("AffineInteractions click to select", dataNode); MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a PointSelectorInteractor was set correctly" ) interactor = mitk::SeedsInteractor::New("AffineInteractions click to select", dataNode); MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a SeedsInteractor was set correctly" ) interactor = mitk::DisplayPointSetInteractor::New("AffineInteractions click to select", dataNode); MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a DisplayPointSetInteractor was set correctly" ) } }; int mitkDataNodeExtTest(int /* argc */, char* /*argv*/[]) { // always start with this! MITK_TEST_BEGIN("DataNode") // Global interaction must(!) be initialized mitk::GlobalInteraction::GetInstance()->Initialize("global"); // let's create an object of our class mitk::DataNode::Pointer myDataNode = mitk::DataNode::New(); // first test: did this work? // using MITK_TEST_CONDITION_REQUIRED makes the test stop after failure, since // it makes no sense to continue without an object. MITK_TEST_CONDITION_REQUIRED(myDataNode.IsNotNull(),"Testing instantiation") //test setData() Method mitkDataNodeExtTestClass::TestDataSetting(myDataNode); mitkDataNodeExtTestClass::TestMapperSetting(myDataNode); //note, that no data is set to the dataNode mitkDataNodeExtTestClass::TestInteractorSetting(myDataNode); // write your own tests here and use the macros from mitkTestingMacros.h !!! // do not write to std::cout and do not return from this function yourself! // always end with this! MITK_TEST_END() } diff --git a/Modules/MitkExt/Testing/mitkToolManagerTest.cpp b/Modules/MitkExt/Testing/mitkToolManagerTest.cpp index 21e6cf46db..f8f749f27d 100644 --- a/Modules/MitkExt/Testing/mitkToolManagerTest.cpp +++ b/Modules/MitkExt/Testing/mitkToolManagerTest.cpp @@ -1,94 +1,93 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkTestingMacros.h" #include "mitkToolManager.h" #include "mitkStandaloneDataStorage.h" #include "mitkCoreObjectFactory.h" -#include "mitkAddContourTool.h" #include "mitkGlobalInteraction.h" class mitkToolManagerTestClass { public: static void TestToolManagerWithOutTools(mitk::ToolManager::Pointer toolManager) { MITK_TEST_CONDITION( toolManager->GetTools().size() == 0, "Get empty tool list" ) MITK_TEST_CONDITION( toolManager->GetToolById(0) == NULL, "Get empty tool by id" ) } static void TestToolManagerWithTools(mitk::ToolManager::Pointer toolManager) { MITK_TEST_CONDITION( toolManager->GetTools().size() > 0, "Get tool list with size 1" ) MITK_TEST_CONDITION( toolManager->GetToolById(0) != NULL, "Test GetToolById() method" ) MITK_TEST_CONDITION( toolManager->ActivateTool(0) == true, "Activate tool" ) MITK_TEST_CONDITION( toolManager->GetActiveToolID() == 0, "Check for right tool id" ) mitk::Tool* tool = toolManager->GetActiveTool(); MITK_TEST_CONDITION( tool != NULL, "Check for right tool" ) } static void TestSetterMethods(mitk::ToolManager::Pointer toolManager) { MITK_TEST_CONDITION( toolManager->GetReferenceData().size() == 0, "Get reference data size (0)" ) mitk::DataNode::Pointer nodeEmpty = mitk::DataNode::New(); toolManager->SetReferenceData(nodeEmpty); MITK_TEST_CONDITION( toolManager->GetReferenceData().size() == 1, "Get reference data size (1)" ) MITK_TEST_CONDITION( toolManager->GetReferenceData(0) == nodeEmpty, "Check if it is the right reference data" ) MITK_TEST_CONDITION( toolManager->GetReferenceData()[0] == nodeEmpty, "Check if it is the right reference data vector" ) mitk::DataNode::Pointer nodeEmpty2 = mitk::DataNode::New(); toolManager->SetWorkingData(nodeEmpty2); MITK_TEST_CONDITION( toolManager->GetWorkingData().size() == 1, "Get working data size (1)" ) MITK_TEST_CONDITION( toolManager->GetWorkingData(0) == nodeEmpty2, "Check if it is the right working data" ) MITK_TEST_CONDITION( toolManager->GetWorkingData()[0] == nodeEmpty2, "Check if it is the right working data vector" ) } }; int mitkToolManagerTest(int /* argc */, char* /*argv*/[]) { // always start with this! MITK_TEST_BEGIN("ToolManager") // Global interaction must(!) be initialized if used mitk::GlobalInteraction::GetInstance()->Initialize("global"); // instantiation mitk::StandaloneDataStorage::Pointer dataStorage = mitk::StandaloneDataStorage::New(); mitk::ToolManager::Pointer toolManager = mitk::ToolManager::New(dataStorage.GetPointer()); // first test: did this work? // using MITK_TEST_CONDITION_REQUIRED makes the test stop after failure, since // it makes no sense to continue without an object. MITK_TEST_CONDITION_REQUIRED(toolManager.IsNotNull(),"Testing instantiation") // write your own tests here and use the macros from mitkTestingMacros.h !!! // do not write to std::cout and do not return from this function yourself! //mitkToolManagerTestClass::TestToolManagerWithOutTools(toolManager); //now we add one tool toolManager = mitk::ToolManager::New(dataStorage.GetPointer()); //start test with tool mitkToolManagerTestClass::TestToolManagerWithTools(toolManager); //now the setter methods mitkToolManagerTestClass::TestSetterMethods(toolManager); // always end with this! MITK_TEST_END() } diff --git a/Modules/MitkExt/files.cmake b/Modules/MitkExt/files.cmake index 562eec0b5f..36b48e6c7a 100644 --- a/Modules/MitkExt/files.cmake +++ b/Modules/MitkExt/files.cmake @@ -1,200 +1,152 @@ set(CPP_FILES - Algorithms/mitkImageToContourFilter.cpp - Algorithms/mitkReduceContourSetFilter.cpp - Algorithms/mitkComputeContourSetNormalsFilter.cpp - Algorithms/mitkCreateDistanceImageFromSurfaceFilter.cpp Algorithms/mitkMaskAndCutRoiImageFilter.cpp Algorithms/mitkBoundingObjectToSegmentationFilter.cpp Algorithms/vtkPointSetSlicer.cxx Algorithms/mitkCoreExtObjectFactory.cpp Algorithms/mitkAngleCorrectByPointFilter.cpp Algorithms/mitkAutoCropImageFilter.cpp Algorithms/mitkBoundingObjectCutter.cpp - Algorithms/mitkCalculateSegmentationVolume.cpp - Algorithms/mitkContourSetToPointSetFilter.cpp - Algorithms/mitkContourUtils.cpp - Algorithms/mitkCorrectorAlgorithm.cpp Algorithms/mitkCylindricToCartesianFilter.cpp - Algorithms/mitkDiffImageApplier.cpp Algorithms/mitkDopplerToStrainRateFilter.cpp Algorithms/mitkGeometryClipImageFilter.cpp Algorithms/mitkGeometryDataSource.cpp Algorithms/mitkHeightFieldSurfaceClipImageFilter.cpp Algorithms/mitkImageToLookupTableFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkInterpolateLinesFilter.cpp Algorithms/mitkLabeledImageToSurfaceFilter.cpp Algorithms/mitkLabeledImageVolumeCalculator.cpp Algorithms/mitkLookupTableSource.cpp - Algorithms/mitkManualSegmentationToSurfaceFilter.cpp Algorithms/mitkMaskImageFilter.cpp Algorithms/mitkMeshSource.cpp Algorithms/mitkNonBlockingAlgorithm.cpp - Algorithms/mitkOverwriteSliceImageFilter.cpp - Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp Algorithms/mitkPadImageFilter.cpp Algorithms/mitkPlaneCutFilter.cpp Algorithms/mitkPlaneFit.cpp Algorithms/mitkPlanesPerpendicularToLinesFilter.cpp Algorithms/mitkPointLocator.cpp Algorithms/mitkPointSetToCurvedGeometryFilter.cpp Algorithms/mitkPointSetToGeometryDataFilter.cpp Algorithms/mitkPointSetIndexToWorldTransformFilter.cpp Algorithms/mitkSurfaceIndexToWorldTransformFilter.cpp Algorithms/mitkPolygonToRingFilter.cpp Algorithms/mitkProbeFilter.cpp - Algorithms/mitkSegmentationSink.cpp - Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp - Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp - Algorithms/mitkShowSegmentationAsSurface.cpp Algorithms/mitkSimpleHistogram.cpp Algorithms/mitkSimpleUnstructuredGridHistogram.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkUnstructuredGridHistogram.cpp Algorithms/mitkUnstructuredGridSource.cpp Algorithms/mitkVolumeVisualizationImagePreprocessor.cpp Controllers/mitkMovieGenerator.cpp Controllers/mitkMultiStepper.cpp - Controllers/mitkSegmentationInterpolationController.cpp - Controllers/mitkSurfaceInterpolationController.cpp Controllers/mitkToolManager.cpp DataManagement/mitkAffineTransformationOperation.cpp DataManagement/mitkApplyDiffImageOperation.cpp DataManagement/mitkBoundingObject.cpp DataManagement/mitkBoundingObjectGroup.cpp DataManagement/mitkCellOperation.cpp DataManagement/mitkColorConversions.cpp DataManagement/mitkColorSequence.cpp DataManagement/mitkColorSequenceCycleH.cpp DataManagement/mitkColorSequenceHalfTones.cpp DataManagement/mitkColorSequenceRainbow.cpp DataManagement/mitkCompressedImageContainer.cpp DataManagement/mitkCone.cpp - DataManagement/mitkContour.cpp - DataManagement/mitkContourSet.cpp DataManagement/mitkCuboid.cpp DataManagement/mitkCylinder.cpp DataManagement/mitkDataStorageSelection.cpp DataManagement/mitkDelegateManager.cpp DataManagement/mitkDrawOperation.cpp DataManagement/mitkEllipsoid.cpp DataManagement/mitkExternAbstractTransformGeometry.cpp - DataManagement/mitkExtrudedContour.cpp DataManagement/mitkFrameOfReferenceUIDManager.cpp DataManagement/mitkGridRepresentationProperty.cpp DataManagement/mitkGridVolumeMapperProperty.cpp DataManagement/mitkItkBaseDataAdapter.cpp DataManagement/mitkLabeledImageLookupTable.cpp DataManagement/mitkLineOperation.cpp DataManagement/mitkMesh.cpp DataManagement/mitkObjectSet.cpp DataManagement/mitkOrganTypeProperty.cpp DataManagement/mitkPlaneLandmarkProjector.cpp DataManagement/mitkPlane.cpp DataManagement/mitkPropertyManager.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkSeedsImage.cpp DataManagement/mitkSeedsImageLookupTableSource.cpp DataManagement/mitkSphereLandmarkProjector.cpp # DataManagement/mitkUSLookupTableSource.cpp DataManagement/mitkUnstructuredGrid.cpp DataManagement/mitkVideoSource.cpp DataManagement/vtkObjectSet.cpp IO/mitkObjFileIOFactory.cpp IO/mitkObjFileReader.cpp IO/mitkPACSPlugin.cpp IO/mitkParRecFileIOFactory.cpp IO/mitkParRecFileReader.cpp IO/mitkStlVolumeTimeSeriesIOFactory.cpp IO/mitkStlVolumeTimeSeriesReader.cpp IO/mitkUnstructuredGridVtkWriter.cpp IO/mitkUnstructuredGridVtkWriterFactory.cpp IO/mitkVtkUnstructuredGridIOFactory.cpp IO/mitkVtkUnstructuredGridReader.cpp IO/mitkVtkVolumeTimeSeriesIOFactory.cpp IO/mitkVtkVolumeTimeSeriesReader.cpp - Interactions/mitkAutoSegmentationTool.cpp Interactions/mitkConferenceEventMapper.cpp Interactions/mitkConnectPointsInteractor.cpp - Interactions/mitkContourInteractor.cpp - Interactions/mitkContourTool.cpp #Interactions/mitkCoordinateSupplier.cpp #Interactions/mitkDisplayCoordinateOperation.cpp #Interactions/mitkDisplayInteractor.cpp Interactions/mitkAffineInteractor3D.cpp Interactions/mitkDisplayPointSetInteractor.cpp #Interactions/mitkDisplayVectorInteractor.cpp - Interactions/mitkExtrudedContourInteractor.cpp - Interactions/mitkFeedbackContourTool.cpp Interactions/mitkInteractionDebug.cpp Interactions/mitkInteractionDebugger.cpp - Interactions/mitkPaintbrushTool.cpp Interactions/mitkPointInteractor.cpp Interactions/mitkPointSelectorInteractor.cpp #Interactions/mitkPositionTracker.cpp Interactions/mitkSeedsInteractor.cpp - Interactions/mitkSegTool2D.cpp - Interactions/mitkSegmentationsProcessingTool.cpp - Interactions/mitkSetRegionTool.cpp Interactions/mitkSocketClient.cpp Interactions/mitkSurfaceDeformationInteractor3D.cpp Interactions/mitkSurfaceInteractor.cpp Interactions/mitkTool.cpp - Interactions/mitkAddContourTool.cpp - Interactions/mitkAutoCropTool.cpp - Interactions/mitkBinaryThresholdTool.cpp - Interactions/mitkCalculateGrayValueStatisticsTool.cpp - Interactions/mitkCalculateVolumetryTool.cpp - Interactions/mitkCorrectorTool2D.cpp - Interactions/mitkCreateSurfaceTool.cpp - Interactions/mitkEraseRegionTool.cpp - Interactions/mitkFillRegionTool.cpp - Interactions/mitkRegionGrowingTool.cpp - Interactions/mitkSubtractContourTool.cpp - Interactions/mitkDrawPaintbrushTool.cpp - Interactions/mitkErasePaintbrushTool.cpp +# Interactions/mitkCreateSurfaceTool.cpp Interactions/mitkMorphologicTool.cpp Interactions/mitkErodeTool.cpp Interactions/mitkDilateTool.cpp Interactions/mitkOpeningTool.cpp Interactions/mitkClosingTool.cpp - Interactions/mitkBinaryThresholdULTool.cpp Interactions/mitkPixelManipulationTool.cpp - Interactions/mitkRegionGrow3DTool.cpp - Rendering/mitkContourMapper2D.cpp - Rendering/mitkContourSetMapper2D.cpp - Rendering/mitkContourSetVtkMapper3D.cpp - Rendering/mitkContourVtkMapper3D.cpp Rendering/mitkEnhancedPointSetVtkMapper3D.cpp Rendering/mitkImageBackground2D.cpp Rendering/mitkLineMapper2D.cpp # Rendering/mitkLineVtkMapper3D.cpp Rendering/mitkMeshMapper2D.cpp Rendering/mitkMeshVtkMapper3D.cpp Rendering/mitkNativeRenderWindowInteractor.cpp Rendering/mitkSplineMapper2D.cpp Rendering/mitkSplineVtkMapper3D.cpp Rendering/mitkUnstructuredGridMapper2D.cpp Rendering/mitkUnstructuredGridVtkMapper3D.cpp Rendering/mitkVectorImageMapper2D.cpp Rendering/vtkUnstructuredGridMapper.cpp Rendering/vtkMaskedGlyph2D.cpp Rendering/vtkMaskedGlyph3D.cpp Rendering/vtkMitkVolumeTextureMapper3D.cpp Rendering/vtkMitkOpenGLVolumeTextureMapper3D.cpp Rendering/mitkGPUVolumeMapper3D.cpp Rendering/vtkMitkGPUVolumeRayCastMapper.cpp Rendering/vtkMitkOpenGLGPUVolumeRayCastMapper.cpp Rendering/vtkMitkOpenGLGPUVolumeRayCastMapperShaders.cpp ) if(WIN32 AND NOT MINGW) set(CPP_FILES Controllers/mitkMovieGeneratorWin32.cpp ${CPP_FILES} ) endif(WIN32 AND NOT MINGW) diff --git a/Modules/QmitkExt/CMakeLists.txt b/Modules/QmitkExt/CMakeLists.txt index 5a20a6c175..3153b7f1bd 100644 --- a/Modules/QmitkExt/CMakeLists.txt +++ b/Modules/QmitkExt/CMakeLists.txt @@ -1,6 +1,6 @@ MITK_CREATE_MODULE( QmitkExt INCLUDE_DIRS QmitkApplicationBase QmitkPropertyObservers QmitkFunctionalityComponents - DEPENDS MitkExt IpPicSupport Qmitk qwt qxt PlanarFigure SceneSerialization + DEPENDS MitkExt IpPicSupport Qmitk qwt qxt PlanarFigure SceneSerialization Segmentation QT_MODULE ) diff --git a/Modules/QmitkExt/QmitkApplicationBase/QmitkCommonFunctionality.cpp b/Modules/QmitkExt/QmitkApplicationBase/QmitkCommonFunctionality.cpp index 2b71192769..f3e8769a21 100644 --- a/Modules/QmitkExt/QmitkApplicationBase/QmitkCommonFunctionality.cpp +++ b/Modules/QmitkExt/QmitkApplicationBase/QmitkCommonFunctionality.cpp @@ -1,694 +1,718 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2008-09-18 09:35:44 +0200 (Do, 18 Sep 2008) $ Version: $Revision: 15278 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include #include #include #include #include #include -#include #include "mitkPointSetWriter.h" #include "mitkConfig.h" #include "mitkCoreObjectFactory.h" #include "QmitkCommonFunctionality.h" #include #include #include #include #include #include #include #include +#include +#include #include void CommonFunctionality::SaveToFileWriter( mitk::FileWriterWithInformation::Pointer fileWriter, mitk::BaseData::Pointer data, const char* aFileName, const char* propFileName) { if (! fileWriter->CanWriteBaseDataType(data) ) { QMessageBox::critical(NULL,"ERROR","Could not write file. Invalid data type for file writer."); return; } QString fileName; if (aFileName == NULL) { QString proposedName(""); if(propFileName == NULL) { proposedName.append(fileWriter->GetDefaultFilename()); } else { proposedName.append(propFileName).append(fileWriter->GetDefaultExtension()); } fileName = GetSaveFileNameStartingInLastDirectory("Save file", proposedName,QString::fromAscii(fileWriter->GetFileDialogPattern())); // Check if an extension exists already and if not, append the default extension if ( !fileName.contains( QRegExp("\\.\\w+$") ) ) { fileName.append( fileWriter->GetDefaultExtension() ); } else { std::string extension = itksys::SystemTools::GetFilenameLastExtension( fileName.toLocal8Bit().constData() ); if (!fileWriter->IsExtensionValid(extension)) { QString message; message.append("File extension not suitable for writing given data. Choose one extension of this list: "); message.append(fileWriter->GetPossibleFileExtensionsAsString().c_str()); QMessageBox::critical(NULL,"ERROR",message); return; } } } else fileName = aFileName; if (fileName.isEmpty() == false ) { fileWriter->SetFileName( fileName.toLocal8Bit().constData() ); fileWriter->DoWrite( data ); } } /** * Saves the given mitk::BaseData to a file. The user is prompted to * enter a file name. Currently only mitk::Image, mitk::Surface, mitk::PointSet and * mitk::VesselGraphData are supported. This function is deprecated * until the save-problem is solved by means of a Save-Factory or any * other "nice" mechanism */ void CommonFunctionality::SaveBaseData( mitk::BaseData* data, const char * aFileName ) { //save initial time QDateTime initialTime = QDateTime::currentDateTime(); std::string fileNameUsed; //file name that was actually used by the writer (e.g. after file open dialog) bool writingSuccessful = false; try{ if (data != NULL) { mitk::Image::Pointer image = dynamic_cast(data); QString classname(data->GetNameOfClass()); if ( image.IsNotNull() && (classname.compare("Image")==0 || classname.compare("SeedsImage")==0 ) ) { fileNameUsed = CommonFunctionality::SaveImage(image, aFileName, true); if(!(fileNameUsed.length()>0)){ return; } else { writingSuccessful = true; } } if(!writingSuccessful) { mitk::PointSet::Pointer pointset = dynamic_cast(data); if(pointset.IsNotNull()) { std::string fileName; if(aFileName == NULL) fileName = "PointSet"; else fileName = aFileName; fileName = itksys::SystemTools::GetFilenameWithoutExtension(fileName); QString initialFileName = QString::fromStdString(fileName); QString selected_suffix("MITK Point-Sets (*.mps)"); QString possible_suffixes("MITK Point-Sets (*.mps)"); /*QString qfileName = QFileDialog::getSaveFileName( NULL, "Save image", initialFilename ,mitk::CoreObjectFactory::GetInstance()->GetSaveFileExtensions(),&selected_suffix); */ QString qfileName = GetSaveFileNameStartingInLastDirectory("Save file", initialFileName, possible_suffixes, &selected_suffix); MITK_INFO<IsExtensionValid(extension)) { QString message; message.append("File extension not suitable for writing point set data. Choose one extension of this list: "); message.append(writer->GetPossibleFileExtensionsAsString().c_str()); QMessageBox::critical(NULL,"ERROR",message); return; } if (qfileName.isEmpty() == false ) { writer->SetInput( pointset ); writer->SetFileName( qfileName.toLocal8Bit().constData() ); writer->Update(); fileNameUsed = writer->GetFileName(); writingSuccessful = true; } else { return; } } if(!writingSuccessful) { mitk::Surface::Pointer surface = dynamic_cast(data); if(surface.IsNotNull()) { fileNameUsed = CommonFunctionality::SaveSurface(surface, aFileName); if(!(fileNameUsed.length()>0)){ return; } else { writingSuccessful = true; } } if(!writingSuccessful) { // now try the file writers provided by the CoreObjectFactory mitk::CoreObjectFactory::FileWriterList fileWriters = mitk::CoreObjectFactory::GetInstance()->GetFileWriters(); bool writerFound = false; for (mitk::CoreObjectFactory::FileWriterList::iterator it = fileWriters.begin() ; it != fileWriters.end() ; ++it) { if ( (*it)->CanWriteBaseDataType(data) ) { writerFound = true; SaveToFileWriter(*it, data, NULL, aFileName); fileNameUsed = (*it)->GetFileName(); // correct writer has been found->break if(!(fileNameUsed.length()>0)){ return; } else { writingSuccessful = true; break; } } } if(!writerFound) { // no appropriate writer has been found QMessageBox::critical(NULL,"ERROR","Could not find file writer for this data type"); return; } } } } } else { QMessageBox::critical(NULL,"ERROR","Cannot write data (invalid/empty)"); return; } } catch(itk::ExceptionObject e) { QMessageBox::critical( NULL, "SaveDialog", e.GetDescription(),QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); } //writing is assumed to have been successful //check if file exists, file size >0 and last modified after this function was called try{ QFileInfo* fileInfo = new QFileInfo(QString(fileNameUsed.c_str())); if(!fileInfo->exists()) { QMessageBox::warning(NULL,"WARNING","File was not created or was split into multiple files"); } else if(fileInfo->size()==0) { QMessageBox::warning(NULL,"WARNING","File is empty"); } else if(fileInfo->lastModified()SetFileName( fileName ); factory->Update(); return factory->GetOutput( 0 ); } catch ( itk::ExceptionObject & ex ) { itkGenericOutputMacro( << "Exception during file open: " << ex ); return NULL; } } mitk::DataNode::Pointer CommonFunctionality::FileOpenImageSequence(const QString& aFileName) { return FileOpenImageSequence( aFileName.toLocal8Bit().constData() ); } mitk::DataNode::Pointer CommonFunctionality::FileOpenImageSequence(const char* aFileName) { if(aFileName==NULL) return NULL; mitk::DataNodeFactory::Pointer factory = mitk::DataNodeFactory::New(); QString fileName = aFileName; if (!fileName.contains("dcm") && !fileName.contains("DCM")) { int fnstart = fileName.lastIndexOf( QRegExp("[/\\\\]"), fileName.length() ); if(fnstart<0) fnstart=0; int start = fileName.indexOf( QRegExp("[0-9]"), fnstart ); if(start<0) { return FileOpen(fileName.toLocal8Bit().constData());; } char prefix[1024], pattern[1024]; strncpy(prefix, fileName.toLocal8Bit().constData(), start); prefix[start]=0; int stop=fileName.indexOf( QRegExp("[^0-9]"), start ); sprintf(pattern, "%%s%%0%uu%s",stop-start,fileName.toLocal8Bit().constData()+stop); factory->SetFilePattern( pattern ); factory->SetFilePrefix( prefix ); } else { // factory->SetFileName( fileName ); factory->SetFilePattern( fileName.toLocal8Bit().constData() ); factory->SetFilePrefix( fileName.toLocal8Bit().constData() ); } factory->Update(); return factory->GetOutput( 0 ); } mitk::DataNode::Pointer CommonFunctionality::FileOpenImageSequence() { QString fileName = QFileDialog::getOpenFileName(NULL,mitk::CoreObjectFactory::GetInstance()->GetFileExtensions()); if ( !fileName.isNull() ) { return FileOpenImageSequence(fileName); } else { return NULL; } } mitk::DataNode::Pointer CommonFunctionality::FileOpen() { return CommonFunctionality::FileOpenSpecific( mitk::CoreObjectFactory::GetInstance()->GetFileExtensions() ); } mitk::DataNode::Pointer CommonFunctionality::FileOpenSpecific( const QString& fileExtensions) { return FileOpenSpecific( fileExtensions.toLocal8Bit().constData() ); } mitk::DataNode::Pointer CommonFunctionality::FileOpenSpecific( const char *fileExtensions ) { QString fileName = QFileDialog::getOpenFileName( NULL, fileExtensions ); if ( !fileName.isNull() ) { mitk::DataNode::Pointer result = FileOpen(fileName.toLocal8Bit().constData()); if ( result.IsNull() ) { return FileOpenImageSequence(fileName); } else { return result; } } else { return NULL; } } mitk::DataNode::Pointer CommonFunctionality::OpenVolumeOrSliceStack() { mitk::DataNode::Pointer newNode = NULL; QString fileName = QFileDialog::getOpenFileName(NULL,mitk::CoreObjectFactory::GetInstance()->GetFileExtensions() ); if ( !fileName.isNull() ) { newNode = CommonFunctionality::FileOpen(fileName); if (newNode.IsNotNull()) { mitk::Image::Pointer imageData = dynamic_cast (newNode->GetData()) ; if (imageData.IsNull()) return NULL; if (imageData->GetDimension(2) == 1) { // std::string dir = itksys::SystemTools::GetFilenamePath( std::string(fileName.ascii()) ) newNode = CommonFunctionality::FileOpenImageSequence(fileName); imageData = dynamic_cast (newNode->GetData()); } return newNode; } } { return NULL; } } #include "mitkSurfaceVtkWriter.h" #include #include #include std::string CommonFunctionality::SaveSurface(mitk::Surface* surface, const char* aFileName) { std::string fileName; if(aFileName == NULL) fileName = "Surface"; else fileName = aFileName; std::string selectedItemsName = itksys::SystemTools::GetFilenameWithoutExtension(fileName); //selectedItemsName += ".stl" QString selected_suffix("STL File (*.stl)"); QString possible_suffixes("STL File (*.stl);; VTK File (*.vtk);; VTP File (*.vtp)"); QString qfileName = GetSaveFileNameStartingInLastDirectory("Save surface object", QString::fromStdString(selectedItemsName), possible_suffixes,&selected_suffix); if (qfileName.isEmpty()) return ""; std::string extension = itksys::SystemTools::GetFilenameLastExtension( qfileName.toStdString() ); if (extension == "") // if no extension has been entered manually into the filename { // get from combobox selected file extension extension = itksys::SystemTools::GetFilenameLastExtension( selected_suffix.toLocal8Bit().constData()); extension = extension.substr(0, extension.size()-1); qfileName += QString::fromStdString(extension); } if(extension == ".stl" ) { mitk::SurfaceVtkWriter::Pointer writer=mitk::SurfaceVtkWriter::New(); // check if surface actually consists of triangles; if not, the writer will not do anything; so, convert to triangles... vtkPolyData* polys = surface->GetVtkPolyData(); if( polys->GetNumberOfStrips() > 0 ) { vtkTriangleFilter* triangleFilter = vtkTriangleFilter::New(); triangleFilter->SetInput(polys); triangleFilter->Update(); polys = triangleFilter->GetOutput(); polys->Register(NULL); triangleFilter->Delete(); surface->SetVtkPolyData(polys); } writer->SetInput( surface ); writer->SetFileName(qfileName.toLocal8Bit().constData()); writer->GetVtkWriter()->SetFileTypeToBinary(); writer->Write(); } else if(extension == ".vtp") { mitk::SurfaceVtkWriter::Pointer writer=mitk::SurfaceVtkWriter::New(); writer->SetInput( surface ); writer->SetFileName(qfileName.toLocal8Bit().constData()); writer->GetVtkWriter()->SetDataModeToBinary(); writer->Write(); } else if (extension == ".vtk") { mitk::SurfaceVtkWriter::Pointer writer=mitk::SurfaceVtkWriter::New(); writer->SetInput( surface ); writer->SetFileName(qfileName.toLocal8Bit().constData()); writer->Write(); } else { // file extension not suitable for writing specified data type QMessageBox::critical(NULL,"ERROR","File extension not suitable for writing Surface data. Choose .vtk, .stl or .vtp"); return ""; } return qfileName.toLocal8Bit().constData(); } #include "mitkImageWriter.h" #include void CommonFunctionality::HandleGZExtension(std::string &baseFileName, std::string &extension) { if (extension == ".gz") { std::string filenameWithoutGZ = baseFileName; baseFileName = itksys::SystemTools::GetFilenameWithoutLastExtension( filenameWithoutGZ ); extension = itksys::SystemTools::GetFilenameLastExtension( filenameWithoutGZ ) + ".gz"; } } std::string CommonFunctionality::SaveImage(mitk::Image* image, const char* aFileName, bool askForDifferentFilename) { QString selected_suffix("Nearly Raw Raster Data (*.nrrd)"); + std::string defaultExtension = ".nrrd"; std::string fileName; if(aFileName == NULL || askForDifferentFilename) { QString initialFilename(aFileName); if (initialFilename.isEmpty()) initialFilename = "NewImage.pic"; QString qfileName = GetSaveFileNameStartingInLastDirectory("Save image", initialFilename ,mitk::CoreObjectFactory::GetInstance()->GetSaveFileExtensions(),&selected_suffix); MITK_INFO<IsExtensionValid(extension)) { + // muellerm, 12-05-02, using default file extension + // if no valid extension was given, see bug 11799 + + MITK_WARN << extension << " extension is unknown. Writing image to file " << fileName + << defaultExtension; + extension = defaultExtension; + baseFilename = itksys::SystemTools::GetFilenameName( fileName ); + //MITK_INFO << baseFilename; + + /* QString message; message.append("File extension not suitable for writing image data. Choose one extension of this list: "); message.append(imageWriter->GetPossibleFileExtensionsAsString().c_str()); QMessageBox::critical(NULL,"ERROR",message); return ""; + */ } dir += "/"; dir += baseFilename; + + if( itksys::SystemTools::FileExists( (dir + extension).c_str() ) ) + { + int answer = QMessageBox::question( QApplication::topLevelWidgets().at(0), "Warning", + QString("File %1 already exists. Overwrite?").arg( QString::fromStdString(dir + extension) ), + QMessageBox::Yes, + QMessageBox::No ); + if( answer == QMessageBox::No ) + return ""; + } imageWriter->SetInput(image); imageWriter->SetFileName(dir.c_str()); imageWriter->SetExtension(extension.c_str()); imageWriter->Write(); + fileName = dir + extension; } catch ( itk::ExceptionObject &err) { itkGenericOutputMacro( << "Exception during write: " << err ); QString exceptionString; exceptionString.append("Error during write image: "); exceptionString.append(err.GetDescription()); QMessageBox::critical(NULL,"ERROR",exceptionString); return ""; } catch ( ... ) { itkGenericOutputMacro( << "Unknown type of exception during write" ); QMessageBox::critical(NULL,"ERROR","Error during write image. Possibly no writing permission."); fileName = ""; } return fileName; } std::string CommonFunctionality::SaveScreenshot( vtkRenderWindow* renderWindow , const char* filename ) { // // perform some error checking // if ( ! renderWindow ) { itkGenericOutputMacro( << "render window is NULL!" ); return std::string(""); } if ( ! renderWindow ) { itkGenericOutputMacro( << "Unsupported type of render window! The only supported type is currently QmitkRenderWindow." ); return std::string(""); } // // create the screenshot before the filechooser is opened, // so there the file chooser will not be part of the screenshot // //QPixmap buffer = QPixmap::grabWindow( qtRenderWindow->winId() ); // new Version: //// take screenshot of render window without the coloured frame of 2 pixels by cropping the raw image data vtkWindowToImageFilter* wti = vtkWindowToImageFilter::New(); wti->SetInput( renderWindow ); wti->Update(); vtkImageData* imageData = wti->GetOutput(); int framesize = 5; int* windowSize = renderWindow->GetSize(); int numberOfScalarComponents = imageData->GetNumberOfScalarComponents(); vtkImageData* processedImageData = vtkImageData::New(); processedImageData->SetNumberOfScalarComponents(numberOfScalarComponents); processedImageData->SetExtent(0,windowSize[0]-2*framesize-1,0,windowSize[1]-2*framesize-1,0,0); processedImageData->SetScalarTypeToUnsignedChar(); for (int i=framesize; iSetScalarComponentFromDouble(i-framesize,j-framesize,0,k,imageData->GetScalarComponentAsDouble(i,j,0,k)); } } } // write new image as *.png to file vtkPNGWriter* pngWriter = vtkPNGWriter::New(); // // if the provided filename is empty ask the user // for the name of the file in which the screenshot // should be saved // std::string concreteFilename = ""; if( filename == NULL ) { // // show a file selector with the supported file formats // QString qfileName = GetSaveFileNameStartingInLastDirectory("Save screenshot", QString::fromStdString(""), QString::fromStdString(".png") ); if ( qfileName.isEmpty() ) return ""; concreteFilename = qfileName.toLocal8Bit().constData(); } else concreteFilename = filename; // make sure the filename ends with .png const std::string outFileSuffix("png"); std::string::size_type pos = concreteFilename.rfind('.'); if ( pos == std::string::npos ) concreteFilename = concreteFilename + '.' + outFileSuffix; else { std::string extname = concreteFilename.substr(pos+1); if ( extname.empty() ) concreteFilename += outFileSuffix; // name ended with '.' if ( !(extname == outFileSuffix) ) concreteFilename.replace( pos+1, std::string::npos, "png" ); } // // wait for 500 ms to let the file chooser close itself // // int msecs = 500; // clock_t ticks = ( clock_t )( ( ( ( float ) msecs ) / 1000.0f ) * ( ( float ) CLOCKS_PER_SEC ) ); // clock_t goal = ticks + std::clock(); // while ( goal > std::clock() ); // // // save the screenshot under the given filename // pngWriter->SetInput(processedImageData); //pngWriter->SetInput( wti->GetOutput() ); pngWriter->SetFileName( concreteFilename.c_str() ); pngWriter->Write(); if ( pngWriter->GetErrorCode() != 0 ) QMessageBox::information(NULL, "Save Screenshot...", "The file could not be saved. Please check filename, format and access rights..."); wti->Delete(); pngWriter->Delete(); return concreteFilename; } QString CommonFunctionality::GetSaveFileNameStartingInLastDirectory(QString caption, QString defaultFilename, QString filter, QString* selectedFilter) { QString returnValue = ""; static QString lastDirectory = ""; QString filename = lastDirectory + defaultFilename; returnValue = QFileDialog::getSaveFileName(NULL,caption,filename,filter,selectedFilter); if (returnValue != "") { std::string dir = itksys::SystemTools::GetFilenamePath( returnValue.toStdString() ); dir += Poco::Path::separator(); lastDirectory = dir.c_str(); // remember path for next save dialog } return returnValue; } diff --git a/Modules/MitkExt/Algorithms/itkAdaptiveThresholdIterator.h b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h similarity index 100% rename from Modules/MitkExt/Algorithms/itkAdaptiveThresholdIterator.h rename to Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h diff --git a/Modules/MitkExt/Algorithms/itkAdaptiveThresholdIterator.txx b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx similarity index 100% rename from Modules/MitkExt/Algorithms/itkAdaptiveThresholdIterator.txx rename to Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx diff --git a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator_Usage.txt b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator_Usage.txt new file mode 100644 index 0000000000..9bc242d73c --- /dev/null +++ b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator_Usage.txt @@ -0,0 +1,10 @@ +Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h:2:#ifndef __itkAdaptiveThresholdIterator_h +Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h:3:#define __itkAdaptiveThresholdIterator_h +Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h:247:#include "itkAdaptiveThresholdIterator.txx" +Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx:1:#ifndef _itkAdaptiveThresholdIterator_txx +Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx:2:#define _itkAdaptiveThresholdIterator_txx +Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx:4:#include "itkAdaptiveThresholdIterator.h" +Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.h:12: * iterator (such as itkAdaptiveThresholdIterator) +Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx:8:#include "itkAdaptiveThresholdIterator.h" +mbi-sb/QFunctionalities/QmitkFailSafeRegionGrowing/QmitkFailSafeRegionGrowing.cpp:78:#include +mbi/Plugins/org.mbi.gui.qt.failsaferegiongrowing/src/internal/QmitkFailSafeRegionGrowing.cpp:75:#include diff --git a/Modules/MitkExt/Algorithms/itkConnectedAdaptiveThresholdImageFilter.h b/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.h similarity index 100% rename from Modules/MitkExt/Algorithms/itkConnectedAdaptiveThresholdImageFilter.h rename to Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.h diff --git a/Modules/MitkExt/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx b/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx similarity index 100% rename from Modules/MitkExt/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx rename to Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx diff --git a/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter_Usage.txt b/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter_Usage.txt new file mode 100644 index 0000000000..e4ff16556f --- /dev/null +++ b/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter_Usage.txt @@ -0,0 +1,16 @@ +Modules/QmitkExt/QmitkAdaptiveRegionGrowingWidget.cpp:33:#include +Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.h:1:#ifndef __itkConnectedAdaptiveThresholdImageFilter_h +Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.h:2:#define __itkConnectedAdaptiveThresholdImageFilter_h +Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.h:117:#include "itkConnectedAdaptiveThresholdImageFilter.txx" +Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx:2:#ifndef _itkConnectedAdaptiveThresholdImageFilter_txx +Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx:3:#define _itkConnectedAdaptiveThresholdImageFilter_txx +Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx:5:#include "itkConnectedAdaptiveThresholdImageFilter.h" +Modules/Segmentation/Interactions/mitkRegionGrow3DTool.cpp:27:#include "itkConnectedAdaptiveThresholdImageFilter.h" +mbi-sb/BundlesQt/org.mitk.gui.qt.patchproposalplanning/src/internal/QmitkPatchProposalPlanningView.cpp:68:#include +mbi-sb/QApplications/VesselnessTools/RegionGrowPhilipp.cpp:4:#include +mbi-sb/QFunctionalities/QmitkAortaPlanning/QmitkAortaPlanning.cpp:71:#include +mbi-sb/QFunctionalities/QmitkFailSafeRegionGrowing/QmitkFailSafeRegionGrowing.cpp:73:#include +mbi/Modules/LiverSegmentation/mitkVesselSegmentationTool.cpp:11://#include +mbi/Modules/VesselSegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingView.cpp:46:#include +mbi/Modules/VesselSegmentationUI/Qmitk/QmitkBoostVesselTreeSegmentationView.cpp:47:#include +mbi/Plugins/org.mbi.gui.qt.failsaferegiongrowing/src/internal/QmitkFailSafeRegionGrowing.cpp:71:#include diff --git a/Modules/MitkExt/Algorithms/itkContourExtractor2DImageFilter.h b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h similarity index 100% rename from Modules/MitkExt/Algorithms/itkContourExtractor2DImageFilter.h rename to Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h diff --git a/Modules/MitkExt/Algorithms/itkContourExtractor2DImageFilter.txx b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx similarity index 100% rename from Modules/MitkExt/Algorithms/itkContourExtractor2DImageFilter.txx rename to Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx diff --git a/Modules/MitkExt/Algorithms/itkImageToPathFilter.h b/Modules/Segmentation/Algorithms/itkImageToPathFilter.h similarity index 100% rename from Modules/MitkExt/Algorithms/itkImageToPathFilter.h rename to Modules/Segmentation/Algorithms/itkImageToPathFilter.h diff --git a/Modules/MitkExt/Algorithms/itkImageToPathFilter.txx b/Modules/Segmentation/Algorithms/itkImageToPathFilter.txx similarity index 100% rename from Modules/MitkExt/Algorithms/itkImageToPathFilter.txx rename to Modules/Segmentation/Algorithms/itkImageToPathFilter.txx diff --git a/Modules/MitkExt/Algorithms/mitkCalculateSegmentationVolume.cpp b/Modules/Segmentation/Algorithms/mitkCalculateSegmentationVolume.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkCalculateSegmentationVolume.cpp rename to Modules/Segmentation/Algorithms/mitkCalculateSegmentationVolume.cpp diff --git a/Modules/MitkExt/Algorithms/mitkCalculateSegmentationVolume.h b/Modules/Segmentation/Algorithms/mitkCalculateSegmentationVolume.h similarity index 93% rename from Modules/MitkExt/Algorithms/mitkCalculateSegmentationVolume.h rename to Modules/Segmentation/Algorithms/mitkCalculateSegmentationVolume.h index 826a265ddb..6131ea0d04 100644 --- a/Modules/MitkExt/Algorithms/mitkCalculateSegmentationVolume.h +++ b/Modules/Segmentation/Algorithms/mitkCalculateSegmentationVolume.h @@ -1,61 +1,61 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITK_CALCULATE_SEGMENTATION_VOLUME_H_INCLUDET_WAD #define MITK_CALCULATE_SEGMENTATION_VOLUME_H_INCLUDET_WAD #include "mitkSegmentationSink.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkImageCast.h" namespace mitk { -class MitkExt_EXPORT CalculateSegmentationVolume : public SegmentationSink +class Segmentation_EXPORT CalculateSegmentationVolume : public SegmentationSink { public: mitkClassMacro( CalculateSegmentationVolume, SegmentationSink ) mitkAlgorithmNewMacro( CalculateSegmentationVolume ); protected: CalculateSegmentationVolume(); // use smart pointers virtual ~CalculateSegmentationVolume(); virtual bool ReadyToRun(); virtual bool ThreadedUpdateFunction(); // will be called from a thread after calling StartAlgorithm template < typename TPixel, unsigned int VImageDimension > void ItkImageProcessing( itk::Image< TPixel, VImageDimension >* itkImage, TPixel* dummy = NULL ); private: unsigned int m_Volume; Vector3D m_CenterOfMass; Vector3D m_MinIndexOfBoundingBox; Vector3D m_MaxIndexOfBoundingBox; }; } // namespace #endif diff --git a/Modules/MitkExt/Algorithms/mitkComputeContourSetNormalsFilter.cpp b/Modules/Segmentation/Algorithms/mitkComputeContourSetNormalsFilter.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkComputeContourSetNormalsFilter.cpp rename to Modules/Segmentation/Algorithms/mitkComputeContourSetNormalsFilter.cpp diff --git a/Modules/MitkExt/Algorithms/mitkComputeContourSetNormalsFilter.h b/Modules/Segmentation/Algorithms/mitkComputeContourSetNormalsFilter.h similarity index 95% rename from Modules/MitkExt/Algorithms/mitkComputeContourSetNormalsFilter.h rename to Modules/Segmentation/Algorithms/mitkComputeContourSetNormalsFilter.h index b3467bd15f..b9e9366da3 100644 --- a/Modules/MitkExt/Algorithms/mitkComputeContourSetNormalsFilter.h +++ b/Modules/Segmentation/Algorithms/mitkComputeContourSetNormalsFilter.h @@ -1,106 +1,106 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: $ Version: $Revision: $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkComputeContourSetNormalsFilter_h_Included #define mitkComputeContourSetNormalsFilter_h_Included -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkSurfaceToSurfaceFilter.h" #include "mitkProgressBar.h" #include "mitkSurface.h" #include "vtkCellArray.h" #include "vtkPolyData.h" #include "vtkSmartPointer.h" #include "vtkDoubleArray.h" #include "vtkMath.h" #include "vtkCellData.h" #include "vtkLine.h" #include "mitkImage.h" namespace mitk { /** \brief Filter to compute the normales for contours based on vtkPolygons This filter takes a number of extracted contours and computes the normals for each contour edge point. The normals can be accessed by calling: filter->GetOutput(i)->GetVtkPolyData()->GetCellData()->GetNormals(); See also the method GetNormalsAsSurface() Note: If a segmentation binary image is provided this filter assures that the computed normals do not point into the segmentation image $Author: fetzer$ */ -class MitkExt_EXPORT ComputeContourSetNormalsFilter : public SurfaceToSurfaceFilter +class Segmentation_EXPORT ComputeContourSetNormalsFilter : public SurfaceToSurfaceFilter { public: mitkClassMacro(ComputeContourSetNormalsFilter,SurfaceToSurfaceFilter); itkNewMacro(Self); itkSetMacro(SegmentationBinaryImage, mitk::Image::Pointer); /* \brief Returns the computed normals as a surface */ mitk::Surface::Pointer GetNormalsAsSurface(); //Resets the filter, i.e. removes all inputs and outputs void Reset(); void SetMaxSpacing(double); /** \brief Set whether the mitkProgressBar should be used \a Parameter true for using the progress bar, false otherwise */ void SetUseProgressBar(bool); /** \brief Set the stepsize which the progress bar should proceed \a Parameter The stepsize for progressing */ void SetProgressStepSize(unsigned int stepSize); protected: ComputeContourSetNormalsFilter(); virtual ~ComputeContourSetNormalsFilter(); virtual void GenerateData(); virtual void GenerateOutputInformation(); private: //The segmentation out of which the contours were extracted. Can be used to determine the direction of the normals mitk::Image::Pointer m_SegmentationBinaryImage; double m_MaxSpacing; unsigned int m_NegativeNormalCounter; unsigned int m_PositiveNormalCounter; bool m_UseProgressBar; unsigned int m_ProgressStepSize; };//class }//namespace #endif diff --git a/Modules/MitkExt/Algorithms/mitkContourSetToPointSetFilter.cpp b/Modules/Segmentation/Algorithms/mitkContourSetToPointSetFilter.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkContourSetToPointSetFilter.cpp rename to Modules/Segmentation/Algorithms/mitkContourSetToPointSetFilter.cpp diff --git a/Modules/MitkExt/Algorithms/mitkContourSetToPointSetFilter.h b/Modules/Segmentation/Algorithms/mitkContourSetToPointSetFilter.h similarity index 93% rename from Modules/MitkExt/Algorithms/mitkContourSetToPointSetFilter.h rename to Modules/Segmentation/Algorithms/mitkContourSetToPointSetFilter.h index 04bdb21113..37e49036b6 100644 --- a/Modules/MitkExt/Algorithms/mitkContourSetToPointSetFilter.h +++ b/Modules/Segmentation/Algorithms/mitkContourSetToPointSetFilter.h @@ -1,69 +1,69 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _mitkContourSetToPointSetFilter_h__ #define _mitkContourSetToPointSetFilter_h__ #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkPointSet.h" #include "mitkPointSetSource.h" #include "mitkContourSet.h" namespace mitk { /** * * @brief Converts a contour set to a point set. * * The resulting pointset consists of sample points of all the contours * * @ingroup SurfaceFilters * @ingroup Process */ -class MitkExt_EXPORT ContourSetToPointSetFilter : public PointSetSource +class Segmentation_EXPORT ContourSetToPointSetFilter : public PointSetSource { public: mitkClassMacro(ContourSetToPointSetFilter, PointSetSource); itkNewMacro(Self); itkSetMacro(Frequency, unsigned int); itkGetMacro(Frequency, unsigned int); virtual void GenerateOutputInformation(); virtual void GenerateData(); const mitk::ContourSet* GetInput(void); virtual void SetInput(const mitk::ContourSet *contourSet); protected: ContourSetToPointSetFilter(); virtual ~ContourSetToPointSetFilter(); protected: unsigned int m_Frequency; }; } // namespace mitk #endif // _mitkContourSetToPointSetFilter_h__ diff --git a/Modules/MitkExt/Algorithms/mitkContourUtils.cpp b/Modules/Segmentation/Algorithms/mitkContourUtils.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkContourUtils.cpp rename to Modules/Segmentation/Algorithms/mitkContourUtils.cpp diff --git a/Modules/MitkExt/Algorithms/mitkContourUtils.h b/Modules/Segmentation/Algorithms/mitkContourUtils.h similarity index 96% rename from Modules/MitkExt/Algorithms/mitkContourUtils.h rename to Modules/Segmentation/Algorithms/mitkContourUtils.h index 4b49e8ebaa..c40da5ff1e 100644 --- a/Modules/MitkExt/Algorithms/mitkContourUtils.h +++ b/Modules/Segmentation/Algorithms/mitkContourUtils.h @@ -1,79 +1,79 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkContourUtilshIncludett #define mitkContourUtilshIncludett #include "mitkImage.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkContour.h" #include "mitkLegacyAdaptors.h" #include namespace mitk { /** * \brief Helpful methods for working with contours and images * * Originally copied from FeedbackContourTool */ -class MitkExt_EXPORT ContourUtils : public itk::Object +class Segmentation_EXPORT ContourUtils : public itk::Object { public: mitkClassMacro(ContourUtils, itk::Object); itkNewMacro(ContourUtils); /** \brief Projects a contour onto an image point by point. Converts from world to index coordinates. \param correctionForIpSegmentation adds 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ Contour::Pointer ProjectContourTo2DSlice(Image* slice, Contour* contourIn3D, bool correctionForIpSegmentation, bool constrainToInside); /** \brief Projects a slice index coordinates of a contour back into world coordinates. \param correctionForIpSegmentation subtracts 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ Contour::Pointer BackProjectContourFrom2DSlice(const Geometry3D* sliceGeometry, Contour* contourIn2D, bool correctionForIpSegmentation = false); /** \brief Fill a contour in a 2D slice with a specified pixel value. */ void FillContourInSlice( Contour* projectedContour, Image* sliceImage, int paintingPixelValue = 1 ); protected: ContourUtils(); virtual ~ContourUtils(); /** \brief Paint a filled contour (e.g. of an ipSegmentation pixel type) into a mitk::Image (or arbitraty pixel type). Will not copy the whole filledContourSlice, but only set those pixels in originalSlice to overwritevalue, where the corresponding pixel in filledContourSlice is non-zero. */ template void ItkCopyFilledContourToSlice( itk::Image* originalSlice, const Image* filledContourSlice, int overwritevalue = 1 ); }; } #endif diff --git a/Modules/MitkExt/Algorithms/mitkCorrectorAlgorithm.cpp b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkCorrectorAlgorithm.cpp rename to Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp diff --git a/Modules/MitkExt/Algorithms/mitkCorrectorAlgorithm.h b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.h similarity index 96% rename from Modules/MitkExt/Algorithms/mitkCorrectorAlgorithm.h rename to Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.h index 4ae9403074..9b1e48b129 100644 --- a/Modules/MitkExt/Algorithms/mitkCorrectorAlgorithm.h +++ b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.h @@ -1,99 +1,99 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkCorrectorAlgorithmhIncluded #define mitkCorrectorAlgorithmhIncluded #include "mitkImageToImageFilter.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkContour.h" #include "ipSegmentation.h" #include namespace mitk { /** * This class encapsulates an algorithm, which takes a 2D binary image and a contour. * The algorithm tests if the line begins and ends inside or outside a segmentation * and whether areas should be added to or subtracted from the segmentation shape. * * This class has two outputs: * \li a difference image from GetDifferenceImage() * \li the modified input image from GetOutput() * * The difference image is an image of the same dimensions as the input. Each pixel has * one of three values -1, 0, +1 meaning * \li -1 this pixel was foreground in the input image and should be removed from the segmentation * \li 0 this pixel needs no change * \li +1 this pixel was background in the input image and should be added to the segmentation * * The output image is a combination of the original input with the generated difference image. * * \sa CorrectorTool2D */ -class MitkExt_EXPORT CorrectorAlgorithm : public ImageToImageFilter +class Segmentation_EXPORT CorrectorAlgorithm : public ImageToImageFilter { public: mitkClassMacro(CorrectorAlgorithm, ImageToImageFilter); itkNewMacro(CorrectorAlgorithm); /** * \brief User drawn contour */ itkSetMacro(Contour, Contour*); /** * \brief Calculated difference image. */ //itkGetObjectMacro(DifferenceImage, Image); protected: // used by TobiasHeimannCorrectionAlgorithm typedef struct { int lineStart; int lineEnd; bool modified; } TSegData; CorrectorAlgorithm(); virtual ~CorrectorAlgorithm(); // does the actual processing virtual void GenerateData(); void TobiasHeimannCorrectionAlgorithm(mitkIpPicDescriptor* pic); bool modifySegment( int lineStart, int lineEnd, ipMITKSegmentationTYPE state, mitkIpPicDescriptor *pic, int* _ofsArray ); void CalculateDifferenceImage( Image* modifiedImage, Image* originalImage ); template void ItkCalculateDifferenceImage( itk::Image* originalImage, Image* modifiedMITKImage ); Image::Pointer m_WorkingImage; Contour::ConstPointer m_Contour; Image::Pointer m_DifferenceImage; }; } #endif diff --git a/Modules/MitkExt/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.cpp b/Modules/Segmentation/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.cpp rename to Modules/Segmentation/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.cpp diff --git a/Modules/MitkExt/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.h b/Modules/Segmentation/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.h similarity index 97% rename from Modules/MitkExt/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.h rename to Modules/Segmentation/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.h index 3113cd11b9..4486652752 100644 --- a/Modules/MitkExt/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.h +++ b/Modules/Segmentation/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.h @@ -1,159 +1,159 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: $ Version: $Revision: $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkCreateDistanceImageFromSurfaceFilter_h_Included #define mitkCreateDistanceImageFromSurfaceFilter_h_Included -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkImageSource.h" #include "mitkSurface.h" #include "mitkProgressBar.h" #include "vtkSmartPointer.h" #include "vtkDoubleArray.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkPolyData.h" #include "vnl/vnl_matrix.h" #include "vnl/vnl_vector.h" #include "vnl/vnl_vector_fixed.h" #include "vnl/algo/vnl_qr.h" #include "itkImage.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkNeighborhoodIterator.h" #include namespace mitk { /** \brief This filter interpolates the 3D surface for a segmented area. The basis for the interpolation are the edge-points of contours that are drawn into an image. The interpolation itself is performed via Radial Basis Function Interpolation. ATTENTION: This filter needs beside the edge points of the delineated contours additionally the normals for each edge point. \sa mitkSurfaceInterpolationController Based on the contour edge points and their normal this filter calculates a distance function with the following properties: - Putting a point into the distance function that lies inside the considered surface gives a negativ scalar value - Putting a point into the distance function that lies outside the considered surface gives a positive scalar value - Putting a point into the distance function that lies exactly on the considered surface gives the value zero With this interpolated distance function a distance image will be created. The desired surface can then be extract e.g. with the marching cubes algorithm. (Within the distance image the surface goes exactly where the pixelvalues are zero) Note that the obtained distance image has always an isotropig spacing. The size (in this case volume) of the image can be adjusted by calling SetDistanceImageVolume(unsigned int volume) which specifies the number ob pixels enclosed by the image. \ingroup Process $Author: fetzer$ */ - class MitkExt_EXPORT CreateDistanceImageFromSurfaceFilter : public ImageSource + class Segmentation_EXPORT CreateDistanceImageFromSurfaceFilter : public ImageSource { public: typedef vnl_vector_fixed PointType; typedef std::vector< PointType > NormalList; typedef std::vector< PointType > CenterList; typedef vnl_matrix SolutionMatrix; typedef vnl_vector FunctionValues; typedef vnl_vector InterpolationWeights; typedef std::vector SurfaceList; mitkClassMacro(CreateDistanceImageFromSurfaceFilter,ImageSource); itkNewMacro(Self); //Methods copied from mitkSurfaceToSurfaceFilter virtual void SetInput( const mitk::Surface* surface ); virtual void SetInput( unsigned int idx, const mitk::Surface* surface ); virtual const mitk::Surface* GetInput(); virtual const mitk::Surface* GetInput( unsigned int idx ); virtual void RemoveInputs(mitk::Surface* input); /* \brief Set the size of the output distance image. The size is specified by the image's volume (i.e. in this case how many pixels are enclosed by the image) If non is set, the volume will be 500000 pixels. */ itkSetMacro(DistanceImageVolume, unsigned int); void PrintEquationSystem(); //Resets the filter, i.e. removes all inputs and outputs void Reset(); /** \brief Set whether the mitkProgressBar should be used \a Parameter true for using the progress bar, false otherwise */ void SetUseProgressBar(bool); /** \brief Set the stepsize which the progress bar should proceed \a Parameter The stepsize for progressing */ void SetProgressStepSize(unsigned int stepSize); protected: CreateDistanceImageFromSurfaceFilter(); virtual ~CreateDistanceImageFromSurfaceFilter(); virtual void GenerateData(); virtual void GenerateOutputInformation(); private: void CreateSolutionMatrixAndFunctionValues(); double CalculateDistanceValue(PointType p); void CreateDistanceImage (); //Datastructures for the interpolation CenterList m_Centers; NormalList m_Normals; FunctionValues m_FunctionValues; InterpolationWeights m_Weights; SolutionMatrix m_SolutionMatrix; double m_DistanceImageSpacing; unsigned int m_DistanceImageVolume; bool m_UseProgressBar; unsigned int m_ProgressStepSize; }; }//namespace #endif diff --git a/Modules/MitkExt/Algorithms/mitkDiffImageApplier.cpp b/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkDiffImageApplier.cpp rename to Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp diff --git a/Modules/MitkExt/Algorithms/mitkDiffImageApplier.h b/Modules/Segmentation/Algorithms/mitkDiffImageApplier.h similarity index 95% rename from Modules/MitkExt/Algorithms/mitkDiffImageApplier.h rename to Modules/Segmentation/Algorithms/mitkDiffImageApplier.h index e21c7595a8..c72b309ad0 100644 --- a/Modules/MitkExt/Algorithms/mitkDiffImageApplier.h +++ b/Modules/Segmentation/Algorithms/mitkDiffImageApplier.h @@ -1,87 +1,87 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.0 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkDiffImageApplier_h_Included #define mitkDiffImageApplier_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkOperationActor.h" #include "mitkImage.h" #include #include namespace mitk { /** \brief Applies difference images to 3D images. This class is supposed to execute ApplyDiffImageOperations, which contain information about pixel changes within one image slice. Class should be called from the undo stack. At the moment, ApplyDiffImageOperations are only created by OverwriteSliceImageFilter. $Author: maleike $ */ -class MitkExt_EXPORT DiffImageApplier : public itk::Object, public OperationActor +class Segmentation_EXPORT DiffImageApplier : public itk::Object, public OperationActor { public: mitkClassMacro(DiffImageApplier, Object); itkNewMacro(DiffImageApplier); virtual void ExecuteOperation( Operation* operation ); static DiffImageApplier* GetInstanceForUndo(); protected: DiffImageApplier(); // purposely hidden virtual ~DiffImageApplier(); template void ItkImageSwitch2DDiff( itk::Image* image ); template void ItkImageSwitch3DDiff( itk::Image* image ); template void ItkImageProcessing2DDiff( itk::Image* itkImage1, itk::Image* itkImage2 ); template void ItkImageProcessing3DDiff( itk::Image* itkImage1, itk::Image* itkImage2 ); template void ItkInvertPixelValues( itk::Image* itkImage ); Image::Pointer m_Image; Image::ConstPointer m_SliceDifferenceImage; unsigned int m_SliceIndex; unsigned int m_SliceDimension; unsigned int m_TimeStep; unsigned int m_Dimension0; unsigned int m_Dimension1; double m_Factor; }; } // namespace #endif diff --git a/Modules/MitkExt/Algorithms/mitkImageToContourFilter.cpp b/Modules/Segmentation/Algorithms/mitkImageToContourFilter.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkImageToContourFilter.cpp rename to Modules/Segmentation/Algorithms/mitkImageToContourFilter.cpp diff --git a/Modules/MitkExt/Algorithms/mitkImageToContourFilter.h b/Modules/Segmentation/Algorithms/mitkImageToContourFilter.h similarity index 96% rename from Modules/MitkExt/Algorithms/mitkImageToContourFilter.h rename to Modules/Segmentation/Algorithms/mitkImageToContourFilter.h index e7022b7470..4c1895e3fc 100644 --- a/Modules/MitkExt/Algorithms/mitkImageToContourFilter.h +++ b/Modules/Segmentation/Algorithms/mitkImageToContourFilter.h @@ -1,92 +1,92 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: $ Version: $Revision: $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkImageToContourFilter_h_Included #define mitkImageToContourFilter_h_Included //#include "MitkSBExports.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "itkImage.h" #include "mitkImage.h" #include "itkContourExtractor2DImageFilter.h" #include "mitkImageToSurfaceFilter.h" #include "mitkSurface.h" #include "vtkPolyData.h" #include "vtkPolygon.h" #include "vtkCellArray.h" #include "mitkProgressBar.h" namespace mitk { /** \brief A filter that can extract contours out of a 2D binary image This class takes an 2D mitk::Image as input and extracts all contours which are drawn it. The contour extraction is done by using the itk::ContourExtractor2DImageFilter. The output is a mitk::Surface. $Author: fetzer$ */ -class MitkExt_EXPORT ImageToContourFilter : public ImageToSurfaceFilter +class Segmentation_EXPORT ImageToContourFilter : public ImageToSurfaceFilter { public: mitkClassMacro(ImageToContourFilter,ImageToSurfaceFilter); itkNewMacro(Self); /** \brief Set macro for the geometry of the slice. If it is not set explicitly the geometry will be taken from the slice \a Parameter The slice`s geometry */ itkSetMacro(SliceGeometry, Geometry3D*); //typedef unsigned int VDimension; typedef itk::PolyLineParametricPath<2> PolyLineParametricPath2D; typedef PolyLineParametricPath2D::VertexListType ContourPath; /** \brief Set whether the mitkProgressBar should be used \a Parameter true for using the progress bar, false otherwise */ void SetUseProgressBar(bool); /** \brief Set the stepsize which the progress bar should proceed \a Parameter The stepsize for progressing */ void SetProgressStepSize(unsigned int stepSize); protected: ImageToContourFilter(); virtual ~ImageToContourFilter(); virtual void GenerateData(); virtual void GenerateOutputInformation(); private: const Geometry3D* m_SliceGeometry; bool m_UseProgressBar; unsigned int m_ProgressStepSize; template void Itk2DContourExtraction (itk::Image* sliceImage); };//class }//namespace #endif diff --git a/Modules/MitkExt/Algorithms/mitkManualSegmentationToSurfaceFilter.cpp b/Modules/Segmentation/Algorithms/mitkManualSegmentationToSurfaceFilter.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkManualSegmentationToSurfaceFilter.cpp rename to Modules/Segmentation/Algorithms/mitkManualSegmentationToSurfaceFilter.cpp diff --git a/Modules/MitkExt/Algorithms/mitkManualSegmentationToSurfaceFilter.h b/Modules/Segmentation/Algorithms/mitkManualSegmentationToSurfaceFilter.h similarity index 97% rename from Modules/MitkExt/Algorithms/mitkManualSegmentationToSurfaceFilter.h rename to Modules/Segmentation/Algorithms/mitkManualSegmentationToSurfaceFilter.h index 590411d0ea..eb5a5c109b 100644 --- a/Modules/MitkExt/Algorithms/mitkManualSegmentationToSurfaceFilter.h +++ b/Modules/Segmentation/Algorithms/mitkManualSegmentationToSurfaceFilter.h @@ -1,170 +1,170 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _MITKMANUALSEGMENTATIONTISURFACEFILTER_h__ #define _MITKMANUALSEGMENTATIONTISURFACEFILTER_h__ #include -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include #include #include #include namespace mitk { /** * @brief Supplies a 3D surface from pre-processed segmentation. * * The resulting surface depends on a filter pipeline based on vtkMedian (1) and a Gaussian filter with vtkImageGaussianSmooth (2). * All voxel can be changed to an isotropic representation of the * image (ATTANTION: the number of voxels in the will change). The * resulting isotropic image has 1mm isotropic voxel by default. But * can be varied freely. * * @ingroup ImageFilters * @ingroup Process */ - class MitkExt_EXPORT ManualSegmentationToSurfaceFilter : public ImageToSurfaceFilter + class Segmentation_EXPORT ManualSegmentationToSurfaceFilter : public ImageToSurfaceFilter { public: mitkClassMacro(ManualSegmentationToSurfaceFilter,ImageToSurfaceFilter); typedef double vtkDouble; /** * Will pre-process a segmentation voxelwise. The segmentation can use * a hole fill relating a median filter and smooth by a Gaussian * filter. * The image can be interpolated to an isotropic image. * By default every filter is disabled. * This method calls CreateSurface from mitkImageToSurfaceFilter and * does not need a manual call since we use Update(). */ virtual void GenerateData(); itkNewMacro(Self); /** * Supplies a method for setting median filter by a bool value. */ itkSetMacro(MedianFilter3D,bool); /** * Return state if median filter is enabled. */ itkGetConstMacro(MedianFilter3D,bool); /** * Enable the median filter (first filter in pipeline). */ itkBooleanMacro(MedianFilter3D); /** * Supplies a method to enable Interpolation. */ itkSetMacro(Interpolation,bool); /** * Returns activation state of interpolation filter. */ itkGetConstMacro(Interpolation,bool); /** * Enable the interpolation filter (second filter in pipeline) for * isotropic voxel. */ itkBooleanMacro(Interpolation); /** * Supplies a method for Gaussian filter (third filter in pipeline). */ itkSetMacro(UseGaussianImageSmooth,bool); /** * Returns activation state of standard deviation filter. */ itkGetConstMacro(UseGaussianImageSmooth,bool); /** * Enables Gaussian image smooth. As well the threshold for the * CreateSurface() method will raise the threshold to 49 and changes * the image range set from 0 to 100. It is made for reasons in * binary images to prevent conflicts with the used filter. There are * better results for dividing fore- and background. */ itkBooleanMacro(UseGaussianImageSmooth); /** * Set standard deviation for Gaussian Filter. * @param _arg by default 1.5 */ itkSetMacro(GaussianStandardDeviation, double); /** * Returns the standard deviation of the Gaussian filter which will be * used when filter is enabled. */ itkGetConstMacro(GaussianStandardDeviation, double); /** * Set the Kernel for Median3DFilter. By default kernel is set to 3x3x3. * If you choose '1' nothing will be processed in this direction. */ void SetMedianKernelSize(int x, int y, int z); /** * Returns the kernel size in the first direction. */ itkGetConstMacro(MedianKernelSizeX, int); /** * Returns the kernel size in the second direction. */ itkGetConstMacro(MedianKernelSizeY, int); /** * Returns the kernel size in the third direction. */ itkGetConstMacro(MedianKernelSizeZ, int); /** * Set the values for Spacing in X, Y and Z-Dimension */ void SetInterpolation(vtkDouble x, vtkDouble y, vtkDouble z); protected: ManualSegmentationToSurfaceFilter(); virtual ~ManualSegmentationToSurfaceFilter(); bool m_MedianFilter3D; int m_MedianKernelSizeX, m_MedianKernelSizeY, m_MedianKernelSizeZ; bool m_UseGaussianImageSmooth; //Gaussian Filter double m_GaussianStandardDeviation; bool m_Interpolation; vtkDouble m_InterpolationX; vtkDouble m_InterpolationY; vtkDouble m_InterpolationZ; };//namespace } #endif //_MITKMANUALSEGMENTATIONTISURFACEFILTER_h__ diff --git a/Modules/MitkExt/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp rename to Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp diff --git a/Modules/MitkExt/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h similarity index 97% rename from Modules/MitkExt/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h rename to Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h index 90ca6832ed..e85537156c 100644 --- a/Modules/MitkExt/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h +++ b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h @@ -1,119 +1,119 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2010-01-28 18:32:03 +0100 (Do, 28 Jan 2010) $ Version: $Revision: 21147 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkOverwriteDirectedPlaneImageFilter_h_Included #define mitkOverwriteDirectedPlaneImageFilter_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkImageToImageFilter.h" #include namespace mitk { /** \brief Writes a 2D slice into a 3D image. \sa SegTool2D \sa ContourTool \sa ExtractImageFilter \ingroup Process \ingroup Reliver There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage This class takes a 3D mitk::Image as input and tries to replace one slice in it with the second input image, which is specified by calling SetSliceImage with a 2D mitk::Image. Two parameters determine which slice is replaced: the "slice dimension" is that one, which is constant for all points in the plane, e.g. transversal would mean 2. The "slice index" is the slice index in the image direction you specified with "affected dimension". Indices count from zero. This class works with all kind of image types, the only restrictions being that the input is 3D, and the slice image is 2D. If requested by SetCreateUndoInformation(true), this class will create instances of ApplyDiffImageOperation for the undo stack. These operations will (on user request) be executed by DiffImageApplier to perform undo. Last contributor: $Author: maleike $ */ -class MitkExt_EXPORT OverwriteDirectedPlaneImageFilter : public ImageToImageFilter +class Segmentation_EXPORT OverwriteDirectedPlaneImageFilter : public ImageToImageFilter { public: mitkClassMacro(OverwriteDirectedPlaneImageFilter, ImageToImageFilter); itkNewMacro(OverwriteDirectedPlaneImageFilter); /** \brief Which plane to overwrite */ const Geometry3D* GetPlaneGeometry3D() const { return m_PlaneGeometry; } void SetPlaneGeometry3D( const Geometry3D *geometry ) { m_PlaneGeometry = geometry; } /** \brief Time step of the slice to overwrite */ itkSetMacro(TimeStep, unsigned int); itkGetConstMacro(TimeStep, unsigned int); /** \brief Whether to create undo operation in the MITK undo stack */ itkSetMacro(CreateUndoInformation, bool); itkGetConstMacro(CreateUndoInformation, bool); itkSetObjectMacro(SliceImage, Image); const Image* GetSliceImage() { return m_SliceImage.GetPointer(); } const Image* GetLastDifferenceImage() { return m_SliceDifferenceImage.GetPointer(); } protected: OverwriteDirectedPlaneImageFilter(); // purposely hidden virtual ~OverwriteDirectedPlaneImageFilter(); virtual void GenerateData(); template void ItkSliceOverwriting (itk::Image* input3D); template void ItkImageSwitch( itk::Image* image ); template void ItkImageProcessing( itk::Image* itkImage1, itk::Image* itkImage2 ); //std::string EventDescription( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ); Image::ConstPointer m_SliceImage; Image::Pointer m_SliceDifferenceImage; const Geometry3D *m_PlaneGeometry; const Geometry3D *m_ImageGeometry3D; unsigned int m_TimeStep; unsigned int m_Dimension0; unsigned int m_Dimension1; bool m_CreateUndoInformation; }; } // namespace #endif diff --git a/Modules/MitkExt/Algorithms/mitkOverwriteSliceImageFilter.cpp b/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkOverwriteSliceImageFilter.cpp rename to Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.cpp diff --git a/Modules/MitkExt/Algorithms/mitkOverwriteSliceImageFilter.h b/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.h similarity index 97% rename from Modules/MitkExt/Algorithms/mitkOverwriteSliceImageFilter.h rename to Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.h index 5e4366f582..dc7c8f1261 100644 --- a/Modules/MitkExt/Algorithms/mitkOverwriteSliceImageFilter.h +++ b/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.h @@ -1,124 +1,124 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkOverwriteSliceImageFilter_h_Included #define mitkOverwriteSliceImageFilter_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkImageToImageFilter.h" #include namespace mitk { /** \brief Writes a 2D slice into a 3D image. \sa SegTool2D \sa ContourTool \sa ExtractImageFilter \ingroup Process \ingroup ToolManagerEtAl There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage This class takes a 3D mitk::Image as input and tries to replace one slice in it with the second input image, which is specified by calling SetSliceImage with a 2D mitk::Image. Two parameters determine which slice is replaced: the "slice dimension" is that one, which is constant for all points in the plane, e.g. transversal would mean 2. The "slice index" is the slice index in the image direction you specified with "affected dimension". Indices count from zero. This class works with all kind of image types, the only restrictions being that the input is 3D, and the slice image is 2D. If requested by SetCreateUndoInformation(true), this class will create instances of ApplyDiffImageOperation for the undo stack. These operations will (on user request) be executed by DiffImageApplier to perform undo. Last contributor: $Author$ */ -class MitkExt_EXPORT OverwriteSliceImageFilter : public ImageToImageFilter +class Segmentation_EXPORT OverwriteSliceImageFilter : public ImageToImageFilter { public: mitkClassMacro(OverwriteSliceImageFilter, ImageToImageFilter); itkNewMacro(OverwriteSliceImageFilter); /** \brief Which slice to overwrite (first one has index 0). */ itkSetMacro(SliceIndex, unsigned int); itkGetConstMacro(SliceIndex, unsigned int); /** \brief The orientation of the slice to overwrite. \a Parameter \a SliceDimension Number of the dimension which is constant for all pixels of the desired slices (e.g. 0 for transversal) */ itkSetMacro(SliceDimension, unsigned int); itkGetConstMacro(SliceDimension, unsigned int); /** \brief Time step of the slice to overwrite */ itkSetMacro(TimeStep, unsigned int); itkGetConstMacro(TimeStep, unsigned int); /** \brief Whether to create undo operation in the MITK undo stack */ itkSetMacro(CreateUndoInformation, bool); itkGetConstMacro(CreateUndoInformation, bool); itkSetObjectMacro(SliceImage, Image); const Image* GetSliceImage() { return m_SliceImage.GetPointer(); } const Image* GetLastDifferenceImage() { return m_SliceDifferenceImage.GetPointer(); } protected: OverwriteSliceImageFilter(); // purposely hidden virtual ~OverwriteSliceImageFilter(); virtual void GenerateData(); template void ItkImageSwitch( itk::Image* image ); template void ItkImageProcessing( itk::Image* itkImage1, itk::Image* itkImage2 ); std::string EventDescription( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ); Image::ConstPointer m_SliceImage; Image::Pointer m_SliceDifferenceImage; unsigned int m_SliceIndex; unsigned int m_SliceDimension; unsigned int m_TimeStep; unsigned int m_Dimension0; unsigned int m_Dimension1; bool m_CreateUndoInformation; }; } // namespace #endif diff --git a/Modules/MitkExt/Algorithms/mitkReduceContourSetFilter.cpp b/Modules/Segmentation/Algorithms/mitkReduceContourSetFilter.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkReduceContourSetFilter.cpp rename to Modules/Segmentation/Algorithms/mitkReduceContourSetFilter.cpp diff --git a/Modules/MitkExt/Algorithms/mitkReduceContourSetFilter.h b/Modules/Segmentation/Algorithms/mitkReduceContourSetFilter.h similarity index 97% rename from Modules/MitkExt/Algorithms/mitkReduceContourSetFilter.h rename to Modules/Segmentation/Algorithms/mitkReduceContourSetFilter.h index 02b2c5171f..6b19ff5b7b 100644 --- a/Modules/MitkExt/Algorithms/mitkReduceContourSetFilter.h +++ b/Modules/Segmentation/Algorithms/mitkReduceContourSetFilter.h @@ -1,122 +1,122 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: $ Version: $Revision: $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkReduceContourSetFilter_h_Included #define mitkReduceContourSetFilter_h_Included -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkSurfaceToSurfaceFilter.h" #include "mitkSurface.h" #include "mitkProgressBar.h" #include "vtkSmartPointer.h" #include "vtkPolyData.h" #include "vtkPolygon.h" #include "vtkPoints.h" #include "vtkCellArray.h" #include "vtkMath.h" #include namespace mitk { /** \brief A filter that reduces the number of points of contours represented by a mitk::Surface The type of the reduction can be set via SetReductionType. The two ways provided by this filter is the \li NTH_POINT Algorithm which reduces the contours according to a certain stepsize \li DOUGLAS_PEUCKER Algorithm which incorpates an error tolerance into the reduction. Stepsize and error tolerance can be set via SetStepSize and SetTolerance. Additional if more than one input contour is provided this filter tries detect contours which occur just because of an intersection. These intersection contours are eliminated. In oder to ensure a correct elimination the min and max spacing of the original image must be provided. The output is a mitk::Surface. $Author: fetzer$ */ - class MitkExt_EXPORT ReduceContourSetFilter : public SurfaceToSurfaceFilter + class Segmentation_EXPORT ReduceContourSetFilter : public SurfaceToSurfaceFilter { public: enum Reduction_Type { NTH_POINT, DOUGLAS_PEUCKER }; struct LineSegment { unsigned int StartIndex; unsigned int EndIndex; }; mitkClassMacro(ReduceContourSetFilter,SurfaceToSurfaceFilter); itkNewMacro(Self); itkSetMacro(MinSpacing, double); itkSetMacro(MaxSpacing, double); itkSetMacro(ReductionType, Reduction_Type); itkSetMacro(StepSize, unsigned int); itkSetMacro(Tolerance, double); //Resets the filter, i.e. removes all inputs and outputs void Reset(); /** \brief Set whether the mitkProgressBar should be used \a Parameter true for using the progress bar, false otherwise */ void SetUseProgressBar(bool); /** \brief Set the stepsize which the progress bar should proceed \a Parameter The stepsize for progressing */ void SetProgressStepSize(unsigned int stepSize); protected: ReduceContourSetFilter(); virtual ~ReduceContourSetFilter(); virtual void GenerateData(); virtual void GenerateOutputInformation(); private: void ReduceNumberOfPointsByNthPoint (vtkIdType cellSize, vtkIdType* cell, vtkPoints* points, vtkPolygon* reducedPolygon, vtkPoints* reducedPoints); void ReduceNumberOfPointsByDouglasPeucker (vtkIdType cellSize, vtkIdType* cell, vtkPoints* points, vtkPolygon* reducedPolygon, vtkPoints* reducedPoints); bool CheckForIntersection (vtkIdType* currentCell, vtkIdType currentCellSize, vtkPoints* currentPoints, /*vtkIdType numberOfIntersections, vtkIdType* intersectionPoints,*/ unsigned int currentInputIndex); double m_MinSpacing; double m_MaxSpacing; Reduction_Type m_ReductionType; unsigned int m_StepSize; double m_Tolerance; unsigned int m_MaxSegmentLenght; bool m_UseProgressBar; unsigned int m_ProgressStepSize; };//class }//namespace #endif diff --git a/Modules/MitkExt/Algorithms/mitkSegmentationInterpolationAlgorithm.h b/Modules/Segmentation/Algorithms/mitkSegmentationInterpolationAlgorithm.h similarity index 95% rename from Modules/MitkExt/Algorithms/mitkSegmentationInterpolationAlgorithm.h rename to Modules/Segmentation/Algorithms/mitkSegmentationInterpolationAlgorithm.h index d38b0d2dcb..c1f30e6ef2 100644 --- a/Modules/MitkExt/Algorithms/mitkSegmentationInterpolationAlgorithm.h +++ b/Modules/Segmentation/Algorithms/mitkSegmentationInterpolationAlgorithm.h @@ -1,71 +1,71 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 13922 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkSegmentationInterpolationAlgorithm_h_Included #define mitkSegmentationInterpolationAlgorithm_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkImage.h" #include namespace mitk { /** * \brief Interface class for interpolation algorithms * * Interpolation algorithms estimate a binary image (segmentation) given * manual segmentations of neighboring slices. They get the following inputs: * * - slice orientation of given and requested slices (dimension which is constant for all pixels of the meant orientation, e.g. 2 for transversal). * - slice indices of the neighboring slices (for upper and lower slice) * - slice data of the neighboring slices (for upper and lower slice) * - slice index of the requested slice (guaranteed to be between upper and lower index) * - image data of the original patient image that is being segmented (optional, may not be present) * - time step of the requested slice (needed to read out original image data) * * Concrete algorithms can use e.g. itk::ImageSliceConstIteratorWithIndex to * inspect the original patient image at appropriate positions - if they * want to take image data into account. * * All processing is triggered by calling Interpolate(). * * Last contributor: * $Author:$ */ -class MitkExt_EXPORT SegmentationInterpolationAlgorithm : public itk::Object +class Segmentation_EXPORT SegmentationInterpolationAlgorithm : public itk::Object { public: mitkClassMacro(SegmentationInterpolationAlgorithm, itk::Object); virtual Image::Pointer Interpolate(Image::ConstPointer lowerSlice, unsigned int lowerSliceIndex, Image::ConstPointer upperSlice, unsigned int upperSliceIndex, unsigned int requestedIndex, unsigned int sliceDimension, Image::Pointer resultImage, unsigned int timeStep = 0, Image::ConstPointer referenceImage = NULL) = 0; }; } // namespace #endif diff --git a/Modules/Segmentation/Algorithms/mitkSegmentationObjectFactory.cpp b/Modules/Segmentation/Algorithms/mitkSegmentationObjectFactory.cpp new file mode 100644 index 0000000000..403cb2a87e --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkSegmentationObjectFactory.cpp @@ -0,0 +1,142 @@ +/*========================================================================= + + Program: Medical Imaging & Interaction Toolkit + Language: C++ + Date: $Date$ + Version: $Revision: 16916 $ + + Copyright (c) German Cancer Research Center, Division of Medical and + Biological Informatics. All rights reserved. + See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + + =========================================================================*/ + +#include "mitkSegmentationObjectFactory.h" + +#include "mitkProperties.h" +#include "mitkBaseRenderer.h" +#include "mitkDataNode.h" +#include "mitkCoreObjectFactory.h" + +#include "mitkContour.h" +#include "mitkContourMapper2D.h" +#include "mitkContourSetMapper2D.h" +#include "mitkContourSetVtkMapper3D.h" +#include "mitkContourVtkMapper3D.h" + + +mitk::SegmentationObjectFactory::SegmentationObjectFactory() +:CoreObjectFactoryBase() +{ + static bool alreadyDone = false; + if (!alreadyDone) + { + MITK_DEBUG << "SegmentationObjectFactory c'tor" << std::endl; + + //CreateFileExtensionsMap(); + + alreadyDone = true; + } +} + +mitk::Mapper::Pointer mitk::SegmentationObjectFactory::CreateMapper(mitk::DataNode* node, MapperSlotId id) +{ + mitk::Mapper::Pointer newMapper=NULL; + mitk::BaseData *data = node->GetData(); + + if ( id == mitk::BaseRenderer::Standard2D ) + { + if((dynamic_cast(data)!=NULL)) + { + newMapper = mitk::ContourMapper2D::New(); + newMapper->SetDataNode(node); + } + else if((dynamic_cast(data)!=NULL)) + { + newMapper = mitk::ContourSetMapper2D::New(); + newMapper->SetDataNode(node); + } + } + else if ( id == mitk::BaseRenderer::Standard3D ) + { + if((dynamic_cast(data)!=NULL)) + { + newMapper = mitk::ContourVtkMapper3D::New(); + newMapper->SetDataNode(node); + } + else if((dynamic_cast(data)!=NULL)) + { + newMapper = mitk::ContourSetVtkMapper3D::New(); + newMapper->SetDataNode(node); + } + } + return newMapper; +} + +void mitk::SegmentationObjectFactory::SetDefaultProperties(mitk::DataNode* node) +{ + + if(node==NULL) + return; + + mitk::DataNode::Pointer nodePointer = node; + +// mitk::Image::Pointer image = dynamic_cast(node->GetData()); +// if(image.IsNotNull() && image->IsInitialized()) +// { +// mitk::GPUVolumeMapper3D::SetDefaultProperties(node); +// } +// +// if (dynamic_cast(node->GetData())) +// { +// mitk::UnstructuredGridVtkMapper3D::SetDefaultProperties(node); +// } + +} + +const char* mitk::SegmentationObjectFactory::GetFileExtensions() +{ + std::string fileExtension; + this->CreateFileExtensions(m_FileExtensionsMap, fileExtension); + return fileExtension.c_str(); +}; + +mitk::CoreObjectFactoryBase::MultimapType mitk::SegmentationObjectFactory::GetFileExtensionsMap() +{ + return m_FileExtensionsMap; +} + +mitk::CoreObjectFactoryBase::MultimapType mitk::SegmentationObjectFactory::GetSaveFileExtensionsMap() +{ + return m_SaveFileExtensionsMap; +} + +void mitk::SegmentationObjectFactory::CreateFileExtensionsMap() +{ +} + +const char* mitk::SegmentationObjectFactory::GetSaveFileExtensions() +{ + std::string fileExtension; + this->CreateFileExtensions(m_SaveFileExtensionsMap, fileExtension); + return fileExtension.c_str(); +} + +void mitk::SegmentationObjectFactory::RegisterIOFactories() +{ +} + +void RegisterSegmentationObjectFactory() +{ + static bool oneSegmentationObjectFactoryRegistered = false; + if ( ! oneSegmentationObjectFactoryRegistered ) + { + MITK_DEBUG << "Registering SegmentationObjectFactory..." << std::endl; + mitk::CoreObjectFactory::GetInstance()->RegisterExtraFactory(mitk::SegmentationObjectFactory::New()); + oneSegmentationObjectFactoryRegistered = true; + } +} diff --git a/Modules/Segmentation/Algorithms/mitkSegmentationObjectFactory.h b/Modules/Segmentation/Algorithms/mitkSegmentationObjectFactory.h new file mode 100644 index 0000000000..fe49f1cd7e --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkSegmentationObjectFactory.h @@ -0,0 +1,51 @@ +/*========================================================================= + + Program: Medical Imaging & Interaction Toolkit + Language: C++ + Date: $Date$ + Version: $Revision: 13129 $ + + Copyright (c) German Cancer Research Center, Division of Medical and + Biological Informatics. All rights reserved. + See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + + =========================================================================*/ + +#ifndef SEGMENTATIONOBJECTFACTORY_H_INCLUDED +#define SEGMENTATIONOBJECTFACTORY_H_INCLUDED + +#include "mitkCoreObjectFactoryBase.h" +#include "SegmentationExports.h" + +namespace mitk { + + class Segmentation_EXPORT SegmentationObjectFactory : public CoreObjectFactoryBase + { + public: + mitkClassMacro(SegmentationObjectFactory,CoreObjectFactoryBase); + itkNewMacro(SegmentationObjectFactory); + virtual Mapper::Pointer CreateMapper(mitk::DataNode* node, MapperSlotId slotId); + virtual void SetDefaultProperties(mitk::DataNode* node); + virtual const char* GetFileExtensions(); + virtual mitk::CoreObjectFactoryBase::MultimapType GetFileExtensionsMap(); + virtual const char* GetSaveFileExtensions(); + virtual mitk::CoreObjectFactoryBase::MultimapType GetSaveFileExtensionsMap(); + void RegisterIOFactories(); + protected: + SegmentationObjectFactory(); + void CreateFileExtensionsMap(); + MultimapType m_FileExtensionsMap; + MultimapType m_SaveFileExtensionsMap; + }; + +} +// global declaration for simple call by +// applications +void Segmentation_EXPORT RegisterSegmentationObjectFactory(); + +#endif + diff --git a/Modules/MitkExt/Algorithms/mitkSegmentationSink.cpp b/Modules/Segmentation/Algorithms/mitkSegmentationSink.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkSegmentationSink.cpp rename to Modules/Segmentation/Algorithms/mitkSegmentationSink.cpp diff --git a/Modules/MitkExt/Algorithms/mitkSegmentationSink.h b/Modules/Segmentation/Algorithms/mitkSegmentationSink.h similarity index 93% rename from Modules/MitkExt/Algorithms/mitkSegmentationSink.h rename to Modules/Segmentation/Algorithms/mitkSegmentationSink.h index e62d80d540..4cddbc7186 100644 --- a/Modules/MitkExt/Algorithms/mitkSegmentationSink.h +++ b/Modules/Segmentation/Algorithms/mitkSegmentationSink.h @@ -1,57 +1,57 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITK_SEGMENTATION_SINK_H_INCLUDET_WAD #define MITK_SEGMENTATION_SINK_H_INCLUDET_WAD #include "mitkNonBlockingAlgorithm.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" namespace mitk { -class MitkExt_EXPORT SegmentationSink : public NonBlockingAlgorithm +class Segmentation_EXPORT SegmentationSink : public NonBlockingAlgorithm { public: mitkClassMacro( SegmentationSink, NonBlockingAlgorithm ) mitkAlgorithmNewMacro( SegmentationSink ); protected: SegmentationSink(); // use smart pointers virtual ~SegmentationSink(); virtual void Initialize(const NonBlockingAlgorithm* other = NULL); virtual bool ReadyToRun(); virtual bool ThreadedUpdateFunction(); // will be called from a thread after calling StartAlgorithm void InsertBelowGroupNode(mitk::DataNode* node); DataNode* LookForPointerTargetBelowGroupNode(const char* name); DataNode* GetGroupNode(); private: }; } // namespace #endif diff --git a/Modules/MitkExt/Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp b/Modules/Segmentation/Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp rename to Modules/Segmentation/Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp diff --git a/Modules/MitkExt/Algorithms/mitkShapeBasedInterpolationAlgorithm.h b/Modules/Segmentation/Algorithms/mitkShapeBasedInterpolationAlgorithm.h similarity index 93% rename from Modules/MitkExt/Algorithms/mitkShapeBasedInterpolationAlgorithm.h rename to Modules/Segmentation/Algorithms/mitkShapeBasedInterpolationAlgorithm.h index db3d962233..b7ee495b81 100644 --- a/Modules/MitkExt/Algorithms/mitkShapeBasedInterpolationAlgorithm.h +++ b/Modules/Segmentation/Algorithms/mitkShapeBasedInterpolationAlgorithm.h @@ -1,60 +1,60 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 13922 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkShapeBasedInterpolationAlgorithm_h_Included #define mitkShapeBasedInterpolationAlgorithm_h_Included #include "mitkSegmentationInterpolationAlgorithm.h" #include "mitkLegacyAdaptors.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" namespace mitk { /** * \brief Shape-based binary image interpolation. * * This class uses legacy code from ipSegmentation to implement * the shape-based interpolation algorithm described in * * G.T. Herman, J. Zheng, C.A. Bucholtz: "Shape-based interpolation" * IEEE Computer Graphics & Applications, pp. 69-79,May 1992 * * Last contributor: * $Author:$ */ -class MitkExt_EXPORT ShapeBasedInterpolationAlgorithm : public SegmentationInterpolationAlgorithm +class Segmentation_EXPORT ShapeBasedInterpolationAlgorithm : public SegmentationInterpolationAlgorithm { public: mitkClassMacro(ShapeBasedInterpolationAlgorithm, SegmentationInterpolationAlgorithm); itkNewMacro(ShapeBasedInterpolationAlgorithm); Image::Pointer Interpolate(Image::ConstPointer lowerSlice, unsigned int lowerSliceIndex, Image::ConstPointer upperSlice, unsigned int upperSliceIndex, unsigned int requestedIndex, unsigned int sliceDimension, Image::Pointer resultImage, unsigned int timeStep, Image::ConstPointer referenceImage); }; } // namespace #endif diff --git a/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp b/Modules/Segmentation/Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp rename to Modules/Segmentation/Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp diff --git a/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSmoothedSurface.h b/Modules/Segmentation/Algorithms/mitkShowSegmentationAsSmoothedSurface.h similarity index 93% rename from Modules/MitkExt/Algorithms/mitkShowSegmentationAsSmoothedSurface.h rename to Modules/Segmentation/Algorithms/mitkShowSegmentationAsSmoothedSurface.h index d2779109b8..db2e56ae61 100644 --- a/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSmoothedSurface.h +++ b/Modules/Segmentation/Algorithms/mitkShowSegmentationAsSmoothedSurface.h @@ -1,46 +1,46 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITK_SHOW_SEGMENTATION_AS_SMOOTHED_SURFACE_H #define MITK_SHOW_SEGMENTATION_AS_SMOOTHED_SURFACE_H #include "mitkSegmentationSink.h" #include namespace mitk { - class MitkExt_EXPORT ShowSegmentationAsSmoothedSurface : public SegmentationSink + class Segmentation_EXPORT ShowSegmentationAsSmoothedSurface : public SegmentationSink { public: mitkClassMacro(ShowSegmentationAsSmoothedSurface, SegmentationSink) mitkAlgorithmNewMacro(ShowSegmentationAsSmoothedSurface) protected: void Initialize(const NonBlockingAlgorithm *other = NULL); bool ReadyToRun(); bool ThreadedUpdateFunction(); void ThreadedUpdateSuccessful(); private: ShowSegmentationAsSmoothedSurface(); ~ShowSegmentationAsSmoothedSurface(); Surface::Pointer m_Surface; }; } #endif diff --git a/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSurface.cpp b/Modules/Segmentation/Algorithms/mitkShowSegmentationAsSurface.cpp similarity index 100% rename from Modules/MitkExt/Algorithms/mitkShowSegmentationAsSurface.cpp rename to Modules/Segmentation/Algorithms/mitkShowSegmentationAsSurface.cpp diff --git a/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSurface.h b/Modules/Segmentation/Algorithms/mitkShowSegmentationAsSurface.h similarity index 93% rename from Modules/MitkExt/Algorithms/mitkShowSegmentationAsSurface.h rename to Modules/Segmentation/Algorithms/mitkShowSegmentationAsSurface.h index c39ce411e7..68eff5cc40 100644 --- a/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSurface.h +++ b/Modules/Segmentation/Algorithms/mitkShowSegmentationAsSurface.h @@ -1,63 +1,63 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITK_SHOW_SEGMENTATION_AS_SURFACE_H_INCLUDET_WAD #define MITK_SHOW_SEGMENTATION_AS_SURFACE_H_INCLUDET_WAD #include "mitkSegmentationSink.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkUIDGenerator.h" #include "mitkSurface.h" namespace mitk { -class MitkExt_EXPORT ShowSegmentationAsSurface : public SegmentationSink +class Segmentation_EXPORT ShowSegmentationAsSurface : public SegmentationSink { public: mitkClassMacro( ShowSegmentationAsSurface, SegmentationSink ) mitkAlgorithmNewMacro( ShowSegmentationAsSurface ); protected: ShowSegmentationAsSurface(); // use smart pointers virtual ~ShowSegmentationAsSurface(); virtual void Initialize(const NonBlockingAlgorithm* other = NULL); virtual bool ReadyToRun(); virtual bool ThreadedUpdateFunction(); // will be called from a thread after calling StartAlgorithm virtual void ThreadedUpdateSuccessful(); // will be called from a thread after calling StartAlgorithm private: UIDGenerator m_UIDGeneratorSurfaces; Surface::Pointer m_Surface; DataNode::Pointer m_Node; bool m_AddToTree; }; } // namespace #endif diff --git a/Modules/Segmentation/CMakeLists.txt b/Modules/Segmentation/CMakeLists.txt new file mode 100644 index 0000000000..133b39a60c --- /dev/null +++ b/Modules/Segmentation/CMakeLists.txt @@ -0,0 +1,10 @@ +#configure_file(${PROJECT_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in $#{PROJECT_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) +#configure_file(${PROJECT_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in $#{PROJECT_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) +#configure_file(${PROJECT_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in $#{PROJECT_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) + +MITK_CREATE_MODULE( Segmentation + INCLUDE_DIRS Algorithms Controllers DataManagement Interactions IO Rendering + DEPENDS Mitk ipSegmentation mitkIpFunc MitkExt +) + +add_subdirectory(Testing) \ No newline at end of file diff --git a/Modules/MitkExt/Controllers/mitkSegmentationInterpolationController.cpp b/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp similarity index 100% rename from Modules/MitkExt/Controllers/mitkSegmentationInterpolationController.cpp rename to Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp diff --git a/Modules/MitkExt/Controllers/mitkSegmentationInterpolationController.h b/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.h similarity index 97% rename from Modules/MitkExt/Controllers/mitkSegmentationInterpolationController.h rename to Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.h index 0e8a95d503..b4d3a5a8aa 100644 --- a/Modules/MitkExt/Controllers/mitkSegmentationInterpolationController.h +++ b/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.h @@ -1,203 +1,203 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkSegmentationInterpolationController_h_Included #define mitkSegmentationInterpolationController_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkImage.h" #include #include #include #include namespace mitk { class Image; /** \brief Generates interpolations of 2D slices. \sa QmitkSlicesInterpolator \sa QmitkInteractiveSegmentation \ingroup ToolManagerEtAl There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage This class keeps track of the contents of a 3D segmentation image. \attention mitk::SegmentationInterpolationController assumes that the image contains pixel values of 0 and 1. After you set the segmentation image using SetSegmentationVolume(), the whole image is scanned for pixels other than 0. SegmentationInterpolationController registers as an observer to the segmentation image, and repeats the scan whenvever the image is modified. You can prevent this (time consuming) scan if you do the changes slice-wise and send difference images to SegmentationInterpolationController. For this purpose SetChangedSlice() should be used. mitk::OverwriteImageFilter already does this every time it changes a slice of an image. There is a static method InterpolatorForImage(), which can be used to find out if there already is an interpolator instance for a specified image. OverwriteImageFilter uses this to get to know its interpolator. SegmentationInterpolationController needs to maintain some information about the image slices (in every dimension). This information is stored internally in m_SegmentationCountInSlice, which is basically three std::vectors (one for each dimension). Each item describes one image dimension, each vector item holds the count of pixels in "its" slice. This is perhaps better to understand from the following picture (where red items just mean to symbolize "there is some segmentation" - in reality there is an integer count). \image html slice_based_segmentation_interpolator.png $Author$ */ -class MitkExt_EXPORT SegmentationInterpolationController : public itk::Object +class Segmentation_EXPORT SegmentationInterpolationController : public itk::Object { public: mitkClassMacro(SegmentationInterpolationController, itk::Object); itkNewMacro(SegmentationInterpolationController); /// specify the segmentation image that should be interpolated /** \brief Find interpolator for a given image. \return NULL if there is no interpolator yet. This method is useful if several "clients" modify the same image and want to access the interpolations. Then they can share the same object. */ static SegmentationInterpolationController* InterpolatorForImage(const Image*); /** \brief Block reaction to an images Modified() events. Blocking the scan of the whole image is especially useful when you are about to change a single slice of the image. Then you would send a difference image of this single slice to SegmentationInterpolationController but call image->Modified() anyway. Before calling image->Modified() you should block SegmentationInterpolationController's reactions to this modified by using this method. */ void BlockModified(bool); /** \brief Initialize with a whole volume. Will scan the volume for segmentation pixels (values other than 0) and fill some internal data structures. You don't have to call this method every time something changes, but only when several slices at once change. When you change a single slice, call SetChangedSlice() instead. */ void SetSegmentationVolume( const Image* segmentation ); /** \brief Set a reference image (original patient image) - optional. If this volume is set (must exactly match the dimensions of the segmentation), the interpolation algorithm may consider image content to improve the interpolated (estimated) segmentation. */ void SetReferenceVolume( const Image* segmentation ); /** \brief Update after changing a single slice. \param sliceDiff is a 2D image with the difference image of the slice determined by sliceDimension and sliceIndex. The difference is (pixel value in the new slice minus pixel value in the old slice). \param sliceDimension Number of the dimension which is constant for all pixels of the meant slice. \param sliceIndex Which slice to take, in the direction specified by sliceDimension. Count starts from 0. \param timeStep Which time step is changed */ void SetChangedSlice( const Image* sliceDiff, unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ); void SetChangedVolume( const Image* sliceDiff, unsigned int timeStep ); /** \brief Generates an interpolated image for the given slice. \param sliceDimension Number of the dimension which is constant for all pixels of the meant slice. \param sliceIndex Which slice to take, in the direction specified by sliceDimension. Count starts from 0. \param timeStep Which time step to use */ Image::Pointer Interpolate( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ); void OnImageModified(const itk::EventObject&); protected: /** \brief Protected class of mitk::SegmentationInterpolationController. Don't use (you shouldn't be able to do so)! */ - class MitkExt_EXPORT SetChangedSliceOptions + class Segmentation_EXPORT SetChangedSliceOptions { public: SetChangedSliceOptions( unsigned int sd, unsigned int si, unsigned int d0, unsigned int d1, unsigned int t, void* pixels ) : sliceDimension(sd), sliceIndex(si), dim0(d0), dim1(d1), timeStep(t), pixelData(pixels) { } unsigned int sliceDimension; unsigned int sliceIndex; unsigned int dim0; unsigned int dim1; unsigned int timeStep; void* pixelData; }; typedef std::vector DirtyVectorType; //typedef std::vector< DirtyVectorType[3] > TimeResolvedDirtyVectorType; // cannot work with C++, so next line is used for implementation typedef std::vector< std::vector > TimeResolvedDirtyVectorType; typedef std::map< const Image*, SegmentationInterpolationController* > InterpolatorMapType; SegmentationInterpolationController();// purposely hidden virtual ~SegmentationInterpolationController(); /// internal scan of a single slice template < typename DATATYPE > void ScanChangedSlice( itk::Image*, const SetChangedSliceOptions& options ); template < typename TPixel, unsigned int VImageDimension > void ScanChangedVolume( itk::Image*, unsigned int timeStep ); template < typename DATATYPE > void ScanWholeVolume( itk::Image*, const Image* volume, unsigned int timeStep ); void PrintStatus(); /** An array of flags. One for each dimension of the image. A flag is set, when a slice in a certain dimension has at least one pixel that is not 0 (which would mean that it has to be considered by the interpolation algorithm). E.g. flags for transversal slices are stored in m_SegmentationCountInSlice[0][index]. Enhanced with time steps it is now m_SegmentationCountInSlice[timeStep][0][index] */ TimeResolvedDirtyVectorType m_SegmentationCountInSlice; static InterpolatorMapType s_InterpolatorForImage; Image::ConstPointer m_Segmentation; Image::ConstPointer m_ReferenceImage; bool m_BlockModified; }; } // namespace #endif diff --git a/Modules/MitkExt/Controllers/mitkSurfaceInterpolationController.cpp b/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.cpp similarity index 100% rename from Modules/MitkExt/Controllers/mitkSurfaceInterpolationController.cpp rename to Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.cpp diff --git a/Modules/MitkExt/Controllers/mitkSurfaceInterpolationController.h b/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.h similarity index 96% rename from Modules/MitkExt/Controllers/mitkSurfaceInterpolationController.h rename to Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.h index 583eca813f..329cc693de 100644 --- a/Modules/MitkExt/Controllers/mitkSurfaceInterpolationController.h +++ b/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.h @@ -1,132 +1,132 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkSurfaceInterpolationController_h_Included #define mitkSurfaceInterpolationController_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkSurface.h" #include "mitkInteractionConst.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkCreateDistanceImageFromSurfaceFilter.h" #include "mitkReduceContourSetFilter.h" #include "mitkComputeContourSetNormalsFilter.h" #include "mitkDataNode.h" #include "mitkDataStorage.h" #include "mitkWeakPointer.h" #include "vtkPolygon.h" #include "vtkPoints.h" #include "vtkCellArray.h" #include "vtkPolyData.h" #include "vtkSmartPointer.h" #include "vtkAppendPolyData.h" #include "vtkMarchingCubes.h" #include "vtkImageData.h" #include "mitkVtkRepresentationProperty.h" #include "vtkProperty.h" #include "mitkProgressBar.h" namespace mitk { - class MitkExt_EXPORT SurfaceInterpolationController : public itk::Object + class Segmentation_EXPORT SurfaceInterpolationController : public itk::Object { public: mitkClassMacro(SurfaceInterpolationController, itk::Object); itkNewMacro(Self); static SurfaceInterpolationController* GetInstance(); /* * Adds a new extracted contour to the list */ void AddNewContour(Surface::Pointer newContour, RestorePlanePositionOperation *op); /* * Interpolates the 3D surface from the given extracted contours */ void Interpolate (); mitk::Surface::Pointer GetInterpolationResult(); void SetMinSpacing(double minSpacing); void SetMaxSpacing(double maxSpacing); void SetDistanceImageVolume(unsigned int distImageVolume); void SetWorkingImage(Image* workingImage); Surface* GetContoursAsSurface(); void SetDataStorage(DataStorage &ds); unsigned int CreateNewContourList(); void SetCurrentListID (unsigned int ID); mitk::Image* GetImage(); protected: SurfaceInterpolationController(); ~SurfaceInterpolationController(); private: struct ContourPositionPair { Surface::Pointer contour; RestorePlanePositionOperation* position; }; typedef std::vector ContourPositionPairList; ContourPositionPairList::iterator m_Iterator; ReduceContourSetFilter::Pointer m_ReduceFilter; ComputeContourSetNormalsFilter::Pointer m_NormalsFilter; CreateDistanceImageFromSurfaceFilter::Pointer m_InterpolateSurfaceFilter; double m_MinSpacing; double m_MaxSpacing; const Image* m_WorkingImage; Surface::Pointer m_Contours; vtkSmartPointer m_PolyData; unsigned int m_DistImageVolume; mitk::WeakPointer m_DataStorage; std::vector m_ListOfContourLists; unsigned int m_CurrentContourListID; mitk::Surface::Pointer m_InterpolationResult; }; } #endif diff --git a/Modules/MitkExt/DataManagement/mitkContour.cpp b/Modules/Segmentation/DataManagement/mitkContour.cpp similarity index 100% rename from Modules/MitkExt/DataManagement/mitkContour.cpp rename to Modules/Segmentation/DataManagement/mitkContour.cpp diff --git a/Modules/MitkExt/DataManagement/mitkContour.h b/Modules/Segmentation/DataManagement/mitkContour.h similarity index 97% rename from Modules/MitkExt/DataManagement/mitkContour.h rename to Modules/Segmentation/DataManagement/mitkContour.h index 529536bfc1..99d0a8c143 100644 --- a/Modules/MitkExt/DataManagement/mitkContour.h +++ b/Modules/Segmentation/DataManagement/mitkContour.h @@ -1,183 +1,183 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _MITK_CONTOUR_H_ #define _MITK_CONTOUR_H_ #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkBaseData.h" #include #include namespace mitk { /** * This class holds stores vertices for drawing a contour * */ -class MitkExt_EXPORT Contour : public BaseData +class Segmentation_EXPORT Contour : public BaseData { public: mitkClassMacro(Contour, BaseData); itkNewMacro(Self); mitkCloneMacro(Contour); typedef itk::PolyLineParametricPath<3> PathType; typedef PathType::Pointer PathPointer; typedef PathType::ContinuousIndexType ContinuousIndexType; typedef PathType::InputType InputType; typedef PathType::OutputType OutputType; typedef PathType::OffsetType OffsetType; typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::PointsContainer PointsContainer; typedef BoundingBoxType::PointsContainer::Pointer PointsContainerPointer; typedef BoundingBoxType::PointsContainerIterator PointsContainerIterator; /** * sets whether the contour should be closed or open. * by default the contour is closed */ itkSetMacro(Closed, bool); /** * returns if the contour is closed or opened */ itkGetMacro(Closed, bool); itkSetMacro(Selected, bool); itkGetMacro(Selected, bool); itkSetMacro(Width, float); itkGetMacro(Width, float); /** * clean up the contour data */ void Initialize(); /** * add a new vertex to the contour */ void AddVertex(mitk::Point3D newPoint); /** * return an itk parametric path of the contour */ PathPointer GetContourPath() const; /** * set the current render window. This is helpful if one * wants to draw the contour in one special window only. */ void SetCurrentWindow(vtkRenderWindow* rw); /** * returns the points to the current render window */ vtkRenderWindow* GetCurrentWindow() const; /** * returns the number of points stored in the contour */ unsigned int GetNumberOfPoints() const; /** * returns the container of the contour points */ PointsContainerPointer GetPoints() const; /** * set the contour points container. */ void SetPoints(PointsContainerPointer points); /** * intherited from parent */ virtual void UpdateOutputInformation(); /** * intherited from parent */ virtual void SetRequestedRegionToLargestPossibleRegion(); /** * intherited from parent */ virtual bool RequestedRegionIsOutsideOfTheBufferedRegion(); /** * intherited from parent */ virtual bool VerifyRequestedRegion(); /** * intherited from parent */ virtual void SetRequestedRegion(itk::DataObject *data); protected: Contour(); Contour(const Contour & other); virtual ~Contour(); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; private: /** * parametric path of a contour; */ PathType::Pointer m_ContourPath; /** * the current render window */ vtkRenderWindow* m_CurrentWindow; /** * the bounding box of the contour */ BoundingBoxType::Pointer m_BoundingBox; /** * container for all contour points */ BoundingBoxType::PointsContainer::Pointer m_Vertices; /** * decide whether th contour is open or closed */ bool m_Closed; bool m_Selected; float m_Width; }; } // namespace mitk #endif //_MITK_CONTOUR_H_ diff --git a/Modules/MitkExt/DataManagement/mitkContourSet.cpp b/Modules/Segmentation/DataManagement/mitkContourSet.cpp similarity index 100% rename from Modules/MitkExt/DataManagement/mitkContourSet.cpp rename to Modules/Segmentation/DataManagement/mitkContourSet.cpp diff --git a/Modules/MitkExt/DataManagement/mitkContourSet.h b/Modules/Segmentation/DataManagement/mitkContourSet.h similarity index 96% rename from Modules/MitkExt/DataManagement/mitkContourSet.h rename to Modules/Segmentation/DataManagement/mitkContourSet.h index 6e95508462..408d3ed970 100644 --- a/Modules/MitkExt/DataManagement/mitkContourSet.h +++ b/Modules/Segmentation/DataManagement/mitkContourSet.h @@ -1,115 +1,115 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _MITK_CONTOUR_SET_H_ #define _MITK_CONTOUR_SET_H_ #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkBaseData.h" #include "mitkContour.h" #include namespace mitk { /** * This class holds stores vertices for drawing a contour * */ -class MitkExt_EXPORT ContourSet : public BaseData +class Segmentation_EXPORT ContourSet : public BaseData { public: mitkClassMacro(ContourSet, BaseData); itkNewMacro(Self); typedef std::map ContourVectorType; typedef ContourVectorType::iterator ContourIterator; typedef itk::BoundingBox BoundingBoxType; /** * clean up the contour data */ void Initialize(); /** * add a contour */ void AddContour(unsigned int index, mitk::Contour::Pointer contour); /** * add a contour */ void RemoveContour(unsigned long index); /** * returns the number of points stored in the contour */ unsigned int GetNumberOfContours(); /** * returns the container of the contour points */ ContourVectorType GetContours(); /** * intherited from parent */ virtual void UpdateOutputInformation(); /** * intherited from parent */ virtual void SetRequestedRegionToLargestPossibleRegion(); /** * intherited from parent */ virtual bool RequestedRegionIsOutsideOfTheBufferedRegion(); /** * intherited from parent */ virtual bool VerifyRequestedRegion(); /** * intherited from parent */ virtual void SetRequestedRegion(itk::DataObject *data); protected: ContourSet(); virtual ~ContourSet(); private: /** * the bounding box of the contour */ BoundingBoxType::Pointer m_BoundingBox; ContourVectorType m_ContourVector; unsigned int m_NumberOfContours; }; } // namespace mitk #endif //_MITK_CONTOUR_SET_H_ diff --git a/Modules/MitkExt/DataManagement/mitkExtrudedContour.cpp b/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp similarity index 100% rename from Modules/MitkExt/DataManagement/mitkExtrudedContour.cpp rename to Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp diff --git a/Modules/MitkExt/DataManagement/mitkExtrudedContour.h b/Modules/Segmentation/DataManagement/mitkExtrudedContour.h similarity index 97% rename from Modules/MitkExt/DataManagement/mitkExtrudedContour.h rename to Modules/Segmentation/DataManagement/mitkExtrudedContour.h index 77854f5438..fcd5bb3266 100644 --- a/Modules/MitkExt/DataManagement/mitkExtrudedContour.h +++ b/Modules/Segmentation/DataManagement/mitkExtrudedContour.h @@ -1,127 +1,127 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITKEXTRUDEDCONTOUR_H_HEADER_INCLUDED #define MITKEXTRUDEDCONTOUR_H_HEADER_INCLUDED #include "mitkBoundingObject.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include #include #include class vtkLinearExtrusionFilter; class vtkPlanes; class vtkClipPolyData; class vtkLinearSubdivisionFilter; class vtkTriangleFilter; class vtkDecimatePro; class vtkPolygon; namespace mitk { //##Documentation //## @brief Data class containing a bounding-object created by //## extruding a Contour along a vector //## //## The m_Contour is extruded in the direction m_Vector until //## reaching m_ClippingGeometry. //## @ingroup Data -class MitkExt_EXPORT ExtrudedContour : public BoundingObject +class Segmentation_EXPORT ExtrudedContour : public BoundingObject { public: mitkClassMacro(ExtrudedContour, BoundingObject); itkNewMacro(Self); virtual mitk::ScalarType GetVolume(); virtual bool IsInside(const Point3D& p) const; virtual void UpdateOutputInformation(); //##Documentation //## @brief Contour to extrude itkGetConstObjectMacro(Contour, mitk::Contour); itkSetObjectMacro(Contour, mitk::Contour); //##Documentation //## @brief Vector to specify the direction of the extrusion mitkGetVectorMacro(Vector, mitk::Vector3D); mitkSetVectorMacro(Vector, mitk::Vector3D); itkGetConstMacro(AutomaticVectorGeneration, bool); itkSetMacro(AutomaticVectorGeneration, bool); itkBooleanMacro(AutomaticVectorGeneration); //##Documentation //## @brief Optional vector to specify the orientation of the bounding-box mitkGetVectorMacro(RightVector, mitk::Vector3D); mitkSetVectorMacro(RightVector, mitk::Vector3D); //##Documentation //## @brief Optional geometry for clipping the extruded contour itkGetConstObjectMacro(ClippingGeometry, mitk::Geometry3D); itkSetObjectMacro(ClippingGeometry, mitk::Geometry3D); virtual unsigned long GetMTime() const; protected: ExtrudedContour(); virtual ~ExtrudedContour(); void BuildSurface(); void BuildGeometry(); mitk::Contour::Pointer m_Contour; mitk::Vector3D m_Vector; mitk::Vector3D m_RightVector; mitk::Geometry3D::Pointer m_ClippingGeometry; bool m_AutomaticVectorGeneration; vtkPolygon* m_Polygon; #if ((VTK_MAJOR_VERSION > 4) || ((VTK_MAJOR_VERSION==4) && (VTK_MINOR_VERSION>=4) )) double m_ProjectedContourBounds[6]; #else float m_ProjectedContourBounds[6]; #endif mitk::PlaneGeometry::Pointer m_ProjectionPlane; //##Documentation //## @brief For fast projection on plane float m_Right[3]; float m_Down[3]; #if ((VTK_MAJOR_VERSION > 4) || ((VTK_MAJOR_VERSION==4) && (VTK_MINOR_VERSION>=4) )) double m_Normal[3]; #else float m_Normal[3]; #endif float m_Origin[3]; vtkLinearExtrusionFilter* m_ExtrusionFilter; vtkTriangleFilter *m_TriangleFilter; vtkDecimatePro* m_Decimate; vtkLinearSubdivisionFilter* m_SubdivisionFilter; vtkPlanes* m_ClippingBox; vtkClipPolyData* m_ClipPolyDataFilter; itk::TimeStamp m_LastCalculateExtrusionTime; }; } #endif /* MITKEXTRUDEDCONTOUR_H_HEADER_INCLUDED */ diff --git a/Modules/MitkExt/Interactions/mitkAddContourTool.cpp b/Modules/Segmentation/Interactions/mitkAddContourTool.cpp similarity index 93% rename from Modules/MitkExt/Interactions/mitkAddContourTool.cpp rename to Modules/Segmentation/Interactions/mitkAddContourTool.cpp index b31a53b7f0..8c5b076001 100644 --- a/Modules/MitkExt/Interactions/mitkAddContourTool.cpp +++ b/Modules/Segmentation/Interactions/mitkAddContourTool.cpp @@ -1,44 +1,44 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkAddContourTool.h" #include "mitkAddContourTool.xpm" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, AddContourTool, "Add tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, AddContourTool, "Add tool"); } mitk::AddContourTool::AddContourTool() :ContourTool(1) { } mitk::AddContourTool::~AddContourTool() { } const char** mitk::AddContourTool::GetXPM() const { return mitkAddContourTool_xpm; } const char* mitk::AddContourTool::GetName() const { return "Add"; } diff --git a/Modules/MitkExt/Interactions/mitkAddContourTool.h b/Modules/Segmentation/Interactions/mitkAddContourTool.h similarity index 94% rename from Modules/MitkExt/Interactions/mitkAddContourTool.h rename to Modules/Segmentation/Interactions/mitkAddContourTool.h index cb31dc4dae..127142b020 100644 --- a/Modules/MitkExt/Interactions/mitkAddContourTool.h +++ b/Modules/Segmentation/Interactions/mitkAddContourTool.h @@ -1,67 +1,67 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkAddContourTool_h_Included #define mitkAddContourTool_h_Included #include "mitkContourTool.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" namespace mitk { /** \brief Fill the inside of a contour with 1 \sa ContourTool \ingroup Interaction \ingroup ToolManagerEtAl Fills a visible contour (from FeedbackContourTool) during mouse dragging. When the mouse button is released, AddContourTool tries to extract a slice from the working image and fill in the (filled) contour as a binary image. All inside pixels are set to 1. While holding the CTRL key, the contour changes color and the pixels on the inside would be filled with 0. \warning Only to be instantiated by mitk::ToolManager. $Author$ */ -class MitkExt_EXPORT AddContourTool : public ContourTool +class Segmentation_EXPORT AddContourTool : public ContourTool { public: mitkClassMacro(AddContourTool, ContourTool); itkNewMacro(AddContourTool); virtual const char** GetXPM() const; virtual const char* GetName() const; protected: AddContourTool(); // purposely hidden virtual ~AddContourTool(); }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkAddContourTool.xpm b/Modules/Segmentation/Interactions/mitkAddContourTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkAddContourTool.xpm rename to Modules/Segmentation/Interactions/mitkAddContourTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkAutoCropTool.cpp b/Modules/Segmentation/Interactions/mitkAutoCropTool.cpp similarity index 96% rename from Modules/MitkExt/Interactions/mitkAutoCropTool.cpp rename to Modules/Segmentation/Interactions/mitkAutoCropTool.cpp index 3ec2e6821a..2db0472109 100644 --- a/Modules/MitkExt/Interactions/mitkAutoCropTool.cpp +++ b/Modules/Segmentation/Interactions/mitkAutoCropTool.cpp @@ -1,89 +1,89 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkAutoCropTool.h" #include "mitkAutoCropTool.xpm" #include "mitkAutoCropImageFilter.h" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, AutoCropImageFilter, "Crop tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, AutoCropImageFilter, "Crop tool"); } mitk::AutoCropTool::AutoCropTool() { } mitk::AutoCropTool::~AutoCropTool() { } const char** mitk::AutoCropTool::GetXPM() const { return mitkAutoCropTool_xpm; } const char* mitk::AutoCropTool::GetName() const { return "Crop"; } std::string mitk::AutoCropTool::GetErrorMessage() { return "Cropping of these nodes failed:"; } bool mitk::AutoCropTool::ProcessOneWorkingData( DataNode* node ) { if (node) { Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNull()) return false; // if (image->GetDimension() == 4) // { // Tool::ErrorMessage.Send("Cropping 3D+t segmentations is not implemented. Sorry. Bug #1281"); // return false; // } AutoCropImageFilter::Pointer cropFilter = AutoCropImageFilter::New(); cropFilter->SetInput( image ); cropFilter->SetBackgroundValue( 0 ); try { cropFilter->Update(); image = cropFilter->GetOutput(); if (image.IsNotNull()) { node->SetData( image ); } else { return false; } } catch(...) { return false; } } return true; } diff --git a/Modules/MitkExt/Interactions/mitkAutoCropTool.h b/Modules/Segmentation/Interactions/mitkAutoCropTool.h similarity index 92% rename from Modules/MitkExt/Interactions/mitkAutoCropTool.h rename to Modules/Segmentation/Interactions/mitkAutoCropTool.h index ecd936078f..a223b6c39a 100644 --- a/Modules/MitkExt/Interactions/mitkAutoCropTool.h +++ b/Modules/Segmentation/Interactions/mitkAutoCropTool.h @@ -1,59 +1,59 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.0 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkAutoCropTool_h_Included #define mitkAutoCropTool_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkSegmentationsProcessingTool.h" namespace mitk { /** \brief Crops selected segmentations. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation Last contributor: $Author$ */ -class MitkExt_EXPORT AutoCropTool : public SegmentationsProcessingTool +class Segmentation_EXPORT AutoCropTool : public SegmentationsProcessingTool { public: mitkClassMacro(AutoCropTool, SegmentationsProcessingTool); itkNewMacro(AutoCropTool); virtual const char** GetXPM() const; virtual const char* GetName() const; protected: virtual bool ProcessOneWorkingData( DataNode* node ); virtual std::string GetErrorMessage(); AutoCropTool(); // purposely hidden virtual ~AutoCropTool(); }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkAutoCropTool.xpm b/Modules/Segmentation/Interactions/mitkAutoCropTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkAutoCropTool.xpm rename to Modules/Segmentation/Interactions/mitkAutoCropTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkAutoSegmentationTool.cpp b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp similarity index 100% rename from Modules/MitkExt/Interactions/mitkAutoSegmentationTool.cpp rename to Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp diff --git a/Modules/MitkExt/Interactions/mitkAutoSegmentationTool.h b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h similarity index 92% rename from Modules/MitkExt/Interactions/mitkAutoSegmentationTool.h rename to Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h index 8cdc853940..72c97c441c 100644 --- a/Modules/MitkExt/Interactions/mitkAutoSegmentationTool.h +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h @@ -1,50 +1,50 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.0 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkAutoSegmentationTool_h_Included #define mitkAutoSegmentationTool_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkTool.h" namespace mitk { /** \brief Superclass for tool that create a new segmentation without user interaction in render windows This class is undocumented. Ask the creator ($Author$) to supply useful comments. */ -class MitkExt_EXPORT AutoSegmentationTool : public Tool +class Segmentation_EXPORT AutoSegmentationTool : public Tool { public: mitkClassMacro(AutoSegmentationTool, Tool); protected: AutoSegmentationTool(); // purposely hidden virtual ~AutoSegmentationTool(); virtual const char* GetGroup() const; }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkBinaryThresholdTool.cpp b/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.cpp similarity index 99% rename from Modules/MitkExt/Interactions/mitkBinaryThresholdTool.cpp rename to Modules/Segmentation/Interactions/mitkBinaryThresholdTool.cpp index 715e485fcd..94a70f072c 100644 --- a/Modules/MitkExt/Interactions/mitkBinaryThresholdTool.cpp +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.cpp @@ -1,313 +1,313 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkBinaryThresholdTool.h" #include "mitkBinaryThresholdTool.xpm" #include "mitkToolManager.h" #include "mitkBoundingObjectToSegmentationFilter.h" #include #include "mitkLevelWindowProperty.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkOrganTypeProperty.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkDataStorage.h" #include "mitkRenderingManager.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" #include #include #include "mitkPadImageFilter.h" #include "mitkMaskAndCutRoiImageFilter.h" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, BinaryThresholdTool, "Thresholding tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, BinaryThresholdTool, "Thresholding tool"); } mitk::BinaryThresholdTool::BinaryThresholdTool() :m_SensibleMinimumThresholdValue(-100), m_SensibleMaximumThresholdValue(+100), m_CurrentThresholdValue(0.0), m_IsFloatImage(false) { this->SupportRoiOn(); m_ThresholdFeedbackNode = DataNode::New(); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties( m_ThresholdFeedbackNode ); m_ThresholdFeedbackNode->SetProperty( "color", ColorProperty::New(1.0, 0.0, 0.0) ); m_ThresholdFeedbackNode->SetProperty( "texture interpolation", BoolProperty::New(false) ); m_ThresholdFeedbackNode->SetProperty( "layer", IntProperty::New( 100 ) ); m_ThresholdFeedbackNode->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow(100, 1) ) ); m_ThresholdFeedbackNode->SetProperty( "name", StringProperty::New("Thresholding feedback") ); m_ThresholdFeedbackNode->SetProperty( "opacity", FloatProperty::New(0.3) ); m_ThresholdFeedbackNode->SetProperty( "helper object", BoolProperty::New(true) ); } mitk::BinaryThresholdTool::~BinaryThresholdTool() { } const char** mitk::BinaryThresholdTool::GetXPM() const { return mitkBinaryThresholdTool_xpm; } const char* mitk::BinaryThresholdTool::GetName() const { return "Thresholding"; } void mitk::BinaryThresholdTool::Activated() { m_ToolManager->RoiDataChanged += mitk::MessageDelegate(this, &mitk::BinaryThresholdTool::OnRoiDataChanged); m_OriginalImageNode = m_ToolManager->GetReferenceData(0); m_NodeForThresholding = m_OriginalImageNode; if ( m_NodeForThresholding.IsNotNull() ) { SetupPreviewNodeFor( m_NodeForThresholding ); } else { m_ToolManager->ActivateTool(-1); } } void mitk::BinaryThresholdTool::Deactivated() { m_ToolManager->RoiDataChanged -= mitk::MessageDelegate(this, &mitk::BinaryThresholdTool::OnRoiDataChanged); m_NodeForThresholding = NULL; m_OriginalImageNode = NULL; try { if (DataStorage* storage = m_ToolManager->GetDataStorage()) { storage->Remove( m_ThresholdFeedbackNode ); RenderingManager::GetInstance()->RequestUpdateAll(); } } catch(...) { // don't care } m_ThresholdFeedbackNode->SetData(NULL); } void mitk::BinaryThresholdTool::SetThresholdValue(double value) { if (m_ThresholdFeedbackNode.IsNotNull()) { m_CurrentThresholdValue = value; m_ThresholdFeedbackNode->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow(m_CurrentThresholdValue, 1) ) ); RenderingManager::GetInstance()->RequestUpdateAll(); } } void mitk::BinaryThresholdTool::AcceptCurrentThresholdValue(const std::string& organName, const Color& color) { CreateNewSegmentationFromThreshold(m_NodeForThresholding, organName, color ); RenderingManager::GetInstance()->RequestUpdateAll(); m_ToolManager->ActivateTool(-1); } void mitk::BinaryThresholdTool::CancelThresholding() { m_ToolManager->ActivateTool(-1); } void mitk::BinaryThresholdTool::SetupPreviewNodeFor( DataNode* nodeForThresholding ) { if (nodeForThresholding) { Image::Pointer image = dynamic_cast( nodeForThresholding->GetData() ); Image::Pointer originalImage = dynamic_cast (m_OriginalImageNode->GetData()); if (image.IsNotNull()) { // initialize and a new node with the same image as our reference image // use the level window property of this image copy to display the result of a thresholding operation m_ThresholdFeedbackNode->SetData( image ); int layer(50); nodeForThresholding->GetIntProperty("layer", layer); m_ThresholdFeedbackNode->SetIntProperty("layer", layer+1); if (DataStorage* storage = m_ToolManager->GetDataStorage()) { if (storage->Exists(m_ThresholdFeedbackNode)) storage->Remove(m_ThresholdFeedbackNode); storage->Add( m_ThresholdFeedbackNode, m_OriginalImageNode ); } if (image.GetPointer() == originalImage.GetPointer()) { if (originalImage->GetPixelType().GetPixelTypeId() == typeid(float)) m_IsFloatImage = true; else m_IsFloatImage = false; m_SensibleMinimumThresholdValue = static_cast( originalImage->GetScalarValueMin() ); m_SensibleMaximumThresholdValue = static_cast( originalImage->GetScalarValueMax() ); } LevelWindowProperty::Pointer lwp = dynamic_cast( m_ThresholdFeedbackNode->GetProperty( "levelwindow" )); if (lwp && !m_IsFloatImage ) { m_CurrentThresholdValue = static_cast( lwp->GetLevelWindow().GetLevel() ); } else { m_CurrentThresholdValue = (m_SensibleMaximumThresholdValue + m_SensibleMinimumThresholdValue)/2; } IntervalBordersChanged.Send(m_SensibleMinimumThresholdValue, m_SensibleMaximumThresholdValue, m_IsFloatImage); ThresholdingValueChanged.Send(m_CurrentThresholdValue); } } } void mitk::BinaryThresholdTool::CreateNewSegmentationFromThreshold(DataNode* node, const std::string& organName, const Color& color) { if (node) { Image::Pointer image = dynamic_cast( m_NodeForThresholding->GetData() ); if (image.IsNotNull()) { // create a new image of the same dimensions and smallest possible pixel type DataNode::Pointer emptySegmentation = Tool::CreateEmptySegmentationNode( image, organName, color ); if (emptySegmentation) { // actually perform a thresholding and ask for an organ type for (unsigned int timeStep = 0; timeStep < image->GetTimeSteps(); ++timeStep) { try { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( image ); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); Image::Pointer image3D = timeSelector->GetOutput(); AccessFixedDimensionByItk_2( image3D, ITKThresholding, 3, dynamic_cast(emptySegmentation->GetData()), timeStep ); } catch(...) { Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation."); } } if (m_OriginalImageNode.GetPointer() != m_NodeForThresholding.GetPointer()) { mitk::PadImageFilter::Pointer padFilter = mitk::PadImageFilter::New(); padFilter->SetInput(0, dynamic_cast (emptySegmentation->GetData())); padFilter->SetInput(1, dynamic_cast (m_OriginalImageNode->GetData())); padFilter->SetBinaryFilter(true); padFilter->SetUpperThreshold(1); padFilter->SetLowerThreshold(1); padFilter->Update(); emptySegmentation->SetData(padFilter->GetOutput()); } if (DataStorage::Pointer storage = m_ToolManager->GetDataStorage()) { storage->Add( emptySegmentation, m_OriginalImageNode ); // add as a child, because the segmentation "derives" from the original } m_ToolManager->SetWorkingData( emptySegmentation ); } } } } template void mitk::BinaryThresholdTool::ITKThresholding( itk::Image* originalImage, Image* segmentation, unsigned int timeStep ) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( segmentation ); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); Image::Pointer segmentation3D = timeSelector->GetOutput(); typedef itk::Image< Tool::DefaultSegmentationDataType, 3> SegmentationType; // this is sure for new segmentations SegmentationType::Pointer itkSegmentation; CastToItkImage( segmentation3D, itkSegmentation ); // iterate over original and segmentation typedef itk::ImageRegionConstIterator< itk::Image > InputIteratorType; typedef itk::ImageRegionIterator< SegmentationType > SegmentationIteratorType; InputIteratorType inputIterator( originalImage, originalImage->GetLargestPossibleRegion() ); SegmentationIteratorType outputIterator( itkSegmentation, itkSegmentation->GetLargestPossibleRegion() ); inputIterator.GoToBegin(); outputIterator.GoToBegin(); while (!outputIterator.IsAtEnd()) { if ( inputIterator.Get() >= m_CurrentThresholdValue ) outputIterator.Set( 1 ); else outputIterator.Set( 0 ); ++inputIterator; ++outputIterator; } } void mitk::BinaryThresholdTool::OnRoiDataChanged() { mitk::DataNode::Pointer node = m_ToolManager->GetRoiData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast (m_NodeForThresholding->GetData()); if (image.IsNull()) return; mitk::MaskAndCutRoiImageFilter::Pointer roiFilter = mitk::MaskAndCutRoiImageFilter::New(); roiFilter->SetInput(image); roiFilter->SetRegionOfInterest(node->GetData()); roiFilter->Update(); mitk::DataNode::Pointer tmpNode = mitk::DataNode::New(); mitk::Image::Pointer tmpImage = roiFilter->GetOutput(); tmpNode->SetData(tmpImage); m_SensibleMaximumThresholdValue = static_cast (roiFilter->GetMaxValue()); m_SensibleMinimumThresholdValue = static_cast (roiFilter->GetMinValue()); SetupPreviewNodeFor( tmpNode ); m_NodeForThresholding = tmpNode; return; } else { this->SetupPreviewNodeFor(m_OriginalImageNode); m_NodeForThresholding = m_OriginalImageNode; return; } } diff --git a/Modules/MitkExt/Interactions/mitkBinaryThresholdTool.h b/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.h similarity index 95% rename from Modules/MitkExt/Interactions/mitkBinaryThresholdTool.h rename to Modules/Segmentation/Interactions/mitkBinaryThresholdTool.h index c13f3e58e2..a85541d4c0 100644 --- a/Modules/MitkExt/Interactions/mitkBinaryThresholdTool.h +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.h @@ -1,89 +1,89 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.0 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkBinaryThresholdTool_h_Included #define mitkBinaryThresholdTool_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkAutoSegmentationTool.h" #include "mitkDataNode.h" #include namespace mitk { /** \brief Calculates the segmented volumes for binary images. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation Last contributor: $Author$ */ - class MitkExt_EXPORT BinaryThresholdTool : public AutoSegmentationTool + class Segmentation_EXPORT BinaryThresholdTool : public AutoSegmentationTool { public: Message3 IntervalBordersChanged; Message1 ThresholdingValueChanged; mitkClassMacro(BinaryThresholdTool, AutoSegmentationTool); itkNewMacro(BinaryThresholdTool); virtual const char** GetXPM() const; virtual const char* GetName() const; virtual void Activated(); virtual void Deactivated(); virtual void SetThresholdValue(double value); virtual void AcceptCurrentThresholdValue(const std::string& organName, const Color& color); virtual void CancelThresholding(); protected: BinaryThresholdTool(); // purposely hidden virtual ~BinaryThresholdTool(); void SetupPreviewNodeFor( DataNode* nodeForThresholding ); void CreateNewSegmentationFromThreshold(DataNode* node, const std::string& organType, const Color& color); void OnRoiDataChanged(); template void ITKThresholding( itk::Image* originalImage, mitk::Image* segmentation, unsigned int timeStep ); DataNode::Pointer m_ThresholdFeedbackNode; DataNode::Pointer m_OriginalImageNode; DataNode::Pointer m_NodeForThresholding; double m_SensibleMinimumThresholdValue; double m_SensibleMaximumThresholdValue; double m_CurrentThresholdValue; bool m_IsFloatImage; }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkBinaryThresholdTool.xpm b/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkBinaryThresholdTool.xpm rename to Modules/Segmentation/Interactions/mitkBinaryThresholdTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkBinaryThresholdULTool.cpp b/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp similarity index 99% rename from Modules/MitkExt/Interactions/mitkBinaryThresholdULTool.cpp rename to Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp index 14c785035f..66b2e418e3 100644 --- a/Modules/MitkExt/Interactions/mitkBinaryThresholdULTool.cpp +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp @@ -1,320 +1,320 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.0 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkBinaryThresholdULTool.h" #include "mitkBinaryThresholdULTool.xpm" #include "mitkToolManager.h" #include "mitkLevelWindowProperty.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkDataStorage.h" #include "mitkRenderingManager.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" #include #include #include "mitkMaskAndCutRoiImageFilter.h" #include "mitkPadImageFilter.h" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, BinaryThresholdULTool, "ThresholdingUL tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, BinaryThresholdULTool, "ThresholdingUL tool"); } mitk::BinaryThresholdULTool::BinaryThresholdULTool() :m_SensibleMinimumThresholdValue(-100), m_SensibleMaximumThresholdValue(+100), m_CurrentLowerThresholdValue(1), m_CurrentUpperThresholdValue(1) { this->SupportRoiOn(); m_ThresholdFeedbackNode = DataNode::New(); m_ThresholdFeedbackNode->SetProperty( "color", ColorProperty::New(1.0, 0.0, 0.0) ); m_ThresholdFeedbackNode->SetProperty( "name", StringProperty::New("Thresholding feedback") ); m_ThresholdFeedbackNode->SetProperty( "opacity", FloatProperty::New(0.3) ); m_ThresholdFeedbackNode->SetProperty("binary", BoolProperty::New(true)); m_ThresholdFeedbackNode->SetProperty( "helper object", BoolProperty::New(true) ); } mitk::BinaryThresholdULTool::~BinaryThresholdULTool() { } const char** mitk::BinaryThresholdULTool::GetXPM() const { return mitkBinaryThresholdULTool_xpm; } const char* mitk::BinaryThresholdULTool::GetName() const { return "ThresholdingUL"; } void mitk::BinaryThresholdULTool::Activated() { m_ToolManager->RoiDataChanged += mitk::MessageDelegate(this, &mitk::BinaryThresholdULTool::OnRoiDataChanged); m_OriginalImageNode = m_ToolManager->GetReferenceData(0); m_NodeForThresholding = m_OriginalImageNode; if ( m_NodeForThresholding.IsNotNull() ) { SetupPreviewNode(); } else { m_ToolManager->ActivateTool(-1); } } void mitk::BinaryThresholdULTool::Deactivated() { m_ToolManager->RoiDataChanged -= mitk::MessageDelegate(this, &mitk::BinaryThresholdULTool::OnRoiDataChanged); m_NodeForThresholding = NULL; m_OriginalImageNode = NULL; try { if (DataStorage* storage = m_ToolManager->GetDataStorage()) { storage->Remove( m_ThresholdFeedbackNode ); RenderingManager::GetInstance()->RequestUpdateAll(); } } catch(...) { // don't care } m_ThresholdFeedbackNode->SetData(NULL); } void mitk::BinaryThresholdULTool::SetThresholdValues(int lower, int upper) { if (m_ThresholdFeedbackNode.IsNotNull()) { m_CurrentLowerThresholdValue = lower; m_CurrentUpperThresholdValue = upper; UpdatePreview(); } } void mitk::BinaryThresholdULTool::AcceptCurrentThresholdValue(const std::string& organName, const Color& color) { CreateNewSegmentationFromThreshold(m_NodeForThresholding, organName, color ); RenderingManager::GetInstance()->RequestUpdateAll(); m_ToolManager->ActivateTool(-1); } void mitk::BinaryThresholdULTool::CancelThresholding() { m_ToolManager->ActivateTool(-1); } void mitk::BinaryThresholdULTool::SetupPreviewNode() { if (m_NodeForThresholding.IsNotNull()) { Image::Pointer image = dynamic_cast( m_NodeForThresholding->GetData() ); Image::Pointer originalImage = dynamic_cast (m_OriginalImageNode->GetData()); if (image.IsNotNull()) { // initialize and a new node with the same image as our reference image // use the level window property of this image copy to display the result of a thresholding operation m_ThresholdFeedbackNode->SetData( image ); int layer(50); m_NodeForThresholding->GetIntProperty("layer", layer); m_ThresholdFeedbackNode->SetIntProperty("layer", layer+1); if (DataStorage* ds = m_ToolManager->GetDataStorage()) { if (!ds->Exists(m_ThresholdFeedbackNode)) ds->Add( m_ThresholdFeedbackNode, m_OriginalImageNode ); } if (image.GetPointer() == originalImage.GetPointer()) { m_SensibleMinimumThresholdValue = static_cast( originalImage->GetScalarValueMin() ); m_SensibleMaximumThresholdValue = static_cast( originalImage->GetScalarValueMax() ); } m_CurrentLowerThresholdValue = (m_SensibleMaximumThresholdValue + m_SensibleMinimumThresholdValue) / 3; m_CurrentUpperThresholdValue = 2*m_CurrentLowerThresholdValue; IntervalBordersChanged.Send(m_SensibleMinimumThresholdValue, m_SensibleMaximumThresholdValue); ThresholdingValuesChanged.Send(m_CurrentLowerThresholdValue, m_CurrentUpperThresholdValue); } } } void mitk::BinaryThresholdULTool::CreateNewSegmentationFromThreshold(DataNode* node, const std::string& organName, const Color& color) { if (node) { Image::Pointer image = dynamic_cast( m_NodeForThresholding->GetData() ); if (image.IsNotNull()) { // create a new image of the same dimensions and smallest possible pixel type DataNode::Pointer emptySegmentation = Tool::CreateEmptySegmentationNode( image, organName, color ); if (emptySegmentation) { // actually perform a thresholding and ask for an organ type for (unsigned int timeStep = 0; timeStep < image->GetTimeSteps(); ++timeStep) { try { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( image ); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); Image::Pointer image3D = timeSelector->GetOutput(); AccessFixedDimensionByItk_2( image3D, ITKThresholding, 3, dynamic_cast(emptySegmentation->GetData()), timeStep ); } catch(...) { Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation."); } } //since we are maybe working on a smaller image, pad it to the size of the original image if (m_OriginalImageNode.GetPointer() != m_NodeForThresholding.GetPointer()) { mitk::PadImageFilter::Pointer padFilter = mitk::PadImageFilter::New(); padFilter->SetInput(0, dynamic_cast (emptySegmentation->GetData())); padFilter->SetInput(1, dynamic_cast (m_OriginalImageNode->GetData())); padFilter->SetBinaryFilter(true); padFilter->SetUpperThreshold(1); padFilter->SetLowerThreshold(1); padFilter->Update(); emptySegmentation->SetData(padFilter->GetOutput()); } if (DataStorage* ds = m_ToolManager->GetDataStorage()) { ds->Add( emptySegmentation, m_OriginalImageNode ); } m_ToolManager->SetWorkingData( emptySegmentation ); } } } } template void mitk::BinaryThresholdULTool::ITKThresholding( itk::Image* originalImage, Image* segmentation, unsigned int timeStep ) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( segmentation ); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); Image::Pointer segmentation3D = timeSelector->GetOutput(); typedef itk::Image< Tool::DefaultSegmentationDataType, 3> SegmentationType; // this is sure for new segmentations SegmentationType::Pointer itkSegmentation; CastToItkImage( segmentation3D, itkSegmentation ); // iterate over original and segmentation typedef itk::ImageRegionConstIterator< itk::Image > InputIteratorType; typedef itk::ImageRegionIterator< SegmentationType > SegmentationIteratorType; InputIteratorType inputIterator( originalImage, originalImage->GetLargestPossibleRegion() ); SegmentationIteratorType outputIterator( itkSegmentation, itkSegmentation->GetLargestPossibleRegion() ); inputIterator.GoToBegin(); outputIterator.GoToBegin(); while (!outputIterator.IsAtEnd()) { if ( (signed)inputIterator.Get() >= m_CurrentLowerThresholdValue && (signed)inputIterator.Get() <= m_CurrentUpperThresholdValue ) { outputIterator.Set( 1 ); } else { outputIterator.Set( 0 ); } ++inputIterator; ++outputIterator; } } void mitk::BinaryThresholdULTool::OnRoiDataChanged() { mitk::DataNode::Pointer node = m_ToolManager->GetRoiData(0); if (node.IsNotNull()) { mitk::MaskAndCutRoiImageFilter::Pointer roiFilter = mitk::MaskAndCutRoiImageFilter::New(); mitk::Image::Pointer image = dynamic_cast (m_NodeForThresholding->GetData()); if (image.IsNull()) return; roiFilter->SetInput(image); roiFilter->SetRegionOfInterest(node->GetData()); roiFilter->Update(); mitk::DataNode::Pointer tmpNode = mitk::DataNode::New(); tmpNode->SetData(roiFilter->GetOutput()); m_SensibleMinimumThresholdValue = static_cast( roiFilter->GetMinValue()); m_SensibleMaximumThresholdValue = static_cast( roiFilter->GetMaxValue()); m_NodeForThresholding = tmpNode; } else m_NodeForThresholding = m_OriginalImageNode; this->SetupPreviewNode(); this->UpdatePreview(); } void mitk::BinaryThresholdULTool::UpdatePreview() { typedef itk::Image ImageType; typedef itk::Image SegmentationType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; mitk::Image::Pointer thresholdimage = dynamic_cast (m_NodeForThresholding->GetData()); if(thresholdimage) { ImageType::Pointer itkImage = ImageType::New(); CastToItkImage(thresholdimage, itkImage); ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); filter->SetInput(itkImage); filter->SetLowerThreshold(m_CurrentLowerThresholdValue); filter->SetUpperThreshold(m_CurrentUpperThresholdValue); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->Update(); mitk::Image::Pointer new_image = mitk::Image::New(); CastToMitkImage(filter->GetOutput(), new_image); m_ThresholdFeedbackNode->SetData(new_image); } RenderingManager::GetInstance()->RequestUpdateAll(); } diff --git a/Modules/MitkExt/Interactions/mitkBinaryThresholdULTool.h b/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.h similarity index 96% rename from Modules/MitkExt/Interactions/mitkBinaryThresholdULTool.h rename to Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.h index b5f279b4f4..87653dabc4 100644 --- a/Modules/MitkExt/Interactions/mitkBinaryThresholdULTool.h +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.h @@ -1,96 +1,96 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.0 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkBinaryThresholdULTool_h_Included #define mitkBinaryThresholdULTool_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkAutoSegmentationTool.h" #include "mitkDataNode.h" #include #include namespace mitk { /** \brief Calculates the segmented volumes for binary images. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation Last contributor: $Author$ */ - class MitkExt_EXPORT BinaryThresholdULTool : public AutoSegmentationTool + class Segmentation_EXPORT BinaryThresholdULTool : public AutoSegmentationTool { public: Message2 IntervalBordersChanged; Message2 ThresholdingValuesChanged; mitkClassMacro(BinaryThresholdULTool, AutoSegmentationTool); itkNewMacro(BinaryThresholdULTool); virtual const char** GetXPM() const; virtual const char* GetName() const; virtual void Activated(); virtual void Deactivated(); virtual void SetThresholdValues(int lower, int upper); virtual void AcceptCurrentThresholdValue(const std::string& organName, const Color& color); virtual void CancelThresholding(); protected: BinaryThresholdULTool(); // purposely hidden virtual ~BinaryThresholdULTool(); void SetupPreviewNode(); void CreateNewSegmentationFromThreshold(DataNode* node, const std::string& organType, const Color& color); void OnRoiDataChanged(); void UpdatePreview(); template void ITKThresholding( itk::Image* originalImage, mitk::Image* segmentation, unsigned int timeStep ); DataNode::Pointer m_ThresholdFeedbackNode; DataNode::Pointer m_OriginalImageNode; DataNode::Pointer m_NodeForThresholding; int m_SensibleMinimumThresholdValue; int m_SensibleMaximumThresholdValue; int m_CurrentLowerThresholdValue; int m_CurrentUpperThresholdValue; typedef itk::Image ImageType; typedef itk::Image< Tool::DefaultSegmentationDataType, 3> SegmentationType; // this is sure for new segmentations typedef itk::BinaryThresholdImageFilter ThresholdFilterType; ThresholdFilterType::Pointer m_ThresholdFilter; }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkBinaryThresholdULTool.xpm b/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkBinaryThresholdULTool.xpm rename to Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkCalculateGrayValueStatisticsTool.cpp b/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.cpp similarity index 99% rename from Modules/MitkExt/Interactions/mitkCalculateGrayValueStatisticsTool.cpp rename to Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.cpp index 0f5d664ecc..63ae54cfbe 100644 --- a/Modules/MitkExt/Interactions/mitkCalculateGrayValueStatisticsTool.cpp +++ b/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.cpp @@ -1,341 +1,341 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkCalculateGrayValueStatisticsTool.h" #include "mitkCalculateGrayValueStatisticsTool.xpm" #include "mitkImageAccessByItk.h" #include "mitkProgressBar.h" #include "mitkStatusBar.h" #include "mitkImageTimeSelector.h" #include "mitkImageCast.h" #include "mitkToolManager.h" #include #include namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, CalculateGrayValueStatisticsTool, "Statistics tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, CalculateGrayValueStatisticsTool, "Statistics tool"); } mitk::CalculateGrayValueStatisticsTool::CalculateGrayValueStatisticsTool() { } mitk::CalculateGrayValueStatisticsTool::~CalculateGrayValueStatisticsTool() { } const char** mitk::CalculateGrayValueStatisticsTool::GetXPM() const { return mitkCalculateGrayValueStatisticsTool_xpm; } const char* mitk::CalculateGrayValueStatisticsTool::GetName() const { return "Statistics"; } std::string mitk::CalculateGrayValueStatisticsTool::GetErrorMessage() { return "No statistics generated for these segmentations:"; } void mitk::CalculateGrayValueStatisticsTool::StartProcessingAllData() { // clear/prepare report m_CompleteReport.str(""); } bool mitk::CalculateGrayValueStatisticsTool::ProcessOneWorkingData( DataNode* node ) { if (node) { Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNull()) return false; DataNode* referencenode = m_ToolManager->GetReferenceData(0); if (!referencenode) return false; try { ProgressBar::GetInstance()->AddStepsToDo(1); // add to report std::string nodename("structure"); node->GetName(nodename); std::string message = "Calculating statistics for "; message += nodename; StatusBar::GetInstance()->DisplayText(message.c_str()); Image::Pointer refImage = dynamic_cast( referencenode->GetData() ); Image::Pointer image = dynamic_cast( node->GetData() ); m_CompleteReport << "======== Gray value analysis of " << nodename << " ========\n"; if (image.IsNotNull() && refImage.IsNotNull() ) { for (unsigned int timeStep = 0; timeStep < image->GetTimeSteps(); ++timeStep) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( refImage ); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); Image::Pointer refImage3D = timeSelector->GetOutput(); ImageTimeSelector::Pointer timeSelector2 = ImageTimeSelector::New(); timeSelector2->SetInput( image ); timeSelector2->SetTimeNr( timeStep ); timeSelector2->UpdateLargestPossibleRegion(); Image::Pointer image3D = timeSelector2->GetOutput(); if (image3D.IsNotNull() && refImage3D.IsNotNull() ) { m_CompleteReport << "=== " << nodename << ", time step " << timeStep << " ===\n"; AccessFixedDimensionByItk_2( refImage3D, ITKHistogramming, 3, image3D, m_CompleteReport ); } } } m_CompleteReport << "======== End of analysis for " << nodename << " ===========\n\n\n"; ProgressBar::GetInstance()->Progress(); } catch(...) { return false; } } return true; } void mitk::CalculateGrayValueStatisticsTool::FinishProcessingAllData() { SegmentationsProcessingTool::FinishProcessingAllData(); // show/send results StatisticsCompleted.Send(); //MITK_INFO << m_CompleteReport.str() << std::endl; } #define ROUND_P(x) ((int)((x)+0.5)) template void mitk::CalculateGrayValueStatisticsTool::CalculateMinMax( itk::Image* referenceImage, Image* segmentation, TPixel& minimum, TPixel& maximum) { typedef itk::Image ImageType; typedef itk::Image SegmentationType; typename SegmentationType::Pointer segmentationItk; CastToItkImage(segmentation, segmentationItk); typename SegmentationType::RegionType segmentationRegion = segmentationItk->GetLargestPossibleRegion(); segmentationRegion.Crop(referenceImage->GetLargestPossibleRegion()); itk::ImageRegionConstIteratorWithIndex segmentationIterator(segmentationItk, segmentationRegion); itk::ImageRegionConstIteratorWithIndex referenceIterator(referenceImage, segmentationRegion); segmentationIterator.GoToBegin(); referenceIterator.GoToBegin(); minimum = std::numeric_limits::max(); maximum = std::numeric_limits::min(); while (!segmentationIterator.IsAtEnd()) { itk::Point pt; segmentationItk->TransformIndexToPhysicalPoint(segmentationIterator.GetIndex(), pt); typename ImageType::IndexType ind; itk::ContinuousIndex contInd; if (referenceImage->TransformPhysicalPointToContinuousIndex(pt, contInd)) { for (unsigned int i = 0; i < 3; ++i) ind[i] = ROUND_P(contInd[i]); referenceIterator.SetIndex(ind); if (segmentationIterator.Get() > 0) { if (referenceIterator.Get() < minimum) minimum = referenceIterator.Get(); if (referenceIterator.Get() > maximum) maximum = referenceIterator.Get(); } } ++segmentationIterator; } } template void mitk::CalculateGrayValueStatisticsTool::ITKHistogramming( itk::Image* referenceImage, Image* segmentation, std::stringstream& report) { typedef itk::Image ImageType; typedef itk::Image SegmentationType; typename SegmentationType::Pointer segmentationItk; CastToItkImage( segmentation, segmentationItk ); // generate histogram typename SegmentationType::RegionType segmentationRegion = segmentationItk->GetLargestPossibleRegion(); segmentationRegion.Crop( referenceImage->GetLargestPossibleRegion() ); itk::ImageRegionConstIteratorWithIndex< SegmentationType > segmentationIterator( segmentationItk, segmentationRegion); itk::ImageRegionConstIteratorWithIndex< ImageType > referenceIterator( referenceImage, segmentationRegion); segmentationIterator.GoToBegin(); referenceIterator.GoToBegin(); m_ITKHistogram = HistogramType::New(); TPixel minimum = std::numeric_limits::max(); TPixel maximum = std::numeric_limits::min(); CalculateMinMax(referenceImage, segmentation, minimum, maximum); //initialize the histogram to the range of the cropped region HistogramType::SizeType size; #if defined(ITK_USE_REVIEW_STATISTICS) typedef typename HistogramType::SizeType::ValueType HSizeValueType; #else typedef typename HistogramType::SizeType::SizeValueType HSizeValueType; #endif size.Fill(static_cast (maximum - minimum + 1)); HistogramType::MeasurementVectorType lowerBound; HistogramType::MeasurementVectorType upperBound; lowerBound[0] = minimum; upperBound[0] = maximum; m_ITKHistogram->Initialize(size, lowerBound, upperBound); double mean(0.0); double sd(0.0); double voxelCount(0.0); //iterate through the cropped region add the values to the histogram while (!segmentationIterator.IsAtEnd()) { itk::Point< float, 3 > pt; segmentationItk->TransformIndexToPhysicalPoint( segmentationIterator.GetIndex(), pt ); typename ImageType::IndexType ind; itk::ContinuousIndex contInd; if (referenceImage->TransformPhysicalPointToContinuousIndex(pt, contInd)) { for (unsigned int i = 0; i < 3; ++i) ind[i] = ROUND_P(contInd[i]); referenceIterator.SetIndex( ind ); if ( segmentationIterator.Get() > 0 ) { HistogramType::MeasurementVectorType currentMeasurementVector; currentMeasurementVector[0] = static_cast (referenceIterator.Get()); m_ITKHistogram->IncreaseFrequency(currentMeasurementVector, 1); mean = (mean * (static_cast (voxelCount) / static_cast (voxelCount + 1))) // 3 points: old center * 2/3 + currentPoint * 1/3; + static_cast (referenceIterator.Get()) / static_cast (voxelCount + 1); voxelCount += 1.0; } } ++segmentationIterator; } // second pass for SD segmentationIterator.GoToBegin(); referenceIterator.GoToBegin(); while ( !segmentationIterator.IsAtEnd() ) { itk::Point< float, 3 > pt; segmentationItk->TransformIndexToPhysicalPoint( segmentationIterator.GetIndex(), pt ); typename ImageType::IndexType ind; itk::ContinuousIndex contInd; if (referenceImage->TransformPhysicalPointToContinuousIndex(pt, contInd)) { for (unsigned int i = 0; i < 3; ++i) ind[i] = ROUND_P(contInd[i]); referenceIterator.SetIndex( ind ); if ( segmentationIterator.Get() > 0 ) { sd += ((static_cast(referenceIterator.Get() ) - mean)*(static_cast(referenceIterator.Get() ) - mean)); } } ++segmentationIterator; } sd /= static_cast(voxelCount - 1); sd = sqrt( sd ); // generate quantiles TPixel histogramQuantileValues[5]; histogramQuantileValues[0] = m_ITKHistogram->Quantile(0, 0.05); histogramQuantileValues[1] = m_ITKHistogram->Quantile(0, 0.25); histogramQuantileValues[2] = m_ITKHistogram->Quantile(0, 0.50); histogramQuantileValues[3] = m_ITKHistogram->Quantile(0, 0.75); histogramQuantileValues[4] = m_ITKHistogram->Quantile(0, 0.95); // report histogram values std::locale C("C"); std::locale originalLocale = report.getloc(); report.imbue(C); report << " Minimum:" << minimum << "\n 5% quantile: " << histogramQuantileValues[0] << "\n 25% quantile: " << histogramQuantileValues[1] << "\n 50% quantile: " << histogramQuantileValues[2] << "\n 75% quantile: " << histogramQuantileValues[3] << "\n 95% quantile: " << histogramQuantileValues[4] << "\n Maximum: " << maximum << "\n Mean: " << mean << "\n SD: " << sd << "\n"; report.imbue(originalLocale); } std::string mitk::CalculateGrayValueStatisticsTool::GetReport() const { return m_CompleteReport.str(); } mitk::CalculateGrayValueStatisticsTool::HistogramType::ConstPointer mitk::CalculateGrayValueStatisticsTool::GetHistogram() { return m_ITKHistogram.GetPointer(); } diff --git a/Modules/MitkExt/Interactions/mitkCalculateGrayValueStatisticsTool.h b/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.h similarity index 95% rename from Modules/MitkExt/Interactions/mitkCalculateGrayValueStatisticsTool.h rename to Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.h index 959cc07a16..a20cc68ec3 100644 --- a/Modules/MitkExt/Interactions/mitkCalculateGrayValueStatisticsTool.h +++ b/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.h @@ -1,102 +1,102 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.0 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkCalculateGrayValueStatisticsTool_h_Included #define mitkCalculateGrayValueStatisticsTool_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkSegmentationsProcessingTool.h" #ifndef __itkHistogram_h #include #endif #include #include namespace mitk { /** \brief Calculates some gray value statistics for segmentations. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation Last contributor: $Author$ */ -class MitkExt_EXPORT CalculateGrayValueStatisticsTool : public SegmentationsProcessingTool +class Segmentation_EXPORT CalculateGrayValueStatisticsTool : public SegmentationsProcessingTool { public: Message<> StatisticsCompleted; mitkClassMacro(CalculateGrayValueStatisticsTool, SegmentationsProcessingTool); itkNewMacro(CalculateGrayValueStatisticsTool); virtual const char** GetXPM() const; virtual const char* GetName() const; virtual std::string GetReport() const; // // Insight/Code/Review/Algorithms version of Histogram takes // only one template parameter, and the 'release' version // takes 2, but the default value for the second, 1, is what // was specified here. typedef itk::Statistics::Histogram HistogramType; HistogramType::Pointer m_ITKHistogram; HistogramType::ConstPointer GetHistogram(); typedef HistogramType::MeasurementType HistogramMeasurementType; protected: CalculateGrayValueStatisticsTool(); // purposely hidden virtual ~CalculateGrayValueStatisticsTool(); virtual void StartProcessingAllData(); virtual bool ProcessOneWorkingData( DataNode* node ); virtual void FinishProcessingAllData(); virtual std::string GetErrorMessage(); /** Calculates the minimum and maximum of the pixelvalues. They have to be known to initialize the histogram. */ template void CalculateMinMax(itk::Image* referenceImage, Image* segmentation, TPixel& minimum, TPixel& maximum); /** - initializes and fills the histogram - calculates mean, sd and quantiles */ template void ITKHistogramming(itk::Image* referenceImage, Image* segmentation, std::stringstream& report); std::stringstream m_CompleteReport; }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkCalculateGrayValueStatisticsTool.xpm b/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkCalculateGrayValueStatisticsTool.xpm rename to Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkCalculateVolumetryTool.cpp b/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.cpp similarity index 96% rename from Modules/MitkExt/Interactions/mitkCalculateVolumetryTool.cpp rename to Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.cpp index 455d7465c2..97a056a7ce 100644 --- a/Modules/MitkExt/Interactions/mitkCalculateVolumetryTool.cpp +++ b/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.cpp @@ -1,90 +1,90 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkCalculateVolumetryTool.h" #include "mitkCalculateVolumetryTool.xpm" #include "mitkVolumeCalculator.h" #include "mitkProperties.h" #include "mitkToolManager.h" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, CalculateVolumetryTool, "Volumetry tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, CalculateVolumetryTool, "Volumetry tool"); } mitk::CalculateVolumetryTool::CalculateVolumetryTool() { } mitk::CalculateVolumetryTool::~CalculateVolumetryTool() { } const char** mitk::CalculateVolumetryTool::GetXPM() const { return mitkCalculateVolumetryTool_xpm; } const char* mitk::CalculateVolumetryTool::GetName() const { return "Volumetry"; } std::string mitk::CalculateVolumetryTool::GetErrorMessage() { return "Volume could not be calculated for these nodes:"; } bool mitk::CalculateVolumetryTool::ProcessOneWorkingData( DataNode* node ) { if (node) { Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNull()) return false; if (image->GetDimension() == 4) { Tool::ErrorMessage("Volumetry only valid for timestep 0! Bug #1280"); } VolumeCalculator::Pointer volumetryFilter = VolumeCalculator::New(); volumetryFilter->SetImage( image ); volumetryFilter->SetThreshold( 1 ); // comparison is >= try { volumetryFilter->ComputeVolume(); float volumeInTimeStep0 = volumetryFilter->GetVolume(); node->SetProperty( "volume", FloatProperty::New(volumeInTimeStep0) ); } catch(...) { return false; } } return true; } void mitk::CalculateVolumetryTool::FinishProcessingAllData() { Superclass::FinishProcessingAllData(); m_ToolManager->NodePropertiesChanged(); } diff --git a/Modules/MitkExt/Interactions/mitkCalculateVolumetryTool.h b/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.h similarity index 92% rename from Modules/MitkExt/Interactions/mitkCalculateVolumetryTool.h rename to Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.h index ecec2b89cf..64fd831b18 100644 --- a/Modules/MitkExt/Interactions/mitkCalculateVolumetryTool.h +++ b/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.h @@ -1,61 +1,61 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.0 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkCalculateVolumetryTool_h_Included #define mitkCalculateVolumetryTool_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkSegmentationsProcessingTool.h" namespace mitk { /** \brief Calculates the segmented volumes for binary images. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation Last contributor: $Author$ */ -class MitkExt_EXPORT CalculateVolumetryTool : public SegmentationsProcessingTool +class Segmentation_EXPORT CalculateVolumetryTool : public SegmentationsProcessingTool { public: mitkClassMacro(CalculateVolumetryTool, SegmentationsProcessingTool); itkNewMacro(CalculateVolumetryTool); virtual const char** GetXPM() const; virtual const char* GetName() const; protected: virtual bool ProcessOneWorkingData( DataNode* node ); virtual std::string GetErrorMessage(); virtual void FinishProcessingAllData(); CalculateVolumetryTool(); // purposely hidden virtual ~CalculateVolumetryTool(); }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkCalculateVolumetryTool.xpm b/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkCalculateVolumetryTool.xpm rename to Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkContourInteractor.cpp b/Modules/Segmentation/Interactions/mitkContourInteractor.cpp similarity index 100% rename from Modules/MitkExt/Interactions/mitkContourInteractor.cpp rename to Modules/Segmentation/Interactions/mitkContourInteractor.cpp diff --git a/Modules/MitkExt/Interactions/mitkContourInteractor.h b/Modules/Segmentation/Interactions/mitkContourInteractor.h similarity index 94% rename from Modules/MitkExt/Interactions/mitkContourInteractor.h rename to Modules/Segmentation/Interactions/mitkContourInteractor.h index a18184a080..38fad3ebe1 100644 --- a/Modules/MitkExt/Interactions/mitkContourInteractor.h +++ b/Modules/Segmentation/Interactions/mitkContourInteractor.h @@ -1,66 +1,66 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef __ContourInteractor_H #define __ContourInteractor_H #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include #include namespace mitk { //##Documentation //## @brief Interactor for the creation of an mitk::Contour //## @ingroup Interaction - class MitkExt_EXPORT ContourInteractor : public mitk::Interactor + class Segmentation_EXPORT ContourInteractor : public mitk::Interactor { public: mitkClassMacro(ContourInteractor, Interactor); mitkNewMacro2Param(Self, const char*, DataNode*); protected: ContourInteractor(const char * type, DataNode* dataNode); virtual ~ContourInteractor(); virtual bool ExecuteAction(Action* action, mitk::StateEvent const* stateEvent); /** * entry method for any interaction. Method is called if user * presses the left mouse button down. */ virtual void Press (mitk::Point3D& op); /** * this method is finally called after user release the left mouse button */ virtual void Release (mitk::Point3D& op); /** * method is called when the user moves the mouse with left mouse button down */ virtual void Move (mitk::Point3D& op); protected: bool m_Positive; bool m_Started; }; } #endif //__ContourInteractor_H diff --git a/Modules/MitkExt/Interactions/mitkContourTool.cpp b/Modules/Segmentation/Interactions/mitkContourTool.cpp similarity index 100% rename from Modules/MitkExt/Interactions/mitkContourTool.cpp rename to Modules/Segmentation/Interactions/mitkContourTool.cpp diff --git a/Modules/MitkExt/Interactions/mitkContourTool.h b/Modules/Segmentation/Interactions/mitkContourTool.h similarity index 95% rename from Modules/MitkExt/Interactions/mitkContourTool.h rename to Modules/Segmentation/Interactions/mitkContourTool.h index 1ecc7f73b4..e34b00f79a 100644 --- a/Modules/MitkExt/Interactions/mitkContourTool.h +++ b/Modules/Segmentation/Interactions/mitkContourTool.h @@ -1,78 +1,78 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkContourTool_h_Included #define mitkContourTool_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkFeedbackContourTool.h" namespace mitk { class Image; /** \brief Simple contour filling tool. \sa FeedbackContourTool \sa ExtractImageFilter \sa OverwriteSliceImageFilter \ingroup Interaction \ingroup ToolManagerEtAl Fills a visible contour (from FeedbackContourTool) during mouse dragging. When the mouse button is released, ContourTool tries to extract a slice from the working image and fill in the (filled) contour as a binary image. The painting "color" is defined by m_PaintingPixelValue, which is set in the constructor (by sub-classes) or during some event (e.g. in OnInvertLogic - when CTRL is pressed). \warning Only to be instantiated by mitk::ToolManager. $Author$ */ -class MitkExt_EXPORT ContourTool : public FeedbackContourTool +class Segmentation_EXPORT ContourTool : public FeedbackContourTool { public: mitkClassMacro(ContourTool, FeedbackContourTool); protected: ContourTool(int paintingPixelValue = 1); // purposely hidden virtual ~ContourTool(); virtual void Activated(); virtual void Deactivated(); virtual bool OnMousePressed (Action*, const StateEvent*); virtual bool OnMouseMoved (Action*, const StateEvent*); virtual bool OnMouseReleased(Action*, const StateEvent*); virtual bool OnInvertLogic (Action*, const StateEvent*); int m_PaintingPixelValue; }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkContourTool.xpm b/Modules/Segmentation/Interactions/mitkContourTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkContourTool.xpm rename to Modules/Segmentation/Interactions/mitkContourTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkCorrectorTool2D.cpp b/Modules/Segmentation/Interactions/mitkCorrectorTool2D.cpp similarity index 98% rename from Modules/MitkExt/Interactions/mitkCorrectorTool2D.cpp rename to Modules/Segmentation/Interactions/mitkCorrectorTool2D.cpp index dd1855ed86..9d6ac9d2fb 100644 --- a/Modules/MitkExt/Interactions/mitkCorrectorTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkCorrectorTool2D.cpp @@ -1,151 +1,151 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkCorrectorTool2D.h" #include "mitkCorrectorAlgorithm.h" #include "mitkToolManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkCorrectorTool2D.xpm" #include "mitkOverwriteDirectedPlaneImageFilter.h" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, CorrectorTool2D, "Correction tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, CorrectorTool2D, "Correction tool"); } mitk::CorrectorTool2D::CorrectorTool2D(int paintingPixelValue) :FeedbackContourTool("PressMoveRelease"), m_PaintingPixelValue(paintingPixelValue) { GetFeedbackContour()->SetClosed( false ); // don't close the contour to a polygon } mitk::CorrectorTool2D::~CorrectorTool2D() { } const char** mitk::CorrectorTool2D::GetXPM() const { return mitkCorrectorTool2D_xpm; } const char* mitk::CorrectorTool2D::GetName() const { return "Correction"; } void mitk::CorrectorTool2D::Activated() { Superclass::Activated(); } void mitk::CorrectorTool2D::Deactivated() { Superclass::Deactivated(); } bool mitk::CorrectorTool2D::OnMousePressed (Action* action, const StateEvent* stateEvent) { if (!FeedbackContourTool::OnMousePressed( action, stateEvent )) return false; const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; Contour* contour = FeedbackContourTool::GetFeedbackContour(); contour->Initialize(); contour->AddVertex( positionEvent->GetWorldPosition() ); FeedbackContourTool::SetFeedbackContourVisible(true); return true; } bool mitk::CorrectorTool2D::OnMouseMoved (Action* action, const StateEvent* stateEvent) { if (!FeedbackContourTool::OnMouseMoved( action, stateEvent )) return false; const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; Contour* contour = FeedbackContourTool::GetFeedbackContour(); contour->AddVertex( positionEvent->GetWorldPosition() ); assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } bool mitk::CorrectorTool2D::OnMouseReleased(Action* action, const StateEvent* stateEvent) { // 1. Hide the feedback contour, find out which slice the user clicked, find out which slice of the toolmanager's working image corresponds to that FeedbackContourTool::SetFeedbackContourVisible(false); const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); if (!FeedbackContourTool::OnMouseReleased( action, stateEvent )) return false; DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if (!workingNode) return false; Image* image = dynamic_cast(workingNode->GetData()); const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); if ( !image || !planeGeometry ) return false; // 2. Slice is known, now we try to get it as a 2D image and project the contour into index coordinates of this slice m_WorkingSlice = FeedbackContourTool::GetAffectedImageSliceAs2DImage( positionEvent, image ); if ( m_WorkingSlice.IsNull() ) { MITK_ERROR << "Unable to extract slice." << std::endl; return false; } CorrectorAlgorithm::Pointer algorithm = CorrectorAlgorithm::New(); algorithm->SetInput( m_WorkingSlice ); algorithm->SetContour( FeedbackContourTool::GetFeedbackContour() ); try { algorithm->UpdateLargestPossibleRegion(); } catch ( std::exception& e ) { MITK_ERROR << "Caught exception '" << e.what() << "'" << std::endl; } mitk::Image::Pointer resultSlice = mitk::Image::New(); resultSlice->Initialize(algorithm->GetOutput()); resultSlice->SetVolume(algorithm->GetOutput()->GetData()); this->WriteBackSegmentationResult(positionEvent, resultSlice); // 6. Make sure the result is drawn again --> is visible then. assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } diff --git a/Modules/MitkExt/Interactions/mitkCorrectorTool2D.h b/Modules/Segmentation/Interactions/mitkCorrectorTool2D.h similarity index 95% rename from Modules/MitkExt/Interactions/mitkCorrectorTool2D.h rename to Modules/Segmentation/Interactions/mitkCorrectorTool2D.h index 0eba6ab07d..f7743d14dd 100644 --- a/Modules/MitkExt/Interactions/mitkCorrectorTool2D.h +++ b/Modules/Segmentation/Interactions/mitkCorrectorTool2D.h @@ -1,81 +1,81 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkCorrectorTool2D_h_Included #define mitkCorrectorTool2D_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkFeedbackContourTool.h" namespace mitk { class Image; /** \brief Corrector tool for 2D binary segmentations \sa FeedbackContourTool \sa ExtractImageFilter \sa OverwriteSliceImageFilter \ingroup Interaction \ingroup ToolManagerEtAl Lets the user draw a (multi-point) line and intelligently decides what to do. The underlying algorithm tests if the line begins and ends inside or outside a segmentation and either adds or subtracts a piece of segmentation. Algorithm is implemented in CorrectorAlgorithm (so that it could be reimplemented in a more modern fashion some time). \warning Only to be instantiated by mitk::ToolManager. $Author$ */ -class MitkExt_EXPORT CorrectorTool2D : public FeedbackContourTool +class Segmentation_EXPORT CorrectorTool2D : public FeedbackContourTool { public: mitkClassMacro(CorrectorTool2D, FeedbackContourTool); itkNewMacro(CorrectorTool2D); virtual const char** GetXPM() const; virtual const char* GetName() const; protected: CorrectorTool2D(int paintingPixelValue = 1); // purposely hidden virtual ~CorrectorTool2D(); virtual void Activated(); virtual void Deactivated(); virtual bool OnMousePressed (Action*, const StateEvent*); virtual bool OnMouseMoved (Action*, const StateEvent*); virtual bool OnMouseReleased(Action*, const StateEvent*); int m_PaintingPixelValue; Image::Pointer m_WorkingSlice; }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkCorrectorTool2D.xpm b/Modules/Segmentation/Interactions/mitkCorrectorTool2D.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkCorrectorTool2D.xpm rename to Modules/Segmentation/Interactions/mitkCorrectorTool2D.xpm diff --git a/Modules/MitkExt/Interactions/mitkCreateSurfaceTool.cpp b/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.cpp similarity index 97% rename from Modules/MitkExt/Interactions/mitkCreateSurfaceTool.cpp rename to Modules/Segmentation/Interactions/mitkCreateSurfaceTool.cpp index a29655c327..153b2d9bfa 100644 --- a/Modules/MitkExt/Interactions/mitkCreateSurfaceTool.cpp +++ b/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.cpp @@ -1,100 +1,100 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkCreateSurfaceTool.h" #include "mitkCreateSurfaceTool.xpm" #include "mitkShowSegmentationAsSurface.h" #include "mitkProgressBar.h" #include "mitkStatusBar.h" #include "mitkToolManager.h" #include "itkCommand.h" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, CreateSurfaceTool, "Surface creation tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, CreateSurfaceTool, "Surface creation tool"); } mitk::CreateSurfaceTool::CreateSurfaceTool() { } mitk::CreateSurfaceTool::~CreateSurfaceTool() { } const char** mitk::CreateSurfaceTool::GetXPM() const { return mitkCreateSurfaceTool_xpm; } const char* mitk::CreateSurfaceTool::GetName() const { return "Surface"; } std::string mitk::CreateSurfaceTool::GetErrorMessage() { return "No surfaces created for these segmentations:"; } bool mitk::CreateSurfaceTool::ProcessOneWorkingData( DataNode* node ) { if (node) { Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNull()) return false; try { mitk::ShowSegmentationAsSurface::Pointer surfaceFilter = mitk::ShowSegmentationAsSurface::New(); // attach observer to get notified about result itk::SimpleMemberCommand::Pointer goodCommand = itk::SimpleMemberCommand::New(); goodCommand->SetCallbackFunction(this, &CreateSurfaceTool::OnSurfaceCalculationDone); surfaceFilter->AddObserver(mitk::ResultAvailable(), goodCommand); itk::SimpleMemberCommand::Pointer badCommand = itk::SimpleMemberCommand::New(); badCommand->SetCallbackFunction(this, &CreateSurfaceTool::OnSurfaceCalculationDone); surfaceFilter->AddObserver(mitk::ProcessingError(), badCommand); DataNode::Pointer nodepointer = node; surfaceFilter->SetPointerParameter("Input", image); surfaceFilter->SetPointerParameter("Group node", nodepointer); surfaceFilter->SetParameter("Show result", true ); surfaceFilter->SetParameter("Sync visibility", false ); surfaceFilter->SetDataStorage( *m_ToolManager->GetDataStorage() ); ProgressBar::GetInstance()->AddStepsToDo(1); StatusBar::GetInstance()->DisplayText("Surface creation started in background..."); surfaceFilter->StartAlgorithm(); } catch(...) { return false; } } return true; } void mitk::CreateSurfaceTool::OnSurfaceCalculationDone() { ProgressBar::GetInstance()->Progress(); m_ToolManager->NewNodesGenerated(); } diff --git a/Modules/MitkExt/Interactions/mitkCreateSurfaceTool.h b/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.h similarity index 92% rename from Modules/MitkExt/Interactions/mitkCreateSurfaceTool.h rename to Modules/Segmentation/Interactions/mitkCreateSurfaceTool.h index 5589fd1cd2..0c1b57481f 100644 --- a/Modules/MitkExt/Interactions/mitkCreateSurfaceTool.h +++ b/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.h @@ -1,60 +1,60 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.0 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkCreateSurfaceTool_h_Included #define mitkCreateSurfaceTool_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkSegmentationsProcessingTool.h" namespace mitk { /** \brief Creates surface models from segmentations. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation Last contributor: $Author$ */ -class MitkExt_EXPORT CreateSurfaceTool : public SegmentationsProcessingTool +class Segmentation_EXPORT CreateSurfaceTool : public SegmentationsProcessingTool { public: mitkClassMacro(CreateSurfaceTool, SegmentationsProcessingTool); itkNewMacro(CreateSurfaceTool); virtual const char** GetXPM() const; virtual const char* GetName() const; void OnSurfaceCalculationDone(); protected: virtual bool ProcessOneWorkingData( DataNode* node ); virtual std::string GetErrorMessage(); CreateSurfaceTool(); // purposely hidden virtual ~CreateSurfaceTool(); }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkCreateSurfaceTool.xpm b/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkCreateSurfaceTool.xpm rename to Modules/Segmentation/Interactions/mitkCreateSurfaceTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkDrawPaintbrushTool.cpp b/Modules/Segmentation/Interactions/mitkDrawPaintbrushTool.cpp similarity index 92% rename from Modules/MitkExt/Interactions/mitkDrawPaintbrushTool.cpp rename to Modules/Segmentation/Interactions/mitkDrawPaintbrushTool.cpp index 90fbb8facb..ee95c182b9 100644 --- a/Modules/MitkExt/Interactions/mitkDrawPaintbrushTool.cpp +++ b/Modules/Segmentation/Interactions/mitkDrawPaintbrushTool.cpp @@ -1,44 +1,44 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkDrawPaintbrushTool.h" #include "mitkDrawPaintbrushTool.xpm" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, DrawPaintbrushTool, "Paintbrush drawing tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, DrawPaintbrushTool, "Paintbrush drawing tool"); } mitk::DrawPaintbrushTool::DrawPaintbrushTool() :PaintbrushTool(1) { } mitk::DrawPaintbrushTool::~DrawPaintbrushTool() { } const char** mitk::DrawPaintbrushTool::GetXPM() const { return mitkDrawPaintbrushTool_xpm; } const char* mitk::DrawPaintbrushTool::GetName() const { return "Paint"; } diff --git a/Modules/MitkExt/Interactions/mitkDrawPaintbrushTool.h b/Modules/Segmentation/Interactions/mitkDrawPaintbrushTool.h similarity index 93% rename from Modules/MitkExt/Interactions/mitkDrawPaintbrushTool.h rename to Modules/Segmentation/Interactions/mitkDrawPaintbrushTool.h index e81c07aa3d..642e2e5fe6 100644 --- a/Modules/MitkExt/Interactions/mitkDrawPaintbrushTool.h +++ b/Modules/Segmentation/Interactions/mitkDrawPaintbrushTool.h @@ -1,64 +1,64 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkPaintContourTool_h_Included #define mitkPaintContourTool_h_Included #include "mitkPaintbrushTool.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" namespace mitk { /** \brief Paintbrush tool for InteractiveSegmentation \sa FeedbackContourTool \sa ExtractImageFilter \sa OverwriteSliceImageFilter \ingroup Interaction \ingroup ToolManagerEtAl Simple paintbrush drawing tool. Right now there are only circular pens of varying size. This class specified only the drawing "color" for the super class PaintbrushTool. \warning Only to be instantiated by mitk::ToolManager. $Author: maleike $ */ -class MitkExt_EXPORT DrawPaintbrushTool : public PaintbrushTool +class Segmentation_EXPORT DrawPaintbrushTool : public PaintbrushTool { public: mitkClassMacro(DrawPaintbrushTool, PaintbrushTool); itkNewMacro(DrawPaintbrushTool); virtual const char** GetXPM() const; virtual const char* GetName() const; protected: DrawPaintbrushTool(); // purposely hidden virtual ~DrawPaintbrushTool(); }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkDrawPaintbrushTool.xpm b/Modules/Segmentation/Interactions/mitkDrawPaintbrushTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkDrawPaintbrushTool.xpm rename to Modules/Segmentation/Interactions/mitkDrawPaintbrushTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkErasePaintbrushTool.cpp b/Modules/Segmentation/Interactions/mitkErasePaintbrushTool.cpp similarity index 92% rename from Modules/MitkExt/Interactions/mitkErasePaintbrushTool.cpp rename to Modules/Segmentation/Interactions/mitkErasePaintbrushTool.cpp index af04098073..4ba4b5f6d9 100644 --- a/Modules/MitkExt/Interactions/mitkErasePaintbrushTool.cpp +++ b/Modules/Segmentation/Interactions/mitkErasePaintbrushTool.cpp @@ -1,45 +1,45 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkErasePaintbrushTool.h" #include "mitkErasePaintbrushTool.xpm" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, ErasePaintbrushTool, "Paintbrush erasing tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, ErasePaintbrushTool, "Paintbrush erasing tool"); } mitk::ErasePaintbrushTool::ErasePaintbrushTool() :PaintbrushTool(0) { FeedbackContourTool::SetFeedbackContourColor( 1.0, 0.0, 0.0 ); } mitk::ErasePaintbrushTool::~ErasePaintbrushTool() { } const char** mitk::ErasePaintbrushTool::GetXPM() const { return mitkErasePaintbrushTool_xpm; } const char* mitk::ErasePaintbrushTool::GetName() const { return "Wipe"; } diff --git a/Modules/MitkExt/Interactions/mitkErasePaintbrushTool.h b/Modules/Segmentation/Interactions/mitkErasePaintbrushTool.h similarity index 93% rename from Modules/MitkExt/Interactions/mitkErasePaintbrushTool.h rename to Modules/Segmentation/Interactions/mitkErasePaintbrushTool.h index b1484fccba..09287190c7 100644 --- a/Modules/MitkExt/Interactions/mitkErasePaintbrushTool.h +++ b/Modules/Segmentation/Interactions/mitkErasePaintbrushTool.h @@ -1,64 +1,64 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkErasePaintbrushTool_h_Included #define mitkErasePaintbrushTool_h_Included #include "mitkPaintbrushTool.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" namespace mitk { /** \brief Paintbrush tool for InteractiveSegmentation \sa FeedbackContourTool \sa ExtractImageFilter \sa OverwriteSliceImageFilter \ingroup Interaction \ingroup ToolManagerEtAl Simple paintbrush drawing tool. Right now there are only circular pens of varying size. This class specified only the drawing "color" for the super class PaintbrushTool. \warning Only to be instantiated by mitk::ToolManager. $Author: maleike $ */ -class MitkExt_EXPORT ErasePaintbrushTool : public PaintbrushTool +class Segmentation_EXPORT ErasePaintbrushTool : public PaintbrushTool { public: mitkClassMacro(ErasePaintbrushTool, PaintbrushTool); itkNewMacro(ErasePaintbrushTool); virtual const char** GetXPM() const; virtual const char* GetName() const; protected: ErasePaintbrushTool(); // purposely hidden virtual ~ErasePaintbrushTool(); }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkErasePaintbrushTool.xpm b/Modules/Segmentation/Interactions/mitkErasePaintbrushTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkErasePaintbrushTool.xpm rename to Modules/Segmentation/Interactions/mitkErasePaintbrushTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkEraseRegionTool.cpp b/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp similarity index 93% rename from Modules/MitkExt/Interactions/mitkEraseRegionTool.cpp rename to Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp index 58ffb471be..e0c3142a13 100644 --- a/Modules/MitkExt/Interactions/mitkEraseRegionTool.cpp +++ b/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp @@ -1,45 +1,45 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkEraseRegionTool.h" #include "mitkEraseRegionTool.xpm" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, EraseRegionTool, "Erase tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, EraseRegionTool, "Erase tool"); } mitk::EraseRegionTool::EraseRegionTool() :SetRegionTool(0) { FeedbackContourTool::SetFeedbackContourColor( 1.0, 0.0, 0.0 ); } mitk::EraseRegionTool::~EraseRegionTool() { } const char** mitk::EraseRegionTool::GetXPM() const { return mitkEraseRegionTool_xpm; } const char* mitk::EraseRegionTool::GetName() const { return "Erase"; } diff --git a/Modules/MitkExt/Interactions/mitkEraseRegionTool.h b/Modules/Segmentation/Interactions/mitkEraseRegionTool.h similarity index 93% rename from Modules/MitkExt/Interactions/mitkEraseRegionTool.h rename to Modules/Segmentation/Interactions/mitkEraseRegionTool.h index 51e8b65e30..c9e150386b 100644 --- a/Modules/MitkExt/Interactions/mitkEraseRegionTool.h +++ b/Modules/Segmentation/Interactions/mitkEraseRegionTool.h @@ -1,63 +1,63 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkEraseRegionTool_h_Included #define mitkEraseRegionTool_h_Included #include "mitkSetRegionTool.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" namespace mitk { /** \brief Fill the inside of a contour with 1 \sa SetRegionTool \ingroup Interaction \ingroup ToolManagerEtAl Finds the outer contour of a shape in 2D (possibly including holes) and sets all the inside pixels to 0 (erasing a segmentation). \warning Only to be instantiated by mitk::ToolManager. $Author$ */ -class MitkExt_EXPORT EraseRegionTool : public SetRegionTool +class Segmentation_EXPORT EraseRegionTool : public SetRegionTool { public: mitkClassMacro(EraseRegionTool, SetRegionTool); itkNewMacro(EraseRegionTool); virtual const char** GetXPM() const; virtual const char* GetName() const; protected: EraseRegionTool(); // purposely hidden virtual ~EraseRegionTool(); }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkEraseRegionTool.xpm b/Modules/Segmentation/Interactions/mitkEraseRegionTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkEraseRegionTool.xpm rename to Modules/Segmentation/Interactions/mitkEraseRegionTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkExtrudedContourInteractor.cpp b/Modules/Segmentation/Interactions/mitkExtrudedContourInteractor.cpp similarity index 100% rename from Modules/MitkExt/Interactions/mitkExtrudedContourInteractor.cpp rename to Modules/Segmentation/Interactions/mitkExtrudedContourInteractor.cpp diff --git a/Modules/MitkExt/Interactions/mitkExtrudedContourInteractor.h b/Modules/Segmentation/Interactions/mitkExtrudedContourInteractor.h similarity index 94% rename from Modules/MitkExt/Interactions/mitkExtrudedContourInteractor.h rename to Modules/Segmentation/Interactions/mitkExtrudedContourInteractor.h index 1deaf74afd..be8c3a7be2 100644 --- a/Modules/MitkExt/Interactions/mitkExtrudedContourInteractor.h +++ b/Modules/Segmentation/Interactions/mitkExtrudedContourInteractor.h @@ -1,73 +1,73 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef __ExtrudedContourInteractor_H #define __ExtrudedContourInteractor_H #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include #include #include #include namespace mitk { //##Documentation //## @brief Interactor for the creation of an mitk::Contour //## @ingroup Interaction - class MitkExt_EXPORT ExtrudedContourInteractor : public mitk::Interactor + class Segmentation_EXPORT ExtrudedContourInteractor : public mitk::Interactor { public: mitkClassMacro(ExtrudedContourInteractor, Interactor); mitkNewMacro2Param(Self, const char*, DataNode*); itkGetObjectMacro(Contour, mitk::Contour); itkGetObjectMacro(ContourNode, mitk::DataNode); protected: ExtrudedContourInteractor(const char * type, DataNode* dataNode); virtual ~ExtrudedContourInteractor(); virtual bool ExecuteAction(Action* action, mitk::StateEvent const* stateEvent); /** * entry method for any interaction. Method is called if user * presses the left mouse button down. */ virtual void Press (mitk::Point3D& op); /** * this method is finally called after user release the left mouse button */ virtual void Release (mitk::Point3D& op); /** * method is called when the user moves the mouse with left mouse button down */ virtual void Move (mitk::Point3D& op); protected: bool m_Positive; bool m_Started; mitk::Contour::Pointer m_Contour; mitk::DataNode::Pointer m_ContourNode; }; } #endif //__ExtrudedContourInteractor_H diff --git a/Modules/MitkExt/Interactions/mitkFeedbackContourTool.cpp b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp similarity index 100% rename from Modules/MitkExt/Interactions/mitkFeedbackContourTool.cpp rename to Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp diff --git a/Modules/MitkExt/Interactions/mitkFeedbackContourTool.h b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h similarity index 97% rename from Modules/MitkExt/Interactions/mitkFeedbackContourTool.h rename to Modules/Segmentation/Interactions/mitkFeedbackContourTool.h index 0291f7ea9e..2eb88d73ee 100644 --- a/Modules/MitkExt/Interactions/mitkFeedbackContourTool.h +++ b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h @@ -1,107 +1,107 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.0 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkFeedbackContourTool_h_Included #define mitkFeedbackContourTool_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkSegTool2D.h" #include "mitkContour.h" #include "mitkContourUtils.h" #include "mitkImage.h" #include "mitkDataNode.h" #include "mitkImageCast.h" namespace mitk { /** \brief Base class for tools that use a contour for feedback \sa Tool \sa Contour \ingroup Interaction \ingroup ToolManagerEtAl Implements helper methods, that might be of use to all kind of 2D segmentation tools that use a contour for user feedback. - Providing a feedback contour that might be added or removed from the visible scene (SetFeedbackContourVisible). - Filling of a contour into a 2D slice These helper methods are actually implemented in ContourUtils now. FeedbackContourTool only forwards such requests. \warning Only to be instantiated by mitk::ToolManager. $Author: nolden $ */ -class MitkExt_EXPORT FeedbackContourTool : public SegTool2D +class Segmentation_EXPORT FeedbackContourTool : public SegTool2D { public: mitkClassMacro(FeedbackContourTool, SegTool2D); protected: FeedbackContourTool(); // purposely hidden FeedbackContourTool(const char*); // purposely hidden virtual ~FeedbackContourTool(); Contour* GetFeedbackContour(); void SetFeedbackContour(Contour&); void Disable3dRendering(); void SetFeedbackContourVisible(bool); /// Provide values from 0.0 (black) to 1.0 (full color) void SetFeedbackContourColor( float r, float g, float b ); void SetFeedbackContourColorDefault(); /** \brief Projects a contour onto an image point by point. Converts from world to index coordinates. \param correctionForIpSegmentation adds 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ Contour::Pointer ProjectContourTo2DSlice(Image* slice, Contour* contourIn3D, bool correctionForIpSegmentation = false, bool constrainToInside = true); /** \brief Projects a slice index coordinates of a contour back into world coordinates. \param correctionForIpSegmentation subtracts 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ Contour::Pointer BackProjectContourFrom2DSlice(const Geometry3D* sliceGeometry, Contour* contourIn2D, bool correctionForIpSegmentation = false); /** \brief Fill a contour in a 2D slice with a specified pixel value. */ void FillContourInSlice( Contour* projectedContour, Image* sliceImage, int paintingPixelValue = 1 ); private: Contour::Pointer m_FeedbackContour; DataNode::Pointer m_FeedbackContourNode; bool m_FeedbackContourVisible; ContourUtils::Pointer m_ContourUtils; }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkFillRegionTool.cpp b/Modules/Segmentation/Interactions/mitkFillRegionTool.cpp similarity index 93% rename from Modules/MitkExt/Interactions/mitkFillRegionTool.cpp rename to Modules/Segmentation/Interactions/mitkFillRegionTool.cpp index 2968c23e5e..dea2ca010a 100644 --- a/Modules/MitkExt/Interactions/mitkFillRegionTool.cpp +++ b/Modules/Segmentation/Interactions/mitkFillRegionTool.cpp @@ -1,44 +1,44 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkFillRegionTool.h" #include "mitkFillRegionTool.xpm" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, FillRegionTool, "Fill tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, FillRegionTool, "Fill tool"); } mitk::FillRegionTool::FillRegionTool() :SetRegionTool(1) { } mitk::FillRegionTool::~FillRegionTool() { } const char** mitk::FillRegionTool::GetXPM() const { return mitkFillRegionTool_xpm; } const char* mitk::FillRegionTool::GetName() const { return "Fill"; } diff --git a/Modules/MitkExt/Interactions/mitkFillRegionTool.h b/Modules/Segmentation/Interactions/mitkFillRegionTool.h similarity index 93% rename from Modules/MitkExt/Interactions/mitkFillRegionTool.h rename to Modules/Segmentation/Interactions/mitkFillRegionTool.h index e3c7e2722f..9809e9b8fb 100644 --- a/Modules/MitkExt/Interactions/mitkFillRegionTool.h +++ b/Modules/Segmentation/Interactions/mitkFillRegionTool.h @@ -1,62 +1,62 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkFillRegionTool_h_Included #define mitkFillRegionTool_h_Included #include "mitkSetRegionTool.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" namespace mitk { /** \brief Fill the inside of a contour with 1 \sa SetRegionTool \ingroup Interaction \ingroup ToolManagerEtAl Finds the outer contour of a shape in 2D (possibly including holes) and sets all the inside pixels to 1, filling holes in a segmentation. \warning Only to be instantiated by mitk::ToolManager. $Author$ */ -class MitkExt_EXPORT FillRegionTool : public SetRegionTool +class Segmentation_EXPORT FillRegionTool : public SetRegionTool { public: mitkClassMacro(FillRegionTool, SetRegionTool); itkNewMacro(FillRegionTool); virtual const char** GetXPM() const; virtual const char* GetName() const; protected: FillRegionTool(); // purposely hidden virtual ~FillRegionTool(); }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkFillRegionTool.xpm b/Modules/Segmentation/Interactions/mitkFillRegionTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkFillRegionTool.xpm rename to Modules/Segmentation/Interactions/mitkFillRegionTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkPaintbrushTool.cpp b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp similarity index 98% rename from Modules/MitkExt/Interactions/mitkPaintbrushTool.cpp rename to Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp index dbb9eeb2b1..ade53e541f 100644 --- a/Modules/MitkExt/Interactions/mitkPaintbrushTool.cpp +++ b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp @@ -1,379 +1,377 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkPaintbrushTool.h" #include "mitkToolManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkBaseRenderer.h" #include "mitkImageDataItem.h" #include "ipSegmentation.h" #include "mitkLevelWindowProperty.h" #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) int mitk::PaintbrushTool::m_Size = 1; mitk::PaintbrushTool::PaintbrushTool(int paintingPixelValue) :FeedbackContourTool("PressMoveReleaseWithCTRLInversionAllMouseMoves"), m_PaintingPixelValue(paintingPixelValue), m_LastContourSize(0) // other than initial mitk::PaintbrushTool::m_Size (around l. 28) { m_MasterContour = Contour::New(); m_MasterContour->Initialize(); m_CurrentPlane = NULL; m_WorkingNode = DataNode::New(); m_WorkingNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_WorkingNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); } mitk::PaintbrushTool::~PaintbrushTool() { } void mitk::PaintbrushTool::Activated() { Superclass::Activated(); FeedbackContourTool::SetFeedbackContourVisible(true); SizeChanged.Send(m_Size); } void mitk::PaintbrushTool::Deactivated() { FeedbackContourTool::SetFeedbackContourVisible(false); if (m_ToolManager->GetDataStorage()->Exists(m_WorkingNode)) m_ToolManager->GetDataStorage()->Remove(m_WorkingNode); Superclass::Deactivated(); } void mitk::PaintbrushTool::SetSize(int value) { m_Size = value; } void mitk::PaintbrushTool::UpdateContour(const StateEvent* stateEvent) { //MITK_INFO<<"Update..."; // examine stateEvent and create a contour that matches the pixel mask that we are going to draw const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return; // create a copy of this slice (at least match the pixel sizes/spacings), // then draw the desired mask on it and create a contour from it // Convert to ipMITKSegmentationTYPE (because getting pixels relys on that data type) itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer correctPixelTypeImage; CastToItkImage(m_WorkingSlice/*dynamic_cast(m_WorkingNode->GetData())*/, correctPixelTypeImage ); assert (correctPixelTypeImage.IsNotNull() ); itk::Image< ipMITKSegmentationTYPE, 2 >::DirectionType imageDirection; imageDirection.SetIdentity(); correctPixelTypeImage->SetDirection(imageDirection); Image::Pointer temporarySlice = Image::New(); CastToMitkImage( correctPixelTypeImage, temporarySlice ); //mitkIpPicDescriptor* stupidClone = mitkIpPicClone( temporarySlice->GetSliceData()->GetPicDescriptor() ); mitkIpPicDescriptor* stupidClone = mitkIpPicNew(); CastToIpPicDescriptor( temporarySlice->GetSliceData(), stupidClone ); unsigned int pixelWidth = m_Size + 1; unsigned int pixelHeight = m_Size + 1; if ( stupidClone->n[0] <= pixelWidth || stupidClone->n[1] <= pixelHeight ) { MITK_INFO << "Brush size is bigger than your working image. Reconsider this...\n" "(Or tell your progammer until (s)he fixes this message.)" << std::endl; mitkIpPicFree( stupidClone ); return; } unsigned int lineLength( stupidClone->n[0] ); unsigned int oneContourOffset(0); float circleCenterX = (float)m_Size / 2.0; float circleCenterY = (float)m_Size / 2.0; for (unsigned int x = 0; x <= pixelWidth; ++x) { for (unsigned int y = 0; y <= pixelHeight; ++y) { unsigned int offset = lineLength * y + x; ipMITKSegmentationTYPE* current = (ipMITKSegmentationTYPE*)stupidClone->data + offset; float pixelCenterX = x + 0.5; float pixelCenterY = y + 0.5; float xoff = pixelCenterX - circleCenterX; float yoff = pixelCenterY - circleCenterY; bool inside = xoff * xoff + yoff * yoff < (m_Size * m_Size) / 4.0; // no idea, if this would work for ellipses if (inside) { *current = 1; oneContourOffset = offset; } else { *current = 0; } } } int numberOfContourPoints( 0 ); int newBufferSize( 0 ); float* contourPoints = ipMITKSegmentationGetContour8N( stupidClone, oneContourOffset, numberOfContourPoints, newBufferSize ); // memory allocated with malloc if (!contourPoints) { mitkIpPicFree( stupidClone ); return; } // copy point from float* to mitk::Contour Contour::Pointer contourInImageIndexCoordinates = Contour::New(); contourInImageIndexCoordinates->Initialize(); Point3D newPoint; //ipMITKSegmentationGetContour8N returns all points, which causes vtk warnings, since the first and the last points are coincident. //leaving the last point out, the contour is still drawn correctly for (int index = 0; index < numberOfContourPoints-1; ++index) { newPoint[0] = contourPoints[ 2 * index + 0 ] - circleCenterX; // master contour should be centered around (0,0) newPoint[1] = contourPoints[ 2 * index + 1] - circleCenterY; newPoint[2] = 0.0; MITK_DEBUG << "Point [" << index << "] (" << newPoint[0] << ", " << newPoint[1] << ")" << std::endl; contourInImageIndexCoordinates->AddVertex( newPoint ); } free(contourPoints); m_MasterContour = contourInImageIndexCoordinates; // The PicDescriptor is only REFERENCING(!) the data, the temporarySlice takes care of deleting the data also the descriptor is pointing on // because they got allocated by the ImageDataItem, not the descriptor. stupidClone->data = NULL; mitkIpPicFree( stupidClone ); } /** Just show the contour, get one point as the central point and add surrounding points to the contour. */ bool mitk::PaintbrushTool::OnMousePressed (Action* action, const StateEvent* stateEvent) { return this->OnMouseMoved(action, stateEvent); } /** Insert the point to the feedback contour,finish to build the contour and at the same time the painting function */ bool mitk::PaintbrushTool::OnMouseMoved (Action* itkNotUsed(action), const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; CheckIfCurrentSliceHasChanged(positionEvent); if ( m_LastContourSize != m_Size ) { UpdateContour( stateEvent ); m_LastContourSize = m_Size; } bool leftMouseButtonPressed( stateEvent->GetId() == 530 || stateEvent->GetId() == 534 || stateEvent->GetId() == 1 || stateEvent->GetId() == 5 ); Point3D worldCoordinates = positionEvent->GetWorldPosition(); Point3D indexCoordinates; m_WorkingSlice->GetGeometry()->WorldToIndex( worldCoordinates, indexCoordinates ); MITK_DEBUG << "Mouse at W " << worldCoordinates << std::endl; MITK_DEBUG << "Mouse at I " << indexCoordinates << std::endl; // round to nearest voxel center (abort if this hasn't changed) if ( m_Size % 2 == 0 ) // even { indexCoordinates[0] = ROUND( indexCoordinates[0] /*+ 0.5*/) + 0.5; indexCoordinates[1] = ROUND( indexCoordinates[1] /*+ 0.5*/ ) + 0.5; } else // odd { indexCoordinates[0] = ROUND( indexCoordinates[0] ) ; indexCoordinates[1] = ROUND( indexCoordinates[1] ) ; } static Point3D lastPos; // uninitialized: if somebody finds out how this can be initialized in a one-liner, tell me static bool lastLeftMouseButtonPressed(false); if ( fabs(indexCoordinates[0] - lastPos[0]) > mitk::eps || fabs(indexCoordinates[1] - lastPos[1]) > mitk::eps || fabs(indexCoordinates[2] - lastPos[2]) > mitk::eps || leftMouseButtonPressed != lastLeftMouseButtonPressed ) { lastPos = indexCoordinates; lastLeftMouseButtonPressed = leftMouseButtonPressed; } else { MITK_DEBUG << "." << std::flush; return false; } MITK_DEBUG << "Mouse at C " << indexCoordinates; Contour::Pointer contour = Contour::New(); contour->Initialize(); for (unsigned int index = 0; index < m_MasterContour->GetNumberOfPoints(); ++index) { Point3D point = m_MasterContour->GetPoints()->ElementAt(index); point[0] += indexCoordinates[ 0 ]; point[1] += indexCoordinates[ 1 ]; MITK_DEBUG << "Contour point [" << index << "] :" << point; contour->AddVertex( point ); } if (leftMouseButtonPressed) { FeedbackContourTool::FillContourInSlice( contour, m_WorkingSlice, m_PaintingPixelValue ); m_WorkingNode->SetData(m_WorkingSlice); m_WorkingNode->Modified(); } // visualize contour Contour::Pointer displayContour = Contour::New(); displayContour->Initialize(); //for (unsigned int index = 0; index < contour->GetNumberOfPoints(); ++index) //{ // Point3D point = contour->GetPoints()->ElementAt(index); // if ( m_Size % 2 == 0 ) // even // { // point[0] += 0.5; // point[1] += 0.5; // } // displayContour->AddVertex( point ); //} displayContour = FeedbackContourTool::BackProjectContourFrom2DSlice( m_WorkingSlice->GetGeometry(), /*displayContour*/contour ); SetFeedbackContour( *displayContour ); assert( positionEvent->GetSender()->GetRenderWindow() ); RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } bool mitk::PaintbrushTool::OnMouseReleased(Action* /*action*/, const StateEvent* stateEvent) { //When mouse is released write segmentationresult back into image const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice); // FeedbackContourTool::SetFeedbackContourVisible(false); return true; } /** Called when the CTRL key is pressed. Will change the painting pixel value from 0 to 1 or from 1 to 0. */ bool mitk::PaintbrushTool::OnInvertLogic(Action* action, const StateEvent* stateEvent) { if (!FeedbackContourTool::OnInvertLogic(action, stateEvent)) return false; // Inversion only for 0 and 1 as painting values if (m_PaintingPixelValue == 1) { m_PaintingPixelValue = 0; FeedbackContourTool::SetFeedbackContourColor( 1.0, 0.0, 0.0 ); } else if (m_PaintingPixelValue == 0) { m_PaintingPixelValue = 1; FeedbackContourTool::SetFeedbackContourColorDefault(); } return true; } void mitk::PaintbrushTool::CheckIfCurrentSliceHasChanged(const PositionEvent *event) { const PlaneGeometry* planeGeometry( dynamic_cast (event->GetSender()->GetCurrentWorldGeometry2D() ) ); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if (!workingNode) return; Image::Pointer image = dynamic_cast(workingNode->GetData()); if ( !image || !planeGeometry ) return; if(m_CurrentPlane.IsNull()) { m_CurrentPlane = const_cast(planeGeometry); m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone(); m_WorkingNode->ReplaceProperty( "color", workingNode->GetProperty("color") ); m_WorkingNode->SetData(m_WorkingSlice); } else { bool isSameSlice (false); isSameSlice = mitk::MatrixEqualElementWise(planeGeometry->GetIndexToWorldTransform()->GetMatrix(),m_CurrentPlane->GetIndexToWorldTransform()->GetMatrix()); isSameSlice = mitk::Equal(planeGeometry->GetIndexToWorldTransform()->GetOffset(),m_CurrentPlane->GetIndexToWorldTransform()->GetOffset()); if (!isSameSlice) { m_ToolManager->GetDataStorage()->Remove(m_WorkingNode); m_CurrentPlane = NULL; m_WorkingSlice = NULL; m_WorkingNode = NULL; m_CurrentPlane = const_cast(planeGeometry); m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone(); m_WorkingNode = mitk::DataNode::New(); m_WorkingNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_WorkingNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); m_WorkingNode->SetData(m_WorkingSlice); } } if(!m_ToolManager->GetDataStorage()->Exists(m_WorkingNode)) { m_WorkingNode->SetProperty( "outline binary", mitk::BoolProperty::New(true) ); m_WorkingNode->SetProperty( "color", workingNode->GetProperty("color") ); - m_WorkingNode->SetProperty( "texture interpolation", mitk::BoolProperty::New(true) ); - m_WorkingNode->SetProperty( "layer", mitk::IntProperty::New( 100 ) ); m_WorkingNode->SetProperty( "name", mitk::StringProperty::New("Paintbrush_Node") ); m_WorkingNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_WorkingNode->SetProperty( "opacity", mitk::FloatProperty::New(0.8) ); m_WorkingNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_ToolManager->GetDataStorage()->Add(m_WorkingNode); } } diff --git a/Modules/MitkExt/Interactions/mitkPaintbrushTool.h b/Modules/Segmentation/Interactions/mitkPaintbrushTool.h similarity index 96% rename from Modules/MitkExt/Interactions/mitkPaintbrushTool.h rename to Modules/Segmentation/Interactions/mitkPaintbrushTool.h index 997f0c97c6..30887b73df 100644 --- a/Modules/MitkExt/Interactions/mitkPaintbrushTool.h +++ b/Modules/Segmentation/Interactions/mitkPaintbrushTool.h @@ -1,97 +1,97 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkPaintbrushTool_h_Included #define mitkPaintbrushTool_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkFeedbackContourTool.h" #include "mitkPointSet.h" #include "mitkPointOperation.h" #include "mitkLegacyAdaptors.h" namespace mitk { /** \brief Paintbrush tool for InteractiveSegmentation \sa FeedbackContourTool \sa ExtractImageFilter \sa OverwriteSliceImageFilter \ingroup Interaction \ingroup ToolManagerEtAl Simple paintbrush drawing tool. Right now there are only circular pens of varying size. \todo Bug #1727: Should be modified, so that the contour is always visible, i.e.without pressing a mouse button. \warning Only to be instantiated by mitk::ToolManager. $Author: maleike $ */ -class MitkExt_EXPORT PaintbrushTool : public FeedbackContourTool +class Segmentation_EXPORT PaintbrushTool : public FeedbackContourTool { public: // sent when the pen size is changed or should be updated in a GUI. Message1 SizeChanged; mitkClassMacro(PaintbrushTool, FeedbackContourTool); void SetSize(int value); protected: PaintbrushTool(int paintingPixelValue = 1); // purposely hidden virtual ~PaintbrushTool(); virtual void Activated(); virtual void Deactivated(); virtual bool OnMousePressed (Action*, const StateEvent*); virtual bool OnMouseMoved (Action*, const StateEvent*); virtual bool OnMouseReleased(Action*, const StateEvent*); virtual bool OnInvertLogic (Action*, const StateEvent*); /** * \todo This is a possible place where to introduce * different types of pens */ void UpdateContour(const StateEvent* stateEvent); /** * Checks if the current slice has changed */ void CheckIfCurrentSliceHasChanged(const PositionEvent* event); int m_PaintingPixelValue; static int m_Size; Contour::Pointer m_MasterContour; int m_LastContourSize; Image::Pointer m_WorkingSlice; PlaneGeometry::Pointer m_CurrentPlane; DataNode::Pointer m_WorkingNode; }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkRegionGrow3DTool.cpp b/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.cpp similarity index 99% rename from Modules/MitkExt/Interactions/mitkRegionGrow3DTool.cpp rename to Modules/Segmentation/Interactions/mitkRegionGrow3DTool.cpp index e4a0b9e01d..83fd12b4f6 100644 --- a/Modules/MitkExt/Interactions/mitkRegionGrow3DTool.cpp +++ b/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.cpp @@ -1,442 +1,442 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 28959 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkRegionGrow3DTool.h" #include "mitkToolManager.h" #include "mitkRenderingManager.h" #include "mitkLevelWindowProperty.h" #include "mitkPointSetInteractor.h" #include "mitkGlobalInteraction.h" #include "mitkITKImageImport.h" #include "itkImage.h" #include "itkBinaryThresholdImageFilter.h" #include "itkConnectedAdaptiveThresholdImageFilter.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkMaskAndCutRoiImageFilter.h" #include "mitkPadImageFilter.h" #include "mitkRegionGrow3DTool.xpm" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, RegionGrow3DTool, "RegionGrower 3D"); + MITK_TOOL_MACRO(Segmentation_EXPORT, RegionGrow3DTool, "RegionGrower 3D"); } mitk::RegionGrow3DTool::RegionGrow3DTool(): Tool("PressMoveRelease"), m_LowerThreshold(-5000), m_UpperThreshold(5000), m_CurrentRGDirectionIsUpwards(false) { CONNECT_ACTION( 42, OnMouseReleased ); this->SupportRoiOn(); m_FeedbackNode = DataNode::New(); m_FeedbackNode->SetProperty( "color", ColorProperty::New(1.0, 0.0, 0.0) ); m_FeedbackNode->SetProperty( "texture interpolation", BoolProperty::New(false) ); m_FeedbackNode->SetProperty( "layer", IntProperty::New( 100 ) ); m_FeedbackNode->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow(100, 1) ) ); m_FeedbackNode->SetProperty( "name", StringProperty::New("failsafe region grow feedback") ); m_FeedbackNode->SetProperty( "opacity", FloatProperty::New(0.3) ); m_FeedbackNode->SetProperty( "helper object", BoolProperty::New(true) ); m_FeedbackNode->SetVisibility(false); m_PointSetNode = mitk::DataNode::New(); m_PointSetNode->SetName("regiongrow3d pointset"); m_PointSetNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PointSetNode->SetProperty("layer", mitk::IntProperty::New(2)); mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); m_PointSetNode->SetData(pointSet); } mitk::RegionGrow3DTool::~RegionGrow3DTool() { } bool mitk::RegionGrow3DTool::OnMouseReleased(Action*, const StateEvent* stateEvent) { mitk::PointSetInteractor::Pointer interactor = dynamic_cast (m_PointSetNode->GetInteractor()); if (interactor.IsNotNull()) { mitk::PointSet::Pointer pointSet = dynamic_cast (m_PointSetNode->GetData()); if (pointSet.IsNotNull()) { if (pointSet->GetSize() == 1) { //check if we have a valid picture this->RunSegmentation(); SeedButtonToggled.Send(false); } } } return true; } void mitk::RegionGrow3DTool::Activated() { if (m_ToolManager) { m_ToolManager->RoiDataChanged += mitk::MessageDelegate(this, &mitk::RegionGrow3DTool::UpdatePreview); m_OriginalImageNode = m_ToolManager->GetReferenceData(0); m_NodeToProceed = m_OriginalImageNode; if (m_NodeToProceed.IsNotNull()) { SetupPreviewNodeFor(m_NodeToProceed); mitk::Image::Pointer image = dynamic_cast (m_OriginalImageNode->GetData()); if (image.IsNotNull()) { m_RoiMin = image->GetScalarValueMin(); m_RoiMax = image->GetScalarValueMax(); } } else m_ToolManager->ActivateTool(-1); } } void mitk::RegionGrow3DTool::Deactivated() { m_ToolManager->RoiDataChanged -= mitk::MessageDelegate(this, &mitk::RegionGrow3DTool::UpdatePreview); if (mitk::DataStorage* ds = m_ToolManager->GetDataStorage()) { ds->Remove(m_PointSetNode); ds->Remove(m_FeedbackNode); } m_FeedbackNode->SetData(NULL); m_FeedbackNode->SetLevelWindow(NULL); m_FeedbackNode->SetVisibility(false); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } const char* mitk::RegionGrow3DTool::GetName() const { return "RegionGrower 3D"; } const char** mitk::RegionGrow3DTool::GetXPM() const { return mitkRegionGrow3DTool_xpm; } void mitk::RegionGrow3DTool::SetSeedPoint(bool toggled) { if (!m_ToolManager->GetDataStorage()->Exists(m_PointSetNode)) //no pointSet present m_ToolManager->GetDataStorage()->Add(m_PointSetNode, m_OriginalImageNode); //add Interactor if there is none and add it to GlobalInteraction if (toggled == true) // button is down { mitk::PointSetInteractor::Pointer interactor = dynamic_cast (m_PointSetNode->GetInteractor()); if (interactor.IsNull()) { //create a new interactor and add it to node interactor = mitk::PointSetInteractor::New("singlepointinteractorwithoutshiftclick", m_PointSetNode, 1); } mitk::GlobalInteraction::GetInstance()->AddInteractor(interactor); } else { mitk::PointSetInteractor::Pointer interactor = dynamic_cast (m_PointSetNode->GetInteractor()); if (interactor.IsNotNull()) { m_PointSetNode->SetInteractor(NULL); mitk::GlobalInteraction::GetInstance()->RemoveInteractor(interactor); } } } void mitk::RegionGrow3DTool::RunSegmentation() { //safety if no pointSet or pointSet empty mitk::PointSet::Pointer seedPointSet = dynamic_cast (m_PointSetNode->GetData()); if (seedPointSet.IsNull()) { return; } if (!(seedPointSet->GetSize() > 0)) { return; } mitk::PointSet::PointType seedPoint = seedPointSet->GetPointSet()->GetPoints()->Begin().Value(); mitk::Image::Pointer image = dynamic_cast (m_NodeToProceed->GetData()); if (image.IsNotNull()) { m_LowerThreshold = static_cast (m_RoiMin); m_UpperThreshold = static_cast (m_RoiMax); AccessByItk_2(image, StartRegionGrowing, image->GetGeometry(), seedPoint); } } template void mitk::RegionGrow3DTool::StartRegionGrowing(itk::Image* itkImage, mitk::Geometry3D* imageGeometry, mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedAdaptiveThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); if ( !imageGeometry->IsInside(seedPoint) ) { return; } IndexType seedIndex; imageGeometry->WorldToIndex( seedPoint, seedIndex);// convert world coordinates to image indices //int seedValue = itkImage->GetPixel(seedIndex); regionGrower->SetInput( itkImage ); regionGrower->AddSeed( seedIndex ); regionGrower->SetLower( m_LowerThreshold ); regionGrower->SetUpper( m_UpperThreshold ); regionGrower->SetGrowingDirectionIsUpwards( m_CurrentRGDirectionIsUpwards ); try { regionGrower->Update(); } catch( ... ) { MITK_ERROR << "Something went wrong!" << endl; return; } m_SeedpointValue = regionGrower->GetSeedpointValue(); //initialize slider if(m_CurrentRGDirectionIsUpwards) { UpperThresholdValueChanged.Send(m_UpperThreshold); LowerThresholdValueChanged.Send(m_SeedpointValue); } else { UpperThresholdValueChanged.Send(m_SeedpointValue); LowerThresholdValueChanged.Send(m_LowerThreshold); } m_DetectedLeakagePoint = regionGrower->GetLeakagePoint(); mitk::Image::Pointer resultImage = mitk::ImportItkImage( regionGrower->GetOutput() ); m_FeedbackNode->SetData( resultImage ); m_FeedbackNode->SetVisibility(true); InitializeLevelWindow(); } void mitk::RegionGrow3DTool::InitializeLevelWindow() { mitk::LevelWindow tempLevelWindow; m_FeedbackNode->GetLevelWindow(tempLevelWindow, NULL, "levelWindow"); mitk::ScalarType* level = new mitk::ScalarType(0.5); mitk::ScalarType* window = new mitk::ScalarType(1); int upper; if (m_CurrentRGDirectionIsUpwards) { upper = m_UpperThreshold - m_SeedpointValue; } else { upper = m_SeedpointValue - m_LowerThreshold; } tempLevelWindow.SetRangeMinMax(mitk::ScalarType(0), mitk::ScalarType(upper)); tempLevelWindow.SetLevelWindow(*level, *window); m_FeedbackNode->SetLevelWindow(tempLevelWindow, NULL, "levelWindow"); //get the suggested threshold from the detected leakage-point and adjust the slider if (m_CurrentRGDirectionIsUpwards) { SliderValueChanged.Send(m_SeedpointValue + m_DetectedLeakagePoint -1); } else { SliderValueChanged.Send(m_SeedpointValue - m_DetectedLeakagePoint +1); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::RegionGrow3DTool::ChangeLevelWindow(int value) { if (m_FeedbackNode.IsNull()) return; mitk::LevelWindow tempLevelWindow; m_FeedbackNode->GetLevelWindow(tempLevelWindow, NULL, "levelWindow"); //get the levelWindow associated with the preview mitk::ScalarType level;// = this->m_UPPERTHRESHOLD - newValue + 0.5; mitk::ScalarType* window = new mitk::ScalarType(1); //adjust the levelwindow according to the position of the slider (newvalue) if (m_CurrentRGDirectionIsUpwards) { level = m_UpperThreshold - value + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } else { level = value - m_LowerThreshold +0.5; tempLevelWindow.SetLevelWindow(level, *window); } m_FeedbackNode->SetLevelWindow(tempLevelWindow, NULL, "levelWindow"); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::RegionGrow3DTool::ConfirmSegmentation( std::string name, mitk::Color color) { mitk::DataNode::Pointer new_node = mitk::DataNode::New(); new_node->SetColor(color); new_node->SetName(name); new_node->SetProperty("binary", mitk::BoolProperty::New("true")); mitk::Image* image = dynamic_cast (m_FeedbackNode->GetData()); mitk::LevelWindow tempLevelWindow; m_FeedbackNode->GetLevelWindow( tempLevelWindow, NULL, "levelWindow"); int upperThresholdLabeledImage = (short int) tempLevelWindow.GetRangeMax(); int lowerThresholdLabeledImage = (short int) tempLevelWindow.GetLowerWindowBound() + 1; typedef itk::Image InputImageType; typedef itk::Image SegmentationType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); InputImageType::Pointer itkImage; mitk::CastToItkImage(image, itkImage); filter->SetInput(itkImage); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->SetUpperThreshold(upperThresholdLabeledImage); filter->SetLowerThreshold(lowerThresholdLabeledImage); filter->Update(); mitk::Image::Pointer new_image = mitk::Image::New(); mitk::CastToMitkImage(filter->GetOutput(), new_image); //pad to original size if (m_OriginalImageNode.GetPointer() != m_NodeToProceed.GetPointer()) { mitk::PadImageFilter::Pointer padFilter = mitk::PadImageFilter::New(); padFilter->SetInput(0, new_image); padFilter->SetInput(1, dynamic_cast (m_OriginalImageNode->GetData())); padFilter->SetBinaryFilter(true); padFilter->SetUpperThreshold(1); padFilter->SetLowerThreshold(1); padFilter->Update(); new_image = padFilter->GetOutput(); } new_node->SetData(new_image); m_ToolManager->GetDataStorage()->Add(new_node); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_ToolManager->ActivateTool(-1); } void mitk::RegionGrow3DTool::CancelSegmentation() { m_ToolManager->ActivateTool(-1); } void mitk::RegionGrow3DTool::SetupPreviewNodeFor( DataNode* nodeToProceed) { if (nodeToProceed) { Image::Pointer image = dynamic_cast( nodeToProceed->GetData() ); if (image.IsNotNull()) { m_FeedbackNode->SetData( image ); int layer(50); nodeToProceed->GetIntProperty("layer", layer); m_FeedbackNode->SetIntProperty("layer", layer+1); m_FeedbackNode->SetLevelWindow(NULL); if (DataStorage* storage = m_ToolManager->GetDataStorage()) { if (storage->Exists(m_FeedbackNode)) storage->Remove(m_FeedbackNode); storage->Add( m_FeedbackNode, nodeToProceed ); } } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::RegionGrow3DTool::UpdatePreview() { typedef itk::Image ItkImageType; typedef itk::Image ItkMaskType; mitk::DataNode::Pointer node = m_ToolManager->GetRoiData(0); if (node.IsNull()) { this->SetupPreviewNodeFor(m_OriginalImageNode); m_NodeToProceed = m_OriginalImageNode; mitk::Image::Pointer image = dynamic_cast (m_OriginalImageNode->GetData()); if (image.IsNotNull()) { m_RoiMin = image->GetScalarValueMin(); m_RoiMax = image->GetScalarValueMax(); } return; } mitk::MaskAndCutRoiImageFilter::Pointer roiFilter = mitk::MaskAndCutRoiImageFilter::New(); roiFilter->SetInput(dynamic_cast (m_NodeToProceed->GetData())); roiFilter->SetRegionOfInterest(node->GetData()); roiFilter->Update(); mitk::DataNode::Pointer new_node = mitk::DataNode::New(); mitk::Image::Pointer tmpImage = roiFilter->GetOutput(); new_node->SetData(tmpImage); m_RoiMax = roiFilter->GetMaxValue(); m_RoiMin = roiFilter->GetMinValue(); this->SetupPreviewNodeFor(new_node); m_NodeToProceed = new_node; //this->RunSegmentation(); } void mitk::RegionGrow3DTool::SetCurrentRGDirectionIsUpwards(bool flag) { m_CurrentRGDirectionIsUpwards = flag; } diff --git a/Modules/MitkExt/Interactions/mitkRegionGrow3DTool.h b/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.h similarity index 96% rename from Modules/MitkExt/Interactions/mitkRegionGrow3DTool.h rename to Modules/Segmentation/Interactions/mitkRegionGrow3DTool.h index 85c9420576..50cea8a508 100644 --- a/Modules/MitkExt/Interactions/mitkRegionGrow3DTool.h +++ b/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.h @@ -1,83 +1,83 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 28959 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITKREGIONGROW3DTOOL_H #define MITKREGIONGROW3DTOOL_H #include "mitkTool.h" #include "mitkPointSet.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkStateEvent.h" #include "itkImage.h" namespace mitk{ - class MitkExt_EXPORT RegionGrow3DTool : public Tool + class Segmentation_EXPORT RegionGrow3DTool : public Tool { public: mitkClassMacro(RegionGrow3DTool, Tool); itkNewMacro(RegionGrow3DTool); mitk::Message1 UpperThresholdValueChanged; mitk::Message1 LowerThresholdValueChanged; mitk::Message1 SliderValueChanged; mitk::Message1 SeedButtonToggled; virtual const char* GetName() const; virtual const char** GetXPM() const; virtual void Activated(); virtual void Deactivated(); void RunSegmentation(); void ConfirmSegmentation(std::string name, mitk::Color color); void CancelSegmentation(); void InitializeLevelWindow(); void ChangeLevelWindow(int); void SetSeedPoint(bool); void SetCurrentRGDirectionIsUpwards(bool); protected: RegionGrow3DTool(); virtual ~RegionGrow3DTool(); void SetupPreviewNodeFor(mitk::DataNode* nodeToProceed); void UpdatePreview(); template < typename TPixel, unsigned int VImageDimension > void StartRegionGrowing( itk::Image< TPixel, VImageDimension >* itkImage, mitk::Geometry3D* imageGeometry, mitk::PointSet::PointType seedPoint ); bool OnMouseReleased(Action*, const StateEvent*); int m_SeedpointValue; mitk::ScalarType m_RoiMax; mitk::ScalarType m_RoiMin; int m_LowerThreshold; int m_UpperThreshold; int m_DetectedLeakagePoint; bool m_CurrentRGDirectionIsUpwards; mitk::DataNode::Pointer m_PointSetNode; mitk::DataNode::Pointer m_FeedbackNode; mitk::DataNode::Pointer m_NodeToProceed; mitk::DataNode::Pointer m_OriginalImageNode; };//class }//namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkRegionGrow3DTool.xpm b/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkRegionGrow3DTool.xpm rename to Modules/Segmentation/Interactions/mitkRegionGrow3DTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkRegionGrowingTool.cpp b/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp similarity index 99% rename from Modules/MitkExt/Interactions/mitkRegionGrowingTool.cpp rename to Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp index dc3d2c7696..3758bd04f5 100644 --- a/Modules/MitkExt/Interactions/mitkRegionGrowingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp @@ -1,661 +1,661 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkRegionGrowingTool.h" #include "mitkToolManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkImageDataItem.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkApplicationCursor.h" #include "ipSegmentation.h" #include "mitkRegionGrowingTool.xpm" #include "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkExtractDirectedPlaneImageFilterNew.h" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, RegionGrowingTool, "Region growing tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, RegionGrowingTool, "Region growing tool"); } #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) mitk::RegionGrowingTool::RegionGrowingTool() :FeedbackContourTool("PressMoveRelease"), m_LowerThreshold(200), m_UpperThreshold(200), m_InitialLowerThreshold(200), m_InitialUpperThreshold(200), m_ScreenYDifference(0), m_OriginalPicSlice(NULL), m_SeedPointMemoryOffset(0), m_VisibleWindow(0), m_DefaultWindow(0), m_MouseDistanceScaleFactor(0.5), m_LastWorkingSeed(-1), m_FillFeedbackContour(true) { } mitk::RegionGrowingTool::~RegionGrowingTool() { } const char** mitk::RegionGrowingTool::GetXPM() const { return mitkRegionGrowingTool_xpm; } const char* mitk::RegionGrowingTool::GetName() const { return "Region Growing"; } void mitk::RegionGrowingTool::Activated() { Superclass::Activated(); } void mitk::RegionGrowingTool::Deactivated() { Superclass::Deactivated(); } /** 1 Determine which slice is clicked into 2 Determine if the user clicked inside or outside of the segmentation 3 Depending on the pixel value under the mouse click position, two different things happen: (separated out into OnMousePressedInside and OnMousePressedOutside) 3.1 Create a skeletonization of the segmentation and try to find a nice cut 3.1.1 Call a ipSegmentation algorithm to create a nice cut 3.1.2 Set the result of this algorithm as the feedback contour 3.2 Initialize region growing 3.2.1 Determine memory offset inside the original image 3.2.2 Determine initial region growing parameters from the level window settings of the image 3.2.3 Perform a region growing (which generates a new feedback contour) */ bool mitk::RegionGrowingTool::OnMousePressed (Action* action, const StateEvent* stateEvent) { //ToolLogger::SetVerboseness(3); MITK_DEBUG << "OnMousePressed" << std::endl; if (FeedbackContourTool::OnMousePressed( action, stateEvent )) { MITK_DEBUG << "OnMousePressed: FeedbackContourTool says ok" << std::endl; // 1. Find out which slice the user clicked, find out which slice of the toolmanager's reference and working image corresponds to that const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (positionEvent) { MITK_DEBUG << "OnMousePressed: got positionEvent" << std::endl; m_ReferenceSlice = FeedbackContourTool::GetAffectedReferenceSlice( positionEvent ); m_WorkingSlice = FeedbackContourTool::GetAffectedWorkingSlice( positionEvent ); if ( m_WorkingSlice.IsNotNull() ) // can't do anything without the segmentation { MITK_DEBUG << "OnMousePressed: got working slice" << std::endl; // 2. Determine if the user clicked inside or outside of the segmentation const Geometry3D* workingSliceGeometry = m_WorkingSlice->GetGeometry(); Point3D mprojectedPointIn2D; workingSliceGeometry->WorldToIndex( positionEvent->GetWorldPosition(), mprojectedPointIn2D); itk::Index<2> projectedPointInWorkingSlice2D; projectedPointInWorkingSlice2D[0] = static_cast( mprojectedPointIn2D[0] - 0.5 ); projectedPointInWorkingSlice2D[1] = static_cast( mprojectedPointIn2D[1] - 0.5 ); if ( workingSliceGeometry->IsIndexInside( projectedPointInWorkingSlice2D ) ) { MITK_DEBUG << "OnMousePressed: point " << positionEvent->GetWorldPosition() << " (index coordinates " << projectedPointInWorkingSlice2D << ") IS in working slice" << std::endl; // Convert to ipMITKSegmentationTYPE (because getting pixels relys on that data type) itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer correctPixelTypeImage; CastToItkImage( m_WorkingSlice, correctPixelTypeImage ); assert (correctPixelTypeImage.IsNotNull() ); // possible bug in CastToItkImage ? // direction maxtrix is wrong/broken/not working after CastToItkImage, leading to a failed assertion in // mitk/Core/DataStructures/mitkSlicedGeometry3D.cpp, 479: // virtual void mitk::SlicedGeometry3D::SetSpacing(const mitk::Vector3D&): Assertion `aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0' failed // solution here: we overwrite it with an unity matrix itk::Image< ipMITKSegmentationTYPE, 2 >::DirectionType imageDirection; imageDirection.SetIdentity(); correctPixelTypeImage->SetDirection(imageDirection); Image::Pointer temporarySlice = Image::New(); // temporarySlice = ImportItkImage( correctPixelTypeImage ); CastToMitkImage( correctPixelTypeImage, temporarySlice ); mitkIpPicDescriptor* workingPicSlice = mitkIpPicNew(); CastToIpPicDescriptor(temporarySlice, workingPicSlice); int initialWorkingOffset = projectedPointInWorkingSlice2D[1] * workingPicSlice->n[0] + projectedPointInWorkingSlice2D[0]; if ( initialWorkingOffset < static_cast( workingPicSlice->n[0] * workingPicSlice->n[1] ) && initialWorkingOffset >= 0 ) { // 3. determine the pixel value under the last click bool inside = static_cast(workingPicSlice->data)[initialWorkingOffset] != 0; m_PaintingPixelValue = inside ? 0 : 1; // if inside, we want to remove a part, otherwise we want to add something if ( m_LastWorkingSeed >= static_cast( workingPicSlice->n[0] * workingPicSlice->n[1] ) || m_LastWorkingSeed < 0 ) { inside = false; } if ( m_ReferenceSlice.IsNotNull() ) { MITK_DEBUG << "OnMousePressed: got reference slice" << std::endl; m_OriginalPicSlice = mitkIpPicNew(); CastToIpPicDescriptor(m_ReferenceSlice, m_OriginalPicSlice); // 3.1. Switch depending on the pixel value if (inside) { OnMousePressedInside(action, stateEvent, workingPicSlice, initialWorkingOffset); } else { OnMousePressedOutside(action, stateEvent); } } } } } } } MITK_DEBUG << "end OnMousePressed" << std::endl; return true; } /** 3.1 Create a skeletonization of the segmentation and try to find a nice cut 3.1.1 Call a ipSegmentation algorithm to create a nice cut 3.1.2 Set the result of this algorithm as the feedback contour */ bool mitk::RegionGrowingTool::OnMousePressedInside(Action* itkNotUsed( action ), const StateEvent* stateEvent, mitkIpPicDescriptor* workingPicSlice, int initialWorkingOffset) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); // checked in OnMousePressed // 3.1.1. Create a skeletonization of the segmentation and try to find a nice cut // apply the skeletonization-and-cut algorithm // generate contour to remove // set m_ReferenceSlice = NULL so nothing will happen during mouse move // remember to fill the contour with 0 in mouserelease mitkIpPicDescriptor* segmentationHistory = ipMITKSegmentationCreateGrowerHistory( workingPicSlice, m_LastWorkingSeed, NULL ); // free again if (segmentationHistory) { tCutResult cutContour = ipMITKSegmentationGetCutPoints( workingPicSlice, segmentationHistory, initialWorkingOffset ); // tCutResult is a ipSegmentation type mitkIpPicFree( segmentationHistory ); if (cutContour.cutIt) { // 3.1.2 copy point from float* to mitk::Contour Contour::Pointer contourInImageIndexCoordinates = Contour::New(); contourInImageIndexCoordinates->Initialize(); Point3D newPoint; for (int index = 0; index < cutContour.deleteSize; ++index) { newPoint[0] = cutContour.deleteCurve[ 2 * index + 0 ]; newPoint[1] = cutContour.deleteCurve[ 2 * index + 1 ]; newPoint[2] = 0.0; contourInImageIndexCoordinates->AddVertex( newPoint - 0.5 ); } free(cutContour.traceline); free(cutContour.deleteCurve); // perhaps visualize this for fun? free(cutContour.onGradient); Contour::Pointer contourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( m_WorkingSlice->GetGeometry(), contourInImageIndexCoordinates, true ); // true: sub 0.5 for ipSegmentation correction FeedbackContourTool::SetFeedbackContour( *contourInWorldCoordinates ); FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); m_FillFeedbackContour = true; } else { m_FillFeedbackContour = false; } } else { m_FillFeedbackContour = false; } m_ReferenceSlice = NULL; return true; } /** 3.2 Initialize region growing 3.2.1 Determine memory offset inside the original image 3.2.2 Determine initial region growing parameters from the level window settings of the image 3.2.3 Perform a region growing (which generates a new feedback contour) */ bool mitk::RegionGrowingTool::OnMousePressedOutside(Action* itkNotUsed( action ), const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); // checked in OnMousePressed // 3.2 If we have a reference image, then perform an initial region growing, considering the reference image's level window // if click was outside the image, don't continue const Geometry3D* sliceGeometry = m_ReferenceSlice->GetGeometry(); Point3D mprojectedPointIn2D; sliceGeometry->WorldToIndex( positionEvent->GetWorldPosition(), mprojectedPointIn2D ); itk::Index<2> projectedPointIn2D; projectedPointIn2D[0] = static_cast( mprojectedPointIn2D[0] - 0.5 ); projectedPointIn2D[1] = static_cast( mprojectedPointIn2D[1] - 0.5 ); if ( sliceGeometry->IsIndexInside( mprojectedPointIn2D ) ) { MITK_DEBUG << "OnMousePressed: point " << positionEvent->GetWorldPosition() << " (index coordinates " << mprojectedPointIn2D << ") IS in reference slice" << std::endl; // 3.2.1 Remember Y cursor position and initial seed point //m_ScreenYPositionAtStart = static_cast(positionEvent->GetDisplayPosition()[1]); m_LastScreenPosition = ApplicationCursor::GetInstance()->GetCursorPosition(); m_ScreenYDifference = 0; m_SeedPointMemoryOffset = projectedPointIn2D[1] * m_OriginalPicSlice->n[0] + projectedPointIn2D[0]; m_LastWorkingSeed = m_SeedPointMemoryOffset; // remember for skeletonization if ( m_SeedPointMemoryOffset < static_cast( m_OriginalPicSlice->n[0] * m_OriginalPicSlice->n[1] ) && m_SeedPointMemoryOffset >= 0 ) { // 3.2.2 Get level window from reference DataNode // Use some logic to determine initial gray value bounds LevelWindow lw(0, 500); m_ToolManager->GetReferenceData(0)->GetLevelWindow(lw); // will fill lw if levelwindow property is present, otherwise won't touch it. ScalarType currentVisibleWindow = lw.GetWindow(); if (!mitk::Equal(currentVisibleWindow, m_VisibleWindow)) { m_InitialLowerThreshold = currentVisibleWindow / 20.0; m_InitialUpperThreshold = currentVisibleWindow / 20.0; m_LowerThreshold = m_InitialLowerThreshold; m_UpperThreshold = m_InitialUpperThreshold; // 3.2.3. Actually perform region growing mitkIpPicDescriptor* result = PerformRegionGrowingAndUpdateContour(); ipMITKSegmentationFree( result); // display the contour FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); m_FillFeedbackContour = true; } } return true; } return false; } /** If in region growing mode (m_ReferenceSlice != NULL), then 1. Calculate the new thresholds from mouse position (relative to first position) 2. Perform a new region growing and update the feedback contour */ bool mitk::RegionGrowingTool::OnMouseMoved(Action* action, const StateEvent* stateEvent) { if (FeedbackContourTool::OnMouseMoved( action, stateEvent )) { if ( m_ReferenceSlice.IsNotNull() && m_OriginalPicSlice ) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (positionEvent) { ApplicationCursor* cursor = ApplicationCursor::GetInstance(); if (!cursor) return false; m_ScreenYDifference += cursor->GetCursorPosition()[1] - m_LastScreenPosition[1]; cursor->SetCursorPosition( m_LastScreenPosition ); m_LowerThreshold = std::max(0.0, m_InitialLowerThreshold - m_ScreenYDifference * m_MouseDistanceScaleFactor); m_UpperThreshold = std::max(0.0, m_InitialUpperThreshold - m_ScreenYDifference * m_MouseDistanceScaleFactor); // 2. Perform region growing again and show the result mitkIpPicDescriptor* result = PerformRegionGrowingAndUpdateContour(); ipMITKSegmentationFree( result ); // 3. Update the contour mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(positionEvent->GetSender()->GetRenderWindow()); } } } return true; } /** If the feedback contour should be filled, then it is done here. (Contour is NOT filled, when skeletonization is done but no nice cut was found) */ bool mitk::RegionGrowingTool::OnMouseReleased(Action* action, const StateEvent* stateEvent) { if (FeedbackContourTool::OnMouseReleased( action, stateEvent )) { // 1. If we have a working slice, use the contour to fill a new piece on segmentation on it (or erase a piece that was selected by ipMITKSegmentationGetCutPoints) if ( m_WorkingSlice.IsNotNull() && m_OriginalPicSlice ) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (positionEvent) { // remember parameters for next time m_InitialLowerThreshold = m_LowerThreshold; m_InitialUpperThreshold = m_UpperThreshold; if (m_FillFeedbackContour) { // 3. use contour to fill a region in our working slice Contour* feedbackContour( FeedbackContourTool::GetFeedbackContour() ); if (feedbackContour) { Contour::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( m_WorkingSlice, feedbackContour, false, false ); // false: don't add any 0.5 // false: don't constrain the contour to the image's inside if (projectedContour.IsNotNull()) { FeedbackContourTool::FillContourInSlice( projectedContour, m_WorkingSlice, m_PaintingPixelValue ); const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); //MITK_DEBUG << "OnMouseReleased: writing back to dimension " << affectedDimension << ", slice " << affectedSlice << " in working image" << std::endl; // 4. write working slice back into image volume this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice); } } } FeedbackContourTool::SetFeedbackContourVisible(false); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); } } } m_ReferenceSlice = NULL; // don't leak m_WorkingSlice = NULL; m_OriginalPicSlice = NULL; return true; } /** Uses ipSegmentation algorithms to do the actual region growing. The result (binary image) is first smoothed by a 5x5 circle mask, then its contour is extracted and converted to MITK coordinates. */ mitkIpPicDescriptor* mitk::RegionGrowingTool::PerformRegionGrowingAndUpdateContour() { // 1. m_OriginalPicSlice and m_SeedPointMemoryOffset are set to sensitive values, as well as m_LowerThreshold and m_UpperThreshold assert (m_OriginalPicSlice); if (m_OriginalPicSlice->n[0] != 256 || m_OriginalPicSlice->n[1] != 256) // ??? assert( (m_SeedPointMemoryOffset < static_cast( m_OriginalPicSlice->n[0] * m_OriginalPicSlice->n[1] )) && (m_SeedPointMemoryOffset >= 0) ); // inside the image // 2. ipSegmentation is used to perform region growing float ignored; int oneContourOffset( 0 ); mitkIpPicDescriptor* regionGrowerResult = ipMITKSegmentationGrowRegion4N( m_OriginalPicSlice, m_SeedPointMemoryOffset, // seed point true, // grayvalue interval relative to seed point gray value? m_LowerThreshold, m_UpperThreshold, 0, // continue until done (maxIterations == 0) NULL, // allocate new memory (only this time, on mouse move we'll reuse the old buffer) oneContourOffset, // a pixel that is near the resulting contour ignored // ignored by us ); if (!regionGrowerResult || oneContourOffset == -1) { Contour::Pointer dummyContour = Contour::New(); dummyContour->Initialize(); FeedbackContourTool::SetFeedbackContour( *dummyContour ); if (regionGrowerResult) ipMITKSegmentationFree(regionGrowerResult); return NULL; } // 3. We smooth the result a little to reduce contour complexity bool smoothResult( true ); // currently fixed, perhaps remove else block mitkIpPicDescriptor* smoothedRegionGrowerResult; if (smoothResult) { // Smooth the result (otherwise very detailed contour) smoothedRegionGrowerResult = SmoothIPPicBinaryImage( regionGrowerResult, oneContourOffset ); ipMITKSegmentationFree( regionGrowerResult ); } else { smoothedRegionGrowerResult = regionGrowerResult; } // 4. convert the result of region growing into a mitk::Contour // At this point oneContourOffset could be useless, if smoothing destroyed a thin bridge. In these // cases, we have two or more unconnected segmentation regions, and we don't know, which one is touched by oneContourOffset. // In the bad case, the contour is not the one around our seedpoint, so the result looks very strange to the user. // -> we remove the point where the contour started so far. Then we look from the bottom of the image for the first segmentation pixel // and start another contour extraction from there. This is done, until the seedpoint is inside the contour int numberOfContourPoints( 0 ); int newBufferSize( 0 ); float* contourPoints = ipMITKSegmentationGetContour8N( smoothedRegionGrowerResult, oneContourOffset, numberOfContourPoints, newBufferSize ); // memory allocated with malloc if (contourPoints) { while ( !ipMITKSegmentationIsInsideContour( contourPoints, // contour numberOfContourPoints, // points in contour m_SeedPointMemoryOffset % smoothedRegionGrowerResult->n[0], // test point x m_SeedPointMemoryOffset / smoothedRegionGrowerResult->n[0] // test point y ) ) { // we decide that this cannot be part of the segmentation because the seedpoint is not contained in the contour (fill the 4-neighborhood with 0) ipMITKSegmentationReplaceRegion4N( smoothedRegionGrowerResult, oneContourOffset, 0 ); // move the contour offset to the last row (x position of the seed point) int rowLength = smoothedRegionGrowerResult->n[0]; // number of pixels in a row oneContourOffset = m_SeedPointMemoryOffset % smoothedRegionGrowerResult->n[0] // x of seed point + rowLength*(smoothedRegionGrowerResult->n[1]-1); // y of last row while ( oneContourOffset >=0 && (*(static_cast(smoothedRegionGrowerResult->data) + oneContourOffset) == 0) ) { oneContourOffset -= rowLength; // if pixel at data+oneContourOffset is 0, then move up one row } if ( oneContourOffset < 0 ) { break; // just use the last contour we found } free(contourPoints); // release contour memory contourPoints = ipMITKSegmentationGetContour8N( smoothedRegionGrowerResult, oneContourOffset, numberOfContourPoints, newBufferSize ); // memory allocated with malloc } // copy point from float* to mitk::Contour Contour::Pointer contourInImageIndexCoordinates = Contour::New(); contourInImageIndexCoordinates->Initialize(); Point3D newPoint; for (int index = 0; index < numberOfContourPoints; ++index) { newPoint[0] = contourPoints[ 2 * index + 0 ]; newPoint[1] = contourPoints[ 2 * index + 1 ]; newPoint[2] = 0; contourInImageIndexCoordinates->AddVertex( newPoint - 0.5); } free(contourPoints); Contour::Pointer contourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( m_ReferenceSlice->GetGeometry(), contourInImageIndexCoordinates, true ); // true: sub 0.5 for ipSegmentation correctio FeedbackContourTool::SetFeedbackContour( *contourInWorldCoordinates ); } // 5. Result HAS TO BE freed by caller, contains the binary region growing result return smoothedRegionGrowerResult; } /** Helper method for SmoothIPPicBinaryImage. Smoothes a given part of and image. \param sourceImage The original binary image. \param dest The smoothed image (will be written without bounds checking). \param contourOfs One offset of the contour. Is updated if a pixel is changed (which might change the contour). \param maskOffsets Memory offsets that describe the smoothing mask. \param maskSize Entries of the mask. \param startOffset First pixel that should be smoothed using this mask. \param endOffset Last pixel that should be smoothed using this mask. */ void mitk::RegionGrowingTool::SmoothIPPicBinaryImageHelperForRows( mitkIpPicDescriptor* sourceImage, mitkIpPicDescriptor* dest, int &contourOfs, int* maskOffsets, int maskSize, int startOffset, int endOffset ) { // work on the very first row ipMITKSegmentationTYPE* current; ipMITKSegmentationTYPE* source = ((ipMITKSegmentationTYPE*)sourceImage->data) + startOffset; // + 1! don't read at start-1 ipMITKSegmentationTYPE* end = ((ipMITKSegmentationTYPE*)dest->data) + endOffset; int ofs = startOffset; int minority = (maskSize - 1) / 2; for (current = ((ipMITKSegmentationTYPE*)dest->data) + startOffset; current minority) { *current = 1; contourOfs = ofs; } else { *current = 0; } ++source; ++ofs; } } /** Smoothes a binary ipPic image with a 5x5 mask. The image borders (some first and last rows) are treated differently. */ mitkIpPicDescriptor* mitk::RegionGrowingTool::SmoothIPPicBinaryImage( mitkIpPicDescriptor* image, int &contourOfs, mitkIpPicDescriptor* dest ) { if (!image) return NULL; // Original code from /trunk/mbi-qm/Qmitk/Qmitk2DSegTools/RegionGrowerTool.cpp (first version by T. Boettger?). Reformatted and documented and restructured. #define MSK_SIZE5x5 21 #define MSK_SIZE3x3 5 #define MSK_SIZE3x1 3 // mask is an array of coordinates that form a rastered circle like this // // OOO // OOOOO // OOOOO // OOOOO // OOO // // int mask5x5[MSK_SIZE5x5][2] = { /******/ {-1,-2}, {0,-2}, {1,-2}, /*****/ {-2,-1}, {-1,-1}, {0,-1}, {1,-1}, {2,-1}, {-2, 0}, {-1, 0}, {0, 0}, {1, 0}, {2, 0}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, /******/ {-1, 2}, {0, 2}, {1, 2} /*****/ }; int mask3x3[MSK_SIZE3x3][2] = { /******/ {0,-1}, /*****/ {-1, 0}, {0, 0}, {1, 0}, /******/ {0, 1} /*****/ }; int mask3x1[MSK_SIZE3x1][2] = { {-1, 0}, {0, 0}, {1, 0} }; // The following lines iterate over all the pixels of a (sliced) image (except the first and last three rows). // For each pixel, all the coordinates around it (according to mask) are evaluated (this means 21 pixels). // If more than 10 of the evaluated pixels are non-zero, then the central pixel is set to 1, else to 0. // This is determining a majority. If there is no clear majority, then the central pixel itself "decides". int maskOffset5x5[MSK_SIZE5x5]; int line = image->n[0]; for (int i=0; in[0]; int spareOut1Rows = 1*image->n[0]; if ( image->n[1] > 0 ) SmoothIPPicBinaryImageHelperForRows( image, dest, contourOfs, maskOffset3x1, MSK_SIZE3x1, 1, dest->n[0] ); if ( image->n[1] > 3 ) SmoothIPPicBinaryImageHelperForRows( image, dest, contourOfs, maskOffset3x3, MSK_SIZE3x3, spareOut1Rows, dest->n[0]*3 ); if ( image->n[1] > 6 ) SmoothIPPicBinaryImageHelperForRows( image, dest, contourOfs, maskOffset5x5, MSK_SIZE5x5, spareOut3Rows, dest->n[0]*dest->n[1] - spareOut3Rows ); if ( image->n[1] > 8 ) SmoothIPPicBinaryImageHelperForRows( image, dest, contourOfs, maskOffset3x3, MSK_SIZE3x3, dest->n[0]*dest->n[1] -spareOut3Rows, dest->n[0]*dest->n[1] - spareOut1Rows ); if ( image->n[1] > 10) SmoothIPPicBinaryImageHelperForRows( image, dest, contourOfs, maskOffset3x1, MSK_SIZE3x1, dest->n[0]*dest->n[1] -spareOut1Rows, dest->n[0]*dest->n[1] - 1 ); // correction for first pixel (sorry for the ugliness) if ( *((ipMITKSegmentationTYPE*)(dest->data)+1) == 1 ) { *((ipMITKSegmentationTYPE*)(dest->data)+0) = 1; } if (dest->n[0] * dest->n[1] > 2) { // correction for last pixel if ( *((ipMITKSegmentationTYPE*)(dest->data)+dest->n[0]*dest->n[1]-2) == 1 ) { *((ipMITKSegmentationTYPE*)(dest->data)+dest->n[0]*dest->n[1]-1) = 1; } } return dest; } diff --git a/Modules/MitkExt/Interactions/mitkRegionGrowingTool.h b/Modules/Segmentation/Interactions/mitkRegionGrowingTool.h similarity index 97% rename from Modules/MitkExt/Interactions/mitkRegionGrowingTool.h rename to Modules/Segmentation/Interactions/mitkRegionGrowingTool.h index c01334b366..ee9a8690c9 100644 --- a/Modules/MitkExt/Interactions/mitkRegionGrowingTool.h +++ b/Modules/Segmentation/Interactions/mitkRegionGrowingTool.h @@ -1,115 +1,115 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkRegionGrowingTool_h_Included #define mitkRegionGrowingTool_h_Included #include "mitkFeedbackContourTool.h" #include "mitkLegacyAdaptors.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" struct mitkIpPicDescriptor; namespace mitk { /** \brief A slice based region growing tool. \sa FeedbackContourTool \ingroup Interaction \ingroup ToolManagerEtAl When the user presses the mouse button, RegionGrowingTool will use the gray values at that position to initialize a region growing algorithm (in the affected 2D slice). By moving the mouse up and down while the button is still pressed, the user can change the parameters of the region growing algorithm (selecting more or less of an object). The current result of region growing will always be shown as a contour to the user. After releasing the button, the current result of the region growing algorithm will be written to the working image of this tool's ToolManager. If the first click is inside a segmentation that was generated by region growing (recently), the tool will try to cut off a part of the segmentation. For this reason a skeletonization of the segmentation is generated and the optimal cut point is determined. \warning Only to be instantiated by mitk::ToolManager. $Author$ */ -class MitkExt_EXPORT RegionGrowingTool : public FeedbackContourTool +class Segmentation_EXPORT RegionGrowingTool : public FeedbackContourTool { public: mitkClassMacro(RegionGrowingTool, FeedbackContourTool); itkNewMacro(RegionGrowingTool); virtual const char** GetXPM() const; virtual const char* GetName() const; protected: RegionGrowingTool(); // purposely hidden virtual ~RegionGrowingTool(); virtual void Activated(); virtual void Deactivated(); virtual bool OnMousePressed (Action*, const StateEvent*); virtual bool OnMousePressedInside (Action*, const StateEvent*, mitkIpPicDescriptor* workingPicSlice, int initialWorkingOffset); virtual bool OnMousePressedOutside (Action*, const StateEvent*); virtual bool OnMouseMoved (Action*, const StateEvent*); virtual bool OnMouseReleased(Action*, const StateEvent*); mitkIpPicDescriptor* PerformRegionGrowingAndUpdateContour(); Image::Pointer m_ReferenceSlice; Image::Pointer m_WorkingSlice; ScalarType m_LowerThreshold; ScalarType m_UpperThreshold; ScalarType m_InitialLowerThreshold; ScalarType m_InitialUpperThreshold; Point2I m_LastScreenPosition; int m_ScreenYDifference; private: mitkIpPicDescriptor* SmoothIPPicBinaryImage( mitkIpPicDescriptor* image, int &contourOfs, mitkIpPicDescriptor* dest = NULL ); void SmoothIPPicBinaryImageHelperForRows( mitkIpPicDescriptor* source, mitkIpPicDescriptor* dest, int &contourOfs, int* maskOffsets, int maskSize, int startOffset, int endOffset ); mitkIpPicDescriptor* m_OriginalPicSlice; int m_SeedPointMemoryOffset; ScalarType m_VisibleWindow; ScalarType m_DefaultWindow; ScalarType m_MouseDistanceScaleFactor; int m_PaintingPixelValue; int m_LastWorkingSeed; bool m_FillFeedbackContour; }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkRegionGrowingTool.xpm b/Modules/Segmentation/Interactions/mitkRegionGrowingTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkRegionGrowingTool.xpm rename to Modules/Segmentation/Interactions/mitkRegionGrowingTool.xpm diff --git a/Modules/MitkExt/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp similarity index 100% rename from Modules/MitkExt/Interactions/mitkSegTool2D.cpp rename to Modules/Segmentation/Interactions/mitkSegTool2D.cpp diff --git a/Modules/MitkExt/Interactions/mitkSegTool2D.h b/Modules/Segmentation/Interactions/mitkSegTool2D.h similarity index 98% rename from Modules/MitkExt/Interactions/mitkSegTool2D.h rename to Modules/Segmentation/Interactions/mitkSegTool2D.h index cc126bc16e..b2b3482014 100644 --- a/Modules/MitkExt/Interactions/mitkSegTool2D.h +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.h @@ -1,136 +1,136 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkSegTool2D_h_Included #define mitkSegTool2D_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkTool.h" #include "mitkImage.h" #include "mitkStateEvent.h" #include "mitkPositionEvent.h" #include "mitkPlanePositionManager.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkInteractionConst.h" namespace mitk { class BaseRenderer; /** \brief Abstract base class for segmentation tools. \sa Tool \ingroup Interaction \ingroup ToolManagerEtAl Implements 2D segmentation specific helper methods, that might be of use to all kind of 2D segmentation tools. At the moment these are: - Determination of the slice where the user paints upon (DetermineAffectedImageSlice) - Projection of a 3D contour onto a 2D plane/slice SegTool2D tries to structure the interaction a bit. If you pass "PressMoveRelease" as the interaction type of your derived tool, you might implement the methods OnMousePressed, OnMouseMoved, and OnMouseReleased. Yes, your guess about when they are called is correct. \warning Only to be instantiated by mitk::ToolManager. $Author$ */ -class MitkExt_EXPORT SegTool2D : public Tool +class Segmentation_EXPORT SegTool2D : public Tool { public: mitkClassMacro(SegTool2D, Tool); /** \brief Calculates for a given Image and PlaneGeometry, which slice of the image (in index corrdinates) is meant by the plane. \return false, if no slice direction seems right (e.g. rotated planes) \param affectedDimension The image dimension, which is constant for all points in the plane, e.g. Transversal --> 2 \param affectedSlice The index of the image slice */ static bool DetermineAffectedImageSlice( const Image* image, const PlaneGeometry* plane, int& affectedDimension, int& affectedSlice ); void SetShowMarkerNodes(bool); void Enable3DInterpolation(bool); protected: SegTool2D(); // purposely hidden SegTool2D(const char*); // purposely hidden virtual ~SegTool2D(); virtual bool OnMousePressed (Action*, const StateEvent*); virtual bool OnMouseMoved (Action*, const StateEvent*); virtual bool OnMouseReleased(Action*, const StateEvent*); virtual bool OnInvertLogic (Action*, const StateEvent*); /** \brief Extract the slice of an image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position. */ Image::Pointer GetAffectedImageSliceAs2DImage(const PositionEvent*, const Image* image); /** \brief Extract the slice of the currently selected working image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no working image is selected. */ Image::Pointer GetAffectedWorkingSlice(const PositionEvent*); /** \brief Extract the slice of the currently selected reference image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no reference image is selected. */ Image::Pointer GetAffectedReferenceSlice(const PositionEvent*); void WriteBackSegmentationResult (const PositionEvent*, Image*); /** \brief Adds a new node called Contourmarker to the datastorage which holds a mitk::PlanarFigure. By selecting this node the slicestack will be reoriented according to the PlanarFigure's Geometry */ unsigned int AddContourmarker ( const PositionEvent* ); void InteractiveSegmentationBugMessage( const std::string& message ); private: BaseRenderer* m_LastEventSender; unsigned int m_LastEventSlice; //The prefix of the contourmarkername. Suffix is a consecutive number const std::string m_Contourmarkername; bool m_ShowMarkerNodes; bool m_3DInterpolationEnabled; }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkSegmentationsProcessingTool.cpp b/Modules/Segmentation/Interactions/mitkSegmentationsProcessingTool.cpp similarity index 100% rename from Modules/MitkExt/Interactions/mitkSegmentationsProcessingTool.cpp rename to Modules/Segmentation/Interactions/mitkSegmentationsProcessingTool.cpp diff --git a/Modules/MitkExt/Interactions/mitkSegmentationsProcessingTool.h b/Modules/Segmentation/Interactions/mitkSegmentationsProcessingTool.h similarity index 96% rename from Modules/MitkExt/Interactions/mitkSegmentationsProcessingTool.h rename to Modules/Segmentation/Interactions/mitkSegmentationsProcessingTool.h index 99cffcdf07..04f7e2d198 100644 --- a/Modules/MitkExt/Interactions/mitkSegmentationsProcessingTool.h +++ b/Modules/Segmentation/Interactions/mitkSegmentationsProcessingTool.h @@ -1,102 +1,102 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.0 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkSegmentationsProcessingTool_h_Included #define mitkSegmentationsProcessingTool_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkTool.h" #include "mitkDataNode.h" namespace mitk { /** \brief Batch processing of all selected segmentations/data This class is undocumented. Ask the creator ($Author$) to supply useful comments. */ -class MitkExt_EXPORT SegmentationsProcessingTool : public Tool +class Segmentation_EXPORT SegmentationsProcessingTool : public Tool { public: mitkClassMacro(SegmentationsProcessingTool, Tool); protected: SegmentationsProcessingTool(); // purposely hidden virtual ~SegmentationsProcessingTool(); virtual const char* GetGroup() const; /** \brief Called when the tool gets activated Will just call ProcessAllObjects and then deactivate this tool again. */ virtual void Activated(); virtual void Deactivated(); /** \brief Loop over all working data items Will call the following methods in turn - StartProcessingAllData - ProcessOneWorkingData(DataNode*) for each node in the working data list of our tool manager - FinishProcessingAllData Subclasses should override these methods as needed and provide meaningful implementations. */ virtual void ProcessAllObjects(); /** \brief Subclasses should override this method \sa ProcessAllObjects */ virtual void StartProcessingAllData(); /** \brief Subclasses should override this method \sa ProcessAllObjects */ virtual bool ProcessOneWorkingData( DataNode* node ); /** \brief Subclasses should override this method \sa ProcessAllObjects */ virtual void FinishProcessingAllData(); /** \brief Sends an error message if there was an error during processing. */ virtual void SendErrorMessageIfAny(); /** \brief Describes the error (if one occurred during processing). */ virtual std::string GetErrorMessage(); std::string m_FailedNodes; }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkSetRegionTool.cpp b/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp similarity index 100% rename from Modules/MitkExt/Interactions/mitkSetRegionTool.cpp rename to Modules/Segmentation/Interactions/mitkSetRegionTool.cpp diff --git a/Modules/MitkExt/Interactions/mitkSetRegionTool.h b/Modules/Segmentation/Interactions/mitkSetRegionTool.h similarity index 94% rename from Modules/MitkExt/Interactions/mitkSetRegionTool.h rename to Modules/Segmentation/Interactions/mitkSetRegionTool.h index 8ab316636c..b36385f262 100644 --- a/Modules/MitkExt/Interactions/mitkSetRegionTool.h +++ b/Modules/Segmentation/Interactions/mitkSetRegionTool.h @@ -1,78 +1,78 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkSetRegionTool_h_Included #define mitkSetRegionTool_h_Included #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkFeedbackContourTool.h" namespace mitk { class Image; /** \brief Fills or erases a 2D region \sa FeedbackContourTool \sa ExtractImageFilter \sa OverwriteSliceImageFilter \ingroup Interaction \ingroup ToolManagerEtAl Finds the outer contour of a shape in 2D (possibly including holes) and sets all the inside pixels to a specified value. This might fill holes or erase segmentations. \warning Only to be instantiated by mitk::ToolManager. $Author$ */ -class MitkExt_EXPORT SetRegionTool : public FeedbackContourTool +class Segmentation_EXPORT SetRegionTool : public FeedbackContourTool { public: mitkClassMacro(SetRegionTool, FeedbackContourTool); protected: SetRegionTool(int paintingPixelValue = 1); // purposely hidden virtual ~SetRegionTool(); virtual void Activated(); virtual void Deactivated(); virtual bool OnMousePressed (Action*, const StateEvent*); virtual bool OnMouseReleased(Action*, const StateEvent*); virtual bool OnInvertLogic (Action*, const StateEvent*); int m_PaintingPixelValue; bool m_FillContour; bool m_StatusFillWholeSlice; Contour::Pointer m_SegmentationContourInWorldCoordinates; Contour::Pointer m_WholeImageContourInWorldCoordinates; }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkSubtractContourTool.cpp b/Modules/Segmentation/Interactions/mitkSubtractContourTool.cpp similarity index 93% rename from Modules/MitkExt/Interactions/mitkSubtractContourTool.cpp rename to Modules/Segmentation/Interactions/mitkSubtractContourTool.cpp index c8fbd4bca2..4d6af47257 100644 --- a/Modules/MitkExt/Interactions/mitkSubtractContourTool.cpp +++ b/Modules/Segmentation/Interactions/mitkSubtractContourTool.cpp @@ -1,45 +1,45 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkSubtractContourTool.h" #include "mitkSubtractContourTool.xpm" namespace mitk { - MITK_TOOL_MACRO(MitkExt_EXPORT, SubtractContourTool, "Subtract tool"); + MITK_TOOL_MACRO(Segmentation_EXPORT, SubtractContourTool, "Subtract tool"); } mitk::SubtractContourTool::SubtractContourTool() :ContourTool(0) { FeedbackContourTool::SetFeedbackContourColor( 1.0, 0.0, 0.0 ); } mitk::SubtractContourTool::~SubtractContourTool() { } const char** mitk::SubtractContourTool::GetXPM() const { return mitkSubtractContourTool_xpm; } const char* mitk::SubtractContourTool::GetName() const { return "Subtract"; } diff --git a/Modules/MitkExt/Interactions/mitkSubtractContourTool.h b/Modules/Segmentation/Interactions/mitkSubtractContourTool.h similarity index 94% rename from Modules/MitkExt/Interactions/mitkSubtractContourTool.h rename to Modules/Segmentation/Interactions/mitkSubtractContourTool.h index 372b5cb6b4..529c893d3b 100644 --- a/Modules/MitkExt/Interactions/mitkSubtractContourTool.h +++ b/Modules/Segmentation/Interactions/mitkSubtractContourTool.h @@ -1,68 +1,68 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkSubtractContourTool_h_Included #define mitkSubtractContourTool_h_Included #include "mitkContourTool.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" namespace mitk { /** \brief Fill the inside of a contour with 1 \sa ContourTool \ingroup Interaction \ingroup ToolManagerEtAl Fills a visible contour (from FeedbackContourTool) during mouse dragging. When the mouse button is released, SubtractContourTool tries to extract a slice from the working image and fill in the (filled) contour as a binary image. All inside pixels are set to 0. While holding the CTRL key, the contour changes color and the pixels on the inside would be filled with 1. \warning Only to be instantiated by mitk::ToolManager. $Author$ */ -class MitkExt_EXPORT SubtractContourTool : public ContourTool +class Segmentation_EXPORT SubtractContourTool : public ContourTool { public: mitkClassMacro(SubtractContourTool, ContourTool); itkNewMacro(SubtractContourTool); virtual const char** GetXPM() const; virtual const char* GetName() const; protected: SubtractContourTool(); // purposely hidden virtual ~SubtractContourTool(); }; } // namespace #endif diff --git a/Modules/MitkExt/Interactions/mitkSubtractContourTool.xpm b/Modules/Segmentation/Interactions/mitkSubtractContourTool.xpm similarity index 100% rename from Modules/MitkExt/Interactions/mitkSubtractContourTool.xpm rename to Modules/Segmentation/Interactions/mitkSubtractContourTool.xpm diff --git a/Modules/MitkExt/Rendering/mitkContourMapper2D.cpp b/Modules/Segmentation/Rendering/mitkContourMapper2D.cpp similarity index 100% rename from Modules/MitkExt/Rendering/mitkContourMapper2D.cpp rename to Modules/Segmentation/Rendering/mitkContourMapper2D.cpp diff --git a/Modules/MitkExt/Rendering/mitkContourMapper2D.h b/Modules/Segmentation/Rendering/mitkContourMapper2D.h similarity index 93% rename from Modules/MitkExt/Rendering/mitkContourMapper2D.h rename to Modules/Segmentation/Rendering/mitkContourMapper2D.h index 4bc7ce4eef..97fda995ab 100644 --- a/Modules/MitkExt/Rendering/mitkContourMapper2D.h +++ b/Modules/Segmentation/Rendering/mitkContourMapper2D.h @@ -1,66 +1,66 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITK_CONTOUR_MAPPER_2D_H_ #define MITK_CONTOUR_MAPPER_2D_H_ #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkGLMapper2D.h" namespace mitk { class BaseRenderer; class Contour; /** * @brief OpenGL-based mapper to display a mitk::Contour object in a 2D render window * * * @ingroup Mapper */ -class MitkExt_EXPORT ContourMapper2D : public GLMapper2D +class Segmentation_EXPORT ContourMapper2D : public GLMapper2D { public: mitkClassMacro(ContourMapper2D, Mapper2D); itkNewMacro(Self); /** * reimplemented from Baseclass */ virtual void Paint(BaseRenderer * renderer); /** * return a refernce of the rendered data object */ const Contour* GetInput(void); protected: ContourMapper2D(); virtual ~ContourMapper2D(); }; } // namespace mitk #endif /* MITKContourMapper2D_H_HEADER_INCLUDED */ diff --git a/Modules/MitkExt/Rendering/mitkContourSetMapper2D.cpp b/Modules/Segmentation/Rendering/mitkContourSetMapper2D.cpp similarity index 100% rename from Modules/MitkExt/Rendering/mitkContourSetMapper2D.cpp rename to Modules/Segmentation/Rendering/mitkContourSetMapper2D.cpp diff --git a/Modules/MitkExt/Rendering/mitkContourSetMapper2D.h b/Modules/Segmentation/Rendering/mitkContourSetMapper2D.h similarity index 93% rename from Modules/MitkExt/Rendering/mitkContourSetMapper2D.h rename to Modules/Segmentation/Rendering/mitkContourSetMapper2D.h index 3f71393784..d494839a0f 100644 --- a/Modules/MitkExt/Rendering/mitkContourSetMapper2D.h +++ b/Modules/Segmentation/Rendering/mitkContourSetMapper2D.h @@ -1,66 +1,66 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITK_CONTOUR_SET_MAPPER_2D_H_ #define MITK_CONTOUR_SET_MAPPER_2D_H_ #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkGLMapper2D.h" namespace mitk { class BaseRenderer; class ContourSet; class BaseRenderer; /** * @brief OpenGL-based mapper to display a mitk::Contour object in a 2D render window * * * @ingroup Mapper */ -class MitkExt_EXPORT ContourSetMapper2D : public GLMapper2D +class Segmentation_EXPORT ContourSetMapper2D : public GLMapper2D { public: mitkClassMacro(ContourSetMapper2D, Mapper2D); itkNewMacro(Self); /** * reimplemented from Baseclass */ virtual void Paint(mitk::BaseRenderer * renderer); /** * return a refernce of the rendered data object */ const mitk::ContourSet * GetInput(void); protected: ContourSetMapper2D(); virtual ~ContourSetMapper2D(); }; } // namespace mitk #endif /* MITK_CONTOUR_SET_MAPPER_2D_H_ */ diff --git a/Modules/MitkExt/Rendering/mitkContourSetVtkMapper3D.cpp b/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp similarity index 100% rename from Modules/MitkExt/Rendering/mitkContourSetVtkMapper3D.cpp rename to Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp diff --git a/Modules/MitkExt/Rendering/mitkContourSetVtkMapper3D.h b/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.h similarity index 94% rename from Modules/MitkExt/Rendering/mitkContourSetVtkMapper3D.h rename to Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.h index c1d21e9c4e..ef8cd4ae77 100644 --- a/Modules/MitkExt/Rendering/mitkContourSetVtkMapper3D.h +++ b/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.h @@ -1,70 +1,70 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITK_CONTOUR_SET_VTK_MAPPER_3D_H #define MITK_CONTOUR_SET_VTK_MAPPER_3D_H #include "mitkCommon.h" -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkVtkMapper3D.h" #include "mitkContourSet.h" #include "mitkBaseRenderer.h" #include class vtkPolyDataMapper; class vtkAppendPolyData; class vtkActor; class vtkTubeFilter; namespace mitk { //##Documentation //## @brief Vtk-based mapper for mitk::Contour //## @ingroup Mapper -class MitkExt_EXPORT ContourSetVtkMapper3D : public VtkMapper3D +class Segmentation_EXPORT ContourSetVtkMapper3D : public VtkMapper3D { public: mitkClassMacro(ContourSetVtkMapper3D, VtkMapper3D); itkNewMacro(Self); virtual const mitk::ContourSet* GetInput(); virtual vtkProp* GetVtkProp(mitk::BaseRenderer* renderer); protected: ContourSetVtkMapper3D(); virtual ~ContourSetVtkMapper3D(); virtual void GenerateDataForRenderer(mitk::BaseRenderer* renderer); vtkPolyDataMapper* m_VtkPolyDataMapper; vtkTubeFilter* m_TubeFilter; vtkPolyData *m_ContourSet; vtkActor *m_Actor; }; } // namespace mitk #endif // MITK_CONTOUR_VTK_MAPPER_3D_H diff --git a/Modules/MitkExt/Rendering/mitkContourVtkMapper3D.cpp b/Modules/Segmentation/Rendering/mitkContourVtkMapper3D.cpp similarity index 100% rename from Modules/MitkExt/Rendering/mitkContourVtkMapper3D.cpp rename to Modules/Segmentation/Rendering/mitkContourVtkMapper3D.cpp diff --git a/Modules/MitkExt/Rendering/mitkContourVtkMapper3D.h b/Modules/Segmentation/Rendering/mitkContourVtkMapper3D.h similarity index 94% rename from Modules/MitkExt/Rendering/mitkContourVtkMapper3D.h rename to Modules/Segmentation/Rendering/mitkContourVtkMapper3D.h index cef2d7200e..d149d2522d 100644 --- a/Modules/MitkExt/Rendering/mitkContourVtkMapper3D.h +++ b/Modules/Segmentation/Rendering/mitkContourVtkMapper3D.h @@ -1,69 +1,69 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITK_CONTOUR_VTK_MAPPER_3D_H #define MITK_CONTOUR_VTK_MAPPER_3D_H -#include "MitkExtExports.h" +#include "SegmentationExports.h" #include "mitkVtkMapper3D.h" #include #include class vtkPolyDataMapper; class vtkAppendPolyData; class vtkActor; class vtkTubeFilter; namespace mitk { class BaseRenderer; class Contour; /** @brief Vtk-based mapper for mitk::Contour @ingroup Mapper */ -class MitkExt_EXPORT ContourVtkMapper3D : public VtkMapper3D +class Segmentation_EXPORT ContourVtkMapper3D : public VtkMapper3D { public: mitkClassMacro(ContourVtkMapper3D, VtkMapper3D); itkNewMacro(Self); virtual const mitk::Contour* GetInput(); virtual vtkProp* GetVtkProp(mitk::BaseRenderer* renderer); protected: ContourVtkMapper3D(); virtual ~ContourVtkMapper3D(); virtual void GenerateDataForRenderer(mitk::BaseRenderer* renderer); vtkSmartPointer m_VtkPolyDataMapper; vtkSmartPointer m_TubeFilter; vtkSmartPointer m_VtkPointList; vtkSmartPointer m_Contour; vtkSmartPointer m_Actor; }; } // namespace mitk #endif // MITK_CONTOUR_VTK_MAPPER_3D_H diff --git a/Modules/Segmentation/Testing/CMakeLists.txt b/Modules/Segmentation/Testing/CMakeLists.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Modules/Segmentation/Testing/Data/img.nrrd b/Modules/Segmentation/Testing/Data/img.nrrd new file mode 100644 index 0000000000..04a51b53cb --- /dev/null +++ b/Modules/Segmentation/Testing/Data/img.nrrd @@ -0,0 +1,17 @@ +NRRD0004 +# Complete NRRD file format specification at: +# http://teem.sourceforge.net/nrrd/format.html +content: SomeIDNumber42 +type: short +dimension: 3 +space: right-anterior-superior +sizes: 3 3 1 +thicknesses: 1 1 1 +space directions: (1,0,0) (0,1,0) (0,0,1) +centerings: cell cell cell +kinds: space space space +encoding: ASCII +space units: "mm" "mm" "mm" +space origin: (0,0,0) + +1 2 3 4 5 6 7 8 9 diff --git a/Modules/Segmentation/Testing/Data/lungs.vtk b/Modules/Segmentation/Testing/Data/lungs.vtk new file mode 100644 index 0000000000..5b0f7f17ac --- /dev/null +++ b/Modules/Segmentation/Testing/Data/lungs.vtk @@ -0,0 +1,18985 @@ +# vtk DataFile Version 3.0 +vtk output +ASCII +DATASET POLYDATA +POINTS 7860 float +59 174 77.8 95 170 80.2059 55 171 80.2222 +56 171 79.2353 56 172 78.5806 57 172 78.1765 +58 173 78.2 59 173 78.6923 61 173 79.6452 +87 173 80.4194 88 173 79.8 89 173 79.4571 +91 173 80.4706 58 174 78.5172 60 174 78.1765 +89 174 80.4706 91 174 80.8235 59 175 78.5172 +60 175 78.0909 63 175 79.4571 88 175 80.5588 +64 176 80.1429 63 177 80.2059 65 177 80.2059 +67 177 80.1429 81 177 80.4706 82 177 79.8 +84 177 79.8 62 177.087 81 64 177.5 81 +68 178 80.2059 80 178 80.2059 82 178 80.2059 +69 179 79.8529 77 179 80.2059 79 179 79.8 +80 179 80.2059 81 179 80.8235 70 180 80.5455 +74 180 80.4706 114 150 83.4706 113 151 83.4706 +112.739 151 84 114 152 82.8529 39 155 83.8 +38.6667 155 84 113 155 82.8 114 155 82.8 +39 156 83.1429 38.3333 156 84 40 156 83.4706 +114 156 83.1429 40 157 83.1429 40 158 83.8 +40.6667 158 84 42 158.833 84 41.3333 159 84 +109 159 83.4706 112 159 81.1765 113 159 81.4545 +43 160 83.2059 108 160 83.4706 109 160 82.8 +111 160 81.5294 112 160 81.1765 113 160 82.3 +110 161 82.2 111 161 81.5143 112 161 81.8182 +44 162 82.8 45 162 82.8 107 162 83.0323 +109 162 83.0323 110 162 82.2 111 162 82.2 +112 162 83.4828 46 163 83.4194 110 163 83.2059 +46.25 164 84 47 164 83.4194 106 164 82.2 +109 164 83.4706 104 165 83.4 105 165 82.4545 +106 165 82.2 108 165 83.4706 49 166 82.8 +50 166 82.8 51.1667 166 84 103 166 83.9 +51.4 167 84 99 167 83.2174 101 167 81.9 +96 168 83.4828 97 168 82.7 98 168 82.8182 +101 168 81.8824 95 169 81.8824 96 169 81.9677 +97 169 82.5556 93 170 82.3636 93 169.333 84 +96 170 81.2222 54 171 82.6552 93 171 81.6667 +94 171 81.1936 91 172 81.6207 92 172 81.9677 +93 172 81.6 94 172 81.8824 95 172 81.5294 +97 172 83.8846 68 173 83.3333 76 173 83.4706 +85 173 83.6842 86 173 81.7826 93 173 82.2 +71 174 82.6452 76 174 82.4571 84 174 83.6842 +85 174 83.8333 92 174 81.5294 57 174.727 84 +75 175 82.6452 80 175 82.4571 82 175 82.0714 +86 175 81.6 69 176 82.6452 71 176 82.8529 +72 176 83.8064 74 176 83.4194 80 176 82.2 +87 176 81.4545 60.2609 177 84 75 177 82.4571 +87 177 81.5294 89 177 82.8529 81 181.25 84 +115 146.357 87 36 149 86.4194 114 149 84.1765 +36 150 86.4194 37 150 86.8 36.0909 151 87 +37 151 86.0323 37 152 85.5556 37 153 86.0526 +38 153 86.6842 111 153 86.9091 37 154 86.1667 +38 154 84.7826 39 154 85.8261 38 155 84.6667 +38 156 85.0714 37.6538 156 87 41 157 84.6818 +41 158 84.1364 40 159 85.8571 41 159 84.1364 +44 158.278 87 108 159 84.5294 41 160 85.2273 +45.5 160 87 41.2 161 87 47 163 84.2727 +46 164 84.2308 52 165 85.3548 100 165 86.5455 +102 165 84.5294 53 166 86.0323 52 167 84.2647 +106 167 84.5294 51 168 84.9677 53 168 84.4545 +95 167.63 87 53 169 84.2647 92 168.75 87 +105 169 86.2059 67 169.75 87 89 170 84.8824 +98 170 85.56 98 171 85.7727 72 172 85 +74 172 85.5556 82 172 84.5294 84 172 84.6923 +98 172 85.3043 99 172 85.2 73 173 85.8261 +74 173 85.8261 84 173 84.8333 73 174 85.1538 +99 174 86.8235 73 175 85 74 175 84.5172 +184.833 178 87 186 178 85.8462 186 179 85 +188 179 84.9677 189 179 85.3 191 179 85.1818 +193 179 84.375 195 179 85.6552 196 179 86.069 +198 179 85.4545 198 178.261 87 201 179 86.4706 +202 179 86.2059 186 180 86.8235 189 180 85.3548 +190 180 84.6 191 180 84.6 193 180 84.4286 +196 180 86.1 198 180 85.5 200 180 86.2059 +201 179.75 87 193 181 86.8966 68 181.706 87 +82 182 84.5294 84 183.087 87 117 143 89.8 +118 143 89.4706 118 142.25 90 117 144 88.5 +118 144 89.4706 115 145 89.2174 116 145 87.9677 +117 145 88.2 34 146 89.6087 115 146 87.6818 +116 146 87.5769 117 146 89 35 147 87.9677 +116 147 87.2308 35 148 87.5769 36 148 87.4545 +35 149 87.5455 35.7727 152 90 39 153 89.7273 +151 155 88.8 42.6429 157 90 152 157 88.8 +153 157 89.1429 43 157.278 90 44 157.105 90 +45.4545 158 90 152 158 89.8 153 158 89.8 +45.8947 159 90 153 159 89.9 47.2857 161 90 +52 162 89.55 48 162.286 90 55 164 88.3043 +56 164 89.2174 98 164 88.8529 55 165 87.6923 +56 165 89.2174 96 165 89.2059 95.1818 165 90 +99 165 87.9677 45 165.346 90 56 166 88 +63 167 87.375 64.6 167 90 92.7692 167 90 +95 167 89.3182 94.375 167 90 64.3 168 90 +68.3333 168 90 93 167.75 90 93 168 89.5714 +94 168 89 94 167.75 90 72 168.474 90 +73 168.333 90 66 169.4 90 50 170.105 90 +174 175 89.4 175 175 88.8 176 175 89.4194 +176 176 89.0323 177 176 88.6452 178.6 176 90 +181 176 89.4231 181 177 89.3077 181 178 89.04 +182 178 88.7586 183 178 89.3182 184 178 89 +202 178 87.5172 204 178 88.5 182 179 89.4194 +183 179 89.7391 184 179 88.875 204 179 88.1538 +181 180 89.8235 183 180 89.4194 202 180 88 +119 140 92.7778 120 140 91.6452 118 141 91.9615 +119 141 90.8571 120 141 91.2 118 142 90.2 +119 142 90.1765 120 142 91.5 120.654 142 93 +119 143 90.5294 150 145 92.8 149.714 145 93 +150 146 91.8 150 147 91.5 151 147 91.8529 +117.895 148 93 150 148 91.8529 151 148 91.5 +151 149 90.5294 151 150 90.1765 36.5909 153 93 +39 154.5 93 39.1053 155 93 39.1667 156 93 +40 156.526 93 154 157 90.5294 156.105 159 93 +46 159.563 93 50.75 160 93 153 160 91.1471 +155 160 91.7 45.3333 161 93 51 161 91.0714 +52 161 91.2273 154 161 90.6 45.8667 162 93 +51 162 90.5 50.6875 162 93 154 162 91.7 +156 161.455 93 155 163 92.7391 155.25 163 93 +47 164 92.3077 46.25 164 93 159 164 91.5 +47 164.857 93 63 165 90.6667 92 165 90.8824 +159 165 90.8824 45 165.591 93 46.5455 166 93 +64 166 90.2609 66 166 92.6842 67 166 92.8636 +92 166 90.2 93 166 91.0385 158 166 91.5 +160 166 90.5806 161 166 92.3077 46.6 167 93 +65 166.684 93 69 166.4 93 70 166.227 93 +71 167 91.5556 72 167 92 75 167 90.6 +93 167 92.25 94 167 92.25 161 167 92.7391 +161.5 167 93 68.3333 168 93 72 168 91.5 +94 168 90.2143 160 168 92.4444 161 168 92.25 +162 168 91.5556 48.4 169 93 67 169 90.2727 +162 170 91.2353 51.2857 171 93 66 170.208 93 +167 171 92.8966 52.625 173 93 170 173 92.8235 +173 173 92.7778 174 174 90.2308 170 175 92.1429 +180 175 91.5 171 176 92.4194 172 175.091 93 +172 176 91.8889 174 176 90.2222 179 176 90.5455 +185 175.618 93 179 177 92.7 183 177 90.375 +177 178 91.2 178 178 92.7 178.333 178 93 +179 177.333 93 179.143 178 93 180 178 90.6 +176 178.833 93 177 178.818 93 177.286 179 93 +178 179 92.2105 179 179 91.5 204.833 179 93 +180 180 90.5294 204 180.172 93 121 139 93.2727 +123.105 139 96 33 140.857 96 34 141 94.8462 +34 142 93.9 35 143 93.6818 149 143 94.8889 +150 144 94.1818 149 145 93.7895 40 151 94.5 +40.0714 153 96 40 153.071 96 39.3529 156 96 +149.588 156 96 46 156.591 96 45 157.133 96 +48 158 94.8 47 159 93.8571 49 159 94.0714 +50.1818 159 96 45.6667 160 96 47 159.625 96 +52 159.895 96 45.5714 161 96 157 161 93.2308 +51 162 94.0714 52 161.722 96 52.8333 162 96 +54 161.5 96 156 162 95.25 158 162 93.2609 +159 162 93.6 41 163 94.8261 46 163 94.125 +51 162.692 96 52 163 95.4 52.75 163 96 +156 163 95.5714 46 164 93.3158 45.2273 164 96 +52 163.6 96 90 164 93.3214 155 164 93.6667 +43 165 94.1111 46 165 93.75 47 165 93.75 +156 165 93.2 45 166 94.5882 45.7273 166 96 +47 165.273 96 46.2727 166 96 54 166 94 +54.2 166 96 69.25 166 96 162 166 94.7727 +46 166.375 96 46 167 94.9286 54 166.667 96 +64.4615 167 96 69 166.5 96 155 166.474 96 +163 166.455 96 47 168 94.5 72 168 94.5 +94.55 168 96 164 168 94.1111 165 168 95.1 +46 169 95.8846 46.1667 169 96 47.5 169 96 +72 169 95.3182 164 169 94.1111 165 169 94.5556 +166 169 94.8 52 170 93.2 94 169.688 96 +166 170 93.4286 167 170 93.5455 51 170.625 96 +163 171 93.6 166 171 94.3636 168 171 94.375 +168 170.133 96 170 171 94.8 52 172 94 +52.1818 172 96 162 172 94.5 163 172 94.3 +164 172 95.7391 165 172 95.8636 167 172 94.3548 +168 172 94.3548 173 172 93.5172 52 172.5 96 +59 173 95.125 164 173 95.4194 165 173 95.0323 +171 173 93.1818 172 173 93.4545 216 173 95.8 +75 174 95 74.8421 174 96 76 173.533 96 +76 174 95 76.6364 174 96 167 174 95.1429 +172 174 93.8182 214 174 95.4706 168 175 94.5 +172 175 93.1765 183 174.941 96 170 177 94.5 +178 176.65 96 179 176.316 96 204.323 177 96 +176.85 178 96 178 178 93.2 179.591 178 96 +176 179 93.3158 177 179 93.8571 177 178.375 96 +178 178.609 96 177 180 94.1538 68 184.056 96 +124 138.071 99 33 139.357 99 34 140.056 99 +149 141 98.2174 150 140.5 99 149 142 96.2609 +115 143.111 99 41 153 98.6 40 154 98.0526 +41 154 98.7 42 154 98.6 43 154 97.9286 +43 153.773 99 44 155 97.5 44 156 98.1667 +44.7143 156 99 46 157 98.4545 42.8182 158 99 +45 158 97.4444 47 158 98.4706 48 158 98.25 +44 158.846 99 45 159 98.6 46 159 98 +46.75 159 99 47 158.75 99 46 160 97.6364 +46 159.375 99 47 160 97 49.1818 160 99 +51 160 97.5 46 161 97.2 47 161 97.5 +51 160.667 99 53 160.545 99 45.2778 162 99 +51 161.333 99 53 161.625 99 53 162 96.75 +161 162 97.8529 49.9333 163 99 52 162.2 99 +59 163 98.25 44.3158 164 99 60 163.667 99 +64 163.933 99 59 165 97 60 164.333 99 +60 165 97.6364 64.8182 165 99 66 165 97.125 +66.7143 165 99 68.75 165 99 47 166 97.8462 +47.3333 166 99 54 166 96.4 59 166 96 +58.2632 166 99 60 166 97.0909 65 166 98.7143 +64.8182 166 99 66 166 98.0526 66.75 166 99 +69 166 96.4286 70 165.833 99 224 166 98.8 +225 166 98.8 225 165.75 99 46 167 97.5 +47.8182 167 99 59 167 97.3636 58.25 167 99 +60 167 97.7143 67 166.286 99 69 167 96.75 +70 167 98.6 70 166.5 99 70.3333 167 99 +72 167 98.6 71.6667 167 99 72 166.5 99 +223 167 97.8529 224 167 97.6452 47 168 96.75 +46.4545 168 99 47.8571 168 99 56 168 98.7273 +55.8667 168 99 56 167.867 99 56.5 168 99 +61.8182 168 99 64.25 168 99 70 168 96.2609 +71 167.222 99 71 168 96.7895 73 168 97 +74 167.357 99 74 168 97.7727 222 168 97.8529 +223 168 97.3 224 168 98.7391 47 169 97.3636 +56 168.286 99 62 169 97.6552 62.9286 169 99 +64.25 169 99 69 169 96.9 71 169 97.35 +74.1 169 99 92.7 169 99 167 168.2 99 +221 169 97.7 222 169 97.3 223 169 98.7391 +46 170 97.5 52 169.462 99 54.2 170 99 +62 170 97.8182 62.6842 170 99 68 169.875 99 +69 170 98.25 68.5 170 99 70 170 97.1739 +71 170 97.25 72 170 97.5 72.9091 170 99 +74 169.056 99 77 170 97.5 76.4545 170 99 +77 169.571 99 170 170 96.4545 220 170 96.9 +221 170 97.0385 222 170 98.7391 61 171 98.7931 +60.7778 171 99 61.2857 171 99 69 170.5 99 +70 171 97.3333 71 171 97.25 72 171 98.625 +77 171 97.8 77.8571 171 99 94 170.294 99 +164 171 96.2727 165 171 96.4091 218 171 97.4 +219 171 96.6 220 171 96.2 221 171 98.2174 +49 172 96.3333 54 171.75 99 59 171.519 99 +60 172 98 69.1429 172 99 71 172 96.3 +72.4375 172 99 77 171.75 99 216 172 97.1818 +216 171.231 99 217 172 97.2414 218 172 96.5172 +219 172 97.5 220 172 98.7273 48 172.722 99 +50 173 97.875 51.5 173 99 58 173 96.5172 +57.1724 173 99 61 172.286 99 215 173 96.5294 +217 173 96.5294 218 173 97.3548 51 174 98.2174 +67 173.818 99 67.25 174 99 75.5 174 99 +78 174 98.7273 78.1053 174 99 180 173.097 99 +211 174 98.88 216 174 96.5294 58 174.737 99 +67 175 97.8462 66.0909 175 99 76 174.643 99 +77 175 98.4 77.75 175 99 78 174.667 99 +210 175 97.25 212 175 97.8261 213 175 96.6 +214 175 96.2 67 176 98.069 66.3571 176 99 +67.9 176 99 77 175.188 99 178 175.923 99 +178.25 176 99 209 176 96.4545 210 176 96.1154 +211 176 97.56 212 176 98.4444 213 176 98.1 +214 176 98.4231 67 176.5 99 179 176.75 99 +179.095 177 99 210 177 96.5806 212 176.833 99 +214 176.172 99 177 178.579 99 178 178.467 99 +63 183.375 99 66 183.818 99 67 184 98 +151 137 101.824 32 138 101.609 33 137.333 102 +150 138 100.696 33 139 99.5769 150 139 99.6667 +149 140 100.8 123 141 100.579 122 144 100.962 +120.8 145 102 107.773 150 102 41 152 99.5455 +34.5882 153 102 45 155 100.579 45.3913 155 102 +41 156 100.154 42 156 100.111 45 156 99.3158 +42 157 99.2727 43 157 100.111 46 157 99.6 +47 157 101.7 44 158 101.75 47 158 102 +48 158 100 43.8421 159 102 50 159 100.071 +51 160 99.4286 50 161 100.826 54 161 101.7 +55 161 101.5 158 161 101.667 157.25 161 102 +158 160.813 102 51 162 99.75 52 162 99.5455 +53 162 100.125 56 161.077 102 158 161.75 102 +227 162 101.1 228 162 101.739 46 162.444 102 +49 162.667 102 49 163 101.8 50 162.2 102 +60 162.944 102 226 163 100.5 227 163 99.9 +225 164 101.1 226 164 99.9 227 164 100.227 +47 165 101.167 59 165 101.727 65 165 99.8571 +66 165 101.5 80 164.567 102 225 165 99.5294 +226 165 100.111 46 166 101.824 60.875 166 102 +65 166 101 65.2 166 102 66 165.2 102 +66.8 166 102 226 165.944 102 46 167 99.9375 +47 167 101.077 60.75 167 102 61.1429 167 102 +62 166.455 102 64 167 100.5 65 167 100.364 +66 167 100.286 70 167 99.8571 72 166.563 102 +167 167 100.5 47 168 100.636 56 168 99.8571 +65 168 100.2 66 168 100.773 67 168 101.654 +74 167.105 102 94 168 101.786 95.375 168 102 +167 168 99.5455 62.6364 169 102 66 169 101.727 +67 169 101.8 68 169 101.8 75 168.571 102 +75 169 101.25 76 168.455 102 76 169 100.2 +95.9167 169 102 54 169.591 102 55 170 101.182 +56 169.167 102 56 170 100.5 61 170 99.8182 +64.4545 170 102 67 170 101.7 73.5455 170 102 +75 169.75 102 76 169.857 102 76.1429 170 102 +52 170.474 102 53 170.143 102 54.3333 171 102 +61 171 99.6 51 172 102 58 171.727 102 +60 172 101 73 171.667 102 72.9333 172 102 +78 171.917 102 60.25 173 102 72 173 101.182 +78 173 101.25 78 172.333 102 79.5 173 102 +180 172.316 102 67.3333 174 102 68 174 101.25 +68.6667 174 102 77 174 100.579 77.75 174 102 +79 174 102 187 174 101.419 188 174 101.8 +206.895 174 102 58 174.25 102 66 175 102 +68 175 102 77 175 99.6 78 174.75 102 +207 175 101 65.5625 176 102 68.1364 176 102 +173 176 100.688 173 175.632 102 176.565 176 102 +215 176 100.826 215.6 176 102 161 176.074 102 +212 177 99.1034 176.8 178 102 203.5 178 102 +212 178 100.556 203 178.867 102 210 179 100.355 +202.091 180 102 203 181 100.75 66 185 100.08 +200 184.172 102 232 99 104.8 232 98.7143 105 +232 99.5 105 235 111 104.824 235 112 104.824 +151 133 104.471 32 136 103.6 31.3 136 105 +33 135.867 105 150 137 102.167 153 141 103.2 +154 142 104.8 154.75 143 105 230 156 104.419 +49 157 104.318 229 157 104.8 45 158 103.313 +46 158 103.615 229 158 103.7 230 158 104.053 +44 159 103.125 45 159 102.75 46 159 103.8 +53 158.316 105 228 159 104.206 229 159 103.5 +37.1765 160 105 54 160 103.5 164 160 103.8 +229 160 103.5 157.533 161 105 158 160.417 105 +158.875 161 105 228 161 102.682 228.944 161 105 +49 162 102.273 50 162 102.316 59.8 162 105 +62 162 104.545 158 161.875 105 46 163 104 +48 163 102.375 225 163 103.111 45.1429 164 105 +46 163.455 105 47 164 103.05 45 165 103.286 +60 165 102.75 167 165 102.529 46 165.25 105 +60 166 103.105 63 166 105 65 166 102.429 +60 167 102.45 61.8571 167 105 62 166.333 105 +62.6667 167 105 64 167 104.5 63.5 167 105 +64.1 167 105 65 166.4 105 64 167.25 105 +68 168 102.261 69 168 103.962 71 168 103.556 +72 168 104.778 73 168 104.333 74 168 104.684 +96.7143 168 105 166 168 103.875 166.75 168 105 +62 169 103.05 69 169 103.304 71 169 103.304 +73 169 104.063 75 169 102.818 76 169 102.947 +97.5455 169 105 54 170 103.929 55 170 103.125 +56 170 104.143 62 170 103.235 65 170 103.636 +66 170 103.65 74 169.273 105 77 170 103 +52 171 103 53 171 102.692 54 171 103 +71 171 103.304 73 171 102.4 78 171 104.75 +178 170.261 105 179 171 103 181 171 104.419 +52 172 103.179 58 172 103.125 72 172 103.826 +77 171.286 105 182 172 104 184 172 102.882 +188 172 104.8 58 173 103.826 59 173 103.5 +70 173 103.125 72 173 103.8 78 173 103.5 +78.4 173 105 81 173 105 181 173 103.5 +182 173 103.909 188 173 102.783 207 173 104.333 +208.125 173 105 59 174 102.692 69.75 174 105 +80 173.857 105 198 174 102.667 206 174 104.217 +210 173.273 105 67 175 103.615 67.75 175 105 +68.2857 175 105 88 175 104 87.25 175 105 +88.75 175 105 176 175 104.308 176 174.455 105 +177 174.87 105 177 175 104.438 66 176 104.625 +67 176 104.625 68 176 103 88 175.75 105 +173.231 176 105 176 176 104.438 178.067 176 105 +204.091 178 105 203.25 179 105 200.571 180 105 +201 179.4 105 203 179.056 105 206 180 103.8 +201.75 181 105 205 181 104.333 206 181 103.8 +207 181 104.1 173 182 103.56 175.667 185 105 +230 94 107.4 230 93.1429 108 231 95 106.889 +231 96 106.355 231 97 106.182 232 97 106.636 +232 98 105.577 231 99 106.5 233 99 106.333 +233 98.1667 108 232 100 105.2 233.292 100 108 +233 101.5 108 233 102 107.684 233 103 106.111 +233 104 105.882 233 105 106.182 234 105 105.682 +234 106 105.222 235 105.75 108 234 107 105.2 +234 108 105.529 235 110 105.577 235.913 110 108 +234 111 105.529 236 111 106.421 236.292 114 108 +235 116 106.355 235 117 107.032 235 119 107.8 +235 120 107.8 236 120 107.739 236 121 106.696 +236 123 105.667 236 125 106.889 236 126 107.333 +235 127 107.824 236 128 106.355 236 130 107.1 +150 131 107.778 150 132 105.9 149.192 132 108 +151 132 105.818 235 132 107.8 31 135 106.8 +31 134.143 108 32 135 105.136 31 136 107 +33 135.133 108 235 136 107.471 120 139.435 108 +235 140 106.889 155 141 105.667 234 141 107.471 +235 141 107.217 234 142 107.143 235 141.857 108 +234 143 107.032 155 144 105.545 156 144 107.625 +234 144 107.333 233 145 107.824 162 151 107.471 +231 156 106.714 230.933 157 108 225.933 159 108 +57 160 107.684 168 160 107.471 57 161 106 +58 161 106.636 59 161 105.316 60 161 105.783 +158 161 106.05 58 162 108 59 162 106.2 +63 162 106 82 161.833 108 83 162 107.8 +83 161.833 108 84 162 106.759 160 162 106.714 +159.455 162 108 160 161.455 108 160.75 162 108 +46 164 105.6 64 166 106.286 65 166 106.636 +66 166 105.75 173 166 106.2 174 166 107.032 +176 166 107.778 62 167 105.333 64 167 105.231 +174 167 107.55 176 167 107.739 177 167 106.962 +216 167 107.1 168.13 168 108 174 168 105.75 +177 168 106.889 178 168 106.889 218 168 105.2 +74 169 105.333 166 168.25 108 167.2 169 108 +168 168.2 108 179 169 105.529 181 169 106.2 +183 170 105.529 192 169.333 108 195 170 107.471 +207 170 107.8 77 171 105.316 96 170.111 108 +197 171 106.645 198 171 106.457 208 171 106.457 +82 172 107.25 82 171.625 108 82.75 172 108 +197 172 106.5 198 172 106.826 203.5 172 108 +205.75 172 108 207 172 106.826 208 172 106.355 +80 173 106.2 82 172.375 108 175 173 107.087 +174.563 173 108 200 173 107.885 203 173 107.912 +205.455 173 108 209 173 106.105 210 173 105.474 +68 174 105.273 69 174 105.6 90 174 107.526 +89.75 174 108 90.75 174 108 175 174 106.895 +176 174 106.25 203 174 107.5 68 175 105.24 +88 175 105.818 90 174.75 108 177 174.895 108 +206 175 106.5 205.833 175 108 171 176 107.5 +171 175.333 108 172.231 176 108 176 175.933 108 +177.13 176 108 205 175.909 108 205 176 107.864 +206 176 106.636 205 178 105.882 204 179 105.29 +201 180 107.25 202 180 107.211 203 180 106.962 +202 181 106 203 181 107.348 169 182 106.235 +203 182 106.826 204 182 106.2 205 182 105.455 +171 183 105.176 173 183 105.778 204 183 107.1 +205 183 107.778 172 184 106.5 86 189.824 108 +227 88 110.4 226.5 88 111 228 89 109.444 +228 90 108.9 228 91 109.2 229 91 109.174 +229 92 108.667 230 91.8182 111 229 93 108.581 +230 93 108.2 233 101 109.5 232 102 108.24 +230.167 107 111 231 108 110.824 230.833 108 111 +232 109 109.182 236 119 108.545 236.64 125 111 +150 129.966 111 234.692 130 111 149 130.231 111 +148.63 131 111 235 131 109.227 236 131 108.273 +236 132 108.158 31 133 110.25 30.5882 135 111 +128 135 110.423 129 135 110.206 128 136 108.231 +129 136 110.1 129 136.818 111 157 138.643 111 +157 140 109.304 232.316 140 111 235.806 140 111 +156 141 108.136 156 142 109.08 157 142 109.4 +156 143 109.227 158 143 109.313 157 144 108.182 +232 145 109.2 234 145 108.682 234.567 145 111 +162 146 110.143 161 147 109.355 231 147 110.824 +233 147 110.864 164 148 110.471 232 148 110.909 +162 149 109.75 232 149 110.423 232.714 149 111 +231 150 110.423 232.929 150 111 231 151 109.696 +232 151 108.789 231 152 109.421 232 152 109 +231 153 110.053 230 154 108.692 170 157 109.457 +168 158 109.2 171 158 109.457 60 159 110.1 +168 159 108.529 172 159 109.457 223 158.875 111 +224 159 110.739 170 160 109.2 171 160 109.457 +172 160 109.8 174 160 110.143 82 161 108.909 +173 161 109.886 175 161 109.457 177 161 110.206 +177.818 161 111 105 162 110.25 106 162 109.2 +174 162 109.8 175 162 109.457 178 162 110.8 +220 162 110.25 220 161.417 111 106 163 109 +106.25 163 111 160 163 108.429 161 163 109.364 +161.2 163 111 175 163 108.882 161 164 109 +178 164 110.333 160 164.909 111 161.13 165 111 +179 165 108.968 184 165 109.457 183 166 109.457 +185 166 109.457 210 166 110.471 214 166 109.2 +215 166 108.882 66 167 109.6 65.125 167 111 +66 166.417 111 67 166.625 111 67.2727 167 111 +184 167 110.333 185 167 110.679 186 167 110.032 +187 167 109.457 194 167 110.8 193.5 167 111 +195 166.625 111 209 167 110.125 215 167 108.529 +66 168 109.5 65.25 168 111 67 168 110.308 +166.696 168 111 169 167.563 111 169 168 110.222 +170 168 110.735 171 168 110.735 172 167.87 111 +185 168 110.211 187 168 108.529 192 168 110.211 +192.714 168 111 193.25 168 111 195 168 109.645 +201 168 109.853 203 168 110.684 204 168 110.684 +207 167.5 111 210 168 109.826 213 168 110.864 +214 168 110.04 92.6842 169 111 167 168.875 111 +168 168.947 111 169 168.438 111 172 169 109.645 +173 169 109.429 192.818 169 111 194 169 109.5 +193.25 169 111 202 169 110.625 205 169 110.471 +207 169 108.75 93.25 170 111 173 170 109.35 +173.688 170 111 193 169.25 111 94 170.4 111 +95 170.625 111 173 171 111 82 172 108.818 +186 172 110.55 185.727 172 111 205 172 109.636 +205 171.375 111 92 173 110.55 91.25 173 111 +93 172.455 111 93 173 110.4 175 173 110.625 +186 172.375 111 195 173 110.471 195 172.571 111 +196 172.444 111 196 173 109.5 205 173 110.143 +90.5 174 111 91 173.2 111 91 174 108.6 +92 174 110.438 94 174 110.55 95 173.667 111 +95 174 109.875 175 174 109.75 176 174 109.313 +196 174 109.821 197 174 109.889 197.667 174 111 +204 174 108.947 205 173.4 111 205.75 174 111 +91 174.25 111 95 174.75 111 171 175 108.273 +176 175 109.909 195.563 175 111 197 175 110.217 +85 176 108 84.25 176 111 85 175.25 111 +85.1875 176 111 171.857 176 111 177 176 108.29 +196 176 111 197 176 110.143 205 175.333 111 +85 176.188 111 197 176.286 111 212 180.067 111 +207 67 113.333 208 68 112.645 209 68 112.889 +210 68.0667 114 210 70 112.457 213 71 112.313 +214 71 112.75 215 71 113.5 215 72 112.25 +216 73 111.261 217 73 111.273 213 74 112.8 +216 74 111.968 218 74 112.364 218.231 74 114 +218 75 111.783 218 76 112.313 219.091 76 114 +218 77 113.419 219 77 113.679 219.25 77 114 +219 78 113.778 220 78 113.419 220 79 113.143 +221 80 113.419 221.857 81 114 222 82 113.8 +222.167 84 114 223 84 113.143 224 85 113.333 +225 85 113.55 225 87 113.778 226 87 112.696 +226 88 111.783 227 87.2632 114 229 98 111.176 +231 110 111.529 230.04 113 114 231 114 111.529 +232 119 112.773 232 120 111.947 233 121 111.5 +233 128 111.581 234 129 112.227 31 130 113.793 +149 130 111.333 233.375 130 114 234 130 113.348 +31 131 112.636 32 131 112.5 234 131 112.364 +31 132 113.182 30.875 132 114 32 132 112.5 +232 132 112.2 233 132 111.455 131 133 112.962 +132 133 113.1 132 132.182 114 149 132.944 114 +129 134 112.111 130 134 111.581 131 134 111.9 +132 134 113.778 232 133.684 114 233 134 111.931 +130 135 111.2 131 135 112.7 126 135.067 114 +158 136 113.333 31.2857 137 114 232 137 111.581 +157 138 112.08 229.5 138 114 233 138 111.176 +229 142 113.824 230 142 113.143 108 143 113.545 +161 143 111.857 231 144 112.5 165 145 112.8 +164 146 112.313 229 146 113.8 231 146 111.091 +230 147 111.176 232.962 148 114 229 149 111.931 +226 151 113.143 227 152 113.545 229 152 113.909 +232.176 152 114 228 153 112.909 103 154 113.4 +104 154 113.455 101.818 155 114 103 155 111.429 +104 155 113 59 157 112.235 59 158 111.529 +222 157.565 114 177 159 113 220 160 112.545 +106 161.053 114 106.783 162 114 161 163 113.571 +189 163 113.206 106 163.13 114 159.677 164 114 +189 164 112.889 191 164 112.889 197.25 164 114 +208 164 112.457 161.667 165 114 191 165 113.684 +192 165 112.889 198 165 113.25 205 165 113.217 +67 166 112 66.4737 166 114 67 165.667 114 +68 166 112.241 68.8947 166 114 190 166 113.5 +191 166 113.25 192 166 111.947 197 166 113.885 +197 165.667 114 198 166 113.778 199 166 112.5 +203 166 111.75 66 166.75 114 68 167 111.923 +192 167 111.545 193 167 111.273 56 168 112.25 +56 167.125 114 56.5833 168 114 66.1429 168 114 +68 167.947 114 67.8571 168 114 90.625 168 114 +93 167.261 114 167.769 168 114 168 167.25 114 +172 167.391 114 193 168 111.6 56 169 111.75 +56.2 169 114 169 168.609 114 184 169 113.684 +184 168.333 114 185 168.75 114 185 169 113.308 +192.417 169 114 202.65 169 114 203 168.125 114 +204 168.455 114 173 169.857 114 185 170 113.308 +184.6 170 114 193 170 111.9 194 169.444 114 +194 170 112.125 95 170.261 114 185 171 112.263 +186 171 113.182 192.889 171 114 194 171 112 +195 171 112.696 93 171.684 114 185 172 113.182 +186.438 172 114 194 172 113 195 172 111.667 +196 172 112.714 204 171.875 114 205 172 112.875 +204.4 172 114 205.231 172 114 94 173 111.5 +185 172.111 114 205 173 112.5 204.867 173 114 +205.5 173 114 91.8667 174 114 94.6667 174 114 +195 173.722 114 197.429 174 114 206 173.25 114 +206 174 111.429 93 174.393 114 172 175 111.75 +172.125 175 114 196 174.667 114 205 175 113.571 +206.15 175 114 85 176 111.346 171 175.545 114 +176 186.853 114 181 187.808 114 201.75 67 117 +204 67 116.125 203 68 116.471 205 69 116.1 +208 73 116.217 209 73 116.125 208 74 116.333 +207.5 74 117 209 74 115.962 210 76 116.1 +211 76 116.217 215 77 115.2 214 78 115.8 +216 78 115.645 217 78 116.333 213 78.6667 117 +218 79 116.444 221.966 80 117 219 81 115.2 +222 81 114.136 221 82 114.176 221 83 114.857 +225 84.1111 117 223 86.8667 117 224 89 115.7 +225 89 115.227 225 90 115.5 224.697 92 117 +224 95 116.824 234.059 105 117 226 108 116.4 +227 110 116.778 228 111 116.824 228 112 116.206 +228 113 116.419 229 114 114.529 229 115 114.857 +229 118 115.8 230 119 116.885 231 119 115.759 +231 121 114.931 230 122 114.882 30 125 116.087 +31 126 115.5 228.5 126 117 146 127.966 117 +145.955 128 117 146 129 115.655 32 130 114.231 +134 130 116.5 135 130 116.471 230 130 116.778 +132 131 116.069 133 131 114.818 134 131 115.3 +135 131 116.778 132 132 114.182 133 132 114.091 +134 132 115.5 133 133 114.931 159 133 115.5 +229 133 116.143 230 133 115.147 149.433 134 117 +230 134 116.032 231 134 116.1 232 134 114.692 +159 135 114.2 161 135 116.217 228 135 116.8 +229 135 115.889 232 135 115.364 159 136 114.857 +160 136 115.56 161 136 116.739 163 136 116.333 +229 136 116.625 162 137 116.217 230 137 114.4 +164 138 116.739 164 139 115.889 226 142 116.824 +105.867 143 117 226 143 116.143 229 143 114.176 +167 144 115.2 228 144 115.853 229 144 115.235 +104 144.933 117 103.857 145 117 105 145 115.5 +168 145 116.625 229 145 115 168 146 116.885 +169 146 116.778 170 146 116.333 227 146 116.545 +170 148 115.8 172 149 115.457 222.125 149 117 +223 150 115.457 224 151 115.2 220 152 116.143 +221 152 115.853 222 152 115.8 224 152 115.853 +51 152.087 117 221 153 115.457 222 153 115.8 +223 153 116.471 102 154 116.143 104 154 115.5 +219 154 116.5 101.895 155 117 103.545 155 117 +182 154.75 117 216 155 116.471 217 155 116.032 +218 155 116.333 181 156 115.457 182 156 116.4 +216 156 115.8 217 156 116.333 217.75 156 117 +223 155.917 117 161 157 114.8 160.313 157 117 +161 156.083 117 162 157 116 162.333 157 117 +182 157 116.032 185 157 116.206 212 157 116.471 +214 157 116.559 216 157 115.457 217 157 116.333 +217.75 157 117 161 157.55 117 162 157.857 117 +176 158 114.207 183 158 115.853 212 158 115.457 +216 158 115.457 217 158 116.032 218 158 115.929 +185 159 115.457 205 158.833 117 208 159 116.419 +210 159 116.125 189 160 115.457 192 160 116.8 +207 160 116.032 208 160 115.645 209 160 116.217 +217 160 114.9 187 161 114.29 188 161 114.968 +204 161 115.8 205 161 115.645 207 161 116.419 +208 161 116.419 209.5 161 117 217 161 116.069 +103.083 162 117 206 162 116.1 210 162 114.667 +216 162 116.864 217 162 116.88 58 163 116.739 +57.8182 163 117 58.25 163 117 69 162.818 117 +103 163 116.087 103 162.125 117 211 163 114.931 +69 164 116.217 103 163.875 117 162.077 164 117 +68 165 116.25 68 164.813 117 159.933 165 117 +197 165 114.333 69 166 114.667 170 166 114.9 +190 166 114.75 190 165.455 117 191 166 115.2 +56 167 114.75 56 166 117 66 167 114.333 +67 167 116.348 67.3333 167 117 91 167 114.783 +191 167 115.765 192 167 116.455 56 168 117 +67 168 114.692 68 167.385 117 89.0909 168 117 +168.125 168 117 172 168 116.1 171.684 168 117 +184 168 114.6 184 167 117 184.333 168 117 +190.923 168 117 192 168 114.923 193 168 115.333 +193.333 168 117 201.067 168 117 202 167.067 117 +203 168 114.13 204.467 168 117 56 169 114.563 +89.25 169 117 91 169 115 94.6364 169 117 +171 169 115.579 172 169 115.7 184 168.8 117 +184 169 116 185 169 116.25 192 169 114.789 +202 169 116.6 201.923 169 117 204.609 169 117 +79 170 115.875 79 169.727 117 80 169.417 117 +80 170 115.385 185 170 116.571 192 169.737 117 +79 170.273 117 80 170.304 117 94 170.286 117 +185 171 115.737 186 171 114.474 195 171 116.727 +194.875 171 117 204 171 116.8 204 170.857 117 +94 171.455 117 186 172 114.778 194 172 115.286 +194 171.467 117 195 172 116.333 196 172 115 +205 172 114.474 92.2273 173 117 195 173 115.773 +196 173 116.1 205 173 114.6 93 174 116.75 +171 173.889 117 197 174 114.692 205 174 115.286 +206 174 115.636 206 175 114.818 172 175.933 117 +204 185 115.579 176.85 186 117 177 185.813 117 +178 185.417 117 184 186 116.735 184 187.385 117 +200.25 67 120 200.333 69 120 204 70 117.968 +202.125 71 120 204 71 118.313 204 74 119.143 +206 77 118.457 208 77 118.5 207 78 119.032 +210 78 118.25 217 79 117.273 210 79.5455 120 +214 80 119.5 216 80 117.261 217 80 117.103 +216 81 117.667 221 86 118.364 219 87 119.824 +220 87 119.8 220.5 87 120 220 88 119.559 +221 88 119.5 222 87.2857 120 222 89 119.1 +222 90.3333 120 223 90.7692 120 224 91.125 120 +222.545 93 120 224 93 118.56 33 105 119.8 +33 107 119.471 32 108 119.739 33 108 119.824 +223.867 108 120 32 109 118.556 33 109 119.824 +224.526 109 120 31 110 119.526 225 110 119.1 +32 111 118.5 224 111 119.419 224 112 118.457 +225 112 118.2 31 113 117.75 224 113 118.457 +225 113 118.645 226 113 118.3 226 114 119.864 +31 119 117.529 226 119 118.457 226 120 118.457 +227 120 118.258 230 120 117.391 225 121 119.471 +227 121 119.739 229 121 118.08 33 127 118.818 +29.4857 128 120 227.929 128 120 145.147 129 120 +228 129 119.864 226 130 119.143 162 131 118.8 +226 131 119.118 161 132 118.235 164 132 118.8 +227 132 118.457 34.4545 133 120 34 134 119.211 +226 135 118.457 230 135 117.517 231 135 117.409 +230 136 117.429 165 137 117.176 169 136.167 120 +169 137 119.143 168 138 118.457 224 138 118.8 +226 138 118.75 110 139 119.824 167 139 118.645 +225 139 119.032 226 139 119.333 227 139 118.556 +167 140 118 226 140 118.941 228 140 117.265 +224 141 118.8 106.133 142 120 221 142 118.8 +106 142.5 120 105.75 143 120 170 143 118.645 +173 143 118.8 103 144 119.769 105.857 144 120 +220 144 118.457 102 145 117.6 103 145 117.947 +104 144.667 120 105 144.857 120 221 145 118.457 +99 146 118.853 101 146 118.182 102 146 117.231 +219 146 118.457 221 146 118.8 222 146 119.206 +223 146 119.471 174 147 118.2 219 147 118.8 +221 147 119.559 222 147 119.273 223 147 118.909 +220.286 148 120 221 148 119.559 222 148 118.545 +221 149 119.1 179 150 118.457 220.346 150 120 +177 151 118.556 178 151 118.889 179 151 118.457 +213 152 118.8 218 152 117.857 181 153 119.1 +185 153 119.824 215 153 118.457 217 153 117.882 +102 153.85 120 103.438 154 120 210 154 118.457 +215 154 118.2 222 154 117.455 223 154 118.111 +224 154 117.45 104 155 119.143 209 155 118.457 +211 155 118.457 213 155 118.7 219 155 117.273 +221 155 118.759 222 155 119.172 223 155 118.435 +103 155.895 120 104 155.143 120 184 156 117.529 +189 156 118.457 211 156 117.882 213 156 117.968 +218.727 156 120 219 155.87 120 219.429 156 120 +220 156 119.294 222 156 119.04 161 157 118.737 +162 157 118.636 189 157 118.2 201 157 119.206 +219 156.75 120 219 157 119.5 198 158 119.419 +202 158 119.206 203 159 118.4 165 160 119.609 +164.727 160 120 165 159.75 120 202 160 117.182 +210 160 117.6 165 160.6 120 210 161 117.333 +211 161 119.786 211.25 161 120 212.75 161 120 +214 161 118.909 216 161 118.105 57 162 118.688 +57 161.125 120 58 161.083 120 58 162 118.565 +104 162 118.65 105 162 118.111 106 162 117.375 +188 162 119.684 57 163 118.688 67.5333 163 120 +68 162.417 120 101 163 119.308 100.143 163 120 +102 162.739 120 102 163 118.364 103 162.563 120 +104 163 119 188 163 119 57 164 119 +56.5556 164 120 57.8 164 120 101 164 119.217 +102 164 119.25 103 164 118 103.111 164 120 +181 164 119.308 188 164 119.308 189 164 118.889 +57 165 117.75 56.25 165 120 57.25 165 120 +68 165 118.125 68.3125 165 120 162 164.053 120 +162.667 165 120 169 164.5 120 180.739 165 120 +189 165 118.444 190 165 117.75 197 165 119.667 +198 165 119.55 57 165.25 120 68 166 118.826 +69 165.55 120 69 166 118.313 93.2 166 120 +161 165.947 120 182 166 118.364 183 166 117.375 +189.067 166 120 191 165.333 120 198 166 118.25 +199 166 118.95 56 167 117.75 68 167 118 +162 166.261 120 171.519 167 120 183 166.636 120 +190 166.737 120 198 166.35 120 200 167 119.182 +199.25 167 120 200 166.571 120 201 166.632 120 +201 167 117.375 184 168 117.75 193 168 118.579 +192.25 168 120 201 168 118 202 167.417 120 +192 169 119.211 192 168.375 120 203 169 119.87 +203 168.857 120 79 170 117.563 80 170 118.75 +193 170 119.684 203 170 117.391 204 170 117.6 +93 171 119 94 171 118 94.3704 171 120 +94.3704 172 120 94 172.667 120 170.192 175 120 +171 175.808 120 184 184.267 120 176.533 186 120 +180 186 117.25 181 186 117.6 181.8 186 120 +182.091 186 120 185 186 117.091 175 186.588 120 +181 187 120 184 187 117.652 183.053 187 120 +185 187 117.088 182 187.615 120 200.25 68 123 +200.333 69 123 205 80 122.625 207 80 121.556 +208 80 122.423 210 80 120.714 221 79.913 123 +205 80.75 123 207 81 121.889 214 81 120.12 +206 82 122.032 207 82 121.645 208 82 122.8 +213 82 120.333 214 82 120.231 211 83 121.645 +213 83 121.5 210.182 84 123 212 84 121.8 +213 84 122.125 213 84.875 123 214 85.5 123 +216 87 122.739 217 87 122.55 221 87 120.273 +222 87 120.333 218 88 121.2 220 89 121.038 +221 89 120.103 221 90 120.231 221 91 121.5 +223 91 122.25 220 92 122.129 221 92 121.7 +222 92 121.889 222.667 92 123 222 93 120.529 +34 102 121.962 34 101.182 123 33 103 121.895 +34 103 121.7 33 104 120.682 34 104 121.5 +31.8235 105 123 34 105 121.5 32 106 120.667 +222 107 120.529 220 109 122.778 224 109 121 +220 110 122.333 221 110 122.032 222 110 122.206 +223 110 121.909 221 111 122.806 222 111 121.909 +221 114 122.032 222 114 120.857 220 115 122.143 +225 115 120.273 226 115 120.692 223 116 121.25 +224 116 121.125 225 116 120.333 221 117 121.457 +224 117 121.2 222 118 122.333 223 118 122.739 +223 119 122.063 223 120 121.853 222 121 121.457 +224 121 120.529 221 122 121.457 223 122 121.457 +226 122 120.455 227 122 121.219 221 123 121.457 +224 123 121.5 225 123 121.853 226 123 121.909 +227 123 121.219 149 123.7 123 226 124 121.909 +227 124 120.094 34.4737 126 123 226 126 121.125 +161 126.5 123 165 127 122.824 225 127 122.1 +226 127 121.7 34.7391 128 123 134 127.833 123 +145.059 128 123 165 128 121.8 217.875 128 123 +219 128 121.8 223 128 121.8 227 128 122.167 +135 128.077 123 162 129 121.235 164 129 121.8 +167 129 121.8 226 129 120.115 227 129 120.316 +35 130 121.818 136.4 130 123 124 133 121.355 +219 133 121.457 168 134 121.5 220 135 121.8 +219 136 122.786 218 138 121.457 220 138 121.457 +221 138 121.645 222 138 121.304 174 139 121.457 +177 139 122.471 220 139 122.8 221 139 122.8 +222 139 122.217 223 139 120.5 104.6 140 123 +106 140 121.355 173 140 121.2 177 141 122.1 +178 141 122.032 179 141 122.471 221 141 120.818 +105 142 123 106 142 120.857 176 142 122.129 +215 142 122.25 102 143 120.29 103 143 120.261 +104 143 120.75 176 143 121.543 212 143 122.471 +104 144 120.857 116 144 121.714 117 143.818 123 +181 144 121.8 182 144 122.471 212 144 121.457 +214 144 121.886 104.417 145 123 106 145 122 +116 145 120.167 117 145 120.3 179 145 121.457 +215 145 121.8 105 145.438 123 106 145.261 123 +116 145.739 123 181 146 122.778 184 146 121.8 +210 146 122.806 214 146 121.645 216 146 121.457 +46 147 121.111 181 147 122.778 182 147 122.778 +184 147 121.457 205 147 122.471 205 146.5 123 +208 147 121.457 209 147 121.8 211 147 121.8 +91 147.5 123 181 148 121.7 205 148 121.457 +207 148 121.457 211 148 121.457 212 148 122.125 +214 148 122.125 90 149 122.419 188 149 121.8 +210 149 121.457 212.375 149 123 42 150 122.4 +41.625 150 123 42 149.625 123 42.2727 150 123 +89 150 120.882 203 150 121.853 204 150 121.457 +209 150 121.457 210 150 121.645 211 150 122.217 +211.75 150 123 220 150 121.08 52 151 120.692 +208 151 121.457 214 150.625 123 53 152 122.5 +54.75 152 123 62 151.5 123 84 152 120.529 +189 152 121.457 208 152 121.2 177 153 122.609 +176.4 153 123 177 152.727 123 177.75 153 123 +177 153.25 123 103 154.211 123 104 155 120.857 +200 155 121.457 101.313 156 123 103 156 120.375 +217 156 122.182 216.455 156 123 218 155.813 123 +218 156 122.182 219.429 156 123 102 156.846 123 +103 156.933 123 217 157 121.696 216.286 157 123 +218 157 122.182 219 157 120.75 217 157.5 123 +206 159 122.053 207 159 121.875 165 159.813 123 +207 160 121.355 208 159.083 123 209 159.727 123 +209 160 122.438 209.429 160 123 60 161 122.333 +165 160.75 123 166 161 121.091 186 161 122.71 +187 160.364 123 188 161 122.55 207 161 121.696 +208 161 122.053 209 160.333 123 212 161 121.125 +57 161.455 123 70 162 120.273 72 161.263 123 +72 162 121.645 167 162 122.125 166.125 162 123 +167 161.125 123 187 162 120.222 57 163 122.625 +71 163 121 98 163 122.526 98 162.625 123 +99 162.045 123 99 163 120.261 100 163 120.158 +102 163 121.636 101.667 163 123 103 163 120.875 +167 162.467 123 169 163 122.71 168.25 163 123 +181 163 122.55 180.87 163 123 181.75 163 123 +57 164 120.75 58 163.273 123 90.6957 164 123 +93 164 122.4 100 164 121.091 102 164 121.125 +103 164 120.333 161 164 121.2 168 164 122.739 +167.923 164 123 172 163.6 123 182 163.143 123 +57 165 120.375 161 164.6 123 163 164.391 123 +182 165 122.842 182 164.857 123 191.375 165 123 +195 164.333 123 196 164.217 123 198 164.923 123 +172.654 166 123 182 166 120.783 195.8 166 123 +210 166 122.609 209.75 166 123 85 167 122.222 +84.125 167 123 85 166.632 123 85.5833 167 123 +162 166.783 123 163 166.526 123 191 167 121.235 +191 166.167 123 200 167 121.125 201 167 121.235 +210 167 122.438 211 167 122.4 84 168 122.4 +83.25 168 123 169 167.609 123 192 168 120.375 +201 168 120.4 202 168 121.05 203 168 120.783 +211 168 120.2 211.875 168 123 84 168.2 123 +90.125 169 123 91 169.368 123 95.1875 170 123 +211 169.15 123 212 169.375 123 92.4615 171 123 +93 172 121.75 170 171.87 123 170 173 120.75 +171 172.727 123 193 174 122.6 193 173.333 123 +194 173.067 123 194 175 120 194.9 175 123 +171 175.316 123 159 180.31 123 183.4 184 123 +184.545 184 123 182.895 185 123 175.143 186 123 +177 185.4 123 177 186 121.615 180.417 186 123 +181 186 121.091 178 187 121.889 179 187 122.1 +180 187 121.5 183 186.333 123 178 187.909 123 +182 188.435 123 193 188.077 123 92 190.6 123 +203 70 125.71 202 71 124.5 202.37 71 126 +202.286 73 126 208 81 123.12 205 83 125.727 +208 83 123.682 207 84 124.826 206 85 125.778 +205.8 85 126 206.5 85 126 206 86 125.125 +205.364 86 126 206.875 86 126 209 86 125.125 +210.375 86 126 211 85.7727 126 207.4 87 126 +209 87 125.032 210 87 125.333 210.545 87 126 +214 87 124.304 212 87.25 126 211.143 88 126 +213 88 125.625 210.867 89 126 212 89 124.645 +213 90 125.333 216 91 125.7 223.909 91 126 +215 92 125.739 216 92 125.75 224 92 124.636 +214 93 125.778 217 93 124.889 216 94 124.645 +217 94 125.217 218 94 125.625 217 95 125.217 +218 95 125.211 219 95 125.786 36 96 125.8 +36 95.7143 126 216 96 124.645 217 96 124.889 +218 96 124.111 219 96 123.136 35 97 125.778 +36 97 125.471 36 98 125.824 34 99 125.609 +35 99 124.5 34 100 124.421 35 100 124.5 +215 100 124.457 35 101 124.818 216 105 124.457 +218 109 124.5 219 109 124 218 111 123.882 +219 111 123.517 216 112 125.471 221 112 123.176 +215 113 125.5 218 113 125.885 220 113 124 +219 114 125.864 214 116 125.824 215.75 117 126 +217 119 124.457 36 121 124.457 37 121 125.471 +152.6 121 126 155 121 124.457 157 121 124.8 +215 121 124.457 152 122 125.625 153 122 124.853 +166 121.833 126 153 123 123.882 155 123 124.457 +165 123 125.125 218 123 124.457 36 124 124.8 +146.923 124 126 165 124 125.217 170 123.833 126 +218 124 124.8 220 124 123.818 36 125 124.457 +160 125 124.313 165 125 125.125 170 125 124.8 +212 125 125.1 211.4 125 126 219.857 125 126 +221 125 125.25 223 125 125.545 224 125 125.206 +35 126 123.882 36 126 124.645 37 126 124.645 +145.8 126 126 160 126 123.783 170 126 124.457 +212 126 125.778 213 126 125.55 213.6 126 126 +219 126 125.885 223 126 125.545 224 126 124.909 +36 127 125.217 213.25 127 126 36 128 124.826 +37 128 124.889 174 127.25 126 217 127.739 126 +216.571 128 126 228 128 123.75 227.25 128 126 +37 129 124.645 38.5 129 126 174 129 124.645 +214 129 125.55 216 129 125.55 216 128.727 126 +228 128.75 126 38 130 125.471 174 130 124.889 +146.5 131 126 212 131 124.457 37 132 125.559 +176 132 124.645 216 132 124.889 177 133 125.143 +211 133 124.8 215 133 124.457 211 134 124.457 +217 135 124.3 216 136 124.147 217 136 124.5 +176 137 124.111 213 138 124.2 207.167 139 126 +209 139 124.457 217 139 124.125 224 139 125.455 +223.2 139 126 224 138.556 126 225 138.5 126 +225 139 125.143 102 140 124.2 104 140 124.227 +182 140 124.645 183 140 124.7 207.5 140 126 +216 140 124.8 217 140 124.545 43 141 125.824 +103 141 123.947 104.857 141 126 167 141 124.385 +166.364 141 126 167 140.417 126 182 141 124.889 +184 141 124.645 209 141 124.645 216 141 124.345 +218 141 123.818 42 142 124.235 43 142 125.559 +99 142 124.2 105 141.25 126 167 142 124 +183 142 124.645 186 142 124.7 186.722 142 126 +203 142 124.457 207.222 142 126 209 142 124.645 +213 142 124.5 46 142.5 126 168 143 123.13 +169 143 124.3 185 143 124.645 190 143 125.778 +191 143 124.457 192 143 124.645 205 143 124.8 +97 144 123.182 105 144 124.826 116 143.769 126 +168 144 124 169 144 123 184 144 123.882 +190 144 125.032 191 144 124.645 198 144 124.457 +202 144 124.457 105 145 124.105 106 145 124.2 +116.737 145 126 168 144.37 126 169 144.609 126 +188 145 125.1 189 144.125 126 193 145 125.55 +201 145 124.457 205 145 124.457 89 145.25 126 +115 145.857 126 115.25 146 126 194 146 124.457 +90 147 124.5 116 146.111 126 191 147 124.645 +202 147 124.457 196 148 124.8 43 149 124.364 +44 149 125.609 45 149 125.8 59 149 125.824 +191 149 124.457 194 149 123.529 197 149 124.556 +214 149 124.412 216 149 124.56 217 149 124.364 +42 150 123.818 43 149.857 126 44 149.273 126 +59.3333 150 126 60 150 125.75 61 150 125.333 +64 150 124.457 212.25 150 126 215 150 125.833 +216 150 125.864 217 150 124.909 54 151 123.316 +55 151 123.833 56 151 124.5 211.4 151 126 +214.75 151 126 54 152 124.286 59 151.375 126 +177 153 124.286 218 153 125.667 217.727 153 126 +176 154 125.308 102 155 125.8 174 155 125.55 +174 154.571 126 175 155 124.862 214 155 125.118 +215 155 124.5 217 155 124.875 217.4 155 126 +175 156 124.889 176 156 124.929 176.667 156 126 +214 155.909 126 216 156 123.938 217.368 156 126 +218 156 123.6 219 156 123.692 100 156.333 126 +102 157 123.75 103 156.273 126 204 157 125.4 +216 157 124.714 218 157 123.6 219 157 123.391 +100 157.909 126 205 158 123.8 206 158 124.091 +184 159 125.323 184 158.125 126 185 158.217 126 +185 159 124.258 205 159 123.517 208 158.923 126 +165 160 123.333 185 160 123.9 186 160 123.13 +208.654 160 126 209 160 123.75 61 161 123.3 +72 161 123.789 74 161 125.471 94 161 125.455 +94.5455 161 126 180 161 125.727 180 160.714 126 +181 160.444 126 185 161 125.684 57 162 124.2 +57.6 162 126 60 162 125.25 61 162 123.75 +70 162 124.579 73 162 123.556 92 162 124.7 +167 162 125.625 167.333 162 126 168 161.923 126 +168 162 125.143 181 162 123.188 180.063 162 126 +208 162 124.875 60 162.097 126 61 162.273 126 +69 163 124.5 71 163 125.727 71.25 163 126 +167.391 163 126 168 163 123.2 171 162.265 126 +181.25 163 126 207 162.2 126 208 163 126 +90 164 124.92 97.7273 164 126 163 164 125.25 +163 163.813 126 181 164 123.563 182 164 123.783 +190 163.773 126 87 165 125.4 86.6 165 126 +87.8571 165 126 192 165 124.111 194 165 123.1 +86 166 124.35 87 166 124.444 87.6364 166 126 +90.7391 166 126 164.435 166 126 191.857 166 126 +198 166 125.318 210 166 125.25 84.4737 167 126 +86 167 124.25 86.5833 167 126 162 166.6 126 +163 167 124.8 171.2 167 126 189 166.143 126 +210 167 124.125 211 167 123.75 84 168 123.818 +90 168 123.4 211 168 125.211 191 170 124.696 +190.333 170 126 192 169.889 126 192 170 125.25 +169 171 124.765 170 170.842 126 170 171 125.609 +191 170.909 126 192 171 124.263 191.083 171 126 +169 172 124.043 168.375 172 126 192 172 125.455 +193 172 124.875 171 173 124.636 170.737 173 126 +193 173 123.75 194 173 123.75 170 174 125.211 +171 174 125 195 175 124.2 195.13 175 126 +194 175.435 126 195 175.375 126 197 178 124.889 +197 179 124.821 197.733 179 126 197 179.917 126 +175 183 125.6 179 183 125.769 164 183.941 126 +184 184 124.2 175 185 124.889 176 185 125.308 +177 184.714 126 184 185 125.684 170 185.722 126 +170.714 186 126 173 185.714 126 176 185.375 126 +171 186.087 126 172 186.261 126 179 186.455 126 +180 186.333 126 192 186.818 126 182.5 188 126 +93 190 125 96 190.059 126 202.654 72 129 +207 72 128.806 209 72.6087 129 210 73.0909 129 +208 76 128 207.533 76 129 210 79 126.789 +210 78.2632 129 211 79 128.6 210 80 127.364 +210.857 80 129 211 79.6667 129 212 80 128.833 +211.5 80 129 212 79.9091 129 212 81 128.483 +211.5 81 129 212.5 81 129 212 81.4545 129 +207 85 126.857 205.1 86 129 207 86 126.5 +208 86 126.214 211 85.6667 129 212 86 126.5 +213 86 126.5 207 87 128 212 87 126.857 +213 87 127.071 205.833 88 129 207 87.0909 129 +210 88 126.4 211 87.75 129 211 88 127 +206 88.1667 129 210 89 127.773 205 90 128.684 +214 90 126.316 204 90.625 129 205 91 127.889 +211 90.625 129 212 90.4167 129 213 91 127.071 +215 91 126.882 223 91 128.143 204 92 128.125 +223 92 126.882 37 93 128.6 38 92.7143 129 +38.0909 93 129 203 93 128.684 205 93 127.457 +208 93 128.206 209 93 128.032 210 93 128.667 +37 94 128.053 203 94 128.333 209 94 128.333 +213.125 94 129 219 94 127.286 36 95 127 +202.739 95 129 209 95 128.125 34.9333 96 129 +203.5 96 129 209.583 96 129 213 96 128.625 +212.75 96 129 206 97 127.8 208 97 127.457 +210 96.7143 129 210 97 128.684 205 97.5 129 +209 98 127.889 211 98 127.889 212 99 127.889 +213 99 127.5 209 100 127.645 209 101 127.457 +212 102 127.457 208 103 127.8 208 104 127.457 +205 109 128.206 203 110 128.143 206 110 128.032 +207 110 127.457 214 110 127.457 212 111 127.457 +200 111.833 129 201 113 127.457 198.625 114 129 +204 114 127.853 218 114 126.818 199 115 127.8 +200 115 127.457 201 117 127.457 215 117 126.265 +223 117 127.895 222.632 117 129 223.875 117 129 +211 118 127.457 212 118 127.5 223 118 128 +222.143 118 129 223.261 118 129 202 119 128.8 +208 119 127.457 213 119 128.206 214 119 127.182 +202 120 128.419 203 120 128.5 209 120 127.457 +161 120.417 129 204 121 128.118 209 121 128.032 +152 121.818 129 166.6 122 129 205 122 127.457 +29.8235 123 129 167 122.25 129 168.6 123 129 +149 123.7 129 170.25 124 129 188 124 128.909 +187.833 124 129 208 124 127.889 196 125 128.143 +197 125 127.8 206 125 127.457 218 124.955 129 +218.1 125 129 39 126 127.182 189 126 128.824 +199 126 128.032 204 126 127.457 206 126 127.645 +210 126 128.308 133 126.824 129 172.75 127 129 +174 126.857 129 174.286 127 129 199 127 128.333 +205 127 127.457 206 127 127.889 210.455 127 129 +211 127 126.75 214 127 128.077 214.571 127 129 +216 126.25 129 218 127 126.3 217.25 127 129 +173 127.125 129 199 128 128.125 206 128 128.125 +215 128 126.964 228 128 126.818 179 128.182 129 +181 129 127.645 206.833 129 129 39 130 126.581 +146.273 130 129 179 130 127.8 183 130 127.457 +197 130 127.853 198 130 127.889 205 130 127.457 +207 130 128.25 209 129.417 129 39 131 126.529 +40 131 127.2 41.625 131 129 197 131 128.4 +198 131 128.55 201 131 128.125 209 131 127.457 +40 132 127.853 188 133 128.032 40 134 127.286 +180 134 127.543 187 134 127.889 188 134 128.217 +192 134 128.471 193 133.75 129 184 135 128.1 +188.286 135 129 192 135 127.457 43 136 127.8 +43.875 136 129 182 136 127.2 184 136 128.778 +185 136 128.778 186 136 128.125 206 136 127.457 +219 136 128.55 183 137 128.143 184 137 128.778 +185 137 128.778 196 137 128.55 203 137 127.8 +218 137 128.625 217.813 137 129 219 137 127.696 +220.375 137 129 102 138 126.29 184 138 127.75 +187 138 127.645 188 138 127.962 202 138 128.559 +219 137.667 129 220 137.375 129 222 138 128.4 +221.25 138 129 223 138 127.25 224 138 127.875 +224.375 138 129 186 139 128.1 187 139 127.889 +188 139 128.333 201 139 128.8 203 139 128.559 +205 139 128.032 206 139 127.444 223 139 126.429 +222.6 139 129 225 138.714 129 225.105 139 129 +186 140 126.563 187 140 127.111 188 140 127.853 +192 140 127.457 193 140 127.457 197 140 127.645 +200 140 127.457 204 140 128.423 204.714 140 129 +225 139.133 129 104 141 127.636 166.667 141 129 +167 140.143 129 188 141 128.4 189 141 128.909 +190 141 128.182 196 141 127.457 205.167 141 129 +213 141 127.154 214 141 127 105 142 126.818 +166.435 142 129 189 142 128.897 199 142 127.235 +200 142 126.882 201 142 126.176 207 142 128 +48 143 127.5 106 143 128.167 187.5 143 129 +189 143 128.833 188.857 143 129 194 143 127.345 +206 143 126.316 207 143 128 208 143 126.682 +50 143.188 129 207 144 127.615 208 144 127.7 +113.6 145 129 114 144.143 129 188 144.091 129 +114 146 128.217 115 147 128.769 114.714 147 129 +116 146.609 129 43 149 127.636 44 149 127.286 +44.4 149 129 45 148.455 129 46 148.727 129 +46 149 128.182 47 149 127.95 47.4667 149 129 +67 149 126.529 46 149.25 129 47 149.875 129 +59 150 126.094 211.455 150 129 214 150 126.75 +59 151 127 210 151 127.625 210 150.522 129 +211 151 126.3 213.667 151 129 208.813 152 129 +210 152 127.889 211 152 127.95 209 153 129 +211 152.304 129 216 153 128.786 214 153.565 129 +217 154 127.875 218 153.933 129 217.75 154 129 +174 154.5 129 176.895 155 129 205 155 128.71 +206 155 128.118 207 154.625 129 207 155 128.667 +101 156 128 102 156 127.826 174.074 156 129 +177 155.087 129 177 156 126.577 183 156 128.25 +182.125 156 129 204 156 127.147 206 156 128.308 +97 157 127.846 98 156.313 129 98 157 127.821 +100 156.067 129 101 156.625 129 101.25 157 129 +102 157 127.2 176 157 126.115 177 157 126.441 +183 157 127.8 184 156.632 129 184 157 128.125 +202 157 128.824 203 157 126.9 214 156.618 129 +217 157 127.5 178 158 128.71 183 158 128.222 +184 158 126.158 61 159 128.727 62 159 127.645 +95 159 128.4 183 158.778 129 183.1 159 129 +207.435 159 129 58.375 160 129 62 160 126.9 +71.9259 160 129 73 160 127.3 75 159.895 129 +94 160 126.75 95 160 127.038 95.7727 160 129 +179 160 128.8 180 159.045 129 180 160 126.577 +187 160 126.316 204 159.333 129 208 160 127.962 +62.4667 161 129 75 161 127.818 95 161 126.682 +95.5862 161 129 179.357 161 129 185 161 126.6 +186 161 128.348 186 160.545 129 208 161 127.304 +62 161.636 129 61.4286 162 129 71 162 128.625 +71.0769 162 129 92 161.056 129 168.769 162 129 +170 162 126.2 180 161.947 129 186 162 126.75 +187 162 128.217 207 162 126.692 207 161.167 129 +61 162.2 129 88.4545 163 129 89 162.455 129 +90 162.105 129 95 163 127.92 97.875 163 129 +167.833 163 129 181 162.231 129 187 162.75 129 +88 164 127.8 88 163.455 129 89 164 126.9 +95 164 127.714 97 164 128.286 96.6429 164 129 +100.176 164 129 163 163.571 129 163.857 164 129 +172.382 164 129 188 163.067 129 193 163.263 129 +86.5417 165 129 87 164.522 129 89 165 127.125 +90 165 127.5 97 165 128.769 98 165 128.217 +162.045 165 129 196 165 128.273 86 166 128.2 +86.3636 166 129 87.5833 166 129 91 165.261 129 +99 165.333 129 162 166 127.929 168.923 166 129 +194 166 126.818 195 166 128.294 195 165.6 129 +196 166 128.429 197 166 127.909 85 167 127.111 +86 167 127.05 92 167 127.5 93 167 128.5 +95.6667 167 129 165 167 126.857 189 167 127.125 +189.667 167 129 191 166.385 129 193 166.111 129 +166 168 128.1 166.857 168 129 189 168 127.696 +190 168 129 95.3704 169 129 164 168.333 129 +167.25 169 129 188.727 169 129 190 169 126.375 +95 169.909 129 168 170 127.75 169 170 126.783 +169.773 170 129 191 169.125 129 192 170 126.75 +168 171 128.25 167.87 171 129 169.944 171 129 +192 171 128.063 192 172 126.333 193 172 126.474 +193 173 126.45 194 173 126.45 194 174 127.222 +194 175 127.111 195 175 126.6 196 177 127.714 +196 176.75 129 197 177 127.333 197.833 177 129 +196 178 127.364 198.667 178 129 198 179.75 129 +207 180 128 92 181 128.667 92.375 181 129 +196 180.778 129 197 181 128.526 196.4 181 129 +202 181 127.174 204 181 127.765 207 181 127.393 +208 181 128.55 92 182 127.636 92.9091 182 129 +200.75 182 129 202 182 127.038 206 182 128.471 +207 182 128 208 182 127.444 209 182 126.9 +92 183 128.571 92.6667 183 129 174 183 126.273 +173.375 183 129 177 183 127.773 197 183 128.609 +198 183 128.71 207 183 128.333 208 183 126.261 +92 183.125 129 172 184 128.526 171.25 184 129 +171 185 128.455 172 185 127.8 173 185 126.682 +170 185.9 129 170.25 186 129 176 185.929 129 +175.68 187 129 179 187 127.565 178.45 187 129 +180 187 127.385 181 186.4 129 179 188 127 +178.5 188 129 180 188 126.5 181 188 127.5 +182 188 127.688 192.727 189 129 94 189.409 129 +95 190 127 193 189.194 129 86 191 126.5 +90.7368 191 129 87 191.8 129 90 191.452 129 +203 72 130.929 204 72 131.308 207 74 131.727 +208 74 130.962 211 74 130.765 208 75 130 +207.474 75 132 209 75 131.526 209 75.375 132 +208 76.5556 132 211 79 129.6 210 80 130.636 +212 80 129.231 212 81 129.882 213 82 130.5 +214 83 130.2 214.6 83 132 214 84 132 +206 85 130.2 214 85 129.429 213.5 85 132 +215 84.1 132 215 85 129.545 206.667 86 132 +210.727 86 132 215 85.5 132 42 87 131.909 +205.9 87 132 205.182 88 132 207 88 132 +41 89 131.824 205 89 130.846 205 88.2857 132 +206 89 129.882 207 89 130.857 208 89 130 +206 90 129.125 208 90 130.345 209 90 129.529 +210 90 129.261 39 91 131.739 211 91 129.391 +212 91 130.75 216 91 131.571 215.8 91 132 +216 90.7143 132 38 92 131.143 39 92 130.962 +39.6 92 132 203.37 92 132 212 92 130.364 +216 92 130.875 39 93 131 212 93 130.655 +216 92.75 132 211 95 132 212 95 132 +213 95 130.071 215 95 131.609 215 94.25 132 +216 95 132 202.533 96 132 215 95.3333 132 +211 97 129.094 34.5 98 132 203 98 131.129 +204 99 131.419 205 99 131.25 206 99 130.773 +37 100 129.529 201.727 100 132 207 100 130.304 +202 101 131.032 203 101 130.457 204 101 130.8 +202 102.25 132 202.857 103 132 203.167 104 132 +198.346 112 132 196 115.929 132 223 117 130.4 +194 118 131.739 193.889 118 132 223 118 130.636 +194 119 131.419 193.143 119 132 195 120 131.559 +212 120 129.882 153 121 129.667 155 121 130.543 +161 121 129.875 192 121 131.806 192 120.75 132 +192.5 121 132 195 121 131.471 212 121 130.154 +161 122 130.543 166 122 129.45 192.667 122 132 +193.75 122 132 40 123 131.1 152 123 129.857 +166 123 129.581 168 123 129.45 191 123 131.125 +193 122.75 132 193 123 131.909 202 123 129.545 +209 123 129.818 210 123 130.56 41 124 131.684 +159 124 130.543 167 124 130.147 187 124 130.364 +188 123.087 132 189 124 130.5 190 124 130.75 +195 124 129.857 210 124 129.409 218 124 131.625 +218 123.727 132 40 125 130.5 150 125 130.645 +157 125 130.543 165 125 129.529 170 125 130.355 +186 125 129.545 185.64 125 132 187 125 129.261 +191.75 125 132 192 124.5 132 194 125 131.182 +209 125 129.469 217 125 129.563 43 126 130.8 +157 126 130.543 158 126 129.581 160 126 129.2 +161 126 130.2 163 126 130.543 187 126 129.2 +190 126 130.235 201 126 129.094 208 126 129.714 +151 127 130.889 153 127 131.143 154 127 131.471 +155 127 131.118 156 127 131.471 159 127 129.5 +170 127 130.543 188 127 129.818 192 126.6 132 +192 127 131.824 210 127 130.667 133 127.5 132 +160 128 130.3 174 128 131.333 175 127.875 132 +176 127.625 132 180 127.435 132 185 128 131.885 +184.857 128 132 187 128 130.235 188 128 129.818 +191 128 131.206 200 128 129.844 201 128 130.545 +206.913 128 132 209.933 128 132 216 127.889 132 +44 129 130.645 161 129 131.824 164 128.909 132 +184 128.231 132 192 129 130.3 200 129 129.844 +201 129 130.182 207 128.286 132 42 130 130.111 +44 130 131.333 187 130 129.529 188 130 129.529 +189 130 130.08 190 130 129.882 191 130 130.5 +192 130 130.111 194 130 130.2 201 130 129.091 +44 131 131.333 188 131 129.265 189 131 130.773 +192 131 131.217 193 131 130.2 199 131 130.2 +46 132 131.333 192 132 131.333 194 132 130.5 +196 132 129.882 199 132 132 43 133 131.909 +47 133 131.032 106 133 129.882 110 132.265 132 +189.538 133 132 192 133 130 193 133 129.529 +195 133 130.235 197 133 131.217 197.462 133 132 +42 134 130.909 48 134 131.419 189 134 130.071 +190 133.545 132 191 134 130.8 194 134 129.682 +195 134 130.5 196 134 130.3 197.5 134 132 +198.857 134 132 45 134.857 132 103 135 129.265 +196 135 130 101 136 129.265 102 136 129.176 +189 136 129.517 196.571 136 132 218 136 129.167 +217.105 136 132 189 137 129.774 190 137 129.469 +196.696 137 132 201 137 129.9 221 136.875 132 +221 137 130.875 49 137.85 132 199.688 138 132 +217 138 131 220 138 129.682 223 138 130.615 +45 139 130.545 46 139 131.206 49 139 130.457 +52 139 130.457 199 139 129.882 199 138.478 132 +222 138.545 132 223 139 130.286 224 139 130.154 +50 141 130.457 87 141 131.206 131.294 141 132 +168 141 130.105 185 141 131.625 186 141 131.438 +186.188 141 132 204.75 141 132 205 140.813 132 +207.333 141 132 50 142 130.2 86 142 130.457 +106 142 131.143 167 142 130.182 186 142 132 +188 142 129.391 205.417 142 132 106 142.286 132 +187 143 131.25 186.333 143 132 187 142.857 132 +187.667 143 132 206 142.259 132 56 144 130.457 +75 144 130.457 80 144 130.457 85 144 129.265 +158 144 130.875 157.667 144 132 159 143.091 132 +159 144 130.421 159.667 144 132 171 144 129.789 +172 143.25 132 186.813 144 132 189 144 132 +74 145 130.457 81 145 129.529 82 145 130.235 +84 145 129.818 113.818 145 132 158 144.75 132 +159 145 131.308 158.143 145 132 159.545 145 132 +170 145 130.8 172 145 130.853 116.111 146 132 +159 145.4 132 172 146 131.118 73 147 129.176 +114 147 129.556 116 146.375 132 114 148 131 +115 147.947 132 45 149 129.783 46 149 129.6 +47 149 130.75 212 150 129.783 212 151 130.957 +208.8 152 132 210.545 152 132 63 153 131.077 +62.2 153 132 63 152.667 132 64 153 130.95 +208 153 129.75 210.133 153 132 216 153 129.273 +218 153 130.826 65 153.571 132 65 154 131.667 +66 153.667 132 66 154 131.419 207 154 130.875 +206.625 154 132 209 154 131.667 209.5 154 132 +216 154 130.667 217 154 129.474 64.8125 155 132 +66 155 131.118 181 155 130.3 182 155 129.1 +183 155 129.167 203.25 155 132 208 155 130.5 +209 155 131.8 210 155 131.471 211 154.421 132 +211 155 130.969 212 154.192 132 65.6 156 132 +67 155.125 132 67 156 131.323 68 156 131.667 +98.5455 156 132 175 155.818 132 176 155.65 132 +180.923 156 132 182 156 129.115 201 156 130.696 +202 155.419 132 208 156 131.8 209 155.5 132 +210.25 156 132 64 157 131.8 64.5 157 132 +66 157 131.769 68 157 131.118 69 157 131.118 +70.1875 157 132 99 156.357 132 99.4091 157 132 +175.1 157 132 184.077 157 132 200 157 130.543 +206.143 157 132 209 157 131.824 209.667 157 132 +212 156.292 132 214 156.519 132 61.2273 158 132 +65 158 131.654 70 157.75 132 93.6667 158 132 +94 157.474 132 185 158 129.947 184.188 158 132 +200 158 130.543 201 158 130.355 206 158 130.5 +206 157.071 132 61 158.278 132 71.4167 159 132 +72 158.533 132 74 158.824 132 75 159 130.889 +98 158.567 132 178 159 130.174 179 158.1 132 +179 159 129.158 183 158.188 132 184 159 131.348 +184 158.375 132 185 159 131.2 200 159 131.71 +204.25 159 132 207 159 130.579 206.25 159 132 +77 159.455 132 77.75 160 132 181 159.227 132 +185 160 130.742 186 160 129.783 206 160 131.885 +207 160 131.04 77 161 131.118 78 160.4 132 +78 161 131.743 89 161 131.053 92.6 161 132 +178 160.545 132 91 161.474 132 99 163 130.909 +181 162.375 132 186 162.474 132 96 163.571 132 +163 164 129.947 168.265 164 132 184 164 131.471 +183.25 164 132 189 163.45 132 193 164 131.625 +87 165 130.65 87.75 165 132 92 164.409 132 +96 165 129.158 98.9333 165 132 163 165 131.172 +164 165 131.333 183.333 165 132 185 165 130.147 +194 165 130.875 195 165 130.5 87 166 129.75 +97 166 131.684 163 166 131.5 166 165.903 132 +190 165.833 132 189.875 166 132 167 166.567 132 +93 168 129.316 163.769 168 132 167 168 129.091 +168 167.346 132 168 168 130.3 188 168 132 +95 168.273 132 166 169 130.227 167 169 129.1 +189 168.857 132 189 169 131.571 190 169 130.105 +62.75 170 132 63 169.8 132 64 169.143 132 +64 170 129.429 94 170 129.882 190 170 129.45 +191 170 129.778 63 171 131.571 62.75 171 132 +169 170.895 132 176 171 131.25 175.143 171 132 +197 170.625 132 175 172 131.667 176 172 130.5 +178 172 131.71 179 172 131.71 179.429 172 132 +198 172 130.444 199 172 131.4 176 173 131.308 +177 172.667 132 179 173 131.71 179.273 173 132 +175 174 131.4 198 173.909 132 198 175.4 132 +199 175.8 132 197 176.6 132 199 177 131.125 +180 179 131.71 196 178.4 132 201 179 130.444 +200.067 179 132 178 180 131.71 178 179.75 132 +178.75 180 132 92 181 132 178 181 130.895 +177 181 132 178.875 181 132 208.526 181 132 +92 182 131 179 181.091 132 196 181.455 132 +92 183 129.333 171 182.941 132 199 183 130.696 +201 183 130.364 206 183 130 172 183.684 132 +180.875 184 132 198 184 129.794 199 184 130.6 +200 184 130.625 202 184 130.821 203 184 131.471 +204 184 131.824 182 184.6 132 183 184.6 132 +184 184.625 132 199 185 131.4 200 185 130.5 +201 185 130.2 202 185 130.8 203 185 131.1 +204 185 130.962 168 185.045 132 171 186 129.45 +172 186 129.75 191 186 131.571 201 186 129.2 +175 187 131.217 176 188 129.783 178 189 130.579 +179 188.75 132 179 189 131.625 93 189.8 132 +179 189.167 132 193 189.323 132 85 191 131.727 +91 190.056 132 86 68 134.625 87 68 134.125 +85 69 134.206 87 69 134.419 91 69 134.824 +203 72.6538 135 207.278 75 135 208 75.6842 135 +215 77 133.444 49 78 134.8 214 78 134.769 +210 79 132.529 214 78.2857 135 216 78.1333 135 +213 79.8667 135 214 80 133.962 213 81 133.5 +212.375 81 135 211.714 82 135 207 82.375 135 +207 83 134.118 208 83 133.846 209 83 134.824 +209.333 83 135 211.714 83 135 43 84 133.696 +44 84 134.419 44 83.1429 135 206 84 132.125 +207 84 133.846 208 84 133.5 208.714 84 135 +209 83.3333 135 210 84 134.769 210.167 84 135 +213 84 134.143 214 83.5 135 213.667 84 135 +42 85 133 41.5652 85 135 43 85 132.581 +44 85 134.471 207 85 134.75 207.125 85 135 +208 84.4167 135 210 84.3333 135 213.75 85 135 +214 84.3333 135 215 84.625 135 215.25 85 135 +42 86 132.261 43 86 133.2 207 86 132.5 +211 86 133.8 41 87 132.316 43.5909 87 135 +206 87 132.143 41 88 132.1 42 88 132.882 +206 88 133.227 40 89 132.115 205 89 132.6 +206 89 133.714 207 89 133.412 39 90 133.8 +206 89.6 135 40 91 132.176 216 91 132.857 +215 94 132.75 214.4 94 135 215.6 94 135 +214 95 133.421 213.333 95 135 216 94.4 135 +214 96 133.444 215 96 132.9 215.737 96 135 +216 95.375 135 214 97 133.778 215 97 134.053 +214 97.9167 135 210 102 134.71 210 101.727 135 +202.278 103 135 230.5 103 135 40 115 132.581 +196 117.143 135 193.25 119 135 192 120.75 135 +192.667 121 135 43 122 133.355 44.25 122 135 +190.75 122 135 192.75 122 135 193.25 122 135 +193 122.167 135 40 124 132.176 188 123.833 135 +189 123.833 135 190 123.286 135 218 123.421 135 +192 125 132.75 193 125 132.316 218.806 125 135 +192 126 132.75 148 127 132.857 149 127 133.2 +150 127 132.176 209.375 127 135 133 128 133.444 +186.143 128 135 206.125 128 135 209.909 128 135 +216 128 133.2 148 129 133.543 149 129 133.543 +156 129 133.543 163 129 132.857 174 129 132.103 +206 129 134.182 207 129 133.25 216 128.75 135 +154 130 133.543 204 130 134.71 203.85 130 135 +205 130 134.25 206 130 134.684 207 129.368 135 +215 130 134.308 215 129.25 135 148 131 132.857 +163 131 133.543 174 131 134.286 189.353 131 135 +204 130.75 135 215 131 133 215.667 131 135 +151 132 134.118 160 132 133.5 162 132 133.8 +163 132 133.543 166 132 134.471 199 131.462 135 +215 132 133.688 214.125 132 135 150 133 134.143 +189 133 133.75 189 132.286 135 191 133 132.833 +44 134 132.29 45 134 132.529 51 134 134.143 +52 134 134.206 150 134 134.032 156 134 133.645 +157 134 133.889 163 134 133.125 164 134 133.147 +175 134 134.727 189 134 133.227 190 134 133.364 +190.5 134 135 46 135 132.882 47 135 132.882 +51 134.833 135 53 135 134.333 154 135 134.471 +155 135 134.143 164 135 133.455 187.643 135 135 +189 135 133.345 198.333 135 135 47 136 134.559 +47.625 136 135 165 136 134.531 190 136 133.966 +211 136 134.654 210.8 136 135 213 136 133.8 +214 136 134.032 215 136 134.333 46 137 133.182 +47 137 134.559 48 137 134.897 49 136.583 135 +57 137 134.8 57 136.75 135 189 136.455 135 +190 136.526 135 195.5 137 135 211 137 134.118 +210.091 137 135 212 137 134.118 214 137 133.8 +215 137 132.857 216 137 132.1 46 138 132.529 +47 138 133.182 56 138 134.217 56 137.143 135 +55.25 138 135 90 138 132.882 161 138 134.714 +55 139 132.429 55 138.1 135 215 138.609 135 +117 140 134.6 116.714 140 135 118 140 133.8 +119 139.455 135 119 140 134.053 119.316 140 135 +185 139.867 135 69 141 133.8 73 141 133.457 +76 141 134.824 76 140.6 135 79 141 133.457 +186 140.444 135 186.333 141 135 203 141 134.032 +204 141 133.35 207.455 141 135 77 142 132.909 +104.5 142 135 105.75 142 135 168 142 132.321 +169 142 133.179 170 142 133.32 185 142 132.13 +186.333 142 135 203 142 134.824 105 142.6 135 +186 143 132.429 186.545 143 135 187 143 132.857 +159 144 134 170 143.167 135 171 143.391 135 +172 143.105 135 186 143.429 135 187 144 134.25 +187.25 144 135 159 145 133.636 188 144.375 135 +113.5 146 135 115 146 134.7 161 146 133.95 +160 146 135 161 145.125 135 162 146 135 +113 147 132.789 113 146.125 135 161 147 134.125 +160.563 147 135 162 147 134.679 172 147 132.091 +113 148 133.3 112.37 148 135 161 147.467 135 +162 147.273 135 173 148 133.645 174 148 134.739 +178 148 133.6 178 147.417 135 50 149 133.714 +49.8125 149 135 51 149 133.6 112.818 149 135 +114 149 134.727 125 148.04 135 174 149 134 +175 149 133.846 177 149 133.355 178 149 133 +178.783 149 135 51 150 133 50.0909 150 135 +52 149.067 135 53 149.565 135 53.9091 150 135 +113 149.133 135 178 150 133.688 53 150.667 135 +177 151 134.71 178 151 133.8 210 151 132.692 +211 151 132.346 179 152 133.969 179.917 152 135 +210 152 133.125 63 153 133 63.7273 153 135 +178 152.188 135 180 153 133.258 181 153 134.55 +65 153.625 135 66 153.667 135 66.2727 154 135 +180 154 132.103 182 153.769 135 210 154 132.375 +211 153.6 135 179.409 155 135 182.955 155 135 +212 154.263 135 214 155 134.471 213.333 155 135 +65 156 133.688 64.5625 156 135 68 155.3 135 +175 156 132.4 177 156 132.875 183 156 134.885 +198 155.65 135 210 156 132.2 212 156 134.625 +213 156 133.4 214 156 133.826 65 157 132.261 +98.6774 157 135 165 157 134.16 164.364 157 135 +165 156.417 135 176 156.867 135 175.867 157 135 +181 156.077 135 182 157 133.2 183 157 134.333 +198 157 132.529 208 157 132.75 210 157 132.273 +61.1429 158 135 67 158 133.258 70 158 132.273 +71 157.625 135 71 158 132.75 195 158 134.471 +197 158 133.543 205 158 133.5 205 157.5 135 +204.333 158 135 208 158 134.609 209.917 158 135 +210 157.909 135 61 159 134.167 61.4545 159 135 +65.9333 159 135 71 158.375 135 72 158.727 135 +77 158.654 135 208 159 134.6 209 159 134.609 +60 160 132.875 61 160 134.2 62 160 134.9 +78 160 132.222 79 159.217 135 80 159.263 135 +80 160 133.765 200.435 160 135 208 159.143 135 +209 159.214 135 60 161 132.333 61 161 133 +62 161 132.692 79 161 132.771 81 161 133.8 +87.5 161 135 95.5 161 135 169 161 133.385 +182 161 134.143 200 160.909 135 77 162 132.577 +81 162 134.471 92 161.778 135 178 162 132.581 +179 162 132.222 182 162 133.421 186 161.931 135 +72 163 132.818 76 162.323 135 81 162.261 135 +93 162.182 135 178 163 133.846 177.333 163 135 +181 163 133.111 182 163 133.355 183 162.364 135 +183 163 133.645 98 163.769 135 182 164 132.794 +183 164 132.194 187.947 164 135 190 164 132.333 +191 164 132.333 88 164.25 135 88 165 133 +91 164.957 135 168.222 165 135 183 165 132.789 +189 165 133.853 190 165 132.882 195 165 134.25 +195 164.333 135 98.4 166 135 149.588 166 135 +165 166 133.962 166 166 133.125 169 166 133.5 +168.5 166 135 172 166 132.261 173 165.091 135 +182 165.292 135 186 165.955 135 187 166 134.842 +195 166 134.143 97 166.706 135 165 166.6 135 +166 166.192 135 168 166.125 135 168 167 133.174 +169 166.286 135 173 167 134.25 172.125 167 135 +173.875 167 135 195 167 134.053 94 167.091 135 +94 168 132.778 95 167.7 135 173 168 134.679 +174 168 134.032 189 168 132.6 195 168 134.419 +196 167.25 135 196 168 134.471 165.433 169 135 +174 169 133.5 196 169 133.625 63 170 135 +64 170 133.2 174 170 133.645 175 170 133.174 +195 169.545 135 196 170 133.688 197 169.75 135 +197 170 133.875 63 171 134 168 170.5 135 +176 170.067 135 196.417 171 135 86 172 133.543 +174.3 172 135 178 171.8 135 179.875 172 135 +219 171.739 135 83 173 134.778 84.7143 173 135 +87 173 133.543 173.926 173 135 174 172.75 135 +177 172.733 135 179.917 173 135 205 173 133.696 +205 172.091 135 206 173 133.565 206.647 173 135 +81 174 134.52 80.5556 174 135 82 174 134.333 +85 173.667 135 176 173.25 135 178 173.273 135 +179.182 174 135 198 173.857 135 205 173.667 135 +206 174 135 81 174.8 135 86 175 134.087 +174 175 134.143 175 175 132.429 175.316 175 135 +179 174.333 135 86 176 133.421 87 176 133.889 +198 175.364 135 199.261 176 135 207 176 134.053 +207 175.25 135 208 175.571 135 208 176 132.429 +86 177 134.182 85.8696 177 135 87 177 134.308 +176 177 134.609 177 177 134.667 207 177 134.069 +208 177 134.182 208.545 177 135 86 177.333 135 +87 177.545 135 176 178 134.609 177 178 134.625 +177.375 178 135 180 177.222 135 181 177.684 135 +199.929 178 135 200.091 178 135 201 177.565 135 +207 178 132.6 208 178 133.091 208.737 178 135 +176 178.6 135 177 179 134.55 178 178.417 135 +178 179 133.688 179 179 132.273 181 179 132.158 +196 179 133.227 198 178.933 135 199 178.684 135 +177 180 135 179 180 132.75 180 180 132.833 +181 180 132.167 182.065 180 135 197 179.429 135 +197 180 134.143 178 180.909 135 178 181 134.625 +180.375 181 135 182 181 133.364 196 181 133.071 +196.818 181 135 197 180.667 135 198 180.818 135 +177 181.5 135 178 181.333 135 181.125 182 135 +199 182 134.6 182 182.875 135 182 184 134.25 +166 184.045 135 184 184.1 135 174 186 134.318 +180 185.391 135 174 187 132.333 177 186.333 135 +190.4 187 135 90 188 134.609 89.625 188 135 +178 187.087 135 90 189 134.125 90.875 189 135 +174 189 134.1 193 188.591 135 91 189.143 135 +179 190 133.154 180 189.133 135 180 190 132.947 +181 190 134.684 88.4737 191 135 84 192 133.304 +93 191.391 135 80 192.5 135 92 68 135.3 +92.6429 68 138 82 69 136.773 83 69 135.273 +84 68.1923 138 66 70 136.5 66 69.4348 138 +69 70 137.419 72 70 137.824 75 70 136.645 +81 70 136 82 70 136.2 83 70 135.6 +84 70 135.75 86.875 70 138 87 69.9333 138 +89 69.875 138 89.1333 70 138 93 70 136.645 +94 69.1818 138 94 70 137.206 97 70 137.684 +65 71 137.739 66 71 136.962 67 71 137.333 +80 70.9444 138 83 71 137 90 71 137.684 +93 71 136.645 94 71 136.457 97 71 137.217 +97.8571 71 138 63 72 137.679 64 72 137.8 +64 71.3333 138 96 72 137.125 97 72 137.125 +97.875 72 138 62 73 137.778 97 72.875 138 +98 72.3333 138 97.3333 73 138 98 73 137.684 +98.5 73 138 99.25 73 138 101 73 136.889 +202 73 136.2 203 73 136.5 92 73.5 138 +101 74 137.419 202 74 136.636 203 73.5 138 +51 76 136.364 51 75.7778 138 52 76 136.364 +52.75 76 138 211 76 136.147 49 77 136.929 +50 77 135.316 51 77 135.261 52 77 137.053 +52.5 77 138 48 78 136.071 50 78 135.321 +51 78 136.556 47 79 137.455 48 79 135.261 +49 79 136 214 79 136.875 216 79 136.444 +47 80 135.5 48 80 136.5 213 80 135.333 +215 80 136.25 46 81 136.125 213 81 135.536 +214 80.2 138 214 81 136.286 45 82 135.714 +214 82 135.346 218.333 82 138 44 83 135.167 +45 83 135.2 46 83 136.5 207 83 136.071 +209 83 135.15 212 83 135.273 219 83 137.739 +218 83 138 45 84 135.529 207 84 136.5 +208 84 135.714 210 84 135.143 213 84 135.273 +218 84 137.52 217.5 84 138 219 83.3333 138 +207.2 85 138 214 85 135.333 215 85 135.45 +218 85 136.8 218.6 85 138 218 86 138 +40.5 87 138 215 94 135.391 215 95 137.25 +216 95 135.333 215 97 137.571 214 98 135.75 +213 98 138 41.375 99 138 204 99 136.889 +213 99 137.25 213.75 99 138 213 100 137.526 +202 101 135.968 212 101 137.667 211.25 101 138 +212 100.25 138 209.263 102 138 211 101.091 138 +211 102 136.696 210 103 135.938 209.421 103 138 +211.545 103 138 210 103.917 138 42 109 137.545 +42.6842 110 138 197 118 136.25 195 121 137.217 +195.316 121 138 45.5 122 138 192 122 135.783 +193 123 136.5 194.857 123 138 199 124 137.824 +188 125 136.2 189 125 136.2 191 125 135.667 +217 125 137.864 49 126 137.143 188 126 136.355 +190 126 137.032 192 126 136.8 186 127 135.222 +191 126.5 138 208 127 135.6 218 126.375 138 +207 128 136.826 208 128 137.143 99 128.5 138 +103 129 135.882 105 128.172 138 206 129 137.571 +208 129 135.818 97 130 137.471 99 130 136.457 +183 130 136.2 184 130 135.875 204 129.864 138 +205 129.533 138 206 129.333 138 98 131 136.889 +177 131 136.543 186 130.4 138 190 130.143 138 +190.4 131 138 204 131 135.6 203 131 138 +204.444 131 138 31 132 135.783 97 132 136.826 +173 132 135.667 182 132 136.543 184 132 136.25 +198 131.85 138 199 131.739 138 202 132 137.4 +202 131.842 138 203 132 136.25 203.583 132 138 +215 132 138 54 133 136.2 55 133 136.8 +56 132.5 138 89.6 133 138 93 133 136.8 +168 133 136.543 172 133 136.543 175 133 135.194 +177 133 136.2 184 133 136.889 184.909 133 138 +200 133 136.636 201 133 137.55 202 133 136.444 +203 133 137 56 134 137.143 92 134 137.032 +151 134 135.529 152 134 136.543 153 134 135.529 +174 134 135.176 176 134 135.455 180 133.273 138 +184 134 137.625 185 134 137.125 202 134 137.4 +51 135 135.2 54 135 135.6 57 135 136.556 +86 134.833 138 87 135 137.206 152 135 136.543 +160 135 136.355 175 135 135.441 185 135 137 +186 135 136.95 187 134.286 138 187 135 136.421 +201 134.182 138 213 134.727 138 48 136 135.391 +49 136 136.313 50 136 137.8 51 136 137.8 +53 136 135.4 55 136 136.2 57 136 136.125 +58 136 137.1 60 136 136.8 82 136 137.471 +89 136 135.968 152 136 135.968 158 136 136.2 +167 136 136.111 170 136 136.543 174 136 137.118 +176 136 135.577 192.818 136 138 210.6 136 138 +211 135.818 138 219 136 136.962 50 137 136.286 +52 136.533 138 53 136.65 138 55.3333 137 138 +56 137 135.429 153 137 135.857 159 137 136.111 +162 137 135.316 167 137 136.696 170 137 136.543 +177 137 137.55 195 137 137.053 209 137 137.625 +210 136.375 138 219 137 137.516 52 137.467 138 +55.5455 138 138 74 138 136.457 77 138 136.457 +79 138 137.217 80 138 137.217 158 138 137.4 +170 138 136.543 175 138 137.824 209 138 136.5 +210 138 135.794 220 138 137.318 221 138 137.143 +154 139 136.543 171 139 137.679 172 139 137.75 +196 139 136.5 208 139 136.75 209 139 135.875 +210 139 136 211 139 136.429 119 140 136 +155 140 137.471 184 139.625 138 184.2 140 138 +203 140 135.158 207 140 136.5 209 140 136.5 +182.684 141 138 202 140.056 138 208 140.75 138 +103.563 142 138 105.9 142 138 130.735 142 138 +156 142 137.4 185.7 142 138 201 142 136.457 +202 142 135.794 157 143 137.857 201 143 137.032 +202 143 136.645 203 143 137.217 205 142.69 138 +207 142.286 138 170 144 136.154 183 143.182 138 +185 143.261 138 188 144 135.29 155 145 137.182 +170.056 145 138 161.667 146 138 170 146 136.5 +162 146.25 138 169 147 137.471 168.143 147 138 +33.8235 148 138 175 148 137.053 50 149 135.333 +51 149 136.105 112 149 135.794 153 149 135.2 +51 150 137 53 149.522 138 54 150 135.6 +54.5 150 138 113 150 137.438 114 149.37 138 +163 150 137.609 163 149.625 138 179 149.5 138 +56.7273 151 138 58 150.125 138 58 151 137.222 +58.5833 151 138 112 150.933 138 163 151 137.71 +164 151 137.7 164.273 151 138 58 151.368 138 +173 151.333 138 176 151.5 138 64 153 135.333 +210 153 135.176 65 154 135.333 95 153.067 138 +182 154 135.783 210 154 138 121.176 155 138 +181 155 137.5 181 154.375 138 182 155 136.853 +67 155.222 138 98.5667 156 138 164.6 156 138 +182 156 136 211 156 135.692 166 157 135.667 +167 157 137.032 57 158 136.875 56.8 158 138 +58 158 135.375 59 158 135.375 63 158 137.739 +71 158 136.8 92.7368 158 138 165 157.875 138 +165.222 158 138 168 158 137.824 193 158 136.765 +207 157.467 138 208 158 137.25 208 157.947 138 +208.25 158 138 57 158.75 138 58 158.875 138 +59 158.467 138 62 159 135.667 72 159 135.375 +77 159 136.5 81 158.44 138 86 159 137.118 +90 159 136.6 168 159 137.471 170 159 137.735 +192 159 137.4 208 159 135.857 209 158.75 138 +209 159 137.25 73 160 136.2 74 160 136.457 +76 160 137.9 77 159.6 138 168 160 136.929 +171.37 160 138 194 160 136.543 73 161 136.2 +76 160.143 138 84 161 136.543 86 161 136.355 +91 161 136.5 106 161 136.714 105.25 161 138 +106.375 161 138 172.067 161 138 194 161 136.8 +193.263 161 138 195 161 137.25 195.467 161 138 +199 161 138 91 161.286 138 91 162 136.636 +91.625 162 138 106 162 136.875 105.25 162 138 +106.375 162 138 167.588 162 138 195 162 137.55 +200 161.75 138 76.9231 163 138 78 163 135.652 +89 163 136.826 91 162.333 138 176.935 163 138 +187.278 163 138 194 163 136.263 195 163 137.25 +77.75 164 138 194 164 137.143 195 164 135.75 +78 164.25 138 87 164.429 138 90.4348 165 138 +98.5 165 138 119.294 165 138 168 165 135.947 +91 166 136.889 167 165.565 138 168 166 135.222 +90 167 137.735 98 167 137.053 97.25 167 138 +99 166.417 138 99 167 136.688 165.567 167 138 +169 167 135.833 171.857 167 138 87 168 137.1 +86.6667 168 138 89 168 136.8 90 168 137.323 +91 167.857 138 93 167.261 138 99 168 136.565 +100 167.813 138 100 168 137.182 174 167.091 138 +196 167.25 138 63 169 136.875 62.625 169 138 +63 168 138 87 169 136.25 88 169 136.688 +89 169 136.543 90.15 169 138 98.8696 169 138 +100 169 135.273 201 169 137.71 63 169.429 138 +86 170 135.115 88 170 136.645 89 170 137.032 +196 170 136.313 196 169.25 138 197 170 135.45 +201 170 137.667 202 170 137.609 202.188 170 138 +89 171 137.684 100 170.133 138 174 170.636 138 +175 171 137.842 175 170.75 138 197 171 135.778 +201 170.188 138 202 170.75 138 83 172 136.421 +82.4444 172 138 176 172 136.688 177 172 136.138 +178 172 135.48 179 172 135.875 198 172 135.882 +84 173 136 173 172.867 138 174 172.455 138 +175 173 137 178 173 135.45 179 173 136.375 +198 173 135.581 205 173 137.727 206 173 137.75 +80.625 174 138 82.75 174 138 84 173.526 138 +85.0667 174 138 81 174.375 138 85.85 175 138 +175.368 175 138 205 175 137.684 205 174.5 138 +169 176 137.8 168.818 176 138 199 176 135.667 +202.286 176 138 203 175.474 138 204 176 137.323 +205 176 136.412 206 175.417 138 169 177 136.895 +168 177 138 175.75 177 138 176 176.571 138 +177 176.364 138 177.467 177 138 198 177 136.4 +199 177 136.4 202 177 137.55 201.813 177 138 +205 177 136.2 206 177 136 169 177.875 138 +177.333 178 138 179 178 137.143 179.4 178 138 +198 178 136.556 199 178 136.444 201.667 178 138 +203 178 135.375 204 178 135.9 208.526 178 138 +177 179 136.125 177 178.375 138 178 179 137.333 +179 179 137.5 179.167 179 138 197 179 135.45 +201 179 137.333 178 180 137.5 179 180 135.75 +181 180 137.318 182 180 135.333 201.684 180 138 +208.077 180 138 172 180.8 138 181 181 137 +181.714 181 138 198 181 137 201.087 181 138 +209 180.923 138 172 182 136.263 178 182 135.375 +183 182 136.364 197 181.333 138 198 181.045 138 +200 181.727 138 172 183 135.273 173 182.071 138 +173 183 135.4 182 183 135.75 183 183 135.923 +191 183 137.4 190.667 183 138 191 182.8 138 +191.273 183 138 184 183.591 138 187 184 136.645 +188 183.818 138 188.5 184 138 191 183.6 138 +167 184.071 138 189 184.667 138 189 185 137.864 +72 185.75 138 85 186 137.125 85 185.364 138 +89 186 135.577 88.3 186 138 196 186 135.462 +195.313 186 138 72 187 136.25 73 187 136.696 +84 187 136.969 85 187 137.032 90 187 135.857 +90.3226 187 138 174 186.091 138 177.944 187 138 +190.455 187 138 195 187 136.5 196 187 135.577 +198 187 135.231 73 188 137 89 188 135.652 +90.3333 188 138 191 187.75 138 73 188.158 138 +88.8 190 138 90.1053 190 138 180 189.125 138 +182.091 190 138 74 190.842 138 179 190.913 138 +180 190.875 138 181 190.929 138 182 190.286 138 +86 192.045 138 86 69 140.571 87 69 139.5 +89 69 140.625 89.1333 69 141 86.5333 70 141 +89 70 138.857 97 69.2 141 63 71 140 +64 71 138.136 71 71 138.529 72 71 138.882 +79 71 138.12 80 71 138.143 86 71 138.24 +87 70.3684 141 87 71 139 88 71 139.429 +89 71 139.08 98 70.5 141 98 71 138.5 +99 71 138.3 99.6429 71 141 62 72 138.214 +65 72 138.909 73 72 139.355 81 72 138.24 +87 72 139.345 98 72 139 100 72 138.857 +60 73 139.8 61 73 138.833 63 73 138.2 +74 73 139.8 76 73 139.889 80 73 140.217 +81 73 139.556 86 73 139.3 87 73 139.556 +89 73 138.931 97 73 138.167 99 73 139 +102 73 138.96 58 74 139.5 59 74 138.6 +60 74 138.6 61 74 139 62 74 139.4 +63 74 140.1 77 74 139.645 78 74 139.889 +80 74 139.889 89 74 140.1 95 73.5417 141 +98 74 139.5 56 75 138.75 58 75 138.25 +59 75 138.667 60 75 139.75 64 74.6 141 +77 75 140.471 79 74.8333 141 83 75 139.889 +87 75 140.806 90 75 139.457 97 75 140.769 +98 75 140.318 99 75 140.684 203 75 140.6 +204 74.1111 141 52 76 139.125 54 76 138.545 +53.25 76 141 55 76 138.316 57 76 138.818 +59 76 139.962 84 76 139.8 85 76 139.8 +86 76 140.206 100 76 139.7 101 76 139.7 +53 76.375 141 53 77 139.636 87 77 140.471 +99 77 140.679 102 77 140.824 54 77.75 141 +96 78 139.8 99 78 140.032 100 78 140.206 +47.3846 79 141 213.737 80 141 217 80 138.103 +53 80.25 141 216 80.9091 141 207 82.9444 141 +48 84 140.471 219 84 139.2 218 84.75 141 +219 84.4 141 215 96 138.29 214 97 139.5 +212.5 98 141 213 97.6842 141 214 98 139.8 +211.778 99 141 212 98.5 141 213.737 100 141 +211.167 101 141 210 104 139 211 104 139.696 +42 105 138.667 210 104.5 141 211 105 140.8 +210.5 105 141 229 105.25 141 42 107 138.783 +210 107 139.091 210 108 140.526 209.25 108 141 +43 109 138.968 43 111 138.529 45 112 140.471 +201 114 140.76 201 115 139.44 202.8 115 141 +204 115 140.76 203.5 115 141 204.286 115 141 +199 116 138.469 202 116 139.143 203 115.5 141 +203 116 140.647 203.5 116 141 201 117 140.118 +202 117 139.846 203 116.167 141 200 118 140.76 +200.167 118 141 201 117.333 141 48 119 139.8 +198 119 138.818 199 119.818 141 200 119.333 141 +197 122 140.143 198 122 140.8 200 121.25 141 +50 123 139.457 198 123 139.645 199 123 138.261 +200 123 138 197 124 139.543 200 124 138.8 +50 125 139.579 53 125 139.8 99 124.833 141 +103 125 140.824 196 125 138.667 198 125 139.625 +199 125 139.35 200 124.55 141 218 125 139 +100 126 140.143 193 126 138.1 196 126 139.6 +199 125.407 141 218 126 138.818 46 127 140.609 +45 127 141 100 127 139.853 102 127 140.545 +216 127 138.333 217 127 138.231 53 128 139.313 +192 128 139.5 216 128 138.321 54 129 139.645 +57 129 139.457 58 129 139.8 96 129 139.2 +189 129 138.6 131 130 138.103 188 130 138.517 +191 130 139.421 204 130 138.45 205 130 138.75 +206 130 138.2 221 130 139.8 222 129.474 141 +222 130 139.333 55 131 138.529 56 131 139.2 +130 131 140.182 132 131 138.6 188 131 139.636 +204 131 138.571 215 131 138.321 221 131 139.091 +220.125 131 141 222 131 139.2 60 132 139.457 +87 132 139.457 186 132 140.8 187 131.565 141 +188 131.909 141 188.071 132 141 190.5 132 141 +197.3 132 141 203 132 139.75 63 133 139.8 +88 133 138.882 185.923 133 141 201 132.75 141 +202 132.167 141 71 135 139.457 182 134.591 141 +112.87 136 141 115 135.955 141 178 136 140.531 +178.625 136 141 184 136 139.607 215 135.75 141 +52 137 139.75 55 136.462 141 173 137 138.455 +182.556 137 141 184 137 139.125 185 137 138.9 +190.286 137 141 194.667 137 141 199 136.25 141 +216 136.667 141 217 137 138.783 216.056 137 141 +129.308 138 141 183 138 139.781 184 138 139.031 +185 138 139.781 186 138 140.7 193.091 138 141 +207 137.727 141 173 139 139.091 174 139 138.094 +175 139 138.5 179 139 138.115 184 139 138.714 +184.889 139 141 192 138.091 141 193 139 139.889 +194 139 138.667 213 138.867 141 212.75 139 141 +105 139.87 141 109 140 139.765 110 140 140.25 +176 139.828 141 179 140 138.652 180 140 139.636 +191 140 140.118 192 140 139.455 194 140 138.857 +196 140 138.103 197 140 139.44 210 140 141 +103.63 141 141 106 141 139.846 108 141 140.735 +107.25 141 141 109 141 139.6 110.875 141 141 +169 140.818 141 171 141 139.645 173 140.261 141 +180 141 139.778 181 141 140 184.273 141 141 +193 141 139.455 194 141 139.147 195 141 139.412 +196 141 138.9 197 141 139.444 198 141 139.696 +199 141 140.438 106.4 142 141 170 142 140.8 +182 142 139.038 192 141.5 141 198 142 140.4 +199 142 139.385 157 144 138.316 181.87 144 141 +183 144 139 184 144 140.053 184.316 144 141 +201 144 138.9 207.857 144 141 170 145 139.5 +201 145 138.947 202 145 140.571 127.548 146 141 +161 146 138.5 168 145.222 141 201 146 139.875 +161 147 138.75 162 147 138.529 168 147 138.158 +167.053 147 141 211 147 140.667 210.818 147 141 +211 146.714 141 114 148 140.444 125 147.481 141 +212 148 140.76 211.833 148 141 212 147.5 141 +213 147.625 141 213 148 140.182 177.667 149 141 +213 149 139.846 214 149 139.385 220 149 139.364 +219 149 141 220 148.143 141 53 150 139.138 +64 150 140.71 164.933 150 141 214 149.875 141 +57.25 151 141 58 150 141 59 151 141 +62 151 140.118 63 151 140.118 64 151 140.471 +162.333 151 141 173 150.955 141 178 151 140.9 +179 153 138.375 180 153 138.75 180 154 138.75 +181 154 138.29 92 155 140.419 167 155 140.625 +169 155 139.8 170 155 139.8 207 155 140.211 +208 155 139.962 210 155 138.529 67 156 140.864 +68 155.565 141 69 155.222 141 71.5556 156 141 +99.0303 156 141 164.867 156 141 167 156 138.316 +168 156 139.147 169 156 139.2 170.773 156 141 +208 156 140.806 209 156 139.235 65 157 139.5 +90 157 138.783 92 157 139.355 168 157 138.794 +170 157 139.543 187 157 140.8 187 156.818 141 +188 157 139.645 191 156.192 141 191 157 138.9 +195 156.684 141 197 156.848 141 57 158 138.474 +58 158 139.105 59 158 139.05 66 158 140.9 +91 158 139.2 92 158 139.355 176 157.391 141 +187 158 140.625 188 158 139.111 191 158 138.194 +209 158 138.375 84 158.263 141 92 159 138.529 +189 159 139.125 190 159 139.25 200 159 138.857 +92 160 138.577 102 160 140.667 102.6 160 141 +166.63 160 141 188 159.13 141 199 160 140.1 +199 159.7 141 219 160 140.4 220 160 139.875 +76 161 138.529 77 161 139.8 78.5 161 141 +93 161 138.167 103 161 139.895 104 160.813 141 +104 161 138.75 106 161 138.391 181 160.333 141 +200 161 138.333 219 160.75 141 82.75 162 141 +89 162 139.2 90 162 138.231 90 161.077 141 +104 161.75 141 106 162 138.391 172 162 140.318 +193 161.909 141 193.083 162 141 195 162 139.125 +77.8125 163 141 85.8125 163 141 90 163 138.2 +91 163 139.304 185.375 163 141 194 163 140.063 +77.4167 164 141 79 163.909 141 91 163.813 141 +185.4 164 141 194 164 138.273 195 164 138.529 +77 165 138.938 78 165 140.077 178 164.778 141 +195 165 138.529 76 166 139.895 77 166 139.174 +172 166 140.739 184 165.52 141 195 166 138.529 +64 167 140.294 63 167 141 64 166 141 +76 167 139.565 75.45 167 141 77 167 140.55 +97 166.8 141 171 166.231 141 172 167 138.316 +173 167 138.677 200.125 167 141 76 168 140.125 +99 168 140.75 99 167.875 141 173 168 138.321 +174 168 138.968 196 168 138.29 63 169 138.818 +75 169 140.25 74.6316 169 141 100 169 139.304 +174 169 139.313 195 169 139.571 202 169 138.455 +203 169 139.853 205 169 140.667 75 170 140.667 +75.2 170 141 85.32 170 141 174 170 138.75 +175 170 138.29 203 170 140.167 201 170.783 141 +202 171 138.273 202.833 171 141 83 172 140.727 +83.125 172 141 82 173 138.778 83 173 140.318 +84 173 139.579 87 172.545 141 173 173 141 +174 173 139.286 81 174 138.29 82 174 138.265 +87 174 139.8 169 175 140.526 204.714 175 141 +205 174.895 141 168.545 176 141 170 176 139.421 +174 175.467 141 176 177 138.375 177 177 138.75 +202 177 139.125 202.172 177 141 170 177.4 141 +180 178 138.391 208.091 178 141 180 179 139 +51.5667 180 141 180 180 138.231 203.261 180 141 +172.286 181 141 182 181 138.545 183 182 139.636 +185 182 139.929 186 182 140.667 198 181.182 141 +171 183 140.5 175 182.517 141 182 183 138.75 +185 183 140.217 186 183 139.6 191 183 138.6 +167.143 184 141 182 184 139.5 183 183.429 141 +189 184 138.75 189.429 184 141 71 185 139.35 +71 184.267 141 90.5667 185 141 180 184.333 141 +86 185.684 141 86 186 139.714 87 185.933 141 +87 186 140.833 88 186 139.227 74 187 138.667 +75 186.455 141 83 187 138.136 86 187 139.333 +87 187 139.696 106 186.433 141 169 187 140.167 +169 186.688 141 178.625 187 141 192 187 139.125 +74 188 138.857 78 187.895 141 81 188 139.543 +83 188 138.091 84 188 138.103 86 188 140.727 +87 188 139.889 169 188 139.655 168.409 188 141 +170 187.182 141 170.75 188 141 193 187.033 141 +74 189 140.647 75 189 139.5 84 189 139.846 +172.467 189 141 73.9091 190 141 76 190 139.364 +82 190 139.543 83 190 139.543 84 190 140.419 +89 189.75 141 89 190 139.8 180 190 140.1 +180.75 190 141 182 189.889 141 80 191 139.2 +83 191 139.543 84 191 140.032 85 191 140.727 +89 191 139.235 179 190.091 141 182 190.182 141 +84 192 138.794 88 192 139 90 192 139.2 +89 193.13 141 68 68.8529 144 67.5455 69 144 +69 68.6571 144 70 68.4857 144 71 68.4857 144 +72 68.4857 144 73 68.4857 144 74 68.3529 144 +75 68.3429 144 76 68.3529 144 77 68.5862 144 +78 68.7391 144 79 68.9444 144 79.1667 69 144 +83 69 144 91 69 144 92 69 144 +93 69 144 94 69 144 65 69.8235 144 +64.5 70 144 66 69.3529 144 67 69.2143 144 +80 69.1515 144 81 69.1818 144 82 69.1818 144 +84 69.2609 144 85 69.9444 144 85.0833 70 144 +89.2273 70 144 90 69.2609 144 95 69.2727 144 +96 70 144 63 70.8235 144 62.7391 71 144 +64 70.2069 144 86 70.4783 144 87 70.7857 144 +88 70.7391 144 89 70.2941 144 97 70.6 144 +98 71 141.882 97.25 71 144 99 71 143.7 +62 71.7727 144 61.7727 72 144 98 72 141.316 +97 72 144 99 72 141.882 100 71.9167 144 +99.9231 72 144 101 71.7391 144 101.353 72 144 +60 72.8235 144 59.5 73 144 61 72.5 144 +97.3158 73 144 99 73 141.545 99.6207 73 144 +102 72.3793 144 102.529 73 144 57.5 74 144 +58 73.8235 144 59 73.1765 144 73 74 141.783 +87 74 141.316 94.6471 74 144 95 73.6667 144 +95.5 74 144 98 73.7222 144 99 74 142.364 +98.4545 74 144 99.2609 74 144 103 73.7273 144 +103.182 74 144 56 75 144 57 74.2609 144 +67.3333 75 144 68 74.75 144 69 74.5455 144 +70 74.3182 144 71 74.2857 144 72 74.2857 144 +73 74.7727 144 73.4167 75 144 94.0833 75 144 +95.6471 75 144 99 74.4615 144 103.364 75 144 +204 74.3929 144 203.393 75 144 205 74.1765 144 +206 74.7857 144 206.261 75 144 54 76 144 +55 75.4074 144 63.0909 76 144 64 75.6429 144 +65 75.4444 144 66 75.3182 144 67 75.1176 144 +74 75.25 144 75 75.697 144 76 75.8485 144 +76.4545 76 144 80 76 141.818 94 76 141.25 +95 75.4074 144 103.364 76 144 203.324 76 144 +207 75.5 144 207.739 76 144 50.7059 77 144 +51 76.8529 144 52 77 144 53.0833 77 144 +61 77 143.833 61.2 77 144 62 76.75 144 +63 76.0588 144 77 76.1765 144 78 76.2222 144 +79 76.5238 144 79.9091 77 144 103.036 77 144 +203.647 77 144 208 76.2069 144 209 76.8148 144 +209.227 77 144 49 78 144 50 77.4286 144 +53 77.0909 144 60.0909 78 144 61 77.0909 144 +80 77.0455 144 81 77.375 144 82 77.75 144 +82.3636 78 144 84 78 142.364 102.429 78 144 +103 77.0588 144 204 77.5217 144 204.324 78 144 +210 77.6071 144 210.688 78 144 48 78.8214 144 +47.8148 79 144 58.0909 79 144 59 78.5455 144 +60 78.0455 144 83 78.3182 144 84 78.4444 144 +85 78.4444 144 86 78.5455 144 86.9091 79 144 +89 79 141.75 101.389 79 144 102 78.5217 144 +204.647 79 144 211 78.1515 144 212 78.5 144 +212.739 79 144 47.4783 80 144 55 80 144 +56 79.7059 144 57 79.75 144 58 79.0588 144 +87 79.0455 144 88 79.4444 144 89 79.5455 144 +90 79.6429 144 91 79.8182 144 91.6667 80 144 +100.059 80 144 101 79.3043 144 205 79.7059 144 +205.147 80 144 213 79.1765 144 214 79.697 144 +214.455 80 144 46 81 144 47 80.5 144 +53 81 141.75 54.5833 81 144 92 80.1176 144 +93 80.1176 144 94 80.4118 144 95 80.75 144 +96 80.75 144 97 80.6 144 98 80.375 144 +99 80.4118 144 100 80.0455 144 205.5 81 144 +215 80.3529 144 216 80.697 144 216.455 81 144 +45 81.6071 144 44.6667 82 144 52 82 143.833 +52.0909 82 144 53 81.6429 144 54 81.3182 144 +205.971 82 144 217 81.5217 144 217.478 82 144 +44 83 144 51.0588 83 144 52 82.0588 144 +206 82.0435 144 206.647 83 144 218 82.9231 144 +218.056 83 144 43.3529 84 144 49.7059 84 144 +50 83.7826 144 51 83.0455 144 207 83.6667 144 +207.176 84 144 218 84 142 219 83.7391 144 +219.273 84 144 43 84.6667 144 42.7857 85 144 +48.6207 85 144 49 84.5217 144 207.647 85 144 +220 84.5926 144 220.478 85 144 42 86 144 +48.0357 86 144 207.824 86 144 221 85.6667 144 +221.176 86 144 41.2143 87 144 47.5294 87 144 +48 86.0588 144 207.971 87 144 221.824 87 144 +41 87.2727 144 40.5152 88 144 47.0357 88 144 +208 87.1667 144 208.179 88 144 222 87.5 144 +222.176 88 144 40 89 144 46.697 89 144 +47 88.0909 144 208.353 89 144 222.667 89 144 +39.5152 90 144 46.3636 90 144 208 90 144 +218 90 143.824 217.909 90 144 218 89.9412 144 +218.036 90 144 223 89.6471 144 223.261 90 144 +39 91 144 46.0357 91 144 207.586 91 144 +218 91 143.833 217.909 91 144 218.045 91 144 +223.793 91 144 38.5152 92 144 45.697 92 144 +46 91.0909 144 207.486 92 144 218 91.0625 144 +224 91.5 144 224.176 92 144 38.1765 93 144 +45.5455 93 144 207.343 93 144 224.485 93 144 +38 93.5 144 37.8235 94 144 45.3636 94 144 +207.176 94 144 224.824 94 144 37.5152 95 144 +45.0357 95 144 207 95 144 225 94.5 144 +225.176 95 144 37.1765 96 144 44.8529 96 144 +45 95.1667 144 206.824 96 144 225.485 96 144 +37 96.5 144 36.8235 97 144 44.6765 97 144 +206.515 97 144 225.824 97 144 36.6571 98 144 +44.6765 98 144 206.176 98 144 226 97.5 144 +226.176 98 144 36.3429 99 144 44.5143 99 144 +206 99 144 211.793 99 144 212 98.4545 144 +213 98.4 144 213.462 99 144 226.485 99 144 +36.1765 100 144 44.5 100 144 205.824 100 144 +211.75 100 144 213.333 100 144 226.824 100 144 +36 100.5 144 35.8235 101 144 44.3529 101 144 +205.821 101 144 211.522 101 144 213 101 144 +227 100.353 144 227.324 101 144 35.5152 102 144 +44.3636 102 144 205.393 102 144 210.667 102 144 +211 101.522 144 213.179 102 144 227.647 102 144 +35.1765 103 144 43 103 141.682 44 103 142.364 +44.3636 103 144 205.214 103 144 210.353 103 144 +213.185 103 144 228 103 144 35 103.5 144 +34.8235 104 144 44.5455 104 144 205 103.353 144 +204.676 104 144 210 104 141.462 210.379 104 144 +212.706 104 144 213 103.5 144 228 104 144 +34.5152 105 144 44.697 105 144 204.353 105 144 +210.478 105 144 212.538 105 144 228.324 105 144 +34.1765 106 144 44.8529 106 144 204 106 144 +209.944 106 144 210 105.917 144 213 105.5 144 +213.261 106 144 221 106 143.7 220 106 144 +221 105 144 221.091 106 144 228.647 106 144 +34 106.545 144 33.8529 107 144 45 106.833 144 +45.0294 107 144 203.676 107 144 209 107 144 +212 107 141.545 213 107 142.2 213.522 107 144 +219 107 143 218.739 107 144 219 106.647 144 +220 107 143 220.333 107 144 221 106.077 144 +229 107 144 33.5152 108 144 45.0294 108 144 +203.353 108 144 208.708 108 144 212 108 144 +213 107.667 144 219 108 142.2 218.333 108 144 +220 108 143.118 220.179 108 144 229.324 108 144 +33.1818 109 144 45.1818 109 144 203 108.706 144 +202.853 109 144 209 109 142.8 208.5 109 144 +210 108.923 144 209.857 109 144 211 108.179 144 +218 109 142.95 217.759 109 144 218 108.462 144 +219 109 142.2 219.667 109 144 220 108.455 144 +229.647 109 144 33 110 144 45.5294 110 144 +202.697 110 144 209 110 143.824 208 110 144 +209.053 110 144 218 110 143.824 217.941 110 144 +218.167 110 144 219 109.706 144 230 110 144 +32.8235 111 144 45.8529 111 144 202.429 111 144 +209 110.2 144 218 110.2 144 230 111 144 +32.6571 112 144 46 111.455 144 46.1818 112 144 +202.045 112 144 230.176 112 144 32.4857 113 144 +46.5294 113 144 202 112.059 144 201.429 113 144 +230.485 113 144 32.4857 114 144 47 113.941 144 +47.0357 114 144 201.045 114 144 208 114 143.842 +207.8 114 144 208 113.8 144 209 113.8 144 +209 114 143.842 210 113.417 144 210 114 142.95 +211 113.588 144 211 114 142.95 211.583 114 144 +230.824 114 144 32.4857 115 144 47.5294 115 144 +202 115 144 203.417 115 144 204 114.696 144 +205 114.588 144 205 115 142.25 206 114.417 144 +207 114.8 144 207 115 143.727 207.2 115 144 +208 114.2 144 209 114.091 144 210 114.412 144 +211 114.304 144 231 115 144 32.4857 116 144 +48 115.941 144 48.0357 116 144 201 115.6 144 +200.636 116 144 201.8 116 144 203 116 142 +202.2 116 144 203.8 116 144 204.2 116 144 +205 115.636 144 205.8 116 144 206.2 116 144 +207 115.2 144 231 116 144 32.4857 117 144 +48.5294 117 144 200.083 117 144 202 116.077 144 +204 116.043 144 206 116.063 144 231 117 144 +32.4857 118 144 49 117.727 144 49.2143 118 144 +200 117.2 144 199.818 118 144 231 118 144 +32.4857 119 144 49.8214 119 144 199.583 119 144 +231 119 144 32.4857 120 144 50 119.217 144 +50.6207 120 144 99 119.833 144 98.6667 120 144 +100 119.833 144 101 120 143.684 101 119.882 144 +101.167 120 144 199 119.636 144 198.818 120 144 +230.824 120 144 32.4857 121 144 51 120.478 144 +51.5217 121 144 97 121 143.684 97 120.833 144 +96.8333 121 144 98 121 143.684 98 120.667 144 +101.167 121 144 198 121 141.3 199 120.8 144 +199.083 121 144 230.657 121 144 32.4857 122 144 +44 122 141.938 43 122 144 44 121.353 144 +45 121.773 144 45.1786 122 144 52 121.478 144 +52.5217 122 144 97 122 143.684 96.6667 122 144 +100 122 143.684 101 122 143.684 101.333 122 144 +199.647 122 144 230.514 122 144 32.4857 123 144 +43.9565 123 144 45 123 142.263 45.5 123 144 +53 122.478 144 53.5217 123 144 96 122.667 144 +95.6667 123 144 98 123 143.684 100 123 143.684 +101 123 143 102 122.8 144 102.083 123 144 +200 122.5 144 200.353 123 144 230.343 123 144 +32.4857 124 144 44 123.056 144 45 124 143.824 +44.9444 124 144 45.2 124 144 54 123.478 144 +54.7059 124 144 95 124 143.684 95 123.667 144 +94.6667 124 144 96 124 143.684 97 124 143.684 +101 124 141.346 102 124 141.24 103 123.393 144 +103 124 141.783 104 123.966 144 104.059 124 144 +200.273 124 144 230.176 124 144 32.4857 125 144 +45 125 144 46 124.4 144 46.5455 125 144 +55 124.217 144 56 124.955 144 56.0588 125 144 +95 125 143.684 94.6667 125 144 96 125 143.684 +97 125 143 102 125 141.24 104 125 141.75 +105 124.727 144 105.222 125 144 200 124.353 144 +199.607 125 144 230 125 144 32.3429 126 144 +44.5 126 144 46 126 142.286 47 125.417 144 +47.6364 126 144 57 125.941 144 57.0588 126 144 +92 125.833 144 91.6667 126 144 93 125.667 144 +94 125.364 144 102 126 141.75 103 126 141.346 +105 126 141.75 105.643 126 144 197.455 126 144 +198 125.824 144 199 125.5 144 230 126 144 +32.3235 127 144 44.0556 127 144 48 126.8 144 +48.2 127 144 58 126.696 144 58.3889 127 144 +89 127 143.684 89 126.882 144 88.6667 127 144 +90 126.833 144 91 126.667 144 105.821 127 144 +195 126.824 144 194.647 127 144 196 126.485 144 +197 126.152 144 230 127 144 32.3429 128 144 +45 127.773 144 45.8333 128 144 46.1667 128 144 +47 127.706 144 48 127.059 144 59 127.478 144 +59.7059 128 144 88 128 143.684 88 127.667 144 +87.6667 128 144 89 128 143.684 90 128 143 +106 128 144 193.571 128 144 194 127.478 144 +230 128 144 32.4857 129 144 46 128.034 144 +60 129 141.75 60 128.217 144 61 128.955 144 +61.0909 129 144 85 129 143.684 85 128.882 144 +84.6667 129 144 86 128.833 144 87 128.667 144 +89 129 142.364 106.147 129 144 192 129 141.13 +193.227 129 144 230.147 129 144 32.4857 130 144 +62 129.588 144 62.6364 130 144 84 129.667 144 +83.6667 130 144 85 130 143 106.571 130 144 +192 130 142.286 193 130 143.053 193.545 130 144 +220.706 130 144 221 129.815 144 222 129.625 144 +222.353 130 144 230.303 130 144 32.4857 131 144 +63 130.25 144 64 130.588 144 65 130.882 144 +65.3333 131 144 81 130.882 144 80.6667 131 144 +82 130.833 144 83 130.364 144 106.727 131 144 +192 131 143.833 193 131 143.833 194 130.833 144 +194 131 143.824 195 130.917 144 195 131 143.833 +195.091 131 144 197.417 131 144 198 130.759 144 +199 130.708 144 199.412 131 144 220 131 141.25 +219 131 144 220 130.522 144 222 130.545 144 +221.545 131 144 230.303 131 144 32.4857 132 144 +66 131.25 144 67 131.588 144 67.6364 132 144 +75 131.882 144 74.6667 132 144 76 131.882 144 +77 131.882 144 78 131.682 144 79 131.588 144 +80 131.25 144 107 131.375 144 107.357 132 144 +129.167 132 144 130 131.821 144 131 132 142 +130.294 132 144 187 132 144 188 131.739 144 +188.545 132 144 193 132 143.842 194.2 132 144 +195 131.2 144 196.667 132 144 197 131.5 144 +200 131.476 144 200.393 132 144 218 132 142.263 +217.607 132 144 218 131.607 144 219 132 141 +220 132 141.176 221 131.5 144 220.727 132 144 +230.303 132 144 32.6571 133 144 68 132.182 144 +69 132.357 144 70 132.455 144 71 132.455 144 +72 133 141.75 72 132.455 144 73 132.357 144 +74 132.182 144 107.739 133 144 127 133 144 +128 132.5 144 129 132.034 144 130.778 133 144 +186 132.706 144 185.815 133 144 188.739 133 144 +192.2 133 144 193 132.2 144 194 132.091 144 +197 132.455 144 197.214 133 144 201 133 142 +200.739 133 144 202 133 142.364 203 133 141.24 +217 133 142.2 216.586 133 144 217 132.586 144 +218 133 141.571 219 133 143.118 219.294 133 144 +220 132.571 144 230.485 133 144 32.8235 134 144 +108 133.261 144 108.586 134 144 126 133.667 144 +125.353 134 144 130.824 134 144 185.593 134 144 +188 134 142 188.414 134 144 192.059 134 144 +197.176 134 144 201 133.375 144 201.588 134 144 +202 134 142.5 216 134 143.786 215.964 134 144 +216 133.944 144 217 134 141.652 218 134 143 +218.261 134 144 219 133.227 144 230.818 134 144 +33 135 144 108.957 135 144 123 135 144 +124 134.657 144 125 134.176 144 130.657 135 144 +180 135 142 181 135 142.364 186 134.733 144 +186.8 135 144 187.083 135 144 188 134.522 144 +192 135 144 196.667 135 144 197 134.353 144 +201.455 135 144 216 135 141.667 215.588 135 144 +217 135 142.25 217.304 135 144 218 134.273 144 +230.824 135 144 33 136 144 109 135.083 144 +110 135.353 144 111 135.227 144 112 135.478 144 +112.364 136 144 115.294 136 144 116 135.571 144 +117 135.667 144 118 135.971 144 119 135.824 144 +120 135.966 144 121 135.793 144 122 135.586 144 +129.875 136 144 130 135.852 144 180 136 143.727 +181 136 143.8 182 136 143.842 187 135.034 144 +191.455 136 144 195.815 136 144 196 135.815 144 +200.773 136 144 201 135.667 144 216 135.412 144 +217 135.25 144 230.657 136 144 33 137 144 +53 137 141.429 54.0833 137 144 55 136.621 144 +56 136.514 144 57 136.233 144 58 136.393 144 +59 136.739 144 59.375 137 144 113 136.656 144 +114 136.815 144 115 136.227 144 129.185 137 144 +181 137 143.8 182 137 142.765 190 136.733 144 +189.75 137 144 191 136.5 144 194.545 137 144 +195 136.815 144 200 137 143.167 200.227 137 144 +212 137 143.1 213 137 143.211 213.833 137 144 +214 136.917 144 214.091 137 144 215 137 142.421 +216 137 141.176 230.514 137 144 33 138 144 +54.0588 138 144 59 138 141.652 60 137.909 144 +60.0909 138 144 128.821 138 144 129 137.5 144 +180.636 138 144 181 137.2 144 181.364 138 144 +189 138 143.571 190 138 143.571 190 137.8 144 +191 138 141.75 191 137.455 144 192 137.353 144 +193 137.273 144 194 137.214 144 199 137.545 144 +198.545 138 144 200 137.185 144 205.5 138 144 +206 137.824 144 207 137.233 144 208 137.261 144 +209 137.412 144 209.833 138 144 210 138 143.684 +211 138 143.684 211.286 138 144 212 137.545 144 +212.5 138 144 213 138 143.167 214 137.083 144 +214 138 141.938 230.343 138 144 33 139 144 +55 138.727 144 55.5455 139 144 58.5455 139 144 +59 138.783 144 60 138.059 144 129 138.5 144 +129.179 139 144 180.688 139 144 181.833 139 144 +185 138.783 144 184.545 139 144 186 138.621 144 +187 138.5 144 188 138.214 144 189 138.091 144 +190 139 142.286 189.455 139 144 198.059 139 144 +203.739 139 144 204 138.824 144 205 138.176 144 +209.588 139 144 211 139 142.364 212 138.217 144 +212 139 141.75 230.176 139 144 33.3529 140 144 +56 139.152 144 57 139.176 144 58 139.176 144 +73 140 143.813 72.8571 140 144 73 139.941 144 +73.0833 140 144 104 140 144 105 139.227 144 +106 139.571 144 107 139.786 144 108 139.5 144 +109 139.214 144 110 139.353 144 110.478 140 144 +129.647 140 144 161 140 141.24 168 140 142.385 +177 140 144 181 139.313 144 181 140 141.938 +184 139.353 144 183.353 140 144 189.552 140 144 +198.706 140 144 202.393 140 144 203 139.5 144 +209.25 140 144 230 140 144 33.6765 141 144 +73 140.167 144 103.676 141 144 107 141 141.12 +110.739 141 144 129.818 141 144 165 141 143 +173 140.515 144 172.407 141 144 174 140.353 144 +175 140.353 144 176 140.176 144 178 140.313 144 +178.667 141 144 183.217 141 144 190 140.722 144 +190.833 141 144 199 140.294 144 200 141 144 +201 141 144 202 140.333 144 209 140.667 144 +208.882 141 144 230 141 144 34 142 144 +104 141.688 144 104.227 142 144 107 142 142.8 +108 142 144 109.545 142 144 110 141.773 144 +130 142 144 160.818 142 144 161 141.75 144 +162 141.059 144 163 141.304 144 164 141.412 144 +165 141.412 144 166 141.2 144 167 141.091 144 +168 141.091 144 168.909 142 144 171.75 142 144 +172 141.647 144 179 141.5 144 179.393 142 144 +183.964 142 144 191 141.036 144 192 141.773 144 +192.417 142 144 208.455 142 144 230 142 144 +34 143 144 105 142.607 144 106 142.353 144 +107 142.182 144 109 142.176 144 129.571 143 144 +160.304 143 144 169 142.091 144 169.625 143 144 +171.059 143 144 180 142.607 144 180.333 143 144 +184 142.077 144 184.522 143 144 193 142.412 144 +193.625 143 144 195 143 141.3 198 143 144 +208.179 143 144 230 143 144 34 144 144 +128.941 144 144 129 143.941 144 159.818 144 144 +160 143.636 144 170 143.353 144 171 143.091 144 +181 143.815 144 181.179 144 144 184.607 144 144 +194 143.222 144 195 143.643 144 196 143.515 144 +197 143.152 144 199 143.75 144 199.182 144 144 +204 143.848 144 203.545 144 144 205 143.852 144 +205.364 144 144 208 143.455 144 207.5 144 144 +229.824 144 144 34 145 144 127.727 145 144 +128 144.727 144 159 145 143 159.636 145 144 +166 145 144 167 144.727 144 168 144.571 144 +169 144.778 144 170 144.688 144 171 144.625 144 +172 144.588 144 172.304 145 144 182 145 144 +183.75 145 144 184 144.81 144 199.696 145 144 +202 145 144 203 144.261 144 206 144.292 144 +207 144.261 144 229.485 145 144 34 146 144 +64 146 143.824 63.5 146 144 64 145.833 144 +64.1429 146 144 127.5 146 144 160 145.8 144 +160 146 143.727 160.059 146 144 167 146 144 +173 146 144 183 145.5 144 200 145.636 144 +200.8 146 144 201.056 146 144 229.176 146 144 +34.3529 147 144 64 146.2 144 126.375 147 144 +127 146.63 144 158.091 147 144 159 146.091 144 +160 146.091 144 167.222 147 144 173.571 147 144 +201 146.059 144 211 147 141.857 229 147 144 +34.6765 148 144 63.8571 148 144 64 147.8 144 +64.1667 148 144 110 148 144 111 147.824 144 +112 147.848 144 112.455 148 144 124 148 144 +125 147.207 144 126 147.176 144 157.304 148 144 +158 147.059 144 168 147.778 144 168.214 148 144 +174 147.706 144 174.217 148 144 212 147.833 144 +211.909 148 144 213 147.333 144 214 147.657 144 +215 148 144 229 148 144 35 149 144 +64 149 141.682 63 149 144 65 148.455 144 +65.2609 149 144 108.227 149 144 109 148.5 144 +113 148.353 144 113.393 149 144 123.676 149 144 +156.5 149 144 157 148.389 144 169 149 144 +175 149 143 174.588 149 144 211.955 149 144 +216 149 144 220 149 144 228.824 149 144 +35.1765 150 144 59.1667 150 144 60 149.821 144 +61 149.667 144 62 149.5 144 65.7222 150 144 +108 150 141.652 107.357 150 144 108 149.217 144 +113 150 144 123.214 150 144 155.607 150 144 +156 149.5 144 163 150 144 164 149.414 144 +165 149.955 144 165.056 150 144 170 149.848 144 +170.185 150 144 175 150 143.625 174.667 150 144 +177 150 143.684 178 150 141.429 212 149.059 144 +212.941 150 144 215 150 144 228.485 150 144 +35.3429 151 144 58 151 141.6 59.0455 151 144 +66 150.833 144 66.0909 151 144 106.955 151 144 +107 150.909 144 112 150.667 144 111.353 151 144 +122.185 151 144 123 150.214 144 155 151 144 +162.214 151 144 165.214 151 144 171 151 144 +173.714 151 144 174 150.667 144 177 151 141.429 +213 150.036 144 214 150.364 144 228.176 151 144 +35.6571 152 144 60 152 144 62 152 141.682 +66 152 143.842 67 151.909 144 67 152 143.833 +67.2 152 144 94.2174 152 144 95 151.182 144 +96 151.217 144 97 151.696 144 98 151.941 144 +98 152 143.842 99 151.941 144 99 152 143.842 +99.2 152 144 107 151.091 144 108 151.706 144 +109 151.773 144 110 151.586 144 111 151.176 144 +121.571 152 144 122 151.294 144 154.657 152 144 +162.786 152 144 165 152 144 172 151.294 144 +173 151.294 144 187 152 143.769 186.909 152 144 +187 151.833 144 187.083 152 144 227.824 152 144 +228 151.5 144 36 153 144 61 152.343 144 +62 152.486 144 63 152.486 144 64 152.414 144 +65 152.304 144 66 152.059 144 67 152.2 144 +68 153 143.667 67.6667 153 144 68 152.667 144 +68.1667 153 144 73 153 143.769 72.9655 153 144 +73 152.857 144 74 152.417 144 74 153 142.6 +74.3043 153 144 93.1852 153 144 94 152.185 144 +99 153 143.842 99.2 153 144 121.179 153 144 +154.343 153 144 163 152.261 144 164 152.773 144 +168.625 153 144 169 152.778 144 170 152.571 144 +170 153 142.286 170.706 153 144 187 152.056 144 +227.485 153 144 36.3429 154 144 68 153.167 144 +73 154 143.833 72.9412 154 144 74 154 143.824 +74.0435 154 144 92 154 144 93 153.185 144 +99.4118 154 144 121.485 154 144 154.176 154 144 +164 154 143.824 163.944 154 144 164 153.833 144 +165 153.588 144 165 154 142.95 166 153.964 144 +167 153.786 144 168 153.357 144 171 153.417 144 +171.368 154 144 227.176 154 144 36.6571 155 144 +68.4545 155 144 69 154.625 144 70 154.778 144 +71 155 144 72 155 143.833 72 154.941 144 +72.2 155 144 73 154.2 144 74 154.059 144 +89.2 155 144 90 154.879 144 91 154.667 144 +99.25 155 144 121.029 155 144 154 154.5 144 +153.824 155 144 164 154.2 144 164.8 155 144 +166 155 141.429 171.586 155 144 203 155 141.571 +202 155 144 203 154.5 144 204 154.586 144 +204.923 155 144 226.647 155 144 227 154.333 144 +37 156 144 67.4783 156 144 68 155.294 144 +72.5652 156 144 87.5217 156 144 88 155.522 144 +89 155.043 144 99 156 144 120.824 156 144 +121 155.143 144 153.515 156 144 165 156 144 +171.486 156 144 185.955 156 144 186 155.938 144 +187 155.478 144 188 155.393 144 189 155 144 +190 155 144 191 155.393 144 191.773 156 144 +198.759 156 144 199 155.696 144 200 155.179 144 +201 155.176 144 205 155.091 144 206 156 143.625 +205.833 156 144 226.324 156 144 37.5152 157 144 +66.8214 157 144 67 156.688 144 72.1765 157 144 +86 157 144 87 156.522 144 98.3529 157 144 +120.514 157 144 153 157 144 165.485 157 144 +171.486 157 144 186 157 142.364 185.571 157 144 +192 156.185 144 193 156.545 144 193.833 157 144 +195 157 144 197.667 157 144 198 156.786 144 +205 156.909 144 204.917 157 144 225.824 157 144 +226 156.647 144 37.8235 158 144 67 157.294 144 +68 158 144 71.2143 158 144 72 157.214 144 +83.9412 158 144 84 157.957 144 85 157.727 144 +98 157.522 144 97.6071 158 144 120.176 158 144 +152.515 158 144 165.5 158 144 171.657 158 144 +174.5 158 144 175 157.333 144 176 157.414 144 +177 157.786 144 177.6 158 144 185.818 158 144 +194 157.091 144 196 157.353 144 197 157.414 144 +203.455 158 144 204 157.647 144 225.485 158 144 +38 158.5 144 38.1765 159 144 69 158.5 144 +70 158.657 144 71 158.207 144 83 159 144 +97.1818 159 144 101.917 159 144 102 158.955 144 +102.091 159 144 119.824 159 144 120 158.5 144 +152.176 159 144 165.853 159 144 171.5 159 144 +174.5 159 144 178 158.235 144 178.722 159 144 +186 158.273 144 186.696 159 144 200.091 159 144 +201 158.697 144 202 158.364 144 203 158.185 144 +225 159 144 38.5152 160 144 82 160 143 +82.6364 160 144 97 159.8 144 96.9412 160 144 +100.294 160 144 101 159.478 144 103 159.476 144 +103.478 160 144 119.514 160 144 152 159.353 144 +151.676 160 144 166 159.556 144 166.19 160 144 +171.393 160 144 174.966 160 144 179 159.238 144 +179.727 160 144 187 159.241 144 188 159.829 144 +188.5 160 144 195 159.588 144 194.364 160 144 +196 159.786 144 197 159.676 144 198 159.657 144 +199 159.353 144 200 159.034 144 217.647 160 144 +218 159.739 144 219 159 144 220 159.588 144 +220.304 160 144 224.485 160 144 39 161 144 +79 161 141.75 80 161 143 81 161 143.684 +83 160.667 144 83 161 143.684 84 160.667 144 +84.3333 161 144 90 161 144 91 160.824 144 +91.375 161 144 96.9545 161 144 99.5882 161 144 +100 160.417 144 101 161 141.12 104 161 144 +119.514 161 144 151.353 161 144 167 160.944 144 +167.059 161 144 171 160.917 144 170.941 161 144 +175 160.059 144 175.571 161 144 180 160.353 144 +180.917 161 144 181.2 161 144 182 160.81 144 +182.444 161 144 189 160.207 144 190 160.176 144 +191 160.607 144 192 161 144 195 161 142 +194.25 161 144 217 161 143.824 216.909 161 144 +217 160.917 144 218 161 142.714 219 161 141.25 +220 161 144 224 161 144 39.5152 162 144 +79 162 143 80 162 143 81 162 142.364 +85 161.25 144 86 161.941 144 86.0909 162 144 +89 161.586 144 88.2941 162 144 92 161.455 144 +92.3529 162 144 96.7778 162 144 100 161.304 144 +101 161.828 144 102 162 144 103 162 144 +119.514 162 144 151 161.522 144 150.676 162 144 +168 161.727 144 168.6 162 144 171 162 143.684 +170.667 162 144 176 162 144 181 161.045 144 +183 161.313 144 183.917 162 144 193 162 144 +194 162 143.75 194 161.8 144 216 162 143 +215.588 162 144 216 161.588 144 217 162 143.053 +218 162 144 219 161.478 144 223.485 162 144 +39.8235 163 144 78 163 141.6 79 163 142.765 +87 162.588 144 88 162.179 144 92.0345 163 144 +96.9643 163 144 119.514 163 144 150.353 163 144 +168.375 163 144 171 162.286 144 171.217 163 144 +176.485 163 144 184 162.059 144 184.727 163 144 +216 162.412 144 217 162.273 144 223 163 144 +40 163.5 144 40.1765 164 144 77.1667 164 144 +78 163.706 144 78.4545 164 144 91.1852 164 144 +92 163.043 144 97 163.045 144 97.6364 164 144 +119.514 164 144 150 163.667 144 149.824 164 144 +168.043 164 144 171.414 164 144 176.966 164 144 +185 163.5 144 185.375 164 144 222.393 164 144 +40.5152 165 144 64.6471 165 144 65 164.818 144 +66 164.75 144 67 164.909 144 67 165 143.833 +67.0588 165 144 76 165 144 77 164.043 144 +78.3929 165 144 90 165 144 91 164.185 144 +98 164.522 144 98.4783 165 144 119.514 165 144 +149.793 165 144 160 165 143.824 159.917 165 144 +160 164.917 144 161 164.941 144 161 165 143.833 +161.091 165 144 167.515 165 144 168 164.059 144 +171.4 165 144 177 164.059 144 177.727 165 144 +181 164.966 144 180.917 165 144 182 164.966 144 +182.083 165 144 185.478 165 144 199 165 143.842 +198.8 165 144 199 164.8 144 199.091 165 144 +210.966 165 144 211 164.941 144 212 164.909 144 +212 165 143.4 212.077 165 144 221.5 165 144 +222 164.5 144 41 166 144 63.4138 166 144 +64 165.393 144 65 166 141 66.5455 166 144 +67 165.091 144 75.5 166 144 78.1515 166 144 +89.1923 166 144 99 166 141.857 98.7059 166 144 +119.514 166 144 149.739 166 144 160 165.059 144 +161 165.091 144 166.522 166 144 167 165.607 144 +171 165.706 144 170.848 166 144 178 165.214 144 +179 165.176 144 180 165.324 144 183 165.333 144 +183.957 166 144 184.167 166 144 185 165.688 144 +199 166 143.833 198.941 166 144 200 165.588 144 +200 166 142.95 201 166 144 211 166 141.5 +210.773 166 144 212 166 144 221.179 166 144 +41.5152 167 144 63.0556 167 144 65 167 142.263 +65.6875 167 144 66 166.706 144 75.0345 167 144 +78 166.227 144 77.5 167 144 88 167 144 +89 166.227 144 96 166.667 144 95.5 167 144 +97 166.586 144 98 167 143.571 98 166.923 144 +99 167 142.615 119.514 167 144 149.739 167 144 +165.593 167 144 166 166.522 144 170.152 167 144 +184 166.056 144 199 166.167 144 199.217 167 144 +202 166.343 144 203 166.676 144 203.647 167 144 +211 166.455 144 220.824 167 144 221 166.455 144 +42 168 144 63 168 142 64 167.739 144 +65 167.324 144 74.5 168 144 75 167.056 144 +77 167.944 144 76.9655 168 144 87.2143 168 144 +91 168 143.571 91 167.957 144 90.9412 168 144 +92 167.818 144 93 167.966 144 93.1667 168 144 +94.2941 168 144 95 167.478 144 119.353 168 144 +149.786 168 144 165.034 168 144 170 167.455 144 +169.824 168 144 199.5 168 144 204 167.261 144 +205 167.621 144 206 167.739 144 207 167.455 144 +207 168 142.8 207.353 168 144 220.485 168 144 +42.6667 169 144 73.8485 169 144 74 168.773 144 +76.6471 169 144 86.7391 169 144 87 168.5 144 +90 168.727 144 89.7273 169 144 94 168.185 144 +119.029 169 144 149.971 169 144 164.545 169 144 +165 168.091 144 170 169 144 200 168.944 144 +200.029 169 144 204 169 141.125 206.944 169 144 +207 168.857 144 220 169 144 43 169.647 144 +43.1765 170 144 74 170 142.263 73.6071 170 144 +76.2333 170 144 85.8519 170 144 86 169.81 144 +89 169.941 144 88.9412 170 144 118.5 170 144 +119 169.056 144 150 169.083 144 150.324 170 144 +165 170 141.5 164.583 170 144 170.773 170 144 +200.414 170 144 205 170 141.682 206.333 170 144 +219.333 170 144 43.8235 171 144 74 171 143.118 +73.7059 171 144 75 171 143.167 75.2381 171 144 +76 170.304 144 85 171 142.105 85.75 171 144 +87 171 144 88 170.727 144 118.176 171 144 +150.647 171 144 165 170.417 144 166 170.478 144 +167 171 144 170 171 141.167 171 170.455 144 +171 171 143 171.273 171 144 201 171 144 +203 170.688 144 202.688 171 144 204 170.304 144 +205 170.515 144 206 170.353 144 218.647 171 144 +219 170.478 144 44 171.261 144 44.5 172 144 +74 171.294 144 75 171.185 144 84 172 141.875 +85 172 141.75 86 171.25 144 86 172 142.364 +117.853 172 144 118 171.545 144 150.824 172 144 +168 171.607 144 168.478 172 144 171 172 144 +202 171.647 144 218 172 144 45 172.586 144 +45.3529 173 144 117.5 173 144 151 172.353 144 +151.324 173 144 169 173 143.833 168.917 173 144 +169.167 173 144 170 172.545 144 217.333 173 144 +46 174 144 117.029 174 144 151.647 174 144 +168.294 174 144 170 173.833 144 170.056 174 144 +173 174 141.9 216.5 174 144 217 173.393 144 +46.6667 175 144 116.5 175 144 117 174.056 144 +152 174.522 144 152.324 175 144 168.261 175 144 +170.739 175 144 174 175 142.5 203.5 175 144 +204 174.955 144 205 174.815 144 205.179 175 144 +215.5 175 144 216 174.5 144 47 175.393 144 +47.5 176 144 115.848 176 144 116 175.773 144 +152.647 176 144 168.529 176 144 171 175.5 144 +171.261 176 144 202.81 176 144 203 175.2 144 +205.957 176 144 214.393 176 144 215 175.5 144 +48 176.5 144 48.5 177 144 115.176 177 144 +153 176.522 144 153.324 177 144 169 176.593 144 +169.393 177 144 171.333 177 144 202.643 177 144 +206 176.045 144 206.778 177 144 213.393 177 144 +214 176.393 144 49 177.5 144 49.5 178 144 +114.5 178 144 115 177.261 144 154 178 144 +170 177.944 144 171 178 144 203 177.909 144 +203.043 178 144 207 177.261 144 207.607 178 144 +212.667 178 144 213 177.5 144 50 178.5 144 +50.5 179 144 113.848 179 144 114 178.773 144 +155 179 144 183 179 143.824 182.941 179 144 +183 178.955 144 183.167 179 144 203.414 179 144 +208 178.647 144 208.176 179 144 211.697 179 144 +212 178.688 144 51 179.5 144 51.5 180 144 +113 180 144 156 180 144 182.964 180 144 +184 179.278 144 184 180 142.143 185 179.824 144 +185.214 180 144 203.515 180 144 209 179.824 144 +210 180 144 211 179.676 144 52 180.5 144 +52.6071 181 144 112 181 144 157 181 144 +183 181 142.5 182.783 181 144 184 181 141.571 +185 181 141.125 186 180.957 144 186.056 181 144 +202.706 181 144 203 180.773 144 53 181.333 144 +53.8148 182 144 111 182 144 158 181.848 144 +158.227 182 144 165 182 143.824 164.957 182 144 +165 181 144 166 181.333 144 166 182 142.2 +166.706 182 144 172 182 144 173 181.545 144 +173.455 182 144 183 181.455 144 184 182 141.938 +183.353 182 144 187 181.63 144 187.37 182 144 +198.353 182 144 199 181.476 144 200 181.179 144 +201 181.343 144 202 181.343 144 54 182.152 144 +55 182.848 144 55.2273 183 144 110 183 144 +159 182.5 144 159.773 183 144 165 182.043 144 +166 183 142.8 165.786 183 144 167 182.833 144 +167 183 143.667 167.077 183 144 171.739 183 144 +174 182.429 144 175 182.643 144 176 182.286 144 +177 182.214 144 178 182.393 144 179 183 144 +183.462 183 144 188 182.773 144 188.417 183 144 +198.515 183 144 56 183.5 144 57 184 144 +70.5882 184 144 71 183.72 144 72 183.966 144 +72.0588 184 144 87.9583 184 144 88 183.957 144 +89 183.5 144 89.7727 184 144 109 184 144 +160 183.152 144 161 184 144 166 183.5 144 +167 184 141.429 166.5 184 144 168 183.545 144 +168.625 184 144 171.167 184 144 180 183.478 144 +181 184 144 182.545 184 144 183 183.545 144 +189 183.318 144 190 183.706 144 190.217 184 144 +198.182 184 144 58 184.676 144 58.6471 185 144 +70.4138 185 144 73 184.593 144 73.5238 185 144 +87.4138 185 144 90 184.227 144 90.6071 185 144 +108 185 144 162 185 144 166.6 185 144 +169 184.182 144 170 184.176 144 171 184.042 144 +182 184.171 144 190.786 185 144 198.179 185 144 +59 185.176 144 60 185.824 144 60.5 186 144 +71 185.5 144 71.7727 186 144 74 185.455 144 +75 185.957 144 75.0909 186 144 83.3529 186 144 +84 185.676 144 85 185.667 144 86 186 144 +87 185.706 144 90.4286 186 144 107 186 144 +163 186 144 166.414 186 144 178 185.964 144 +177.857 186 144 178.2 186 144 191 185.214 144 +191.786 186 144 196.688 186 144 197 185.815 144 +198 185.192 144 61 186.176 144 62 186.824 144 +62.2609 187 144 72 186.179 144 73 186.667 144 +73.2727 187 144 76 186.303 144 77 186.75 144 +78 186.964 144 79 186.966 144 80 186.647 144 +81 186.514 144 82 186.343 144 83 186.176 144 +90 186.5 144 89.6471 187 144 105.706 187 144 +106 186.848 144 164 186.821 144 164.227 187 144 +166.077 187 144 176 186.767 144 175.611 187 144 +177 186.207 144 178.706 187 144 192 186.214 144 +192.815 187 144 195.522 187 144 196 186.5 144 +63 187.5 144 63.7391 188 144 74 188 144 +89.1471 188 144 103.478 188 144 104 187.586 144 +105 187.343 144 165 187.515 144 166 187.045 144 +169 187.217 144 168.217 188 144 170 187.217 144 +171 187.909 144 171.063 188 144 174.179 188 144 +175 187.324 144 178.5 188 144 193 187.152 144 +194 187.676 144 195 187.522 144 64 188.176 144 +65 188.657 144 66 189 144 74.625 189 144 +85 188.727 144 84.7931 189 144 86 188.407 144 +86.7273 189 144 89 188.5 144 88.7059 189 144 +102.5 189 144 103 188.393 144 168.455 189 144 +170 189 141.429 171.083 189 144 173 189 144 +174 188.227 144 178.485 189 144 67 189.5 144 +68 190 144 75 189.353 144 75 190 141.938 +76 190 143.571 76 189.941 144 76.0357 190 144 +84.9706 190 144 87 189.214 144 88 189.414 144 +101.393 190 144 102 189.5 144 169 189.353 144 +170 189.522 144 171 189.091 144 173 190 144 +179 190 141.167 178.5 190 144 182 190 143 +69 190.343 144 70 190.657 144 71 191 144 +72 191 144 73 190.778 144 74 190.714 144 +75 190.688 144 76 190.083 144 85 190.056 144 +85.6071 191 144 100 191 144 101 190.333 144 +174 191 144 178 191 144 86 191.478 144 +86.3529 192 144 98 192 144 99 191.5 144 +175 191.514 144 176 191.514 144 177 191.514 144 +87 192.815 144 87.1852 193 144 95 193 144 +96 192.657 144 97 192.343 144 88 193.786 144 +89 193.824 144 90 193.647 144 91 193.514 144 +92 193.514 144 93 193.514 144 94 193.343 144 + +POLYGONS 13733 54932 +3 0 13 17 +3 14 0 17 +3 3 2 4 +3 5 3 4 +3 7 3 6 +3 6 3 5 +3 2 13 4 +3 5 4 13 +3 6 5 13 +3 9 10 11 +3 0 7 13 +3 7 6 13 +3 14 7 0 +3 14 8 7 +3 19 8 14 +3 10 9 20 +3 20 9 126 +3 11 10 15 +3 15 10 20 +3 12 11 16 +3 16 11 15 +3 16 121 12 +3 17 18 14 +3 19 14 22 +3 21 8 19 +3 28 17 13 +3 17 28 18 +3 14 18 22 +3 22 18 28 +3 132 20 126 +3 15 20 132 +3 21 19 29 +3 29 19 22 +3 23 21 29 +3 21 23 24 +3 24 127 21 +3 125 25 26 +3 27 125 26 +3 24 23 29 +3 127 24 30 +3 34 31 25 +3 26 25 32 +3 32 25 31 +3 27 26 32 +3 30 24 38 +3 127 30 33 +3 31 34 36 +3 36 34 35 +3 37 31 36 +3 37 32 31 +3 27 32 37 +3 38 33 30 +3 39 33 38 +3 35 34 39 +3 35 137 36 +3 37 36 137 +3 40 140 42 +3 42 41 40 +3 40 41 43 +3 43 41 42 +3 43 42 47 +3 47 42 46 +3 48 44 45 +3 49 48 45 +3 50 44 48 +3 51 46 58 +3 47 46 51 +3 51 2917 47 +3 50 48 52 +3 52 155 50 +3 48 53 52 +3 54 155 52 +3 53 54 52 +3 46 160 58 +3 160 57 58 +3 51 58 59 +3 65 51 59 +3 60 55 56 +3 61 160 71 +3 160 61 57 +3 57 61 62 +3 58 57 66 +3 66 57 62 +3 63 58 66 +3 64 58 63 +3 59 58 65 +3 65 58 64 +3 56 69 60 +3 70 60 69 +3 62 61 72 +3 66 62 72 +3 63 66 67 +3 64 63 68 +3 68 63 67 +3 65 64 75 +3 75 64 68 +3 75 2917 65 +3 76 60 70 +3 72 61 71 +3 66 72 73 +3 67 66 74 +3 74 66 73 +3 68 67 75 +3 75 67 74 +3 69 165 70 +3 76 70 165 +3 72 71 81 +3 73 72 77 +3 77 72 81 +3 77 74 73 +3 75 74 77 +3 164 78 79 +3 164 76 165 +3 164 79 86 +3 80 71 168 +3 81 71 80 +3 82 80 168 +3 89 82 168 +3 83 80 82 +3 84 80 83 +3 81 80 85 +3 85 80 84 +3 78 86 79 +3 88 86 87 +3 92 89 168 +3 84 83 171 +3 171 83 82 +3 171 85 84 +3 88 87 90 +3 90 87 86 +3 174 91 92 +3 89 92 96 +3 93 94 174 +3 101 93 174 +3 94 95 174 +3 174 95 91 +3 92 91 96 +3 96 91 95 +3 89 96 177 +3 101 97 93 +3 93 97 98 +3 94 93 99 +3 99 93 98 +3 180 95 94 +3 96 95 180 +3 103 173 175 +3 3 173 103 +3 3 7 173 +3 173 7 264 +3 101 100 1 +3 97 101 1 +3 102 97 1 +3 102 98 97 +3 99 98 111 +3 111 98 102 +3 111 94 99 +3 122 103 175 +3 3 103 2 +3 104 100 101 +3 107 104 101 +3 1 100 105 +3 105 100 104 +3 122 2 103 +3 112 264 7 +3 179 185 115 +3 11 179 115 +3 11 106 179 +3 179 106 107 +3 104 107 108 +3 105 104 109 +3 109 104 108 +3 105 110 1 +3 105 109 110 +3 1 110 102 +3 111 102 110 +3 122 13 2 +3 8 112 7 +3 115 185 114 +3 11 115 9 +3 106 11 12 +3 121 106 12 +3 121 107 106 +3 108 107 116 +3 116 107 121 +3 109 108 111 +3 111 108 116 +3 110 109 111 +3 112 8 21 +3 117 112 127 +3 113 123 118 +3 184 113 118 +3 125 184 118 +3 119 184 125 +3 114 119 120 +3 115 114 120 +3 9 115 126 +3 111 116 136 +3 136 116 121 +3 127 112 21 +3 117 127 128 +3 128 193 117 +3 194 123 113 +3 131 118 123 +3 125 118 124 +3 124 118 131 +3 126 119 125 +3 120 119 126 +3 115 120 126 +3 15 121 16 +3 28 13 122 +3 129 193 128 +3 129 130 194 +3 194 130 123 +3 123 130 134 +3 131 123 134 +3 25 124 131 +3 125 124 25 +3 126 125 27 +3 132 136 15 +3 121 15 136 +3 28 122 133 +3 128 127 33 +3 129 128 130 +3 34 131 134 +3 25 131 34 +3 135 126 27 +3 132 126 135 +3 136 132 135 +3 29 133 218 +3 29 22 133 +3 22 28 133 +3 130 128 33 +3 134 130 33 +3 27 219 135 +3 136 135 219 +3 29 38 24 +3 134 33 39 +3 34 134 39 +3 38 29 218 +3 27 37 219 +3 219 37 137 +3 218 39 38 +3 137 35 39 +3 140 234 138 +3 139 236 237 +3 140 138 42 +3 141 139 237 +3 142 139 141 +3 40 43 140 +3 43 234 140 +3 143 144 141 +3 141 144 142 +3 144 147 142 +3 148 42 138 +3 2917 234 43 +3 145 144 143 +3 147 144 145 +3 146 145 143 +3 147 145 146 +3 42 148 46 +3 47 2917 43 +3 154 149 146 +3 147 146 150 +3 150 146 149 +3 151 147 150 +3 154 152 149 +3 149 152 150 +3 44 152 45 +3 44 151 152 +3 151 150 152 +3 44 155 151 +3 46 148 760 +3 153 152 154 +3 45 153 49 +3 152 153 45 +3 44 50 155 +3 154 49 153 +3 154 157 49 +3 157 48 49 +3 48 157 53 +3 155 54 156 +3 244 155 55 +3 55 155 156 +3 46 760 160 +3 51 65 2917 +3 53 156 54 +3 53 157 156 +3 157 158 156 +3 55 158 56 +3 55 156 158 +3 55 60 245 +3 55 245 244 +3 245 60 159 +3 157 163 161 +3 157 161 158 +3 158 161 56 +3 60 162 159 +3 160 760 71 +3 56 161 163 +3 69 56 163 +3 76 162 60 +3 69 163 165 +3 76 164 162 +3 256 168 760 +3 71 760 168 +3 78 164 165 +3 86 252 164 +3 86 88 252 +3 81 75 77 +3 165 359 78 +3 252 88 166 +3 252 166 257 +3 256 167 168 +3 78 359 86 +3 257 166 169 +3 167 261 92 +3 168 167 92 +3 85 171 81 +3 171 75 81 +3 166 90 170 +3 88 90 166 +3 169 166 173 +3 173 166 170 +3 264 169 173 +3 261 259 174 +3 92 261 174 +3 86 359 172 +3 86 172 90 +3 90 172 170 +3 172 173 170 +3 89 177 82 +3 171 82 177 +3 2917 171 177 +3 173 172 175 +3 175 172 359 +3 178 264 112 +3 179 107 176 +3 176 101 174 +3 101 176 107 +3 94 111 180 +3 359 122 175 +3 183 178 182 +3 184 276 183 +3 111 181 180 +3 187 180 181 +3 180 187 96 +3 182 178 112 +3 179 184 185 +3 186 181 111 +3 187 181 186 +3 177 96 187 +3 182 112 117 +3 183 182 188 +3 189 183 188 +3 113 183 189 +3 184 183 113 +3 185 184 190 +3 185 190 114 +3 117 193 182 +3 188 182 191 +3 191 182 193 +3 189 188 194 +3 194 188 191 +3 113 189 194 +3 119 190 184 +3 114 190 119 +3 136 192 111 +3 186 111 192 +3 187 186 192 +3 194 191 193 +3 194 193 129 +3 133 122 218 +3 136 219 220 +3 196 195 197 +3 197 195 295 +3 198 196 197 +3 198 199 196 +3 201 202 200 +3 202 203 205 +3 200 202 205 +3 203 204 205 +3 205 204 206 +3 206 207 291 +3 295 208 197 +3 198 197 208 +3 199 198 209 +3 209 198 208 +3 210 199 209 +3 200 199 211 +3 211 199 210 +3 201 200 211 +3 212 201 211 +3 202 201 213 +3 213 201 212 +3 203 202 213 +3 204 203 214 +3 214 203 213 +3 215 204 214 +3 206 204 215 +3 215 216 206 +3 207 206 216 +3 211 210 217 +3 217 210 209 +3 212 211 217 +3 217 213 212 +3 214 213 217 +3 215 214 217 +3 220 137 39 +3 219 137 220 +3 221 222 223 +3 221 227 224 +3 222 221 225 +3 225 221 224 +3 225 315 222 +3 224 227 228 +3 315 225 228 +3 228 225 224 +3 226 540 230 +3 227 226 231 +3 231 226 230 +3 228 227 232 +3 232 227 231 +3 232 315 228 +3 230 540 138 +3 234 230 138 +3 234 231 230 +3 232 231 315 +3 315 231 234 +3 235 233 229 +3 238 235 229 +3 236 233 235 +3 761 233 236 +3 237 235 238 +3 236 235 237 +3 237 238 141 +3 139 142 236 +3 142 761 236 +3 143 141 238 +3 239 142 147 +3 239 419 142 +3 238 320 143 +3 320 146 143 +3 146 320 154 +3 147 151 239 +3 148 138 760 +3 155 239 151 +3 241 239 155 +3 320 762 154 +3 762 157 154 +3 155 244 241 +3 325 240 243 +3 243 240 242 +3 247 242 240 +3 243 242 248 +3 248 242 247 +3 248 325 243 +3 159 246 245 +3 159 249 246 +3 247 250 248 +3 762 163 157 +3 159 162 249 +3 251 162 164 +3 251 164 253 +3 252 253 164 +3 253 252 336 +3 165 163 262 +3 333 252 254 +3 254 252 257 +3 255 333 254 +3 165 262 359 +3 255 254 258 +3 258 254 257 +3 259 451 260 +3 256 451 261 +3 261 451 259 +3 167 256 261 +3 258 257 263 +3 263 257 169 +3 264 258 263 +3 255 258 264 +3 267 259 260 +3 75 171 2917 +3 263 169 264 +3 264 346 255 +3 351 346 265 +3 265 346 264 +3 259 267 174 +3 269 265 264 +3 178 363 270 +3 363 178 275 +3 271 266 354 +3 271 354 272 +3 272 273 274 +3 271 272 274 +3 268 274 273 +3 267 268 273 +3 267 273 174 +3 264 178 269 +3 269 178 360 +3 275 178 276 +3 184 365 276 +3 179 805 184 +3 354 179 176 +3 272 354 176 +3 174 272 176 +3 273 272 174 +3 178 277 360 +3 270 277 178 +3 276 178 183 +3 122 359 278 +3 187 192 177 +3 279 280 385 +3 385 280 281 +3 391 282 279 +3 279 282 280 +3 281 280 282 +3 283 281 282 +3 281 283 284 +3 122 382 218 +3 1226 192 136 +3 285 286 395 +3 401 287 286 +3 288 286 287 +3 395 286 289 +3 289 286 288 +3 289 290 395 +3 393 395 290 +3 196 290 195 +3 393 290 196 +3 196 199 393 +3 199 200 393 +3 393 200 205 +3 205 291 292 +3 287 401 293 +3 287 293 288 +3 289 288 294 +3 294 288 293 +3 290 289 295 +3 295 289 294 +3 290 295 195 +3 291 205 206 +3 207 296 291 +3 292 291 296 +3 293 297 298 +3 294 293 298 +3 295 294 298 +3 295 298 208 +3 216 215 299 +3 216 299 207 +3 207 299 296 +3 299 409 296 +3 215 409 299 +3 209 409 217 +3 208 409 209 +3 409 215 217 +3 300 301 410 +3 301 304 410 +3 300 302 303 +3 301 300 304 +3 304 300 303 +3 303 302 306 +3 306 302 305 +3 304 303 307 +3 307 303 306 +3 307 308 304 +3 223 302 221 +3 305 302 223 +3 222 305 223 +3 222 309 305 +3 309 306 305 +3 307 306 308 +3 308 306 309 +3 221 302 227 +3 222 315 309 +3 315 308 309 +3 233 415 229 +3 415 414 229 +3 302 226 227 +3 302 540 226 +3 312 310 311 +3 423 312 311 +3 312 314 310 +3 313 312 423 +3 314 312 313 +3 316 313 423 +3 314 313 317 +3 317 313 316 +3 317 914 314 +3 317 316 318 +3 914 317 318 +3 142 419 761 +3 540 760 138 +3 319 316 423 +3 318 316 319 +3 914 318 319 +3 2917 315 234 +3 229 320 238 +3 241 322 321 +3 239 241 321 +3 319 240 325 +3 324 322 241 +3 323 322 324 +3 319 423 240 +3 241 244 324 +3 245 425 244 +3 246 425 245 +3 240 423 247 +3 325 248 250 +3 330 325 250 +3 427 426 249 +3 249 426 246 +3 247 423 250 +3 249 430 327 +3 249 162 430 +3 427 249 327 +3 250 423 329 +3 330 250 329 +3 442 163 762 +3 251 331 162 +3 331 430 162 +3 337 332 328 +3 333 328 332 +3 330 329 334 +3 326 330 434 +3 434 330 334 +3 335 331 251 +3 336 332 337 +3 252 333 336 +3 333 332 336 +3 423 338 329 +3 329 338 334 +3 339 434 338 +3 338 434 334 +3 335 342 443 +3 335 251 342 +3 251 253 342 +3 337 253 336 +3 760 451 256 +3 341 339 338 +3 340 341 338 +3 440 344 441 +3 163 442 262 +3 343 443 342 +3 255 346 333 +3 573 441 344 +3 262 442 349 +3 342 345 343 +3 451 347 260 +3 344 440 348 +3 348 440 356 +3 573 344 357 +3 357 344 348 +3 262 349 454 +3 454 350 262 +3 352 346 351 +3 347 805 179 +3 354 347 179 +3 355 347 354 +3 260 347 355 +3 348 356 357 +3 358 573 357 +3 262 350 359 +3 360 351 265 +3 352 351 360 +3 361 363 362 +3 363 364 362 +3 362 364 365 +3 365 184 805 +3 266 366 354 +3 366 355 354 +3 367 355 366 +3 267 367 268 +3 267 260 367 +3 260 355 367 +3 358 357 368 +3 368 369 358 +3 265 269 360 +3 363 361 270 +3 270 361 370 +3 364 363 371 +3 371 363 275 +3 276 364 371 +3 365 364 276 +3 271 643 266 +3 366 266 832 +3 832 266 643 +3 367 366 474 +3 366 832 474 +3 372 271 274 +3 474 268 367 +3 274 268 372 +3 372 268 474 +3 356 373 357 +3 368 357 374 +3 374 357 373 +3 375 369 368 +3 375 368 374 +3 377 270 370 +3 276 371 275 +3 372 643 271 +3 375 374 378 +3 378 374 373 +3 278 359 376 +3 380 360 277 +3 277 270 377 +3 277 377 380 +3 379 122 278 +3 494 122 379 +3 122 494 382 +3 177 192 1226 +3 518 386 383 +3 507 383 386 +3 385 384 516 +3 387 384 385 +3 507 386 388 +3 388 389 507 +3 389 391 516 +3 385 391 279 +3 385 516 391 +3 281 387 385 +3 386 518 388 +3 389 388 390 +3 389 390 391 +3 387 281 284 +3 284 392 387 +3 285 387 392 +3 393 387 285 +3 285 395 393 +3 391 283 282 +3 391 390 283 +3 283 392 284 +3 283 394 392 +3 285 392 286 +3 392 394 286 +3 283 390 396 +3 396 397 283 +3 398 399 394 +3 398 394 397 +3 397 394 283 +3 399 400 401 +3 399 401 394 +3 394 401 286 +3 292 393 205 +3 382 707 218 +3 402 396 390 +3 403 396 402 +3 403 404 405 +3 403 405 396 +3 396 405 397 +3 400 398 397 +3 400 397 406 +3 406 397 405 +3 400 406 401 +3 292 296 407 +3 405 408 406 +3 401 406 408 +3 297 293 408 +3 293 401 408 +3 296 409 407 +3 297 408 298 +3 532 408 405 +3 298 408 532 +3 39 218 1226 +3 39 1226 220 +3 304 411 410 +3 413 412 414 +3 761 413 414 +3 761 414 415 +3 418 416 539 +3 423 418 539 +3 417 416 418 +3 417 914 416 +3 414 412 229 +3 310 418 311 +3 310 417 418 +3 310 914 417 +3 415 233 761 +3 418 423 311 +3 310 314 914 +3 319 573 914 +3 420 761 419 +3 229 762 320 +3 419 239 421 +3 421 420 419 +3 319 325 573 +3 239 542 421 +3 239 321 542 +3 542 321 322 +3 542 322 422 +3 322 323 422 +3 326 325 330 +3 324 422 323 +3 246 424 425 +3 324 244 422 +3 426 424 246 +3 426 554 424 +3 429 554 426 +3 429 426 428 +3 428 426 427 +3 327 430 557 +3 427 327 431 +3 431 327 557 +3 428 427 431 +3 429 428 431 +3 328 432 564 +3 573 326 440 +3 430 331 433 +3 328 564 337 +3 328 333 432 +3 438 432 333 +3 440 326 434 +3 331 335 433 +3 433 335 449 +3 570 435 337 +3 570 337 564 +3 435 436 437 +3 570 436 435 +3 339 439 434 +3 439 447 434 +3 440 434 447 +3 443 449 335 +3 337 444 253 +3 337 435 444 +3 444 435 445 +3 435 437 445 +3 445 437 446 +3 333 346 438 +3 346 576 438 +3 423 470 338 +3 439 341 447 +3 339 341 439 +3 441 573 440 +3 448 449 443 +3 443 343 448 +3 444 342 253 +3 445 450 444 +3 446 450 445 +3 346 579 576 +3 338 452 340 +3 340 447 341 +3 340 452 447 +3 452 456 447 +3 356 447 456 +3 447 356 440 +3 442 453 349 +3 454 449 448 +3 458 449 454 +3 343 345 455 +3 343 455 448 +3 448 455 454 +3 345 459 455 +3 342 459 345 +3 444 459 342 +3 579 346 352 +3 352 584 579 +3 584 352 586 +3 451 805 347 +3 452 338 470 +3 349 453 457 +3 454 349 458 +3 457 458 349 +3 459 454 455 +3 459 350 454 +3 459 460 350 +3 589 462 461 +3 586 352 353 +3 365 805 598 +3 456 452 470 +3 573 358 464 +3 464 471 573 +3 465 458 457 +3 465 457 466 +3 466 457 453 +3 466 460 465 +3 466 359 460 +3 359 350 460 +3 461 467 589 +3 462 467 461 +3 352 360 595 +3 360 468 595 +3 353 352 607 +3 352 595 607 +3 608 361 469 +3 463 362 598 +3 463 469 362 +3 469 361 362 +3 365 598 362 +3 456 470 356 +3 464 369 375 +3 358 369 464 +3 471 464 375 +3 453 477 466 +3 359 466 472 +3 466 477 472 +3 468 360 639 +3 608 626 370 +3 608 370 361 +3 470 893 356 +3 375 475 471 +3 476 471 475 +3 478 617 472 +3 478 472 477 +3 376 617 479 +3 376 472 617 +3 376 359 472 +3 370 653 377 +3 480 628 473 +3 480 473 629 +3 372 474 485 +3 373 356 893 +3 475 378 481 +3 375 378 475 +3 476 475 482 +3 482 475 481 +3 483 471 476 +3 483 476 482 +3 376 648 278 +3 479 648 376 +3 380 639 360 +3 485 643 372 +3 481 378 678 +3 482 481 679 +3 679 481 678 +3 483 482 486 +3 486 482 679 +3 487 483 486 +3 278 648 684 +3 379 488 484 +3 379 278 488 +3 650 685 484 +3 685 379 484 +3 380 377 987 +3 378 489 678 +3 679 490 486 +3 381 487 490 +3 487 486 490 +3 381 491 492 +3 381 492 487 +3 491 493 664 +3 492 491 664 +3 859 278 684 +3 488 278 859 +3 495 494 685 +3 494 379 685 +3 378 373 496 +3 373 893 496 +3 378 496 489 +3 496 497 489 +3 498 678 489 +3 498 489 497 +3 679 499 490 +3 381 490 500 +3 490 499 500 +3 381 500 491 +3 500 501 491 +3 493 491 501 +3 507 493 501 +3 502 493 507 +3 387 493 502 +3 503 700 382 +3 382 495 503 +3 494 495 382 +3 504 701 687 +3 893 505 496 +3 496 505 497 +3 498 497 505 +3 499 498 506 +3 506 498 505 +3 500 499 506 +3 501 500 506 +3 383 507 501 +3 502 507 508 +3 384 502 508 +3 502 384 387 +3 382 700 707 +3 510 512 513 +3 512 514 513 +3 515 505 893 +3 505 515 506 +3 501 506 515 +3 383 501 518 +3 516 508 507 +3 508 516 384 +3 513 511 510 +3 501 515 518 +3 516 389 519 +3 507 389 516 +3 516 519 389 +3 515 521 518 +3 388 518 521 +3 388 521 390 +3 524 393 292 +3 292 407 524 +3 521 1217 390 +3 522 403 525 +3 522 526 403 +3 399 398 526 +3 399 526 523 +3 523 526 522 +3 523 400 399 +3 523 527 400 +3 390 528 402 +3 529 525 528 +3 529 530 525 +3 528 525 402 +3 402 525 403 +3 530 404 531 +3 530 529 404 +3 531 404 526 +3 526 404 403 +3 398 531 526 +3 398 400 531 +3 400 527 531 +3 529 528 532 +3 532 528 390 +3 404 529 405 +3 529 532 405 +3 407 409 524 +3 218 707 533 +3 220 1226 136 +3 1052 298 532 +3 208 298 1052 +3 533 1226 218 +3 409 208 1052 +3 411 534 410 +3 534 411 1258 +3 535 412 536 +3 411 304 757 +3 1258 411 757 +3 412 413 536 +3 410 540 300 +3 538 537 914 +3 761 536 413 +3 540 302 300 +3 304 308 758 +3 758 757 304 +3 539 914 537 +3 762 412 535 +3 416 914 539 +3 759 758 308 +3 539 537 423 +3 308 315 759 +3 315 2917 759 +3 412 762 229 +3 541 761 420 +3 421 541 420 +3 421 542 541 +3 542 543 541 +3 546 541 544 +3 544 541 543 +3 544 545 546 +3 422 543 542 +3 546 545 547 +3 765 543 422 +3 548 545 544 +3 547 545 548 +3 548 549 547 +3 325 326 573 +3 422 768 765 +3 549 548 769 +3 424 550 425 +3 422 244 768 +3 551 768 244 +3 425 550 552 +3 424 553 550 +3 553 424 554 +3 425 555 244 +3 555 551 244 +3 555 425 556 +3 425 552 556 +3 550 557 552 +3 557 556 552 +3 558 553 559 +3 558 557 553 +3 557 550 553 +3 554 559 553 +3 429 776 554 +3 557 430 556 +3 560 561 430 +3 556 430 561 +3 557 558 431 +3 431 558 562 +3 561 560 558 +3 562 558 560 +3 431 562 429 +3 429 563 776 +3 562 563 429 +3 560 433 565 +3 430 433 560 +3 562 560 566 +3 566 560 565 +3 563 562 566 +3 432 567 564 +3 568 567 432 +3 568 432 438 +3 779 568 438 +3 565 433 569 +3 433 449 569 +3 564 567 570 +3 436 570 785 +3 437 571 572 +3 437 436 571 +3 436 785 571 +3 438 786 572 +3 438 572 779 +3 571 779 572 +3 576 786 438 +3 577 569 449 +3 444 450 575 +3 572 575 446 +3 572 446 437 +3 446 575 450 +3 786 575 572 +3 760 805 451 +3 579 578 576 +3 449 458 577 +3 459 444 588 +3 444 574 588 +3 580 581 582 +3 583 579 584 +3 585 584 586 +3 459 587 460 +3 459 588 587 +3 591 580 590 +3 582 592 580 +3 592 590 580 +3 592 582 581 +3 594 583 593 +3 584 595 583 +3 595 593 583 +3 596 584 585 +3 595 584 596 +3 353 597 586 +3 597 463 598 +3 597 598 586 +3 599 600 601 +3 601 600 814 +3 602 458 465 +3 602 815 458 +3 815 577 458 +3 465 460 587 +3 465 587 602 +3 603 587 588 +3 462 589 467 +3 604 591 590 +3 605 591 604 +3 592 606 590 +3 606 604 590 +3 606 592 581 +3 593 820 594 +3 593 468 820 +3 595 468 593 +3 595 596 607 +3 597 353 607 +3 608 463 609 +3 608 469 463 +3 609 463 610 +3 610 463 597 +3 610 611 609 +3 612 613 614 +3 644 573 471 +3 599 615 616 +3 616 600 599 +3 442 477 453 +3 815 617 618 +3 815 602 617 +3 602 587 617 +3 619 587 603 +3 617 587 619 +3 620 621 622 +3 622 623 620 +3 606 605 604 +3 625 820 468 +3 625 468 639 +3 626 608 609 +3 627 609 611 +3 627 628 609 +3 628 626 609 +3 613 628 627 +3 612 628 613 +3 612 473 628 +3 473 612 629 +3 629 630 631 +3 474 832 833 +3 599 632 615 +3 615 632 633 +3 616 615 634 +3 634 615 633 +3 634 600 616 +3 635 618 478 +3 617 478 618 +3 635 479 619 +3 617 619 479 +3 620 636 621 +3 623 636 620 +3 370 626 640 +3 628 641 626 +3 641 640 626 +3 628 480 641 +3 631 660 629 +3 660 631 642 +3 474 833 485 +3 471 483 644 +3 645 599 680 +3 599 645 632 +3 632 645 646 +3 633 632 647 +3 647 632 646 +3 647 634 633 +3 477 635 478 +3 477 648 635 +3 635 648 479 +3 484 859 649 +3 488 859 484 +3 484 649 650 +3 668 669 624 +3 624 637 668 +3 637 651 668 +3 652 637 638 +3 651 637 652 +3 370 654 653 +3 370 640 654 +3 655 653 654 +3 656 654 640 +3 641 657 640 +3 657 656 640 +3 480 658 641 +3 641 658 657 +3 480 659 658 +3 480 629 659 +3 660 659 629 +3 661 662 663 +3 483 487 644 +3 487 492 644 +3 664 644 492 +3 680 665 645 +3 645 665 666 +3 646 645 667 +3 667 645 666 +3 667 647 646 +3 670 668 651 +3 652 670 651 +3 987 377 653 +3 654 671 655 +3 671 654 672 +3 654 656 672 +3 657 673 656 +3 673 672 656 +3 658 674 657 +3 674 673 657 +3 674 658 659 +3 675 662 661 +3 691 662 675 +3 676 661 663 +3 675 661 676 +3 485 677 643 +3 387 664 493 +3 680 681 665 +3 665 681 682 +3 666 665 683 +3 683 665 682 +3 683 667 666 +3 648 698 684 +3 686 669 687 +3 669 668 687 +3 703 668 670 +3 687 668 703 +3 688 671 672 +3 673 689 672 +3 689 688 672 +3 674 690 673 +3 690 689 673 +3 676 691 675 +3 499 678 498 +3 679 678 499 +3 692 694 693 +3 693 694 680 +3 680 694 695 +3 681 680 696 +3 696 680 695 +3 682 681 697 +3 697 681 696 +3 697 683 682 +3 698 699 684 +3 859 684 699 +3 859 699 707 +3 685 503 495 +3 685 700 503 +3 702 686 701 +3 687 701 686 +3 703 504 687 +3 866 688 689 +3 690 866 689 +3 714 517 693 +3 693 517 704 +3 692 704 509 +3 692 693 704 +3 509 705 692 +3 705 694 692 +3 695 694 706 +3 706 694 705 +3 697 696 706 +3 706 696 695 +3 504 702 701 +3 716 702 504 +3 710 510 511 +3 710 512 510 +3 514 512 867 +3 512 710 867 +3 514 712 711 +3 867 712 514 +3 393 713 387 +3 517 715 704 +3 509 704 715 +3 509 715 705 +3 715 738 705 +3 738 697 706 +3 738 706 705 +3 703 716 504 +3 718 708 717 +3 717 708 709 +3 511 719 710 +3 511 513 719 +3 514 719 513 +3 514 720 719 +3 730 719 720 +3 721 711 722 +3 721 720 711 +3 720 514 711 +3 712 722 711 +3 2917 177 1226 +3 393 520 713 +3 520 1014 713 +3 714 885 723 +3 714 723 724 +3 517 714 725 +3 725 714 724 +3 517 725 726 +3 517 726 715 +3 726 738 715 +3 727 718 717 +3 728 718 727 +3 729 717 709 +3 727 717 729 +3 721 730 720 +3 520 393 877 +3 1021 877 393 +3 1021 393 524 +3 723 885 734 +3 734 885 733 +3 724 723 735 +3 735 723 734 +3 736 724 735 +3 725 724 737 +3 737 724 736 +3 726 725 738 +3 738 725 737 +3 727 739 728 +3 729 739 727 +3 893 521 515 +3 731 522 525 +3 731 732 740 +3 731 740 522 +3 522 740 523 +3 741 523 740 +3 734 733 742 +3 735 734 742 +3 894 743 736 +3 894 736 735 +3 737 736 743 +3 744 738 737 +3 893 1217 521 +3 746 527 741 +3 527 523 741 +3 735 742 894 +3 530 745 525 +3 531 746 530 +3 530 746 745 +3 746 531 527 +3 524 409 901 +3 390 1051 532 +3 707 747 533 +3 532 1051 1052 +3 747 748 533 +3 533 748 749 +3 749 748 902 +3 533 749 902 +3 902 1226 533 +3 913 753 750 +3 752 751 754 +3 1103 410 534 +3 755 753 913 +3 756 755 913 +3 750 753 914 +3 914 753 755 +3 535 754 751 +3 535 536 754 +3 755 756 538 +3 914 755 538 +3 538 756 537 +3 535 751 762 +3 761 764 536 +3 756 423 537 +3 541 546 761 +3 546 764 761 +3 547 763 764 +3 546 547 764 +3 544 543 766 +3 766 543 765 +3 544 766 548 +3 763 549 767 +3 547 549 763 +3 764 763 767 +3 768 766 765 +3 548 766 769 +3 766 768 769 +3 769 767 549 +3 769 770 767 +3 764 767 770 +3 771 764 770 +3 551 769 768 +3 772 769 551 +3 772 773 770 +3 772 770 769 +3 770 773 771 +3 774 771 773 +3 774 554 918 +3 555 775 551 +3 551 775 772 +3 559 773 558 +3 558 773 926 +3 554 773 559 +3 774 773 554 +3 554 776 918 +3 918 776 777 +3 762 930 442 +3 555 924 775 +3 556 924 555 +3 556 925 924 +3 561 926 556 +3 556 926 925 +3 926 561 558 +3 776 563 778 +3 777 776 778 +3 566 778 563 +3 567 777 778 +3 568 777 567 +3 568 779 931 +3 931 779 780 +3 760 1132 805 +3 781 782 783 +3 565 569 791 +3 565 791 566 +3 791 945 566 +3 939 778 945 +3 945 778 566 +3 940 778 939 +3 570 940 784 +3 570 778 940 +3 570 567 778 +3 784 785 570 +3 785 786 571 +3 571 786 779 +3 780 779 786 +3 780 786 787 +3 787 786 576 +3 781 788 782 +3 569 577 791 +3 793 574 792 +3 792 574 794 +3 940 444 784 +3 940 794 444 +3 794 574 444 +3 575 784 444 +3 785 784 575 +3 786 785 575 +3 576 578 787 +3 789 796 797 +3 797 790 789 +3 792 945 801 +3 793 792 801 +3 793 801 574 +3 795 578 803 +3 579 803 578 +3 789 946 798 +3 789 798 796 +3 796 798 799 +3 797 796 800 +3 800 796 799 +3 800 790 797 +3 801 588 574 +3 802 951 580 +3 951 581 580 +3 803 579 583 +3 803 804 1128 +3 804 585 586 +3 804 586 1128 +3 586 598 1128 +3 952 573 825 +3 798 946 806 +3 799 798 807 +3 807 798 806 +3 814 800 807 +3 807 800 799 +3 790 800 814 +3 577 808 953 +3 577 953 950 +3 801 953 808 +3 801 808 588 +3 802 591 954 +3 580 591 802 +3 951 809 581 +3 803 594 810 +3 583 594 803 +3 811 812 804 +3 811 804 810 +3 810 804 803 +3 812 585 804 +3 812 813 585 +3 813 596 585 +3 601 946 599 +3 806 946 601 +3 814 807 806 +3 601 814 806 +3 577 815 808 +3 588 808 816 +3 816 808 815 +3 588 816 603 +3 954 605 957 +3 591 605 954 +3 581 809 606 +3 809 817 606 +3 820 810 594 +3 820 821 810 +3 822 811 810 +3 822 810 821 +3 607 813 822 +3 607 596 813 +3 597 823 610 +3 610 823 611 +3 613 824 614 +3 824 612 614 +3 831 612 824 +3 573 644 825 +3 952 825 664 +3 618 826 815 +3 826 816 815 +3 603 826 619 +3 816 826 603 +3 622 621 827 +3 622 827 623 +3 605 606 957 +3 957 606 817 +3 637 818 819 +3 637 624 818 +3 819 960 637 +3 625 828 820 +3 828 821 820 +3 822 821 829 +3 829 821 828 +3 607 822 830 +3 830 822 829 +3 966 607 830 +3 597 607 966 +3 967 597 966 +3 823 597 967 +3 611 823 627 +3 627 823 968 +3 824 613 627 +3 968 824 627 +3 629 612 831 +3 630 629 831 +3 831 631 630 +3 839 631 831 +3 825 644 834 +3 664 825 834 +3 600 634 814 +3 477 442 648 +3 618 635 826 +3 619 826 635 +3 621 636 827 +3 623 827 636 +3 637 960 638 +3 960 835 638 +3 625 639 828 +3 829 828 836 +3 836 828 639 +3 830 829 837 +3 837 829 836 +3 976 966 830 +3 976 830 838 +3 838 830 837 +3 839 840 642 +3 839 642 631 +3 840 841 842 +3 839 841 840 +3 841 989 842 +3 833 843 485 +3 664 834 644 +3 599 1154 680 +3 647 814 634 +3 648 442 698 +3 649 859 855 +3 855 844 649 +3 844 650 649 +3 650 844 845 +3 845 846 847 +3 844 846 845 +3 624 669 848 +3 624 848 985 +3 638 835 652 +3 835 985 652 +3 639 380 849 +3 836 639 849 +3 836 850 837 +3 653 838 850 +3 838 837 850 +3 653 655 976 +3 653 976 838 +3 976 655 993 +3 851 659 988 +3 988 659 660 +3 840 660 642 +3 840 852 660 +3 852 988 660 +3 842 852 840 +3 853 852 842 +3 854 842 662 +3 854 853 842 +3 662 842 663 +3 663 842 989 +3 995 663 989 +3 677 485 843 +3 1154 693 680 +3 667 814 647 +3 859 990 855 +3 856 844 855 +3 845 857 650 +3 650 857 685 +3 848 669 858 +3 670 985 848 +3 670 848 858 +3 985 670 652 +3 380 987 849 +3 653 850 987 +3 671 993 655 +3 659 862 674 +3 994 659 851 +3 862 659 994 +3 662 691 854 +3 995 676 663 +3 996 387 997 +3 683 814 667 +3 991 685 992 +3 992 685 857 +3 686 861 669 +3 860 861 686 +3 669 861 858 +3 861 703 858 +3 858 703 670 +3 671 688 993 +3 862 863 674 +3 674 863 690 +3 676 864 691 +3 995 864 676 +3 387 870 997 +3 697 892 683 +3 892 814 683 +3 700 999 859 +3 700 859 707 +3 991 999 685 +3 999 700 685 +3 686 702 860 +3 860 702 1006 +3 865 861 860 +3 703 861 865 +3 993 688 1008 +3 688 866 1008 +3 690 1009 866 +3 690 863 1009 +3 868 869 867 +3 870 387 713 +3 870 713 1013 +3 693 1187 714 +3 699 698 707 +3 702 716 1006 +3 1006 716 880 +3 703 865 1018 +3 709 708 872 +3 872 708 871 +3 871 873 872 +3 1009 1008 866 +3 710 874 867 +3 867 875 868 +3 874 875 867 +3 876 867 869 +3 876 712 867 +3 713 1014 1013 +3 1004 1014 520 +3 1004 520 877 +3 1187 1023 714 +3 697 738 892 +3 716 1018 880 +3 1018 716 703 +3 871 708 881 +3 708 718 881 +3 872 882 709 +3 873 882 872 +3 730 710 719 +3 730 883 710 +3 883 874 710 +3 721 722 884 +3 721 884 874 +3 721 874 883 +3 875 874 884 +3 876 722 712 +3 884 722 876 +3 878 877 1021 +3 1046 885 879 +3 714 1023 885 +3 728 886 881 +3 728 881 718 +3 882 729 709 +3 882 887 729 +3 721 883 730 +3 890 1033 731 +3 1033 732 731 +3 892 738 891 +3 728 739 886 +3 739 887 886 +3 729 887 739 +3 525 895 890 +3 731 525 890 +3 741 740 732 +3 733 885 1046 +3 743 897 737 +3 894 897 743 +3 892 737 897 +3 892 744 737 +3 891 738 744 +3 891 744 892 +3 733 1046 742 +3 892 897 899 +3 525 745 895 +3 746 1040 745 +3 898 896 901 +3 901 896 524 +3 742 1046 899 +3 899 894 742 +3 897 894 899 +3 900 898 901 +3 903 1047 901 +3 901 409 903 +3 747 902 748 +3 903 409 1052 +3 904 905 906 +3 909 1093 750 +3 1095 909 750 +3 1095 750 914 +3 751 910 911 +3 912 910 752 +3 752 910 751 +3 750 1093 913 +3 912 754 536 +3 912 752 754 +3 913 1093 756 +3 912 536 764 +3 762 751 911 +3 1258 757 759 +3 1105 914 915 +3 540 410 1103 +3 915 914 916 +3 760 540 1103 +3 757 758 759 +3 916 914 573 +3 759 2917 4237 +3 771 918 764 +3 917 919 923 +3 773 772 920 +3 920 921 773 +3 771 774 918 +3 928 922 919 +3 923 919 922 +3 772 775 924 +3 772 924 920 +3 924 925 920 +3 921 920 926 +3 926 920 925 +3 921 926 773 +3 777 927 918 +3 777 568 927 +3 932 1115 573 +3 922 928 929 +3 938 923 929 +3 929 923 922 +3 931 927 568 +3 1121 927 931 +3 1301 1132 760 +3 929 928 933 +3 933 938 929 +3 780 787 1121 +3 780 1121 931 +3 1121 787 1126 +3 783 934 935 +3 782 934 783 +3 935 781 783 +3 936 781 935 +3 937 928 789 +3 938 933 937 +3 937 933 928 +3 782 943 934 +3 788 943 782 +3 936 788 781 +3 943 788 936 +3 937 789 790 +3 937 790 938 +3 944 945 791 +3 792 939 945 +3 794 939 792 +3 940 939 794 +3 787 578 1126 +3 941 578 795 +3 942 941 795 +3 795 803 1128 +3 795 1128 942 +3 791 577 947 +3 791 947 948 +3 791 948 944 +3 948 1137 949 +3 948 949 944 +3 944 949 945 +3 945 949 801 +3 952 932 573 +3 577 950 947 +3 950 953 947 +3 801 949 1137 +3 801 1137 953 +3 805 1128 598 +3 932 952 1141 +3 938 790 814 +3 802 954 951 +3 951 954 809 +3 956 812 811 +3 1140 813 812 +3 809 957 817 +3 954 957 809 +3 819 958 959 +3 818 958 819 +3 959 960 819 +3 962 955 961 +3 963 956 964 +3 963 961 956 +3 961 955 956 +3 811 964 956 +3 811 822 964 +3 822 1140 964 +3 1140 822 813 +3 824 969 831 +3 664 1141 952 +3 1144 958 624 +3 958 818 624 +3 961 965 962 +3 963 965 961 +3 967 968 823 +3 968 969 824 +3 831 969 970 +3 970 971 831 +3 831 971 839 +3 833 832 972 +3 832 1366 972 +3 930 698 442 +3 1144 624 975 +3 960 975 835 +3 1144 975 960 +3 976 967 966 +3 977 968 967 +3 969 968 977 +3 970 969 978 +3 978 969 977 +3 1155 971 978 +3 978 971 970 +3 979 971 1155 +3 839 971 979 +3 839 980 841 +3 979 980 839 +3 841 980 989 +3 643 1366 832 +3 972 981 833 +3 981 843 833 +3 982 983 844 +3 844 984 846 +3 983 984 844 +3 846 984 847 +3 624 985 975 +3 975 985 835 +3 849 987 836 +3 986 987 849 +3 836 987 850 +3 994 977 967 +3 978 977 994 +3 851 988 978 +3 851 978 994 +3 1155 978 988 +3 1155 852 979 +3 1155 988 852 +3 853 979 852 +3 980 979 853 +3 854 980 853 +3 854 989 980 +3 1382 1366 677 +3 1366 643 677 +3 1382 677 981 +3 843 981 677 +3 996 664 387 +3 693 1154 1365 +3 855 991 856 +3 990 991 855 +3 856 982 844 +3 856 991 982 +3 991 992 982 +3 845 992 857 +3 845 983 992 +3 983 982 992 +3 847 983 845 +3 984 983 847 +3 987 986 849 +3 993 967 976 +3 994 967 993 +3 989 854 1165 +3 854 691 1165 +3 995 989 1165 +3 997 998 1161 +3 996 997 1161 +3 698 930 707 +3 990 859 999 +3 991 990 999 +3 862 1001 863 +3 862 994 1001 +3 994 993 1001 +3 691 1002 1165 +3 995 1002 864 +3 1002 691 864 +3 998 997 870 +3 1161 998 1003 +3 1004 1161 1003 +3 1005 1161 1004 +3 693 1169 1187 +3 1365 1169 693 +3 860 1006 1000 +3 860 1000 1007 +3 1000 1006 1007 +3 1007 865 860 +3 1001 993 1009 +3 1009 993 1008 +3 1001 1009 863 +3 1011 868 1010 +3 1011 1179 868 +3 1179 869 868 +3 1179 1012 869 +3 1013 998 870 +3 1003 998 1014 +3 1014 998 1013 +3 1004 1003 1014 +3 1005 1004 877 +3 1015 1005 877 +3 878 1005 1015 +3 1162 1005 878 +3 1162 878 1163 +3 1163 878 1021 +3 1007 1006 1018 +3 1018 1006 880 +3 865 1007 1018 +3 873 871 1189 +3 871 1188 1189 +3 873 1189 1019 +3 1010 875 884 +3 868 875 1010 +3 876 1011 1010 +3 876 1010 884 +3 869 1020 876 +3 1020 1011 876 +3 1012 1020 869 +3 1015 877 878 +3 1016 1022 879 +3 885 1016 879 +3 1017 1016 885 +3 885 1023 1017 +3 871 881 1024 +3 871 1024 1025 +3 871 1025 1188 +3 873 1026 882 +3 873 1019 1026 +3 1028 1029 1027 +3 1030 1032 1033 +3 1031 1032 1030 +3 1022 1046 879 +3 881 886 1034 +3 881 1034 1024 +3 1034 1035 1024 +3 1024 1036 1025 +3 1024 1035 1036 +3 1036 1196 1025 +3 1036 1026 1196 +3 1036 887 1026 +3 882 1026 887 +3 1027 1037 1028 +3 1029 1037 1027 +3 888 1202 889 +3 1038 888 889 +3 1039 1031 1030 +3 1205 1031 1039 +3 1033 890 1039 +3 1033 1039 1030 +3 1033 1032 1040 +3 1033 1040 732 +3 887 1034 886 +3 1035 1034 887 +3 1036 1035 887 +3 888 1204 1202 +3 1204 888 1038 +3 1205 1039 890 +3 1205 890 895 +3 741 732 1040 +3 1041 524 896 +3 1427 895 745 +3 1040 1427 745 +3 741 1040 746 +3 1022 1210 1046 +3 902 747 707 +3 898 1045 1042 +3 896 898 1042 +3 896 1042 1041 +3 1043 1044 900 +3 1045 900 1044 +3 1045 898 900 +3 1050 899 1046 +3 1043 900 1047 +3 900 901 1047 +3 1049 1046 1048 +3 1050 1046 1049 +3 1433 899 1050 +3 1217 1051 390 +3 1049 1048 1220 +3 1225 1222 1051 +3 1051 1222 1052 +3 1047 903 1215 +3 903 1052 1223 +3 1055 1053 1056 +3 1056 1053 1060 +3 1058 1053 1055 +3 1056 1058 1055 +3 1060 1057 1056 +3 1058 1056 1057 +3 1058 1057 1059 +3 1059 1057 1060 +3 1059 1062 1058 +3 905 1059 1060 +3 904 1061 1062 +3 905 904 1062 +3 905 1062 1059 +3 905 1063 906 +3 905 1060 1063 +3 1060 1238 1063 +3 906 1061 904 +3 906 1063 1061 +3 1063 1064 1061 +3 1064 1062 1061 +3 1238 1064 1063 +3 1065 1237 1066 +3 1067 1066 1237 +3 1238 1067 1237 +3 1067 1238 1068 +3 1066 1067 1070 +3 1070 1067 1068 +3 1238 1239 1069 +3 1238 1069 1068 +3 1070 1068 1069 +3 1072 1066 1070 +3 1242 1069 1239 +3 1070 1069 1071 +3 1071 1069 1242 +3 1072 1070 1071 +3 1071 1242 1073 +3 1072 1071 1073 +3 1073 1242 1074 +3 1072 1073 1076 +3 1076 1073 1074 +3 1076 1074 1075 +3 1074 1242 1077 +3 907 1075 1077 +3 1075 1074 1077 +3 907 1078 1076 +3 907 1076 1075 +3 907 1081 908 +3 1077 1081 907 +3 907 908 1078 +3 908 1081 1080 +3 908 1080 1078 +3 1078 1080 1079 +3 1472 1082 1081 +3 1243 1079 1080 +3 1081 1243 1080 +3 1082 1243 1081 +3 1084 1082 1083 +3 1084 1083 1085 +3 1085 1083 1086 +3 1086 1084 1085 +3 1087 1084 1086 +3 1088 1084 1087 +3 1087 1089 1088 +3 1088 1089 1090 +3 1090 1091 1088 +3 1089 1091 1090 +3 1094 1093 1092 +3 1095 1092 1093 +3 909 1095 1093 +3 1098 1097 1099 +3 1102 1251 1096 +3 1100 1097 1098 +3 1504 1100 1098 +3 910 1100 911 +3 910 1099 1100 +3 1099 1097 1100 +3 1099 910 1101 +3 910 912 1101 +3 1504 911 1100 +3 534 1258 1256 +3 1095 914 1259 +3 1504 762 911 +3 1259 914 1105 +3 1101 912 764 +3 1263 1259 1105 +3 1102 1106 1104 +3 1104 1106 1107 +3 1258 759 4237 +3 1112 1263 1105 +3 1109 1107 1108 +3 1108 1107 1106 +3 915 916 1105 +3 916 1112 1105 +3 1110 1109 1108 +3 916 573 1111 +3 1112 916 1111 +3 1108 1114 1110 +3 1110 1114 1113 +3 573 1268 1111 +3 1268 1112 1111 +3 1114 1270 1113 +3 760 1511 2369 +3 1115 1268 573 +3 1288 917 1116 +3 1276 1115 1290 +3 919 917 1118 +3 917 1288 1118 +3 1116 917 1117 +3 1118 928 919 +3 917 923 1117 +3 1532 927 1119 +3 1290 1115 932 +3 1117 923 938 +3 1121 1119 927 +3 1120 1290 932 +3 1122 1119 1121 +3 1122 1123 1119 +3 1123 1124 1292 +3 1119 1123 1292 +3 1124 942 1292 +3 935 934 1125 +3 935 1125 936 +3 1297 1120 932 +3 928 1118 789 +3 1126 1122 1121 +3 1123 1122 1127 +3 1127 1122 1126 +3 941 1124 1127 +3 1124 1123 1127 +3 942 1124 941 +3 942 1128 1292 +3 1129 1130 1131 +3 1131 1130 1132 +3 934 943 1125 +3 936 1125 943 +3 1133 1134 1135 +3 1135 1136 1133 +3 1297 932 1141 +3 1118 1311 789 +3 1127 1126 578 +3 941 1127 578 +3 1129 805 1132 +3 1130 1129 1132 +3 1133 1315 1134 +3 1136 1315 1133 +3 948 947 1137 +3 1298 1141 1318 +3 1311 946 789 +3 947 953 1137 +3 423 893 470 +3 1142 1318 1141 +3 1143 1318 1142 +3 946 1311 599 +3 956 955 1138 +3 1138 1139 956 +3 956 1140 812 +3 1139 1140 956 +3 959 958 1144 +3 959 1144 960 +3 1138 962 1145 +3 955 962 1138 +3 964 1145 963 +3 964 1139 1145 +3 1139 1138 1145 +3 1140 1139 964 +3 1142 1141 1146 +3 1147 1142 1146 +3 1143 1142 1147 +3 1148 1143 1147 +3 1323 1148 1353 +3 962 965 1145 +3 963 1145 965 +3 974 973 1577 +3 1577 1150 974 +3 664 1151 1141 +3 1151 1146 1141 +3 1147 1146 1151 +3 1152 1147 1151 +3 1148 1147 1152 +3 1153 1148 1152 +3 1160 1148 1153 +3 1353 1148 1160 +3 1149 1343 1154 +3 974 1156 973 +3 974 1157 1156 +3 1157 1150 1158 +3 1157 974 1150 +3 1153 1152 1151 +3 1160 1153 1159 +3 1354 1353 1160 +3 1365 1154 1343 +3 1166 1382 981 +3 664 996 1151 +3 1153 1151 996 +3 1159 1153 996 +3 996 1160 1159 +3 1161 1160 996 +3 1354 1160 1161 +3 1354 1161 1162 +3 1005 1162 1161 +3 1373 1162 1163 +3 1168 1163 1167 +3 1359 1168 1175 +3 1377 1164 1169 +3 707 930 5643 +3 995 1165 1002 +3 1171 1172 1170 +3 1167 1163 1173 +3 1168 1167 1174 +3 1174 1167 1173 +3 1183 1168 1174 +3 1175 1168 1183 +3 1184 1175 1183 +3 1164 1176 1177 +3 1169 1164 1178 +3 1178 1164 1177 +3 1186 1169 1178 +3 1187 1169 1186 +3 1172 1180 1170 +3 1173 1163 1021 +3 1021 1174 1173 +3 1185 1016 1176 +3 1177 1176 1016 +3 1017 1177 1016 +3 1178 1177 1017 +3 1017 1186 1178 +3 1179 1011 1020 +3 1012 1179 1020 +3 1193 1182 1181 +3 1409 1194 1181 +3 1194 1193 1181 +3 1021 1183 1174 +3 1184 1183 1195 +3 1195 1183 1021 +3 1016 1185 1022 +3 1022 1185 1415 +3 1023 1186 1017 +3 1187 1186 1023 +3 1188 1025 1196 +3 1019 1196 1026 +3 1019 1189 1196 +3 1189 1188 1196 +3 1028 1197 1029 +3 1190 1198 1191 +3 1192 1198 1190 +3 1193 1205 1182 +3 1031 1205 1193 +3 1031 1193 1194 +3 1194 1409 1199 +3 1194 1199 1031 +3 1031 1199 1032 +3 524 1195 1021 +3 524 1413 1195 +3 1200 1022 1201 +3 1201 1022 1415 +3 1028 1037 1197 +3 1029 1197 1037 +3 1202 1203 889 +3 1038 1203 1204 +3 1038 889 1203 +3 1206 1040 1032 +3 1206 1032 1199 +3 524 1041 1413 +3 1207 1413 1208 +3 1208 1413 1041 +3 1201 1207 1208 +3 1201 1208 1200 +3 1200 1208 1209 +3 1200 1209 1022 +3 1205 895 1427 +3 1206 1427 1040 +3 1210 1208 1041 +3 1209 1208 1210 +3 1022 1209 1210 +3 1041 1042 1211 +3 1041 1211 1210 +3 1210 1211 1046 +3 1433 892 899 +3 1044 1043 1212 +3 1212 1213 1044 +3 1044 1214 1045 +3 1213 1214 1044 +3 1045 1211 1042 +3 1045 1214 1211 +3 1046 1211 1048 +3 1047 1212 1043 +3 1047 1215 1212 +3 1215 1213 1212 +3 1214 1213 1216 +3 1216 1213 1215 +3 1211 1214 1216 +3 1216 1215 1218 +3 1211 1216 1219 +3 1219 1216 1218 +3 1048 1211 1220 +3 1211 1219 1220 +3 1049 1433 1050 +3 1217 1221 1051 +3 1051 1221 1225 +3 1215 903 1223 +3 1218 1215 1223 +3 1219 1218 1223 +3 1220 1219 1224 +3 1224 1219 1223 +3 1893 1433 1049 +3 1893 1049 1224 +3 1224 1049 1220 +3 902 707 5643 +3 1217 1632 1221 +3 1221 1632 1225 +3 1893 1224 1223 +3 1632 1052 1225 +3 1225 1052 1222 +3 902 5643 1226 +3 1229 1227 1230 +3 1234 1229 1230 +3 1227 1231 1230 +3 1234 1230 1232 +3 1232 1230 1231 +3 1232 1231 1233 +3 1234 1232 1233 +3 1233 1231 1235 +3 1234 1233 1236 +3 1236 1233 1235 +3 1235 1231 1469 +3 1054 1469 1053 +3 1054 1236 1469 +3 1236 1235 1469 +3 1054 1053 1234 +3 1054 1234 1236 +3 1053 1469 1060 +3 1234 1053 1058 +3 1058 1062 1234 +3 1238 1060 1469 +3 1062 1064 1234 +3 1234 1064 1663 +3 1064 1237 1663 +3 1238 1237 1064 +3 1237 1065 1066 +3 1237 1066 1663 +3 1066 1072 1663 +3 1241 1240 1239 +3 1242 1239 1240 +3 1241 1242 1240 +3 1472 1242 1241 +3 1470 1472 1241 +3 1072 1076 1079 +3 1242 1472 1077 +3 1077 1472 1081 +3 1076 1078 1079 +3 1472 1473 1082 +3 1473 1083 1082 +3 1475 1083 1473 +3 1082 1084 1243 +3 1243 1084 1244 +3 1086 1083 1475 +3 1476 1086 1475 +3 1086 1476 1087 +3 1084 1088 1244 +3 1089 1087 1476 +3 1244 1088 1091 +3 1089 1476 1246 +3 1249 1089 1246 +3 1089 1249 1091 +3 1245 1247 1248 +3 1245 1248 1092 +3 1245 1092 1503 +3 1091 1249 1250 +3 1091 1250 1244 +3 1248 1094 1092 +3 1096 1246 1489 +3 1249 1246 1096 +3 1251 1249 1096 +3 1251 1250 1249 +3 1251 1244 1250 +3 1262 1244 1251 +3 1099 1252 1098 +3 1099 1487 1252 +3 1095 1503 1092 +3 1102 1096 1489 +3 1499 1102 1489 +3 1098 1252 1253 +3 1967 1487 1101 +3 1487 1099 1101 +3 1098 1253 1504 +3 1255 1254 1257 +3 1257 1254 1256 +3 1258 1500 1255 +3 1257 1258 1255 +3 1505 1102 1499 +3 1103 534 1502 +3 1256 1502 534 +3 1258 1257 1256 +3 1259 1503 1095 +3 1505 1508 1102 +3 1094 423 1093 +3 1251 1102 1262 +3 1967 1101 764 +3 1102 1104 1262 +3 1260 1259 1263 +3 1506 1259 1260 +3 1093 423 756 +3 1508 1261 1106 +3 1102 1508 1106 +3 1104 1107 1262 +3 1263 1112 1264 +3 1260 1263 1265 +3 1265 1263 1264 +3 1260 1512 1506 +3 1261 1108 1106 +3 1109 1262 1107 +3 1264 1112 1266 +3 1265 1264 1267 +3 1267 1264 1266 +3 1260 1265 1267 +3 1273 1260 1267 +3 1512 1260 1273 +3 1109 1110 1262 +3 1268 1266 1112 +3 1268 1267 1266 +3 1273 1267 1268 +3 1114 1108 1269 +3 1262 1110 1271 +3 1110 1113 1271 +3 1103 1511 760 +3 1272 1512 1273 +3 1270 1271 1113 +3 1114 1269 1275 +3 1114 1275 1271 +3 1114 1271 1270 +3 1274 1517 1518 +3 1517 1274 1269 +3 1275 1269 1274 +3 1272 1273 1278 +3 1276 1272 1278 +3 1277 1275 1274 +3 1279 1280 1277 +3 1115 1273 1268 +3 1115 1278 1273 +3 1284 1279 1281 +3 1282 1280 1279 +3 1284 1282 1279 +3 1115 1276 1278 +3 1284 1281 1283 +3 1286 1282 1284 +3 1285 1283 1281 +3 1284 1283 1286 +3 1286 1283 1285 +3 1287 1285 1281 +3 1287 1286 1285 +3 1289 1276 1290 +3 1525 1288 1287 +3 918 1740 764 +3 1287 1288 1116 +3 918 927 1740 +3 927 1531 1740 +3 1525 1533 1118 +3 1525 1118 1288 +3 1287 1116 1117 +3 1531 927 1532 +3 2369 1301 760 +3 1773 1289 1291 +3 1292 1532 1119 +3 1293 1290 1120 +3 1289 1290 1293 +3 1291 1289 1293 +3 1773 1291 1300 +3 1300 1291 1294 +3 1533 1296 1118 +3 1120 1297 1293 +3 1291 1293 1298 +3 1298 1293 1297 +3 1294 1291 1299 +3 1299 1291 1298 +3 1300 1294 1302 +3 1302 1294 1299 +3 1296 1295 1535 +3 1118 1296 1535 +3 1287 1117 814 +3 1303 1300 1302 +3 1304 1300 1303 +3 1300 1304 1305 +3 1118 1535 1311 +3 1129 1301 805 +3 1131 1301 1129 +3 1132 1301 1131 +3 1135 1134 1538 +3 1135 1538 1136 +3 1298 1297 1141 +3 1302 1299 1308 +3 1308 1299 1298 +3 1303 1302 1309 +3 1309 1302 1308 +3 1304 1303 1309 +3 1305 1304 1310 +3 1310 1304 1309 +3 1312 1311 1535 +3 938 814 1117 +3 1307 1313 1306 +3 1313 1307 1314 +3 1315 1541 1134 +3 1538 1134 1541 +3 1315 1136 1316 +3 1316 1136 1538 +3 1538 1317 1316 +3 1318 1308 1298 +3 1309 1308 1318 +3 1321 1541 1315 +3 1316 1319 1315 +3 1319 1316 1317 +3 1320 1309 1318 +3 1310 1309 1320 +3 1801 599 1311 +3 1321 1315 1322 +3 1315 1319 1322 +3 1322 1319 1317 +3 1318 1143 1320 +3 1310 1320 1323 +3 1323 1320 1143 +3 1324 1310 1323 +3 599 1801 1154 +3 1323 1143 1148 +3 1325 1324 1323 +3 1326 1324 1325 +3 1329 1801 1328 +3 1154 1801 1149 +3 1149 1801 1329 +3 1330 1331 1332 +3 1330 1333 1334 +3 1332 1333 1330 +3 1325 1323 1353 +3 1326 1325 1336 +3 1336 1325 1335 +3 1324 1326 1337 +3 1337 1326 1336 +3 1338 1324 1337 +3 1542 1324 1338 +3 1342 1327 1362 +3 1364 1327 1342 +3 1328 1327 1364 +3 1329 1328 1343 +3 1149 1329 1343 +3 1344 1331 1330 +3 1345 1331 1344 +3 1334 1346 1330 +3 1346 1344 1330 +3 1347 1577 973 +3 1150 1348 1349 +3 1150 1577 1348 +3 1348 1350 1349 +3 1335 1325 1353 +3 1336 1335 1353 +3 1337 1336 1354 +3 1354 1336 1353 +3 1338 1337 1354 +3 1355 1338 1354 +3 1566 1338 1355 +3 1566 1355 1356 +3 1357 1339 1340 +3 1341 1339 1358 +3 1358 1339 1357 +3 1341 1358 1561 +3 1563 1359 1360 +3 1342 1362 1363 +3 1364 1342 1363 +3 1328 1364 1365 +3 1343 1328 1365 +3 1346 1345 1344 +3 1156 1157 1367 +3 1156 1367 1347 +3 1156 1347 973 +3 1367 1157 1158 +3 1367 1158 1368 +3 1349 1158 1150 +3 1349 1369 1158 +3 1369 1368 1158 +3 1350 1369 1349 +3 1351 1369 1350 +3 1351 1352 1370 +3 1371 1370 1352 +3 1355 1354 1162 +3 1372 1356 1355 +3 1162 1372 1355 +3 1374 1373 1357 +3 1358 1357 1163 +3 1163 1357 1373 +3 1561 1358 1168 +3 1168 1358 1163 +3 1562 1561 1168 +3 1359 1562 1168 +3 1360 1359 1375 +3 1362 1361 1377 +3 1377 1361 1376 +3 1363 1362 1377 +3 1169 1363 1377 +3 1365 1363 1169 +3 1364 1363 1365 +3 1382 1378 1366 +3 981 1383 1166 +3 1384 1351 1370 +3 1371 1379 1370 +3 1379 1384 1370 +3 1379 1371 1380 +3 1162 1381 1372 +3 1162 1373 1381 +3 1374 1381 1373 +3 1359 1175 1375 +3 1377 1376 1164 +3 1166 1383 1382 +3 1380 1384 1379 +3 1171 1170 1385 +3 1171 1385 1172 +3 1175 1388 1389 +3 1609 1175 1389 +3 1164 1389 1388 +3 1176 1164 1388 +3 1170 1180 1385 +3 1172 1385 1180 +3 1390 1392 1393 +3 1392 1613 1393 +3 1181 1182 1394 +3 1386 1395 1387 +3 1396 1398 1399 +3 1397 1398 1396 +3 1175 1184 1413 +3 1388 1175 1400 +3 1400 1175 1413 +3 1176 1400 1185 +3 1388 1400 1176 +3 1401 1190 1191 +3 1401 1402 1190 +3 1192 1190 1403 +3 1403 1190 1402 +3 1391 1403 1402 +3 1391 1390 1403 +3 1390 1404 1403 +3 1404 1390 1393 +3 1613 1405 1393 +3 1405 1406 1407 +3 1613 1406 1405 +3 1394 1182 1408 +3 1181 1394 1409 +3 1394 1408 1409 +3 1396 1399 1410 +3 1411 1410 1399 +3 1411 1399 1412 +3 1184 1195 1413 +3 1414 1400 1413 +3 1414 1185 1400 +3 1414 1415 1185 +3 1191 1416 1401 +3 1191 1198 1416 +3 1403 1416 1192 +3 1192 1416 1198 +3 1404 1416 1403 +3 1417 1405 1407 +3 1182 1205 1408 +3 1205 1419 1408 +3 1419 1409 1408 +3 1409 1419 1199 +3 1420 1396 1410 +3 1411 1421 1410 +3 1421 1411 1412 +3 1414 1413 1430 +3 1415 1629 1201 +3 1422 1423 1424 +3 1424 1425 1422 +3 1203 1202 1418 +3 1202 1631 1418 +3 1418 1426 1203 +3 1418 1625 1426 +3 1203 1426 1204 +3 1205 1427 1419 +3 1427 1199 1419 +3 1199 1427 1206 +3 1410 1428 1420 +3 1428 1410 1429 +3 1410 1421 1429 +3 1429 1421 1432 +3 1207 1430 1413 +3 1201 1629 1207 +3 1629 1430 1207 +3 1433 814 892 +3 1422 1431 1423 +3 1425 1431 1422 +3 1426 1631 1202 +3 1204 1426 1202 +3 1429 1432 1428 +3 1632 1217 893 +3 1226 2516 2917 +3 1052 1632 1633 +3 1435 1436 1437 +3 1434 1435 1437 +3 1435 1434 1438 +3 1436 1435 1438 +3 1437 1436 1439 +3 1439 1436 1438 +3 1434 1638 1438 +3 1441 1437 1439 +3 1445 1438 1638 +3 1439 1438 1445 +3 1440 1441 1439 +3 1638 1639 1445 +3 1441 1440 1442 +3 1448 1441 1444 +3 1444 1441 1442 +3 1440 1439 1445 +3 1442 1440 1445 +3 1444 1442 1443 +3 1443 1442 1446 +3 1446 1442 1445 +3 1444 1443 1447 +3 1447 1443 1446 +3 1448 1444 1447 +3 1446 1445 1645 +3 1449 1447 1446 +3 1451 1448 1447 +3 1449 1451 1447 +3 1645 1647 1446 +3 1450 1446 1647 +3 1449 1446 1450 +3 1451 1449 1450 +3 1647 1452 1450 +3 1451 1450 1453 +3 1453 1450 1452 +3 1453 1454 1451 +3 1452 1648 1455 +3 1452 1455 1453 +3 1456 1454 1453 +3 1456 1453 1455 +3 1648 1457 1455 +3 1455 1457 1456 +3 1457 1458 1456 +3 1457 1654 1458 +3 1654 1459 1458 +3 1653 1654 1460 +3 1461 1460 1654 +3 1461 1462 1460 +3 1461 1463 1462 +3 1462 1463 1460 +3 1464 1460 1463 +3 1463 1466 1464 +3 1465 1466 1463 +3 1659 1467 1465 +3 1465 1467 1466 +3 1228 1227 1468 +3 1228 1468 1466 +3 1228 1466 1467 +3 1468 1227 1229 +3 1467 1659 1228 +3 1228 1659 1227 +3 1234 1468 1229 +3 1227 1659 1231 +3 1234 4051 1468 +3 1469 1231 1662 +3 1238 1469 1664 +3 1238 1664 1239 +3 1664 1666 1239 +3 1239 1666 1241 +3 1241 1666 1470 +3 1072 1079 1663 +3 1471 1470 1666 +3 1663 1079 1243 +3 1471 1670 1470 +3 1470 1670 1472 +3 1673 1472 1670 +3 1473 1472 1673 +3 1475 1473 1474 +3 1243 1244 1663 +3 1674 1474 1473 +3 1474 1674 1475 +3 1475 1674 1476 +3 1476 1477 1246 +3 1476 1480 1477 +3 1480 1481 1477 +3 1477 1481 1246 +3 1486 1482 1478 +3 1483 1478 1482 +3 1247 1493 1248 +3 1247 1479 1493 +3 1245 1479 1247 +3 1481 1480 1484 +3 1481 1484 1246 +3 1485 1482 1486 +3 1483 1482 1487 +3 1487 1482 1485 +3 1487 1967 1483 +3 1248 1493 1094 +3 1245 1503 1694 +3 1489 1480 1488 +3 1246 1484 1489 +3 1484 1480 1489 +3 1252 1485 1486 +3 1252 1486 1504 +3 1487 1485 1252 +3 1490 1491 1492 +3 1494 1490 1492 +3 1492 1491 1693 +3 1697 1094 1493 +3 1701 1694 1503 +3 1252 1504 1253 +3 1490 1494 1496 +3 1496 1494 1495 +3 1491 1490 1497 +3 1497 1490 1496 +3 1497 1693 1491 +3 1498 1499 1488 +3 1488 1499 1489 +3 1256 1254 1502 +3 1502 1255 1494 +3 1502 1254 1255 +3 1500 1494 1255 +3 1500 1495 1494 +3 1496 1495 1501 +3 1501 1495 1500 +3 1501 1497 1496 +3 1705 1499 1498 +3 1500 1258 1501 +3 2278 1497 1501 +3 1705 1712 1499 +3 1499 1712 1505 +3 1258 2278 1501 +3 1697 423 1094 +3 1503 1259 1506 +3 1258 4237 2278 +3 1712 1507 1261 +3 1505 1712 1261 +3 1508 1505 1261 +3 1512 1503 1506 +3 1719 1503 1512 +3 2686 1967 764 +3 1103 1978 1511 +3 1507 1509 1510 +3 1261 1507 1513 +3 1513 1507 1510 +3 1108 1261 1513 +3 1514 1512 1515 +3 1513 1510 1721 +3 1108 1513 1269 +3 1262 1271 1519 +3 1511 1724 2369 +3 1272 1515 1512 +3 1513 1721 1269 +3 1272 1276 1515 +3 1721 1516 1518 +3 1269 1721 1517 +3 1517 1721 1518 +3 1514 1515 1276 +3 1271 1275 1519 +3 1518 1277 1274 +3 1277 1519 1275 +3 1731 1514 1276 +3 1518 1516 1520 +3 1279 1518 1520 +3 1277 1518 1279 +3 1277 1280 1519 +3 1519 1280 1524 +3 2686 764 1740 +3 1279 1520 1281 +3 1280 1282 1524 +3 1289 1731 1276 +3 1520 1521 1522 +3 1281 1520 1523 +3 1282 1286 1524 +3 1521 1735 1522 +3 1523 1520 1522 +3 4845 930 762 +3 1523 1522 1525 +3 1281 1523 1287 +3 1287 1523 1525 +3 1287 1524 1286 +3 1773 2021 1289 +3 1526 1529 1528 +3 1527 1530 1526 +3 1530 1529 1526 +3 1522 2034 1525 +3 1530 1528 1529 +3 1533 1525 2034 +3 1292 1531 1532 +3 1128 1531 1292 +3 1753 1300 1534 +3 1533 1776 1535 +3 1296 1533 1295 +3 1301 1128 805 +3 1774 1534 1779 +3 1776 1788 1535 +3 1295 1533 1535 +3 1305 1534 1300 +3 1310 1534 1305 +3 1779 1534 1310 +3 1542 1779 1310 +3 1535 1788 1312 +3 1797 1536 1306 +3 1307 1306 1536 +3 1537 1307 1536 +3 1789 1542 1790 +3 1306 1313 1806 +3 1797 1306 1806 +3 1307 1537 1314 +3 1537 1540 1314 +3 1542 1310 1324 +3 1539 1790 1542 +3 1312 1801 1311 +3 1287 814 4422 +3 1313 1810 1806 +3 1313 1540 1810 +3 1314 1540 1313 +3 1538 1811 1317 +3 1783 1539 1542 +3 1543 1783 1542 +3 1545 1328 1808 +3 1799 1545 1808 +3 1317 1811 1322 +3 1811 1546 1322 +3 1543 1542 1547 +3 1548 1543 1547 +3 1560 1549 1544 +3 1562 1544 1549 +3 1563 1544 1562 +3 1550 1544 1563 +3 1545 1550 1327 +3 1328 1545 1327 +3 1551 1552 1553 +3 1551 1553 1554 +3 1553 1555 1554 +3 1322 1546 1321 +3 1547 1542 1556 +3 1557 1547 1556 +3 1548 1547 1558 +3 1558 1547 1557 +3 1339 1548 1558 +3 1815 1548 1339 +3 1341 1815 1339 +3 1341 1559 1560 +3 1815 1341 1560 +3 1560 1559 1549 +3 1549 1559 1561 +3 1562 1549 1561 +3 1327 1550 1563 +3 1332 1823 1564 +3 1331 1823 1332 +3 1552 1551 1333 +3 1552 1333 1564 +3 1564 1333 1332 +3 1334 1333 1551 +3 1334 1551 1565 +3 1565 1551 1554 +3 1565 1554 1555 +3 1348 1817 1351 +3 1817 1578 1351 +3 1556 1542 1338 +3 1566 1556 1338 +3 1557 1556 1566 +3 1566 1558 1557 +3 1339 1558 1567 +3 1567 1558 1566 +3 1339 1567 1340 +3 1559 1341 1561 +3 1359 1563 1562 +3 1327 1563 1362 +3 1569 1570 1568 +3 1571 1823 1345 +3 1823 1331 1345 +3 1565 1572 1573 +3 1565 1573 1334 +3 1334 1573 1346 +3 1555 1572 1565 +3 1574 1575 1366 +3 1366 1575 972 +3 1575 981 972 +3 1576 1577 1347 +3 1350 1348 1351 +3 1351 1578 1352 +3 1356 1579 1566 +3 1579 1567 1566 +3 1340 1579 1357 +3 1567 1579 1340 +3 1361 1563 1360 +3 1362 1563 1361 +3 1580 1569 1568 +3 1847 1569 1580 +3 1581 1568 1570 +3 1580 1568 1581 +3 1571 1345 1830 +3 1345 1346 1830 +3 1830 1346 1573 +3 1850 981 1575 +3 1347 1367 1576 +3 1367 1368 1576 +3 1369 1582 1368 +3 1368 1582 1576 +3 1582 1369 1351 +3 1352 1578 1834 +3 1371 1352 1834 +3 1583 1585 1586 +3 1584 1585 1583 +3 1579 1587 1841 +3 1579 1356 1587 +3 1356 1372 1587 +3 1841 1357 1579 +3 1841 1595 1357 +3 1595 1374 1357 +3 1360 1588 1589 +3 1375 1588 1360 +3 1361 1360 1590 +3 1360 1589 1590 +3 1590 1376 1361 +3 1581 1847 1580 +3 1383 1850 1597 +3 981 1850 1383 +3 1351 1384 1582 +3 1591 1380 1834 +3 1380 1371 1834 +3 1583 1592 1593 +3 1583 1586 1592 +3 1592 1586 1585 +3 1594 1372 1381 +3 1594 1587 1372 +3 1595 1594 1374 +3 1595 1596 1594 +3 1594 1381 1374 +3 1375 1175 1588 +3 1588 1175 1874 +3 1376 1590 1164 +3 1383 1597 1382 +3 1382 1597 1378 +3 1380 1591 1384 +3 1598 1593 1592 +3 1870 1599 1592 +3 1599 1598 1592 +3 1600 1587 1594 +3 1596 1601 1594 +3 1601 1600 1594 +3 1602 1596 1595 +3 1602 1601 1596 +3 1874 1175 1609 +3 1604 1593 1598 +3 1614 1593 1604 +3 1386 1387 1604 +3 1386 1604 1599 +3 1599 1604 1598 +3 1599 1870 1386 +3 1870 1605 1386 +3 1600 1601 1606 +3 1602 1607 1601 +3 1607 1606 1601 +3 1880 1608 1602 +3 1608 1607 1602 +3 1610 1611 1609 +3 1610 1609 1389 +3 1389 1612 1610 +3 1389 1164 1612 +3 1164 1590 1612 +3 1603 1392 1391 +3 1391 1392 1390 +3 1392 1619 1613 +3 1392 1603 1619 +3 1387 1614 1604 +3 1387 1395 1614 +3 1395 1876 1614 +3 1605 1395 1386 +3 1876 1395 1605 +3 1397 1606 1607 +3 1398 1397 1607 +3 1608 1398 1607 +3 1608 1880 1398 +3 1398 1880 1399 +3 1615 1611 1610 +3 1616 1611 1615 +3 1617 1610 1612 +3 1615 1610 1617 +3 1402 1401 1618 +3 1402 1618 1391 +3 1613 1619 1406 +3 1619 1407 1406 +3 1396 1620 1397 +3 1399 1880 1412 +3 1880 1621 1412 +3 1414 1616 1615 +3 1415 1622 1623 +3 1414 1622 1415 +3 1414 1617 1622 +3 1414 1615 1617 +3 1401 1624 1618 +3 1401 1416 1624 +3 1416 1404 1624 +3 1393 1624 1404 +3 1405 1624 1393 +3 1405 1886 1624 +3 1619 1417 1407 +3 1619 1886 1417 +3 1886 1405 1417 +3 1625 1418 1626 +3 1418 1887 1626 +3 1627 1620 1420 +3 1620 1396 1420 +3 1627 1621 1888 +3 1421 1412 1621 +3 1628 1616 1430 +3 1616 1414 1430 +3 1623 1629 1415 +3 1623 1622 1629 +3 1424 1423 1630 +3 1424 1630 1425 +3 1625 1892 1631 +3 1625 1631 1426 +3 1626 1892 1625 +3 1420 1428 1627 +3 1428 1432 1627 +3 1432 1621 1627 +3 1621 1432 1421 +3 1628 1430 1891 +3 1430 1629 1891 +3 1423 1431 1630 +3 1425 1630 1431 +3 814 1433 1893 +3 1634 1636 1635 +3 1434 1437 1635 +3 1636 1637 1635 +3 1434 1635 1637 +3 1637 1903 1434 +3 1441 1635 1437 +3 1434 1903 1641 +3 1434 1641 1638 +3 1448 1635 1441 +3 1640 1638 1641 +3 1639 1638 1642 +3 1642 1638 1640 +3 1641 1642 1640 +3 1639 1642 1643 +3 1445 1639 1643 +3 1445 1643 1645 +3 1645 1643 1644 +3 1644 1643 1908 +3 1908 1649 1644 +3 1644 1649 1645 +3 1651 1448 1454 +3 1448 1451 1454 +3 1646 1645 1649 +3 1647 1645 1646 +3 1647 1648 1452 +3 1454 1456 1651 +3 1647 1646 1649 +3 1912 1648 1647 +3 1648 1909 1650 +3 1651 1456 1458 +3 1912 1647 1649 +3 1913 1650 1909 +3 1648 1650 1652 +3 1648 1652 1457 +3 1913 1914 1650 +3 1650 1914 1652 +3 1653 1651 1459 +3 1651 1458 1459 +3 1457 1652 1654 +3 1654 1653 1459 +3 1653 1460 1656 +3 1654 1652 1655 +3 1654 1655 1461 +3 1655 1652 1914 +3 1464 1656 1460 +3 1461 1655 1463 +3 1466 1656 1464 +3 1657 1658 1655 +3 1655 1658 1463 +3 1463 1658 1465 +3 1466 1468 1656 +3 1922 1658 1657 +3 1465 1658 1659 +3 1658 1922 1660 +3 1658 1660 1659 +3 1231 1660 1661 +3 1660 1231 1659 +3 1662 1231 1661 +3 4051 1234 1663 +3 1666 1664 1665 +3 1666 1665 1667 +3 1667 1665 1668 +3 1669 1666 1667 +3 1471 1666 1669 +3 1669 1667 1668 +3 1471 1669 1670 +3 1669 1668 1670 +3 1670 1668 1671 +3 1670 1671 1673 +3 1673 1671 1672 +3 1672 1951 1673 +3 1673 1951 1674 +3 1673 1674 1473 +3 1676 1677 1947 +3 1951 1954 1675 +3 1674 1951 1675 +3 1675 1954 1476 +3 1675 1476 1674 +3 1476 1954 1678 +3 1677 1676 1956 +3 1677 1956 1478 +3 1681 1679 1680 +3 1245 1681 1479 +3 1476 1678 1480 +3 1480 1678 1685 +3 1682 1677 1478 +3 1682 1955 1677 +3 1956 1486 1478 +3 1478 1483 1682 +3 1483 1967 1955 +3 1483 1955 1682 +3 1686 1687 1683 +3 1683 1687 1688 +3 1684 1683 1689 +3 1689 1683 1688 +3 1479 1681 1493 +3 1245 1694 1963 +3 1685 1965 1488 +3 1480 1685 1488 +3 1687 1686 1691 +3 1691 1686 1690 +3 1688 1687 1692 +3 1692 1687 1691 +3 1692 1689 1688 +3 1486 1956 1504 +3 2279 1494 1686 +3 1492 1686 1494 +3 1690 1686 1492 +3 1693 1690 1492 +3 1693 1691 1690 +3 1689 1692 1693 +3 1693 1692 1691 +3 1493 1681 1697 +3 1703 1695 1965 +3 1488 1965 1696 +3 1696 1965 1695 +3 1488 1696 1498 +3 1494 2279 1502 +3 1693 1497 2278 +3 1689 1693 2278 +3 1696 1695 1698 +3 1698 1695 1704 +3 1498 1696 1699 +3 1699 1696 1698 +3 1498 1699 1700 +3 1956 3036 1504 +3 1707 1694 1701 +3 1702 1694 1707 +3 1709 1694 1702 +3 1704 1695 1703 +3 1969 1698 1704 +3 1699 1698 1969 +3 1970 1699 1969 +3 1970 1705 1699 +3 1699 1705 1700 +3 1700 1705 1498 +3 1701 1503 1706 +3 1707 1701 1706 +3 1702 1707 1708 +3 1709 1702 1711 +3 1711 1702 1708 +3 1703 1710 1704 +3 1710 1969 1704 +3 1705 1970 1971 +3 1708 1707 1711 +3 1711 1707 1706 +3 1972 1709 1711 +3 1705 1971 1712 +3 1503 1711 1706 +3 1972 1711 1713 +3 1968 1985 1710 +3 1712 1985 1507 +3 1712 1710 1985 +3 1719 1711 1503 +3 1714 1711 1719 +3 1713 1711 1714 +3 1972 1713 1714 +3 1983 1972 1714 +3 1504 3036 762 +3 3634 1978 1103 +3 1719 1512 1514 +3 1983 1714 1719 +3 1985 1509 1507 +3 1509 1985 1715 +3 1718 1715 1717 +3 1715 1718 1509 +3 1509 1718 1510 +3 4845 762 3036 +3 1724 1511 1716 +3 1983 1719 1725 +3 1715 2000 1717 +3 1718 1717 1720 +3 1721 1718 1720 +3 1718 1721 1510 +3 1722 1724 1716 +3 2000 1730 1717 +3 1720 1717 1730 +3 1721 1720 1726 +3 1722 1723 1724 +3 1719 1514 1727 +3 1725 1719 1727 +3 1728 1725 1727 +3 1516 1726 1720 +3 1721 1726 1516 +3 1729 1728 1731 +3 1731 1728 1727 +3 1516 1720 1521 +3 1720 1730 1521 +3 1514 1731 1727 +3 2008 1729 1731 +3 2008 1731 1732 +3 1733 1735 1730 +3 1521 1730 1735 +3 1516 1521 1520 +3 1733 1738 1734 +3 1735 1733 1734 +3 1289 1732 1731 +3 2021 1732 1289 +3 1738 1736 1737 +3 1734 1738 1739 +3 1735 1734 1739 +3 1735 1739 1522 +3 2023 1746 1736 +3 1741 1736 1746 +3 1737 1736 1741 +3 1738 1737 1742 +3 1742 1737 1741 +3 1739 1738 1743 +3 1743 1738 1742 +3 2034 1739 1743 +3 1522 1739 2034 +3 1744 2028 1526 +3 2028 1745 1526 +3 1527 1526 1745 +3 1749 2021 1753 +3 1751 1746 2023 +3 1746 2032 1742 +3 1746 1742 1741 +3 1743 1742 2032 +3 1528 2028 1744 +3 1528 1747 2028 +3 1526 1528 1744 +3 1748 1530 1745 +3 1745 1530 1527 +3 1746 1751 1752 +3 1530 1747 1528 +3 1748 1747 1530 +3 1753 2021 1773 +3 1749 1753 1754 +3 1750 2031 1755 +3 1751 1750 1756 +3 1756 1750 1755 +3 1757 1752 1756 +3 1756 1752 1751 +3 1531 1128 3731 +3 1759 1760 1761 +3 1759 1763 1762 +3 1761 1763 1759 +3 1753 1773 1300 +3 1754 1753 1764 +3 2045 1754 1765 +3 1765 1754 1764 +3 1755 2031 1768 +3 1768 2031 1767 +3 1756 1755 1769 +3 1769 1755 1768 +3 1770 1757 1756 +3 1769 1770 1756 +3 2034 1758 1533 +3 1301 3731 1128 +3 1759 1771 1760 +3 1762 1771 1759 +3 1772 1771 1762 +3 1763 1772 1762 +3 1764 1753 1774 +3 1765 1764 1774 +3 1779 1765 1774 +3 2025 1765 1779 +3 2046 2025 1779 +3 1766 1781 1775 +3 1788 1766 1775 +3 1767 1766 1788 +3 1768 1767 1776 +3 1776 1767 1788 +3 1769 1768 1777 +3 1777 1768 1776 +3 1770 1769 1778 +3 1778 1769 1777 +3 1533 1770 1778 +3 4845 5643 930 +3 1753 1534 1774 +3 1775 1781 1782 +3 1788 1775 1782 +3 1778 1777 1776 +3 1533 1778 1776 +3 2056 1779 1783 +3 1784 2056 1783 +3 2066 1785 1780 +3 1781 1780 1786 +3 1786 1780 1785 +3 1787 1781 1786 +3 2067 1782 1787 +3 1787 1782 1781 +3 1542 1789 1779 +3 1789 1783 1779 +3 1790 1783 1789 +3 2066 2060 1791 +3 1791 2060 1544 +3 1785 2066 1791 +3 1792 1785 1791 +3 1798 1785 1792 +3 1793 1785 1798 +3 1786 1785 1794 +3 1794 1785 1793 +3 1787 1786 1795 +3 1795 1786 1794 +3 1795 2067 1787 +3 2074 1796 1788 +3 1536 1797 2081 +3 2081 1537 1536 +3 1783 1790 1539 +3 1784 1783 1543 +3 1792 1791 1798 +3 1794 1793 1799 +3 1799 1793 1798 +3 1795 1794 1799 +3 1796 1800 1801 +3 1788 1796 1801 +3 1788 1801 1312 +3 1806 1807 1797 +3 2080 1540 2081 +3 1540 1537 2081 +3 1798 1791 1550 +3 1799 1798 1545 +3 1801 1800 1328 +3 1809 1805 2421 +3 2080 2091 1540 +3 2091 1810 1540 +3 1548 1784 1543 +3 1815 1784 1548 +3 1544 1784 1815 +3 1550 1791 1544 +3 1545 1798 1550 +3 1809 1812 1813 +3 1809 1816 1812 +3 1816 1809 2421 +3 1541 1321 1814 +3 1546 1811 2109 +3 1544 1815 1560 +3 1553 1552 1824 +3 1812 1553 1813 +3 1824 1813 1553 +3 1555 1553 1812 +3 1555 1812 1816 +3 1546 2121 1321 +3 1321 2121 1814 +3 2110 2446 1817 +3 2446 1835 1817 +3 1819 1820 1818 +3 1821 2128 1822 +3 1822 2128 1821 +3 1564 1824 1552 +3 1564 1823 1824 +3 1825 1813 1824 +3 1816 1831 1555 +3 1826 2120 1575 +3 1348 1833 2110 +3 1817 1348 2110 +3 1835 1578 1817 +3 1818 1820 1827 +3 1828 1827 1820 +3 1829 2128 1569 +3 1821 1569 2128 +3 1829 1570 2128 +3 1821 2128 1569 +3 1569 2128 1570 +3 1571 1830 1823 +3 1830 1824 1823 +3 1573 1572 1831 +3 1573 1831 1824 +3 1573 1824 1830 +3 1825 1824 1831 +3 1831 1572 1555 +3 1826 1574 1832 +3 1575 1574 1826 +3 1577 1576 1833 +3 1577 1833 1348 +3 1834 1578 1835 +3 1836 2140 1837 +3 1837 1838 1836 +3 1839 1818 1827 +3 1828 1840 1827 +3 1840 1839 1827 +3 1842 1841 1828 +3 1841 1840 1828 +3 1845 1843 1844 +3 1845 1846 1590 +3 1844 1846 1845 +3 1569 1847 1829 +3 1570 1847 1581 +3 1829 1847 1570 +3 1849 1832 1574 +3 1848 1832 1849 +3 1849 1574 1366 +3 1576 1582 1833 +3 1835 1851 1834 +3 1834 1851 1852 +3 1853 1583 1854 +3 1853 2140 1583 +3 1584 1583 1836 +3 1836 1583 2140 +3 1584 1838 1585 +3 1584 1836 1838 +3 1585 1838 1855 +3 1853 1854 1838 +3 1855 1838 1854 +3 1839 1856 1865 +3 1839 1840 1856 +3 1841 1587 1856 +3 1841 1856 1840 +3 1841 1842 1595 +3 1857 1843 1845 +3 1858 1843 1857 +3 1589 1588 1857 +3 1589 1857 1845 +3 1590 1589 1845 +3 1846 1859 1590 +3 1860 2149 1861 +3 1860 1862 1863 +3 1861 1862 1860 +3 1378 2485 1849 +3 1378 1849 1366 +3 1868 1597 1850 +3 1384 1851 1582 +3 1852 1851 1384 +3 1591 1852 1384 +3 1834 1852 1591 +3 1593 1854 1583 +3 1593 1864 1854 +3 1864 1855 1854 +3 1585 1864 1592 +3 1855 1864 1585 +3 1865 1856 1587 +3 1858 1857 1588 +3 1858 1588 2152 +3 2152 1588 1874 +3 1859 2153 1590 +3 1860 1866 2149 +3 1863 1866 1860 +3 1867 1866 1863 +3 1378 1868 2485 +3 1868 1378 1597 +3 1864 1593 1869 +3 1592 1864 1870 +3 1864 1869 1870 +3 1865 1587 1600 +3 1865 1600 1878 +3 1871 1602 1595 +3 1871 1595 1872 +3 1872 1595 1842 +3 1590 2153 1873 +3 1874 1873 2153 +3 1603 2154 1875 +3 1593 1614 1869 +3 1870 1869 1876 +3 1876 1869 1614 +3 1870 1876 1605 +3 1606 1877 1878 +3 1600 1606 1878 +3 1872 1878 1877 +3 1872 1877 1871 +3 1871 1877 1879 +3 1602 1871 1880 +3 1871 1879 1880 +3 1874 1609 1873 +3 1611 1873 1609 +3 1611 1881 1873 +3 1881 1590 1873 +3 1590 1881 1612 +3 1391 1882 1603 +3 1603 1882 2154 +3 1603 1875 1619 +3 1875 2157 1619 +3 1397 1877 1606 +3 1397 1883 1877 +3 1883 1879 1877 +3 1880 1879 1884 +3 1884 1879 1883 +3 1881 1616 1885 +3 1611 1616 1881 +3 1612 1885 1617 +3 1881 1885 1612 +3 1391 1618 1882 +3 1886 1882 1618 +3 1886 1619 2157 +3 1397 1620 1883 +3 1620 1888 1883 +3 1888 1884 1883 +3 1880 1884 1888 +3 1880 1888 1621 +3 1885 1616 1889 +3 1617 1890 1622 +3 1617 1885 1890 +3 1885 1889 1890 +3 1886 1618 1624 +3 1620 1627 1888 +3 1616 1628 1889 +3 1628 1891 1889 +3 1891 1890 1889 +3 1622 1891 1629 +3 1890 1891 1622 +3 1631 2160 1418 +3 1631 1892 2160 +3 893 2500 1632 +3 1895 1896 1894 +3 1896 1632 1894 +3 1633 1632 1896 +3 1633 1896 2163 +3 2163 2173 1633 +3 1893 1223 2515 +3 1633 2173 1898 +3 1052 1633 1898 +3 2172 1052 1898 +3 1635 1899 1634 +3 1634 1899 1636 +3 1899 1900 1636 +3 1900 1902 1636 +3 1637 1636 1901 +3 1902 1903 1636 +3 1901 1636 1903 +3 1637 1901 1903 +3 1902 1904 1903 +3 1906 1903 1904 +3 1641 1903 1906 +3 1635 1448 2180 +3 1906 1904 1905 +3 1642 1906 1643 +3 1641 1906 1642 +3 1643 1906 1910 +3 1910 1908 1643 +3 1651 2180 1448 +3 1906 1905 1907 +3 1910 1906 1907 +3 2189 1908 1910 +3 1649 1908 1911 +3 1911 1908 2189 +3 1911 1912 1649 +3 1912 1909 1648 +3 1912 1913 1909 +3 2183 1914 1911 +3 1911 1914 1912 +3 1913 1912 1914 +3 1653 2180 1651 +3 1656 2180 1653 +3 1914 1916 1915 +3 1914 1915 1655 +3 4051 2180 1656 +3 1915 1657 1655 +3 1918 2198 1915 +3 1918 1915 1917 +3 1917 1915 1916 +3 1657 1915 2198 +3 2199 1657 2198 +3 1916 1919 1917 +3 1918 1917 1920 +3 1920 1917 1919 +3 1918 1920 1921 +3 1920 2203 1922 +3 1920 1922 1921 +3 1657 1921 1922 +3 1923 1922 2203 +3 1924 1922 1923 +3 1924 1925 1922 +3 1922 1925 1660 +3 1660 1925 1661 +3 1927 1926 1662 +3 1927 1662 1661 +3 1468 4051 1656 +3 2557 2563 1926 +3 1926 2563 1662 +3 1469 1662 2563 +3 1664 1469 2220 +3 2219 1929 1928 +3 1930 1929 2219 +3 1929 1930 1931 +3 1664 2220 1932 +3 1936 1933 1930 +3 1931 1930 1934 +3 1934 1930 1933 +3 1932 1935 1937 +3 1932 1937 1664 +3 1933 1936 1938 +3 1934 1933 1938 +3 2226 1937 1935 +3 1664 1937 1665 +3 2226 1939 1937 +3 1937 1939 1941 +3 1665 1937 1941 +3 1939 1943 1940 +3 1941 1939 1940 +3 1665 1941 1668 +3 1938 1936 1942 +3 1941 1940 1944 +3 1944 1940 1943 +3 1668 1941 1945 +3 1945 1941 1944 +3 1944 1943 1946 +3 1945 1944 1946 +3 1668 1945 2233 +3 2233 1945 1946 +3 1936 3036 1942 +3 1942 3036 1947 +3 1938 1942 1947 +3 2234 2232 1946 +3 2234 1946 1943 +3 2233 2236 1671 +3 2233 1671 1668 +3 1671 2236 2238 +3 1671 2238 1948 +3 1947 3036 1676 +3 1948 2238 1949 +3 1949 2238 1952 +3 1671 1948 1950 +3 1950 1948 1949 +3 1672 1671 1951 +3 1671 1950 1951 +3 1950 1949 1953 +3 1954 1950 1953 +3 1951 1950 1954 +3 1949 1952 2247 +3 1953 1949 2247 +3 2606 1947 1677 +3 1953 1678 1954 +3 1244 1262 1663 +3 2256 1678 1953 +3 2606 1677 1955 +3 2254 1679 1245 +3 1679 1958 1680 +3 1245 1679 1681 +3 2258 1957 1678 +3 2254 1245 1963 +3 2272 2254 1963 +3 1678 1957 1959 +3 1684 2279 1683 +3 1680 1958 1681 +3 2272 1963 1961 +3 2275 1960 2276 +3 1959 2276 1960 +3 1959 1960 1685 +3 1678 1959 1685 +3 1967 1966 1955 +3 1966 2263 1955 +3 1683 2279 1686 +3 1684 1689 2278 +3 1962 1960 2275 +3 1965 1960 1962 +3 1685 1960 1965 +3 1964 1961 1963 +3 2272 1961 1964 +3 1958 1697 1681 +3 1964 1963 1694 +3 2279 1103 1502 +3 1975 2281 1964 +3 1965 1962 1703 +3 1709 1964 1694 +3 1962 2275 1968 +3 1968 2275 1976 +3 1703 1962 1968 +3 2281 1975 1973 +3 1968 1976 1977 +3 1703 1968 1710 +3 1969 1710 1971 +3 1970 1969 1971 +3 1972 1964 1709 +3 1975 1964 1972 +3 1974 1973 1975 +3 1710 1712 1971 +3 1968 1977 1985 +3 1979 1975 1972 +3 1974 1975 1991 +3 1973 1974 1991 +3 2288 1973 1991 +3 2287 2293 1976 +3 1977 1976 1980 +3 1981 1977 1980 +3 1982 1977 1981 +3 1977 1982 1985 +3 1983 1979 1972 +3 1975 1979 1983 +3 1991 1975 1983 +3 1976 2293 1986 +3 1980 1976 1984 +3 1984 1976 1986 +3 1981 1980 1984 +3 1982 1981 1984 +3 2296 2288 1991 +3 1982 1984 1985 +3 1987 1511 1978 +3 2304 1988 2300 +3 1986 2300 1988 +3 2000 1986 1988 +3 1715 1984 2000 +3 1984 1986 2000 +3 1985 1984 1715 +3 1511 1989 1990 +3 1716 1511 1990 +3 1987 1989 1511 +3 1992 2296 1991 +3 1988 2304 1995 +3 1990 1994 1716 +3 1983 1725 1991 +3 1992 1991 2008 +3 2330 2004 2304 +3 1995 2304 2004 +3 2305 2001 1996 +3 1993 2305 1997 +3 1997 2305 1996 +3 1723 1722 1998 +3 1723 1998 1993 +3 1723 1993 1997 +3 1716 1999 1722 +3 1722 1999 1998 +3 1999 1716 1994 +3 1991 1725 1728 +3 2008 1991 1728 +3 1988 1995 2000 +3 1996 2001 2002 +3 2003 1996 2002 +3 1724 2003 2369 +3 1724 1997 2003 +3 1997 1996 2003 +3 1723 1997 1724 +3 1729 2008 1728 +3 2000 1995 2005 +3 2005 1995 2004 +3 2006 2000 2005 +3 2007 2000 2006 +3 1730 2000 2007 +3 2002 2001 2369 +3 2003 2002 2369 +3 2321 1992 2008 +3 2004 2330 2009 +3 2013 2004 2009 +3 2005 2004 2010 +3 2010 2004 2013 +3 2006 2005 2011 +3 2011 2005 2010 +3 2007 2006 2012 +3 2012 2006 2011 +3 1730 2007 2012 +3 2331 2686 1740 +3 2013 2014 2010 +3 2011 2010 2015 +3 2015 2010 2014 +3 2012 2011 1733 +3 1733 2011 2015 +3 1730 2012 1733 +3 2016 2014 2013 +3 2015 2014 1733 +3 1733 2014 2016 +3 2706 2355 2001 +3 2321 2008 2019 +3 2019 2008 1732 +3 2017 2321 2019 +3 2018 1736 2016 +3 1733 1736 1738 +3 1733 2016 1736 +3 2363 2331 1740 +3 2017 2019 2020 +3 2021 2017 2020 +3 2366 2363 1740 +3 2001 2355 2369 +3 2021 2019 1732 +3 2020 2019 2021 +3 2017 2021 1749 +3 2341 2017 2024 +3 2024 2017 1749 +3 2022 2027 2018 +3 2360 2022 2018 +3 2027 2023 2018 +3 2018 2023 1736 +3 1740 2367 2366 +3 2022 2360 2030 +3 2027 2022 2026 +3 1531 2367 1740 +3 1745 2028 2377 +3 2029 1745 2377 +3 2025 2024 1749 +3 2030 2371 2036 +3 2022 2030 2031 +3 2031 2030 2038 +3 2026 2022 2031 +3 1750 2026 2031 +3 1751 2027 1750 +3 2027 2026 1750 +3 2023 2027 1751 +3 2032 2033 1743 +3 2033 2034 1743 +3 2035 1748 2029 +3 2029 1748 1745 +3 2045 2025 1749 +3 1781 2036 2371 +3 2038 2030 2037 +3 1746 1752 2039 +3 2039 2040 1746 +3 1746 2040 2032 +3 2040 2041 2032 +3 2033 2032 2042 +3 2042 2032 2041 +3 2034 2042 1758 +3 2034 2033 2042 +3 2035 1747 1748 +3 2035 2044 1747 +3 2044 2043 1747 +3 1754 2045 1749 +3 2025 2045 1765 +3 2037 2030 2047 +3 2047 2030 2036 +3 2038 2037 1766 +3 1766 2037 2047 +3 2048 2038 1766 +3 2031 2038 2048 +3 1752 1757 2049 +3 1752 2049 2050 +3 1752 2050 2039 +3 2050 2051 2052 +3 2050 2052 2039 +3 2039 2052 2040 +3 2041 2040 2053 +3 2053 2040 2052 +3 1758 2042 2053 +3 2042 2041 2053 +3 1761 1760 2054 +3 1761 2055 1763 +3 2054 2055 1761 +3 1784 2046 2056 +3 1781 2379 2061 +3 2061 2379 2057 +3 1766 2036 1781 +3 2047 2036 1766 +3 1767 2048 1766 +3 2031 2048 1767 +3 2058 2049 1757 +3 2058 1757 2059 +3 2059 1757 1770 +3 2051 2058 2059 +3 2052 2051 2059 +3 2052 2059 1533 +3 1758 2052 1533 +3 2053 2052 1758 +3 1760 1771 2054 +3 1772 2054 1771 +3 2055 2054 1772 +3 1763 2055 1772 +3 2056 2046 1779 +3 2046 1784 2060 +3 1533 2059 1770 +3 2057 2060 2066 +3 2061 2057 2062 +3 2062 2057 2066 +3 1781 2061 1780 +3 1780 2061 2062 +3 2063 2064 2065 +3 2060 1784 1544 +3 1780 2062 2066 +3 2067 2071 1782 +3 1782 2071 1788 +3 2071 2074 1788 +3 2063 2068 2064 +3 2067 1795 2069 +3 2071 2067 2070 +3 2070 2067 2069 +3 2411 2072 2071 +3 2073 2071 2072 +3 2074 2071 2073 +3 2075 2094 2076 +3 2075 2077 2078 +3 2076 2077 2075 +3 2077 2402 2078 +3 1797 2080 2081 +3 2079 2080 1797 +3 2082 2419 2407 +3 1795 1799 2069 +3 1808 2069 1799 +3 2070 2069 1808 +3 2070 1808 2071 +3 2072 2071 1328 +3 2071 1808 1328 +3 1800 2072 1328 +3 2073 2072 1800 +3 2074 2073 1800 +3 1796 2074 1800 +3 2083 2094 2075 +3 1802 1803 2083 +3 1802 2083 2078 +3 2078 2083 2075 +3 1802 2402 1804 +3 1802 2078 2402 +3 2084 2085 1805 +3 2086 2088 2089 +3 2089 1807 1806 +3 2089 2088 1807 +3 2088 2090 1807 +3 1797 1807 2090 +3 1797 2090 2091 +3 1797 2091 2079 +3 2429 2091 2090 +3 2080 2079 2091 +3 2092 2419 2082 +3 2092 2082 2407 +3 4422 1524 1287 +3 2093 2094 2083 +3 1803 2093 2083 +3 1803 2095 2093 +3 1804 1803 1802 +3 2084 1805 1809 +3 2096 2087 2086 +3 2089 2097 2086 +3 2097 2096 2086 +3 1806 1810 2098 +3 1806 2098 2089 +3 2089 2098 2097 +3 2091 2429 2099 +3 2091 2099 1810 +3 2098 1810 2099 +3 2443 1538 1541 +3 1811 2443 2108 +3 1811 1538 2443 +3 2101 2419 2092 +3 2124 2419 2101 +3 2102 2101 2092 +3 2103 2094 2093 +3 2104 2094 2103 +3 2105 2093 2095 +3 2103 2093 2105 +3 1813 2106 2107 +3 1813 2107 1809 +3 1809 2107 2084 +3 2099 2097 2098 +3 1814 2443 1541 +3 2108 2109 1811 +3 2447 2123 2100 +3 2123 2111 2100 +3 2124 2101 2112 +3 2101 2102 2112 +3 2113 2112 2102 +3 2102 2125 2113 +3 2103 2116 2104 +3 2105 2116 2103 +3 2106 1813 2117 +3 2118 2107 2106 +3 2118 2106 2119 +3 2119 2106 2117 +3 2119 1816 2118 +3 1816 2421 2118 +3 2819 1826 1832 +3 2120 1826 2819 +3 1546 2109 2130 +3 2446 2131 1835 +3 2111 2122 2458 +3 2111 2123 2122 +3 1819 1818 2124 +3 1819 2124 2112 +3 1819 2112 2113 +3 2113 2125 1819 +3 1819 2125 1820 +3 2114 2115 2126 +3 2127 2126 2115 +3 2117 1825 2129 +3 1813 1825 2117 +3 1816 2129 1831 +3 1816 2119 2129 +3 2119 2117 2129 +3 1546 2130 2121 +3 2123 2458 2122 +3 2132 2458 2123 +3 1818 2133 2124 +3 2142 1828 1820 +3 2142 1820 2125 +3 2127 2134 2126 +3 2134 2135 2136 +3 2134 2127 2135 +3 2137 2135 2127 +3 2135 2138 2139 +3 2137 2138 2135 +3 2138 2144 2139 +3 1825 1831 2129 +3 1850 1575 2120 +3 1837 2140 1838 +3 2133 1818 1839 +3 2133 1839 2146 +3 1842 2142 2141 +3 1842 1828 2142 +3 2136 2135 2143 +3 2135 2139 2143 +3 1843 2144 1844 +3 1843 2139 2144 +3 1843 2143 2139 +3 1844 2144 2480 +3 1846 1844 2480 +3 1832 1848 2484 +3 1582 2476 1833 +3 2476 1851 2131 +3 2476 1582 1851 +3 1835 2131 1851 +3 1838 2140 1853 +3 1865 2145 2146 +3 1839 1865 2146 +3 2142 2146 2145 +3 2142 2145 2141 +3 2141 2145 2151 +3 2141 2151 1842 +3 2143 1843 1858 +3 2143 1858 2479 +3 2148 2479 1858 +3 1846 2148 2147 +3 1846 2147 2153 +3 1846 2153 1859 +3 1861 2150 1862 +3 2149 2150 1861 +3 1862 2150 1863 +3 1849 2484 1848 +3 1849 2485 2484 +3 1850 2156 1868 +3 1865 2151 2145 +3 1842 2151 1872 +3 2152 2148 1858 +3 2152 2147 2148 +3 2153 2147 2152 +3 1867 2149 1866 +3 2150 2149 1867 +3 1863 2150 1867 +3 2154 2485 1868 +3 2154 1868 2155 +3 1868 2156 2155 +3 1878 2151 1865 +3 1878 1872 2151 +3 2153 2152 1874 +3 2155 1875 2154 +3 2155 2156 1875 +3 2156 2157 1875 +3 2490 2489 1882 +3 2489 2154 1882 +3 1882 1886 2490 +3 2158 2490 1886 +3 2157 2158 1886 +3 2492 2493 1887 +3 1887 2493 1626 +3 2159 1887 1418 +3 1418 2160 2159 +3 1892 2889 2160 +3 2889 1892 1626 +3 2500 2906 1632 +3 1894 2162 1895 +3 2162 1896 1895 +3 2163 1896 2507 +3 2163 2508 2164 +3 2507 2508 2163 +3 2508 2165 2164 +3 1897 2166 2161 +3 2167 1897 2161 +3 3403 1893 2515 +3 1894 1632 2504 +3 1632 2168 2504 +3 2504 2162 1894 +3 2169 2163 2164 +3 2169 2173 2163 +3 2165 2169 2164 +3 1897 2171 2166 +3 1897 2170 2171 +3 1897 2167 2170 +3 2170 2167 2172 +3 2161 2172 2167 +3 2172 2161 2515 +3 1898 2173 2915 +3 2171 2170 1898 +3 2171 1898 2915 +3 2172 1898 2170 +3 1052 2515 1223 +3 2172 2515 1052 +3 1635 2174 1899 +3 1899 2174 1900 +3 2174 2175 1900 +3 1902 1900 2175 +3 1902 1905 1904 +3 1905 2176 1907 +3 1907 2176 2177 +3 2178 1907 2177 +3 1910 1907 2179 +3 1907 2178 2179 +3 2181 2182 2176 +3 2176 2182 2177 +3 2521 2178 2182 +3 2182 2178 2177 +3 2178 2521 2179 +3 2191 2179 2521 +3 1910 2191 2189 +3 2179 2191 1910 +3 2184 2182 2181 +3 2185 2182 2184 +3 2521 2182 2186 +3 2186 2182 2185 +3 2521 2186 2191 +3 2189 2187 1911 +3 1911 2187 2183 +3 2187 2188 2183 +3 2183 2188 1914 +3 2184 2181 2525 +3 2185 2184 2525 +3 2523 2185 2525 +3 2186 2185 2523 +3 2187 2189 2190 +3 2188 2187 2190 +3 2188 2190 1914 +3 2191 2192 2189 +3 2190 2189 2193 +3 2193 2189 2192 +3 2196 2190 2193 +3 1914 2190 2196 +3 2192 2191 2194 +3 2194 2193 2192 +3 2194 2196 2193 +3 2196 2194 2195 +3 2196 2197 1914 +3 1914 2197 2200 +3 1914 2200 1916 +3 2196 2200 2197 +3 1916 2200 1919 +3 1921 2198 1918 +3 2199 2198 1921 +3 1657 2199 1921 +3 1919 2200 2201 +3 1919 2202 1920 +3 2201 2202 1919 +3 1920 2202 2203 +3 2201 2200 2204 +3 2202 2201 2203 +3 2201 2204 2203 +3 2203 2204 1923 +3 1923 2205 1924 +3 2962 1924 2205 +3 2962 2546 1924 +3 1925 1924 2546 +3 2204 2206 2207 +3 1923 2204 2208 +3 2208 2204 2207 +3 2209 2962 2205 +3 2209 2205 2208 +3 2208 2205 1923 +3 1925 2546 1661 +3 2546 2549 1661 +3 2208 2207 2210 +3 1926 2209 2208 +3 1926 2208 2210 +3 1927 2549 1926 +3 1926 2549 2209 +3 2549 1927 1661 +3 2210 2207 2557 +3 2210 2557 1926 +3 2574 2563 2562 +3 2563 2574 1469 +3 2213 2212 2217 +3 2212 2213 2211 +3 2211 2213 2214 +3 2214 2212 2211 +3 1469 2574 2220 +3 2215 2213 2217 +3 2214 2213 2216 +3 2216 2213 2215 +3 2216 2572 2214 +3 2215 2217 1928 +3 1928 2218 2215 +3 2218 2216 2215 +3 2218 2572 2216 +3 2217 2219 1928 +3 2218 1929 1931 +3 1928 1929 2218 +3 1931 2572 2218 +3 1930 2219 2217 +3 2220 2226 1932 +3 1930 2217 1936 +3 2220 2574 2221 +3 1932 2226 2222 +3 1932 2222 1935 +3 2220 2221 2224 +3 2224 2221 2223 +3 2226 2220 2225 +3 2225 2220 2224 +3 2222 2226 1935 +3 2224 2223 2227 +3 2225 2224 2228 +3 2228 2224 2227 +3 2226 2225 1939 +3 1939 2225 2228 +3 2227 2579 2230 +3 2227 2230 2228 +3 1943 1939 2230 +3 1939 2228 2230 +3 1934 1938 2587 +3 2582 2229 2579 +3 2230 2579 2229 +3 2582 2231 2229 +3 2229 2231 2234 +3 2230 2229 2234 +3 1943 2230 2234 +3 2232 2233 1946 +3 2587 1938 1947 +3 2234 2235 2232 +3 2235 2236 2232 +3 2233 2232 2236 +3 2234 2231 2237 +3 2234 2237 2240 +3 2235 2234 2238 +3 2238 2234 2240 +3 2236 2235 2238 +3 2240 2237 2239 +3 2237 2231 2245 +3 2245 2231 2586 +3 2239 2237 2245 +3 2240 2239 2241 +3 2238 2240 2244 +3 2244 2240 2241 +3 2239 2245 2242 +3 2241 2239 2242 +3 2244 2241 2242 +3 2238 2244 1952 +3 2242 2245 2243 +3 2246 2242 2243 +3 2244 2242 2246 +3 2587 1947 2606 +3 2245 2586 2249 +3 2244 2246 2250 +3 1952 2244 2247 +3 2244 2250 2247 +3 2247 2248 1953 +3 2243 2245 2249 +3 2246 2243 2250 +3 2250 2243 2249 +3 2247 2250 2251 +3 2252 2247 2251 +3 2248 2247 2253 +3 2253 2247 2252 +3 2248 2253 1953 +3 1676 3036 1956 +3 2250 2249 2605 +3 2615 2250 2605 +3 2251 2250 2615 +3 2251 2615 2255 +3 2251 2255 2252 +3 2253 2252 2256 +3 2256 2252 2255 +3 2253 2256 1953 +3 2258 2255 2615 +3 2256 2255 2258 +3 1955 2257 2606 +3 2254 2601 1679 +3 2256 2258 1678 +3 1679 2601 1958 +3 1958 2601 2265 +3 2615 2261 2258 +3 2258 2261 2262 +3 2257 1955 2263 +3 2259 2254 2272 +3 2272 2273 2260 +3 2260 2273 2266 +3 2274 2260 2266 +3 2267 2268 2269 +3 2262 2261 2269 +3 2258 2262 2270 +3 2258 2270 1957 +3 2264 2279 2271 +3 2279 1684 2271 +3 2278 2271 1684 +3 2266 2273 2274 +3 2262 2269 2275 +3 2270 2262 2276 +3 2276 2262 2275 +3 1957 2276 1959 +3 2270 2276 1957 +3 2274 2273 2272 +3 2269 2268 2280 +3 2263 1966 2277 +3 2272 1964 2274 +3 2268 2267 2280 +3 2275 2269 2280 +3 1966 2648 2277 +3 2280 2267 2653 +3 1958 2265 1697 +3 2281 2274 1964 +3 1967 2648 1966 +3 2288 2274 2281 +3 2275 2280 2282 +3 2279 3634 1103 +3 2282 2280 2283 +3 2275 2282 2287 +3 2275 2287 1976 +3 2288 2281 1973 +3 2288 2658 2274 +3 2282 2283 2285 +3 2285 2283 2284 +3 2287 2282 2286 +3 2286 2282 2285 +3 2290 2284 2662 +3 2284 2290 2285 +3 2286 2285 2291 +3 2291 2285 2290 +3 2287 2286 2292 +3 2292 2286 2291 +3 2287 2292 2293 +3 3634 2294 1978 +3 1978 2294 2295 +3 2289 2288 2297 +3 2299 2289 2297 +3 2293 2292 2291 +3 2295 2294 2301 +3 2288 2296 2303 +3 2297 2288 2303 +3 2299 2297 2298 +3 2291 2290 2300 +3 2293 2300 1986 +3 2291 2300 2293 +3 2688 2307 2668 +3 2668 2307 2676 +3 2295 2301 2302 +3 1978 2302 1987 +3 2295 2302 1978 +3 1992 2303 2296 +3 2298 2297 2313 +3 2299 2298 2313 +3 2691 2299 2313 +3 2300 2685 2304 +3 2307 2688 2305 +3 2305 2688 2706 +3 2306 2307 2305 +3 2302 2301 3172 +3 3172 1987 2302 +3 1989 1987 3172 +3 2297 2303 2308 +3 2313 2297 2321 +3 2321 2297 2308 +3 2305 2706 2001 +3 2306 2305 1993 +3 1998 2306 1993 +3 1998 2310 2306 +3 2310 2307 2306 +3 2310 2317 2307 +3 1990 1989 1994 +3 1994 1989 3172 +3 1992 2308 2303 +3 2321 2308 1992 +3 2691 2313 2314 +3 2328 2315 2309 +3 2316 2309 2315 +3 2304 2309 2316 +3 2310 1998 1999 +3 2310 1999 2317 +3 1999 1994 2318 +3 2324 1994 3172 +3 2318 1994 2324 +3 2312 2320 2311 +3 2320 2319 2311 +3 2339 2315 2328 +3 2304 2316 2330 +3 2330 2316 2322 +3 1999 2323 2317 +3 2318 2323 1999 +3 2324 2323 2318 +3 2320 2325 2319 +3 2313 2321 2326 +3 2314 2313 2333 +3 2333 2313 2326 +3 2334 2314 2333 +3 2327 2314 2334 +3 2315 2339 2345 +3 2316 2315 2345 +3 2329 2316 2345 +3 2322 2316 2330 +3 2330 2316 2329 +3 2326 2321 2017 +3 2333 2326 2332 +3 2721 2327 2334 +3 2336 2335 2328 +3 2335 2337 2328 +3 2337 2338 2328 +3 2339 2328 2338 +3 2329 2345 2346 +3 2009 2330 2329 +3 2332 2326 2341 +3 2341 2326 2017 +3 2333 2332 2341 +3 2334 2333 2341 +3 2715 2342 2336 +3 2336 2342 2335 +3 2337 2335 2343 +3 2343 2335 2342 +3 2339 2338 2344 +3 2345 2339 2344 +3 2346 2345 2350 +3 2009 2329 2346 +3 2346 2013 2009 +3 2706 2340 2355 +3 2355 2340 2347 +3 2344 2338 2349 +3 2345 2344 2361 +3 2361 2350 2345 +3 2013 2346 2742 +3 2744 2013 2742 +3 2362 2013 2744 +3 2013 2362 2016 +3 2351 2352 2353 +3 2353 2354 2351 +3 2369 2355 2730 +3 2347 2730 2355 +3 2739 2348 2370 +3 2740 2370 2356 +3 2342 2715 2357 +3 2357 2715 2356 +3 2337 2343 2364 +3 2338 2337 2358 +3 2358 2337 2364 +3 2349 2338 2359 +3 2359 2338 2358 +3 2344 2349 2360 +3 2360 2349 2359 +3 2360 2361 2344 +3 2362 2744 2755 +3 2016 2362 2018 +3 2331 2363 2738 +3 2756 2738 2363 +3 2343 2342 2371 +3 2364 2343 2371 +3 2359 2358 2030 +3 2360 2359 2030 +3 2361 2360 2365 +3 2365 2018 2755 +3 2362 2755 2018 +3 2366 2756 2363 +3 2348 2334 2341 +3 2370 2348 2341 +3 2357 2356 2371 +3 2342 2357 2371 +3 2358 2364 2030 +3 2030 2364 2371 +3 2018 2365 2360 +3 2761 2366 2367 +3 1531 2368 2367 +3 2372 2373 2374 +3 2374 2375 2372 +3 2025 2341 2024 +3 2372 2376 2373 +3 2375 2376 2372 +3 2046 2341 2025 +3 2370 2341 2046 +3 2356 2370 2046 +3 2028 1747 2767 +3 2377 2028 2767 +3 2035 2377 2378 +3 2035 2029 2377 +3 2371 2356 2046 +3 2379 2371 2046 +3 2381 2380 1747 +3 2380 2767 1747 +3 1747 2043 2381 +3 2378 2043 2044 +3 2381 2043 2378 +3 2035 2378 2044 +3 2379 2046 2057 +3 1781 2371 2379 +3 2382 2384 2385 +3 2385 2050 2049 +3 2385 2384 2050 +3 2384 2386 2050 +3 2386 2051 2050 +3 2381 2387 2380 +3 2388 2387 2381 +3 2378 2388 2381 +3 2389 2383 2382 +3 2390 2383 2389 +3 2385 2391 2382 +3 2391 2389 2382 +3 2049 2058 2392 +3 2049 2392 2385 +3 2385 2392 2391 +3 2058 2789 2392 +3 2058 2051 2789 +3 2051 2386 2789 +3 2057 2046 2060 +3 2389 2393 2390 +3 2391 2393 2389 +3 2789 2391 2392 +3 2394 2398 2395 +3 2065 2799 2396 +3 2064 2799 2065 +3 2396 2063 2065 +3 2395 2397 2394 +3 2398 2397 2395 +3 2397 2399 2400 +3 2398 2399 2397 +3 2399 2401 2400 +3 2077 2814 3259 +3 3259 2402 2077 +3 2064 2403 2799 +3 2068 2403 2064 +3 2396 2404 2063 +3 2063 2404 2068 +3 2068 2404 2403 +3 2396 2418 2404 +3 2801 2406 2405 +3 2394 2397 2408 +3 2409 2408 2397 +3 2409 2400 2410 +3 2409 2397 2400 +3 2401 2410 2400 +3 2076 2813 2412 +3 2094 2813 2076 +3 2814 2077 2412 +3 2412 2077 2076 +3 2804 2815 2402 +3 2413 2414 2415 +3 2817 2414 2413 +3 2404 2417 2403 +3 2417 2404 2416 +3 2418 2416 2404 +3 2405 2406 2419 +3 2407 2419 2406 +3 2072 2411 2071 +3 2420 2813 2094 +3 1804 2815 2827 +3 2402 2815 1804 +3 2084 2829 2085 +3 1805 2085 2817 +3 2085 2829 2817 +3 2817 2413 1805 +3 2421 1805 2413 +3 2421 2413 2415 +3 2118 2421 2415 +3 2422 2424 2425 +3 2423 2424 2422 +3 2425 2424 2426 +3 2426 2086 2087 +3 2426 2424 2086 +3 2424 2428 2086 +3 2427 2088 2428 +3 2088 2086 2428 +3 2088 2429 2090 +3 2427 2429 2088 +3 2416 2430 2417 +3 2433 2434 2824 +3 2824 2435 2433 +3 2844 2092 2407 +3 2420 2094 2436 +3 2095 1803 2437 +3 2095 2437 2420 +3 2095 2420 2436 +3 2827 2437 1804 +3 2437 1803 1804 +3 2422 2425 2440 +3 2426 2440 2425 +3 2087 2096 2440 +3 2426 2087 2440 +3 2441 2427 2428 +3 2429 2427 2442 +3 2442 2427 2441 +3 2429 2442 2099 +3 2444 2445 2833 +3 2432 2444 2833 +3 2432 2431 2444 +3 2431 2110 2444 +3 2446 2110 2431 +3 2100 2434 2433 +3 2100 2111 2434 +3 2433 2435 2447 +3 2433 2447 2100 +3 2864 2419 2124 +3 2102 2092 2844 +3 2436 2104 2448 +3 2094 2104 2436 +3 2095 2448 2105 +3 2436 2448 2095 +3 2829 2107 2118 +3 2829 2084 2107 +3 2853 2438 1832 +3 2819 1832 2438 +3 2096 2839 2440 +3 2441 2839 2097 +3 2839 2096 2097 +3 2099 2441 2097 +3 2442 2441 2099 +3 2443 1814 2449 +3 2108 2443 2449 +3 2109 2108 2450 +3 2450 2854 2109 +3 2110 1833 2445 +3 2110 2445 2444 +3 2131 2446 2457 +3 2111 2451 2452 +3 2123 2843 2451 +3 2123 2447 2843 +3 2452 2451 2843 +3 2102 2453 2125 +3 2102 2844 2453 +3 2849 2455 2114 +3 2454 2455 2849 +3 2115 2114 2456 +3 2114 2455 2456 +3 2104 2116 2448 +3 2105 2448 2116 +3 2819 2439 2120 +3 2109 2467 2130 +3 2467 2109 2854 +3 2458 2451 2111 +3 2123 2451 2132 +3 2132 2451 2458 +3 2849 2114 2459 +3 2114 2126 2459 +3 2115 2456 2470 +3 2127 2115 2470 +3 2462 2463 2464 +3 2464 2465 2462 +3 1832 2868 2853 +3 2130 2466 2121 +3 2467 2466 2130 +3 2133 2864 2124 +3 2133 2468 2469 +3 2133 2469 2864 +3 2468 2125 2469 +3 2468 2142 2125 +3 2134 2459 2126 +3 2134 2136 2470 +3 2137 2127 2470 +3 2137 2471 2138 +3 2470 2471 2137 +3 2138 2471 2144 +3 2472 2461 2460 +3 2473 2472 2460 +3 2474 2475 2463 +3 2483 2462 2465 +3 2483 2474 2462 +3 2463 2462 2474 +3 2439 1850 2120 +3 2476 2131 2457 +3 2146 2468 2133 +3 2146 2477 2468 +3 2142 2468 2477 +3 2143 2470 2136 +3 2143 2478 2470 +3 2478 2471 2470 +3 2144 2471 2479 +3 2471 2478 2479 +3 2479 2480 2144 +3 2473 2481 2472 +3 2481 2865 2472 +3 2481 2473 2482 +3 2474 2483 2475 +3 2868 1832 2484 +3 423 4373 893 +3 2142 2477 2146 +3 2479 2478 2143 +3 2480 2479 2148 +3 1846 2480 2148 +3 2865 2481 2487 +3 2482 2488 2481 +3 2488 2487 2481 +3 2439 2156 1850 +3 2489 2485 2154 +3 2489 2490 2158 +3 2156 2486 2157 +3 2486 2158 2157 +3 2881 2491 2492 +3 2493 2492 2491 +3 2492 1887 2881 +3 2493 2889 1626 +3 2494 2496 2497 +3 2495 2496 2494 +3 2496 2498 2497 +3 814 1893 4422 +3 2159 2881 1887 +3 2499 2881 2159 +3 2160 2499 2159 +3 2499 2160 2889 +3 2494 2497 2892 +3 2892 2497 2498 +3 893 4373 2500 +3 2900 2906 2500 +3 2161 2501 2901 +3 2901 2502 2161 +3 2899 2162 2904 +3 2899 1896 2162 +3 2507 1896 2899 +3 2501 2161 2503 +3 1632 2906 2910 +3 2910 2168 1632 +3 2505 2162 2506 +3 2505 2903 2162 +3 2904 2162 2903 +3 2161 2166 2503 +3 2166 2512 2503 +3 2161 2502 2515 +3 2515 2502 3945 +3 2506 2162 2504 +3 2506 2504 2513 +3 2513 2509 2506 +3 2510 2506 2509 +3 2510 2511 2507 +3 2508 2507 2511 +3 2508 2511 2169 +3 2165 2508 2169 +3 2171 2512 2166 +3 2510 2509 2513 +3 2514 2510 2513 +3 2511 2510 2514 +3 2169 2511 2514 +3 2173 2169 2514 +3 2512 2171 2915 +3 2915 2173 2514 +3 1635 2919 2174 +3 2175 2174 2518 +3 2919 2517 2174 +3 2517 2518 2174 +3 2175 2518 1902 +3 2519 2518 2517 +3 2518 2519 1902 +3 2519 2520 1902 +3 1902 2520 1905 +3 1905 2520 2176 +3 2520 2522 2176 +3 2181 2176 2522 +3 2181 2522 2525 +3 2186 2523 2191 +3 2523 2525 2524 +3 2191 2523 2531 +3 2523 2524 2531 +3 2526 2525 2522 +3 2527 2524 2525 +3 2191 2531 2194 +3 2528 2525 2526 +3 2529 2528 2526 +3 2530 2527 2525 +3 2528 2530 2525 +3 2532 2533 2194 +3 2532 2194 2531 +3 2943 2194 2533 +3 2195 2194 2943 +3 2531 2534 2535 +3 2532 2531 2536 +3 2536 2531 2535 +3 2536 2537 2532 +3 2538 2195 2943 +3 2946 2538 2943 +3 2196 2195 2538 +3 2948 2535 2534 +3 2536 2535 2948 +3 2949 2536 2948 +3 2537 2536 2949 +3 2946 2541 2538 +3 2538 2961 2196 +3 2540 2542 2539 +3 2539 2542 2543 +3 2541 2539 2543 +3 2541 2961 2538 +3 2961 2200 2196 +3 2544 2543 2542 +3 2955 2541 2544 +3 2544 2541 2543 +3 2961 2541 2955 +3 2545 2200 2961 +3 2200 2545 2204 +3 2545 2547 2548 +3 2551 2545 2548 +3 2204 2545 2551 +3 2206 2204 2551 +3 2209 2964 2962 +3 2546 2964 2549 +3 2548 2547 2551 +3 2551 2554 2206 +3 2554 2207 2206 +3 2964 2209 2549 +3 2551 2547 2552 +3 2553 2551 2552 +3 2554 2551 2553 +3 2977 2207 2554 +3 2550 2560 2547 +3 2552 2547 2560 +3 2553 2552 2555 +3 2554 2553 2556 +3 2556 2553 2555 +3 2557 2554 2556 +3 2977 2557 2207 +3 2555 2552 2561 +3 2561 2552 2560 +3 2556 2555 2562 +3 2562 2555 2561 +3 2557 2556 2563 +3 2563 2556 2562 +3 2558 2981 2564 +3 2558 2564 2565 +3 2568 2564 2981 +3 2565 2564 2566 +3 2566 2564 2568 +3 2561 2560 2571 +3 2562 2561 2571 +3 2981 3496 2567 +3 2981 2567 2568 +3 2994 2571 2560 +3 2569 2567 3496 +3 2568 2567 2570 +3 2570 2567 2569 +3 2572 2566 2568 +3 2570 2572 2568 +3 2212 2569 3496 +3 2570 2569 2572 +3 2572 2569 2212 +3 2574 2571 2573 +3 2571 2574 2562 +3 2212 3496 2217 +3 2212 2214 2572 +3 2997 2573 2571 +3 2217 3496 1936 +3 3004 2573 2997 +3 1262 4051 1663 +3 2572 1931 4052 +3 2574 2575 2221 +3 1931 1934 4052 +3 2575 2574 2223 +3 2575 2223 2221 +3 2576 2573 2578 +3 2574 2573 2576 +3 2223 2574 2577 +3 2577 2574 2576 +3 2223 2577 2227 +3 4052 1934 2587 +3 2582 2576 2578 +3 2577 2576 2582 +3 2577 2582 2227 +3 2580 2578 3004 +3 2582 2578 2581 +3 2581 2578 2580 +3 2579 2227 2582 +3 2581 2580 3010 +3 2582 2581 2583 +3 3010 2580 2585 +3 3010 2231 2583 +3 2582 2583 2231 +3 2580 2584 2585 +3 2585 2231 3010 +3 2586 2585 3014 +3 2231 2585 2586 +3 3026 2592 3014 +3 2586 3014 2592 +3 2589 2590 2591 +3 2588 2587 2600 +3 2589 2593 2594 +3 2590 2589 2594 +3 2591 2590 2597 +3 2599 2586 2592 +3 2586 2599 2249 +3 2600 2587 2606 +3 3533 2254 2593 +3 2594 2593 2596 +3 2596 2593 2254 +3 2590 2594 2597 +3 2597 2594 2596 +3 2595 2591 2598 +3 2610 2592 3527 +3 2592 2610 2604 +3 2599 2592 2604 +3 2601 2254 3533 +3 2254 2597 2596 +3 2591 2597 2607 +3 2598 2591 2608 +3 2602 2598 2608 +3 2608 2598 2602 +3 2603 2595 2598 +3 2249 2599 2605 +3 2599 2604 2605 +3 2588 2600 2618 +3 2618 2600 2606 +3 2607 2597 2254 +3 2591 2607 2608 +3 2603 2598 2609 +3 2609 2621 2603 +3 2604 2612 2613 +3 2604 2613 2605 +3 2614 2605 2613 +3 2615 2605 2614 +3 2606 2257 2617 +3 2617 2257 2616 +3 2618 2606 2617 +3 2620 2607 2254 +3 2608 2607 2259 +3 2259 2607 2620 +3 2609 2598 2608 +3 2622 2610 2611 +3 2604 2610 2623 +3 2623 2610 2622 +3 2624 2604 2623 +3 2612 2625 2613 +3 2613 2625 2626 +3 2614 2613 2626 +3 2615 2614 2627 +3 2627 2614 2626 +3 2617 2616 2628 +3 2618 2617 2631 +3 2631 2617 2628 +3 2644 2618 2631 +3 2601 2619 2265 +3 2259 2620 2254 +3 2272 2608 2259 +3 2260 2608 2272 +3 2621 2609 2608 +3 2629 2624 2623 +3 2267 2625 2633 +3 2626 2625 2269 +3 2269 2625 2267 +3 2261 2626 2269 +3 2627 2626 2261 +3 2615 2627 2261 +3 2257 2263 2616 +3 2628 2616 2630 +3 2630 2616 2263 +3 2631 2628 2630 +3 3055 2264 2271 +3 3587 3055 2271 +3 2260 2274 2608 +3 2640 2629 2623 +3 2635 2636 3073 +3 2637 2630 2263 +3 2631 2630 2637 +3 2644 2631 2637 +3 2264 3055 2279 +3 3587 2271 2278 +3 2639 2621 2608 +3 2632 2621 2639 +3 2645 2632 2639 +3 2623 2622 2640 +3 2634 2642 2641 +3 2634 2641 2633 +3 2633 2641 2267 +3 2635 2643 2636 +3 3073 2643 2635 +3 2263 2277 2637 +3 2645 2639 2608 +3 2637 2277 2648 +3 2644 2637 2648 +3 2646 2265 2619 +3 2645 2608 2658 +3 2645 2649 2651 +3 2647 2622 2652 +3 2640 2622 2647 +3 2641 2640 2650 +3 2267 2641 2650 +3 2658 2608 2274 +3 2650 2640 2653 +3 2267 2650 2653 +3 1697 2265 2646 +3 2649 2645 2658 +3 3110 2654 2652 +3 2640 2647 2652 +3 2659 2652 2654 +3 2640 2652 2659 +3 2653 2640 2659 +3 2653 2655 2280 +3 2651 2649 2658 +3 3110 2661 2654 +3 2655 2653 2659 +3 2280 2655 2283 +3 2655 2659 2656 +3 2657 2655 2656 +3 2283 2655 2657 +3 2648 1967 3095 +3 3106 2651 2658 +3 2660 2661 3110 +3 2284 2657 2656 +3 2283 2657 2284 +3 2675 3095 1967 +3 3634 2669 2294 +3 2288 2289 2658 +3 2654 2661 2659 +3 2284 2656 2659 +3 2668 2669 3121 +3 3106 2658 2289 +3 2670 3106 2289 +3 2659 2662 2284 +3 2659 2673 2662 +3 2663 2664 2665 +3 2663 2666 2667 +3 2665 2666 2663 +3 2681 2670 2289 +3 3144 2670 2671 +3 2672 2661 2660 +3 2683 2661 2672 +3 3162 2659 2661 +3 3163 2659 3162 +3 2659 3163 2673 +3 2674 2662 2673 +3 2685 2662 2674 +3 2290 2662 2685 +3 2686 2675 1967 +3 2668 3121 2688 +3 2676 3154 2669 +3 2676 2669 2668 +3 3154 2294 2669 +3 3154 2677 2294 +3 2677 2301 2294 +3 2678 2679 2680 +3 2671 2670 2691 +3 2691 2670 2681 +3 2682 2671 2691 +3 3144 2671 2682 +3 2692 3144 2682 +3 2673 3163 2684 +3 2674 2673 2684 +3 2685 2674 2684 +3 2300 2290 2685 +3 2675 2686 2687 +3 2698 2675 2687 +3 2689 2301 2677 +3 2690 2679 2678 +3 2680 2690 2678 +3 2299 2691 2289 +3 2691 2681 2289 +3 2692 2682 2701 +3 3169 2694 3137 +3 2705 3137 2694 +3 2683 2672 2696 +3 2696 2672 2695 +3 2697 2683 2696 +3 2683 2697 3162 +3 2697 2304 3162 +3 2684 3162 2304 +3 2685 2684 2304 +3 2698 2687 2686 +3 2307 3164 2676 +3 2689 3172 2301 +3 2719 2679 2690 +3 2699 2719 2690 +3 2680 2700 2690 +3 2700 2699 2690 +3 2682 2691 2711 +3 2701 2693 2692 +3 2703 3159 2702 +3 2694 3169 2715 +3 2705 2694 2725 +3 3179 2696 2695 +3 2309 2696 2328 +3 2697 2696 2309 +3 2304 2697 2309 +3 2331 2698 2686 +3 2706 2726 2340 +3 3164 2317 2707 +3 3164 2307 2317 +3 3164 2707 3172 +3 2312 2311 2708 +3 2719 2699 2709 +3 2700 2710 2699 +3 2710 2709 2699 +3 2710 2700 2720 +3 2314 2711 2691 +3 2701 2682 2711 +3 2712 2702 3159 +3 2722 2712 3159 +3 2703 2702 2713 +3 2713 2702 2712 +3 2704 2703 2713 +3 3167 3160 2714 +3 3167 2714 2724 +3 3169 3167 2724 +3 2715 3169 2724 +3 2328 2696 3179 +3 2707 2317 2716 +3 3172 2707 2717 +3 2717 2707 2716 +3 2717 2324 3172 +3 2319 3183 3184 +3 2319 3184 2708 +3 2319 2708 2311 +3 2718 2312 2708 +3 2312 2718 2320 +3 2710 2719 2709 +3 2720 2719 2710 +3 2314 2327 2711 +3 2701 2711 2327 +3 2693 2701 2721 +3 2722 2721 2712 +3 2712 2721 2732 +3 2713 2712 2732 +3 2723 2704 2713 +3 2723 2729 3176 +3 2714 3176 2729 +3 2694 2715 2725 +3 2340 2726 2730 +3 2317 2323 2716 +3 2324 2716 2323 +3 2717 2716 2324 +3 2319 2727 3183 +3 2731 2728 2727 +3 2325 2731 2727 +3 2325 2727 2319 +3 2718 2731 2325 +3 2718 2325 2320 +3 2721 2701 2327 +3 2714 2729 2734 +3 2715 2724 2733 +3 2336 2725 2715 +3 2328 2725 2336 +3 2723 2713 2732 +3 2729 2723 2732 +3 2724 2714 2733 +3 2732 2721 2334 +3 2334 2348 2732 +3 2740 2732 2739 +3 2729 2732 2740 +3 2734 2729 2740 +3 2714 2734 2733 +3 2715 2733 2356 +3 2735 3193 2736 +3 3193 2737 2736 +3 3171 2331 2738 +3 2340 2730 2347 +3 2739 2732 2348 +3 2741 2734 2740 +3 2356 2733 2734 +3 2350 2742 2346 +3 2743 2744 2742 +3 2353 2352 2745 +3 2354 2353 2745 +3 2736 2746 2735 +3 2747 2746 2736 +3 2737 2747 2736 +3 3201 2748 2749 +3 3201 2749 2738 +3 2750 2738 2749 +3 2751 2738 2750 +3 2369 3718 3731 +3 2730 3718 2369 +3 2740 2739 2370 +3 2356 2741 2740 +3 2734 2741 2356 +3 2350 2361 2752 +3 2350 2752 3203 +3 2350 3203 2742 +3 3203 2753 2742 +3 2743 2742 2754 +3 2754 2742 2753 +3 2744 2743 2755 +3 2755 2743 2754 +3 1524 4422 1519 +3 2352 2351 2745 +3 2354 2745 2351 +3 2757 2738 2756 +3 2758 2738 2757 +3 3201 2738 2758 +3 2750 2749 2748 +3 2751 2750 2368 +3 3198 2751 2368 +3 1531 3198 2368 +3 3731 3198 1531 +3 2369 3731 1301 +3 2752 2361 2759 +3 2361 2365 2759 +3 2753 2760 2365 +3 2759 2365 2760 +3 2754 2753 2365 +3 2755 2754 2365 +3 2756 2366 2761 +3 2367 2757 2761 +3 2757 2756 2761 +3 2758 2757 2367 +3 2368 2758 2367 +3 2758 2368 2762 +3 2748 2762 2368 +3 2750 2748 2368 +3 2374 2373 2763 +3 2374 2763 2375 +3 2373 2376 2763 +3 2375 2763 2376 +3 2767 3226 2377 +3 2377 3226 2378 +3 2768 2766 2770 +3 2769 2766 2768 +3 2771 2773 2772 +3 2380 2783 2767 +3 3226 2785 2378 +3 2768 2770 2775 +3 2766 2776 2770 +3 2776 2775 2770 +3 2776 2766 2777 +3 2778 2771 2772 +3 2382 2383 2779 +3 2382 2779 2773 +3 2773 2779 2772 +3 2773 2384 2382 +3 2773 2780 2384 +3 2773 2774 2780 +3 2780 2781 2384 +3 2384 2782 2386 +3 2781 2782 2384 +3 2783 2380 3236 +3 2380 2784 3236 +3 2380 2387 2784 +3 3240 2784 2387 +3 3240 2387 2785 +3 2785 2387 2388 +3 2785 2388 2378 +3 1697 4373 423 +3 3241 2775 2776 +3 2777 3241 2776 +3 2778 2787 3249 +3 2778 2772 2787 +3 2772 2779 2787 +3 2383 2390 2787 +3 2779 2383 2787 +3 2788 2781 2780 +3 3249 2788 2780 +3 2782 2781 2789 +3 2789 2781 2788 +3 2782 2789 2386 +3 2784 2790 3236 +3 3240 2790 2784 +3 2792 2791 2786 +3 2791 2797 2786 +3 2390 3249 2787 +3 2390 2393 3249 +3 3249 2391 2788 +3 3249 2393 2391 +3 2789 2788 2391 +3 2793 2795 2796 +3 2794 2795 2793 +3 2394 2797 2792 +3 2792 2797 2791 +3 2792 2798 2394 +3 2398 2394 2798 +3 2793 2796 2800 +3 2795 2801 2796 +3 2801 2800 2796 +3 2797 2836 3247 +3 2836 2797 2394 +3 2398 2802 2399 +3 2398 2798 2802 +3 2802 2803 2399 +3 2399 2803 2401 +3 2402 3259 2804 +3 2396 2799 2418 +3 2810 2811 2809 +3 2812 2793 2800 +3 2801 2405 2812 +3 2801 2812 2800 +3 2795 2406 2801 +3 2795 2407 2406 +3 2795 2844 2407 +3 2410 2803 2802 +3 2410 2802 2409 +3 2401 2803 2410 +3 2412 2813 2814 +3 2815 2804 2816 +3 2816 2804 2828 +3 2805 2414 2817 +3 2415 2414 2805 +3 2415 2805 2818 +3 2818 2805 2806 +3 2403 2417 2799 +3 2417 2820 2799 +3 2418 2799 2820 +3 2821 2418 2820 +3 2821 2822 2418 +3 2416 2418 2823 +3 2823 2418 2822 +3 2825 2809 2824 +3 2809 2811 2824 +3 2824 2811 2435 +3 2405 3290 2812 +3 2408 2836 2394 +3 2408 2409 2826 +3 2409 3281 2826 +3 2409 2802 3281 +3 2420 2437 2814 +3 2420 2814 2813 +3 2437 2827 2814 +3 2816 2827 2815 +3 2828 2827 2816 +3 2829 2830 2817 +3 2415 2831 2118 +3 2415 2818 2831 +3 2439 2819 2807 +3 3306 2439 2807 +3 3306 2807 2808 +3 2422 3299 2423 +3 2423 3299 2424 +3 2417 2430 2820 +3 2821 2430 2832 +3 2821 2820 2430 +3 2832 2430 2833 +3 2416 2823 2430 +3 2833 2430 2823 +3 2432 2822 2431 +3 2432 2823 2822 +3 2432 2833 2823 +3 2446 2431 3288 +3 2431 2822 3288 +3 3288 2834 2446 +3 2824 2434 2825 +3 3290 2405 2419 +3 2826 2836 2408 +3 2837 2836 2826 +3 3281 2837 2826 +3 2118 2830 2829 +3 2830 2118 2831 +3 2819 2438 2838 +3 2839 3299 2422 +3 2422 2440 2839 +3 2428 3309 2441 +3 2833 2445 2832 +3 3301 2111 2842 +3 3301 2434 2111 +3 2447 2842 2843 +3 2447 2835 2842 +3 2447 2435 2835 +3 3301 2842 2835 +3 2419 3302 3290 +3 3302 2419 2864 +3 2848 3314 2454 +3 3314 3322 2454 +3 2838 2853 3305 +3 2853 2838 2438 +3 2441 3309 2839 +3 3321 2449 1814 +3 3321 2450 2449 +3 2450 2108 2449 +3 2450 2840 2854 +3 2446 2834 2457 +3 2452 2842 2111 +3 2843 2842 2452 +3 2844 2848 2453 +3 2849 2848 2454 +3 2454 3322 2455 +3 2455 3322 2856 +3 2456 2455 2856 +3 2850 2464 2846 +3 2846 2845 2850 +3 2845 2851 2850 +3 2852 2845 2847 +3 2851 2845 2852 +3 1814 2121 3321 +3 3321 2121 2861 +3 2832 2445 3329 +3 3329 2445 1833 +3 2848 2855 2453 +3 2453 2855 2125 +3 2855 2848 3344 +3 2849 3344 2848 +3 2456 2856 2470 +3 2460 2461 2857 +3 2463 2858 2464 +3 2465 2850 2859 +3 2465 2464 2850 +3 2860 2859 2851 +3 2859 2850 2851 +3 2852 2860 2851 +3 2439 3306 2486 +3 2466 2861 2121 +3 2862 2861 2467 +3 2467 2861 2466 +3 2467 2854 2862 +3 2469 2855 2864 +3 2855 2469 2125 +3 2459 3330 2849 +3 2459 2856 3330 +3 2134 2856 2459 +3 2470 2856 2134 +3 2857 2472 2865 +3 2461 2472 2857 +3 2460 2866 2473 +3 2460 2857 2866 +3 2857 2865 2866 +3 2463 2475 2867 +3 2463 2867 2858 +3 2858 2867 3335 +3 2483 2465 2867 +3 3335 2867 2465 +3 2859 3335 2465 +3 2860 3335 2859 +3 2868 2485 2853 +3 1833 2476 3329 +3 2457 2863 2476 +3 2476 2863 3329 +3 2866 2865 2869 +3 2473 2869 2482 +3 2866 2869 2473 +3 2475 2483 2867 +3 2484 2485 2868 +3 2487 2869 2865 +3 2482 2869 2488 +3 2869 2487 2488 +3 2439 2486 2156 +3 2870 2872 2873 +3 2489 3889 2485 +3 2874 3360 3356 +3 2874 2875 2876 +3 3356 2875 2874 +3 2870 2877 2871 +3 2877 2878 2879 +3 2877 2870 2878 +3 2870 2873 2878 +3 3365 2873 2872 +3 2878 2873 3365 +3 2489 2158 3889 +3 2158 2486 3889 +3 2880 3360 2874 +3 2881 3360 2880 +3 2491 2880 2876 +3 2876 2880 2874 +3 2876 2885 2491 +3 2882 2879 2878 +3 3365 2883 2878 +3 2883 2882 2878 +3 2880 2491 2881 +3 2884 2493 2491 +3 2884 2491 2885 +3 2882 2886 3366 +3 2882 2883 2886 +3 2887 2886 2883 +3 2885 2888 2884 +3 2884 2888 2889 +3 2884 2889 2493 +3 2495 2494 2886 +3 2886 2494 3366 +3 2886 2887 2496 +3 2886 2496 2495 +3 2494 3369 3366 +3 2887 2498 2496 +3 2887 2883 2498 +3 2499 2888 2881 +3 2499 2889 2888 +3 2891 2890 2498 +3 2891 2498 2883 +3 2494 2892 3369 +3 2890 2893 2892 +3 2890 2892 2498 +3 2891 2893 2890 +3 3381 2894 2895 +3 2895 2894 2896 +3 2895 2897 3381 +3 2896 2897 2895 +3 3395 4422 1893 +3 1893 3404 3395 +3 2898 2902 3398 +3 2903 2898 3400 +3 2902 2898 2903 +3 3403 3404 1893 +3 2902 3410 3398 +3 2503 2901 2501 +3 2503 2905 2901 +3 2502 2901 2905 +3 2502 2905 3945 +3 2910 2906 2907 +3 2168 2910 2908 +3 3410 2168 2908 +3 2902 2168 3410 +3 2909 2902 2903 +3 2909 2504 2902 +3 2909 3414 2504 +3 2168 2902 2504 +3 2903 2505 2909 +3 2899 3418 2507 +3 2512 2905 2503 +3 2910 2911 2908 +3 2513 2504 3414 +3 2506 2912 2505 +3 2510 2912 2506 +3 2507 2913 2510 +3 2510 2913 2912 +3 2913 2507 3418 +3 2905 2512 2915 +3 3945 2914 2515 +3 2513 3421 2514 +3 3427 3403 2515 +3 3423 2514 3421 +3 3423 2915 2514 +3 2916 3426 2516 +3 2516 1226 3428 +3 2516 3426 2917 +3 2916 2516 3428 +3 2517 2918 2519 +3 2919 2918 2517 +3 2519 2918 2520 +3 2920 2919 2921 +3 2921 2919 1635 +3 2922 2923 3437 +3 2924 2925 2926 +3 2928 2926 2929 +3 2928 2927 2926 +3 2927 2924 2926 +3 2929 2930 2931 +3 2929 2926 2930 +3 2932 2930 2925 +3 2925 2930 2926 +3 2933 2931 2930 +3 2934 2931 2933 +3 2935 2930 2932 +3 2933 2930 2935 +3 2933 2936 2934 +3 2935 2936 2933 +3 2522 3450 2526 +3 2524 2527 2937 +3 2526 3450 2529 +3 3450 2938 2529 +3 2937 2530 2939 +3 2527 2530 2937 +3 2524 2940 2531 +3 2524 2937 2940 +3 2937 2939 2940 +3 2533 3456 2941 +3 2532 3456 2533 +3 2942 2533 2941 +3 2942 2943 2533 +3 2528 2529 2947 +3 2947 2529 2938 +3 2528 2939 2530 +3 2528 2944 2939 +3 2531 2944 2534 +3 2531 2940 2944 +3 2940 2939 2944 +3 2532 2537 3456 +3 3456 2537 2950 +3 2950 2945 2942 +3 2943 2942 2946 +3 2946 2942 2945 +3 2952 2528 2947 +3 2952 2948 2944 +3 2952 2944 2528 +3 2534 2944 2948 +3 2950 2537 2949 +3 2950 2949 2951 +3 2945 2950 2951 +3 2945 2951 2540 +3 2945 2540 2539 +3 2541 2945 2539 +3 2946 2945 2541 +3 3467 3470 2948 +3 2949 2948 2953 +3 2953 2948 3470 +3 2540 2953 2542 +3 2540 2951 2953 +3 2951 2949 2953 +3 3471 2953 3470 +3 2542 3471 2958 +3 2542 2953 3471 +3 2958 2544 2542 +3 2954 2956 2957 +3 3468 2954 2957 +3 2959 2544 2958 +3 2544 2959 2960 +3 2544 2960 2955 +3 2961 2955 2960 +3 2957 2956 2969 +3 2969 2956 2963 +3 3468 2957 2969 +3 2970 3468 2987 +3 2987 3468 2969 +3 2547 2961 2550 +3 2961 2960 2550 +3 2545 2961 2547 +3 2962 2964 2546 +3 3470 2970 2971 +3 2972 3470 2971 +3 2550 2960 2959 +3 2976 2550 2959 +3 3501 2967 2966 +3 3501 2966 2973 +3 2973 2966 2965 +3 2963 2968 2986 +3 2986 2968 2974 +3 2969 2963 2986 +3 2971 2970 2975 +3 2975 2972 2971 +3 2986 2974 2979 +3 2975 2970 2980 +3 2980 2970 2987 +3 2554 2557 2977 +3 2978 2973 2981 +3 2978 2981 2558 +3 2978 2558 2559 +3 2558 2978 2559 +3 2558 3501 2978 +3 3501 2973 2978 +3 2979 2982 2986 +3 2987 2969 2986 +3 2976 2985 2984 +3 2976 2984 2550 +3 2550 2984 2560 +3 2558 2565 3501 +3 2982 2990 2986 +3 2980 2987 2991 +3 2988 2983 2980 +3 2988 2980 2989 +3 2989 2980 2991 +3 2988 2989 3495 +3 2992 2985 3495 +3 2985 2992 2984 +3 2984 2992 2994 +3 2560 2984 2994 +3 2565 2566 3501 +3 2987 2986 2991 +3 2991 2986 2990 +3 3495 2989 2992 +3 2992 2989 2991 +3 2995 2991 2990 +3 2992 2991 3005 +3 3005 2991 2995 +3 2993 2992 3005 +3 2994 2992 2993 +3 2566 2572 3501 +3 2993 3005 2997 +3 2994 2993 2997 +3 2996 2995 3503 +3 3005 2995 2996 +3 2994 2997 2571 +3 2996 3503 2998 +3 3501 2572 4052 +3 3005 2996 2998 +3 2998 3503 2999 +3 3005 2998 2999 +3 2997 3005 3004 +3 3005 2999 3000 +3 3509 3001 3000 +3 3002 3000 3001 +3 3003 3000 3002 +3 3003 3002 3005 +3 3000 3003 3005 +3 3004 2578 2573 +3 3004 3005 2580 +3 3001 3006 3007 +3 3002 3001 3009 +3 3005 3002 3009 +3 3019 3005 3009 +3 2580 3005 3019 +3 2584 2580 3019 +3 2581 3010 2583 +3 3007 3008 3012 +3 3012 3008 3011 +3 3007 3012 3013 +3 3001 3007 3013 +3 3009 3001 3013 +3 2584 3019 3014 +3 2584 3014 2585 +3 3012 3011 3013 +3 3009 3013 3023 +3 3031 3009 3023 +3 3019 3009 3018 +3 3014 3019 3026 +3 3020 3016 3015 +3 3021 3016 3020 +3 3022 3015 3017 +3 3020 3015 3022 +3 2588 4052 2587 +3 3011 3008 3045 +3 3024 3009 3043 +3 3018 3009 3024 +3 3019 3018 3025 +3 3026 3019 3025 +3 3013 3011 3045 +3 3023 3013 3027 +3 3031 3023 3028 +3 3028 3023 3027 +3 3018 3024 3029 +3 3025 3018 3519 +3 2588 3532 4052 +3 2589 3521 3520 +3 2591 3521 2589 +3 2591 3030 3521 +3 3013 3045 3051 +3 3028 3027 3539 +3 3031 3028 3539 +3 3029 3024 3032 +3 3032 3024 3043 +3 3032 3018 3029 +3 3519 3018 3032 +3 3026 3025 2592 +3 3520 2593 2589 +3 3033 2593 3520 +3 2595 3030 2591 +3 2595 3529 3030 +3 3034 3529 2595 +3 3027 3013 3539 +3 3009 3031 3035 +3 3043 3009 3035 +3 3025 3527 2592 +3 3533 2593 3033 +3 2595 3037 3034 +3 2595 3535 3037 +3 3038 3535 2595 +3 3539 3013 3051 +3 3540 3032 3043 +3 2601 3533 3039 +3 2603 3038 2595 +3 2603 3040 3038 +3 3549 3044 3045 +3 3574 3539 3051 +3 3031 3539 3052 +3 3035 3031 3052 +3 3564 3540 3043 +3 3550 2611 3527 +3 3527 2611 2610 +3 2588 2618 3532 +3 3049 3532 2618 +3 2601 3039 2619 +3 2603 2621 3040 +3 3052 3539 3574 +3 3043 3035 3046 +3 2612 3047 3048 +3 2612 2604 3047 +3 3056 3040 2621 +3 3572 3050 3041 +3 3045 3044 3081 +3 3051 3045 3081 +3 3046 3035 3053 +3 3053 3035 3052 +3 3043 3046 3061 +3 3061 3046 3053 +3 3061 3564 3043 +3 3063 2611 3054 +3 2611 3063 2622 +3 2624 3066 2604 +3 3066 3047 2604 +3 2612 2633 2625 +3 2618 2644 3049 +3 3078 2619 3039 +3 3056 2621 2632 +3 3044 3549 3081 +3 3598 3574 3059 +3 3059 3574 3051 +3 3574 3599 3052 +3 3053 3052 3061 +3 3061 3052 3060 +3 3586 3062 3063 +3 3586 3063 3054 +3 2624 2629 3064 +3 3065 3066 3064 +3 3064 3066 2624 +3 3067 2612 3048 +3 3068 3067 3048 +3 2612 3067 2633 +3 3553 3049 3088 +3 2632 3069 3056 +3 3069 2632 3057 +3 2632 3058 3057 +3 2632 3074 3058 +3 3051 3081 3070 +3 3059 3051 3070 +3 3070 3598 3059 +3 3061 3060 3071 +3 3085 3063 3062 +3 2629 2640 3064 +3 3065 3064 3072 +3 3072 3064 2640 +3 3072 2634 3068 +3 3065 3072 3068 +3 2634 2633 3068 +3 3068 2633 3067 +3 2644 2638 3049 +3 3088 3049 2638 +3 2645 3074 2632 +3 3079 3074 2645 +3 3074 3079 3075 +3 3060 3052 3083 +3 3071 3060 3083 +3 3063 3085 2622 +3 3072 2640 2641 +3 2642 3072 2641 +3 2634 3072 2642 +3 2636 2643 3073 +3 2638 2644 3077 +3 3088 2638 3087 +3 3087 2638 3077 +3 3080 3075 3079 +3 3074 3075 3080 +3 3074 3080 3613 +3 3070 3081 3082 +3 3626 3070 3082 +3 3052 3609 3620 +3 3076 3071 3084 +3 3084 3071 3083 +3 3077 2648 3086 +3 2644 2648 3077 +3 3087 3077 3086 +3 2619 3078 2646 +3 3079 2645 2651 +3 3619 3630 3089 +3 3619 3089 3081 +3 3082 3081 3090 +3 3090 3081 3089 +3 3090 3626 3082 +3 3091 3620 3626 +3 3620 3091 3052 +3 3084 3083 3092 +3 3076 3084 3092 +3 3085 3076 3092 +3 2622 3092 2652 +3 3085 3092 2622 +3 3087 3086 3093 +3 3088 3087 3093 +3 3096 3079 2651 +3 3080 3079 3096 +3 3622 3080 3094 +3 3626 3090 3089 +3 3116 3091 3626 +3 2652 3092 3110 +3 3095 3086 2648 +3 3093 3086 3095 +3 3632 3088 3095 +3 3095 3088 3093 +3 4097 2646 3078 +3 3080 3096 3106 +3 3052 3091 3116 +3 3092 3083 3110 +3 3642 3632 3095 +3 1697 2646 4097 +3 3080 3101 3109 +3 3097 3080 3109 +3 3094 3080 3098 +3 3098 3080 3097 +3 3099 3647 3100 +3 3083 3052 3110 +3 3080 3106 3112 +3 3101 3080 3112 +3 3098 3097 3102 +3 3099 3646 3103 +3 3647 3099 3103 +3 3642 3095 3104 +3 3642 3104 3105 +3 2651 3106 3096 +3 3101 3112 3107 +3 3108 3101 3107 +3 3109 3101 3108 +3 3097 3109 3123 +3 3102 3097 3123 +3 3110 3052 3116 +3 2675 3104 3095 +3 3655 3121 3656 +3 3656 3121 3634 +3 2669 3634 3121 +3 3107 3112 3113 +3 3108 3107 3114 +3 3114 3107 3113 +3 3109 3108 3114 +3 3661 3657 3102 +3 3103 3646 3662 +3 3115 3654 3647 +3 3117 3118 3659 +3 3111 3117 3659 +3 3111 3119 3117 +3 3120 3119 3111 +3 2670 3122 3106 +3 3122 3112 3106 +3 3113 3112 3122 +3 3114 3113 3122 +3 3109 3114 3133 +3 3133 3114 3122 +3 3123 3109 3133 +3 3102 3123 3124 +3 3124 3661 3102 +3 3116 3664 3125 +3 3110 3116 3125 +3 2660 3110 3139 +3 3119 3118 3117 +3 3126 3118 3119 +3 3120 3126 3119 +3 3127 3126 3120 +3 3130 3132 3131 +3 3128 3132 3130 +3 3672 3104 2675 +3 2670 3144 3122 +3 3133 3122 3144 +3 3123 3133 3134 +3 3124 3123 3135 +3 3135 3123 3134 +3 3135 3661 3124 +3 3103 3662 3147 +3 3147 3662 3158 +3 3647 3103 3148 +3 3148 3103 3147 +3 3115 3647 3160 +3 3676 3115 3149 +3 3149 3115 3160 +3 3125 3136 3137 +3 3110 3125 3138 +3 3138 3125 3137 +3 3139 3110 3138 +3 3128 3140 3141 +3 3128 3130 3140 +3 2665 2664 3140 +3 2665 3140 3131 +3 3131 3140 3130 +3 3131 3132 3142 +3 3131 3142 2665 +3 2665 3142 2666 +3 3142 2667 2666 +3 3143 2667 3142 +3 3134 3133 3145 +3 3145 3133 3144 +3 3135 3134 3146 +3 3146 3134 3145 +3 3158 3661 3135 +3 3158 3135 3146 +3 3149 3150 3676 +3 3676 3150 3136 +3 3136 3150 3137 +3 3138 3137 3151 +3 3152 3138 3151 +3 3139 3690 2660 +3 2660 3690 2672 +3 2664 3141 3140 +3 2664 2663 3141 +3 2663 3153 3141 +3 2667 3153 2663 +3 3153 2667 3143 +3 3121 3682 2706 +3 2680 3155 3156 +3 2679 3155 2680 +3 2680 3156 3684 +3 2692 3157 3144 +3 3157 3145 3144 +3 3146 3145 3157 +3 3158 3146 3157 +3 3147 3158 3159 +3 3647 3148 3160 +3 3160 3148 3176 +3 3137 3150 3169 +3 2705 3151 3137 +3 3161 3152 3151 +3 3161 3151 2705 +3 3690 3170 2672 +3 3162 2661 2683 +3 2698 3672 2675 +3 3674 3672 3171 +3 3171 3672 2698 +3 3121 2706 2688 +3 2676 3164 3154 +3 2677 3154 2689 +3 2689 3154 3164 +3 3155 2679 3165 +3 2692 2693 3157 +3 3696 3157 2693 +3 3157 3166 3158 +3 3159 3158 3166 +3 2703 3147 3159 +3 2704 3147 2703 +3 2704 3148 3147 +3 3176 3148 2704 +3 3149 3160 3167 +3 3150 3149 3168 +3 3168 3149 3167 +3 3169 3150 3168 +3 2695 2672 3170 +3 2684 3163 3162 +3 3180 3681 3171 +3 2689 3164 3172 +3 2679 2719 3165 +3 2700 2680 3684 +3 3714 2700 3684 +3 3173 3696 2693 +3 3175 3174 3166 +3 3159 3166 3174 +3 3176 2704 2723 +3 3169 3168 3167 +3 3177 3161 2705 +3 3170 3177 3178 +3 2695 3170 3179 +3 3170 3178 3179 +3 2331 3171 2698 +3 3704 3180 3171 +3 2706 3682 2726 +3 2726 3682 3707 +3 3713 2720 3714 +3 2720 2700 3714 +3 3173 2693 3185 +3 2722 3174 3175 +3 3716 2722 3175 +3 3174 2722 3159 +3 2714 3160 3176 +3 2705 2725 3177 +3 3178 3177 3181 +3 3179 3178 3182 +3 3182 3178 3181 +3 3179 3182 2328 +3 2720 3726 2719 +3 3713 3726 2720 +3 2721 3185 2693 +3 3716 3185 2722 +3 3185 2721 2722 +3 3181 3177 2725 +3 3182 3181 2725 +3 2328 3182 2725 +3 2726 3707 3720 +3 3183 2727 3186 +3 2718 3189 2731 +3 2738 3704 3171 +3 3704 2738 3198 +3 2730 2726 3720 +3 3732 3187 3188 +3 3732 3186 3187 +3 3186 2727 3187 +3 2728 3187 2727 +3 2728 3189 3187 +3 2728 2731 3189 +3 3731 3717 3198 +3 3720 3718 2730 +3 1262 1519 4422 +3 2735 3191 3193 +3 3190 3191 2735 +3 3192 3193 3191 +3 2737 3194 3195 +3 2737 3193 3194 +3 3194 3196 3195 +3 3198 2738 2751 +3 2735 2746 3190 +3 2747 3190 2746 +3 3191 3190 2747 +3 3191 2747 3192 +3 2747 2737 3192 +3 3195 3199 2737 +3 3199 3192 2737 +3 3196 3199 3195 +3 3200 3199 3196 +3 3197 3200 3196 +3 2752 3202 3739 +3 2752 3739 3203 +3 2758 3204 3201 +3 3201 3204 2748 +3 3205 3202 3207 +3 3206 3202 3205 +3 2752 3207 3202 +3 2752 2759 3207 +3 2760 3203 3739 +3 3208 2760 3739 +3 3203 2760 2753 +3 2758 2762 3204 +3 2748 3204 2762 +3 3206 3205 3209 +3 3205 3210 3209 +3 3207 3211 3205 +3 3211 3210 3205 +3 2759 3211 3207 +3 2759 3213 3211 +3 2759 3208 3213 +3 3208 2759 2760 +3 3210 3212 3209 +3 3213 3212 3210 +3 3211 3213 3210 +3 3214 2764 2765 +3 3214 3750 2764 +3 3214 2773 3215 +3 3216 2773 3214 +3 2765 3216 3214 +3 2765 3217 3216 +3 2765 2764 3217 +3 3217 3218 3216 +3 3217 2764 3750 +3 2768 3219 2769 +3 2766 2769 3779 +3 2769 3219 3779 +3 3779 3220 2766 +3 3221 3223 3222 +3 3222 3223 3224 +3 2771 3771 3215 +3 2773 2771 3215 +3 3216 2774 2773 +3 3216 3218 2774 +3 2767 2783 3225 +3 3225 3226 2767 +3 2768 3227 3219 +3 3227 2768 2775 +3 3228 3229 2777 +3 3228 2777 3220 +3 3220 2777 2766 +3 3230 3231 3765 +3 3247 3221 3232 +3 3222 3233 3221 +3 3233 3232 3221 +3 3233 3222 3224 +3 2771 2778 3771 +3 2780 2774 3249 +3 3234 3235 3236 +3 3236 3235 2783 +3 3235 3237 2783 +3 3225 2783 3238 +3 3238 2783 3237 +3 3238 3239 3240 +3 3238 3240 3225 +3 3225 3240 3226 +3 2785 3226 3240 +3 3227 2775 3241 +3 3241 2777 3229 +3 3241 3229 3242 +3 3250 3242 3229 +3 3243 3231 3230 +3 3251 3231 3243 +3 3244 3245 3230 +3 3245 3243 3230 +3 2786 3247 3232 +3 2792 2786 3232 +3 2798 2792 3232 +3 2798 3232 3233 +3 2778 3249 3248 +3 3234 3819 3255 +3 3236 3819 3234 +3 3239 2790 3240 +3 3820 3227 3241 +3 3241 3242 3820 +3 3250 3820 3242 +3 3245 3252 3243 +3 3252 3251 3243 +3 3835 3245 3244 +3 3835 2795 3245 +3 2795 3252 3245 +3 3246 3247 3811 +3 2786 2797 3247 +3 3233 3812 2798 +3 2790 3819 3236 +3 2794 2793 3257 +3 2794 3257 3251 +3 2794 3251 3252 +3 3251 3257 3256 +3 2795 2794 3252 +3 3247 3271 3811 +3 2798 3812 3258 +3 2804 3259 3253 +3 3253 3260 2804 +3 3260 3253 3254 +3 3261 3263 3262 +3 3262 3263 3274 +3 3255 3265 3264 +3 3265 3255 3266 +3 3267 3268 3269 +3 3822 3268 3267 +3 3269 3833 2811 +3 3268 3833 3269 +3 2793 3834 3257 +3 2795 3835 3270 +3 2836 3271 3247 +3 3272 2798 3258 +3 2798 3272 2802 +3 2804 3260 3273 +3 2805 3285 3261 +3 2806 2805 3261 +3 3262 2806 3261 +3 2806 3262 3274 +3 3264 2807 3286 +3 2808 2807 3264 +3 2808 3264 3275 +3 3275 3264 3265 +3 3276 3265 3266 +3 3275 3265 3276 +3 2810 2809 3277 +3 2810 3277 3267 +3 2810 3267 3269 +3 2811 2810 3269 +3 3833 4338 2811 +3 3834 2812 3278 +3 3834 2793 2812 +3 3278 3279 3280 +3 3834 3278 3280 +3 3270 3280 3279 +3 2795 3270 2844 +3 2836 3293 3271 +3 3281 3272 3258 +3 3293 3281 3258 +3 3272 3281 2802 +3 2804 3282 3283 +3 2804 3283 2828 +3 3273 3282 2804 +3 2805 2817 3284 +3 2805 3284 3285 +3 2806 2831 2818 +3 2806 3274 2831 +3 3838 2831 3274 +3 3286 2807 2819 +3 3275 3298 2808 +3 3298 3275 3276 +3 3288 2822 3287 +3 2809 3289 3277 +3 2825 3289 2809 +3 2811 4338 2435 +3 4338 3301 2435 +3 3290 3278 2812 +3 3290 3279 3278 +3 3270 3279 3291 +3 3291 3279 3290 +3 3270 3291 2844 +3 2836 3292 3293 +3 3293 3292 3281 +3 2814 2827 3294 +3 2827 2828 3294 +3 3294 2828 3283 +3 2817 2830 3284 +3 3284 2830 2831 +3 3284 2831 3285 +3 3295 3297 2838 +3 3296 3297 3295 +3 2838 3297 2819 +3 2424 3299 3845 +3 3309 2428 3845 +3 2428 2424 3845 +3 2822 3300 3287 +3 2822 2821 3300 +3 2821 2832 3300 +3 3312 2834 3288 +3 3301 2825 2434 +3 2835 2435 3301 +3 3302 3291 3290 +3 3302 3313 3291 +3 3291 3313 2844 +3 2837 3292 2836 +3 3281 3292 2837 +3 3295 3303 3304 +3 3295 3305 3303 +3 2838 3305 3295 +3 3298 3306 2808 +3 3306 3298 3276 +3 3307 3308 3299 +3 3307 3299 2839 +3 2840 3310 2841 +3 3310 2840 2841 +3 3311 2840 3310 +3 2844 3313 3314 +3 2848 2844 3314 +3 2845 3315 3316 +3 2846 3315 2845 +3 2847 3316 3303 +3 2847 2845 3316 +3 3316 3304 3303 +3 3305 3317 3303 +3 3317 2847 3303 +3 3305 2853 3318 +3 3305 3318 3317 +3 3319 3308 3307 +3 3858 3308 3319 +3 2839 3320 3307 +3 3307 3320 3319 +3 3327 2839 3309 +3 2450 3321 2840 +3 2840 3321 3310 +3 2854 2840 3311 +3 2834 3312 2457 +3 2846 2464 3323 +3 2846 3323 3324 +3 2846 3324 3315 +3 2847 3856 3325 +3 2847 3325 2852 +3 3317 3856 2847 +3 3318 3856 3317 +3 3326 3856 3318 +3 3318 2853 3326 +3 3319 3867 3858 +3 3320 3867 3319 +3 3327 3320 2839 +3 3321 2861 3328 +3 2832 3329 3300 +3 3302 3347 3863 +3 3330 3864 3344 +3 3330 3331 3332 +3 3864 3330 3332 +3 3322 3332 3331 +3 3322 3331 3333 +3 2856 3322 3334 +3 3334 3322 3333 +3 2464 2858 3335 +3 2464 3335 3323 +3 3335 3336 3323 +3 2860 3866 3323 +3 2860 3323 3336 +3 3324 3323 3866 +3 3325 2860 2852 +3 3866 2860 3325 +3 2485 3326 2853 +3 2485 3337 3326 +3 3337 3857 3326 +3 3338 3857 3337 +3 3339 3306 3858 +3 2854 3340 2862 +3 3345 2854 3311 +3 3345 3340 2854 +3 2457 3312 2863 +3 2864 3341 3302 +3 3302 3341 3347 +3 3342 3341 2864 +3 2855 3343 2864 +3 2864 3343 3342 +3 2855 3344 3343 +3 3344 2849 3330 +3 2856 3331 3330 +3 3333 3331 2856 +3 3334 3333 2856 +3 2860 3336 3335 +3 3873 3338 3337 +3 2486 3306 3339 +3 3350 2861 2862 +3 2862 3340 3350 +3 3345 3350 3340 +3 3348 3341 3342 +3 3347 3341 3348 +3 2485 3873 3337 +3 3339 3349 2486 +3 3345 3880 3350 +3 3346 3880 3345 +3 3348 3353 3347 +3 3353 3352 3347 +3 3348 3358 3353 +3 3873 2485 3889 +3 3354 2486 3349 +3 3355 3351 3877 +3 3356 3355 3877 +3 2871 3358 2870 +3 2871 3353 3358 +3 3359 3358 3891 +3 3359 2872 3358 +3 2872 2870 3358 +3 2486 3354 3889 +3 3360 3351 3355 +3 3361 3351 3360 +3 3356 3360 3355 +3 3356 3362 2875 +3 3356 3357 3362 +3 2875 3362 2876 +3 2871 2877 3353 +3 2877 3891 3353 +3 2879 3363 3359 +3 2879 3359 3891 +3 2879 3891 2877 +3 2872 3363 3365 +3 3359 3363 2872 +3 2876 3362 2885 +3 3363 2882 3364 +3 2879 2882 3363 +3 3363 3364 3365 +3 3894 2881 2888 +3 3894 2888 3362 +3 3366 3364 2882 +3 3366 3365 3364 +3 3365 3367 2883 +3 3365 3366 3367 +3 2885 3362 2888 +3 3369 3368 3366 +3 3368 3367 3366 +3 3367 3368 2883 +3 2883 3370 2891 +3 2883 3368 3370 +3 3368 3369 3370 +3 2893 3369 2892 +3 3370 3369 2893 +3 2891 3370 2893 +3 3372 3373 3371 +3 3375 3372 3371 +3 2894 3375 3373 +3 3373 3375 3371 +3 2894 3374 3376 +3 2894 3373 3374 +3 2500 4373 3952 +3 3381 3372 3375 +3 3375 2894 3381 +3 3377 2894 3376 +3 2896 2894 3377 +3 2896 3377 2897 +3 3381 3382 3383 +3 3381 2897 3382 +3 3382 2897 3377 +3 3386 3385 3378 +3 3387 3386 3378 +3 3389 3379 3380 +3 3388 3379 3389 +3 3391 3390 3384 +3 3385 3391 3384 +3 3392 3391 3385 +3 3386 3393 3385 +3 3393 3392 3385 +3 3387 3394 3386 +3 3394 3393 3386 +3 3395 3394 3387 +3 3396 3379 3388 +3 3405 3379 3396 +3 3397 3388 3389 +3 3396 3388 3397 +3 2900 2500 3952 +3 3398 3399 2898 +3 3402 3401 3382 +3 3940 3390 3391 +3 3392 3393 3403 +3 3394 3404 3393 +3 3404 3403 3393 +3 3395 3404 3394 +3 3397 3405 3396 +3 3411 2900 3952 +3 3406 3399 3410 +3 3398 3410 3399 +3 2899 2904 3400 +3 2906 2900 3411 +3 3407 3406 3408 +3 3406 3409 3408 +3 3410 3409 3406 +3 3400 2904 2903 +3 3411 3408 2906 +3 2906 3408 2907 +3 2907 3408 3412 +3 3954 3408 3409 +3 2908 3954 3409 +3 2908 3409 3410 +3 3414 2909 3413 +3 2505 4514 2909 +3 2909 4514 3413 +3 3956 3951 3403 +3 3934 3403 3951 +3 2907 3412 3953 +3 2910 2907 3953 +3 2911 2910 3954 +3 2910 3953 3954 +3 3954 2908 2911 +3 2505 3416 4514 +3 2912 3415 3416 +3 2912 3416 2505 +3 2912 2913 3417 +3 3415 2912 3417 +3 3418 3422 3417 +3 3417 2913 3418 +3 3418 3943 3422 +3 2915 3945 2905 +3 3945 3955 2914 +3 3427 3956 3403 +3 3419 3416 3415 +3 3420 3416 3419 +3 3417 3421 3415 +3 3421 3419 3415 +3 3422 3421 3417 +3 3943 3423 3422 +3 3423 3943 3945 +3 3423 3945 2915 +3 3955 3424 2914 +3 3414 3959 2513 +3 3420 3419 2513 +3 3420 2513 3959 +3 3421 2513 3419 +3 3422 3423 3421 +3 3427 2515 2914 +3 3427 2914 3424 +3 2916 3962 3425 +3 3426 2916 3425 +3 4531 3965 1226 +3 3428 1226 3965 +3 3426 3425 2917 +3 1226 5643 4531 +3 3428 3965 3430 +3 3428 3430 3431 +3 3429 2916 3428 +3 3431 3429 3428 +3 3432 2918 3433 +3 3433 2918 2919 +3 3432 2520 2918 +3 2919 3435 3433 +3 3435 2919 2920 +3 3433 3434 3438 +3 3433 3435 3434 +3 2921 3436 2920 +3 2920 3436 3435 +3 2520 4582 2522 +3 3438 3434 3437 +3 3434 3435 3437 +3 3439 3437 3435 +3 3440 3439 3436 +3 3439 3435 3436 +3 3975 3436 2921 +3 1635 3975 2921 +3 3437 2923 3438 +3 3440 3437 3439 +3 3440 2922 3437 +3 1635 2180 3975 +3 2922 3441 2923 +3 3441 2922 3440 +3 2924 3978 2925 +3 3442 2925 3978 +3 3978 2927 3443 +3 3978 2924 2927 +3 3442 3978 3443 +3 3442 3443 2928 +3 3442 2928 2929 +3 2931 3442 2929 +3 2931 3444 3442 +3 2925 3442 2932 +3 2932 3442 3444 +3 2928 3443 2927 +3 3444 2934 3445 +3 2931 2934 3444 +3 2932 3445 2935 +3 3444 3445 2932 +3 2934 2936 3445 +3 2935 3445 2936 +3 2522 3995 3450 +3 4002 3446 3447 +3 3983 3447 3446 +3 3983 3448 3447 +3 3447 3449 4002 +3 3448 3449 3447 +3 3451 3452 3449 +3 3451 3453 3454 +3 3449 3453 3451 +3 3450 3459 2938 +3 3455 3450 3995 +3 3459 3450 3455 +3 2941 3456 4020 +3 4020 2942 2941 +3 3457 3451 3454 +3 2938 3459 2947 +3 4020 3456 2950 +3 2950 2942 4020 +3 3459 3460 2947 +3 2948 2952 3461 +3 3462 4024 4027 +3 3463 3464 4028 +3 2952 3463 3465 +3 2952 3460 3463 +3 2952 2947 3460 +3 3464 3463 3460 +3 2952 3465 3461 +3 3465 3466 3461 +3 2948 3461 3467 +3 3461 3466 3467 +3 3463 4028 3481 +3 3463 3481 2954 +3 3468 3463 2954 +3 3468 3465 3463 +3 3466 3465 3469 +3 3469 3465 3468 +3 3467 3466 3469 +3 3467 3469 3470 +3 2954 3481 2956 +3 2970 3469 3468 +3 3470 3469 2970 +3 3471 3473 2958 +3 2958 3474 2959 +3 3473 3474 2958 +3 3475 3476 3477 +3 3472 3496 3478 +3 3472 3478 3479 +3 3480 4033 3472 +3 3479 3480 3472 +3 2956 3481 2963 +3 3470 2972 3471 +3 2972 3473 3471 +3 3474 3473 3482 +3 3474 3482 2959 +3 3483 3476 3475 +3 3486 3476 3483 +3 3483 3475 3486 +3 3478 3496 2965 +3 3478 2965 2966 +3 2967 3478 2966 +3 2967 3484 3478 +3 3484 3479 3478 +3 4642 3480 3479 +3 3484 4642 3479 +3 3481 2968 2963 +3 3493 2968 3481 +3 3473 2972 3487 +3 3482 3473 3485 +3 3485 3473 3487 +3 2959 3485 2976 +3 3482 3485 2959 +3 2965 3496 2973 +3 2967 3501 3484 +3 3501 4642 3484 +3 2968 3493 2974 +3 2972 2975 3487 +3 3485 3487 3488 +3 3485 3488 2976 +3 2974 3493 2979 +3 2975 2980 2983 +3 3487 2975 2983 +3 2976 3488 3489 +3 3491 3492 3490 +3 2973 3496 2981 +3 2982 2979 3493 +3 3487 2983 3495 +3 3488 3487 2985 +3 2985 3487 3495 +3 3489 3488 2985 +3 2976 3489 2985 +3 3492 3494 3490 +3 3497 2982 3493 +3 2982 3497 2990 +3 3495 2983 2988 +3 3493 3502 3497 +3 2990 3497 3499 +3 3499 3497 3498 +3 2990 3499 3500 +3 3497 3502 3505 +3 3498 3497 3506 +3 3506 3497 3505 +3 3499 3498 3500 +3 3500 3498 3506 +3 2990 3500 3503 +3 2990 3503 2995 +3 3507 3504 3502 +3 3505 3502 3504 +3 3505 3504 3506 +3 3506 3504 3507 +3 3507 3508 3506 +3 3509 3506 3508 +3 3503 3500 2999 +3 3500 3506 2999 +3 3506 3509 3000 +3 2999 3506 3000 +3 3509 3510 3001 +3 3001 3510 3006 +3 3510 3007 3006 +3 1936 4702 3036 +3 3007 3510 3008 +3 3511 3518 3510 +3 3008 3510 3518 +3 3511 3513 3518 +3 3015 3016 3512 +3 3015 3512 3017 +3 3511 3514 3513 +3 3512 3021 3515 +3 3016 3021 3512 +3 3017 3515 3022 +3 3512 3515 3017 +3 3516 3513 3514 +3 3517 3516 3514 +3 3518 3513 3516 +3 3518 3045 3008 +3 3021 3020 3515 +3 3022 3515 3020 +3 3521 3030 3522 +3 3524 3523 3525 +3 3531 3516 3517 +3 3518 3516 3526 +3 3526 3516 3531 +3 3549 3518 3526 +3 3518 3549 3045 +3 3032 3527 3519 +3 3519 3527 3025 +3 3033 3520 3521 +3 3522 3543 3521 +3 3528 3543 3522 +3 3030 3528 3522 +3 3030 3529 3528 +3 3523 3524 3536 +3 3530 3525 3523 +3 3536 3530 3523 +3 3526 3531 3563 +3 3032 3540 3541 +3 3527 3032 3541 +3 3521 3577 3033 +3 3577 3533 3033 +3 3571 3528 3529 +3 3534 3571 3529 +3 3034 3037 3529 +3 3529 3037 3534 +3 3537 3530 3536 +3 3537 3536 3538 +3 3531 3537 3538 +3 3531 3538 3563 +3 3532 4063 3542 +3 3037 3544 3534 +3 3544 3556 3534 +3 3535 3544 3037 +3 3038 3544 3535 +3 3544 3038 3040 +3 3545 3042 3041 +3 3545 3041 3546 +3 3041 3547 3546 +3 3524 3548 3536 +3 3536 3548 3561 +3 3562 3536 3561 +3 3538 3536 3562 +3 3526 3563 3549 +3 3541 3540 3550 +3 3550 3540 3564 +3 3541 3550 3527 +3 4063 3049 3553 +3 4063 3532 3049 +3 4063 3553 3542 +3 3533 3554 3039 +3 3577 3554 3533 +3 3543 3555 3521 +3 3528 3570 3543 +3 3571 3570 3528 +3 3534 3556 3571 +3 3040 3557 3544 +3 3546 3559 3558 +3 3546 3558 3545 +3 3545 3558 3560 +3 3042 3560 3041 +3 3545 3560 3042 +3 3548 3547 3573 +3 3573 3547 3041 +3 3561 3548 3573 +3 3538 4069 3563 +3 2611 3550 3564 +3 3552 3565 5316 +3 3551 3565 3552 +3 3551 3047 3565 +3 3551 3048 3047 +3 3542 3553 3566 +3 3577 3576 3554 +3 3521 3579 3577 +3 3555 3567 3521 +3 3567 3579 3521 +3 3543 3568 3555 +3 3568 3567 3555 +3 3569 3568 3543 +3 3570 3569 3543 +3 3557 3556 3544 +3 3040 3056 3557 +3 3557 3056 3582 +3 3560 3558 3572 +3 3560 3572 3041 +3 3050 3573 3041 +3 3549 3563 3585 +3 3564 3061 3575 +3 2611 3575 3054 +3 3564 3575 2611 +3 3565 3066 5316 +3 3047 3066 3565 +3 3048 3551 3068 +3 3039 4081 3078 +3 4081 3039 4072 +3 3554 4074 3039 +3 4074 3554 3576 +3 3579 3578 3577 +3 3567 3580 3579 +3 3568 3580 3567 +3 3569 3581 3568 +3 3556 3582 3571 +3 3557 3582 3556 +3 3057 3589 3069 +3 3057 3058 3589 +3 3593 3558 3559 +3 3572 3558 3593 +3 3572 3583 3050 +3 3050 3583 3573 +3 3583 3561 3573 +3 3584 3561 3585 +3 3563 3584 3585 +3 3549 3585 3081 +3 3598 3599 3574 +3 3586 3054 3575 +3 5316 3066 3065 +3 3611 3566 3553 +3 3577 4074 3576 +3 4089 4074 3577 +3 3578 4089 3577 +3 3579 4089 3578 +3 3580 4089 3579 +3 3580 3568 4083 +3 3581 3588 3568 +3 3588 4083 3568 +3 3569 3588 3581 +3 3570 3588 3569 +3 3571 3604 3570 +3 3604 3588 3570 +3 3582 3604 3571 +3 3056 3589 3582 +3 3056 3069 3589 +3 3058 3590 3589 +3 3058 3591 3590 +3 3074 3591 3058 +3 3591 3074 3592 +3 3559 3594 3593 +3 3572 3593 3595 +3 3583 3572 3596 +3 3596 3572 3595 +3 3585 3561 3597 +3 3599 3609 3052 +3 3061 3071 3600 +3 3575 3061 3600 +3 3062 3601 3085 +3 3586 3601 3062 +3 3065 3602 5316 +3 3068 3602 3065 +3 3566 3611 3603 +3 3566 3603 3612 +3 3587 2279 3055 +3 3604 4083 3588 +3 3605 3604 3582 +3 4085 3582 3589 +3 3590 4085 3589 +3 3074 3606 3592 +3 3594 3606 3593 +3 3583 3596 3615 +3 3561 3583 3615 +3 3597 3561 3615 +3 3585 3597 3607 +3 3081 3585 3619 +3 3598 3070 3608 +3 3599 3598 3609 +3 3609 3598 3608 +3 3610 3600 3076 +3 3600 3071 3076 +3 3601 3076 3085 +3 3553 3088 3611 +3 3603 3611 3612 +3 5338 3587 2278 +3 3613 3606 3074 +3 3613 3593 3606 +3 3595 3593 3613 +3 3596 3595 3614 +3 3614 3595 3613 +3 3615 3596 3614 +3 3597 3615 3617 +3 3617 3615 3616 +3 3607 3597 3618 +3 3618 3597 3617 +3 3585 3607 3625 +3 3625 3607 3618 +3 3619 3585 3625 +3 3626 3608 3070 +3 3609 3608 3620 +3 3620 3608 3626 +3 3612 3611 3621 +3 3621 3611 3088 +3 3078 4081 4097 +3 3622 3613 3080 +3 3622 3614 3613 +3 3615 3614 3623 +3 3623 3614 3622 +3 3623 4697 3616 +3 3623 3616 3615 +3 3617 3616 4697 +3 3624 3617 4697 +3 3617 3624 3618 +3 3625 3618 3624 +3 3627 3612 3621 +3 4672 3612 3627 +3 3094 3644 3622 +3 3644 3623 3622 +3 3644 3636 3623 +3 3625 3624 3628 +3 3619 3625 3629 +3 3639 3619 3629 +3 3630 3619 3639 +3 3089 3631 3626 +3 3621 3088 3632 +3 3632 4116 3621 +3 4116 4117 3621 +3 3627 3621 3633 +3 3633 3621 4117 +3 3643 4672 3627 +3 3643 3627 3633 +3 2279 3635 3634 +3 4115 3637 3628 +3 3625 3628 3638 +3 3638 3628 3637 +3 3629 3625 3639 +3 3639 3625 3638 +3 3630 3639 3649 +3 3089 3630 3640 +3 3640 3630 3649 +3 3641 3089 3640 +3 3631 3089 3641 +3 3116 3626 3631 +3 4117 3643 3633 +3 3094 3098 3644 +3 3645 3636 3644 +3 3645 3644 3646 +3 3645 3646 4115 +3 3637 4115 3646 +3 3637 3646 3099 +3 3100 3637 3099 +3 3638 3637 3100 +3 3647 3638 3100 +3 3647 3639 3638 +3 3648 3639 3647 +3 3649 3639 3648 +3 3640 3649 3650 +3 3650 3641 3640 +3 3651 3116 3631 +3 3632 3642 3105 +3 4116 3632 3652 +3 3652 3632 3105 +3 3098 3102 3644 +3 3649 3648 3654 +3 3654 3648 3647 +3 3654 3650 3649 +3 4694 4168 3655 +3 3634 4694 3655 +3 3653 3634 3655 +3 3656 3653 3655 +3 3634 3653 3656 +3 3102 3657 3644 +3 3646 3644 3662 +3 3662 3644 3657 +3 3658 3650 3654 +3 3664 3116 3651 +3 3659 3660 3111 +3 3104 3672 3105 +3 3672 3652 3105 +3 3662 3657 3661 +3 3654 3115 3658 +3 3658 3115 3663 +3 3118 4162 3660 +3 3659 3118 3660 +3 3120 3665 3666 +3 3120 3111 3665 +3 4163 3652 3672 +3 3655 4168 3121 +3 3663 3115 3676 +3 3663 3676 3677 +3 3664 3668 3125 +3 3669 4162 3118 +3 3669 3118 3126 +3 3126 3127 3670 +3 3127 3120 3666 +3 3127 3666 3670 +3 3670 3666 3129 +3 3129 3665 3128 +3 3129 3666 3665 +3 3671 3128 3665 +3 3671 3132 3128 +3 4163 3672 3673 +3 4163 3674 3667 +3 3675 3667 3674 +3 4170 3667 3675 +3 3662 3661 3158 +3 3668 3677 3676 +3 3668 3676 3136 +3 3125 3668 3136 +3 3126 3678 3669 +3 3670 3678 3126 +3 3129 3678 3670 +3 3129 3128 3678 +3 3141 3679 3671 +3 3141 3671 3678 +3 3141 3678 3128 +3 3132 3671 3680 +3 3680 3671 3679 +3 3132 3143 3142 +3 3132 3680 3143 +3 3673 3672 3674 +3 4163 3673 3674 +3 3675 3674 3681 +3 3153 3679 3141 +3 3680 3679 3153 +3 3143 3680 3153 +3 3171 3681 3674 +3 3704 4170 3675 +3 3121 4168 3682 +3 3161 3688 3152 +3 3138 3152 3689 +3 3689 3152 3688 +3 3689 4189 3139 +3 3689 3139 3138 +3 3690 3139 4189 +3 3675 3681 3691 +3 3682 3706 3692 +3 3156 3165 3694 +3 3155 3165 3156 +3 4193 3684 3694 +3 3694 3684 3156 +3 4195 3684 4193 +3 3686 4196 3685 +3 3695 4196 3686 +3 3687 3695 3686 +3 3157 3696 3166 +3 3688 3161 3697 +3 3177 3170 4833 +3 4833 3170 3690 +3 3180 3691 3681 +3 3704 3691 3180 +3 3675 3691 3704 +3 3717 4180 3704 +3 3692 3706 3720 +3 3165 2719 3694 +3 4193 3694 2719 +3 3684 4195 3714 +3 3699 3700 3701 +3 3173 3701 3696 +3 3173 3699 3701 +3 3702 3175 3701 +3 3696 3701 3175 +3 3696 3175 3166 +3 3161 3703 3697 +3 3177 3703 3161 +3 3703 3177 4833 +3 3717 3704 3198 +3 3705 4180 3717 +3 3692 3720 3707 +3 3682 3692 3707 +3 3708 3710 3711 +3 3709 3710 3708 +3 3710 3712 3711 +3 4841 4193 2719 +3 3714 3727 3713 +3 3715 3700 3699 +3 3173 3185 3715 +3 3173 3715 3699 +3 4190 3705 3718 +3 3706 4190 3718 +3 3719 3706 3718 +3 3720 3706 3719 +3 3183 3721 3184 +3 3184 3728 2708 +3 3721 3728 3184 +3 2708 3728 2718 +3 3708 3722 3709 +3 3722 3723 3724 +3 3722 3708 3723 +3 3708 3711 3723 +3 3725 3711 3712 +3 3723 3711 3725 +3 4841 2719 3726 +3 3185 4211 3715 +3 4211 3185 3716 +3 3705 3717 3731 +3 3718 3705 3731 +3 3720 3719 3718 +3 3183 3186 3721 +3 3728 3189 2718 +3 3723 3729 3724 +3 3725 3729 3723 +3 4841 3726 4223 +3 3726 3713 4223 +3 3727 3730 3713 +3 3730 4223 3713 +3 3187 3189 3733 +3 3733 3189 3728 +3 3734 3186 3732 +3 3188 3734 3732 +3 3188 3735 3734 +3 3188 3187 3735 +3 3735 3187 3733 +3 3193 3192 3736 +3 3193 3737 3194 +3 3736 3737 3193 +3 3194 3738 3196 +3 3737 3738 3194 +3 3196 3738 3197 +3 3199 3736 3192 +3 3737 3736 3199 +3 3200 3737 3199 +3 3738 3737 3200 +3 3197 3738 3200 +3 3202 3206 4254 +3 3206 4253 4254 +3 3202 4254 3740 +3 3202 3740 3739 +3 3739 3740 3208 +3 4253 3206 3209 +3 3741 4253 3209 +3 3740 4254 3742 +3 3740 3742 3213 +3 3208 3740 3213 +3 3743 3744 3745 +3 3745 3746 3743 +3 3212 3747 3741 +3 3212 3741 3209 +3 3212 3213 3748 +3 3742 3748 3213 +3 3749 3750 3214 +3 3743 3746 3761 +3 3751 3752 3746 +3 3752 3761 3746 +3 3752 3753 3754 +3 3751 3753 3752 +3 3741 3755 3756 +3 3741 3747 3755 +3 3212 3757 3747 +3 3758 3748 4268 +3 3758 3757 3748 +3 3757 3212 3748 +3 3215 3749 3214 +3 3215 3759 3749 +3 3750 3749 3760 +3 3760 3749 3759 +3 3217 3760 3218 +3 3217 3750 3760 +3 3754 3762 3752 +3 3762 3761 3752 +3 3774 3754 3753 +3 3762 3754 3774 +3 3219 3778 3779 +3 3763 4266 4262 +3 4267 3764 4262 +3 3764 3763 4262 +3 3765 3764 4267 +3 3223 3221 3766 +3 3756 3755 3223 +3 3756 3223 3766 +3 3224 3223 3755 +3 3224 3755 3767 +3 3767 3755 3747 +3 3757 3768 3747 +3 3768 3767 3747 +3 3758 3769 3757 +3 3769 3768 3757 +3 3769 3770 3771 +3 3758 3770 3769 +3 3770 3772 3771 +3 3771 3772 3215 +3 3759 3215 3772 +3 3760 3759 2774 +3 3760 2774 3218 +3 3761 3762 3773 +3 3774 3775 3762 +3 3775 3773 3762 +3 3775 4277 3776 +3 3774 4277 3775 +3 3234 3777 3235 +3 3778 3219 3227 +3 3778 3227 4278 +3 3220 3779 3228 +3 3228 3779 4279 +3 4279 3229 3228 +3 3780 4266 3763 +3 3764 3781 3763 +3 3781 3780 3763 +3 3765 3231 3781 +3 3765 3781 3764 +3 3765 3796 3230 +3 3782 3783 3246 +3 3246 3783 3247 +3 3783 3766 3247 +3 3766 3221 3247 +3 3224 3767 3233 +3 3784 3768 3785 +3 3784 3767 3768 +3 3769 3785 3768 +3 3786 3785 3769 +3 3769 3771 3786 +3 3801 3786 3771 +3 3771 2778 3801 +3 4285 3249 2774 +3 3775 3790 3773 +3 3790 3789 3773 +3 3776 3790 3775 +3 3791 3776 4277 +3 3791 3790 3776 +3 3234 3255 3807 +3 3235 3777 3793 +3 3794 3237 3793 +3 3237 3235 3793 +3 3237 3239 3238 +3 3794 3239 3237 +3 4278 3227 3795 +3 3229 4279 3821 +3 3250 3229 3821 +3 3780 3781 4294 +3 3231 3251 4294 +3 3781 3231 4294 +3 3244 3230 3796 +3 4296 3783 3797 +3 3783 3782 3797 +3 3246 3797 3782 +3 3767 4297 3798 +3 3767 3798 3233 +3 3233 3798 3812 +3 3784 4297 3767 +3 3802 3801 2778 +3 2778 3248 3802 +3 3248 3249 4285 +3 3248 4285 3802 +3 3254 3803 3787 +3 3787 3260 3254 +3 3804 3787 3788 +3 3804 3260 3787 +3 3805 3790 3791 +3 3792 3805 3791 +3 3807 3255 3806 +3 3794 3819 2790 +3 3239 3794 2790 +3 3795 3227 3820 +3 4294 3251 3823 +3 3808 3835 3244 +3 3808 3244 3809 +3 3809 3244 3796 +3 3810 4296 3797 +3 3811 3810 3797 +3 3246 3811 3797 +3 3812 3813 4306 +3 3812 3798 3813 +3 3803 3254 3814 +3 3814 3254 3253 +3 4314 3260 3804 +3 3817 3831 3818 +3 3255 3264 3806 +3 3821 3820 3250 +3 3821 3822 3820 +3 3822 3833 3268 +3 3821 3833 3822 +3 3256 3823 3251 +3 3257 3824 3825 +3 3257 3825 3256 +3 3256 3825 3823 +3 3809 3825 3824 +3 3809 3824 3808 +3 3808 3824 3826 +3 3808 3826 3835 +3 3827 3810 3811 +3 3811 3271 3827 +3 3812 4306 3830 +3 3258 3830 3829 +3 3258 3812 3830 +3 4320 3253 3259 +3 3260 4314 3273 +3 3261 3815 3816 +3 3263 3261 3816 +3 3817 3263 3816 +3 3818 3263 3817 +3 3274 3263 3818 +3 3274 3818 3831 +3 3264 3286 3806 +3 3255 3819 3266 +3 3795 3820 3843 +3 3822 3267 3843 +3 3822 3843 3820 +3 3257 3834 3824 +3 3834 3826 3824 +3 3826 3834 3835 +3 3271 3836 3828 +3 3830 3836 3829 +3 3829 3836 3837 +3 3829 3837 3258 +3 3259 2814 4330 +3 4330 4320 3259 +3 3273 4314 4332 +3 3261 3285 3815 +3 3831 3838 3274 +3 3839 3831 3832 +3 3839 3840 3831 +3 3840 3838 3831 +3 3286 3842 3806 +3 3267 3277 3843 +3 3835 3834 3280 +3 3270 3835 3280 +3 3293 3836 3271 +3 3293 3837 3836 +3 3837 3293 3258 +3 2814 3294 4331 +3 2814 4331 4330 +3 3294 3283 3282 +3 3294 3282 4331 +3 4331 3282 4332 +3 4332 3282 3273 +3 3815 3285 4347 +3 4348 3838 4340 +3 3840 4340 3838 +3 3841 3296 4335 +3 3844 3297 3841 +3 3297 3296 3841 +3 3844 3286 2819 +3 3844 4342 3286 +3 4342 3842 3286 +3 3266 4336 3276 +3 3288 3287 4913 +3 3287 4337 4913 +3 3312 3288 4913 +3 3277 3846 3843 +3 3289 3846 3277 +3 4338 4345 3301 +3 3285 2831 4347 +3 4348 4347 2831 +3 3838 4348 2831 +3 3296 3295 4335 +3 3844 2819 3297 +3 3287 3300 3850 +3 3287 3850 4337 +3 2825 3846 3289 +3 2825 3301 3846 +3 3846 3301 4345 +3 3863 3847 3302 +3 3847 3853 3302 +3 3853 3313 3302 +3 3304 3856 4335 +3 3295 3304 4335 +3 3848 3306 4336 +3 4336 3306 3276 +3 3299 3308 3848 +3 3845 3299 3848 +3 3309 3845 4357 +3 3310 3849 3311 +3 3851 3847 3863 +3 3314 3313 4361 +3 3313 3853 4361 +3 3314 4361 4362 +3 3854 3314 4362 +3 3316 3315 3855 +3 3316 3855 3856 +3 3316 3856 3304 +3 3306 3848 3858 +3 3848 3308 3858 +3 3309 4357 3859 +3 3309 3859 3327 +3 3849 3321 3860 +3 3310 3321 3849 +3 3311 3849 3861 +3 3849 3860 3861 +3 3862 3852 3851 +3 3863 3862 3851 +3 3314 3854 3864 +3 3314 3865 3322 +3 3864 3865 3314 +3 3855 3324 3866 +3 3315 3324 3855 +3 3325 3856 3855 +3 3325 3855 3866 +3 3326 4364 3856 +3 3857 4364 3326 +3 3327 3867 3320 +3 3860 3328 3868 +3 3321 3328 3860 +3 3868 4374 3861 +3 3868 3861 3860 +3 3311 3861 4374 +3 3311 4374 3869 +3 3850 3300 3329 +3 3347 3862 3863 +3 3871 3343 3870 +3 3864 3870 3343 +3 3344 3864 3343 +3 3865 3864 3332 +3 3322 3865 3332 +3 3858 3867 3339 +3 3328 2861 3868 +3 2861 3874 3868 +3 3311 3869 3345 +3 3345 3872 3877 +3 3345 3869 3872 +3 3872 3876 3877 +3 2863 4378 3329 +3 4378 2863 3312 +3 3347 3878 3862 +3 3343 3871 3342 +3 3338 3873 3857 +3 3867 3879 3339 +3 2861 3350 3874 +3 3877 3875 3346 +3 3345 3877 3346 +3 3342 4399 3348 +3 4395 3857 3873 +3 4395 3349 3879 +3 3349 3339 3879 +3 3880 3874 3350 +3 3880 3346 3875 +3 3880 3875 3881 +3 3877 3351 3881 +3 3877 3881 3875 +3 3877 3876 3356 +3 3882 3352 3883 +3 3882 3878 3352 +3 3878 3347 3352 +3 3348 3883 3884 +3 3348 4399 3883 +3 3882 3883 4399 +3 3348 3884 3358 +3 3885 3887 3888 +3 3886 3887 3885 +3 3887 4407 3888 +3 3889 4395 3873 +3 3354 3349 3889 +3 3349 4395 3889 +3 3874 3880 3361 +3 3880 3881 3361 +3 3881 3351 3361 +3 3356 3876 3357 +3 3876 3362 3357 +3 3353 3883 3352 +3 3353 3890 3883 +3 3890 3884 3883 +3 3358 3884 3891 +3 3884 3890 3891 +3 3893 3885 3892 +3 3892 3888 4407 +3 3892 3885 3888 +3 3891 3890 3353 +3 3361 3360 3894 +3 2881 3894 3360 +3 3896 3895 3898 +3 3895 3899 3898 +3 3900 3899 3895 +3 3904 3903 3897 +3 3903 3910 3897 +3 3899 3905 3898 +3 3906 3905 3899 +3 3900 3906 3899 +3 3906 3900 3907 +3 3900 3901 3907 +3 3908 3901 3902 +3 3907 3901 3908 +3 3909 3905 3906 +3 3909 3898 3905 +3 3904 3910 3903 +3 3913 3373 3372 +3 3374 3911 3376 +3 3374 3913 3911 +3 3374 3373 3913 +3 3912 3914 3911 +3 3914 3376 3911 +3 3916 3913 3372 +3 3376 3914 4473 +3 3372 3381 3916 +3 3376 4473 4486 +3 3376 4486 3377 +3 3920 3921 3919 +3 3916 3381 3929 +3 3377 4486 3382 +3 3384 3918 3917 +3 3385 3384 3917 +3 3917 5068 3385 +3 4476 3378 3385 +3 3378 3926 3387 +3 3378 4476 3926 +3 3379 3922 3380 +3 3923 3920 3919 +3 3924 3920 3923 +3 3925 3919 3921 +3 3923 3919 3925 +3 3922 3379 3927 +3 3380 3927 3389 +3 3922 3927 3380 +3 3923 2899 3924 +3 3923 3925 3928 +3 3923 3928 2899 +3 3929 3381 3383 +3 3929 3383 3401 +3 3401 3383 3382 +3 4506 3402 3382 +3 3384 3390 3918 +3 3387 3926 3395 +3 3927 3405 3930 +3 3379 3405 3927 +3 3389 3930 3397 +3 3927 3930 3389 +3 2898 3399 3924 +3 3399 3935 3924 +3 3400 2898 3924 +3 3924 2899 3400 +3 2899 3928 3936 +3 4506 3932 3402 +3 4506 3939 3932 +3 3933 3390 3940 +3 3940 3391 3942 +3 3391 3392 3934 +3 3942 3391 3934 +3 3392 3403 3934 +3 3397 3930 3405 +3 3407 3935 3406 +3 3931 3935 3407 +3 3399 3406 3935 +3 3401 3402 3937 +3 3402 3932 3937 +3 3937 3932 3938 +3 3939 3938 3932 +3 3940 3939 3933 +3 3942 3941 3940 +3 3934 3951 3942 +3 3931 3408 3952 +3 3407 3408 3931 +3 3943 3418 2899 +3 3943 2899 3936 +3 3937 5130 3401 +3 5130 3937 3946 +3 3937 3938 3946 +3 3939 3947 3938 +3 3947 3946 3938 +3 3948 3947 3939 +3 3940 3949 3939 +3 3949 3948 3939 +3 3941 3950 3940 +3 3950 3949 3940 +3 3942 3951 3941 +3 3951 3950 3941 +3 3408 3411 3952 +3 3408 3953 3412 +3 3953 3408 3954 +3 3413 4514 4511 +3 3944 3945 3943 +3 5130 3946 3956 +3 3946 3947 3956 +3 3948 3956 3947 +3 3949 3956 3948 +3 3951 3956 3949 +3 3950 3951 3949 +3 3957 4511 4513 +3 3957 3414 4511 +3 3414 3413 4511 +3 3427 5130 3956 +3 3414 3957 3958 +3 3957 4513 3958 +3 3414 3958 3959 +3 4514 3416 3960 +3 3960 3416 3420 +3 3960 3420 3959 +3 3960 3959 3961 +3 3960 3961 4525 +3 3427 3964 5130 +3 2917 3425 3962 +3 3963 3959 3958 +3 3961 3959 3963 +3 4525 3961 3963 +3 3427 3424 3964 +3 3966 2916 3429 +3 3430 3965 4529 +3 3431 3430 3966 +3 3429 3431 3966 +3 3967 3969 3970 +3 3968 3967 3970 +3 4532 3968 3970 +3 3971 4532 3970 +3 3970 3969 4545 +3 4548 3971 3970 +3 2520 3432 4578 +3 3432 3972 4578 +3 3433 3972 3432 +3 3433 3438 3972 +3 2520 4578 4582 +3 3438 3973 3972 +3 3440 3436 4588 +3 4588 3436 3975 +3 3974 3973 2923 +3 3973 3438 2923 +3 3974 3440 4588 +3 4588 3975 3977 +3 2923 3441 3974 +3 3440 3974 3441 +3 3980 3977 3975 +3 3975 2180 3980 +3 3980 3979 3977 +3 4601 3980 2180 +3 4582 3986 2522 +3 3984 3981 3983 +3 3982 3983 3981 +3 3983 3982 4605 +3 3983 3985 3984 +3 3983 3446 3985 +3 3986 3987 2522 +3 3987 3995 2522 +3 3987 3986 3988 +3 3988 3986 3989 +3 3986 3990 3989 +3 3446 4002 3991 +3 3446 3991 3985 +3 3983 4605 3448 +3 4605 4611 3448 +3 2180 4051 5237 +3 3992 3993 3994 +3 3994 3993 4621 +3 3987 3996 3995 +3 3988 3997 3987 +3 3997 3996 3987 +3 3998 3989 3999 +3 3998 3997 3989 +3 3997 3988 3989 +3 3999 4000 4012 +3 3999 3989 4000 +3 4001 4000 3990 +3 3990 4000 3989 +3 4002 3449 4003 +3 4003 4004 4002 +3 4611 3449 3448 +3 4003 3449 4611 +3 3994 4006 4005 +3 3994 4005 3992 +3 3992 4005 4007 +3 3993 3992 4008 +3 4008 3992 4007 +3 4022 4621 3993 +3 4008 4022 3993 +3 4009 3995 3996 +3 4010 3997 4011 +3 4010 4009 3997 +3 4009 3996 3997 +3 3998 4011 3997 +3 4001 4012 4000 +3 4013 4002 3452 +3 4013 3991 4002 +3 3452 4002 3449 +3 4004 4014 4002 +3 3449 4002 4014 +3 3453 3449 4015 +3 3449 4014 4015 +3 4015 3454 3453 +3 4016 3454 4015 +3 4017 4005 4006 +3 4007 4005 4018 +3 4018 4005 4017 +3 4022 4008 4018 +3 4018 4008 4007 +3 4009 4019 3455 +3 3995 4009 3455 +3 4019 4009 4010 +3 3451 4013 3452 +3 3457 4631 3451 +3 3451 4631 4013 +3 4016 3457 3454 +3 4631 3457 4016 +3 4021 4006 4031 +3 4017 4006 4021 +3 4017 4021 3458 +3 4017 4022 4018 +3 3459 3455 4019 +3 3459 4019 4023 +3 4023 4019 3461 +3 3461 4019 4010 +3 4021 4031 4024 +3 3458 4021 4025 +3 4021 4024 4025 +3 3458 4025 4017 +3 4022 4017 4025 +3 4023 4026 3460 +3 3459 4023 3460 +3 3461 4026 4023 +3 4024 4031 4027 +3 3462 4025 4024 +3 3464 3460 4026 +3 3464 4026 4028 +3 4028 4026 4029 +3 3461 4030 4026 +3 4030 4029 4026 +3 4030 3461 4010 +3 3481 4028 4032 +3 4032 4028 4029 +3 4030 4032 4029 +3 4033 4031 3472 +3 4033 4027 4031 +3 3462 4027 4033 +3 3477 3476 4034 +3 3477 4034 3475 +3 3472 4031 3496 +3 4033 3480 4642 +3 3462 4033 4642 +3 3476 3486 4034 +3 3475 4034 3486 +3 3493 3481 4032 +3 3493 4032 4647 +3 4647 4032 4629 +3 4035 4036 4636 +3 4636 4037 4035 +3 4038 4039 4036 +3 4035 4038 4036 +3 4035 3490 4038 +3 4035 3491 3490 +3 3491 4040 3492 +3 3491 4037 4040 +3 3491 4035 4037 +3 4040 4638 3492 +3 4041 4039 4038 +3 3490 3494 4042 +3 3490 4042 4038 +3 4038 4042 4041 +3 3494 4043 4042 +3 3492 4043 3494 +3 3492 4044 4043 +3 4638 4044 3492 +3 4045 4039 4041 +3 4042 4046 4041 +3 4046 4045 4041 +3 4046 4042 4043 +3 4045 4047 4039 +3 4046 4047 4045 +3 4647 3502 3493 +3 3502 4647 3507 +3 4642 3501 5261 +3 3507 4647 4050 +3 4050 3508 3507 +3 3509 3508 4050 +3 3496 4702 1936 +3 5261 3501 4052 +3 3510 3509 4050 +3 4050 5269 3510 +3 3510 4053 3511 +3 3510 5269 4053 +3 3511 4053 3514 +3 4052 3532 5285 +3 3514 4054 3517 +3 4053 4054 3514 +3 4055 3524 3525 +3 4056 4055 3525 +3 3517 4054 3531 +3 5285 3532 4057 +3 4058 5285 4057 +3 4059 3524 4055 +3 3525 3530 4056 +3 4056 3530 4060 +3 4054 4061 3531 +3 3532 3542 4057 +3 4059 4066 3524 +3 3530 4062 4060 +3 3537 4062 3530 +3 4061 3537 3531 +3 4062 3537 4061 +3 3542 3566 4057 +3 4058 4057 3566 +3 3547 4065 3546 +3 3546 4065 4064 +3 3548 4066 3547 +3 3547 4066 4065 +3 4066 3548 3524 +3 4067 3551 3552 +3 3546 4064 3559 +3 3562 3561 4068 +3 3562 4069 3538 +3 4068 4069 3562 +3 4067 3552 5316 +3 4672 4058 3566 +3 4676 3559 4064 +3 4068 3584 4071 +3 3561 3584 4068 +3 3563 4069 4071 +3 4069 4068 4071 +3 3551 4070 3068 +3 4072 3039 4073 +3 3039 4074 4073 +3 3563 4071 3584 +3 3586 3575 4678 +3 4075 3586 4678 +3 4073 4082 4072 +3 4074 4082 4073 +3 4089 3580 4083 +3 4077 3559 4676 +3 4077 3594 3559 +3 4678 3575 3600 +3 4078 4678 3600 +3 3586 4075 3601 +3 4075 4079 3601 +3 5316 3602 4080 +3 4080 3602 4070 +3 4070 3602 3068 +3 3612 4672 3566 +3 3587 4076 5326 +3 4076 3587 5338 +3 4082 4081 4072 +3 4084 3604 3605 +3 4085 3590 3592 +3 3591 3592 3590 +3 3592 3606 4690 +3 3606 3594 4690 +3 4690 3594 4077 +3 3610 4087 4086 +3 3610 4086 4078 +3 3610 4078 3600 +3 3610 3076 4087 +3 4079 3076 3601 +3 4686 3076 4079 +3 5326 2279 3587 +3 4082 4097 4081 +3 4083 3604 4122 +3 3582 4108 3605 +3 3592 4099 4085 +3 4099 3592 4695 +3 4090 4078 4092 +3 4091 4078 4090 +3 4078 4086 4092 +3 4086 4093 4092 +3 4093 4087 4094 +3 4093 4086 4087 +3 3076 4094 4087 +3 3076 4686 4094 +3 4683 3634 3635 +3 3604 4105 4122 +3 4107 4105 3604 +3 4084 4107 3604 +3 4098 4107 4084 +3 3605 4098 4084 +3 4108 4098 3605 +3 4720 4108 3582 +3 3582 4085 4720 +3 4099 4720 4085 +3 3623 4100 4697 +3 3628 3624 4697 +3 4090 4101 4091 +3 4092 4101 4090 +3 4093 4101 4092 +3 4111 4095 4102 +3 4103 4095 4096 +3 4102 4095 4103 +3 4082 4112 4097 +3 4074 4112 4082 +3 4089 4104 4074 +3 4083 4122 4089 +3 4122 4121 4089 +3 4107 4106 4105 +3 4124 4107 4098 +3 4108 4124 4098 +3 4720 4099 4704 +3 4721 4099 4695 +3 3623 3636 4100 +3 4100 3636 4114 +3 4110 4111 4102 +3 4110 4102 4103 +3 4672 3643 4118 +3 4672 4118 4714 +3 3634 4683 4694 +3 4104 4112 4074 +3 4104 4089 4733 +3 4105 4746 4122 +3 4106 4746 4105 +3 4107 4123 4106 +3 4124 4123 4107 +3 4108 4141 4124 +3 4099 4721 4704 +3 4113 4114 3636 +3 4128 4115 3628 +3 4109 3631 3641 +3 4109 4725 3631 +3 4714 4118 4119 +3 4714 4119 4132 +3 1697 4097 4120 +3 4120 4097 4112 +3 4120 4104 4731 +3 4120 4112 4104 +3 4121 4733 4089 +3 4125 4734 4721 +3 4126 4114 4113 +3 4136 4114 4126 +3 3636 3645 4127 +3 3636 4127 4113 +3 4113 4127 4126 +3 4115 4128 3645 +3 4127 3645 4128 +3 3641 3650 4109 +3 4109 3650 5374 +3 4725 3651 3631 +3 4138 3651 4725 +3 4117 4116 3652 +3 3652 4129 4117 +3 4117 4130 3643 +3 4117 4129 4130 +3 3643 4130 4118 +3 4131 4132 4118 +3 4118 4132 4119 +3 5338 2278 3683 +3 1697 4120 4731 +3 4121 4134 4733 +3 4134 4133 4733 +3 4766 4134 4122 +3 4134 4121 4122 +3 4746 4106 4782 +3 4123 4782 4106 +3 4124 4135 4123 +3 4141 4135 4124 +3 4136 4126 4137 +3 4127 4137 4126 +3 4142 4127 4128 +3 4129 3652 4148 +3 4130 4129 4139 +3 4139 4129 4148 +3 4140 4130 4139 +3 4118 4130 4140 +3 4755 4118 4140 +3 4131 4118 4755 +3 4741 4132 4758 +3 4135 4141 4123 +3 4141 4782 4123 +3 4136 4137 4154 +3 4127 4142 4137 +3 4142 4154 4137 +3 3658 5374 3650 +3 3651 4138 3664 +3 4143 4147 4145 +3 4144 4147 4143 +3 4146 4145 4147 +3 3111 3660 4774 +3 4845 3036 4702 +3 4139 4148 4149 +3 4150 4140 4139 +3 4150 4139 4149 +3 4152 4153 4166 +3 1697 4731 4780 +3 4155 4154 4142 +3 4156 5374 3658 +3 3658 3663 4156 +3 4157 4144 4143 +3 4158 4144 4157 +3 4145 4159 4143 +3 4159 4157 4143 +3 4160 4159 4145 +3 4146 4160 4145 +3 4147 4161 4146 +3 4161 4160 4146 +3 3660 4162 4147 +3 4162 4161 4147 +3 3111 4774 4802 +3 3665 3111 4802 +3 3652 4163 4148 +3 4149 4148 4164 +3 4164 4148 4163 +3 4150 4149 3667 +3 3667 4149 4164 +3 4151 4150 3667 +3 4151 3667 4775 +3 3667 4171 4775 +3 4165 4166 4167 +3 4166 4165 4152 +3 3663 4806 4786 +3 3663 4786 4156 +3 3663 3677 4806 +3 3664 4806 3668 +3 3664 4138 4806 +3 4157 4800 4158 +3 4159 4800 4157 +3 4160 4800 4159 +3 4172 4800 4160 +3 4161 4172 4160 +3 4162 3669 4161 +3 3665 4802 3671 +3 3667 4164 4163 +3 3667 4170 4171 +3 4167 4171 4170 +3 4167 4170 4165 +3 4165 4170 4152 +3 4763 3682 4168 +3 3668 4806 3677 +3 3669 4172 4161 +3 3678 4172 3669 +3 3678 4801 4172 +3 4802 4801 3678 +3 3671 4802 3678 +3 4170 3704 4152 +3 4762 4152 3704 +3 4763 4184 3682 +3 4173 4174 4175 +3 4175 4176 4177 +3 4174 4176 4175 +3 4176 4178 4177 +3 3704 4180 4762 +3 4181 4182 4183 +3 3706 3682 4184 +3 4175 4174 4173 +3 4177 4174 4175 +3 1697 4780 4838 +3 4179 4185 3685 +3 3685 4185 3686 +3 4185 3687 3686 +3 4186 3687 4185 +3 4187 4815 4188 +3 4188 3689 3688 +3 4188 4815 3689 +3 3689 4815 4189 +3 4190 4181 4180 +3 4182 4181 4190 +3 4183 4182 4190 +3 4184 4183 4190 +3 3706 4184 4190 +3 4192 3693 4191 +3 4193 4194 4195 +3 3685 4196 4835 +3 3695 3687 4197 +3 3687 4186 4197 +3 4188 4198 4187 +3 3688 4198 4188 +3 3688 3697 4198 +3 3690 4189 4833 +3 4190 4180 3705 +3 3693 4199 4191 +3 4192 3698 3693 +3 4192 4199 3698 +3 3693 3698 4199 +3 4204 4194 4193 +3 4195 4194 4204 +3 4835 4196 4200 +3 3695 4200 4196 +3 4201 3701 4197 +3 4201 4202 3701 +3 4197 3701 3695 +3 3700 4200 3701 +3 3695 3701 4200 +3 3701 4202 3702 +3 3697 4832 4198 +3 3697 3703 4832 +3 4833 4832 3703 +3 3709 4203 3710 +3 3710 4203 3712 +3 4193 4841 4204 +3 4205 4195 4204 +3 3714 4195 4205 +3 3714 4205 4206 +3 4202 4207 4208 +3 4202 4201 4207 +3 4208 4207 3715 +3 4200 3700 4207 +3 3715 4207 3700 +3 3702 4202 4208 +3 3702 4208 4837 +3 3702 4837 3175 +3 4837 4208 4209 +3 4837 3716 3175 +3 3683 2278 4237 +3 3683 4237 4823 +3 3724 3709 3722 +3 3724 4210 3709 +3 4210 4203 3709 +3 3712 4210 3725 +3 4203 4210 3712 +3 4206 4846 3727 +3 4206 3727 3714 +3 4208 4211 4209 +3 4208 3715 4211 +3 4837 4211 3716 +3 3721 4212 5455 +3 3721 3186 4212 +3 3721 5455 4213 +3 4213 3728 3721 +3 4850 1697 4838 +3 3724 3729 4210 +3 3725 4210 3729 +3 4214 4215 4216 +3 4216 4217 4214 +3 4846 4229 3730 +3 3727 4846 3730 +3 4218 4219 4212 +3 4218 4212 3186 +3 3733 3728 4213 +3 4220 4215 4214 +3 4221 4215 4220 +3 4217 4222 4214 +3 4222 4220 4214 +3 4223 3730 4229 +3 4224 4219 4218 +3 4225 4219 4224 +3 3186 3734 4224 +3 4218 3186 4224 +3 3735 3733 4236 +3 4220 4226 4221 +3 4222 4226 4220 +3 4227 4226 4222 +3 4229 4228 4223 +3 4232 4245 4234 +3 4233 4245 4232 +3 4248 4225 4224 +3 4235 4225 4248 +3 3734 4236 4224 +3 4224 4236 4248 +3 3735 4236 3734 +3 4229 4238 4228 +3 4239 4229 4846 +3 4239 4238 4229 +3 4239 4231 4240 +3 4231 4230 4240 +3 4230 4241 4240 +3 4242 4230 4231 +3 4241 4230 4242 +3 4232 4244 4233 +3 4244 4232 4243 +3 4232 4234 4243 +3 4245 4243 4234 +3 4243 4246 4247 +3 4245 4246 4243 +3 4869 4228 4238 +3 4869 4238 4239 +3 4249 4239 4240 +3 4241 4249 4240 +3 4249 4241 4242 +3 4243 4250 4244 +3 4247 4250 4243 +3 4870 4239 4251 +3 4239 4249 4251 +3 4252 4251 4249 +3 4256 4249 4242 +3 4256 4252 4249 +3 4870 4255 4260 +3 4870 4251 4255 +3 4251 4252 4255 +3 4256 4255 4252 +3 3741 4257 4253 +3 4254 4257 3742 +3 4254 4253 4257 +3 3745 3744 4258 +3 4259 3745 4258 +3 4259 4871 3745 +3 4871 3746 3745 +3 4871 3751 3746 +3 4260 4255 4261 +3 4256 4261 4255 +3 4256 4262 4261 +3 4872 4257 3741 +3 4872 3742 4257 +3 4872 3748 3742 +3 3744 3743 4258 +3 3743 3761 4276 +3 3743 4276 4259 +3 3743 4259 4258 +3 4263 3751 4871 +3 3753 3751 4264 +3 3751 4263 4264 +3 4265 3753 4264 +3 1697 4850 4373 +3 4270 4260 4261 +3 4266 4270 4261 +3 4262 4266 4261 +3 3741 3756 4872 +3 4269 4268 3748 +3 4269 4272 4268 +3 4277 3774 4265 +3 4265 3774 3753 +3 4271 3765 4267 +3 4872 3756 3766 +3 3770 3758 4268 +3 4272 3770 4268 +3 3770 4272 3772 +3 3772 4272 4274 +3 4273 3759 4274 +3 3759 3772 4274 +3 4273 2774 3759 +3 4275 4276 3761 +3 3761 3773 4275 +3 3777 3234 4874 +3 4882 3777 4874 +3 3778 4279 3779 +3 4278 4279 3778 +3 4266 4293 4270 +3 4293 4266 3780 +3 3765 4280 3796 +3 4271 4280 3765 +3 4296 4281 3783 +3 3783 4281 3766 +3 3785 3786 4282 +3 3784 3785 4282 +3 4283 4284 4274 +3 4274 4284 4273 +3 4273 4284 4285 +3 2774 4273 4285 +3 3787 4299 4276 +3 3788 4275 4286 +3 3788 4276 4275 +3 3788 3787 4276 +3 3773 3789 4286 +3 4275 3773 4286 +3 4277 4302 3791 +3 3791 4302 3792 +3 3234 3807 4874 +3 3794 3793 3777 +3 3794 3777 4287 +3 4287 3777 4882 +3 4288 4289 4290 +3 4290 4886 4288 +3 4291 4279 4278 +3 4291 4278 3795 +3 4291 3795 4292 +3 3821 4279 4291 +3 4294 4884 3780 +3 3780 4884 4293 +3 4280 4884 4294 +3 4280 4294 4295 +3 4280 4295 3796 +3 4304 4281 4305 +3 4281 4296 4305 +3 4297 4309 3798 +3 3799 4297 3784 +3 3800 3799 3784 +3 3800 3784 4298 +3 4298 3784 4282 +3 3786 4298 4282 +3 3786 4885 4298 +3 4885 4311 4298 +3 4283 4885 3801 +3 4885 3786 3801 +3 3802 4283 3801 +3 4284 4283 3802 +3 4285 4284 3802 +3 3803 4299 3787 +3 3788 4286 3804 +3 3789 3804 4286 +3 3789 3790 4300 +3 3804 3789 4300 +3 3790 3805 4300 +3 4300 3805 4301 +3 4302 4301 3792 +3 4302 4303 4301 +3 4301 3805 3792 +3 4302 4893 4303 +3 3807 3806 4350 +3 3819 3794 4287 +3 3823 4295 4294 +3 3796 4295 3825 +3 3825 4295 3823 +3 3796 3825 3809 +3 4306 4307 4308 +3 4306 3813 4307 +3 4309 3813 3798 +3 3799 4319 4297 +3 4297 4319 4309 +3 3799 4310 4319 +3 3800 4310 3799 +3 3800 4311 4310 +3 3800 4298 4311 +3 3814 3253 4312 +3 3814 4312 4313 +3 3814 4313 3803 +3 3803 4313 4299 +3 4300 4314 3804 +3 4315 4314 4300 +3 4301 4315 4300 +3 4303 4315 4301 +3 3815 4315 4303 +3 3815 4303 3816 +3 4893 4316 4303 +3 3816 4303 4316 +3 3817 3816 4316 +3 4919 4317 3817 +3 3831 3817 4317 +3 3827 4308 4327 +3 3271 4308 3827 +3 3828 4306 4308 +3 3828 4308 3271 +3 4306 3836 3830 +3 4306 3828 3836 +3 4328 4309 4318 +3 4319 4318 4309 +3 3253 4320 4312 +3 4320 4321 4312 +3 4322 4313 4312 +3 4322 4312 4321 +3 3817 4316 4919 +3 3832 4324 4323 +3 3831 4324 3832 +3 3831 4317 4324 +3 4323 4324 4333 +3 4333 4325 4326 +3 4324 4325 4333 +3 3266 3819 4336 +3 4339 4304 4305 +3 4305 4296 4339 +3 3810 4339 4296 +3 3810 3827 4339 +3 4319 4328 4318 +3 4329 4328 4319 +3 4310 4329 4319 +3 4321 4320 4331 +3 4331 4320 4330 +3 4322 4321 4332 +3 4332 4321 4331 +3 4314 4906 4332 +3 4906 4322 4332 +3 3832 4323 3839 +3 3840 3839 4323 +3 3840 4323 4333 +3 4325 4334 4326 +3 4334 4333 4326 +3 3841 4335 4910 +3 4910 4911 3841 +3 3841 4911 3844 +3 4342 3844 4911 +3 3806 3842 4350 +3 4922 4913 4337 +3 3843 4344 3795 +3 3821 4338 3833 +3 4327 4339 3827 +3 4925 3815 4347 +3 3840 4333 4340 +3 4334 4341 4333 +3 4349 4341 4334 +3 3842 4342 4350 +3 4343 3795 4344 +3 3843 3846 4344 +3 4345 4338 4355 +3 4347 4348 4925 +3 4949 4348 4340 +3 4333 4949 4340 +3 4349 4333 4341 +3 4335 3856 4363 +3 4933 3312 4913 +3 4351 3795 4343 +3 4352 3795 4351 +3 4344 4351 4343 +3 3846 4353 4344 +3 4344 4353 4351 +3 3846 4345 4353 +3 4353 4345 4354 +3 4355 4356 4345 +3 4356 4354 4345 +3 4356 4346 3847 +3 4355 4346 4356 +3 3847 4346 3853 +3 3845 3848 4357 +3 4964 4337 3850 +3 4933 4379 3312 +3 4351 4353 4352 +3 4354 4358 4353 +3 4356 4359 4354 +3 4359 4358 4354 +3 3851 3852 4359 +3 3851 4359 4356 +3 3847 3851 4356 +3 4360 3853 4346 +3 4360 4368 3853 +3 4368 4361 3853 +3 4363 3856 4364 +3 4363 4364 4950 +3 4950 4364 4365 +3 4364 3857 4365 +3 3859 4357 4962 +3 4964 3850 4366 +3 4353 4380 4352 +3 4353 4358 4380 +3 4359 4367 4358 +3 4367 4380 4358 +3 3852 3862 4367 +3 4359 3852 4367 +3 4360 4382 4368 +3 4361 4368 4369 +3 4362 4361 3864 +3 3864 4361 4369 +3 4362 3864 3854 +3 3327 4372 3867 +3 3327 3859 4372 +3 3859 4962 4372 +3 4374 4375 3869 +3 4376 4377 3850 +3 4377 4366 3850 +3 3850 3329 4376 +3 4379 4378 3312 +3 4381 4380 4367 +3 3862 4381 4367 +3 4368 4382 4399 +3 3870 4399 3871 +3 3870 4369 4399 +3 4369 4368 4399 +3 3864 4369 3870 +3 4383 4371 4370 +3 4383 4370 4371 +3 4372 4384 3867 +3 3868 3874 4374 +3 4403 4385 4374 +3 4375 4374 4385 +3 4386 4375 4385 +3 3869 4375 4386 +3 3869 4386 3872 +3 3872 4386 4973 +3 3876 4387 4388 +3 3876 3872 4387 +3 3872 4973 4387 +3 4974 4376 4389 +3 4974 4388 4376 +3 4377 4376 4387 +3 4387 4376 4388 +3 3329 4389 4376 +3 4378 4389 3329 +3 4378 4390 4391 +3 4392 4378 4379 +3 4390 4378 4392 +3 3862 3878 4381 +3 3878 4382 4381 +3 3871 4399 3342 +3 4393 4383 4401 +3 4394 3857 4395 +3 4394 4395 4396 +3 4396 3879 3867 +3 4396 3867 4384 +3 3874 4403 4374 +3 3876 4388 4974 +3 4397 4391 4390 +3 4392 4398 4390 +3 4398 4397 4390 +3 4392 4416 4398 +3 3878 4399 4382 +3 4401 4402 4393 +3 4402 4400 4393 +3 4396 4395 3879 +3 4397 4404 4419 +3 4397 4398 4404 +3 4416 4404 4398 +3 3878 3882 4399 +3 4402 4405 4400 +3 4405 4402 4412 +3 3886 3885 4406 +3 3886 4407 3887 +3 4406 4407 3886 +3 4403 3874 3361 +3 4415 3362 4974 +3 3362 3876 4974 +3 4408 4419 4404 +3 4416 4409 4404 +3 4409 4408 4404 +3 4410 4400 4411 +3 4400 4405 4411 +3 4412 4413 4405 +3 4413 4411 4405 +3 4412 5012 4413 +3 4406 3893 4414 +3 3885 3893 4406 +3 4407 4414 3892 +3 4407 4406 4414 +3 4998 4990 4418 +3 3894 4415 3361 +3 3361 4415 4403 +3 3362 4415 3894 +3 3896 4416 3895 +3 3896 4409 4416 +3 3896 4408 4409 +3 4416 3900 3895 +3 4420 3900 4416 +3 4411 4417 4410 +3 3897 4417 4411 +3 3897 4411 4413 +3 4413 3904 3897 +3 4413 5021 3904 +3 4413 5012 5021 +3 3893 3892 4414 +3 4990 4425 4418 +3 3898 4419 4408 +3 3896 3898 4408 +3 3901 3900 5020 +3 3900 4420 5020 +3 5020 3902 3901 +3 4421 3902 5020 +3 3897 3910 4417 +3 4425 4424 4418 +3 4426 4427 4419 +3 4426 4419 3898 +3 3906 4428 4438 +3 3906 4439 4428 +3 3902 4421 3908 +3 4421 4429 3908 +3 3904 5021 5028 +3 4430 4431 4432 +3 4431 4433 4432 +3 4434 4423 4436 +3 4437 4424 4425 +3 5634 4426 4446 +3 3898 3909 4446 +3 4426 3898 4446 +3 3909 3906 4438 +3 3907 4449 4439 +3 3907 4439 3906 +3 3907 4440 4449 +3 3907 3908 4440 +3 3908 4429 4440 +3 3910 4441 4417 +3 5028 3910 3904 +3 4441 3910 5028 +3 4432 4442 4430 +3 4443 4442 4432 +3 4433 4443 4432 +3 4434 4444 4435 +3 4436 4444 4434 +3 4445 4437 4425 +3 4451 4445 4425 +3 3909 4447 4446 +3 4447 3909 4448 +3 3909 4438 4448 +3 4451 4450 4445 +3 4467 4451 4425 +3 4447 5634 4446 +3 3912 3911 5042 +3 3911 4452 5042 +3 4453 3912 5042 +3 4454 4456 4457 +3 4455 4456 4454 +3 4458 4445 4450 +3 4459 4445 4458 +3 4451 4460 4450 +3 4460 4458 4450 +3 4460 4451 4467 +3 3913 5075 3911 +3 3911 5075 4452 +3 3912 4453 3914 +3 4453 4473 3914 +3 4463 4455 4454 +3 4457 4464 4454 +3 4464 4463 4454 +3 4464 4457 4465 +3 4458 4466 4459 +3 4460 4466 4458 +3 4467 4466 4460 +3 4479 4461 4468 +3 4462 4469 4461 +3 4469 4468 4461 +3 4469 4462 4470 +3 4471 4472 3915 +3 3913 3916 5075 +3 3917 4474 4475 +3 4463 4476 5068 +3 4464 4477 4463 +3 4477 4476 4463 +3 4478 4464 4465 +3 4477 4464 4478 +3 4479 4468 4480 +3 4468 4469 4480 +3 4481 4469 4470 +3 4481 4482 4469 +3 4482 4480 4469 +3 4471 4482 4481 +3 4471 4483 4482 +3 3915 4483 4471 +3 4484 3915 4472 +3 4484 4472 4492 +3 3916 4485 4493 +3 3916 4493 5075 +3 4486 4473 4487 +3 3917 3918 4474 +3 3917 5067 5068 +3 3917 4475 5067 +3 5068 4476 3385 +3 3926 4476 4477 +3 5069 4477 4478 +3 4480 4488 4479 +3 3920 4488 4480 +3 3920 4480 4482 +3 3921 3920 4482 +3 3921 4482 4489 +3 4489 4482 4483 +3 4483 3915 4490 +3 4483 4490 4489 +3 3915 4484 4490 +3 4490 4484 4491 +3 4492 4491 4484 +3 3916 3929 4485 +3 4493 4485 4494 +3 4486 4493 4494 +3 4486 4494 3382 +3 3926 5069 5082 +3 4477 5069 3926 +3 3395 5088 4422 +3 4495 3924 4496 +3 4495 4488 3924 +3 4488 3920 3924 +3 4489 4495 3925 +3 4489 3925 3921 +3 3925 4495 4496 +3 4497 4489 4490 +3 4497 4490 4498 +3 4490 4491 4498 +3 4492 4498 4491 +3 4485 3929 4499 +3 4500 4501 4494 +3 4500 4494 4499 +3 4499 4494 4485 +3 4494 4502 3382 +3 4494 4501 4502 +3 4506 3382 4502 +3 4496 3924 4504 +3 4504 3924 4503 +3 3928 4504 5090 +3 3928 4496 4504 +3 3928 3925 4496 +3 3928 5090 3936 +3 4498 4505 4497 +3 5091 4498 4492 +3 4499 3929 3401 +3 4499 3401 4500 +3 3918 3390 4474 +3 4474 3390 5087 +3 3395 3926 5088 +3 5083 5095 3931 +3 3924 3935 4503 +3 3936 5090 4512 +3 4498 4507 4505 +3 5091 4507 4498 +3 4506 5094 3939 +3 3939 5094 5087 +3 3939 5087 3933 +3 3933 5087 3390 +3 3935 3931 5095 +3 3935 5095 5097 +3 5097 4503 3935 +3 5098 3944 4508 +3 5117 4500 3401 +3 5677 4237 2917 +3 4508 3943 3936 +3 3944 3943 4508 +3 3944 5104 4510 +3 3944 4510 3945 +3 3955 3945 4510 +3 3955 4510 4515 +3 3401 5130 5117 +3 3955 4515 5134 +3 4516 4517 5123 +3 3960 4518 4514 +3 3964 5129 5130 +3 4519 4517 4516 +3 4519 4516 4520 +3 4513 4521 3958 +3 4518 3960 4525 +3 3955 5134 3424 +3 3424 5134 4522 +3 5129 3964 4522 +3 4523 4519 4520 +3 4523 3966 4519 +3 3966 4523 2916 +3 4530 2916 4523 +3 4530 3962 2916 +3 3958 4521 3963 +3 4521 4524 3963 +3 4525 4524 4526 +3 4524 4525 3963 +3 4525 4526 4527 +3 3424 4522 3964 +3 4531 5643 5140 +3 4519 3966 4528 +3 3962 4530 2917 +3 5677 2917 4530 +3 4527 4524 4521 +3 4526 4524 4527 +3 3430 4529 4528 +3 4528 3966 3430 +3 4529 3965 4531 +3 3968 4536 3967 +3 4532 4533 3968 +3 3968 4533 4536 +3 4535 3969 3967 +3 4535 3967 4534 +3 4534 3967 4536 +3 4533 4532 4552 +3 4552 4532 3971 +3 4533 4552 4551 +3 4538 4537 4539 +3 4540 4541 5727 +3 5727 4541 4534 +3 4542 4534 4541 +3 4543 4534 4542 +3 4535 4534 4544 +3 4544 4534 4543 +3 3969 4535 4545 +3 4535 4544 4545 +3 4546 3970 4545 +3 4546 4547 3970 +3 3970 4547 4548 +3 4548 4560 3971 +3 4552 3971 4550 +3 4550 3971 4560 +3 4538 4554 4537 +3 4537 4554 4555 +3 4556 4537 4555 +3 4539 4537 4556 +3 5179 4541 4540 +3 5157 4541 5179 +3 4557 4541 5157 +3 4541 4557 4542 +3 4543 4542 4558 +3 4558 4542 4557 +3 4544 4543 4558 +3 4545 4544 4558 +3 4558 5159 4545 +3 4546 4545 5159 +3 4559 4548 4549 +3 4560 4548 4559 +3 4552 4550 4561 +3 4561 4550 4560 +3 4551 4552 4561 +3 4553 4551 4562 +3 4562 4551 4561 +3 4562 4563 4553 +3 4564 4565 4566 +3 5154 4556 4554 +3 4555 4554 4556 +3 4558 4557 5171 +3 5172 4558 5171 +3 4560 4559 4561 +3 4562 4561 4567 +3 4568 4562 4567 +3 4569 4563 4562 +3 4568 4569 4562 +3 5168 5177 4564 +3 5168 4570 5177 +3 4565 4564 5177 +3 4561 4559 4580 +3 4580 4567 4561 +3 4571 4568 4567 +3 4572 4573 4574 +3 4569 4568 4571 +3 4572 4574 4575 +3 4577 5174 4576 +3 4578 3972 4579 +3 5177 4570 5192 +3 4577 4576 4581 +3 4581 5187 4577 +3 5213 4579 4583 +3 5213 4582 4579 +3 4582 4578 4579 +3 3972 4583 4579 +3 3972 3974 4583 +3 3973 3974 3972 +3 4584 4586 4587 +3 4585 4584 4587 +3 4589 4590 4585 +3 4585 4590 4584 +3 4584 4590 4591 +3 4586 4584 4592 +3 4592 4584 4591 +3 4593 4587 4586 +3 4592 4593 4586 +3 3977 4600 4588 +3 4589 4594 3976 +3 3976 4595 4589 +3 4595 4590 4589 +3 4591 4590 4596 +3 4596 4590 4595 +3 4596 4592 4591 +3 4593 4592 4596 +3 4589 5235 4597 +3 4589 4597 4594 +3 4594 4597 4598 +3 3976 4594 4599 +3 4594 4598 4599 +3 3976 4599 4595 +3 4599 5238 4595 +3 5238 4596 4595 +3 4582 5213 5240 +3 4600 3977 3979 +3 3979 3980 4600 +3 4600 3980 4601 +3 4602 4597 5235 +3 4598 4597 4603 +3 4603 4597 4602 +3 4599 4598 5238 +3 5238 4598 4603 +3 4582 5240 3986 +3 4600 3981 4604 +3 4600 3982 3981 +3 3982 4600 4605 +3 4605 4600 4601 +3 5237 4605 4601 +3 5237 4601 2180 +3 4606 5235 4635 +3 5235 4606 4602 +3 4606 4603 4602 +3 5238 4603 4606 +3 4604 3984 4607 +3 3981 3984 4604 +3 4608 4604 4609 +3 4609 4604 4607 +3 4605 4608 4609 +3 5237 4608 4605 +3 4635 4610 4606 +3 4606 4610 4615 +3 3985 4607 3984 +3 4609 4607 4611 +3 4609 4611 4605 +3 4619 4612 5237 +3 4635 4613 4610 +3 4610 4613 4614 +3 4615 4610 4614 +3 5241 4606 4615 +3 4616 3986 5240 +3 4616 4617 3986 +3 3986 4617 3990 +3 3985 3991 4618 +3 4607 3985 4618 +3 4611 4607 4618 +3 4620 4612 4619 +3 5237 4051 4619 +3 4635 3994 4613 +3 4621 4613 3994 +3 4621 4614 4613 +3 4615 4614 5241 +3 5241 4614 4621 +3 4622 4616 5240 +3 4623 4616 4622 +3 3999 4623 3998 +3 3999 4617 4623 +3 4617 4616 4623 +3 4012 4617 3999 +3 4012 4624 4617 +3 3990 4617 4001 +3 4001 4617 4624 +3 3991 4625 4618 +3 4003 4625 4004 +3 4003 4611 4625 +3 4611 4618 4625 +3 4626 4627 4620 +3 4626 4619 4628 +3 4620 4619 4626 +3 4051 4628 4619 +3 4635 4006 3994 +3 4622 5240 4629 +3 4011 4629 4010 +3 4011 4622 4629 +3 4011 4623 4622 +3 3998 4623 4011 +3 4001 4624 4012 +3 4013 4625 3991 +3 4013 4630 4625 +3 4004 4625 4014 +3 4014 4625 4630 +3 4014 4631 4015 +3 4630 4631 4014 +3 4015 4631 4016 +3 4632 4627 4626 +3 4632 4626 4633 +3 4022 4642 4621 +3 4642 5241 4621 +3 4631 4630 4013 +3 4632 4634 4627 +3 4633 4634 4632 +3 4031 4006 4635 +3 4642 4022 4025 +3 4010 4629 4030 +3 4629 4032 4030 +3 4025 3462 4642 +3 4031 4635 3496 +3 4643 4647 4629 +3 4036 4039 5246 +3 4036 5246 4636 +3 5246 4637 4636 +3 4037 4638 4040 +3 4037 4636 4638 +3 4636 4637 4638 +3 5245 4637 5246 +3 4044 5245 4043 +3 4044 4637 5245 +3 4044 4638 4637 +3 4639 4046 5245 +3 4046 4043 5245 +3 4640 4039 4047 +3 4640 4641 4039 +3 4641 5246 4039 +3 4639 5249 4047 +3 4639 4047 4046 +3 4640 4047 5249 +3 4645 4644 4640 +3 4641 4640 4644 +3 5249 4645 4640 +3 4650 4644 4646 +3 4646 4644 4645 +3 4648 4649 4650 +3 4650 4646 4648 +3 4048 4651 4049 +3 4048 4652 4653 +3 4048 4049 4652 +3 4649 4653 4652 +3 4649 4648 4653 +3 4648 4656 4653 +3 4050 4647 4643 +3 4654 4655 4048 +3 4655 4651 4048 +3 4048 4653 4654 +3 4654 4653 4656 +3 4654 4657 4655 +3 4656 4657 4654 +3 5261 5256 4642 +3 4658 5261 4052 +3 5269 4050 5268 +3 5266 4659 4658 +3 4052 5266 4658 +3 4053 5269 5274 +3 5274 4660 4053 +3 4053 4660 4661 +3 4054 4053 4661 +3 4662 4660 5274 +3 4662 4661 4660 +3 4661 4061 4054 +3 4663 5285 4058 +3 4663 5292 5285 +3 4059 4055 4664 +3 4056 4060 4664 +3 4055 4056 4664 +3 4661 4662 4061 +3 4664 4665 4059 +3 4060 4062 4665 +3 4060 4665 4664 +3 4062 4061 4665 +3 4665 4061 4666 +3 4666 4061 4662 +3 5292 4663 4058 +3 4668 4064 4065 +3 4065 4066 4669 +3 4066 4059 4669 +3 4665 4670 4059 +3 5308 4670 4665 +3 5308 4665 4666 +3 5292 4058 4672 +3 4065 4669 4668 +3 4059 4670 4669 +3 4067 4671 5306 +3 5306 3551 4067 +3 5306 4070 3551 +3 4668 4673 4064 +3 4673 4676 4064 +3 4669 4674 4668 +3 4674 4673 4668 +3 4670 4677 4669 +3 4677 4674 4669 +3 4675 4677 4670 +3 4675 4670 5308 +3 4067 5316 4671 +3 5306 4679 4070 +3 4674 4677 4673 +3 5319 4677 4675 +3 4070 4679 5317 +3 4673 4077 4676 +3 4077 4673 4677 +3 4078 4680 4678 +3 4680 4681 4678 +3 4079 4678 4681 +3 4678 4079 4075 +3 5316 4080 5320 +3 4070 5317 5320 +3 4080 4070 5320 +3 4682 4683 4684 +3 5314 4682 4684 +3 4677 5327 4077 +3 4078 4685 4680 +3 4685 4686 4680 +3 4681 4680 4686 +3 4079 4681 4686 +3 4088 5320 4080 +3 5320 4088 4080 +3 4682 5324 4687 +3 4682 4687 4688 +3 4683 4682 4688 +3 5326 4076 5338 +3 4695 3592 4689 +3 3592 4690 4689 +3 4077 4696 4690 +3 4696 4077 5327 +3 4100 5327 5325 +3 5325 4697 4100 +3 4078 4091 4692 +3 4091 4691 4692 +3 4685 4078 4693 +3 4693 4078 4692 +3 4094 5331 4093 +3 4094 4685 5331 +3 4094 4686 4685 +3 4685 4693 5331 +3 4096 4095 5341 +3 4688 4687 4694 +3 4683 4688 4694 +3 4684 4683 3635 +3 4689 4722 4695 +3 3628 4697 4698 +3 4699 4091 4101 +3 4699 4700 4091 +3 4700 4691 4091 +3 4701 4699 4101 +3 4701 4101 5331 +3 4093 5331 4101 +3 4095 4111 5341 +3 5341 4103 4096 +3 5318 5298 4715 +3 4715 5298 4672 +3 5318 4715 4716 +3 4687 5324 4703 +3 4694 4687 4703 +3 4722 4721 4695 +3 4689 4705 4722 +3 4690 4706 4689 +3 4706 4690 4696 +3 3628 4698 4128 +3 4707 4708 4109 +3 4709 4700 4711 +3 4710 4700 4709 +3 4712 4699 4701 +3 4712 4711 4699 +3 4700 4699 4711 +3 4713 5341 4111 +3 4713 4110 5341 +3 4110 4103 5341 +3 4715 4672 4714 +3 4694 4703 4168 +3 4104 4733 4732 +3 4720 4719 4108 +3 4736 4722 4705 +3 4689 4723 4705 +3 4723 4736 4705 +3 4706 4723 4689 +3 4723 4706 4724 +3 4708 4725 4109 +3 4708 4726 4725 +3 4710 4709 4726 +3 4709 4727 4726 +3 4711 4728 4709 +3 4728 4727 4709 +3 4728 4711 4712 +3 4111 4110 4713 +3 4132 4741 4714 +3 4715 4714 4741 +3 4716 4715 4729 +3 4716 4729 4742 +3 4718 4717 4730 +3 4703 4718 4168 +3 4731 4104 4732 +3 4719 4141 4108 +3 4721 4734 4704 +3 4125 4721 4735 +3 4735 4721 4722 +3 4736 4785 4722 +3 4724 4738 4723 +3 4738 4737 4723 +3 4724 4749 4738 +3 4136 4750 4100 +3 4136 4100 4114 +3 4109 5374 4707 +3 4726 4752 4725 +3 4727 4739 4726 +3 4739 4752 4726 +3 4739 4728 4712 +3 4739 4727 4728 +3 4715 4741 4759 +3 4729 4715 4742 +3 4742 4715 4759 +3 4742 4761 4762 +3 4764 4730 4717 +3 4718 4730 4764 +3 4168 4718 4764 +3 4732 4745 4731 +3 4733 4745 4732 +3 4766 4122 4746 +3 4768 4719 4720 +3 4704 4769 4720 +3 4769 4768 4720 +3 4734 4769 4704 +3 4734 4125 4747 +3 4734 4747 4769 +3 4125 4735 4747 +3 4737 4738 4748 +3 4749 4748 4738 +3 4749 4750 4751 +3 4136 4751 4750 +3 4128 4771 4142 +3 4725 4752 4138 +3 4754 4755 4140 +3 4755 4756 4131 +3 4131 4756 4740 +3 4756 4757 4740 +3 4131 4776 4132 +3 4131 4740 4776 +3 4740 4757 4776 +3 4776 4758 4132 +3 4758 4777 4741 +3 4759 4741 4777 +3 4760 4742 4759 +3 4761 4742 4760 +3 4743 4763 4764 +3 4744 4743 4764 +3 4717 4744 4764 +3 5379 5338 3683 +3 4780 4731 4765 +3 4765 4731 4745 +3 4733 4780 4745 +3 4780 4765 4745 +3 4733 4133 4780 +3 4746 4781 4766 +3 4782 4169 4746 +3 4719 4767 4141 +3 4747 4770 4769 +3 4735 4770 4747 +3 4722 4785 4735 +3 4785 4770 4735 +3 4748 5372 4737 +3 4749 5372 4748 +3 5372 4749 4154 +3 4749 4751 4154 +3 4136 4154 4751 +3 4144 4772 4773 +3 4147 4144 4773 +3 4773 4753 4147 +3 4774 3660 4753 +3 3660 4147 4753 +3 4140 4150 4754 +3 4151 4754 4150 +3 4755 4754 4151 +3 4775 4755 4151 +3 4775 4756 4755 +3 4775 4757 4756 +3 4777 4758 4776 +3 4759 4778 4779 +3 4153 4779 4166 +3 4153 4760 4779 +3 4760 4759 4779 +3 4152 4760 4153 +3 4152 4762 4760 +3 4762 4761 4760 +3 4168 4764 4763 +3 4133 4134 4780 +3 4746 4169 4781 +3 4767 4783 4141 +3 4719 4784 4767 +3 4784 4783 4767 +3 4768 4784 4719 +3 5369 4784 4768 +3 5369 4768 4769 +3 4770 4798 4769 +3 4155 4771 4154 +3 4142 4771 4155 +3 4786 5394 5374 +3 4786 5374 4156 +3 4787 4788 4158 +3 5385 4788 4787 +3 4158 4788 4772 +3 4158 4772 4144 +3 4753 4789 4774 +3 4790 4775 4171 +3 4166 4779 4778 +3 4166 4778 4167 +3 4167 4778 4791 +3 4792 5354 4180 +3 4794 5354 4792 +3 4793 4794 4792 +3 4763 4794 4795 +3 4134 4803 4780 +3 4796 4803 4134 +3 4766 4796 4134 +3 4781 4796 4766 +3 4169 4796 4781 +3 6695 4169 4782 +3 6695 4782 4141 +3 4783 6695 4141 +3 4783 4784 4797 +3 5369 4804 4784 +3 4804 4797 4784 +3 4805 4804 5369 +3 5388 4798 4770 +3 4785 5388 4770 +3 4799 5385 4787 +3 4158 4800 4787 +3 4800 4799 4787 +3 4774 4789 4801 +3 4802 4774 4801 +3 4791 4171 4167 +3 4181 4792 4180 +3 4794 4793 4184 +3 4795 4794 4184 +3 5379 3683 4823 +3 4812 4803 4796 +3 4797 4804 4783 +3 4786 4806 5394 +3 4807 4816 5385 +3 5385 4799 4807 +3 4799 4808 4807 +3 4800 4809 4799 +3 4809 4808 4799 +3 4810 4809 4800 +3 4810 4800 4172 +3 4801 4789 4172 +3 4181 4183 4792 +3 4183 4793 4792 +3 4184 4793 4183 +3 4763 4795 4184 +3 4174 4811 4176 +3 4176 4811 4178 +3 4838 4780 4803 +3 4812 4838 4803 +3 3685 4813 4179 +3 4814 4179 4813 +3 5406 5394 4806 +3 4808 4817 4807 +3 4817 4816 4807 +3 4820 4817 4809 +3 4817 4808 4809 +3 4810 4820 4809 +3 4811 4174 4177 +3 4178 4811 4177 +3 3685 4818 4813 +3 4179 4814 4825 +3 4185 4825 4186 +3 4185 4179 4825 +3 4815 4827 4819 +3 4815 4187 4827 +3 4816 4189 4815 +3 4820 4189 4816 +3 4817 4820 4816 +3 4822 4192 4191 +3 4812 4824 4838 +3 3685 4835 4818 +3 4819 4826 5433 +3 4827 4826 4819 +3 4187 4198 4827 +3 4189 4820 4833 +3 4191 4199 4821 +3 4822 4199 4192 +3 4835 5430 4818 +3 4207 4186 4825 +3 4836 4207 4825 +3 4197 4207 4201 +3 4186 4207 4197 +3 5433 4829 5439 +3 5433 4826 4829 +3 4827 4830 4826 +3 4830 4829 4826 +3 4198 4831 4827 +3 4827 4831 4830 +3 4832 4831 4198 +3 5434 4838 4824 +3 5434 4824 4828 +3 4204 4834 4205 +3 4834 4206 4205 +3 4200 5437 4835 +3 4200 4836 5437 +3 4836 4200 4207 +3 4830 5439 4829 +3 4831 5439 4830 +3 4832 5439 4831 +3 4834 4841 4839 +3 4204 4841 4834 +3 4839 4206 4834 +3 4209 4211 4837 +3 4422 6658 1262 +3 4215 5445 4216 +3 5445 4217 4216 +3 4840 4217 5445 +3 4219 4849 6840 +3 5455 4212 4219 +3 5455 4219 6840 +3 4213 5455 3733 +3 4215 4221 5445 +3 5445 4221 5448 +3 4217 4840 4842 +3 4842 4222 4217 +3 5449 4222 4842 +3 4225 4849 4219 +3 3733 5455 4236 +3 4226 5448 4221 +3 4227 5449 4226 +3 4226 5449 5448 +3 5449 4227 4222 +3 4843 4841 4869 +3 4841 4223 4869 +3 4846 5462 4231 +3 4233 4848 4245 +3 4847 4848 4233 +3 4225 4235 4849 +3 4236 5455 4856 +3 4231 4239 4846 +3 4231 5462 4242 +3 5462 4859 4242 +3 4233 4244 4847 +3 4244 4851 4847 +3 4851 4848 4847 +3 4245 4848 4851 +3 4246 4245 4852 +3 4247 5468 4853 +3 4247 4246 5468 +3 4246 4852 5468 +3 5468 4854 4853 +3 4864 6882 4849 +3 4235 4248 4855 +3 4235 4855 4849 +3 4855 4248 4856 +3 4856 4248 4236 +3 4223 4228 4869 +3 4859 4256 4242 +3 4244 4250 4851 +3 4250 5468 4851 +3 4853 4250 4247 +3 4853 4854 4250 +3 4854 5468 4250 +3 4860 4861 4862 +3 4861 4863 4862 +3 4855 4864 4849 +3 4865 4858 4857 +3 4867 4857 4858 +3 4867 4866 4857 +3 4866 4865 4857 +3 4869 4239 4870 +3 4859 5482 4256 +3 4868 4860 4862 +3 4863 4868 4862 +3 4867 4865 4866 +3 5481 4870 4260 +3 4262 4256 5482 +3 4259 4276 4871 +3 4276 4873 4871 +3 4263 4871 4873 +3 4263 4265 4264 +3 4873 4265 4263 +3 5481 4260 4270 +3 5481 4270 5483 +3 4262 5482 5484 +3 4267 5484 4875 +3 4267 4262 5484 +3 3748 4872 4269 +3 4872 4876 4269 +3 4265 4881 4277 +3 4265 4873 4881 +3 4270 4879 5483 +3 5484 4879 4878 +3 5484 4878 4875 +3 4875 4878 4880 +3 4267 4880 4271 +3 4875 4880 4267 +3 5489 4876 3766 +3 4876 4872 3766 +3 4876 5491 4269 +3 4272 4269 5491 +3 4272 5491 4274 +3 4873 4276 5504 +3 4277 4881 5493 +3 4302 4277 5495 +3 4277 5493 5495 +3 5506 4874 3807 +3 4290 4883 5497 +3 5497 5486 4290 +3 4270 4293 4879 +3 4293 4878 4879 +3 4880 4878 4884 +3 4884 4878 4293 +3 4271 4884 4280 +3 4880 4884 4271 +3 4281 4898 5489 +3 3766 4281 5489 +3 4283 5491 4885 +3 4274 5491 4283 +3 4276 4299 4892 +3 4892 5504 4276 +3 3807 4894 5506 +3 3807 4350 4894 +3 4290 4289 4883 +3 4886 4290 5486 +3 5498 4887 5486 +3 4887 4886 5486 +3 5498 5507 4887 +3 4281 4304 4898 +3 4885 5491 4311 +3 4888 5518 4890 +3 4889 5518 4888 +3 4890 5518 4891 +3 4893 5495 5519 +3 4302 5495 4893 +3 4287 4882 3819 +3 4288 4895 4289 +3 4886 4895 4288 +3 4896 4895 4886 +3 4887 4912 4886 +3 4897 4887 5507 +3 4897 4912 4887 +3 4291 4292 5522 +3 3821 4291 5522 +3 3813 4899 4307 +3 4307 4899 4308 +3 4309 4899 3813 +3 4309 4900 4901 +3 4309 4901 4899 +3 4901 4900 4902 +3 4311 5491 5503 +3 4311 5503 4310 +3 4310 5503 5526 +3 4888 4903 4889 +3 4890 4903 4888 +3 4904 4903 4890 +3 4891 4904 4890 +3 4905 4904 4891 +3 5518 4905 4891 +3 4299 4313 4906 +3 4892 4299 4906 +3 5519 4892 4906 +3 4315 5519 4314 +3 5519 4315 4893 +3 3815 4893 4315 +3 3815 4907 4893 +3 4316 4893 4907 +3 4325 4921 4909 +3 5505 4911 4910 +3 5528 4911 5505 +3 4894 4350 5544 +3 4963 4877 4237 +3 4896 4886 4912 +3 4897 4913 4912 +3 4355 3821 5522 +3 4898 4914 5525 +3 5531 4327 4308 +3 4900 4328 4915 +3 4309 4328 4900 +3 4916 4902 4900 +3 4916 4900 4917 +3 4917 4900 4915 +3 4917 5526 4916 +3 4917 4310 5526 +3 4313 4322 4906 +3 4906 4314 5519 +3 3815 4918 4907 +3 4918 4316 4907 +3 4316 4918 4919 +3 4908 4317 4920 +3 4920 4317 4919 +3 4926 4921 4908 +3 4926 4908 4920 +3 4317 4908 4921 +3 4317 4921 4324 +3 4921 4325 4324 +3 4927 4325 4909 +3 4909 4910 4927 +3 4928 4927 4910 +3 5528 5532 4911 +3 4882 4336 3819 +3 4913 4922 4912 +3 4914 4898 4935 +3 4304 4924 4898 +3 4924 4935 4898 +3 4936 4924 4304 +3 4936 4304 4339 +3 5531 5550 4327 +3 4329 4915 4328 +3 4917 4915 4329 +3 4310 4917 4329 +3 4918 3815 4925 +3 4919 4918 4925 +3 4925 4926 4920 +3 4925 4920 4919 +3 4325 4927 4334 +3 4910 4335 4928 +3 4342 4911 4929 +3 4929 4911 5532 +3 4930 4931 5548 +3 5548 4932 4930 +3 4913 4923 4933 +3 3795 4952 4292 +3 4338 3821 4355 +3 4935 4924 4934 +3 4936 4934 4924 +3 4339 4937 4936 +3 4339 4938 4937 +3 4339 4327 5550 +3 5541 4925 4348 +3 4334 4927 4349 +3 4335 4363 4928 +3 4939 5554 4940 +3 4929 5532 4342 +3 4940 4941 4939 +3 4342 5544 4350 +3 4342 5532 5544 +3 4962 4336 4882 +3 3848 4336 4962 +3 4942 4931 4930 +3 4943 4931 4942 +3 4944 4930 4932 +3 4942 4930 4944 +3 4945 4922 4337 +3 4954 4935 4934 +3 4936 4946 4934 +3 4946 4954 4934 +3 4946 4936 4937 +3 4339 4947 4938 +3 5550 4947 4339 +3 4348 4948 5541 +3 4948 4348 4949 +3 4949 4333 4349 +3 4950 5553 4960 +3 5553 4950 5564 +3 4940 5554 4951 +3 4941 4940 4951 +3 4942 4944 4943 +3 3795 4352 4952 +3 4946 4955 4954 +3 4955 4946 5573 +3 4948 4959 4956 +3 4948 4949 4959 +3 4349 4959 4949 +3 4928 4363 4960 +3 4960 4363 4950 +3 4961 5564 4950 +3 3848 4962 4357 +3 4964 4945 4337 +3 4360 4346 4953 +3 5572 4954 4957 +3 4955 4958 4954 +3 4958 4957 4954 +3 4958 4955 5573 +3 4961 4950 4365 +3 4966 4945 4964 +3 4933 5592 4379 +3 4380 4952 4352 +3 4360 4953 4382 +3 4371 5577 4957 +3 4371 4957 4958 +3 4957 5577 5572 +3 4958 5573 4371 +3 5573 5577 4371 +3 4965 4961 4365 +3 3857 4965 4365 +3 4962 4971 4372 +3 4966 4964 4973 +3 4964 4967 4973 +3 4366 4377 4967 +3 4964 4366 4967 +3 4381 4952 4380 +3 4382 4953 4381 +3 4371 4383 5577 +3 5577 4383 5582 +3 4371 5577 4383 +3 5577 5582 4383 +3 4981 4968 4965 +3 3857 4981 4965 +3 3857 4394 4981 +3 4969 4970 4384 +3 4969 4384 4372 +3 4971 4969 4372 +3 4971 4972 4969 +3 4973 4385 4403 +3 4973 4386 4385 +3 4967 4387 4973 +3 4377 4387 4967 +3 5590 4974 4389 +3 4389 4975 5590 +3 4378 4975 4389 +3 4379 5592 4392 +3 5592 4985 4392 +3 4383 4393 5582 +3 5582 4393 5605 +3 4383 5582 4401 +3 5582 4986 4401 +3 4968 4978 4976 +3 4979 4978 4968 +3 4993 4968 4980 +3 4993 4979 4968 +3 4981 4980 4968 +3 4394 4396 4981 +3 4969 4994 4970 +3 4994 4969 4982 +3 4969 4972 4982 +3 4983 4972 4971 +3 4983 4984 4972 +3 4984 4982 4972 +3 4975 4378 4391 +3 4975 4391 5597 +3 5597 4391 4397 +3 4985 4416 4392 +3 4393 4400 5605 +3 4986 4402 4401 +3 5599 4402 4986 +3 4987 4988 4989 +3 4989 5600 4987 +3 4990 4977 4976 +3 4991 4990 4976 +3 4978 4992 4976 +3 4992 4991 4976 +3 4979 4992 4978 +3 4984 4995 4982 +3 4995 4994 4982 +3 5603 4984 4983 +3 4995 4984 5603 +3 5597 4397 4419 +3 5009 5597 4419 +3 4402 5599 4412 +3 4987 4997 4988 +3 5600 4997 4987 +3 4977 4990 4998 +3 4991 4999 4990 +3 4992 5000 4991 +3 5000 4999 4991 +3 5007 4979 4993 +3 5007 5000 4979 +3 5000 4992 4979 +3 4994 4995 5008 +3 5008 4995 5603 +3 4974 5590 4415 +3 5613 4416 4985 +3 4410 5605 4400 +3 4410 5001 5002 +3 4410 5002 5605 +3 4412 5001 5003 +3 4412 5599 5001 +3 5002 5001 5599 +3 4412 5003 5012 +3 5006 4996 5606 +3 5006 5005 4996 +3 5005 5004 4996 +3 4998 4418 4977 +3 5000 5007 4999 +3 4415 7464 4403 +3 5009 5010 5011 +3 4416 5010 5017 +3 4416 5613 5010 +3 5011 5010 5613 +3 4416 5017 4420 +3 4410 4417 5001 +3 4417 5012 5001 +3 5012 5003 5001 +3 5005 5013 5004 +3 5014 5013 5005 +3 5006 5014 5005 +3 4977 5015 5016 +3 4977 4418 5015 +3 4999 5007 4425 +3 4990 4999 4425 +3 4419 5025 5009 +3 5025 5010 5009 +3 5017 5010 5025 +3 4420 5017 5018 +3 5018 5019 4420 +3 5019 5020 4420 +3 5012 4417 5021 +3 5015 4434 5016 +3 5015 4423 4434 +3 4423 5015 5022 +3 5022 5015 4418 +3 4418 4424 5022 +3 5023 4427 4426 +3 5023 5024 4427 +3 4419 4427 5024 +3 4419 5024 5025 +3 4428 5025 4438 +3 4428 5018 5025 +3 5018 5017 5025 +3 4439 5018 4428 +3 4439 5026 5018 +3 5026 5019 5018 +3 5020 5019 5027 +3 5027 5019 5026 +3 4421 5027 4429 +3 5020 5027 4421 +3 5028 5021 4417 +3 4431 4430 5029 +3 5029 5030 4431 +3 4431 5030 4433 +3 5016 4434 5031 +3 5031 4434 4435 +3 5022 4436 4423 +3 5022 5033 4436 +3 5033 5032 4436 +3 4424 5033 5022 +3 4424 4437 5033 +3 4437 5034 5033 +3 5007 5628 4425 +3 5023 4426 5634 +3 5025 5024 4448 +3 4438 5025 4448 +3 4449 5026 4439 +3 4449 5027 5026 +3 4429 5027 4440 +3 4417 4441 5028 +3 4430 4442 5029 +3 4443 5029 4442 +3 5030 5029 4443 +3 4433 5030 4443 +3 4435 5035 5031 +3 4444 5035 4435 +3 4436 5032 4444 +3 4444 5032 5035 +3 4437 4445 5034 +3 5034 4445 5036 +3 4440 5027 4449 +3 4467 4425 5628 +3 5037 4447 4448 +3 5634 4447 5037 +3 5038 5046 5045 +3 5047 5046 5038 +3 4456 4455 5047 +3 4445 4459 5036 +3 4467 5036 4459 +3 5048 5041 5040 +3 5049 5041 5048 +3 4461 5050 5051 +3 4462 4461 5052 +3 4461 5051 5052 +3 5053 4462 5052 +3 4452 5075 5054 +3 5042 4452 5055 +3 5055 4452 5054 +3 4453 5055 4473 +3 5042 5055 4453 +3 5043 5044 5056 +3 5044 5067 5056 +3 5045 5058 5044 +3 5058 5067 5044 +3 5046 5058 5045 +3 5047 5059 5046 +3 5059 5058 5046 +3 4455 4463 5059 +3 5047 4455 5059 +3 4456 5047 5641 +3 5641 4457 4456 +3 5641 4465 4457 +3 4467 4459 4466 +3 5048 5060 5049 +3 5050 4461 4479 +3 5050 4479 5071 +3 4462 5053 4470 +3 5053 5061 4470 +3 5063 4471 5062 +3 5063 5640 4471 +3 4472 4471 5640 +3 5075 5064 5054 +3 5055 5054 5065 +3 5065 5054 5064 +3 5055 5065 4473 +3 4475 4474 5066 +3 5056 5066 5057 +3 5056 4475 5066 +3 5067 4475 5056 +3 5058 5068 5067 +3 4463 5068 5059 +3 5059 5068 5058 +3 4465 5641 4478 +3 5641 5069 4478 +3 4479 5070 5071 +3 4481 5070 5072 +3 4481 5061 5070 +3 4481 4470 5061 +3 5071 5070 5061 +3 4481 5062 4471 +3 4481 5072 5062 +3 5072 5073 5062 +3 5074 5063 5062 +3 5073 5074 5062 +3 5642 4472 5640 +3 4472 5642 4492 +3 4486 5064 5075 +3 4487 5064 4486 +3 5065 5064 4487 +3 4473 5065 4487 +3 5066 4474 5076 +3 4479 4488 5070 +3 4488 5077 5070 +3 5077 5072 5070 +3 5073 5072 5078 +3 5078 5072 5077 +3 5644 5074 5073 +3 5078 5644 5073 +3 5644 5079 5642 +3 4492 5642 5080 +3 5642 5079 5080 +3 4486 5075 4493 +3 4488 4495 5077 +3 4489 5077 4495 +3 5078 5077 4489 +3 5078 4489 5644 +3 4489 4497 5644 +3 4497 5084 5079 +3 4497 5079 5644 +3 5085 5647 5080 +3 5085 5080 5084 +3 5084 5080 5079 +3 5647 4492 5080 +3 4501 5086 4502 +3 4500 5086 4501 +3 4502 5086 4506 +3 5076 4474 5087 +3 5087 5081 5076 +3 3926 5082 5088 +3 5083 3931 5109 +3 5089 5083 5096 +3 4503 5090 4504 +3 5096 5090 4503 +3 4497 4505 5084 +3 5084 4505 5085 +3 5086 4500 5092 +3 5093 5086 5092 +3 4506 5086 5093 +3 4506 5093 5094 +3 5083 5089 5095 +3 5096 5097 5089 +3 5097 5095 5089 +3 5097 5096 4503 +3 5666 5090 5096 +3 5090 5666 4512 +3 5098 4505 4507 +3 5098 5654 4505 +3 5654 5085 4505 +3 4507 5091 5098 +3 5098 5091 5099 +3 5099 5091 5104 +3 5100 5101 5102 +3 5102 5103 5100 +3 5092 4500 5118 +3 5098 4508 5654 +3 4508 5659 5654 +3 5099 3944 5098 +3 3944 5099 5104 +3 4510 5104 5105 +3 5100 5108 5101 +3 5103 5108 5100 +3 5118 4500 5117 +3 5109 3952 4509 +3 3931 3952 5109 +3 3936 5666 5659 +3 5659 4508 3936 +3 4515 4510 5105 +3 4515 5105 5106 +3 5110 5106 5107 +3 5110 5111 5106 +3 5111 4515 5106 +3 5125 4511 4514 +3 5666 3936 4512 +3 4515 5111 5127 +3 5120 5119 5112 +3 5112 5672 5120 +3 5121 5674 5114 +3 5114 5113 5121 +3 5113 5122 5121 +3 5122 5113 5114 +3 4511 5125 4513 +3 5125 4514 4518 +3 5126 5125 4518 +3 5128 5681 5118 +3 5118 5117 5128 +3 5117 5129 5128 +3 5130 5129 5117 +3 5119 5131 5135 +3 5119 5120 5131 +3 5682 5120 5672 +3 5131 5120 5682 +3 5121 5686 5674 +3 5122 5686 5121 +3 5116 5115 5132 +3 5123 4517 5132 +3 5123 5132 5115 +3 5123 5124 4516 +3 5124 5133 4516 +3 4513 5125 5697 +3 4518 5680 5126 +3 5127 5134 4515 +3 5681 5129 4522 +3 5681 5128 5129 +3 5682 5135 5131 +3 4517 4519 5136 +3 5132 4517 5136 +3 4516 5133 4520 +3 5133 5137 4520 +3 4513 5697 4521 +3 5680 4518 5138 +3 5138 4518 4525 +3 5681 4522 5134 +3 4519 4528 5136 +3 4523 4520 5137 +3 4527 5138 4525 +3 4527 5139 5138 +3 4528 5712 5136 +3 4530 4523 5137 +3 5712 4530 5137 +3 5141 4527 4521 +3 4527 5141 5142 +3 4527 5142 5143 +3 5139 4527 5144 +3 5144 4527 5143 +3 5699 4531 5140 +3 5699 5708 4531 +3 5708 5715 4531 +3 4529 5145 4528 +3 4528 5145 5716 +3 5716 5712 4528 +3 4529 4531 5715 +3 4529 5715 5145 +3 4536 5146 4534 +3 5147 5146 4536 +3 4533 5148 4536 +3 5148 5147 4536 +3 5149 5148 4533 +3 4551 5152 4533 +3 5727 4539 4540 +3 4547 4546 5150 +3 4547 5150 5146 +3 4547 5146 5147 +3 4548 4547 5147 +3 4548 5147 5148 +3 5149 5160 5151 +3 5151 4549 5149 +3 5148 5149 4548 +3 4548 5149 4549 +3 4553 5152 4551 +3 5164 5152 4553 +3 5153 5154 4538 +3 4554 4538 5154 +3 4539 5155 4540 +3 5155 5156 4540 +3 5157 5158 4557 +3 5160 5150 4546 +3 5160 4546 5159 +3 5160 5159 5161 +3 5161 5162 5160 +3 5160 5162 5151 +3 5151 5162 5163 +3 4549 5163 4559 +3 5151 5163 4549 +3 5164 4563 5165 +3 5164 4553 4563 +3 5165 5166 5167 +3 5164 5165 5167 +3 5153 5168 4564 +3 4566 5153 4564 +3 5154 5153 4566 +3 4565 5154 4566 +3 4565 5169 5154 +3 4556 5154 5169 +3 5169 5155 4556 +3 4556 5155 4539 +3 5156 5155 5170 +3 4540 5156 5170 +3 5179 4540 5170 +3 4557 5158 5171 +3 5158 5157 5171 +3 4558 5172 5159 +3 5161 5159 5172 +3 5163 5162 5184 +3 5184 5162 5161 +3 5163 5184 4559 +3 5165 4569 5173 +3 4563 4569 5165 +3 5166 5165 5173 +3 5768 5167 5166 +3 5768 5166 5174 +3 5174 4577 5768 +3 5175 5176 5153 +3 5168 5153 5176 +3 5168 5176 4570 +3 4565 5177 5169 +3 5155 5169 5783 +3 5170 5155 5783 +3 5179 5170 5178 +3 5157 5179 5180 +3 5171 5157 5181 +3 5181 5157 5180 +3 5172 5171 5181 +3 5183 5172 5182 +3 5161 5172 5183 +3 5184 5161 5183 +3 4567 5185 4571 +3 4571 5173 4569 +3 4571 5185 5173 +3 4572 5173 4573 +3 4573 5173 5185 +3 4575 5173 4572 +3 4575 5186 5173 +3 5186 5166 5173 +3 5174 5186 4576 +3 5174 5166 5186 +3 4577 5187 5768 +3 5188 5189 5175 +3 5217 5188 5175 +3 5175 5189 5190 +3 5176 5175 5191 +3 5191 5175 5190 +3 4570 5176 5192 +3 5176 5191 5192 +3 5192 5193 5177 +3 5204 5177 5193 +3 5169 5177 5204 +3 5179 5178 5205 +3 5180 5179 5195 +3 5195 5179 5194 +3 5196 5180 5195 +3 5181 5180 5196 +3 5784 5183 5182 +3 5784 5197 5183 +3 5184 5183 5197 +3 4559 5184 5209 +3 5209 5184 5197 +3 4559 5209 4580 +3 4580 5198 4567 +3 5185 4567 5199 +3 5199 4567 5198 +3 4573 5199 4574 +3 4573 5185 5199 +3 4574 5186 4575 +3 4574 5199 5186 +3 5199 5212 5186 +3 4576 5186 4581 +3 5186 5212 4581 +3 5214 4583 3974 +3 5217 5200 5201 +3 5188 5217 5201 +3 5189 5188 5202 +3 5202 5188 5201 +3 5190 5189 5203 +3 5203 5189 5202 +3 5191 5190 5193 +3 5193 5190 5203 +3 5192 5191 5193 +3 5194 5179 5205 +3 5206 5195 5205 +3 5205 5195 5194 +3 5206 5824 5195 +3 5195 5824 5196 +3 5181 5196 5207 +3 5207 5196 5824 +3 5172 5181 5207 +3 5182 5172 5207 +3 5223 5182 5207 +3 5208 5784 5182 +3 5208 5182 5223 +3 4580 5209 5825 +3 5825 5198 4580 +3 5198 5210 5199 +3 5211 5199 5210 +3 5212 5199 5211 +3 4581 5212 5224 +3 4581 5224 5225 +3 5225 5187 4581 +3 5213 4583 5214 +3 4585 4587 5215 +3 5216 5218 5217 +3 5217 5218 5200 +3 5201 5200 5219 +3 5219 5200 5218 +3 5202 5201 5220 +3 5220 5201 5219 +3 5193 5203 5220 +3 5220 5203 5202 +3 5221 5207 5857 +3 5222 5207 5221 +3 5223 5207 5222 +3 5208 5223 5228 +3 5197 5208 5228 +3 5209 5197 5228 +3 5211 5210 5233 +3 5212 5211 5229 +3 5229 5211 5233 +3 5224 5212 5229 +3 5187 5225 5230 +3 5879 5214 3974 +3 5879 3974 4588 +3 5226 4589 4585 +3 5215 5226 4585 +3 5215 4587 4593 +3 5215 4593 5226 +3 5227 5226 4593 +3 5217 5226 5227 +3 5217 5227 5216 +3 5216 5227 5231 +3 5218 5216 5231 +3 5219 5218 5231 +3 5220 5219 5231 +3 5222 5221 5857 +3 5223 5222 5228 +3 5228 5222 5857 +3 5825 5209 5228 +3 5233 5210 5232 +3 5224 5229 5234 +3 5225 5224 5234 +3 5230 5225 5234 +3 5226 5235 4589 +3 4596 5227 4593 +3 5825 5228 5874 +3 5210 5825 5874 +3 5234 5229 5233 +3 4600 5879 4588 +3 5227 4596 5238 +3 5238 5231 5227 +3 5233 5232 5234 +3 5879 4600 5236 +3 5236 4600 4604 +3 4608 5236 4604 +3 4608 5239 5236 +3 5237 5239 4608 +3 5237 4612 5239 +3 5238 4606 5241 +3 4628 5242 4626 +3 5242 4628 4051 +3 5243 4620 4627 +3 5242 5244 5243 +3 5242 5243 4633 +3 5242 4633 4626 +3 4051 5244 5242 +3 4627 4634 5243 +3 4633 5243 4634 +3 5244 4051 5964 +3 4643 4629 6015 +3 4639 5245 5246 +3 4641 5247 5248 +3 4641 5248 5246 +3 6028 5249 5248 +3 5246 5248 5249 +3 5246 5249 4639 +3 5247 5250 5251 +3 4641 5250 5247 +3 4641 4644 5250 +3 4645 5249 6028 +3 5250 4644 4650 +3 4645 6028 5252 +3 4645 5252 4646 +3 4650 4649 5250 +3 4649 5253 5250 +3 4646 5252 4648 +3 4049 4651 5253 +3 5253 4652 4049 +3 5253 4649 4652 +3 4648 5252 4656 +3 5256 6052 4642 +3 4651 4655 5253 +3 5254 4655 4657 +3 5254 6064 4655 +3 6064 5253 4655 +3 4657 4656 5255 +3 5254 4657 5255 +3 4656 5258 5255 +3 4656 6066 5258 +3 5254 5257 6064 +3 5257 5258 5259 +3 5257 5254 5258 +3 5254 5255 5258 +3 6053 5256 5265 +3 5259 5258 5262 +3 5260 4051 1262 +3 5265 5256 5261 +3 5259 5262 5264 +3 5264 5262 5263 +3 4658 5265 5261 +3 4658 4659 5265 +3 5265 4659 5267 +3 5267 4659 5266 +3 5268 4050 6143 +3 4052 5267 5266 +3 5270 5275 5268 +3 5275 5269 5268 +3 5276 5275 5270 +3 5276 5277 5275 +3 5278 5271 5273 +3 5278 5277 5271 +3 5272 5271 5276 +3 5276 5271 5277 +3 5267 4052 5285 +3 5269 5279 5274 +3 5275 5280 5269 +3 5280 5279 5269 +3 5280 5277 5281 +3 5280 5275 5277 +3 5278 5281 5277 +3 5279 5282 5274 +3 5282 5287 5274 +3 5283 5279 5284 +3 5283 5282 5279 +3 5280 5284 5279 +3 5274 5286 4662 +3 5287 5286 5274 +3 5282 5288 5287 +3 5288 5282 5283 +3 4662 5286 6230 +3 6230 5286 5287 +3 6241 4662 6230 +3 6241 5289 4662 +3 5290 5289 6241 +3 5290 5291 5295 +3 4666 4662 5289 +3 4666 5289 5302 +3 5296 5302 5289 +3 5290 5293 5289 +3 5293 5296 5289 +3 5294 5293 5290 +3 5295 5294 5290 +3 6274 5295 5291 +3 5299 5285 5292 +3 4667 5296 5294 +3 5294 5296 5293 +3 4667 5294 5297 +3 5297 5294 5295 +3 5305 5295 6274 +3 5297 5295 5305 +3 4672 5298 5292 +3 5299 5292 5298 +3 5302 5308 4666 +3 4667 5304 5296 +3 5296 5304 5303 +3 4667 5305 5304 +3 4667 5297 5305 +3 5302 5309 5308 +3 5309 5302 5296 +3 5303 5309 5296 +3 5310 5309 5303 +3 5304 5310 5303 +3 5305 5310 5304 +3 4671 5316 5306 +3 5306 5311 4679 +3 5299 5298 5318 +3 5307 5300 5314 +3 5315 5307 5314 +3 6326 5315 4684 +3 5319 4675 5308 +3 5316 5317 5306 +3 4679 5311 5317 +3 5311 5306 5317 +3 5321 5299 5318 +3 5322 5299 5321 +3 4682 5300 5324 +3 5314 5300 4682 +3 4684 5315 5314 +3 5317 5316 5320 +3 6369 5322 5323 +3 5327 4677 5325 +3 5319 5325 4677 +3 5328 5325 5319 +3 6380 5328 5319 +3 5308 6380 5319 +3 5335 5321 5318 +3 5322 5321 5335 +3 5328 4697 5325 +3 4691 5330 4692 +3 5329 5330 4691 +3 4692 5331 4693 +3 5330 5331 4692 +3 5332 5333 5334 +3 5343 5333 5332 +3 5336 5322 5335 +3 5323 5322 5336 +3 6369 5323 5345 +3 5346 5324 6378 +3 5337 5326 6440 +3 5326 5338 6440 +3 4696 5327 5339 +3 5327 4100 5339 +3 4698 4697 5328 +3 4698 5328 6380 +3 5329 4700 5340 +3 4691 4700 5329 +3 5331 5340 4701 +3 5331 5330 5340 +3 5330 5329 5340 +3 5342 5343 5332 +3 5334 5344 5332 +3 5344 5342 5332 +3 4716 5335 5318 +3 4716 5336 5335 +3 5345 5354 6369 +3 4717 5324 5346 +3 4718 5324 4717 +3 4703 5324 4718 +3 5337 2279 5326 +3 4706 4696 5347 +3 5347 5339 5348 +3 5347 4696 5339 +3 5349 5348 5339 +3 5349 5339 4100 +3 5349 4100 5350 +3 4698 5351 4128 +3 4707 5352 6415 +3 4708 4707 6417 +3 4707 6415 6417 +3 4708 6417 5357 +3 4710 5353 4700 +3 5358 5353 4710 +3 4700 5353 5340 +3 4701 5340 4712 +3 4712 5340 5353 +3 5344 5343 5342 +3 5345 5323 5336 +3 5354 5345 5336 +3 6428 6369 5354 +3 4743 5346 6405 +3 4717 5346 5355 +3 5356 4724 5347 +3 4724 4706 5347 +3 5350 4100 6501 +3 4708 5357 4726 +3 5358 4710 4726 +3 5358 4726 5357 +3 6485 5353 5358 +3 6485 4712 5353 +3 4742 5336 4716 +3 4762 5336 4742 +3 5354 5336 4180 +3 6405 6465 4763 +3 4743 6405 4763 +3 5346 4743 4744 +3 5355 5346 4744 +3 4717 5355 4744 +3 5360 4736 4723 +3 4749 5356 4750 +3 4724 5356 4749 +3 6501 4100 4750 +3 4771 4128 5351 +3 5352 4707 5374 +3 4712 6485 4739 +3 4762 4180 5336 +3 4794 6465 5359 +3 4763 6465 4794 +3 3635 5361 4684 +3 4785 4736 5363 +3 5363 4736 5360 +3 5360 4723 5365 +3 4723 4737 5365 +3 5362 3635 2279 +3 6440 5338 5379 +3 4737 5372 5365 +3 5375 4138 4752 +3 4739 5375 4752 +3 4753 4773 5366 +3 4757 5367 4776 +3 4775 5367 4757 +3 6567 4776 5367 +3 4777 4776 5368 +3 4759 4777 5368 +3 4778 4759 5368 +3 5368 6598 4778 +3 4794 5359 5354 +3 4769 4798 5369 +3 5371 5370 5365 +3 5372 5371 5365 +3 4154 4771 5373 +3 4138 5375 6615 +3 4788 5385 6623 +3 4773 4772 4788 +3 4773 4788 6623 +3 5366 4773 6623 +3 4753 5366 5376 +3 5377 5376 5378 +3 5377 4753 5376 +3 4753 5377 4789 +3 5367 4775 4790 +3 4790 6567 5367 +3 6598 4791 4778 +3 5369 5386 4805 +3 5369 4798 5386 +3 4785 5363 5389 +3 5364 5389 5363 +3 5389 5364 6603 +3 5371 5381 5370 +3 5381 5380 5370 +3 5372 5382 5371 +3 5382 5381 5371 +3 5383 5382 5372 +3 5383 5372 4154 +3 5373 5383 4154 +3 5394 5384 5374 +3 4138 6615 4806 +3 5395 4172 5378 +3 5377 5378 4172 +3 5377 4172 4789 +3 4171 6660 4790 +3 4791 6660 4171 +3 6598 6660 4791 +3 4804 6695 4783 +3 4798 5387 5386 +3 5387 4798 5388 +3 5388 4785 5401 +3 4785 5389 5401 +3 6603 6680 5389 +3 5381 5390 5380 +3 5391 5390 5382 +3 5390 5381 5382 +3 5383 5391 5382 +3 6609 5392 5393 +3 5384 5393 5392 +3 5384 5394 5393 +3 4810 5395 5396 +3 4172 5395 4810 +3 5411 5399 5398 +3 4796 6676 4812 +3 4169 6676 4796 +3 6695 6676 4169 +3 6677 6695 4804 +3 4805 5386 5417 +3 5387 5417 5386 +3 5417 5388 5400 +3 5417 5387 5388 +3 5401 5400 5388 +3 6680 5402 5389 +3 5402 5401 5389 +3 5430 5370 5380 +3 4813 5380 5390 +3 5390 4814 4813 +3 5390 5391 4814 +3 5393 5404 6609 +3 5404 5403 6609 +3 5405 5404 5393 +3 5394 5405 5393 +3 5394 5406 5405 +3 5406 4806 5407 +3 5407 4806 6615 +3 4819 5385 4815 +3 5385 4816 4815 +3 4810 5396 4820 +3 4820 5396 5408 +3 4191 5409 6667 +3 5397 4191 6667 +3 5397 4822 4191 +3 5410 4822 5397 +3 5397 6692 5410 +3 5398 5413 5411 +3 5399 5413 5398 +3 5413 5399 5414 +3 5415 6677 5416 +3 6677 4804 5416 +3 4805 5416 4804 +3 5417 5416 4805 +3 5400 5401 5418 +3 5401 5402 5418 +3 6680 5419 5402 +3 5419 5418 5402 +3 6680 5430 5419 +3 5380 4818 5430 +3 4813 4818 5380 +3 4825 4814 5420 +3 5403 5404 5431 +3 5405 5421 5404 +3 5421 5431 5404 +3 5422 5421 5405 +3 5406 5423 5405 +3 5423 5422 5405 +3 5424 5423 5406 +3 5407 5425 5406 +3 5425 5424 5406 +3 5426 5425 5407 +3 5407 5427 5426 +3 5427 6707 4819 +3 5385 4819 6707 +3 4191 4821 5409 +3 5428 5410 6692 +3 4822 5410 5428 +3 5414 5411 5413 +3 4823 6720 5379 +3 4828 4824 4812 +3 6676 4828 4812 +3 5429 5415 5416 +3 5417 5429 5416 +3 5400 5418 5419 +3 5421 5422 5431 +3 5423 5431 5422 +3 5424 5432 5423 +3 5432 5431 5423 +3 5425 5432 5424 +3 5426 5432 5425 +3 5427 5433 5426 +3 5433 5432 5426 +3 4819 5433 5427 +3 4820 6739 4833 +3 5408 6739 4820 +3 4821 4199 5409 +3 4822 5428 4199 +3 4823 5444 6720 +3 5419 5435 5400 +3 5435 5419 5430 +3 4825 5438 4836 +3 5420 5438 4825 +3 5432 6757 5431 +3 5439 5432 5433 +3 4833 6739 6782 +3 4835 5436 5430 +3 5430 5436 5435 +3 5436 4835 5437 +3 5438 5437 4836 +3 5439 4832 5443 +3 6779 5443 4832 +3 6779 4832 5440 +3 4833 5440 4832 +3 5440 4833 6782 +3 5441 4206 4839 +3 5438 5436 5437 +3 5439 5442 6818 +3 5443 5442 5439 +3 4838 5434 4850 +3 5441 4839 4841 +3 5447 6818 5442 +3 5447 5442 5443 +3 5456 5444 4823 +3 4840 5449 4842 +3 4840 5445 5449 +3 5445 5448 5449 +3 5450 6815 5446 +3 5451 6815 5450 +3 4843 4844 5450 +3 4843 5450 5441 +3 5441 5450 5446 +3 4841 4843 5441 +3 6830 4846 4206 +3 5452 5453 5454 +3 6882 6840 4849 +3 4823 4237 5456 +3 4843 5451 5450 +3 4843 5450 4844 +3 5453 5457 5458 +3 5453 5452 5457 +3 5459 5457 5454 +3 5454 5457 5452 +3 5457 5460 5461 +3 5459 5460 5457 +3 4869 5451 4843 +3 5458 5463 5471 +3 5458 5457 5463 +3 5457 5461 5463 +3 5460 5464 5461 +3 5464 5463 5461 +3 5465 5466 5467 +3 4245 5468 4852 +3 4851 5468 4245 +3 4856 5455 4855 +3 4858 6890 4867 +3 4858 5478 6890 +3 6890 5470 4867 +3 4869 5479 5451 +3 5464 5471 5463 +3 5472 5473 4860 +3 5473 4861 4860 +3 5473 4863 4861 +3 5474 4863 5473 +3 5469 5477 5476 +3 5455 4864 4855 +3 4858 4865 5478 +3 5470 6949 4867 +3 4859 5480 5482 +3 5472 4860 6904 +3 4860 4868 6904 +3 5474 4868 4863 +3 6904 4868 5474 +3 5476 6926 5475 +3 5456 4237 6980 +3 6983 5478 4867 +3 4867 5478 4865 +3 4867 6949 6983 +3 4870 5479 4869 +3 5479 4870 6920 +3 4870 5482 5480 +3 4870 5480 6920 +3 4877 6980 4237 +3 5481 5482 4870 +3 6933 5496 4874 +3 4877 7000 6980 +3 5483 5482 5481 +3 5482 5483 5484 +3 4882 4874 5496 +3 5486 5488 5487 +3 5484 5483 4879 +3 5489 5490 4876 +3 5490 5491 4876 +3 4873 5504 5492 +3 4873 5492 4881 +3 5492 5493 4881 +3 5495 5493 5494 +3 5485 4874 5506 +3 5497 7032 5486 +3 5487 5499 5486 +3 5499 5498 5486 +3 5500 5499 5487 +3 5488 5508 5487 +3 5508 5500 5487 +3 5508 5488 5501 +3 5515 5489 4898 +3 5489 5515 7034 +3 5489 5502 5490 +3 5503 5490 5502 +3 5491 5490 5503 +3 4910 5485 5505 +3 5506 5505 5485 +3 4882 5496 4962 +3 5497 4883 4289 +3 5507 5498 5499 +3 5500 5507 5499 +3 5509 5512 5511 +3 5510 5512 5509 +3 5511 5512 5513 +3 5513 5514 5525 +3 5512 5514 5513 +3 4898 5514 5515 +3 4889 5517 5518 +3 5516 5517 4889 +3 5519 5504 4892 +3 4910 5527 5485 +3 5520 5505 5506 +3 5521 5520 5506 +3 5506 4894 5521 +3 4289 4895 5497 +3 5497 4895 4896 +3 5497 4896 5535 +3 4897 5507 5500 +3 5508 4913 5500 +3 4913 4897 5500 +3 5501 4913 5508 +3 5523 5510 5509 +3 5511 5524 5509 +3 5524 5523 5509 +3 5513 5530 5511 +3 5530 5524 5511 +3 5525 5530 5513 +3 5514 4898 5525 +3 5531 4308 4899 +3 7067 5531 4899 +3 4901 7067 4899 +3 5502 7067 4901 +3 4902 5502 4901 +3 4902 5526 5502 +3 5526 5503 5502 +3 4889 4903 5516 +3 4904 5516 4903 +3 5517 5516 4904 +3 4905 5517 4904 +3 5518 5517 4905 +3 4909 4921 5527 +3 4909 5527 4910 +3 5505 5520 5528 +3 5528 5520 5521 +3 4894 5544 5528 +3 5521 4894 5528 +3 5535 4896 4912 +3 5523 5524 5536 +3 5530 5529 5524 +3 5529 5536 5524 +3 5525 4914 5530 +3 4902 4916 5526 +3 4921 5543 7180 +3 4921 7180 5527 +3 5528 5544 5532 +3 7237 4373 4850 +3 4912 4922 5535 +3 4913 5501 4923 +3 5501 5558 4923 +3 4355 5522 5549 +3 5530 5536 5529 +3 4914 5536 5530 +3 5538 5537 5531 +3 5531 5537 5550 +3 4926 4925 5541 +3 4926 5541 4921 +3 5541 5542 4921 +3 5543 4921 5542 +3 7192 5533 5545 +3 5534 5545 5533 +3 5545 5546 5547 +3 5534 5546 5545 +3 7197 5535 4922 +3 4933 4923 5558 +3 4935 5559 4914 +3 4937 7169 7211 +3 4938 7169 4937 +3 5538 4938 5537 +3 5538 7169 4938 +3 4938 5550 5537 +3 5540 5551 5539 +3 4845 7220 5643 +3 5552 4927 5563 +3 5563 4927 4928 +3 5553 5554 5555 +3 4939 5555 5554 +3 5547 5556 5545 +3 5548 4943 5557 +3 4931 4943 5548 +3 4932 5557 4944 +3 5548 5557 4932 +3 4922 4945 7197 +3 4346 4355 5549 +3 5559 4935 5560 +3 5561 7211 7248 +3 5561 4946 7211 +3 4946 4937 7211 +3 4938 4947 5550 +3 5541 4948 5542 +3 5562 5542 4948 +3 4927 5552 4349 +3 4349 5552 7223 +3 5553 5555 4960 +3 5564 5554 5553 +3 5554 5565 4951 +3 5554 5564 5565 +3 4951 5565 5570 +3 4951 5570 4941 +3 4943 4944 5557 +3 4292 4952 5576 +3 5566 4953 4346 +3 7248 4954 5567 +3 7248 5560 4954 +3 5560 4935 4954 +3 7248 5567 5561 +3 5561 5567 5573 +3 5561 5573 4946 +3 4948 4956 5562 +3 5562 4956 5568 +3 5569 4349 7223 +3 5569 4959 4349 +3 5563 4928 4960 +3 5570 5565 5564 +3 5570 5564 4961 +3 4953 5566 5571 +3 4954 5572 5567 +3 5573 5567 5572 +3 5574 5587 5568 +3 4959 5575 5574 +3 4959 5574 5568 +3 4959 5568 4956 +3 4959 7302 5575 +3 4959 5569 7302 +3 5580 4933 5558 +3 5580 5592 4933 +3 5571 7361 4381 +3 4953 5571 4381 +3 5577 5573 5572 +3 5587 5574 5578 +3 5574 5579 5578 +3 7302 5579 5575 +3 5579 5574 5575 +3 4965 7344 4961 +3 7345 4971 4962 +3 4973 7464 4966 +3 7464 4945 4966 +3 4952 5581 5576 +3 4381 5581 4952 +3 7361 5581 4381 +3 5583 5584 5585 +3 5586 5587 5578 +3 5579 5588 5578 +3 5588 5586 5578 +3 5588 5579 7302 +3 7344 4965 4968 +3 7344 4968 4977 +3 5589 4384 4970 +3 7345 5596 4971 +3 4975 5591 5580 +3 4975 5580 5590 +3 5592 5580 5591 +3 4989 7402 5584 +3 5600 4989 5583 +3 5584 5583 4989 +3 5594 5587 5586 +3 5602 5587 5594 +3 5594 5586 5588 +3 4977 4968 4976 +3 7410 4993 4980 +3 7410 4980 5589 +3 5589 4980 4981 +3 4396 5589 4981 +3 5589 4396 4384 +3 4994 5589 4970 +3 4994 5595 5596 +3 4994 5596 5589 +3 4983 5596 5595 +3 4983 4971 5596 +3 4973 4403 7464 +3 5597 5591 4975 +3 5597 5592 5591 +3 4985 5592 5598 +3 5592 5597 5598 +3 4986 5582 5599 +3 5582 5605 5599 +3 5607 4996 5593 +3 7402 4988 5600 +3 4989 4988 7402 +3 5602 5594 5601 +3 4993 7410 5623 +3 4983 5595 5603 +3 5603 5595 4994 +3 5009 5604 5597 +3 5604 5598 5597 +3 5598 5604 4985 +3 5607 5606 4996 +3 4988 4997 5600 +3 5609 5602 5601 +3 5610 5601 5594 +3 5609 5601 5610 +3 5007 4993 5623 +3 5008 5603 4994 +3 7486 4415 5590 +3 5604 5009 5612 +3 4985 5604 5613 +3 5604 5612 5613 +3 5599 5605 5002 +3 4996 5004 5593 +3 5593 5004 5615 +3 5006 5606 5614 +3 5614 5606 5607 +3 5617 5607 7447 +3 5614 5607 5617 +3 4977 5619 7477 +3 5611 4977 7477 +3 5011 5612 5009 +3 5613 5612 5011 +3 5013 5615 5004 +3 5616 5615 5014 +3 5014 5615 5013 +3 5014 5617 5616 +3 5006 5617 5014 +3 5006 5614 5617 +3 5016 5618 5619 +3 4977 5016 5619 +3 5007 5623 5628 +3 5016 5620 5618 +3 5620 5621 5618 +3 5619 5618 5622 +3 5622 5618 5621 +3 5619 5622 7503 +3 4963 4237 5677 +3 5023 5625 5024 +3 5624 5625 5023 +3 5031 5620 5016 +3 5031 5626 5620 +3 5626 5627 5620 +3 5032 5621 5627 +3 5621 5620 5627 +3 5033 5621 5032 +3 5622 5621 5033 +3 5034 5622 5033 +3 5034 5628 5622 +3 5628 7503 5622 +3 5623 7503 5628 +3 5624 5023 7530 +3 5023 5634 7530 +3 7540 5625 5624 +3 4448 5625 7540 +3 4448 5024 5625 +3 5031 5035 5626 +3 5032 5626 5035 +3 5627 5626 5032 +3 5034 5036 5628 +3 5037 4448 7540 +3 5039 5630 5631 +3 5038 5630 5039 +3 5631 5038 5039 +3 5628 5036 4467 +3 5040 5041 5629 +3 5040 5629 5633 +3 5044 5043 7556 +3 5045 5044 5630 +3 5044 7556 5630 +3 5630 5038 5045 +3 5047 5038 5631 +3 7565 5049 5639 +3 7565 5632 5049 +3 5632 5041 5049 +3 5048 5040 5633 +3 5048 5633 5639 +3 5051 5050 5635 +3 5051 5636 5052 +3 5635 5636 5051 +3 5052 5636 5053 +3 5056 5057 5637 +3 5056 5637 5638 +3 5056 5638 5043 +3 5047 5631 5641 +3 5049 5060 5639 +3 5048 5639 5060 +3 5071 5635 5050 +3 5071 5636 5635 +3 5053 5071 5061 +3 5636 5071 5053 +3 5057 5066 5637 +3 5640 5074 5642 +3 5063 5074 5640 +3 5066 5076 5637 +3 5645 5638 5637 +3 5645 5637 5076 +3 5069 5641 5082 +3 5074 5644 5642 +3 5645 5076 5081 +3 5641 7610 5082 +3 4373 7616 3952 +3 5081 5087 5645 +3 5088 5082 7610 +3 3952 7616 4509 +3 5109 5652 5083 +3 5646 5096 5083 +3 5647 5085 5654 +3 4492 5648 5091 +3 4492 5647 5648 +3 5647 5654 5648 +3 5649 7640 7620 +3 7620 5650 5649 +3 5651 5093 5092 +3 5094 5093 5651 +3 5087 5094 5645 +3 5094 5651 5645 +3 5096 5646 5653 +3 7666 5096 5653 +3 5666 5096 7666 +3 5091 5648 5104 +3 5655 7640 5649 +3 5650 5656 5649 +3 5656 5655 5649 +3 5105 5656 5650 +3 5102 5101 5657 +3 5102 5657 5103 +3 5116 7680 5115 +3 5115 7680 5665 +3 5109 5658 5652 +3 5660 5648 5659 +3 5659 5648 5654 +3 5104 5648 5660 +3 5655 5104 5660 +3 5105 5104 5655 +3 5656 5105 5655 +3 5107 5662 5661 +3 5107 5106 5662 +3 5106 5105 5662 +3 5101 5108 5657 +3 5103 5657 5108 +3 5118 7671 5092 +3 5663 5664 5112 +3 5671 7680 5116 +3 5115 5665 5123 +3 7686 5109 4509 +3 7686 5658 5109 +3 5660 5659 5666 +3 5107 5661 5110 +3 5661 5111 5110 +3 5661 5662 5111 +3 5112 5673 5672 +3 5114 5667 5668 +3 5668 5669 5670 +3 5667 5669 5668 +3 5671 5670 5669 +3 7777 7761 4509 +3 7761 7686 4509 +3 5662 5127 5111 +3 5683 7724 5674 +3 5114 5674 7724 +3 5668 5675 5122 +3 5114 5668 5122 +3 5670 5676 5668 +3 5676 5675 5668 +3 5671 5116 5670 +3 5116 5676 5670 +3 5124 5123 5665 +3 5679 5678 5691 +3 5127 5693 5681 +3 5127 5662 5693 +3 5681 5693 5118 +3 5119 5135 5112 +3 5673 5695 5672 +3 5695 5682 5672 +3 5673 5683 5695 +3 5683 5674 5684 +3 5685 5684 5674 +3 5674 5686 5685 +3 5122 5675 5687 +3 5676 5688 5675 +3 5688 5687 5675 +3 5116 5132 5688 +3 5116 5688 5676 +3 5703 5137 5665 +3 5124 5137 5133 +3 5665 5137 5124 +3 5689 5678 5679 +3 5690 5689 5679 +3 5691 5678 5689 +3 5691 5689 5692 +3 5125 7784 5697 +3 5126 7784 5125 +3 5127 5681 5134 +3 5694 5135 5682 +3 5695 5694 5682 +3 5708 5695 5683 +3 5685 5700 5684 +3 5686 5696 5685 +3 5696 5686 5122 +3 5687 5696 5122 +3 5132 5136 5688 +3 7803 5689 5690 +3 5692 5689 7803 +3 5680 5138 7824 +3 5695 5699 5694 +3 5699 5698 5694 +3 5708 5699 5695 +3 5684 5708 5683 +3 5685 5701 5700 +3 5696 5702 5685 +3 5702 5701 5685 +3 5688 5136 5703 +3 5704 5703 5136 +3 5703 5704 5137 +3 5138 5705 7824 +3 5706 5705 5138 +3 5139 5707 5138 +3 5138 5707 5706 +3 7826 5707 5139 +3 5140 5643 5698 +3 5699 5140 5698 +3 5700 5708 5684 +3 5701 5709 5700 +3 5702 5710 5701 +3 5710 5709 5701 +3 5711 5710 5702 +3 5136 5712 5704 +3 5137 5704 5712 +3 5141 7840 5713 +3 7824 5705 5142 +3 7824 5142 5713 +3 5713 5142 5141 +3 5705 5706 5142 +3 5142 5706 5143 +3 5144 5143 5714 +3 5143 5706 5714 +3 7826 5139 5144 +3 5714 7826 5144 +3 5700 5715 5708 +3 5709 5715 5700 +3 5710 5715 5709 +3 5711 5145 5710 +3 5145 5715 5710 +3 5711 5716 5145 +3 5716 5717 5712 +3 5712 5717 4530 +3 5677 4530 5718 +3 5141 4521 7840 +3 5716 5718 5717 +3 4530 5717 5718 +3 4539 5719 5720 +3 4539 5721 5719 +3 4539 5722 5721 +3 4539 5723 5722 +3 4539 5724 5723 +3 4539 5725 5724 +3 5727 5726 4539 +3 4539 5726 5725 +3 5727 5729 5728 +3 5727 5730 5729 +3 5727 5731 5730 +3 4534 5732 5731 +3 4534 5731 5727 +3 5733 4534 5146 +3 5745 5733 5146 +3 5734 5749 5149 +3 5734 5149 4533 +3 4533 5735 5734 +3 5152 5736 4533 +3 4533 5736 5735 +3 5152 5737 5736 +3 4538 5738 5739 +3 4538 5740 5738 +3 4539 5741 4538 +3 4538 5741 5740 +3 5741 4539 5720 +3 4534 5742 5732 +3 5743 5742 4534 +3 5744 5743 4534 +3 5733 5744 4534 +3 5746 5745 5146 +3 5150 5747 5746 +3 5146 5150 5746 +3 5149 5748 5160 +3 5149 5749 5748 +3 5152 5750 5737 +3 5751 5750 5152 +3 5152 5759 5751 +3 5152 5164 5759 +3 5153 5752 5753 +3 4538 5754 5153 +3 5153 5754 5752 +3 5754 4538 5739 +3 5150 5755 5747 +3 5150 5160 5755 +3 5160 5756 5755 +3 5160 5757 5756 +3 5748 5758 5160 +3 5758 5757 5160 +3 5760 5761 5759 +3 5164 5760 5759 +3 5164 5167 5762 +3 5760 5164 5762 +3 5153 5763 5764 +3 5763 5153 5753 +3 5765 5761 5760 +3 5766 5761 5765 +3 5762 5767 5760 +3 5767 5765 5760 +3 5167 5767 5762 +3 5167 5768 5767 +3 5768 5769 5767 +3 5771 5770 5768 +3 5175 5772 5773 +3 5153 5774 5175 +3 5175 5774 5772 +3 5774 5153 5764 +3 5776 5766 5765 +3 5775 5766 5776 +3 5767 5776 5765 +3 5777 5767 5769 +3 5776 5767 5777 +3 5768 5778 5771 +3 5779 5778 5768 +3 5780 5217 5781 +3 5175 5782 5217 +3 5217 5782 5781 +3 5782 5175 5773 +3 5170 5783 5178 +3 5198 5785 5786 +3 5787 5198 5786 +3 5776 5788 5775 +3 5788 5789 5790 +3 5788 5776 5789 +3 5791 5776 5777 +3 5789 5776 5791 +3 5792 5779 5187 +3 5779 5768 5187 +3 5792 5187 5793 +3 5795 5794 5217 +3 5780 5795 5217 +3 5204 5817 5169 +3 5817 5818 5169 +3 5819 5169 5818 +3 5796 5797 5783 +3 5796 5783 5819 +3 5819 5783 5169 +3 5798 5783 5797 +3 5799 5783 5798 +3 5800 5783 5799 +3 5801 5783 5800 +3 5802 5783 5801 +3 5820 5802 5803 +3 5178 5802 5820 +3 5178 5783 5802 +3 5820 5205 5178 +3 5197 5784 5208 +3 5198 5825 5804 +3 5198 5804 5785 +3 5210 5198 5805 +3 5805 5198 5787 +3 5789 5806 5790 +3 5791 5806 5789 +3 5807 5793 5187 +3 5214 5808 5809 +3 5213 5214 5809 +3 5214 5810 5808 +3 5214 5811 5810 +3 5812 5811 5214 +3 5217 5814 5813 +3 5814 5217 5794 +3 5835 5838 5193 +3 5815 5816 5204 +3 5815 5204 5838 +3 5838 5204 5193 +3 5817 5204 5816 +3 5821 5205 5820 +3 5205 5821 5822 +3 5823 5205 5822 +3 5205 5823 5206 +3 5823 5840 5206 +3 5824 5206 5840 +3 5824 5857 5207 +3 5826 5804 5210 +3 5210 5804 5825 +3 5805 5826 5210 +3 5827 5807 5187 +3 5213 5809 5240 +3 5809 5828 5240 +3 5214 5829 5812 +3 5829 5214 5879 +3 5830 5829 5879 +3 5831 5226 5832 +3 5226 5833 5832 +3 5850 5833 5226 +3 5226 5834 5850 +3 5226 5217 5834 +3 5217 5813 5834 +3 5220 5868 5193 +3 5835 5193 5868 +3 5836 5837 5835 +3 5838 5835 5837 +3 5840 5823 5839 +3 5841 5824 5840 +3 5841 5842 5824 +3 5854 5824 5842 +3 5857 5824 5854 +3 5187 5230 5843 +3 5843 5827 5187 +3 5828 5844 5240 +3 5879 5845 5830 +3 5879 5846 5845 +3 5847 5846 5879 +3 5226 5849 5235 +3 5849 5848 5235 +3 5831 5849 5226 +3 5868 5220 5231 +3 5851 5852 5835 +3 5851 5835 5868 +3 5836 5835 5852 +3 5854 5842 5853 +3 5855 5857 5854 +3 5855 5856 5857 +3 5230 5234 5858 +3 5230 5858 5859 +3 5230 5859 5843 +3 5240 5844 5860 +3 5861 5240 5860 +3 5879 5862 5847 +3 5879 5863 5862 +3 5235 5864 5865 +3 5864 5235 5848 +3 5866 5867 5231 +3 5866 5231 5885 +3 5868 5231 5867 +3 5857 5856 5869 +3 5870 5857 5869 +3 5871 5857 5870 +3 5872 5857 5871 +3 5872 5873 5857 +3 5857 5873 5228 +3 5228 5873 5874 +3 5874 5232 5210 +3 5234 5893 5875 +3 5234 5875 5876 +3 5234 5876 5858 +3 5861 5877 5240 +3 5879 5878 5863 +3 5880 5879 5236 +3 5235 5865 5881 +3 5882 5883 5231 +3 5884 5231 5883 +3 5885 5231 5884 +3 5874 5873 5886 +3 5887 5874 5886 +3 5888 5874 5887 +3 5889 5874 5888 +3 5890 5874 5889 +3 5890 5891 5874 +3 5904 5874 5891 +3 5232 5874 5905 +3 5905 5874 5904 +3 5234 5232 5909 +3 5909 5232 5908 +3 5910 5234 5909 +3 5892 5234 5910 +3 5892 5893 5234 +3 5240 5877 5894 +3 5895 5240 5894 +3 5236 5896 5880 +3 5236 5897 5896 +3 5897 5236 5898 +3 5900 5899 5235 +3 5881 5900 5235 +3 5238 5901 5231 +3 5901 5902 5231 +3 5902 5882 5231 +3 5904 5891 5903 +3 5906 5232 5905 +3 5907 5232 5906 +3 5908 5232 5907 +3 5911 5892 5910 +3 5895 5912 5240 +3 5236 5913 5898 +3 5236 5239 5913 +3 5239 5914 5913 +3 5915 5914 5239 +3 5235 5916 5917 +3 5916 5235 5899 +3 5919 5920 5901 +3 5919 5901 5918 +3 5918 5901 5238 +3 5902 5901 5920 +3 5921 5902 5920 +3 5922 5240 5912 +3 5239 5923 5915 +3 4612 5923 5239 +3 5924 5923 4612 +3 5925 5235 5917 +3 5926 5927 5918 +3 5926 5918 5935 +3 5935 5918 5238 +3 5919 5918 5927 +3 5240 5928 5929 +3 5240 5922 5928 +3 5930 4620 5931 +3 5930 5924 4620 +3 5924 4612 4620 +3 5925 5932 5235 +3 5241 5944 5933 +3 5241 5933 5934 +3 5241 5934 5238 +3 5935 5238 5934 +3 5240 5929 5936 +3 5936 5937 5240 +3 5931 4620 5938 +3 5939 5931 5938 +3 5939 5938 5940 +3 5235 5941 5942 +3 5941 5235 5932 +3 5241 5943 5944 +3 5937 5945 5240 +3 4620 5243 5938 +3 5244 5938 5243 +3 5938 5946 5940 +3 5938 5244 5946 +3 5947 5946 5244 +3 5948 5235 5942 +3 5949 5943 5241 +3 5945 5950 5240 +3 5244 5951 5947 +3 5952 5951 5244 +3 5235 5953 4635 +3 5235 5948 5953 +3 5241 5954 5955 +3 5241 5955 5949 +3 5956 5240 5950 +3 5952 5244 5957 +3 4635 5958 5959 +3 4635 5959 3496 +3 5953 5958 4635 +3 5241 4642 5960 +3 5960 5954 5241 +3 5961 5240 5956 +3 5961 5962 5240 +3 5244 5963 5957 +3 5964 5963 5244 +3 5965 3496 5959 +3 4642 5966 5967 +3 4642 5967 5960 +3 5240 5962 4629 +3 5962 5968 4629 +3 5964 4051 5969 +3 5965 5970 3496 +3 5971 5966 4642 +3 5972 4629 5968 +3 5973 5974 5975 +3 5975 5976 5973 +3 4051 5977 5969 +3 5978 5977 4051 +3 5979 3496 5970 +3 5980 5971 4642 +3 5972 5981 4629 +3 5982 5974 5973 +3 5983 5974 5982 +3 5984 5973 5976 +3 5982 5973 5984 +3 5985 5978 4051 +3 5979 5986 3496 +3 4642 5987 5988 +3 4642 5988 5980 +3 4629 5981 5989 +3 5982 5990 5983 +3 5984 5990 5982 +3 5991 5985 4051 +3 5991 4051 5992 +3 5986 5993 3496 +3 5994 5987 4642 +3 4629 6000 6015 +3 5995 4629 5989 +3 6000 4629 5995 +3 5992 4051 5996 +3 3496 5997 5998 +3 5997 3496 5993 +3 5999 5994 4642 +3 6001 5996 4051 +3 5998 6002 3496 +3 6003 5999 4642 +3 6009 6003 4642 +3 6000 6004 6015 +3 6005 6001 4051 +3 6005 4051 6006 +3 6002 6007 3496 +3 6009 4642 6008 +3 6004 6010 6015 +3 6006 4051 6011 +3 3496 6012 6013 +3 6012 3496 6007 +3 4642 6014 6008 +3 6016 6011 4051 +3 6013 6017 3496 +3 6018 6014 4642 +3 6015 6019 4643 +3 5248 5247 6027 +3 5248 6027 6028 +3 6020 6016 4051 +3 6020 4051 6021 +3 6017 6022 3496 +3 6023 6018 4642 +3 6019 6024 4643 +3 5251 6025 6026 +3 5250 6025 5251 +3 6027 5247 5251 +3 6027 5251 6026 +3 6029 6021 4051 +3 3496 6022 6030 +3 6031 6023 4642 +3 6024 6032 4643 +3 6025 5250 6033 +3 5252 6028 6034 +3 6035 6029 4051 +3 3496 6036 6037 +3 6030 6036 3496 +3 4642 6052 6053 +3 6038 6031 4642 +3 6053 6038 4642 +3 6032 6039 4643 +3 5250 5253 6033 +3 6033 5253 6040 +3 6041 5252 6034 +3 6042 6035 4051 +3 6042 4051 6043 +3 6037 6044 3496 +3 6045 6038 6053 +3 4643 6039 6046 +3 5253 6047 6048 +3 6048 6040 5253 +3 6041 6049 5252 +3 6050 6043 4051 +3 4702 3496 6051 +3 3496 6044 6051 +3 6054 6045 6053 +3 6055 4643 6046 +3 6062 4643 6055 +3 5253 6056 6047 +3 6049 6057 4656 +3 5252 6049 4656 +3 4051 6058 6050 +3 4702 6059 6060 +3 6051 6059 4702 +3 6053 6052 5256 +3 6061 6054 6053 +3 6065 6056 5253 +3 6064 6065 5253 +3 4656 6067 6066 +3 4656 6057 6067 +3 6058 4051 6068 +3 6060 6069 4702 +3 6070 6061 6053 +3 6062 6063 4643 +3 6063 6071 4643 +3 6064 5257 5259 +3 6064 5259 6065 +3 6065 5259 6072 +3 6066 6073 5258 +3 6068 4051 6074 +3 4702 6075 6087 +3 4702 6069 6075 +3 6070 6053 6076 +3 4050 4643 6077 +3 6077 4643 6071 +3 5259 6078 6079 +3 6079 6072 5259 +3 6073 6093 5258 +3 6080 6094 6073 +3 6094 6093 6073 +3 6080 6081 6094 +3 6082 6083 6084 +3 6084 6085 6082 +3 5260 6086 4051 +3 6086 6074 4051 +3 6087 6088 4702 +3 6089 5265 6090 +3 6089 6076 5265 +3 6076 6053 5265 +3 6077 6091 4050 +3 5264 6092 5259 +3 5259 6092 6078 +3 5258 6093 5262 +3 6095 6094 6081 +3 6096 6097 6098 +3 6096 6083 6099 +3 6098 6083 6096 +3 6100 6082 6101 +3 6100 6099 6082 +3 6083 6082 6099 +3 6085 6101 6082 +3 5260 6102 6086 +3 6088 6103 4702 +3 6090 5265 6104 +3 6091 6105 4050 +3 6092 5264 6106 +3 5263 6107 6122 +3 5262 6107 5263 +3 5262 6093 6107 +3 6094 6107 6093 +3 6108 6107 6094 +3 6095 6108 6094 +3 6109 6097 6096 +3 6110 6097 6109 +3 6099 6111 6096 +3 6111 6109 6096 +3 6112 6099 6100 +3 6111 6099 6112 +3 6102 5260 6113 +3 4702 6103 6114 +3 6104 5265 6115 +3 4050 6116 6117 +3 6116 4050 6105 +3 6118 6106 5264 +3 6119 6106 6118 +3 5264 6120 6118 +3 5264 5263 6120 +3 6120 6121 6118 +3 6122 6120 5263 +3 6123 6124 6125 +3 6110 6123 6125 +3 6110 6109 6123 +3 6109 6126 6123 +3 6127 6111 6128 +3 6127 6126 6111 +3 6126 6109 6111 +3 6112 6128 6111 +3 6129 6113 5260 +3 6114 6130 4702 +3 5265 5267 6115 +3 5267 6131 6115 +3 4050 6117 6143 +3 6117 6132 6143 +3 6133 6119 6118 +3 6134 6119 6133 +3 6135 6118 6121 +3 6133 6118 6135 +3 6136 6124 6123 +3 6137 6124 6136 +3 6138 6126 6139 +3 6138 6136 6126 +3 6136 6123 6126 +3 6127 6139 6126 +3 5260 6140 6129 +3 6130 6141 4702 +3 6131 5267 6142 +3 6133 6144 6134 +3 6135 6144 6133 +3 6136 6145 6137 +3 6138 6145 6136 +3 6140 5260 6146 +3 6141 6147 4702 +3 6148 5267 6149 +3 6148 6142 5267 +3 6143 6154 5268 +3 6143 6150 6154 +3 6146 5260 6151 +3 6147 6152 4702 +3 5267 6153 6149 +3 6154 6155 5268 +3 6151 5260 6156 +3 6152 6157 4702 +3 5267 6159 6158 +3 5267 6158 6153 +3 6155 6160 5268 +3 6161 6162 6163 +3 6161 6164 6165 +3 6163 6164 6161 +3 6165 6166 6167 +3 6164 6166 6165 +3 6167 6168 6169 +3 6166 6168 6167 +3 6168 6170 6169 +3 6171 6156 5260 +3 6157 6172 4702 +3 5267 6173 6159 +3 6174 5270 5268 +3 6174 5268 6160 +3 5271 6175 6176 +3 5272 6175 5271 +3 5273 6177 6178 +3 5273 5271 6177 +3 5271 6176 6177 +3 6177 6179 6178 +3 6178 6180 6181 +3 6179 6180 6178 +3 6182 6161 6183 +3 6182 6181 6161 +3 6162 6161 6180 +3 6180 6161 6181 +3 6165 6183 6161 +3 6184 6183 6165 +3 6167 6184 6165 +3 6185 6184 6167 +3 6169 6185 6167 +3 6186 6185 6169 +3 6170 6186 6169 +3 5260 6187 6171 +3 6172 6188 4702 +3 6189 6173 5267 +3 6189 5267 6190 +3 5284 6191 6192 +3 6191 5284 6193 +3 6194 6174 6195 +3 5276 6174 6194 +3 5276 5270 6174 +3 5273 6197 5278 +3 5276 6194 5272 +3 5272 6194 6196 +3 5272 6196 6175 +3 5273 6198 6197 +3 5273 6178 6198 +3 6198 6208 6199 +3 6198 6178 6208 +3 6200 6181 6201 +3 6200 6208 6181 +3 6208 6178 6181 +3 6182 6201 6181 +3 6187 5260 6202 +3 6188 6203 4702 +3 5285 6190 5267 +3 5285 6204 6190 +3 6205 5284 6192 +3 6206 6193 5284 +3 6206 5284 5280 +3 6195 6206 5280 +3 6194 6195 5280 +3 6194 5280 5281 +3 6194 6207 6196 +3 6194 5281 6207 +3 6197 6207 5278 +3 5278 6207 5281 +3 6202 5260 6209 +3 6203 6210 4702 +3 5285 6212 6211 +3 5285 6211 6204 +3 5283 6213 6214 +3 6205 6213 5283 +3 5284 6205 5283 +3 6209 5260 6215 +3 6210 6216 4702 +3 5285 6217 6212 +3 5288 5283 6214 +3 6218 5288 6214 +3 6215 5260 6219 +3 6216 6220 4702 +3 5285 6221 6217 +3 6221 5285 6222 +3 6238 6223 6224 +3 6223 6238 6225 +3 6238 6226 6227 +3 6225 6238 6227 +3 6227 6226 6228 +3 5287 6229 6230 +3 6218 6229 5288 +3 6229 5287 5288 +3 5260 6231 6219 +3 6220 6232 4702 +3 5285 6233 6222 +3 6233 5285 6234 +3 6235 6236 6237 +3 6235 6238 6239 +3 6236 6235 6239 +3 6224 6239 6238 +3 6240 6228 6226 +3 6242 6241 6230 +3 6242 5291 6241 +3 6242 6243 5291 +3 5260 6244 6231 +3 6232 6245 4702 +3 6246 6247 6248 +3 6248 6249 6246 +3 5285 6251 6234 +3 6251 5285 6252 +3 6253 6235 6237 +3 6254 6253 6237 +3 6238 6235 6268 +3 6268 6235 6253 +3 6255 6238 6268 +3 6226 6238 6256 +3 6256 6238 6255 +3 6257 6240 6226 +3 6256 6257 6226 +3 5291 5290 6241 +3 5291 6243 6258 +3 6244 5260 6259 +3 6245 6260 4702 +3 6261 6247 6246 +3 6249 6262 6246 +3 6262 6261 6246 +3 6263 6249 6250 +3 6262 6249 6263 +3 5285 6264 6252 +3 6253 6266 6267 +3 6254 6266 6253 +3 6255 6268 6269 +3 6256 6255 6270 +3 6270 6255 6269 +3 6271 6257 6256 +3 6271 6256 6272 +3 6272 6256 6270 +3 6273 5291 6258 +3 6273 6274 5291 +3 6259 5260 6275 +3 6260 6276 4702 +3 6277 6278 6279 +3 6277 6261 6278 +3 6261 6262 6278 +3 6280 6262 6263 +3 6278 6262 6280 +3 5285 5299 6264 +3 5299 6281 6265 +3 5299 6265 6264 +3 6283 6284 6285 +3 6267 6284 6283 +3 6267 6283 6253 +3 6253 6283 6286 +3 6287 6253 6286 +3 6268 6253 6287 +3 6288 6268 6287 +3 6269 6268 6288 +3 6270 6269 6288 +3 6272 6270 6289 +3 6289 6270 6288 +3 6290 6272 6291 +3 6291 6272 6289 +3 6290 6291 6292 +3 6274 6294 5305 +3 6275 5260 6295 +3 6276 6296 4702 +3 6278 6297 6279 +3 6297 6278 6316 +3 6298 6316 6280 +3 6280 6316 6278 +3 6298 6299 6316 +3 5299 6300 6282 +3 5299 6282 6281 +3 6300 5299 6301 +3 6303 6283 6285 +3 6304 6303 6285 +3 6286 6283 6305 +3 6305 6283 6303 +3 6287 6286 6306 +3 6306 6286 6305 +3 5300 6287 6306 +3 6288 6287 5300 +3 5307 6288 5300 +3 5307 6307 6288 +3 6307 6289 6288 +3 5301 6291 6307 +3 6291 6289 6307 +3 5301 6308 6291 +3 6308 6292 6291 +3 6309 6293 6292 +3 6309 6292 6310 +3 6310 6292 6308 +3 5305 6311 6312 +3 6311 5305 6294 +3 6295 5260 6313 +3 6296 6314 4702 +3 6297 6316 6315 +3 6317 6316 6299 +3 5322 6301 5299 +3 6319 6302 6301 +3 6319 6301 5322 +3 6319 5322 6320 +3 6304 6324 6303 +3 6303 6324 5324 +3 6305 6303 5324 +3 6306 6305 5324 +3 5300 6306 5324 +3 6307 5307 6325 +3 5301 6307 6326 +3 6307 6325 6326 +3 5301 6326 6308 +3 6310 6308 6327 +3 6327 6308 6326 +3 6327 6328 6310 +3 6329 6348 5309 +3 6329 5309 6330 +3 6330 5309 5310 +3 5310 6331 6330 +3 6312 5310 5305 +3 6331 5310 6312 +3 6313 5260 6332 +3 6314 6333 4702 +3 6334 6315 5313 +3 5312 5313 6316 +3 6316 5313 6315 +3 5312 6316 6317 +3 6335 6317 6318 +3 6335 5312 6317 +3 6335 6336 5312 +3 6337 6320 5322 +3 6337 5322 6338 +3 6339 6340 6341 +3 6340 6339 6342 +3 6342 6362 6343 +3 6322 6343 6362 +3 6322 6362 6321 +3 6321 6362 5324 +3 6323 6321 5324 +3 6324 6323 5324 +3 5307 5315 6325 +3 6326 6325 5315 +3 6327 6326 4684 +3 6327 4684 6328 +3 6328 4684 6344 +3 5308 6345 6346 +3 5309 6347 5308 +3 5308 6347 6345 +3 6348 6347 5309 +3 6332 5260 6349 +3 6333 6350 4702 +3 5313 6351 6334 +3 6353 6352 6351 +3 5312 6353 6351 +3 5312 6351 5313 +3 5312 6354 6353 +3 6355 6354 5312 +3 6336 6355 5312 +3 5322 6356 6338 +3 6356 5322 6357 +3 6358 6359 6360 +3 6341 6359 6358 +3 6341 6358 6339 +3 6339 6358 6361 +3 6342 6339 6362 +3 6362 6339 6361 +3 5324 6362 6378 +3 6363 6344 4684 +3 6364 5308 6365 +3 6365 5308 6346 +3 6349 5260 6366 +3 6350 6367 4702 +3 6353 6368 6352 +3 6369 6370 6357 +3 6369 6357 5322 +3 6370 6369 6371 +3 6373 6374 6375 +3 6374 6373 6376 +3 6376 6378 6377 +3 6360 6377 6378 +3 6360 6378 6358 +3 6361 6358 6378 +3 6362 6361 6378 +3 6363 4684 6379 +3 6380 5308 6381 +3 6381 5308 6364 +3 6366 5260 6382 +3 6367 6383 4702 +3 6384 6372 6371 +3 6384 6371 6369 +3 6384 6369 6385 +3 6388 6386 6387 +3 6375 6386 6388 +3 6375 6388 6373 +3 6376 6373 6388 +3 6378 6376 5346 +3 5346 6376 6388 +3 6379 4684 6389 +3 6381 6391 6380 +3 6391 6390 6380 +3 6391 6381 6392 +3 5343 6393 6394 +3 5333 5343 6395 +3 5343 6394 6395 +3 6395 5334 5333 +3 6396 5334 6395 +3 6397 6382 5260 +3 6383 6398 4702 +3 6399 6385 6369 +3 6399 6369 6400 +3 6400 6402 6401 +3 6403 6404 6405 +3 6387 6406 6388 +3 6407 6389 4684 +3 6390 6408 4698 +3 6380 6390 4698 +3 6391 6409 6390 +3 6409 6408 6390 +3 6410 6391 6392 +3 6410 6411 6391 +3 6411 6409 6391 +3 6411 6412 6413 +3 6410 6412 6411 +3 6412 6414 6413 +3 6416 6417 6415 +3 6419 6420 6421 +3 6419 6421 6393 +3 6419 6393 5343 +3 6422 5344 6396 +3 5344 5334 6396 +3 1262 6397 5260 +3 6424 6397 1262 +3 6398 6425 4702 +3 6369 6428 6400 +3 6402 6400 6428 +3 6426 6402 6428 +3 6426 6428 6427 +3 6432 6465 6433 +3 6433 6465 6434 +3 6465 6404 6435 +3 6434 6465 6435 +3 6388 6406 6405 +3 5346 6388 6405 +3 4684 6436 6407 +3 6436 4684 6437 +3 6438 6472 2279 +3 6438 2279 6439 +3 6439 2279 5337 +3 6440 6441 6439 +3 6440 6439 5337 +3 5347 5348 6442 +3 5349 6443 5348 +3 5348 6443 6442 +3 6443 5349 5350 +3 6444 6443 5350 +3 4698 6408 5351 +3 6409 6445 6408 +3 6411 6445 6409 +3 6446 6413 6447 +3 6446 6445 6413 +3 6445 6411 6413 +3 6414 6447 6413 +3 6415 6448 6449 +3 6415 5352 6448 +3 6417 6418 6450 +3 6417 6450 5357 +3 6451 5357 6450 +3 6452 6453 6454 +3 6452 6420 6455 +3 6454 6420 6452 +3 6420 6419 6455 +3 6419 6456 6455 +3 5343 6456 6419 +3 5343 6457 6456 +3 6457 6458 6456 +3 6423 5344 6422 +3 6423 6457 5344 +3 6457 5343 5344 +3 6459 6424 1262 +3 6425 6460 4702 +3 6461 6428 5359 +3 5359 6428 5354 +3 6461 5359 6462 +3 6462 5359 6463 +3 6463 5359 6464 +3 5359 6465 6466 +3 6464 5359 6466 +3 6466 6465 6467 +3 6467 6465 6468 +3 6430 6468 6465 +3 6430 6465 6429 +3 6431 6429 6465 +3 6432 6431 6465 +3 6404 6465 6405 +3 6495 6469 4684 +3 6469 6437 4684 +3 2279 6471 6470 +3 6472 6471 2279 +3 6473 6441 6440 +3 5356 6474 6475 +3 6442 5356 5347 +3 6474 5356 6442 +3 5350 6501 6444 +3 6444 6501 6476 +3 6477 6445 6478 +3 6477 6408 6445 +3 6479 6478 6445 +3 6446 6479 6445 +3 6480 6448 5352 +3 6480 5352 6481 +3 6482 5357 6483 +3 6483 5357 6451 +3 5357 6484 5358 +3 6482 6484 5357 +3 6484 6485 5358 +3 6486 6487 6488 +3 6453 6486 6488 +3 6453 6452 6486 +3 6452 6489 6486 +3 6455 6490 6452 +3 6490 6489 6452 +3 6491 6456 6492 +3 6491 6490 6456 +3 6490 6455 6456 +3 6458 6492 6456 +3 6493 6459 1262 +3 6460 6494 4702 +3 6541 6496 6495 +3 4684 6541 6495 +3 4684 5361 6541 +3 2279 6497 6498 +3 6497 2279 6470 +3 6499 6473 6440 +3 4750 5356 6500 +3 6500 5356 6475 +3 6501 6502 6476 +3 4771 5351 6503 +3 6503 5351 6408 +3 6503 6408 6477 +3 5352 5374 6481 +3 6481 5374 6504 +3 6482 6483 6505 +3 6505 6506 6507 +3 6505 6507 6482 +3 6482 6507 6484 +3 6485 6507 4739 +3 6485 6484 6507 +3 6508 6509 6510 +3 6487 6508 6510 +3 6487 6486 6508 +3 6486 6511 6508 +3 6489 6512 6486 +3 6512 6511 6486 +3 6513 6490 6514 +3 6513 6512 6490 +3 6512 6489 6490 +3 6491 6514 6490 +3 6515 6493 1262 +3 6494 6516 4702 +3 6539 6496 6541 +3 6517 6496 6539 +3 6518 2279 6519 +3 2279 6520 6519 +3 6520 2279 6498 +3 6440 6521 6499 +3 5363 5360 6523 +3 6522 5363 6523 +3 4750 6500 6524 +3 6557 6524 6525 +3 4750 6524 6557 +3 4750 6557 6526 +3 4750 6526 6527 +3 4750 6527 6501 +3 6502 6501 6527 +3 6503 6528 4771 +3 5374 6529 6530 +3 6530 6504 5374 +3 4739 6507 6506 +3 4739 6506 6531 +3 6532 6509 6508 +3 6533 6509 6532 +3 6511 6534 6508 +3 6534 6532 6508 +3 6535 6512 6536 +3 6535 6534 6512 +3 6534 6511 6512 +3 6513 6536 6512 +3 6537 6515 1262 +3 1262 6595 6537 +3 4702 6516 4845 +3 6516 6538 4845 +3 6540 6539 6541 +3 6542 6541 5361 +3 6542 5361 6543 +3 6544 5362 6545 +3 5362 6546 6545 +3 5362 6547 6546 +3 6547 2279 6548 +3 6548 2279 6549 +3 6549 2279 6550 +3 2279 6551 6550 +3 6518 6551 2279 +3 6553 6521 5379 +3 5379 6521 6440 +3 5364 5363 6522 +3 5364 6522 6554 +3 6523 6555 6522 +3 6555 6554 6522 +3 5360 6556 6523 +3 6556 6555 6523 +3 5360 5365 6556 +3 6528 6558 4771 +3 5374 6559 6560 +3 6560 6529 5374 +3 6561 5375 6562 +3 6562 5375 4739 +3 6562 4739 6531 +3 6532 6563 6533 +3 6534 6563 6532 +3 6564 6563 6534 +3 6535 6564 6534 +3 6538 6566 4845 +3 4776 6568 5368 +3 6567 6568 4776 +3 6568 6569 5368 +3 5368 6569 6570 +3 6598 5368 6571 +3 5368 6570 6571 +3 6598 6571 6572 +3 6572 6573 6598 +3 5361 6575 6543 +3 3635 6576 5361 +3 5361 6576 6575 +3 5362 6577 3635 +3 6577 6576 3635 +3 6544 6577 5362 +3 5362 2279 6547 +3 5379 6552 6553 +3 5379 6578 6552 +3 5364 6554 6603 +3 6555 6579 6554 +3 6579 6603 6554 +3 6556 6580 6555 +3 6580 6579 6555 +3 5365 5370 6580 +3 6556 5365 6580 +3 5373 6581 6582 +3 6581 5373 6583 +3 6558 5373 4771 +3 6583 5373 6558 +3 5374 6584 6585 +3 6585 6559 5374 +3 5375 6586 6615 +3 6587 5375 6561 +3 6586 5375 6587 +3 6623 6588 5366 +3 6588 6589 5366 +3 6590 6591 5366 +3 6590 5366 6589 +3 6591 6592 6593 +3 6591 6593 5366 +3 5366 6594 5376 +3 5366 6593 6594 +3 5376 6594 5378 +3 6595 6565 6537 +3 6566 6596 4845 +3 4790 6597 6567 +3 6597 6568 6567 +3 6599 6573 6574 +3 6599 6598 6573 +3 6599 6600 6598 +3 5379 6601 6602 +3 5379 6602 6578 +3 6603 6579 6604 +3 6604 6580 6605 +3 6604 6579 6580 +3 5383 5373 6606 +3 6607 6606 5373 +3 5373 6582 6608 +3 6607 5373 6609 +3 6609 5373 6610 +3 6610 5373 6608 +3 6609 6611 5392 +3 6610 6611 6609 +3 6611 6612 5392 +3 5392 5374 5384 +3 5392 6612 5374 +3 6612 6613 5374 +3 6613 6584 5374 +3 6586 6614 6615 +3 6616 6614 6586 +3 6587 6616 6586 +3 6618 6619 5385 +3 5385 6619 6620 +3 6623 5385 6621 +3 6621 5385 6620 +3 6621 6622 6623 +3 6625 6626 6588 +3 6625 6588 6624 +3 6624 6588 6623 +3 6626 6627 6628 +3 6626 6628 6588 +3 6588 6628 6589 +3 6629 6590 6589 +3 6629 6589 6630 +3 6630 6589 6628 +3 6592 6629 6630 +3 6593 6592 6630 +3 6593 6630 5395 +3 5378 6593 5395 +3 6594 6593 5378 +3 6631 6595 1262 +3 6596 6632 4845 +3 6597 4790 6633 +3 4790 6660 6633 +3 6660 6634 6633 +3 6660 6598 6635 +3 6598 6636 6635 +3 6637 6636 6598 +3 6600 6637 6598 +3 5379 6638 6601 +3 6638 5379 6639 +3 6603 6640 6680 +3 6641 6605 6580 +3 6580 5370 6641 +3 5391 6642 6643 +3 5383 6644 5391 +3 5391 6644 6642 +3 6645 6644 5383 +3 6606 6645 5383 +3 6646 6645 6606 +3 6647 6646 6606 +3 6647 6648 6649 +3 6647 6606 6648 +3 6606 6607 6648 +3 6609 6648 6607 +3 5385 6651 6652 +3 6652 6653 5385 +3 6617 6618 5385 +3 6617 5385 6653 +3 6654 6623 6622 +3 6624 6623 6655 +3 6655 6623 6654 +3 6656 6625 6624 +3 6656 6624 6657 +3 6657 6624 6655 +3 6627 6656 6657 +3 6628 6627 6657 +3 6628 6657 5396 +3 6628 5396 5395 +3 6630 6628 5395 +3 1262 6658 6631 +3 6659 4845 6632 +3 6661 6660 6635 +3 6662 6661 6635 +3 6663 6664 6665 +3 6665 6666 6663 +3 5397 6667 6668 +3 5397 6668 6669 +3 6669 6670 5397 +3 6670 6671 5411 +3 6671 6672 5411 +3 5399 5411 6673 +3 5411 6672 6673 +3 6674 5399 6673 +3 6675 6639 5379 +3 5417 5400 6700 +3 6678 6700 5400 +3 6679 6680 6640 +3 6641 6680 6679 +3 6641 5430 6680 +3 6641 5370 5430 +3 4814 6681 6682 +3 4814 6643 6681 +3 4814 5391 6643 +3 6683 6649 6648 +3 6609 5403 6648 +3 6615 6650 5407 +3 5407 6650 6684 +3 6685 6686 5385 +3 6651 5385 6686 +3 5408 6654 6687 +3 6655 6654 5408 +3 5396 6655 5408 +3 6657 6655 5396 +3 6659 6689 4845 +3 6663 6690 6664 +3 6666 6690 6663 +3 5409 6691 6667 +3 6692 5397 6670 +3 5411 5412 6692 +3 5411 6692 6670 +3 5399 6674 5414 +3 6674 6693 5414 +3 6694 6675 5379 +3 6724 6723 6676 +3 6695 6724 6676 +3 6728 6727 6695 +3 6677 6729 6695 +3 6729 6728 6695 +3 6677 5415 6729 +3 5417 6696 6697 +3 5417 6698 6696 +3 6699 6698 5417 +3 6700 6699 5417 +3 5400 6701 6678 +3 5400 6702 6701 +3 5420 4814 6682 +3 6703 5420 6682 +3 6648 6704 6683 +3 5403 6705 6704 +3 5403 6704 6648 +3 5407 6684 6706 +3 5407 6706 5427 +3 6707 5427 6706 +3 5385 6707 6708 +3 5385 6708 6709 +3 6685 5385 6709 +3 5408 6710 6711 +3 6710 5408 6687 +3 6688 6658 6712 +3 6689 6713 4845 +3 6714 5409 6715 +3 6714 6691 5409 +3 6692 6716 5428 +3 5412 6716 6692 +3 5412 6717 6716 +3 5412 5411 6717 +3 6745 6717 5411 +3 6718 6745 5411 +3 6718 5411 6719 +3 6719 5411 5414 +3 6719 5414 6693 +3 5379 6720 6694 +3 6676 6722 6721 +3 6723 6722 6676 +3 6725 6724 6695 +3 6726 6725 6695 +3 6727 6726 6695 +3 6729 5415 6730 +3 5415 5429 6730 +3 6731 5429 6732 +3 6732 5429 5417 +3 6732 5417 6697 +3 5400 6733 6702 +3 5400 6734 6733 +3 6735 5420 6703 +3 5403 6736 6705 +3 5403 5431 6736 +3 5431 6737 6736 +3 5431 6738 6737 +3 5408 6711 6739 +3 6712 6658 6740 +3 6713 6741 4845 +3 6715 5409 6742 +3 5409 4199 6742 +3 5428 6743 4199 +3 4199 6743 6742 +3 6716 6744 5428 +3 6744 6743 5428 +3 6717 6744 6716 +3 5444 6746 6720 +3 6676 6747 4828 +3 6747 6676 6721 +3 6766 6749 6748 +3 5429 6766 6748 +3 5429 6748 6730 +3 6766 5429 6767 +3 6750 5429 6731 +3 6767 5429 6750 +3 5400 6751 6734 +3 5435 6752 6751 +3 5435 6751 5400 +3 6753 5420 6735 +3 6753 6754 5420 +3 5431 6755 6738 +3 6771 6756 6755 +3 5431 6771 6755 +3 6771 5431 6757 +3 6758 6774 5432 +3 5439 6758 5432 +3 6782 6739 6759 +3 6740 6658 6760 +3 6741 6761 4845 +3 6763 6746 5444 +3 4828 6765 6764 +3 4828 6747 6765 +3 5435 6768 6752 +3 6768 5435 6769 +3 5420 6754 5438 +3 6754 6770 5438 +3 6772 6771 6757 +3 5432 6772 6757 +3 6773 6772 5432 +3 6774 6773 5432 +3 5439 6775 6758 +3 6775 5439 6776 +3 5443 6777 6778 +3 5443 6779 6777 +3 6780 6779 5440 +3 6759 6781 6782 +3 6783 6760 4422 +3 4422 6760 6658 +3 6761 6784 4845 +3 5444 6785 6786 +3 6762 5444 6786 +3 6763 5444 6762 +3 5434 4828 6787 +3 6788 4828 6764 +3 6787 4828 6788 +3 6789 6791 5446 +3 6790 6791 6789 +3 5446 6792 5441 +3 6791 6792 5446 +3 6792 6793 5441 +3 4206 5441 6794 +3 5441 6793 6794 +3 4206 6794 6795 +3 6796 4206 6795 +3 5435 6797 6769 +3 5436 6797 5435 +3 6798 5436 6799 +3 6799 5436 5438 +3 6799 5438 6770 +3 6800 6776 5439 +3 6801 5443 6802 +3 6802 5443 6778 +3 5440 6803 6780 +3 5440 6804 6803 +3 6804 5440 6782 +3 4422 6805 6783 +3 6784 6806 4845 +3 6807 6808 6809 +3 6809 6810 6807 +3 5444 6811 6785 +3 6787 6827 5434 +3 6812 6787 6788 +3 6812 6813 6787 +3 6813 6827 6787 +3 6812 6814 6813 +3 5446 6815 6789 +3 6816 4206 6796 +3 5436 6817 6797 +3 6798 6817 5436 +3 5439 6818 6800 +3 6819 6818 5447 +3 5443 6801 5447 +3 5447 6801 6820 +3 6805 4422 6821 +3 6822 4845 6806 +3 6807 6823 6808 +3 6810 6823 6807 +3 5456 6824 6825 +3 6811 5444 5456 +3 6825 6811 5456 +3 6826 6846 6827 +3 6846 5434 6827 +3 6828 6827 6813 +3 6814 6828 6813 +3 6815 5451 6829 +3 6816 6830 4206 +3 5447 6831 6819 +3 6831 5447 6820 +3 5454 5453 6832 +3 6821 4422 6833 +3 6822 6834 4845 +3 6858 6835 6836 +3 6836 6837 6858 +3 6882 6838 6839 +3 6882 6839 6840 +3 6840 6841 5455 +3 5456 6842 6843 +3 5456 6843 6844 +3 6844 6824 5456 +3 5434 6846 6845 +3 5451 6847 6829 +3 6848 6847 5451 +3 6849 4846 6830 +3 6850 4846 6849 +3 5459 6832 6851 +3 5459 5454 6832 +3 6851 6832 6852 +3 5453 5458 6832 +3 6852 6832 5458 +3 5460 5459 6853 +3 5459 6851 6853 +3 5460 6853 6854 +3 6854 6855 5460 +3 6833 4422 6856 +3 6834 6857 4845 +3 6859 6835 6858 +3 6860 6858 6837 +3 6860 6861 6858 +3 6882 6862 6863 +3 6863 6838 6882 +3 6864 5455 6841 +3 6864 6865 5455 +3 5456 6866 6842 +3 5434 6868 6867 +3 5434 6845 6868 +3 5451 6869 6848 +3 6870 4846 6871 +3 6871 4846 6850 +3 6870 5462 4846 +3 6872 6852 5458 +3 5458 5471 6872 +3 5460 6855 5464 +3 6855 6873 5464 +3 5467 5466 6874 +3 5467 6874 5465 +3 6875 6856 4422 +3 6857 6876 4845 +3 6878 6879 5475 +3 6879 6880 5475 +3 6880 6859 5475 +3 6858 5475 6859 +3 6858 5476 5475 +3 6858 5469 5476 +3 5469 6858 6927 +3 6927 6858 6861 +3 6927 6861 6881 +3 6882 6883 6884 +3 6862 6882 6884 +3 6885 5455 6865 +3 6866 5456 6886 +3 5434 6887 4850 +3 5434 6888 6887 +3 6867 6888 5434 +3 6889 6890 5478 +3 5470 6891 6892 +3 5470 6890 6891 +3 5451 6893 6869 +3 6893 5451 5479 +3 6894 6893 5479 +3 6895 6870 6871 +3 6896 6895 6871 +3 6897 6870 6895 +3 5462 6870 6897 +3 5462 6898 4859 +3 5462 6897 6898 +3 5471 6900 6899 +3 5471 6899 6872 +3 5464 6901 5471 +3 6873 6901 5464 +3 5466 5465 6874 +3 4422 6902 6875 +3 6876 6903 4845 +3 5473 5472 6904 +3 5473 6904 5474 +3 5475 6877 6878 +3 5475 5474 6877 +3 5474 6905 6877 +3 5469 6927 5477 +3 6906 6927 6881 +3 6906 6907 6927 +3 6882 6908 6909 +3 6883 6882 6909 +3 4864 6910 6911 +3 6885 6910 4864 +3 6885 4864 5455 +3 5456 6912 6913 +3 6913 6886 5456 +3 6887 6914 4850 +3 6889 5478 6915 +3 6892 6949 5470 +3 6892 6916 6949 +3 5479 6917 6894 +3 6918 5479 6919 +3 6896 5479 6895 +3 6896 6919 5479 +3 5479 6920 6895 +3 6920 6897 6895 +3 5480 6898 6920 +3 6898 6897 6920 +3 4859 6898 5480 +3 5471 6921 6900 +3 6922 6921 5471 +3 6901 6922 5471 +3 6902 4422 6923 +3 6903 6924 4845 +3 5474 6925 6905 +3 5475 6925 5474 +3 6926 6925 5475 +3 5476 5477 6927 +3 6928 6927 6907 +3 6928 6929 6927 +3 6928 6930 6929 +3 4874 6931 6932 +3 6932 6933 4874 +3 6934 6935 6936 +3 6936 6937 6938 +3 6935 6937 6936 +3 6937 6939 6938 +3 6882 6940 6908 +3 6941 6940 6882 +3 6942 6941 6882 +3 6882 6943 6942 +3 4864 6944 6882 +3 6882 6944 6943 +3 6911 6944 4864 +3 6980 6945 6946 +3 6980 6946 6912 +3 6980 6912 5456 +3 6914 6947 4850 +3 6948 6915 5478 +3 5479 6950 6917 +3 6951 6950 5479 +3 6918 6951 5479 +3 6952 6953 6954 +3 6954 6955 6952 +3 4422 6956 6957 +3 4422 6957 6923 +3 6924 6958 4845 +3 6926 6959 6925 +3 6960 6959 6926 +3 5476 6960 6926 +3 6961 6960 5476 +3 6927 6961 5476 +3 6962 6961 6927 +3 6963 6962 6927 +3 6964 6963 6927 +3 6929 6964 6927 +3 6965 6964 6929 +3 6965 6966 6967 +3 6965 6929 6966 +3 6968 6966 6930 +3 6930 6966 6929 +3 6968 6969 6966 +3 6970 6971 6972 +3 6970 6973 6974 +3 6972 6973 6970 +3 6973 6975 6974 +3 5485 6976 6977 +3 6931 4874 6977 +3 6977 4874 5485 +3 6936 6933 6934 +3 6938 6978 6936 +3 6979 6938 6939 +3 6978 6938 6979 +3 4850 6947 6981 +3 5478 6982 6948 +3 6983 6982 5478 +3 6985 6986 6987 +3 6986 6988 6987 +3 6952 6989 6953 +3 6955 6989 6952 +3 4422 6990 6956 +3 6991 4845 6958 +3 6966 6992 6967 +3 6969 6992 6966 +3 6993 6971 6970 +3 6994 6971 6993 +3 6974 6995 6970 +3 6995 6993 6970 +3 6996 6974 6975 +3 6995 6974 6996 +3 5485 6997 6998 +3 5485 6998 6976 +3 6936 5496 6933 +3 5496 6936 6978 +3 6999 6978 6979 +3 5496 6978 6999 +3 7001 4850 6981 +3 7028 4850 7001 +3 7002 7003 7004 +3 7002 7005 7006 +3 7004 7005 7002 +3 7005 7007 7006 +3 7008 7009 5488 +3 6984 5488 7009 +3 6985 5488 6984 +3 6987 5488 6985 +3 5488 7010 7011 +3 5488 6988 7010 +3 5488 6987 6988 +3 6990 4422 7012 +3 6991 7013 4845 +3 5494 7014 7015 +3 7015 7016 5494 +3 7016 7017 5494 +3 7018 7017 7019 +3 7020 6993 7021 +3 7020 7018 6993 +3 6994 6993 7019 +3 7019 6993 7018 +3 6995 7021 6993 +3 7022 7021 6995 +3 6996 7022 6995 +3 7024 7025 5485 +3 7025 6997 5485 +3 7026 5496 6999 +3 7000 4877 7027 +3 7028 7029 4850 +3 7002 7030 7003 +3 7030 7002 7031 +3 7002 7006 7031 +3 7007 7032 7006 +3 7032 7031 7006 +3 5486 7032 7008 +3 7008 7032 7007 +3 5488 5486 7008 +3 5488 7011 5501 +3 5501 7011 7033 +3 7034 7035 7036 +3 7036 7037 7034 +3 7037 7038 7034 +3 7040 7012 4422 +3 7013 7041 4845 +3 5493 7042 7043 +3 5492 7042 5493 +3 7014 5494 7043 +3 7043 5494 5493 +3 5494 7017 5495 +3 5495 7017 7018 +3 7044 7018 7020 +3 5495 7018 7044 +3 5485 7045 7046 +3 7046 7047 5485 +3 7023 5485 7047 +3 7024 5485 7023 +3 7048 5496 7026 +3 4877 7049 7050 +3 7050 7027 4877 +3 4850 7029 7051 +3 7052 7031 5497 +3 7031 7032 5497 +3 7053 5501 7033 +3 7054 7056 7081 +3 7055 7056 7054 +3 7081 7057 5512 +3 7056 7057 7081 +3 7057 7058 5512 +3 7058 7059 5512 +3 7059 7060 5512 +3 7060 7061 5512 +3 5515 7062 7063 +3 5515 7063 7064 +3 5515 7064 7065 +3 5515 7065 7035 +3 7035 7034 5515 +3 7034 7038 7066 +3 7034 7066 5489 +3 7067 7066 7068 +3 7067 5489 7066 +3 5489 7067 5502 +3 7040 4422 7039 +3 4422 7069 7039 +3 7070 4845 7041 +3 7071 7072 5504 +3 7072 5492 5504 +3 7072 7042 5492 +3 5495 7073 5519 +3 7073 5495 7044 +3 5485 7074 7075 +3 7045 5485 7075 +3 5496 7048 4962 +3 4962 7048 7076 +3 7049 4877 7077 +3 7051 7078 4850 +3 7052 5497 7079 +3 7080 5501 7053 +3 7082 7054 7081 +3 5512 5510 7081 +3 5512 7061 7083 +3 5512 7083 5514 +3 5514 7083 7084 +3 5514 7084 7085 +3 5514 7114 5515 +3 7086 7114 5514 +3 7087 7088 5515 +3 7088 7062 5515 +3 7067 7068 7089 +3 7067 7089 5531 +3 7070 7093 4845 +3 5519 7094 7071 +3 5519 7071 5504 +3 7094 5519 7095 +3 5519 7096 7123 +3 5519 7097 7096 +3 5519 7073 7097 +3 7098 7100 5527 +3 7099 7100 7098 +3 5527 7100 5485 +3 7100 7074 5485 +3 4962 7101 7102 +3 7101 4962 7076 +3 7077 4877 7103 +3 7078 7104 4850 +3 5497 5535 7079 +3 7079 5535 7105 +3 7080 7106 5501 +3 5522 7107 7108 +3 4292 7107 5522 +3 5522 7108 7109 +3 5522 7109 7110 +3 7111 5522 7110 +3 5523 7082 7081 +3 7112 7082 5523 +3 7081 5510 5523 +3 5514 7085 7113 +3 7086 5514 7113 +3 7115 5515 7114 +3 7087 5515 7115 +3 7116 5531 7117 +3 7090 5531 7089 +3 7090 7117 5531 +3 4422 7091 7092 +3 4422 7118 7091 +3 7069 4422 7092 +3 7119 4845 7093 +3 7119 7120 4845 +3 7095 5519 7121 +3 5519 7122 7121 +3 7123 7122 5519 +3 7145 7124 5527 +3 7098 5527 7124 +3 7102 7125 4962 +3 4963 7129 7130 +3 7103 4877 4963 +3 7130 7103 4963 +3 4850 7131 7154 +3 4850 7104 7131 +3 7132 7105 5535 +3 5501 7106 5558 +3 7106 7133 5558 +3 7107 4292 7134 +3 5522 7111 7135 +3 5549 7135 7136 +3 5549 5522 7135 +3 5523 7137 7112 +3 7162 7138 7137 +3 5523 7162 7137 +3 5523 5536 7162 +3 7139 7171 5538 +3 7139 5538 7140 +3 7140 5538 5531 +3 5531 7141 7140 +3 7142 7141 5531 +3 7116 7142 5531 +3 7118 4422 7143 +3 7120 7144 4845 +3 7180 7145 5527 +3 7146 7124 7145 +3 4962 7147 7148 +3 7147 4962 7125 +3 7192 7149 7150 +3 7126 7192 7150 +3 7127 7192 7126 +3 7127 5533 7192 +3 5533 7151 5534 +3 5533 7128 7151 +3 5533 7127 7128 +3 7151 7152 5534 +3 7129 4963 7153 +3 7154 7155 4850 +3 5535 7156 7132 +3 7156 5535 7157 +3 7133 7158 5558 +3 7159 7134 4292 +3 5549 7136 7160 +3 5549 7160 7161 +3 5536 7163 7162 +3 7164 7163 5536 +3 7164 5536 4914 +3 7167 7169 7168 +3 7169 5538 7170 +3 7171 7170 5538 +3 5539 7172 7173 +3 7173 7174 5539 +3 5540 5539 7175 +3 5539 7174 7175 +3 7176 5540 7175 +3 4422 7177 7143 +3 7144 7178 4845 +3 5543 7179 7180 +3 7145 7180 7181 +3 7182 7146 7145 +3 7182 7145 7183 +3 7183 7145 7181 +3 7182 7183 7184 +3 7186 5555 4939 +3 7186 4939 7187 +3 7188 7187 4939 +3 7148 7189 4962 +3 7192 7190 7191 +3 7149 7192 7191 +3 5534 7193 5546 +3 5534 7152 7193 +3 5546 7193 5547 +3 7153 4963 7194 +3 4850 7195 7237 +3 4850 7155 7195 +3 5535 7196 7157 +3 7197 7196 5535 +3 7198 5558 7158 +3 7200 7159 4292 +3 7200 4292 7201 +3 5549 7202 7203 +3 5549 7161 7202 +3 7204 7205 5549 +3 7206 5549 7205 +3 7208 7207 7164 +3 4914 7208 7164 +3 7209 7208 4914 +3 4914 5559 7209 +3 5559 7210 7209 +3 7211 7165 7166 +3 7212 7211 7166 +3 7167 7165 7211 +3 7169 7167 7211 +3 7213 7214 7215 +3 7172 7213 7215 +3 7172 5539 7213 +3 5539 7216 7213 +3 5539 5551 7217 +3 5539 7217 7216 +3 7218 7217 5551 +3 7218 5551 5540 +3 7218 5540 7176 +3 7177 4422 7219 +3 7178 7220 4845 +3 7179 5542 7221 +3 5543 5542 7179 +3 7180 7179 7222 +3 7222 7179 7221 +3 7181 7180 7223 +3 7223 7180 7222 +3 5552 7181 7223 +3 7183 7181 5552 +3 5563 7183 5552 +3 7184 7183 5563 +3 5563 7224 7185 +3 5563 7185 7184 +3 5563 7226 7225 +3 7224 5563 7225 +3 5555 7227 7228 +3 7186 7227 5555 +3 7229 7188 4941 +3 7188 4939 4941 +3 7229 4941 7230 +3 7189 7231 4962 +3 7192 7232 7190 +3 7233 7232 7192 +3 7192 5545 7233 +3 5545 7234 7233 +3 5556 7235 5545 +3 5545 7235 7234 +3 7193 7235 5556 +3 5547 7193 5556 +3 7194 4963 7236 +3 7239 7197 4945 +3 7239 4945 7240 +3 7241 7198 7199 +3 7242 7241 7199 +3 7198 7241 5558 +3 4292 7243 7201 +3 5549 7203 7244 +3 7204 5549 7244 +3 5549 7206 7245 +3 7246 5549 7245 +3 5559 7247 7210 +3 5560 7248 7249 +3 5560 7249 5559 +3 5559 7249 7247 +3 7212 7249 7248 +3 7211 7212 7248 +3 7250 7251 7252 +3 7214 7250 7252 +3 7214 7213 7250 +3 7213 7253 7250 +3 7254 7253 7216 +3 7253 7213 7216 +3 7217 7254 7216 +3 7255 7254 7217 +3 7218 7255 7217 +3 4422 7256 7219 +3 7220 7257 5643 +3 5542 5562 7258 +3 5542 7258 7221 +3 7258 7259 7221 +3 7223 7259 5569 +3 7223 7222 7259 +3 7222 7221 7259 +3 7260 7226 4960 +3 4960 7226 5563 +3 7260 4960 7261 +3 7228 4960 5555 +3 7261 4960 7228 +3 4941 5570 7262 +3 7262 7230 4941 +3 7231 7263 4962 +3 7236 4963 7264 +3 7237 7238 4373 +3 7238 7265 4373 +3 7266 7240 4945 +3 7241 7242 7267 +3 7267 5558 7241 +3 7267 7268 5558 +3 4292 5576 7243 +3 7243 5576 7269 +3 4346 7270 7271 +3 4346 7246 7270 +3 4346 5549 7246 +3 7250 7272 7251 +3 7253 7272 7250 +3 7273 7272 7253 +3 7254 7273 7253 +3 7256 4422 7274 +3 5643 7257 7275 +3 7276 5643 7275 +3 7258 7277 7278 +3 7258 5562 7277 +3 5562 5568 7277 +3 5569 7258 7279 +3 5569 7259 7258 +3 7258 7278 7279 +3 5570 4961 7280 +3 5570 7280 7281 +3 5570 7281 7262 +3 4962 7263 7282 +3 7283 4962 7282 +3 7264 4963 7284 +3 4373 7285 7286 +3 7285 4373 7265 +3 7287 7266 4945 +3 7316 7287 4945 +3 7268 7288 5558 +3 7289 7269 5576 +3 4346 7271 7290 +3 7290 5566 4346 +3 7290 7291 5566 +3 7291 5571 5566 +3 4422 7292 7274 +3 7276 7293 5643 +3 7295 7297 7298 +3 7296 7297 7295 +3 7297 7299 7298 +3 5568 7300 7301 +3 5587 7300 5568 +3 7301 7277 5568 +3 5569 7279 7302 +3 4961 7303 7304 +3 7280 4961 7304 +3 4962 7283 7305 +3 7306 4962 7305 +3 7284 4963 7307 +3 7286 7308 4373 +3 7309 7310 7311 +3 7309 7312 7313 +3 7311 7312 7309 +3 7312 7314 7313 +3 7316 4945 7315 +3 5580 5558 7317 +3 7317 5558 7288 +3 5576 7319 7318 +3 5576 7318 7289 +3 7320 5581 7322 +3 7322 5581 7323 +3 7361 5571 7324 +3 5571 7291 7324 +3 7325 7326 7327 +3 7327 7328 7325 +3 7330 7331 7332 +3 7331 7333 7332 +3 4422 7334 7335 +3 7335 7292 4422 +3 7293 7336 5643 +3 5585 7337 7338 +3 7294 5585 7338 +3 7294 7295 5585 +3 7295 7339 5585 +3 7340 7298 7341 +3 7340 7339 7298 +3 7339 7295 7298 +3 7299 7341 7298 +3 5587 7342 7300 +3 7302 7343 7377 +3 4961 7344 7303 +3 7345 4962 7346 +3 7346 4962 7306 +3 7307 4963 7347 +3 7308 7348 4373 +3 7309 7349 7310 +3 7313 7349 7309 +3 7350 7349 7313 +3 7314 7350 7313 +3 7464 7351 7352 +3 7315 4945 7464 +3 7352 7315 7464 +3 5580 7317 7353 +3 5576 7355 7319 +3 5576 7356 7355 +3 5576 7357 7356 +3 7321 5581 7320 +3 7321 7357 5581 +3 7357 5576 5581 +3 5581 7358 7323 +3 5581 7359 7358 +3 7361 7360 5581 +3 7362 7326 7325 +3 7363 7326 7362 +3 7364 7325 7328 +3 7364 7365 7325 +3 7365 7362 7325 +3 7364 7366 7365 +3 7367 7329 7330 +3 7368 7329 7367 +3 7369 7367 7332 +3 7367 7330 7332 +3 7333 7369 7332 +3 7334 4422 7370 +3 7371 5643 7336 +3 5585 5584 7337 +3 5584 7372 7337 +3 5583 5585 7373 +3 7373 5585 7339 +3 7374 7339 7375 +3 7374 7373 7339 +3 7340 7375 7339 +3 7342 5587 7376 +3 7377 7378 7302 +3 7302 7378 5588 +3 7379 7380 4977 +3 7380 7344 4977 +3 5589 7381 7382 +3 5589 7383 7381 +3 5589 7384 7385 +3 5589 7385 7383 +3 7346 7385 7384 +3 7346 7384 7345 +3 7345 7384 7386 +3 7345 7386 5596 +3 7347 4963 7387 +3 7348 7388 4373 +3 7464 7389 7390 +3 7390 7351 7464 +3 5590 7353 7354 +3 5590 7354 7391 +3 5580 7353 5590 +3 5581 7392 7359 +3 7360 7392 5581 +3 7362 7393 7363 +3 7393 5593 7394 +3 7393 7362 5593 +3 7362 7365 5593 +3 5593 7365 7366 +3 5593 7366 7395 +3 5593 7396 5607 +3 5593 7395 7396 +3 7396 7397 5607 +3 7367 7398 7368 +3 7369 7398 7367 +3 4422 7399 7400 +3 7400 7370 4422 +3 7371 7401 5643 +3 5584 7402 7372 +3 7372 7402 7403 +3 5600 5583 7402 +3 7403 7402 5583 +3 7373 7404 5583 +3 7404 7403 5583 +3 7374 7404 7373 +3 5602 7405 7406 +3 5602 7406 7376 +3 5602 7376 5587 +3 5588 7407 7408 +3 5594 5588 7408 +3 7407 5588 7378 +3 4977 7409 7379 +3 7410 7411 7412 +3 7410 5589 7411 +3 7411 5589 7413 +3 5589 7414 7413 +3 5589 7415 7414 +3 7416 5589 7417 +3 7417 5589 7382 +3 5596 7384 5589 +3 7386 7384 5596 +3 7387 4963 7418 +3 7388 7419 4373 +3 7389 7464 7420 +3 5590 7421 7422 +3 7421 5590 7391 +3 5593 7423 7394 +3 5607 7424 7447 +3 5607 7397 7424 +3 7424 7425 7447 +3 7425 7426 7428 +3 7426 7427 7428 +3 7427 7429 7428 +3 7399 4422 7430 +3 7431 5643 7401 +3 5602 7433 7405 +3 5594 7408 7434 +3 7409 7435 7436 +3 7409 4977 7435 +3 4977 5611 7435 +3 7410 7437 7438 +3 7412 7437 7410 +3 5589 7439 7415 +3 7416 7439 5589 +3 4963 7461 7440 +3 4963 7440 7418 +3 7441 4373 7419 +3 7464 7442 7443 +3 7464 7443 7420 +3 5590 7422 7486 +3 7422 7444 7486 +3 5593 7445 7423 +3 7446 7445 5593 +3 5608 7447 7425 +3 5608 7425 7428 +3 7448 7428 7449 +3 7448 5608 7428 +3 7429 7449 7428 +3 4422 7450 7430 +3 5643 7431 7451 +3 7452 5643 7451 +3 7453 7432 7433 +3 7454 7432 7453 +3 5602 5609 7453 +3 7433 5602 7453 +3 5594 7455 5610 +3 7455 5594 7434 +3 5611 7456 7457 +3 7457 7435 5611 +3 7410 7458 7459 +3 7438 7458 7410 +3 7461 4963 7460 +3 7462 4373 7441 +3 7462 7463 4373 +3 7465 7442 7464 +3 7466 7486 7444 +3 5593 5615 7446 +3 7446 5615 7467 +3 7447 7493 5617 +3 7447 5608 7468 +3 7447 7468 7493 +3 7468 5608 7469 +3 7469 5608 7448 +3 7450 4422 7470 +3 7452 7471 5643 +3 7472 7454 7453 +3 7473 7454 7472 +3 5609 7474 7453 +3 7453 7474 7472 +3 5609 7475 7474 +3 5610 7475 5609 +3 5610 7476 7475 +3 5610 7455 7476 +3 7477 7478 5611 +3 5611 7478 7456 +3 7410 7459 7480 +3 7410 7480 5623 +3 4963 7507 7481 +3 4963 7481 7460 +3 7463 7482 4373 +3 7464 7483 7465 +3 7464 7484 7483 +3 4415 7485 7464 +3 7464 7485 7484 +3 7487 7486 7466 +3 7487 7488 7486 +3 7487 7489 7488 +3 5615 7490 7467 +3 5617 7491 7492 +3 7493 7491 5617 +3 7494 7493 7468 +3 7469 7494 7468 +3 7495 7494 7469 +3 4422 7496 7497 +3 7497 7470 4422 +3 5643 7471 7498 +3 7499 5643 7498 +3 7472 7500 7473 +3 7474 7500 7472 +3 7501 7500 7474 +3 7475 7501 7474 +3 5619 7502 7477 +3 7502 7503 7477 +3 7504 7478 7477 +3 7504 7477 7505 +3 7505 7477 7503 +3 7479 7504 7505 +3 7479 7505 5623 +3 7480 7479 5623 +3 7507 4963 7506 +3 4963 5677 7506 +3 7508 4373 7482 +3 4415 7509 7485 +3 4415 7510 7509 +3 4415 7486 7522 +3 7511 7522 7488 +3 7522 7486 7488 +3 7489 7511 7488 +3 5615 7512 7490 +3 5616 7512 5615 +3 7492 5616 5617 +3 7512 5616 7492 +3 7496 4422 7513 +3 5643 7499 7514 +3 7515 5643 7514 +3 7503 7502 5619 +3 5623 7505 7503 +3 7506 5677 7516 +3 4373 7508 7517 +3 7517 7518 4373 +3 7519 7510 4415 +3 7520 7510 7519 +3 7521 7519 7522 +3 7519 4415 7522 +3 4422 7523 7513 +3 5088 7523 4422 +3 7524 5643 7515 +3 5677 7525 7516 +3 7526 4373 7518 +3 7527 7520 7519 +3 7528 7519 7521 +3 7528 7529 7519 +3 7530 7540 5624 +3 5088 7531 7532 +3 7532 7523 5088 +3 7524 7533 5643 +3 5677 7534 7535 +3 5677 7535 7525 +3 4373 7526 7536 +3 7536 7537 4373 +3 5629 7527 7519 +3 5629 5041 7527 +3 5041 7538 7527 +3 5629 7519 7529 +3 5629 7529 7539 +3 5634 7540 7530 +3 7542 5631 5630 +3 7542 7543 5631 +3 7544 5631 7543 +3 5088 7545 7546 +3 7546 7531 5088 +3 5643 7533 7547 +3 7548 5643 7547 +3 5677 7549 7550 +3 7534 5677 7550 +3 7551 4373 7537 +3 5041 5632 7538 +3 7538 5632 7552 +3 5633 7553 7567 +3 5629 7553 5633 +3 5629 7539 7553 +3 7553 7554 7567 +3 5037 7540 5634 +3 5043 7555 7556 +3 7541 7542 5630 +3 7541 5630 7556 +3 7557 5631 7544 +3 5088 7558 7559 +3 7559 7545 5088 +3 5643 7548 7560 +3 7561 5643 7560 +3 5677 7562 7549 +3 4373 7551 7563 +3 7563 7564 4373 +3 5632 7565 7552 +3 7566 7565 5639 +3 5633 7567 5639 +3 5043 5638 7555 +3 7555 5638 7568 +3 5641 7569 7570 +3 5641 7557 7569 +3 5641 5631 7557 +3 5088 7571 7572 +3 7572 7558 5088 +3 5643 7561 7573 +3 7574 5643 7573 +3 5677 7575 7576 +3 5677 7576 7562 +3 7577 4373 7564 +3 5639 7578 7566 +3 7567 7579 5639 +3 7579 7578 5639 +3 7580 5638 7581 +3 7580 7568 5638 +3 5641 7582 7583 +3 5641 7570 7582 +3 5088 7584 7585 +3 7585 7571 5088 +3 5643 7574 7586 +3 7587 5643 7586 +3 5677 7588 7589 +3 7575 5677 7589 +3 4373 7577 7590 +3 7591 7592 7593 +3 7593 7594 7591 +3 5638 5645 7581 +3 7581 5645 7595 +3 5641 7583 7596 +3 7596 7597 5641 +3 5088 7598 7599 +3 7599 7584 5088 +3 5643 7587 7600 +3 7601 5643 7600 +3 5677 7602 7588 +3 4373 7590 7603 +3 7604 7592 7591 +3 7605 7591 7594 +3 7605 7606 7591 +3 7606 7604 7591 +3 7605 7607 7606 +3 7595 5645 7609 +3 7597 7610 5641 +3 5088 7611 7612 +3 7610 7611 5088 +3 7612 7598 5088 +3 5643 7601 7613 +3 7614 5643 7613 +3 5677 7615 7602 +3 4373 7603 7616 +3 7618 7604 7617 +3 7606 7619 7604 +3 7619 7617 7604 +3 7607 7620 7606 +3 7620 7619 7606 +3 7621 7607 7608 +3 7621 7620 7607 +3 7623 7624 5645 +3 7624 7609 5645 +3 5643 7614 7625 +3 5643 7625 7626 +3 5677 7627 7615 +3 7616 7628 7629 +3 7630 7631 7632 +3 7630 7633 7634 +3 7632 7633 7630 +3 7633 7635 7634 +3 5083 5652 7636 +3 5646 5083 7636 +3 5646 7636 7637 +3 7638 5646 7637 +3 7617 7639 7618 +3 7639 7640 7641 +3 7639 7617 7640 +3 7617 7619 7640 +3 7620 7640 7619 +3 7621 5650 7620 +3 5650 7622 7642 +3 5650 7621 7622 +3 7642 7643 5650 +3 5651 5092 7644 +3 7644 7645 5651 +3 5651 7645 7646 +3 5645 5651 7647 +3 5651 7646 7647 +3 7648 5645 7647 +3 7623 5645 7648 +3 5643 7626 7649 +3 5643 7649 7650 +3 7651 5643 7650 +3 5677 7652 7627 +3 7616 7629 7653 +3 7616 7653 7654 +3 7630 7655 7631 +3 7655 7656 7657 +3 7655 7630 7656 +3 7630 7634 7656 +3 7658 7634 7635 +3 7658 7659 7634 +3 7659 7656 7634 +3 7658 7660 7659 +3 7636 5652 7661 +3 5646 7638 7662 +3 5653 5646 7663 +3 5646 7662 7663 +3 7666 5653 7664 +3 5653 7663 7664 +3 7666 7664 7665 +3 7668 7641 7640 +3 7640 5655 7668 +3 5650 7669 5105 +3 5650 7643 7669 +3 7669 7670 5105 +3 5092 7671 7644 +3 5643 7651 7672 +3 7673 5643 7672 +3 5664 7674 7675 +3 5664 7676 7677 +3 7675 7676 5664 +3 7678 7679 7680 +3 5665 7680 7681 +3 5677 7682 7652 +3 4509 7683 7684 +3 4509 7654 7683 +3 4509 7616 7654 +3 7656 7685 7657 +3 7685 7686 7687 +3 7685 7656 7686 +3 7656 7659 7686 +3 5658 7659 7688 +3 5658 7686 7659 +3 7660 7688 7659 +3 7689 5658 7688 +3 5652 5658 7712 +3 5652 7712 7690 +3 5652 7690 7661 +3 5666 7666 7667 +3 5666 7667 7691 +3 7691 7692 5666 +3 5660 7693 7694 +3 7668 5660 7694 +3 5655 5660 7668 +3 5105 7670 7695 +3 5105 7695 5662 +3 5662 7696 7697 +3 5662 7695 7696 +3 7671 5118 7698 +3 5643 7673 7699 +3 7700 5643 7699 +3 5664 5663 7674 +3 5663 7701 7674 +3 7677 7702 5664 +3 5664 7702 5112 +3 7703 5112 7702 +3 5671 7704 7678 +3 7680 5671 7678 +3 5665 7681 7705 +3 7705 7706 5665 +3 5677 7707 7682 +3 4509 7684 7708 +3 7686 7709 7687 +3 5658 7689 7710 +3 5658 7710 7711 +3 7712 5658 7711 +3 5660 5666 7713 +3 5666 7692 7713 +3 7713 7693 5660 +3 5662 7697 7714 +3 7698 5118 7715 +3 5643 7700 7716 +3 5643 7716 7717 +3 7718 5643 7717 +3 5663 7719 7701 +3 5112 7719 5663 +3 7720 7719 5112 +3 5112 7703 7721 +3 5673 5112 7722 +3 5112 7721 7722 +3 5114 7724 7726 +3 7724 7725 7726 +3 7727 5667 7726 +3 5667 5114 7726 +3 7727 7728 5667 +3 7728 5669 5667 +3 7704 5671 5669 +3 7728 7704 5669 +3 7706 7729 5665 +3 5677 7730 7707 +3 4509 7708 7731 +3 4509 7731 7777 +3 7686 7761 7709 +3 7709 7761 7732 +3 5662 7714 7736 +3 7736 7737 5662 +3 7738 7739 5118 +3 7715 5118 7740 +3 7740 5118 7739 +3 5643 7718 7741 +3 5643 7741 7742 +3 7743 5643 7742 +3 5112 7744 7720 +3 5112 7746 7745 +3 5112 7745 7744 +3 5673 7723 7747 +3 5673 7722 7723 +3 7747 7748 5673 +3 7750 7751 5683 +3 7751 7752 5683 +3 7752 7753 5683 +3 7724 5683 7754 +3 5683 7753 7754 +3 5665 7755 7756 +3 7755 5665 7729 +3 5677 7757 7758 +3 7730 5677 7758 +3 7777 7731 7759 +3 7777 7759 7760 +3 7777 7778 7761 +3 5679 5691 7779 +3 5126 7762 7763 +3 5126 7764 7762 +3 7734 5126 7733 +3 7734 7764 5126 +3 7733 5126 5680 +3 7733 5680 7735 +3 7735 5680 7765 +3 5662 7737 7766 +3 5662 7766 5693 +3 7767 5693 7766 +3 5118 7768 7769 +3 7738 5118 7769 +3 5643 7743 7770 +3 5698 5643 7770 +3 7771 5698 7770 +3 5112 5135 7746 +3 5135 5694 7772 +3 5135 7772 7746 +3 5683 5673 7748 +3 7749 5683 7748 +3 7750 5683 7749 +3 5665 7756 5703 +3 7756 7773 5703 +3 5677 7774 7775 +3 5677 7775 7776 +3 7776 7757 5677 +3 7779 7780 5679 +3 5679 7780 5690 +3 5691 7781 7779 +3 5692 7783 7782 +3 5692 7782 5691 +3 5691 7782 7781 +3 7785 7784 5126 +3 7785 5126 7763 +3 7765 5680 7786 +3 5693 7767 7787 +3 5693 7787 7788 +3 5118 5693 7789 +3 7789 5693 7788 +3 7768 5118 7789 +3 5698 7771 7790 +3 5698 7790 7791 +3 7792 5698 7791 +3 7772 5694 5698 +3 5698 7793 7772 +3 5696 7794 7795 +3 5687 7796 5696 +3 5696 7796 7794 +3 7797 7796 5688 +3 5688 7796 5687 +3 5688 5703 7797 +3 7798 5703 7773 +3 5677 7800 7801 +3 7801 7774 5677 +3 7802 5690 7780 +3 7803 7783 5692 +3 7803 7804 7783 +3 7784 7806 5697 +3 7806 7805 5697 +3 7786 5680 7807 +3 5698 7792 7808 +3 7809 5698 7808 +3 7810 7793 5698 +3 7810 5698 7811 +3 7811 7812 7813 +3 7810 7811 7813 +3 7813 7812 7814 +3 5696 7795 5702 +3 7795 7815 5702 +3 5703 7816 7797 +3 5703 7817 7816 +3 7799 5703 7798 +3 7799 7817 5703 +3 7818 7819 5677 +3 7819 7800 5677 +3 5690 7802 7820 +3 7803 5690 7820 +3 7821 7803 7820 +3 7804 7803 7821 +3 7822 7804 7821 +3 7805 7823 5697 +3 7824 7825 5680 +3 7825 7807 5680 +3 5706 5707 7826 +3 5698 7809 7827 +3 5698 7827 7828 +3 7829 5698 7828 +3 7829 7830 5698 +3 7830 7831 5698 +3 7832 5698 7831 +3 7811 5698 7832 +3 7833 7811 7832 +3 7812 7811 7833 +3 7834 7812 7833 +3 7814 7812 7834 +3 5702 7815 7835 +3 5711 5702 7835 +3 7836 5711 7835 +3 5677 7837 7838 +3 7818 5677 7838 +3 7839 4521 7823 +3 4521 5697 7823 +3 5713 7825 7824 +3 5713 7840 7825 +3 7826 5714 5706 +3 5711 7836 7841 +3 5716 7841 7842 +3 5716 5711 7841 +3 5718 7843 5677 +3 7843 7844 5677 +3 7837 5677 7844 +3 4521 7839 7845 +3 4521 7845 7846 +3 4521 7846 7847 +3 4521 7847 7840 +3 5716 7842 7848 +3 5716 7848 7849 +3 7850 7851 5718 +3 7852 5718 7851 +3 7843 5718 7852 +3 5716 7849 7853 +3 5716 7853 5718 +3 5718 7853 7854 +3 7855 5718 7854 +3 5718 7855 7856 +3 5718 7856 7857 +3 5718 7857 7858 +3 5718 7858 7859 +3 7850 5718 7859 + +CELL_DATA 13733 +POINT_DATA 7860 +NORMALS normals float +-0.133614 -0.057263 -0.989378 -0.0307275 -0.537162 -0.842919 -0.864861 -0.161839 -0.47521 +-0.0591288 -0.790848 -0.60915 -0.72247 0.175378 -0.66879 0.236808 -0.629513 -0.740024 +0.197983 -0.368125 -0.908453 0.355323 -0.801092 -0.48166 0.285198 -0.791312 -0.540821 +-0.618792 -0.400138 -0.676007 -0.398669 -0.360336 -0.843339 0 -0.0860472 -0.996291 +0.65077 -0.224068 -0.72546 -0.616992 0.576954 -0.535205 0.303636 -0.510558 -0.804448 +0.0991771 0.682572 -0.724058 0.372129 0.614105 -0.695985 -0.490425 0.764772 -0.41786 +0.0313472 0.583987 -0.811158 0.399766 -0.323516 -0.857627 0.216481 0.546615 -0.808918 +0.390625 0.179303 -0.902919 -0.088772 0.773204 -0.627914 -0.161114 0.301929 -0.939618 +0.336389 -0.421911 -0.841922 -0.529158 -0.497371 -0.687469 -0.448965 -0.270927 -0.851486 +0.456537 0.204655 -0.865847 -0.370621 0.853657 -0.365937 -0.188935 0.739997 -0.645529 +0.0329652 -0.212296 -0.976649 -0.149265 -0.468952 -0.870519 0.135512 0.597534 -0.79031 +-0.431091 0.395359 -0.811081 -0.313014 -0.35348 -0.881518 0.208532 0.181332 -0.961058 +0.440965 0.440965 -0.781728 0.381019 0.634407 -0.672571 -0.260464 0.83826 -0.479039 +0 0.637398 -0.770535 0.253912 -0.463967 -0.848683 -0.745225 -0.1198 -0.655963 +-0.819732 -0.123691 -0.559232 0.351087 -0.152592 -0.923826 0.299891 -0.766145 -0.568407 +-0.0578228 -0.708329 -0.70351 -0.298115 -0.407947 -0.862964 0.626218 0.27526 -0.72944 +-0.428964 0 -0.903322 -0.840707 0.309226 -0.444512 0.612134 -0.612134 -0.500582 +0.778143 0.124987 -0.615526 0.298546 0.138859 -0.94424 -0.483754 0.68549 -0.544138 +0.150583 0.282344 -0.947421 0.318835 -0.670203 -0.670203 -0.447214 0 -0.894427 +-0.557993 -0.557993 -0.614238 -0.350425 -0.313294 -0.882638 0.678742 0.132751 -0.722279 +0.184754 -0.606401 -0.773398 -0.555685 -0.412346 -0.721932 -0.54253 -0.19323 -0.817511 +-0.454534 -0.307546 -0.835951 0.309976 0.302415 -0.901366 0.791268 0.485184 -0.372145 +-0.601158 0 -0.79913 -0.195297 0.328291 -0.924167 0.561633 0.64151 -0.522527 +-0.430992 0.549168 -0.716003 0.450158 -0.054019 -0.891313 -0.0141727 -0.37479 -0.927001 +-0.483179 0.180464 -0.856721 -0.327193 0.433721 -0.839542 0.334286 0.754839 -0.564332 +0.671316 0.671909 -0.312846 0.344796 0.158164 -0.925257 0.22389 0.743178 -0.630524 +-0.553661 0.562312 -0.614218 -0.166154 -0.166154 -0.972001 -0.0172758 -0.302326 -0.953048 +0.591926 0.484469 -0.644137 -0.687459 0.261533 -0.677496 -0.505059 0.325209 -0.799471 +0.166733 0.45256 -0.876008 0.588363 0.588363 -0.55467 -0.406719 0.382316 -0.829707 +0.473568 -0.163299 -0.865486 0.720113 -0.256824 -0.644577 0.236649 -0.191878 -0.95246 +0.413754 0.227159 -0.881593 -0.417457 -0.790013 -0.449009 0.124878 -0.405854 -0.905366 +-0.39225 -0.851721 -0.347435 -0.239402 -0.729666 -0.640527 -0.0931752 -0.0931752 -0.99128 +0.123225 0.432494 -0.893177 -0.249097 -0.886882 -0.389089 0.217978 -0.743821 -0.631836 +0.732431 0.259049 -0.629634 -0.737644 -0.569222 -0.363137 -0.669417 -0.68991 -0.275509 +0.821447 -0.111914 -0.559196 -0.924616 0.233679 -0.300798 -0.731344 -0.200835 -0.651768 +-0.487093 0.295284 -0.821917 0.119911 -0.875437 -0.468222 -0.0338245 -0.717313 -0.69593 +-0.0261782 0.279234 -0.959866 -0.0250979 0.633721 -0.773154 0.119187 0.815737 -0.566011 +0.879941 0.148971 -0.451122 0.201057 -0.61231 -0.764626 -0.220722 -0.715565 -0.662758 +-0.919569 -0.0491032 -0.389849 -0.835811 -0.311849 -0.451853 0.523791 0.363162 -0.770555 +0.492487 -0.363645 -0.790708 -0.236316 -0.379469 -0.894516 0.285703 -0.82894 -0.480866 +-0.603185 -0.603185 -0.521857 0.582435 0.465226 -0.666584 -0.634173 0.76534 -0.109907 +-0.584343 -0.235207 -0.776673 -0.0678275 -0.131887 -0.988941 -0.269185 -0.72255 -0.636757 +-0.634909 -0.317455 -0.704353 0.0535556 -0.444923 -0.893966 0.512611 -0.367788 -0.775862 +0.341278 -0.713496 -0.611925 -0.487268 -0.603938 -0.630736 -0.378509 -0.485634 -0.787966 +0.0320423 0.339926 -0.939906 -0.550019 0.821907 -0.148148 0.0585813 -0.422704 -0.904372 +0.382161 0.543608 -0.747291 0.500733 0.646371 -0.575734 0.0850264 0.786494 -0.611717 +-0.159823 -0.820268 -0.549196 0.365676 -0.424613 -0.828242 0.0242708 -0.651266 -0.758462 +-0.507728 0.237573 -0.828113 0.60161 -0.6034 -0.523425 -0.809091 0.35261 -0.470147 +0.216092 -0.520584 -0.826012 -0.357151 -0.0957722 -0.929124 -0.846672 0.235721 -0.477055 +0.660332 -0.623161 -0.419085 -0.68403 -0.523612 -0.507872 -0.923242 0.314121 -0.22125 +-0.0158154 -0.661987 -0.749348 0.647451 -0.716615 -0.259365 -0.887292 0.0362983 -0.459778 +-0.880705 0.414627 -0.229004 -0.880691 0.463666 -0.09694 0.854087 -0.349399 -0.385299 +0.591791 -0.197264 -0.781582 -0.69874 0.685205 -0.205562 -0.644528 0.34271 -0.683472 +0.519893 -0.777214 -0.354472 -0.588419 -0.588419 -0.55455 -0.814164 0.524526 -0.249018 +0.666706 -0.72418 -0.176256 -0.767549 0.610615 -0.194978 0.596127 -0.675259 -0.434347 +-0.568865 0.667022 -0.481118 0.468899 -0.549215 -0.691735 -0.673848 -0.595175 -0.43783 +-0.37961 -0.541691 -0.749978 0.649603 -0.356158 -0.67169 0.467638 -0.171231 -0.867176 +0.197129 0.824751 -0.530025 -0.315227 0.736623 -0.598347 -0.189691 -0.331959 -0.924024 +-0.432596 -0.884943 -0.172444 -0.653129 0.0762328 -0.753399 -0.196901 -0.798542 -0.568824 +0.595899 0.595899 -0.538339 -0.0833766 -0.90672 -0.413409 0.244745 -0.840249 -0.483819 +-0.0411872 0.720775 -0.691944 -0.346388 -0.148984 -0.926185 0.262214 -0.510627 -0.818843 +-0.32034 -0.0488322 -0.946043 0.0269138 -0.558461 -0.829094 -0.231713 -0.311614 -0.921524 +0.415067 -0.105914 -0.903605 0.113699 0.437303 -0.892098 0.687616 -0.181196 -0.703102 +-0.730426 -0.191407 -0.655623 -0.0673477 -0.43776 -0.896566 0.587931 -0.355209 -0.726749 +0.407993 0.710211 -0.573709 0.0627344 -0.643028 -0.763269 -0.716044 -0.492216 -0.494979 +-0.625571 -0.657112 -0.420552 -0.125133 -0.871118 -0.474864 -0.513312 0.500792 -0.696935 +0.205739 -0.154691 -0.966303 0.139194 -0.63921 -0.756331 -0.183705 -0.7983 -0.573558 +0.129194 -0.79939 -0.586758 0.395605 -0.699374 -0.595292 0 -0.714057 -0.700088 +0.0821513 -0.670499 -0.737348 0 -0.947723 -0.319096 0 -0.424893 -0.905244 +0.232035 0.261205 -0.93698 -0.26748 0.823524 -0.500263 -0.505771 0.579733 -0.638831 +-0.302437 0.55059 -0.778064 -0.0322538 0.628948 -0.776778 0.216548 0.804992 -0.552353 +-0.0276135 0.796846 -0.603551 0.0606424 0.818673 -0.571049 0.42076 0.688839 -0.590307 +0.442564 0.636671 -0.631495 0.0703097 0.953021 -0.294631 -0.393632 0.883207 -0.254949 +0 0.842439 -0.538792 0.247275 0.851023 -0.463265 -0.53666 -0.712685 -0.451749 +0.330773 -0.301587 -0.894223 -0.311853 -0.648992 -0.693943 -0.0186271 -0.614693 -0.788546 +0.778019 0.198507 -0.596055 -0.721324 -0.65162 -0.234698 -0.244691 -0.700758 -0.670123 +0.722534 0.148377 -0.675225 -0.94044 -0.111906 -0.321012 -0.414019 -0.785113 -0.460636 +0.578245 -0.302339 -0.757776 0.804844 0.371467 -0.462859 -0.215747 -0.449986 -0.866583 +0.873669 0 -0.486522 -0.796789 -0.150159 -0.585304 0.577632 -0.654494 -0.48783 +-0.919215 0.189644 -0.345078 -0.815376 0.574037 -0.0751172 0.94216 -0.314825 -0.114973 +-0.526794 0.237428 -0.816159 0.367513 -0.929476 0.0317603 -0.438774 0.371842 -0.818053 +0.647808 -0.0398651 -0.76076 0.204368 -0.978894 -0.000987286 0.26964 -0.955995 -0.115618 +0.631516 -0.66722 -0.394976 -0.501994 0.643066 -0.578332 0.464369 0.337251 -0.818916 +0.953793 -0.124408 -0.2735 -0.151531 0.567922 -0.809013 0.764898 -0.642514 0.0458939 +0.2831 -0.886154 -0.366858 0.607346 -0.764806 0.214946 0.270611 -0.704715 -0.655855 +0.401862 -0.438789 -0.803723 -0.434244 -0.672652 -0.599143 0.726992 -0.275329 -0.629028 +0.592987 -0.393762 -0.702366 -0.474079 -0.574123 -0.667557 -0.503787 -0.440437 -0.743111 +-0.457974 -0.673915 -0.57974 -0.588721 0.792639 -0.158528 0.0695623 -0.890398 -0.449836 +0.457902 -0.737537 -0.496352 0.970729 0.0507571 -0.234752 0.707754 -0.540756 -0.454606 +-0.697492 -0.656992 -0.286122 -0.725648 -0.563773 -0.394455 0.985733 -0.147733 -0.0806586 +-0.550024 -0.829022 -0.100971 0.512143 -0.857839 -0.0426786 0.322015 -0.946725 -0.00429354 +-0.522973 -0.852252 0.0129129 -0.649531 -0.759452 0.0366402 0.14246 -0.939007 -0.313002 +-0.280881 -0.853879 -0.438175 -0.0711379 -0.995931 0.0553295 -0.548838 0.804265 -0.227891 +-0.74702 0 -0.664801 0.0171827 -0.326472 -0.945051 0.410419 -0.679111 -0.608575 +-0.364289 0.167455 -0.916107 0.246603 -0.332377 -0.910336 0.451961 -0.335743 -0.826443 +0.144032 -0.882629 -0.44746 -0.38057 -0.225126 -0.896931 -0.575825 0.465475 -0.67213 +0.112 0.0263529 -0.993359 0.281641 -0.345346 -0.895217 -0.652357 -0.588712 -0.477334 +-0.0965727 -0.915831 -0.389778 0.660328 -0.660328 -0.357678 -0.279956 0.36428 -0.888214 +-0.100151 -0.0166918 -0.994832 -0.887081 -0.100325 -0.450581 0.76069 0.544434 -0.353471 +-0.402301 0.504888 -0.763702 -0.349163 0.311494 -0.883774 0.441792 0.834495 -0.329298 +-0.559507 -0.808386 -0.182932 -0.138395 -0.827316 -0.544421 -0.631133 -0.746974 -0.209046 +-0.173925 -0.780433 -0.600562 0.767655 -0.0383828 -0.639713 -0.453607 -0.685278 -0.569767 +0.551526 -0.14722 -0.821063 0.826474 0.363649 -0.429767 0.8577 0.388891 -0.336325 +0.823711 0.279248 -0.493478 0.333318 -0.754251 -0.565689 0.0406417 -0.746792 -0.663815 +0.015947 -0.534225 -0.845192 -0.520495 0.0208198 -0.853611 0.661244 -0.495308 -0.563406 +0.913519 0.321215 -0.249607 -0.747552 0.00807074 -0.664154 0.461353 -0.53233 -0.709773 +0.292762 -0.56131 -0.774094 0.190724 -0.486347 -0.852696 -0.888497 0.430483 -0.158928 +0.994314 -0.0509905 0.0934825 0.986835 -0.131761 0.0937882 0.90288 -0.379832 0.201332 +0.619007 -0.75584 0.213394 0.869212 -0.160118 -0.467796 0.824087 -0.222516 -0.52093 +0.60237 0.798086 -0.0144975 -0.0559393 -0.964954 -0.256389 -0.379812 0.634713 -0.672965 +0.648721 -0.131186 -0.749634 0.977478 -0.153099 0.145248 -0.681476 -0.526052 -0.508782 +0.253505 -0.869394 -0.424134 -0.295363 0.210974 -0.9318 0.771082 -0.571005 0.281756 +-0.520388 -0.81452 -0.256423 -0.932492 -0.332476 0.141129 -0.387589 0.66745 -0.635834 +0.72924 0.447094 -0.517992 0.490614 0.598931 -0.632913 0.554927 0.541393 -0.631625 +-0.0398377 -0.632976 0.773146 0.833643 0.0106877 0.5522 0.277725 -0.55545 -0.783801 +-0.428552 -0.68747 0.586284 0.232916 -0.842788 -0.485241 -0.207499 -0.735837 -0.644584 +-0.128406 -0.342416 -0.930733 -0.160642 0.973546 -0.162488 -0.986898 0.0986898 0.127639 +0.451855 -0.636122 -0.62544 0.64379 0.539243 -0.542911 0.593303 0.25522 -0.763449 +0.234245 -0.669273 -0.705126 0.35794 0.188615 -0.914496 -0.643042 0.270755 -0.716372 +0.521396 -0.109909 -0.846207 0.91988 -0.041365 -0.390013 -0.946342 0.288601 -0.145419 +0.72821 0.682613 -0.0612429 -0.346952 -0.919119 -0.186664 0.0326775 -0.980326 -0.19466 +0.297089 -0.871314 -0.390576 0 -0.849343 -0.527841 0.0658001 -0.870586 -0.487596 +0.74046 0.64113 -0.20167 -0.540906 0.801701 -0.254355 0.833497 -0.00766434 -0.552471 +0.72087 -0.493227 -0.486903 -0.912475 -0.382435 0.14537 0.254155 -0.914959 -0.313458 +-0.66355 -0.737278 0.126976 -0.327662 0.703275 -0.630905 -0.471757 -0.36856 -0.801005 +0.245787 -0.825896 -0.50743 -0.79949 0.588196 -0.121827 -0.554541 -0.831811 0.0239616 +-0.0360758 0.742401 -0.668984 -0.522275 0.838219 0.156897 0.317629 -0.923394 0.215534 +0.290532 0.576195 -0.763931 -0.77193 0.604489 -0.196766 -0.130676 -0.659232 -0.740498 +-0.459006 -0.442006 -0.770677 -0.374676 -0.769072 -0.517827 -0.376284 0.430038 -0.820657 +0.222776 -0.879965 -0.419562 -0.48734 0.46584 -0.738575 -0.249152 -0.738395 -0.626654 +-0.706638 0.0936891 -0.701345 -0.382983 0.718093 -0.581091 0.0830439 -0.110725 -0.990376 +0.260005 -0.913991 -0.311476 0 0.999101 0.0423861 0.444728 -0.690636 -0.570298 +0.0428856 0.950631 -0.307347 0.815639 0.577103 0.0410385 0.942857 0.171429 0.285714 +-0.728315 0.662105 0.176561 -0.722965 -0.690103 -0.032862 -0.910416 0.0343553 -0.412264 +-0.128238 0.881635 -0.454175 -0.186605 0.92473 -0.331742 -0.486271 0.75559 -0.43889 +-0.746592 0.409132 -0.524605 -0.67561 -0.275249 -0.683951 0.959044 0.280696 0.0380109 +-0.551393 0.600772 -0.578826 0.682383 0.720548 -0.123144 -0.129602 -0.931015 -0.341196 +0.899206 0.0372805 -0.435934 -0.865383 -0.397797 -0.304745 0.355155 -0.856956 -0.373486 +-0.121418 -0.689481 -0.714054 0.723358 -0.632815 -0.276221 -0.226561 -0.696637 -0.680711 +0.511263 -0.723072 -0.464519 -0.911951 -0.130058 -0.38914 0.937919 -0.290078 -0.190162 +0.760164 0.550687 -0.344809 0.766852 0.546222 -0.337015 0.934208 -0.3242 -0.148829 +-0.982977 0.174299 -0.0580998 0.177826 -0.98367 -0.0277619 -0.728453 -0.67903 0.0909671 +0.778298 -0.598196 -0.190822 0.504274 0.784427 -0.361085 0.313858 -0.859187 -0.404092 +0.818402 -0.572881 -0.0450121 0.902922 0.409137 -0.131676 -0.0824148 0.970663 -0.225878 +0.26844 -0.96313 -0.017896 0.954296 0.149554 -0.258752 0.698362 -0.101079 -0.708572 +-0.990127 -0.0735905 0.119306 -0.519702 0.814759 0.257057 -0.661742 0.672771 0.330871 +-0.106395 -0.984152 0.14186 0.586209 0.768585 -0.256195 -0.0510752 -0.752508 -0.6566 +0.534006 -0.738057 -0.412444 -0.746554 0.590949 -0.305673 0.951414 0.0126434 0.307655 +-0.405861 -0.900274 0.157425 0.402726 -0.836431 0.371747 -0.48941 -0.870062 0.0589104 +0.786062 -0.550243 -0.281672 0.894147 0.108601 0.434404 0.931719 -0.0291162 0.362012 +-0.46618 -0.884546 -0.0159378 -0.294639 -0.837308 -0.460547 -0.0271759 0.570694 -0.820713 +-0.646277 0.656016 -0.389832 0.838007 0.164315 0.520331 -0.451493 -0.799787 0.395594 +-0.694515 -0.0292427 -0.718884 0.0571292 0.956915 -0.284694 0.463583 -0.86278 0.201745 +-0.527545 -0.83658 0.147754 -0.53086 -0.812675 0.240307 0.703842 0.703842 0.0959785 +0.14104 0.967135 -0.211561 -0.0949207 -0.976327 -0.194361 0.902432 -0.258894 -0.344371 +-0.166339 -0.970311 0.17558 -0.960289 0.0561572 -0.273299 0.431889 -0.431889 -0.791797 +0.629445 0.769322 0.109279 -0.972387 -0.233373 0 -0.707308 0.579529 -0.404799 +0.575168 -0.692435 -0.435564 -0.722346 0.685303 -0.0926085 0.403399 0.576284 0.71075 +-0.838388 0.540438 0.0709405 0.618425 -0.531767 -0.578597 0.666068 -0.609667 -0.429721 +-0.591621 0.591621 -0.547694 -0.571101 0.598297 -0.562036 -0.522678 0.709348 -0.472899 +0.248187 -0.849265 0.465997 0.749401 0.343811 -0.565856 0.327269 0.139263 -0.934613 +0.470209 -0.798609 -0.37567 -0.448116 0.426572 0.785639 -0.355483 -0.891999 0.27923 +-0.816525 -0.199965 -0.541573 0.626849 -0.690813 -0.360331 0.932835 0.339604 0.120366 +0.750775 0.459982 -0.474081 -0.84453 0.341834 -0.412211 0.573251 -0.473762 -0.66853 +0.278974 -0.852913 -0.441264 0.0873541 -0.762893 -0.640597 -0.983169 0.172055 0.0614481 +-0.602585 -0.71815 0.348069 -0.297116 0.847331 -0.440172 0.303829 0.717405 -0.626911 +0.653544 -0.308205 -0.691296 -0.201612 -0.632644 -0.74774 -0.279857 0.612659 -0.739141 +-0.068935 -0.0771416 -0.994634 0.33931 -0.819482 -0.461863 -0.883753 -0.45613 0.10453 +-0.254414 0.166439 0.952666 -0.249208 0.320836 -0.913761 -0.207199 0.148716 -0.966929 +0.304056 -0.33407 -0.892159 -0.170821 0.134216 -0.976118 0 -0.311641 -0.9502 +0.694672 -0.709144 0.120603 0.859581 -0.509036 -0.0447504 -0.781435 0.407462 0.472582 +-0.647169 -0.392224 0.653706 -0.886995 0.0490504 0.459166 -0.497468 0.297268 -0.814958 +0.0882852 0.0294284 -0.99566 -0.269897 -0.304247 -0.913559 -0.719314 0.401156 -0.567152 +-0.133211 -0.793344 -0.594021 0.102573 -0.981767 -0.160036 -0.479456 0.76713 -0.426184 +0.53753 0.808457 0.239703 -0.402972 0.914818 0.0268648 0.917997 -0.396562 0.00452816 +0.980883 0.0779716 0.178295 0.883936 0.441968 0.152713 -0.716192 -0.695922 -0.0525508 +-0.0555452 0.864653 -0.499289 -0.243071 0.729213 -0.63966 0.952863 -0.276809 0.124209 +-0.211241 -0.977383 -0.00998402 -0.540551 0.390592 -0.745146 -0.458135 0.823741 -0.33401 +0.369622 -0.870401 -0.325241 -0.146564 -0.840301 -0.521931 0.72583 -0.672963 -0.142449 +-0.29592 -0.883917 -0.362107 0.647963 -0.416547 -0.637678 -0.149366 -0.852922 -0.500214 +-0.746148 -0.662663 -0.0643531 0.855179 -0.117505 -0.504838 0.794308 0.21938 -0.566522 +0.239602 0.339436 -0.909601 -0.318953 0.134296 -0.938208 0.472654 -0.759727 -0.446557 +0.496001 -0.807052 -0.320392 0.576018 -0.62402 -0.528017 -0.116539 0.773139 -0.623438 +0.71045 0.333298 -0.619817 -0.105028 -0.992996 0.0541056 0.693252 -0.683712 -0.227904 +-0.66256 -0.603026 0.444267 -0.0633257 -0.909588 0.410658 0.278523 -0.960424 -0.00320141 +0.329474 -0.929344 0.166631 0.23583 -0.946032 0.222277 0.17609 0.440225 0.880451 +-0.741536 -0.670913 0 -0.757684 -0.651961 0.0293676 0.915488 -0.0938963 -0.391234 +0.0657912 -0.997833 0 -0.0423798 0.720457 -0.692204 0.548646 0.759664 -0.349139 +-0.393073 -0.917171 -0.0655122 0.721223 0.523468 -0.453672 0.517381 0.776071 -0.360599 +-0.928473 -0.295423 -0.225084 0.710282 -0.701925 -0.0529229 0.698377 0.704904 -0.124011 +-0.95902 -0.139857 -0.246415 0.898349 -0.431207 -0.0838459 -0.724146 0.599293 0.341264 +0.740475 -0.598136 -0.306481 -0.762629 -0.635902 -0.11843 0.104777 -0.681047 -0.724704 +0.638241 -0.769309 0.0284929 0.994249 -0.0887288 0.0599629 0.17609 -0.968496 0.17609 +0.513226 -0.856379 -0.0566906 0.296071 0.911897 0.284228 -0.735612 0.559065 0.382518 +-0.730931 0.608526 0.308926 0.89569 -0.432749 0.102317 0.124686 -0.992063 -0.0162633 +-0.417759 -0.908461 -0.0132622 0.323677 -0.921233 -0.215784 -0.965722 -0.259028 0.0168931 +-0.982888 -0.182016 -0.0283136 0.299775 0.930552 -0.210259 0.301131 0 0.953583 +0.976112 0.208611 -0.0607151 -0.890387 0.0460545 0.452869 0.813767 0.48358 0.322387 +0.839341 0.453698 0.299441 0.0112225 0.864131 0.503141 -0.860695 0.43913 0.257623 +0.01133 -0.940393 -0.339901 0.361886 -0.882096 -0.301571 -0.283706 -0.831774 -0.477141 +0.753653 -0.0519761 -0.655214 0.558975 -0.264243 -0.785953 0.586881 -0.8031 -0.102962 +-0.895676 0.422393 0.139101 0.200534 -0.968295 0.148968 0.755142 -0.655406 0.014248 +-0.409691 -0.872821 0.265211 -0.722956 0.690464 -0.0243693 -0.790186 0.603615 0.10609 +-0.369077 0.918116 0.144377 -0.19593 0.979651 -0.04354 -0.218537 0.967805 0.124878 +-0.13994 0.965583 0.219239 -0.102463 0.973399 0.204926 -0.297193 0.934035 0.198129 +-0.522716 -0.678668 -0.515924 0.578153 -0.140245 -0.803785 -0.691431 0.718024 0.0797805 +0.435884 -0.89158 -0.12284 -0.606334 -0.785479 -0.124023 0.730789 0.501956 0.462587 +0.829482 0.47399 0.295454 0.692996 0.679921 0.239716 0 0.462566 0.886585 +0.186443 0.865288 0.465312 0.908919 -0.398649 -0.122252 -0.651332 0.195812 0.733092 +0.0958325 0.972015 0.214482 0.576707 0.527885 0.623495 -0.783524 0.547223 0.29434 +-0.713561 0.5622 0.418046 -0.813596 0.355572 0.460033 -0.637132 -0.58963 -0.496385 +0.252222 0.252222 -0.934221 0.71509 0.659113 -0.232844 0.250099 -0.701892 -0.666932 +0.830773 0.288001 0.47631 0.196129 0.442546 0.875035 -0.926488 0.188352 0.325798 +0.654477 -0.711388 -0.2561 -0.433262 -0.753876 0.493919 0.660852 -0.189307 0.726249 +-0.48085 -0.824314 0.298814 0.983296 -0.150169 -0.102855 0.520532 -0.753605 -0.401405 +-0.491369 -0.728328 -0.477592 0.358962 0.243277 -0.90109 0.689078 0.689078 -0.224374 +-0.717496 0.573997 -0.394623 0.264013 0.940669 -0.213166 -0.529192 0.797988 0.288395 +-0.261115 -0.821228 0.507349 -0.680331 -0.684186 0.262753 -0.218519 -0.963269 -0.156085 +0.652871 -0.748414 0.116774 0.203874 -0.974064 -0.0981614 0.205266 -0.173687 0.963171 +-0.171388 0.0507816 0.983894 -0.350738 -0.85736 0.376719 -0.855798 -0.474442 0.206192 +-0.475789 -0.836182 0.272809 -0.135139 0.844618 0.518032 0.84978 0.186952 0.492872 +-0.486418 0.856759 0.171352 0.307937 -0.865918 -0.394159 -0.438607 -0.758597 -0.481824 +0.659006 0.110713 -0.743945 0.69225 0.686197 -0.223436 0.231633 -0.146029 0.96178 +0.539235 0.550958 0.636923 -0.383801 -0.733736 0.56065 0.893078 -0.435089 -0.114497 +0.506432 -0.350607 0.787783 -0.604419 0.36457 0.708354 -0.873243 -0.169356 0.456909 +0.688789 -0.439653 0.576433 -0.885817 0.117133 0.449009 0 -0.999061 0.0433356 +0.871587 0.0901642 -0.481878 -0.511752 -0.0261691 -0.858735 -0.53265 -0.764754 -0.362539 +-0.489831 -0.473503 -0.732025 0.450223 0.452841 -0.769568 0.769039 0.607173 -0.199799 +-0.378939 0.801186 -0.463148 -0.718397 -0.689075 0.0952976 0.184114 0.97763 0.101694 +-0.382269 0.888214 0.254846 0.867466 -0.485462 -0.108765 -0.560007 -0.222072 0.798171 +-0.913832 -0.290254 0.284012 0.560901 -0.780385 0.276386 -0.368434 -0.846421 -0.384483 +-0.38097 -0.881165 -0.280018 -0.210366 -0.839178 -0.501525 0.0851775 -0.00967926 -0.996319 +0.52573 0.738864 -0.421531 0.541798 0.823036 -0.170489 -0.601124 0.743495 -0.293029 +0.0634667 0.335467 -0.939912 -0.164716 -0.973322 -0.159725 0.874235 0.298474 0.382919 +0.975442 0.177995 0.129729 -0.932739 -0.328536 0.148533 -0.310257 -0.810372 -0.497028 +0.620221 0.334651 -0.709461 0.587911 0.732468 -0.343294 -0.74274 0.275875 -0.610107 +0.239939 0.945067 0.221985 -0.192387 0.902738 0.384774 0.908868 -0.0873912 -0.407825 +-0.9459 0.169696 0.276542 -0.940466 0.175983 0.290781 0.36958 -0.90952 -0.19022 +-0.460918 -0.800411 -0.383272 0.572474 0.677593 -0.461672 0.223733 -0.968343 0.110701 +-0.216985 0.278012 0.935749 0.791297 0.238404 0.563038 0.79788 -0.555363 -0.234433 +0.0708869 -0.992416 0.100423 -0.555622 -0.807224 0.199185 -0.868642 -0.414275 0.271729 +0.142525 -0.891428 -0.430166 -0.668659 0.260825 -0.696323 -0.486004 0.602064 -0.633497 +0.123554 0.809965 -0.573316 0.22312 -0.80881 0.5441 0.509371 -0.709386 0.487147 +-0.85552 -0.392807 0.337326 0.128324 -0.991598 0.0163322 0.04847 0.981518 0.185129 +-0.160852 0.97798 0.132971 -0.783356 -0.0257743 -0.621039 0.396049 -0.203525 -0.895389 +0.800749 -0.0250234 -0.598477 0.197399 0.540055 -0.818153 -0.0079445 0.897728 -0.440478 +0.373377 0.886251 -0.274134 0.141833 -0.921916 0.360493 -0.929152 0.349305 -0.121092 +-0.978305 0.142402 -0.150472 0.148585 0.728276 -0.668982 0.644562 0.528742 -0.552242 +0.41021 0.876547 -0.251779 0.329187 -0.938472 -0.104435 -0.419297 -0.898714 -0.128461 +-0.403814 0.779454 -0.478942 -0.155245 0.851859 -0.500235 -0.654974 0.664606 -0.359594 +0.623948 -0.263531 -0.73569 -0.91542 -0.191728 -0.353903 0.556984 -0.718688 -0.41624 +-0.299696 -0.775301 -0.555958 0.0263015 -0.836387 -0.547509 0.329968 -0.532663 -0.779353 +-0.871129 -0.454099 -0.186891 0.610586 0.732157 -0.301878 0.682646 0.626428 -0.376273 +0.656219 0.686047 -0.31419 -0.702782 -0.649801 -0.289579 0.909282 -0.357218 -0.213544 +-0.956792 0.278167 -0.0846846 0.741177 -0.626202 -0.241926 0.680638 -0.686146 -0.25678 +0.558265 -0.197035 -0.805927 -0.586108 -0.0572744 -0.808206 0.867414 0 -0.497587 +0.522385 -0.700066 -0.486848 0.374142 0.363513 -0.853157 0.431889 -0.431889 -0.791797 +0.831385 -0.356979 -0.42587 0.766349 0.569701 -0.2969 -0.78086 -0.496911 -0.378599 +-0.0498854 -0.997708 -0.0457283 0.898977 -0.433004 -0.0659396 0.886174 -0.43135 -0.169214 +-0.19776 -0.939362 -0.280161 -0.457524 0.481138 -0.747782 0.475591 -0.744403 -0.468698 +0.0990088 -0.964033 -0.246653 -0.601644 0.595687 0.532147 0.683818 0.683818 0.254532 +-0.524195 0.725137 0.446537 -0.752249 -0.451349 -0.480006 0.15757 -0.601632 -0.783077 +0.290014 -0.913545 -0.285181 0.220903 -0.965483 -0.138 -0.794953 -0.539432 0.277603 +-0.326052 -0.846725 -0.420413 0.903413 -0.171776 -0.392858 0.338093 0.899623 -0.276355 +-0.34795 -0.394343 -0.850544 -0.548044 -0.631898 -0.548044 -0.724555 -0.169577 -0.66803 +-0.0188745 -0.968891 -0.246767 -0.64358 -0.64358 -0.414258 0.356294 -0.456056 -0.815517 +-0.648772 -0.689441 -0.322127 -0.465801 -0.237753 -0.852351 0.741325 0.585257 -0.328499 +-0.779814 -0.582047 -0.23046 0.928625 0.19967 -0.312711 0.874323 -0.483668 -0.0403057 +-0.0607823 -0.850952 -0.521715 -0.118168 -0.948487 -0.293953 -0.308258 -0.703795 -0.640039 +0.753462 0.453637 -0.475929 0.0160721 -0.962033 -0.272461 -0.830418 0.553612 -0.0626109 +0.968504 -0.141732 0.204724 0.79849 -0.598867 0.0614223 0 -0.848336 -0.529458 +-0.82896 -0.524195 -0.195049 0.844479 0.52011 -0.12783 0.767202 -0.627521 -0.132732 +-0.353643 0.640381 -0.681798 0.25207 -0.930719 -0.264996 0.305425 -0.916274 -0.259148 +-0.384486 0.90221 0.195415 0.822951 0.565779 -0.0514344 -0.152098 -0.221233 -0.963287 +-0.354471 -0.16953 -0.91957 -0.196067 0.980334 -0.0224076 0.133009 0.975401 -0.17579 +0.542551 -0.613318 -0.574003 0.0839211 -0.917138 -0.389634 0.710839 0.690135 0.135727 +0.732502 -0.138581 -0.666511 0.629762 0.414389 -0.657025 0.528107 0.55369 -0.64384 +-0.491848 0.86233 -0.1203 0.379547 0.919673 -0.100726 -0.167817 0.976388 0.136033 +0.431277 -0.782524 -0.449062 -0.662868 -0.224902 -0.714161 0.22194 0.887761 -0.403264 +0.0235352 0.0823732 -0.996324 0.420952 -0.762634 -0.491111 -0.752994 0.648856 -0.109478 +-0.92798 0.107075 0.356915 -0.701876 0.694926 -0.156358 -0.392322 0.603572 0.694107 +-0.618742 -0.563607 0.547271 0.184252 0.982043 -0.0405227 -0.792281 0.313923 0.523205 +0.519907 0.854133 -0.0123787 -0.790345 -0.451626 0.41399 0.994274 0.0994274 0.0391684 +0.758853 -0.649703 -0.0450461 -0.771996 -0.571631 -0.277958 -0.395817 -0.918132 0.0190427 +0.108157 -0.973417 0.201894 0.375903 -0.884477 -0.276399 0.468316 -0.81339 -0.345075 +0.460938 0.784575 -0.414704 0.570948 0.741135 -0.353183 -0.572372 -0.803757 -0.162375 +0.662022 -0.165505 0.730982 0.820325 -0.392329 -0.416107 0.185374 0.98164 -0.0449392 +-0.345809 0.912937 -0.216707 -0.95324 -0.300571 -0.0314884 -0.947779 -0.31749 -0.0302372 +-0.0734306 -0.996558 0.0384637 -0.888005 -0.186481 -0.420323 -0.38819 -0.916742 0.094306 +0.549025 0.695432 0.463622 0.676753 -0.728811 0.104116 -0.717518 0.391374 0.576189 +0.585463 -0.712237 -0.387236 0.335432 0.934417 -0.119797 -0.371232 0.877457 0.303735 +-0.662085 0.0827606 0.744845 0.848308 0.0614716 -0.525923 0.905197 -0.201155 -0.374372 +-0.596285 -0.745356 0.298142 0.286282 -0.866402 -0.409134 0.135725 -0.841956 -0.522197 +-0.616898 -0.663344 -0.423571 0.533046 -0.78434 -0.31729 0.637231 0.756711 -0.146032 +-0.991192 0.052168 0.121725 0.384619 -0.915758 -0.115996 0.311161 -0.889033 -0.335857 +-0.926151 -0.139271 -0.350498 0.98582 -0.167403 0.0116252 -0.938026 -0.343943 -0.0425586 +-0.748222 -0.407409 0.523625 -0.670201 0.736762 0.0895131 0.75071 0.619153 0.230399 +0.48915 0.850043 -0.195342 0.504961 0.844554 -0.178164 -0.536778 0.782554 -0.315402 +0.688318 0.51766 -0.508179 0.294609 -0.922464 -0.249532 0.864722 0.502097 -0.0123975 +0.605492 0.749313 -0.268159 0.839586 0.529429 0.121658 0.217879 0.917911 -0.331615 +0.757777 0.277496 0.590568 0.864994 0.445953 0.230024 -0.388816 0.772288 -0.502388 +0.65577 0.733198 -0.179964 -0.415453 -0.127365 -0.900654 -0.314221 -0.287478 -0.904777 +-0.449256 0.256718 -0.855725 0.106878 -0.299781 -0.948002 -0.267316 0.169883 -0.948515 +0.305573 -0.672644 -0.673925 -0.328982 -0.769959 -0.546749 -0.948924 -0.141738 -0.281875 +0.817136 -0.548751 -0.176527 -0.677952 -0.623459 -0.389462 0.685388 -0.292848 -0.666696 +0.838632 -0.00618156 -0.544664 0.843287 -0.130996 -0.521255 -0.204589 -0.7573 -0.620193 +0.357737 -0.829122 -0.429628 -0.638917 -0.610451 -0.468118 0.469127 0.69267 -0.547839 +-0.268075 0.842522 -0.467217 -0.550732 -0.483631 -0.680291 0.826703 0.40761 -0.387836 +0.907922 -0.344613 -0.238578 0.0448372 -0.941581 -0.333788 -0.486454 -0.758296 -0.433993 +0.394619 -0.841407 -0.369202 -0.664798 -0.47826 -0.573857 0.276694 -0.122975 -0.953057 +-0.883592 0.465557 -0.0502072 0.462115 -0.777194 -0.427106 0.603159 -0.356707 -0.713414 +0.820164 0.340068 -0.460092 0.952035 0.0115749 -0.30577 -0.0800304 0.98323 -0.163872 +-0.814202 0.576286 0.0704937 0.282028 -0.568795 -0.772614 0.903205 0.418662 -0.0945708 +0.376086 0.621111 -0.687591 -0.637916 0.0609773 -0.767688 -0.57735 -0.57735 -0.57735 +0.276421 -0.900183 -0.336545 0.35853 -0.90913 -0.211988 0.30702 0.846952 -0.434063 +-0.452599 -0.243188 -0.857912 -0.765682 -0.566025 -0.305526 0.66924 0.547077 -0.502816 +-0.0733032 0.704525 -0.705883 -0.897898 -0.242961 -0.367082 0.770579 -0.597782 -0.221055 +-0.311776 0.770271 -0.556307 0.541317 -0.589939 -0.599123 -0.485384 -0.654705 -0.579451 +0.467348 -0.233674 -0.852632 0.344958 0.936315 -0.0657063 0.905647 -0.416108 0.0815898 +0.204546 -0.920458 -0.333043 0.828805 -0.214514 -0.516784 0.493847 0.823079 -0.280457 +0.417315 -0.602789 -0.680069 -0.715093 -0.666336 -0.211277 -0.486664 -0.811107 -0.324443 +-0.668648 -0.728751 -0.147754 -0.98481 -0.171271 -0.0285452 -0.583193 -0.770648 -0.256883 +0.724062 0.395093 -0.565363 0.431021 0.636194 -0.639905 0.453626 0.583736 -0.673406 +0.38199 -0.273335 -0.88282 -0.114376 0.564731 -0.817311 -0.620628 0.639645 -0.453514 +-0.457017 0.888877 0.0321449 0.87115 0.195564 0.450391 -0.912988 0.300749 0.275687 +0.633054 -0.296257 -0.715174 0.571811 -0.62993 -0.525567 0 0.0634628 -0.997984 +0.649847 -0.361026 -0.668849 -0.781651 0.586238 -0.21295 -0.783687 0.391844 -0.481968 +-0.898908 -0.437864 0.015454 0.714292 0.479267 -0.509989 -0.399538 -0.762754 -0.508503 +-0.814241 -0.425626 -0.394784 0.3771 -0.627326 -0.681365 0.646204 -0.427328 -0.632307 +-0.779839 -0.241456 -0.577538 -0.0748111 -0.654597 -0.752267 -0.176038 0.909527 -0.376525 +0.331348 0.649953 -0.683937 -0.0898383 -0.235826 -0.967634 -0.468896 -0.805914 -0.36144 +0.5891 0.0632205 -0.805583 -0.956807 -0.264417 -0.12085 -0.651873 0.641186 -0.404897 +0.00783244 -0.871638 -0.490087 0.156549 -0.834926 -0.527627 0.416594 -0.505777 -0.755407 +-0.199523 -0.737045 -0.645721 0.278518 0.875341 -0.39523 -0.415601 0.373479 -0.82933 +0.857881 0.0504636 -0.511364 -0.19959 -0.471758 -0.858841 -0.0649442 -0.828039 -0.556897 +0.140919 -0.79407 -0.591265 0.517987 -0.426577 -0.741432 -0.527934 0 -0.849285 +0.901637 0.324026 -0.286458 -0.339653 -0.915176 -0.217 0.984734 -0.0364716 -0.170201 +0.885734 -0.207594 -0.415188 -0.858898 0.458079 0.229039 0.803824 -0.506112 -0.312598 +-0.27654 -0.72098 -0.635384 0.0623288 -0.849938 -0.523184 -0.166814 -0.925299 -0.34058 +0.292642 -0.899488 -0.324473 -0.318389 -0.833989 -0.450655 -0.978475 -0.070904 -0.193804 +-0.0285972 -0.972306 -0.231955 0.117531 -0.919095 -0.3761 -0.957871 -0.133723 -0.254168 +-0.823829 -0.457683 -0.334414 0.398265 0.792466 -0.461933 0.428799 -0.227011 -0.874413 +-0.170514 -0.882662 -0.437987 -0.309645 0.945233 0.103215 0.299377 0.94349 -0.142128 +-0.686641 0.672336 0.276564 0.79082 0.39394 0.468417 0.198386 0.839325 0.506139 +-0.599515 0.792319 0.113188 -0.505675 0.852423 0.13292 0.686569 0.473733 -0.551544 +-0.317313 -0.463766 -0.827184 -0.885988 -0.437782 -0.152876 -0.462528 -0.886512 -0.012848 +-0.895062 0.401235 -0.194616 0.978544 -0.144845 0.146529 -0.920899 0.327367 -0.211603 +0.872236 0.208885 -0.442234 0.262363 0.910983 -0.318237 0.856852 -0.509602 0.078169 +0.452362 0.876452 0.164924 0.258517 0.939418 -0.225084 -0.577073 -0.146094 -0.803519 +0.907065 -0.420878 0.00967536 -0.680325 0.255122 -0.687074 -0.0663093 0.582048 -0.810446 +0.241924 0.836654 -0.491408 -0.214169 0.938494 -0.270853 -0.510582 0.807553 -0.295235 +-0.504783 -0.261739 -0.82261 0.188695 -0.677906 -0.71052 0.477558 -0.586803 -0.653911 +-0.263785 -0.338687 -0.903166 -0.75132 0.0655221 -0.656677 0.727267 -0.595036 -0.342074 +0.126841 -0.692484 -0.710196 -0.89961 0.188291 -0.394015 0.971484 -0.139854 -0.191466 +0.937898 -0.346129 -0.0232614 -0.411939 0.56794 -0.712566 0.98726 0.13226 -0.088458 +0.84923 -0.36028 -0.386014 0.289955 -0.849693 -0.440395 -0.275267 -0.572488 -0.772324 +-0.634616 0.0475962 -0.771361 -0.768179 0.303905 -0.563509 0.409018 -0.553643 -0.725385 +-0.332772 -0.181342 -0.925407 0.972512 -0.230689 -0.0316632 -0.461936 0.14838 -0.874413 +-0.41959 0.13741 -0.897253 0.525532 -0.586224 -0.616569 0.917388 -0.391152 -0.0734801 +-0.786912 0.208901 -0.580629 0.946439 -0.259633 -0.191951 0.975783 0.118072 -0.184137 +-0.0359614 0.471647 -0.881054 0.00288181 0.370312 -0.928903 -0.282292 0.341722 -0.896402 +-0.530768 0 -0.847517 0.714956 -0.492864 -0.495906 0.257836 -0.699282 -0.666727 +-0.389729 0.167027 -0.905656 -0.259181 0.610927 -0.74806 0.184859 0.0591548 -0.980983 +-0.792411 -0.113653 -0.599307 -0.0431343 -0.197422 -0.979369 -0.533551 0.568348 -0.626342 +-0.166914 -0.926267 -0.337888 -0.555622 -0.65112 -0.517037 -0.967191 -0.0541803 -0.248204 +0.355469 -0.823079 -0.442925 -0.443465 -0.518587 -0.731031 -0.962629 -0.172669 -0.208642 +-0.897661 -0.368472 -0.241728 0.111658 -0.899335 -0.422764 -0.985853 0.131067 -0.104474 +0.84965 -0.515126 -0.112876 -0.31059 0.148658 -0.938848 -0.546689 -0.81886 -0.17493 +0.0932093 -0.0955993 -0.991046 0.873465 -0.11493 -0.473127 -0.570436 -0.371767 -0.732388 +0.709737 0.415677 -0.568758 -0.392645 -0.208593 -0.895722 0.867856 0.3632 -0.338986 +0.142514 0.0729142 -0.987103 0.837133 -0.107522 -0.536328 0.486846 -0.699546 -0.523084 +0.571804 0.507796 -0.644347 -0.324074 0.544199 -0.773837 0.587523 -0.587523 -0.556447 +0.988308 -0.0515375 -0.143497 0.917428 0.397619 0.0150234 -0.835169 -0.378436 -0.399098 +0.756881 -0.47434 -0.449592 0.557993 -0.557993 -0.614238 0.645238 -0.737415 -0.199717 +-0.536173 -0.632409 -0.559086 -0.0521256 -0.79926 -0.598721 0.334866 -0.837718 -0.431384 +0.79371 0.163781 -0.585833 -0.655386 -0.573462 -0.491539 -0.865591 -0.0748042 -0.495132 +0.43624 -0.833989 -0.337872 -0.0930372 -0.868347 -0.487153 -0.227076 -0.866197 -0.445129 +-0.221386 -0.854474 -0.469961 -0.381018 -0.847195 -0.37025 -0.570541 0.0691565 0.818352 +0.98138 -0.160694 -0.105216 -0.247 0.968627 0.0274444 -0.885824 0.420198 0.19685 +-0.531494 0.341675 -0.775095 0.421847 0.888099 -0.182554 -0.0482555 0.965109 -0.257362 +-0.942078 0.285478 -0.176045 0.583927 -0.237896 -0.776166 0.419812 0.257826 -0.87022 +0.35965 -0.0384092 -0.932296 0.799879 -0.131939 -0.585479 -0.56129 -0.799013 -0.215712 +0.725606 -0.0846922 -0.682878 -0.315342 -0.386549 -0.866683 -0.271599 -0.364468 -0.890728 +-0.537244 -0.672859 -0.508556 -0.911339 0.0233079 0.410996 0.499059 -0.767783 -0.401807 +0.0522273 -0.377777 -0.924422 -0.117719 -0.375663 -0.919249 -0.181005 -0.837847 -0.515024 +-0.158557 -0.372608 -0.914343 0.980312 -0.121694 -0.155498 -0.219502 -0.969891 0.105497 +-0.903913 -0.18403 0.386102 -0.136131 -0.658635 -0.740046 0.227687 -0.607165 -0.761256 +0.0231565 -0.708202 -0.70563 0.348935 -0.747719 -0.564943 -0.147009 -0.65104 -0.744671 +-0.42495 -0.459539 -0.779898 0.594258 0.556527 -0.580633 -0.325345 -0.945592 0.0025368 +-0.188083 -0.442548 -0.876799 0.0850369 -0.357863 -0.929894 -0.389308 -0.448572 -0.804501 +0.670611 0.63708 0.380013 0.909109 0.37532 0.18071 -0.646918 -0.752538 0.123223 +0.255233 -0.478561 -0.84014 0.411 -0.135232 -0.901549 0.375551 -0.187775 -0.90758 +-0.656132 -0.549009 -0.517764 -0.3431 -0.740116 -0.578369 -0.168283 -0.501845 -0.848429 +0.232733 0.806808 -0.543044 -0.114145 -0.984498 -0.133169 0.0698558 0.878187 0.47319 +0.960232 0.0751202 0.268908 0.511673 -0.473514 -0.71692 0.132824 -0.136849 -0.981647 +-0.887468 -0.320718 -0.33097 0.193703 -0.830886 -0.52164 -0.903527 -0.172972 -0.392071 +0.658512 0.70729 -0.257104 -0.180472 0.789563 -0.586532 0.285217 0.918699 0.273208 +0.58228 0.804605 0.116456 -0.738717 -0.110808 0.664845 0.939025 -0.222655 0.262024 +-0.464892 0.858465 0.216597 0.317284 -0.784144 -0.533339 0.135133 -0.713777 -0.687213 +0.199732 0.965371 -0.16783 -0.591054 -0.797923 0.118211 -0.895819 0.422518 -0.137792 +-0.976474 0.205573 -0.0650983 -0.985232 0.168897 -0.0281495 0.830002 -0.536817 0.15141 +0.881484 0.336567 0.331224 -0.934465 -0.271554 -0.230288 0.493791 -0.841532 -0.219076 +-0.714548 -0.519671 -0.468363 0.429581 -0.801884 -0.415261 0.437341 -0.799514 -0.411716 +-0.968303 -0.162936 -0.189316 -0.0718217 -0.655373 -0.751883 0.103111 0.523737 -0.845617 +0.719326 -0.31275 -0.620288 -0.118581 0.783188 -0.610372 -0.500901 0.712981 -0.490669 +0.928063 -0.326783 -0.178641 -0.574786 -0.0334178 -0.817621 -0.35944 0.86132 -0.359069 +-0.252345 -0.400784 -0.880735 -0.499005 0.483651 -0.719079 -0.210502 0.834124 -0.50983 +-0.40673 0.777216 -0.480111 -0.00439597 0.84073 -0.541437 0.291792 0.827992 -0.478839 +0.302693 0.91365 -0.27133 -0.130404 0.891094 -0.43468 0.0603017 0.983449 -0.170855 +-0.186761 -0.832641 -0.521373 -0.679514 -0.434134 -0.591429 0.420585 -0.723185 -0.547825 +-0.245572 -0.0731492 -0.966614 -0.661545 0.340598 -0.668095 0.629657 -0.553173 -0.545465 +-0.358405 -0.248571 -0.899866 0.891806 -0.452011 -0.0191974 -0.754601 0.0855841 -0.650578 +0.356064 -0.715145 -0.601486 0.995831 -0.0142262 -0.090099 -0.58215 0.429977 -0.690087 +-0.724248 0.347639 -0.595493 -0.801478 0.281976 -0.527373 -0.779075 0.302749 -0.548985 +-0.898627 0.149771 -0.412357 0.914188 -0.148247 -0.377205 0.992642 -0.0428094 0.113266 +0.0517198 -0.947414 -0.315805 -0.927056 0.0902244 -0.363905 -0.559794 -0.737206 -0.378363 +-0.958316 -0.180477 -0.221494 -0.714704 -0.565303 -0.411863 0.646173 0.396238 -0.652269 +0.892075 0.079219 -0.444889 -0.800266 -0.561011 -0.211754 -0.996918 0.0657309 -0.0428246 +-0.189974 -0.93027 -0.313859 0.290537 -0.646816 -0.705136 0.490444 -0.759708 -0.426975 +0.901897 0.167689 -0.398073 0.890305 0.39509 -0.22641 0.724946 -0.54371 -0.422885 +0.793386 -0.288132 -0.536207 -0.776237 -0.292262 -0.558605 0.976611 0.213166 0.0281372 +0.842339 0.304002 -0.445025 0.814105 0.306631 -0.493164 0.305038 0.0938579 -0.947704 +0.710796 -0.527646 -0.465143 0.374728 -0.498793 -0.781527 0.242357 -0.73988 -0.627568 +-0.867817 0.0565968 -0.49365 0.726207 0.6205 -0.295979 0.927935 0.371065 -0.0353137 +0.652765 -0.293271 -0.698491 0.696431 -0.115888 -0.708204 -0.302605 0.048315 -0.951891 +0.520128 0.779495 -0.349076 0.488382 -0.586761 -0.645906 0.327712 -0.0121375 -0.9447 +0.457791 -0.3689 -0.808913 -0.00402853 -0.739235 -0.673436 0.868894 -0.408891 -0.278983 +-0.641868 -0.421128 -0.640825 0.986782 0.150123 -0.0610255 -0.779776 -0.320574 -0.537756 +0.651451 -0.138391 -0.745962 -0.869597 0.0763549 -0.487823 0.710504 0.617323 -0.337781 +0.220767 -0.0229966 -0.975055 -0.0934787 -0.933175 -0.347053 0.0896729 -0.174364 -0.98059 +0.501089 -0.418718 -0.757354 0.172447 -0.172447 -0.969806 0.171111 -0.841528 -0.512397 +0.556848 -0.556848 -0.616312 0.180093 0 -0.98365 -0.244977 -0.792573 -0.558404 +-0.269363 -0.778844 -0.566432 0.378509 -0.485634 -0.787966 0.286378 -0.266507 -0.920305 +0.217689 0.0473238 -0.97487 0.200596 -0.351042 -0.914621 -0.101472 -0.854825 -0.508898 +0.178932 -0.178932 -0.967454 -0.0207192 -0.361976 -0.931957 0.498923 -0.507459 -0.702539 +0.483004 -0.509034 -0.712455 0.76903 0.587655 0.251507 -0.301348 0.919499 0.252411 +-0.0384775 -0.577162 -0.815723 0.082675 -0.277205 -0.957247 0.238274 -0.201616 -0.95004 +-0.765755 -0.402021 -0.501995 -0.69594 -0.510917 -0.504609 -0.13824 -0.984957 0.10368 +-0.502977 -0.863808 0.0291581 0.545883 -0.745784 0.381862 -0.953289 0.223045 0.203694 +-0.850018 0.525734 0.032756 0.409694 -0.594898 -0.691554 -0.674859 -0.503077 0.539887 +-0.0078526 -0.753849 -0.657001 0.737376 -0.63715 0.224315 -0.474709 -0.833032 0.284092 +0.279673 -0.619451 -0.733528 0.172541 -0.366033 -0.914467 0.157571 -0.0555251 -0.985945 +0 0.320807 -0.947145 -0.113267 -0.550789 -0.826923 -0.258576 -0.430959 -0.86453 +-0.347626 -0.61694 -0.706075 -0.394927 0.668337 0.630364 0.862274 0.485029 -0.145708 +0.210028 0.977087 0.0344973 -0.499032 0.738152 0.453981 -0.786821 0.236416 0.570106 +0.695176 0.283864 -0.660417 -0.176292 0.557082 -0.81153 -0.534678 -0.0379013 -0.844205 +0 -0.542159 -0.840276 -0.302755 -0.513602 -0.802841 -0.291386 -0.388514 -0.874157 +0.278614 -0.767009 -0.577989 -0.228 -0.182875 -0.956333 -0.754886 -0.310732 -0.577575 +0.269663 -0.910112 0.314607 0.833897 -0.551655 -0.0171056 -0.727183 -0.650326 0.219731 +0.930932 0.241584 -0.273865 -0.529984 0.711485 0.461417 -0.849591 0 0.527442 +-0.230759 0 0.973011 0.0269059 0.666241 0.745251 -0.281486 0.910101 0.304109 +-0.327088 -0.727603 -0.602998 -0.0227522 -0.712902 -0.700894 0.66698 -0.618231 -0.415847 +0.478994 -0.319329 -0.817676 -0.427317 -0.0899615 -0.899615 -0.0149172 -0.615334 -0.788125 +0.0405642 -0.699732 -0.713253 0.329685 0.576949 -0.747287 -0.29226 0.670479 -0.68194 +-0.0274813 -0.659552 -0.751156 -0.135381 -0.631778 -0.763235 -0.185869 -0.430745 -0.883126 +-0.888935 -0.146262 -0.434054 0.968516 0.0131871 0.248601 0.562413 -0.788051 -0.250335 +-0.161129 -0.986917 -0.00559477 -0.547542 -0.752558 0.365861 0.74427 0.0636547 0.664838 +-0.488658 0.730359 0.477273 0.614369 -0.78882 -0.0176979 -0.570599 -0.570599 -0.59062 +-0.380847 -0.914032 -0.139644 0.861457 -0.267811 -0.431473 -0.909721 0.1077 -0.40101 +-0.128373 -0.826398 -0.548258 0.714629 -0.691576 0.105017 -0.0867184 -0.552259 0.82915 +-0.997986 0.0608753 0.0178322 0.459158 -0.880053 0.121167 0.313637 -0.947183 0.0669092 +-0.357703 -0.933826 0.00422068 0.561474 -0.827436 -0.00985042 0.911792 -0.293076 -0.287649 +0.102258 0.2876 0.952276 0.641782 -0.187839 0.743527 -0.586427 -0.662095 -0.466619 +-0.970237 -0.222192 -0.0962831 0.213508 0.781899 0.585703 0.713906 0.695119 0.0845415 +-0.381851 0.575194 0.723423 -0.309021 0.0457809 0.949953 -0.393099 0.48295 -0.782453 +0.634537 -0.625066 0.454594 0.953295 -0.120956 0.276763 0.873406 0.291135 0.390386 +-0.280131 0.931437 0.232276 0.204539 0.562482 0.801111 -0.097859 0.831801 -0.546379 +0.907674 0 -0.419677 0.819948 0.571479 -0.0331292 -0.720129 -0.49272 0.488509 +-0.537713 -0.778958 0.322628 0.668143 -0.348287 0.657481 -0.687771 0.723042 0.0646623 +-0.0484929 0.994104 0.0969857 0.808223 -0.528712 -0.259305 -0.791052 0.566536 -0.230811 +0.752408 -0.165439 0.637583 -0.824815 -0.045823 0.563543 -0.940138 -0.197126 0.277998 +0.772106 -0.292395 -0.564231 0.667928 0.742142 -0.0556606 -0.836563 0.497088 0.230358 +0.186484 -0.959063 -0.213125 0.0693341 -0.990487 -0.118858 0.472632 0.836734 0.276577 +-0.0268747 -0.698743 -0.714868 0.924193 -0.370082 0.0943742 -0.969623 -0.102912 0.221904 +0.248282 -0.910366 0.331042 0.948932 -0.199775 -0.24417 -0.365232 0.896478 -0.250866 +-0.738858 -0.577498 -0.347254 -0.693041 -0.713791 -0.100982 -0.624894 -0.621727 -0.472189 +0.695174 -0.695174 -0.18294 -0.991053 -0.125374 -0.0457715 0.58828 -0.788073 -0.181294 +-0.595792 -0.746726 -0.295689 -0.759902 -0.597864 -0.255163 0.591665 0.792456 -0.148139 +-0.137568 -0.792204 -0.594548 -0.176128 -0.401005 -0.898985 0.393653 -0.719251 -0.572465 +0.55508 -0.80699 -0.201625 -0.0898359 0.285728 -0.954091 0.0976536 -0.19194 -0.976536 +0.427482 -0.61799 -0.659809 0.578262 -0.813487 -0.0620733 -0.255468 -0.271109 -0.928028 +-0.523024 0.156704 -0.837789 0.683384 -0.416816 -0.599375 -0.395116 0.531363 -0.749358 +-0.301731 0.484682 -0.821001 0.845864 -0.510983 -0.153006 0.819229 -0.569405 0.0681339 +0.709237 0.164138 -0.685596 0.207528 0.6127 -0.762581 0.923431 -0.310708 -0.225245 +-0.162754 0.787458 -0.594492 0.652019 0.0197582 -0.757945 0.768873 -0.253047 -0.587198 +-0.621695 0.321359 -0.714299 0.591213 -0.564986 -0.57555 -0.160851 0.0419611 -0.986086 +0.628373 -0.265136 -0.731334 0.729737 -0.367285 -0.576702 0.112827 -0.126101 -0.98558 +-0.664376 0.408847 -0.625659 -0.410122 0.31638 -0.855397 -0.337348 0.363297 -0.868454 +0.685398 -0.602109 -0.409504 -0.697605 0.379467 -0.607743 -0.0654955 -0.688832 -0.721956 +-0.871567 -0.0234853 -0.489713 0.670593 -0.741681 -0.0146125 -0.865168 0.146297 -0.479668 +-0.8769 0.10167 -0.469798 -0.847356 -0.194703 -0.494043 -0.829636 0.173678 -0.530603 +-0.901618 -0.0424442 -0.430445 -0.886804 -0.0928051 -0.452732 -0.811178 -0.0201535 -0.584451 +-0.85455 0.278911 -0.438125 -0.661704 0.676585 -0.323081 -0.429258 -0.520739 -0.737949 +-0.422608 -0.808144 -0.410251 -0.777924 0.0138915 -0.628205 -0.868429 0.0315543 -0.494808 +-0.761754 -0.364631 -0.535514 0.807114 -0.417473 -0.417473 -0.609969 -0.738179 -0.288149 +-0.949876 -0.0166645 -0.312182 -0.959867 0.157535 -0.232029 0.883655 -0.294552 -0.363858 +-0.758329 -0.262279 -0.59678 -0.711843 -0.4919 -0.501312 -0.153665 -0.881345 -0.446786 +0.652715 -0.141231 -0.744324 -0.298263 -0.742896 -0.599287 -0.894969 0.444145 -0.0420212 +-0.424483 -0.849683 -0.312815 -0.0522991 -0.780217 -0.623319 0.73675 -0.0437674 -0.674747 +0.845535 0.431687 -0.314192 -0.801317 0.489281 -0.34423 -0.927278 0.161833 -0.337588 +0.754098 0.324301 -0.571109 0.756034 0.574219 -0.314142 -0.459049 -0.86686 -0.194495 +0.776158 -0.0757228 -0.625975 -0.960802 0.277236 0 -0.725056 -0.28665 -0.6262 +0.717335 -0.531622 -0.450343 -0.812963 -0.0150549 -0.582121 -0.792509 -0.074795 -0.605256 +-0.644503 -0.245979 -0.723954 -0.565292 0.0699885 -0.821916 -0.480075 -0.801048 -0.357561 +0.508248 -0.512954 -0.691782 -0.871898 -0.272468 -0.406886 0.520541 -0.362801 -0.772924 +0.522675 -0.441918 -0.729053 -0.662861 -0.628622 -0.406755 -0.758493 -0.204926 -0.618623 +-0.537976 -0.37459 -0.755158 0.932353 0.346853 -0.10203 -0.816905 -0.00464811 -0.576753 +-0.383825 0.119945 -0.915583 -0.292393 -0.247722 -0.923656 -0.475811 -0.254623 -0.841885 +0.943693 0.321587 0.0776246 -0.0446794 -0.854494 -0.517537 0.238988 0.942297 0.234436 +-0.780142 0.61709 0.102848 0.825891 -0.560899 0.0574149 0.0991213 0.429526 0.897598 +-0.964809 -0.244802 0.0960009 0.411222 -0.712785 -0.568185 0.242342 -0.693802 -0.678167 +-0.163558 -0.906226 -0.389877 0.260388 -0.698313 -0.666751 -0.564163 -0.57856 -0.589057 +-0.00563318 0.9982 0.0597117 -0.996706 -0.0699794 -0.0409879 -0.368071 0.926019 -0.0837409 +0.21344 -0.649488 -0.729801 -0.420871 -0.899368 -0.118338 0.97701 0.212449 -0.0178101 +0.214514 -0.477185 -0.85222 0.0847024 -0.0889375 -0.992429 -0.450341 -0.378696 -0.808568 +-0.172528 -0.73739 -0.653062 -0.80767 -0.560697 0.182449 -0.302778 0.43692 -0.847011 +-0.29517 -0.361763 -0.884309 -0.716899 0.109357 -0.688547 -0.00174721 -0.676172 -0.736742 +0.111441 0.984393 0.136205 0.677331 0.727815 -0.107279 0.523706 0.851896 -0.00232758 +-0.55464 0.741248 0.378054 -0.961 0.0864778 0.262681 0.572965 -0.791734 -0.211823 +-0.625792 -0.727271 -0.281888 -0.366019 -0.524073 -0.76901 0.299987 -0.784325 -0.542994 +0.0899026 -0.70124 -0.707234 -0.726774 -0.34349 -0.594823 -0.540466 -0.493469 -0.681458 +-0.100363 -0.451634 -0.886541 0.908532 0.345828 -0.23446 -0.889587 -0.167233 0.425051 +0.111754 -0.621633 -0.775295 -0.349217 -0.133035 -0.92755 0.236301 0.843931 0.481603 +0.512517 0.772843 0.374219 -0.999969 -0.00476176 0.00634901 0.567379 -0.756505 -0.32524 +-0.532288 -0.846394 -0.0169279 -0.476215 -0.878352 -0.0414483 0.632215 0.12772 0.764193 +-0.0429579 0.948003 0.31535 0.956566 -0.206483 -0.205781 0.873818 0.484142 -0.0452653 +-0.636874 0.770953 0.00474862 -0.506477 0.569787 -0.647166 -0.0814436 -0.977323 0.195465 +-0.141925 -0.978539 -0.149395 0.415453 -0.909612 0.00232467 0.9282 -0.330592 0.170745 +0.882775 0.396348 0.252222 -0.809829 0.583077 0.0647864 -0.910695 0.395136 0.120423 +0.862701 0.38634 0.326326 0.988963 -0.0830152 0.122718 0.381722 0.819996 0.426492 +-0.702105 0.706334 0.0902303 -0.635553 -0.566701 -0.524331 0.188958 0.6771 0.711218 +0.93302 0.0222148 0.359139 0.675939 -0.692629 0.251739 -0.72403 0.686661 0.0653963 +-0.915396 0.349515 0.199723 -0.48367 -0.860083 -0.162235 0.862666 0.342529 0.37213 +-0.851916 0.523678 -0.000835212 0.808312 -0.5819 0.0895773 0.618689 -0.471382 0.628509 +-0.749072 0.650357 0.1262 0.10313 0.980641 0.166456 0.682158 -0.731202 -0.00222927 +-0.951088 -0.118595 -0.285249 0.77083 -0.632254 0.0779491 0.211129 0.889759 0.404664 +-0.542737 0.839349 0.0305026 0.789355 -0.612946 -0.0348831 -0.802624 -0.526311 -0.280699 +0.696452 -0.712102 -0.0886867 -0.972939 -0.023615 -0.229853 -0.531443 0.77455 0.342988 +0.590401 -0.801961 -0.0910202 0.868927 0.463428 -0.173785 0.981048 -0.151578 -0.120701 +-0.80975 0.563304 -0.164297 0.649936 -0.747776 -0.135695 -0.919676 -0.326727 -0.217818 +0.832367 -0.542793 -0.111985 -0.887132 -0.3314 -0.321203 -0.919632 0.386829 -0.0681209 +-0.918796 0.350723 0.181125 0.186736 -0.982362 0.00968799 -0.859083 0.501132 0.104131 +-0.961772 0.225352 0.1556 0.277818 -0.884901 -0.373854 0.662087 -0.735246 -0.145098 +-0.650969 -0.754723 -0.0814409 -0.647192 -0.616373 -0.448583 0.804875 -0.593398 -0.00736487 +0.120998 0.992372 0.0236056 -0.193053 0.978683 0.0700657 -0.676753 0.104116 -0.728811 +-0.239465 -0.810878 -0.533979 -0.580163 0.463296 -0.669901 -0.60503 0.537085 -0.587774 +-0.518402 0.439682 -0.733443 -0.562085 0.436355 -0.702606 -0.608176 0.351391 -0.711791 +-0.683912 0.254025 -0.683912 -0.479149 0.199326 -0.854801 -0.272635 0.576555 -0.77023 +-0.215907 0.663423 -0.716418 -0.394579 0.506252 -0.766823 -0.406585 0.609878 -0.680248 +0.297571 0.732482 -0.612309 -0.0874499 0.911977 -0.400812 -0.522842 0.692766 -0.4967 +-0.814067 0.323509 -0.482323 0.826154 -0.56344 0.00224542 -0.489245 0.462437 -0.739453 +0.77798 -0.430456 -0.457661 -0.566856 0.474453 -0.673475 -0.708262 0.397687 -0.583275 +0.806212 -0.58939 0.0514062 -0.817192 0.498769 -0.288835 -0.679102 0.469527 -0.564239 +-0.773952 0.348278 -0.528867 -0.910986 0.0700759 -0.40644 -0.978773 -0.13328 -0.155691 +-0.78692 0.041236 -0.615676 0.955127 -0.267503 0.127183 -0.70305 0.212241 -0.67873 +-0.572229 0.171218 -0.802022 -0.407421 0 -0.913241 -0.531609 -0.182043 -0.827195 +-0.770144 0.0738629 -0.633579 -0.823196 -0.00460744 -0.567739 -0.800541 0.198299 -0.56552 +-0.464681 0.52922 -0.709929 -0.445637 0.728326 -0.520527 -0.844319 0.360537 -0.396407 +-0.803115 -0.473484 -0.361689 -0.760715 -0.41624 -0.498053 -0.900008 0.133334 -0.414979 +0.525763 -0.126183 -0.84122 -0.747258 0.161173 -0.644693 -0.551081 -0.76919 -0.323507 +-0.572163 -0.754913 -0.320525 -0.897505 -0.110123 -0.427034 0.742705 -0.553222 -0.377274 +-0.167768 -0.894763 -0.413828 0.59585 -0.546195 -0.588756 -0.554865 -0.615994 -0.559175 +-0.566758 -0.775831 -0.277257 -0.204349 -0.819952 -0.534714 0.666597 -0.367943 -0.64828 +0.812309 0.445752 -0.376111 -0.420328 -0.754858 -0.503501 0.492021 0.00531915 -0.870567 +0.852066 0.41589 -0.317834 0.792062 0.517678 -0.323493 0.593697 -0.521295 -0.613005 +-0.705055 -0.236478 -0.668562 -0.620622 0.404258 -0.671866 -0.822447 0.568842 0 +0.11671 0.729157 -0.674322 -0.331979 0.799635 -0.500373 -0.803889 0.500019 -0.322092 +0.798379 -0.0523527 -0.599875 0.369866 0.156217 -0.915858 -0.619742 0.236812 -0.748225 +0.147107 0.249524 -0.95713 -0.94483 -0.181092 -0.272951 0.797186 -0.028269 -0.603072 +0.656777 -0.402486 -0.637691 0.314249 -0.196992 -0.928677 0.42417 -0.24661 -0.871357 +-0.3767 0.219742 -0.899895 0.149819 -0.301842 -0.941512 -0.636536 -0.642656 -0.426397 +0.392456 -0.364706 -0.844374 0.453076 -0.372224 -0.810044 -0.601956 -0.566373 -0.562913 +-0.769846 -0.60387 -0.206587 -0.648616 -0.253806 -0.717551 -0.482492 0.516685 -0.707275 +0.579276 -0.177645 -0.795539 -0.416049 0.462771 -0.782781 -0.664953 0.31518 -0.677125 +-0.309773 -0.937782 -0.156861 -0.28769 -0.939543 -0.185724 -0.654088 -0.735849 -0.175202 +0.821154 0.186192 -0.539479 -0.743262 -0.408794 -0.529574 0.532575 -0.282284 -0.797922 +-0.264237 -0.397348 -0.878802 0.0706018 -0.600115 -0.796792 -0.778534 -0.257697 -0.572256 +-0.0263625 -0.254837 -0.966624 0.156417 -0.449142 -0.879662 -0.72083 -0.511382 -0.46786 +-0.449142 -0.156417 -0.879662 -0.55333 0.166757 -0.816099 -0.372586 -0.62904 -0.682267 +-0.165314 -0.521468 -0.837104 -0.0278004 -0.185336 -0.982282 -0.2608 0.587367 -0.766148 +0.411074 -0.88683 -0.211069 0 0.211055 -0.977474 0.40504 0.495825 -0.76818 +0.272106 0.705253 -0.654658 0.880827 0.463391 0.0970186 -0.901997 0.43139 -0.0174299 +-0.100298 0.254602 -0.961831 0.712622 -0.698888 0.0610383 -0.762781 -0.642749 0.0709864 +0.542105 -0.496929 -0.677631 -0.506658 -0.608718 -0.610541 -0.1159 -0.321207 -0.93989 +0.477504 0.187591 -0.85837 0.384429 -0.242307 -0.890787 0.598357 -0.295391 -0.74479 +-0.294199 -0.501868 -0.813373 0.663932 0.165983 -0.729139 0.814149 0.32566 -0.480736 +-0.721139 -0.655581 -0.22399 0.247493 -0.489361 0.836226 0.87133 -0.483522 -0.0836124 +-0.163022 0.984484 -0.0649265 -0.905469 0.347303 0.243939 -0.847847 0.520966 0.0987452 +0.292962 -0.404503 -0.866343 0.0265558 -0.68455 -0.728482 0 -0.73366 -0.679517 +-0.26173 -0.628348 -0.732582 -0.00576413 -0.201744 -0.979421 0.782567 -0.146079 -0.605185 +0.677627 -0.511417 -0.528464 0.53679 -0.84314 -0.0311772 0.00518468 -0.995459 0.0950525 +-0.18027 -0.755749 -0.62956 0.131033 -0.512738 -0.848487 0 -0.263428 -0.964679 +0.0960711 -0.148345 -0.984258 0.242487 -0.664796 -0.706573 -0.196046 -0.844506 -0.498373 +0.125768 -0.440786 -0.888758 -0.244309 -0.745455 -0.620169 -0.228088 -0.740599 -0.632051 +-0.122469 0.35652 -0.926226 0.257634 -0.434757 -0.862908 0.00588786 -0.812524 -0.582898 +-0.25625 -0.318246 -0.912719 0.02465 0 -0.999696 0.686449 0.171612 -0.706638 +-0.77205 0.353728 -0.528029 0.409933 -0.625121 -0.664213 0.471881 -0.434815 -0.766984 +-0.0935671 -0.340244 -0.935671 0.13288 0 -0.991132 0.225659 0.0569847 -0.972538 +0.166533 -0.313345 -0.934923 0.463903 -0.746279 -0.477349 -0.807694 0.472775 -0.352298 +0.616546 0.784695 0.0642236 0.0344266 -0.496727 -0.867224 0.226224 -0.690792 -0.686752 +0.00989547 -0.951202 -0.308409 -0.668022 -0.276926 -0.690694 0.255987 0.252662 0.933077 +0.455952 0.237345 0.857773 -0.265447 0.245028 0.932469 -0.0574943 0.767959 0.637913 +0.904957 -0.0530101 0.422187 0.654389 0.75029 0.0940213 0.150119 -0.850672 -0.503807 +0.34337 0.587598 0.732684 0.0545363 -0.96711 0.248443 -0.830342 0.556603 0.0269394 +0.674611 0.732767 0.0891727 0.819487 0.569374 0.0652255 0.891889 -0.445384 -0.0785311 +-0.482966 -0.0817328 -0.871816 -0.975184 -0.0872116 0.203494 -0.0860946 0.941769 0.325051 +0.805688 -0.591934 0.0219235 0.343258 0.86368 0.369094 -0.966533 0.250162 0.0568549 +0.591278 0.743866 0.311534 0.743494 0.650557 0.154895 0.943002 0.189716 -0.273415 +0.874134 -0.277382 -0.398683 0.67779 -0.550704 -0.487161 0.33154 0.754817 0.565979 +0.626842 -0.676591 0.386386 -0.598319 0.800793 0.0272997 0 -0.871576 -0.490261 +0.0722282 -0.967055 -0.244105 -0.139767 -0.941485 -0.306711 0.828475 0.555684 -0.0696009 +0.903477 -0.424268 -0.0610457 -0.954305 0.183792 -0.235631 -0.946472 -0.165543 -0.277104 +0.62114 0.763484 0.176852 -0.669754 0.740255 0.0587504 -0.827886 0.454002 -0.329374 +0.854212 -0.51096 0.0961315 0.763126 0.457875 0.456058 -0.639139 0.768489 0.0304352 +-0.726498 0.687076 -0.0112635 0.827618 -0.529078 0.187416 -0.0556839 0.997249 0.0489344 +0.238792 0.878661 0.413442 -0.898035 0.436249 -0.0567479 0.0794507 -0.979177 -0.186817 +0.477464 -0.878192 -0.0284204 0.248372 -0.931394 0.266113 -0.953889 -0.0260367 -0.299027 +0.065624 -0.976008 -0.20761 -0.4787 -0.656139 -0.583376 0.793964 -0.53758 -0.283952 +0.870715 -0.477104 -0.119276 -0.904923 0.3541 -0.236067 0.971278 -0.182912 0.152191 +0.823809 -0.566067 0.03011 0.796188 -0.60419 0.0322235 -0.974865 -0.162277 -0.152661 +0.970984 0.192592 0.141769 0.931307 0.347631 -0.108724 -0.205526 0.976251 0.0685088 +-0.798314 -0.434083 0.417452 -0.417846 -0.473559 -0.775337 0.78154 -0.623635 -0.0165593 +0.828034 -0.520319 -0.208873 -0.473877 -0.880563 -0.00707279 -0.745437 -0.64853 0.154057 +0.550147 -0.696653 -0.460449 -0.926113 0.356864 -0.122326 -0.863385 0.400411 -0.306981 +-0.864079 0.29614 -0.407023 0.347919 -0.748956 -0.56393 0.167435 -0.771399 -0.61393 +-0.879002 0.355188 0.318115 -0.536021 -0.716349 -0.446684 0.645486 -0.761253 -0.0619761 +0.403714 -0.847329 -0.345034 -0.246641 -0.44043 -0.863243 -0.693521 0.709105 -0.127275 +-0.825708 -0.324556 -0.461378 0.982818 0.0530375 -0.176792 0.746583 -0.584152 -0.318403 +-0.510715 -0.2293 -0.828608 0.906152 0.247132 -0.34324 0.122792 -0.960899 -0.248184 +0.310242 0.916184 0.253688 -0.708175 -0.465174 -0.531132 0.978202 -0.145819 -0.147844 +-0.945683 0.282989 -0.160002 -0.494376 -0.861905 -0.112752 -0.539 -0.842296 0.00404395 +0.449857 0.878736 -0.159535 0.528707 0.827357 0.189603 0.537905 0.815755 0.212607 +0.064815 0.914355 0.399693 0.0703927 0.240253 0.968155 0.493916 0.631321 0.597898 +-0.61754 -0.782218 -0.0823387 -0.810859 0.548522 -0.20404 -0.36257 0.611997 -0.702854 +-0.778382 0.555209 -0.293027 -0.353858 0.234048 -0.905542 -0.697202 0.312684 -0.645088 +-0.3748 0.3748 -0.847968 0.0431331 0.539164 -0.841096 -0.116571 0.657874 -0.744052 +-0.249592 0.660891 -0.707762 0.0597491 0.336768 -0.93969 -0.577712 0.659838 -0.480483 +-0.60388 0.530683 -0.594731 -0.150688 0.539966 -0.828088 -0.369478 -0.383689 -0.846326 +-0.707931 0.353966 -0.611181 -0.306082 0.83371 -0.459609 -0.465637 0.456639 -0.758066 +0.153931 0.262965 -0.952447 0.213066 0.319599 -0.923287 -0.309679 0.424909 -0.850618 +-0.0362285 -0.00905712 -0.999303 -0.771425 0.115714 -0.625711 -0.810519 0.0457551 -0.583922 +-0.451093 0.663373 -0.597035 -0.312808 0.904909 -0.288603 -0.799353 0.583714 -0.14252 +-0.593688 -0.604582 -0.531051 -0.716216 -0.648564 -0.257681 0.389333 -0.449012 -0.804243 +0.574993 0.146991 -0.804846 -0.631734 -0.539535 -0.556609 0.696127 0.126568 -0.706674 +-0.661887 0.625641 -0.412891 0.134114 -0.560673 -0.817104 0.833614 0 -0.552348 +-0.890851 0.234503 -0.389092 -0.912051 -0.343773 -0.223569 -0.879735 -0.149288 -0.45142 +0.516374 0.344249 -0.784124 -0.539225 -0.644543 -0.542034 -0.555452 -0.336169 -0.760567 +-0.551223 0.045306 -0.833127 0.344184 -0.251519 -0.904586 -0.379469 0.236316 -0.894516 +-0.0505197 0.523332 -0.85063 -0.428999 0.712708 -0.554983 -0.423668 0.738436 -0.524612 +0.414105 -0.164673 -0.895209 -0.699193 0 -0.714933 -0.503052 0.220748 -0.835589 +-0.193854 0.693313 -0.694073 -0.749304 -0.194264 -0.633092 -0.559887 0.352447 -0.749872 +0.121946 0.858652 -0.497842 -0.782248 -0.349631 -0.515603 0.913806 -0.214453 -0.344917 +-0.997963 0 -0.0637986 -0.934474 0.254436 -0.249039 -0.970636 0.23386 -0.0563426 +-0.615577 -0.685302 -0.389135 -0.560925 -0.362052 -0.744501 0.307347 -0.53607 -0.786236 +-0.669301 0 -0.742992 0.401257 -0.517128 -0.756023 0.395116 -0.531363 -0.749358 +-0.632136 -0.102429 -0.768058 0.805925 -0.0215323 -0.591627 0.519204 -0.705333 -0.482631 +-0.672804 -0.067374 -0.736747 0.553892 0.568681 -0.608116 -0.71218 0.0727614 -0.698216 +-0.363524 -0.813978 -0.453088 0.561699 -0.27675 -0.779682 0.404642 -0.617612 -0.674404 +0.542029 -0.496095 -0.678302 0.17444 -0.0847279 -0.981016 -0.570814 -0.32865 -0.752436 +-0.303507 0.360772 -0.881888 -0.410297 -0.776689 -0.477923 0.407867 -0.210306 -0.888491 +0.372813 0.135367 -0.917979 -0.317082 0.018119 -0.948225 -0.831034 0.0494128 -0.554023 +0.55003 -0.523838 -0.650432 -0.305021 -0.570503 -0.762554 -0.833039 -0.183966 -0.521731 +-0.421117 -0.197667 -0.885205 -0.902386 -0.374105 -0.213882 -0.347955 -0.665913 -0.659914 +-0.960518 -0.274434 -0.0457389 -0.968183 -0.212205 0.132628 0.238512 -0.0226325 -0.970876 +0.422694 -0.422694 -0.801661 0.380996 -0.627881 -0.678681 -0.864546 -0.364306 0.346181 +-0.330467 -0.165233 -0.929241 -0.290911 -0.661823 -0.690914 0.0875772 -0.860865 -0.50124 +0.683175 -0.704524 -0.192143 -0.564599 -0.793711 0.226385 0 0.175069 -0.984556 +-0.37607 -0.75214 -0.541162 -0.455337 -0.610873 -0.64769 -0.578046 -0.611896 -0.539857 +-0.454739 0 -0.890625 0.321236 0.470648 -0.821766 0.309639 0.379102 -0.872012 +-0.174146 0.245853 -0.953535 0.270138 -0.433858 -0.85953 0.0202675 0.668827 -0.743141 +0.0359715 0.373359 -0.926989 -0.315727 -0.315727 -0.894781 -0.380819 -0.635748 -0.671417 +-0.423972 0.527501 -0.7362 -0.641047 -0.166119 -0.749308 -0.54974 -0.66345 -0.507562 +-0.87643 -0.316259 -0.363112 0.256253 -0.169224 -0.951682 -0.82949 -0.49722 -0.254398 +0.660376 -0.23983 -0.711607 -0.115367 -0.195076 -0.973979 0.00509622 -0.225508 -0.974228 +-0.23051 -0.669224 -0.706403 -0.501724 -0.666412 -0.551514 0.495783 -0.452462 -0.741268 +0.156365 -0.721412 -0.674621 -0.133386 -0.306152 -0.942592 -0.356597 -0.662251 -0.658986 +0.735799 0.674674 -0.0584333 -0.63828 0.760538 -0.119082 -0.172447 -0.172447 -0.969806 +-0.492528 -0.379489 -0.783201 0.325622 0.842744 -0.428664 0.107415 0.747668 -0.655328 +-0.81243 -0.183011 -0.553593 -0.983947 -0.178461 0 -0.172447 -0.172447 -0.969806 +0.082675 -0.277205 -0.957247 -0.14805 -0.345451 -0.926685 0.536267 0.701781 -0.468958 +0.312591 0.842878 -0.437999 -0.0678658 0.729558 -0.680544 -0.83351 -0.176319 -0.523615 +-0.0240081 -0.970826 0.23858 -0.976271 -0.21639 0.00838721 0.121592 -0.688336 -0.715128 +0.253107 -0.523776 -0.813385 -0.221956 -0.635875 -0.739188 -0.0115213 -0.724692 -0.688976 +0.386263 0.920088 -0.0651006 0.222209 0.96929 -0.105358 -0.0538851 0.948377 -0.312533 +-0.515738 0.055012 -0.854979 -0.427164 -0.768895 -0.475743 0.872329 -0.187977 -0.45134 +-0.696362 0.666457 -0.266298 0.378509 -0.485634 -0.787966 -0.135512 -0.597534 -0.79031 +-0.12771 -0.989754 -0.0638551 -0.14718 -0.987332 -0.0592808 -0.18581 -0.854396 -0.485264 +0.170756 -0.282894 -0.943829 -0.260393 -0.791765 -0.552542 -0.388232 0.522619 0.759042 +0.638935 -0.0418974 0.768119 -0.537887 0.758559 0.367786 0.00162546 -0.831423 -0.555637 +0.697849 0.536807 -0.474179 0.509548 -0.774121 0.375629 0.502088 -0.793742 -0.343339 +0.761545 -0.598772 -0.248035 0.718602 -0.617745 -0.319378 -0.655845 -0.139119 -0.741966 +-0.526761 -0.325527 -0.78521 -0.760006 0.313617 -0.569241 0.792057 0.243343 0.559848 +0.27298 0.961115 0.0417052 -0.135909 0.96431 0.227235 -0.455563 0.754413 0.472571 +0.0211604 0.921441 -0.387941 -0.545395 0.253523 -0.798918 -0.807688 -0.264334 -0.527037 +-0.401322 0.825934 0.395947 0.974495 -0.0417342 0.220496 0.984002 -0.127601 0.124329 +0.689761 0.720147 0.074952 0.398757 0.806309 0.436873 0.399496 0.559294 0.726356 +0.105949 0.994286 -0.0130398 0.676602 0.67255 0.299811 -0.109824 0.93223 -0.344797 +-0.850381 -0.390716 -0.35241 0.274537 0.137269 0.951728 0.747409 0 0.664364 +0.944553 0.280813 -0.17019 -0.691431 -0.718024 -0.0797805 0.412394 -0.830269 0.374945 +0.0349829 -0.988267 0.148677 -0.17372 -0.978318 0.112766 -0.550285 -0.822469 -0.143981 +0.597449 0.74922 0.285876 0.700209 -0.65902 0.274592 -0.284758 0.801629 0.525646 +-0.542715 -0.50395 0.671933 0.934404 -0.0133486 -0.355964 -0.610997 -0.73741 -0.287941 +0.936698 0.342018 -0.0749769 0.852352 -0.437889 -0.285919 -0.499767 0.866067 -0.0126647 +-0.971465 0.124172 0.202084 0.603574 0.720053 0.342378 0.888564 -0.458445 -0.0167852 +0.955239 -0.114 0.272988 -0.278246 0.924176 0.261684 0.348285 0.868507 0.352693 +-0.375022 0.912919 0.16105 -0.6494 -0.684822 -0.330604 0.754167 -0.270397 -0.598429 +-0.16011 -0.936841 -0.310957 -0.564127 -0.798593 -0.209785 -0.838848 0.522174 0.153845 +0.509181 -0.86066 -0.000343577 0.56089 -0.820125 0.113121 -0.749457 0.356538 0.557848 +0.757747 -0.652537 0.00394044 -0.855023 0.510462 0.0914577 0.317331 -0.769286 0.554527 +-0.641643 0.641643 0.420224 0.706267 -0.462727 -0.535789 -0.0413064 -0.888088 -0.457813 +-0.275058 -0.933038 0.231911 -0.854788 -0.518817 0.012922 0.153124 -0.922995 -0.353035 +0.577741 -0.812855 -0.0740391 0.396683 -0.895789 0.200509 0.70818 -0.682192 0.181918 +0.428481 -0.895219 0.122423 -0.476731 0.800909 0.362316 -0.473672 0.879163 -0.0520321 +0.258292 0.926072 0.275092 0.25796 -0.51592 -0.816874 -0.742338 0.652228 -0.153402 +-0.631057 0.308815 -0.711618 0.759959 -0.643043 0.094647 -0.43105 0.826179 -0.3628 +0.781053 -0.574353 -0.245103 0.325906 -0.422797 -0.845593 -0.145221 -0.875773 -0.460361 +-0.250892 -0.802855 -0.540812 0.972572 -0.0342254 -0.230071 0.25179 -0.795428 -0.551268 +-0.129511 -0.647553 -0.750934 0.485874 -0.85679 -0.172735 -0.682432 -0.554476 -0.476281 +0.944536 0.277064 0.176313 -0.862268 0.22419 0.454128 -0.964739 0.0639285 0.255327 +-0.931844 -0.362605 -0.013577 -0.594279 -0.772562 -0.223562 0.840345 -0.541131 0.0315954 +0.337175 -0.937691 -0.083952 -0.196653 0.947336 0.252749 0.567186 0.823586 0.00258989 +-0.154528 0.881383 0.446415 -0.151051 0.868543 0.472034 0.219365 0.877462 -0.426544 +0.668805 0.622144 -0.406986 -0.600171 0.493881 0.629187 0.237233 0.964443 0.116487 +-0.864581 0.432291 -0.256172 0.743713 0.131362 0.655465 0.987282 -0.0806346 0.137013 +-0.110199 0.539397 0.834809 -0.184052 0.94071 -0.28494 -0.806674 -0.235797 0.541919 +-0.824177 0.524476 0.213675 -0.79624 0.498977 -0.342088 0.18834 0.690065 -0.698813 +-0.0866422 0.880174 -0.466676 -0.647615 0.674422 -0.354612 0.766549 -0.618904 0.171349 +-0.874694 0.35783 -0.326906 0.500186 0.0218937 -0.865641 -0.428552 0.220262 -0.876258 +-0.633891 0.465654 -0.617534 0.317038 0.462008 -0.828273 0.45888 0.209489 -0.863449 +-0.172331 0.623042 -0.762969 -0.436479 0.421808 -0.79471 -0.436819 0.422177 -0.794327 +-0.204932 0.63529 -0.744587 -0.551799 0.469195 -0.689474 -0.0814733 0.537724 -0.839175 +0.0705667 0.650782 -0.755978 0.0268041 0.830926 -0.555737 -0.403932 0.580653 -0.706881 +-0.45241 0.407383 -0.793325 -0.706872 -0.115358 -0.697872 0.312871 0.368741 -0.875295 +-0.781045 0.292892 -0.551528 -0.643803 0.366898 -0.671493 -0.55345 0.675872 -0.486713 +-0.674034 0.26364 -0.690052 -0.620867 0.46565 -0.630629 -0.648835 0.511203 -0.563635 +0.414562 0.910015 -0.00337042 -0.370703 0 -0.928751 -0.160067 0.101506 -0.981873 +0.535569 -0.494819 -0.684339 0.619153 -0.755367 -0.21464 -0.649905 -0.400235 -0.646093 +0.720422 -0.432253 -0.542355 0.408888 -0.713695 -0.568726 -0.712135 -0.577994 -0.39848 +0.83064 -0.139465 -0.539061 -0.202699 -0.719788 -0.663942 0.873608 -0.0513887 -0.48391 +-0.943873 -0.330306 -0.00130685 0.917684 0 -0.397311 -0.936431 -0.211366 -0.280038 +-0.635925 0.236485 -0.734626 -0.786572 -0.0172602 -0.617258 -0.806092 0.307588 -0.505575 +-0.625074 0 -0.780565 -0.0319201 0.516273 -0.855829 -0.0713707 0.0356854 -0.996811 +-0.537102 -0.559349 -0.631387 -0.3801 0.471936 -0.795488 -0.749884 -0.326389 -0.575451 +-0.735108 -0.385716 -0.557529 -0.736127 0.114607 -0.66707 -0.65306 -0.483595 -0.582794 +0.477285 0.369327 -0.797369 -0.747115 -0.0498077 -0.662826 -0.205603 0.699771 -0.68414 +-0.364781 0.674051 -0.642331 -0.522794 -0.459039 -0.71831 0.185773 0 -0.982593 +-0.882847 0.0509335 -0.46689 0.671071 0.0447381 -0.740042 -0.52554 -0.21146 -0.824071 +-0.497897 -0.342304 -0.796823 -0.436782 -0.288301 -0.852117 0 -0.185408 -0.982662 +-0.723993 -0.00559789 -0.689784 -0.454739 0 -0.890625 -0.297124 0 -0.954839 +0.22072 0.839394 -0.496689 -0.258042 0.642919 -0.721158 -0.402083 0.467097 -0.787496 +0.133029 0.768613 -0.62573 0.131468 0.821464 -0.5549 -0.278108 0.661446 -0.696523 +-0.835613 -0.33955 -0.431807 -0.151789 -0.893317 -0.423019 -0.863961 -0.2239 -0.451044 +-0.839578 -0.423524 -0.340199 0.867114 -0.00530345 -0.498082 -0.904054 0.138161 -0.404472 +0.180169 -0.750704 -0.635596 0.0305688 -0.730056 -0.682704 -0.423027 -0.687823 -0.58987 +-0.492764 0.236527 -0.8374 0.822643 -0.364328 -0.43649 0.0683147 -0.959512 -0.273259 +-0.997079 -0.0487461 0.0587908 0.0222418 -0.578286 -0.815531 -0.76425 -0.390849 -0.51299 +-0.5314 -0.442833 -0.722159 -0.177164 -0.500682 -0.847308 0.547588 -0.305631 -0.778933 +0.474633 -0.870765 -0.128421 0.397145 -0.723829 -0.564223 -0.0380044 -0.547264 -0.836097 +0.395116 -0.531363 -0.749358 -0.11476 -0.774954 -0.621511 0.0101811 -0.897637 -0.440617 +0.883631 -0.239682 -0.402181 0.978105 -0.204513 0.0385314 -0.435043 -0.791438 -0.429375 +-0.333969 0.0811067 -0.939088 0.428472 -0.265245 -0.863746 -0.508177 0.182626 -0.841668 +-0.772113 -0.15745 -0.615672 -0.470757 0.262312 -0.842366 0.0920962 0.446937 -0.889812 +-0.0210331 0.57922 -0.8149 -0.634238 0.27247 -0.723535 0 -0.180093 -0.98365 +0.621395 -0.517209 -0.588527 -0.127733 0.58088 -0.803905 -0.261064 0.124687 -0.957235 +-0.713487 -0.249232 -0.654844 -0.919597 -0.038546 -0.390967 -0.826018 -0.0225278 -0.563194 +-0.636143 -0.373933 -0.674904 0.519706 -0.377321 -0.766508 -0.0341066 0.0341066 -0.998836 +0.177046 -0.481884 -0.858162 0.488382 -0.586761 -0.645906 -0.387423 -0.819548 -0.422191 +-0.628337 0.7181 -0.299208 -0.97997 -0.0544428 -0.191558 0.288155 -0.288155 -0.9132 +-0.328004 -0.770429 -0.546674 -0.319041 -0.738927 -0.593464 0.0421692 -0.685708 -0.726655 +0.598082 -0.642938 -0.478465 0.354843 -0.593661 -0.722255 -0.11671 -0.786079 -0.607008 +0.925631 -0.0970419 -0.365773 0.492707 0.854026 0.166973 -0.557297 0.827398 -0.0695197 +0.46927 -0.210363 -0.857632 0.570595 -0.423409 -0.703666 -0.117946 -0.451565 -0.884408 +0 -0.179487 -0.98376 0.846403 -0.50626 -0.165238 -0.644232 -0.759273 0.0920331 +0.812889 -0.452536 0.366638 -0.509134 -0.83879 0.192909 0.218713 -0.132745 -0.966718 +-0.200509 -0.200509 -0.958954 0.192226 -0.973665 -0.122579 -0.516551 -0.856214 0.00846805 +-0.0354702 -0.986073 0.162488 0.409464 0.394299 -0.82272 0.13231 -0.573343 -0.808561 +-0.490945 -0.499693 -0.713638 -0.106641 -0.00784122 -0.994267 -0.409572 -0.253756 -0.876276 +0.625638 -0.687389 -0.368882 0.494146 -0.484163 -0.722085 -0.3461 -0.359944 -0.866404 +-0.172447 -0.172447 -0.969806 -0.318335 -0.695493 -0.644167 -0.224632 -0.703848 -0.673897 +0 -0.587764 -0.809032 0.153572 -0.661029 -0.734477 0.00854327 -0.341731 -0.939759 +-0.575751 -0.589459 -0.566612 0.488449 -0.636069 -0.597355 -0.30195 -0.433514 -0.849054 +0 -0.175069 -0.984556 0.230173 -0.0886227 -0.969106 0.451094 0.263138 -0.852803 +-0.256417 0.682522 -0.684408 -0.349964 -0.817377 -0.457625 0.509858 -0.509858 -0.692884 +0.0929787 0.0929787 -0.991317 0.735538 0.465457 -0.492274 -0.726582 -0.59617 0.341556 +0.785634 -0.583274 -0.206328 0.139874 0.963575 0.227943 -0.602536 -0.763212 0.233363 +-0.279498 -0.792159 -0.542554 -0.210776 -0.321124 -0.923284 -0.19265 -0.17179 -0.966113 +0.0960711 -0.148345 -0.984258 0.349424 -0.255216 -0.901536 0.644899 -0.12441 -0.754074 +0.858479 -0.280656 -0.429239 -0.82493 -0.434405 -0.361638 0.731604 -0.491344 -0.472585 +-0.141882 -0.141882 -0.979663 0.208143 -0.976118 -0.0622036 0.728672 -0.613151 -0.305094 +-0.340743 -0.908647 -0.241359 -0.0150653 -0.858722 -0.51222 -0.184493 -0.798436 -0.573117 +0 -0.185408 -0.982662 -0.423191 -0.423191 -0.801136 0.423718 -0.501943 0.754 +0.914698 -0.0531801 0.400624 -0.0418217 0.888712 0.456554 -0.968621 0.0284889 0.246903 +0.0250435 -0.751305 0.659479 -0.358429 0.907385 -0.219503 -0.996783 0.0419208 -0.0683153 +-0.119944 -0.420374 -0.899388 0.934608 -0.142688 0.325805 -0.0671048 -0.971254 0.228392 +0.502134 0.819272 0.276866 0.616506 0.616506 0.489735 -0.0298865 0.999538 0.00553454 +0.289798 0.955825 -0.049147 -0.747094 0.516174 -0.418825 0.275001 -0.910122 0.309922 +-0.299544 -0.948843 0.0998479 0.710932 -0.640053 0.291389 0.798598 -0.466295 0.38054 +-0.0722302 -0.996777 -0.0349113 -0.268286 -0.960636 -0.0721198 -0.0161611 -0.985825 0.166998 +0.622713 0.178399 0.761842 -0.271273 0.883079 0.38286 0.113325 0.869937 -0.479965 +0.531963 0.0115144 0.846689 -0.354269 0.919412 0.170808 -0.784283 0.61792 0.0554544 +-0.976588 0.151139 0.153076 -0.972625 0.196841 -0.123508 -0.72475 0.102208 0.681389 +0.828698 -0.510537 -0.229372 -0.790963 -0.0903958 0.60515 0.894124 0.0286272 0.446903 +-0.275851 0.927108 0.253727 -0.542035 0.832958 0.111265 0.729044 -0.557877 0.396572 +-0.639486 -0.689102 0.340876 -0.924675 -0.377418 0.0503224 -0.219365 -0.731218 -0.645909 +0.891443 0.432647 -0.134707 -0.142372 0.968598 0.203833 -0.235422 0.881707 0.408864 +-0.814427 0 0.580265 -0.470187 -0.606294 0.641352 0.645084 -0.763198 0.0373523 +-0.763394 0.643827 0.0521193 0.716086 0.461247 0.5239 0.849921 -0.449344 -0.275179 +-0.914794 -0.333429 0.227986 0.963175 -0.157253 0.218095 0.969073 0.199515 0.145225 +-0.0623225 0.996256 0.0599139 0.674192 -0.244272 0.696991 0.371349 0.516538 0.771549 +-0.569795 0.776663 -0.268566 -0.718254 0.64363 -0.264293 -0.718274 0.517692 -0.464841 +0.480531 -0.835574 0.266282 0 0.890064 0.455835 0.365912 0.643118 0.672687 +0.832113 0.421851 0.360041 0.954546 0.130835 0.267813 -0.72099 0.674474 0.158928 +0.476716 -0.762746 -0.43699 -0.21813 -0.916637 -0.334956 0.858905 0.394416 0.326678 +-0.816859 0.315719 0.482766 0.417419 -0.849571 0.322477 -0.454239 -0.883701 -0.112871 +-0.561202 -0.810967 -0.165483 0.394132 0.878868 -0.268795 0.964523 -0.0595029 0.257206 +0.964573 -0.123663 0.233036 -0.769838 0.620974 0.147446 -0.859744 0.510178 0.0236194 +-0.424986 -0.786224 -0.448596 0.89496 0.356203 -0.268636 -0.693899 0.696463 0.182875 +-0.0058703 -0.817929 -0.57529 -0.113789 -0.785143 -0.608771 -0.773993 0.456103 0.43921 +0.312699 0.799121 0.513445 -0.111902 0.973338 0.200228 -0.576003 0.805213 -0.1409 +-0.982313 -0.133382 0.131417 0.395951 -0.905306 -0.153767 0.498366 -0.835842 0.230216 +-0.220191 0.790099 0.572066 0.624134 0.271363 0.732679 -0.419525 0.483477 0.768277 +0.632047 0.765289 0.121854 0.196978 0.875974 0.440305 -0.79877 -0.26753 0.538883 +0.297715 -0.952687 0.0612688 -0.580683 -0.763926 0.281468 -0.0227317 -0.941743 -0.335564 +-0.601703 -0.791714 -0.105562 0.100041 -0.969632 -0.223169 -0.721377 0.55087 -0.41971 +0.985732 0.0490414 0.161019 -0.556792 0.82601 0.0876998 -0.596851 -0.750327 0.284215 +0.878503 0.459525 -0.130649 0.059197 -0.980263 -0.188628 -0.371376 0.00884229 -0.92844 +0.57772 -0.804488 -0.137981 0.107465 -0.0179108 -0.994048 -0.686893 0.482398 -0.543572 +-0.240862 0.51314 0.823816 -0.926045 0.37362 -0.0533743 -0.412635 -0.909913 0.0423215 +0.906899 -0.378443 -0.185244 0.48306 -0.852947 -0.197825 -0.943578 0.229741 0.238493 +0.536044 -0.838564 -0.0973017 -0.818782 -0.438633 -0.370401 0.867657 -0.495804 -0.0367262 +0.569369 -0.765426 -0.299904 -0.202798 0.916731 0.344205 0.446501 0.788353 0.423246 +-0.736485 0.675257 0.0402355 0.984051 0.140318 0.109339 0.943237 0.210987 0.256494 +-0.579521 0.788357 0.206516 -0.71773 0.239243 0.653932 -0.987262 -0.0239204 0.157295 +0.0166855 -0.920734 -0.389833 -0.751227 0.659395 0.0292666 0.516148 0.842823 -0.152449 +-0.329945 0.942416 0.0546615 0.970761 0.142024 -0.193528 -0.0254645 0.96765 0.251007 +0.98907 0.121111 -0.0841046 0.723093 0.61011 -0.323886 -0.933481 -0.283745 -0.21932 +-0.798414 0.562519 -0.214725 -0.441277 0.375903 -0.814845 -0.242647 -0.0808824 -0.966737 +-0.853984 0.204956 -0.478231 0.989226 -0.0613474 -0.132919 -0.277813 0.909421 -0.309472 +-0.0427886 0.996974 -0.064896 -0.568726 0.820137 -0.0626494 0.181231 0.9834 -0.00891299 +-0.461182 -0.733552 0.499212 -0.987359 0.0658239 0.144186 -0.90507 -0.318266 0.282053 +-0.993195 0.116088 -0.00931568 0.0815103 0.151376 -0.98511 -0.949476 0.291748 -0.115663 +-0.0710296 0.756256 -0.650408 0.114096 0.932051 -0.343895 -0.480534 0.439512 -0.758891 +-0.736143 0.313542 -0.59982 -0.234798 0.626128 -0.743527 -0.412541 0.164372 -0.895987 +-0.925036 0.143975 -0.351538 0.66157 0.0374474 -0.748948 -0.185527 0.272833 -0.944003 +0.5317 0.745858 -0.401237 0.0477638 0.987686 -0.148977 -0.505042 0.603245 -0.617274 +-0.0350279 0.612227 -0.789905 0.6321 0.330034 -0.70109 0.771794 0.00627475 -0.635841 +-0.650412 0.459625 -0.604739 -0.0762216 -0.70505 -0.70505 -0.496523 -0.553814 -0.668397 +-0.165255 -0.694072 -0.700682 -0.792172 0.433567 -0.429515 -0.423179 0.215207 -0.880117 +0.0894459 0.673826 -0.733456 -0.895594 0.0334801 -0.443612 -0.887991 0.426662 -0.171554 +-0.144472 -0.764993 -0.627626 -0.664857 -0.441506 -0.602526 -0.766713 -0.606981 -0.209104 +-0.857561 -0.0751252 -0.508867 -0.282113 0.408106 -0.868252 0.0240754 -0.214672 -0.976389 +0.542341 0.198275 -0.816427 0.629121 0.351881 -0.693099 0.361147 -0.208119 -0.908988 +0.157398 -0.724033 -0.671567 -0.371133 -0.871357 -0.320932 0.406464 -0.468768 -0.784247 +0.312422 -0.551724 -0.7733 -0.455835 -0.0267213 -0.889663 -0.228108 -0.409086 -0.883525 +-0.497743 -0.617619 -0.608932 -0.409339 -0.7567 -0.509752 -0.729704 -0.508382 -0.457252 +0.656527 0.025579 -0.753869 0.760126 0.302839 -0.574889 -0.842957 -0.469264 -0.263088 +0.54045 -0.237798 -0.807072 -0.629224 -0.641809 -0.438359 0.827071 0.109236 -0.551381 +-0.373767 -0.289837 -0.881075 0.893271 0.0915583 -0.440096 -0.311345 0.166368 -0.935621 +-0.205981 -0.137321 -0.968873 -0.694872 0.0534517 -0.717144 -0.448149 0.0206045 -0.893722 +-0.499994 0.314238 -0.807007 -0.392693 0.145716 -0.908052 -0.767328 -0.00537847 -0.641233 +-0.138503 -0.246228 -0.959265 -0.100131 0.807082 -0.581887 -0.816115 0.0429534 -0.576292 +-0.830445 -0.337487 -0.443241 -0.335017 0.00744482 -0.942183 -0.423305 -0.100787 -0.900364 +-0.297061 -0.0206124 -0.954636 0.718451 0.0671799 -0.692326 0.797564 0.118416 -0.591498 +-0.604608 -0.763446 -0.227155 -0.186032 -0.677774 -0.711347 0.324318 -0.746686 -0.580756 +-0.450544 -0.0813013 -0.889044 -0.457855 -0.848157 -0.266456 -0.287508 -0.713168 -0.639321 +0.401061 -0.749153 -0.527181 0.178405 -0.766553 -0.616902 0.147486 -0.516902 -0.843244 +0.332688 -0.266844 -0.904496 -0.480452 0.182781 -0.857763 0.699503 -0.14626 -0.699503 +-0.652521 -0.756581 0.0424404 0 0 -1 0.574039 -0.732031 -0.366893 +0.0215465 0.797221 -0.603303 -0.366363 0.838326 -0.403718 0.540566 -0.0766156 -0.837806 +0.293806 -0.321788 -0.900073 0 -0.805683 -0.592347 0.58472 -0.577319 -0.569917 +-0.643565 0.213155 -0.735112 -0.759482 -0.165432 -0.629142 -0.483722 0.668977 -0.564342 +-0.206678 0.792266 -0.574106 -0.0288845 0.788133 -0.614827 -0.55797 0.52973 -0.638792 +0.759035 0.185262 -0.624135 0.363141 0.276421 -0.889786 0.481804 0.0161499 -0.87613 +-0.942707 -0.306703 0.13129 0.0975776 -0.780621 -0.617341 0.408642 -0.207282 -0.888845 +-0.362616 0.442157 -0.82037 0.172079 0.465394 -0.868215 0.544485 0.69033 -0.476425 +-0.365771 -0.802035 -0.472177 -0.13498 -0.781247 -0.609454 -0.589435 -0.63244 -0.502579 +0.365376 0.152051 -0.918358 0.857891 -0.0919169 -0.505543 0.483016 -0.438554 -0.75787 +0.479833 -0.125979 -0.868268 0.428564 -0.767844 -0.476182 -0.669657 -0.685368 -0.286061 +-0.584652 -0.733349 -0.346961 -0.630364 0.775832 0.0269386 0.702414 0.702414 -0.115015 +0.587823 -0.182105 -0.788227 0.762597 -0.0649019 -0.64361 0.574273 -0.19292 -0.795608 +0.452318 -0.452318 -0.768646 -0.568191 -0.409674 -0.71367 -0.552606 -0.566003 -0.611774 +-0.552647 -0.82897 -0.0859673 0.761816 0.128864 -0.634847 0.379944 0.293593 -0.877181 +-0.825219 0.5639 0.0320919 -0.449142 -0.156417 -0.879662 0.72444 -0.10363 -0.681503 +0.324992 -0.181527 -0.928131 -0.540602 -0.422114 -0.727715 0.51775 -0.285291 -0.806563 +-0.554546 -0.284383 -0.782052 -0.133974 -0.173529 -0.975673 -0.330467 -0.165233 -0.929241 +-0.244907 0.379966 -0.89199 -0.024013 -0.515648 -0.856464 0.0603724 -0.603724 -0.794904 +0.561852 -0.494688 -0.663028 -0.432075 0.0652188 -0.899476 -0.757459 0.269319 -0.594746 +-0.188802 0.0917038 -0.977724 -0.253825 0.779287 -0.572961 0.263356 -0.784216 0.561826 +0.60898 -0.750797 0.255827 0.276018 0.552036 0.78681 -0.513996 0.747631 0.420542 +-0.532352 -0.760503 0.371801 -0.133219 -0.666095 -0.733873 -0.589831 0.518208 -0.619322 +0.124862 -0.264413 -0.956293 0.165416 -0.478328 -0.862461 -0.875574 0.149034 -0.459521 +-0.543368 0.123027 -0.830431 -0.497284 0.0252588 -0.86722 0.764705 -0.0791655 -0.6395 +0.607932 -0.645026 -0.46299 -0.581591 0.803149 -0.129242 -0.424753 0.817144 0.389694 +0.83265 0.550926 0.0563447 -0.270036 0.938696 0.214314 0.149199 -0.0239454 -0.988517 +0.194734 -0.301523 -0.933361 -0.110504 0 -0.993876 -0.256578 -0.843645 -0.471625 +-0.400846 -0.754534 -0.519616 0.786517 -0.32386 -0.525837 0.555913 -0.526654 -0.643115 +-0.261925 -0.641027 -0.721443 -0.612827 0.779962 -0.126898 0.514418 -0.163678 0.841774 +-0.0646994 -0.139931 -0.988045 0.646157 -0.332785 -0.68683 0.913205 -0.145859 -0.380502 +0.0755444 -0.522145 -0.849504 -0.806435 0.280012 -0.520823 -0.354884 0 -0.93491 +-0.181037 -0.929324 -0.321844 0.445548 -0.661135 -0.603645 -0.208956 0.46676 0.859344 +-0.704145 0.631129 0.325356 0.109766 -0.259888 -0.95938 -0.788177 -0.498977 -0.360276 +-0.401809 -0.385644 -0.830559 0.568719 -0.290243 -0.769622 0.549939 -0.46946 -0.690778 +-0.363265 -0.690573 -0.625418 0.910828 -0.27297 -0.309644 -0.142386 0.987209 -0.0717203 +0.699557 -0.679852 0.220049 -0.853627 0.284542 0.436298 0.324002 -0.616962 -0.717203 +-0.582605 -0.543416 -0.604376 -0.0985291 0.239285 -0.965937 0.0603236 -0.475492 -0.877649 +-0.25542 0 -0.96683 0.481442 -0.813153 -0.327104 -0.518022 -0.84937 -0.101115 +-0.971218 -0.194552 -0.137422 0.461727 -0.887022 -0.000247841 -0.175985 -0.984321 -0.0119312 +-0.151461 -0.731338 -0.664984 -0.408019 -0.848249 -0.337629 0.0300072 -0.49762 -0.866876 +0 -0.25542 -0.96683 0.232757 -0.411802 -0.881047 -0.362132 -0.641275 -0.676482 +0.606449 -0.741216 0.287783 0.293729 -0.917009 0.269849 -0.160886 -0.326504 -0.931403 +-0.535961 -0.375173 -0.756301 -0.408275 -0.898718 0.160057 0.104298 -0.208596 -0.972425 +-0.271329 -0.407547 -0.871944 0.235196 -0.0306778 -0.971464 -0.0768463 0.922156 0.379109 +-0.694839 0.191197 0.693284 -0.629876 -0.404648 0.66296 0.171286 -0.236777 -0.956346 +0.16836 -0.274186 -0.946825 0 -0.724005 -0.689795 -0.0958648 -0.636026 -0.76569 +-0.0688492 0.907334 -0.414734 0.0720212 0.916019 -0.394616 -0.300483 0.832339 -0.465749 +0.445947 -0.876246 -0.182551 0.0776202 -0.993539 -0.0827949 -0.548156 -0.754604 0.360691 +0.0170736 0.273177 -0.961812 -0.335984 -0.0912549 -0.937437 -0.441987 -0.338543 -0.830684 +0.126111 -0.693611 -0.709225 0.635762 0.771252 0.031267 -0.163299 0.701899 -0.693304 +-0.387619 0.113702 -0.914781 -0.6267 -0.586958 -0.512569 0.636046 0.158279 -0.755244 +0.482455 0.0331458 -0.875293 0.714132 -0.0892666 -0.694296 0.779214 -0.25568 0.572235 +-0.418474 -0.89534 -0.152467 0.292683 -0.097561 -0.951219 -0.206772 -0.935644 -0.286035 +0.134147 0.972568 -0.190042 0.0410159 0.750004 0.66016 0.200766 0.790517 0.578597 +0.325909 0.678978 0.657854 0.209063 0.928485 -0.306933 0.890858 -0.206446 0.404663 +0.839116 0.427626 0.336184 0.129066 0.530605 0.837735 0.801616 0.248904 0.543561 +0.290777 0.717249 0.633247 -0.555659 0.782755 0.280246 -0.981049 0.178373 0.0756732 +0.535912 -0.602901 0.591024 -0.613857 -0.371237 0.696679 -0.868085 -0.0190788 0.496049 +0.637757 -0.699839 0.3217 0.715899 0.166664 0.678021 -0.941787 -0.0425323 -0.333508 +-0.390771 0.882853 -0.260514 -0.660552 0.590438 -0.46374 0.623763 0.733839 0.269074 +0.0144218 -0.973473 0.228346 -0.923465 -0.259298 -0.282802 0.602693 0.609244 0.515346 +0.646749 -0.729533 0.222482 -0.389103 -0.915112 -0.105682 -0.557366 -0.804395 -0.20565 +0.0460165 -0.966347 0.253091 0.473718 0.702629 0.530946 -0.36819 0.911494 0.183346 +0.920294 -0.0626903 0.386172 0.566078 0.649209 0.508019 -0.400389 0.883808 0.242017 +0.107032 0.791103 0.602246 0.851886 -0.387471 0.352357 -0.698937 0.712301 -0.0641472 +0.436605 0.703781 -0.560418 0.827097 -0.232813 0.511575 0.00514209 0.742175 0.670186 +-0.97065 0.0500804 -0.235226 -0.994384 0.057813 -0.0886466 -0.805787 0.401439 0.435377 +0.0855229 0.958325 0.27258 -0.829449 0.32249 0.456086 -0.105929 0.658274 0.745288 +-0.670837 0.213448 0.710223 0.937375 0.124064 0.325478 0.88404 0.276743 0.376678 +-0.463262 0.8165 0.344551 0.76058 -0.645527 0.0693831 0.982153 -0.0637072 -0.176965 +0.710444 -0.647925 -0.274705 -0.120542 -0.98945 0.0803614 -0.340571 -0.84512 0.412048 +0.745192 0.628883 -0.2218 -0.892643 -0.241861 0.380384 0.497432 0.834363 0.237485 +0.792949 0.109914 -0.599292 0.509942 0.747914 -0.424951 -0.0398567 0.99841 -0.0398567 +-0.185057 0.981606 0.0469347 0.215159 0.66063 0.719218 0.924525 -0.365326 0.108587 +-0.919617 -0.391427 0.0330119 -0.0499747 -0.998357 0.0280161 -0.249731 -0.968003 0.0245767 +0.726701 -0.0641207 -0.683954 0.208396 -0.968932 -0.133201 0.171561 -0.981113 -0.0893546 +0.983822 -0.168166 -0.0617616 0.645454 0.368348 0.66911 -0.449547 0.877687 0.166053 +-0.137044 -0.939732 -0.313244 0.663553 -0.744598 -0.0726025 -0.421637 -0.737865 -0.527046 +0.942883 0.0574669 0.328129 0.905207 -0.373301 0.203091 -0.257075 0.956559 0.137505 +-0.129518 0.979908 0.151673 0.757905 -0.606733 -0.239697 -0.864204 -0.251266 -0.435909 +-0.279377 0.95525 0.0971923 0.164566 0.919298 0.357505 0.416017 0.886297 0.203487 +-0.505457 0.315911 0.80294 -0.466701 0.132768 0.874393 0.656575 -0.366616 0.659168 +0.272729 0.873174 0.403962 -0.829695 -0.140709 0.540191 -0.801073 -0.563995 0.200476 +0.857995 -0.472093 -0.202419 -0.908661 0.147067 0.390777 -0.0739412 -0.985882 0.15023 +-0.906681 0.0546992 -0.418256 -0.676726 0.502087 -0.53847 0.909123 0.284535 -0.304198 +-0.69733 -0.686764 0.205149 -0.554284 -0.832311 0.00531262 0.766618 -0.622362 -0.157995 +-0.0734825 -0.981516 0.176708 -0.18421 -0.97222 -0.144412 0.578781 -0.720523 0.381916 +0.980987 -0.158048 -0.112632 -0.663863 0.743847 -0.0773174 0.00736467 -0.979502 -0.201301 +0.974387 0.111722 -0.19516 -0.563754 -0.317517 -0.762472 0.821896 -0.115656 0.557773 +0.840799 -0.450428 0.300285 -0.653473 0.756901 -0.00861895 -0.647625 0.761127 0.0356083 +0.422265 0.777187 0.466555 -0.721025 0.675819 0.152945 -0.692976 0.702392 0.162573 +0.722882 -0.684516 0.0942304 0.541656 -0.0492415 0.839157 0.713851 -0.695309 0.0834371 +0.879118 0.295715 0.37377 0.96499 -0.236752 0.112882 0.730211 -0.683181 -0.00742588 +-0.870976 0.491082 0.0154428 -0.918998 0.370755 -0.134103 -0.95269 -0.0790501 -0.293485 +0.950151 0.160415 0.267358 -0.632724 0.764542 0.12303 0.371687 -0.877865 -0.301995 +-0.746607 -0.411175 -0.522985 -0.996271 0.0379532 0.0774877 -0.973645 0.216366 -0.0721218 +0.496914 -0.819696 -0.284912 -0.945534 -0.280158 -0.16576 0.13028 0.70514 0.696997 +0.188894 -0.554088 0.810744 -0.868409 -0.169371 0.466026 0.190266 -0.874654 0.445846 +-0.409669 0.718718 0.561797 -0.249379 0.892778 0.375177 -0.286159 0.950614 0.120193 +0.368298 0.909472 -0.192918 0.309305 0.565341 0.76467 -0.153646 -0.171722 0.97309 +0.920076 -0.26439 0.289066 0.866675 0.454878 -0.204843 -0.161646 0.971446 -0.173678 +-0.313982 0.921559 -0.228351 0.540462 0.808422 0.23314 -0.935596 -0.165529 0.311865 +-0.315534 0.919775 -0.23335 0.123151 0.985211 -0.119136 0.423102 -0.883142 0.202597 +-0.814527 -0.550356 0.183452 -0.870338 0.490779 0.040604 0.635008 0.759741 0.139853 +0.346862 0.923006 0.166572 0.138148 0.989628 -0.0393882 -0.835204 -0.528989 0.150352 +0.256576 -0.837052 0.483231 0.41113 -0.828846 0.379456 0.339057 -0.856564 0.389023 +0.0881929 -0.264579 0.960323 0.89914 -0.167889 0.404178 0.0615907 0.588961 0.805811 +-0.126984 0.991592 0.0248987 -0.87468 0.471182 0.113674 0.250648 -0.943617 0.216246 +-0.214176 -0.942376 0.257012 -0.975106 0.0286796 0.219877 -0.713099 0.693524 0.102537 +-0.907426 0.36297 0.211733 -0.70946 0.701444 0.0681402 0.0514998 -0.885796 0.461209 +0.477187 -0.827124 0.296916 -0.717137 0.358569 0.597614 0.403604 -0.861021 0.309429 +-0.291659 0.556803 -0.777757 -0.986657 -0.130252 0.0976888 0.69208 0.0768978 -0.717713 +-0.666319 -0.0888426 -0.740355 0 0.999989 0.00476185 -0.22439 0.940992 -0.253344 +-0.717168 0.649934 -0.251507 -0.514956 0.836804 -0.185956 -0.407553 -0.584159 -0.701897 +-0.847143 -0.331942 -0.414927 0.0431467 0.848553 -0.527349 -0.519903 0.852641 -0.0519903 +-0.10897 0.796083 -0.595296 0.329238 -0.93284 -0.146328 -0.280591 -0.795009 -0.5378 +0.391983 0.744768 -0.540065 -0.525994 0.713849 -0.462332 -0.187814 -0.77597 -0.602159 +0.557839 0.424072 -0.713427 -0.844915 -0.455553 -0.28034 0.0729597 -0.496126 -0.86518 +-0.00704243 0.640861 -0.767625 -0.481616 0.854254 -0.195693 -0.703482 0.572602 -0.420999 +-0.484667 -0.274155 -0.830624 0.204732 0.584948 -0.784806 -0.816358 -0.19948 -0.542004 +0.313793 -0.926031 -0.209764 -0.685239 -0.711043 -0.157691 0.803183 -0.274421 -0.528762 +0.877625 0.0706135 -0.474119 -0.798958 -0.510612 -0.317714 0.00539076 0 -0.999985 +-0.0771935 -0.439117 -0.895107 0.183726 -0.110869 -0.976705 0.668356 0.200917 -0.716193 +0.604408 -0.304476 -0.736196 -0.869006 -0.025559 -0.494141 0.623928 0.0605303 -0.779134 +-0.941095 0.218178 -0.25834 -0.743661 0.40669 -0.530633 -0.294724 -0.854699 -0.427349 +-0.873177 0.311768 -0.374649 0.782583 -0.0622195 -0.619429 -0.843976 -0.53427 0.047538 +-0.628695 0.603547 -0.490382 0.762248 -0.31208 -0.567083 -0.861042 -0.315841 -0.398561 +-0.778741 -0.507254 -0.369128 -0.42219 0.509539 -0.74975 0.172483 0.172483 -0.969793 +0.553028 -0.563979 -0.613259 0.420491 -0.591386 -0.688077 -0.59626 0.59626 -0.537538 +0 -0.143589 -0.989637 0 -0.432897 -0.901444 -0.042924 -0.126906 -0.990986 +-0.556175 -0.498639 -0.664852 -0.586204 -0.103807 -0.803486 -0.5199 -0.0879433 -0.849688 +-0.313539 0 -0.949575 -0.579962 -0.297416 -0.758411 -0.469937 -0.16366 -0.867396 +-0.155191 -0.39592 -0.905076 -0.427715 -0.427715 -0.796316 -0.298438 -0.235307 -0.924968 +-0.25542 0 -0.96683 -0.423431 0 -0.905928 -0.132191 0 -0.991224 +-0.707368 -0.485599 -0.513638 -0.330467 -0.165233 -0.929241 -0.674422 -0.513411 -0.530626 +0 0.230805 -0.973 -0.0597167 0.17915 -0.982008 -0.586297 -0.377836 -0.716586 +-0.172447 -0.172447 -0.969806 0.175069 0 -0.984556 -0.133423 0.222371 -0.965789 +0.643257 0.757345 0.112469 0.665527 0.744119 0.0579688 -0.947659 -0.299019 -0.11194 +0.0212536 0.17503 -0.984334 -0.0969803 0.562486 -0.8211 -0.455376 -0.885453 0.0927618 +0.982315 -0.0605536 0.177175 -0.61987 -0.783887 0.0358197 0.626527 0 -0.7794 +0 -0.195557 -0.980692 -0.498237 0.486937 -0.717393 -0.845802 -0.127289 -0.518089 +0.501584 0 -0.865109 0 -0.0480727 -0.998844 0.247475 0.247475 -0.936756 +0.171035 -0.95851 0.228047 -0.544173 -0.229125 -0.80708 0.521558 0.462619 -0.716911 +-0.675914 -0.667248 0.312923 0.52245 -0.8127 0.258 -0.195557 0 -0.980692 +-0.984739 -0.0599483 0.163388 0.472562 -0.836072 0.278691 0.411622 -0.886571 0.211088 +-0.218638 -0.881923 0.417624 0.694576 -0.694576 0.187425 0.232311 -0.546207 -0.804791 +0.111943 -0.567709 -0.815583 0.655627 0.109967 -0.747034 -0.561904 -0.275844 -0.779855 +-0.149572 0.202363 -0.967821 0.0857304 0.0857304 -0.992623 -0.296679 0.941633 0.159089 +-0.446721 0.887487 0.113169 0.856361 -0.324379 -0.401775 0.576977 0.0940259 -0.81133 +0.0688846 0.251578 -0.965382 -0.172447 0.172447 -0.969806 0.300108 0.200072 -0.932688 +-0.738603 0.281173 -0.612705 0.0425635 -0.998883 0.0205271 0.229846 -0.919382 0.31923 +0.126139 -0.986587 0.103614 0.124193 -0.989536 0.0734473 0.452953 0.0654266 -0.88913 +-0.00508037 0 -0.999987 0.538409 0.198361 -0.819004 -0.865946 0.382366 -0.322387 +-0.789659 0.0535362 -0.611205 0.93286 -0.237879 -0.270529 0.620645 -0.746713 -0.239207 +0.599293 0.724146 -0.341264 -0.732201 -0.666237 -0.141456 -0.430444 -0.901882 -0.0364397 +0.0901019 -0.95508 0.282319 0.682593 0.0374693 -0.729837 0.815201 0 -0.579178 +0.0259785 -0.929042 -0.36906 -0.71174 0.652429 -0.260312 -0.231655 -0.817109 -0.527892 +0.0174929 -0.552483 -0.833341 0.74628 -0.564577 -0.35259 0.726239 -0.0264488 -0.686933 +-0.927635 0.334059 0.167029 -0.0436685 -0.375549 -0.925773 0.219376 -0.383615 -0.897058 +-0.425108 0.242417 -0.872076 0.329003 0.310726 -0.891743 0.109489 0 -0.993988 +0.397227 -0.687096 -0.608366 -0.546354 -0.714462 -0.437083 0.583549 0.12799 -0.801928 +0.655027 0.154953 -0.739547 0.817504 0.099876 -0.567197 -0.278014 0.636506 -0.719422 +0.424149 0.678248 -0.600064 -0.635402 -0.394892 -0.66357 -0.390559 -0.386314 -0.835599 +0.739463 0.225398 -0.634343 0.722494 -0.125552 -0.679882 0.804525 -0.496023 -0.326649 +0.545706 -0.36951 -0.752108 0 0.132589 -0.991171 0.605665 0.223876 -0.763577 +-0.284537 -0.785047 -0.550218 0.294689 -0.678677 -0.672723 0.149357 0.460874 -0.874807 +0.687954 0 -0.725754 -0.299843 -0.465393 -0.832768 0.598635 -0.603424 -0.526798 +0.731639 -0.529377 -0.429494 0.602229 -0.356157 -0.714473 0.333962 0.305576 -0.89168 +-0.242339 0.314525 -0.917794 -0.497679 -0.130162 -0.857539 -0.294744 -0.126319 -0.94719 +-0.428243 0.817554 0.384986 0.622071 -0.429014 -0.654962 0.326843 -0.362795 -0.87267 +-0.3676 -0.197415 -0.908789 0.822263 -0.369475 -0.432865 -0.35897 0.166963 -0.918294 +0.725959 -0.436415 0.531531 0.798561 -0.293901 0.525284 0 -0.676436 0.736502 +-0.604206 0.609241 0.513575 -0.231295 -0.762899 -0.603728 0.449134 -0.691834 -0.56537 +-0.0518748 0.10375 -0.99325 0.602678 0.0827511 -0.793682 -0.530196 0.151803 -0.834175 +0.165497 -0.803841 0.571358 -0.444921 -0.289552 0.847469 0.783503 0.383416 0.488995 +0.483077 -0.634039 0.603847 0.163302 0.969816 0.181077 -0.1954 0.98043 0.0239965 +-0.389313 0.908397 -0.152481 0.299667 -0.466149 -0.832409 0.137031 -0.236868 -0.961829 +0.601839 -0.0947777 -0.792974 -0.161495 -0.77245 -0.614199 -0.183883 -0.348838 -0.918966 +-0.280702 0.573879 -0.769331 -0.487241 0.648067 -0.585325 0.643089 -0.749384 0.157672 +0.565406 -0.824767 -0.00864536 -0.911609 0.104184 -0.397635 -0.891602 -0.253777 -0.375025 +0.517288 -0.660717 -0.543936 0.525293 -0.233464 -0.818267 0.676675 0.0424282 -0.735059 +-0.306654 -0.403031 -0.862282 0 -0.278157 -0.960536 0.10493 -0.314791 -0.943343 +-0.172439 -0.623969 -0.762186 0.668872 -0.536069 -0.515015 0.726475 0.152053 -0.670159 +-0.84123 -0.387913 -0.376637 0.59115 0.698632 -0.403057 0.823398 0.385968 -0.415987 +0.106888 0.956368 -0.271909 0.47685 0.541508 -0.692375 -0.136187 -0.00567446 -0.990667 +-0.661598 -0.497522 -0.561035 0 -0.285738 -0.958308 0.843392 -0.526448 -0.107438 +0.274146 -0.609214 -0.744112 -0.468236 -0.717962 -0.515059 0.751858 0.273403 -0.599967 +0.954202 -0.148505 -0.2597 -0.734667 -0.0192657 -0.678154 0.155893 -0.568552 -0.807741 +-0.430524 -0.527604 -0.732314 -0.704419 -0.351344 -0.616727 -0.941018 -0.27882 -0.191689 +0.562007 -0.589422 -0.580284 0.556402 -0.525144 -0.643926 0.979045 -0.0559454 -0.195809 +-0.916496 -0.212826 -0.338733 -0.910914 -0.179862 -0.371328 0.54976 -0.326371 -0.768925 +0.823957 -0.324067 -0.46484 0.0953891 -0.317964 -0.943292 -0.738019 0.546491 -0.395823 +0.329136 -0.738063 -0.58901 0.495584 -0.650275 -0.575794 -0.760372 -0.230097 -0.607363 +0.972335 -0.152681 0.176788 -0.247391 0.965624 -0.0798036 0.227384 -0.968006 0.106113 +0.901801 -0.332075 0.276552 -0.0711611 -0.947658 0.311257 0.140252 -0.918653 0.369331 +-0.621543 -0.779547 0.0774018 0.91459 0.250573 -0.317392 0.804573 -0.378622 -0.457502 +0.671444 -0.400009 -0.623824 0.293508 0.905397 -0.306774 -0.14704 0.974141 -0.171547 +-0.769258 -0.415815 0.485118 -0.813039 0.492103 0.31113 -0.745524 0.635176 0.201857 +0.0947376 -0.720748 -0.686692 0.334992 -0.937977 -0.0893311 -0.0170181 -0.991306 0.130472 +0.186891 0.409954 -0.892754 0.298121 0.95278 -0.0577442 -0.564954 0.695328 -0.444237 +-0.285975 -0.222425 -0.932065 0.833103 0.484642 0.266575 0.559668 0.822328 0.102707 +0.82239 0.216711 0.526033 -0.859304 -0.394815 -0.325142 0.870448 0.387098 0.304099 +0.326204 -0.820714 0.469062 -0.272914 -0.94649 0.172265 -0.307926 -0.835799 0.454557 +-0.377904 -0.920267 0.101474 0.379991 0.923864 0.0456229 0.27047 0.959923 0.0734368 +-0.907951 0.36318 0.209104 -0.54294 -0.827151 -0.145045 -0.567032 -0.808855 -0.155656 +0.860864 0.479826 -0.16935 -0.656845 0.749181 -0.0853409 0.339534 0.881557 0.327985 +0.0530097 0.689127 0.722699 -0.67747 0.164435 0.716935 -0.790441 -0.234205 0.565995 +0.0322058 0.955438 -0.29343 -0.590012 0.590012 -0.551156 0.884118 -0.463081 -0.0623758 +-0.67414 0.735736 -0.0650186 -0.91007 0.201826 0.361991 -0.414751 0.582894 0.698725 +0.775483 -0.178623 0.605574 0.1426 0.901463 0.408692 -0.895211 0.0354038 0.444234 +0.494864 0.345619 0.797281 0.151667 0.933473 0.325 0.494524 0.450187 0.743491 +-0.11886 0.984361 -0.13002 -0.742923 0.309996 -0.593269 -0.826573 -0.275524 -0.490778 +-0.713162 -0.665618 -0.219892 0.107999 -0.862893 0.493712 -0.668426 -0.42153 0.612796 +0.732081 0.0170251 0.681005 -0.761813 0.627981 0.158997 -0.60463 0.763136 0.228136 +0.814006 -0.0307172 0.580043 0.886447 0.0448204 0.460654 0.430911 -0.899216 0.0756766 +-0.834017 -0.476581 -0.278006 -0.764471 -0.420901 0.488289 0.924827 -0.288031 0.248461 +0.450952 0.742645 0.495097 0.305142 0.933735 0.187154 -0.0974951 0.822038 0.561025 +-0.171133 0.677401 0.715431 0.820962 -0.56521 0.0809879 0.790612 -0.611033 0.0396472 +-0.853855 0.492772 -0.167656 0.646857 0.757603 -0.0872544 -0.877944 0.231183 0.419248 +0.781429 0.603208 0.159713 0.123034 0.954677 0.271024 -0.602238 0.663367 0.444132 +0.34682 0.829648 0.437492 -0.613503 0.560385 0.556402 -0.930951 -0.131847 0.340508 +0.912091 -0.165588 0.375061 -0.143247 0.962328 0.231093 0.527443 0.704356 0.475065 +-0.73662 0.673403 -0.0626066 0.625738 -0.779689 0.0231755 -0.976936 0.0585186 -0.205357 +-0.89319 -0.443772 0.0726446 -0.912663 -0.0641022 0.403654 -0.90457 -0.120245 0.409015 +-0.965814 -0.207253 0.155725 0.858966 -0.460974 0.222893 0.701885 -0.712257 0.00691512 +-0.0340011 -0.827361 -0.560641 -0.77167 -0.338782 -0.538287 -0.920225 -0.319336 -0.226298 +-0.732257 -0.679953 -0.0382439 -0.334789 -0.932627 -0.134624 0.981843 0.0730674 -0.175057 +0.983404 0.0459665 -0.175508 0.718093 0.695021 0.0358886 0.456378 0.889457 0.0242122 +-0.1093 0.976869 0.183793 0.556377 -0.828232 0.0669081 0.797056 -0.599337 -0.0741447 +-0.275749 0.624835 -0.730441 0.548589 -0.822883 -0.148032 -0.300049 -0.862642 -0.40721 +-0.0534147 -0.986391 -0.155496 0.823127 0.469235 0.319814 0.476461 0.682057 0.554783 +0.120242 0.901814 0.415057 -0.969943 -0.0365535 0.240573 0.530243 0.845 -0.0694078 +0.963615 0.235028 -0.127307 -0.222518 -0.971662 -0.0797357 0.242703 0.955643 -0.166858 +0.707656 0.647572 0.282617 0.86375 0.462908 0.199128 0.730136 0.380012 0.567884 +-0.908267 0.17841 0.378445 0.897266 0.327638 0.295917 0.62797 0.65202 0.424882 +-0.969913 -0.13204 -0.204533 0.309728 0.939509 -0.14626 -0.584816 0.811083 -0.0116035 +-0.992025 0.114754 0.0521341 -0.201264 0.967781 -0.151305 -0.487205 0.867834 -0.097441 +0.836936 0.531264 -0.131514 0.742367 0.667217 -0.060933 -0.217208 -0.87413 0.434416 +0.306422 -0.94591 0.106581 0.252596 -0.8055 0.536064 0.24317 -0.940582 0.23701 +0.939034 0.195763 -0.282652 -0.749052 0.542011 -0.380979 0.921365 0.165175 -0.351857 +0.740634 -0.384329 -0.551138 -0.363888 -0.907528 -0.209711 0.17328 -0.958524 -0.226288 +-0.30323 -0.952143 0.0384092 0.880712 -0.439715 -0.176057 0.661341 -0.740603 -0.118893 +0.101353 -0.994637 0.0206142 0.161383 -0.981749 -0.100616 -0.2495 -0.842061 -0.478208 +-0.145196 -0.923357 -0.355428 -0.868831 -0.186722 -0.45855 0.810466 -0.25963 -0.525107 +-0.265396 -0.940805 -0.210834 0.648096 -0.363566 -0.669172 0.960941 -0.0105021 -0.276555 +-0.946306 -0.226291 0.230862 -0.652514 0.00526221 0.757758 -0.0437809 -0.718007 0.694657 +-0.938791 -0.336283 0.0747297 -0.2118 -0.969823 -0.120763 -0.275402 -0.950996 -0.14057 +-0.440281 -0.522193 0.730388 -0.718678 0.327717 0.613272 0.672565 -0.325607 0.664558 +-0.907898 0.41903 -0.0116397 -0.970987 -0.18132 -0.15591 0.48366 -0.870587 0.0902831 +0.525126 -0.0145868 0.850899 0.749726 -0.646576 0.140888 0.598312 0.444461 0.666691 +-0.655326 -0.6746 -0.339799 0.823216 0.128127 0.553081 -0.154807 0.877656 0.453601 +-0.895835 0.440452 0.0590113 -0.84036 0.503562 -0.200553 -0.708394 0.702903 -0.0640667 +0.921436 -0.36737 0.126472 0.864675 -0.498321 0.063357 -0.975243 -0.019477 -0.220276 +-0.503076 -0.416689 -0.757155 0.674162 -0.732531 -0.0943632 -0.904376 0.222616 -0.364069 +0.868548 -0.388027 -0.308316 -0.747161 0.633011 -0.202603 -0.053242 0.298155 -0.953031 +0.605262 -0.671463 -0.427545 -0.961025 0.187517 -0.203144 0.899104 0.422028 0.116211 +0.801056 0.585719 -0.12346 -0.403295 0.88985 0.213356 -0.565497 0.759382 0.321795 +0.987215 0.0881084 0.13283 -0.949529 0.170702 0.263165 -0.934238 -0.356579 0.00713159 +-0.299684 0.789167 0.536101 0.361516 0.893748 0.265558 -0.195447 0.951177 0.23888 +0.833412 -0.507814 0.218061 -0.258731 -0.668066 0.697672 0.698858 -0.624862 0.348059 +-0.148604 0.652564 0.743019 0.14157 -0.37752 0.915116 -0.37459 0 0.927191 +-0.768366 0.555973 0.317029 0.919919 0.215046 0.327879 -0.990085 0.119177 -0.0743582 +0.950314 -0.266088 0.161553 -0.239335 -0.73077 0.639291 0.260221 -0.785937 0.560881 +0.491809 -0.412485 0.766799 0.378423 0.6775 0.630706 0.380476 0.88647 0.263457 +0.756507 -0.65393 0.0085481 -0.326836 -0.884379 -0.333244 0.708466 0.655496 0.261536 +0.492276 0.868449 0.0588254 0.345989 0.851543 0.393911 0.407102 -0.693581 0.594318 +-0.576904 -0.457104 0.676932 0.847669 0.337882 0.409015 0.742929 0.611445 0.272381 +0.700824 -0.712007 -0.0434908 0.475483 0.832095 0.285541 0.658145 0.658145 0.365636 +0.832426 0.0582116 0.55107 0.788202 0.0297435 0.614698 0.651707 0.465797 0.59859 +-0.0346193 0.979726 0.19733 -0.147418 0.967433 0.205771 -0.718491 -0.694857 0.030725 +-0.938765 0.235545 -0.251475 0.691282 -0.628592 0.356372 0.780483 -0.537356 0.319523 +-0.572271 -0.45088 0.684991 -0.841616 0.255035 0.476066 0.463893 0.884295 0.0531544 +0.771709 0.6288 0.0952727 -0.100257 0.989198 0.10694 -0.166482 0.965594 0.199778 +0.387115 0.883912 0.262378 -0.480241 0.865153 -0.144492 -0.626087 0.779017 -0.0338703 +-0.202675 0.97863 -0.0347442 -0.282004 0.952066 -0.118506 -0.425091 0.902906 -0.0637087 +0.5488 0.832523 0.0756593 -0.222703 0.960615 -0.166196 0.404422 0.913358 0.0471237 +-0.440282 -0.87416 0.204931 0.172043 -0.927122 0.332936 0.582501 -0.701441 0.410699 +0.338416 -0.0273719 0.940598 0.223578 -0.91511 0.33554 0.337478 0.826822 0.449971 +0.951425 -0.214495 0.220867 -0.720844 -0.503891 0.475897 -0.772683 -0.576238 0.266292 +0.157717 -0.97259 -0.17086 -0.847561 0.530647 -0.0073701 0.441633 -0.883266 -0.157482 +-0.707635 0.70381 0.0624759 0.566219 -0.724761 0.392579 0.507093 0.169031 0.845154 +-0.379071 0.324918 0.866449 -0.934703 0.354203 0.0295169 0.8 0.6 0 +-0.968274 -0.0305771 0.248014 0.323587 -0.846305 0.423153 0.923508 -0.382918 0.0225246 +-0.945638 0.3199 0.0585897 -0.92389 -0.148236 0.352779 -0.821489 0.410744 0.395532 +0.567814 0.786203 -0.243869 -0.797812 -0.602791 0.0118194 0.663011 0.279608 -0.694432 +-0.630759 -0.608627 0.481369 -0.875577 -0.430516 0.219137 0.123613 -0.988903 0.0824086 +0.571771 0.177228 -0.801042 -0.77956 -0.625426 -0.0335932 -0.87093 -0.45754 0.17927 +0.354707 -0.36259 -0.861807 0.030052 -0.957909 -0.285494 -0.839178 0.342522 -0.422444 +0.566257 -0.452559 -0.688871 -0.369468 -0.385142 -0.845671 -0.461181 -0.0272888 -0.886886 +-0.407831 0.480728 -0.776257 -0.387238 -0.741126 -0.548433 0.35976 0.299397 -0.883705 +0.0221855 0.192274 -0.98109 -0.167261 0.957948 0.233151 0.0790021 0.974359 0.210672 +0.0416105 0.970912 0.235793 -0.619476 -0.77877 -0.0988212 0.717143 -0.250568 -0.650325 +0.757263 -0.028576 -0.652485 -0.912356 -0.36374 0.187882 -0.0381181 0.0889423 -0.995307 +-0.311458 -0.934374 -0.173032 0.817402 0.19659 -0.541486 -0.223864 0.466699 -0.855615 +0.809424 -0.57816 0.102784 0.748834 -0.157649 -0.643735 -0.487377 -0.433224 -0.758143 +-0.9674 -0.107489 -0.22931 0.539375 -0.353275 0.764377 0.524331 0.635553 0.566701 +-0.666895 -0.611321 0.426072 -0.990641 0.130815 -0.0389483 0.391348 -0.535529 0.748368 +-0.157112 -0.843336 -0.513908 -0.912871 -0.365148 0.182574 -0.749074 -0.0995502 -0.654964 +0.322788 0.0370075 -0.945748 -0.263274 0.614305 -0.743852 -0.453511 0.717401 -0.528833 +0.839846 0.311148 -0.444798 -0.965111 -0.161247 -0.206301 -0.735422 0.0232851 -0.677209 +-0.862863 0.21461 -0.457613 -0.0959334 0.419003 -0.902903 0.319076 -0.0299134 -0.947257 +-0.730122 0.540831 -0.417642 -0.655516 0.460633 -0.598429 -0.76833 -0.18568 -0.61253 +-0.851079 -0.499786 -0.160866 -0.676383 -0.697941 -0.235342 0.28543 0.924404 -0.252995 +-0.865371 -0.397553 -0.305099 -0.85425 -0.470042 -0.222078 -0.643708 -0.743483 -0.181311 +-0.320134 -0.147363 -0.935841 -0.795769 -0.51946 -0.311308 -0.632008 -0.0574552 -0.772829 +-0.183694 0.671357 -0.718009 -0.592225 -0.744157 0.309032 -0.0817317 -0.687508 0.721563 +0.143158 -0.823156 0.549472 -0.133221 -0.717342 -0.683866 -0.336195 -0.770448 -0.541648 +-0.0824786 -0.412393 -0.907265 -0.813482 -0.171921 -0.5556 -0.777154 -0.16239 -0.607997 +0.107518 -0.295676 0.949219 0.478444 -0.722357 0.499291 0.411476 -0.257172 -0.874386 +-0.564649 -0.0996439 -0.819294 0.651038 0.421969 -0.630945 -0.645161 -0.415147 0.641421 +-0.109675 -0.250687 0.961836 0.281709 -0.888291 0.362738 -0.155999 -0.189428 -0.969423 +0.293346 -0.258835 -0.9203 0.263598 -0.263598 -0.927918 0.0433038 0.158781 -0.986364 +0.810293 0.23402 -0.53727 -0.0708804 -0.432877 -0.898662 0.142177 -0.277062 -0.950275 +0 0.253012 0.967463 -0.379967 -0.287542 0.879173 -0.660942 -0.710266 -0.242236 +-0.00861109 -0.999963 -0.000717591 0.461757 -0.799194 -0.384797 0.388164 -0.659411 -0.643822 +-0.75955 -0.379775 -0.528068 -0.502656 -0.677047 -0.537535 0.175132 0.916075 0.360742 +0.283368 0.902466 0.324436 0.715145 -0.552612 -0.428004 0.236102 -0.681105 0.693074 +0.19549 0 0.980706 0 -0.706566 0.707647 0.618815 -0.374817 0.690348 +-0.866634 -0.312135 -0.389251 -0.873185 -0.478844 0.0908622 -0.31719 -0.261072 -0.911719 +0.619827 0.216219 -0.754363 0.388103 0.388103 -0.835914 -0.85118 -0.275382 -0.446831 +0.133732 -0.105788 -0.985355 0.441379 0.891117 0.105329 0.533347 -0.478644 -0.697453 +0.399762 -0.261913 0.878403 0.647459 0.00130273 0.762099 -0.587672 0.402092 0.702114 +-0.626249 0.0447321 0.778338 0 -0.501806 0.86498 -0.39884 0.16758 -0.901579 +0.835364 0.271903 -0.477741 0 0.423036 -0.906113 0.360268 0.838057 -0.409717 +0.40156 -0.558507 0.725824 -0.179132 -0.569009 0.802584 0.00613572 -0.654477 0.756057 +0 -0.652141 0.758097 0 -0.724005 0.689795 0.311837 -0.623673 0.716791 +0.39968 -0.325454 0.856934 0.150796 0.552918 -0.819477 -0.138478 -0.553913 -0.820978 +-0.31691 -0.636168 -0.703462 -0.715076 0.667754 -0.206811 0.343255 -0.899886 0.269038 +-0.058261 -0.802942 0.593203 -0.552275 -0.591443 0.587526 -0.359813 -0.768692 0.528817 +-0.0479092 -0.910276 0.411221 0.0980957 -0.972334 0.212002 -0.500128 -0.823847 -0.266735 +-0.434547 -0.880381 -0.189997 -0.73962 0.325635 -0.589003 -0.274968 0 -0.961453 +-0.0337316 -0.464146 -0.885116 0.837203 0.141188 -0.528353 -0.00184239 0.182397 -0.983223 +0.963078 0.266531 0.0379815 -0.880658 -0.470216 -0.0577807 -0.345702 -0.888949 0.300432 +0.496918 0.175992 -0.849764 -0.263281 -0.623355 0.736282 0.405375 -0.585225 0.70227 +-0.138413 -0.989816 -0.0332621 -0.156821 -0.374803 -0.913745 0.704014 -0.179153 -0.687218 +-0.160456 -0.677765 -0.717558 0.996562 0.0683127 0.0468813 0.801537 -0.0716319 -0.593639 +0.379186 0.315167 -0.869993 -0.1138 -0.710198 -0.694744 0.218208 -0.293741 -0.930646 +0.394224 0.194032 -0.898298 0.167283 0.683469 -0.710553 0 0.760034 -0.649884 +-0.0681252 0.346465 -0.935586 -0.30227 -0.0259089 -0.95287 -0.455913 -0.644806 -0.613489 +0.215119 0.225875 -0.950108 0.630857 -0.376284 -0.67855 0.929806 0.170964 -0.325932 +-0.779166 0.35014 -0.519905 -0.35576 0 -0.934577 0.355314 0.87052 -0.34051 +0.220674 -0.299486 -0.92823 -0.799715 -0.336722 -0.497066 0.0233819 -0.140291 -0.989834 +-0.243373 0.491565 -0.836142 -0.519122 0.849473 -0.0943858 0.491325 0.164189 -0.855361 +0.199089 -0.170648 -0.965009 -0.216983 -0.760268 -0.6123 0.334149 -0.942425 -0.0134376 +0.949603 -0.279794 -0.14131 -0.584431 -0.652191 -0.482791 -0.027351 -0.537903 -0.842563 +0.189589 0.108735 -0.975824 0.740397 0.498254 -0.451171 0.777968 0.563356 -0.278201 +0.597671 -0.605605 -0.525387 0.362821 0.571128 -0.736324 0.922932 -0.229862 -0.308805 +0.385831 -0.90522 -0.178076 -0.602383 -0.761838 -0.238197 0.582575 -0.588123 -0.560998 +0.266404 -0.133202 -0.954613 0.314997 -0.0633957 -0.946973 0.850919 0.33429 -0.4052 +-0.987785 -0.104239 -0.115821 0.622746 -0.404558 -0.669717 -0.375952 -0.621059 -0.68771 +0.813313 -0.240297 -0.529886 -0.244476 -0.700173 -0.670812 -0.277144 -0.705624 -0.65214 +0.73243 0.0415295 -0.679574 0.986537 0.0544296 0.154217 0.615961 0.733768 0.286663 +0.616041 0.773443 0.149261 0.70385 -0.0454097 -0.708896 -0.587203 -0.268107 -0.763748 +0.843237 -0.527389 0.103983 -0.866153 0.278655 -0.414886 -0.767205 0.639724 0.0463568 +-0.694629 0.7113 0.107436 -0.313249 -0.865523 -0.390827 -0.638247 -0.707328 -0.303856 +0.512049 -0.753581 0.412215 0.0742819 -0.909603 0.408784 -0.433559 0.872504 -0.225307 +0.736309 -0.412988 -0.535994 0.183294 -0.843305 -0.505213 -0.0189262 -0.480949 -0.876544 +0.1776 -0.350125 -0.919712 -0.0818006 -0.96252 -0.258581 -0.219074 -0.955087 -0.199537 +-0.527875 -0.842354 -0.10857 0.247274 -0.931592 -0.266443 -0.542872 -0.67582 -0.498556 +0.294744 -0.126319 -0.94719 -0.234806 -0.712455 -0.661267 0.820648 0.562414 -0.101129 +0.191893 0.965237 -0.177465 0.796596 0.411286 0.443034 -0.642803 0.653822 0.39915 +-0.732479 0.644874 0.218203 0.916885 0.216487 0.335342 0.678735 0.709587 0.189223 +-0.923451 0.383319 0.0174236 0.490758 -0.457144 -0.741739 -0.236099 -0.607113 -0.75873 +-0.399497 0.916087 0.0344394 0.949939 -0.0436419 -0.309373 -0.68483 -0.249029 0.68483 +0.352752 0.647048 -0.67594 0.478836 -0.85027 0.218534 -0.74563 0.664583 0.0486281 +0.563337 0.826227 0 0.720216 -0.330099 0.610183 -0.187108 0.982319 -0.00623695 +-0.315064 0.933941 -0.168785 0.245169 -0.964484 0.0982987 0.119944 -0.420374 -0.899388 +0 -0.589777 -0.807566 0 -0.699193 -0.714933 -0.378538 -0.614196 -0.692439 +0.965134 0.23815 0.10863 0.971272 0.129503 -0.19965 -0.111979 0.993081 -0.0353619 +-0.886187 -0.158973 0.435201 -0.937662 0.317595 0.141153 -0.866843 -0.298306 0.399496 +-0.877954 0.45548 0.147426 0.90494 -0.397534 0.151822 -0.999671 0 -0.0256326 +-0.271329 -0.407547 -0.871944 0.245978 -0.70176 -0.668601 0.155893 -0.568552 -0.807741 +-0.557396 -0.405064 -0.72473 0.390873 0.870007 -0.30051 0.811941 -0.582687 -0.0350249 +-0.298246 -0.898246 0.322807 0.649004 -0.7604 0.0242166 -0.68699 -0.610088 0.394763 +0.452039 -0.880077 0.145346 -0.802921 -0.00613586 0.596055 -0.964678 0.15152 -0.215495 +0.0744809 -0.9757 0.206064 -0.356966 -0.474504 0.804625 -0.188334 -0.867437 -0.460525 +0.719104 -0.563221 0.407028 -0.982481 -0.104459 -0.154336 0.242026 -0.892469 0.380686 +-0.564821 -0.800404 0.200825 0.627277 -0.466811 -0.623387 0.637568 0 -0.770394 +-0.142326 0.906669 -0.397107 0.159465 0.955366 -0.248689 -0.76274 -0.290672 -0.577701 +0.74587 0.617038 -0.250883 -0.81045 -0.479654 -0.336309 0.402746 -0.684668 0.607475 +0.658069 -0.731188 -0.17975 0.159197 0.987019 -0.0212262 -0.608463 0.571842 0.550245 +0.81123 0.504278 0.295989 -0.931872 -0.337191 0.133854 0.361893 0.932219 0.000763488 +-0.760264 0.410953 -0.503107 -0.299494 0.861957 0.409064 0.258938 0.490132 0.832299 +-0.601025 0.758216 0.252739 -0.60909 0.673264 0.419195 0.330928 0.931923 0.148347 +0.370937 0.928658 4.67317e-018 -0.737688 -0.436198 0.515313 -0.717518 -0.0326145 0.695776 +-0.411705 0.799192 -0.437941 -0.832317 -0.392603 -0.391294 0.807314 -0.438668 0.394733 +-0.167761 -0.0524254 0.984433 0.883076 0.0169618 0.468924 0.398877 0.656771 0.63996 +-0.550062 0.741453 0.38429 0.269297 0.96283 -0.0209044 -0.780604 -0.483975 0.395506 +-0.406482 -0.269789 0.872918 0.439425 -0.0298928 0.897782 0.20025 0.810206 0.550878 +0.71028 0.325003 0.6244 0.163913 0.980285 0.11034 0.706998 -0.255723 0.659363 +-0.681199 0.618602 0.391536 -0.0740353 0.329794 0.941146 -0.43931 0.761009 0.47736 +-0.435495 0.899894 0.0231236 0.805272 0.368949 -0.464127 -0.0156035 0.915408 -0.402225 +0.781356 -0.612276 0.120833 0.814023 -0.260067 0.519357 0.473539 0.796306 0.376374 +0.298136 0.951485 0.076104 -0.445801 -0.655864 0.609183 -0.273077 -0.109231 0.955771 +0.314567 -0.56075 0.765903 -0.202444 0.91642 0.345239 -0.145551 0.836917 0.527622 +0.345219 -0.455689 0.82047 0.109863 -0.576781 0.809477 -0.291199 0 0.956663 +-0.689988 0.605675 0.396327 -0.779207 0.611971 -0.135381 -0.947068 0 -0.321033 +0.905529 -0.405661 -0.124326 -0.957779 0.191881 -0.214105 0.570971 0.401648 0.71601 +-0.609188 -0.793025 -0.00120155 0.22393 -0.453529 0.862651 -0.298246 -0.561404 0.77193 +-0.016947 -0.995634 0.091796 0.114627 -0.984316 -0.134102 0.76324 0.626272 0.158896 +-0.780768 -0.0803318 0.619635 -0.0273474 -0.683685 0.729264 0.852852 0.497026 0.160027 +0.764328 0.60805 0.214659 -0.785482 0.607238 -0.119501 -0.798365 -0.10563 -0.592837 +0.424906 -0.465741 0.776235 0.275393 -0.768981 0.576912 -0.912551 0.323436 -0.250278 +-0.691563 -0.720303 -0.053888 0.814957 0.568197 0.114 0.938593 -0.152877 0.309309 +-0.0306653 0.967137 0.252399 0 0.999012 0.0444406 -0.122325 0.960364 0.250475 +-0.575306 -0.815582 0.0620429 0.542611 -0.663519 0.515088 -0.614936 0.786501 0.0571884 +0.309303 0.662791 0.681938 0.453619 -0.857548 -0.242571 0.325035 -0.787823 -0.523152 +-0.36419 -0.582704 -0.726513 -0.753345 0.120135 -0.646559 -0.0994293 -0.867548 0.487313 +0.192513 -0.941751 -0.275761 -0.768165 0.584621 -0.26104 -0.515661 0.515661 -0.684242 +-0.632944 0.637947 0.438641 -0.568495 0.59692 0.566127 -0.648077 0.761306 0.0202524 +0.159836 -0.82674 -0.539402 -0.908765 0.024747 -0.416574 0.145153 -0.79507 -0.588892 +-0.802954 -0.370481 -0.466914 -0.734038 -0.250939 0.631045 -0.621609 0.359879 0.695766 +-0.632406 -0.213986 0.744494 0.182029 0.923914 0.336525 0.799461 0.572663 -0.181438 +0.862405 -0.311735 0.398847 -0.388229 0.915205 0.108064 -0.31451 0.904216 -0.288925 +-0.371498 -0.894791 0.247665 0.204669 0.96003 0.190926 -0.483809 0.743171 0.462196 +0.469073 0.835769 -0.285415 0.993743 -0.106441 -0.0338403 0.553575 0.609784 0.567202 +0.544186 0.493953 0.678139 -0.369926 0.908427 -0.194719 -0.441629 0.612082 -0.655987 +0.949654 0.260179 -0.174537 0.626501 -0.750153 -0.211582 -0.0895616 -0.993198 -0.0744118 +-0.689787 0.338757 0.639874 -0.765414 -0.634917 -0.104983 0.668823 0.412376 -0.618564 +-0.238575 0.865341 -0.440758 0.758853 -0.457257 0.463743 0.778364 -0.333329 0.532016 +-0.474954 -0.633272 -0.611052 -0.785467 0.499843 -0.364964 0.0092984 -0.920542 -0.390533 +-0.527143 -0.811955 0.250699 0.858022 -0.269183 -0.437423 -0.486986 0.872854 0.031162 +-0.660371 -0.609574 -0.438554 -0.678026 -0.620321 -0.394313 -0.5705 0.801753 0.178105 +0.977199 0.068692 -0.200906 0.903148 -0.401399 -0.152326 -0.701963 0.447823 0.553808 +-0.567391 0.769922 0.292039 -0.532566 0.790644 0.302086 0.649099 -0.74896 -0.133149 +-0.538312 -0.820533 -0.192213 0.472901 -0.858963 0.196334 0.599272 -0.414755 0.684727 +-0.0792451 -0.871696 -0.483598 0.136622 -0.914773 -0.380165 -0.835916 0.0272952 -0.548179 +0.789339 0.592004 0.162715 0.638063 0.749724 0.175467 0.172341 0.982346 -0.0727664 +-0.449201 -0.711235 0.540705 0.119806 -0.907281 -0.403097 0.505122 -0.842952 -0.185157 +-0.675275 -0.369933 -0.638086 -0.253837 -0.930737 0.263239 0.313045 -0.922659 0.225173 +-0.515346 -0.804469 -0.295377 -0.0862739 0.901083 0.424979 0.724233 0.485998 0.489175 +0.257861 0.759646 0.597031 0.932992 0.115541 0.340845 0.215433 -0.0615522 0.974577 +-0.360523 0.733178 0.576605 -0.246761 0.856111 0.454074 -0.418989 0.80354 0.422815 +0.40057 0.35215 0.845893 -0.927404 0.345937 0.1423 -0.343669 -0.862136 0.372308 +-0.351982 -0.854814 0.381314 -0.286761 -0.742204 0.605724 -0.469965 -0.724208 0.504634 +-0.533947 -0.158207 0.830585 -0.0396385 -0.87771 0.47755 0.486597 0.71118 0.507392 +-0.78616 0.54793 0.285876 0.749396 0.659043 -0.0637784 -0.843981 0.279303 0.457915 +0.0431624 0.156464 0.98674 0.917384 0.352563 -0.184676 0.0663639 0.859781 0.506332 +0.982786 -0.133659 0.127544 0.330211 0.749706 0.573499 0.407645 0.733761 0.543526 +-0.325328 0.325328 0.887875 0.691598 0.296399 -0.658665 -0.0286735 0.93189 0.361605 +0.811029 0.575569 0.104649 -0.835007 0.544827 0.0769864 -0.605674 0.795532 -0.0169357 +0.526432 -0.723844 -0.446005 -0.858721 0.511499 0.0311131 0.95516 -0.294414 -0.0314641 +0.654318 -0.752291 -0.0769786 -0.184002 0.950676 0.249717 -0.766319 0.484996 0.421348 +0.814556 -0.30927 0.490766 0.747596 -0.142399 0.648708 0.150572 0.983936 0.0959089 +-0.707383 0.697349 -0.115389 -0.0524396 -0.830294 0.554853 -0.422703 -0.329105 0.8444 +-0.0389351 0.49591 0.867501 -0.367122 -0.453782 0.811975 -0.39595 -0.444575 0.803478 +0.325111 -0.0237886 0.945377 -0.222089 0.888354 0.401875 -0.013383 0.843126 0.537549 +0.059459 0.959272 0.276154 0.465452 -0.717806 0.517792 0.500189 -0.180068 0.846987 +-0.137309 0.52635 0.839108 -0.337721 0.621407 0.706963 -0.0715918 0.8342 0.546795 +0.437739 0.835922 0.331087 -0.313847 0.936785 0.154704 -0.316154 0.839023 0.442818 +0.826227 0.338002 0.450669 -0.363858 0.909645 -0.200385 0.509167 0.737621 0.443468 +-0.889945 -0.0778334 -0.449378 -0.639379 0.715837 -0.280664 0.0616832 0.953938 -0.293593 +0.916946 -0.16374 -0.363867 0.805817 0.351409 -0.476624 -0.421712 0.822072 -0.382566 +0.659112 0.483905 -0.57568 0.0866908 0.99413 0.0647366 -0.620591 0.663146 -0.418456 +0.596674 0.769434 0.227927 -0.306038 -0.889423 -0.339511 -0.198727 -0.817677 -0.540288 +-0.16199 0.241365 -0.956819 -0.0259789 0.777923 -0.627823 0.128085 0.577638 -0.806182 +0.241196 -0.952135 0.187785 0.615898 -0.784377 0.0736399 0.490558 -0.865848 -0.0982902 +0.397915 -0.862814 0.311793 -0.076443 0.0955537 -0.992485 0.344402 -0.593367 0.727532 +0.304721 0.557874 -0.77196 0.34759 -0.385168 0.854884 0.350881 -0.818723 0.454504 +0.719072 0.694651 0.0198988 -0.409496 0.834609 0.368431 0.669983 0.736982 -0.0893311 +0.771905 0.500695 -0.391748 0.851906 0.377427 -0.363049 -0.718531 0.695165 -0.0214196 +-0.673618 0.721355 0.16089 -0.340565 0.936554 -0.0829582 -0.703328 0.708697 -0.0554788 +-0.62385 0.779813 0.0519875 0.904023 -0.420476 0.0770872 -0.274518 -0.877431 -0.39339 +0.664087 -0.193795 -0.722102 -0.277649 -0.771865 -0.571957 -0.855761 0.403953 0.323256 +-0.980981 -0.161503 0.107669 -0.329698 -0.941995 -0.0627997 -0.318638 -0.88094 -0.349877 +-0.913812 0 -0.406138 -0.843795 0.281265 -0.457056 -0.809858 -0.0449921 -0.584898 +0.642086 -0.713429 -0.280615 -0.677066 -0.112844 -0.727219 -0.479839 -0.221464 -0.848945 +-0.593281 -0.741602 -0.313121 -0.810737 -0.585093 -0.0192656 0.700229 -0.0479288 -0.712308 +0.868324 0.183022 -0.460996 -0.990763 0.0176472 0.134455 -0.989301 -0.058887 0.133477 +-0.596471 -0.796965 -0.0952348 0.0651251 -0.716376 -0.694668 0.432176 -0.85327 -0.291811 +-0.16173 -0.549883 -0.819434 -0.808247 -0.153567 -0.568467 -0.739123 -0.558113 -0.377104 +-0.00482716 -0.391 -0.920378 0.751728 0.381391 -0.538001 -0.782525 0.595786 0.180811 +-0.0433646 0.520375 -0.852836 -0.72194 -0.469261 -0.508524 0.8223 0.441605 -0.358898 +-0.558705 -0.649001 0.516379 -0.122262 -0.217176 -0.968445 0.771176 0.396563 -0.498021 +-0.485481 -0.729729 0.48146 -0.739498 -0.372247 -0.560869 -0.753035 -0.637183 0.164123 +-0.384129 -0.85523 0.34789 -0.408185 -0.910361 0.0680309 -0.890682 -0.435701 -0.129811 +-0.417446 -0.834891 0.358742 0.569232 0.182244 -0.801724 0.0217289 0.999528 -0.0217289 +0.293995 0.930985 0.216413 0.428122 0.887267 -0.171662 -0.571268 0.736852 -0.361527 +0.899559 0.311847 0.30585 0.779239 0.618808 -0.0993148 -0.939353 0.139904 -0.313118 +0.797465 -0.194225 0.57125 -0.280294 -0.779216 0.560587 -0.90969 -0.414222 -0.0297458 +-0.962264 -0.226415 -0.150943 0.418821 -0.732937 0.536091 -0.672912 -0.716482 0.183962 +-0.159153 -0.925074 0.344831 -0.363915 0.166424 0.916444 -0.284834 0.575146 0.766861 +-0.922282 0.372087 0.104631 0.922211 -0.269318 0.277479 0.807698 -0.0695976 -0.585474 +-0.481251 -0.704689 0.521355 -0.756467 -0.550158 0.353673 -0.442581 -0.785225 0.433063 +-0.539279 -0.41483 0.732867 0.747241 -0.314455 -0.585448 0.842745 -0.256488 -0.473281 +-0.677798 -0.551429 0.48633 0.0859382 -0.880866 0.465499 -0.52058 -0.77123 0.366334 +-0.0380571 -0.938742 0.342514 0.713643 -0.198685 -0.671743 -0.350684 -0.769412 0.533878 +-0.138461 -0.860866 0.48963 -0.592205 -0.572016 0.56753 0.298066 0.938909 -0.172066 +0.292737 0.360292 -0.885717 -0.609509 -0.0156955 -0.792624 -0.975401 -0.220289 -0.00809461 +0.226415 -0.509434 -0.830189 -0.63785 -0.494817 0.590172 0.277409 -0.569419 0.773826 +0.681484 -0.453204 0.574618 -0.446719 0.824045 -0.348412 0.51393 -0.765361 0.387425 +-0.84761 -0.081501 0.524323 0.783492 0.61996 0.042307 -0.827831 -0.477595 -0.294276 +-0.441068 -0.866097 0.235236 -0.551845 0 0.833947 0.175069 0 0.984556 +0 -0.454739 0.890625 0.17378 -0.568735 0.803953 -0.493062 -0.571798 0.655696 +0.940981 0.209107 0.266136 0.451165 -0.892154 0.0226148 -0.892489 -0.435089 0.118999 +0 -0.195557 0.980692 0.751783 0.573536 0.325388 0.752332 0.596134 0.280393 +0.197539 0.395078 0.897157 -0.561401 -0.800219 0.210897 -0.246252 -0.969016 -0.0191884 +0.809813 0.524227 0.263417 0.910246 0.379269 0.166156 -0.759757 0.291306 0.5813 +-0.172447 -0.172447 0.969806 -0.196671 -0.763545 0.615078 0.959401 0.266693 0.0917839 +0.4073 -0.492154 0.769345 0.80829 0.11547 0.57735 -0.990492 -0.137568 0 +-0.281662 -0.537523 0.794818 -0.280577 -0.542448 0.79185 0.121636 -0.429782 0.894702 +0.132161 0.0213975 0.990997 -0.507118 -0.507118 0.696895 0.0118686 0.999929 0.00098905 +-0.0506589 -0.905106 0.422158 0.932648 0.185624 0.309373 -0.673277 -0.0968744 0.733016 +0.945484 0.297152 0.133268 0.919568 0.33984 0.197241 -0.868611 -0.38537 -0.311456 +0.368731 -0.538557 -0.757624 -0.0650865 0.0735761 -0.995163 0.337086 0.204659 -0.91896 +0.314574 -0.340888 -0.885911 -0.876715 0.23123 0.421787 -0.0176222 -0.418527 0.908033 +-0.135326 -0.582698 0.801342 0.540635 -0.138302 0.829811 -0.499233 0.0274447 0.866033 +-0.159867 -0.188933 0.968889 0.947565 0.0485338 0.315855 -0.782687 -0.538732 0.311719 +-0.904768 0.392635 0.165021 0.363733 0.544511 -0.755781 0.0826539 0.870818 -0.484608 +-0.0118038 0.755442 -0.65511 0.385896 -0.0110256 -0.922476 0.569415 -0.251897 0.782505 +0.0355019 -0.443773 0.895436 -0.180905 -0.671932 0.718179 0.787789 -0.342626 0.511855 +0.656553 -0.632645 0.410729 -0.982679 -0.0289023 0.183048 0.56552 0.557749 -0.607538 +0.489065 0.619483 -0.614049 -0.449277 -0.587709 0.672866 -0.210012 -0.926893 0.31107 +0.562404 0.778392 0.278941 0.605463 0.762982 0.226433 -0.0221856 0.321691 0.946585 +-0.27345 0.626657 0.729744 -0.143918 0.949248 0.279671 0.882628 -0.233014 -0.408256 +0.583178 -0.479771 -0.655532 -0.0129257 -0.843399 -0.537133 0.313224 -0.792495 -0.523299 +0.137234 -0.772338 -0.620211 0.129915 -0.75587 -0.641702 0.529696 -0.815144 0.23444 +-0.376512 -0.909517 0.176119 0.867327 -0.412509 0.278532 0.433694 -0.501216 0.748794 +0.882248 0.0577171 0.467234 0 -0.689184 0.724587 0.620613 -0.297554 0.725466 +0.673255 0.290017 0.68016 0.684599 0.489865 0.539775 0.648062 -0.539542 -0.537504 +0.024134 -0.877121 -0.479664 -0.377302 -0.747617 -0.546545 -0.405857 -0.789166 -0.460973 +-0.270585 -0.947047 -0.172874 -0.309803 -0.727292 -0.612428 -0.370652 -0.833377 0.409999 +0.449384 -0.755973 -0.475983 -0.0992201 -0.988232 -0.116418 0.163172 -0.979035 0.121923 +0.965393 0.0546449 0.25501 0.955532 -0.241761 0.168849 0.268769 0.761512 0.589799 +-0.717536 0.694877 -0.0478357 -0.878072 -0.435326 0.1987 -0.918316 -0.395168 0.0232011 +-0.201291 0.978272 0.0496517 0.217787 -0.745062 -0.630437 0.247182 -0.72645 -0.641226 +-0.0932922 -0.713411 -0.694508 -0.124591 -0.662302 -0.738805 -0.126111 -0.693611 -0.709225 +-0.819069 0.565132 -0.0987529 -0.93673 0.326121 -0.12721 0.80039 -0.0188981 0.599181 +0.773185 -0.036082 0.633153 -0.974482 -0.214048 0.0675941 -0.118932 -0.793781 -0.596462 +0.660087 0.660087 0.358566 -0.683426 0.729604 0.024628 0.825296 0.293417 -0.482486 +0.400152 0.639933 -0.656022 -0.330778 0.873827 -0.356389 0.728524 -0.414529 0.54536 +-0.993146 0.0522708 -0.104542 0.42005 -0.725105 0.545692 0.53976 -0.725303 0.42731 +0.480591 -0.756341 0.443825 -0.920498 0.114466 -0.373605 -0.37285 0.915915 -0.1486 +0 0.80975 -0.586775 0.279475 0.784908 -0.553003 0.0700335 0.964906 -0.253084 +-0.229426 0.964466 -0.131031 0.0737118 -0.986604 -0.145533 0.840993 -0.434513 -0.322381 +0.52657 -0.182274 -0.830361 0.312081 -0.92946 -0.196747 -0.334275 -0.315173 -0.888215 +0.27134 0.943793 -0.188759 -0.401074 0.860683 -0.313632 -0.378267 0.342242 0.860107 +0.976251 -0.205526 -0.0685088 -0.122878 0.988872 0.0838688 -0.872789 0.417421 0.252982 +0.976102 0.0377658 0.214006 0.651681 0.75762 -0.0363829 0.364101 -0.7756 0.51563 +0.742195 -0.656862 0.13296 -0.871366 -0.309023 0.381085 0.146872 -0.78273 0.604783 +0.877004 -0.372166 0.303901 0.957069 0.0118614 0.289617 0.558115 -0.796969 0.230971 +-0.82509 -0.437877 0.357057 -0.399533 -0.528 0.749392 -0.730739 0.540921 0.416443 +0 0.845299 0.534293 0.134867 0.990863 0.000917466 0.606041 0.795429 0.00252517 +0.836213 0.470829 -0.28119 -0.445512 0.860086 0.248538 0.423396 -0.729626 0.537012 +-0.305044 -0.934501 0.183457 0.806679 0.590989 0.000539224 0.342321 -0.222509 0.912856 +0.241416 0.64858 0.721846 0.200802 0.312538 0.928439 -0.535649 -0.204807 0.819228 +-0.982708 0.1121 0.147371 -0.0159597 -0.877784 0.478791 0.67601 -0.720258 -0.155687 +-0.49998 0.862466 0.0785683 -0.533904 0.782134 0.321267 -0.826654 0.296921 0.477997 +0.226547 -0.841459 0.490534 -0.495535 -0.31297 0.810244 -0.299892 -0.886638 0.352048 +0.54181 -0.725035 0.425165 0.245359 -0.500156 0.830447 0.363325 0.835104 -0.413033 +-0.590006 0.548805 -0.592204 -0.116529 0.169081 0.978689 -0.706242 0.684456 0.180948 +-0.309995 0.476063 -0.822962 0.95009 -0.19489 -0.243613 0.475945 0.655064 -0.586828 +0.728125 -0.630945 0.267849 -0.0407481 0.890951 0.452267 -0.582969 0.80907 0.0745233 +-0.0549774 0.923621 -0.379344 -0.513722 0.834798 -0.197997 -0.557588 0.811228 -0.176081 +0.765321 0.26353 0.587227 -0.675956 0.731082 -0.0927525 -0.58211 0.40905 0.702727 +-0.594237 0.801932 -0.0615391 0.723883 -0.685921 -0.0742002 -0.896904 0.294657 -0.329758 +-0.638909 0.729387 -0.244521 -0.666179 0.460368 -0.586743 -0.678242 0.413304 -0.607592 +0.960313 0.122187 0.250738 0.913422 0.404416 0.0459035 -0.4968 0.867295 0.0314359 +0.876056 0.259828 -0.40622 -0.341332 0.898593 -0.275723 -0.872526 -0.119972 -0.473609 +0.584025 0.805516 0.100298 0.222134 -0.456384 0.861609 -0.311531 -0.914709 -0.2574 +0 -0.983582 -0.180463 -0.174272 -0.894402 -0.411915 0.0946618 0.636816 0.765183 +-0.989303 -0.0594693 -0.133204 -0.20138 -0.47599 0.856084 0.892971 0.140995 0.427462 +0.222449 0.826241 0.517535 0.530197 0.745678 -0.403554 0.774297 0.526993 -0.350347 +0.494518 -0.85912 -0.131778 0.620653 -0.776792 -0.106695 -0.426685 -0.694779 -0.578983 +0.764098 0.363617 0.532858 0.361256 -0.794763 0.487695 -0.622033 -0.551448 0.55586 +0.959018 0 -0.283346 -0.194219 -0.920341 0.339488 0.208337 -0.876339 0.434311 +-0.928985 0.364538 0.0640229 -0.707637 0.435469 0.556432 0.714521 0.136193 0.68623 +0.608432 -0.0612033 0.791242 -0.74959 -0.617309 -0.23884 -0.597472 -0.792565 -0.121933 +-0.501535 -0.844691 -0.186976 0.84596 -0.484014 0.223791 -0.862354 -0.505442 -0.029558 +-0.879617 -0.473284 -0.0477132 0.92322 0.0869844 -0.374296 0.78666 -0.248249 -0.565277 +-0.44754 -0.888317 0.102961 -0.342413 -0.924516 -0.167402 -0.0276609 0.874084 -0.484987 +-0.325084 0.945244 -0.0288963 0.774788 -0.615273 0.145408 -0.424469 -0.859505 0.28474 +0.873828 0.160835 -0.458865 0.630084 -0.422443 -0.651564 0.195384 -0.784541 -0.58849 +-0.508922 0.675479 0.533597 -0.0545349 0.974688 0.216818 -0.177901 0.899011 0.400164 +-0.476704 0.727146 0.493976 -0.908257 -0.416233 0.0426587 0.751682 -0.652623 0.0951742 +-0.435742 -0.86247 0.257439 0.507963 -0.736138 -0.447297 0.0781703 -0.906776 -0.414303 +-0.335164 -0.918402 -0.210247 -0.596191 -0.122268 0.793478 -0.649051 0.102482 0.75381 +0.888894 0.165376 0.427221 -0.918033 -0.393443 -0.0491803 0.527496 0.805815 0.269091 +-0.929075 0.360048 0.0847665 -0.633245 -0.697642 0.335107 -0.17146 -0.925188 0.338568 +-0.377802 -0.763377 0.523947 -0.278163 0.960533 0.00144877 0.636431 -0.383329 0.669338 +0.354603 -0.839506 0.411687 -0.952117 0.07129 0.297306 0.0171572 0.998693 0.0481355 +0 -0.974802 -0.22307 0.271945 -0.960733 -0.055124 -0.334501 -0.836773 0.433497 +0.599162 0.800444 -0.0171635 0.212221 -0.88361 0.417367 0.677376 -0.693698 0.244835 +0.0219415 -0.727446 0.685814 -0.269743 0.351839 0.896352 -0.34553 0.8828 0.318234 +-0.0328036 0.880377 0.47314 -0.689033 0.714695 -0.120185 0.605622 -0.175543 0.776149 +0.535361 0.414935 0.735675 -0.885942 0.3648 -0.286406 -0.277827 0.845221 -0.456524 +-0.169461 0.782128 -0.599632 0.584915 -0.779887 -0.222825 0.302739 -0.930641 -0.205563 +0.185381 -0.975458 0.118806 0.976954 -0.144377 0.157211 0.631576 -0.672323 0.386125 +-0.86472 -0.0238269 -0.50169 -0.64589 -0.25329 -0.720188 -0.557171 0.78004 0.284777 +-0.708727 0.590606 0.385863 -0.848634 -0.494632 0.187508 -0.969816 0.231324 -0.0771081 +0.194631 0.915417 -0.352322 -0.327382 0.944849 -0.00897883 0.605589 -0.789899 0.0965432 +0.629575 -0.198813 0.751072 -0.554213 -0.810811 0.188235 -0.943854 0.328847 0.0316199 +0.365393 -0.917264 0.158477 0.149393 -0.953978 -0.260014 -0.0996518 -0.866971 -0.488294 +0.950321 0.10182 0.294147 -0.357663 -0.925321 0.125933 0.733973 0.53509 -0.418286 +0.137962 0.984228 -0.110729 0.0876307 0.820962 0.564219 -0.561393 0.773908 0.293093 +0 -0.914429 0.404747 0 -0.62788 0.77831 0.495251 -0.839773 0.222504 +-0.757878 0.611192 0.228178 0.891005 -0.18472 0.414716 0.665947 -0.745266 -0.0330495 +0.63056 -0.677268 -0.379081 -0.0272008 -0.994528 -0.100869 0.815211 -0.490259 0.308346 +-0.553932 0.658963 0.50885 -0.667498 -0.451267 -0.592287 0.84932 -0.116523 0.514857 +-0.91859 0.380106 0.108225 -0.871966 0.38644 0.300564 0.768589 -0.638718 0.0361996 +0.619813 0.215587 0.754555 -0.34832 0.34832 0.870257 0 0.863779 -0.503871 +-0.889261 -0.370525 -0.26819 0.879936 -0.288775 0.377255 -0.163912 0.861059 0.481363 +0.802858 -0.539062 0.254621 0.810407 -0.476505 0.340858 -0.870374 0.486587 -0.0753867 +-0.746696 0.662248 0.0622247 -0.460255 -0.887635 -0.0164377 -0.127855 -0.990877 -0.0426184 +-0.76004 0.649094 -0.0318809 0.763761 -0.609075 -0.213767 0.33931 0.0657978 0.938371 +0.995588 -0.060645 0.0715948 -0.249263 0.926948 -0.280421 -0.60159 0.767546 -0.221275 +0.749145 0.649551 0.129868 -0.317376 0.372175 0.872214 0.563123 -0.412957 0.715792 +-0.501663 0.0876942 0.860607 0.907507 0.380871 0.177113 0.947121 0.282254 0.152627 +-0.154801 -0.942629 -0.295781 -0.95041 -0.256197 -0.176308 0.892504 -0.0263146 0.450272 +-0.0551289 0.98707 0.150511 -0.670795 0.585071 0.455769 -0.92149 0.356335 0.154539 +0.67505 0.319615 0.664947 0.838591 0.268349 0.474083 -0.525761 -0.467343 0.710751 +0.819567 -0.402838 0.407469 -0.579188 -0.780295 -0.235966 0.778029 -0.602345 -0.178473 +-0.389986 -0.91729 0.0805604 -0.365704 -0.683289 -0.631962 0.744031 -0.644087 0.177679 +0 -0.993263 0.115881 0.112211 -0.94756 0.29923 0.879996 0.118177 0.460046 +0.693532 -0.704541 0.150449 -0.725772 -0.652632 0.217544 -0.815089 -0.565128 0.127516 +-0.168956 -0.92926 0.328526 0.816034 0.1515 0.557796 -0.935008 -0.189282 0.299887 +0.575748 0.663926 -0.477197 -0.710934 0.680731 -0.176572 0.946574 0.153445 0.28364 +0.446711 0.893423 0.0473785 -0.685454 0.697804 -0.2079 0.604175 -0.648929 0.462455 +0.847875 -0.529405 0.0289518 0.86957 -0.48236 -0.105723 -0.817904 -0.505664 0.274477 +0.655328 0.602811 0.455153 -0.665534 0.543604 0.511428 0.719596 0.474341 0.507131 +-0.725722 0.302971 0.617686 -0.974869 0.141911 0.171733 0.815892 -0.565685 -0.119664 +-0.642664 -0.731708 0.227126 0.92752 -0.209047 0.30985 -0.608522 0.0747307 0.790011 +-0.394443 0.661272 0.63807 -0.295373 0.945194 0.139154 -0.78874 0.613802 0.0337069 +-0.385115 0.135504 -0.912866 -0.221351 0.544863 -0.808782 -0.0461717 0.998767 0.0182257 +0.416136 0.808493 0.416136 -0.826732 0.300275 0.475761 -0.963011 -0.249362 0.10212 +0.886783 -0.446581 0.119088 0.854321 -0.0678032 0.515305 0.224021 0.950944 0.213353 +0.46955 0.812992 0.344336 0.641377 0.408994 0.649123 -0.8007 0.402122 0.444046 +0.902565 0.357773 -0.239532 -0.307914 -0.902323 -0.301667 -0.241942 -0.957545 -0.156751 +0.958194 -0.283909 -0.0354887 -0.498761 -0.546262 0.672931 0.166099 -0.861342 0.480105 +-0.0416921 -0.481354 0.875534 -0.920533 0.38377 -0.0730684 0.612958 -0.290349 -0.734833 +-0.343579 -0.343579 -0.874017 -0.511089 -0.584102 -0.630565 -0.609956 -0.43874 -0.659894 +0.603958 -0.795944 -0.0413304 -0.917812 -0.12132 0.378026 0.931316 -0.334376 -0.144373 +0.674781 0.687675 -0.267907 0.537652 0.752712 -0.379941 -0.560346 0.828218 -0.00820018 +0.586744 0.757878 -0.285223 -0.714804 -0.0238268 -0.698919 0.790435 -0.59912 0.127544 +-0.562124 0.811274 0.160782 0.154868 -0.542037 0.825961 0.668151 0.70991 0.222717 +-0.240452 0.951187 -0.193456 -0.429097 0.894287 0.126991 -0.335591 -0.929912 -0.150475 +-0.210184 0.97459 -0.0774362 -0.760155 -0.305761 -0.573301 0.479909 -0.87477 0.0668227 +-0.721273 0.656607 -0.220527 -0.357147 0.664399 0.656521 0.28204 0.378991 0.881374 +0.702879 -0.711184 -0.0133378 0.713275 0.394178 0.579536 -0.685865 0.727432 -0.0207838 +-0.568874 0.733732 -0.37151 0.371274 0.85945 0.351429 -0.715244 0.694207 -0.0806402 +-0.175969 0.907842 -0.380601 0.698151 -0.713006 -0.0648637 0.389244 0.758469 -0.522699 +0.953043 -0.0914082 -0.28871 0.636914 0.756475 0.148613 0.00965772 0.950078 -0.311864 +-0.274688 0.864996 -0.419915 -0.161561 0.98637 0.0311784 0.659398 -0.67938 -0.321928 +0.535644 -0.84238 0.0590073 -0.347294 -0.88035 -0.323064 -0.607834 -0.487768 -0.626594 +-0.154945 -0.985868 0.0636907 -0.615443 -0.553899 -0.560737 -0.464606 -0.872205 -0.152968 +0.359253 0.146086 -0.921735 0.0444477 0.112427 -0.992665 -0.355045 -0.0730133 -0.931994 +0.0674104 0.269642 -0.960598 -0.191121 0.210233 -0.958788 -0.228765 0.591634 -0.773069 +0.00978398 0.743583 -0.668572 0.437264 0.896952 -0.0654027 0.415123 0.909468 -0.0232384 +-0.538665 0.819915 -0.193856 -0.551434 0.797567 -0.244556 0.595659 -0.0374086 -0.802366 +0.414913 -0.827079 -0.379192 0.30393 -0.649204 -0.697252 0.542147 -0.715456 -0.44068 +-0.522983 0.298517 -0.798359 -0.0838339 0.854366 -0.512866 0.257119 0.817507 -0.515337 +-0.301512 0.819938 -0.486612 0.115936 0.830872 -0.544253 -0.568658 0.434602 -0.69839 +-0.129469 0 -0.991583 0 -0.286888 -0.957964 0.396823 -0.235874 -0.887071 +0.455331 -0.659765 -0.597816 -0.0958466 -0.443734 -0.891018 0.492498 0.357252 -0.79361 +-0.245426 -0.47449 -0.845355 0.115963 0.552531 -0.825386 0.419206 0.375752 -0.826484 +0.693484 -0.1517 -0.70432 -0.116862 0.629659 -0.768032 -0.205244 0.840999 -0.500595 +0.576685 0.0678454 -0.814144 -0.199622 0.829201 -0.522089 0.0382157 0.636928 -0.769975 +-0.132784 0.708183 -0.69343 -0.509512 0.445823 -0.735962 0.551977 -0.296608 -0.779324 +-0.621821 -0.74489 0.241819 0.173984 -0.951112 0.255176 0.0641335 0.689436 -0.721502 +0.384586 0.590081 -0.709858 -0.977292 0.141508 0.157723 -0.254904 -0.892165 0.372915 +-0.299088 -0.94711 -0.116312 -0.326407 -0.940819 0.0912019 0.633746 -0.660714 -0.402272 +0.768 -0.6 -0.224 0.26906 -0.858646 0.436272 -0.570063 -0.819726 -0.0554806 +-0.399285 -0.613471 -0.681341 0.514472 0.35586 -0.780181 0.833419 0.44824 -0.323255 +0.84591 0.422955 -0.324878 -0.745351 -0.643481 -0.174311 0.507703 0.58509 -0.632382 +0.641742 0.647196 -0.411466 -0.846289 -0.531477 -0.0364195 -0.0377937 0.259517 -0.964999 +0.474143 0.720697 -0.505752 0.811386 -0.298932 0.502287 0.319102 -0.78366 0.532964 +-0.326634 0.0130654 -0.945061 0.480956 0.775735 -0.408554 0.816645 0.576703 -0.0224507 +-0.831019 -0.250951 0.496417 -0.490006 -0.601371 -0.631068 0.893498 0.0845509 -0.441036 +0.763268 -0.637292 -0.106215 -0.0642451 -0.936143 -0.3457 -0.431452 -0.748863 -0.503044 +-0.796918 -0.446582 -0.4068 0.886277 -0.276366 0.371665 -0.426638 -0.781221 -0.455712 +0.607777 0.00542658 -0.794089 0.802224 0.36572 -0.471896 -0.869659 0.49359 0.00783477 +-0.71059 0.688726 -0.14394 0.885195 -0.42083 -0.198322 0.587107 -0.569098 0.575701 +0.561558 0.505402 0.65515 0.815869 0.273909 -0.509247 -0.98744 0.143628 0.0658293 +-0.773334 -0.559509 -0.298167 -0.643952 -0.13365 -0.753302 -0.0106825 -0.758459 -0.651634 +-0.0861221 0.5844 0.806882 0.89086 0.222715 0.395938 0.414513 -0.612759 0.672834 +-0.933702 0.322464 0.155617 0.403577 -0.828969 -0.387216 -0.589968 -0.543392 -0.597213 +0.0960738 0.288221 0.952732 -0.759953 -0.595639 0.260164 0.058722 -0.998274 0 +-0.823847 -0.561068 0.0804908 -0.0323125 0.972067 -0.23247 -0.555582 0.694477 -0.457198 +-0.971022 -0.0357431 -0.236302 -0.924955 -0.295986 -0.238433 -0.22434 -0.931012 0.287903 +0.933969 0.324859 0.148894 0.851205 0.206353 -0.482564 -0.753044 -0.331278 0.568489 +0.750727 -0.034124 0.65973 -0.625343 -0.606951 0.490465 -0.446065 -0.592582 0.670725 +-0.923537 -0.0157474 0.383186 -0.341077 0.420397 0.840793 0.207583 0.928019 0.309339 +0.548625 0.648375 0.527844 0.970118 0.191682 -0.148761 0.246795 0.943394 0.221585 +-0.805546 0.109847 0.582262 -0.0574128 -0.80059 0.596456 0.939336 -0.34205 -0.0254657 +-0.611717 -0.694063 0.379578 0.00944936 -0.982733 0.184787 0.837139 -0.335124 -0.432309 +0.867052 -0.144121 -0.476917 -0.62838 -0.583709 0.51422 -0.814936 -0.382599 0.435313 +-0.762198 -0.396171 0.51196 0.666667 -0.133333 -0.733333 0.0066872 -0.595161 0.803579 +-0.264305 -0.816942 0.512591 -0.624136 -0.289599 0.725663 0.322258 -0.314065 0.893036 +-0.396763 -0.523727 0.75385 0.0234246 -0.726164 0.687123 0.174694 -0.744069 0.644859 +0.699113 0.636239 -0.326253 0.710531 -0.4222 -0.562933 -0.492213 -0.118658 0.862349 +-0.155189 -0.731085 0.664403 -0.310017 -0.75782 0.574106 -0.852302 -0.171869 0.494006 +-0.0637487 -0.812796 0.57905 0.22359 0.941931 -0.250547 -0.644618 -0.738952 -0.196006 +0.506935 0.76695 -0.393452 -0.467142 0.27479 -0.840398 -0.200632 -0.72536 -0.658483 +-0.191611 -0.8191 -0.540704 0.358312 -0.879151 -0.314177 0.143058 0.839276 -0.524548 +-0.461237 -0.864036 -0.201748 -0.550213 -0.431616 -0.714824 -0.269224 -0.423066 -0.865179 +0.0698754 -0.635866 0.76863 0.172456 -0.426183 0.888047 0.66742 0.725401 -0.168358 +0.386712 0.858631 -0.336461 -0.576683 0.384455 -0.720854 -0.435259 -0.0423404 -0.899309 +-0.155511 -0.459672 0.874367 -0.433461 -0.791066 0.431655 -0.604078 0.745775 0.280909 +-0.996926 -0.0772811 -0.0128802 -0.060117 -0.865685 0.496967 0.6506 0.74819 -0.13012 +-0.50084 -0.797035 -0.337483 -0.98064 0.0492949 0.189512 0.0126828 -0.674 -0.738622 +0.6305 -0.339879 0.697819 0 -0.472408 0.88138 -0.445054 -0.376861 0.812344 +0.573308 0.806359 0.145269 -0.317861 0.946014 0.063428 0.555598 0.811046 0.183071 +0.554127 0.829097 0.0744419 -0.390133 -0.23242 0.890942 -0.908379 -0.416506 -0.0370227 +-0.768679 -0.361731 -0.527525 0.404002 -0.404002 -0.820709 0.513144 -0.372556 -0.77323 +0.542005 -0.527357 -0.654313 -0.347528 -0.538328 -0.767742 -0.286044 -0.618654 -0.731741 +-0.156417 -0.449142 0.879662 0.284843 -0.0931216 0.95404 -0.397439 0.228527 0.888717 +-0.152511 0.0538274 0.986835 -0.258021 -0.488103 0.833775 -0.840079 0.294433 0.455606 +-0.931626 0.2465 0.267042 0.473692 0.68183 0.557426 0.225875 -0.309977 0.923523 +-0.779569 -0.550896 0.297969 0.389904 -0.322095 -0.862687 -0.457239 -0.452489 -0.765628 +-0.769422 -0.129357 0.625505 0 -0.454739 0.890625 0.712958 -0.235879 0.660343 +0.590895 -0.401198 0.699916 -0.692747 0.161911 0.70277 -0.347437 -0.820281 0.454341 +0.488719 -0.57264 0.658207 -0.153549 0.472832 0.867671 -0.243055 -0.884449 0.39834 +-0.0845631 0.819611 -0.566645 0.735378 0.1329 -0.664498 0.241294 -0.487415 -0.839168 +-0.343322 -0.672138 -0.656019 -0.365424 -0.637432 -0.67834 -0.0832131 0.254534 0.963477 +0.120501 -0.271127 0.954971 0.309647 -0.465699 0.829001 0.604564 -0.310033 0.733745 +0.325999 -0.10161 0.939894 0.338341 0.931664 0.132394 0.71773 -0.118145 0.686225 +-0.242618 -0.679331 0.692565 0.516544 0.843397 -0.147862 0.484088 0.633273 -0.603842 +0.661233 0.418971 -0.622281 0.31461 0.750833 -0.58075 -0.206203 0.904889 -0.372366 +0.0224029 0.847576 -0.530201 -0.190866 0.438992 -0.877984 0.275766 -0.529172 -0.802453 +0.31833 -0.48339 -0.815475 0.0705311 -0.712364 -0.698257 -0.131773 -0.779013 -0.613004 +-0.193575 -0.71323 -0.67367 -0.489911 0.553631 0.67341 -0.276104 -0.516582 0.810499 +-0.285549 -0.095183 0.953626 -0.0892342 0 0.996011 0.539156 -0.589431 0.601566 +-0.474298 -0.380462 0.793908 -0.986091 -0.152084 0.0670476 0.420109 0.904591 -0.0722769 +0.441151 0.893024 -0.0888429 -0.43675 0.780551 -0.447202 0.415877 -0.883738 -0.214603 +-0.188738 0.924816 -0.330292 0.121147 0.987349 -0.102302 -0.779696 0.60643 0.155939 +-0.48517 -0.749809 -0.449885 -0.381799 0 0.924245 0.494724 -0.169152 0.852429 +-0.456887 -0.45452 0.764635 0.0189 -0.7686 0.63945 -0.305122 0 0.952313 +-0.883178 -0.0334176 0.467846 0.975461 0.0248701 0.218765 0.696289 0.690828 0.194779 +0.443242 0.89612 -0.0224833 -0.369419 -0.181656 -0.911335 0.451936 -0.885795 -0.105452 +-0.72774 -0.610632 0.312287 0 -0.61519 -0.788379 0.133662 -0.61917 -0.773798 +0.113717 -0.760903 -0.638824 -0.369801 -0.665642 -0.648204 -0.0342371 -0.725827 0.687025 +-0.167154 -0.297295 0.94004 0.16328 -0.569211 0.805815 0.743706 0.424975 0.516041 +0.490768 -0.208642 0.84594 0.287294 -0.721474 -0.63003 -0.742765 -0.427652 -0.515184 +-0.676961 0.132552 0.723985 -0.164613 -0.420322 0.892318 -0.572824 -0.329707 0.750444 +0.272078 -0.743681 0.610665 0.659524 0.544182 0.51855 0.339597 0.25412 0.905592 +-0.245284 -0.531449 0.810801 -0.310388 -0.843483 0.438401 -0.955175 -0.212261 -0.206365 +-0.783488 0 0.621407 -0.31772 0.926235 0.202838 -0.397139 0.917528 0.0205417 +0.400384 0.85243 0.336236 0.123878 0.916698 0.379893 -0.310369 -0.846462 0.432636 +0.845257 0.432523 0.313791 0.655033 0.74668 0.115764 -0.407329 -0.878302 0.250337 +0.991945 -0.101338 0.0760036 -0.997329 0 0.0730367 0.898404 0.431997 0.0790521 +-0.727004 0.2812 0.626412 -0.975467 -0.00950748 -0.21994 0.728168 0.102717 0.677658 +0.59963 -0.237136 0.764337 -0.766545 0.178928 0.61676 0.568873 -0.644363 0.511057 +-0.0122066 -0.772068 0.635422 -0.331944 -0.881242 0.336491 -0.0746191 -0.983867 0.162596 +-0.859657 -0.459269 0.223746 0.799719 0.517609 -0.304188 0.646401 -0.609812 0.458579 +-0.432498 -0.900382 -0.0475093 -0.364245 0.610786 -0.70304 -0.915037 -0.174293 0.36377 +0.824065 0.560746 -0.0804965 -0.489021 0.789957 -0.3699 0.975721 -0.136391 0.171363 +-0.853388 0.426694 -0.299434 0.608354 -0.01767 0.793469 0.659089 0.0063374 0.752038 +-0.948889 0.289914 0.124741 -0.314891 0.944674 0.0918433 0.831238 0.465312 -0.304185 +0.094667 0.9493 -0.299779 0.809295 -0.012302 0.587273 -0.845043 -0.423074 0.326971 +0.808137 -0.493278 -0.321855 -0.129371 0.954112 -0.270062 -0.873153 0.293771 0.388975 +-0.916029 0.373197 -0.147017 -0.573422 -0.798089 0.185045 -0.728867 -0.684582 -0.0100463 +0.662043 0.367159 0.65337 0.0576586 0.897827 0.436558 -0.932517 0.345674 -0.104506 +0.773286 -0.594163 0.221355 -0.160785 0.920046 0.3573 -0.652284 -0.489828 0.578441 +-0.719465 -0.527608 0.451664 -0.197394 -0.958769 0.204443 0.625048 -0.644351 0.440598 +-0.450171 -0.467825 0.760583 -0.681206 -0.390797 0.619061 -0.269787 -0.926849 0.261084 +0.488812 -0.852821 0.183738 0.24203 -0.96812 0.0645413 0.152247 0.792952 -0.589956 +-0.1013 0.964653 -0.243274 0.36482 0.781757 -0.505729 0.120654 0.890714 0.43826 +-0.750137 0.641055 -0.162306 -0.300564 0.826551 -0.475893 0.889031 0.457648 0.0135305 +0.0175601 -0.807767 -0.589241 -0.627992 -0.250176 -0.736912 -0.832573 -0.0101197 -0.553822 +-0.180239 0.959128 -0.218146 -0.975822 0.203105 0.0807432 0.446144 0.892288 0.0691209 +-0.18645 -0.849383 -0.493747 -0.47971 -0.830184 -0.284029 -0.873968 -0.0771149 0.479826 +-0.812278 0.330308 0.48073 0.642143 0.762545 0.0785956 0.685601 0.713303 -0.145431 +0.293974 0.551202 0.780869 -0.42041 -0.371525 0.827783 0.728482 0.0466762 -0.683473 +-0.891428 -0.445714 -0.0818219 0.916834 -0.00274091 0.399259 0.532432 -0.846287 -0.0177477 +0.494079 -0.868381 0.0424209 -0.487983 0.486076 0.724985 0.620801 0.558721 0.549942 +0.311032 -0.948301 -0.0631279 0.699592 -0.699592 -0.145405 0.584159 -0.767357 -0.264427 +0.425544 -0.830331 -0.35981 0.464224 -0.858815 -0.216638 -0.188658 -0.981024 -0.044719 +-0.659388 -0.746212 -0.0915165 0.771295 -0.290089 -0.566527 0.48177 0.404687 -0.777256 +-0.194547 0.960578 -0.1986 -0.0345892 0.992606 0.116346 0.330335 0.739792 0.586163 +-0.554409 0.671126 0.492159 0.104307 0.382459 0.918066 -0.464886 0.757472 0.458385 +0.696904 -0.420772 0.580754 0.82618 -0.563304 -0.0107296 -0.701191 -0.643716 -0.306531 +-0.637416 -0.762623 -0.11003 0.58002 0.305686 -0.755071 0.385464 0.566682 -0.72821 +0.488593 0.576477 -0.654944 0.232487 0.854254 -0.464974 0.864582 0.27765 0.418818 +-0.717908 0.683609 0.131478 0.481512 -0.502773 0.71789 0.654403 -0.364243 -0.662634 +0.566525 0.446772 -0.692419 -0.334364 -0.675893 0.656787 0.383583 -0.280166 0.879984 +-0.82266 0.558234 0.107729 0.157532 0.984574 0.0761404 0.0894869 0.995541 0.029829 +-0.456209 0.82688 -0.328851 -0.873372 0.487054 0 0.61682 0.106348 0.779887 +0.906605 -0.215459 0.362829 -0.735083 -0.479402 0.479402 -0.697206 -0.681626 0.222015 +0.337685 -0.92096 -0.194425 -0.78914 -0.39457 -0.470715 -0.451943 0.795246 -0.404142 +0.423041 0.867963 -0.260146 0.0770327 -0.99661 0.0288872 0.182021 -0.982915 0.0273032 +-0.406562 -0.877078 -0.255814 0.998458 -0.0264492 0.0488051 -0.985628 -0.0467954 0.162321 +-0.225258 -0.946086 -0.232767 0.908438 -0.417071 -0.0281424 0.19366 -0.937125 0.290332 +0.384013 -0.776411 -0.49972 0.370627 0.688307 -0.623594 0.881539 -0.467803 0.0636275 +-0.935347 0.289678 -0.203008 0.930651 -0.101641 0.351509 -0.990077 0.12673 0.0607247 +0.505532 -0.767014 0.395129 0.780537 -0.617513 -0.0971554 -0.87798 0.432438 0.205299 +0.0433967 -0.902651 0.42818 0.291171 -0.934174 0.206246 0.979034 -0.19221 0.067442 +-0.933366 0.35779 -0.0285195 0.978478 0.181628 -0.0979368 0.976701 0.111094 0.183614 +0.722059 -0.499788 0.478375 0.726421 0.682865 0.0775144 0.275503 0.771407 0.573611 +0.117289 0.640673 0.758803 0.217482 -0.931812 0.290565 0.109131 -0.992576 0.0536996 +-0.850588 0.514502 0.108571 -0.755283 0.422272 0.501233 0.795559 0.595362 -0.112382 +-0.965902 0.0616179 0.251468 0.393091 -0.91277 -0.111043 0.627833 0.623675 0.465677 +0.815527 0.547 0.188964 -0.129827 0.536618 0.833778 -0.629313 0.0646238 0.77446 +-0.627186 -0.677179 0.384795 0.142175 -0.95994 0.241455 0.838717 -0.279572 0.467325 +-0.828097 0.559978 -0.0260671 -0.793616 0.605257 0.0619489 -0.732389 0.642249 -0.226102 +-0.919556 0.276615 -0.279107 -0.80546 0.577246 0.134243 0.85064 -0.515539 -0.103108 +-0.529412 0.705882 0.470588 0.204655 0.708985 0.674875 -0.0936265 0.206624 0.973931 +-0.430175 -0.0977054 0.897442 -0.87821 -0.40941 0.247247 0.817799 -0.572902 0.0546677 +0.274368 -0.00783907 0.961593 0.468355 0.212223 0.857674 -0.867086 -0.412898 -0.278706 +0.333247 0.773089 0.539704 -0.738222 0.0025812 0.674553 -0.729397 -0.388927 0.562775 +-0.0345494 -0.958747 -0.282154 -0.759089 -0.498152 -0.41908 -0.883272 0.430186 -0.186469 +0.782948 -0.445471 0.434221 -0.702032 0.226257 0.675247 -0.818275 0.221564 0.53041 +-0.759455 -0.607564 0.232583 0.038568 -0.994504 -0.0973383 0.741072 -0.553145 -0.380582 +-0.402702 -0.434919 -0.805405 -0.591824 -0.313319 -0.742681 0.179559 -0.193371 -0.964555 +0.779622 -0.507457 0.366984 -0.0705151 -0.564121 0.822676 0.555662 0.789844 0.259586 +0.701905 0.712266 -0.00259006 -0.393066 -0.426583 -0.814571 -0.638362 -0.0640302 -0.767069 +-0.156625 0.86731 -0.472485 -0.154957 0.923893 -0.349871 -0.566104 0.299702 -0.767923 +-0.0540124 -0.900207 0.432099 0.717203 0.646973 0.258931 0.0387284 0.96821 -0.247124 +-0.852252 0.233952 -0.467903 0.770828 -0.513885 -0.376492 -0.433293 0.0772538 -0.897936 +-0.441542 -0.532343 -0.722254 0.586711 0.369167 -0.720754 -0.597891 -0.0128579 -0.801474 +0.817247 0.0674791 -0.572323 -0.284468 -0.924521 -0.253651 -0.0394122 -0.992603 -0.114831 +0.586584 -0.782685 -0.208143 0.210896 -0.723071 -0.657793 0.784555 -0.564991 -0.255458 +-0.985826 0.125934 -0.110848 0.428532 0.836033 0.342651 0.544306 0.834603 0.0846699 +-0.0273532 0.877789 0.478266 0.213399 0.87769 0.429093 -0.616112 0.724439 -0.309184 +0.725074 0.623471 0.292493 0.158607 0.929215 0.33377 0.241388 0.836883 0.491283 +0.277472 0.501692 0.819337 -0.586161 0.806416 0.0781548 -0.76939 0.377436 0.515346 +0.999076 0 -0.042971 0.901624 -0.276498 -0.332599 0.674453 0.551825 -0.490511 +-0.302206 0.910816 -0.28122 -0.823832 0.322169 -0.466378 0.54395 0.390528 -0.742701 +-0.786183 0.293769 -0.543706 0.923501 0.340491 0.17667 0.972855 0.221754 0.0661684 +0.249189 0.386672 0.887913 0.461058 0.675504 0.57543 -0.07817 -0.986144 -0.146318 +-0.251057 -0.502114 -0.827558 0.781083 0.414068 -0.467395 0.162452 0.699794 -0.695628 +0.389264 -0.528137 -0.754682 -0.636464 -0.603463 -0.48036 0.922692 0.168944 -0.346551 +-0.218208 0.877029 0.428024 0.0545856 0.846076 0.53026 -0.968434 -0.220467 -0.11632 +0.783573 -0.602749 -0.150687 0.166574 -0.760448 -0.627672 0.318941 0.528531 -0.786722 +0.707104 -0.335421 -0.622493 0.531772 -0.335856 -0.777444 0.705496 -0.264561 -0.657483 +0.90796 -0.0974804 -0.407561 0.141996 -0.812534 -0.565355 0.12281 -0.973037 -0.195237 +-0.830287 -0.0919985 -0.549691 -0.907136 0.412125 -0.0851879 0.813701 0.28835 -0.504723 +-0.980578 0.155423 0.119629 -0.21579 0.951074 0.221118 0.884389 -0.365954 -0.289714 +0.175323 0.91001 -0.375692 -0.294649 0.955378 -0.0208338 0.803538 0.583107 -0.119636 +-0.0328396 0.97026 0.239828 -0.762362 0.39702 0.511057 -0.644103 0.644103 -0.412629 +-0.861474 0.501651 0.0787934 0.757674 0.631395 -0.165134 -0.173715 0.98398 -0.0400881 +0.0590994 0.98105 0.184521 -0.485725 0.794409 0.364672 -0.601819 0.71081 0.364093 +0.0892751 0.982026 0.166297 0.755531 -0.458715 0.46771 -0.778652 0.593134 0.204679 +0.253929 0.948003 0.191858 0.253345 0.957082 0.140747 -0.01281 0.999179 0.03843 +-0.784161 0.61138 -0.106327 -0.472775 0.8678 0.152994 -0.20039 0.901035 0.384682 +-0.450055 0.725713 0.520376 -0.466501 0.641439 0.609043 -0.307698 -0.946762 -0.0946762 +0.606794 0.794186 0.0327193 -0.768634 0.409112 0.491761 -0.83657 0.375603 0.398841 +0.0107634 0.8826 0.470002 -0.305333 0.395137 0.866394 0.0352067 0.95058 0.308477 +0.69164 0.332688 0.641056 0.845559 0.383356 0.371575 0.673364 -0.424423 0.605348 +0.810594 0.583705 0.0471877 0.558877 0.0745169 0.825896 -0.753087 0.423041 0.503881 +0.457134 -0.0489787 0.888048 -0.556414 -0.529127 0.640647 -0.88778 0.333531 0.317181 +-0.997911 0.0636821 -0.0108395 -0.0206386 -0.97591 0.217196 0.576738 -0.814743 -0.059724 +-0.688388 0.697176 0.20017 0.46534 0.850908 0.24375 0.204143 0.74805 0.631464 +0.620335 0.592866 0.513512 -0.0176425 -0.970336 0.241114 0.792424 -0.255187 0.554025 +-0.988113 -0.0306392 -0.150643 -0.291551 0.892875 0.34318 0.188902 -0.981878 0.0152229 +0.992506 0.0577038 0.107714 -0.903829 0.0703452 -0.422071 0.506059 -0.857637 0.0914458 +0.888234 -0.41419 -0.198715 0.0314464 0.993183 0.112246 0.0985576 0.995086 -0.00950994 +0.00309445 0.996412 0.0845815 0.0966845 0.995281 0.00821502 0.970079 -0.0928306 -0.22434 +0.0858495 0.985225 0.148193 0.459898 -0.813099 0.356881 0.420206 -0.452529 0.786539 +-0.67743 -0.658185 0.328451 -0.603843 -0.738414 0.300196 0.538519 -0.817208 0.20535 +-0.856362 0.486709 -0.172505 0.440858 -0.896412 0.0457186 -0.496167 -0.857016 -0.139077 +-0.493412 -0.695837 -0.521878 0.121931 0.738758 -0.66285 0 0.69568 -0.718351 +0.117862 0.833876 -0.53922 -0.301478 0.836417 -0.457731 0.415422 0.671664 -0.613427 +0.431768 -0.895729 0.106048 0.732543 0.0261622 -0.680218 -0.0326107 -0.605627 -0.79508 +-0.827311 -0.15551 -0.539791 0.779259 -0.610771 0.140407 0.529982 -0.714323 -0.457013 +0.54406 -0.746676 -0.382718 0.444184 -0.895533 -0.026866 -0.612625 -0.722253 -0.321001 +0.471829 0.688978 -0.55017 -0.223669 0.688465 -0.689919 -0.13129 0.774159 -0.619226 +0.0911533 0.299504 -0.949731 0.892607 -0.318788 -0.318788 0.0223457 -0.938521 -0.344497 +-0.444963 -0.874583 -0.192647 -0.60169 -0.575867 -0.553486 0.342074 0.724872 -0.597951 +-0.270215 0.628249 -0.72958 0.121069 0.407716 -0.905047 -0.23157 0.605852 -0.76113 +-0.342352 0.569403 -0.747379 0.117507 0.568803 -0.814036 0.023847 0.7614 -0.647843 +-0.526226 0.585519 -0.616648 -0.231181 0.854087 -0.46593 -0.784243 0.54556 -0.295512 +0.9009 -0.345628 -0.262526 -0.35482 -0.906762 -0.227786 -0.227612 -0.699695 -0.677215 +0.202606 0.162084 -0.965753 0.331769 0.539125 -0.774128 0.330417 0.788996 -0.517987 +0.22223 0.864528 -0.450783 -0.0817823 0.324208 -0.942444 0.112184 0.518025 -0.847977 +-0.129178 0.172871 -0.976437 -0.715707 0.486015 -0.50155 0.0109128 0.979177 -0.202714 +-0.10432 0.799788 -0.591148 -0.33193 -0.86539 -0.375397 0.188744 0.0330303 -0.981471 +0.475564 0.466758 -0.745638 0.464235 0.714406 -0.523554 0.182576 0.845068 -0.50252 +0.0765634 0.796955 -0.599166 -0.168661 0.748856 -0.640912 -0.0476265 0.483069 -0.874286 +0.356329 -0.344451 -0.868552 -0.390794 0.256037 -0.884152 -0.696654 0.171308 -0.696654 +-0.126787 0.235461 -0.963578 -0.803303 0.087633 -0.589089 -0.98186 -0.11417 0.151381 +-0.191918 -0.967673 0.163636 -0.00956072 -0.984755 0.173686 -0.389125 0.243203 -0.888501 +-0.574119 -0.812714 0.0994146 0.00366827 0.201755 -0.979429 0.243627 0.705803 -0.665198 +0.427219 0.759135 -0.491119 -0.337822 0.445311 -0.829201 0.186444 0.445845 -0.875477 +0.329095 0.329095 -0.885095 -0.285756 0.417995 -0.862336 0.312529 0.659652 -0.683509 +-0.428604 -0.898519 0.0946715 0.889324 0.0896797 -0.448398 -0.474634 0.443728 -0.76015 +0 -0.241626 -0.970369 0.664454 0.45961 -0.589287 0.0151411 0.847901 -0.529938 +0.101695 0.38983 -0.915254 0.0947333 0.271776 -0.957686 0.334016 0.513871 -0.79017 +-0.798846 -0.589048 0.121934 0.652356 -0.753425 -0.0823527 0.478811 -0.806061 0.34786 +0.552936 0.535923 -0.638003 0.430075 -0.900803 0.0599109 -0.812626 0.58231 -0.0235386 +0.739027 0.451547 -0.499944 0.0858603 -0.932198 0.351618 0.592979 -0.662741 -0.45733 +0.229178 -0.965324 -0.125006 -0.865837 0.151789 -0.476745 -0.290488 0.544665 -0.786738 +0.566408 0.82264 0.0494483 0.238092 0.965783 -0.102838 -0.931211 0.271112 -0.243608 +0.913181 0.391363 0.11373 0.844132 0.522029 0.122177 -0.85241 -0.522741 -0.0118208 +0.773164 0.629435 -0.0776468 0.42169 -0.901543 0.0969401 -0.40193 -0.818801 0.409899 +0.877593 -0.00566189 -0.479373 0.996265 -0.0797012 -0.0332089 -0.630452 -0.302759 0.71475 +0.469776 -0.058722 0.88083 0.885716 -0.36621 0.285303 0.806559 -0.12566 -0.577644 +-0.0478107 -0.273204 0.960767 -0.125909 -0.924588 0.359562 0.739254 -0.576042 0.348825 +0.810052 -0.236301 -0.536635 0.851356 -0.0949053 -0.515931 0.8308 -0.200538 -0.519187 +-0.981549 0.0885608 0.169468 -0.779176 0.446306 0.440108 -0.266874 0.951168 0.155106 +-0.220171 0.798397 0.560435 -0.198288 0.925346 0.323137 -0.313712 0.686775 0.655686 +-0.669239 -0.540294 0.5101 -0.528226 0.136947 0.837988 -0.711376 0.643626 0.282292 +-0.868 -0.432311 0.244301 -0.64222 -0.725087 0.248601 0.0137037 -0.986669 0.162161 +-0.429028 -0.899734 0.0800853 -0.796435 -0.584053 0.156759 -0.930467 -0.266065 0.251875 +-0.928756 -0.321726 0.184132 -0.459829 -0.887945 -0.0105708 0.65052 -0.359335 -0.669106 +-0.595695 -0.689505 0.411983 -0.592954 -0.67838 0.433828 -0.932875 -0.201208 0.298764 +-0.548243 0.146952 0.823307 -0.124359 0.683972 0.71883 -0.622472 0.77809 0.0842931 +0.294561 -0.13105 -0.946604 0.434018 0.648209 0.625663 0.560395 0.557255 0.612719 +-0.768221 0 0.640184 -0.0907617 0.0819377 0.992496 -0.794135 -0.560566 0.234767 +0.744918 -0.157802 -0.648225 0.267261 -0.455916 -0.848947 -0.2543 -0.736411 -0.626921 +0.195894 -0.138278 -0.970827 -0.435293 -0.114005 0.893041 0.0790537 -0.837969 0.539961 +-0.345503 -0.877873 0.331611 -0.773505 -0.630704 0.0624754 -0.194607 0.7914 -0.579495 +0.463032 -0.40981 -0.785912 -0.53091 -0.3492 0.772136 -0.48301 -0.802979 0.34918 +-0.373332 -0.919782 0.120931 -0.691705 -0.620847 -0.368909 -0.378586 0.107728 0.919276 +0.772425 0 0.635105 0.311129 -0.677475 -0.666503 0 -0.913276 -0.407341 +0.69236 0.437533 -0.573761 -0.426071 -0.809534 -0.403879 0.51719 -0.292749 -0.804247 +-0.275126 -0.776825 0.566435 -0.641663 -0.278551 -0.714617 0.420445 -0.544391 -0.725855 +0 -0.352946 -0.935644 0.399175 -0.536822 -0.743291 -0.378509 -0.485634 -0.787966 +-0.564532 0.0903252 0.820454 0.00265338 -0.934652 0.355553 -0.0899031 -0.611341 0.786244 +-0.8985 -0.252409 0.359149 0.673672 0.688717 -0.26802 -0.294485 -0.0631039 -0.95357 +-0.830727 -0.420057 -0.365301 0.658488 0.719872 0.219496 -0.213353 0.963834 0.159703 +-0.627803 0.700068 0.340248 0.546366 -0.561496 -0.621456 0.397347 -0.397347 -0.827182 +-0.363614 -0.886822 0.285187 0.898894 -0.254554 0.356641 0.506943 -0.699884 0.50316 +-0.573804 -0.538189 -0.617334 -0.483412 -0.0303714 -0.874866 0.598451 -0.619825 0.507615 +0.832499 0.445981 0.328705 -0.691032 -0.707291 0.149046 0.172447 -0.172447 -0.969806 +-0.147857 -0.535454 -0.831521 -0.796696 -0.534456 0.282193 -0.197786 -0.928159 0.315279 +0.780603 -0.617143 0.0989653 0.820248 -0.568277 0.0652267 -0.973758 -0.0226455 0.226455 +0.919315 0.315586 0.235088 -0.600009 0.695182 -0.395868 0.307166 -0.557185 -0.771488 +-0.357909 -0.586027 -0.726962 -0.991927 0.0148882 0.12593 -0.0436156 0.992255 -0.116308 +0.112852 0.94439 -0.308857 0 -0.699193 -0.714933 0.425581 -0.816792 0.389526 +0.554604 -0.805184 0.209984 -0.249555 -0.960203 0.125428 -0.907917 -0.0466872 0.416542 +-0.819624 -0.015762 0.572686 0.897071 -0.135263 0.420675 -0.184387 0.964488 -0.189115 +0.785175 0.141838 -0.602812 -0.138233 0.990354 -0.00946802 -0.189037 -0.578924 0.793166 +0.80907 0.367141 0.458926 0.712364 0.274947 0.64571 -0.0772307 -0.724038 0.685423 +-0.537326 -0.828757 0.156341 0.644661 0.764466 0.00190166 -0.633842 -0.741595 0.219732 +-0.918632 -0.260508 -0.29707 -0.7758 -0.123306 -0.618813 -0.869091 -0.364103 -0.334829 +0.974566 -0.0251501 0.222683 0.856428 0.260562 0.445689 0 0.286582 0.958056 +-0.622242 -0.656182 0.426896 -0.357277 -0.849524 0.388152 0.37847 0.879466 0.288617 +0.498339 0.839761 0.215544 -0.144164 -0.677962 0.720822 0.23215 -0.747922 0.621868 +-0.0706076 -0.723728 0.686463 -0.717444 0.105168 0.688631 -0.689846 0.489349 0.533526 +-0.731974 -0.671632 0.114562 0.0533486 0.906926 0.417897 0.494058 0.56507 0.660759 +0.729844 0.00793309 0.683568 -0.482697 -0.848026 -0.218757 -0.419436 -0.885477 -0.200009 +-0.00301935 0.960153 0.279458 0.440196 0.752001 0.490635 -0.518201 0.782381 0.345467 +-0.205353 -0.966659 0.152971 0.211218 -0.766884 0.606033 -0.834638 0.244123 0.493745 +0.799797 -0.307182 0.515716 0.527763 -0.0232623 0.849073 0.32594 -0.211257 0.921484 +-0.274095 -0.713168 0.645185 -0.883103 -0.186223 0.43064 -0.551854 -0.827781 -0.101173 +0.968937 0.238534 0.0652922 -0.743488 0.529791 0.408102 0.545829 -0.433453 0.71707 +-0.0797452 0 0.996815 0.391437 -0.669001 0.631834 -0.994407 -0.0678005 0.0809839 +0.652413 -0.609865 0.449913 -0.0276256 -0.910692 0.412161 -0.462232 -0.850126 0.252242 +0.270156 -0.689204 0.672319 -0.107629 0.522768 0.845654 -0.85372 0.357847 -0.378295 +0.362319 -0.687358 0.629495 0.0133976 -0.697707 0.716258 0.0683529 -0.772767 0.630998 +0.0312361 -0.777431 0.628192 -0.367265 -0.477998 0.797894 -0.38953 0.174335 0.904364 +-0.158268 0.939195 0.304735 -0.657367 -0.734704 0.167564 0.338555 -0.63728 0.692283 +0.862128 0.00451376 0.50667 0.623929 -0.736236 0.26205 0.290356 -0.758885 0.582912 +0.778424 -0.346822 0.523231 -0.606172 -0.589621 0.533762 0.75355 -0.58685 0.29626 +0.246752 -0.809346 0.532984 -0.52009 -0.821074 0.235252 -0.709675 -0.68583 0.161238 +0.724845 -0.50997 0.463175 -0.916951 -0.395842 0.0501066 0.676329 0.736447 -0.0150295 +0.767536 -0.167899 0.618626 -0.726525 -0.676171 0.122286 0.807047 0.590411 -0.00949268 +0.536529 0.75552 -0.375935 0.189109 0.866118 0.462686 0.442605 -0.872192 0.208285 +0.762131 -0.568369 -0.31002 -0.913272 -0.258988 -0.31442 0.69705 -0.0279752 0.716477 +0.789299 -0.613216 0.0311944 0.605487 -0.630201 0.486037 0.760199 -0.612195 0.21752 +-0.0822578 0.740321 0.667202 -0.856848 0.386508 -0.341209 0.413355 0.908883 0.0554045 +0.863375 -0.234283 0.446873 0.839448 -0.395966 0.372208 0.172774 0.777482 0.604708 +-0.370247 0.887797 0.273373 -0.0265889 0.868572 0.49485 -0.703839 0.614933 -0.355624 +0.703157 -0.559768 0.438439 -0.295777 -0.371963 0.879863 -0.402732 -0.826118 0.394127 +0.751439 -0.644091 0.143131 -0.202927 0.96954 -0.137164 0.159409 0.299785 -0.940594 +-0.345135 0.446563 0.825508 -0.755784 0.644915 0.113472 -0.0127457 -0.939996 0.340948 +0.554481 -0.73529 -0.389744 0.0383201 0.996322 -0.0766402 0 -0.993884 0.110432 +0.0623914 0.0594204 0.996281 -0.198595 -0.0379667 0.979346 -0.552502 -0.357105 0.753139 +0.95364 -0.283515 0.100948 0.0788381 -0.996887 -0.00103734 -0.623823 -0.516158 -0.586879 +0.619117 -0.711247 -0.332898 -0.587694 0.350116 -0.729407 0.425525 -0.633559 -0.646167 +-0.792719 0.125052 -0.596623 0.549647 0.682485 0.48177 0.284613 0.868068 0.406759 +0.125081 0.646252 0.752803 -0.482884 0.46248 0.743596 0.0495907 0.963994 -0.261258 +-0.220279 0.885436 -0.409244 -0.769262 -0.210159 -0.603382 0.463245 0.863706 -0.198534 +0.457551 0.889148 0.00801034 0.106283 0.978284 0.177943 -0.824598 0.385214 0.414305 +-0.970146 -0.233118 0.0668742 0.99625 0.0510898 -0.0698227 -0.35326 0.855806 0.377895 +-0.221715 0.565373 0.794478 -0.193881 0.320325 0.927255 -0.925243 0.0155069 0.379057 +-0.843303 0.0018575 -0.537435 -0.894793 -0.13778 -0.424691 0.799902 0.47053 -0.372503 +-0.316261 0.67903 0.662493 0.597121 0.11642 0.793658 -0.584023 0.460358 0.668572 +-0.651687 0.267705 0.709675 0.730825 0.375507 0.56999 0.678212 0.452142 0.579306 +0.440507 0.652459 0.616645 -0.343026 0.883311 0.319524 0.00573542 0.81443 0.580234 +-0.074976 0.995877 -0.0510706 0.256632 0.946919 -0.193609 0.713131 0.671647 -0.200836 +0.233665 0.826294 -0.512483 -0.883754 0.0489375 -0.465386 0.536882 -0.649454 -0.538486 +-0.412153 0.527015 0.743226 0.47761 0.365454 0.798957 -0.198993 0.979813 0.0191922 +0.83459 -0.316449 0.45091 0.520414 0.122544 0.845075 0.585649 0.151293 0.79632 +-0.742989 -0.398677 -0.53761 0.250072 0.953478 0.168357 -0.0907066 0.379318 0.920809 +0.0711989 -0.750327 0.657221 0.238656 -0.765445 0.59761 -0.858499 -0.505471 -0.086474 +-0.148188 0.254037 0.955775 0.230954 0.798552 0.555855 -0.227606 0.878812 0.419385 +0.830754 -0.527494 0.177759 0.483491 -0.84246 0.237692 -0.547495 -0.79833 -0.250835 +-0.596855 -0.778153 -0.195557 0.657571 0.553195 0.511444 -0.588348 0.784465 0.196116 +0.751382 -0.253756 -0.609125 0.69777 0.279108 -0.659709 0.569258 0.597721 -0.564514 +0.57905 0.539268 0.611466 0.355506 -0.0334594 0.934075 -0.695148 0.718065 0.0339511 +0.258622 0.862073 0.435826 -0.32263 0.897752 -0.299919 -0.594266 0.779492 -0.198089 +-0.906621 -0.139084 -0.398364 0.426953 -0.760511 0.489218 -0.229504 -0.944247 -0.236062 +-0.527683 -0.828512 -0.187401 -0.532891 -0.444664 -0.719932 -0.352344 -0.932926 -0.0741776 +-0.257656 -0.96621 -0.00715711 -0.25711 -0.939012 -0.228365 -0.969541 0.153364 -0.19097 +0.799125 -0.599344 0.0467573 0.777426 -0.628471 0.025166 -0.966318 -0.256574 -0.0199928 +0.894109 0.296593 -0.335562 0.690268 -0.711399 -0.132067 0.686383 -0.11969 -0.717323 +0.713259 0.632277 -0.302469 -0.712278 0.651226 -0.261847 0.255656 -0.941177 -0.220965 +0.828486 0.555535 0.0706461 -0.666811 -0.742253 -0.0665188 0.837255 0.52587 -0.149886 +-0.935068 -0.289133 -0.205059 0.703161 -0.689533 -0.173519 -0.949556 -0.0091597 -0.313465 +0.590602 0.309984 0.74505 -0.830706 -0.480177 0.281704 0.508962 -0.840972 0.18364 +0.259785 0 -0.965666 0.837165 0.429937 0.338096 -0.454403 -0.495712 0.740126 +-0.77485 -0.478956 -0.412563 -0.0814985 -0.995301 0.0522949 0.714269 0.236299 -0.658774 +-0.634238 -0.369972 0.678869 0.781088 0.618361 0.0867875 0.474481 0.800686 0.365746 +0.520859 -0.129625 0.843744 0.90976 0.304175 0.282514 -0.746589 -0.603824 0.279288 +0.126534 -0.989943 -0.063267 -0.503895 -0.852012 -0.142002 0.410782 -0.898505 -0.15475 +-0.356862 -0.172444 -0.918103 0.655409 0.607867 0.448259 -0.46487 -0.542042 0.700061 +0.00422915 -0.90081 -0.434193 -0.191351 -0.867457 -0.459242 0.821086 -0.454422 -0.345425 +-0.546042 0.58859 -0.596154 -0.93058 0.113535 -0.348038 -0.753609 -0.628008 -0.194111 +0.438023 0.109506 0.892269 0.777655 0.330412 0.534866 -0.889174 -0.0967962 -0.447213 +0.160625 -0.103259 -0.981599 -0.0679258 -0.666908 -0.742038 -0.728188 0.305994 0.613279 +-0.833474 -0.0123217 0.552422 -0.107566 0.0759289 0.991294 -0.180939 -0.719548 0.670456 +-0.350117 -0.73456 0.58124 0.775063 0.60803 -0.171976 0.547788 -0.620137 -0.561568 +-0.852937 -0.0140401 -0.521824 -0.941919 -0.217366 0.256009 0.682862 -0.72604 0.0810265 +0.226125 -0.767773 0.599494 -0.77116 -0.632747 0.0703052 0.881302 0.375022 -0.287517 +0.886711 0.28195 -0.366398 0.880522 0.266924 -0.391705 0.329377 -0.838118 -0.434821 +-0.0988595 -0.97384 -0.204605 -0.467157 -0.647056 -0.602564 0.463428 0.868927 -0.173785 +-0.538635 0.817571 -0.203591 0.504489 -0.246379 -0.827519 -0.159358 -0.830206 -0.534194 +-0.520467 -0.546931 -0.655729 0.395295 0.796767 0.45706 -0.168681 0.984547 0.0470471 +-0.256167 0.966621 0.004627 0.989896 -0.134516 0.0448385 -0.826469 0.373294 0.421427 +0.343333 -0.924358 -0.166385 0.844812 -0.0808081 -0.528926 -0.666903 0.283434 -0.689134 +0.999991 -0.00306746 -0.00306746 0.970461 -0.206424 -0.124882 -0.19123 -0.956151 0.221827 +-0.207602 0.851885 -0.480826 -0.936313 0.317231 -0.150605 0.106412 -0.127695 -0.986088 +-0.680817 0.732192 -0.0195712 0.139002 -0.914116 -0.380882 0.911713 0.363804 -0.190858 +-0.0763092 0.985661 -0.150499 -0.0643992 0.965988 -0.250441 -0.532559 0.817202 -0.220369 +0.0588725 0.408645 0.910793 -0.376454 0.792696 0.479494 0.564532 0.8054 -0.18065 +0.888415 0.376903 -0.262037 0.042241 0.995884 -0.0801862 0.978205 -0.0670004 -0.196534 +0.558875 0.62795 0.541607 0.362945 0.798117 0.480917 -0.342674 0.920935 -0.185615 +0.686221 0.721715 0.0907074 0.728373 0.681881 -0.067155 0.296334 0.931334 -0.211667 +-0.560047 0.665716 0.493123 -0.728108 0.6721 0.134687 0.809224 0.325276 0.489237 +0.26383 0.906776 0.328863 -0.983703 0.14414 0.107479 -0.429642 0.839754 0.331996 +-0.563812 0.82589 0.00459786 -0.812234 0.54149 0.216943 0.298822 0.914343 0.273281 +0.258055 0.918501 0.299606 0.892747 0.334366 0.301996 -0.85362 0.219279 0.472493 +-0.700469 0.62234 0.349337 0.724669 0.537697 0.430972 -0.851145 -0.338675 0.401063 +0.452102 0.322536 0.83161 0.640457 0.751488 -0.158367 -0.418052 -0.8813 -0.220325 +-0.341742 -0.93979 -0.00251281 0.932318 -0.334771 -0.136788 -0.147033 0.952039 0.268335 +-0.43836 -0.769566 0.464337 0.170428 0.806892 0.565579 0.381367 0.560659 0.734998 +0.368843 -0.319411 0.872887 -0.24938 -0.798426 0.54802 -0.0984517 -0.950836 0.293628 +0.66899 -0.561804 0.48665 -0.573497 0.517834 -0.634783 -0.934294 0.219331 -0.281048 +0.566153 -0.781677 -0.261631 0.839666 -0.138027 -0.525271 -0.312362 0.94996 0.00261234 +0.910303 -0.40317 0.0938195 0.401091 -0.0668485 0.913596 -0.754771 -0.55993 0.341759 +-0.994941 0.0826658 -0.0570787 0.725394 0.6883 0.00686926 0.265795 0.9093 0.320198 +0 -0.185408 0.982662 -0.368689 -0.345428 0.862988 -0.823699 -0.0296295 0.566252 +-0.706004 -0.644612 -0.293316 -0.313848 -0.849235 -0.424617 0.157286 0.259059 0.952969 +-0.568035 0.0908856 0.817971 0.897795 -0.439829 0.0226716 -0.304582 0.64458 0.701246 +-0.241642 0.283963 0.927887 -0.393831 0.65717 0.64267 -0.384949 0.88418 0.264652 +0.501991 -0.00994041 -0.864816 0.892835 0.275335 0.356421 0.996713 0.0228604 0.0777253 +0.235249 0.88545 0.400794 0.0265536 0.862992 -0.504519 -0.461695 0.619809 -0.634567 +0.015591 0.930263 -0.366562 -0.279485 -0.960092 0.0105233 -0.274154 -0.961669 0.00568194 +-0.201044 -0.979353 0.0211785 -0.0885584 -0.996071 0 0 -1 0 +0 -0.999816 -0.0191892 -0.0667311 -0.997587 -0.0191465 -0.0785018 -0.996512 -0.0283222 +0 -0.999901 -0.014081 0.111426 -0.993624 -0.0171897 0.182745 -0.983081 0.0124766 +0.14425 -0.988923 0.0349697 0.209281 -0.975923 0.061447 0.20126 -0.977087 0.0692509 +0 -0.995197 0.0978883 -0.10444 -0.974769 0.197275 0 -0.983175 0.182666 +0 -0.990057 0.140664 0.0975198 -0.991451 0.0866843 -0.446082 -0.894992 0 +-0.465128 -0.885243 0 -0.308237 -0.951157 -0.0170229 -0.263313 -0.964684 0.00718127 +0.1499 -0.983638 0.0999335 0.0198833 -0.994163 0.106044 -0.0851522 -0.991866 0.0946136 +0.354005 -0.919619 0.170241 0.608501 -0.760931 0.22519 0.607536 -0.761714 0.225149 +-0.682813 -0.716331 0.143649 -0.43741 -0.874004 0.211633 0.30826 -0.943021 0.12525 +0.695662 -0.664041 0.274049 -0.564745 -0.825265 0 -0.610299 -0.792104 0.0102787 +-0.46671 -0.884401 -0.00408499 0.430166 -0.866834 0.252104 0.146086 -0.980865 0.128695 +-0.235457 -0.963758 0.125423 -0.698113 -0.704852 0.125786 0.796187 -0.442326 0.412837 +0.73523 -0.487031 0.471421 0.825203 -0.255697 0.503645 0.0075539 -0.89136 0.453234 +-0.712932 -0.697357 0.0736295 -0.669195 -0.738447 0.0829133 0.863017 -0.249821 0.439079 +0.924922 -0.0660658 0.374373 -0.725353 0.565463 0.392574 -0.532116 -0.821564 0.20466 +-0.668001 -0.705112 0.237892 0.336973 -0.937546 0.0863569 0.399804 -0.914306 0.0648134 +-0.328104 -0.944567 0.0118388 -0.388972 -0.92125 0 -0.437541 -0.898948 0.0212141 +0.842528 -0.454397 0.289257 -0.919454 -0.060292 0.388549 -0.916877 -0.297229 0.266443 +0.614886 -0.788568 0.0086409 0.769212 -0.638913 -0.0101843 -0.489603 -0.870405 0.0518098 +-0.517831 -0.854646 0.0378242 -0.475235 -0.879761 -0.0131553 -0.454792 0.773371 -0.441658 +0.457439 0.470894 -0.754327 0.637711 0.755443 -0.150434 0.195588 0.97794 -0.0733455 +-0.351294 0.91877 -0.180151 0.569061 -0.803759 0.173612 -0.73254 -0.656961 0.17829 +0.15325 -0.973776 0.168149 -0.661326 -0.726081 0.188294 0.883769 -0.459398 -0.0889158 +0.908735 -0.400459 -0.117617 -0.487417 -0.870388 0.0696311 -0.486002 -0.872009 0.05833 +0.30655 0.892601 -0.330593 0.287824 0.897735 -0.33351 0.198127 0.936202 -0.290295 +0.140983 0.94177 -0.305276 0.0184246 0.95071 -0.309534 -0.266425 0.916503 -0.298396 +-0.439484 0.867163 -0.23427 -0.453132 0.863108 -0.22297 0.928792 -0.271056 -0.252733 +-0.708115 -0.624926 -0.328695 -0.366922 -0.927089 0.0766459 0.975779 -0.110088 -0.18904 +-0.515842 -0.851813 0.0912198 -0.894821 -0.434946 0.100591 0.168938 -0.985625 0.00180683 +0.610801 -0.790751 0.0404458 0.62596 -0.778972 0.0370939 -0.679775 -0.722261 0.127458 +-0.474595 -0.877109 0.0737466 0.460456 0.850674 -0.253641 0.29013 0.924335 -0.24785 +0.180154 0.948812 -0.259422 0.165823 0.939665 -0.299219 0.282149 0.900644 -0.330502 +-0.455763 0.862374 -0.220434 -0.311262 0.92517 -0.217201 -0.24785 0.95001 -0.189871 +-0.214222 0.959033 -0.185375 -0.184099 0.796646 -0.575728 0.589037 -0.674199 -0.445523 +-0.0897068 -0.937437 -0.336401 0.961929 0.166077 -0.217051 -0.987045 0.150574 0.0554062 +0.604209 -0.796135 0.0331723 0.544451 -0.83808 0.0345683 -0.28431 -0.948035 0.142823 +-0.222907 -0.964731 0.140025 0.149932 -0.97456 0.166591 -0.448672 -0.88538 0.12164 +0.327741 0.853608 -0.404893 0.356679 0.857281 -0.37128 0.427049 0.847627 -0.314894 +0.479715 0.839502 -0.255168 -0.180057 0.964724 -0.19206 -0.17241 0.949471 -0.262258 +-0.401497 0.867562 -0.293492 -0.390424 0.877381 -0.278874 0.879973 0.436878 -0.186507 +-0.8848 0.465867 0.00985042 0.536194 -0.843495 0.0318177 0.605344 -0.794767 0.0436388 +0.616143 -0.78655 0.0413109 -0.609367 -0.781239 0.135415 -0.454729 -0.878828 0.144508 +-0.333345 -0.935307 0.118666 0.596007 0.736388 -0.320169 0.359282 0.851087 -0.382842 +-0.381715 0.880964 -0.279637 -0.388603 0.850528 -0.354387 -0.421969 0.843939 -0.331223 +-0.430517 0.844565 -0.318378 -0.111816 0.901373 -0.418359 0.764673 0.614553 -0.193905 +0.875611 0.446136 -0.185119 -0.87505 0.483939 0.009489 -0.87991 0.475038 0.00987847 +0.583376 -0.811049 0.043256 0.455673 -0.888468 0.0546554 -0.784014 -0.607878 0.125727 +-0.812292 -0.56948 0.125995 0.52514 0.8029 -0.2821 0.470129 0.831596 -0.295679 +0.588126 0.743735 -0.317752 -0.329969 0.889279 -0.316708 -0.0609368 0.962802 -0.263247 +-0.0487821 0.963446 -0.263423 -0.26952 0.919791 -0.285207 -0.465079 0.84739 -0.256188 +-0.106709 0.895169 -0.432764 0.619572 0.756938 -0.207787 0.687799 0.699214 -0.195019 +-0.90749 0.418979 -0.0303091 0.398396 -0.915758 0.051644 0.452701 -0.891031 0.0335334 +0.526696 -0.849625 0.02701 -0.846911 -0.50641 0.162144 0.632236 0.632236 -0.447834 +0.0810448 0.921649 -0.379466 0.311677 0.885447 -0.344734 0.533229 0.797007 -0.283632 +-0.467859 0.846086 -0.255435 -0.257223 0.932138 -0.254863 -0.105308 0.967076 -0.231677 +-0.149137 0.955078 -0.256094 -0.234589 0.927496 -0.291064 -0.210937 0.922849 -0.322265 +0.462607 0.856168 -0.230153 0.607283 0.764151 -0.217441 -0.915423 0.401011 -0.0345132 +-0.917805 0.394702 -0.0429383 0.545751 -0.837912 0.00765966 0.536292 -0.837903 -0.101538 +0.514291 -0.852072 -0.0973555 -0.438949 -0.893575 0.0940605 -0.643464 -0.747248 0.166055 +0.331179 0.804293 -0.49339 0.652385 0.671787 -0.350849 -0.167097 0.925588 -0.339655 +-0.139846 0.929562 -0.341113 -0.277767 0.902352 -0.329555 -0.162397 0.924959 -0.343624 +0.042895 0.93654 -0.347926 0.197486 0.895271 -0.399361 0.0469726 0.935839 -0.349284 +0.206169 0.91721 -0.34091 0.452113 0.861337 -0.231715 -0.906546 0.412737 -0.0884435 +0.478404 -0.874821 -0.0762803 0.525524 -0.849231 -0.0512869 0.582049 -0.812504 -0.0325002 +-0.674652 -0.73764 0.0270694 -0.745972 -0.665709 0.0188854 0.565346 0.785774 -0.250884 +0.545339 0.80327 -0.239507 0.336209 0.906564 -0.255159 0.504298 0.800663 -0.323452 +-0.863022 0.501553 -0.0603085 0.674282 -0.738209 -0.019787 0.792273 -0.607985 -0.0515567 +-0.83483 -0.548247 0.0498406 0.640116 0.735073 -0.223426 0.569923 0.786184 -0.238962 +-0.860948 0.505315 -0.058522 -0.828601 0.553486 -0.0841081 0.847111 -0.511494 -0.144143 +0.838008 -0.525074 -0.148458 -0.845244 -0.528173 0.0812146 0.607048 0.767285 -0.2068 +0.56719 0.795933 -0.211626 0.637268 0.737754 -0.222729 -0.873019 0.481051 -0.0801752 +-0.889695 0.44926 -0.0812946 0.720579 -0.633612 -0.281605 0.559646 -0.819889 -0.12074 +0.563916 -0.818588 -0.109145 -0.846393 -0.523492 0.097849 -0.838657 -0.535475 0.0996064 +0.778682 0.597046 -0.192849 0.722873 0.660975 -0.201412 -0.944739 0.320337 -0.0696634 +0.635403 -0.769735 -0.0614203 0.706902 -0.706216 -0.0393486 -0.747686 -0.657963 0.0897223 +0.84117 0.510537 -0.178283 -0.985739 0.165025 -0.0329462 0.82516 -0.564333 0.0252922 +0.842692 -0.537594 0.0293678 -0.766405 -0.637963 0.0750128 0.879043 0.439522 -0.184673 +0.843606 0.506872 -0.177228 -0.98623 0.1627 -0.029661 0.892631 -0.450742 -0.00643918 +-0.787627 -0.611447 0.0759991 -0.844881 -0.529365 0.0771261 0.879973 0.436878 -0.186507 +-0.987768 0.15465 -0.0199549 -0.985775 0.165093 0.0315033 0.901295 -0.433181 -0.00465786 +0.906341 -0.422545 -0.0011836 -0.894208 -0.440431 0.0800783 0.917305 0.346811 -0.195637 +0.882356 0.432583 -0.185256 -0.996776 -0.0190001 0.0779493 0.860584 -0.507798 0.0391984 +-0.875593 -0.477047 0.0759135 0.914808 0.347158 -0.206418 -0.918262 -0.375653 0.125218 +-0.647596 0.627359 0.432481 -0.574074 0.683601 0.450698 -0.628074 0.664893 0.404278 +-0.658946 0.629894 0.411126 0.833918 -0.547733 0.0675979 0.840194 -0.537841 0.0692894 +-0.895805 -0.441217 0.0534809 0.914241 0.355832 -0.19377 -0.958697 -0.265429 0.102213 +-0.485848 -0.696872 0.527562 -0.374688 -0.745019 0.551866 -0.538516 -0.66768 0.514008 +0.915806 -0.384406 0.116326 -0.911935 -0.4079 0.0446234 0.946198 0.252955 -0.201799 +0.916367 0.351076 -0.192397 -0.980923 -0.115036 0.156704 -0.462568 -0.72496 0.510357 +0.938309 -0.327317 0.11153 0.94166 -0.319294 0.106431 -0.942007 -0.332123 0.0481338 +0.951038 0.208849 -0.227835 -0.973728 -0.156552 0.165366 0.935161 -0.334447 0.116703 +-0.947121 -0.318063 0.0424084 -0.946323 -0.320875 0.0388939 0.939721 0.268262 -0.21204 +-0.970249 -0.173227 0.169145 0.937562 -0.330556 0.108213 -0.940597 -0.336391 0.0460259 +0.938056 0.283766 -0.198817 -0.970969 -0.173905 0.164244 0.942445 -0.316493 0.107842 +0.94166 -0.319294 0.106431 -0.942007 -0.332123 0.0481338 0.965795 0.178257 -0.188318 +0.94359 0.267626 -0.194972 -0.958271 -0.238916 0.156961 0.932844 -0.333618 0.136021 +-0.955365 -0.292313 0.0427776 -0.964187 -0.262245 0.0396281 0.977703 0.0987872 -0.185307 +-0.927979 -0.331878 0.169444 0.934909 -0.329621 0.131511 -0.967903 -0.24577 0.0525545 +0.982451 0.085296 -0.165878 -0.9542 -0.24866 0.166347 0.939229 -0.315413 0.135511 +0.937489 -0.317879 0.141657 -0.968373 -0.243346 0.0550971 0.983436 0.0874351 -0.158771 +-0.970969 -0.173905 0.164244 0.951928 0.306221 -0.00782675 0.744662 0.667362 0.0103067 +-0.447367 0.858657 -0.250141 -0.920055 0.277962 -0.276109 0.930883 -0.332917 0.150409 +-0.959368 -0.259522 0.11073 0.980586 0.0876947 -0.175389 -0.984588 -0.0919425 0.148774 +0.972924 0.205659 -0.105466 -0.939769 -0.284404 -0.189602 0.91166 -0.378968 0.158931 +-0.95251 -0.284331 0.108994 -0.94166 -0.319294 0.106431 0.976638 0.0810446 -0.199021 +-0.960915 -0.208065 0.182621 0.807409 0.585228 -0.0748237 -0.999142 -0.0344532 0.0229688 +0.907595 -0.389554 0.156585 0.898921 -0.414243 0.14263 -0.935161 -0.334447 0.116703 +0.968309 -0.00756491 -0.249642 -0.943049 -0.298862 0.146083 0.827908 0.551939 -0.0996556 +0.829409 0.553701 -0.0741315 -0.988999 0.0766979 0.126484 0.923209 -0.350874 0.156754 +-0.938228 -0.330791 0.101521 0.81747 0 -0.575971 0.886783 -0.136041 -0.441712 +0.953334 -0.135022 -0.270043 -0.914936 -0.375234 0.148631 0.979671 0.163446 -0.116317 +-0.963879 -0.205729 0.169155 0.970969 -0.173905 0.164244 -0.942445 -0.316493 0.107842 +-0.94166 -0.319294 0.106431 0.940581 -0.229456 -0.250316 -0.915144 -0.379766 0.135234 +-0.912054 -0.39573 0.107493 0.975988 -0.184472 -0.115832 0.994562 -0.07154 -0.0756873 +-0.924056 -0.334353 0.185281 -0.96241 -0.212743 0.168844 0.973323 -0.159799 0.164642 +-0.932844 -0.333618 0.136021 0.958965 -0.188685 -0.211623 -0.929024 -0.353084 0.110664 +0.981489 0.19151 0.00177324 -0.914695 0.195669 0.353619 0.927322 -0.339685 0.157127 +-0.942986 -0.305259 0.132647 0.967742 -0.176726 -0.179562 -0.944318 -0.324169 0.0563772 +0.778301 0.622885 -0.0791354 0.799704 0.595128 -0.0793504 -0.8851 0.366684 0.286604 +-0.827431 0.48322 0.286107 -0.5688 -0.724941 0.388494 0.558626 0.791388 0.248278 +0.034426 0.998353 0.0459013 -0.617527 -0.693453 0.371191 0.920138 -0.349707 0.176216 +-0.936023 -0.320393 0.145633 -0.927877 -0.338893 0.155552 0.979754 -0.101184 -0.172753 +0.981068 -0.085914 -0.173564 -0.938781 -0.343883 0.0208414 0.886292 0.462413 -0.0256896 +-0.678384 -0.345007 0.648664 -0.819457 -0.320657 0.475047 -0.940819 -0.210288 0.26578 +0.686347 0.630996 0.361624 0.751684 0.602857 0.267457 0.706789 0.660391 0.25364 +-0.819705 0.102463 0.563547 -0.8922 -0.181026 0.413774 -0.627888 -0.689446 0.361138 +0.933945 -0.320608 0.157981 -0.918705 -0.372095 0.13239 0.981989 -0.0742681 -0.173727 +-0.917171 -0.398376 0.00965761 0.947075 0.290642 0.136296 -0.226875 -0.948751 0.22 +-0.557122 -0.793708 0.244218 0.587346 0.306441 0.749079 0.874185 0.417752 0.247557 +-0.906806 -0.303399 0.292664 -0.913171 -0.31716 0.255985 0.927322 -0.339685 0.157127 +-0.956602 -0.274784 0.0969827 0.94461 -0.272176 -0.183391 -0.933106 -0.359366 0.0130463 +-0.943618 -0.330819 0.0119795 0.368099 -0.609268 0.70235 0.913666 0.201007 0.353284 +-0.66647 -0.719787 0.194228 -0.65525 -0.721163 0.224879 -0.297492 -0.946762 0.12305 +0.898269 0.175236 0.402994 0.92303 0.26147 0.282221 0.910131 0.340691 0.23578 +-0.414276 -0.659075 0.627691 -0.768872 -0.586989 0.253535 -0.867554 -0.438009 0.23558 +0.920138 -0.349707 0.176216 -0.982641 -0.175995 0.0586652 0.91092 -0.365516 -0.19137 +-0.969412 -0.230631 0.0839648 -0.755723 -0.432203 0.49202 0.948314 0.0278916 0.316105 +-0.785132 -0.42864 0.44703 0.556362 -0.568725 0.605816 0.656064 -0.498123 0.566969 +0.276547 -0.769146 0.576139 -0.543138 -0.78524 0.297319 0.970969 -0.173905 0.164244 +-0.982995 -0.179131 0.0404011 0.929544 -0.325885 -0.172474 -0.928036 -0.356484 0.108025 +-0.781546 -0.390773 0.486295 0.32972 -0.74187 0.583879 0.982172 -0.0879557 0.166139 +-0.984305 -0.174911 0.0234358 0.925966 -0.337834 -0.168689 0.922301 -0.342488 -0.17906 +-0.840928 -0.522527 0.140736 0.960749 -0.239534 0.139946 -0.995476 -0.0885055 0.0345593 +0.893041 -0.408998 -0.187614 -0.8376 -0.529841 0.133024 -0.899114 -0.436794 0.0283632 +0.935161 -0.334447 0.116703 -0.997618 0 0.0689857 0.843606 -0.506872 -0.177228 +0.84233 -0.508536 -0.178528 -0.994726 -0.101503 -0.014764 0.344364 0.0153051 0.938711 +0.384816 0.25161 0.888037 0.45747 0.0714796 0.886348 0.334283 -0.276146 0.90111 +0.388453 -0.371265 0.843367 -0.0610664 0.692086 0.719227 0.341072 -0.259577 0.903488 +-0.531593 0.607067 0.590659 -0.487994 -0.350143 0.799538 -0.848697 -0.414609 0.328348 +0.963556 -0.255241 0.0800755 -0.994649 0 0.103311 0.886275 -0.419608 -0.196078 +-0.437057 0.874115 -0.211907 0.405084 0.914226 0.00991031 0.488817 0.86761 0.091162 +0.210173 0.928038 0.307526 -0.469168 0.310748 0.82663 0.0414232 0.925119 0.377412 +-0.66882 0.299209 0.680554 -0.802416 0.0272005 0.596145 -0.797353 -0.246454 0.550898 +0.144292 -0.272552 0.95126 0.319219 -0.520209 0.792137 0.0466797 -0.855794 0.515206 +-0.598057 -0.628148 0.497754 0.994258 -0.089038 0.0593587 -0.994649 0 0.103311 +0.816194 -0.5454 -0.190699 0.813785 -0.548357 -0.192508 -0.67218 -0.640671 -0.371099 +-0.557019 -0.768686 -0.314406 -0.0780157 -0.84517 -0.528773 -0.740772 -0.666695 0.082308 +0 -0.853639 -0.520865 0 -0.999504 0.0314805 0 -0.994853 0.101328 +0 -0.909398 0.415926 0 -0.934997 0.354654 -0.147687 -0.912182 0.382248 +-0.837426 -0.233381 0.494218 0.995086 0 0.0990135 -0.994649 0 0.103311 +0.835057 -0.518216 -0.184747 -0.926832 -0.344868 -0.148485 -0.0234658 -0.863541 -0.503732 +-0.00729327 -0.99772 0.0670981 0.0127155 -0.925688 0.378074 0.995086 0 0.0990135 +-0.994649 0 0.103311 0.820178 -0.547229 -0.166876 0.811536 -0.559388 -0.168805 +-0.956952 -0.264351 -0.119839 -0.982107 -0.175595 -0.0680601 0.995086 0 0.0990135 +-0.994649 0 0.103311 0.803754 -0.56963 -0.17176 -0.932644 -0.351766 -0.0802274 +0.994258 0.089038 0.0593587 -0.994649 0 0.103311 0.797774 -0.57796 -0.171809 +0.754661 -0.632201 -0.175526 -0.297991 -0.736701 -0.60702 -0.39539 -0.691932 -0.604067 +0.0430287 -0.774517 -0.631088 0.48321 -0.683083 -0.547637 0.485352 -0.7371 -0.470232 +0.578083 -0.662522 -0.476323 -0.928843 -0.368139 -0.0415336 -0.975413 -0.220063 0.0118953 +0.98126 0.175193 0.0802233 -0.994649 0 0.103311 0.718864 -0.672108 -0.177497 +0.695091 -0.695091 -0.183567 -0.551784 -0.551784 -0.625355 -0.58159 -0.63064 -0.513855 +-0.626391 -0.584163 -0.516128 0 -0.40364 -0.914918 -0.245541 -0.613852 -0.750264 +0.773549 -0.0521494 -0.631587 -0.627189 -0.137893 0.766564 -0.935451 0.350794 0.0433079 +-0.898769 0.436471 0.0413227 0.982812 0.158012 0.0954571 -0.994649 0 0.103311 +0.185375 0.17796 0.96642 0.857402 -0.483663 0.175877 -0.338906 0.897994 0.280623 +-0.712029 0.66542 0.224121 -0.780361 0.588177 0.212333 0.695091 -0.695091 -0.183567 +0.695091 -0.695091 -0.183567 -0.40364 0 -0.914918 -0.613852 -0.245541 -0.750264 +0 0 -1 0.382859 -0.316729 -0.867814 0.6379 -0.38274 -0.668276 +-0.837997 0.535257 -0.106115 0.979643 0.159679 0.12166 -0.994649 0 0.103311 +0.738928 -0.627898 0.244395 -0.713958 -0.294895 0.63506 -0.909343 0.239301 0.340339 +0.695091 -0.695091 -0.183567 0.673365 -0.714803 -0.188773 -0.564888 -0.451911 -0.690419 +-0.522083 -0.540085 -0.660104 0 -0.342852 -0.939389 -0.229748 -0.742263 -0.629493 +0.0276674 -0.774687 -0.631739 0.58132 -0.72818 -0.36307 0.529586 -0.781548 -0.329728 +-0.912074 0.372537 -0.171281 -0.961153 0.247659 -0.121864 0.973547 0.173 0.149255 +-0.994649 0 0.103311 0.748742 -0.614416 0.248752 0.635278 -0.541163 0.550967 +0.674978 -0.528131 0.51525 0.4346 -0.39114 0.811253 0.647272 -0.736992 -0.194632 +0.592917 -0.77952 -0.201986 -0.374299 -0.374299 -0.848411 -0.522083 -0.540085 -0.660104 +-0.484928 -0.464723 -0.740863 0 0 -1 -0.32432 -0.32432 -0.888613 +-0.15923 -0.641385 -0.750514 0.129084 -0.619922 -0.773973 0.547381 -0.800591 -0.243777 +0.437088 -0.782692 -0.443111 0.570316 -0.795567 -0.204483 0.571862 -0.794706 -0.20351 +-0.940204 -0.335531 -0.0586132 0.971269 0.176995 0.15909 -0.991959 -0.073494 0.103032 +0.890045 0.193488 0.412774 -0.601734 0.657157 0.45394 -0.559559 0.742089 0.36905 +0.593383 -0.776323 -0.212648 0.690387 -0.682013 -0.241294 0.695867 -0.676604 -0.240784 +-0.334529 -0.559575 -0.758265 -0.351027 -0.604546 -0.715055 -0.229748 -0.742263 -0.629493 +-0.511382 -0.665985 -0.543095 0 0.208691 -0.977982 0.576997 -0.552269 -0.601726 +0.695706 -0.683943 -0.219579 0.765076 -0.601888 -0.228887 -0.88332 -0.466227 -0.04877 +-0.696474 -0.717474 0.012444 0.982172 0.0879557 0.166139 -0.991745 -0.0846403 0.0963246 +0.80803 0.512788 0.290062 -0.0340272 0.782625 0.621563 -0.650659 0.679684 0.338633 +-0.575574 0.698237 0.425652 0.573936 -0.783507 -0.238148 0.570736 -0.786242 -0.23682 +-0.304518 -0.790296 -0.531697 -0.352924 -0.764669 -0.53919 -0.269052 -0.739894 -0.616578 +-0.336836 -0.759934 -0.555916 0.22181 -0.208763 -0.95248 -0.201876 -0.232063 -0.951521 +0.689665 -0.515507 -0.508541 0.911446 -0.334489 -0.239547 -0.32526 -0.94218 0.0806431 +-0.310025 -0.947492 0.0783771 -0.513201 -0.857889 0.0255324 0.985993 0 0.166785 +-0.995816 0 0.0913839 0.922812 -0.350879 0.159065 -0.769477 -0.246993 0.588982 +-0.680666 -0.490079 0.544533 0.607283 -0.764151 -0.217441 0.598502 -0.772542 -0.21207 +-0.266017 -0.752103 -0.602972 -0.278994 -0.80958 -0.516471 -0.404761 -0.769046 -0.494708 +-0.0803889 -0.840429 -0.535926 -0.334393 -0.759983 -0.557321 0.968851 -0.186702 -0.162698 +-0.442193 -0.895896 0.0428444 -0.484923 -0.87324 0.0479871 -0.375434 -0.9257 0.0461502 +-0.346325 -0.934029 0.0874559 0.985993 0 0.166785 -0.991745 0.0846403 0.0963246 +0.492928 -0.860757 0.126966 0.0776973 -0.99064 0.112229 -0.0571247 -0.991282 0.11873 +-0.493055 -0.84793 0.19471 -0.653859 -0.553454 0.515904 0.590372 -0.780377 -0.20609 +0.572871 -0.793442 -0.205594 -0.324841 -0.593574 -0.736307 -0.45913 -0.688695 -0.561159 +-0.404494 -0.674157 -0.617977 -0.281767 -0.569727 -0.772022 -0.350017 -0.725961 -0.592004 +0.985853 -0.159009 -0.0530029 -0.864813 -0.480157 0.146788 -0.692506 -0.714244 0.101443 +0.983335 -0.0733832 0.166335 -0.991959 0.073494 0.103032 0.00402836 -0.993661 0.112346 +0.587596 -0.69129 -0.420535 0.58271 -0.785392 -0.208824 0.56284 -0.791292 -0.238892 +0.562033 -0.79094 -0.241935 -0.226493 -0.82773 -0.513385 -0.241248 -0.861599 -0.446595 +-0.365597 -0.8165 -0.446841 -0.0694331 -0.883694 -0.462887 -0.292104 -0.823202 -0.48684 +-0.431539 -0.721202 -0.541887 0.961957 -0.269611 0.0441523 -0.73451 -0.576341 0.358227 +-0.952994 -0.155379 0.260117 0.970003 -0.163404 0.179981 -0.994649 0 0.103311 +0.521358 -0.801554 -0.292741 0.471267 -0.826425 -0.308107 -0.517718 -0.711862 -0.474575 +-0.457399 -0.73991 -0.493274 -0.350017 -0.725961 -0.592004 0.938158 -0.332209 0.097448 +-0.711954 -0.561604 0.421571 -0.738749 -0.211071 0.640077 -0.776984 0.365639 0.512449 +0.526008 0.850448 0.00736319 0.561594 0.827147 -0.0209819 -0.398197 0.911999 -0.0984787 +-0.996499 -0.0379831 -0.0744768 0.973268 -0.0957941 0.20874 -0.994649 0 0.103311 +0.432317 -0.845277 -0.314021 0.277767 -0.902352 -0.329555 0.29672 -0.896508 -0.328984 +0.30655 -0.892601 -0.330593 -0.211224 -0.895818 -0.391017 -0.246372 -0.893097 -0.376401 +-0.19087 -0.90133 -0.388809 -0.380769 -0.826024 -0.415571 0.91681 -0.368812 0.153092 +-0.574699 -0.549468 0.606471 0.00420699 -0.37863 0.925539 -0.202648 0.572181 0.794696 +0 0.447214 0.894427 -0.630304 0.467218 0.620019 -0.614424 0.384409 0.688995 +-0.597018 0.447763 0.665641 0.345204 0.927614 0.14271 0.219501 0.969407 0.109858 +-0.229929 0.965862 0.119341 -0.518612 0.842456 0.14598 0.761176 0.567208 0.314463 +0.480353 0.857773 0.182992 0.412654 0.903095 0.118895 -0.83391 -0.525598 -0.168348 +-0.73334 -0.668785 -0.122223 0.977765 0 0.209704 -0.990783 0.0880882 0.102909 +0.318775 -0.888499 -0.330079 0.414728 -0.854726 -0.312161 0.393909 -0.871766 -0.291306 +-0.167097 -0.925588 -0.339655 -0.210937 -0.922849 -0.322265 0 -0.938787 -0.344499 +-0.123661 -0.931581 -0.341855 -0.122384 -0.945694 -0.301139 -0.20314 -0.91973 -0.335902 +-0.28152 -0.89949 -0.334162 0.896953 -0.419203 0.140515 0.871755 -0.470252 0.137501 +-0.268549 -0.960267 0.0759533 0.175164 -0.967724 0.181186 0.856429 -0.394109 0.333477 +0.445689 -0.869571 0.212622 -0.336085 -0.93357 0.124476 0.0232069 -0.999117 -0.0350139 +0.373427 -0.908543 -0.187355 0 -0.357789 0.933803 -0.323885 -0.689561 0.647769 +-0.677957 0.0739589 0.731372 0.94085 -0.210308 0.265652 0.847716 0.493216 0.195232 +-0.82378 0.554737 0.116848 -0.850412 0.525255 0.0301072 0.724963 0.626742 0.285698 +0.717732 0.67434 0.173569 0.683848 0.706168 0.183517 0.425656 0.243232 0.871582 +-0.379604 -0.827626 0.413445 -0.799874 -0.599905 0.017775 -0.784501 -0.613566 0.0899658 +0.97202 -0.108245 0.208472 -0.980301 0.1742 0.0930774 0.342227 -0.896509 -0.28134 +0.149137 -0.955078 -0.256094 0.0528742 -0.971123 -0.232647 0 -0.972483 -0.232972 +0 -0.90031 -0.435249 -0.0528742 -0.971123 -0.232647 -0.149137 -0.955078 -0.256094 +-0.234589 -0.927496 -0.291064 0.78306 -0.599178 0.166741 -0.502994 -0.864119 0.0171964 +-0.441164 -0.896792 0.0337502 -0.327019 -0.94308 0.0604809 0.970338 -0.188498 0.15137 +-0.82888 -0.559316 0.0110834 -0.893176 -0.448092 -0.0380928 0.963121 0.216896 -0.159229 +-0.759243 -0.172095 0.627641 -0.150614 -0.506612 0.848917 -0.326814 -0.673139 0.663383 +0.987592 -0.127364 0.091869 0.984198 -0.176688 0.0116819 -0.391963 0.905571 -0.162192 +-0.806214 0.586434 -0.0781912 -0.444323 0.654278 -0.611962 -0.927756 -0.0655707 -0.367382 +0.601259 0.720474 0.345551 0.73298 0.662853 0.152857 0.686924 0.703664 0.18164 +-0.284726 -0.260999 0.922394 -0.571962 -0.784995 0.237995 -0.573067 -0.796486 0.192884 +-0.621233 -0.762161 0.182152 0.938767 -0.287472 0.18994 -0.981345 0.178831 0.0705833 +0.803877 -0.572697 0.160625 0.832671 -0.512363 0.210101 -0.544894 -0.838298 0.0186288 +-0.480725 -0.876849 0.00632533 0.994235 0.0699282 0.081284 -0.973598 0.122971 -0.192316 +0.738164 0.649585 -0.182081 0.860037 0.474696 -0.187086 -0.991592 -0.0517088 0.118626 +0.961565 0.272979 -0.0295963 -0.799181 0.592106 -0.103538 -0.967347 0.190745 -0.166901 +-0.911891 -0.333191 -0.239664 0.860748 0.472624 0.189049 0.865778 0.469974 0.171908 +0.863642 0.474633 0.169839 0.114888 0.0774826 0.990352 -0.7072 -0.65397 0.268685 +-0.693397 -0.690052 0.207434 -0.602114 -0.772772 0.200705 0.965312 -0.182386 0.186835 +-0.994258 0.089038 0.0593587 0.750582 -0.592685 0.292147 -0.397157 -0.917569 -0.0182601 +-0.388419 -0.921292 -0.0187255 -0.450752 -0.892649 0 0.929841 0.356706 0.0903121 +-0.39972 -0.799439 0.448466 -0.0508834 -0.85371 0.518257 -0.692984 0.694726 -0.192689 +0.0248318 0.980858 -0.193137 0.162674 0.967991 -0.191129 0.662743 0.717365 -0.214845 +-0.984132 -0.176639 0.0168228 0.856534 0.514883 -0.0352879 0.936277 0.349317 -0.0369269 +-0.942913 -0.331188 0.035067 0.403378 0.541117 0.737887 0.994668 0.0219412 0.100767 +-0.704075 -0.656962 0.26959 -0.603306 -0.784298 0.14456 -0.724981 -0.660808 0.194253 +0.981579 0.0849699 0.17112 -0.998223 0 0.0595954 0.727857 -0.618455 0.296204 +0.0147027 -0.940973 0.338162 0.0770382 -0.969719 0.231757 0.582397 -0.804078 0.119466 +0.66045 -0.737666 0.140195 -0.582227 -0.812493 0.0294295 -0.179542 -0.980125 -0.0843751 +0.229626 -0.971496 -0.0588786 0.08238 -0.99386 -0.073869 -0.01356 -0.997112 -0.0747306 +-0.00279177 -0.997221 -0.0744473 -0.178722 -0.983813 -0.0130727 -0.416589 -0.909073 0.00627838 +0.765165 0.633543 0.114658 0.792715 0.598654 0.114961 -0.575386 -0.405201 0.710453 +-0.0360068 -0.711135 0.702133 0.631299 -0.26971 0.72713 0.13398 0.973679 -0.184389 +-0.786589 -0.587223 -0.190908 0.683335 0.729251 -0.0352922 0.72863 0.683751 -0.0397755 +-0.798233 -0.578233 0.168732 -0.866879 -0.482376 0.125837 0.323581 -0.942908 0.0788656 +-0.502939 -0.858277 0.102046 0.973728 0.156552 0.165366 -0.998223 0 0.0595954 +0.777952 0.529219 -0.3387 0.749279 0.613653 -0.249019 0.309423 0.948679 -0.0653135 +0.20013 0.979296 0.0304582 0.089334 0.982674 0.162392 -0.235125 0.953014 0.190998 +-0.546514 0.805437 0.229332 -0.568801 0.78387 0.249027 0.485678 -0.857445 0.170016 +-0.159158 -0.968464 0.191691 -0.636435 -0.766822 0.0832775 0.799041 0.585829 0.135416 +-0.0232585 -0.424468 0.905144 0.756611 0.346981 0.554206 -0.707849 -0.671763 -0.218369 +-0.847774 -0.51472 -0.127839 -0.656344 -0.725433 -0.207267 0.407615 0.912807 0.0251809 +0.530167 0.847803 -0.012388 -0.558937 -0.792326 0.24456 -0.621979 -0.753065 0.214559 +0.322649 0.875361 -0.360058 0.278898 0.756639 -0.591366 -0.26714 0.252299 -0.930044 +-0.327934 0.213539 -0.92025 -0.509336 -0.0836223 -0.856495 -0.744883 -0.440047 -0.501506 +-0.865884 -0.368839 -0.337938 0.974931 0.158911 0.155747 -0.998223 0 0.0595954 +0.886257 -0.418595 -0.198309 -0.819473 -0.281649 0.499138 -0.916385 -0.078894 0.392447 +-0.898507 -0.211238 0.384791 0.989082 -0.0196767 0.146045 0.938853 0.309822 0.150217 +-0.784379 -0.182088 0.592953 0 -0.471215 0.882018 0.604551 -0.453413 0.654931 +-0.170221 -0.958912 0.226962 0.340323 0.850809 0.400381 0.0535363 0.963654 0.261733 +0.322316 0.799344 0.507111 0.240544 0.955847 0.168803 0.119379 0.967217 0.224141 +0.0841203 0.96101 0.263407 0.242414 0.965428 0.0958381 -0.667303 -0.696589 0.263574 +-0.78311 -0.594693 0.181875 -0.583646 -0.781176 0.221631 0.418615 0.897032 0.141753 +0.409875 0.900101 0.147717 0.327479 0.936939 0.122079 -0.0806806 0.996095 0.035858 +-0.478523 0.849237 -0.223185 -0.616534 0.561904 -0.551498 -0.560483 0.552552 -0.616884 +0.43921 0.380418 -0.813865 0.323313 0.0668923 -0.943925 0.281222 0.55646 -0.781836 +-0.414636 -0.763803 -0.494653 -0.560148 -0.737139 -0.37797 -0.491984 -0.242527 -0.836141 +-0.484683 -0.846938 -0.218583 0.972022 0.172729 0.159181 -0.982641 0.175995 0.0586652 +0.524648 -0.851137 -0.0176141 0.283137 -0.958311 0.0383739 -0.286332 -0.94321 0.16843 +-0.486938 -0.844547 0.222782 -0.889608 -0.255526 0.378556 0.959574 -0.242135 0.143488 +0.882036 -0.450006 0.139671 -0.742364 0.593891 0.310143 0.918054 0.0612036 0.391703 +-0.299391 -0.953943 -0.0189158 -0.408799 -0.907228 -0.0991028 -0.149332 -0.981512 0.119729 +-0.188888 -0.969078 0.158775 -0.214925 -0.965081 0.149754 -0.06556 -0.971108 0.22946 +0.891861 0.0964174 0.441913 0.942344 -0.105825 0.317474 -0.98989 0.00856306 0.141576 +0.574859 0.817835 0.0261383 0.550082 0.833389 0.0536054 0.453329 0.880641 0.137707 +-0.874919 -0.271263 -0.401164 -0.337804 -0.855989 -0.391372 -0.25232 -0.820983 -0.512173 +-0.290521 -0.933019 -0.212304 0.972073 0.177141 0.153934 -0.929024 0.353084 0.110664 +0.180625 -0.981099 0.0694205 0.0125628 -0.995155 0.0975114 -0.158983 -0.977415 0.139228 +-0.314324 0.679726 0.662701 0.0455877 0.747638 0.662541 -0.27552 0.74046 0.613032 +-0.470453 0.623969 0.623969 0.852567 0.518954 0.0617802 0.078002 0.972492 0.219486 +-0.282537 0.936832 0.206202 0.0781423 0.967762 0.239436 0.290705 0.935707 0.19986 +0.162957 0.972994 0.163484 -0.649065 0.740365 0.174855 -0.859571 0.508927 0.0461662 +0.938298 -0.295476 0.179694 -0.323151 -0.799539 0.506271 0.539562 -0.756329 0.369918 +0.0591047 -0.990004 0.12806 -0.447683 0.826491 0.34131 -0.372896 0.864362 0.337382 +-0.623361 -0.749652 -0.222359 -0.841719 -0.497379 -0.210055 0.923711 -0.300417 0.237713 +-0.747978 0.635781 0.190556 0.615968 0.782256 -0.0930588 0.645985 0.760378 -0.0672901 +-0.924648 -0.344908 -0.161446 0.982172 0.0879557 0.166139 -0.93006 0.340688 0.137551 +-0.468715 0.338517 0.815912 0.998236 -0.0576002 -0.0143583 -0.338731 0.193561 0.920758 +-0.981036 -0.193008 -0.0177724 0.970405 -0.193783 0.144095 -0.191495 -0.833568 0.518164 +-0.464684 -0.883052 0.0654866 -0.676968 -0.734808 0.0421014 -0.0893582 -0.994723 0.0504072 +-0.0905864 -0.990233 0.10598 -0.180049 -0.978796 0.0976737 0.527412 -0.846036 0.0778419 +0.750225 -0.653257 0.102071 -0.877754 0.455368 -0.148952 0.615922 -0.74676 0.250977 +0.408844 -0.906834 0.102467 -0.738009 0.654412 0.164583 -0.228873 0.972709 -0.0381454 +0.337487 0.935758 -0.102269 0.581949 0.807511 -0.0962396 -0.913794 -0.406131 -0.0061535 +-0.90515 -0.419733 0.0672855 0.985993 0 0.166785 -0.973323 0.159799 0.164642 +0.893689 -0.438371 -0.0956583 0.734908 -0.666249 -0.126582 -0.34802 -0.912492 0.215037 +0 -0.9837 0.179816 -0.256757 -0.960868 0.103963 -0.534985 -0.841855 0.0712107 +0.992076 0.0888426 0.0888426 -0.77454 -0.580905 0.250276 -0.731025 -0.629021 0.264453 +-0.151516 -0.925564 0.346949 0.144294 -0.945232 0.292771 0.0801606 -0.921847 0.379173 +-0.0369768 -0.90593 0.421809 -0.288508 -0.601059 0.745313 -0.0283629 -0.855615 0.516835 +0.630647 -0.667527 0.395845 0.752454 -0.620057 0.222132 -0.859289 -0.492892 0.136672 +-0.858604 -0.506671 0.0779977 0.78356 -0.609436 0.12092 0.773686 -0.613753 0.157216 +-0.83706 0.506832 -0.206039 0.397346 -0.914024 0.0817053 0.640441 -0.765318 0.0642225 +0.626561 -0.775742 0.0751431 -0.904265 -0.368404 0.215833 0.985993 0 0.166785 +-0.985993 0 0.166785 0.127883 -0.989553 -0.0665629 -0.234956 -0.971224 0.0389789 +-0.222632 -0.960744 0.165547 -0.0664783 -0.986728 0.148152 0.854386 0.515038 0.0689927 +-0.883359 -0.419596 0.208844 0.746926 -0.630354 0.211557 0.391488 -0.897001 0.205246 +-0.644467 -0.731907 0.221299 0.74956 -0.643764 0.154037 0.728101 -0.667812 0.154586 +-0.84479 0.495855 -0.20114 -0.939313 0.337442 -0.0618315 0.577231 -0.812488 0.0816531 +0.54668 -0.829782 0.112265 0.0757077 -0.791146 0.606924 0.122598 -0.98078 0.151787 +-0.935367 -0.344489 0.0801019 0.982172 0.0879557 0.166139 -0.991737 0 0.128284 +0.698525 0.709022 0.0966993 0.709161 0.698746 0.0940498 -0.921047 -0.292973 0.256591 +-0.924892 -0.296325 0.238257 0.433101 -0.880099 0.194548 -0.605116 -0.759848 0.237625 +0.759829 -0.631018 0.156451 0.772156 -0.617008 0.151905 -0.968309 -0.239311 0.0714704 +0.526635 -0.840004 0.130571 0.163394 -0.972194 0.167751 -0.29119 -0.934755 0.203569 +-0.25437 -0.951374 0.173732 0.796281 -0.566678 0.21169 0.828117 -0.516914 0.216846 +-0.183889 -0.981474 0.0537944 -0.320398 -0.946777 0.0309718 0.2411 -0.966844 0.0841678 +0.233528 -0.969895 0.069054 -0.903472 -0.428388 0.0148887 -0.611621 -0.79001 -0.0424737 +0.95884 0.249869 0.134877 -0.995086 0 0.0990135 0.842244 0.527974 0.108947 +0.749169 0.654664 0.100806 -0.763318 -0.501609 0.407103 -0.924073 -0.100311 0.368819 +0.925196 0.201129 0.321807 0.380651 0.827094 0.413547 -0.0736462 0.97213 0.222575 +-0.113333 0.988034 0.104615 0.125218 0.990362 0.059194 0.0616862 0.99647 -0.0569411 +-0.604642 0.77921 -0.165047 -0.654665 0.737226 -0.167066 0.595467 -0.782614 0.181476 +-0.473794 -0.85559 0.208531 -0.6647 -0.724096 0.184008 0.840159 -0.507061 0.192413 +-0.64744 -0.761694 -0.0253898 -0.490858 -0.871197 0.00863829 0.167001 -0.983267 0.0727743 +-0.236405 -0.971655 0 0.932844 0.333618 0.136021 -0.982641 0.175995 0.0586652 +-0.39749 0.0709803 0.914857 0.80754 0.13459 0.574251 -0.39443 0.302396 0.867745 +-0.663323 0.151617 0.732813 0.89597 0.443798 0.0167471 -0.912339 -0.180567 0.36747 +-0.861592 -0.292516 0.414841 -0.899625 -0.282235 0.333194 0.886292 -0.462413 0.0256896 +-0.841918 0.528263 -0.110055 -0.0856621 -0.980355 0.177669 0.73686 -0.653738 0.172233 +-0.147438 -0.989063 0.0040955 -0.406043 -0.912823 -0.043394 0.960749 0.239534 0.139946 +-0.929024 0.353084 0.110664 -0.320564 0.0674871 0.94482 0.39365 0.919182 -0.0119893 +0.726182 0.68718 -0.0210488 -0.724245 -0.654157 0.218052 -0.449876 -0.736161 0.505646 +-0.84227 -0.409501 0.350558 0.746128 -0.664265 -0.0452199 -0.848602 0.516435 -0.114763 +-0.352292 -0.935056 -0.0395094 0.922438 -0.359595 0.140711 0.987849 0.0884641 0.127781 +-0.934583 0.342345 0.096723 0.257508 0.835331 0.485707 0.0494662 0.831032 0.554021 +-0.217452 0.848061 0.483226 0.322634 0.939848 0.112221 0.0790893 0.996705 0.0179748 +-0.180111 0.978306 -0.102358 -0.333222 0.928936 -0.161372 0.916886 0.387913 0.0940396 +0.161262 0.983526 0.0816756 0.238774 0.971075 0 -0.777287 -0.606089 0.168764 +-0.74616 -0.633307 0.205349 0.776905 -0.629617 -0.00107903 0.762229 -0.64725 0.00859036 +-0.872436 0.473766 -0.120006 -0.863423 0.485162 -0.13827 0.901675 0.430157 -0.0441187 +0.983641 0.178108 -0.026986 -0.213526 0.97368 0.0797165 -0.264783 0.957141 0.117354 +-0.398744 0.886098 0.236293 0.992076 0.0888426 0.0888426 -0.964809 0.244802 0.0960009 +0.195266 0.78616 0.586365 0.747579 0.615653 0.249193 -0.883377 0.376598 0.278961 +-0.841635 0.479792 0.247892 0.631517 0.758482 0.16091 0.509108 0.846123 0.157752 +-0.73302 0.640187 -0.229873 -0.985746 0.0789435 -0.148572 0.94526 0.322741 0.0481914 +-0.707235 -0.681672 0.18746 -0.749768 -0.638475 0.173774 0.745203 -0.66536 0.0443573 +-0.561706 0.6366 -0.528419 -0.843485 0.316307 -0.434147 0.907987 -0.404496 0.109285 +-0.869999 -0.39875 0.29 -0.80228 0.566315 -0.188772 0.965174 0.25152 0.0719457 +-0.98126 0.175193 0.0802233 0.420999 0.90047 0.109148 0.214094 0.961517 0.172189 +0.177858 0.966364 0.185763 0.289185 0.937659 0.19279 -0.887417 0.371686 0.272656 +0.867886 0.286595 0.405756 0.879213 0.432113 0.200654 0.713733 0.678553 0.173641 +-0.743582 -0.659084 -0.112664 0.796122 0.601769 0.0637452 -0.783658 -0.602979 0.149319 +-0.730793 -0.660824 0.171037 0.560643 0.824475 -0.076951 0.0938288 0.995588 0.000955 +-0.771634 0.635276 0.031711 -0.801444 0.59723 0.031681 0.727177 -0.677801 0.108628 +0.717909 -0.686696 0.11426 -0.403455 -0.14483 -0.903464 -0.754303 -0.258618 -0.603443 +-0.739379 -0.285893 -0.609577 -0.902111 0.0674808 -0.426194 0.898344 -0.425962 0.107401 +0.530778 -0.836912 0.133612 -0.654043 -0.744256 0.135319 0.935161 0.334447 0.116703 +-0.968373 0.243346 0.0550971 0.271817 -0.888631 -0.369392 0.905576 -0.424178 -0.00248785 +-0.898645 0.246485 0.362881 -0.878041 0.269225 0.395679 0.942709 0.00771658 0.333528 +0.944479 0.0718895 0.320611 -0.532907 -0.836714 -0.126171 -0.418588 -0.906668 -0.0523235 +0.688407 0.716801 0.110871 0.762943 0.642586 0.0707184 -0.90686 -0.392522 0.153399 +0.998228 -0.0462258 0.0374804 -0.997735 -0.057916 0.0342231 0.491553 -0.849046 0.193642 +-0.451911 -0.891608 0.0284989 -0.72445 -0.678208 -0.123311 -0.154193 -0.90974 -0.385483 +0.521877 -0.84215 0.135751 0.0316642 -0.987177 0.156458 0.94166 0.319294 0.106431 +-0.945479 0.321611 0.051337 0.504367 -0.852206 0.139136 0.0773271 -0.947258 0.311004 +-0.270119 -0.764532 0.585257 -0.393708 0.51711 0.759994 -0.337067 0.41068 0.847188 +-0.511949 0.500043 0.698473 0.749796 0.644924 0.147914 0.372719 0.873962 0.311884 +-0.27968 0.925729 0.254567 -0.387366 0.867965 0.31078 -0.209284 0.863295 0.459262 +-0.221396 0.846047 0.484962 -0.225881 0.797226 0.55983 -0.22643 0.769375 0.597321 +-0.370753 0.750334 0.547303 0.936041 -0.0744578 0.343924 0.22259 -0.926929 0.302087 +-0.0853507 -0.971545 0.220943 -0.27938 -0.954058 0.108262 -0.394411 -0.91891 -0.00662875 +0.899368 0.412949 0.143561 0.751775 0.647856 0.122955 -0.938592 -0.319268 0.130814 +0.733675 -0.669384 0.116817 -0.938927 -0.333841 0.0834602 0.137194 -0.968931 0.205791 +-0.146459 -0.97814 0.147622 -0.020719 -0.80804 0.588764 0.150539 -0.784057 0.602156 +0.0168332 -0.52183 0.852883 -0.249279 -0.807447 0.534686 0.937562 0.330556 0.108213 +0.942445 0.316493 0.107842 -0.93994 0.336695 0.0561158 0.236437 -0.956391 0.171506 +0.0787831 -0.982312 0.169871 -0.0329484 -0.98484 0.170308 -0.0917315 -0.974491 0.204825 +-0.187575 -0.935656 0.298936 -0.22054 -0.803395 0.5531 -0.0340662 0.0170331 0.999274 +-0.393818 -0.393818 0.830551 0.120903 -0.70527 0.698553 -0.70527 0.120903 0.698553 +-0.671994 -0.186091 0.716794 0.954748 0.168736 0.244917 0.953272 0.197675 0.228465 +0.935294 0.277849 0.219148 -0.580904 0.783082 0.22211 -0.792735 0.0468434 0.607764 +-0.94386 0.152858 0.292854 0.667641 0.731633 0.137727 0.784931 0.605469 0.131491 +-0.600932 0.348833 0.719164 -0.714862 0.194962 0.671537 0.987926 0.0177048 0.153913 +-0.960602 -0.241393 0.137742 0.729484 -0.66932 0.140939 0.173191 -0.899037 0.402166 +0.424978 0.882816 0.200076 0.39292 0.894521 0.21318 -0.103486 0.952782 0.285478 +-0.21034 0.893083 0.397692 -0.539139 0.787489 0.298648 -0.0822085 -0.829234 0.552823 +0.935161 0.334447 0.116703 -0.944881 0.32297 0.0537605 -0.223261 -0.458925 0.859967 +0.784895 -0.242509 0.570201 0.829037 -0.138173 0.541854 -0.661083 -0.669713 0.338309 +-0.687977 -0.651833 0.319062 0.573134 0.807597 0.138941 0.65065 0.744605 0.149055 +-0.9574 -0.109048 0.267382 0.991783 0.117286 0.0510981 -0.951919 -0.257508 0.165949 +0.895763 0.0453551 0.442212 0.912843 0.0370907 0.406624 0.882829 -0.0764655 0.463428 +-0.517766 0.534577 0.667941 -0.0185995 0.658955 0.751953 -0.034456 0.955934 0.291551 +0.343907 0.910341 0.230234 0.45066 0.870621 0.197292 -0.777171 0.564647 0.277809 +-0.92431 0.299776 0.236187 0.915302 0.380062 0.133326 -0.945479 0.321611 0.051337 +0.61184 0.772478 0.170087 0.0868562 0.959366 0.268465 -0.18378 0.966933 0.176819 +-0.0796151 0.987227 0.138 -0.133291 0.911708 0.388616 -0.0562991 0.925181 0.375327 +-0.264587 0.841867 0.470376 0.609402 -0.370421 0.701012 -0.666254 -0.674645 0.317743 +0.299222 0.933099 0.199481 0.18655 0.967198 0.172417 0.474731 0.864116 0.167134 +-0.969563 -0.217657 0.112126 0.949223 0.314029 0.019032 -0.946577 -0.28256 0.155408 +-0.937489 -0.317879 0.141657 0.902487 -0.0638929 0.425952 0.83711 -0.413388 0.358269 +0.403713 0.858964 0.314954 -0.982239 0.010807 0.187322 0.170172 0.96184 0.214256 +0.322634 0.939848 0.112221 0.235282 0.957355 0.167672 -0.251342 0.952172 0.173767 +-0.650482 0.757175 0.0596562 0.890888 0.428295 0.151266 0.907316 0.395497 0.142689 +-0.916348 0.396628 0.0547073 0.79735 0.601228 -0.0525137 0.816542 0.569431 0.0949051 +-0.936468 -0.248335 0.247703 0.622716 0.770025 0.138877 0.639269 0.757837 0.130452 +0.328412 0.921155 0.208854 -0.90309 -0.429338 -0.00986984 0.962071 0.272799 0 +0.952346 0.304588 0.0162447 -0.900467 -0.409868 0.145494 0.882518 -0.468838 -0.0367716 +-0.984232 -0.0461571 0.170751 0.756585 0.596597 0.267678 0.734355 0.622262 0.271132 +0.230891 0.935931 0.265936 0.189247 0.96344 0.189657 0.189054 0.962454 0.194782 +-0.189464 0.964546 0.183723 -0.487393 0.856514 0.169799 -0.391319 0.904234 0.170972 +0.734944 0.673608 -0.0781624 0.68022 0.730716 -0.0579226 0.216172 0.976324 0.00774349 +0.0927428 0.99513 0.0333874 -0.705545 0.70769 0.0371716 -0.849536 0.263464 -0.457029 +-0.909507 0.196297 -0.36642 0.898921 0.414243 0.14263 -0.907331 0.412992 0.0786651 +0.972589 0.00495136 -0.232478 0.963906 0.19031 -0.18619 -0.849418 -0.501891 0.163079 +0.479853 0.870845 0.106634 0.620652 0.778636 0.0922828 -0.847093 -0.529328 -0.0473859 +0.936479 0.350724 0 -0.886935 -0.436849 0.150029 0.979753 -0.188784 -0.0666717 +-0.98167 0.0994456 0.162586 0.905524 0.109557 0.409908 0.967228 0.00614113 0.253834 +-0.430995 0.889658 0.150838 -0.4468 0.880649 0.157567 -0.423661 0.90028 -0.100031 +-0.290129 0.928414 -0.232104 0.462084 0.873942 -0.15068 0.625276 0.769006 -0.132889 +-0.676037 -0.730851 -0.0939666 -0.627252 -0.774974 -0.0772701 0.915704 0.380649 0.12881 +0.910762 0.390913 0.133042 -0.94485 0.320375 0.0679584 0.935441 -0.261671 -0.237653 +0.463751 -0.879528 -0.106609 -0.646283 -0.752002 0.129656 -0.833262 -0.528879 0.161124 +0.556124 0.826158 0.0904978 0.545323 0.831994 0.102023 0.496161 0.858403 0.130261 +-0.84961 -0.52248 -0.0719597 -0.829593 -0.549583 -0.0986588 0.922822 0.385228 0 +-0.904864 -0.404737 0.131941 0.979963 -0.199179 0 -0.988907 0.0111331 0.148118 +0.950273 0.291417 0.109809 0.32035 0.944621 0.0711888 -0.209442 0.977802 -0.00608843 +-0.334132 0.942514 0.00479042 -0.417537 0.908529 0.0154643 0.876575 -0.443532 0.186803 +-0.372264 0.908099 -0.191772 -0.0882881 0.975584 -0.201101 0.304687 0.935642 -0.178157 +-0.435788 -0.896981 0.0742598 -0.528449 -0.84741 0.0513582 0.904864 0.404737 0.131941 +-0.94561 0.317556 0.0705679 -0.940309 0.331525 0.0768753 0.328661 -0.943846 0.0337088 +-0.130234 -0.985437 0.109332 -0.613055 -0.778677 0.133517 0.737178 0.641024 -0.213675 +-0.941726 -0.325287 -0.0856723 0.0731249 0.967077 0.24375 0.0333514 0.9682 0.247945 +-0.0491532 0.966679 0.251227 0.950925 0.309421 0 0.935084 0.354427 0 +-0.915704 -0.380649 0.12881 0.957916 -0.280314 0.0618193 -0.986087 -0.143278 0.084281 +0.961897 -0.242722 0.125856 -0.567532 0.82305 0.0222562 -0.790305 0.609033 -0.0670562 +0.832218 -0.523935 0.181399 0.668505 -0.724698 0.167072 -0.336249 -0.939275 0.0685467 +-0.373255 -0.914115 0.158351 -0.308917 -0.940954 0.13848 -0.346823 -0.931835 0.106756 +0.891109 0.438905 0.115268 -0.909999 0.407034 0.0788965 0.655354 0.472465 -0.589311 +0.715155 0.131873 -0.686413 -0.970202 -0.240965 0.0253648 -0.980567 -0.182926 0.0709014 +0.55495 0.793739 0.249016 0.564847 0.796469 0.215836 -0.74402 0.6392 0.194569 +-0.795926 0.597597 0.0968449 0.987001 0.160715 0 -0.910762 -0.390913 0.133042 +-0.898921 -0.414243 0.14263 0.9271 -0.356142 0.11683 0.855212 -0.493465 0.158443 +-0.966198 -0.256732 -0.0234648 0.835548 -0.538439 0.109282 -0.752948 0.649358 -0.106788 +-0.63996 0.734769 -0.224867 0.644276 -0.746059 0.168239 0.421389 -0.893143 0.157251 +0.302968 -0.939202 0.161583 -0.0432635 -0.822006 -0.567833 -0.730952 -0.472755 -0.49215 +-0.00879051 -0.959924 -0.280124 -0.0714231 -0.975179 -0.209583 -0.187769 -0.964695 -0.18468 +-0.312614 -0.94709 -0.0727582 -0.331391 -0.941849 0.0556724 0.69814 0.679073 0.226847 +0.704182 0.678293 0.209874 0.136649 0.976065 0.169185 -0.782013 0.616988 0.0882119 +-0.962902 0.26848 0.0271878 0.87301 0.47564 0.1078 -0.894208 0.440431 0.0800783 +0.58504 0.645561 -0.490896 0.565425 0.565425 -0.60049 0.345295 0 -0.938494 +0.183808 -0.76865 -0.612692 0 -0.87317 -0.487416 0.256686 -0.81284 -0.52288 +0.369262 -0.812376 -0.45132 -0.324628 -0.945655 -0.018819 0.125901 -0.99192 -0.0155877 +0.279469 -0.958763 -0.051678 -0.979733 -0.139762 0.143489 0.842539 -0.401381 0.359194 +0.816503 0.468688 0.337126 0.656996 -0.399171 0.639546 -0.987105 0.151862 -0.0506207 +1 0 0 -0.872008 -0.466563 0.148061 0.62707 -0.777092 0.0539647 +0.618165 -0.784549 0.0485288 -0.925432 -0.311053 -0.216385 -0.912262 -0.334167 -0.236878 +0.834184 -0.540728 0.108402 0.871034 -0.478699 0.110216 -0.603543 0.766511 -0.219538 +-0.219701 0.957567 -0.186539 -0.164018 0.973854 -0.157183 -0.109557 0.99348 -0.031539 +-0.307155 0.950999 0.035441 0.17421 -0.968971 0.175348 0.166698 -0.972992 0.15968 +0.353095 -0.920885 0.165211 0.54846 -0.822689 0.14958 -0.77583 -0.573902 -0.262153 +-0.890532 -0.287568 -0.352502 0.620804 0.64591 0.444301 0.626694 0.663362 0.408907 +0.618814 0.673415 0.404453 0.726395 -0.333287 0.601057 0.229135 -0.910085 0.345316 +-0.682228 -0.730959 0.0162435 0.894208 0.440431 0.0800783 -0.907331 0.412992 0.0786651 +0.681032 0.376741 -0.627902 -0.241627 -0.638585 -0.730634 -0.366851 -0.791307 -0.489135 +0.567982 -0.743976 -0.351989 0.601696 -0.75212 -0.268843 0.561811 -0.783055 -0.266819 +-0.544378 -0.838636 -0.0185274 -0.466562 -0.881284 -0.0752192 0.821962 -0.557699 -0.115544 +0.99236 -0.0870827 -0.0874017 -0.994794 0.0306877 0.0971778 0.779985 -0.548672 0.300969 +0.314976 -0.926439 0.206159 0.0796151 -0.987227 0.138 -0.118885 -0.990709 0.0660473 +1 0 0 -0.866056 -0.478626 0.144445 -0.876693 -0.463248 0.129658 +0.692094 -0.711521 -0.12142 0.673143 -0.641088 -0.368626 -0.840155 0.228775 -0.491734 +-0.794007 0.174681 -0.582271 0.904379 -0.415125 0.0988393 -0.188828 0.964867 -0.182688 +-0.540485 0.83688 0.0866467 -0.750966 0.659385 -0.0355224 0.472117 -0.881286 0.020983 +-0.689591 -0.678858 -0.252224 -0.76161 -0.58942 -0.269323 0.690456 0.0470766 0.721841 +0.844057 0.19916 0.497899 0.776658 0.44287 0.44796 -0.227808 -0.672703 0.703971 +-0.686188 -0.648067 0.330387 -0.470251 -0.853687 0.223791 0.865962 0.49676 0.0577907 +-0.94485 0.320375 0.0679584 0.810314 0.450174 -0.375145 -0.385487 -0.416151 -0.82354 +-0.124783 -0.945051 -0.302172 -0.435675 -0.89242 -0.117358 0.845825 0.506554 -0.167283 +-0.90183 0.432091 -0.00038614 1 0 0 -0.89611 -0.430805 0.106743 +0.933381 0.285803 -0.217065 -0.863862 0.284682 -0.41557 -0.920171 0.288747 -0.264404 +0.909999 -0.407034 0.0788965 -0.76547 0.642157 -0.0411043 -0.682768 0.720467 -0.121474 +0.487663 -0.624719 0.609845 -0.232717 -0.867757 0.439137 0.87627 0.474048 0.0861905 +-0.946459 0.317841 0.056505 -0.942007 0.332123 0.0481338 0.477676 0.877509 0.0424601 +0.0474296 0.986536 -0.156518 -0.909538 0.400197 -0.112176 0.665187 0.741896 -0.0843615 +0.837273 0.520343 -0.16798 -0.897915 0.440169 0.000488263 -0.814913 0.579494 0.0102154 +1 0 0 -0.946887 -0.315629 0.0615072 -0.964187 -0.262245 0.0396281 +0.913814 0.396829 -0.0864277 -0.982288 0.0762009 -0.171185 0.88096 -0.463637 0.0945998 +-0.765014 0.639602 -0.0752473 -0.938451 0.345317 -0.00812512 0.802708 0.596364 -0.00310001 +-0.911935 0.4079 0.0446234 0.405731 0.894223 0.189072 0.330252 0.920704 0.207937 +0.0132112 0.95121 0.308262 -0.855886 0.109428 0.505455 -0.880425 0 0.474186 +-0.894675 -0.0378345 0.445112 0.784203 0.615061 0.0820082 0.523255 0.850394 0.0550795 +-0.978238 -0.19341 0.0751261 0.748462 0.656814 0.0916485 0.683425 0.727721 -0.0579108 +-0.84381 0.536501 -0.0122825 -0.875652 0.482762 -0.0132554 1 0 0 +-0.998383 -0.0380227 0.0422474 0.68437 -0.28888 0.669467 0.756813 -0.24925 0.604242 +0.701308 -0.137098 0.699551 -0.622639 0.437793 0.648582 -0.623985 0.339192 0.703983 +-0.666768 0.35864 0.653298 0.78887 0.613957 -0.0272198 0.909601 0.407789 -0.0795857 +-0.964919 -0.225766 -0.134017 0.874059 -0.476451 0.0949511 0.466345 -0.875391 0.12733 +-0.191218 -0.978682 -0.0749441 -0.18324 -0.980797 -0.0667926 0.185905 -0.979727 -0.0746564 +0.206426 -0.976194 -0.0665889 -0.91413 -0.36041 0.185666 -0.392605 0.326002 0.859991 +-0.111041 0.142767 0.983508 -0.441563 0.454179 0.773786 -0.493862 0.409681 0.766982 +0.804592 0.572029 0.159418 0.789212 0.592309 0.162218 -0.696034 0.685325 0.214164 +-0.671285 0.706616 0.223762 -0.817155 0.547715 0.179627 0.754161 0.651321 0.0837957 +0.77317 0.634173 0.00579153 -0.896946 0.441779 0.0178497 0.862379 0.493586 0.112583 +0.599199 0.783999 0.162192 -0.452839 0.251577 0.855363 -0.832275 -0.488101 0.262824 +-0.89702 -0.0967048 0.431281 0.883359 0.462319 0.0770532 -0.887349 -0.450996 0.0959912 +0.627455 0.778103 0.029252 -0.944143 0.328398 -0.0273665 -0.993313 0.0180057 -0.114036 +0.999408 0 -0.0344137 -0.998955 -0.0284287 0.0357992 0.670098 -0.383875 0.635302 +-0.640411 0.15137 0.752968 0.682656 0.730145 0.0294609 0.709228 0.704913 -0.00970806 +-0.862109 -0.49602 -0.103594 -0.793538 -0.602136 -0.0879165 0.320821 -0.937495 0.13482 +0.0602766 -0.997713 0.0305881 -0.110478 -0.993817 0.0110316 0.467763 -0.883552 0.0230994 +0.402916 -0.895479 0.189144 0.238213 -0.944344 0.226869 -0.786527 -0.566171 0.246628 +0.857732 -0.219715 0.464781 0.859164 -0.271201 0.433921 -0.390518 0.836011 0.385462 +-0.0214505 0.87782 0.478511 -0.315553 0.930052 0.188225 0.940861 0.0775968 0.329786 +0.966406 -0.211753 0.145674 -0.816968 -0.576683 0 0.934871 0.354583 0.0169167 +-0.877317 0.477987 0.0429295 0.995081 0.0990068 -0.003342 -0.506055 -0.815438 0.281014 +-0.580456 -0.792608 0.186665 -0.702755 -0.679441 0.210938 0.873762 0.479722 0.0800368 +-0.879136 -0.467433 0.0928828 -0.839357 -0.533423 0.104593 0.792351 0.598665 -0.117385 +0.608994 0.793169 -0.00289584 -0.386139 -0.91992 -0.0681422 -0.501173 -0.864928 -0.0269448 +0.0353772 -0.995515 -0.0877355 0.225014 -0.964347 -0.139295 0.131541 -0.978663 -0.157849 +-0.92299 -0.301215 -0.239498 0.994016 0.0808006 -0.0735061 -0.999674 0.0237077 -0.00948307 +0.786206 0.614147 0.0685787 0.745826 0.664091 0.0522191 -0.827312 -0.561551 0.0146811 +0.398312 -0.895884 0.196823 0.890365 -0.257455 0.375455 0.947689 -0.238664 0.211953 +-0.311313 0.939372 0.143747 -0.421666 0.893895 0.152152 -0.474223 0.868607 0.143646 +0.783056 -0.613877 0.099896 0.955642 0.293845 0.0201067 0.948216 0.31657 0.0258831 +-0.869706 0.493266 0.0173076 0.397071 -0.887569 -0.233571 -0.210322 -0.974072 0.0833535 +-0.42977 -0.883827 0.184791 0.817617 0.557814 0.142637 0.870774 0.484908 0.0813462 +-0.896909 -0.426012 0.118609 -0.899513 -0.419499 0.122055 0.838843 0.514054 -0.17914 +-0.46401 -0.882192 -0.0801993 -0.427994 -0.899229 -0.0905994 -0.449055 -0.888681 -0.0927182 +0.0184781 -0.999355 0.0307968 0.203288 -0.969071 0.139911 0.141264 -0.979215 0.145544 +-0.374923 -0.917243 0.134529 -0.648587 -0.760662 0.0269943 0.963864 0.247301 -0.0990371 +-0.991303 0.122134 -0.0490118 0.903905 0.418992 0.086033 -0.941562 -0.336377 0.0176268 +-0.999317 -0.03624 0.00724799 0.927754 -0.36205 0.0905126 -0.511409 0.843703 0.163175 +-0.212154 0.960347 0.180896 -0.00765895 0.967216 0.253839 -0.178031 0.922856 0.341529 +-0.812055 0.165164 0.559721 -0.928033 -0.164527 0.334195 0.912076 0.407963 -0.0410359 +-0.83404 0.551393 0.0185342 0.893366 0.421589 0.155434 0.885165 0.440939 0.148513 +-0.926284 -0.338503 0.165572 0.822803 0.530898 -0.202835 0.843827 0.497414 -0.201334 +-0.687959 -0.713822 -0.13104 -0.695958 -0.70123 -0.154657 -0.211188 -0.96277 0.168743 +0.9139 0.395713 -0.0905412 -0.966118 0.249114 -0.0675129 0.943885 0.305949 0.124405 +0.911594 0.4008 0.0914105 -0.933941 0.352142 0.061242 0.919684 -0.392399 0.0143062 +0.916233 -0.400521 0.0100402 -0.471059 -0.203593 0.858285 -0.915223 -0.345325 0.207646 +-0.919381 -0.326333 0.219647 0.869706 0.493266 -0.0173076 -0.856248 0.516528 0.00617672 +-0.863412 0.5045 0 0.949901 0 0.312551 0.966694 0.133233 0.218522 +-0.829007 -0.531972 0.172494 0.854524 0.481433 -0.194963 0.840974 0.507777 -0.186883 +-0.696513 -0.697582 -0.168075 -0.691162 -0.703023 -0.167489 0.904852 0.419322 -0.0735652 +0.913292 0.397282 -0.089807 -0.965403 0.252157 -0.0664415 -0.943215 0.327134 -0.0577045 +0.881904 -0.386588 0.269806 0.893837 -0.441355 0.0791225 -0.844528 0.50902 0.166346 +0.886347 -0.463017 0.0020044 -0.0828959 -0.828959 0.553133 -0.804999 -0.542142 0.240952 +0.808104 0.58812 -0.0329227 -0.837432 0.546542 0 0.534473 -0.749104 0.391386 +0.728828 -0.596314 0.336482 -0.379356 -0.895194 0.233918 -0.453989 -0.868649 0.198353 +-0.766829 -0.616239 0.179505 0.801561 0.389275 -0.453834 0.674657 -0.500177 -0.542827 +-0.273896 -0.903855 -0.328675 -0.502434 -0.846655 -0.175317 0.943631 0.313417 -0.106444 +-0.965631 0.253008 -0.0595312 0.480927 -0.874065 0.0687038 0.33854 -0.939856 0.0453944 +0.457787 -0.884002 0.0947145 -0.792912 -0.448754 0.412202 -0.873995 0.430419 0.225547 +-0.914481 0.145486 0.37757 -0.924688 0.260226 0.277912 0.705414 -0.705414 0.0691582 +-0.559827 -0.828604 -0.00296991 -0.535111 -0.844212 -0.0310209 0.0216605 -0.982702 0.183924 +0.0438133 -0.986753 0.156204 -0.478183 -0.847161 0.231646 0.792308 0.609598 0.0252596 +0.806769 0.590681 -0.014824 -0.821657 0.569983 0 -0.758782 0.651344 0 +0.419925 -0.835832 0.353621 -0.381501 -0.902954 0.197816 0.369933 -0.494845 -0.786307 +0.147043 -0.735215 -0.661693 0.364599 -0.798177 -0.479563 0.0134744 -0.881226 -0.472503 +0.937113 0.329655 -0.114662 0.942484 0.315008 -0.111777 -0.943881 0.312655 -0.106472 +0.708104 -0.689293 0.153176 0.666982 -0.698981 0.257993 -0.652166 -0.652166 0.386469 +-0.195227 -0.980728 -0.00765596 0.810902 0.584913 0.0177246 -0.759496 0.650492 -0.00500152 +-0.788524 0.61494 -0.00882193 0.902719 0.410994 -0.127212 -0.930072 0.35149 -0.106866 +-0.899866 0.424305 -0.101025 0.436202 0.00411512 0.899839 0.534324 0.0360218 0.844512 +0.235431 -0.0692444 0.969421 -0.599185 -0.607869 0.52103 0.812685 0.582424 -0.0180597 +-0.835147 0.548455 -0.0415496 0.88482 0.44965 -0.122098 -0.878117 0.462348 -0.123063 +0.806244 0.35495 0.473267 -0.785754 0.522178 0.331542 -0.769256 0.56541 0.297584 +0.924872 -0.305423 -0.226558 0.725795 0.687257 -0.0299738 0.785491 0.618516 -0.0210092 +-0.786373 0.615992 -0.0465999 0.851665 0.50546 -0.138482 0.884147 0.450915 -0.122307 +-0.869551 0.480897 -0.112332 -0.87569 0.47276 -0.0983109 0.931887 -0.298993 0.2054 +-0.774241 0.584113 0.243645 -0.309426 -0.618853 -0.721995 0.376057 0.924474 0.0626762 +0.143217 0.984764 0.0986342 -0.452387 0.891597 0.0200085 -0.542807 0.839708 0.0158348 +0.687566 0.726122 0 0.707008 0.707008 -0.0166846 -0.762011 0.646569 -0.0358951 +-0.725795 0.687257 -0.0299738 0.794815 0.590631 -0.13937 0.806362 0.57469 -0.139682 +-0.878117 0.462348 -0.123063 0.818428 -0.574589 0.00482848 -0.889982 0.389367 0.237329 +-0.942235 0.240827 0.232799 0.949207 0.244655 -0.197863 0.743874 0.66668 -0.0467846 +-0.70665 0.698237 -0.114503 0.632487 0.773272 0.0448425 0.666306 0.745471 0.0175922 +-0.707008 0.707008 -0.0166846 -0.707008 0.707008 -0.0166846 0.799405 0.586597 -0.129831 +-0.850808 0.513854 -0.109911 -0.824838 0.555611 -0.104586 0.793489 -0.607421 -0.0376137 +0.702593 -0.711259 -0.0217755 -0.953977 -0.0681412 0.292034 0.968046 -0.154887 -0.197223 +-0.70948 0.695059 -0.116324 -0.750326 0.641609 -0.159214 0.780173 0.619123 0.0895342 +0.649066 0.758225 0.0617139 -0.706805 0.706805 -0.0291896 -0.706805 0.706805 -0.0291896 +0.836657 0.530563 -0.136042 0.811074 0.570608 -0.128706 -0.733792 0.668275 -0.122299 +0.567254 -0.815787 0.11276 -0.496535 -0.722233 0.481489 0.915373 -0.377906 -0.138846 +0.909728 -0.394216 -0.130341 -0.77009 0.621238 -0.145001 -0.809467 0.575253 -0.117678 +0.794994 0.605489 0.0369765 0.819705 0.568624 0.0689241 -0.706805 0.706805 -0.0291896 +-0.706805 0.706805 -0.0291896 0.789101 0.598476 -0.138368 0.806362 0.57469 -0.139682 +-0.702247 0.702247 -0.117041 0.381865 0.816705 0.432633 0.468256 0.784886 0.405822 +0.374228 0.83328 0.406938 0.15149 0.908938 0.388435 0.976021 -0.206743 -0.0681131 +-0.837875 0.54286 -0.0571733 -0.825293 0.564318 -0.0209006 0.699576 0.714556 -0.00188929 +0.726079 0.687511 0.011738 -0.707008 0.707008 -0.0166846 -0.707008 0.707008 -0.0166846 +0.702247 0.702247 -0.117041 -0.699976 0.699976 -0.141662 0.972209 0.0920126 0.215275 +-0.431596 0.868096 0.245225 0.0797681 0.765773 0.638144 -0.566086 0.804585 0.179415 +-0.589585 0.787992 0.177364 0.973088 0.221881 -0.0621977 -0.458034 0.883862 0.0948273 +0.0732056 0.995596 0.0585645 0.532326 0.846239 -0.022555 -0.687257 0.725795 -0.0299738 +-0.64644 0.761859 -0.0410583 0.702247 0.702247 -0.117041 -0.699976 0.699976 -0.141662 +0.959724 0.0239931 0.279919 0.982839 -0.150106 0.107219 0.432905 0.132964 0.891579 +-0.388253 0.745925 0.541161 -0.619265 0.76639 0.170755 -0.615557 0.769446 0.170418 +0.510862 0.849929 -0.128996 0.719484 0.682962 -0.126123 -0.631207 0.773877 -0.05188 +-0.62591 0.777172 -0.0651199 0.702247 0.702247 -0.117041 -0.598023 0.788504 -0.143633 +-0.57469 0.806362 -0.139682 0.805703 -0.523707 0.276721 0.81319 -0.521513 0.258352 +0.547421 0.646952 0.530833 -0.510677 0.805299 0.301169 -0.276292 0.621658 0.732942 +-0.797498 0.531665 0.285184 0.834017 0.476581 -0.278006 -0.433011 0.899851 -0.0526229 +-0.42302 0.905901 -0.0199538 0.968 -0.23474 0.0887333 0.915018 -0.223407 0.335903 +0.963438 -0.255693 0.0800511 -0.675917 0.724073 0.137311 -0.672087 0.729168 0.128893 +0.952642 0.156456 -0.26076 0.458781 0.873746 -0.161518 0.00928327 0.99994 0.00574678 +-0.0698733 0.996454 -0.0468715 0.215983 0.970446 -0.107639 -0.625859 0.77732 -0.0638284 +-0.597383 0.801148 -0.0360066 -0.580015 0.813834 -0.0354454 0.702247 0.702247 -0.117041 +-0.525649 0.839674 -0.136532 -0.570557 0.811923 -0.123471 0.806603 -0.531971 0.257679 +0.7963 -0.482163 0.365275 0.754212 -0.612311 0.237149 -0.828484 0.49962 0.252972 +-0.778659 0.562949 0.27709 -0.803751 0.547575 0.232693 0.972897 0.201475 -0.113487 +-0.440032 0.897903 -0.0118928 0.0335315 0.997962 -0.0542891 0.309552 0.950768 0.0147406 +-0.0730282 0.994077 0.08048 -0.472144 0.872894 0.123031 -0.445174 0.890348 0.0953944 +0.918071 0.367722 0.148076 -0.607681 0.791702 0.0627037 -0.542928 0.837084 0.0672356 +0.96818 0.142503 -0.205721 -0.504826 0.862411 -0.0373945 -0.512535 0.858496 -0.0170845 +0.629993 0.760337 0.158102 0.146781 0.974779 0.168109 -0.405681 0.901513 0.150658 +-0.420219 0.89516 0.148677 0.722843 0.687674 0.0678445 0.702802 0.708286 0.0663365 +-0.13555 0.988715 -0.0637881 -0.511905 0.856869 -0.0610642 0.702247 0.702247 -0.117041 +-0.593961 0.795024 -0.123073 -0.702247 0.702247 -0.117041 0.831794 -0.521423 0.190361 +0.814657 0.569062 0.111816 0.96461 -0.208999 0.160768 -0.468793 0.882546 0.0366937 +-0.254913 0.96691 0.0102551 0.324573 0.933702 -0.151171 -0.396089 0.902252 0.170454 +-0.36321 0.928204 0.0807134 0.41152 0.909894 -0.0523878 0.74245 0.669215 -0.0303041 +-0.431545 0.896854 0.0970712 -0.647366 0.745287 0.159575 -0.754312 0.641948 0.137538 +0.965728 0.181539 -0.185507 -0.547652 0.836428 -0.0215729 -0.509393 0.860512 -0.00614652 +0.893389 -0.447659 0.0381625 -0.683946 0.72111 0.110537 -0.640153 0.766706 0.0486504 +0.888531 0.45377 0.0678611 -0.708849 0.702835 -0.0596345 -0.974618 0.223189 0.017505 +0.702247 0.702247 -0.117041 -0.702247 0.702247 -0.117041 0.996896 0.0750352 0.0238207 +-0.238509 0.971074 0.0113576 0.0596535 0.997654 -0.033598 0.243225 0.957762 -0.153403 +0.0867785 0.995858 -0.0271308 -0.789815 0.600173 0.126428 0.871693 0.460363 -0.167982 +-0.500058 0.865992 0 -0.468148 0.88365 0 -0.449818 0.893022 0.01323 +0.678991 -0.734044 -0.0122341 0.542535 -0.837459 -0.065714 -0.5071 0.86187 0.00534493 +-0.438332 0.895684 0.0749286 -0.42997 0.899028 0.0829143 0.322186 0.946591 0.0127059 +0.307146 0.950664 -0.0435743 -0.181453 0.967752 -0.174733 -0.14762 0.984136 -0.0984136 +0.673835 0.737049 0.0520059 -0.858604 -0.512637 -0.00168354 0.702247 0.702247 -0.117041 +-0.665916 0.731202 -0.147981 0.982715 0.181589 -0.0360245 -0.0643781 -0.981086 -0.182556 +-0.1195 -0.979236 -0.163759 0.113419 -0.975399 -0.189031 -0.793613 0.595728 0.123635 +-0.709686 0.695789 0.11056 0.563008 0.811297 -0.157541 0.528451 0.835585 -0.150126 +0.814319 0.555351 -0.168733 -0.457502 0.888747 0.0286436 -0.550553 0.834103 0.0340962 +-0.574767 0.817704 0.0316554 0.534635 -0.840508 -0.0878206 0.649105 -0.740743 -0.173095 +0.737686 -0.658186 -0.150372 -0.363244 0.915201 0.174532 -0.412962 0.880654 0.232187 +-0.105526 0.972834 0.206056 0.169772 0.969818 0.175016 0.204462 0.965997 0.158256 +0.15206 0.975996 0.155917 0.173893 0.978574 0.110246 0.292003 0.955725 0.0363921 +-0.856901 -0.512676 -0.0537089 -0.812914 -0.577504 -0.0752371 0.479499 0.871225 -0.10511 +0.562478 0.818834 -0.114587 -0.616715 0.771145 -0.158106 -0.535488 0.82647 -0.173781 +0.670118 0.713468 -0.204708 -0.473457 -0.880601 0.0195039 -0.495026 -0.867695 0.0453174 +-0.321311 -0.943506 -0.0809718 0.993584 0.111048 -0.0214302 -0.695781 0.712525 0.0905384 +-0.599816 0.800085 -0.00925335 0.682727 0.717103 -0.14017 0.673821 0.719973 -0.166148 +-0.626889 0.778445 0.0321482 -0.537073 0.843487 0.00904544 0.858828 -0.496212 -0.127234 +-0.915686 -0.391304 -0.0916567 0.624353 0.77943 -0.0516923 0.441226 0.896205 -0.0462237 +0.312499 0.946568 -0.0797052 -0.158298 0.966672 -0.201214 0.624981 0.752234 -0.208669 +-0.385295 -0.917935 0.0945696 -0.920835 -0.381253 -0.0819083 0.188245 -0.982101 0.00645411 +0.925438 -0.37178 -0.0731078 0.944088 -0.322686 -0.0676105 -0.653123 -0.748006 0.117973 +-0.576251 -0.81473 0.0644219 0.99542 0.0704722 0.0645995 -0.58365 0.811376 -0.0319472 +-0.227703 0.961412 -0.154397 0.478233 0.861316 -0.171546 -0.497727 0.867334 0 +-0.392374 0.919676 0.0154814 -0.397157 0.917569 0.0182601 0.772741 -0.624267 -0.11473 +-0.860065 -0.500567 0.0985965 -0.945583 -0.301323 0.12279 0.36121 -0.916403 0.17243 +0.454101 -0.876765 0.158353 -0.887808 -0.448053 -0.105099 -0.717029 -0.691722 -0.0859632 +0.74326 0.66433 -0.0789303 0.727248 0.684071 -0.0561823 -0.679343 0.71428 -0.168218 +0.160269 0.910797 -0.380476 0.884122 0.424657 -0.194924 -0.856608 -0.48949 0.163163 +-0.681658 -0.718998 0.13559 0.989926 0 0.141587 -0.478352 0.878168 0 +-0.397157 0.917569 -0.0182601 0.75075 -0.637123 -0.174499 0.685822 0.636443 -0.35297 +0.99177 -0.0627702 -0.111592 0.979575 -0.171037 -0.105732 0.986894 -0.119791 -0.108119 +-0.917159 0.374499 0.136269 0.443643 -0.887287 0.126107 -0.12786 -0.991428 -0.0268939 +0.64434 0.759385 -0.090338 0.704894 0.704894 -0.0790535 -0.42833 0.888728 -0.163391 +-0.0259677 0.969837 -0.242365 0.850593 0.47778 -0.219586 -0.962319 0.271423 0.0164499 +0.909347 0.199221 0.365238 0.965527 0.200753 0.165701 0.992061 0.0646997 0.107833 +-0.321976 0.946552 -0.0192389 -0.326273 0.945081 -0.0192006 -0.175995 0.982641 -0.0586652 +0.0976805 0.993085 -0.0651203 0.183147 0.976783 -0.11114 0.0580726 0.97562 -0.211643 +0.265251 0.89973 -0.346594 0.990571 -0.0466412 -0.128819 -0.910502 0.391643 0.132668 +-0.784732 0.617641 0.0521165 0.499462 0.85805 -0.119529 0.615386 0.781235 -0.104747 +-0.493157 0.867277 -0.0680217 0.662902 0.745765 0.0662903 -0.800862 0.597836 -0.0348297 +-0.82467 0.555207 -0.108002 0.394016 0.910313 -0.12681 0.47473 0.87152 -0.122816 +-0.207983 0.974714 -0.0817076 0 0.99922 -0.0394837 0.217662 0.976024 0 +-0.78792 0.597133 -0.150381 -0.738577 0.656931 -0.151476 0.335756 0.93732 -0.0932657 +0.325028 0.941474 -0.0893519 0.319809 0.940182 -0.117386 -0.312036 0.934975 -0.168689 +0.0745504 0.984242 -0.160342 0.158075 0.973779 -0.163606 0.0663259 0.99153 -0.111663 +0 0.995211 -0.0977514 0.0882513 0.992617 -0.0832084 0.254057 0.962565 -0.0944677 + diff --git a/Modules/Segmentation/Testing/Data/testNrrd.ascii b/Modules/Segmentation/Testing/Data/testNrrd.ascii new file mode 100644 index 0000000000..f78381e84b --- /dev/null +++ b/Modules/Segmentation/Testing/Data/testNrrd.ascii @@ -0,0 +1,8 @@ +2 2 +2 2 +2 2 +2 2 +1 1 +1 1 +1 1 +1 1 \ No newline at end of file diff --git a/Modules/Segmentation/Testing/Data/testNrrd.nhdr b/Modules/Segmentation/Testing/Data/testNrrd.nhdr new file mode 100644 index 0000000000..d62bdcaba6 --- /dev/null +++ b/Modules/Segmentation/Testing/Data/testNrrd.nhdr @@ -0,0 +1,20 @@ +NRRD0005 +content: SomeIDNumber42 +type: short +dimension: 4 +space: right-anterior-superior +sizes: 2 2 2 2 +thicknesses: NaN NaN 2.5 NaN +space directions: (2,0,0) (0,2.5,0) (0,0,2) none +centerings: cell cell cell none +kinds: space space space list +endian: little +encoding: ascii +space units: "mm" "mm" "mm" +space origin: (0,0,0) +data file: testNrrd.ascii +measurement frame: (1,0,0) (0,0,1) (0,1,0) +modality:=DWMRI +DWMRI_b-value:=1000 +DWMRI_gradient_0000:= 0 0 0 +DWMRI_gradient_0001:= 1 0 1 \ No newline at end of file diff --git a/Modules/Segmentation/Testing/Data/testimage.dcm b/Modules/Segmentation/Testing/Data/testimage.dcm new file mode 100644 index 0000000000..ecf7b12d33 Binary files /dev/null and b/Modules/Segmentation/Testing/Data/testimage.dcm differ diff --git a/Modules/Segmentation/Testing/Data/vtkUnstructuredGrid.vtk b/Modules/Segmentation/Testing/Data/vtkUnstructuredGrid.vtk new file mode 100644 index 0000000000..392ef94c61 Binary files /dev/null and b/Modules/Segmentation/Testing/Data/vtkUnstructuredGrid.vtk differ diff --git a/Modules/Segmentation/Testing/files.cmake b/Modules/Segmentation/Testing/files.cmake new file mode 100644 index 0000000000..2f9a9aa8a0 --- /dev/null +++ b/Modules/Segmentation/Testing/files.cmake @@ -0,0 +1,22 @@ +set(MODULE_TESTS + mitkContourMapper2DTest.cpp + mitkContourTest.cpp + mitkDataNodeSegmentationTest.cpp +# mitkSegmentationInterpolationTest.cpp +) + +set(MODULE_IMAGE_TESTS + mitkManualSegmentationToSurfaceFilterTest.cpp + mitkOverwriteSliceImageFilterTest.cpp +) +set(MODULE_CUSTOM_TESTS +) +set(MODULE_TESTIMAGES + US4DCyl.nrrd + Pic3D.nrrd + Pic2DplusT.nrrd + BallBinary30x30x30.nrrd + Png2D-bw.png + binary.stl + ball.stl +) diff --git a/Modules/MitkExt/Testing/mitkCompareImageSliceTestHelper.h b/Modules/Segmentation/Testing/mitkCompareImageSliceTestHelper.h similarity index 100% rename from Modules/MitkExt/Testing/mitkCompareImageSliceTestHelper.h rename to Modules/Segmentation/Testing/mitkCompareImageSliceTestHelper.h diff --git a/Modules/MitkExt/Testing/mitkContourMapper2DTest.cpp b/Modules/Segmentation/Testing/mitkContourMapper2DTest.cpp similarity index 100% rename from Modules/MitkExt/Testing/mitkContourMapper2DTest.cpp rename to Modules/Segmentation/Testing/mitkContourMapper2DTest.cpp diff --git a/Modules/MitkExt/Testing/mitkContourTest.cpp b/Modules/Segmentation/Testing/mitkContourTest.cpp similarity index 100% rename from Modules/MitkExt/Testing/mitkContourTest.cpp rename to Modules/Segmentation/Testing/mitkContourTest.cpp diff --git a/Modules/MitkExt/Testing/mitkDataNodeExtTest.cpp b/Modules/Segmentation/Testing/mitkDataNodeSegmentationTest.cpp similarity index 54% copy from Modules/MitkExt/Testing/mitkDataNodeExtTest.cpp copy to Modules/Segmentation/Testing/mitkDataNodeSegmentationTest.cpp index 4faeb11b65..150220120e 100644 --- a/Modules/MitkExt/Testing/mitkDataNodeExtTest.cpp +++ b/Modules/Segmentation/Testing/mitkDataNodeSegmentationTest.cpp @@ -1,257 +1,165 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2008-02-25 17:27:17 +0100 (Mo, 25 Feb 2008) $ Version: $Revision: 7837 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkDataNode.h" #include #include "mitkVtkPropRenderer.h" #include "mitkTestingMacros.h" #include "mitkGlobalInteraction.h" #include //Basedata Test #include #include #include -#include -#include -#include -#include //Mapper Test #include #include #include #include #include -#include -#include -#include -#include #include #include -#include -#include -#include -#include //Interactors #include #include #include -#include -#include -#include -#include //Propertylist Test #include #include #include #include #include #include #include #include #include #include #include /** * Extended test for mitk::DataNode. A number of tests from the core test * mitkDataNodeTest are assumed to pass! */ -class mitkDataNodeExtTestClass { public: +class mitkDataNodeSegmentationTestClass { public: static void TestDataSetting(mitk::DataNode::Pointer dataNode) { mitk::BaseData::Pointer baseData; //NULL pointer Test dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a NULL pointer was set correctly" ) baseData = mitk::Contour::New(); dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a Contour object was set correctly" ) baseData = mitk::ContourSet::New(); dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a ContourSet object was set correctly" ) - - baseData = mitk::ItkBaseDataAdapter::New(); - dataNode->SetData(baseData); - MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a ItkBaseDataAdapter object was set correctly" ) - - baseData = mitk::Mesh::New(); - dataNode->SetData(baseData); - MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a Mesh object was set correctly" ) - - baseData = mitk::SeedsImage::New(); - dataNode->SetData(baseData); - MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a SeedsImage object was set correctly" ) - - baseData = mitk::BoundingObject::New(); - dataNode->SetData(baseData); - MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a BoundingObject object was set correctly" ) - - baseData = mitk::UnstructuredGrid::New(); - dataNode->SetData(baseData); - MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a UnstructuredGrid object was set correctly" ) } static void TestMapperSetting(mitk::DataNode::Pointer dataNode) { //tests the SetMapper() method //in dataNode is a mapper vector which can be accessed by index //in this test method we use only slot 0 (filled with null) and slot 1 //so we also test the destructor of the mapper classes mitk::Mapper::Pointer mapper; dataNode->SetMapper(0,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(0), "Testing if a NULL pointer was set correctly" ) mapper = mitk::ContourMapper2D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourMapper2D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) mapper = mitk::ContourSetMapper2D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourSetMapper2D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - mapper = mitk::MeshMapper2D::New(); - dataNode->SetMapper(1,mapper); - MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a MeshMapper2D was set correctly" ) - MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - - mapper = mitk::UnstructuredGridMapper2D::New(); - dataNode->SetMapper(1,mapper); - MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a UnstructuredGridMapper2D was set correctly" ) - MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - - mapper = mitk::LineMapper2D::New(); - dataNode->SetMapper(1,mapper); - MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a LineMapper2D was set correctly" ) - MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - - mapper = mitk::SplineMapper2D::New(); - dataNode->SetMapper(1,mapper); - MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a SplineMapper2D was set correctly" ) - MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - //3D Mappers mapper = mitk::ContourSetVtkMapper3D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourSetVtkMapper3D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) mapper = mitk::ContourVtkMapper3D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourVtkMapper3D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - - mapper = mitk::MeshVtkMapper3D::New(); - dataNode->SetMapper(1,mapper); - MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a MeshVtkMapper3D was set correctly" ) - MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - - mapper = mitk::UnstructuredGridVtkMapper3D::New(); - dataNode->SetMapper(1,mapper); - MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a UnstructuredGridVtkMapper3D was set correctly" ) - MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - - //linker error - //mapper = mitk::LineVtkMapper3D::New(); - //dataNode->SetMapper(1,mapper); - //MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a LineVtkMapper3D was set correctly" ) - //MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) - - mapper = mitk::SplineVtkMapper3D::New(); - dataNode->SetMapper(1,mapper); - MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a SplineVtkMapper3D was set correctly" ) - MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) } static void TestInteractorSetting(mitk::DataNode::Pointer dataNode) { //this method tests the SetInteractor() and GetInteractor methods //the Interactor base class calls the DataNode->SetInteractor method mitk::Interactor::Pointer interactor; MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a NULL pointer was set correctly (Interactor)" ) - - interactor = mitk::ConnectPointsInteractor::New("AffineInteractions click to select", dataNode); - MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a ConnectPointsInteractor was set correctly" ) - + interactor = mitk::ContourInteractor::New("AffineInteractions click to select", dataNode); MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a ContourInteractor was set correctly" ) interactor = mitk::ExtrudedContourInteractor::New("AffineInteractions click to select", dataNode); MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a ExtrudedContourInteractor was set correctly" ) - - interactor = mitk::PointInteractor::New("AffineInteractions click to select", dataNode); - MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a PointInteractor was set correctly" ) - - interactor = mitk::PointSelectorInteractor::New("AffineInteractions click to select", dataNode); - MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a PointSelectorInteractor was set correctly" ) - - interactor = mitk::SeedsInteractor::New("AffineInteractions click to select", dataNode); - MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a SeedsInteractor was set correctly" ) - - interactor = mitk::DisplayPointSetInteractor::New("AffineInteractions click to select", dataNode); - MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a DisplayPointSetInteractor was set correctly" ) } }; int mitkDataNodeExtTest(int /* argc */, char* /*argv*/[]) { // always start with this! MITK_TEST_BEGIN("DataNode") // Global interaction must(!) be initialized mitk::GlobalInteraction::GetInstance()->Initialize("global"); // let's create an object of our class mitk::DataNode::Pointer myDataNode = mitk::DataNode::New(); // first test: did this work? // using MITK_TEST_CONDITION_REQUIRED makes the test stop after failure, since // it makes no sense to continue without an object. MITK_TEST_CONDITION_REQUIRED(myDataNode.IsNotNull(),"Testing instantiation") //test setData() Method mitkDataNodeExtTestClass::TestDataSetting(myDataNode); mitkDataNodeExtTestClass::TestMapperSetting(myDataNode); //note, that no data is set to the dataNode mitkDataNodeExtTestClass::TestInteractorSetting(myDataNode); // write your own tests here and use the macros from mitkTestingMacros.h !!! // do not write to std::cout and do not return from this function yourself! // always end with this! MITK_TEST_END() } diff --git a/Modules/MitkExt/Testing/mitkManualSegmentationToSurfaceFilterTest.cpp b/Modules/Segmentation/Testing/mitkManualSegmentationToSurfaceFilterTest.cpp similarity index 100% rename from Modules/MitkExt/Testing/mitkManualSegmentationToSurfaceFilterTest.cpp rename to Modules/Segmentation/Testing/mitkManualSegmentationToSurfaceFilterTest.cpp diff --git a/Modules/MitkExt/Testing/mitkOverwriteSliceImageFilterTest.cpp b/Modules/Segmentation/Testing/mitkOverwriteSliceImageFilterTest.cpp similarity index 100% rename from Modules/MitkExt/Testing/mitkOverwriteSliceImageFilterTest.cpp rename to Modules/Segmentation/Testing/mitkOverwriteSliceImageFilterTest.cpp diff --git a/Modules/MitkExt/Testing/mitkSegmentationInterpolationTest.cpp b/Modules/Segmentation/Testing/mitkSegmentationInterpolationTest.cpp similarity index 98% rename from Modules/MitkExt/Testing/mitkSegmentationInterpolationTest.cpp rename to Modules/Segmentation/Testing/mitkSegmentationInterpolationTest.cpp index 68f470689c..ef0950dfd3 100644 --- a/Modules/MitkExt/Testing/mitkSegmentationInterpolationTest.cpp +++ b/Modules/Segmentation/Testing/mitkSegmentationInterpolationTest.cpp @@ -1,366 +1,366 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkSegmentationInterpolationController.h" #include "mitkCoreObjectFactory.h" #include "mitkStandardFileLocations.h" #include "mitkDataNodeFactory.h" #include "ipSegmentation.h" -#include "mitkCompareImageSliceTestHelper.h" +#include "mitkCompareImageSliceTestHelper.h"s class mitkSegmentationInterpolationTestClass { public: mitkSegmentationInterpolationTestClass() {} ~mitkSegmentationInterpolationTestClass() {} bool Test() { return CreateNewInterpolator() && CreateSegmentation() && ClearSegmentation() && CreateTwoSlices(2) && TestInterpolation(2) && ClearSegmentation() && CreateTwoSlices(1) && TestInterpolation(1) && ClearSegmentation() && CreateTwoSlices(0) && TestInterpolation(0) && DeleteInterpolator() && CreateNewInterpolator() && LoadTestImages() && CompareInterpolationsToDefinedReference(); } protected: bool CreateNewInterpolator(); bool CreateSegmentation(); bool ClearSegmentation(); bool CreateTwoSlices(int); bool TestInterpolation(int); bool DeleteInterpolator(); bool LoadTestImages(); bool CompareInterpolationsToDefinedReference(); mitk::Image::Pointer LoadImage(const std::string& filename); mitk::SegmentationInterpolationController::Pointer m_Interpolator; mitk::Image::Pointer m_Image; mitk::Image::Pointer m_ManualSlices; mitk::Image::Pointer m_InterpolatedSlices; unsigned int dim[3]; int pad[3]; }; bool mitkSegmentationInterpolationTestClass::CreateNewInterpolator() { std::cout << "Instantiation" << std::endl; // instantiation m_Interpolator = mitk::SegmentationInterpolationController::New(); if (m_Interpolator.IsNotNull()) { std::cout << " (II) Instantiation works." << std::endl; } else { std::cout << " Instantiation test failed!" << std::endl; return false; } return true; } bool mitkSegmentationInterpolationTestClass::CreateSegmentation() { m_Image = mitk::Image::New(); dim[0]=15; dim[1]=20; dim[2]=25; pad[0]=2; pad[1]=3; pad[2]=4; - m_Image->Initialize(mitk::PixelType(typeid(int)), 3, dim); + m_Image->Initialize( mitk::MakeScalarPixelType(), 3, dim); return true; } bool mitkSegmentationInterpolationTestClass::ClearSegmentation() { int* p = (int*)m_Image->GetData(); // pointer to pixel data int size = dim[0]*dim[1]*dim[2]; for(int i=0; iGetData(); // pointer to pixel data int size = dim[0]*dim[1]*dim[2]; for(int i=0; i= pad[xdim]) && (x < ( (signed) dim[xdim]-pad[xdim])) && (y >= pad[ydim]) && (y < ( (signed) dim[ydim]-pad[ydim])) ) { *p = 1; } else { *p = 0; } } m_Interpolator->SetSegmentationVolume( m_Image ); std::cout << " (II) SetSegmentationVolume works (slicedim " << slicedim << ")" << std::endl; return true; } /** * Checks if interpolation would create a square in slice 1 */ bool mitkSegmentationInterpolationTestClass::TestInterpolation(int slicedim) { int slice = 1; mitk::Image::Pointer interpolated = m_Interpolator->Interpolate( slicedim, slice, 0 ); // interpolate second slice transversal if (interpolated.IsNull()) { std::cerr << " (EE) Interpolation did not return anything for slicedim == " << slicedim << " (although it should)." << std::endl; return false; } int xdim,ydim; switch (slicedim) { case 0: xdim = 1; ydim = 2; // different than above! break; case 1: xdim = 0; ydim = 2; break; case 2: default: xdim = 0; ydim = 1; break; } ipMITKSegmentationTYPE* p = (ipMITKSegmentationTYPE*)interpolated->GetData(); // pointer to pixel data int size = dim[xdim]*dim[ydim]; if ( (signed) interpolated->GetDimension(0) * (signed) interpolated->GetDimension(1) != size ) { std::cout << " (EE) Size of interpolated image differs from original segmentation..." << std::endl; return false; } for(int i=0; i= pad[xdim]) && (x < ((signed) dim[xdim]-pad[xdim])) && (y >= pad[ydim]) && (y < ((signed) dim[ydim]-pad[ydim])) && (value != 1) ) { std::cout << " (EE) Interpolation of a square figure failed" << std::endl; std::cout << " Value at " << x << " " << y << ": " << (int)value << std::endl; return false; } } std::cout << " (II) Interpolation of a square figure works like expected (slicedim " << slicedim << ")" << std::endl; return true; } bool mitkSegmentationInterpolationTestClass::DeleteInterpolator() { std::cout << "Object destruction" << std::endl; // freeing m_Interpolator = NULL; std::cout << " (II) Freeing works." << std::endl; return true; } bool mitkSegmentationInterpolationTestClass::LoadTestImages() { std::string filename1 = mitk::StandardFileLocations::GetInstance()->FindFile("interpolation_test_manual.pic.gz", "../mitk/Core/Testing/Data/"); if ( filename1.empty() ) { filename1 = mitk::StandardFileLocations::GetInstance()->FindFile("interpolation_test_manual.pic.gz", "Testing/Data/"); } std::cout << "Found test image (manual slices) in '" << filename1 << "'" << std::endl; std::string filename2 = mitk::StandardFileLocations::GetInstance()->FindFile("interpolation_test_result.pic.gz", "../mitk/Core/Testing/Data/"); if ( filename2.empty() ) { filename2 = mitk::StandardFileLocations::GetInstance()->FindFile("interpolation_test_result.pic.gz", "Testing/Data/"); } std::cout << "Found test image (reference for interpolation) in '" << filename2 << "'" << std::endl; if ( filename1.empty() || filename2.empty() ) { return false; } else { m_ManualSlices = LoadImage( filename1 ); m_InterpolatedSlices = LoadImage( filename2 ); return ( m_ManualSlices.IsNotNull() && m_InterpolatedSlices.IsNotNull() ); } return true; } mitk::Image::Pointer mitkSegmentationInterpolationTestClass::LoadImage(const std::string& filename) { mitk::Image::Pointer image = NULL; mitk::DataNodeFactory::Pointer factory = mitk::DataNodeFactory::New(); try { factory->SetFileName( filename ); factory->Update(); if(factory->GetNumberOfOutputs()<1) { std::cerr<<"File " << filename << " could not be loaded [FAILED]"<GetOutput( 0 ); image = dynamic_cast(node->GetData()); if(image.IsNull()) { std::cout<<"File " << filename << " is not an image! [FAILED]"<SetSegmentationVolume( m_ManualSlices ); std::cout << "OK" << std::endl; std::cout << " (II) Testing interpolation result for slice " << std::flush; for (unsigned int slice = 1; slice < 98; ++slice) { if (slice % 2 == 0) continue; // these were manually drawn, no interpolation possible std::cout << slice << " " << std::flush; mitk::Image::Pointer interpolation = m_Interpolator->Interpolate( 2, slice, 0 ); if ( interpolation.IsNull() ) { std::cerr << " (EE) Interpolated image is NULL." << std::endl; return false; } if ( !CompareImageSliceTestHelper::CompareSlice( m_InterpolatedSlices, 2, slice, interpolation ) ) { std::cerr << " (EE) interpolated image is not identical to reference in slice " << slice << std::endl; return false; } } std::cout << std::endl; std::cout << " (II) Interpolations are the same as the saved references." << std::endl; return true; } /// ctest entry point int mitkSegmentationInterpolationTest(int /*argc*/, char* /*argv*/[]) { // one big variable to tell if anything went wrong std::cout << "Creating CoreObjectFactory" << std::endl; itk::ObjectFactoryBase::RegisterFactory(mitk::CoreObjectFactory::New()); mitkSegmentationInterpolationTestClass test; if ( test.Test() ) { std::cout << "[PASSED]" << std::endl; return EXIT_SUCCESS; } else { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } } diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake new file mode 100644 index 0000000000..9911599184 --- /dev/null +++ b/Modules/Segmentation/files.cmake @@ -0,0 +1,53 @@ +set(CPP_FILES +Algorithms/mitkCalculateSegmentationVolume.cpp +Algorithms/mitkComputeContourSetNormalsFilter.cpp +Algorithms/mitkContourSetToPointSetFilter.cpp +Algorithms/mitkContourUtils.cpp +Algorithms/mitkCorrectorAlgorithm.cpp +Algorithms/mitkCreateDistanceImageFromSurfaceFilter.cpp +Algorithms/mitkDiffImageApplier.cpp +Algorithms/mitkImageToContourFilter.cpp +Algorithms/mitkManualSegmentationToSurfaceFilter.cpp +Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp +Algorithms/mitkOverwriteSliceImageFilter.cpp +Algorithms/mitkReduceContourSetFilter.cpp +Algorithms/mitkSegmentationObjectFactory.cpp +Algorithms/mitkSegmentationSink.cpp +Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp +Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp +Algorithms/mitkShowSegmentationAsSurface.cpp +Controllers/mitkSegmentationInterpolationController.cpp +Controllers/mitkSurfaceInterpolationController.cpp +# DataManagement/mitkApplyDiffImageOperation.cpp +DataManagement/mitkContour.cpp +DataManagement/mitkContourSet.cpp +DataManagement/mitkExtrudedContour.cpp +Interactions/mitkAddContourTool.cpp +Interactions/mitkAutoCropTool.cpp +Interactions/mitkAutoSegmentationTool.cpp +Interactions/mitkBinaryThresholdTool.cpp +Interactions/mitkBinaryThresholdULTool.cpp +Interactions/mitkCalculateGrayValueStatisticsTool.cpp +Interactions/mitkCalculateVolumetryTool.cpp +Interactions/mitkContourInteractor.cpp +Interactions/mitkContourTool.cpp +Interactions/mitkCorrectorTool2D.cpp +Interactions/mitkCreateSurfaceTool.cpp +Interactions/mitkDrawPaintbrushTool.cpp +Interactions/mitkErasePaintbrushTool.cpp +Interactions/mitkEraseRegionTool.cpp +Interactions/mitkExtrudedContourInteractor.cpp +Interactions/mitkFeedbackContourTool.cpp +Interactions/mitkFillRegionTool.cpp +Interactions/mitkPaintbrushTool.cpp +Interactions/mitkRegionGrow3DTool.cpp +Interactions/mitkRegionGrowingTool.cpp +Interactions/mitkSegmentationsProcessingTool.cpp +Interactions/mitkSetRegionTool.cpp +Interactions/mitkSegTool2D.cpp +Interactions/mitkSubtractContourTool.cpp +Rendering/mitkContourMapper2D.cpp +Rendering/mitkContourSetMapper2D.cpp +Rendering/mitkContourSetVtkMapper3D.cpp +Rendering/mitkContourVtkMapper3D.cpp +) diff --git a/Plugins/PluginList.cmake b/Plugins/PluginList.cmake index de27ac4b0d..67b5e41db5 100644 --- a/Plugins/PluginList.cmake +++ b/Plugins/PluginList.cmake @@ -1,40 +1,41 @@ # Plug-ins must be ordered according to their dependencies set(MITK_EXT_PLUGINS org.mitk.core.services:ON org.mitk.gui.common:ON org.mitk.planarfigure:ON org.mitk.core.ext:OFF org.mitk.core.jobs:OFF org.mitk.diffusionimaging:OFF org.mitk.gui.qt.application:ON org.mitk.gui.qt.coreapplication:OFF org.mitk.gui.qt.ext:OFF org.mitk.gui.qt.extapplication:OFF org.mitk.gui.qt.common:ON org.mitk.gui.qt.stdmultiwidgeteditor:ON org.mitk.gui.qt.common.legacy:OFF org.mitk.gui.qt.diffusionimagingapp:OFF org.mitk.gui.qt.datamanager:ON org.mitk.gui.qt.basicimageprocessing:OFF org.mitk.gui.qt.diffusionimaging:OFF org.mitk.gui.qt.dtiatlasapp:OFF org.mitk.gui.qt.examples:OFF org.mitk.gui.qt.examplesopencv:OFF org.mitk.gui.qt.igtexamples:OFF org.mitk.gui.qt.igttracking:OFF org.mitk.gui.qt.imagecropper:OFF org.mitk.gui.qt.imagenavigator:ON org.mitk.gui.qt.materialeditor:OFF org.mitk.gui.qt.measurementtoolbox:OFF + org.mitk.gui.qt.meshdecimation:OFF org.mitk.gui.qt.moviemaker:OFF org.mitk.gui.qt.pointsetinteraction:OFF org.mitk.gui.qt.python.console:OFF org.mitk.gui.qt.registration:OFF org.mitk.gui.qt.segmentation:OFF org.mitk.gui.qt.toftutorial:OFF org.mitk.gui.qt.tofutil:OFF org.mitk.gui.qt.ugvisualization:OFF org.mitk.gui.qt.volumevisualization:OFF -) \ No newline at end of file +) diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.cpp b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.cpp index 423053e073..c230739aae 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.cpp @@ -1,561 +1,559 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "QmitkAbstractView.h" #include "QmitkDataNodeSelectionProvider.h" #include "internal/QmitkCommonActivator.h" #include "internal/QmitkDataNodeItemModel.h" // mitk Includes #include #include #include #include #include #include // berry Includes #include #include #include // CTK Includes #include // Qt Includes #include #include #include #include #include class QmitkAbstractViewPrivate { public: QmitkAbstractViewPrivate(QmitkAbstractView* qq) : q(qq) , m_PrefServiceTracker(QmitkCommonActivator::GetContext()) , m_DataStorageServiceTracker(QmitkCommonActivator::GetContext()) , m_Parent(0) , m_DataNodeItemModel(new QmitkDataNodeItemModel) , m_DataNodeSelectionModel(new QItemSelectionModel(m_DataNodeItemModel)) , m_InDataStorageChanged(false) { m_PrefServiceTracker.open(); m_DataStorageServiceTracker.open(); } ~QmitkAbstractViewPrivate() { delete m_DataNodeSelectionModel; delete m_DataNodeItemModel; m_PrefServiceTracker.close(); m_DataStorageServiceTracker.close(); } /** * Called when a DataStorage Add Event was thrown. Sets * m_InDataStorageChanged to true and calls NodeAdded afterwards. * \see m_InDataStorageChanged */ void NodeAddedProxy(const mitk::DataNode* node) { // garantuee no recursions when a new node event is thrown in NodeAdded() if(!m_InDataStorageChanged) { m_InDataStorageChanged = true; q->NodeAdded(node); q->DataStorageModified(); m_InDataStorageChanged = false; } } /** * Called when a DataStorage remove event was thrown. Sets * m_InDataStorageChanged to true and calls NodeRemoved afterwards. * \see m_InDataStorageChanged */ void NodeRemovedProxy(const mitk::DataNode* node) { // garantuee no recursions when a new node event is thrown in NodeAdded() if(!m_InDataStorageChanged) { m_InDataStorageChanged = true; q->NodeRemoved(node); q->DataStorageModified(); m_InDataStorageChanged = false; } } /** * Called when a DataStorage changed event was thrown. Sets * m_InDataStorageChanged to true and calls NodeChanged afterwards. * \see m_InDataStorageChanged */ void NodeChangedProxy(const mitk::DataNode* node) { // garantuee no recursions when a new node event is thrown in NodeAdded() if(!m_InDataStorageChanged) { m_InDataStorageChanged = true; q->NodeChanged(node); q->DataStorageModified(); m_InDataStorageChanged = false; } } /** - * reactions to selection events from data manager (and potential other senders) + * reactions to selection events from views */ void BlueBerrySelectionChanged(berry::IWorkbenchPart::Pointer sourcepart, berry::ISelection::ConstPointer selection) { - if(sourcepart.IsNull() || sourcepart->GetSite()->GetId() != "org.mitk.views.datamanager") + if(sourcepart.IsNull() || sourcepart.GetPointer() == static_cast(q)) return; mitk::DataNodeSelection::ConstPointer _DataNodeSelection = selection.Cast(); q->OnSelectionChanged(sourcepart, this->DataNodeSelectionToQList(_DataNodeSelection)); } /** * Converts a mitk::DataNodeSelection to a QList (possibly empty) */ QList DataNodeSelectionToQList(mitk::DataNodeSelection::ConstPointer currentSelection) const; QmitkAbstractView* const q; ctkServiceTracker m_PrefServiceTracker; ctkServiceTracker m_DataStorageServiceTracker; /** * Saves the parent of this view (this is the scrollarea created in CreatePartControl(void*) * \see CreatePartControl(void*) */ QWidget* m_Parent; /** * Holds the current selection (selection made by this View !!!) */ QmitkDataNodeSelectionProvider::Pointer m_SelectionProvider; /** * Holds a helper model for firing selection events. */ QmitkDataNodeItemModel* m_DataNodeItemModel; /** * The selection model for the QmitkDataNodeItemModel; */ QItemSelectionModel* m_DataNodeSelectionModel; /** * object to observe BlueBerry selections */ berry::ISelectionListener::Pointer m_BlueBerrySelectionListener; /** * Saves if this class is currently working on DataStorage changes. * This is a protector variable to avoid recursive calls on event listener functions. */ bool m_InDataStorageChanged; }; QmitkAbstractView::QmitkAbstractView() : d(new QmitkAbstractViewPrivate(this)) { } void QmitkAbstractView::CreatePartControl(void* parent) { // scrollArea QScrollArea* scrollArea = new QScrollArea; //QVBoxLayout* scrollAreaLayout = new QVBoxLayout(scrollArea); scrollArea->setFrameShadow(QFrame::Plain); scrollArea->setFrameShape(QFrame::NoFrame); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); // m_Parent d->m_Parent = new QWidget; //m_Parent->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); this->CreateQtPartControl(d->m_Parent); //scrollAreaLayout->addWidget(m_Parent); //scrollArea->setLayout(scrollAreaLayout); // set the widget now scrollArea->setWidgetResizable(true); scrollArea->setWidget(d->m_Parent); // add the scroll area to the real parent (the view tabbar) QWidget* parentQWidget = static_cast(parent); QVBoxLayout* parentLayout = new QVBoxLayout(parentQWidget); parentLayout->setMargin(0); parentLayout->setSpacing(0); parentLayout->addWidget(scrollArea); // finally set the layout containing the scroll area to the parent widget (= show it) parentQWidget->setLayout(parentLayout); this->AfterCreateQtPartControl(); } void QmitkAbstractView::AfterCreateQtPartControl() { this->SetSelectionProvider(); // REGISTER DATASTORAGE LISTENER this->GetDataStorage()->AddNodeEvent.AddListener( mitk::MessageDelegate1 ( d.data(), &QmitkAbstractViewPrivate::NodeAddedProxy ) ); this->GetDataStorage()->ChangedNodeEvent.AddListener( mitk::MessageDelegate1 ( d.data(), &QmitkAbstractViewPrivate::NodeChangedProxy ) ); this->GetDataStorage()->RemoveNodeEvent.AddListener( mitk::MessageDelegate1 ( d.data(), &QmitkAbstractViewPrivate::NodeRemovedProxy ) ); // REGISTER PREFERENCES LISTENER berry::IBerryPreferences::Pointer prefs = this->GetPreferences().Cast(); if(prefs.IsNotNull()) prefs->OnChanged.AddListener( berry::MessageDelegate1(this, &QmitkAbstractView::OnPreferencesChanged)); // REGISTER FOR WORKBENCH SELECTION EVENTS d->m_BlueBerrySelectionListener = berry::ISelectionListener::Pointer( new berry::SelectionChangedAdapter(d.data(), &QmitkAbstractViewPrivate::BlueBerrySelectionChanged)); - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddPostSelectionListener(/*"org.mitk.views.datamanager",*/ d->m_BlueBerrySelectionListener); + this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddPostSelectionListener(d->m_BlueBerrySelectionListener); // EMULATE INITIAL SELECTION EVENTS // send the current selection berry::IWorkbenchPart::Pointer activePart = this->GetSite()->GetPage()->GetActivePart(); if (activePart.IsNotNull()) { this->OnSelectionChanged(activePart, this->GetCurrentSelection()); } // send preferences changed event this->OnPreferencesChanged(this->GetPreferences().Cast().GetPointer()); } QmitkAbstractView::~QmitkAbstractView() { this->Register(); this->GetDataStorage()->AddNodeEvent.RemoveListener( mitk::MessageDelegate1 ( d.data(), &QmitkAbstractViewPrivate::NodeAddedProxy ) ); this->GetDataStorage()->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1 ( d.data(), &QmitkAbstractViewPrivate::NodeRemovedProxy) ); this->GetDataStorage()->ChangedNodeEvent.RemoveListener( mitk::MessageDelegate1 ( d.data(), &QmitkAbstractViewPrivate::NodeChangedProxy ) ); berry::IBerryPreferences::Pointer prefs = this->GetPreferences().Cast(); if(prefs.IsNotNull()) { prefs->OnChanged.RemoveListener( berry::MessageDelegate1(this, &QmitkAbstractView::OnPreferencesChanged)); // flush the preferences here (disabled, everyone should flush them by themselves at the right moment) // prefs->Flush(); } // REMOVE SELECTION PROVIDER this->GetSite()->SetSelectionProvider(berry::ISelectionProvider::Pointer(NULL)); berry::ISelectionService* s = GetSite()->GetWorkbenchWindow()->GetSelectionService(); if(s) { s->RemovePostSelectionListener(d->m_BlueBerrySelectionListener); } this->UnRegister(false); } void QmitkAbstractView::SetSelectionProvider() { // REGISTER A SELECTION PROVIDER d->m_SelectionProvider = QmitkDataNodeSelectionProvider::Pointer(new QmitkDataNodeSelectionProvider); d->m_SelectionProvider->SetItemSelectionModel(GetDataNodeSelectionModel()); this->GetSite()->SetSelectionProvider(berry::ISelectionProvider::Pointer(d->m_SelectionProvider)); } QItemSelectionModel *QmitkAbstractView::GetDataNodeSelectionModel() const { return d->m_DataNodeSelectionModel; } void QmitkAbstractView::OnPreferencesChanged( const berry::IBerryPreferences* ) { } void QmitkAbstractView::DataStorageModified() { } void QmitkAbstractView::DataStorageChanged(mitk::IDataStorageReference::Pointer /*dsRef*/) { } mitk::IRenderWindowPart* QmitkAbstractView::GetRenderWindowPart( IRenderWindowPartStrategies strategies ) const { berry::IWorkbenchPage::Pointer page = this->GetSite()->GetPage(); // Return the active editor if it implements mitk::IRenderWindowPart mitk::IRenderWindowPart* renderPart = dynamic_cast(page->GetActiveEditor().GetPointer()); if (renderPart) return renderPart; // No suitable active editor found, check visible editors std::list editors = page->GetEditorReferences(); for (std::list::iterator i = editors.begin(); i != editors.end(); ++i) { berry::IWorkbenchPart::Pointer part = (*i)->GetPart(false); if (page->IsPartVisible(part)) { renderPart = dynamic_cast(part.GetPointer()); if (renderPart) return renderPart; } } // No suitable visible editor found, check visible views std::vector views = page->GetViewReferences(); for(std::vector::iterator i = views.begin(); i != views.end(); ++i) { berry::IWorkbenchPart::Pointer part = (*i)->GetPart(false); if (page->IsPartVisible(part)) { renderPart = dynamic_cast(part.GetPointer()); if (renderPart) return renderPart; } } // No strategies given if (strategies == NONE) return 0; mitk::DataStorageEditorInput::Pointer input(new mitk::DataStorageEditorInput(GetDataStorageReference())); bool activate = false; if(strategies & ACTIVATE) { activate = true; } berry::IEditorPart::Pointer editorPart; if(strategies & OPEN) { // This will create a default editor for the given input. If an editor // with that input is already open, the editor is brought to the front. editorPart = mitk::WorkbenchUtil::OpenEditor(page, input, activate); } else if (activate || (strategies & BRING_TO_FRONT)) { // check if a suitable editor is already opened editorPart = page->FindEditor(input); if (activate) { page->Activate(editorPart); } else { page->BringToTop(editorPart); } } return dynamic_cast(editorPart.GetPointer()); } void QmitkAbstractView::RequestRenderWindowUpdate(mitk::RenderingManager::RequestType requestType) { mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); if (renderPart == 0) return; if (mitk::IRenderingManager* renderingManager = renderPart->GetRenderingManager()) { renderingManager->RequestUpdateAll(requestType); } else { renderPart->RequestUpdate(requestType); } } void QmitkAbstractView::HandleException( const char* str, QWidget* parent, bool showDialog ) const { //itkGenericOutputMacro( << "Exception caught: " << str ); MITK_ERROR << str; if ( showDialog ) { QMessageBox::critical ( parent, "Exception caught!", str ); } } void QmitkAbstractView::HandleException( std::exception& e, QWidget* parent, bool showDialog ) const { HandleException( e.what(), parent, showDialog ); } void QmitkAbstractView::WaitCursorOn() { QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); } void QmitkAbstractView::BusyCursorOn() { QApplication::setOverrideCursor( QCursor(Qt::BusyCursor) ); } void QmitkAbstractView::WaitCursorOff() { this->RestoreOverrideCursor(); } void QmitkAbstractView::BusyCursorOff() { this->RestoreOverrideCursor(); } void QmitkAbstractView::RestoreOverrideCursor() { QApplication::restoreOverrideCursor(); } berry::IPreferences::Pointer QmitkAbstractView::GetPreferences() const { berry::IPreferencesService* prefService = d->m_PrefServiceTracker.getService(); // const_cast workaround for bad programming: const uncorrectness this->GetViewSite() should be const std::string id = "/" + (const_cast(this))->GetViewSite()->GetId(); return prefService ? prefService->GetSystemPreferences()->Node(id): berry::IPreferences::Pointer(0); } mitk::DataStorage::Pointer QmitkAbstractView::GetDataStorage() const { mitk::IDataStorageService* dsService = d->m_DataStorageServiceTracker.getService(); if (dsService != 0) { return dsService->GetDataStorage()->GetDataStorage(); } return 0; } mitk::IDataStorageReference::Pointer QmitkAbstractView::GetDataStorageReference() const { mitk::IDataStorageService* dsService = d->m_DataStorageServiceTracker.getService(); if (dsService != 0) { return dsService->GetDataStorage(); } return mitk::IDataStorageReference::Pointer(0); } QList QmitkAbstractView::GetCurrentSelection() const { berry::ISelection::ConstPointer selection( this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection()); - // buffer for the data manager selection mitk::DataNodeSelection::ConstPointer currentSelection = selection.Cast(); return d->DataNodeSelectionToQList(currentSelection); } QList QmitkAbstractView::GetDataManagerSelection() const { berry::ISelection::ConstPointer selection( this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); - // buffer for the data manager selection mitk::DataNodeSelection::ConstPointer currentSelection = selection.Cast(); return d->DataNodeSelectionToQList(currentSelection); } void QmitkAbstractView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& /*nodes*/) { } QList QmitkAbstractViewPrivate::DataNodeSelectionToQList(mitk::DataNodeSelection::ConstPointer currentSelection) const { QList selectedNodes; if(currentSelection.IsNull()) return selectedNodes; mitk::DataNodeObject::Pointer _DataNodeObject; mitk::DataNode::Pointer _DataNode; for(mitk::DataNodeSelection::iterator it = currentSelection->Begin(); it != currentSelection->End(); ++it) { _DataNodeObject = it->Cast(); if(_DataNodeObject.IsNotNull()) { _DataNode = _DataNodeObject->GetDataNode(); if(_DataNode.IsNotNull()) selectedNodes << _DataNode; } } return selectedNodes; } void QmitkAbstractView::NodeAdded( const mitk::DataNode* /*node*/ ) { } void QmitkAbstractView::NodeRemoved( const mitk::DataNode* /*node*/ ) { } void QmitkAbstractView::NodeChanged( const mitk::DataNode* /*node*/ ) { } void QmitkAbstractView::FireNodeSelected( mitk::DataNode::Pointer node ) { QList nodes; nodes << node; this->FireNodesSelected(nodes); } void QmitkAbstractView::FireNodesSelected( const QList& nodes ) { if (nodes.empty()) { d->m_DataNodeSelectionModel->clearSelection(); d->m_DataNodeItemModel->clear(); return; } // The helper data node model is just used for sending selection events. // We add the to be selected nodes and set the selection range to everything. d->m_DataNodeItemModel->clear(); foreach(mitk::DataNode::Pointer node, nodes) { d->m_DataNodeItemModel->AddDataNode(node); } d->m_DataNodeSelectionModel->select(QItemSelection(d->m_DataNodeItemModel->index(0,0), d->m_DataNodeItemModel->index(nodes.size(), 0)), QItemSelectionModel::ClearAndSelect); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkBrainNetworkAnalysis.dox b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkBrainNetworkAnalysis.dox index 47b688052a..81963a008a 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkBrainNetworkAnalysis.dox +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkBrainNetworkAnalysis.dox @@ -1,61 +1,69 @@ /** \page org_brainnetworkanalysis The Brain Network Analysis Module \image html QmitkBrainNetworkAnalysisViewIcon_64.png "Icon of the Module" \section QmitkBrainNetworkAnalysisUserManualSummary Summary This module can be used to create a network from a parcellation and a fiber image as well as to calculate and display network statistics. This document will tell you how to use this module, but it is assumed that you already know how to use MITK in general. Please see \ref QmitkBrainNetworkAnalysisUserManualDetails for more detailed information on usage and supported filters. If you encounter problems using the module, please have a look at the \ref QmitkBrainNetworkAnalysisUserManualTrouble page. \section QmitkBrainNetworkAnalysisUserManualDetails Details Manual sections: - \ref QmitkBrainNetworkAnalysisUserManualOverview - \ref QmitkBrainNetworkAnalysisUserManualUsage - \ref QmitkBrainNetworkAnalysisUserManualTrouble \section QmitkBrainNetworkAnalysisUserManualOverview Overview This module is currently under heavy development and as such the interface as well as the capabilities are likely to change significantly between different versions. This documentation describes the features of this current version. \image html QmitkBrainNetworkAnalysisInterface.png "The interface" \section QmitkBrainNetworkAnalysisUserManualUsage Usage To create a network select first a parcellation of the brain (e.g. as provided by freesurfer ) by CTRL+Leftclick and secondly a fiber image ( as created using tractography module). Then click on the "Create Network" button. To calculate network statistics select a network in the datamanager. At this time the following statistics are calculated for the entire network:
  • The number of vertices in the network
  • The number of edges in the network
  • The number of edges which have the same vertex as beginning and end point
  • The average degree of the nodes in the network
  • The connection density the network (the number of edges divided by the number of possible edges)
  • The unweighted efficiency of the network ( 1 divided by average path length, this is zero for disconnected graphs) +
  • The global clustering
Furthermore some statistics are calculated on a per node basis and displayed as histograms:
  • The degree of each node
  • The (unweighted) betweenness centrality of each node
  • The spread of shortest paths between each pair of nodes (For disconnected graphs the shortest paths with infinite length are omitted for readability)
+Additionally you have the option to create artificial networks, for testing purposes. Currently choices are: +
    +
  • A regular lattice, where each node is placed in a cubic pattern and only connected to its neighbours +
  • A heterogenic sphere, where one node is placed in the center and connected to all nodes on the shell +
  • A random network, where nodes are randomly placed on a spherical shell and randomly connected +
+ \section QmitkBrainNetworkAnalysisUserManualTrouble Troubleshooting No known problems. All other problems.
Please report to the MITK mailing list. See http://www.mitk.org/wiki/Mailinglist on how to do this. */ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkBrainNetworkAnalysisInterface.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkBrainNetworkAnalysisInterface.png index f6ef05bdb9..40d69844a5 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkBrainNetworkAnalysisInterface.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkBrainNetworkAnalysisInterface.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkDiffusionImagingUserManual.dox b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkDiffusionImagingUserManual.dox index 252dc1d22c..d39b26362c 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkDiffusionImagingUserManual.dox +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkDiffusionImagingUserManual.dox @@ -1,124 +1,122 @@ /** \bundlemainpage{org_diffusion} MITK Diffusion Imaging (MITK-DI) This module provides means to diffusion weighted image reconstruction, visualization and quantification. Diffusion tensors as well as different q-ball reconstruction schemes are supported. Q-ball imaging aims at recovering more detailed information about the orientations of fibers from diffusion MRI measurements and, in particular, to resolve the orientations of crossing fibers. Available sections: - \ref QmitkDiffusionImagingUserManualIssues - \ref QmitkDiffusionImagingUserManualPreprocessing - \ref QmitkDiffusionImagingUserManualTensorReconstruction - \ref QmitkDiffusionImagingUserManualQBallReconstruction - \ref QmitkDiffusionImagingUserManualDicomImport - \ref QmitkDiffusionImagingUserManualQuantification - \ref QmitkDiffusionImagingUserManualVisualizationSettings - \ref QmitkDiffusionImagingUserManualReferences - \ref QmitkDiffusionImagingUserManualTechnicalDetail - \ref QmitkDiffusionImagingUserManualSubManuals -\image html overview.png The MITK Diffusion Imaging Module - \section QmitkDiffusionImagingUserManualIssues Known Issues \li Dicom Import: The dicom import has so far only been implemented for Siemens dicom images. MITK-DI is capable of reading the nrrd format, which is documented elsewhere [1, 2]. These files can be created by combining the raw image data with a corresponding textual header file. The file extension should be changed from *.nrrd to *.dwi or from *.nhdr to *.hdwi respectively in order to let MITK-DI recognize the diffusion related header information provided in the files. \section QmitkDiffusionImagingUserManualPreprocessing Preprocessing -The preprocessing view gives an overview over the important features of a diffusion weighted image like the number of gradient directions, b-value and the measurement frame. Additionally it allows the extraction of the B0 image and the generation of a binary brain mask. The image volume can be modified by applying a new mesurement frame, which is useful if the measurement frame is not set correctly in the image header, or by averaging redundant gradient directions. +The preprocessing view gives an overview over the important features of a diffusion weighted image like the number of gradient directions, b-value and the measurement frame. Additionally it allows the extraction of the B0 image, reduction of gradient directions and the generation of a binary brain mask. The image volume can be modified by applying a new mesurement frame, which is useful if the measurement frame is not set correctly in the image header, or by averaging redundant gradient directions. \image html prepro1.png Preprocessing \section QmitkDiffusionImagingUserManualTensorReconstruction Tensor Reconstruction The tensor reconstruction view allows ITK based tensor reconstruction [3]. The advanced settings for ITK reconstruction let you configure a manual threshold on the non-diffusion weighted image. All voxels below this threshold will not be reconstructed and left blank. It is also possible to check for negative eigenvalues. The according voxels are also left blank. \image html tensor1.png ITK tensor reconstruction A few seconds (depending on the image size) after the reconstruction button is hit, a colored image should appear in the main window. \image html tensor4.png Tensor image after reconstruction The view also allows the generation of artificial diffusion weighted or Q-Ball images from the selected tensor image. The ODFs of the Q-Ball image are directly initialized from the tensor values and afterwards normalized. The diffusion weighted image is estimated using the l2-norm image of the tensor image as B0. The gradient images are afterwards generated using the standard tensor equation. \section QmitkDiffusionImagingUserManualQBallReconstruction Q-Ball Reconstruction The q-ball reonstruction bundle implements a variety of reconstruction methods. The different reconstruction methods are described in the following: \li Numerical: The original, numerical q-ball reconstruction presented by Tuch et al. [5] \li Standard (SH): Descoteaux's reconstruction based on spherical harmonic basis functions [6] \li Solid Angle (SH): Aganj's reconstruction with solid angle consideration [7] \li ADC-profile only: The ADC-profile reconstructed with spherical harmonic basis functions \li Raw signal only: The raw signal reconstructed with spherical harmonic basis functions \image html qballs1.png The q-ball resonstruction view B0 threshold works the same as in tensor reconstruction. The maximum l-level configures the size of the spherical harmonics basis. Larger l-values (e.g. l=8) allow higher levels of detail, lower levels are more stable against noise (e.g. l=4). Lambda is a regularisation parameter. Set it to 0 for no regularisation. lambda = 0.006 has proven to be a stable choice under various settings. \image html qballs2.png Advanced q-ball reconstruction settings This is how a q-ball image should initially look after reconstruction. Standard q-balls feature a relatively low GFA and thus appear rather dark. Adjust the level-window to solve this. \image html qballs3.png q-ball image after reconstruction \section QmitkDiffusionImagingUserManualDicomImport Dicom Import The dicom import does not cover all hardware manufacturers but only Siemens dicom images. MITK-DI is also capable of reading the nrrd format, which is documented elsewhere [1, 2]. These files can be created by combining the raw image data with a corresponding textual header file. The file extension should be changed from *.nrrd to *.dwi or from *.nhdr to *.hdwi respectively in order to let MITK-DI recognize the diffusion related header information provided in the files. In case your dicom images are readable by MITK-DI, select one or more input dicom folders and click import. Each input folder must only contain DICOM-images that can be combined into one vector-valued 3D output volume. Different patients must be loaded from different input-folders. The folders must not contain other acquisitions (e.g. T1,T2,localizer). In case many imports are performed at once, it is recommended to set the the optional output folder argument. This prevents the images from being kept in memory. \image html dicom1.png Dicom import The option "Average duplicate gradients" accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "blur radius" > 0 is configured. \section QmitkDiffusionImagingUserManualQuantification Quantification The quantification view allows the derivation of different scalar anisotropy measures for the reconstructed tensors (Fractional Anisotropy, Relative Anisotropy, Axial Diffusivity, Radial Diffusivity) or q-balls (Generalized Fractional Anisotropy). \image html quantification.png Anisotropy quantification \section QmitkDiffusionImagingUserManualVisualizationSettings ODF Visualization Setting In this small view, the visualization of ODFs and diffusion images can be configured. Depending on the selected image in the data storage, different options are shown here. For tensor or q-ball images, the visibility of glyphs in the different render windows (T)ransversal, (S)agittal, and (C)oronal can be configured here. The maximal number of glyphs to display can also be configured here for. This is usefull to keep the system response time during rendering feasible. The other options configure normalization and scaling of the glyphs. In diffusion images, a slider lets you choose the desired image channel from the vector of images (each gradient direction one image) for rendering. Furthermore reinit can be performed and texture interpolation toggled. This is how a visualization with activated glyphs should look like: \image html visualization3.png Q-ball image with ODF glyph visibility toggled ON \section QmitkDiffusionImagingUserManualReferences References 1. http://teem.sourceforge.net/nrrd/format.html 2. http://www.cmake.org/Wiki/Getting_Started_with_the_NRRD_Format 3. C.F.Westin, S.E.Maier, H.Mamata, A.Nabavi, F.A.Jolesz, R.Kikinis, "Processing and visualization for Diffusion tensor MRI", Medical image Analysis, 2002, pp 93-108 5. Tuch, D.S., 2004. Q-ball imaging. Magn Reson Med 52, 1358-1372. 6. Descoteaux, M., Angelino, E., Fitzgibbons, S., Deriche, R., 2007. Regularized, fast, and robust analytical Q-ball imaging. Magn Reson Med 58, 497-510. 7. Aganj, I., Lenglet, C., Sapiro, G., 2009. ODF reconstruction in q-ball imaging with solid angle consideration. Proceedings of the Sixth IEEE International Symposium on Biomedical Imaging Boston, MA. 8. Goh, A., Lenglet, C., Thompson, P.M., Vidal, R., 2009. Estimating Orientation Distribution Functions with Probability Density Constraints and Spatial Regularity. Med Image Comput Comput Assist Interv Int Conf Med Image Comput Comput Assist Interv LNCS 5761, 877 ff. \section QmitkDiffusionImagingUserManualTechnicalDetail Technical Information for Developers The diffusion imaging module uses additional properties beside the ones in use in other modules, for further information see \subpage DiffusionImagingPropertiesPage . \section QmitkDiffusionImagingUserManualSubManuals Manuals of componentes The MITK Diffusion tools consist of further components, which have their own documentation, see: \li \subpage org_fiberprocessing \li \subpage org_gibbstracking \li \subpage org_odfdetails \li \subpage org_pvanalysis \li \subpage screenshot_maker \li \subpage org_stochastictracking \li \subpage org_ivim \li \subpage org_brainnetworkanalysis - + \li \subpage org_tractbasedspatialstatistics */ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkFiberProcessingViewUserManual.dox b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkFiberProcessingViewUserManual.dox index a93e08af92..9c1e982b69 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkFiberProcessingViewUserManual.dox +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkFiberProcessingViewUserManual.dox @@ -1,25 +1,23 @@ /** \page org_fiberprocessing Fiber Processing View This view provides everything needed to process fiber bundles. -Available sections: - - \ref QmitkFiberProcessingUserManualFiberManpulation - - \ref QmitkFiberProcessingUserManualFiberProcessing - \image html fiberprocessing.png The Fiber Processing View -\section QmitkFiberProcessingUserManualFiberManpulation Fiber Bundle Manipulation - Fiber extraction: -Place ROIs in the 2D render widgets (cricles or polygons) and extract fibers from the bundle that pass through these ROIs by selecting the according ROI and fiber bundle in the datamanger and starting the extraction. The ROIs can be combined via logical operations. All fibers that pass through the thus generated composite ROI are extracted. +Place ROIs in the 2D render widgets (cricles or polygons) and extract fibers from the bundle that pass through these ROIs by selecting the according ROI and fiber bundle in the datamanger and starting the extraction. The ROIs can be combined via logical operations. All fibers that pass through the thus generated composite ROI are extracted. The extraction can also be performed using 3D ROIs represented as closed surface meshes. In this extraction method, the logical operations are not implemented at the moment. -\section QmitkFiberProcessingUserManualFiberProcessing Generation of additional information from fiber bundles +The selected fiber bundle can be smoothed by interpolating the fiber points using Kochanek splines with the specified number of points per cm. +If a float image with pixel values between 0 and 1 is selcted, the fiber bundle can be colored according to the pixel values. + +Generation of additional data from fiber bundles: \li Tract density image: generate a 2D heatmap from a fiber bundle \li Binary envelope: generate a binary image from a fiber bundle \li Fiber bundle image: generate a 2D rgba image representation of the fiber bundle \li Fiber endings image: generate a 2D binary image showing the locations of fiber endpoints \li Fiber endings pointset: generate a poinset containing the locations of fiber endpoints + */ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkGibbsTrackingViewUserManual.dox b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkGibbsTrackingViewUserManual.dox index e4d56d33b5..433c4e7775 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkGibbsTrackingViewUserManual.dox +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkGibbsTrackingViewUserManual.dox @@ -1,40 +1,44 @@ /** \page org_gibbstracking Gibbs Tracking View This view provides the user interface for the Gibbs Tracking algorithm, a global fiber tracking algorithm, originally proposed by Reisert et.al. [1]. Available sections: - \ref QmitkGibbsTrackingUserManualInputData - \ref QmitkGibbsTrackingUserManualParameters - \ref QmitkGibbsTrackingUserManualTrackingSurveillance - \ref QmitkGibbsTrackingUserManualReferences \image html gibbstrackingview.png The Gibbs Tracking View \section QmitkGibbsTrackingUserManualInputData Input Data Mandatory Input: \li One Q-Ball image selected in the datamanager Optional Input: \li Mask Image: Float image used as probability mask for the generation of fiber segments. Usually used as binary brain mask to reduce the searchspace of the algorithm and to avoid fibers resulting from noise outside of the brain. -\li GFA Image: Float image used to automatically determine the "particle weight" parameter. \section QmitkGibbsTrackingUserManualParameters Q-Ball Reconstruction \li Number of iterations: More iterations causes the algorithm to be more stable but also to take longer to finish the tracking. Rcommended: 10â·-10â¹ iterations. \li Particle length/width/weight controlling the contribution of each particle to the model M \li Start and end temperature controlling how fast the process reaches a stable state. (usually no change needed) \li Weighting between the internal (affinity of the model to long and straigt fibers) and external energy (affinity of the model towards the data). (usually no change needed). -\li Minimum fiber length constraint. Fibers containing less segments are discarded after the tracking. (usually no change needed) +\li Minimum fiber length constraint (in mm). Shorter fibers are discarded after the tracking. + +The automatic selection of parameters for the particle length/width and weight are determined directly from the input image using information about the image spacing and GFA. + +\image html gibbstrackingviewadvanced.png Advanced Tracking Parameters \section QmitkGibbsTrackingUserManualTrackingSurveillance Surveilance of the tracking process -Once started, the tracking can be monitored via the textual output that informs about the tracking progress and several stats of the current state of the algorithm. If enabled, the intermediate tracking results are displayed in the renderwindows each second. This live visualization should usually be disabled for performance reasons. It can be turned on and off during the tracking process via the according checkbox. +Once started, the tracking can be monitored via the textual output that informs about the tracking progress and several stats of the current state of the algorithm. +If enabled, the intermediate tracking results are displayed in the renderwindows each second. This live visualization should usually be disabled for performance reasons. It can be turned on and off during the tracking process via the according checkbox. The button next to this checkbox allows the visualization of only the next iteration step. \section QmitkGibbsTrackingUserManualReferences References [1] Reisert, M., Mader, I., Anastasopoulos, C., Weigel, M., Schnell, S., Kiselev, V.: Global fiber reconstruction becomes practical. Neuroimage 54 (2011) 955-962 */ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkOdfDetailsViewUserManual.dox b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkOdfDetailsViewUserManual.dox index e7098f064f..7885ebd5dc 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkOdfDetailsViewUserManual.dox +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkOdfDetailsViewUserManual.dox @@ -1,11 +1,8 @@ /** \page org_odfdetails ODF Details View -This view provides detailed information about the orentation distribution function at the current crosshair position (if a Q-Ball image is selected). A visualization of the ODF as well as the ODF values and according statistical information are displayed. +This view provides detailed information about the orentation distribution function at the current crosshair position (if a Tensor/Q-Ball image is selected). A visualization of the ODF as well as statistical information are displayed. \image html odfdetails.png The Gibbs Tracking View -\section QmitkOdfDetailsUserManualInputData Issues -At the moment this view can opnly process Q-Ball images but not tensor images. Also the normalization properties etc. of the image as well as the correct rotation of the ODF are currently not incorporated into the views visualization. - */ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkStochasticTrackingViewUserManual.dox b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkStochasticTrackingViewUserManual.dox index 7f899fc34a..7573738c5e 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkStochasticTrackingViewUserManual.dox +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkStochasticTrackingViewUserManual.dox @@ -1,36 +1,31 @@ /** \page org_stochastictracking Stochastic Tracking View This view provides the user interface for the Stochastic Fibertracking algorithm, proposed by Ngo [1]. Available sections: - \ref QmitkStochasticTrackingUserManualInputData - \ref QmitkStochasticTrackingUserManualParameters - \ref QmitkStochasticTrackingUserManualReferences \image html stochastictrackingview.png Stochastic Tracking View -\image html segmentationview.png Segmentation View to define ROIs - \section QmitkStochasticTrackingUserManualInputData Input Data Mandatory Input: -\li For a successful execution of the stochastic tractography filter, a DWI input and a binary image defining the desired ROI are required. The ROI serves as the origin from where on the fibers are beeing tracked. Both, DWI and ROI data can be imported via drag nÕ drop or via the opening dialog box provided by MITK. Alternatively, the segmentation view offers tools for generating ROI data. \li One DWI Image image selected in the datamanager \li One or more ROIs selected in the datamanager +For a successful execution of the stochastic tractography filter, a DWI input and a binary image defining the desired ROI are required. The ROI serves as the origin from where on the fibers are beeing tracked. To generate the seed ROI the segmentation view in the quantification perspective can be used or a binary image can be loaded. \section QmitkStochasticTrackingUserManualParameters Input Parameters - -\li Parameters such as max. tract length (Tract_len), number of total tracts per voxel (TotalTracts) and likelihood cache size in MB (Lkhd chache) are individually set by the user. - +\li Parameters such as max. tract length, number of seeds per voxel and likelihood cache size in MB can be controlled individually. \li After successfully setting necessary Input and Parameter, pressing the command button executes the algorithm. - \section QmitkStochasticTrackingUserManualReferences References [1] Tri M. Ngo, Polina Golland, and Tri M. Ngo. A stochastic tractography system and applications, 2007 */ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkTbssViewUserManual.dox b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkTbssViewUserManual.dox index e213b09d22..26b49bbb0a 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkTbssViewUserManual.dox +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkTbssViewUserManual.dox @@ -1,61 +1,59 @@ /** -\bundlemainpage{org.mitk.views.tractbasedspatialstatistics} The TBSS Module +\page org_tractbasedspatialstatistics The TBSS Module \image html tbss.png "Icon of the Module" \section QmitkTractbasedSpatialStatistics Summary This module can be used to locally explore data resulting from preprocessing with the TBSS module of FSL This document will tell you how to use this module, but it is assumed that you already know how to use MITK in general and how to work with the TBSS module of FSL. If you encounter problems using the module, please have a look at the \ref QmitkTractbasedSpatialStatisticsUserManualTrouble page. Sections: - \ref QmitkTractbasedSpatialStatisticsUserManualOverview - \ref QmitkTractbasedSpatialStatisticsUserManualFSLImport - \ref QmitkTractbasedSpatialStatisticsUserManualRois - \ref QmitkTractbasedSpatialStatisticsUserManualProfiles - \ref QmitkTractbasedSpatialStatisticsUserManualTroubleshooting - \ref QmitkTractbasedSpatialStatisticsUserManualReferences \section QmitkTractbasedSpatialStatisticsUserManualOverview Overview This module is currently under development and as such the interface as well as the capabilities are likely to change significantly between different versions. This documentation describes the features of this current version. \section QmitkTractbasedSpatialStatisticsUserManualFSLImport FSL Import The FSL import allows to import data that has been preprocessed by FSL. FSL creates output images that typically have names like all_FA_skeletonized.nii.gz that are 4-dimensional images that contain registered images of all subjects. By loading this 4D image into the datamanager and listing the groups with the correct number of subjects, in the order of occurrence in the 4D image, in the TBSS-View using the Add button and clicking the import subject data a TBSS file is created that contains all the information needed for tract analysis. The diffusion measure of the image can be set as well. \image html fslimport.png "FSL Import" \section QmitkTractbasedSpatialStatisticsUserManualRois Regions of Interest (ROIs) To create a ROI the mean FA skeleton (typically called mean_FA_skeleton.nii.gz) that is created by FSL should be loaded in to the datamanager and selected. By using the Pointlistwidget points should be set on the skeleton (make sure to select points with relatively high FA values). Points are set by first selecting the button with the '+' and than shift-leftclick in the image. When the correct image is selected in the datamanager the Create ROI button is enabled. Clicking this will create a region of interest that passes through the previously selected points. The roi appears in the datamanager. Before doing so, the name of the roi and the information on the structure on which the ROI lies can be set. This will be saved as extra information in the roi-image. Before the ROI is calculated, a pop-up window will ask the user to provide a threshold value. This should be the same threshold that was previously used in FSL to create a binary mask of the FA skeleton. When this is not done correctly, the region of interest will possible contain zero-valued voxels. \image html tbssRoi.png "Regions of Interest (ROIs)" \section QmitkTractbasedSpatialStatisticsUserManualProfiles y selecting a tbss image with group information and a region of interest image (as was created in a previous stap). A profile plot is drawn in the plot canvas. By clicking in the graph the crosshairs jump to the corresponding location in the image. \image html profiles.png "Profile plots" \section QmitkTractbasedSpatialStatisticsUserManualTroubleshooting Troubleshooting -No known problems. - -All other problems.
Please report to the MITK mailing list. See http://www.mitk.org/wiki/Mailinglist on how to do this. \section QmitkTractbasedSpatialStatisticsUserManualReferences References 1. S.M. Smith, M. Jenkinson, H. Johansen-Berg, D. Rueckert, T.E. Nichols, C.E. Mackay, K.E. Watkins, O. Ciccarelli, M.Z. Cader, P.M. Matthews, and T.E.J. Behrens. Tract-based spatial statistics: Voxelwise analysis of multi-subject diffusion data. NeuroImage, 31:1487-1505, 2006. -2. S.M. Smith, M. Jenkinson, M.W. Woolrich, C.F. Beckmann, T.E.J. Behrens, H. Johansen-Berg, P.R. Bannister, M. De Luca, I. Drobnjak, D.E. Flitney, R. Niazy, J. Saunders, J. Vickers, Y. Zhang, N. De Stefano, J.M. Brady, and P.M. Matthews. Advances in functional and structural MR image analysis and implementation as FSL. NeuroImage, 23(S1):208-219, 2004. \ No newline at end of file +2. S.M. Smith, M. Jenkinson, M.W. Woolrich, C.F. Beckmann, T.E.J. Behrens, H. Johansen-Berg, P.R. Bannister, M. De Luca, I. Drobnjak, D.E. Flitney, R. Niazy, J. Saunders, J. Vickers, Y. Zhang, N. De Stefano, J.M. Brady, and P.M. Matthews. Advances in functional and structural MR image analysis and implementation as FSL. NeuroImage, 23(S1):208-219, 2004. +*/ \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/fiberprocessing.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/fiberprocessing.png index 595de7ff23..bc8e88b655 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/fiberprocessing.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/fiberprocessing.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/gibbstrackingview.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/gibbstrackingview.png index 3e20911bed..e30a8b2059 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/gibbstrackingview.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/gibbstrackingview.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/gibbstrackingviewadvanced.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/gibbstrackingviewadvanced.png new file mode 100644 index 0000000000..fc41fe616d Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/gibbstrackingviewadvanced.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/ivimview.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/ivimview.png index 363485be94..d5941c42fd 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/ivimview.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/ivimview.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/odfdetails.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/odfdetails.png index 59ac0b25f6..1e55cd7f20 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/odfdetails.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/odfdetails.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/prepro1.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/prepro1.png index d387eb6e62..90b679ee66 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/prepro1.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/prepro1.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/pvanalysisview.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/pvanalysisview.png index 5967855bcc..7ceb6659d8 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/pvanalysisview.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/pvanalysisview.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/qballs1.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/qballs1.png index eb8e18aed4..379f21dc7e 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/qballs1.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/qballs1.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/qballs3.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/qballs3.png index edfdd5178d..8f5155ae2f 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/qballs3.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/qballs3.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/quantification.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/quantification.png index 463fa4061f..a768b66dca 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/quantification.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/quantification.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/stochastictrackingview.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/stochastictrackingview.png index 09705b8ae6..f4f1023de6 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/stochastictrackingview.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/stochastictrackingview.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/tensor1.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/tensor1.png index 8720375677..6aae4ff4d9 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/tensor1.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/tensor1.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/tensor4.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/tensor4.png index f426bd2aba..c4708fff21 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/tensor4.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/tensor4.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/visualization3.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/visualization3.png index edef97fb66..acc816f8d7 100644 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/visualization3.png and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/visualization3.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkODFDetailsWidget.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkODFDetailsWidget.cpp index 3cc2bbfd30..faf92ede4a 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkODFDetailsWidget.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkODFDetailsWidget.cpp @@ -1,64 +1,64 @@ /*========================================================================= Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "QmitkODFDetailsWidget.h" #include QmitkODFDetailsWidget::QmitkODFDetailsWidget( QWidget * parent ) : QmitkPlotWidget(parent) { m_Plot->setCanvasLineWidth(0); m_Plot->setMargin(0); QwtLinearScaleEngine* scale = new QwtLinearScaleEngine(); m_Plot->setAxisScaleEngine(0, scale); m_Plot->setAxisScale ( 0, -0.5, 0.5 ); } QmitkODFDetailsWidget::~QmitkODFDetailsWidget() { } void QmitkODFDetailsWidget::SetParameters( itk::OrientationDistributionFunction odf ) { this->Clear(); std::vector xVals; std::vector yVals; - float max = itk::NumericTraits::min(); + float max = itk::NumericTraits::NonpositiveMin(); float min = itk::NumericTraits::max(); for (int i=0; imax) max = odf[i]; if (odf[i]0) m_Plot->setAxisScale ( 0, 0, max ); else m_Plot->setAxisScale ( 0, min, max ); int curveId = this->InsertCurve( "ODF Values" ); this->SetCurveData( curveId, xVals, yVals ); this->SetCurvePen( curveId, QPen(Qt::blue, 0.5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin) ); QwtLegend* legend = new QwtLegend(); //m_Plot->insertLegend(legend, QwtPlot::BottomLegend); this->Replot(); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisView.cpp index e8e6630bcb..bf4b45eab5 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisView.cpp @@ -1,549 +1,678 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ // ####### Blueberry includes ####### #include #include // ####### Qmitk includes ####### #include "QmitkBrainNetworkAnalysisView.h" #include "QmitkStdMultiWidget.h" // ####### Qt includes ####### #include // ####### ITK includes ####### #include // ####### MITK includes ####### #include #include "mitkConnectomicsSyntheticNetworkGenerator.h" #include "mitkConnectomicsSimulatedAnnealingManager.h" #include "mitkConnectomicsSimulatedAnnealingPermutationModularity.h" #include "mitkConnectomicsSimulatedAnnealingCostFunctionModularity.h" // Includes for image casting between ITK and MITK #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" const std::string QmitkBrainNetworkAnalysisView::VIEW_ID = "org.mitk.views.brainnetworkanalysis"; QmitkBrainNetworkAnalysisView::QmitkBrainNetworkAnalysisView() : QmitkFunctionality() , m_Controls( 0 ) , m_MultiWidget( NULL ) , m_ConnectomicsNetworkCreator( mitk::ConnectomicsNetworkCreator::New() ) , m_demomode( false ) , m_currentIndex( 0 ) { } QmitkBrainNetworkAnalysisView::~QmitkBrainNetworkAnalysisView() { } void QmitkBrainNetworkAnalysisView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkBrainNetworkAnalysisViewControls; m_Controls->setupUi( parent ); QObject::connect( m_Controls->convertToRGBAImagePushButton, SIGNAL(clicked()), this, SLOT(OnConvertToRGBAImagePushButtonClicked()) ); QObject::connect( m_Controls->networkifyPushButton, SIGNAL(clicked()), this, SLOT(OnNetworkifyPushButtonClicked()) ); QObject::connect( m_Controls->syntheticNetworkCreationPushButton, SIGNAL(clicked()), this, SLOT(OnSyntheticNetworkCreationPushButtonClicked()) ); QObject::connect( (QObject*)( m_Controls->syntheticNetworkComboBox ), SIGNAL(currentIndexChanged (int)), this, SLOT(OnSyntheticNetworkComboBoxCurrentIndexChanged(int)) ); QObject::connect( (QObject*)( m_Controls->modularizePushButton ), SIGNAL(clicked()), this, SLOT(OnModularizePushButtonClicked()) ); } // GUI is different for developer and demo mode m_demomode = false; if( m_demomode ) { this->m_Controls->convertToRGBAImagePushButton->hide(); this->m_Controls->networkifyPushButton->show(); this->m_Controls->networkifyPushButton->setText( "Create Network" ); this->m_Controls->modularizePushButton->hide(); this->m_Controls->syntheticNetworkOptionsGroupBox->show(); //--------------------------- fill comboBox--------------------------- this->m_Controls->syntheticNetworkComboBox->insertItem(0,"Regular lattice"); this->m_Controls->syntheticNetworkComboBox->insertItem(1,"Heterogenic sphere"); this->m_Controls->syntheticNetworkComboBox->insertItem(2,"Random network"); } else { this->m_Controls->convertToRGBAImagePushButton->show(); this->m_Controls->networkifyPushButton->show(); this->m_Controls->networkifyPushButton->setText( "Networkify" ); this->m_Controls->modularizePushButton->show(); this->m_Controls->syntheticNetworkOptionsGroupBox->show(); //--------------------------- fill comboBox--------------------------- this->m_Controls->syntheticNetworkComboBox->insertItem(0,"Regular lattice"); this->m_Controls->syntheticNetworkComboBox->insertItem(1,"Heterogenic sphere"); this->m_Controls->syntheticNetworkComboBox->insertItem(2,"Random network"); this->m_Controls->syntheticNetworkComboBox->insertItem(3,"Scale free network"); this->m_Controls->syntheticNetworkComboBox->insertItem(4,"Small world network"); } + + this->WipeDisplay(); } void QmitkBrainNetworkAnalysisView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkBrainNetworkAnalysisView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkBrainNetworkAnalysisView::WipeDisplay() { + m_Controls->lblWarning->setVisible( true ); + m_Controls->inputImageOneNameLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); + m_Controls->inputImageOneNameLabel->setVisible( false ); + m_Controls->inputImageOneLabel->setVisible( false ); + m_Controls->inputImageTwoNameLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); + m_Controls->inputImageTwoNameLabel->setVisible( false ); + m_Controls->inputImageTwoLabel->setVisible( false ); m_Controls->numberOfVerticesLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->numberOfEdgesLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->numberOfSelfLoopsLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->averageDegreeLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->connectionDensityLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->efficiencyLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->globalClusteringLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->betweennessNetworkHistogramCanvas->SetHistogram( NULL ); m_Controls->degreeNetworkHistogramCanvas->SetHistogram( NULL ); m_Controls->shortestPathNetworkHistogramCanvas->SetHistogram( NULL ); m_Controls->betweennessNetworkHistogramCanvas->update(); m_Controls->degreeNetworkHistogramCanvas->update(); m_Controls->shortestPathNetworkHistogramCanvas->update(); + m_Controls->betweennessNetworkHistogramCanvas->Clear(); + m_Controls->degreeNetworkHistogramCanvas->Clear(); + m_Controls->shortestPathNetworkHistogramCanvas->Clear(); + m_Controls->betweennessNetworkHistogramCanvas->Replot(); + m_Controls->degreeNetworkHistogramCanvas->Replot(); + m_Controls->shortestPathNetworkHistogramCanvas->Replot(); } void QmitkBrainNetworkAnalysisView::OnSelectionChanged( std::vector nodes ) { this->WipeDisplay(); + // Valid options are either + // 1 image (parcellation) + // + // 1 image (parcellation) + // 1 fiber bundle + // + // 1 network + if( nodes.size() > 2 ) + { + return; + } + + bool alreadyFiberBundleSelected( false ), alreadyImageSelected( false ), currentFormatUnknown( true ); // iterate all selected objects, adjust warning visibility for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; + currentFormatUnknown = true; if( node.IsNotNull() && dynamic_cast(node->GetData()) ) { + currentFormatUnknown = false; + if( alreadyImageSelected ) + { + this->WipeDisplay(); + return; + } + alreadyImageSelected = true; m_Controls->lblWarning->setVisible( false ); - return; + m_Controls->inputImageOneNameLabel->setText(node->GetName().c_str()); + m_Controls->inputImageOneNameLabel->setVisible( true ); + m_Controls->inputImageOneLabel->setVisible( true ); } + if( node.IsNotNull() && dynamic_cast(node->GetData()) ) { + currentFormatUnknown = false; + // a fiber bundle has to be in conjunction with a parcellation + if( nodes.size() != 2 || alreadyFiberBundleSelected ) + { + this->WipeDisplay(); + return; + } + alreadyFiberBundleSelected = true; + m_Controls->lblWarning->setVisible( false ); + m_Controls->inputImageTwoNameLabel->setText(node->GetName().c_str()); + m_Controls->inputImageTwoNameLabel->setVisible( true ); + m_Controls->inputImageTwoLabel->setVisible( true ); + } + + { // network section mitk::ConnectomicsNetwork* network = dynamic_cast( node->GetData() ); if( node.IsNotNull() && network ) { + currentFormatUnknown = false; + if( nodes.size() != 1 ) + { + // only valid option is a single network + this->WipeDisplay(); + return; + } m_Controls->lblWarning->setVisible( false ); + m_Controls->inputImageOneNameLabel->setText(node->GetName().c_str()); + m_Controls->inputImageOneNameLabel->setVisible( true ); + m_Controls->inputImageOneLabel->setVisible( true ); int noVertices = network->GetNumberOfVertices(); int noEdges = network->GetNumberOfEdges(); int noSelfLoops = network->GetNumberOfSelfLoops(); double averageDegree = network->GetAverageDegree(); double connectionDensity = network->GetConnectionDensity(); double globalClustering = network->GetGlobalClusteringCoefficient(); m_Controls->numberOfVerticesLabel->setText( QString::number( noVertices ) ); m_Controls->numberOfEdgesLabel->setText( QString::number( noEdges ) ); m_Controls->numberOfSelfLoopsLabel->setText( QString::number( noSelfLoops ) ); m_Controls->averageDegreeLabel->setText( QString::number( averageDegree ) ); m_Controls->connectionDensityLabel->setText( QString::number( connectionDensity ) ); m_Controls->globalClusteringLabel->setText( QString::number( globalClustering ) ); mitk::ConnectomicsNetwork::Pointer connectomicsNetwork( network ); mitk::ConnectomicsHistogramsContainer *histogramContainer = histogramCache[ connectomicsNetwork ]; - m_Controls->betweennessNetworkHistogramCanvas->SetHistogram( histogramContainer->GetBetweennessHistogram() ); - m_Controls->degreeNetworkHistogramCanvas->SetHistogram( histogramContainer->GetDegreeHistogram() ); - m_Controls->shortestPathNetworkHistogramCanvas->SetHistogram( histogramContainer->GetShortestPathHistogram() ); - m_Controls->betweennessNetworkHistogramCanvas->DrawProfiles(); - m_Controls->degreeNetworkHistogramCanvas->DrawProfiles(); - m_Controls->shortestPathNetworkHistogramCanvas->DrawProfiles(); - - double efficiency = histogramContainer->GetShortestPathHistogram()->GetEfficiency(); + if(histogramContainer) + { + m_Controls->betweennessNetworkHistogramCanvas->SetHistogram( histogramContainer->GetBetweennessHistogram() ); + m_Controls->degreeNetworkHistogramCanvas->SetHistogram( histogramContainer->GetDegreeHistogram() ); + m_Controls->shortestPathNetworkHistogramCanvas->SetHistogram( histogramContainer->GetShortestPathHistogram() ); + m_Controls->betweennessNetworkHistogramCanvas->DrawProfiles(); + m_Controls->degreeNetworkHistogramCanvas->DrawProfiles(); + m_Controls->shortestPathNetworkHistogramCanvas->DrawProfiles(); - m_Controls->efficiencyLabel->setText( QString::number( efficiency ) ); + double efficiency = histogramContainer->GetShortestPathHistogram()->GetEfficiency(); - return; + m_Controls->efficiencyLabel->setText( QString::number( efficiency ) ); + } } - } - } + } // end network section - m_Controls->lblWarning->setVisible( true ); + if ( currentFormatUnknown ) + { + this->WipeDisplay(); + return; + } + } // end for loop } void QmitkBrainNetworkAnalysisView::OnSyntheticNetworkComboBoxCurrentIndexChanged(int currentIndex) { m_currentIndex = currentIndex; switch (m_currentIndex) { case 0: this->m_Controls->parameterOneLabel->setText( "Nodes per side" ); this->m_Controls->parameterTwoLabel->setText( "Internode distance" ); this->m_Controls->parameterOneSpinBox->setEnabled( true ); this->m_Controls->parameterOneSpinBox->setValue( 5 ); this->m_Controls->parameterTwoDoubleSpinBox->setEnabled( true ); this->m_Controls->parameterTwoDoubleSpinBox->setMaximum( 999.999 ); this->m_Controls->parameterTwoDoubleSpinBox->setValue( 10.0 ); break; case 1: this->m_Controls->parameterOneLabel->setText( "Number of nodes" ); this->m_Controls->parameterTwoLabel->setText( "Radius" ); this->m_Controls->parameterOneSpinBox->setEnabled( true ); this->m_Controls->parameterOneSpinBox->setValue( 1000 ); this->m_Controls->parameterTwoDoubleSpinBox->setEnabled( true ); this->m_Controls->parameterTwoDoubleSpinBox->setMaximum( 999.999 ); this->m_Controls->parameterTwoDoubleSpinBox->setValue( 50.0 ); break; case 2: this->m_Controls->parameterOneLabel->setText( "Number of nodes" ); this->m_Controls->parameterTwoLabel->setText( "Edge percentage" ); this->m_Controls->parameterOneSpinBox->setEnabled( true ); this->m_Controls->parameterOneSpinBox->setValue( 100 ); this->m_Controls->parameterTwoDoubleSpinBox->setEnabled( true ); this->m_Controls->parameterTwoDoubleSpinBox->setMaximum( 1.0 ); this->m_Controls->parameterTwoDoubleSpinBox->setValue( 0.5 ); break; case 3: //GenerateSyntheticScaleFreeNetwork( network, 1000 ); break; case 4: //GenerateSyntheticSmallWorldNetwork( network, 1000 ); break; default: this->m_Controls->parameterOneLabel->setText( "Parameter 1" ); this->m_Controls->parameterTwoLabel->setText( "Paramater 2" ); this->m_Controls->parameterOneSpinBox->setEnabled( false ); this->m_Controls->parameterOneSpinBox->setValue( 0 ); this->m_Controls->parameterTwoDoubleSpinBox->setEnabled( false ); this->m_Controls->parameterTwoDoubleSpinBox->setValue( 0.0 ); } } void QmitkBrainNetworkAnalysisView::OnSyntheticNetworkCreationPushButtonClicked() { + // warn if trying to create a very big network + // big network is a network with > 5000 nodes (estimate) + // this might fill up the memory to the point it freezes + int numberOfNodes( 0 ); + switch (m_currentIndex) { + case 0: + numberOfNodes = this->m_Controls->parameterOneSpinBox->value() + * this->m_Controls->parameterOneSpinBox->value() + * this->m_Controls->parameterOneSpinBox->value(); + break; + case 1: + numberOfNodes = this->m_Controls->parameterOneSpinBox->value(); + break; + case 2: + numberOfNodes = this->m_Controls->parameterOneSpinBox->value(); + break; + case 3: + // not implemented yet + break; + case 4: + // not implemented yet + break; + default: + break; + + } + + if( numberOfNodes > 5000 ) + { + QMessageBox msgBox; + msgBox.setText("Trying to generate very large network."); + msgBox.setIcon( QMessageBox::Warning ); + msgBox.setInformativeText("You are trying to generate a network with more than 5000 nodes, this is very resource intensive and might lead to program instability. Proceed with network generation?"); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::No); + int ret = msgBox.exec(); + + switch (ret) { + case QMessageBox::Yes: + // continue + break; + case QMessageBox::No: + // stop + return; + break; + + default: + // should never be reached + break; + } + } + + // proceed mitk::ConnectomicsSyntheticNetworkGenerator::Pointer generator = mitk::ConnectomicsSyntheticNetworkGenerator::New(); mitk::DataNode::Pointer networkNode = mitk::DataNode::New(); int parameterOne = this->m_Controls->parameterOneSpinBox->value(); double parameterTwo = this->m_Controls->parameterTwoDoubleSpinBox->value(); ////add network to datastorage networkNode->SetData( generator->CreateSyntheticNetwork( m_currentIndex, parameterOne, parameterTwo ) ); networkNode->SetName( mitk::ConnectomicsConstantsManager::CONNECTOMICS_PROPERTY_DEFAULT_CNF_NAME ); - this->GetDefaultDataStorage()->Add( networkNode ); + if( generator->WasGenerationSuccessfull() ) + { + this->GetDefaultDataStorage()->Add( networkNode ); + } + else + { + MITK_WARN << "Problem occured during synthetic network generation."; + } return; } void QmitkBrainNetworkAnalysisView::OnConvertToRGBAImagePushButtonClicked() { std::vector nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataNode* node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return QMessageBox::information( NULL, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_CONNECTOMICS_CREATION, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_SELECTION_WARNING); return; } // here we have a valid mitk::DataNode // a node itself is not very useful, we need its data item (the image) mitk::BaseData* data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image* image = dynamic_cast( data ); if (image) { std::stringstream message; std::string name; message << mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_PERFORMING_IMAGE_PROCESSING_FOR_IMAGE; if (node->GetName(name)) { // a property called "name" was found for this DataNode message << "'" << name << "'"; } message << "."; MITK_INFO << message.str(); // Convert to RGBA AccessByItk( image, TurnIntoRGBA ); this->GetDefaultDataStorage()->GetNamedNode( mitk::ConnectomicsConstantsManager::CONNECTOMICS_PROPERTY_DEFAULT_RGBA_NAME )->GetData()->SetGeometry( node->GetData()->GetGeometry() ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } } template < typename TPixel, unsigned int VImageDimension > void QmitkBrainNetworkAnalysisView::TurnIntoRGBA( itk::Image* inputImage) { typedef itk::RGBAPixel< unsigned char > RGBAPixelType; typedef itk::Image< TPixel, VImageDimension > TemplateImageType; typedef itk::Image< RGBAPixelType, VImageDimension > RGBAImageType; itk::ImageRegionIterator it_inputImage(inputImage, inputImage->GetLargestPossibleRegion()); TPixel minimumValue, maximumValue; it_inputImage.GoToBegin(); maximumValue = minimumValue = it_inputImage.Value(); for(it_inputImage.GoToBegin(); !it_inputImage.IsAtEnd(); ++it_inputImage) { if ( it_inputImage.Value() < minimumValue ) { minimumValue = it_inputImage.Value(); } else { if ( it_inputImage.Value() > maximumValue ) { maximumValue = it_inputImage.Value(); } } } int range = int ( maximumValue - minimumValue ); //needs to be castable to int int offset = int ( minimumValue ); if ( range < 0 ) //error { return; } std::vector< unsigned int > histogram; histogram.resize( range + 1, 0 ); for(it_inputImage.GoToBegin(); !it_inputImage.IsAtEnd(); ++it_inputImage) { histogram[ int ( it_inputImage.Value() ) - offset ] += 1; } int gapCounter = 0; //this variable will be used to count the empty labels //stores how much has to be subtracted from the image to remove gaps std::vector< TPixel > subtractionStorage; subtractionStorage.resize( range + 1, 0 ); for( int index = 0; index <= range; index++ ) { if( histogram[ index ] == 0 ) { gapCounter++; //if the label is empty, increase gapCounter } else { subtractionStorage[ index ] = TPixel ( gapCounter ); } } //remove gaps from label image for(it_inputImage.GoToBegin(); !it_inputImage.IsAtEnd(); ++it_inputImage) { it_inputImage.Value() = it_inputImage.Value() - subtractionStorage[ int ( it_inputImage.Value() ) ]; } // create colour vector std::vector< RGBAPixelType > lookupTable; { RGBAPixelType backgroundColour; for( int elementIndex = 0; elementIndex < 4; ++elementIndex ) { backgroundColour.SetElement( elementIndex, 0 ); } lookupTable.push_back( backgroundColour ); for(int colourNumber = 0; colourNumber < range ; ++colourNumber) { RGBAPixelType colour; for( int elementIndex = 0; elementIndex < 3; ++elementIndex ) { colour.SetElement( elementIndex, rand() % 256 ); } colour.SetAlpha( 255 ); lookupTable.push_back( colour ); } } // create RGBA image typename RGBAImageType::Pointer rgbaImage = RGBAImageType::New(); rgbaImage->SetRegions(inputImage->GetLargestPossibleRegion().GetSize()); rgbaImage->SetSpacing(inputImage->GetSpacing()); rgbaImage->SetOrigin(inputImage->GetOrigin()); rgbaImage->Allocate(); //fill with appropriate colours itk::ImageRegionIterator it_rgbaImage(rgbaImage, rgbaImage->GetLargestPossibleRegion()); for(it_inputImage.GoToBegin(), it_rgbaImage.GoToBegin(); !it_inputImage.IsAtEnd(); ++it_inputImage, ++it_rgbaImage) { it_rgbaImage.Value() = lookupTable[ int ( it_inputImage.Value() ) ]; } mitk::Image::Pointer mitkRGBAImage = mitk::ImportItkImage( rgbaImage ); mitk::DataNode::Pointer rgbaImageNode = mitk::DataNode::New(); rgbaImageNode->SetData(mitkRGBAImage); rgbaImageNode->SetProperty(mitk::ConnectomicsConstantsManager::CONNECTOMICS_PROPERTY_NAME, mitk::StringProperty::New(mitk::ConnectomicsConstantsManager::CONNECTOMICS_PROPERTY_DEFAULT_RGBA_NAME)); rgbaImageNode->SetBoolProperty( mitk::ConnectomicsConstantsManager::CONNECTOMICS_PROPERTY_VOLUMERENDERING, true); this->GetDefaultDataStorage()->Add( rgbaImageNode ); } void QmitkBrainNetworkAnalysisView::OnNetworkifyPushButtonClicked() { std::vector nodes = this->GetDataManagerSelection(); if ( nodes.empty() ) { QMessageBox::information( NULL, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_CONNECTOMICS_CREATION, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_SELECTION_WARNING); return; } if (! ( nodes.size() == 2 ) ) { QMessageBox::information( NULL, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_CONNECTOMICS_CREATION, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_SELECTION_WARNING); return; } mitk::DataNode* firstNode = nodes.front(); mitk::DataNode* secondNode = nodes.at(1); if (!firstNode) { // Nothing selected. Inform the user and return QMessageBox::information( NULL, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_CONNECTOMICS_CREATION, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_SELECTION_WARNING); return; } // here we have a valid mitk::DataNode // a node itself is not very useful, we need its data item (the image) mitk::BaseData* firstData = firstNode->GetData(); mitk::BaseData* secondData = secondNode->GetData(); if (firstData && secondData) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image* image = dynamic_cast( firstData ); mitk::FiberBundleX* fiberBundle = dynamic_cast( secondData ); // check whether order was switched if (! (image && fiberBundle) ) { image = dynamic_cast( secondData ); fiberBundle = dynamic_cast( firstData ); } if (image && fiberBundle) { m_ConnectomicsNetworkCreator->SetSegmentation( image ); m_ConnectomicsNetworkCreator->SetFiberBundle( fiberBundle ); m_ConnectomicsNetworkCreator->CreateNetworkFromFibersAndSegmentation(); mitk::DataNode::Pointer networkNode = mitk::DataNode::New(); ////add network to datastorage networkNode->SetData( m_ConnectomicsNetworkCreator->GetNetwork() ); networkNode->SetName( mitk::ConnectomicsConstantsManager::CONNECTOMICS_PROPERTY_DEFAULT_CNF_NAME ); this->GetDefaultDataStorage()->Add( networkNode ); } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkBrainNetworkAnalysisView::OnModularizePushButtonClicked() { std::vector nodes = this->GetDataManagerSelection(); if ( nodes.empty() ) { QMessageBox::information( NULL, "Modularization calculation", "Please select exactly one network."); return; } for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; if( node.IsNotNull() && dynamic_cast(node->GetData()) ) { return; } { mitk::ConnectomicsNetwork* network = dynamic_cast( node->GetData() ); if( node.IsNotNull() && network ) { typedef mitk::ConnectomicsSimulatedAnnealingPermutationModularity::ToModuleMapType MappingType; int depthOfModuleRecursive( 2 ); double startTemperature( 2.0 ); double stepSize( 4.0 ); mitk::ConnectomicsNetwork::Pointer connectomicsNetwork( network ); mitk::ConnectomicsSimulatedAnnealingManager::Pointer manager = mitk::ConnectomicsSimulatedAnnealingManager::New(); mitk::ConnectomicsSimulatedAnnealingPermutationModularity::Pointer permutation = mitk::ConnectomicsSimulatedAnnealingPermutationModularity::New(); mitk::ConnectomicsSimulatedAnnealingCostFunctionModularity::Pointer costFunction = mitk::ConnectomicsSimulatedAnnealingCostFunctionModularity::New(); permutation->SetCostFunction( costFunction.GetPointer() ); permutation->SetNetwork( connectomicsNetwork ); permutation->SetDepth( depthOfModuleRecursive ); permutation->SetStepSize( stepSize ); manager->SetPermutation( permutation.GetPointer() ); manager->RunSimulatedAnnealing( startTemperature, stepSize ); MappingType mapping = permutation->GetMapping(); MappingType::iterator iter = mapping.begin(); MappingType::iterator end = mapping.end(); int loop( 0 ); while( iter != end ) { MBI_DEBUG << "Vertex " << iter->first << " belongs to module " << iter->second ; MBI_INFO << "Vertex " << iter->first << " belongs to module " << iter->second ; iter++; } MBI_DEBUG << "Overall number of modules is " << permutation->getNumberOfModules( &mapping ) ; MBI_DEBUG << "Cost is " << costFunction->Evaluate( network, &mapping ) ; MBI_INFO << "Overall number of modules is " << permutation->getNumberOfModules( &mapping ) ; MBI_INFO << "Cost is " << costFunction->Evaluate( network, &mapping ) ; return; } } } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisViewControls.ui index d92a3700de..497e043298 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisViewControls.ui @@ -1,318 +1,363 @@ QmitkBrainNetworkAnalysisViewControls 0 0 227 - 941 + 1012 0 0 QmitkTemplate - - - QLabel { color: rgb(255, 0, 0) } - - - Please select data! + + + Data + + + + + QLabel { color: rgb(255, 0, 0) } + + + Please select data! + + + + + + + + + Image 1: + + + + + + + - + + + + + + + + + + + Image 2: + + + + + + + - + + + + + + Convert the selected image to RGBA format Convert to RGBA Create a network from a parcellation and a fiber image Networkify Create Synthetic Networks Divide in Modules Qt::Vertical 20 40 true Synthetic Network Options Parameter 1 false 9999 Parameter 2 false 3 999.899999999999977 Network Statistics # of vertices: # of edges: - - # of self loops: - average degree - connection density - efficiency - global clustering - Histograms 0 0 50 150 50 150 50 150 Qt::Vertical 20 40 QmitkNetworkHistogramCanvas QWidget
internal/Connectomics/QmitkNetworkHistogramCanvas.h
1
diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionDicomImportView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionDicomImportView.cpp index fdcf725c3f..52114cd27b 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionDicomImportView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionDicomImportView.cpp @@ -1,755 +1,737 @@ /*========================================================================= Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "QmitkDiffusionDicomImportView.h" // qt includes #include // itk includes #include "itkTimeProbesCollectorBase.h" #include "itkGDCMSeriesFileNames.h" #include "itksys/SystemTools.hxx" // mitk includes #include "mitkProgressBar.h" #include "mitkStatusBar.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkMemoryUtilities.h" // diffusion module includes #include "mitkDicomDiffusionImageHeaderReader.h" #include "mitkGroupDiffusionHeadersFilter.h" #include "mitkDicomDiffusionImageReader.h" #include "mitkDiffusionImage.h" #include "mitkNrrdDiffusionImageWriter.h" #include "gdcmDirectory.h" #include "gdcmScanner.h" #include "gdcmSorter.h" #include "gdcmIPPSorter.h" #include "gdcmAttribute.h" #include "gdcmVersion.h" #include const std::string QmitkDiffusionDicomImport::VIEW_ID = "org.mitk.views.diffusiondicomimport"; QmitkDiffusionDicomImport::QmitkDiffusionDicomImport(QObject* /*parent*/, const char* /*name*/) : QmitkFunctionality(), m_Controls(NULL), m_MultiWidget(NULL), m_OutputFolderName(""), m_OutputFolderNameSet(false) { } QmitkDiffusionDicomImport::QmitkDiffusionDicomImport(const QmitkDiffusionDicomImport& other) { Q_UNUSED(other) throw std::runtime_error("Copy constructor not implemented"); } QmitkDiffusionDicomImport::~QmitkDiffusionDicomImport() {} void QmitkDiffusionDicomImport::CreateQtPartControl(QWidget *parent) { m_Parent = parent; if (m_Controls == NULL) { m_Controls = new Ui::QmitkDiffusionDicomImportControls; m_Controls->setupUi(parent); this->CreateConnections(); m_Controls->m_DicomLoadRecursiveCheckbox->setChecked(true); m_Controls->m_DicomLoadAverageDuplicatesCheckbox->setChecked(false); m_Controls->m_DicomLoadRecursiveCheckbox->setVisible(false); AverageClicked(); - AdvancedCheckboxClicked(); } } void QmitkDiffusionDicomImport::CreateConnections() { if ( m_Controls ) { connect( m_Controls->m_AddFoldersButton, SIGNAL(clicked()), this, SLOT(DicomLoadAddFolderNames()) ); connect( m_Controls->m_DeleteFoldersButton, SIGNAL(clicked()), this, SLOT(DicomLoadDeleteFolderNames()) ); connect( m_Controls->m_DicomLoadStartLoadButton, SIGNAL(clicked()), this, SLOT(DicomLoadStartLoad()) ); connect( m_Controls->m_DicomLoadAverageDuplicatesCheckbox, SIGNAL(clicked()), this, SLOT(AverageClicked()) ); connect( m_Controls->m_OutputSetButton, SIGNAL(clicked()), this, SLOT(OutputSet()) ); connect( m_Controls->m_OutputClearButton, SIGNAL(clicked()), this, SLOT(OutputClear()) ); - connect( m_Controls->m_Advanced, SIGNAL(clicked()), this, SLOT(AdvancedCheckboxClicked()) ); connect( m_Controls->m_Remove, SIGNAL(clicked()), this, SLOT(Remove()) ); } } void QmitkDiffusionDicomImport::Remove() { int i = m_Controls->listWidget->currentRow(); m_Controls->listWidget->takeItem(i); } -void QmitkDiffusionDicomImport::AdvancedCheckboxClicked() -{ - bool check = m_Controls-> - m_Advanced->isChecked(); - - m_Controls->m_AdvancedFrame->setVisible(check); -} - void QmitkDiffusionDicomImport::OutputSet() { // SELECT FOLDER DIALOG QFileDialog* w = new QFileDialog( m_Parent, QString("Select folders containing DWI data") ); w->setFileMode( QFileDialog::Directory ); // RETRIEVE SELECTION if ( w->exec() != QDialog::Accepted ) return; m_OutputFolderName = w->selectedFiles()[0]; m_OutputFolderNameSet = true; m_Controls->m_OutputLabel->setText(m_OutputFolderName); } void QmitkDiffusionDicomImport::OutputClear() { m_OutputFolderName = ""; m_OutputFolderNameSet = false; m_Controls->m_OutputLabel->setText("... optional out-folder ..."); } void QmitkDiffusionDicomImport::AverageClicked() { m_Controls->m_Blur->setEnabled(m_Controls->m_DicomLoadAverageDuplicatesCheckbox->isChecked()); } void QmitkDiffusionDicomImport::Activated() { QmitkFunctionality::Activated(); } void QmitkDiffusionDicomImport::DicomLoadDeleteFolderNames() { m_Controls->listWidget->clear(); } void QmitkDiffusionDicomImport::DicomLoadAddFolderNames() { // SELECT FOLDER DIALOG QFileDialog* w = new QFileDialog( m_Parent, QString("Select folders containing DWI data") ); w->setFileMode( QFileDialog::Directory ); // RETRIEVE SELECTION if ( w->exec() != QDialog::Accepted ) return; m_Controls->listWidget->addItems(w->selectedFiles()); } bool SortBySeriesUID(gdcm::DataSet const & ds1, gdcm::DataSet const & ds2 ) { gdcm::Attribute<0x0020,0x000e> at1; at1.Set( ds1 ); gdcm::Attribute<0x0020,0x000e> at2; at2.Set( ds2 ); return at1 < at2; } bool SortByAcquisitionNumber(gdcm::DataSet const & ds1, gdcm::DataSet const & ds2 ) { gdcm::Attribute<0x0020,0x0012> at1; at1.Set( ds1 ); gdcm::Attribute<0x0020,0x0012> at2; at2.Set( ds2 ); return at1 < at2; } bool SortBySeqName(gdcm::DataSet const & ds1, gdcm::DataSet const & ds2 ) { gdcm::Attribute<0x0018, 0x0024> at1; at1.Set( ds1 ); gdcm::Attribute<0x0018, 0x0024> at2; at2.Set( ds2 ); std::string str1 = at1.GetValue().Trim(); std::string str2 = at2.GetValue().Trim(); return std::lexicographical_compare(str1.begin(), str1.end(), str2.begin(), str2.end() ); } void QmitkDiffusionDicomImport::Status(QString status) { mitk::StatusBar::GetInstance()->DisplayText(status.toAscii()); MITK_INFO << status.toStdString().c_str(); } void QmitkDiffusionDicomImport::Status(std::string status) { mitk::StatusBar::GetInstance()->DisplayText(status.c_str()); MITK_INFO << status.c_str(); } void QmitkDiffusionDicomImport::Status(const char* status) { mitk::StatusBar::GetInstance()->DisplayText(status); MITK_INFO << status; } void QmitkDiffusionDicomImport::Error(QString status) { mitk::StatusBar::GetInstance()->DisplayErrorText(status.toAscii()); MITK_ERROR << status.toStdString().c_str(); } void QmitkDiffusionDicomImport::Error(std::string status) { mitk::StatusBar::GetInstance()->DisplayErrorText(status.c_str()); MITK_ERROR << status.c_str(); } void QmitkDiffusionDicomImport::Error(const char* status) { mitk::StatusBar::GetInstance()->DisplayErrorText(status); MITK_ERROR << status; } void QmitkDiffusionDicomImport::PrintMemoryUsage() { size_t processSize = mitk::MemoryUtilities::GetProcessMemoryUsage(); size_t totalSize = mitk::MemoryUtilities::GetTotalSizeOfPhysicalRam(); float percentage = ( (float) processSize / (float) totalSize ) * 100.0; MITK_INFO << "Current memory usage: " << GetMemoryDescription( processSize, percentage ); } std::string QmitkDiffusionDicomImport::FormatMemorySize( size_t size ) { double val = size; std::string descriptor("B"); if ( val >= 1000.0 ) { val /= 1024.0; descriptor = "KB"; } if ( val >= 1000.0 ) { val /= 1024.0; descriptor = "MB"; } if ( val >= 1000.0 ) { val /= 1024.0; descriptor = "GB"; } std::ostringstream str; str << std::fixed << std::setprecision(2) << val << " " << descriptor; return str.str(); } std::string QmitkDiffusionDicomImport::FormatPercentage( double val ) { std::ostringstream str; str << std::fixed << std::setprecision(2) << val << " " << "%"; return str.str(); } std::string QmitkDiffusionDicomImport::GetMemoryDescription( size_t processSize, float percentage ) { std::ostringstream str; str << FormatMemorySize(processSize) << " (" << FormatPercentage( percentage ) <<")" ; return str.str(); } void QmitkDiffusionDicomImport::DicomLoadStartLoad() { itk::TimeProbesCollectorBase clock; bool imageSuccessfullySaved = true; try { const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, NULL ); if ( locale.compare(currLocale)!=0 ) { try { MITK_INFO << " ** Changing locale from " << setlocale(LC_ALL, NULL) << " to '" << locale << "'"; setlocale(LC_ALL, locale.c_str()); } catch(...) { MITK_INFO << "Could not set locale " << locale; } } int nrFolders = m_Controls->listWidget->count(); if(!nrFolders) { Error(QString("No input folders were selected. ABORTING.")); return; } Status(QString("GDCM %1 used for DICOM parsing and sorting!").arg(gdcm::Version::GetVersion())); PrintMemoryUsage(); QString status; mitk::DataNode::Pointer node; mitk::ProgressBar::GetInstance()->AddStepsToDo(2*nrFolders); std::string folder = m_Controls->m_OutputLabel->text().toStdString(); if(berry::Platform::IsWindows()) { folder.append("\\import.log"); } else { folder.append("/import.log"); } ofstream logfile; if(m_OutputFolderNameSet) logfile.open(folder.c_str()); while(m_Controls->listWidget->count()) { // RETREIVE FOLDERNAME QListWidgetItem * item = m_Controls->listWidget->takeItem(0); QString folderName = item->text(); if(m_OutputFolderNameSet) logfile << "Reading " << folderName.toStdString() << '\n'; // PARSING DIRECTORY PrintMemoryUsage(); clock.Start(folderName.toAscii()); std::vector seriesUIDs(0); std::vector > seriesFilenames(0); Status("== Initial Directory Scan =="); if(m_OutputFolderNameSet) logfile << "== Initial Directory Scan ==\n"; gdcm::Directory d; d.Load( folderName.toStdString().c_str(), true ); // recursive ! const gdcm::Directory::FilenamesType &l1 = d.GetFilenames(); const unsigned int ntotalfiles = l1.size(); Status(QString(" ... found %1 different files").arg(ntotalfiles)); if(m_OutputFolderNameSet)logfile << "...found " << ntotalfiles << " different files\n"; Status("Scanning Headers"); if(m_OutputFolderNameSet) logfile << "Scanning Headers\n"; gdcm::Scanner s; const gdcm::Tag t1(0x0020,0x000d); // Study Instance UID const gdcm::Tag t2(0x0020,0x000e); // Series Instance UID const gdcm::Tag t5(0x0028, 0x0010); // number rows const gdcm::Tag t6(0x0028, 0x0011); // number cols s.AddTag( t1 ); s.AddTag( t2 ); s.AddTag( t5 ); s.AddTag( t6 ); bool b = s.Scan( d.GetFilenames() ); if( !b ) { Error("Scanner failed"); if(m_OutputFolderNameSet )logfile << "ERROR: scanner failed\n"; continue; } // Only get the DICOM files: gdcm::Directory::FilenamesType l2 = s.GetKeys(); const int nfiles = l2.size(); if(nfiles < 1) { Error("No DICOM files found"); if(m_OutputFolderNameSet)logfile << "ERROR: No DICOM files found\n"; continue; } Status(QString(" ... successfully scanned %1 headers.").arg(nfiles)); if(m_OutputFolderNameSet) logfile << "...succesfully scanned " << nfiles << " headers\n"; Status("Sorting"); if(m_OutputFolderNameSet) logfile << "Sorting\n"; const gdcm::Scanner::ValuesType &values1 = s.GetValues(t1); int nvalues; if(m_Controls->m_DuplicateID->isChecked()) { nvalues = 1; } else { nvalues = values1.size(); } if(nvalues>1) { Error("Multiple sSeries tudies found. Please limit to 1 study per folder"); if(m_OutputFolderNameSet) logfile << "Multiple series found. Limit to one. If you are convinced this is an error use the merge duplicate study IDs option \n"; continue; } const gdcm::Scanner::ValuesType &values5 = s.GetValues(t5); const gdcm::Scanner::ValuesType &values6 = s.GetValues(t6); if(values5.size()>1 || values6.size()>1) { Error("Folder contains images of unequal dimensions that cannot be combined in one 3d volume. ABORTING."); if(m_OutputFolderNameSet) logfile << "Folder contains images of unequal dimensions that cannot be combined in one 3d volume. ABORTING\n."; continue; } const gdcm::Scanner::ValuesType &values2 = s.GetValues(t2); int nSeries; if(m_Controls->m_DuplicateID->isChecked()) { nSeries = 1; } else { nSeries = values2.size(); } gdcm::Directory::FilenamesType files; if(nSeries > 1) { gdcm::Sorter sorter; sorter.SetSortFunction( SortBySeriesUID ); sorter.StableSort( l2 ); files = sorter.GetFilenames(); } else { files = l2; } unsigned int nTotalAcquis = 0; if(nfiles % nSeries != 0) { Error("Number of files in series not equal, ABORTING"); if(m_OutputFolderNameSet) logfile << "Number of files in series not equal, Some volumes are probably incomplete. ABORTING \n"; continue; } int filesPerSeries = nfiles / nSeries; gdcm::Scanner::ValuesType::const_iterator it2 = values2.begin(); for(int i=0; i & list = ippsorter.GetFilenames(); seriesFilenames.push_back(list); seriesUIDs.push_back(identifier.c_str()); } ++it2; } if(nfiles % nTotalAcquis != 0) { Error("Number of files per acquisition differs between series, ABORTING"); if(m_OutputFolderNameSet) logfile << "Number of files per acquisition differs between series, ABORTING \n"; continue; } int slices = nfiles/nTotalAcquis; Status(QString("Series is composed of %1 different 3D volumes with %2 slices.").arg(nTotalAcquis).arg(slices)); if(m_OutputFolderNameSet) logfile << "Series is composed of " << nTotalAcquis << " different 3D volumes with " << slices << " slices\n"; // READING HEADER-INFOS PrintMemoryUsage(); Status(QString("Reading Headers %1").arg(folderName)); if(m_OutputFolderNameSet) logfile << "Reading Headers "<< folderName.toStdString() << "\n"; mitk::DicomDiffusionImageHeaderReader::Pointer headerReader; mitk::GroupDiffusionHeadersFilter::InputType inHeaders; unsigned int size2 = seriesUIDs.size(); for ( unsigned int i = 0 ; i < size2 ; ++i ) { Status(QString("Reading header image #%1/%2").arg(i+1).arg(size2)); headerReader = mitk::DicomDiffusionImageHeaderReader::New(); headerReader->SetSeriesDicomFilenames(seriesFilenames[i]); headerReader->Update(); inHeaders.push_back(headerReader->GetOutput()); //Status(std::endl; } mitk::ProgressBar::GetInstance()->Progress(); // // GROUP HEADERS // mitk::GroupDiffusionHeadersFilter::Pointer grouper // = mitk::GroupDiffusionHeadersFilter::New(); // mitk::GroupDiffusionHeadersFilter::OutputType outHeaders; // grouper->SetInput(inHeaders); // grouper->Update(); // outHeaders = grouper->GetOutput(); // READ VOLUMES PrintMemoryUsage(); if(m_OutputFolderNameSet) logfile << "Loading volumes\n"; Status(QString("Loading Volumes %1").arg(folderName)); typedef short PixelValueType; typedef mitk::DicomDiffusionImageReader< PixelValueType, 3 > VolumesReader; VolumesReader::Pointer vReader = VolumesReader::New(); VolumesReader::HeaderContainer hc = inHeaders; // hc.insert(hc.end(), outHeaders[1].begin(), outHeaders[1].end() ); // hc.insert(hc.end(), outHeaders[2].begin(), outHeaders[2].end() ); if(hc.size()>1) { vReader->SetHeaders(hc); vReader->Update(); VolumesReader::OutputImageType::Pointer vecImage; vecImage = vReader->GetOutput(); Status(QString("Volumes Loaded (%1)").arg(folderName)); // CONSTRUCT CONTAINER WITH DIRECTIONS typedef vnl_vector_fixed< double, 3 > GradientDirectionType; typedef itk::VectorContainer< unsigned int, GradientDirectionType > GradientDirectionContainerType; GradientDirectionContainerType::Pointer directions = GradientDirectionContainerType::New(); std::vector b_vals; double maxb = 0; for(unsigned int i=0; ibValue; if(maxb vect = hc[i]->DiffusionVector; vect.normalize(); vect *= sqrt(b_vals[i]/maxb); directions->push_back(vect); } // DWI TO DATATREE PrintMemoryUsage(); Status(QString("Initializing Diffusion Image")); if(m_OutputFolderNameSet) logfile << "Initializing Diffusion Image\n"; typedef mitk::DiffusionImage DiffVolumesType; DiffVolumesType::Pointer diffImage = DiffVolumesType::New(); diffImage->SetDirections(directions); diffImage->SetOriginalDirections(directions); - if(m_Controls->m_DicomLoadDKFZ->isChecked()) - { - diffImage->CorrectDKFZBrokenGradientScheme(m_Controls->m_Blur->value()); - } diffImage->SetVectorImage(vecImage); diffImage->SetB_Value(maxb); diffImage->InitializeFromVectorImage(); Status(QString("Diffusion Image initialized")); if(m_OutputFolderNameSet) logfile << "Diffusion Image initialized\n"; if(m_Controls->m_DicomLoadAverageDuplicatesCheckbox->isChecked()) { PrintMemoryUsage(); Status(QString("Averaging gradient directions")); logfile << "Averaging gradient directions\n"; diffImage->AverageRedundantGradients(m_Controls->m_Blur->value()); } - //if(m_Controls->m_DicomLoadDuplicateIfSingleSliceCheckbox->isChecked()) - // diffVolumes->DuplicateIfSingleSlice(); - QString descr = QString("%1_%2_%3") .arg(((inHeaders)[0])->seriesDescription.c_str()) .arg(((inHeaders)[0])->seriesNumber) .arg(((inHeaders)[0])->patientName.c_str()); descr = descr.trimmed(); descr = descr.replace(" ", "_"); if(!m_OutputFolderNameSet) { node=mitk::DataNode::New(); node->SetData( diffImage ); GetDefaultDataStorage()->Add(node); SetDwiNodeProperties(node, descr.toStdString().c_str()); Status(QString("Image %1 added to datastorage").arg(descr)); } else { typedef mitk::NrrdDiffusionImageWriter WriterType; WriterType::Pointer writer = WriterType::New(); QString fullpath = QString("%1/%2.dwi") .arg(m_OutputFolderName) .arg(descr); - //std::string pathstring = itksys::SystemTools::ConvertToOutputPath(fullpath.toStdString().c_str()); writer->SetFileName(fullpath.toStdString()); writer->SetInput(diffImage); try { writer->Update(); } catch (itk::ExceptionObject &ex) { imageSuccessfullySaved = false; Error(QString("%1\n%2\n%3\n%4\n%5\n%6").arg(ex.GetNameOfClass()).arg(ex.GetFile()).arg(ex.GetLine()).arg(ex.GetLocation()).arg(ex.what()).arg(ex.GetDescription())); logfile << QString("%1\n%2\n%3\n%4\n%5\n%6").arg(ex.GetNameOfClass()).arg(ex.GetFile()).arg(ex.GetLine()).arg(ex.GetLocation()).arg(ex.what()).arg(ex.GetDescription()).toStdString() << "\n"; node=mitk::DataNode::New(); node->SetData( diffImage ); GetDefaultDataStorage()->Add(node); SetDwiNodeProperties(node, descr.toStdString().c_str()); Status(QString("Image %1 added to datastorage").arg(descr)); logfile << "Image " << descr.toStdString() << " added to datastorage\n"; continue ; } Status(QString("Image %1 written to disc (%1)").arg(fullpath.toStdString().c_str())); logfile << "Image " << fullpath.toStdString() << "\n"; } } else { Status(QString("No diffusion information found (%1)").arg(folderName)); if(m_OutputFolderNameSet) logfile << "No diffusion information found "<< folderName.toStdString(); } Status(QString("Finished processing %1 with memory:").arg(folderName)); if(m_OutputFolderNameSet) logfile << "Finished processing " << folderName.toStdString() << "\n"; PrintMemoryUsage(); clock.Stop(folderName.toAscii()); mitk::ProgressBar::GetInstance()->Progress(); int lwidget = m_Controls->listWidget->count(); std::cout << lwidget <GetData(); if (basedata.IsNotNull()) { mitk::RenderingManager::GetInstance()->InitializeViews( basedata->GetTimeSlicedGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); try { MITK_INFO << " ** Changing locale back from " << setlocale(LC_ALL, NULL) << " to '" << currLocale << "'"; setlocale(LC_ALL, currLocale.c_str()); } catch(...) { MITK_INFO << "Could not reset locale " << currLocale; } } catch (itk::ExceptionObject &ex) { Error(QString("%1\n%2\n%3\n%4\n%5\n%6").arg(ex.GetNameOfClass()).arg(ex.GetFile()).arg(ex.GetLine()).arg(ex.GetLocation()).arg(ex.what()).arg(ex.GetDescription())); return ; } if (!imageSuccessfullySaved) QMessageBox::warning(NULL,"WARNING","One or more files could not be saved! The according files where moved to the datastorage."); Status(QString("Finished import with memory:")); PrintMemoryUsage(); } void QmitkDiffusionDicomImport::SetDwiNodeProperties(mitk::DataNode::Pointer node, std::string name) { node->SetProperty( "IsDWIRawVolume", mitk::BoolProperty::New( true ) ); // set foldername as string property mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New( name ); node->SetProperty( "name", nameProp ); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionDicomImportView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionDicomImportView.h index b7dcb07d03..87aceb3cab 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionDicomImportView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionDicomImportView.h @@ -1,119 +1,112 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: 2009-05-05 11:31:02 +0200 (Di, 05 Mai 2009) $ Version: $Revision: 10185 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef QmitkDiffusionDicomImportView_H__INCLUDED #define QmitkDiffusionDicomImportView_H__INCLUDED #include "QmitkFunctionality.h" #include "ui_QmitkDiffusionDicomImportViewControls.h" /*! -\brief QmitkDiffusionDicomImport +\brief QmitkDiffusionDicomImport \sa QmitkFunctionality \ingroup Functionalities */ class QmitkDiffusionDicomImport : public QmitkFunctionality -{ +{ Q_OBJECT -public: +public: static const std::string VIEW_ID; - /*! + /*! \ Convenient typedefs */ typedef mitk::DataStorage::SetOfObjects ConstVector; typedef ConstVector::ConstPointer ConstVectorPointer; typedef ConstVector::ConstIterator ConstVectorIterator; - /*! - \brief default constructor - */ + /*! + \brief default constructor + */ QmitkDiffusionDicomImport(QObject *parent=0, const char *name=0); QmitkDiffusionDicomImport(const QmitkDiffusionDicomImport& other); - /*! - \brief default destructor - */ + /*! + \brief default destructor + */ virtual ~QmitkDiffusionDicomImport(); - /*! - \brief method for creating the widget containing the application controls, like sliders, buttons etc. - */ + /*! + \brief method for creating the widget containing the application controls, like sliders, buttons etc. + */ virtual void CreateQtPartControl(QWidget *parent); - /*! - \brief method for creating the connections of main and control widget - */ + /*! + \brief method for creating the connections of main and control widget + */ virtual void CreateConnections(); virtual void Activated(); void SetDwiNodeProperties(mitk::DataNode::Pointer node, std::string name); protected slots: void DicomLoadAddFolderNames(); - void DicomLoadDeleteFolderNames(); - void DicomLoadStartLoad() ; - void AverageClicked(); - void OutputSet(); void OutputClear(); - - void AdvancedCheckboxClicked(); - void Remove(); -protected: +protected: void Status(QString status); void Status(std::string status); void Status(const char* status); void Error(QString status); void Error(std::string status); void Error(const char* status); void PrintMemoryUsage(); std::string FormatMemorySize( size_t size ); std::string FormatPercentage( double val ); std::string GetMemoryDescription( size_t processSize, float percentage ); - /*! - * controls containing sliders for scrolling through the slices - */ + /*! + * controls containing sliders for scrolling through the slices + */ Ui::QmitkDiffusionDicomImportControls *m_Controls; QmitkStdMultiWidget* m_MultiWidget; QWidget *m_Parent; QString m_OutputFolderName; bool m_OutputFolderNameSet; }; #endif // !defined(QmitkDiffusionDicomImport_H__INCLUDED) diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionDicomImportViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionDicomImportViewControls.ui index 3e096d8494..02cbb86b1e 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionDicomImportViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionDicomImportViewControls.ui @@ -1,330 +1,313 @@ QmitkDiffusionDicomImportControls 0 0 - 313 + 338 498 0 0 true QmitkDiffusionDicomImport 16777215 70 QFrame::NoFrame QFrame::Plain 0 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> <table border="0" style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> <tr> <td style="border: none;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">Each input folder must only contain DICOM-images that can be combined into one vector-valued 3D output volume. Different patients must be loaded from different input-folders. The folders must not contain other acquisitions (e.g. T1,T2,localizer).</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">In case many imports are performed at once, it is recommended to set the the optional output folder argument. This prevents the images from being kept in memory.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;"></p></td></tr></table></body></html> 0 0 QFrame::NoFrame QFrame::Raised 0 0 Add Input Folders Remove Clear 0 0 0 70 QFrame::Box QFrame::Plain 1 0 Qt::ScrollBarAsNeeded Qt::ScrollBarAlwaysOn QListView::Adjust true QFrame::StyledPanel QFrame::Raised Recursive + + Multiple acquistions of one gradient direction can be averaged. Due to rounding errors, similar gradients often differ in the last decimal positions. The Merge radius allows to average them anyway by taking into account all directions within a certain radius. + QFrame::NoFrame QFrame::Raised 0 - Avg. dupl. grad., blur= + Merge duplicate gradients: false + + + - 6 + 4 2.000000000000000 0.000100000000000 0.001000000000000 - + + + Select this option if you are sure that your DICOM dataset contains multiple Study Instance UID or Series Instance UID. It will ignore that fact and attempt to import all images as one. + - Advanced + Merge duplicate study IDs - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - Correct DKFZ Gradient Scheme - - - - - - - Select this option if you are sure that your DICOM dataset contains multiple Study Instance UID or Series Instance UID. It will ignore that fact and attempt to import all images as one. - - - merge duplicate study IDs - - - - - - QFrame::NoFrame QFrame::Raised 0 40 25 30 16777215 + + Files are automaticall saved to disc. If the files can not be written, they are added to the data manager. + Set ... optional out-folder ... false true 50 25 30 16777215 Clear Import DICOM as *.dwi Qt::Vertical 20 0 diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionQuantificationView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionQuantificationView.cpp index 0030b57e25..198577bba2 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionQuantificationView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionQuantificationView.cpp @@ -1,665 +1,573 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: 2009-05-28 17:19:30 +0200 (Do, 28 Mai 2009) $ Version: $Revision: 17495 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "QmitkDiffusionQuantificationView.h" #include "mitkDiffusionImagingConfigure.h" #include "itkTimeProbe.h" #include "itkImage.h" #include "mitkNodePredicateDataType.h" #include "mitkDataNodeObject.h" #include "mitkQBallImage.h" +#include #include "mitkImageCast.h" #include "mitkStatusBar.h" #include "itkDiffusionQballGeneralizedFaImageFilter.h" #include "itkShiftScaleImageFilter.h" #include "itkTensorFractionalAnisotropyImageFilter.h" #include "itkTensorRelativeAnisotropyImageFilter.h" #include "itkTensorDerivedMeasurementsFilter.h" #include "QmitkDataStorageComboBox.h" #include "QmitkStdMultiWidget.h" #include #include "berryIWorkbenchWindow.h" #include "berryISelectionService.h" const std::string QmitkDiffusionQuantificationView::VIEW_ID = "org.mitk.views.diffusionquantification"; -using namespace berry; -struct DqSelListener : ISelectionListener -{ - - berryObjectMacro(DqSelListener); - - DqSelListener(QmitkDiffusionQuantificationView* view) - { - m_View = view; - } - - void DoSelectionChanged(ISelection::ConstPointer selection) - { - // save current selection in member variable - m_View->m_CurrentSelection = selection.Cast(); - - // do something with the selected items - if(m_View->m_CurrentSelection) - { - bool foundQBIVolume = false; - bool foundTensorVolume = false; - - // iterate selection - for (IStructuredSelection::iterator i = m_View->m_CurrentSelection->Begin(); - i != m_View->m_CurrentSelection->End(); ++i) - { - - // extract datatree node - if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) - { - mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - - // only look at interesting types - // process only on valid nodes - const mitk::BaseData* nodeData = node->GetData(); - if(nodeData) - { - if(QString("QBallImage").compare(nodeData->GetNameOfClass())==0) - { - foundQBIVolume = true; - } - - if(QString("TensorImage").compare(nodeData->GetNameOfClass())==0) - { - foundTensorVolume = true; - } - } - } - } - - m_View->m_Controls->m_GFAButton->setEnabled(foundQBIVolume); - m_View->m_Controls->m_CurvatureButton->setEnabled(foundQBIVolume); - - m_View->m_Controls->m_FAButton->setEnabled(foundTensorVolume); - m_View->m_Controls->m_RAButton->setEnabled(foundTensorVolume); - m_View->m_Controls->m_ADButton->setEnabled(foundTensorVolume); - m_View->m_Controls->m_RDButton->setEnabled(foundTensorVolume); - m_View->m_Controls->m_MDButton->setEnabled(foundTensorVolume); - m_View->m_Controls->m_ClusteringAnisotropy->setEnabled(foundTensorVolume); - } - } - - void SelectionChanged(IWorkbenchPart::Pointer part, ISelection::ConstPointer selection) - { - // check, if selection comes from datamanager - if (part) - { - QString partname(part->GetPartName().c_str()); - if(partname.compare("Datamanager")==0) - { - - // apply selection - DoSelectionChanged(selection); - - } - } - } - - QmitkDiffusionQuantificationView* m_View; -}; QmitkDiffusionQuantificationView::QmitkDiffusionQuantificationView() -: QmitkFunctionality(), -m_Controls(NULL), -m_MultiWidget(NULL) + : QmitkFunctionality(), + m_Controls(NULL), + m_MultiWidget(NULL) { + m_QBallImages = mitk::DataStorage::SetOfObjects::New(); + m_TensorImages = mitk::DataStorage::SetOfObjects::New(); } QmitkDiffusionQuantificationView::QmitkDiffusionQuantificationView(const QmitkDiffusionQuantificationView& other) { Q_UNUSED(other) throw std::runtime_error("Copy constructor not implemented"); } QmitkDiffusionQuantificationView::~QmitkDiffusionQuantificationView() { - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->RemovePostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); + } void QmitkDiffusionQuantificationView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkDiffusionQuantificationViewControls; m_Controls->setupUi(parent); this->CreateConnections(); GFACheckboxClicked(); #ifndef DIFFUSION_IMAGING_EXTENDED m_Controls->m_StandardGFACheckbox->setVisible(false); m_Controls->frame_3->setVisible(false); m_Controls->m_CurvatureButton->setVisible(false); #endif } - - m_SelListener = berry::ISelectionListener::Pointer(new DqSelListener(this)); - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddPostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); - berry::ISelection::ConstPointer sel( - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); - m_CurrentSelection = sel.Cast(); - m_SelListener.Cast()->DoSelectionChanged(sel); } void QmitkDiffusionQuantificationView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkDiffusionQuantificationView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkDiffusionQuantificationView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_StandardGFACheckbox), SIGNAL(clicked()), this, SLOT(GFACheckboxClicked()) ); connect( (QObject*)(m_Controls->m_GFAButton), SIGNAL(clicked()), this, SLOT(GFA()) ); connect( (QObject*)(m_Controls->m_CurvatureButton), SIGNAL(clicked()), this, SLOT(Curvature()) ); connect( (QObject*)(m_Controls->m_FAButton), SIGNAL(clicked()), this, SLOT(FA()) ); connect( (QObject*)(m_Controls->m_RAButton), SIGNAL(clicked()), this, SLOT(RA()) ); connect( (QObject*)(m_Controls->m_ADButton), SIGNAL(clicked()), this, SLOT(AD()) ); connect( (QObject*)(m_Controls->m_RDButton), SIGNAL(clicked()), this, SLOT(RD()) ); connect( (QObject*)(m_Controls->m_MDButton), SIGNAL(clicked()), this, SLOT(MD()) ); connect( (QObject*)(m_Controls->m_ClusteringAnisotropy), SIGNAL(clicked()), this, SLOT(ClusterAnisotropy()) ); } } +void QmitkDiffusionQuantificationView::OnSelectionChanged( std::vector nodes ) +{ + if (!m_Visible) + return; + + m_QBallImages = mitk::DataStorage::SetOfObjects::New(); + m_TensorImages = mitk::DataStorage::SetOfObjects::New(); + bool foundQBIVolume = false; + bool foundTensorVolume = false; + mitk::DataNode::Pointer selNode = NULL; + + int c=0; + for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) + { + mitk::DataNode::Pointer node = *it; + + if( node.IsNotNull() && dynamic_cast(node->GetData()) ) + { + foundQBIVolume = true; + m_QBallImages->push_back(node); + selNode = node; + c++; + } + else if( node.IsNotNull() && dynamic_cast(node->GetData()) ) + { + foundTensorVolume = true; + m_TensorImages->push_back(node); + selNode = node; + c++; + } + } + + m_Controls->m_GFAButton->setEnabled(foundQBIVolume); + m_Controls->m_CurvatureButton->setEnabled(foundQBIVolume); + + if (c>0) + m_Controls->m_InputImageLabel->setText(selNode->GetName().c_str()); + else + m_Controls->m_InputImageLabel->setText("-"); + + m_Controls->m_FAButton->setEnabled(foundTensorVolume); + m_Controls->m_RAButton->setEnabled(foundTensorVolume); + m_Controls->m_ADButton->setEnabled(foundTensorVolume); + m_Controls->m_RDButton->setEnabled(foundTensorVolume); + m_Controls->m_MDButton->setEnabled(foundTensorVolume); + m_Controls->m_ClusteringAnisotropy->setEnabled(foundTensorVolume); +} + void QmitkDiffusionQuantificationView::Activated() { - berry::ISelection::ConstPointer sel( - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); - m_CurrentSelection = sel.Cast(); - m_SelListener.Cast()->DoSelectionChanged(sel); QmitkFunctionality::Activated(); } void QmitkDiffusionQuantificationView::Deactivated() { QmitkFunctionality::Deactivated(); } void QmitkDiffusionQuantificationView::GFACheckboxClicked() { - m_Controls->frame_2->setVisible(m_Controls-> - m_StandardGFACheckbox->isChecked()); + m_Controls->frame_2->setVisible(m_Controls->m_StandardGFACheckbox->isChecked()); } void QmitkDiffusionQuantificationView::GFA() { if(m_Controls->m_StandardGFACheckbox->isChecked()) { QBIQuantify(13); } else { QBIQuantify(0); } } void QmitkDiffusionQuantificationView::Curvature() { QBIQuantify(12); } void QmitkDiffusionQuantificationView::FA() { TensorQuantify(0); } void QmitkDiffusionQuantificationView::RA() { TensorQuantify(1); } void QmitkDiffusionQuantificationView::AD() { TensorQuantify(2); } void QmitkDiffusionQuantificationView::RD() { TensorQuantify(3); } void QmitkDiffusionQuantificationView::ClusterAnisotropy() { TensorQuantify(4); } void QmitkDiffusionQuantificationView::MD() { TensorQuantify(5); } void QmitkDiffusionQuantificationView::QBIQuantify(int method) { - if (m_CurrentSelection) - { - mitk::DataStorage::SetOfObjects::Pointer set = - mitk::DataStorage::SetOfObjects::New(); - - int at = 0; - for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); - i != m_CurrentSelection->End(); - ++i) - { - - if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) - { - mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - if(QString("QBallImage").compare(node->GetData()->GetNameOfClass())==0) - { - set->InsertElement(at++, node); - } - } - } - - QBIQuantification(set, method); - - } + QBIQuantification(m_QBallImages, method); } void QmitkDiffusionQuantificationView::TensorQuantify(int method) { - if (m_CurrentSelection) - { - mitk::DataStorage::SetOfObjects::Pointer set = - mitk::DataStorage::SetOfObjects::New(); - - int at = 0; - for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); - i != m_CurrentSelection->End(); - ++i) - { - - if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) - { - mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - // process only on valid nodes - mitk::BaseData* nodeData = node->GetData(); - if(nodeData) - { - if(QString("TensorImage").compare(nodeData->GetNameOfClass())==0) - { - set->InsertElement(at++, node); - } - } - } - } - - TensorQuantification(set, method); - - } + TensorQuantification(m_TensorImages, method); } void QmitkDiffusionQuantificationView::QBIQuantification( mitk::DataStorage::SetOfObjects::Pointer inImages, int method) { itk::TimeProbe clock; QString status; int nrFiles = inImages->size(); if (!nrFiles) return; mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { typedef float TOdfPixelType; const int odfsize = QBALL_ODFSIZE; typedef itk::Vector OdfVectorType; typedef itk::Image OdfVectorImgType; mitk::Image* vol = - static_cast((*itemiter)->GetData()); + static_cast((*itemiter)->GetData()); OdfVectorImgType::Pointer itkvol = OdfVectorImgType::New(); mitk::CastToItkImage(vol, itkvol); std::string nodename; (*itemiter)->GetStringProperty("name", nodename); ++itemiter; float p1 = m_Controls->m_ParamKEdit->text().toFloat(); float p2 = m_Controls->m_ParamPEdit->text().toFloat(); // COMPUTE RA clock.Start(); MBI_INFO << "Computing GFA "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( - "Computing GFA for %s", nodename.c_str()).toAscii()); + "Computing GFA for %s", nodename.c_str()).toAscii()); typedef OdfVectorType::ValueType RealValueType; typedef itk::Image< RealValueType, 3 > RAImageType; typedef itk::DiffusionQballGeneralizedFaImageFilter - GfaFilterType; + GfaFilterType; GfaFilterType::Pointer gfaFilter = GfaFilterType::New(); gfaFilter->SetInput(itkvol); gfaFilter->SetNumberOfThreads(8); double scale = 1; std::string newname; newname.append(nodename); switch(method) { case 0: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_STANDARD); newname.append("GFA"); break; } case 1: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_QUANTILES_HIGH_LOW); newname.append("01"); break; } case 2: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_QUANTILE_HIGH); newname.append("02"); break; } case 3: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_MAX_ODF_VALUE); newname.append("03"); break; } case 4: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_DECONVOLUTION_COEFFS); newname.append("04"); break; } case 5: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_MIN_MAX_NORMALIZED_STANDARD); newname.append("05"); break; } case 6: - { - gfaFilter->SetComputationMethod(GfaFilterType::GFA_NORMALIZED_ENTROPY); - newname.append("06"); - break; - } + { + gfaFilter->SetComputationMethod(GfaFilterType::GFA_NORMALIZED_ENTROPY); + newname.append("06"); + break; + } case 7: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_NEMATIC_ORDER_PARAMETER); newname.append("07"); break; } case 8: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_QUANTILES_LOW_HIGH); newname.append("08"); break; } case 9: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_QUANTILE_LOW); newname.append("09"); break; } case 10: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_MIN_ODF_VALUE); newname.append("10"); break; } case 11: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_STD_BY_MAX); newname.append("11"); break; } case 12: { p1 = m_Controls->MinAngle->text().toFloat(); p2 = m_Controls->MaxAngle->text().toFloat(); gfaFilter->SetComputationMethod(GfaFilterType::GFA_PRINCIPLE_CURVATURE); QString paramString; paramString = paramString.append("PC%1-%2").arg(p1).arg(p2); newname.append(paramString.toAscii()); gfaFilter->SetParam1(p1); gfaFilter->SetParam2(p2); break; } case 13: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_GENERALIZED_GFA); QString paramString; paramString = paramString.append("GFAK%1P%2").arg(p1).arg(p2); newname.append(paramString.toAscii()); gfaFilter->SetParam1(p1); gfaFilter->SetParam2(p2); break; } default: { newname.append("0"); gfaFilter->SetComputationMethod(GfaFilterType::GFA_STANDARD); } } gfaFilter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s."; typedef itk::Image ImgType; ImgType::Pointer img = ImgType::New(); img->SetSpacing( gfaFilter->GetOutput()->GetSpacing() ); // Set the image spacing img->SetOrigin( gfaFilter->GetOutput()->GetOrigin() ); // Set the image origin img->SetDirection( gfaFilter->GetOutput()->GetDirection() ); // Set the image direction img->SetLargestPossibleRegion( gfaFilter->GetOutput()->GetLargestPossibleRegion()); img->SetBufferedRegion( gfaFilter->GetOutput()->GetLargestPossibleRegion() ); img->Allocate(); itk::ImageRegionIterator ot (img, img->GetLargestPossibleRegion() ); ot = ot.Begin(); itk::ImageRegionConstIterator it - (gfaFilter->GetOutput(), gfaFilter->GetOutput()->GetLargestPossibleRegion() ); + (gfaFilter->GetOutput(), gfaFilter->GetOutput()->GetLargestPossibleRegion() ); it = it.Begin(); for (it = it.Begin(); !it.IsAtEnd(); ++it) { GfaFilterType::OutputImageType::PixelType val = it.Get(); ot.Set(val * m_Controls->m_ScaleImageValuesBox->value()); ++ot; } // GFA TO DATATREE mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( img.GetPointer() ); image->SetVolume( img->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); node->SetProperty( "name", mitk::StringProperty::New(newname) ); nodes.push_back(node); mitk::StatusBar::GetInstance()->DisplayText("Computation complete."); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) GetDefaultDataStorage()->Add(*nodeIt); m_MultiWidget->RequestUpdate(); } void QmitkDiffusionQuantificationView::TensorQuantification( mitk::DataStorage::SetOfObjects::Pointer inImages, int method) { itk::TimeProbe clock; QString status; int nrFiles = inImages->size(); if (!nrFiles) return; mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { typedef float TTensorPixelType; typedef itk::DiffusionTensor3D< TTensorPixelType > TensorPixelType; typedef itk::Image< TensorPixelType, 3 > TensorImageType; mitk::Image* vol = - static_cast((*itemiter)->GetData()); + static_cast((*itemiter)->GetData()); TensorImageType::Pointer itkvol = TensorImageType::New(); mitk::CastToItkImage(vol, itkvol); std::string nodename; (*itemiter)->GetStringProperty("name", nodename); ++itemiter; // COMPUTE FA clock.Start(); MBI_INFO << "Computing FA "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( - "Computing FA for %s", nodename.c_str()).toAscii()); + "Computing FA for %s", nodename.c_str()).toAscii()); typedef itk::Image< TTensorPixelType, 3 > FAImageType; typedef itk::ShiftScaleImageFilter - ShiftScaleFilterType; + ShiftScaleFilterType; ShiftScaleFilterType::Pointer multi = - ShiftScaleFilterType::New(); + ShiftScaleFilterType::New(); multi->SetShift(0.0); multi->SetScale(m_Controls->m_ScaleImageValuesBox->value());//itk::NumericTraits::max() typedef itk::TensorDerivedMeasurementsFilter MeasurementsType; if(method == 0) //FA { - /* typedef itk::TensorFractionalAnisotropyImageFilter< + /* typedef itk::TensorFractionalAnisotropyImageFilter< TensorImageType, FAImageType > FilterType; FilterType::Pointer anisotropyFilter = FilterType::New(); anisotropyFilter->SetInput( itkvol.GetPointer() ); anisotropyFilter->Update(); multi->SetInput(anisotropyFilter->GetOutput()); nodename = QString(nodename.c_str()).append("_FA").toStdString();*/ MeasurementsType::Pointer measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(itkvol.GetPointer() ); measurementsCalculator->SetMeasure(MeasurementsType::FA); measurementsCalculator->Update(); multi->SetInput(measurementsCalculator->GetOutput()); nodename = QString(nodename.c_str()).append("_FA").toStdString(); } else if(method == 1) //RA { /*typedef itk::TensorRelativeAnisotropyImageFilter< TensorImageType, FAImageType > FilterType; FilterType::Pointer anisotropyFilter = FilterType::New(); anisotropyFilter->SetInput( itkvol.GetPointer() ); anisotropyFilter->Update(); multi->SetInput(anisotropyFilter->GetOutput()); nodename = QString(nodename.c_str()).append("_RA").toStdString();*/ MeasurementsType::Pointer measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(itkvol.GetPointer() ); measurementsCalculator->SetMeasure(MeasurementsType::RA); measurementsCalculator->Update(); multi->SetInput(measurementsCalculator->GetOutput()); nodename = QString(nodename.c_str()).append("_RA").toStdString(); } else if(method == 2) // AD (Axial diffusivity) { MeasurementsType::Pointer measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(itkvol.GetPointer() ); measurementsCalculator->SetMeasure(MeasurementsType::AD); measurementsCalculator->Update(); multi->SetInput(measurementsCalculator->GetOutput()); nodename = QString(nodename.c_str()).append("_AD").toStdString(); } else if(method == 3) // RD (Radial diffusivity, (Lambda2+Lambda3)/2 { MeasurementsType::Pointer measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(itkvol.GetPointer() ); measurementsCalculator->SetMeasure(MeasurementsType::RD); measurementsCalculator->Update(); multi->SetInput(measurementsCalculator->GetOutput()); nodename = QString(nodename.c_str()).append("_RD").toStdString(); } else if(method == 4) // 1-(Lambda2+Lambda3)/(2*Lambda1) { MeasurementsType::Pointer measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(itkvol.GetPointer() ); measurementsCalculator->SetMeasure(MeasurementsType::CA); measurementsCalculator->Update(); multi->SetInput(measurementsCalculator->GetOutput()); nodename = QString(nodename.c_str()).append("_CA").toStdString(); } else if(method == 5) // MD (Mean Diffusivity, (Lambda1+Lambda2+Lambda3)/3 ) { MeasurementsType::Pointer measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(itkvol.GetPointer() ); measurementsCalculator->SetMeasure(MeasurementsType::MD); measurementsCalculator->Update(); multi->SetInput(measurementsCalculator->GetOutput()); nodename = QString(nodename.c_str()).append("_MD").toStdString(); } multi->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s."; // FA TO DATATREE mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( multi->GetOutput() ); image->SetVolume( multi->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); node->SetProperty( "name", mitk::StringProperty::New(nodename) ); nodes.push_back(node); mitk::StatusBar::GetInstance()->DisplayText("Computation complete."); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) GetDefaultDataStorage()->Add(*nodeIt); m_MultiWidget->RequestUpdate(); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionQuantificationView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionQuantificationView.h index cb98ea5a5c..3e8c319f7b 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionQuantificationView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionQuantificationView.h @@ -1,108 +1,104 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: 2009-05-28 17:19:30 +0200 (Do, 28 Mai 2009) $ -Version: $Revision: 17495 $ - +Version: $Revision: 17495 $ + Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _QMITKDIFFUSIONQUANTIFICATIONVIEW_H_INCLUDED #define _QMITKDIFFUSIONQUANTIFICATIONVIEW_H_INCLUDED #include #include -#include "berryISelectionListener.h" -#include "berryIStructuredSelection.h" - #include "ui_QmitkDiffusionQuantificationViewControls.h" - - - /*! * \ingroup org_mitk_gui_qt_diffusionquantification_internal * * \brief QmitkDiffusionQuantificationView * * Document your class here. * * \sa QmitkFunctionality */ class QmitkDiffusionQuantificationView : public QmitkFunctionality { friend struct DqSelListener; // this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) Q_OBJECT public: static const std::string VIEW_ID; QmitkDiffusionQuantificationView(); QmitkDiffusionQuantificationView(const QmitkDiffusionQuantificationView& other); virtual ~QmitkDiffusionQuantificationView(); virtual void CreateQtPartControl(QWidget *parent); /// \brief Creation of the connections of main and control widget virtual void CreateConnections(); /// \brief Called when the functionality is activated virtual void Activated(); virtual void Deactivated(); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); protected slots: void GFACheckboxClicked(); - + void GFA(); void Curvature(); void FA(); void RA(); void AD(); void RD(); void ClusterAnisotropy(); void MD(); void QBIQuantify(int method); - void QBIQuantification(mitk::DataStorage::SetOfObjects::Pointer inImages, + void QBIQuantification(mitk::DataStorage::SetOfObjects::Pointer inImages, int method) ; void TensorQuantify(int method); - void TensorQuantification(mitk::DataStorage::SetOfObjects::Pointer inImages, + void TensorQuantification(mitk::DataStorage::SetOfObjects::Pointer inImages, int method) ; protected: + /// \brief called by QmitkFunctionality when DataManager's selection has changed + virtual void OnSelectionChanged( std::vector nodes ); + Ui::QmitkDiffusionQuantificationViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; - - berry::ISelectionListener::Pointer m_SelListener; - berry::IStructuredSelection::ConstPointer m_CurrentSelection; + mitk::DataStorage::SetOfObjects::Pointer m_QBallImages; + mitk::DataStorage::SetOfObjects::Pointer m_TensorImages; static const float m_ScaleDAIValues; }; #endif // _QMITKDIFFUSIONQUANTIFICATIONVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionQuantificationViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionQuantificationViewControls.ui index fc834371ea..46d6099700 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionQuantificationViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDiffusionQuantificationViewControls.ui @@ -1,280 +1,300 @@ QmitkDiffusionQuantificationViewControls 0 0 343 - 553 + 543 0 0 QmitkTemplate - General Parameters + Data - + - Scale values by: + Tensor/Q-Ball Image: - - - All tensor or Q-Ball derived quantities are scaled by this value. + + + - + + + + + + + + + General Parameters + + + + + + Scale Image Values: + + + + + 100.000000000000000 1.000000000000000 Q-Ball Imaging QFrame::NoFrame QFrame::Raised 0 Generalized GFA QFrame::NoFrame QFrame::Raised 0 true k true true p true false GFA QFrame::NoFrame QFrame::Raised 0 Min. angle Max. angle false Curvature Tensor Imaging false FA (Fractional Anisotropy) false RA (Relative Anisotropy) false AD (Axial Diffusivity) false RD (Radial Diffusivity) false MD (Mean Diffusivity) false 1-(λ2+λ3)/(2*λ1) Qt::Vertical QSizePolicy::Expanding 20 220 QmitkDataStorageComboBox.h diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.cpp index 2dde0a992c..204f17b3ff 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.cpp @@ -1,1759 +1,1767 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2010-03-31 16:40:27 +0200 (Mi, 31 Mrz 2010) $ Version: $Revision: 21975 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkFiberProcessingView.h" #include // Qt #include // MITK #include #include #include #include #include #include #include #include #include #include #include // ITK #include #include #include #include #include #include #include #include const std::string QmitkFiberProcessingView::VIEW_ID = "org.mitk.views.fiberprocessing"; const std::string id_DataManager = "org.mitk.views.datamanager"; using namespace mitk; QmitkFiberProcessingView::QmitkFiberProcessingView() : QmitkFunctionality() , m_Controls( 0 ) , m_MultiWidget( NULL ) , m_EllipseCounter(0) , m_PolygonCounter(0) , m_UpsamplingFactor(5) { } // Destructor QmitkFiberProcessingView::~QmitkFiberProcessingView() { } void QmitkFiberProcessingView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkFiberProcessingViewControls; m_Controls->setupUi( parent ); m_Controls->doExtractFibersButton->setDisabled(true); m_Controls->PFCompoANDButton->setDisabled(true); m_Controls->PFCompoORButton->setDisabled(true); m_Controls->PFCompoNOTButton->setDisabled(true); m_Controls->m_PlanarFigureButtonsFrame->setEnabled(false); m_Controls->m_RectangleButton->setVisible(false); connect( m_Controls->doExtractFibersButton, SIGNAL(clicked()), this, SLOT(DoFiberExtraction()) ); connect( m_Controls->m_CircleButton, SIGNAL( clicked() ), this, SLOT( OnDrawCircle() ) ); connect( m_Controls->m_PolygonButton, SIGNAL( clicked() ), this, SLOT( OnDrawPolygon() ) ); connect(m_Controls->PFCompoANDButton, SIGNAL(clicked()), this, SLOT(GenerateAndComposite()) ); connect(m_Controls->PFCompoORButton, SIGNAL(clicked()), this, SLOT(GenerateOrComposite()) ); connect(m_Controls->PFCompoNOTButton, SIGNAL(clicked()), this, SLOT(GenerateNotComposite()) ); connect(m_Controls->m_JoinBundles, SIGNAL(clicked()), this, SLOT(JoinBundles()) ); connect(m_Controls->m_SubstractBundles, SIGNAL(clicked()), this, SLOT(SubstractBundles()) ); connect(m_Controls->m_GenerateRoiImage, SIGNAL(clicked()), this, SLOT(GenerateRoiImage()) ); connect(m_Controls->m_Extract3dButton, SIGNAL(clicked()), this, SLOT(Extract3d())); connect( m_Controls->m_ProcessFiberBundleButton, SIGNAL(clicked()), this, SLOT(ProcessSelectedBundles()) ); connect( m_Controls->m_ResampleFibersButton, SIGNAL(clicked()), this, SLOT(ResampleSelectedBundles()) ); connect(m_Controls->m_FaColorFibersButton, SIGNAL(clicked()), this, SLOT(DoFaColorCoding())); } } void QmitkFiberProcessingView::Extract3d() { std::vector nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::FiberBundleX::Pointer fib = mitk::FiberBundleX::New(); mitk::Surface::Pointer roi = mitk::Surface::New(); bool fibB = false; bool roiB = false; for (int i=0; i(nodes.at(i)->GetData())) { fib = dynamic_cast(nodes.at(i)->GetData()); fibB = true; } else if (dynamic_cast(nodes.at(i)->GetData())) { roi = dynamic_cast(nodes.at(i)->GetData()); roiB = true; } } if (!fibB) return; if (!roiB) return; vtkSmartPointer polyRoi = roi->GetVtkPolyData(); vtkSmartPointer polyFib = fib->GetFiberPolyData(); vtkSmartPointer selectEnclosedPoints = vtkSmartPointer::New(); selectEnclosedPoints->SetInput(polyFib); selectEnclosedPoints->SetSurface(polyRoi); selectEnclosedPoints->Update(); vtkSmartPointer newPoly = vtkSmartPointer::New(); vtkSmartPointer newCellArray = vtkSmartPointer::New(); vtkSmartPointer newPoints = vtkSmartPointer::New(); vtkSmartPointer newPolyComplement = vtkSmartPointer::New(); vtkSmartPointer newCellArrayComplement = vtkSmartPointer::New(); vtkSmartPointer newPointsComplement = vtkSmartPointer::New(); vtkSmartPointer vLines = polyFib->GetLines(); vLines->InitTraversal(); int numberOfLines = vLines->GetNumberOfCells(); // each line for (int j=0; jGetNextCell ( numPoints, points ); bool isPassing = false; // each point of this line for (int k=0; kIsInside(points[k])) { isPassing = true; // fill new polydata vtkSmartPointer container = vtkSmartPointer::New(); for (int k=0; kGetPoint(points[k]); vtkIdType pointId = newPoints->InsertNextPoint(point); container->GetPointIds()->InsertNextId(pointId); } newCellArray->InsertNextCell(container); break; } } if (!isPassing) { vtkSmartPointer container = vtkSmartPointer::New(); for (int k=0; kGetPoint(points[k]); vtkIdType pointId = newPointsComplement->InsertNextPoint(point); container->GetPointIds()->InsertNextId(pointId); } newCellArrayComplement->InsertNextCell(container); } } newPoly->SetPoints(newPoints); newPoly->SetLines(newCellArray); mitk::FiberBundleX::Pointer fb = mitk::FiberBundleX::New(newPoly); DataNode::Pointer newNode = DataNode::New(); newNode->SetData(fb); newNode->SetName("passing surface"); GetDefaultDataStorage()->Add(newNode); newPolyComplement->SetPoints(newPointsComplement); newPolyComplement->SetLines(newCellArrayComplement); mitk::FiberBundleX::Pointer fbComplement = mitk::FiberBundleX::New(newPolyComplement); DataNode::Pointer newNodeComplement = DataNode::New(); newNodeComplement->SetData(fbComplement); newNodeComplement->SetName("not passing surface"); GetDefaultDataStorage()->Add(newNodeComplement); } void QmitkFiberProcessingView::GenerateRoiImage(){ if (m_SelectedPF.empty()) return; mitk::Geometry3D::Pointer geometry; - if (m_SelectedImage.IsNotNull()) - geometry = m_SelectedImage->GetGeometry(); - else if (!m_SelectedFB.empty()) + if (!m_SelectedFB.empty()) { mitk::FiberBundleX::Pointer fib = dynamic_cast(m_SelectedFB.front()->GetData()); geometry = fib->GetGeometry(); } else return; + mitk::Vector3D spacing = geometry->GetSpacing(); + spacing /= m_UpsamplingFactor; + + mitk::Point3D newOrigin = geometry->GetOrigin(); + mitk::Geometry3D::BoundsArrayType bounds = geometry->GetBounds(); + newOrigin[0] += bounds.GetElement(0); + newOrigin[1] += bounds.GetElement(2); + newOrigin[2] += bounds.GetElement(4); + itk::Matrix direction; itk::ImageRegion<3> imageRegion; for (int i=0; i<3; i++) for (int j=0; j<3; j++) - direction[j][i] = geometry->GetMatrixColumn(i)[j]; + direction[j][i] = geometry->GetMatrixColumn(i)[j]/spacing[j]; imageRegion.SetSize(0, geometry->GetExtent(0)*m_UpsamplingFactor); imageRegion.SetSize(1, geometry->GetExtent(1)*m_UpsamplingFactor); imageRegion.SetSize(2, geometry->GetExtent(2)*m_UpsamplingFactor); m_PlanarFigureImage = itkUCharImageType::New(); - m_PlanarFigureImage->SetSpacing( geometry->GetSpacing() ); // Set the image spacing - m_PlanarFigureImage->SetOrigin( geometry->GetOrigin() ); // Set the image origin + m_PlanarFigureImage->SetSpacing( spacing ); // Set the image spacing + m_PlanarFigureImage->SetOrigin( newOrigin ); // Set the image origin m_PlanarFigureImage->SetDirection( direction ); // Set the image direction m_PlanarFigureImage->SetRegions( imageRegion ); m_PlanarFigureImage->Allocate(); m_PlanarFigureImage->FillBuffer( 0 ); Image::Pointer tmpImage = Image::New(); tmpImage->InitializeByItk(m_PlanarFigureImage.GetPointer()); tmpImage->SetVolume(m_PlanarFigureImage->GetBufferPointer()); for (int i=0; iInitializeByItk(m_PlanarFigureImage.GetPointer()); tmpImage->SetVolume(m_PlanarFigureImage->GetBufferPointer()); node->SetData(tmpImage); node->SetName("ROI Image"); this->GetDefaultDataStorage()->Add(node); } void QmitkFiberProcessingView::CompositeExtraction(mitk::DataNode::Pointer node, mitk::Image* image) { if (dynamic_cast(node.GetPointer()->GetData()) && !dynamic_cast(node.GetPointer()->GetData())) { m_PlanarFigure = dynamic_cast(node.GetPointer()->GetData()); AccessFixedDimensionByItk_2( image, InternalReorientImagePlane, 3, m_PlanarFigure->GetGeometry(), -1); // itk::Image< unsigned char, 3 >::Pointer outimage = itk::Image< unsigned char, 3 >::New(); // outimage->SetSpacing( m_PlanarFigure->GetGeometry()->GetSpacing()/m_UpsamplingFactor ); // Set the image spacing // mitk::Point3D origin = m_PlanarFigure->GetGeometry()->GetOrigin(); // mitk::Point3D indexOrigin; // m_PlanarFigure->GetGeometry()->WorldToIndex(origin, indexOrigin); // indexOrigin[0] = indexOrigin[0] - .5 * (1.0-1.0/m_UpsamplingFactor); // indexOrigin[1] = indexOrigin[1] - .5 * (1.0-1.0/m_UpsamplingFactor); // indexOrigin[2] = indexOrigin[2] - .5 * (1.0-1.0/m_UpsamplingFactor); // mitk::Point3D newOrigin; // m_PlanarFigure->GetGeometry()->IndexToWorld(indexOrigin, newOrigin); // outimage->SetOrigin( newOrigin ); // Set the image origin // itk::Matrix matrix; // for (int i=0; i<3; i++) // for (int j=0; j<3; j++) // matrix[j][i] = m_PlanarFigure->GetGeometry()->GetMatrixColumn(i)[j]/m_PlanarFigure->GetGeometry()->GetSpacing().GetElement(i); // outimage->SetDirection( matrix ); // Set the image direction // itk::ImageRegion<3> upsampledRegion; // upsampledRegion.SetSize(0, m_PlanarFigure->GetGeometry()->GetParametricExtentInMM(0)/m_PlanarFigure->GetGeometry()->GetSpacing()[0]); // upsampledRegion.SetSize(1, m_PlanarFigure->GetGeometry()->GetParametricExtentInMM(1)/m_PlanarFigure->GetGeometry()->GetSpacing()[1]); // upsampledRegion.SetSize(2, 1); // typename itk::Image< unsigned char, 3 >::RegionType::SizeType upsampledSize = upsampledRegion.GetSize(); // for (unsigned int n = 0; n < 2; n++) // { // upsampledSize[n] = upsampledSize[n] * m_UpsamplingFactor; // } // upsampledRegion.SetSize( upsampledSize ); // outimage->SetRegions( upsampledRegion ); // outimage->Allocate(); // this->m_InternalImage = mitk::Image::New(); // this->m_InternalImage->InitializeByItk( outimage.GetPointer() ); // this->m_InternalImage->SetVolume( outimage->GetBufferPointer() ); AccessFixedDimensionByItk_2( m_InternalImage, InternalCalculateMaskFromPlanarFigure, 3, 2, node->GetName() ); } } template < typename TPixel, unsigned int VImageDimension > void QmitkFiberProcessingView::InternalReorientImagePlane( const itk::Image< TPixel, VImageDimension > *image, mitk::Geometry3D* planegeo3D, int additionalIndex ) { MITK_INFO << "InternalReorientImagePlane() start"; typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< float, VImageDimension > FloatImageType; typedef itk::ResampleImageFilter ResamplerType; typename ResamplerType::Pointer resampler = ResamplerType::New(); mitk::PlaneGeometry* planegeo = dynamic_cast(planegeo3D); float upsamp = m_UpsamplingFactor; float gausssigma = 0.5; // Spacing typename ResamplerType::SpacingType spacing = planegeo->GetSpacing(); spacing[0] = image->GetSpacing()[0] / upsamp; spacing[1] = image->GetSpacing()[1] / upsamp; spacing[2] = image->GetSpacing()[2]; resampler->SetOutputSpacing( spacing ); // Size typename ResamplerType::SizeType size; size[0] = planegeo->GetParametricExtentInMM(0) / spacing[0]; size[1] = planegeo->GetParametricExtentInMM(1) / spacing[1]; size[2] = 1; resampler->SetSize( size ); // Origin typename mitk::Point3D orig = planegeo->GetOrigin(); typename mitk::Point3D corrorig; planegeo3D->WorldToIndex(orig,corrorig); corrorig[0] += 0.5/upsamp; corrorig[1] += 0.5/upsamp; corrorig[2] += 0; planegeo3D->IndexToWorld(corrorig,corrorig); resampler->SetOutputOrigin(corrorig ); // Direction typename ResamplerType::DirectionType direction; typename mitk::AffineTransform3D::MatrixType matrix = planegeo->GetIndexToWorldTransform()->GetMatrix(); for(int c=0; cSetOutputDirection( direction ); // Gaussian interpolation if(gausssigma != 0) { double sigma[3]; for( unsigned int d = 0; d < 3; d++ ) { sigma[d] = gausssigma * image->GetSpacing()[d]; } double alpha = 2.0; typedef itk::GaussianInterpolateImageFunction GaussianInterpolatorType; typename GaussianInterpolatorType::Pointer interpolator = GaussianInterpolatorType::New(); interpolator->SetInputImage( image ); interpolator->SetParameters( sigma, alpha ); resampler->SetInterpolator( interpolator ); } else { // typedef typename itk::BSplineInterpolateImageFunction // InterpolatorType; typedef typename itk::LinearInterpolateImageFunction InterpolatorType; typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); interpolator->SetInputImage( image ); resampler->SetInterpolator( interpolator ); } // Other resampling options resampler->SetInput( image ); resampler->SetDefaultPixelValue(0); MITK_INFO << "Resampling requested image plane ... "; resampler->Update(); MITK_INFO << " ... done"; if(additionalIndex < 0) { this->m_InternalImage = mitk::Image::New(); this->m_InternalImage->InitializeByItk( resampler->GetOutput() ); this->m_InternalImage->SetVolume( resampler->GetOutput()->GetBufferPointer() ); } } template < typename TPixel, unsigned int VImageDimension > void QmitkFiberProcessingView::InternalCalculateMaskFromPlanarFigure( itk::Image< TPixel, VImageDimension > *image, unsigned int axis, std::string nodeName ) { MITK_INFO << "InternalCalculateMaskFromPlanarFigure() start"; typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::CastImageFilter< ImageType, itkUCharImageType > CastFilterType; // Generate mask image as new image with same header as input image and // initialize with "1". itkUCharImageType::Pointer newMaskImage = itkUCharImageType::New(); newMaskImage->SetSpacing( image->GetSpacing() ); // Set the image spacing newMaskImage->SetOrigin( image->GetOrigin() ); // Set the image origin newMaskImage->SetDirection( image->GetDirection() ); // Set the image direction newMaskImage->SetRegions( image->GetLargestPossibleRegion() ); newMaskImage->Allocate(); newMaskImage->FillBuffer( 1 ); // Generate VTK polygon from (closed) PlanarFigure polyline // (The polyline points are shifted by -0.5 in z-direction to make sure // that the extrusion filter, which afterwards elevates all points by +0.5 // in z-direction, creates a 3D object which is cut by the the plane z=0) const Geometry2D *planarFigureGeometry2D = m_PlanarFigure->GetGeometry2D(); const PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const Geometry3D *imageGeometry3D = m_InternalImage->GetGeometry( 0 ); vtkPolyData *polyline = vtkPolyData::New(); polyline->Allocate( 1, 1 ); // Determine x- and y-dimensions depending on principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } // Create VTK polydata object of polyline contour vtkPoints *points = vtkPoints::New(); PlanarFigure::PolyLineType::const_iterator it; std::vector indices; unsigned int numberOfPoints = 0; for ( it = planarFigurePolyline.begin(); it != planarFigurePolyline.end(); ++it ) { Point3D point3D; // Convert 2D point back to the local index coordinates of the selected // image Point2D point2D = it->Point; planarFigureGeometry2D->WorldToIndex(point2D, point2D); point2D[0] -= 0.5/m_UpsamplingFactor; point2D[1] -= 0.5/m_UpsamplingFactor; planarFigureGeometry2D->IndexToWorld(point2D, point2D); planarFigureGeometry2D->Map( point2D, point3D ); // Polygons (partially) outside of the image bounds can not be processed // further due to a bug in vtkPolyDataToImageStencil if ( !imageGeometry3D->IsInside( point3D ) ) { float bounds[2] = {0,0}; bounds[0] = this->m_InternalImage->GetLargestPossibleRegion().GetSize().GetElement(i0); bounds[1] = this->m_InternalImage->GetLargestPossibleRegion().GetSize().GetElement(i1); imageGeometry3D->WorldToIndex( point3D, point3D ); // if (point3D[i0]<0) // point3D[i0] = 0.5; // else if (point3D[i0]>bounds[0]) // point3D[i0] = bounds[0]-0.5; // if (point3D[i1]<0) // point3D[i1] = 0.5; // else if (point3D[i1]>bounds[1]) // point3D[i1] = bounds[1]-0.5; if (point3D[i0]<0) point3D[i0] = 0.0; else if (point3D[i0]>bounds[0]) point3D[i0] = bounds[0]-0.001; if (point3D[i1]<0) point3D[i1] = 0.0; else if (point3D[i1]>bounds[1]) point3D[i1] = bounds[1]-0.001; points->InsertNextPoint( point3D[i0], point3D[i1], -0.5 ); numberOfPoints++; } else { imageGeometry3D->WorldToIndex( point3D, point3D ); // Add point to polyline array points->InsertNextPoint( point3D[i0], point3D[i1], -0.5 ); numberOfPoints++; } } polyline->SetPoints( points ); points->Delete(); vtkIdType *ptIds = new vtkIdType[numberOfPoints]; for ( vtkIdType i = 0; i < numberOfPoints; ++i ) { ptIds[i] = i; } polyline->InsertNextCell( VTK_POLY_LINE, numberOfPoints, ptIds ); // Extrude the generated contour polygon vtkLinearExtrusionFilter *extrudeFilter = vtkLinearExtrusionFilter::New(); extrudeFilter->SetInput( polyline ); extrudeFilter->SetScaleFactor( 1 ); extrudeFilter->SetExtrusionTypeToNormalExtrusion(); extrudeFilter->SetVector( 0.0, 0.0, 1.0 ); // Make a stencil from the extruded polygon vtkPolyDataToImageStencil *polyDataToImageStencil = vtkPolyDataToImageStencil::New(); polyDataToImageStencil->SetInput( extrudeFilter->GetOutput() ); // Export from ITK to VTK (to use a VTK filter) typedef itk::VTKImageImport< itkUCharImageType > ImageImportType; typedef itk::VTKImageExport< itkUCharImageType > ImageExportType; typename ImageExportType::Pointer itkExporter = ImageExportType::New(); itkExporter->SetInput( newMaskImage ); vtkImageImport *vtkImporter = vtkImageImport::New(); this->ConnectPipelines( itkExporter, vtkImporter ); vtkImporter->Update(); // Apply the generated image stencil to the input image vtkImageStencil *imageStencilFilter = vtkImageStencil::New(); imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() ); imageStencilFilter->SetStencil( polyDataToImageStencil->GetOutput() ); imageStencilFilter->ReverseStencilOff(); imageStencilFilter->SetBackgroundValue( 0 ); imageStencilFilter->Update(); // Export from VTK back to ITK vtkImageExport *vtkExporter = vtkImageExport::New(); vtkExporter->SetInputConnection( imageStencilFilter->GetOutputPort() ); vtkExporter->Update(); typename ImageImportType::Pointer itkImporter = ImageImportType::New(); this->ConnectPipelines( vtkExporter, itkImporter ); itkImporter->Update(); // calculate cropping bounding box m_InternalImageMask3D = itkImporter->GetOutput(); m_InternalImageMask3D->SetDirection(image->GetDirection()); itk::ImageRegionConstIterator itmask(m_InternalImageMask3D, m_InternalImageMask3D->GetLargestPossibleRegion()); itk::ImageRegionIterator itimage(image, image->GetLargestPossibleRegion()); itmask = itmask.Begin(); itimage = itimage.Begin(); typename ImageType::SizeType lowersize = {{9999999999,9999999999,9999999999}}; typename ImageType::SizeType uppersize = {{0,0,0}}; while( !itmask.IsAtEnd() ) { if(itmask.Get() == 0) { itimage.Set(0); } else { typename ImageType::IndexType index = itimage.GetIndex(); typename ImageType::SizeType signedindex; signedindex[0] = index[0]; signedindex[1] = index[1]; signedindex[2] = index[2]; lowersize[0] = signedindex[0] < lowersize[0] ? signedindex[0] : lowersize[0]; lowersize[1] = signedindex[1] < lowersize[1] ? signedindex[1] : lowersize[1]; lowersize[2] = signedindex[2] < lowersize[2] ? signedindex[2] : lowersize[2]; uppersize[0] = signedindex[0] > uppersize[0] ? signedindex[0] : uppersize[0]; uppersize[1] = signedindex[1] > uppersize[1] ? signedindex[1] : uppersize[1]; uppersize[2] = signedindex[2] > uppersize[2] ? signedindex[2] : uppersize[2]; } ++itmask; ++itimage; } typename ImageType::IndexType index; index[0] = lowersize[0]; index[1] = lowersize[1]; index[2] = lowersize[2]; typename ImageType::SizeType size; size[0] = uppersize[0] - lowersize[0] + 1; size[1] = uppersize[1] - lowersize[1] + 1; size[2] = uppersize[2] - lowersize[2] + 1; itk::ImageRegion<3> cropRegion = itk::ImageRegion<3>(index, size); // crop internal mask typedef itk::RegionOfInterestImageFilter< itkUCharImageType, itkUCharImageType > ROIMaskFilterType; typename ROIMaskFilterType::Pointer roi2 = ROIMaskFilterType::New(); roi2->SetRegionOfInterest(cropRegion); roi2->SetInput(m_InternalImageMask3D); roi2->Update(); m_InternalImageMask3D = roi2->GetOutput(); Image::Pointer tmpImage = Image::New(); tmpImage->InitializeByItk(m_InternalImageMask3D.GetPointer()); tmpImage->SetVolume(m_InternalImageMask3D->GetBufferPointer()); Image::Pointer tmpImage2 = Image::New(); tmpImage2->InitializeByItk(m_PlanarFigureImage.GetPointer()); const Geometry3D *pfImageGeometry3D = tmpImage2->GetGeometry( 0 ); const Geometry3D *intImageGeometry3D = tmpImage->GetGeometry( 0 ); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType imageIterator (m_InternalImageMask3D, m_InternalImageMask3D->GetRequestedRegion()); imageIterator.GoToBegin(); while ( !imageIterator.IsAtEnd() ) { unsigned char val = imageIterator.Value(); if (val>0) { itk::Index<3> index = imageIterator.GetIndex(); Point3D point; point[0] = index[0]; point[1] = index[1]; point[2] = index[2]; intImageGeometry3D->IndexToWorld(point, point); pfImageGeometry3D->WorldToIndex(point, point); point[i0] += 0.5; point[i1] += 0.5; index[0] = point[0]; index[1] = point[1]; index[2] = point[2]; - m_PlanarFigureImage->SetPixel(index, 1); + if (pfImageGeometry3D->IsIndexInside(index)) + m_PlanarFigureImage->SetPixel(index, 1); } ++imageIterator; } // Clean up VTK objects polyline->Delete(); extrudeFilter->Delete(); polyDataToImageStencil->Delete(); vtkImporter->Delete(); imageStencilFilter->Delete(); //vtkExporter->Delete(); // TODO: crashes when outcommented; memory leak?? delete[] ptIds; } void QmitkFiberProcessingView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkFiberProcessingView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } /* OnSelectionChanged is registered to SelectionService, therefore no need to implement SelectionService Listener explicitly */ void QmitkFiberProcessingView::UpdateGui() { // are fiber bundles selected? if ( m_SelectedFB.empty() ) { m_Controls->m_JoinBundles->setEnabled(false); m_Controls->m_SubstractBundles->setEnabled(false); m_Controls->m_ProcessFiberBundleButton->setEnabled(false); m_Controls->doExtractFibersButton->setEnabled(false); m_Controls->m_Extract3dButton->setEnabled(false); m_Controls->m_ResampleFibersButton->setEnabled(false); m_Controls->m_PlanarFigureButtonsFrame->setEnabled(false); m_Controls->m_FaColorFibersButton->setEnabled(false); } else { m_Controls->m_PlanarFigureButtonsFrame->setEnabled(true); m_Controls->m_ProcessFiberBundleButton->setEnabled(true); m_Controls->m_ResampleFibersButton->setEnabled(true); if (m_Surfaces.size()>0) m_Controls->m_Extract3dButton->setEnabled(true); // one bundle and one planar figure needed to extract fibers if (!m_SelectedPF.empty()) m_Controls->doExtractFibersButton->setEnabled(true); // more than two bundles needed to join/subtract if (m_SelectedFB.size() > 1) { m_Controls->m_JoinBundles->setEnabled(true); m_Controls->m_SubstractBundles->setEnabled(true); } else { m_Controls->m_JoinBundles->setEnabled(false); m_Controls->m_SubstractBundles->setEnabled(false); } if (m_SelectedImage.IsNotNull()) m_Controls->m_FaColorFibersButton->setEnabled(true); } // are planar figures selected? if ( m_SelectedPF.empty() ) { m_Controls->doExtractFibersButton->setEnabled(false); m_Controls->PFCompoANDButton->setEnabled(false); m_Controls->PFCompoORButton->setEnabled(false); m_Controls->PFCompoNOTButton->setEnabled(false); m_Controls->m_GenerateRoiImage->setEnabled(false); } else { - if ( m_SelectedImage.IsNotNull() || !m_SelectedFB.empty() ) + if ( !m_SelectedFB.empty() ) m_Controls->m_GenerateRoiImage->setEnabled(true); else m_Controls->m_GenerateRoiImage->setEnabled(false); if (m_SelectedPF.size() > 1) { m_Controls->PFCompoANDButton->setEnabled(true); m_Controls->PFCompoORButton->setEnabled(true); m_Controls->PFCompoNOTButton->setEnabled(false); } else { m_Controls->PFCompoANDButton->setEnabled(false); m_Controls->PFCompoORButton->setEnabled(false); m_Controls->PFCompoNOTButton->setEnabled(true); } } } void QmitkFiberProcessingView::OnSelectionChanged( std::vector nodes ) { if ( !this->IsVisible() ) return; //reset existing Vectors containing FiberBundles and PlanarFigures from a previous selection m_SelectedFB.clear(); m_SelectedPF.clear(); m_Surfaces.clear(); m_SelectedImage = NULL; for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; if ( dynamic_cast(node->GetData()) ) m_SelectedFB.push_back(node); else if (dynamic_cast(node->GetData())) m_SelectedPF.push_back(node); else if (dynamic_cast(node->GetData())) m_SelectedImage = dynamic_cast(node->GetData()); else if (dynamic_cast(node->GetData())) m_Surfaces.push_back(dynamic_cast(node->GetData())); } UpdateGui(); GenerateStats(); } void QmitkFiberProcessingView::OnDrawPolygon() { // bool checked = m_Controls->m_PolygonButton->isChecked(); // if(!this->AssertDrawingIsPossible(checked)) // return; mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); figure->ClosedOn(); this->AddFigureToDataStorage(figure, QString("Polygon%1").arg(++m_PolygonCounter)); MITK_INFO << "PlanarPolygon created ..."; mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDefaultDataStorage()->GetAll(); mitk::DataNode* node = 0; mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; mitk::PlanarFigure* figureP = 0; for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() ; it++) { node = const_cast(it->Value().GetPointer()); figureP = dynamic_cast(node->GetData()); if(figureP) { figureInteractor = dynamic_cast(node->GetInteractor()); if(figureInteractor.IsNull()) figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", node); mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); } } } void QmitkFiberProcessingView::OnDrawCircle() { //bool checked = m_Controls->m_CircleButton->isChecked(); //if(!this->AssertDrawingIsPossible(checked)) // return; mitk::PlanarCircle::Pointer figure = mitk::PlanarCircle::New(); this->AddFigureToDataStorage(figure, QString("Circle%1").arg(++m_EllipseCounter)); this->GetDataStorage()->Modified(); MITK_INFO << "PlanarCircle created ..."; //call mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDefaultDataStorage()->GetAll(); mitk::DataNode* node = 0; mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; mitk::PlanarFigure* figureP = 0; for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() ; it++) { node = const_cast(it->Value().GetPointer()); figureP = dynamic_cast(node->GetData()); if(figureP) { figureInteractor = dynamic_cast(node->GetInteractor()); if(figureInteractor.IsNull()) figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", node); mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); } } } void QmitkFiberProcessingView::Activated() { MITK_INFO << "FB OPerations ACTIVATED()"; /* mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDefaultDataStorage()->GetAll(); mitk::DataNode* node = 0; mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; mitk::PlanarFigure* figure = 0; for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() ; it++) { node = const_cast(it->Value().GetPointer()); figure = dynamic_cast(node->GetData()); if(figure) { figureInteractor = dynamic_cast(node->GetInteractor()); if(figureInteractor.IsNull()) figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", node); mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); } } */ } void QmitkFiberProcessingView::AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name, const char *propertyKey, mitk::BaseProperty *property ) { // initialize figure's geometry with empty geometry mitk::PlaneGeometry::Pointer emptygeometry = mitk::PlaneGeometry::New(); figure->SetGeometry2D( emptygeometry ); //set desired data to DataNode where Planarfigure is stored mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetName(name.toStdString()); newNode->SetData(figure); newNode->AddProperty( "planarfigure.default.line.color", mitk::ColorProperty::New(1.0,0.0,0.0)); newNode->AddProperty( "planarfigure.line.width", mitk::FloatProperty::New(2.0)); newNode->AddProperty( "planarfigure.drawshadow", mitk::BoolProperty::New(true)); newNode->AddProperty( "selected", mitk::BoolProperty::New(true) ); newNode->AddProperty( "planarfigure.ishovering", mitk::BoolProperty::New(true) ); newNode->AddProperty( "planarfigure.drawoutline", mitk::BoolProperty::New(true) ); newNode->AddProperty( "planarfigure.drawquantities", mitk::BoolProperty::New(false) ); newNode->AddProperty( "planarfigure.drawshadow", mitk::BoolProperty::New(true) ); newNode->AddProperty( "planarfigure.line.width", mitk::FloatProperty::New(3.0) ); newNode->AddProperty( "planarfigure.shadow.widthmodifier", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.outline.width", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.helperline.width", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.default.line.color", mitk::ColorProperty::New(1.0,1.0,1.0) ); newNode->AddProperty( "planarfigure.default.line.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.default.outline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.default.outline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.default.helperline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.default.helperline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.default.markerline.color", mitk::ColorProperty::New(0.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.default.markerline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.default.marker.color", mitk::ColorProperty::New(1.0,1.0,1.0) ); newNode->AddProperty( "planarfigure.default.marker.opacity",mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.hover.line.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.hover.line.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.hover.outline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.hover.outline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.hover.helperline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.hover.helperline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.hover.markerline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.hover.markerline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.hover.marker.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.hover.marker.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.selected.line.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.selected.line.opacity",mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.selected.outline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.selected.outline.opacity", mitk::FloatProperty::New(2.0)); newNode->AddProperty( "planarfigure.selected.helperline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.selected.helperline.opacity",mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.selected.markerline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.selected.markerline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.selected.marker.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.selected.marker.opacity",mitk::FloatProperty::New(2.0)); // figure drawn on the topmost layer / image newNode->SetColor(1.0,1.0,1.0); newNode->SetOpacity(0.8); GetDataStorage()->Add(newNode ); std::vector selectedNodes = GetDataManagerSelection(); for(unsigned int i = 0; i < selectedNodes.size(); i++) { selectedNodes[i]->SetSelected(false); } newNode->SetSelected(true); } void QmitkFiberProcessingView::DoFiberExtraction() { if ( m_SelectedFB.empty() ){ QMessageBox::information( NULL, "Warning", "No fibe bundle selected!"); MITK_WARN("QmitkFiberProcessingView") << "no fibe bundle selected"; return; } for (int i=0; i(m_SelectedFB.at(i)->GetData()); mitk::PlanarFigure::Pointer roi = dynamic_cast (m_SelectedPF.at(0)->GetData()); mitk::FiberBundleX::Pointer extFB = fib->ExtractFiberSubset(roi); if (extFB->GetNumFibers()<=0) continue; mitk::DataNode::Pointer node; node = mitk::DataNode::New(); node->SetData(extFB); QString name(m_SelectedFB.at(i)->GetName().c_str()); name += "_"; name += m_SelectedPF.at(0)->GetName().c_str(); node->SetName(name.toStdString()); GetDataStorage()->Add(node); m_SelectedFB.at(i)->SetVisibility(false); } } void QmitkFiberProcessingView::GenerateAndComposite() { mitk::PlanarFigureComposite::Pointer PFCAnd = mitk::PlanarFigureComposite::New(); mitk::PlaneGeometry* currentGeometry2D = dynamic_cast( const_cast(GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer()->GetCurrentWorldGeometry2D())); PFCAnd->SetGeometry2D(currentGeometry2D); PFCAnd->setOperationType(mitk::PFCOMPOSITION_AND_OPERATION); for( std::vector::iterator it = m_SelectedPF.begin(); it != m_SelectedPF.end(); ++it ) { mitk::DataNode::Pointer nodePF = *it; mitk::PlanarFigure::Pointer tmpPF = dynamic_cast( nodePF->GetData() ); PFCAnd->addPlanarFigure( tmpPF ); PFCAnd->addDataNode( nodePF ); PFCAnd->setDisplayName("AND_COMPO"); } AddCompositeToDatastorage(PFCAnd, NULL); } void QmitkFiberProcessingView::GenerateOrComposite() { mitk::PlanarFigureComposite::Pointer PFCOr = mitk::PlanarFigureComposite::New(); mitk::PlaneGeometry* currentGeometry2D = dynamic_cast( const_cast(GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer()->GetCurrentWorldGeometry2D())); PFCOr->SetGeometry2D(currentGeometry2D); PFCOr->setOperationType(mitk::PFCOMPOSITION_OR_OPERATION); for( std::vector::iterator it = m_SelectedPF.begin(); it != m_SelectedPF.end(); ++it ) { mitk::DataNode::Pointer nodePF = *it; mitk::PlanarFigure::Pointer tmpPF = dynamic_cast( nodePF->GetData() ); PFCOr->addPlanarFigure( tmpPF ); PFCOr->addDataNode( nodePF ); PFCOr->setDisplayName("OR_COMPO"); } AddCompositeToDatastorage(PFCOr, NULL); } void QmitkFiberProcessingView::GenerateNotComposite() { mitk::PlanarFigureComposite::Pointer PFCNot = mitk::PlanarFigureComposite::New(); mitk::PlaneGeometry* currentGeometry2D = dynamic_cast( const_cast(GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer()->GetCurrentWorldGeometry2D())); PFCNot->SetGeometry2D(currentGeometry2D); PFCNot->setOperationType(mitk::PFCOMPOSITION_NOT_OPERATION); for( std::vector::iterator it = m_SelectedPF.begin(); it != m_SelectedPF.end(); ++it ) { mitk::DataNode::Pointer nodePF = *it; mitk::PlanarFigure::Pointer tmpPF = dynamic_cast( nodePF->GetData() ); PFCNot->addPlanarFigure( tmpPF ); PFCNot->addDataNode( nodePF ); PFCNot->setDisplayName("NOT_COMPO"); } AddCompositeToDatastorage(PFCNot, NULL); } /* CLEANUP NEEDED */ void QmitkFiberProcessingView::AddCompositeToDatastorage(mitk::PlanarFigureComposite::Pointer pfcomp, mitk::DataNode::Pointer parentDataNode ) { mitk::DataNode::Pointer newPFCNode; newPFCNode = mitk::DataNode::New(); newPFCNode->SetName( pfcomp->getDisplayName() ); newPFCNode->SetData(pfcomp); newPFCNode->SetVisibility(true); switch (pfcomp->getOperationType()) { case 0: { if (!parentDataNode.IsNull()) { GetDataStorage()->Add(newPFCNode, parentDataNode); } else { GetDataStorage()->Add(newPFCNode); } //iterate through its childs for(int i=0; igetNumberOfChildren(); ++i) { mitk::PlanarFigure::Pointer tmpPFchild = pfcomp->getChildAt(i); mitk::DataNode::Pointer savedPFchildNode = pfcomp->getDataNodeAt(i); mitk::PlanarFigureComposite::Pointer pfcompcast= dynamic_cast(tmpPFchild.GetPointer()); if ( !pfcompcast.IsNull() ) { // child is of type planar Figure composite // make new node of the child, cuz later the child has to be removed of its old position in datamanager // feed new dataNode with information of the savedDataNode, which is gonna be removed soon mitk::DataNode::Pointer newChildPFCNode; newChildPFCNode = mitk::DataNode::New(); newChildPFCNode->SetData(tmpPFchild); newChildPFCNode->SetName( savedPFchildNode->GetName() ); pfcompcast->setDisplayName( savedPFchildNode->GetName() ); //name might be changed in DataManager by user //update inside vector the dataNodePointer pfcomp->replaceDataNodeAt(i, newChildPFCNode); AddCompositeToDatastorage(pfcompcast, newPFCNode); //the current PFCNode becomes the childs parent // remove savedNode here, cuz otherwise its children will change their position in the dataNodeManager // without having its parent anymore //GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; }else{ MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); } // remove old child position in dataStorage GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " still exists"; } } else { // child is not of type PlanarFigureComposite, so its one of the planarFigures // create new dataNode containing the data of the old dataNode, but position in dataManager will be // modified cuz we re setting a (new) parent. mitk::DataNode::Pointer newPFchildNode = mitk::DataNode::New(); newPFchildNode->SetName(savedPFchildNode->GetName() ); newPFchildNode->SetData(tmpPFchild); newPFchildNode->SetVisibility(true); // replace the dataNode in PFComp DataNodeVector pfcomp->replaceDataNodeAt(i, newPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; } else { MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); } // remove old child position in dataStorage GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " still exists"; } MITK_INFO << "adding " << newPFchildNode->GetName() << " to " << newPFCNode->GetName(); //add new child to datamanager with its new position as child of newPFCNode parent GetDataStorage()->Add(newPFchildNode, newPFCNode); } } GetDataStorage()->Modified(); break; } case 1: { if (!parentDataNode.IsNull()) { MITK_INFO << "adding " << newPFCNode->GetName() << " to " << parentDataNode->GetName() ; GetDataStorage()->Add(newPFCNode, parentDataNode); } else { MITK_INFO << "adding " << newPFCNode->GetName(); GetDataStorage()->Add(newPFCNode); } for(int i=0; igetNumberOfChildren(); ++i) { mitk::PlanarFigure::Pointer tmpPFchild = pfcomp->getChildAt(i); mitk::DataNode::Pointer savedPFchildNode = pfcomp->getDataNodeAt(i); mitk::PlanarFigureComposite::Pointer pfcompcast= dynamic_cast(tmpPFchild.GetPointer()); if ( !pfcompcast.IsNull() ) { // child is of type planar Figure composite // make new node of the child, cuz later the child has to be removed of its old position in datamanager // feed new dataNode with information of the savedDataNode, which is gonna be removed soon mitk::DataNode::Pointer newChildPFCNode; newChildPFCNode = mitk::DataNode::New(); newChildPFCNode->SetData(tmpPFchild); newChildPFCNode->SetName( savedPFchildNode->GetName() ); pfcompcast->setDisplayName( savedPFchildNode->GetName() ); //name might be changed in DataManager by user //update inside vector the dataNodePointer pfcomp->replaceDataNodeAt(i, newChildPFCNode); AddCompositeToDatastorage(pfcompcast, newPFCNode); //the current PFCNode becomes the childs parent // remove savedNode here, cuz otherwise its children will change their position in the dataNodeManager // without having its parent anymore //GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; }else{ MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); } // remove old child position in dataStorage GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " still exists"; } } else { // child is not of type PlanarFigureComposite, so its one of the planarFigures // create new dataNode containing the data of the old dataNode, but position in dataManager will be // modified cuz we re setting a (new) parent. mitk::DataNode::Pointer newPFchildNode = mitk::DataNode::New(); newPFchildNode->SetName(savedPFchildNode->GetName() ); newPFchildNode->SetData(tmpPFchild); newPFchildNode->SetVisibility(true); // replace the dataNode in PFComp DataNodeVector pfcomp->replaceDataNodeAt(i, newPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; }else{ MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); } // remove old child position in dataStorage GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " still exists"; } MITK_INFO << "adding " << newPFchildNode->GetName() << " to " << newPFCNode->GetName(); //add new child to datamanager with its new position as child of newPFCNode parent GetDataStorage()->Add(newPFchildNode, newPFCNode); } } GetDataStorage()->Modified(); break; } case 2: { if (!parentDataNode.IsNull()) { MITK_INFO << "adding " << newPFCNode->GetName() << " to " << parentDataNode->GetName() ; GetDataStorage()->Add(newPFCNode, parentDataNode); } else { MITK_INFO << "adding " << newPFCNode->GetName(); GetDataStorage()->Add(newPFCNode); } //iterate through its childs for(int i=0; igetNumberOfChildren(); ++i) { mitk::PlanarFigure::Pointer tmpPFchild = pfcomp->getChildAt(i); mitk::DataNode::Pointer savedPFchildNode = pfcomp->getDataNodeAt(i); mitk::PlanarFigureComposite::Pointer pfcompcast= dynamic_cast(tmpPFchild.GetPointer()); if ( !pfcompcast.IsNull() ) { // child is of type planar Figure composite // makeRemoveBundle new node of the child, cuz later the child has to be removed of its old position in datamanager // feed new dataNode with information of the savedDataNode, which is gonna be removed soon mitk::DataNode::Pointer newChildPFCNode; newChildPFCNode = mitk::DataNode::New(); newChildPFCNode->SetData(tmpPFchild); newChildPFCNode->SetName( savedPFchildNode->GetName() ); pfcompcast->setDisplayName( savedPFchildNode->GetName() ); //name might be changed in DataManager by user //update inside vector the dataNodePointer pfcomp->replaceDataNodeAt(i, newChildPFCNode); AddCompositeToDatastorage(pfcompcast, newPFCNode); //the current PFCNode becomes the childs parent // remove savedNode here, cuz otherwise its children will change their position in the dataNodeManager // without having its parent anymore //GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; }else{ MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); } // remove old child position in dataStorage GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " still exists"; } } else { // child is not of type PlanarFigureComposite, so its one of the planarFigures // create new dataNode containing the data of the old dataNode, but position in dataManager will be // modified cuz we re setting a (new) parent. mitk::DataNode::Pointer newPFchildNode = mitk::DataNode::New(); newPFchildNode->SetName(savedPFchildNode->GetName() ); newPFchildNode->SetData(tmpPFchild); newPFchildNode->SetVisibility(true); // replace the dataNode in PFComp DataNodeVector pfcomp->replaceDataNodeAt(i, newPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; }else{ MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); } // remove old child position in dataStorage GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " still exists"; } MITK_INFO << "adding " << newPFchildNode->GetName() << " to " << newPFCNode->GetName(); //add new child to datamanager with its new position as child of newPFCNode parent GetDataStorage()->Add(newPFchildNode, newPFCNode); } } GetDataStorage()->Modified(); break; } default: MITK_INFO << "we have an UNDEFINED composition... ERROR" ; break; } } void QmitkFiberProcessingView::JoinBundles() { if ( m_SelectedFB.size()<2 ){ QMessageBox::information( NULL, "Warning", "Select at least two fiber bundles!"); MITK_WARN("QmitkFiberProcessingView") << "Select at least two fiber bundles!"; return; } std::vector::const_iterator it = m_SelectedFB.begin(); mitk::FiberBundleX::Pointer newBundle = dynamic_cast((*it)->GetData()); QString name(""); name += QString((*it)->GetName().c_str()); ++it; for (it; it!=m_SelectedFB.end(); ++it) { newBundle = newBundle->AddBundle(dynamic_cast((*it)->GetData())); name += "+"+QString((*it)->GetName().c_str()); } mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); fbNode->SetData(newBundle); fbNode->SetName(name.toStdString()); fbNode->SetVisibility(true); GetDataStorage()->Add(fbNode); } void QmitkFiberProcessingView::SubstractBundles() { if ( m_SelectedFB.size()<2 ){ QMessageBox::information( NULL, "Warning", "Select at least two fiber bundles!"); MITK_WARN("QmitkFiberProcessingView") << "Select at least two fiber bundles!"; return; } std::vector::const_iterator it = m_SelectedFB.begin(); mitk::FiberBundleX::Pointer newBundle = dynamic_cast((*it)->GetData()); QString name(""); name += QString((*it)->GetName().c_str()); ++it; for (it; it!=m_SelectedFB.end(); ++it) { newBundle = newBundle->SubtractBundle(dynamic_cast((*it)->GetData())); if (newBundle.IsNull()) break; name += "-"+QString((*it)->GetName().c_str()); } if (newBundle.IsNull()) { QMessageBox::information(NULL, "No output generated:", "The resulting fiber bundle contains no fibers. Did you select the fiber bundles in the correct order? X-Y is not equal to Y-X!"); return; } mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); fbNode->SetData(newBundle); fbNode->SetName(name.toStdString()); fbNode->SetVisibility(true); GetDataStorage()->Add(fbNode); } void QmitkFiberProcessingView::GenerateStats() { if ( m_SelectedFB.empty() ) return; QString stats(""); for( int i=0; i(node->GetData())) { if (i>0) stats += "\n-----------------------------\n"; stats += QString(node->GetName().c_str()) + "\n"; mitk::FiberBundleX::Pointer fib = dynamic_cast(node->GetData()); vtkSmartPointer fiberPolyData = fib->GetFiberPolyData(); vtkSmartPointer vLines = fiberPolyData->GetLines(); vLines->InitTraversal(); int numberOfLines = vLines->GetNumberOfCells(); stats += "Number of fibers: "+ QString::number(numberOfLines) + "\n"; float length = 0; std::vector lengths; for (int i=0; iGetNextCell ( numPoints, points ); float l=0; for (unsigned int j=0; j p1; itk::Point p2; fiberPolyData->GetPoint(points[j], p1.GetDataPointer()); fiberPolyData->GetPoint(points[j+1], p2.GetDataPointer()); float dist = p1.EuclideanDistanceTo(p2); length += dist; l += dist; } itk::Point p2; fiberPolyData->GetPoint(points[numPoints-1], p2.GetDataPointer()); lengths.push_back(l); } std::sort(lengths.begin(), lengths.end()); if (numberOfLines>0) length /= numberOfLines; float dev=0; int count = 0; vLines->InitTraversal(); for (int i=0; iGetNextCell ( numPoints, points ); float l=0; for (unsigned int j=0; j p1; itk::Point p2; fiberPolyData->GetPoint(points[j], p1.GetDataPointer()); fiberPolyData->GetPoint(points[j+1], p2.GetDataPointer()); float dist = p1.EuclideanDistanceTo(p2); l += dist; } dev += (length-l)*(length-l); count++; } if (numberOfLines>1) dev /= (numberOfLines-1); else dev = 0; stats += "Min. length: "+ QString::number(lengths.front(),'f',1) + " mm\n"; stats += "Max. length: "+ QString::number(lengths.back(),'f',1) + " mm\n"; stats += "Mean length: "+ QString::number(length,'f',1) + " mm\n"; stats += "Median length: "+ QString::number(lengths.at(lengths.size()/2),'f',1) + " mm\n"; stats += "Standard deviation: "+ QString::number(sqrt(dev),'f',1) + " mm\n"; } } this->m_Controls->m_StatsTextEdit->setText(stats); } void QmitkFiberProcessingView::ProcessSelectedBundles() { if ( m_SelectedFB.empty() ){ QMessageBox::information( NULL, "Warning", "No fibe bundle selected!"); MITK_WARN("QmitkFiberProcessingView") << "no fibe bundle selected"; return; } int generationMethod = m_Controls->m_GenerationBox->currentIndex(); for( int i=0; i(node->GetData())) { mitk::FiberBundleX::Pointer fib = dynamic_cast(node->GetData()); QString name(node->GetName().c_str()); DataNode::Pointer newNode = NULL; switch(generationMethod){ case 0: newNode = GenerateTractDensityImage(fib, false); name += "_TDI"; break; case 1: newNode = GenerateTractDensityImage(fib, true); name += "_envelope"; break; case 2: newNode = GenerateColorHeatmap(fib); break; case 3: newNode = GenerateFiberEndingsImage(fib); name += "_fiber_endings"; break; case 4: newNode = GenerateFiberEndingsPointSet(fib); name += "_fiber_endings"; break; } if (newNode.IsNotNull()) { newNode->SetName(name.toStdString()); GetDataStorage()->Add(newNode); } } } } // generate pointset displaying the fiber endings mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateFiberEndingsPointSet(mitk::FiberBundleX::Pointer fib) { mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); vtkSmartPointer fiberPolyData = fib->GetFiberPolyData(); vtkSmartPointer vLines = fiberPolyData->GetLines(); vLines->InitTraversal(); int count = 0; int numFibers = fib->GetNumFibers(); for( int i=0; iGetNextCell ( numPoints, points ); if (numPoints>0) { double* point = fiberPolyData->GetPoint(points[0]); itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; pointSet->InsertPoint(count, itkPoint); count++; } if (numPoints>2) { double* point = fiberPolyData->GetPoint(points[numPoints-1]); itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; pointSet->InsertPoint(count, itkPoint); count++; } } mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( pointSet ); return node; } // generate image displaying the fiber endings mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateFiberEndingsImage(mitk::FiberBundleX::Pointer fib) { typedef unsigned char OutPixType; typedef itk::Image OutImageType; typedef itk::TractsToFiberEndingsImageFilter< OutImageType > ImageGeneratorType; ImageGeneratorType::Pointer generator = ImageGeneratorType::New(); generator->SetFiberBundle(fib); generator->SetInvertImage(m_Controls->m_InvertCheckbox->isChecked()); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { OutImageType::Pointer itkImage = OutImageType::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } generator->Update(); // get output image OutImageType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // init data node mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); return node; } // generate rgba heatmap from fiber bundle mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateColorHeatmap(mitk::FiberBundleX::Pointer fib) { typedef itk::RGBAPixel OutPixType; typedef itk::Image OutImageType; typedef itk::TractsToRgbaImageFilter< OutImageType > ImageGeneratorType; ImageGeneratorType::Pointer generator = ImageGeneratorType::New(); generator->SetFiberBundle(fib); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { itk::Image::Pointer itkImage = itk::Image::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } generator->Update(); // get output image typedef itk::Image OutType; OutType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // init data node mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); return node; } // generate tract density image from fiber bundle mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateTractDensityImage(mitk::FiberBundleX::Pointer fib, bool binary) { typedef float OutPixType; typedef itk::Image OutImageType; itk::TractDensityImageFilter< OutImageType >::Pointer generator = itk::TractDensityImageFilter< OutImageType >::New(); generator->SetFiberBundle(fib); generator->SetBinaryOutput(binary); generator->SetInvertImage(m_Controls->m_InvertCheckbox->isChecked()); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { OutImageType::Pointer itkImage = OutImageType::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } generator->Update(); // get output image typedef itk::Image OutType; OutType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // init data node mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); return node; } void QmitkFiberProcessingView::ResampleSelectedBundles() { int factor = this->m_Controls->m_ResampleFibersSpinBox->value(); for (int i=0; i(m_SelectedFB.at(i)->GetData()); fib->DoFiberSmoothing(factor); } } void QmitkFiberProcessingView::DoFaColorCoding() { if (m_SelectedImage.IsNull()) return; // mitk::PixelType pType = mitk::MakeScalarPixelType(); // if (m_SelectedImage->GetPixelType()!=pType) // { // //mitk::Image bla; bla.GetPixelType().GetNameOfClass() // MITK_INFO << m_SelectedImage->GetPixelType().GetNameOfClass(); // QMessageBox::warning(NULL, "Wrong Image Type", "FA/GFA image should be of type float"); //// return; // } for( int i=0; i(m_SelectedFB.at(i)->GetData()); fib->SetFAMap(m_SelectedImage); fib->SetColorCoding(mitk::FiberBundleX::COLORCODING_FA_BASED); fib->DoColorCodingFaBased(); } if(m_MultiWidget) m_MultiWidget->RequestUpdate(); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingViewControls.ui index c1cf883f2d..e907ff8063 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingViewControls.ui @@ -1,683 +1,683 @@ QmitkFiberProcessingViewControls 0 0 665 587 Form 0 9 3 9 3 Fiber Bundle Modification 0 0 200 0 16777215 60 QFrame::NoFrame QFrame::Raised 0 30 30 Draw circular ROI. Select reference fiber bundle to execute. :/QmitkDiffusionImaging/circle.png:/QmitkDiffusionImaging/circle.png 32 32 false true 30 30 Draw rectangular ROI. Select reference fiber bundle to execute. :/QmitkDiffusionImaging/rectangle.png:/QmitkDiffusionImaging/rectangle.png 32 32 true true 30 30 Draw polygonal ROI. Select reference fiber bundle to execute. :/QmitkDiffusionImaging/polygon.png:/QmitkDiffusionImaging/polygon.png 32 32 true true Qt::Horizontal 40 20 QFrame::NoFrame QFrame::Raised 0 false 0 0 200 16777215 11 Extract fibers passing through selected ROI or composite ROI. Select ROI and fiber bundle to execute. Extract false 0 0 200 16777215 11 Returns all fibers contained in bundle X that are not contained in bundle Y (not commutative!). Select at least two fiber bundles to execute. Substract false 0 0 200 16777215 11 Merge selected fiber bundles. Select at least two fiber bundles to execute. Join Qt::Horizontal 40 20 false 0 0 200 16777215 11 Extract fibers passing through selected surface mesh. Select surface mesh and fiber bundle to execute. Extract 3D false 0 0 16777215 16777215 11 Generate a binary image containing all selected ROIs. Select at least one ROI (planar figure) and a reference fiber bundle or image. ROI Image 0 0 200 0 16777215 60 QFrame::NoFrame QFrame::Raised 0 Qt::Horizontal 40 20 false 60 16777215 Create AND composition with selected ROIs. AND false 60 16777215 Create OR composition with selected ROIs. OR false 60 16777215 Create NOT composition from selected ROI. NOT Fiber Bundle Processing 0 0 Tract Density Image (TDI) Binary Envelope Fiber Bundle Image Fiber Endings Image Fiber Endings Pointset Upsampling factor 1 10 2 false 0 0 200 16777215 11 Perform selected operation on all selected fiber bundles. Generate If selected operation generates an image, the inverse image is returned. Invert false 0 0 200 16777215 11 Resample fibers using a Kochanek spline interpolation. Smooth Fibers Points per cm 1 50 10 false 0 0 200 16777215 11 - Resample fibers using a Kochanek spline interpolation. + Apply float image values (0-1) as color coding to the selected fiber bundle. - FA Color Coding + Color By Scalar Map Fiber Bundle Statistics Courier 10 Pitch false true Qt::Vertical 20 40 diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkIVIMView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkIVIMView.cpp index 4ca5b1f513..17eb182803 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkIVIMView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkIVIMView.cpp @@ -1,842 +1,814 @@ /*========================================================================= Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkIVIMView.h" #include "QmitkStdMultiWidget.h" // qt #include "qmessagebox.h" #include "qclipboard.h" // mitk #include "mitkDiffusionImage.h" #include "mitkImageCast.h" // itk #include "itkScalarImageToHistogramGenerator.h" #include "itkRegionOfInterestImageFilter.h" #include "itkImageRegionConstIteratorWithIndex.h" // itk/mitk #include "itkDiffusionIntravoxelIncoherentMotionReconstructionImageFilter.h" #include "itkRegularizedIVIMReconstructionFilter.h" #include "mitkImageCast.h" const std::string QmitkIVIMView::VIEW_ID = "org.mitk.views.ivim"; QmitkIVIMView::QmitkIVIMView() : QmitkFunctionality() , m_Controls( 0 ) , m_MultiWidget( NULL ) , m_Active(false) , m_SliceObserverTag1(0), m_SliceObserverTag2(0), m_SliceObserverTag3(0) + , m_DiffusionImageNode(NULL) + , m_MaskImageNode(NULL) { } QmitkIVIMView::~QmitkIVIMView() { QmitkStdMultiWidget* MultiWidget = this->GetActiveStdMultiWidget(false); if(MultiWidget) { //unregister observers when view is destroyed if( MultiWidget->mitkWidget1 != NULL && m_SliceObserverTag1 != 0) { mitk::SliceNavigationController* slicer = MultiWidget->mitkWidget1->GetSliceNavigationController(); slicer->RemoveObserver( m_SliceObserverTag1 ); } if( MultiWidget->mitkWidget2 != NULL && m_SliceObserverTag2 != 0) { mitk::SliceNavigationController* slicer = MultiWidget->mitkWidget2->GetSliceNavigationController(); slicer->RemoveObserver( m_SliceObserverTag2 ); } if( MultiWidget->mitkWidget3!= NULL && m_SliceObserverTag3 != 0) { mitk::SliceNavigationController* slicer = MultiWidget->mitkWidget3->GetSliceNavigationController(); slicer->RemoveObserver( m_SliceObserverTag3 ); } } } void QmitkIVIMView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkIVIMViewControls; m_Controls->setupUi( parent ); connect( m_Controls->m_ButtonStart, SIGNAL(clicked()), this, SLOT(FittIVIMStart()) ); connect( m_Controls->m_ButtonAutoThres, SIGNAL(clicked()), this, SLOT(AutoThreshold()) ); connect( m_Controls->m_MethodCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(MethodCombo(int)) ); connect( m_Controls->m_DStarSlider, SIGNAL(valueChanged(int)), this, SLOT(DStarSlider(int)) ); connect( m_Controls->m_BThreshSlider, SIGNAL(valueChanged(int)), this, SLOT(BThreshSlider(int)) ); connect( m_Controls->m_S0ThreshSlider, SIGNAL(valueChanged(int)), this, SLOT(S0ThreshSlider(int)) ); connect( m_Controls->m_NumItSlider, SIGNAL(valueChanged(int)), this, SLOT(NumItsSlider(int)) ); connect( m_Controls->m_LambdaSlider, SIGNAL(valueChanged(int)), this, SLOT(LambdaSlider(int)) ); connect( m_Controls->m_DisplayResultsCheckbox, SIGNAL(clicked()), this, SLOT(Checkbox()) ); connect( m_Controls->m_CheckDStar, SIGNAL(clicked()), this, SLOT(Checkbox()) ); connect( m_Controls->m_CheckD, SIGNAL(clicked()), this, SLOT(Checkbox()) ); connect( m_Controls->m_Checkf, SIGNAL(clicked()), this, SLOT(Checkbox()) ); connect( m_Controls->m_ChooseMethod, SIGNAL(clicked()), this, SLOT(ChooseMethod()) ); connect( m_Controls->m_CurveClipboard, SIGNAL(clicked()), this, SLOT(ClipboardCurveButtonClicked()) ); connect( m_Controls->m_ValuesClipboard, SIGNAL(clicked()), this, SLOT(ClipboardStatisticsButtonClicked()) ); } QString dstar = QString::number(m_Controls->m_DStarSlider->value()/1000.0); m_Controls->m_DStarLabel->setText(dstar); QString bthresh = QString::number(m_Controls->m_BThreshSlider->value()*5.0); m_Controls->m_BThreshLabel->setText(bthresh); QString s0thresh = QString::number(m_Controls->m_S0ThreshSlider->value()*0.5); m_Controls->m_S0ThreshLabel->setText(s0thresh); QString numits = QString::number(m_Controls->m_NumItSlider->value()); m_Controls->m_NumItsLabel->setText(numits); QString lambda = QString::number(m_Controls->m_LambdaSlider->value()*.00001); m_Controls->m_LambdaLabel->setText(lambda); m_Controls->m_VisualizeResultsWidget->setVisible(m_Controls->m_DisplayResultsCheckbox->isChecked()); m_Controls->m_MethodCombo->setVisible(m_Controls->m_ChooseMethod->isChecked()); - -// m_Controls->m_ADCBValues->setVisible(m_Controls->m_CheckADC->isChecked()); + m_Controls->m_Warning->setVisible(false); MethodCombo(m_Controls->m_MethodCombo->currentIndex()); } void QmitkIVIMView::Checkbox() { m_Controls->m_VisualizeResultsWidget->setVisible(m_Controls->m_DisplayResultsCheckbox->isChecked()); // m_Controls->m_ADCBValues->setVisible(m_Controls->m_CheckADC->isChecked()); itk::StartEvent dummy; OnSliceChanged(dummy); } void QmitkIVIMView::MethodCombo(int val) { switch(val) { case 0: - m_Controls->dstar->setVisible(false); - m_Controls->thres->setVisible(false); - m_Controls->thres_2->setVisible(true); - m_Controls->m_RegFrame->setVisible(false); + m_Controls->m_DstarFrame->setVisible(false); + m_Controls->m_NeglSiFrame->setVisible(true); + m_Controls->m_NeglBframe->setVisible(false); + m_Controls->m_IterationsFrame->setVisible(false); + m_Controls->m_LambdaFrame->setVisible(false); break; case 1: - m_Controls->dstar->setVisible(true); - m_Controls->thres->setVisible(false); - m_Controls->thres_2->setVisible(true); - m_Controls->m_RegFrame->setVisible(false); + m_Controls->m_DstarFrame->setVisible(true); + m_Controls->m_NeglSiFrame->setVisible(true); + m_Controls->m_NeglBframe->setVisible(false); + m_Controls->m_IterationsFrame->setVisible(false); + m_Controls->m_LambdaFrame->setVisible(false); break; case 2: - m_Controls->dstar->setVisible(false); - m_Controls->thres->setVisible(true); - m_Controls->thres_2->setVisible(true); - m_Controls->m_RegFrame->setVisible(false); + m_Controls->m_DstarFrame->setVisible(false); + m_Controls->m_NeglSiFrame->setVisible(true); + m_Controls->m_NeglBframe->setVisible(true); + m_Controls->m_IterationsFrame->setVisible(false); + m_Controls->m_LambdaFrame->setVisible(false); break; case 3: - m_Controls->dstar->setVisible(false); - m_Controls->thres->setVisible(true); - m_Controls->thres_2->setVisible(true); - m_Controls->m_RegFrame->setVisible(false); + m_Controls->m_DstarFrame->setVisible(false); + m_Controls->m_NeglSiFrame->setVisible(true); + m_Controls->m_NeglBframe->setVisible(true); + m_Controls->m_IterationsFrame->setVisible(false); + m_Controls->m_LambdaFrame->setVisible(false); break; case 4: - m_Controls->dstar->setVisible(false); - m_Controls->thres->setVisible(true); - m_Controls->thres_2->setVisible(true); - m_Controls->m_RegFrame->setVisible(true); + m_Controls->m_DstarFrame->setVisible(false); + m_Controls->m_NeglSiFrame->setVisible(false); + m_Controls->m_NeglBframe->setVisible(false); + m_Controls->m_IterationsFrame->setVisible(false); + m_Controls->m_LambdaFrame->setVisible(false); break; } itk::StartEvent dummy; OnSliceChanged(dummy); } void QmitkIVIMView::DStarSlider (int val) { QString sval = QString::number(val/1000.0); m_Controls->m_DStarLabel->setText(sval); itk::StartEvent dummy; OnSliceChanged(dummy); } void QmitkIVIMView::BThreshSlider (int val) { QString sval = QString::number(val*5.0); m_Controls->m_BThreshLabel->setText(sval); itk::StartEvent dummy; OnSliceChanged(dummy); } void QmitkIVIMView::S0ThreshSlider (int val) { QString sval = QString::number(val*0.5); m_Controls->m_S0ThreshLabel->setText(sval); itk::StartEvent dummy; OnSliceChanged(dummy); } void QmitkIVIMView::NumItsSlider (int val) { QString sval = QString::number(val); m_Controls->m_NumItsLabel->setText(sval); itk::StartEvent dummy; OnSliceChanged(dummy); } void QmitkIVIMView::LambdaSlider (int val) { QString sval = QString::number(val*.00001); m_Controls->m_LambdaLabel->setText(sval); itk::StartEvent dummy; OnSliceChanged(dummy); } void QmitkIVIMView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget1->GetSliceNavigationController(); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkIVIMView::OnSliceChanged ); m_SliceObserverTag1 = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget2->GetSliceNavigationController(); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkIVIMView::OnSliceChanged ); m_SliceObserverTag2 = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget3->GetSliceNavigationController(); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkIVIMView::OnSliceChanged ); m_SliceObserverTag3 = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } } void QmitkIVIMView::StdMultiWidgetNotAvailable() { { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget1->GetSliceNavigationController(); slicer->RemoveObserver( m_SliceObserverTag1 ); } { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget2->GetSliceNavigationController(); slicer->RemoveObserver( m_SliceObserverTag2 ); } { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget3->GetSliceNavigationController(); slicer->RemoveObserver( m_SliceObserverTag3 ); } m_MultiWidget = NULL; } void QmitkIVIMView::OnSelectionChanged( std::vector nodes ) { bool foundOneDiffusionImage = false; + m_Controls->m_DiffusionImageLabel->setText("-"); + m_Controls->m_MaskImageLabel->setText("-"); + m_MaskImageNode = NULL; + m_DiffusionImageNode = NULL; // iterate all selected objects, adjust warning visibility - for( std::vector::iterator it = nodes.begin(); - it != nodes.end(); - ++it ) + for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; - if( node.IsNotNull() ) + if( node.IsNotNull() && dynamic_cast(node->GetData()) ) { - mitk::DiffusionImage* img = dynamic_cast*>(node->GetData()); - if( img ) + if( dynamic_cast*>(node->GetData()) ) { - if(!foundOneDiffusionImage ) - { - foundOneDiffusionImage = true; - } - else + m_DiffusionImageNode = node; + foundOneDiffusionImage = true; + m_Controls->m_DiffusionImageLabel->setText(node->GetName().c_str()); + } + else + { + bool isBinary = false; + node->GetPropertyValue("binary", isBinary); + if (isBinary) { - foundOneDiffusionImage = false; + m_MaskImageNode = node; + m_Controls->m_MaskImageLabel->setText(node->GetName().c_str()); } } } } -// m_Controls->m_ADCBValues->setVisible( foundOneDiffusionImage && m_Controls->m_CheckADC->isChecked() ); - m_Controls->m_ButtonStart->setEnabled( foundOneDiffusionImage ); m_Controls->m_ButtonAutoThres->setEnabled( foundOneDiffusionImage ); m_Controls->m_ControlsFrame->setEnabled( foundOneDiffusionImage ); m_Controls->m_BottomControlsFrame->setEnabled( foundOneDiffusionImage ); itk::StartEvent dummy; OnSliceChanged(dummy); } void QmitkIVIMView::AutoThreshold() { std::vector nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; if (!nodes.front()) { // Nothing selected. Inform the user and return QMessageBox::information( NULL, "Template", "Please load and select a diffusion image before starting image processing."); return; } typedef mitk::DiffusionImage DiffImgType; DiffImgType* dimg = dynamic_cast(nodes.front()->GetData()); if (!dimg) { // Nothing selected. Inform the user and return QMessageBox::information( NULL, "Template", "No valid diffusion image was found."); return; } // find bzero index int index = -1; DiffImgType::GradientDirectionContainerType::Pointer directions = dimg->GetDirections(); for(DiffImgType::GradientDirectionContainerType::ConstIterator it = directions->Begin(); it != directions->End(); ++it) { index++; DiffImgType::GradientDirectionType g = it.Value(); if(g[0] == 0 && g[1] == 0 && g[2] == 0 ) break; } typedef itk::VectorImage VecImgType; VecImgType::Pointer vecimg = dimg->GetVectorImage(); int vecLength = vecimg->GetVectorLength(); index = index > vecLength-1 ? vecLength-1 : index; MITK_INFO << "Performing Histogram Analysis on Channel" << index; typedef itk::Image ImgType; ImgType::Pointer img = ImgType::New(); mitk::CastToItkImage(dimg, img); itk::ImageRegionIterator itw (img, img->GetLargestPossibleRegion() ); itw = itw.Begin(); itk::ImageRegionConstIterator itr (vecimg, vecimg->GetLargestPossibleRegion() ); itr = itr.Begin(); while(!itr.IsAtEnd()) { itw.Set(itr.Get().GetElement(index)); ++itr; ++itw; } typedef itk::Statistics::ScalarImageToHistogramGenerator< ImgType > HistogramGeneratorType; typedef HistogramGeneratorType::HistogramType HistogramType; HistogramGeneratorType::Pointer histogramGenerator = HistogramGeneratorType::New(); histogramGenerator->SetInput( img ); histogramGenerator->SetMarginalScale( 10 ); // Defines y-margin width of histogram histogramGenerator->SetNumberOfBins( 100 ); // CT range [-1024, +2048] --> bin size 4 values histogramGenerator->SetHistogramMin( dimg->GetScalarValueMin() ); histogramGenerator->SetHistogramMax( dimg->GetScalarValueMax() * .5 ); histogramGenerator->Compute(); HistogramType::ConstIterator iter = histogramGenerator->GetOutput()->Begin(); float maxFreq = 0; float maxValue = 0; while ( iter != histogramGenerator->GetOutput()->End() ) { if(iter.GetFrequency() > maxFreq) { maxFreq = iter.GetFrequency(); maxValue = iter.GetMeasurementVector()[0]; } ++iter; } maxValue *= 2; int sliderPos = maxValue * 2; m_Controls->m_S0ThreshSlider->setValue(sliderPos); S0ThreshSlider(sliderPos); } void QmitkIVIMView::FittIVIMStart() { std::vector nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; if (!nodes.front()) { // Nothing selected. Inform the user and return QMessageBox::information( NULL, "Template", "Please load and select a diffusion image before starting image processing."); return; } mitk::DiffusionImage* img = dynamic_cast*>( nodes.front()->GetData()); if (!img) { // Nothing selected. Inform the user and return QMessageBox::information( NULL, "Template", "No valid diffusion image was found."); return; } typedef itk::VectorImage VecImgType; VecImgType::Pointer vecimg = img->GetVectorImage(); OutImgType::IndexType dummy; FittIVIM(vecimg, img->GetDirections(), img->GetB_Value(), true, dummy); OutputToDatastorage(nodes); } void QmitkIVIMView::OnSliceChanged(const itk::EventObject& /*e*/) { - if(!m_Controls) + m_Controls->m_Warning->setVisible(false); + if(!m_Controls || m_DiffusionImageNode.IsNull()) return; - m_Controls->m_Warning->setVisible(false); - if(!m_Active) - return; +// if(!m_Active) +// return; m_Controls->m_VisualizeResultsWidget->setVisible(false); if(!m_Controls->m_DisplayResultsCheckbox->isChecked()) return; - std::vector nodes = this->GetDataManagerSelection(); - if (nodes.empty()) return; - if (!nodes.front()) return; - if (nodes.size()>2) return; - - mitk::DiffusionImage* diffusionImg = 0; - mitk::DiffusionImage* img1 = - dynamic_cast*>( - nodes.front()->GetData()); - - mitk::DiffusionImage* img2 = 0; - mitk::Image* maskImg = 0; - if(nodes.size()>1) - { - if(img1) - { - if(strcmp(nodes.at(1)->GetData()->GetNameOfClass(), "Image") != 0 ) - return; - - maskImg = dynamic_cast( - nodes.at(1)->GetData()); - - diffusionImg = img1; - } - else - { - if(strcmp(nodes.front()->GetData()->GetNameOfClass(), "Image") != 0 ) - return; - - maskImg = dynamic_cast( - nodes.front()->GetData()); - - diffusionImg = dynamic_cast*>( - nodes.at(1)->GetData()); - } - } - else - { - diffusionImg = img1; - } - if (nodes.size()==2 && (!diffusionImg || !maskImg || m_Controls->m_MethodCombo->currentIndex() == 4 )) return; - if (nodes.size()==1 && !diffusionImg) return; + mitk::DiffusionImage::Pointer diffusionImg = dynamic_cast*>(m_DiffusionImageNode->GetData()); + mitk::Image::Pointer maskImg = NULL; + if (m_MaskImageNode.IsNotNull()) + maskImg = dynamic_cast(m_MaskImageNode->GetData()); IVIMFilterType::GradientDirectionContainerType::ConstIterator gdcit = diffusionImg->GetDirections()->Begin(); bool foundB0 = false; while( gdcit != diffusionImg->GetDirections()->End() ) { if(gdcit.Value().one_norm() <= 0.0) foundB0 = true; ++gdcit; } if(!foundB0) { m_Controls->m_Warning->setText(QString("No baseline (non diffusion-weighted) image found.. aborting:(")); m_Controls->m_Warning->setVisible(true); } else { m_Controls->m_Warning->setVisible(false); } if (!m_MultiWidget) return; m_Controls->m_VisualizeResultsWidget->setVisible(true); typedef itk::VectorImage VecImgType; VecImgType::Pointer vecimg = (VecImgType*)diffusionImg->GetVectorImage().GetPointer(); VecImgType::Pointer roiImage = VecImgType::New(); - if(maskImg == 0) + if(maskImg.IsNull()) { int roisize = 0; if(m_Controls->m_MethodCombo->currentIndex() == 4) roisize = 5; mitk::Point3D pos = m_MultiWidget->GetCrossPosition(); VecImgType::IndexType crosspos; diffusionImg->GetTimeSlicedGeometry()->WorldToIndex(pos, crosspos); VecImgType::IndexType index; index[0] = crosspos[0] - roisize; index[0] = index[0] < 0 ? 0 : index[0]; index[1] = crosspos[1] - roisize; index[1] = index[1] < 0 ? 0 : index[1]; index[2] = crosspos[2] - roisize; index[2] = index[2] < 0 ? 0 : index[2]; VecImgType::SizeType size; size[0] = roisize*2+1; size[1] = roisize*2+1; size[2] = roisize*2+1; VecImgType::SizeType maxSize = vecimg->GetLargestPossibleRegion().GetSize(); size[0] = index[0]+size[0] > maxSize[0] ? maxSize[0]-index[0] : size[0]; size[1] = index[1]+size[1] > maxSize[1] ? maxSize[1]-index[1] : size[1]; size[2] = index[2]+size[2] > maxSize[2] ? maxSize[2]-index[2] : size[2]; VecImgType::RegionType region; region.SetSize( size ); region.SetIndex( index ); vecimg->SetRequestedRegion( region ); VecImgType::IndexType newstart; newstart.Fill(0); VecImgType::RegionType newregion; newregion.SetSize( size ); newregion.SetIndex( newstart ); roiImage->CopyInformation( vecimg ); roiImage->SetRegions( newregion ); roiImage->SetOrigin( pos ); roiImage->Allocate(); roiImage->SetPixel(newstart, vecimg->GetPixel(index)); FittIVIM(roiImage, diffusionImg->GetDirections(), diffusionImg->GetB_Value(), false, crosspos); } else { typedef itk::Image MaskImgType; MaskImgType::Pointer maskItk; CastToItkImage( maskImg, maskItk ); mitk::Point3D pos; pos[0] = 0; pos[1] = 0; pos[2] = 0; VecImgType::IndexType index; index[0] = 0; index[1] = 0; index[2] = 0; VecImgType::SizeType size; size[0] = 1; size[1] = 1; size[2] = 1; VecImgType::RegionType region; region.SetSize( size ); region.SetIndex( index ); vecimg->SetRequestedRegion( region ); // iterators over output and input itk::ImageRegionConstIteratorWithIndex vecit(vecimg, vecimg->GetLargestPossibleRegion()); itk::VariableLengthVector avg(vecimg->GetVectorLength()); avg.Fill(0); float numPixels = 0; while ( ! vecit.IsAtEnd() ) { VecImgType::PointType point; vecimg->TransformIndexToPhysicalPoint(vecit.GetIndex(), point); MaskImgType::IndexType index; maskItk->TransformPhysicalPointToIndex(point, index); if(maskItk->GetPixel(index) != 0) { avg += vecit.Get(); numPixels += 1.0; } // update iterators ++vecit; } avg /= numPixels; m_Controls->m_Warning->setText(QString("Averaging ")+QString::number((int)numPixels)+QString(" voxels!")); m_Controls->m_Warning->setVisible(true); roiImage->CopyInformation( vecimg ); roiImage->SetRegions( region ); roiImage->SetOrigin( pos ); roiImage->Allocate(); roiImage->SetPixel(index, avg); FittIVIM(roiImage, diffusionImg->GetDirections(), diffusionImg->GetB_Value(), false, index); } vecimg->SetRegions( vecimg->GetLargestPossibleRegion() ); m_Controls->m_VisualizeResultsWidget->SetParameters(m_Snap); } void QmitkIVIMView::FittIVIM(itk::VectorImage* vecimg, DirContainerType* dirs, float bval, bool multivoxel, OutImgType::IndexType &crosspos) { IVIMFilterType::Pointer filter = IVIMFilterType::New(); filter->SetInput(vecimg); filter->SetGradientDirections(dirs); filter->SetBValue(bval); switch(m_Controls->m_MethodCombo->currentIndex()) { case 0: filter->SetMethod(IVIMFilterType::IVIM_FIT_ALL); filter->SetS0Thres(m_Controls->m_S0ThreshLabel->text().toDouble()); break; case 1: filter->SetMethod(IVIMFilterType::IVIM_DSTAR_FIX); filter->SetDStar(m_Controls->m_DStarLabel->text().toDouble()); filter->SetS0Thres(m_Controls->m_S0ThreshLabel->text().toDouble()); break; case 2: filter->SetMethod(IVIMFilterType::IVIM_D_THEN_DSTAR); filter->SetBThres(m_Controls->m_BThreshLabel->text().toDouble()); filter->SetS0Thres(m_Controls->m_S0ThreshLabel->text().toDouble()); filter->SetFitDStar(m_Controls->m_CheckDStar->isChecked()); break; case 3: filter->SetMethod(IVIMFilterType::IVIM_LINEAR_D_THEN_F); filter->SetBThres(m_Controls->m_BThreshLabel->text().toDouble()); filter->SetS0Thres(m_Controls->m_S0ThreshLabel->text().toDouble()); filter->SetFitDStar(m_Controls->m_CheckDStar->isChecked()); break; case 4: filter->SetMethod(IVIMFilterType::IVIM_REGULARIZED); filter->SetBThres(m_Controls->m_BThreshLabel->text().toDouble()); filter->SetS0Thres(m_Controls->m_S0ThreshLabel->text().toDouble()); filter->SetNumberIterations(m_Controls->m_NumItsLabel->text().toInt()); filter->SetLambda(m_Controls->m_LambdaLabel->text().toDouble()); filter->SetFitDStar(m_Controls->m_CheckDStar->isChecked()); break; } if(!multivoxel) { filter->SetFitDStar(true); } filter->SetNumberOfThreads(1); filter->SetVerbose(multivoxel); filter->SetCrossPosition(crosspos); filter->Update(); m_Snap = filter->GetSnapshot(); m_DStarMap = filter->GetOutput(2); m_DMap = filter->GetOutput(1); m_fMap = filter->GetOutput(0); } void QmitkIVIMView::OutputToDatastorage(std::vector nodes) { // Outputs to Datastorage QString basename(nodes.front()->GetName().c_str()); if(m_Controls->m_CheckDStar->isChecked()) { mitk::Image::Pointer dstarimage = mitk::Image::New(); dstarimage->InitializeByItk(m_DStarMap.GetPointer()); dstarimage->SetVolume(m_DStarMap->GetBufferPointer()); QString newname2 = basename; newname2 = newname2.append("_DStarMap%1").arg(m_Controls->m_MethodCombo->currentIndex()); mitk::DataNode::Pointer node2=mitk::DataNode::New(); node2->SetData( dstarimage ); node2->SetName(newname2.toAscii()); GetDefaultDataStorage()->Add(node2); } if(m_Controls->m_CheckD->isChecked()) { mitk::Image::Pointer dimage = mitk::Image::New(); dimage->InitializeByItk(m_DMap.GetPointer()); dimage->SetVolume(m_DMap->GetBufferPointer()); QString newname1 = basename; newname1 = newname1.append("_DMap%1").arg(m_Controls->m_MethodCombo->currentIndex()); mitk::DataNode::Pointer node1=mitk::DataNode::New(); node1->SetData( dimage ); node1->SetName(newname1.toAscii()); GetDefaultDataStorage()->Add(node1); } if(m_Controls->m_Checkf->isChecked()) { mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk(m_fMap.GetPointer()); image->SetVolume(m_fMap->GetBufferPointer()); QString newname0 = basename; newname0 = newname0.append("_fMap%1").arg(m_Controls->m_MethodCombo->currentIndex()); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); node->SetName(newname0.toAscii()); GetDefaultDataStorage()->Add(node); } m_MultiWidget->RequestUpdate(); } void QmitkIVIMView::ChooseMethod() { m_Controls->m_MethodCombo->setVisible(m_Controls->m_ChooseMethod->isChecked()); } void QmitkIVIMView::ClipboardCurveButtonClicked() { if(true) { QString clipboard("Measurement Points\n"); for ( int i=0; isetText( clipboard, QClipboard::Clipboard ); } else { QApplication::clipboard()->clear(); } } void QmitkIVIMView::ClipboardStatisticsButtonClicked() { if ( true ) { QString clipboard( "f \t D \t D* \n" ); clipboard = clipboard.append( "%L1 \t %L2 \t %L3" ) .arg( m_Snap.currentF, 0, 'f', 10 ) .arg( m_Snap.currentD, 0, 'f', 10 ) .arg( m_Snap.currentDStar, 0, 'f', 10 ) ; QApplication::clipboard()->setText( clipboard, QClipboard::Clipboard ); } else { QApplication::clipboard()->clear(); } } void QmitkIVIMView::Activated() { m_Active = true; } void QmitkIVIMView::Deactivated() { m_Active = false; } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkIVIMView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkIVIMView.h index f3e407e1c1..c06c29d8de 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkIVIMView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkIVIMView.h @@ -1,112 +1,115 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2010-03-31 16:40:27 +0200 (Mi, 31 Mrz 2010) $ -Version: $Revision: 21975 $ +Version: $Revision: 21975 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _QMITKIVIMVIEW_H_INCLUDED #define _QMITKIVIMVIEW_H_INCLUDED #include #include #include "ui_QmitkIVIMViewControls.h" #include "itkVectorImage.h" #include "itkImage.h" #include "mitkDiffusionImage.h" #include "itkDiffusionIntravoxelIncoherentMotionReconstructionImageFilter.h" /*! \brief QmitkIVIMView \warning This application module is not yet documented. Use "svn blame/praise/annotate" and ask the author to provide basic documentation. \sa QmitkFunctionality \ingroup Functionalities */ class QmitkIVIMView : public QmitkFunctionality -{ +{ // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; QmitkIVIMView(); virtual ~QmitkIVIMView(); typedef mitk::DiffusionImage::GradientDirectionContainerType DirContainerType; typedef itk::DiffusionIntravoxelIncoherentMotionReconstructionImageFilter IVIMFilterType; typedef itk::Image OutImgType; virtual void CreateQtPartControl(QWidget *parent); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); void OnSliceChanged(const itk::EventObject& e); void OutputToDatastorage(std::vector nodes); void FittIVIM(itk::VectorImage* vecimg, DirContainerType* dirs, float bval, bool multivoxel, OutImgType::IndexType &crosspos); void Activated(); void Deactivated(); protected slots: - + /// \brief Called when the user clicks the GUI button void FittIVIMStart(); void AutoThreshold(); void MethodCombo(int val); void Checkbox(); void DStarSlider(int val); void BThreshSlider(int val); void S0ThreshSlider(int val); void NumItsSlider(int val); void LambdaSlider(int val); void ChooseMethod(); void ClipboardStatisticsButtonClicked(); void ClipboardCurveButtonClicked(); protected: /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged( std::vector nodes ); Ui::QmitkIVIMViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; int m_SliceObserverTag1; int m_SliceObserverTag2; int m_SliceObserverTag3; OutImgType::Pointer m_DStarMap; OutImgType::Pointer m_DMap; OutImgType::Pointer m_fMap; IVIMFilterType::IVIMSnapshot m_Snap; + mitk::DataNode::Pointer m_DiffusionImageNode; + mitk::DataNode::Pointer m_MaskImageNode; + bool m_Active; }; #endif // _QMITKIVIMVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkIVIMViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkIVIMViewControls.ui index 49d20b570b..0967ddb8ae 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkIVIMViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkIVIMViewControls.ui @@ -1,631 +1,803 @@ QmitkIVIMViewControls 0 0 - 307 - 790 + 360 + 922 0 0 QmitkTemplate - 0 + 9 Intra Voxel Incoherent Motion Estimation 0 9 0 0 - - - QFrame::NoFrame + + + Data - - QFrame::Raised + + + + + Diffusion Image: + + + + + + + - + + + + + + + Mask Image: + + + + + + + - + + + + + + + + + + Parameters - - - 0 + + + 9 - - + + QFrame::NoFrame QFrame::Raised - + 0 - + + 0 + + 80 16777215 D* - + 100 60 Qt::Horizontal - + 51 16777215 200 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - - + + QFrame::NoFrame QFrame::Raised - + 0 - + + 0 + + 80 16777215 neglect b< - + 250 34 Qt::Horizontal - + 51 16777215 46.5 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - - + + QFrame::NoFrame - QFrame::Raised + QFrame::Plain - + 0 - + + 0 + + 80 16777215 neglect Si< - + 100 0 Qt::Horizontal - - + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 30 + 16777215 + + + + TextLabel + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 15 + 16777215 + + + + * + + + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + - 30 + 80 16777215 - TextLabel + #iterations - - + + + + 100 + + + 10 + + + Qt::Horizontal + + + + + - 15 + 30 16777215 - * + TextLabel + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + QFrame::NoFrame QFrame::Raised - + 0 - - - - QFrame::NoFrame + + 0 + + + + + + 80 + 16777215 + - - QFrame::Raised + + lambda - - - 0 - - - - - - 80 - 16777215 - - - - #iterations - - - - - - - 100 - - - 10 - - - Qt::Horizontal - - - - - - - - 30 - 16777215 - - - - TextLabel - - - - - - + + + + 1000 + + + 10 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + QFrame::NoFrame QFrame::Raised - + 0 - - - - - 80 - 16777215 - - - - lambda - - - - - - - 1000 - - - 10 - - - Qt::Horizontal - - - - + + 0 + + + + + 0 + 0 + + 30 16777215 TextLabel + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - + 15 16777215 * + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + + QFrame::NoFrame QFrame::Raised 0 80 0 Output Images f true D false D* false Generate Output Images color: rgb(255, 0, 0); font: 75 14pt "Ubuntu"; - Bla bla bla + warning display Qt::RichText display voxel-wise results true QFrame::StyledPanel QFrame::Raised 0 0 true 0 0 0 400 0 QFrame::NoFrame QFrame::Raised 0 QFrame::NoFrame QFrame::Raised 0 QFrame::NoFrame QFrame::Raised 0 Curve to Clipboard Values to Clipboard Choose Method 2 3 Param. Fit Fit D & f with fixed D* value Fit D & f (high b), then fit D* Linearly fit D & f (high b), then fit D* Regularized Qt::Vertical QSizePolicy::Expanding 20 220 QmitkIVIMWidget QWidget
QmitkIVIMWidget.h
1
diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkODFDetailsView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkODFDetailsView.cpp index 8cda17993f..c13bd7be11 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkODFDetailsView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkODFDetailsView.cpp @@ -1,308 +1,323 @@ /*========================================================================= Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkODFDetailsView.h" #include #include #include #include #include #include #include #include #include #include #include const std::string QmitkODFDetailsView::VIEW_ID = "org.mitk.views.odfdetails"; QmitkODFDetailsView::QmitkODFDetailsView() : QmitkFunctionality() , m_Controls( 0 ) , m_MultiWidget( NULL ) , m_OdfNormalization(0) - , m_SelectedNode(NULL) + , m_ImageNode(NULL) { m_VtkActor = vtkActor::New(); m_VtkMapper = vtkPolyDataMapper::New(); m_Renderer = vtkRenderer::New(); m_VtkRenderWindow = vtkRenderWindow::New(); m_RenderWindowInteractor = vtkRenderWindowInteractor::New(); m_Camera = vtkCamera::New(); m_VtkRenderWindow->SetSize(300,300); } QmitkODFDetailsView::~QmitkODFDetailsView() { QmitkStdMultiWidget* MultiWidget = this->GetActiveStdMultiWidget(false); if(MultiWidget) { //unregister observers when view is destroyed if( MultiWidget->mitkWidget1 != NULL && m_SliceObserverTag1 != 0) { mitk::SliceNavigationController* slicer = MultiWidget->mitkWidget1->GetSliceNavigationController(); slicer->RemoveObserver( m_SliceObserverTag1 ); } if( MultiWidget->mitkWidget2 != NULL && m_SliceObserverTag2 != 0) { mitk::SliceNavigationController* slicer = MultiWidget->mitkWidget2->GetSliceNavigationController(); slicer->RemoveObserver( m_SliceObserverTag2 ); } if( MultiWidget->mitkWidget3!= NULL && m_SliceObserverTag3 != 0) { mitk::SliceNavigationController* slicer = MultiWidget->mitkWidget3->GetSliceNavigationController(); slicer->RemoveObserver( m_SliceObserverTag3 ); } } } void QmitkODFDetailsView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkODFDetailsViewControls; m_Controls->setupUi( parent ); m_Controls->m_OdfBox->setVisible(false); m_Controls->m_ODFRenderWidget->setVisible(false); } } void QmitkODFDetailsView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget1->GetSliceNavigationController(); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkODFDetailsView::OnSliceChanged ); m_SliceObserverTag1 = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget2->GetSliceNavigationController(); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkODFDetailsView::OnSliceChanged ); m_SliceObserverTag2 = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget3->GetSliceNavigationController(); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkODFDetailsView::OnSliceChanged ); m_SliceObserverTag3 = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } - } void QmitkODFDetailsView::StdMultiWidgetNotAvailable() { { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget1->GetSliceNavigationController(); slicer->RemoveObserver( m_SliceObserverTag1 ); } { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget2->GetSliceNavigationController(); slicer->RemoveObserver( m_SliceObserverTag2 ); } { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget3->GetSliceNavigationController(); slicer->RemoveObserver( m_SliceObserverTag3 ); } m_MultiWidget = NULL; } void QmitkODFDetailsView::OnSelectionChanged( std::vector nodes ) { - if (m_SelectedNode.IsNotNull()) - m_SelectedNode->RemoveObserver( m_PropertyObserverTag ); + if (m_ImageNode.IsNotNull()) + m_ImageNode->RemoveObserver( m_PropertyObserverTag ); + + m_ImageNode = NULL; + m_Controls->m_InputImageLabel->setText("-"); + // iterate selection + for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) + { + mitk::DataNode::Pointer node = *it; + + if( node.IsNotNull() && (dynamic_cast(node->GetData()) || dynamic_cast(node->GetData())) ) + { + m_Controls->m_InputImageLabel->setText(node->GetName().c_str()); + m_ImageNode = node; + } + } + UpdateOdf(); - if (m_SelectedNode.IsNotNull()) + if (m_ImageNode.IsNotNull()) { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkODFDetailsView::OnSliceChanged ); - m_PropertyObserverTag = m_SelectedNode->AddObserver( itk::ModifiedEvent(), command ); + m_PropertyObserverTag = m_ImageNode->AddObserver( itk::ModifiedEvent(), command ); } } void QmitkODFDetailsView::UpdateOdf() { + try { m_Values.clear(); - std::vector nodes = this->GetDataManagerSelection(); - if (nodes.empty()) return; - if (!nodes.front()) return; - m_SelectedNode = nodes.front(); - - mitk::Image::Pointer img = dynamic_cast(nodes.front()->GetData()); - - if (!img) return; - if (!m_MultiWidget) return; + m_Controls->m_OverviewBox->setVisible(true); + if (m_ImageNode.IsNull() || !m_MultiWidget) + { + m_Controls->m_ODFRenderWidget->setVisible(false); + m_Controls->m_OdfBox->setVisible(false); + m_Controls->m_OverviewBox->setVisible(false); + return; + } // ODF Normalization Property - mitk::OdfNormalizationMethodProperty* nmp = dynamic_cast(nodes.front()->GetProperty( "Normalization" )); + mitk::OdfNormalizationMethodProperty* nmp = dynamic_cast(m_ImageNode->GetProperty( "Normalization" )); if(nmp) m_OdfNormalization = nmp->GetNormalization(); m_TemplateOdf = itk::OrientationDistributionFunction::GetBaseMesh(); m_OdfTransform = vtkSmartPointer::New(); m_OdfTransform->Identity(); m_OdfVals = vtkSmartPointer::New(); m_OdfSource = vtkSmartPointer::New(); itk::OrientationDistributionFunction odf; mitk::Point3D world = m_MultiWidget->GetCrossPosition(); mitk::Point3D index; + mitk::Image::Pointer img = dynamic_cast(m_ImageNode->GetData()); img->GetTimeSlicedGeometry()->WorldToIndex(world, index); float sum = 0; - float max = itk::NumericTraits::min(); + float max = itk::NumericTraits::NonpositiveMin(); float min = itk::NumericTraits::max(); QString values; QString overviewText; + // check if dynamic_cast successfull and if the crosshair position is inside of the geometry of the ODF data // otherwise possible crash for a scenario with multiple nodes - if (dynamic_cast(nodes.front()->GetData()) && ( nodes.front()->GetData()->GetGeometry()->IsInside(world) ) ) + if (dynamic_cast(m_ImageNode->GetData()) && ( m_ImageNode->GetData()->GetGeometry()->IsInside(world) ) ) { m_Controls->m_ODFRenderWidget->setVisible(true); m_Controls->m_OdfBox->setVisible(true); OdfVectorImgType::Pointer itkQBallImage = OdfVectorImgType::New(); - mitk::CastToItkImage(dynamic_cast(nodes.front()->GetData()), itkQBallImage); - + mitk::CastToItkImage(dynamic_cast(m_ImageNode->GetData()), itkQBallImage); OdfVectorImgType::IndexType ind; ind[0] = (int)(index[0]+0.5); ind[1] = (int)(index[1]+0.5); ind[2] = (int)(index[2]+0.5); OdfVectorImgType::PixelType pixel = itkQBallImage->GetPixel(ind); for (int i=0; imax) max = val; if (val pd = odf.GetDirection(odf.GetPrincipleDiffusionDirection()); overviewText += "Main Diffusion:\n "+QString::number(pd[0])+"\n "+QString::number(pd[1])+"\n "+QString::number(pd[2])+"\n"; m_Controls->m_OdfValuesTextEdit->setText(values); } - else if (dynamic_cast(nodes.front()->GetData())) + else if (dynamic_cast(m_ImageNode->GetData())) { m_Controls->m_ODFRenderWidget->setVisible(true); m_Controls->m_OdfBox->setVisible(false); TensorImageType::Pointer itkQBallImage = TensorImageType::New(); - mitk::CastToItkImage(dynamic_cast(nodes.front()->GetData()), itkQBallImage); + mitk::CastToItkImage(dynamic_cast(m_ImageNode->GetData()), itkQBallImage); TensorImageType::IndexType ind; ind[0] = (int)(index[0]+0.5); ind[1] = (int)(index[1]+0.5); ind[2] = (int)(index[2]+0.5); TensorImageType::PixelType pixel = itkQBallImage->GetPixel(ind); float tensorelems[6] = { (float)pixel[0], (float)pixel[1], (float)pixel[2], (float)pixel[3], (float)pixel[4], (float)pixel[5], }; itk::DiffusionTensor3D tensor(tensorelems); odf.InitFromTensor(tensor); /** Array of eigen-values. */ typedef itk::FixedArray EigenValuesArrayType; /** Matrix of eigen-vectors. */ typedef itk::Matrix MatrixType; typedef itk::Matrix EigenVectorsMatrixType; EigenValuesArrayType eigenValues; EigenVectorsMatrixType eigenVectors; QString pos = QString::number(ind[0])+", "+QString::number(ind[1])+", "+QString::number(ind[2]); overviewText += "Coordinates: "+pos+"\n"; overviewText += "FA: "+QString::number(tensor.GetFractionalAnisotropy())+"\n"; overviewText += "RA: "+QString::number(tensor.GetRelativeAnisotropy())+"\n"; overviewText += "Trace: "+QString::number(tensor.GetTrace())+"\n"; tensor.ComputeEigenAnalysis(eigenValues,eigenVectors); overviewText += "Eigenvalues:\n "+QString::number(eigenValues[2])+"\n "+QString::number(eigenValues[1])+"\n "+QString::number(eigenValues[0])+"\n"; overviewText += "Main Diffusion:\n "+QString::number(eigenVectors[0][0])+"\n "+QString::number(eigenVectors[1][0])+"\n "+QString::number(eigenVectors[2][0])+"\n"; overviewText += "Values:\n "+QString::number(tensorelems[0])+"\n "+QString::number(tensorelems[1])+"\n "+QString::number(tensorelems[2])+"\n "+QString::number(tensorelems[3])+"\n "+QString::number(tensorelems[4])+"\n "+QString::number(tensorelems[5])+"\n "+"\n"; } else { m_Controls->m_ODFRenderWidget->setVisible(false); m_Controls->m_OdfBox->setVisible(false); - overviewText += "Please select a Q-Ball or tensor image\n"; + overviewText += "Please reinit image geometry.\n"; } m_Controls->m_ODFDetailsWidget->SetParameters(odf); switch(m_OdfNormalization) { case 0: odf = odf.MinMaxNormalize(); break; case 1: odf = odf.MaxNormalize(); break; case 2: odf = odf.MaxNormalize(); break; default: odf = odf.MinMaxNormalize(); } m_Controls->m_ODFRenderWidget->GenerateODF(odf); m_Controls->m_OverviewTextEdit->setText(overviewText.toStdString().c_str()); } catch(...) { QMessageBox::critical(0, "Error", "Data could not be analyzed. The image might be corrupted."); } } void QmitkODFDetailsView::OnSliceChanged(const itk::EventObject& /*e*/) { UpdateOdf(); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkODFDetailsView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkODFDetailsView.h index a935492329..37162c8af2 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkODFDetailsView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkODFDetailsView.h @@ -1,114 +1,115 @@ /*========================================================================= Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _QMITKQmitkODFDetailsView_H_INCLUDED #define _QMITKQmitkODFDetailsView_H_INCLUDED #include #include #include "ui_QmitkODFDetailsViewControls.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \brief QmitkODFDetailsView \warning This application module is not yet documented. Use "svn blame/praise/annotate" and ask the author to provide basic documentation. \sa QmitkFunctionality \ingroup Functionalities */ class QmitkODFDetailsView : public QmitkFunctionality { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; QmitkODFDetailsView(); QmitkODFDetailsView(const QmitkODFDetailsView& other) { Q_UNUSED(other) throw std::runtime_error("Copy constructor not implemented"); } virtual ~QmitkODFDetailsView(); typedef float TOdfPixelType; typedef itk::Vector OdfVectorType; typedef itk::Image OdfVectorImgType; typedef itk::DiffusionTensor3D< TOdfPixelType > TensorPixelType; typedef itk::Image< TensorPixelType, 3 > TensorImageType; virtual void CreateQtPartControl(QWidget *parent); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); void OnSliceChanged(const itk::EventObject& e); protected slots: protected: /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged( std::vector nodes ); void UpdateOdf(); Ui::QmitkODFDetailsViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; int m_SliceObserverTag1; int m_SliceObserverTag2; int m_SliceObserverTag3; int m_PropertyObserverTag; vtkPolyData* m_TemplateOdf; vtkSmartPointer m_OdfTransform; vtkSmartPointer m_OdfVals; vtkSmartPointer m_OdfSource; vtkActor* m_VtkActor; vtkPolyDataMapper* m_VtkMapper; vtkRenderer* m_Renderer; vtkRenderWindow* m_VtkRenderWindow; vtkRenderWindowInteractor* m_RenderWindowInteractor; vtkCamera* m_Camera; - mitk::DataNode::Pointer m_SelectedNode; std::vector m_Values; int m_OdfNormalization; + + mitk::DataNode::Pointer m_ImageNode; }; #endif // _QmitkODFDetailsView_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkODFDetailsViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkODFDetailsViewControls.ui index 895117622a..b3705a1f4d 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkODFDetailsViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkODFDetailsViewControls.ui @@ -1,191 +1,205 @@ QmitkODFDetailsViewControls 0 0 351 734 0 0 QmitkTemplate - 0 + 6 - - 4 - - - 0 - - - 0 - - - 0 + + 9 - + + + Data + + + + + + Tensor/Q-Ball Image: + + + + + + + - + + + + + + + + Overview 0 9 0 0 0 0 0 0 true 0 0 200 200 0 0 0 0 QFrame::NoFrame QFrame::Raised 0 0 ODF Values true 0 0 0 200 Qt::Vertical QSizePolicy::Expanding 20 220 QmitkODFDetailsWidget QWidget
QmitkODFDetailsWidget.h
1
QmitkODFRenderWidget QWidget
QmitkODFRenderWidget.h
1
diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPartialVolumeAnalysisView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPartialVolumeAnalysisView.cpp index 52ea734ec8..560405779d 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPartialVolumeAnalysisView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPartialVolumeAnalysisView.cpp @@ -1,2169 +1,2139 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2009-05-22 11:00:35 +0200 (Fr, 22 Mai 2009) $ Version: $Revision: 10185 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "QmitkPartialVolumeAnalysisView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "QmitkStdMultiWidget.h" +#include "QmitkStdMultiWidgetEditor.h" #include "QmitkSliderNavigatorWidget.h" +#include #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateOr.h" #include "mitkImageTimeSelector.h" #include "mitkProperties.h" #include "mitkProgressBar.h" #include "mitkImageCast.h" #include "mitkImageToItk.h" #include "mitkITKImageImport.h" #include "mitkDataNodeObject.h" #include "mitkNodePredicateData.h" #include "mitkPlanarFigureInteractor.h" #include "mitkGlobalInteraction.h" #include "mitkTensorImage.h" #include "mitkPlanarCircle.h" #include "mitkPlanarRectangle.h" #include "mitkPlanarPolygon.h" #include "mitkPartialVolumeAnalysisClusteringCalculator.h" #include "mitkDiffusionImage.h" #include #include "itkTensorDerivedMeasurementsFilter.h" #include "itkDiffusionTensor3D.h" #include "itkCartesianToPolarVectorImageFilter.h" #include "itkPolarToCartesianVectorImageFilter.h" #include "itkBinaryThresholdImageFilter.h" #include "itkMaskImageFilter.h" #include "itkCastImageFilter.h" #include "itkImageMomentsCalculator.h" #include #include #include #include #define _USE_MATH_DEFINES #include #define PVA_PI M_PI const std::string QmitkPartialVolumeAnalysisView::VIEW_ID = -"org.mitk.views.partialvolumeanalysisview"; + "org.mitk.views.partialvolumeanalysisview"; class QmitkRequestStatisticsUpdateEvent : public QEvent { public: enum Type { StatisticsUpdateRequest = QEvent::MaxUser - 1025 - }; + }; QmitkRequestStatisticsUpdateEvent() : QEvent( (QEvent::Type) StatisticsUpdateRequest ) {}; }; typedef itk::Image ImageType; typedef itk::Image FloatImageType; typedef itk::Image, 3> VectorImageType; inline bool my_isnan(float x) { volatile float d = x; if(d!=d) return true; if(d==d) return false; return d != d; } QmitkPartialVolumeAnalysisView::QmitkPartialVolumeAnalysisView(QObject * /*parent*/, const char * /*name*/) - : QmitkFunctionality(), - m_Controls( NULL ), - m_TimeStepperAdapter( NULL ), - m_MeasurementInfoRenderer(0), - m_MeasurementInfoAnnotation(0), - m_SelectedImageNodes( ), - m_SelectedImage( NULL ), - m_SelectedMaskNode( NULL ), - m_SelectedImageMask( NULL ), - m_SelectedPlanarFigureNodes(0), - m_SelectedPlanarFigure( NULL ), - m_IsTensorImage(false), - m_FAImage(0), - m_RDImage(0), - m_ADImage(0), - m_MDImage(0), - m_CAImage(0), - // m_DirectionImage(0), - m_DirectionComp1Image(0), - m_DirectionComp2Image(0), - m_AngularErrorImage(0), - m_SelectedRenderWindow(NULL), - m_LastRenderWindow(NULL), - m_ImageObserverTag( -1 ), - m_ImageMaskObserverTag( -1 ), - m_PlanarFigureObserverTag( -1 ), - m_CurrentStatisticsValid( false ), - m_StatisticsUpdatePending( false ), - m_GaussianSigmaChangedSliding(false), - m_NumberBinsSliding(false), - m_UpsamplingChangedSliding(false), - m_ClusteringResult(NULL), - m_EllipseCounter(0), - m_RectangleCounter(0), - m_PolygonCounter(0), - m_CurrentFigureNodeInitialized(false), - m_QuantifyClass(2), - m_IconTexOFF(new QIcon(":/QmitkPartialVolumeAnalysisView/texIntOFFIcon.png")), - m_IconTexON(new QIcon(":/QmitkPartialVolumeAnalysisView/texIntONIcon.png")), - m_TexIsOn(true) + : //QmitkFunctionality(), + m_Controls( NULL ), + m_TimeStepperAdapter( NULL ), + m_MeasurementInfoRenderer(0), + m_MeasurementInfoAnnotation(0), + m_SelectedImageNodes( ), + m_SelectedImage( NULL ), + m_SelectedMaskNode( NULL ), + m_SelectedImageMask( NULL ), + m_SelectedPlanarFigureNodes(0), + m_SelectedPlanarFigure( NULL ), + m_IsTensorImage(false), + m_FAImage(0), + m_RDImage(0), + m_ADImage(0), + m_MDImage(0), + m_CAImage(0), + // m_DirectionImage(0), + m_DirectionComp1Image(0), + m_DirectionComp2Image(0), + m_AngularErrorImage(0), + m_SelectedRenderWindow(NULL), + m_LastRenderWindow(NULL), + m_ImageObserverTag( -1 ), + m_ImageMaskObserverTag( -1 ), + m_PlanarFigureObserverTag( -1 ), + m_CurrentStatisticsValid( false ), + m_StatisticsUpdatePending( false ), + m_GaussianSigmaChangedSliding(false), + m_NumberBinsSliding(false), + m_UpsamplingChangedSliding(false), + m_ClusteringResult(NULL), + m_EllipseCounter(0), + m_RectangleCounter(0), + m_PolygonCounter(0), + m_CurrentFigureNodeInitialized(false), + m_QuantifyClass(2), + m_IconTexOFF(new QIcon(":/QmitkPartialVolumeAnalysisView/texIntOFFIcon.png")), + m_IconTexON(new QIcon(":/QmitkPartialVolumeAnalysisView/texIntONIcon.png")), + m_TexIsOn(true), + m_Visible(false) { } QmitkPartialVolumeAnalysisView::~QmitkPartialVolumeAnalysisView() { if ( m_SelectedImage.IsNotNull() ) m_SelectedImage->RemoveObserver( m_ImageObserverTag ); if ( m_SelectedImageMask.IsNotNull() ) m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); if ( m_SelectedPlanarFigure.IsNotNull() ) { m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag ); m_SelectedPlanarFigure->RemoveObserver( m_InitializedObserverTag ); } - this->GetDefaultDataStorage()->AddNodeEvent -= mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeAddedInDataStorage ); + this->GetDataStorage()->AddNodeEvent -= mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeAddedInDataStorage ); m_SelectedPlanarFigureNodes->NodeChanged.RemoveListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeChanged ) ); m_SelectedPlanarFigureNodes->NodeRemoved.RemoveListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeRemoved ) ); m_SelectedPlanarFigureNodes->PropertyChanged.RemoveListener( mitk::MessageDelegate2( this, &QmitkPartialVolumeAnalysisView::PropertyChanged ) ); m_SelectedImageNodes->NodeChanged.RemoveListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeChanged ) ); m_SelectedImageNodes->NodeRemoved.RemoveListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeRemoved ) ); m_SelectedImageNodes->PropertyChanged.RemoveListener( mitk::MessageDelegate2( this, &QmitkPartialVolumeAnalysisView::PropertyChanged ) ); } void QmitkPartialVolumeAnalysisView::CreateQtPartControl(QWidget *parent) { if (m_Controls == NULL) { m_Controls = new Ui::QmitkPartialVolumeAnalysisViewControls; m_Controls->setupUi(parent); this->CreateConnections(); - - m_Controls->m_ErrorMessageLabel->hide(); } SetHistogramVisibility(); m_Controls->m_TextureIntON->setIcon(*m_IconTexON); m_Controls->m_SimilarAnglesFrame->setVisible(false); m_Controls->m_SimilarAnglesLabel->setVisible(false); vtkTextProperty *textProp = vtkTextProperty::New(); textProp->SetColor(1.0, 1.0, 1.0); m_MeasurementInfoAnnotation = vtkCornerAnnotation::New(); m_MeasurementInfoAnnotation->SetMaximumFontSize(12); m_MeasurementInfoAnnotation->SetTextProperty(textProp); m_MeasurementInfoRenderer = vtkRenderer::New(); m_MeasurementInfoRenderer->AddActor(m_MeasurementInfoAnnotation); - m_SelectedPlanarFigureNodes = mitk::DataStorageSelection::New(this->GetDefaultDataStorage(), false); + m_SelectedPlanarFigureNodes = mitk::DataStorageSelection::New(this->GetDataStorage(), false); m_SelectedPlanarFigureNodes->NodeChanged.AddListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeChanged ) ); m_SelectedPlanarFigureNodes->NodeRemoved.AddListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeRemoved ) ); m_SelectedPlanarFigureNodes->PropertyChanged.AddListener( mitk::MessageDelegate2( this, &QmitkPartialVolumeAnalysisView::PropertyChanged ) ); - m_SelectedImageNodes = mitk::DataStorageSelection::New(this->GetDefaultDataStorage(), false); + m_SelectedImageNodes = mitk::DataStorageSelection::New(this->GetDataStorage(), false); m_SelectedImageNodes->PropertyChanged.AddListener( mitk::MessageDelegate2( this, &QmitkPartialVolumeAnalysisView::PropertyChanged ) ); m_SelectedImageNodes->NodeChanged.AddListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeChanged ) ); m_SelectedImageNodes->NodeRemoved.AddListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeRemoved ) ); - this->GetDefaultDataStorage()->AddNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeAddedInDataStorage ) ); + this->GetDataStorage()->AddNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeAddedInDataStorage ) ); Select(NULL,true,true); SetAdvancedVisibility(); } void QmitkPartialVolumeAnalysisView::SetHistogramVisibility() { m_Controls->m_HistogramWidget->setVisible(m_Controls->m_DisplayHistogramCheckbox->isChecked()); } void QmitkPartialVolumeAnalysisView::SetAdvancedVisibility() { m_Controls->frame_7->setVisible(m_Controls->m_AdvancedCheckbox->isChecked()); } void QmitkPartialVolumeAnalysisView::CreateConnections() { if ( m_Controls ) { connect( m_Controls->m_DisplayHistogramCheckbox, SIGNAL( clicked() ) , this, SLOT( SetHistogramVisibility() ) ); connect( m_Controls->m_AdvancedCheckbox, SIGNAL( clicked() ) , this, SLOT( SetAdvancedVisibility() ) ); connect( m_Controls->m_NumberBinsSlider, SIGNAL( sliderReleased () ), this, SLOT( NumberBinsReleasedSlider( ) ) ); connect( m_Controls->m_UpsamplingSlider, SIGNAL( sliderReleased( ) ), this, SLOT( UpsamplingReleasedSlider( ) ) ); connect( m_Controls->m_GaussianSigmaSlider, SIGNAL( sliderReleased( ) ), this, SLOT( GaussianSigmaReleasedSlider( ) ) ); connect( m_Controls->m_SimilarAnglesSlider, SIGNAL( sliderReleased( ) ), this, SLOT( SimilarAnglesReleasedSlider( ) ) ); connect( m_Controls->m_NumberBinsSlider, SIGNAL( valueChanged (int) ), this, SLOT( NumberBinsChangedSlider( int ) ) ); connect( m_Controls->m_UpsamplingSlider, SIGNAL( valueChanged( int ) ), this, SLOT( UpsamplingChangedSlider( int ) ) ); connect( m_Controls->m_GaussianSigmaSlider, SIGNAL( valueChanged( int ) ), this, SLOT( GaussianSigmaChangedSlider( int ) ) ); connect( m_Controls->m_SimilarAnglesSlider, SIGNAL( valueChanged( int ) ), this, SLOT( SimilarAnglesChangedSlider(int) ) ); connect( m_Controls->m_OpacitySlider, SIGNAL( valueChanged( int ) ), this, SLOT( OpacityChangedSlider(int) ) ); connect( (QObject*)(m_Controls->m_ButtonCopyHistogramToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(ToClipBoard())); connect( m_Controls->m_CircleButton, SIGNAL( clicked() ) , this, SLOT( ActionDrawEllipseTriggered() ) ); connect( m_Controls->m_RectangleButton, SIGNAL( clicked() ) , this, SLOT( ActionDrawRectangleTriggered() ) ); connect( m_Controls->m_PolygonButton, SIGNAL( clicked() ) , this, SLOT( ActionDrawPolygonTriggered() ) ); connect( m_Controls->m_GreenRadio, SIGNAL( clicked(bool) ) , this, SLOT( GreenRadio(bool) ) ); connect( m_Controls->m_PartialVolumeRadio, SIGNAL( clicked(bool) ) , this, SLOT( PartialVolumeRadio(bool) ) ); connect( m_Controls->m_BlueRadio, SIGNAL( clicked(bool) ) , this, SLOT( BlueRadio(bool) ) ); connect( m_Controls->m_AllRadio, SIGNAL( clicked(bool) ) , this, SLOT( AllRadio(bool) ) ); connect( m_Controls->m_EstimateCircle, SIGNAL( clicked() ) , this, SLOT( EstimateCircle() ) ); connect( (QObject*)(m_Controls->m_TextureIntON), SIGNAL(clicked()), this, SLOT(TextIntON()) ); connect( m_Controls->m_ExportClusteringResultsButton, SIGNAL(clicked()), this, SLOT(ExportClusteringResults())); } } void QmitkPartialVolumeAnalysisView::ExportClusteringResults() { if (m_ClusteringResult.IsNull() || m_SelectedImage.IsNull()) return; - mitk::DiffusionImage::Pointer diffusionImage = NULL; - if (dynamic_cast*>(m_SelectedImage.GetPointer())) - diffusionImage = dynamic_cast*>(m_SelectedImage.GetPointer()); - else - return; + mitk::Geometry3D* geometry = m_SelectedImage->GetGeometry(); + + itk::Image< short, 3>::Pointer referenceImage = itk::Image< short, 3>::New(); + + mitk::Vector3D newSpacing = geometry->GetSpacing(); + mitk::Point3D newOrigin = geometry->GetOrigin(); + mitk::Geometry3D::BoundsArrayType bounds = geometry->GetBounds(); + newOrigin[0] += bounds.GetElement(0); + newOrigin[1] += bounds.GetElement(2); + newOrigin[2] += bounds.GetElement(4); + itk::Matrix newDirection; + itk::ImageRegion<3> imageRegion; + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + newDirection[j][i] = geometry->GetMatrixColumn(i)[j]/newSpacing[j]; + imageRegion.SetSize(0, geometry->GetExtent(0)); + imageRegion.SetSize(1, geometry->GetExtent(1)); + imageRegion.SetSize(2, geometry->GetExtent(2)); + + // apply new image parameters + referenceImage->SetSpacing( newSpacing ); + referenceImage->SetOrigin( newOrigin ); + referenceImage->SetDirection( newDirection ); + referenceImage->SetRegions( imageRegion ); + referenceImage->Allocate(); typedef itk::Image< float, 3 > OutType; mitk::Image::Pointer mitkInImage = dynamic_cast(m_ClusteringResult->GetData()); - typedef itk::ExtractChannelFromRgbaImageFilter< OutType > ExtractionFilterType; typedef itk::Image< itk::RGBAPixel, 3 > ItkRgbaImageType; typedef mitk::ImageToItk< ItkRgbaImageType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(mitkInImage); caster->Update(); ItkRgbaImageType::Pointer itkInImage = caster->GetOutput(); + typedef itk::ExtractChannelFromRgbaImageFilter< itk::Image< short, 3>, OutType > ExtractionFilterType; ExtractionFilterType::Pointer filter = ExtractionFilterType::New(); filter->SetInput(itkInImage); filter->SetChannel(ExtractionFilterType::ALPHA); - filter->SetReferenceImage(diffusionImage->GetVectorImage()); + filter->SetReferenceImage(referenceImage); filter->Update(); - OutType::Pointer outImg = filter->GetOutput(); -// mitk::Geometry3D* geometry = m_SelectedImage->GetGeometry(); -// itk::Matrix direction; -// itk::ImageRegion<3> imageRegion; -// for (int i=0; i<3; i++) -// for (int j=0; j<3; j++) -// direction[j][i] = geometry->GetMatrixColumn(i)[j]; -// imageRegion.SetSize(0, geometry->GetExtent(0)); -// imageRegion.SetSize(1, geometry->GetExtent(1)); -// imageRegion.SetSize(2, geometry->GetExtent(2)); -// typedef itk::ResampleImageFilter ResamplerType; -// ResamplerType::Pointer resampler = ResamplerType::New(); -// resampler->SetOutputSpacing( geometry->GetSpacing() ); -// resampler->SetOutputOrigin( geometry->GetOrigin() ); -// resampler->SetOutputDirection( direction ); -// resampler->SetSize( imageRegion.GetSize() ); -// resampler->SetInput( outImg ); -// const itk::Transform* trafo = geometry->GetParametricTransform(); -// itk::Transform::InverseTransformBasePointer t = trafo->GetInverseTransform(); -// itk::Transform* invTrafo = dynamic_cast*>(t.GetPointer()); -// resampler->SetTransform(invTrafo); - -//// double gausssigma = 10; -//// double sigma[3]; -//// for( unsigned int d = 0; d < 3; d++ ) -//// sigma[d] = gausssigma * geometry->GetSpacing()[d]; -//// double alpha = 2.0; -//// typedef itk::GaussianInterpolateImageFunction InterpolatorType; -//// typedef itk::NearestNeighborInterpolateImageFunction InterpolatorType; -//// typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); -//// interpolator->SetInputImage( outImg ); -//// interpolator->SetParameters( sigma, alpha ); -//// resampler->SetInterpolator( interpolator ); - -// resampler->Update(); -// outImg = resampler->GetOutput(); - - mitk::Image::Pointer img = mitk::Image::New(); - img->InitializeByItk(outImg.GetPointer()); + mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // init data node mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); node->SetName("Clustering Result"); GetDataStorage()->Add(node); } void QmitkPartialVolumeAnalysisView::EstimateCircle() { typedef itk::Image SegImageType; SegImageType::Pointer mask_itk = SegImageType::New(); typedef mitk::ImageToItk CastType; CastType::Pointer caster = CastType::New(); caster->SetInput(m_SelectedImageMask); caster->Update(); typedef itk::ImageMomentsCalculator< SegImageType > MomentsType; MomentsType::Pointer momentsCalc = MomentsType::New(); momentsCalc->SetImage(caster->GetOutput()); momentsCalc->Compute(); MomentsType::VectorType cog = momentsCalc->GetCenterOfGravity(); MomentsType::MatrixType axes = momentsCalc->GetPrincipalAxes(); MomentsType::VectorType moments = momentsCalc->GetPrincipalMoments(); // moments-coord conversion // third coordinate min oder max? // max-min = extent MomentsType::AffineTransformPointer trafo = momentsCalc->GetPhysicalAxesToPrincipalAxesTransform(); itk::ImageRegionIterator itimage(caster->GetOutput(), caster->GetOutput()->GetLargestPossibleRegion()); itimage = itimage.Begin(); double max = -9999999999.0; double min = 9999999999.0; while( !itimage.IsAtEnd() ) { if(itimage.Get()) { ImageType::IndexType index = itimage.GetIndex(); itk::Point point; caster->GetOutput()->TransformIndexToPhysicalPoint(index,point); itk::Point newPoint; newPoint = trafo->TransformPoint(point); if(newPoint[2]max) max = newPoint[2]; } ++itimage; } double extent = max - min; MITK_INFO << "EXTENT = " << extent; mitk::Point3D origin; mitk::Vector3D right, bottom, normal; double factor = 1000.0; mitk::FillVector3D(origin, cog[0]-factor*axes[1][0]-factor*axes[2][0], cog[1]-factor*axes[1][1]-factor*axes[2][1], cog[2]-factor*axes[1][2]-factor*axes[2][2]); // mitk::FillVector3D(normal, axis[0][0],axis[0][1],axis[0][2]); mitk::FillVector3D(bottom, 2*factor*axes[1][0], 2*factor*axes[1][1], 2*factor*axes[1][2]); mitk::FillVector3D(right, 2*factor*axes[2][0], 2*factor*axes[2][1], 2*factor*axes[2][2]); mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(right.Get_vnl_vector(), bottom.Get_vnl_vector()); planegeometry->SetOrigin(origin); double len1 = sqrt(axes[1][0]*axes[1][0] + axes[1][1]*axes[1][1] + axes[1][2]*axes[1][2]); double len2 = sqrt(axes[2][0]*axes[2][0] + axes[2][1]*axes[2][1] + axes[2][2]*axes[2][2]); mitk::Point2D point1; point1[0] = factor*len1; point1[1] = factor*len2; mitk::Point2D point2; point2[0] = factor*len1+extent*.5; point2[1] = factor*len2; mitk::PlanarCircle::Pointer circle = mitk::PlanarCircle::New(); circle->SetGeometry2D(planegeometry); circle->PlaceFigure( point1 ); circle->SetControlPoint(0,point1); circle->SetControlPoint(1,point2); //circle->SetCurrentControlPoint( point2 ); mitk::PlanarFigure::PolyLineType polyline = circle->GetPolyLine( 0 ); MITK_INFO << "SIZE of planar figure polyline: " << polyline.size(); AddFigureToDataStorage(circle, "Circle"); } -void QmitkPartialVolumeAnalysisView::StdMultiWidgetAvailable( QmitkStdMultiWidget& stdMultiWidget ) -{ - QmitkFunctionality::StdMultiWidgetAvailable(stdMultiWidget); -} - bool QmitkPartialVolumeAnalysisView::AssertDrawingIsPossible(bool checked) { if (m_SelectedImageNodes->GetNode().IsNull()) { checked = false; - this->HandleException("Please select an image!", this->m_Parent, true); + this->HandleException("Please select an image!", dynamic_cast(this->parent()), true); return false; } //this->GetActiveStdMultiWidget()->SetWidgetPlanesVisibility(false); return checked; } void QmitkPartialVolumeAnalysisView::ActionDrawEllipseTriggered() { bool checked = m_Controls->m_CircleButton->isChecked(); if(!this->AssertDrawingIsPossible(checked)) return; mitk::PlanarCircle::Pointer figure = mitk::PlanarCircle::New(); this->AddFigureToDataStorage(figure, QString("Circle%1").arg(++m_EllipseCounter)); MITK_INFO << "PlanarCircle created ..."; } void QmitkPartialVolumeAnalysisView::ActionDrawRectangleTriggered() { bool checked = m_Controls->m_RectangleButton->isChecked(); if(!this->AssertDrawingIsPossible(checked)) return; mitk::PlanarRectangle::Pointer figure = mitk::PlanarRectangle::New(); this->AddFigureToDataStorage(figure, QString("Rectangle%1").arg(++m_RectangleCounter)); MITK_INFO << "PlanarRectangle created ..."; } void QmitkPartialVolumeAnalysisView::ActionDrawPolygonTriggered() { bool checked = m_Controls->m_PolygonButton->isChecked(); if(!this->AssertDrawingIsPossible(checked)) return; mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); figure->ClosedOn(); this->AddFigureToDataStorage(figure, QString("Polygon%1").arg(++m_PolygonCounter)); MITK_INFO << "PlanarPolygon created ..."; } void QmitkPartialVolumeAnalysisView::AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name, - const char *propertyKey, mitk::BaseProperty *property ) + const char *propertyKey, mitk::BaseProperty *property ) { mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetName(name.toStdString()); newNode->SetData(figure); // Add custom property, if available if ( (propertyKey != NULL) && (property != NULL) ) { newNode->AddProperty( propertyKey, property ); } // figure drawn on the topmost layer / image this->GetDataStorage()->Add(newNode, m_SelectedImageNodes->GetNode() ); - std::vector selectedNodes = GetDataManagerSelection(); + QList selectedNodes = this->GetDataManagerSelection(); for(unsigned int i = 0; i < selectedNodes.size(); i++) { selectedNodes[i]->SetSelected(false); } - selectedNodes = m_SelectedPlanarFigureNodes->GetNodes(); - for(unsigned int i = 0; i < selectedNodes.size(); i++) + + std::vector selectedPFNodes = m_SelectedPlanarFigureNodes->GetNodes(); + for(unsigned int i = 0; i < selectedPFNodes.size(); i++) { - selectedNodes[i]->SetSelected(false); + selectedPFNodes[i]->SetSelected(false); } newNode->SetSelected(true); Select(newNode); } void QmitkPartialVolumeAnalysisView::PlanarFigureInitialized() { if(m_SelectedPlanarFigureNodes->GetNode().IsNull()) return; m_CurrentFigureNodeInitialized = true; this->Select(m_SelectedPlanarFigureNodes->GetNode()); m_Controls->m_CircleButton->setChecked(false); m_Controls->m_RectangleButton->setChecked(false); m_Controls->m_PolygonButton->setChecked(false); //this->GetActiveStdMultiWidget()->SetWidgetPlanesVisibility(true); this->RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::PlanarFigureFocus(mitk::DataNode* node) { mitk::PlanarFigure* _PlanarFigure = 0; _PlanarFigure = dynamic_cast (node->GetData()); if (_PlanarFigure) { FindRenderWindow(node); const mitk::PlaneGeometry * _PlaneGeometry = dynamic_cast (_PlanarFigure->GetGeometry2D()); // make node visible if (m_SelectedRenderWindow) { mitk::Point3D centerP = _PlaneGeometry->GetOrigin(); m_SelectedRenderWindow->GetSliceNavigationController()->ReorientSlices( - centerP, _PlaneGeometry->GetNormal()); + centerP, _PlaneGeometry->GetNormal()); m_SelectedRenderWindow->GetSliceNavigationController()->SelectSliceByPoint( - centerP); + centerP); } } } void QmitkPartialVolumeAnalysisView::FindRenderWindow(mitk::DataNode* node) { - - if(node) + if (node && dynamic_cast (node->GetData())) { - mitk::PlanarFigure* _PlanarFigure = 0; - _PlanarFigure = dynamic_cast (node->GetData()); + m_SelectedRenderWindow = 0; + bool PlanarFigureInitializedWindow = false; - if (_PlanarFigure) + foreach(QmitkRenderWindow * window, this->GetRenderWindowPart()->GetRenderWindows().values()) { - m_SelectedRenderWindow = 0; - QmitkRenderWindow* RenderWindow1 = - this->GetActiveStdMultiWidget()->GetRenderWindow1(); - QmitkRenderWindow* RenderWindow2 = - this->GetActiveStdMultiWidget()->GetRenderWindow2(); - QmitkRenderWindow* RenderWindow3 = - this->GetActiveStdMultiWidget()->GetRenderWindow3(); - QmitkRenderWindow* RenderWindow4 = - this->GetActiveStdMultiWidget()->GetRenderWindow4(); - - bool PlanarFigureInitializedWindow = false; - - // find initialized renderwindow - if (node->GetBoolProperty("PlanarFigureInitializedWindow", - PlanarFigureInitializedWindow, RenderWindow1->GetRenderer())) + if (!m_SelectedRenderWindow && node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, window->GetRenderer())) { - m_SelectedRenderWindow = RenderWindow1; + m_SelectedRenderWindow = window; } - - if (!m_SelectedRenderWindow && node->GetBoolProperty( - "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, - RenderWindow2->GetRenderer())) - { - m_SelectedRenderWindow = RenderWindow2; - } - - if (!m_SelectedRenderWindow && node->GetBoolProperty( - "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, - RenderWindow3->GetRenderer())) - { - m_SelectedRenderWindow = RenderWindow3; - } - - if (!m_SelectedRenderWindow && node->GetBoolProperty( - "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, - RenderWindow4->GetRenderer())) - { - m_SelectedRenderWindow = RenderWindow4; - } - } } } -void QmitkPartialVolumeAnalysisView::OnSelectionChanged( std::vector nodes ) + +void QmitkPartialVolumeAnalysisView::OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList &nodes) { - if ( !this->IsVisible() ) + if ( !m_Visible ) { return; } if ( nodes.empty() || nodes.size() > 1 ) { // Nothing to do: invalidate image, clear statistics, histogram, and GUI return; } Select(nodes.front()); } void QmitkPartialVolumeAnalysisView::Select( mitk::DataNode::Pointer node, bool clearMaskOnFirstArgNULL, bool clearImageOnFirstArgNULL ) { // Clear any unreferenced images this->RemoveOrphanImages(); bool somethingChanged = false; if(node.IsNull()) { somethingChanged = true; if(clearMaskOnFirstArgNULL) { if ( (m_SelectedImageMask.IsNotNull()) && (m_ImageMaskObserverTag >= 0) ) { m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); m_ImageMaskObserverTag = -1; } if ( (m_SelectedPlanarFigure.IsNotNull()) && (m_PlanarFigureObserverTag >= 0) ) { m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag ); m_PlanarFigureObserverTag = -1; } if ( (m_SelectedPlanarFigure.IsNotNull()) && (m_InitializedObserverTag >= 0) ) { m_SelectedPlanarFigure->RemoveObserver( m_InitializedObserverTag ); m_InitializedObserverTag = -1; } m_SelectedPlanarFigure = NULL; m_SelectedPlanarFigureNodes->RemoveAllNodes(); m_CurrentFigureNodeInitialized = false; m_SelectedRenderWindow = 0; m_SelectedMaskNode = NULL; m_SelectedImageMask = NULL; } if(clearImageOnFirstArgNULL) { if ( (m_SelectedImage.IsNotNull()) && (m_ImageObserverTag >= 0) ) { m_SelectedImage->RemoveObserver( m_ImageObserverTag ); m_ImageObserverTag = -1; } m_SelectedImageNodes->RemoveAllNodes(); m_SelectedImage = NULL; m_IsTensorImage = false; m_FAImage = NULL; m_RDImage = NULL; m_ADImage = NULL; m_MDImage = NULL; m_CAImage = NULL; m_DirectionComp1Image = NULL; m_DirectionComp2Image = NULL; m_AngularErrorImage = NULL; m_Controls->m_SimilarAnglesFrame->setVisible(false); m_Controls->m_SimilarAnglesLabel->setVisible(false); } } else { typedef itk::SimpleMemberCommand< QmitkPartialVolumeAnalysisView > ITKCommandType; ITKCommandType::Pointer changeListener; changeListener = ITKCommandType::New(); changeListener->SetCallbackFunction( this, &QmitkPartialVolumeAnalysisView::RequestStatisticsUpdate ); // Get selected element mitk::TensorImage *selectedTensorImage = dynamic_cast< mitk::TensorImage * >( node->GetData() ); mitk::Image *selectedImage = dynamic_cast< mitk::Image * >( node->GetData() ); mitk::PlanarFigure *selectedPlanar = dynamic_cast< mitk::PlanarFigure * >( node->GetData() ); bool isMask = false; bool isImage = false; bool isPlanar = false; bool isTensorImage = false; if (selectedTensorImage != NULL) { isTensorImage = true; } else if(selectedImage != NULL) { node->GetPropertyValue("binary", isMask); isImage = !isMask; } else if ( (selectedPlanar != NULL) ) { isPlanar = true; } // image if(isImage && selectedImage->GetDimension()==3) { if(selectedImage != m_SelectedImage.GetPointer()) { somethingChanged = true; if ( (m_SelectedImage.IsNotNull()) && (m_ImageObserverTag >= 0) ) { m_SelectedImage->RemoveObserver( m_ImageObserverTag ); m_ImageObserverTag = -1; } *m_SelectedImageNodes = node; m_SelectedImage = selectedImage; m_IsTensorImage = false; m_FAImage = NULL; m_RDImage = NULL; m_ADImage = NULL; m_MDImage = NULL; m_CAImage = NULL; m_DirectionComp1Image = NULL; m_DirectionComp2Image = NULL; m_AngularErrorImage = NULL; // Add change listeners to selected objects m_ImageObserverTag = m_SelectedImage->AddObserver( - itk::ModifiedEvent(), changeListener ); + itk::ModifiedEvent(), changeListener ); m_Controls->m_SimilarAnglesFrame->setVisible(false); m_Controls->m_SimilarAnglesLabel->setVisible(false); m_Controls->m_SelectedImageLabel->setText( m_SelectedImageNodes->GetNode()->GetName().c_str() ); } } //planar if(isPlanar) { if(selectedPlanar != m_SelectedPlanarFigure.GetPointer()) { MITK_INFO << "Planar selection changed"; somethingChanged = true; // Possibly previous change listeners if ( (m_SelectedPlanarFigure.IsNotNull()) && (m_PlanarFigureObserverTag >= 0) ) { m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag ); m_PlanarFigureObserverTag = -1; } if ( (m_SelectedPlanarFigure.IsNotNull()) && (m_InitializedObserverTag >= 0) ) { m_SelectedPlanarFigure->RemoveObserver( m_InitializedObserverTag ); m_InitializedObserverTag = -1; } m_SelectedPlanarFigure = selectedPlanar; *m_SelectedPlanarFigureNodes = node; m_CurrentFigureNodeInitialized = selectedPlanar->IsPlaced(); m_SelectedMaskNode = NULL; m_SelectedImageMask = NULL; m_PlanarFigureObserverTag = m_SelectedPlanarFigure->AddObserver( - mitk::EndInteractionPlanarFigureEvent(), changeListener ); + mitk::EndInteractionPlanarFigureEvent(), changeListener ); if(!m_CurrentFigureNodeInitialized) { typedef itk::SimpleMemberCommand< QmitkPartialVolumeAnalysisView > ITKCommandType; ITKCommandType::Pointer initializationCommand; initializationCommand = ITKCommandType::New(); // set the callback function of the member command initializationCommand->SetCallbackFunction( this, &QmitkPartialVolumeAnalysisView::PlanarFigureInitialized ); // add an observer m_InitializedObserverTag = selectedPlanar->AddObserver( mitk::EndPlacementPlanarFigureEvent(), initializationCommand ); } m_Controls->m_SelectedMaskLabel->setText( m_SelectedPlanarFigureNodes->GetNode()->GetName().c_str() ); PlanarFigureFocus(node); } } //mask if(isMask && selectedImage->GetDimension()==3) { if(selectedImage != m_SelectedImage.GetPointer()) { somethingChanged = true; if ( (m_SelectedImageMask.IsNotNull()) && (m_ImageMaskObserverTag >= 0) ) { m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); m_ImageMaskObserverTag = -1; } m_SelectedMaskNode = node; m_SelectedImageMask = selectedImage; m_SelectedPlanarFigure = NULL; m_SelectedPlanarFigureNodes->RemoveAllNodes(); m_ImageMaskObserverTag = m_SelectedImageMask->AddObserver( - itk::ModifiedEvent(), changeListener ); + itk::ModifiedEvent(), changeListener ); m_Controls->m_SelectedMaskLabel->setText( m_SelectedMaskNode->GetName().c_str() ); } } //tensor image if(isTensorImage && selectedTensorImage->GetDimension()==3) { if(selectedImage != m_SelectedImage.GetPointer()) { somethingChanged = true; if ( (m_SelectedImage.IsNotNull()) && (m_ImageObserverTag >= 0) ) { m_SelectedImage->RemoveObserver( m_ImageObserverTag ); m_ImageObserverTag = -1; } *m_SelectedImageNodes = node; m_SelectedImage = selectedImage; m_IsTensorImage = true; ExtractTensorImages(selectedImage); // Add change listeners to selected objects m_ImageObserverTag = m_SelectedImage->AddObserver( - itk::ModifiedEvent(), changeListener ); + itk::ModifiedEvent(), changeListener ); m_Controls->m_SimilarAnglesFrame->setVisible(true); m_Controls->m_SimilarAnglesLabel->setVisible(true); m_Controls->m_SelectedImageLabel->setText( m_SelectedImageNodes->GetNode()->GetName().c_str() ); } } } if(somethingChanged) { this->SetMeasurementInfoToRenderWindow(""); if(m_SelectedPlanarFigure.IsNull() && m_SelectedImageMask.IsNull() ) { - m_Controls->m_SelectedMaskLabel->setText( "None" ); + m_Controls->m_SelectedMaskLabel->setText( "-" ); m_Controls->m_ResampleOptionsFrame->setEnabled(false); m_Controls->m_HistogramWidget->setEnabled(false); m_Controls->m_ClassSelector->setEnabled(false); m_Controls->m_DisplayHistogramCheckbox->setEnabled(false); m_Controls->m_AdvancedCheckbox->setEnabled(false); m_Controls->frame_7->setEnabled(false); } else { m_Controls->m_ResampleOptionsFrame->setEnabled(true); m_Controls->m_HistogramWidget->setEnabled(true); m_Controls->m_ClassSelector->setEnabled(true); m_Controls->m_DisplayHistogramCheckbox->setEnabled(true); m_Controls->m_AdvancedCheckbox->setEnabled(true); m_Controls->frame_7->setEnabled(true); } // Clear statistics / histogram GUI if nothing is selected if ( m_SelectedImage.IsNull() ) { m_Controls->m_PlanarFigureButtonsFrame->setEnabled(false); m_Controls->m_OpacityFrame->setEnabled(false); - m_Controls->m_SelectedImageLabel->setText( "None" ); + m_Controls->m_SelectedImageLabel->setText( "-" ); } else { m_Controls->m_PlanarFigureButtonsFrame->setEnabled(true); m_Controls->m_OpacityFrame->setEnabled(true); } if( m_SelectedImage.IsNull() - || (m_SelectedPlanarFigure.IsNull() && m_SelectedImageMask.IsNull()) ) - { + || (m_SelectedPlanarFigure.IsNull() && m_SelectedImageMask.IsNull()) ) + { m_Controls->m_HistogramWidget->ClearItemModel(); m_CurrentStatisticsValid = false; - m_Controls->m_ErrorMessageLabel->hide(); } else { this->RequestStatisticsUpdate(); } } } void QmitkPartialVolumeAnalysisView::ShowClusteringResults() { typedef itk::Image MaskImageType; mitk::Image::Pointer mask = 0; MaskImageType::Pointer itkmask = 0; if(m_IsTensorImage && m_Controls->m_SimilarAnglesSlider->value() != 0) { typedef itk::Image AngularErrorImageType; typedef mitk::ImageToItk CastType; CastType::Pointer caster = CastType::New(); caster->SetInput(m_AngularErrorImage); caster->Update(); typedef itk::BinaryThresholdImageFilter< AngularErrorImageType, MaskImageType > ThreshType; ThreshType::Pointer thresh = ThreshType::New(); thresh->SetUpperThreshold((90-m_Controls->m_SimilarAnglesSlider->value())*(PVA_PI/180.0)); thresh->SetInsideValue(1.0); thresh->SetInput(caster->GetOutput()); thresh->Update(); itkmask = thresh->GetOutput(); mask = mitk::Image::New(); mask->InitializeByItk(itkmask.GetPointer()); mask->SetVolume(itkmask->GetBufferPointer()); // GetDefaultDataStorage()->Remove(m_newnode); // m_newnode = mitk::DataNode::New(); // m_newnode->SetData(mask); // m_newnode->SetName("masking node"); // m_newnode->SetIntProperty( "layer", 1002 ); // GetDefaultDataStorage()->Add(m_newnode, m_SelectedImageNodes->GetNode()); } mitk::Image::Pointer clusteredImage; ClusteringType::Pointer clusterer = ClusteringType::New(); if(m_QuantifyClass==3) { if(m_IsTensorImage) { double *green_fa, *green_rd, *green_ad, *green_md; //double *greengray_fa, *greengray_rd, *greengray_ad, *greengray_md; double *gray_fa, *gray_rd, *gray_ad, *gray_md; //double *redgray_fa, *redgray_rd, *redgray_ad, *redgray_md; double *red_fa, *red_rd, *red_ad, *red_md; mitk::Image* tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(0); mitk::Image::ConstPointer imgToCluster = tmpImg; red_fa = clusterer->PerformQuantification(imgToCluster, m_CurrentRGBClusteringResults->rgbChannels->r, mask); green_fa = clusterer->PerformQuantification(imgToCluster, m_CurrentRGBClusteringResults->rgbChannels->g, mask); gray_fa = clusterer->PerformQuantification(imgToCluster, m_CurrentRGBClusteringResults->rgbChannels->b, mask); tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(3); mitk::Image::ConstPointer imgToCluster3 = tmpImg; red_rd = clusterer->PerformQuantification(imgToCluster3, m_CurrentRGBClusteringResults->rgbChannels->r, mask); green_rd = clusterer->PerformQuantification(imgToCluster3, m_CurrentRGBClusteringResults->rgbChannels->g, mask); gray_rd = clusterer->PerformQuantification(imgToCluster3, m_CurrentRGBClusteringResults->rgbChannels->b, mask); tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(4); mitk::Image::ConstPointer imgToCluster4 = tmpImg; red_ad = clusterer->PerformQuantification(imgToCluster4, m_CurrentRGBClusteringResults->rgbChannels->r, mask); green_ad = clusterer->PerformQuantification(imgToCluster4, m_CurrentRGBClusteringResults->rgbChannels->g, mask); gray_ad = clusterer->PerformQuantification(imgToCluster4, m_CurrentRGBClusteringResults->rgbChannels->b, mask); tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(5); mitk::Image::ConstPointer imgToCluster5 = tmpImg; red_md = clusterer->PerformQuantification(imgToCluster5, m_CurrentRGBClusteringResults->rgbChannels->r, mask); green_md = clusterer->PerformQuantification(imgToCluster5, m_CurrentRGBClusteringResults->rgbChannels->g, mask); gray_md = clusterer->PerformQuantification(imgToCluster5, m_CurrentRGBClusteringResults->rgbChannels->b, mask); // clipboard QString clipboardText("FA\t%1\t%2\t\t%3\t%4\t\t%5\t%6\t"); clipboardText = clipboardText - .arg(red_fa[0]).arg(red_fa[1]) - .arg(gray_fa[0]).arg(gray_fa[1]) - .arg(green_fa[0]).arg(green_fa[1]); + .arg(red_fa[0]).arg(red_fa[1]) + .arg(gray_fa[0]).arg(gray_fa[1]) + .arg(green_fa[0]).arg(green_fa[1]); QString clipboardText3("RD\t%1\t%2\t\t%3\t%4\t\t%5\t%6\t"); clipboardText3 = clipboardText3 - .arg(red_rd[0]).arg(red_rd[1]) - .arg(gray_rd[0]).arg(gray_rd[1]) - .arg(green_rd[0]).arg(green_rd[1]); + .arg(red_rd[0]).arg(red_rd[1]) + .arg(gray_rd[0]).arg(gray_rd[1]) + .arg(green_rd[0]).arg(green_rd[1]); QString clipboardText4("AD\t%1\t%2\t\t%3\t%4\t\t%5\t%6\t"); clipboardText4 = clipboardText4 - .arg(red_ad[0]).arg(red_ad[1]) - .arg(gray_ad[0]).arg(gray_ad[1]) - .arg(green_ad[0]).arg(green_ad[1]); + .arg(red_ad[0]).arg(red_ad[1]) + .arg(gray_ad[0]).arg(gray_ad[1]) + .arg(green_ad[0]).arg(green_ad[1]); QString clipboardText5("MD\t%1\t%2\t\t%3\t%4\t\t%5\t%6"); clipboardText5 = clipboardText5 - .arg(red_md[0]).arg(red_md[1]) - .arg(gray_md[0]).arg(gray_md[1]) - .arg(green_md[0]).arg(green_md[1]); + .arg(red_md[0]).arg(red_md[1]) + .arg(gray_md[0]).arg(gray_md[1]) + .arg(green_md[0]).arg(green_md[1]); QApplication::clipboard()->setText(clipboardText+clipboardText3+clipboardText4+clipboardText5, QClipboard::Clipboard); // now paint infos also on renderwindow QString plainInfoText("%1 %2 %3 \n"); plainInfoText = plainInfoText - .arg("Red ", 20) - .arg("Gray ", 20) - .arg("Green", 20); + .arg("Red ", 20) + .arg("Gray ", 20) + .arg("Green", 20); QString plainInfoText0("FA:%1 ± %2%3 ± %4%5 ± %6\n"); plainInfoText0 = plainInfoText0 - .arg(red_fa[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(red_fa[1], -10, 'g', 2, QLatin1Char( ' ' )) - .arg(gray_fa[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(gray_fa[1], -10, 'g', 2, QLatin1Char( ' ' )) - .arg(green_fa[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(green_fa[1], -10, 'g', 2, QLatin1Char( ' ' )); + .arg(red_fa[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(red_fa[1], -10, 'g', 2, QLatin1Char( ' ' )) + .arg(gray_fa[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(gray_fa[1], -10, 'g', 2, QLatin1Char( ' ' )) + .arg(green_fa[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(green_fa[1], -10, 'g', 2, QLatin1Char( ' ' )); QString plainInfoText3("RDx10³:%1 ± %2%3 ± %4%5 ± %6\n"); plainInfoText3 = plainInfoText3 - .arg(1000.0 * red_rd[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_rd[1], -10, 'g', 2, QLatin1Char( ' ' )) - .arg(1000.0 * gray_rd[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * gray_rd[1], -10, 'g', 2, QLatin1Char( ' ' )) - .arg(1000.0 * green_rd[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * green_rd[1], -10, 'g', 2, QLatin1Char( ' ' )); + .arg(1000.0 * red_rd[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_rd[1], -10, 'g', 2, QLatin1Char( ' ' )) + .arg(1000.0 * gray_rd[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * gray_rd[1], -10, 'g', 2, QLatin1Char( ' ' )) + .arg(1000.0 * green_rd[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * green_rd[1], -10, 'g', 2, QLatin1Char( ' ' )); QString plainInfoText4("ADx10³:%1 ± %2%3 ± %4%5 ± %6\n"); plainInfoText4 = plainInfoText4 - .arg(1000.0 * red_ad[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_ad[1], -10, 'g', 2, QLatin1Char( ' ' )) - .arg(1000.0 * gray_ad[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * gray_ad[1], -10, 'g', 2, QLatin1Char( ' ' )) - .arg(1000.0 * green_ad[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * green_ad[1], -10, 'g', 2, QLatin1Char( ' ' )); + .arg(1000.0 * red_ad[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_ad[1], -10, 'g', 2, QLatin1Char( ' ' )) + .arg(1000.0 * gray_ad[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * gray_ad[1], -10, 'g', 2, QLatin1Char( ' ' )) + .arg(1000.0 * green_ad[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * green_ad[1], -10, 'g', 2, QLatin1Char( ' ' )); QString plainInfoText5("MDx10³:%1 ± %2%3 ± %4%5 ± %6"); plainInfoText5 = plainInfoText5 - .arg(1000.0 * red_md[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_md[1], -10, 'g', 2, QLatin1Char( ' ' )) - .arg(1000.0 * gray_md[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * gray_md[1], -10, 'g', 2, QLatin1Char( ' ' )) - .arg(1000.0 * green_md[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * green_md[1], -10, 'g', 2, QLatin1Char( ' ' )); + .arg(1000.0 * red_md[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_md[1], -10, 'g', 2, QLatin1Char( ' ' )) + .arg(1000.0 * gray_md[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * gray_md[1], -10, 'g', 2, QLatin1Char( ' ' )) + .arg(1000.0 * green_md[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * green_md[1], -10, 'g', 2, QLatin1Char( ' ' )); this->SetMeasurementInfoToRenderWindow(plainInfoText+plainInfoText0+plainInfoText3+plainInfoText4+plainInfoText5); } else { double* green; double* gray; double* red; mitk::Image* tmpImg = m_CurrentStatisticsCalculator->GetInternalImage(); mitk::Image::ConstPointer imgToCluster = tmpImg; red = clusterer->PerformQuantification(imgToCluster, m_CurrentRGBClusteringResults->rgbChannels->r); green = clusterer->PerformQuantification(imgToCluster, m_CurrentRGBClusteringResults->rgbChannels->g); gray = clusterer->PerformQuantification(imgToCluster, m_CurrentRGBClusteringResults->rgbChannels->b); // clipboard QString clipboardText("%1\t%2\t\t%3\t%4\t\t%5\t%6"); clipboardText = clipboardText.arg(red[0]).arg(red[1]) - .arg(gray[0]).arg(gray[1]) - .arg(green[0]).arg(green[1]); + .arg(gray[0]).arg(gray[1]) + .arg(green[0]).arg(green[1]); QApplication::clipboard()->setText(clipboardText, QClipboard::Clipboard); // now paint infos also on renderwindow QString plainInfoText("Red: %1 ± %2\nGray: %3 ± %4\nGreen: %5 ± %6"); plainInfoText = plainInfoText.arg(red[0]).arg(red[1]) - .arg(gray[0]).arg(gray[1]) - .arg(green[0]).arg(green[1]); + .arg(gray[0]).arg(gray[1]) + .arg(green[0]).arg(green[1]); this->SetMeasurementInfoToRenderWindow(plainInfoText); } clusteredImage = m_CurrentRGBClusteringResults->rgb; } else { if(m_IsTensorImage) { double *red_fa, *red_rd, *red_ad, *red_md; mitk::Image* tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(0); mitk::Image::ConstPointer imgToCluster = tmpImg; red_fa = clusterer->PerformQuantification(imgToCluster, m_CurrentPerformClusteringResults->clusteredImage, mask); tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(3); mitk::Image::ConstPointer imgToCluster3 = tmpImg; red_rd = clusterer->PerformQuantification(imgToCluster3, m_CurrentPerformClusteringResults->clusteredImage, mask); tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(4); mitk::Image::ConstPointer imgToCluster4 = tmpImg; red_ad = clusterer->PerformQuantification(imgToCluster4, m_CurrentPerformClusteringResults->clusteredImage, mask); tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(5); mitk::Image::ConstPointer imgToCluster5 = tmpImg; red_md = clusterer->PerformQuantification(imgToCluster5, m_CurrentPerformClusteringResults->clusteredImage, mask); // clipboard QString clipboardText("FA\t%1\t%2\t"); clipboardText = clipboardText - .arg(red_fa[0]).arg(red_fa[1]); + .arg(red_fa[0]).arg(red_fa[1]); QString clipboardText3("RD\t%1\t%2\t"); clipboardText3 = clipboardText3 - .arg(red_rd[0]).arg(red_rd[1]); + .arg(red_rd[0]).arg(red_rd[1]); QString clipboardText4("AD\t%1\t%2\t"); clipboardText4 = clipboardText4 - .arg(red_ad[0]).arg(red_ad[1]); + .arg(red_ad[0]).arg(red_ad[1]); QString clipboardText5("MD\t%1\t%2\t"); clipboardText5 = clipboardText5 - .arg(red_md[0]).arg(red_md[1]); + .arg(red_md[0]).arg(red_md[1]); QApplication::clipboard()->setText(clipboardText+clipboardText3+clipboardText4+clipboardText5, QClipboard::Clipboard); // now paint infos also on renderwindow QString plainInfoText("%1 \n"); plainInfoText = plainInfoText - .arg("Red ", 20); + .arg("Red ", 20); QString plainInfoText0("FA:%1 ± %2\n"); plainInfoText0 = plainInfoText0 - .arg(red_fa[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(red_fa[1], -10, 'g', 2, QLatin1Char( ' ' )); + .arg(red_fa[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(red_fa[1], -10, 'g', 2, QLatin1Char( ' ' )); QString plainInfoText3("RDx10³:%1 ± %2\n"); plainInfoText3 = plainInfoText3 - .arg(1000.0 * red_rd[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_rd[1], -10, 'g', 2, QLatin1Char( ' ' )); + .arg(1000.0 * red_rd[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_rd[1], -10, 'g', 2, QLatin1Char( ' ' )); QString plainInfoText4("ADx10³:%1 ± %2\n"); plainInfoText4 = plainInfoText4 - .arg(1000.0 * red_ad[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_ad[1], -10, 'g', 2, QLatin1Char( ' ' )); + .arg(1000.0 * red_ad[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_ad[1], -10, 'g', 2, QLatin1Char( ' ' )); QString plainInfoText5("MDx10³:%1 ± %2"); plainInfoText5 = plainInfoText5 - .arg(1000.0 * red_md[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_md[1], -10, 'g', 2, QLatin1Char( ' ' )); + .arg(1000.0 * red_md[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_md[1], -10, 'g', 2, QLatin1Char( ' ' )); this->SetMeasurementInfoToRenderWindow(plainInfoText+plainInfoText0+plainInfoText3+plainInfoText4+plainInfoText5); } else { double* quant; mitk::Image* tmpImg = m_CurrentStatisticsCalculator->GetInternalImage(); mitk::Image::ConstPointer imgToCluster = tmpImg; quant = clusterer->PerformQuantification(imgToCluster, m_CurrentPerformClusteringResults->clusteredImage); // clipboard QString clipboardText("%1\t%2"); clipboardText = clipboardText.arg(quant[0]).arg(quant[1]); QApplication::clipboard()->setText(clipboardText, QClipboard::Clipboard); // now paint infos also on renderwindow QString plainInfoText("Measurement: %1 ± %2"); plainInfoText = plainInfoText.arg(quant[0]).arg(quant[1]); this->SetMeasurementInfoToRenderWindow(plainInfoText); } clusteredImage = m_CurrentPerformClusteringResults->displayImage; } if(mask.IsNotNull()) { typedef itk::Image,3> RGBImageType; typedef mitk::ImageToItk ClusterCasterType; ClusterCasterType::Pointer clCaster = ClusterCasterType::New(); clCaster->SetInput(clusteredImage); clCaster->Update(); clCaster->GetOutput(); typedef itk::MaskImageFilter< RGBImageType, MaskImageType, RGBImageType > MaskType; MaskType::Pointer masker = MaskType::New(); masker->SetInput1(clCaster->GetOutput()); masker->SetInput2(itkmask); masker->Update(); clusteredImage = mitk::Image::New(); clusteredImage->InitializeByItk(masker->GetOutput()); clusteredImage->SetVolume(masker->GetOutput()->GetBufferPointer()); } if(m_ClusteringResult.IsNotNull()) { - GetDefaultDataStorage()->Remove(m_ClusteringResult); + this->GetDataStorage()->Remove(m_ClusteringResult); } m_ClusteringResult = mitk::DataNode::New(); m_ClusteringResult->SetBoolProperty("helper object", true); m_ClusteringResult->SetIntProperty( "layer", 1000 ); m_ClusteringResult->SetBoolProperty("texture interpolation", m_TexIsOn); m_ClusteringResult->SetData(clusteredImage); m_ClusteringResult->SetName("Clusterprobs"); - GetDefaultDataStorage()->Add(m_ClusteringResult, m_SelectedImageNodes->GetNode()); + this->GetDataStorage()->Add(m_ClusteringResult, m_SelectedImageNodes->GetNode()); if(m_SelectedPlanarFigure.IsNotNull() && m_SelectedPlanarFigureNodes->GetNode().IsNotNull()) { m_SelectedPlanarFigureNodes->GetNode()->SetIntProperty( "layer", 1001 ); } - GetActiveStdMultiWidget()->RequestUpdate(); + this->RequestRenderWindowUpdate(); } void QmitkPartialVolumeAnalysisView::UpdateStatistics() { MITK_INFO << "UpdateStatistics()"; if(!m_CurrentFigureNodeInitialized && m_SelectedPlanarFigure.IsNotNull()) { MITK_INFO << "Selected planar figure not initialized. No stats calculation performed."; return; } // Remove any cached images that are no longer referenced elsewhere this->RemoveOrphanImages(); - QmitkStdMultiWidget *multiWidget = this->GetActiveStdMultiWidget(); + + QmitkStdMultiWidget *multiWidget = 0; + QmitkStdMultiWidgetEditor * multiWidgetEdit = 0; + + multiWidgetEdit = dynamic_cast(this->GetRenderWindowPart()); + if(multiWidgetEdit){ + multiWidget = multiWidgetEdit->GetStdMultiWidget(); + } + if ( multiWidget == NULL ) { return; } if ( m_SelectedImage.IsNotNull() ) { // Check if a the selected image is a multi-channel image. If yes, statistics // cannot be calculated currently. if ( !m_IsTensorImage && m_SelectedImage->GetPixelType().GetNumberOfComponents() > 1 ) { - std::stringstream message; - message << "Non-tensor multi-component images not supported."; - m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); - m_Controls->m_ErrorMessageLabel->show(); + QMessageBox::information( NULL, "Warning", "Non-tensor multi-component images not supported."); m_Controls->m_HistogramWidget->ClearItemModel(); m_CurrentStatisticsValid = false; return; } // Retrieve HistogramStatisticsCalculator from has map (or create a new one // for this image if non-existant) PartialVolumeAnalysisMapType::iterator it = m_PartialVolumeAnalysisMap.find( m_SelectedImage ); if ( it != m_PartialVolumeAnalysisMap.end() ) { m_CurrentStatisticsCalculator = it->second; MITK_INFO << "Retrieving StatisticsCalculator"; } else { m_CurrentStatisticsCalculator = mitk::PartialVolumeAnalysisHistogramCalculator::New(); m_CurrentStatisticsCalculator->SetPlanarFigureThickness(m_Controls->m_PlanarFiguresThickness->value()); if(m_IsTensorImage) { m_CurrentStatisticsCalculator->SetImage( m_CAImage ); m_CurrentStatisticsCalculator->AddAdditionalResamplingImage( m_FAImage ); m_CurrentStatisticsCalculator->AddAdditionalResamplingImage( m_DirectionComp1Image ); m_CurrentStatisticsCalculator->AddAdditionalResamplingImage( m_DirectionComp2Image ); m_CurrentStatisticsCalculator->AddAdditionalResamplingImage( m_RDImage ); m_CurrentStatisticsCalculator->AddAdditionalResamplingImage( m_ADImage ); m_CurrentStatisticsCalculator->AddAdditionalResamplingImage( m_MDImage ); } else { m_CurrentStatisticsCalculator->SetImage( m_SelectedImage ); } m_PartialVolumeAnalysisMap[m_SelectedImage] = m_CurrentStatisticsCalculator; MITK_INFO << "Creating StatisticsCalculator"; } std::string maskName; std::string maskType; unsigned int maskDimension; if ( m_SelectedImageMask.IsNotNull() ) { mitk::PixelType pixelType = m_SelectedImageMask->GetPixelType(); std::cout << pixelType.GetItkTypeAsString() << std::endl; if(pixelType.GetBitsPerComponent() == 16) { //convert from short to uchar typedef itk::Image ShortImageType; typedef itk::Image CharImageType; CharImageType::Pointer charImage; ShortImageType::Pointer shortImage; mitk::CastToItkImage(m_SelectedImageMask, shortImage); typedef itk::CastImageFilter ImageCasterType; ImageCasterType::Pointer caster = ImageCasterType::New(); caster->SetInput( shortImage ); caster->Update(); charImage = caster->GetOutput(); mitk::CastToMitkImage(charImage, m_SelectedImageMask); } m_CurrentStatisticsCalculator->SetImageMask( m_SelectedImageMask ); m_CurrentStatisticsCalculator->SetMaskingModeToImage(); maskName = m_SelectedMaskNode->GetName(); maskType = m_SelectedImageMask->GetNameOfClass(); maskDimension = 3; std::stringstream maskLabel; maskLabel << maskName; if ( maskDimension > 0 ) { maskLabel << " [" << maskDimension << "D " << maskType << "]"; } m_Controls->m_SelectedMaskLabel->setText( maskLabel.str().c_str() ); } else if ( m_SelectedPlanarFigure.IsNotNull() && m_SelectedPlanarFigureNodes->GetNode().IsNotNull()) { m_CurrentStatisticsCalculator->SetPlanarFigure( m_SelectedPlanarFigure ); m_CurrentStatisticsCalculator->SetMaskingModeToPlanarFigure(); maskName = m_SelectedPlanarFigureNodes->GetNode()->GetName(); maskType = m_SelectedPlanarFigure->GetNameOfClass(); maskDimension = 2; } else { m_CurrentStatisticsCalculator->SetMaskingModeToNone(); - maskName = "None"; + maskName = "-"; maskType = ""; maskDimension = 0; } bool statisticsChanged = false; bool statisticsCalculationSuccessful = false; // Initialize progress bar mitk::ProgressBar::GetInstance()->AddStepsToDo( 100 ); // Install listener for progress events and initialize progress bar typedef itk::SimpleMemberCommand< QmitkPartialVolumeAnalysisView > ITKCommandType; ITKCommandType::Pointer progressListener; progressListener = ITKCommandType::New(); progressListener->SetCallbackFunction( this, &QmitkPartialVolumeAnalysisView::UpdateProgressBar ); unsigned long progressObserverTag = m_CurrentStatisticsCalculator - ->AddObserver( itk::ProgressEvent(), progressListener ); + ->AddObserver( itk::ProgressEvent(), progressListener ); ClusteringType::ParamsType *cparams = 0; ClusteringType::ClusterResultType *cresult = 0; ClusteringType::HistType *chist = 0; try { m_CurrentStatisticsCalculator->SetNumberOfBins(m_Controls->m_NumberBins->text().toInt()); m_CurrentStatisticsCalculator->SetUpsamplingFactor(m_Controls->m_Upsampling->text().toDouble()); m_CurrentStatisticsCalculator->SetGaussianSigma(m_Controls->m_GaussianSigma->text().toDouble()); // Compute statistics statisticsChanged = m_CurrentStatisticsCalculator->ComputeStatistics( ); mitk::Image* tmpImg = m_CurrentStatisticsCalculator->GetInternalImage(); mitk::Image::ConstPointer imgToCluster = tmpImg; if(imgToCluster.IsNotNull()) { // perform clustering const HistogramType *histogram = m_CurrentStatisticsCalculator->GetHistogram( ); if(histogram != NULL) { ClusteringType::Pointer clusterer = ClusteringType::New(); clusterer->SetStepsNumIntegration(200); clusterer->SetMaxIt(1000); mitk::Image::Pointer pFiberImg; if(m_QuantifyClass==3) { if(m_Controls->m_Quantiles->isChecked()) { m_CurrentRGBClusteringResults = clusterer->PerformRGBQuantiles(imgToCluster, histogram, m_Controls->m_q1->value(),m_Controls->m_q2->value()); } else { m_CurrentRGBClusteringResults = clusterer->PerformRGBClustering(imgToCluster, histogram); } pFiberImg = m_CurrentRGBClusteringResults->rgbChannels->r; cparams = m_CurrentRGBClusteringResults->params; cresult = m_CurrentRGBClusteringResults->result; chist = m_CurrentRGBClusteringResults->hist; } else { if(m_Controls->m_Quantiles->isChecked()) { m_CurrentPerformClusteringResults = clusterer->PerformQuantiles(imgToCluster, histogram, m_Controls->m_q1->value(),m_Controls->m_q2->value()); } else { m_CurrentPerformClusteringResults = - clusterer->PerformClustering(imgToCluster, histogram, m_QuantifyClass); + clusterer->PerformClustering(imgToCluster, histogram, m_QuantifyClass); } pFiberImg = m_CurrentPerformClusteringResults->clusteredImage; cparams = m_CurrentPerformClusteringResults->params; cresult = m_CurrentPerformClusteringResults->result; chist = m_CurrentPerformClusteringResults->hist; } if(m_IsTensorImage) { m_AngularErrorImage = clusterer->CaculateAngularErrorImage( - m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(1), - m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(2), - pFiberImg); + m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(1), + m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(2), + pFiberImg); // GetDefaultDataStorage()->Remove(m_newnode2); // m_newnode2 = mitk::DataNode::New(); // m_newnode2->SetData(m_AngularErrorImage); // m_newnode2->SetName(("AngularError")); // m_newnode2->SetIntProperty( "layer", 1003 ); // GetDefaultDataStorage()->Add(m_newnode2, m_SelectedImageNodes->GetNode()); // newnode = mitk::DataNode::New(); // newnode->SetData(m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(1)); // newnode->SetName(("Comp1")); // GetDefaultDataStorage()->Add(newnode, m_SelectedImageNodes->GetNode()); // newnode = mitk::DataNode::New(); // newnode->SetData(m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(2)); // newnode->SetName(("Comp2")); // GetDefaultDataStorage()->Add(newnode, m_SelectedImageNodes->GetNode()); } ShowClusteringResults(); } } statisticsCalculationSuccessful = true; } catch ( const std::runtime_error &e ) { - // In case of exception, print error message on GUI - std::stringstream message; - message << e.what(); - m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); - m_Controls->m_ErrorMessageLabel->show(); + QMessageBox::information( NULL, "Warning", e.what()); } catch ( const std::exception &e ) { MITK_ERROR << "Caught exception: " << e.what(); - // In case of exception, print error message on GUI - std::stringstream message; - message << "Error in calculating histogram: " << e.what(); - m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); - m_Controls->m_ErrorMessageLabel->show(); + QMessageBox::information( NULL, "Warning", e.what()); } m_CurrentStatisticsCalculator->RemoveObserver( progressObserverTag ); // Make sure that progress bar closes mitk::ProgressBar::GetInstance()->Progress( 100 ); if ( statisticsCalculationSuccessful ) { if ( statisticsChanged ) { // Do not show any error messages - m_Controls->m_ErrorMessageLabel->hide(); - m_CurrentStatisticsValid = true; } // m_Controls->m_HistogramWidget->SetHistogramModeToDirectHistogram(); m_Controls->m_HistogramWidget->SetParameters( - cparams, cresult, chist ); + cparams, cresult, chist ); // m_Controls->m_HistogramWidget->UpdateItemModelFromHistogram(); } else { - m_Controls->m_SelectedMaskLabel->setText( "None" ); + m_Controls->m_SelectedMaskLabel->setText( "-" ); // Clear statistics and histogram m_Controls->m_HistogramWidget->ClearItemModel(); m_CurrentStatisticsValid = false; // If a (non-closed) PlanarFigure is selected, display a line profile widget if ( m_SelectedPlanarFigure.IsNotNull() ) { // TODO: enable line profile widget //m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 1 ); //m_Controls->m_LineProfileWidget->SetImage( m_SelectedImage ); //m_Controls->m_LineProfileWidget->SetPlanarFigure( m_SelectedPlanarFigure ); //m_Controls->m_LineProfileWidget->UpdateItemModelFromPath(); } } } } void QmitkPartialVolumeAnalysisView::SetMeasurementInfoToRenderWindow(const QString& text) { FindRenderWindow(m_SelectedPlanarFigureNodes->GetNode()); if(m_LastRenderWindow != m_SelectedRenderWindow) { if(m_LastRenderWindow) { QObject::disconnect( m_LastRenderWindow, SIGNAL( destroyed(QObject*) ) , this, SLOT( OnRenderWindowDelete(QObject*) ) ); } m_LastRenderWindow = m_SelectedRenderWindow; if(m_LastRenderWindow) { QObject::connect( m_LastRenderWindow, SIGNAL( destroyed(QObject*) ) , this, SLOT( OnRenderWindowDelete(QObject*) ) ); } } if(m_LastRenderWindow && m_SelectedPlanarFigureNodes->GetNode().IsNotNull()) { if (!text.isEmpty()) { m_MeasurementInfoAnnotation->SetText(1, text.toLatin1().data()); mitk::VtkLayerController::GetInstance(m_LastRenderWindow->GetRenderWindow())->InsertForegroundRenderer( - m_MeasurementInfoRenderer, true); + m_MeasurementInfoRenderer, true); } else { if (mitk::VtkLayerController::GetInstance( - m_LastRenderWindow->GetRenderWindow()) ->IsRendererInserted( - m_MeasurementInfoRenderer)) + m_LastRenderWindow->GetRenderWindow()) ->IsRendererInserted( + m_MeasurementInfoRenderer)) mitk::VtkLayerController::GetInstance(m_LastRenderWindow->GetRenderWindow())->RemoveRenderer( - m_MeasurementInfoRenderer); + m_MeasurementInfoRenderer); } } else { + QmitkStdMultiWidget *multiWidget = 0; + QmitkStdMultiWidgetEditor * multiWidgetEdit = 0; + + multiWidgetEdit = dynamic_cast(this->GetRenderWindowPart()); + if(multiWidgetEdit){ + multiWidget = multiWidgetEdit->GetStdMultiWidget(); + } + + if ( multiWidget == NULL ) + { + return; + } + if (!text.isEmpty()) { m_MeasurementInfoAnnotation->SetText(1, text.toLatin1().data()); - mitk::VtkLayerController::GetInstance(this->GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderWindow())->InsertForegroundRenderer( - m_MeasurementInfoRenderer, true); + mitk::VtkLayerController::GetInstance(multiWidget->GetRenderWindow1()->GetRenderWindow())->InsertForegroundRenderer( + m_MeasurementInfoRenderer, true); } else { if (mitk::VtkLayerController::GetInstance( - this->GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderWindow()) ->IsRendererInserted( - m_MeasurementInfoRenderer)) - mitk::VtkLayerController::GetInstance(this->GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderWindow())->RemoveRenderer( - m_MeasurementInfoRenderer); + multiWidget->GetRenderWindow1()->GetRenderWindow()) ->IsRendererInserted( + m_MeasurementInfoRenderer)) + mitk::VtkLayerController::GetInstance(multiWidget->GetRenderWindow1()->GetRenderWindow())->RemoveRenderer( + m_MeasurementInfoRenderer); } } } void QmitkPartialVolumeAnalysisView::UpdateProgressBar() { mitk::ProgressBar::GetInstance()->Progress(); } void QmitkPartialVolumeAnalysisView::RequestStatisticsUpdate() { if ( !m_StatisticsUpdatePending ) { QApplication::postEvent( this, new QmitkRequestStatisticsUpdateEvent ); m_StatisticsUpdatePending = true; } } void QmitkPartialVolumeAnalysisView::RemoveOrphanImages() { PartialVolumeAnalysisMapType::iterator it = m_PartialVolumeAnalysisMap.begin(); while ( it != m_PartialVolumeAnalysisMap.end() ) { mitk::Image *image = it->first; mitk::PartialVolumeAnalysisHistogramCalculator *calculator = it->second; ++it; mitk::NodePredicateData::Pointer hasImage = mitk::NodePredicateData::New( image ); - if ( this->GetDefaultDataStorage()->GetNode( hasImage ) == NULL ) + if ( this->GetDataStorage()->GetNode( hasImage ) == NULL ) { if ( m_SelectedImage == image ) { m_SelectedImage = NULL; m_SelectedImageNodes->RemoveAllNodes(); } if ( m_CurrentStatisticsCalculator == calculator ) { m_CurrentStatisticsCalculator = NULL; } m_PartialVolumeAnalysisMap.erase( image ); it = m_PartialVolumeAnalysisMap.begin(); } } } void QmitkPartialVolumeAnalysisView::ExtractTensorImages( mitk::Image::ConstPointer tensorimage) { typedef itk::Image< itk::DiffusionTensor3D, 3> TensorImageType; typedef mitk::ImageToItk CastType; CastType::Pointer caster = CastType::New(); caster->SetInput(tensorimage); caster->Update(); TensorImageType::Pointer image = caster->GetOutput(); typedef itk::TensorDerivedMeasurementsFilter MeasurementsType; MeasurementsType::Pointer measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(image ); measurementsCalculator->SetMeasure(MeasurementsType::FA); measurementsCalculator->Update(); MeasurementsType::OutputImageType::Pointer fa = measurementsCalculator->GetOutput(); m_FAImage = mitk::Image::New(); m_FAImage->InitializeByItk(fa.GetPointer()); m_FAImage->SetVolume(fa->GetBufferPointer()); // mitk::DataNode::Pointer node = mitk::DataNode::New(); // node->SetData(m_FAImage); // GetDefaultDataStorage()->Add(node); measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(image ); measurementsCalculator->SetMeasure(MeasurementsType::CA); measurementsCalculator->Update(); MeasurementsType::OutputImageType::Pointer ca = measurementsCalculator->GetOutput(); m_CAImage = mitk::Image::New(); m_CAImage->InitializeByItk(ca.GetPointer()); m_CAImage->SetVolume(ca->GetBufferPointer()); // node = mitk::DataNode::New(); // node->SetData(m_CAImage); // GetDefaultDataStorage()->Add(node); measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(image ); measurementsCalculator->SetMeasure(MeasurementsType::RD); measurementsCalculator->Update(); MeasurementsType::OutputImageType::Pointer rd = measurementsCalculator->GetOutput(); m_RDImage = mitk::Image::New(); m_RDImage->InitializeByItk(rd.GetPointer()); m_RDImage->SetVolume(rd->GetBufferPointer()); // node = mitk::DataNode::New(); // node->SetData(m_CAImage); // GetDefaultDataStorage()->Add(node); measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(image ); measurementsCalculator->SetMeasure(MeasurementsType::AD); measurementsCalculator->Update(); MeasurementsType::OutputImageType::Pointer ad = measurementsCalculator->GetOutput(); m_ADImage = mitk::Image::New(); m_ADImage->InitializeByItk(ad.GetPointer()); m_ADImage->SetVolume(ad->GetBufferPointer()); // node = mitk::DataNode::New(); // node->SetData(m_CAImage); // GetDefaultDataStorage()->Add(node); measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(image ); measurementsCalculator->SetMeasure(MeasurementsType::RA); measurementsCalculator->Update(); MeasurementsType::OutputImageType::Pointer md = measurementsCalculator->GetOutput(); m_MDImage = mitk::Image::New(); m_MDImage->InitializeByItk(md.GetPointer()); m_MDImage->SetVolume(md->GetBufferPointer()); // node = mitk::DataNode::New(); // node->SetData(m_CAImage); // GetDefaultDataStorage()->Add(node); typedef DirectionsFilterType::OutputImageType DirImageType; DirectionsFilterType::Pointer dirFilter = DirectionsFilterType::New(); dirFilter->SetInput(image ); dirFilter->Update(); itk::ImageRegionIterator itd(dirFilter->GetOutput(), dirFilter->GetOutput()->GetLargestPossibleRegion()); itd = itd.Begin(); while( !itd.IsAtEnd() ) { DirImageType::PixelType direction = itd.Get(); direction[0] = fabs(direction[0]); direction[1] = fabs(direction[1]); direction[2] = fabs(direction[2]); itd.Set(direction); ++itd; } typedef itk::CartesianToPolarVectorImageFilter< DirImageType, DirImageType, true> C2PFilterType; C2PFilterType::Pointer cpFilter = C2PFilterType::New(); cpFilter->SetInput(dirFilter->GetOutput()); cpFilter->Update(); DirImageType::Pointer dir = cpFilter->GetOutput(); typedef itk::Image CompImageType; CompImageType::Pointer comp1 = CompImageType::New(); comp1->SetSpacing( dir->GetSpacing() ); // Set the image spacing comp1->SetOrigin( dir->GetOrigin() ); // Set the image origin comp1->SetDirection( dir->GetDirection() ); // Set the image direction comp1->SetRegions( dir->GetLargestPossibleRegion() ); comp1->Allocate(); CompImageType::Pointer comp2 = CompImageType::New(); comp2->SetSpacing( dir->GetSpacing() ); // Set the image spacing comp2->SetOrigin( dir->GetOrigin() ); // Set the image origin comp2->SetDirection( dir->GetDirection() ); // Set the image direction comp2->SetRegions( dir->GetLargestPossibleRegion() ); comp2->Allocate(); itk::ImageRegionConstIterator it(dir, dir->GetLargestPossibleRegion()); itk::ImageRegionIterator it1(comp1, comp1->GetLargestPossibleRegion()); itk::ImageRegionIterator it2(comp2, comp2->GetLargestPossibleRegion()); it = it.Begin(); it1 = it1.Begin(); it2 = it2.Begin(); while( !it.IsAtEnd() ) { it1.Set(it.Get()[1]); it2.Set(it.Get()[2]); ++it; ++it1; ++it2; } m_DirectionComp1Image = mitk::Image::New(); m_DirectionComp1Image->InitializeByItk(comp1.GetPointer()); m_DirectionComp1Image->SetVolume(comp1->GetBufferPointer()); m_DirectionComp2Image = mitk::Image::New(); m_DirectionComp2Image->InitializeByItk(comp2.GetPointer()); m_DirectionComp2Image->SetVolume(comp2->GetBufferPointer()); } void QmitkPartialVolumeAnalysisView::OnRenderWindowDelete(QObject * obj) { if(obj == m_LastRenderWindow) m_LastRenderWindow = 0; if(obj == m_SelectedRenderWindow) m_SelectedRenderWindow = 0; } bool QmitkPartialVolumeAnalysisView::event( QEvent *event ) { if ( event->type() == (QEvent::Type) QmitkRequestStatisticsUpdateEvent::StatisticsUpdateRequest ) { // Update statistics m_StatisticsUpdatePending = false; this->UpdateStatistics(); return true; } return false; } -void QmitkPartialVolumeAnalysisView::Visible() -{ - this->OnSelectionChanged( this->GetDataManagerSelection() ); -} bool QmitkPartialVolumeAnalysisView::IsExclusiveFunctionality() const { return true; } void QmitkPartialVolumeAnalysisView::Activated() { - this->GetActiveStdMultiWidget()->SetWidgetPlanesVisibility(false); + + MITK_INFO << "QmitkPartialVolumeAnalysisView:Activated"; + + //this->GetActiveStdMultiWidget()->SetWidgetPlanesVisibility(false); //this->GetActiveStdMultiWidget()->GetRenderWindow1()->FullScreenMode(true); - mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDefaultDataStorage()->GetAll(); + mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDataStorage()->GetAll(); mitk::DataNode* node = 0; mitk::PlanarFigure* figure = 0; mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; // finally add all nodes to the model for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() - ; it++) - { + ; it++) + { node = const_cast(it->Value().GetPointer()); figure = dynamic_cast(node->GetData()); if(figure) { figureInteractor = dynamic_cast(node->GetInteractor()); if(figureInteractor.IsNull()) figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", node); mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); } } m_Visible = true; } void QmitkPartialVolumeAnalysisView::Deactivated() { - this->GetActiveStdMultiWidget()->SetWidgetPlanesVisibility(true); - //this->GetActiveStdMultiWidget()->GetRenderWindow1()->FullScreenMode(false); + MITK_INFO << "QmitkPartialVolumeAnalysisView:Deactivated"; +} + +void QmitkPartialVolumeAnalysisView::ActivatedZombieView(berry::IWorkbenchPartReference::Pointer reference) +{ + MITK_INFO << "QmitkPartialVolumeAnalysisView:ActivatedZombieView"; + + //this->GetActiveStdMultiWidget()->SetWidgetPlanesVisibility(true); this->SetMeasurementInfoToRenderWindow(""); - mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDefaultDataStorage()->GetAll(); + mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDataStorage()->GetAll(); mitk::DataNode* node = 0; mitk::PlanarFigure* figure = 0; mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; // finally add all nodes to the model for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() - ; it++) - { + ; it++) + { node = const_cast(it->Value().GetPointer()); figure = dynamic_cast(node->GetData()); if(figure) { figureInteractor = dynamic_cast(node->GetInteractor()); if(figureInteractor) mitk::GlobalInteraction::GetInstance()->RemoveInteractor(figureInteractor); } } - m_Visible = false; } +void QmitkPartialVolumeAnalysisView::Hidden() +{ + +} + +void QmitkPartialVolumeAnalysisView::Visible() +{ + //this->OnSelectionChanged( this->Get, this->GetDataManagerSelection() ); +} + +void QmitkPartialVolumeAnalysisView::SetFocus() +{ + +} + + void QmitkPartialVolumeAnalysisView::GreenRadio(bool checked) { if(checked) { m_Controls->m_PartialVolumeRadio->setChecked(false); m_Controls->m_BlueRadio->setChecked(false); m_Controls->m_AllRadio->setChecked(false); m_Controls->m_ExportClusteringResultsButton->setEnabled(true); } m_QuantifyClass = 0; RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::PartialVolumeRadio(bool checked) { if(checked) { m_Controls->m_GreenRadio->setChecked(false); m_Controls->m_BlueRadio->setChecked(false); m_Controls->m_AllRadio->setChecked(false); m_Controls->m_ExportClusteringResultsButton->setEnabled(true); } m_QuantifyClass = 1; RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::BlueRadio(bool checked) { if(checked) { m_Controls->m_PartialVolumeRadio->setChecked(false); m_Controls->m_GreenRadio->setChecked(false); m_Controls->m_AllRadio->setChecked(false); m_Controls->m_ExportClusteringResultsButton->setEnabled(true); } m_QuantifyClass = 2; RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::AllRadio(bool checked) { if(checked) { m_Controls->m_BlueRadio->setChecked(false); m_Controls->m_PartialVolumeRadio->setChecked(false); m_Controls->m_GreenRadio->setChecked(false); m_Controls->m_ExportClusteringResultsButton->setEnabled(false); } m_QuantifyClass = 3; RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::NumberBinsChangedSlider(int v ) { m_Controls->m_NumberBins->setText(QString("%1").arg(m_Controls->m_NumberBinsSlider->value()*5.0)); } void QmitkPartialVolumeAnalysisView::UpsamplingChangedSlider( int v) { m_Controls->m_Upsampling->setText(QString("%1").arg(m_Controls->m_UpsamplingSlider->value()/10.0)); } void QmitkPartialVolumeAnalysisView::GaussianSigmaChangedSlider(int v ) { m_Controls->m_GaussianSigma->setText(QString("%1").arg(m_Controls->m_GaussianSigmaSlider->value()/100.0)); } void QmitkPartialVolumeAnalysisView::SimilarAnglesChangedSlider(int v ) { m_Controls->m_SimilarAngles->setText(QString("%1°").arg(90-m_Controls->m_SimilarAnglesSlider->value())); ShowClusteringResults(); } void QmitkPartialVolumeAnalysisView::OpacityChangedSlider(int v ) { if(m_SelectedImageNodes->GetNode().IsNotNull()) { float opacImag = 1.0f-(v-5)/5.0f; opacImag = opacImag < 0 ? 0 : opacImag; m_SelectedImageNodes->GetNode()->SetFloatProperty("opacity", opacImag); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } if(m_ClusteringResult.IsNotNull()) { float opacClust = v/5.0f; opacClust = opacClust > 1 ? 1 : opacClust; m_ClusteringResult->SetFloatProperty("opacity", opacClust); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkPartialVolumeAnalysisView::NumberBinsReleasedSlider( ) { RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::UpsamplingReleasedSlider( ) { RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::GaussianSigmaReleasedSlider( ) { RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::SimilarAnglesReleasedSlider( ) { } void QmitkPartialVolumeAnalysisView::ToClipBoard() { std::vector* > vals = m_Controls->m_HistogramWidget->m_Vals; QString clipboardText; for (std::vector* >::iterator it = vals.begin(); it - != vals.end(); ++it) + != vals.end(); ++it) { for (std::vector::iterator it2 = (**it).begin(); it2 != (**it).end(); ++it2) { clipboardText.append(QString("%1 \t").arg(*it2)); } clipboardText.append(QString("\n")); } QApplication::clipboard()->setText(clipboardText, QClipboard::Clipboard); } void QmitkPartialVolumeAnalysisView::PropertyChanged(const mitk::DataNode* /*node*/, const mitk::BaseProperty* /*prop*/) { } void QmitkPartialVolumeAnalysisView::NodeChanged(const mitk::DataNode* /*node*/) { } void QmitkPartialVolumeAnalysisView::NodeRemoved(const mitk::DataNode* node) { if (dynamic_cast(node->GetData())) - GetDefaultDataStorage()->Remove(m_ClusteringResult); + this->GetDataStorage()->Remove(m_ClusteringResult); if( node == m_SelectedPlanarFigureNodes->GetNode().GetPointer() - || node == m_SelectedMaskNode.GetPointer() ) - { + || node == m_SelectedMaskNode.GetPointer() ) + { this->Select(NULL,true,false); SetMeasurementInfoToRenderWindow(""); } if( node == m_SelectedImageNodes->GetNode().GetPointer() ) { this->Select(NULL,false,true); SetMeasurementInfoToRenderWindow(""); } } void QmitkPartialVolumeAnalysisView::NodeAddedInDataStorage(const mitk::DataNode* node) { if(!m_Visible) return; mitk::DataNode* nonConstNode = const_cast(node); mitk::PlanarFigure* figure = dynamic_cast(nonConstNode->GetData()); if(figure) { // set interactor for new node (if not already set) mitk::PlanarFigureInteractor::Pointer figureInteractor = dynamic_cast(node->GetInteractor()); if(figureInteractor.IsNull()) figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", nonConstNode); mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); // remove uninitialized old planars if( m_SelectedPlanarFigureNodes->GetNode().IsNotNull() && m_CurrentFigureNodeInitialized == false ) { mitk::Interactor::Pointer oldInteractor = m_SelectedPlanarFigureNodes->GetNode()->GetInteractor(); if(oldInteractor.IsNotNull()) mitk::GlobalInteraction::GetInstance()->RemoveInteractor(oldInteractor); - this->GetDefaultDataStorage()->Remove(m_SelectedPlanarFigureNodes->GetNode()); + this->GetDataStorage()->Remove(m_SelectedPlanarFigureNodes->GetNode()); } } } void QmitkPartialVolumeAnalysisView::TextIntON() { if(m_ClusteringResult.IsNotNull()) { if(m_TexIsOn) { m_Controls->m_TextureIntON->setIcon(*m_IconTexOFF); } else { m_Controls->m_TextureIntON->setIcon(*m_IconTexON); } m_ClusteringResult->SetBoolProperty("texture interpolation", !m_TexIsOn); m_TexIsOn = !m_TexIsOn; - GetActiveStdMultiWidget()->RequestUpdate(); + this->RequestRenderWindowUpdate(); } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPartialVolumeAnalysisView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPartialVolumeAnalysisView.h index bbae16184c..df2d207363 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPartialVolumeAnalysisView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPartialVolumeAnalysisView.h @@ -1,270 +1,281 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2009-05-28 20:08:26 +0200 (Do, 28 Mai 2009) $ Version: $Revision: 10185 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #if !defined(QmitkPartialVolumeAnalysisView_H__INCLUDED) #define QmitkPartialVolumeAnalysisView_H__INCLUDED -#include "QmitkFunctionality.h" +//#include "QmitkFunctionality.h" #include "ui_QmitkPartialVolumeAnalysisViewControls.h" +#include +#include +#include // berry #include #include // itk #include #include "itkDiffusionTensorPrincipleDirectionImageFilter.h" #include // qmitk #include "QmitkStepperAdapter.h" #include "QmitkRenderWindow.h" // mitk #include "mitkPartialVolumeAnalysisHistogramCalculator.h" #include "mitkPlanarLine.h" #include #include "mitkDataStorageSelection.h" #include // vtk #include #include #include //#include "itkProcessObject.h" /*! \brief QmitkPartialVolumeAnalysis \sa QmitkFunctionality \ingroup Functionalities */ -class QmitkPartialVolumeAnalysisView : public QmitkFunctionality//, public itk::ProcessObject +class QmitkPartialVolumeAnalysisView : public QmitkAbstractView, public mitk::IZombieViewPart//, public itk::ProcessObject { Q_OBJECT public: /*! \ Convenient typedefs */ typedef mitk::DataStorage::SetOfObjects ConstVector; typedef ConstVector::ConstPointer ConstVectorPointer; typedef ConstVector::ConstIterator ConstVectorIterator; typedef mitk::PartialVolumeAnalysisHistogramCalculator HistogramCalculatorType; typedef HistogramCalculatorType::HistogramType HistogramType; typedef mitk::PartialVolumeAnalysisClusteringCalculator ClusteringType; typedef itk::DiffusionTensorPrincipleDirectionImageFilter DirectionsFilterType; /*! \brief default constructor */ QmitkPartialVolumeAnalysisView(QObject *parent=0, const char *name=0); /*! \brief default destructor */ virtual ~QmitkPartialVolumeAnalysisView(); /*! \brief method for creating the widget containing the application controls, like sliders, buttons etc. */ virtual void CreateQtPartControl(QWidget *parent); /*! \brief method for creating the connections of main and control widget */ virtual void CreateConnections(); bool IsExclusiveFunctionality() const; virtual bool event( QEvent *event ); - void OnSelectionChanged( std::vector nodes ); + virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList &nodes); virtual void Activated(); virtual void Deactivated(); + virtual void ActivatedZombieView(berry::IWorkbenchPartReference::Pointer reference); + + virtual void Hidden(); + + virtual void Visible(); + + virtual void SetFocus(); + bool AssertDrawingIsPossible(bool checked); virtual void NodeChanged(const mitk::DataNode* node); virtual void PropertyChanged(const mitk::DataNode* node, const mitk::BaseProperty* prop); virtual void NodeRemoved(const mitk::DataNode* node); virtual void NodeAddedInDataStorage(const mitk::DataNode* node); virtual void AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name, const char *propertyKey = NULL, mitk::BaseProperty *property = NULL ); void PlanarFigureInitialized(); void PlanarFigureFocus(mitk::DataNode* node); void ShowClusteringResults(); static const std::string VIEW_ID; protected slots: void EstimateCircle(); void SetHistogramVisibility(); void SetAdvancedVisibility(); void NumberBinsChangedSlider(int v ); void UpsamplingChangedSlider( int v ); void GaussianSigmaChangedSlider( int v ); void SimilarAnglesChangedSlider(int v ); void OpacityChangedSlider(int v ); void NumberBinsReleasedSlider( ); void UpsamplingReleasedSlider( ); void GaussianSigmaReleasedSlider( ); void SimilarAnglesReleasedSlider( ); void ActionDrawEllipseTriggered(); void ActionDrawRectangleTriggered(); void ActionDrawPolygonTriggered(); void ToClipBoard(); void GreenRadio(bool checked); void PartialVolumeRadio(bool checked); void BlueRadio(bool checked); void AllRadio(bool checked); void OnRenderWindowDelete(QObject * obj); void TextIntON(); void ExportClusteringResults(); protected: - void StdMultiWidgetAvailable( QmitkStdMultiWidget& stdMultiWidget ); + //void StdMultiWidgetAvailable( QmitkStdMultiWidget& stdMultiWidget ); /** \brief Issues a request to update statistics by sending an event to the * Qt event processing queue. * * Statistics update should only be executed after program execution returns * to the Qt main loop. This mechanism also prevents multiple execution of * updates where only one is required.*/ void RequestStatisticsUpdate(); /** \brief Recalculate statistics for currently selected image and mask and * update the GUI. */ void UpdateStatistics(); /** \brief Listener for progress events to update progress bar. */ void UpdateProgressBar(); /** \brief Removes any cached images which are no longer referenced elsewhere. */ void RemoveOrphanImages(); void Select( mitk::DataNode::Pointer node, bool clearMaskOnFirstArgNULL=false, bool clearImageOnFirstArgNULL=false ); - void Visible( ); - void SetMeasurementInfoToRenderWindow(const QString& text); void FindRenderWindow(mitk::DataNode* node); void ExtractTensorImages( mitk::Image::ConstPointer tensorimage); typedef std::map< mitk::Image *, mitk::PartialVolumeAnalysisHistogramCalculator::Pointer > PartialVolumeAnalysisMapType; /*! * controls containing sliders for scrolling through the slices */ Ui::QmitkPartialVolumeAnalysisViewControls *m_Controls; QmitkStepperAdapter* m_TimeStepperAdapter; unsigned int m_CurrentTime; QString m_Clipboard; // result text rendering vtkRenderer * m_MeasurementInfoRenderer; vtkCornerAnnotation *m_MeasurementInfoAnnotation; // Image and mask data mitk::DataStorageSelection::Pointer m_SelectedImageNodes; mitk::Image::Pointer m_SelectedImage; mitk::DataNode::Pointer m_SelectedMaskNode; mitk::Image::Pointer m_SelectedImageMask; mitk::DataStorageSelection::Pointer m_SelectedPlanarFigureNodes; mitk::PlanarFigure::Pointer m_SelectedPlanarFigure; bool m_IsTensorImage; mitk::Image::Pointer m_FAImage; mitk::Image::Pointer m_CAImage; mitk::Image::Pointer m_RDImage; mitk::Image::Pointer m_ADImage; mitk::Image::Pointer m_MDImage; // mitk::Image::Pointer m_DirectionImage; mitk::Image::Pointer m_DirectionComp1Image; mitk::Image::Pointer m_DirectionComp2Image; mitk::Image::Pointer m_AngularErrorImage; QmitkRenderWindow* m_SelectedRenderWindow; QmitkRenderWindow* m_LastRenderWindow; long m_ImageObserverTag; long m_ImageMaskObserverTag; long m_PlanarFigureObserverTag; // Hash map for associating one image statistics calculator with each iamge // (so that previously calculated histograms / statistics can be recovered // if a recalculation is not required) PartialVolumeAnalysisMapType m_PartialVolumeAnalysisMap; HistogramCalculatorType::Pointer m_CurrentStatisticsCalculator; bool m_CurrentStatisticsValid; bool m_StatisticsUpdatePending; bool m_GaussianSigmaChangedSliding; bool m_NumberBinsSliding; bool m_UpsamplingChangedSliding; + bool m_Visible; + mitk::DataNode::Pointer m_ClusteringResult; int m_EllipseCounter; int m_RectangleCounter; int m_PolygonCounter; unsigned int m_InitializedObserverTag; bool m_CurrentFigureNodeInitialized; int m_QuantifyClass; ClusteringType::HelperStructPerformRGBClusteringRetval* m_CurrentRGBClusteringResults; ClusteringType::HelperStructPerformClusteringRetval *m_CurrentPerformClusteringResults; // mitk::DataNode::Pointer m_newnode; // mitk::DataNode::Pointer m_newnode2; QIcon* m_IconTexOFF; QIcon* m_IconTexON; bool m_TexIsOn; }; #endif // !defined(QmitkPartialVolumeAnalysis_H__INCLUDED) diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPartialVolumeAnalysisViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPartialVolumeAnalysisViewControls.ui index ef60ffb5a4..ab458e8f78 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPartialVolumeAnalysisViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPartialVolumeAnalysisViewControls.ui @@ -1,793 +1,797 @@ QmitkPartialVolumeAnalysisViewControls true 0 0 - 343 - 470 + 360 + 528 Form 0 - - 0 + + 9 + + + 3 + + + 9 + + + 3 - - - false + + + Data - - true + + + + + + 0 + 0 + + + + Tensor/Scalar Image: + + + + + + + + 0 + 0 + + + + Mask Image: + + + + + + + - + + + + + + + - + + + + + + + + + + Parameters QFrame::NoFrame QFrame::Raised 0 QFrame::NoFrame QFrame::Raised 0 0 - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 6 - - - 0 - - - 0 - - - 0 - - - 9 - - - 0 - - - - - - 0 - 0 - - - - Image: - - - - - - - false - - - true - - - - - - - - 0 - 0 - - - - Mask: - - - - - - - false - - - true - - - - - - QFrame::NoFrame QFrame::Raised 0 30 30 + + Draw circular ROI + :/QmitkDiffusionImaging/circle.png:/QmitkDiffusionImaging/circle.png 32 32 true true 30 30 + + Draw quadratic ROI + :/QmitkDiffusionImaging/rectangle.png:/QmitkDiffusionImaging/rectangle.png 32 32 true true 30 30 + + Draw polygonal ROI + :/QmitkDiffusionImaging/polygon.png:/QmitkDiffusionImaging/polygon.png 32 32 true true + + + + Qt::Horizontal + + + + 40 + 20 + + + + true QFrame::NoFrame QFrame::Raised + + QFormLayout::AllNonFixedFieldsGrow + 0 Upsampling QFrame::NoFrame QFrame::Raised 0 1 50 1 25 Qt::Horizontal 50 0 2.5 Similar angles QFrame::NoFrame 0 90 0 Qt::Horizontal QSlider::NoTicks 50 0 90° QFrame::NoFrame QFrame::Raised 0 0 display histogram true 0 0 QFrame::NoFrame QFrame::Raised 0 QFrame::NoFrame QFrame::Raised 0 20 20 true true Green Partial Volume Partial Volume Partial Volume Partial Volume PV Red true All Export clustering result as float image. ... :/org.mitk.gui.qt.diffusionimaging/resources/arrow.png:/org.mitk.gui.qt.diffusionimaging/resources/arrow.png QFrame::NoFrame 0 Opacity 10 5 Qt::Horizontal QSlider::TicksBelow Histogram to Clipboard Advanced Qt::Vertical QSizePolicy::Preferred 10 1 QFrame::NoFrame QFrame::Raised QFormLayout::AllNonFixedFieldsGrow 0 Blurring QFrame::NoFrame QFrame::Raised 0 200 1 0 Qt::Horizontal 50 0 0.0 # Bins QFrame::NoFrame 0 1 100 10 Qt::Horizontal QSlider::NoTicks 50 0 50 quantiles QFrame::StyledPanel QFrame::Raised 0 1.000000000000000 0.010000000000000 0.250000000000000 1.000000000000000 0.010000000000000 0.750000000000000 Estimate circle from binary image "Thick" PFs Qt::Vertical 20 40 QmitkPartialVolumeAnalysisWidget QWidget
QmitkPartialVolumeAnalysisWidget.h
1
diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.cpp index c6302d1b44..e5d7925b30 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.cpp @@ -1,561 +1,388 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: 2009-05-28 17:19:30 +0200 (Do, 28 Mai 2009) $ Version: $Revision: 17495 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ //#define MBILOG_ENABLE_DEBUG #include "QmitkPreprocessingView.h" #include "mitkDiffusionImagingConfigure.h" // qt includes #include // itk includes #include "itkTimeProbe.h" #include "itkB0ImageExtractionImageFilter.h" #include "itkBrainMaskExtractionImageFilter.h" #include "itkCastImageFilter.h" #include "itkVectorContainer.h" #include // mitk includes #include "QmitkDataStorageComboBox.h" #include "QmitkStdMultiWidget.h" #include "mitkProgressBar.h" #include "mitkStatusBar.h" #include "mitkNodePredicateDataType.h" #include "mitkProperties.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkTransferFunction.h" #include "mitkTransferFunctionProperty.h" #include "mitkDataNodeObject.h" #include "mitkOdfNormalizationMethodProperty.h" #include "mitkOdfScaleByProperty.h" #include -#include "berryIStructuredSelection.h" -#include "berryIWorkbenchWindow.h" -#include "berryISelectionService.h" - #include #include const std::string QmitkPreprocessingView::VIEW_ID = "org.mitk.views.preprocessing"; #define DI_INFO MITK_INFO("DiffusionImaging") typedef float TTensorPixelType; -using namespace berry; - -struct PrpSelListener : ISelectionListener -{ - - berryObjectMacro(PrpSelListener); - - PrpSelListener(QmitkPreprocessingView* view) - { - m_View = view; - } - - void DoSelectionChanged(ISelection::ConstPointer selection) - { - // save current selection in member variable - m_View->m_CurrentSelection = selection.Cast(); - - // do something with the selected items - if(m_View->m_CurrentSelection) - { - bool foundDwiVolume = false; - m_View->m_DiffusionImage = NULL; - - // iterate selection - for (IStructuredSelection::iterator i = m_View->m_CurrentSelection->Begin(); - i != m_View->m_CurrentSelection->End(); ++i) - { - - // extract datatree node - if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) - { - mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - - // process only on valid nodes - mitk::BaseData* nodeData = node->GetData(); - if(nodeData) - { - // only look at interesting types - if(QString("DiffusionImage").compare(nodeData->GetNameOfClass())==0) - { - foundDwiVolume = true; - m_View->m_DiffusionImage = dynamic_cast*>(nodeData); - } - } - } - } - - m_View->m_Controls->m_ButtonBrainMask->setEnabled(foundDwiVolume); - m_View->m_Controls->m_ButtonAverageGradients->setEnabled(foundDwiVolume); - m_View->m_Controls->m_ButtonExtractB0->setEnabled(foundDwiVolume); - m_View->m_Controls->m_ModifyMeasurementFrame->setEnabled(foundDwiVolume); - m_View->m_Controls->m_MeasurementFrameTable->setEnabled(foundDwiVolume); - m_View->m_Controls->m_ReduceGradientsButton->setEnabled(foundDwiVolume); - m_View->m_Controls->m_ShowGradientsButton->setEnabled(foundDwiVolume); - m_View->m_Controls->m_MirrorGradientToHalfSphereButton->setEnabled(foundDwiVolume); - - if (foundDwiVolume) - { - vnl_matrix_fixed< double, 3, 3 > mf = m_View->m_DiffusionImage->GetMeasurementFrame(); - for (int r=0; r<3; r++) - for (int c=0; c<3; c++) - { - QTableWidgetItem* item = m_View->m_Controls->m_MeasurementFrameTable->item(r,c); - delete item; - item = new QTableWidgetItem(); - item->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter); - item->setText(QString::number(mf.get(r,c))); - m_View->m_Controls->m_MeasurementFrameTable->setItem(r,c,item); - } - m_View->m_Controls->m_GradientsLabel->setText(QString::number(m_View->m_DiffusionImage->GetNumDirections())); - - if (m_View->m_DiffusionImage->IsMultiBval()) - m_View->m_Controls->m_BvalLabel->setText("Acquisition with multiple b-values!"); - else - m_View->m_Controls->m_BvalLabel->setText(QString::number(m_View->m_DiffusionImage->GetB_Value())); - } - else - { - for (int r=0; r<3; r++) - for (int c=0; c<3; c++) - { - QTableWidgetItem* item = m_View->m_Controls->m_MeasurementFrameTable->item(r,c); - delete item; - item = new QTableWidgetItem(); - m_View->m_Controls->m_MeasurementFrameTable->setItem(r,c,item); - } - m_View->m_Controls->m_GradientsLabel->setText("-"); - m_View->m_Controls->m_BvalLabel->setText("-"); - } - } - } - - void SelectionChanged(IWorkbenchPart::Pointer part, ISelection::ConstPointer selection) - { - // check, if selection comes from datamanager - if (part) - { - QString partname(part->GetPartName().c_str()); - if(partname.compare("Datamanager")==0) - { - - // apply selection - DoSelectionChanged(selection); - - } - } - } - - QmitkPreprocessingView* m_View; -}; QmitkPreprocessingView::QmitkPreprocessingView() : QmitkFunctionality(), m_Controls(NULL), m_MultiWidget(NULL), m_DiffusionImage(NULL) { } QmitkPreprocessingView::QmitkPreprocessingView(const QmitkPreprocessingView& other) { Q_UNUSED(other) throw std::runtime_error("Copy constructor not implemented"); } QmitkPreprocessingView::~QmitkPreprocessingView() { - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->RemovePostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); + } void QmitkPreprocessingView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkPreprocessingViewControls; m_Controls->setupUi(parent); this->CreateConnections(); m_Controls->m_MeasurementFrameTable->horizontalHeader()->setResizeMode(QHeaderView::Stretch); m_Controls->m_MeasurementFrameTable->verticalHeader()->setResizeMode(QHeaderView::Stretch); } - - m_SelListener = berry::ISelectionListener::Pointer(new PrpSelListener(this)); - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddPostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); - berry::ISelection::ConstPointer sel( - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); - m_CurrentSelection = sel.Cast(); - m_SelListener.Cast()->DoSelectionChanged(sel); } void QmitkPreprocessingView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkPreprocessingView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkPreprocessingView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_ButtonAverageGradients), SIGNAL(clicked()), this, SLOT(AverageGradients()) ); connect( (QObject*)(m_Controls->m_ButtonExtractB0), SIGNAL(clicked()), this, SLOT(ExtractB0()) ); connect( (QObject*)(m_Controls->m_ButtonBrainMask), SIGNAL(clicked()), this, SLOT(BrainMask()) ); connect( (QObject*)(m_Controls->m_ModifyMeasurementFrame), SIGNAL(clicked()), this, SLOT(DoApplyMesurementFrame()) ); connect( (QObject*)(m_Controls->m_ReduceGradientsButton), SIGNAL(clicked()), this, SLOT(DoReduceGradientDirections()) ); connect( (QObject*)(m_Controls->m_ShowGradientsButton), SIGNAL(clicked()), this, SLOT(DoShowGradientDirections()) ); connect( (QObject*)(m_Controls->m_MirrorGradientToHalfSphereButton), SIGNAL(clicked()), this, SLOT(DoHalfSphereGradientDirections()) ); } } +void QmitkPreprocessingView::OnSelectionChanged( std::vector nodes ) +{ + bool foundDwiVolume = false; + m_DiffusionImage = NULL; + m_SelectedDiffusionNodes = mitk::DataStorage::SetOfObjects::New(); + + // iterate selection + for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) + { + mitk::DataNode::Pointer node = *it; + + if( node.IsNotNull() && dynamic_cast*>(node->GetData()) ) + { + foundDwiVolume = true; + m_DiffusionImage = dynamic_cast*>(node->GetData()); + m_Controls->m_DiffusionImageLabel->setText(node->GetName().c_str()); + m_SelectedDiffusionNodes->push_back(node); + } + } + + m_Controls->m_ButtonBrainMask->setEnabled(foundDwiVolume); + m_Controls->m_ButtonAverageGradients->setEnabled(foundDwiVolume); + m_Controls->m_ButtonExtractB0->setEnabled(foundDwiVolume); + m_Controls->m_ModifyMeasurementFrame->setEnabled(foundDwiVolume); + m_Controls->m_MeasurementFrameTable->setEnabled(foundDwiVolume); + m_Controls->m_ReduceGradientsButton->setEnabled(foundDwiVolume); + m_Controls->m_ShowGradientsButton->setEnabled(foundDwiVolume); + m_Controls->m_MirrorGradientToHalfSphereButton->setEnabled(foundDwiVolume); + + if (foundDwiVolume) + { + vnl_matrix_fixed< double, 3, 3 > mf = m_DiffusionImage->GetMeasurementFrame(); + for (int r=0; r<3; r++) + for (int c=0; c<3; c++) + { + QTableWidgetItem* item = m_Controls->m_MeasurementFrameTable->item(r,c); + delete item; + item = new QTableWidgetItem(); + item->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter); + item->setText(QString::number(mf.get(r,c))); + m_Controls->m_MeasurementFrameTable->setItem(r,c,item); + } + m_Controls->m_GradientsLabel->setText(QString::number(m_DiffusionImage->GetNumDirections())); + + if (m_DiffusionImage->IsMultiBval()) + m_Controls->m_BvalLabel->setText("Acquisition with multiple b-values!"); + else + m_Controls->m_BvalLabel->setText(QString::number(m_DiffusionImage->GetB_Value())); + } + else + { + for (int r=0; r<3; r++) + for (int c=0; c<3; c++) + { + QTableWidgetItem* item = m_Controls->m_MeasurementFrameTable->item(r,c); + delete item; + item = new QTableWidgetItem(); + m_Controls->m_MeasurementFrameTable->setItem(r,c,item); + } + m_Controls->m_GradientsLabel->setText("-"); + m_Controls->m_BvalLabel->setText("-"); + m_Controls->m_DiffusionImageLabel->setText("-"); + } +} + void QmitkPreprocessingView::Activated() { QmitkFunctionality::Activated(); - - berry::ISelection::ConstPointer sel( - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); - m_CurrentSelection = sel.Cast(); - m_SelListener.Cast()->DoSelectionChanged(sel); } void QmitkPreprocessingView::Deactivated() { QmitkFunctionality::Deactivated(); } void QmitkPreprocessingView::DoHalfSphereGradientDirections() { if (m_DiffusionImage.IsNull()) return; GradientDirectionContainerType::Pointer gradientContainer = m_DiffusionImage->GetOriginalDirections(); for (int j=0; jSize(); j++) if (gradientContainer->at(j)[0]<0) gradientContainer->at(j) = -gradientContainer->at(j); } void QmitkPreprocessingView::DoApplyMesurementFrame() { if (m_DiffusionImage.IsNull()) return; vnl_matrix_fixed< double, 3, 3 > mf; for (int r=0; r<3; r++) for (int c=0; c<3; c++) { QTableWidgetItem* item = m_Controls->m_MeasurementFrameTable->item(r,c); if (!item) return; mf[r][c] = item->text().toDouble(); } m_DiffusionImage->SetMeasurementFrame(mf); } void QmitkPreprocessingView::DoShowGradientDirections() { if (m_DiffusionImage.IsNull()) return; GradientDirectionContainerType::Pointer gradientContainer = m_DiffusionImage->GetOriginalDirections(); mitk::PointSet::Pointer pointset = mitk::PointSet::New(); for (int j=0; jSize(); j++) { mitk::Point3D p; vnl_vector_fixed< double, 3 > v = gradientContainer->at(j); if (fabs(v[0])>0.001 || fabs(v[1])>0.001 || fabs(v[2])>0.001) { p[0] = v[0]; p[1] = v[1]; p[2] = v[2]; pointset->InsertPoint(j, p); } } mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(pointset); node->SetName("gradient directions"); node->SetProperty("pointsize", mitk::FloatProperty::New(0.05)); node->SetProperty("color", mitk::ColorProperty::New(1,0,0)); GetDefaultDataStorage()->Add(node); } void QmitkPreprocessingView::DoReduceGradientDirections() { if (m_DiffusionImage.IsNull()) return; typedef mitk::DiffusionImage DiffusionImageType; typedef itk::ReduceDirectionGradientsFilter FilterType; GradientDirectionContainerType::Pointer gradientContainer = m_DiffusionImage->GetOriginalDirections(); FilterType::Pointer filter = FilterType::New(); filter->SetInput(m_DiffusionImage->GetVectorImage()); filter->SetOriginalGradientDirections(gradientContainer); filter->SetNumGradientDirections(m_Controls->m_ReduceGradientsBox->value()); filter->Update(); DiffusionImageType::Pointer image = DiffusionImageType::New(); image->SetVectorImage( filter->GetOutput() ); image->SetB_Value(m_DiffusionImage->GetB_Value()); image->SetDirections(filter->GetGradientDirections()); image->SetOriginalDirections(filter->GetGradientDirections()); image->SetMeasurementFrame(m_DiffusionImage->GetMeasurementFrame()); image->InitializeFromVectorImage(); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( image ); imageNode->SetName("reduced_image"); GetDefaultDataStorage()->Add(imageNode); } void QmitkPreprocessingView::ExtractB0() -{ - - if (m_CurrentSelection) - { - mitk::DataStorage::SetOfObjects::Pointer set = - mitk::DataStorage::SetOfObjects::New(); - - int at = 0; - for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); - i != m_CurrentSelection->End(); - ++i) - { - - if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) - { - mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - - // process only on valid nodes - const mitk::BaseData* nodeData = node->GetData(); - if(nodeData) - { - if(QString("DiffusionImage").compare(nodeData->GetNameOfClass())==0) - { - set->InsertElement(at++, node); - } - } - } - } - - DoExtractB0(set); - - } -} - -void QmitkPreprocessingView::DoExtractB0 -(mitk::DataStorage::SetOfObjects::Pointer inImages) { typedef mitk::DiffusionImage DiffusionImageType; typedef DiffusionImageType::GradientDirectionContainerType GradientContainerType; - int nrFiles = inImages->size(); + int nrFiles = m_SelectedDiffusionNodes->size(); if (!nrFiles) return; - mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); - mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); + mitk::DataStorage::SetOfObjects::const_iterator itemiter( m_SelectedDiffusionNodes->begin() ); + mitk::DataStorage::SetOfObjects::const_iterator itemiterend( m_SelectedDiffusionNodes->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { DiffusionImageType* vols = static_cast( (*itemiter)->GetData()); std::string nodename; (*itemiter)->GetStringProperty("name", nodename); // Extract image using found index typedef itk::B0ImageExtractionImageFilter FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput(vols->GetVectorImage()); filter->SetDirections(vols->GetDirections()); filter->Update(); mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk( filter->GetOutput() ); mitkImage->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( mitkImage ); node->SetProperty( "name", mitk::StringProperty::New(nodename + "_B0")); GetDefaultDataStorage()->Add(node); ++itemiter; } - - } void QmitkPreprocessingView::AverageGradients() { - - if (m_CurrentSelection) - { - mitk::DataStorage::SetOfObjects::Pointer set = - mitk::DataStorage::SetOfObjects::New(); - - int at = 0; - for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); - i != m_CurrentSelection->End(); - ++i) - { - - if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) - { - mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - // process only on valid nodes - const mitk::BaseData* nodeData = node->GetData(); - - if(nodeData) - { - if(QString("DiffusionImage").compare(nodeData->GetNameOfClass())==0) - { - set->InsertElement(at++, node); - } - } - } - } - - DoAverageGradients(set); - - } - -} - -void QmitkPreprocessingView::DoAverageGradients -(mitk::DataStorage::SetOfObjects::Pointer inImages) -{ - int nrFiles = inImages->size(); + int nrFiles = m_SelectedDiffusionNodes->size(); if (!nrFiles) return; - mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); - mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); + mitk::DataStorage::SetOfObjects::const_iterator itemiter( m_SelectedDiffusionNodes->begin() ); + mitk::DataStorage::SetOfObjects::const_iterator itemiterend( m_SelectedDiffusionNodes->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { mitk::DiffusionImage* vols = static_cast*>( (*itemiter)->GetData()); vols->AverageRedundantGradients(m_Controls->m_Blur->value()); ++itemiter; } } void QmitkPreprocessingView::BrainMask() { - - if (m_CurrentSelection) - { - mitk::DataStorage::SetOfObjects::Pointer set = - mitk::DataStorage::SetOfObjects::New(); - - int at = 0; - for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); - i != m_CurrentSelection->End(); - ++i) - { - - if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) - { - mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - // process only on valid nodes - const mitk::BaseData* nodeData = node->GetData(); - - if(nodeData) - { - if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) - { - set->InsertElement(at++, node); - } - } - } - } - - DoBrainMask(set); - - } - -} - -void QmitkPreprocessingView::DoBrainMask -(mitk::DataStorage::SetOfObjects::Pointer inImages) -{ - int nrFiles = inImages->size(); + int nrFiles = m_SelectedDiffusionNodes->size(); if (!nrFiles) return; - mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); - mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); + mitk::DataStorage::SetOfObjects::const_iterator itemiter( m_SelectedDiffusionNodes->begin() ); + mitk::DataStorage::SetOfObjects::const_iterator itemiterend( m_SelectedDiffusionNodes->end() ); while ( itemiter != itemiterend ) // for all items { mitk::DiffusionImage* vols = static_cast*>( (*itemiter)->GetData()); std::string nodename; (*itemiter)->GetStringProperty("name", nodename); // Extract image using found index typedef itk::B0ImageExtractionImageFilter FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput(vols->GetVectorImage()); filter->SetDirections(vols->GetDirections()); typedef itk::CastImageFilter, itk::Image > CastFilterType; CastFilterType::Pointer castfilter = CastFilterType::New(); castfilter->SetInput(filter->GetOutput()); typedef itk::BrainMaskExtractionImageFilter MaskFilterType; MaskFilterType::Pointer maskfilter = MaskFilterType::New(); maskfilter->SetInput(castfilter->GetOutput()); maskfilter->Update(); mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk( maskfilter->GetOutput() ); mitkImage->SetVolume( maskfilter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( mitkImage ); node->SetProperty( "name", mitk::StringProperty::New(nodename + "_Mask")); GetDefaultDataStorage()->Add(node); ++itemiter; } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.h index 3577677361..1c449b4fa2 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.h @@ -1,114 +1,107 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: 2009-05-28 17:19:30 +0200 (Do, 28 Mai 2009) $ Version: $Revision: 17495 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _QMITKPREPROCESSINGVIEW_H_INCLUDED #define _QMITKPREPROCESSINGVIEW_H_INCLUDED #include #include #include "ui_QmitkPreprocessingViewControls.h" #include "mitkDiffusionImage.h" -#include -#include -#include - typedef short DiffusionPixelType; struct PrpSelListener; /*! * \ingroup org_mitk_gui_qt_preprocessing_internal * * \brief QmitkPreprocessingView * * Document your class here. * * \sa QmitkFunctionality */ class QmitkPreprocessingView : public QmitkFunctionality { friend struct PrpSelListener; // this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) Q_OBJECT public: static const std::string VIEW_ID; typedef vnl_vector_fixed< double, 3 > GradientDirectionType; typedef itk::VectorContainer< unsigned int, GradientDirectionType > GradientDirectionContainerType; QmitkPreprocessingView(); QmitkPreprocessingView(const QmitkPreprocessingView& other); virtual ~QmitkPreprocessingView(); virtual void CreateQtPartControl(QWidget *parent); /// \brief Creation of the connections of main and control widget virtual void CreateConnections(); /// \brief Called when the functionality is activated virtual void Activated(); virtual void Deactivated(); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); static const int nrconvkernels; protected slots: void AverageGradients(); - void DoAverageGradients(mitk::DataStorage::SetOfObjects::Pointer inImages); - void ExtractB0(); - void DoExtractB0(mitk::DataStorage::SetOfObjects::Pointer inImages); - void BrainMask(); - void DoBrainMask(mitk::DataStorage::SetOfObjects::Pointer inImages); void DoApplyMesurementFrame(); void DoReduceGradientDirections(); void DoShowGradientDirections(); void DoHalfSphereGradientDirections(); protected: + /// \brief called by QmitkFunctionality when DataManager's selection has changed + virtual void OnSelectionChanged( std::vector nodes ); + Ui::QmitkPreprocessingViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; void SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name); - berry::ISelectionListener::Pointer m_SelListener; - berry::IStructuredSelection::ConstPointer m_CurrentSelection; mitk::DiffusionImage::Pointer m_DiffusionImage; + mitk::DataStorage::SetOfObjects::Pointer m_SelectedDiffusionNodes; }; #endif // _QMITKPREPROCESSINGVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingViewControls.ui index 104e53d529..fd89ef86d2 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingViewControls.ui @@ -1,496 +1,519 @@ QmitkPreprocessingViewControls 0 0 389 913 0 0 true QmitkPreprocessingViewControls + + + + Data + + + + + + Diffusion Image: + + + + + + + - + + + + + + Info QFormLayout::AllNonFixedFieldsGrow Number of Gradients: - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter b-Value: - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter false - + Generate pointset displaying the gradient vectors. Show gradients Reduce size 0 70 Multiple acquistions of one gradient direction can be averaged. Due to rounding errors, similar gradients often differ in the last decimal positions. The Merge radius allows to average them anyway by taking into account all directions within a certain radius. true QFrame::NoFrame QFrame::Raised 0 Accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "Merge radius" > 0 is configured. Accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "Merge radius" > 0 is configured. Accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "Merge radius" > 0 is configured. 6 2.000000000000000 0.000100000000000 0.001000000000000 Accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "Merge radius" > 0 is configured. Accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "Merge radius" > 0 is configured. Accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "Merge radius" > 0 is configured. Merge radius false Average redundant gradients false - + Mirror all gradients around one axis. Mirror gradients to half sphere Qt::Horizontal QFrame::NoFrame QFrame::Raised 0 0 0 New number of gradients: 1 30 false - + Retain only the specified number of gradient directions and according image volumes. The retained directions are spread equally over the half sphere. Reduce number of gradients Non diffusion weighted image 0 30 Average and extract all images that were acquired without diffusion weighting. true false Extract B0 Brain mask false Estimate binary brain mask 0 0 Measurment frame Qt::Horizontal 40 20 false 0 0 10 10 IBeamCursor true Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff true false false true true false true true New Row New Row New Row New Column New Column New Column false Apply new mesurement frame Qt::Vertical 20 40 diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.cpp index 331a9f2616..7edbd04136 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.cpp @@ -1,801 +1,796 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: 2009-05-28 17:19:30 +0200 (Do, 28 Mai 2009) $ Version: $Revision: 17495 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ //#define MBILOG_ENABLE_DEBUG #include "QmitkQBallReconstructionView.h" #include "mitkDiffusionImagingConfigure.h" // qt includes #include // itk includes #include "itkTimeProbe.h" // mitk includes #include "mitkProgressBar.h" #include "mitkStatusBar.h" #include "mitkNodePredicateDataType.h" #include "QmitkDataStorageComboBox.h" #include "QmitkStdMultiWidget.h" #include "itkDiffusionQballReconstructionImageFilter.h" #include "itkAnalyticalDiffusionQballReconstructionImageFilter.h" #include "itkVectorContainer.h" #include "mitkQBallImage.h" #include "mitkProperties.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkTransferFunction.h" #include "mitkTransferFunctionProperty.h" #include "mitkDataNodeObject.h" #include "mitkOdfNormalizationMethodProperty.h" #include "mitkOdfScaleByProperty.h" #include "berryIStructuredSelection.h" #include "berryIWorkbenchWindow.h" #include "berryISelectionService.h" #include const std::string QmitkQBallReconstructionView::VIEW_ID = "org.mitk.views.qballreconstruction"; #define DI_INFO MITK_INFO("DiffusionImaging") typedef float TTensorPixelType; const int QmitkQBallReconstructionView::nrconvkernels = 252; using namespace berry; struct QbrSelListener : ISelectionListener { berryObjectMacro(QbrSelListener); QbrSelListener(QmitkQBallReconstructionView* view) { m_View = view; } void DoSelectionChanged(ISelection::ConstPointer selection) { // save current selection in member variable m_View->m_CurrentSelection = selection.Cast(); // do something with the selected items if(m_View->m_CurrentSelection) { bool foundDwiVolume = false; + m_View->m_Controls->m_DiffusionImageLabel->setText("-"); // iterate selection for (IStructuredSelection::iterator i = m_View->m_CurrentSelection->Begin(); i != m_View->m_CurrentSelection->End(); ++i) { // extract datatree node if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) { mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - // check if node valid - const mitk::BaseData* nodeData = node->GetData(); - - if(nodeData) + // only look at interesting types + if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) { - // only look at interesting types - if(QString("DiffusionImage").compare(nodeData->GetNameOfClass())==0) - { - foundDwiVolume = true; - } + foundDwiVolume = true; + m_View->m_Controls->m_DiffusionImageLabel->setText(node->GetName().c_str()); } } } m_View->m_Controls->m_ButtonStandard->setEnabled(foundDwiVolume); } } void SelectionChanged(IWorkbenchPart::Pointer part, ISelection::ConstPointer selection) { // check, if selection comes from datamanager if (part) { QString partname(part->GetPartName().c_str()); if(partname.compare("Datamanager")==0) { // apply selection DoSelectionChanged(selection); } } } QmitkQBallReconstructionView* m_View; }; QmitkQBallReconstructionView::QmitkQBallReconstructionView() : QmitkFunctionality(), m_Controls(NULL), m_MultiWidget(NULL) { } QmitkQBallReconstructionView::QmitkQBallReconstructionView(const QmitkQBallReconstructionView& other) { Q_UNUSED(other); throw std::runtime_error("Copy constructor not implemented"); } //void QmitkQBallReconstructionView::OpactiyChanged(int value) //{ // if (m_CurrentSelection) // { // if (mitk::DataNodeObject::Pointer nodeObj = m_CurrentSelection->Begin()->Cast()) // { // mitk::DataNode::Pointer node = nodeObj->GetDataNode(); // if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) // { // node->SetIntProperty("DisplayChannel", value); // mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // } // } // } //} // //void QmitkQBallReconstructionView::OpactiyActionChanged() //{ // if (m_CurrentSelection) // { // if (mitk::DataNodeObject::Pointer nodeObj = m_CurrentSelection->Begin()->Cast()) // { // mitk::DataNode::Pointer node = nodeObj->GetDataNode(); // if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) // { // int displayChannel = 0.0; // if(node->GetIntProperty("DisplayChannel", displayChannel)) // { // m_OpacitySlider->setValue(displayChannel); // } // } // } // } // // MITK_INFO << "changed"; //} QmitkQBallReconstructionView::~QmitkQBallReconstructionView() { this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->RemovePostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); } void QmitkQBallReconstructionView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkQBallReconstructionViewControls; m_Controls->setupUi(parent); this->CreateConnections(); QStringList items; items << "2" << "4" << "6" << "8" << "10" << "12"; m_Controls->m_QBallReconstructionMaxLLevelComboBox->addItems(items); m_Controls->m_QBallReconstructionMaxLLevelComboBox->setCurrentIndex(1); MethodChoosen(m_Controls->m_QBallReconstructionMethodComboBox->currentIndex()); #ifndef DIFFUSION_IMAGING_EXTENDED m_Controls->m_QBallReconstructionMethodComboBox->removeItem(3); #endif AdvancedCheckboxClicked(); // define data type for combobox //m_Controls->m_ImageSelector->SetDataStorage( this->GetDefaultDataStorage() ); //m_Controls->m_ImageSelector->SetPredicate( mitk::NodePredicateDataType::New("DiffusionImage") ); } m_SelListener = berry::ISelectionListener::Pointer(new QbrSelListener(this)); this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddPostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); berry::ISelection::ConstPointer sel( this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); m_CurrentSelection = sel.Cast(); m_SelListener.Cast()->DoSelectionChanged(sel); } void QmitkQBallReconstructionView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkQBallReconstructionView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkQBallReconstructionView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_ButtonStandard), SIGNAL(clicked()), this, SLOT(ReconstructStandard()) ); connect( (QObject*)(m_Controls->m_AdvancedCheckbox), SIGNAL(clicked()), this, SLOT(AdvancedCheckboxClicked()) ); connect( (QObject*)(m_Controls->m_QBallReconstructionMethodComboBox), SIGNAL(currentIndexChanged(int)), this, SLOT(MethodChoosen(int)) ); } } +void QmitkQBallReconstructionView::OnSelectionChanged( std::vector nodes ) +{ + +} + void QmitkQBallReconstructionView::Activated() { QmitkFunctionality::Activated(); berry::ISelection::ConstPointer sel( this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); m_CurrentSelection = sel.Cast(); m_SelListener.Cast()->DoSelectionChanged(sel); } void QmitkQBallReconstructionView::Deactivated() { QmitkFunctionality::Deactivated(); } void QmitkQBallReconstructionView::ReconstructStandard() { int index = m_Controls->m_QBallReconstructionMethodComboBox->currentIndex(); #ifndef DIFFUSION_IMAGING_EXTENDED if(index>=3) { index = index + 1; } #endif switch(index) { case 0: { // Numerical Reconstruct(0,0); break; } case 1: { // Standard Reconstruct(1,0); break; } case 2: { // Solid Angle Reconstruct(1,6); break; } case 3: { // Constrained Solid Angle Reconstruct(1,7); break; } case 4: { // ADC Reconstruct(1,4); break; } case 5: { // Raw Signal Reconstruct(1,5); break; } } } void QmitkQBallReconstructionView::MethodChoosen(int method) { switch(method) { case 0: m_Controls->m_Description->setText("Numerical recon. (Tuch2004)"); break; case 1: m_Controls->m_Description->setText("Spherical harmonics recon. (Descoteaux2007)"); break; case 2: m_Controls->m_Description->setText("SH recon. with solid angle consideration (Aganj2009)"); break; case 3: m_Controls->m_Description->setText("SH solid angle with non-neg. constraint (Goh2009)"); break; case 4: m_Controls->m_Description->setText("SH recon. of the plain ADC-profiles"); break; case 5: m_Controls->m_Description->setText("SH recon. of the raw diffusion signal"); break; } } void QmitkQBallReconstructionView::AdvancedCheckboxClicked() { bool check = m_Controls-> m_AdvancedCheckbox->isChecked(); m_Controls->m_QBallReconstructionMaxLLevelTextLabel_2->setVisible(check); m_Controls->m_QBallReconstructionMaxLLevelComboBox->setVisible(check); m_Controls->m_QBallReconstructionLambdaTextLabel_2->setVisible(check); m_Controls->m_QBallReconstructionLambdaLineEdit->setVisible(check); m_Controls->m_QBallReconstructionThresholdLabel_2->setVisible(check); m_Controls->m_QBallReconstructionThreasholdEdit->setVisible(check); m_Controls->m_OutputB0Image->setVisible(check); m_Controls->label_2->setVisible(check); //m_Controls->textLabel1_2->setVisible(check); //m_Controls->m_QBallReconstructionLambdaStepLineEdit->setVisible(check); //m_Controls->textLabel1_3->setVisible(check); m_Controls->frame_2->setVisible(check); } void QmitkQBallReconstructionView::Reconstruct(int method, int normalization) { if (m_CurrentSelection) { mitk::DataStorage::SetOfObjects::Pointer set = mitk::DataStorage::SetOfObjects::New(); int at = 0; for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); i != m_CurrentSelection->End(); ++i) { if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) { mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - - // check if node's data valid - const mitk::BaseData* nodeData = node->GetData(); - if(nodeData) - { - if(QString("DiffusionImage").compare(nodeData->GetNameOfClass())==0) + if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) { set->InsertElement(at++, node); } - } } } if(method == 0) { NumericalQBallReconstruction(set, normalization); } else { #if BOOST_VERSION / 100000 > 0 #if BOOST_VERSION / 100 % 1000 > 34 if(method == 1) { AnalyticalQBallReconstruction(set, normalization); } #else std::cout << "ERROR: Boost 1.35 minimum required" << std::endl; QMessageBox::warning(NULL,"ERROR","Boost 1.35 minimum required"); #endif #else std::cout << "ERROR: Boost 1.35 minimum required" << std::endl; QMessageBox::warning(NULL,"ERROR","Boost 1.35 minimum required"); #endif } } } void QmitkQBallReconstructionView::NumericalQBallReconstruction (mitk::DataStorage::SetOfObjects::Pointer inImages, int normalization) { try { itk::TimeProbe clock; int nrFiles = inImages->size(); if (!nrFiles) return; QString status; mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles); mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { mitk::DiffusionImage* vols = static_cast*>( (*itemiter)->GetData()); std::string nodename; (*itemiter)->GetStringProperty("name", nodename); ++itemiter; // QBALL RECONSTRUCTION clock.Start(); MBI_INFO << "QBall reconstruction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "QBall reconstruction for %s", nodename.c_str()).toAscii()); typedef itk::DiffusionQballReconstructionImageFilter QballReconstructionImageFilterType; QballReconstructionImageFilterType::Pointer filter = QballReconstructionImageFilterType::New(); filter->SetGradientImage( vols->GetDirections(), vols->GetVectorImage() ); filter->SetBValue(vols->GetB_Value()); filter->SetThreshold( m_Controls->m_QBallReconstructionThreasholdEdit->text().toFloat() ); switch(normalization) { case 0: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_STANDARD); break; } case 1: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_B_ZERO_B_VALUE); break; } case 2: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_B_ZERO); break; } case 3: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_NONE); break; } default: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_STANDARD); } } filter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s." ; // ODFs TO DATATREE mitk::QBallImage::Pointer image = mitk::QBallImage::New(); image->InitializeByItk( filter->GetOutput() ); //image->SetImportVolume( filter->GetOutput()->GetBufferPointer(), 0, 0, mitk::Image::ImportMemoryManagementType::ManageMemory ); image->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); QString newname; newname = newname.append(nodename.c_str()); newname = newname.append("_QN%1").arg(normalization); SetDefaultNodeProperties(node, newname.toStdString()); nodes.push_back(node); // B-Zero TO DATATREE if(m_Controls->m_OutputB0Image->isChecked()) { mitk::Image::Pointer image4 = mitk::Image::New(); image4->InitializeByItk( filter->GetBZeroImage().GetPointer() ); image4->SetVolume( filter->GetBZeroImage()->GetBufferPointer() ); mitk::DataNode::Pointer node4=mitk::DataNode::New(); node4->SetData( image4 ); node4->SetProperty( "name", mitk::StringProperty::New( QString(nodename.c_str()).append("_b0").toStdString()) ); nodes.push_back(node4); } mitk::ProgressBar::GetInstance()->Progress(); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) GetDefaultDataStorage()->Add(*nodeIt); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles).toAscii()); m_MultiWidget->RequestUpdate(); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex ; return ; } } void QmitkQBallReconstructionView::AnalyticalQBallReconstruction( mitk::DataStorage::SetOfObjects::Pointer inImages, int normalization) { try { itk::TimeProbe clock; int nrFiles = inImages->size(); if (!nrFiles) return; std::vector lambdas; float minLambda = m_Controls->m_QBallReconstructionLambdaLineEdit->text().toFloat(); lambdas.push_back(minLambda); int nLambdas = lambdas.size(); QString status; mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles*nLambdas); mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); std::vector* nodes = new std::vector(); while ( itemiter != itemiterend ) // for all items { mitk::DiffusionImage* vols = static_cast*>( (*itemiter)->GetData()); std::string nodename; (*itemiter)->GetStringProperty("name",nodename); itemiter++; // QBALL RECONSTRUCTION clock.Start(); MBI_INFO << "QBall reconstruction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "QBall reconstruction for %s", nodename.c_str()).toAscii()); for(int i=0; im_QBallReconstructionMaxLLevelComboBox->currentIndex()) { case 0: { TemplatedAnalyticalQBallReconstruction<2>(vols, currentLambda, nodename, nodes, normalization); break; } case 1: { TemplatedAnalyticalQBallReconstruction<4>(vols, currentLambda, nodename, nodes, normalization); break; } case 2: { TemplatedAnalyticalQBallReconstruction<6>(vols, currentLambda, nodename, nodes, normalization); break; } case 3: { TemplatedAnalyticalQBallReconstruction<8>(vols, currentLambda, nodename, nodes, normalization); break; } case 4: { TemplatedAnalyticalQBallReconstruction<10>(vols, currentLambda, nodename, nodes, normalization); break; } case 5: { TemplatedAnalyticalQBallReconstruction<12>(vols, currentLambda, nodename, nodes, normalization); break; } } clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s." ; mitk::ProgressBar::GetInstance()->Progress(); } } std::vector::iterator nodeIt; for(nodeIt = nodes->begin(); nodeIt != nodes->end(); ++nodeIt) GetDefaultDataStorage()->Add(*nodeIt); m_MultiWidget->RequestUpdate(); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles).toAscii()); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex ; return ; } } template void QmitkQBallReconstructionView::TemplatedAnalyticalQBallReconstruction( mitk::DiffusionImage* vols, float lambda, std::string nodename, std::vector* nodes, int normalization) { typedef itk::AnalyticalDiffusionQballReconstructionImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetGradientImage( vols->GetDirections(), vols->GetVectorImage() ); filter->SetBValue(vols->GetB_Value()); filter->SetThreshold( m_Controls->m_QBallReconstructionThreasholdEdit->text().toFloat() ); filter->SetLambda(lambda); switch(normalization) { case 0: { filter->SetNormalizationMethod(FilterType::QBAR_STANDARD); break; } case 1: { filter->SetNormalizationMethod(FilterType::QBAR_B_ZERO_B_VALUE); break; } case 2: { filter->SetNormalizationMethod(FilterType::QBAR_B_ZERO); break; } case 3: { filter->SetNormalizationMethod(FilterType::QBAR_NONE); break; } case 4: { filter->SetNormalizationMethod(FilterType::QBAR_ADC_ONLY); break; } case 5: { filter->SetNormalizationMethod(FilterType::QBAR_RAW_SIGNAL); break; } case 6: { filter->SetNormalizationMethod(FilterType::QBAR_SOLID_ANGLE); break; } case 7: { filter->SetNormalizationMethod(FilterType::QBAR_NONNEG_SOLID_ANGLE); break; } default: { filter->SetNormalizationMethod(FilterType::QBAR_STANDARD); } } filter->Update(); // ODFs TO DATATREE mitk::QBallImage::Pointer image = mitk::QBallImage::New(); image->InitializeByItk( filter->GetOutput() ); image->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); QString newname; newname = newname.append(nodename.c_str()); newname = newname.append("_QA%1").arg(normalization); SetDefaultNodeProperties(node, newname.toStdString()); nodes->push_back(node); // mitk::Image::Pointer image5 = mitk::Image::New(); // image5->InitializeByItk( filter->GetODFSumImage().GetPointer() ); // image5->SetVolume( filter->GetODFSumImage()->GetBufferPointer() ); // mitk::DataNode::Pointer node5=mitk::DataNode::New(); // node5->SetData( image5 ); // node5->SetProperty( "name", mitk::StringProperty::New( // QString(nodename.c_str()).append("_ODF").toStdString()) ); // nodes->push_back(node5); // B-Zero TO DATATREE if(m_Controls->m_OutputB0Image->isChecked()) { mitk::Image::Pointer image4 = mitk::Image::New(); image4->InitializeByItk( filter->GetBZeroImage().GetPointer() ); image4->SetVolume( filter->GetBZeroImage()->GetBufferPointer() ); mitk::DataNode::Pointer node4=mitk::DataNode::New(); node4->SetData( image4 ); node4->SetProperty( "name", mitk::StringProperty::New( QString(nodename.c_str()).append("_b0").toStdString()) ); nodes->push_back(node4); } } void QmitkQBallReconstructionView::SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name) { node->SetProperty( "ShowMaxNumber", mitk::IntProperty::New( 500 ) ); node->SetProperty( "Scaling", mitk::FloatProperty::New( 1.0 ) ); node->SetProperty( "Normalization", mitk::OdfNormalizationMethodProperty::New()); node->SetProperty( "ScaleBy", mitk::OdfScaleByProperty::New()); node->SetProperty( "IndexParam1", mitk::FloatProperty::New(2)); node->SetProperty( "IndexParam2", mitk::FloatProperty::New(1)); node->SetProperty( "visible", mitk::BoolProperty::New( true ) ); node->SetProperty( "VisibleOdfs", mitk::BoolProperty::New( false ) ); node->SetProperty ("layer", mitk::IntProperty::New(100)); node->SetProperty( "DoRefresh", mitk::BoolProperty::New( true ) ); //node->SetProperty( "opacity", mitk::FloatProperty::New(1.0f) ); node->SetProperty( "name", mitk::StringProperty::New(name) ); } //node->SetProperty( "volumerendering", mitk::BoolProperty::New( false ) ); //node->SetProperty( "use color", mitk::BoolProperty::New( true ) ); //node->SetProperty( "texture interpolation", mitk::BoolProperty::New( true ) ); //node->SetProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ); //node->SetProperty( "layer", mitk::IntProperty::New(0)); //node->SetProperty( "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ); //node->SetOpacity(1.0f); //node->SetColor(1.0,1.0,1.0); //node->SetVisibility(true); //node->SetProperty( "IsQBallVolume", mitk::BoolProperty::New( true ) ); //mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); //mitk::LevelWindow levelwindow; //// levelwindow.SetAuto( image ); //levWinProp->SetLevelWindow( levelwindow ); //node->GetPropertyList()->SetProperty( "levelwindow", levWinProp ); //// add a default rainbow lookup table for color mapping //if(!node->GetProperty("LookupTable")) //{ // mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); // vtkLookupTable* vtkLut = mitkLut->GetVtkLookupTable(); // vtkLut->SetHueRange(0.6667, 0.0); // vtkLut->SetTableRange(0.0, 20.0); // vtkLut->Build(); // mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); // mitkLutProp->SetLookupTable(mitkLut); // node->SetProperty( "LookupTable", mitkLutProp ); //} //if(!node->GetProperty("binary")) // node->SetProperty( "binary", mitk::BoolProperty::New( false ) ); //// add a default transfer function //mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); //node->SetProperty ( "TransferFunction", mitk::TransferFunctionProperty::New ( tf.GetPointer() ) ); //// set foldername as string property //mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New( name ); //node->SetProperty( "name", nameProp ); diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.h index bfc262add3..9843de4102 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.h @@ -1,119 +1,122 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: 2009-05-28 17:19:30 +0200 (Do, 28 Mai 2009) $ -Version: $Revision: 17495 $ - +Version: $Revision: 17495 $ + Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _QMITKQBALLRECONSTRUCTIONVIEW_H_INCLUDED #define _QMITKQBALLRECONSTRUCTIONVIEW_H_INCLUDED #include #include #include "ui_QmitkQBallReconstructionViewControls.h" #include "mitkDiffusionImage.h" #include #include #include typedef short DiffusionPixelType; struct QbrSelListener; /*! * \ingroup org_mitk_gui_qt_qballreconstruction_internal * * \brief QmitkQBallReconstructionView * * Document your class here. * * \sa QmitkFunctionality */ class QmitkQBallReconstructionView : public QmitkFunctionality { friend struct QbrSelListener; // this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) Q_OBJECT public: static const std::string VIEW_ID; QmitkQBallReconstructionView(); QmitkQBallReconstructionView(const QmitkQBallReconstructionView& other); virtual ~QmitkQBallReconstructionView(); virtual void CreateQtPartControl(QWidget *parent); /// \brief Creation of the connections of main and control widget virtual void CreateConnections(); /// \brief Called when the functionality is activated virtual void Activated(); virtual void Deactivated(); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); static const int nrconvkernels; protected slots: void ReconstructStandard(); //void ReconstructNormalized1(); //void ReconstructNormalized2(); //void ReconstructNonNormalized(); //void AnalyticallyReconstructStandard(); //void AnalyticallyReconstructSolidAngle(); //void AnalyticallyReconstructNonNegSolidAngle(); //void AnalyticallyReconstructAdc(); //void AnalyticallyReconstructRaw(); - + void AdvancedCheckboxClicked(); void MethodChoosen(int method); - + void Reconstruct(int method, int normalization); - + void NumericalQBallReconstruction(mitk::DataStorage::SetOfObjects::Pointer inImages, int normalization); void AnalyticalQBallReconstruction(mitk::DataStorage::SetOfObjects::Pointer inImages, int normalization); protected: + /// \brief called by QmitkFunctionality when DataManager's selection has changed + virtual void OnSelectionChanged( std::vector nodes ); + Ui::QmitkQBallReconstructionViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; template - void TemplatedAnalyticalQBallReconstruction(mitk::DiffusionImage* vols, + void TemplatedAnalyticalQBallReconstruction(mitk::DiffusionImage* vols, float lambda, std::string nodename, std::vector* nodes, int normalization); void SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name); berry::ISelectionListener::Pointer m_SelListener; berry::IStructuredSelection::ConstPointer m_CurrentSelection; }; #endif // _QMITKQBALLRECONSTRUCTIONVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionViewControls.ui index ce29e09017..f436cbda5a 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionViewControls.ui @@ -1,230 +1,253 @@ QmitkQBallReconstructionViewControls 0 0 350 385 0 0 true QmitkQBallReconstructionViewControls + + + + Data + + + + + + Diffusion Image: + + + + + + + - + + + + + + Reconstruction Advanced Settings QFrame::StyledPanel QFrame::Raised QFormLayout::AllNonFixedFieldsGrow true B0 Threshold false true 0 true Output B0-Image true Spherical Harmonics: true Maximum l-Level false true -1 true Regularization Parameter Lambda false true 0.006 2 Numerical Standard (SH) Solid Angle (SH) Constraint Solid Angle (SH) ADC-Profile only (SH) Raw Signal only (SH) TextLabel false Start Reconstruction Qt::Vertical 20 0 diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStochasticFiberTrackingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStochasticFiberTrackingView.cpp index 2db143d7d3..6b079cbe23 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStochasticFiberTrackingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStochasticFiberTrackingView.cpp @@ -1,272 +1,280 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2010-03-31 16:40:27 +0200 (Mi, 31 Mrz 2010) $ Version: $Revision: 21975 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ // Blueberry #include #include #include // Qmitk #include "QmitkStochasticFiberTrackingView.h" #include "QmitkStdMultiWidget.h" // Qt #include // MITK #include #include // VTK #include #include #include #include #include #include const std::string QmitkStochasticFiberTrackingView::VIEW_ID = "org.mitk.views.stochasticfibertracking"; const std::string id_DataManager = "org.mitk.views.datamanager"; using namespace berry; QmitkStochasticFiberTrackingView::QmitkStochasticFiberTrackingView() : QmitkFunctionality() , m_Controls( 0 ) , m_MultiWidget( NULL ) , m_DiffusionImage( NULL ) , m_SeedRoi( NULL ) { } // Destructor QmitkStochasticFiberTrackingView::~QmitkStochasticFiberTrackingView() { } void QmitkStochasticFiberTrackingView::CreateQtPartControl( QWidget *parent ) { if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkStochasticFiberTrackingViewControls; m_Controls->setupUi( parent ); connect( m_Controls->commandLinkButton, SIGNAL(clicked()), this, SLOT(DoFiberTracking()) ); connect( m_Controls->m_SeedsPerVoxelSlider, SIGNAL(valueChanged(int)), this, SLOT(OnSeedsPerVoxelChanged(int)) ); connect( m_Controls->m_MaxCacheSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(OnMaxCacheSizeChanged(int)) ); connect( m_Controls->m_MaxTractLengthSlider, SIGNAL(valueChanged(int)), this, SLOT(OnMaxTractLengthChanged(int)) ); } } void QmitkStochasticFiberTrackingView::OnSeedsPerVoxelChanged(int value) { m_Controls->m_SeedsPerVoxelLabel->setText(QString("Seeds per Voxel: ")+QString::number(value)); } void QmitkStochasticFiberTrackingView::OnMaxTractLengthChanged(int value) { m_Controls->m_MaxTractLengthLabel->setText(QString("Max. Tract Length: ")+QString::number(value)); } void QmitkStochasticFiberTrackingView::OnMaxCacheSizeChanged(int value) { m_Controls->m_MaxCacheSizeLabel->setText(QString("Max. Cache Size: ")+QString::number(value)+"GB"); } void QmitkStochasticFiberTrackingView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkStochasticFiberTrackingView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkStochasticFiberTrackingView::OnSelectionChanged( std::vector nodes ) { + m_DiffusionImageNode = NULL; m_DiffusionImage = NULL; m_SeedRoi = NULL; m_Controls->m_DiffusionImageLabel->setText("-"); m_Controls->m_RoiImageLabel->setText("-"); if(nodes.empty()) return; for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; if( node.IsNotNull() && dynamic_cast(node->GetData()) ) { if( dynamic_cast*>(node->GetData()) ) { + m_DiffusionImageNode = node; m_DiffusionImage = dynamic_cast*>(node->GetData()); m_Controls->m_DiffusionImageLabel->setText(node->GetName().c_str()); } else { bool isBinary = false; node->GetPropertyValue("binary", isBinary); if (isBinary) { m_SeedRoi = dynamic_cast(node->GetData()); m_Controls->m_RoiImageLabel->setText(node->GetName().c_str()); } } } } if(m_DiffusionImage.IsNotNull() && m_SeedRoi.IsNotNull()) m_Controls->commandLinkButton->setEnabled(true); else m_Controls->commandLinkButton->setEnabled(false); } void QmitkStochasticFiberTrackingView::DoFiberTracking() { /* get Gradients/Direction of dwi */ itk::VectorContainer< unsigned int, vnl_vector_fixed >::Pointer Pdir = m_DiffusionImage->GetDirections(); /* bValueContainer, Container includes b-values according to corresponding gradient-direction*/ PTFilterType::bValueContainerType::Pointer vecCont = PTFilterType::bValueContainerType::New(); /* for each gradient set b-Value; for 0-gradient set b-value eq. 0 */ for ( int i=0; i<(int)Pdir->size(); ++i) { vnl_vector_fixed valsGrad = Pdir->at(i); if (valsGrad.get(0) == 0 && valsGrad.get(1) == 0 && valsGrad.get(2) == 0) { //set 0-Gradient to bValue 0 vecCont->InsertElement(i,0); }else{ vecCont->InsertElement(i,m_DiffusionImage->GetB_Value()); } } /* define measurement frame (identity-matrix 3x3) */ PTFilterType::MeasurementFrameType measurement_frame = m_DiffusionImage->GetMeasurementFrame(); /* generate white matterImage (dummy?)*/ FloatImageType::Pointer wmImage = FloatImageType::New(); wmImage->SetSpacing( m_DiffusionImage->GetVectorImage()->GetSpacing() ); wmImage->SetOrigin( m_DiffusionImage->GetVectorImage()->GetOrigin() ); wmImage->SetDirection( m_DiffusionImage->GetVectorImage()->GetDirection() ); wmImage->SetLargestPossibleRegion( m_DiffusionImage->GetVectorImage()->GetLargestPossibleRegion() ); wmImage->SetBufferedRegion( wmImage->GetLargestPossibleRegion() ); wmImage->SetRequestedRegion( wmImage->GetLargestPossibleRegion() ); wmImage->Allocate(); itk::ImageRegionIterator ot(wmImage, wmImage->GetLargestPossibleRegion() ); while (!ot.IsAtEnd()) { ot.Set(1); ++ot; } /* init TractographyFilter */ PTFilterType::Pointer trackingFilter = PTFilterType::New(); trackingFilter->SetInput(m_DiffusionImage->GetVectorImage().GetPointer()); trackingFilter->SetbValues(vecCont); trackingFilter->SetGradients(Pdir); trackingFilter->SetMeasurementFrame(measurement_frame); trackingFilter->SetWhiteMatterProbabilityImageInput(wmImage); trackingFilter->SetTotalTracts(m_Controls->m_SeedsPerVoxelSlider->value()); trackingFilter->SetMaxLikelihoodCacheSize(m_Controls->m_MaxCacheSizeSlider->value()*1000); trackingFilter->SetMaxTractLength(m_Controls->m_MaxTractLengthSlider->value()); m_tractcontainer = PTFilterType::TractContainerType::New(); //itk::Image< char, 3 > mitk::ImageToItk< itk::Image< unsigned char, 3 > >::Pointer binaryImageToItk1 = mitk::ImageToItk< itk::Image< unsigned char, 3 > >::New(); binaryImageToItk1->SetInput( m_SeedRoi ); binaryImageToItk1->Update(); + vtkSmartPointer vPoints = vtkSmartPointer::New(); + vtkSmartPointer vCellArray = vtkSmartPointer::New(); + itk::ImageRegionConstIterator< BinaryImageType > it(binaryImageToItk1->GetOutput(), binaryImageToItk1->GetOutput()->GetRequestedRegion()); it.Begin(); + mitk::Geometry3D* geom = m_DiffusionImage->GetGeometry(); while(!it.IsAtEnd()) { itk::ImageConstIterator::PixelType tmpPxValue = it.Get(); if(tmpPxValue != 0){ + mitk::Point3D point; itk::ImageRegionConstIterator< BinaryImageType >::IndexType seedIdx = it.GetIndex(); trackingFilter->SetSeedIndex(seedIdx); trackingFilter->Update(); /* get results from Filter */ /* write each single tract into member container */ PTFilterType::TractContainerType::Pointer container_tmp = trackingFilter->GetOutputTractContainer(); PTFilterType::TractContainerType::Iterator elIt = container_tmp->Begin(); PTFilterType::TractContainerType::Iterator end = container_tmp->End(); + bool addTract = true; while( elIt != end ){ - PTFilterType::TractContainerType::Element tract_tmp = elIt.Value(); - m_tractcontainer->InsertElement(m_tractcontainer->Size(),tract_tmp); - ++elIt; - } - } - ++it; - } - - /* allocate the VTK Polydata to output the tracts */ - vtkSmartPointer vPoints = vtkSmartPointer::New(); - vtkSmartPointer vCellArray = vtkSmartPointer::New(); - - for( int i=0; i<(int)m_tractcontainer->Size(); i++ ) - { - mitk::Point3D point; + PTFilterType::TractContainerType::Element tract = elIt.Value(); + PTFilterType::TractContainerType::Element::ObjectType::VertexListType::ConstPointer vertexlist = tract->GetVertexList(); - PTFilterType::TractContainerType::Element tract = m_tractcontainer->GetElement(i); - PTFilterType::TractContainerType::Element::ObjectType::VertexListType::ConstPointer vertexlist = tract->GetVertexList(); - - vtkSmartPointer vPolyLine = vtkSmartPointer::New(); - for( int j=0; j<(int)vertexlist->Size(); j++) - { - PTFilterType::TractContainerType::Element::ObjectType::VertexListType::Element vertex = vertexlist->GetElement(j); - mitk::Point3D index; - index[0] = (float)vertex[0]; - index[1] = (float)vertex[1]; - index[2] = (float)vertex[2]; + vtkSmartPointer vPolyLine = vtkSmartPointer::New(); + for( int j=0; j<(int)vertexlist->Size(); j++) + { + PTFilterType::TractContainerType::Element::ObjectType::VertexListType::Element vertex = vertexlist->GetElement(j); + mitk::Point3D index; + index[0] = (float)vertex[0]; + index[1] = (float)vertex[1]; + index[2] = (float)vertex[2]; + + if (geom->IsIndexInside(index)) + { + geom->IndexToWorld(index, point); + vtkIdType id = vPoints->InsertNextPoint(point.GetDataPointer()); + vPolyLine->GetPointIds()->InsertNextId(id); + } + else + { + addTract = false; + break; + } + } - m_DiffusionImage->GetGeometry()->IndexToWorld(index, point); + if (addTract) + vCellArray->InsertNextCell(vPolyLine); - vtkIdType id = vPoints->InsertNextPoint(point.GetDataPointer()); - vPolyLine->GetPointIds()->InsertNextId(id); + ++elIt; + } } - vCellArray->InsertNextCell(vPolyLine); + ++it; } vtkSmartPointer fiberPolyData = vtkSmartPointer::New(); fiberPolyData->SetPoints(vPoints); fiberPolyData->SetLines(vCellArray); mitk::FiberBundleX::Pointer fib = mitk::FiberBundleX::New(fiberPolyData); mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); fbNode->SetData(fib); - fbNode->SetName("GroupFinberBundle"); + QString name(m_DiffusionImageNode->GetName().c_str()); + name += "_FiberBundle"; + fbNode->SetName(name.toStdString()); fbNode->SetVisibility(true); GetDataStorage()->Add(fbNode); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStochasticFiberTrackingView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStochasticFiberTrackingView.h index 34f31895ba..d6857a757b 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStochasticFiberTrackingView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStochasticFiberTrackingView.h @@ -1,102 +1,103 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2010-03-31 16:40:27 +0200 (Mi, 31 Mrz 2010) $ Version: $Revision: 21975 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef QmitkStochasticFiberTrackingView_h #define QmitkStochasticFiberTrackingView_h #include #include #include #include "ui_QmitkStochasticFiberTrackingViewControls.h" #include #include #include #include #include #include #include #include //define the input/output types typedef itk::VectorImage< short int, 3 > DWIVectorImageType; typedef itk::Image< float, 3 > FloatImageType; typedef itk::Image< unsigned int, 3 > CImageType; typedef itk::StochasticTractographyFilter< DWIVectorImageType, FloatImageType, CImageType > PTFilterType; typedef itk::DTITubeSpatialObject<3> DTITubeType; typedef itk::DTITubeSpatialObjectPoint<3> DTITubePointType; typedef itk::SceneSpatialObject<3> SceneSpatialObjectType; /*! \brief QmitkFiberTrackingView \warning This application module is not yet documented. Use "svn blame/praise/annotate" and ask the author to provide basic documentation. \sa QmitkFunctionality \ingroup Functionalities */ class QmitkStochasticFiberTrackingView : public QmitkFunctionality { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; QmitkStochasticFiberTrackingView(); virtual ~QmitkStochasticFiberTrackingView(); virtual void CreateQtPartControl(QWidget *parent); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); protected slots: void DoFiberTracking(); protected: /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged( std::vector nodes ); Ui::QmitkStochasticFiberTrackingViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; protected slots: void OnSeedsPerVoxelChanged(int value); void OnMaxTractLengthChanged(int value); void OnMaxCacheSizeChanged(int value); private: mitk::Image::Pointer m_SeedRoi; mitk::DiffusionImage::Pointer m_DiffusionImage; + mitk::DataNode::Pointer m_DiffusionImageNode; typedef itk::Image< unsigned char, 3 > BinaryImageType; PTFilterType::TractContainerType::Pointer m_tractcontainer; }; #endif // _QMITKFIBERTRACKINGVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.cpp index fbe44bf130..12d4575f1f 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.cpp @@ -1,1377 +1,1339 @@ -/*========================================================================= + /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: 2009-05-28 17:19:30 +0200 (Do, 28 Mai 2009) $ Version: $Revision: 17495 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "QmitkTensorReconstructionView.h" #include "mitkDiffusionImagingConfigure.h" // qt includes #include #include #include #include #include // itk includes #include "itkTimeProbe.h" //#include "itkTensor.h" // mitk includes #include "mitkProgressBar.h" #include "mitkStatusBar.h" #include "mitkNodePredicateDataType.h" #include "QmitkDataStorageComboBox.h" #include "QmitkStdMultiWidget.h" #include "mitkTeemDiffusionTensor3DReconstructionImageFilter.h" #include "itkDiffusionTensor3DReconstructionImageFilter.h" #include "itkTensorImageToDiffusionImageFilter.h" #include "itkPointShell.h" #include "itkVector.h" #include "itkB0ImageExtractionImageFilter.h" +#include "itkTensorReconstructionWithEigenvalueCorrectionFilter.h" +//#include "itkFreeWaterEliminationFilter.h" #include "mitkProperties.h" #include "mitkDataNodeObject.h" #include "mitkOdfNormalizationMethodProperty.h" #include "mitkOdfScaleByProperty.h" #include "mitkDiffusionImageMapper.h" #include "mitkLookupTableProperty.h" #include "mitkLookupTable.h" #include "mitkImageStatisticsHolder.h" -#include "berryIStructuredSelection.h" -#include "berryIWorkbenchWindow.h" -#include "berryISelectionService.h" - #include #include const std::string QmitkTensorReconstructionView::VIEW_ID = "org.mitk.views.tensorreconstruction"; #define DI_INFO MITK_INFO("DiffusionImaging") typedef float TTensorPixelType; typedef itk::DiffusionTensor3D< TTensorPixelType > TensorPixelType; typedef itk::Image< TensorPixelType, 3 > TensorImageType; using namespace berry; struct TrSelListener : ISelectionListener { berryObjectMacro(TrSelListener); TrSelListener(QmitkTensorReconstructionView* view) { m_View = view; } void DoSelectionChanged(ISelection::ConstPointer selection) { + /* // if(!m_View->IsVisible()) // return; // save current selection in member variable m_View->m_CurrentSelection = selection.Cast(); // do something with the selected items if(m_View->m_CurrentSelection) { bool foundDwiVolume = false; bool foundTensorVolume = false; // iterate selection for (IStructuredSelection::iterator i = m_View->m_CurrentSelection->Begin(); i != m_View->m_CurrentSelection->End(); ++i) { // extract datatree node if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) { mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - mitk::BaseData* nodeData = node->GetData(); - if( nodeData != NULL ) + // only look at interesting types + if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) { - // only look at interesting types - if(QString("DiffusionImage").compare(nodeData->GetNameOfClass())==0) - { - foundDwiVolume = true; - } + foundDwiVolume = true; + } - // only look at interesting types - if(QString("TensorImage").compare(nodeData->GetNameOfClass())==0) - { - foundTensorVolume = true; - } + // only look at interesting types + if(QString("TensorImage").compare(node->GetData()->GetNameOfClass())==0) + { + foundTensorVolume = true; } } } m_View->m_Controls->m_ItkReconstruction->setEnabled(foundDwiVolume); m_View->m_Controls->m_TeemReconstruction->setEnabled(foundDwiVolume); + m_View->m_Controls->m_ReconstructionWithCorrection->setEnabled(foundDwiVolume); m_View->m_Controls->m_TensorsToDWIButton->setEnabled(foundTensorVolume); m_View->m_Controls->m_TensorsToQbiButton->setEnabled(foundTensorVolume); m_View->m_Controls->m_ResidualButton->setEnabled(foundDwiVolume && foundTensorVolume); m_View->m_Controls->m_PercentagesOfOutliers->setEnabled(foundDwiVolume && foundTensorVolume); m_View->m_Controls->m_PerSliceView->setEnabled(foundDwiVolume && foundTensorVolume); } + */ } void SelectionChanged(IWorkbenchPart::Pointer part, ISelection::ConstPointer selection) { // check, if selection comes from datamanager if (part) { QString partname(part->GetPartName().c_str()); if(partname.compare("Datamanager")==0) { // apply selection DoSelectionChanged(selection); } } } QmitkTensorReconstructionView* m_View; }; + QmitkTensorReconstructionView::QmitkTensorReconstructionView() -: QmitkFunctionality(), -m_Controls(NULL), -m_MultiWidget(NULL) + : QmitkFunctionality(), + m_Controls(NULL), + m_MultiWidget(NULL) { - - if(m_CurrentSelection) - { - mitk::DataStorage::SetOfObjects::Pointer set = - mitk::DataStorage::SetOfObjects::New(); - - mitk::DiffusionImage::Pointer diffImage - = mitk::DiffusionImage::New(); - - - TensorImageType::Pointer tensorImage; - - std::string nodename; - - - for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); - i != m_CurrentSelection->End(); - ++i) - { - - if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) - { - mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - - mitk::BaseData* nodeData = node->GetData(); - if(nodeData) - { - if(QString("DiffusionImage").compare(nodeData->GetNameOfClass())==0) - { - diffImage = static_cast*>((node)->GetData()); - } - else if((QString("TensorImage").compare(nodeData->GetNameOfClass())==0)) - { - mitk::TensorImage* mitkVol; - mitkVol = static_cast((node)->GetData()); - mitk::CastToItkImage(mitkVol, tensorImage); - node->GetStringProperty("name", nodename); - } - } - } - } - - } - - + m_DiffusionImages = mitk::DataStorage::SetOfObjects::New(); + m_TensorImages = mitk::DataStorage::SetOfObjects::New(); } QmitkTensorReconstructionView::QmitkTensorReconstructionView(const QmitkTensorReconstructionView& other) { Q_UNUSED(other) throw std::runtime_error("Copy constructor not implemented"); } QmitkTensorReconstructionView::~QmitkTensorReconstructionView() { - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->RemovePostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); + } void QmitkTensorReconstructionView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkTensorReconstructionViewControls; m_Controls->setupUi(parent); this->CreateConnections(); QStringList items; items << "LLS (Linear Least Squares)" - << "MLE (Maximum Likelihood)" - << "NLS (Nonlinear Least Squares)" - << "WLS (Weighted Least Squares)"; + << "MLE (Maximum Likelihood)" + << "NLS (Nonlinear Least Squares)" + << "WLS (Weighted Least Squares)"; m_Controls->m_TensorEstimationTeemEstimationMethodCombo->addItems(items); m_Controls->m_TensorEstimationTeemEstimationMethodCombo->setCurrentIndex(0); m_Controls->m_TensorEstimationManualThreashold->setChecked(false); m_Controls->m_TensorEstimationTeemSigmaEdit->setText("NaN"); m_Controls->m_TensorEstimationTeemNumItsSpin->setValue(1); m_Controls->m_TensorEstimationTeemFuzzyEdit->setText("0.0"); m_Controls->m_TensorEstimationTeemMinValEdit->setText("1.0"); m_Controls->m_TensorEstimationTeemNumItsLabel_2->setEnabled(true); m_Controls->m_TensorEstimationTeemNumItsSpin->setEnabled(true); m_Controls->m_TensorsToDWIBValueEdit->setText("1000"); Advanced1CheckboxClicked(); Advanced2CheckboxClicked(); + Advanced3CheckboxClicked(); TeemCheckboxClicked(); #ifndef DIFFUSION_IMAGING_EXTENDED m_Controls->m_TeemToggle->setVisible(false); #endif // define data type for combobox //m_Controls->m_ImageSelector->SetDataStorage( this->GetDefaultDataStorage() ); //m_Controls->m_ImageSelector->SetPredicate( mitk::NodePredicateDataType::New("DiffusionImage") ); } - - m_SelListener = berry::ISelectionListener::Pointer(new TrSelListener(this)); - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddPostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); - berry::ISelection::ConstPointer sel( - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); - m_CurrentSelection = sel.Cast(); - m_SelListener.Cast()->DoSelectionChanged(sel); - - - - - - } void QmitkTensorReconstructionView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { - berry::ISelection::ConstPointer sel( - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); - m_CurrentSelection = sel.Cast(); - m_SelListener.Cast()->DoSelectionChanged(sel); m_MultiWidget = &stdMultiWidget; } void QmitkTensorReconstructionView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkTensorReconstructionView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_TeemToggle), SIGNAL(clicked()), this, SLOT(TeemCheckboxClicked()) ); connect( (QObject*)(m_Controls->m_ItkReconstruction), SIGNAL(clicked()), this, SLOT(ItkReconstruction()) ); connect( (QObject*)(m_Controls->m_TeemReconstruction), SIGNAL(clicked()), this, SLOT(TeemReconstruction()) ); + connect( (QObject*)(m_Controls->m_ReconstructionWithCorrection), SIGNAL(clicked()), this, SLOT(ReconstructionWithCorrection()) ); connect( (QObject*)(m_Controls->m_TensorEstimationTeemEstimationMethodCombo), SIGNAL(currentIndexChanged(int)), this, SLOT(MethodChoosen(int)) ); connect( (QObject*)(m_Controls->m_Advanced1), SIGNAL(clicked()), this, SLOT(Advanced1CheckboxClicked()) ); connect( (QObject*)(m_Controls->m_Advanced2), SIGNAL(clicked()), this, SLOT(Advanced2CheckboxClicked()) ); + connect( (QObject*)(m_Controls->m_Advanced3), SIGNAL(clicked()), this, SLOT(Advanced3CheckboxClicked()) ); connect( (QObject*)(m_Controls->m_TensorEstimationManualThreashold), SIGNAL(clicked()), this, SLOT(ManualThresholdClicked()) ); connect( (QObject*)(m_Controls->m_TensorsToDWIButton), SIGNAL(clicked()), this, SLOT(TensorsToDWI()) ); connect( (QObject*)(m_Controls->m_TensorsToQbiButton), SIGNAL(clicked()), this, SLOT(TensorsToQbi()) ); connect( (QObject*)(m_Controls->m_ResidualButton), SIGNAL(clicked()), this, SLOT(ResidualCalculation()) ); connect( (QObject*)(m_Controls->m_PerSliceView), SIGNAL(pointSelected(int, int)), this, SLOT(ResidualClicked(int, int)) ); } } void QmitkTensorReconstructionView::ResidualClicked(int slice, int volume) { // Use image coord to reset crosshair // Find currently selected diffusion image // Update Label // to do: This position should be modified in order to skip B0 volumes that are not taken into account // when calculating residuals // Find the diffusion image mitk::DiffusionImage* diffImage; - - mitk::DataNodeObject::Pointer nodeObj; mitk::DataNode::Pointer correctNode; mitk::Geometry3D* geometry; - for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); - i != m_CurrentSelection->End(); - ++i) + if (m_DiffusionImage.IsNotNull()) { - if (nodeObj = i->Cast()) - { - mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - // process only on valid nodes - mitk::BaseData* nodeData = node->GetData(); - if(nodeData) - { - if(QString("DiffusionImage").compare(nodeData->GetNameOfClass())==0) - { - diffImage = static_cast*>(nodeData); + diffImage = static_cast*>(m_DiffusionImage->GetData()); - geometry = diffImage->GetGeometry(); + geometry = diffImage->GetGeometry(); - // Remember the node whose display index must be updated - correctNode = mitk::DataNode::New(); - correctNode = node; - } - } - } + // Remember the node whose display index must be updated + correctNode = mitk::DataNode::New(); + correctNode = m_DiffusionImage; } if(diffImage != NULL) { typedef vnl_vector_fixed< double, 3 > GradientDirectionType; typedef itk::VectorContainer< unsigned int, - GradientDirectionType > GradientDirectionContainerType; + GradientDirectionType > GradientDirectionContainerType; GradientDirectionContainerType::Pointer dirs = diffImage->GetDirections(); - - for(int i=0; iSize() && i<=volume; i++) { GradientDirectionType grad = dirs->ElementAt(i); // check if image is b0 weighted if(fabs(grad[0]) < 0.001 && fabs(grad[1]) < 0.001 && fabs(grad[2]) < 0.001) { volume++; } } - QString pos = "Volume: "; pos.append(QString::number(volume)); pos.append(", Slice: "); pos.append(QString::number(slice)); m_Controls->m_PositionLabel->setText(pos); - - - if(correctNode) { - int oldDisplayVal; correctNode->GetIntProperty("DisplayChannel", oldDisplayVal); std::string oldVal = QString::number(oldDisplayVal).toStdString(); std::string newVal = QString::number(volume).toStdString(); - correctNode->SetIntProperty("DisplayChannel",volume); - correctNode->SetSelected(true); - this->FirePropertyChanged("DisplayChannel", oldVal, newVal); - - correctNode->UpdateOutputInformation(); mitk::Point3D p3 = m_MultiWidget->GetCrossPosition(); itk::Index<3> ix; geometry->WorldToIndex(p3, ix); - // ix[2] = slice; + // ix[2] = slice; mitk::Vector3D vec; vec[0] = ix[0]; vec[1] = ix[1]; vec[2] = slice; - mitk::Vector3D v3New; geometry->IndexToWorld(vec, v3New); - - mitk::Point3D origin = geometry->GetOrigin(); - mitk::Point3D p3New; p3New[0] = v3New[0] + origin[0]; p3New[1] = v3New[1] + origin[1]; p3New[2] = v3New[2] + origin[2]; - - m_MultiWidget->MoveCrossToPosition(p3New); - - m_MultiWidget->RequestUpdate(); - - } - - - } - } void QmitkTensorReconstructionView::TeemCheckboxClicked() { m_Controls->groupBox_3->setVisible(m_Controls-> - m_TeemToggle->isChecked()); + m_TeemToggle->isChecked()); } void QmitkTensorReconstructionView::Advanced1CheckboxClicked() { bool check = m_Controls-> - m_Advanced1->isChecked(); + m_Advanced1->isChecked(); m_Controls->frame->setVisible(check); } void QmitkTensorReconstructionView::Advanced2CheckboxClicked() { bool check = m_Controls-> - m_Advanced2->isChecked(); + m_Advanced2->isChecked(); m_Controls->frame_2->setVisible(check); } +void QmitkTensorReconstructionView::Advanced3CheckboxClicked() +{ + bool check = m_Controls-> + m_Advanced3->isChecked(); + + m_Controls->frame_6->setVisible(check); +} + void QmitkTensorReconstructionView::ManualThresholdClicked() { m_Controls->m_TensorReconstructionThreasholdEdit_2->setEnabled( - m_Controls->m_TensorEstimationManualThreashold->isChecked()); + m_Controls->m_TensorEstimationManualThreashold->isChecked()); } void QmitkTensorReconstructionView::Activated() { QmitkFunctionality::Activated(); } void QmitkTensorReconstructionView::Deactivated() { QmitkFunctionality::Deactivated(); } void QmitkTensorReconstructionView::MethodChoosen(int method) { m_Controls->m_TensorEstimationTeemNumItsLabel_2->setEnabled(method==3); m_Controls->m_TensorEstimationTeemNumItsSpin->setEnabled(method==3); } void QmitkTensorReconstructionView::ResidualCalculation() { // Extract dwi and dti from current selection // In case of multiple selections, take the first one, since taking all combinations is not meaningful - - - if(m_CurrentSelection) - { - mitk::DataStorage::SetOfObjects::Pointer set = + mitk::DataStorage::SetOfObjects::Pointer set = mitk::DataStorage::SetOfObjects::New(); - mitk::DiffusionImage::Pointer diffImage - = mitk::DiffusionImage::New(); + mitk::DiffusionImage::Pointer diffImage + = mitk::DiffusionImage::New(); - TensorImageType::Pointer tensorImage; - - std::string nodename; - - - for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); - i != m_CurrentSelection->End(); - ++i) - { - - if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) - { - mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - - // process only on valid nodes - mitk::BaseData* nodeData = node->GetData(); - if(nodeData) - { - if(QString("DiffusionImage").compare(nodeData->GetNameOfClass())==0) - { - diffImage = static_cast*>((node)->GetData()); - } - else if((QString("TensorImage").compare(nodeData->GetNameOfClass())==0)) - { - mitk::TensorImage* mitkVol; - mitkVol = static_cast(nodeData); - mitk::CastToItkImage(mitkVol, tensorImage); - node->GetStringProperty("name", nodename); - } - } - } - } + TensorImageType::Pointer tensorImage; + std::string nodename; + if(m_DiffusionImage.IsNotNull()) + { + diffImage = static_cast*>(m_DiffusionImage->GetData()); + } + else + return; + if(m_TensorImage.IsNotNull()) + { + mitk::TensorImage* mitkVol; + mitkVol = static_cast(m_TensorImage->GetData()); + mitk::CastToItkImage(mitkVol, tensorImage); + m_TensorImage->GetStringProperty("name", nodename); + } + else + return; - typedef itk::TensorImageToDiffusionImageFilter< + typedef itk::TensorImageToDiffusionImageFilter< TTensorPixelType, DiffusionPixelType > FilterType; - FilterType::GradientListType gradientList; - mitk::DiffusionImage::GradientDirectionContainerType* gradients - = diffImage->GetDirections(); + FilterType::GradientListType gradientList; + mitk::DiffusionImage::GradientDirectionContainerType* gradients + = diffImage->GetDirections(); - // Copy gradients vectors from gradients to gradientList - for(int i=0; iSize(); i++) - { - mitk::DiffusionImage::GradientDirectionType vec = gradients->at(i); - itk::Vector grad; + // Copy gradients vectors from gradients to gradientList + for(int i=0; iSize(); i++) + { + mitk::DiffusionImage::GradientDirectionType vec = gradients->at(i); + itk::Vector grad; - grad[0] = vec[0]; - grad[1] = vec[1]; - grad[2] = vec[2]; + grad[0] = vec[0]; + grad[1] = vec[1]; + grad[2] = vec[2]; - gradientList.push_back(grad); - } + gradientList.push_back(grad); + } - // Find the min and the max values from a baseline image - mitk::ImageStatisticsHolder *stats = diffImage->GetStatistics(); + // Find the min and the max values from a baseline image + mitk::ImageStatisticsHolder *stats = diffImage->GetStatistics(); - //Initialize filter that calculates the modeled diffusion weighted signals - FilterType::Pointer filter = FilterType::New(); - filter->SetInput( tensorImage ); - filter->SetBValue(diffImage->GetB_Value()); - filter->SetGradientList(gradientList); - filter->SetMin(stats->GetScalarValueMin()); - filter->SetMax(500); - filter->Update(); + //Initialize filter that calculates the modeled diffusion weighted signals + FilterType::Pointer filter = FilterType::New(); + filter->SetInput( tensorImage ); + filter->SetBValue(diffImage->GetB_Value()); + filter->SetGradientList(gradientList); + filter->SetMin(stats->GetScalarValueMin()); + filter->SetMax(500); + filter->Update(); - // TENSORS TO DATATREE - mitk::DiffusionImage::Pointer image = mitk::DiffusionImage::New(); - image->SetVectorImage( filter->GetOutput() ); - image->SetB_Value(diffImage->GetB_Value()); - image->SetDirections(gradientList); - image->SetOriginalDirections(gradientList); - image->InitializeFromVectorImage(); - mitk::DataNode::Pointer node = mitk::DataNode::New(); - node->SetData( image ); - mitk::DiffusionImageMapper::SetDefaultProperties(node); + // TENSORS TO DATATREE + mitk::DiffusionImage::Pointer image = mitk::DiffusionImage::New(); + image->SetVectorImage( filter->GetOutput() ); + image->SetB_Value(diffImage->GetB_Value()); + image->SetDirections(gradientList); + image->SetOriginalDirections(gradientList); + image->InitializeFromVectorImage(); + mitk::DataNode::Pointer node = mitk::DataNode::New(); + node->SetData( image ); + mitk::DiffusionImageMapper::SetDefaultProperties(node); - QString newname; - newname = newname.append(nodename.c_str()); - newname = newname.append("_dwi"); - node->SetName(newname.toAscii()); + QString newname; + newname = newname.append(nodename.c_str()); + newname = newname.append("_dwi"); + node->SetName(newname.toAscii()); - GetDefaultDataStorage()->Add(node); + GetDefaultDataStorage()->Add(node); - std::vector b0Indices = image->GetB0Indices(); + std::vector b0Indices = image->GetB0Indices(); - typedef itk::ResidualImageFilter ResidualImageFilterType; + typedef itk::ResidualImageFilter ResidualImageFilterType; - ResidualImageFilterType::Pointer residualFilter = ResidualImageFilterType::New(); - residualFilter->SetInput(diffImage->GetVectorImage()); - residualFilter->SetSecondDiffusionImage(image->GetVectorImage()); - residualFilter->SetGradients(gradients); - residualFilter->SetB0Index(b0Indices[0]); - residualFilter->SetB0Threshold(30); - residualFilter->Update(); + ResidualImageFilterType::Pointer residualFilter = ResidualImageFilterType::New(); + residualFilter->SetInput(diffImage->GetVectorImage()); + residualFilter->SetSecondDiffusionImage(image->GetVectorImage()); + residualFilter->SetGradients(gradients); + residualFilter->SetB0Index(b0Indices[0]); + residualFilter->SetB0Threshold(30); + residualFilter->Update(); - itk::Image::Pointer residualImage = itk::Image::New(); - residualImage = residualFilter->GetOutput(); + itk::Image::Pointer residualImage = itk::Image::New(); + residualImage = residualFilter->GetOutput(); - mitk::Image::Pointer mitkResImg = mitk::Image::New(); + mitk::Image::Pointer mitkResImg = mitk::Image::New(); - mitk::CastToMitkImage(residualImage, mitkResImg); + mitk::CastToMitkImage(residualImage, mitkResImg); - stats = mitkResImg->GetStatistics(); - float min = stats->GetScalarValueMin(); - float max = stats->GetScalarValueMax(); + stats = mitkResImg->GetStatistics(); + float min = stats->GetScalarValueMin(); + float max = stats->GetScalarValueMax(); - mitk::LookupTableProperty::Pointer lutProp = mitk::LookupTableProperty::New(); - mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); + mitk::LookupTableProperty::Pointer lutProp = mitk::LookupTableProperty::New(); + mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); - vtkSmartPointer lookupTable = - vtkSmartPointer::New(); + vtkSmartPointer lookupTable = + vtkSmartPointer::New(); - lookupTable->SetTableRange(min, max); + lookupTable->SetTableRange(min, max); - // If you don't want to use the whole color range, you can use - // SetValueRange, SetHueRange, and SetSaturationRange - lookupTable->Build(); + // If you don't want to use the whole color range, you can use + // SetValueRange, SetHueRange, and SetSaturationRange + lookupTable->Build(); - int size = lookupTable->GetTable()->GetSize(); + int size = lookupTable->GetTable()->GetSize(); - vtkSmartPointer reversedlookupTable = - vtkSmartPointer::New(); - reversedlookupTable->SetTableRange(min+1, max); - reversedlookupTable->Build(); + vtkSmartPointer reversedlookupTable = + vtkSmartPointer::New(); + reversedlookupTable->SetTableRange(min+1, max); + reversedlookupTable->Build(); - for(int i=0; i<256; i++) - { - double* rgba = reversedlookupTable->GetTableValue(255-i); + for(int i=0; i<256; i++) + { + double* rgba = reversedlookupTable->GetTableValue(255-i); - lookupTable->SetTableValue(i, rgba[0], rgba[1], rgba[2], rgba[3]); - } + lookupTable->SetTableValue(i, rgba[0], rgba[1], rgba[2], rgba[3]); + } - lut->SetVtkLookupTable(lookupTable); - lutProp->SetLookupTable(lut); + lut->SetVtkLookupTable(lookupTable); + lutProp->SetLookupTable(lut); - // Create lookuptable + // Create lookuptable - mitk::DataNode::Pointer resNode=mitk::DataNode::New(); - resNode->SetData( mitkResImg ); - resNode->SetName("Residual Image"); + mitk::DataNode::Pointer resNode=mitk::DataNode::New(); + resNode->SetData( mitkResImg ); + resNode->SetName("Residual Image"); - resNode->SetProperty("LookupTable", lutProp); + resNode->SetProperty("LookupTable", lutProp); - bool b; - resNode->GetBoolProperty("use color", b); - resNode->SetBoolProperty("use color", false); + bool b; + resNode->GetBoolProperty("use color", b); + resNode->SetBoolProperty("use color", false); - GetDefaultDataStorage()->Add(resNode); + GetDefaultDataStorage()->Add(resNode); - m_MultiWidget->RequestUpdate(); + m_MultiWidget->RequestUpdate(); - // Draw Graph - std::vector means = residualFilter->GetMeans(); - std::vector q1s = residualFilter->GetQ1(); - std::vector q3s = residualFilter->GetQ3(); - std::vector percentagesOfOUtliers = residualFilter->GetPercentagesOfOutliers(); + // Draw Graph + std::vector means = residualFilter->GetMeans(); + std::vector q1s = residualFilter->GetQ1(); + std::vector q3s = residualFilter->GetQ3(); + std::vector percentagesOfOUtliers = residualFilter->GetPercentagesOfOutliers(); - m_Controls->m_ResidualAnalysis->SetMeans(means); - m_Controls->m_ResidualAnalysis->SetQ1(q1s); - m_Controls->m_ResidualAnalysis->SetQ3(q3s); - m_Controls->m_ResidualAnalysis->SetPercentagesOfOutliers(percentagesOfOUtliers); + m_Controls->m_ResidualAnalysis->SetMeans(means); + m_Controls->m_ResidualAnalysis->SetQ1(q1s); + m_Controls->m_ResidualAnalysis->SetQ3(q3s); + m_Controls->m_ResidualAnalysis->SetPercentagesOfOutliers(percentagesOfOUtliers); - if(m_Controls->m_PercentagesOfOutliers->isChecked()) - { - m_Controls->m_ResidualAnalysis->DrawPercentagesOfOutliers(); - } - else - { - m_Controls->m_ResidualAnalysis->DrawMeans(); - } + if(m_Controls->m_PercentagesOfOutliers->isChecked()) + { + m_Controls->m_ResidualAnalysis->DrawPercentagesOfOutliers(); + } + else + { + m_Controls->m_ResidualAnalysis->DrawMeans(); + } - // Draw Graph for volumes per slice in the QGraphicsView - std::vector< std::vector > outliersPerSlice = residualFilter->GetOutliersPerSlice(); - int xSize = outliersPerSlice.size(); - if(xSize == 0) - { - return; - } - int ySize = outliersPerSlice[0].size(); + // Draw Graph for volumes per slice in the QGraphicsView + std::vector< std::vector > outliersPerSlice = residualFilter->GetOutliersPerSlice(); + int xSize = outliersPerSlice.size(); + if(xSize == 0) + { + return; + } + int ySize = outliersPerSlice[0].size(); - // Find maximum in outliersPerSlice - double maxOutlier= 0.0; - for(int i=0; imaxOutlier) { - if(outliersPerSlice[i][j]>maxOutlier) - { - maxOutlier = outliersPerSlice[i][j]; - } + maxOutlier = outliersPerSlice[i][j]; } } + } - // Create some QImage - QImage qImage(xSize, ySize, QImage::Format_RGB32); - QImage legend(1, 256, QImage::Format_RGB32); - QRgb value; + // Create some QImage + QImage qImage(xSize, ySize, QImage::Format_RGB32); + QImage legend(1, 256, QImage::Format_RGB32); + QRgb value; - vtkSmartPointer lookup = - vtkSmartPointer::New(); + vtkSmartPointer lookup = + vtkSmartPointer::New(); - lookup->SetTableRange(0.0, maxOutlier); - lookup->Build(); + lookup->SetTableRange(0.0, maxOutlier); + lookup->Build(); - reversedlookupTable->SetTableRange(0, maxOutlier); - reversedlookupTable->Build(); + reversedlookupTable->SetTableRange(0, maxOutlier); + reversedlookupTable->Build(); - for(int i=0; i<256; i++) - { - double* rgba = reversedlookupTable->GetTableValue(255-i); - lookup->SetTableValue(i, rgba[0], rgba[1], rgba[2], rgba[3]); - } + for(int i=0; i<256; i++) + { + double* rgba = reversedlookupTable->GetTableValue(255-i); + lookup->SetTableValue(i, rgba[0], rgba[1], rgba[2], rgba[3]); + } - // Fill qImage - for(int i=0; iMapValue(out); - int r, g, b; - r = _rgba[0]; - g = _rgba[1]; - b = _rgba[2]; + double out = outliersPerSlice[i][j]; - value = qRgb(r, g, b); - - qImage.setPixel(i,j,value); - - } - } - - for(int i=0; i<256; i++) - { - double* rgba = lookup->GetTableValue(i); + unsigned char *_rgba = lookup->MapValue(out); int r, g, b; - r = rgba[0]*255; - g = rgba[1]*255; - b = rgba[2]*255; + r = _rgba[0]; + g = _rgba[1]; + b = _rgba[2]; + value = qRgb(r, g, b); - legend.setPixel(0,255-i,value); - } - QString upper = QString::number(maxOutlier, 'g', 3); - upper.append(" %"); - QString lower = QString::number(0.0); - lower.append(" %"); - m_Controls->m_UpperLabel->setText(upper); - m_Controls->m_LowerLabel->setText(lower); + qImage.setPixel(i,j,value); - QGraphicsScene* scene = new QGraphicsScene; - QGraphicsScene* scene2 = new QGraphicsScene; + } + } + for(int i=0; i<256; i++) + { + double* rgba = lookup->GetTableValue(i); + int r, g, b; + r = rgba[0]*255; + g = rgba[1]*255; + b = rgba[2]*255; + value = qRgb(r, g, b); + legend.setPixel(0,255-i,value); + } - QPixmap pixmap(QPixmap::fromImage(qImage)); - QGraphicsPixmapItem *item = new QGraphicsPixmapItem( pixmap, 0, scene); - item->scale(10.0, 3.0); + QString upper = QString::number(maxOutlier, 'g', 3); + upper.append(" %"); + QString lower = QString::number(0.0); + lower.append(" %"); + m_Controls->m_UpperLabel->setText(upper); + m_Controls->m_LowerLabel->setText(lower); - QPixmap pixmap2(QPixmap::fromImage(legend)); - QGraphicsPixmapItem *item2 = new QGraphicsPixmapItem( pixmap2, 0, scene2); - item2->scale(20.0, 1.0); + QGraphicsScene* scene = new QGraphicsScene; + QGraphicsScene* scene2 = new QGraphicsScene; - m_Controls->m_PerSliceView->SetResidualPixmapItem(item); + QPixmap pixmap(QPixmap::fromImage(qImage)); + QGraphicsPixmapItem *item = new QGraphicsPixmapItem( pixmap, 0, scene); + item->scale(10.0, 3.0); + QPixmap pixmap2(QPixmap::fromImage(legend)); + QGraphicsPixmapItem *item2 = new QGraphicsPixmapItem( pixmap2, 0, scene2); + item2->scale(20.0, 1.0); - m_Controls->m_PerSliceView->setScene(scene); - m_Controls->m_LegendView->setScene(scene2); - m_Controls->m_PerSliceView->show(); - m_Controls->m_PerSliceView->repaint(); + m_Controls->m_PerSliceView->SetResidualPixmapItem(item); - m_Controls->m_LegendView->setHorizontalScrollBarPolicy ( Qt::ScrollBarAlwaysOff ); - m_Controls->m_LegendView->setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOff ); - m_Controls->m_LegendView->show(); - m_Controls->m_LegendView->repaint(); - } + m_Controls->m_PerSliceView->setScene(scene); + m_Controls->m_LegendView->setScene(scene2); + m_Controls->m_PerSliceView->show(); + m_Controls->m_PerSliceView->repaint(); + m_Controls->m_LegendView->setHorizontalScrollBarPolicy ( Qt::ScrollBarAlwaysOff ); + m_Controls->m_LegendView->setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOff ); + m_Controls->m_LegendView->show(); + m_Controls->m_LegendView->repaint(); } void QmitkTensorReconstructionView::ItkReconstruction() { Reconstruct(0); } void QmitkTensorReconstructionView::TeemReconstruction() { Reconstruct(1); } +void QmitkTensorReconstructionView::ReconstructionWithCorrection() +{ + Reconstruct(2); +} + void QmitkTensorReconstructionView::Reconstruct(int method) { - if (m_CurrentSelection) + if(method == 0) + ItkTensorReconstruction(m_DiffusionImages); + + + if(method == 1) + TeemTensorReconstruction(m_DiffusionImages); + + if(method == 2) { - mitk::DataStorage::SetOfObjects::Pointer set = - mitk::DataStorage::SetOfObjects::New(); + TensorReconstructionWithCorr(m_DiffusionImages); + } - int at = 0; - for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); - i != m_CurrentSelection->End(); - ++i) - { + +} - if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) - { - mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) - { - set->InsertElement(at++, node); - } - } - } +void QmitkTensorReconstructionView::TensorReconstructionWithCorr +(mitk::DataStorage::SetOfObjects::Pointer inImages) +{ + try + { + itk::TimeProbe clock; - if(method == 0) - { - ItkTensorReconstruction(set); - } + int nrFiles = inImages->size(); + if (!nrFiles) return; + + QString status; + mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles); - if(method == 1) + mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); + mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); + + std::vector nodes; + while ( itemiter != itemiterend ) // for all items { - TeemTensorReconstruction(set); + + mitk::DiffusionImage* vols = + static_cast*>( + (*itemiter)->GetData()); + + std::string nodename; + (*itemiter)->GetStringProperty("name", nodename); + ++itemiter; + + // TENSOR RECONSTRUCTION + clock.Start(); + MBI_INFO << "Tensor reconstruction with correction for negative eigenvalues"; + mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( + "Tensor reconstruction for %s", nodename.c_str()).toAscii()); + + typedef itk::TensorReconstructionWithEigenvalueCorrectionFilter< + DiffusionPixelType, TTensorPixelType > ReconstructionFilter; + + float b0Threshold = m_Controls->m_ReconstructionThreshold->text().toFloat(); + + ReconstructionFilter::Pointer reconFilter = ReconstructionFilter::New(); + reconFilter->SetGradientImage( vols->GetDirections(), vols->GetVectorImage() ); + reconFilter->SetBValue(vols->GetB_Value()); + reconFilter->SetB0Threshold(b0Threshold); + reconFilter->Update(); + + itk::Image,3>::Pointer outputTensorImg = reconFilter->GetOutput(); + + + mitk::TensorImage::Pointer image = mitk::TensorImage::New(); + image->InitializeByItk( outputTensorImg.GetPointer() ); + image->SetVolume( outputTensorImg->GetBufferPointer() ); + mitk::DataNode::Pointer node=mitk::DataNode::New(); + node->SetData( image ); + + QString newname; + newname = newname.append(nodename.c_str()); + newname = newname.append("_dti"); + + SetDefaultNodeProperties(node, newname.toStdString()); + nodes.push_back(node); + + + mitk::ProgressBar::GetInstance()->Progress(); + } + std::vector::iterator nodeIt; + for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) + GetDefaultDataStorage()->Add(*nodeIt); + + mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles).toAscii()); + m_MultiWidget->RequestUpdate(); + + } + catch (itk::ExceptionObject &ex) + { + MBI_INFO << ex ; + return ; } + } void QmitkTensorReconstructionView::ItkTensorReconstruction (mitk::DataStorage::SetOfObjects::Pointer inImages) { try { itk::TimeProbe clock; int nrFiles = inImages->size(); if (!nrFiles) return; QString status; mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles); mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { mitk::DiffusionImage* vols = - static_cast*>( - (*itemiter)->GetData()); + static_cast*>( + (*itemiter)->GetData()); std::string nodename; (*itemiter)->GetStringProperty("name", nodename); ++itemiter; // TENSOR RECONSTRUCTION clock.Start(); MBI_INFO << "Tensor reconstruction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( - "Tensor reconstruction for %s", nodename.c_str()).toAscii()); + "Tensor reconstruction for %s", nodename.c_str()).toAscii()); typedef itk::DiffusionTensor3DReconstructionImageFilter< - DiffusionPixelType, DiffusionPixelType, TTensorPixelType > TensorReconstructionImageFilterType; + DiffusionPixelType, DiffusionPixelType, TTensorPixelType > TensorReconstructionImageFilterType; TensorReconstructionImageFilterType::Pointer tensorReconstructionFilter = - TensorReconstructionImageFilterType::New(); - - - mitk::DiffusionImage::GradientDirectionContainerType::Pointer dirs = vols->GetDirections(); - itk::VectorImage::Pointer img = vols->GetVectorImage(); - + TensorReconstructionImageFilterType::New(); tensorReconstructionFilter->SetGradientImage( vols->GetDirections(), vols->GetVectorImage() ); tensorReconstructionFilter->SetBValue(vols->GetB_Value()); tensorReconstructionFilter->SetThreshold( m_Controls->m_TensorReconstructionThreasholdEdit->text().toFloat() ); tensorReconstructionFilter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s."; // TENSORS TO DATATREE mitk::TensorImage::Pointer image = mitk::TensorImage::New(); typedef itk::Image, 3> TensorImageType; TensorImageType::Pointer tensorImage; tensorImage = tensorReconstructionFilter->GetOutput(); // Check the tensor for negative eigenvalues if(m_Controls->m_CheckNegativeEigenvalues->isChecked()) { typedef itk::ImageRegionIterator TensorImageIteratorType; TensorImageIteratorType tensorIt(tensorImage, tensorImage->GetRequestedRegion()); tensorIt.GoToBegin(); while(!tensorIt.IsAtEnd()) { typedef itk::DiffusionTensor3D TensorType; //typedef itk::Tensor TensorType2; TensorType tensor = tensorIt.Get(); - // TensorType2 tensor2; + // TensorType2 tensor2; /* for(int i=0; i SymEigenSystemType; SymEigenSystemType eig (tensor2.GetVnlMatrix()); for(unsigned int i=0; iInitializeByItk( tensorImage.GetPointer() ); image->SetVolume( tensorReconstructionFilter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); QString newname; newname = newname.append(nodename.c_str()); newname = newname.append("_dti"); SetDefaultNodeProperties(node, newname.toStdString()); nodes.push_back(node); mitk::ProgressBar::GetInstance()->Progress(); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) GetDefaultDataStorage()->Add(*nodeIt); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles).toAscii()); m_MultiWidget->RequestUpdate(); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex ; return ; } } void QmitkTensorReconstructionView::TeemTensorReconstruction (mitk::DataStorage::SetOfObjects::Pointer inImages) { try { itk::TimeProbe clock; int nrFiles = inImages->size(); if (!nrFiles) return; QString status; mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles); mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { mitk::DiffusionImage* vols = - static_cast*>( - (*itemiter)->GetData()); + static_cast*>( + (*itemiter)->GetData()); std::string nodename; (*itemiter)->GetStringProperty("name", nodename); ++itemiter; // TENSOR RECONSTRUCTION clock.Start(); MBI_INFO << "Teem Tensor reconstruction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( - "Teem Tensor reconstruction for %s", nodename.c_str()).toAscii()); + "Teem Tensor reconstruction for %s", nodename.c_str()).toAscii()); typedef mitk::TeemDiffusionTensor3DReconstructionImageFilter< - DiffusionPixelType, TTensorPixelType > TensorReconstructionImageFilterType; + DiffusionPixelType, TTensorPixelType > TensorReconstructionImageFilterType; TensorReconstructionImageFilterType::Pointer tensorReconstructionFilter = - TensorReconstructionImageFilterType::New(); + TensorReconstructionImageFilterType::New(); tensorReconstructionFilter->SetInput( vols ); if(!m_Controls->m_TensorEstimationTeemSigmaEdit->text().contains(QString("NaN"))) tensorReconstructionFilter->SetSigma( m_Controls->m_TensorEstimationTeemSigmaEdit->text().toFloat() ); switch(m_Controls->m_TensorEstimationTeemEstimationMethodCombo->currentIndex()) { - // items << "LLS (Linear Least Squares)" - //<< "MLE (Maximum Likelihood)" - //<< "NLS (Nonlinear Least Squares)" - //<< "WLS (Weighted Least Squares)"; + // items << "LLS (Linear Least Squares)" + //<< "MLE (Maximum Likelihood)" + //<< "NLS (Nonlinear Least Squares)" + //<< "WLS (Weighted Least Squares)"; case 0: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsLLS); break; case 1: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsMLE); break; case 2: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsNLS); break; case 3: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsWLS); break; default: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsLLS); } tensorReconstructionFilter->SetNumIterations( m_Controls->m_TensorEstimationTeemNumItsSpin->value() ); if(m_Controls->m_TensorEstimationManualThreashold->isChecked()) tensorReconstructionFilter->SetConfidenceThreshold( m_Controls->m_TensorReconstructionThreasholdEdit_2->text().toDouble() ); tensorReconstructionFilter->SetConfidenceFuzzyness( m_Controls->m_TensorEstimationTeemFuzzyEdit->text().toFloat() ); tensorReconstructionFilter->SetMinPlausibleValue( m_Controls->m_TensorEstimationTeemMinValEdit->text().toDouble() ); tensorReconstructionFilter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s." ; // TENSORS TO DATATREE mitk::DataNode::Pointer node2=mitk::DataNode::New(); node2->SetData( tensorReconstructionFilter->GetOutputItk() ); QString newname; newname = newname.append(nodename.c_str()); newname = newname.append("_dtix"); SetDefaultNodeProperties(node2, newname.toStdString()); nodes.push_back(node2); mitk::ProgressBar::GetInstance()->Progress(); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) GetDefaultDataStorage()->Add(*nodeIt); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles).toAscii()); m_MultiWidget->RequestUpdate(); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex ; return ; } } void QmitkTensorReconstructionView::SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name) { node->SetProperty( "ShowMaxNumber", mitk::IntProperty::New( 500 ) ); node->SetProperty( "Scaling", mitk::FloatProperty::New( 1.0 ) ); node->SetProperty( "Normalization", mitk::OdfNormalizationMethodProperty::New()); node->SetProperty( "ScaleBy", mitk::OdfScaleByProperty::New()); node->SetProperty( "IndexParam1", mitk::FloatProperty::New(2)); node->SetProperty( "IndexParam2", mitk::FloatProperty::New(1)); node->SetProperty( "visible", mitk::BoolProperty::New( true ) ); node->SetProperty( "VisibleOdfs", mitk::BoolProperty::New( false ) ); node->SetProperty ("layer", mitk::IntProperty::New(100)); node->SetProperty( "DoRefresh", mitk::BoolProperty::New( true ) ); //node->SetProperty( "opacity", mitk::FloatProperty::New(1.0f) ); node->SetProperty( "name", mitk::StringProperty::New(name) ); } //node->SetProperty( "volumerendering", mitk::BoolProperty::New( false ) ); //node->SetProperty( "use color", mitk::BoolProperty::New( true ) ); //node->SetProperty( "texture interpolation", mitk::BoolProperty::New( true ) ); //node->SetProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ); //node->SetProperty( "layer", mitk::IntProperty::New(0)); //node->SetProperty( "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ); //node->SetOpacity(1.0f); //node->SetColor(1.0,1.0,1.0); //node->SetVisibility(true); //node->SetProperty( "IsTensorVolume", mitk::BoolProperty::New( true ) ); //mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); //mitk::LevelWindow levelwindow; //// levelwindow.SetAuto( image ); //levWinProp->SetLevelWindow( levelwindow ); //node->GetPropertyList()->SetProperty( "levelwindow", levWinProp ); //// add a default rainbow lookup table for color mapping //if(!node->GetProperty("LookupTable")) //{ // mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); // vtkLookupTable* vtkLut = mitkLut->GetVtkLookupTable(); // vtkLut->SetHueRange(0.6667, 0.0); // vtkLut->SetTableRange(0.0, 20.0); // vtkLut->Build(); // mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); // mitkLutProp->SetLookupTable(mitkLut); // node->SetProperty( "LookupTable", mitkLutProp ); //} //if(!node->GetProperty("binary")) // node->SetProperty( "binary", mitk::BoolProperty::New( false ) ); //// add a default transfer function //mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); //node->SetProperty ( "TransferFunction", mitk::TransferFunctionProperty::New ( tf.GetPointer() ) ); //// set foldername as string property //mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New( name ); //node->SetProperty( "name", nameProp ); void QmitkTensorReconstructionView::TensorsToDWI() { - if (m_CurrentSelection) - { - mitk::DataStorage::SetOfObjects::Pointer set = - mitk::DataStorage::SetOfObjects::New(); - - int at = 0; - for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); - i != m_CurrentSelection->End(); - ++i) - { - - if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) - { - mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - // process only on valid nodes - const mitk::BaseData* nodeData = node->GetData(); - if(nodeData) - { - if(QString("TensorImage").compare(nodeData->GetNameOfClass())==0) - { - set->InsertElement(at++, node); - } - } - } - } - - DoTensorsToDWI(set); - } + DoTensorsToDWI(m_TensorImages); } void QmitkTensorReconstructionView::TensorsToQbi() { - std::vector nodes = this->GetDataManagerSelection(); - for (int i=0; isize(); i++) { - mitk::DataNode::Pointer tensorImageNode = nodes.at(i); + mitk::DataNode::Pointer tensorImageNode = m_TensorImages->at(i); MITK_INFO << "starting Q-Ball estimation"; typedef float TTensorPixelType; typedef itk::DiffusionTensor3D< TTensorPixelType > TensorPixelType; typedef itk::Image< TensorPixelType, 3 > TensorImageType; TensorImageType::Pointer itkvol = TensorImageType::New(); mitk::CastToItkImage(dynamic_cast(tensorImageNode->GetData()), itkvol); typedef itk::TensorImageToQBallImageFilter< TTensorPixelType, TTensorPixelType > FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput( itkvol ); filter->Update(); typedef itk::Vector OutputPixelType; typedef itk::Image OutputImageType; mitk::QBallImage::Pointer image = mitk::QBallImage::New(); OutputImageType::Pointer outimg = filter->GetOutput(); image->InitializeByItk( outimg.GetPointer() ); image->SetVolume( outimg->GetBufferPointer() ); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); QString newname; newname = newname.append(tensorImageNode->GetName().c_str()); newname = newname.append("_qbi"); node->SetName(newname.toAscii()); GetDefaultDataStorage()->Add(node); } } void QmitkTensorReconstructionView::OnSelectionChanged( std::vector nodes ) { if ( !this->IsVisible() ) return; + m_DiffusionImages = mitk::DataStorage::SetOfObjects::New(); + m_TensorImages = mitk::DataStorage::SetOfObjects::New(); + bool foundDwiVolume = false; + bool foundTensorVolume = false; + m_Controls->m_DiffusionImageLabel->setText("-"); + m_Controls->m_TensorImageLabel->setText("-"); + m_DiffusionImage = NULL; + m_TensorImage = NULL; + + // iterate selection + for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) + { + mitk::DataNode::Pointer node = *it; + if (node.IsNull()) + continue; + + // only look at interesting types + if(dynamic_cast*>(node->GetData())) + { + foundDwiVolume = true; + m_Controls->m_DiffusionImageLabel->setText(node->GetName().c_str()); + m_DiffusionImages->push_back(node); + m_DiffusionImage = node; + } + else if(dynamic_cast(node->GetData())) + { + foundTensorVolume = true; + m_Controls->m_TensorImageLabel->setText(node->GetName().c_str()); + m_TensorImages->push_back(node); + m_TensorImage = node; + } + } + + m_Controls->m_ItkReconstruction->setEnabled(foundDwiVolume); + m_Controls->m_TeemReconstruction->setEnabled(foundDwiVolume); + + m_Controls->m_TensorsToDWIButton->setEnabled(foundTensorVolume); + m_Controls->m_TensorsToQbiButton->setEnabled(foundTensorVolume); + + + m_Controls->m_ResidualButton->setEnabled(foundDwiVolume && foundTensorVolume); + m_Controls->m_PercentagesOfOutliers->setEnabled(foundDwiVolume && foundTensorVolume); + m_Controls->m_PerSliceView->setEnabled(foundDwiVolume && foundTensorVolume); } template std::vector > QmitkTensorReconstructionView::MakeGradientList() { std::vector > retval; vnl_matrix_fixed* U = - itk::PointShell >::DistributePointShell(); + itk::PointShell >::DistributePointShell(); for(int i=0; i v; v[0] = U->get(0,i); v[1] = U->get(1,i); v[2] = U->get(2,i); retval.push_back(v); } // Add 0 vector for B0 itk::Vector v; v.Fill(0.0); retval.push_back(v); return retval; } void QmitkTensorReconstructionView::DoTensorsToDWI (mitk::DataStorage::SetOfObjects::Pointer inImages) { try { itk::TimeProbe clock; int nrFiles = inImages->size(); if (!nrFiles) return; QString status; mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles); mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { std::string nodename; (*itemiter)->GetStringProperty("name", nodename); mitk::TensorImage* vol = - static_cast((*itemiter)->GetData()); + static_cast((*itemiter)->GetData()); ++itemiter; typedef float TTensorPixelType; typedef itk::DiffusionTensor3D< TTensorPixelType > TensorPixelType; typedef itk::Image< TensorPixelType, 3 > TensorImageType; TensorImageType::Pointer itkvol = TensorImageType::New(); mitk::CastToItkImage(vol, itkvol); typedef itk::TensorImageToDiffusionImageFilter< - TTensorPixelType, DiffusionPixelType > FilterType; + TTensorPixelType, DiffusionPixelType > FilterType; FilterType::GradientListType gradientList; switch(m_Controls->m_TensorsToDWINumDirsSelect->currentIndex()) { case 0: gradientList = MakeGradientList<12>(); break; case 1: gradientList = MakeGradientList<42>(); break; case 2: gradientList = MakeGradientList<92>(); break; case 3: gradientList = MakeGradientList<162>(); break; case 4: gradientList = MakeGradientList<252>(); break; case 5: gradientList = MakeGradientList<362>(); break; case 6: gradientList = MakeGradientList<492>(); break; case 7: gradientList = MakeGradientList<642>(); break; case 8: gradientList = MakeGradientList<812>(); break; case 9: gradientList = MakeGradientList<1002>(); break; default: gradientList = MakeGradientList<92>(); } double bVal = m_Controls->m_TensorsToDWIBValueEdit->text().toDouble(); // DWI ESTIMATION clock.Start(); MBI_INFO << "DWI Estimation "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( - "DWI Estimation for %s", nodename.c_str()).toAscii()); + "DWI Estimation for %s", nodename.c_str()).toAscii()); FilterType::Pointer filter = FilterType::New(); filter->SetInput( itkvol ); filter->SetBValue(bVal); filter->SetGradientList(gradientList); //filter->SetNumberOfThreads(1); filter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s."; // TENSORS TO DATATREE mitk::DiffusionImage::Pointer image = mitk::DiffusionImage::New(); image->SetVectorImage( filter->GetOutput() ); image->SetB_Value(bVal); image->SetDirections(gradientList); image->SetOriginalDirections(gradientList); image->InitializeFromVectorImage(); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); mitk::DiffusionImageMapper::SetDefaultProperties(node); QString newname; newname = newname.append(nodename.c_str()); newname = newname.append("_dwi"); node->SetName(newname.toAscii()); nodes.push_back(node); mitk::ProgressBar::GetInstance()->Progress(); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) GetDefaultDataStorage()->Add(*nodeIt); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles).toAscii()); m_MultiWidget->RequestUpdate(); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex ; return ; } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.h index 8afe39952d..afbe6f821d 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.h @@ -1,125 +1,127 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: 2009-05-28 17:19:30 +0200 (Do, 28 Mai 2009) $ Version: $Revision: 17495 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _QMITKTENSORRECONSTRUCTIONVIEW_H_INCLUDED #define _QMITKTENSORRECONSTRUCTIONVIEW_H_INCLUDED #include #include #include "ui_QmitkTensorReconstructionViewControls.h" #include #include -#include -#include -#include - typedef short DiffusionPixelType; struct TrSelListener; /*! * \ingroup org_mitk_gui_qt_tensorreconstruction_internal * * \brief QmitkTensorReconstructionView * * Document your class here. * * \sa QmitkFunctionality */ class QmitkTensorReconstructionView : public QmitkFunctionality { friend struct TrSelListener; // this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) Q_OBJECT public: static const std::string VIEW_ID; QmitkTensorReconstructionView(); QmitkTensorReconstructionView(const QmitkTensorReconstructionView& other); virtual ~QmitkTensorReconstructionView(); virtual void CreateQtPartControl(QWidget *parent); /// \brief Creation of the connections of main and control widget virtual void CreateConnections(); /// \brief Called when the functionality is activated virtual void Activated(); virtual void Deactivated(); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); static const int nrconvkernels; protected slots: void TensorsToQbi(); void TensorsToDWI(); void DoTensorsToDWI(mitk::DataStorage::SetOfObjects::Pointer inImages); void TeemCheckboxClicked(); void Advanced1CheckboxClicked(); void Advanced2CheckboxClicked(); + void Advanced3CheckboxClicked(); void ManualThresholdClicked(); void MethodChoosen(int method); void Reconstruct(int method); void TeemReconstruction(); void ItkReconstruction(); + void ReconstructionWithCorrection(); void ResidualCalculation(); void ResidualClicked(int slice, int volume); protected: void ItkTensorReconstruction (mitk::DataStorage::SetOfObjects::Pointer inImages); void TeemTensorReconstruction + (mitk::DataStorage::SetOfObjects::Pointer inImages); + void TensorReconstructionWithCorr (mitk::DataStorage::SetOfObjects::Pointer inImages); void OnSelectionChanged( std::vector nodes ); Ui::QmitkTensorReconstructionViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; template std::vector > MakeGradientList() ; template void TemplatedAnalyticalTensorReconstruction(mitk::DiffusionImage* vols, float lambda, std::string nodename, std::vector* nodes, int normalization); void SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name); - berry::ISelectionListener::Pointer m_SelListener; - berry::IStructuredSelection::ConstPointer m_CurrentSelection; + mitk::DataNode::Pointer m_DiffusionImage; + mitk::DataNode::Pointer m_TensorImage; + mitk::DataStorage::SetOfObjects::Pointer m_DiffusionImages; + mitk::DataStorage::SetOfObjects::Pointer m_TensorImages; }; #endif // _QMITKTENSORRECONSTRUCTIONVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionViewControls.ui index 23f2ebb31f..323796533b 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionViewControls.ui @@ -1,696 +1,790 @@ QmitkTensorReconstructionViewControls 0 0 - 345 + 363 1303 0 0 true QmitkTensorReconstructionViewControls - + + + + + Data: + + + + + + Diffusion Image: + + + + + + + - + + + + + + + Tensor Image: + + + + + + + - + + + + + + ITK Reconstruction Advanced Settings false QFrame::StyledPanel QFrame::Raised QFormLayout::AllNonFixedFieldsGrow B0 Threshold false 0 Check for negative eigenvalues QFrame::StyledPanel QFrame::Raised false ITK Tensor Reconstruction Estimate Diffusion Image from Tensors QFrame::StyledPanel QFrame::Raised QFormLayout::AllNonFixedFieldsGrow 6 6 9 how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" B-Value false 0 0 how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" # Gradient Directions 3 12 42 92 162 252 362 492 642 812 1002 false Diffusion Image Estimation Estimate Q-Ball Image from Tensors false Calculate ODF value as tensor value in the according direction Q-Ball Image Estimation + + + + Tensor reconstruction correction + + + + + + Advanced Settings + + + false + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + B0 Threshold + + + false + + + + + + + 50 + + + + + + + + + + false + + + + + + + + + + + + Reconstruction with correction + + + + + + true Teem Reconstruction true Teem Reconstruction Advanced Settings QFrame::StyledPanel QFrame::Raised QFormLayout::AllNonFixedFieldsGrow important in case of method wls # Iterations false how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" Fuzzy confidence false 0 0 how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" minimum plausible value (especially important for linear least squares estimation) (double); default: "1.0" minimum plausible value (especially important for linear least squares estimation) (double); default: "1.0" minimum plausible value (especially important for linear least squares estimation) (double); default: "1.0" Min plausible value false Rician noise parameter (float) Rician noise parameter (float) Rician noise parameter (float) Sigma false 0 0 minimum plausible value (especially important for linear least squares estimation) (double); default: "1.0" minimum plausible value (especially important for linear least squares estimation) (double); default: "1.0" minimum plausible value (especially important for linear least squares estimation) (double); default: "1.0" 0 0 Rician noise parameter (float) Rician noise parameter (float) Rician noise parameter (float) Method B0-Threshold false 0 false Teem Tensor Reconstruction Residuals false false QFrame::StyledPanel QFrame::Raised false Calculate the residual from a dti and a dwi iimage Residual Image Calculation 1 Per volume 200 300 Per slice outliers per slice QFrame::StyledPanel QFrame::Raised 300 400 QFrame::StyledPanel QFrame::Raised 20 255 Volume: .., Slice:.. false percentages of error - - - - Qt::Vertical - - - - 20 - 1150 - - - - QmitkResidualAnalysisWidget QWidget
QmitkResidualAnalysisWidget.h
1
QmitkResidualViewWidget QGraphicsView
QmitkResidualViewWidget.h
diff --git a/Plugins/org.mitk.gui.qt.diffusionimagingapp/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.diffusionimagingapp/manifest_headers.cmake index 3474a73b0d..6f2d820686 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimagingapp/manifest_headers.cmake +++ b/Plugins/org.mitk.gui.qt.diffusionimagingapp/manifest_headers.cmake @@ -1,8 +1,8 @@ set(Plugin-Name "MITK Diffusion") set(Plugin-Version "1.0") set(Plugin-Vendor "DKFZ, Medical and Biological Informatics") set(Plugin-ContactAddress "http://www.mitk.org") -set(Require-Plugin org.mitk.gui.qt.ext org.mitk.gui.qt.stdmultiwidgeteditor) +set(Require-Plugin org.mitk.gui.qt.ext) set(Plugin-Activator QmitkDiffusionImagingAppApplicationPlugin) diff --git a/Plugins/org.mitk.gui.qt.diffusionimagingapp/src/internal/QmitkDiffusionImagingAppIntroPart.cpp b/Plugins/org.mitk.gui.qt.diffusionimagingapp/src/internal/QmitkDiffusionImagingAppIntroPart.cpp index f6d2bccbc0..c12bf82cdf 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimagingapp/src/internal/QmitkDiffusionImagingAppIntroPart.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimagingapp/src/internal/QmitkDiffusionImagingAppIntroPart.cpp @@ -1,184 +1,212 @@ /*========================================================================= Program: BlueBerry Platform Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "QmitkDiffusionImagingAppIntroPart.h" #include "mitkNodePredicateDataType.h" #include #include #include #include #include #include + #include +#include + +#include +#include #include #include #include #include #ifdef QT_WEBKIT #include #include #endif #include #include #include #include #include #include -#include "QmitkStdMultiWidget.h" -#include "QmitkStdMultiWidgetEditor.h" #include "QmitkDiffusionApplicationPlugin.h" #include "mitkDataStorageEditorInput.h" +#include #include "mitkBaseDataIOFactory.h" #include "mitkSceneIO.h" #include "mitkProgressBar.h" #include "mitkDataNodeFactory.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateProperty.h" QmitkDiffusionImagingAppIntroPart::QmitkDiffusionImagingAppIntroPart() : m_Controls(NULL) { berry::IPreferences::Pointer workbenchPrefs = QmitkDiffusionApplicationPlugin::GetDefault()->GetPreferencesService()->GetSystemPreferences(); workbenchPrefs->PutBool(berry::WorkbenchPreferenceConstants::SHOW_INTRO, true); workbenchPrefs->Flush(); } QmitkDiffusionImagingAppIntroPart::~QmitkDiffusionImagingAppIntroPart() { // if the workbench is not closing (that means, welcome screen was closed explicitly), set "Show_intro" false if (!this->GetIntroSite()->GetPage()->GetWorkbenchWindow()->GetWorkbench()->IsClosing()) { berry::IPreferences::Pointer workbenchPrefs = QmitkDiffusionApplicationPlugin::GetDefault()->GetPreferencesService()->GetSystemPreferences(); workbenchPrefs->PutBool(berry::WorkbenchPreferenceConstants::SHOW_INTRO, false); workbenchPrefs->Flush(); } else { berry::IPreferences::Pointer workbenchPrefs = QmitkDiffusionApplicationPlugin::GetDefault()->GetPreferencesService()->GetSystemPreferences(); workbenchPrefs->PutBool(berry::WorkbenchPreferenceConstants::SHOW_INTRO, true); workbenchPrefs->Flush(); } // if workbench is not closing (Just welcome screen closing), open last used perspective if (this->GetIntroSite()->GetPage()->GetPerspective()->GetId() == "org.mitk.diffusionimagingapp.perspectives.welcome" && !this->GetIntroSite()->GetPage()->GetWorkbenchWindow()->GetWorkbench()->IsClosing()) { berry::IPerspectiveDescriptor::Pointer perspective = this->GetIntroSite()->GetWorkbenchWindow()->GetWorkbench()->GetPerspectiveRegistry()->FindPerspectiveWithId("org.mitk.diffusionimagingapp.perspectives.diffusionimagingapp"); if (perspective) { this->GetIntroSite()->GetPage()->SetPerspective(perspective); } } } void QmitkDiffusionImagingAppIntroPart::CreateQtPartControl(QWidget* parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkWelcomeScreenViewControls; m_Controls->setupUi(parent); #ifdef QT_WEBKIT // create a QWebView as well as a QWebPage and QWebFrame within the QWebview m_view = new QWebView(parent); m_view->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); QUrl urlQtResource(QString("qrc:/org.mitk.gui.qt.welcomescreen/mitkdiffusionimagingappwelcomeview.html"), QUrl::TolerantMode ); m_view->load( urlQtResource ); // adds the webview as a widget parent->layout()->addWidget(m_view); this->CreateConnections(); #else parent->layout()->addWidget(new QLabel("

Please install Qt with the WebKit option to see cool pictures!

")); #endif } } #ifdef QT_WEBKIT void QmitkDiffusionImagingAppIntroPart::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_view->page()), SIGNAL(linkClicked(const QUrl& )), this, SLOT(DelegateMeTo(const QUrl& )) ); } } void QmitkDiffusionImagingAppIntroPart::DelegateMeTo(const QUrl& showMeNext) { QString scheme = showMeNext.scheme(); QByteArray urlHostname = showMeNext.encodedHost(); QByteArray urlPath = showMeNext.encodedPath(); QByteArray dataset = showMeNext.encodedQueryItemValue("dataset"); QByteArray clear = showMeNext.encodedQueryItemValue("clear"); if (scheme.isEmpty()) MITK_INFO << " empty scheme of the to be delegated link" ; // if the scheme is set to mitk, it is to be tested which action should be applied if (scheme.contains(QString("mitk")) ) { if(urlPath.isEmpty() ) MITK_INFO << " mitk path is empty " ; // searching for the perspective keyword within the host name if(urlHostname.contains(QByteArray("perspectives")) ) { // the simplified method removes every whitespace // ( whitespace means any character for which the standard C++ isspace() method returns true) urlPath = urlPath.simplified(); QString tmpPerspectiveId(urlPath.data()); tmpPerspectiveId.replace(QString("/"), QString("") ); std::string perspectiveId = tmpPerspectiveId.toStdString(); // is working fine as long as the perspective id is valid, if not the application crashes GetIntroSite()->GetWorkbenchWindow()->GetWorkbench()->ShowPerspective(perspectiveId, GetIntroSite()->GetWorkbenchWindow() ); + + // search the Workbench for opened StdMultiWidgets to ensure the focus does not stay on the welcome screen and is switched to + // an StdMultiWidget if one available + mitk::IDataStorageService::Pointer service = + berry::Platform::GetServiceRegistry().GetServiceById(mitk::IDataStorageService::ID); + berry::IEditorInput::Pointer editorInput; + editorInput = new mitk::DataStorageEditorInput( service->GetActiveDataStorage() ); + + // the solution is not clean, but the dependency to the StdMultiWidget was removed in order to fix a crash problem + // as described in Bug #11715 + // This is the correct way : use the static string ID variable + // berry::IEditorPart::Pointer editor = GetIntroSite()->GetPage()->FindEditors( editorInput, QmitkStdMultiWidgetEditor::EDITOR_ID ); + // QuickFix: we use the same string for an local variable + const std::string stdEditorID = "org.mitk.editors.stdmultiwidget"; + + // search for opened StdMultiWidgetEditors + std::vector editorList = GetIntroSite()->GetPage()->FindEditors( editorInput, stdEditorID, 1 ); + + // if an StdMultiWidgetEditor open was found, give focus to it + if(editorList.size()) + { + GetIntroSite()->GetPage()->Activate( editorList[0]->GetPart(true) ); + } + } } // if the scheme is set to http, by default no action is performed, if an external webpage needs to be // shown it should be implemented below else if (scheme.contains(QString("http")) ) { QDesktopServices::openUrl(showMeNext); // m_view->load( ) ; } else if(scheme.contains("qrc")) { m_view->load(showMeNext); } } #endif void QmitkDiffusionImagingAppIntroPart::StandbyStateChanged(bool standby) { } void QmitkDiffusionImagingAppIntroPart::SetFocus() { } diff --git a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp index 5ba07d39aa..f1c849dc34 100644 --- a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp +++ b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp @@ -1,1148 +1,1148 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "QmitkExtWorkbenchWindowAdvisor.h" #include "QmitkExtActionBarAdvisor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // UGLYYY #include "internal/QmitkExtWorkbenchWindowAdvisorHack.h" #include "internal/QmitkCommonExtPlugin.h" #include "mitkUndoController.h" #include "mitkVerboseLimitedLinearUndo.h" #include #include #include #include QmitkExtWorkbenchWindowAdvisorHack * QmitkExtWorkbenchWindowAdvisorHack::undohack = new QmitkExtWorkbenchWindowAdvisorHack(); QString QmitkExtWorkbenchWindowAdvisor::QT_SETTINGS_FILENAME = "QtSettings.ini"; class PartListenerForTitle: public berry::IPartListener { public: PartListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa) : windowAdvisor(wa) { } Events::Types GetPartEventTypes() const { return Events::ACTIVATED | Events::BROUGHT_TO_TOP | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartActivated(berry::IWorkbenchPartReference::Pointer ref) { if (ref.Cast ()) { windowAdvisor->UpdateTitle(false); } } void PartBroughtToTop(berry::IWorkbenchPartReference::Pointer ref) { if (ref.Cast ()) { windowAdvisor->UpdateTitle(false); } } void PartClosed(berry::IWorkbenchPartReference::Pointer /*ref*/) { windowAdvisor->UpdateTitle(false); } void PartHidden(berry::IWorkbenchPartReference::Pointer ref) { if (!windowAdvisor->lastActiveEditor.Expired() && ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock()) { windowAdvisor->UpdateTitle(true); } } void PartVisible(berry::IWorkbenchPartReference::Pointer ref) { if (!windowAdvisor->lastActiveEditor.Expired() && ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock()) { windowAdvisor->UpdateTitle(false); } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; }; class PartListenerForImageNavigator: public berry::IPartListener { public: PartListenerForImageNavigator(QAction* act) : imageNavigatorAction(act) { } Events::Types GetPartEventTypes() const { return Events::OPENED | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartOpened(berry::IWorkbenchPartReference::Pointer ref) { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(true); } } void PartClosed(berry::IWorkbenchPartReference::Pointer ref) { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(false); } } void PartVisible(berry::IWorkbenchPartReference::Pointer ref) { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(true); } } void PartHidden(berry::IWorkbenchPartReference::Pointer ref) { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(false); } } private: QAction* imageNavigatorAction; }; class PerspectiveListenerForTitle: public berry::IPerspectiveListener { public: PerspectiveListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa) : windowAdvisor(wa), perspectivesClosed(false) { } Events::Types GetPerspectiveEventTypes() const { return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED // remove the following line when command framework is finished | Events::CLOSED | Events::OPENED; } void PerspectiveActivated(berry::IWorkbenchPage::Pointer /*page*/, berry::IPerspectiveDescriptor::Pointer /*perspective*/) { windowAdvisor->UpdateTitle(false); } void PerspectiveSavedAs(berry::IWorkbenchPage::Pointer /*page*/, berry::IPerspectiveDescriptor::Pointer /*oldPerspective*/, berry::IPerspectiveDescriptor::Pointer /*newPerspective*/) { windowAdvisor->UpdateTitle(false); } void PerspectiveDeactivated(berry::IWorkbenchPage::Pointer /*page*/, berry::IPerspectiveDescriptor::Pointer /*perspective*/) { windowAdvisor->UpdateTitle(false); } void PerspectiveOpened(berry::IWorkbenchPage::Pointer /*page*/, berry::IPerspectiveDescriptor::Pointer /*perspective*/) { if (perspectivesClosed) { QListIterator i(windowAdvisor->viewActions); while (i.hasNext()) { i.next()->setEnabled(true); } windowAdvisor->fileSaveProjectAction->setEnabled(true); windowAdvisor->closeProjectAction->setEnabled(true); windowAdvisor->undoAction->setEnabled(true); windowAdvisor->redoAction->setEnabled(true); windowAdvisor->imageNavigatorAction->setEnabled(true); windowAdvisor->resetPerspAction->setEnabled(true); if( windowAdvisor->GetShowClosePerspectiveMenuItem() ) { windowAdvisor->closePerspAction->setEnabled(true); } } perspectivesClosed = false; } void PerspectiveClosed(berry::IWorkbenchPage::Pointer /*page*/, berry::IPerspectiveDescriptor::Pointer /*perspective*/) { berry::IWorkbenchWindow::Pointer wnd = windowAdvisor->GetWindowConfigurer()->GetWindow(); bool allClosed = true; if (wnd->GetActivePage()) { std::vector perspectives(wnd->GetActivePage()->GetOpenPerspectives()); allClosed = perspectives.empty(); } if (allClosed) { perspectivesClosed = true; QListIterator i(windowAdvisor->viewActions); while (i.hasNext()) { i.next()->setEnabled(false); } windowAdvisor->fileSaveProjectAction->setEnabled(false); windowAdvisor->closeProjectAction->setEnabled(false); windowAdvisor->undoAction->setEnabled(false); windowAdvisor->redoAction->setEnabled(false); windowAdvisor->imageNavigatorAction->setEnabled(false); windowAdvisor->resetPerspAction->setEnabled(false); if( windowAdvisor->GetShowClosePerspectiveMenuItem() ) { windowAdvisor->closePerspAction->setEnabled(false); } } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; bool perspectivesClosed; }; class PerspectiveListenerForMenu: public berry::IPerspectiveListener { public: PerspectiveListenerForMenu(QmitkExtWorkbenchWindowAdvisor* wa) : windowAdvisor(wa) { } Events::Types GetPerspectiveEventTypes() const { return Events::ACTIVATED | Events::DEACTIVATED; } void PerspectiveActivated(berry::IWorkbenchPage::Pointer /*page*/, berry::IPerspectiveDescriptor::Pointer perspective) { QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()]; if (action) { action->setChecked(true); } } void PerspectiveDeactivated(berry::IWorkbenchPage::Pointer /*page*/, berry::IPerspectiveDescriptor::Pointer perspective) { QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()]; if (action) { action->setChecked(false); } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; }; QmitkExtWorkbenchWindowAdvisor::QmitkExtWorkbenchWindowAdvisor(berry::WorkbenchAdvisor* wbAdvisor, berry::IWorkbenchWindowConfigurer::Pointer configurer) : berry::WorkbenchWindowAdvisor(configurer), lastInput(0), wbAdvisor(wbAdvisor), showViewToolbar(true), showPerspectiveToolbar(false), showVersionInfo(true), showMitkVersionInfo(true), showViewMenuItem(true), -showNewWindowMenuItem(true), +showNewWindowMenuItem(false), showClosePerspectiveMenuItem(true), dropTargetListener(new QmitkDefaultDropTargetListener) { productName = berry::Platform::GetConfiguration().getString("application.baseName"); } berry::ActionBarAdvisor::Pointer QmitkExtWorkbenchWindowAdvisor::CreateActionBarAdvisor( berry::IActionBarConfigurer::Pointer configurer) { berry::ActionBarAdvisor::Pointer actionBarAdvisor( new QmitkExtActionBarAdvisor(configurer)); return actionBarAdvisor; } void* QmitkExtWorkbenchWindowAdvisor::CreateEmptyWindowContents(void* parent) { QWidget* parentWidget = static_cast(parent); QLabel* label = new QLabel(parentWidget); label->setText("No perspectives are open. Open a perspective in the Window->Open Perspective menu."); label->setContentsMargins(10,10,10,10); label->setAlignment(Qt::AlignTop); label->setEnabled(false); parentWidget->layout()->addWidget(label); return label; } void QmitkExtWorkbenchWindowAdvisor::ShowClosePerspectiveMenuItem(bool show) { showClosePerspectiveMenuItem = show; } bool QmitkExtWorkbenchWindowAdvisor::GetShowClosePerspectiveMenuItem() { return showClosePerspectiveMenuItem; } void QmitkExtWorkbenchWindowAdvisor::ShowNewWindowMenuItem(bool show) { showNewWindowMenuItem = show; } void QmitkExtWorkbenchWindowAdvisor::ShowViewToolbar(bool show) { showViewToolbar = show; } void QmitkExtWorkbenchWindowAdvisor::ShowViewMenuItem(bool show) { showViewMenuItem = show; } void QmitkExtWorkbenchWindowAdvisor::ShowPerspectiveToolbar(bool show) { showPerspectiveToolbar = show; } void QmitkExtWorkbenchWindowAdvisor::ShowVersionInfo(bool show) { showVersionInfo = show; } void QmitkExtWorkbenchWindowAdvisor::ShowMitkVersionInfo(bool show) { showMitkVersionInfo = show; } void QmitkExtWorkbenchWindowAdvisor::SetProductName(const std::string& product) { productName = product; } void QmitkExtWorkbenchWindowAdvisor::SetWindowIcon(const std::string& wndIcon) { windowIcon = wndIcon; } void QmitkExtWorkbenchWindowAdvisor::PostWindowCreate() { // very bad hack... berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow(); QMainWindow* mainWindow = static_cast (window->GetShell()->GetControl()); if (!windowIcon.empty()) { mainWindow->setWindowIcon(QIcon(QString::fromStdString(windowIcon))); } mainWindow->setContextMenuPolicy(Qt::PreventContextMenu); /*mainWindow->setStyleSheet("color: white;" "background-color: #808080;" "selection-color: #659EC7;" "selection-background-color: #808080;" " QMenuBar {" "background-color: #808080; }");*/ // ==== Application menu ============================ QMenuBar* menuBar = mainWindow->menuBar(); menuBar->setContextMenuPolicy(Qt::PreventContextMenu); QMenu* fileMenu = menuBar->addMenu("&File"); fileMenu->setObjectName("FileMenu"); QAction* fileOpenAction = new QmitkFileOpenAction(QIcon(":/org.mitk.gui.qt.ext/Load_48.png"), window); fileMenu->addAction(fileOpenAction); fileSaveProjectAction = new QmitkExtFileSaveProjectAction(window); fileSaveProjectAction->setIcon(QIcon(":/org.mitk.gui.qt.ext/Save_48.png")); fileMenu->addAction(fileSaveProjectAction); closeProjectAction = new QmitkCloseProjectAction(window); closeProjectAction->setIcon(QIcon(":/org.mitk.gui.qt.ext/Remove_48.png")); fileMenu->addAction(closeProjectAction); fileMenu->addSeparator(); QAction* fileExitAction = new QmitkFileExitAction(window); fileExitAction->setObjectName("QmitkFileExitAction"); fileMenu->addAction(fileExitAction); berry::IViewRegistry* viewRegistry = berry::PlatformUI::GetWorkbench()->GetViewRegistry(); const std::vector& viewDescriptors = viewRegistry->GetViews(); // another bad hack to get an edit/undo menu... QMenu* editMenu = menuBar->addMenu("&Edit"); undoAction = editMenu->addAction(QIcon(":/org.mitk.gui.qt.ext/Undo_48.png"), "&Undo", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onUndo()), QKeySequence("CTRL+Z")); undoAction->setToolTip("Undo the last action (not supported by all modules)"); redoAction = editMenu->addAction(QIcon(":/org.mitk.gui.qt.ext/Redo_48.png") , "&Redo", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onRedo()), QKeySequence("CTRL+Y")); redoAction->setToolTip("execute the last action that was undone again (not supported by all modules)"); imageNavigatorAction = new QAction(QIcon(":/org.mitk.gui.qt.ext/Slider.png"), "&Image Navigator", NULL); bool imageNavigatorViewFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.imagenavigator"); if (imageNavigatorViewFound) { QObject::connect(imageNavigatorAction, SIGNAL(triggered(bool)), QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onImageNavigator())); imageNavigatorAction->setCheckable(true); // add part listener for image navigator imageNavigatorPartListener = new PartListenerForImageNavigator(imageNavigatorAction); window->GetPartService()->AddPartListener(imageNavigatorPartListener); berry::IViewPart::Pointer imageNavigatorView = window->GetActivePage()->FindView("org.mitk.views.imagenavigator"); imageNavigatorAction->setChecked(false); if (imageNavigatorView) { bool isImageNavigatorVisible = window->GetActivePage()->IsPartVisible(imageNavigatorView); if (isImageNavigatorVisible) imageNavigatorAction->setChecked(true); } imageNavigatorAction->setToolTip("Open image navigator for navigating through image"); } // toolbar for showing file open, undo, redo and other main actions QToolBar* mainActionsToolBar = new QToolBar; mainActionsToolBar->setContextMenuPolicy(Qt::PreventContextMenu); #ifdef __APPLE__ mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextUnderIcon ); #else mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextBesideIcon ); #endif mainActionsToolBar->addAction(fileOpenAction); mainActionsToolBar->addAction(fileSaveProjectAction); mainActionsToolBar->addAction(closeProjectAction); mainActionsToolBar->addAction(undoAction); mainActionsToolBar->addAction(redoAction); if (imageNavigatorViewFound) { mainActionsToolBar->addAction(imageNavigatorAction); } mainWindow->addToolBar(mainActionsToolBar); #ifdef __APPLE__ mainWindow->setUnifiedTitleAndToolBarOnMac(true); #endif // ==== Window Menu ========================== QMenu* windowMenu = menuBar->addMenu("Window"); if (showNewWindowMenuItem) { windowMenu->addAction("&New Window", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onNewWindow())); windowMenu->addSeparator(); } QMenu* perspMenu = windowMenu->addMenu("&Open Perspective"); QMenu* viewMenu; if (showViewMenuItem) { viewMenu = windowMenu->addMenu("Show &View"); viewMenu->setObjectName("Show View"); } windowMenu->addSeparator(); resetPerspAction = windowMenu->addAction("&Reset Perspective", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onResetPerspective())); if(showClosePerspectiveMenuItem) closePerspAction = windowMenu->addAction("&Close Perspective", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onClosePerspective())); windowMenu->addSeparator(); windowMenu->addAction("&Preferences...", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onEditPreferences()), QKeySequence("CTRL+P")); // fill perspective menu berry::IPerspectiveRegistry* perspRegistry = window->GetWorkbench()->GetPerspectiveRegistry(); QActionGroup* perspGroup = new QActionGroup(menuBar); std::vector perspectives( perspRegistry->GetPerspectives()); bool skip = false; for (std::vector::iterator perspIt = perspectives.begin(); perspIt != perspectives.end(); ++perspIt) { // if perspectiveExcludeList is set, it contains the id-strings of perspectives, which // should not appear as an menu-entry in the perspective menu if (perspectiveExcludeList.size() > 0) { for (unsigned int i=0; iGetId()) { skip = true; break; } } if (skip) { skip = false; continue; } } QAction* perspAction = new berry::QtOpenPerspectiveAction(window, *perspIt, perspGroup); mapPerspIdToAction.insert(std::make_pair((*perspIt)->GetId(), perspAction)); } perspMenu->addActions(perspGroup->actions()); // sort elements (converting vector to map...) std::vector::const_iterator iter; std::map VDMap; skip = false; for (iter = viewDescriptors.begin(); iter != viewDescriptors.end(); ++iter) { // if viewExcludeList is set, it contains the id-strings of view, which // should not appear as an menu-entry in the menu if (viewExcludeList.size() > 0) { for (unsigned int i=0; iGetId()) { skip = true; break; } } if (skip) { skip = false; continue; } } if ((*iter)->GetId() == "org.blueberry.ui.internal.introview") continue; if ((*iter)->GetId() == "org.mitk.views.imagenavigator") continue; std::pair p( (*iter)->GetLabel(), (*iter)); VDMap.insert(p); } // ================================================== // ==== Perspective Toolbar ================================== QToolBar* qPerspectiveToolbar = new QToolBar; if (showPerspectiveToolbar) { qPerspectiveToolbar->addActions(perspGroup->actions()); mainWindow->addToolBar(qPerspectiveToolbar); } else delete qPerspectiveToolbar; // ==== View Toolbar ================================== QToolBar* qToolbar = new QToolBar; std::map::const_iterator MapIter; for (MapIter = VDMap.begin(); MapIter != VDMap.end(); ++MapIter) { berry::QtShowViewAction* viewAction = new berry::QtShowViewAction(window, (*MapIter).second); viewActions.push_back(viewAction); if(showViewMenuItem) viewMenu->addAction(viewAction); if (showViewToolbar) { qToolbar->addAction(viewAction); } } if (showViewToolbar) { mainWindow->addToolBar(qToolbar); } else delete qToolbar; QSettings settings(GetQSettingsFile(), QSettings::IniFormat); mainWindow->restoreState(settings.value("ToolbarPosition").toByteArray()); // ==================================================== // ===== Help menu ==================================== QMenu* helpMenu = menuBar->addMenu("Help"); helpMenu->addAction("&Welcome",this, SLOT(onIntro())); helpMenu->addAction("&Contents", this, SLOT(onHelpContents())); helpMenu->addAction("Context &Help",this, SLOT(onHelp()), QKeySequence("F1")); helpMenu->addAction("&About",this, SLOT(onAbout())); // ===================================================== QStatusBar* qStatusBar = new QStatusBar(); //creating a QmitkStatusBar for Output on the QStatusBar and connecting it with the MainStatusBar QmitkStatusBar *statusBar = new QmitkStatusBar(qStatusBar); //disabling the SizeGrip in the lower right corner statusBar->SetSizeGripEnabled(false); QmitkProgressBar *progBar = new QmitkProgressBar(); qStatusBar->addPermanentWidget(progBar, 0); progBar->hide(); // progBar->AddStepsToDo(2); // progBar->Progress(1); mainWindow->setStatusBar(qStatusBar); QmitkMemoryUsageIndicatorView* memoryIndicator = new QmitkMemoryUsageIndicatorView(); qStatusBar->addPermanentWidget(memoryIndicator, 0); } void QmitkExtWorkbenchWindowAdvisor::PreWindowOpen() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); // show the shortcut bar and progress indicator, which are hidden by // default //configurer->SetShowPerspectiveBar(true); //configurer->SetShowFastViewBars(true); //configurer->SetShowProgressIndicator(true); // // add the drag and drop support for the editor area // configurer.addEditorAreaTransfer(EditorInputTransfer.getInstance()); // configurer.addEditorAreaTransfer(ResourceTransfer.getInstance()); // configurer.addEditorAreaTransfer(FileTransfer.getInstance()); // configurer.addEditorAreaTransfer(MarkerTransfer.getInstance()); // configurer.configureEditorAreaDropListener(new EditorAreaDropAdapter( // configurer.getWindow())); this->HookTitleUpdateListeners(configurer); menuPerspectiveListener = new PerspectiveListenerForMenu(this); configurer->GetWindow()->AddPerspectiveListener(menuPerspectiveListener); configurer->AddEditorAreaTransfer(QStringList("text/uri-list")); configurer->ConfigureEditorAreaDropListener(dropTargetListener); } void QmitkExtWorkbenchWindowAdvisor::onIntro() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onIntro(); } void QmitkExtWorkbenchWindowAdvisor::onHelp() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelp(); } void QmitkExtWorkbenchWindowAdvisor::onHelpContents() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelpContents(); } void QmitkExtWorkbenchWindowAdvisor::onAbout() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onAbout(); } //-------------------------------------------------------------------------------- // Ugly hack from here on. Feel free to delete when command framework // and undo buttons are done. //-------------------------------------------------------------------------------- QmitkExtWorkbenchWindowAdvisorHack::QmitkExtWorkbenchWindowAdvisorHack() : QObject() { } QmitkExtWorkbenchWindowAdvisorHack::~QmitkExtWorkbenchWindowAdvisorHack() { } void QmitkExtWorkbenchWindowAdvisorHack::onUndo() { mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel(); if (model) { if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast( model )) { mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetUndoDescriptions(); if (descriptions.size() >= 1) { MITK_INFO << "Undo " << descriptions.front().second; } } model->Undo(); } else { MITK_ERROR << "No undo model instantiated"; } } void QmitkExtWorkbenchWindowAdvisorHack::onRedo() { mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel(); if (model) { if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast( model )) { mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetRedoDescriptions(); if (descriptions.size() >= 1) { MITK_INFO << "Redo " << descriptions.front().second; } } model->Redo(); } else { MITK_ERROR << "No undo model instantiated"; } } void QmitkExtWorkbenchWindowAdvisorHack::onImageNavigator() { // get ImageNavigatorView berry::IViewPart::Pointer imageNavigatorView = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->FindView("org.mitk.views.imagenavigator"); if (imageNavigatorView) { bool isImageNavigatorVisible = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->IsPartVisible(imageNavigatorView); if (isImageNavigatorVisible) { berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->HideView(imageNavigatorView); return; } } berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ShowView("org.mitk.views.imagenavigator"); //berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ResetPerspective(); } void QmitkExtWorkbenchWindowAdvisorHack::onEditPreferences() { QmitkPreferencesDialog _PreferencesDialog(QApplication::activeWindow()); _PreferencesDialog.exec(); } void QmitkExtWorkbenchWindowAdvisorHack::onQuit() { berry::PlatformUI::GetWorkbench()->Close(); } void QmitkExtWorkbenchWindowAdvisorHack::onResetPerspective() { berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ResetPerspective(); } void QmitkExtWorkbenchWindowAdvisorHack::onClosePerspective() { berry::IWorkbenchPage::Pointer page = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage(); page->ClosePerspective(page->GetPerspective(), true, true); } void QmitkExtWorkbenchWindowAdvisorHack::onNewWindow() { berry::PlatformUI::GetWorkbench()->OpenWorkbenchWindow(0); } void QmitkExtWorkbenchWindowAdvisorHack::onIntro() { bool hasIntro = berry::PlatformUI::GetWorkbench()->GetIntroManager()->HasIntro(); if (!hasIntro) { QRegExp reg("(.*)(\\n)*"); QRegExp reg2("(\\n)*(.*)"); QFile file(":/org.mitk.gui.qt.ext/index.html"); file.open(QIODevice::ReadOnly | QIODevice::Text); // Als Text-Datei nur zum Lesen öffnen QString text = QString(file.readAll()); file.close(); QString title = text; title.replace(reg, ""); title.replace(reg2, ""); std::cout << title.toStdString() << std::endl; QMessageBox::information(NULL, title, text, "Close"); } else { berry::PlatformUI::GetWorkbench()->GetIntroManager()->ShowIntro( berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow(), false); } } void QmitkExtWorkbenchWindowAdvisorHack::onHelp() { ctkPluginContext* context = QmitkCommonExtPlugin::getContext(); if (context == 0) { MITK_WARN << "Plugin context not set, unable to open context help"; return; } // Check if the org.blueberry.ui.qt.help plug-in is installed and started QList > plugins = context->getPlugins(); foreach(QSharedPointer p, plugins) { if (p->getSymbolicName() == "org.blueberry.ui.qt.help") { if (p->getState() != ctkPlugin::ACTIVE) { // try to activate the plug-in explicitly try { p->start(ctkPlugin::START_TRANSIENT); } catch (const ctkPluginException& pe) { MITK_ERROR << "Activating org.blueberry.ui.qt.help failed: " << pe.what(); return; } } } } ctkServiceReference eventAdminRef = context->getServiceReference(); ctkEventAdmin* eventAdmin = 0; if (eventAdminRef) { eventAdmin = context->getService(eventAdminRef); } if (eventAdmin == 0) { MITK_WARN << "ctkEventAdmin service not found. Unable to open context help"; } else { ctkEvent ev("org/blueberry/ui/help/CONTEXTHELP_REQUESTED"); eventAdmin->postEvent(ev); } } void QmitkExtWorkbenchWindowAdvisorHack::onHelpContents() { berry::PlatformUI::GetWorkbench()->ShowPerspective("org.blueberry.perspectives.help", berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()); } void QmitkExtWorkbenchWindowAdvisorHack::onAbout() { QmitkAboutDialog* aboutDialog = new QmitkAboutDialog(QApplication::activeWindow(),NULL); aboutDialog->open(); } void QmitkExtWorkbenchWindowAdvisor::HookTitleUpdateListeners( berry::IWorkbenchWindowConfigurer::Pointer configurer) { // hook up the listeners to update the window title titlePartListener = new PartListenerForTitle(this); titlePerspectiveListener = new PerspectiveListenerForTitle(this); editorPropertyListener = new berry::PropertyChangeIntAdapter< QmitkExtWorkbenchWindowAdvisor>(this, &QmitkExtWorkbenchWindowAdvisor::PropertyChange); // configurer.getWindow().addPageListener(new IPageListener() { // public void pageActivated(IWorkbenchPage page) { // updateTitle(false); // } // // public void pageClosed(IWorkbenchPage page) { // updateTitle(false); // } // // public void pageOpened(IWorkbenchPage page) { // // do nothing // } // }); configurer->GetWindow()->AddPerspectiveListener(titlePerspectiveListener); configurer->GetWindow()->GetPartService()->AddPartListener(titlePartListener); } std::string QmitkExtWorkbenchWindowAdvisor::ComputeTitle() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); berry::IWorkbenchPage::Pointer currentPage = configurer->GetWindow()->GetActivePage(); berry::IEditorPart::Pointer activeEditor; if (currentPage) { activeEditor = lastActiveEditor.Lock(); } std::string title; //TODO Product // IProduct product = Platform.getProduct(); // if (product != null) { // title = product.getName(); // } // instead of the product name, we use a custom variable for now title = productName; if(showMitkVersionInfo) { title += std::string(" ") + MITK_VERSION_STRING; } if (showVersionInfo) { // add version informatioin QString versions = QString(" (ITK %1.%2.%3 VTK %4.%5.%6 Qt %7 MITK %8)") .arg(ITK_VERSION_MAJOR).arg(ITK_VERSION_MINOR).arg(ITK_VERSION_PATCH) .arg(VTK_MAJOR_VERSION).arg(VTK_MINOR_VERSION).arg(VTK_BUILD_VERSION) .arg(QT_VERSION_STR) .arg(MITK_VERSION_STRING); title += versions.toStdString(); } if (currentPage) { if (activeEditor) { lastEditorTitle = activeEditor->GetTitleToolTip(); if (!lastEditorTitle.empty()) title = lastEditorTitle + " - " + title; } berry::IPerspectiveDescriptor::Pointer persp = currentPage->GetPerspective(); std::string label = ""; if (persp) { label = persp->GetLabel(); } berry::IAdaptable* input = currentPage->GetInput(); if (input && input != wbAdvisor->GetDefaultPageInput()) { label = currentPage->GetLabel(); } if (!label.empty()) { title = label + " - " + title; } } title += " (Not for use in diagnosis or treatment of patients)"; return title; } void QmitkExtWorkbenchWindowAdvisor::RecomputeTitle() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); std::string oldTitle = configurer->GetTitle(); std::string newTitle = ComputeTitle(); if (newTitle != oldTitle) { configurer->SetTitle(newTitle); } } void QmitkExtWorkbenchWindowAdvisor::UpdateTitle(bool editorHidden) { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); berry::IWorkbenchWindow::Pointer window = configurer->GetWindow(); berry::IEditorPart::Pointer activeEditor; berry::IWorkbenchPage::Pointer currentPage = window->GetActivePage(); berry::IPerspectiveDescriptor::Pointer persp; berry::IAdaptable* input = 0; if (currentPage) { activeEditor = currentPage->GetActiveEditor(); persp = currentPage->GetPerspective(); input = currentPage->GetInput(); } if (editorHidden) { activeEditor = 0; } // Nothing to do if the editor hasn't changed if (activeEditor == lastActiveEditor.Lock() && currentPage == lastActivePage.Lock() && persp == lastPerspective.Lock() && input == lastInput) { return; } if (!lastActiveEditor.Expired()) { lastActiveEditor.Lock()->RemovePropertyListener(editorPropertyListener); } lastActiveEditor = activeEditor; lastActivePage = currentPage; lastPerspective = persp; lastInput = input; if (activeEditor) { activeEditor->AddPropertyListener(editorPropertyListener); } RecomputeTitle(); } void QmitkExtWorkbenchWindowAdvisor::PropertyChange(berry::Object::Pointer /*source*/, int propId) { if (propId == berry::IWorkbenchPartConstants::PROP_TITLE) { if (!lastActiveEditor.Expired()) { std::string newTitle = lastActiveEditor.Lock()->GetPartName(); if (lastEditorTitle != newTitle) { RecomputeTitle(); } } } } void QmitkExtWorkbenchWindowAdvisor::SetPerspectiveExcludeList(std::vector v) { this->perspectiveExcludeList = v; } std::vector QmitkExtWorkbenchWindowAdvisor::GetPerspectiveExcludeList() { return this->perspectiveExcludeList; } void QmitkExtWorkbenchWindowAdvisor::SetViewExcludeList(std::vector v) { this->viewExcludeList = v; } std::vector QmitkExtWorkbenchWindowAdvisor::GetViewExcludeList() { return this->viewExcludeList; } void QmitkExtWorkbenchWindowAdvisor::PostWindowClose() { berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow(); QMainWindow* mainWindow = static_cast (window->GetShell()->GetControl()); QSettings settings(GetQSettingsFile(), QSettings::IniFormat); settings.setValue("ToolbarPosition", mainWindow->saveState()); } QString QmitkExtWorkbenchWindowAdvisor::GetQSettingsFile() const { QFileInfo settingsInfo = QmitkCommonExtPlugin::getContext()->getDataFile(QT_SETTINGS_FILENAME); return settingsInfo.canonicalFilePath(); } diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/CMakeLists.txt b/Plugins/org.mitk.gui.qt.meshdecimation/CMakeLists.txt new file mode 100644 index 0000000000..514256f123 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.meshdecimation/CMakeLists.txt @@ -0,0 +1,8 @@ +PROJECT(org_mitk_gui_qt_meshdecimation) + +MACRO_CREATE_MITK_CTK_PLUGIN( + EXPORT_DIRECTIVE MESHDECIMATION_EXPORT + EXPORTED_INCLUDE_SUFFIXES src + MODULE_DEPENDENCIES QmitkExt +) + diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/documentation/Manual/meshdecimation-manual.dox b/Plugins/org.mitk.gui.qt.meshdecimation/documentation/Manual/meshdecimation-manual.dox new file mode 100644 index 0000000000..33ead4ba4d --- /dev/null +++ b/Plugins/org.mitk.gui.qt.meshdecimation/documentation/Manual/meshdecimation-manual.dox @@ -0,0 +1,33 @@ +/** + +\bundlemainpage{org_meshdecimation} The Mesh Decimation Module + +\image html meshdecimation.png "Icon of the Module" + +Available sections: + - \ref meshdecimationOverview + - \ref meshdecimationFeatures + - \ref meshdecimationUsage + +\section meshdecimationOverview Overview + +MeshDecimation is a user friendly tool to decimate a MITK surface. + +\section meshdecimationFeatures Features + +The module offers two basic procedures to decimate surfaces: One that reduces a surface with a possible loss of topology (quality), but with a garuanteed reduction rate that is expressed in terms of percent of the original mesh. The other variant preserves the topology and stops decimating when it detects heavy topological changes. + +\section meshdecimationUsage Usage + +\image html meshdecimation-ui.png "The user interface of the Mesh Decimation Module" + +The usage of the module should be straightforward, as shown in the screenshot. To decimate a MITK surface do the following: +- Select a surface in the datamanager +- Enter a target reduction rate +- Select a decimation method (\ref meshdecimationFeatures) +- Press the "Decimate" button +- Repeat the process until you are satisfied with the decimation +- Save the surface to disk + +*/ + diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/documentation/Manual/meshdecimation-ui.png b/Plugins/org.mitk.gui.qt.meshdecimation/documentation/Manual/meshdecimation-ui.png new file mode 100644 index 0000000000..97b45078fe Binary files /dev/null and b/Plugins/org.mitk.gui.qt.meshdecimation/documentation/Manual/meshdecimation-ui.png differ diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/documentation/Manual/meshdecimation.png b/Plugins/org.mitk.gui.qt.meshdecimation/documentation/Manual/meshdecimation.png new file mode 100644 index 0000000000..8ba6124527 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.meshdecimation/documentation/Manual/meshdecimation.png differ diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/documentation/doxygen/modules.dox b/Plugins/org.mitk.gui.qt.meshdecimation/documentation/doxygen/modules.dox new file mode 100644 index 0000000000..8c37f6ce32 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.meshdecimation/documentation/doxygen/modules.dox @@ -0,0 +1,8 @@ +/** + \defgroup org_mbi_gui_qt_meshdecimation org.mbi.gui.qt.meshdecimation Plugin + \ingroup MBIPlugins + + \brief The plugin is a user friendly tool to decimate a mesh. + + It has no public API and uses VTK filters internally to decimate MITK Surfaces. Please refer to the user manual for usage information. +*/ diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/files.cmake b/Plugins/org.mitk.gui.qt.meshdecimation/files.cmake new file mode 100644 index 0000000000..6db717a8e8 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.meshdecimation/files.cmake @@ -0,0 +1,37 @@ +SET(SRC_CPP_FILES + +) + +SET(INTERNAL_CPP_FILES + QmitkMeshDecimationView.cpp + mitkPluginActivator.cpp +) + +SET(UI_FILES + src/internal/QmitkMeshDecimationView.ui +) + +SET(MOC_H_FILES + src/internal/QmitkMeshDecimationView.h + src/internal/mitkPluginActivator.h +) + +SET(CACHED_RESOURCE_FILES + resources/meshdecimation.png + plugin.xml +) + +SET(QRC_FILES + +) + +SET(CPP_FILES ) + +foreach(file ${SRC_CPP_FILES}) + SET(CPP_FILES ${CPP_FILES} src/${file}) +endforeach(file ${SRC_CPP_FILES}) + +foreach(file ${INTERNAL_CPP_FILES}) + SET(CPP_FILES ${CPP_FILES} src/internal/${file}) +endforeach(file ${INTERNAL_CPP_FILES}) + diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.meshdecimation/manifest_headers.cmake new file mode 100644 index 0000000000..824126c979 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.meshdecimation/manifest_headers.cmake @@ -0,0 +1,5 @@ +set(Plugin-Name "Mesh Decimation") +set(Plugin-Version "0.1") +set(Plugin-Vendor "DKFZ, Medical and Biological Informatics") +set(Plugin-ContactAddress "http://www.mitk.org") +set(Require-Plugin org.mitk.gui.qt.common) diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/plugin.xml b/Plugins/org.mitk.gui.qt.meshdecimation/plugin.xml new file mode 100644 index 0000000000..13005afb6a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.meshdecimation/plugin.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/resources/meshdecimation.png b/Plugins/org.mitk.gui.qt.meshdecimation/resources/meshdecimation.png new file mode 100644 index 0000000000..8ba6124527 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.meshdecimation/resources/meshdecimation.png differ diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/QmitkMeshDecimationView.cpp b/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/QmitkMeshDecimationView.cpp new file mode 100644 index 0000000000..44d8642dac --- /dev/null +++ b/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/QmitkMeshDecimationView.cpp @@ -0,0 +1,232 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision$ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +// Blueberry +#include +#include + +// Qmitk +#include "QmitkMeshDecimationView.h" +#include "QmitkStdMultiWidget.h" + +// Qt +#include +#include +#include + +// mitk +#include +#include +#include + +// VTK +#include +#include +#include + +const std::string QmitkMeshDecimationView::VIEW_ID = "org.mitk.views.meshdecimation"; + +QmitkMeshDecimationView::QmitkMeshDecimationView() +: QmitkAbstractView() +, m_Controls( 0 ) +, m_MessageBox( 0 ) +, m_MessageBoxDisplayTime( 5 ) +, m_MessageBoxTimer( new QTimer(this) ) +{ + m_MessageBoxTimer->setInterval(1000); + connect(m_MessageBoxTimer, SIGNAL(timeout()), this, SLOT(MessageBoxTimer())); +} + +QmitkMeshDecimationView::~QmitkMeshDecimationView() +{ +} + +void QmitkMeshDecimationView::CreateQtPartControl( QWidget *parent ) +{ + // build up qt view, unless already done + if ( !m_Controls ) + { + // create GUI widgets from the Qt Designer's .ui file + m_Controls = new Ui::QmitkMeshDecimationView; + m_Controls->setupUi( parent ); + + connect((QObject*)(m_Controls->Decimate), SIGNAL(clicked()), this, SLOT(Decimate())); + } +} + +void QmitkMeshDecimationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList& nodes) +{ + this->m_Node = 0; + this->DisableGui(); + + for( int i=0; iGetData(); + if( dynamic_cast( baseData ) != 0 ) + { + this->m_Node = nodes.at(i); + this->m_Controls->NodeLabel->setEnabled( true ); + this->m_Controls->TargetReduction->setEnabled( true ); + this->m_Controls->Decimate->setEnabled( true ); + this->m_Controls->DecimationType->setEnabled( true ); + break; + } + } + this->UpdateNodeLabel(); +} + +void QmitkMeshDecimationView::SetFocus() +{ + m_Controls->NodeLabel->setFocus(); +} + +void QmitkMeshDecimationView::DisableGui() +{ + this->m_Controls->NodeLabel->setEnabled( false ); + this->m_Controls->TargetReduction->setEnabled( false ); + this->m_Controls->Decimate->setEnabled( false ); + this->m_Controls->DecimationType->setEnabled( false ); +} + +void QmitkMeshDecimationView::UpdateNodeLabel() +{ + QString nodeLabel = "none selected"; + if (this->m_Node.IsNotNull()) + { + vtkPolyData* polyData = static_cast(this->m_Node->GetData())->GetVtkPolyData(); + nodeLabel = QString::fromStdString( this->m_Node->GetName() ); + nodeLabel.append(" ("); + nodeLabel.append( QString("%1").arg( polyData->GetNumberOfCells() ) ); + nodeLabel.append(" cells)"); + } + this->m_Controls->NodeLabel->setText( nodeLabel ); +} + +void QmitkMeshDecimationView::Decimate() +{ + if (this->m_Node.IsNotNull()) + { + this->WaitCursorOn(); + QString error; + try + { + vtkPolyData* polyData = static_cast(this->m_Node->GetData())->GetVtkPolyData(); + int startingNumberOfCells = polyData->GetNumberOfCells(); + if( startingNumberOfCells == 0 ) + throw std::logic_error("Could not reduce Mesh since it is already empty."); + + + double targetReduction = static_cast(this->m_Controls->TargetReduction->value()) / 100.0; + + vtkPolyData* newPolyData = 0; + if( this->m_Controls->DecimationType->currentText() == "DecimatePro" ) + { + vtkSmartPointer decimator = vtkDecimatePro::New(); + decimator->SetInput( polyData ); + decimator->SetTargetReduction( targetReduction ); + decimator->SetPreserveTopology( 1 ); + decimator->Update(); + newPolyData = decimator->GetOutput(); + } + else + { + vtkSmartPointer decimator = vtkQuadricDecimation::New(); + decimator->SetInput( polyData ); + decimator->SetTargetReduction( targetReduction ); + decimator->Update(); + newPolyData = decimator->GetOutput(); + } + + if( newPolyData != 0 ) + { + static_cast(this->m_Node->GetData())->SetVtkPolyData(newPolyData); + this->m_Node->Update(); + this->UpdateNodeLabel(); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } + + int endingNumberOfCells = newPolyData->GetNumberOfCells(); + int reducedCells = startingNumberOfCells - endingNumberOfCells; + + QString message; + if( reducedCells == 0 ) + message = "Decimation was not able to reduce any more cells from Mesh."; + else + message = QString("Decimation successful. %1 cells removed").arg(reducedCells); + + this->WaitCursorOff(); + this->ShowAutoCloseMessageDialog(message); + } + catch(std::exception& e) + { + this->WaitCursorOff(); + error = QString::fromStdString(e.what()); + } + catch(...) + { + this->WaitCursorOff(); + error = "An unknown error occured. Please report error at bugs.mitk.org."; + } + if( !error.isEmpty() ) + this->ShowAutoCloseErrorDialog(error); + } + else + this->DisableGui(); +} + +void QmitkMeshDecimationView::MessageBoxTimer() +{ + m_MessageBoxDisplayTime--; + QString text = m_MessageBoxText; + text.append( QString(" (Will close in %1 seconds)").arg(m_MessageBoxDisplayTime) ); + m_MessageBox->setText( text ); + + if( m_MessageBoxDisplayTime == 0 ) + { + m_MessageBox->accept(); + } +} + +void QmitkMeshDecimationView::ShowAutoCloseErrorDialog( const QString& error ) +{ + m_MessageBox = new QMessageBox(QMessageBox::Warning, "Warning", error, QMessageBox::Ok ); + m_MessageBoxDisplayTime = 5; + m_MessageBoxText = error; + + this->MessageBoxTimer(); + m_MessageBoxTimer->start(); + m_MessageBox->exec(); + m_MessageBoxTimer->stop(); + + delete m_MessageBox; + m_MessageBox = 0; +} + +void QmitkMeshDecimationView::ShowAutoCloseMessageDialog( const QString& message ) +{ + m_MessageBox = new QMessageBox(QMessageBox::Information, "Information", message, QMessageBox::Ok ); + m_MessageBoxDisplayTime = 5; + m_MessageBoxText = message; + + this->MessageBoxTimer(); + m_MessageBoxTimer->start(); + m_MessageBox->exec(); + m_MessageBoxTimer->stop(); + + delete m_MessageBox; + m_MessageBox = 0; +} diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/QmitkMeshDecimationView.h b/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/QmitkMeshDecimationView.h new file mode 100644 index 0000000000..e333577be4 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/QmitkMeshDecimationView.h @@ -0,0 +1,72 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision$ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#ifndef QmitkMeshDecimationView_h +#define QmitkMeshDecimationView_h + +#include +#include +#include +#include +#include +#include "ui_QmitkMeshDecimationView.h" + +/*! + \brief QmitkMeshDecimationView + + \warning This application module is not yet documented. Use "svn blame/praise/annotate" and ask the author to provide basic documentation. + + \sa QmitkFunctionality + \ingroup Functionalities +*/ +class QmitkMeshDecimationView : public QmitkAbstractView +{ +// this is needed for all Qt objects that should have a Qt meta-object +// (everything that derives from QObject and wants to have signal/slots) +Q_OBJECT +public: + static const std::string VIEW_ID; + + QmitkMeshDecimationView(); + virtual ~QmitkMeshDecimationView(); + + virtual void CreateQtPartControl(QWidget *parent); + virtual void SetFocus(); + + virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList&); +protected slots: + void Decimate(); + void MessageBoxTimer(); +protected: + void DisableGui(); + void UpdateNodeLabel(); + void ShowAutoCloseErrorDialog( const QString& error ); + void ShowAutoCloseMessageDialog( const QString& message ); + + Ui::QmitkMeshDecimationView* m_Controls; + mitk::WeakPointer m_Node; + QWidget* m_Parent; + QMessageBox* m_MessageBox; + int m_MessageBoxDisplayTime; + QString m_MessageBoxText; + QTimer *m_MessageBoxTimer; +}; + + + +#endif // _QMITKMESHSMOOTHINGVIEW_H_INCLUDED + diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/QmitkMeshDecimationView.ui b/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/QmitkMeshDecimationView.ui new file mode 100644 index 0000000000..23d2dcd7f1 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/QmitkMeshDecimationView.ui @@ -0,0 +1,145 @@ + + + QmitkMeshDecimationView + + + + 0 + 0 + 389 + 811 + + + + + 0 + 0 + + + + QmitkTemplate + + + + + + + + Selected node: + + + + + + + none selected + + + + + + + + 132 + 0 + + + + Target reduction [percent]: + + + + + + + false + + + 1 + + + 100 + + + 50 + + + + + + + Decimation Type*: + + + true + + + + + + + false + + + + DecimatePro + + + + + Quadric + + + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">* Quadric Decimation reduces the mesh without accounting much for its topology. DecimatePro preserves topology best but does not garantuee the target reduction rate.</span></p></body></html> + + + true + + + + + + + false + + + Do image processing + + + Decimate + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 220 + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/mitkPluginActivator.cpp new file mode 100644 index 0000000000..0995ef5ffe --- /dev/null +++ b/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/mitkPluginActivator.cpp @@ -0,0 +1,22 @@ +#include "mitkPluginActivator.h" + +#include + +#include "QmitkMeshDecimationView.h" + +namespace mitk { + +void PluginActivator::start(ctkPluginContext* context) +{ + BERRY_REGISTER_EXTENSION_CLASS(QmitkMeshDecimationView, context) + //BERRY_REGISTER_EXTENSION_CLASS(QmitkHexVolumeMesherView, context) +} + +void PluginActivator::stop(ctkPluginContext* context) +{ + Q_UNUSED(context) +} + +} + +Q_EXPORT_PLUGIN2(org_mbi_gui_qt_meshdecimation, mitk::PluginActivator) diff --git a/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/mitkPluginActivator.h b/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/mitkPluginActivator.h new file mode 100644 index 0000000000..a34be34be4 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.meshdecimation/src/internal/mitkPluginActivator.h @@ -0,0 +1,23 @@ +#ifndef MITKPLUGINACTIVATOR_H +#define MITKPLUGINACTIVATOR_H + +#include + +namespace mitk { + +class PluginActivator : + public QObject, public ctkPluginActivator +{ + Q_OBJECT + Q_INTERFACES(ctkPluginActivator) + +public: + + void start(ctkPluginContext* context); + void stop(ctkPluginContext* context); + +}; // PluginActivator + +} + +#endif // MITKPLUGINACTIVATOR_H diff --git a/Plugins/org.mitk.gui.qt.segmentation/CMakeLists.txt b/Plugins/org.mitk.gui.qt.segmentation/CMakeLists.txt index 4cf6b1231e..c0878bf72f 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.segmentation/CMakeLists.txt @@ -1,7 +1,7 @@ project(org_mitk_gui_qt_segmentation) MACRO_CREATE_MITK_CTK_PLUGIN( EXPORT_DIRECTIVE MITK_QT_SEGMENTATION EXPORTED_INCLUDE_SUFFIXES src - MODULE_DEPENDENCIES QmitkExt ClippingTools + MODULE_DEPENDENCIES QmitkExt ClippingTools Segmentation ) diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp index 2b2701c80d..39be172557 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp @@ -1,1227 +1,1230 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkDataNodeObject.h" #include "mitkProperties.h" #include "mitkSegTool2D.h" #include "mitkGlobalInteraction.h" #include "QmitkStdMultiWidget.h" #include "QmitkNewSegmentationDialog.h" #include #include #include "QmitkSegmentationView.h" #include "QmitkSegmentationPostProcessing.h" #include "QmitkSegmentationOrganNamesHandling.cpp" #include #include //For Segmentation in rotated slices //TODO clean up includes #include "mitkVtkResliceInterpolationProperty.h" #include "mitkPlanarCircle.h" #include "mitkGetModuleContext.h" #include "mitkModule.h" #include "mitkModuleRegistry.h" +#include "mitkSegmentationObjectFactory.h" + const std::string QmitkSegmentationView::VIEW_ID = "org.mitk.views.segmentation"; // public methods QmitkSegmentationView::QmitkSegmentationView() :m_Parent(NULL) ,m_Controls(NULL) ,m_MultiWidget(NULL) ,m_RenderingManagerObserverTag(0) { + RegisterSegmentationObjectFactory(); } QmitkSegmentationView::~QmitkSegmentationView() { // delete m_PostProcessing; delete m_Controls; } void QmitkSegmentationView::NewNodesGenerated() { // ForceDisplayPreferencesUponAllImages(); } void QmitkSegmentationView::NewNodeObjectsGenerated(mitk::ToolManager::DataVectorType* nodes) { if (!nodes) return; mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); if (!toolManager) return; for (mitk::ToolManager::DataVectorType::iterator iter = nodes->begin(); iter != nodes->end(); ++iter) { this->FireNodeSelected( *iter ); // only last iteration meaningful, multiple generated objects are not taken into account here } } void QmitkSegmentationView::Activated() { // should be moved to ::BecomesVisible() or similar if( m_Controls ) { m_Controls->m_ManualToolSelectionBox->setEnabled( true ); m_Controls->m_OrganToolSelectionBox->setEnabled( true ); m_Controls->m_LesionToolSelectionBox->setEnabled( true ); m_Controls->m_SlicesInterpolator->Enable3DInterpolation( m_Controls->widgetStack->currentWidget() == m_Controls->pageManual ); //TODO Remove Observer itk::ReceptorMemberCommand::Pointer command1 = itk::ReceptorMemberCommand::New(); command1->SetCallbackFunction( this, &QmitkSegmentationView::RenderingManagerReinitialized ); m_RenderingManagerObserverTag = mitk::RenderingManager::GetInstance()->AddObserver( mitk::RenderingManagerViewsInitializedEvent(), command1 ); //Adding observers for node visibility to existing segmentations mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isSegmentation = mitk::NodePredicateAnd::New( isImage, isBinary ); mitk::DataStorage::SetOfObjects::ConstPointer segmentations = this->GetDefaultDataStorage()->GetSubset( isSegmentation ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = segmentations->begin(); iter != segmentations->end(); ++iter) { mitk::DataNode* node = *iter; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnWorkingNodeVisibilityChanged); m_WorkingDataObserverTags.insert( std::pair( node, node->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ) ) ); } if(segmentations->Size() > 0) { FireNodeSelected(segmentations->ElementAt(0)); segmentations->ElementAt(0)->GetProperty("visible")->Modified(); } } } void QmitkSegmentationView::Deactivated() { if( m_Controls ) { mitk::RenderingManager::GetInstance()->RemoveObserver( m_RenderingManagerObserverTag ); m_Controls->m_ManualToolSelectionBox->setEnabled( false ); //deactivate all tools m_Controls->m_ManualToolSelectionBox->GetToolManager()->ActivateTool(-1); m_Controls->m_OrganToolSelectionBox->setEnabled( false ); m_Controls->m_LesionToolSelectionBox->setEnabled( false ); m_Controls->m_SlicesInterpolator->EnableInterpolation( false ); //Removing all observers for ( NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter ) { (*dataIter).first->GetProperty("visible")->RemoveObserver( (*dataIter).second ); } m_WorkingDataObserverTags.clear(); // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); service->RemoveAllPlanePositions(); } } void QmitkSegmentationView::StdMultiWidgetAvailable( QmitkStdMultiWidget& stdMultiWidget ) { SetMultiWidget(&stdMultiWidget); } void QmitkSegmentationView::StdMultiWidgetNotAvailable() { SetMultiWidget(NULL); } void QmitkSegmentationView::StdMultiWidgetClosed( QmitkStdMultiWidget& /*stdMultiWidget*/ ) { SetMultiWidget(NULL); } void QmitkSegmentationView::SetMultiWidget(QmitkStdMultiWidget* multiWidget) { if (m_MultiWidget) { mitk::SlicesCoordinator* coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) { coordinator->RemoveObserver( m_SlicesRotationObserverTag1 ); } coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) { coordinator->RemoveObserver( m_SlicesRotationObserverTag2 ); } } // save the current multiwidget as the working widget m_MultiWidget = multiWidget; //TODO Remove Observers if (m_MultiWidget) { mitk::SlicesCoordinator* coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) { itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSegmentationView::SliceRotation ); m_SlicesRotationObserverTag1 = coordinator->AddObserver( mitk::SliceRotationEvent(), command2 ); } coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) { itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSegmentationView::SliceRotation ); m_SlicesRotationObserverTag2 = coordinator->AddObserver( mitk::SliceRotationEvent(), command2 ); } } //TODO End Remove Observers if (m_Parent) { m_Parent->setEnabled(m_MultiWidget); } // tell the interpolation about toolmanager and multiwidget (and data storage) if (m_Controls && m_MultiWidget) { mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); m_Controls->m_SlicesInterpolator->SetDataStorage( *(this->GetDefaultDataStorage())); m_Controls->m_SlicesInterpolator->Initialize( toolManager, m_MultiWidget ); } } void QmitkSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences*) { ForceDisplayPreferencesUponAllImages(); } //TODO remove function void QmitkSegmentationView::RenderingManagerReinitialized(const itk::EventObject&) { CheckImageAlignment(); } //TODO remove function void QmitkSegmentationView::SliceRotation(const itk::EventObject&) { CheckImageAlignment(); } // protected slots void QmitkSegmentationView::CreateNewSegmentation() { mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull()) { if (image->GetDimension()>1) { // ask about the name and organ type of the new segmentation QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog( m_Parent ); // needs a QWidget as parent, "this" is not QWidget QString storedList = QString::fromStdString( this->GetPreferences()->GetByteArray("Organ-Color-List","") ); QStringList organColors; if (storedList.isEmpty()) { organColors = GetDefaultOrganColorString(); } else { /* a couple of examples of how organ names are stored: a simple item is built up like 'name#AABBCC' where #AABBCC is the hexadecimal notation of a color as known from HTML items are stored separated by ';' this makes it necessary to escape occurrences of ';' in name. otherwise the string "hugo;ypsilon#AABBCC;eugen#AABBCC" could not be parsed as two organs but we would get "hugo" and "ypsilon#AABBCC" and "eugen#AABBCC" so the organ name "hugo;ypsilon" is stored as "hugo\;ypsilon" and must be unescaped after loading the following lines could be one split with Perl's negative lookbehind */ // recover string list from BlueBerry view's preferences QString storedString = QString::fromStdString( this->GetPreferences()->GetByteArray("Organ-Color-List","") ); MITK_DEBUG << "storedString: " << storedString.toStdString(); // match a string consisting of any number of repetitions of either "anything but ;" or "\;". This matches everything until the next unescaped ';' QRegExp onePart("(?:[^;]|\\\\;)*"); MITK_DEBUG << "matching " << onePart.pattern().toStdString(); int count = 0; int pos = 0; while( (pos = onePart.indexIn( storedString, pos )) != -1 ) { ++count; int length = onePart.matchedLength(); if (length == 0) break; QString matchedString = storedString.mid(pos, length); MITK_DEBUG << " Captured length " << length << ": " << matchedString.toStdString(); pos += length + 1; // skip separating ';' // unescape possible occurrences of '\;' in the string matchedString.replace("\\;", ";"); // add matched string part to output list organColors << matchedString; } MITK_DEBUG << "Captured " << count << " organ name/colors"; } dialog->SetSuggestionList( organColors ); int dialogReturnValue = dialog->exec(); if ( dialogReturnValue == QDialog::Rejected ) return; // user clicked cancel or pressed Esc or something similar // ask the user about an organ type and name, add this information to the image's (!) propertylist // create a new image of the same dimensions and smallest possible pixel type mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); mitk::Tool* firstTool = toolManager->GetToolById(0); if (firstTool) { try { mitk::DataNode::Pointer emptySegmentation = firstTool->CreateEmptySegmentationNode( image, dialog->GetSegmentationName().toStdString(), dialog->GetColor() ); //Here we change the reslice interpolation mode for a segmentation, so that contours in rotated slice can be shown correctly emptySegmentation->SetProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_LINEAR) ); // initialize showVolume to false to prevent recalculating the volume while working on the segmentation emptySegmentation->SetProperty( "showVolume", mitk::BoolProperty::New( false ) ); if (!emptySegmentation) return; // could be aborted by user UpdateOrganList( organColors, dialog->GetSegmentationName(), dialog->GetColor() ); /* escape ';' here (replace by '\;'), see longer comment above */ std::string stringForStorage = organColors.replaceInStrings(";","\\;").join(";").toStdString(); MITK_DEBUG << "Will store: " << stringForStorage; this->GetPreferences()->PutByteArray("Organ-Color-List", stringForStorage ); this->GetPreferences()->Flush(); if(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)) { m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)->SetSelected(false); } emptySegmentation->SetSelected(true); this->GetDefaultDataStorage()->Add( emptySegmentation, node ); // add as a child, because the segmentation "derives" from the original this->FireNodeSelected( emptySegmentation ); this->OnSelectionChanged( emptySegmentation ); this->SetToolManagerSelection(node, emptySegmentation); } catch (std::bad_alloc) { QMessageBox::warning(NULL,"Create new segmentation","Could not allocate memory for new segmentation"); } } } else { QMessageBox::information(NULL,"Segmentation","Segmentation is currently not supported for 2D images"); } } } else { MITK_ERROR << "'Create new segmentation' button should never be clickable unless a patient image is selected..."; } } void QmitkSegmentationView::OnWorkingNodeVisibilityChanged(/*const itk::Object* caller, const itk::EventObject& e*/) { if (!m_Parent || !m_Parent->isVisible()) return; // The new selection behaviour is: // // When clicking on the checkbox of a segmentation the node will e selected and its reference node either // The previous selected segmentation (if there is one) will be deselected. Additionally a reinit on the // selected segmenation will be performed. // If more than one segmentation is selected the tools will be disabled. if (!m_Controls) return; // might happen on initialization (preferences loaded) mitk::DataNode::Pointer referenceDataNew = mitk::DataNode::New(); mitk::DataNode::Pointer workingData; bool workingNodeIsVisible (true); unsigned int numberOfSelectedSegmentations (0); // iterate all images mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDefaultDataStorage()->GetSubset( isImage ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); bool isSegmentation(false); node->GetBoolProperty("binary", isSegmentation); if (node->IsSelected() && isSegmentation) { workingNodeIsVisible = node->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); if (!workingNodeIsVisible) return; numberOfSelectedSegmentations++; workingData = node; if (this->GetDefaultDataStorage()->GetSources(node)->Size() != 0) { referenceDataNew = this->GetDefaultDataStorage()->GetSources(node)->ElementAt(0); } bool isBinary(false); //Find topmost source or first source which is no binary image while (referenceDataNew && this->GetDefaultDataStorage()->GetSources(referenceDataNew)->Size() != 0) { referenceDataNew = this->GetDefaultDataStorage()->GetSources(referenceDataNew)->ElementAt(0); referenceDataNew->GetBoolProperty("binary",isBinary); if (!isBinary) break; } if (workingNodeIsVisible && referenceDataNew) { //Since the binary property of a segmentation can be set to false and afterwards you can create a new segmentation out of it //->could lead to a deadloop NodeTagMapType::iterator searchIter = m_WorkingDataObserverTags.find( referenceDataNew ); if ( searchIter != m_WorkingDataObserverTags.end()) { referenceDataNew->GetProperty("visible")->RemoveObserver( (*searchIter).second ); } referenceDataNew->SetVisibility(true); } //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(referenceDataNew) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); continue; } if (workingData.IsNull() || (workingNodeIsVisible && node != referenceDataNew)) { node->SetVisibility((false)); } } if(numberOfSelectedSegmentations == 1) SetToolManagerSelection(referenceDataNew, workingData); mitk::DataStorage::SetOfObjects::Pointer temp = mitk::DataStorage::SetOfObjects::New(); temp->InsertElement(0,workingData); mitk::TimeSlicedGeometry::Pointer bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(temp); // initialize the views to the bounding geometry /*mitk::RenderingManager::GetInstance()->InitializeViews(bounds); mitk::RenderingManager::GetInstance()->RequestUpdateAll();*/ } void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node) { bool isSeg(false); bool isHelperObject(false); node->GetBoolProperty("helper object", isHelperObject); node->GetBoolProperty("binary", isSeg); if(isSeg && !isHelperObject) { mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations(node, mitk::NodePredicateProperty::New("isContourMarker" , mitk::BoolProperty::New(true))); // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; service->RemovePlanePosition(id); this->GetDataStorage()->Remove(it->Value()); } mitk::DataNode* tempNode = const_cast(node); node->GetProperty("visible")->RemoveObserver( m_WorkingDataObserverTags[tempNode] ); m_WorkingDataObserverTags.erase(tempNode); this->SetToolManagerSelection(NULL, NULL); } } void QmitkSegmentationView::CreateSegmentationFromSurface() { mitk::DataNode::Pointer surfaceNode = m_Controls->MaskSurfaces->GetSelectedNode(); mitk::Surface::Pointer surface(0); if(surfaceNode.IsNotNull()) surface = dynamic_cast ( surfaceNode->GetData() ); if(surface.IsNull()) { this->HandleException( "No surface selected.", m_Parent, true); return; } mitk::DataNode::Pointer imageNode = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); mitk::Image::Pointer image(0); if (imageNode.IsNotNull()) image = dynamic_cast( imageNode->GetData() ); if(image.IsNull()) { this->HandleException( "No image selected.", m_Parent, true); return; } mitk::SurfaceToImageFilter::Pointer s2iFilter = mitk::SurfaceToImageFilter::New(); s2iFilter->MakeOutputBinaryOn(); s2iFilter->SetInput(surface); s2iFilter->SetImage(image); s2iFilter->Update(); mitk::DataNode::Pointer resultNode = mitk::DataNode::New(); std::string nameOfResultImage = imageNode->GetName(); nameOfResultImage.append(surfaceNode->GetName()); resultNode->SetProperty("name", mitk::StringProperty::New(nameOfResultImage) ); resultNode->SetProperty("binary", mitk::BoolProperty::New(true) ); resultNode->SetData( s2iFilter->GetOutput() ); this->GetDataStorage()->Add(resultNode, imageNode); } void QmitkSegmentationView::ManualToolSelected(int id) { // disable crosshair movement when a manual drawing tool is active (otherwise too much visual noise) if (m_MultiWidget) { if (id >= 0) { m_MultiWidget->DisableNavigationControllerEventListening(); } else { m_MultiWidget->EnableNavigationControllerEventListening(); } } } void QmitkSegmentationView::ToolboxStackPageChanged(int id) { // interpolation only with manual tools visible m_Controls->m_SlicesInterpolator->EnableInterpolation( id == 0 ); if( id == 0 ) { mitk::DataNode::Pointer workingData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0); if( workingData.IsNotNull() ) { m_Controls->lblSegmentation->setText( workingData->GetName().c_str() ); m_Controls->lblSegImage->show(); m_Controls->lblSegmentation->show(); } } else { m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } // this is just a workaround, should be removed when all tools support 3D+t if (id==2) // lesions { mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull()) { if (image->GetDimension()>3) { m_Controls->widgetStack->setCurrentIndex(0); QMessageBox::information(NULL,"Segmentation","Lesion segmentation is currently not supported for 4D images"); } } } } } // protected void QmitkSegmentationView::OnComboBoxSelectionChanged( const mitk::DataNode* node ) { mitk::DataNode* selectedNode = const_cast(node); if( selectedNode != NULL ) { m_Controls->refImageSelector->show(); m_Controls->lblReferenceImageSelectionWarning->hide(); bool isBinary(false); selectedNode->GetBoolProperty("binary", isBinary); if ( isBinary ) { FireNodeSelected(selectedNode); selectedNode->SetVisibility(true); } else if (node != m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)) { if (m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)) m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)->SetVisibility(false); if (m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)) { m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)->SetVisibility(false); } FireNodeSelected(selectedNode); selectedNode->SetVisibility(true); SetToolManagerSelection(selectedNode, NULL); } } else { m_Controls->refImageSelector->hide(); m_Controls->lblReferenceImageSelectionWarning->show(); } } void QmitkSegmentationView::OnShowMarkerNodes (bool state) { mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetTools().size(); for(unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetToolById(i)); if (manualSegmentationTool) { if(state == true) { manualSegmentationTool->SetShowMarkerNodes( true ); } else { manualSegmentationTool->SetShowMarkerNodes( false ); } } } } void QmitkSegmentationView::On3DInterpolationEnabled (bool state) { mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetTools().size(); for(unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetToolById(i)); if (manualSegmentationTool) { manualSegmentationTool->Enable3DInterpolation( state ); } } } void QmitkSegmentationView::OnSelectionChanged(mitk::DataNode* node) { std::vector nodes; nodes.push_back( node ); this->OnSelectionChanged( nodes ); } void QmitkSegmentationView::OnSurfaceSelectionChanged() { // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull())) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); } void QmitkSegmentationView::OnSelectionChanged(std::vector nodes) { // if the selected node is a contourmarker if ( !nodes.empty() ) { std::string markerName = "Position"; unsigned int numberOfNodes = nodes.size(); std::string nodeName = nodes.at( 0 )->GetName(); if ( ( numberOfNodes == 1 ) && ( nodeName.find( markerName ) == 0) ) { this->OnContourMarkerSelected( nodes.at( 0 ) ); } } // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull())) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); if (!m_Parent || !m_Parent->isVisible()) return; // reaction to BlueBerry selection events // this method will try to figure out if a relevant segmentation and its corresponding original image were selected // a warning is issued if the selection is invalid // appropriate reactions are triggered otherwise mitk::DataNode::Pointer referenceData = FindFirstRegularImage( nodes ); //m_Controls->refImageSelector->GetSelectedNode(); //FindFirstRegularImage( nodes ); mitk::DataNode::Pointer workingData = FindFirstSegmentation( nodes ); if(referenceData.IsNull() && workingData.IsNull()) return; bool invalidSelection( !nodes.empty() && ( nodes.size() > 2 || // maximum 2 selected nodes (nodes.size() == 2 && (workingData.IsNull() || referenceData.IsNull()) ) || // with two nodes, one must be the original image, one the segmentation ( workingData.GetPointer() == referenceData.GetPointer() ) //one node is selected as reference and working image // one item is always ok (might be working or reference or nothing ) ); if (invalidSelection) { // TODO visible warning when two images are selected MITK_ERROR << "WARNING: No image, too many (>2) or two equal images were selected."; workingData = NULL; if( m_Controls->refImageSelector->GetSelectedNode().IsNull() ) referenceData = NULL; } if ( workingData.IsNotNull() && referenceData.IsNull() ) { // find the DataStorage parent of workingData // try to find a "normal image" parent, select this as reference image mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer isNotBinary = mitk::NodePredicateNot::New( isBinary ); mitk::NodePredicateAnd::Pointer isNormalImage = mitk::NodePredicateAnd::New( isImage, isNotBinary ); mitk::DataStorage::SetOfObjects::ConstPointer possibleParents = this->GetDefaultDataStorage()->GetSources( workingData, isNormalImage ); if (possibleParents->size() > 0) { if (possibleParents->size() > 1) { // TODO visible warning for this rare case MITK_ERROR << "Selected binary image has multiple parents. Using arbitrary first one for segmentation."; } referenceData = (*possibleParents)[0]; } NodeTagMapType::iterator searchIter = m_WorkingDataObserverTags.find( workingData ); if ( searchIter == m_WorkingDataObserverTags.end() ) { //MITK_INFO<<"Creating new observer"; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnWorkingNodeVisibilityChanged); m_WorkingDataObserverTags.insert( std::pair( workingData, workingData->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ) ) ); workingData->GetProperty("visible")->Modified(); return; } if(workingData->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1")))) { //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(workingData) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull()) || (!referenceData)) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); SetToolManagerSelection(referenceData, workingData); FireNodeSelected(workingData); } else { SetToolManagerSelection(NULL, NULL); FireNodeSelected(workingData); } } else { //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(referenceData) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull()) || (!referenceData)) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); SetToolManagerSelection(referenceData, workingData); FireNodeSelected(referenceData); } } void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode *node) { //TODO renderWindow anders bestimmen, siehe CheckAlignment QmitkRenderWindow* selectedRenderWindow = 0; QmitkRenderWindow* RenderWindow1 = this->GetActiveStdMultiWidget()->GetRenderWindow1(); QmitkRenderWindow* RenderWindow2 = this->GetActiveStdMultiWidget()->GetRenderWindow2(); QmitkRenderWindow* RenderWindow3 = this->GetActiveStdMultiWidget()->GetRenderWindow3(); QmitkRenderWindow* RenderWindow4 = this->GetActiveStdMultiWidget()->GetRenderWindow4(); bool PlanarFigureInitializedWindow = false; // find initialized renderwindow if (node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow1->GetRenderer())) { selectedRenderWindow = RenderWindow1; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow2->GetRenderer())) { selectedRenderWindow = RenderWindow2; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow3->GetRenderer())) { selectedRenderWindow = RenderWindow3; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow4->GetRenderer())) { selectedRenderWindow = RenderWindow4; } // make node visible if (selectedRenderWindow) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id)); selectedRenderWindow->GetRenderer()->GetDisplayGeometry()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } mitk::DataNode::Pointer QmitkSegmentationView::FindFirstRegularImage( std::vector nodes ) { if (nodes.empty()) return NULL; for(unsigned int i = 0; i < nodes.size(); ++i) { //mitk::DataNode::Pointer node = i.value() bool isImage(false); if (nodes.at(i)->GetData()) { isImage = dynamic_cast(nodes.at(i)->GetData()) != NULL; } // make sure this is not a binary image bool isSegmentation(false); nodes.at(i)->GetBoolProperty("binary", isSegmentation); // return first proper mitk::Image if (isImage && !isSegmentation) return nodes.at(i); } return NULL; } mitk::DataNode::Pointer QmitkSegmentationView::FindFirstSegmentation( std::vector nodes ) { if (nodes.empty()) return NULL; for(unsigned int i = 0; i < nodes.size(); ++i) { bool isImage(false); if (nodes.at(i)->GetData()) { isImage = dynamic_cast(nodes.at(i)->GetData()) != NULL; } bool isSegmentation(false); nodes.at(i)->GetBoolProperty("binary", isSegmentation); // return first proper binary mitk::Image if (isImage && isSegmentation) { return nodes.at(i); } } return NULL; } void QmitkSegmentationView::SetToolManagerSelection(const mitk::DataNode* referenceData, const mitk::DataNode* workingData) { // called as a result of new BlueBerry selections // tells the ToolManager for manual segmentation about new selections // updates GUI information about what the user should select mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); toolManager->SetReferenceData(const_cast(referenceData)); toolManager->SetWorkingData( const_cast(workingData)); // check original image m_Controls->btnNewSegmentation->setEnabled(referenceData != NULL); if (referenceData) { m_Controls->lblReferenceImageSelectionWarning->hide(); } else { m_Controls->lblReferenceImageSelectionWarning->show(); m_Controls->lblWorkingImageSelectionWarning->hide(); m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } //TODO remove statement // check, wheter reference image is aligned like render windows. Otherwise display a visible warning (because 2D tools will probably not work) CheckImageAlignment(); // check segmentation if (referenceData) { if (!workingData) { m_Controls->lblWorkingImageSelectionWarning->show(); if( m_Controls->widgetStack->currentIndex() == 0 ) { m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } } else { m_Controls->lblWorkingImageSelectionWarning->hide(); this->FireNodeSelected(const_cast(workingData)); if( m_Controls->widgetStack->currentIndex() == 0 ) { m_Controls->lblSegmentation->setText( workingData->GetName().c_str() ); m_Controls->lblSegmentation->show(); m_Controls->lblSegImage->show(); } } } } //TODO remove function void QmitkSegmentationView::CheckImageAlignment() { bool wrongAlignment(true); mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull() && m_MultiWidget) { wrongAlignment = !( IsRenderWindowAligned(m_MultiWidget->GetRenderWindow1(), image ) && IsRenderWindowAligned(m_MultiWidget->GetRenderWindow2(), image ) && IsRenderWindowAligned(m_MultiWidget->GetRenderWindow3(), image ) ); } if (wrongAlignment) { m_Controls->lblAlignmentWarning->show(); } } } //TODO remove function bool QmitkSegmentationView::IsRenderWindowAligned(QmitkRenderWindow* renderWindow, mitk::Image* image) { if (!renderWindow) return false; // for all 2D renderwindows of m_MultiWidget check alignment mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast( renderWindow->GetRenderer()->GetCurrentWorldGeometry2D() ); if (displayPlane.IsNull()) return false; int affectedDimension(-1); int affectedSlice(-1); return mitk::SegTool2D::DetermineAffectedImageSlice( image, displayPlane, affectedDimension, affectedSlice ); } //TODO remove function void QmitkSegmentationView::ForceDisplayPreferencesUponAllImages() { if (!m_Parent || !m_Parent->isVisible()) return; // check all images and segmentations in DataStorage: // (items in brackets are implicitly done by previous steps) // 1. // if a reference image is selected, // show the reference image // and hide all other images (orignal and segmentation), // (and hide all segmentations of the other original images) // and show all the reference's segmentations // if no reference image is selected, do do nothing // // 2. // if a segmentation is selected, // show it // (and hide all all its siblings (childs of the same parent, incl, NULL parent)) // if no segmentation is selected, do nothing if (!m_Controls) return; // might happen on initialization (preferences loaded) mitk::DataNode::Pointer referenceData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); mitk::DataNode::Pointer workingData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0); // 1. if (referenceData.IsNotNull()) { // iterate all images mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDefaultDataStorage()->GetSubset( isImage ); //mitk::DataStorage::SetOfObjects::ConstPointer allSegmentationChilds = this->GetDefaultDataStorage()->GetDerivations(referenceData, isImage ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); // set visibility if(!node->IsSelected() || (node->IsSelected() && !node->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))))) node->SetVisibility((node == referenceData) || node->IsSelected() ); } } // 2. //if (workingData.IsNotNull() && !workingData->IsSelected()) //{ // workingData->SetVisibility(true); //} mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::ApplyDisplayOptions(mitk::DataNode* node) { if (!node) return; bool isBinary(false); node->GetPropertyValue("binary", isBinary); if (isBinary) { node->SetProperty( "outline binary", mitk::BoolProperty::New( this->GetPreferences()->GetBool("draw outline", true)) ); node->SetProperty( "outline width", mitk::FloatProperty::New( 2.0 ) ); node->SetProperty( "opacity", mitk::FloatProperty::New( this->GetPreferences()->GetBool("draw outline", true) ? 1.0 : 0.3 ) ); node->SetProperty( "volumerendering", mitk::BoolProperty::New( this->GetPreferences()->GetBool("volume rendering", false) ) ); } } void QmitkSegmentationView::CreateQtPartControl(QWidget* parent) { // setup the basic GUI of this view m_Parent = parent; m_Controls = new Ui::QmitkSegmentationControls; m_Controls->setupUi(parent); m_Controls->lblWorkingImageSelectionWarning->hide(); m_Controls->lblAlignmentWarning->hide(); m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); m_Controls->refImageSelector->SetDataStorage(this->GetDefaultDataStorage()); m_Controls->refImageSelector->SetPredicate(mitk::NodePredicateDataType::New("Image")); if( m_Controls->refImageSelector->GetSelectedNode().IsNotNull() ) m_Controls->lblReferenceImageSelectionWarning->hide(); else m_Controls->refImageSelector->hide(); mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); toolManager->SetDataStorage( *(this->GetDefaultDataStorage()) ); assert ( toolManager ); // all part of open source MITK m_Controls->m_ManualToolSelectionBox->SetGenerateAccelerators(true); m_Controls->m_ManualToolSelectionBox->SetToolGUIArea( m_Controls->m_ManualToolGUIContainer ); m_Controls->m_ManualToolSelectionBox->SetDisplayedToolGroups("Add Subtract Paint Wipe 'Region Growing' Correction Fill Erase"); m_Controls->m_ManualToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingData ); // available only in the 3M application if ( !m_Controls->m_OrganToolSelectionBox->children().count() ) { m_Controls->widgetStack->setItemEnabled( 1, false ); } m_Controls->m_OrganToolSelectionBox->SetToolManager( *toolManager ); m_Controls->m_OrganToolSelectionBox->SetToolGUIArea( m_Controls->m_OrganToolGUIContainer ); m_Controls->m_OrganToolSelectionBox->SetDisplayedToolGroups("'Hippocampus left' 'Hippocampus right' 'Lung left' 'Lung right' 'Liver' 'Heart LV' 'Endocard LV' 'Epicard LV' 'Prostate'"); m_Controls->m_OrganToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceData ); // available only in the 3M application if ( !m_Controls->m_LesionToolSelectionBox->children().count() ) { m_Controls->widgetStack->setItemEnabled( 2, false ); } m_Controls->m_LesionToolSelectionBox->SetToolManager( *toolManager ); m_Controls->m_LesionToolSelectionBox->SetToolGUIArea( m_Controls->m_LesionToolGUIContainer ); m_Controls->m_LesionToolSelectionBox->SetDisplayedToolGroups("'Lymph Node'"); m_Controls->m_LesionToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceData ); toolManager->NewNodesGenerated += mitk::MessageDelegate( this, &QmitkSegmentationView::NewNodesGenerated ); // update the list of segmentations toolManager->NewNodeObjectsGenerated += mitk::MessageDelegate1( this, &QmitkSegmentationView::NewNodeObjectsGenerated ); // update the list of segmentations // create signal/slot connections connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); connect( m_Controls->btnNewSegmentation, SIGNAL(clicked()), this, SLOT(CreateNewSegmentation()) ); connect( m_Controls->CreateSegmentationFromSurface, SIGNAL(clicked()), this, SLOT(CreateSegmentationFromSurface()) ); connect( m_Controls->m_ManualToolSelectionBox, SIGNAL(ToolSelected(int)), this, SLOT(ManualToolSelected(int)) ); connect( m_Controls->widgetStack, SIGNAL(currentChanged(int)), this, SLOT(ToolboxStackPageChanged(int)) ); connect(m_Controls->MaskSurfaces, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSurfaceSelectionChanged( ) ) ); connect(m_Controls->MaskSurfaces, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSurfaceSelectionChanged( ) ) ); connect(m_Controls->m_SlicesInterpolator, SIGNAL(SignalShowMarkerNodes(bool)), this, SLOT(OnShowMarkerNodes(bool))); connect(m_Controls->m_SlicesInterpolator, SIGNAL(Signal3DInterpolationEnabled(bool)), this, SLOT(On3DInterpolationEnabled(bool))); m_Controls->MaskSurfaces->SetDataStorage(this->GetDefaultDataStorage()); m_Controls->MaskSurfaces->SetPredicate(mitk::NodePredicateDataType::New("Surface")); //// create helper class to provide context menus for segmentations in data manager // m_PostProcessing = new QmitkSegmentationPostProcessing(this->GetDefaultDataStorage(), this, m_Parent); } //void QmitkSegmentationView::OnPlaneModeChanged(int i) //{ // //if plane mode changes, disable all tools // if (m_MultiWidget) // { // mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); // // if (toolManager) // { // if (toolManager->GetActiveToolID() >= 0) // { // toolManager->ActivateTool(-1); // } // else // { // m_MultiWidget->EnableNavigationControllerEventListening(); // } // } // } //} // ATTENTION some methods for handling the known list of (organ names, colors) are defined in QmitkSegmentationOrganNamesHandling.cpp diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkThresholdAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkThresholdAction.cpp index 2cb05d7528..17cefd99c8 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkThresholdAction.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkThresholdAction.cpp @@ -1,99 +1,100 @@ #include "QmitkThresholdAction.h" // MITK #include #include #include // Qt #include #include +#include using namespace berry; using namespace mitk; using namespace std; QmitkThresholdAction::QmitkThresholdAction() : m_ThresholdingDialog(NULL) { } QmitkThresholdAction::~QmitkThresholdAction() { } void QmitkThresholdAction::Run(const QList &selectedNodes) { m_ThresholdingToolManager = ToolManager::New(m_DataStorage); m_ThresholdingToolManager->RegisterClient(); m_ThresholdingToolManager->ActiveToolChanged += mitk::MessageDelegate(this, &QmitkThresholdAction::OnThresholdingToolManagerToolModified); - m_ThresholdingDialog = new QDialog; + m_ThresholdingDialog = new QDialog(QApplication::activeWindow()); connect(m_ThresholdingDialog, SIGNAL(finished(int)), this, SLOT(ThresholdingDone(int))); QVBoxLayout *layout = new QVBoxLayout; layout->setContentsMargins(0, 0, 0, 0); Tool *binaryThresholdTool = m_ThresholdingToolManager->GetToolById(m_ThresholdingToolManager->GetToolIdByToolType()); if (binaryThresholdTool != NULL) { QmitkToolGUI *gui = dynamic_cast(binaryThresholdTool->GetGUI("Qmitk", "GUI").GetPointer()); if (gui != NULL) { gui->SetTool(binaryThresholdTool); gui->setParent(m_ThresholdingDialog); layout->addWidget(gui); m_ThresholdingDialog->setLayout(layout); m_ThresholdingDialog->setFixedSize(300, 80); m_ThresholdingDialog->open(); } m_ThresholdingToolManager->SetReferenceData(selectedNodes[0]); m_ThresholdingToolManager->ActivateTool(m_ThresholdingToolManager->GetToolIdByToolType()); } } void QmitkThresholdAction::ThresholdingDone(int result) { if (result == QDialog::Rejected) m_ThresholdingToolManager->ActivateTool(-1); m_ThresholdingDialog->deleteLater(); m_ThresholdingDialog = NULL; m_ThresholdingToolManager->SetReferenceData(NULL); m_ThresholdingToolManager->SetWorkingData(NULL); RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkThresholdAction::OnThresholdingToolManagerToolModified() { if (m_ThresholdingToolManager.IsNotNull()) if (m_ThresholdingToolManager->GetActiveToolID() < 0) if (m_ThresholdingDialog != NULL) m_ThresholdingDialog->accept(); } void QmitkThresholdAction::SetDataStorage(DataStorage *dataStorage) { m_DataStorage = dataStorage; } void QmitkThresholdAction::SetSmoothed(bool) { } void QmitkThresholdAction::SetDecimated(bool) { } void QmitkThresholdAction::SetFunctionality(QtViewPart* /*functionality*/) { } diff --git a/SuperBuild.cmake b/SuperBuild.cmake index 9273d6c61b..333dc51aff 100644 --- a/SuperBuild.cmake +++ b/SuperBuild.cmake @@ -1,309 +1,309 @@ #----------------------------------------------------------------------------- # Convenient macro allowing to download a file #----------------------------------------------------------------------------- macro(downloadFile url dest) file(DOWNLOAD ${url} ${dest} STATUS status) list(GET status 0 error_code) list(GET status 1 error_msg) if(error_code) message(FATAL_ERROR "error: Failed to download ${url} - ${error_msg}") endif() endmacro() #----------------------------------------------------------------------------- # MITK Prerequisites #----------------------------------------------------------------------------- if(UNIX AND NOT APPLE) #----------------------------- libxt-dev -------------------- include(${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake) set(CMAKE_REQUIRED_INCLUDES "/usr/include/X11/") CHECK_INCLUDE_FILE("StringDefs.h" STRING_DEFS_H) if(NOT STRING_DEFS_H) message(FATAL_ERROR "error: could not find StringDefs.h provided by libxt-dev") endif() set(CMAKE_REQUIRED_INCLUDES "/usr/include/") CHECK_INCLUDE_FILE("tiff.h" TIFF_H) if(NOT TIFF_H) message(FATAL_ERROR "error: could not find tiff.h - libtiff4-dev needs to be installed") endif() CHECK_INCLUDE_FILE("tcpd.h" LIB_WRAP) if(NOT LIB_WRAP) - message(FATAL_ERROR "error: could not find tcpd.h - libwrap0-dev needs to be installed") + message(STATUS "Could not find tcpd.h - if this is missing in the build libwrap0-dev needs to be installed") endif() endif() #----------------------------------------------------------------------------- # ExternalProjects #----------------------------------------------------------------------------- set(external_projects VTK GDCM CableSwig ITK Boost DCMTK CTK OpenCV MITKData ) set(MITK_USE_CableSwig ${MITK_USE_Python}) set(MITK_USE_GDCM 1) set(MITK_USE_ITK 1) set(MITK_USE_VTK 1) foreach(proj VTK GDCM CableSwig ITK DCMTK CTK OpenCV) if(MITK_USE_${proj}) set(EXTERNAL_${proj}_DIR "${${proj}_DIR}" CACHE PATH "Path to ${proj} build directory") mark_as_advanced(EXTERNAL_${proj}_DIR) if(EXTERNAL_${proj}_DIR) set(${proj}_DIR ${EXTERNAL_${proj}_DIR}) endif() endif() endforeach() if(MITK_USE_Boost) set(EXTERNAL_BOOST_ROOT "${BOOST_ROOT}" CACHE PATH "Path to Boost directory") mark_as_advanced(EXTERNAL_BOOST_ROOT) if(EXTERNAL_BOOST_ROOT) set(BOOST_ROOT ${EXTERNAL_BOOST_ROOT}) endif() endif() if(BUILD_TESTING) set(EXTERNAL_MITK_DATA_DIR "${MITK_DATA_DIR}" CACHE PATH "Path to the MITK data directory") mark_as_advanced(EXTERNAL_MITK_DATA_DIR) if(EXTERNAL_MITK_DATA_DIR) set(MITK_DATA_DIR ${EXTERNAL_MITK_DATA_DIR}) endif() endif() # Look for git early on, if needed if((BUILD_TESTING AND NOT EXTERNAL_MITK_DATA_DIR) OR (MITK_USE_CTK AND NOT EXTERNAL_CTK_DIR)) find_package(Git REQUIRED) endif() #----------------------------------------------------------------------------- # External project settings #----------------------------------------------------------------------------- include(ExternalProject) set(ep_base "${CMAKE_BINARY_DIR}/CMakeExternals") set_property(DIRECTORY PROPERTY EP_BASE ${ep_base}) set(ep_install_dir ${ep_base}/Install) #set(ep_build_dir ${ep_base}/Build) set(ep_source_dir ${ep_base}/Source) #set(ep_parallelism_level) set(ep_build_shared_libs ON) set(ep_build_testing OFF) if(NOT MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL) set(MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL http://mitk.org/download/thirdparty) endif() # Compute -G arg for configuring external projects with the same CMake generator: if(CMAKE_EXTRA_GENERATOR) set(gen "${CMAKE_EXTRA_GENERATOR} - ${CMAKE_GENERATOR}") else() set(gen "${CMAKE_GENERATOR}") endif() # Use this value where semi-colons are needed in ep_add args: set(sep "^^") ## if(MSVC90 OR MSVC10) set(ep_common_C_FLAGS "${CMAKE_C_FLAGS} /bigobj /MP") set(ep_common_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj /MP") else() set(ep_common_C_FLAGS "${CMAKE_C_FLAGS} -DLINUX_EXTRA") set(ep_common_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DLINUX_EXTRA") endif() set(ep_common_args -DBUILD_TESTING:BOOL=${ep_build_testing} -DCMAKE_INSTALL_PREFIX:PATH=${ep_install_dir} -DBUILD_SHARED_LIBS:BOOL=ON -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} -DCMAKE_C_FLAGS:STRING=${ep_common_C_FLAGS} -DCMAKE_CXX_FLAGS:STRING=${ep_common_CXX_FLAGS} #debug flags -DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG} -DCMAKE_C_FLAGS_DEBUG:STRING=${CMAKE_C_FLAGS_DEBUG} #release flags -DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE} -DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELEASE} #relwithdebinfo -DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DCMAKE_C_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_C_FLAGS_RELWITHDEBINFO} ) # Include external projects foreach(p ${external_projects}) include(CMakeExternals/${p}.cmake) endforeach() #----------------------------------------------------------------------------- # Set superbuild boolean args #----------------------------------------------------------------------------- set(mitk_cmake_boolean_args BUILD_SHARED_LIBS WITH_COVERAGE BUILD_TESTING MITK_USE_QT MITK_BUILD_ALL_PLUGINS MITK_BUILD_ALL_APPS MITK_BUILD_TUTORIAL MITK_USE_Boost MITK_USE_SYSTEM_Boost MITK_USE_BLUEBERRY MITK_USE_CTK MITK_USE_DCMTK MITK_USE_OpenCV MITK_USE_Python ) #----------------------------------------------------------------------------- # Create the final variable containing superbuild boolean args #----------------------------------------------------------------------------- set(mitk_superbuild_boolean_args) foreach(mitk_cmake_arg ${mitk_cmake_boolean_args}) list(APPEND mitk_superbuild_boolean_args -D${mitk_cmake_arg}:BOOL=${${mitk_cmake_arg}}) endforeach() if(MITK_BUILD_ALL_PLUGINS) list(APPEND mitk_superbuild_boolean_args -DBLUEBERRY_BUILD_ALL_PLUGINS:BOOL=ON) endif() #----------------------------------------------------------------------------- # MITK Utilities #----------------------------------------------------------------------------- set(proj MITK-Utilities) ExternalProject_Add(${proj} DOWNLOAD_COMMAND "" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" DEPENDS # Mandatory dependencies ${VTK_DEPENDS} ${ITK_DEPENDS} # Optionnal dependencies ${Boost_DEPENDS} ${CTK_DEPENDS} ${DCMTK_DEPENDS} ${OpenCV_DEPENDS} ${MITK-Data_DEPENDS} ) #----------------------------------------------------------------------------- # MITK Configure #----------------------------------------------------------------------------- if(MITK_INITIAL_CACHE_FILE) set(mitk_initial_cache_arg -C "${MITK_INITIAL_CACHE_FILE}") endif() set(mitk_optional_cache_args ) foreach(type RUNTIME ARCHIVE LIBRARY) if(DEFINED CTK_PLUGIN_${type}_OUTPUT_DIRECTORY) list(APPEND mitk_optional_cache_args -DCTK_PLUGIN_${type}_OUTPUT_DIRECTORY:PATH=${CTK_PLUGIN_${type}_OUTPUT_DIRECTORY}) endif() endforeach() set(proj MITK-Configure) ExternalProject_Add(${proj} LIST_SEPARATOR ^^ DOWNLOAD_COMMAND "" CMAKE_GENERATOR ${gen} CMAKE_CACHE_ARGS ${ep_common_args} ${mitk_superbuild_boolean_args} ${mitk_optional_cache_args} -DMITK_USE_SUPERBUILD:BOOL=OFF -DMITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH=${MITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY} -DMITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${MITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY} -DMITK_CMAKE_ARCHIVE_OUTPUT_DIRECTORY:PATH=${MITK_CMAKE_ARCHIVE_OUTPUT_DIRECTORY} -DCTEST_USE_LAUNCHERS:BOOL=${CTEST_USE_LAUNCHERS} -DMITK_CTEST_SCRIPT_MODE:STRING=${MITK_CTEST_SCRIPT_MODE} -DMITK_SUPERBUILD_BINARY_DIR:PATH=${MITK_BINARY_DIR} -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE} -DMITK_KWSTYLE_EXECUTABLE:FILEPATH=${MITK_KWSTYLE_EXECUTABLE} -DMITK_MODULES_TO_BUILD:INTERNAL=${MITK_MODULES_TO_BUILD} -DCTK_DIR:PATH=${CTK_DIR} -DDCMTK_DIR:PATH=${DCMTK_DIR} -DVTK_DIR:PATH=${VTK_DIR} # FindVTK expects VTK_DIR -DITK_DIR:PATH=${ITK_DIR} # FindITK expects ITK_DIR -DOpenCV_DIR:PATH=${OpenCV_DIR} -DGDCM_DIR:PATH=${GDCM_DIR} -DBOOST_ROOT:PATH=${BOOST_ROOT} -DMITK_USE_Boost_LIBRARIES:STRING=${MITK_USE_Boost_LIBRARIES} -DMITK_DATA_DIR:PATH=${MITK_DATA_DIR} -DMITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES} -DMITK_ACCESSBYITK_FLOATING_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES} -DMITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES} -DMITK_ACCESSBYITK_DIMENSIONS:STRING=${MITK_ACCESSBYITK_DIMENSIONS} CMAKE_ARGS ${mitk_initial_cache_arg} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} BINARY_DIR ${CMAKE_BINARY_DIR}/MITK-build BUILD_COMMAND "" INSTALL_COMMAND "" DEPENDS MITK-Utilities ) #----------------------------------------------------------------------------- # MITK #----------------------------------------------------------------------------- if(CMAKE_GENERATOR MATCHES ".*Makefiles.*") set(mitk_build_cmd "$(MAKE)") else() set(mitk_build_cmd ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/MITK-build --config ${CMAKE_CFG_INTDIR}) endif() if(NOT DEFINED SUPERBUILD_EXCLUDE_MITKBUILD_TARGET OR NOT SUPERBUILD_EXCLUDE_MITKBUILD_TARGET) set(MITKBUILD_TARGET_ALL_OPTION "ALL") else() set(MITKBUILD_TARGET_ALL_OPTION "") endif() add_custom_target(MITK-build ${MITKBUILD_TARGET_ALL_OPTION} COMMAND ${mitk_build_cmd} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/MITK-build DEPENDS MITK-Configure ) #----------------------------------------------------------------------------- # Custom target allowing to drive the build of the MITK project itself #----------------------------------------------------------------------------- add_custom_target(MITK COMMAND ${mitk_build_cmd} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/MITK-build ) diff --git a/Utilities/Poco/Foundation/include/Poco/Hash.h b/Utilities/Poco/Foundation/include/Poco/Hash.h index 99b1aa9d90..3f4b98881b 100755 --- a/Utilities/Poco/Foundation/include/Poco/Hash.h +++ b/Utilities/Poco/Foundation/include/Poco/Hash.h @@ -1,127 +1,127 @@ // // Hash.h // // $Id$ // // Library: Foundation // Package: Hashing // Module: Hash // // Definition of the Hash class. // // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Hash_INCLUDED #define Foundation_Hash_INCLUDED #include "Poco/Foundation.h" #include namespace Poco { +std::size_t Foundation_API hash(Int8 n); +std::size_t Foundation_API hash(UInt8 n); +std::size_t Foundation_API hash(Int16 n); +std::size_t Foundation_API hash(UInt16 n); +std::size_t Foundation_API hash(Int32 n); +std::size_t Foundation_API hash(UInt32 n); +std::size_t Foundation_API hash(Int64 n); +std::size_t Foundation_API hash(UInt64 n); +std::size_t Foundation_API hash(const std::string& str); + template struct Hash /// A generic hash function. { std::size_t operator () (T value) const /// Returns the hash for the given value. { return hash(value); } }; -std::size_t Foundation_API hash(Int8 n); -std::size_t Foundation_API hash(UInt8 n); -std::size_t Foundation_API hash(Int16 n); -std::size_t Foundation_API hash(UInt16 n); -std::size_t Foundation_API hash(Int32 n); -std::size_t Foundation_API hash(UInt32 n); -std::size_t Foundation_API hash(Int64 n); -std::size_t Foundation_API hash(UInt64 n); -std::size_t Foundation_API hash(const std::string& str); - // // inlines // inline std::size_t hash(Int8 n) { return static_cast(n)*2654435761U; } inline std::size_t hash(UInt8 n) { return static_cast(n)*2654435761U; } inline std::size_t hash(Int16 n) { return static_cast(n)*2654435761U; } inline std::size_t hash(UInt16 n) { return static_cast(n)*2654435761U; } inline std::size_t hash(Int32 n) { return static_cast(n)*2654435761U; } inline std::size_t hash(UInt32 n) { return static_cast(n)*2654435761U; } inline std::size_t hash(Int64 n) { return static_cast(n)*2654435761U; } inline std::size_t hash(UInt64 n) { return static_cast(n)*2654435761U; } } // namespace Poco #endif // Foundation_Hash_INCLUDED