diff --git a/Core/Code/Algorithms/mitkTimeHelper.h b/Core/Code/Algorithms/mitkTimeHelper.h index ad644043eb..b14edcdab3 100644 --- a/Core/Code/Algorithms/mitkTimeHelper.h +++ b/Core/Code/Algorithms/mitkTimeHelper.h @@ -1,78 +1,78 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKTIMEHELPER_H_HEADER_INCLUDED_C1C2FCD2 #define MITKTIMEHELPER_H_HEADER_INCLUDED_C1C2FCD2 #include namespace mitk { //## @brief convert the start- and end-index-time of output-region in //## start- and end-index-time of input-region via millisecond-time template void GenerateTimeInInputRegion(const mitk::TimeGeometry *outputTimeGeometry, const TOutputRegion& outputRegion, const mitk::TimeGeometry *inputTimeGeometry, TInputRegion& inputRegion) { assert(outputTimeGeometry!=NULL); assert(inputTimeGeometry!=NULL); // convert the start-index-time of output in start-index-time of input via millisecond-time mitk::TimePointType timeInMS = outputTimeGeometry->TimeStepToTimePoint(outputRegion.GetIndex(3)); mitk::TimeStepType timestep = inputTimeGeometry->TimePointToTimeStep( timeInMS ); - if( ( timeInMS > ScalarTypeNumericTraits::NonpositiveMin() ) && ( inputTimeGeometry->IsValidTimeStep( timestep ) ) ) + if( ( timeInMS > itk::NumericTraits::NonpositiveMin() ) && ( inputTimeGeometry->IsValidTimeStep( timestep ) ) ) inputRegion.SetIndex( 3, timestep ); else inputRegion.SetIndex( 3, 0 ); // convert the end-index-time of output in end-index-time of input via millisecond-time timeInMS = outputTimeGeometry->TimeStepToTimePoint(outputRegion.GetIndex(3)+outputRegion.GetSize(3)-1); timestep = inputTimeGeometry->TimePointToTimeStep( timeInMS ); - if( ( timeInMS > ScalarTypeNumericTraits::NonpositiveMin() ) && ( outputTimeGeometry->IsValidTimeStep( timestep ) ) ) + if( ( timeInMS > itk::NumericTraits::NonpositiveMin() ) && ( outputTimeGeometry->IsValidTimeStep( timestep ) ) ) inputRegion.SetSize( 3, timestep - inputRegion.GetIndex(3) + 1 ); else inputRegion.SetSize( 3, 1 ); } //##Documentation //## @brief convert the start- and end-index-time of output in //## start- and end-index-time of input1 and input2 via millisecond-time template void GenerateTimeInInputRegion(const TOutputData* output, TInputData* input) { assert(output!=NULL); assert(input!=NULL); const typename TOutputData::RegionType& outputRegion = output->GetRequestedRegion(); typename TInputData::RegionType inputRegion; if(outputRegion.GetSize(3)<1) { typename TInputData::RegionType::SizeType inputsize; inputsize.Fill(0); inputRegion.SetSize(inputsize); input->SetRequestedRegion( &inputRegion ); } // convert the start-index-time of output in start-index-time of input via millisecond-time inputRegion = input->GetRequestedRegion(); GenerateTimeInInputRegion(output->GetTimeGeometry(), outputRegion, input->GetTimeGeometry(), inputRegion); input->SetRequestedRegion( &inputRegion ); } } // end namespace mitk #endif // MITKTIMEHELPER_H_HEADER_INCLUDED_C1C2FCD2 diff --git a/Core/Code/Controllers/mitkRenderingManager.cpp b/Core/Code/Controllers/mitkRenderingManager.cpp index e40878169f..5452319bb2 100644 --- a/Core/Code/Controllers/mitkRenderingManager.cpp +++ b/Core/Code/Controllers/mitkRenderingManager.cpp @@ -1,926 +1,926 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkRenderingManager.h" #include "mitkRenderingManagerFactory.h" #include "mitkBaseRenderer.h" #include "mitkGlobalInteraction.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateProperty.h" #include "mitkProportionalTimeGeometry.h" #include #include -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include #include #include #include namespace mitk { RenderingManager::Pointer RenderingManager::s_Instance = 0; RenderingManagerFactory *RenderingManager::s_RenderingManagerFactory = 0; RenderingManager ::RenderingManager() : m_UpdatePending( false ), m_MaxLOD( 1 ), m_LODIncreaseBlocked( false ), m_LODAbortMechanismEnabled( false ), m_ClippingPlaneEnabled( false ), m_TimeNavigationController( SliceNavigationController::New("dummy") ), m_DataStorage( NULL ), m_ConstrainedPaddingZooming ( true ) { m_ShadingEnabled.assign( 3, false ); m_ShadingValues.assign( 4, 0.0 ); m_GlobalInteraction = mitk::GlobalInteraction::GetInstance(); InitializePropertyList(); } RenderingManager ::~RenderingManager() { // Decrease reference counts of all registered vtkRenderWindows for // proper destruction RenderWindowVector::iterator it; for ( it = m_AllRenderWindows.begin(); it != m_AllRenderWindows.end(); ++it ) { (*it)->UnRegister( NULL ); RenderWindowCallbacksList::iterator callbacks_it = this->m_RenderWindowCallbacksList.find(*it); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { (*it)->RemoveObserver(callbacks_it->second.commands[0u]); (*it)->RemoveObserver(callbacks_it->second.commands[1u]); (*it)->RemoveObserver(callbacks_it->second.commands[2u]); } } } void RenderingManager ::SetFactory( RenderingManagerFactory *factory ) { s_RenderingManagerFactory = factory; } const RenderingManagerFactory * RenderingManager ::GetFactory() { return s_RenderingManagerFactory; } bool RenderingManager ::HasFactory() { if ( RenderingManager::s_RenderingManagerFactory ) { return true; } else { return false; } } RenderingManager::Pointer RenderingManager ::New() { const RenderingManagerFactory* factory = GetFactory(); if(factory == NULL) return NULL; return factory->CreateRenderingManager(); } RenderingManager * RenderingManager ::GetInstance() { if ( !RenderingManager::s_Instance ) { if ( s_RenderingManagerFactory ) { s_Instance = s_RenderingManagerFactory->CreateRenderingManager(); } } return s_Instance; } bool RenderingManager ::IsInstantiated() { if ( RenderingManager::s_Instance ) return true; else return false; } void RenderingManager ::AddRenderWindow( vtkRenderWindow *renderWindow ) { if ( renderWindow && (m_RenderWindowList.find( renderWindow ) == m_RenderWindowList.end()) ) { m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_AllRenderWindows.push_back( renderWindow ); if ( m_DataStorage.IsNotNull() ) mitk::BaseRenderer::GetInstance( renderWindow )->SetDataStorage( m_DataStorage.GetPointer() ); // Register vtkRenderWindow instance renderWindow->Register( NULL ); // Add callbacks for rendering abort mechanism //BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow ); vtkCallbackCommand *startCallbackCommand = vtkCallbackCommand::New(); startCallbackCommand->SetCallback( RenderingManager::RenderingStartCallback ); renderWindow->AddObserver( vtkCommand::StartEvent, startCallbackCommand ); vtkCallbackCommand *progressCallbackCommand = vtkCallbackCommand::New(); progressCallbackCommand->SetCallback( RenderingManager::RenderingProgressCallback ); renderWindow->AddObserver( vtkCommand::AbortCheckEvent, progressCallbackCommand ); vtkCallbackCommand *endCallbackCommand = vtkCallbackCommand::New(); endCallbackCommand->SetCallback( RenderingManager::RenderingEndCallback ); renderWindow->AddObserver( vtkCommand::EndEvent, endCallbackCommand ); RenderWindowCallbacks callbacks; callbacks.commands[0u] = startCallbackCommand; callbacks.commands[1u] = progressCallbackCommand; callbacks.commands[2u] = endCallbackCommand; this->m_RenderWindowCallbacksList[renderWindow] = callbacks; //Delete vtk variables correctly startCallbackCommand->Delete(); progressCallbackCommand->Delete(); endCallbackCommand->Delete(); } } void RenderingManager ::RemoveRenderWindow( vtkRenderWindow *renderWindow ) { if (m_RenderWindowList.erase( renderWindow )) { RenderWindowCallbacksList::iterator callbacks_it = this->m_RenderWindowCallbacksList.find(renderWindow); if(callbacks_it != this->m_RenderWindowCallbacksList.end()) { renderWindow->RemoveObserver(callbacks_it->second.commands[0u]); renderWindow->RemoveObserver(callbacks_it->second.commands[1u]); renderWindow->RemoveObserver(callbacks_it->second.commands[2u]); this->m_RenderWindowCallbacksList.erase(callbacks_it); } RenderWindowVector::iterator rw_it = std::find( m_AllRenderWindows.begin(), m_AllRenderWindows.end(), renderWindow ); if(rw_it != m_AllRenderWindows.end()) { // Decrease reference count for proper destruction (*rw_it)->UnRegister(NULL); m_AllRenderWindows.erase( rw_it ); } } } const RenderingManager::RenderWindowVector& RenderingManager ::GetAllRegisteredRenderWindows() { return m_AllRenderWindows; } void RenderingManager ::RequestUpdate( vtkRenderWindow *renderWindow ) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find( renderWindow ) == m_RenderWindowList.end()) { return; } m_RenderWindowList[renderWindow] = RENDERING_REQUESTED; if ( !m_UpdatePending ) { m_UpdatePending = true; this->GenerateRenderingRequestEvent(); } } void RenderingManager ::ForceImmediateUpdate( vtkRenderWindow *renderWindow ) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find( renderWindow ) == m_RenderWindowList.end()) { return; } // Erase potentially pending requests for this window m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_UpdatePending = false; // Immediately repaint this window (implementation platform specific) // If the size is 0 it crahses int *size = renderWindow->GetSize(); if ( 0 != size[0] && 0 != size[1] ) { //prepare the camera etc. before rendering //Note: this is a very important step which should be called before the VTK render! //If you modify the camera anywhere else or after the render call, the scene cannot be seen. mitk::VtkPropRenderer *vPR = dynamic_cast(mitk::BaseRenderer::GetInstance( renderWindow )); if(vPR) vPR->PrepareRender(); // Execute rendering renderWindow->Render(); } } void RenderingManager ::RequestUpdateAll( RequestType type ) { RenderWindowList::iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ( (type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2)) ) { this->RequestUpdate( it->first ); } } } void RenderingManager ::ForceImmediateUpdateAll( RequestType type ) { RenderWindowList::iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ( (type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2)) ) { // Immediately repaint this window (implementation platform specific) // If the size is 0, it crashes this->ForceImmediateUpdate(it->first); } } } void RenderingManager::InitializeViewsByBoundingObjects( const DataStorage *ds) { if (!ds) return; // get all nodes that have not set "includeInBoundingBox" to false mitk::NodePredicateNot::Pointer pred = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("includeInBoundingBox" , mitk::BoolProperty::New(false))); mitk::DataStorage::SetOfObjects::ConstPointer rs = ds->GetSubset(pred); // calculate bounding geometry of these nodes mitk::TimeGeometry::Pointer bounds = ds->ComputeBoundingGeometry3D(rs, "visible"); // initialize the views to the bounding geometry this->InitializeViews(bounds); } //TODO_GOETZ // Remove old function, so only this one is working. bool RenderingManager ::InitializeViews( const BaseGeometry * dataGeometry, RequestType type, bool preserveRoughOrientationInWorldSpace ) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(dataGeometry->Clone().GetPointer()), 1); return InitializeViews(propTimeGeometry,type, preserveRoughOrientationInWorldSpace); } bool RenderingManager ::InitializeViews( const TimeGeometry * dataGeometry, RequestType type, bool /*preserveRoughOrientationInWorldSpace*/ ) { MITK_DEBUG << "initializing views"; bool boundingBoxInitialized = false; TimeGeometry::ConstPointer timeGeometry = dataGeometry; TimeGeometry::Pointer modifiedGeometry = NULL; if (dataGeometry!=NULL) { modifiedGeometry = dataGeometry->Clone(); } // //TODO_GOETZ previously this code section has been disabled by // a later asignment to geometry (e.g. timeGeometry) // This has been fixed during Geometry-1-Plattform Project // Propably this code is not working anymore, test!! /* if (dataGeometry && preserveRoughOrientationInWorldSpace) { // clone the input geometry assert(modifiedGeometry.IsNotNull()); // construct an affine transform from it Geometry3D::TransformType::Pointer transform = Geometry3D::TransformType::New(); assert( modifiedGeometry->GetGeometryForTimeStep(0)->GetIndexToWorldTransform() ); transform->SetMatrix( modifiedGeometry->GetGeometryForTimeStep(0)->GetIndexToWorldTransform()->GetMatrix() ); transform->SetOffset( modifiedGeometry->GetGeometryForTimeStep(0)->GetIndexToWorldTransform()->GetOffset() ); // get transform matrix Geometry3D::TransformType::MatrixType::InternalMatrixType& oldMatrix = const_cast< Geometry3D::TransformType::MatrixType::InternalMatrixType& > ( transform->GetMatrix().GetVnlMatrix() ); Geometry3D::TransformType::MatrixType::InternalMatrixType newMatrix(oldMatrix); // get offset and bound Vector3D offset = modifiedGeometry->GetIndexToWorldTransform()->GetOffset(); Geometry3D::BoundsArrayType oldBounds = modifiedGeometry->GetBounds(); Geometry3D::BoundsArrayType newBounds = modifiedGeometry->GetBounds(); // get rid of rotation other than pi/2 degree for ( unsigned int i = 0; i < 3; ++i ) { // i-th column of the direction matrix Vector3D currentVector; currentVector[0] = oldMatrix(0,i); currentVector[1] = oldMatrix(1,i); currentVector[2] = oldMatrix(2,i); // matchingRow will store the row that holds the biggest // value in the column unsigned int matchingRow = 0; // maximum value in the column ScalarType max = std::numeric_limits::min(); // sign of the maximum value (-1 or 1) int sign = 1; // iterate through the column vector for (unsigned int dim = 0; dim < 3; ++dim) { if ( fabs(currentVector[dim]) > max ) { matchingRow = dim; max = fabs(currentVector[dim]); if(currentVector[dim]<0) sign = -1; else sign = 1; } } // in case we found a negative maximum, // we negate the column and adjust the offset // (in order to run through the dimension in the opposite direction) if(sign == -1) { currentVector *= sign; offset += modifiedGeometry->GetAxisVector(i); } // matchingRow is now used as column index to place currentVector // correctly in the new matrix vnl_vector newMatrixColumn(3); newMatrixColumn[0] = currentVector[0]; newMatrixColumn[1] = currentVector[1]; newMatrixColumn[2] = currentVector[2]; newMatrix.set_column( matchingRow, newMatrixColumn ); // if a column is moved, we also have to adjust the bounding // box accordingly, this is done here newBounds[2*matchingRow ] = oldBounds[2*i ]; newBounds[2*matchingRow+1] = oldBounds[2*i+1]; } // set the newly calculated bounds array modifiedGeometry->SetBounds(newBounds); // set new offset and direction matrix Geometry3D::TransformType::MatrixType newMatrixITK( newMatrix ); transform->SetMatrix( newMatrixITK ); transform->SetOffset( offset ); modifiedGeometry->SetIndexToWorldTransform( transform ); geometry = modifiedGeometry; }*/ int warningLevel = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); if ( (timeGeometry.IsNotNull() ) && (const_cast< mitk::BoundingBox * >( timeGeometry->GetBoundingBoxInWorld())->GetDiagonalLength2() > mitk::eps) ) { boundingBoxInitialized = true; } if (timeGeometry.IsNotNull() ) {// make sure bounding box has an extent bigger than zero in any direction // clone the input geometry //Old Geometry3D::Pointer modifiedGeometry = dynamic_cast( dataGeometry->Clone().GetPointer() ); assert(modifiedGeometry.IsNotNull()); for (TimeStepType step = 0; step < modifiedGeometry->CountTimeSteps(); ++step) { BaseGeometry::BoundsArrayType newBounds = modifiedGeometry->GetGeometryForTimeStep(step)->GetBounds(); for( unsigned int dimension = 0; ( 2 * dimension ) < newBounds.Size() ; dimension++ ) { //check for equality but for an epsilon if( Equal( newBounds[ 2 * dimension ], newBounds[ 2 * dimension + 1 ] ) ) { newBounds[ 2 * dimension + 1 ] += 1; } } modifiedGeometry->GetGeometryForTimeStep(step)->SetBounds(newBounds); } } timeGeometry = modifiedGeometry; RenderWindowList::iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance( it->first ); baseRenderer->GetDisplayGeometry()->SetConstrainZoomingAndPanning(m_ConstrainedPaddingZooming); int id = baseRenderer->GetMapperID(); if ( ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) ) { this->InternalViewInitialization( baseRenderer, timeGeometry, boundingBoxInitialized, id ); } } if ( boundingBoxInitialized ) { m_TimeNavigationController->SetInputWorldTimeGeometry( timeGeometry ); } m_TimeNavigationController->Update(); this->RequestUpdateAll( type ); vtkObject::SetGlobalWarningDisplay( warningLevel ); // Inform listeners that views have been initialized this->InvokeEvent( mitk::RenderingManagerViewsInitializedEvent() ); return boundingBoxInitialized; } bool RenderingManager ::InitializeViews( RequestType type ) { RenderWindowList::iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance( it->first ); int id = baseRenderer->GetMapperID(); if ( (type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2)) ) { mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); // Update the SNC nc->Update(); } } this->RequestUpdateAll( type ); return true; } bool RenderingManager::InitializeView( vtkRenderWindow * renderWindow, const BaseGeometry * geometry, bool initializeGlobalTimeSNC ) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(geometry->Clone().GetPointer()), 1); return InitializeView(renderWindow, propTimeGeometry, initializeGlobalTimeSNC ); } bool RenderingManager::InitializeView( vtkRenderWindow * renderWindow, const TimeGeometry * geometry, bool initializeGlobalTimeSNC ) { bool boundingBoxInitialized = false; int warningLevel = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); if ( (geometry != NULL ) && (const_cast< mitk::BoundingBox * >( geometry->GetBoundingBoxInWorld())->GetDiagonalLength2() > mitk::eps) ) { boundingBoxInitialized = true; } mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance( renderWindow ); int id = baseRenderer->GetMapperID(); this->InternalViewInitialization( baseRenderer, geometry, boundingBoxInitialized, id ); if ( boundingBoxInitialized && initializeGlobalTimeSNC ) { m_TimeNavigationController->SetInputWorldTimeGeometry( geometry ); } m_TimeNavigationController->Update(); this->RequestUpdate( renderWindow ); vtkObject::SetGlobalWarningDisplay( warningLevel ); return boundingBoxInitialized; } bool RenderingManager::InitializeView( vtkRenderWindow * renderWindow ) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance( renderWindow ); mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); // Update the SNC nc->Update(); this->RequestUpdate( renderWindow ); return true; } void RenderingManager::InternalViewInitialization(mitk::BaseRenderer *baseRenderer, const mitk::TimeGeometry *geometry, bool boundingBoxInitialized, int mapperID ) { mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); if ( boundingBoxInitialized ) { // Set geometry for NC nc->SetInputWorldTimeGeometry( geometry ); nc->Update(); if ( mapperID == 1 ) { // For 2D SNCs, steppers are set so that the cross is centered // in the image nc->GetSlice()->SetPos( nc->GetSlice()->GetSteps() / 2 ); } // Fit the render window DisplayGeometry baseRenderer->GetDisplayGeometry()->Fit(); baseRenderer->GetCameraController()->SetViewToAnterior(); } else { nc->Update(); } } const SliceNavigationController* RenderingManager::GetTimeNavigationController() const { return m_TimeNavigationController.GetPointer(); } SliceNavigationController* RenderingManager::GetTimeNavigationController() { return m_TimeNavigationController.GetPointer(); } void RenderingManager::ExecutePendingRequests() { m_UpdatePending = false; // Satisfy all pending update requests RenderWindowList::iterator it; int i = 0; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it, ++i ) { if ( it->second == RENDERING_REQUESTED ) { this->ForceImmediateUpdate( it->first ); } } } void RenderingManager::RenderingStartCallback( vtkObject *caller, unsigned long , void *, void * ) { vtkRenderWindow *renderWindow = dynamic_cast< vtkRenderWindow * >( caller ); mitk::RenderingManager* renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); RenderWindowList &renderWindowList = renman->m_RenderWindowList; if ( renderWindow ) { renderWindowList[renderWindow] = RENDERING_INPROGRESS; } renman->m_UpdatePending = false; } void RenderingManager ::RenderingProgressCallback( vtkObject *caller, unsigned long , void *, void * ) { vtkRenderWindow *renderWindow = dynamic_cast< vtkRenderWindow * >( caller ); mitk::RenderingManager* renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); if ( renman->m_LODAbortMechanismEnabled ) { vtkRenderWindow *renderWindow = dynamic_cast< vtkRenderWindow * >( caller ); if ( renderWindow ) { BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow ); if ( renderer && (renderer->GetNumberOfVisibleLODEnabledMappers() > 0) ) { renman->DoMonitorRendering(); } } } } void RenderingManager ::RenderingEndCallback( vtkObject *caller, unsigned long , void *, void * ) { vtkRenderWindow *renderWindow = dynamic_cast< vtkRenderWindow * >( caller ); mitk::RenderingManager* renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); RenderWindowList &renderWindowList = renman->m_RenderWindowList; RendererIntMap &nextLODMap = renman->m_NextLODMap; if ( renderWindow ) { BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow ); if ( renderer ) { renderWindowList[renderer->GetRenderWindow()] = RENDERING_INACTIVE; // Level-of-Detail handling if ( renderer->GetNumberOfVisibleLODEnabledMappers() > 0 ) { if(nextLODMap[renderer]==0) renman->StartOrResetTimer(); else nextLODMap[renderer] = 0; } } } } bool RenderingManager ::IsRendering() const { RenderWindowList::const_iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { if ( it->second == RENDERING_INPROGRESS ) { return true; } } return false; } void RenderingManager ::AbortRendering() { RenderWindowList::iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { if ( it->second == RENDERING_INPROGRESS ) { it->first->SetAbortRender( true ); m_RenderingAbortedMap[BaseRenderer::GetInstance(it->first)] = true; } } } int RenderingManager ::GetNextLOD( BaseRenderer *renderer ) { if ( renderer != NULL ) { return m_NextLODMap[renderer]; } else { return 0; } } void RenderingManager ::ExecutePendingHighResRenderingRequest() { RenderWindowList::iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { BaseRenderer *renderer = BaseRenderer::GetInstance( it->first ); if(renderer->GetNumberOfVisibleLODEnabledMappers()>0) { if(m_NextLODMap[renderer]==0) { m_NextLODMap[renderer]=1; RequestUpdate( it->first ); } } } } void RenderingManager ::SetMaximumLOD( unsigned int max ) { m_MaxLOD = max; } //enable/disable shading void RenderingManager ::SetShading(bool state, unsigned int lod) { if(lod>m_MaxLOD) { itkWarningMacro(<<"LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return; } m_ShadingEnabled[lod] = state; } bool RenderingManager ::GetShading(unsigned int lod) { if(lod>m_MaxLOD) { itkWarningMacro(<<"LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return false; } return m_ShadingEnabled[lod]; } //enable/disable the clipping plane void RenderingManager ::SetClippingPlaneStatus(bool status) { m_ClippingPlaneEnabled = status; } bool RenderingManager ::GetClippingPlaneStatus() { return m_ClippingPlaneEnabled; } void RenderingManager ::SetShadingValues(float ambient, float diffuse, float specular, float specpower) { m_ShadingValues[0] = ambient; m_ShadingValues[1] = diffuse; m_ShadingValues[2] = specular; m_ShadingValues[3] = specpower; } RenderingManager::FloatVector & RenderingManager ::GetShadingValues() { return m_ShadingValues; } void RenderingManager::InitializePropertyList() { if (m_PropertyList.IsNull()) { m_PropertyList = PropertyList::New(); } this->SetProperty("coupled-zoom", BoolProperty::New(false)); this->SetProperty("coupled-plane-rotation", BoolProperty::New(false)); this->SetProperty("MIP-slice-rendering", BoolProperty::New(false)); } PropertyList::Pointer RenderingManager::GetPropertyList() const { return m_PropertyList; } BaseProperty* RenderingManager::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void RenderingManager::SetProperty(const char *propertyKey, BaseProperty* propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void RenderingManager::SetDataStorage( DataStorage* storage ) { if ( storage != NULL ) { m_DataStorage = storage; RenderingManager::RenderWindowVector::iterator iter; for ( iter = m_AllRenderWindows.begin(); iterSetDataStorage( m_DataStorage.GetPointer() ); } } } mitk::DataStorage* RenderingManager::GetDataStorage() { return m_DataStorage; } void RenderingManager::SetGlobalInteraction( mitk::GlobalInteraction* globalInteraction ) { if ( globalInteraction != NULL ) { m_GlobalInteraction = globalInteraction; } } mitk::GlobalInteraction* RenderingManager::GetGlobalInteraction() { return m_GlobalInteraction; } // Create and register generic RenderingManagerFactory. TestingRenderingManagerFactory renderingManagerFactory; } // namespace diff --git a/Core/Code/Controllers/mitkSliceNavigationController.cpp b/Core/Code/Controllers/mitkSliceNavigationController.cpp index 07af28be43..02d3ac4895 100644 --- a/Core/Code/Controllers/mitkSliceNavigationController.cpp +++ b/Core/Code/Controllers/mitkSliceNavigationController.cpp @@ -1,837 +1,837 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSliceNavigationController.h" #include "mitkBaseRenderer.h" #include "mitkSlicedGeometry3D.h" #include "mitkPlaneGeometry.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkStateEvent.h" #include "mitkCrosshairPositionEvent.h" #include "mitkPositionEvent.h" #include "mitkProportionalTimeGeometry.h" #include "mitkInteractionConst.h" #include "mitkAction.h" #include "mitkGlobalInteraction.h" #include "mitkEventMapper.h" #include "mitkFocusManager.h" #include "mitkVtkPropRenderer.h" #include "mitkRenderingManager.h" #include "mitkInteractionConst.h" #include "mitkPointOperation.h" #include "mitkPlaneOperation.h" #include "mitkUndoController.h" #include "mitkOperationEvent.h" #include "mitkNodePredicateDataType.h" #include "mitkStatusBar.h" #include "mitkImage.h" #include "mitkApplyTransformMatrixOperation.h" #include "mitkMemoryUtilities.h" #include namespace mitk { SliceNavigationController::SliceNavigationController( const char *type ) : BaseController( type ), m_InputWorldGeometry3D( NULL ), m_InputWorldTimeGeometry( NULL ), m_CreatedWorldGeometry( NULL ), m_ViewDirection( Axial ), m_DefaultViewDirection( Axial ), m_RenderingManager( NULL ), m_Renderer( NULL ), m_Top( false ), m_FrontSide( false ), m_Rotated( false ), m_BlockUpdate( false ), m_SliceLocked( false ), m_SliceRotationLocked( false ), m_OldPos(0) { typedef itk::SimpleMemberCommand< SliceNavigationController > SNCCommandType; SNCCommandType::Pointer sliceStepperChangedCommand, timeStepperChangedCommand; sliceStepperChangedCommand = SNCCommandType::New(); timeStepperChangedCommand = SNCCommandType::New(); sliceStepperChangedCommand->SetCallbackFunction( this, &SliceNavigationController::SendSlice ); timeStepperChangedCommand->SetCallbackFunction( this, &SliceNavigationController::SendTime ); m_Slice->AddObserver( itk::ModifiedEvent(), sliceStepperChangedCommand ); m_Time->AddObserver( itk::ModifiedEvent(), timeStepperChangedCommand ); m_Slice->SetUnitName( "mm" ); m_Time->SetUnitName( "ms" ); m_Top = false; m_FrontSide = false; m_Rotated = false; } SliceNavigationController::~SliceNavigationController() { } void SliceNavigationController::SetInputWorldGeometry3D( const BaseGeometry *geometry ) { if ( geometry != NULL ) { if ( const_cast< BoundingBox * >( geometry->GetBoundingBox()) ->GetDiagonalLength2() < eps ) { itkWarningMacro( "setting an empty bounding-box" ); geometry = NULL; } } if ( m_InputWorldGeometry3D != geometry ) { m_InputWorldGeometry3D = geometry; m_InputWorldTimeGeometry = NULL; this->Modified(); } } void SliceNavigationController::SetInputWorldTimeGeometry( const TimeGeometry *geometry ) { if ( geometry != NULL ) { if ( const_cast< BoundingBox * >( geometry->GetBoundingBoxInWorld()) ->GetDiagonalLength2() < eps ) { itkWarningMacro( "setting an empty bounding-box" ); geometry = NULL; } } if ( m_InputWorldTimeGeometry != geometry ) { m_InputWorldTimeGeometry = geometry; m_InputWorldGeometry3D = NULL; this->Modified(); } } RenderingManager * SliceNavigationController::GetRenderingManager() const { mitk::RenderingManager* renderingManager = m_RenderingManager.GetPointer(); if (renderingManager != NULL) return renderingManager; if ( m_Renderer != NULL ) { renderingManager = m_Renderer->GetRenderingManager(); if (renderingManager != NULL) return renderingManager; } return mitk::RenderingManager::GetInstance(); } void SliceNavigationController::SetViewDirectionToDefault() { m_ViewDirection = m_DefaultViewDirection; } const char* SliceNavigationController::GetViewDirectionAsString() { const char* viewDirectionString; switch(m_ViewDirection) { case SliceNavigationController::Axial: viewDirectionString = "Axial"; break; case SliceNavigationController::Sagittal: viewDirectionString = "Sagittal"; break; case SliceNavigationController::Frontal: viewDirectionString = "Coronal"; break; case SliceNavigationController::Original: viewDirectionString = "Original"; break; default: viewDirectionString = "No View Direction Available"; break; } return viewDirectionString; } void SliceNavigationController::Update() { if ( !m_BlockUpdate ) { if ( m_ViewDirection == Axial ) { this->Update( Axial, false, false, true ); } else { this->Update( m_ViewDirection ); } } } void SliceNavigationController::Update( SliceNavigationController::ViewDirection viewDirection, bool top, bool frontside, bool rotated ) { TimeGeometry::ConstPointer worldTimeGeometry = m_InputWorldTimeGeometry; if( m_BlockUpdate || ( m_InputWorldTimeGeometry.IsNull() && m_InputWorldGeometry3D.IsNull() ) || ( (worldTimeGeometry.IsNotNull()) && (worldTimeGeometry->CountTimeSteps() == 0) ) ) { return; } m_BlockUpdate = true; if ( m_InputWorldTimeGeometry.IsNotNull() && m_LastUpdateTime < m_InputWorldTimeGeometry->GetMTime() ) { Modified(); } if ( m_InputWorldGeometry3D.IsNotNull() && m_LastUpdateTime < m_InputWorldGeometry3D->GetMTime() ) { Modified(); } this->SetViewDirection( viewDirection ); this->SetTop( top ); this->SetFrontSide( frontside ); this->SetRotated( rotated ); if ( m_LastUpdateTime < GetMTime() ) { m_LastUpdateTime = GetMTime(); // initialize the viewplane SlicedGeometry3D::Pointer slicedWorldGeometry = NULL; BaseGeometry::ConstPointer currentGeometry = NULL; if (m_InputWorldTimeGeometry.IsNotNull()) if (m_InputWorldTimeGeometry->IsValidTimeStep(GetTime()->GetPos())) currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(GetTime()->GetPos()); else currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(0); else currentGeometry = m_InputWorldGeometry3D; m_CreatedWorldGeometry = NULL; switch ( viewDirection ) { case Original: if ( worldTimeGeometry.IsNotNull()) { m_CreatedWorldGeometry = worldTimeGeometry->Clone(); worldTimeGeometry = m_CreatedWorldGeometry.GetPointer(); slicedWorldGeometry = dynamic_cast< SlicedGeometry3D * >( m_CreatedWorldGeometry->GetGeometryForTimeStep( this->GetTime()->GetPos() ).GetPointer() ); if ( slicedWorldGeometry.IsNotNull() ) { break; } } else { const SlicedGeometry3D *worldSlicedGeometry = dynamic_cast< const SlicedGeometry3D * >( currentGeometry.GetPointer()); if ( worldSlicedGeometry != NULL ) { slicedWorldGeometry = static_cast< SlicedGeometry3D * >( currentGeometry->Clone().GetPointer()); break; } } //else: use Axial: no "break" here!! case Axial: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( currentGeometry, PlaneGeometry::Axial, top, frontside, rotated ); slicedWorldGeometry->SetSliceNavigationController( this ); break; case Frontal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( currentGeometry, PlaneGeometry::Frontal, top, frontside, rotated ); slicedWorldGeometry->SetSliceNavigationController( this ); break; case Sagittal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( currentGeometry, PlaneGeometry::Sagittal, top, frontside, rotated ); slicedWorldGeometry->SetSliceNavigationController( this ); break; default: itkExceptionMacro("unknown ViewDirection"); } m_Slice->SetPos( 0 ); m_Slice->SetSteps( (int)slicedWorldGeometry->GetSlices() ); if ( m_CreatedWorldGeometry.IsNull() ) { // initialize TimeGeometry m_CreatedWorldGeometry = ProportionalTimeGeometry::New(); } if ( worldTimeGeometry.IsNull()) { m_CreatedWorldGeometry = ProportionalTimeGeometry::New(); dynamic_cast(m_CreatedWorldGeometry.GetPointer())->Initialize(slicedWorldGeometry, 1); m_Time->SetSteps( 0 ); m_Time->SetPos( 0 ); m_Time->InvalidateRange(); } else { m_BlockUpdate = true; m_Time->SetSteps( worldTimeGeometry->CountTimeSteps() ); m_Time->SetPos( 0 ); const TimeBounds &timeBounds = worldTimeGeometry->GetTimeBounds(); m_Time->SetRange( timeBounds[0], timeBounds[1] ); m_BlockUpdate = false; assert( worldTimeGeometry->GetGeometryForTimeStep( this->GetTime()->GetPos() ).IsNotNull() ); TimePointType minimumTimePoint = worldTimeGeometry->TimeStepToTimePoint(this->GetTime()->GetPos()); TimePointType stepDuration = worldTimeGeometry->TimeStepToTimePoint(this->GetTime()->GetPos()+1)-worldTimeGeometry->TimeStepToTimePoint(this->GetTime()->GetPos()); //@todo implement for non-evenly-timed geometry! m_CreatedWorldGeometry = ProportionalTimeGeometry::New(); dynamic_cast(m_CreatedWorldGeometry.GetPointer())->Initialize(slicedWorldGeometry, worldTimeGeometry->CountTimeSteps()); dynamic_cast(m_CreatedWorldGeometry.GetPointer())->GetMinimumTimePoint(minimumTimePoint); dynamic_cast(m_CreatedWorldGeometry.GetPointer())->SetStepDuration(stepDuration); } } // unblock update; we may do this now, because if m_BlockUpdate was already // true before this method was entered, then we will never come here. m_BlockUpdate = false; // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry and time/slice data. this->SendCreatedWorldGeometry(); this->SendSlice(); this->SendTime(); // Adjust the stepper range of slice stepper according to geometry this->AdjustSliceStepperRange(); } void SliceNavigationController::SendCreatedWorldGeometry() { // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry. if ( !m_BlockUpdate ) { this->InvokeEvent( GeometrySendEvent(m_CreatedWorldGeometry, 0) ); } } void SliceNavigationController::SendCreatedWorldGeometryUpdate() { if ( !m_BlockUpdate ) { this->InvokeEvent( GeometryUpdateEvent(m_CreatedWorldGeometry, m_Slice->GetPos()) ); } } void SliceNavigationController::SendSlice() { if ( !m_BlockUpdate ) { if ( m_CreatedWorldGeometry.IsNotNull() ) { this->InvokeEvent( GeometrySliceEvent(m_CreatedWorldGeometry, m_Slice->GetPos()) ); // send crosshair event crosshairPositionEvent.Send(); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SendTime() { if ( !m_BlockUpdate ) { if ( m_CreatedWorldGeometry.IsNotNull() ) { this->InvokeEvent( GeometryTimeEvent(m_CreatedWorldGeometry, m_Time->GetPos()) ); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SetGeometry( const itk::EventObject & ) { } void SliceNavigationController ::SetGeometryTime( const itk::EventObject &geometryTimeEvent ) { const SliceNavigationController::GeometryTimeEvent *timeEvent = dynamic_cast< const SliceNavigationController::GeometryTimeEvent * >( &geometryTimeEvent); assert( timeEvent != NULL ); TimeGeometry *timeGeometry = timeEvent->GetTimeGeometry(); assert( timeGeometry != NULL ); if ( m_CreatedWorldGeometry.IsNotNull() ) { int timeStep = (int) timeEvent->GetPos(); ScalarType timeInMS; timeInMS = timeGeometry->TimeStepToTimePoint( timeStep ); timeStep = m_CreatedWorldGeometry->TimePointToTimeStep( timeInMS ); this->GetTime()->SetPos( timeStep ); } } void SliceNavigationController ::SetGeometrySlice(const itk::EventObject & geometrySliceEvent) { const SliceNavigationController::GeometrySliceEvent* sliceEvent = dynamic_cast( &geometrySliceEvent); assert(sliceEvent!=NULL); this->GetSlice()->SetPos(sliceEvent->GetPos()); } void SliceNavigationController::SelectSliceByPoint( const Point3D &point ) { //@todo add time to PositionEvent and use here!! SlicedGeometry3D* slicedWorldGeometry = dynamic_cast< SlicedGeometry3D * >( m_CreatedWorldGeometry->GetGeometryForTimeStep( this->GetTime()->GetPos() ).GetPointer() ); if ( slicedWorldGeometry ) { int bestSlice = -1; double bestDistance = itk::NumericTraits::max(); int s, slices; slices = slicedWorldGeometry->GetSlices(); if ( slicedWorldGeometry->GetEvenlySpaced() ) { mitk::PlaneGeometry *plane = slicedWorldGeometry->GetPlaneGeometry( 0 ); const Vector3D &direction = slicedWorldGeometry->GetDirectionVector(); Point3D projectedPoint; plane->Project( point, projectedPoint ); // Check whether the point is somewhere within the slice stack volume; // otherwise, the defualt slice (0) will be selected if ( direction[0] * (point[0] - projectedPoint[0]) + direction[1] * (point[1] - projectedPoint[1]) + direction[2] * (point[2] - projectedPoint[2]) >= 0 ) { bestSlice = (int)(plane->Distance( point ) / slicedWorldGeometry->GetSpacing()[2] + 0.5); } } else { Point3D projectedPoint; for ( s = 0; s < slices; ++s ) { slicedWorldGeometry->GetPlaneGeometry( s )->Project( point, projectedPoint ); Vector3D distance = projectedPoint - point; ScalarType currentDistance = distance.GetSquaredNorm(); if ( currentDistance < bestDistance ) { bestDistance = currentDistance; bestSlice = s; } } } if ( bestSlice >= 0 ) { this->GetSlice()->SetPos( bestSlice ); } else { this->GetSlice()->SetPos( 0 ); } this->SendCreatedWorldGeometryUpdate(); } } void SliceNavigationController::ReorientSlices( const Point3D &point, const Vector3D &normal ) { PlaneOperation op( OpORIENT, point, normal ); m_CreatedWorldGeometry->ExecuteOperation( &op ); this->SendCreatedWorldGeometryUpdate(); } void SliceNavigationController::ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1 ) { PlaneOperation op( OpORIENT, point, axisVec0, axisVec1 ); m_CreatedWorldGeometry->ExecuteOperation( &op ); this->SendCreatedWorldGeometryUpdate(); } mitk::TimeGeometry * SliceNavigationController::GetCreatedWorldGeometry() { return m_CreatedWorldGeometry; } const mitk::BaseGeometry * SliceNavigationController::GetCurrentGeometry3D() { if ( m_CreatedWorldGeometry.IsNotNull() ) { return m_CreatedWorldGeometry->GetGeometryForTimeStep( this->GetTime()->GetPos() ); } else { return NULL; } } const mitk::PlaneGeometry * SliceNavigationController::GetCurrentPlaneGeometry() { const mitk::SlicedGeometry3D *slicedGeometry = dynamic_cast< const mitk::SlicedGeometry3D * > ( this->GetCurrentGeometry3D() ); if ( slicedGeometry ) { const mitk::PlaneGeometry *planeGeometry = dynamic_cast< mitk::PlaneGeometry * > ( slicedGeometry->GetPlaneGeometry(this->GetSlice()->GetPos()) ); return planeGeometry; } else { return NULL; } } void SliceNavigationController::SetRenderer( BaseRenderer *renderer ) { m_Renderer = renderer; } BaseRenderer * SliceNavigationController::GetRenderer() const { return m_Renderer; } void SliceNavigationController::AdjustSliceStepperRange() { const mitk::SlicedGeometry3D *slicedGeometry = dynamic_cast< const mitk::SlicedGeometry3D * > ( this->GetCurrentGeometry3D() ); const Vector3D &direction = slicedGeometry->GetDirectionVector(); int c = 0; int i, k = 0; for ( i = 0; i < 3; ++i ) { if ( fabs(direction[i]) < 0.000000001 ) { ++c; } else { k = i; } } if ( c == 2 ) { ScalarType min = slicedGeometry->GetOrigin()[k]; ScalarType max = min + slicedGeometry->GetExtentInMM( k ); m_Slice->SetRange( min, max ); } else { m_Slice->InvalidateRange(); } } void SliceNavigationController::ExecuteOperation( Operation *operation ) { // switch on type // - select best slice for a given point // - rotate created world geometry according to Operation->SomeInfo() if ( !operation ) { return; } switch ( operation->GetOperationType() ) { case OpMOVE: // should be a point operation { if ( !m_SliceLocked ) //do not move the cross position { // select a slice PointOperation *po = dynamic_cast< PointOperation * >( operation ); if ( po && po->GetIndex() == -1 ) { this->SelectSliceByPoint( po->GetPoint() ); } else if ( po && po->GetIndex() != -1 ) // undo case because index != -1, index holds the old position of this slice { this->GetSlice()->SetPos( po->GetIndex() ); } } break; } case OpRESTOREPLANEPOSITION: { m_CreatedWorldGeometry->ExecuteOperation( operation ); this->SendCreatedWorldGeometryUpdate(); break; } case OpAPPLYTRANSFORMMATRIX: { m_CreatedWorldGeometry->ExecuteOperation( operation ); this->SendCreatedWorldGeometryUpdate(); break; } default: { // do nothing break; } } } mitk::DataNode::Pointer SliceNavigationController::GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes,mitk::Point3D worldposition) { mitk::DataNode::Pointer node; int maxlayer = -32768; bool isHelper (false); if(nodes.IsNotNull()) { for (unsigned int x = 0; x < nodes->size(); x++) { nodes->at(x)->GetBoolProperty("helper object", isHelper); if(nodes->at(x)->GetData()->GetGeometry()->IsInside(worldposition) && isHelper == false) { int layer = 0; if(!(nodes->at(x)->GetIntProperty("layer", layer))) continue; if(layer > maxlayer) { if(static_cast(nodes->at(x))->IsVisible(m_Renderer)) { node = nodes->at(x); maxlayer = layer; } } } } } return node; } // Relict from the old times, when automous decisions were accepted // behavior. Remains in here, because some RenderWindows do exist outside // of StdMultiWidgets. bool SliceNavigationController ::ExecuteAction( Action* action, StateEvent const* stateEvent ) { bool ok = false; const PositionEvent* posEvent = dynamic_cast< const PositionEvent * >( stateEvent->GetEvent() ); if ( posEvent != NULL ) { if ( m_CreatedWorldGeometry.IsNull() ) { return true; } switch (action->GetActionId()) { case AcMOVE: { BaseRenderer *baseRenderer = posEvent->GetSender(); if ( !baseRenderer ) { baseRenderer = const_cast( GlobalInteraction::GetInstance()->GetFocus() ); } if ( baseRenderer ) if ( baseRenderer->GetMapperID() == 1 ) { PointOperation doOp(OpMOVE, posEvent->GetWorldPosition()); this->ExecuteOperation( &doOp ); // If click was performed in this render window than we have to update the status bar information about position and pixel value. if(baseRenderer == m_Renderer) { { std::string statusText; TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = baseRenderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); mitk::Point3D worldposition = posEvent->GetWorldPosition(); //int maxlayer = -32768; mitk::Image::Pointer image3D; mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; bool isBinary (false); node = this->GetTopLayerNode(nodes,worldposition); if(node.IsNotNull()) { node->GetBoolProperty("binary", isBinary); if(isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = baseRenderer->GetDataStorage()->GetSources(node, NULL, true); if(!sourcenodes->empty()) { topSourceNode = this->GetTopLayerNode(sourcenodes,worldposition); } if(topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); } else { image3D = dynamic_cast(node->GetData()); } } else { image3D = dynamic_cast(node->GetData()); } } std::stringstream stream; stream.imbue(std::locale::classic()); // get the position and gray value from the image and build up status bar text if(image3D.IsNotNull()) { - Index3D p; + itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(worldposition, p); stream.precision(2); stream<<"Position: <" << std::fixed < mm"; stream<<"; Index: <"< "; mitk::ScalarType pixelValue = image3D->GetPixelValueByIndex(p, baseRenderer->GetTimeStep()); if (fabs(pixelValue)>1000000 || fabs(pixelValue) < 0.01) { stream<<"; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: " << std::scientific<< pixelValue <<" "; } else { stream<<"; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: "<< pixelValue <<" "; } } else { stream << "No image information at this position!"; } statusText = stream.str(); mitk::StatusBar::GetInstance()->DisplayGreyValueText(statusText.c_str()); } } ok = true; break; } } default: ok = true; break; } return ok; } const DisplayPositionEvent *displPosEvent = dynamic_cast< const DisplayPositionEvent * >( stateEvent->GetEvent() ); if ( displPosEvent != NULL ) { return true; } return false; } } // namespace diff --git a/Core/Code/Controllers/mitkSlicesRotator.h b/Core/Code/Controllers/mitkSlicesRotator.h index 68d61afea9..33eac0839d 100644 --- a/Core/Code/Controllers/mitkSlicesRotator.h +++ b/Core/Code/Controllers/mitkSlicesRotator.h @@ -1,165 +1,165 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef SLICESROTATOR_H_HEADER_INCLUDED_C1C55A2F #define SLICESROTATOR_H_HEADER_INCLUDED_C1C55A2F #include #pragma GCC visibility push(default) #include #pragma GCC visibility pop -#include +#include namespace mitk { /** \brief Coordinates rotation of multiple visible rendering planes (represented as lines in other render windows). \ingroup NavigationControl This class takes care of several SliceNavigationControllers and handles slice selection / slice rotation. It is added as listener to GlobalInteraction by QmitkStdMultiWidget. The SlicesRotator class adds the possibility of slice rotation to the "normal" behaviour of SliceNavigationControllers (which is picking one plane from a stack of planes). This additional class SlicesRotator is needed, because one has to be aware of multiple "visible slices" (selected PlaneGeometries of some SliceNavigationControllers) in order to choose between rotation and slice selection. Such functionality could not be implemented by a single SliceNavigationController. Rotation is achieved by modifying (rotating) the generated TimeGeometry of the corresponding SliceNavigationControllers. \section mitkSlicesRotator_StandardCase The standard case: three orthogonal views (MPR) With SlicesRotator, the rule to choose between slice rotation and selection is simple: For a mouse down event, count the number of visible planes, which are "near" the cursor. If this number is 2 (one for the window, which currently holds the cursor, one for the intersection line of another visible slice), then initiate rotation, else select slices near the cursor. If the "LinkPlanes" flag is set, the rotation is applied to the planes of all registered SNCs, not only of the one associated with the directly selected plane. In contrast to the situation without the SlicesRotator, the SliceNavigationControllers are now NOT directly registered as listeners to GlobalInteraction. SlicesRotator is registered as a listener and decides whether something should be rotated or whether another slice should be selected. In the latter case, a PositionEvent is just forwarded to the SliceNavigationController. \section mitkSlicesRotator_GeneralizedCase The generalized case: any number of views Above section as well as the original implementation of this class assumes that we have exactly three 2D vies in our scene. This used to be the standard setup of the MITK associated application for a long time. With custom applications based on MITK it is easy to create different situations. One usual use case would be to have one extra render window display the contents of any of the other ones and behave exactly like it (could e.g. be used on a second screen). In this situation the above assumption "we rotate when there are exactly 2 slices close to the cursor" will not hold: since we always have two render windows displaying the exact same slice, the number of 2 is the minimum we get. Whenever the user clicks in one of those windows and the cursor is close to one of the orthogonal planes, we will get a count of 3 or more planes that are "close to the cursor". For the class to behave correctly, we actually need to distinguish three separate cases: 1. the cursor is not close to any orthogonal planes. This should result in slice selection. 2. the cursor is close to just one orthogonal plane OR multiple which are not distinguishable visually. This should result in rotation. 3. the cursor is close to multiple orthogonal planes which are rendered as distinguishable lines on the render window. This is the case when we hit the crosshair-center of the view. In this case, we need to also just select slices. \section mitkSlicesRotator_Solution Deciding between slice selection and rotation The "counting nearby lines in the renderwindow" can also work for the general case described above. Only one details needs to be accounted for: we must not count a line when it is identical to another line. I.e. we just count how many visible lines on the screen are very close to the cursor. When this number is 1, we rotate, otherwise we let the SliceNavigationControllers do their slice selection job. \sa SlicesSwiveller */ class MITK_CORE_EXPORT SlicesRotator : public SlicesCoordinator { public: mitkClassMacro(SlicesRotator, SlicesCoordinator); static Pointer New(); /** \brief New Macro with one parameter for creating this object with static New(..) method. Needs to be the "slices-rotator" pattern of StateMachine.xml to work as expected. **/ mitkNewMacro1Param(Self, const char*); /** \brief Callback for modifications in observed SliceNavigationControllers -- forwards to UpdateRotatableSNCs(). This method is called when an observed SliceNavigationController changes its world geometry. The connection is established by calling the other SliceNavigationController's method ConnectGeometrySendEvent (or similar). */ virtual void SetGeometry(const itk::EventObject& EventObject); /** \brief NOT USED by anything open-source. Deprecated. Highly obfuscated code. Use SliceNavigationController::ReorientSlices() instead! #Deprecated */ virtual void RotateToPoint( SliceNavigationController *rotationPlaneSNC, SliceNavigationController *rotatedPlaneSNC, const Point3D &point, bool linked = false ); protected: SlicesRotator(const char* machine); virtual ~SlicesRotator(); /** \brief Called from SlicesCoordinator after a new controller is added (to internal list m_SliceNavigationControllers). */ virtual void OnSliceControllerAdded(SliceNavigationController* snc); /* \brief Called from SlicesCoordinator after a new controller is being removed (to internal list m_SliceNavigationControllers). */ virtual void OnSliceControllerRemoved(SliceNavigationController* snc); /** \brief Check all observed SliceNavigationControllers: remember those that are rotatable in m_RotatableSNCs. */ virtual void UpdateRotatableSNCs(); // following methods called from superclass ExecuteAction bool DoSelectSlice(Action*, const StateEvent*); bool DoDecideBetweenRotationAndSliceSelection(Action*, const StateEvent*); bool DoStartRotation(Action*, const StateEvent*); bool DoEndRotation(Action*, const StateEvent*); bool DoRotationStep(Action*, const StateEvent*); SNCVector m_RotatableSNCs; /// all SNCs that currently have CreatedWorldGeometries, that can be rotated. SNCVector m_SNCsToBeRotated; /// all SNCs that will be rotated (exceptions are the ones parallel to the one being clicked) Point3D m_LastCursorPosition; /// used for calculation of the rotation angle Point3D m_CenterOfRotation; /// used for calculation of the rotation angle }; } // namespace #endif diff --git a/Core/Code/Controllers/mitkSlicesSwiveller.h b/Core/Code/Controllers/mitkSlicesSwiveller.h index def17b0dd7..efa3500da0 100644 --- a/Core/Code/Controllers/mitkSlicesSwiveller.h +++ b/Core/Code/Controllers/mitkSlicesSwiveller.h @@ -1,119 +1,119 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef SLICESSWIVELLER_H_HEADER_INCLUDED #define SLICESSWIVELLER_H_HEADER_INCLUDED #include -#include +#include #pragma GCC visibility push(default) #include #pragma GCC visibility pop namespace mitk { /** * \brief Enables arbitrary rotation of visible slices around a swivel point * (for sliced geometries). * \ingroup NavigationControl * * This class takes care of several SliceNavigationControllers and handles * slice selection / slice rotation. It is added as listener to * GlobalInteraction by QmitkStdMultiWidget. * * The SlicesSwiveller class adds the possibility of slice rotation to the * "normal" behaviour of SliceNavigationControllers. This additional class * is needed, because one has to be aware of several "visible slices" * (selected PlaneGeometries of some SliceNavigationControllers) in order to * choose between rotation and slice selection. * * Rotation is achieved by modifying (rotating) the generated * TimeGeometry of the corresponding SliceNavigationController. * * With SlicesSwiveller, slice rotation works as follows: the user clicks onto * a 2D view (2D plane) and drags the mouse; the relative direction and angle * of the dragged mouse movement directly effects the rotation axis and * angle. If "LinkPlanes" is set to true, the rotation is applied to the * planes of all registered SNCs, not only of the one associated with the * plane clicked on. * * In contrast to the situation without the SlicesRotator, the * SliceNavigationControllers are now not directly registered as listeners to * GlobalInteraction. SlicesRotator is registered as a listener and decides * whether something should be rotated or whether another slice should be * selected. In the latter case, a PositionEvent is just forwarded to the * SliceNavigationController. * * \sa SlicesRotator */ class MITK_CORE_EXPORT SlicesSwiveller : public SlicesCoordinator { public: mitkClassMacro(SlicesSwiveller, SlicesCoordinator); static Pointer New(); /** * @brief New Macro with one parameter for creating this object with static New(..) method **/ mitkNewMacro1Param(Self, const char*); virtual void SetGeometry(const itk::EventObject& EventObject); protected: SlicesSwiveller(const char* machine); // clear list of controllers virtual ~SlicesSwiveller(); // check if the slices of this SliceNavigationController can be rotated (???) Possible virtual void OnSliceControllerAdded(SliceNavigationController* snc); virtual void OnSliceControllerRemoved(SliceNavigationController* snc); virtual void UpdateRelevantSNCs(); virtual bool ExecuteAction(Action * action, StateEvent const* stateEvent); /** All SNCs that currently have CreatedWorldGeometries, that can be rotated */ SNCVector m_RelevantSNCs; /** SNCs that will be rotated (clicked plane + all relevant others, if linked) */ SNCVector m_SNCsToBeRotated; Point3D m_LastCursorPosition; Point3D m_CenterOfRotation; Point2D m_ReferenceCursor; Vector3D m_RotationPlaneNormal; Vector3D m_RotationPlaneXVector; Vector3D m_RotationPlaneYVector; Vector3D m_PreviousRotationAxis; ScalarType m_PreviousRotationAngle; }; } // namespace #endif diff --git a/Core/Code/Controllers/mitkStepper.h b/Core/Code/Controllers/mitkStepper.h index f7ee40990e..9651a5f9af 100644 --- a/Core/Code/Controllers/mitkStepper.h +++ b/Core/Code/Controllers/mitkStepper.h @@ -1,152 +1,152 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef STEPPER_H_HEADER_INCLUDED_C1E77191 #define STEPPER_H_HEADER_INCLUDED_C1E77191 #include #include -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include #include #include namespace mitk { /** * \brief Helper class to step through a list * * A helper class to step through a list. Does not contain the list, just the * position in the list (between 0 and GetSteps()). Provides methods like * First (go to the first element), Next (go to the next one), etc. * * Besides the actual number of steps, the stepper can also hold a stepping * range, indicating the scalar values corresponding to the covered steps. * For example, steppers are generally used to slice a dataset with a plane; * Hereby, Steps indicates the total number of steps (positions) available for * the plane, Pos indicates the current step, and Range indicates the physical * minimum and maximum values for the plane, in this case a value in mm. * * The range can also be supplied with a unit name (a string) which can be * used by classes providing information about the stepping (e.g. graphical * sliders). * * \ingroup NavigationControl */ class MITK_CORE_EXPORT Stepper : public itk::Object { public: mitkClassMacro(Stepper, itk::Object); itkFactorylessNewMacro(Self) itkCloneMacro(Self) itkGetMacro(Pos, unsigned int); virtual void SetPos(unsigned int pos) { // copied from itkMacro.h, itkSetClampMacro(...) unsigned int newPos; if ( m_Steps != 0 ) { newPos = (pos > m_Steps-1 ? m_Steps-1 : pos); } else { newPos = 0; } if (this->m_Pos != newPos ) { this->m_Pos = newPos ; this->Modified(); } } itkGetMacro(Steps, unsigned int); itkSetMacro(Steps, unsigned int); itkGetMacro(AutoRepeat, bool); itkSetMacro(AutoRepeat, bool); itkBooleanMacro(AutoRepeat); /** Causes the stepper to shift direction when the boundary is reached */ itkSetMacro(PingPong, bool); itkGetMacro(PingPong, bool); itkBooleanMacro(PingPong); /** If set to true, the Next() decreases the stepper and Previous() * decreases it */ itkSetMacro(InverseDirection, bool); itkGetMacro(InverseDirection, bool); itkBooleanMacro(InverseDirection); void SetRange( ScalarType min, ScalarType max ); void InvalidateRange(); ScalarType GetRangeMin() const; ScalarType GetRangeMax() const; bool HasValidRange() const; void RemoveRange(); bool HasRange() const; void SetUnitName( const char *unitName ); const char *GetUnitName() const; void RemoveUnitName(); bool HasUnitName() const; virtual void Next(); virtual void Previous(); virtual void First(); virtual void Last(); protected: Stepper(); virtual ~Stepper(); void Increase(); void Decrease(); unsigned int m_Pos; unsigned int m_Steps; bool m_AutoRepeat; bool m_PingPong; bool m_InverseDirection; ScalarType m_RangeMin; ScalarType m_RangeMax; bool m_RangeValid; bool m_HasRange; std::string m_UnitName; bool m_HasUnitName; }; } // namespace mitk #endif /* STEPPER_H_HEADER_INCLUDED_C1E77191 */ diff --git a/Core/Code/DataManagement/itkVtkAbstractTransform.txx b/Core/Code/DataManagement/itkVtkAbstractTransform.txx index 8a1c3b001f..f0330aa7ba 100644 --- a/Core/Code/DataManagement/itkVtkAbstractTransform.txx +++ b/Core/Code/DataManagement/itkVtkAbstractTransform.txx @@ -1,250 +1,250 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "itkVtkAbstractTransform.h" #include -#include +#include namespace itk { template itk::VtkAbstractTransform::VtkAbstractTransform() : m_VtkAbstractTransform(NULL), m_InverseVtkAbstractTransform(NULL), m_LastVtkAbstractTransformTimeStamp(0) { } template itk::VtkAbstractTransform::~VtkAbstractTransform() { if(m_VtkAbstractTransform!=NULL) m_VtkAbstractTransform->UnRegister(NULL); } template vtkAbstractTransform* itk::VtkAbstractTransform::GetVtkAbstractTransform() const { return m_VtkAbstractTransform; } template vtkAbstractTransform* itk::VtkAbstractTransform::GetInverseVtkAbstractTransform() const { return m_InverseVtkAbstractTransform; } template void itk::VtkAbstractTransform::SetVtkAbstractTransform(vtkAbstractTransform* aVtkAbstractTransform) { if(m_VtkAbstractTransform==aVtkAbstractTransform) return; if(m_VtkAbstractTransform!=NULL) m_VtkAbstractTransform->UnRegister(NULL); m_VtkAbstractTransform=aVtkAbstractTransform; if(m_VtkAbstractTransform!=NULL) { m_VtkAbstractTransform->Register(NULL); m_InverseVtkAbstractTransform=m_VtkAbstractTransform->GetInverse(); // memory managed by m_VtkAbstractTransform } m_LastVtkAbstractTransformTimeStamp = m_VtkAbstractTransform->GetMTime(); this->Modified(); } // Transform a point template typename itk::VtkAbstractTransform::OutputPointType itk::VtkAbstractTransform:: TransformPoint(const InputPointType &point) const { assert(m_VtkAbstractTransform!=NULL); OutputPointType outputpoint; vnl_vector vnl_vec; - double vtkpt[3]; + mitk::ScalarType vtkpt[3]; mitk::itk2vtk(point, vtkpt); m_VtkAbstractTransform->TransformPoint(vtkpt, vtkpt); mitk::vtk2itk(vtkpt, outputpoint); return outputpoint; } // Transform a vector template typename itk::VtkAbstractTransform::OutputVectorType itk::VtkAbstractTransform:: TransformVector(const InputVectorType &vect) const { assert(m_VtkAbstractTransform!=NULL); OutputVectorType outputvector; vnl_vector vnl_vec; - double vtkpt[3]={0,0,0}; - double vtkvec[3]; - mitk::vnl2vtk(vect.GetVnlVector(), vtkvec); + mitk::ScalarType vtkpt[3]={0,0,0}; + mitk::ScalarType vtkvec[3]; + mitk::vnl2vtk(vect.GetVnlVector(), vtkvec); m_VtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::vtk2itk(vtkvec, outputvector); return outputvector; } // Transform a vnl_vector_fixed template typename itk::VtkAbstractTransform::OutputVnlVectorType itk::VtkAbstractTransform:: TransformVector(const InputVnlVectorType &vect) const { assert(m_VtkAbstractTransform!=NULL); OutputVnlVectorType outputvector; - double vtkpt[3]={0,0,0}; - double vtkvec[3]; - mitk::vnl2vtk(vect, vtkvec); + mitk::ScalarType vtkpt[3]={0,0,0}; + mitk::ScalarType vtkvec[3]; + mitk::vnl2vtk(vect, vtkvec); m_VtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::vtk2itk(vtkvec, outputvector); return outputvector; } // Transform a CovariantVector template typename itk::VtkAbstractTransform::OutputCovariantVectorType itk::VtkAbstractTransform:: TransformCovariantVector(const InputCovariantVectorType &/*vec*/) const { itkExceptionMacro( << "implement before using!" ); OutputCovariantVectorType result; // Converted vector // for (unsigned int i = 0; i < NDimensions; i++) // { -// result[i] = NumericTraits::Zero; +// result[i] = NumericTraits::Zero; // for (unsigned int j = 0; j < NDimensions; j++) // { // result[i] += m_Inverse[j][i]*vec[j]; // Inverse transposed // } // } return result; } // Back transform a point template typename VtkAbstractTransform::InputPointType itk::VtkAbstractTransform:: BackTransform(const OutputPointType &point) const { assert(m_VtkAbstractTransform!=NULL); OutputPointType outputpoint; - double vtkpt[3]; + mitk::ScalarType vtkpt[3]; mitk::itk2vtk(point, vtkpt); m_InverseVtkAbstractTransform->TransformPoint(vtkpt, vtkpt); mitk::vtk2itk(vtkpt, outputpoint); return outputpoint; } // Back transform a vector template typename VtkAbstractTransform::InputVectorType itk::VtkAbstractTransform:: BackTransform(const OutputVectorType &vect ) const { assert(m_VtkAbstractTransform!=NULL); OutputVectorType outputvector; - double vtkpt[3]={0,0,0}; - double vtkvec[3]; + mitk::ScalarType vtkpt[3]={0,0,0}; + mitk::ScalarType vtkvec[3]; mitk::itk2vtk(vect, vtkvec); m_InverseVtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::vtk2itk(vtkvec, outputvector); return outputvector; } // Back transform a vnl_vector template typename VtkAbstractTransform::InputVnlVectorType itk::VtkAbstractTransform:: BackTransform(const OutputVnlVectorType &vect ) const { assert(m_InverseVtkAbstractTransform!=NULL); OutputVnlVectorType outputvector; - double vtkpt[3]={0,0,0}; - double vtkvec[3]; + mitk::ScalarType vtkpt[3]={0,0,0}; + mitk::ScalarType vtkvec[3]; mitk::itk2vtk(vect, vtkvec); m_InverseVtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::vtk2itk(vtkvec, outputvector); return outputvector; } // Back Transform a CovariantVector template typename VtkAbstractTransform::InputCovariantVectorType itk::VtkAbstractTransform:: BackTransform(const OutputCovariantVectorType &vec) const { itkExceptionMacro( << "implement before using!" ); // for (unsigned int i = 0; i < NDimensions; i++) // { -// result[i] = NumericTraits::Zero; +// result[i] = NumericTraits::Zero; // for (unsigned int j = 0; j < NDimensions; j++) // { // result[i] += m_Matrix[j][i]*vec[j]; // Direct matrix transposed // } // } return vec; } template unsigned long itk::VtkAbstractTransform::GetMTime() const { if((m_VtkAbstractTransform != NULL) && (m_LastVtkAbstractTransformTimeStamp < m_VtkAbstractTransform->GetMTime())) { m_LastVtkAbstractTransformTimeStamp=m_VtkAbstractTransform->GetMTime(); this->Modified(); } return Superclass::GetMTime(); } template void itk::VtkAbstractTransform::SetParameters(const ParametersType&) { // TODO } template void itk::VtkAbstractTransform::SetFixedParameters(const ParametersType&) { // TODO } template void itk::VtkAbstractTransform::ComputeJacobianWithRespectToParameters(const InputPointType&, JacobianType&) const { // TODO } template void itk::VtkAbstractTransform::ComputeJacobianWithRespectToPosition(const InputPointType&, JacobianType&) const { // TODO } } // namespace itk diff --git a/Core/Code/DataManagement/mitkAbstractTransformGeometry.cpp b/Core/Code/DataManagement/mitkAbstractTransformGeometry.cpp index cf26d5d323..db80da8ae6 100644 --- a/Core/Code/DataManagement/mitkAbstractTransformGeometry.cpp +++ b/Core/Code/DataManagement/mitkAbstractTransformGeometry.cpp @@ -1,318 +1,318 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkAbstractTransformGeometry.h" #include mitk::AbstractTransformGeometry::AbstractTransformGeometry() : m_Plane(NULL), m_FrameGeometry(NULL) { Initialize(); } mitk::AbstractTransformGeometry::AbstractTransformGeometry(const AbstractTransformGeometry& other) : Superclass(other), m_ParametricBoundingBox(other.m_ParametricBoundingBox) { if(other.m_ParametricBoundingBox.IsNotNull()) { m_ParametricBoundingBox = other.m_ParametricBoundingBox->DeepCopy(); this->SetParametricBounds(m_ParametricBoundingBox->GetBounds()); } this->SetPlane(other.m_Plane); this->SetFrameGeometry(other.m_FrameGeometry); } mitk::AbstractTransformGeometry::~AbstractTransformGeometry() { } void mitk::AbstractTransformGeometry::PostInitialize() { m_ItkVtkAbstractTransform = itk::VtkAbstractTransform::New(); } vtkAbstractTransform* mitk::AbstractTransformGeometry::GetVtkAbstractTransform() const { return m_ItkVtkAbstractTransform->GetVtkAbstractTransform(); } mitk::ScalarType mitk::AbstractTransformGeometry::GetParametricExtentInMM(int direction) const { if(m_Plane.IsNull()) { itkExceptionMacro(<<"m_Plane is NULL."); } return m_Plane->GetExtentInMM(direction); } -const mitk::Transform3D* mitk::AbstractTransformGeometry::GetParametricTransform() const +const itk::Transform* mitk::AbstractTransformGeometry::GetParametricTransform() const { return m_ItkVtkAbstractTransform; } bool mitk::AbstractTransformGeometry::Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const { assert(this->IsBoundingBoxNull()==false); mitk::Point2D pt2d_mm; bool isInside; isInside = Map(pt3d_mm, pt2d_mm); Map(pt2d_mm, projectedPt3d_mm); return isInside; //Point3D pt3d_units; //pt3d_units = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); //pt3d_units[2] = 0; //projectedPt3d_mm = m_ItkVtkAbstractTransform->TransformPoint(pt3d_units); //return const_cast(m_BoundingBox.GetPointer())->IsInside(pt3d_units); } bool mitk::AbstractTransformGeometry::Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); Point3D pt3d_units; pt3d_units = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); return m_Plane->Map(pt3d_units, pt2d_mm); } void mitk::AbstractTransformGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); m_Plane->Map(pt2d_mm, pt3d_mm); pt3d_mm = m_ItkVtkAbstractTransform->TransformPoint(pt3d_mm); } bool mitk::AbstractTransformGeometry::Project(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { itkExceptionMacro("not implemented yet - replace GetIndexToWorldTransform by m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()"); assert(this->IsBoundingBoxNull()==false); Vector3D vec3d_units; vec3d_units = GetIndexToWorldTransform()->GetInverseMatrix() * vec3d_mm; vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); Point3D pt3d_units; mitk::ScalarType temp[3]; unsigned int i, j; for (j = 0; j < 3; ++j) temp[j] = atPt3d_mm[j] - GetIndexToWorldTransform()->GetOffset()[j]; for (i = 0; i < 3; ++i) { pt3d_units[i] = 0.0; for (j = 0; j < 3; ++j) pt3d_units[i] += GetIndexToWorldTransform()->GetInverseMatrix()[i][j] * temp[j]; } return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); } bool mitk::AbstractTransformGeometry::Project(const mitk::Vector3D &/*vec3d_mm*/, mitk::Vector3D &/*projectedVec3d_mm*/) const { MITK_WARN << "Need additional point! No standard value defined. Please use Project(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm). Unfortunatley this one is not implemented at the moment. Sorry :("; itkExceptionMacro("not implemented yet - replace GetIndexToWorldTransform by m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()"); return false; } bool mitk::AbstractTransformGeometry::Map(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); ScalarType vtkpt[3], vtkvec[3]; itk2vtk(atPt3d_mm, vtkpt); itk2vtk(vec3d_mm, vtkvec); m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::Vector3D vec3d_units; vtk2itk(vtkvec, vec3d_units); return m_Plane->Map(atPt3d_mm, vec3d_units, vec2d_mm); } void mitk::AbstractTransformGeometry::Map(const mitk::Point2D & atPt2d_mm, const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const { m_Plane->Map(atPt2d_mm, vec2d_mm, vec3d_mm); Point3D atPt3d_mm; Map(atPt2d_mm, atPt3d_mm); float vtkpt[3], vtkvec[3]; itk2vtk(atPt3d_mm, vtkpt); itk2vtk(vec3d_mm, vtkvec); m_ItkVtkAbstractTransform->GetVtkAbstractTransform()->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); vtk2itk(vtkvec, vec3d_mm); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Point2D &pt_units, mitk::Point2D &pt_mm) const { m_Plane->IndexToWorld(pt_units, pt_mm); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units) const { m_Plane->WorldToIndex(pt_mm, pt_units); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Point2D & /*atPt2d_units*/, const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const { MITK_WARN<<"Warning! Call of the deprecated function AbstractTransformGeometry::IndexToWorld(point, vec, vec). Use AbstractTransformGeometry::IndexToWorld(vec, vec) instead!"; this->IndexToWorld(vec_units, vec_mm); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const { m_Plane->IndexToWorld(vec_units, vec_mm); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Point2D & /*atPt2d_mm*/, const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const { MITK_WARN<<"Warning! Call of the deprecated function AbstractTransformGeometry::WorldToIndex(point, vec, vec). Use AbstractTransformGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const { m_Plane->WorldToIndex(vec_mm, vec_units); } bool mitk::AbstractTransformGeometry::IsAbove(const mitk::Point3D& pt3d_mm, bool considerBoundingBox) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); Point3D pt3d_ParametricWorld; pt3d_ParametricWorld = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); Point3D pt3d_ParametricUnits; ((BaseGeometry*)m_Plane)->WorldToIndex(pt3d_ParametricWorld, pt3d_ParametricUnits); return (pt3d_ParametricUnits[2] > m_ParametricBoundingBox->GetBounds()[4]); } void mitk::AbstractTransformGeometry::SetVtkAbstractTransform(vtkAbstractTransform* aVtkAbstractTransform) { m_ItkVtkAbstractTransform->SetVtkAbstractTransform(aVtkAbstractTransform); } void mitk::AbstractTransformGeometry::SetPlane(const mitk::PlaneGeometry* aPlane) { if(aPlane!=NULL) { m_Plane = static_cast(aPlane->Clone().GetPointer()); BoundingBox::BoundsArrayType b=m_Plane->GetBoundingBox()->GetBounds(); SetParametricBounds(b); CalculateFrameGeometry(); } else { if(m_Plane.IsNull()) return; m_Plane=NULL; } Modified(); } void mitk::AbstractTransformGeometry::CalculateFrameGeometry() { if((m_Plane.IsNull()) || (m_FrameGeometry.IsNotNull())) return; //@warning affine-transforms and bounding-box should be set by specific sub-classes! SetBounds(m_Plane->GetBoundingBox()->GetBounds()); } void mitk::AbstractTransformGeometry::SetFrameGeometry(const mitk::BaseGeometry* frameGeometry) { if((frameGeometry != NULL) && (frameGeometry->IsValid())) { m_FrameGeometry = static_cast(frameGeometry->Clone().GetPointer()); SetIndexToWorldTransform(m_FrameGeometry->GetIndexToWorldTransform()); SetBounds(m_FrameGeometry->GetBounds()); } else { m_FrameGeometry = NULL; } } unsigned long mitk::AbstractTransformGeometry::GetMTime() const { if(Superclass::GetMTime()GetMTime()) return m_ItkVtkAbstractTransform->GetMTime(); return Superclass::GetMTime(); } void mitk::AbstractTransformGeometry::SetOversampling(mitk::ScalarType oversampling) { if(m_Plane.IsNull()) { itkExceptionMacro(<< "m_Plane is not set."); } mitk::BoundingBox::BoundsArrayType bounds = m_Plane->GetBounds(); bounds[1]*=oversampling; bounds[3]*=oversampling; bounds[5]*=oversampling; SetParametricBounds(bounds); } itk::LightObject::Pointer mitk::AbstractTransformGeometry::InternalClone() const { Self::Pointer newGeometry = new AbstractTransformGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void mitk::AbstractTransformGeometry::SetParametricBounds(const BoundingBox::BoundsArrayType& bounds) { m_ParametricBoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for(pointid=0; pointid<2;++pointid) { unsigned int i; for(i=0; iInsertElement(pointid, p); } m_ParametricBoundingBox->SetPoints(pointscontainer); m_ParametricBoundingBox->ComputeBoundingBox(); this->Modified(); } const mitk::BoundingBox::BoundsArrayType& mitk::AbstractTransformGeometry::GetParametricBounds() const { assert(m_ParametricBoundingBox.IsNotNull()); return m_ParametricBoundingBox->GetBounds(); } mitk::ScalarType mitk::AbstractTransformGeometry::GetParametricExtent(int direction) const { if (direction < 0 || direction>=3) mitkThrow() << "Invalid direction. Must be between either 0, 1 or 2. "; assert(m_ParametricBoundingBox.IsNotNull()); BoundingBoxType::BoundsArrayType bounds = m_ParametricBoundingBox->GetBounds(); return bounds[direction*2+1]-bounds[direction*2]; } diff --git a/Core/Code/DataManagement/mitkAbstractTransformGeometry.h b/Core/Code/DataManagement/mitkAbstractTransformGeometry.h index 514b6195b9..2bda9bc8bb 100644 --- a/Core/Code/DataManagement/mitkAbstractTransformGeometry.h +++ b/Core/Code/DataManagement/mitkAbstractTransformGeometry.h @@ -1,218 +1,218 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #define MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #include #include "mitkPlaneGeometry.h" #include "itkVtkAbstractTransform.h" class vtkAbstractTransform; namespace mitk { //##Documentation //## @brief Describes a geometry defined by an vtkAbstractTransform and a plane //## //## vtkAbstractTransform is the most general transform in vtk (superclass for //## all vtk geometric transformations). It defines an arbitrary 3D transformation, //## i.e., a transformation of 3D space into 3D space. In contrast, //## AbstractTransformGeometry (since it is a subclass of PlaneGeometry) describes a //## 2D manifold in 3D space. The 2D manifold is defined as the manifold that results //## from transforming a rectangle (given in m_Plane as a PlaneGeometry) by the //## vtkAbstractTransform (given in m_VtkAbstractTransform). //## The PlaneGeometry m_Plane is used to define the parameter space. 2D coordinates are //## first mapped by the PlaneGeometry and the resulting 3D coordinates are put into //## the vtkAbstractTransform. //## @note This class is the superclass of concrete geometries. Since there is no //## write access to the vtkAbstractTransform and m_Plane, this class is somehow //## abstract. For full write access from extern, use ExternAbstractTransformGeometry. //## @note The bounds of the PlaneGeometry are used as the parametric bounds. //## @sa ExternAbstractTransformGeometry //## @ingroup Geometry class MITK_CORE_EXPORT AbstractTransformGeometry : public PlaneGeometry { public: mitkClassMacro(AbstractTransformGeometry, PlaneGeometry); itkFactorylessNewMacro(Self) itkCloneMacro(Self) //##Documentation //## @brief Get the vtkAbstractTransform (stored in m_VtkAbstractTransform) virtual vtkAbstractTransform* GetVtkAbstractTransform() const; virtual unsigned long GetMTime() const; //##Documentation //## @brief Get the rectangular area that is used for transformation by //## m_VtkAbstractTransform and therewith defines the 2D manifold described by //## AbstractTransformGeometry itkGetConstObjectMacro(Plane, PlaneGeometry); /** * \brief projects the given point onto the curved plane */ virtual bool Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const; /** * \brief projects a given vector starting from given point onto the curved plane * \warning no satisfiyng implementation existing yet */ virtual bool Project(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const; /** * \brief projects a given vector starting from standard point onto the curved plane * \warning no satisfying implementation existing yet */ virtual bool Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const; virtual bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const; virtual void Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const; virtual bool Map(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const; virtual void Map(const mitk::Point2D & atPt2d_mm, const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const; virtual void IndexToWorld(const mitk::Point2D &pt_units, mitk::Point2D &pt_mm) const; virtual void WorldToIndex(const mitk::Point2D &pt_mm, mitk::Point2D &pt_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 (Point2D) is not used. If possible, please use void IndexToWorld(const mitk::Vector2D& vec_units, mitk::Vector2D& vec_mm) const. //## For further information about coordinates types, please see the Geometry documentation virtual void IndexToWorld(const mitk::Point2D &atPt2d_units, const mitk::Vector2D &vec_units, mitk::Vector2D &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 virtual void IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## @deprecated First parameter (Point2D) is not used. If possible, please use void WorldToIndex(const mitk::Vector2D& vec_mm, mitk::Vector2D& vec_units) const. //## For further information about coordinates types, please see the Geometry documentation virtual void WorldToIndex(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec_mm, mitk::Vector2D &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 virtual void WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const; virtual bool IsAbove(const Point3D& pt3d_mm, bool considerBoundingBox=false) const; virtual mitk::ScalarType GetParametricExtentInMM(int direction) const; - virtual const Transform3D* GetParametricTransform() const; + virtual const itk::Transform* GetParametricTransform() const; //##Documentation //## @brief Change the parametric bounds to @a oversampling times //## the bounds of m_Plane. //## //## The change is done once (immediately). Later changes of the bounds //## of m_Plane will not influence the parametric bounds. (Consequently, //## there is no method to get the oversampling.) virtual void SetOversampling(mitk::ScalarType oversampling); //##Documentation //## @brief Calculates the standard part of a BaseGeometry //## (IndexToWorldTransform and bounding box) around the //## curved geometry. Has to be implemented in subclasses. //## //## \sa SetFrameGeometry virtual void CalculateFrameGeometry(); //##Documentation //## @brief Set the frame geometry which is used as the standard //## part of an BaseGeometry (IndexToWorldTransform and bounding box) //## //## Maybe used as a hint within which the interpolation shall occur //## by concrete sub-classes. //## \sa CalculateFrameGeometry virtual void SetFrameGeometry(const mitk::BaseGeometry* frameGeometry); virtual itk::LightObject::Pointer InternalClone() const; //##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; //##Documentation //## @brief Get the parametric extent //## //## See AbstractTransformGeometry for an example usage of this. mitk::ScalarType GetParametricExtent(int direction) const; protected: AbstractTransformGeometry(); AbstractTransformGeometry(const AbstractTransformGeometry& other); virtual ~AbstractTransformGeometry(); //##Documentation //## @brief Set the vtkAbstractTransform (stored in m_VtkAbstractTransform) //## //## Protected in this class, made public in ExternAbstractTransformGeometry. virtual void SetVtkAbstractTransform(vtkAbstractTransform* aVtkAbstractTransform); //##Documentation //## @brief Set the rectangular area that is used for transformation by //## m_VtkAbstractTransform and therewith defines the 2D manifold described by //## ExternAbstractTransformGeometry //## //## Protected in this class, made public in ExternAbstractTransformGeometry. //## @note The bounds of the PlaneGeometry are used as the parametric bounds. //## @note The PlaneGeometry is cloned, @em not linked/referenced. virtual void SetPlane(const mitk::PlaneGeometry* aPlane); //##Documentation //## @brief The rectangular area that is used for transformation by //## m_VtkAbstractTransform and therewith defines the 2D manifold described by //## AbstractTransformGeometry. mitk::PlaneGeometry::Pointer m_Plane; itk::VtkAbstractTransform::Pointer m_ItkVtkAbstractTransform; mitk::BaseGeometry::Pointer m_FrameGeometry; //##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); mutable mitk::BoundingBox::Pointer m_ParametricBoundingBox; private: virtual void PostInitialize(); }; } // namespace mitk #endif /* MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C */ diff --git a/Core/Code/DataManagement/mitkVector.cpp b/Core/Code/DataManagement/mitkAffineTransform3D.h similarity index 66% copy from Core/Code/DataManagement/mitkVector.cpp copy to Core/Code/DataManagement/mitkAffineTransform3D.h index 3a6f50b86b..70dd84a0b9 100644 --- a/Core/Code/DataManagement/mitkVector.cpp +++ b/Core/Code/DataManagement/mitkAffineTransform3D.h @@ -1,21 +1,31 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include "mitkVector.h" -const mitk::ScalarType mitk::eps = vnl_math::eps*100; -const mitk::ScalarType mitk::sqrteps = vnl_math::sqrteps; -extern const mitk::ScalarType mitk::large = std::numeric_limits::max(); +#ifndef MITKAFFINETRANSFORM3D_H_ +#define MITKAFFINETRANSFORM3D_H_ + + +#include + +namespace mitk +{ + typedef itk::AffineGeometryFrame::TransformType AffineTransform3D; +} + + + +#endif /* MITKAFFINETRANSFORM3D_H_ */ diff --git a/Core/Code/DataManagement/mitkAnnotationProperty.h b/Core/Code/DataManagement/mitkAnnotationProperty.h index 120bb5342e..8a1d6e0275 100644 --- a/Core/Code/DataManagement/mitkAnnotationProperty.h +++ b/Core/Code/DataManagement/mitkAnnotationProperty.h @@ -1,92 +1,92 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKANNOTATIONPROPERTY_H_HEADER_INCLUDED #define MITKANNOTATIONPROPERTY_H_HEADER_INCLUDED #include #include "mitkBaseProperty.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include #include namespace mitk { /** * \brief Property for annotations * \ingroup DataManagement */ class MITK_CORE_EXPORT AnnotationProperty : public BaseProperty { public: mitkClassMacro(AnnotationProperty, BaseProperty); typedef std::string ValueType; itkFactorylessNewMacro(Self) itkCloneMacro(Self) mitkNewMacro2Param( AnnotationProperty, const char *, const Point3D & ); mitkNewMacro2Param( AnnotationProperty, const std::string &, const Point3D & ); mitkNewMacro4Param( AnnotationProperty, const char *, ScalarType, ScalarType, ScalarType ); mitkNewMacro4Param( AnnotationProperty, const std::string &, ScalarType, ScalarType, ScalarType ); itkGetStringMacro( Label ); itkSetStringMacro( Label ); const Point3D &GetPosition() const; void SetPosition( const Point3D &position ); virtual std::string GetValueAsString() const; virtual BaseProperty& operator=(const BaseProperty& other) { return Superclass::operator=(other); } \ using BaseProperty::operator =; protected: std::string m_Label; Point3D m_Position; AnnotationProperty(); AnnotationProperty( const char *label, const Point3D &position ); AnnotationProperty( const std::string &label, const Point3D &position ); AnnotationProperty( const char *label, ScalarType x, ScalarType y, ScalarType z ); AnnotationProperty( const std::string &label, ScalarType x, ScalarType y, ScalarType z ); AnnotationProperty(const AnnotationProperty& other); private: // purposely not implemented AnnotationProperty& operator=(const AnnotationProperty&); itk::LightObject::Pointer InternalClone() const; virtual bool IsEqual(const BaseProperty& property) const; virtual bool Assign(const BaseProperty & property); }; } // namespace mitk #endif /* MITKANNOTATIONPROPERTY_H_HEADER_INCLUDED */ diff --git a/Core/Code/DataManagement/mitkApplicationCursor.h b/Core/Code/DataManagement/mitkApplicationCursor.h index 21a6e40fd6..7145a20e01 100644 --- a/Core/Code/DataManagement/mitkApplicationCursor.h +++ b/Core/Code/DataManagement/mitkApplicationCursor.h @@ -1,109 +1,109 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITK_APPLICATION_CURSOR_H_DEFINED_AND_ALL_IS_GOOD #define MITK_APPLICATION_CURSOR_H_DEFINED_AND_ALL_IS_GOOD #include -#include "mitkVector.h" +#include "mitkNumericTypes.h" namespace mitk { /*! \brief Toolkit specific implementation of mitk::ApplicationCursor For any toolkit, this class has to be sub-classed. One instance of that sub-class has to be registered with mitk::ApplicationCursor. See the (very simple) implmentation of QmitkApplicationCursor for an example. */ class MITK_CORE_EXPORT ApplicationCursorImplementation { public: /// Change the current application cursor virtual void PushCursor(const char* XPM[], int hotspotX, int hotspotY) = 0; /// Change the current application cursor virtual void PushCursor(std::istream&, int hotspotX, int hotspotY) = 0; /// Restore the previous cursor virtual void PopCursor() = 0; /// Get absolute mouse position on screen virtual const Point2I GetCursorPosition() = 0; /// Set absolute mouse position on screen virtual void SetCursorPosition(const Point2I&) = 0; virtual ~ApplicationCursorImplementation() {} protected: private: }; /*! \brief Allows to override the application's cursor. Base class for classes that allow to override the applications cursor with context dependent cursors. Accepts cursors in the XPM format. The behaviour is stack-like. You can push your cursor on top of the stack and later pop it to reset the cursor to its former state. This is mimicking Qt's Application::setOverrideCuror() behaviour, but should be ok for most cases where you want to switch a cursor. */ class MITK_CORE_EXPORT ApplicationCursor { public: /// This class is a singleton. static ApplicationCursor* GetInstance(); /// To be called by a toolkit specific ApplicationCursorImplementation. static void RegisterImplementation(ApplicationCursorImplementation* implementation); /// Change the current application cursor void PushCursor(const char* XPM[], int hotspotX = -1, int hotspotY = -1); /// Change the current application cursor void PushCursor(std::istream&, int hotspotX = -1, int hotspotY = -1); /// Restore the previous cursor void PopCursor(); /// Get absolute mouse position on screen /// \return (-1, -1) if querying mouse position is not possible const Point2I GetCursorPosition(); /// Set absolute mouse position on screen void SetCursorPosition(const Point2I&); protected: /// Purposely hidden - singleton ApplicationCursor(); private: static ApplicationCursorImplementation* m_Implementation; }; } // namespace #endif diff --git a/Core/Code/DataManagement/mitkArray.h b/Core/Code/DataManagement/mitkArray.h new file mode 100644 index 0000000000..fb9561b71e --- /dev/null +++ b/Core/Code/DataManagement/mitkArray.h @@ -0,0 +1,149 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKARRAY_H_ +#define MITKARRAY_H_ + +#include + +#include "mitkNumericConstants.h" +#include "mitkEqual.h" + + +namespace mitk { + + + /** + * Methods to copy from itk::FixedArray s (like mitk::Vector and mitk::Point) into ArrayTypes and vice versa. + * ArrayTypes here are all types who implement operator[]. + * The two templated methods were made free floating so you may specialize them + * for your concrete type. + */ + + + /** + * @brief Copies elements of an array to this Vector + * @param[in] array the array whose values will be copied into the Vector. Must be of a type which overrides the [] operator + * @param[out] toArray the FixedArrray (e.g., mitk::Vector or mitk::Point) which should hold the elements of array. + * @attention array must be of dimension NVectorDimension! + * @attention this method implicitly converts between data types. + */ + template + void FillArray(itk::FixedArray& toArray, const ArrayType& array) + { + itk::FixedArray vectorOrPoint; + for (unsigned short int var = 0; var < NVectorDimension; ++var) + { + toArray[var] = array[var]; + } + } + + /** + * @brief Copies elements of an array to this Vector + * @param[in] array the array whose values will be copied into the Vector. Must be of a type which overrides the [] operator + * @param return the FixedArrray (e.g., mitk::Vector or mitk::Point) which should hold the elements of array. + * @attention array must be of dimension NVectorDimension! + * @attention this method implicitly converts between data types. + */ + template + itk::FixedArray FillArray(const ArrayType& array) + { + itk::FixedArray vectorOrPoint; + + mitk::FillArray(vectorOrPoint, array); + + return vectorOrPoint; + } + + /** + * @brief Copies the elements of this into an array + * @param[in] vectorOrPoint the itk::FixedArray which shall be copied into the array. Can e.g. be of type mitk::Vector or mitk::Point + * @param[out] array the array which will hold the elements. Must be of a type which overrides the [] operator. + * @attention array must be of dimension NVectorDimension! + * @attention this method implicitly converts between data types. + */ + template + void ToArray(ArrayType& array, const itk::FixedArray& vectorOrPoint) + { + for (unsigned short int var = 0; var < NVectorDimension; ++var) + { + array[var] = vectorOrPoint[var]; + } + } + + + /** + * @brief Copies the elements of this into an array + * @param[in] vectorOrPoint the itk::FixedArray which shall be copied into the array. Can e.g. be of type mitk::Vector or mitk::Point + * @return the array which will hold the elements. Must be of a type which overrides the [] operator. + * @attention array must be of dimension NVectorDimension! + * @attention this method implicitly converts between data types. + */ + template + ArrayType ToArray(const itk::FixedArray& vectorOrPoint) + { + ArrayType result; + + mitk::ToArray(result, vectorOrPoint); + + return result; + } + + + // The FillVector3D and FillVector4D methods are implemented for all common array types here + + template + inline void FillVector3D(Tout& out, mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z) + { + out[0] = x; + out[1] = y; + out[2] = z; + } + + template + inline void FillVector4D(Tout& out, mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z, mitk::ScalarType t) + { + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = t; + } + + + + /** + * Compares two ArrayTypes of size size. + * ArrayTypes are all Objects/Types who have a [] operator. Pay attention not to set size higher + * than the actual size of the ArrayType as this will lead to unexpected results. + */ + template + inline bool EqualArray(TArrayType1& arrayType1, TArrayType2& arrayType2, int size, ScalarType eps = mitk::eps, bool verbose = false) + { + bool isEqual = true; + for (int var = 0; var < size; ++var) + { + isEqual = isEqual && Equal(arrayType1[var], arrayType2[var], eps); + } + + ConditionalOutputOfDifference(arrayType1, arrayType2, eps, verbose, isEqual); + + return isEqual; + } + +} + + +#endif /* MITKARRAY_H_ */ diff --git a/Core/Code/DataManagement/mitkBaseGeometry.h b/Core/Code/DataManagement/mitkBaseGeometry.h index 60b518418b..f3673c08b7 100644 --- a/Core/Code/DataManagement/mitkBaseGeometry.h +++ b/Core/Code/DataManagement/mitkBaseGeometry.h @@ -1,759 +1,759 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef BaseGeometry_H_HEADER_INCLUDED #define BaseGeometry_H_HEADER_INCLUDED #include #include #include "mitkOperationActor.h" #include -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include #include #include "itkScalableAffineTransform.h" #include class vtkMatrix4x4; class vtkMatrixToLinearTransform; class vtkLinearTransform; namespace mitk { //##Documentation //## @brief Standard 3D-BoundingBox typedef //## //## Standard 3D-BoundingBox typedef to get rid of template arguments (3D, type). typedef itk::BoundingBox BoundingBox; //##Documentation //## @brief Standard typedef for time-bounds typedef itk::FixedArray TimeBounds; typedef itk::FixedArray FixedArrayType; typedef itk::AffineGeometryFrame AffineGeometryFrame3D; //##Documentation //## @brief BaseGeometry Describes the geometry of a data object //## //## The class holds //## \li a bounding box which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels), to be accessed by //## GetBoundingBox() //## \li a transform to convert intrinsic coordinates into a //## world-coordinate system with coordinates in millimeters //## and milliseconds (all are floating point values), to //## be accessed by GetIndexToWorldTransform() //## \li an origin and spacing to define the geometry //## //## BaseGeometry and its sub-classes allow converting between //## intrinsic coordinates (called index or unit coordinates) //## and world-coordinates (called world or mm coordinates), //## e.g. WorldToIndex. //## In case you need integer index coordinates, provide an //## mitk::Index3D (or itk::Index) as target variable to //## WorldToIndex, otherwise you will get a continuous index //## (floating point values). //## //## An important sub-class is SlicedGeometry3D, which descibes //## data objects consisting of slices, e.g., objects of type Image. //## Conversions between world coordinates (in mm) and unit coordinates //## (e.g., pixels in the case of an Image) can be performed. //## //## For more information on related classes, see \ref Geometry. //## //## BaseGeometry instances referring to an Image need a slightly //## different definition of corners, see SetImageGeometry. This //## is usualy automatically called by Image. //## //## BaseGeometry have to be initialized in the method GenerateOutputInformation() //## of BaseProcess (or CopyInformation/ UpdateOutputInformation of BaseData, //## if possible, e.g., by analyzing pic tags in Image) subclasses. See also //## itk::ProcessObject::GenerateOutputInformation(), //## itk::DataObject::CopyInformation() and //## itk::DataObject::UpdateOutputInformation(). //## //## At least, it can return the bounding box of the data object. //## //## The BaseGeometry class is an abstract class. The most simple implementation //## is the sublass Geometry3D. //## //## Rule: everything is in mm (ms) if not stated otherwise. //## @ingroup Geometry class MITK_CORE_EXPORT BaseGeometry : public itk::Object, public OperationActor { public: mitkClassMacro(BaseGeometry, itk::Object); // ********************************** TypeDef ********************************** typedef itk::ScalableAffineTransform TransformType; typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::BoundsArrayType BoundsArrayType; typedef BoundingBoxType::Pointer BoundingBoxPointer; // ********************************** Origin, Spacing ********************************** //##Documentation //## @brief Get the origin, e.g. the upper-left corner of the plane const Point3D& GetOrigin() const; //##Documentation //## @brief Set the origin, i.e. the upper-left corner of the plane //## void SetOrigin(const Point3D& origin); //##Documentation //## @brief Get the spacing (size of a pixel). //## itkGetConstReferenceMacro(Spacing, mitk::Vector3D); //##Documentation //## @brief Set the spacing (m_Spacing). //## //##The spacing is also changed in the IndexToWorldTransform. void SetSpacing(const mitk::Vector3D& aSpacing, bool enforceSetSpacing = false); //##Documentation //## @brief Get the origin as VnlVector //## //## \sa GetOrigin VnlVector GetOriginVnl() const; // ********************************** other functions ********************************** //##Documentation //## @brief Get the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkGetConstMacro(FrameOfReferenceID, unsigned int); //##Documentation //## @brief Set the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkSetMacro(FrameOfReferenceID, unsigned int); itkGetConstMacro(IndexToWorldTransformLastModified, unsigned long); //##Documentation //## @brief Overload of function Modified() to prohibit several calls of Modified() using the ModifiedLock class. //## //## For the use of Modified(), see class ModifiedLock. void Modified() const; friend class ModifiedLock; //##Documentation //## @brief Is this BaseGeometry in a state that is valid? //## //## This function returns always true in the BaseGeometry class. Other implementations are possible in subclasses. virtual bool IsValid() const; // ********************************** Initialize ********************************** //##Documentation //## @brief Initialize the BaseGeometry void Initialize(); void InitializeGeometry(Self * newGeometry) const; // ********************************** Transformations Set/Get ********************************** // 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. The spacing of the new transform is //## copied to m_spacing. void SetIndexToWorldTransform(mitk::AffineTransform3D* transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4.The spacing of //## the new transform is copied to m_spacing. //## \sa SetIndexToWorldTransform virtual void SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4* vtkmatrix); //## Get the IndexToWorldTransform itkGetConstObjectMacro(IndexToWorldTransform, AffineTransform3D); itkGetObjectMacro(IndexToWorldTransform, AffineTransform3D); //## Get the Vtk Matrix which describes the transform. vtkMatrix4x4* GetVtkMatrix(); //##Documentation //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform vtkLinearTransform* GetVtkTransform() const; //##Documentation //## @brief Set the transform to identity, the spacing to 1 and origin to 0 //## virtual void SetIdentity(); // ********************************** Transformations ********************************** //##Documentation //## @brief Compose new IndexToWorldTransform with a given transform. //## //## This method composes m_IndexToWorldTransform with another transform, //## modifying self to be the composition of self and other. //## If the argument pre is true, then other is precomposed with self; //## that is, the resulting transformation consists of first applying //## other to the source, followed by self. If pre is false or omitted, //## then other is post-composed with self; that is the resulting //## transformation consists of first applying self to the source, //## followed by other. //## This method also changes m_spacing. void Compose( const BaseGeometry::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. void Compose( const vtkMatrix4x4 * vtkmatrix, bool pre = 0 ); //##Documentation //## @brief Translate the origin by a vector //## void Translate(const Vector3D& vector); //##Documentation //##@brief executes affine operations (translate, rotate, scale) virtual void ExecuteOperation(Operation* operation); //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (continuous!) index coordinates //## \warning If you need (discrete) integer index coordinates (e.g., for iterating easily over an image), //## use WorldToIndex(const mitk::Point3D& pt_mm, itk::Index &index). //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point3D& pt_mm, mitk::Point3D& pt_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Vector3D& vec_mm, mitk::Vector3D& vec_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (discrete!) index coordinates. //## This method rounds to integer indices! //## For further information about coordinates types, please see the Geometry documentation template void WorldToIndex(const mitk::Point3D& pt_mm, itk::Index &index) const { typedef itk::Index IndexType; mitk::Point3D pt_units; this->WorldToIndex(pt_mm, pt_units); int i, dim=index.GetIndexDimension(); if(dim>3) { index.Fill(0); dim=3; } for(i=0;i( pt_units[i] ); } } //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Vector3D& vec_units, mitk::Vector3D& vec_mm) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point3D& pt_units, mitk::Point3D& pt_mm) const; //##Documentation //## @brief Convert (discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation template void IndexToWorld(const itk::Index &index, mitk::Point3D& pt_mm ) const { mitk::Point3D pt_units; pt_units.Fill(0); int i, dim=index.GetIndexDimension(); if(dim>3) { dim=3; } for(i=0;i void ItkPhysicalPointToWorld(const itk::Point& itkPhysicalPoint, mitk::Point3D& pt_mm) const { mitk::vtk2itk(itkPhysicalPoint, pt_mm); } //##Documentation //## @brief Deprecated for use with ITK version 3.10 or newer. //## Convert world coordinates (in mm) of a \em point to //## ITK physical coordinates (in mm, but without a possible rotation) //## //## This method is useful if you have want to access an mitk::Image //## via an itk::Image. ITK v3.8 and older did not support rotated (tilted) //## images, i.e., ITK images are always parallel to the coordinate axes. //## When accessing a (possibly rotated) mitk::Image via an itk::Image //## the rotational part of the transformation in the BaseGeometry is //## simply discarded; in other word: only the origin and spacing is //## used by ITK, not the complete matrix available in MITK. //## With WorldToItkPhysicalPoint you can convert an MITK world //## coordinate (including the rotation) into a coordinate that //## can be used with the ITK image as a ITK physical coordinate //## (excluding the rotation). template void WorldToItkPhysicalPoint(const mitk::Point3D& pt_mm, itk::Point& itkPhysicalPoint) const { mitk::vtk2itk(pt_mm, itkPhysicalPoint); } // ********************************** BoundingBox ********************************** /** Get the bounding box */ itkGetConstObjectMacro(BoundingBox, BoundingBoxType); //##Documentation //## @brief Get the time bounds (in ms) //itkGetConstReferenceMacro(TimeBounds, TimeBounds); // a bit of a misuse, but we want only doxygen to see the following: #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get bounding box (in index/unit coordinates) itkGetConstObjectMacro(BoundingBox, BoundingBoxType); //##Documentation //## @brief Get bounding box (in index/unit coordinates) as a BoundsArrayType const BoundsArrayType GetBounds() const; #endif const BoundsArrayType GetBounds() const; //##Documentation //## \brief Set the bounding box (in index/unit coordinates) //## //## Only possible via the BoundsArray to make clear that a //## copy of the bounding-box is stored, not a reference to it. void SetBounds(const BoundsArrayType& bounds); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a float array void SetFloatBounds(const float bounds[6]); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a double array void SetFloatBounds(const double bounds[6]); //##Documentation //## @brief Get a VnlVector along bounding-box in the specified //## @a direction, length is spacing //## //## \sa GetAxisVector VnlVector GetMatrixColumn(unsigned int direction) const; //##Documentation //## @brief Calculates a bounding-box around the geometry relative //## to a coordinate system defined by a transform //## mitk::BoundingBox::Pointer CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D* transform) const; //##Documentation //## @brief Set the time bounds (in ms) //void SetTimeBounds(const TimeBounds& timebounds); // ********************************** Geometry ********************************** #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get the extent of the bounding box (in index/unit coordinates) //## //## To access the extent in mm use GetExtentInMM ScalarType GetExtent(unsigned int direction) const; #endif /** Get the extent of the bounding box */ ScalarType GetExtent(unsigned int direction) const; //##Documentation //## @brief Get the extent of the bounding-box in the specified @a direction in mm //## //## Equals length of GetAxisVector(direction). ScalarType GetExtentInMM(int direction) const; //##Documentation //## @brief Get vector along bounding-box in the specified @a direction in mm //## //## The length of the vector is the size of the bounding-box in the //## specified @a direction in mm //## \sa GetMatrixColumn Vector3D GetAxisVector(unsigned int direction) const; //##Documentation //## @brief Checks, if the given geometry can be converted to 2D without information loss //## e.g. when a 2D image is saved, the matrix is usually cropped to 2x2, and when you load it back to MITK //## it will be filled with standard values. This function checks, if information would be lost during this //## procedure virtual bool Is2DConvertable(); //##Documentation //## @brief Get the center of the bounding-box in mm //## Point3D GetCenter() const; //##Documentation //## @brief Get the squared length of the diagonal of the bounding-box in mm //## double GetDiagonalLength2() const; //##Documentation //## @brief Get the length of the diagonal of the bounding-box in mm //## double GetDiagonalLength() const; //##Documentation //## @brief Get the position of the corner number \a id (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(int id) const; //##Documentation //## @brief Get the position of a corner (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(bool xFront=true, bool yFront=true, bool zFront=true) const; //##Documentation //## @brief Set the extent of the bounding-box in the specified @a direction in mm //## //## @note This changes the matrix in the transform, @a not the bounds, which are given in units! void SetExtentInMM(int direction, ScalarType extentInMM); //##Documentation //## @brief Test whether the point \a p (world coordinates in mm) is //## inside the bounding box bool IsInside(const mitk::Point3D& p) const; //##Documentation //## @brief Test whether the point \a p ((continous!)index coordinates in units) is //## inside the bounding box bool IsIndexInside(const mitk::Point3D& index) const; //##Documentation //## @brief Convenience method for working with ITK indices template bool IsIndexInside(const itk::Index &index) const { int i, dim=index.GetIndexDimension(); Point3D pt_index; pt_index.Fill(0); for ( i = 0; i < dim; ++i ) { pt_index[i] = index[i]; } return IsIndexInside(pt_index); } // ********************************* Image Geometry ******************************** //##Documentation //## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to change the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and changes the origin respectively. virtual void ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ); //##Documentation //## @brief Is this an ImageGeometry? //## //## For more information, see SetImageGeometry itkGetConstMacro(ImageGeometry, bool); //##Documentation //## @brief Define that this BaseGeometry is refering to an Image //## //## A geometry referring to an Image needs a slightly different //## definition of the position of the corners (see GetCornerPoint). //## The position of a voxel is defined by the position of its center. //## If we would use the origin (position of the (center of) the first //## voxel) as a corner and display this point, it would seem to be //## \em not at the corner but a bit within the image. Even worse for //## the opposite corner of the image: here the corner would appear //## outside the image (by half of the voxel diameter). Thus, we have //## to correct for this and to be able to do that, we need to know //## that the BaseGeometry is referring to an Image. itkSetMacro(ImageGeometry, bool); itkBooleanMacro(ImageGeometry); protected: // ********************************** Constructor ********************************** BaseGeometry(); BaseGeometry(const BaseGeometry& other); virtual ~BaseGeometry(); //##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 itk::LightObject::Pointer InternalClone() const =0; virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; void BackTransform(const mitk::Point3D& in, mitk::Point3D& out) const; //Without redundant parameter Point3D void BackTransform(const mitk::Vector3D& in, mitk::Vector3D& out) const; //##Documentation //## @brief Deprecated void BackTransform(const mitk::Point3D& at, const mitk::Vector3D& in, mitk::Vector3D& out) const; //##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(); static const std::string GetTransformAsString( TransformType* transformType ); itkGetConstMacro(NDimensions, unsigned int); bool IsBoundingBoxNull() const; bool IsIndexToWorldTransformNull() const; //##Documentation //## @brief Intern functions to assure a consistent behaviour of SetSpacing. void _SetSpacing(const mitk::Vector3D& aSpacing, bool enforceSetSpacing = false); private: //##Documentation //## @brief Pre- and Post-functions are empty in BaseGeometry //## //## These virtual functions allow for a different beahiour in subclasses. virtual void PreSetBounds(const BoundsArrayType& bounds); virtual void PostInitialize(); virtual void PostInitializeGeometry(Self * newGeometry) const; virtual void PostSetExtentInMM(int direction, ScalarType extentInMM); //virtual void PostSetTimeBounds(const TimeBounds& timebounds); virtual void PreSetIndexToWorldTransform(mitk::AffineTransform3D* transform); virtual void PostSetIndexToWorldTransform(mitk::AffineTransform3D* transform); virtual void PreSetSpacing(const mitk::Vector3D& aSpacing); // ********************************** Variables ********************************** //##Documentation //## @brief Spacing, measurement of the resolution //## mitk::Vector3D m_Spacing; //##Documentation //## @brief Index to World Transform, contains a transformation matrix to convert //## points from indes coordinates to world coordinates (mm). The Spacing is included in this variable. AffineTransform3D::Pointer m_IndexToWorldTransform; //##Documentation //## @brief Bounding Box, which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels) BoundingBoxPointer m_BoundingBox; vtkMatrixToLinearTransform* m_VtkIndexToWorldTransform; vtkMatrix4x4* m_VtkMatrix; unsigned int m_FrameOfReferenceID; //mitk::TimeBounds m_TimeBounds; static const unsigned int m_NDimensions = 3; mutable TransformType::Pointer m_InvertedTransform; mutable unsigned long m_IndexToWorldTransformLastModified; //##Documentation //## @brief Origin, i.e. upper-left corner of the plane //## Point3D m_Origin; bool m_ImageGeometry; //##Documentation //## @brief ModifiedLockFlag is used to prohibit the call of Modified() //## //## For the use of this Flag, see class ModifiedLock. This flag should only be set //## by the ModifiedLock class! bool m_ModifiedLockFlag; //##Documentation //## @brief ModifiedcalledFlag is used to collect calls of Modified(). //## //## For the use of this Flag, see class ModifiedLock. This flag should only be set //## by the Modified() function! mutable bool m_ModifiedCalledFlag; }; // ********************************** Equal Functions ********************************** // // Static compare functions mainly for testing // /** * @brief Equal A function comparing two geometries for beeing identical. * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::mitk::BaseGeometry& g1, const mitk::BaseGeometry& g2) instead. * * @ingroup MITKTestingAPI * * The function compares the spacing, origin, axisvectors, extents, the matrix of the * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * If you want to use different tolarance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ DEPRECATED( MITK_CORE_EXPORT bool Equal(const mitk::BaseGeometry* leftHandSide, const mitk::BaseGeometry* rightHandSide, ScalarType eps, bool verbose)); /** * @brief Equal A function comparing two geometries for beeing identical. * * @ingroup MITKTestingAPI * * The function compares the spacing, origin, axisvectors, extents, the matrix of the * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * If you want to use different tolarance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITK_CORE_EXPORT bool Equal(const mitk::BaseGeometry& leftHandSide, const mitk::BaseGeometry& rightHandSide, ScalarType eps, bool verbose); /** * @brief Equal A function comparing two transforms (TransformType) for beeing identical. * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::mitk::BaseGeometry::TransformType& t1, const mitk::BaseGeometry::TransformType& t2) instead. * * @ingroup MITKTestingAPI * * The function compares the IndexToWorldTransform (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ DEPRECATED( MITK_CORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType *leftHandSide, const mitk::BaseGeometry::TransformType *rightHandSide, ScalarType eps, bool verbose)); /** * @brief Equal A function comparing two transforms (TransformType) for beeing identical. * * @ingroup MITKTestingAPI * * The function compares the IndexToWorldTransform (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITK_CORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType& leftHandSide, const mitk::BaseGeometry::TransformType& rightHandSide, ScalarType eps, bool verbose); /** * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::mitk::BaseGeometry::BoundingBoxType& b1, const mitk::BaseGeometry::BoundingBoxType& b2) instead. * * @ingroup MITKTestingAPI * * The function compares the bounds (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ DEPRECATED( MITK_CORE_EXPORT bool Equal( const mitk::BaseGeometry::BoundingBoxType *leftHandSide, const mitk::BaseGeometry::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose)); /** * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. * * @ingroup MITKTestingAPI * * The function compares the bounds (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITK_CORE_EXPORT bool Equal( const mitk::BaseGeometry::BoundingBoxType& leftHandSide, const mitk::BaseGeometry::BoundingBoxType& rightHandSide, ScalarType eps, bool verbose); } // namespace mitk #endif /* BaseGeometry_H_HEADER_INCLUDED */ diff --git a/Core/Code/DataManagement/mitkClippingProperty.h b/Core/Code/DataManagement/mitkClippingProperty.h index e7e5685626..9d61e0a40b 100644 --- a/Core/Code/DataManagement/mitkClippingProperty.h +++ b/Core/Code/DataManagement/mitkClippingProperty.h @@ -1,96 +1,96 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKCLIPPINGPROPERTY_H_HEADER_INCLUDED #define MITKCLIPPINGPROPERTY_H_HEADER_INCLUDED #include #include "mitkBaseProperty.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include #include namespace mitk { #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4522) #endif /** * \brief Property for clipping datasets; currently only * clipping planes are possible * \ingroup DataManagement */ class MITK_CORE_EXPORT ClippingProperty : public BaseProperty { public: mitkClassMacro(ClippingProperty, BaseProperty); typedef std::string ValueType; itkFactorylessNewMacro(Self) itkCloneMacro(Self) mitkNewMacro2Param( ClippingProperty, const Point3D &, const Vector3D & ); bool GetClippingEnabled() const; void SetClippingEnabled( bool enabled ); const Point3D &GetOrigin() const; void SetOrigin( const Point3D &origin ); const Vector3D &GetNormal() const; void SetNormal( const Vector3D &normal ); virtual std::string GetValueAsString() const; using BaseProperty::operator =; protected: bool m_ClippingEnabled; Point3D m_Origin; Vector3D m_Normal; ClippingProperty(); ClippingProperty(const ClippingProperty& other); ClippingProperty( const Point3D &origin, const Vector3D &normal ); private: // purposely not implemented ClippingProperty& operator=(const ClippingProperty&); virtual bool IsEqual(const BaseProperty& property) const; virtual bool Assign(const BaseProperty& property); virtual itk::LightObject::Pointer InternalClone() const; }; #ifdef _MSC_VER # pragma warning(pop) #endif } // namespace mitk #endif /* MITKCLIPPINGPROPERTY_H_HEADER_INCLUDED */ diff --git a/Core/Code/DataManagement/mitkDataStorage.cpp b/Core/Code/DataManagement/mitkDataStorage.cpp index 9d6f8e14a5..9ca5dbda84 100644 --- a/Core/Code/DataManagement/mitkDataStorage.cpp +++ b/Core/Code/DataManagement/mitkDataStorage.cpp @@ -1,502 +1,502 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDataStorage.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkGroupTagProperty.h" #include "mitkImage.h" #include "itkMutexLockHolder.h" #include "itkCommand.h" mitk::DataStorage::DataStorage() : itk::Object() , m_BlockNodeModifiedEvents(false) { } mitk::DataStorage::~DataStorage() { ///// we can not call GetAll() in destructor, because it is implemented in a subclass //SetOfObjects::ConstPointer all = this->GetAll(); //for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) // this->RemoveListeners(it->Value()); //m_NodeModifiedObserverTags.clear(); //m_NodeDeleteObserverTags.clear(); } void mitk::DataStorage::Add(mitk::DataNode* node, mitk::DataNode* parent) { mitk::DataStorage::SetOfObjects::Pointer parents = mitk::DataStorage::SetOfObjects::New(); if (parent != NULL) //< Return empty set if parent is null parents->InsertElement(0, parent); this->Add(node, parents); } void mitk::DataStorage::Remove(const mitk::DataStorage::SetOfObjects* nodes) { if (nodes == NULL) return; for (mitk::DataStorage::SetOfObjects::ConstIterator it = nodes->Begin(); it != nodes->End(); it++) this->Remove(it.Value()); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::GetSubset(const NodePredicateBase* condition) const { mitk::DataStorage::SetOfObjects::ConstPointer result = this->FilterSetOfObjects(this->GetAll(), condition); return result; } mitk::DataNode* mitk::DataStorage::GetNamedNode(const char* name) const { if (name == NULL) return NULL; mitk::StringProperty::Pointer s(mitk::StringProperty::New(name)); mitk::NodePredicateProperty::Pointer p = mitk::NodePredicateProperty::New("name", s); mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(p); if (rs->Size() >= 1) return rs->GetElement(0); else return NULL; } mitk::DataNode* mitk::DataStorage::GetNode(const NodePredicateBase* condition) const { if (condition == NULL) return NULL; mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(condition); if (rs->Size() >= 1) return rs->GetElement(0); else return NULL; } mitk::DataNode* mitk::DataStorage::GetNamedDerivedNode(const char* name, const mitk::DataNode* sourceNode, bool onlyDirectDerivations) const { if (name == NULL) return NULL; mitk::StringProperty::Pointer s(mitk::StringProperty::New(name)); mitk::NodePredicateProperty::Pointer p = mitk::NodePredicateProperty::New("name", s); mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetDerivations(sourceNode, p, onlyDirectDerivations); if (rs->Size() >= 1) return rs->GetElement(0); else return NULL; } void mitk::DataStorage::PrintSelf(std::ostream& os, itk::Indent indent) const { //Superclass::PrintSelf(os, indent); mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetAll(); os << indent << "DataStorage " << this << " is managing " << all->Size() << " objects. List of objects:" << std::endl; for (mitk::DataStorage::SetOfObjects::ConstIterator allIt = all->Begin(); allIt != all->End(); allIt++) { std::string name; allIt.Value()->GetName(name); std::string datatype; if (allIt.Value()->GetData() != NULL) datatype = allIt.Value()->GetData()->GetNameOfClass(); os << indent << " " << allIt.Value().GetPointer() << "<" << datatype << ">: " << name << std::endl; mitk::DataStorage::SetOfObjects::ConstPointer parents = this->GetSources(allIt.Value()); if (parents->Size() > 0) { os << indent << " Direct sources: "; for (mitk::DataStorage::SetOfObjects::ConstIterator parentIt = parents->Begin(); parentIt != parents->End(); parentIt++) os << parentIt.Value().GetPointer() << ", "; os << std::endl; } mitk::DataStorage::SetOfObjects::ConstPointer derivations = this->GetDerivations(allIt.Value()); if (derivations->Size() > 0) { os << indent << " Direct derivations: "; for (mitk::DataStorage::SetOfObjects::ConstIterator derivationIt = derivations->Begin(); derivationIt != derivations->End(); derivationIt++) os << derivationIt.Value().GetPointer() << ", "; os << std::endl; } } os << std::endl; } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::FilterSetOfObjects(const SetOfObjects* set, const NodePredicateBase* condition) const { if (set == NULL) return NULL; mitk::DataStorage::SetOfObjects::Pointer result = mitk::DataStorage::SetOfObjects::New(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = set->Begin(); it != set->End(); it++) if (condition == NULL || condition->CheckNode(it.Value()) == true) //alway copy the set, otherwise the iterator in mitk::DataStorage::Remove() will crash result->InsertElement(result->Size(), it.Value()); return mitk::DataStorage::SetOfObjects::ConstPointer(result); } const mitk::DataNode::GroupTagList mitk::DataStorage::GetGroupTags() const { DataNode::GroupTagList result; SetOfObjects::ConstPointer all = this->GetAll(); if (all.IsNull()) return result; for (mitk::DataStorage::SetOfObjects::ConstIterator nodeIt = all->Begin(); nodeIt != all->End(); nodeIt++) // for each node { mitk::PropertyList* pl = nodeIt.Value()->GetPropertyList(); for (mitk::PropertyList::PropertyMap::const_iterator propIt = pl->GetMap()->begin(); propIt != pl->GetMap()->end(); propIt++) if (dynamic_cast(propIt->second.GetPointer()) != NULL) result.insert(propIt->first); } return result; } void mitk::DataStorage::EmitAddNodeEvent(const mitk::DataNode* node) { AddNodeEvent.Send(node); } void mitk::DataStorage::EmitRemoveNodeEvent(const mitk::DataNode* node) { RemoveNodeEvent.Send(node); } void mitk::DataStorage::OnNodeInteractorChanged( itk::Object *caller, const itk::EventObject& ) { const mitk::DataNode* _Node = dynamic_cast(caller); if(_Node) { InteractorChangedNodeEvent.Send( _Node ); } } void mitk::DataStorage::OnNodeModifiedOrDeleted( const itk::Object *caller, const itk::EventObject &event ) { if( m_BlockNodeModifiedEvents ) return; const mitk::DataNode* _Node = dynamic_cast(caller); if(_Node) { const itk::ModifiedEvent* modEvent = dynamic_cast(&event); if(modEvent) ChangedNodeEvent.Send(_Node); else DeleteNodeEvent.Send(_Node); } } void mitk::DataStorage::AddListeners( const mitk::DataNode* _Node ) { itk::MutexLockHolder locked(m_MutexOne); // node must not be 0 and must not be yet registered mitk::DataNode* NonConstNode = const_cast(_Node); if(_Node && m_NodeModifiedObserverTags .find(NonConstNode) == m_NodeModifiedObserverTags.end()) { itk::MemberCommand::Pointer nodeModifiedCommand = itk::MemberCommand::New(); nodeModifiedCommand->SetCallbackFunction(this , &mitk::DataStorage::OnNodeModifiedOrDeleted); m_NodeModifiedObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand); itk::MemberCommand::Pointer interactorChangedCommand = itk::MemberCommand::New(); interactorChangedCommand->SetCallbackFunction(this, &mitk::DataStorage::OnNodeInteractorChanged); m_NodeInteractorChangedObserverTags[NonConstNode] = NonConstNode->AddObserver( mitk::DataNode::InteractorChangedEvent(), interactorChangedCommand); // add itk delete listener on datastorage itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction(this, &mitk::DataStorage::OnNodeModifiedOrDeleted); // add observer m_NodeDeleteObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::DeleteEvent(), deleteCommand); } } void mitk::DataStorage::RemoveListeners( const mitk::DataNode* _Node ) { itk::MutexLockHolder locked(m_MutexOne) ; // node must not be 0 and must be registered mitk::DataNode* NonConstNode = const_cast(_Node); if(_Node && m_NodeModifiedObserverTags .find(NonConstNode) != m_NodeModifiedObserverTags.end()) { // const cast is bad! but sometimes it is necessary. removing an observer does not really // touch the internal state NonConstNode->RemoveObserver(m_NodeModifiedObserverTags .find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeDeleteObserverTags .find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeInteractorChangedObserverTags .find(NonConstNode)->second); m_NodeModifiedObserverTags.erase(NonConstNode); m_NodeDeleteObserverTags.erase(NonConstNode); m_NodeInteractorChangedObserverTags.erase(NonConstNode); } } mitk::TimeGeometry::Pointer mitk::DataStorage::ComputeBoundingGeometry3D( const SetOfObjects* input, const char* boolPropertyKey, mitk::BaseRenderer* renderer, const char* boolPropertyKey2) const { if (input == NULL) throw std::invalid_argument("DataStorage: input is invalid"); BoundingBox::PointsContainer::Pointer pointscontainer=BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid=0; Point3D point; Vector3D minSpacing; - minSpacing.Fill(ScalarTypeNumericTraits::max()); + minSpacing.Fill(itk::NumericTraits::max()); ScalarType stmin, stmax; - stmin= ScalarTypeNumericTraits::NonpositiveMin(); - stmax= ScalarTypeNumericTraits::max(); + stmin= itk::NumericTraits::NonpositiveMin(); + stmax= itk::NumericTraits::max(); ScalarType minimalIntervallSize = stmax; ScalarType minimalTime = stmax; ScalarType maximalTime = 0; // Needed for check of zero bounding boxes mitk::ScalarType nullpoint[]={0,0,0,0,0,0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); for (SetOfObjects::ConstIterator it = input->Begin(); it != input->End(); ++it) { DataNode::Pointer node = it->Value(); if((node.IsNotNull()) && (node->GetData() != NULL) && (node->GetData()->IsEmpty()==false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer) ) { const TimeGeometry* timeGeometry = node->GetData()->GetUpdatedTimeGeometry(); if (timeGeometry != NULL ) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = timeGeometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for(i=0; i<8; ++i) { point = timeGeometry->GetCornerPointInWorld(i); if(point[0]*point[0]+point[1]*point[1]+point[2]*point[2] < large) pointscontainer->InsertElement( pointid++, point); else { itkGenericOutputMacro( << "Unrealistically distant corner point encountered. Ignored. Node: " << node ); } } try { // time bounds // iterate over all time steps // Attention: Objects with zero bounding box are not respected in time bound calculation for (TimeStepType i=0; iCountTimeSteps(); i++) { Vector3D spacing = node->GetData()->GetGeometry(i)->GetSpacing(); for (int axis = 0; axis < 3; ++ axis) { if (spacing[axis] < minSpacing[axis]) minSpacing[axis] = spacing[axis]; } const TimeBounds & curTimeBounds = node->GetData()->GetTimeGeometry()->GetTimeBounds(i); // get the minimal time of all objects in the DataStorage if ((curTimeBounds[0]stmin)) { minimalTime=curTimeBounds[0]; } // get the maximal time of all objects in the DataStorage if ((curTimeBounds[1]>maximalTime)&&(curTimeBounds[1]SetPoints(pointscontainer); result->ComputeBoundingBox(); // compute the number of time steps unsigned int numberOfTimeSteps = 1; if (maximalTime==0) // make sure that there is at least one time sliced geometry in the data storage { minimalTime = 0; maximalTime = 1; minimalIntervallSize = 1; } numberOfTimeSteps = static_cast((maximalTime-minimalTime)/minimalIntervallSize); TimeGeometry::Pointer timeGeometry = NULL; if ( result->GetPoints()->Size()>0 ) { // Initialize a geometry of a single time step Geometry3D::Pointer geometry = Geometry3D::New(); geometry->Initialize(); // correct bounding-box (is now in mm, should be in index-coordinates) // according to spacing BoundingBox::BoundsArrayType bounds = result->GetBounds(); int i; for(i = 0; i < 6; ++i) { bounds[i] /= minSpacing[i/2]; } geometry->SetBounds(bounds); geometry->SetSpacing(minSpacing); // Initialize the time sliced geometry timeGeometry = ProportionalTimeGeometry::New(); dynamic_cast(timeGeometry.GetPointer())->Initialize(geometry,numberOfTimeSteps); dynamic_cast(timeGeometry.GetPointer())->SetFirstTimePoint(minimalTime); dynamic_cast(timeGeometry.GetPointer())->SetStepDuration(minimalIntervallSize); } return timeGeometry; } mitk::TimeGeometry::Pointer mitk::DataStorage::ComputeBoundingGeometry3D( const char* boolPropertyKey, mitk::BaseRenderer* renderer, const char* boolPropertyKey2) const { return this->ComputeBoundingGeometry3D(this->GetAll(), boolPropertyKey, renderer, boolPropertyKey2); } mitk::TimeGeometry::Pointer mitk::DataStorage::ComputeVisibleBoundingGeometry3D( mitk::BaseRenderer* renderer, const char* boolPropertyKey ) { return ComputeBoundingGeometry3D( "visible", renderer, boolPropertyKey ); } mitk::BoundingBox::Pointer mitk::DataStorage::ComputeBoundingBox( const char* boolPropertyKey, mitk::BaseRenderer* renderer, const char* boolPropertyKey2) { BoundingBox::PointsContainer::Pointer pointscontainer=BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid=0; Point3D point; // Needed for check of zero bounding boxes mitk::ScalarType nullpoint[]={0,0,0,0,0,0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if((node.IsNotNull()) && (node->GetData() != NULL) && (node->GetData()->IsEmpty()==false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer) ) { const TimeGeometry* geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != NULL ) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = geometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for(i=0; i<8; ++i) { point = geometry->GetCornerPointInWorld(i); if(point[0]*point[0]+point[1]*point[1]+point[2]*point[2] < large) pointscontainer->InsertElement( pointid++, point); else { itkGenericOutputMacro( << "Unrealistically distant corner point encountered. Ignored. Node: " << node ); } } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } mitk::TimeBounds mitk::DataStorage::ComputeTimeBounds( const char* boolPropertyKey, mitk::BaseRenderer* renderer, const char* boolPropertyKey2) { TimeBounds timeBounds; ScalarType stmin, stmax, cur; - stmin= ScalarTypeNumericTraits::NonpositiveMin(); - stmax= ScalarTypeNumericTraits::max(); + stmin= itk::NumericTraits::NonpositiveMin(); + stmax= itk::NumericTraits::max(); timeBounds[0]=stmax; timeBounds[1]=stmin; SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if((node.IsNotNull()) && (node->GetData() != NULL) && (node->GetData()->IsEmpty()==false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer) ) { const TimeGeometry* geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != NULL ) { const TimeBounds & curTimeBounds = geometry->GetTimeBounds(); cur=curTimeBounds[0]; //is it after -infinity, but before everything else that we found until now? if((cur > stmin) && (cur < timeBounds[0])) timeBounds[0] = cur; cur=curTimeBounds[1]; //is it before infinity, but after everything else that we found until now? if((cur < stmax) && (cur > timeBounds[1])) timeBounds[1] = cur; } } } if(!(timeBounds[0] < stmax)) { timeBounds[0] = stmin; timeBounds[1] = stmax; } return timeBounds; } void mitk::DataStorage::BlockNodeModifiedEvents( bool block ) { m_BlockNodeModifiedEvents = block; } diff --git a/Core/Code/DataManagement/mitkDisplayGeometry.cpp b/Core/Code/DataManagement/mitkDisplayGeometry.cpp index da1dc11ebb..ab93699403 100644 --- a/Core/Code/DataManagement/mitkDisplayGeometry.cpp +++ b/Core/Code/DataManagement/mitkDisplayGeometry.cpp @@ -1,613 +1,613 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDisplayGeometry.h" itk::LightObject::Pointer mitk::DisplayGeometry::InternalClone() const { // itkExceptionMacro(<<"calling mitk::DisplayGeometry::Clone does not make much sense."); DisplayGeometry* returnValue = const_cast(this); return returnValue; } bool mitk::DisplayGeometry::IsValid() const { return m_WorldGeometry.IsNotNull() && m_WorldGeometry->IsValid(); } unsigned long mitk::DisplayGeometry::GetMTime() const { if((m_WorldGeometry.IsNotNull()) && (PlaneGeometry::GetMTime() < m_WorldGeometry->GetMTime())) { Modified(); } return PlaneGeometry::GetMTime(); } //const mitk::TimeBounds& mitk::DisplayGeometry::GetTimeBounds() const //{ // if(m_WorldGeometry.IsNull()) // { // return m_TimeBounds; // } // // return m_WorldGeometry->GetTimeBounds(); //} // size definition methods void mitk::DisplayGeometry::SetWorldGeometry(const PlaneGeometry* aWorldGeometry) { m_WorldGeometry = aWorldGeometry; Modified(); } bool mitk::DisplayGeometry::SetOriginInMM(const Vector2D& origin_mm) { m_OriginInMM = origin_mm; WorldToDisplay(m_OriginInMM, m_OriginInDisplayUnits); Modified(); return !this->RefitVisibleRect(); } mitk::Vector2D mitk::DisplayGeometry::GetOriginInMM() const { return m_OriginInMM; } mitk::Vector2D mitk::DisplayGeometry::GetOriginInDisplayUnits() const { return m_OriginInDisplayUnits; } void mitk::DisplayGeometry::SetSizeInDisplayUnits(unsigned int width, unsigned int height, bool keepDisplayedRegion) { Vector2D oldSizeInMM( m_SizeInMM ); Point2D oldCenterInMM; if(keepDisplayedRegion) { Point2D centerInDisplayUnits; centerInDisplayUnits[0] = m_SizeInDisplayUnits[0]*0.5; centerInDisplayUnits[1] = m_SizeInDisplayUnits[1]*0.5; DisplayToWorld(centerInDisplayUnits, oldCenterInMM); } m_SizeInDisplayUnits[0]=width; m_SizeInDisplayUnits[1]=height; if(m_SizeInDisplayUnits[0] <= 0) m_SizeInDisplayUnits[0] = 1; if(m_SizeInDisplayUnits[1] <= 0) m_SizeInDisplayUnits[1] = 1; DisplayToWorld(m_SizeInDisplayUnits, m_SizeInMM); if(keepDisplayedRegion) { Point2D positionOfOldCenterInCurrentDisplayUnits; WorldToDisplay(oldCenterInMM, positionOfOldCenterInCurrentDisplayUnits); Point2D currentNewCenterInDisplayUnits; currentNewCenterInDisplayUnits[0] = m_SizeInDisplayUnits[0]*0.5; currentNewCenterInDisplayUnits[1] = m_SizeInDisplayUnits[1]*0.5; Vector2D shift; - shift=positionOfOldCenterInCurrentDisplayUnits.GetVectorFromOrigin()-currentNewCenterInDisplayUnits; + shift=positionOfOldCenterInCurrentDisplayUnits-currentNewCenterInDisplayUnits; MoveBy(shift); Zoom(m_SizeInMM.GetNorm()/oldSizeInMM.GetNorm(), currentNewCenterInDisplayUnits); } Modified(); } mitk::Vector2D mitk::DisplayGeometry::GetSizeInDisplayUnits() const { return m_SizeInDisplayUnits; } mitk::Vector2D mitk::DisplayGeometry::GetSizeInMM() const { return m_SizeInMM; } unsigned int mitk::DisplayGeometry::GetDisplayWidth() const { assert(m_SizeInDisplayUnits[0] >= 0); return (unsigned int)m_SizeInDisplayUnits[0]; } unsigned int mitk::DisplayGeometry::GetDisplayHeight() const { assert(m_SizeInDisplayUnits[1] >= 0); return (unsigned int)m_SizeInDisplayUnits[1]; } // zooming, panning, restriction of both void mitk::DisplayGeometry::SetConstrainZoomingAndPanning(bool constrain) { m_ConstrainZoomingAndPanning = constrain; if (m_ConstrainZoomingAndPanning) { this->RefitVisibleRect(); } } bool mitk::DisplayGeometry::GetConstrainZommingAndPanning() const { return m_ConstrainZoomingAndPanning; } bool mitk::DisplayGeometry::SetScaleFactor(ScalarType mmPerDisplayUnit) { if(mmPerDisplayUnit<0.0001) { mmPerDisplayUnit=0.0001; } m_ScaleFactorMMPerDisplayUnit = mmPerDisplayUnit; - assert(m_ScaleFactorMMPerDisplayUnit < ScalarTypeNumericTraits::infinity()); + assert(m_ScaleFactorMMPerDisplayUnit < itk::NumericTraits::infinity()); DisplayToWorld(m_SizeInDisplayUnits, m_SizeInMM); return !this->RefitVisibleRect(); } mitk::ScalarType mitk::DisplayGeometry::GetScaleFactorMMPerDisplayUnit() const { return m_ScaleFactorMMPerDisplayUnit; } // Zooms with a factor (1.0=identity) around the specified center in display units bool mitk::DisplayGeometry::Zoom(ScalarType factor, const Point2D& centerInDisplayUnits) { assert(factor > 0); if ( SetScaleFactor(m_ScaleFactorMMPerDisplayUnit/factor) ) { return SetOriginInMM(m_OriginInMM-centerInDisplayUnits.GetVectorFromOrigin()*(1-factor)*m_ScaleFactorMMPerDisplayUnit); } else { return false; } } // Zooms with a factor (1.0=identity) around the specified center, but tries (if its within view contraints) to match the center in display units with the center in world coordinates. bool mitk::DisplayGeometry::ZoomWithFixedWorldCoordinates(ScalarType factor, const Point2D& focusDisplayUnits, const Point2D& focusUnitsInMM ) { assert(factor > 0); SetScaleFactor(m_ScaleFactorMMPerDisplayUnit/factor); SetOriginInMM(focusUnitsInMM.GetVectorFromOrigin()-focusDisplayUnits.GetVectorFromOrigin()*m_ScaleFactorMMPerDisplayUnit); return true; } bool mitk::DisplayGeometry::MoveBy(const Vector2D& shiftInDisplayUnits) { SetOriginInMM(m_OriginInMM+shiftInDisplayUnits*m_ScaleFactorMMPerDisplayUnit); Modified(); return !this->RefitVisibleRect(); } void mitk::DisplayGeometry::Fit() { if((m_WorldGeometry.IsNull()) || (m_WorldGeometry->IsValid() == false)) return; /// \FIXME: try to remove all the casts int width=(int)m_SizeInDisplayUnits[0]; int height=(int)m_SizeInDisplayUnits[1]; ScalarType w = width; ScalarType h = height; const ScalarType& widthInMM = m_WorldGeometry->GetExtentInMM(0); const ScalarType& heightInMM = m_WorldGeometry->GetExtentInMM(1); ScalarType aspRatio=((ScalarType)widthInMM)/heightInMM; ScalarType x = (ScalarType)w/widthInMM; ScalarType y = (ScalarType)h/heightInMM; if (x > y) { w = (int) (aspRatio*h); } else { h = (int) (w/aspRatio); } if(w>0) { SetScaleFactor(widthInMM/w); } Vector2D origin_display; origin_display[0]=-(width-w)/2.0; origin_display[1]=-(height-h)/2.0; SetOriginInMM(origin_display*m_ScaleFactorMMPerDisplayUnit); this->RefitVisibleRect(); Modified(); } // conversion methods void mitk::DisplayGeometry::DisplayToWorld(const Point2D &pt_display, Point2D &pt_mm) const { pt_mm[0]=m_ScaleFactorMMPerDisplayUnit*pt_display[0]+m_OriginInMM[0]; pt_mm[1]=m_ScaleFactorMMPerDisplayUnit*pt_display[1]+m_OriginInMM[1]; } void mitk::DisplayGeometry::WorldToDisplay(const Point2D &pt_mm, Point2D &pt_display) const { pt_display[0]=(pt_mm[0]-m_OriginInMM[0])*(1.0/m_ScaleFactorMMPerDisplayUnit); pt_display[1]=(pt_mm[1]-m_OriginInMM[1])*(1.0/m_ScaleFactorMMPerDisplayUnit); } void mitk::DisplayGeometry::DisplayToWorld(const Vector2D &vec_display, Vector2D &vec_mm) const { vec_mm=vec_display*m_ScaleFactorMMPerDisplayUnit; } void mitk::DisplayGeometry::WorldToDisplay(const Vector2D &vec_mm, Vector2D &vec_display) const { vec_display=vec_mm*(1.0/m_ScaleFactorMMPerDisplayUnit); } void mitk::DisplayGeometry::ULDisplayToMM(const Point2D &pt_ULdisplay, Point2D &pt_mm) const { ULDisplayToDisplay(pt_ULdisplay, pt_mm); DisplayToWorld(pt_mm, pt_mm); } void mitk::DisplayGeometry::MMToULDisplay(const Point2D &pt_mm, Point2D &pt_ULdisplay) const { WorldToDisplay(pt_mm, pt_ULdisplay); DisplayToULDisplay(pt_ULdisplay, pt_ULdisplay); } void mitk::DisplayGeometry::ULDisplayToMM(const Vector2D &vec_ULdisplay, Vector2D &vec_mm) const { ULDisplayToDisplay(vec_ULdisplay, vec_mm); DisplayToWorld(vec_mm, vec_mm); } void mitk::DisplayGeometry::MMToULDisplay(const Vector2D &vec_mm, Vector2D &vec_ULdisplay) const { WorldToDisplay(vec_mm, vec_ULdisplay); DisplayToULDisplay(vec_ULdisplay, vec_ULdisplay); } void mitk::DisplayGeometry::ULDisplayToDisplay(const Point2D &pt_ULdisplay, Point2D &pt_display) const { pt_display[0]=pt_ULdisplay[0]; pt_display[1]=GetDisplayHeight()-pt_ULdisplay[1]; } void mitk::DisplayGeometry::DisplayToULDisplay(const Point2D &pt_display, Point2D &pt_ULdisplay) const { ULDisplayToDisplay(pt_display, pt_ULdisplay); } void mitk::DisplayGeometry::ULDisplayToDisplay(const Vector2D &vec_ULdisplay, Vector2D &vec_display) const { vec_display[0]= vec_ULdisplay[0]; vec_display[1]=-vec_ULdisplay[1]; } void mitk::DisplayGeometry::DisplayToULDisplay(const Vector2D &vec_display, Vector2D &vec_ULdisplay) const { ULDisplayToDisplay(vec_display, vec_ULdisplay); } bool mitk::DisplayGeometry::Project(const Point3D &pt3d_mm, Point3D &projectedPt3d_mm) const { if(m_WorldGeometry.IsNotNull()) { return m_WorldGeometry->Project(pt3d_mm, projectedPt3d_mm); } else { return false; } } bool mitk::DisplayGeometry::Project(const Point3D & atPt3d_mm, const Vector3D &vec3d_mm, Vector3D &projectedVec3d_mm) const { if(m_WorldGeometry.IsNotNull()) { return m_WorldGeometry->Project(atPt3d_mm, vec3d_mm, projectedVec3d_mm); } else { return false; } } bool mitk::DisplayGeometry::Project(const Vector3D &vec3d_mm, Vector3D &projectedVec3d_mm) const { if(m_WorldGeometry.IsNotNull()) { return m_WorldGeometry->Project(vec3d_mm, projectedVec3d_mm); } else { return false; } } bool mitk::DisplayGeometry::Map(const Point3D &pt3d_mm, Point2D &pt2d_mm) const { if(m_WorldGeometry.IsNotNull()) { return m_WorldGeometry->Map(pt3d_mm, pt2d_mm); } else { return false; } } void mitk::DisplayGeometry::Map(const Point2D &pt2d_mm, Point3D &pt3d_mm) const { if(m_WorldGeometry.IsNull()) return; m_WorldGeometry->Map(pt2d_mm, pt3d_mm); } bool mitk::DisplayGeometry::Map(const Point3D & atPt3d_mm, const Vector3D &vec3d_mm, Vector2D &vec2d_mm) const { if(m_WorldGeometry.IsNotNull()) { return m_WorldGeometry->Map(atPt3d_mm, vec3d_mm, vec2d_mm); } else { return false; } } void mitk::DisplayGeometry::Map(const Point2D & atPt2d_mm, const Vector2D &vec2d_mm, Vector3D &vec3d_mm) const { if(m_WorldGeometry.IsNull()) return; m_WorldGeometry->Map(atPt2d_mm, vec2d_mm, vec3d_mm); } // protected methods mitk::DisplayGeometry::DisplayGeometry() :m_ScaleFactorMMPerDisplayUnit(1.0) ,m_WorldGeometry(NULL) ,m_ConstrainZoomingAndPanning(true) ,m_MaxWorldViewPercentage(1.0) ,m_MinWorldViewPercentage(0.1) { m_OriginInMM.Fill(0.0); m_OriginInDisplayUnits.Fill(0.0); m_SizeInMM.Fill(1.0); m_SizeInDisplayUnits.Fill(10.0); } mitk::DisplayGeometry::~DisplayGeometry() { } bool mitk::DisplayGeometry::RefitVisibleRect() { // do nothing if not asked to if (!m_ConstrainZoomingAndPanning) return false; // don't allow recursion (need to be fixed, singleton) static bool inRecalculate = false; if (inRecalculate) return false; inRecalculate = true; // rename some basic measures of the current viewport and world geometry (MM = milimeters Px = Pixels = display units) float displayXMM = m_OriginInMM[0]; float displayYMM = m_OriginInMM[1]; float displayWidthPx = m_SizeInDisplayUnits[0]; float displayHeightPx = m_SizeInDisplayUnits[1]; float displayWidthMM = m_SizeInDisplayUnits[0] * m_ScaleFactorMMPerDisplayUnit; float displayHeightMM = m_SizeInDisplayUnits[1] * m_ScaleFactorMMPerDisplayUnit; float worldWidthMM = m_WorldGeometry->GetExtentInMM(0); float worldHeightMM = m_WorldGeometry->GetExtentInMM(1); // reserve variables for the correction logic to save a corrected origin and zoom factor Vector2D newOrigin = m_OriginInMM; bool correctPanning = false; float newScaleFactor = m_ScaleFactorMMPerDisplayUnit; bool correctZooming = false; // start of the correction logic // zoom to big means: // at a given percentage of the world's width/height should be visible. Otherwise // the whole screen could show only one pixel // // zoom to small means: // zooming out should be limited at the point where the smaller of the world's sides is completely visible bool zoomXtooSmall = displayWidthPx * m_ScaleFactorMMPerDisplayUnit > m_MaxWorldViewPercentage * worldWidthMM; bool zoomXtooBig = displayWidthPx * m_ScaleFactorMMPerDisplayUnit < m_MinWorldViewPercentage * worldWidthMM; bool zoomYtooSmall = displayHeightPx * m_ScaleFactorMMPerDisplayUnit > m_MaxWorldViewPercentage * worldHeightMM; bool zoomYtooBig = displayHeightPx * m_ScaleFactorMMPerDisplayUnit < m_MinWorldViewPercentage * worldHeightMM; // constrain zooming in both direction if ( zoomXtooBig && zoomYtooBig) { double fx = worldWidthMM * m_MinWorldViewPercentage / displayWidthPx; double fy = worldHeightMM * m_MinWorldViewPercentage / displayHeightPx; newScaleFactor = fx < fy ? fx : fy; correctZooming = true; } // constrain zooming in x direction else if ( zoomXtooBig ) { newScaleFactor = worldWidthMM * m_MinWorldViewPercentage / displayWidthPx; correctZooming = true; } // constrain zooming in y direction else if ( zoomYtooBig ) { newScaleFactor = worldHeightMM * m_MinWorldViewPercentage / displayHeightPx; correctZooming = true; } // constrain zooming out // we stop zooming out at these situations: // // *** display // --- image // // ********************** // * * x side maxed out // * * // *--------------------* // *| |* // *| |* // *--------------------* // * * // * * // * * // ********************** // // ********************** // * |------| * y side maxed out // * | | * // * | | * // * | | * // * | | * // * | | * // * | | * // * | | * // * |------| * // ********************** // // In both situations we center the not-maxed out direction // if ( zoomXtooSmall && zoomYtooSmall ) { // determine and set the bigger scale factor float fx = worldWidthMM * m_MaxWorldViewPercentage / displayWidthPx; float fy = worldHeightMM * m_MaxWorldViewPercentage / displayHeightPx; newScaleFactor = fx > fy ? fx : fy; correctZooming = true; } // actually execute correction if (correctZooming) { SetScaleFactor(newScaleFactor); } displayWidthMM = m_SizeInDisplayUnits[0] * m_ScaleFactorMMPerDisplayUnit; displayHeightMM = m_SizeInDisplayUnits[1] * m_ScaleFactorMMPerDisplayUnit; // constrain panning if(worldWidthMM center x newOrigin[0] = (worldWidthMM - displayWidthMM) / 2.0; correctPanning = true; } else { // make sure left display border inside our world if (displayXMM < 0) { newOrigin[0] = 0; correctPanning = true; } // make sure right display border inside our world else if (displayXMM + displayWidthMM > worldWidthMM) { newOrigin[0] = worldWidthMM - displayWidthMM; correctPanning = true; } } if (worldHeightMM center y newOrigin[1] = (worldHeightMM - displayHeightMM) / 2.0; correctPanning = true; } else { // make sure top display border inside our world if (displayYMM + displayHeightMM > worldHeightMM) { newOrigin[1] = worldHeightMM - displayHeightMM; correctPanning = true; } // make sure bottom display border inside our world else if (displayYMM < 0) { newOrigin[1] = 0; correctPanning = true; } } if (correctPanning) { SetOriginInMM( newOrigin ); } inRecalculate = false; if ( correctPanning || correctZooming ) { Modified(); } // return true if any correction has been made return correctPanning || correctZooming; } void mitk::DisplayGeometry::PrintSelf(std::ostream& os, itk::Indent indent) const { if(m_WorldGeometry.IsNull()) { os << indent << " WorldGeometry: " << "NULL" << std::endl; } else { m_WorldGeometry->Print(os, indent); os << indent << " OriginInMM: " << m_OriginInMM << std::endl; os << indent << " OriginInDisplayUnits: " << m_OriginInDisplayUnits << std::endl; os << indent << " SizeInMM: " << m_SizeInMM << std::endl; os << indent << " SizeInDisplayUnits: " << m_SizeInDisplayUnits << std::endl; os << indent << " ScaleFactorMMPerDisplayUni: " << m_ScaleFactorMMPerDisplayUnit << std::endl; } Superclass::PrintSelf(os,indent); } diff --git a/Core/Code/DataManagement/mitkEqual.h b/Core/Code/DataManagement/mitkEqual.h new file mode 100644 index 0000000000..db6ebd4cc5 --- /dev/null +++ b/Core/Code/DataManagement/mitkEqual.h @@ -0,0 +1,72 @@ +/* + * mitkEqual.h + * + * Created on: Apr 14, 2014 + * Author: wirkert + */ + +#ifndef MITKEQUAL_H_ +#define MITKEQUAL_H_ + +#include + +#include "mitkNumericConstants.h" +#include "mitkLogMacros.h" + +namespace mitk { + + /** + * Helper method to check if the difference is bigger or equal to a given epsilon + * + * @param diff the difference to be checked against the epsilon + * @param the epsilon. The absolute difference needs to be smaller than this. + * @return true if abs(diff) >= eps + */ + template + inline bool DifferenceBiggerOrEqualEps(DifferenceType diff, mitk::ScalarType epsilon = mitk::eps) + { + return fabs(diff) >= epsilon; + } + + /** + * outputs elem1, elem2 and eps in case verbose and !isEqual. + * Elem can e.g. be a mitk::Vector or an mitk::Point. + * + * @param elem1 first element to be output + * @param elem2 second + * @param eps the epsilon which their difference was bigger than + * @param verbose tells the function if something shall be output + * @param isEqual function will only output something if the two elements are not equal + */ + template + inline void ConditionalOutputOfDifference(ElementToOutput1 elem1, ElementToOutput2 elem2, mitk::ScalarType eps, bool verbose, bool isEqual) + { + if(verbose && !isEqual) + { + MITK_INFO << typeid(ElementToOutput1).name() << " and " << typeid(ElementToOutput2).name() << " not equal. Lefthandside " << std::setprecision(12) << elem1 << " - Righthandside " << elem2 << " - epsilon " << eps; + } + } + + /** + * @ingroup MITKTestingAPI + * + * @param scalar1 Scalar value to compare. + * @param scalar2 Scalar value to compare. + * @param eps Tolerance for floating point comparison. + * @param verbose Flag indicating detailed console output. + * @return True if scalars are equal. + */ + inline bool Equal(ScalarType scalar1, ScalarType scalar2, ScalarType eps=mitk::eps, bool verbose=false) + { + bool isEqual( !DifferenceBiggerOrEqualEps(scalar1-scalar2, eps)); + + ConditionalOutputOfDifference(scalar1, scalar2, eps, verbose, isEqual); + + return isEqual; + } + +} + + + +#endif /* MITKEQUAL_H_ */ diff --git a/Core/Code/DataManagement/mitkGenericLookupTable.h b/Core/Code/DataManagement/mitkGenericLookupTable.h index aafebf299f..2dfe6b8cf1 100644 --- a/Core/Code/DataManagement/mitkGenericLookupTable.h +++ b/Core/Code/DataManagement/mitkGenericLookupTable.h @@ -1,161 +1,161 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKGENERICLOOKUPTABLE_H_HEADER_INCLUDED_C1061CEE #define MITKGENERICLOOKUPTABLE_H_HEADER_INCLUDED_C1061CEE #include #include #include #include #include -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include namespace mitk { /** * @brief Template class for generating lookup-tables * * This class template can be instantiated for all classes/internal types that fulfills * these requirements: * - an operator<< so that the properties value can be put into a std::stringstream * - an operator== so that two properties can be checked for equality * * The main purpose of this class is to be used in conjunction with * GenericLookupTableProperty. This enables passing of arbitrary lookup * tables to mappers to configure the rendering process. */ template class GenericLookupTable { public: typedef unsigned int IdentifierType; typedef T ValueType; typedef std::map< IdentifierType, ValueType > LookupTableType; typedef GenericLookupTable Self; GenericLookupTable() {} virtual ~GenericLookupTable() { } virtual const char *GetNameOfClass() const { return "GenericLookupTable"; } void SetTableValue( IdentifierType id, ValueType value ) { m_LookupTable[id] = value; } bool ValueExists(IdentifierType id) const { typename LookupTableType::const_iterator it = m_LookupTable.find(id); return (it != m_LookupTable.end()); } ValueType GetTableValue( IdentifierType id ) const { typename LookupTableType::const_iterator it = m_LookupTable.find(id); if (it != m_LookupTable.end()) return it->second; else throw std::range_error("id does not exist in the lookup table"); } const LookupTableType& GetLookupTable() const { return m_LookupTable; } bool operator==( const Self& lookupTable ) const { return (m_LookupTable == lookupTable.m_LookupTable); } bool operator!=( const Self& lookupTable ) const { return !(m_LookupTable == lookupTable.m_LookupTable); } virtual Self& operator=(const Self& other) // \TODO: this needs to be unit tested! { if ( this == &other ) { return *this; } else { m_LookupTable.clear(); m_LookupTable = other.m_LookupTable; return *this; } } protected: LookupTableType m_LookupTable; }; } // namespace mitk /** * Generates a specialized subclass of mitk::GenericLookupTable. * This way, GetNameOfClass() returns the value provided by LookupTableName. * Please see mitkProperties.h for examples. * @param LookupTableName the name of the instantiation of GenericLookupTable * @param Type the value type of the GenericLookupTable */ #define mitkSpecializeGenericLookupTable(LookupTableName,Type) \ class MITK_CORE_EXPORT LookupTableName: public GenericLookupTable< Type > \ { \ public: \ typedef LookupTableName Self; \ typedef GenericLookupTable< Type > Superclass; \ virtual const char *GetNameOfClass() const \ {return #LookupTableName;} \ LookupTableName() {} \ virtual Superclass& operator=(const Superclass& other) { return Superclass::operator=(other); } \ virtual ~LookupTableName() {} \ }; \ MITK_CORE_EXPORT std::ostream& operator<<(std::ostream& stream, const LookupTableName& /*l*/); /** * Generates the ostream << operator for the lookuptable. This definition * of a global function must be in a cpp file, therefore it is split from the * class declaration macro mitkSpecializeGenericLookupTable. */ #define mitkSpecializeGenericLookupTableOperator(LookupTableName) \ std::ostream& mitk::operator<<(std::ostream& stream, const LookupTableName& l) \ { \ typedef LookupTableName::LookupTableType::const_iterator IterType; \ IterType e = l.GetLookupTable().end(); \ IterType b = l.GetLookupTable().begin(); \ stream << "["; \ for (IterType i = b; i != e; ++i) \ { \ if (i != b) \ { \ stream << ", "; \ } \ stream << i->first << " -> " << i->second; \ } \ return stream << "]"; \ }; #endif diff --git a/Core/Code/DataManagement/mitkGenericProperty.h b/Core/Code/DataManagement/mitkGenericProperty.h index a1088ede70..2567a6f4b2 100644 --- a/Core/Code/DataManagement/mitkGenericProperty.h +++ b/Core/Code/DataManagement/mitkGenericProperty.h @@ -1,149 +1,149 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKGENERICPROPERTY_H_HEADER_INCLUDED_C1061CEE #define MITKGENERICPROPERTY_H_HEADER_INCLUDED_C1061CEE #include #include #include -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include #include "mitkBaseProperty.h" namespace mitk { #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4522) #endif /*! @ brief Template class for generating properties for int, float, bool, etc. This class template can be instantiated for all classes/internal types that fulfills these requirements: - an operator<< so that the properties value can be put into a std::stringstream - an operator== so that two properties can be checked for equality Note: you must use the macro mitkSpecializeGenericProperty to provide specializations for concrete types (e.g. BoolProperty). Please see mitkProperties.h for examples. If you don't use the mitkSpecializeGenericProperty Macro, GetNameOfClass() returns a wrong name. */ template class MITK_EXPORT GenericProperty : public BaseProperty { public: mitkClassMacro(GenericProperty, BaseProperty); mitkNewMacro1Param(GenericProperty, T); itkCloneMacro(Self) typedef T ValueType; itkSetMacro(Value,T); itkGetConstMacro(Value,T); virtual std::string GetValueAsString() const { std::stringstream myStr; myStr << GetValue() ; return myStr.str(); } using BaseProperty::operator=; protected: GenericProperty() {} GenericProperty(T x) : m_Value(x) {} GenericProperty(const GenericProperty& other) : BaseProperty(other) , m_Value(other.m_Value) {} T m_Value; private: // purposely not implemented GenericProperty& operator=(const GenericProperty&); virtual itk::LightObject::Pointer InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } virtual bool IsEqual(const BaseProperty& other) const { return (this->m_Value == static_cast(other).m_Value); } virtual bool Assign(const BaseProperty& other) { this->m_Value = static_cast(other).m_Value; return true; } }; #ifdef _MSC_VER # pragma warning(pop) #endif } // namespace mitk /** * Generates a specialized subclass of mitk::GenericProperty. * This way, GetNameOfClass() returns the value provided by PropertyName. * Please see mitkProperties.h for examples. * @param PropertyName the name of the subclass of GenericProperty * @param Type the value type of the GenericProperty * @param Export the export macro for DLL usage */ #define mitkDeclareGenericProperty(PropertyName,Type,Export) \ class Export PropertyName: public GenericProperty< Type > \ { \ public: \ mitkClassMacro(PropertyName, GenericProperty< Type >) \ itkFactorylessNewMacro(Self) \ itkCloneMacro(Self) \ mitkNewMacro1Param(PropertyName, Type) \ using BaseProperty::operator=; \ protected: \ PropertyName(); \ PropertyName(const PropertyName&); \ PropertyName(Type x); \ private: \ itk::LightObject::Pointer InternalClone() const; \ }; #define mitkDefineGenericProperty(PropertyName,Type,DefaultValue) \ mitk::PropertyName::PropertyName() : Superclass(DefaultValue) { } \ mitk::PropertyName::PropertyName(const PropertyName& other) : GenericProperty< Type >(other) {} \ mitk::PropertyName::PropertyName(Type x) : Superclass(x) {} \ itk::LightObject::Pointer mitk::PropertyName::InternalClone() const { \ itk::LightObject::Pointer result(new Self(*this)); \ result->UnRegister(); \ return result; \ } \ #endif /* MITKGENERICPROPERTY_H_HEADER_INCLUDED_C1061CEE */ diff --git a/Core/Code/DataManagement/mitkGeometry3D.h b/Core/Code/DataManagement/mitkGeometry3D.h index e3f6b5e6b4..e796798c4e 100644 --- a/Core/Code/DataManagement/mitkGeometry3D.h +++ b/Core/Code/DataManagement/mitkGeometry3D.h @@ -1,72 +1,73 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef GEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #define GEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #include #include +#include "mitkNumericTypes.h" #include "itkScalableAffineTransform.h" #include #include "mitkBaseGeometry.h" class vtkLinearTransform; namespace mitk { //##Documentation //## @brief Standard implementation of BaseGeometry. //## //## @ingroup Geometry class MITK_CORE_EXPORT Geometry3D : public BaseGeometry { public: mitkClassMacro(Geometry3D, mitk::BaseGeometry); typedef itk::QuaternionRigidTransform< ScalarType > QuaternionTransformType; typedef QuaternionTransformType::VnlQuaternionType VnlQuaternionType; /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) mitkNewMacro1Param(Self, const Self&); itkCloneMacro(Self) //itkGetConstReferenceMacro(TimeBounds, TimeBounds); //virtual void SetTimeBounds(const TimeBounds& timebounds); protected: Geometry3D(); Geometry3D(const Geometry3D& other); //##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 itk::LightObject::Pointer InternalClone() const; virtual ~Geometry3D(); }; } // namespace mitk #endif /* GEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Core/Code/DataManagement/mitkImage.cpp b/Core/Code/DataManagement/mitkImage.cpp index 1c69b60898..74e8ed55df 100644 --- a/Core/Code/DataManagement/mitkImage.cpp +++ b/Core/Code/DataManagement/mitkImage.cpp @@ -1,1372 +1,1372 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //MITK #include "mitkImage.h" #include "mitkImageStatisticsHolder.h" #include "mitkPixelTypeMultiplex.h" #include #include "mitkCompareImageDataFilter.h" //VTK #include //Other #include #define FILL_C_ARRAY( _arr, _size, _value) for(unsigned int i=0u; i<_size; i++) \ { _arr[i] = _value; } mitk::Image::Image() : m_Dimension(0), m_Dimensions(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 TimeGeometry::Pointer cloned = other.GetTimeGeometry()->Clone(); this->SetTimeGeometry(cloned.GetPointer()); if (this->GetDimension() > 3) { const unsigned int time_steps = this->GetDimension(3); for (unsigned int i = 0u; i < time_steps; ++i) { ImageDataItemPointer volume = 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 mitk::Image::GetPixelValueByIndex(const itk::Index<3> &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) // bug-11978 : we still need to catch index with negative values if ( position[0] < 0 || position[1] < 0 || position[2] < 0 ) { MITK_WARN << "Given position ("<< position << ") is out of image range, returning 0." ; } // check if the given position is inside the index range of the image, the 3rd dimension needs to be compared only if the dimension is not 0 else if ( (unsigned int)position[0] >= imageDims[0] || (unsigned int)position[1] >= imageDims[1] || ( imageDims[2] && (unsigned int)position[2] >= imageDims[2] )) { MITK_WARN << "Given position ("<< position << ") is out of image range, returning 0." ; } else { 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; + itk::Index<3> itkIndex; this->GetGeometry()->WorldToIndex(position, itkIndex); value = this->GetPixelValueByIndex( itkIndex, timestep); return value; } mitk::ImageVtkAccessor* 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(this) == NULL) return NULL; SlicedGeometry3D* geom3d = GetSlicedGeometry(t); const mitk::Vector3D vspacing = (geom3d->GetSpacing()); double dspacing[3] = {vspacing[0],vspacing[1],vspacing[2]}; volume->GetVtkImageData(this)->SetSpacing( dspacing ); return volume->GetVtkImageData(this); } 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]); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); for (TimeStepType step = 0; step < timeGeometry->CountTimeSteps(); ++step) { timeGeometry->GetGeometryForTimeStep(step)->ImageGeometryOn(); } SetTimeGeometry(timeGeometry); ImageDataItemPointer dnull=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::BaseGeometry& geometry, unsigned int channels, int tDim ) { mitk::ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); itk::LightObject::Pointer lopointer = geometry.Clone(); timeGeometry->Initialize(dynamic_cast(lopointer.GetPointer()), tDim); this->Initialize(type, *timeGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::PixelType& type, const mitk::TimeGeometry& geometry, unsigned int channels, int tDim ) { unsigned int dimensions[5]; dimensions[0] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(0)+0.5); dimensions[1] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(1)+0.5); dimensions[2] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(2)+0.5); dimensions[3] = (tDim > 0) ? tDim : geometry.CountTimeSteps(); dimensions[4] = 0; unsigned int dimension = 2; if ( dimensions[2] > 1 ) dimension = 3; if ( dimensions[3] > 1 ) dimension = 4; Initialize( type, dimension, dimensions, channels ); if (geometry.CountTimeSteps() > 1) { TimeGeometry::Pointer cloned = geometry.Clone(); SetTimeGeometry(cloned.GetPointer()); } else Superclass::SetGeometry(geometry.GetGeometryForTimeStep(0)); /* //Old //TODO_GOETZ Really necessary? mitk::BoundingBox::BoundsArrayType bounds = geometry.GetBoundingBoxInWorld()->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.GetVnlVector().data_block()); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); }*/ } void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::PlaneGeometry& 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->GetTimeGeometry()); } void mitk::Image::Initialize(vtkImageData* vtkimagedata, int channels, int tDim, int sDim, int pDim) { 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(pDim>=0) { tmpDimensions[1]=pDim; if(m_Dimension < 2) m_Dimension = 2; } if(sDim>=0) { tmpDimensions[2]=sDim; if(m_Dimension < 3) m_Dimension = 3; } if(tDim>=0) { tmpDimensions[3]=tDim; if(m_Dimension < 4) m_Dimension = 4; } 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; double vtkorigin[3]; vtkimagedata->GetOrigin(vtkorigin); FillVector3D(origin, vtkorigin[0], 0.0, 0.0); if(m_Dimension>=2) origin[1]=vtkorigin[1]; if(m_Dimension>=3) origin[2]=vtkorigin[2]; SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast(slicedGeometry->GetPlaneGeometry(0)); planeGeometry->SetOrigin(origin); // re-initialize SlicedGeometry3D slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); delete [] tmpDimensions; } bool mitk::Image::IsValidSlice(int s, int t, int n) const { if(m_Initialized) return ((s>=0) && (s<(int)m_Dimensions[2]) && (t>=0) && (t< (int) m_Dimensions[3]) && (n>=0) && (n< (int)GetNumberOfChannels())); else return false; } bool mitk::Image::IsValidVolume(int t, int n) const { if(m_Initialized) return IsValidSlice(0, t, n); else return false; } bool mitk::Image::IsValidChannel(int n) const { if(m_Initialized) return IsValidSlice(0, 0, n); else return false; } void mitk::Image::ComputeOffsetTable() { if(m_OffsetTable!=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(BaseGeometry* aGeometry3D) { // Please be aware of the 0.5 offset/pixel-center issue! See Geometry documentation for further information if(aGeometry3D->GetImageGeometry()==false) { MITK_INFO << "WARNING: Applied a non-image geometry onto an image. Please be SURE that this geometry is pixel-center-based! If it is not, you need to call Geometry3D->ChangeImageGeometryConsideringOriginOffset(true) before calling image->setGeometry(..)\n"; } Superclass::SetGeometry(aGeometry3D); for (TimeStepType step = 0; step < GetTimeGeometry()->CountTimeSteps(); ++step) GetTimeGeometry()->GetGeometryForTimeStep(step)->ImageGeometryOn(); } void mitk::Image::PrintSelf(std::ostream& os, itk::Indent indent) const { 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.GetPixelTypeAsString() << std::endl; os << indent << " BytesPerElement: " << chPixelType.GetSize() << std::endl; os << indent << " ComponentType: " << chPixelType.GetComponentTypeAsString() << std::endl; os << indent << " NumberOfComponents: " << chPixelType.GetNumberOfComponents() << std::endl; os << indent << " BitsPerComponent: " << chPixelType.GetBitsPerComponent() << std::endl; } } else { os << indent << " Image not initialized: m_Initialized: false" << std::endl; } Superclass::PrintSelf(os,indent); } bool mitk::Image::IsRotated() const { const mitk::BaseGeometry* geo = this->GetGeometry(); bool ret = false; if(geo) { const vnl_matrix_fixed & mx = geo->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::ScalarType ref = 0; for(short k = 0; k < 3; ++k) ref += mx[k][k]; ref/=1000; // Arbitrary value; if a non-diagonal (nd) element is bigger then this, matrix is considered nd. for(short i = 0; i < 3; ++i) { for(short j = 0; j < 3; ++j) { if(i != j) { if(std::abs(mx[i][j]) > ref) // matrix is nd ret = true; } } } } return ret; } mitk::ScalarType mitk::Image::GetScalarValueMin(int t) const { return m_ImageStatistics->GetScalarValueMin(t); } //## \brief Get the maximum for scalar images mitk::ScalarType mitk::Image::GetScalarValueMax(int t) const { return m_ImageStatistics->GetScalarValueMax(t); } //## \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); } bool mitk::Equal(const mitk::Image* leftHandSide, const mitk::Image* rightHandSide, ScalarType eps, bool verbose) { if((leftHandSide == NULL) || (rightHandSide == NULL)) { MITK_ERROR << "mitk::Equal(const mitk::Image* leftHandSide, const mitk::Image* rightHandSide, ScalarType eps, bool verbose) does not work with NULL pointer input."; return false; } return mitk::Equal( *leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(const mitk::Image& leftHandSide, const mitk::Image& rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; // Dimensionality if( rightHandSide.GetDimension() != leftHandSide.GetDimension() ) { if(verbose) { MITK_INFO << "[( Image )] Dimensionality differs."; MITK_INFO << "leftHandSide is " << leftHandSide.GetDimension() << "rightHandSide is " << rightHandSide.GetDimension(); } returnValue = false; } // Pair-wise dimension (size) comparison unsigned int minDimensionality = std::min(rightHandSide.GetDimension(),leftHandSide.GetDimension()); for( unsigned int i=0; i< minDimensionality; ++i) { if( rightHandSide.GetDimension(i) != leftHandSide.GetDimension(i) ) { returnValue = false; if(verbose) { MITK_INFO << "[( Image )] dimension differs."; MITK_INFO << "leftHandSide->GetDimension("<GetDimension("<SetInput(0, &rightHandSide); compareFilter->SetInput(1, &leftHandSide); compareFilter->SetTolerance(eps); compareFilter->Update(); if(( !compareFilter->GetResult() ) ) { returnValue = false; if(verbose) { MITK_INFO << "[(Image)] Pixel values differ: "; compareFilter->GetCompareResults().PrintSelf(); } } } return returnValue; } diff --git a/Core/Code/DataManagement/mitkImage.h b/Core/Code/DataManagement/mitkImage.h index d35bf688b2..fef19a3a80 100644 --- a/Core/Code/DataManagement/mitkImage.h +++ b/Core/Code/DataManagement/mitkImage.h @@ -1,737 +1,737 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #define MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #include #include "mitkSlicedData.h" #include "mitkBaseData.h" #include "mitkLevelWindow.h" #include "mitkPlaneGeometry.h" #include #include "mitkImageDataItem.h" #include "mitkImageDescriptor.h" #include "mitkImageAccessorBase.h" #include "mitkImageVtkAccessor.h" //DEPRECATED #include #ifndef __itkHistogram_h #include #endif class vtkImageData; namespace mitk { class SubImageSelector; class ImageTimeSelector; class ImageStatisticsHolder; //##Documentation //## @brief Image class for storing images //## //## Can be asked for header information, the data vector, //## the mitkIpPicDescriptor struct or vtkImageData objects. If not the complete //## data is required, the appropriate SubImageSelector class should be used //## for access. //## Image organizes sets of slices (s x 2D), volumes (t x 3D) and channels (n //## x ND). Channels are for different kind of data, e.g., morphology in //## channel 0, velocities in channel 1. All channels must have the same Geometry! In //## particular, the dimensions of all channels are the same, only the pixel-type //## may differ between channels. //## //## For importing ITK images use of mitk::ITKImageImport is recommended, see //## \ref Adaptor. //## //## For ITK v3.8 and older: Converting coordinates from the ITK physical //## coordinate system (which does not support rotated images) to the MITK world //## coordinate system should be performed via the BaseGeometry of the Image, see //## BaseGeometry::WorldToItkPhysicalPoint. //## //## For more information, see \ref MitkImagePage . //## @ingroup Data class MITK_CORE_EXPORT Image : public SlicedData { friend class SubImageSelector; friend class ImageAccessorBase; friend class ImageVtkAccessor; friend class ImageReadAccessor; friend class ImageWriteAccessor; public: mitkClassMacro(Image, SlicedData); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Smart Pointer type to a ImageDataItem. */ typedef itk::SmartPointer ImageDataItemPointer; typedef itk::Statistics::Histogram HistogramType; typedef mitk::ImageStatisticsHolder* StatisticsHolderPointer; //## @param ImportMemoryManagementType This parameter is evaluated when setting new data to an image. //## The different options are: //## CopyMemory: Data to be set is copied and assigned to a new memory block. Data memory block will be freed on deletion of mitk::Image. //## MamageMemory: Data to be set will be referenced, and Data memory block will be freed on deletion of mitk::Image. //## Reference Memory: Data to be set will be referenced, but Data memory block will not be freed on deletion of mitk::Image. //## DontManageMemory = ReferenceMemory. enum ImportMemoryManagementType { CopyMemory, ManageMemory, ReferenceMemory, DontManageMemory = ReferenceMemory }; //##Documentation //## @brief Vector container of SmartPointers to ImageDataItems; //## Class is only for internal usage to allow convenient access to all slices over iterators; //## See documentation of ImageDataItem for details. typedef std::vector ImageDataItemPointerArray; public: //##Documentation //## @brief Returns the PixelType of channel @a n. const mitk::PixelType GetPixelType(int n = 0) const; //##Documentation //## @brief Get dimension of the image //## unsigned int GetDimension() const; //##Documentation //## @brief Get the size of dimension @a i (e.g., i=0 results in the number of pixels in x-direction). //## //## @sa GetDimensions() unsigned int GetDimension(int i) const; /** @brief Get the data vector of the complete image, i.e., of all channels linked together. If you only want to access a slice, volume at a specific time or single channel use one of the SubImageSelector classes. \deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by ImageWriteAccessor::GetData() or ImageReadAccessor::GetData() */ DEPRECATED(virtual void* GetData()); public: /** @brief Get the pixel value at one specific index position. The pixel type is always being converted to double. \deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by a method from ImagePixelWriteAccessor or ImagePixelReadAccessor */ - DEPRECATED(double GetPixelValueByIndex(const mitk::Index3D& position, unsigned int timestep = 0)); + DEPRECATED(double GetPixelValueByIndex(const itk::Index<3>& position, unsigned int timestep = 0)); /** @brief Get the pixel value at one specific world position. The pixel type is always being converted to double. \deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by a method from ImagePixelWriteAccessor or ImagePixelReadAccessor */ DEPRECATED(double GetPixelValueByWorldCoordinate(const mitk::Point3D& position, unsigned int timestep = 0)); //##Documentation //## @brief Get a volume at a specific time @a t of channel @a n as a vtkImageData. virtual ImageVtkAccessor* GetVtkImageData(int t = 0, int n = 0); //##Documentation //## @brief Get the complete image, i.e., all channels linked together, as a @a mitkIpPicDescriptor. //## //## If you only want to access a slice, volume at a specific time or single channel //## use one of the SubImageSelector classes. //virtual mitkIpPicDescriptor* GetPic(); //##Documentation //## @brief Check whether slice @a s at time @a t in channel @a n is set virtual bool IsSliceSet(int s = 0, int t = 0, int n = 0) const; //##Documentation //## @brief Check whether volume at time @a t in channel @a n is set virtual bool IsVolumeSet(int t = 0, int n = 0) const; //##Documentation //## @brief Check whether the channel @a n is set virtual bool IsChannelSet(int n = 0) const; //##Documentation //## @brief Set @a data as slice @a s at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a slice (at least is not smaller than a slice), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportSlice with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicSlice, SetImportSlice, SetImportVolume virtual bool SetSlice(const void *data, int s = 0, int t = 0, int n = 0); //##Documentation //## @brief Set @a data as volume at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a volume (at least is not smaller than a volume), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportVolume with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicVolume, SetImportVolume virtual bool SetVolume(const void *data, int t = 0, int n = 0); //##Documentation //## @brief Set @a data in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a channel (at least is not smaller than a channel), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportChannel with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicChannel, SetImportChannel virtual bool SetChannel(const void *data, int n = 0); //##Documentation //## @brief Set @a data as slice @a s at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a slice (at least is not smaller than a slice), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicSlice virtual bool SetImportSlice(void *data, int s = 0, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## @brief Set @a data as volume at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a volume (at least is not smaller than a volume), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicVolume virtual bool SetImportVolume(void *data, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## @brief Set @a data in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a channel (at least is not smaller than a channel), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicChannel virtual bool SetImportChannel(void *data, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## initialize new (or re-initialize) image information //## @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0). virtual void Initialize(const mitk::PixelType& type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels = 1); //##Documentation //## initialize new (or re-initialize) image information by a BaseGeometry //## //## @param tDim defines the number of time steps for which the Image should be initialized virtual void Initialize(const mitk::PixelType& type, const mitk::BaseGeometry& geometry, unsigned int channels = 1, int tDim=1); /** * initialize new (or re-initialize) image information by a TimeGeometry * * @param tDim defines the number of time steps for which the Image should be initialized * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(virtual void Initialize(const mitk::PixelType& /*type*/, const mitk::TimeSlicedGeometry* /*geometry*/, unsigned int /*channels = 1*/, int /*tDim=1*/)){} /** * \brief Initialize new (or re-initialize) image information by a TimeGeometry * * \param tDim override time dimension if the value is bigger than 0 (Default -1) */ virtual void Initialize(const mitk::PixelType& type, const mitk::TimeGeometry& geometry, unsigned int channels = 1, int tDim=-1 ); //##Documentation //## initialize new (or re-initialize) image information by a PlaneGeometry and number of slices //## //## Initializes the bounding box according to the width/height of the //## PlaneGeometry and @a sDim via SlicedGeometry3D::InitializeEvenlySpaced. //## The spacing is calculated from the PlaneGeometry. //## \sa SlicedGeometry3D::InitializeEvenlySpaced virtual void Initialize(const mitk::PixelType& type, int sDim, const mitk::PlaneGeometry& geometry2d, bool flipped = false, unsigned int channels = 1, int tDim=1); //##Documentation //## initialize new (or re-initialize) image information by another //## mitk-image. //## Only the header is used, not the data vector! //## virtual void Initialize(const mitk::Image* image); virtual void Initialize(const mitk::ImageDescriptor::Pointer inDesc); //##Documentation //## initialize new (or re-initialize) image information by @a pic. //## Dimensions and @a Geometry3D /@a PlaneGeometry are set according //## to the tags in @a pic. //## Only the header is used, not the data vector! Use SetPicVolume(pic) //## to set the data vector. //## //## @param tDim override time dimension (@a n[3]) in @a pic (if >0) //## @param sDim override z-space dimension (@a n[2]) in @a pic (if >0) //## @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0). //virtual void Initialize(const mitkIpPicDescriptor* pic, int channels = 1, int tDim = -1, int sDim = -1); //##Documentation //## initialize new (or re-initialize) image information by @a vtkimagedata, //## a vtk-image. //## Only the header is used, not the data vector! Use //## SetVolume(vtkimage->GetScalarPointer()) to set the data vector. //## //## @param tDim override time dimension in @a vtkimagedata (if >0 and <) //## @param sDim override z-space dimension in @a vtkimagedata (if >0 and <) //## @param pDim override y-space dimension in @a vtkimagedata (if >0 and <) virtual void Initialize(vtkImageData* vtkimagedata, int channels = 1, int tDim = -1, int sDim = -1, int pDim = -1); //##Documentation //## initialize new (or re-initialize) image information by @a itkimage, //## a templated itk-image. //## Only the header is used, not the data vector! Use //## SetVolume(itkimage->GetBufferPointer()) to set the data vector. //## //## @param tDim override time dimension in @a itkimage (if >0 and <) //## @param sDim override z-space dimension in @a itkimage (if >0 and <) template void InitializeByItk(const itkImageType* itkimage, int channels = 1, int tDim = -1, int sDim=-1) { if(itkimage==NULL) return; MITK_DEBUG << "Initializing MITK image from ITK image."; // build array with dimensions in each direction with at least 4 entries m_Dimension=itkimage->GetImageDimension(); unsigned int i, *tmpDimensions=new unsigned int[m_Dimension>4?m_Dimension:4]; for(i=0;iGetLargestPossibleRegion().GetSize().GetSize()[i]; if(m_Dimension<4) { unsigned int *p; for(i=0,p=tmpDimensions+m_Dimension;i<4-m_Dimension;++i, ++p) *p=1; } // overwrite number of slices if sDim is set if((m_Dimension>2) && (sDim>=0)) tmpDimensions[2]=sDim; // overwrite number of time points if tDim is set if((m_Dimension>3) && (tDim>=0)) tmpDimensions[3]=tDim; // rough initialization of Image // mitk::PixelType importType = ImportItkPixelType( itkimage::PixelType ); Initialize(MakePixelType(), m_Dimension, tmpDimensions, channels); const typename itkImageType::SpacingType & itkspacing = itkimage->GetSpacing(); MITK_DEBUG << "ITK spacing " << itkspacing; // access spacing of itk::Image Vector3D spacing; FillVector3D(spacing, itkspacing[0], 1.0, 1.0); if(m_Dimension >= 2) spacing[1]=itkspacing[1]; if(m_Dimension >= 3) spacing[2]=itkspacing[2]; // access origin of itk::Image Point3D origin; const typename itkImageType::PointType & itkorigin = itkimage->GetOrigin(); MITK_DEBUG << "ITK origin " << itkorigin; FillVector3D(origin, itkorigin[0], 0.0, 0.0); if(m_Dimension>=2) origin[1]=itkorigin[1]; if(m_Dimension>=3) origin[2]=itkorigin[2]; // access direction of itk::Imagm_PixelType = new mitk::PixelType(type);e and include spacing const typename itkImageType::DirectionType & itkdirection = itkimage->GetDirection(); MITK_DEBUG << "ITK direction " << itkdirection; mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (m_Dimension >= 3? 3 : m_Dimension); // check if spacing has no zero entry and itkdirection has no zero columns bool itkdirectionOk = true; mitk::ScalarType columnSum; for( j=0; j < itkDimMax3; ++j ) { columnSum = 0.0; for ( i=0; i < itkDimMax3; ++i) { columnSum += fabs(itkdirection[i][j]); } if(columnSum < mitk::eps) { itkdirectionOk = false; } if ( (spacing[j] < - mitk::eps) // (normally sized) negative value && (j==2) && (m_Dimensions[2] == 1) ) { // Negative spacings can occur when reading single DICOM slices with ITK via GDCMIO // In these cases spacing is not determind by ITK correctly (because it distinguishes correctly // between slice thickness and inter slice distance -- slice distance is meaningless for // single slices). // I experienced that ITK produced something meaningful nonetheless because is is // evaluating the tag "(0018,0088) Spacing between slices" as a fallback. This tag is not // reliable (http://www.itk.org/pipermail/insight-users/2005-September/014711.html) // but gives at least a hint. // In real world cases I experienced that this tag contained the correct inter slice distance // with a negative sign, so we just invert such negative spacings. MITK_WARN << "Illegal value of itk::Image::GetSpacing()[" << j <<"]=" << spacing[j] << ". Using inverted value " << -spacing[j]; spacing[j] = -spacing[j]; } else if (spacing[j] < mitk::eps) // value near zero { MITK_ERROR << "Illegal value of itk::Image::GetSpacing()[" << j <<"]=" << spacing[j] << ". Using 1.0 instead."; spacing[j] = 1.0; } } if(itkdirectionOk == false) { MITK_ERROR << "Illegal matrix returned by itk::Image::GetDirection():" << itkdirection << " Using identity instead."; for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) if ( i == j ) matrix[i][j] = spacing[j]; else matrix[i][j] = 0.0; } else { for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) matrix[i][j] = itkdirection[i][j]*spacing[j]; } // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast(GetSlicedGeometry(0)->GetPlaneGeometry(0)); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, m_Dimensions[2]); slicedGeometry->SetSpacing(spacing); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); // clean-up delete [] tmpDimensions; this->Initialize(); }; //##Documentation //## @brief Check whether slice @a s at time @a t in channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidSlice(int s = 0, int t = 0, int n = 0) const; //##Documentation //## @brief Check whether volume at time @a t in channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidVolume(int t = 0, int n = 0) const; //##Documentation //## @brief Check whether the channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidChannel(int n = 0) const; //##Documentation //## @brief Returns true if an image is rotated, i.e. its geometry's //## transformation matrix has nonzero elements besides the diagonal. //## Non-diagonal elements are checked if larger then 1/1000 of the matrix' trace. bool IsRotated() const; //##Documentation //## @brief Get the sizes of all dimensions as an integer-array. //## //## @sa GetDimension(int i); unsigned int* GetDimensions() const; ImageDescriptor::Pointer GetImageDescriptor() const { return m_ImageDescriptor; } ChannelDescriptor GetChannelDescriptor( int id = 0 ) const { return m_ImageDescriptor->GetChannelDescriptor(id); } /** \brief Sets a geometry to an image. */ virtual void SetGeometry(BaseGeometry* aGeometry3D); /** * @warning for internal use only */ virtual ImageDataItemPointer GetSliceData(int s = 0, int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * @warning for internal use only */ virtual ImageDataItemPointer GetVolumeData(int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * @warning for internal use only */ virtual ImageDataItemPointer GetChannelData(int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** \brief (DEPRECATED) Get the minimum for scalar images */ DEPRECATED (ScalarType GetScalarValueMin(int t=0) const); /** \brief (DEPRECATED) Get the maximum for scalar images \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValueMax(int t=0) const); /** \brief (DEPRECATED) Get the second smallest value for scalar images \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMin(int t=0) const); /** \brief (DEPRECATED) Get the smallest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValueMinNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the second smallest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMinNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the second largest value for scalar images \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMax(int t=0) const); /** \brief (DEPRECATED) Get the largest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValueMaxNoRecompute( unsigned int t = 0 ) const ); /** \brief (DEPRECATED) Get the second largest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMaxNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the count of voxels with the smallest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetCountOfMinValuedVoxels(int t = 0) const); /** \brief (DEPRECATED) Get the count of voxels with the largest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetCountOfMaxValuedVoxels(int t = 0) const); /** \brief (DEPRECATED) Get the count of voxels with the largest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (unsigned int GetCountOfMaxValuedVoxelsNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the count of voxels with the smallest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (unsigned int GetCountOfMinValuedVoxelsNoRecompute( unsigned int t = 0 ) const); /** \brief Returns a pointer to the ImageStatisticsHolder object that holds all statistics information for the image. All Get-methods for statistics properties formerly accessible directly from an Image object are now moved to the new \a ImageStatisticsHolder object. */ StatisticsHolderPointer GetStatistics() const { return m_ImageStatistics; } protected: mitkCloneMacro(Self); int GetSliceIndex(int s = 0, int t = 0, int n = 0) const; int GetVolumeIndex(int t = 0, int n = 0) const; void ComputeOffsetTable(); virtual bool IsValidTimeStep(int t) const; virtual void Expand( unsigned int timeSteps ); virtual ImageDataItemPointer AllocateSliceData(int s = 0, int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); virtual ImageDataItemPointer AllocateVolumeData(int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); virtual ImageDataItemPointer AllocateChannelData(int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); Image(); Image(const Image &other); virtual ~Image(); virtual void Clear(); //## @warning Has to be called by every Initialize method! virtual void Initialize(); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; mutable ImageDataItemPointerArray m_Channels; mutable ImageDataItemPointerArray m_Volumes; mutable ImageDataItemPointerArray m_Slices; unsigned int m_Dimension; unsigned int* m_Dimensions; ImageDescriptor::Pointer m_ImageDescriptor; size_t *m_OffsetTable; ImageDataItemPointer m_CompleteData; // Image statistics Holder replaces the former implementation directly inside this class friend class ImageStatisticsHolder; StatisticsHolderPointer m_ImageStatistics; private: /** Stores all existing ImageReadAccessors */ std::vector m_Readers; /** Stores all existing ImageWriteAccessors */ std::vector m_Writers; /** Stores all existing ImageVtkAccessors */ std::vector m_VtkReaders; /** A mutex, which needs to be locked to manage m_Readers and m_Writers */ itk::SimpleFastMutexLock m_ReadWriteLock; /** A mutex, which needs to be locked to manage m_VtkReaders */ itk::SimpleFastMutexLock m_VtkReadersLock; }; /** * @brief Equal A function comparing two images for beeing equal in meta- and imagedata * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::Image& i1, const mitk::Image& i2) instead. * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - dimension of the images * - size of the images * - pixel type * - pixel values : pixel values are expected to be identical at each position ( for other options see mitk::CompareImageFilter ) * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ DEPRECATED (MITK_CORE_EXPORT bool Equal( const mitk::Image* leftHandSide, const mitk::Image* rightHandSide, ScalarType eps, bool verbose )); /** * @brief Equal A function comparing two images for beeing equal in meta- and imagedata * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - dimension of the images * - size of the images * - pixel type * - pixel values : pixel values are expected to be identical at each position ( for other options see mitk::CompareImageFilter ) * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITK_CORE_EXPORT bool Equal( const mitk::Image& leftHandSide, const mitk::Image& rightHandSide, ScalarType eps, bool verbose ); //} //##Documentation //## @brief Cast an itk::Image (with a specific type) to an mitk::Image. //## //## CastToMitkImage does not cast pixel types etc., just image data //## Needs "mitkImage.h" header included. //## If you get a compile error, try image.GetPointer(); //## @ingroup Adaptor //## \sa mitkITKImageImport template void CastToMitkImage(const itk::SmartPointer& itkimage, itk::SmartPointer& mitkoutputimage) { if(mitkoutputimage.IsNull()) { mitkoutputimage = mitk::Image::New(); } mitkoutputimage->InitializeByItk(itkimage.GetPointer()); mitkoutputimage->SetChannel(itkimage->GetBufferPointer()); } //##Documentation //## @brief Cast an itk::Image (with a specific type) to an mitk::Image. //## //## CastToMitkImage does not cast pixel types etc., just image data //## Needs "mitkImage.h" header included. //## If you get a compile error, try image.GetPointer(); //## @ingroup Adaptor //## \sa mitkITKImageImport template void CastToMitkImage(const ItkOutputImageType* itkimage, itk::SmartPointer& mitkoutputimage) { if(mitkoutputimage.IsNull()) { mitkoutputimage = mitk::Image::New(); } mitkoutputimage->InitializeByItk(itkimage); mitkoutputimage->SetChannel(itkimage->GetBufferPointer()); } } // namespace mitk #endif /* MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 */ diff --git a/Core/Code/DataManagement/mitkImagePixelReadAccessor.h b/Core/Code/DataManagement/mitkImagePixelReadAccessor.h index 252131513b..1fd6f79cf3 100644 --- a/Core/Code/DataManagement/mitkImagePixelReadAccessor.h +++ b/Core/Code/DataManagement/mitkImagePixelReadAccessor.h @@ -1,171 +1,171 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKIMAGEPIXELREADACCESSOR_H #define MITKIMAGEPIXELREADACCESSOR_H #include #include #include #include #include "mitkImageDataItem.h" #include "mitkPixelType.h" #include "mitkImage.h" #include "mitkImageReadAccessor.h" #include "mitkImagePixelAccessor.h" namespace mitk { class Image; typedef itk::SmartPointer ImagePointer; //##Documentation //## @brief Gives locked and index-based read access for a particular image part. //## The class provides several set- and get-methods, which allow an easy pixel access. //## It needs to know about pixel type and dimension of its image at compile time. //## @tparam TPixel defines the PixelType //## @tparam VDimension defines the dimension for accessing data //## @ingroup Data template class ImagePixelReadAccessor : public ImagePixelAccessor { friend class Image; public: typedef ImagePixelAccessor ImagePixelAccessorType; /** \brief Instantiates a mitk::ImageReadAccessor (see its doxygen page for more details) * \param Image::Pointer specifies the associated Image * \param ImageDataItem* specifies the allocated image part * \param OptionFlags properties from mitk::ImageAccessorBase::Options can be chosen and assembled with bitwise unification. * \throws mitk::Exception if the Constructor was created inappropriately * \throws mitk::MemoryIsLockedException if requested image area is exclusively locked and mitk::ImageAccessorBase::ExceptionIfLocked is set in OptionFlags * * Includes a check if typeid of PixelType coincides with templated TPixel * and a check if VDimension equals to the Dimension of the Image.*/ ImagePixelReadAccessor( ImagePointer iP, ImageDataItem* iDI = NULL, int OptionFlags = ImageAccessorBase::DefaultBehavior ) : ImagePixelAccessor(iP,iDI), m_ReadAccessor(iP, iDI, OptionFlags) { // Check if Dimensions are correct if(ImagePixelAccessor::m_ImageDataItem == NULL) { if(m_ReadAccessor.m_Image->GetDimension() != VDimension) mitkThrow() << "Invalid ImageAccessor: The Dimensions of ImageAccessor and Image are not equal. They have to be equal if an entire image is requested"; } else { if(ImagePixelAccessor::m_ImageDataItem->GetDimension() != VDimension) mitkThrow() << "Invalid ImageAccessor: The Dimensions of ImageAccessor and ImageDataItem are not equal."; } // Check if PixelType is correct if(!(m_ReadAccessor.m_Image->GetPixelType() == mitk::MakePixelType< itk::Image >()) ) { mitkThrow() << "Invalid ImageAccessor: PixelTypes of Image and ImageAccessor are not equal"; } } /** Destructor informs Image to unlock memory. */ virtual ~ImagePixelReadAccessor() { } /** Returns a const reference to the pixel at given index. */ const TPixel & GetPixelByIndex(const itk::Index& idx) const { unsigned int offset = ImagePixelAccessorType::GetOffset(idx); return *(((TPixel*)m_ReadAccessor.m_AddressBegin) + offset); } /** Extends GetPixel by integrating index validation to prevent overflow. * \throws mitk::Exception in case of overflow */ const TPixel & GetPixelByIndexSafe(const itk::Index& idx) const { unsigned int offset = ImagePixelAccessorType::GetOffset(idx); TPixel* targetAddress = ((TPixel*)m_ReadAccessor.m_AddressBegin) + offset; if(!(targetAddress >= m_ReadAccessor.m_AddressBegin && targetAddress < m_ReadAccessor.m_AddressEnd)) { mitkThrow() << "ImageAccessor Overflow: image access exceeds the requested image area at " << idx << "."; } return *targetAddress; } /** Returns a const reference to the pixel at given world coordinate - works only with three-dimensional ImageAccessor */ const TPixel & GetPixelByWorldCoordinates(mitk::Point3D position) { - Index3D itkIndex; + itk::Index<3> itkIndex; m_ReadAccessor.m_Image->GetGeometry()->WorldToIndex(position, itkIndex); return GetPixelByIndex( itkIndex); } /** Returns a const reference to the pixel at given world coordinate - works only with four-dimensional ImageAccessor */ const TPixel & GetPixelByWorldCoordinates(mitk::Point3D position, unsigned int timestep) { - Index3D itkIndex; + itk::Index<3> itkIndex; m_ReadAccessor.m_Image->GetGeometry()->WorldToIndex(position, itkIndex); if (m_ReadAccessor.m_Image->GetTimeSteps() < timestep) { timestep = m_ReadAccessor.m_Image->GetTimeSteps(); } itk::Index<4> itk4Index; for(int i=0; i<3; ++i) itk4Index[i] = itkIndex[i]; itk4Index[3] = timestep; return GetPixelByIndex( itk4Index ); } /** \brief Gives const access to the data. */ virtual inline const TPixel * GetConstData() { return (TPixel*) m_ReadAccessor.m_AddressBegin; } protected: // protected members private: ImageReadAccessor m_ReadAccessor; ImagePixelReadAccessor& operator=(const ImagePixelReadAccessor&); // Not implemented on purpose. ImagePixelReadAccessor(const ImagePixelReadAccessor&); }; } #endif // MITKIMAGEPIXELREADACCESSOR_H diff --git a/Core/Code/DataManagement/mitkImagePixelWriteAccessor.h b/Core/Code/DataManagement/mitkImagePixelWriteAccessor.h index ccc2c58002..1e79de3cc6 100644 --- a/Core/Code/DataManagement/mitkImagePixelWriteAccessor.h +++ b/Core/Code/DataManagement/mitkImagePixelWriteAccessor.h @@ -1,171 +1,171 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKIMAGEPIXELWRITEACCESSOR_H #define MITKIMAGEPIXELWRITEACCESSOR_H #include "mitkImagePixelAccessor.h" #include "mitkImageWriteAccessor.h" namespace mitk { //##Documentation //## @brief Gives locked and index-based write access for a particular image part. //## The class provides several set- and get-methods, which allow an easy pixel access. //## It needs to know about pixel type and dimension of its image at compile time. //## @tparam TPixel defines the PixelType //## @tparam VDimension defines the dimension for accessing data //## @ingroup Data template class ImagePixelWriteAccessor : public ImagePixelAccessor { friend class Image; public: typedef ImagePixelAccessor ImagePixelAccessorType; /** \brief Instantiates a mitk::ImageWriteAccessor (see its doxygen page for more details) * \param Image::Pointer specifies the associated Image * \param ImageDataItem* specifies the allocated image part * \param OptionFlags properties from mitk::ImageAccessorBase::Options can be chosen and assembled with bitwise unification. * \throws mitk::Exception if the Constructor was created inappropriately * \throws mitk::MemoryIsLockedException if requested image area is exclusively locked and mitk::ImageAccessorBase::ExceptionIfLocked is set in OptionFlags * * Includes a check if typeid of PixelType coincides with templated TPixel * and a check if VDimension equals to the Dimension of the Image.*/ ImagePixelWriteAccessor( ImagePointer iP, ImageDataItem* iDI = NULL, int OptionFlags = ImageAccessorBase::DefaultBehavior ) : ImagePixelAccessor(iP,iDI), m_WriteAccessor(iP , iDI, OptionFlags) { // Check if Dimensions are correct if(ImagePixelAccessor::m_ImageDataItem == NULL) { if(m_WriteAccessor.m_Image->GetDimension() != VDimension) mitkThrow() << "Invalid ImageAccessor: The Dimensions of ImageAccessor and Image are not equal. They have to be equal if an entire image is requested"; } else { if(ImagePixelAccessor::m_ImageDataItem->GetDimension() != VDimension) mitkThrow() << "Invalid ImageAccessor: The Dimensions of ImageAccessor and ImageDataItem are not equal."; } // Check if PixelType is correct if(!(m_WriteAccessor.m_Image->GetPixelType() == mitk::MakePixelType< itk::Image >()) ) { mitkThrow() << "Invalid ImageAccessor: PixelTypes of Image and ImageAccessor are not equal"; } } /** \brief Gives full data access. */ virtual inline TPixel * GetData() { return (TPixel*) m_WriteAccessor.m_AddressBegin; } /// Sets a pixel value at given index. void SetPixelByIndex(const itk::Index& idx, const TPixel & value) { unsigned int offset = ImagePixelAccessor::GetOffset(idx); *(((TPixel*)m_WriteAccessor.m_AddressBegin) + offset) = value; } /** Extends SetPixel by integrating index validation to prevent overflow. */ void SetPixelByIndexSafe(const itk::Index& idx, const TPixel & value) { unsigned int offset = ImagePixelAccessorType::GetOffset(idx); TPixel* targetAddress = ((TPixel*)m_WriteAccessor.m_AddressBegin) + offset; if(targetAddress >= m_WriteAccessor.m_AddressBegin && targetAddress < m_WriteAccessor.m_AddressEnd) { *targetAddress = value; } else { //printf("image dimensions = %d, %d, %d\n", imageDims[0], imageDims[1], imageDims[2]); //printf("m_AddressBegin: %p, m_AddressEnd: %p, offset: %u\n", m_WriteAccessor.m_AddressBegin, m_WriteAccessor.m_AddressEnd, offset); mitkThrow() << "ImageAccessor Overflow: image access exceeds the requested image area at " << idx << "."; } } /** Returns a const reference to the pixel at given index. */ const TPixel & GetPixelByIndex(const itk::Index& idx) const { unsigned int offset = ImagePixelAccessorType::GetOffset(idx); return *(((TPixel*)m_WriteAccessor.m_AddressBegin) + offset); } /** Extends GetPixel by integrating index validation to prevent overflow. * \throws mitk::Exception in case of overflow */ const TPixel & GetPixelByIndexSafe(const itk::Index& idx) const { unsigned int offset = ImagePixelAccessorType::GetOffset(idx); TPixel* targetAddress = ((TPixel*)m_WriteAccessor.m_AddressBegin) + offset; if(!(targetAddress >= m_WriteAccessor.m_AddressBegin && targetAddress < m_WriteAccessor.m_AddressEnd)) { mitkThrow() << "ImageAccessor Overflow: image access exceeds the requested image area at " << idx << "."; } return *targetAddress; } /** Returns a const reference to the pixel at given world coordinate - works only with three-dimensional ImageAccessor */ const TPixel & GetPixelByWorldCoordinates(mitk::Point3D position) { - Index3D itkIndex; + itk::Index<3> itkIndex; m_WriteAccessor.m_Image->GetGeometry()->WorldToIndex(position, itkIndex); return GetPixelByIndex( itkIndex); } /** Returns a reference to the pixel at given world coordinate */ void SetPixelByWorldCoordinates(const mitk::Point3D&, const TPixel & value, unsigned int timestep = 0); virtual ~ImagePixelWriteAccessor() { } private: ImageWriteAccessor m_WriteAccessor; ImagePixelWriteAccessor& operator=(const ImagePixelWriteAccessor&); // Not implemented on purpose. ImagePixelWriteAccessor(const ImagePixelWriteAccessor &); }; } #endif // MITKIMAGEWRITEACCESSOR_H diff --git a/Core/Code/DataManagement/mitkLevelWindow.h b/Core/Code/DataManagement/mitkLevelWindow.h index f6061cc0d6..e2b7f796f5 100644 --- a/Core/Code/DataManagement/mitkLevelWindow.h +++ b/Core/Code/DataManagement/mitkLevelWindow.h @@ -1,241 +1,241 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef LEVELWINDOW_H_HEADER_INCLUDED_C1F4F02C #define LEVELWINDOW_H_HEADER_INCLUDED_C1F4F02C -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include namespace mitk { class Image; /** * @brief The LevelWindow class Class to store level/window values. * * Current min and max value are stored in m_LowerWindowBound and m_UpperWindowBound. * The maximum and minimum of valid value range is stored in m_RangeMin and m_RangeMax. * m_DefaultLevel amd m_DefaultWindow store the initial Level/Window values for the image. * m_DefaultRangeMin and m_DefaultRangeMax store the initial minrange and maxrange for the image. * * See documentation of SetAuto for information on how the level window is initialized from an image. * * @ingroup DataManagement * * @note If you want to apply the mitk::LevelWindow to an mitk::Image, make sure * to use the mitk::LevelWindowProperty and set the mitk::RenderingModeProperty * to a mode which supports level window (e.g. LEVELWINDOW_COLOR). * Make sure to check the documentation of the mitk::RenderingModeProperty. For a * code example how to use the mitk::LevelWindowProperty check the * mitkImageVtkMapper2DLevelWindowTest.cpp in Core\Code\Testing. */ class MITK_CORE_EXPORT LevelWindow { public: LevelWindow(ScalarType level=127.5, ScalarType window=255.0); LevelWindow(const mitk::LevelWindow& levWin); virtual ~LevelWindow(); /*! * \brief method that returns the level value, i.e. the center of * the current grey value interval */ ScalarType GetLevel() const; /*! * \brief returns the current window size, i.e the range size of the current grey value interval */ ScalarType GetWindow() const; /*! * \brief method returns the default level value for the image */ ScalarType GetDefaultLevel() const; /*! * \brief returns the default window size for the image */ ScalarType GetDefaultWindow() const; /*! * \brief Resets the level and the window value to the default values */ void ResetDefaultLevelWindow(); /*! * Returns the minimum Value of the window */ ScalarType GetLowerWindowBound() const; /*! * Returns the upper window bound value of the window */ ScalarType GetUpperWindowBound() const; /*! * To set the level and the window value */ void SetLevelWindow(ScalarType level, ScalarType window, bool expandRangesIfNecessary = true); /*! * Set the lower and upper bound of the window */ void SetWindowBounds(ScalarType lowerBound, ScalarType upperBound, bool expandRangesIfNecessary = true); /*! * sets the window to its maximum Size in scaleRange */ void SetToMaxWindowSize(); /*! * Set the range minimum and maximum value */ void SetRangeMinMax(ScalarType min, ScalarType max); /*! * Get the range minimum value */ ScalarType GetRangeMin() const; /*! * Get the range maximum value */ ScalarType GetRangeMax() const; /*! * Get the default range minimum value */ ScalarType GetDefaultLowerBound() const; /*! * Get the default range maximum value */ ScalarType GetDefaultUpperBound() const; /*! * \brief the default min and max range for image will be reset */ void ResetDefaultRangeMinMax(); /**! * \brief returns the size of the grey value range */ ScalarType GetRange() const; /*! * set the default level and window value */ void SetDefaultLevelWindow(ScalarType level, ScalarType window); /*! * set the default Bounderies */ void SetDefaultBoundaries(ScalarType low, ScalarType up); /**! * \brief sets level/window to the min/max greyvalues of the given Image */ void SetAuto(const Image* image, bool tryPicTags = true, bool guessByCentralSlice = true); /** * If a level window is set to fixed, the set and get methods won't accept * modifications to the level window settings anymore. This behaviour can * be turned of by setting fixed to false; */ void SetFixed( bool fixed ); /** * Returns whether the level window settings are fixed (@see SetFixed(bool)) or not */ bool GetFixed() const; /** * Returns whether the level window settings are fixed (@see SetFixed(bool)) or not */ bool IsFixed() const; /*! * \brief equality operator implementation that allows to compare two level windows */ virtual bool operator==(const LevelWindow& levWin) const; /*! * \brief non equality operator implementation that allows to compare two level windows */ virtual bool operator!=(const LevelWindow& levWin) const; /*! * \brief implementation necessary because operator made * private in itk::Object */ virtual LevelWindow& operator=(const LevelWindow& levWin); protected: /*! * lower bound of current window */ ScalarType m_LowerWindowBound; /*! * upper bound of current window */ ScalarType m_UpperWindowBound; /*! * minimum gray value of the window */ ScalarType m_RangeMin; /*! * maximum gray value of the window */ ScalarType m_RangeMax; /*! * default minimum gray value of the window */ ScalarType m_DefaultLowerBound; /*! * default maximum gray value of the window */ ScalarType m_DefaultUpperBound; /*! * Defines whether the level window settings may be changed after * initialization or not. */ bool m_Fixed; /*! * confidence tests * * if m_LowerWindowBound > m_UpperWindowBound, then the values for m_LowerWindowBound and m_UpperWindowBound will be exchanged * * if m_LowerWindowBound < m_RangeMin, m_LowerWindowBound will be set to m_RangeMin. m_UpperWindowBound will be decreased the same as m_LowerWindowBound will be increased, but minimum value for m_UpperWindowBound is also m_RangeMin. * * if m_UpperWindowBound > m_RangeMax, m_UpperWindowBound will be set to m_RangeMax. m_LowerWindowBound will be increased the same as m_UpperWindowBound will be decreased, but maximum value for m_LowerWindowBound is also m_RangeMax. * */ inline void EnsureConsistency(); }; } // namespace mitk #endif /* LEVELWINDOW_H_HEADER_INCLUDED_C1F4F02C */ diff --git a/Core/Code/DataManagement/mitkLine.h b/Core/Code/DataManagement/mitkLine.h index 991d58b3d7..7a4fafd853 100644 --- a/Core/Code/DataManagement/mitkLine.h +++ b/Core/Code/DataManagement/mitkLine.h @@ -1,427 +1,427 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKLINE_H_HEADER_INCLUDED_C19C01E2 #define MITKLINE_H_HEADER_INCLUDED_C19C01E2 -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include #include #include #include namespace mitk { //##Documentation //## @brief Descibes a line //## @ingroup Geometry template class Line { public: Line() { m_Point.Fill(0); m_Direction.Fill(0); }; //##Documentation //## @brief Define line by point and direction //## //## Length of direction defines the the length of the line Line( const itk::Point& point, const itk::Vector& direction ) { this->m_Point = point; this->m_Direction = direction; } //##Documentation //## @brief Get start point of the line const itk::Point& GetPoint() const { return m_Point; } //##Documentation //## @brief Get start point of the line itk::Point& GetPoint() { return m_Point; } //##Documentation //## @brief Get point on the line with parameter @a t //## //## @return m_Point+t*m_Direction const itk::Point GetPoint(TCoordRep t) const { return m_Point+m_Direction*t; } //##Documentation //## @brief Set/change start point of the line void SetPoint( const itk::Point& point1 ) { itk::Point point2; point2 = m_Point + m_Direction; m_Point = point1; m_Direction = point2.GetVectorFromOrigin() - point1.GetVectorFromOrigin(); } //##Documentation //## @brief Get the direction vector of the line const itk::Vector& GetDirection() const { return m_Direction; } //##Documentation //## @brief Get the direction vector of the line itk::Vector& GetDirection() { return m_Direction; } //##Documentation //## @brief Set the direction vector of the line void SetDirection( const itk::Vector& direction ) { m_Direction = direction; } //##Documentation //## @brief Define line by point and direction //## //## Length of direction defines the the length of the line void Set( const itk::Point& point, const itk::Vector& direction ) { this->m_Point = point; this->m_Direction = direction; } //##Documentation //## @brief Define line by two points void SetPoints( const itk::Point& point1, const itk::Point& point2 ) { this->m_Point = point1; //this->m_Direction.sub( point2, point1 ); m_Direction = point2 - point1; } //##Documentation //## @brief Set/change start point of the line void SetPoint1( const itk::Point& point1 ) { itk::Vector point2; point2 = m_Point + m_Direction; m_Point = point1; m_Direction = point2 - point1; } //##Documentation //## @brief Get start point of the line const itk::Point& GetPoint1() const { return m_Point; } //##Documentation //## @brief Set/change end point of the line void SetPoint2( const itk::Point& point2 ) { m_Direction = point2 - m_Point; } //##Documentation //## @brief Get end point of the line itk::Point GetPoint2() const { itk::Point point2; point2 = m_Point+m_Direction; return point2; } //##Documentation //## @brief Transform the line with a Transform void Transform(itk::Transform& transform) { m_Direction = transform.TransformVector(m_Direction); m_Point = transform.TransformPoint(m_Point); } //##Documentation //## @brief Transform the line with a matrix //## //## Only the direction will be changed, not the start point. void Transform( const itk::Matrix& matrix ) { m_Direction = matrix*m_Direction; } //##Documentation //## @brief Distance between two lines double Distance( const Line& line ) const; //##Documentation //## @brief Distance of a point from the line double Distance( const itk::Point& point ) const { itk::Vector diff; diff = Project(point)-point; return diff.GetNorm(); } //##Documentation //## @brief Project a point on the line itk::Point Project( const itk::Point& point ) const { if(m_Direction.GetNorm()==0) return this->m_Point; itk::Vector diff; diff = point-this->m_Point; itk::Vector normalizedDirection = m_Direction; normalizedDirection.Normalize(); normalizedDirection *= dot_product(diff.GetVnlVector(), normalizedDirection.GetVnlVector()); return this->m_Point + normalizedDirection; } //##Documentation //## @brief Test if a point is part of the line //## //## Length of the direction vector defines the length of the line bool IsPartOfStraightLine( const itk::Point& point ) const { if( Distance( point ) > eps ) return false; itk::Vector diff; diff = point - this->m_Point; if( diff*m_Direction < 0 ) return false; if( diff.GetSquaredNorm() <= m_Direction.GetSquaredNorm() ) return true; return false; } //##Documentation //## @brief Test if a point is part of the line (line having infinite length) bool IsPartOfLine( const itk::Point& point ) const { if ( Distance( point ) < eps ) return true; return false; } //##Documentation //## @brief Test if a lines is parallel to this line bool IsParallel( const Line& line) const { vnl_vector normal; normal = vnl_cross_3d( m_Direction.GetVnlVector(), line.GetDirection().GetVnlVector() ); if ( normal.squared_magnitude() < eps ) return true; return false; } //##Documentation //## @brief Test if a line is part of the line (line having infinite length) bool IsPartOfLine( const Line& line ) const { return ( Distance( line.GetPoint() ) < 0 ) && ( IsParallel( line ) ); } //##Documentation //## @brief Test if the two lines are identical //## //## Start point and direction and length of direction vector must be //## equal for identical lines. bool operator==( const Line& line ) const { itk::Vector diff; diff = GetPoint1()-line.GetPoint1(); if(diff.GetSquaredNorm() > eps) return false; diff = GetPoint2()-line.GetPoint2(); if(diff.GetSquaredNorm() > eps) return false; return true; } //##Documentation //## @brief Set the line by another line inline const Line& operator=( const Line& line ) { m_Point = line.GetPoint(); m_Direction = line.GetDirection(); return *this; } //##Documentation //## @brief Test if two lines are not identical //## //## \sa operator== bool operator!=( const Line& line ) const { return !((*this)==line); } //##Documentation //## @brief Calculates the intersection points of a straight line in 2D //## with a rectangle //## //## @param x1,y1,x2,y2 rectangle //## @param p,d straight line: p point on it, d direction of line //## @param s1 first intersection point (valid only if s_num>0) //## @param s2 second intersection point (valid only if s_num==2) //## @return number of intersection points (0<=s_num<=2) static int RectangleLineIntersection( TCoordRep x1, TCoordRep y1, TCoordRep x2, TCoordRep y2, itk::Point< TCoordRep, 2 > p, itk::Vector< TCoordRep, 2 > d, itk::Point< TCoordRep, 2 > &s1, itk::Point< TCoordRep, 2 > &s2 ) { int s_num; TCoordRep t; s_num=0; /*test if intersecting with the horizontal axis*/ if(fabs(d[0])>eps) { t=(x1-p[0])/d[0]; itk::Point l=p+d*t; if((l[1]>=y1) && (l[1]eps) { t=(x2-p[0])/d[0]; itk::Point l=p+d*t; if((l[1]>=y1) && (l[1]eps) { t=(y1-p[1])/d[1]; itk::Point l=p+d*t; if((l[0]>=x1) && (l[0]eps) { t=(y2-p[1])/d[1]; itk::Point l=p+d*t; if((l[0]>=x1) && (l[0]0) * \param s2 second intersection point (valid only if s_num==2) * \return number of intersection points (0<=s_num<=2) */ static int BoxLineIntersection( TCoordRep x1, TCoordRep y1, TCoordRep z1, TCoordRep x2, TCoordRep y2, TCoordRep z2, itk::Point< TCoordRep, 3 > p, itk::Vector< TCoordRep, 3 > d, itk::Point< TCoordRep, 3 > &s1, itk::Point< TCoordRep, 3 > &s2 ) { int num = 0; ScalarType box[6]; box[0] = x1; box[1] = x2; box[2] = y1; box[3] = y2; box[4] = z1; box[5] = z2; itk::Point< TCoordRep, 3 > point; int i, j; for ( i = 0; i < 6; ++i ) { j = i / 2; if ( fabs( d[j] ) > eps ) { ScalarType lambda = (box[i] - p[j]) / d[j]; point = p + d * lambda; int k = (j + 1) % 3; int l = (j + 2) % 3; if ( (point[k] >= box[k*2]) && (point[k] <= box[k*2+1]) && (point[l] >= box[l*2]) && (point[l] <= box[l*2+1]) ) { if ( num == 0 ) { s1 = point; } else { s2 = point; } ++num; } } } return num; } protected: itk::Point m_Point; itk::Vector m_Direction; }; typedef Line Line3D; } // namespace mitk #endif /* MITKLINE_H_HEADER_INCLUDED_C19C01E2 */ diff --git a/Core/Code/DataManagement/mitkMatrix.h b/Core/Code/DataManagement/mitkMatrix.h new file mode 100644 index 0000000000..4e682b903e --- /dev/null +++ b/Core/Code/DataManagement/mitkMatrix.h @@ -0,0 +1,191 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKMATRIX_H_ +#define MITKMATRIX_H_ + +#include + +#include "mitkNumericConstants.h" +#include "mitkArray.h" +#include "mitkEqual.h" + +namespace mitk +{ + + template< class T, unsigned int NRows = 3, unsigned int NColumns = 3 > + class Matrix : public itk::Matrix + { + public: + + /** Standard class typedefs. */ + typedef Matrix Self; + + typedef typename itk::Matrix::InternalMatrixType InternalMatrixType; + + /** Default constructor. */ + explicit Matrix() : + itk::Matrix() {} + + /** Copy constructor. */ + explicit Matrix(const Matrix & matrix) : + itk::Matrix(matrix) {} + + /** Copy constructor for itk compatibility */ + Matrix(const itk::Matrix & matrix) : + itk::Matrix(matrix) {} + + /**For every operator=, there should be an equivalent copy constructor. */ + inline Matrix(const vnl_matrix< T > & matrix) : + itk::Matrix(matrix) {} + + /**For every operator=, there should be an equivalent copy constructor. */ + inline explicit Matrix(InternalMatrixType & matrix) : + itk::Matrix(matrix) {} + + /** + * Necessary because otherwise operator= is default operator= from Matrix. + */ + using itk::Matrix::operator=; + + + /** + * Copies the elements from array array to this. + * Note that this method will assign doubles to floats without complaining! + * + * @param array the array whose values shall be copied. Must overload [] operator. + */ + template + void FillMatrix(const ArrayType& array) + { + for (unsigned i = 0; i < NRows; i++) + { + for (unsigned j = 0; j < NColumns; j++) + { + (*this)[i][j] = array[i][j]; + } + } + }; + + /** + * Warning: matrix must have same dimension as Matrix + */ + template + void ToArray(MatrixType matrix) const + { + for (unsigned i = 0; i < NRows; i++) + { + for (unsigned j = 0; j < NColumns; j++) + { + matrix[i][j] = (*this)[i][j]; + } + } + } + + + }; + + typedef Matrix Matrix2D; + typedef Matrix Matrix3D; + typedef Matrix Matrix4D; + + + + /*! + \brief Check for matrix equality with a user defined accuracy. As an equality metric the root mean squared error (RMS) of all elements is calculated. + \param matrix1 first vnl matrix + \param matrix2 second vnl matrix + \param epsilon user defined accuracy bounds + */ + template + 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 + \param 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; r + inline bool MatrixEqualElementWise(const itk::Matrix& matrix1,const itk::Matrix& matrix2,mitk::ScalarType epsilon=mitk::eps) + { + return mitk::MatrixEqualElementWise(matrix1.GetVnlMatrix(),matrix2.GetVnlMatrix(),epsilon); + } + +} + + +#endif /* MITKMATRIX_H_ */ diff --git a/Core/Code/DataManagement/mitkVector.cpp b/Core/Code/DataManagement/mitkNumericConstants.cpp similarity index 78% copy from Core/Code/DataManagement/mitkVector.cpp copy to Core/Code/DataManagement/mitkNumericConstants.cpp index 3a6f50b86b..58a1382ec5 100644 --- a/Core/Code/DataManagement/mitkVector.cpp +++ b/Core/Code/DataManagement/mitkNumericConstants.cpp @@ -1,21 +1,23 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include "mitkVector.h" +#include "mitkNumericConstants.h" +#include "vnl/vnl_math.h" +#include const mitk::ScalarType mitk::eps = vnl_math::eps*100; const mitk::ScalarType mitk::sqrteps = vnl_math::sqrteps; -extern const mitk::ScalarType mitk::large = std::numeric_limits::max(); +extern const mitk::ScalarType mitk::large = std::numeric_limits::max(); diff --git a/Core/Code/DataManagement/mitkVector.cpp b/Core/Code/DataManagement/mitkNumericConstants.h similarity index 61% copy from Core/Code/DataManagement/mitkVector.cpp copy to Core/Code/DataManagement/mitkNumericConstants.h index 3a6f50b86b..8ebbb96b6c 100644 --- a/Core/Code/DataManagement/mitkVector.cpp +++ b/Core/Code/DataManagement/mitkNumericConstants.h @@ -1,21 +1,33 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include "mitkVector.h" -const mitk::ScalarType mitk::eps = vnl_math::eps*100; -const mitk::ScalarType mitk::sqrteps = vnl_math::sqrteps; -extern const mitk::ScalarType mitk::large = std::numeric_limits::max(); +#ifndef MITKNUMERICCONSTANTS_H_ +#define MITKNUMERICCONSTANTS_H_ + +#include + +namespace mitk { + +typedef double ScalarType; + +MITK_CORE_EXPORT extern const ScalarType eps; +MITK_CORE_EXPORT extern const ScalarType sqrteps; +MITK_CORE_EXPORT extern const double large; + +} + +#endif // MITKNUMERICCONSTANTS_H_ diff --git a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorEvent.cpp b/Core/Code/DataManagement/mitkNumericTypes.h similarity index 51% copy from Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorEvent.cpp copy to Core/Code/DataManagement/mitkNumericTypes.h index 458c2ed902..ec9b99c320 100644 --- a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorEvent.cpp +++ b/Core/Code/DataManagement/mitkNumericTypes.h @@ -1,25 +1,40 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include "mitkSpaceNavigatorEvent.h" -#include "mitkInteractionConst.h" +#ifndef MITKNUMERICTYPES_H_ +#define MITKNUMERICTYPES_H_ + + +#include "mitkNumericConstants.h" +#include "mitkQuaternion.h" +#include "mitkAffineTransform3D.h" +#include "mitkPoint.h" #include "mitkVector.h" +#include "mitkMatrix.h" +#include "mitkEqual.h" + + +// this include hold the old deprecated ways to convert from itk 2 vtk and the likes. +// calls to these functions shall be removed in future bugsquashings so that this include can be removed. +#include "mitkVectorDeprecated.h" + + + + + -mitk::SpaceNavigatorEvent::SpaceNavigatorEvent(int buttonState, const Vector3D& translation, const Vector3D& rotation, const ScalarType& angle) -: Event(NULL, mitk::Type_SpaceNavigatorInput, BS_NoButton, buttonState, Key_none), m_Translation(translation), m_Rotation(rotation), m_Angle(angle) -{ -} +#endif /* MITKNUMERICTYPES_H_ */ diff --git a/Core/Code/DataManagement/mitkPlaneGeometry.cpp b/Core/Code/DataManagement/mitkPlaneGeometry.cpp index f2ff00020f..8ad0309caf 100644 --- a/Core/Code/DataManagement/mitkPlaneGeometry.cpp +++ b/Core/Code/DataManagement/mitkPlaneGeometry.cpp @@ -1,916 +1,916 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlaneGeometry.h" #include "mitkPlaneOperation.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include #include namespace mitk { PlaneGeometry::PlaneGeometry() : m_ScaleFactorMMPerUnitX( 1.0 ), m_ScaleFactorMMPerUnitY( 1.0 ), m_ReferenceGeometry( NULL ) { Initialize(); } PlaneGeometry::~PlaneGeometry() { } PlaneGeometry::PlaneGeometry(const PlaneGeometry& other) : Superclass(other), m_ScaleFactorMMPerUnitX( other.m_ScaleFactorMMPerUnitX), m_ScaleFactorMMPerUnitY( other.m_ScaleFactorMMPerUnitY), m_ReferenceGeometry( other.m_ReferenceGeometry ) { } void PlaneGeometry::EnsurePerpendicularNormal(mitk::AffineTransform3D *transform) { //ensure row(2) of transform to be perpendicular to plane, keep length. VnlVector normal = vnl_cross_3d( transform->GetMatrix().GetVnlMatrix().get_column(0), transform->GetMatrix().GetVnlMatrix().get_column(1) ); normal.normalize(); ScalarType len = transform->GetMatrix() .GetVnlMatrix().get_column(2).two_norm(); if (len==0) len = 1; normal*=len; Matrix3D matrix = transform->GetMatrix(); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); } void PlaneGeometry::PreSetIndexToWorldTransform(mitk::AffineTransform3D *transform) { EnsurePerpendicularNormal(transform); } void PlaneGeometry::PreSetBounds(const BoundingBox::BoundsArrayType &bounds) { //currently the unit rectangle must be starting at the origin [0,0] assert(bounds[0]==0); assert(bounds[2]==0); //the unit rectangle must be two-dimensional assert(bounds[1]>0); assert(bounds[3]>0); } void PlaneGeometry::IndexToWorld( const Point2D &pt_units, Point2D &pt_mm ) const { pt_mm[0]=m_ScaleFactorMMPerUnitX*pt_units[0]; pt_mm[1]=m_ScaleFactorMMPerUnitY*pt_units[1]; } void PlaneGeometry::WorldToIndex( const Point2D &pt_mm, Point2D &pt_units ) const { pt_units[0]=pt_mm[0]*(1.0/m_ScaleFactorMMPerUnitX); pt_units[1]=pt_mm[1]*(1.0/m_ScaleFactorMMPerUnitY); } void PlaneGeometry::IndexToWorld( const Point2D & /*atPt2d_units*/, const Vector2D &vec_units, Vector2D &vec_mm) const { MITK_WARN<<"Warning! Call of the deprecated function PlaneGeometry::IndexToWorld(point, vec, vec). Use PlaneGeometry::IndexToWorld(vec, vec) instead!"; this->IndexToWorld(vec_units, vec_mm); } void PlaneGeometry::IndexToWorld(const Vector2D &vec_units, Vector2D &vec_mm) const { vec_mm[0] = m_ScaleFactorMMPerUnitX * vec_units[0]; vec_mm[1] = m_ScaleFactorMMPerUnitY * vec_units[1]; } void PlaneGeometry::WorldToIndex( const Point2D & /*atPt2d_mm*/, const Vector2D &vec_mm, Vector2D &vec_units) const { MITK_WARN<<"Warning! Call of the deprecated function PlaneGeometry::WorldToIndex(point, vec, vec). Use PlaneGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } void PlaneGeometry::WorldToIndex( const Vector2D &vec_mm, Vector2D &vec_units) const { vec_units[0] = vec_mm[0] * ( 1.0 / m_ScaleFactorMMPerUnitX ); vec_units[1] = vec_mm[1] * ( 1.0 / m_ScaleFactorMMPerUnitY ); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const Vector3D & spacing, PlaneGeometry::PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated ) { AffineTransform3D::Pointer transform; transform = AffineTransform3D::New(); AffineTransform3D::MatrixType matrix; AffineTransform3D::MatrixType::InternalMatrixType &vnlmatrix = matrix.GetVnlMatrix(); vnlmatrix.set_identity(); vnlmatrix(0,0) = spacing[0]; vnlmatrix(1,1) = spacing[1]; vnlmatrix(2,2) = spacing[2]; transform->SetIdentity(); transform->SetMatrix(matrix); InitializeStandardPlane(width, height, transform.GetPointer(), planeorientation, zPosition, frontside, rotated); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const AffineTransform3D* transform, PlaneGeometry::PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated ) { Superclass::Initialize(); //construct standard view Point3D origin; VnlVector rightDV(3), bottomDV(3); origin.Fill(0); int normalDirection; switch(planeorientation) { case Axial: if(frontside) { if(rotated==false) { FillVector3D(origin, 0, 0, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else { FillVector3D(origin, width, height, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } else { if(rotated==false) { FillVector3D(origin, width, 0, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else { FillVector3D(origin, 0, height, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } normalDirection = 2; break; case Frontal: if(frontside) { if(rotated==false) { FillVector3D(origin, 0, zPosition, 0); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else { FillVector3D(origin, width, zPosition, height); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if(rotated==false) { FillVector3D(origin, width, zPosition, 0); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else { FillVector3D(origin, 0, zPosition, height); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 1; break; case Sagittal: if(frontside) { if(rotated==false) { FillVector3D(origin, zPosition, 0, 0); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, 1); } else { FillVector3D(origin, zPosition, width, height); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if(rotated==false) { FillVector3D(origin, zPosition, width, 0); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, 1); } else { FillVector3D(origin, zPosition, 0, height); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 0; break; default: itkExceptionMacro("unknown PlaneOrientation"); } if ( transform != NULL ) { origin = transform->TransformPoint( origin ); rightDV = transform->TransformVector( rightDV ); bottomDV = transform->TransformVector( bottomDV ); } ScalarType bounds[6]= { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); if ( transform == NULL ) { this->SetMatrixByVectors( rightDV, bottomDV ); } else { this->SetMatrixByVectors( rightDV, bottomDV, transform->GetMatrix().GetVnlMatrix() .get_column(normalDirection).magnitude() ); } this->SetOrigin(origin); } void PlaneGeometry::InitializeStandardPlane( const BaseGeometry *geometry3D, PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated ) { this->SetReferenceGeometry( const_cast< BaseGeometry * >( geometry3D ) ); ScalarType width, height; const BoundingBox::BoundsArrayType& boundsarray = geometry3D->GetBoundingBox()->GetBounds(); Vector3D originVector; FillVector3D(originVector, boundsarray[0], boundsarray[2], boundsarray[4]); if(geometry3D->GetImageGeometry()) { FillVector3D( originVector, originVector[0] - 0.5, originVector[1] - 0.5, originVector[2] - 0.5 ); } switch(planeorientation) { case Axial: width = geometry3D->GetExtent(0); height = geometry3D->GetExtent(1); break; case Frontal: width = geometry3D->GetExtent(0); height = geometry3D->GetExtent(2); break; case Sagittal: width = geometry3D->GetExtent(1); height = geometry3D->GetExtent(2); break; default: itkExceptionMacro("unknown PlaneOrientation"); } InitializeStandardPlane( width, height, geometry3D->GetIndexToWorldTransform(), planeorientation, zPosition, frontside, rotated ); ScalarType bounds[6]= { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); Point3D origin; originVector = geometry3D->GetIndexToWorldTransform() ->TransformVector( originVector ); origin = GetOrigin() + originVector; SetOrigin(origin); } void PlaneGeometry::InitializeStandardPlane( const BaseGeometry *geometry3D, bool top, PlaneOrientation planeorientation, bool frontside, bool rotated ) { ScalarType zPosition; switch(planeorientation) { case Axial: zPosition = (top ? 0.5 : geometry3D->GetExtent(2)-1+0.5); break; case Frontal: zPosition = (top ? 0.5 : geometry3D->GetExtent(1)-1+0.5); break; case Sagittal: zPosition = (top ? 0.5 : geometry3D->GetExtent(0)-1+0.5); break; default: itkExceptionMacro("unknown PlaneOrientation"); } InitializeStandardPlane( geometry3D, planeorientation, zPosition, frontside, rotated ); } void PlaneGeometry::InitializeStandardPlane( const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing ) { InitializeStandardPlane( rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing ); } void PlaneGeometry::InitializeStandardPlane( const VnlVector& rightVector, const VnlVector &downVector, const Vector3D *spacing ) { ScalarType width = rightVector.magnitude(); ScalarType height = downVector.magnitude(); InitializeStandardPlane( width, height, rightVector, downVector, spacing ); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing ) { InitializeStandardPlane( width, height, rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing ); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing ) { assert(width > 0); assert(height > 0); VnlVector rightDV = rightVector; rightDV.normalize(); VnlVector downDV = downVector; downDV.normalize(); VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); if(spacing!=NULL) { rightDV *= (*spacing)[0]; downDV *= (*spacing)[1]; normal *= (*spacing)[2]; } AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightDV); matrix.GetVnlMatrix().set_column(1, downDV); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); ScalarType bounds[6] = { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); this->SetIndexToWorldTransform( transform ); } void PlaneGeometry::InitializePlane( const Point3D &origin, const Vector3D &normal ) { VnlVector rightVectorVnl(3), downVectorVnl; if( Equal( normal[1], 0.0f ) == false ) { FillVector3D( rightVectorVnl, 1.0f, -normal[0]/normal[1], 0.0f ); rightVectorVnl.normalize(); } else { FillVector3D( rightVectorVnl, 0.0f, 1.0f, 0.0f ); } downVectorVnl = vnl_cross_3d( normal.GetVnlVector(), rightVectorVnl ); downVectorVnl.normalize(); InitializeStandardPlane( rightVectorVnl, downVectorVnl ); SetOrigin(origin); } void PlaneGeometry::SetMatrixByVectors( const VnlVector &rightVector, const VnlVector &downVector, ScalarType thickness ) { VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); normal *= thickness; AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightVector); matrix.GetVnlMatrix().set_column(1, downVector); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); SetIndexToWorldTransform(transform); } Vector3D PlaneGeometry::GetNormal() const { Vector3D frontToBack; frontToBack.SetVnlVector( this->GetIndexToWorldTransform() ->GetMatrix().GetVnlMatrix().get_column(2) ); return frontToBack; } VnlVector PlaneGeometry::GetNormalVnl() const { return this->GetIndexToWorldTransform() ->GetMatrix().GetVnlMatrix().get_column(2); } ScalarType PlaneGeometry::DistanceFromPlane( const Point3D &pt3d_mm ) const { return fabs(SignedDistance( pt3d_mm )); } ScalarType PlaneGeometry::SignedDistance( const Point3D &pt3d_mm ) const { return SignedDistanceFromPlane(pt3d_mm); } //Function from Geometry2D // mitk::ScalarType // PlaneGeometry::SignedDistance(const mitk::Point3D& pt3d_mm) const //{ // Point3D projectedPoint; // Project(pt3d_mm, projectedPoint); // Vector3D direction = pt3d_mm-projectedPoint; // ScalarType distance = direction.GetNorm(); // if(IsAbove(pt3d_mm) == false) // distance*=-1.0; // return distance; //} bool PlaneGeometry::IsAbove( const Point3D &pt3d_mm , bool considerBoundingBox) const { if(considerBoundingBox) { Point3D pt3d_units; BaseGeometry::WorldToIndex(pt3d_mm, pt3d_units); return (pt3d_units[2] > this->GetBoundingBox()->GetBounds()[4]); } else return SignedDistanceFromPlane(pt3d_mm) > 0; } bool PlaneGeometry::IntersectionLine( const PlaneGeometry* plane, Line3D& crossline ) const { Vector3D normal = this->GetNormal(); normal.Normalize(); Vector3D planeNormal = plane->GetNormal(); planeNormal.Normalize(); Vector3D direction = itk::CrossProduct( normal, planeNormal ); if ( direction.GetSquaredNorm() < eps ) return false; crossline.SetDirection( direction ); double N1dN2 = normal * planeNormal; double determinant = 1.0 - N1dN2 * N1dN2; Vector3D origin = this->GetOrigin().GetVectorFromOrigin(); Vector3D planeOrigin = plane->GetOrigin().GetVectorFromOrigin(); double d1 = normal * origin; double d2 = planeNormal * planeOrigin; double c1 = ( d1 - d2 * N1dN2 ) / determinant; double c2 = ( d2 - d1 * N1dN2 ) / determinant; Vector3D p = normal * c1 + planeNormal * c2; crossline.GetPoint().GetVnlVector() = p.GetVnlVector(); return true; } unsigned int PlaneGeometry::IntersectWithPlane2D( const PlaneGeometry* plane, Point2D& lineFrom, Point2D &lineTo ) const { Line3D crossline; if ( this->IntersectionLine( plane, crossline ) == false ) return 0; Point2D point2; Vector2D direction2; this->Map( crossline.GetPoint(), point2 ); this->Map( crossline.GetPoint(), crossline.GetDirection(), direction2 ); return Line3D::RectangleLineIntersection( 0, 0, GetExtentInMM(0), GetExtentInMM(1), point2, direction2, lineFrom, lineTo ); } double PlaneGeometry::Angle( const PlaneGeometry *plane ) const { return angle(plane->GetMatrixColumn(2), GetMatrixColumn(2)); } double PlaneGeometry::Angle( const Line3D &line ) const { return vnl_math::pi_over_2 - angle( line.GetDirection().GetVnlVector(), GetMatrixColumn(2) ); } bool PlaneGeometry::IntersectionPoint( const Line3D &line, Point3D &intersectionPoint ) const { Vector3D planeNormal = this->GetNormal(); planeNormal.Normalize(); Vector3D lineDirection = line.GetDirection(); lineDirection.Normalize(); double t = planeNormal * lineDirection; if ( fabs( t ) < eps ) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = ( planeNormal * diff ) / t; intersectionPoint = line.GetPoint() + lineDirection * t; return true; } bool PlaneGeometry::IntersectionPointParam( const Line3D &line, double &t ) const { Vector3D planeNormal = this->GetNormal(); Vector3D lineDirection = line.GetDirection(); t = planeNormal * lineDirection; if ( fabs( t ) < eps ) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = ( planeNormal * diff ) / t; return true; } bool PlaneGeometry::IsParallel( const PlaneGeometry *plane ) const { return ( (Angle(plane) < 10.0 * mitk::sqrteps ) || ( Angle(plane) > ( vnl_math::pi - 10.0 * sqrteps ) ) ) ; } bool PlaneGeometry::IsOnPlane( const Point3D &point ) const { return Distance(point) < eps; } bool PlaneGeometry::IsOnPlane( const Line3D &line ) const { return ( (Distance( line.GetPoint() ) < eps) && (Distance( line.GetPoint2() ) < eps) ); } bool PlaneGeometry::IsOnPlane( const PlaneGeometry *plane ) const { return ( IsParallel( plane ) && (Distance( plane->GetOrigin() ) < eps) ); } Point3D PlaneGeometry::ProjectPointOntoPlane( const Point3D& pt ) const { ScalarType len = this->GetNormalVnl().two_norm(); return pt - this->GetNormal() * this->SignedDistanceFromPlane( pt ) / len; } itk::LightObject::Pointer PlaneGeometry::InternalClone() const { Self::Pointer newGeometry = new PlaneGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void PlaneGeometry::ExecuteOperation( Operation *operation ) { vtkTransform *transform = vtkTransform::New(); transform->SetMatrix( this->GetVtkMatrix()); switch ( operation->GetOperationType() ) { case OpORIENT: { mitk::PlaneOperation *planeOp = dynamic_cast< mitk::PlaneOperation * >( operation ); if ( planeOp == NULL ) { return; } Point3D center = planeOp->GetPoint(); Vector3D orientationVector = planeOp->GetNormal(); Vector3D defaultVector; FillVector3D( defaultVector, 0.0, 0.0, 1.0 ); Vector3D rotationAxis = itk::CrossProduct( orientationVector, defaultVector ); //double rotationAngle = acos( orientationVector[2] / orientationVector.GetNorm() ); double rotationAngle = atan2( (double) rotationAxis.GetNorm(), (double) (orientationVector * defaultVector) ); rotationAngle *= 180.0 / vnl_math::pi; transform->PostMultiply(); transform->Identity(); transform->Translate( center[0], center[1], center[2] ); transform->RotateWXYZ( rotationAngle, rotationAxis[0], rotationAxis[1], rotationAxis[2] ); transform->Translate( -center[0], -center[1], -center[2] ); break; } case OpRESTOREPLANEPOSITION: { RestorePlanePositionOperation *op = dynamic_cast< mitk::RestorePlanePositionOperation* >(operation); if(op == NULL) { return; } AffineTransform3D::Pointer transform2 = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(0)); matrix.GetVnlMatrix().set_column(1, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(1)); matrix.GetVnlMatrix().set_column(2, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(2)); transform2->SetMatrix(matrix); Vector3D offset = op->GetTransform()->GetOffset(); transform2->SetOffset(offset); this->SetIndexToWorldTransform(transform2); ScalarType bounds[6] = {0, op->GetWidth(), 0, op->GetHeight(), 0 ,1 }; this->SetBounds(bounds); TransferItkToVtkTransform(); this->Modified(); transform->Delete(); return; } default: Superclass::ExecuteOperation( operation ); transform->Delete(); return; } this->GetVtkMatrix()->DeepCopy(transform->GetMatrix()); this->TransferVtkToItkTransform(); this->Modified(); transform->Delete(); } void PlaneGeometry::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os,indent); os << indent << " ScaleFactorMMPerUnitX: " << m_ScaleFactorMMPerUnitX << std::endl; os << indent << " ScaleFactorMMPerUnitY: " << m_ScaleFactorMMPerUnitY << std::endl; os << indent << " Normal: " << GetNormal() << std::endl; } void PlaneGeometry::PostSetIndexToWorldTransform( mitk::AffineTransform3D* transform) { m_ScaleFactorMMPerUnitX=GetExtentInMM(0)/GetExtent(0); m_ScaleFactorMMPerUnitY=GetExtentInMM(1)/GetExtent(1); - assert(m_ScaleFactorMMPerUnitX::infinity()); + assert(m_ScaleFactorMMPerUnitY::infinity()); } void PlaneGeometry::PostSetExtentInMM(int direction, ScalarType extentInMM) { m_ScaleFactorMMPerUnitX=GetExtentInMM(0)/GetExtent(0); m_ScaleFactorMMPerUnitY=GetExtentInMM(1)/GetExtent(1); - assert(m_ScaleFactorMMPerUnitX::infinity()); + assert(m_ScaleFactorMMPerUnitY::infinity()); } bool PlaneGeometry::Map( const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const { assert(this->IsBoundingBoxNull()==false); Point3D pt3d_units; BackTransform(pt3d_mm, pt3d_units); pt2d_mm[0]=pt3d_units[0]*m_ScaleFactorMMPerUnitX; pt2d_mm[1]=pt3d_units[1]*m_ScaleFactorMMPerUnitY; pt3d_units[2]=0; return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); } void PlaneGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const { Point3D pt3d_units; pt3d_units[0]=pt2d_mm[0]/m_ScaleFactorMMPerUnitX; pt3d_units[1]=pt2d_mm[1]/m_ScaleFactorMMPerUnitY; pt3d_units[2]=0; pt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); } void PlaneGeometry::SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height) { ScalarType bounds[6]={0, width, 0, height, 0, 1}; ScalarType extent, newextentInMM; if(GetExtent(0)>0) { extent = GetExtent(0); if(width>extent) newextentInMM = GetExtentInMM(0)/width*extent; else newextentInMM = GetExtentInMM(0)*extent/width; SetExtentInMM(0, newextentInMM); } if(GetExtent(1)>0) { extent = GetExtent(1); if(width>extent) newextentInMM = GetExtentInMM(1)/height*extent; else newextentInMM = GetExtentInMM(1)*extent/height; SetExtentInMM(1, newextentInMM); } SetBounds(bounds); } bool PlaneGeometry::Project( const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const { assert(this->IsBoundingBoxNull()==false); Point3D pt3d_units; BackTransform(pt3d_mm, pt3d_units); pt3d_units[2] = 0; projectedPt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); } bool PlaneGeometry::Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { assert(this->IsBoundingBoxNull()==false); Vector3D vec3d_units; BackTransform(vec3d_mm, vec3d_units); vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); return true; } bool PlaneGeometry::Project(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { MITK_WARN << "Deprecated function! Call Project(vec3D,vec3D) instead."; assert(this->IsBoundingBoxNull()==false); Vector3D vec3d_units; BackTransform(atPt3d_mm, vec3d_mm, vec3d_units); vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); Point3D pt3d_units; BackTransform(atPt3d_mm, pt3d_units); return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); } bool PlaneGeometry::Map(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const { Point2D pt2d_mm_start, pt2d_mm_end; Point3D pt3d_mm_end; bool inside=Map(atPt3d_mm, pt2d_mm_start); pt3d_mm_end = atPt3d_mm+vec3d_mm; inside&=Map(pt3d_mm_end, pt2d_mm_end); vec2d_mm=pt2d_mm_end-pt2d_mm_start; return inside; } void PlaneGeometry::Map(const mitk::Point2D &/*atPt2d_mm*/, const mitk::Vector2D &/*vec2d_mm*/, mitk::Vector3D &/*vec3d_mm*/) const { //@todo implement parallel to the other Map method! assert(false); } void PlaneGeometry::SetReferenceGeometry( mitk::BaseGeometry *geometry ) { m_ReferenceGeometry = geometry; } mitk::BaseGeometry * PlaneGeometry::GetReferenceGeometry() const { return m_ReferenceGeometry; } bool PlaneGeometry::HasReferenceGeometry() const { return ( m_ReferenceGeometry != NULL ); } } // namespace diff --git a/Core/Code/DataManagement/mitkPlaneOperation.h b/Core/Code/DataManagement/mitkPlaneOperation.h index 10da6c7e44..34d7a5f8c4 100644 --- a/Core/Code/DataManagement/mitkPlaneOperation.h +++ b/Core/Code/DataManagement/mitkPlaneOperation.h @@ -1,60 +1,60 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPlaneOperation_H #define MITKPlaneOperation_H #include #include "mitkPointOperation.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" namespace mitk { /** * @brief Operation for setting a plane (defined by its origin and normal) * * @ingroup Undo */ class MITK_CORE_EXPORT PlaneOperation : public PointOperation { public: PlaneOperation( OperationType operationType, Point3D point, Vector3D normal ); PlaneOperation( OperationType operationType, Point3D point, Vector3D axisVec0, Vector3D axisVec1 ); virtual ~PlaneOperation(); Vector3D GetNormal(); Vector3D GetAxisVec0(); Vector3D GetAxisVec1(); bool AreAxisDefined(); private: Vector3D m_Normal; Vector3D m_AxisVec0; Vector3D m_AxisVec1; bool m_AreAxisDefined; }; } //namespace mitk #endif /* MITKPlaneOperation_H */ diff --git a/Core/Code/DataManagement/mitkPoint.h b/Core/Code/DataManagement/mitkPoint.h new file mode 100644 index 0000000000..cfbc5956cc --- /dev/null +++ b/Core/Code/DataManagement/mitkPoint.h @@ -0,0 +1,135 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + + +#ifndef MITKPOINT_H_ +#define MITKPOINT_H_ + + +#include + +#include "mitkNumericConstants.h" +#include "mitkArray.h" +#include "mitkEqual.h" + +namespace mitk { + + //##Documentation + //##@brief enumeration of the type a point can be + enum PointSpecificationType + { + PTUNDEFINED = 0, + PTSTART, + PTCORNER, + PTEDGE, + PTEND + }; + + template + class Point : public itk::Point + { + + public: + /** Default constructor has nothing to do. */ + explicit Point() : itk::Point() {} + + /** Pass-through constructors for the Array base class. */ + template< typename TPointValueType > + explicit Point(const Point< TPointValueType, NPointDimension > & r):itk::Point(r) {} + + template< typename TPointValueType > + explicit Point(const TPointValueType r[NPointDimension]):itk::Point(r) {} + + template< typename TPointValueType > + Point(const TPointValueType & v):itk::Point(v) {} + + + Point(const mitk::Point& r) : itk::Point(r) {} + Point(const TCoordRep r[NPointDimension]):itk::Point(r) {} + Point(const TCoordRep & v):itk::Point(v) {} + Point(const itk::Point & p) : itk::Point(p) {} + + + /** + * Copies the elements from array array to this. + * Note that this method will assign doubles to floats without complaining! + * + * @param array the array whose values shall be copied. Must overload [] operator. + */ + template + void FillPoint(const ArrayType& array) + { + itk::FixedArray* thisP = dynamic_cast* >(this); + mitk::FillArray(*thisP, array); + } + + + /** + * Copies the values stored in this point into the array array. + * + * @param array the array which should store the values of this. + */ + template + void ToArray(ArrayType array) const + { + mitk::ToArray(array, *this); + } + + }; + + typedef Point Point2D; + typedef Point Point3D; + typedef Point Point4D; + + typedef Point Point2I; + typedef Point Point3I; + typedef Point Point4I; + + + /** + * @ingroup MITKTestingAPI + * + * @param point1 Point to compare. + * @param point2 Point to compare. + * @param eps Tolerance for floating point comparison. + * @param verbose Flag indicating detailed console output. + * @return True if points are equal. + */ + template + inline bool Equal(const itk::Point& point1, const itk::Point& point2, TCoordRep eps=mitk::eps, bool verbose=false) + { + bool isEqual = true; + typename itk::Point::VectorType diff = point1-point2; + for (unsigned int i=0; i #include "mitkOperation.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" namespace mitk { //##Documentation //## @brief Operation that handles all actions on one Point. //## //## Stores everything for Adding, Moving and Deleting a Point. //## @ingroup Undo class MITK_CORE_EXPORT PointOperation : public Operation { public: //##Documentation //##@brief Operation that handles all actions on one Point. //## //## @param operationType is the type of the operation (see mitkOperation.h; e.g. move or add; Information for StateMachine::ExecuteOperation()); //## @param point is the information of the point to add or is the information to change a point into //## @param index is e.g. the position in a list which describes the element to change PointOperation(OperationType operationType, Point3D point, int index = -1, bool selected = true, PointSpecificationType type = PTUNDEFINED); //##Documentation //##@brief Operation that handles all actions on one Point. //## //## @param operationType is the type of the operation (see mitkOperation.h; e.g. move or add; Information for StateMachine::ExecuteOperation()); //## @param point is the information of the point to add or is the information to change a point into //## @param index is e.g. the position in a list which describes the element to change PointOperation(OperationType operationType, ScalarType timeInMS, Point3D point, int index = -1, bool selected = true, PointSpecificationType type = PTUNDEFINED); virtual ~PointOperation(); Point3D GetPoint(); int GetIndex(); bool GetSelected(); PointSpecificationType GetPointType(); ScalarType GetTimeInMS() const; private: Point3D m_Point; //##Documentation //##@brief to declare an index where to store the point in data int m_Index; //to declare weather the point is selected or deselected bool m_Selected; //##Documentation //##@brief to describe the type of the point. See enum PointSpecification for different types PointSpecificationType m_Type; ScalarType m_TimeInMS; }; }//namespace mitk #endif /* MITKPOINTOPERATION_H*/ diff --git a/Core/Code/DataManagement/mitkPointSet.cpp b/Core/Code/DataManagement/mitkPointSet.cpp index e4dc354337..3266246244 100755 --- a/Core/Code/DataManagement/mitkPointSet.cpp +++ b/Core/Code/DataManagement/mitkPointSet.cpp @@ -1,880 +1,880 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPointSet.h" #include "mitkPointOperation.h" #include "mitkInteractionConst.h" -#include +#include #include mitk::PointSet::PointSet() { this->InitializeEmpty(); } mitk::PointSet::PointSet(const PointSet &other) : BaseData(other) , m_PointSetSeries(other.GetPointSetSeriesSize()) , m_CalculateBoundingBox(true) { // Copy points for (std::size_t t = 0; t < m_PointSetSeries.size(); ++t) { m_PointSetSeries[t] = DataType::New(); DataType::Pointer otherPts = other.GetPointSet(t); for (PointsConstIterator i = other.Begin(t); i != other.End(t); ++i) { m_PointSetSeries[t]->SetPoint(i.Index(), i.Value()); PointDataType pointData; if (otherPts->GetPointData(i.Index(), &pointData)) { m_PointSetSeries[t]->SetPointData(i.Index(), pointData); } } } } mitk::PointSet::~PointSet() { this->ClearData(); } void mitk::PointSet::ClearData() { m_PointSetSeries.clear(); Superclass::ClearData(); } void mitk::PointSet::InitializeEmpty() { m_PointSetSeries.resize( 1 ); m_PointSetSeries[0] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[0]->SetPointData( pointData ); m_CalculateBoundingBox = false; Superclass::InitializeTimeGeometry(1); m_Initialized = true; } 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; } } mitk::PointSet::PointsIterator mitk::PointSet::Begin( int t ) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return PointsIterator(); } mitk::PointSet::PointsConstIterator mitk::PointSet::Begin(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return PointsConstIterator(); } mitk::PointSet::PointsIterator mitk::PointSet::End( int t ) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return PointsIterator(); } mitk::PointSet::PointsConstIterator mitk::PointSet::End(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return PointsConstIterator(); } int mitk::PointSet::SearchPoint( Point3D point, ScalarType distance, int t ) const { if ( t >= (int)m_PointSetSeries.size() ) { return -1; } // Out is the point which is checked to be the searched point PointType out; out.Fill( 0 ); PointType indexPoint; this->GetGeometry( t )->WorldToIndex(point, indexPoint); // Searching the first point in the Set, that is +- distance far away fro // the given point unsigned int i; PointsContainer::Iterator it, end; end = m_PointSetSeries[t]->GetPoints()->End(); int bestIndex = -1; distance = distance * distance; // To correct errors from converting index to world and world to index if (distance == 0.0) { distance = 0.000001; } ScalarType bestDist = distance; ScalarType dist, tmp; for ( it = m_PointSetSeries[t]->GetPoints()->Begin(), i = 0; it != end; ++it, ++i ) { bool ok = m_PointSetSeries[t]->GetPoints() ->GetElementIfIndexExists( it->Index(), &out ); if ( !ok ) { return -1; } else if ( indexPoint == out ) //if totally equal { return it->Index(); } //distance calculation tmp = out[0] - indexPoint[0]; dist = tmp * tmp; tmp = out[1] - indexPoint[1]; dist += tmp * tmp; tmp = out[2] - indexPoint[2]; dist += tmp * tmp; if ( dist < bestDist ) { bestIndex = it->Index(); bestDist = dist; } } return bestIndex; } mitk::PointSet::PointType mitk::PointSet::GetPoint( PointIdentifier id, int t ) const { PointType out; out.Fill(0); if ( (unsigned int) t >= m_PointSetSeries.size() ) { return out; } if ( m_PointSetSeries[t]->GetPoints()->IndexExists(id) ) { m_PointSetSeries[t]->GetPoint( id, &out ); this->GetGeometry(t)->IndexToWorld( out, out ); return out; } else { return out; } } bool mitk::PointSet ::GetPointIfExists( PointIdentifier id, PointType* point, int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return false; } if ( m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(id, point) ) { this->GetGeometry( t )->IndexToWorld( *point, *point ); return true; } else { return false; } } void mitk::PointSet::SetPoint( PointIdentifier id, PointType point, int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); mitk::Point3D indexPoint; this->GetGeometry( t )->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->SetPoint( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->SetPointData( id, defaultPointData ); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::SetPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); mitk::Point3D indexPoint; this->GetGeometry( t )->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->SetPoint( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->SetPointData( id, defaultPointData ); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::InsertPoint( PointIdentifier id, PointType point, int t ) { this->InsertPoint(id, point, mitk::PTUNDEFINED, t); } void mitk::PointSet::InsertPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t ) { if ( (unsigned int) t < m_PointSetSeries.size() ) { mitk::Point3D indexPoint; mitk::BaseGeometry* tempGeometry = this->GetGeometry( t ); if (tempGeometry == 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 TimePointType timeInMS = this->GetTimeGeometry()->TimeStepToTimePoint( t ); // point Point3D point = this->GetPoint( position, t ); std::auto_ptr op; if (selected) { op.reset(new mitk::PointOperation(OpSELECTPOINT, timeInMS, point, position )); } else { op.reset(new mitk::PointOperation(OpDESELECTPOINT, timeInMS, point, position )); } this->ExecuteOperation( op.get() ); } } mitk::PointSpecificationType mitk::PointSet::GetSpecificationTypeInfo( int position, int t ) const { if ( this->IndexExists( position, t ) ) { PointDataType pointData = { 0, false, PTUNDEFINED }; m_PointSetSeries[t]->GetPointData( position, &pointData ); return pointData.pointSpec; } else { return PTUNDEFINED; } } int mitk::PointSet::GetNumberOfSelected( int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return 0; } int numberOfSelected = 0; PointDataIterator it; for ( it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++ ) { if (it->Value().selected == true) { ++numberOfSelected; } } return numberOfSelected; } int mitk::PointSet::SearchSelectedPoint( int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return -1; } PointDataIterator it; for ( it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++ ) { if ( it->Value().selected == true ) { return it->Index(); } } return -1; } void mitk::PointSet::ExecuteOperation( Operation* operation ) { int timeStep = -1; mitkCheckOperationTypeMacro(PointOperation, operation, pointOp); if ( pointOp ) { timeStep = this->GetTimeGeometry()->TimePointToTimeStep( pointOp->GetTimeInMS() ); } if ( timeStep < 0 ) { MITK_ERROR << "Time step (" << timeStep << ") outside of PointSet time bounds" << std::endl; return; } switch (operation->GetOperationType()) { case OpNOTHING: break; case OpINSERT://inserts the point at the given position and selects it. { int position = pointOp->GetIndex(); PointType pt; pt.CastFrom(pointOp->GetPoint()); //transfer from world to index coordinates mitk::BaseGeometry* 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 = { static_cast(pointOp->GetIndex()), pointOp->GetSelected(), pointOp->GetPointType() }; m_PointSetSeries[timeStep]->GetPointData() ->InsertElement(position, pointData); this->Modified(); //boundingbox has to be computed m_CalculateBoundingBox = true; this->InvokeEvent( PointSetAddEvent() ); this->OnPointSetChange(); } break; case OpMOVE://moves the point given by index { PointType pt; pt.CastFrom(pointOp->GetPoint()); //transfer from world to index coordinates this->GetGeometry( timeStep )->WorldToIndex(pt, pt); // Copy new point into container m_PointSetSeries[timeStep]->SetPoint(pointOp->GetIndex(), pt); // Insert a default point data object to keep the containers in sync // (if no point data object exists yet) PointDataType pointData; if ( !m_PointSetSeries[timeStep]->GetPointData( pointOp->GetIndex(), &pointData ) ) { m_PointSetSeries[timeStep]->SetPointData( pointOp->GetIndex(), pointData ); } this->OnPointSetChange(); this->Modified(); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent( PointSetMoveEvent() ); } break; case OpREMOVE://removes the point at given by position { m_PointSetSeries[timeStep]->GetPoints()->DeleteIndex((unsigned)pointOp->GetIndex()); m_PointSetSeries[timeStep]->GetPointData()->DeleteIndex((unsigned)pointOp->GetIndex()); this->OnPointSetChange(); this->Modified(); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent( PointSetRemoveEvent() ); } break; case OpSELECTPOINT://select the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = true; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpDESELECTPOINT://unselect the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = false; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpSETPOINTTYPE: { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.pointSpec = pointOp->GetPointType(); m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpMOVEPOINTUP: // swap content of point with ID pointOp->GetIndex() with the point preceding it in the container // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that precedes this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); 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 // TimeGeometry* timeGeometry = GetTimeGeometry(); if ( timeGeometry->CountTimeSteps() != m_PointSetSeries.size() ) { itkExceptionMacro(<<"timeGeometry->CountTimeSteps() != m_PointSetSeries.size() -- use Initialize(timeSteps) with correct number of timeSteps!"); } // This is needed to detect zero objects mitk::ScalarType nullpoint[]={0,0,0,0,0,0}; BoundingBox::BoundsArrayType itkBoundsNull(nullpoint); // // Iterate over the PointSets and update the Geometry // information of each of the items. // if (m_CalculateBoundingBox) { for ( unsigned int i = 0 ; i < m_PointSetSeries.size() ; ++i ) { const DataType::BoundingBoxType *bb = m_PointSetSeries[i]->GetBoundingBox(); BoundingBox::BoundsArrayType itkBounds = bb->GetBounds(); if ( m_PointSetSeries[i].IsNull() || (m_PointSetSeries[i]->GetNumberOfPoints() == 0) || (itkBounds == itkBoundsNull) ) { itkBounds = itkBoundsNull; continue; } // Ensure minimal bounds of 1.0 in each dimension for ( unsigned int j = 0; j < 3; ++j ) { if ( itkBounds[j*2+1] - itkBounds[j*2] < 1.0 ) { BoundingBox::CoordRepType center = (itkBounds[j*2] + itkBounds[j*2+1]) / 2.0; itkBounds[j*2] = center - 0.5; itkBounds[j*2+1] = center + 0.5; } } this->GetGeometry(i)->SetBounds(itkBounds); } m_CalculateBoundingBox = false; } this->GetTimeGeometry()->Update(); } void mitk::PointSet::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::PointSet::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::PointSet::VerifyRequestedRegion() { return true; } void mitk::PointSet::SetRequestedRegion(const DataObject * ) { } void mitk::PointSet::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os, indent); os << indent << "Number timesteps: " << m_PointSetSeries.size() << "\n"; unsigned int i = 0; for (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; } bool mitk::PointSet::PointDataType::operator ==(const mitk::PointSet::PointDataType &other) const { return id == other.id && selected == other.selected && pointSpec == other.pointSpec; } bool mitk::Equal( const mitk::PointSet* leftHandSide, const mitk::PointSet* rightHandSide, mitk::ScalarType eps, bool verbose ) { if((leftHandSide == NULL) || (rightHandSide == NULL)) { MITK_ERROR << "mitk::Equal( const mitk::PointSet* leftHandSide, const mitk::PointSet* rightHandSide, mitk::ScalarType eps, bool verbose ) does not work with NULL pointer input."; return false; } return Equal( *leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal( const mitk::PointSet& leftHandSide, const mitk::PointSet& rightHandSide, mitk::ScalarType eps, bool verbose ) { bool result = true; if( !mitk::Equal( *leftHandSide.GetGeometry(), *rightHandSide.GetGeometry(), eps, verbose) ) { if(verbose) MITK_INFO << "[( PointSet )] Geometries differ."; result = false; } if ( leftHandSide.GetSize() != rightHandSide.GetSize()) { if(verbose) MITK_INFO << "[( PointSet )] Number of points differ."; result = false; } else { //if the size is equal, we compare the point values mitk::Point3D pointLeftHandSide; mitk::Point3D pointRightHandSide; int numberOfIncorrectPoints = 0; //Iterate over both pointsets in order to compare all points pair-wise mitk::PointSet::PointsConstIterator end = leftHandSide.End(); for( mitk::PointSet::PointsConstIterator pointSetIteratorLeft = leftHandSide.Begin(), pointSetIteratorRight = rightHandSide.Begin(); pointSetIteratorLeft != end; ++pointSetIteratorLeft, ++pointSetIteratorRight) //iterate simultaneously over both sets { pointLeftHandSide = pointSetIteratorLeft.Value(); pointRightHandSide = pointSetIteratorRight.Value(); if( !mitk::Equal( pointLeftHandSide, pointRightHandSide, eps, verbose ) ) { if(verbose) MITK_INFO << "[( PointSet )] Point values are different."; result = false; numberOfIncorrectPoints++; } } if((numberOfIncorrectPoints > 0) && verbose) { MITK_INFO << numberOfIncorrectPoints <<" of a total of " << leftHandSide.GetSize() << " points are different."; } } return result; } diff --git a/Core/Code/DataManagement/mitkProperties.h b/Core/Code/DataManagement/mitkProperties.h index 78d836b99d..5fc3457b49 100644 --- a/Core/Code/DataManagement/mitkProperties.h +++ b/Core/Code/DataManagement/mitkProperties.h @@ -1,54 +1,54 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPROPERTIES_H_HEADER_INCLUDED #define MITKPROPERTIES_H_HEADER_INCLUDED #include "mitkGenericProperty.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkLookupTables.h" namespace mitk { mitkDeclareGenericProperty(BoolProperty,bool,MITK_CORE_EXPORT); mitkDeclareGenericProperty(IntProperty,int,MITK_CORE_EXPORT); mitkDeclareGenericProperty(FloatProperty,float,MITK_CORE_EXPORT); mitkDeclareGenericProperty(DoubleProperty,double,MITK_CORE_EXPORT); mitkDeclareGenericProperty(Vector3DProperty,Vector3D,MITK_CORE_EXPORT); mitkDeclareGenericProperty(Point2dProperty,Point2D,MITK_CORE_EXPORT); mitkDeclareGenericProperty(Point3dProperty,Point3D,MITK_CORE_EXPORT); mitkDeclareGenericProperty(Point4dProperty,Point4D,MITK_CORE_EXPORT); mitkDeclareGenericProperty(Point3iProperty,Point3I,MITK_CORE_EXPORT); mitkDeclareGenericProperty(FloatLookupTableProperty, FloatLookupTable,MITK_CORE_EXPORT); mitkDeclareGenericProperty(BoolLookupTableProperty, BoolLookupTable,MITK_CORE_EXPORT); mitkDeclareGenericProperty(IntLookupTableProperty, IntLookupTable,MITK_CORE_EXPORT); mitkDeclareGenericProperty(StringLookupTableProperty, StringLookupTable,MITK_CORE_EXPORT); /** * \warning If you add more specialization of GenericProperty, you must also add these to the * templated GetPropertyValue() method in mitkPropertyList.cpp! */ } // namespace mitk #endif /* MITKPROPERTIES_H_HEADER_INCLUDED */ diff --git a/Core/Code/DataManagement/mitkPropertyList.cpp b/Core/Code/DataManagement/mitkPropertyList.cpp index 5b69877899..4aa8c37cd3 100644 --- a/Core/Code/DataManagement/mitkPropertyList.cpp +++ b/Core/Code/DataManagement/mitkPropertyList.cpp @@ -1,358 +1,358 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPropertyList.h" #include "mitkProperties.h" #include "mitkStringProperty.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" mitk::BaseProperty* mitk::PropertyList::GetProperty(const std::string& propertyKey) const { PropertyMap::const_iterator it; it=m_Properties.find( propertyKey ); if(it!=m_Properties.end()) return it->second; else return NULL; } void mitk::PropertyList::SetProperty(const std::string& propertyKey, BaseProperty* property) { if (!property) return; //make sure that BaseProperty*, which may have just been created and never been //assigned to a SmartPointer, is registered/unregistered properly. If we do not //do that, it will a) not deleted in case it is identical to the old one or //b) possibly deleted when temporarily added to a smartpointer somewhere below. BaseProperty::Pointer tmpSmartPointerToProperty = property; PropertyMap::iterator it( m_Properties.find( propertyKey ) ); // Is a property with key @a propertyKey contained in the list? if( it != m_Properties.end() ) { // yes //is the property contained in the list identical to the new one? if( it->second->operator==(*property) ) { // yes? do nothing and return. return; } if (it->second->AssignProperty(*property)) { // The assignment was successfull this->Modified(); } else { MITK_ERROR << "In " __FILE__ ", l." << __LINE__ << ": Trying to set existing property " << it->first << " of type " << it->second->GetNameOfClass() << " to a property with different type " << property->GetNameOfClass() << "." << " Use ReplaceProperty() instead." << std::endl; } return; } //no? add it. PropertyMapElementType newProp; newProp.first = propertyKey; newProp.second = property; m_Properties.insert ( newProp ); this->Modified(); } void mitk::PropertyList::ReplaceProperty(const std::string& propertyKey, BaseProperty* property) { if (!property) return; PropertyMap::iterator it( m_Properties.find( propertyKey ) ); // Is a property with key @a propertyKey contained in the list? if( it != m_Properties.end() ) { it->second=NULL; m_Properties.erase(it); } //no? add/replace it. PropertyMapElementType newProp; newProp.first = propertyKey; newProp.second = property; m_Properties.insert ( newProp ); Modified(); } mitk::PropertyList::PropertyList() { } mitk::PropertyList::PropertyList(const mitk::PropertyList& other) : itk::Object() { for (PropertyMap::const_iterator i = other.m_Properties.begin(); i != other.m_Properties.end(); ++i) { m_Properties.insert(std::make_pair(i->first, i->second->Clone())); } } mitk::PropertyList::~PropertyList() { Clear(); } /** * Consider the list as changed when any of the properties has changed recently. */ unsigned long mitk::PropertyList::GetMTime() const { for ( PropertyMap::const_iterator it = m_Properties.begin() ; it != m_Properties.end(); ++it ) { if( it->second.IsNull() ) { itkWarningMacro(<< "Property '" << it->first <<"' contains nothing (NULL)."); continue; } if( Superclass::GetMTime() < it->second->GetMTime() ) { Modified(); break; } } return Superclass::GetMTime(); } bool mitk::PropertyList::DeleteProperty(const std::string& propertyKey) { PropertyMap::iterator it; it=m_Properties.find( propertyKey ); if(it!=m_Properties.end()) { it->second=NULL; m_Properties.erase(it); Modified(); return true; } return false; } void mitk::PropertyList::Clear() { PropertyMap::iterator it = m_Properties.begin(), end = m_Properties.end(); while(it!=end) { it->second = NULL; ++it; } m_Properties.clear(); } itk::LightObject::Pointer mitk::PropertyList::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } void mitk::PropertyList::ConcatenatePropertyList(PropertyList *pList, bool replace) { if (pList) { const PropertyMap* propertyMap = pList->GetMap(); for ( PropertyMap::const_iterator iter = propertyMap->begin(); // m_PropertyList is created in the constructor, so we don't check it here iter != propertyMap->end(); ++iter ) { const std::string key = iter->first; BaseProperty* value = iter->second; if (replace) { ReplaceProperty( key.c_str(), value ); } else { SetProperty( key.c_str(), value ); } } } } bool mitk::PropertyList::GetBoolProperty(const char* propertyKey, bool& boolValue) const { BoolProperty *gp = dynamic_cast( GetProperty(propertyKey) ); if ( gp != NULL ) { boolValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs //return GetPropertyValue(propertyKey, boolValue); } bool mitk::PropertyList::GetIntProperty(const char* propertyKey, int &intValue) const { IntProperty *gp = dynamic_cast( GetProperty(propertyKey) ); if ( gp != NULL ) { intValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs //return GetPropertyValue(propertyKey, intValue); } bool mitk::PropertyList::GetFloatProperty(const char* propertyKey, float &floatValue) const { FloatProperty *gp = dynamic_cast( GetProperty(propertyKey) ); if ( gp != NULL ) { floatValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs //return GetPropertyValue(propertyKey, floatValue); } bool mitk::PropertyList::GetStringProperty(const char* propertyKey, std::string& stringValue) const { StringProperty* sp= dynamic_cast(GetProperty(propertyKey)); if ( sp != NULL ) { stringValue = sp->GetValue(); return true; } return false; } void mitk::PropertyList::SetIntProperty(const char* propertyKey, int intValue) { SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void mitk::PropertyList::SetBoolProperty( const char* propertyKey, bool boolValue) { SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void mitk::PropertyList::SetFloatProperty( const char* propertyKey, float floatValue) { SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void mitk::PropertyList::SetStringProperty( const char* propertyKey, const char* stringValue) { SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void mitk::PropertyList::Set( const char* propertyKey, bool boolValue ) { this->SetBoolProperty( propertyKey, boolValue ); } void mitk::PropertyList::Set( const char* propertyKey, int intValue ) { this->SetIntProperty(propertyKey, intValue); } void mitk::PropertyList::Set( const char* propertyKey, float floatValue ) { this->SetFloatProperty(propertyKey, floatValue); } void mitk::PropertyList::Set( const char* propertyKey, double doubleValue ) { this->SetDoubleProperty(propertyKey, doubleValue); } void mitk::PropertyList::Set( const char* propertyKey, const char* stringValue ) { this->SetStringProperty(propertyKey, stringValue); } void mitk::PropertyList::Set( const char* propertyKey, const std::string& stringValue ) { this->SetStringProperty(propertyKey, stringValue.c_str()); } bool mitk::PropertyList::Get( const char* propertyKey, bool& boolValue ) const { return this->GetBoolProperty( propertyKey, boolValue ); } bool mitk::PropertyList::Get( const char* propertyKey, int &intValue ) const { return this->GetIntProperty(propertyKey, intValue); } bool mitk::PropertyList::Get( const char* propertyKey, float &floatValue ) const { return this->GetFloatProperty( propertyKey, floatValue ); } bool mitk::PropertyList::Get( const char* propertyKey, double &doubleValue ) const { return this->GetDoubleProperty( propertyKey, doubleValue ); } bool mitk::PropertyList::Get( const char* propertyKey, std::string& stringValue ) const { return this->GetStringProperty( propertyKey, stringValue ); } bool mitk::PropertyList::GetDoubleProperty( const char* propertyKey, double &doubleValue ) const { DoubleProperty *gp = dynamic_cast( GetProperty(propertyKey) ); if ( gp != NULL ) { doubleValue = gp->GetValue(); return true; } return false; } void mitk::PropertyList::SetDoubleProperty( const char* propertyKey, double doubleValue ) { SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); } diff --git a/Core/Code/DataManagement/mitkVector.cpp b/Core/Code/DataManagement/mitkQuaternion.h similarity index 68% copy from Core/Code/DataManagement/mitkVector.cpp copy to Core/Code/DataManagement/mitkQuaternion.h index 3a6f50b86b..889e2fe285 100644 --- a/Core/Code/DataManagement/mitkVector.cpp +++ b/Core/Code/DataManagement/mitkQuaternion.h @@ -1,21 +1,30 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include "mitkVector.h" +#ifndef MITKQUATERNION_H_ +#define MITKQUATERNION_H_ -const mitk::ScalarType mitk::eps = vnl_math::eps*100; -const mitk::ScalarType mitk::sqrteps = vnl_math::sqrteps; -extern const mitk::ScalarType mitk::large = std::numeric_limits::max(); + +#include +#include "mitkNumericConstants.h" + +namespace mitk { + +typedef vnl_quaternion Quaternion; + +} + +#endif /* MITKQUATERNION_H_ */ diff --git a/Core/Code/DataManagement/mitkRestorePlanePositionOperation.h b/Core/Code/DataManagement/mitkRestorePlanePositionOperation.h index f277cf43f0..6b3501637b 100644 --- a/Core/Code/DataManagement/mitkRestorePlanePositionOperation.h +++ b/Core/Code/DataManagement/mitkRestorePlanePositionOperation.h @@ -1,76 +1,76 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkRestorePlanePositionOperation_h_Included #define mitkRestorePlanePositionOperation_h_Included #include "mitkCommon.h" #include "mitkPointOperation.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" namespace mitk { //##Documentation //## TODO class MITK_CORE_EXPORT RestorePlanePositionOperation : public Operation { public: //##Documentation //##@brief Operation that handles all actions on one Point. //## //## @param operationType is the type of the operation (see mitkOperation.h; e.g. move or add; Information for StateMachine::ExecuteOperation()); //## @param point is the information of the point to add or is the information to change a point into //## @param index is e.g. the position in a list which describes the element to change //PointOperation(OperationType operationType, Point3D point, int index = -1, bool selected = true, PointSpecificationType type = PTUNDEFINED); RestorePlanePositionOperation(OperationType operationType, ScalarType width, ScalarType height, Vector3D spacing, unsigned int pos, Vector3D direction, AffineTransform3D::Pointer transform); virtual ~RestorePlanePositionOperation(); Vector3D GetDirectionVector(); ScalarType GetWidth(); ScalarType GetHeight(); Vector3D GetSpacing(); unsigned int GetPos(); AffineTransform3D::Pointer GetTransform(); private: Vector3D m_Spacing; Vector3D m_DirectionVector; ScalarType m_Width; ScalarType m_Height; unsigned int m_Pos; AffineTransform3D::Pointer m_Transform; }; }//namespace mitk #endif diff --git a/Core/Code/DataManagement/mitkRotationOperation.h b/Core/Code/DataManagement/mitkRotationOperation.h index 5301d45214..b1869a5b99 100644 --- a/Core/Code/DataManagement/mitkRotationOperation.h +++ b/Core/Code/DataManagement/mitkRotationOperation.h @@ -1,47 +1,47 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKROTATIONOPERATION_H_HEADER_INCLUDED #define MITKROTATIONOPERATION_H_HEADER_INCLUDED #include "mitkOperation.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" namespace mitk { //##Documentation //## @brief Operation, that holds everything necessary for an rotation operation //## //## @ingroup Undo class MITK_CORE_EXPORT RotationOperation : public Operation { public: RotationOperation(OperationType operationType, Point3D pointOfRotation, Vector3D vectorOfRotation, ScalarType angleOfRotation); virtual ~RotationOperation(void); virtual ScalarType GetAngleOfRotation(); virtual const Point3D GetCenterOfRotation(); virtual const Vector3D GetVectorOfRotation(); protected: ScalarType m_AngleOfRotation; Point3D m_PointOfRotation; Vector3D m_VectorOfRotation; }; } // namespace mitk #endif /* MITKROTATIONOPERATION_H_HEADER_INCLUDED */ diff --git a/Core/Code/DataManagement/mitkVector.cpp b/Core/Code/DataManagement/mitkVector.cpp index 3a6f50b86b..9359e39422 100644 --- a/Core/Code/DataManagement/mitkVector.cpp +++ b/Core/Code/DataManagement/mitkVector.cpp @@ -1,21 +1,17 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkVector.h" - -const mitk::ScalarType mitk::eps = vnl_math::eps*100; -const mitk::ScalarType mitk::sqrteps = vnl_math::sqrteps; -extern const mitk::ScalarType mitk::large = std::numeric_limits::max(); diff --git a/Core/Code/DataManagement/mitkVector.h b/Core/Code/DataManagement/mitkVector.h index bece633e5a..b953dadc55 100644 --- a/Core/Code/DataManagement/mitkVector.h +++ b/Core/Code/DataManagement/mitkVector.h @@ -1,552 +1,232 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ +#ifndef MITKVECTOR_H_ +#define MITKVECTOR_H_ -#ifndef MITKVECTOR_H_HEADER_INCLUDED_C1EBD0AD -#define MITKVECTOR_H_HEADER_INCLUDED_C1EBD0AD -#include -#include - -#include -#include -#include -#include #include -#include -#include -#include - -#include - -#ifndef DOXYGEN_SKIP - -namespace mitk { -typedef double 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 float 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; -}; +#include +#include -template<> class VectorTraits< vnl_vector_fixed > { - public: - typedef ScalarType ValueType; -}; +#include "mitkNumericConstants.h" +#include "mitkArray.h" +#include "mitkEqual.h" +#include "mitkExceptionMacro.h" -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< double[4] > { - public: - typedef double 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; -}; +namespace mitk +{ -template<> class VectorTraits< itk::Point > { - public: - typedef double ValueType; -}; + template + class Vector : public itk::Vector + { + public: + /** + * @brief Default constructor has nothing to do. + */ + explicit Vector() + : itk::Vector() {} + + /** + * @brief Copy constructor. + */ + explicit Vector(const mitk::Vector& r) + : itk::Vector(r) {} + + /** + * @brief Constructor to convert from itk::Vector to mitk::Vector. + */ + Vector(const itk::Vector& r) + : itk::Vector(r) {} + + + /** + * @brief Constructor to convert an array to mitk::Vector + * @param r the array. + * @attention must have NVectorDimension valid arguments! + */ + Vector(const TCoordRep r[NVectorDimension]) + : itk::Vector(r) {} + + /** + * Constructor to initialize entire vector to one value. + */ + Vector(const TCoordRep & v) + : itk::Vector(v) {} + + /** + * @brief Constructor for vnl_vectors. + * @throws mitk::Exception if vnl_vector.size() != NVectorDimension. + */ + Vector(const vnl_vector& vnlVector) + : itk::Vector() + { + if (vnlVector.size() != NVectorDimension) + mitkThrow() << "when constructing mitk::Vector from vnl_vector: sizes didn't match: mitk::Vector " << NVectorDimension << "; vnl_vector " << vnlVector.size(); -template<> class VectorTraits< itk::Point > { -public: - typedef double ValueType; -}; + for (unsigned int var = 0; (var < NVectorDimension) && (var < vnlVector.size()); ++var) { + this->SetElement(var, vnlVector.get(var)); + } + } -template<> class VectorTraits< itk::Vector > { - public: - typedef int ValueType; -}; + /** + * @brief Constructor for vnl_vector_fixed. + */ + Vector(const vnl_vector_fixed& vnlVectorFixed) + : itk::Vector() + { + for (unsigned int var = 0; var < NVectorDimension; ++var) { + this->SetElement(var, vnlVectorFixed[var]); + } + }; + + /** + * Copies the elements from array array to this. + * Note that this method will assign doubles to floats without complaining! + * + * @param array the array whose values shall be copied. Must overload [] operator. + */ + template + void FillVector(const ArrayType& array) + { + itk::FixedArray* thisP = dynamic_cast* >(this); + mitk::FillArray(*thisP, array); + } -template<> class VectorTraits< itk::Point > { - public: - typedef int ValueType; -}; + /** + * Copies the values stored in this vector into the array array.d + * + * @param array the array which should store the values of this. + */ + template + void ToArray(ArrayType array) const + { + mitk::ToArray(array, *this); + } -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; -} + /** + * @brief User defined conversion of mitk::Vector to vnl_vector. + * Note: the conversion to mitk::Vector to vnl_vector_fixed has not been implemented since this + * would collide with the conversion vnl_vector to vnl_vector_fixed provided by vnl. + */ + operator vnl_vector () const + { + return this->GetVnlVector(); + } -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 Vector2D; + typedef Vector Vector3D; + typedef Vector Vector4D; -template - inline void vnl2vtk(const vnl_vector_fixed& in, Tout *out) -{ - unsigned int i; - for(i=0; i VnlVector; -template - 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 bool Equal(const itk::Vector& vector1, const itk::Vector& vector2, TCoordRep eps=mitk::eps, bool verbose=false) { - sub[i] = vector[i]+point[i]; - } - return sub; -} + bool isEqual = true; + typename itk::Vector::VectorType diff = vector1-vector2; + 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()) ) + /** + * @ingroup MITKTestingAPI + * + * @param vector1 Vector to compare. + * @param vector2 Vector to compare. + * @param eps Tolerance for floating point comparison. + * @param verbose Flag indicating detailed console output. + * @return True if vectors are equal. + */ + inline bool Equal(const mitk::VnlVector& vector1, const mitk::VnlVector& vector2, ScalarType eps=mitk::eps, bool verbose=false) { - 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 -\param 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; - } + isEqual = false; + break; } } - 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 -\param 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); -} - -/** - * @ingroup MITKTestingAPI - * - * @param vector1 Vector to compare. - * @param vector2 Vector to compare. - * @param eps Tolerance for floating point comparison. - * @param verbose Flag indicating detailed console output. - * @return True if vectors are equal. - */ -template -inline bool Equal(const itk::Vector& vector1, const itk::Vector& vector2, TCoordRep eps=mitk::eps, bool verbose=false) -{ - bool isEqual = true; - typename itk::Vector::VectorType diff = vector1-vector2; - for (unsigned int i=0; ieps || diff[i]<-eps) - { - isEqual = false; - break; - } - } - if(verbose && !isEqual) - { - MITK_INFO << "Vectors not equal. Lefthandside " << std::setprecision(12) << vector1 << " - Righthandside " << vector2 << " - epsilon " << eps; - } - return isEqual; -} - -/** - * @ingroup MITKTestingAPI - * - * @param point1 Point to compare. - * @param point2 Point to compare. - * @param eps Tolerance for floating point comparison. - * @param verbose Flag indicating detailed console output. - * @return True if points are equal. - */ -template - inline bool Equal(const itk::Point& point1, const itk::Point& point2, TCoordRep eps=mitk::eps, bool verbose=false) -{ - bool isEqual = true; - typename itk::Point::VectorType diff = point1-point2; - for (unsigned int i=0; ieps || diff[i]<-eps) - { - isEqual = false; - break; - } - } + ConditionalOutputOfDifference(vector1, vector2, eps, verbose, isEqual); - if(verbose && !isEqual) - { - MITK_INFO << "Points not equal. Lefthandside " << std::setprecision(12) << point1 << " - Righthandside " << point2 << " - epsilon " << eps; - } - return isEqual; -} - -/** - * @ingroup MITKTestingAPI - * - * @param vector1 Vector to compare. - * @param vector2 Vector to compare. - * @param eps Tolerance for floating point comparison. - * @param verbose Flag indicating detailed console output. - * @return True if vectors are equal. - */ -inline bool Equal(const mitk::VnlVector& vector1, const mitk::VnlVector& vector2, ScalarType eps=mitk::eps, bool verbose=false) -{ - bool isEqual = true; - mitk::VnlVector diff = vector1-vector2; - for (unsigned int i=0; ieps || diff[i]<-eps) - { - isEqual = false; - break; - } + return isEqual; } - if(verbose && !isEqual) - { - MITK_INFO << "Vectors not equal. Lefthandside " << std::setprecision(12) << vector1 << " - Righthandside " << vector2 << " - epsilon " << eps; - } - return isEqual; -} - -/** - * @ingroup MITKTestingAPI - * - * @param scalar1 Scalar value to compare. - * @param scalar2 Scalar value to compare. - * @param eps Tolerance for floating point comparison. - * @param verbose Flag indicating detailed console output. - * @return True if scalars are equal. - */ -inline bool Equal(ScalarType scalar1, ScalarType scalar2, ScalarType eps=mitk::eps, bool verbose=false) -{ - bool isEqual( fabs(scalar1-scalar2) < eps ); - if(verbose && !isEqual) - { - MITK_INFO << "Scalars not equal. Lefthandside " << std::setprecision(12) << scalar1 << " - Righthandside " << scalar2 << " - epsilon " << eps; - } - return isEqual; -} - -/** - * @ingroup MITKTestingAPI - * - * @param vector1 Vector to compare. - * @param vector2 Vector to compare. - * @param eps Tolerance for floating point comparison. - * @param verbose Flag indicating detailed console output. - * @return True if vectors are equal. - */ -template - inline bool Equal(const vnl_vector_fixed & vector1, const vnl_vector_fixed& vector2, TCoordRep eps=mitk::eps, bool verbose=false) -{ - vnl_vector_fixed diff = vector1-vector2; - bool isEqual = true; - for( unsigned int i=0; i + inline bool Equal(const vnl_vector_fixed & vector1, const vnl_vector_fixed& vector2, TCoordRep eps=mitk::eps, bool verbose=false) { - if(diff[i]>eps || diff[i]<-eps) + vnl_vector_fixed diff = vector1-vector2; + bool isEqual = true; + for( unsigned int i=0; i -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]; -} + ConditionalOutputOfDifference(vector1, vector2, eps, verbose, isEqual); - -} // 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(); \ - } \ + return isEqual; } -#define mitkSetVectorMacro(name,type) \ - mitkSetConstReferenceMacro(name,type) -#define mitkGetVectorMacro(name,type) \ - itkGetConstReferenceMacro(name,type) +} // end namespace mitk -#endif /* MITKVECTOR_H_HEADER_INCLUDED_C1EBD0AD */ +#endif /* MITKVECTOR_H_ */ diff --git a/Core/Code/DataManagement/mitkVectorDeprecated.h b/Core/Code/DataManagement/mitkVectorDeprecated.h new file mode 100644 index 0000000000..ba472a5a4b --- /dev/null +++ b/Core/Code/DataManagement/mitkVectorDeprecated.h @@ -0,0 +1,238 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + + +#ifndef MITKVECTORDEPRECATED_H_ +#define MITKVECTORDEPRECATED_H_ + +#include +#include + + +#include "mitkNumericConstants.h" +#include "mitkVector.h" +#include "mitkPoint.h" +#include "mitkMatrix.h" + + +template class VectorTraits { +public: + typedef T ValueType; +}; + +template <> class VectorTraits { +public: + typedef mitk::ScalarType ValueType; +}; + +template<> class VectorTraits { +public: + typedef float 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 mitk::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< double[4] > { +public: + typedef double ValueType; +}; + +template<> class VectorTraits< itk::Vector > { +public: + typedef float ValueType; +}; + +template<> class VectorTraits< itk::Vector > { +public: + typedef double ValueType; +}; + +template<> class VectorTraits< itk::Vector > { +public: + typedef int ValueType; +}; + +template<> class VectorTraits< mitk::Vector > { +public: + typedef double ValueType; +}; + +template<> class VectorTraits< mitk::Point > { +public: + typedef float ValueType; +}; + +template<> class VectorTraits< mitk::Point > { +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< mitk::Point > { +public: + typedef double ValueType; +}; + +template<> class VectorTraits< mitk::Point > { +public: + typedef double ValueType; +}; + +template<> class VectorTraits< itk::Point > { +public: + typedef double ValueType; +}; + +template<> class VectorTraits< itk::Point > { +public: + typedef double ValueType; +}; + +template<> class VectorTraits< mitk::Point > { +public: + typedef int ValueType; +}; + +namespace mitk +{ + + + 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 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 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 + 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]; + } + +#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) + +} // namespace mitk + + + + + +#endif /* MITKVECTORDEPRECATED_H_ */ diff --git a/Core/Code/Interactions/mitkAffineInteractor.h b/Core/Code/Interactions/mitkAffineInteractor.h index 69fb70f13e..6ddf60faf0 100755 --- a/Core/Code/Interactions/mitkAffineInteractor.h +++ b/Core/Code/Interactions/mitkAffineInteractor.h @@ -1,84 +1,84 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKAFFINEINTERACTOR_H_HEADER_INCLUDED_C188C29F #define MITKAFFINEINTERACTOR_H_HEADER_INCLUDED_C188C29F #include #include "mitkInteractor.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" namespace mitk { class DisplayPositionEvent; //##Documentation //## @brief Interactor for Affine transformations translate, rotate and scale //## //## An object of this class can translate, rotate and scale the data objects //## by modifying its geometry. //## @ingroup Interaction //create events for interactions #pragma GCC visibility push(default) itkEventMacro(AffineInteractionEvent, itk::AnyEvent); itkEventMacro(ScaleEvent, AffineInteractionEvent); itkEventMacro(RotateEvent, AffineInteractionEvent); itkEventMacro(TranslateEvent, AffineInteractionEvent); #pragma GCC visibility pop class MITK_CORE_EXPORT AffineInteractor : public Interactor { public: mitkClassMacro(AffineInteractor,Interactor); // itkFactorylessNewMacro(Self) // itkCloneMacro(Self) mitkNewMacro2Param(Self, const char*, DataNode*); protected: // AffineInteractor(); //obsolete //##Documentation //## @brief Constructor //## //## @param dataNode is the node, this Interactor is connected to //## @param type is the type of StateMachine like declared in the XML-Configure-File AffineInteractor(const char * type, DataNode* dataNode); //##Documentation //## @brief Destructor ~AffineInteractor(){}; virtual bool ExecuteAction(Action* action, mitk::StateEvent const* stateEvent); //##Documentation //## @brief calculates how good the data this state machine handles is hit by the event. //## //## Returns a value between 0 and 1. //## (Used by GlobalInteraction to decide which DESELECTED state machine to send the event to.) //## //## \WARNING This is interactor currently does not work for interaction in 3D. Try using mitkAffineInteractor3D instead. virtual float CanHandleEvent(StateEvent const* stateEvent) const; bool CheckSelected(const mitk::Point3D& worldPoint, int timestep); bool ConvertDisplayEventToWorldPosition(mitk::DisplayPositionEvent const* displayEvent, mitk::Point3D& worldPoint); mitk::Point3D m_LastMousePosition; }; } // namespace mitk #endif /* MITKAFFINEINTERACTOR_H_HEADER_INCLUDED_C188C29F */ diff --git a/Core/Code/Interactions/mitkCoordinateSupplier.h b/Core/Code/Interactions/mitkCoordinateSupplier.h index 5ee9699faf..59f8f9a730 100755 --- a/Core/Code/Interactions/mitkCoordinateSupplier.h +++ b/Core/Code/Interactions/mitkCoordinateSupplier.h @@ -1,65 +1,65 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKCOORDINATESUPPLIER_H #define MITKCOORDINATESUPPLIER_H #include #include "mitkStateMachine.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" namespace mitk { class Operation; class OperationActor; //##Documentation //## @brief Interactor //## //## sends a Point, that can be processed in its own OperationActor //## @ingroup Interaction class MITK_CORE_EXPORT CoordinateSupplier : public StateMachine { public: mitkClassMacro(CoordinateSupplier, StateMachine); mitkNewMacro2Param(Self, const char*, OperationActor*); itkGetConstReferenceMacro(CurrentPoint, Point3D); protected: //##Documentation //## @brief Constructor with needed arguments //## @param type: string, that describes the StateMachine-Scheme to take from all SM (see XML-File) //## @param operationActor: the Data, operations (+ points) are send to CoordinateSupplier(const char * type, OperationActor* operationActor); ~CoordinateSupplier(); //##Documentation //## @brief executes the actions that are sent to this statemachine //## derived from StateMachine virtual bool ExecuteAction(Action* action, mitk::StateEvent const* stateEvent); private: OperationActor* m_Destination; Point3D m_OldPoint; Point3D m_CurrentPoint; }; } // namespace mitk #endif /* MITKCOORDINATESUPPLIER_H */ diff --git a/Core/Code/Interactions/mitkDisplayCoordinateOperation.h b/Core/Code/Interactions/mitkDisplayCoordinateOperation.h index 21ca403f1a..e47e245889 100644 --- a/Core/Code/Interactions/mitkDisplayCoordinateOperation.h +++ b/Core/Code/Interactions/mitkDisplayCoordinateOperation.h @@ -1,84 +1,84 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKDISPLAYCOORDINATEOPERATION_H_HEADER_INCLUDED_C10E33D0 #define MITKDISPLAYCOORDINATEOPERATION_H_HEADER_INCLUDED_C10E33D0 #include #include "mitkBaseRenderer.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkOperation.h" #include #define mitkGetMacro(name,type) \ virtual type Get##name () \ { \ return this->m_##name; \ } namespace mitk { // TODO Legacy , no longer necessary when after migrating all DisplayInteractions to new Interactions. // Coordinate supplier can probably also be removed then. //##Documentation //## @brief Operation with informations necessary for operations of DisplayVectorInteractor //## @ingroup Undo class MITK_CORE_EXPORT DisplayCoordinateOperation : public Operation { public: DisplayCoordinateOperation(mitk::OperationType operationType, mitk::BaseRenderer* renderer, const mitk::Point2D& startDisplayCoordinate, const mitk::Point2D& lastDisplayCoordinate, const mitk::Point2D& currentDisplayCoordinate ); DisplayCoordinateOperation(mitk::OperationType operationType, mitk::BaseRenderer* renderer, const mitk::Point2D& startDisplayCoordinate, const mitk::Point2D& lastDisplayCoordinate, const mitk::Point2D& currentDisplayCoordinate, const mitk::Point2D& startCoordinateInMM ); virtual ~DisplayCoordinateOperation(); mitk::BaseRenderer* GetRenderer(); mitkGetMacro(StartDisplayCoordinate, mitk::Point2D); mitkGetMacro(LastDisplayCoordinate, mitk::Point2D); mitkGetMacro(CurrentDisplayCoordinate, mitk::Point2D); mitkGetMacro(StartCoordinateInMM, mitk::Point2D); mitk::Vector2D GetLastToCurrentDisplayVector(); mitk::Vector2D GetStartToCurrentDisplayVector(); mitk::Vector2D GetStartToLastDisplayVector(); private: mitk::WeakPointer< mitk::BaseRenderer > m_Renderer; const mitk::Point2D m_StartDisplayCoordinate; const mitk::Point2D m_LastDisplayCoordinate; const mitk::Point2D m_CurrentDisplayCoordinate; const mitk::Point2D m_StartCoordinateInMM; }; } #endif /* MITKDISPLAYCOORDINATEOPERATION_H_HEADER_INCLUDED_C10E33D0 */ diff --git a/Core/Code/Interactions/mitkDisplayPositionEvent.h b/Core/Code/Interactions/mitkDisplayPositionEvent.h index b975c45b93..b85111e0d5 100644 --- a/Core/Code/Interactions/mitkDisplayPositionEvent.h +++ b/Core/Code/Interactions/mitkDisplayPositionEvent.h @@ -1,87 +1,87 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef DISPLAYPOSITIONEVENT_H_HEADER_INCLUDED_C184F366 #define DISPLAYPOSITIONEVENT_H_HEADER_INCLUDED_C184F366 #include #include "mitkEvent.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkDataNode.h" namespace mitk { /** * \brief Event that stores coordinates * * Stores display position of the mouse. * * If requested, the correspondent 3D world position in mm is calculated via * picking (delegated to the BaseRenderer). Additionally, the mitk::BaseData or * mitk::DataNode corresponding to the picked object in the (3D) scene can * be retrieved. * \ingroup Interaction * \deprecatedSince{2013_03} mitk::DisplayPositionEvent is deprecated. Use mitk::InteractionPositionEvent instead. * Refer to \see DataInteractionPage for general information about the concept of the new implementation. */ class MITK_CORE_EXPORT DisplayPositionEvent : public Event { public: /** \brief Constructor with all necessary arguments. * * \param sender is the renderer that caused that event * \param type, button, buttonState, key: information from the Event * \param displPosition is the 2D Position of the mouse */ DisplayPositionEvent(BaseRenderer* sender, int type, int button, int buttonState, int key, const Point2D& displPosition); const Point2D& GetDisplayPosition() const { return m_DisplayPosition; } void SetDisplayPosition(const Point2D& displayPosition) { m_DisplayPosition = displayPosition; m_WorldPositionIsSet = false; m_PickedObjectIsSet = false; } Point3D& GetWorldPosition() const; /** Returns node with object at the current position (NULL if not applicable) */ mitk::DataNode *GetPickedObjectNode() const; /** Returns object at the current position (NULL if not applicable) */ mitk::BaseData *GetPickedObject() const; protected: Point2D m_DisplayPosition; mutable Point3D m_WorldPosition; mutable bool m_WorldPositionIsSet; mutable mitk::DataNode::Pointer m_PickedObjectNode; mutable bool m_PickedObjectIsSet; }; typedef DisplayPositionEvent MouseEvent; } // namespace mitk #endif /* DISPLAYPOSITIONozsiEVENT_H_HEADER_INCLUDED_C184F366 */ diff --git a/Core/Code/Interactions/mitkKeyEvent.h b/Core/Code/Interactions/mitkKeyEvent.h index df128ed817..41c4f67d67 100644 --- a/Core/Code/Interactions/mitkKeyEvent.h +++ b/Core/Code/Interactions/mitkKeyEvent.h @@ -1,86 +1,86 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef KeyEvent_H_HEADER_INCLUDED_C184F366 #define KeyEvent_H_HEADER_INCLUDED_C184F366 #include #include "mitkEvent.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" namespace mitk { //##Documentation //## @brief Event that stores coordinates and the key which is pressed //## //## Stores display position of the mouse. If requested, the correspondent //## 3D world position in mm is calculated via picking (delegated to the //## BaseRenderer). //## @ingroup Interaction /** * \deprecatedSince{2013_03} KeyEvent is deprecated. Use mitk::InteractionKeyEvent instead. * Refer to \see DataInteractionPage for general information about the concept of the new implementation. */ class MITK_CORE_EXPORT KeyEvent : public Event { public: //##Documentation //## @brief Constructor with all necessary arguments. //## //## @param sender is the renderer that caused that event //## @param type, button, buttonState, key: information from the Event //## @param displPosition is the 2D Position of the mouse KeyEvent(BaseRenderer* sender, int type, int button, int buttonState, int key, std::string text, const Point2D& displPosition); const Point2D& GetDisplayPosition() const { return m_DisplayPosition; } void SetDisplayPosition(const Point2D& displPosition) { m_DisplayPosition = displPosition; m_WorldPositionIsSet = false; } const Point3D& GetWorldPosition() const; int GetKey() const { return m_Key; } const char* GetText() const { return m_Text.c_str(); } int GetType() const { return m_Type; } protected: Point2D m_DisplayPosition; int m_Key; std::string m_Text; mutable Point3D m_WorldPosition; mutable bool m_WorldPositionIsSet; }; } // namespace mitk #endif /* DISPLAYPOSITIONozsiEVENT_H_HEADER_INCLUDED_C184F366 */ diff --git a/Core/Code/Interactions/mitkMouseMovePointSetInteractor.h b/Core/Code/Interactions/mitkMouseMovePointSetInteractor.h index 99174d4033..bc22bae081 100644 --- a/Core/Code/Interactions/mitkMouseMovePointSetInteractor.h +++ b/Core/Code/Interactions/mitkMouseMovePointSetInteractor.h @@ -1,69 +1,69 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKMOUSEMOVEPOINTSETINTERACTOR_H_HEADER_INCLUDED_C11202FF #define MITKMOUSEMOVEPOINTSETINTERACTOR_H_HEADER_INCLUDED_C11202FF #include -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include namespace mitk { class DataNode; /** * \brief Interaction with a single point by mouse movement. * * A new point is added by mouse movement, an existing point will be removed before adding a new one. * \ingroup Interaction */ class MITK_CORE_EXPORT MouseMovePointSetInteractor : public PointSetInteractor { public: mitkClassMacro(MouseMovePointSetInteractor, Interactor); mitkNewMacro3Param(Self, const char*, DataNode*, int); mitkNewMacro2Param(Self, const char*, DataNode*); /** * \brief calculates how good the data, this statemachine handles, is hit * by the event. * * overwritten, cause we don't look at the boundingbox, we look at each point * and want to accept mouse movement for setting points */ virtual float CanHandleEvent(StateEvent const* stateEvent) const; protected: /** * \brief Constructor with Param n for limited Set of Points * * if no n is set, then the number of points is unlimited* */ MouseMovePointSetInteractor(const char * type, DataNode* dataNode, int n = -1); /** * \brief Default Destructor **/ virtual ~MouseMovePointSetInteractor(); private: }; } #endif /* MITKMOUSEMOVEPOINTSETINTERACTOR_H_HEADER_INCLUDED_C11202FF */ diff --git a/Core/Code/Interactions/mitkNodeDepententPointSetInteractor.h b/Core/Code/Interactions/mitkNodeDepententPointSetInteractor.h index f7e386d12f..2be61e5fea 100644 --- a/Core/Code/Interactions/mitkNodeDepententPointSetInteractor.h +++ b/Core/Code/Interactions/mitkNodeDepententPointSetInteractor.h @@ -1,77 +1,77 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKNodeDepententPointSetInteractor_H_HEADER_INCLUDED #define MITKNodeDepententPointSetInteractor_H_HEADER_INCLUDED #include -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include #include #include namespace mitk { /** * \brief PointSetInteraction that is dependent on the visibility property of a data node. * * The interactor checks if the renderwindow specific property "visible" of a different node (e.g. image) * specified by @param dependentDataNode is true. The specific renderwindow is specified by the sender of the event. * If the property is true the the object behaves as described by PointSetInteractor. * If not, interaction is blocked. * * This class shows how to write an interactor, that is dependent on a special property of the associated node. * See bug #6047 for further information and a patch to test this class embedded in tutorial Step 5. * \ingroup Interaction */ class MITK_CORE_EXPORT NodeDepententPointSetInteractor : public PointSetInteractor { public: mitkClassMacro(NodeDepententPointSetInteractor, PointSetInteractor); mitkNewMacro4Param(Self, const char*, DataNode*, DataNode*, int); mitkNewMacro3Param(Self, const char*, DataNode*, DataNode*); /** * \brief Checks visibility of the specified node (e.g. image), * returns 0 if node is not visible in sending render window * If Sender within stateEvent is NULL a value of 0 is returned. */ virtual float CanHandleEvent(StateEvent const* stateEvent) const; protected: /** * \brief Constructor with Param n for limited Set of Points * * If no n is set, then the number of points is unlimited * n=0 is not supported. In this case, n is set to 1. */ NodeDepententPointSetInteractor(const char * type, DataNode* dataNode, DataNode* dependentDataNode, int n = -1); /** * \brief Default Destructor **/ virtual ~NodeDepententPointSetInteractor(); public: mitk::DataNode::Pointer m_DependentDataNode; }; } #endif /* MITKNodeDepententPointSetInteractor_H_HEADER_INCLUDED */ diff --git a/Core/Code/Interactions/mitkPointSetInteractor.h b/Core/Code/Interactions/mitkPointSetInteractor.h index f0f6ef6281..eed134fdc7 100644 --- a/Core/Code/Interactions/mitkPointSetInteractor.h +++ b/Core/Code/Interactions/mitkPointSetInteractor.h @@ -1,128 +1,128 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPOINTSETINTERACTOR_H_HEADER_INCLUDED_C11202FF #define MITKPOINTSETINTERACTOR_H_HEADER_INCLUDED_C11202FF #include -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include namespace mitk { class DataNode; /** * \brief Interaction with a set of points. * * Points can be added, removed and moved. * In case the interaction shall be done on a * loaded set of points, the associated data * object (mitkPointSet) needs to be loaded * prior to the instanciation of this object. * The number of points are checked and the internal * statemachine set to the apropriate state. * The management of 0 points is not supported. * In this case, the amount of managed points is set to 1. * \ingroup Interaction */ class MITK_CORE_EXPORT PointSetInteractor : public Interactor { public: mitkClassMacro(PointSetInteractor, Interactor); mitkNewMacro3Param(Self, const char*, DataNode*, int); mitkNewMacro2Param(Self, const char*, DataNode*); /** * @brief Clears all the elements from the given timeStep in the list with undo-functionality and * resets the statemachine */ void Clear( unsigned int timeStep = 0, ScalarType timeInMS = 0.0 ); itkGetMacro( Precision, unsigned int ); itkSetMacro( Precision, unsigned int ); /** * \brief calculates how good the data, this statemachine handles, is hit * by the event. * * overwritten, cause we don't look at the boundingbox, we look at each point */ virtual float CanHandleEvent(StateEvent const* stateEvent) const; /** *@brief If data changed then initialize according to numbers of loaded points **/ virtual void DataChanged(); protected: /** * \brief Constructor with Param n for limited Set of Points * * If no n is set, then the number of points is unlimited * n=0 is not supported. In this case, n is set to 1. */ PointSetInteractor(const char * type, DataNode* dataNode, int n = -1); /** * \brief Default Destructor **/ virtual ~PointSetInteractor(); /** * @brief Convert the given Actions to Operations and send to data and UndoController */ virtual bool ExecuteAction( Action* action, mitk::StateEvent const* stateEvent ); /** \brief Deselects the Points in the PointSet. * supports Undo if enabled */ void UnselectAll( unsigned int timeStep = 0, ScalarType timeInMS = 0.0 ); /** * \brief Selects the point. * supports Undo if enabled. * \param position Needed for declaring operations */ void SelectPoint( int position, unsigned int timeStep = 0, ScalarType timeInMS = 0.0 ); /** \brief to calculate a direction vector from last point and actual * point */ Point3D m_LastPoint; /** \brief summ-vector for Movement */ Vector3D m_SumVec; /** \brief to store the value of precision to pick a point */ unsigned int m_Precision; private: /** * \brief the number of possible points in this object * * if -1, then no limit set */ int m_N; /** * @brief Init the StatateMachine according to the current number of points in case of a loaded pointset. **/ void InitAccordingToNumberOfPoints(); }; } #endif /* MITKPOINTSETINTERACTOR_H_HEADER_INCLUDED_C11202FF */ diff --git a/Core/Code/Interactions/mitkPositionEvent.cpp b/Core/Code/Interactions/mitkPositionEvent.cpp index f4eab0dde6..864de0d500 100644 --- a/Core/Code/Interactions/mitkPositionEvent.cpp +++ b/Core/Code/Interactions/mitkPositionEvent.cpp @@ -1,26 +1,26 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPositionEvent.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" mitk::PositionEvent::PositionEvent(mitk::BaseRenderer* sender, int type, int button, int buttonState, int key, const mitk::Point2D& displPosition, const mitk::Point3D& worldPosition) : DisplayPositionEvent(sender, type, button, buttonState, key, displPosition) { m_WorldPosition = worldPosition; m_WorldPositionIsSet = true; } diff --git a/Core/Code/Interactions/mitkPositionEvent.h b/Core/Code/Interactions/mitkPositionEvent.h index e75df353ad..47bf0315b7 100644 --- a/Core/Code/Interactions/mitkPositionEvent.h +++ b/Core/Code/Interactions/mitkPositionEvent.h @@ -1,55 +1,55 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef POSITIONEVENT_H_HEADER_INCLUDED_C184F366 #define POSITIONEVENT_H_HEADER_INCLUDED_C184F366 #include #include "mitkDisplayPositionEvent.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" namespace mitk { //##Documentation //## @brief Event that stores coordinates //## //## Stores display position of the mouse and 3D world position in mm. //## @ingroup Interaction /** * \deprecatedSince{2013_03} mitk::PositionEvent is deprecated. Use mitk::InteractionPositionEvent instead. * Refer to \see DataInteractionPage for general information about the concept of the new implementation. */ class MITK_CORE_EXPORT PositionEvent : public DisplayPositionEvent { public: //##Documentation //## @brief Constructor with all necessary arguments. //## //## @param sender: the widget, that caused that event, so that it can be asked for worldCoordinates. changed later to a focus //## @param type, button, buttonState, key: information from the Event //## @param displPosition: the 2D Position e.g. from the mouse //## @param worldPosition: the 3D position e.g. from a picking PositionEvent(BaseRenderer* sender, int type, int button, int buttonState, int key, const Point2D& displPosition, const Point3D& worldPosition); }; } // namespace mitk #endif /* POSITIONEVENT_H_HEADER_INCLUDED_C184F366 */ diff --git a/Core/Code/Interactions/mitkPositionTracker.cpp b/Core/Code/Interactions/mitkPositionTracker.cpp index e5e46d41f2..725adc1a02 100755 --- a/Core/Code/Interactions/mitkPositionTracker.cpp +++ b/Core/Code/Interactions/mitkPositionTracker.cpp @@ -1,84 +1,84 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include -#include // for PointDataType +#include // for PointDataType #include mitk::PositionTracker::PositionTracker(const char * type, mitk::OperationActor* /*operationActor*/) : mitk::StateMachine(type) {} bool mitk::PositionTracker::ExecuteAction(Action* /*action*/, mitk::StateEvent const* stateEvent) { bool ok = false; const DisplayPositionEvent* displayPositionEvent = dynamic_cast(stateEvent->GetEvent()); mitk::DataNode::Pointer dtnode; mitk::DataStorage* ds = NULL; if (displayPositionEvent == NULL) return false; if (stateEvent->GetEvent()->GetSender()!=NULL) { ds = stateEvent->GetEvent()->GetSender()->GetDataStorage(); } else { itkWarningMacro(<<"StateEvent::GetSender()==NULL - setting timeInMS to 0"); return false; } if (ds == NULL) return false; // looking for desired point set dtnode = ds->GetNode(mitk::NodePredicateProperty::New("inputdevice", mitk::BoolProperty::New(true))); if (dtnode.IsNull()) return false; dtnode->SetIntProperty("BaseRendererMapperID", stateEvent->GetEvent()->GetSender()->GetMapperID()); mitk::PointSet* ps = dynamic_cast(dtnode->GetData()); if (ps == NULL) return false; int position = 0; if( ps->GetPointSet()->GetPoints()->IndexExists( position )) //first element { ps->GetPointSet()->GetPoints()->SetElement( position, displayPositionEvent->GetWorldPosition()); } else { mitk::PointSet::PointDataType pointData = {static_cast(position) , false /*selected*/, mitk::PTUNDEFINED}; ps->GetPointSet()->GetPointData()->InsertElement(position, pointData); ps->GetPointSet()->GetPoints()->InsertElement(position, displayPositionEvent->GetWorldPosition()); } ps->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return ok; } diff --git a/Core/Code/Interactions/mitkWheelEvent.cpp b/Core/Code/Interactions/mitkWheelEvent.cpp index 3b52a99640..f22a03e57a 100644 --- a/Core/Code/Interactions/mitkWheelEvent.cpp +++ b/Core/Code/Interactions/mitkWheelEvent.cpp @@ -1,31 +1,31 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkWheelEvent.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" mitk::WheelEvent::WheelEvent(mitk::BaseRenderer* sender, int type, int button, int buttonState, int key, const mitk::Point2D& displPosition, int delta) : DisplayPositionEvent(sender, type, button, buttonState, key, displPosition) { //m_WorldPosition = worldPosition; //m_WorldPositionIsSet = true; m_Delta = delta; } int mitk::WheelEvent::GetDelta() const { return m_Delta; } diff --git a/Core/Code/Interactions/mitkWheelEvent.h b/Core/Code/Interactions/mitkWheelEvent.h index 44bd907f56..f40e4d0105 100644 --- a/Core/Code/Interactions/mitkWheelEvent.h +++ b/Core/Code/Interactions/mitkWheelEvent.h @@ -1,60 +1,60 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef WheelEvent_H_HEADER_INCLUDED_C184F366 #define WheelEvent_H_HEADER_INCLUDED_C184F366 #include #include "mitkDisplayPositionEvent.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" namespace mitk { //##Documentation //## @brief Event that stores coordinates and rotation on mouse wheel events //## //## Stores display position of the mouse and 3D world position in mm. //## @ingroup Interaction /** * \deprecatedSince{2013_03} mitk::WheelEvent is deprecated. Use mitk::MouseWheelEvent instead. * Refer to \see DataInteractionPage for general information about the concept of the new implementation. */ class MITK_CORE_EXPORT WheelEvent : public DisplayPositionEvent { public: //##Documentation //## @brief Constructor with all necessary arguments. //## //## @param sender: the widget, that caused that event, so that it can be asked for worldCoordinates. changed later to a focus //## @param type, button, buttonState, key: information from the Event //## @param displPosition: the 2D Position e.g. from the mouse //## @param worldPosition: the 3D position e.g. from a picking //## @param delta: the movement of the mousewheel WheelEvent(BaseRenderer* sender, int type, int button, int buttonState, int key, const Point2D& displPosition, int delta); int GetDelta() const; protected: int m_Delta; }; } // namespace mitk #endif /* WheelEvent_H_HEADER_INCLUDED_C184F366 */ diff --git a/Core/Code/Rendering/mitkBaseRenderer.cpp b/Core/Code/Rendering/mitkBaseRenderer.cpp index 1d7454239f..493306c976 100644 --- a/Core/Code/Rendering/mitkBaseRenderer.cpp +++ b/Core/Code/Rendering/mitkBaseRenderer.cpp @@ -1,876 +1,876 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkBaseRenderer.h" #include "mitkMapper.h" #include "mitkResliceMethodProperty.h" #include "mitkKeyEvent.h" // Geometries #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" // Controllers #include "mitkCameraController.h" #include "mitkSliceNavigationController.h" #include "mitkCameraRotationController.h" #include "mitkVtkInteractorCameraController.h" #ifdef MITK_USE_TD_MOUSE #include "mitkTDMouseVtkCameraController.h" #else #include "mitkCameraController.h" #endif #include "mitkVtkLayerController.h" // Events // TODO: INTERACTION_LEGACY #include "mitkEventMapper.h" #include "mitkGlobalInteraction.h" #include "mitkPositionEvent.h" #include "mitkDisplayPositionEvent.h" #include "mitkProperties.h" #include "mitkWeakPointerProperty.h" #include "mitkInteractionConst.h" #include "mitkOverlayManager.h" // VTK #include #include #include #include #include #include #include mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::baseRendererMap; mitk::BaseRenderer* mitk::BaseRenderer::GetInstance(vtkRenderWindow * renWin) { for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); mapit++) { if ((*mapit).first == renWin) return (*mapit).second; } return NULL; } void mitk::BaseRenderer::AddInstance(vtkRenderWindow* renWin, BaseRenderer* baseRenderer) { if (renWin == NULL || baseRenderer == NULL) return; // ensure that no BaseRenderer is managed twice mitk::BaseRenderer::RemoveInstance(renWin); baseRendererMap.insert(BaseRendererMapType::value_type(renWin, baseRenderer)); } void mitk::BaseRenderer::RemoveInstance(vtkRenderWindow* renWin) { BaseRendererMapType::iterator mapit = baseRendererMap.find(renWin); if (mapit != baseRendererMap.end()) baseRendererMap.erase(mapit); } mitk::BaseRenderer* mitk::BaseRenderer::GetByName(const std::string& name) { for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); mapit++) { if ((*mapit).second->m_Name == name) return (*mapit).second; } return NULL; } vtkRenderWindow* mitk::BaseRenderer::GetRenderWindowByName(const std::string& name) { for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); mapit++) { if ((*mapit).second->m_Name == name) return (*mapit).first; } return NULL; } mitk::BaseRenderer::BaseRenderer(const char* name, vtkRenderWindow * renWin, mitk::RenderingManager* rm,RenderingMode::Type renderingMode) : m_RenderWindow(NULL), m_VtkRenderer(NULL), m_MapperID(defaultMapper), m_DataStorage(NULL), m_RenderingManager(rm), m_LastUpdateTime(0), m_CameraController( NULL), m_SliceNavigationController(NULL), m_CameraRotationController(NULL), /*m_Size(),*/ m_Focused(false), m_WorldGeometry(NULL), m_WorldTimeGeometry(NULL), m_CurrentWorldGeometry(NULL), m_CurrentWorldPlaneGeometry(NULL), m_DisplayGeometry( NULL), m_Slice(0), m_TimeStep(), m_CurrentWorldPlaneGeometryUpdateTime(), m_DisplayGeometryUpdateTime(), m_TimeStepUpdateTime(), m_WorldGeometryData( NULL), m_DisplayGeometryData(NULL), m_CurrentWorldPlaneGeometryData(NULL), m_WorldGeometryNode(NULL), m_DisplayGeometryNode(NULL), m_CurrentWorldPlaneGeometryNode( NULL), m_DisplayGeometryTransformTime(0), m_CurrentWorldPlaneGeometryTransformTime(0), m_Name(name), /*m_Bounds(),*/m_EmptyWorldGeometry( true), m_NumberOfVisibleLODEnabledMappers(0) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; if (name != NULL) { m_Name = name; } else { m_Name = "unnamed renderer"; itkWarningMacro(<< "Created unnamed renderer. Bad for serialization. Please choose a name."); } if (renWin != NULL) { m_RenderWindow = renWin; m_RenderWindow->Register(NULL); } else { itkWarningMacro(<< "Created mitkBaseRenderer without vtkRenderWindow present."); } m_Size[0] = 0; m_Size[1] = 0; //instances.insert( this ); //adding this BaseRenderer to the List of all BaseRenderer // TODO: INTERACTION_LEGACY m_RenderingManager->GetGlobalInteraction()->AddFocusElement(this); m_BindDispatcherInteractor = new mitk::BindDispatcherInteractor( GetName() ); WeakPointerProperty::Pointer rendererProp = WeakPointerProperty::New((itk::Object*) this); m_CurrentWorldPlaneGeometry = mitk::PlaneGeometry::New(); m_CurrentWorldPlaneGeometryData = mitk::PlaneGeometryData::New(); m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryNode = mitk::DataNode::New(); m_CurrentWorldPlaneGeometryNode->SetData(m_CurrentWorldPlaneGeometryData); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("renderer", rendererProp); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("layer", IntProperty::New(1000)); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(1)); m_CurrentWorldPlaneGeometryTransformTime = m_CurrentWorldPlaneGeometryNode->GetVtkTransform()->GetMTime(); m_DisplayGeometry = mitk::DisplayGeometry::New(); m_DisplayGeometry->SetWorldGeometry(m_CurrentWorldPlaneGeometry); m_DisplayGeometryData = mitk::PlaneGeometryData::New(); m_DisplayGeometryData->SetPlaneGeometry(m_DisplayGeometry); m_DisplayGeometryNode = mitk::DataNode::New(); m_DisplayGeometryNode->SetData(m_DisplayGeometryData); m_DisplayGeometryNode->GetPropertyList()->SetProperty("renderer", rendererProp); m_DisplayGeometryTransformTime = m_DisplayGeometryNode->GetVtkTransform()->GetMTime(); mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::SliceNavigationController::New("navigation"); sliceNavigationController->SetRenderer(this); sliceNavigationController->ConnectGeometrySliceEvent(this); sliceNavigationController->ConnectGeometryUpdateEvent(this); sliceNavigationController->ConnectGeometryTimeEvent(this, false); m_SliceNavigationController = sliceNavigationController; m_CameraRotationController = mitk::CameraRotationController::New(); m_CameraRotationController->SetRenderWindow(m_RenderWindow); m_CameraRotationController->AcquireCamera(); //if TD Mouse Interaction is activated, then call TDMouseVtkCameraController instead of VtkInteractorCameraController #ifdef MITK_USE_TD_MOUSE m_CameraController = mitk::TDMouseVtkCameraController::New(); #else m_CameraController = mitk::CameraController::New(NULL); #endif m_VtkRenderer = vtkRenderer::New(); if( renderingMode == RenderingMode::DepthPeeling ) { m_VtkRenderer->SetUseDepthPeeling(1); m_VtkRenderer->SetMaximumNumberOfPeels(8); m_VtkRenderer->SetOcclusionRatio(0.0); } if (mitk::VtkLayerController::GetInstance(m_RenderWindow) == NULL) { mitk::VtkLayerController::AddInstance(m_RenderWindow, m_VtkRenderer); mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer); } else mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer); } mitk::BaseRenderer::~BaseRenderer() { if (m_OverlayManager.IsNotNull()) { m_OverlayManager->RemoveBaseRenderer(this); } if (m_VtkRenderer != NULL) { m_VtkRenderer->Delete(); m_VtkRenderer = NULL; } if (m_CameraController.IsNotNull()) m_CameraController->SetRenderer(NULL); m_RenderingManager->GetGlobalInteraction()->RemoveFocusElement(this); mitk::VtkLayerController::RemoveInstance(m_RenderWindow); RemoveAllLocalStorages(); m_DataStorage = NULL; if (m_BindDispatcherInteractor != NULL) { delete m_BindDispatcherInteractor; } if (m_RenderWindow != NULL) { m_RenderWindow->Delete(); m_RenderWindow = NULL; } } void mitk::BaseRenderer::RemoveAllLocalStorages() { this->InvokeEvent(mitk::BaseRenderer::RendererResetEvent()); std::list::iterator it; for (it = m_RegisteredLocalStorageHandlers.begin(); it != m_RegisteredLocalStorageHandlers.end(); it++) (*it)->ClearLocalStorage(this, false); m_RegisteredLocalStorageHandlers.clear(); } void mitk::BaseRenderer::RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.push_back(lsh); } mitk::Dispatcher::Pointer mitk::BaseRenderer::GetDispatcher() const { return m_BindDispatcherInteractor->GetDispatcher(); } mitk::Point3D mitk::BaseRenderer::Map2DRendererPositionTo3DWorldPosition(const Point2D& mousePosition) const { Point2D p_mm; Point3D position; if (m_MapperID == 1) { GetDisplayGeometry()->DisplayToWorld(mousePosition, p_mm); GetDisplayGeometry()->Map(p_mm, position); } else if (m_MapperID == 2) { PickWorldPoint(mousePosition, position); } return position; } void mitk::BaseRenderer::UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.remove(lsh); } void mitk::BaseRenderer::SetDataStorage(DataStorage* storage) { if (storage != NULL) { m_DataStorage = storage; m_BindDispatcherInteractor->SetDataStorage(m_DataStorage); this->Modified(); } } const mitk::BaseRenderer::MapperSlotId mitk::BaseRenderer::defaultMapper = 1; void mitk::BaseRenderer::Paint() { } void mitk::BaseRenderer::Initialize() { } void mitk::BaseRenderer::Resize(int w, int h) { m_Size[0] = w; m_Size[1] = h; if (m_CameraController) m_CameraController->Resize(w, h); //(formerly problematic on windows: vtkSizeBug) GetDisplayGeometry()->SetSizeInDisplayUnits(w, h); } void mitk::BaseRenderer::InitRenderer(vtkRenderWindow* renderwindow) { if (m_RenderWindow != NULL) { m_RenderWindow->Delete(); } m_RenderWindow = renderwindow; if (m_RenderWindow != NULL) { m_RenderWindow->Register(NULL); } RemoveAllLocalStorages(); if (m_CameraController.IsNotNull()) { m_CameraController->SetRenderer(this); } } void mitk::BaseRenderer::InitSize(int w, int h) { m_Size[0] = w; m_Size[1] = h; GetDisplayGeometry()->SetSizeInDisplayUnits(w, h, false); GetDisplayGeometry()->Fit(); } void mitk::BaseRenderer::SetSlice(unsigned int slice) { if (m_Slice != slice) { m_Slice = slice; if (m_WorldTimeGeometry.IsNotNull()) { SlicedGeometry3D* slicedWorldGeometry = dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != NULL) { if (m_Slice >= slicedWorldGeometry->GetSlices()) m_Slice = slicedWorldGeometry->GetSlices() - 1; SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } void mitk::BaseRenderer::SetOverlayManager(itk::SmartPointer overlayManager) { if(overlayManager.IsNull()) return; if(this->m_OverlayManager.IsNotNull()) { if(this->m_OverlayManager.GetPointer() == overlayManager.GetPointer()) { return; } else { this->m_OverlayManager->RemoveBaseRenderer(this); } } this->m_OverlayManager = overlayManager; this->m_OverlayManager->AddBaseRenderer(this); //TODO } itk::SmartPointer mitk::BaseRenderer::GetOverlayManager() { if(this->m_OverlayManager.IsNull()) { m_OverlayManager = mitk::OverlayManager::New(); m_OverlayManager->AddBaseRenderer(this); } return this->m_OverlayManager; } void mitk::BaseRenderer::SetTimeStep(unsigned int timeStep) { if (m_TimeStep != timeStep) { m_TimeStep = timeStep; m_TimeStepUpdateTime.Modified(); if (m_WorldTimeGeometry.IsNotNull()) { if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; SlicedGeometry3D* slicedWorldGeometry = dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != NULL) { SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } int mitk::BaseRenderer::GetTimeStep(const mitk::BaseData* data) const { if ((data == NULL) || (data->IsInitialized() == false)) { return -1; } return data->GetTimeGeometry()->TimePointToTimeStep(GetTime()); } mitk::ScalarType mitk::BaseRenderer::GetTime() const { if (m_WorldTimeGeometry.IsNull()) { return 0; } else { ScalarType timeInMS = m_WorldTimeGeometry->TimeStepToTimePoint(GetTimeStep()); - if (timeInMS == ScalarTypeNumericTraits::NonpositiveMin()) + if (timeInMS == itk::NumericTraits::NonpositiveMin()) return 0; else return timeInMS; } } void mitk::BaseRenderer::SetWorldTimeGeometry(mitk::TimeGeometry* geometry) { assert(geometry != NULL); itkDebugMacro("setting WorldTimeGeometry to " << geometry); if (m_WorldTimeGeometry != geometry) { if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() == 0) return; m_WorldTimeGeometry = geometry; itkDebugMacro("setting WorldTimeGeometry to " << m_WorldTimeGeometry); if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; BaseGeometry* geometry3d; geometry3d = m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep); SetWorldGeometry3D(geometry3d); } } void mitk::BaseRenderer::SetWorldGeometry3D(mitk::BaseGeometry* geometry) { itkDebugMacro("setting WorldGeometry3D to " << geometry); if (m_WorldGeometry != geometry) { if (geometry->GetBoundingBox()->GetDiagonalLength2() == 0) return; m_WorldGeometry = geometry; SlicedGeometry3D* slicedWorldGeometry; slicedWorldGeometry = dynamic_cast(geometry); PlaneGeometry::Pointer geometry2d; if (slicedWorldGeometry != NULL) { if (m_Slice >= slicedWorldGeometry->GetSlices() && (m_Slice != 0)) m_Slice = slicedWorldGeometry->GetSlices() - 1; geometry2d = slicedWorldGeometry->GetPlaneGeometry(m_Slice); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(slicedWorldGeometry); geometry2d = plane; } SetCurrentWorldGeometry(slicedWorldGeometry); } else { geometry2d = dynamic_cast(geometry); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = PlaneGeometry::New(); plane->InitializeStandardPlane(geometry); geometry2d = plane; } SetCurrentWorldGeometry(geometry); } SetCurrentWorldPlaneGeometry(geometry2d); // calls Modified() } if (m_CurrentWorldPlaneGeometry.IsNull()) itkWarningMacro("m_CurrentWorldPlaneGeometry is NULL"); } void mitk::BaseRenderer::SetDisplayGeometry(mitk::DisplayGeometry* geometry2d) { itkDebugMacro("setting DisplayGeometry to " << geometry2d); if (m_DisplayGeometry != geometry2d) { m_DisplayGeometry = geometry2d; m_DisplayGeometryData->SetPlaneGeometry(m_DisplayGeometry); m_DisplayGeometryUpdateTime.Modified(); Modified(); } } void mitk::BaseRenderer::SetCurrentWorldPlaneGeometry(mitk::PlaneGeometry* geometry2d) { if (m_CurrentWorldPlaneGeometry != geometry2d) { m_CurrentWorldPlaneGeometry = geometry2d; m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_DisplayGeometry->SetWorldGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryUpdateTime.Modified(); Modified(); } } void mitk::BaseRenderer::SendUpdateSlice() { m_DisplayGeometryUpdateTime.Modified(); m_CurrentWorldPlaneGeometryUpdateTime.Modified(); } void mitk::BaseRenderer::SetCurrentWorldGeometry(mitk::BaseGeometry* geometry) { m_CurrentWorldGeometry = geometry; if (geometry == NULL) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; m_EmptyWorldGeometry = true; return; } BoundingBox::Pointer boundingBox = m_CurrentWorldGeometry->CalculateBoundingBoxRelativeToTransform(NULL); const BoundingBox::BoundsArrayType& worldBounds = boundingBox->GetBounds(); m_Bounds[0] = worldBounds[0]; m_Bounds[1] = worldBounds[1]; m_Bounds[2] = worldBounds[2]; m_Bounds[3] = worldBounds[3]; m_Bounds[4] = worldBounds[4]; m_Bounds[5] = worldBounds[5]; if (boundingBox->GetDiagonalLength2() <= mitk::eps) m_EmptyWorldGeometry = true; else m_EmptyWorldGeometry = false; } void mitk::BaseRenderer::UpdateOverlays() { if(m_OverlayManager.IsNotNull()) { m_OverlayManager->UpdateOverlays(this); } } void mitk::BaseRenderer::SetGeometry(const itk::EventObject & geometrySendEvent) { const SliceNavigationController::GeometrySendEvent* sendEvent = dynamic_cast(&geometrySendEvent); assert(sendEvent!=NULL); SetWorldTimeGeometry(sendEvent->GetTimeGeometry()); } void mitk::BaseRenderer::UpdateGeometry(const itk::EventObject & geometryUpdateEvent) { const SliceNavigationController::GeometryUpdateEvent* updateEvent = dynamic_cast(&geometryUpdateEvent); if (updateEvent == NULL) return; if (m_CurrentWorldGeometry.IsNotNull()) { SlicedGeometry3D* slicedWorldGeometry = dynamic_cast(m_CurrentWorldGeometry.GetPointer()); if (slicedWorldGeometry) { PlaneGeometry* geometry2D = slicedWorldGeometry->GetPlaneGeometry(m_Slice); SetCurrentWorldPlaneGeometry(geometry2D); // calls Modified() } } } void mitk::BaseRenderer::SetGeometrySlice(const itk::EventObject & geometrySliceEvent) { const SliceNavigationController::GeometrySliceEvent* sliceEvent = dynamic_cast(&geometrySliceEvent); assert(sliceEvent!=NULL); SetSlice(sliceEvent->GetPos()); } void mitk::BaseRenderer::SetGeometryTime(const itk::EventObject & geometryTimeEvent) { const SliceNavigationController::GeometryTimeEvent * timeEvent = dynamic_cast(&geometryTimeEvent); assert(timeEvent!=NULL); SetTimeStep(timeEvent->GetPos()); } const double* mitk::BaseRenderer::GetBounds() const { return m_Bounds; } void mitk::BaseRenderer::MousePressEvent(mitk::MouseEvent *me) { //set the Focus on the renderer /*bool success =*/m_RenderingManager->GetGlobalInteraction()->SetFocus(this); /* if (! success) mitk::StatusBar::GetInstance()->DisplayText("Warning! from mitkBaseRenderer.cpp: Couldn't focus this BaseRenderer!"); */ //if (m_CameraController) //{ // if(me->GetButtonState()!=512) // provisorisch: Ctrl nicht durchlassen. Bald wird aus m_CameraController eine StateMachine // m_CameraController->MousePressEvent(me); //} if (m_MapperID == 1) { Point2D p(me->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::PositionEvent event(this, me->GetType(), me->GetButton(), me->GetButtonState(), mitk::Key_unknown, p, position); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID > 1) //==2 for 3D and ==5 for stencil { Point2D p(me->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); me->SetDisplayPosition(p); mitk::EventMapper::MapEvent(me, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::MouseReleaseEvent(mitk::MouseEvent *me) { //if (m_CameraController) //{ // if(me->GetButtonState()!=512) // provisorisch: Ctrl nicht durchlassen. Bald wird aus m_CameraController eine StateMachine // m_CameraController->MouseReleaseEvent(me); //} if (m_MapperID == 1) { Point2D p(me->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::PositionEvent event(this, me->GetType(), me->GetButton(), me->GetButtonState(), mitk::Key_unknown, p, position); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID == 2) { Point2D p(me->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); me->SetDisplayPosition(p); mitk::EventMapper::MapEvent(me, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::MouseMoveEvent(mitk::MouseEvent *me) { //if (m_CameraController) //{ // if((me->GetButtonState()<=512) || (me->GetButtonState()>=516))// provisorisch: Ctrl nicht durchlassen. Bald wird aus m_CameraController eine StateMachine // m_CameraController->MouseMoveEvent(me); //} if (m_MapperID == 1) { Point2D p(me->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::PositionEvent event(this, me->GetType(), me->GetButton(), me->GetButtonState(), mitk::Key_unknown, p, position); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID == 2) { Point2D p(me->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); me->SetDisplayPosition(p); mitk::EventMapper::MapEvent(me, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::PickWorldPoint(const mitk::Point2D& displayPoint, mitk::Point3D& worldPoint) const { mitk::Point2D worldPoint2D; GetDisplayGeometry()->DisplayToWorld(displayPoint, worldPoint2D); GetDisplayGeometry()->Map(worldPoint2D, worldPoint); } void mitk::BaseRenderer::WheelEvent(mitk::WheelEvent * we) { if (m_MapperID == 1) { Point2D p(we->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::PositionEvent event(this, we->GetType(), we->GetButton(), we->GetButtonState(), mitk::Key_unknown, p, position); mitk::EventMapper::MapEvent(we, m_RenderingManager->GetGlobalInteraction()); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID == 2) { Point2D p(we->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); we->SetDisplayPosition(p); mitk::EventMapper::MapEvent(we, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::KeyPressEvent(mitk::KeyEvent *ke) { if (m_MapperID == 1) { Point2D p(ke->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::KeyEvent event(this, ke->GetType(), ke->GetButton(), ke->GetButtonState(), ke->GetKey(), ke->GetText(), p); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID == 2) { Point2D p(ke->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); ke->SetDisplayPosition(p); mitk::EventMapper::MapEvent(ke, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::DrawOverlayMouse(mitk::Point2D& itkNotUsed(p2d)) { MITK_INFO<<"BaseRenderer::DrawOverlayMouse()- should be inconcret implementation OpenGLRenderer."<RequestUpdate(this->m_RenderWindow); } void mitk::BaseRenderer::ForceImmediateUpdate() { m_RenderingManager->ForceImmediateUpdate(this->m_RenderWindow); } unsigned int mitk::BaseRenderer::GetNumberOfVisibleLODEnabledMappers() const { return m_NumberOfVisibleLODEnabledMappers; } mitk::RenderingManager* mitk::BaseRenderer::GetRenderingManager() const { return m_RenderingManager.GetPointer(); } /*! Sets the new Navigation controller */ void mitk::BaseRenderer::SetSliceNavigationController(mitk::SliceNavigationController *SlicenavigationController) { if (SlicenavigationController == NULL) return; //disconnect old from globalinteraction m_RenderingManager->GetGlobalInteraction()->RemoveListener(SlicenavigationController); //copy worldgeometry SlicenavigationController->SetInputWorldTimeGeometry(SlicenavigationController->GetCreatedWorldGeometry()); SlicenavigationController->Update(); //set new m_SliceNavigationController = SlicenavigationController; m_SliceNavigationController->SetRenderer(this); if (m_SliceNavigationController.IsNotNull()) { m_SliceNavigationController->ConnectGeometrySliceEvent(this); m_SliceNavigationController->ConnectGeometryUpdateEvent(this); m_SliceNavigationController->ConnectGeometryTimeEvent(this, false); } } /*! Sets the new camera controller and deletes the vtkRenderWindowInteractor in case of the VTKInteractorCameraController */ void mitk::BaseRenderer::SetCameraController(CameraController* cameraController) { mitk::VtkInteractorCameraController::Pointer vtkInteractorCameraController = dynamic_cast(cameraController); if (vtkInteractorCameraController.IsNotNull()) MITK_INFO<<"!!!WARNING!!!: RenderWindow interaction events are no longer handled via CameraController (See Bug #954)."<SetRenderer(NULL); m_CameraController = NULL; m_CameraController = cameraController; m_CameraController->SetRenderer(this); } void mitk::BaseRenderer::PrintSelf(std::ostream& os, itk::Indent indent) const { os << indent << " MapperID: " << m_MapperID << std::endl; os << indent << " Slice: " << m_Slice << std::endl; os << indent << " TimeStep: " << m_TimeStep << std::endl; os << indent << " WorldGeometry: "; if (m_WorldGeometry.IsNull()) os << "NULL" << std::endl; else m_WorldGeometry->Print(os, indent); os << indent << " CurrentWorldPlaneGeometry: "; if (m_CurrentWorldPlaneGeometry.IsNull()) os << "NULL" << std::endl; else m_CurrentWorldPlaneGeometry->Print(os, indent); os << indent << " CurrentWorldPlaneGeometryUpdateTime: " << m_CurrentWorldPlaneGeometryUpdateTime << std::endl; os << indent << " CurrentWorldPlaneGeometryTransformTime: " << m_CurrentWorldPlaneGeometryTransformTime << std::endl; os << indent << " DisplayGeometry: "; if (m_DisplayGeometry.IsNull()) os << "NULL" << std::endl; else m_DisplayGeometry->Print(os, indent); os << indent << " DisplayGeometryTransformTime: " << m_DisplayGeometryTransformTime << std::endl; Superclass::PrintSelf(os, indent); } diff --git a/Core/Code/Rendering/mitkPointSetGLMapper2D.cpp b/Core/Code/Rendering/mitkPointSetGLMapper2D.cpp index 968099664a..da947a7855 100644 --- a/Core/Code/Rendering/mitkPointSetGLMapper2D.cpp +++ b/Core/Code/Rendering/mitkPointSetGLMapper2D.cpp @@ -1,521 +1,521 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPointSetGLMapper2D.h" #include "mitkPointSet.h" #include "mitkPlaneGeometry.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "vtkLinearTransform.h" #include "mitkStringProperty.h" #include "mitkPointSet.h" #include "mitkVtkPropRenderer.h" #include "mitkGL.h" //const float selectedColor[]={1.0,0.0,0.6}; //for selected! mitk::PointSetGLMapper2D::PointSetGLMapper2D() : m_Polygon(false), m_ShowPoints(true), m_ShowDistances(false), m_DistancesDecimalDigits(1), m_ShowAngles(false), m_ShowDistantLines(true), m_LineWidth(1) { } mitk::PointSetGLMapper2D::~PointSetGLMapper2D() { } const mitk::PointSet *mitk::PointSetGLMapper2D::GetInput(void) { return static_cast ( GetDataNode()->GetData() ); } void mitk::PointSetGLMapper2D::ApplyAllProperties(mitk::BaseRenderer* renderer) { GLMapper::ApplyColorAndOpacityProperties( renderer ); const mitk::DataNode* node=GetDataNode(); if( node == NULL ) return; node->GetBoolProperty("show contour", m_Polygon); node->GetBoolProperty("close contour", m_PolygonClosed); node->GetBoolProperty("show points", m_ShowPoints); node->GetBoolProperty("show distances", m_ShowDistances); node->GetIntProperty("distance decimal digits", m_DistancesDecimalDigits); node->GetBoolProperty("show angles", m_ShowAngles); node->GetBoolProperty("show distant lines", m_ShowDistantLines); node->GetIntProperty("line width", m_LineWidth); node->GetIntProperty("point line width", m_PointLineWidth); node->GetIntProperty("point 2D size", m_Point2DSize); } static bool makePerpendicularVector2D(const mitk::Vector2D& in, mitk::Vector2D& out) { if((fabs(in[0])>0) && ( (fabs(in[0])>fabs(in[1])) || (in[1] == 0) ) ) { out[0]=-in[1]/in[0]; out[1]=1; out.Normalize(); return true; } else if(fabs(in[1])>0) { out[0]=1; out[1]=-in[0]/in[1]; out.Normalize(); return true; } else return false; } void mitk::PointSetGLMapper2D::Paint( mitk::BaseRenderer *renderer ) { const mitk::DataNode* node=GetDataNode(); if( node == NULL ) return; const int text2dDistance = 10; bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible) return; // @FIXME: Logik fuer update bool updateNeccesary=true; if (updateNeccesary) { // ok, das ist aus GenerateData kopiert mitk::PointSet::Pointer input = const_cast(this->GetInput()); // Get the TimeGeometry of the input object const TimeGeometry* inputTimeGeometry = input->GetTimeGeometry(); if (( inputTimeGeometry == NULL ) || ( inputTimeGeometry->CountTimeSteps() == 0 ) ) { return; } // // get the world time // ScalarType time = renderer->GetTime(); // // convert the world time in time steps of the input object // int timeStep=0; - if ( time > ScalarTypeNumericTraits::NonpositiveMin() ) + if ( time > itk::NumericTraits::NonpositiveMin() ) timeStep = inputTimeGeometry->TimePointToTimeStep( time ); if ( inputTimeGeometry->IsValidTimeStep( timeStep ) == false ) { return; } mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet( timeStep ); if ( itkPointSet.GetPointer() == NULL) { return; } mitk::DisplayGeometry::Pointer displayGeometry = renderer->GetDisplayGeometry(); assert(displayGeometry.IsNotNull()); //apply color and opacity read from the PropertyList this->ApplyAllProperties(renderer); vtkLinearTransform* transform = GetDataNode()->GetVtkTransform(); //List of the Points PointSet::DataType::PointsContainerConstIterator it, end; it = itkPointSet->GetPoints()->Begin(); end = itkPointSet->GetPoints()->End(); //iterator on the additional data of each point PointSet::DataType::PointDataContainerIterator selIt, selEnd; bool pointDataBroken = (itkPointSet->GetPointData()->Size() != itkPointSet->GetPoints()->Size()); selIt = itkPointSet->GetPointData()->Begin(); selEnd = itkPointSet->GetPointData()->End(); int counter = 0; //for writing text int j = 0; //for switching back to old color after using selected color float recallColor[4]; glGetFloatv(GL_CURRENT_COLOR,recallColor); //get the properties for coloring the points float unselectedColor[4] = {1.0, 1.0, 0.0, 1.0};//yellow //check if there is an unselected property if (dynamic_cast(node->GetPropertyList(renderer)->GetProperty("unselectedcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("unselectedcolor"))->GetValue(); unselectedColor[0] = tmpColor[0]; unselectedColor[1] = tmpColor[1]; unselectedColor[2] = tmpColor[2]; unselectedColor[3] = 1.0f; //!!define a new ColorProp to be able to pass alpha value } else if (dynamic_cast(node->GetPropertyList(NULL)->GetProperty("unselectedcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("unselectedcolor"))->GetValue(); unselectedColor[0] = tmpColor[0]; unselectedColor[1] = tmpColor[1]; unselectedColor[2] = tmpColor[2]; unselectedColor[3] = 1.0f; //!!define a new ColorProp to be able to pass alpha value } else { //get the color from the dataNode node->GetColor(unselectedColor, NULL); } //get selected property float selectedColor[4] = {1.0, 0.0, 0.6, 1.0}; if (dynamic_cast(node->GetPropertyList(renderer)->GetProperty("selectedcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor"))->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; } else if (dynamic_cast(node->GetPropertyList(NULL)->GetProperty("selectedcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("selectedcolor"))->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; } //check if there is an pointLineWidth property if (dynamic_cast(node->GetPropertyList(renderer)->GetProperty("point line width")) != NULL) { m_PointLineWidth = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("point line width"))->GetValue(); } else if (dynamic_cast(node->GetPropertyList(NULL)->GetProperty("point line width")) != NULL) { m_PointLineWidth = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("point line width"))->GetValue(); } //check if there is an point 2D size property if (dynamic_cast(node->GetPropertyList(renderer)->GetProperty("point 2D size")) != NULL) { m_Point2DSize = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("point 2D size"))->GetValue(); } else if (dynamic_cast(node->GetPropertyList(NULL)->GetProperty("point 2D size")) != NULL) { m_Point2DSize = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("point 2D size"))->GetValue(); } Point3D p; // currently visited point Point3D lastP; // last visited point Vector3D vec; // p - lastP Vector3D lastVec; // lastP - point before lastP vec.Fill(0); mitk::Point3D projected_p; // p projected on viewplane Point2D pt2d; // projected_p in display coordinates Point2D lastPt2d; // last projected_p in display coordinates Point2D preLastPt2d;// projected_p in display coordinates before lastPt2d Point2D lastPt2DInPointSet; // The last point in the pointset in display coordinates mitk::PointSet::DataType::PointType plob; plob.Fill(0); itkPointSet->GetPoint( itkPointSet->GetNumberOfPoints()-1, &plob); //map lastPt2DInPointSet to display coordinates float vtkp[3]; itk2vtk(plob, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, lastPt2DInPointSet); displayGeometry->WorldToDisplay(lastPt2DInPointSet, lastPt2DInPointSet); while(it!=end) // iterate over all points { lastP = p; // valid only for counter > 0 lastVec = vec; // valid only for counter > 1 preLastPt2d = lastPt2d; // valid only for counter > 1 lastPt2d = pt2d; // valid only for counter > 0 itk2vtk(it->Value(), vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); vec = p-lastP; // valid only for counter > 0 displayGeometry->Project(p, projected_p); Vector3D diff=p-projected_p; ScalarType scalardiff = diff.GetSquaredNorm(); //MouseOrientation bool isInputDevice=false; bool isRendererSlice = scalardiff < 0.00001; //cause roundoff error if(this->GetDataNode()->GetBoolProperty("inputdevice",isInputDevice) && isInputDevice && !isRendererSlice ) { displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); //Point size depending of distance to slice /*float p_size = (1/scalardiff)*10*m_Point2DSize; if(p_size < m_Point2DSize * 0.6 ) p_size = m_Point2DSize * 0.6 ; else if ( p_size > m_Point2DSize ) p_size = m_Point2DSize;*/ float p_size = (1/scalardiff)*100.0; if(p_size < 6.0 ) p_size = 6.0 ; else if ( p_size > 10.0 ) p_size = 10.0; //draw Point float opacity = (p_size<8)?0.3:1.0;//don't get the opacity from the node? Feature not a bug! Otehrwise the 2D cross is hardly seen. glColor4f(unselectedColor[0],unselectedColor[1],unselectedColor[2],opacity); glPointSize(p_size); //glShadeModel(GL_FLAT); glBegin (GL_POINTS); glVertex2dv(&pt2d[0]); glEnd (); } //for point set if(!isInputDevice && ( (scalardiff<4.0) || (m_Polygon))) { Point2D tmp; displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); Vector2D horz,vert; horz[0]=(float)m_Point2DSize-scalardiff*2; horz[1]=0; vert[0]=0; vert[1]=(float)m_Point2DSize-scalardiff*2; // now paint text if available if (dynamic_cast(this->GetDataNode() ->GetProperty("label")) != NULL) { const char * pointLabel = dynamic_cast( this->GetDataNode()->GetProperty("label"))->GetValue(); std::string l = pointLabel; if (input->GetSize()>1) { // char buffer[20]; // sprintf(buffer,"%d",it->Index()); std::stringstream ss; ss << it->Index(); l.append(ss.str()); } if (unselectedColor != NULL) { mitk::VtkPropRenderer* OpenGLrenderer = dynamic_cast( renderer ); float rgb[3];//yellow rgb[0] = unselectedColor[0]; rgb[1] = unselectedColor[1]; rgb[2] = unselectedColor[2]; OpenGLrenderer->WriteSimpleText(l, pt2d[0] + text2dDistance, pt2d[1] + text2dDistance,rgb[0], rgb[1],rgb[2]); } else { mitk::VtkPropRenderer* OpenGLrenderer = dynamic_cast( renderer ); OpenGLrenderer->WriteSimpleText(l, pt2d[0] + text2dDistance, pt2d[1] + text2dDistance,0.0,1.0,0.0); } } if((m_ShowPoints) && (scalardiff<4.0)) { //check if the point is to be marked as selected if(selIt != selEnd || pointDataBroken) { bool addAsSelected = false; if (pointDataBroken) addAsSelected = false; else if (selIt->Value().selected) addAsSelected = true; else addAsSelected = false; if (addAsSelected) { horz[0]=(float)m_Point2DSize; vert[1]=(float)m_Point2DSize; glColor3f(selectedColor[0],selectedColor[1],selectedColor[2]); glLineWidth(m_PointLineWidth); //a diamond around the point with the selected color glBegin (GL_LINE_LOOP); tmp=pt2d-horz; glVertex2dv(&tmp[0]); tmp=pt2d+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz; glVertex2dv(&tmp[0]); tmp=pt2d-vert; glVertex2dv(&tmp[0]); glEnd (); glLineWidth(1); //the actual point in the specified color to see the usual color of the point glColor3f(unselectedColor[0],unselectedColor[1],unselectedColor[2]); glPointSize(1); glBegin (GL_POINTS); tmp=pt2d; glVertex2dv(&tmp[0]); glEnd (); } else //if not selected { glColor3f(unselectedColor[0],unselectedColor[1],unselectedColor[2]); glLineWidth(m_PointLineWidth); //drawing crosses glBegin (GL_LINES); tmp=pt2d-horz; glVertex2dv(&tmp[0]); tmp=pt2d+horz; glVertex2dv(&tmp[0]); tmp=pt2d-vert; glVertex2dv(&tmp[0]); tmp=pt2d+vert; glVertex2dv(&tmp[0]); glEnd (); glLineWidth(1); } } } bool drawLinesEtc = true; if (!m_ShowDistantLines && counter > 0) // check, whether this line should be drawn { ScalarType currentDistance = displayGeometry->GetWorldGeometry()->SignedDistance(p); ScalarType lastDistance = displayGeometry->GetWorldGeometry()->SignedDistance(lastP); if ( currentDistance * lastDistance > 0.5 ) // points on same side of plane drawLinesEtc = false; } // draw a line if ((m_Polygon && counter>0 && drawLinesEtc) || (m_Polygon && m_PolygonClosed && drawLinesEtc)) { if ((counter == 0) && ( m_PolygonClosed)) { lastPt2d = lastPt2DInPointSet; } //get contour color property float contourColor[4] = {unselectedColor[0], unselectedColor[1], unselectedColor[2], unselectedColor[3]};//so if no property set, then use unselected color if (dynamic_cast(node->GetPropertyList(renderer)->GetProperty("contourcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor"))->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } else if (dynamic_cast(node->GetPropertyList(NULL)->GetProperty("contourcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("contourcolor"))->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } //set this color glColor3f(contourColor[0],contourColor[1],contourColor[2]); glLineWidth( m_LineWidth ); glBegin (GL_LINES); glVertex2dv(&pt2d[0]); glVertex2dv(&lastPt2d[0]); glEnd (); glLineWidth(1.0); if(m_ShowDistances) // calculate and print a distance { std::stringstream buffer; float distance = vec.GetNorm(); buffer<( renderer ); OpenGLrenderer->WriteSimpleText(buffer.str(), pos2d[0], pos2d[1]); //this->WriteTextXY(pos2d[0], pos2d[1], buffer.str(),renderer); } if(m_ShowAngles && counter > 1 ) // calculate and print the angle btw. two lines { std::stringstream buffer; //buffer << angle(vec.Get_vnl_vector(), -lastVec.Get_vnl_vector())*180/vnl_math::pi << "�"; buffer << angle(vec.GetVnlVector(), -lastVec.GetVnlVector())*180/vnl_math::pi << (char)176; Vector2D vec2d = pt2d-lastPt2d; vec2d.Normalize(); Vector2D lastVec2d = lastPt2d-preLastPt2d; lastVec2d.Normalize(); vec2d=vec2d-lastVec2d; vec2d.Normalize(); Vector2D pos2d = lastPt2d.GetVectorFromOrigin()+vec2d*text2dDistance*text2dDistance; mitk::VtkPropRenderer* OpenGLrenderer = dynamic_cast( renderer ); OpenGLrenderer->WriteSimpleText(buffer.str(), pos2d[0], pos2d[1]); //this->WriteTextXY(pos2d[0], pos2d[1], buffer.str(),renderer); } } counter++; } ++it; if(selIt != selEnd && !pointDataBroken) ++selIt; j++; } //recall the color to the same color before this drawing glColor3f(recallColor[0],recallColor[1],recallColor[2]); } } void mitk::PointSetGLMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty( "line width", mitk::IntProperty::New(2), renderer, overwrite ); // width of the line from one point to another node->AddProperty( "point line width", mitk::IntProperty::New(1), renderer, overwrite ); //width of the cross marking a point node->AddProperty( "point 2D size", mitk::IntProperty::New(8), renderer, overwrite ); // length of the cross marking a point // length of an edge of the box marking a point node->AddProperty( "show contour", mitk::BoolProperty::New(false), renderer, overwrite ); // contour of the line between points node->AddProperty( "close contour", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "show points", mitk::BoolProperty::New(true), renderer, overwrite ); //show or hide points node->AddProperty( "show distances", mitk::BoolProperty::New(false), renderer, overwrite ); //show or hide distance measure (not always available) node->AddProperty( "distance decimal digits", mitk::IntProperty::New(2), renderer, overwrite ); //set the number of decimal digits to be shown node->AddProperty( "show angles", mitk::BoolProperty::New(false), renderer, overwrite ); //show or hide angle measurement (not always available) node->AddProperty( "show distant lines", mitk::BoolProperty::New(false), renderer, overwrite ); //show the line between to points from a distant view (equals "always on top" option) node->AddProperty( "layer", mitk::IntProperty::New(1), renderer, overwrite ); // default to draw pointset above images (they have a default layer of 0) Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Core/Code/Rendering/mitkPointSetVtkMapper2D.cpp b/Core/Code/Rendering/mitkPointSetVtkMapper2D.cpp index c414dcc3d5..6d1bb4bc79 100644 --- a/Core/Code/Rendering/mitkPointSetVtkMapper2D.cpp +++ b/Core/Code/Rendering/mitkPointSetVtkMapper2D.cpp @@ -1,717 +1,717 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPointSetVtkMapper2D.h" //mitk includes #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkVtkPropRenderer.h" #include "mitkPointSet.h" #include "mitkPlaneGeometry.h" //vtk includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include // constructor LocalStorage mitk::PointSetVtkMapper2D::LocalStorage::LocalStorage() { // points m_UnselectedPoints = vtkSmartPointer::New(); m_SelectedPoints = vtkSmartPointer::New(); m_ContourPoints = vtkSmartPointer::New(); // scales m_UnselectedScales = vtkSmartPointer::New(); m_SelectedScales = vtkSmartPointer::New(); // distances m_DistancesBetweenPoints = vtkSmartPointer::New(); // lines m_ContourLines = vtkSmartPointer::New(); // glyph source (provides the different shapes) m_UnselectedGlyphSource2D = vtkSmartPointer::New(); m_SelectedGlyphSource2D = vtkSmartPointer::New(); // glyphs m_UnselectedGlyph3D = vtkSmartPointer::New(); m_SelectedGlyph3D = vtkSmartPointer::New(); // polydata m_VtkUnselectedPointListPolyData = vtkSmartPointer::New(); m_VtkSelectedPointListPolyData = vtkSmartPointer ::New(); m_VtkContourPolyData = vtkSmartPointer::New(); // actors m_UnselectedActor = vtkSmartPointer ::New(); m_SelectedActor = vtkSmartPointer ::New(); m_ContourActor = vtkSmartPointer ::New(); // mappers m_VtkUnselectedPolyDataMapper = vtkSmartPointer::New(); m_VtkSelectedPolyDataMapper = vtkSmartPointer::New(); m_VtkContourPolyDataMapper = vtkSmartPointer::New(); // propassembly m_PropAssembly = vtkSmartPointer ::New(); } // destructor LocalStorage mitk::PointSetVtkMapper2D::LocalStorage::~LocalStorage() { } // input for this mapper ( = point set) const mitk::PointSet* mitk::PointSetVtkMapper2D::GetInput() const { return static_cast ( GetDataNode()->GetData() ); } // constructor PointSetVtkMapper2D mitk::PointSetVtkMapper2D::PointSetVtkMapper2D() : m_ShowContour(false), m_CloseContour(false), m_ShowPoints(true), m_ShowDistances(false), m_DistancesDecimalDigits(1), m_ShowAngles(false), m_ShowDistantLines(false), m_LineWidth(1), m_PointLineWidth(1), m_Point2DSize(6), m_IDShapeProperty(mitk::PointSetShapeProperty::CROSS), m_FillShape(false), m_DistanceToPlane(4.0f) { } // destructor mitk::PointSetVtkMapper2D::~PointSetVtkMapper2D() { } // reset mapper so that nothing is displayed e.g. toggle visiblity of the propassembly void mitk::PointSetVtkMapper2D::ResetMapper( BaseRenderer* renderer ) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_PropAssembly->VisibilityOff(); } // returns propassembly vtkProp* mitk::PointSetVtkMapper2D::GetVtkProp(mitk::BaseRenderer * renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_PropAssembly; } static bool makePerpendicularVector2D(const mitk::Vector2D& in, mitk::Vector2D& out) { // The dot product of orthogonal vectors is zero. // In two dimensions the slopes of perpendicular lines are negative reciprocals. if((fabs(in[0])>0) && ( (fabs(in[0])>fabs(in[1])) || (in[1] == 0) ) ) { // negative reciprocal out[0]=-in[1]/in[0]; out[1]=1; out.Normalize(); return true; } else if(fabs(in[1])>0) { out[0]=1; // negative reciprocal out[1]=-in[0]/in[1]; out.Normalize(); return true; } else return false; } void mitk::PointSetVtkMapper2D::CreateVTKRenderObjects(mitk::BaseRenderer* renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); unsigned i = 0; // The vtk text actors need to be removed manually from the propassembly // since the same vtk text actors are not overwriten within this function, // but new actors are added to the propassembly each time this function is executed. // Thus, the actors from the last call must be removed in the beginning. for(i=0; i< ls->m_VtkTextLabelActors.size(); i++) { if(ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextLabelActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextLabelActors.at(i)); } for(i=0; i< ls->m_VtkTextDistanceActors.size(); i++) { if(ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextDistanceActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextDistanceActors.at(i)); } for(i=0; i< ls->m_VtkTextAngleActors.size(); i++) { if(ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextAngleActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextAngleActors.at(i)); } // initialize polydata here, otherwise we have update problems when // executing this function again ls->m_VtkUnselectedPointListPolyData = vtkSmartPointer::New(); ls->m_VtkSelectedPointListPolyData = vtkSmartPointer ::New(); ls->m_VtkContourPolyData = vtkSmartPointer::New(); // get input point set and update the PointSet mitk::PointSet::Pointer input = const_cast(this->GetInput()); // only update the input data, if the property tells us to bool update = true; this->GetDataNode()->GetBoolProperty("updateDataOnRender", update); if (update == true) input->Update(); int timestep = this->GetTimestep(); mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet( timestep ); if ( itkPointSet.GetPointer() == NULL) { ls->m_PropAssembly->VisibilityOff(); return; } //iterator for point set mitk::PointSet::PointsContainer::Iterator pointsIter = itkPointSet->GetPoints()->Begin(); // PointDataContainer has additional information to each point, e.g. whether // it is selected or not mitk::PointSet::PointDataContainer::Iterator pointDataIter; pointDataIter = itkPointSet->GetPointData()->Begin(); //check if the list for the PointDataContainer is the same size as the PointsContainer. //If not, then the points were inserted manually and can not be visualized according to the PointData (selected/unselected) bool pointDataBroken = (itkPointSet->GetPointData()->Size() != itkPointSet->GetPoints()->Size()); if( itkPointSet->GetPointData()->size() == 0 || pointDataBroken) { ls->m_PropAssembly->VisibilityOff(); return; } ls->m_PropAssembly->VisibilityOn(); // empty point sets, cellarrays, scalars ls->m_UnselectedPoints->Reset(); ls->m_SelectedPoints->Reset(); ls->m_ContourPoints->Reset(); ls->m_ContourLines->Reset(); ls->m_UnselectedScales->Reset(); ls->m_SelectedScales->Reset(); ls->m_DistancesBetweenPoints->Reset(); ls->m_VtkTextLabelActors.clear(); ls->m_VtkTextDistanceActors.clear(); ls->m_VtkTextAngleActors.clear(); ls->m_UnselectedScales->SetNumberOfComponents(3); ls->m_SelectedScales->SetNumberOfComponents(3); int NumberContourPoints = 0; bool pointsOnSameSideOfPlane = false; const int text2dDistance = 10; // initialize points with a random start value // current point in point set itk::Point point = pointsIter->Value(); mitk::Point3D p = point; // currently visited point mitk::Point3D lastP = point; // last visited point (predecessor in point set of "point") mitk::Vector3D vec; // p - lastP mitk::Vector3D lastVec; // lastP - point before lastP - vec.Fill(0); - lastVec.Fill(0); + vec.Fill(0.0); + lastVec.Fill(0.0); mitk::Point3D projected_p = point; // p projected on viewplane mitk::Point2D pt2d; pt2d[0] = point[0]; // projected_p in display coordinates pt2d[1] = point[1]; mitk::Point2D lastPt2d = pt2d; // last projected_p in display coordinates (predecessor in point set of "pt2d") mitk::Point2D preLastPt2d = pt2d ; // projected_p in display coordinates before lastPt2 mitk::DisplayGeometry::Pointer displayGeometry = renderer->GetDisplayGeometry(); const mitk::PlaneGeometry* geo2D = renderer->GetCurrentWorldPlaneGeometry(); vtkLinearTransform* dataNodeTransform = input->GetGeometry()->GetVtkTransform(); int count = 0; for (pointsIter=itkPointSet->GetPoints()->Begin(); pointsIter!=itkPointSet->GetPoints()->End(); pointsIter++) { lastP = p; // valid for number of points count > 0 preLastPt2d = lastPt2d; // valid only for count > 1 lastPt2d = pt2d; // valid for number of points count > 0 lastVec = vec; // valid only for counter > 1 // get current point in point set point = pointsIter->Value(); // transform point { float vtkp[3]; itk2vtk(point, vtkp); dataNodeTransform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,point); } p[0] = point[0]; p[1] = point[1]; p[2] = point[2]; displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); vec = p-lastP; // valid only for counter > 0 // compute distance to current plane float diff = geo2D->Distance(point); diff = diff * diff; // draw markers on slices a certain distance away from the points true location according to the tolerance threshold (m_DistanceToPlane) if(diff < m_DistanceToPlane) { // is point selected or not? if (pointDataIter->Value().selected) { ls->m_SelectedPoints->InsertNextPoint(point[0],point[1],point[2]); // point is scaled according to its distance to the plane ls->m_SelectedScales->InsertNextTuple3(m_Point2DSize - (2*diff),0,0); } else { ls->m_UnselectedPoints->InsertNextPoint(point[0],point[1],point[2]); // point is scaled according to its distance to the plane ls->m_UnselectedScales->InsertNextTuple3(m_Point2DSize - (2*diff),0,0); } //---- LABEL -----// // paint label for each point if available if (dynamic_cast(this->GetDataNode()->GetProperty("label")) != NULL) { const char * pointLabel = dynamic_cast( this->GetDataNode()->GetProperty("label"))->GetValue(); std::string l = pointLabel; if (input->GetSize()>1) { std::stringstream ss; ss << pointsIter->Index(); l.append(ss.str()); } ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetPosition(pt2d[0] + text2dDistance, pt2d[1] + text2dDistance); ls->m_VtkTextActor->SetInput(l.c_str()); ls->m_VtkTextActor->GetTextProperty()->SetOpacity( 100 ); float unselectedColor[4]; //check if there is a color property GetDataNode()->GetColor(unselectedColor); if (unselectedColor != NULL) ls->m_VtkTextActor->GetTextProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]); else ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0f, 1.0f, 0.0f); ls->m_VtkTextLabelActors.push_back(ls->m_VtkTextActor); } } // draw contour, distance text and angle text in render window // lines between points, which intersect the current plane, are drawn if( m_ShowContour && count > 0 ) { ScalarType distance = displayGeometry->GetWorldGeometry()->SignedDistance(point); ScalarType lastDistance = displayGeometry->GetWorldGeometry()->SignedDistance(lastP); pointsOnSameSideOfPlane = (distance * lastDistance) > 0.5; // Points must be on different side of plane in order to draw a contour. // If "show distant lines" is enabled this condition is disregarded. if ( !pointsOnSameSideOfPlane || m_ShowDistantLines) { vtkSmartPointer line = vtkSmartPointer::New(); ls->m_ContourPoints->InsertNextPoint(lastP[0],lastP[1],lastP[2]); line->GetPointIds()->SetId(0, NumberContourPoints); NumberContourPoints++; ls->m_ContourPoints->InsertNextPoint(point[0], point[1], point[2]); line->GetPointIds()->SetId(1, NumberContourPoints); NumberContourPoints++; ls->m_ContourLines->InsertNextCell(line); if(m_ShowDistances) // calculate and print distance between adjacent points { float distancePoints = point.EuclideanDistanceTo(lastP); std::stringstream buffer; buffer<m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetPosition(pos2d[0],pos2d[1]); ls->m_VtkTextActor->SetInput(buffer.str().c_str()); ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0); ls->m_VtkTextDistanceActors.push_back(ls->m_VtkTextActor); } if(m_ShowAngles && count > 1) // calculate and print angle between connected lines { std::stringstream buffer; //(char) 176 is the degree sign buffer << angle(vec.GetVnlVector(), -lastVec.GetVnlVector())*180/vnl_math::pi << (char)176; //compute desired display position of text Vector2D vec2d = pt2d-lastPt2d; // first arm enclosing the angle vec2d.Normalize(); Vector2D lastVec2d = lastPt2d-preLastPt2d; // second arm enclosing the angle lastVec2d.Normalize(); vec2d=vec2d-lastVec2d; // vector connecting both arms vec2d.Normalize(); // middle between two vectors that enclose the angle Vector2D pos2d = lastPt2d.GetVectorFromOrigin() + vec2d * text2dDistance * text2dDistance; ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetPosition(pos2d[0],pos2d[1]); ls->m_VtkTextActor->SetInput(buffer.str().c_str()); ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0); ls->m_VtkTextAngleActors.push_back(ls->m_VtkTextActor); } } } if(pointDataIter != itkPointSet->GetPointData()->End()) { pointDataIter++; count++; } } // add each single text actor to the assembly for(i=0; i< ls->m_VtkTextLabelActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextLabelActors.at(i)); } for(i=0; i< ls->m_VtkTextDistanceActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextDistanceActors.at(i)); } for(i=0; i< ls->m_VtkTextAngleActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextAngleActors.at(i)); } //---- CONTOUR -----// //create lines between the points which intersect the plane if (m_ShowContour) { // draw line between first and last point which is rendered if(m_CloseContour && NumberContourPoints > 1){ vtkSmartPointer closingLine = vtkSmartPointer::New(); closingLine->GetPointIds()->SetId(0, 0); // index of first point closingLine->GetPointIds()->SetId(1, NumberContourPoints-1); // index of last point ls->m_ContourLines->InsertNextCell(closingLine); } ls->m_VtkContourPolyData->SetPoints(ls->m_ContourPoints); ls->m_VtkContourPolyData->SetLines(ls->m_ContourLines); ls->m_VtkContourPolyDataMapper->SetInputData(ls->m_VtkContourPolyData); ls->m_ContourActor->SetMapper(ls->m_VtkContourPolyDataMapper); ls->m_ContourActor->GetProperty()->SetLineWidth(m_LineWidth); ls->m_PropAssembly->AddPart(ls->m_ContourActor); } // the point set must be transformed in order to obtain the appropriate glyph orientation // according to the current view vtkSmartPointer transform = vtkSmartPointer::New(); vtkSmartPointer a,b = vtkSmartPointer::New(); a = geo2D->GetVtkTransform()->GetMatrix(); b->DeepCopy( a ); // delete transformation from matrix, only take orientation b->SetElement(3,3,1); b->SetElement(2,3,0); b->SetElement(1,3,0); b->SetElement(0,3,0); b->SetElement(3,2,0); b->SetElement(3,1,0); b->SetElement(3,0,0); transform->SetMatrix( b ); //---- UNSELECTED POINTS -----// // apply properties to glyph ls->m_UnselectedGlyphSource2D->SetGlyphType(m_IDShapeProperty); if(m_FillShape) ls->m_UnselectedGlyphSource2D->FilledOn(); else ls->m_UnselectedGlyphSource2D->FilledOff(); // apply transform vtkSmartPointer transformFilterU = vtkSmartPointer::New(); transformFilterU->SetInputConnection(ls->m_UnselectedGlyphSource2D->GetOutputPort()); transformFilterU->SetTransform(transform); ls->m_VtkUnselectedPointListPolyData->SetPoints(ls->m_UnselectedPoints); ls->m_VtkUnselectedPointListPolyData->GetPointData()->SetVectors(ls->m_UnselectedScales); // apply transform of current plane to glyphs ls->m_UnselectedGlyph3D->SetSourceConnection(transformFilterU->GetOutputPort()); ls->m_UnselectedGlyph3D->SetInputData(ls->m_VtkUnselectedPointListPolyData); ls->m_UnselectedGlyph3D->SetScaleModeToScaleByVector(); ls->m_UnselectedGlyph3D->SetVectorModeToUseVector(); ls->m_VtkUnselectedPolyDataMapper->SetInputConnection(ls->m_UnselectedGlyph3D->GetOutputPort()); ls->m_UnselectedActor->SetMapper(ls->m_VtkUnselectedPolyDataMapper); ls->m_UnselectedActor->GetProperty()->SetLineWidth(m_PointLineWidth); ls->m_PropAssembly->AddPart(ls->m_UnselectedActor); //---- SELECTED POINTS -----// ls->m_SelectedGlyphSource2D->SetGlyphTypeToDiamond(); ls->m_SelectedGlyphSource2D->CrossOn(); ls->m_SelectedGlyphSource2D->FilledOff(); // apply transform vtkSmartPointer transformFilterS = vtkSmartPointer::New(); transformFilterS->SetInputConnection(ls->m_SelectedGlyphSource2D->GetOutputPort()); transformFilterS->SetTransform(transform); ls->m_VtkSelectedPointListPolyData->SetPoints(ls->m_SelectedPoints); ls->m_VtkSelectedPointListPolyData->GetPointData()->SetVectors(ls->m_SelectedScales); // apply transform of current plane to glyphs ls->m_SelectedGlyph3D->SetSourceConnection(transformFilterS->GetOutputPort()); ls->m_SelectedGlyph3D->SetInputData(ls->m_VtkSelectedPointListPolyData); ls->m_SelectedGlyph3D->SetScaleModeToScaleByVector(); ls->m_SelectedGlyph3D->SetVectorModeToUseVector(); ls->m_VtkSelectedPolyDataMapper->SetInputConnection(ls->m_SelectedGlyph3D->GetOutputPort()); ls->m_SelectedActor->SetMapper(ls->m_VtkSelectedPolyDataMapper); ls->m_SelectedActor->GetProperty()->SetLineWidth(m_PointLineWidth); ls->m_PropAssembly->AddPart(ls->m_SelectedActor); } void mitk::PointSetVtkMapper2D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { const mitk::DataNode* node = GetDataNode(); if( node == NULL ) return; LocalStorage *ls = m_LSH.GetLocalStorage(renderer); // check whether the input data has been changed bool needGenerateData = ls->IsGenerateDataRequired( renderer, this, GetDataNode() ); // toggle visibility bool visible = true; node->GetVisibility(visible, renderer, "visible"); if(!visible) { ls->m_UnselectedActor->VisibilityOff(); ls->m_SelectedActor->VisibilityOff(); ls->m_ContourActor->VisibilityOff(); ls->m_PropAssembly->VisibilityOff(); return; }else{ ls->m_PropAssembly->VisibilityOn(); } node->GetBoolProperty("show contour", m_ShowContour, renderer); node->GetBoolProperty("close contour", m_CloseContour, renderer); node->GetBoolProperty("show points", m_ShowPoints, renderer); node->GetBoolProperty("show distances", m_ShowDistances, renderer); node->GetIntProperty("distance decimal digits", m_DistancesDecimalDigits, renderer); node->GetBoolProperty("show angles", m_ShowAngles, renderer); node->GetBoolProperty("show distant lines", m_ShowDistantLines, renderer); node->GetIntProperty("line width", m_LineWidth, renderer); node->GetIntProperty("point line width", m_PointLineWidth, renderer); node->GetIntProperty("point 2D size", m_Point2DSize, renderer); node->GetBoolProperty("Pointset.2D.fill shape", m_FillShape, renderer); node->GetFloatProperty("Pointset.2D.distance to plane", m_DistanceToPlane, renderer ); mitk::PointSetShapeProperty::Pointer shape = dynamic_cast(this->GetDataNode()->GetProperty( "Pointset.2D.shape", renderer )); if(shape.IsNotNull()) { m_IDShapeProperty = shape->GetPointSetShape(); } //check for color props and use it for rendering of selected/unselected points and contour //due to different params in VTK (double/float) we have to convert float unselectedColor[4]; double selectedColor[4]={1.0f,0.0f,0.0f,1.0f}; //red double contourColor[4]={1.0f,0.0f,0.0f,1.0f}; //red float opacity = 1.0; GetDataNode()->GetOpacity(opacity, renderer); // apply color and opacity if(m_ShowPoints) { ls->m_UnselectedActor->VisibilityOn(); ls->m_SelectedActor->VisibilityOn(); //check if there is a color property GetDataNode()->GetColor(unselectedColor); //get selected color property if (dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor"))->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; // alpha value } else if (dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("selectedcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("selectedcolor"))->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; // alpha value } ls->m_SelectedActor->GetProperty()->SetColor(selectedColor); ls->m_SelectedActor->GetProperty()->SetOpacity(opacity); ls->m_UnselectedActor->GetProperty()->SetColor(unselectedColor[0],unselectedColor[1],unselectedColor[2]); ls->m_UnselectedActor->GetProperty()->SetOpacity(opacity); } else { ls->m_UnselectedActor->VisibilityOff(); ls-> m_SelectedActor->VisibilityOff(); } if (m_ShowContour) { ls->m_ContourActor->VisibilityOn(); //get contour color property if (dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor"))->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } else if (dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("contourcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("contourcolor"))->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } ls->m_ContourActor->GetProperty()->SetColor(contourColor); ls->m_ContourActor->GetProperty()->SetOpacity(opacity); } else { ls->m_ContourActor->VisibilityOff(); } if(needGenerateData) { // create new vtk render objects (e.g. a circle for a point) this->CreateVTKRenderObjects(renderer); } } void mitk::PointSetVtkMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty( "line width", mitk::IntProperty::New(2), renderer, overwrite ); node->AddProperty( "point line width", mitk::IntProperty::New(1), renderer, overwrite ); node->AddProperty( "point 2D size", mitk::IntProperty::New(6), renderer, overwrite ); node->AddProperty( "show contour", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "close contour", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "show points", mitk::BoolProperty::New(true), renderer, overwrite ); node->AddProperty( "show distances", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "distance decimal digits", mitk::IntProperty::New(2), renderer, overwrite ); node->AddProperty( "show angles", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "show distant lines", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "layer", mitk::IntProperty::New(1), renderer, overwrite ); node->AddProperty( "Pointset.2D.fill shape", mitk::BoolProperty::New(false), renderer, overwrite); // fill or do not fill the glyph shape mitk::PointSetShapeProperty::Pointer pointsetShapeProperty = mitk::PointSetShapeProperty::New(); node->AddProperty( "Pointset.2D.shape", pointsetShapeProperty, renderer, overwrite); node->AddProperty( "Pointset.2D.distance to plane", mitk::FloatProperty::New(4.0f), renderer, overwrite ); //show the point at a certain distance above/below the 2D imaging plane. Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Core/Code/Rendering/mitkSurfaceGLMapper2D.cpp b/Core/Code/Rendering/mitkSurfaceGLMapper2D.cpp index 92d78155fa..f63a8b4069 100644 --- a/Core/Code/Rendering/mitkSurfaceGLMapper2D.cpp +++ b/Core/Code/Rendering/mitkSurfaceGLMapper2D.cpp @@ -1,538 +1,538 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include "mitkSurfaceGLMapper2D.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkSurface.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkVtkScalarModeProperty.h" #include "mitkAbstractTransformGeometry.h" #include "mitkLookupTableProperty.h" #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::SurfaceGLMapper2D::SurfaceGLMapper2D() : m_Plane( vtkPlane::New() ), m_Cutter( vtkCutter::New() ), m_LUT( vtkLookupTable::New() ), m_PointLocator( vtkPKdTree::New() ), m_Stripper( vtkStripper::New() ), m_DrawNormals(false), m_FrontNormalLengthInPixels(10.0), m_BackNormalLengthInPixels(10.0) { // default for normals on front side = green m_FrontSideColor[0] = 0.0; m_FrontSideColor[1] = 1.0; m_FrontSideColor[2] = 0.0; m_FrontSideColor[3] = 1.0; // default for normals on back side = red m_BackSideColor[0] = 1.0; m_BackSideColor[1] = 0.0; m_BackSideColor[2] = 0.0; m_BackSideColor[3] = 1.0; // default for line color = yellow m_LineColor[0] = 1.0; m_LineColor[1] = 1.0; m_LineColor[2] = 0.0; m_LineColor[3] = 1.0; m_Cutter->SetCutFunction(m_Plane); m_Cutter->GenerateValues(1,0,1); m_LUT->SetTableRange(0,255); m_LUT->SetNumberOfColors(255); m_LUT->SetRampToLinear(); m_LUT->Build(); } mitk::SurfaceGLMapper2D::~SurfaceGLMapper2D() { m_Plane->Delete(); m_Cutter->Delete(); m_LUT->Delete(); m_PointLocator->Delete(); m_Stripper->Delete(); } const mitk::Surface *mitk::SurfaceGLMapper2D::GetInput(void) { if(m_Surface.IsNotNull()) return m_Surface; return static_cast ( GetDataNode()->GetData() ); } void mitk::SurfaceGLMapper2D::SetDataNode( mitk::DataNode* node ) { Superclass::SetDataNode( node ); bool useCellData; if (dynamic_cast(node->GetProperty("deprecated useCellDataForColouring")) == NULL) useCellData = false; else useCellData = dynamic_cast(node->GetProperty("deprecated useCellDataForColouring"))->GetValue(); if (!useCellData) { // search min/max point scalars over all time steps double dataRange[2] = {0,0}; double range[2]; Surface::Pointer input = const_cast< Surface* >(dynamic_cast( this->GetDataNode()->GetData() )); if(input.IsNull()) return; const TimeGeometry::Pointer inputTimeGeometry = input->GetTimeGeometry(); if(( inputTimeGeometry.IsNull() ) || ( inputTimeGeometry->CountTimeSteps() == 0 ) ) return; for (unsigned int timestep=0; timestepCountTimeSteps(); timestep++) { vtkPolyData * vtkpolydata = input->GetVtkPolyData( timestep ); if((vtkpolydata==NULL) || (vtkpolydata->GetNumberOfPoints() < 1 )) continue; vtkDataArray *vpointscalars = vtkpolydata->GetPointData()->GetScalars(); if (vpointscalars) { vpointscalars->GetRange( range, 0 ); if (dataRange[0]==0 && dataRange[1]==0) { dataRange[0] = range[0]; dataRange[1] = range[1]; } else { if (range[0] < dataRange[0]) dataRange[0] = range[0]; if (range[1] > dataRange[1]) dataRange[1] = range[1]; } } } if (dataRange[1] - dataRange[0] > 0) { m_LUT->SetTableRange( dataRange ); m_LUT->Build(); } } } void mitk::SurfaceGLMapper2D::Paint(mitk::BaseRenderer * renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if(!visible) return; Surface::Pointer input = const_cast(this->GetInput()); if(input.IsNull()) return; // // get the TimeGeometry of the input object // const TimeGeometry* inputTimeGeometry = input->GetTimeGeometry(); if(( inputTimeGeometry == NULL ) || ( inputTimeGeometry->CountTimeSteps() == 0 ) ) return; if (dynamic_cast(this->GetDataNode()->GetProperty("line width")) == NULL) m_LineWidth = 1; else m_LineWidth = dynamic_cast(this->GetDataNode()->GetProperty("line width"))->GetValue(); // // get the world time // ScalarType time =renderer->GetTime(); int timestep=0; - if( time > ScalarTypeNumericTraits::NonpositiveMin() ) + if( time > itk::NumericTraits::NonpositiveMin() ) timestep = inputTimeGeometry->TimePointToTimeStep( time ); // int timestep = this->GetTimestep(); if( inputTimeGeometry->IsValidTimeStep( timestep ) == false ) return; vtkPolyData * vtkpolydata = input->GetVtkPolyData( timestep ); if((vtkpolydata==NULL) || (vtkpolydata->GetNumberOfPoints() < 1 )) return; //apply color and opacity read from the PropertyList this->ApplyAllProperties(renderer); if (m_DrawNormals) { m_PointLocator->SetDataSet( vtkpolydata ); m_PointLocator->BuildLocatorFromPoints( vtkpolydata->GetPoints() ); } if(vtkpolydata!=NULL) { Point3D point; Vector3D normal; //Check if Lookup-Table is already given, else use standard one. double* scalarLimits = m_LUT->GetTableRange(); double scalarsMin = scalarLimits[0], scalarsMax = scalarLimits[1]; vtkLookupTable *lut; LookupTableProperty::Pointer lookupTableProp; this->GetDataNode()->GetProperty(lookupTableProp, "LookupTable", renderer); if (lookupTableProp.IsNotNull() ) { lut = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); if (dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMinimum")) != NULL) scalarsMin = dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMinimum"))->GetValue(); if (dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMaximum")) != NULL) scalarsMax = dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMaximum"))->GetValue(); // check if the scalar range has been changed, e.g. manually, for the data tree node, and rebuild the LUT if necessary. double* oldRange = lut->GetTableRange(); if( oldRange[0] != scalarsMin || oldRange[1] != scalarsMax ) { lut->SetTableRange(scalarsMin, scalarsMax); lut->Build(); } } else { lut = m_LUT; } vtkLinearTransform * vtktransform = GetDataNode()->GetVtkTransform(timestep); PlaneGeometry::ConstPointer worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); assert( worldGeometry.IsNotNull() ); if (worldGeometry.IsNotNull()) { // set up vtkPlane according to worldGeometry point=worldGeometry->GetOrigin(); normal=worldGeometry->GetNormal(); normal.Normalize(); m_Plane->SetTransform((vtkAbstractTransform*)NULL); } else { AbstractTransformGeometry::ConstPointer worldAbstractGeometry = dynamic_cast(renderer->GetCurrentWorldPlaneGeometry()); if(worldAbstractGeometry.IsNotNull()) { AbstractTransformGeometry::ConstPointer surfaceAbstractGeometry = dynamic_cast(input->GetTimeGeometry()->GetGeometryForTimeStep(0).GetPointer()); if(surfaceAbstractGeometry.IsNotNull()) //@todo substitude by operator== after implementation, see bug id 28 { PaintCells(renderer, vtkpolydata, worldGeometry, renderer->GetDisplayGeometry(), vtktransform, lut); return; } else { //@FIXME: does not work correctly. Does m_Plane->SetTransform really transforms a "flat plane" into a "curved plane"? return; // set up vtkPlane according to worldGeometry point=const_cast(worldAbstractGeometry->GetParametricBoundingBox())->GetMinimum(); FillVector3D(normal, 0, 0, 1); m_Plane->SetTransform(worldAbstractGeometry->GetVtkAbstractTransform()->GetInverse()); } } else return; } double vp[3], vnormal[3]; vnl2vtk(point.GetVnlVector(), vp); vnl2vtk(normal.GetVnlVector(), vnormal); //normally, we would need to transform the surface and cut the transformed surface with the cutter. //This might be quite slow. Thus, the idea is, to perform an inverse transform of the plane instead. //@todo It probably does not work for scaling operations yet:scaling operations have to be //dealed with after the cut is performed by scaling the contour. vtkLinearTransform * inversetransform = vtktransform->GetLinearInverse(); inversetransform->TransformPoint(vp, vp); inversetransform->TransformNormalAtPoint(vp, vnormal, vnormal); m_Plane->SetOrigin(vp); m_Plane->SetNormal(vnormal); //set data into cutter m_Cutter->SetInputData(vtkpolydata); m_Cutter->Update(); // m_Cutter->GenerateCutScalarsOff(); // m_Cutter->SetSortByToSortByCell(); if (m_DrawNormals) { m_Stripper->SetInputData( m_Cutter->GetOutput() ); // calculate the cut m_Stripper->Update(); PaintCells(renderer, m_Stripper->GetOutput(), worldGeometry, renderer->GetDisplayGeometry(), vtktransform, lut, vtkpolydata); } else { PaintCells(renderer, m_Cutter->GetOutput(), worldGeometry, renderer->GetDisplayGeometry(), vtktransform, lut, vtkpolydata); } } } void mitk::SurfaceGLMapper2D::PaintCells(mitk::BaseRenderer* renderer, vtkPolyData* contour, const PlaneGeometry* worldGeometry, const DisplayGeometry* displayGeometry, vtkLinearTransform * vtktransform, vtkLookupTable *lut, vtkPolyData* original3DObject) { // deprecated settings bool usePointData = false; bool useCellData = false; this->GetDataNode()->GetBoolProperty("deprecated useCellDataForColouring", useCellData); bool scalarVisibility = false; this->GetDataNode()->GetBoolProperty("scalar visibility", scalarVisibility); if(scalarVisibility) { VtkScalarModeProperty* scalarMode; if(this->GetDataNode()->GetProperty(scalarMode, "scalar mode", renderer)) { if( (scalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_POINT_DATA) || (scalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_DEFAULT) ) { usePointData = true; } if(scalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_CELL_DATA) { useCellData = true; } } else { usePointData = true; } } vtkPoints *vpoints = contour->GetPoints(); vtkDataArray *vpointscalars = contour->GetPointData()->GetScalars(); vtkCellArray *vlines = contour->GetLines(); vtkDataArray* vcellscalars = contour->GetCellData()->GetScalars(); Point3D p; Point2D p2d, last; int i, j; int numberOfLines = vlines->GetNumberOfCells(); glLineWidth( m_LineWidth ); glBegin (GL_LINES); glColor4fv(m_LineColor); double distanceSinceLastNormal(0.0); vlines->InitTraversal(); for(i=0;iGetNextCell(cellSize, cell); vpoints->GetPoint(cell[0], vp); //take transformation via vtktransform into account vtktransform->TransformPoint(vp, vp); vtk2itk(vp, p); //convert 3D point (in mm) to 2D point on slice (also in mm) worldGeometry->Map(p, p2d); //convert point (until now mm and in world coordinates) to display coordinates (units ) displayGeometry->WorldToDisplay(p2d, p2d); last=p2d; for(j=1; jGetPoint(cell[j], vp); Point3D originalPoint; vtk2itk(vp, originalPoint); //take transformation via vtktransform into account vtktransform->TransformPoint(vp, vp); vtk2itk(vp, p); //convert 3D point (in mm) to 2D point on slice (also in mm) worldGeometry->Map(p, p2d); //convert point (until now mm and in world coordinates) to display coordinates (units ) displayGeometry->WorldToDisplay(p2d, p2d); double color[3]; if (useCellData && vcellscalars != NULL ) { // color each cell according to cell data lut->GetColor( vcellscalars->GetComponent(i,0),color); glColor3f(color[0],color[1],color[2]); glVertex2f(last[0], last[1]); glVertex2f(p2d[0], p2d[1]); } else if (usePointData && vpointscalars != NULL ) { lut->GetColor( vpointscalars->GetComponent(cell[j-1],0),color); glColor3f(color[0],color[1],color[2]); glVertex2f(last[0], last[1]); lut->GetColor( vpointscalars->GetComponent(cell[j],0),color); glColor3f(color[0],color[1],color[2]); glVertex2f(p2d[0], p2d[1]); } else { glVertex2f(last[0], last[1]); glVertex2f(p2d[0], p2d[1]); // draw normals ? if (m_DrawNormals && original3DObject) { distanceSinceLastNormal += sqrt((p2d[0]-last[0])*(p2d[0]-last[0]) + (p2d[1]-last[1])*(p2d[1]-last[1])); if (distanceSinceLastNormal >= 5.0) { distanceSinceLastNormal = 0.0; vtkPointData* pointData = original3DObject->GetPointData(); if (!pointData) break; vtkDataArray* normalsArray = pointData->GetNormals(); if (!normalsArray) break; // find 3D point closest to the currently drawn point double distance(0.0); vtkIdType closestPointId = m_PointLocator->FindClosestPoint(originalPoint[0], originalPoint[1], originalPoint[2], distance); if (closestPointId >= 0) { // find normal of 3D object at this 3D point double* normal = normalsArray->GetTuple3(closestPointId); double transformedNormal[3]; vtktransform->TransformNormal(normal, transformedNormal); Vector3D normalITK; vtk2itk(transformedNormal, normalITK); normalITK.Normalize(); // calculate a point (point from the cut 3D object) + (normal vector of closest point) Point3D tip3D = p + normalITK; // map this point into our 2D coordinate system Point2D tip2D; worldGeometry->Map(tip3D, tip2D); displayGeometry->WorldToDisplay(tip2D, tip2D); // calculate 2D vector from point to point+normal, normalize it to standard length Vector2D tipVectorGLFront = tip2D - p2d; tipVectorGLFront.Normalize(); tipVectorGLFront *= m_FrontNormalLengthInPixels; Vector2D tipVectorGLBack = p2d - tip2D; tipVectorGLBack.Normalize(); tipVectorGLBack *= m_BackNormalLengthInPixels; Point2D tipPoint2D = p2d + tipVectorGLFront; Point2D backTipPoint2D = p2d + tipVectorGLBack; // draw normalized mapped normal vector glColor4f(m_BackSideColor[0], m_BackSideColor[1], m_BackSideColor[2], m_BackSideColor[3]); // red backside glVertex2f(p2d[0], p2d[1]); glVertex2f(tipPoint2D[0], tipPoint2D[1]); glColor4f(m_FrontSideColor[0], m_FrontSideColor[1], m_FrontSideColor[2], m_FrontSideColor[3]); // green backside glVertex2f(p2d[0], p2d[1]); glVertex2f(backTipPoint2D[0], backTipPoint2D[1]); glColor4fv(m_LineColor); // back to line color } } } } last=p2d; } } glEnd(); glLineWidth(1.0); } void mitk::SurfaceGLMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty( "line width", IntProperty::New(2), renderer, overwrite ); node->AddProperty( "scalar mode", VtkScalarModeProperty::New(), renderer, overwrite ); node->AddProperty( "draw normals 2D", BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "invert normals", BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "front color", ColorProperty::New(0.0, 1.0, 0.0), renderer, overwrite ); node->AddProperty( "back color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite ); node->AddProperty( "front normal lenth (px)", FloatProperty::New(10.0), renderer, overwrite ); node->AddProperty( "back normal lenth (px)", FloatProperty::New(10.0), renderer, overwrite ); node->AddProperty( "layer", mitk::IntProperty::New(100), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } void mitk::SurfaceGLMapper2D::ApplyAllProperties(mitk::BaseRenderer* renderer) { ApplyColorAndOpacityProperties(renderer); DataNode * node = GetDataNode(); if(node == NULL) { return; } node->GetBoolProperty("draw normals 2D", m_DrawNormals, renderer); // check for color and opacity properties, use it for rendering if they exists node->GetColor(m_LineColor, renderer, "color"); node->GetOpacity(m_LineColor[3], renderer, "opacity"); bool invertNormals(false); node->GetBoolProperty("invert normals", invertNormals, renderer); if (!invertNormals) { node->GetColor(m_FrontSideColor, renderer, "front color"); node->GetOpacity(m_FrontSideColor[3], renderer, "opacity"); node->GetColor(m_BackSideColor, renderer, "back color"); node->GetOpacity(m_BackSideColor[3], renderer, "opacity"); node->GetFloatProperty( "front normal lenth (px)", m_FrontNormalLengthInPixels, renderer ); node->GetFloatProperty( "back normal lenth (px)", m_BackNormalLengthInPixels, renderer ); } else { node->GetColor(m_FrontSideColor, renderer, "back color"); node->GetOpacity(m_FrontSideColor[3], renderer, "opacity"); node->GetColor(m_BackSideColor, renderer, "front color"); node->GetOpacity(m_BackSideColor[3], renderer, "opacity"); node->GetFloatProperty( "back normal lenth (px)", m_FrontNormalLengthInPixels, renderer ); node->GetFloatProperty( "front normal lenth (px)", m_BackNormalLengthInPixels, renderer ); } } diff --git a/Core/Code/Testing/files.cmake b/Core/Code/Testing/files.cmake index 929ebe6a6c..92008c4785 100644 --- a/Core/Code/Testing/files.cmake +++ b/Core/Code/Testing/files.cmake @@ -1,203 +1,208 @@ # tests with no extra command line parameter set(MODULE_TESTS # IMPORTANT: If you plan to deactivate / comment out a test please write a bug number to the commented out line of code. # # Example: #mitkMyTest #this test is commented out because of bug 12345 # # It is important that the bug is open and that the test will be activated again before the bug is closed. This assures that # no test is forgotten after it was commented out. If there is no bug for your current problem, please add a new one and # mark it as critical. ################## DISABLED TESTS ################################################# #mitkAbstractTransformGeometryTest.cpp #seems as tested class mitkExternAbstractTransformGeometry doesnt exist any more #mitkStateMachineContainerTest.cpp #rewrite test, indirect since no longer exported Bug 14529 #mitkRegistrationBaseTest.cpp #tested class mitkRegistrationBase doesn't exist any more #mitkSegmentationInterpolationTest.cpp #file doesn't exist! #mitkPipelineSmartPointerCorrectnessTest.cpp #file doesn't exist! #mitkITKThreadingTest.cpp #test outdated because itk::Semaphore was removed from ITK #mitkAbstractTransformPlaneGeometryTest.cpp #mitkVtkAbstractTransformPlaneGeometry doesn't exist any more #mitkTestUtilSharedLibrary.cpp #Linker problem with this test... #mitkTextOverlay2DSymbolsRenderingTest.cpp #Implementation of the tested feature is not finished yet. Ask Christoph or see bug 15104 for details. ################# RUNNING TESTS ################################################### mitkAccessByItkTest.cpp mitkCoreObjectFactoryTest.cpp mitkMaterialTest.cpp mitkActionTest.cpp mitkDispatcherTest.cpp mitkEnumerationPropertyTest.cpp mitkEventTest.cpp mitkFocusManagerTest.cpp mitkGenericPropertyTest.cpp mitkGeometry3DTest.cpp mitkGeometry3DEqualTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkGlobalInteractionTest.cpp mitkImageEqualTest.cpp mitkImageDataItemTest.cpp mitkImageGeneratorTest.cpp mitkIOUtilTest.cpp mitkBaseDataTest.cpp mitkImportItkImageTest.cpp mitkGrabItkImageMemoryTest.cpp mitkInstantiateAccessFunctionTest.cpp mitkInteractorTest.cpp mitkLevelWindowTest.cpp mitkMessageTest.cpp mitkPixelTypeTest.cpp mitkPlaneGeometryTest.cpp mitkPointSetTest.cpp mitkPointSetEqualTest.cpp mitkPointSetFileIOTest.cpp mitkPointSetOnEmptyTest.cpp mitkPointSetWriterTest.cpp mitkPointSetReaderTest.cpp mitkPointSetInteractorTest.cpp mitkPointSetPointOperationsTest.cpp mitkPropertyTest.cpp mitkPropertyListTest.cpp mitkSlicedGeometry3DTest.cpp mitkSliceNavigationControllerTest.cpp mitkStateMachineTest.cpp mitkStateTest.cpp mitkSurfaceTest.cpp mitkSurfaceEqualTest.cpp mitkSurfaceToSurfaceFilterTest.cpp mitkTimeGeometryTest.cpp mitkTransitionTest.cpp mitkUndoControllerTest.cpp mitkVtkWidgetRenderingTest.cpp mitkVerboseLimitedLinearUndoTest.cpp mitkWeakPointerTest.cpp mitkTransferFunctionTest.cpp mitkStepperTest.cpp mitkRenderingManagerTest.cpp vtkMitkThickSlicesFilterTest.cpp mitkNodePredicateSourceTest.cpp mitkVectorTest.cpp mitkClippedSurfaceBoundsCalculatorTest.cpp mitkExceptionTest.cpp mitkExtractSliceFilterTest.cpp mitkLogTest.cpp mitkImageDimensionConverterTest.cpp mitkLoggingAdapterTest.cpp mitkUIDGeneratorTest.cpp mitkShaderRepositoryTest.cpp mitkPlanePositionManagerTest.cpp mitkAffineTransformBaseTest.cpp mitkPropertyAliasesTest.cpp mitkPropertyDescriptionsTest.cpp mitkPropertyExtensionsTest.cpp mitkPropertyFiltersTest.cpp mitkTinyXMLTest.cpp mitkRawImageFileReaderTest.cpp mitkInteractionEventTest.cpp mitkLookupTableTest.cpp mitkSTLFileReaderTest.cpp + mitkPointTypeConversionTest.cpp + mitkVectorTypeConversionTest.cpp + mitkMatrixTypeConversionTest.cpp + mitkArrayTypeConversionTest.cpp mitkSurfaceToImageFilterTest.cpp mitkBaseGeometryTest.cpp mitkImageToSurfaceFilterTest.cpp mitkEqualTest.cpp mitkLineTest.cpp ) if(MITK_ENABLE_RENDERING_TESTING) #since mitkInteractionTestHelper is currently creating a vtkRenderWindow set(MODULE_TESTS ${MODULE_TESTS} mitkPointSetDataInteractorTest.cpp ) endif() + # test with image filename as an extra command line parameter set(MODULE_IMAGE_TESTS mitkImageTimeSelectorTest.cpp #only runs on images mitkImageAccessorTest.cpp #only runs on images mitkDataNodeFactoryTest.cpp #runs on all types of data ) set(MODULE_SURFACE_TESTS mitkSurfaceVtkWriterTest.cpp #only runs on surfaces mitkDataNodeFactoryTest.cpp #runs on all types of data ) # list of images for which the tests are run set(MODULE_TESTIMAGES US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png ) set(MODULE_TESTSURFACES binary.stl ball.stl ) set(MODULE_CUSTOM_TESTS mitkDataStorageTest.cpp mitkDataNodeTest.cpp mitkDicomSeriesReaderTest.cpp mitkDICOMLocaleTest.cpp mitkEventMapperTest.cpp mitkEventConfigTest.cpp mitkNodeDependentPointSetInteractorTest.cpp mitkStateMachineFactoryTest.cpp mitkPointSetLocaleTest.cpp mitkImageTest.cpp mitkImageWriterTest.cpp mitkImageVtkMapper2DTest.cpp mitkImageVtkMapper2DLevelWindowTest.cpp mitkImageVtkMapper2DOpacityTest.cpp mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp mitkImageVtkMapper2DColorTest.cpp mitkImageVtkMapper2DSwivelTest.cpp mitkImageVtkMapper2DTransferFunctionTest.cpp mitkImageVtkMapper2DLookupTableTest.cpp mitkSurfaceVtkMapper3DTest mitkSurfaceVtkMapper3DTexturedSphereTest.cpp mitkSurfaceGLMapper2DColorTest.cpp mitkSurfaceGLMapper2DOpacityTest.cpp mitkVolumeCalculatorTest.cpp mitkLevelWindowManagerTest.cpp mitkPointSetVtkMapper2DTest.cpp mitkPointSetVtkMapper2DImageTest.cpp mitkPointSetVtkMapper2DGlyphTypeTest.cpp mitkPointSetVtkMapper2DTransformedPointsTest.cpp mitkLabelOverlay3DRendering2DTest.cpp mitkLabelOverlay3DRendering3DTest.cpp mitkTextOverlay2DRenderingTest.cpp mitkTextOverlay2DLayouterRenderingTest.cpp mitkTextOverlay3DRendering2DTest.cpp mitkTextOverlay3DRendering3DTest.cpp mitkTextOverlay3DColorRenderingTest.cpp mitkVTKRenderWindowSizeTest.cpp mitkMultiComponentImageDataComparisonFilterTest.cpp mitkImageToItkTest.cpp mitkImageSliceSelectorTest.cpp mitkSurfaceDepthPeelingTest.cpp mitkSurfaceDepthSortingTest.cpp ) set(MODULE_RESOURCE_FILES Interactions/AddAndRemovePoints.xml Interactions/globalConfig.xml Interactions/StatemachineTest.xml Interactions/StatemachineConfigTest.xml ) # Create an artificial module initializing class for # the usServiceListenerTest.cpp usFunctionGenerateExecutableInit(testdriver_init_file IDENTIFIER ${MODULE_NAME}TestDriver ) # Embed the resources set(testdriver_resources ) usFunctionEmbedResources(testdriver_resources EXECUTABLE_NAME ${MODULE_NAME}TestDriver ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Resources FILES ${MODULE_RESOURCE_FILES} ) set(TEST_CPP_FILES ${testdriver_init_file} ${testdriver_resources}) diff --git a/Core/Code/Testing/mitkAffineTransformBaseTest.cpp b/Core/Code/Testing/mitkAffineTransformBaseTest.cpp index 9f257dd1bf..2766b1dc67 100644 --- a/Core/Code/Testing/mitkAffineTransformBaseTest.cpp +++ b/Core/Code/Testing/mitkAffineTransformBaseTest.cpp @@ -1,185 +1,185 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "itkScalableAffineTransform.h" #include "mitkMatrixConvert.h" #include "mitkTestingMacros.h" using namespace mitk; static Vector3D offset; static Matrix3D rotation; static Point3D originalPoint; static double originalPointDouble[4]; static vtkMatrix4x4* homogenMatrix; static vtkMatrix4x4* expectedHomogenousMatrix; static const double expectedPointAfterTransformation[] = {2, 4, 4, 1}; static void Setup() { originalPoint[0] = 1.0; originalPoint[1] = 0.0; originalPoint[2] = 0.0; for (int i = 0; i < 3; i++) originalPointDouble[i] = originalPoint[i]; // same point as the Point3D version originalPointDouble[3] = 1; // homogenous extension offset[0] = 2.0; offset[1] = 3.0; offset[2] = 4.0; - // 90° rotation + // 90� rotation rotation.Fill(0); rotation[0][1] = -1; rotation[1][0] = 1; // prepare a Matrix which shall be set later to a specific // homogen matrix by TransferItkTransformToVtkMatrix // this makes sure, that the initialization to the identity does not // overshadow any bugs in TransferItkTransformToVtkMatrix // (it actually did that by "helping out" TransferItkTransformToVtkMatrix // by setting the (3,3) element to 1). homogenMatrix = vtkMatrix4x4::New(); for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) homogenMatrix->SetElement(i,j, i+j*j); // just some not trivial value // set the expected homogenous matrix result expectedHomogenousMatrix = vtkMatrix4x4::New(); expectedHomogenousMatrix->Zero(); expectedHomogenousMatrix->SetElement(0,1,-1); expectedHomogenousMatrix->SetElement(1,0,1); expectedHomogenousMatrix->SetElement(0,3,2); expectedHomogenousMatrix->SetElement(1,3,3); expectedHomogenousMatrix->SetElement(2,3,4); expectedHomogenousMatrix->SetElement(3,3,1); } /** * This first test basically assures that we understand the usage of AffineTransform3D correct. * Meaning that the rotation is set by SetMatrix and the translation is set by SetOffset */ static void testIfPointIsTransformedAsExpected(void) { Setup(); /** construct the transformation */ AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetOffset(offset); transform->SetMatrix(rotation); TransferItkTransformToVtkMatrix(transform.GetPointer(), homogenMatrix); /** Let a point be transformed by the AffineTransform3D */ Point3D pointTransformedByAffineTransform3D = transform->TransformPoint(originalPoint); /** assert that the transformation was successful */ bool pointCorrect = true; for (int i = 0; i < 3; i++) // only first three since no homogenous coordinates pointCorrect &= Equal(pointTransformedByAffineTransform3D[i], expectedPointAfterTransformation[i]); MITK_TEST_CONDITION(pointCorrect, "Point has been correctly transformed by AffineTranform3D") } /** * This test ensures that the function TransferItkTransformToVtkMatrix translates the AffineTransform3D * correctly to a VtkMatrix4x4 */ static void testTransferItkTransformToVtkMatrix(void) { Setup(); AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetOffset(offset); transform->SetMatrix(rotation); TransferItkTransformToVtkMatrix(transform.GetPointer(), homogenMatrix); bool allElementsEqual = true; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) allElementsEqual &= Equal(homogenMatrix->GetElement(i,j), expectedHomogenousMatrix->GetElement(i,j)); MITK_TEST_CONDITION(allElementsEqual, "Homogenous Matrix is set as expected") } /** * This test basically is just a sanity check and should be PASSED exactly when both * testTransferItkTransformToVtkMatrix and testIfPointIsTransformedAsExpected are PASSED. * Tests if we get the same * result by using the AffineTransform3D to transform a point or the vtkMatrix4x4 which we got by applying * the TransferItkTransformToVtkMatrix function. This test e.g. ensures we made no mistake in our * reference results. * */ static void testIfBothTransformationsProduceSameResults(void) { Setup(); /** * create both a AffineTransform3D * and let this AffineTransform describe also a homogenous 4x4 Matrix vtkMatrix * by using our transfer method */ AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetOffset(offset); transform->SetMatrix(rotation); TransferItkTransformToVtkMatrix(transform.GetPointer(), homogenMatrix); /** Let a point be transformed by the AffineTransform3D and by homogenMatrix */ Point3D pointTransformedByAffineTransform3D = transform->TransformPoint(originalPoint); double* pointTransformedByHomogenous= homogenMatrix->MultiplyDoublePoint(originalPointDouble); /* check if results match */ bool pointsMatch = true; for (int i = 0; i < 3; i++) // only first three since no homogenous coordinates pointsMatch &= Equal(pointTransformedByAffineTransform3D[i], pointTransformedByHomogenous[i]); bool homogenousComponentCorrect = Equal(1, pointTransformedByHomogenous[3]); MITK_TEST_CONDITION(pointsMatch && homogenousComponentCorrect, "Point transformed by AffineTransform and homogenous coordinates match") } /** * This test shall ensure and document the basic functionality of the * itk AffineTransformation functionality and test some basic transformation functionalities provided by mitk. */ int mitkAffineTransformBaseTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN("AffineTransformationBaseTest"); testIfPointIsTransformedAsExpected(); testTransferItkTransformToVtkMatrix(); testIfBothTransformationsProduceSameResults(); MITK_TEST_END(); } diff --git a/Core/Code/Testing/mitkArrayTypeConversionTest.cpp b/Core/Code/Testing/mitkArrayTypeConversionTest.cpp new file mode 100644 index 0000000000..8955142ebc --- /dev/null +++ b/Core/Code/Testing/mitkArrayTypeConversionTest.cpp @@ -0,0 +1,98 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkTestingMacros.h" +#include "mitkTestFixture.h" + +#include "mitkNumericConstants.h" +#include "mitkArray.h" + + +class mitkArrayTypeConversionTestSuite : public mitk::TestFixture +{ + + CPPUNIT_TEST_SUITE(mitkArrayTypeConversionTestSuite); + + MITK_TEST(EqualArray_ReturnsTrue); + MITK_TEST(EqualArray_ReturnsFalse); + + MITK_TEST(FillVector3D_CorrectlyFilled); + MITK_TEST(FillVector4D_CorrectlyFilled); + + CPPUNIT_TEST_SUITE_END(); + +private: + + mitk::ScalarType a[3]; + mitk::ScalarType b[3]; + +public: + + void setUp(void) + { + b[0] = a[0] = 1.0; + b[1] = a[1] = 2.12; + b[2] = a[2] = 3.13; + } + + void tearDown(void) + { + + } + + void EqualArray_ReturnsTrue(void) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE("test if EqualArray method returns true for two equal arrays.", + true, mitk::EqualArray(a, b, 3)); + } + + void EqualArray_ReturnsFalse(void) + { + b[2] += mitk::eps; + + CPPUNIT_ASSERT_EQUAL_MESSAGE("test if EqualArray method returns false for two non-equal arrays.", + false, mitk::EqualArray(a, b, 3)); + } + + void FillVector3D_CorrectlyFilled(void) + { + mitk::ScalarType c[3]; + + mitk::FillVector3D(c, a[0], a[1], a[2]); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("test if FillVector3D correctly fills array types.", + true, mitk::EqualArray(a, c, 3)); + } + + void FillVector4D_CorrectlyFilled(void) + { + mitk::ScalarType e[4]; + mitk::ScalarType f[4]; + + e[0] = 1.0; + e[1] = 2.12; + e[2] = 3.13; + e[3] = 4.14; + + mitk::FillVector4D(f, e[0], e[1], e[2], e[3]); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("test if FillVector4D correctly fills array types.", + true, mitk::EqualArray(e, f, 4)); + } + +}; + +MITK_TEST_SUITE_REGISTRATION(mitkArrayTypeConversion) diff --git a/Core/Code/Testing/mitkBaseGeometryTest.cpp b/Core/Code/Testing/mitkBaseGeometryTest.cpp index 08e292d380..74a324ab9a 100644 --- a/Core/Code/Testing/mitkBaseGeometryTest.cpp +++ b/Core/Code/Testing/mitkBaseGeometryTest.cpp @@ -1,1230 +1,1230 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include #include #include #include #include #include "mitkOperationActor.h" #include #include "mitkVector.h" #include #include #include "itkScalableAffineTransform.h" #include #include #include #include "mitkRotationOperation.h" #include "mitkInteractionConst.h" #include #include class vtkMatrix4x4; class vtkMatrixToLinearTransform; class vtkLinearTransform; typedef itk::BoundingBox BoundingBox; typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::BoundsArrayType BoundsArrayType; typedef BoundingBoxType::Pointer BoundingBoxPointer; // Dummy instance of abstract base class class DummyTestClass : public mitk::BaseGeometry { public: DummyTestClass(){}; DummyTestClass(const DummyTestClass& other) : BaseGeometry(other){}; ~DummyTestClass(){}; mitkClassMacro(DummyTestClass, mitk::BaseGeometry); itkNewMacro(Self); mitkNewMacro1Param(Self, const Self&); itk::LightObject::Pointer InternalClone() const { Self::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } virtual void PrintSelf(std::ostream& os, itk::Indent indent) const{}; }; class mitkBaseGeometryTestSuite : public mitk::TestFixture { // List of Tests CPPUNIT_TEST_SUITE(mitkBaseGeometryTestSuite); //Constructor MITK_TEST(TestConstructors); MITK_TEST(TestInitialize); //Set MITK_TEST(TestSetOrigin); MITK_TEST(TestSetBounds); MITK_TEST(TestSetFloatBounds); MITK_TEST(TestSetFloatBoundsDouble); MITK_TEST(TestSetFrameOfReferenceID); MITK_TEST(TestSetIndexToWorldTransform); MITK_TEST(TestSetSpacing); MITK_TEST(TestTransferItkToVtkTransform); MITK_TEST(TestSetIndexToWorldTransformByVtkMatrix); MITK_TEST(TestSetIdentity); MITK_TEST(TestSetImageGeometry); //Equal MITK_TEST(Equal_CloneAndOriginal_ReturnsTrue); MITK_TEST(Equal_DifferentOrigin_ReturnsFalse); MITK_TEST(Equal_DifferentIndexToWorldTransform_ReturnsFalse); MITK_TEST(Equal_DifferentSpacing_ReturnsFalse); MITK_TEST(Equal_InputIsNull_ReturnsFalse); MITK_TEST(Equal_DifferentBoundingBox_ReturnsFalse); //other Functions MITK_TEST(TestComposeTransform); MITK_TEST(TestComposeVtkMatrix); MITK_TEST(TestTranslate); MITK_TEST(TestIndexToWorld); MITK_TEST(TestExecuteOperation); MITK_TEST(TestCalculateBoundingBoxRelToTransform); //MITK_TEST(TestSetTimeBounds); MITK_TEST(TestIs2DConvertable); MITK_TEST(TestGetCornerPoint); MITK_TEST(TestExtentInMM); MITK_TEST(TestGetAxisVector); MITK_TEST(TestGetCenter); MITK_TEST(TestGetDiagonalLength); MITK_TEST(TestGetExtent); MITK_TEST(TestIsInside); MITK_TEST(TestGetMatrixColumn); CPPUNIT_TEST_SUITE_END(); // Used Variables private: mitk::Point3D aPoint; float aFloatSpacing[3]; mitk::Vector3D aSpacing; mitk::AffineTransform3D::Pointer aTransform; BoundingBoxPointer aBoundingBox; mitk::AffineTransform3D::MatrixType aMatrix; mitk::Point3D anotherPoint; mitk::Vector3D anotherSpacing; BoundingBoxPointer anotherBoundingBox; BoundingBoxPointer aThirdBoundingBox; mitk::AffineTransform3D::Pointer anotherTransform; mitk::AffineTransform3D::Pointer aThirdTransform; mitk::AffineTransform3D::MatrixType anotherMatrix; mitk::AffineTransform3D::MatrixType aThirdMatrix; DummyTestClass::Pointer aDummyGeometry; DummyTestClass::Pointer anotherDummyGeometry; public: // Set up for variables void setUp() { mitk::FillVector3D(aFloatSpacing, 1,1,1); mitk::FillVector3D(aSpacing, 1,1,1); mitk::FillVector3D(aPoint, 0,0,0); //Transform aTransform = mitk::AffineTransform3D::New(); aTransform->SetIdentity(); aMatrix.SetIdentity(); anotherTransform = mitk::AffineTransform3D::New(); anotherMatrix.SetIdentity(); anotherMatrix(1,1) = 2; anotherTransform->SetMatrix( anotherMatrix ); aThirdTransform = mitk::AffineTransform3D::New(); aThirdMatrix.SetIdentity(); aThirdMatrix(1,1) = 7; aThirdTransform->SetMatrix( aThirdMatrix ); //Bounding Box float bounds[6] = {0,1,0,1,0,1}; mitk::BoundingBox::BoundsArrayType b; const float *input = bounds; int j=0; for(mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); j < 6 ;++j) *it++ = (mitk::ScalarType)*input++; aBoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for(pointid=0; pointid<2;++pointid) { unsigned int i; for(i=0; i<3; ++i) { p[i] = bounds[2*i+pointid]; } pointscontainer->InsertElement(pointid, p); } aBoundingBox->SetPoints(pointscontainer); aBoundingBox->ComputeBoundingBox(); anotherBoundingBox = BoundingBoxType::New(); p[0]=11; p[1]=12; p[2]=13; pointscontainer->InsertElement(1, p); anotherBoundingBox->SetPoints(pointscontainer); anotherBoundingBox->ComputeBoundingBox(); aThirdBoundingBox = BoundingBoxType::New(); p[0]=22; p[1]=23; p[2]=24; pointscontainer->InsertElement(1, p); aThirdBoundingBox->SetPoints(pointscontainer); aThirdBoundingBox->ComputeBoundingBox(); mitk::FillVector3D(anotherPoint, 2,3,4); mitk::FillVector3D(anotherSpacing, 5,6.5,7); aDummyGeometry = DummyTestClass::New(); aDummyGeometry->Initialize(); anotherDummyGeometry = aDummyGeometry->Clone(); } void tearDown() { aDummyGeometry = NULL; anotherDummyGeometry = NULL; } // Test functions void TestSetOrigin() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(anotherPoint==dummy->GetOrigin()); //undo changes, new and changed object need to be the same! dummy->SetOrigin(aPoint); DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestSetImageGeometry() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetImageGeometry(true); CPPUNIT_ASSERT(dummy->GetImageGeometry()); //undo changes, new and changed object need to be the same! dummy->SetImageGeometry(false); CPPUNIT_ASSERT(dummy->GetImageGeometry()==false); DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestSetFloatBounds(){ float bounds[6] = {0,11,0,12,0,13}; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFloatBounds(bounds); CPPUNIT_ASSERT(mitk::Equal( dummy->GetBoundingBox(), anotherBoundingBox, mitk::eps, true)); //Wrong bounds, test needs to fail bounds[1]=7; dummy->SetFloatBounds(bounds); CPPUNIT_ASSERT((mitk::Equal( dummy->GetBoundingBox(), anotherBoundingBox, mitk::eps, false))==false); //undo changes, new and changed object need to be the same! float originalBounds[6] = {0,1,0,1,0,1}; dummy->SetFloatBounds(originalBounds); DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestSetBounds(){ DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetBounds(anotherBoundingBox->GetBounds()); CPPUNIT_ASSERT(mitk::Equal( dummy->GetBoundingBox(), anotherBoundingBox, mitk::eps, true)); //Test needs to fail now dummy->SetBounds(aThirdBoundingBox->GetBounds()); CPPUNIT_ASSERT(mitk::Equal( dummy->GetBoundingBox(), anotherBoundingBox, mitk::eps, false)==false); //undo changes, new and changed object need to be the same! dummy->SetBounds(aBoundingBox->GetBounds()); DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestSetFloatBoundsDouble(){ double bounds[6] = {0,11,0,12,0,13}; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFloatBounds(bounds); CPPUNIT_ASSERT(mitk::Equal( dummy->GetBoundingBox(), anotherBoundingBox, mitk::eps, true)); //Test needs to fail now bounds[3]=7; dummy->SetFloatBounds(bounds); CPPUNIT_ASSERT(mitk::Equal( dummy->GetBoundingBox(), anotherBoundingBox, mitk::eps, false)==false); //undo changes, new and changed object need to be the same! double originalBounds[6] = {0,1,0,1,0,1}; dummy->SetFloatBounds(originalBounds); DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestSetFrameOfReferenceID() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFrameOfReferenceID(5); CPPUNIT_ASSERT(dummy->GetFrameOfReferenceID()==5); //undo changes, new and changed object need to be the same! dummy->SetFrameOfReferenceID(0); DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestSetIndexToWorldTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); CPPUNIT_ASSERT(mitk::Equal(anotherTransform,dummy->GetIndexToWorldTransform(),mitk::eps,true)); //Test needs to fail now dummy->SetIndexToWorldTransform(aThirdTransform); CPPUNIT_ASSERT(mitk::Equal(anotherTransform,dummy->GetIndexToWorldTransform(),mitk::eps,false)==false); //undo changes, new and changed object need to be the same! dummy->SetIndexToWorldTransform(aTransform); DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestSetIndexToWorldTransformByVtkMatrix() { vtkMatrix4x4* vtkmatrix; vtkmatrix = vtkMatrix4x4::New(); vtkmatrix->Identity(); vtkmatrix->SetElement(1,1,2); DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); CPPUNIT_ASSERT(mitk::Equal(anotherTransform,dummy->GetIndexToWorldTransform(),mitk::eps,true)); //test needs to fail now vtkmatrix->SetElement(1,1,7); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); CPPUNIT_ASSERT(mitk::Equal(anotherTransform,dummy->GetIndexToWorldTransform(),mitk::eps,false)==false); //undo changes, new and changed object need to be the same! vtkmatrix->SetElement(1,1,1); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestSetIdentity() { DummyTestClass::Pointer dummy = DummyTestClass::New(); //Change IndextoWorldTransform and Origin dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetOrigin(anotherPoint); //Set Identity should reset ITWT and Origin dummy->SetIdentity(); CPPUNIT_ASSERT(mitk::Equal(aTransform,dummy->GetIndexToWorldTransform(),mitk::eps,true)); CPPUNIT_ASSERT(aPoint==dummy->GetOrigin()); CPPUNIT_ASSERT(aSpacing==dummy->GetSpacing()); //new and changed object need to be the same! DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestSetSpacing() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetSpacing(anotherSpacing); CPPUNIT_ASSERT(anotherSpacing==dummy->GetSpacing()); //undo changes, new and changed object need to be the same! dummy->SetSpacing(aSpacing); DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestTransferItkToVtkTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); //calls TransferItkToVtkTransform mitk::AffineTransform3D::Pointer dummyTransform = dummy->GetIndexToWorldTransform(); CPPUNIT_ASSERT(mitk::MatrixEqualElementWise( anotherMatrix, dummyTransform->GetMatrix() )); } void TestConstructors() { //test standard constructor DummyTestClass::Pointer dummy1 = DummyTestClass::New(); bool test = dummy1->IsValid(); CPPUNIT_ASSERT(test == true); CPPUNIT_ASSERT(dummy1->GetFrameOfReferenceID() == 0); CPPUNIT_ASSERT(dummy1->GetIndexToWorldTransformLastModified() == 0); CPPUNIT_ASSERT(dummy1->GetSpacing() == aSpacing); CPPUNIT_ASSERT(dummy1->GetOrigin()==aPoint); CPPUNIT_ASSERT(dummy1->GetImageGeometry()==false); CPPUNIT_ASSERT(mitk::Equal( dummy1->GetIndexToWorldTransform(), aTransform, mitk::eps, true)); CPPUNIT_ASSERT(mitk::Equal( dummy1->GetBoundingBox(), aBoundingBox, mitk::eps, true)); DummyTestClass::Pointer dummy2 = DummyTestClass::New(); dummy2->SetOrigin(anotherPoint); float bounds[6] = {0,11,0,12,0,13}; dummy2->SetFloatBounds(bounds); dummy2->SetIndexToWorldTransform(anotherTransform); dummy2->SetSpacing(anotherSpacing); DummyTestClass::Pointer dummy3 = DummyTestClass::New(*dummy2); CPPUNIT_ASSERT(mitk::Equal(dummy3,dummy2,mitk::eps,true)); } //Equal Tests void Equal_CloneAndOriginal_ReturnsTrue() { CPPUNIT_ASSERT( mitk::Equal(aDummyGeometry, anotherDummyGeometry, mitk::eps,true)); } void Equal_DifferentOrigin_ReturnsFalse() { anotherDummyGeometry->SetOrigin(anotherPoint); CPPUNIT_ASSERT( mitk::Equal(aDummyGeometry, anotherDummyGeometry, mitk::eps,false)==false); } void Equal_DifferentIndexToWorldTransform_ReturnsFalse() { anotherDummyGeometry->SetIndexToWorldTransform(anotherTransform); CPPUNIT_ASSERT( mitk::Equal(aDummyGeometry, anotherDummyGeometry, mitk::eps,false)==false); } void Equal_DifferentSpacing_ReturnsFalse() { anotherDummyGeometry->SetSpacing(anotherSpacing); CPPUNIT_ASSERT( mitk::Equal(aDummyGeometry, anotherDummyGeometry, mitk::eps,false)==false); } void Equal_InputIsNull_ReturnsFalse() { DummyTestClass::Pointer geometryNull = NULL; MITK_INFO<<"Test, if a Null pointer throws an error. The next line needs to display an error."; CPPUNIT_ASSERT( mitk::Equal(geometryNull, anotherDummyGeometry, mitk::eps,false)==false); } void Equal_DifferentBoundingBox_ReturnsFalse() { //create different bounds to make the comparison false mitk::ScalarType bounds[ ] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; anotherDummyGeometry->SetBounds(bounds); CPPUNIT_ASSERT( mitk::Equal(aDummyGeometry, anotherDummyGeometry, mitk::eps,false)==false); } void TestComposeTransform(){ //Create Transformations to set and compare mitk::AffineTransform3D::Pointer transform1; transform1 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix1; matrix1.SetIdentity(); matrix1(1,1) = 2; transform1->SetMatrix( matrix1 ); //Spacing = 2 mitk::AffineTransform3D::Pointer transform2; transform2 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix2; matrix2.SetIdentity(); matrix2(1,1) = 2; transform2->SetMatrix( matrix2 ); //Spacing = 2 mitk::AffineTransform3D::Pointer transform3; transform3 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix3; matrix3.SetIdentity(); matrix3(1,1) = 4; transform3->SetMatrix( matrix3 ); //Spacing = 4 mitk::AffineTransform3D::Pointer transform4; transform4 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix4; matrix4.SetIdentity(); matrix4(1,1) = 0.25; transform4->SetMatrix( matrix4 ); //Spacing = 0.25 //Vector to compare spacing mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 4; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(transform1); //Spacing = 2 dummy->Compose(transform2); //Spacing = 4 CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); CPPUNIT_ASSERT(mitk::Equal(transform3,dummy->GetIndexToWorldTransform(),mitk::eps,true)); // 4=4 //undo changes, new and changed object need to be the same! dummy->Compose(transform4); //Spacing = 1 DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); // 1=1 } void TestComposeVtkMatrix(){ //Create Transformations to set and compare mitk::AffineTransform3D::Pointer transform1; transform1 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix1; matrix1.SetIdentity(); matrix1(1,1) = 2; transform1->SetMatrix( matrix1 ); //Spacing = 2 vtkMatrix4x4* vtkmatrix2; vtkmatrix2 = vtkMatrix4x4::New(); vtkmatrix2->Identity(); vtkmatrix2->SetElement(1,1,2); //Spacing = 2 mitk::AffineTransform3D::Pointer transform3; transform3 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix3; matrix3.SetIdentity(); matrix3(1,1) = 4; transform3->SetMatrix( matrix3 ); //Spacing = 4 vtkMatrix4x4* vtkmatrix4; vtkmatrix4 = vtkMatrix4x4::New(); vtkmatrix4->Identity(); vtkmatrix4->SetElement(1,1,0.25); //Spacing = 0.25 //Vector to compare spacing mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 4; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(transform1); //Spacing = 2 dummy->Compose(vtkmatrix2); //Spacing = 4 CPPUNIT_ASSERT(mitk::Equal(transform3,dummy->GetIndexToWorldTransform(),mitk::eps,true)); // 4=4 CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); //undo changes, new and changed object need to be the same! dummy->Compose(vtkmatrix4); //Spacing = 1 DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); // 1=1 } void TestTranslate(){ DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(anotherPoint==dummy->GetOrigin()); //use some random values for translation mitk::Vector3D translationVector; translationVector.SetElement(0, 17.5f); translationVector.SetElement(1, -32.3f); translationVector.SetElement(2, 4.0f); //compute ground truth mitk::Point3D tmpResult = anotherPoint + translationVector; dummy->Translate(translationVector); CPPUNIT_ASSERT( mitk::Equal( dummy->GetOrigin(), tmpResult )); //undo changes translationVector*=-1; dummy->Translate(translationVector); CPPUNIT_ASSERT( mitk::Equal( dummy->GetOrigin(), anotherPoint )); //undo changes, new and changed object need to be the same! translationVector.SetElement(0, -1 * anotherPoint[0]); translationVector.SetElement(1, -1 * anotherPoint[1]); translationVector.SetElement(2, -1 * anotherPoint[2]); dummy->Translate(translationVector); DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } // a part of the test requires axis-parallel coordinates int testIndexAndWorldConsistency(DummyTestClass::Pointer dummyGeometry) { //Testing consistency of index and world coordinate systems mitk::Point3D origin = dummyGeometry->GetOrigin(); mitk::Point3D dummyPoint; //Testing index->world->index conversion consistency dummyGeometry->WorldToIndex(origin, dummyPoint); dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); CPPUNIT_ASSERT(dummyPoint == origin); //Testing WorldToIndex(origin, mitk::Point3D)==(0,0,0) mitk::Point3D globalOrigin; mitk::FillVector3D(globalOrigin, 0,0,0); mitk::Point3D originContinuousIndex; dummyGeometry->WorldToIndex(origin, originContinuousIndex); CPPUNIT_ASSERT(originContinuousIndex == globalOrigin); //Testing WorldToIndex(origin, itk::Index)==(0,0,0) itk::Index<3> itkindex; dummyGeometry->WorldToIndex(origin, itkindex); itk::Index<3> globalOriginIndex; mitk::vtk2itk(globalOrigin, globalOriginIndex); CPPUNIT_ASSERT(itkindex == globalOriginIndex); //Testing WorldToIndex(origin-0.5*spacing, itk::Index)==(0,0,0) mitk::Vector3D halfSpacingStep = dummyGeometry->GetSpacing()*0.5; mitk::Matrix3D rotation; mitk::Point3D originOffCenter = origin-halfSpacingStep; dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(itkindex == globalOriginIndex); //Testing WorldToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0) originOffCenter = origin+halfSpacingStep; originOffCenter -= 0.0001; dummyGeometry->WorldToIndex( originOffCenter, itkindex); CPPUNIT_ASSERT(itkindex == globalOriginIndex); //Testing WorldToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); originOffCenter = origin+halfSpacingStep; itk::Index<3> global111; mitk::FillVector3D(global111, 1,1,1); dummyGeometry->WorldToIndex( originOffCenter, itkindex); CPPUNIT_ASSERT(itkindex == global111); //Testing WorldToIndex(GetCenter())==BoundingBox.GetCenter mitk::Point3D center = dummyGeometry->GetCenter(); mitk::Point3D centerContIndex; dummyGeometry->WorldToIndex(center, centerContIndex); mitk::BoundingBox::ConstPointer boundingBox = dummyGeometry->GetBoundingBox(); mitk::BoundingBox::PointType centerBounds = boundingBox->GetCenter(); CPPUNIT_ASSERT(mitk::Equal(centerContIndex,centerBounds)); //Testing GetCenter()==IndexToWorld(BoundingBox.GetCenter) center = dummyGeometry->GetCenter(); mitk::Point3D centerBoundsInWorldCoords; dummyGeometry->IndexToWorld(centerBounds, centerBoundsInWorldCoords); CPPUNIT_ASSERT(mitk::Equal(center,centerBoundsInWorldCoords)); //Test using random point, //Testing consistency of index and world coordinate systems mitk::Point3D point; mitk::FillVector3D(point,3.5,-2,4.6); //Testing index->world->index conversion consistency dummyGeometry->WorldToIndex(point, dummyPoint); dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); CPPUNIT_ASSERT(dummyPoint == point); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForVectors(DummyTestClass::Pointer dummyGeometry) { //Testing consistency of index and world coordinate systems for vectors mitk::Vector3D xAxisMM = dummyGeometry->GetAxisVector(0); mitk::Vector3D xAxisContinuousIndex; mitk::Point3D p, pIndex, origin; origin = dummyGeometry->GetOrigin(); p[0] = xAxisMM[0]+origin[0]; p[1] = xAxisMM[1]+origin[1]; p[2] = xAxisMM[2]+origin[2]; dummyGeometry->WorldToIndex(p,pIndex); dummyGeometry->WorldToIndex(xAxisMM,xAxisContinuousIndex); CPPUNIT_ASSERT(xAxisContinuousIndex[0] == pIndex[0]); CPPUNIT_ASSERT(xAxisContinuousIndex[1] == pIndex[1]); CPPUNIT_ASSERT(xAxisContinuousIndex[2] == pIndex[2]); dummyGeometry->IndexToWorld(xAxisContinuousIndex,xAxisContinuousIndex); dummyGeometry->IndexToWorld(pIndex,p); CPPUNIT_ASSERT(xAxisContinuousIndex == xAxisMM); CPPUNIT_ASSERT(xAxisContinuousIndex[0] == p[0]-origin[0]); CPPUNIT_ASSERT(xAxisContinuousIndex[1] == p[1]-origin[1]); CPPUNIT_ASSERT(xAxisContinuousIndex[2] == p[2]-origin[2]); //Test consictency for random vector mitk::Vector3D vector; mitk::FillVector3D(vector, 2.5,-3.2,8.1); mitk::Vector3D vectorContinuousIndex; p[0] = vector[0]+origin[0]; p[1] = vector[1]+origin[1]; p[2] = vector[2]+origin[2]; dummyGeometry->WorldToIndex(p,pIndex); dummyGeometry->WorldToIndex(vector,vectorContinuousIndex); CPPUNIT_ASSERT(vectorContinuousIndex[0] == pIndex[0]); CPPUNIT_ASSERT(vectorContinuousIndex[1] == pIndex[1]); CPPUNIT_ASSERT(vectorContinuousIndex[2] == pIndex[2]); dummyGeometry->IndexToWorld(vectorContinuousIndex,vectorContinuousIndex); dummyGeometry->IndexToWorld(pIndex,p); CPPUNIT_ASSERT(vectorContinuousIndex == vector); CPPUNIT_ASSERT(vectorContinuousIndex[0] == p[0]-origin[0]); CPPUNIT_ASSERT(vectorContinuousIndex[1] == p[1]-origin[1]); CPPUNIT_ASSERT(vectorContinuousIndex[2] == p[2]-origin[2]); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForIndex(DummyTestClass::Pointer dummyGeometry) { //Testing consistency of index and world coordinate systems // creating testing data itk::Index<4> itkIndex4, itkIndex4b; itk::Index<3> itkIndex3, itkIndex3b; itk::Index<2> itkIndex2, itkIndex2b; - mitk::Index3D mitkIndex, mitkIndexb; + itk::Index<3> mitkIndex, mitkIndexb; itkIndex4[0] = itkIndex4[1] = itkIndex4[2] = itkIndex4[3] = 4; itkIndex3[0] = itkIndex3[1] = itkIndex3[2] = 6; itkIndex2[0] = itkIndex2[1] = 2; mitkIndex[0] = mitkIndex[1] = mitkIndex[2] = 13; // check for constistency mitk::Point3D point; dummyGeometry->IndexToWorld(itkIndex2,point); dummyGeometry->WorldToIndex(point,itkIndex2b); CPPUNIT_ASSERT( ((itkIndex2b[0] == itkIndex2[0]) && (itkIndex2b[1] == itkIndex2[1]))); //Testing itk::index<2> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(itkIndex3,point); dummyGeometry->WorldToIndex(point,itkIndex3b); CPPUNIT_ASSERT( ((itkIndex3b[0] == itkIndex3[0]) && (itkIndex3b[1] == itkIndex3[1]) && (itkIndex3b[2] == itkIndex3[2]))); //Testing itk::index<3> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(itkIndex4,point); dummyGeometry->WorldToIndex(point,itkIndex4b); CPPUNIT_ASSERT( ((itkIndex4b[0] == itkIndex4[0]) && (itkIndex4b[1] == itkIndex4[1]) && (itkIndex4b[2] == itkIndex4[2]) && (itkIndex4b[3] == 0))); //Testing itk::index<3> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(mitkIndex,point); dummyGeometry->WorldToIndex(point,mitkIndexb); CPPUNIT_ASSERT( ((mitkIndexb[0] == mitkIndex[0]) && (mitkIndexb[1] == mitkIndex[1]) && (mitkIndexb[2] == mitkIndex[2]))); //Testing mitk::Index for IndexToWorld/WorldToIndex consistency return EXIT_SUCCESS; } void TestIndexToWorld(){ DummyTestClass::Pointer dummy = DummyTestClass::New(); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); //Geometry must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); //Test with other geometries dummy->SetOrigin(anotherPoint); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetIndexToWorldTransform(anotherTransform); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetOrigin(anotherPoint); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetSpacing(anotherSpacing); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); } void TestExecuteOperation(){ DummyTestClass::Pointer dummy = DummyTestClass::New(); //Do same Operations with new Dummy and compare DummyTestClass::Pointer newDummy = DummyTestClass::New(); //Test operation Nothing mitk::Operation* opN = new mitk::Operation(mitk::OpNOTHING); dummy->ExecuteOperation(opN); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); //Test operation Move mitk::PointOperation* opP = new mitk::PointOperation(mitk::OpMOVE,anotherPoint); dummy->ExecuteOperation(opP); CPPUNIT_ASSERT(anotherPoint==dummy->GetOrigin()); newDummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); //Test operation Scale, Scale sets spacing to scale+1 mitk::Point3D spacing; spacing[0]=anotherSpacing[0]-1.; spacing[1]=anotherSpacing[1]-1.; spacing[2]=anotherSpacing[2]-1.; mitk::PointOperation* opS = new mitk::PointOperation(mitk::OpSCALE,spacing); dummy->ExecuteOperation(opS); CPPUNIT_ASSERT(anotherSpacing==dummy->GetSpacing()); newDummy->SetSpacing(anotherSpacing); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); //change Geometry to test more cases dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetSpacing(anotherSpacing); //Testing a rotation of the geometry double angle = 35.0; mitk::Vector3D rotationVector; mitk::FillVector3D( rotationVector, 1, 0, 0 ); mitk::Point3D center = dummy->GetCenter(); mitk::RotationOperation* opR = new mitk::RotationOperation( mitk::OpROTATE, center, rotationVector, angle ); dummy->ExecuteOperation(opR); mitk::Matrix3D rotation; mitk::GetRotation(dummy, rotation); mitk::Vector3D voxelStep=rotation*anotherSpacing; mitk::Vector3D voxelStepIndex; dummy->WorldToIndex(voxelStep, voxelStepIndex); mitk::Vector3D expectedVoxelStepIndex; expectedVoxelStepIndex.Fill(1); CPPUNIT_ASSERT(mitk::Equal(voxelStepIndex,expectedVoxelStepIndex)); delete opR; delete opN; delete opS; delete opP; } void TestCalculateBoundingBoxRelToTransform(){ DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetExtentInMM(0,15); dummy->SetExtentInMM(1,20); dummy->SetExtentInMM(2,8); mitk::BoundingBox::Pointer dummyBoundingBox = dummy->CalculateBoundingBoxRelativeToTransform(anotherTransform); mitk::BoundingBox::PointsContainer::Pointer pointscontainer=mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid=0; unsigned char i; mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); anotherTransform->GetInverse(inverse); for(i=0; i<8; ++i) pointscontainer->InsertElement( pointid++, inverse->TransformPoint( dummy->GetCornerPoint(i) )); mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); CPPUNIT_ASSERT(mitk::Equal(result,dummyBoundingBox,mitk::eps,true)); //dummy still needs to be unchanged, except for extend DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetExtentInMM(0,15); newDummy->SetExtentInMM(1,20); newDummy->SetExtentInMM(2,8); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } //void TestSetTimeBounds(){ // mitk::TimeBounds timeBounds; // timeBounds[0] = 1; // timeBounds[1] = 9; // DummyTestClass::Pointer dummy = DummyTestClass::New(); // dummy->SetTimeBounds(timeBounds); // mitk::TimeBounds timeBounds2 = dummy->GetTimeBounds(); // CPPUNIT_ASSERT(timeBounds[0]==timeBounds2[0]); // CPPUNIT_ASSERT(timeBounds[1]==timeBounds2[1]); // //undo changes, new and changed object need to be the same! // timeBounds[0]=mitk::ScalarTypeNumericTraits::NonpositiveMin(); // timeBounds[1]=mitk::ScalarTypeNumericTraits::max(); // DummyTestClass::Pointer newDummy = DummyTestClass::New(); // CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); //} void TestIs2DConvertable(){ DummyTestClass::Pointer dummy = DummyTestClass::New(); //new initialized geometry is 2D convertable CPPUNIT_ASSERT(dummy->Is2DConvertable()); //Wrong Spacing needs to fail dummy->SetSpacing(anotherSpacing); CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); //undo dummy->SetSpacing(aSpacing); CPPUNIT_ASSERT(dummy->Is2DConvertable()); //Wrong Origin needs to fail dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); //undo dummy->SetOrigin(aPoint); CPPUNIT_ASSERT(dummy->Is2DConvertable()); //third dimension must not be transformed mitk::AffineTransform3D::Pointer dummyTransform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType dummyMatrix; dummyMatrix.SetIdentity(); dummyTransform->SetMatrix( dummyMatrix ); dummy->SetIndexToWorldTransform(dummyTransform); //identity matrix is 2DConvertable CPPUNIT_ASSERT(dummy->Is2DConvertable()); dummyMatrix(0,2) = 3; dummyTransform->SetMatrix( dummyMatrix ); CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); dummyMatrix.SetIdentity(); dummyMatrix(1,2) = 0.4; dummyTransform->SetMatrix( dummyMatrix ); CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); dummyMatrix.SetIdentity(); dummyMatrix(2,2) = 3; dummyTransform->SetMatrix( dummyMatrix ); CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); dummyMatrix.SetIdentity(); dummyMatrix(2,1) = 3; dummyTransform->SetMatrix( dummyMatrix ); CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); dummyMatrix.SetIdentity(); dummyMatrix(2,0) = 3; dummyTransform->SetMatrix( dummyMatrix ); CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); //undo changes, new and changed object need to be the same! dummyMatrix.SetIdentity(); dummyTransform->SetMatrix( dummyMatrix ); DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestGetCornerPoint(){ DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0,11,0,12,0,13}; dummy->SetFloatBounds(bounds); mitk::Point3D corner, refCorner; //Corner 0 mitk::FillVector3D(refCorner,bounds[0],bounds[2],bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner=dummy->GetCornerPoint(0); CPPUNIT_ASSERT(refCorner==corner); corner=dummy->GetCornerPoint(true,true,true); CPPUNIT_ASSERT(refCorner==corner); //Corner 1 mitk::FillVector3D(refCorner,bounds[0],bounds[2],bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner=dummy->GetCornerPoint(1); CPPUNIT_ASSERT(refCorner==corner); corner=dummy->GetCornerPoint(true,true,false); CPPUNIT_ASSERT(refCorner==corner); //Corner 2 mitk::FillVector3D(refCorner,bounds[0],bounds[3],bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner=dummy->GetCornerPoint(2); CPPUNIT_ASSERT(refCorner==corner); corner=dummy->GetCornerPoint(true,false,true); CPPUNIT_ASSERT(refCorner==corner); //Corner 3 mitk::FillVector3D(refCorner,bounds[0],bounds[3],bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner=dummy->GetCornerPoint(3); CPPUNIT_ASSERT(refCorner==corner); corner=dummy->GetCornerPoint(true,false,false); CPPUNIT_ASSERT(refCorner==corner); //Corner 4 mitk::FillVector3D(refCorner,bounds[1],bounds[2],bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner=dummy->GetCornerPoint(4); CPPUNIT_ASSERT(refCorner==corner); corner=dummy->GetCornerPoint(false,true,true); CPPUNIT_ASSERT(refCorner==corner); //Corner 5 mitk::FillVector3D(refCorner,bounds[1],bounds[2],bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner=dummy->GetCornerPoint(5); CPPUNIT_ASSERT(refCorner==corner); corner=dummy->GetCornerPoint(false,true,false); CPPUNIT_ASSERT(refCorner==corner); //Corner 6 mitk::FillVector3D(refCorner,bounds[1],bounds[3],bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner=dummy->GetCornerPoint(6); CPPUNIT_ASSERT(refCorner==corner); corner=dummy->GetCornerPoint(false,false,true); CPPUNIT_ASSERT(refCorner==corner); //Corner 7 mitk::FillVector3D(refCorner,bounds[1],bounds[3],bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner=dummy->GetCornerPoint(7); CPPUNIT_ASSERT(refCorner==corner); corner=dummy->GetCornerPoint(false,false,false); CPPUNIT_ASSERT(refCorner==corner); //Wrong Corner needs to fail CPPUNIT_ASSERT_THROW(dummy->GetCornerPoint(20),itk::ExceptionObject); //dummy geometry must not have changed! DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetIndexToWorldTransform(anotherTransform); newDummy->SetFloatBounds(bounds); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestExtentInMM() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetExtentInMM(0,50); CPPUNIT_ASSERT(50==dummy->GetExtentInMM(0)); //Vnl Matrix has changed. The next line only works because the spacing is 1! CPPUNIT_ASSERT(50==dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude()); //Smaller extent than original dummy->SetExtentInMM(0,5); CPPUNIT_ASSERT(5==dummy->GetExtentInMM(0)); CPPUNIT_ASSERT(5==dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude()); dummy->SetExtentInMM(1,4); CPPUNIT_ASSERT(4==dummy->GetExtentInMM(1)); CPPUNIT_ASSERT(4==dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).magnitude()); dummy->SetExtentInMM(2,2.5); CPPUNIT_ASSERT(2.5==dummy->GetExtentInMM(2)); CPPUNIT_ASSERT(2.5==dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).magnitude()); } void TestGetAxisVector(){ DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0,11,0,12,0,13}; dummy->SetFloatBounds(bounds); mitk::Vector3D vector; mitk::FillVector3D(vector,bounds[1],0,0); dummy->IndexToWorld(vector,vector); CPPUNIT_ASSERT(dummy->GetAxisVector(0)==vector); mitk::FillVector3D(vector,0,bounds[3],0); dummy->IndexToWorld(vector,vector); CPPUNIT_ASSERT(dummy->GetAxisVector(1)==vector); mitk::FillVector3D(vector,0,0,bounds[5]); dummy->IndexToWorld(vector,vector); CPPUNIT_ASSERT(dummy->GetAxisVector(2)==vector); } void TestGetCenter(){ DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0,11,2,12,1,13}; dummy->SetFloatBounds(bounds); mitk::Point3D refCenter; for( int i=0;i<3;i++) refCenter.SetElement(i,( bounds[2 * i] + bounds[2 * i + 1] ) / 2.0); dummy->IndexToWorld(refCenter,refCenter); CPPUNIT_ASSERT(dummy->GetCenter()==refCenter); } void TestGetDiagonalLength(){ DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1,3,5,8,7.5,11.5}; dummy->SetFloatBounds(bounds); //3-1=2, 8-5=3, 11.5-7.5=4; 2^2+3^2+4^2 = 29 double expectedLength = sqrt(29.); CPPUNIT_ASSERT(expectedLength==dummy->GetDiagonalLength()); CPPUNIT_ASSERT(29==dummy->GetDiagonalLength2()); //dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestGetExtent(){ DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1,3,5,8,7.5,11.5}; dummy->SetFloatBounds(bounds); CPPUNIT_ASSERT(2==dummy->GetExtent(0)); CPPUNIT_ASSERT(3==dummy->GetExtent(1)); CPPUNIT_ASSERT(4==dummy->GetExtent(2)); //dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestIsInside(){ DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1,3,5,8,7.5,11.5}; dummy->SetFloatBounds(bounds); mitk::Point3D insidePoint; mitk::Point3D outsidePoint; mitk::FillVector3D(insidePoint,2,6,7.6); mitk::FillVector3D(outsidePoint,0,9,8.2); CPPUNIT_ASSERT(dummy->IsIndexInside(insidePoint)); CPPUNIT_ASSERT(false==dummy->IsIndexInside(outsidePoint)); dummy->IndexToWorld(insidePoint,insidePoint); dummy->IndexToWorld(outsidePoint,outsidePoint); CPPUNIT_ASSERT(dummy->IsInside(insidePoint)); CPPUNIT_ASSERT(false==dummy->IsInside(outsidePoint)); //dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } void TestInitialize() { //test standard constructor DummyTestClass::Pointer dummy1 = DummyTestClass::New(); DummyTestClass::Pointer dummy2 = DummyTestClass::New(); dummy2->SetOrigin(anotherPoint); dummy2->SetBounds(anotherBoundingBox->GetBounds()); //mitk::TimeBounds timeBounds; //timeBounds[0] = 1; //timeBounds[1] = 9; //dummy2->SetTimeBounds(timeBounds); dummy2->SetIndexToWorldTransform(anotherTransform); dummy2->SetSpacing(anotherSpacing); dummy1->InitializeGeometry(dummy2); CPPUNIT_ASSERT(mitk::Equal(dummy1,dummy2,mitk::eps,true)); dummy1->Initialize(); DummyTestClass::Pointer dummy3 = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy3,dummy1,mitk::eps,true)); } void TestGetMatrixColumn(){ DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); mitk::Vector3D testVector,refVector; testVector.SetVnlVector(dummy->GetMatrixColumn(0)); mitk::FillVector3D(refVector,1,0,0); CPPUNIT_ASSERT(testVector==refVector); testVector.SetVnlVector(dummy->GetMatrixColumn(1)); mitk::FillVector3D(refVector,0,2,0); CPPUNIT_ASSERT(testVector==refVector); testVector.SetVnlVector(dummy->GetMatrixColumn(2)); mitk::FillVector3D(refVector,0,0,1); CPPUNIT_ASSERT(testVector==refVector); //dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetIndexToWorldTransform(anotherTransform); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } /* void (){ DummyTestClass::Pointer dummy = DummyTestClass::New(); CPPUNIT_ASSERT(); //undo changes, new and changed object need to be the same! DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } */ };//end class mitkBaseGeometryTestSuite MITK_TEST_SUITE_REGISTRATION(mitkBaseGeometry) diff --git a/Core/Code/Testing/mitkClippedSurfaceBoundsCalculatorTest.cpp b/Core/Code/Testing/mitkClippedSurfaceBoundsCalculatorTest.cpp index 186dbda875..21f4f3d702 100644 --- a/Core/Code/Testing/mitkClippedSurfaceBoundsCalculatorTest.cpp +++ b/Core/Code/Testing/mitkClippedSurfaceBoundsCalculatorTest.cpp @@ -1,484 +1,484 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include #include "mitkClippedSurfaceBoundsCalculator.h" #include "mitkGeometry3D.h" #include "mitkPlaneGeometry.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" static void CheckPlanesInsideBoundingBoxOnlyOnOneSlice(mitk::BaseGeometry::Pointer geometry3D) { //Check planes which are inside the bounding box mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(geometry3D.GetPointer()) ); //Check planes which are only on one slice: //Slice 0 mitk::Point3D origin; origin[0] = 511; origin[1] = 0; origin[2] = 0; mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New(); planeOnSliceZero->InitializePlane(origin, normal); calculator->SetInput( planeOnSliceZero , image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0"); //Slice 3 origin[2] = 3; mitk::PlaneGeometry::Pointer planeOnSliceThree = mitk::PlaneGeometry::New(); planeOnSliceThree->InitializePlane(origin, normal); planeOnSliceThree->SetImageGeometry(false); calculator->SetInput( planeOnSliceThree , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 3 && minMax.second == 3, "Check if plane is on slice 3"); //Slice 17 origin[2] = 17; mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New(); planeOnSliceSeventeen->InitializePlane(origin, normal); calculator->SetInput( planeOnSliceSeventeen , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 17 && minMax.second == 17, "Check if plane is on slice 17"); //Slice 20 origin[2] = 19; mitk::PlaneGeometry::Pointer planeOnSliceTwenty = mitk::PlaneGeometry::New(); planeOnSliceTwenty->InitializePlane(origin, normal); calculator->SetInput( planeOnSliceTwenty , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 19 && minMax.second == 19, "Check if plane is on slice 19"); delete calculator; } static void CheckPlanesInsideBoundingBox(mitk::BaseGeometry::Pointer geometry3D) { //Check planes which are inside the bounding box mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(geometry3D.GetPointer()) ); //Check planes which are only on one slice: //Slice 0 mitk::Point3D origin; origin[0] = 511; // Set to 511.9 so that the intersection point is inside the bounding box origin[1] = 0; origin[2] = 0; mitk::Vector3D normal; mitk::FillVector3D(normal, 1, 0, 0); mitk::PlaneGeometry::Pointer planeSagittalOne = mitk::PlaneGeometry::New(); planeSagittalOne->InitializePlane(origin, normal); calculator->SetInput( planeSagittalOne , image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); //Slice 3 origin[0] = 256; MITK_INFO << "Case1 origin: " << origin; mitk::PlaneGeometry::Pointer planeSagittalTwo = mitk::PlaneGeometry::New(); planeSagittalTwo->InitializePlane(origin, normal); MITK_INFO << "PlaneNormal: " << planeSagittalTwo->GetNormal(); MITK_INFO << "PlaneOrigin: " << planeSagittalTwo->GetOrigin(); calculator->SetInput( planeSagittalTwo , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); //Slice 17 origin[0] = 0; // Set to 0.1 so that the intersection point is inside the bounding box mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New(); planeOnSliceSeventeen->InitializePlane(origin, normal); calculator->SetInput( planeOnSliceSeventeen , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); //Crooked planes: origin[0] = 0; origin[1] = 507; origin[2] = 0; normal[0] = 1; normal[1] = -1; normal[2] = 1; mitk::PlaneGeometry::Pointer planeCrookedOne = mitk::PlaneGeometry::New(); planeCrookedOne->InitializePlane(origin, normal); calculator->SetInput( planeCrookedOne , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 4, "Check if plane is from slice 0 to slice 4 with inclined plane"); origin[0] = 512; origin[1] = 0; origin[2] = 16; mitk::PlaneGeometry::Pointer planeCrookedTwo = mitk::PlaneGeometry::New(); planeCrookedTwo->InitializePlane(origin, normal); calculator->SetInput( planeCrookedTwo , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 17 && minMax.second == 19, "Check if plane is from slice 17 to slice 19 with inclined plane"); origin[0] = 511; origin[1] = 0; origin[2] = 0; normal[1] = 0; normal[2] = 0.04; mitk::PlaneGeometry::Pointer planeCrookedThree = mitk::PlaneGeometry::New(); planeCrookedThree->InitializePlane(origin, normal); calculator->SetInput( planeCrookedThree , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19 with inclined plane"); delete calculator; } static void CheckPlanesOutsideOfBoundingBox(mitk::BaseGeometry::Pointer geometry3D) { //Check planes which are outside of the bounding box mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(geometry3D.GetPointer()) ); //In front of the bounding box mitk::Point3D origin; origin[0] = 511; origin[1] = 0; origin[2] = -5; mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); mitk::PlaneGeometry::Pointer planeInFront = mitk::PlaneGeometry::New(); planeInFront->InitializePlane(origin, normal); calculator->SetInput( planeInFront , image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Behind the bounding box origin[2] = 515; mitk::PlaneGeometry::Pointer planeBehind = mitk::PlaneGeometry::New(); planeBehind->InitializePlane(origin, normal); calculator->SetInput( planeBehind , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Above origin[1] = 515; mitk::FillVector3D(normal, 0, 1, 0); mitk::PlaneGeometry::Pointer planeAbove = mitk::PlaneGeometry::New(); planeAbove->InitializePlane(origin, normal); calculator->SetInput( planeAbove , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Below origin[1] = -5; mitk::PlaneGeometry::Pointer planeBelow = mitk::PlaneGeometry::New(); planeBelow->InitializePlane(origin, normal); calculator->SetInput( planeBelow , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Left side origin[0] = -5; mitk::FillVector3D(normal, 1, 0, 0); mitk::PlaneGeometry::Pointer planeLeftSide = mitk::PlaneGeometry::New(); planeLeftSide->InitializePlane(origin, normal); calculator->SetInput( planeLeftSide , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Right side origin[1] = 515; mitk::PlaneGeometry::Pointer planeRightSide = mitk::PlaneGeometry::New(); planeRightSide->InitializePlane(origin, normal); calculator->SetInput( planeRightSide , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); delete calculator; } static void CheckIntersectionPointsOfTwoGeometry3D(mitk::BaseGeometry::Pointer firstGeometry3D, mitk::BaseGeometry::Pointer secondGeometry3D) { mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer firstImage = mitk::Image::New(); firstImage->Initialize( mitk::MakePixelType(), *(firstGeometry3D.GetPointer()) ); calculator->SetInput( secondGeometry3D, firstImage); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); } static void CheckIntersectionWithPointCloud( mitk::BaseGeometry::Pointer geometry3D ) { //Check planes which are inside the bounding box mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(geometry3D.GetPointer()) ); { mitk::Point3D pnt1, pnt2; pnt1[0] = 3; pnt1[1] = 5; pnt1[2] = 3; pnt2[0] = 8; pnt2[1] = 3; pnt2[2] = 8; mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist; pointlist.push_back( pnt1 ); pointlist.push_back( pnt2 ); mitk::ClippedSurfaceBoundsCalculator calculator; calculator.SetInput( pointlist, image ); calculator.Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMaxZ.first == 3 && minMaxZ.second == 8, "Check if points span from slice 3 to slice 8 in axial"); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX(); MITK_TEST_CONDITION(minMaxX.first == 3 && minMaxX.second == 5, "Check if points span from slice 3 to slice 5 in sagittal"); } { mitk::Point3D pnt1, pnt2; pnt1.Fill( -3 ); pnt2.Fill( 600 ); mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist; pointlist.push_back( pnt1 ); pointlist.push_back( pnt2 ); mitk::ClippedSurfaceBoundsCalculator calculator; calculator.SetInput( pointlist, image ); calculator.Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMaxZ.first == 0 && minMaxZ.second == 19, "Check if points are correctly clipped to slice 0 and slice 19 in axial"); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX(); MITK_TEST_CONDITION(minMaxX.first == 0 && minMaxX.second == 511, "Check if points are correctly clipped to slice 0 and slice 511 in sagittal"); } } int mitkClippedSurfaceBoundsCalculatorTest(int, char* []) { // always start with this! MITK_TEST_BEGIN("ClippedSurfaceBoundsCalculator"); /** The class mitkClippedSurfaceBoundsCalculator calculates the intersection points of a PlaneGeometry and a Geometry3D. * This unittest checks if the correct min and max values for the three spatial directions (x, y, z) * are calculated. To test this we define artifical PlaneGeometries and Geometry3Ds and test different * scenarios: * * 1. planes which are inside the bounding box of a 3D geometry but only on one slice * 2. planes which are outside of the bounding box * 3. planes which are inside the bounding box but over more than one slice * * Note: Currently rotated geometries are not tested! */ /********************* Define Geometry3D ***********************/ //Define origin: mitk::Point3D origin; origin[0] = 511; origin[1] = 0; origin[2] = 0; //Define normal: mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); //Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializePlane(origin, normal); //Set Bounds: mitk::BoundingBox::BoundsArrayType bounds = planeGeometry->GetBounds(); bounds[0] = 0; bounds[1] = 512; bounds[2] = 0; bounds[3] = 512; bounds[4] = 0; bounds[5] = 1; planeGeometry->SetBounds(bounds); //Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer slicedGeometry3D = mitk::SlicedGeometry3D::New(); slicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry.GetPointer()), 20); mitk::BaseGeometry::Pointer geometry3D = dynamic_cast< mitk::BaseGeometry* > ( slicedGeometry3D.GetPointer() ); geometry3D->SetImageGeometry(true); //Define origin for second Geometry3D; mitk::Point3D origin2; origin2[0] = 511; origin2[1] = 60; origin2[2] = 0; //Define normal: mitk::Vector3D normal2; mitk::FillVector3D(normal2, 0, 1, 0); //Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry2 = mitk::PlaneGeometry::New(); planeGeometry2->InitializePlane(origin2, normal2); //Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer secondSlicedGeometry3D = mitk::SlicedGeometry3D::New(); secondSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry2.GetPointer()), 20); mitk::BaseGeometry::Pointer secondGeometry3D = dynamic_cast< mitk::BaseGeometry* > ( secondSlicedGeometry3D.GetPointer() ); secondGeometry3D->SetImageGeometry(true); /***************************************************************/ CheckPlanesInsideBoundingBoxOnlyOnOneSlice(geometry3D); CheckPlanesOutsideOfBoundingBox(geometry3D); CheckPlanesInsideBoundingBox(geometry3D); CheckIntersectionPointsOfTwoGeometry3D(geometry3D, secondGeometry3D); CheckIntersectionWithPointCloud( geometry3D ); /** ToDo: * test also rotated 3D geometry! */ MITK_TEST_END(); } diff --git a/Core/Code/Testing/mitkEqualTest.cpp b/Core/Code/Testing/mitkEqualTest.cpp index bab2a1ca58..ef6cfbeb0d 100644 --- a/Core/Code/Testing/mitkEqualTest.cpp +++ b/Core/Code/Testing/mitkEqualTest.cpp @@ -1,207 +1,207 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include -#include +#include class mitkEqualTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkEqualTestSuite); MITK_TEST(Equal_SameMitkVector3D_ReturnTrue); MITK_TEST(Equal_SameMitkVector2D_ReturnTrue); MITK_TEST(Equal_DifferentMitkVector3D_ReturnFalse); MITK_TEST(Equal_DifferentMitkVector2D_ReturnFalse); MITK_TEST(Equal_SameMitkVnlVector_ReturnTrue); MITK_TEST(Equal_DifferentMitkVnlVector_ReturnFalse); MITK_TEST(Equal_SameVnlVector_ReturnTrue); MITK_TEST(Equal_DifferentVnlVector_ReturnFalse); MITK_TEST(Equal_SameItkVector_ReturnTrue); MITK_TEST(Equal_DifferentItkVector_ReturnFalse); MITK_TEST(Equal_SameScalar_ReturnTrue); MITK_TEST(Equal_DifferentScalar_ReturnFalse); MITK_TEST(Equal_SameItkPoint_ReturnTrue); MITK_TEST(Equal_DifferentItkPoint_ReturnFalse); CPPUNIT_TEST_SUITE_END(); public: void Equal_SameMitkVector3D_ReturnTrue() { mitk::Vector3D v1; v1.Fill(1); mitk::Vector3D v2; v2.Fill(1); CPPUNIT_ASSERT(mitk::Equal(v1,v2)); } void Equal_SameMitkVector2D_ReturnTrue() { mitk::Vector2D v1; v1.Fill(2); mitk::Vector2D v2; v2.Fill(2); CPPUNIT_ASSERT(mitk::Equal(v1,v2)); } void Equal_DifferentMitkVector3D_ReturnFalse() { mitk::Vector3D v1; v1.SetElement(0, 1); v1.SetElement(1, 1); v1.SetElement(2, 1); mitk::Vector3D v2; v2.SetElement(0, 2); v2.SetElement(1, 1); v2.SetElement(2, 1); CPPUNIT_ASSERT_EQUAL_MESSAGE("Coparison of 2 different 3D MITK vectors (first element). Result should be false.", mitk::Equal(v1,v2), false); mitk::Vector3D v3; v3.SetElement(0, 1); v3.SetElement(1, 2); v3.SetElement(2, 1); CPPUNIT_ASSERT_EQUAL_MESSAGE("Coparison of 2 different 3D MITK vectors (second element). Result should be false.", mitk::Equal(v1,v3), false); mitk::Vector3D v4; v4.SetElement(0, 1); v4.SetElement(1, 1); v4.SetElement(2, 2); CPPUNIT_ASSERT_EQUAL_MESSAGE("Coparison of 2 different 3D MITK vectors (third element). Result should be false.", mitk::Equal(v1,v4), false); } void Equal_DifferentMitkVector2D_ReturnFalse() { mitk::Vector2D v1; v1.SetElement(0, 1); v1.SetElement(1, 1); mitk::Vector2D v2; v2.SetElement(0, 2); v2.SetElement(1, 1); CPPUNIT_ASSERT_EQUAL_MESSAGE("Coparison of 2 different 2D MITK vectors (first element). Result should be false.", mitk::Equal(v1,v2), false); mitk::Vector2D v3; v3.SetElement(0, 1); v3.SetElement(1, 2); CPPUNIT_ASSERT_EQUAL_MESSAGE("Coparison of 2 different 2D MITK vectors (second element). Result should be false.", mitk::Equal(v1,v3), false); } void Equal_SameMitkVnlVector_ReturnTrue() { mitk::VnlVector v1(2); v1.fill(8); mitk::VnlVector v2(2); v2.fill(8); CPPUNIT_ASSERT(mitk::Equal(v1,v2)); } void Equal_DifferentMitkVnlVector_ReturnFalse() { mitk::VnlVector v1(3); v1.fill(9.2); mitk::VnlVector v2(3); v2.fill(9.2); v2[0] = 6; CPPUNIT_ASSERT_EQUAL_MESSAGE("Comparison of 2 different MITK VNL vectors. Result should be false.", mitk::Equal(v1,v2), false); } void Equal_SameVnlVector_ReturnTrue() { vnl_vector_fixed v1; v1.fill(3.0); vnl_vector_fixed v2; v2.fill(3.0); CPPUNIT_ASSERT(mitk::Equal(v1,v2)); } void Equal_DifferentVnlVector_ReturnFalse() { vnl_vector_fixed v1; v1.fill(8.231); vnl_vector_fixed v2; v2[0] = 3.1; v2[1] = 8.231; CPPUNIT_ASSERT_EQUAL_MESSAGE("Comparison of 2 different VNL vectors (first element). Result should be false.", mitk::Equal(v1,v2), false); vnl_vector_fixed v3; v3[0] = 8.231; v3[1] = 2.14; CPPUNIT_ASSERT_EQUAL_MESSAGE("Comparison of 2 different VNL vectors (first element). Result should be false.", mitk::Equal(v1,v3), false); } void Equal_SameItkVector_ReturnTrue() { itk::Vector v1; v1.Fill(1.32); itk::Vector v2; v2.Fill(1.32); mitk::Equal(v1, v2); CPPUNIT_ASSERT(mitk::Equal(v1,v2)); } void Equal_DifferentItkVector_ReturnFalse() { itk::Vector v1; v1.Fill(5.2); itk::Vector v2; v2.Fill(5.2); v2.SetElement(0, 1.4); CPPUNIT_ASSERT_EQUAL_MESSAGE("Coparison of 2 different ITK vectors (first element). Result should be false.", mitk::Equal(v1,v2), false); itk::Vector v3; v3.Fill(5.2); v3.SetElement(1, 1.4); CPPUNIT_ASSERT_EQUAL_MESSAGE("Coparison of 2 different ITK vectors (second element). Result should be false.", mitk::Equal(v1,v3), false); itk::Vector v4; v4.Fill(5.2); v4.SetElement(2, 1.4); CPPUNIT_ASSERT_EQUAL_MESSAGE("Coparison of 2 different ITK vectors (third element). Result should be false.", mitk::Equal(v1,v4), false); } void Equal_SameScalar_ReturnTrue() { mitk::ScalarType a = 6.432; mitk::ScalarType b = 6.432; CPPUNIT_ASSERT(mitk::Equal(a,b)); } void Equal_DifferentScalar_ReturnFalse() { - mitk::ScalarType a = 6; - mitk::ScalarType b = 6 + 1.01*mitk::eps; + mitk::ScalarType a = 0.6; + mitk::ScalarType b = 0.6 + 1.01*mitk::eps; CPPUNIT_ASSERT_EQUAL_MESSAGE("Comparison of 2 different scalars. Result should be false", mitk::Equal(a,b), false); } void Equal_SameItkPoint_ReturnTrue() { itk::Point p1; p1.Fill(3.21); itk::Point p2; p2.Fill(3.21); CPPUNIT_ASSERT(mitk::Equal(p1,p2)); } void Equal_DifferentItkPoint_ReturnFalse() { itk::Point p1; p1.Fill(2.1); itk::Point p2; p2[0] = 1; p2[1] = 2.1; CPPUNIT_ASSERT_EQUAL_MESSAGE("Comparison of 2 different points (first element). Result should be false.", mitk::Equal(p1,p2), false); itk::Point p3; p3[0] = 2.1; p3[1] = 2.1 + 1.01*mitk::eps; CPPUNIT_ASSERT_EQUAL_MESSAGE("Comparison of 2 different points (second element). Result should be false.", mitk::Equal(p1,p3), false); } }; MITK_TEST_SUITE_REGISTRATION(mitkEqual) diff --git a/Core/Code/Testing/mitkExtractSliceFilterTest.cpp b/Core/Code/Testing/mitkExtractSliceFilterTest.cpp index 4ad3a0a71e..7a70c208dc 100644 --- a/Core/Code/Testing/mitkExtractSliceFilterTest.cpp +++ b/Core/Code/Testing/mitkExtractSliceFilterTest.cpp @@ -1,1169 +1,1169 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #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 //use this to create the test volume on the fly #define CREATE_VOLUME //use this to save the created volume //#define SAVE_VOLUME //use this to calculate the error from the sphere mathematical model to our pixel based one //#define CALC_TESTFAILURE_DEVIATION //use this to render an oblique slice through a specified image //#define SHOW_SLICE_IN_RENDER_WINDOW //use this to have infos printed in mbilog //#define EXTRACTOR_DEBUG /*these are the deviations calculated by the function CalcTestFailureDeviation (see for details)*/ #define Testfailure_Deviation_Mean_128 0.853842 #define Testfailure_Deviation_Volume_128 0.145184 #define Testfailure_Deviation_Diameter_128 1.5625 #define Testfailure_Deviation_Mean_256 0.397693 #define Testfailure_Deviation_Volume_256 0.0141357 #define Testfailure_Deviation_Diameter_256 0.78125 #define Testfailure_Deviation_Mean_512 0.205277 #define Testfailure_Deviation_Volume_512 0.01993 #define Testfailure_Deviation_Diameter_512 0.390625 class mitkExtractSliceFilterTestClass{ public: static void TestSlice(mitk::PlaneGeometry* planeGeometry, std::string testname) { TestPlane = planeGeometry; TestName = testname; mitk::ScalarType centerCoordValue = TestvolumeSize / 2.0; mitk::ScalarType center[3] = {centerCoordValue, centerCoordValue, centerCoordValue}; mitk::Point3D centerIndex(center); double radius = TestvolumeSize / 4.0; if(TestPlane->Distance(centerIndex) >= radius ) return;//outside sphere //feed ExtractSliceFilter mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(); slicer->SetInput(TestVolume); slicer->SetWorldGeometry(TestPlane); slicer->Update(); MITK_TEST_CONDITION_REQUIRED(slicer->GetOutput() != NULL, "Extractor returned a slice"); mitk::Image::Pointer reslicedImage = slicer->GetOutput(); AccessFixedDimensionByItk(reslicedImage, TestSphereRadiusByItk, 2); AccessFixedDimensionByItk(reslicedImage, TestSphereAreaByItk, 2); /* double devArea, devDiameter; if(TestvolumeSize == 128.0){ devArea = Testfailure_Deviation_Volume_128; devDiameter = Testfailure_Deviation_Diameter_128; } else if(TestvolumeSize == 256.0){devArea = Testfailure_Deviation_Volume_256; devDiameter = Testfailure_Deviation_Diameter_256;} else if (TestvolumeSize == 512.0){devArea = Testfailure_Deviation_Volume_512; devDiameter = Testfailure_Deviation_Diameter_512;} else{devArea = Testfailure_Deviation_Volume_128; devDiameter = Testfailure_Deviation_Diameter_128;} */ std::string areatestName = TestName.append(" area"); std::string diametertestName = TestName.append(" testing diameter"); //TODO think about the deviation, 1% makes no sense at all MITK_TEST_CONDITION(std::abs(100 - testResults.percentageAreaCalcToPixel) < 1, areatestName ); MITK_TEST_CONDITION(std::abs(100 - testResults.percentageRadiusToPixel) < 1, diametertestName ); #ifdef EXTRACTOR_DEBUG MITK_INFO << TestName << " >>> " << "planeDistanceToSphereCenter: " << testResults.planeDistanceToSphereCenter; MITK_INFO << "area in pixels: " << testResults.areaInPixel << " <-> area in mm: " << testResults.areaCalculated << " = " << testResults.percentageAreaCalcToPixel << "%"; MITK_INFO << "calculated diameter: " << testResults.diameterCalculated << " <-> diameter in mm: " << testResults.diameterInMM << " <-> diameter in pixel: " << testResults.diameterInPixel << " = " << testResults.percentageRadiusToPixel << "%"; #endif } /* * get the radius of the slice of a sphere based on pixel distance from edge to edge of the circle. */ template static void TestSphereRadiusByItk (itk::Image* inputImage) { typedef itk::Image InputImageType; //set the index to the middle of the image's edge at x and y axis typename InputImageType::IndexType currentIndexX; currentIndexX[0] = (int)(TestvolumeSize / 2.0); currentIndexX[1] = 0; typename InputImageType::IndexType currentIndexY; currentIndexY[0] = 0; currentIndexY[1] = (int)(TestvolumeSize / 2.0); //remember the last pixel value double lastValueX = inputImage->GetPixel(currentIndexX); double lastValueY = inputImage->GetPixel(currentIndexY); //storage for the index marks std::vector indicesX; std::vector indicesY; /*Get four indices on the edge of the circle*/ while(currentIndexX[1] < TestvolumeSize && currentIndexX[0] < TestvolumeSize) { //move x direction currentIndexX[1] += 1; //move y direction currentIndexY[0] += 1; if(inputImage->GetPixel(currentIndexX) > lastValueX) { //mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexX[0]; markIndex[1] = currentIndexX[1]; indicesX.push_back(markIndex); } else if( inputImage->GetPixel(currentIndexX) < lastValueX) { //mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexX[0]; markIndex[1] = currentIndexX[1] - 1;//value inside the sphere indicesX.push_back(markIndex); } if(inputImage->GetPixel(currentIndexY) > lastValueY) { //mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexY[0]; markIndex[1] = currentIndexY[1]; indicesY.push_back(markIndex); } else if( inputImage->GetPixel(currentIndexY) < lastValueY) { //mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexY[0]; markIndex[1] = currentIndexY[1] - 1;//value inside the sphere indicesY.push_back(markIndex); } //found both marks? if(indicesX.size() == 2 && indicesY.size() == 2) break; //the new 'last' values lastValueX = inputImage->GetPixel(currentIndexX); lastValueY = inputImage->GetPixel(currentIndexY); } /* *If we are here we found the four marks on the edge of the circle. *For the case our plane is rotated and shifted, we have to calculate the center of the circle, *else the center is the intersection of both straight lines between the marks. *When we have the center, the diameter of the circle will be checked to the reference value(math!). */ //each distance from the first mark of each direction to the center of the straight line between the marks double distanceToCenterX = std::abs(indicesX[0][1] - indicesX[1][1]) / 2.0; //double distanceToCenterY = std::abs(indicesY[0][0] - indicesY[1][0]) / 2.0; //the center of the straight lines typename InputImageType::IndexType centerX; //typename InputImageType::IndexType centerY; centerX[0] = indicesX[0][0]; centerX[1] = indicesX[0][1] + distanceToCenterX; //TODO think about implicit cast to int. this is not the real center of the image, which could be between two pixels //centerY[0] = indicesY[0][0] + distanceToCenterY; //centerY[1] = inidcesY[0][1]; typename InputImageType::IndexType currentIndex(centerX); lastValueX = inputImage->GetPixel(currentIndex); long sumpixels = 0; std::vector diameterIndices; //move up while(currentIndex[1] < TestvolumeSize) { currentIndex[1] += 1; if( inputImage->GetPixel(currentIndex) != lastValueX) { typename InputImageType::IndexType markIndex; markIndex[0] = currentIndex[0]; markIndex[1] = currentIndex[1] - 1; diameterIndices.push_back(markIndex); break; } sumpixels++; lastValueX = inputImage->GetPixel(currentIndex); } currentIndex[1] -= sumpixels; //move back to center to go in the other direction lastValueX = inputImage->GetPixel(currentIndex); //move down while(currentIndex[1] >= 0) { currentIndex[1] -= 1; if( inputImage->GetPixel(currentIndex) != lastValueX) { typename InputImageType::IndexType markIndex; markIndex[0] = currentIndex[0]; markIndex[1] = currentIndex[1];//outside sphere because we want to calculate the distance from edge to edge diameterIndices.push_back(markIndex); break; } sumpixels++; lastValueX = inputImage->GetPixel(currentIndex); } /* *Now sumpixels should be the apromximate diameter of the circle. This is checked with the calculated diameter from the plane transformation(math). */ mitk::Point3D volumeCenter; volumeCenter[0] = volumeCenter[1] = volumeCenter[2] = TestvolumeSize / 2.0; double planeDistanceToSphereCenter = TestPlane->Distance(volumeCenter); double sphereRadius = TestvolumeSize/4.0; //calculate the radius of the circle cut from the sphere by the plane double diameter = 2.0 * std::sqrt(std::pow(sphereRadius, 2) - std::pow( planeDistanceToSphereCenter , 2)); double percentageRadiusToPixel = 100 / diameter * sumpixels; /* *calculate the radius in mm by the both marks of the center line by using the world coordinates */ //get the points as 3D coordinates mitk::Vector3D diameterPointRight, diameterPointLeft; diameterPointRight[2] = diameterPointLeft[2] = 0.0; diameterPointLeft[0] = diameterIndices[0][0]; diameterPointLeft[1] = diameterIndices[0][1]; diameterPointRight[0] = diameterIndices[1][0]; diameterPointRight[1] = diameterIndices[1][1]; //transform to worldcoordinates TestVolume->GetGeometry()->IndexToWorld(diameterPointLeft, diameterPointLeft); TestVolume->GetGeometry()->IndexToWorld(diameterPointRight, diameterPointRight); //euklidian distance double diameterInMM = ( (diameterPointLeft * -1.0) + diameterPointRight).GetNorm(); testResults.diameterInMM = diameterInMM; testResults.diameterCalculated = diameter; testResults.diameterInPixel = sumpixels; testResults.percentageRadiusToPixel = percentageRadiusToPixel; testResults.planeDistanceToSphereCenter = planeDistanceToSphereCenter; } /*brute force the area pixel by pixel*/ template static void TestSphereAreaByItk (itk::Image* inputImage) { typedef itk::Image InputImageType; typedef itk::ImageRegionConstIterator< InputImageType > ImageIterator; ImageIterator imageIterator( inputImage, inputImage->GetLargestPossibleRegion() ); imageIterator.GoToBegin(); int sumPixelsInArea = 0; while( !imageIterator.IsAtEnd() ) { if(inputImage->GetPixel(imageIterator.GetIndex()) == pixelValueSet) sumPixelsInArea++; ++imageIterator; } mitk::Point3D volumeCenter; volumeCenter[0] = volumeCenter[1] = volumeCenter[2] = TestvolumeSize / 2.0; double planeDistanceToSphereCenter = TestPlane->Distance(volumeCenter); double sphereRadius = TestvolumeSize/4.0; //calculate the radius of the circle cut from the sphere by the plane double radius = std::sqrt(std::pow(sphereRadius, 2) - std::pow( planeDistanceToSphereCenter , 2)); double areaInMM = 3.14159265358979 * std::pow(radius, 2); testResults.areaCalculated = areaInMM; testResults.areaInPixel = sumPixelsInArea; testResults.percentageAreaCalcToPixel = 100 / areaInMM * sumPixelsInArea; } /* * random a voxel. define plane through this voxel. reslice at the plane. compare the pixel vaues of the voxel * in the volume with the pixel value in the resliced image. * there are some indice shifting problems which causes the test to fail for oblique planes. seems like the chosen * worldcoordinate is not corrresponding to the index in the 2D image. and so the pixel values are not the same as * expected. */ static void PixelvalueBasedTest() { /* setup itk image */ typedef itk::Image ImageType; typedef itk::ImageRegionConstIterator< ImageType > ImageIterator; ImageType::Pointer image = ImageType::New(); ImageType::IndexType start; start[0] = start[1] = start[2] = 0; ImageType::SizeType size; size[0] = size[1] = size[2] = 32; ImageType::RegionType imgRegion; imgRegion.SetSize(size); imgRegion.SetIndex(start); image->SetRegions(imgRegion); image->SetSpacing(1.0); image->Allocate(); ImageIterator imageIterator( image, image->GetLargestPossibleRegion() ); imageIterator.GoToBegin(); unsigned short pixelValue = 0; //fill the image with distinct values while ( !imageIterator.IsAtEnd() ) { image->SetPixel(imageIterator.GetIndex(), pixelValue); ++imageIterator; ++pixelValue; } /* end setup itk image */ mitk::Image::Pointer imageInMitk; CastToMitkImage(image, imageInMitk); /*mitk::ImageWriter::Pointer writer = mitk::ImageWriter::New(); writer->SetInput(imageInMitk); std::string file = "C:\\Users\\schroedt\\Desktop\\cube.nrrd"; writer->SetFileName(file); writer->Update();*/ PixelvalueBasedTestByPlane(imageInMitk, mitk::PlaneGeometry::Frontal); PixelvalueBasedTestByPlane(imageInMitk, mitk::PlaneGeometry::Sagittal); PixelvalueBasedTestByPlane(imageInMitk, mitk::PlaneGeometry::Axial); } static void PixelvalueBasedTestByPlane(mitk::Image* imageInMitk, mitk::PlaneGeometry::PlaneOrientation orientation){ typedef itk::Image ImageType; //set the seed of the rand function srand((unsigned)time(0)); /* setup a random orthogonal plane */ int sliceindex = 17;//rand() % 32; bool isFrontside = true; bool isRotated = false; if( orientation == mitk::PlaneGeometry::Axial) { /*isFrontside = false; isRotated = true;*/ } mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(imageInMitk->GetGeometry(), orientation, sliceindex, isFrontside, isRotated); mitk::Point3D origin = plane->GetOrigin(); mitk::Vector3D normal; normal = plane->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 plane->SetOrigin(origin); //we dont need this any more, because we are only testing orthogonal planes /*mitk::Vector3D rotationVector; rotationVector[0] = randFloat(); rotationVector[1] = randFloat(); rotationVector[2] = randFloat(); float degree = randFloat() * 180.0; mitk::RotationOperation* op = new mitk::RotationOperation(mitk::OpROTATE, plane->GetCenter(), rotationVector, degree); plane->ExecuteOperation(op); delete op;*/ /* end setup plane */ /* define a point in the 3D volume. * add the two axis vectors of the plane (each multiplied with a * random number) to the origin. now the two random numbers * become our index coordinates in the 2D image, because the * length of the axis vectors is 1. */ mitk::Point3D planeOrigin = plane->GetOrigin(); mitk::Vector3D axis0, axis1; axis0 = plane->GetAxisVector(0); axis1 = plane->GetAxisVector(1); axis0.Normalize(); axis1.Normalize(); unsigned char n1 = 7;// rand() % 32; unsigned char n2 = 13;// rand() % 32; mitk::Point3D testPoint3DInWorld; testPoint3DInWorld = planeOrigin + (axis0 * n1) + (axis1 * n2); //get the index of the point in the 3D volume ImageType::IndexType testPoint3DInIndex; imageInMitk->GetGeometry()->WorldToIndex(testPoint3DInWorld, testPoint3DInIndex); - mitk::Index3D testPoint2DInIndex; + itk::Index<3> testPoint2DInIndex; /* end define a point in the 3D volume.*/ //do reslicing at the plane mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(); slicer->SetInput(imageInMitk); slicer->SetWorldGeometry(plane); slicer->Update(); mitk::Image::Pointer slice = slicer->GetOutput(); // Get TestPoiont3D as Index in Slice slice->GetGeometry()->WorldToIndex(testPoint3DInWorld,testPoint2DInIndex); mitk::Point3D p, sliceIndexToWorld, imageIndexToWorld; p[0] = testPoint2DInIndex[0]; p[1] = testPoint2DInIndex[1]; p[2] = testPoint2DInIndex[2]; slice->GetGeometry()->IndexToWorld(p, sliceIndexToWorld); p[0] = testPoint3DInIndex[0]; p[1] = testPoint3DInIndex[1]; p[2] = testPoint3DInIndex[2]; imageInMitk->GetGeometry()->IndexToWorld(p, imageIndexToWorld); itk::Index<2> testPoint2DIn2DIndex; testPoint2DIn2DIndex[0] = testPoint2DInIndex[0]; testPoint2DIn2DIndex[1] = testPoint2DInIndex[1]; typedef mitk::ImagePixelReadAccessor< unsigned short, 3 > VolumeReadAccessorType; typedef mitk::ImagePixelReadAccessor< unsigned short, 2 > SliceReadAccessorType; VolumeReadAccessorType VolumeReadAccessor( imageInMitk ); SliceReadAccessorType SliceReadAccessor( slice ); //compare the pixelvalues of the defined point in the 3D volume with the value of the resliced image unsigned short valueAt3DVolume = VolumeReadAccessor.GetPixelByIndex( testPoint3DInIndex ); unsigned short valueAtSlice = SliceReadAccessor.GetPixelByIndex( testPoint2DIn2DIndex ); //valueAt3DVolume == valueAtSlice is not always working. because of rounding errors //indices are shifted MITK_TEST_CONDITION(valueAt3DVolume == valueAtSlice, "comparing pixelvalues for orthogonal plane"); vtkSmartPointer imageInVtk = vtkSmartPointer::New(); imageInVtk = imageInMitk->GetVtkImageData(); vtkSmartPointer sliceInVtk = vtkSmartPointer::New(); sliceInVtk = slice->GetVtkImageData(); double PixelvalueByMitkOutput = sliceInVtk->GetScalarComponentAsDouble(n1, n2, 0, 0); //double valueVTKinImage = imageInVtk->GetScalarComponentAsDouble(testPoint3DInIndex[0], testPoint3DInIndex[1], testPoint3DInIndex[2], 0); /* Test that everything is working equally if vtkoutput is used instead of the default output * from mitk ImageToImageFilter */ mitk::ExtractSliceFilter::Pointer slicerWithVtkOutput = mitk::ExtractSliceFilter::New(); slicerWithVtkOutput->SetInput(imageInMitk); slicerWithVtkOutput->SetWorldGeometry(plane); slicerWithVtkOutput->SetVtkOutputRequest(true); slicerWithVtkOutput->Update(); vtkSmartPointer vtkImageByVtkOutput = vtkSmartPointer::New(); vtkImageByVtkOutput = slicerWithVtkOutput->GetVtkOutput(); double PixelvalueByVtkOutput = vtkImageByVtkOutput->GetScalarComponentAsDouble(n1, n2, 0, 0); MITK_TEST_CONDITION(PixelvalueByMitkOutput == PixelvalueByVtkOutput, "testing convertion of image output vtk->mitk by reslicer"); /*================ mbilog outputs ===========================*/ #ifdef EXTRACTOR_DEBUG MITK_INFO << "\n" << "TESTINFO index: " << sliceindex << " orientation: " << orientation << " frontside: " << isFrontside << " rotated: " << isRotated; MITK_INFO << "\n" << "slice index to world: " << sliceIndexToWorld; MITK_INFO << "\n" << "image index to world: " << imageIndexToWorld; MITK_INFO << "\n" << "vtk: slice: " << PixelvalueByMitkOutput << ", image: "<< valueVTKinImage; MITK_INFO << "\n" << "testPoint3D InWorld" << testPoint3DInWorld << " is " << testPoint2DInIndex << " in 2D"; MITK_INFO << "\n" << "randoms: " << ((int)n1) << ", " << ((int)n2); MITK_INFO << "\n" << "point is inside plane: " << plane->IsInside(testPoint3DInWorld) << " and volume: " << imageInMitk->GetGeometry()->IsInside(testPoint3DInWorld); MITK_INFO << "\n" << "volume idx: " << testPoint3DInIndex << " = " << valueAt3DVolume ; MITK_INFO << "\n" << "volume world: " << testPoint3DInWorld << " = " << valueAt3DVolumeByWorld ; MITK_INFO << "\n" << "slice idx: " << testPoint2DInIndex << " = " << valueAtSlice ; - mitk::Index3D curr; + itk::Index<3> curr; curr[0] = curr[1] = curr[2] = 0; for( int i = 0; i < 32 ; ++i){ for( int j = 0; j < 32; ++j){ ++curr[1]; if(SliceReadAccessor.GetPixelByIndex( curr ) == valueAt3DVolume){ MITK_INFO << "\n" << valueAt3DVolume << " MATCHED mitk " << curr; } } curr[1] = 0; ++curr[0]; } typedef itk::Image Image2DType; Image2DType::Pointer img = Image2DType::New(); CastToItkImage(slice, img); typedef itk::ImageRegionConstIterator< Image2DType > Iterator2D; Iterator2D iter(img, img->GetLargestPossibleRegion()); iter.GoToBegin(); while( !iter.IsAtEnd() ){ if(img->GetPixel(iter.GetIndex()) == valueAt3DVolume) MITK_INFO << "\n" << valueAt3DVolume << " MATCHED itk " << iter.GetIndex(); ++iter; } #endif //EXTRACTOR_DEBUG } /* random a float value */ static float randFloat(){ return (((float)rand()+1.0) / ((float)RAND_MAX + 1.0)) + (((float)rand()+1.0) / ((float)RAND_MAX + 1.0)) / ((float)RAND_MAX + 1.0);} /* create a sphere with the size of the given testVolumeSize*/ static void InitializeTestVolume() { #ifdef CREATE_VOLUME //do sphere creation ItkVolumeGeneration(); #ifdef SAVE_VOLUME //save in file mitk::ImageWriter::Pointer writer = mitk::ImageWriter::New(); writer->SetInput(TestVolume); std::string file; std::ostringstream filename; filename << "C:\\home\\schroedt\\MITK\\Modules\\ImageExtraction\\Testing\\Data\\sphere_"; filename << TestvolumeSize; filename << ".nrrd"; file = filename.str(); writer->SetFileName(file); writer->Update(); #endif//SAVE_VOLUME #endif #ifndef CREATE_VOLUME //read from file mitk::StandardFileLocations::Pointer locator = mitk::StandardFileLocations::GetInstance(); std::string filename = locator->FindFile("sphere_512.nrrd.mhd", "Modules/ImageExtraction/Testing/Data"); mitk::ItkImageFileReader::Pointer reader = mitk::ItkImageFileReader::New(); reader->SetFileName(filename); reader->Update(); TestVolume = reader->GetOutput(); #endif #ifdef CALC_TESTFAILURE_DEVIATION //get the TestFailureDeviation in % AccessFixedDimensionByItk(TestVolume, CalcTestFailureDeviation, 3); #endif } //the test result of the sphere reslice struct SliceProperties{ double planeDistanceToSphereCenter; double diameterInMM; double diameterInPixel; double diameterCalculated; double percentageRadiusToPixel; double areaCalculated; double areaInPixel; double percentageAreaCalcToPixel; }; static mitk::Image::Pointer TestVolume; static double TestvolumeSize; static mitk::PlaneGeometry::Pointer TestPlane; static std::string TestName; static unsigned char pixelValueSet; static SliceProperties testResults; static double TestFailureDeviation; private: /* * Generate a sphere with a radius of TestvolumeSize / 4.0 */ static void ItkVolumeGeneration () { typedef itk::Image TestVolumeType; typedef itk::ImageRegionConstIterator< TestVolumeType > ImageIterator; TestVolumeType::Pointer sphereImage = TestVolumeType::New(); TestVolumeType::IndexType start; start[0] = start[1] = start[2] = 0; TestVolumeType::SizeType size; size[0] = size[1] = size[2] = TestvolumeSize; TestVolumeType::RegionType imgRegion; imgRegion.SetSize(size); imgRegion.SetIndex(start); sphereImage->SetRegions(imgRegion); sphereImage->SetSpacing(1.0); sphereImage->Allocate(); sphereImage->FillBuffer(0); mitk::Vector3D center; center[0] = center[1] = center[2] = TestvolumeSize / 2.0; double radius = TestvolumeSize / 4.0; double pixelValue = pixelValueSet; double distanceToCenter = 0.0; ImageIterator imageIterator( sphereImage, sphereImage->GetLargestPossibleRegion() ); imageIterator.GoToBegin(); mitk::Vector3D currentVoxelInIndex; while ( !imageIterator.IsAtEnd() ) { currentVoxelInIndex[0] = imageIterator.GetIndex()[0]; currentVoxelInIndex[1] = imageIterator.GetIndex()[1]; currentVoxelInIndex[2] = imageIterator.GetIndex()[2]; distanceToCenter = (center + ( currentVoxelInIndex * -1.0 )).GetNorm(); //if distance to center is smaller then the radius of the sphere if( distanceToCenter < radius) { sphereImage->SetPixel(imageIterator.GetIndex(), pixelValue); } ++imageIterator; } CastToMitkImage(sphereImage, TestVolume); } /* calculate the devation of the voxel object to the mathematical sphere object. * this is use to make a statement about the accuracy of the resliced image, eg. the circle's diameter or area. */ template static void CalcTestFailureDeviation (itk::Image* inputImage) { typedef itk::Image InputImageType; typedef itk::ImageRegionConstIterator< InputImageType > ImageIterator; ImageIterator iterator(inputImage, inputImage->GetLargestPossibleRegion()); iterator.GoToBegin(); int volumeInPixel = 0; while( !iterator.IsAtEnd() ) { if(inputImage->GetPixel(iterator.GetIndex()) == pixelValueSet) volumeInPixel++; ++iterator; } double diameter = TestvolumeSize / 2.0; double volumeCalculated = (1.0 / 6.0) * 3.14159265358979 * std::pow(diameter, 3); double volumeDeviation = std::abs( 100 - (100 / volumeCalculated * volumeInPixel) ); typename InputImageType::IndexType index; index[0] = index[1] = TestvolumeSize / 2.0; index[2] = 0; int sumpixels = 0; while (index[2] < TestvolumeSize ) { if(inputImage->GetPixel(index) == pixelValueSet) sumpixels++; index[2] += 1; } double diameterDeviation = std::abs( 100 - (100 / diameter * sumpixels) ); #ifdef DEBUG MITK_INFO << "volume deviation: " << volumeDeviation << " diameter deviation:" << diameterDeviation; #endif mitkExtractSliceFilterTestClass::TestFailureDeviation = (volumeDeviation + diameterDeviation) / 2.0; } }; /*================ #END class ================*/ /*================#BEGIN Instanciation of members ================*/ mitk::Image::Pointer mitkExtractSliceFilterTestClass::TestVolume = mitk::Image::New(); double mitkExtractSliceFilterTestClass::TestvolumeSize = 256.0; mitk::PlaneGeometry::Pointer mitkExtractSliceFilterTestClass::TestPlane = mitk::PlaneGeometry::New(); std::string mitkExtractSliceFilterTestClass::TestName = ""; unsigned char mitkExtractSliceFilterTestClass::pixelValueSet = 255; mitkExtractSliceFilterTestClass::SliceProperties mitkExtractSliceFilterTestClass::testResults = {-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0}; double mitkExtractSliceFilterTestClass::TestFailureDeviation = 0.0; /*================ #END Instanciation of members ================*/ /*================ #BEGIN test main ================*/ int mitkExtractSliceFilterTest(int argc, char* argv[]) { MITK_TEST_BEGIN("mitkExtractSliceFilterTest") //pixelvalue based testing mitkExtractSliceFilterTestClass::PixelvalueBasedTest(); //initialize sphere test volume mitkExtractSliceFilterTestClass::InitializeTestVolume(); mitk::Vector3D spacing = mitkExtractSliceFilterTestClass::TestVolume->GetGeometry()->GetSpacing(); //the center of the sphere = center of image double sphereCenter = mitkExtractSliceFilterTestClass::TestvolumeSize / 2.0; double planeSize = mitkExtractSliceFilterTestClass::TestvolumeSize; /* axial plane */ mitk::PlaneGeometry::Pointer geometryAxial = mitk::PlaneGeometry::New(); geometryAxial->InitializeStandardPlane(planeSize, planeSize, spacing, mitk::PlaneGeometry::Axial, sphereCenter, false, true); geometryAxial->ChangeImageGeometryConsideringOriginOffset(true); mitk::Point3D origin = geometryAxial->GetOrigin(); mitk::Vector3D normal; normal = geometryAxial->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 //geometryAxial->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometryAxial, "Testing axial plane"); /* end axial plane */ /* sagittal plane */ mitk::PlaneGeometry::Pointer geometrySagital = mitk::PlaneGeometry::New(); geometrySagital->InitializeStandardPlane(planeSize, planeSize, spacing, mitk::PlaneGeometry::Sagittal, sphereCenter, true, false); geometrySagital->ChangeImageGeometryConsideringOriginOffset(true); origin = geometrySagital->GetOrigin(); normal = geometrySagital->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 //geometrySagital->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometrySagital, "Testing sagittal plane"); /* sagittal plane */ /* sagittal shifted plane */ mitk::PlaneGeometry::Pointer geometrySagitalShifted = mitk::PlaneGeometry::New(); geometrySagitalShifted->InitializeStandardPlane(planeSize, planeSize, spacing, mitk::PlaneGeometry::Sagittal, (sphereCenter - 14), true, false); geometrySagitalShifted->ChangeImageGeometryConsideringOriginOffset(true); origin = geometrySagitalShifted->GetOrigin(); normal = geometrySagitalShifted->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 //geometrySagitalShifted->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometrySagitalShifted, "Testing sagittal plane shifted"); /* end sagittal shifted plane */ /* coronal plane */ mitk::PlaneGeometry::Pointer geometryCoronal = mitk::PlaneGeometry::New(); geometryCoronal->InitializeStandardPlane(planeSize, planeSize, spacing, mitk::PlaneGeometry::Frontal, sphereCenter, true, false); geometryCoronal->ChangeImageGeometryConsideringOriginOffset(true); origin = geometryCoronal->GetOrigin(); normal = geometryCoronal->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 //geometryCoronal->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometryCoronal, "Testing coronal plane"); /* end coronal plane */ /* oblique plane */ mitk::PlaneGeometry::Pointer obliquePlane = mitk::PlaneGeometry::New(); obliquePlane->InitializeStandardPlane(planeSize, planeSize, spacing, mitk::PlaneGeometry::Sagittal, sphereCenter, true, false); obliquePlane->ChangeImageGeometryConsideringOriginOffset(true); origin = obliquePlane->GetOrigin(); normal = obliquePlane->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 //obliquePlane->SetOrigin(origin); mitk::Vector3D rotationVector; rotationVector[0] = 0.2; rotationVector[1] = 0.4; rotationVector[2] = 0.62; float degree = 37.0; mitk::RotationOperation* op = new mitk::RotationOperation(mitk::OpROTATE, obliquePlane->GetCenter(), rotationVector, degree); obliquePlane->ExecuteOperation(op); delete op; mitkExtractSliceFilterTestClass::TestSlice(obliquePlane, "Testing oblique plane"); /* end oblique plane */ #ifdef SHOW_SLICE_IN_RENDER_WINDOW /*================ #BEGIN vtk render code ================*/ //set reslicer for renderwindow mitk::ItkImageFileReader::Pointer reader = mitk::ItkImageFileReader::New(); std::string filename = "C:\\home\\Pics\\Pic3D.nrrd"; reader->SetFileName(filename); reader->Update(); mitk::Image::Pointer pic = reader->GetOutput(); vtkSmartPointer slicer = vtkSmartPointer::New(); slicer->SetInput(pic->GetVtkImageData()); mitk::PlaneGeometry::Pointer obliquePl = mitk::PlaneGeometry::New(); obliquePl->InitializeStandardPlane(pic->GetGeometry(), mitk::PlaneGeometry::Sagittal, pic->GetGeometry()->GetCenter()[0], true, false); obliquePl->ChangeImageGeometryConsideringOriginOffset(true); mitk::Point3D origin2 = obliquePl->GetOrigin(); mitk::Vector3D n; n = obliquePl->GetNormal(); n.Normalize(); origin2 += n * 0.5;//pixelspacing is 1, so half the spacing is 0.5 obliquePl->SetOrigin(origin2); mitk::Vector3D rotation; rotation[0] = 0.534307; rotation[1] = 0.000439605; rotation[2] = 0.423017; MITK_INFO << rotation; float rotateDegree = 70; mitk::RotationOperation* operation = new mitk::RotationOperation(mitk::OpROTATE, obliquePl->GetCenter(), rotationVector, degree); obliquePl->ExecuteOperation(operation); delete operation; double origin[3]; origin[0] = obliquePl->GetOrigin()[0]; origin[1] = obliquePl->GetOrigin()[1]; origin[2] = obliquePl->GetOrigin()[2]; slicer->SetResliceAxesOrigin(origin); mitk::Vector3D right, bottom, normal; right = obliquePl->GetAxisVector( 0 ); bottom = obliquePl->GetAxisVector( 1 ); normal = obliquePl->GetNormal(); right.Normalize(); bottom.Normalize(); normal.Normalize(); double cosines[9]; mitk::vnl2vtk(right.GetVnlVector(), cosines);//x mitk::vnl2vtk(bottom.GetVnlVector(), cosines + 3);//y mitk::vnl2vtk(normal.GetVnlVector(), cosines + 6);//n slicer->SetResliceAxesDirectionCosines(cosines); slicer->SetOutputDimensionality(2); slicer->Update(); //set vtk renderwindow vtkSmartPointer vtkPlane = vtkSmartPointer::New(); vtkPlane->SetOrigin(0.0, 0.0, 0.0); //These two points define the axes of the plane in combination with the origin. //Point 1 is the x-axis and point 2 the y-axis. //Each plane is transformed according to the view (axial, coronal and saggital) afterwards. vtkPlane->SetPoint1(1.0, 0.0, 0.0); //P1: (xMax, yMin, depth) vtkPlane->SetPoint2(0.0, 1.0, 0.0); //P2: (xMin, yMax, depth) //these are not the correct values for all slices, only a square plane by now vtkSmartPointer imageMapper = vtkSmartPointer::New(); imageMapper->SetInputConnection(vtkPlane->GetOutputPort()); vtkSmartPointer lookupTable = vtkSmartPointer::New(); //built a default lookuptable lookupTable->SetRampToLinear(); lookupTable->SetSaturationRange( 0.0, 0.0 ); lookupTable->SetHueRange( 0.0, 0.0 ); lookupTable->SetValueRange( 0.0, 1.0 ); lookupTable->Build(); //map all black values to transparent lookupTable->SetTableValue(0, 0.0, 0.0, 0.0, 0.0); lookupTable->SetRange(-255.0, 255.0); //lookupTable->SetRange(-1022.0, 1184.0);//pic3D range vtkSmartPointer texture = vtkSmartPointer::New(); texture->SetInput(slicer->GetOutput()); texture->SetLookupTable(lookupTable); texture->SetMapColorScalarsThroughLookupTable(true); vtkSmartPointer imageActor = vtkSmartPointer::New(); imageActor->SetMapper(imageMapper); imageActor->SetTexture(texture); // Setup renderers vtkSmartPointer renderer = vtkSmartPointer::New(); renderer->AddActor(imageActor); // Setup render window vtkSmartPointer renderWindow = vtkSmartPointer::New(); renderWindow->AddRenderer(renderer); // Setup render window interactor vtkSmartPointer renderWindowInteractor = vtkSmartPointer::New(); vtkSmartPointer style = vtkSmartPointer::New(); renderWindowInteractor->SetInteractorStyle(style); // Render and start interaction renderWindowInteractor->SetRenderWindow(renderWindow); //renderer->AddViewProp(imageActor); renderWindow->Render(); renderWindowInteractor->Start(); // always end with this! /*================ #END vtk render code ================*/ #endif //SHOW_SLICE_IN_RENDER_WINDOW MITK_TEST_END() } diff --git a/Core/Code/Testing/mitkGenericPropertyTest.cpp b/Core/Code/Testing/mitkGenericPropertyTest.cpp index 67da3680ca..0897dd81a0 100644 --- a/Core/Code/Testing/mitkGenericPropertyTest.cpp +++ b/Core/Code/Testing/mitkGenericPropertyTest.cpp @@ -1,109 +1,109 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include "mitkGenericProperty.h" #include "mitkStringProperty.h" #include "mitkProperties.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include #include // call with testValue1 != testValue2 template int TestGenericPropertyForDataType(typename T::ValueType testValue1, typename T::ValueType testValue2, std::string testValue1AsString, std::string testValue2AsString, std::string type) { std::cout << "Testing mitk::GenericProperty<" << type << ">(" << testValue1AsString << ", " << testValue2AsString << ") \n"; typename T::Pointer prop(T::New()); typename T::Pointer prop2(T::New(testValue1)); typename T::Pointer prop3(T::New(testValue2)); unsigned long tBefore = prop->GetMTime(); prop->SetValue(testValue1); unsigned long tAfter = prop->GetMTime(); prop->SetValue(testValue1); unsigned long tAfterAll = prop->GetMTime(); MITK_TEST_CONDITION_REQUIRED(prop->GetValue() == testValue1 && prop->GetValueAsString() == testValue1AsString,"Testing SetValue") MITK_TEST_CONDITION_REQUIRED((*prop == *prop2),"Testing equality operator (operator==)"); prop->SetValue(testValue2); unsigned long tAfterEverything = prop->GetMTime(); std::cout << " Testing MTime correctness when changing property value: "; if (tBefore >= tAfter || tAfterAll != tAfter || tAfterEverything <= tAfterAll) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; prop->SetValue(testValue1); std::cout << " Testing Assignment: "; prop->AssignProperty(*prop3); if ( (! (*prop == *prop3)) || (*prop == *prop2) ) { std::cout << " [FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << std::endl; return EXIT_SUCCESS; } int mitkGenericPropertyTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN(GenericPropertyTest) // testing for some different data types TestGenericPropertyForDataType(1, 2, "1", "2", "int"); TestGenericPropertyForDataType(true, false, "1", "0", "bool"); TestGenericPropertyForDataType(1.0, -1.0, "1", "-1", "float"); TestGenericPropertyForDataType(1.0, -1.0, "1", "-1", "double"); TestGenericPropertyForDataType(std::string("eins"), std::string("zwei"), std::string("eins"), std::string("zwei"), "std::string"); { mitk::Point3D p1; p1[0] = 2.0; p1[1] = 3.0; p1[2] = 4.0; mitk::Point3D p2; p2[0] =-1.0; p2[1] = 2.0; p2[2] = 3.0; TestGenericPropertyForDataType( p1, p2, "[2, 3, 4]", "[-1, 2, 3]", "mitk::Point3D"); } { mitk::Point4D p1; p1[0] = 2.0; p1[1] = 3.0; p1[2] = 4.0; p1[3] =-2.0; mitk::Point4D p2; p2[0] =-1.0; p2[1] = 2.0; p2[2] = 3.0; p2[3] = 5.0; TestGenericPropertyForDataType( p1, p2, "[2, 3, 4, -2]", "[-1, 2, 3, 5]", "mitk::Point4D"); } /* THIS won't compile because of the interface of XMLWriter... that should be reworked perhaps { mitk::Vector2D p1; p1[0] = 2.0; p1[1] = 3.0; mitk::Vector2D p2; p2[0] =-1.0; p2[1] = 2.0; TestGenericPropertyForDataType( p1, p2, "[2, 3]", "[-1, 2]", "mitk::Vector2D"); } */ { mitk::Vector3D p1; p1[0] = 2.0; p1[1] = 3.0; p1[2] = 4.0; mitk::Vector3D p2; p2[0] =-1.0; p2[1] = 2.0; p2[2] = 3.0; TestGenericPropertyForDataType( p1, p2, "[2, 3, 4]", "[-1, 2, 3]", "mitk::Vector3D"); } MITK_TEST_END(); } diff --git a/Core/Code/Testing/mitkGeometry3DTest.cpp b/Core/Code/Testing/mitkGeometry3DTest.cpp index 486683b9cd..eb78e14768 100644 --- a/Core/Code/Testing/mitkGeometry3DTest.cpp +++ b/Core/Code/Testing/mitkGeometry3DTest.cpp @@ -1,614 +1,614 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkGeometry3D.h" #include #include #include "mitkRotationOperation.h" #include "mitkInteractionConst.h" #include #include #include "mitkTestingMacros.h" #include -#include +#include bool testGetAxisVectorVariants(mitk::Geometry3D* geometry) { int direction; for(direction=0; direction<3; ++direction) { mitk::Vector3D frontToBack; switch(direction) { case 0: frontToBack = geometry->GetCornerPoint(false, false, false)-geometry->GetCornerPoint(true , false, false); break; //7-3 case 1: frontToBack = geometry->GetCornerPoint(false, false, false)-geometry->GetCornerPoint(false, true , false); break; //7-5 case 2: frontToBack = geometry->GetCornerPoint(false, false, false)-geometry->GetCornerPoint(false , false, true); break; //7-2 } std::cout << "Testing GetAxisVector(int) vs GetAxisVector(bool, bool, bool): "; if(mitk::Equal(geometry->GetAxisVector(direction), frontToBack) == false) { std::cout<<"[FAILED]"<GetAxisVector(direction).GetNorm(), geometry->GetExtentInMM(direction)) == false) { std::cout<<"[FAILED]"<GetOrigin(); mitk::Point3D dummy; MITK_TEST_OUTPUT( << " Testing index->world->index conversion consistency"); geometry3d->WorldToIndex(origin, dummy); geometry3d->IndexToWorld(dummy, dummy); MITK_TEST_CONDITION_REQUIRED(dummy == origin, ""); MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin, mitk::Point3D)==(0,0,0)"); mitk::Point3D globalOrigin; mitk::FillVector3D(globalOrigin, 0,0,0); mitk::Point3D originContinuousIndex; geometry3d->WorldToIndex(origin, originContinuousIndex); MITK_TEST_CONDITION_REQUIRED(originContinuousIndex == globalOrigin, ""); MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin, itk::Index)==(0,0,0)"); itk::Index<3> itkindex; geometry3d->WorldToIndex(origin, itkindex); itk::Index<3> globalOriginIndex; mitk::vtk2itk(globalOrigin, globalOriginIndex); MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin-0.5*spacing, itk::Index)==(0,0,0)"); mitk::Vector3D halfSpacingStep = geometry3d->GetSpacing()*0.5; mitk::Matrix3D rotation; mitk::Point3D originOffCenter = origin-halfSpacingStep; geometry3d->WorldToIndex(originOffCenter, itkindex); MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0)"); originOffCenter = origin+halfSpacingStep; originOffCenter -= 0.0001; geometry3d->WorldToIndex( originOffCenter, itkindex); MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); originOffCenter = origin+halfSpacingStep; itk::Index<3> global111; mitk::FillVector3D(global111, 1,1,1); geometry3d->WorldToIndex( originOffCenter, itkindex); MITK_TEST_CONDITION_REQUIRED(itkindex == global111, ""); MITK_TEST_OUTPUT( << " Testing WorldToIndex(GetCenter())==BoundingBox.GetCenter: "); mitk::Point3D center = geometry3d->GetCenter(); mitk::Point3D centerContIndex; geometry3d->WorldToIndex(center, centerContIndex); mitk::BoundingBox::ConstPointer boundingBox = geometry3d->GetBoundingBox(); mitk::BoundingBox::PointType centerBounds = boundingBox->GetCenter(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(centerContIndex,centerBounds), ""); MITK_TEST_OUTPUT( << " Testing GetCenter()==IndexToWorld(BoundingBox.GetCenter): "); center = geometry3d->GetCenter(); mitk::Point3D centerBoundsInWorldCoords; geometry3d->IndexToWorld(centerBounds, centerBoundsInWorldCoords); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(center,centerBoundsInWorldCoords), ""); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForVectors(mitk::Geometry3D* geometry3d) { MITK_TEST_OUTPUT( << "Testing consistency of index and world coordinate systems for vectors: "); mitk::Vector3D xAxisMM = geometry3d->GetAxisVector(0); mitk::Vector3D xAxisContinuousIndex; mitk::Vector3D xAxisContinuousIndexDeprecated; mitk::Point3D p, pIndex, origin; origin = geometry3d->GetOrigin(); p[0] = xAxisMM[0]; p[1] = xAxisMM[1]; p[2] = xAxisMM[2]; geometry3d->WorldToIndex(p,pIndex); geometry3d->WorldToIndex(xAxisMM, xAxisContinuousIndexDeprecated); geometry3d->WorldToIndex(xAxisMM,xAxisContinuousIndex); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[0] == pIndex[0],""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[1] == pIndex[1],""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[2] == pIndex[2],""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[0] == xAxisContinuousIndexDeprecated[0],""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[1] == xAxisContinuousIndexDeprecated[1],""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[2] == xAxisContinuousIndexDeprecated[2],""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[0] == pIndex[0],""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[1] == pIndex[1],""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[2] == pIndex[2],""); geometry3d->IndexToWorld(xAxisContinuousIndex,xAxisContinuousIndex); geometry3d->IndexToWorld(xAxisContinuousIndexDeprecated,xAxisContinuousIndexDeprecated); geometry3d->IndexToWorld(pIndex,p); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex == xAxisMM,""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[0] == p[0],""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[1] == p[1],""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[2] == p[2],""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated == xAxisMM,""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[0] == p[0],""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[1] == p[1],""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[2] == p[2],""); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForIndex(mitk::Geometry3D* geometry3d) { MITK_TEST_OUTPUT( << "Testing consistency of index and world coordinate systems: "); // creating testing data itk::Index<4> itkIndex4, itkIndex4b; itk::Index<3> itkIndex3, itkIndex3b; itk::Index<2> itkIndex2, itkIndex2b; - mitk::Index3D mitkIndex, mitkIndexb; + itk::Index<3> mitkIndex, mitkIndexb; itkIndex4[0] = itkIndex4[1] = itkIndex4[2] = itkIndex4[3] = 4; itkIndex3[0] = itkIndex3[1] = itkIndex3[2] = 6; itkIndex2[0] = itkIndex2[1] = 2; mitkIndex[0] = mitkIndex[1] = mitkIndex[2] = 13; // check for constistency mitk::Point3D point; geometry3d->IndexToWorld(itkIndex2,point); geometry3d->WorldToIndex(point,itkIndex2b); MITK_TEST_CONDITION_REQUIRED( ((itkIndex2b[0] == itkIndex2[0]) && (itkIndex2b[1] == itkIndex2[1])), "Testing itk::index<2> for IndexToWorld/WorldToIndex consistency"); geometry3d->IndexToWorld(itkIndex3,point); geometry3d->WorldToIndex(point,itkIndex3b); MITK_TEST_CONDITION_REQUIRED( ((itkIndex3b[0] == itkIndex3[0]) && (itkIndex3b[1] == itkIndex3[1]) && (itkIndex3b[2] == itkIndex3[2])), "Testing itk::index<3> for IndexToWorld/WorldToIndex consistency"); geometry3d->IndexToWorld(itkIndex4,point); geometry3d->WorldToIndex(point,itkIndex4b); MITK_TEST_CONDITION_REQUIRED( ((itkIndex4b[0] == itkIndex4[0]) && (itkIndex4b[1] == itkIndex4[1]) && (itkIndex4b[2] == itkIndex4[2]) && (itkIndex4b[3] == 0)), "Testing itk::index<3> for IndexToWorld/WorldToIndex consistency"); geometry3d->IndexToWorld(mitkIndex,point); geometry3d->WorldToIndex(point,mitkIndexb); MITK_TEST_CONDITION_REQUIRED( ((mitkIndexb[0] == mitkIndex[0]) && (mitkIndexb[1] == mitkIndex[1]) && (mitkIndexb[2] == mitkIndex[2])), "Testing mitk::Index for IndexToWorld/WorldToIndex consistency"); return EXIT_SUCCESS; } #include int testItkImageIsCenterBased() { MITK_TEST_OUTPUT(<< "Testing whether itk::Image coordinates are center-based."); typedef itk::Image ItkIntImage3D; ItkIntImage3D::Pointer itkintimage = ItkIntImage3D::New(); ItkIntImage3D::SizeType size; size.Fill(10); mitk::Point3D origin; mitk::FillVector3D(origin, 2,3,7); itkintimage->Initialize(); itkintimage->SetRegions(size); itkintimage->SetOrigin(origin); std::cout<<"[PASSED]"< originContinuousIndex; itkintimage->TransformPhysicalPointToContinuousIndex(origin, originContinuousIndex); MITK_TEST_CONDITION_REQUIRED(originContinuousIndex == globalOrigin, ""); MITK_TEST_OUTPUT( << " Testing itk::Image::TransformPhysicalPointToIndex(origin)==(0,0,0)"); itk::Index<3> itkindex; itkintimage->TransformPhysicalPointToIndex(origin, itkindex); itk::Index<3> globalOriginIndex; mitk::vtk2itk(globalOrigin, globalOriginIndex); MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); MITK_TEST_OUTPUT( << " Testing itk::Image::TransformPhysicalPointToIndex(origin-0.5*spacing)==(0,0,0)"); mitk::Vector3D halfSpacingStep = itkintimage->GetSpacing()*0.5; mitk::Matrix3D rotation; mitk::Point3D originOffCenter = origin-halfSpacingStep; itkintimage->TransformPhysicalPointToIndex(originOffCenter, itkindex); MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); MITK_TEST_OUTPUT( << " Testing itk::Image::TransformPhysicalPointToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0)"); originOffCenter = origin+halfSpacingStep; originOffCenter -= 0.0001; itkintimage->TransformPhysicalPointToIndex( originOffCenter, itkindex); MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); MITK_TEST_OUTPUT( << " Testing itk::Image::TransformPhysicalPointToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); originOffCenter = origin+halfSpacingStep; itk::Index<3> global111; mitk::FillVector3D(global111, 1,1,1); itkintimage->TransformPhysicalPointToIndex( originOffCenter, itkindex); MITK_TEST_CONDITION_REQUIRED(itkindex == global111, ""); MITK_TEST_OUTPUT( << "=> Yes, itk::Image coordinates are center-based."); return EXIT_SUCCESS; } int testGeometry3D(bool imageGeometry) { // Build up a new image Geometry mitk::Geometry3D::Pointer geometry3d = mitk::Geometry3D::New(); float bounds[ ] = {-10.0, 17.0, -12.0, 188.0, 13.0, 211.0}; MITK_TEST_OUTPUT( << "Initializing"); geometry3d->Initialize(); MITK_TEST_OUTPUT(<< "Setting ImageGeometry to " << imageGeometry); geometry3d->SetImageGeometry(imageGeometry); MITK_TEST_OUTPUT(<< "Setting bounds by SetFloatBounds(): " << bounds); geometry3d->SetFloatBounds(bounds); MITK_TEST_OUTPUT( << "Testing AxisVectors"); if(testGetAxisVectorVariants(geometry3d) == false) return EXIT_FAILURE; if(testGetAxisVectorExtent(geometry3d) == false) return EXIT_FAILURE; MITK_TEST_OUTPUT( << "Creating an AffineTransform3D transform"); mitk::AffineTransform3D::MatrixType matrix; matrix.SetIdentity(); matrix(1,1) = 2; mitk::AffineTransform3D::Pointer transform; transform = mitk::AffineTransform3D::New(); transform->SetMatrix(matrix); MITK_TEST_OUTPUT( << "Testing a SetIndexToWorldTransform"); geometry3d->SetIndexToWorldTransform(transform); MITK_TEST_OUTPUT( << "Testing correctness of value returned by GetSpacing"); const mitk::Vector3D& spacing1 = geometry3d->GetSpacing(); mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 2; if( mitk::Equal(spacing1, expectedSpacing) == false ) { MITK_TEST_OUTPUT( << " [FAILED]"); return EXIT_FAILURE; } MITK_TEST_OUTPUT( << "Testing a Compose(transform)"); geometry3d->Compose(transform); MITK_TEST_OUTPUT( << "Testing correctness of value returned by GetSpacing"); const mitk::Vector3D& spacing2 = geometry3d->GetSpacing(); expectedSpacing[1] = 4; if( mitk::Equal(spacing2, expectedSpacing) == false ) { MITK_TEST_OUTPUT( << " [FAILED]"); return EXIT_FAILURE; } MITK_TEST_OUTPUT( << "Testing correctness of SetSpacing"); mitk::Vector3D newspacing; mitk::FillVector3D(newspacing, 1.5, 2.5, 3.5); geometry3d->SetSpacing(newspacing); const mitk::Vector3D& spacing3 = geometry3d->GetSpacing(); if( mitk::Equal(spacing3, newspacing) == false ) { MITK_TEST_OUTPUT( << " [FAILED]"); return EXIT_FAILURE; } // Seperate Test function for Index and World consistency testIndexAndWorldConsistency(geometry3d); testIndexAndWorldConsistencyForVectors(geometry3d); testIndexAndWorldConsistencyForIndex(geometry3d); MITK_TEST_OUTPUT( << "Testing a rotation of the geometry"); double angle = 35.0; mitk::Vector3D rotationVector; mitk::FillVector3D( rotationVector, 1, 0, 0 ); mitk::Point3D center = geometry3d->GetCenter(); mitk::RotationOperation* op = new mitk::RotationOperation( mitk::OpROTATE, center, rotationVector, angle ); geometry3d->ExecuteOperation(op); MITK_TEST_OUTPUT( << "Testing mitk::GetRotation() and success of rotation"); mitk::Matrix3D rotation; mitk::GetRotation(geometry3d, rotation); mitk::Vector3D voxelStep=rotation*newspacing; mitk::Vector3D voxelStepIndex; geometry3d->WorldToIndex(voxelStep, voxelStepIndex); mitk::Vector3D expectedVoxelStepIndex; expectedVoxelStepIndex.Fill(1); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(voxelStepIndex,expectedVoxelStepIndex), ""); delete op; std::cout<<"[PASSED]"<GetImageGeometry() == imageGeometry, ""); //Test if the translate function moves the origin correctly. mitk::Point3D oldOrigin = geometry3d->GetOrigin(); //use some random values for translation mitk::Vector3D translationVector; translationVector.SetElement(0, 17.5f); translationVector.SetElement(1, -32.3f); translationVector.SetElement(2, 4.0f); //compute ground truth mitk::Point3D tmpResult = geometry3d->GetOrigin() + translationVector; geometry3d->Translate(translationVector); MITK_TEST_CONDITION( mitk::Equal( geometry3d->GetOrigin(), tmpResult ), "Testing if origin was translated."); translationVector*=-1; //vice versa geometry3d->Translate(translationVector); MITK_TEST_CONDITION( mitk::Equal( geometry3d->GetOrigin(), oldOrigin ), "Testing if the translation could be done vice versa." ); return EXIT_SUCCESS; } int testGeometryAfterCasting() { // Epsilon. Allowed difference for rotationvalue float eps = 0.0001; // Cast ITK and MITK images and see if geometry stays typedef itk::Image Image2DType; typedef itk::Image Image3DType; // Create 3D ITK Image from Scratch, cast to 3D MITK image, compare Geometries Image3DType::Pointer image3DItk = Image3DType::New(); Image3DType::RegionType myRegion; Image3DType::SizeType mySize; Image3DType::IndexType myIndex; Image3DType::SpacingType mySpacing; Image3DType::DirectionType myDirection, rotMatrixX, rotMatrixY, rotMatrixZ; mySpacing[0] = 31; mySpacing[1] = 0.1; mySpacing[2] = 2.9; myIndex[0] = -15; myIndex[1] = 15; myIndex[2] = 12; mySize[0] = 10; mySize[1] = 2; mySize[2] = 555; myRegion.SetSize( mySize); myRegion.SetIndex( myIndex ); image3DItk->SetSpacing(mySpacing); image3DItk->SetRegions( myRegion); image3DItk->Allocate(); image3DItk->FillBuffer(0); myDirection.SetIdentity(); rotMatrixX.SetIdentity(); rotMatrixY.SetIdentity(); rotMatrixZ.SetIdentity(); mitk::Image::Pointer mitkImage; // direction [row] [coloum] MITK_TEST_OUTPUT( << "Casting a rotated 3D ITK Image to a MITK Image and check if Geometry is still same" ); for (double rotX=0; rotX < (itk::Math::pi*2); rotX+=0.4 ) { // Set Rotation X rotMatrixX[1][1] = cos( rotX ); rotMatrixX[1][2] = -sin( rotX ); rotMatrixX[2][1] = sin( rotX ); rotMatrixX[2][2] = cos( rotX ); for (double rotY=0; rotY < (itk::Math::pi*2); rotY+=0.33 ) { // Set Rotation Y rotMatrixY[0][0] = cos( rotY ); rotMatrixY[0][2] = sin( rotY ); rotMatrixY[2][0] = -sin( rotY ); rotMatrixY[2][2] = cos( rotY ); for (double rotZ=0; rotZ < (itk::Math::pi*2); rotZ+=0.5 ) { // Set Rotation Z rotMatrixZ[0][0] = cos( rotZ ); rotMatrixZ[0][1] = -sin( rotZ ); rotMatrixZ[1][0] = sin( rotZ ); rotMatrixZ[1][1] = cos( rotZ ); // Multiply matrizes myDirection = myDirection * rotMatrixX * rotMatrixY * rotMatrixZ; image3DItk->SetDirection(myDirection); mitk::CastToMitkImage(image3DItk, mitkImage); const mitk::AffineTransform3D::MatrixType& matrix = mitkImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(); for (int row=0; row<3; row++) { for (int col=0; col<3; col++) { double mitkValue = matrix[row][col] / mitkImage->GetGeometry()->GetSpacing()[col]; double itkValue = myDirection[row][col]; double diff = mitkValue - itkValue; // if you decrease this value, you can see that there might be QUITE high inaccuracy!!! if (diff > eps) // need to check, how exact it SHOULD be .. since it is NOT EXACT! { std::cout << "Had a difference of : " << diff; std::cout << "Error: Casting altered Geometry!"; std::cout << "ITK Matrix:\n" << myDirection; std::cout << "Mitk Matrix (With Spacing):\n" << matrix; std::cout << "Mitk Spacing: " << mitkImage->GetGeometry()->GetSpacing(); MITK_TEST_CONDITION_REQUIRED(false == true, ""); return false; } } } } } } // Create 2D ITK Image from Scratch, cast to 2D MITK image, compare Geometries Image2DType::Pointer image2DItk = Image2DType::New(); Image2DType::RegionType myRegion2D; Image2DType::SizeType mySize2D; Image2DType::IndexType myIndex2D; Image2DType::SpacingType mySpacing2D; Image2DType::DirectionType myDirection2D, rotMatrix; mySpacing2D[0] = 31; mySpacing2D[1] = 0.1; myIndex2D[0] = -15; myIndex2D[1] = 15; mySize2D[0] = 10; mySize2D[1] = 2; myRegion2D.SetSize( mySize2D); myRegion2D.SetIndex( myIndex2D ); image2DItk->SetSpacing(mySpacing2D); image2DItk->SetRegions( myRegion2D); image2DItk->Allocate(); image2DItk->FillBuffer(0); myDirection2D.SetIdentity(); rotMatrix.SetIdentity(); // direction [row] [coloum] MITK_TEST_OUTPUT( << "Casting a rotated 2D ITK Image to a MITK Image and check if Geometry is still same" ); for (double rotTheta=0; rotTheta < (itk::Math::pi*2); rotTheta+=0.1 ) { // Set Rotation rotMatrix[0][0] = cos(rotTheta); rotMatrix[0][1] = -sin(rotTheta); rotMatrix[1][0] = sin(rotTheta); rotMatrix[1][1] = cos(rotTheta); // Multiply matrizes myDirection2D = myDirection2D * rotMatrix; image2DItk->SetDirection(myDirection2D); mitk::CastToMitkImage(image2DItk, mitkImage); const mitk::AffineTransform3D::MatrixType& matrix = mitkImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(); // Compare MITK and ITK matrix for (int row=0; row<3; row++) { for (int col=0; col<3; col++) { double mitkValue = matrix[row][col] / mitkImage->GetGeometry()->GetSpacing()[col]; if ((row == 2) && (col == row)) { if (mitkValue != 1) { MITK_TEST_OUTPUT(<< "After casting a 2D ITK to 3D MITK images, MITK matrix values for 0|2, 1|2, 2|0, 2|1 MUST be 0 and value for 2|2 must be 1"); return false; } } else if ((row == 2) || (col == 2)) { if (mitkValue != 0) { MITK_TEST_OUTPUT(<< "After casting a 2D ITK to 3D MITK images, MITK matrix values for 0|2, 1|2, 2|0, 2|1 MUST be 0 and value for 2|2 must be 1"); return false; } } else { double itkValue = myDirection2D[row][col]; double diff = mitkValue - itkValue; // if you decrease this value, you can see that there might be QUITE high inaccuracy!!! if (diff > eps) // need to check, how exact it SHOULD be .. since it is NOT EXACT! { std::cout << "Had a difference of : " << diff; std::cout << "Error: Casting altered Geometry!"; std::cout << "ITK Matrix:\n" << myDirection2D; std::cout << "Mitk Matrix (With Spacing):\n" << matrix; std::cout << "Mitk Spacing: " << mitkImage->GetGeometry()->GetSpacing(); MITK_TEST_CONDITION_REQUIRED(false == true, ""); return false; } } } } } // THIS WAS TESTED: // 2D ITK -> 2D MITK, // 3D ITK -> 3D MITK, // Still need to test: 2D MITK Image with ADDITIONAL INFORMATION IN MATRIX -> 2D ITK // 1. Possibility: 3x3 MITK matrix can be converted without loss into 2x2 ITK matrix // 2. Possibility: 3x3 MITK matrix can only be converted with loss into 2x2 ITK matrix // .. before implementing this, we wait for further development in geometry classes (e.g. Geoemtry3D::SetRotation(..)) return EXIT_SUCCESS; } int mitkGeometry3DTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN(mitkGeometry3DTest); int result; MITK_TEST_CONDITION_REQUIRED( (result = testItkImageIsCenterBased()) == EXIT_SUCCESS, ""); MITK_TEST_OUTPUT(<< "Running main part of test with ImageGeometry = false"); MITK_TEST_CONDITION_REQUIRED( (result = testGeometry3D(false)) == EXIT_SUCCESS, ""); MITK_TEST_OUTPUT(<< "Running main part of test with ImageGeometry = true"); MITK_TEST_CONDITION_REQUIRED( (result = testGeometry3D(true)) == EXIT_SUCCESS, ""); MITK_TEST_OUTPUT(<< "Running test to see if Casting MITK to ITK and the other way around destroys geometry"); MITK_TEST_CONDITION_REQUIRED( (result = testGeometryAfterCasting()) == EXIT_SUCCESS, ""); MITK_TEST_END(); return EXIT_SUCCESS; } diff --git a/Core/Code/Testing/mitkImageTest.cpp b/Core/Code/Testing/mitkImageTest.cpp index ffbbc84044..978fee8647 100644 --- a/Core/Code/Testing/mitkImageTest.cpp +++ b/Core/Code/Testing/mitkImageTest.cpp @@ -1,526 +1,526 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // mitk includes #include #include #include #include "mitkItkImageFileReader.h" #include #include #include "mitkImageGenerator.h" #include "mitkImageReadAccessor.h" #include "mitkException.h" #include "mitkPixelTypeMultiplex.h" #include "mitkImagePixelReadAccessor.h" #include "mitkImageSliceSelector.h" // itk includes #include #include // stl includes #include // vtk includes #include // Checks if reference count is correct after using GetVtkImageData() bool ImageVtkDataReferenceCheck(const char* fname) { const std::string filename = std::string(fname); 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 false; } { mitk::Image::Pointer image = imageReader->GetOutput(); vtkImageData* vtk = image->GetVtkImageData(); if(vtk == NULL) return false; } return true; } template void TestRandomPixelAccess( const mitk::PixelType ptype, mitk::Image::Pointer image, mitk::Point3D & point, mitk::ScalarType & value ) { // generate a random point in world coordinates mitk::Point3D xMax, yMax, zMax, xMaxIndex, yMaxIndex, zMaxIndex; xMaxIndex.Fill(0.0f); yMaxIndex.Fill(0.0f); zMaxIndex.Fill(0.0f); xMaxIndex[0] = image->GetLargestPossibleRegion().GetSize()[0]; yMaxIndex[1] = image->GetLargestPossibleRegion().GetSize()[1]; zMaxIndex[2] = image->GetLargestPossibleRegion().GetSize()[2]; image->GetGeometry()->IndexToWorld(xMaxIndex, xMax); image->GetGeometry()->IndexToWorld(yMaxIndex, yMax); image->GetGeometry()->IndexToWorld(zMaxIndex, zMax); MITK_INFO << "Origin " << image->GetGeometry()->GetOrigin()[0] << " "<< image->GetGeometry()->GetOrigin()[1] << " "<< image->GetGeometry()->GetOrigin()[2] << ""; MITK_INFO << "MaxExtend " << xMax[0] << " "<< yMax[1] << " "<< zMax[2] << ""; itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randomGenerator = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randomGenerator->Initialize( std::rand() ); // initialize with random value, to get sensible random points for the image point[0] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[0], xMax[0]); point[1] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[1], yMax[1]); point[2] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[2], zMax[2]); MITK_INFO << "RandomPoint " << point[0] << " "<< point[1] << " "<< point[2] << ""; // test values and max/min mitk::ScalarType imageMin = image->GetStatistics()->GetScalarValueMin(); mitk::ScalarType imageMax = image->GetStatistics()->GetScalarValueMax(); // test accessing PixelValue with coordinate leading to a negative index const mitk::Point3D geom_origin = image->GetGeometry()->GetOrigin(); const mitk::Point3D geom_center = image->GetGeometry()->GetCenter(); // shift position from origin outside of the image ( in the opposite direction to [center-origin] vector which points in the inside) mitk::Point3D position = geom_origin + (geom_origin - geom_center); MITK_INFO << "Testing access outside of the image"; unsigned int dim = image->GetDimension(); if(dim == 3 || dim == 4){ mitk::ImagePixelReadAccessor imAccess3(image,image->GetVolumeData(0)); // Comparison ?>=0 not needed since all position[i] and timestep are unsigned int // (position[0]>=0 && position[1] >=0 && position[2]>=0 && timestep>=0) // bug-11978 : we still need to catch index with negative values if ( point[0] < 0 || point[1] < 0 || point[2] < 0 ) { MITK_WARN << "Given position ("<< point << ") is out of image range, returning 0." ; } else { value = static_cast(imAccess3.GetPixelByWorldCoordinates(point)); MITK_TEST_CONDITION( (value >= imageMin && value <= imageMax), "Value returned is between max/min"); } - mitk::Index3D itkIndex; + itk::Index<3> itkIndex; image->GetGeometry()->WorldToIndex(position, itkIndex); MITK_TEST_FOR_EXCEPTION_BEGIN(mitk::Exception); imAccess3.GetPixelByIndexSafe(itkIndex); MITK_TEST_FOR_EXCEPTION_END(mitk::Exception); } MITK_INFO << imageMin << " "<< imageMax << " "<< value << ""; } class mitkImageTestClass { public: void SetClonedGeometry_None_ClonedEqualInput() { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(100, 100, 100, 1, 0.2, 0.3, 0.4); //----------------- // geometry information for image mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::Vector3D spacing; mitk::FillVector3D(origin, 17.0, 19.92, 7.83); mitk::FillVector3D(right, 1.0, 2.0, 3.0); mitk::FillVector3D(bottom, 0.0, -3.0, 2.0); mitk::FillVector3D(spacing, 0.78, 0.91, 2.23); //InitializeStandardPlane(rightVector, downVector, spacing) mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing); planegeometry->SetOrigin(origin); planegeometry->ChangeImageGeometryConsideringOriginOffset(true); image->SetClonedGeometry(planegeometry); mitk::BaseGeometry::Pointer imageGeometry = image->GetGeometry(); itk::ScalableAffineTransform* frameNew = imageGeometry->GetIndexToWorldTransform(); itk::ScalableAffineTransform* frameOld = planegeometry->GetIndexToWorldTransform(); bool matrixEqual = true; for (int i = 0; i < 16; ++i) { double valueNew = *(frameNew->GetMatrix()[i]); double valueOld = *(frameOld->GetMatrix()[i]); //MITK_INFO << "Index: " << i << " Old: " << valueOld << " New: " << valueNew << " Difference:" << valueOld-valueNew<< std::endl; matrixEqual = matrixEqual && mitk::Equal(valueNew, valueOld, mitk::eps); } // Disabled because this test fails on the dashboard. Does not fail on my machine. // See Bug 6505 // MITK_TEST_CONDITION(matrixEqual, "Matrix elements of cloned matrix equal original matrix"); } }; int mitkImageTest(int argc, char* argv[]) { MITK_TEST_BEGIN(mitkImageTest); mitkImageTestClass tester; tester.SetClonedGeometry_None_ClonedEqualInput(); //Create Image out of nowhere mitk::Image::Pointer imgMem = mitk::Image::New(); mitk::PixelType pt = mitk::MakeScalarPixelType(); unsigned int dim[]={100,100,20}; MITK_TEST_CONDITION_REQUIRED( imgMem.IsNotNull(), "An image was created. "); // Initialize image imgMem->Initialize( pt, 3, dim); MITK_TEST_CONDITION_REQUIRED( imgMem->IsInitialized(), "Image::IsInitialized() ?"); MITK_TEST_CONDITION_REQUIRED( imgMem->GetPixelType() == pt, "PixelType was set correctly."); int *p = NULL; int *p2 = NULL; try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int*)imgMemAcc.GetData(); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION( p != NULL, "GetData() returned not-NULL pointer."); // filling image const unsigned int size = dim[0]*dim[1]*dim[2]; for(unsigned int i=0; iGetSliceData(dim[2]/2)); p2 = (int*)imgMemAcc.GetData(); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } 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: try { mitk::ImageReadAccessor imgMemAcc(imgMem); imgMem->SetVolume(imgMemAcc.GetData()); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } //----------------- // geometry information for image mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::Vector3D spacing; mitk::FillVector3D(origin, 17.0, 19.92, 7.83); mitk::FillVector3D(right, 1.0, 2.0, 3.0); mitk::FillVector3D(bottom, 0.0, -3.0, 2.0); mitk::FillVector3D(spacing, 0.78, 0.91, 2.23); //InitializeStandardPlane(rightVector, downVector, spacing) mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing); planegeometry->SetOrigin(origin); // Testing Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int slices) with PlaneGeometry and GetData(): "; imgMem->Initialize( mitk::MakePixelType(), *planegeometry); MITK_TEST_CONDITION_REQUIRED( imgMem->GetGeometry()->GetOrigin() == static_cast(planegeometry)->GetOrigin(), "Testing correct setting of geometry via initialize!"); try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int*)imgMemAcc.GetData(); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } 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); try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int*)imgMemAcc.GetData(); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } 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(): "); // Setting origin via SetOrigin(origin): "; mitk::FillVector3D(origin, 37.0, 17.92, 27.83); imgMem->SetOrigin(origin); // Test origin MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetOrigin(), origin), "Testing correctness of changed origin via GetGeometry()->GetOrigin(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetPlaneGeometry(0)->GetOrigin(), origin), "Testing correctness of changed origin via GetSlicedGeometry()->GetPlaneGeometry(0)->GetOrigin(): "); //----------------- // testing spacing information and methodsunsigned int dim[]={100,100,20}; MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correct spacing from Geometry3D!"); mitk::FillVector3D(spacing, 7.0, 0.92, 1.83); imgMem->SetSpacing(spacing); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correctness of changed spacing via GetGeometry()->GetSpacing(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetPlaneGeometry(0)->GetSpacing(), spacing), "Testing correctness of changed spacing via GetSlicedGeometry()->GetPlaneGeometry(0)->GetSpacing(): "); mitk::Image::Pointer vecImg = mitk::Image::New(); try { mitk::ImageReadAccessor imgMemAcc(imgMem); vecImg->Initialize( imgMem->GetPixelType(), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/ ); vecImg->SetImportChannel(const_cast(imgMemAcc.GetData()), 0, mitk::Image::CopyMemory ); vecImg->SetImportChannel(const_cast(imgMemAcc.GetData()), 1, mitk::Image::CopyMemory ); mitk::ImageReadAccessor vecImgAcc(vecImg); mitk::ImageReadAccessor vecImgAcc0(vecImg, vecImg->GetChannelData(0)); mitk::ImageReadAccessor vecImgAcc1(vecImg, vecImg->GetChannelData(1)); MITK_TEST_CONDITION_REQUIRED(vecImgAcc0.GetData() != NULL && vecImgAcc1.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(imgMemAcc.GetData() != vecImgAcc.GetData(), ""); MITK_TEST_OUTPUT(<< " Testing destruction after SetImportChannel"); vecImg = NULL; MITK_TEST_CONDITION_REQUIRED(vecImg.IsNull() , "testing destruction!"); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } //----------------- MITK_TEST_OUTPUT(<< "Testing initialization via vtkImageData"); MITK_TEST_OUTPUT(<< " Setting up vtkImageData"); vtkImageData* vtkimage = vtkImageData::New(); vtkimage->Initialize(); vtkimage->SetDimensions( 2, 3, 4); double vtkorigin[] = {-350,-358.203, -1363.5}; vtkimage->SetOrigin(vtkorigin); mitk::Point3D vtkoriginAsMitkPoint; mitk::vtk2itk(vtkorigin, vtkoriginAsMitkPoint); double vtkspacing[] = {1.367, 1.367, 2}; vtkimage->SetSpacing(vtkspacing); vtkimage->AllocateScalars(VTK_SHORT,1); std::cout<<"[PASSED]"<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), ""); // 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::PlaneGeometry& 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(); mitk::Point3D point; mitk::ScalarType value = -1.; mitkPixelTypeMultiplex3(TestRandomPixelAccess,image->GetImageDescriptor()->GetChannelTypeById(0),image,point,value) { // testing the clone method of mitk::Image mitk::Image::Pointer cloneImage = image->Clone(); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension() == image->GetDimension(), "Clone (testing dimension)"); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetPixelType() == image->GetPixelType(), "Clone (testing pixel type)"); // After cloning an image the geometry of both images should be equal too MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetOrigin() == image->GetGeometry()->GetOrigin(), "Clone (testing origin)"); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetSpacing() == image->GetGeometry()->GetSpacing(), "Clone (testing spacing)"); MITK_TEST_CONDITION_REQUIRED(mitk::MatrixEqualElementWise(cloneImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(), image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix()), "Clone (testing transformation matrix)"); MITK_TEST_CONDITION_REQUIRED(mitk::MatrixEqualElementWise(cloneImage->GetTimeGeometry()->GetGeometryForTimeStep(cloneImage->GetDimension(3)-1)->GetIndexToWorldTransform()->GetMatrix(), cloneImage->GetTimeGeometry()->GetGeometryForTimeStep(image->GetDimension(3)-1)->GetIndexToWorldTransform()->GetMatrix()), "Clone(testing time sliced geometry)"); for (unsigned int i = 0u; i < cloneImage->GetDimension(); ++i) { MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension(i) == image->GetDimension(i), "Clone (testing dimension " << i << ")"); } } //access via itk if(image->GetDimension()> 3) // CastToItk only works with 3d images so we need to check for 4d images { mitk::ImageTimeSelector::Pointer selector = mitk::ImageTimeSelector::New(); selector->SetTimeNr(0); selector->SetInput(image); selector->Update(); image = selector->GetOutput(); } if(image->GetDimension()==3) { typedef itk::Image ItkFloatImage3D; ItkFloatImage3D::Pointer itkimage; try { mitk::CastToItkImage(image, itkimage); MITK_TEST_CONDITION_REQUIRED(itkimage.IsNotNull(), "Test conversion to itk::Image!"); } catch (std::exception& e) { MITK_INFO << e.what(); } mitk::Point3D itkPhysicalPoint; image->GetGeometry()->WorldToItkPhysicalPoint(point, itkPhysicalPoint); MITK_INFO << "ITKPoint " << itkPhysicalPoint[0] << " "<< itkPhysicalPoint[1] << " "<< itkPhysicalPoint[2] << ""; mitk::Point3D backTransformedPoint; image->GetGeometry()->ItkPhysicalPointToWorld(itkPhysicalPoint, backTransformedPoint); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(point,backTransformedPoint), "Testing world->itk-physical->world consistency"); itk::Index<3> idx; bool status = itkimage->TransformPhysicalPointToIndex(itkPhysicalPoint, idx); MITK_INFO << "ITK Index " << idx[0] << " "<< idx[1] << " "<< idx[2] << ""; if(status && value != -1.) { float valByItk = itkimage->GetPixel(idx); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(valByItk, value), "Compare value of pixel returned by mitk in comparison to itk"); } else { MITK_WARN<< "Index is out buffered region!"; } } else { MITK_INFO << "Image does not contain three dimensions, some test cases are skipped!"; } // clone generated 3D image with one slice in z direction (cf. bug 11058) 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_CONDITION_REQUIRED( ImageVtkDataReferenceCheck(argv[1]), "Checking reference count of Image after using GetVtkImageData()"); MITK_TEST_END(); } diff --git a/Core/Code/Testing/mitkLogTest.cpp b/Core/Code/Testing/mitkLogTest.cpp index 0a93be0cc2..cee8373457 100644 --- a/Core/Code/Testing/mitkLogTest.cpp +++ b/Core/Code/Testing/mitkLogTest.cpp @@ -1,286 +1,286 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCommon.h" #include "mitkTestingMacros.h" #include -#include +#include #include #include #include /** Documentation * * @brief Objects of this class can start an internal thread by calling the Start() method. * The thread is then logging messages until the method Stop() is called. The class * can be used to test if logging is thread-save by using multiple objects and let * them log simuntanously. */ class mitkTestLoggingThread : public itk::Object { public: mitkClassMacro(mitkTestLoggingThread,itk::Object); mitkNewMacro1Param(mitkTestLoggingThread,itk::MultiThreader::Pointer); int NumberOfMessages; protected: mitkTestLoggingThread(itk::MultiThreader::Pointer MultiThreader) { ThreadID = -1; NumberOfMessages = 0; m_MultiThreader = MultiThreader; } bool LoggingRunning; int ThreadID; itk::MultiThreader::Pointer m_MultiThreader; void LogMessages() { while(LoggingRunning) { MITK_INFO << "Test info stream in thread" << ThreadID << "\n even with newlines"; MITK_WARN << "Test warning stream in thread " << ThreadID <<". " << "Even with a very long text, even without meaning or implied meaning or content, just a long sentence to see whether something has problems with long sentences or output in files or into windows or commandlines or whatever."; MITK_DEBUG << "Test debugging stream in thread " << ThreadID; MITK_ERROR << "Test error stream in thread " << ThreadID; MITK_FATAL << "Test fatal stream in thread " << ThreadID; NumberOfMessages += 5; } } static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void* pInfoStruct) { /* extract this pointer from Thread Info structure */ struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; if (pInfo == NULL) { return ITK_THREAD_RETURN_VALUE; } if (pInfo->UserData == NULL) { return ITK_THREAD_RETURN_VALUE; } mitkTestLoggingThread *thisthread = (mitkTestLoggingThread*)pInfo->UserData; if (thisthread != NULL) thisthread->LogMessages(); return ITK_THREAD_RETURN_VALUE; } public: int Start() { LoggingRunning = true; this->ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); return ThreadID; } void Stop() { LoggingRunning = false; } }; /** Documentation * * @brief This class holds static test methods to sturcture the test of the mitk logging mechanism. */ class mitkLogTestClass { public: static void TestSimpleLog() { bool testSucceded = true; try { MITK_INFO << "Test info stream."; MITK_WARN << "Test warning stream."; MITK_DEBUG << "Test debugging stream."; //only activated if cmake variable is on! //so no worries if you see no output for this line MITK_ERROR << "Test error stream."; MITK_FATAL << "Test fatal stream."; } catch(mitk::Exception e) { testSucceded = false; } MITK_TEST_CONDITION_REQUIRED(testSucceded,"Test logging streams."); } static void TestObjectInfoLogging() { bool testSucceded = true; try { int i = 123; float f = .32234; double d = 123123; std::string testString = "testString"; std::stringstream testStringStream; testStringStream << "test" << "String" << "Stream"; mitk::Point3D testMitkPoint; testMitkPoint.Fill(2); MITK_INFO << i; MITK_INFO << f; MITK_INFO << d; MITK_INFO << testString; MITK_INFO << testStringStream.str(); MITK_INFO << testMitkPoint; } catch(mitk::Exception e) { testSucceded = false; } MITK_TEST_CONDITION_REQUIRED(testSucceded,"Test logging of object information."); } static void TestThreadSaveLog(bool toFile) { bool testSucceded = true; try { if (toFile) { std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + "/testthreadlog.log"; itksys::SystemTools::RemoveFile(filename.c_str()); // remove old file, we do not want to append to large files mitk::LoggingBackend::SetLogFile(filename.c_str()); } unsigned int numberOfThreads = 20; unsigned int threadRuntimeInMilliseconds = 2000; std::vector threadIDs; std::vector threads; itk::MultiThreader::Pointer multiThreader = itk::MultiThreader::New(); for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { //initialize threads... mitkTestLoggingThread::Pointer newThread = mitkTestLoggingThread::New(multiThreader); threads.push_back(newThread); std::cout << "Created " << threadIdx << ". thread." << std::endl; } for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { //start them std::cout << "Start " << threadIdx << ". thread." << std::endl; threadIDs.push_back( threads[threadIdx]->Start() ); std::cout << threadIdx << ". thread has ID " << threadIDs[threadIdx] << std::endl; } //wait for some time (milliseconds) itksys::SystemTools::Delay( threadRuntimeInMilliseconds ); for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { //stop them std::cout << "Stop " << threadIdx << ". thread." << std::endl; threads[threadIdx]->Stop(); } for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { //Wait for all threads to end multiThreader->TerminateThread(threadIDs[threadIdx]); std::cout << "Terminated " << threadIdx << ". thread (" << threads[threadIdx]->NumberOfMessages << " messages)." << std::endl; } } catch(std::exception e) { MITK_ERROR << "exception during 'TestThreadSaveLog': "<GetOptionDirectory() + "/testlog.log"; mitk::LoggingBackend::SetLogFile(filename.c_str()); MITK_INFO << "Test logging to default filename: " << mitk::LoggingBackend::GetLogFile(); MITK_TEST_CONDITION_REQUIRED(itksys::SystemTools::FileExists(filename.c_str()),"Testing if log file exists."); //TODO delete log file? } static void TestAddAndRemoveBackends() { mbilog::BackendCout myBackend = mbilog::BackendCout(); mbilog::RegisterBackend(&myBackend); MITK_INFO << "Test logging"; mbilog::UnregisterBackend(&myBackend); //if no error occured until now, everything is ok MITK_TEST_CONDITION_REQUIRED(true,"Test add/remove logging backend."); } static void TestDefaultBackend() { //not possible now, because we cannot unregister the mitk logging backend in the moment. If such a method is added to mbilog utility one may add this test. } }; int mitkLogTest(int /* argc */, char* /*argv*/[]) { // always start with this! MITK_TEST_BEGIN("Log") MITK_TEST_OUTPUT(<<"TESTING ALL LOGGING OUTPUTS, ERROR MESSAGES ARE ALSO TESTED AND NOT MEANING AN ERROR OCCURED!") mitkLogTestClass::TestSimpleLog(); mitkLogTestClass::TestObjectInfoLogging(); mitkLogTestClass::TestLoggingToFile(); mitkLogTestClass::TestAddAndRemoveBackends(); mitkLogTestClass::TestThreadSaveLog( false ); // false = to console mitkLogTestClass::TestThreadSaveLog( true ); // true = to file // TODO actually test file somehow? // always end with this! MITK_TEST_END() } diff --git a/Core/Code/Testing/mitkLookupTableTest.cpp b/Core/Code/Testing/mitkLookupTableTest.cpp index 402c96d948..bc9189ec54 100644 --- a/Core/Code/Testing/mitkLookupTableTest.cpp +++ b/Core/Code/Testing/mitkLookupTableTest.cpp @@ -1,183 +1,183 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include "mitkTestingMacros.h" -#include +#include #include #include #include class mitkLookupTableTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkLookupTableTestSuite); MITK_TEST(TestCreateLookupTable); MITK_TEST(TestSetVtkLookupTable); MITK_TEST(TestSetOpacity); MITK_TEST(TestCreateColorTransferFunction); MITK_TEST(TestCreateOpacityTransferFunction); MITK_TEST(TestCreateGradientTransferFunction); CPPUNIT_TEST_SUITE_END(); private: mitk::LookupTable::Pointer m_LookupTable; public: void TestCreateLookupTable() { // let's create an object of our class mitk::LookupTable::Pointer myLookupTable = mitk::LookupTable::New(); // first test: did this work? // it makes no sense to continue without an object. CPPUNIT_ASSERT_MESSAGE("Testing instantiation", myLookupTable.IsNotNull()); } void TestSetVtkLookupTable () { mitk::LookupTable::Pointer myLookupTable = mitk::LookupTable::New(); // create a vtkLookupTable and add two values vtkLookupTable *lut = vtkLookupTable::New(); lut->SetTableValue(0, 0.5, 0.5, 0.5, 1.0); lut->SetTableValue(1, 0.5, 0.5, 0.5, 0.5); lut->Build(); myLookupTable->SetVtkLookupTable(lut); // check if the same lookuptable is returned vtkLookupTable *lut2 = myLookupTable->GetVtkLookupTable(); CPPUNIT_ASSERT_MESSAGE("Input and output table are not equal",lut == lut2); lut->Delete(); } void TestSetOpacity() { mitk::LookupTable::Pointer myLookupTable = mitk::LookupTable::New(); // create a vtkLookupTable and add two values vtkLookupTable *lut = vtkLookupTable::New(); lut->SetRange(0, 200); lut->SetAlphaRange(0.0, 1.0); lut->Build(); myLookupTable->SetVtkLookupTable(lut); myLookupTable->ChangeOpacityForAll(0.7f); for (int i = 0; i < lut->GetNumberOfTableValues(); ++i) { CPPUNIT_ASSERT_MESSAGE("Opacity not set for all", mitk::Equal(0.7, lut->GetOpacity(i), 0.01, true)); } vtkIdType tableIndex = 10; myLookupTable->ChangeOpacity(tableIndex, 1.0); double rgba[4]; lut->GetTableValue(tableIndex, rgba); CPPUNIT_ASSERT_MESSAGE("Opacity not set for value", mitk::Equal(1.0, rgba[3], 0.01, true)); } void TestCreateColorTransferFunction () { mitk::LookupTable::Pointer myLookupTable = mitk::LookupTable::New(); // create a vtkLookupTable and add two values vtkLookupTable *lut = vtkLookupTable::New(); lut->SetRange(0, 255); lut->Build(); myLookupTable->SetVtkLookupTable(lut); vtkSmartPointer colorTransferFunction = myLookupTable->CreateColorTransferFunction(); CPPUNIT_ASSERT(colorTransferFunction != 0); vtkIdType numberOfTableEntries = lut->GetNumberOfTableValues(); double rgbaTable[4]; double rgbaFunction[4]; for (int i = 0; i < numberOfTableEntries; ++i) { lut->GetIndexedColor(i, rgbaTable); colorTransferFunction->GetIndexedColor(i, rgbaFunction); CPPUNIT_ASSERT_MESSAGE("Wrong color of transfer function", mitk::Equal(rgbaTable[0], rgbaFunction[0], 0.000001, true) && mitk::Equal(rgbaTable[1], rgbaFunction[1], 0.000001, true) && mitk::Equal(rgbaTable[2], rgbaFunction[2], 0.000001, true) ); } } void TestCreateOpacityTransferFunction () { mitk::LookupTable::Pointer myLookupTable = mitk::LookupTable::New(); // create a vtkLookupTable and add two values vtkLookupTable *lut = vtkLookupTable::New(); lut->SetAlphaRange(0, 1.0); lut->Build(); myLookupTable->SetVtkLookupTable(lut); vtkSmartPointer opacityTransferFunction = myLookupTable->CreateOpacityTransferFunction(); CPPUNIT_ASSERT(opacityTransferFunction != 0); int funcSize = opacityTransferFunction->GetSize(); double *table = new double[funcSize]; double rgba[4]; opacityTransferFunction->GetTable(0, 1, funcSize, table); for (int i = 0; i < funcSize; ++i) { lut->GetIndexedColor(i, rgba); CPPUNIT_ASSERT_MESSAGE("Wrong opacity of transfer function", mitk::Equal(table[i], rgba[3], 0.000001, true) ); } delete [] table; } void TestCreateGradientTransferFunction () { mitk::LookupTable::Pointer myLookupTable = mitk::LookupTable::New(); // create a vtkLookupTable and add two values vtkLookupTable *lut = vtkLookupTable::New(); lut->SetAlphaRange(0, 0.73); lut->Build(); myLookupTable->SetVtkLookupTable(lut); vtkSmartPointer gradientTransferFunction = myLookupTable->CreateGradientTransferFunction(); CPPUNIT_ASSERT(gradientTransferFunction != 0); int funcSize = gradientTransferFunction->GetSize(); double *table = new double[funcSize]; double rgba[4]; gradientTransferFunction->GetTable(0, 1, funcSize, table); for (int i = 0; i < funcSize; ++i) { lut->GetIndexedColor(i, rgba); CPPUNIT_ASSERT_MESSAGE("Wrong opacity of transfer function", mitk::Equal(table[i], rgba[3], 0.000001, true) ); } delete [] table; } }; MITK_TEST_SUITE_REGISTRATION(mitkLookupTable) diff --git a/Core/Code/Testing/mitkMatrixTypeConversionTest.cpp b/Core/Code/Testing/mitkMatrixTypeConversionTest.cpp new file mode 100644 index 0000000000..4c24a6bcf0 --- /dev/null +++ b/Core/Code/Testing/mitkMatrixTypeConversionTest.cpp @@ -0,0 +1,100 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include + +#include "mitkTestingMacros.h" +#include "mitkTestFixture.h" + +#include "mitkMatrix.h" + + +class mitkMatrixTypeConversionTestSuite : public mitk::TestFixture + +{ + + CPPUNIT_TEST_SUITE(mitkMatrixTypeConversionTestSuite); + + MITK_TEST(Mitk2Pod); + MITK_TEST(Pod2Mitk); + + CPPUNIT_TEST_SUITE_END(); + +private: + + mitk::Matrix3D mitkMatrix3D; + mitk::ScalarType podMatrix3D[3][3]; + + + /** + * @brief Convenience method to test if one matrix has been assigned successfully to the other. + * + * More specifically, tests if m1 = m2 was performed correctly. + * + * @param m1 The matrix m1 of the assignment m1 = m2 + * @param m2 The matrix m2 of the assignment m1 = m2 + */ + template + void TestForEquality(T1 m1, T2 m2) + { + for (unsigned i = 0; i < 3; i++) + { + for (unsigned j = 0; j < 3; j++) + { + std::stringstream ss; + ss << "element [" << i << "][" + j << "] equal for mitkMatrix and podMatrix"; + + CPPUNIT_ASSERT_EQUAL_MESSAGE(ss.str(), true, + (mitkMatrix3D[i][j]==podMatrix3D[i][j])); + } + } + } + + +public: + + void setUp(void) + { + for (unsigned i = 0; i < 3; i++) + for (unsigned j = 0; j < 3; j++) + { + mitkMatrix3D[i][j] = i + j; + podMatrix3D[i][j] = (mitk::ScalarType) (9 - (i + j)); + } + } + + void tearDown(void) + { + + } + + void Mitk2Pod(void) + { + mitkMatrix3D.ToArray(podMatrix3D); + + TestForEquality(mitkMatrix3D, podMatrix3D); + } + + void Pod2Mitk(void) + { + mitkMatrix3D.FillMatrix(podMatrix3D); + + TestForEquality(podMatrix3D, mitkMatrix3D); + } + +}; + +MITK_TEST_SUITE_REGISTRATION(mitkMatrixTypeConversion) diff --git a/Core/Code/Testing/mitkPointSetTest.cpp b/Core/Code/Testing/mitkPointSetTest.cpp index 2aabf27e7b..61a73f43b0 100644 --- a/Core/Code/Testing/mitkPointSetTest.cpp +++ b/Core/Code/Testing/mitkPointSetTest.cpp @@ -1,359 +1,359 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include "mitkTestFixture.h" #include -#include +#include #include #include #include /** * TestSuite for PointSet stuff not only operating on an empty PointSet */ class mitkPointSetTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPointSetTestSuite); MITK_TEST(TestIsNotEmpty); MITK_TEST(TestSetSelectInfo); MITK_TEST(TestGetNumberOfSelected); MITK_TEST(TestSearchSelectedPoint); MITK_TEST(TestGetPointIfExists); MITK_TEST(TestSwapPointPositionUpwards); MITK_TEST(TestSwapPointPositionUpwardsNotPossible); MITK_TEST(TestSwapPointPositionDownwards); MITK_TEST(TestSwapPointPositionDownwardsNotPossible); MITK_TEST(TestCreateHoleInThePointIDs); MITK_TEST(TestInsertPointWithPointSpecification); CPPUNIT_TEST_SUITE_END(); private: mitk::PointSet::Pointer pointSet; static const mitk::PointSet::PointIdentifier selectedPointId = 2; public: void setUp() { //Create PointSet pointSet = mitk::PointSet::New(); // add some points 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); mitk::Point3D point1; mitk::FillVector3D(point1, 1.0, 2.0, 3.0); pointSet->InsertPoint(1, point1); mitk::Point3D point0; point0.Fill(1); pointSet->InsertPoint(0, point0); // select point with id 2 pointSet->SetSelectInfo(2, true); } void tearDown() { pointSet = NULL; } void TestIsNotEmpty() { //PointSet can not be empty! CPPUNIT_ASSERT_EQUAL_MESSAGE( "check if the PointSet is not empty ", true, !pointSet->IsEmptyTimeStep(0) ); /* std::cout << "check if the PointSet is not empty "; if (pointSet->IsEmpty(0)) { std::cout<<"[FAILED]"<SetSelectInfo(4, true); CPPUNIT_ASSERT_EQUAL_MESSAGE("check SetSelectInfo", true, pointSet->GetSelectInfo(4)); /* if (!pointSet->GetSelectInfo(2)) { std::cout<<"[FAILED]"<SearchSelectedPoint() == (int) selectedPointId); /* if( pointSet->SearchSelectedPoint() != 4) { std::cout<<"[FAILED]"<GetNumberOfSelected() == 1); /* if(pointSet->GetNumberOfSelected() != 1) { std::cout<<"[FAILED]"<GetPointIfExists(4, &tmpPoint); CPPUNIT_ASSERT_EQUAL_MESSAGE("check GetPointIfExists: ", true, tmpPoint == point4); /* if (tmpPoint != point5) { std::cout<<"[FAILED]"<GetPoint(1); pointSet->SwapPointPosition(1, true); tempPoint = pointSet->GetPoint(0); CPPUNIT_ASSERT_EQUAL_MESSAGE("check SwapPointPosition upwards", true, point == tempPoint); /* if(point != tempPoint) { std::cout<<"[FAILED]"<SwapPointPosition(0, true)); /* if(pointSet->SwapPointPosition(0, true)) { std::cout<<"[FAILED]"<GetPoint(0); pointSet->SwapPointPosition(0, false); tempPoint = pointSet->GetPoint(1); CPPUNIT_ASSERT_EQUAL_MESSAGE("check SwapPointPosition down", true, point == tempPoint); /* if(point != tempPoint) { std::cout<<"[FAILED]"<SetPoint(id, point); //Check SwapPointPosition downwards not possible CPPUNIT_ASSERT_EQUAL_MESSAGE("check SwapPointPosition downwards not possible", false, pointSet2->SwapPointPosition(id, false)); /* if(pointSet->SwapPointPosition(1, false)) { std::cout<<"[FAILED]"<InsertPoint(10, p10); pointSet->InsertPoint(11, p11); pointSet->InsertPoint(12, p12); CPPUNIT_ASSERT_EQUAL_MESSAGE("add points with id 10, 11, 12: ", true, (pointSet->IndexExists(10) == true) || (pointSet->IndexExists(11) == true) || (pointSet->IndexExists(12) == true)); //check OpREMOVE ExecuteOperation int id = 11; mitk::PointOperation* doOp = new mitk::PointOperation(mitk::OpREMOVE, point, id); pointSet->ExecuteOperation(doOp); CPPUNIT_ASSERT_EQUAL_MESSAGE( "remove point id 11: ", false, pointSet->IndexExists(id)); /* 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); CPPUNIT_ASSERT_EQUAL_MESSAGE("check PointOperation OpMOVEPOINTUP for point id 12:", true, ((newP10 == p12) && (newP12 == p10))); //check OpMOVEPOINTDOWN ExecuteOperation doOp = new mitk::PointOperation(mitk::OpMOVEPOINTDOWN, p10, 10); pointSet->ExecuteOperation(doOp); delete doOp; newP10 = pointSet->GetPoint(10); newP12 = pointSet->GetPoint(12); CPPUNIT_ASSERT_EQUAL_MESSAGE("check PointOperation OpMOVEPOINTDOWN for point id 10: ", true, ((newP10 == p10) && (newP12 == p12))); } void TestInsertPointWithPointSpecification() { //check InsertPoint with PointSpecification mitk::Point3D point5; mitk::Point3D tempPoint; point5.Fill(7); pointSet->SetPoint(5, point5, mitk::PTEDGE ); tempPoint = pointSet->GetPoint(5); CPPUNIT_ASSERT_EQUAL_MESSAGE("check InsertPoint with PointSpecification" , true, tempPoint == point5); /* if (tempPoint != point5) { std::cout<<"[FAILED]"< + +using namespace mitk; + + +class mitkPointTypeConversionTestSuite : public mitk::TestFixture +{ + + CPPUNIT_TEST_SUITE(mitkPointTypeConversionTestSuite); + + MITK_TEST(Vector2Point); + + MITK_TEST(Mitk2Itk_PointCompatibility); + MITK_TEST(Itk2Mitk_PointCompatibility); + + MITK_TEST(Vtk2Mitk_PointCompatibility); + + MITK_TEST(Mitk2Pod_PointCompatibility); + MITK_TEST(Pod2Mitk_PointCompatibility); + + CPPUNIT_TEST_SUITE_END(); + +private: + + vtkSmartPointer a_vtkPoints; + ScalarType originalValues[3]; + ScalarType valuesToCopy[3]; + + /** + * @brief Convenience method to test if one vector has been assigned successfully to the other. + * + * More specifically, tests if v1 = v2 was performed correctly. + * + * @param v1 The vector v1 of the assignment v1 = v2 + * @param v2 The vector v2 of the assignment v1 = v2 + * @param v1Name The type name of v1 (e.g.: mitk::Vector3D). Necessary for the correct test output. + * @param v2Name The type name of v2 (e.g.: mitk::Vector3D). Necessary for the correct test output. + * @param eps defines the allowed tolerance when testing for equality. + */ + template + void TestForEquality(T1 v1, T2 v2, std::string v1Name, std::string v2Name, ScalarType eps = mitk::eps) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE("\nAssigning " + v2Name + " to " + v1Name + ":\n both are equal", true, EqualArray(v1, v2, 3, eps)); + } + + + +public: + + + void setUp(void) + { + FillVector3D(originalValues, 1.0, 2.0, 3.0); + FillVector3D(valuesToCopy, 4.0, 5.0, 6.0); + + a_vtkPoints = vtkSmartPointer::New(); + a_vtkPoints->Initialize(); + } + + void tearDown(void) + { + // a_vtkPoints = NULL; + } + + + void Mitk2Itk_PointCompatibility() + { + itk::Point itkPoint3D = originalValues; + mitk::Point3D point3D = valuesToCopy; + + itkPoint3D = point3D; + + TestForEquality(itkPoint3D, point3D, "itk::Point", "mitk:Point"); + } + + + void Itk2Mitk_PointCompatibility() + { + mitk::Point3D point3D = originalValues; + itk::Point itkPoint3D = valuesToCopy; + + point3D = itkPoint3D; + + TestForEquality(point3D, itkPoint3D, "mitk:Point", "itk::Point"); + } + + + void Vtk2Mitk_PointCompatibility() + { + mitk::Point3D point3D = originalValues; + a_vtkPoints->InsertNextPoint(valuesToCopy); + double vtkPoint[3]; + a_vtkPoints->GetPoint(0, vtkPoint); + + point3D = vtkPoint; + + TestForEquality(point3D, vtkPoint, "mitk:Point", "vtkPoint"); + + } + + + void Mitk2Pod_PointCompatibility() + { + ScalarType podPoint[] = {1.0, 2.0, 3.0}; + mitk::Point3D point3D = valuesToCopy; + + point3D.ToArray(podPoint); + + TestForEquality(podPoint, point3D, "POD point", "mitk::Point"); + + } + + void Pod2Mitk_PointCompatibility() + { + itk::Point point3D = originalValues; + ScalarType podPoint[] = {4.0, 5.0, 6.0}; + + point3D = podPoint; + + TestForEquality(point3D, podPoint, "mitk::Point3D", "POD point"); + } + + + void Vector2Point() + { + itk::Point point3D = valuesToCopy; + itk::Vector vector3D = originalValues; + + point3D = vector3D; + + TestForEquality(point3D, vector3D, "mitk::Point", "mitk::Vector"); + } + +}; + + +MITK_TEST_SUITE_REGISTRATION(mitkPointTypeConversion) + + diff --git a/Core/Code/Testing/mitkSurfaceTest.cpp b/Core/Code/Testing/mitkSurfaceTest.cpp index 66e95e50b7..d3b475963d 100644 --- a/Core/Code/Testing/mitkSurfaceTest.cpp +++ b/Core/Code/Testing/mitkSurfaceTest.cpp @@ -1,150 +1,150 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurface.h" #include "mitkCommon.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkTestingMacros.h" #include "vtkPolyData.h" #include "vtkSphereSource.h" #include int mitkSurfaceTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN("Surface"); mitk::Surface::Pointer surface = mitk::Surface::New(); MITK_TEST_CONDITION_REQUIRED( surface.GetPointer(), "Testing initialization!" ); mitk::Surface::Pointer cloneSurface = surface->Clone(); MITK_TEST_CONDITION_REQUIRED( cloneSurface.GetPointer(), "Testing clone surface initialization!" ); vtkSphereSource* sphereSource = vtkSphereSource::New(); sphereSource->SetCenter(0,0,0); sphereSource->SetRadius(5.0); sphereSource->SetThetaResolution(10); sphereSource->SetPhiResolution(10); sphereSource->Update(); vtkPolyData* polys = sphereSource->GetOutput(); MITK_TEST_CONDITION_REQUIRED(surface->GetVtkPolyData() == NULL, "Testing initial state of vtkPolyData"); surface->SetVtkPolyData( polys ); sphereSource->Delete(); MITK_TEST_CONDITION_REQUIRED(surface->GetVtkPolyData()!= NULL, "Testing set vtkPolyData"); cloneSurface= NULL; cloneSurface = surface->Clone(); MITK_TEST_CONDITION_REQUIRED(cloneSurface->GetVtkPolyData()!= NULL, "Testing set vtkPolyData of cloned surface!"); cloneSurface = NULL; double bounds[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; polys->ComputeBounds(); polys->GetBounds( bounds ); surface->UpdateOutputInformation(); surface->SetRequestedRegionToLargestPossibleRegion(); mitk::BoundingBox* bb = const_cast(surface->GetGeometry()->GetBoundingBox()); mitk::BoundingBox::BoundsArrayType surfBounds = bb->GetBounds(); bool passed = false; if ( bounds[0] == surfBounds[0] && bounds[1] == surfBounds[1] && bounds[2] == surfBounds[2] && bounds[3] == surfBounds[3] && bounds[4] == surfBounds[4] && bounds[5] == surfBounds[5] ) { passed = true; } MITK_TEST_CONDITION_REQUIRED(passed, "Testing GetBoundingBox()!"); surface->Expand(5); surface->Update(); surface->SetRequestedRegionToLargestPossibleRegion(); mitk::Surface::RegionType requestedRegion = surface->GetRequestedRegion(); MITK_TEST_CONDITION_REQUIRED(requestedRegion.GetSize(3) == 5, "Testing mitk::Surface::Expand( timesteps ): "); double boundsMat[5][6]; for (int i=0;i<5;i++) { vtkSphereSource* sphereSource = vtkSphereSource::New(); sphereSource->SetCenter(0,0,0); sphereSource->SetRadius(1.0 * (i+1.0)); sphereSource->SetThetaResolution(10); sphereSource->SetPhiResolution(10); sphereSource->Update(); sphereSource->GetOutput()->ComputeBounds(); sphereSource->GetOutput()->GetBounds( boundsMat[i] ); surface->SetVtkPolyData( sphereSource->GetOutput(),i ); sphereSource->Delete(); } surface->UpdateOutputInformation(); surface->SetRequestedRegionToLargestPossibleRegion(); passed = true; for (int i=0;i<5;i++) { mitk::BoundingBox::BoundsArrayType surfBounds = (const_cast(surface->GetTimeGeometry()->GetGeometryForTimeStep(i)->GetBoundingBox()))->GetBounds(); if ( boundsMat[i][0] != surfBounds[0] || boundsMat[i][1] != surfBounds[1] || boundsMat[i][2] != surfBounds[2] || boundsMat[i][3] != surfBounds[3] || boundsMat[i][4] != surfBounds[4] || boundsMat[i][5] != surfBounds[5] ) { passed = false; break; } } MITK_TEST_CONDITION_REQUIRED(passed, "Testing mitk::Surface::Testing 4D surface data creation!" ); const mitk::TimeGeometry* inputTimeGeometry = surface->GetUpdatedTimeGeometry(); int time = 3; int timestep=0; timestep = inputTimeGeometry->TimePointToTimeStep( time ); MITK_TEST_CONDITION_REQUIRED(time == timestep, "Testing correctness of geometry for surface->GetUpdatedTimeGeometry()!"); sphereSource = vtkSphereSource::New(); sphereSource->SetCenter(0,0,0); sphereSource->SetRadius( 100.0 ); sphereSource->SetThetaResolution(10); sphereSource->SetPhiResolution(10); sphereSource->Update(); surface->SetVtkPolyData( sphereSource->GetOutput(), 3 ); sphereSource->Delete(); inputTimeGeometry = surface->GetUpdatedTimeGeometry(); time = 3; timestep=0; timestep = inputTimeGeometry->TimePointToTimeStep( time ); MITK_TEST_CONDITION_REQUIRED(time == timestep, "Explicitly changing the data of timestep 3 and checking for timebounds correctness of surface's geometry again!"); unsigned int numberoftimesteps = surface->GetTimeSteps(); mitk::Surface::Pointer dummy = mitk::Surface::New(); dummy->Graft(surface); MITK_TEST_CONDITION_REQUIRED( dummy->GetVtkPolyData() != NULL, "Testing copying a Surface with Graft()!"); MITK_TEST_CONDITION_REQUIRED( dummy->GetTimeSteps() == numberoftimesteps, "orig-numberofTimeSteps:" << numberoftimesteps << " copy-numberofTimeSteps:" << dummy->GetTimeSteps()); surface = NULL; MITK_TEST_CONDITION_REQUIRED( surface.IsNull(), "Testing destruction of surface!"); MITK_TEST_END(); } diff --git a/Core/Code/Testing/mitkSurfaceToSurfaceFilterTest.cpp b/Core/Code/Testing/mitkSurfaceToSurfaceFilterTest.cpp index d907c0efea..7f26be3e42 100644 --- a/Core/Code/Testing/mitkSurfaceToSurfaceFilterTest.cpp +++ b/Core/Code/Testing/mitkSurfaceToSurfaceFilterTest.cpp @@ -1,121 +1,121 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurface.h" #include "mitkSurfaceToSurfaceFilter.h" #include "mitkCommon.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "vtkPolyData.h" #include "vtkSphereSource.h" #include int mitkSurfaceToSurfaceFilterTest(int /*argc*/, char* /*argv*/[]) { mitk::Surface::Pointer surface; surface = mitk::Surface::New(); vtkSphereSource* sphereSource = vtkSphereSource::New(); sphereSource->SetCenter(0,0,0); sphereSource->SetRadius(5.0); sphereSource->SetThetaResolution(10); sphereSource->SetPhiResolution(10); sphereSource->Update(); vtkPolyData* polys = sphereSource->GetOutput(); surface->SetVtkPolyData( polys ); sphereSource->Delete(); mitk::SurfaceToSurfaceFilter::Pointer filter = mitk::SurfaceToSurfaceFilter::New(); std::cout << "Testing mitk::SurfaceToSurfaceFilter::SetInput() and ::GetNumberOfInputs() : " ; filter->SetInput( surface ); if ( filter->GetNumberOfInputs() < 1 ) { std::cout<<"[FAILED] : zero inputs set "<GetInput() != surface ) { std::cout<<"[FAILED] : GetInput does not return correct input. "<GetInput(5) != NULL ) { std::cout<<"[FAILED] : GetInput returns inputs that were not set. "< is NULL" << std::endl; std::cout << "Testing whether Output is created correctly : " << std::endl; if ( filter->GetNumberOfOutputs() != filter->GetNumberOfInputs() ) { std::cout <<"[FAILED] : number of outputs != number of inputs" << std::endl; return EXIT_FAILURE; } std::cout << "[SUCCESS] : number of inputs == number of outputs." << std::endl; mitk::Surface::Pointer outputSurface = filter->GetOutput(); if ( outputSurface->GetVtkPolyData()->GetNumberOfPolys() != surface->GetVtkPolyData()->GetNumberOfPolys() ) { std::cout << "[FAILED] : number of Polys in PolyData of output != number of Polys in PolyData of input" << std::endl; return EXIT_FAILURE; } std::cout << "[SUCCESS] : number of Polys in PolyData of input and output are identical." << std::endl; filter->Update(); outputSurface = filter->GetOutput(); if ( outputSurface->GetSizeOfPolyDataSeries() != surface->GetSizeOfPolyDataSeries() ) { std::cout << "[FAILED] : number of PolyDatas in PolyDataSeries of output != number of PolyDatas of input" << std::endl; return EXIT_FAILURE; } std::cout << "[SUCCESS] : Size of PolyDataSeries of input and output are identical." << std::endl; //std::cout << "Testing RemoveInputs() : " << std::endl; //unsigned int numOfInputs = filter->GetNumberOfInputs(); //filter->RemoveInputs( mitk::Surface::New() ); //if ( filter->GetNumberOfInputs() != numOfInputs ) //{ // std::cout << "[FAILED] : input was removed that was not set." << std::endl; // return EXIT_FAILURE; //} //std::cout << "[SUCCESS] : no input was removed that was not set." << std::endl; //filter->RemoveInputs( surface ); //if ( filter->GetNumberOfInputs() != 0 ) //{ // std::cout << "[FAILED] : existing input was not removed correctly." << std::endl; // return EXIT_FAILURE; //} //std::cout << "[SUCCESS] : existing input was removed correctly." << std::endl; std::cout<<"[TEST DONE]"< +#include #include #include #include #include #include static const std::string filename = itksys::SystemTools::GetCurrentWorkingDirectory() + "/TinyXMLTest.txt"; static const std::string elementToStoreAttributeName = "DoubleTest"; static const std::string attributeToStoreName = "CommaValue"; static double calcPrecision(const unsigned int requiredDecimalPlaces) { return pow(10.0, -1.0 * ((double) requiredDecimalPlaces)); } /** * create a simple xml document which stores the values * @param valueToWrite value which should be stored * @return true, if document was successfully created. */ static bool Setup(double valueToWrite) { // 1. create simple document TiXmlDocument document; TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" ); // TODO what to write here? encoding? etc.... document.LinkEndChild( decl ); TiXmlElement* version = new TiXmlElement("Version"); version->SetAttribute("Writer", __FILE__ ); version->SetAttribute("CVSRevision", "$Revision: 17055 $" ); version->SetAttribute("FileVersion", 1 ); document.LinkEndChild(version); // 2. store one element containing a double value with potentially many after comma digits. TiXmlElement* vElement = new TiXmlElement( elementToStoreAttributeName ); vElement->SetDoubleAttribute( attributeToStoreName, valueToWrite ); document.LinkEndChild(vElement); // 3. store in file. return document.SaveFile( filename ); } static int readValueFromSetupDocument(double& readOutValue) { TiXmlDocument document; if (!document.LoadFile(filename)) { MITK_TEST_CONDITION_REQUIRED(false, "Test Setup failed, could not open " << filename); return TIXML_NO_ATTRIBUTE; } else { TiXmlElement* doubleTest = document.FirstChildElement(elementToStoreAttributeName); return doubleTest->QueryDoubleAttribute(attributeToStoreName, &readOutValue); } } /** * * @return true if TearDown was successful. */ static bool TearDown() { return !remove(filename.c_str()); } static void Test_Setup_works() { MITK_TEST_CONDITION_REQUIRED(Setup(1.0) && TearDown(), "Test if setup and teardown correctly writes data to " << filename << " and deletes the file after the test"); } /** * this first test ensures we can correctly readout values from the * TinyXMLDocument. */ static void Test_ReadOutValue_works() { Setup(1.0); double readValue; MITK_TEST_CONDITION_REQUIRED(TIXML_SUCCESS == readValueFromSetupDocument(readValue), "checking if readout mechanism works."); } static void Test_DoubleValueWriteOut() { const double valueToWrite = -1.123456; const int validDigitsAfterComma = 6; // indicates the number of valid digits after comma of valueToWrite const double neededPrecision = calcPrecision(validDigitsAfterComma + 1); double readValue; Setup(valueToWrite); readValueFromSetupDocument(readValue); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(valueToWrite, readValue, neededPrecision), std::setprecision(validDigitsAfterComma) << "Testing if value " << valueToWrite << " equals " << readValue << " which was retrieved from TinyXML document"); TearDown(); } static void Test_DoubleValueWriteOut_manyDecimalPlaces() { const double valueToWrite = -1.12345678910111; const int validDigitsAfterComma = 14; // indicates the number of valid digits after comma of valueToWrite const double neededPrecision = calcPrecision(validDigitsAfterComma + 1); double readValue; Setup(valueToWrite); readValueFromSetupDocument(readValue); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(valueToWrite, readValue, neededPrecision), std::setprecision(validDigitsAfterComma) << "Testing if value " << valueToWrite << " equals " << readValue << " which was retrieved from TinyXML document"); TearDown(); } int mitkTinyXMLTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("TinyXMLTest"); Test_Setup_works(); Test_ReadOutValue_works(); Test_DoubleValueWriteOut(); Test_DoubleValueWriteOut_manyDecimalPlaces(); MITK_TEST_END() } diff --git a/Core/Code/Testing/mitkVectorTest.cpp b/Core/Code/Testing/mitkVectorTest.cpp index 9f2b8c4256..e91de13f2c 100644 --- a/Core/Code/Testing/mitkVectorTest.cpp +++ b/Core/Code/Testing/mitkVectorTest.cpp @@ -1,139 +1,141 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include -#include +#include #include int mitkVectorTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN("mitkVector"); // test itk vector equality methods itk::Vector itkVector_1; itkVector_1[0] = 4.6; itkVector_1[1] = 9.76543; itkVector_1[2] = 746.09; itk::Vector itkVector_2; itk::Vector itkVector_3; for (int i=0; i<3; i++) { itkVector_2[i] = itkVector_1[i] - mitk::eps*1.1; itkVector_3[i] = itkVector_1[i] - mitk::eps*0.9; } MITK_TEST_CONDITION(mitk::Equal(itkVector_1,itkVector_1), "Test vector equality using the same vector with mitk::eps"); MITK_TEST_CONDITION(!mitk::Equal(itkVector_1,itkVector_2), "Test vector equality using different vectors with an element-wise difference greater than mitk::eps"); MITK_TEST_CONDITION( mitk::Equal(itkVector_1, itkVector_2, mitk::eps*1.2), "Vectors are equal for higher epsilon tolerance ( 1.2 * mitk::eps )"); MITK_TEST_CONDITION(mitk::Equal(itkVector_1,itkVector_3), "Test vector equality using different vectors with an element-wise difference less than mitk::eps"); // test itk point equality methods itk::Point itkPoint_1; itk::Point itkPoint_2; itk::Point itkPoint_3; for (int i=0; i<3; i++) { itkPoint_1[i] = itkVector_1[i]; itkPoint_2[i] = itkVector_2[i]; itkPoint_3[i] = itkVector_3[i]; } MITK_TEST_CONDITION(mitk::Equal(itkPoint_1,itkPoint_1), "Test point equality using the same point with mitk::eps"); MITK_TEST_CONDITION(!mitk::Equal(itkPoint_1,itkPoint_2), "Test point equality using different points with an element-wise difference greater than mitk::eps"); MITK_TEST_CONDITION( mitk::Equal(itkPoint_1, itkPoint_2, mitk::eps * 1.2), "Points are equal for higher epsilon tolerance ( 1.2 * mitk::eps )"); MITK_TEST_CONDITION(mitk::Equal(itkPoint_1,itkPoint_3), "Test point equality using different points with an element-wise difference less than mitk::eps"); // test mitk vnl vector equality methods mitk::VnlVector mitk_vnl_vector_1(3); mitk::VnlVector mitk_vnl_vector_2(3); mitk::VnlVector mitk_vnl_vector_3(3); for (int i=0; i<3; i++) { mitk_vnl_vector_1.put(i,itkVector_1[i]); mitk_vnl_vector_2.put(i,itkVector_2[i]); mitk_vnl_vector_3.put(i,itkVector_1[i]); } MITK_TEST_CONDITION(mitk::Equal(mitk_vnl_vector_1,mitk_vnl_vector_1), "Test mitk vnl vector equality using the same mitk vnl vector with mitk::eps"); MITK_TEST_CONDITION(!mitk::Equal(mitk_vnl_vector_1,mitk_vnl_vector_2), "Test mitk vnl vector equality using different mitk vnl vectors with an element-wise difference greater than mitk::eps"); MITK_TEST_CONDITION( mitk::Equal(mitk_vnl_vector_1, mitk_vnl_vector_2, mitk::eps*1.2), "Vnl vectors are equal for higher epsilon tolerance ( 1.2 * mitk::eps )"); MITK_TEST_CONDITION(mitk::Equal(mitk_vnl_vector_1,mitk_vnl_vector_3), "Test mitk vnl vector equality using different mitk vnl vectors with an element-wise difference less than mitk::eps"); // test vnl_vector equality method typedef mitk::ScalarType VnlValueType; vnl_vector_fixed vnlVector_1; vnlVector_1[3] = 56.98; vnlVector_1[4] = 22.32; vnlVector_1[5] = 1.00; vnlVector_1[6] = 746.09; vnl_vector_fixed vnlVector_2; vnl_vector_fixed vnlVector_3; for (int i=0; i<7; i++) { if (i<3) { vnlVector_1.put(i,itkVector_1[i]); } vnlVector_2[i] = vnlVector_1[i] - mitk::eps * 1.1f; vnlVector_3[i] = vnlVector_1[i] - mitk::eps * 0.9f; } MITK_TEST_CONDITION( (mitk::Equal(vnlVector_1,vnlVector_1)), "vnl_fixed : v_1 == v_1 "); // the v_2 is constructed so that the equality test fails for mitk::eps, the norm of the difference between the vectors is 7 * eps/6.9 MITK_TEST_CONDITION(!(mitk::Equal(vnlVector_1,vnlVector_2)), "vnl_fixed : v_1 != v_2 with mitk::eps "); // increase the epsilon value used for testing equality - should now pass ( 1.2 * mitk::eps > 7 * mitk::eps/6.9 ) MITK_TEST_CONDITION( (mitk::Equal(vnlVector_1,vnlVector_2, mitk::eps*1.2f)) , "vnl_fixed : v_1 == v_2 with eps = 1.2 * mitk::eps "); MITK_TEST_CONDITION( (mitk::Equal(vnlVector_1,vnlVector_3, mitk::eps)), "vnl_fixed : v_1 == v_3 with eps = 0.8 * mitk::eps "); MITK_TEST_CONDITION(!(mitk::Equal(vnlVector_1,vnlVector_3, mitk::eps*0.8f)), "vnl_fixed : v_1 != v_3 with eps = 0.8 * mitk::eps "); // test scalar equality method mitk::ScalarType scalar1 = 0.5689; - mitk::ScalarType scalar2 = scalar1 + mitk::eps; - mitk::ScalarType scalar3 = scalar1 + mitk::eps*0.95; + mitk::ScalarType scalar2 = scalar1 + mitk::eps*1.01; + mitk::ScalarType scalar3 = scalar1; + mitk::ScalarType scalar4 = scalar1 + mitk::eps*0.95; MITK_TEST_CONDITION(mitk::Equal(scalar1,scalar1), "Test scalar equality using the same scalar with mitk::eps"); MITK_TEST_CONDITION(!mitk::Equal(scalar1,scalar2), "Test scalar equality using the different scalars with a difference greater than mitk::eps"); - MITK_TEST_CONDITION(mitk::Equal(scalar1,scalar3), "Test scalar equality using the different scalars with a difference less than mitk::eps"); + MITK_TEST_CONDITION(mitk::Equal(scalar1,scalar3), "Test scalar equality using the different scalars with a difference equal to mitk::eps"); + MITK_TEST_CONDITION(mitk::Equal(scalar1,scalar4), "Test scalar equality using the different scalars with a difference less than mitk::eps"); // test matrix equality methods vnl_matrix_fixed vnlMatrix3x3_1; vnlMatrix3x3_1(0,0) = 1.1; vnlMatrix3x3_1(0,1) = 0.4; vnlMatrix3x3_1(0,2) = 5.3; vnlMatrix3x3_1(1,0) = 2.7; vnlMatrix3x3_1(1,1) = 3578.56418; vnlMatrix3x3_1(1,2) = 123.56; vnlMatrix3x3_1(2,0) = 546.89; vnlMatrix3x3_1(2,1) = 0.0001; vnlMatrix3x3_1(2,2) = 1.0; vnl_matrix_fixed vnlMatrix3x3_2; vnlMatrix3x3_2(0,0) = 1.1000009; vnlMatrix3x3_2(0,1) = 0.4000009; vnlMatrix3x3_2(0,2) = 5.3000009; vnlMatrix3x3_2(1,0) = 2.7000009; vnlMatrix3x3_2(1,1) = 3578.5641809; vnlMatrix3x3_2(1,2) = 123.5600009; vnlMatrix3x3_2(2,0) = 546.8900009; vnlMatrix3x3_2(2,1) = 0.0001009; vnlMatrix3x3_2(2,2) = 1.0000009; mitk::ScalarType epsilon = 0.000001; - MITK_TEST_CONDITION(mitk::MatrixEqualElementWise(vnlMatrix3x3_1,vnlMatrix3x3_1,0.0),"Test for matrix equality with given epsilon=0.0 and exactly the same matrix elements"); + MITK_TEST_CONDITION(mitk::MatrixEqualElementWise(vnlMatrix3x3_1,vnlMatrix3x3_1,mitk::eps),"Test for matrix equality with given epsilon=mitk::eps and exactly the same matrix elements"); MITK_TEST_CONDITION(!mitk::MatrixEqualElementWise(vnlMatrix3x3_1,vnlMatrix3x3_2,0.0),"Test for matrix equality with given epsilon=0.0 and slightly different matrix elements"); MITK_TEST_CONDITION(mitk::MatrixEqualElementWise(vnlMatrix3x3_1,vnlMatrix3x3_2,epsilon),"Test for matrix equality with given epsilon and slightly different matrix elements"); MITK_TEST_CONDITION(!mitk::MatrixEqualRMS(vnlMatrix3x3_1,vnlMatrix3x3_2,0.0),"Test for matrix equality with given epsilon=0.0 and slightly different matrix elements"); MITK_TEST_CONDITION(mitk::MatrixEqualRMS(vnlMatrix3x3_1,vnlMatrix3x3_2,epsilon),"Test for matrix equality with given epsilon and slightly different matrix elements"); MITK_TEST_END(); } diff --git a/Core/Code/Testing/mitkVectorTypeConversionTest.cpp b/Core/Code/Testing/mitkVectorTypeConversionTest.cpp new file mode 100644 index 0000000000..6d02c7300a --- /dev/null +++ b/Core/Code/Testing/mitkVectorTypeConversionTest.cpp @@ -0,0 +1,264 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include + +#include "itkVector.h" + +#include "mitkTestFixture.h" +#include "mitkTestingMacros.h" + +#include +#include "vnl/vnl_math.h" + +#include "mitkNumericConstants.h" +#include "mitkVector.h" +#include "mitkPoint.h" + +using namespace mitk; + + +class mitkVectorTypeConversionTestSuite : public mitk::TestFixture + +{ + + CPPUNIT_TEST_SUITE(mitkVectorTypeConversionTestSuite); + + MITK_TEST(Point2Vector); + + MITK_TEST(Pod2Mitk); + MITK_TEST(Mitk2Pod); + + MITK_TEST(OneElement2Mitk); + + MITK_TEST(Itk2Mitk); + MITK_TEST(Mitk2Itk); + + MITK_TEST(Vnlfixed2Mitk); + MITK_TEST(Mitk2Vnlfixed); + + MITK_TEST(Vnl2Mitk); + MITK_TEST(Mitk2Vnl); + MITK_TEST(Vnl2Mitk_WrongVnlVectorSize); + + MITK_TEST(ToArray_DifferentType); + MITK_TEST(Fill_DifferentType); + + CPPUNIT_TEST_SUITE_END(); + +private: + + /** + * these variables are used in the test functions + * + * The variable which should be copied into is set to its original value. + * The value which should be copied is set to valuesToCopy. + * + * Then the copying takes place. The test is successful, if the variable which + * should be copied into holds the valuesToCopy afterwards and is equal to the + * vector which should be copied. + */ + ScalarType originalValues[3]; + ScalarType valuesToCopy[3]; + + float epsDouble2Float; + + /** + * @brief Convenience method to test if one vector has been assigned successfully to the other. + * + * More specifically, tests if v1 = v2 was performed correctly. + * + * @param v1 The vector v1 of the assignment v1 = v2 + * @param v2 The vector v2 of the assignment v1 = v2 + * @param v1Name The type name of v1 (e.g.: mitk::Vector3D). Necessary for the correct test output. + * @param v2Name The type name of v2 (e.g.: mitk::Vector3D). Necessary for the correct test output. + * @param eps defines the allowed tolerance when testing for equality. + */ + template + void TestForEquality(const T1& v1, const T2& v2, const std::string& v1Name, const std::string& v2Name, const ScalarType& eps = mitk::eps) const + { + CPPUNIT_ASSERT_EQUAL_MESSAGE("\nAssigning " + v2Name + " to " + v1Name + ":\n both are equal", true, + EqualArray(v1, v2, 3, eps)); + } + +public: + + + void setUp(void) + { + FillVector3D(originalValues, 1.123456789987, 2.789456321456, 3.123654789987456); + FillVector3D(valuesToCopy, 4.654789123321, 5.987456789321, 6.321654987789546); + + epsDouble2Float = vnl_math::float_eps * 10.0; + } + + void tearDown(void) + { + + } + + + void Pod2Mitk(void) + { + mitk::Vector3D vector3D = valuesToCopy; + + TestForEquality(vector3D, valuesToCopy, "mitk::Vector3D", "double POD"); + } + + + void Mitk2Pod(void) + { + ScalarType podArray[3]; + mitk::Vector3D vector3D = valuesToCopy; + + vector3D.ToArray(podArray); + + TestForEquality(podArray, vector3D, "double POD", "mitk::Vector3D"); + } + + + void OneElement2Mitk(void) + { + double twos[] = {2.0, 2.0, 2.0}; + mitk::Vector vector3D(2.0); + + CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE( "\n one values initializes all elements to this value", EqualArray(vector3D, twos, 3)); + } + + + void Itk2Mitk(void) + { + Vector3D vector3D = originalValues; + itk::Vector itkVector = valuesToCopy; + + vector3D = itkVector; + + TestForEquality(vector3D, itkVector, "mitk::Vector3D", "itk::Vector"); + } + + + void Mitk2Itk(void) + { + Vector3D vector3D = valuesToCopy; + itk::Vector itkVector = originalValues; + + itkVector = vector3D; + + TestForEquality(itkVector, vector3D, "itk::Vector", "mitk::Vector3D"); + } + + + void Vnlfixed2Mitk(void) + { + mitk::Vector3D vector3D = originalValues; + vnl_vector_fixed vnlVectorFixed(valuesToCopy); + + vector3D = vnlVectorFixed; + + TestForEquality(vector3D, vnlVectorFixed, "mitk::Vector3D", "vnl_vector_fixed"); + } + + + void Mitk2Vnlfixed(void) + { + vnl_vector_fixed vnlVectorFixed(originalValues); + mitk::Vector3D vector3D = valuesToCopy; + + vnlVectorFixed = vector3D; + + TestForEquality(vnlVectorFixed, vector3D, "vnl_vector_fixed", "mitk::Vector3D"); + } + + + void Vnl2Mitk(void) + { + mitk::Vector3D vector3D = originalValues; + vnl_vector vnlVector(3); + vnlVector.set(valuesToCopy); + + vector3D = vnlVector; + + TestForEquality(vector3D, vnlVector, "mitk::Vector3D", "vnl_vector"); + } + + + void Mitk2Vnl(void) + { + vnl_vector vnlVector(3); + vnlVector.set(originalValues); + mitk::Vector3D vector3D = valuesToCopy; + + vnlVector = vector3D; + + TestForEquality(vnlVector, vector3D, "vnl_vector", "mitk::Vector3D"); + } + + + /** + * @brief Tests if an exception is thrown when constructing an mitk::Vector form a vnl_vector of not suited size. + */ + void Vnl2Mitk_WrongVnlVectorSize() + { + ScalarType largerValuesToCopy[] = {4.12345678910, 5.10987654321, 6.123456789132456, 7.123456987789456}; + mitk::Vector3D vector3D = originalValues; + vnl_vector vnlVector(4); + vnlVector.set(largerValuesToCopy); + + CPPUNIT_ASSERT_THROW(vector3D = vnlVector, mitk::Exception); + } + + + void ToArray_DifferentType(void) + { + float podArray[3]; + for (int var = 0; var < 3; ++var) { + podArray[var] = originalValues[var]; + } + mitk::Vector3D vector3D = valuesToCopy; + + vector3D.ToArray(podArray); + + TestForEquality(podArray, vector3D, "float POD", "mitk::Vector3D", epsDouble2Float); + } + + + void Fill_DifferentType(void) + { + mitk::Vector3D vector3D = originalValues; + float podArray[3]; + for (int var = 0; var < 3; ++var) { + podArray[var] = valuesToCopy[var]; + } + + vector3D.FillVector(podArray); + + TestForEquality(vector3D, podArray, "mitk::Vector3D", "float POD", epsDouble2Float); + } + + void Point2Vector() + { + mitk::Point3D point3D = originalValues; + mitk::Vector3D vector3D = valuesToCopy; + + vector3D = point3D.GetVectorFromOrigin(); + + TestForEquality(point3D, vector3D, "mitk::Point3D", "mitk::Vector3D"); + } + + +}; + +MITK_TEST_SUITE_REGISTRATION(mitkVectorTypeConversion) diff --git a/Core/Code/files.cmake b/Core/Code/files.cmake index 772bd25bde..f3fc897c5a 100644 --- a/Core/Code/files.cmake +++ b/Core/Code/files.cmake @@ -1,406 +1,413 @@ set(H_FILES Algorithms/itkImportMitkImageContainer.h Algorithms/itkImportMitkImageContainer.txx Algorithms/itkMITKScalarImageToHistogramGenerator.h Algorithms/itkMITKScalarImageToHistogramGenerator.txx Algorithms/mitkInstantiateAccessFunctions.h Algorithms/mitkPixelTypeList.h Algorithms/mitkPPArithmeticDec.h Algorithms/mitkPPArgCount.h Algorithms/mitkPPCat.h Algorithms/mitkPPConfig.h Algorithms/mitkPPControlExprIIf.h Algorithms/mitkPPControlIf.h Algorithms/mitkPPControlIIf.h Algorithms/mitkPPDebugError.h Algorithms/mitkPPDetailAutoRec.h Algorithms/mitkPPDetailDMCAutoRec.h Algorithms/mitkPPExpand.h Algorithms/mitkPPFacilitiesEmpty.h Algorithms/mitkPPFacilitiesExpand.h Algorithms/mitkPPLogicalBool.h Algorithms/mitkPPRepetitionDetailDMCFor.h Algorithms/mitkPPRepetitionDetailEDGFor.h Algorithms/mitkPPRepetitionDetailFor.h Algorithms/mitkPPRepetitionDetailMSVCFor.h Algorithms/mitkPPRepetitionFor.h Algorithms/mitkPPSeqElem.h Algorithms/mitkPPSeqForEach.h Algorithms/mitkPPSeqForEachProduct.h Algorithms/mitkPPSeq.h Algorithms/mitkPPSeqEnum.h Algorithms/mitkPPSeqSize.h Algorithms/mitkPPSeqToTuple.h Algorithms/mitkPPStringize.h Algorithms/mitkPPTupleEat.h Algorithms/mitkPPTupleElem.h Algorithms/mitkPPTupleRem.h Algorithms/mitkClippedSurfaceBoundsCalculator.h Algorithms/mitkExtractSliceFilter.h Algorithms/mitkConvert2Dto3DImageFilter.h Algorithms/mitkPlaneClipping.h Common/mitkCommon.h Common/mitkExceptionMacro.h DataManagement/mitkProportionalTimeGeometry.h DataManagement/mitkTimeGeometry.h DataManagement/mitkImageAccessByItk.h DataManagement/mitkImageCast.h DataManagement/mitkImagePixelAccessor.h DataManagement/mitkImagePixelReadAccessor.h DataManagement/mitkImagePixelWriteAccessor.h DataManagement/mitkImageReadAccessor.h DataManagement/mitkImageWriteAccessor.h DataManagement/mitkITKImageImport.h DataManagement/mitkITKImageImport.txx DataManagement/mitkImageToItk.h DataManagement/mitkShaderProperty.h DataManagement/mitkImageToItk.txx DataManagement/mitkTimeSlicedGeometry.h # Deprecated, empty for compatibility reasons. DataManagement/mitkPropertyListReplacedObserver.cpp + DataManagement/mitkVectorDeprecated.h + DataManagement/mitkArray.h + DataManagement/mitkQuaternion.h + DataManagement/mitkNumericTypes.h + DataManagement/mitkVector.h + DataManagement/mitkPoint.h + DataManagement/mitkMatrix.h Interactions/mitkEventMapperAddOn.h Interfaces/mitkIDataNodeReader.h Rendering/mitkLocalStorageHandler.h Rendering/Colortables/HotIron.h Rendering/Colortables/Jet.h Rendering/Colortables/PET20.h Rendering/Colortables/PETColor.h IO/mitkPixelTypeTraits.h ) set(CPP_FILES Algorithms/mitkBaseDataSource.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkCoreActivator.cpp Controllers/mitkFocusManager.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkSlicesRotator.cpp Controllers/mitkSlicesSwiveller.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkInteractorCameraController.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataStorage.cpp # DataManagement/mitkDataTree.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataNodeFactory.cpp # DataManagement/mitkDataTreeStorage.cpp DataManagement/mitkDisplayGeometry.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModeOperation.cpp DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStateTransitionOperation.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkVector.cpp - + DataManagement/mitkNumericConstants.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkMaterial.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkShaderProperty.cpp Interactions/mitkAction.cpp Interactions/mitkAffineInteractor.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCoordinateSupplier.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkDisplayPositionEvent.cpp # Interactions/mitkDisplayVectorInteractorLevelWindow.cpp # legacy, prob even now unneeded # Interactions/mitkDisplayVectorInteractorScroll.cpp Interactions/mitkEvent.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventDescription.cpp Interactions/mitkEventFactory.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkEventMapper.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkGlobalInteraction.cpp Interactions/mitkInteractor.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMovePointSetInteractor.cpp Interactions/mitkMoveBaseDataInteractor.cpp Interactions/mitkNodeDepententPointSetInteractor.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkPointSetInteractor.cpp Interactions/mitkPositionEvent.cpp Interactions/mitkPositionTracker.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkState.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateEvent.cpp Interactions/mitkStateMachine.cpp Interactions/mitkStateMachineFactory.cpp Interactions/mitkTransition.cpp Interactions/mitkWheelEvent.cpp Interactions/mitkKeyEvent.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkCrosshairPositionEvent.cpp Interactions/mitkXML2EventParser.cpp Interfaces/mitkInteractionEventObserver.cpp Interfaces/mitkIShaderRepository.cpp Interfaces/mitkIPropertyAliases.cpp Interfaces/mitkIPropertyDescriptions.cpp Interfaces/mitkIPropertyExtensions.cpp Interfaces/mitkIPropertyFilters.cpp Interfaces/mitkIPersistenceService.cpp IO/mitkBaseDataIOFactory.cpp IO/mitkCoreDataNodeReader.cpp IO/mitkDicomSeriesReader.cpp IO/mitkDicomSR_LoadDICOMScalar.cpp IO/mitkDicomSR_LoadDICOMScalar4D.cpp IO/mitkDicomSR_LoadDICOMRGBPixel.cpp IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp IO/mitkDicomSR_ImageBlockDescriptor.cpp IO/mitkDicomSR_GantryTiltInformation.cpp IO/mitkDicomSR_SliceGroupingResult.cpp IO/mitkFileReader.cpp IO/mitkFileSeriesReader.cpp IO/mitkFileWriter.cpp # IO/mitkIpPicGet.c IO/mitkImageGenerator.cpp IO/mitkImageWriter.cpp IO/mitkImageWriterFactory.cpp IO/mitkItkImageFileIOFactory.cpp IO/mitkItkImageFileReader.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkItkPictureWrite.cpp IO/mitkIOUtil.cpp IO/mitkLookupTableProperty.cpp IO/mitkOperation.cpp # IO/mitkPicFileIOFactory.cpp # IO/mitkPicFileReader.cpp # IO/mitkPicFileWriter.cpp # IO/mitkPicHelper.cpp # IO/mitkPicVolumeTimeSeriesIOFactory.cpp # IO/mitkPicVolumeTimeSeriesReader.cpp IO/mitkPixelType.cpp IO/mitkPointSetIOFactory.cpp IO/mitkPointSetReader.cpp IO/mitkPointSetWriter.cpp IO/mitkPointSetWriterFactory.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSTLFileIOFactory.cpp IO/mitkSTLFileReader.cpp IO/mitkSurfaceVtkWriter.cpp IO/mitkSurfaceVtkWriterFactory.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkVtiFileIOFactory.cpp IO/mitkVtiFileReader.cpp IO/mitkVtkImageIOFactory.cpp IO/mitkVtkImageReader.cpp IO/mitkVtkSurfaceIOFactory.cpp IO/mitkVtkSurfaceReader.cpp IO/vtkPointSetXMLParser.cpp IO/mitkLog.cpp Rendering/mitkBaseRenderer.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkRenderWindowFrame.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkGLMapper.cpp Rendering/mitkGradientBackground.cpp Rendering/mitkManufacturerLogo.cpp Rendering/mitkMapper.cpp Rendering/mitkPointSetGLMapper2D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkSurfaceGLMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVolumeDataVtkMapper3D.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp Rendering/mitkOverlay.cpp Rendering/mitkVtkOverlay.cpp Rendering/mitkVtkOverlay2D.cpp Rendering/mitkVtkOverlay3D.cpp Rendering/mitkOverlayManager.cpp Rendering/mitkAbstractOverlayLayouter.cpp Rendering/mitkTextOverlay2D.cpp Rendering/mitkTextOverlay3D.cpp Rendering/mitkLabelOverlay3D.cpp Rendering/mitkOverlay2DLayouter.cpp Rendering/mitkScaleLegendOverlay Common/mitkException.cpp Common/mitkCommon.h Common/mitkCoreObjectFactoryBase.cpp Common/mitkCoreObjectFactory.cpp Common/mitkCoreServices.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigPACS.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigMITK.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml ) diff --git a/Core/Documentation/Doxygen/Concepts/BasicDataTypes.dox b/Core/Documentation/Doxygen/Concepts/BasicDataTypes.dox new file mode 100644 index 0000000000..d35e6a6d97 --- /dev/null +++ b/Core/Documentation/Doxygen/Concepts/BasicDataTypes.dox @@ -0,0 +1,60 @@ +/** +\page BasicDataTypesPage Numeric MITK data types and their usage. + +This page describes how to use very foundational data-tyes in MITK like mitk::Vector, mitk::Point and mitk::Matrix and +how they can interact. + +\tableofcontents + +\section Structure Structure + +The previously known, monolythic structure of putting every basic type into mitkVector.h has been broken and +mitkVector.h has been split up into several, more atomic files, namely: + +-# mitkNumericConstants.h : contains basic constants like mitk::ScalarType or mitk::eps +-# mitkArray.h : copy itk::FixedArrays (like itk::Point and itk::Vector) from and to types which implement the [] operator (array-types), like e.g. Plain Old Datatypes (POD) or the opencv vector +-# mitkPoint.h : the mitk::Point class. This is basically the itk::Point with the added ToArray and Fill members to conveniently copy from and to array-types. In MITK, a point is considered a fixed geometric location and thus cannot be summed or multiplied. +-# mitkVector.h : the mitk::Vector class. This is an itk::Vector, but with the possiblity to implicitly convert to vnl_vector and vnl_vector_fixed. In MITK, vectors denote directions and can be summed or multiplied with scalars. +-# mitkMatrix.h : the mitk::Matrix class. This is an itk::Matrix with the added ToArray and Fill members to conveniently copy from and to array-types. +-# mitkQuaternion.h : a typedef to vnl_quaternion. +-# mitkAffineTransform3D.h : a typedef to itk::AffineGeometryFrame +-# mitkNumericTypes.h : this file includes all of the above as a convenience header + +The Equal methods to compare Points, Vectors, Matrices, ... have been moved into the respective files. +E.g., if you want to compare two vectors simply use the Equal method provided by mitkVector.h. + +\section Conversion Conversion between the data-types + +If you want to convert a mitk::Vector from or to a vnl_vector or a vnl_vector_fixed, simply write + +\code + mitkVector3D = vnlVector3D; + vnlVector3D_2 = mitkVector3D; +\endcode + +Unfortunately this mechanism couldn't be implemented to every type of conversion. But in any case, the ToArray and FillVector/FillPoint/FillMatrix member +functions can be used to convert to array-types. E.g., + +\code + cv::Vec3d cvVec3D; + + mitkVector3D.ToArray(cvVec3D); + mitkVector3D_2.FillVector(cvVec3D); +\endcode + +No implicit conversion from mitk::Point to mitk::Vector was implemented as this would break with itk's +concept of separating points and vectors. If you want to convert, use: + +\code + mitkVector3D = mitkPoint3D.GetVectorFromOrigin(); + mitkPoint3D_2 = mitkVector3D; +\endcode + +more examples of how to convert between data types can be found in the tests: + +-# mitkArrayTypeConversionTest.cpp +-# mitkPointTypeConversionTest.cpp +-# mitkVectorTypeConversionTest.cpp +-# mitkMatrixTypeConversionTest.cpp + +*/ \ No newline at end of file diff --git a/Core/Documentation/Doxygen/Concepts/Concepts.dox b/Core/Documentation/Doxygen/Concepts/Concepts.dox index d6ec0293cd..549334e725 100644 --- a/Core/Documentation/Doxygen/Concepts/Concepts.dox +++ b/Core/Documentation/Doxygen/Concepts/Concepts.dox @@ -1,31 +1,32 @@ /** \page Concepts MITK Concepts The following items describe some issues about MITK on a more abstract level. -# \subpage OverviewPage -# \subpage CodingPage "Coding Concepts" -# \ref CodingPageGeneral -# \ref CodingPageStyle -# \ref CodingPageMITKMacros -# \subpage MicroServices_Overview -# Data Concepts + -# \subpage BasicDataTypesPage -# \subpage DataManagementPage -# \subpage MitkImagePage -# \subpage PropertiesPage -# \subpage GeometryOverviewPage -# \subpage PipelineingConceptPage -# \subpage OverlaysPage -# \subpage PersistenceConceptPage -# \subpage QVTKRendering -# Interaction -# \subpage DataInteractionPage -# \subpage InteractionPage -# \subpage LoggingPage -# \subpage ExceptionPage -# \subpage ModularizationPage "Modularization Concept" -# \ref ModularizationPageOverview -# \ref ModularizationPageHowTo If you want to start using MITK, you also want to see the chapter \ref Development. */ diff --git a/Core/Documentation/Doxygen/Concepts/GeometryOverview.dox b/Core/Documentation/Doxygen/Concepts/GeometryOverview.dox index d0327a4b4e..e755504527 100644 --- a/Core/Documentation/Doxygen/Concepts/GeometryOverview.dox +++ b/Core/Documentation/Doxygen/Concepts/GeometryOverview.dox @@ -1,134 +1,134 @@ namespace mitk{ /** \page GeometryOverviewPage Geometry Overview \tableofcontents \section GeometryOverviewPage_Introduction Introduction to Geometries Geometries are used to describe the geometrical properties of data objects in space and time.\n To use the geometry classes in the right way you have to understand the three different coordinate types present in MITK:\n\n \imageMacro{CoordinateTypes.png,"",16}
The different coordinate types\n\n
\n -# World coordinates: - World coordinates are describing the actual spacial position of all MITK objects regarding a global coordinate system, normally specified by the imaging modality - World coordinates are represented by mitk::Point3D objects. - The geometry defines the offset, orientation, and scale of the considered data objects in reference to the world coordinate systems. - World coordinates are always measured in mm - If you are dealing with an image geometry, the origin of an image is pointing to the CENTER of the bottom-left-back voxel.\n - If you are NOT dealing with an image geometry (no defined discrete Voxels), the origin is pointing to the bottom-left-back CORNER - Index coordinates can be converted to world coordinates by calling BaseGeometry::IndexToWorld()\n\n \imageMacro{worldcoordinateSystem.png,"",16}
Corner-based coordinates\n\n
\imageMacro{WorldcoordinateSystemCenterBased.png,"",16}
Center-based image-coordinates\n\n
\n -# Continuous index coordinates: - Dividing world coordinates through the pixel spacing and simultanously taking the offset into account leads to continuous index coordinates inside your dataobject.\n So continuous coordinates can be float values! - Continuous index coordinates are represented by mitk::Point3D objects. - They can be obtained by calling BaseGeometry::WorldToIndex(), where &pt_mm is a point in worldcoordinates.\n -# Index coordinate system: - Index coordinates are discrete values that address voxels of a data object explicitly. - - Index coordinates are represented by mitk::Index3D objects. + - Index coordinates are represented by itk::Index<3> objects. - Basically they are continuous index coordinates which are rounded from half integer up. - E.g. (0,0) specifies the very first pixel of a 2D image, (0,1) the pixel of the next column in the same row - If you have world coordinates, they can be converted to discrete index coordinates by calling BaseGeometry::WorldToIndex()\n\n \section GeometryOverviewPage_PointsAndVector Difference between Points and Vectors Like ITK, MITK differenciate between points and vectors. A point defines a position in a coordinate system while a vector is the distance between two points. Therefore points and vectors behave different if a coordinate transformation is applied. An offest in a coordinate transformation will affect a transformed point but not a vector. An Example:\n If two systems are given, which differ by a offset of (1,0,0). The point A(2,2,2) in system one will correspont to point A'(3,2,2) in the second system. But a vector a(2,2,2) will correspond to the vector a'(2,2,2). \section GeometryOverviewPage_Concept The Geometry Concept As the superclass of all MITK geometries BaseGeometry holds: - a spacial bounding box which is axes-parallel in index coordinates (often discrete indices of pixels), to be accessed by BaseGeometry::GetBoundingBox() - a time related bounding box which holds the temporal validity of the considered data object in milliseconds (start and end time), to be accessed by BaseGeometry::GetTimeBounds().\n The default for 3D geometries is minus infinity to plus infinity, meaning the object is always displayed independent of displayed time in MITK. - position information in form of a Euclidean transform in respect to world coordinates (i.e. a linear transformation matrix and offset) to convert (discrete or continuous) index coordinates to world coordinates and vice versa,\n to be accessed by BaseGeometry::GetIndexToWorldTransform()\n See also: \ref GeometryOverviewPage_Introduction "Introduction to Geometries" - Many other properties (e.g. origin, extent, ...) which can be found in the \ref BaseGeometry "class documentation" - VERY IMPORTANT:\n A flag called isImageGeometry, which indicates whether the coordinates are center-based or not!\n See also: \ref GeometryOverviewPage_Introduction "Introduction to Geometries" and \ref GeometryOverviewPage_Putting_Together "IMPORTANT: Putting it together for an Image"\n\n Every data object (sub-)class of BaseData has a TimeGeometry which is accessed by BaseData::GetTimeGeometry(). This TimeGeometry holds one or more BaseGeometry objects which describes the object at specific time points, e.g. provides conversion between world and index coordinates and contains bounding boxes covering the area in which the data are placed. There is the possibility of using different implementations of the abstract TimeGeometry class which may differ in how the time steps are saved and the times are calculated. There are two ways to represent a time, either by a TimePointType or a TimeStepType. The first is similar to the continous index coordinates and defines a Time Point in milliseconds from timepoint zero. The second type is similar to index coordinates. These are discrete values which specify the number of the current time step going from 0 to GetNumberOfTimeSteps(). The conversion between a time point and a time step is done by calling the method TimeGeometry::TimeStepToTimePoint() or TimeGeometry::TimePointToTimeStep(). Note that the duration of a time step may differ from object to object, so in general it is better to calculate the corresponding time steps by using time points. Also the distance of the time steps does not need to be equidistant over time, it depends on the used TimeGeometry implementation. Each TimeGeometry has a bounding box covering the whole area in which the corresponding object is situated during all time steps. This bounding box may be accessed by calling TimeGeometry::GetBoundingBoxInWorld() and is always in world coordinates. The bounding box is calculated from all time steps, to manually start this calculation process call TimeGeometry::Update(). The bounding box is not updated if the getter is called. The TimeGeometry does not provide a transformation of world coordinates into image coordinates since each time step may has a different transformation. If a conversion between image and world is needed, the BaseGeometry for a specific time step or time point must be fetched either by TimeGeometry::GetGeometryForTimeStep() or TimeGeometry::GetGeometryForTimePoint() and then the conversion is calculated by using this geometry. The TimeGeometry class is an abstract class therefore it is not possible to instantiate it. Instead a derived class must be used. Currently the only class that can be chosen is ProportionalTimeGeometry() which assumes that the time steps are ordered equidistant. To initialize an object with given geometries call ProportionalTimeGeometry::Initialize() with an existing BaseGeometry and the number of time steps. The given geometries will be copied and not referenced! Also, the BaseGeometry is an abstract class and derived classes must be used. The most simple implementation, i.e. the one to one implementation of the BaseGeometry class, is the class Geometry3D. SlicedGeometry3D is a sub-class of BaseGeometry, which describes data objects consisting of slices, e.g., objects of type Image (or SlicedData, which is the super-class of Image). Therefore, Image::GetTimeGeometry() will contain a list of SlicedGeometry3D instances. There is a special method SlicedData::GetSlicedGeometry(t) which directly returns\n a SlicedGeometry3D to avoid the need of casting. The class SlicedGeometry3D contains a list of PlaneGeometry objects describing the slices in the image.We have here spatial steps from 0 to GetSlices(). SlicedGeometry3D::InitializeEvenlySpaced (PlaneGeometry *planeGeometry, unsigned int slices) initializes a stack of slices with the same thickness, one starting at the position where the previous one ends. PlaneGeometry provides methods for working with 2D manifolds (i.e., simply spoken, an object that can be described using a 2D coordinate-system) in 3D space.\n For example it allows mapping of a 3D point on the 2D manifold using PlaneGeometry::Map(). \n An important subclass of PlaneGeometry is the DisplayGeometry which describes the geometry of the display (the monitor screen). Basically it represents a rectangular view on a 2D world geometry.\n The DisplayGeometry converts between screen and world coordinates, processes input events (e.g. mouse click) and provides methods for zooming and panning.\n \imageMacro{DisplayGeometry.png,"",16}
Display Geometry\n\n
Finally there is the AbstractTransformGeometry which describes a 2D manifold in 3D space, defined by a vtkAbstractTransform. It is a abstract superclass for arbitrary user defined geometries.\n An example is the ThinPlateSplineCurvedGeometry.\n \subsection GeometryOverviewPage_Putting_Together IMPORTANT: Putting it together for an Image Please read this section accurately if you are working with Images! The definition of the position of the corners of an image is different than the one of other data objects: As mentioned in the previous section, world coordinates of data objects (e.g. surfaces ) usually specify the bottom left back corner of an object. In contrast to that a geometry of an Image is center-based, which means that the world coordinates of a voxel belonging to an image points to the center of that voxel. E.g: \imageMacro{PixelCenterBased.png,"",16}
Center-based voxel\n\n
If the origin of e.g. a surface lies at (15,10,0) in world coordinates, the origin`s world coordinates for an image are internally calculated like the following:
(15-0.5*X-Spacing\n 10-0.5*Y-Spacing\n 0-0.5*Z-Spacing)\n
If the image`s spacing is (x,y,z)=(1,1,3) then the corner coordinates are (14.5,9.5,-1.5). If your geometry describes an image, the member variable isImageGeometry must be changed to true. This variable indicates also if your geometry is center-based or not.\n The change can be done in two ways:\n -# You are sure that your origin is already center-based. Whether because you adjusted it manually or you copied it from another image.\n In that case, you can call the function setImageGeometry(true) or imageGeometryOn() to set the bool variable to true. -# You created a new geometry, did not manually adjust the origin to be center-based and have the bool value isImageGeometry set to false (default).\n In that case, call the function ChangeImageGeometryConsideringOriginOffset(true). It will adjust your origin automatically and set the bool flag to true.\n If you experience displaced contours, figures or other stuff, it is an indicator that you have not considered the origin offset mentioned above.\n\n An image has a TimeGeometry, which contains one or more SlicedGeometry3D instances (one for each time step), all of which contain one or more instances of (sub-classes of) PlaneGeometry.\n As a reminder: Geometry instances referring to images need a slightly different definition of corners, see BaseGeometry::SetImageGeometry. This is usualy automatically called by Image.\n\n \section GeometryOverviewPage_Connection Connection between MITK, ITK and VTK Geometries \imageMacro{ITK_VTK_MITK_Geometries.png,"",16} \n\n - VTK transformation for rendering - ITK transformation for calculations - Both automatically updated when one is changed\n Attention:Not automatically updated when changed hardcoded. Example: geometry->GetVtkMatrix()->Rotate(....) */ } diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/regiongrowing.xpm b/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/regiongrowing.xpm index 5ba3fbe102..45304646ee 100644 --- a/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/regiongrowing.xpm +++ b/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/regiongrowing.xpm @@ -1,53 +1,53 @@ -/* XPM */ -static const char * icon_xpm[] = { -"28 26 24 1", -" c None", -". c #000000", -"+ c #639A31", -"@ c #316500", -"# c #6B9A31", -"$ c #94CF63", -"% c #396500", -"& c #9CCB6B", -"* c #94CB63", -"= c #9CCB63", -"- c #6B9639", -"; c #316900", -"> c #949A94", -", c #9C969C", -"' c #6B656B", -") c #CECBCE", -"! c #9C9A9C", -"~ c #393039", -"{ c #313431", -"] c #C6CFC6", -"^ c #636963", -"/ c #636563", -"( c #9C6500", -"_ c #946500", -" .. ", -" .+@. ", -" .#$%. ", -" .&+. .... ", -" .+. .+&*@. ", -" . .=$#+%. ", -" .$.... ", -" . ", -" ............ ", -" ..+*&=+&++-+++.. ", -" .=-;%;@%@%;@@%;-+. ... ", -" .+@@@@%;@;%;%;@@&+. .++-. ", -" .%@-+=&+-++#++-*#+. .&+@.", -" .>>@;@;;@;#+%+;@;-. .&%.", -" .,'))>!'>'=+%;'~{+. .;.", -" .>!])'!^!{-+;//{{-@. . ", -" .!'))>!'{~;&@'{/~+@. ", -" .(>>)]'>{{/'@/{'{{+%. ", -"._{{{{{{~{,'^'/'/{~@@. ", -" .(,>))/>/!//^'^{'{.. ", -" .>/])>,/>'/'/~^{{. ", -" .!,>>'>'>/^/^'/~{./^ ", -" .{{!'!!'>''''/~{{.''//'/ ", -" /..{{~{{~{{{{{{../^^/'^'' ", -" ^/............'^/''^'// ", -" /'^'/^'//^//'// "}; +/* XPM */ +static const char * icon_xpm[] = { +"28 26 24 1", +" c None", +". c #000000", +"+ c #639A31", +"@ c #316500", +"# c #6B9A31", +"$ c #94CF63", +"% c #396500", +"& c #9CCB6B", +"* c #94CB63", +"= c #9CCB63", +"- c #6B9639", +"; c #316900", +"> c #949A94", +", c #9C969C", +"' c #6B656B", +") c #CECBCE", +"! c #9C9A9C", +"~ c #393039", +"{ c #313431", +"] c #C6CFC6", +"^ c #636963", +"/ c #636563", +"( c #9C6500", +"_ c #946500", +" .. ", +" .+@. ", +" .#$%. ", +" .&+. .... ", +" .+. .+&*@. ", +" . .=$#+%. ", +" .$.... ", +" . ", +" ............ ", +" ..+*&=+&++-+++.. ", +" .=-;%;@%@%;@@%;-+. ... ", +" .+@@@@%;@;%;%;@@&+. .++-. ", +" .%@-+=&+-++#++-*#+. .&+@.", +" .>>@;@;;@;#+%+;@;-. .&%.", +" .,'))>!'>'=+%;'~{+. .;.", +" .>!])'!^!{-+;//{{-@. . ", +" .!'))>!'{~;&@'{/~+@. ", +" .(>>)]'>{{/'@/{'{{+%. ", +"._{{{{{{~{,'^'/'/{~@@. ", +" .(,>))/>/!//^'^{'{.. ", +" .>/])>,/>'/'/~^{{. ", +" .!,>>'>'>/^/^'/~{./^ ", +" .{{!'!!'>''''/~{{.''//'/ ", +" /..{{~{{~{{{{{{../^^/'^'' ", +" ^/............'^/''^'// ", +" /'^'/^'//^//'// "}; diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/viewInitializationIcon.xpm b/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/viewInitializationIcon.xpm index 11ccbcc699..819882324c 100644 --- a/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/viewInitializationIcon.xpm +++ b/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/viewInitializationIcon.xpm @@ -1,17 +1,17 @@ -/* XPM */ -static const char * icon_xpm[] = { -"11 11 3 1", -" c None", -". c #CBCBCB", -"+ c #404040", -"+++++++++++", -"+..+...+..+", -"+++++++++++", -"+..+...+..+", -"+..+...+..+", -"+++++++++++", -"+..+...+..+", -"+..+...+..+", -"+++++++++++", -"+..+...+..+", -"+++++++++++"}; +/* XPM */ +static const char * icon_xpm[] = { +"11 11 3 1", +" c None", +". c #CBCBCB", +"+ c #404040", +"+++++++++++", +"+..+...+..+", +"+++++++++++", +"+..+...+..+", +"+..+...+..+", +"+++++++++++", +"+..+...+..+", +"+..+...+..+", +"+++++++++++", +"+..+...+..+", +"+++++++++++"}; diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/resources/surfaceUtilities.xpm b/Examples/Plugins/org.mitk.example.gui.imaging/resources/surfaceUtilities.xpm index b43aea934e..d0cf3deb46 100644 --- a/Examples/Plugins/org.mitk.example.gui.imaging/resources/surfaceUtilities.xpm +++ b/Examples/Plugins/org.mitk.example.gui.imaging/resources/surfaceUtilities.xpm @@ -1,3837 +1,3837 @@ -/* XPM */ -static char * surfaceUtilities_xpm[] = { -"200 168 3666 2", -" c None", -". c #58789E", -"+ c #627D9E", -"@ c #456E9F", -"# c #4775AD", -"$ c #4D7099", -"% c #7089A8", -"& c #466FA2", -"* c #4E80BC", -"= c #4F81BD", -"- c #4979B2", -"; c #466891", -"> c #738DAB", -", c #436B9C", -"' c #4F81BE", -") c #456FA1", -"! c #4877AF", -"~ c #4C6E97", -"{ c #456EA0", -"] c #466991", -"^ c #6E88A7", -"/ c #4E7FBC", -"( c #4978B1", -"_ c #4F719A", -": c #647FA0", -"< c #4670A2", -"[ c #4A6C95", -"} c #446D9E", -"| c #486A93", -"1 c #6A84A4", -"2 c #436C9C", -"3 c #4D7FBC", -"4 c #4977B0", -"5 c #50729B", -"6 c #6882A3", -"7 c #4978B0", -"8 c #446C9D", -"9 c #6681A1", -"0 c #4D7FBB", -"a c #6E87A7", -"b c #476991", -"c c #416897", -"d c #4C7CB7", -"e c #4C7DB8", -"f c #4D7EB9", -"g c #4E7FBA", -"h c #4776AD", -"i c #4B6D95", -"j c #507198", -"k c #4E719C", -"l c #50749F", -"m c #50749E", -"n c #466C99", -"o c #4F82BF", -"p c #416A9D", -"q c #50739D", -"r c #4D6F98", -"s c #5082BF", -"t c #3D6596", -"u c #496C95", -"v c #6784A6", -"w c #607DA1", -"x c #53739A", -"y c #3A608E", -"z c #6380A3", -"A c #53749D", -"B c #4170A6", -"C c #56779F", -"D c #4774A9", -"E c #4B6E97", -"F c #4979B3", -"G c #4B7BB6", -"H c #55779E", -"I c #4672A8", -"J c #4E80BD", -"K c #446D9F", -"L c #627FA1", -"M c #52739C", -"N c #4A7AB4", -"O c #4F82BE", -"P c #3F689A", -"Q c #627FA2", -"R c #52739B", -"S c #4575AE", -"T c #5081BE", -"U c #4C6E96", -"V c #456994", -"W c #476B95", -"X c #56769D", -"Y c #59789D", -"Z c #486B94", -"` c #4F80BC", -" . c #4E7FBB", -".. c #436C9D", -"+. c #637FA1", -"@. c #3A618F", -"#. c #3E6696", -"$. c #3E6695", -"%. c #3E6595", -"&. c #3D6594", -"*. c #416795", -"=. c #426896", -"-. c #416794", -";. c #3D6290", -">. c #3F638F", -",. c #3D618D", -"'. c #527298", -"). c #54759D", -"!. c #4878B2", -"~. c #496B95", -"{. c #4E81BC", -"]. c #3B6290", -"^. c #3F6797", -"/. c #406898", -"(. c #406797", -"_. c #3B6291", -":. c #395E8D", -"<. c #436691", -"[. c #54749A", -"}. c #50719A", -"|. c #4675AE", -"1. c #4D7EBA", -"2. c #446FA2", -"3. c #4F76A5", -"4. c #4E76A5", -"5. c #4770A2", -"6. c #4D7FBA", -"7. c #4571A7", -"8. c #4C74A3", -"9. c #4870A1", -"0. c #4A79B1", -"a. c #466FA0", -"b. c #3C6494", -"c. c #456995", -"d. c #486A95", -"e. c #4A6B93", -"f. c #4B7BB5", -"g. c #416A9C", -"h. c #406897", -"i. c #3F6696", -"j. c #3E6391", -"k. c #456791", -"l. c #5C7A9F", -"m. c #55769E", -"n. c #4777B0", -"o. c #3A6191", -"p. c #436792", -"q. c #517199", -"r. c #4E7099", -"s. c #4676AF", -"t. c #4F81BC", -"u. c #3C6392", -"v. c #436894", -"w. c #476892", -"x. c #4F7199", -"y. c #4F80BD", -"z. c #6380A2", -"A. c #3F6695", -"B. c #3B608E", -"C. c #56759A", -"D. c #55769D", -"E. c #436B9D", -"F. c #3F6593", -"G. c #4A6B94", -"H. c #3E6797", -"I. c #3D618E", -"J. c #92908A", -"K. c #828079", -"L. c #817E77", -"M. c #7D7A72", -"N. c #7C7972", -"O. c #7C7971", -"P. c #7C7970", -"Q. c #7A7770", -"R. c #79766F", -"S. c #79766E", -"T. c #78746D", -"U. c #76746D", -"V. c #5D6A78", -"W. c #436D9F", -"X. c #4876AF", -"Y. c #466893", -"Z. c #3E6491", -"`. c #476B97", -" + c #406693", -".+ c #436996", -"++ c #3D6494", -"@+ c #3D6493", -"#+ c #3D628F", -"$+ c #A8A7A3", -"%+ c #8E8C85", -"&+ c #88857E", -"*+ c #89867E", -"=+ c #807D76", -"-+ c #817F77", -";+ c #838078", -">+ c #827F78", -",+ c #838079", -"'+ c #827F77", -")+ c #807E76", -"!+ c #7F7C75", -"~+ c #7F7D75", -"{+ c #7E7B74", -"]+ c #7D7B73", -"^+ c #636E79", -"/+ c #3C679B", -"(+ c #4A6C97", -"_+ c #56769C", -":+ c #4B6D97", -"<+ c #436692", -"[+ c #385F8E", -"}+ c #6682A4", -"|+ c #BFBEBA", -"1+ c #AEADA8", -"2+ c #83817A", -"3+ c #807D75", -"4+ c #848179", -"5+ c #86837B", -"6+ c #84817A", -"7+ c #85827B", -"8+ c #7E7C74", -"9+ c #7E7A72", -"0+ c #66707A", -"a+ c #3F6A9D", -"b+ c #4E80BB", -"c+ c #4A79B3", -"d+ c #436590", -"e+ c #5B799F", -"f+ c #527299", -"g+ c #3E618D", -"h+ c #3E6594", -"i+ c #3F6798", -"j+ c #A7A5A0", -"k+ c #8F8D86", -"l+ c #908D86", -"m+ c #817E76", -"n+ c #827F79", -"o+ c #88857D", -"p+ c #87847B", -"q+ c #85827A", -"r+ c #87847C", -"s+ c #7D7A73", -"t+ c #7C7871", -"u+ c #5F6B79", -"v+ c #426C9F", -"w+ c #4876AE", -"x+ c #57769B", -"y+ c #446691", -"z+ c #426692", -"A+ c #426795", -"B+ c #406899", -"C+ c #3C6290", -"D+ c #8D8B84", -"E+ c #87847D", -"F+ c #7B7871", -"G+ c #7A7870", -"H+ c #7A7670", -"I+ c #616C77", -"J+ c #3C679C", -"K+ c #496C96", -"L+ c #52739A", -"M+ c #3F6490", -"N+ c #3A6190", -"O+ c #9E9B97", -"P+ c #8A8680", -"Q+ c #837F77", -"R+ c #86847C", -"S+ c #86837C", -"T+ c #79776F", -"U+ c #78766E", -"V+ c #79756D", -"W+ c #616B75", -"X+ c #416C9E", -"Y+ c #4A79B2", -"Z+ c #42648F", -"`+ c #436591", -" @ c #3E6492", -".@ c #446792", -"+@ c #95938D", -"@@ c #8E8B85", -"#@ c #7F7C74", -"$@ c #7C7A72", -"%@ c #7D7972", -"&@ c #5F6C7A", -"*@ c #406B9E", -"=@ c #4778B2", -"-@ c #4777B1", -";@ c #4776B0", -">@ c #4877B0", -",@ c #4C7CB6", -"'@ c #4A6B95", -")@ c #3F6592", -"!@ c #496B94", -"~@ c #A09E9A", -"{@ c #84827B", -"]@ c #828078", -"^@ c #7E7B75", -"/@ c #817D77", -"(@ c #69737D", -"_@ c #3B669B", -":@ c #4B7CB7", -"<@ c #42648C", -"[@ c #56697E", -"}@ c #54677C", -"|@ c #53667C", -"1@ c #52657C", -"2@ c #53677C", -"3@ c #54677D", -"4@ c #55687E", -"5@ c #54687E", -"6@ c #5E7289", -"7@ c #617EA1", -"8@ c #466C9A", -"9@ c #4D7EB8", -"0@ c #3E6799", -"a@ c #4F729A", -"b@ c #4D709A", -"c@ c #4673A9", -"d@ c #4A6C96", -"e@ c #395F8D", -"f@ c #406593", -"g@ c #8B8882", -"h@ c #7E7B73", -"i@ c #838077", -"j@ c #848078", -"k@ c #67727D", -"l@ c #436DA0", -"m@ c #4A7BB6", -"n@ c #4E6887", -"o@ c #807B70", -"p@ c #7B786F", -"q@ c #79756C", -"r@ c #76726A", -"s@ c #757168", -"t@ c #736F67", -"u@ c #726E66", -"v@ c #726D66", -"w@ c #827E78", -"x@ c #96938D", -"y@ c #5E7999", -"z@ c #3F6595", -"A@ c #3D6595", -"B@ c #57779E", -"C@ c #84827A", -"D@ c #838179", -"E@ c #63707E", -"F@ c #3E699D", -"G@ c #4E6987", -"H@ c #817C73", -"I@ c #78756E", -"J@ c #77756D", -"K@ c #76736C", -"L@ c #74716B", -"M@ c #726F68", -"N@ c #52667E", -"O@ c #4C7EB9", -"P@ c #496B96", -"Q@ c #4F6F97", -"R@ c #3B618F", -"S@ c #89867F", -"T@ c #84807A", -"U@ c #837F78", -"V@ c #87857D", -"W@ c #7D7B72", -"X@ c #837F79", -"Y@ c #6A747F", -"Z@ c #3C689C", -"`@ c #4F6A89", -" # c #868178", -".# c #827F76", -"+# c #566A82", -"@# c #4C7DB9", -"## c #4A7AB3", -"$# c #3F638E", -"%# c #3C6493", -"&# c #55749B", -"*# c #93918D", -"=# c #8A877F", -"-# c #848079", -";# c #65717E", -"># c #436EA0", -",# c #516C8A", -"'# c #8E897F", -")# c #8B8880", -"!# c #8A887F", -"~# c #89867D", -"{# c #8B887F", -"]# c #5A6E86", -"^# c #436690", -"/# c #3E6593", -"(# c #3F6898", -"_# c #3C608D", -":# c #8A8981", -"<# c #8A8780", -"[# c #89857D", -"}# c #697582", -"|# c #8D887E", -"1# c #8B8780", -"2# c #8C8981", -"3# c #8D8A82", -"4# c #8E8B83", -"5# c #908C84", -"6# c #8F8B83", -"7# c #8D8B83", -"8# c #5C7188", -"9# c #4C6C95", -"0# c #908E88", -"a# c #8C8980", -"b# c #8B8881", -"c# c #86827C", -"d# c #8C8881", -"e# c #8A867E", -"f# c #6D7882", -"g# c #396397", -"h# c #516B89", -"i# c #918E85", -"j# c #949189", -"k# c #98948C", -"l# c #939189", -"m# c #5D7289", -"n# c #426C9E", -"o# c #456793", -"p# c #39608E", -"q# c #3E6493", -"r# c #507097", -"s# c #8B8983", -"t# c #86847D", -"u# c #8C8982", -"v# c #8A8880", -"w# c #86837D", -"x# c #88857C", -"y# c #88847D", -"z# c #667483", -"A# c #566C85", -"B# c #8C887E", -"C# c #85837B", -"D# c #908D85", -"E# c #53729A", -"F# c #3F6796", -"G# c #537299", -"H# c #898781", -"I# c #898780", -"J# c #8A867F", -"K# c #8D8982", -"L# c #7E7F7D", -"M# c #8B877F", -"N# c #8F8C84", -"O# c #4D6D96", -"P# c #3B5F8A", -"Q# c #8E8C87", -"R# c #8D8A83", -"S# c #8C8983", -"T# c #8E8B82", -"U# c #8E8B84", -"V# c #908C85", -"W# c #918E86", -"X# c #928F87", -"Y# c #8C8A82", -"Z# c #88847E", -"`# c #40638F", -" $ c #375B88", -".$ c #94928D", -"+$ c #87857E", -"@$ c #928E86", -"#$ c #918E87", -"$$ c #8F8C85", -"%$ c #908D84", -"&$ c #939088", -"*$ c #85837D", -"=$ c #6F7881", -"-$ c #707880", -";$ c #727A80", -">$ c #737B82", -",$ c #737A81", -"'$ c #516987", -")$ c #3C6495", -"!$ c #3F6491", -"~$ c #8B8982", -"{$ c #918D85", -"]$ c #938F87", -"^$ c #4A6686", -"/$ c #3F689B", -"($ c #4976AB", -"_$ c #4875AA", -":$ c #4876AC", -"<$ c #4979B1", -"[$ c #4C78AC", -"}$ c #4F7AAF", -"|$ c #4E7AAE", -"1$ c #476B96", -"2$ c #5A799E", -"3$ c #87847E", -"4$ c #95928A", -"5$ c #96938B", -"6$ c #94928A", -"7$ c #8D8981", -"8$ c #8B8980", -"9$ c #8A8781", -"0$ c #87837C", -"a$ c #8C877F", -"b$ c #4A688A", -"c$ c #416999", -"d$ c #446893", -"e$ c #8B8981", -"f$ c #8C8882", -"g$ c #8E8A84", -"h$ c #8E8C84", -"i$ c #959189", -"j$ c #96928A", -"k$ c #96938A", -"l$ c #97948C", -"m$ c #939089", -"n$ c #928E87", -"o$ c #938F88", -"p$ c #8C8880", -"q$ c #84847F", -"r$ c #4C688A", -"s$ c #4776AF", -"t$ c #677380", -"u$ c #4A6C94", -"v$ c #928F86", -"w$ c #969289", -"x$ c #969389", -"y$ c #95928B", -"z$ c #98958D", -"A$ c #99958D", -"B$ c #949088", -"C$ c #928F88", -"D$ c #86847E", -"E$ c #496789", -"F$ c #416898", -"G$ c #656E76", -"H$ c #77746D", -"I$ c #77756E", -"J$ c #7D7A74", -"K$ c #96948E", -"L$ c #375C89", -"M$ c #888680", -"N$ c #82807A", -"O$ c #88867F", -"P$ c #88857F", -"Q$ c #949089", -"R$ c #95918A", -"S$ c #98958B", -"T$ c #8A8881", -"U$ c #4A6789", -"V$ c #5F6C78", -"W$ c #75736C", -"X$ c #727069", -"Y$ c #83807B", -"Z$ c #385D8B", -"`$ c #8A8882", -" % c #807E77", -".% c #83807A", -"+% c #84817B", -"@% c #85827C", -"#% c #908D87", -"$% c #97948B", -"%% c #98958C", -"&% c #8F8D85", -"*% c #817F78", -"=% c #797976", -"-% c #486587", -";% c #4776AE", -">% c #416899", -",% c #636E78", -"'% c #7C786E", -")% c #79756F", -"!% c #75726B", -"~% c #737069", -"{% c #6F6D66", -"]% c #7E7C75", -"^% c #7C7A74", -"/% c #486D9A", -"(% c #416B9E", -"_% c #365B87", -":% c #8D8C87", -"<% c #84827C", -"[% c #84837C", -"}% c #918F87", -"|% c #97938B", -"1% c #99968E", -"2% c #9A968E", -"3% c #99978E", -"4% c #97958D", -"5% c #89877F", -"6% c #807D74", -"7% c #7F7D76", -"8% c #476588", -"9% c #666F79", -"0% c #75726C", -"a% c #716E67", -"b% c #6E6C65", -"c% c #6D6B64", -"d% c #6D6B65", -"e% c #6B6963", -"f% c #6C6A63", -"g% c #706E68", -"h% c #72706A", -"i% c #78776E", -"j% c #8F8E89", -"k% c #4775AB", -"l% c #4876AD", -"m% c #365A85", -"n% c #83817B", -"o% c #807E79", -"p% c #807E78", -"q% c #7F7E78", -"r% c #817E79", -"s% c #87857F", -"t% c #898680", -"u% c #99968D", -"v% c #9B978F", -"w% c #9A978F", -"x% c #9C988F", -"y% c #9B9890", -"z% c #95938B", -"A% c #94918A", -"B% c #817F79", -"C% c #4A6689", -"D% c #626F7C", -"E% c #74726B", -"F% c #74716A", -"G% c #73716A", -"H% c #76736D", -"I% c #77746C", -"J% c #9C9B95", -"K% c #355984", -"L% c #888682", -"M% c #7D7B75", -"N% c #82807B", -"O% c #7F7D77", -"P% c #7E7C77", -"Q% c #7F7D78", -"R% c #8C8A83", -"S% c #9F9B93", -"T% c #9E9B92", -"U% c #9C9991", -"V% c #9F9C93", -"W% c #85837C", -"X% c #7F7F7B", -"Y% c #49678A", -"Z% c #5082BE", -"`% c #6A747D", -" & c #858177", -".& c #7F7B74", -"+& c #7B7971", -"@& c #8C8984", -"#& c #4C6C94", -"$& c #777571", -"%& c #7C7A76", -"&& c #7D7B76", -"*& c #807F79", -"=& c #7E7C76", -"-& c #817F7A", -";& c #A19E95", -">& c #A29F96", -",& c #9E9A91", -"'& c #9A978E", -")& c #99968F", -"!& c #98968E", -"~& c #828179", -"{& c #48668A", -"]& c #68737E", -"^& c #7D7B74", -"/& c #7F7C76", -"(& c #8F8C83", -"_& c #939087", -":& c #8E8A82", -"<& c #999791", -"[& c #506F95", -"}& c #4572A6", -"|& c #355884", -"1& c #83817E", -"2& c #7C7A75", -"3& c #7D7A75", -"4& c #7D7B77", -"5& c #7E7B77", -"6& c #9F9C94", -"7& c #A29E96", -"8& c #A39F96", -"9& c #A3A097", -"0& c #9D9991", -"a& c #9B9990", -"b& c #9C9990", -"c& c #97948D", -"d& c #96938C", -"e& c #817E78", -"f& c #807F7A", -"g& c #9B988F", -"h& c #9A958D", -"i& c #98948B", -"j& c #8E8A83", -"k& c #8E8C86", -"l& c #466E9F", -"m& c #45668E", -"n& c #7D7C77", -"o& c #7C7B76", -"p& c #7B7A76", -"q& c #7B7A77", -"r& c #84817C", -"s& c #95918B", -"t& c #9D9A92", -"u& c #A09C94", -"v& c #9E9B93", -"w& c #9C9890", -"x& c #858279", -"y& c #7D7D79", -"z& c #486588", -"A& c #6B747E", -"B& c #858078", -"C& c #9D9A91", -"D& c #9F9B92", -"E& c #9D9990", -"F& c #9B988E", -"G& c #939188", -"H& c #476D9B", -"I& c #4A78B1", -"J& c #4E6D93", -"K& c #91918D", -"L& c #7C7C78", -"M& c #7D7A76", -"N& c #7C7B77", -"O& c #7B7B77", -"P& c #7B7977", -"Q& c #7D7B78", -"R& c #9B9891", -"S& c #99978F", -"T& c #908E86", -"U& c #807D78", -"V& c #817D78", -"W& c #88867E", -"X& c #8D8A81", -"Y& c #476589", -"Z& c #66717E", -"`& c #838178", -" * c #949188", -".* c #9A968D", -"+* c #9D9A90", -"@* c #9E9C93", -"#* c #A09C93", -"$* c #A9A59B", -"%* c #A9A59C", -"&* c #A09D94", -"** c #A29E95", -"=* c #7A7871", -"-* c #918F8A", -";* c #979590", -">* c #4470A5", -",* c #807F7D", -"'* c #7A7976", -")* c #7A7975", -"!* c #7B7975", -"~* c #7B7A75", -"{* c #7B7976", -"]* c #7C7A77", -"^* c #93918A", -"/* c #9A9790", -"(* c #9B9790", -"_* c #98958E", -":* c #807D77", -"<* c #7F7E77", -"[* c #7E7B76", -"}* c #89857E", -"|* c #7E7E7A", -"1* c #4A678A", -"2* c #4878B1", -"3* c #63707C", -"4* c #868177", -"5* c #959289", -"6* c #A5A298", -"7* c #A8A59B", -"8* c #A6A399", -"9* c #A9A49B", -"0* c #ACA89E", -"a* c #AEAAA0", -"b* c #ABA89E", -"c* c #ABA79E", -"d* c #AAA69D", -"e* c #AAA59C", -"f* c #939187", -"g* c #87857C", -"h* c #7C7973", -"i* c #928F89", -"j* c #8D8B85", -"k* c #8F8D87", -"l* c #928F8A", -"m* c #8F8C87", -"n* c #8E8B86", -"o* c #8F8D88", -"p* c #94938D", -"q* c #5083C0", -"r* c #3C6393", -"s* c #858481", -"t* c #7A7876", -"u* c #787674", -"v* c #787774", -"w* c #777774", -"x* c #767572", -"y* c #777674", -"z* c #777673", -"A* c #797875", -"B* c #7A7977", -"C* c #827F7A", -"D* c #96948C", -"E* c #7F7E76", -"F* c #86837A", -"G* c #82817C", -"H* c #6C757E", -"I* c #858178", -"J* c #A7A39A", -"K* c #ABA79D", -"L* c #AFABA1", -"M* c #ADA99F", -"N* c #ACA89F", -"O* c #ADAAA0", -"P* c #A8A49A", -"Q* c #4A6B92", -"R* c #4772A7", -"S* c #4B7CB6", -"T* c #7D7C79", -"U* c #767471", -"V* c #757471", -"W* c #747370", -"X* c #747371", -"Y* c #757472", -"Z* c #757573", -"`* c #787775", -" = c #84827D", -".= c #918F88", -"+= c #72797F", -"@= c #86827A", -"#= c #99958C", -"$= c #A19D94", -"%= c #A5A198", -"&= c #ADAA9F", -"*= c #B1ADA3", -"== c #B2AEA3", -"-= c #B0ACA2", -";= c #B4AFA5", -">= c #B4B0A5", -",= c #86847B", -"'= c #7B7870", -")= c #908E89", -"!= c #486B96", -"~= c #426DA2", -"{= c #73726F", -"]= c #71716F", -"^= c #71706E", -"/= c #6E6E6D", -"(= c #6C6B6A", -"_= c #6C6C6B", -":= c #72716F", -"<= c #7B7B76", -"[= c #807E7A", -"}= c #87837A", -"|= c #86827B", -"1= c #98968C", -"2= c #AAA79D", -"3= c #B1ADA2", -"4= c #B2ADA4", -"5= c #A4A197", -"6= c #85817B", -"7= c #8C8B84", -"8= c #4A7BB5", -"9= c #4B709C", -"0= c #838281", -"a= c #716F6E", -"b= c #70706E", -"c= c #706F6D", -"d= c #6D6D6A", -"e= c #6D6D6B", -"f= c #696968", -"g= c #6A6969", -"h= c #6B6B6A", -"i= c #71706F", -"j= c #7C7975", -"k= c #85827D", -"l= c #888580", -"m= c #B3AFA5", -"n= c #A5A197", -"o= c #9D9B91", -"p= c #938F85", -"q= c #8D8B82", -"r= c #827D75", -"s= c #6D7D91", -"t= c #4976AC", -"u= c #848382", -"v= c #6F6E6D", -"w= c #6E6D6B", -"x= c #6C6C6A", -"y= c #6D6C6A", -"z= c #6B6A69", -"A= c #6A6968", -"B= c #696867", -"C= c #6D6C6B", -"D= c #6F6F6E", -"E= c #6F6F6D", -"F= c #727170", -"G= c #797876", -"H= c #7B7A78", -"I= c #7C7B78", -"J= c #7C7B75", -"K= c #81807A", -"L= c #948F88", -"M= c #A7A49A", -"N= c #AFABA2", -"O= c #AEAAA1", -"P= c #AFACA2", -"Q= c #AEABA1", -"R= c #A4A097", -"S= c #8A877E", -"T= c #788085", -"U= c #5F7389", -"V= c #9B978E", -"W= c #908C83", -"X= c #918D84", -"Y= c #85847F", -"Z= c #42648D", -"`= c #416EA4", -" - c #7F7F7D", -".- c #6E6D6C", -"+- c #6A6A68", -"@- c #727270", -"#- c #767674", -"$- c #7B7B78", -"%- c #7F7D79", -"&- c #86857F", -"*- c #85847D", -"=- c #7C7974", -"-- c #7B7973", -";- c #7C7A73", -">- c #9A978D", -",- c #A8A49B", -"'- c #B0ACA1", -")- c #A6A299", -"!- c #A19E94", -"~- c #A3A096", -"{- c #928D83", -"]- c #747D85", -"^- c #60768F", -"/- c #A09D93", -"(- c #A09B91", -"_- c #A6A093", -":- c #898C8B", -"<- c #657587", -"[- c #4E6E95", -"}- c #416FA7", -"|- c #426A99", -"1- c #818181", -"2- c #666564", -"3- c #686866", -"4- c #686867", -"5- c #6E6D6D", -"6- c #86837E", -"7- c #7B7873", -"8- c #817D76", -"9- c #AAA79E", -"0- c #ACA89D", -"a- c #ABA89D", -"b- c #A6A298", -"c- c #7A8085", -"d- c #426895", -"e- c #657A92", -"f- c #A6A39A", -"g- c #A7A399", -"h- c #A39F95", -"i- c #A39E94", -"j- c #A6A196", -"k- c #878E94", -"l- c #4E6A8B", -"m- c #416DA2", -"n- c #617FA2", -"o- c #7C7C7C", -"p- c #636362", -"q- c #626261", -"r- c #616160", -"s- c #5F5F5E", -"t- c #61605F", -"u- c #646362", -"v- c #636261", -"w- c #646463", -"x- c #676665", -"y- c #6A6A69", -"z- c #6D6C6C", -"A- c #706F6E", -"B- c #747372", -"C- c #777775", -"D- c #797774", -"E- c #7A7775", -"F- c #7A7974", -"G- c #7B7974", -"H- c #7A7872", -"I- c #797771", -"J- c #797772", -"K- c #787670", -"L- c #8E8C83", -"M- c #959188", -"N- c #B2AFA4", -"O- c #B8B3A9", -"P- c #B8B4A9", -"Q- c #B2AEA4", -"R- c #ADA9A0", -"S- c #AEA9A0", -"T- c #ACA79D", -"U- c #9E9990", -"V- c #7F8281", -"W- c #436793", -"X- c #5D738B", -"Y- c #A09C92", -"Z- c #9E9C95", -"`- c #959693", -" ; c #878D91", -".; c #677C94", -"+; c #416694", -"@; c #3B699F", -"#; c #5083BF", -"$; c #4977AE", -"%; c #787878", -"&; c #616161", -"*; c #626161", -"=; c #616261", -"-; c #636363", -";; c #666665", -">; c #686967", -",; c #6D6D6C", -"'; c #6F706E", -"); c #747472", -"!; c #797874", -"~; c #7A7875", -"{; c #7F7C77", -"]; c #7A7873", -"^; c #787671", -"/; c #77766F", -"(; c #77756F", -"_; c #797671", -":; c #797570", -"<; c #7A7771", -"[; c #7B7972", -"}; c #807C76", -"|; c #8F8B84", -"1; c #B3AFA4", -"2; c #B5B1A6", -"3; c #B6B2A7", -"4; c #B8B4AA", -"5; c #BEB9AE", -"6; c #BCB9AD", -"7; c #BBB7AC", -"8; c #BBB6AC", -"9; c #BAB6AB", -"0; c #B4B0A6", -"a; c #ACA99F", -"b; c #ABA69D", -"c; c #A9A69C", -"d; c #A4A198", -"e; c #9E9B91", -"f; c #8C887F", -"g; c #7C8080", -"h; c #43658E", -"i; c #4A7BB7", -"j; c #5D728A", -"k; c #928E83", -"l; c #959086", -"m; c #8A8B8B", -"n; c #76818A", -"o; c #617489", -"p; c #547090", -"q; c #4B719D", -"r; c #4371A8", -"s; c #4777B2", -"t; c #4574AC", -"u; c #4C6788", -"v; c #858683", -"w; c #6A6A6A", -"x; c #5B5B5B", -"y; c #5B5C5C", -"z; c #5D5D5D", -"A; c #5E5E5D", -"B; c #5A5A5A", -"C; c #605F5F", -"D; c #606060", -"E; c #626262", -"F; c #676766", -"G; c #686767", -"H; c #696868", -"I; c #72706F", -"J; c #767472", -"K; c #7E7D77", -"L; c #77746F", -"M; c #75736E", -"N; c #76746E", -"O; c #7B7872", -"P; c #A19C94", -"Q; c #AAA69C", -"R; c #BDB8AE", -"S; c #C3BEB3", -"T; c #BCB8AD", -"U; c #B9B5AB", -"V; c #BEBAAE", -"W; c #BDB9AE", -"X; c #B5B1A7", -"Y; c #A8A39A", -"Z; c #96948A", -"`; c #848583", -" > c #45658B", -".> c #496688", -"+> c #3A5F8B", -"@> c #4572A7", -"#> c #4674AD", -"$> c #747373", -"%> c #585858", -"&> c #595959", -"*> c #5C5C5C", -"=> c #626160", -"-> c #5E5E5E", -";> c #60605F", -">> c #666666", -",> c #656665", -"'> c #676666", -")> c #72716E", -"!> c #7C7976", -"~> c #797670", -"{> c #787571", -"]> c #777671", -"^> c #76746F", -"/> c #75726D", -"(> c #74726D", -"_> c #74726C", -":> c #73716C", -"<> c #75736D", -"[> c #78756F", -"}> c #797770", -"|> c #918D86", -"1> c #9F9C92", -"2> c #BFBBB0", -"3> c #C3BFB3", -"4> c #BBB7AD", -"5> c #B7B2A9", -"6> c #B7B3A8", -"7> c #9C9891", -"8> c #8E8E89", -"9> c #4F6C8D", -"0> c #4979B4", -"a> c #4B7DB8", -"b> c #4472A9", -"c> c #546C88", -"d> c #7E7D76", -"e> c #94938E", -"f> c #797879", -"g> c #585859", -"h> c #575757", -"i> c #535354", -"j> c #515152", -"k> c #525253", -"l> c #545555", -"m> c #555555", -"n> c #5F5F5F", -"o> c #5C5C5D", -"p> c #59595A", -"q> c #666664", -"r> c #6E6E6C", -"s> c #6F6E6C", -"t> c #73716B", -"u> c #716F6A", -"v> c #7E7A75", -"w> c #A4A298", -"x> c #A39E96", -"y> c #9E9A92", -"z> c #B2ADA3", -"A> c #B9B5AA", -"B> c #B5B0A6", -"C> c #A5A299", -"D> c #A29E94", -"E> c #9D9A94", -"F> c #4E81BD", -"G> c #3A669A", -"H> c #687889", -"I> c #989691", -"J> c #4D4D4F", -"K> c #525353", -"L> c #505152", -"M> c #4F4F50", -"N> c #505051", -"O> c #4F5051", -"P> c #4E4F50", -"Q> c #4C4D4E", -"R> c #595A5A", -"S> c #5B5A5A", -"T> c #747471", -"U> c #767573", -"V> c #787672", -"W> c #777570", -"X> c #787570", -"Y> c #767470", -"Z> c #72706B", -"`> c #706E69", -" , c #6F6D68", -"., c #85817A", -"+, c #908E85", -"@, c #ACA9A0", -"#, c #ACA99E", -"$, c #B9B4AA", -"%, c #B6B1A7", -"&, c #BAB7AB", -"*, c #B7B3A9", -"=, c #B1ACA2", -"-, c #A09E96", -";, c #58718E", -">, c #426690", -",, c #8D9295", -"', c #96928B", -"), c #989792", -"!, c #4C4C4E", -"~, c #4E4F4F", -"{, c #575758", -"], c #5D5C5C", -"^, c #5A5B5B", -"/, c #4B4B4D", -"(, c #494B4C", -"_, c #4A4B4C", -":, c #4C4C4D", -"<, c #545455", -"[, c #565656", -"}, c #555656", -"|, c #555556", -"1, c #737370", -"2, c #7D7A78", -"3, c #7B7A74", -"4, c #797773", -"5, c #777672", -"6, c #76756F", -"7, c #ADA99E", -"8, c #ADA89E", -"9, c #C2BDB3", -"0, c #D0CCC0", -"a, c #D7D3C7", -"b, c #D5D1C4", -"c, c #CBC6BB", -"d, c #C5C2B5", -"e, c #C2BEB3", -"f, c #BAB6AC", -"g, c #B2AEA5", -"h, c #B6B2A8", -"i, c #C2BDB2", -"j, c #697D94", -"k, c #4372AA", -"l, c #667D98", -"m, c #9C9D9A", -"n, c #A19E96", -"o, c #9A968F", -"p, c #5A5A5C", -"q, c #494A4B", -"r, c #4D4E4F", -"s, c #515252", -"t, c #5F605F", -"u, c #4D4E4E", -"v, c #4A4A4C", -"w, c #4D4D4E", -"x, c #4E4E50", -"y, c #5D5D5C", -"z, c #656563", -"A, c #70706F", -"B, c #7D7C78", -"C, c #7A7874", -"D, c #6E6C67", -"E, c #6D6B66", -"F, c #716F69", -"G, c #A4A096", -"H, c #A5A098", -"I, c #97938A", -"J, c #A29D95", -"K, c #BEBAAF", -"L, c #BEB9AF", -"M, c #BDB9AF", -"N, c #C4C0B4", -"O, c #CDC8BC", -"P, c #D5CFC3", -"Q, c #D0CBBF", -"R, c #CBC7BB", -"S, c #CCC7BC", -"T, c #D1CDC0", -"U, c #CEC9BD", -"V, c #CDC8BD", -"W, c #C8C4B9", -"X, c #BCB7AD", -"Y, c #B1ACA0", -"Z, c #657B94", -"`, c #4371A7", -" ' c #3F6BA3", -".' c #4E6F96", -"+' c #A6ACAE", -"@' c #C4C0B6", -"#' c #A09D95", -"$' c #A19D95", -"%' c #898782", -"&' c #575858", -"*' c #49494A", -"=' c #4E4E4F", -"-' c #515153", -";' c #4B4C4E", -">' c #494A4A", -",' c #48494B", -"'' c #484849", -")' c #48484A", -"!' c #656464", -"~' c #7C7C79", -"{' c #787772", -"]' c #74736E", -"^' c #716F6B", -"/' c #6F6D69", -"(' c #6B6965", -"_' c #6C6B66", -":' c #6C6A65", -"<' c #AAA79C", -"[' c #ACA79E", -"}' c #C0BCB1", -"|' c #B0ADA3", -"1' c #BAB6AA", -"2' c #C1BDB2", -"3' c #C5C0B5", -"4' c #C6C2B6", -"5' c #C6C2B7", -"6' c #C7C3B8", -"7' c #C5C1B6", -"8' c #C7C3B6", -"9' c #CCC8BC", -"0' c #D6D1C5", -"a' c #DDD8CB", -"b' c #E1DBCE", -"c' c #E3DED1", -"d' c #EAE4D6", -"e' c #E6E1D3", -"f' c #EDE8DA", -"g' c #E2DDD0", -"h' c #C8C4B8", -"i' c #D1CDC1", -"j' c #C0BCB2", -"k' c #738396", -"l' c #436FA5", -"m' c #4877AE", -"n' c #436791", -"o' c #8A98A5", -"p' c #CCC6BA", -"q' c #BFBAB0", -"r' c #91908B", -"s' c #747576", -"t' c #444546", -"u' c #474749", -"v' c #4A4B4D", -"w' c #494A4C", -"x' c #47484B", -"y' c #464749", -"z' c #48494A", -"A' c #474849", -"B' c #47484A", -"C' c #4F5050", -"D' c #565657", -"E' c #656565", -"F' c #737372", -"G' c #767570", -"H' c #74726E", -"I' c #74736F", -"J' c #6D6B67", -"K' c #6B6A65", -"L' c #686762", -"M' c #6A6863", -"N' c #6B6964", -"O' c #7E7D75", -"P' c #A7A39B", -"Q' c #B8B3A8", -"R' c #BAB5AB", -"S' c #B6B3A7", -"T' c #CAC6BA", -"U' c #D1CCC0", -"V' c #D2CDC1", -"W' c #D4CFC3", -"X' c #E3DDD0", -"Y' c #E8E3D6", -"Z' c #ECE6D8", -"`' c #EDE7D9", -" ) c #F3EEE0", -".) c #F7F1E2", -"+) c #F2EDDF", -"@) c #EAE4D7", -"#) c #C9C4B9", -"$) c #DAD6C9", -"%) c #E0DBCE", -"&) c #B6B4AC", -"*) c #537091", -"=) c #4879B4", -"-) c #406DA4", -";) c #59789C", -">) c #8391A0", -",) c #BEBCB4", -"') c #C8C2B6", -")) c #C1BDB3", -"!) c #C1BEB3", -"~) c #B1ADA4", -"{) c #A39F97", -"]) c #A4A199", -"^) c #49494B", -"/) c #464648", -"() c #454648", -"_) c #464649", -":) c #484A4C", -"<) c #454547", -"[) c #444547", -"}) c #454647", -"|) c #4A4A4B", -"1) c #5C5D5C", -"2) c #656564", -"3) c #737371", -"4) c #787773", -"5) c #75736F", -"6) c #75746E", -"7) c #74726F", -"8) c #72706C", -"9) c #6A6864", -"0) c #696762", -"a) c #666460", -"b) c #686662", -"c) c #B5B2A7", -"d) c #BEBBAF", -"e) c #AEABA0", -"f) c #99978D", -"g) c #C9C5B9", -"h) c #D6D1C4", -"i) c #D3CEC2", -"j) c #DAD4C7", -"k) c #DCD8CA", -"l) c #E1DCCF", -"m) c #E5DFD2", -"n) c #E8E2D5", -"o) c #EBE6D8", -"p) c #F1ECDE", -"q) c #F9F3E4", -"r) c #FEFCED", -"s) c #FCF7E9", -"t) c #EFEADD", -"u) c #E9E4D5", -"v) c #EAE6D8", -"w) c #CDC9BE", -"x) c #D7D2C6", -"y) c #E1DDCF", -"z) c #C6C1B6", -"A) c #C3BEB4", -"B) c #AEACA6", -"C) c #4D6B90", -"D) c #4573AB", -"E) c #366093", -"F) c #5B7596", -"G) c #91979B", -"H) c #B2B0AA", -"I) c #BEBAB0", -"J) c #C1BCB2", -"K) c #C3BFB4", -"L) c #C4C0B5", -"M) c #A29F97", -"N) c #85847E", -"O) c #83817D", -"P) c #555657", -"Q) c #434445", -"R) c #4B4B4C", -"S) c #434447", -"T) c #434545", -"U) c #484949", -"V) c #6C6B6B", -"W) c #767574", -"X) c #7C7B79", -"Y) c #787673", -"Z) c #757470", -"`) c #73726D", -" ! c #696863", -".! c #656460", -"+! c #64625E", -"@! c #676661", -"#! c #827E79", -"$! c #C1BDB1", -"%! c #CDC9BD", -"&! c #D8D3C6", -"*! c #E6E1D4", -"=! c #E7E2D4", -"-! c #E8E3D5", -";! c #E9E4D6", -">! c #EAE5D7", -",! c #F0EBDD", -"'! c #F9F3E5", -")! c #FDF9EB", -"!! c #FFFFF2", -"~! c #FFFFF3", -"{! c #EAE4D8", -"]! c #E7E2D5", -"^! c #E5E0D3", -"/! c #D9D4C7", -"(! c #DDD7CB", -"_! c #CCC7BD", -":! c #547091", -"~ c #605F5C", -",~ c #605E5B", -"'~ c #696763", -")~ c #BDBAAF", -"!~ c #C0BBB0", -"~~ c #BFBAAF", -"{~ c #C5C1B5", -"]~ c #DFDACD", -"^~ c #DCD7CA", -"/~ c #E6E0D3", -"(~ c #EFE9DC", -"_~ c #FBF6E7", -":~ c #FFFEF0", -"<~ c #FAF5E7", -"[~ c #F8F4E6", -"}~ c #FEFBEC", -"|~ c #F1EBDD", -"1~ c #D8D4C7", -"2~ c #EEE8DB", -"3~ c #CBC7BC", -"4~ c #A4A19A", -"5~ c #B2AFA5", -"6~ c #C0BBB1", -"7~ c #C5C0B4", -"8~ c #6E839A", -"9~ c #3D69A0", -"0~ c #416CA1", -"a~ c #345D90", -"b~ c #446891", -"c~ c #6E86A2", -"d~ c #9EA9B5", -"e~ c #C7C7C0", -"f~ c #CAC7BE", -"g~ c #C5C2B8", -"h~ c #C4BFB4", -"i~ c #B7B3AA", -"j~ c #A6A29A", -"k~ c #BFBBB1", -"l~ c #B6B3A9", -"m~ c #424445", -"n~ c #424446", -"o~ c #434244", -"p~ c #414245", -"q~ c #414244", -"r~ c #414243", -"s~ c #424345", -"t~ c #434344", -"u~ c #797877", -"v~ c #73716F", -"w~ c #72716D", -"x~ c #71706C", -"y~ c #676662", -"z~ c #666561", -"A~ c #63615E", -"B~ c #5D5D59", -"C~ c #61605D", -"D~ c #73716D", -"E~ c #B3B0A6", -"F~ c #B1ACA3", -"G~ c #B5B1A8", -"H~ c #C1BCB1", -"I~ c #DDD7CA", -"J~ c #F5EFE1", -"K~ c #FFFFF1", -"L~ c #FEFFF1", -"M~ c #FEFDF0", -"N~ c #F4EEE0", -"O~ c #F6F1E3", -"P~ c #D1CCC1", -"Q~ c #D8D3C7", -"R~ c #DEDACD", -"S~ c #ACA8A0", -"T~ c #C0BDB3", -"U~ c #CAC3B7", -"V~ c #7D8FA2", -"W~ c #4471A5", -"X~ c #738AA5", -"Y~ c #E7E5D9", -"Z~ c #FFF6E5", -"`~ c #F3ECDC", -" { c #EEE8D9", -".{ c #E9E3D6", -"+{ c #B7B4AA", -"@{ c #A9A69E", -"#{ c #A7A59C", -"${ c #C2BEB2", -"%{ c #B0ADA4", -"&{ c #ABA89F", -"*{ c #82817D", -"={ c #48494C", -"-{ c #454548", -";{ c #424244", -">{ c #414345", -",{ c #414143", -"'{ c #404144", -"){ c #474748", -"!{ c #5D5E5E", -"~{ c #676767", -"{{ c #737270", -"]{ c #72726E", -"^{ c #73716E", -"/{ c #676562", -"({ c #656360", -"_{ c #5F5D5B", -":{ c #63625F", -"<{ c #6C6B68", -"[{ c #A4A098", -"}{ c #B4B1A5", -"|{ c #ABA69C", -"1{ c #C7C3B7", -"2{ c #DED9CC", -"3{ c #E4DFD2", -"4{ c #F4EFE0", -"5{ c #FDF9EA", -"6{ c #FFFDEE", -"7{ c #FCF9EB", -"8{ c #D0CBC0", -"9{ c #99A7B4", -"0{ c #39659A", -"a{ c #8397AD", -"b{ c #F4EFE1", -"c{ c #E4DED2", -"d{ c #E9E4D7", -"e{ c #DDD8CC", -"f{ c #A7A49D", -"g{ c #AEABA2", -"h{ c #A7A49C", -"i{ c #A19E97", -"j{ c #C3BFB5", -"k{ c #CEC9BE", -"l{ c #737475", -"m{ c #47474A", -"n{ c #48484C", -"o{ c #404244", -"p{ c #404142", -"q{ c #404143", -"r{ c #404042", -"s{ c #636262", -"t{ c #6C6C6C", -"u{ c #777773", -"v{ c #72706D", -"w{ c #706F6B", -"x{ c #6D6C68", -"y{ c #696864", -"z{ c #646360", -"A{ c #5F5E5C", -"B{ c #656361", -"C{ c #6E6C69", -"D{ c #B0ADA2", -"E{ c #FEFDEE", -"F{ c #FEFEF0", -"G{ c #FFFBEC", -"H{ c #FFFEF1", -"I{ c #FAF6E8", -"J{ c #EEE8DC", -"K{ c #BEBBB0", -"L{ c #B9B5AC", -"M{ c #EEE7D9", -"N{ c #9DA6AE", -"O{ c #3A6598", -"P{ c #7C90A7", -"Q{ c #F0ECDF", -"R{ c #D9D5C9", -"S{ c #D5D1C6", -"T{ c #A5A29B", -"U{ c #B1AEA5", -"V{ c #9E9B94", -"W{ c #A5A199", -"X{ c #AEAAA2", -"Y{ c #A7A49B", -"Z{ c #E2DED0", -"`{ c #D5D1C5", -" ] c #C8C3B7", -".] c #A7A59B", -"+] c #48484B", -"@] c #494A4D", -"#] c #49494C", -"$] c #3F4042", -"%] c #686868", -"&] c #737170", -"*] c #716F6C", -"=] c #6C6A67", -"-] c #696865", -";] c #686764", -">] c #5E5D5B", -",] c #60605D", -"'] c #6F6D6A", -")] c #9D9A93", -"!] c #B8B4AB", -"~] c #B9B4A9", -"{] c #AEAA9F", -"]] c #F6EFE1", -"^] c #FDFAEB", -"/] c #FEFDEF", -"(] c #FFFCED", -"_] c #FFFCEE", -":] c #FEFCEE", -"<] c #EFEADC", -"[] c #E3DED0", -"}] c #E4DFD3", -"|] c #B9B6AB", -"1] c #BDB8AD", -"2] c #C9C5BA", -"3] c #D6D0C3", -"4] c #A9B1B8", -"5] c #476F9F", -"6] c #4A7AB5", -"7] c #778BA3", -"8] c #D8D3C8", -"9] c #C8C3B9", -"0] c #D5D0C5", -"a] c #C3C0B6", -"b] c #ABA79F", -"c] c #C2BEB5", -"d] c #AFABA3", -"e] c #A6A39B", -"f] c #CFCBBE", -"g] c #DCD6CA", -"h] c #F0EADD", -"i] c #8C8A84", -"j] c #87847F", -"k] c #6A6966", -"l] c #4A4B4E", -"m] c #46474A", -"n] c #414144", -"o] c #3F3F41", -"p] c #3E3F41", -"q] c #3F4143", -"r] c #6F6E6B", -"s] c #6C6A68", -"t] c #666563", -"u] c #656462", -"v] c #7A7972", -"w] c #AFACA1", -"x] c #A29F95", -"y] c #D0CCBF", -"z] c #F4EEDF", -"A] c #F6F0E2", -"B] c #EFEBDD", -"C] c #FEFBEB", -"D] c #FCF9EA", -"E] c #EEE9DB", -"F] c #D6D1C6", -"G] c #BEC2C4", -"H] c #667C96", -"I] c #D3CEC3", -"J] c #CFCBC0", -"K] c #B2AEA6", -"L] c #BEBBB1", -"M] c #DAD5C9", -"N] c #98958F", -"O] c #96948D", -"P] c #918D87", -"Q] c #8D8B86", -"R] c #8B8985", -"S] c #5A5A5B", -"T] c #4A4C4E", -"U] c #444548", -"V] c #3E4042", -"W] c #3E3E40", -"X] c #3F4043", -"Y] c #3F4041", -"Z] c #706F6C", -"`] c #6E6D6A", -" ^ c #6B6A67", -".^ c #676663", -"+^ c #5E5E5C", -"@^ c #5E5D5C", -"#^ c #686765", -"$^ c #B0ACA3", -"%^ c #B4B1A6", -"&^ c #ECE7D8", -"*^ c #FEF9EB", -"=^ c #F8F3E4", -"-^ c #FFFEEF", -";^ c #FDFBEC", -">^ c #CBC7BA", -",^ c #C7C2B7", -"'^ c #CECABE", -")^ c #BABBB9", -"!^ c #8190A0", -"~^ c #D6D2C6", -"{^ c #BFBBB2", -"]^ c #C8C5B9", -"^^ c #A5A29A", -"/^ c #E2DDD1", -"(^ c #D4D0C4", -"_^ c #E8E3D7", -":^ c #B6B2A9", -"<^ c #8B8883", -"[^ c #8B8A85", -"}^ c #888783", -"|^ c #4B4C4F", -"1^ c #45464A", -"2^ c #454649", -"3^ c #424245", -"4^ c #3D3E40", -"5^ c #3E3F40", -"6^ c #505151", -"7^ c #6B6A6A", -"8^ c #71706D", -"9^ c #6D6C69", -"0^ c #696866", -"a^ c #676664", -"b^ c #60605E", -"c^ c #5C5C5B", -"d^ c #85817C", -"e^ c #B1AEA3", -"f^ c #B6B3A8", -"g^ c #CCC7BB", -"h^ c #D5D0C4", -"i^ c #D8D4C8", -"j^ c #ECE6D9", -"k^ c #FFFDEF", -"l^ c #FCF8E9", -"m^ c #D2CCBE", -"n^ c #CFCABF", -"o^ c #E1DCD0", -"p^ c #C6C1B7", -"q^ c #ADAAA1", -"r^ c #97948E", -"s^ c #8C8A86", -"t^ c #8A8884", -"u^ c #868581", -"v^ c #767678", -"w^ c #3D3D40", -"x^ c #3C3D3F", -"y^ c #6B6A68", -"z^ c #8D8A84", -"A^ c #A8A49C", -"B^ c #B7B2A7", -"C^ c #F9F4E6", -"D^ c #A5A297", -"E^ c #C2BEB4", -"F^ c #BCB8AF", -"G^ c #D3CFC3", -"H^ c #CAC5BA", -"I^ c #D2CEC2", -"J^ c #B0ACA4", -"K^ c #8D8A85", -"L^ c #8B8A84", -"M^ c #858480", -"N^ c #7F7D7B", -"O^ c #6C6D6E", -"P^ c #4A4B4F", -"Q^ c #4A4A4E", -"R^ c #4A4A4D", -"S^ c #47494B", -"T^ c #3B3C3E", -"U^ c #3A3B3D", -"V^ c #3D3D3F", -"W^ c #545454", -"X^ c #616060", -"Y^ c #6B6B69", -"Z^ c #6E6E6B", -"`^ c #6B6B68", -" / c #5F5E5D", -"./ c #75746F", -"+/ c #A49F98", -"@/ c #B5B0A7", -"#/ c #CFCBBF", -"$/ c #76746C", -"%/ c #C4BFB5", -"&/ c #DBD6CA", -"*/ c #A09D96", -"=/ c #9F9C95", -"-/ c #878682", -";/ c #84827F", -">/ c #484A4D", -",/ c #4A4C4F", -"'/ c #3C3D40", -")/ c #393A3D", -"!/ c #38393C", -"~/ c #393A3B", -"{/ c #3C3C3E", -"]/ c #6C6B69", -"^/ c #5C5B5A", -"// c #DBD5C9", -"(/ c #D3CEC1", -"_/ c #C8C3B8", -":/ c #D0CBBE", -"( c #5B5B5A", -",( c #5F5E5E", -"'( c #686766", -")( c #595857", -"!( c #6D6D69", -"~( c #71716D", -"{( c #837F7A", -"]( c #E5E0D2", -"^( c #F2ECDE", -"/( c #E7E1D4", -"(( c #ADA9A1", -"_( c #EDE8DB", -":( c #F3EDDF", -"<( c #D4CFC2", -"[( c #78766F", -"}( c #696761", -"|( c #5E5C57", -"1( c #65635E", -"2( c #CCC7BA", -"3( c #9C9A91", -"4( c #878581", -"5( c #8A8983", -"6( c #8B8984", -"7( c #7E7D7A", -"8( c #6D6D6F", -"9( c #3C3E40", -"0( c #353638", -"a( c #323335", -"b( c #313233", -"c( c #303134", -"d( c #414142", -"e( c #575756", -"f( c #5D5C5D", -"g( c #565554", -"h( c #5A5957", -"i( c #666462", -"j( c #EBE5D7", -"k( c #F1EDDE", -"l( c #FAF4E6", -"m( c #EBE6D9", -"n( c #FEF9EA", -"o( c #BBB6AB", -"p( c #6E6C66", -"q( c #615F5A", -"r( c #5B5955", -"s( c #696661", -"t( c #E1DCCE", -"u( c #8C8B85", -"v( c #93908A", -"w( c #8A8885", -"x( c #3B3C3F", -"y( c #3B3C3D", -"z( c #37383C", -"A( c #353639", -"B( c #313235", -"C( c #3E3E41", -"D( c #4D4D4D", -"E( c #5C5B5B", -"F( c #676765", -"G( c #686865", -"H( c #6C6C69", -"I( c #636260", -"J( c #545351", -"K( c #5C5B59", -"L( c #97938C", -"M( c #FDF8E9", -"N( c #F8F3E5", -"O( c #F6F0E1", -"P( c #D7D2C7", -"Q( c #F5EFE2", -"R( c #DAD4C8", -"S( c #605E59", -"T( c #5E5C58", -"U( c #686661", -"V( c #DED9CD", -"W( c #E2DDCF", -"X( c #98968F", -"Y( c #99958E", -"Z( c #94928B", -"`( c #7F7E7A", -" _ c #7E7C7A", -"._ c #868582", -"+_ c #767673", -"@_ c #5D5D5F", -"#_ c #343538", -"$_ c #323336", -"%_ c #323235", -"&_ c #2F2F32", -"*_ c #2B2C2E", -"=_ c #5C5D5F", -"-_ c #3A3A3C", -";_ c #5E5D5A", -">_ c #6F6E6A", -",_ c #E3DFD2", -"'_ c #F6F2E3", -")_ c #FEFDF2", -"!_ c #FAF6E9", -"~_ c #EDE7DA", -"{_ c #BCB9AF", -"]_ c #B9B6AC", -"^_ c #FFFEF2", -"/_ c #F8F2E4", -"(_ c #6F6D67", -"__ c #676560", -":_ c #62605C", -"<_ c #63615C", -"[_ c #726F6B", -"}_ c #949289", -"|_ c #F9F4E5", -"1_ c #FCF9EF", -"2_ c #AFACA3", -"3_ c #A29D96", -"4_ c #9D9992", -"5_ c #7A7979", -"6_ c #69696A", -"7_ c #303033", -"8_ c #2F3032", -"9_ c #2E2F32", -"0_ c #2E2F31", -"a_ c #2C2D30", -"b_ c #2C2D2F", -"c_ c #333437", -"d_ c #525255", -"e_ c #3D3E3F", -"f_ c #434444", -"g_ c #4C4D4D", -"h_ c #636361", -"i_ c #5D5C59", -"j_ c #565553", -"k_ c #6A6865", -"l_ c #FEFCEF", -"m_ c #FFFFF7", -"n_ c #FEFDF4", -"o_ c #FCF7E8", -"p_ c #BEB9B0", -"q_ c #BDB9B0", -"r_ c #C5C1B7", -"s_ c #FFFEF4", -"t_ c #FFFFF6", -"u_ c #FFFBED", -"v_ c #827E77", -"w_ c #77746E", -"x_ c #686660", -"y_ c #66645F", -"z_ c #FBF6E8", -"A_ c #FFFEF3", -"B_ c #FFFFF9", -"C_ c #F9F6EA", -"D_ c #8A8782", -"E_ c #858380", -"F_ c #817F7C", -"G_ c #82807C", -"H_ c #5A5959", -"I_ c #5E5F60", -"J_ c #3F4040", -"K_ c #605F5E", -"L_ c #646361", -"M_ c #585755", -"N_ c #A5A19A", -"O_ c #FDFAEC", -"P_ c #FEFAED", -"Q_ c #D0CCC1", -"R_ c #B0AEA4", -"S_ c #D8D2C7", -"T_ c #E3DDD2", -"U_ c #FBF7E8", -"V_ c #FFFEF5", -"W_ c #FBF7E9", -"X_ c #6F6C67", -"Y_ c #76736E", -"Z_ c #D2CDC2", -"`_ c #F4F0E1", -" : c #888480", -".: c #908D88", -"+: c #82807D", -"@: c #717272", -"#: c #4B4B4B", -"$: c #535353", -"%: c #62615F", -"&: c #636360", -"*: c #676563", -"=: c #61605E", -"-: c #575654", -";: c #585855", -">: c #63625E", -",: c #FFFDF0", -"': c #FCF8EA", -"): c #FFFDF1", -"!: c #FDF7E9", -"~: c #BAB6AD", -"{: c #FEFFF3", -"]: c #7A7772", -"^: c #CECABF", -"/: c #E6E1D5", -"(: c #FFFFF4", -"_: c #FEFEF7", -":: c #FFFFF5", -"<: c #FBF8EB", -"[: c #918E88", -"}: c #82817E", -"|: c #707071", -"1: c #505050", -"2: c #575656", -"3: c #595958", -"4: c #626360", -"5: c #636160", -"6: c #585754", -"7: c #595855", -"8: c #6F6E69", -"9: c #98948D", -"0: c #908E87", -"a: c #B8B3AA", -"b: c #B4B2A8", -"c: c #D6D2C5", -"d: c #FFFFF0", -"e: c #F9F5E6", -"f: c #E5E1D4", -"g: c #C9C4BA", -"h: c #A8A49D", -"i: c #A4A29A", -"j: c #D1CDC2", -"k: c #FAF6E7", -"l: c #6E6C68", -"m: c #ECE8DB", -"n: c #FEFEF4", -"o: c #FFFFF8", -"p: c #F5F0E3", -"q: c #F6F0E3", -"r: c #CAC6BC", -"s: c #83827D", -"t: c #797872", -"u: c #737373", -"v: c #515151", -"w: c #545353", -"x: c #5D5C5B", -"y: c #5A5958", -"z: c #555452", -"A: c #5C5B57", -"B: c #6C6A66", -"C: c #74716D", -"D: c #807C77", -"E: c #FEFEF1", -"F: c #A2A097", -"G: c #DEDACE", -"H: c #F9F5E7", -"I: c #706F6A", -"J: c #D9D5CA", -"K: c #FDFAED", -"L: c #FBF9EE", -"M: c #F6F1E4", -"N: c #E2DED2", -"O: c #D2CDC3", -"P: c #9F9D96", -"Q: c #9A9891", -"R: c #93918B", -"S: c #84827E", -"T: c #5F6060", -"U: c #545453", -"V: c #515150", -"W: c #585857", -"X: c #5D5C5A", -"Y: c #5B5A59", -"Z: c #535351", -"`: c #545350", -" < c #6D6C67", -".< c #6E6D68", -"+< c #B7B2A8", -"@< c #E4E0D3", -"#< c #F1EBDE", -"$< c #F5F0E2", -"%< c #726F6A", -"&< c #9F9B94", -"*< c #CCC8BD", -"=< c #DED9CE", -"-< c #E9E4D8", -";< c #F0EBDF", -">< c #E7E3D8", -",< c #F6F0E4", -"'< c #D8D4CA", -")< c #E3DFD4", -"!< c #D6D1C7", -"~< c #BBB7AE", -"{< c #85837F", -"]< c #8A8985", -"^< c #83827E", -"/< c #3E3E3F", -"(< c #494949", -"_< c #525251", -":< c #525151", -"<< c #5A5A59", -"[< c #4F4D4C", -"}< c #555451", -"|< c #605F5B", -"1< c #686763", -"2< c #6E6D69", -"3< c #CAC6BB", -"4< c #F7F3E6", -"5< c #D2CEC1", -"6< c #CFCBC1", -"7< c #E6E2D6", -"8< c #EAE6DB", -"9< c #DFDCD1", -"0< c #D3CFC5", -"a< c #C7C4BB", -"b< c #E5E1D6", -"c< c #B5B2A9", -"d< c #85837E", -"e< c #7B7A79", -"f< c #84837F", -"g< c #656463", -"h< c #7C7C7D", -"i< c #555554", -"j< c #565653", -"k< c #4D4D4B", -"l< c #535250", -"m< c #64635F", -"n< c #6F6E68", -"o< c #C9C5B8", -"p< c #FEFBEE", -"q< c #A6A49B", -"r< c #D6D0C5", -"s< c #E0DACD", -"t< c #72716C", -"u< c #B3AFA7", -"v< c #C9C6BC", -"w< c #C8C4BB", -"x< c #E2DED4", -"y< c #E0DCD2", -"z< c #C2C0B7", -"A< c #BDBAB2", -"B< c #D4D0C5", -"C< c #757371", -"D< c #82827E", -"E< c #797873", -"F< c #7E7D79", -"G< c #7A7A78", -"H< c #636464", -"I< c #8B8B8C", -"J< c #3E3F3F", -"K< c #434343", -"L< c #535352", -"M< c #555553", -"N< c #51504E", -"O< c #4A4A48", -"P< c #585654", -"Q< c #73706B", -"R< c #929088", -"S< c #F5F0E1", -"T< c #FAF7E8", -"U< c #CBC8BC", -"V< c #B6B2AB", -"W< c #AAA8A1", -"X< c #B8B5AD", -"Y< c #C7C4BC", -"Z< c #CCC9C1", -"`< c #D4D1C8", -" [ c #DBD8CF", -".[ c #DFDAD1", -"+[ c #D4D0C7", -"@[ c #C3BFB7", -"#[ c #E0DBD1", -"$[ c #60615F", -"%[ c #515251", -"&[ c #585757", -"*[ c #535251", -"=[ c #4C4B4A", -"-[ c #484746", -";[ c #5C5B58", -">[ c #BCB8AC", -",[ c #B3AEA5", -"'[ c #FEFAEB", -")[ c #C8C5BC", -"![ c #BBB8B0", -"~[ c #CDCAC2", -"{[ c #DDD9CF", -"][ c #D2CEC5", -"^[ c #C9C7BE", -"/[ c #DBD7CE", -"([ c #C3C0B8", -"_[ c #E1DDD1", -":[ c #9C9A93", -"<[ c #6D6B68", -"[[ c #5B5B5C", -"}[ c #464646", -"|[ c #535252", -"1[ c #565655", -"2[ c #4F4F4E", -"3[ c #494846", -"4[ c #484846", -"5[ c #5B5A57", -"6[ c #CAC6B9", -"7[ c #FAF5E5", -"8[ c #EEE9DC", -"9[ c #B3B0A8", -"0[ c #CECAC1", -"a[ c #C6C3B9", -"b[ c #C5C1B9", -"c[ c #C6C3BB", -"d[ c #D1CDC5", -"e[ c #DAD7CD", -"f[ c #D9D5CC", -"g[ c #C6C4BC", -"h[ c #B1AFA7", -"i[ c #686968", -"j[ c #6A6964", -"k[ c #454545", -"l[ c #545553", -"m[ c #4C4C4B", -"n[ c #444443", -"o[ c #494947", -"p[ c #64635E", -"q[ c #B3B0A5", -"r[ c #D5CFC4", -"s[ c #F3EEDF", -"t[ c #FFFCEC", -"u[ c #FDFBEF", -"v[ c #B4B1A9", -"w[ c #C9C5BC", -"x[ c #C5C2BA", -"y[ c #C8C5BD", -"z[ c #B6B4AD", -"A[ c #C5C2BB", -"B[ c #C3C0B9", -"C[ c #C1BFB7", -"D[ c #C5C3BC", -"E[ c #6A6867", -"F[ c #696966", -"G[ c #686966", -"H[ c #797776", -"I[ c #525252", -"J[ c #3A3B3B", -"K[ c #4E4D4D", -"L[ c #525150", -"M[ c #4A4948", -"N[ c #424240", -"O[ c #4B4B49", -"P[ c #676561", -"Q[ c #F9F6E9", -"R[ c #E1DBCF", -"S[ c #A5A29C", -"T[ c #B3B0A9", -"U[ c #BFBCB5", -"V[ c #C0BDB6", -"W[ c #B5B2AC", -"X[ c #B0AEA9", -"Y[ c #CAC7C0", -"Z[ c #94928E", -"`[ c #6D6D6E", -" } c #3D3D3D", -".} c #4A4A4A", -"+} c #50504F", -"@} c #454443", -"#} c #434241", -"$} c #5D5C58", -"%} c #6D6A65", -"&} c #DDD8CA", -"*} c #E4DED1", -"=} c #F7F1E3", -"-} c #82817B", -";} c #83817C", -">} c #8A8883", -",} c #989690", -"'} c #9E9B96", -")} c #A4A29D", -"!} c #B1AEA9", -"~} c #BCBAB3", -"{} c #BFBDB6", -"]} c #B9B7B1", -"^} c #B1AFAB", -"/} c #BCB9B3", -"(} c #B7B6B0", -"_} c #B8B6AF", -":} c #B9B6B0", -"<} c #716F6D", -"[} c #5F5F60", -"}} c #373838", -"|} c #4F4F4F", -"1} c #4E4E4D", -"2} c #403F3E", -"3} c #444342", -"4} c #4C4B49", -"5} c #61615D", -"6} c #DDD9CC", -"7} c #FDF7E8", -"8} c #F8F2E3", -"9} c #97948F", -"0} c #A09E99", -"a} c #9F9D98", -"b} c #A3A09B", -"c} c #AAA8A2", -"d} c #AEACA7", -"e} c #B1AFAA", -"f} c #B3B1AC", -"g} c #ADACA7", -"h} c #B2B0AB", -"i} c #AAA9A4", -"j} c #C5C3BB", -"k} c #898884", -"l} c #7F7E79", -"m} c #4D4C4D", -"n} c #464645", -"o} c #4E4E4E", -"p} c #4A4A49", -"q} c #3E3D3C", -"r} c #444442", -"s} c #52514F", -"t} c #5B5A56", -"u} c #62615C", -"v} c #D9D3C7", -"w} c #DFDACE", -"x} c #EDE9DC", -"y} c #908F89", -"z} c #9B9995", -"A} c #A3A19C", -"B} c #A19F99", -"C} c #ADABA6", -"D} c #ACA9A4", -"E} c #737271", -"F} c #707171", -"G} c #515052", -"H} c #444545", -"I} c #4D4D4C", -"J} c #474746", -"K} c #3F3F3E", -"L} c #494946", -"M} c #53524F", -"N} c #E2DCCF", -"O} c #ECE7DA", -"P} c #F8F5E6", -"Q} c #FEFDF3", -"R} c #F3EFE1", -"S} c #E0DBCF", -"T} c #F9F7EC", -"U} c #EEEBDD", -"V} c #817F7B", -"W} c #8D8B87", -"X} c #989692", -"Y} c #9A9894", -"Z} c #999893", -"`} c #9A9893", -" | c #A9A8A4", -".| c #A8A6A2", -"+| c #A1A09C", -"@| c #A4A39E", -"#| c #707172", -"$| c #4C4C4C", -"%| c #434342", -"&| c #3C3C3B", -"*| c #474744", -"=| c #7B7A73", -"-| c #FBF5E7", -";| c #FAF4E7", -">| c #F4EEE1", -",| c #F6F4E8", -"'| c #CEC9BF", -")| c #9E9C94", -"!| c #F3F0E4", -"~| c #FFFEF6", -"{| c #FAF8ED", -"]| c #72726F", -"^| c #92918D", -"/| c #92918E", -"(| c #93918E", -"_| c #979693", -":| c #A4A29E", -"<| c #9D9C98", -"[| c #999795", -"}| c #9A9997", -"|| c #4B4A4A", -"1| c #3E3E3D", -"2| c #4B4A47", -"3| c #5B5855", -"4| c #DED8CC", -"5| c #FDF8EA", -"6| c #F3EDE0", -"7| c #EBE5D9", -"8| c #F5F1E3", -"9| c #71716E", -"0| c #7D7D7B", -"a| c #8C8B88", -"b| c #91908D", -"c| c #918F8C", -"d| c #949390", -"e| c #999896", -"f| c #989794", -"g| c #A09F9B", -"h| c #9F9E9B", -"i| c #979593", -"j| c #696767", -"k| c #3B3B3B", -"l| c #4B4B4A", -"m| c #3B3B3A", -"n| c #504E4C", -"o| c #615F5B", -"p| c #E5E1D3", -"q| c #DFDBCE", -"r| c #FBF5E6", -"s| c #D2CEC3", -"t| c #C7C3B9", -"u| c #DFDACC", -"v| c #878683", -"w| c #8F8E8B", -"x| c #8E8D8B", -"y| c #8E8E8C", -"z| c #91908E", -"A| c #9C9B98", -"B| c #93928F", -"C| c #878784", -"D| c #6E6E6F", -"E| c #484847", -"F| c #3D3C3B", -"G| c #575652", -"H| c #FFFEF8", -"I| c #FBF6E9", -"J| c #FCF7EA", -"K| c #F3EEE1", -"L| c #939290", -"M| c #92918F", -"N| c #969593", -"O| c #999895", -"P| c #8C8B8A", -"Q| c #9A9996", -"R| c #767676", -"S| c #424241", -"T| c #3D3D3C", -"U| c #40403E", -"V| c #FEFAEC", -"W| c #FEFEEF", -"X| c #FEFEF2", -"Y| c #E4DFD4", -"Z| c #F1EBDF", -"`| c #E5E0D5", -" 1 c #BAB7AD", -".1 c #CDC8BE", -"+1 c #C6C3B8", -"@1 c #B8B5AB", -"#1 c #B4AFA7", -"$1 c #81807E", -"%1 c #92928F", -"&1 c #908F8D", -"*1 c #8F8F8D", -"=1 c #898886", -"-1 c #8F8E8D", -";1 c #7A7A7B", -">1 c #383837", -",1 c #3A3A39", -"'1 c #424140", -")1 c #575552", -"!1 c #D4CFC4", -"~1 c #E7E2D6", -"{1 c #FEFEEE", -"]1 c #FEFFF4", -"^1 c #FFFFEF", -"/1 c #FDFCF1", -"(1 c #FAF7E9", -"_1 c #E8E4D9", -":1 c #ECE7DC", -"<1 c #DEDAD0", -"[1 c #E0DCD0", -"}1 c #B9B6AD", -"|1 c #CAC5BB", -"11 c #9C9992", -"21 c #81807B", -"31 c #70706D", -"41 c #7E7D7B", -"51 c #888886", -"61 c #8D8C8A", -"71 c #8A8988", -"81 c #888786", -"91 c #868583", -"01 c #313131", -"a1 c #3A3939", -"b1 c #4A4947", -"c1 c #61605C", -"d1 c #B6B1A8", -"e1 c #D1CBC0", -"f1 c #F2EDE0", -"g1 c #EFEADE", -"h1 c #F4EEE3", -"i1 c #E9E5DA", -"j1 c #E6E1D6", -"k1 c #DDD9CE", -"l1 c #C4C1B7", -"m1 c #7E7D7C", -"n1 c #878684", -"o1 c #8B8A88", -"p1 c #8B8B89", -"q1 c #8B8A89", -"r1 c #868584", -"s1 c #888887", -"t1 c #2E2E2E", -"u1 c #7C7B74", -"v1 c #E2DCD0", -"w1 c #F2ECDF", -"x1 c #F8F4E8", -"y1 c #F1ECDF", -"z1 c #E2DED3", -"A1 c #EAE4D9", -"B1 c #CECAC0", -"C1 c #C6C3BA", -"D1 c #DDD9CD", -"E1 c #C1BEB4", -"F1 c #FAF5E6", -"G1 c #B0AEA6", -"H1 c #A19E99", -"I1 c #908F8A", -"J1 c #777573", -"K1 c #747271", -"L1 c #727271", -"M1 c #7E7E7C", -"N1 c #8C8C8A", -"O1 c #888787", -"P1 c #838282", -"Q1 c #444341", -"R1 c #65635F", -"S1 c #F8F2E5", -"T1 c #FFFAED", -"U1 c #E9E4D9", -"V1 c #DBD7CC", -"W1 c #EDE8DD", -"X1 c #D1CDC3", -"Y1 c #E3DED2", -"Z1 c #D9D5C8", -"`1 c #FEFEF3", -" 2 c #D8D4C9", -".2 c #9C9994", -"+2 c #868684", -"@2 c #8D8C8B", -"#2 c #8A8A89", -"$2 c #858584", -"%2 c #868685", -"&2 c #4D4C4A", -"*2 c #71706B", -"=2 c #FAF7EA", -"-2 c #F7F2E5", -";2 c #E2DDD2", -">2 c #CCC8BF", -",2 c #E1DDD2", -"'2 c #DBD7CB", -")2 c #DCD8CC", -"!2 c #FDFCEE", -"~2 c #FFFDF2", -"{2 c #F3EFE3", -"]2 c #D9D5CB", -"^2 c #B4B1A8", -"/2 c #9F9C96", -"(2 c #81807C", -"_2 c #7F7E7D", -":2 c #8A8A88", -"<2 c #898887", -"[2 c #7C7B7B", -"}2 c #7E7E7F", -"|2 c #696766", -"12 c #444647", -"22 c #6B6B6D", -"32 c #61615F", -"42 c #5A5955", -"52 c #C7C2B8", -"62 c #E1DDD0", -"72 c #FEFCF0", -"82 c #FFFEF7", -"92 c #F8F4E7", -"02 c #E9E5D8", -"a2 c #D4D0C6", -"b2 c #BCB9B0", -"c2 c #DAD5CA", -"d2 c #E3DED3", -"e2 c #EEE9DE", -"f2 c #DFDBCF", -"g2 c #E0DBD0", -"h2 c #E2DED1", -"i2 c #F7F2E4", -"j2 c #C2BFB5", -"k2 c #B1AEA7", -"l2 c #9A9892", -"m2 c #878580", -"n2 c #8C8B87", -"o2 c #7C7C7A", -"p2 c #878786", -"q2 c #848483", -"r2 c #7F7F80", -"s2 c #797878", -"t2 c #838383", -"u2 c #62615D", -"v2 c #D3CFC4", -"w2 c #FFFAEB", -"x2 c #E7E3D7", -"y2 c #DFDACF", -"z2 c #A7A49E", -"A2 c #DBD7CD", -"B2 c #E0DCD1", -"C2 c #D8D3CA", -"D2 c #DFDBD0", -"E2 c #F3EDE1", -"F2 c #DDD8CD", -"G2 c #F0EBDE", -"H2 c #C1BEB6", -"I2 c #9B9993", -"J2 c #979692", -"K2 c #908E8B", -"L2 c #838280", -"M2 c #878785", -"N2 c #7D7C7C", -"O2 c #808080", -"P2 c #7B7B7B", -"Q2 c #565758", -"R2 c #A7A7A6", -"S2 c #52504D", -"T2 c #EAE5D9", -"U2 c #A9A69F", -"V2 c #DAD6CB", -"W2 c #ECE7DB", -"X2 c #E8E4D7", -"Y2 c #E5E0D4", -"Z2 c #BAB7AE", -"`2 c #DCD8CB", -" 3 c #CDC9BF", -".3 c #F4EFE2", -"+3 c #F6F2E5", -"@3 c #D7D3C9", -"#3 c #ABA9A1", -"$3 c #918E89", -"%3 c #92908B", -"&3 c #9E9C97", -"*3 c #A19F9A", -"=3 c #9E9C98", -"-3 c #908F8C", -";3 c #80807F", -">3 c #7F7F7E", -",3 c #727272", -"'3 c #777879", -")3 c #3A3B3C", -"!3 c #898681", -"~3 c #AEABA3", -"{3 c #BBB8AE", -"]3 c #F8F3E6", -"^3 c #F6F2E4", -"/3 c #ADAAA2", -"(3 c #D8D3C9", -"_3 c #C3BEB6", -":3 c #DEDACF", -"<3 c #CBC7BF", -"[3 c #E7E4D7", -"}3 c #F7F1E5", -"|3 c #B6B3AA", -"13 c #CBC8BD", -"23 c #F3EFE2", -"33 c #EEE9DD", -"43 c #C4C0B7", -"53 c #A8A69E", -"63 c #9B9892", -"73 c #9C9993", -"83 c #9F9D97", -"93 c #A5A39D", -"03 c #A9A7A2", -"a3 c #A2A09C", -"b3 c #949391", -"c3 c #898988", -"d3 c #7E7E7E", -"e3 c #767778", -"f3 c #666767", -"g3 c #5B5C5A", -"h3 c #353537", -"i3 c #555557", -"j3 c #696764", -"k3 c #EDE8DC", -"l3 c #FDF7EA", -"m3 c #FEFEF5", -"n3 c #FBF7EA", -"o3 c #E8E2D6", -"p3 c #E9E3D7", -"q3 c #C1BDB4", -"r3 c #BBB9B0", -"s3 c #A09D97", -"t3 c #B7B5AD", -"u3 c #E1DCD1", -"v3 c #B7B4AD", -"w3 c #E4DFD5", -"x3 c #E4E0D4", -"y3 c #F1ECE0", -"z3 c #F0ECE0", -"A3 c #CAC6BE", -"B3 c #C9C5BB", -"C3 c #A8A59E", -"D3 c #F7F3E5", -"E3 c #FBF8EA", -"F3 c #E6E2D4", -"G3 c #BDB9B1", -"H3 c #B0ADA6", -"I3 c #ABA9A4", -"J3 c #A6A4A0", -"K3 c #9F9E9A", -"L3 c #9A9995", -"M3 c #828282", -"N3 c #7B7B7A", -"O3 c #818180", -"P3 c #787777", -"Q3 c #6D6E71", -"R3 c #626263", -"S3 c #707072", -"T3 c #B2AFA6", -"U3 c #CBC6BC", -"V3 c #EEEADD", -"W3 c #FCF8EB", -"X3 c #FEFCF1", -"Y3 c #F7F4E8", -"Z3 c #D9D4C9", -"`3 c #A6A29B", -" 4 c #B5B2AA", -".4 c #DAD5CB", -"+4 c #ACA9A2", -"@4 c #C2BFB7", -"#4 c #E7E2D8", -"$4 c #EBE6DB", -"%4 c #DCD9CF", -"&4 c #A5A39C", -"*4 c #DBD6CC", -"=4 c #CFCCC1", -"-4 c #C5C1B8", -";4 c #D3CEC4", -">4 c #F4EFE3", -",4 c #DAD6CA", -"'4 c #B8B4AC", -")4 c #BBB9B1", -"!4 c #C0BEB6", -"~4 c #B8B6B0", -"{4 c #ABAAA5", -"]4 c #A2A19C", -"^4 c #90908E", -"/4 c #878685", -"(4 c #787776", -"_4 c #787979", -":4 c #66676A", -"<4 c #636466", -"[4 c #7E7C78", -"}4 c #D5D1C7", -"|4 c #DCD7CC", -"14 c #DFDAD0", -"24 c #ECE8DC", -"34 c #FFFAEC", -"44 c #FCF6E9", -"54 c #F9F4E7", -"64 c #C8C5BB", -"74 c #C7C2BA", -"84 c #ACAAA3", -"94 c #D6D2C7", -"04 c #A3A099", -"a4 c #A8A69F", -"b4 c #E1DDD3", -"c4 c #DCD7CE", -"d4 c #E4E1D5", -"e4 c #C4C1B8", -"f4 c #A7A49F", -"g4 c #CFCBC2", -"h4 c #9B9992", -"i4 c #A7A59D", -"j4 c #B1AEA6", -"k4 c #BEBBB2", -"l4 c #ADAAA3", -"m4 c #CBC8BF", -"n4 c #E6E2D8", -"o4 c #E3DFD5", -"p4 c #EFEBDF", -"q4 c #F2EDE1", -"r4 c #FFFBEE", -"s4 c #FEF8EB", -"t4 c #CCC9BE", -"u4 c #BCB9B1", -"v4 c #C4C1B9", -"w4 c #C1BEB8", -"x4 c #B4B2AD", -"y4 c #AFADA8", -"z4 c #A19F9C", -"A4 c #979694", -"B4 c #959492", -"C4 c #7C7B7A", -"D4 c #727273", -"E4 c #6B6C6D", -"F4 c #5B5C5E", -"G4 c #555659", -"H4 c #5E5D5D", -"I4 c #606160", -"J4 c #6A6B6D", -"K4 c #646462", -"L4 c #7A7A74", -"M4 c #A8A59D", -"N4 c #D0CCC2", -"O4 c #DDD8CE", -"P4 c #E8E4D8", -"Q4 c #EBE6DA", -"R4 c #F3EEE2", -"S4 c #FDF8EB", -"T4 c #9C9A94", -"U4 c #989590", -"V4 c #E4E0D5", -"W4 c #BCB8B1", -"X4 c #ABA8A2", -"Y4 c #CCC8BE", -"Z4 c #CBC8BE", -"`4 c #9D9B94", -" 5 c #918E8A", -".5 c #A4A29C", -"+5 c #E6E1D7", -"@5 c #E7E3D9", -"#5 c #E9E5D9", -"$5 c #F9F4E8", -"%5 c #BAB7B1", -"&5 c #A9A7A3", -"*5 c #ACAAA5", -"=5 c #A3A19E", -"-5 c #696A6D", -";5 c #505153", -">5 c #505155", -",5 c #95938C", -"'5 c #A29F98", -")5 c #B6B3AB", -"!5 c #C7C5BC", -"~5 c #C6C2B9", -"{5 c #DCD8CE", -"]5 c #E1DED2", -"^5 c #F7F2E6", -"/5 c #F8F3E7", -"(5 c #E5E1D5", -"_5 c #B8B5AC", -":5 c #D3D0C5", -"<5 c #A19E98", -"[5 c #BEBAB3", -"}5 c #DBD8CD", -"|5 c #D8D5CB", -"15 c #C5C2B9", -"25 c #D7D4CA", -"35 c #ACA9A3", -"45 c #BAB7AF", -"55 c #C9C6BD", -"65 c #B5B2AB", -"75 c #C9C5BD", -"85 c #81807D", -"95 c #908E8A", -"05 c #DFDBD2", -"a5 c #E0DBD2", -"b5 c #E3DED4", -"c5 c #F4F0E3", -"d5 c #BFBDB7", -"e5 c #ABA9A5", -"f5 c #7B7A7A", -"g5 c #717170", -"h5 c #79797A", -"i5 c #6A6B6E", -"j5 c #575657", -"k5 c #3F3F42", -"l5 c #5D5E5F", -"m5 c #A6A39C", -"n5 c #ACA9A1", -"o5 c #C2BFB6", -"p5 c #CAC6BD", -"q5 c #B9B6AE", -"r5 c #C4C0B8", -"s5 c #D1CDC4", -"t5 c #D6D2C8", -"u5 c #D8D4CB", -"v5 c #D9D4CA", -"w5 c #9A9791", -"x5 c #999690", -"y5 c #DAD6CC", -"z5 c #A5A29D", -"A5 c #C0BDB5", -"B5 c #C8C4BC", -"C5 c #D0CDC3", -"D5 c #E6E2D7", -"E5 c #CFCCC3", -"F5 c #D3D0C6", -"G5 c #B7B3AC", -"H5 c #8D8C88", -"I5 c #A7A59E", -"J5 c #D6D2C9", -"K5 c #989791", -"L5 c #D5D1C9", -"M5 c #9D9A95", -"N5 c #D0CDC4", -"O5 c #8C8B86", -"P5 c #DFDCD2", -"Q5 c #CDC9C1", -"R5 c #D2CFC7", -"S5 c #D7D3CA", -"T5 c #D7D2C9", -"U5 c #D4D1C6", -"V5 c #C9C6BE", -"W5 c #B3B1AB", -"X5 c #B4B2AC", -"Y5 c #747473", -"Z5 c #656566", -"`5 c #6B6C6F", -" 6 c #616061", -".6 c #56575A", -"+6 c #515255", -"@6 c #4B4C4D", -"#6 c #5D5E60", -"$6 c #4F4E4C", -"%6 c #827F7B", -"&6 c #888681", -"*6 c #95928C", -"=6 c #CFCCC2", -"-6 c #CDCAC1", -";6 c #D1CEC4", -">6 c #EDE9DE", -",6 c #E0DDD2", -"'6 c #EAE6DA", -")6 c #D5D2C7", -"!6 c #A6A49D", -"~6 c #D5D1C8", -"{6 c #B2AFA8", -"]6 c #A2A09A", -"^6 c #D7D2CA", -"/6 c #DFDBD1", -"(6 c #DCD8CF", -"_6 c #ADAAA4", -":6 c #D2CFC6", -"<6 c #C7C3BB", -"[6 c #D7D4CB", -"}6 c #A8A6A0", -"|6 c #8B8A86", -"16 c #827F7D", -"26 c #D2CDC5", -"36 c #ABA9A2", -"46 c #CECBC2", -"56 c #95938E", -"66 c #CFCCC4", -"76 c #C8C4BD", -"86 c #B5B3AD", -"96 c #BAB8B2", -"06 c #CAC7BF", -"a6 c #C4C0B9", -"b6 c #C3C1BA", -"c6 c #C0BEB7", -"d6 c #B9B6B1", -"e6 c #B6B4AE", -"f6 c #92908E", -"g6 c #848381", -"h6 c #828180", -"i6 c #6D6D6D", -"j6 c #68696C", -"k6 c #606163", -"l6 c #626364", -"m6 c #A9A6A0", -"n6 c #BBB7AF", -"o6 c #EAE5DB", -"p6 c #9F9C97", -"q6 c #DAD6CD", -"r6 c #A29F9A", -"s6 c #9E9B95", -"t6 c #DCD7CD", -"u6 c #DEDAD1", -"v6 c #BEBAB4", -"w6 c #B1AEA8", -"x6 c #BBB8B2", -"y6 c #B7B4AE", -"z6 c #C6C2BB", -"A6 c #B5B3AC", -"B6 c #7E7C79", -"C6 c #999894", -"D6 c #B7B5AF", -"E6 c #C3BFB9", -"F6 c #C6C3BC", -"G6 c #BEBCB5", -"H6 c #B4B2AE", -"I6 c #C2BFB9", -"J6 c #828281", -"K6 c #606161", -"L6 c #595A5C", -"M6 c #55565B", -"N6 c #525354", -"O6 c #5D5D5E", -"P6 c #7C7C77", -"Q6 c #A3A09A", -"R6 c #B9B6AF", -"S6 c #D1CEC5", -"T6 c #D4D0C8", -"U6 c #CBC7BE", -"V6 c #D3CFC6", -"W6 c #D0CCC4", -"X6 c #8A8984", -"Y6 c #AFADA6", -"Z6 c #7F7D7A", -"`6 c #DAD5CD", -" 7 c #A6A39D", -".7 c #888782", -"+7 c #D5D2C9", -"@7 c #B2B0A8", -"#7 c #A6A49E", -"$7 c #C7C5BE", -"%7 c #93918C", -"&7 c #D0CDC5", -"*7 c #D2CEC6", -"=7 c #84837E", -"-7 c #9C9A97", -";7 c #A5A39F", -">7 c #A8A6A1", -",7 c #C5C2BC", -"'7 c #BEBBB5", -")7 c #BAB8B3", -"!7 c #BAB7B2", -"~7 c #C2C0BA", -"{7 c #787879", -"]7 c #5A5B5E", -"^7 c #55575A", -"/7 c #4E4F52", -"(7 c #47494A", -"_7 c #404043", -":7 c #7F8081", -"<7 c #7E7D78", -"[7 c #A09E98", -"}7 c #DDD8CF", -"|7 c #878582", -"17 c #9A9792", -"27 c #B4B1AA", -"37 c #C5C1BA", -"47 c #D1CDC6", -"57 c #ACAAA4", -"67 c #BAB7B0", -"77 c #C1BEB7", -"87 c #A9A7A0", -"97 c #C4C2BA", -"07 c #9F9E98", -"a7 c #9D9B96", -"b7 c #93928E", -"c7 c #969591", -"d7 c #9B9A97", -"e7 c #8D8C89", -"f7 c #A3A19D", -"g7 c #BAB8B1", -"h7 c #BBB9B3", -"i7 c #B7B5B0", -"j7 c #B2B1AB", -"k7 c #969490", -"l7 c #858582", -"m7 c #646363", -"n7 c #69696B", -"o7 c #656568", -"p7 c #545457", -"q7 c #545559", -"r7 c #4E4E4C", -"s7 c #9B9994", -"t7 c #9E9C96", -"u7 c #A4A19C", -"v7 c #B1AFA9", -"w7 c #CEC9C0", -"x7 c #DAD5CC", -"y7 c #96948F", -"z7 c #AAA8A3", -"A7 c #918F8B", -"B7 c #CFCDC4", -"C7 c #9F9E99", -"D7 c #AFADA7", -"E7 c #B8B4AE", -"F7 c #B2AFA9", -"G7 c #96938F", -"H7 c #D3D0C7", -"I7 c #92908C", -"J7 c #A3A29F", -"K7 c #A4A39F", -"L7 c #8A8986", -"M7 c #9F9D9A", -"N7 c #969592", -"O7 c #989693", -"P7 c #A09E9B", -"Q7 c #9A9A97", -"R7 c #91908F", -"S7 c #848481", -"T7 c #82817F", -"U7 c #706F70", -"V7 c #5F5F61", -"W7 c #656669", -"X7 c #4E5052", -"Y7 c #525355", -"Z7 c #545352", -"`7 c #7B7875", -" 8 c #7A7A76", -".8 c #868480", -"+8 c #9C9A95", -"@8 c #ABA9A3", -"#8 c #B3B1AA", -"$8 c #BFBCB4", -"%8 c #D9D6CD", -"&8 c #CDCAC3", -"*8 c #BEBBB4", -"=8 c #888884", -"-8 c #CCC9C2", -";8 c #83837F", -">8 c #C3C1B9", -",8 c #BFBDB5", -"'8 c #C3BFB8", -")8 c #95938F", -"!8 c #8E8E8A", -"~8 c #A3A29E", -"{8 c #8C8C89", -"]8 c #81817F", -"^8 c #818080", -"/8 c #848484", -"(8 c #7D7D7D", -"_8 c #5F5F5D", -":8 c #5D5F62", -"<8 c #535457", -"[8 c #5B5C5D", -"}8 c #878681", -"|8 c #9B9893", -"18 c #9D9B95", -"28 c #AAA7A1", -"38 c #B0AEA7", -"48 c #A9A7A1", -"58 c #CAC6BF", -"68 c #D1CEC6", -"78 c #9A9993", -"88 c #94938F", -"98 c #CBC8C0", -"08 c #B2AFAA", -"a8 c #C7C4BD", -"b8 c #B2B0A9", -"c8 c #CCC8C1", -"d8 c #C9C6BF", -"e8 c #C8C5BE", -"f8 c #8E8D89", -"g8 c #B3B2AC", -"h8 c #9C9B97", -"i8 c #8B8A87", -"j8 c #8A8987", -"k8 c #81807F", -"l8 c #747475", -"m8 c #757677", -"n8 c #78797B", -"o8 c #797A7C", -"p8 c #78797A", -"q8 c #7A7B7C", -"r8 c #797A7B", -"s8 c #6C6C6E", -"t8 c #5C5D61", -"u8 c #595859", -"v8 c #93908C", -"w8 c #A7A59F", -"x8 c #B7B3AD", -"y8 c #BFBCB6", -"z8 c #9D9B97", -"A8 c #C7C3BC", -"B8 c #959490", -"C8 c #A6A49F", -"D8 c #A9A6A2", -"E8 c #92908D", -"F8 c #8E8C8A", -"G8 c #959491", -"H8 c #7A7A7A", -"I8 c #757676", -"J8 c #737374", -"K8 c #737476", -"L8 c #747577", -"M8 c #767779", -"N8 c #77787A", -"O8 c #78787B", -"P8 c #616263", -"Q8 c #5B5C5F", -"R8 c #515155", -"S8 c #4C4D50", -"T8 c #797977", -"U8 c #999793", -"V8 c #ADABA5", -"W8 c #8F8E8A", -"X8 c #B4B1AC", -"Y8 c #AEABA5", -"Z8 c #A2A09D", -"`8 c #9E9D99", -" 9 c #B7B4AF", -".9 c #A1A09D", -"+9 c #848482", -"@9 c #6B6B6B", -"#9 c #727374", -"$9 c #757577", -"%9 c #717174", -"&9 c #68696D", -"*9 c #6B6C70", -"=9 c #6D6E72", -"-9 c #707175", -";9 c #717275", -">9 c #6F7073", -",9 c #67696D", -"'9 c #5B5D5F", -")9 c #525254", -"!9 c #4F5053", -"~9 c #515256", -"{9 c #4E4F51", -"]9 c #5F6062", -"^9 c #8F8D8A", -"/9 c #969390", -"(9 c #9B9A95", -"_9 c #ACAAA6", -":9 c #A7A5A1", -"<9 c #888784", -"[9 c #817F7D", -"}9 c #898986", -"|9 c #9F9D99", -"19 c #A2A19D", -"29 c #7F7E7C", -"39 c #727172", -"49 c #6C6C6D", -"59 c #717274", -"69 c #676869", -"79 c #6A6C6E", -"89 c #666769", -"99 c #626468", -"09 c #5E6064", -"a9 c #5C5E62", -"b9 c #5B5D62", -"c9 c #5D5F63", -"d9 c #595B5F", -"e9 c #55565A", -"f9 c #4C4D4F", -"g9 c #47494C", -"h9 c #565759", -"i9 c #666667", -"j9 c #898885", -"k9 c #8B8986", -"l9 c #9E9C99", -"m9 c #AEACA8", -"n9 c #7B7B79", -"o9 c #A3A29D", -"p9 c #8F8E8C", -"q9 c #858482", -"r9 c #7E7D7E", -"s9 c #6F6F71", -"t9 c #6B6B6C", -"u9 c #737375", -"v9 c #5E5E5F", -"w9 c #5F6063", -"x9 c #616266", -"y9 c #5D5E62", -"z9 c #606165", -"A9 c #64656A", -"B9 c #616367", -"C9 c #5B5C61", -"D9 c #595A5E", -"E9 c #57595D", -"F9 c #585A5E", -"G9 c #56585C", -"H9 c #4E5053", -"I9 c #3B3B3E", -"J9 c #666768", -"K9 c #7D7C7A", -"L9 c #7D7D7A", -"M9 c #A19F9B", -"N9 c #7E7E7D", -"O9 c #7D7D7C", -"P9 c #727474", -"Q9 c #6E6E70", -"R9 c #707174", -"S9 c #6A6A6B", -"T9 c #67676A", -"U9 c #5E5D5E", -"V9 c #68696B", -"W9 c #616265", -"X9 c #616162", -"Y9 c #5F6163", -"Z9 c #5A5B5D", -"`9 c #626467", -" 0 c #5B5B5D", -".0 c #58595C", -"+0 c #606266", -"@0 c #5C5D62", -"#0 c #525357", -"$0 c #4E5054", -"%0 c #646567", -"&0 c #858483", -"*0 c #898987", -"=0 c #848383", -"-0 c #7F7F7F", -";0 c #5F5E5F", -">0 c #5F6061", -",0 c #58585A", -"'0 c #5A5B5F", -")0 c #58595B", -"!0 c #535456", -"~0 c #57585C", -"{0 c #57585A", -"]0 c #595B5D", -"^0 c #545558", -"/0 c #57595C", -"(0 c #535458", -"_0 c #46484B", -":0 c #595A5B", -"<0 c #666566", -"[0 c #7A7978", -"}0 c #808180", -"|0 c #858484", -"10 c #686665", -"20 c #868585", -"30 c #6E6E6E", -"40 c #757678", -"50 c #78787A", -"60 c #717173", -"70 c #6C6D6F", -"80 c #5D5E61", -"90 c #545658", -"00 c #535558", -"a0 c #4E4E51", -"b0 c #4B4C50", -"c0 c #515254", -"d0 c #5E5F5F", -"e0 c #666565", -"f0 c #6C6B6C", -"g0 c #787877", -"h0 c #8C8B89", -"i0 c #727171", -"j0 c #6D6D70", -"k0 c #707073", -"l0 c #626365", -"m0 c #636468", -"n0 c #5C5C5F", -"o0 c #636467", -"p0 c #535355", -"q0 c #525356", -"r0 c #505255", -"s0 c #515154", -"t0 c #505053", -"u0 c #4E4F53", -"v0 c #838486", -"w0 c #5B5A5B", -"x0 c #5A5B5A", -"y0 c #6E6D6E", -"z0 c #717070", -"A0 c #777776", -"B0 c #6D6E70", -"C0 c #646569", -"D0 c #4D4E51", -"E0 c #4C4E51", -"F0 c #4E4E52", -"G0 c #4D4E52", -"H0 c #3C3E41", -"I0 c #6E6F70", -"J0 c #696969", -"K0 c #696A6A", -"L0 c #777778", -"M0 c #79797B", -"N0 c #7D7E7F", -"O0 c #7B7A7C", -"P0 c #646364", -"Q0 c #626366", -"R0 c #4C4D51", -"S0 c #4F5054", -"T0 c #494B4E", -"U0 c #424243", -"V0 c #535454", -"W0 c #616364", -"X0 c #646466", -"Y0 c #646465", -"Z0 c #626466", -"`0 c #606064", -" a c #5E5E61", -".a c #606063", -"+a c #707173", -"@a c #737477", -"#a c #67686A", -"$a c #616264", -"%a c #646568", -"&a c #58595D", -"*a c #545458", -"=a c #444649", -"-a c #3D3D41", -";a c #5C5C5E", -">a c #676769", -",a c #6E6F72", -"'a c #5C5D5D", -")a c #494B4D", -"!a c #434547", -"~a c #414346", -"{a c #545557", -"]a c #5B5C60", -"^a c #626367", -"/a c #606164", -"(a c #6E7073", -"_a c #606062", -":a c #5F6162", -" , * * * ' - $ ", -" + ) * = = = * = ! ~ ", -" { * = * = = = = = - ] ", -" ^ , / = = = = = = = * ' ( _ ", -" : < * = * = = = = = = = * = ! [ ", -" } * * = = = = = = = = = = * = - | ", -" 1 2 3 = = = = = = = = = = = = = * ' 4 5 ", -" 6 < * = * = = = = = = = = = = = = = * = 7 | ", -" 8 * * = = = = = = = = = = = = = = = = * ' - [ ", -" 9 8 0 = = = = = = = = = = = = = = = = = = = * = ! _ ", -" a < * = * = = = = = = = = = = = = = = = = = = = * = 7 b ", -" c d e e e e f * = = = = = = = = = = = = = g e e e e f h i ", -" j k l m m m m n f = = = = = = = = = = = = o p q m m m m l r ", -" [ e = = = = = = = = = = = = s t ", -" u e = = = = = = = = = = = = s t ", -" v u e = = = = = = = = = = = = s t w ", -" ~ x u e = = = = = = = = = = = = s t y z ", -" A B C u e = = = = = = = = = = = = s t D { ", -" E F G H u e = = = = = = = = = = = = s t I J K L ", -" M N = N H u e = = = = = = = = = = = = s t I O s P Q ", -" R S ' = N H u e = = = = = = = = = = = = s t I O * T { U V W X Y Z ", -" E N ' ` = N H u e = = = = = = = = = = = = s t I O ` = ...+. @.#.#.$.%.%.&.*.=.-.;.>.,.'. ", -" ).!.= * = = N ). ~.e = = = = = = = = = = = = s t I O = ` {.s P Q ].^././././.(././././././.%._.:.<.[. ", -" }.|.O * = = = 1.2.3.4.4.4.4.4.4.4.4.4.4.4.5.6.= = = = = = = = = = = = O 7.8.4.4.4.4.4.4.4.4.4.4.4.9.0.= = = ` * = a. ].^././././././././././.^./././.^.b._.c.d.e. ", -" r f.= ` = = = * = * * * * * * * * * * * * * * = = = = = = = = = = = = = * = * * * * * * * * * * * * * = * = = = ` = * g.+. ].^./././././././././././././.h./././.^.i.i.j.k.l. ", -" m.n.= * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ` * s g. ].^./././././././././././././././././././././.^.o.p.q. ", -" r.s.O t.= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ` * * a.Q ].^././././././././././././././././././././.^././.^.u.v.w. ", -" x.f.= ` * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ` = y.P z. ].^././././././././././././././././././././././.^././.^.A.B.C. ", -" D.|.= * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ` * s E. ].^./././././././././././././././././././././././././.^././._.u ", -" r n.O ` = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ` {. .@ z. ].^./././././././././././././././././././././././././././.^./.#.F.G. ", -" m.N = = * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ` = T H. ].^./././././.^././././././././././././././././././././././.^./.^.%.I. ", -" J.K.L.M.N.O.O.P.Q.R.S.T.U.V.W.* = * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * = X.Y. I.@.Z.`.v. +.+++@+&.%.#.^./././.^./././././././././././././././.^./.#.#+ ", -" $+%+&+*+=+-+;+>+,+>+>+'+)+!+~+{+]+M.M.^+/+* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * ' 4 (+ _+:+E <+[+_.++/././.^./././././././././././././.^./.^.].}+ ", -" |+1+2+3+'+;+,+4+5+6+4+;+7+4+,+;+>+3+=+8+{+N.9+0+a+b+= * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = c+d+ e+f+g+B.h+/.^.^././.(././././././././././.i+/.[+Q ", -" j+k+l+4+m+,+;+n+;+4+5+o+p+q+7+r+5+6+;+>+>+3+!+s+N.N.t+u+v+* = * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * = w+Y. x+y+z+A+b.^././.^./././././././././.^.B+C+ ", -" D+>+6+q+6+,+,+n+n+;+6+E+r+5+r+*+o+5+,+;+3+{+s+s+M.F+G+H+S.I+J+* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * ' ( K+ L+M+N+^./.^./././././././././.^.^.;. ", -" O+P+s+Q+;+n+>+>+,+,+,+>+,+7+R+q+6+S+r+6+>+3+!+{+{+s+N.F+Q.T+R.U+V+W+X+b+= * = = = * = = = = = = = = = = = = = = = = = = = = = = = = = = = = * = = = = = = = = = = = = = = = * = = = = = = Y+Z+ v `+ @^.^./.^././././././././.#..@ ", -" +@@@,+!+'+,+n+,+;+,+,+,+,+,+,+n+'+L.>+>+=+#@{+s+$@s+s+]+s+$@s+s+s+s+]+N.%@&@*@* = = = = = * F =@=@=@=@=@=@=@=@=@-@;@>@- * = = = = = = = = = = = = = ,@! >@>@>@>@>@>@>@>@>@>@>@! 1.= = = = * = w+d. '@)@&././././././././.^./.++!@ ", -" ~@E+{@]@>+n+n+;+,+,+,+,+,+,+,+,+,+,+>+'+^@{+s+N.N.N.N.s+{+#@~+/@-+=+{+$@{+L.'+Q+(@_@* * = = = :@<@[@}@|@|@1@2@3@4@5@6@7@8@v.9@= = = = = = = = = = = = s 0@a@b@b@b@b@b@b@b@b@b@b@b@~ c@' = = * ' Y+d. d@e@^./.^./././././.^./.f@ ", -" g@h@>+q+S+S+q+,+,+,+>+n+,+,+,+,+,+,+;+n+i@;+;+>+>+>+>+;+;+;+;+;+;+,+>+>+4+r+4+,+n+j@k@l@* = * = m@n@o@p@q@r@s@t@u@v@u@u@w@x@y@e = = = = = = = = = = = = s t I O = = = ( Z+ >.z@^./.^././././.^.A@B@ ", -" &+,+>+C@,+,+,+7+E+&+o+S+D@;+>+>+,+,+,+;+,+;+i@>+'+'+-+m+m+'+'+;+,+,+,+n+K.,+S+o+5+6+>+n+;+E@F@` = = m@G@H@]+h@]+s+N.Q.I@J@K@L@M@N@O@= = = = = = = = = = = = s t I O * = ! P@ Q@Z.#./.^./././././.R@ ", -" S@D@T@7+q+S+o+o+7+,+U@4+5+E+V@5+6+;+>+n+,+,+;+'+>+=+{+s+$@s+s+s+W@8+;+n+;+,+X@>+,+,+4+n+>+,+;+;+Y@Z@* = m@`@ #,+>+>+>+>+.#'+>+'+L.=++#@#= = = = = = = = = = = = s t I O ' ##Y. $#%./.^././././.%#&# ", -" *#R+6+S+S+r+*+*+&+*+=#=#o+5+q+4+C@r+&+E+7+-#]@n+;+>+L.~+!+L.>+;+;+,+,+>+m+;+n+,+,+,+,+X@n+,+,+,+>+]@j@;#>#* m@,#'#)#!#*+~#E+E+E+*+{#=#S@]#e = = = = = = = = = = = = s t I O 7 d+ ^#/#(#^././.^.B+_# ", -" :#,+7+r+o+&+*+S@S@S@=#<#<#S@*+*+*+o+S+6+7+S+R+C@,+>+n+]@,+6+q+7+7+7+S+o+=#&+6+,+,+,+,+,+,+,+,+n+>+;+4+7+[#}#Z@m@,#|#1#2#3#4#2#)#2#4#5#6#7#8#e = = = = = = = = = = = = s t c@( (+ 9#_.B+^./././.R@ ", -" 0#L.S+E+o+*+S@=#<#a#2#3#3#3#b#<#b#S@*+o+*+*+*+7+6+7+c#C@;+>+>+7+S@)#b#d#2#=#o+S+6+4+n+,+,+,+,+,+n+;+6+r+*+=#=#e#f#g#h#'#)#1#*+*+*+S@4#i#j#k#l#m#e = = = = = = = = = = = = s t n#o# e+p#/.^././.q#r# ", -" s#>+t#o+&+S@S@<#a#b#2#2#u#3#)#=#<#)#b#2#2#b#)#)#v#S@w#r+E+x#o+y#r+S+r+r+r+r+S+q+6+,+,+,+,+,+;+;+;+,+6+r+*+S@o+r+o+e#z#A#B#r+C#6+4+6+r+2#D#i#5#D#m#e = = = = = = = = = = = = s t k. E#u./.^./.F#G# ", -" H#I#{@S+E+J#S@=#2#2#3#3#3#K#3#u#)#)#b#2#u#3#4#4#4#4#3#u#u#2#)#2#3#2#2#2#)#b#b#2#b#b#=#=#*+*+&+o+r+p+5+S+*+*+E+7+7+r+J#*+E+L#q+6+,+q+r+&+M#d#)#3#4#N#8#e = = = = = = = = = = = = s t O#h+/.^.^.P# ", -" Q#6+t#V@S@<#b#b#a#b#3#R#u#3#4#3#R#S#R#4#T#U#V#D#W#W#D#4#U#U#4#4#D#W#W#X#D#N#7#Y#)#S@o+r+r+o+&+=#=#)#)#)#)#=#&+5+4+,+7+S+q+4+4+;+,+6+Z#1#M#*+*+)#2#2#u#8#e = = = = = = = = = = = = s t `#F#/.^. $ ", -" .$2++$&+S@b#2#b#2#3#3#R#3#R#4#4#4#4#4#N#N#l+W#@$X#X##$W#$$N#N#N#U#%$W#X#&$&$X#X#@$W#D#4#2#<#*+r+7+6+,+4+4+4+7+q+,+;+>+'+-+;+;+>+,+q+E+*+*$=$-$;$>$>$,$>$'$e = = = = = = = = = = = = s )$ !$^.^. $ ", -" ~$S+&+S@<#<#b#u#2#3#R#4#U#U#N#D#D#D#l+D#{$W#]$&$j#j#j#j#X#D#N#$$U#N#$$W#X#&$X#W#X#X#W#N#N#4#Y#b#{#*+S@E+C#4+,+,+;+;+,+6+5+5+6+4+;+4+5+*+[#o+^$/$($_$_$_$_$:$b+= = = = = = = = = = = = = <$[$}$|$|$|$}$1$ 2$++^. $ ", -" t#3$<#b#b#b#<#b#2#Y#4#U#U#D#D#$$W#W#X#X#&$j#4$4$5$4$4$6$j#X#D#N#U#%$$$i#W#W#X#&$&$X#X#W#N#U#R#7$8$9$=#S@S@=#)#)#=#*+*+S@)#)#1#r+6+,+q+0$r+S+o+a$b$<$= = = = = = = = = = = = = = = = = = * = = = = = ' c$ d$^. $ ", -" s#7+S@<#<#b#e$b#f$u#3#g$4#h$D#l+W#W#&$j#i$4$4$j$k$l$k#l$4$m$W#N#N#N#D#W#W#X#n$o$j#j#X#W#W#N#4#S#2#)#)#=#=#)#)#b#p$d#)#1#S@*+E+S+q+4+4+6+5+5+q+0$*+q$r$s$s * = = = = = = = = = = = = = = = = = = = * 0 h.t$ u$$. $ ", -" H#t#S@<#I#S@I#<#b#u#3#R#7#7#h$l+v$X#&$&$4$4$w$x$y$k$z$A$l$i$X#l+D#D#D#X#X#X#B$B$C$B$j#X#W#W#$$4#3#)#v#)#)#)#2#u#3#b#1#<#1#=#o+7+4+,+;+,+;+>+6+6+6+4+p+D$E$|.= t.= = = = = = = = = = = = = = = = = * o F$G$H$I$J$K$ y L$ ", -" M$N$w#O$<#v#<#I#P$S@<#u#U#D#$$N#l+#$#$B$Q$&$j#R$w$y$k$S$A$l$B$X#X#X#X#&$&$j#R$j#R$4$j#Q$X#X#W#$$4#Y#b#S@S@T$b#b#)#=#=#<#&+r+r+o+w#q+K.X@>+;+'+#@L.'+m+m+>+q+U$Y+= * t.= = = = = = = = = = = = = = = J (.V$p@R.I@W$X$Y$ [.Z$L$ ", -" `$ %.%+%@%w#+$I#S@&+&+&+b#U#l+D#D#l+#%#$#$C$j#Q$j#4$5$$%%%A$l$4$&$&$B$j#5$4$y$5$5$j$4$k$j#&$W#&%N#3#u#b#S@&+=#b#)#<#S@*+o+S+S+7+4+,+]@*%>+>+>+n+;+=+3+'+m+~+{+=%-%;%s * = = = = = = = = = = = = = * * >%,%'%)%I$H$K@!%~%{%]%^% /%(%_% ", -" :%n+<%[%+%N$.%.%<%+$O$&+P$<#b#U#l+X#X#l+W#l+l+}%j#j#4$|%1%2%3%A$l$k#k#k#l$4%k#z$l$l$5$5$4$4$o$W#$$R#T$<#<#5%E+&+S@S@&+E+E+S+7+6+6+D@,+>+*%>+,+,+4+7+r+R+>+;+;+'+6%7%8%s$= t.= = = = = = = = = = = * s h.9%F+T+T+T.W$0%X$M@a%b%c%d%c%e%e%f%c%{%g%h%!%I@I@T+Q.R.i%K@7%2+j% k%l%m% ", -" Y$N$@%<%n%Y$o%p%q%r%{@s%I#t%<#u#U#l+W#l+D#N#U#N#X#j#l$u%v%w%u%2%x%y%A$A$z$5$z%4$l$|%4$A%R$4$&$W#U#u#<#w#S++$E+E+S+E+S+7+7+7+6+,+4+,+,+D@6+w#&+S@S@=#)#)#*+S+6+q+q+;+B%C%Y+' * t.= = = = = = = = = * /.D%H@s+N.F+Q.R.H$K@E%F%G%G%F%L@K@H%I$R.F+]+!+L.>+>+>+'+4+]@>+m+3+N.I%N$H#J% /%O@,@K% ", -" L%M%o%N%<%n%B%o%O%P%P%Q%@%O$O$O$P$t%b#R%R#N#U#4#U#l+j#l$y%S%T%U%U%V%V%y%1%A$z$z$z$z$z$z$k#l$j#X#D#4#b#S@w#2+2+{@W%{@+%+%+%.%2+6+,+,+,+6+C#r+w#&+=#)#2#1#=#)#)#o+0$5+7+q+X%Y%|.Z%* = = = = = = = * J c$`% &L.!+.&M.N.+&F+Q.Q.Q.Q.Q.Q.G+N.h@!+'+D@S+o+5+*+K#u#2#=#)#<#*+o+E+S+,+L.=+{@@& #&s$' b+K% ", -" $&%&&&P%q%n+*&B%O%O%=&Q%-&W%O$9$O$D$*$3$+$s%T$R#U#&%Q$l$w%S%;&V%V%>&;&,&'&)&1%z$1%1%!&u%l$4$X#$$4#3#<#E++%~&,+2+.%.%K..%2+K.U@>+2+6+6+S+E+o+*+)#3#4#3#3#3#2#=#*+*+o+r+6+5+5+{&>@= t.= = = = = * s /.]&,+'+-+!+{+{+~+!+#@^&{+!+/&8+!+-+q+r+S@)#(&i#X#X#i#(&{$B$_&W#&%:&3#p$S@&+E+7+6+>+h@<& [&}&' = * |& ", -" 1&2&&&3&2&2&&&&&4&O%5&&&O%p%B%@%3$O$P$*$<%n%+%*$T$R%$$C$5$v%6&7&8&9&7&V%0&y%a&b&y%c&d&z$k#4$}%$$U#R#<#&+@%n+*%K.n+e&>+B%*%>+>+*%>+.%+%7+E+*+<#)#)#b#2#3#3#2#d#)#)#=#*+S+q+q+5+f&U$- O * = = = = 0 F$E@ #>+n+;+,+K.K.;+>+L.=+L.>+;+7+*+K#(&D#_&i$i$4$j$k#2%b&g&'&h&i&_&X#X#D#j&2#=#&+E+R+6+]%Q.k& l&= * = f m& ", -" 4&&&n&o&%&%&p&q&p&%&%&%&4&P%o%N$Y$*$t#D$*$r&N$N$+%I#l+m$s&w%t&T%u&7&7&v&t&t&w&b&v%5$C$j#4$m$D#4#u#v#P$w#+%r% %e&e&e&e&e&*%r%e&*%>+2+6+c#&+S@<#)#b#b#2#2#)#8$d#=#*+r+5+6+4+,+6+x&y&z&|.O t.= * ' c$A&B&;+;+;+;+>+,+6+7+6+,+,+5+E+o+o+=#3#%$X#j$u%b&C&T%D&E&F&1%2%'&;&8&C&G&&$_&4#2#b#=#E+E+7+,+7%>+D$ H&O@= = = I&J& ", -" K&L&M&&&4&=&N&N&q&q&N&O&N&P&q&2&Q&P%Q%-&n%n%*$*$+%n%r&Y#d&z$y$1%t&w&R&T%0&)&1%S&z$l$l$j#T&C$W#D+b#O$&+t#{@K.L.U&q%/&/&7%V&e&B%e&*%n+D@2+t#W&<#<#=#1#u#X&2#)#<#J#&+E+S+6+4+4+4+;+K.,+'+Y&<$= = O F$Z&`&4+q+5+7+7+7+q+4+r+*+*+o+o+<#4# *k#.*v%b&+*@*#*8&$*%*9&D&+*g&2%&$u%&***|%v$j#D#2#=#)#&+o+S+,+K.!+=*-*;* i.= = * * O >* ", -" ,*'*)*!*p&~*p&p&{*q&p&%&]*]*p&!*p&%&2&&&P%p%r%K.n%@%3$O$D+^*)&/*1%!&1%(*w%_*l$l$5$l#C$C$l+$$%+R#S@W%n%+%K.p%:* %<*7%[*/&<*U& %e&n+~&6+7+S+E+*+S@=#)#=#=#=#=#=#=#)#<#)#!#}*o+o+y#q+4+4+|*1*2** c$3*4*4+r+*+)#2#2#3#2#M#*+*+=#b#4#D#5*1%C&D&**6*7*%*8*9*0*a*0*b*c*d*e*6*g&|%$%g&.*f*&$W#T#)#=#&+g*S+6+>+L.8+N.h*L.t%i*k&j*k&k*l*M$J$W%i*k*m*k&n*o*p* 8 * = * = ` q*r* ", -" s*t*u*v*w*x*y*u*z*A*B*q&p&p&p&%&{*p&p&2&%&&&P%p%C*n%*$D$S#D*l$5$)&2%y$A%A%y$5$d&A%C$$$U#$$u#T$<#&+W%N$p%*%:*7%7%:*:*]%/&E* %U&:*L..%2++%@%S+E+&+&+*+S@o+*+=#=#S@o+5+q+F*S+o+*+=#E+q+,+5+G*{&)$H*I*;+6+o+=#=#1#2#N#$$N#h$N#W#j#$%g&D&8&J*c*0*K*b*L*a*M*M*a*N*O*a*M*M*N*K*P*&*'&5$i$X#v$i#(&)#*+*+S+S+7+>+m+!+/&~+{+$@s+$@#@!+3+!+!+L.'+>+m+!+s+s+s+{+L.@@ Q*R*0 = * = = = S*Q@ ", -" T*A*z*x*U*V*W*W*X*X*Y*Z*y*`*A*!*N&O&]*o&2&~*2&&&Q%N$n%N$ =U#_*c&.=j#1%d&C$$$#$&$X#$$R#b#u#b#O$w#+%{@K. %/&/&=&=&^@^@/&:*:*7%:*e&V& %.%2+2+{@C#S+S+E+~#=#&+p+S+E+S@=#3$7+,+;+4+6+6+4+,+7+5+7++=@=5+r+r+*+e$5#&$j#j#4$|%k##=1%g&T%$=%=%*&=L**===-=-=;=>=-=M*b*K*a*M*0*b*$*%=#*w&g&2%%%&${$@$N#p$<#<#r+E+S+T@K. %!+=+=+!+~+!+'+4+q+o+,=7+V@e#o+r+6+-#>+-+h@'=J$)= !=X.' * = = = = ' ~= ", -" `*y*x*V*X*V*X*{=]=^=/=(=_=:=y*=%P&N&p&p&<=~*%&2&2&Q%-&B%[=Y$*$S#R%H#T$R%D+D+U#l+&%R#b#<#S@+$t#w#2+p%p%:*O%/&M%]%^@^&]%=&O%/&7% %:*e&*%n+,+,+6+7+7+S+5+7+R+o+*+o+7+4+7+5+6+X@>+>+;+;+4+7+5+q+}=|=5+5+o+*+=#X&i$g&b&.*1=2%g&w&C&V%**6*2=M*-=-=-=3=3=-=*=4=L*K*d*0*0*7*5=#*b&g&v%#=5$k$$%&$%$i#{$4#2#)#=#&+o+S+6=6+>+L.L.-+>+>+,+r+=#2#3#j&4#T#3#2#{#o+5+q+4+'+{+{@k*7= Q@..8=' * = = = = * O@9= ", -"0=^=:=^=a=b=^=^=c=d=e=_=f=g=h=i=y*'*'*N&N&]*~*j=2&&&O%Y$<%k=*$D$3$O$M$l=S@S@s%t%9$9$I#<#t%+$W%n%n+N$:*O%O%=&=&^@J$M%M%=&^@=&=&/&:*:*:* % %*%,+K.6+6+q+q+7+6+4+q+q+C@2+;+>+L.-+>+;+>+n+;+,+C@q+4+T@q+q+7+5+o+J#!#X#E&T%x%A$$%l$A$w&$=8*0*a*L**=>=m=m=*=&=&=a*d*d*0*2=n=o=x%E&E&D&&*E&i&k$|%j$D#W#D#4#2#2#2#*+o+o+o+C#7+6+7+q+S+=#v#%$p=W#]$i$ *&$B$_&_&X#q==#M#*+4+'+'+r=s=h+0.* = * = = = = = = t= ", -"u=v=/=w=e=x=y=x=y=z=A=B=z=C=D=E=F=y*G='*H=I=N&<=J=4&P%K=D$s%P$P$&+3$3$D$s%O$M$D$*$<%@%D$3$*$2+*%B%p%=&=&^@J$^%^%J$J$M%J$M%^@{+M%]%]%^@/&=+:**%*%>+,+K.;+,+>+>+>+L.=+=+L.=+!+7%e&'+,+n+>+4+>+,+4+x&6+q+6+q+7+r+y#o+3#@$v$X#L=5$2%T%9&M=0*N=-=L*O=P=*=*=Q=b*M*K*%*%*J*R=8&8&&*C&A$k#2%u%k$j#k$|%i#X#D#D#3#3#3#<#&+*+5%V@&+*+S=<#2#{$T=U=A%5*$%u%u%z$A$v%E&V=4$%$W=X=Y=-$Z=`== = t.= = = = = * = .z+ ", -" -w=.-C=y=z=+-A=A=B=A=A=A=_=/=i=@-X*#-G=q&I=$-]*p&%&%&N&%-n%*$3$s%&-<%n%<%D$3$*-<%n%N$C*.%N$B%<*:* %/&M%3&=-----------^%J$J$J$=&M%J$;-J$]%:*/& %*%*%>+L.w@L. %L.=+!+!+!+!+{+~+L.'+n+;+;+C@r+S+q+q+7+r+S+5+r+r+o+r+r+o+=#4#&$l$>-D&8&,-0*'-m=m=*=a*M*N*0*0*c*7*M=%*)-**!-~-8&**;&V%0&0&b&x%$%|% *v$X#D#W#4#3#N#2#*+S@)#S@S@)#3#{-]-%.^-y%E&/-$=V%#*/-,&x%g&(-_-:-<-[-}-@#O = * = = = = = = = ' |- ", -"1-2-3-B=+-z=A=A=z=A=3-4-f=_=C=C=5-@-Z*G=G=q&]*q&'*)*)*)*!*p&4&%-n%6-N%o%-&r&w#+%.%N$N$n+B%e&p%[*&&=&^@^%=-=-^%^%=---7-----7-----h*;-J$h*^&!+=&~+L.L.L.L.L.8-=+8-L.!+=+)+=+!+ %'+>+,+4+7+r+J#=#*+)#<#)#)#p$=#=#*+=#E+*+:&D#5*F&$=%=%=J*a*-===*=O=b*0*9-0-a-2=$*8*J*J*b-6*&*E&D&8&>&D&y%A$|%$%5*V=l$v$ *D#D#N#2#N#3#=#=#)#<#b#4#c-d-@#e-f-g-%=%=8&;&7&h-i-j-k-l-=.0.0 = = = = = = = = = = t.= m-n- ", -"o-p-q-r-s-t-u-v-w-x-3-y-h=h=C=_=_=z-A-B-C-G=B*'*A*D-D-E-F-!*%&%&4&Q%o%O%P%U&-&n%N$B%p%*&O%=&=&=&^%J$^%=-G-H-H-7-7-7-I-I-J-H-H-K-K-H---N.;-J${+]%!+7%=+ % %L.=+m+L.=+)+>+;+]@D@C@7+S+*+2#L-5#W=@$j#M-@$D#N#N#3#4#4#@$D#{$ *5$2%&*d**=N-m=O-P-Q-Q=a*R-S-M*c*T-b*c*d*J*J*,-8*6*8&$=C&x%U-#*V%T%x%j#$%$%_& *{$D#D#3#4#3#)#=#<#p$V-W-6.@#X-v%&*D&U-Y-Z-`- ;.;+;@;:@#;= t.= = = = = = = = = * = $;'. ", -"%;&;*;=;&;r-*;p--;*;q-p-;;>;y-h=_=_=(=,;';);`*G=v*v*v*!;~;~;!*p&o&4&&&&&&&M&&&{;o%p%O%p%e&=&2&2&=-G-];7---H-J-H-7-H-H-^;/;(;_;:;K-I-<;=*[;;-^@{+7%};7%=+=+=+ %L.'+>+U@U@;+6+E+)#K#|;D#@$B$ *5*i$j$j$x%0&5* *1%v$v$&$B$ *u%#*R=$*0*1;2;3;4;5;6;7;8;9;O-0;L*M*a;0*M*b;7*K*2=c;7*d;8&R=5=R=!-e;#*&*,&|%i$k#B$X#v$X#D#4#4#2#f;g;h;i;= @#j;k;l;m;n;o;p;q;r;s;0 o = * = = = = = = = = = = * s t;u;v; ", -"w;x;y;z;A;z;B;x;x;z;C;D;E;E;q-w-F;y-y-G;H;4-_=E=I;X*J;y*!;!;!;!*p&n&4&M&2&J=2&p&M%O%M%^@K;O%M%--G---J-I-J-H-H-J-H-I-I-K-L;M;M;K-N;(;K-<;O;J$^&{+7%:*<*8-=+L.*%B%>+,+7+R+r+r+*+)#N#_&j$j$$%z$E&u&D&g&B$D&g-A$5*P;j$_&g&u%n$C&Q;M*a*Q-R;S;T;U;V;W;9;4;O-X;0;1;N-*=*=*=L*a*N*0-0*Y;g-$*)-&*x%2%Z;5*g&5=0&5$C&|%i$j#j#]$D#N#`; >f = = f >.>+>^.@>,@* * * = = * = = = = = = = = = = = * = #>E$R+,+3& ", -"$>%>&>*>A;=>E;*;q-D;s-->;>q-&;->A;->p->>,>'>;;;;A=C=c=)>X*x*D-A*!*%&%&&&~*p&!>!>!*~*^%G-G-2&^%G-];];I-^;~>{>I-I-^;]>^>/>(>_>:><>N;(;[>}>O;^&J$/&=+ %L.*%>+]@,+4+C#c#S+o+2#N#|>&$$%x%T%1>$=9&,-Q;K*Q;h-i$V%$*g&4$$='&j$R=R=|%E&c;M*a*1;2>3>4>5>3;X;>=Q-*=*=X;O-6>X;X;0;m=a*N*M*0*0*c*,-8&**R=d;**g&V%9*#*D&T%7>A$u%A$i&8>9>0>= * = * ( a>q*s O = * = = = = = = = = = = = = = = * = = b>c><#S@t#.%d>e> ", -"f>g>B;h>i>j>k>i>l>l>i>m>x;->A;n>o>B;p>x;->D;-;-;q>z=r>s>s>c=W*z*A*'*]*o&~*j=2&%&2&=-~*G-2&G-G-7-H-H-^;K-^;$&K-I-K-^>(>:>t>u>u>t>N;(;K-}>N.v>]%7%*%*%>+K.,+6+C#r+R+o+)#j&D#&$5$.*T%8&%=w>7*b*0*M*M*M*0*d;4$#*K*#*4$x>y>5*n=M=k#A$c;M*M*-=4;8;2;a*N*a*a*M*a*z>2;6>6>6>A>A>B>*=N=M*0*a*c*P*%*0*a*L*%*b-0*$*$=C>**;&t&D>E>r$-@= * = = = = = F>` = = = = = = = = = = = = = = = = * = 6.G>H>C$4#u#<#S+K./&I> ", -" J>K>L>M>N>N>N>N>O>P>Q>P>i>m>h>R>S>&>g>h>&>B;z;n>E;B=C=v=^=F=T>U>A*'*]*%&j=~*=-^%j=!*~;];F-];V>^;J-K-^;W>X>^;Y>M;^>:>u>Z>`>g% ,<>L;~>O;;-]%7% %K.,+,+7+.,<%5+&+1#2#R#+,_&$%g&&*R=7*K*c*M*O*@,@,a;0*M*a;%=i$**K*1>_&#*D&n$8&M=%%F&P*g-)-2=M*0*Q;P*J*d*#,L*1;P-9;U;$,A>6>6>A>O-m=*=Q-%,&,W;5;7;W;T;O=-=*,-=K*a*N*=,-,;,0.= * = = = = = = = = = = = = = = = = = = = = = = * = = ,@>,,,D&A%',N#g$T$W%K.J$), ", -" L>!,~,O>{,],A;z;^,m>P>/,(,_,:,N><,[,},|,<,|,h>B;*>&;'>r>1,Y*U>y*`*'*q&2,I=%&~*!*3,!*G-D-4,4,^;5,^;W>6,Y>W>W>^>M;u>u>`> ,g%g%u>_>(;<;J$J$7%:**%,+2+{@7+S+E+O$=#b#T#@$k#w&#*8&J*K*a*'-L*L*a*O*&=R-7,a;8,M*;&j##*,-V%4$w&T%i$D&%=k#%%b&2%u%b&V%8&%=)-P*K*L*2;9;T;W;T;T;9;4;4;P-*,O-P-U;9,0,a,b,c,d,e,f,g,*,h,h,R-i,j,k,= * = = = = = = = = = = = = = = = = = = = = = * = O i;2.l,m,8&n,_*l$o,T&R#<#E+,+N$ ", -" p,q,r,M>s,h>B;x;A;t,&>u,v,q,q,q,_,w,x,x,N>i>s,<,&>y,&;z,z=A,Y*A*'*q&q&Q&B,B,N&!*F-C,G-4,4,4,^;$&W>Y>Y>^>W>^>:>:>u>D, ,E,E,D,F,_>}>h*M%/&]%e&n+,++%S+E+&+b#3#D#X#5*u%T%$=R=P*M**=0;X;h,%,-=M*a*R-8,M*a;M*0*G,j#%%H,R=l$5*x%5*w$V=$%j$u%u%I,$%'&J,J*2=M*M*g,7;5;T;T;K,2>K,L,T;M,N,O,P,Q,R,S,T,U,O,V,W,X,O=W;i,Y,Z,`,= = = = = = = = = = = = = = = = = = = = = = = * = = '.'+'@'X;#'&*$'y$v&/*D+R%P$O$ %%' ", -" &'*'!,M>j>='x,x,='M>-';'q,>',''')')')'*'v,J>K><,[,g>->&;!'H;_=@-`*q&q&T*~'I=B,4&p&!*G-4,{'V>W>Y>Y>]'M;^>M;M;^'/'D,('_':':'E,h%N;[;=&^@:*e&*%,++%7+E+<#2#U#D#j#2%#*)-<'['=,h,X;*=*=m=8;}'U;-=M*O*0*b*,-8*7*d*M=A$&$b&,&5$X#j$ *X#&$i$i$%%.*g&D&/-**P*|'*,1'7;2'3'4'4'5'6'7'8'9'0'a'b'c'd'e'f'g'S,h'i'@'m=}'j'k'l's * = = = = = = = = = = = = = = = = = = * = = * 0 m'n'o'p'S;2'q'd;#'9&y%)&U%#$R#9$s%O$H-r' ", -" s't'u')')'*'q,v'v'w'x'y'z')'''A'u'u'u'B'z'z'_,='C'L>D'->E;E'F;y-v=F'A*I=T*Q&N&N&N&p&)*!;{']>G'Y>^>H'I'M;M;M;^'J'K'L'M'M'N'D,u>X>h*O'/&:*K..%6+7+E+S@3#$$W#j#'&D>P'N*m=Q'R'8;7;R'4;3;0;W;2'A>P=M*0*7***b&A$E&>&5=E&j$5$j$v$W#@$]$_&j#5*k$'&$=8*6*!-7&K*S'3'h'7'T'U'T,0,V'W'a,a'X'Y'Z'`' ).)+)@)#)6'$)%)4;5'&)*)!.O * = = = = = = = = = = = = = = = = = = ' s =)-);)>),)')))!)j'2>~)n,{)])/*1%A%U#%+I#M$p%2& ", -" q,t'A'^)/)()_)x':)v,u'()<)[)})<)[)<)})u'z'z'^)|)v,:,<,1)2)f=z=,;A,3)y*H=T*I=I=N&p&{*~;4)]>Y>5)6)7)M;H'8)(>u>('9)0)a)b)9) ,(>K-h*=&O%:*K..%7+S+S@b#U#X#k$2%D&d;d*L*c)9;7;X,7;X,V;5;$,>=X,d)6>e)0*7*~-$=V%A$ *|%%%%%k#f)A$B$W#n$_&j#i$4$5*|%g&n=g-Q;a*-=*,g)h)i)i)j)k)l)g'm)n)o)p)q)r)s)t)u)v)c,w)x)y)z)A)K,B)C)t;O J = = = = = = = = = = = = = = = O o D)E)F)G)H)A>I)J)}'K)L)M,f,d*9&M)&*d&z%l+k+k&P$N)M%O) ", -" P)Q)[)q,Q>v'w'^)R)Q>z'<)t'})<)S)t'T)Q)Q)[)/)u'U)z'*'|)O>%>-;V)D=@-B-W)y*B*X)I=o&{*~;4)Y)5,Z)Z)I'5)H'`)^'/' ,N' !.!+!@!:'Z>L;I-H-^&O%O%#!2+W%&+<#R#v$5$'&C&!-d;Q;|'3;P-9;7;W;K,2>}'$!4;X;8;9;Q-0*J*%=M=c;M=#*'&A$k#|%i$k$l$$%i$_&&$4$g&,&C&u%u%5=*=T;K)2'3'%!&!a'l)*!=!-!;!>!,!'!)!!!~!{!]!^!/!(!V'_!e,K,U;O={):!# = = * = = = = = * = = = = 6.k>z;e!z-f!W)y*g!B*H=N&p&A*v*z*h!Z)i!5)I'H'j!8)k!l!9)b)m!n!0) ,:>^;];7---M%:*n++%S++$<#D#5$v%0&/-~-o!*=P-f,f,p!K,K,5;2>2>2'}'q!2;6>0;M*)-%=,-r!%=#*V=#=v%b&b&'&z$k$ * *u%.*j#k#R=,-%=%=z>K)Q,s!W't!u!v!(!l)w!^!]!x!y!z!~!)!~!A!B!C!D!E!c,!)U;F!M)G!g,H!I!J!s = = = = = O q*0 K!L!M!N!O!P!Q!R!S!C>T!d;5!K,U;~)U!S,K)b*~)p!V!W!U%X!d&m$#%D+g@s%6-%&Y!", -" Z!`! ~[)}).~B'B'B','y'_)<)c!+~@~Q)a!Q)#~Q)#~Q)Q)#~()A'q,$~N>%~&;*>2)3)&~g!g!g!*~q&'*A*h!x*Z)I'I'(>H'8)=~-~(';~.!>~,~'~^'N;^;];--^%^%^@n+{@E+O$~$l+'&$'>&d;%*-=X;f,)~2>e,K)2'!~~~~~5;K,R;2;N-N-P=K*,-P*d;D&x%E&v%j$j$|%k$i&E&T%A$X#v%8&'&_&**K*%*8&a*h,7;5;{~i)]~%)a'^~%)/~(~_~:~~!<~[~}~|~1~$)2~3~p!d;4~5~f,p!6~7~8~9~= * = = .0~a~b~c~d~e~f~g~h~j'i~j~#'O=d;V!L,k~}'K)2'j'U;Q=4!l~h,6&5$d&A%i*$$T$O$k=%-q&", -" /)<)/)A'y'B'B'B'B'y'()[)t'#~m~n~o~p~q~r~r~q~s~t~s~#~}))'Q>:,l>e!E;E;_=B-y*G=u~G=A*z*x*U*W*{=v~H'w~x~/'l!y~z~A~B~C~('D~W>^;^;J-H-=-M%O%6+E+<#l+',,&5=J*K*-=E~P-4>~~i,S;h~e,6~K,W;R;R'R'6>F~M*M*b*K*,-%=~-~-)-P*R=S%C&g&%%j#5$C&v&j#j$8&D&j#$=c;$'V%0*-=G~H~Q,t!^~I~l)e'x!J~)!K~L~M~N~O~O~P~Q~R~3~h~C>S~X,K,))e,T~U~V~W~* = * e X~Y~Z~`~ {.{5'k~+{@{n,#{P=T!#'N=4>I)L)e,K)))${}'g,%{~)&{l$m$y$A%.=g@9$*$4&*{", -" D'/)[)y')'B')'={={B'/)-{t's~#~#~;{>{`!,{'{r~r~`!s~a!Q)#~t'){^)L>!{~{w;/={{&~G=A*4)v*x*Z)5)j!]{j!^{x~/'-~/{({>~_{:{<{I'^>^;J-_;I-];M%=&B%t#~$j#0&;&[{,-*=4;X,p!M,q'S;i,!~K,R;W;A>}{N--=L*M*|{%*,-%*%*%=R=,-Q;P*5=#*b&T%~-8&C&j$z$~-U%j$5=7&|%~-J*w&R=&=-=p!1{P~u!2{]~3{4{5{z!6{K~K~7{x!J~l)&!S;0'8{o!%*k~}'j'))K)K)^~9{0{` = e a{b{c{d{e{f'I)f{[{9-g{h{i{j~g{@,h{j{a'K)7'T'k{9'm=*=a*N=w%C$^*6$R$U#t%*$4&Y)", -" l{<)u'()m{m{B'={^)n{x'()-{[)s~#~s~q~;{o{,{p{q{r{q{;{s~Q)<)u'*'v,M>D'x;s{t{@-#-#-Z*u{X*{{j!)>v{^{v{w{x{y{y~z{A{C~B{C{I'Y>W>J-^;J-];^%p%B%+%R#',y>8&,-o!O=4;T;I)}'K,2>e,H~M,T;f,$,X;D{a*c*a-c;,-,-,-Q;%*d*J*R=$=C&g&2%5* *0&)-J*1%w&J*T%%%J*;&1%)-8&&*Q;M*a*Q-3;K,i';!E!b{E{:~F{G{H{I{;!p)J{-!K{{~%!h,d;L{L)J)W;${z)x)M{N{O{b+e P{Q{R{S{g'w)T{@,U{o!V{W{~)X{M)Y{I)a,Z{W,`{v!Q, ]f,4!.]R-w%m$m$m$y$.=T$k=2&v{", -" ;'y'A'u'B'+]@]@]#]={B'()[)#~#~s~s~s~q~q{p{$]$]$]r{q{;{t'.~)'^):,o>4-f=%]y-A,U>U>B-{{{=&])>]{)>w~*]=]-];]z{>],];]']i!Z)Y>^;J-H-7-G-O%.%@%b#',)])-O*N=S~N=$,~~H~W;A>!]8;7;9;~]6>X;m=-={]M*a*M*K*Q;,-P*%*P*$=E&,&$=%=5=e;k$|%%=%=u%~-d*,&$=,-$'R=%=&*%=6*M**=3;T;h'c''!]]^]6{/](]_]:]<]b'[]}]S,5'|]f,*=O=1]*,g,g)2]k~^!3]4]5]6]7]8]9]0]a]b]c]%{n,W{g{d]e]])~)K)2{x)f]5'g]h]W,A)))W;[{7!y%)&y$8!8!8!i]j]K;k]", -" -'[)y'B','={@]l]v'={B'm]()S)#~d!s~s~p~n]q{$]o]p]$]p{q]r{r{;{A'*'''C'D;f=w;V)/={{Y*{{:=:=:={={=w~r]s]-]t]u]>]>]-]w{Z)U*U*W>^;v]M%M%=&K.S@u#m$0&d;R-~)Q=O*5~U;p!T;f,A>A>9;4;4;*,0;m=E~m=*=w]a*M*c*%*J*%*c;)-8*M=Q;b*0*0*,-b&$%)->&#*Q;$*g-$*6*c;c;x]6*w>d*9;3'R,y]/!z]A]B]C]^]D]:~G{r)E]/!F]i'S,h,O*h,3;0*d*Y{i,L)f,k{S,3{G]r*H]k{I]j'~)J]K]#'L]N=])e]g,p!q'6'd'i)6'6'M]+)2]))k~4>P'1%c&N]l$O]P]i]Q]R] =;~", -" S]-{m]B'={={v'l]T]l]={m]y'[)()U]d!#~q~r~q{$]p]V]W]W]p]q{X]Y]p{){:,_,w,*>e!f=_=(=E=:=)>)>)>:=)>Z]`] ^k].^t]+^@^#^)>V*Y>Z)G'$&_;M%B%n+N$t#R%#$w%9&%*P=~)R-R-g,X;h,*,*,h,4!m=h,0;m=m=3;G~0;Q--=$^-=-=-=O=N*Q;c*N*M*M*8,a;a*M*$=#*J*J*$*0*a*0*,-9--=a*M*c*7*%^1{U,8{i)3{@)2{&^*^=^-^:]r);^^!V'8{>^~~%*>&$*=,&*g&,-f,,^7'X;'^c')^!^~^{^3~M,k~%{j']^^^Q-M,k~j'7'6!w)/^i'K)(^_^c,2':^7!#'t&R&U%x@0#j*i]<^[^}^t]", -" [)()B'={w'l]|^l]T]l]x'1^2^U][)S)s~3^p~q~q{p]4^W]W]p]5^p]Y]Y]p{<)v,w,6^x;2)h=(=7^.-:=F=:=:=8^Z]9^ ^0^-]a^b^c^v-^={=I'i!Y>$&4,G-{;d^P$g@D+l#w%#'%=c*~)*=b]d*9-a*e^m=*=N=N=F~Q-Q-0;h,h,X;X;Q-z>m=0;2;>=D{a;R-a*a*a*a*L*Q-f^L*J*Q;0*c*0*N=3;m=m===N=*=5;R'O=-=2'g^U,h^g't!i^j^=^k^K~k^l^f'i)z)X,g,b-k# *4$5$X#|>g&h,L,6*c*W;2{m^L)L)n^N=0,w)7'j'p!}'))j{L)e,e,5'o^g'p^5'w)8]N=M)d*q^n,_*r^.=j*R]R]s^t^u^a^", -" v^()2^,'={@]l];'|^|^@]B'm]2^[)S)#~s~q~'{q{$]p]w^x^x^w^4^W]W]$]p{a!*'P>j>|,p-(=z=A=w={{^=&]c=y=<{ ^ ^k]y^w-S>B;3-Z]^{I'Z)$&J-!*=&N$s%z^#$8!1%u&;&P'N*L*R-A^C>)-d*a*-=O=Q=Q=L**=X;4;$,$,*,X;B^h,X;X;X;1;L*e)L*L*-=Q-3=0;!~W;Q=d**=>=-=*=~)S;1{1{~~M**,g)z)h,M*4;S;5'U'h^i)`{l)C^G{O~-!h^S;*=D^y>b&G&&+;+]@6+|=5+R#d;R=l$u&I)T'}'2'E^F^G^3{H^))))L)E^j'9,K)2'!)I^o^%!5'5'7'J^%{f-#'Y{t&_*k&K^Q]Q]L^M^N^{{", -" O^>{()x'={P^Q^R^@]={S^m]2^()[)S)S)@~s~'{$]p]x^T^U^U^T^x^x^V^5^p{a!*'M>s,W^X^y-h=Y^^=:=b=Z^`^y^y^ ^<{w=(=;>&> /0^*]^{./$&J-~*=&B%W%b#A%w%1%U%M)d;P'f-Y{j~+/>&9&,-M*-=L**=|'2;9;W;X,4;h,h,A>7;4;*,6>P-U;T;f,~]K,h'6'@/3;K,7;Q=N=X,i,M,@,J)%!%!9'8;m=O,#/9'K)9;K)9'Q,U'U'U'U'2{,!^!x)k{T;g-l$4#=#*+6+^&I@K@$/}>;-C@5*D#&$v&E~g,k~M,2'Q,f'n^))L)2'e,2'}'2>i,L)%/S,&/j'2]M,@,*/j~q^=/@&9!1&M^-/u^}^;/'*B*", -" -'@~m]x'={>/={l],/@]x'_)()U][)S)@~@~q~$]$]p]'/T^)/!/~/U^{/x^W]p{;{A'N>k>|,&;4-+-r>b=Z^y=]/A= ^y^9^c=c=x-@^^/t]`]D~5)$&4,2&P%r%+%3$~$y$U%U%t&n,7!7!n,$'7&>&;&{)J*N**=0;P-9;X,I)W;|]4;7;W;W;W;T;4>W;4'I^//W'c,k{^~(/3>_/_/K,*,W;g)!)L*}'S,#)7'2'!)1~h)i':/%!%!K@}/|/1/_>k$2#K.7+i#7$@$g&S-{~2/V,A)L)g)R,P-4;K{k~j'e,L)3/W,4/j~h{q^T{5/6/K^Q]n*7/j*8/`*9/c=0/", -" u'q~()()a/b/x'c/={x'2^U]U]S)#~S)@~q~'{$]d/'/x^U^e/f/g/h/U^{/V^p{`!i/j/W^&>p-k/.-s>w=]/y^l/y^y=s>c=Z]0^A{m/t]-~v{Z)n/];&&U&N$s%R#D+$$z%y>t&6&o/p/0&6&#'#'#'$=7!o!g{*,f,9;4>f,f,7;T;2>}'2>2'K){~U,u!c{q/d{i)H^0,r/#/U,_/s/}'H^R,L,c;W;S;t/H^#/)~d'f'b,V'T,u/O,8;v/6>W;*,==a*$^D{%*T%X#8$y#F*-+{+}>H%g%1/w/x/$$f,*,l$o+q+L.=*I$'=)+&$*,2]O,'^H^A>X;4!f,M,W;))3>L)2>g,y/V%)&x@#'^*Q#i]z/A/t^X)`*B/ ", -" C/q~S)2^D/2^U]2^2^U]@~'{'{'{X]'{'{q{X]X]E/'/U^F/G/H/G/I/h/J/K/,{`!L/j>%>y,q>.-w=y=A=M/M/y^s>c=E=w=M/N/O/P/ ^8^Y>{'];&&O%B%3$U#Q/1%d&g&S%R&t&C&d&_*y>y>v&#'**W{c*R/A>U;6>R/0;X;P-T;2>2'K) ]U,S/-!x!x!E]j^u!0'h^U'g-R=0&.*w%v$b#r+2+L.J$}>H$U/V/W/X/_>+%b&,^0'X,~-&$X#D#E+L.L.>+^@8&,^2]|]7;5;X;+{5!X;f,p!M,Y/])#'Z/M)J.i]`/9/B*X*@- (T*E=,; ", -" .(+('{p~S)S)@~@~n~p~@(E/E/X]X]X]E/d/'/#(U^!/$(H/G/%(&(*(=(F/K/-(`!;(|,>(,(4-w=(=B='(A=<{`]8^c=y=x-)(m/z{!(~(Z){'];=&B%{(w#9$@@d&V%V%#'t&c&_*U%w%l$1%U%v&$'%=o!L*X;h,g,L**=X;R'W;K)5'_/8{x)l)](e'q/^('!/(3{w!'^7'e,k~E^%/6~f,M)((I)e,P~_(I]I]:(*^E]h)'^<(O,U;n=$%&$j$W#e$r+,+L.=+{+F+[(L@}(|(1(`>H-7+N#d;2(w!&!N,*=T%3(j#b#3$&+&+:*n$2'0;|]9;~)U;+{h,Z/s/Z/n,Z/Y{r^4(L%5(j*6(4(7(r>b=b=A=A, ", -" 8(9(E/q{X]@(Y]E/X]'{'{d/U^#(T^#(#(#(U^)/)/H/I/0(%(&(a(b(c(F/{/d(`!>'e(f(X^B=]/0^x-0^y^y=c=`]<{P/g(h(i(C{w~Y>V>];=&N$<%P$S#A%c&C&#'v&v&y%C$m$z$w%z$c&y%;&C>,-d*-=0;|'R-Q-4;7;M,K)T'w)F]]~g';!j(k('!l(3{*!m(_/L)A)I)k~M,g,i{7&0;9,A)x) )x)d{;![~n(3{h~o(5;T;-=;& *)#o+5+;+=+{++&R.L@p(q(r(s(t>H-,+R#w%v/#/t(g'a'9'8;d;0&&$u#9$<#3$7%7+b*5>U;R-q^g,X;V{R-])7!6&Q]u(v(U%A%0#6/Q]w(G=y-M/F;o- ", -" x'4^)/x^E/p]4^T^#(x(x(y(z(I/A(I/G/G/H/A(0(0(%(B(c(%(e/C(t~e/d(t~D(E(s-r-;;F(q>a^G(H(`]9^x{I(J(K(/{C{j!Y>V>];K;N$D$<#R#m$U%>&7!>&u&6&d&U#R%7/m$L(y%u&d;J*Z/R-~)$^a*~)*,7;W;}'H^h^u!g'^!f'M(G{6{N(/~l)R{6'))I)p!m=^^M)O=L{I)}'A)h^O(P(^(O~5{Q(@)R(S;>=a;%=V=W#=#5+;+=+{+F+[(L@0)S(T(U(Z><;6+7#z$M=e,V(q/W(%)W'O,p!7&t&y$R#e$<#2+^%+%**L*M=W!f-Z/v&^^X(d&N]Y(U%Z(s#`(v*U> _._+_A=2-2) ", -" @_x(#_g/g/g/H/g/g/G/A(A(A(0(&($_%_c(&_*_T^C/C/=_ M>-_Y]''W^->;>r-u-2-u]a^ ^<{<{ ^m!J(;_;]>_7)Z)$&];P%@%t%R%k+',U%{)C>j~%=>&0&l#@@%+P]m$y$R&>&f-Z/c*L*m=V!~)m=*,p!}'#)h^u!,_o)'_H{)_!_<~~_D!T'6!{_~)W{W{d]]_M,2>}'K)K)3~<]2/^!^_k^/_<]=!T,P-b-y%j#N#*+4+3+s+Q.0%(___:_<_M'[_X>/&+$}_d*c,c'|_k^1_B]U'7'2'2_3_M)z%D+g@+$p%W><#**&*y>4_R&w%U%y%_*X(j*O)'*X*b=y=Y^h=f!h=0^*;5_ ", -" 6_q{&(c(7_8_c(8_9_0_a_b_%_c_g/d_@_ <)e_f_g_%>z;s-;>h_u]F(;] ^=].^i_j_C~k_w{j!5)$&J-[*@%u#l+4$c&S%[{j~j~C>M)y%4$l+%+#$y$o,t&t&d;c*['&{V!*,h,0;|'8;L)6'0,&!m)J~l_m_n_C^o_R~,^t!p_{)d;|'4>q_K,k~U;N=r_5'E^`{/^,!s_t_u_'!m(3/0*D&&$u#o+q+v_!+[;w_g%e%x_y_:'`><>];N$<#a&7'@)z_(]A_B_C_P~p^e,f,J*9&U%#$D_M$*${;`>|%b&j#m$)&)&w%A%6/E_F_ (G_Y=1&Z*z=k/.-x-F;H_ ", -" I_ i/J_:,W^x;y,c^K_z,2-L_t]l!z{M_M_m!k_^'D~]'$&];2&B%R#5$w%v&$'d;N_W{M)6&y%8!$$$$&$4$z%1%V%[{c*O*R-v/*,f,f,*,4;e,L)2]h^-!O_t_m_H{P_o_//6'Q_R_$^7;2>q'M,i~F!M)d]S_G~R/T_+)U_V_B_W_j^I^P=g&]$R#S@6+ %]%h*<;W$X_x/M'D,(>Y_I-=&.%~$C>Z_`_6{F{~!t_O_o^6!}'j'5!$'/*8!i]`$ :.%G-I-4$m$7/^*5/A%v(7/j*k&k*i*.:k&+:x=B=B=x-t-@: ", -" ;(#:$:%>&>c^@^%:L_I(&:*:=:-:;:>:=]8)8)D~W>=-&&B%9$5$0&;&e],-)-9&C&z$4$m$X##$C$j#A%d&6&d*g,h,g,$^s/f,f,f,4>I)K)c,x)x!,:!!':):)_!:l)7'L)2>K,2>I)5!o!{)@{U{~)@'~:2]J~b{/]{:t_W_&/l~t&#$<#S+2+e&/&^&H-[(t>(_(_`>t>]:7-M%N$O$l$Q=^:/:7{(:_:::<:_^8{j{}'p!Z/d&8!%+R%t% =C*:>W%&%#$D+C$[:0#6/6(@&%'Y=F_}:1&E=B=v-2)D; ", -" |:t~1:[,2:3:y,^/I(4:5:%:i_6:7:P//'8:Z>(>X>M%n+{@9$j#v%9&'-V!W!&*9:0:$$$$T&X#j#y$5$w%>&Q=*,f,f,a:4;f,+{b:*,T;p^0,c:B!d:~!z_e:7{A]f:g:K)I)q_f^h:#'h{U{N=])i:j:(^I]N(k:F{!!6{|~H^7*j#T$E+B%O%=&--H-K-<>t>l: ,(>^>H-O%.%t#~$v&{_I]S{m:n:o:M~p:q:r:g:k~J^7>7/#%k*n*s#l=s:C,t:S@7#g@R%R#@&R]t^G_{{r>x=r>w*z=3-p-B;u: ", -" *'>'v:w:$:x:y:+^I(I(A{;:z:A:'~B:J'C:M;I-D:+%3$T$W#2%R=-=*=$*y>m$u#<#b#D+l+4$2%U%V%J**=4;X,W;M,p!p!8;4;*,i~L)#/0'E]!!!!E:e:p)x!x!h'k~5!A^i{o!E~N=d;F:X{T;%!G:<] )H:!!:~z_c'R'T%l+W&.% %=&J$O;X>^>(>`>I:Z>^>X>7-P%n%g@U%N=j{I]J:]!K:L:M:[~N:(^O:P:Q:F:y$R:i*%+K^`$S:o%8)2+9$I#P$u#P$6(Q]t^G=(=k/A=x='(2)y,T: ", -" i>+~:,U:V:W:X:H_=:I(Y:Z:`:X:.^ <.W>]: %{@E+S@N#4$b&&*#*2%i$l+2#S@S@Y#X#l$y>5=7*N=+<7;K,e,%/T;+{f,4>W;W;j'c,&/b{t_{:!!:]@u>%<^>^;O;M%o%Y$I#&<4>*<(^=<-<;<><,<'<)*> ", -" /<(<_<_<:< /<N;H-:*7++%6+S@4#@$X#D#4#3#2#<#S@<#2#&$v%$=j~N*2;2>2'K)3j~Q=U;p!f,^^h{K,5'O,5W>M;(>:>(>M;^;--2&&&B%O$#$M)I)J]6<7<8<9<7<0_>H-=&L.K.7+E+)#3#3#3#!#&+E+o+&+<#R#&$.*T%R=M*W;H^T'z)o!X'&!L)Q=U%N#@%p%M%H-W>M;M;t<:>M;K-H-M%p%p%o%w#5$@{u.%e&B%*$E(x:MI-^&:*>+7+S+E+r+S+S+E+&+S+5+S+=#4#R<$%y>)-3=W;,^,^A)}'W;h,m=0;h,M,5'W,h'V'=!f'>!f'<~S<0'3<5~E~4;&{%=g,7!L{3~k{S,h'a,(~g'T<_~f'/(sH'(>:>`)^>^;--=&N$<%<%g@*/V.-x=B=$[&>{, ", -" <,''d(%[L<&[Y:*[=[-[;[m!a)9)D,Q<_><;h*/&L.,+7+S+E+7+6+4+q+S+*+=#)#3#D#B$2%b-L*m=0;3;*,X;V!|'-=|'m=h,>[e,_/V'%).{l)h)/!c'I]4',[7!N*N=%*N*N*q'5'e,%/3<0'.{=!'[6{_]7{p)h^h,$'W#3$U&G-^;Y>(>(>t7-M%{;N$D$z^d&U{)[Xm> ", -" [[+~}[){|[1[&[2[3[4[5[mY_<;h*/&K.6+5+5+r+S@*+o+5+7+r+=#2#N#W#4$'&#*6*Q;Q;Q;a*-=-=L*R-a*|'0;U;H~6'Q,t![]b'6[T;7;9;0;f^u&$'W{-=P=c*e,2>V!5'Q,5(>D~8)(>W>2&O%C*<%9$6$=/9[0[a[b[c[d[e[([f[g[ [h[Z]B=B=A=A=A=f=A=f=i[>;3-A=.-'*9^j[^;=&=&J-l/x=;;gN;<;h*e&,+C@r+&+o+r+o+=#)#)#)#2#K#N#v$|%'&b&,&8&J*%*['N*N=Q-O*a**=*,f,)~h~T'q[R-9&M*>&U%;&Q;6>,-T;f,E~,^^>(>:>D~^>J-2&P%p%*$7/R&T{v[w[x[c[c[y[z[A[B[C[D[U>f=E[A=A=A=A=A=f=>;k/F[G[B=A=H[>_l!8)B%-&V>0^f=q-X^I[o- ", -" o>J[d(K[i(;O;=&>+7+E+o+o+<#)#=#*+*+M#2#4#{$B$j$'&,&$=>&{)%=r!M*O*M*-=R-b**=U;W;i,5'H^w)Q,P~M]u!%!3;v&t&$'t&;&9&0;Y{m=N=p!>^^>H'H'./V>G-Q%Q%4&-&k&:[S[T[U[x[A[V[W[X[Y[H)Y[Z['(4-A=A=A=A=A=k/f=>;F[k/k/B=H;b=W*`]l!G_dW:`[ ", -" #: }.}|[+}@}#}=[`:$}+!m<%}C:X>^&*%6+t#S@{#)#)#d#2#3#3#3#3#4#|>5*V=E&E&#*n=)-9&)-0*Q-m=3=L*D{U;K)]^g^U,U'i)&!&}%)*}n)Z_%*v%w%U%u&{)P=Z/V!d*h,,^7'6'x) )<]*^=}x!%)6!Z/l$I#B%&&];V>Y>Z)Y>5,];~*P%N$-};}>},}'})}!}~}{}]}^}/}(}_}:}s>4-%]A=f=A=A=f=A=>;k/k/k/k/>;B=x=T><}`]G-4(p&k]t-*>[} ", -" h<}}.}|}1}2}3}4}l<5[5}__8:/>-- %.%w#&+<#2#u#R#4#N#N#N#N#i#]$ *$%'&T%>&)-)-R=~-g-M*Q-P-A>8;2>U!i)W'b,S_t!6}]~%)](x! )l)h,u&w%)&y%n,Q=W!2;O*g,i~U;R,i)%)_(7}8}E]/(^:Q=)&~$K.&&];4)5,5,4)!;j=!*P%N$s%.:9}0}a}b}c}d}e}f}g}h}i}j}k}B=3->;f=A=A=A=M/f=G[F[k/k/k/G[B=A={{)>Z]D-l}q&M/A;m} ", -" W^n}o}p}q}r}M[s}t}u}9)F,K-/&e&7+&+)#u#7#N#D#D#D#W#X#X#j#A$C&#*D&D&~-2=*=0;m=Q-Q-1;7;N,T'i'`{1~v}M]e{w}g']!q/x}O~d:m())J*y%U%y%v&*=P'$^O*N=0;:^R,U's!*}K:p<:]M:U'$^C&z^n%=&G-4,4)4)'*N&Q&]*2&P%6-y}z}A}B}A}C}^}f}e}d}X[e}D}c=4-F[k/f=A=A=M/f=G[k/k/k/k/k/G[B=A=E}w=z*z*x* (t-w,F} ", -" G}H}I}J}K}r}L}M}$}___'_>H-:**%S+<#3#4#D#D#W##$X#&$i$l$v%**%*b*$*$*Q=Q-B>M,g^s!U'6'S;g^2/D!N}c'c';!,!d{n)O}P}V_Q}R}S}0]N=U%#'S%@*R-b*o!*=O*N=J)V,0,8{h^T}m_t_U}e,j~g&&$P$p%2&C,C,C,O&Q&N&)*D-~;V}W}X}Y}Z}`}~@ |X[j+.|+|@|._k/G[k/F[f=A=f=f=k/F[k/k/k/B=k/k/3-k/c=f=y*Y*T>W*[,#| ", -" d($|%|&|3}*|`:u}:'`>W>=|p%.%S+=#3#W#X#X#X#X#o$4$u%C&R=2=V!h,A>i,3|Q}m_,|o^^!'|P'Z/O=)|{)W{g{9&g,4;V!3'k{c,5''^!|~|{|&/U;**5$W#u#<%P%!*C,]*B,q&A*x*]|{='*E_^|/|(|/|_|:|.|<|+|[|}|B-3-k/k/F[i[f=M/>;F[k/k/k/k/k/B=B=G[B=x=3-c=s>:=x-~{ ", -" K<||K}1|3}2|3|0)D,(>H-M%p%{@+$I#3#7/C$j#j#i$l$u%$'$*O=m=T;L)0,4|u!&/4|R[](g'c'e'j(E!N~A]J~=}o_G{5|+),!':t_u[6|7|I]3/p!U;h{#'P=^^9-T!f-*,p!K)7'1{%!W'](8|>!9'g,>&o,6$$$&+r%%&I=]*)*x*9|c=r>w=:=0|a|b|c|c|d|e|f|g|h|i|d|Y^3-k/k/F[i[k/f=>;F[k/B=k/k/B=j|B=G[k/B=h_h=Y^y-c^ ", -" k|l|m|1|3}n|o|B::>^>--O%e&+%P$2#U#D#m$5$z$1%C&>&%*L*4!5;R,/!p|^(Z'%)S/q|#;F[k/B=k/B=B=j|B=B=0^>;3-gD| ", -" *>E|F|2}#}G|L'/'^>I-^%{;N$@%P$u#N##$j#l$w%y>{)Y{d*L*3;,^l):('!M(n(z__~:(f'n(G{(](]'['[^_::::H|^_d:!!(:':I|J|K|p:t|q_5'S{T!Z/~:7;P'e]4!4;$^${2);>c^ ", -" R|S|T|U|L}o|K':>I-h*=&e&.%W%S@z^l+C$5$z$v%>&c;M*R-|'2>x)#~C=x=y=w=D=W)$1x|%1L|&1M|*1=1-1(=3-k/k/k/k/k/k/F[k/B=B=j|B=B=B=B=B=j|3-C;r-*>E(;1 ", -" >1,1'1)1P[l:^>J-^%V&B%+%D$S@D+X#y$1%w%y%#'o!N*P=7;!1~1(~o_(]{1(:t_]1K~M~M(6{^1!!H{~!H{t_o:B_]1H{/1(1K:/:_1:1<1[1M,}1H^|1])Z/L)a,K,N=U;H^U,Q,V'W'w!`'w!-!*}*!}]w)T!!&11)|O]s#21)*V*31Z^y=r>A-A-B-415161M|z|y|7181914-3-k/k/k/k/F[F[B=B=j|j|B=B=B=B=B='(E[s-y,W:%>>> ", -" 01a1b1c1B::>W>;-M%{;.%w#P$<#u##$5$v%v&$=9&J*9-d1e1o)E]j^f1)!:~~!t_t_A_L~H{E:!!K~~!~!(:A_::m_(:':g1h1i1j1;F=f!m1n1o1p1p1q1r1s10|'(B=k/B=k/k/B=k/B=j|B=B=B=B=B=B=B=B=p-H_W:m>A' ", -" t1&|r(@!8:^>^;u1U&r%B%S+I#b#R%U#m$1%;&)-,-R-~)}'v1t)j^j^w1;^K~A_(:m_t_^_!!K~^_!!!!(:m_(:F{:~~!x1y1;s>^=F=^=L1M1=1N1O1s181P1q1&~'(B=B=k/B=k/k/B=j|B=B=B=B=B=B=B='('(>(I[='R) ", -" |}Q1R1B:(>W>];G-=&N$7+3$t%b#U#U#l+l$$'Z/b]-=I)e13{B!>!m(S1:~!!::::o:o:::~!::d:!!K~!!::t_T1^]s)O_O_p)p:U1m:V1a[W1X1r:Y10,J]&/7',^R[j^I^t)Z1_/&2b)*2(;7-3&2&[*B%@%P$9$S#g$$$$$.=7>o!V!5!L)x)l)]!d{O}=2t_K~::B_o:o:o:t_o:::H{K~K~E:(:*^l(q:s)*^J|<~-2;2>2~1,2N:Y1&/'2)28]`{%!t!#J-^%M%B%@%{@+%*$s%H#g@U#c&y%A%C$9-w)v2I]F]V(Y'>|H{K~:~:~(:o:m_o:m_o:o:A_p|E2F2Y'V(/^_^0'p)h^j:]!&/3~W'-2_(G2_](:s_~!(:!!!!Q[V1H2I20#o*)=Q#6/Z[J2K28/L2M2#2p1%2N2O2P2h<%]B=4-B=j|B=B=B=B=B=B=4-|24-3-&>Y]F/Q2 ", -" R2S2y{5)^;];2&&&Q%K=<%s%P$P$P$M$b##%/*_*y$i{j'n^I]i^;2~1M:u__]:]6{H{m_t_m_::_:m_:~r);|O~E]c2T2_^b2B<;2U2>2~1V2g1y3O2,3'3~{3-j|B=B=B=B=B=B=4-B=4-3-w-N>)3,{O^ ", -" m!,~=~W>];G-^%&&B%n%w#s%9$9$!3t%H#D+z$v&U%[{~3{3*|Y'L]X1d2/3t|(3f{_3:3<3W2N:[3q:}3f1V2B!s|Q_M:|3U{(^r:L{!1I]13K|G2236{H{::~!~!(:`1~!l_334353=/6373839303a3Q|b3c3%2%2o-d3o-t{e3f3F;B=4-B=B=B=4-0^j|4-'(g3u'h3i3 ", -" 7:j37)V>];G-&&4&:*+%P$S#U#C$C$U#~$R%L(7!@{F!&{R/j{S{S}}]W2k3.3u_l3l_]1::H|m3A_n3o3y1p3/3q3Y2U2r3_[s3t3u3v3<1w3x3y3>>Q3R32-4-B=3-j|B=B=G;F(*;w,V^J_S3 ", -" L_i_=~G'4,G-2&&&P%P%O%p%{@I#l+4%j#z^R%c&P'T3R/J^@1U3B4P_/]H{H{!!H{k^u_C^,4'4d])4!4~4{4]4Q|61^4/4$2(4_4|:;>:4<4K_q>3-B=B=B=3-E;$:r~x^L> ", -" a^m!j!^;C,2&2&&&=&P%p%Q%[4%-S:z^@@g@f$A%9&@,~3/3i~6<}4V2|414G:24;x-!'I41[+~p]4^J4 ", -" K49^Y)C,L4%&M%&&P%Q%N$r& =;};}D$M$t%i]d&i{h{M4@{j4w[w[N4O414'<;2P4Q4R4S4I|I|0<64Y1}1~:94~3U2V1C3T4V2j4U4V1<1x;5>5x,k>M>''r~x^W]E/ ", -" ;[{=A*p&!*2&2&&&&&P%C**$s%s%s%P$H#9$S#,5'5h{f{U2)5!5r3~5{5]5,2,2z1u3x}^5/5V1-4(5_5{_:5<5C3a,f{R]+[[5%3a2}5|5#5{5><1525f~35}545C155R]@[@47(657585A27<-4j{v5w5x5y5z5T*A5B5R]w[C5A.6+6@6v,u'a!r{p]g/#6 ", -" $6]/y*{*Q&L&N&q&<=4&Q%4&[4%6k=s%&6s#k*%+*6/2e]M4((q3=6b2-6][-6<30[;6s5)<>6,6'6C1u4x3l26/)6X<)*!6~6u({6t5]6^6/6a5(6f~b4_6:6<6Z[[6}6|6u5s3162636Y=46A}5666!}768696B[06<3^[a6/}b6c6e}d6:|e6f6A|n1g6h6C=M3i6->j6k6&>i>B'@~3^3^$]5^Q)l6 ", -" 0|NA*I=B,n&]*q&]*o&N&n&o&4&Q%V}9!>}k+k*0#63^^f{m6n6a2J505o6r5p6q6r6B,<6x[)*s6t6M5/2{5S[o5u6:6x{*T*X)P6]*]*{*{*4&n&N&%&4&%--&dV6-/K5&7`(r'*7=78596+:8/X[8/-7d|J2;7>7d}D6I6,7'7)7!7~7K2I3F_u=b|(={7`[->]7^7|,[,/7(7s~p~_7{/r{:7 ", -" 4-X:V*q&T*~'Q&q&%&j=)*!*%&Q&B,%-<7%-G*m2i]Q#)=;*M5[7U2l4 4`h>p7q7%~='[)q~p]4^.( ", -" r7u]v*I=T*T*]*q&'*'*%&p&%&n&B,4&[4l}*{d<&6Q#56:[M5s7t7u7v7d[X1w7;6+[b4J5x7([y7J5B}}^@3z7c=57>2I=A7B7z/C7A[D7R5E7`8U[,846.8'8A6w**5k7c=:|)8k/!8-7C=9/~8E}9/A|X*{8n1,*M3]8h6^8/8P1O2r2(8h<,;(8u:z,|:22_8O6:8<8/,u's~'{Y]4^[8 ", -" g(e=W)I=X)X)'*A*y*x*W*Y*5,z*!;%&4&N&F<`(G_}8R:|8`},}18283848G177x[58+[68}678V5F_8898[=c=08y6E=*3a8F_b8c8X[d8z7e8f8u^g8v*85&5x*q&+|`*r>h8i80^C|j8k/_2k8h=h{{y*y*B*B*y*+_x*V*W*W*V*x*V*x*D-'*p&N&]*`(R])8.$v8K$B}W<48w8x8G696B[y8A/e}z84)96&3x=-/A8`/u*:}B8._F6)}'7B)C8D8c=E8/|(=F8G8A=g6N7Y^F=c3g5F(H80!2-u:I8'>m8J8D4K88(L8M8N8O8'3n8v^N86_P8Q8N6|,R8S8w'u'()#~T^r~S3 ", -" E;x:);T8G=Z*B-);X*W*W*V*X*{=I'{{{=V*v*'*!*'*]*85L%] 9-/M7L29|.9T7y=d|$1y^L7+9q>(=q8@9w-#9l8w-D=$9e!%9#9D|K8&9*9=9-9;9>9=9i5,9'9)9!9~9{9Q>()d!q~p]W]]9 ", -" >>n>F=(4`*);]=E=b=^=i={={{:=)>{=W*x*v*A*A*'*G=B*H=41}:v|^9/9e>(9*5_9:9g|<9z703X*J3h8M/[9X8 (y^=1}9:=|9B|19O79/O|Y*29A|F=@-&1:=A=N239g<4-'349p-D|0!m76_59D;697989,99909a9b9b9t8c909d9e9C/f9l]g9S),{T^()h9 ", -" i9y,/=Y5y*);A-w=+-A=z=(=w=:={{{=I'Cy*q&7(H=G=H=T*}:j9k9)8I3d}19l9W8m9(2 (d}413-i8d|x=y^J6n929z4&1o9}:8/p9e=q9=14-A-r9G;;;l{s9q-2-m8i6r-t9u9&;v9w9]7=_x9y9z9A9B9c9C9D9E9F9E9G9H9@]S)'{$]I9G/J9 ", -" E'y,B=g5E});@-c=x=4-2)2)u-2)B=]/Y^]/v=^=W*Y*'*41K9F>R9E;s{S9T9U9=;V9W9y;X9Y9h9{,Z9`9 0.009+0+0@0E9#0$0|^m]2^U]$]'/C(V^[)%0 ", -" T:H_H;e!A=x=e=.-w=(=M/x-x-z,s{p-2-'(A=(=C=r>D=L1B-Z*C-`*G=*~K991b|*1L|&0q1&0Y^j80='(_=*0o2F(e=s1n9P11-=0-0E'`[i5;0>0:4&;n>:4%0z;l5x9 0>(,0'0)0g>!0~0{0p>]0^0+6/0(0/7S8P^_0S)@~p~d/H/#_@]:0 ", -" <0D'D;7^e!!'-;p-w-2)3-k/x-w-2)q>2-F;B=A=f=g=_=_=,;A-]=@-g5L1B-[0}0|0N1M1<2Gl580Q2{,{090;5{,{0G9Y7},.600a0;'l]b0|^,/,/l]@]2^@~X]T^*(c0l{ ", -" M>[[d0w-->],&>[,m>l>3:n>K4z,w-p-e0F;f=7^_=t{f0h=f05-30D=A,F'g0^881h0[0=0Y5v=c3i0F(h=r849gL6'0%~p0q0r0s0k>t0^7%~L>u0~9|^,'@]l]l],'B'={={2^n~$]'/U^x^c!v0 ", -" ){D'&;->z;x;w0*>x0m>N>j/$:&>->n>->&;-;4-e=,;_=@9,;303030y0z0A0d3%2+2$>t2g5/=O2G;2)2)B069;>;>2259%0C0=_{0'0!0!9(0H9D0/7E0F0j/!9#0a0!,G0l]y'@]@]m]x'@]={x'U]s~s~p~X]H0h/s~;'I0 ", -" @6q,w,N>k>3:],1)D;q-z;<,O>O>i>[[y;y; 0->>>@97^J0K0@9t9,;30i0L0M0N0O0i6n8S9P0J4k6X^D;k6Q0[[C;[}c9G4q7~9G0R0m]={|^|^|^|^S8S0|^={={2^x'>/2^x'T0={U]()U]s~p~q~q{X]4^h/0(F4 ", -" -0U0*'q,z'|)C'k>V0%>->x;N6O>L>h>v9v9y;.(l5W0X0Y0X0Z0`0 a.a:4+a@aN840f3`5#a->$a%af(;>w0&a*aO>x,|^|^l]T0={m]2^>/@]c/m]=aS)S)2^1^m]U]x'-{[)U]s~p~p~q{q{$]-aT^E/s~B'V9 ", -" r,})A'u'})<))';'/,J>+6-'P>P>O>,0I_;aQ2p,k6l0Z0`9x9:8D9&ac9W7Q3@aR9#a>a,ad0'at890i>%>i>)ay'={x'y'x'U]!aS)@~~ap~'{X]'{p~@~@~p~q{'{$]E/E/E/p]'/~/I/I/x^;'{0 ", -" F}e_s~})/)<)()B'@]v')a!,r,J>f9C/,0Z9{a.6]aa9z9^aQ0/aw9]9$ao0-5(a-9i5_a:4:a&>s,T0T]l]r,x,J>m]U]()`!'{p~'{'{p~q{X]d/4^'/x(x(#(#(T^'/x^+(H/B(p]59a'02a]a]a2a@0]aG9^70a9ae9|aE9~0E9(0aa)a@]S^<)s~p~p~q~'{_7p]p]4^4^d/'/x(T^#(#()/!/$_q~L6F422 ", -" u9bas~#~()D/u'B'x'@]@]@]v'S8/7caM6D92a]ada2a2aeaC9fa0a(0cagahaca(0q0@]ia[)s~'{q{q{X]w^bax(T^'/x^)/!/e/)/$(H/d/Y7#6 ", -" l{)'o]#~2^y'B'x'={w'@]@])a|^S0ja0ae9G9F9E9ka(0>5la,/T0={iamap~naE/oap]X]$]p]x^#(+(e/h/paqa$(x^q~={{0h9 ", -" raT^E/@~@~#~U]m]x'={@]|^E0sataq7ua#0vaq7~9wa/7S8xaS)p~'{X]d/'/x(U^e/yayazaAaBaU^{9CaDa ", -" h9X]'{X]X]'{'{p~S)U]1^xaE0$0r0~9u0E0R0R0@]EaX]E/p]4^#(e/g/G/G/e/h/FaGa#aHa ", -" IaS8+]h/U^'/'/p]E/X]'{S)2^U]S)S)#~p~$]p]4^U^ya%($_s~v''{Ja ", -" Ka$aB'y'G/La$(H/H/)/#()/!/!/g/I/0(B(@6+]Ma:7 "}; +/* XPM */ +static char * surfaceUtilities_xpm[] = { +"200 168 3666 2", +" c None", +". c #58789E", +"+ c #627D9E", +"@ c #456E9F", +"# c #4775AD", +"$ c #4D7099", +"% c #7089A8", +"& c #466FA2", +"* c #4E80BC", +"= c #4F81BD", +"- c #4979B2", +"; c #466891", +"> c #738DAB", +", c #436B9C", +"' c #4F81BE", +") c #456FA1", +"! c #4877AF", +"~ c #4C6E97", +"{ c #456EA0", +"] c #466991", +"^ c #6E88A7", +"/ c #4E7FBC", +"( c #4978B1", +"_ c #4F719A", +": c #647FA0", +"< c #4670A2", +"[ c #4A6C95", +"} c #446D9E", +"| c #486A93", +"1 c #6A84A4", +"2 c #436C9C", +"3 c #4D7FBC", +"4 c #4977B0", +"5 c #50729B", +"6 c #6882A3", +"7 c #4978B0", +"8 c #446C9D", +"9 c #6681A1", +"0 c #4D7FBB", +"a c #6E87A7", +"b c #476991", +"c c #416897", +"d c #4C7CB7", +"e c #4C7DB8", +"f c #4D7EB9", +"g c #4E7FBA", +"h c #4776AD", +"i c #4B6D95", +"j c #507198", +"k c #4E719C", +"l c #50749F", +"m c #50749E", +"n c #466C99", +"o c #4F82BF", +"p c #416A9D", +"q c #50739D", +"r c #4D6F98", +"s c #5082BF", +"t c #3D6596", +"u c #496C95", +"v c #6784A6", +"w c #607DA1", +"x c #53739A", +"y c #3A608E", +"z c #6380A3", +"A c #53749D", +"B c #4170A6", +"C c #56779F", +"D c #4774A9", +"E c #4B6E97", +"F c #4979B3", +"G c #4B7BB6", +"H c #55779E", +"I c #4672A8", +"J c #4E80BD", +"K c #446D9F", +"L c #627FA1", +"M c #52739C", +"N c #4A7AB4", +"O c #4F82BE", +"P c #3F689A", +"Q c #627FA2", +"R c #52739B", +"S c #4575AE", +"T c #5081BE", +"U c #4C6E96", +"V c #456994", +"W c #476B95", +"X c #56769D", +"Y c #59789D", +"Z c #486B94", +"` c #4F80BC", +" . c #4E7FBB", +".. c #436C9D", +"+. c #637FA1", +"@. c #3A618F", +"#. c #3E6696", +"$. c #3E6695", +"%. c #3E6595", +"&. c #3D6594", +"*. c #416795", +"=. c #426896", +"-. c #416794", +";. c #3D6290", +">. c #3F638F", +",. c #3D618D", +"'. c #527298", +"). c #54759D", +"!. c #4878B2", +"~. c #496B95", +"{. c #4E81BC", +"]. c #3B6290", +"^. c #3F6797", +"/. c #406898", +"(. c #406797", +"_. c #3B6291", +":. c #395E8D", +"<. c #436691", +"[. c #54749A", +"}. c #50719A", +"|. c #4675AE", +"1. c #4D7EBA", +"2. c #446FA2", +"3. c #4F76A5", +"4. c #4E76A5", +"5. c #4770A2", +"6. c #4D7FBA", +"7. c #4571A7", +"8. c #4C74A3", +"9. c #4870A1", +"0. c #4A79B1", +"a. c #466FA0", +"b. c #3C6494", +"c. c #456995", +"d. c #486A95", +"e. c #4A6B93", +"f. c #4B7BB5", +"g. c #416A9C", +"h. c #406897", +"i. c #3F6696", +"j. c #3E6391", +"k. c #456791", +"l. c #5C7A9F", +"m. c #55769E", +"n. c #4777B0", +"o. c #3A6191", +"p. c #436792", +"q. c #517199", +"r. c #4E7099", +"s. c #4676AF", +"t. c #4F81BC", +"u. c #3C6392", +"v. c #436894", +"w. c #476892", +"x. c #4F7199", +"y. c #4F80BD", +"z. c #6380A2", +"A. c #3F6695", +"B. c #3B608E", +"C. c #56759A", +"D. c #55769D", +"E. c #436B9D", +"F. c #3F6593", +"G. c #4A6B94", +"H. c #3E6797", +"I. c #3D618E", +"J. c #92908A", +"K. c #828079", +"L. c #817E77", +"M. c #7D7A72", +"N. c #7C7972", +"O. c #7C7971", +"P. c #7C7970", +"Q. c #7A7770", +"R. c #79766F", +"S. c #79766E", +"T. c #78746D", +"U. c #76746D", +"V. c #5D6A78", +"W. c #436D9F", +"X. c #4876AF", +"Y. c #466893", +"Z. c #3E6491", +"`. c #476B97", +" + c #406693", +".+ c #436996", +"++ c #3D6494", +"@+ c #3D6493", +"#+ c #3D628F", +"$+ c #A8A7A3", +"%+ c #8E8C85", +"&+ c #88857E", +"*+ c #89867E", +"=+ c #807D76", +"-+ c #817F77", +";+ c #838078", +">+ c #827F78", +",+ c #838079", +"'+ c #827F77", +")+ c #807E76", +"!+ c #7F7C75", +"~+ c #7F7D75", +"{+ c #7E7B74", +"]+ c #7D7B73", +"^+ c #636E79", +"/+ c #3C679B", +"(+ c #4A6C97", +"_+ c #56769C", +":+ c #4B6D97", +"<+ c #436692", +"[+ c #385F8E", +"}+ c #6682A4", +"|+ c #BFBEBA", +"1+ c #AEADA8", +"2+ c #83817A", +"3+ c #807D75", +"4+ c #848179", +"5+ c #86837B", +"6+ c #84817A", +"7+ c #85827B", +"8+ c #7E7C74", +"9+ c #7E7A72", +"0+ c #66707A", +"a+ c #3F6A9D", +"b+ c #4E80BB", +"c+ c #4A79B3", +"d+ c #436590", +"e+ c #5B799F", +"f+ c #527299", +"g+ c #3E618D", +"h+ c #3E6594", +"i+ c #3F6798", +"j+ c #A7A5A0", +"k+ c #8F8D86", +"l+ c #908D86", +"m+ c #817E76", +"n+ c #827F79", +"o+ c #88857D", +"p+ c #87847B", +"q+ c #85827A", +"r+ c #87847C", +"s+ c #7D7A73", +"t+ c #7C7871", +"u+ c #5F6B79", +"v+ c #426C9F", +"w+ c #4876AE", +"x+ c #57769B", +"y+ c #446691", +"z+ c #426692", +"A+ c #426795", +"B+ c #406899", +"C+ c #3C6290", +"D+ c #8D8B84", +"E+ c #87847D", +"F+ c #7B7871", +"G+ c #7A7870", +"H+ c #7A7670", +"I+ c #616C77", +"J+ c #3C679C", +"K+ c #496C96", +"L+ c #52739A", +"M+ c #3F6490", +"N+ c #3A6190", +"O+ c #9E9B97", +"P+ c #8A8680", +"Q+ c #837F77", +"R+ c #86847C", +"S+ c #86837C", +"T+ c #79776F", +"U+ c #78766E", +"V+ c #79756D", +"W+ c #616B75", +"X+ c #416C9E", +"Y+ c #4A79B2", +"Z+ c #42648F", +"`+ c #436591", +" @ c #3E6492", +".@ c #446792", +"+@ c #95938D", +"@@ c #8E8B85", +"#@ c #7F7C74", +"$@ c #7C7A72", +"%@ c #7D7972", +"&@ c #5F6C7A", +"*@ c #406B9E", +"=@ c #4778B2", +"-@ c #4777B1", +";@ c #4776B0", +">@ c #4877B0", +",@ c #4C7CB6", +"'@ c #4A6B95", +")@ c #3F6592", +"!@ c #496B94", +"~@ c #A09E9A", +"{@ c #84827B", +"]@ c #828078", +"^@ c #7E7B75", +"/@ c #817D77", +"(@ c #69737D", +"_@ c #3B669B", +":@ c #4B7CB7", +"<@ c #42648C", +"[@ c #56697E", +"}@ c #54677C", +"|@ c #53667C", +"1@ c #52657C", +"2@ c #53677C", +"3@ c #54677D", +"4@ c #55687E", +"5@ c #54687E", +"6@ c #5E7289", +"7@ c #617EA1", +"8@ c #466C9A", +"9@ c #4D7EB8", +"0@ c #3E6799", +"a@ c #4F729A", +"b@ c #4D709A", +"c@ c #4673A9", +"d@ c #4A6C96", +"e@ c #395F8D", +"f@ c #406593", +"g@ c #8B8882", +"h@ c #7E7B73", +"i@ c #838077", +"j@ c #848078", +"k@ c #67727D", +"l@ c #436DA0", +"m@ c #4A7BB6", +"n@ c #4E6887", +"o@ c #807B70", +"p@ c #7B786F", +"q@ c #79756C", +"r@ c #76726A", +"s@ c #757168", +"t@ c #736F67", +"u@ c #726E66", +"v@ c #726D66", +"w@ c #827E78", +"x@ c #96938D", +"y@ c #5E7999", +"z@ c #3F6595", +"A@ c #3D6595", +"B@ c #57779E", +"C@ c #84827A", +"D@ c #838179", +"E@ c #63707E", +"F@ c #3E699D", +"G@ c #4E6987", +"H@ c #817C73", +"I@ c #78756E", +"J@ c #77756D", +"K@ c #76736C", +"L@ c #74716B", +"M@ c #726F68", +"N@ c #52667E", +"O@ c #4C7EB9", +"P@ c #496B96", +"Q@ c #4F6F97", +"R@ c #3B618F", +"S@ c #89867F", +"T@ c #84807A", +"U@ c #837F78", +"V@ c #87857D", +"W@ c #7D7B72", +"X@ c #837F79", +"Y@ c #6A747F", +"Z@ c #3C689C", +"`@ c #4F6A89", +" # c #868178", +".# c #827F76", +"+# c #566A82", +"@# c #4C7DB9", +"## c #4A7AB3", +"$# c #3F638E", +"%# c #3C6493", +"&# c #55749B", +"*# c #93918D", +"=# c #8A877F", +"-# c #848079", +";# c #65717E", +"># c #436EA0", +",# c #516C8A", +"'# c #8E897F", +")# c #8B8880", +"!# c #8A887F", +"~# c #89867D", +"{# c #8B887F", +"]# c #5A6E86", +"^# c #436690", +"/# c #3E6593", +"(# c #3F6898", +"_# c #3C608D", +":# c #8A8981", +"<# c #8A8780", +"[# c #89857D", +"}# c #697582", +"|# c #8D887E", +"1# c #8B8780", +"2# c #8C8981", +"3# c #8D8A82", +"4# c #8E8B83", +"5# c #908C84", +"6# c #8F8B83", +"7# c #8D8B83", +"8# c #5C7188", +"9# c #4C6C95", +"0# c #908E88", +"a# c #8C8980", +"b# c #8B8881", +"c# c #86827C", +"d# c #8C8881", +"e# c #8A867E", +"f# c #6D7882", +"g# c #396397", +"h# c #516B89", +"i# c #918E85", +"j# c #949189", +"k# c #98948C", +"l# c #939189", +"m# c #5D7289", +"n# c #426C9E", +"o# c #456793", +"p# c #39608E", +"q# c #3E6493", +"r# c #507097", +"s# c #8B8983", +"t# c #86847D", +"u# c #8C8982", +"v# c #8A8880", +"w# c #86837D", +"x# c #88857C", +"y# c #88847D", +"z# c #667483", +"A# c #566C85", +"B# c #8C887E", +"C# c #85837B", +"D# c #908D85", +"E# c #53729A", +"F# c #3F6796", +"G# c #537299", +"H# c #898781", +"I# c #898780", +"J# c #8A867F", +"K# c #8D8982", +"L# c #7E7F7D", +"M# c #8B877F", +"N# c #8F8C84", +"O# c #4D6D96", +"P# c #3B5F8A", +"Q# c #8E8C87", +"R# c #8D8A83", +"S# c #8C8983", +"T# c #8E8B82", +"U# c #8E8B84", +"V# c #908C85", +"W# c #918E86", +"X# c #928F87", +"Y# c #8C8A82", +"Z# c #88847E", +"`# c #40638F", +" $ c #375B88", +".$ c #94928D", +"+$ c #87857E", +"@$ c #928E86", +"#$ c #918E87", +"$$ c #8F8C85", +"%$ c #908D84", +"&$ c #939088", +"*$ c #85837D", +"=$ c #6F7881", +"-$ c #707880", +";$ c #727A80", +">$ c #737B82", +",$ c #737A81", +"'$ c #516987", +")$ c #3C6495", +"!$ c #3F6491", +"~$ c #8B8982", +"{$ c #918D85", +"]$ c #938F87", +"^$ c #4A6686", +"/$ c #3F689B", +"($ c #4976AB", +"_$ c #4875AA", +":$ c #4876AC", +"<$ c #4979B1", +"[$ c #4C78AC", +"}$ c #4F7AAF", +"|$ c #4E7AAE", +"1$ c #476B96", +"2$ c #5A799E", +"3$ c #87847E", +"4$ c #95928A", +"5$ c #96938B", +"6$ c #94928A", +"7$ c #8D8981", +"8$ c #8B8980", +"9$ c #8A8781", +"0$ c #87837C", +"a$ c #8C877F", +"b$ c #4A688A", +"c$ c #416999", +"d$ c #446893", +"e$ c #8B8981", +"f$ c #8C8882", +"g$ c #8E8A84", +"h$ c #8E8C84", +"i$ c #959189", +"j$ c #96928A", +"k$ c #96938A", +"l$ c #97948C", +"m$ c #939089", +"n$ c #928E87", +"o$ c #938F88", +"p$ c #8C8880", +"q$ c #84847F", +"r$ c #4C688A", +"s$ c #4776AF", +"t$ c #677380", +"u$ c #4A6C94", +"v$ c #928F86", +"w$ c #969289", +"x$ c #969389", +"y$ c #95928B", +"z$ c #98958D", +"A$ c #99958D", +"B$ c #949088", +"C$ c #928F88", +"D$ c #86847E", +"E$ c #496789", +"F$ c #416898", +"G$ c #656E76", +"H$ c #77746D", +"I$ c #77756E", +"J$ c #7D7A74", +"K$ c #96948E", +"L$ c #375C89", +"M$ c #888680", +"N$ c #82807A", +"O$ c #88867F", +"P$ c #88857F", +"Q$ c #949089", +"R$ c #95918A", +"S$ c #98958B", +"T$ c #8A8881", +"U$ c #4A6789", +"V$ c #5F6C78", +"W$ c #75736C", +"X$ c #727069", +"Y$ c #83807B", +"Z$ c #385D8B", +"`$ c #8A8882", +" % c #807E77", +".% c #83807A", +"+% c #84817B", +"@% c #85827C", +"#% c #908D87", +"$% c #97948B", +"%% c #98958C", +"&% c #8F8D85", +"*% c #817F78", +"=% c #797976", +"-% c #486587", +";% c #4776AE", +">% c #416899", +",% c #636E78", +"'% c #7C786E", +")% c #79756F", +"!% c #75726B", +"~% c #737069", +"{% c #6F6D66", +"]% c #7E7C75", +"^% c #7C7A74", +"/% c #486D9A", +"(% c #416B9E", +"_% c #365B87", +":% c #8D8C87", +"<% c #84827C", +"[% c #84837C", +"}% c #918F87", +"|% c #97938B", +"1% c #99968E", +"2% c #9A968E", +"3% c #99978E", +"4% c #97958D", +"5% c #89877F", +"6% c #807D74", +"7% c #7F7D76", +"8% c #476588", +"9% c #666F79", +"0% c #75726C", +"a% c #716E67", +"b% c #6E6C65", +"c% c #6D6B64", +"d% c #6D6B65", +"e% c #6B6963", +"f% c #6C6A63", +"g% c #706E68", +"h% c #72706A", +"i% c #78776E", +"j% c #8F8E89", +"k% c #4775AB", +"l% c #4876AD", +"m% c #365A85", +"n% c #83817B", +"o% c #807E79", +"p% c #807E78", +"q% c #7F7E78", +"r% c #817E79", +"s% c #87857F", +"t% c #898680", +"u% c #99968D", +"v% c #9B978F", +"w% c #9A978F", +"x% c #9C988F", +"y% c #9B9890", +"z% c #95938B", +"A% c #94918A", +"B% c #817F79", +"C% c #4A6689", +"D% c #626F7C", +"E% c #74726B", +"F% c #74716A", +"G% c #73716A", +"H% c #76736D", +"I% c #77746C", +"J% c #9C9B95", +"K% c #355984", +"L% c #888682", +"M% c #7D7B75", +"N% c #82807B", +"O% c #7F7D77", +"P% c #7E7C77", +"Q% c #7F7D78", +"R% c #8C8A83", +"S% c #9F9B93", +"T% c #9E9B92", +"U% c #9C9991", +"V% c #9F9C93", +"W% c #85837C", +"X% c #7F7F7B", +"Y% c #49678A", +"Z% c #5082BE", +"`% c #6A747D", +" & c #858177", +".& c #7F7B74", +"+& c #7B7971", +"@& c #8C8984", +"#& c #4C6C94", +"$& c #777571", +"%& c #7C7A76", +"&& c #7D7B76", +"*& c #807F79", +"=& c #7E7C76", +"-& c #817F7A", +";& c #A19E95", +">& c #A29F96", +",& c #9E9A91", +"'& c #9A978E", +")& c #99968F", +"!& c #98968E", +"~& c #828179", +"{& c #48668A", +"]& c #68737E", +"^& c #7D7B74", +"/& c #7F7C76", +"(& c #8F8C83", +"_& c #939087", +":& c #8E8A82", +"<& c #999791", +"[& c #506F95", +"}& c #4572A6", +"|& c #355884", +"1& c #83817E", +"2& c #7C7A75", +"3& c #7D7A75", +"4& c #7D7B77", +"5& c #7E7B77", +"6& c #9F9C94", +"7& c #A29E96", +"8& c #A39F96", +"9& c #A3A097", +"0& c #9D9991", +"a& c #9B9990", +"b& c #9C9990", +"c& c #97948D", +"d& c #96938C", +"e& c #817E78", +"f& c #807F7A", +"g& c #9B988F", +"h& c #9A958D", +"i& c #98948B", +"j& c #8E8A83", +"k& c #8E8C86", +"l& c #466E9F", +"m& c #45668E", +"n& c #7D7C77", +"o& c #7C7B76", +"p& c #7B7A76", +"q& c #7B7A77", +"r& c #84817C", +"s& c #95918B", +"t& c #9D9A92", +"u& c #A09C94", +"v& c #9E9B93", +"w& c #9C9890", +"x& c #858279", +"y& c #7D7D79", +"z& c #486588", +"A& c #6B747E", +"B& c #858078", +"C& c #9D9A91", +"D& c #9F9B92", +"E& c #9D9990", +"F& c #9B988E", +"G& c #939188", +"H& c #476D9B", +"I& c #4A78B1", +"J& c #4E6D93", +"K& c #91918D", +"L& c #7C7C78", +"M& c #7D7A76", +"N& c #7C7B77", +"O& c #7B7B77", +"P& c #7B7977", +"Q& c #7D7B78", +"R& c #9B9891", +"S& c #99978F", +"T& c #908E86", +"U& c #807D78", +"V& c #817D78", +"W& c #88867E", +"X& c #8D8A81", +"Y& c #476589", +"Z& c #66717E", +"`& c #838178", +" * c #949188", +".* c #9A968D", +"+* c #9D9A90", +"@* c #9E9C93", +"#* c #A09C93", +"$* c #A9A59B", +"%* c #A9A59C", +"&* c #A09D94", +"** c #A29E95", +"=* c #7A7871", +"-* c #918F8A", +";* c #979590", +">* c #4470A5", +",* c #807F7D", +"'* c #7A7976", +")* c #7A7975", +"!* c #7B7975", +"~* c #7B7A75", +"{* c #7B7976", +"]* c #7C7A77", +"^* c #93918A", +"/* c #9A9790", +"(* c #9B9790", +"_* c #98958E", +":* c #807D77", +"<* c #7F7E77", +"[* c #7E7B76", +"}* c #89857E", +"|* c #7E7E7A", +"1* c #4A678A", +"2* c #4878B1", +"3* c #63707C", +"4* c #868177", +"5* c #959289", +"6* c #A5A298", +"7* c #A8A59B", +"8* c #A6A399", +"9* c #A9A49B", +"0* c #ACA89E", +"a* c #AEAAA0", +"b* c #ABA89E", +"c* c #ABA79E", +"d* c #AAA69D", +"e* c #AAA59C", +"f* c #939187", +"g* c #87857C", +"h* c #7C7973", +"i* c #928F89", +"j* c #8D8B85", +"k* c #8F8D87", +"l* c #928F8A", +"m* c #8F8C87", +"n* c #8E8B86", +"o* c #8F8D88", +"p* c #94938D", +"q* c #5083C0", +"r* c #3C6393", +"s* c #858481", +"t* c #7A7876", +"u* c #787674", +"v* c #787774", +"w* c #777774", +"x* c #767572", +"y* c #777674", +"z* c #777673", +"A* c #797875", +"B* c #7A7977", +"C* c #827F7A", +"D* c #96948C", +"E* c #7F7E76", +"F* c #86837A", +"G* c #82817C", +"H* c #6C757E", +"I* c #858178", +"J* c #A7A39A", +"K* c #ABA79D", +"L* c #AFABA1", +"M* c #ADA99F", +"N* c #ACA89F", +"O* c #ADAAA0", +"P* c #A8A49A", +"Q* c #4A6B92", +"R* c #4772A7", +"S* c #4B7CB6", +"T* c #7D7C79", +"U* c #767471", +"V* c #757471", +"W* c #747370", +"X* c #747371", +"Y* c #757472", +"Z* c #757573", +"`* c #787775", +" = c #84827D", +".= c #918F88", +"+= c #72797F", +"@= c #86827A", +"#= c #99958C", +"$= c #A19D94", +"%= c #A5A198", +"&= c #ADAA9F", +"*= c #B1ADA3", +"== c #B2AEA3", +"-= c #B0ACA2", +";= c #B4AFA5", +">= c #B4B0A5", +",= c #86847B", +"'= c #7B7870", +")= c #908E89", +"!= c #486B96", +"~= c #426DA2", +"{= c #73726F", +"]= c #71716F", +"^= c #71706E", +"/= c #6E6E6D", +"(= c #6C6B6A", +"_= c #6C6C6B", +":= c #72716F", +"<= c #7B7B76", +"[= c #807E7A", +"}= c #87837A", +"|= c #86827B", +"1= c #98968C", +"2= c #AAA79D", +"3= c #B1ADA2", +"4= c #B2ADA4", +"5= c #A4A197", +"6= c #85817B", +"7= c #8C8B84", +"8= c #4A7BB5", +"9= c #4B709C", +"0= c #838281", +"a= c #716F6E", +"b= c #70706E", +"c= c #706F6D", +"d= c #6D6D6A", +"e= c #6D6D6B", +"f= c #696968", +"g= c #6A6969", +"h= c #6B6B6A", +"i= c #71706F", +"j= c #7C7975", +"k= c #85827D", +"l= c #888580", +"m= c #B3AFA5", +"n= c #A5A197", +"o= c #9D9B91", +"p= c #938F85", +"q= c #8D8B82", +"r= c #827D75", +"s= c #6D7D91", +"t= c #4976AC", +"u= c #848382", +"v= c #6F6E6D", +"w= c #6E6D6B", +"x= c #6C6C6A", +"y= c #6D6C6A", +"z= c #6B6A69", +"A= c #6A6968", +"B= c #696867", +"C= c #6D6C6B", +"D= c #6F6F6E", +"E= c #6F6F6D", +"F= c #727170", +"G= c #797876", +"H= c #7B7A78", +"I= c #7C7B78", +"J= c #7C7B75", +"K= c #81807A", +"L= c #948F88", +"M= c #A7A49A", +"N= c #AFABA2", +"O= c #AEAAA1", +"P= c #AFACA2", +"Q= c #AEABA1", +"R= c #A4A097", +"S= c #8A877E", +"T= c #788085", +"U= c #5F7389", +"V= c #9B978E", +"W= c #908C83", +"X= c #918D84", +"Y= c #85847F", +"Z= c #42648D", +"`= c #416EA4", +" - c #7F7F7D", +".- c #6E6D6C", +"+- c #6A6A68", +"@- c #727270", +"#- c #767674", +"$- c #7B7B78", +"%- c #7F7D79", +"&- c #86857F", +"*- c #85847D", +"=- c #7C7974", +"-- c #7B7973", +";- c #7C7A73", +">- c #9A978D", +",- c #A8A49B", +"'- c #B0ACA1", +")- c #A6A299", +"!- c #A19E94", +"~- c #A3A096", +"{- c #928D83", +"]- c #747D85", +"^- c #60768F", +"/- c #A09D93", +"(- c #A09B91", +"_- c #A6A093", +":- c #898C8B", +"<- c #657587", +"[- c #4E6E95", +"}- c #416FA7", +"|- c #426A99", +"1- c #818181", +"2- c #666564", +"3- c #686866", +"4- c #686867", +"5- c #6E6D6D", +"6- c #86837E", +"7- c #7B7873", +"8- c #817D76", +"9- c #AAA79E", +"0- c #ACA89D", +"a- c #ABA89D", +"b- c #A6A298", +"c- c #7A8085", +"d- c #426895", +"e- c #657A92", +"f- c #A6A39A", +"g- c #A7A399", +"h- c #A39F95", +"i- c #A39E94", +"j- c #A6A196", +"k- c #878E94", +"l- c #4E6A8B", +"m- c #416DA2", +"n- c #617FA2", +"o- c #7C7C7C", +"p- c #636362", +"q- c #626261", +"r- c #616160", +"s- c #5F5F5E", +"t- c #61605F", +"u- c #646362", +"v- c #636261", +"w- c #646463", +"x- c #676665", +"y- c #6A6A69", +"z- c #6D6C6C", +"A- c #706F6E", +"B- c #747372", +"C- c #777775", +"D- c #797774", +"E- c #7A7775", +"F- c #7A7974", +"G- c #7B7974", +"H- c #7A7872", +"I- c #797771", +"J- c #797772", +"K- c #787670", +"L- c #8E8C83", +"M- c #959188", +"N- c #B2AFA4", +"O- c #B8B3A9", +"P- c #B8B4A9", +"Q- c #B2AEA4", +"R- c #ADA9A0", +"S- c #AEA9A0", +"T- c #ACA79D", +"U- c #9E9990", +"V- c #7F8281", +"W- c #436793", +"X- c #5D738B", +"Y- c #A09C92", +"Z- c #9E9C95", +"`- c #959693", +" ; c #878D91", +".; c #677C94", +"+; c #416694", +"@; c #3B699F", +"#; c #5083BF", +"$; c #4977AE", +"%; c #787878", +"&; c #616161", +"*; c #626161", +"=; c #616261", +"-; c #636363", +";; c #666665", +">; c #686967", +",; c #6D6D6C", +"'; c #6F706E", +"); c #747472", +"!; c #797874", +"~; c #7A7875", +"{; c #7F7C77", +"]; c #7A7873", +"^; c #787671", +"/; c #77766F", +"(; c #77756F", +"_; c #797671", +":; c #797570", +"<; c #7A7771", +"[; c #7B7972", +"}; c #807C76", +"|; c #8F8B84", +"1; c #B3AFA4", +"2; c #B5B1A6", +"3; c #B6B2A7", +"4; c #B8B4AA", +"5; c #BEB9AE", +"6; c #BCB9AD", +"7; c #BBB7AC", +"8; c #BBB6AC", +"9; c #BAB6AB", +"0; c #B4B0A6", +"a; c #ACA99F", +"b; c #ABA69D", +"c; c #A9A69C", +"d; c #A4A198", +"e; c #9E9B91", +"f; c #8C887F", +"g; c #7C8080", +"h; c #43658E", +"i; c #4A7BB7", +"j; c #5D728A", +"k; c #928E83", +"l; c #959086", +"m; c #8A8B8B", +"n; c #76818A", +"o; c #617489", +"p; c #547090", +"q; c #4B719D", +"r; c #4371A8", +"s; c #4777B2", +"t; c #4574AC", +"u; c #4C6788", +"v; c #858683", +"w; c #6A6A6A", +"x; c #5B5B5B", +"y; c #5B5C5C", +"z; c #5D5D5D", +"A; c #5E5E5D", +"B; c #5A5A5A", +"C; c #605F5F", +"D; c #606060", +"E; c #626262", +"F; c #676766", +"G; c #686767", +"H; c #696868", +"I; c #72706F", +"J; c #767472", +"K; c #7E7D77", +"L; c #77746F", +"M; c #75736E", +"N; c #76746E", +"O; c #7B7872", +"P; c #A19C94", +"Q; c #AAA69C", +"R; c #BDB8AE", +"S; c #C3BEB3", +"T; c #BCB8AD", +"U; c #B9B5AB", +"V; c #BEBAAE", +"W; c #BDB9AE", +"X; c #B5B1A7", +"Y; c #A8A39A", +"Z; c #96948A", +"`; c #848583", +" > c #45658B", +".> c #496688", +"+> c #3A5F8B", +"@> c #4572A7", +"#> c #4674AD", +"$> c #747373", +"%> c #585858", +"&> c #595959", +"*> c #5C5C5C", +"=> c #626160", +"-> c #5E5E5E", +";> c #60605F", +">> c #666666", +",> c #656665", +"'> c #676666", +")> c #72716E", +"!> c #7C7976", +"~> c #797670", +"{> c #787571", +"]> c #777671", +"^> c #76746F", +"/> c #75726D", +"(> c #74726D", +"_> c #74726C", +":> c #73716C", +"<> c #75736D", +"[> c #78756F", +"}> c #797770", +"|> c #918D86", +"1> c #9F9C92", +"2> c #BFBBB0", +"3> c #C3BFB3", +"4> c #BBB7AD", +"5> c #B7B2A9", +"6> c #B7B3A8", +"7> c #9C9891", +"8> c #8E8E89", +"9> c #4F6C8D", +"0> c #4979B4", +"a> c #4B7DB8", +"b> c #4472A9", +"c> c #546C88", +"d> c #7E7D76", +"e> c #94938E", +"f> c #797879", +"g> c #585859", +"h> c #575757", +"i> c #535354", +"j> c #515152", +"k> c #525253", +"l> c #545555", +"m> c #555555", +"n> c #5F5F5F", +"o> c #5C5C5D", +"p> c #59595A", +"q> c #666664", +"r> c #6E6E6C", +"s> c #6F6E6C", +"t> c #73716B", +"u> c #716F6A", +"v> c #7E7A75", +"w> c #A4A298", +"x> c #A39E96", +"y> c #9E9A92", +"z> c #B2ADA3", +"A> c #B9B5AA", +"B> c #B5B0A6", +"C> c #A5A299", +"D> c #A29E94", +"E> c #9D9A94", +"F> c #4E81BD", +"G> c #3A669A", +"H> c #687889", +"I> c #989691", +"J> c #4D4D4F", +"K> c #525353", +"L> c #505152", +"M> c #4F4F50", +"N> c #505051", +"O> c #4F5051", +"P> c #4E4F50", +"Q> c #4C4D4E", +"R> c #595A5A", +"S> c #5B5A5A", +"T> c #747471", +"U> c #767573", +"V> c #787672", +"W> c #777570", +"X> c #787570", +"Y> c #767470", +"Z> c #72706B", +"`> c #706E69", +" , c #6F6D68", +"., c #85817A", +"+, c #908E85", +"@, c #ACA9A0", +"#, c #ACA99E", +"$, c #B9B4AA", +"%, c #B6B1A7", +"&, c #BAB7AB", +"*, c #B7B3A9", +"=, c #B1ACA2", +"-, c #A09E96", +";, c #58718E", +">, c #426690", +",, c #8D9295", +"', c #96928B", +"), c #989792", +"!, c #4C4C4E", +"~, c #4E4F4F", +"{, c #575758", +"], c #5D5C5C", +"^, c #5A5B5B", +"/, c #4B4B4D", +"(, c #494B4C", +"_, c #4A4B4C", +":, c #4C4C4D", +"<, c #545455", +"[, c #565656", +"}, c #555656", +"|, c #555556", +"1, c #737370", +"2, c #7D7A78", +"3, c #7B7A74", +"4, c #797773", +"5, c #777672", +"6, c #76756F", +"7, c #ADA99E", +"8, c #ADA89E", +"9, c #C2BDB3", +"0, c #D0CCC0", +"a, c #D7D3C7", +"b, c #D5D1C4", +"c, c #CBC6BB", +"d, c #C5C2B5", +"e, c #C2BEB3", +"f, c #BAB6AC", +"g, c #B2AEA5", +"h, c #B6B2A8", +"i, c #C2BDB2", +"j, c #697D94", +"k, c #4372AA", +"l, c #667D98", +"m, c #9C9D9A", +"n, c #A19E96", +"o, c #9A968F", +"p, c #5A5A5C", +"q, c #494A4B", +"r, c #4D4E4F", +"s, c #515252", +"t, c #5F605F", +"u, c #4D4E4E", +"v, c #4A4A4C", +"w, c #4D4D4E", +"x, c #4E4E50", +"y, c #5D5D5C", +"z, c #656563", +"A, c #70706F", +"B, c #7D7C78", +"C, c #7A7874", +"D, c #6E6C67", +"E, c #6D6B66", +"F, c #716F69", +"G, c #A4A096", +"H, c #A5A098", +"I, c #97938A", +"J, c #A29D95", +"K, c #BEBAAF", +"L, c #BEB9AF", +"M, c #BDB9AF", +"N, c #C4C0B4", +"O, c #CDC8BC", +"P, c #D5CFC3", +"Q, c #D0CBBF", +"R, c #CBC7BB", +"S, c #CCC7BC", +"T, c #D1CDC0", +"U, c #CEC9BD", +"V, c #CDC8BD", +"W, c #C8C4B9", +"X, c #BCB7AD", +"Y, c #B1ACA0", +"Z, c #657B94", +"`, c #4371A7", +" ' c #3F6BA3", +".' c #4E6F96", +"+' c #A6ACAE", +"@' c #C4C0B6", +"#' c #A09D95", +"$' c #A19D95", +"%' c #898782", +"&' c #575858", +"*' c #49494A", +"=' c #4E4E4F", +"-' c #515153", +";' c #4B4C4E", +">' c #494A4A", +",' c #48494B", +"'' c #484849", +")' c #48484A", +"!' c #656464", +"~' c #7C7C79", +"{' c #787772", +"]' c #74736E", +"^' c #716F6B", +"/' c #6F6D69", +"(' c #6B6965", +"_' c #6C6B66", +":' c #6C6A65", +"<' c #AAA79C", +"[' c #ACA79E", +"}' c #C0BCB1", +"|' c #B0ADA3", +"1' c #BAB6AA", +"2' c #C1BDB2", +"3' c #C5C0B5", +"4' c #C6C2B6", +"5' c #C6C2B7", +"6' c #C7C3B8", +"7' c #C5C1B6", +"8' c #C7C3B6", +"9' c #CCC8BC", +"0' c #D6D1C5", +"a' c #DDD8CB", +"b' c #E1DBCE", +"c' c #E3DED1", +"d' c #EAE4D6", +"e' c #E6E1D3", +"f' c #EDE8DA", +"g' c #E2DDD0", +"h' c #C8C4B8", +"i' c #D1CDC1", +"j' c #C0BCB2", +"k' c #738396", +"l' c #436FA5", +"m' c #4877AE", +"n' c #436791", +"o' c #8A98A5", +"p' c #CCC6BA", +"q' c #BFBAB0", +"r' c #91908B", +"s' c #747576", +"t' c #444546", +"u' c #474749", +"v' c #4A4B4D", +"w' c #494A4C", +"x' c #47484B", +"y' c #464749", +"z' c #48494A", +"A' c #474849", +"B' c #47484A", +"C' c #4F5050", +"D' c #565657", +"E' c #656565", +"F' c #737372", +"G' c #767570", +"H' c #74726E", +"I' c #74736F", +"J' c #6D6B67", +"K' c #6B6A65", +"L' c #686762", +"M' c #6A6863", +"N' c #6B6964", +"O' c #7E7D75", +"P' c #A7A39B", +"Q' c #B8B3A8", +"R' c #BAB5AB", +"S' c #B6B3A7", +"T' c #CAC6BA", +"U' c #D1CCC0", +"V' c #D2CDC1", +"W' c #D4CFC3", +"X' c #E3DDD0", +"Y' c #E8E3D6", +"Z' c #ECE6D8", +"`' c #EDE7D9", +" ) c #F3EEE0", +".) c #F7F1E2", +"+) c #F2EDDF", +"@) c #EAE4D7", +"#) c #C9C4B9", +"$) c #DAD6C9", +"%) c #E0DBCE", +"&) c #B6B4AC", +"*) c #537091", +"=) c #4879B4", +"-) c #406DA4", +";) c #59789C", +">) c #8391A0", +",) c #BEBCB4", +"') c #C8C2B6", +")) c #C1BDB3", +"!) c #C1BEB3", +"~) c #B1ADA4", +"{) c #A39F97", +"]) c #A4A199", +"^) c #49494B", +"/) c #464648", +"() c #454648", +"_) c #464649", +":) c #484A4C", +"<) c #454547", +"[) c #444547", +"}) c #454647", +"|) c #4A4A4B", +"1) c #5C5D5C", +"2) c #656564", +"3) c #737371", +"4) c #787773", +"5) c #75736F", +"6) c #75746E", +"7) c #74726F", +"8) c #72706C", +"9) c #6A6864", +"0) c #696762", +"a) c #666460", +"b) c #686662", +"c) c #B5B2A7", +"d) c #BEBBAF", +"e) c #AEABA0", +"f) c #99978D", +"g) c #C9C5B9", +"h) c #D6D1C4", +"i) c #D3CEC2", +"j) c #DAD4C7", +"k) c #DCD8CA", +"l) c #E1DCCF", +"m) c #E5DFD2", +"n) c #E8E2D5", +"o) c #EBE6D8", +"p) c #F1ECDE", +"q) c #F9F3E4", +"r) c #FEFCED", +"s) c #FCF7E9", +"t) c #EFEADD", +"u) c #E9E4D5", +"v) c #EAE6D8", +"w) c #CDC9BE", +"x) c #D7D2C6", +"y) c #E1DDCF", +"z) c #C6C1B6", +"A) c #C3BEB4", +"B) c #AEACA6", +"C) c #4D6B90", +"D) c #4573AB", +"E) c #366093", +"F) c #5B7596", +"G) c #91979B", +"H) c #B2B0AA", +"I) c #BEBAB0", +"J) c #C1BCB2", +"K) c #C3BFB4", +"L) c #C4C0B5", +"M) c #A29F97", +"N) c #85847E", +"O) c #83817D", +"P) c #555657", +"Q) c #434445", +"R) c #4B4B4C", +"S) c #434447", +"T) c #434545", +"U) c #484949", +"V) c #6C6B6B", +"W) c #767574", +"X) c #7C7B79", +"Y) c #787673", +"Z) c #757470", +"`) c #73726D", +" ! c #696863", +".! c #656460", +"+! c #64625E", +"@! c #676661", +"#! c #827E79", +"$! c #C1BDB1", +"%! c #CDC9BD", +"&! c #D8D3C6", +"*! c #E6E1D4", +"=! c #E7E2D4", +"-! c #E8E3D5", +";! c #E9E4D6", +">! c #EAE5D7", +",! c #F0EBDD", +"'! c #F9F3E5", +")! c #FDF9EB", +"!! c #FFFFF2", +"~! c #FFFFF3", +"{! c #EAE4D8", +"]! c #E7E2D5", +"^! c #E5E0D3", +"/! c #D9D4C7", +"(! c #DDD7CB", +"_! c #CCC7BD", +":! c #547091", +"~ c #605F5C", +",~ c #605E5B", +"'~ c #696763", +")~ c #BDBAAF", +"!~ c #C0BBB0", +"~~ c #BFBAAF", +"{~ c #C5C1B5", +"]~ c #DFDACD", +"^~ c #DCD7CA", +"/~ c #E6E0D3", +"(~ c #EFE9DC", +"_~ c #FBF6E7", +":~ c #FFFEF0", +"<~ c #FAF5E7", +"[~ c #F8F4E6", +"}~ c #FEFBEC", +"|~ c #F1EBDD", +"1~ c #D8D4C7", +"2~ c #EEE8DB", +"3~ c #CBC7BC", +"4~ c #A4A19A", +"5~ c #B2AFA5", +"6~ c #C0BBB1", +"7~ c #C5C0B4", +"8~ c #6E839A", +"9~ c #3D69A0", +"0~ c #416CA1", +"a~ c #345D90", +"b~ c #446891", +"c~ c #6E86A2", +"d~ c #9EA9B5", +"e~ c #C7C7C0", +"f~ c #CAC7BE", +"g~ c #C5C2B8", +"h~ c #C4BFB4", +"i~ c #B7B3AA", +"j~ c #A6A29A", +"k~ c #BFBBB1", +"l~ c #B6B3A9", +"m~ c #424445", +"n~ c #424446", +"o~ c #434244", +"p~ c #414245", +"q~ c #414244", +"r~ c #414243", +"s~ c #424345", +"t~ c #434344", +"u~ c #797877", +"v~ c #73716F", +"w~ c #72716D", +"x~ c #71706C", +"y~ c #676662", +"z~ c #666561", +"A~ c #63615E", +"B~ c #5D5D59", +"C~ c #61605D", +"D~ c #73716D", +"E~ c #B3B0A6", +"F~ c #B1ACA3", +"G~ c #B5B1A8", +"H~ c #C1BCB1", +"I~ c #DDD7CA", +"J~ c #F5EFE1", +"K~ c #FFFFF1", +"L~ c #FEFFF1", +"M~ c #FEFDF0", +"N~ c #F4EEE0", +"O~ c #F6F1E3", +"P~ c #D1CCC1", +"Q~ c #D8D3C7", +"R~ c #DEDACD", +"S~ c #ACA8A0", +"T~ c #C0BDB3", +"U~ c #CAC3B7", +"V~ c #7D8FA2", +"W~ c #4471A5", +"X~ c #738AA5", +"Y~ c #E7E5D9", +"Z~ c #FFF6E5", +"`~ c #F3ECDC", +" { c #EEE8D9", +".{ c #E9E3D6", +"+{ c #B7B4AA", +"@{ c #A9A69E", +"#{ c #A7A59C", +"${ c #C2BEB2", +"%{ c #B0ADA4", +"&{ c #ABA89F", +"*{ c #82817D", +"={ c #48494C", +"-{ c #454548", +";{ c #424244", +">{ c #414345", +",{ c #414143", +"'{ c #404144", +"){ c #474748", +"!{ c #5D5E5E", +"~{ c #676767", +"{{ c #737270", +"]{ c #72726E", +"^{ c #73716E", +"/{ c #676562", +"({ c #656360", +"_{ c #5F5D5B", +":{ c #63625F", +"<{ c #6C6B68", +"[{ c #A4A098", +"}{ c #B4B1A5", +"|{ c #ABA69C", +"1{ c #C7C3B7", +"2{ c #DED9CC", +"3{ c #E4DFD2", +"4{ c #F4EFE0", +"5{ c #FDF9EA", +"6{ c #FFFDEE", +"7{ c #FCF9EB", +"8{ c #D0CBC0", +"9{ c #99A7B4", +"0{ c #39659A", +"a{ c #8397AD", +"b{ c #F4EFE1", +"c{ c #E4DED2", +"d{ c #E9E4D7", +"e{ c #DDD8CC", +"f{ c #A7A49D", +"g{ c #AEABA2", +"h{ c #A7A49C", +"i{ c #A19E97", +"j{ c #C3BFB5", +"k{ c #CEC9BE", +"l{ c #737475", +"m{ c #47474A", +"n{ c #48484C", +"o{ c #404244", +"p{ c #404142", +"q{ c #404143", +"r{ c #404042", +"s{ c #636262", +"t{ c #6C6C6C", +"u{ c #777773", +"v{ c #72706D", +"w{ c #706F6B", +"x{ c #6D6C68", +"y{ c #696864", +"z{ c #646360", +"A{ c #5F5E5C", +"B{ c #656361", +"C{ c #6E6C69", +"D{ c #B0ADA2", +"E{ c #FEFDEE", +"F{ c #FEFEF0", +"G{ c #FFFBEC", +"H{ c #FFFEF1", +"I{ c #FAF6E8", +"J{ c #EEE8DC", +"K{ c #BEBBB0", +"L{ c #B9B5AC", +"M{ c #EEE7D9", +"N{ c #9DA6AE", +"O{ c #3A6598", +"P{ c #7C90A7", +"Q{ c #F0ECDF", +"R{ c #D9D5C9", +"S{ c #D5D1C6", +"T{ c #A5A29B", +"U{ c #B1AEA5", +"V{ c #9E9B94", +"W{ c #A5A199", +"X{ c #AEAAA2", +"Y{ c #A7A49B", +"Z{ c #E2DED0", +"`{ c #D5D1C5", +" ] c #C8C3B7", +".] c #A7A59B", +"+] c #48484B", +"@] c #494A4D", +"#] c #49494C", +"$] c #3F4042", +"%] c #686868", +"&] c #737170", +"*] c #716F6C", +"=] c #6C6A67", +"-] c #696865", +";] c #686764", +">] c #5E5D5B", +",] c #60605D", +"'] c #6F6D6A", +")] c #9D9A93", +"!] c #B8B4AB", +"~] c #B9B4A9", +"{] c #AEAA9F", +"]] c #F6EFE1", +"^] c #FDFAEB", +"/] c #FEFDEF", +"(] c #FFFCED", +"_] c #FFFCEE", +":] c #FEFCEE", +"<] c #EFEADC", +"[] c #E3DED0", +"}] c #E4DFD3", +"|] c #B9B6AB", +"1] c #BDB8AD", +"2] c #C9C5BA", +"3] c #D6D0C3", +"4] c #A9B1B8", +"5] c #476F9F", +"6] c #4A7AB5", +"7] c #778BA3", +"8] c #D8D3C8", +"9] c #C8C3B9", +"0] c #D5D0C5", +"a] c #C3C0B6", +"b] c #ABA79F", +"c] c #C2BEB5", +"d] c #AFABA3", +"e] c #A6A39B", +"f] c #CFCBBE", +"g] c #DCD6CA", +"h] c #F0EADD", +"i] c #8C8A84", +"j] c #87847F", +"k] c #6A6966", +"l] c #4A4B4E", +"m] c #46474A", +"n] c #414144", +"o] c #3F3F41", +"p] c #3E3F41", +"q] c #3F4143", +"r] c #6F6E6B", +"s] c #6C6A68", +"t] c #666563", +"u] c #656462", +"v] c #7A7972", +"w] c #AFACA1", +"x] c #A29F95", +"y] c #D0CCBF", +"z] c #F4EEDF", +"A] c #F6F0E2", +"B] c #EFEBDD", +"C] c #FEFBEB", +"D] c #FCF9EA", +"E] c #EEE9DB", +"F] c #D6D1C6", +"G] c #BEC2C4", +"H] c #667C96", +"I] c #D3CEC3", +"J] c #CFCBC0", +"K] c #B2AEA6", +"L] c #BEBBB1", +"M] c #DAD5C9", +"N] c #98958F", +"O] c #96948D", +"P] c #918D87", +"Q] c #8D8B86", +"R] c #8B8985", +"S] c #5A5A5B", +"T] c #4A4C4E", +"U] c #444548", +"V] c #3E4042", +"W] c #3E3E40", +"X] c #3F4043", +"Y] c #3F4041", +"Z] c #706F6C", +"`] c #6E6D6A", +" ^ c #6B6A67", +".^ c #676663", +"+^ c #5E5E5C", +"@^ c #5E5D5C", +"#^ c #686765", +"$^ c #B0ACA3", +"%^ c #B4B1A6", +"&^ c #ECE7D8", +"*^ c #FEF9EB", +"=^ c #F8F3E4", +"-^ c #FFFEEF", +";^ c #FDFBEC", +">^ c #CBC7BA", +",^ c #C7C2B7", +"'^ c #CECABE", +")^ c #BABBB9", +"!^ c #8190A0", +"~^ c #D6D2C6", +"{^ c #BFBBB2", +"]^ c #C8C5B9", +"^^ c #A5A29A", +"/^ c #E2DDD1", +"(^ c #D4D0C4", +"_^ c #E8E3D7", +":^ c #B6B2A9", +"<^ c #8B8883", +"[^ c #8B8A85", +"}^ c #888783", +"|^ c #4B4C4F", +"1^ c #45464A", +"2^ c #454649", +"3^ c #424245", +"4^ c #3D3E40", +"5^ c #3E3F40", +"6^ c #505151", +"7^ c #6B6A6A", +"8^ c #71706D", +"9^ c #6D6C69", +"0^ c #696866", +"a^ c #676664", +"b^ c #60605E", +"c^ c #5C5C5B", +"d^ c #85817C", +"e^ c #B1AEA3", +"f^ c #B6B3A8", +"g^ c #CCC7BB", +"h^ c #D5D0C4", +"i^ c #D8D4C8", +"j^ c #ECE6D9", +"k^ c #FFFDEF", +"l^ c #FCF8E9", +"m^ c #D2CCBE", +"n^ c #CFCABF", +"o^ c #E1DCD0", +"p^ c #C6C1B7", +"q^ c #ADAAA1", +"r^ c #97948E", +"s^ c #8C8A86", +"t^ c #8A8884", +"u^ c #868581", +"v^ c #767678", +"w^ c #3D3D40", +"x^ c #3C3D3F", +"y^ c #6B6A68", +"z^ c #8D8A84", +"A^ c #A8A49C", +"B^ c #B7B2A7", +"C^ c #F9F4E6", +"D^ c #A5A297", +"E^ c #C2BEB4", +"F^ c #BCB8AF", +"G^ c #D3CFC3", +"H^ c #CAC5BA", +"I^ c #D2CEC2", +"J^ c #B0ACA4", +"K^ c #8D8A85", +"L^ c #8B8A84", +"M^ c #858480", +"N^ c #7F7D7B", +"O^ c #6C6D6E", +"P^ c #4A4B4F", +"Q^ c #4A4A4E", +"R^ c #4A4A4D", +"S^ c #47494B", +"T^ c #3B3C3E", +"U^ c #3A3B3D", +"V^ c #3D3D3F", +"W^ c #545454", +"X^ c #616060", +"Y^ c #6B6B69", +"Z^ c #6E6E6B", +"`^ c #6B6B68", +" / c #5F5E5D", +"./ c #75746F", +"+/ c #A49F98", +"@/ c #B5B0A7", +"#/ c #CFCBBF", +"$/ c #76746C", +"%/ c #C4BFB5", +"&/ c #DBD6CA", +"*/ c #A09D96", +"=/ c #9F9C95", +"-/ c #878682", +";/ c #84827F", +">/ c #484A4D", +",/ c #4A4C4F", +"'/ c #3C3D40", +")/ c #393A3D", +"!/ c #38393C", +"~/ c #393A3B", +"{/ c #3C3C3E", +"]/ c #6C6B69", +"^/ c #5C5B5A", +"// c #DBD5C9", +"(/ c #D3CEC1", +"_/ c #C8C3B8", +":/ c #D0CBBE", +"( c #5B5B5A", +",( c #5F5E5E", +"'( c #686766", +")( c #595857", +"!( c #6D6D69", +"~( c #71716D", +"{( c #837F7A", +"]( c #E5E0D2", +"^( c #F2ECDE", +"/( c #E7E1D4", +"(( c #ADA9A1", +"_( c #EDE8DB", +":( c #F3EDDF", +"<( c #D4CFC2", +"[( c #78766F", +"}( c #696761", +"|( c #5E5C57", +"1( c #65635E", +"2( c #CCC7BA", +"3( c #9C9A91", +"4( c #878581", +"5( c #8A8983", +"6( c #8B8984", +"7( c #7E7D7A", +"8( c #6D6D6F", +"9( c #3C3E40", +"0( c #353638", +"a( c #323335", +"b( c #313233", +"c( c #303134", +"d( c #414142", +"e( c #575756", +"f( c #5D5C5D", +"g( c #565554", +"h( c #5A5957", +"i( c #666462", +"j( c #EBE5D7", +"k( c #F1EDDE", +"l( c #FAF4E6", +"m( c #EBE6D9", +"n( c #FEF9EA", +"o( c #BBB6AB", +"p( c #6E6C66", +"q( c #615F5A", +"r( c #5B5955", +"s( c #696661", +"t( c #E1DCCE", +"u( c #8C8B85", +"v( c #93908A", +"w( c #8A8885", +"x( c #3B3C3F", +"y( c #3B3C3D", +"z( c #37383C", +"A( c #353639", +"B( c #313235", +"C( c #3E3E41", +"D( c #4D4D4D", +"E( c #5C5B5B", +"F( c #676765", +"G( c #686865", +"H( c #6C6C69", +"I( c #636260", +"J( c #545351", +"K( c #5C5B59", +"L( c #97938C", +"M( c #FDF8E9", +"N( c #F8F3E5", +"O( c #F6F0E1", +"P( c #D7D2C7", +"Q( c #F5EFE2", +"R( c #DAD4C8", +"S( c #605E59", +"T( c #5E5C58", +"U( c #686661", +"V( c #DED9CD", +"W( c #E2DDCF", +"X( c #98968F", +"Y( c #99958E", +"Z( c #94928B", +"`( c #7F7E7A", +" _ c #7E7C7A", +"._ c #868582", +"+_ c #767673", +"@_ c #5D5D5F", +"#_ c #343538", +"$_ c #323336", +"%_ c #323235", +"&_ c #2F2F32", +"*_ c #2B2C2E", +"=_ c #5C5D5F", +"-_ c #3A3A3C", +";_ c #5E5D5A", +">_ c #6F6E6A", +",_ c #E3DFD2", +"'_ c #F6F2E3", +")_ c #FEFDF2", +"!_ c #FAF6E9", +"~_ c #EDE7DA", +"{_ c #BCB9AF", +"]_ c #B9B6AC", +"^_ c #FFFEF2", +"/_ c #F8F2E4", +"(_ c #6F6D67", +"__ c #676560", +":_ c #62605C", +"<_ c #63615C", +"[_ c #726F6B", +"}_ c #949289", +"|_ c #F9F4E5", +"1_ c #FCF9EF", +"2_ c #AFACA3", +"3_ c #A29D96", +"4_ c #9D9992", +"5_ c #7A7979", +"6_ c #69696A", +"7_ c #303033", +"8_ c #2F3032", +"9_ c #2E2F32", +"0_ c #2E2F31", +"a_ c #2C2D30", +"b_ c #2C2D2F", +"c_ c #333437", +"d_ c #525255", +"e_ c #3D3E3F", +"f_ c #434444", +"g_ c #4C4D4D", +"h_ c #636361", +"i_ c #5D5C59", +"j_ c #565553", +"k_ c #6A6865", +"l_ c #FEFCEF", +"m_ c #FFFFF7", +"n_ c #FEFDF4", +"o_ c #FCF7E8", +"p_ c #BEB9B0", +"q_ c #BDB9B0", +"r_ c #C5C1B7", +"s_ c #FFFEF4", +"t_ c #FFFFF6", +"u_ c #FFFBED", +"v_ c #827E77", +"w_ c #77746E", +"x_ c #686660", +"y_ c #66645F", +"z_ c #FBF6E8", +"A_ c #FFFEF3", +"B_ c #FFFFF9", +"C_ c #F9F6EA", +"D_ c #8A8782", +"E_ c #858380", +"F_ c #817F7C", +"G_ c #82807C", +"H_ c #5A5959", +"I_ c #5E5F60", +"J_ c #3F4040", +"K_ c #605F5E", +"L_ c #646361", +"M_ c #585755", +"N_ c #A5A19A", +"O_ c #FDFAEC", +"P_ c #FEFAED", +"Q_ c #D0CCC1", +"R_ c #B0AEA4", +"S_ c #D8D2C7", +"T_ c #E3DDD2", +"U_ c #FBF7E8", +"V_ c #FFFEF5", +"W_ c #FBF7E9", +"X_ c #6F6C67", +"Y_ c #76736E", +"Z_ c #D2CDC2", +"`_ c #F4F0E1", +" : c #888480", +".: c #908D88", +"+: c #82807D", +"@: c #717272", +"#: c #4B4B4B", +"$: c #535353", +"%: c #62615F", +"&: c #636360", +"*: c #676563", +"=: c #61605E", +"-: c #575654", +";: c #585855", +">: c #63625E", +",: c #FFFDF0", +"': c #FCF8EA", +"): c #FFFDF1", +"!: c #FDF7E9", +"~: c #BAB6AD", +"{: c #FEFFF3", +"]: c #7A7772", +"^: c #CECABF", +"/: c #E6E1D5", +"(: c #FFFFF4", +"_: c #FEFEF7", +":: c #FFFFF5", +"<: c #FBF8EB", +"[: c #918E88", +"}: c #82817E", +"|: c #707071", +"1: c #505050", +"2: c #575656", +"3: c #595958", +"4: c #626360", +"5: c #636160", +"6: c #585754", +"7: c #595855", +"8: c #6F6E69", +"9: c #98948D", +"0: c #908E87", +"a: c #B8B3AA", +"b: c #B4B2A8", +"c: c #D6D2C5", +"d: c #FFFFF0", +"e: c #F9F5E6", +"f: c #E5E1D4", +"g: c #C9C4BA", +"h: c #A8A49D", +"i: c #A4A29A", +"j: c #D1CDC2", +"k: c #FAF6E7", +"l: c #6E6C68", +"m: c #ECE8DB", +"n: c #FEFEF4", +"o: c #FFFFF8", +"p: c #F5F0E3", +"q: c #F6F0E3", +"r: c #CAC6BC", +"s: c #83827D", +"t: c #797872", +"u: c #737373", +"v: c #515151", +"w: c #545353", +"x: c #5D5C5B", +"y: c #5A5958", +"z: c #555452", +"A: c #5C5B57", +"B: c #6C6A66", +"C: c #74716D", +"D: c #807C77", +"E: c #FEFEF1", +"F: c #A2A097", +"G: c #DEDACE", +"H: c #F9F5E7", +"I: c #706F6A", +"J: c #D9D5CA", +"K: c #FDFAED", +"L: c #FBF9EE", +"M: c #F6F1E4", +"N: c #E2DED2", +"O: c #D2CDC3", +"P: c #9F9D96", +"Q: c #9A9891", +"R: c #93918B", +"S: c #84827E", +"T: c #5F6060", +"U: c #545453", +"V: c #515150", +"W: c #585857", +"X: c #5D5C5A", +"Y: c #5B5A59", +"Z: c #535351", +"`: c #545350", +" < c #6D6C67", +".< c #6E6D68", +"+< c #B7B2A8", +"@< c #E4E0D3", +"#< c #F1EBDE", +"$< c #F5F0E2", +"%< c #726F6A", +"&< c #9F9B94", +"*< c #CCC8BD", +"=< c #DED9CE", +"-< c #E9E4D8", +";< c #F0EBDF", +">< c #E7E3D8", +",< c #F6F0E4", +"'< c #D8D4CA", +")< c #E3DFD4", +"!< c #D6D1C7", +"~< c #BBB7AE", +"{< c #85837F", +"]< c #8A8985", +"^< c #83827E", +"/< c #3E3E3F", +"(< c #494949", +"_< c #525251", +":< c #525151", +"<< c #5A5A59", +"[< c #4F4D4C", +"}< c #555451", +"|< c #605F5B", +"1< c #686763", +"2< c #6E6D69", +"3< c #CAC6BB", +"4< c #F7F3E6", +"5< c #D2CEC1", +"6< c #CFCBC1", +"7< c #E6E2D6", +"8< c #EAE6DB", +"9< c #DFDCD1", +"0< c #D3CFC5", +"a< c #C7C4BB", +"b< c #E5E1D6", +"c< c #B5B2A9", +"d< c #85837E", +"e< c #7B7A79", +"f< c #84837F", +"g< c #656463", +"h< c #7C7C7D", +"i< c #555554", +"j< c #565653", +"k< c #4D4D4B", +"l< c #535250", +"m< c #64635F", +"n< c #6F6E68", +"o< c #C9C5B8", +"p< c #FEFBEE", +"q< c #A6A49B", +"r< c #D6D0C5", +"s< c #E0DACD", +"t< c #72716C", +"u< c #B3AFA7", +"v< c #C9C6BC", +"w< c #C8C4BB", +"x< c #E2DED4", +"y< c #E0DCD2", +"z< c #C2C0B7", +"A< c #BDBAB2", +"B< c #D4D0C5", +"C< c #757371", +"D< c #82827E", +"E< c #797873", +"F< c #7E7D79", +"G< c #7A7A78", +"H< c #636464", +"I< c #8B8B8C", +"J< c #3E3F3F", +"K< c #434343", +"L< c #535352", +"M< c #555553", +"N< c #51504E", +"O< c #4A4A48", +"P< c #585654", +"Q< c #73706B", +"R< c #929088", +"S< c #F5F0E1", +"T< c #FAF7E8", +"U< c #CBC8BC", +"V< c #B6B2AB", +"W< c #AAA8A1", +"X< c #B8B5AD", +"Y< c #C7C4BC", +"Z< c #CCC9C1", +"`< c #D4D1C8", +" [ c #DBD8CF", +".[ c #DFDAD1", +"+[ c #D4D0C7", +"@[ c #C3BFB7", +"#[ c #E0DBD1", +"$[ c #60615F", +"%[ c #515251", +"&[ c #585757", +"*[ c #535251", +"=[ c #4C4B4A", +"-[ c #484746", +";[ c #5C5B58", +">[ c #BCB8AC", +",[ c #B3AEA5", +"'[ c #FEFAEB", +")[ c #C8C5BC", +"![ c #BBB8B0", +"~[ c #CDCAC2", +"{[ c #DDD9CF", +"][ c #D2CEC5", +"^[ c #C9C7BE", +"/[ c #DBD7CE", +"([ c #C3C0B8", +"_[ c #E1DDD1", +":[ c #9C9A93", +"<[ c #6D6B68", +"[[ c #5B5B5C", +"}[ c #464646", +"|[ c #535252", +"1[ c #565655", +"2[ c #4F4F4E", +"3[ c #494846", +"4[ c #484846", +"5[ c #5B5A57", +"6[ c #CAC6B9", +"7[ c #FAF5E5", +"8[ c #EEE9DC", +"9[ c #B3B0A8", +"0[ c #CECAC1", +"a[ c #C6C3B9", +"b[ c #C5C1B9", +"c[ c #C6C3BB", +"d[ c #D1CDC5", +"e[ c #DAD7CD", +"f[ c #D9D5CC", +"g[ c #C6C4BC", +"h[ c #B1AFA7", +"i[ c #686968", +"j[ c #6A6964", +"k[ c #454545", +"l[ c #545553", +"m[ c #4C4C4B", +"n[ c #444443", +"o[ c #494947", +"p[ c #64635E", +"q[ c #B3B0A5", +"r[ c #D5CFC4", +"s[ c #F3EEDF", +"t[ c #FFFCEC", +"u[ c #FDFBEF", +"v[ c #B4B1A9", +"w[ c #C9C5BC", +"x[ c #C5C2BA", +"y[ c #C8C5BD", +"z[ c #B6B4AD", +"A[ c #C5C2BB", +"B[ c #C3C0B9", +"C[ c #C1BFB7", +"D[ c #C5C3BC", +"E[ c #6A6867", +"F[ c #696966", +"G[ c #686966", +"H[ c #797776", +"I[ c #525252", +"J[ c #3A3B3B", +"K[ c #4E4D4D", +"L[ c #525150", +"M[ c #4A4948", +"N[ c #424240", +"O[ c #4B4B49", +"P[ c #676561", +"Q[ c #F9F6E9", +"R[ c #E1DBCF", +"S[ c #A5A29C", +"T[ c #B3B0A9", +"U[ c #BFBCB5", +"V[ c #C0BDB6", +"W[ c #B5B2AC", +"X[ c #B0AEA9", +"Y[ c #CAC7C0", +"Z[ c #94928E", +"`[ c #6D6D6E", +" } c #3D3D3D", +".} c #4A4A4A", +"+} c #50504F", +"@} c #454443", +"#} c #434241", +"$} c #5D5C58", +"%} c #6D6A65", +"&} c #DDD8CA", +"*} c #E4DED1", +"=} c #F7F1E3", +"-} c #82817B", +";} c #83817C", +">} c #8A8883", +",} c #989690", +"'} c #9E9B96", +")} c #A4A29D", +"!} c #B1AEA9", +"~} c #BCBAB3", +"{} c #BFBDB6", +"]} c #B9B7B1", +"^} c #B1AFAB", +"/} c #BCB9B3", +"(} c #B7B6B0", +"_} c #B8B6AF", +":} c #B9B6B0", +"<} c #716F6D", +"[} c #5F5F60", +"}} c #373838", +"|} c #4F4F4F", +"1} c #4E4E4D", +"2} c #403F3E", +"3} c #444342", +"4} c #4C4B49", +"5} c #61615D", +"6} c #DDD9CC", +"7} c #FDF7E8", +"8} c #F8F2E3", +"9} c #97948F", +"0} c #A09E99", +"a} c #9F9D98", +"b} c #A3A09B", +"c} c #AAA8A2", +"d} c #AEACA7", +"e} c #B1AFAA", +"f} c #B3B1AC", +"g} c #ADACA7", +"h} c #B2B0AB", +"i} c #AAA9A4", +"j} c #C5C3BB", +"k} c #898884", +"l} c #7F7E79", +"m} c #4D4C4D", +"n} c #464645", +"o} c #4E4E4E", +"p} c #4A4A49", +"q} c #3E3D3C", +"r} c #444442", +"s} c #52514F", +"t} c #5B5A56", +"u} c #62615C", +"v} c #D9D3C7", +"w} c #DFDACE", +"x} c #EDE9DC", +"y} c #908F89", +"z} c #9B9995", +"A} c #A3A19C", +"B} c #A19F99", +"C} c #ADABA6", +"D} c #ACA9A4", +"E} c #737271", +"F} c #707171", +"G} c #515052", +"H} c #444545", +"I} c #4D4D4C", +"J} c #474746", +"K} c #3F3F3E", +"L} c #494946", +"M} c #53524F", +"N} c #E2DCCF", +"O} c #ECE7DA", +"P} c #F8F5E6", +"Q} c #FEFDF3", +"R} c #F3EFE1", +"S} c #E0DBCF", +"T} c #F9F7EC", +"U} c #EEEBDD", +"V} c #817F7B", +"W} c #8D8B87", +"X} c #989692", +"Y} c #9A9894", +"Z} c #999893", +"`} c #9A9893", +" | c #A9A8A4", +".| c #A8A6A2", +"+| c #A1A09C", +"@| c #A4A39E", +"#| c #707172", +"$| c #4C4C4C", +"%| c #434342", +"&| c #3C3C3B", +"*| c #474744", +"=| c #7B7A73", +"-| c #FBF5E7", +";| c #FAF4E7", +">| c #F4EEE1", +",| c #F6F4E8", +"'| c #CEC9BF", +")| c #9E9C94", +"!| c #F3F0E4", +"~| c #FFFEF6", +"{| c #FAF8ED", +"]| c #72726F", +"^| c #92918D", +"/| c #92918E", +"(| c #93918E", +"_| c #979693", +":| c #A4A29E", +"<| c #9D9C98", +"[| c #999795", +"}| c #9A9997", +"|| c #4B4A4A", +"1| c #3E3E3D", +"2| c #4B4A47", +"3| c #5B5855", +"4| c #DED8CC", +"5| c #FDF8EA", +"6| c #F3EDE0", +"7| c #EBE5D9", +"8| c #F5F1E3", +"9| c #71716E", +"0| c #7D7D7B", +"a| c #8C8B88", +"b| c #91908D", +"c| c #918F8C", +"d| c #949390", +"e| c #999896", +"f| c #989794", +"g| c #A09F9B", +"h| c #9F9E9B", +"i| c #979593", +"j| c #696767", +"k| c #3B3B3B", +"l| c #4B4B4A", +"m| c #3B3B3A", +"n| c #504E4C", +"o| c #615F5B", +"p| c #E5E1D3", +"q| c #DFDBCE", +"r| c #FBF5E6", +"s| c #D2CEC3", +"t| c #C7C3B9", +"u| c #DFDACC", +"v| c #878683", +"w| c #8F8E8B", +"x| c #8E8D8B", +"y| c #8E8E8C", +"z| c #91908E", +"A| c #9C9B98", +"B| c #93928F", +"C| c #878784", +"D| c #6E6E6F", +"E| c #484847", +"F| c #3D3C3B", +"G| c #575652", +"H| c #FFFEF8", +"I| c #FBF6E9", +"J| c #FCF7EA", +"K| c #F3EEE1", +"L| c #939290", +"M| c #92918F", +"N| c #969593", +"O| c #999895", +"P| c #8C8B8A", +"Q| c #9A9996", +"R| c #767676", +"S| c #424241", +"T| c #3D3D3C", +"U| c #40403E", +"V| c #FEFAEC", +"W| c #FEFEEF", +"X| c #FEFEF2", +"Y| c #E4DFD4", +"Z| c #F1EBDF", +"`| c #E5E0D5", +" 1 c #BAB7AD", +".1 c #CDC8BE", +"+1 c #C6C3B8", +"@1 c #B8B5AB", +"#1 c #B4AFA7", +"$1 c #81807E", +"%1 c #92928F", +"&1 c #908F8D", +"*1 c #8F8F8D", +"=1 c #898886", +"-1 c #8F8E8D", +";1 c #7A7A7B", +">1 c #383837", +",1 c #3A3A39", +"'1 c #424140", +")1 c #575552", +"!1 c #D4CFC4", +"~1 c #E7E2D6", +"{1 c #FEFEEE", +"]1 c #FEFFF4", +"^1 c #FFFFEF", +"/1 c #FDFCF1", +"(1 c #FAF7E9", +"_1 c #E8E4D9", +":1 c #ECE7DC", +"<1 c #DEDAD0", +"[1 c #E0DCD0", +"}1 c #B9B6AD", +"|1 c #CAC5BB", +"11 c #9C9992", +"21 c #81807B", +"31 c #70706D", +"41 c #7E7D7B", +"51 c #888886", +"61 c #8D8C8A", +"71 c #8A8988", +"81 c #888786", +"91 c #868583", +"01 c #313131", +"a1 c #3A3939", +"b1 c #4A4947", +"c1 c #61605C", +"d1 c #B6B1A8", +"e1 c #D1CBC0", +"f1 c #F2EDE0", +"g1 c #EFEADE", +"h1 c #F4EEE3", +"i1 c #E9E5DA", +"j1 c #E6E1D6", +"k1 c #DDD9CE", +"l1 c #C4C1B7", +"m1 c #7E7D7C", +"n1 c #878684", +"o1 c #8B8A88", +"p1 c #8B8B89", +"q1 c #8B8A89", +"r1 c #868584", +"s1 c #888887", +"t1 c #2E2E2E", +"u1 c #7C7B74", +"v1 c #E2DCD0", +"w1 c #F2ECDF", +"x1 c #F8F4E8", +"y1 c #F1ECDF", +"z1 c #E2DED3", +"A1 c #EAE4D9", +"B1 c #CECAC0", +"C1 c #C6C3BA", +"D1 c #DDD9CD", +"E1 c #C1BEB4", +"F1 c #FAF5E6", +"G1 c #B0AEA6", +"H1 c #A19E99", +"I1 c #908F8A", +"J1 c #777573", +"K1 c #747271", +"L1 c #727271", +"M1 c #7E7E7C", +"N1 c #8C8C8A", +"O1 c #888787", +"P1 c #838282", +"Q1 c #444341", +"R1 c #65635F", +"S1 c #F8F2E5", +"T1 c #FFFAED", +"U1 c #E9E4D9", +"V1 c #DBD7CC", +"W1 c #EDE8DD", +"X1 c #D1CDC3", +"Y1 c #E3DED2", +"Z1 c #D9D5C8", +"`1 c #FEFEF3", +" 2 c #D8D4C9", +".2 c #9C9994", +"+2 c #868684", +"@2 c #8D8C8B", +"#2 c #8A8A89", +"$2 c #858584", +"%2 c #868685", +"&2 c #4D4C4A", +"*2 c #71706B", +"=2 c #FAF7EA", +"-2 c #F7F2E5", +";2 c #E2DDD2", +">2 c #CCC8BF", +",2 c #E1DDD2", +"'2 c #DBD7CB", +")2 c #DCD8CC", +"!2 c #FDFCEE", +"~2 c #FFFDF2", +"{2 c #F3EFE3", +"]2 c #D9D5CB", +"^2 c #B4B1A8", +"/2 c #9F9C96", +"(2 c #81807C", +"_2 c #7F7E7D", +":2 c #8A8A88", +"<2 c #898887", +"[2 c #7C7B7B", +"}2 c #7E7E7F", +"|2 c #696766", +"12 c #444647", +"22 c #6B6B6D", +"32 c #61615F", +"42 c #5A5955", +"52 c #C7C2B8", +"62 c #E1DDD0", +"72 c #FEFCF0", +"82 c #FFFEF7", +"92 c #F8F4E7", +"02 c #E9E5D8", +"a2 c #D4D0C6", +"b2 c #BCB9B0", +"c2 c #DAD5CA", +"d2 c #E3DED3", +"e2 c #EEE9DE", +"f2 c #DFDBCF", +"g2 c #E0DBD0", +"h2 c #E2DED1", +"i2 c #F7F2E4", +"j2 c #C2BFB5", +"k2 c #B1AEA7", +"l2 c #9A9892", +"m2 c #878580", +"n2 c #8C8B87", +"o2 c #7C7C7A", +"p2 c #878786", +"q2 c #848483", +"r2 c #7F7F80", +"s2 c #797878", +"t2 c #838383", +"u2 c #62615D", +"v2 c #D3CFC4", +"w2 c #FFFAEB", +"x2 c #E7E3D7", +"y2 c #DFDACF", +"z2 c #A7A49E", +"A2 c #DBD7CD", +"B2 c #E0DCD1", +"C2 c #D8D3CA", +"D2 c #DFDBD0", +"E2 c #F3EDE1", +"F2 c #DDD8CD", +"G2 c #F0EBDE", +"H2 c #C1BEB6", +"I2 c #9B9993", +"J2 c #979692", +"K2 c #908E8B", +"L2 c #838280", +"M2 c #878785", +"N2 c #7D7C7C", +"O2 c #808080", +"P2 c #7B7B7B", +"Q2 c #565758", +"R2 c #A7A7A6", +"S2 c #52504D", +"T2 c #EAE5D9", +"U2 c #A9A69F", +"V2 c #DAD6CB", +"W2 c #ECE7DB", +"X2 c #E8E4D7", +"Y2 c #E5E0D4", +"Z2 c #BAB7AE", +"`2 c #DCD8CB", +" 3 c #CDC9BF", +".3 c #F4EFE2", +"+3 c #F6F2E5", +"@3 c #D7D3C9", +"#3 c #ABA9A1", +"$3 c #918E89", +"%3 c #92908B", +"&3 c #9E9C97", +"*3 c #A19F9A", +"=3 c #9E9C98", +"-3 c #908F8C", +";3 c #80807F", +">3 c #7F7F7E", +",3 c #727272", +"'3 c #777879", +")3 c #3A3B3C", +"!3 c #898681", +"~3 c #AEABA3", +"{3 c #BBB8AE", +"]3 c #F8F3E6", +"^3 c #F6F2E4", +"/3 c #ADAAA2", +"(3 c #D8D3C9", +"_3 c #C3BEB6", +":3 c #DEDACF", +"<3 c #CBC7BF", +"[3 c #E7E4D7", +"}3 c #F7F1E5", +"|3 c #B6B3AA", +"13 c #CBC8BD", +"23 c #F3EFE2", +"33 c #EEE9DD", +"43 c #C4C0B7", +"53 c #A8A69E", +"63 c #9B9892", +"73 c #9C9993", +"83 c #9F9D97", +"93 c #A5A39D", +"03 c #A9A7A2", +"a3 c #A2A09C", +"b3 c #949391", +"c3 c #898988", +"d3 c #7E7E7E", +"e3 c #767778", +"f3 c #666767", +"g3 c #5B5C5A", +"h3 c #353537", +"i3 c #555557", +"j3 c #696764", +"k3 c #EDE8DC", +"l3 c #FDF7EA", +"m3 c #FEFEF5", +"n3 c #FBF7EA", +"o3 c #E8E2D6", +"p3 c #E9E3D7", +"q3 c #C1BDB4", +"r3 c #BBB9B0", +"s3 c #A09D97", +"t3 c #B7B5AD", +"u3 c #E1DCD1", +"v3 c #B7B4AD", +"w3 c #E4DFD5", +"x3 c #E4E0D4", +"y3 c #F1ECE0", +"z3 c #F0ECE0", +"A3 c #CAC6BE", +"B3 c #C9C5BB", +"C3 c #A8A59E", +"D3 c #F7F3E5", +"E3 c #FBF8EA", +"F3 c #E6E2D4", +"G3 c #BDB9B1", +"H3 c #B0ADA6", +"I3 c #ABA9A4", +"J3 c #A6A4A0", +"K3 c #9F9E9A", +"L3 c #9A9995", +"M3 c #828282", +"N3 c #7B7B7A", +"O3 c #818180", +"P3 c #787777", +"Q3 c #6D6E71", +"R3 c #626263", +"S3 c #707072", +"T3 c #B2AFA6", +"U3 c #CBC6BC", +"V3 c #EEEADD", +"W3 c #FCF8EB", +"X3 c #FEFCF1", +"Y3 c #F7F4E8", +"Z3 c #D9D4C9", +"`3 c #A6A29B", +" 4 c #B5B2AA", +".4 c #DAD5CB", +"+4 c #ACA9A2", +"@4 c #C2BFB7", +"#4 c #E7E2D8", +"$4 c #EBE6DB", +"%4 c #DCD9CF", +"&4 c #A5A39C", +"*4 c #DBD6CC", +"=4 c #CFCCC1", +"-4 c #C5C1B8", +";4 c #D3CEC4", +">4 c #F4EFE3", +",4 c #DAD6CA", +"'4 c #B8B4AC", +")4 c #BBB9B1", +"!4 c #C0BEB6", +"~4 c #B8B6B0", +"{4 c #ABAAA5", +"]4 c #A2A19C", +"^4 c #90908E", +"/4 c #878685", +"(4 c #787776", +"_4 c #787979", +":4 c #66676A", +"<4 c #636466", +"[4 c #7E7C78", +"}4 c #D5D1C7", +"|4 c #DCD7CC", +"14 c #DFDAD0", +"24 c #ECE8DC", +"34 c #FFFAEC", +"44 c #FCF6E9", +"54 c #F9F4E7", +"64 c #C8C5BB", +"74 c #C7C2BA", +"84 c #ACAAA3", +"94 c #D6D2C7", +"04 c #A3A099", +"a4 c #A8A69F", +"b4 c #E1DDD3", +"c4 c #DCD7CE", +"d4 c #E4E1D5", +"e4 c #C4C1B8", +"f4 c #A7A49F", +"g4 c #CFCBC2", +"h4 c #9B9992", +"i4 c #A7A59D", +"j4 c #B1AEA6", +"k4 c #BEBBB2", +"l4 c #ADAAA3", +"m4 c #CBC8BF", +"n4 c #E6E2D8", +"o4 c #E3DFD5", +"p4 c #EFEBDF", +"q4 c #F2EDE1", +"r4 c #FFFBEE", +"s4 c #FEF8EB", +"t4 c #CCC9BE", +"u4 c #BCB9B1", +"v4 c #C4C1B9", +"w4 c #C1BEB8", +"x4 c #B4B2AD", +"y4 c #AFADA8", +"z4 c #A19F9C", +"A4 c #979694", +"B4 c #959492", +"C4 c #7C7B7A", +"D4 c #727273", +"E4 c #6B6C6D", +"F4 c #5B5C5E", +"G4 c #555659", +"H4 c #5E5D5D", +"I4 c #606160", +"J4 c #6A6B6D", +"K4 c #646462", +"L4 c #7A7A74", +"M4 c #A8A59D", +"N4 c #D0CCC2", +"O4 c #DDD8CE", +"P4 c #E8E4D8", +"Q4 c #EBE6DA", +"R4 c #F3EEE2", +"S4 c #FDF8EB", +"T4 c #9C9A94", +"U4 c #989590", +"V4 c #E4E0D5", +"W4 c #BCB8B1", +"X4 c #ABA8A2", +"Y4 c #CCC8BE", +"Z4 c #CBC8BE", +"`4 c #9D9B94", +" 5 c #918E8A", +".5 c #A4A29C", +"+5 c #E6E1D7", +"@5 c #E7E3D9", +"#5 c #E9E5D9", +"$5 c #F9F4E8", +"%5 c #BAB7B1", +"&5 c #A9A7A3", +"*5 c #ACAAA5", +"=5 c #A3A19E", +"-5 c #696A6D", +";5 c #505153", +">5 c #505155", +",5 c #95938C", +"'5 c #A29F98", +")5 c #B6B3AB", +"!5 c #C7C5BC", +"~5 c #C6C2B9", +"{5 c #DCD8CE", +"]5 c #E1DED2", +"^5 c #F7F2E6", +"/5 c #F8F3E7", +"(5 c #E5E1D5", +"_5 c #B8B5AC", +":5 c #D3D0C5", +"<5 c #A19E98", +"[5 c #BEBAB3", +"}5 c #DBD8CD", +"|5 c #D8D5CB", +"15 c #C5C2B9", +"25 c #D7D4CA", +"35 c #ACA9A3", +"45 c #BAB7AF", +"55 c #C9C6BD", +"65 c #B5B2AB", +"75 c #C9C5BD", +"85 c #81807D", +"95 c #908E8A", +"05 c #DFDBD2", +"a5 c #E0DBD2", +"b5 c #E3DED4", +"c5 c #F4F0E3", +"d5 c #BFBDB7", +"e5 c #ABA9A5", +"f5 c #7B7A7A", +"g5 c #717170", +"h5 c #79797A", +"i5 c #6A6B6E", +"j5 c #575657", +"k5 c #3F3F42", +"l5 c #5D5E5F", +"m5 c #A6A39C", +"n5 c #ACA9A1", +"o5 c #C2BFB6", +"p5 c #CAC6BD", +"q5 c #B9B6AE", +"r5 c #C4C0B8", +"s5 c #D1CDC4", +"t5 c #D6D2C8", +"u5 c #D8D4CB", +"v5 c #D9D4CA", +"w5 c #9A9791", +"x5 c #999690", +"y5 c #DAD6CC", +"z5 c #A5A29D", +"A5 c #C0BDB5", +"B5 c #C8C4BC", +"C5 c #D0CDC3", +"D5 c #E6E2D7", +"E5 c #CFCCC3", +"F5 c #D3D0C6", +"G5 c #B7B3AC", +"H5 c #8D8C88", +"I5 c #A7A59E", +"J5 c #D6D2C9", +"K5 c #989791", +"L5 c #D5D1C9", +"M5 c #9D9A95", +"N5 c #D0CDC4", +"O5 c #8C8B86", +"P5 c #DFDCD2", +"Q5 c #CDC9C1", +"R5 c #D2CFC7", +"S5 c #D7D3CA", +"T5 c #D7D2C9", +"U5 c #D4D1C6", +"V5 c #C9C6BE", +"W5 c #B3B1AB", +"X5 c #B4B2AC", +"Y5 c #747473", +"Z5 c #656566", +"`5 c #6B6C6F", +" 6 c #616061", +".6 c #56575A", +"+6 c #515255", +"@6 c #4B4C4D", +"#6 c #5D5E60", +"$6 c #4F4E4C", +"%6 c #827F7B", +"&6 c #888681", +"*6 c #95928C", +"=6 c #CFCCC2", +"-6 c #CDCAC1", +";6 c #D1CEC4", +">6 c #EDE9DE", +",6 c #E0DDD2", +"'6 c #EAE6DA", +")6 c #D5D2C7", +"!6 c #A6A49D", +"~6 c #D5D1C8", +"{6 c #B2AFA8", +"]6 c #A2A09A", +"^6 c #D7D2CA", +"/6 c #DFDBD1", +"(6 c #DCD8CF", +"_6 c #ADAAA4", +":6 c #D2CFC6", +"<6 c #C7C3BB", +"[6 c #D7D4CB", +"}6 c #A8A6A0", +"|6 c #8B8A86", +"16 c #827F7D", +"26 c #D2CDC5", +"36 c #ABA9A2", +"46 c #CECBC2", +"56 c #95938E", +"66 c #CFCCC4", +"76 c #C8C4BD", +"86 c #B5B3AD", +"96 c #BAB8B2", +"06 c #CAC7BF", +"a6 c #C4C0B9", +"b6 c #C3C1BA", +"c6 c #C0BEB7", +"d6 c #B9B6B1", +"e6 c #B6B4AE", +"f6 c #92908E", +"g6 c #848381", +"h6 c #828180", +"i6 c #6D6D6D", +"j6 c #68696C", +"k6 c #606163", +"l6 c #626364", +"m6 c #A9A6A0", +"n6 c #BBB7AF", +"o6 c #EAE5DB", +"p6 c #9F9C97", +"q6 c #DAD6CD", +"r6 c #A29F9A", +"s6 c #9E9B95", +"t6 c #DCD7CD", +"u6 c #DEDAD1", +"v6 c #BEBAB4", +"w6 c #B1AEA8", +"x6 c #BBB8B2", +"y6 c #B7B4AE", +"z6 c #C6C2BB", +"A6 c #B5B3AC", +"B6 c #7E7C79", +"C6 c #999894", +"D6 c #B7B5AF", +"E6 c #C3BFB9", +"F6 c #C6C3BC", +"G6 c #BEBCB5", +"H6 c #B4B2AE", +"I6 c #C2BFB9", +"J6 c #828281", +"K6 c #606161", +"L6 c #595A5C", +"M6 c #55565B", +"N6 c #525354", +"O6 c #5D5D5E", +"P6 c #7C7C77", +"Q6 c #A3A09A", +"R6 c #B9B6AF", +"S6 c #D1CEC5", +"T6 c #D4D0C8", +"U6 c #CBC7BE", +"V6 c #D3CFC6", +"W6 c #D0CCC4", +"X6 c #8A8984", +"Y6 c #AFADA6", +"Z6 c #7F7D7A", +"`6 c #DAD5CD", +" 7 c #A6A39D", +".7 c #888782", +"+7 c #D5D2C9", +"@7 c #B2B0A8", +"#7 c #A6A49E", +"$7 c #C7C5BE", +"%7 c #93918C", +"&7 c #D0CDC5", +"*7 c #D2CEC6", +"=7 c #84837E", +"-7 c #9C9A97", +";7 c #A5A39F", +">7 c #A8A6A1", +",7 c #C5C2BC", +"'7 c #BEBBB5", +")7 c #BAB8B3", +"!7 c #BAB7B2", +"~7 c #C2C0BA", +"{7 c #787879", +"]7 c #5A5B5E", +"^7 c #55575A", +"/7 c #4E4F52", +"(7 c #47494A", +"_7 c #404043", +":7 c #7F8081", +"<7 c #7E7D78", +"[7 c #A09E98", +"}7 c #DDD8CF", +"|7 c #878582", +"17 c #9A9792", +"27 c #B4B1AA", +"37 c #C5C1BA", +"47 c #D1CDC6", +"57 c #ACAAA4", +"67 c #BAB7B0", +"77 c #C1BEB7", +"87 c #A9A7A0", +"97 c #C4C2BA", +"07 c #9F9E98", +"a7 c #9D9B96", +"b7 c #93928E", +"c7 c #969591", +"d7 c #9B9A97", +"e7 c #8D8C89", +"f7 c #A3A19D", +"g7 c #BAB8B1", +"h7 c #BBB9B3", +"i7 c #B7B5B0", +"j7 c #B2B1AB", +"k7 c #969490", +"l7 c #858582", +"m7 c #646363", +"n7 c #69696B", +"o7 c #656568", +"p7 c #545457", +"q7 c #545559", +"r7 c #4E4E4C", +"s7 c #9B9994", +"t7 c #9E9C96", +"u7 c #A4A19C", +"v7 c #B1AFA9", +"w7 c #CEC9C0", +"x7 c #DAD5CC", +"y7 c #96948F", +"z7 c #AAA8A3", +"A7 c #918F8B", +"B7 c #CFCDC4", +"C7 c #9F9E99", +"D7 c #AFADA7", +"E7 c #B8B4AE", +"F7 c #B2AFA9", +"G7 c #96938F", +"H7 c #D3D0C7", +"I7 c #92908C", +"J7 c #A3A29F", +"K7 c #A4A39F", +"L7 c #8A8986", +"M7 c #9F9D9A", +"N7 c #969592", +"O7 c #989693", +"P7 c #A09E9B", +"Q7 c #9A9A97", +"R7 c #91908F", +"S7 c #848481", +"T7 c #82817F", +"U7 c #706F70", +"V7 c #5F5F61", +"W7 c #656669", +"X7 c #4E5052", +"Y7 c #525355", +"Z7 c #545352", +"`7 c #7B7875", +" 8 c #7A7A76", +".8 c #868480", +"+8 c #9C9A95", +"@8 c #ABA9A3", +"#8 c #B3B1AA", +"$8 c #BFBCB4", +"%8 c #D9D6CD", +"&8 c #CDCAC3", +"*8 c #BEBBB4", +"=8 c #888884", +"-8 c #CCC9C2", +";8 c #83837F", +">8 c #C3C1B9", +",8 c #BFBDB5", +"'8 c #C3BFB8", +")8 c #95938F", +"!8 c #8E8E8A", +"~8 c #A3A29E", +"{8 c #8C8C89", +"]8 c #81817F", +"^8 c #818080", +"/8 c #848484", +"(8 c #7D7D7D", +"_8 c #5F5F5D", +":8 c #5D5F62", +"<8 c #535457", +"[8 c #5B5C5D", +"}8 c #878681", +"|8 c #9B9893", +"18 c #9D9B95", +"28 c #AAA7A1", +"38 c #B0AEA7", +"48 c #A9A7A1", +"58 c #CAC6BF", +"68 c #D1CEC6", +"78 c #9A9993", +"88 c #94938F", +"98 c #CBC8C0", +"08 c #B2AFAA", +"a8 c #C7C4BD", +"b8 c #B2B0A9", +"c8 c #CCC8C1", +"d8 c #C9C6BF", +"e8 c #C8C5BE", +"f8 c #8E8D89", +"g8 c #B3B2AC", +"h8 c #9C9B97", +"i8 c #8B8A87", +"j8 c #8A8987", +"k8 c #81807F", +"l8 c #747475", +"m8 c #757677", +"n8 c #78797B", +"o8 c #797A7C", +"p8 c #78797A", +"q8 c #7A7B7C", +"r8 c #797A7B", +"s8 c #6C6C6E", +"t8 c #5C5D61", +"u8 c #595859", +"v8 c #93908C", +"w8 c #A7A59F", +"x8 c #B7B3AD", +"y8 c #BFBCB6", +"z8 c #9D9B97", +"A8 c #C7C3BC", +"B8 c #959490", +"C8 c #A6A49F", +"D8 c #A9A6A2", +"E8 c #92908D", +"F8 c #8E8C8A", +"G8 c #959491", +"H8 c #7A7A7A", +"I8 c #757676", +"J8 c #737374", +"K8 c #737476", +"L8 c #747577", +"M8 c #767779", +"N8 c #77787A", +"O8 c #78787B", +"P8 c #616263", +"Q8 c #5B5C5F", +"R8 c #515155", +"S8 c #4C4D50", +"T8 c #797977", +"U8 c #999793", +"V8 c #ADABA5", +"W8 c #8F8E8A", +"X8 c #B4B1AC", +"Y8 c #AEABA5", +"Z8 c #A2A09D", +"`8 c #9E9D99", +" 9 c #B7B4AF", +".9 c #A1A09D", +"+9 c #848482", +"@9 c #6B6B6B", +"#9 c #727374", +"$9 c #757577", +"%9 c #717174", +"&9 c #68696D", +"*9 c #6B6C70", +"=9 c #6D6E72", +"-9 c #707175", +";9 c #717275", +">9 c #6F7073", +",9 c #67696D", +"'9 c #5B5D5F", +")9 c #525254", +"!9 c #4F5053", +"~9 c #515256", +"{9 c #4E4F51", +"]9 c #5F6062", +"^9 c #8F8D8A", +"/9 c #969390", +"(9 c #9B9A95", +"_9 c #ACAAA6", +":9 c #A7A5A1", +"<9 c #888784", +"[9 c #817F7D", +"}9 c #898986", +"|9 c #9F9D99", +"19 c #A2A19D", +"29 c #7F7E7C", +"39 c #727172", +"49 c #6C6C6D", +"59 c #717274", +"69 c #676869", +"79 c #6A6C6E", +"89 c #666769", +"99 c #626468", +"09 c #5E6064", +"a9 c #5C5E62", +"b9 c #5B5D62", +"c9 c #5D5F63", +"d9 c #595B5F", +"e9 c #55565A", +"f9 c #4C4D4F", +"g9 c #47494C", +"h9 c #565759", +"i9 c #666667", +"j9 c #898885", +"k9 c #8B8986", +"l9 c #9E9C99", +"m9 c #AEACA8", +"n9 c #7B7B79", +"o9 c #A3A29D", +"p9 c #8F8E8C", +"q9 c #858482", +"r9 c #7E7D7E", +"s9 c #6F6F71", +"t9 c #6B6B6C", +"u9 c #737375", +"v9 c #5E5E5F", +"w9 c #5F6063", +"x9 c #616266", +"y9 c #5D5E62", +"z9 c #606165", +"A9 c #64656A", +"B9 c #616367", +"C9 c #5B5C61", +"D9 c #595A5E", +"E9 c #57595D", +"F9 c #585A5E", +"G9 c #56585C", +"H9 c #4E5053", +"I9 c #3B3B3E", +"J9 c #666768", +"K9 c #7D7C7A", +"L9 c #7D7D7A", +"M9 c #A19F9B", +"N9 c #7E7E7D", +"O9 c #7D7D7C", +"P9 c #727474", +"Q9 c #6E6E70", +"R9 c #707174", +"S9 c #6A6A6B", +"T9 c #67676A", +"U9 c #5E5D5E", +"V9 c #68696B", +"W9 c #616265", +"X9 c #616162", +"Y9 c #5F6163", +"Z9 c #5A5B5D", +"`9 c #626467", +" 0 c #5B5B5D", +".0 c #58595C", +"+0 c #606266", +"@0 c #5C5D62", +"#0 c #525357", +"$0 c #4E5054", +"%0 c #646567", +"&0 c #858483", +"*0 c #898987", +"=0 c #848383", +"-0 c #7F7F7F", +";0 c #5F5E5F", +">0 c #5F6061", +",0 c #58585A", +"'0 c #5A5B5F", +")0 c #58595B", +"!0 c #535456", +"~0 c #57585C", +"{0 c #57585A", +"]0 c #595B5D", +"^0 c #545558", +"/0 c #57595C", +"(0 c #535458", +"_0 c #46484B", +":0 c #595A5B", +"<0 c #666566", +"[0 c #7A7978", +"}0 c #808180", +"|0 c #858484", +"10 c #686665", +"20 c #868585", +"30 c #6E6E6E", +"40 c #757678", +"50 c #78787A", +"60 c #717173", +"70 c #6C6D6F", +"80 c #5D5E61", +"90 c #545658", +"00 c #535558", +"a0 c #4E4E51", +"b0 c #4B4C50", +"c0 c #515254", +"d0 c #5E5F5F", +"e0 c #666565", +"f0 c #6C6B6C", +"g0 c #787877", +"h0 c #8C8B89", +"i0 c #727171", +"j0 c #6D6D70", +"k0 c #707073", +"l0 c #626365", +"m0 c #636468", +"n0 c #5C5C5F", +"o0 c #636467", +"p0 c #535355", +"q0 c #525356", +"r0 c #505255", +"s0 c #515154", +"t0 c #505053", +"u0 c #4E4F53", +"v0 c #838486", +"w0 c #5B5A5B", +"x0 c #5A5B5A", +"y0 c #6E6D6E", +"z0 c #717070", +"A0 c #777776", +"B0 c #6D6E70", +"C0 c #646569", +"D0 c #4D4E51", +"E0 c #4C4E51", +"F0 c #4E4E52", +"G0 c #4D4E52", +"H0 c #3C3E41", +"I0 c #6E6F70", +"J0 c #696969", +"K0 c #696A6A", +"L0 c #777778", +"M0 c #79797B", +"N0 c #7D7E7F", +"O0 c #7B7A7C", +"P0 c #646364", +"Q0 c #626366", +"R0 c #4C4D51", +"S0 c #4F5054", +"T0 c #494B4E", +"U0 c #424243", +"V0 c #535454", +"W0 c #616364", +"X0 c #646466", +"Y0 c #646465", +"Z0 c #626466", +"`0 c #606064", +" a c #5E5E61", +".a c #606063", +"+a c #707173", +"@a c #737477", +"#a c #67686A", +"$a c #616264", +"%a c #646568", +"&a c #58595D", +"*a c #545458", +"=a c #444649", +"-a c #3D3D41", +";a c #5C5C5E", +">a c #676769", +",a c #6E6F72", +"'a c #5C5D5D", +")a c #494B4D", +"!a c #434547", +"~a c #414346", +"{a c #545557", +"]a c #5B5C60", +"^a c #626367", +"/a c #606164", +"(a c #6E7073", +"_a c #606062", +":a c #5F6162", +" , * * * ' - $ ", +" + ) * = = = * = ! ~ ", +" { * = * = = = = = - ] ", +" ^ , / = = = = = = = * ' ( _ ", +" : < * = * = = = = = = = * = ! [ ", +" } * * = = = = = = = = = = * = - | ", +" 1 2 3 = = = = = = = = = = = = = * ' 4 5 ", +" 6 < * = * = = = = = = = = = = = = = * = 7 | ", +" 8 * * = = = = = = = = = = = = = = = = * ' - [ ", +" 9 8 0 = = = = = = = = = = = = = = = = = = = * = ! _ ", +" a < * = * = = = = = = = = = = = = = = = = = = = * = 7 b ", +" c d e e e e f * = = = = = = = = = = = = = g e e e e f h i ", +" j k l m m m m n f = = = = = = = = = = = = o p q m m m m l r ", +" [ e = = = = = = = = = = = = s t ", +" u e = = = = = = = = = = = = s t ", +" v u e = = = = = = = = = = = = s t w ", +" ~ x u e = = = = = = = = = = = = s t y z ", +" A B C u e = = = = = = = = = = = = s t D { ", +" E F G H u e = = = = = = = = = = = = s t I J K L ", +" M N = N H u e = = = = = = = = = = = = s t I O s P Q ", +" R S ' = N H u e = = = = = = = = = = = = s t I O * T { U V W X Y Z ", +" E N ' ` = N H u e = = = = = = = = = = = = s t I O ` = ...+. @.#.#.$.%.%.&.*.=.-.;.>.,.'. ", +" ).!.= * = = N ). ~.e = = = = = = = = = = = = s t I O = ` {.s P Q ].^././././.(././././././.%._.:.<.[. ", +" }.|.O * = = = 1.2.3.4.4.4.4.4.4.4.4.4.4.4.5.6.= = = = = = = = = = = = O 7.8.4.4.4.4.4.4.4.4.4.4.4.9.0.= = = ` * = a. ].^././././././././././.^./././.^.b._.c.d.e. ", +" r f.= ` = = = * = * * * * * * * * * * * * * * = = = = = = = = = = = = = * = * * * * * * * * * * * * * = * = = = ` = * g.+. ].^./././././././././././././.h./././.^.i.i.j.k.l. ", +" m.n.= * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ` * s g. ].^./././././././././././././././././././././.^.o.p.q. ", +" r.s.O t.= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ` * * a.Q ].^././././././././././././././././././././.^././.^.u.v.w. ", +" x.f.= ` * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ` = y.P z. ].^././././././././././././././././././././././.^././.^.A.B.C. ", +" D.|.= * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ` * s E. ].^./././././././././././././././././././././././././.^././._.u ", +" r n.O ` = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ` {. .@ z. ].^./././././././././././././././././././././././././././.^./.#.F.G. ", +" m.N = = * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ` = T H. ].^./././././.^././././././././././././././././././././././.^./.^.%.I. ", +" J.K.L.M.N.O.O.P.Q.R.S.T.U.V.W.* = * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * = X.Y. I.@.Z.`.v. +.+++@+&.%.#.^./././.^./././././././././././././././.^./.#.#+ ", +" $+%+&+*+=+-+;+>+,+>+>+'+)+!+~+{+]+M.M.^+/+* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * ' 4 (+ _+:+E <+[+_.++/././.^./././././././././././././.^./.^.].}+ ", +" |+1+2+3+'+;+,+4+5+6+4+;+7+4+,+;+>+3+=+8+{+N.9+0+a+b+= * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = c+d+ e+f+g+B.h+/.^.^././.(././././././././././.i+/.[+Q ", +" j+k+l+4+m+,+;+n+;+4+5+o+p+q+7+r+5+6+;+>+>+3+!+s+N.N.t+u+v+* = * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * = w+Y. x+y+z+A+b.^././.^./././././././././.^.B+C+ ", +" D+>+6+q+6+,+,+n+n+;+6+E+r+5+r+*+o+5+,+;+3+{+s+s+M.F+G+H+S.I+J+* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * ' ( K+ L+M+N+^./.^./././././././././.^.^.;. ", +" O+P+s+Q+;+n+>+>+,+,+,+>+,+7+R+q+6+S+r+6+>+3+!+{+{+s+N.F+Q.T+R.U+V+W+X+b+= * = = = * = = = = = = = = = = = = = = = = = = = = = = = = = = = = * = = = = = = = = = = = = = = = * = = = = = = Y+Z+ v `+ @^.^./.^././././././././.#..@ ", +" +@@@,+!+'+,+n+,+;+,+,+,+,+,+,+n+'+L.>+>+=+#@{+s+$@s+s+]+s+$@s+s+s+s+]+N.%@&@*@* = = = = = * F =@=@=@=@=@=@=@=@=@-@;@>@- * = = = = = = = = = = = = = ,@! >@>@>@>@>@>@>@>@>@>@>@! 1.= = = = * = w+d. '@)@&././././././././.^./.++!@ ", +" ~@E+{@]@>+n+n+;+,+,+,+,+,+,+,+,+,+,+>+'+^@{+s+N.N.N.N.s+{+#@~+/@-+=+{+$@{+L.'+Q+(@_@* * = = = :@<@[@}@|@|@1@2@3@4@5@6@7@8@v.9@= = = = = = = = = = = = s 0@a@b@b@b@b@b@b@b@b@b@b@b@~ c@' = = * ' Y+d. d@e@^./.^./././././.^./.f@ ", +" g@h@>+q+S+S+q+,+,+,+>+n+,+,+,+,+,+,+;+n+i@;+;+>+>+>+>+;+;+;+;+;+;+,+>+>+4+r+4+,+n+j@k@l@* = * = m@n@o@p@q@r@s@t@u@v@u@u@w@x@y@e = = = = = = = = = = = = s t I O = = = ( Z+ >.z@^./.^././././.^.A@B@ ", +" &+,+>+C@,+,+,+7+E+&+o+S+D@;+>+>+,+,+,+;+,+;+i@>+'+'+-+m+m+'+'+;+,+,+,+n+K.,+S+o+5+6+>+n+;+E@F@` = = m@G@H@]+h@]+s+N.Q.I@J@K@L@M@N@O@= = = = = = = = = = = = s t I O * = ! P@ Q@Z.#./.^./././././.R@ ", +" S@D@T@7+q+S+o+o+7+,+U@4+5+E+V@5+6+;+>+n+,+,+;+'+>+=+{+s+$@s+s+s+W@8+;+n+;+,+X@>+,+,+4+n+>+,+;+;+Y@Z@* = m@`@ #,+>+>+>+>+.#'+>+'+L.=++#@#= = = = = = = = = = = = s t I O ' ##Y. $#%./.^././././.%#&# ", +" *#R+6+S+S+r+*+*+&+*+=#=#o+5+q+4+C@r+&+E+7+-#]@n+;+>+L.~+!+L.>+;+;+,+,+>+m+;+n+,+,+,+,+X@n+,+,+,+>+]@j@;#>#* m@,#'#)#!#*+~#E+E+E+*+{#=#S@]#e = = = = = = = = = = = = s t I O 7 d+ ^#/#(#^././.^.B+_# ", +" :#,+7+r+o+&+*+S@S@S@=#<#<#S@*+*+*+o+S+6+7+S+R+C@,+>+n+]@,+6+q+7+7+7+S+o+=#&+6+,+,+,+,+,+,+,+,+n+>+;+4+7+[#}#Z@m@,#|#1#2#3#4#2#)#2#4#5#6#7#8#e = = = = = = = = = = = = s t c@( (+ 9#_.B+^./././.R@ ", +" 0#L.S+E+o+*+S@=#<#a#2#3#3#3#b#<#b#S@*+o+*+*+*+7+6+7+c#C@;+>+>+7+S@)#b#d#2#=#o+S+6+4+n+,+,+,+,+,+n+;+6+r+*+=#=#e#f#g#h#'#)#1#*+*+*+S@4#i#j#k#l#m#e = = = = = = = = = = = = s t n#o# e+p#/.^././.q#r# ", +" s#>+t#o+&+S@S@<#a#b#2#2#u#3#)#=#<#)#b#2#2#b#)#)#v#S@w#r+E+x#o+y#r+S+r+r+r+r+S+q+6+,+,+,+,+,+;+;+;+,+6+r+*+S@o+r+o+e#z#A#B#r+C#6+4+6+r+2#D#i#5#D#m#e = = = = = = = = = = = = s t k. E#u./.^./.F#G# ", +" H#I#{@S+E+J#S@=#2#2#3#3#3#K#3#u#)#)#b#2#u#3#4#4#4#4#3#u#u#2#)#2#3#2#2#2#)#b#b#2#b#b#=#=#*+*+&+o+r+p+5+S+*+*+E+7+7+r+J#*+E+L#q+6+,+q+r+&+M#d#)#3#4#N#8#e = = = = = = = = = = = = s t O#h+/.^.^.P# ", +" Q#6+t#V@S@<#b#b#a#b#3#R#u#3#4#3#R#S#R#4#T#U#V#D#W#W#D#4#U#U#4#4#D#W#W#X#D#N#7#Y#)#S@o+r+r+o+&+=#=#)#)#)#)#=#&+5+4+,+7+S+q+4+4+;+,+6+Z#1#M#*+*+)#2#2#u#8#e = = = = = = = = = = = = s t `#F#/.^. $ ", +" .$2++$&+S@b#2#b#2#3#3#R#3#R#4#4#4#4#4#N#N#l+W#@$X#X##$W#$$N#N#N#U#%$W#X#&$&$X#X#@$W#D#4#2#<#*+r+7+6+,+4+4+4+7+q+,+;+>+'+-+;+;+>+,+q+E+*+*$=$-$;$>$>$,$>$'$e = = = = = = = = = = = = s )$ !$^.^. $ ", +" ~$S+&+S@<#<#b#u#2#3#R#4#U#U#N#D#D#D#l+D#{$W#]$&$j#j#j#j#X#D#N#$$U#N#$$W#X#&$X#W#X#X#W#N#N#4#Y#b#{#*+S@E+C#4+,+,+;+;+,+6+5+5+6+4+;+4+5+*+[#o+^$/$($_$_$_$_$:$b+= = = = = = = = = = = = = <$[$}$|$|$|$}$1$ 2$++^. $ ", +" t#3$<#b#b#b#<#b#2#Y#4#U#U#D#D#$$W#W#X#X#&$j#4$4$5$4$4$6$j#X#D#N#U#%$$$i#W#W#X#&$&$X#X#W#N#U#R#7$8$9$=#S@S@=#)#)#=#*+*+S@)#)#1#r+6+,+q+0$r+S+o+a$b$<$= = = = = = = = = = = = = = = = = = * = = = = = ' c$ d$^. $ ", +" s#7+S@<#<#b#e$b#f$u#3#g$4#h$D#l+W#W#&$j#i$4$4$j$k$l$k#l$4$m$W#N#N#N#D#W#W#X#n$o$j#j#X#W#W#N#4#S#2#)#)#=#=#)#)#b#p$d#)#1#S@*+E+S+q+4+4+6+5+5+q+0$*+q$r$s$s * = = = = = = = = = = = = = = = = = = = * 0 h.t$ u$$. $ ", +" H#t#S@<#I#S@I#<#b#u#3#R#7#7#h$l+v$X#&$&$4$4$w$x$y$k$z$A$l$i$X#l+D#D#D#X#X#X#B$B$C$B$j#X#W#W#$$4#3#)#v#)#)#)#2#u#3#b#1#<#1#=#o+7+4+,+;+,+;+>+6+6+6+4+p+D$E$|.= t.= = = = = = = = = = = = = = = = = * o F$G$H$I$J$K$ y L$ ", +" M$N$w#O$<#v#<#I#P$S@<#u#U#D#$$N#l+#$#$B$Q$&$j#R$w$y$k$S$A$l$B$X#X#X#X#&$&$j#R$j#R$4$j#Q$X#X#W#$$4#Y#b#S@S@T$b#b#)#=#=#<#&+r+r+o+w#q+K.X@>+;+'+#@L.'+m+m+>+q+U$Y+= * t.= = = = = = = = = = = = = = = J (.V$p@R.I@W$X$Y$ [.Z$L$ ", +" `$ %.%+%@%w#+$I#S@&+&+&+b#U#l+D#D#l+#%#$#$C$j#Q$j#4$5$$%%%A$l$4$&$&$B$j#5$4$y$5$5$j$4$k$j#&$W#&%N#3#u#b#S@&+=#b#)#<#S@*+o+S+S+7+4+,+]@*%>+>+>+n+;+=+3+'+m+~+{+=%-%;%s * = = = = = = = = = = = = = * * >%,%'%)%I$H$K@!%~%{%]%^% /%(%_% ", +" :%n+<%[%+%N$.%.%<%+$O$&+P$<#b#U#l+X#X#l+W#l+l+}%j#j#4$|%1%2%3%A$l$k#k#k#l$4%k#z$l$l$5$5$4$4$o$W#$$R#T$<#<#5%E+&+S@S@&+E+E+S+7+6+6+D@,+>+*%>+,+,+4+7+r+R+>+;+;+'+6%7%8%s$= t.= = = = = = = = = = = * s h.9%F+T+T+T.W$0%X$M@a%b%c%d%c%e%e%f%c%{%g%h%!%I@I@T+Q.R.i%K@7%2+j% k%l%m% ", +" Y$N$@%<%n%Y$o%p%q%r%{@s%I#t%<#u#U#l+W#l+D#N#U#N#X#j#l$u%v%w%u%2%x%y%A$A$z$5$z%4$l$|%4$A%R$4$&$W#U#u#<#w#S++$E+E+S+E+S+7+7+7+6+,+4+,+,+D@6+w#&+S@S@=#)#)#*+S+6+q+q+;+B%C%Y+' * t.= = = = = = = = = * /.D%H@s+N.F+Q.R.H$K@E%F%G%G%F%L@K@H%I$R.F+]+!+L.>+>+>+'+4+]@>+m+3+N.I%N$H#J% /%O@,@K% ", +" L%M%o%N%<%n%B%o%O%P%P%Q%@%O$O$O$P$t%b#R%R#N#U#4#U#l+j#l$y%S%T%U%U%V%V%y%1%A$z$z$z$z$z$z$k#l$j#X#D#4#b#S@w#2+2+{@W%{@+%+%+%.%2+6+,+,+,+6+C#r+w#&+=#)#2#1#=#)#)#o+0$5+7+q+X%Y%|.Z%* = = = = = = = * J c$`% &L.!+.&M.N.+&F+Q.Q.Q.Q.Q.Q.G+N.h@!+'+D@S+o+5+*+K#u#2#=#)#<#*+o+E+S+,+L.=+{@@& #&s$' b+K% ", +" $&%&&&P%q%n+*&B%O%O%=&Q%-&W%O$9$O$D$*$3$+$s%T$R#U#&%Q$l$w%S%;&V%V%>&;&,&'&)&1%z$1%1%!&u%l$4$X#$$4#3#<#E++%~&,+2+.%.%K..%2+K.U@>+2+6+6+S+E+o+*+)#3#4#3#3#3#2#=#*+*+o+r+6+5+5+{&>@= t.= = = = = * s /.]&,+'+-+!+{+{+~+!+#@^&{+!+/&8+!+-+q+r+S@)#(&i#X#X#i#(&{$B$_&W#&%:&3#p$S@&+E+7+6+>+h@<& [&}&' = * |& ", +" 1&2&&&3&2&2&&&&&4&O%5&&&O%p%B%@%3$O$P$*$<%n%+%*$T$R%$$C$5$v%6&7&8&9&7&V%0&y%a&b&y%c&d&z$k#4$}%$$U#R#<#&+@%n+*%K.n+e&>+B%*%>+>+*%>+.%+%7+E+*+<#)#)#b#2#3#3#2#d#)#)#=#*+S+q+q+5+f&U$- O * = = = = 0 F$E@ #>+n+;+,+K.K.;+>+L.=+L.>+;+7+*+K#(&D#_&i$i$4$j$k#2%b&g&'&h&i&_&X#X#D#j&2#=#&+E+R+6+]%Q.k& l&= * = f m& ", +" 4&&&n&o&%&%&p&q&p&%&%&%&4&P%o%N$Y$*$t#D$*$r&N$N$+%I#l+m$s&w%t&T%u&7&7&v&t&t&w&b&v%5$C$j#4$m$D#4#u#v#P$w#+%r% %e&e&e&e&e&*%r%e&*%>+2+6+c#&+S@<#)#b#b#2#2#)#8$d#=#*+r+5+6+4+,+6+x&y&z&|.O t.= * ' c$A&B&;+;+;+;+>+,+6+7+6+,+,+5+E+o+o+=#3#%$X#j$u%b&C&T%D&E&F&1%2%'&;&8&C&G&&$_&4#2#b#=#E+E+7+,+7%>+D$ H&O@= = = I&J& ", +" K&L&M&&&4&=&N&N&q&q&N&O&N&P&q&2&Q&P%Q%-&n%n%*$*$+%n%r&Y#d&z$y$1%t&w&R&T%0&)&1%S&z$l$l$j#T&C$W#D+b#O$&+t#{@K.L.U&q%/&/&7%V&e&B%e&*%n+D@2+t#W&<#<#=#1#u#X&2#)#<#J#&+E+S+6+4+4+4+;+K.,+'+Y&<$= = O F$Z&`&4+q+5+7+7+7+q+4+r+*+*+o+o+<#4# *k#.*v%b&+*@*#*8&$*%*9&D&+*g&2%&$u%&***|%v$j#D#2#=#)#&+o+S+,+K.!+=*-*;* i.= = * * O >* ", +" ,*'*)*!*p&~*p&p&{*q&p&%&]*]*p&!*p&%&2&&&P%p%r%K.n%@%3$O$D+^*)&/*1%!&1%(*w%_*l$l$5$l#C$C$l+$$%+R#S@W%n%+%K.p%:* %<*7%[*/&<*U& %e&n+~&6+7+S+E+*+S@=#)#=#=#=#=#=#=#)#<#)#!#}*o+o+y#q+4+4+|*1*2** c$3*4*4+r+*+)#2#2#3#2#M#*+*+=#b#4#D#5*1%C&D&**6*7*%*8*9*0*a*0*b*c*d*e*6*g&|%$%g&.*f*&$W#T#)#=#&+g*S+6+>+L.8+N.h*L.t%i*k&j*k&k*l*M$J$W%i*k*m*k&n*o*p* 8 * = * = ` q*r* ", +" s*t*u*v*w*x*y*u*z*A*B*q&p&p&p&%&{*p&p&2&%&&&P%p%C*n%*$D$S#D*l$5$)&2%y$A%A%y$5$d&A%C$$$U#$$u#T$<#&+W%N$p%*%:*7%7%:*:*]%/&E* %U&:*L..%2++%@%S+E+&+&+*+S@o+*+=#=#S@o+5+q+F*S+o+*+=#E+q+,+5+G*{&)$H*I*;+6+o+=#=#1#2#N#$$N#h$N#W#j#$%g&D&8&J*c*0*K*b*L*a*M*M*a*N*O*a*M*M*N*K*P*&*'&5$i$X#v$i#(&)#*+*+S+S+7+>+m+!+/&~+{+$@s+$@#@!+3+!+!+L.'+>+m+!+s+s+s+{+L.@@ Q*R*0 = * = = = S*Q@ ", +" T*A*z*x*U*V*W*W*X*X*Y*Z*y*`*A*!*N&O&]*o&2&~*2&&&Q%N$n%N$ =U#_*c&.=j#1%d&C$$$#$&$X#$$R#b#u#b#O$w#+%{@K. %/&/&=&=&^@^@/&:*:*7%:*e&V& %.%2+2+{@C#S+S+E+~#=#&+p+S+E+S@=#3$7+,+;+4+6+6+4+,+7+5+7++=@=5+r+r+*+e$5#&$j#j#4$|%k##=1%g&T%$=%=%*&=L**===-=-=;=>=-=M*b*K*a*M*0*b*$*%=#*w&g&2%%%&${$@$N#p$<#<#r+E+S+T@K. %!+=+=+!+~+!+'+4+q+o+,=7+V@e#o+r+6+-#>+-+h@'=J$)= !=X.' * = = = = ' ~= ", +" `*y*x*V*X*V*X*{=]=^=/=(=_=:=y*=%P&N&p&p&<=~*%&2&2&Q%-&B%[=Y$*$S#R%H#T$R%D+D+U#l+&%R#b#<#S@+$t#w#2+p%p%:*O%/&M%]%^@^&]%=&O%/&7% %:*e&*%n+,+,+6+7+7+S+5+7+R+o+*+o+7+4+7+5+6+X@>+>+;+;+4+7+5+q+}=|=5+5+o+*+=#X&i$g&b&.*1=2%g&w&C&V%**6*2=M*-=-=-=3=3=-=*=4=L*K*d*0*0*7*5=#*b&g&v%#=5$k$$%&$%$i#{$4#2#)#=#&+o+S+6=6+>+L.L.-+>+>+,+r+=#2#3#j&4#T#3#2#{#o+5+q+4+'+{+{@k*7= Q@..8=' * = = = = * O@9= ", +"0=^=:=^=a=b=^=^=c=d=e=_=f=g=h=i=y*'*'*N&N&]*~*j=2&&&O%Y$<%k=*$D$3$O$M$l=S@S@s%t%9$9$I#<#t%+$W%n%n+N$:*O%O%=&=&^@J$M%M%=&^@=&=&/&:*:*:* % %*%,+K.6+6+q+q+7+6+4+q+q+C@2+;+>+L.-+>+;+>+n+;+,+C@q+4+T@q+q+7+5+o+J#!#X#E&T%x%A$$%l$A$w&$=8*0*a*L**=>=m=m=*=&=&=a*d*d*0*2=n=o=x%E&E&D&&*E&i&k$|%j$D#W#D#4#2#2#2#*+o+o+o+C#7+6+7+q+S+=#v#%$p=W#]$i$ *&$B$_&_&X#q==#M#*+4+'+'+r=s=h+0.* = * = = = = = = t= ", +"u=v=/=w=e=x=y=x=y=z=A=B=z=C=D=E=F=y*G='*H=I=N&<=J=4&P%K=D$s%P$P$&+3$3$D$s%O$M$D$*$<%@%D$3$*$2+*%B%p%=&=&^@J$^%^%J$J$M%J$M%^@{+M%]%]%^@/&=+:**%*%>+,+K.;+,+>+>+>+L.=+=+L.=+!+7%e&'+,+n+>+4+>+,+4+x&6+q+6+q+7+r+y#o+3#@$v$X#L=5$2%T%9&M=0*N=-=L*O=P=*=*=Q=b*M*K*%*%*J*R=8&8&&*C&A$k#2%u%k$j#k$|%i#X#D#D#3#3#3#<#&+*+5%V@&+*+S=<#2#{$T=U=A%5*$%u%u%z$A$v%E&V=4$%$W=X=Y=-$Z=`== = t.= = = = = * = .z+ ", +" -w=.-C=y=z=+-A=A=B=A=A=A=_=/=i=@-X*#-G=q&I=$-]*p&%&%&N&%-n%*$3$s%&-<%n%<%D$3$*-<%n%N$C*.%N$B%<*:* %/&M%3&=-----------^%J$J$J$=&M%J$;-J$]%:*/& %*%*%>+L.w@L. %L.=+!+!+!+!+{+~+L.'+n+;+;+C@r+S+q+q+7+r+S+5+r+r+o+r+r+o+=#4#&$l$>-D&8&,-0*'-m=m=*=a*M*N*0*0*c*7*M=%*)-**!-~-8&**;&V%0&0&b&x%$%|% *v$X#D#W#4#3#N#2#*+S@)#S@S@)#3#{-]-%.^-y%E&/-$=V%#*/-,&x%g&(-_-:-<-[-}-@#O = * = = = = = = = ' |- ", +"1-2-3-B=+-z=A=A=z=A=3-4-f=_=C=C=5-@-Z*G=G=q&]*q&'*)*)*)*!*p&4&%-n%6-N%o%-&r&w#+%.%N$N$n+B%e&p%[*&&=&^@^%=-=-^%^%=---7-----7-----h*;-J$h*^&!+=&~+L.L.L.L.L.8-=+8-L.!+=+)+=+!+ %'+>+,+4+7+r+J#=#*+)#<#)#)#p$=#=#*+=#E+*+:&D#5*F&$=%=%=J*a*-===*=O=b*0*9-0-a-2=$*8*J*J*b-6*&*E&D&8&>&D&y%A$|%$%5*V=l$v$ *D#D#N#2#N#3#=#=#)#<#b#4#c-d-@#e-f-g-%=%=8&;&7&h-i-j-k-l-=.0.0 = = = = = = = = = = t.= m-n- ", +"o-p-q-r-s-t-u-v-w-x-3-y-h=h=C=_=_=z-A-B-C-G=B*'*A*D-D-E-F-!*%&%&4&Q%o%O%P%U&-&n%N$B%p%*&O%=&=&=&^%J$^%=-G-H-H-7-7-7-I-I-J-H-H-K-K-H---N.;-J${+]%!+7%=+ % %L.=+m+L.=+)+>+;+]@D@C@7+S+*+2#L-5#W=@$j#M-@$D#N#N#3#4#4#@$D#{$ *5$2%&*d**=N-m=O-P-Q-Q=a*R-S-M*c*T-b*c*d*J*J*,-8*6*8&$=C&x%U-#*V%T%x%j#$%$%_& *{$D#D#3#4#3#)#=#<#p$V-W-6.@#X-v%&*D&U-Y-Z-`- ;.;+;@;:@#;= t.= = = = = = = = = * = $;'. ", +"%;&;*;=;&;r-*;p--;*;q-p-;;>;y-h=_=_=(=,;';);`*G=v*v*v*!;~;~;!*p&o&4&&&&&&&M&&&{;o%p%O%p%e&=&2&2&=-G-];7---H-J-H-7-H-H-^;/;(;_;:;K-I-<;=*[;;-^@{+7%};7%=+=+=+ %L.'+>+U@U@;+6+E+)#K#|;D#@$B$ *5*i$j$j$x%0&5* *1%v$v$&$B$ *u%#*R=$*0*1;2;3;4;5;6;7;8;9;O-0;L*M*a;0*M*b;7*K*2=c;7*d;8&R=5=R=!-e;#*&*,&|%i$k#B$X#v$X#D#4#4#2#f;g;h;i;= @#j;k;l;m;n;o;p;q;r;s;0 o = * = = = = = = = = = = * s t;u;v; ", +"w;x;y;z;A;z;B;x;x;z;C;D;E;E;q-w-F;y-y-G;H;4-_=E=I;X*J;y*!;!;!;!*p&n&4&M&2&J=2&p&M%O%M%^@K;O%M%--G---J-I-J-H-H-J-H-I-I-K-L;M;M;K-N;(;K-<;O;J$^&{+7%:*<*8-=+L.*%B%>+,+7+R+r+r+*+)#N#_&j$j$$%z$E&u&D&g&B$D&g-A$5*P;j$_&g&u%n$C&Q;M*a*Q-R;S;T;U;V;W;9;4;O-X;0;1;N-*=*=*=L*a*N*0-0*Y;g-$*)-&*x%2%Z;5*g&5=0&5$C&|%i$j#j#]$D#N#`; >f = = f >.>+>^.@>,@* * * = = * = = = = = = = = = = = * = #>E$R+,+3& ", +"$>%>&>*>A;=>E;*;q-D;s-->;>q-&;->A;->p->>,>'>;;;;A=C=c=)>X*x*D-A*!*%&%&&&~*p&!>!>!*~*^%G-G-2&^%G-];];I-^;~>{>I-I-^;]>^>/>(>_>:><>N;(;[>}>O;^&J$/&=+ %L.*%>+]@,+4+C#c#S+o+2#N#|>&$$%x%T%1>$=9&,-Q;K*Q;h-i$V%$*g&4$$='&j$R=R=|%E&c;M*a*1;2>3>4>5>3;X;>=Q-*=*=X;O-6>X;X;0;m=a*N*M*0*0*c*,-8&**R=d;**g&V%9*#*D&T%7>A$u%A$i&8>9>0>= * = * ( a>q*s O = * = = = = = = = = = = = = = = * = = b>c><#S@t#.%d>e> ", +"f>g>B;h>i>j>k>i>l>l>i>m>x;->A;n>o>B;p>x;->D;-;-;q>z=r>s>s>c=W*z*A*'*]*o&~*j=2&%&2&=-~*G-2&G-G-7-H-H-^;K-^;$&K-I-K-^>(>:>t>u>u>t>N;(;K-}>N.v>]%7%*%*%>+K.,+6+C#r+R+o+)#j&D#&$5$.*T%8&%=w>7*b*0*M*M*M*0*d;4$#*K*#*4$x>y>5*n=M=k#A$c;M*M*-=4;8;2;a*N*a*a*M*a*z>2;6>6>6>A>A>B>*=N=M*0*a*c*P*%*0*a*L*%*b-0*$*$=C>**;&t&D>E>r$-@= * = = = = = F>` = = = = = = = = = = = = = = = = * = 6.G>H>C$4#u#<#S+K./&I> ", +" J>K>L>M>N>N>N>N>O>P>Q>P>i>m>h>R>S>&>g>h>&>B;z;n>E;B=C=v=^=F=T>U>A*'*]*%&j=~*=-^%j=!*~;];F-];V>^;J-K-^;W>X>^;Y>M;^>:>u>Z>`>g% ,<>L;~>O;;-]%7% %K.,+,+7+.,<%5+&+1#2#R#+,_&$%g&&*R=7*K*c*M*O*@,@,a;0*M*a;%=i$**K*1>_&#*D&n$8&M=%%F&P*g-)-2=M*0*Q;P*J*d*#,L*1;P-9;U;$,A>6>6>A>O-m=*=Q-%,&,W;5;7;W;T;O=-=*,-=K*a*N*=,-,;,0.= * = = = = = = = = = = = = = = = = = = = = = = * = = ,@>,,,D&A%',N#g$T$W%K.J$), ", +" L>!,~,O>{,],A;z;^,m>P>/,(,_,:,N><,[,},|,<,|,h>B;*>&;'>r>1,Y*U>y*`*'*q&2,I=%&~*!*3,!*G-D-4,4,^;5,^;W>6,Y>W>W>^>M;u>u>`> ,g%g%u>_>(;<;J$J$7%:**%,+2+{@7+S+E+O$=#b#T#@$k#w&#*8&J*K*a*'-L*L*a*O*&=R-7,a;8,M*;&j##*,-V%4$w&T%i$D&%=k#%%b&2%u%b&V%8&%=)-P*K*L*2;9;T;W;T;T;9;4;4;P-*,O-P-U;9,0,a,b,c,d,e,f,g,*,h,h,R-i,j,k,= * = = = = = = = = = = = = = = = = = = = = = * = O i;2.l,m,8&n,_*l$o,T&R#<#E+,+N$ ", +" p,q,r,M>s,h>B;x;A;t,&>u,v,q,q,q,_,w,x,x,N>i>s,<,&>y,&;z,z=A,Y*A*'*q&q&Q&B,B,N&!*F-C,G-4,4,4,^;$&W>Y>Y>^>W>^>:>:>u>D, ,E,E,D,F,_>}>h*M%/&]%e&n+,++%S+E+&+b#3#D#X#5*u%T%$=R=P*M**=0;X;h,%,-=M*a*R-8,M*a;M*0*G,j#%%H,R=l$5*x%5*w$V=$%j$u%u%I,$%'&J,J*2=M*M*g,7;5;T;T;K,2>K,L,T;M,N,O,P,Q,R,S,T,U,O,V,W,X,O=W;i,Y,Z,`,= = = = = = = = = = = = = = = = = = = = = = = * = = '.'+'@'X;#'&*$'y$v&/*D+R%P$O$ %%' ", +" &'*'!,M>j>='x,x,='M>-';'q,>',''')')')'*'v,J>K><,[,g>->&;!'H;_=@-`*q&q&T*~'I=B,4&p&!*G-4,{'V>W>Y>Y>]'M;^>M;M;^'/'D,('_':':'E,h%N;[;=&^@:*e&*%,++%7+E+<#2#U#D#j#2%#*)-<'['=,h,X;*=*=m=8;}'U;-=M*O*0*b*,-8*7*d*M=A$&$b&,&5$X#j$ *X#&$i$i$%%.*g&D&/-**P*|'*,1'7;2'3'4'4'5'6'7'8'9'0'a'b'c'd'e'f'g'S,h'i'@'m=}'j'k'l's * = = = = = = = = = = = = = = = = = = * = = * 0 m'n'o'p'S;2'q'd;#'9&y%)&U%#$R#9$s%O$H-r' ", +" s't'u')')'*'q,v'v'w'x'y'z')'''A'u'u'u'B'z'z'_,='C'L>D'->E;E'F;y-v=F'A*I=T*Q&N&N&N&p&)*!;{']>G'Y>^>H'I'M;M;M;^'J'K'L'M'M'N'D,u>X>h*O'/&:*K..%6+7+E+S@3#$$W#j#'&D>P'N*m=Q'R'8;7;R'4;3;0;W;2'A>P=M*0*7***b&A$E&>&5=E&j$5$j$v$W#@$]$_&j#5*k$'&$=8*6*!-7&K*S'3'h'7'T'U'T,0,V'W'a,a'X'Y'Z'`' ).)+)@)#)6'$)%)4;5'&)*)!.O * = = = = = = = = = = = = = = = = = = ' s =)-);)>),)')))!)j'2>~)n,{)])/*1%A%U#%+I#M$p%2& ", +" q,t'A'^)/)()_)x':)v,u'()<)[)})<)[)<)})u'z'z'^)|)v,:,<,1)2)f=z=,;A,3)y*H=T*I=I=N&p&{*~;4)]>Y>5)6)7)M;H'8)(>u>('9)0)a)b)9) ,(>K-h*=&O%:*K..%7+S+S@b#U#X#k$2%D&d;d*L*c)9;7;X,7;X,V;5;$,>=X,d)6>e)0*7*~-$=V%A$ *|%%%%%k#f)A$B$W#n$_&j#i$4$5*|%g&n=g-Q;a*-=*,g)h)i)i)j)k)l)g'm)n)o)p)q)r)s)t)u)v)c,w)x)y)z)A)K,B)C)t;O J = = = = = = = = = = = = = = = O o D)E)F)G)H)A>I)J)}'K)L)M,f,d*9&M)&*d&z%l+k+k&P$N)M%O) ", +" P)Q)[)q,Q>v'w'^)R)Q>z'<)t'})<)S)t'T)Q)Q)[)/)u'U)z'*'|)O>%>-;V)D=@-B-W)y*B*X)I=o&{*~;4)Y)5,Z)Z)I'5)H'`)^'/' ,N' !.!+!@!:'Z>L;I-H-^&O%O%#!2+W%&+<#R#v$5$'&C&!-d;Q;|'3;P-9;7;W;K,2>}'$!4;X;8;9;Q-0*J*%=M=c;M=#*'&A$k#|%i$k$l$$%i$_&&$4$g&,&C&u%u%5=*=T;K)2'3'%!&!a'l)*!=!-!;!>!,!'!)!!!~!{!]!^!/!(!V'_!e,K,U;O={):!# = = * = = = = = * = = = = 6.k>z;e!z-f!W)y*g!B*H=N&p&A*v*z*h!Z)i!5)I'H'j!8)k!l!9)b)m!n!0) ,:>^;];7---M%:*n++%S++$<#D#5$v%0&/-~-o!*=P-f,f,p!K,K,5;2>2>2'}'q!2;6>0;M*)-%=,-r!%=#*V=#=v%b&b&'&z$k$ * *u%.*j#k#R=,-%=%=z>K)Q,s!W't!u!v!(!l)w!^!]!x!y!z!~!)!~!A!B!C!D!E!c,!)U;F!M)G!g,H!I!J!s = = = = = O q*0 K!L!M!N!O!P!Q!R!S!C>T!d;5!K,U;~)U!S,K)b*~)p!V!W!U%X!d&m$#%D+g@s%6-%&Y!", +" Z!`! ~[)}).~B'B'B','y'_)<)c!+~@~Q)a!Q)#~Q)#~Q)Q)#~()A'q,$~N>%~&;*>2)3)&~g!g!g!*~q&'*A*h!x*Z)I'I'(>H'8)=~-~(';~.!>~,~'~^'N;^;];--^%^%^@n+{@E+O$~$l+'&$'>&d;%*-=X;f,)~2>e,K)2'!~~~~~5;K,R;2;N-N-P=K*,-P*d;D&x%E&v%j$j$|%k$i&E&T%A$X#v%8&'&_&**K*%*8&a*h,7;5;{~i)]~%)a'^~%)/~(~_~:~~!<~[~}~|~1~$)2~3~p!d;4~5~f,p!6~7~8~9~= * = = .0~a~b~c~d~e~f~g~h~j'i~j~#'O=d;V!L,k~}'K)2'j'U;Q=4!l~h,6&5$d&A%i*$$T$O$k=%-q&", +" /)<)/)A'y'B'B'B'B'y'()[)t'#~m~n~o~p~q~r~r~q~s~t~s~#~}))'Q>:,l>e!E;E;_=B-y*G=u~G=A*z*x*U*W*{=v~H'w~x~/'l!y~z~A~B~C~('D~W>^;^;J-H-=-M%O%6+E+<#l+',,&5=J*K*-=E~P-4>~~i,S;h~e,6~K,W;R;R'R'6>F~M*M*b*K*,-%=~-~-)-P*R=S%C&g&%%j#5$C&v&j#j$8&D&j#$=c;$'V%0*-=G~H~Q,t!^~I~l)e'x!J~)!K~L~M~N~O~O~P~Q~R~3~h~C>S~X,K,))e,T~U~V~W~* = * e X~Y~Z~`~ {.{5'k~+{@{n,#{P=T!#'N=4>I)L)e,K)))${}'g,%{~)&{l$m$y$A%.=g@9$*$4&*{", +" D'/)[)y')'B')'={={B'/)-{t's~#~#~;{>{`!,{'{r~r~`!s~a!Q)#~t'){^)L>!{~{w;/={{&~G=A*4)v*x*Z)5)j!]{j!^{x~/'-~/{({>~_{:{<{I'^>^;J-_;I-];M%=&B%t#~$j#0&;&[{,-*=4;X,p!M,q'S;i,!~K,R;W;A>}{N--=L*M*|{%*,-%*%*%=R=,-Q;P*5=#*b&T%~-8&C&j$z$~-U%j$5=7&|%~-J*w&R=&=-=p!1{P~u!2{]~3{4{5{z!6{K~K~7{x!J~l)&!S;0'8{o!%*k~}'j'))K)K)^~9{0{` = e a{b{c{d{e{f'I)f{[{9-g{h{i{j~g{@,h{j{a'K)7'T'k{9'm=*=a*N=w%C$^*6$R$U#t%*$4&Y)", +" l{<)u'()m{m{B'={^)n{x'()-{[)s~#~s~q~;{o{,{p{q{r{q{;{s~Q)<)u'*'v,M>D'x;s{t{@-#-#-Z*u{X*{{j!)>v{^{v{w{x{y{y~z{A{C~B{C{I'Y>W>J-^;J-];^%p%B%+%R#',y>8&,-o!O=4;T;I)}'K,2>e,H~M,T;f,$,X;D{a*c*a-c;,-,-,-Q;%*d*J*R=$=C&g&2%5* *0&)-J*1%w&J*T%%%J*;&1%)-8&&*Q;M*a*Q-3;K,i';!E!b{E{:~F{G{H{I{;!p)J{-!K{{~%!h,d;L{L)J)W;${z)x)M{N{O{b+e P{Q{R{S{g'w)T{@,U{o!V{W{~)X{M)Y{I)a,Z{W,`{v!Q, ]f,4!.]R-w%m$m$m$y$.=T$k=2&v{", +" ;'y'A'u'B'+]@]@]#]={B'()[)#~#~s~s~s~q~q{p{$]$]$]r{q{;{t'.~)'^):,o>4-f=%]y-A,U>U>B-{{{=&])>]{)>w~*]=]-];]z{>],];]']i!Z)Y>^;J-H-7-G-O%.%@%b#',)])-O*N=S~N=$,~~H~W;A>!]8;7;9;~]6>X;m=-={]M*a*M*K*Q;,-P*%*P*$=E&,&$=%=5=e;k$|%%=%=u%~-d*,&$=,-$'R=%=&*%=6*M**=3;T;h'c''!]]^]6{/](]_]:]<]b'[]}]S,5'|]f,*=O=1]*,g,g)2]k~^!3]4]5]6]7]8]9]0]a]b]c]%{n,W{g{d]e]])~)K)2{x)f]5'g]h]W,A)))W;[{7!y%)&y$8!8!8!i]j]K;k]", +" -'[)y'B','={@]l]v'={B'm]()S)#~d!s~s~p~n]q{$]o]p]$]p{q]r{r{;{A'*'''C'D;f=w;V)/={{Y*{{:=:=:={={=w~r]s]-]t]u]>]>]-]w{Z)U*U*W>^;v]M%M%=&K.S@u#m$0&d;R-~)Q=O*5~U;p!T;f,A>A>9;4;4;*,0;m=E~m=*=w]a*M*c*%*J*%*c;)-8*M=Q;b*0*0*,-b&$%)->&#*Q;$*g-$*6*c;c;x]6*w>d*9;3'R,y]/!z]A]B]C]^]D]:~G{r)E]/!F]i'S,h,O*h,3;0*d*Y{i,L)f,k{S,3{G]r*H]k{I]j'~)J]K]#'L]N=])e]g,p!q'6'd'i)6'6'M]+)2]))k~4>P'1%c&N]l$O]P]i]Q]R] =;~", +" S]-{m]B'={={v'l]T]l]={m]y'[)()U]d!#~q~r~q{$]p]V]W]W]p]q{X]Y]p{){:,_,w,*>e!f=_=(=E=:=)>)>)>:=)>Z]`] ^k].^t]+^@^#^)>V*Y>Z)G'$&_;M%B%n+N$t#R%#$w%9&%*P=~)R-R-g,X;h,*,*,h,4!m=h,0;m=m=3;G~0;Q--=$^-=-=-=O=N*Q;c*N*M*M*8,a;a*M*$=#*J*J*$*0*a*0*,-9--=a*M*c*7*%^1{U,8{i)3{@)2{&^*^=^-^:]r);^^!V'8{>^~~%*>&$*=,&*g&,-f,,^7'X;'^c')^!^~^{^3~M,k~%{j']^^^Q-M,k~j'7'6!w)/^i'K)(^_^c,2':^7!#'t&R&U%x@0#j*i]<^[^}^t]", +" [)()B'={w'l]|^l]T]l]x'1^2^U][)S)s~3^p~q~q{p]4^W]W]p]5^p]Y]Y]p{<)v,w,6^x;2)h=(=7^.-:=F=:=:=8^Z]9^ ^0^-]a^b^c^v-^={=I'i!Y>$&4,G-{;d^P$g@D+l#w%#'%=c*~)*=b]d*9-a*e^m=*=N=N=F~Q-Q-0;h,h,X;X;Q-z>m=0;2;>=D{a;R-a*a*a*a*L*Q-f^L*J*Q;0*c*0*N=3;m=m===N=*=5;R'O=-=2'g^U,h^g't!i^j^=^k^K~k^l^f'i)z)X,g,b-k# *4$5$X#|>g&h,L,6*c*W;2{m^L)L)n^N=0,w)7'j'p!}'))j{L)e,e,5'o^g'p^5'w)8]N=M)d*q^n,_*r^.=j*R]R]s^t^u^a^", +" v^()2^,'={@]l];'|^|^@]B'm]2^[)S)#~s~q~'{q{$]p]w^x^x^w^4^W]W]$]p{a!*'P>j>|,p-(=z=A=w={{^=&]c=y=<{ ^ ^k]y^w-S>B;3-Z]^{I'Z)$&J-!*=&N$s%z^#$8!1%u&;&P'N*L*R-A^C>)-d*a*-=O=Q=Q=L**=X;4;$,$,*,X;B^h,X;X;X;1;L*e)L*L*-=Q-3=0;!~W;Q=d**=>=-=*=~)S;1{1{~~M**,g)z)h,M*4;S;5'U'h^i)`{l)C^G{O~-!h^S;*=D^y>b&G&&+;+]@6+|=5+R#d;R=l$u&I)T'}'2'E^F^G^3{H^))))L)E^j'9,K)2'!)I^o^%!5'5'7'J^%{f-#'Y{t&_*k&K^Q]Q]L^M^N^{{", +" O^>{()x'={P^Q^R^@]={S^m]2^()[)S)S)@~s~'{$]p]x^T^U^U^T^x^x^V^5^p{a!*'M>s,W^X^y-h=Y^^=:=b=Z^`^y^y^ ^<{w=(=;>&> /0^*]^{./$&J-~*=&B%W%b#A%w%1%U%M)d;P'f-Y{j~+/>&9&,-M*-=L**=|'2;9;W;X,4;h,h,A>7;4;*,6>P-U;T;f,~]K,h'6'@/3;K,7;Q=N=X,i,M,@,J)%!%!9'8;m=O,#/9'K)9;K)9'Q,U'U'U'U'2{,!^!x)k{T;g-l$4#=#*+6+^&I@K@$/}>;-C@5*D#&$v&E~g,k~M,2'Q,f'n^))L)2'e,2'}'2>i,L)%/S,&/j'2]M,@,*/j~q^=/@&9!1&M^-/u^}^;/'*B*", +" -'@~m]x'={>/={l],/@]x'_)()U][)S)@~@~q~$]$]p]'/T^)/!/~/U^{/x^W]p{;{A'N>k>|,&;4-+-r>b=Z^y=]/A= ^y^9^c=c=x-@^^/t]`]D~5)$&4,2&P%r%+%3$~$y$U%U%t&n,7!7!n,$'7&>&;&{)J*N**=0;P-9;X,I)W;|]4;7;W;W;W;T;4>W;4'I^//W'c,k{^~(/3>_/_/K,*,W;g)!)L*}'S,#)7'2'!)1~h)i':/%!%!K@}/|/1/_>k$2#K.7+i#7$@$g&S-{~2/V,A)L)g)R,P-4;K{k~j'e,L)3/W,4/j~h{q^T{5/6/K^Q]n*7/j*8/`*9/c=0/", +" u'q~()()a/b/x'c/={x'2^U]U]S)#~S)@~q~'{$]d/'/x^U^e/f/g/h/U^{/V^p{`!i/j/W^&>p-k/.-s>w=]/y^l/y^y=s>c=Z]0^A{m/t]-~v{Z)n/];&&U&N$s%R#D+$$z%y>t&6&o/p/0&6&#'#'#'$=7!o!g{*,f,9;4>f,f,7;T;2>}'2>2'K){~U,u!c{q/d{i)H^0,r/#/U,_/s/}'H^R,L,c;W;S;t/H^#/)~d'f'b,V'T,u/O,8;v/6>W;*,==a*$^D{%*T%X#8$y#F*-+{+}>H%g%1/w/x/$$f,*,l$o+q+L.=*I$'=)+&$*,2]O,'^H^A>X;4!f,M,W;))3>L)2>g,y/V%)&x@#'^*Q#i]z/A/t^X)`*B/ ", +" C/q~S)2^D/2^U]2^2^U]@~'{'{'{X]'{'{q{X]X]E/'/U^F/G/H/G/I/h/J/K/,{`!L/j>%>y,q>.-w=y=A=M/M/y^s>c=E=w=M/N/O/P/ ^8^Y>{'];&&O%B%3$U#Q/1%d&g&S%R&t&C&d&_*y>y>v&#'**W{c*R/A>U;6>R/0;X;P-T;2>2'K) ]U,S/-!x!x!E]j^u!0'h^U'g-R=0&.*w%v$b#r+2+L.J$}>H$U/V/W/X/_>+%b&,^0'X,~-&$X#D#E+L.L.>+^@8&,^2]|]7;5;X;+{5!X;f,p!M,Y/])#'Z/M)J.i]`/9/B*X*@- (T*E=,; ", +" .(+('{p~S)S)@~@~n~p~@(E/E/X]X]X]E/d/'/#(U^!/$(H/G/%(&(*(=(F/K/-(`!;(|,>(,(4-w=(=B='(A=<{`]8^c=y=x-)(m/z{!(~(Z){'];=&B%{(w#9$@@d&V%V%#'t&c&_*U%w%l$1%U%v&$'%=o!L*X;h,g,L**=X;R'W;K)5'_/8{x)l)](e'q/^('!/(3{w!'^7'e,k~E^%/6~f,M)((I)e,P~_(I]I]:(*^E]h)'^<(O,U;n=$%&$j$W#e$r+,+L.=+{+F+[(L@}(|(1(`>H-7+N#d;2(w!&!N,*=T%3(j#b#3$&+&+:*n$2'0;|]9;~)U;+{h,Z/s/Z/n,Z/Y{r^4(L%5(j*6(4(7(r>b=b=A=A, ", +" 8(9(E/q{X]@(Y]E/X]'{'{d/U^#(T^#(#(#(U^)/)/H/I/0(%(&(a(b(c(F/{/d(`!>'e(f(X^B=]/0^x-0^y^y=c=`]<{P/g(h(i(C{w~Y>V>];=&N$<%P$S#A%c&C&#'v&v&y%C$m$z$w%z$c&y%;&C>,-d*-=0;|'R-Q-4;7;M,K)T'w)F]]~g';!j(k('!l(3{*!m(_/L)A)I)k~M,g,i{7&0;9,A)x) )x)d{;![~n(3{h~o(5;T;-=;& *)#o+5+;+=+{++&R.L@p(q(r(s(t>H-,+R#w%v/#/t(g'a'9'8;d;0&&$u#9$<#3$7%7+b*5>U;R-q^g,X;V{R-])7!6&Q]u(v(U%A%0#6/Q]w(G=y-M/F;o- ", +" x'4^)/x^E/p]4^T^#(x(x(y(z(I/A(I/G/G/H/A(0(0(%(B(c(%(e/C(t~e/d(t~D(E(s-r-;;F(q>a^G(H(`]9^x{I(J(K(/{C{j!Y>V>];K;N$D$<#R#m$U%>&7!>&u&6&d&U#R%7/m$L(y%u&d;J*Z/R-~)$^a*~)*,7;W;}'H^h^u!g'^!f'M(G{6{N(/~l)R{6'))I)p!m=^^M)O=L{I)}'A)h^O(P(^(O~5{Q(@)R(S;>=a;%=V=W#=#5+;+=+{+F+[(L@0)S(T(U(Z><;6+7#z$M=e,V(q/W(%)W'O,p!7&t&y$R#e$<#2+^%+%**L*M=W!f-Z/v&^^X(d&N]Y(U%Z(s#`(v*U> _._+_A=2-2) ", +" @_x(#_g/g/g/H/g/g/G/A(A(A(0(&($_%_c(&_*_T^C/C/=_ M>-_Y]''W^->;>r-u-2-u]a^ ^<{<{ ^m!J(;_;]>_7)Z)$&];P%@%t%R%k+',U%{)C>j~%=>&0&l#@@%+P]m$y$R&>&f-Z/c*L*m=V!~)m=*,p!}'#)h^u!,_o)'_H{)_!_<~~_D!T'6!{_~)W{W{d]]_M,2>}'K)K)3~<]2/^!^_k^/_<]=!T,P-b-y%j#N#*+4+3+s+Q.0%(___:_<_M'[_X>/&+$}_d*c,c'|_k^1_B]U'7'2'2_3_M)z%D+g@+$p%W><#**&*y>4_R&w%U%y%_*X(j*O)'*X*b=y=Y^h=f!h=0^*;5_ ", +" 6_q{&(c(7_8_c(8_9_0_a_b_%_c_g/d_@_ <)e_f_g_%>z;s-;>h_u]F(;] ^=].^i_j_C~k_w{j!5)$&J-[*@%u#l+4$c&S%[{j~j~C>M)y%4$l+%+#$y$o,t&t&d;c*['&{V!*,h,0;|'8;L)6'0,&!m)J~l_m_n_C^o_R~,^t!p_{)d;|'4>q_K,k~U;N=r_5'E^`{/^,!s_t_u_'!m(3/0*D&&$u#o+q+v_!+[;w_g%e%x_y_:'`><>];N$<#a&7'@)z_(]A_B_C_P~p^e,f,J*9&U%#$D_M$*${;`>|%b&j#m$)&)&w%A%6/E_F_ (G_Y=1&Z*z=k/.-x-F;H_ ", +" I_ i/J_:,W^x;y,c^K_z,2-L_t]l!z{M_M_m!k_^'D~]'$&];2&B%R#5$w%v&$'d;N_W{M)6&y%8!$$$$&$4$z%1%V%[{c*O*R-v/*,f,f,*,4;e,L)2]h^-!O_t_m_H{P_o_//6'Q_R_$^7;2>q'M,i~F!M)d]S_G~R/T_+)U_V_B_W_j^I^P=g&]$R#S@6+ %]%h*<;W$X_x/M'D,(>Y_I-=&.%~$C>Z_`_6{F{~!t_O_o^6!}'j'5!$'/*8!i]`$ :.%G-I-4$m$7/^*5/A%v(7/j*k&k*i*.:k&+:x=B=B=x-t-@: ", +" ;(#:$:%>&>c^@^%:L_I(&:*:=:-:;:>:=]8)8)D~W>=-&&B%9$5$0&;&e],-)-9&C&z$4$m$X##$C$j#A%d&6&d*g,h,g,$^s/f,f,f,4>I)K)c,x)x!,:!!':):)_!:l)7'L)2>K,2>I)5!o!{)@{U{~)@'~:2]J~b{/]{:t_W_&/l~t&#$<#S+2+e&/&^&H-[(t>(_(_`>t>]:7-M%N$O$l$Q=^:/:7{(:_:::<:_^8{j{}'p!Z/d&8!%+R%t% =C*:>W%&%#$D+C$[:0#6/6(@&%'Y=F_}:1&E=B=v-2)D; ", +" |:t~1:[,2:3:y,^/I(4:5:%:i_6:7:P//'8:Z>(>X>M%n+{@9$j#v%9&'-V!W!&*9:0:$$$$T&X#j#y$5$w%>&Q=*,f,f,a:4;f,+{b:*,T;p^0,c:B!d:~!z_e:7{A]f:g:K)I)q_f^h:#'h{U{N=])i:j:(^I]N(k:F{!!6{|~H^7*j#T$E+B%O%=&--H-K-<>t>l: ,(>^>H-O%.%t#~$v&{_I]S{m:n:o:M~p:q:r:g:k~J^7>7/#%k*n*s#l=s:C,t:S@7#g@R%R#@&R]t^G_{{r>x=r>w*z=3-p-B;u: ", +" *'>'v:w:$:x:y:+^I(I(A{;:z:A:'~B:J'C:M;I-D:+%3$T$W#2%R=-=*=$*y>m$u#<#b#D+l+4$2%U%V%J**=4;X,W;M,p!p!8;4;*,i~L)#/0'E]!!!!E:e:p)x!x!h'k~5!A^i{o!E~N=d;F:X{T;%!G:<] )H:!!:~z_c'R'T%l+W&.% %=&J$O;X>^>(>`>I:Z>^>X>7-P%n%g@U%N=j{I]J:]!K:L:M:[~N:(^O:P:Q:F:y$R:i*%+K^`$S:o%8)2+9$I#P$u#P$6(Q]t^G=(=k/A=x='(2)y,T: ", +" i>+~:,U:V:W:X:H_=:I(Y:Z:`:X:.^ <.W>]: %{@E+S@N#4$b&&*#*2%i$l+2#S@S@Y#X#l$y>5=7*N=+<7;K,e,%/T;+{f,4>W;W;j'c,&/b{t_{:!!:]@u>%<^>^;O;M%o%Y$I#&<4>*<(^=<-<;<><,<'<)*> ", +" /<(<_<_<:< /<N;H-:*7++%6+S@4#@$X#D#4#3#2#<#S@<#2#&$v%$=j~N*2;2>2'K)3j~Q=U;p!f,^^h{K,5'O,5W>M;(>:>(>M;^;--2&&&B%O$#$M)I)J]6<7<8<9<7<0_>H-=&L.K.7+E+)#3#3#3#!#&+E+o+&+<#R#&$.*T%R=M*W;H^T'z)o!X'&!L)Q=U%N#@%p%M%H-W>M;M;t<:>M;K-H-M%p%p%o%w#5$@{u.%e&B%*$E(x:MI-^&:*>+7+S+E+r+S+S+E+&+S+5+S+=#4#R<$%y>)-3=W;,^,^A)}'W;h,m=0;h,M,5'W,h'V'=!f'>!f'<~S<0'3<5~E~4;&{%=g,7!L{3~k{S,h'a,(~g'T<_~f'/(sH'(>:>`)^>^;--=&N$<%<%g@*/V.-x=B=$[&>{, ", +" <,''d(%[L<&[Y:*[=[-[;[m!a)9)D,Q<_><;h*/&L.,+7+S+E+7+6+4+q+S+*+=#)#3#D#B$2%b-L*m=0;3;*,X;V!|'-=|'m=h,>[e,_/V'%).{l)h)/!c'I]4',[7!N*N=%*N*N*q'5'e,%/3<0'.{=!'[6{_]7{p)h^h,$'W#3$U&G-^;Y>(>(>t7-M%{;N$D$z^d&U{)[Xm> ", +" [[+~}[){|[1[&[2[3[4[5[mY_<;h*/&K.6+5+5+r+S@*+o+5+7+r+=#2#N#W#4$'&#*6*Q;Q;Q;a*-=-=L*R-a*|'0;U;H~6'Q,t![]b'6[T;7;9;0;f^u&$'W{-=P=c*e,2>V!5'Q,5(>D~8)(>W>2&O%C*<%9$6$=/9[0[a[b[c[d[e[([f[g[ [h[Z]B=B=A=A=A=f=A=f=i[>;3-A=.-'*9^j[^;=&=&J-l/x=;;gN;<;h*e&,+C@r+&+o+r+o+=#)#)#)#2#K#N#v$|%'&b&,&8&J*%*['N*N=Q-O*a**=*,f,)~h~T'q[R-9&M*>&U%;&Q;6>,-T;f,E~,^^>(>:>D~^>J-2&P%p%*$7/R&T{v[w[x[c[c[y[z[A[B[C[D[U>f=E[A=A=A=A=A=f=>;k/F[G[B=A=H[>_l!8)B%-&V>0^f=q-X^I[o- ", +" o>J[d(K[i(;O;=&>+7+E+o+o+<#)#=#*+*+M#2#4#{$B$j$'&,&$=>&{)%=r!M*O*M*-=R-b**=U;W;i,5'H^w)Q,P~M]u!%!3;v&t&$'t&;&9&0;Y{m=N=p!>^^>H'H'./V>G-Q%Q%4&-&k&:[S[T[U[x[A[V[W[X[Y[H)Y[Z['(4-A=A=A=A=A=k/f=>;F[k/k/B=H;b=W*`]l!G_dW:`[ ", +" #: }.}|[+}@}#}=[`:$}+!m<%}C:X>^&*%6+t#S@{#)#)#d#2#3#3#3#3#4#|>5*V=E&E&#*n=)-9&)-0*Q-m=3=L*D{U;K)]^g^U,U'i)&!&}%)*}n)Z_%*v%w%U%u&{)P=Z/V!d*h,,^7'6'x) )<]*^=}x!%)6!Z/l$I#B%&&];V>Y>Z)Y>5,];~*P%N$-};}>},}'})}!}~}{}]}^}/}(}_}:}s>4-%]A=f=A=A=f=A=>;k/k/k/k/>;B=x=T><}`]G-4(p&k]t-*>[} ", +" h<}}.}|}1}2}3}4}l<5[5}__8:/>-- %.%w#&+<#2#u#R#4#N#N#N#N#i#]$ *$%'&T%>&)-)-R=~-g-M*Q-P-A>8;2>U!i)W'b,S_t!6}]~%)](x! )l)h,u&w%)&y%n,Q=W!2;O*g,i~U;R,i)%)_(7}8}E]/(^:Q=)&~$K.&&];4)5,5,4)!;j=!*P%N$s%.:9}0}a}b}c}d}e}f}g}h}i}j}k}B=3->;f=A=A=A=M/f=G[F[k/k/k/G[B=A={{)>Z]D-l}q&M/A;m} ", +" W^n}o}p}q}r}M[s}t}u}9)F,K-/&e&7+&+)#u#7#N#D#D#D#W#X#X#j#A$C&#*D&D&~-2=*=0;m=Q-Q-1;7;N,T'i'`{1~v}M]e{w}g']!q/x}O~d:m())J*y%U%y%v&*=P'$^O*N=0;:^R,U's!*}K:p<:]M:U'$^C&z^n%=&G-4,4)4)'*N&Q&]*2&P%6-y}z}A}B}A}C}^}f}e}d}X[e}D}c=4-F[k/f=A=A=M/f=G[k/k/k/k/k/G[B=A=E}w=z*z*x* (t-w,F} ", +" G}H}I}J}K}r}L}M}$}___'_>H-:**%S+<#3#4#D#D#W##$X#&$i$l$v%**%*b*$*$*Q=Q-B>M,g^s!U'6'S;g^2/D!N}c'c';!,!d{n)O}P}V_Q}R}S}0]N=U%#'S%@*R-b*o!*=O*N=J)V,0,8{h^T}m_t_U}e,j~g&&$P$p%2&C,C,C,O&Q&N&)*D-~;V}W}X}Y}Z}`}~@ |X[j+.|+|@|._k/G[k/F[f=A=f=f=k/F[k/k/k/B=k/k/3-k/c=f=y*Y*T>W*[,#| ", +" d($|%|&|3}*|`:u}:'`>W>=|p%.%S+=#3#W#X#X#X#X#o$4$u%C&R=2=V!h,A>i,3|Q}m_,|o^^!'|P'Z/O=)|{)W{g{9&g,4;V!3'k{c,5''^!|~|{|&/U;**5$W#u#<%P%!*C,]*B,q&A*x*]|{='*E_^|/|(|/|_|:|.|<|+|[|}|B-3-k/k/F[i[f=M/>;F[k/k/k/k/k/B=B=G[B=x=3-c=s>:=x-~{ ", +" K<||K}1|3}2|3|0)D,(>H-M%p%{@+$I#3#7/C$j#j#i$l$u%$'$*O=m=T;L)0,4|u!&/4|R[](g'c'e'j(E!N~A]J~=}o_G{5|+),!':t_u[6|7|I]3/p!U;h{#'P=^^9-T!f-*,p!K)7'1{%!W'](8|>!9'g,>&o,6$$$&+r%%&I=]*)*x*9|c=r>w=:=0|a|b|c|c|d|e|f|g|h|i|d|Y^3-k/k/F[i[k/f=>;F[k/B=k/k/B=j|B=G[k/B=h_h=Y^y-c^ ", +" k|l|m|1|3}n|o|B::>^>--O%e&+%P$2#U#D#m$5$z$1%C&>&%*L*4!5;R,/!p|^(Z'%)S/q|#;F[k/B=k/B=B=j|B=B=0^>;3-gD| ", +" *>E|F|2}#}G|L'/'^>I-^%{;N$@%P$u#N##$j#l$w%y>{)Y{d*L*3;,^l):('!M(n(z__~:(f'n(G{(](]'['[^_::::H|^_d:!!(:':I|J|K|p:t|q_5'S{T!Z/~:7;P'e]4!4;$^${2);>c^ ", +" R|S|T|U|L}o|K':>I-h*=&e&.%W%S@z^l+C$5$z$v%>&c;M*R-|'2>x)#~C=x=y=w=D=W)$1x|%1L|&1M|*1=1-1(=3-k/k/k/k/k/k/F[k/B=B=j|B=B=B=B=B=j|3-C;r-*>E(;1 ", +" >1,1'1)1P[l:^>J-^%V&B%+%D$S@D+X#y$1%w%y%#'o!N*P=7;!1~1(~o_(]{1(:t_]1K~M~M(6{^1!!H{~!H{t_o:B_]1H{/1(1K:/:_1:1<1[1M,}1H^|1])Z/L)a,K,N=U;H^U,Q,V'W'w!`'w!-!*}*!}]w)T!!&11)|O]s#21)*V*31Z^y=r>A-A-B-415161M|z|y|7181914-3-k/k/k/k/F[F[B=B=j|j|B=B=B=B=B='(E[s-y,W:%>>> ", +" 01a1b1c1B::>W>;-M%{;.%w#P$<#u##$5$v%v&$=9&J*9-d1e1o)E]j^f1)!:~~!t_t_A_L~H{E:!!K~~!~!(:A_::m_(:':g1h1i1j1;F=f!m1n1o1p1p1q1r1s10|'(B=k/B=k/k/B=k/B=j|B=B=B=B=B=B=B=B=p-H_W:m>A' ", +" t1&|r(@!8:^>^;u1U&r%B%S+I#b#R%U#m$1%;&)-,-R-~)}'v1t)j^j^w1;^K~A_(:m_t_^_!!K~^_!!!!(:m_(:F{:~~!x1y1;s>^=F=^=L1M1=1N1O1s181P1q1&~'(B=B=k/B=k/k/B=j|B=B=B=B=B=B=B='('(>(I[='R) ", +" |}Q1R1B:(>W>];G-=&N$7+3$t%b#U#U#l+l$$'Z/b]-=I)e13{B!>!m(S1:~!!::::o:o:::~!::d:!!K~!!::t_T1^]s)O_O_p)p:U1m:V1a[W1X1r:Y10,J]&/7',^R[j^I^t)Z1_/&2b)*2(;7-3&2&[*B%@%P$9$S#g$$$$$.=7>o!V!5!L)x)l)]!d{O}=2t_K~::B_o:o:o:t_o:::H{K~K~E:(:*^l(q:s)*^J|<~-2;2>2~1,2N:Y1&/'2)28]`{%!t!#J-^%M%B%@%{@+%*$s%H#g@U#c&y%A%C$9-w)v2I]F]V(Y'>|H{K~:~:~(:o:m_o:m_o:o:A_p|E2F2Y'V(/^_^0'p)h^j:]!&/3~W'-2_(G2_](:s_~!(:!!!!Q[V1H2I20#o*)=Q#6/Z[J2K28/L2M2#2p1%2N2O2P2h<%]B=4-B=j|B=B=B=B=B=B=4-|24-3-&>Y]F/Q2 ", +" R2S2y{5)^;];2&&&Q%K=<%s%P$P$P$M$b##%/*_*y$i{j'n^I]i^;2~1M:u__]:]6{H{m_t_m_::_:m_:~r);|O~E]c2T2_^b2B<;2U2>2~1V2g1y3O2,3'3~{3-j|B=B=B=B=B=B=4-B=4-3-w-N>)3,{O^ ", +" m!,~=~W>];G-^%&&B%n%w#s%9$9$!3t%H#D+z$v&U%[{~3{3*|Y'L]X1d2/3t|(3f{_3:3<3W2N:[3q:}3f1V2B!s|Q_M:|3U{(^r:L{!1I]13K|G2236{H{::~!~!(:`1~!l_334353=/6373839303a3Q|b3c3%2%2o-d3o-t{e3f3F;B=4-B=B=B=4-0^j|4-'(g3u'h3i3 ", +" 7:j37)V>];G-&&4&:*+%P$S#U#C$C$U#~$R%L(7!@{F!&{R/j{S{S}}]W2k3.3u_l3l_]1::H|m3A_n3o3y1p3/3q3Y2U2r3_[s3t3u3v3<1w3x3y3>>Q3R32-4-B=3-j|B=B=G;F(*;w,V^J_S3 ", +" L_i_=~G'4,G-2&&&P%P%O%p%{@I#l+4%j#z^R%c&P'T3R/J^@1U3B4P_/]H{H{!!H{k^u_C^,4'4d])4!4~4{4]4Q|61^4/4$2(4_4|:;>:4<4K_q>3-B=B=B=3-E;$:r~x^L> ", +" a^m!j!^;C,2&2&&&=&P%p%Q%[4%-S:z^@@g@f$A%9&@,~3/3i~6<}4V2|414G:24;x-!'I41[+~p]4^J4 ", +" K49^Y)C,L4%&M%&&P%Q%N$r& =;};}D$M$t%i]d&i{h{M4@{j4w[w[N4O414'<;2P4Q4R4S4I|I|0<64Y1}1~:94~3U2V1C3T4V2j4U4V1<1x;5>5x,k>M>''r~x^W]E/ ", +" ;[{=A*p&!*2&2&&&&&P%C**$s%s%s%P$H#9$S#,5'5h{f{U2)5!5r3~5{5]5,2,2z1u3x}^5/5V1-4(5_5{_:5<5C3a,f{R]+[[5%3a2}5|5#5{5><1525f~35}545C155R]@[@47(657585A27<-4j{v5w5x5y5z5T*A5B5R]w[C5A.6+6@6v,u'a!r{p]g/#6 ", +" $6]/y*{*Q&L&N&q&<=4&Q%4&[4%6k=s%&6s#k*%+*6/2e]M4((q3=6b2-6][-6<30[;6s5)<>6,6'6C1u4x3l26/)6X<)*!6~6u({6t5]6^6/6a5(6f~b4_6:6<6Z[[6}6|6u5s3162636Y=46A}5666!}768696B[06<3^[a6/}b6c6e}d6:|e6f6A|n1g6h6C=M3i6->j6k6&>i>B'@~3^3^$]5^Q)l6 ", +" 0|NA*I=B,n&]*q&]*o&N&n&o&4&Q%V}9!>}k+k*0#63^^f{m6n6a2J505o6r5p6q6r6B,<6x[)*s6t6M5/2{5S[o5u6:6x{*T*X)P6]*]*{*{*4&n&N&%&4&%--&dV6-/K5&7`(r'*7=78596+:8/X[8/-7d|J2;7>7d}D6I6,7'7)7!7~7K2I3F_u=b|(={7`[->]7^7|,[,/7(7s~p~_7{/r{:7 ", +" 4-X:V*q&T*~'Q&q&%&j=)*!*%&Q&B,%-<7%-G*m2i]Q#)=;*M5[7U2l4 4`h>p7q7%~='[)q~p]4^.( ", +" r7u]v*I=T*T*]*q&'*'*%&p&%&n&B,4&[4l}*{d<&6Q#56:[M5s7t7u7v7d[X1w7;6+[b4J5x7([y7J5B}}^@3z7c=57>2I=A7B7z/C7A[D7R5E7`8U[,846.8'8A6w**5k7c=:|)8k/!8-7C=9/~8E}9/A|X*{8n1,*M3]8h6^8/8P1O2r2(8h<,;(8u:z,|:22_8O6:8<8/,u's~'{Y]4^[8 ", +" g(e=W)I=X)X)'*A*y*x*W*Y*5,z*!;%&4&N&F<`(G_}8R:|8`},}18283848G177x[58+[68}678V5F_8898[=c=08y6E=*3a8F_b8c8X[d8z7e8f8u^g8v*85&5x*q&+|`*r>h8i80^C|j8k/_2k8h=h{{y*y*B*B*y*+_x*V*W*W*V*x*V*x*D-'*p&N&]*`(R])8.$v8K$B}W<48w8x8G696B[y8A/e}z84)96&3x=-/A8`/u*:}B8._F6)}'7B)C8D8c=E8/|(=F8G8A=g6N7Y^F=c3g5F(H80!2-u:I8'>m8J8D4K88(L8M8N8O8'3n8v^N86_P8Q8N6|,R8S8w'u'()#~T^r~S3 ", +" E;x:);T8G=Z*B-);X*W*W*V*X*{=I'{{{=V*v*'*!*'*]*85L%] 9-/M7L29|.9T7y=d|$1y^L7+9q>(=q8@9w-#9l8w-D=$9e!%9#9D|K8&9*9=9-9;9>9=9i5,9'9)9!9~9{9Q>()d!q~p]W]]9 ", +" >>n>F=(4`*);]=E=b=^=i={={{:=)>{=W*x*v*A*A*'*G=B*H=41}:v|^9/9e>(9*5_9:9g|<9z703X*J3h8M/[9X8 (y^=1}9:=|9B|19O79/O|Y*29A|F=@-&1:=A=N239g<4-'349p-D|0!m76_59D;697989,99909a9b9b9t8c909d9e9C/f9l]g9S),{T^()h9 ", +" i9y,/=Y5y*);A-w=+-A=z=(=w=:={{{=I'Cy*q&7(H=G=H=T*}:j9k9)8I3d}19l9W8m9(2 (d}413-i8d|x=y^J6n929z4&1o9}:8/p9e=q9=14-A-r9G;;;l{s9q-2-m8i6r-t9u9&;v9w9]7=_x9y9z9A9B9c9C9D9E9F9E9G9H9@]S)'{$]I9G/J9 ", +" E'y,B=g5E});@-c=x=4-2)2)u-2)B=]/Y^]/v=^=W*Y*'*41K9F>R9E;s{S9T9U9=;V9W9y;X9Y9h9{,Z9`9 0.009+0+0@0E9#0$0|^m]2^U]$]'/C(V^[)%0 ", +" T:H_H;e!A=x=e=.-w=(=M/x-x-z,s{p-2-'(A=(=C=r>D=L1B-Z*C-`*G=*~K991b|*1L|&0q1&0Y^j80='(_=*0o2F(e=s1n9P11-=0-0E'`[i5;0>0:4&;n>:4%0z;l5x9 0>(,0'0)0g>!0~0{0p>]0^0+6/0(0/7S8P^_0S)@~p~d/H/#_@]:0 ", +" <0D'D;7^e!!'-;p-w-2)3-k/x-w-2)q>2-F;B=A=f=g=_=_=,;A-]=@-g5L1B-[0}0|0N1M1<2Gl580Q2{,{090;5{,{0G9Y7},.600a0;'l]b0|^,/,/l]@]2^@~X]T^*(c0l{ ", +" M>[[d0w-->],&>[,m>l>3:n>K4z,w-p-e0F;f=7^_=t{f0h=f05-30D=A,F'g0^881h0[0=0Y5v=c3i0F(h=r849gL6'0%~p0q0r0s0k>t0^7%~L>u0~9|^,'@]l]l],'B'={={2^n~$]'/U^x^c!v0 ", +" ){D'&;->z;x;w0*>x0m>N>j/$:&>->n>->&;-;4-e=,;_=@9,;303030y0z0A0d3%2+2$>t2g5/=O2G;2)2)B069;>;>2259%0C0=_{0'0!0!9(0H9D0/7E0F0j/!9#0a0!,G0l]y'@]@]m]x'@]={x'U]s~s~p~X]H0h/s~;'I0 ", +" @6q,w,N>k>3:],1)D;q-z;<,O>O>i>[[y;y; 0->>>@97^J0K0@9t9,;30i0L0M0N0O0i6n8S9P0J4k6X^D;k6Q0[[C;[}c9G4q7~9G0R0m]={|^|^|^|^S8S0|^={={2^x'>/2^x'T0={U]()U]s~p~q~q{X]4^h/0(F4 ", +" -0U0*'q,z'|)C'k>V0%>->x;N6O>L>h>v9v9y;.(l5W0X0Y0X0Z0`0 a.a:4+a@aN840f3`5#a->$a%af(;>w0&a*aO>x,|^|^l]T0={m]2^>/@]c/m]=aS)S)2^1^m]U]x'-{[)U]s~p~p~q{q{$]-aT^E/s~B'V9 ", +" r,})A'u'})<))';'/,J>+6-'P>P>O>,0I_;aQ2p,k6l0Z0`9x9:8D9&ac9W7Q3@aR9#a>a,ad0'at890i>%>i>)ay'={x'y'x'U]!aS)@~~ap~'{X]'{p~@~@~p~q{'{$]E/E/E/p]'/~/I/I/x^;'{0 ", +" F}e_s~})/)<)()B'@]v')a!,r,J>f9C/,0Z9{a.6]aa9z9^aQ0/aw9]9$ao0-5(a-9i5_a:4:a&>s,T0T]l]r,x,J>m]U]()`!'{p~'{'{p~q{X]d/4^'/x(x(#(#(T^'/x^+(H/B(p]59a'02a]a]a2a@0]aG9^70a9ae9|aE9~0E9(0aa)a@]S^<)s~p~p~q~'{_7p]p]4^4^d/'/x(T^#(#()/!/$_q~L6F422 ", +" u9bas~#~()D/u'B'x'@]@]@]v'S8/7caM6D92a]ada2a2aeaC9fa0a(0cagahaca(0q0@]ia[)s~'{q{q{X]w^bax(T^'/x^)/!/e/)/$(H/d/Y7#6 ", +" l{)'o]#~2^y'B'x'={w'@]@])a|^S0ja0ae9G9F9E9ka(0>5la,/T0={iamap~naE/oap]X]$]p]x^#(+(e/h/paqa$(x^q~={{0h9 ", +" raT^E/@~@~#~U]m]x'={@]|^E0sataq7ua#0vaq7~9wa/7S8xaS)p~'{X]d/'/x(U^e/yayazaAaBaU^{9CaDa ", +" h9X]'{X]X]'{'{p~S)U]1^xaE0$0r0~9u0E0R0R0@]EaX]E/p]4^#(e/g/G/G/e/h/FaGa#aHa ", +" IaS8+]h/U^'/'/p]E/X]'{S)2^U]S)S)#~p~$]p]4^U^ya%($_s~v''{Ja ", +" Ka$aB'y'G/La$(H/H/)/#()/!/!/g/I/0(B(@6+]Ma:7 "}; diff --git a/Modules/CameraCalibration/mitkCameraIntrinsics.h b/Modules/CameraCalibration/mitkCameraIntrinsics.h index 9f6e43ede3..bf184d257d 100644 --- a/Modules/CameraCalibration/mitkCameraIntrinsics.h +++ b/Modules/CameraCalibration/mitkCameraIntrinsics.h @@ -1,144 +1,144 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkCameraIntrinsics_h #define mitkCameraIntrinsics_h #include -#include +#include #include #include #include #include #include "mitkXMLSerializable.h" #include int mitkCameraIntrinsicsTest(int argc, char* argv[]); namespace mitk { /// /// \brief class representing camera intrinsics and related functions /// class MitkCameraCalibration_EXPORT CameraIntrinsics: virtual public itk::Object, virtual public mitk::XMLSerializable { public: /// /// for testing purposes /// friend int mitkCameraIntrinsicsTest(int argc, char* argv[]); /// /// smartpointer typedefs /// mitkClassMacro(CameraIntrinsics, itk::Object); /// /// the static new function /// itkFactorylessNewMacro(Self); /// /// make a clone of this intrinsics /// itkCloneMacro(Self) /// /// copy information from other to this /// void Copy(const CameraIntrinsics* other); /// /// checks two intrinsics for equality /// bool Equals( const CameraIntrinsics* other ) const; /// /// \return the intrinsic parameter matrix as a 3x3 vnl matrix /// vnl_matrix_fixed GetVnlCameraMatrix() const; /// /// \return the intrinsic parameter matrix as a 3x4 vnl matrix /// (the last column only containing zeros) /// vnl_matrix_fixed GetVnlCameraMatrix3x4() const; /// /// \return true if the intrinsics are set (some plausibility checks /// may be done here) /// bool IsValid() const; void SetValid(bool valid); cv::Mat GetCameraMatrix() const; cv::Mat GetDistorsionCoeffs(); cv::Mat GetDistorsionCoeffs() const; void ToXML(TiXmlElement* elem) const; std::string ToString() const; std::string GetString(); double GetFocalLengthX() const; double GetFocalLengthY() const; double GetPrincipalPointX() const; double GetPrincipalPointY() const; mitk::Point4D GetDistorsionCoeffsAsPoint4D() const; mitk::Point3D GetFocalPoint() const; mitk::Point3D GetPrincipalPoint() const; vnl_vector_fixed GetFocalPointAsVnlVector() const; vnl_vector_fixed GetPrincipalPointAsVnlVector() const; /// /// set a new camera matrix utilizing a vnl matrix /// void SetCameraMatrix( const vnl_matrix_fixed& _CameraMatrix ); void SetIntrinsics( const cv::Mat& _CameraMatrix , const cv::Mat& _DistorsionCoeffs); void SetFocalLength( double x, double y ); void SetPrincipalPoint( double x, double y ); void SetDistorsionCoeffs( double k1, double k2, double p1, double p2 ); void SetIntrinsics( const mitk::Point3D& focalPoint, const mitk::Point3D& principalPoint, const mitk::Point4D& distortionCoefficients); void FromXML(TiXmlElement* elem); void FromGMLCalibrationXML(TiXmlElement* elem); std::string ToOctaveString(const std::string& varName="CameraIntrinsics"); virtual ~CameraIntrinsics(); protected: CameraIntrinsics(); CameraIntrinsics(const CameraIntrinsics& other); cv::Mat m_CameraMatrix; cv::Mat m_DistorsionCoeffs; bool m_Valid; itk::FastMutexLock::Pointer m_Mutex; private: virtual itk::LightObject::Pointer InternalClone() const; }; } // namespace mitk MitkCameraCalibration_EXPORT std::ostream& operator<< (std::ostream& os, mitk::CameraIntrinsics::Pointer p); #endif // mitkCameraIntrinsics_h diff --git a/Modules/CameraCalibration/mitkTransform.h b/Modules/CameraCalibration/mitkTransform.h index f14feae63d..ada8c83f0e 100644 --- a/Modules/CameraCalibration/mitkTransform.h +++ b/Modules/CameraCalibration/mitkTransform.h @@ -1,305 +1,305 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKTRANSFORM_H #define MITKTRANSFORM_H #include #include #include -#include +#include #include #include #include #include #include #include namespace mitk { /// /// \brief class representing a transfrom in 3D /// /// internally it stores a mitk navigation data. this is more /// or less a wrapper for navigation data for easy casting /// between opencv/vnl/mitk/xml representations of transform /// data /// class MitkCameraCalibration_EXPORT Transform: public itk::Object, public XMLSerializable { public: mitkClassMacro(Transform, itk::Object); itkFactorylessNewMacro(Transform); mitkNewMacro1Param(Transform, const mitk::NavigationData*); mitkNewMacro1Param(Transform, const std::string&); /// /// constants describing the type of transform /// represented here /// static const std::string UNKNOWN_TYPE; static const std::string ENDOSCOPE_SCOPE_TOOL; static const std::string ENDOSCOPE_CAM_TOOL; static const std::string CHESSBOARD_TOOL; static const std::string POINTER_TOOL; static const std::string POINTER_TO_CHESSBOARD_ORIGIN; static const std::string POINTER_TO_CHESSBOARD_X_SUPPORT_POINT; static const std::string POINTER_TO_CHESSBOARD_Y_SUPPORT_POINT; static const std::string BOARD_TO_BOARD_TOOL; static const std::string REFERENCE_CAMERA_TRANSFORM; static const std::string REFERENCE_SCOPE_TRANSFORM; static const std::string EYE_TO_HAND_TRANSFORM; static const std::string CAMERA_EXTRINSICS; itkGetConstMacro(Type, std::string); itkSetMacro(Type, std::string&); /// /// Copies the content of transform to this /// instance /// void Copy( const mitk::Transform* transform ); /// /// Copies the content of transform to this /// instance /// void Copy( const mitk::NavigationData* transform ); /// /// Inverts the rotation of this transform /// (Polaris navigation Data have inverted rotation /// so you may want to call this function when using /// polaris data) /// void TransposeRotation(); /// /// get a copy of this transform /// mitk::Transform::Pointer Clone() const; /// /// concatenate this transform with the given one, /// i.e. this transform is done first, then transform /// ( if x is this transform, y is transform, then this will be y*x) /// post multiply semantics! /// \see vtkTransform /// void Concatenate( mitk::Transform* transform ); /// /// same as above with vnl mat argument /// void Concatenate( const vnl_matrix_fixed& transform ); /// /// same as above with vtk mat argument /// void Concatenate( const vtkMatrix4x4* transform ); /// /// invert this transform /// void Invert(); /// /// resets the internal variables except type /// void Reset(); /// /// read from xml /// void FromXML(TiXmlElement* elem); /// /// read csv file /// void FromCSVFile(const std::string& file); /// /// grafts the data from naviData to this transform /// void SetNavigationData( const mitk::NavigationData* naviData ); /// /// method to set orientation quat /// void SetOrientation( const vnl_quaternion& orientation); /// /// method to set float valued orientation quat /// void SetOrientation( const vnl_quaternion& orientation); /// /// method to set translation /// void SetTranslation( const vnl_vector_fixed& transl); /// /// method to set a vector of doubles as translation /// void SetTranslation( const vnl_vector& transl); /// /// method to set a mitk::Point3D as position /// void SetPosition( const mitk::Point3D& transl); /// /// sets rotation with a rotation matrix /// void SetRotation( vnl_matrix_fixed& mat); /// /// sets rotation with a non fixed rotation matrix /// void SetRotation( vnl_matrix& mat); /// /// sets rotation and translation with a transformation matrix /// void SetMatrix( const vnl_matrix_fixed& mat); /// /// sets rotation and translation with a vtk transformation matrix /// void SetMatrix( const vtkMatrix4x4* mat); /// /// sets translation from a POD vector /// void SetTranslation( float* array ); /// /// sets translation from a POD vector. this must be a /// 3x3=9 sized vector in row major format (first row = first /// three elements) /// void SetRotation( float* array ); /// /// sets translation from a POD vector /// void SetTranslation( double array[3] ); /// /// sets translation from a POD vector /// void SetRotation( double array[3][3] ); /// /// method to set translation by cv vector /// void SetTranslation( const cv::Mat& transl); /// /// sets rotation with a rotation matrix /// void SetRotation( const cv::Mat& mat ); /// /// sets rotation with a rodrigues rotation vector /// void SetRotationVector( const cv::Mat& rotVec); /// /// \return the navigation data that stores all information /// mitk::NavigationData::Pointer GetNavigationData() const; /// /// calls navigationdata::GetPosition() /// mitk::Point3D GetPosition() const; /// /// same as GetPosition /// mitk::Point3D GetTranslation() const; /// /// calls navigationdata::IsValid() /// bool IsValid() const; /// /// calls navigationdata::SetValid() /// void SetValid(bool valid); /// /// calls navigationdata::GetOrientation() /// mitk::Quaternion GetOrientation() const; /// /// \return the homogeneous matrix representing this transform /// vnl_matrix_fixed GetMatrix() const; /// /// \return the homogeneous vtk matrix representing this transform /// void GetMatrix(vtkMatrix4x4* matrix) const; /// /// \return the homogeneous vtk matrix representing this transform /// in !OpenGL! left handed coordinate system /// void GetVtkOpenGlMatrix(vtkMatrix4x4* matrix) const; mitk::Point3D TransformPoint(mitk::Point3D point) const; /// /// create xml representation /// void ToXML(TiXmlElement* elem) const; /// /// create string representation /// std::string ToString() const; /// /// create string csv representation (only the transformation values!!!!) /// std::string ToCSVString() const; /// /// create matlab representation /// std::string ToMatlabString(const std::string& varname="transform", bool printLastRow=true) const; /// /// write csv representation to file (only the transformation values!!!!) /// void ToCSVFile(const std::string& file) const; /// /// write matlab representation to file /// void ToMatlabFile(const std::string& file , const std::string& varname="transform") const; /// /// conversion to cv types /// cv::Mat GetCvTranslation() const; cv::Mat GetCvRotationVector() const; cv::Mat GetCvRotationMatrix() const; cv::Mat GetCvMatrix() const; /// /// conversion to vnl types /// vnl_vector_fixed GetVnlTranslation() const; vnl_vector_fixed GetVnlDoubleTranslation() const; vnl_quaternion GetVnlDoubleQuaternion() const; vnl_matrix_fixed GetVnlRotationMatrix() const; vnl_matrix_fixed GetVnlDoubleMatrix() const; protected: Transform(); Transform(const mitk::NavigationData* nd); Transform(const std::string& s); // everything is stored here mitk::NavigationData::Pointer m_NavData; /// /// saves the type of the transform (Default is UNKNOWN_TYPE) /// std::string m_Type; }; } // namespace mitk MitkCameraCalibration_EXPORT std::ostream& operator<< (std::ostream& os, mitk::Transform::Pointer p); #endif // MITKTRANSFORM_H diff --git a/Modules/ContourModel/DataManagement/mitkContourElement.h b/Modules/ContourModel/DataManagement/mitkContourElement.h index 75207c8717..95da74d51c 100644 --- a/Modules/ContourModel/DataManagement/mitkContourElement.h +++ b/Modules/ContourModel/DataManagement/mitkContourElement.h @@ -1,269 +1,269 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _mitkContourElement_H_ #define _mitkContourElement_H_ #include "mitkCommon.h" #include -#include +#include //#include #include namespace mitk { /** \brief Represents a contour in 3D space. A ContourElement is consisting of linked vertices implicitely defining the contour. They are stored in a double ended queue making it possible to add vertices at front and end of the contour and to iterate in both directions. To mark a vertex as a special one it can be set as a control point. \Note It is highly not recommend to use this class directly as no secure mechanism is used here. Use mitk::ContourModel instead providing some additional features. */ class MitkContourModel_EXPORT ContourElement : public itk::LightObject { public: mitkClassMacro(ContourElement, itk::LightObject); itkFactorylessNewMacro(Self) itkCloneMacro(Self) // Data container representing vertices /** \brief Represents a single vertex of contour. */ struct ContourModelVertex { ContourModelVertex(mitk::Point3D &point, bool active=false) : IsControlPoint(active), Coordinates(point) { } ContourModelVertex( const ContourModelVertex &other) : IsControlPoint(other.IsControlPoint), Coordinates(other.Coordinates) { } /** \brief Treat point special. */ bool IsControlPoint; /** \brief Coordinates in 3D space. */ mitk::Point3D Coordinates; }; // END Data container representing vertices typedef ContourModelVertex VertexType; typedef std::deque VertexListType; typedef VertexListType::iterator VertexIterator; typedef VertexListType::const_iterator ConstVertexIterator; // start of inline methods /** \brief Return a const iterator a the front. */ virtual ConstVertexIterator ConstIteratorBegin() { return this->m_Vertices->begin(); } /** \brief Return a const iterator a the end. */ virtual ConstVertexIterator ConstIteratorEnd() { return this->m_Vertices->end(); } /** \brief Return an iterator a the front. */ virtual VertexIterator IteratorBegin() { return this->m_Vertices->begin(); } /** \brief Return an iterator a the end. */ virtual VertexIterator IteratorEnd() { return this->m_Vertices->end(); } /** \brief Returns the number of contained vertices. */ virtual int GetSize() { return this->m_Vertices->size(); } // end of inline methods /** \brief Add a vertex at the end of the contour \param point - coordinates in 3D space. \param isControlPoint - is the vertex a special control point. */ virtual void AddVertex(mitk::Point3D &point, bool isControlPoint); /** \brief Add a vertex at the end of the contour \param vertex - a contour element vertex. */ virtual void AddVertex(VertexType &vertex); /** \brief Add a vertex at the front of the contour \param point - coordinates in 3D space. \param isControlPoint - is the vertex a control point. */ virtual void AddVertexAtFront(mitk::Point3D &point, bool isControlPoint); /** \brief Add a vertex at the front of the contour \param vertex - a contour element vertex. */ virtual void AddVertexAtFront(VertexType &vertex); /** \brief Add a vertex at a given index of the contour \param point - coordinates in 3D space. \param isControlPoint - is the vertex a special control point. \param index - the index to be inserted at. */ virtual void InsertVertexAtIndex(mitk::Point3D &point, bool isControlPoint, int index); /** \brief Set coordinates a given index. \param pointId Index of vertex. \param point Coordinates. */ virtual void SetVertexAt(int pointId, const mitk::Point3D &point); /** \brief Set vertex a given index. \param pointId Index of vertex. \param vertex Vertex. */ virtual void SetVertexAt(int pointId, const VertexType* vertex); /** \brief Returns the vertex a given index \param index */ virtual VertexType* GetVertexAt(int index); /** \brief Returns the approximate nearest vertex a given posoition in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ virtual VertexType* GetVertexAt(const mitk::Point3D &point, float eps); /** \brief Returns the index of the given vertex within the contour. \param vertex - the vertex to be searched. \return index of vertex. -1 if not found. */ virtual int GetIndex(const VertexType* vertex); /** \brief Returns the container of the vertices. */ VertexListType* GetVertexList(); /** \brief Returns whether the contour element is empty. */ bool IsEmpty(); /** \brief Returns if the conour is closed or not. */ virtual bool IsClosed(); /** \brief Returns whether a given point is near a contour, according to eps. \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ virtual bool IsNearContour(const mitk::Point3D &point, float eps); /** \brief Close the contour. Connect first with last element. */ virtual void Close(); /** \brief Open the contour. Disconnect first and last element. */ virtual void Open(); /** \brief Set the contours IsClosed property. \param isClosed - true = closed; false = open; */ virtual void SetClosed(bool isClosed); /** \brief Concatenate the contuor with a another contour. All vertices of the other contour will be added after last vertex. \param other - the other contour \param check - set it true to avoid intersections */ void Concatenate(mitk::ContourElement* other, bool check); /** \brief Remove the given vertex from the container if exists. \param vertex - the vertex to be removed. */ virtual bool RemoveVertex(const VertexType* vertex); /** \brief Remove a vertex at given index within the container if exists. \param index - the index where the vertex should be removed. */ virtual bool RemoveVertexAt(int index); /** \brief Remove the approximate nearest vertex at given position in 3D space if one exists. \param point - query point in 3D space. \param eps - error bound for search algorithm. */ virtual bool RemoveVertexAt(mitk::Point3D &point, float eps); /** \brief Clear the storage container. */ virtual void Clear(); /** \brief Returns the approximate nearest vertex a given posoition in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ VertexType* BruteForceGetVertexAt(const mitk::Point3D &point, float eps); /** \brief Returns the approximate nearest vertex a given posoition in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ //VertexType* OptimizedGetVertexAt(const mitk::Point3D &point, float eps); VertexListType* GetControlVertices(); /** \brief Uniformly redistribute control points with a given period (in number of vertices) \param vertex - the vertex around which the redistribution is done. \param period - number of vertices between control points. */ void RedistributeControlVertices(const VertexType* vertex, int period); protected: mitkCloneMacro(Self); ContourElement(); ContourElement(const mitk::ContourElement &other); virtual ~ContourElement(); VertexListType* m_Vertices; //double ended queue with vertices bool m_IsClosed; }; } // namespace mitk #endif // _mitkContourElement_H_ diff --git a/Modules/DataTypesExt/Testing/mitkMeshTest.cpp b/Modules/DataTypesExt/Testing/mitkMeshTest.cpp index 4020866878..140d0a4ca5 100644 --- a/Modules/DataTypesExt/Testing/mitkMeshTest.cpp +++ b/Modules/DataTypesExt/Testing/mitkMeshTest.cpp @@ -1,81 +1,81 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include -#include +#include #include #include #include int mitkMeshTest(int /*argc*/, char* /*argv*/[]) { //Create mesh mitk::Mesh::Pointer mesh; mesh = mitk::Mesh::New(); //try to get the itkmesh std::cout << "Create a mesh and try to get the itkMesh"; mitk::Mesh::DataType::Pointer itkdata = NULL; itkdata = mesh->GetMesh(); if (itkdata.IsNull()) { std::cout<<"[FAILED]"<GetSize() != 0) { std::cout<<"[FAILED]"<ExecuteOperation(doOp); //now check new condition! if ( (mesh->GetSize()!=1) || (!mesh->IndexExists(position))) { std::cout<<"[FAILED]"<GetPoint(position); if (tempPoint != point) { std::cout<<"[FAILED]"<SetRadius(1.0); cone->SetHeight(2.0); cone->SetDirection(0.0, -1.0, 0.0); cone->SetCenter(0.0, 0.0, 0.0); cone->SetResolution(20); cone->CappingOn(); cone->Update(); SetVtkPolyData(cone->GetOutput()); cone->Delete(); } mitk::Cone::~Cone() { } bool mitk::Cone::IsInside(const Point3D& worldPoint) const { // transform point from world to object coordinates ScalarType p[4]; p[0] = worldPoint[0]; p[1] = worldPoint[1]; p[2] = worldPoint[2]; p[3] = 1; GetGeometry()->GetVtkTransform()->GetInverse()->TransformPoint(p, p); p[1] += 1; // translate point, so that it fits to the formula below, which describes a cone that has its cone vertex at the origin return (sqrt(p[0] * p[0] + p[2] * p[2]) <= p[1] * 0.5) && (p[1] <= 2); // formula to calculate if a given point is inside a cone that has its cone vertex at the origin, is aligned on the second axis, has a radius of one an a height of two } mitk::ScalarType mitk::Cone::GetVolume() { TimeGeometry* geometry = GetTimeGeometry(); return geometry->GetExtentInWorld(0) * 0.5 * geometry->GetExtentInWorld(2) * 0.5 * vnl_math::pi / 3.0 * geometry->GetExtentInWorld(1); } diff --git a/Modules/DataTypesExt/mitkCuboid.cpp b/Modules/DataTypesExt/mitkCuboid.cpp index 4ba86b8101..ab25b5444a 100644 --- a/Modules/DataTypesExt/mitkCuboid.cpp +++ b/Modules/DataTypesExt/mitkCuboid.cpp @@ -1,64 +1,64 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCuboid.h" #include "vtkLinearTransform.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "vtkCubeSource.h" #include mitk::Cuboid::Cuboid() : BoundingObject() { vtkCubeSource* cube = vtkCubeSource::New(); cube->SetXLength(2.0); cube->SetYLength(2.0); cube->SetZLength(2.0); cube->Update(); SetVtkPolyData(cube->GetOutput()); cube->Delete(); } mitk::Cuboid::~Cuboid() { } bool mitk::Cuboid::IsInside(const Point3D& worldPoint) const { // transform point from world to object coordinates ScalarType p[4]; p[0] = worldPoint[0]; p[1] = worldPoint[1]; p[2] = worldPoint[2]; p[3] = 1; GetGeometry()->GetVtkTransform()->GetInverse()->TransformPoint(p, p); return (p[0] >= -1) && (p[0] <= 1) && (p[1] >= -1) && (p[1] <= 1) && (p[2] >= -1) && (p[2] <= 1); } mitk::ScalarType mitk::Cuboid::GetVolume() { TimeGeometry* geometry = GetTimeGeometry(); return geometry->GetExtentInWorld(0) * geometry->GetExtentInWorld(1) * geometry->GetExtentInWorld(2); } diff --git a/Modules/DataTypesExt/mitkCylinder.cpp b/Modules/DataTypesExt/mitkCylinder.cpp index 454869e2f2..9c131033c0 100644 --- a/Modules/DataTypesExt/mitkCylinder.cpp +++ b/Modules/DataTypesExt/mitkCylinder.cpp @@ -1,68 +1,68 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCylinder.h" #include "vtkLinearTransform.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "vtkCylinderSource.h" mitk::Cylinder::Cylinder() : BoundingObject() { vtkCylinderSource* cylinder = vtkCylinderSource::New(); cylinder->SetRadius(1.0); cylinder->SetHeight(2.0); cylinder->SetCenter(0.0, 0.0, 0.0); cylinder->SetResolution(100); cylinder->CappingOn(); cylinder->Update(); SetVtkPolyData(cylinder->GetOutput()); cylinder->Delete(); } mitk::Cylinder::~Cylinder() { } bool mitk::Cylinder::IsInside(const Point3D& worldPoint) const { // transform point from world to object coordinates ScalarType p[4]; p[0] = worldPoint[0]; p[1] = worldPoint[1]; p[2] = worldPoint[2]; p[3] = 1; GetGeometry()->GetVtkTransform()->GetInverse()->TransformPoint(p, p); mitk::ScalarType v = pow(p[0], 2) + pow(p[2], 2); bool retval = (v <= 1) && (p[1] >= -1) && (p[1] <= 1); return retval; } mitk::ScalarType mitk::Cylinder::GetVolume() { TimeGeometry* geometry = GetTimeGeometry(); return geometry->GetExtentInWorld(0) * 0.5 * geometry->GetExtentInWorld(2) * 0.5 * vnl_math::pi * geometry->GetExtentInWorld(1); } diff --git a/Modules/DataTypesExt/mitkEllipsoid.cpp b/Modules/DataTypesExt/mitkEllipsoid.cpp index 8405b2fd22..dc6d5343c2 100644 --- a/Modules/DataTypesExt/mitkEllipsoid.cpp +++ b/Modules/DataTypesExt/mitkEllipsoid.cpp @@ -1,64 +1,64 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkEllipsoid.h" #include "vtkLinearTransform.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "vtkSphereSource.h" mitk::Ellipsoid::Ellipsoid() : BoundingObject() { vtkSphereSource* sphere = vtkSphereSource::New(); sphere->SetRadius(1.0); sphere->SetThetaResolution(20); sphere->SetPhiResolution(20); sphere->Update(); SetVtkPolyData(sphere->GetOutput()); sphere->Delete(); } mitk::Ellipsoid::~Ellipsoid() { } bool mitk::Ellipsoid::IsInside(const Point3D& worldPoint) const { // transform point from world to object coordinates ScalarType p[4]; p[0] = worldPoint[0]; p[1] = worldPoint[1]; p[2] = worldPoint[2]; p[3] = 1; GetGeometry()->GetVtkTransform()->GetInverse()->TransformPoint(p, p); return (pow(p[0], 2) + pow(p[1], 2) + pow(p[2], 2) <= 1); } mitk::ScalarType mitk::Ellipsoid::GetVolume() { return GetGeometry()->GetExtentInMM(0) * 0.5 * GetGeometry()->GetExtentInMM(1) * 0.5 * GetGeometry()->GetExtentInMM(2) * 0.5 * vnl_math::pi * 4.0/3.0; } diff --git a/Modules/DataTypesExt/mitkMesh.cpp b/Modules/DataTypesExt/mitkMesh.cpp index 55cc2efcb8..e532b2a178 100644 --- a/Modules/DataTypesExt/mitkMesh.cpp +++ b/Modules/DataTypesExt/mitkMesh.cpp @@ -1,840 +1,840 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMesh.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkLineOperation.h" #include "mitkLineOperation.h" #include "mitkPointOperation.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkStatusBar.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include "mitkRenderingManager.h" mitk::Mesh::Mesh() { } mitk::Mesh::~Mesh() { } const mitk::Mesh::DataType *mitk::Mesh::GetMesh( int t ) const { return m_PointSetSeries[t]; } mitk::Mesh::DataType* mitk::Mesh::GetMesh( int t ) { return m_PointSetSeries[t]; } void mitk::Mesh::SetMesh( DataType *mesh, int t ) { this->Expand( t+1 ); m_PointSetSeries[t] = mesh; } unsigned long mitk::Mesh::GetNumberOfCells( int t ) { return m_PointSetSeries[t]->GetNumberOfCells(); } //search a line that is close enough according to the given position bool mitk::Mesh::SearchLine( Point3D point, float distance, unsigned long &lineId, unsigned long &cellId, int t ) { //returns true if a line is found ScalarType bestDist = distance; //iterate through all cells. ConstCellIterator cellIt = m_PointSetSeries[t]->GetCells()->Begin(); ConstCellIterator cellEnd = m_PointSetSeries[t]->GetCells()->End(); while( cellIt != cellEnd) { if (cellIt.Value()->GetNumberOfPoints() >1) { //then iterate through all indexes of points in it->Value() PointIdIterator inAIt = cellIt.Value()->PointIdsBegin(); // first point PointIdIterator inBIt = cellIt.Value()->PointIdsBegin(); // second point PointIdIterator inEnd = cellIt.Value()->PointIdsEnd(); ++inAIt; //so it points to the point before inBIt int currentLineId = 0; while(inAIt != inEnd) { mitk::PointSet::PointType pointA, pointB; if ( m_PointSetSeries[t]->GetPoint((*inAIt), &pointA) && m_PointSetSeries[t]->GetPoint((*inBIt), &pointB)) { Line *line = new Line(); line->SetPoints(pointA, pointB); double thisDistance = line->Distance(point); if (thisDistance < bestDist) { cellId = cellIt->Index(); lineId = currentLineId; bestDist = thisDistance; } } ++inAIt; ++inBIt; ++currentLineId; } // If the cell is closed, then check the line from the last index to // the first index if inAIt points to inEnd, then inBIt points to the // last index. CellDataType cellData; bool dataOk = m_PointSetSeries[t]->GetCellData(cellIt->Index(), &cellData); if (dataOk) { if (cellData.closed) { // get the points PointIdIterator inAIt = cellIt.Value()->PointIdsBegin();//first point // inBIt points to last. mitk::PointSet::PointType pointA, pointB; if ( m_PointSetSeries[t]->GetPoint((*inAIt), &pointA) && m_PointSetSeries[t]->GetPoint((*inBIt), &pointB)) { Line *line = new Line(); line->SetPoints(pointA, pointB); double thisDistance = line->Distance(point); if (thisDistance < bestDist) { cellId = cellIt->Index(); lineId = currentLineId; bestDist = thisDistance; } } } } } ++cellIt; } return (bestDist < distance); } int mitk::Mesh::SearchFirstCell( unsigned long pointId, int t ) { //iterate through all cells and find the cell the given pointId is inside ConstCellIterator it = m_PointSetSeries[t]->GetCells()->Begin(); ConstCellIterator end = m_PointSetSeries[t]->GetCells()->End(); while( it != end) { PointIdIterator position = std::find(it->Value()->PointIdsBegin(), it->Value()->PointIdsEnd(), pointId); if ( position != it->Value()->PointIdsEnd()) { return it->Index(); } ++it; } return -1; } // Due to not implemented itk::CellInterface::EvaluatePosition and errors in // using vtkCell::EvaluatePosition (changing iterator!) we must implement // it in mitk::Mesh // make it easy and look for hit points and hit lines: needs to be done anyway! bool mitk::Mesh::EvaluatePosition( mitk::Point3D point, unsigned long &cellId, float precision, int t ) { int pointId = this->SearchPoint( point, precision, t ); if (pointId > -1) { //search the cell the point lies inside cellId = this->SearchFirstCell( pointId, t ); return true; } unsigned long lineId = 0; if ( this->SearchLine(point, precision, lineId, cellId, t) ) { return true; } return false; } unsigned long mitk::Mesh::GetNewCellId( int t ) { long nextCellId = -1; ConstCellIterator it = m_PointSetSeries[t]->GetCells()->Begin(); ConstCellIterator end = m_PointSetSeries[t]->GetCells()->End(); while (it!=end) { nextCellId = it.Index(); ++it; } ++nextCellId; return nextCellId; } int mitk::Mesh::SearchSelectedCell( int t ) { CellDataIterator cellDataIt, cellDataEnd; cellDataEnd = m_PointSetSeries[t]->GetCellData()->End(); for ( cellDataIt = m_PointSetSeries[t]->GetCellData()->Begin(); cellDataIt != cellDataEnd; cellDataIt++ ) { //then declare an operation which unselects this line; UndoOperation as well! if ( cellDataIt->Value().selected ) { return cellDataIt->Index(); } } return -1; } // get the cell; then iterate through the Ids times lineId. Then IdA ist the // one, IdB ist ne next.don't forget the last closing line if the cell is // closed bool mitk::Mesh::GetPointIds( unsigned long cellId, unsigned long lineId, int &idA, int &idB, int t ) { bool ok = false; bool found = false; CellAutoPointer cellAutoPointer; ok = m_PointSetSeries[t]->GetCell(cellId, cellAutoPointer); if (ok) { CellType * cell = cellAutoPointer.GetPointer(); //Get the cellData to also check the closing line CellDataType cellData; m_PointSetSeries[t]->GetCellData(cellId, &cellData); bool closed = cellData.closed; PointIdIterator pointIdIt = cell->PointIdsBegin(); PointIdIterator pointIdEnd = cell->PointIdsEnd(); unsigned int counter = 0; while (pointIdIt != pointIdEnd) { if(counter == lineId) { idA = (*pointIdIt); ++pointIdIt; found = true; break; } ++counter; ++pointIdIt; } if(found) { //if in the middle if (pointIdIt != pointIdEnd) { idB = (*pointIdIt); } // if found but on the end, then it is the closing connection, so the // last and the first point else if (closed) { pointIdIt = cell->PointIdsBegin(); idB = (*pointIdIt); } } else ok = false; } return ok; } void mitk::Mesh::ExecuteOperation(Operation* operation) { //adding only the operations, that aren't implemented by the pointset. switch (operation->GetOperationType()) { case OpNOTHING: break; case OpNEWCELL: { mitk::LineOperation *lineOp = dynamic_cast(operation); // if no lineoperation, then call superclass pointSet if (lineOp == NULL) { Superclass::ExecuteOperation(operation); } bool ok; int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); // if it doesn't already exist if (!ok) { cellAutoPointer.TakeOwnership( new PolygonType ); m_PointSetSeries[0]->SetCell(cellId, cellAutoPointer); CellDataType cellData; cellData.selected = true; cellData.selectedLines.clear(); cellData.closed = false; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpDELETECELL: { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == NULL)//if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } m_PointSetSeries[0]->GetCells()->DeleteIndex((unsigned)lineOp->GetCellId()); m_PointSetSeries[0]->GetCellData()->DeleteIndex((unsigned)lineOp->GetCellId()); } break; case OpCLOSECELL: //sets the bolean flag closed from a specified cell to true. { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == NULL)//if no lineoperation, then call superclass pointSet { //then search the selected cell!//TODO Superclass::ExecuteOperation(operation); } bool ok; int cellId = lineOp->GetCellId(); if (cellId<0)//cellId isn't set { cellId = this->SearchSelectedCell( 0 ); if (cellId < 0 )//still not found return; } CellAutoPointer cellAutoPointer; //get directly the celldata!TODO ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); cellData.closed = true; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpOPENCELL: { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == NULL)//if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } bool ok; int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); cellData.closed = false;; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpADDLINE: // inserts the ID of the selected point into the indexes of lines in the // selected cell afterwars the added line is selected { mitk::LineOperation *lineOp = dynamic_cast(operation); int cellId = -1; int pId = -1; if (lineOp == NULL) { cellId = this->SearchSelectedCell( 0 ); if (cellId == -1) return; pId = this->SearchSelectedPoint( 0 ); if (pId == -1) return; } else { cellId = lineOp->GetCellId(); if (cellId == -1) return; pId = lineOp->GetPIdA(); if (pId == -1) return; } bool ok; CellAutoPointer cellAutoPointer; ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellType * cell = cellAutoPointer.GetPointer(); if( cell->GetType() == CellType::POLYGON_CELL ) { PolygonType * polygon = static_cast( cell ); // add the pointId to the Cell. filling the empty cell with // one id doesn't mean to add a line, it means, that the // initilal PointId is set. The next addition of a pointId adds // a line polygon->AddPointId(pId); // select the line, if we really added a line, so now have more than // 1 pointID in the cell CellDataType cellData; ok = m_PointSetSeries[0]->GetCellData(cellId, &cellData); if (ok) { // A line between point 0 and 1 has the Id 0. A line between // 1 and 2 has a Id = 1. So we add getnumberofpoints-2. if (polygon->GetNumberOfPoints()>1) cellData.selectedLines.push_back(polygon->GetNumberOfPoints()-2); } m_PointSetSeries[0]->SetCellData(cellId, cellData); m_PointSetSeries[0]->SetCell(cellId, cellAutoPointer); } } } break; case OpDELETELINE: { // deleted the last line through removing the index PIdA // (if set to -1, use the last point) in the given cellId mitk::LineOperation *lineOp = dynamic_cast(operation); int cellId = -1; int pId = -1; if (lineOp == NULL) { cellId = this->SearchSelectedCell( 0 ); if (cellId == -1) return; pId = this->SearchSelectedPoint( 0 ); } else { cellId = lineOp->GetCellId(); if (cellId == -1) return; pId = lineOp->GetPIdA(); } bool ok; CellAutoPointer cellAutoPointer; ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellType * cell = cellAutoPointer.GetPointer(); if( cell->GetType() == CellType::POLYGON_CELL ) { PolygonType * oldPolygon = static_cast( cell ); PolygonType * newPolygonCell = new PolygonType; CellAutoPointer newCell; newCell.TakeOwnership( newPolygonCell ); PointIdConstIterator it, oldend; oldend = oldPolygon->PointIdsEnd(); if(pId >= 0) { for(it = oldPolygon->PointIdsBegin(); it != oldend; ++it) { if((*it) != (MeshType::PointIdentifier)pId) { newPolygonCell->AddPointId(*it); } } } else { --oldend; for(it = oldPolygon->PointIdsBegin(); it != oldend; ++it) newPolygonCell->AddPointId(*it); } oldPolygon->SetPointIds(0, newPolygonCell->GetNumberOfPoints(), newPolygonCell->PointIdsBegin()); } } } break; case OpREMOVELINE: //Remove the given Index in the given cell through copying everything // into a new cell accept the one that has to be deleted. { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == NULL)//if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } bool ok; CellAutoPointer cellAutoPointer; int cellId = lineOp->GetCellId(); ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (!ok) return; CellType * cell = cellAutoPointer.GetPointer(); CellAutoPointer newCellAutoPointer; newCellAutoPointer.TakeOwnership( new PolygonType ); PolygonType * newPolygon = static_cast( cell ); PointIdIterator it = cell->PointIdsBegin(); PointIdIterator end = cell->PointIdsEnd(); int pointId = lineOp->GetPIdA(); if (pointId<0)//if not initialized!! return; while (it!=end) { if ((*it)==(unsigned int)pointId) { break; } else { newPolygon ->AddPointId(*it); } ++it; } while (it!=end) { newPolygon ->AddPointId(*it); it++; } m_PointSetSeries[0]->SetCell(cellId, newCellAutoPointer); } break; case OpINSERTLINE: // //insert line between two other points. ////before A--B after A--C--B // //the points A, B and C have to be in the pointset. // //needed: CellId, Id of C , Id A and Id B ////the cell has to exist! //{ //mitk::LineOperation *lineOp = dynamic_cast(operation); // if (lineOp == NULL)//if no lineoperation, then call superclass pointSet // { // Superclass::ExecuteOperation(operation); // } // int cellId = lineOp->GetCellId(); // int pIdC = lineOp->GetPIdC(); // int pIdA = lineOp->GetPIdA(); // int pIdB = lineOp->GetPIdB(); // //the points of the given PointIds have to exist in the PointSet // bool ok; // ok = m_PointSetSeries[0]->GetPoints()->IndexExists(pIdA); // if (!ok) // return; // ok = m_PointSetSeries[0]->GetPoints()->IndexExists(pIdB); // if (!ok) // return; // ok = m_PointSetSeries[0]->GetPoints()->IndexExists(pIdC); // if (!ok) // return; // // so the points do exist. So now check, if there is already a cell // // with the given Id // DataType::CellAutoPointer cell; // ok = m_PointSetSeries[0]->GetCell(cellId, cell); // if (!ok) // return; // //pIdA and pIdB should exist in the cell // // PointIdIterator pit = cell->PointIdsBegin(); // PointIdIterator end = cell->PointIdsEnd(); // // //now arrange the new Ids in the cell like desired; pIdC between // // pIdA and pIdB // unsigned int nuPoints = cell->GetNumberOfPoints(); // std::vector newPoints; // pit = cell->PointIdsBegin(); // end = cell->PointIdsEnd(); // int i = 0; // while( pit != end ) // { // if ((*pit) = pIdA) // { // //now we have found the place to add pIdC after // newPoints[i] = (*pit); // i++; // newPoints[i] = pIdC; // } // else // newPoints[i] = (*pit); // pit++; // } // //now we have the Ids, that existed before combined with the new ones // //so delete the old cell // //doesn't seem to be necessary! // //cell->ClearPoints(); // pit = cell->PointIdsBegin(); // cell->SetPointIds(pit); //} break; case OpMOVELINE://(moves two points) { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == NULL) { mitk::StatusBar::GetInstance()->DisplayText( "Message from mitkMesh: Recieved wrong type of operation! See mitkMeshInteractor.cpp", 10000); return; } //create two operations out of the one operation and call superclass //through the transmitted pointIds get the koordinates of the points. //then add the transmitted vestor to them //create two operations and send them to superclass Point3D pointA, pointB; pointA.Fill(0.0); pointB.Fill(0.0); m_PointSetSeries[0]->GetPoint(lineOp->GetPIdA(), &pointA); m_PointSetSeries[0]->GetPoint(lineOp->GetPIdB(), &pointB); pointA[0] += lineOp->GetVector()[0]; pointA[1] += lineOp->GetVector()[1]; pointA[2] += lineOp->GetVector()[2]; pointB[0] += lineOp->GetVector()[0]; pointB[1] += lineOp->GetVector()[1]; pointB[2] += lineOp->GetVector()[2]; mitk::PointOperation* operationA = new mitk::PointOperation(OpMOVE, pointA, lineOp->GetPIdA()); mitk::PointOperation* operationB = new mitk::PointOperation(OpMOVE, pointB, lineOp->GetPIdB()); Superclass::ExecuteOperation(operationA); Superclass::ExecuteOperation(operationB); } break; case OpSELECTLINE://(select the given line) { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == NULL)//if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); SelectedLinesType *selectedLines = &(cellData.selectedLines); SelectedLinesIter position = std::find(selectedLines->begin(), selectedLines->end(), (unsigned int) lineOp->GetId()); if (position == selectedLines->end())//if not alsready selected { cellData.selectedLines.push_back(lineOp->GetId()); } m_PointSetSeries[0]->SetCellData(lineOp->GetCellId(), cellData); } } break; case OpDESELECTLINE://(deselect the given line) { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == NULL) { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); SelectedLinesType *selectedLines = &(cellData.selectedLines); SelectedLinesIter position = std::find(selectedLines->begin(), selectedLines->end(), (unsigned int) lineOp->GetId()); if (position != selectedLines->end())//if found { selectedLines->erase(position); } m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpSELECTCELL://(select the given cell) { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == NULL)//if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; //directly get the data!//TODO bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); cellData.selected = true; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpDESELECTCELL://(deselect the given cell) { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == NULL)//if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); cellData.selected = false; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpMOVECELL: //moves all Points of one cell according to the given vector { mitk::CellOperation *lineOp = dynamic_cast(operation); if (lineOp == NULL)//if no celloperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); Vector3D vector = lineOp->GetVector(); //get the cell CellAutoPointer cellAutoPointer; bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (!ok) return; CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); // iterate through the pointIds of the CellData and move those points in // the pointset PointIdIterator it = cellAutoPointer->PointIdsBegin(); PointIdIterator end = cellAutoPointer->PointIdsEnd(); while(it != end) { unsigned int position = (*it); PointType point; point.Fill(0); m_PointSetSeries[0]->GetPoint(position, &point); point = point + vector; m_PointSetSeries[0]->SetPoint(position, point); ++it; } } break; default: //if the operation couldn't be handled here, then send it to superclass Superclass::ExecuteOperation(operation); return; } //to tell the mappers, that the data is modifierd and has to be updated this->Modified(); mitk::OperationEndEvent endevent(operation); ((const itk::Object*)this)->InvokeEvent(endevent); // As discussed lately, don't mess with rendering from inside data structures //*todo has to be done here, cause of update-pipeline not working yet //mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } mitk::Mesh::DataType::BoundingBoxPointer mitk::Mesh::GetBoundingBoxFromCell( unsigned long cellId, int t ) { // itk::CellInterface has also a GetBoundingBox, but it // returns CoordRepType [PointDimension *2] DataType::BoundingBoxPointer bBoxPointer = NULL; CellAutoPointer cellAutoPointer; if ( m_PointSetSeries[t]->GetCell(cellId, cellAutoPointer)) { DataType::PointsContainerPointer pointsContainer = DataType::PointsContainer::New(); PointIdIterator bbIt = cellAutoPointer.GetPointer()->PointIdsBegin(); PointIdIterator bbEnd = cellAutoPointer.GetPointer()->PointIdsEnd(); while(bbIt != bbEnd) { mitk::PointSet::PointType point; bool pointOk = m_PointSetSeries[t]->GetPoint((*bbIt), &point); if (pointOk) pointsContainer->SetElement((*bbIt), point); ++bbIt; } bBoxPointer = DataType::BoundingBoxType::New(); bBoxPointer->SetPoints(pointsContainer); bBoxPointer->ComputeBoundingBox(); } return bBoxPointer; } diff --git a/Modules/DataTypesExt/mitkPlane.cpp b/Modules/DataTypesExt/mitkPlane.cpp index 9605bf45d5..01b785c9c1 100644 --- a/Modules/DataTypesExt/mitkPlane.cpp +++ b/Modules/DataTypesExt/mitkPlane.cpp @@ -1,113 +1,113 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlane.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include #include #include #include namespace mitk { Plane::Plane() : BoundingObject() { // Set up Plane Surface. m_PlaneSource = vtkPlaneSource::New(); m_PlaneSource->SetOrigin( -32.0, -32.0, 0.0 ); m_PlaneSource->SetPoint1( 32.0, -32.0, 0.0 ); m_PlaneSource->SetPoint2( -32.0, 32.0, 0.0 ); m_PlaneSource->SetResolution( 128, 128 ); m_PlaneSource->Update(); m_PlaneNormal = vtkDoubleArray::New(); m_PlaneNormal->SetNumberOfComponents( 3 ); m_PlaneNormal->SetNumberOfTuples( m_PlaneSource->GetOutput()->GetNumberOfPoints() ); m_PlaneNormal->SetTuple3( 0, 0.0, 0.0, 1.0 ); m_PlaneNormal->SetName( "planeNormal" ); m_Plane = vtkPolyData::New(); m_Plane->DeepCopy( m_PlaneSource->GetOutput() ); m_Plane->GetPointData()->SetVectors( m_PlaneNormal ); this->SetVtkPolyData( m_Plane ); } Plane::~Plane() { m_PlaneSource->Delete(); m_Plane->Delete(); m_PlaneNormal->Delete(); } void Plane::SetExtent( const double x, const double y ) { m_PlaneSource->SetOrigin( -x / 2.0, -y / 2.0, 0.0 ); m_PlaneSource->SetPoint1( x / 2.0, -y / 2.0, 0.0 ); m_PlaneSource->SetPoint2( -x / 2.0, y / 2.0, 0.0 ); m_PlaneSource->Update(); m_Plane->DeepCopy( m_PlaneSource->GetOutput() ); m_Plane->GetPointData()->SetVectors( m_PlaneNormal ); this->Modified(); } void Plane::GetExtent( double &x, double &y ) const { x = m_PlaneSource->GetPoint1()[0] - m_PlaneSource->GetOrigin()[0]; y = m_PlaneSource->GetPoint2()[1] - m_PlaneSource->GetOrigin()[1]; } void Plane::SetResolution( const int xR, const int yR ) { m_PlaneSource->SetResolution( xR, yR ); m_PlaneSource->Update(); m_Plane->DeepCopy( m_PlaneSource->GetOutput() ); m_Plane->GetPointData()->SetVectors( m_PlaneNormal ); this->Modified(); } void Plane::GetResolution( int &xR, int &yR ) const { m_PlaneSource->GetResolution( xR, yR ); } bool Plane::IsInside( const Point3D &/*worldPoint*/ ) const { // Plane does not have a volume return false; } ScalarType Plane::GetVolume() { // Plane does not have a volume return 0.0; } } diff --git a/Modules/DiffusionImaging/Connectomics/Algorithms/mitkConnectomicsNetworkCreator.cpp b/Modules/DiffusionImaging/Connectomics/Algorithms/mitkConnectomicsNetworkCreator.cpp index 532263b49b..43e32282a7 100644 --- a/Modules/DiffusionImaging/Connectomics/Algorithms/mitkConnectomicsNetworkCreator.cpp +++ b/Modules/DiffusionImaging/Connectomics/Algorithms/mitkConnectomicsNetworkCreator.cpp @@ -1,864 +1,864 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkConnectomicsNetworkCreator.h" #include #include #include "mitkConnectomicsConstantsManager.h" #include "mitkImageAccessByItk.h" #include "mitkImageStatisticsHolder.h" #include "mitkImageCast.h" #include "itkImageRegionIteratorWithIndex.h" // VTK #include #include #include mitk::ConnectomicsNetworkCreator::ConnectomicsNetworkCreator() : m_FiberBundle() , m_Segmentation() , m_ConNetwork( mitk::ConnectomicsNetwork::New() ) , idCounter(0) , m_LabelToVertexMap() , m_LabelToNodePropertyMap() , allowLoops( false ) , m_UseCoMCoordinates( false ) , m_LabelsToCoordinatesMap() , m_MappingStrategy( EndElementPositionAvoidingWhiteMatter ) , m_EndPointSearchRadius( 10.0 ) , m_ZeroLabelInvalid( true ) , m_AbortConnection( false ) { } mitk::ConnectomicsNetworkCreator::ConnectomicsNetworkCreator( mitk::Image::Pointer segmentation, mitk::FiberBundleX::Pointer fiberBundle ) : m_FiberBundle(fiberBundle) , m_Segmentation(segmentation) , m_ConNetwork( mitk::ConnectomicsNetwork::New() ) , idCounter(0) , m_LabelToVertexMap() , m_LabelToNodePropertyMap() , allowLoops( false ) , m_LabelsToCoordinatesMap() , m_MappingStrategy( EndElementPositionAvoidingWhiteMatter ) , m_EndPointSearchRadius( 10.0 ) , m_ZeroLabelInvalid( true ) , m_AbortConnection( false ) { mitk::CastToItkImage( segmentation, m_SegmentationItk ); } mitk::ConnectomicsNetworkCreator::~ConnectomicsNetworkCreator() { } void mitk::ConnectomicsNetworkCreator::SetFiberBundle(mitk::FiberBundleX::Pointer fiberBundle) { m_FiberBundle = fiberBundle; } void mitk::ConnectomicsNetworkCreator::SetSegmentation(mitk::Image::Pointer segmentation) { m_Segmentation = segmentation; mitk::CastToItkImage( segmentation, m_SegmentationItk ); } itk::Point mitk::ConnectomicsNetworkCreator::GetItkPoint(double point[3]) { itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; return itkPoint; } void mitk::ConnectomicsNetworkCreator::CreateNetworkFromFibersAndSegmentation() { //empty graph m_ConNetwork = mitk::ConnectomicsNetwork::New(); m_LabelToVertexMap.clear(); m_LabelToNodePropertyMap.clear(); idCounter = 0; vtkSmartPointer fiberPolyData = m_FiberBundle->GetFiberPolyData(); vtkSmartPointer vLines = fiberPolyData->GetLines(); vLines->InitTraversal(); int numFibers = m_FiberBundle->GetNumFibers(); for( int fiberID( 0 ); fiberID < numFibers; fiberID++ ) { vtkIdType numPointsInCell(0); vtkIdType* pointsInCell(NULL); vLines->GetNextCell ( numPointsInCell, pointsInCell ); TractType::Pointer singleTract = TractType::New(); for( int pointInCellID( 0 ); pointInCellID < numPointsInCell ; pointInCellID++) { // push back point PointType point = GetItkPoint( fiberPolyData->GetPoint( pointsInCell[ pointInCellID ] ) ); singleTract->InsertElement( singleTract->Size(), point ); } if ( singleTract && ( singleTract->Size() > 0 ) ) { AddConnectionToNetwork( ReturnAssociatedVertexPairForLabelPair( ReturnLabelForFiberTract( singleTract, m_MappingStrategy ) ) ); m_AbortConnection = false; } } // Prune unconnected nodes //m_ConNetwork->PruneUnconnectedSingleNodes(); // provide network with geometry m_ConNetwork->SetGeometry( dynamic_cast(m_Segmentation->GetGeometry()->Clone().GetPointer()) ); m_ConNetwork->UpdateBounds(); m_ConNetwork->SetIsModified( true ); MBI_INFO << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_INFO_NETWORK_CREATED; } void mitk::ConnectomicsNetworkCreator::AddConnectionToNetwork(ConnectionType newConnection) { if( m_AbortConnection ) { MITK_DEBUG << "Connection aborted"; return; } VertexType vertexA = newConnection.first; VertexType vertexB = newConnection.second; // check for loops (if they are not allowed) if( allowLoops || !( vertexA == vertexB ) ) { // If the connection already exists, increment weight, else create connection if ( m_ConNetwork->EdgeExists( vertexA, vertexB ) ) { m_ConNetwork->IncreaseEdgeWeight( vertexA, vertexB ); } else { m_ConNetwork->AddEdge( vertexA, vertexB ); } } } mitk::ConnectomicsNetworkCreator::VertexType mitk::ConnectomicsNetworkCreator::ReturnAssociatedVertexForLabel( ImageLabelType label ) { if( m_ZeroLabelInvalid && ( label == 0 ) ) { m_AbortConnection = true; return ULONG_MAX; } // if label is not known, create entry if( ! ( m_LabelToVertexMap.count( label ) > 0 ) ) { VertexType newVertex = m_ConNetwork->AddVertex( idCounter ); idCounter++; SupplyVertexWithInformation(label, newVertex); m_LabelToVertexMap.insert( std::pair< ImageLabelType, VertexType >( label, newVertex ) ); } //return associated vertex return m_LabelToVertexMap.find( label )->second; } mitk::ConnectomicsNetworkCreator::ConnectionType mitk::ConnectomicsNetworkCreator::ReturnAssociatedVertexPairForLabelPair( ImageLabelPairType labelpair ) { //hand both labels through to the single label function ConnectionType connection( ReturnAssociatedVertexForLabel(labelpair.first), ReturnAssociatedVertexForLabel(labelpair.second) ); return connection; } mitk::ConnectomicsNetworkCreator::ImageLabelPairType mitk::ConnectomicsNetworkCreator::ReturnLabelForFiberTract( TractType::Pointer singleTract, mitk::ConnectomicsNetworkCreator::MappingStrategy strategy) { switch( strategy ) { case EndElementPosition: { return EndElementPositionLabel( singleTract ); } case JustEndPointVerticesNoLabel: { return JustEndPointVerticesNoLabelTest( singleTract ); } case EndElementPositionAvoidingWhiteMatter: { return EndElementPositionLabelAvoidingWhiteMatter( singleTract ); } case PrecomputeAndDistance: { return PrecomputeVertexLocationsBySegmentation( singleTract ); } } // To remove warnings, this code should never be reached MBI_ERROR << mitk::ConnectomicsConstantsManager::CONNECTOMICS_ERROR_INVALID_MAPPING; ImageLabelPairType nullPair( 0,0 ); return nullPair; } mitk::ConnectomicsNetworkCreator::ImageLabelPairType mitk::ConnectomicsNetworkCreator::EndElementPositionLabel( TractType::Pointer singleTract ) { ImageLabelPairType labelpair; {// Note: .fib image tracts are safed using index coordinates mitk::Point3D firstElementFiberCoord, lastElementFiberCoord; mitk::Point3D firstElementSegCoord, lastElementSegCoord; - mitk::Index3D firstElementSegIndex, lastElementSegIndex; + itk::Index<3> firstElementSegIndex, lastElementSegIndex; if( singleTract->front().Size() != 3 ) { MBI_ERROR << mitk::ConnectomicsConstantsManager::CONNECTOMICS_ERROR_INVALID_DIMENSION_NEED_3; } for( unsigned int index = 0; index < singleTract->front().Size(); index++ ) { firstElementFiberCoord.SetElement( index, singleTract->front().GetElement( index ) ); lastElementFiberCoord.SetElement( index, singleTract->back().GetElement( index ) ); } // convert from fiber index coordinates to segmentation index coordinates FiberToSegmentationCoords( firstElementFiberCoord, firstElementSegCoord ); FiberToSegmentationCoords( lastElementFiberCoord, lastElementSegCoord ); for( int index = 0; index < 3; index++ ) { firstElementSegIndex.SetElement( index, firstElementSegCoord.GetElement( index ) ); lastElementSegIndex.SetElement( index, lastElementSegCoord.GetElement( index ) ); } int firstLabel = m_SegmentationItk->GetPixel(firstElementSegIndex); int lastLabel = m_SegmentationItk->GetPixel(lastElementSegIndex ); labelpair.first = firstLabel; labelpair.second = lastLabel; // Add property to property map CreateNewNode( firstLabel, firstElementSegIndex, m_UseCoMCoordinates ); CreateNewNode( lastLabel, lastElementSegIndex, m_UseCoMCoordinates ); } return labelpair; } mitk::ConnectomicsNetworkCreator::ImageLabelPairType mitk::ConnectomicsNetworkCreator::PrecomputeVertexLocationsBySegmentation( TractType::Pointer /*singleTract*/ ) { ImageLabelPairType labelpair; return labelpair; } mitk::ConnectomicsNetworkCreator::ImageLabelPairType mitk::ConnectomicsNetworkCreator::EndElementPositionLabelAvoidingWhiteMatter( TractType::Pointer singleTract ) { ImageLabelPairType labelpair; {// Note: .fib image tracts are safed using index coordinates mitk::Point3D firstElementFiberCoord, lastElementFiberCoord; mitk::Point3D firstElementSegCoord, lastElementSegCoord; - mitk::Index3D firstElementSegIndex, lastElementSegIndex; + itk::Index<3> firstElementSegIndex, lastElementSegIndex; if( singleTract->front().Size() != 3 ) { MBI_ERROR << mitk::ConnectomicsConstantsManager::CONNECTOMICS_ERROR_INVALID_DIMENSION_NEED_3; } for( unsigned int index = 0; index < singleTract->front().Size(); index++ ) { firstElementFiberCoord.SetElement( index, singleTract->front().GetElement( index ) ); lastElementFiberCoord.SetElement( index, singleTract->back().GetElement( index ) ); } // convert from fiber index coordinates to segmentation index coordinates FiberToSegmentationCoords( firstElementFiberCoord, firstElementSegCoord ); FiberToSegmentationCoords( lastElementFiberCoord, lastElementSegCoord ); for( int index = 0; index < 3; index++ ) { firstElementSegIndex.SetElement( index, firstElementSegCoord.GetElement( index ) ); lastElementSegIndex.SetElement( index, lastElementSegCoord.GetElement( index ) ); } int firstLabel = m_SegmentationItk->GetPixel(firstElementSegIndex); int lastLabel = m_SegmentationItk->GetPixel(lastElementSegIndex ); // Check whether the labels belong to the white matter (which means, that the fibers ended early) bool extendFront(false), extendEnd(false), retractFront(false), retractEnd(false); extendFront = !IsNonWhiteMatterLabel( firstLabel ); extendEnd = !IsNonWhiteMatterLabel( lastLabel ); retractFront = IsBackgroundLabel( firstLabel ); retractEnd = IsBackgroundLabel( lastLabel ); //if( extendFront || extendEnd ) //{ //MBI_INFO << "Before Start: " << firstLabel << " at " << firstElementSegIndex[ 0 ] << " " << firstElementSegIndex[ 1 ] << " " << firstElementSegIndex[ 2 ] << " End: " << lastLabel << " at " << lastElementSegIndex[ 0 ] << " " << lastElementSegIndex[ 1 ] << " " << lastElementSegIndex[ 2 ]; //} if ( extendFront ) { std::vector< int > indexVectorOfPointsToUse; //Use first two points for direction indexVectorOfPointsToUse.push_back( 1 ); indexVectorOfPointsToUse.push_back( 0 ); // label and coordinate temp storage int tempLabel( firstLabel ); - mitk::Index3D tempIndex = firstElementSegIndex; + itk::Index<3> tempIndex = firstElementSegIndex; LinearExtensionUntilGreyMatter( indexVectorOfPointsToUse, singleTract, tempLabel, tempIndex ); firstLabel = tempLabel; firstElementSegIndex = tempIndex; } if ( extendEnd ) { std::vector< int > indexVectorOfPointsToUse; //Use last two points for direction indexVectorOfPointsToUse.push_back( singleTract->Size() - 2 ); indexVectorOfPointsToUse.push_back( singleTract->Size() - 1 ); // label and coordinate temp storage int tempLabel( lastLabel ); - mitk::Index3D tempIndex = lastElementSegIndex; + itk::Index<3> tempIndex = lastElementSegIndex; LinearExtensionUntilGreyMatter( indexVectorOfPointsToUse, singleTract, tempLabel, tempIndex ); lastLabel = tempLabel; lastElementSegIndex = tempIndex; } if ( retractFront ) { // label and coordinate temp storage int tempLabel( firstLabel ); - mitk::Index3D tempIndex = firstElementSegIndex; + itk::Index<3> tempIndex = firstElementSegIndex; RetractionUntilBrainMatter( true, singleTract, tempLabel, tempIndex ); firstLabel = tempLabel; firstElementSegIndex = tempIndex; } if ( retractEnd ) { // label and coordinate temp storage int tempLabel( lastLabel ); - mitk::Index3D tempIndex = lastElementSegIndex; + itk::Index<3> tempIndex = lastElementSegIndex; RetractionUntilBrainMatter( false, singleTract, tempLabel, tempIndex ); lastLabel = tempLabel; lastElementSegIndex = tempIndex; } //if( extendFront || extendEnd ) //{ // MBI_INFO << "After Start: " << firstLabel << " at " << firstElementSegIndex[ 0 ] << " " << firstElementSegIndex[ 1 ] << " " << firstElementSegIndex[ 2 ] << " End: " << lastLabel << " at " << lastElementSegIndex[ 0 ] << " " << lastElementSegIndex[ 1 ] << " " << lastElementSegIndex[ 2 ]; //} labelpair.first = firstLabel; labelpair.second = lastLabel; // Add property to property map CreateNewNode( firstLabel, firstElementSegIndex, m_UseCoMCoordinates ); CreateNewNode( lastLabel, lastElementSegIndex, m_UseCoMCoordinates ); } return labelpair; } mitk::ConnectomicsNetworkCreator::ImageLabelPairType mitk::ConnectomicsNetworkCreator::JustEndPointVerticesNoLabelTest( TractType::Pointer singleTract ) { ImageLabelPairType labelpair; {// Note: .fib image tracts are safed using index coordinates mitk::Point3D firstElementFiberCoord, lastElementFiberCoord; mitk::Point3D firstElementSegCoord, lastElementSegCoord; - mitk::Index3D firstElementSegIndex, lastElementSegIndex; + itk::Index<3> firstElementSegIndex, lastElementSegIndex; if( singleTract->front().Size() != 3 ) { MBI_ERROR << mitk::ConnectomicsConstantsManager::CONNECTOMICS_ERROR_INVALID_DIMENSION_NEED_3; } for( unsigned int index = 0; index < singleTract->front().Size(); index++ ) { firstElementFiberCoord.SetElement( index, singleTract->front().GetElement( index ) ); lastElementFiberCoord.SetElement( index, singleTract->back().GetElement( index ) ); } // convert from fiber index coordinates to segmentation index coordinates FiberToSegmentationCoords( firstElementFiberCoord, firstElementSegCoord ); FiberToSegmentationCoords( lastElementFiberCoord, lastElementSegCoord ); for( int index = 0; index < 3; index++ ) { firstElementSegIndex.SetElement( index, firstElementSegCoord.GetElement( index ) ); lastElementSegIndex.SetElement( index, lastElementSegCoord.GetElement( index ) ); } int firstLabel = 1 * firstElementSegIndex[ 0 ] + 1000 * firstElementSegIndex[ 1 ] + 1000000 * firstElementSegIndex[ 2 ]; int lastLabel = 1 * firstElementSegIndex[ 0 ] + 1000 * firstElementSegIndex[ 1 ] + 1000000 * firstElementSegIndex[ 2 ]; labelpair.first = firstLabel; labelpair.second = lastLabel; // Add property to property map CreateNewNode( firstLabel, firstElementSegIndex, m_UseCoMCoordinates ); CreateNewNode( lastLabel, lastElementSegIndex, m_UseCoMCoordinates ); } return labelpair; } void mitk::ConnectomicsNetworkCreator::SupplyVertexWithInformation( ImageLabelType& label, VertexType& vertex ) { // supply a vertex with the additional information belonging to the label // TODO: Implement additional information acquisition m_ConNetwork->SetLabel( vertex, m_LabelToNodePropertyMap.find( label )->second.label ); m_ConNetwork->SetCoordinates( vertex, m_LabelToNodePropertyMap.find( label )->second.coordinates ); } std::string mitk::ConnectomicsNetworkCreator::LabelToString( ImageLabelType& label ) { int tempInt = (int) label; std::stringstream ss;//create a stringstream std::string tempString; ss << tempInt;//add number to the stream tempString = ss.str(); return tempString;//return a string with the contents of the stream } mitk::ConnectomicsNetwork::Pointer mitk::ConnectomicsNetworkCreator::GetNetwork() { return m_ConNetwork; } void mitk::ConnectomicsNetworkCreator::FiberToSegmentationCoords( mitk::Point3D& fiberCoord, mitk::Point3D& segCoord ) { mitk::Point3D tempPoint; // convert from fiber index coordinates to segmentation index coordinates m_FiberBundle->GetGeometry()->IndexToWorld( fiberCoord, tempPoint ); m_Segmentation->GetGeometry()->WorldToIndex( tempPoint, segCoord ); } void mitk::ConnectomicsNetworkCreator::SegmentationToFiberCoords( mitk::Point3D& segCoord, mitk::Point3D& fiberCoord ) { mitk::Point3D tempPoint; // convert from fiber index coordinates to segmentation index coordinates m_Segmentation->GetGeometry()->IndexToWorld( segCoord, tempPoint ); m_FiberBundle->GetGeometry()->WorldToIndex( tempPoint, fiberCoord ); } bool mitk::ConnectomicsNetworkCreator::IsNonWhiteMatterLabel( int labelInQuestion ) { bool isWhite( false ); isWhite = ( ( labelInQuestion == freesurfer_Left_Cerebral_White_Matter ) || ( labelInQuestion == freesurfer_Left_Cerebellum_White_Matter ) || ( labelInQuestion == freesurfer_Right_Cerebral_White_Matter ) || ( labelInQuestion == freesurfer_Right_Cerebellum_White_Matter )|| ( labelInQuestion == freesurfer_Left_Cerebellum_Cortex ) || ( labelInQuestion == freesurfer_Right_Cerebellum_Cortex ) || ( labelInQuestion == freesurfer_Brain_Stem ) ); return !isWhite; } bool mitk::ConnectomicsNetworkCreator::IsBackgroundLabel( int labelInQuestion ) { bool isBackground( false ); isBackground = ( labelInQuestion == 0 ); return isBackground; } void mitk::ConnectomicsNetworkCreator::LinearExtensionUntilGreyMatter( std::vector & indexVectorOfPointsToUse, TractType::Pointer singleTract, int & label, - mitk::Index3D & mitkIndex ) + itk::Index<3> & mitkIndex ) { if( indexVectorOfPointsToUse.size() > singleTract->Size() ) { MBI_WARN << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_MORE_POINTS_THAN_PRESENT; return; } if( indexVectorOfPointsToUse.size() < 2 ) { MBI_WARN << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_ESTIMATING_LESS_THAN_2; return; } for( unsigned int index( 0 ); index < indexVectorOfPointsToUse.size(); index++ ) { if( indexVectorOfPointsToUse[ index ] < 0 ) { MBI_WARN << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_ESTIMATING_BEYOND_START; return; } if( (unsigned int)indexVectorOfPointsToUse[ index ] > singleTract->Size() ) { MBI_WARN << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_ESTIMATING_BEYOND_END; return; } } mitk::Point3D startPoint, endPoint; std::vector< double > differenceVector; differenceVector.resize( singleTract->front().Size() ); { // which points to use, currently only last two //TODO correct using all points int endPointIndex = indexVectorOfPointsToUse.size() - 1; int startPointIndex = indexVectorOfPointsToUse.size() - 2; // convert to segmentation coords mitk::Point3D startFiber, endFiber; for( unsigned int index = 0; index < singleTract->front().Size(); index++ ) { endFiber.SetElement( index, singleTract->GetElement( indexVectorOfPointsToUse[ endPointIndex ] ).GetElement( index ) ); startFiber.SetElement( index, singleTract->GetElement( indexVectorOfPointsToUse[ startPointIndex ] ).GetElement( index ) ); } FiberToSegmentationCoords( endFiber, endPoint ); FiberToSegmentationCoords( startFiber, startPoint ); // calculate straight line for( unsigned int index = 0; index < singleTract->front().Size(); index++ ) { differenceVector[ index ] = endPoint.GetElement( index ) - startPoint.GetElement( index ); } // normalizing direction vector double length( 0.0 ); double sum( 0.0 ); for( unsigned int index = 0; index < differenceVector.size() ; index++ ) { sum = sum + differenceVector[ index ] * differenceVector[ index ]; } length = std::sqrt( sum ); for( unsigned int index = 0; index < differenceVector.size() ; index++ ) { differenceVector[ index ] = differenceVector[ index ] / length; } // follow line until first non white matter label - mitk::Index3D tempIndex; + itk::Index<3> tempIndex; int tempLabel( label ); bool keepOn( true ); itk::ImageRegion<3> itkRegion = m_SegmentationItk->GetLargestPossibleRegion(); for( int parameter( 0 ) ; keepOn ; parameter++ ) { if( parameter > 1000 ) { MBI_WARN << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_DID_NOT_FIND_WHITE; break; } for( int index( 0 ); index < 3; index++ ) { tempIndex.SetElement( index, endPoint.GetElement( index ) + parameter * differenceVector[ index ] ); } if( itkRegion.IsInside( tempIndex ) ) { tempLabel = m_SegmentationItk->GetPixel( tempIndex ); } else { tempLabel = -1; } if( IsNonWhiteMatterLabel( tempLabel ) ) { if( tempLabel < 1 ) { keepOn = false; //MBI_WARN << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_NOT_EXTEND_TO_WHITE; } else { label = tempLabel; mitkIndex = tempIndex; keepOn = false; } } } } } void mitk::ConnectomicsNetworkCreator::RetractionUntilBrainMatter( bool retractFront, TractType::Pointer singleTract, - int & label, mitk::Index3D & mitkIndex ) + int & label, itk::Index<3> & mitkIndex ) { int retractionStartIndex( singleTract->Size() - 1 ); int retractionStepIndexSize( -1 ); int retractionTerminationIndex( 0 ); if( retractFront ) { retractionStartIndex = 0; retractionStepIndexSize = 1; retractionTerminationIndex = singleTract->Size() - 1; } int currentRetractionIndex = retractionStartIndex; bool keepRetracting( true ); mitk::Point3D currentPoint, nextPoint; std::vector< double > differenceVector; differenceVector.resize( singleTract->front().Size() ); while( keepRetracting && ( currentRetractionIndex != retractionTerminationIndex ) ) { // convert to segmentation coords mitk::Point3D currentPointFiberCoord, nextPointFiberCoord; for( unsigned int index = 0; index < singleTract->front().Size(); index++ ) { currentPointFiberCoord.SetElement( index, singleTract->GetElement( currentRetractionIndex ).GetElement( index ) ); nextPointFiberCoord.SetElement( index, singleTract->GetElement( currentRetractionIndex + retractionStepIndexSize ).GetElement( index ) ); } FiberToSegmentationCoords( currentPointFiberCoord, currentPoint ); FiberToSegmentationCoords( nextPointFiberCoord, nextPoint ); // calculate straight line for( unsigned int index = 0; index < singleTract->front().Size(); index++ ) { differenceVector[ index ] = nextPoint.GetElement( index ) - currentPoint.GetElement( index ); } // calculate length of direction vector double length( 0.0 ); double sum( 0.0 ); for( unsigned int index = 0; index < differenceVector.size() ; index++ ) { sum = sum + differenceVector[ index ] * differenceVector[ index ]; } length = std::sqrt( sum ); // retract - mitk::Index3D tempIndex; + itk::Index<3> tempIndex; int tempLabel( label ); for( int parameter( 0 ) ; parameter < length ; parameter++ ) { for( int index( 0 ); index < 3; index++ ) { tempIndex.SetElement( index, currentPoint.GetElement( index ) + ( 1.0 + parameter ) / ( 1.0 + length ) * differenceVector[ index ] ); } tempLabel = m_SegmentationItk->GetPixel( tempIndex ); if( !IsBackgroundLabel( tempLabel ) ) { // check whether result is within the search space { mitk::Point3D endPoint, foundPointSegmentation, foundPointFiber; for( unsigned int index = 0; index < singleTract->front().Size(); index++ ) { // this is in fiber (world) coordinates endPoint.SetElement( index, singleTract->GetElement( retractionStartIndex ).GetElement( index ) ); } for( int index( 0 ); index < 3; index++ ) { foundPointSegmentation.SetElement( index, currentPoint.GetElement( index ) + ( 1.0 + parameter ) / ( 1.0 + length ) * differenceVector[ index ] ); } SegmentationToFiberCoords( foundPointSegmentation, foundPointFiber ); std::vector< double > finalDistance; finalDistance.resize( singleTract->front().Size() ); for( unsigned int index = 0; index < singleTract->front().Size(); index++ ) { finalDistance[ index ] = foundPointFiber.GetElement( index ) - endPoint.GetElement( index ); } // calculate length of direction vector double finalLength( 0.0 ); double finalSum( 0.0 ); for( unsigned int index = 0; index < finalDistance.size() ; index++ ) { finalSum = finalSum + finalDistance[ index ] * finalDistance[ index ]; } finalLength = std::sqrt( finalSum ); if( finalLength > m_EndPointSearchRadius ) { // the found point was not within the search space return; } } label = tempLabel; mitkIndex = tempIndex; return; } // hit next point without finding brain matter currentRetractionIndex = currentRetractionIndex + retractionStepIndexSize; if( ( currentRetractionIndex < 1 ) || ( (unsigned int)currentRetractionIndex > ( singleTract->Size() - 2 ) ) ) { keepRetracting = false; } } } } void mitk::ConnectomicsNetworkCreator::CalculateCenterOfMass() { const int dimensions = 3; int max = m_Segmentation->GetStatistics()->GetScalarValueMax(); int min = m_Segmentation->GetStatistics()->GetScalarValueMin(); int range = max - min +1; // each label owns a vector of coordinates std::vector< std::vector< std::vector< double> > > coordinatesPerLabelVector; coordinatesPerLabelVector.resize( range ); itk::ImageRegionIteratorWithIndex it_itkImage( m_SegmentationItk, m_SegmentationItk->GetLargestPossibleRegion() ); for( it_itkImage.GoToBegin(); !it_itkImage.IsAtEnd(); ++it_itkImage ) { std::vector< double > coordinates; coordinates.resize(dimensions); itk::Index< dimensions > index = it_itkImage.GetIndex(); for( int loop(0); loop < dimensions; loop++) { coordinates.at( loop ) = index.GetElement( loop ); } // add the coordinates to the corresponding label vector coordinatesPerLabelVector.at( it_itkImage.Value() - min ).push_back( coordinates ); } for(int currentIndex(0), currentLabel( min ); currentIndex < range; currentIndex++, currentLabel++ ) { std::vector< double > currentCoordinates; currentCoordinates.resize(dimensions); int numberOfPoints = coordinatesPerLabelVector.at( currentIndex ).size(); std::vector< double > sumCoords; sumCoords.resize( dimensions ); for( int loop(0); loop < numberOfPoints; loop++ ) { for( int loopDimension( 0 ); loopDimension < dimensions; loopDimension++ ) { sumCoords.at( loopDimension ) += coordinatesPerLabelVector.at( currentIndex ).at( loop ).at( loopDimension ); } } for( int loopDimension( 0 ); loopDimension < dimensions; loopDimension++ ) { currentCoordinates.at( loopDimension ) = sumCoords.at( loopDimension ) / numberOfPoints; } m_LabelsToCoordinatesMap.insert( std::pair< int, std::vector >( currentLabel, currentCoordinates ) ); } //can now use center of mass coordinates m_UseCoMCoordinates = true; } std::vector< double > mitk::ConnectomicsNetworkCreator::GetCenterOfMass( int label ) { // if label is not known, warn if( ! ( m_LabelsToCoordinatesMap.count( label ) > 0 ) ) { MITK_ERROR << "Label " << label << " not found. Could not return coordinates."; std::vector< double > nullVector; nullVector.resize(3); return nullVector; } //return associated coordinates return m_LabelsToCoordinatesMap.find( label )->second; } -void mitk::ConnectomicsNetworkCreator::CreateNewNode( int label, mitk::Index3D index, bool useCoM ) +void mitk::ConnectomicsNetworkCreator::CreateNewNode( int label, itk::Index<3> index, bool useCoM ) { // Only create node if it does not exist yet if( ! ( m_LabelToNodePropertyMap.count( label ) > 0 ) ) { NetworkNode newNode; newNode.coordinates.resize( 3 ); if( !useCoM ) { for( unsigned int loop = 0; loop < newNode.coordinates.size() ; loop++ ) { newNode.coordinates[ loop ] = index[ loop ] ; } } else { std::vector labelCoords = GetCenterOfMass( label ); for( unsigned int loop = 0; loop < newNode.coordinates.size() ; loop++ ) { newNode.coordinates[ loop ] = labelCoords.at( loop ) ; } } newNode.label = LabelToString( label ); m_LabelToNodePropertyMap.insert( std::pair< ImageLabelType, NetworkNode >( label, newNode ) ); } } diff --git a/Modules/DiffusionImaging/Connectomics/Algorithms/mitkConnectomicsNetworkCreator.h b/Modules/DiffusionImaging/Connectomics/Algorithms/mitkConnectomicsNetworkCreator.h index d6e00b460f..8a956f4c8b 100644 --- a/Modules/DiffusionImaging/Connectomics/Algorithms/mitkConnectomicsNetworkCreator.h +++ b/Modules/DiffusionImaging/Connectomics/Algorithms/mitkConnectomicsNetworkCreator.h @@ -1,249 +1,249 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkConnectomicsNetworkCreator_h #define mitkConnectomicsNetworkCreator_h #include #include #include #include "mitkCommon.h" #include "mitkImage.h" #include "mitkFiberBundleX.h" #include "mitkConnectomicsNetwork.h" #include namespace mitk { /** * \brief Creates connectomics networks from fibers and parcellation * * This class needs a parcellation image and a fiber image to be set. Then you can create * a connectomics network from the two, using different strategies. */ class MitkConnectomics_EXPORT ConnectomicsNetworkCreator : public itk::Object { public: /** Enum for different ways to create the mapping from fibers to network */ enum MappingStrategy { EndElementPosition, EndElementPositionAvoidingWhiteMatter, JustEndPointVerticesNoLabel, PrecomputeAndDistance }; /** Standard class typedefs. */ /** Method for creation through the object factory. */ mitkClassMacro(ConnectomicsNetworkCreator, itk::Object); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Type for Images **/ typedef itk::Image ITKImageType; /** Types for the standardized Tract **/ typedef itk::Point PointType; typedef itk::VectorContainer TractType; typedef itk::VectorContainer< unsigned int, TractType::Pointer > TractContainerType; //init via smartpointer /** Types for Network **/ typedef mitk::ConnectomicsNetwork::VertexDescriptorType VertexType; typedef mitk::ConnectomicsNetwork::EdgeDescriptorType EdgeType; typedef mitk::ConnectomicsNetwork::NetworkNode NetworkNode; typedef std::pair< VertexType, VertexType > ConnectionType; /** Types for labels **/ typedef int ImageLabelType; typedef std::pair< ImageLabelType, ImageLabelType > ImageLabelPairType; /** Given a fiber bundle and a parcellation are set, this will create a network from both */ void CreateNetworkFromFibersAndSegmentation(); void SetFiberBundle(mitk::FiberBundleX::Pointer fiberBundle); void SetSegmentation(mitk::Image::Pointer segmentation); mitk::ConnectomicsNetwork::Pointer GetNetwork(); itkSetMacro(MappingStrategy, MappingStrategy); itkSetMacro(EndPointSearchRadius, double); itkSetMacro(ZeroLabelInvalid, bool); /** \brief Calculate the locations of vertices * * Calculate the center of mass for each label and store the information. This will need a set parcellation image. * Unless this function is called the first location where a label is encountered will be used. After calling this function * the center of mass will be used instead. */ void CalculateCenterOfMass(); protected: //////////////////// Functions /////////////////////// ConnectomicsNetworkCreator(); ConnectomicsNetworkCreator( mitk::Image::Pointer segmentation, mitk::FiberBundleX::Pointer fiberBundle ); ~ConnectomicsNetworkCreator(); /** Add a connection to the network */ void AddConnectionToNetwork(ConnectionType newConnection); /** Determine if a label is already identified with a vertex, otherwise create a new one */ VertexType ReturnAssociatedVertexForLabel( ImageLabelType label ); /** Return the vertexes associated with a pair of labels */ ConnectionType ReturnAssociatedVertexPairForLabelPair( ImageLabelPairType labelpair ); /** Return the pair of labels which identify the areas connected by a single fiber */ ImageLabelPairType ReturnLabelForFiberTract( TractType::Pointer singleTract, MappingStrategy strategy ); /** Assign the additional information which should be part of the vertex */ void SupplyVertexWithInformation( ImageLabelType& label, VertexType& vertex ); /** Create a string from the label */ std::string LabelToString( ImageLabelType& label ); /** Check whether the label in question belongs to white matter according to the freesurfer table */ bool IsNonWhiteMatterLabel( int labelInQuestion ); /** Check whether the label in question belongs to background according to the freesurfer table */ bool IsBackgroundLabel( int labelInQuestion ); /** Extend a straight line through the given points and look for the first non white matter label It will try extend in the direction of the points in the vector so a vector {B,C} will result in extending from C in the direction C-B */ void LinearExtensionUntilGreyMatter( std::vector & indexVectorOfPointsToUse, TractType::Pointer singleTract, - int & label, mitk::Index3D & mitkIndex ); + int & label, itk::Index<3> & mitkIndex ); /** Retract fiber until the first brain matter label is hit The bool parameter controls whether the front or the end is retracted */ void RetractionUntilBrainMatter( bool retractFront, TractType::Pointer singleTract, - int & label, mitk::Index3D & mitkIndex ); + int & label, itk::Index<3> & mitkIndex ); /** \brief Get the location of the center of mass for a specific label * This can throw an exception if the label is not found. */ std::vector< double > GetCenterOfMass( int label ); /** Convert point to itk point */ itk::Point GetItkPoint(double point[3]); /** \brief Creates a new node * * A new node will be created, using the label and either the supplied coordinates * or the center of mass coordinates, depending on the supplied bool. */ - void CreateNewNode( int label, mitk::Index3D, bool useIndex ); + void CreateNewNode( int label, itk::Index<3>, bool useIndex ); ///////// Mapping strategies ////////// /** Use the position of the end and starting element only to map to labels Map a fiber to a vertex by taking the value of the parcellation image at the same world coordinates as the last and first element of the tract.*/ ImageLabelPairType EndElementPositionLabel( TractType::Pointer singleTract ); /** Map by distance between elements and vertices depending on their volume First go through the parcellation and compute the coordinates of the future vertices. Assign a radius according on their volume. Then map an edge to a label by considering the nearest vertices and comparing the distance to them to their radii. */ ImageLabelPairType PrecomputeVertexLocationsBySegmentation( TractType::Pointer singleTract ); /** Use the position of the end and starting element only to map to labels Just take first and last position, no labelling, nothing */ ImageLabelPairType JustEndPointVerticesNoLabelTest( TractType::Pointer singleTract ); /** Use the position of the end and starting element unless it is in white matter, then search for nearby parcellation to map to labels Map a fiber to a vertex by taking the value of the parcellation image at the same world coordinates as the last and first element of the tract. If this happens to be white matter, then try to extend the fiber in a line and take the first non-white matter parcel, that is intersected. */ ImageLabelPairType EndElementPositionLabelAvoidingWhiteMatter( TractType::Pointer singleTract ); ///////// Conversions ////////// /** Convert fiber index to segmentation index coordinates */ void FiberToSegmentationCoords( mitk::Point3D& fiberCoord, mitk::Point3D& segCoord ); /** Convert segmentation index to fiber index coordinates */ void SegmentationToFiberCoords( mitk::Point3D& segCoord, mitk::Point3D& fiberCoord ); /////////////////////// Variables //////////////////////// mitk::FiberBundleX::Pointer m_FiberBundle; mitk::Image::Pointer m_Segmentation; ITKImageType::Pointer m_SegmentationItk; // the graph itself mitk::ConnectomicsNetwork::Pointer m_ConNetwork; // the id counter int idCounter; // the map mapping labels to vertices std::map< ImageLabelType, VertexType > m_LabelToVertexMap; // mapping labels to additional information std::map< ImageLabelType, NetworkNode > m_LabelToNodePropertyMap; // toggles whether edges between a node and itself can exist bool allowLoops; // toggles whether to use the center of mass coordinates bool m_UseCoMCoordinates; // stores the coordinates of labels std::map< int, std::vector< double> > m_LabelsToCoordinatesMap; // the straty to use for mapping MappingStrategy m_MappingStrategy; // search radius for finding a non white matter/background area. Should be in mm double m_EndPointSearchRadius; // toggles whether a node with the label 0 may be present bool m_ZeroLabelInvalid; // used internally to communicate a connection should not be added if the a problem // is encountered while adding it bool m_AbortConnection; //////////////////////// IDs //////////////////////////// // These IDs are the freesurfer ids used in parcellation static const int freesurfer_Left_Cerebral_White_Matter = 2; static const int freesurfer_Left_Cerebellum_White_Matter = 7; static const int freesurfer_Left_Cerebellum_Cortex = 8; static const int freesurfer_Brain_Stem = 16; static const int freesurfer_Right_Cerebral_White_Matter = 41; static const int freesurfer_Right_Cerebellum_White_Matter = 46; static const int freesurfer_Right_Cerebellum_Cortex = 47; }; }// end namespace mitk #endif // _mitkConnectomicsNetworkCreator_H_INCLUDED diff --git a/Modules/DiffusionImaging/DiffusionCore/mitkDiffusionFunctionCollection.cpp b/Modules/DiffusionImaging/DiffusionCore/mitkDiffusionFunctionCollection.cpp index c9d976a5f2..4e17e88feb 100644 --- a/Modules/DiffusionImaging/DiffusionCore/mitkDiffusionFunctionCollection.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/mitkDiffusionFunctionCollection.cpp @@ -1,253 +1,253 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDiffusionFunctionCollection.h" #include -#include "mitkVector.h" +#include "mitkNumericTypes.h" // for Windows #ifndef M_PI #define M_PI 3.14159265358979323846 #endif // Namespace ::SH #include #include #include // Namespace ::Gradients #include "itkVectorContainer.h" #include "vnl/vnl_vector.h" //------------------------- SH-function ------------------------------------ double mitk::sh::factorial(int number) { if(number <= 1) return 1; double result = 1.0; for(int i=1; i<=number; i++) result *= i; return result; } void mitk::sh::Cart2Sph(double x, double y, double z, double *cart) { double phi, th, rad; rad = sqrt(x*x+y*y+z*z); if( rad < mitk::eps ) { th = M_PI/2; phi = M_PI/2; } else { th = acos(z/rad); phi = atan2(y, x); } cart[0] = phi; cart[1] = th; cart[2] = rad; } double mitk::sh::legendre0(int l) { if( l%2 != 0 ) { return 0; } else { double prod1 = 1.0; for(int i=1;i mitk::gradients::GetAllUniqueDirections(const BValueMap & refBValueMap, GradientDirectionContainerType *refGradientsContainer ) { IndiciesVector directioncontainer; BValueMap::const_iterator mapIterator = refBValueMap.begin(); if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1) mapIterator++; //skip bzero Values for( ; mapIterator != refBValueMap.end(); mapIterator++){ IndiciesVector currentShell = mapIterator->second; while(currentShell.size()>0) { unsigned int wntIndex = currentShell.back(); currentShell.pop_back(); IndiciesVector::iterator containerIt = directioncontainer.begin(); bool directionExist = false; while(containerIt != directioncontainer.end()) { if (fabs(dot(refGradientsContainer->ElementAt(*containerIt), refGradientsContainer->ElementAt(wntIndex))) > 0.9998) { directionExist = true; break; } containerIt++; } if(!directionExist) { directioncontainer.push_back(wntIndex); } } } return directioncontainer; } bool mitk::gradients::CheckForDifferingShellDirections(const BValueMap & refBValueMap, GradientDirectionContainerType::ConstPointer refGradientsContainer) { BValueMap::const_iterator mapIterator = refBValueMap.begin(); if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1) mapIterator++; //skip bzero Values for( ; mapIterator != refBValueMap.end(); mapIterator++){ BValueMap::const_iterator mapIterator_2 = refBValueMap.begin(); if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1) mapIterator_2++; //skip bzero Values for( ; mapIterator_2 != refBValueMap.end(); mapIterator_2++){ if(mapIterator_2 == mapIterator) continue; IndiciesVector currentShell = mapIterator->second; IndiciesVector testShell = mapIterator_2->second; for (unsigned int i = 0; i< currentShell.size(); i++) if (fabs(dot(refGradientsContainer->ElementAt(currentShell[i]), refGradientsContainer->ElementAt(testShell[i]))) <= 0.9998) { return true; } } } return false; } template double mitk::gradients::dot (vnl_vector_fixed< type ,3> const& v1, vnl_vector_fixed< type ,3 > const& v2 ) { double result = (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) / (v1.two_norm() * v2.two_norm()); return result ; } vnl_matrix mitk::gradients::ComputeSphericalFromCartesian(const IndiciesVector & refShell, const GradientDirectionContainerType * refGradientsContainer) { vnl_matrix Q(3, refShell.size()); Q.fill(0.0); for(unsigned int i = 0; i < refShell.size(); i++) { GradientDirectionType dir = refGradientsContainer->ElementAt(refShell[i]); double x = dir.normalize().get(0); double y = dir.normalize().get(1); double z = dir.normalize().get(2); double cart[3]; mitk::sh::Cart2Sph(x,y,z,cart); Q(0,i) = cart[0]; Q(1,i) = cart[1]; Q(2,i) = cart[2]; } return Q; } vnl_matrix mitk::gradients::ComputeSphericalHarmonicsBasis(const vnl_matrix & QBallReference, const unsigned int & LOrder) { vnl_matrix SHBasisOutput(QBallReference.cols(), (LOrder+1)*(LOrder+2)*0.5); SHBasisOutput.fill(0.0); for(int i=0; i< (int)SHBasisOutput.rows(); i++) for(int k = 0; k <= (int)LOrder; k += 2) for(int m =- k; m <= k; m++) { int j = ( k * k + k + 2 ) / 2.0 + m - 1; double phi = QBallReference(0,i); double th = QBallReference(1,i); double val = mitk::sh::Yj(m,k,th,phi); SHBasisOutput(i,j) = val; } return SHBasisOutput; } mitk::gradients::GradientDirectionContainerType::Pointer mitk::gradients::CreateNormalizedUniqueGradientDirectionContainer(const mitk::gradients::BValueMap & bValueMap, const GradientDirectionContainerType *origninalGradentcontainer) { mitk::gradients::GradientDirectionContainerType::Pointer directioncontainer = mitk::gradients::GradientDirectionContainerType::New(); BValueMap::const_iterator mapIterator = bValueMap.begin(); if(bValueMap.find(0) != bValueMap.end() && bValueMap.size() > 1){ mapIterator++; //skip bzero Values vnl_vector_fixed vec; vec.fill(0.0); directioncontainer->push_back(vec); } for( ; mapIterator != bValueMap.end(); mapIterator++){ IndiciesVector currentShell = mapIterator->second; while(currentShell.size()>0) { unsigned int wntIndex = currentShell.back(); currentShell.pop_back(); mitk::gradients::GradientDirectionContainerType::Iterator containerIt = directioncontainer->Begin(); bool directionExist = false; while(containerIt != directioncontainer->End()) { if (fabs(dot(containerIt.Value(), origninalGradentcontainer->ElementAt(wntIndex))) > 0.9998) { directionExist = true; break; } containerIt++; } if(!directionExist) { GradientDirectionType dir(origninalGradentcontainer->ElementAt(wntIndex)); directioncontainer->push_back(dir.normalize()); } } } return directioncontainer; } diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFieldmapGeneratorFilter.h b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFieldmapGeneratorFilter.h index d10d42db92..74e17eb311 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFieldmapGeneratorFilter.h +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFieldmapGeneratorFilter.h @@ -1,93 +1,93 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __itkFieldmapGeneratorFilter_h__ #define __itkFieldmapGeneratorFilter_h__ #include #include #include #include -#include +#include #include #include namespace itk{ /** * \brief Generate float image with artificial frequency maps used by Fiberfox. Simulates additional frequencies at (possibly multiple) positions based on 3D gaussians with the specified variance and amplitude and/or as a linear gradient in the image. * See "Fiberfox: Facilitating the creation of realistic white matter software phantoms" (DOI: 10.1002/mrm.25045) for details. */ template< class OutputImageType > class FieldmapGeneratorFilter : public ImageSource< OutputImageType > { public: typedef FieldmapGeneratorFilter Self; typedef ProcessObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; typedef typename OutputImageType::PixelType PixelType; typedef typename OutputImageType::IndexType IndexType; typedef itk::ImageRegion<3> OutputImageRegionType; typedef itk::Matrix MatrixType; itkFactorylessNewMacro(Self) itkCloneMacro(Self) itkTypeMacro( FieldmapGeneratorFilter, ImageSource ) /** Output image parameters. */ itkSetMacro( Spacing, itk::Vector ) itkSetMacro( Origin, mitk::Point3D ) itkSetMacro( DirectionMatrix, MatrixType ) itkSetMacro( ImageRegion, OutputImageRegionType ) /** Gradient direction and offset. */ void SetGradient( vnl_vector_fixed< double, 3 > gradient ) { m_Gradient=gradient; } void SetOffset( vnl_vector_fixed< double, 3 > offset ) { m_Offset=offset; } /** Parameters of gaussian frequency sources. */ void SetVariances( std::vector< double > variances ) { m_Variances=variances; } void SetHeights( std::vector< double > heights ) { m_Heights=heights; } void SetWorldPositions( std::vector< mitk::Point3D > worldPositions ) { m_WorldPositions=worldPositions; } protected: void BeforeThreadedGenerateData(); void ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, ThreadIdType threadId); FieldmapGeneratorFilter(); virtual ~FieldmapGeneratorFilter(); itk::Vector m_Spacing; ///< output image spacing mitk::Point3D m_Origin; ///< output image origin MatrixType m_DirectionMatrix; ///< output image rotation OutputImageRegionType m_ImageRegion; ///< output image size std::vector< double > m_Variances; std::vector< double > m_Heights; std::vector< mitk::Point3D > m_WorldPositions; vnl_vector_fixed< double, 3 > m_Gradient; vnl_vector_fixed< double, 3 > m_Offset; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkFieldmapGeneratorFilter.cpp" #endif #endif // __itkFieldmapGeneratorFilter_h__ diff --git a/Modules/DiffusionImaging/FiberTracking/Interactions/mitkFiberBundleInteractor.h b/Modules/DiffusionImaging/FiberTracking/Interactions/mitkFiberBundleInteractor.h index b38120e475..f2467bc717 100644 --- a/Modules/DiffusionImaging/FiberTracking/Interactions/mitkFiberBundleInteractor.h +++ b/Modules/DiffusionImaging/FiberTracking/Interactions/mitkFiberBundleInteractor.h @@ -1,99 +1,99 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKFiberBundleINTERACTOR_H_HEADER_INCLUDED #define MITKFiberBundleINTERACTOR_H_HEADER_INCLUDED #include "mitkCommon.h" #include "MitkExtExports.h" #include -#include +#include #include #include namespace mitk { class DataNode; //##Documentation //## @brief Just select a point, that's the only way to interact with the point //## //## Interact with a point: Select the point without moving to get parameters that does not change //## All Set-operations would be done through the method "ExecuteAction", if there would be anyone. //## the identificationnumber of this point is set by this points and evalued from an empty place in the DataStructure //## @ingroup Interaction class MitkFiberTracking_EXPORT FiberBundleInteractor : public Interactor { public: mitkClassMacro(FiberBundleInteractor, Interactor); mitkNewMacro2Param(Self, const char*, DataNode*); //##Documentation //## @brief Sets the amount of precision void SetPrecision(unsigned int precision); //##Documentation //## @brief derived from mitk::Interactor; calculates Jurisdiction according to points //## //## standard method can not be used, since it doesn't calculate in points, only in BoundingBox of Points virtual float CanHandleEvent(StateEvent const* stateEvent) const; protected: //##Documentation //##@brief Constructor FiberBundleInteractor(const char * type, DataNode* dataNode); //##Documentation //##@brief Destructor virtual ~FiberBundleInteractor(); //##Documentation //## @brief select the point on the given position virtual void SelectFiber(int position); //##Documentation //## @brief unselect all points that exist in mesh virtual void DeselectAllFibers(); //##Documentation //## @brief Executes Actions virtual bool ExecuteAction(Action* action, mitk::StateEvent const* stateEvent); private: //##Documentation //## @brief to calculate a direction vector from last point and actual point Point3D m_LastPoint; //##Documentation //## @brief to store a position unsigned int m_LastPosition; // Point3D m_InitialPickedPoint; // Point2D m_InitialPickedDisplayPoint; // double m_InitialPickedPointWorld[4]; Point3D m_CurrentPickedPoint; Point2D m_CurrentPickedDisplayPoint; double m_CurrentPickedPointWorld[4]; }; } #endif /* MITKFiberBundleInteractor_H_HEADER_INCLUDED */ diff --git a/Modules/Ext/Algorithms/mitkProbeFilter.cpp b/Modules/Ext/Algorithms/mitkProbeFilter.cpp index c54135875e..42a0b6e4a5 100644 --- a/Modules/Ext/Algorithms/mitkProbeFilter.cpp +++ b/Modules/Ext/Algorithms/mitkProbeFilter.cpp @@ -1,214 +1,214 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkProbeFilter.h" #include "mitkSurface.h" #include "mitkImage.h" #include #include #include #include #include mitk::ProbeFilter::ProbeFilter() { } mitk::ProbeFilter::~ProbeFilter() { } const mitk::Surface *mitk::ProbeFilter::GetInput(void) { if (this->GetNumberOfInputs() < 1) { return 0; } return static_cast< const mitk::Surface * >(this->ProcessObject::GetInput(0) ); } const mitk::Image *mitk::ProbeFilter::GetSource(void) { return static_cast< const mitk::Image * >(this->ProcessObject::GetInput(1)); } void mitk::ProbeFilter::SetInput(const mitk::Surface *input) { this->ProcessObject::SetNthInput( 0, const_cast< mitk::Surface * >( input ) ); } void mitk::ProbeFilter::SetSource(const mitk::Image *source) { this->ProcessObject::SetNthInput( 1, const_cast< mitk::Image * >( source ) ); } void mitk::ProbeFilter::GenerateOutputInformation() { mitk::Surface::ConstPointer input = this->GetInput(); mitk::Image::ConstPointer source = this->GetSource(); mitk::Surface::Pointer output = this->GetOutput(); if(input.IsNull()) return; if(source.IsNull()) return; if(input->GetGeometry()==NULL) return; if(source->GetGeometry()==NULL) return; if( (input->GetTimeGeometry()->CountTimeSteps()==1) && (source->GetTimeGeometry()->CountTimeSteps()>1) ) { Geometry3D::Pointer geo3D = Geometry3D::New(); BaseGeometry::Pointer geometry3D = dynamic_cast(geo3D.GetPointer()); geometry3D->Initialize(); geometry3D->SetBounds(source->GetTimeGeometry()->GetBoundsInWorld()); ProportionalTimeGeometry::Pointer outputTimeGeometry = ProportionalTimeGeometry::New(); outputTimeGeometry->Initialize(geometry3D, source->GetTimeGeometry()->CountTimeSteps()); outputTimeGeometry->SetFirstTimePoint(source->GetTimeGeometry()->GetMinimumTimePoint()); TimePointType stepDuration = source->GetTimeGeometry()->GetMaximumTimePoint(0) - source->GetTimeGeometry()->GetMinimumTimePoint(0); outputTimeGeometry->SetStepDuration(stepDuration); output->Expand(outputTimeGeometry->CountTimeSteps()); output->SetTimeGeometry( outputTimeGeometry ); } else output->SetGeometry( static_cast(input->GetGeometry()->Clone().GetPointer()) ); itkDebugMacro(<<"GenerateOutputInformation()"); } void mitk::ProbeFilter::GenerateData() { mitk::Surface *input = const_cast< mitk::Surface * >(this->GetInput()); mitk::Image *source = const_cast< mitk::Image * >(this->GetSource()); mitk::Surface::Pointer output = this->GetOutput(); itkDebugMacro(<<"Generating Data"); if(output.IsNull()) { itkDebugMacro(<<"Output is NULL."); return; } mitk::Surface::RegionType outputRegion = output->GetRequestedRegion(); const TimeGeometry *outputTimeGeometry = output->GetTimeGeometry(); const TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); const TimeGeometry *sourceTimeGeometry = source->GetTimeGeometry(); TimePointType timeInMS; int timestep=0; int tstart, tmax; tstart=outputRegion.GetIndex(3); tmax=tstart+outputRegion.GetSize(3); int t; for(t=tstart;tTimeStepToTimePoint( t ); vtkProbeFilter* probe = vtkProbeFilter::New(); timestep = inputTimeGeometry->TimePointToTimeStep( timeInMS ); probe->SetInputData( input->GetVtkPolyData(timestep) ); timestep = sourceTimeGeometry->TimePointToTimeStep( timeInMS ); probe->SetSourceData( source->GetVtkImageData(timestep) ); output->SetVtkPolyData( probe->GetPolyDataOutput(), t ); probe->Update(); probe->Delete(); } } void mitk::ProbeFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); mitk::Surface *input = const_cast< mitk::Surface * >( this->GetInput() ); mitk::Image *source = const_cast< mitk::Image * >( this->GetSource() ); if(input==NULL) return; if(source==NULL) return; mitk::Surface::Pointer output = this->GetOutput(); mitk::Surface::RegionType outputRegion = output->GetRequestedRegion(); const TimeGeometry *outputTimeGeometry = output->GetTimeGeometry(); mitk::Surface::RegionType inputSurfaceRegion = outputRegion; Image::RegionType sourceImageRegion = source->GetLargestPossibleRegion(); if(outputRegion.GetSize(3)<1) { mitk::Surface::RegionType::SizeType surfacesize; surfacesize.Fill(0); inputSurfaceRegion.SetSize(surfacesize); input->SetRequestedRegion( &inputSurfaceRegion ); mitk::Image::RegionType::SizeType imagesize; imagesize.Fill(0); sourceImageRegion.SetSize(imagesize); source->SetRequestedRegion( &sourceImageRegion ); return; } //create and set input requested region for the input surface const TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); ScalarType timeInMS; int timestep=0; // convert the start-index-time of output in start-index-time of input via millisecond-time timeInMS = outputTimeGeometry->TimeStepToTimePoint(outputRegion.GetIndex(3)); timestep = inputTimeGeometry->TimePointToTimeStep( timeInMS ); - if( ( timeInMS > ScalarTypeNumericTraits::NonpositiveMin() ) && ( inputTimeGeometry->IsValidTimeStep( timestep ) ) ) + if( ( timeInMS > itk::NumericTraits::NonpositiveMin() ) && ( inputTimeGeometry->IsValidTimeStep( timestep ) ) ) inputSurfaceRegion.SetIndex( 3, timestep ); else inputSurfaceRegion.SetIndex( 3, 0 ); // convert the end-index-time of output in end-index-time of input via millisecond-time timeInMS = outputTimeGeometry->TimeStepToTimePoint(outputRegion.GetIndex(3)+outputRegion.GetSize(3)-1); timestep = inputTimeGeometry->TimePointToTimeStep( timeInMS ); - if( ( timeInMS > ScalarTypeNumericTraits::NonpositiveMin() ) && ( outputTimeGeometry->IsValidTimeStep( timestep ) ) ) + if( ( timeInMS > itk::NumericTraits::NonpositiveMin() ) && ( outputTimeGeometry->IsValidTimeStep( timestep ) ) ) inputSurfaceRegion.SetSize( 3, timestep - inputSurfaceRegion.GetIndex(3) + 1 ); else inputSurfaceRegion.SetSize( 3, 1 ); input->SetRequestedRegion( &inputSurfaceRegion ); //create and set input requested region for the source image const TimeGeometry *sourceTimeGeometry = source->GetTimeGeometry(); // convert the start-index-time of output in start-index-time of source via millisecond-time timeInMS = outputTimeGeometry->TimeStepToTimePoint(outputRegion.GetIndex(3)); timestep = sourceTimeGeometry->TimePointToTimeStep( timeInMS ); - if( ( timeInMS > ScalarTypeNumericTraits::NonpositiveMin() ) && ( sourceTimeGeometry->IsValidTimeStep( timestep ) ) ) + if( ( timeInMS > itk::NumericTraits::NonpositiveMin() ) && ( sourceTimeGeometry->IsValidTimeStep( timestep ) ) ) sourceImageRegion.SetIndex( 3, timestep ); else sourceImageRegion.SetIndex( 3, 0 ); // convert the end-index-time of output in end-index-time of source via millisecond-time timeInMS = outputTimeGeometry->TimeStepToTimePoint(outputRegion.GetIndex(3)+outputRegion.GetSize(3)-1); timestep = sourceTimeGeometry->TimePointToTimeStep( timeInMS ); - if( ( timeInMS > ScalarTypeNumericTraits::NonpositiveMin() ) && ( outputTimeGeometry->IsValidTimeStep( timestep ) ) ) + if( ( timeInMS > itk::NumericTraits::NonpositiveMin() ) && ( outputTimeGeometry->IsValidTimeStep( timestep ) ) ) sourceImageRegion.SetSize( 3, timestep - sourceImageRegion.GetIndex(3) + 1 ); else sourceImageRegion.SetSize( 3, 1 ); sourceImageRegion.SetIndex( 4, 0 ); sourceImageRegion.SetSize( 4, 1 ); source->SetRequestedRegion( &sourceImageRegion ); } diff --git a/Modules/Ext/Controllers/mitkTDMouseVtkCameraController.cpp b/Modules/Ext/Controllers/mitkTDMouseVtkCameraController.cpp index 92135bac60..9cdcd2fd2f 100644 --- a/Modules/Ext/Controllers/mitkTDMouseVtkCameraController.cpp +++ b/Modules/Ext/Controllers/mitkTDMouseVtkCameraController.cpp @@ -1,241 +1,241 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTDMouseVtkCameraController.h" #include "mitkVtkPropRenderer.h" #include "vtkCamera.h" #include "vtkRenderer.h" #include "vtkTransform.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkInteractionConst.h" #include "mitkStateEvent.h" #include "mitkTDMouseEventThrower.h" #include "mitkTDMouseEvent.h" #include "mitkGlobalInteraction.h" #include "vtkMath.h" const double TRANSLATIONSENSITIVITY = 2.0; const double ROTATIONSENSIVITY = 3.5; const double ANGLESENSITIVITY = 3.5; mitk::TDMouseVtkCameraController::TDMouseVtkCameraController() : CameraController("TDMouseInteraction") { //activate and instanciate EventThrower mitk::TDMouseEventThrower* thrower = mitk::TDMouseEventThrower::GetInstance(); //add this to GlobalInteracion to listen to events mitk::GlobalInteraction::GetInstance()->AddListener(this); //connect method this->OnTDMouseEvent to StateMachineEventMechanism CONNECT_ACTION(AcONTDMOUSEINPUT, OnTDMouseEvent); CONNECT_ACTION(AcONTDMOUSEKEYDOWN, OnTDMouseKeyDown); m_ClippingRangeIsSet = false; } mitk::TDMouseVtkCameraController::~TDMouseVtkCameraController() { } bool mitk::TDMouseVtkCameraController::OnTDMouseEvent(mitk::Action* a, const mitk::StateEvent* e) { //only if 3D rendering mitk::BaseRenderer const* br = this->GetRenderer(); mitk::BaseRenderer::MapperSlotId id = ((mitk::BaseRenderer*)(br))->GetMapperID(); if (id != mitk::BaseRenderer::Standard3D) return true; //only if focused by the FocusManager if (this->GetRenderer() != mitk::GlobalInteraction::GetInstance()->GetFocus()) return true; //pre-checking for safety vtkRenderer* vtkRenderer = ((mitk::VtkPropRenderer*)this->GetRenderer())->GetVtkRenderer(); if (vtkRenderer == NULL) return false; vtkCamera* vtkCam = (vtkCamera*)vtkRenderer->GetActiveCamera(); if(!m_ClippingRangeIsSet) vtkCam->SetClippingRange(0.1, 1000000); const mitk::TDMouseEvent* tdevent = dynamic_cast(e->GetEvent()); if (tdevent == NULL) { std::cout<<"Wrong event for TDMouseCameraController!"<GetTranslation(); mitk::Vector3D rotation = tdevent->GetRotation(); mitk::ScalarType angle = tdevent->GetAngle(); //output for debug //std::cout<<"translation: "<GetDataStorage(); mitk::BoundingBox::Pointer bb = ds->ComputeBoundingBox(); mitk::BoundingBox::AccumulateType length = bb->GetDiagonalLength2(); if (length > 0.00001)//if length not zero sceneSensivity *= 100.0 / (sqrt(length)) ; //sensivity to adapt to mitk speed translation *= sceneSensivity * TRANSLATIONSENSITIVITY; rotation *= sceneSensivity * ROTATIONSENSIVITY; angle *= sceneSensivity * ANGLESENSITIVITY; //compute the global space coordinates from the relative mouse coordinate //first we need the position of the camera mitk::Vector3D camPosition; double camPositionTemp[3]; vtkCam->GetPosition(camPositionTemp); camPosition[0] = camPositionTemp[0]; camPosition[1] = camPositionTemp[1]; camPosition[2] = camPositionTemp[2]; //then the upvector of the camera mitk::Vector3D upCamVector; double upCamTemp[3]; vtkCam->GetViewUp(upCamTemp); upCamVector[0] = upCamTemp[0]; upCamVector[1] = upCamTemp[1]; upCamVector[2] = upCamTemp[2]; upCamVector.Normalize(); //then the vector to which the camera is heading at (focalpoint) mitk::Vector3D focalPoint; double focalPointTemp[3]; vtkCam->GetFocalPoint(focalPointTemp); focalPoint[0] = focalPointTemp[0]; focalPoint[1] = focalPointTemp[1]; focalPoint[2] = focalPointTemp[2]; mitk::Vector3D focalVector; focalVector = focalPoint - camPosition; focalVector.Normalize(); //orthogonal vector to focalVector and upCamVector mitk::Vector3D crossVector; crossVector = CrossProduct(upCamVector, focalVector); crossVector.Normalize(); //now we have the current orientation so we can adapt it according to the current information, which we got from the TDMouse //new position of the camera: //left/right = camPosition + crossVector * translation[0]; mitk::Vector3D vectorX = crossVector * -translation[0]; //changes the magnitude, not the direction double nextCamPosition[3]; nextCamPosition[0] = camPosition[0] + vectorX[0]; nextCamPosition[1] = camPosition[1] + vectorX[1]; nextCamPosition[2] = camPosition[2] + vectorX[2]; //now the up/down movement mitk::Vector3D vectorY = upCamVector * translation[1]; //changes the magnitude, not the direction nextCamPosition[0] += vectorY[0]; nextCamPosition[1] += vectorY[1]; nextCamPosition[2] += vectorY[2]; //forward/backward movement mitk::Vector3D vectorZ = focalVector * -translation[2]; //changes the magnitude, not the direction nextCamPosition[0] += vectorZ[0]; nextCamPosition[1] += vectorZ[1]; nextCamPosition[2] += vectorZ[2]; //set the next position double nextPosition[3]; nextPosition[0] = nextCamPosition[0]; nextPosition[1] = nextCamPosition[1]; nextPosition[2] = nextCamPosition[2]; vtkCam->SetPosition(nextPosition); //adapt the focal point the same way double currentFocalPoint[3], nextFocalPoint[3]; vtkCam->GetFocalPoint(currentFocalPoint); nextFocalPoint[0] = currentFocalPoint[0] + vectorX[0] + vectorY[0] + vectorZ[0]; nextFocalPoint[1] = currentFocalPoint[1] + vectorX[1] + vectorY[1] + vectorZ[1]; ; nextFocalPoint[2] = currentFocalPoint[2] + vectorX[2] + vectorY[2] + vectorZ[2]; vtkCam->SetFocalPoint(nextFocalPoint); //now adapt the rotation of the mouse and adapt the camera according to it //Pitch: //Rotate the focal point about the cross product of the view up vector and the direction of //projection, centered at the camera's position. vtkCam->Pitch(rotation[0]*angle); //Yaw: //Rotate the focal point about the view up vector centered at the camera's position. //Note that the view up vector is not necessarily perpendicular to the direction of projection. vtkCam->Yaw(rotation[1]*angle); //Roll: //Rotate the camera about the direction of projection. vtkCam->Roll(-rotation[2]*angle * 1.5);//*1.5 to speed up the rotation[2] a little bit //Recompute the ViewUp vector to force it to be perpendicular to camera->focalpoint vector. //Unless you are going to use Yaw or Azimuth on the camera, there is no need to do this. vtkCam->OrthogonalizeViewUp(); //no zooming, only translating to the front or back // dolly: Move the position of the camera along the direction // of projection. Moving towards the focal point (e.g., greater // than 1) is a dolly-in, moving away from the focal point // (e.g., less than 1) is a dolly-out. //double distance = ((tdevent->GetTranslation())[1] / 10.0);//make it less sensitive in comparison to translation and rotatipn //vtkCam->Dolly(1.0 + distance ); //Reset the camera clipping range based on the bounds of the visible actors. //This ensures that no props are cut off vtkRenderer->ResetCameraClippingRange(); mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(mitk::GlobalInteraction::GetInstance()->GetFocus()->GetRenderWindow()); return true; } bool mitk::TDMouseVtkCameraController::OnTDMouseKeyDown(mitk::Action* a, const mitk::StateEvent* e) { //reset the camera, so that the objects shown in the scene can be seen. const mitk::VtkPropRenderer* glRenderer = dynamic_cast(m_Renderer); if (glRenderer) { vtkRenderer* vtkRenderer = glRenderer->GetVtkRenderer(); mitk::DataStorage* ds = m_Renderer->GetDataStorage(); if (ds == NULL) return false; mitk::BoundingBox::Pointer bb = ds->ComputeBoundingBox(); mitk::Point3D middle =bb->GetCenter(); vtkRenderer->GetActiveCamera()->SetFocalPoint(middle[0],middle[1],middle[2]); vtkRenderer->ResetCamera(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } return false; } diff --git a/Modules/Ext/DataManagement/mitkAffineTransformationOperation.h b/Modules/Ext/DataManagement/mitkAffineTransformationOperation.h index 95d87a795d..95171dd660 100644 --- a/Modules/Ext/DataManagement/mitkAffineTransformationOperation.h +++ b/Modules/Ext/DataManagement/mitkAffineTransformationOperation.h @@ -1,45 +1,45 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKAFFINETRANSFORMATIONOPERATION_H_HEADER_INCLUDED #define MITKAFFINETRANSFORMATIONOPERATION_H_HEADER_INCLUDED #include "mitkPointOperation.h" #include "MitkExtExports.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" namespace mitk { //##Documentation //## @brief Operation, that holds everything necessary for an affine operation. //## //## @ingroup Undo class MitkExt_EXPORT AffineTransformationOperation : public PointOperation { public: AffineTransformationOperation(OperationType operationType, Point3D point, ScalarType angle, int index); virtual ~AffineTransformationOperation(void); ScalarType GetAngle(); protected: ScalarType m_Angle; }; } // namespace mitk #endif /* MITKAFFINETRANSFORMATIONOPERATION_H_HEADER_INCLUDED */ diff --git a/Modules/Ext/DataManagement/mitkDrawOperation.h b/Modules/Ext/DataManagement/mitkDrawOperation.h index 4eb8729be2..eb7b818f9b 100755 --- a/Modules/Ext/DataManagement/mitkDrawOperation.h +++ b/Modules/Ext/DataManagement/mitkDrawOperation.h @@ -1,76 +1,76 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKDRAWOPERATION_H #define MITKDRAWOPERATION_H #include #include "MitkExtExports.h" #include -#include +#include #include #include namespace mitk { //##Documentation //## @brief DrawOperation, that handles all actions on seeds. //## //## Stores everything for adding and deleting seeds. //## @ingroup Undo class MitkExt_EXPORT DrawOperation : public Operation { public: //##Documentation //##@brief DrawOperation, that handles all actions on seeds. //## //## @param operationType is the type of that operation (see mitkOperation.h; e.g. move or add; Information for StateMachine::ExecuteOperation()); //## @param point is the information of the seedpoint to add to the seedsimage //## @param last_point is the information of the point added before //## @param draw_state represents the seeds type e.g foreground or background seeds //## @param radius is the radius of seeds DrawOperation(OperationType operationType, Point3D point, Point3D last_point, int draw_state, int radius); virtual ~DrawOperation(); Point3D GetPoint(); Point3D GetLastPoint(); int GetDrawState(); int GetRadius(); SeedsImage::Pointer GetSeedsImage(); SeedsImage::Pointer GetLastSeedsImage(); private: Point3D m_Point; Point3D m_LastPoint; int m_DrawState; int m_Radius; SeedsImage::Pointer m_SeedsImage; SeedsImage::Pointer m_LastSeedsImage; }; }//namespace mitk #endif /* MITKDRAWOPERATION_H*/ diff --git a/Modules/Ext/DataManagement/mitkNamedPoint.h b/Modules/Ext/DataManagement/mitkNamedPoint.h index 8aa471db49..fa983c10cf 100644 --- a/Modules/Ext/DataManagement/mitkNamedPoint.h +++ b/Modules/Ext/DataManagement/mitkNamedPoint.h @@ -1,89 +1,89 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITK_NAMED_POINT_H #define MITK_NAMED_POINT_H -#include +#include #include namespace mitk { class NamedPoint{ std::string m_Name; mitk::Point3D m_Point; public: /** * */ NamedPoint() { }; /** * */ NamedPoint( const NamedPoint& namedPoint ) : m_Name( namedPoint.m_Name ), m_Point( namedPoint.m_Point ) { }; /** * */ NamedPoint( const std::string name, const Point3D& point ) : m_Name( name ), m_Point( point ) { }; /** * */ const std::string& GetName() const { return m_Name; }; /** * */ void SetName( const std::string& name ) { m_Name = name; }; /** * */ const Point3D& GetPoint() const { return m_Point; }; /** * */ void SetPoint( const Point3D& point ) { m_Point = point; }; }; } // mitk #endif // MITK_NAMED_POINT_H diff --git a/Modules/Ext/DataManagement/mitkSphereLandmarkProjector.cpp b/Modules/Ext/DataManagement/mitkSphereLandmarkProjector.cpp index 183d0fcdf8..f9851d51f6 100644 --- a/Modules/Ext/DataManagement/mitkSphereLandmarkProjector.cpp +++ b/Modules/Ext/DataManagement/mitkSphereLandmarkProjector.cpp @@ -1,158 +1,158 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSphereLandmarkProjector.h" #include #include #include #include mitk::SphereLandmarkProjector::SphereLandmarkProjector() { m_SphericalTransform = vtkSphericalTransform::New(); m_SphereRotation = vtkTransform::New(); m_SpatialPlacementTransform = vtkTransform::New(); m_PlaneToSphericalTransform = vtkGeneralTransform::New(); m_SphereRotation->RotateX(90); //setup parameter-plane of the sphere: x is phi, y is theta; the radius is always 1 mitk::ScalarType origin[3] = {1, 0, 2*vnl_math::pi}; // (1, 0, 6.28) (1, 0, 0) mitk::ScalarType right[3] = {0, 0, -2*vnl_math::pi}; // (0,3.14, 0) (0, 0, 6.28) mitk::ScalarType bottom[3] = {0, vnl_math::pi, 0}; // (0, 0,-6.28) (0,3.14, 0) m_SphereParameterPlane = mitk::PlaneGeometry::New(); m_SphereParameterPlane->InitializeStandardPlane(right, bottom); m_SphereParameterPlane->SetOrigin(origin); m_SphereParameterPlane->SetSizeInUnits(100, 50); m_ParameterPlane = m_SphereParameterPlane; } mitk::SphereLandmarkProjector::~SphereLandmarkProjector() { m_SphericalTransform->Delete(); m_SphereRotation->Delete(); m_SpatialPlacementTransform->Delete(); m_PlaneToSphericalTransform->Delete(); } void mitk::SphereLandmarkProjector::ComputeCompleteAbstractTransform() { m_PlaneToSphericalTransform->Identity(); m_PlaneToSphericalTransform->PostMultiply(); m_PlaneToSphericalTransform->Concatenate(m_SphericalTransform); m_PlaneToSphericalTransform->Concatenate(m_SphereRotation); m_PlaneToSphericalTransform->Concatenate(m_InterpolatingAbstractTransform);//GetInterpolatingAbstractTransform()); m_PlaneToSphericalTransform->Concatenate(m_SpatialPlacementTransform); m_CompleteAbstractTransform = m_PlaneToSphericalTransform; } void mitk::SphereLandmarkProjector::ProjectLandmarks( const mitk::PointSet::DataType::PointsContainer* targetLandmarks) { unsigned int size=targetLandmarks->Size(); mitk::PointSet::DataType::PointsContainer::ConstIterator pointiterator, start = targetLandmarks->Begin(); mitk::PointSet::DataType::PointsContainer::ElementIdentifier id; //Part I: Calculate center of sphere mitk::Point3D center; center.Fill(0); mitk::ScalarType radius; mitk::PointSet::PointType point; //determine center for(id=0, pointiterator=start;idValue(); center[0]+=point[0]; center[1]+=point[1]; center[2]+=point[2]; } center[0]/=(mitk::ScalarType)size; center[1]/=(mitk::ScalarType)size; center[2]/=(mitk::ScalarType)size; //determine radius switch(0) { case 0/*MIN*/: - radius = mitk::ScalarTypeNumericTraits::max(); + radius = itk::NumericTraits::max(); for(id=0, pointiterator=start;idValue(); mitk::Vector3D v; v[0]=point[0]-center[0]; v[1]=point[1]-center[1]; v[2]=point[2]-center[2]; if (v.GetNorm() < radius) radius = v.GetNorm(); } break; case 1/*MAX*/: radius = 0; for(id=0, pointiterator=start;idValue(); mitk::Vector3D v; v[0]=point[0]-center[0]; v[1]=point[1]-center[1]; v[2]=point[2]-center[2]; if (v.GetNorm() > radius) radius = v.GetNorm(); } break; case 2/*AVERAGE*/: radius = 0; for(id=0, pointiterator=start;idValue(); mitk::Vector3D v; v[0]=point[0]-center[0]; v[1]=point[1]-center[1]; v[2]=point[2]-center[2]; radius += v.GetNorm(); } radius*=1.0/size; break; } mitk::Point3D origin = m_SphereParameterPlane->GetOrigin(); origin[0]=radius; m_SphereParameterPlane->SetOrigin(origin); m_SpatialPlacementTransform->GetMatrix()->SetElement(0, 3, center[0]); m_SpatialPlacementTransform->GetMatrix()->SetElement(1, 3, center[1]); m_SpatialPlacementTransform->GetMatrix()->SetElement(2, 3, center[2]); //Part II: Project points on sphere mitk::Point3D projectedPoint; m_WritableFinalTargetLandmarks->Initialize(); m_ProjectedLandmarks->Initialize(); m_WritableFinalTargetLandmarks->Reserve(size); m_ProjectedLandmarks->Reserve(size); for(id=0, pointiterator=start;idValue(); mitk::Vector3D v; v=point-center; mitk::FillVector3D(point, v[0], v[1], v[2]); v.Normalize(); v*=radius; mitk::FillVector3D(projectedPoint, v[0], v[1], v[2]); m_WritableFinalTargetLandmarks->InsertElement(id, point); m_ProjectedLandmarks->InsertElement(id, projectedPoint); } } diff --git a/Modules/Ext/Interactions/mitkConnectPointsInteractor.h b/Modules/Ext/Interactions/mitkConnectPointsInteractor.h index dd97d0eac3..e8a2308923 100644 --- a/Modules/Ext/Interactions/mitkConnectPointsInteractor.h +++ b/Modules/Ext/Interactions/mitkConnectPointsInteractor.h @@ -1,97 +1,97 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKCONNECTPOINTSINTERACTOR_H_HEADER_INCLUDED_C11202FF #define MITKCONNECTPOINTSINTERACTOR_H_HEADER_INCLUDED_C11202FF #include "mitkCommon.h" #include "MitkExtExports.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include namespace mitk { class DataNode; //##Documentation //## @brief Interaction for mitk::Mesh: Connect existing points to lines //## @ingroup Interaction class MitkExt_EXPORT ConnectPointsInteractor : public Interactor { public: mitkClassMacro(ConnectPointsInteractor, Interactor); mitkNewMacro3Param(Self, const char*, DataNode*, int); mitkNewMacro2Param(Self, const char*, DataNode*); //##Documentation //## @brief Sets the amount of precision void SetPrecision( unsigned int precision ); //##Documentation //## @brief calculates how good the data, this statemachine handles, is hit by the event. //## //## overwritten, cause we don't look at the boundingbox, we look at each point virtual float CanHandleEvent(StateEvent const* stateEvent) const; protected: //##Documentation //##@brief Constructor with Param n for limited Set of Points //## //## if no n is set, then the number of points is unlimited* ConnectPointsInteractor(const char * type, DataNode* dataNode, int n = -1); virtual ~ConnectPointsInteractor(); virtual bool ExecuteAction( Action* action, mitk::StateEvent const* stateEvent ); //##Documentation //## @brief deselects the Points in the PointSet. //## supports Undo if enabled void UnselectAll(); //##Documentation //## @brief Selects the point. //## supports Undo if enabled. //## @param position is the index of the point that has to be selected void SelectPoint( int position ); private: //##Documentation //## @brief the number of possible points in this object //## //## if -1, then no limit set int m_N; //##Documentation //## @brief stores the current CellId this Statemachine works in unsigned int m_CurrentCellId; //##Documentation //## @brief to calculate a direction vector from last point and actual point Point3D m_LastPoint; //##Documentation //## @brief summ-vector for Movement Vector3D m_SumVec; //##Documentation //## @brief to store the value of precision to pick a point unsigned int m_Precision; }; } #endif /* MITKCONNECTPOINTSINTERACTOR_H_HEADER_INCLUDED_C11202FF */ diff --git a/Modules/Ext/Interactions/mitkDisplayPointSetInteractor.cpp b/Modules/Ext/Interactions/mitkDisplayPointSetInteractor.cpp index db8e95b7b8..9945b641c9 100644 --- a/Modules/Ext/Interactions/mitkDisplayPointSetInteractor.cpp +++ b/Modules/Ext/Interactions/mitkDisplayPointSetInteractor.cpp @@ -1,135 +1,135 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDisplayPointSetInteractor.h" #include "mitkInteractionConst.h" #include "mitkStateEvent.h" #include "mitkBaseRenderer.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" mitk::DisplayPointSetInteractor ::DisplayPointSetInteractor(const char * type, DataNode* dataNode, int n) :PointSetInteractor(type, dataNode, n) { } mitk::DisplayPointSetInteractor::~DisplayPointSetInteractor() { } bool mitk::DisplayPointSetInteractor ::ExecuteAction( Action* action, mitk::StateEvent const* stateEvent ) { bool ok = false;//for return type bool //get the timestep to also support 3D+T //const mitk::Event *theEvent = stateEvent->GetEvent(); /*Each case must watch the type of the event!*/ /*Each time a Point is added or removed or finished moved, the display-coordinates and the last renderer is stored.*/ switch (action->GetActionId()) { case AcADDPOINT: { mitk::DisplayPositionEvent const *posEvent = dynamic_cast < const mitk::DisplayPositionEvent * > (stateEvent->GetEvent()); if ( posEvent == NULL ) { return false; } m_LastDisplayCoordinates = posEvent->GetDisplayPosition(); m_LastRenderer = posEvent->GetSender(); ok = Superclass::ExecuteAction( action, stateEvent ); break; } case AcREMOVEPOINT://remove the given Point from the list { mitk::DisplayPositionEvent const *posEvent = dynamic_cast < const mitk::DisplayPositionEvent * > (stateEvent->GetEvent()); if ( posEvent == NULL ) { return false; } m_LastDisplayCoordinates = posEvent->GetDisplayPosition(); m_LastRenderer = posEvent->GetSender(); ok = Superclass::ExecuteAction( action, stateEvent ); break; } case AcREMOVEALL: { mitk::DisplayPositionEvent const *posEvent = dynamic_cast < const mitk::DisplayPositionEvent * > (stateEvent->GetEvent()); if ( posEvent == NULL ) { return false; } m_LastDisplayCoordinates = posEvent->GetDisplayPosition(); m_LastRenderer = posEvent->GetSender(); ok = Superclass::ExecuteAction( action, stateEvent ); break; } case AcFINISHMOVEMENT: { mitk::DisplayPositionEvent const *posEvent = dynamic_cast < const mitk::DisplayPositionEvent * > (stateEvent->GetEvent()); if ( posEvent == NULL ) { return false; } m_LastDisplayCoordinates = posEvent->GetDisplayPosition(); m_LastRenderer = posEvent->GetSender(); ok = Superclass::ExecuteAction( action, stateEvent ); break; } default: return Superclass::ExecuteAction( action, stateEvent ); } return ok; } mitk::Point2D mitk::DisplayPointSetInteractor::GetLastDisplayCoordinates() { return m_LastDisplayCoordinates; } mitk::BaseRenderer* mitk::DisplayPointSetInteractor::GetLastRenderer() { return m_LastRenderer; } diff --git a/Modules/Ext/Interactions/mitkDisplayPointSetInteractor.h b/Modules/Ext/Interactions/mitkDisplayPointSetInteractor.h index 2bbf70a7d3..25eda89b8a 100644 --- a/Modules/Ext/Interactions/mitkDisplayPointSetInteractor.h +++ b/Modules/Ext/Interactions/mitkDisplayPointSetInteractor.h @@ -1,78 +1,78 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKDISPLAYPOINTSETINTERACTOR_H_HEADER_INCLUDED_C11202FF #define MITKDISPLAYPOINTSETINTERACTOR_H_HEADER_INCLUDED_C11202FF #include "mitkCommon.h" #include "MitkExtExports.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include #include "mitkPointSetInteractor.h" #include namespace mitk { class DataNode; /** * \brief Interaction with a set of points. * * Points can be added, removed and moved. * \ingroup Interaction */ class MitkExt_EXPORT DisplayPointSetInteractor : public PointSetInteractor { public: mitkClassMacro(DisplayPointSetInteractor, PointSetInteractor); mitkNewMacro3Param(Self, const char*, DataNode*, int); mitkNewMacro2Param(Self, const char*, DataNode*); Point2D GetLastDisplayCoordinates(); BaseRenderer* GetLastRenderer(); protected: /** * \brief Constructor with Param n for limited Set of Points * * if no n is set, then the number of points is unlimited* */ DisplayPointSetInteractor(const char * type, DataNode* dataNode, int n = -1); /** * \brief Default Destructor **/ virtual ~DisplayPointSetInteractor(); virtual bool ExecuteAction( Action* action, mitk::StateEvent const* stateEvent ); /** \brief last display-coordinates of the point * */ Point2D m_LastDisplayCoordinates; mitk::BaseRenderer* m_LastRenderer; }; } #endif /* MITKDisplayPointSetInteractor_H_HEADER_INCLUDED_C11202FF */ diff --git a/Modules/Ext/Interactions/mitkPointInteractor.h b/Modules/Ext/Interactions/mitkPointInteractor.h index b1cba5d710..2b4ae96e34 100644 --- a/Modules/Ext/Interactions/mitkPointInteractor.h +++ b/Modules/Ext/Interactions/mitkPointInteractor.h @@ -1,88 +1,88 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPOINTINTERACTOR_H_HEADER_INCLUDED #define MITKPOINTINTERACTOR_H_HEADER_INCLUDED #include "mitkCommon.h" #include "MitkExtExports.h" #include -#include +#include namespace mitk { class DataNode; //##Documentation //## @brief Interaction with a point //## //## Interact with a point: set point, select point, move point and remove point //## All Set-operations are done through the method "ExecuteAction". //## the identificationnumber of this point is set by this points and evalued from an empty place in the DataStructure //## @ingroup Interaction class MitkExt_EXPORT PointInteractor : public Interactor { public: mitkClassMacro(PointInteractor, Interactor); mitkNewMacro2Param(Self, const char*, DataNode*); //##Documentation //## @brief Sets the amount of precision void SetPrecision(unsigned int precision); //##Documentation //## @brief derived from mitk::Interactor; calculates Jurisdiction according to points //## //## standard method can not be used, since it doesn't calculate in points, only in BoundingBox of Points virtual float CanHandleEvent(StateEvent const* stateEvent) const; protected: //##Documentation //##@brief Constructor PointInteractor(const char * type, DataNode* dataNode); //##Documentation //##@brief Destructor virtual ~PointInteractor(); //##Documentation //## @brief select the point on the given position virtual void SelectPoint(int position); //##Documentation //## @brief unselect all points that exist in mesh virtual void DeselectAllPoints(); //##Documentation //## @brief Executes Actions virtual bool ExecuteAction(Action* action, mitk::StateEvent const* stateEvent); private: //##Documentation //## @brief to calculate a direction vector from last point and actual point Point3D m_LastPoint; //##Documentation //## @brief to store a position unsigned int m_LastPosition; }; } #endif /* MITKPOINTINTERACTOR_H_HEADER_INCLUDED */ diff --git a/Modules/Ext/Interactions/mitkPointSelectorInteractor.h b/Modules/Ext/Interactions/mitkPointSelectorInteractor.h index 51c9a71e4f..05adfa433e 100644 --- a/Modules/Ext/Interactions/mitkPointSelectorInteractor.h +++ b/Modules/Ext/Interactions/mitkPointSelectorInteractor.h @@ -1,88 +1,88 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPOINTSELECTORINTERACTOR_H_HEADER_INCLUDED #define MITKPOINTSELECTORINTERACTOR_H_HEADER_INCLUDED #include "mitkCommon.h" #include "MitkExtExports.h" #include -#include +#include namespace mitk { class DataNode; //##Documentation //## @brief Just select a point, that's the only way to interact with the point //## //## Interact with a point: Select the point without moving to get parameters that does not change //## All Set-operations would be done through the method "ExecuteAction", if there would be anyone. //## the identificationnumber of this point is set by this points and evalued from an empty place in the DataStructure //## @ingroup Interaction class MitkExt_EXPORT PointSelectorInteractor : public Interactor { public: mitkClassMacro(PointSelectorInteractor, Interactor); mitkNewMacro2Param(Self, const char*, DataNode*); //##Documentation //## @brief Sets the amount of precision void SetPrecision(unsigned int precision); //##Documentation //## @brief derived from mitk::Interactor; calculates Jurisdiction according to points //## //## standard method can not be used, since it doesn't calculate in points, only in BoundingBox of Points virtual float CanHandleEvent(StateEvent const* stateEvent) const; protected: //##Documentation //##@brief Constructor PointSelectorInteractor(const char * type, DataNode* dataNode); //##Documentation //##@brief Destructor virtual ~PointSelectorInteractor(); //##Documentation //## @brief select the point on the given position virtual void SelectPoint(int position); //##Documentation //## @brief unselect all points that exist in mesh virtual void DeselectAllPoints(); //##Documentation //## @brief Executes Actions virtual bool ExecuteAction(Action* action, mitk::StateEvent const* stateEvent); private: //##Documentation //## @brief to calculate a direction vector from last point and actual point Point3D m_LastPoint; //##Documentation //## @brief to store a position unsigned int m_LastPosition; }; } #endif /* MITKPointSelectorInteractor_H_HEADER_INCLUDED */ diff --git a/Modules/Ext/Interactions/mitkSeedsInteractor.h b/Modules/Ext/Interactions/mitkSeedsInteractor.h index dccec14cfe..8d30dfc594 100644 --- a/Modules/Ext/Interactions/mitkSeedsInteractor.h +++ b/Modules/Ext/Interactions/mitkSeedsInteractor.h @@ -1,72 +1,72 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __SeedsInteractor_H #define __SeedsInteractor_H #include #include "MitkExtExports.h" #include -#include +#include #include //needed by QmitkSimplexMeshes (SMDeformation.cpp and LungSegmentation.cpp) #include #include namespace mitk { //##Documentation //## @brief SeedsInteractor handles all actions on the seedsimage //## @ingroup Interaction class MitkExt_EXPORT SeedsInteractor : public Interactor { public: mitkClassMacro(SeedsInteractor, Interactor); mitkNewMacro2Param(Self, const char*, DataNode*); /// sets the radius of the seeds. void SetRadius(int val){m_Radius=val;}; void SetCurrentDrawState(int val){m_CurrentDrawState = val;}; itkSetMacro(Config, unsigned int); itkGetMacro(Config, unsigned int); protected: /** * @brief Default Constructor **/ SeedsInteractor(const char * type, DataNode* dataNode); /** * @brief Default Destructor **/ virtual ~SeedsInteractor(); virtual bool ExecuteAction(Action* action, StateEvent const* stateEvent); protected: SeedsImage::Pointer m_SeedsImage; SeedsImage::Pointer m_LastSeedsImage; Point3D event_point; Point3D last_point; int m_Radius; int m_DrawState; int m_CurrentDrawState; unsigned int m_Config; // determine whether 254,255 or smoothed float values are used. }; } #endif //__SeedsInteractor_H diff --git a/Modules/Ext/Interactions/mitkTDMouseEvent.cpp b/Modules/Ext/Interactions/mitkTDMouseEvent.cpp index d9baf59e39..13e62055d0 100644 --- a/Modules/Ext/Interactions/mitkTDMouseEvent.cpp +++ b/Modules/Ext/Interactions/mitkTDMouseEvent.cpp @@ -1,25 +1,25 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTDMouseEvent.h" #include "mitkInteractionConst.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" mitk::TDMouseEvent::TDMouseEvent(int buttonState, const Vector3D& translation, const Vector3D& rotation, const ScalarType& angle) : Event(NULL, Type_TDMouseInput, BS_NoButton, buttonState, Key_none), m_Translation(translation), m_Rotation(rotation), m_Angle(angle) { } diff --git a/Modules/Ext/Interactions/mitkTDMouseEvent.h b/Modules/Ext/Interactions/mitkTDMouseEvent.h index 0df579907f..9e69d43eb3 100644 --- a/Modules/Ext/Interactions/mitkTDMouseEvent.h +++ b/Modules/Ext/Interactions/mitkTDMouseEvent.h @@ -1,76 +1,76 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef TDMOUSEEVENT_H_HEADER_INCLUDED #define TDMOUSEEVENT_H_HEADER_INCLUDED #include "mitkCommon.h" #include "mitkEvent.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" namespace mitk { //##Documentation //## @brief Event on 3D Mouse input //## //## Seven coordinates exposed by the 3D Mouse: //## 3-dimensional translation vector //## 3-dimensional rotation achsis (length allways 1.0) //## scalar rotation angle //## @ingroup Interaction class TDMouseEvent : public Event { public: //##Documentation //## @brief Constructor with all necessary arguments. //## //## buttonState: information from the Event TDMouseEvent(int buttonState, const Vector3D& translation, const Vector3D& rotation, const ScalarType& angle); const Vector3D& GetTranslation() const { return m_Translation; } void SetTranslation(const Vector3D& translation) { m_Translation = translation; } const Vector3D& GetRotation() const { return m_Rotation; } void SetRotation(const Vector3D& rotation) { m_Rotation = rotation; } const ScalarType& GetAngle() const { return m_Angle; } void SetAngle(const ScalarType& angle) { m_Angle = angle; } protected: Vector3D m_Translation; Vector3D m_Rotation; ScalarType m_Angle; }; } // namespace mitk #endif /* TDMOUSEEVENT_H_HEADER_INCLUDED */ diff --git a/Modules/Ext/Interactions/mitkTDMouseEventThrower.cpp b/Modules/Ext/Interactions/mitkTDMouseEventThrower.cpp index bac5f67267..b419761930 100644 --- a/Modules/Ext/Interactions/mitkTDMouseEventThrower.cpp +++ b/Modules/Ext/Interactions/mitkTDMouseEventThrower.cpp @@ -1,64 +1,64 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTDMouseEventThrower.h" #include "mitkTDMouseEvent.h" #include "mitkInteractionConst.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkGlobalInteraction.h" #include "mitkStateEvent.h" #include "mitkSpaceNavigatorDriver.h" mitk::TDMouseEventThrower * mitk::TDMouseEventThrower::GetInstance() { //instance will not be initialize and return 0 static TDMouseEventThrower instance; return &instance; } mitk::TDMouseEventThrower::TDMouseEventThrower() { //init the driver SpaceNavigatorDriver* spaceNavigatorDriver = SpaceNavigatorDriver::GetInstance(); } mitk::TDMouseEventThrower::~TDMouseEventThrower() { } void mitk::TDMouseEventThrower::DeviceChange (long device, long keys, long programmableKeys) { } void mitk::TDMouseEventThrower::KeyDown (int keyCode) { //send the informations to GlobalInteraction mitk::Event* e = new mitk::Event(NULL, mitk::Type_TDMouseKeyDown, mitk::BS_LeftButton, keyCode, mitk::Key_none); mitk::StateEvent* se = new mitk::StateEvent(mitk::EIDTDMOUSEKEYDOWN, e); mitk::GlobalInteraction::GetInstance()->HandleEvent(se); } void mitk::TDMouseEventThrower::KeyUp (int keyCode) { } void mitk::TDMouseEventThrower::SensorInput( mitk::Vector3D translation, mitk::Vector3D rotation, mitk::ScalarType angle) { mitk::TDMouseEvent* e = new mitk::TDMouseEvent(mitk::BS_NoButton, translation, rotation, angle); mitk::StateEvent* se = new mitk::StateEvent(mitk::EIDTDMOUSEINPUT, e); mitk::GlobalInteraction::GetInstance()->HandleEvent(se); } diff --git a/Modules/Ext/Interactions/mitkTDMouseEventThrower.h b/Modules/Ext/Interactions/mitkTDMouseEventThrower.h index 0189ed1594..9f86a61f32 100644 --- a/Modules/Ext/Interactions/mitkTDMouseEventThrower.h +++ b/Modules/Ext/Interactions/mitkTDMouseEventThrower.h @@ -1,44 +1,44 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef TDMouseEventThrower__h__ #define TDMouseEventThrower__h__ -#include +#include #include namespace mitk { class TDMouseEventThrower { public: static TDMouseEventThrower* GetInstance(); void DeviceChange (long device, long keys, long programmableKeys); void KeyDown (int keyCode); void KeyUp (int keyCode); void SensorInput( mitk::Vector3D translation, mitk::Vector3D rotation, mitk::ScalarType angle); protected: TDMouseEventThrower(); ~TDMouseEventThrower(); }; }//namespace #endif // TDMouseEventThrower__h__ diff --git a/Modules/Ext/Testing/mitkPlaneFitTest.cpp b/Modules/Ext/Testing/mitkPlaneFitTest.cpp index 2b038c967c..43a2a1ef27 100644 --- a/Modules/Ext/Testing/mitkPlaneFitTest.cpp +++ b/Modules/Ext/Testing/mitkPlaneFitTest.cpp @@ -1,98 +1,98 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include -#include +#include #include #include int mitkPlaneFitTest(int, char*[] ) { //float bounds[]={0.0f,10.0f,0.0f,10.0f,0.0f,5.0f}; mitk::PlaneFit::Pointer PlaneFit = mitk::PlaneFit::New(); mitk::PointSet::Pointer PointSet = mitk::PointSet::New(); mitk::PlaneGeometry::Pointer planeGeo= mitk::PlaneGeometry::New(); mitk::BaseGeometry::Pointer BaseGeometry = dynamic_cast(planeGeo.GetPointer()); mitk::Point3D Point; //first without any point, then incrementally add points within thre points there will be a plane geometry std::cout <<"Start PlaneFitTest "<GetPointSet()->GetPoints()->InsertElement(position, Point); } //Set Input PlaneFit->SetInput(PointSet); const mitk::PointSet* testPointSet = PlaneFit->GetInput(); std::cout<<" Size test of Input Method: "; if( testPointSet->GetSize() == PointSet->GetSize() ) { std::cout<<"[PASSED]"<Update(); const mitk::Point3D ¢roid = PlaneFit->GetCentroid(); mitk::Point3D expectedCentroid; expectedCentroid[0]=2.5; expectedCentroid[1]=3.75; expectedCentroid[2]=2.5; if ( centroid == expectedCentroid ) { std::cout<<"[PASSED]"<( PlaneFit->GetOutput()->GetGeometry()); if( PlaneGeometry ) { std::cout<<"[PASSED]"< #include #include -#include +#include namespace mitk { /**Documentation * \brief Navigation Data * * This class represents the data object that is passed through the MITK-IGT navigation filter * pipeline. It encapsulates position and orientation of a tracked tool/sensor. Additionally, * it contains a data structure that contains error/plausibility information * * It provides methods to work with the affine transformation represented by its orientation and position. * Additionally, it provides a constructor to construct a NavigationData object from an AffineTransform3D and * a getter to create an AffineTransform3D from a NavigationData object. * * \ingroup IGT */ class MitkIGT_EXPORT NavigationData : public itk::DataObject { public: mitkClassMacro(NavigationData, itk::DataObject); itkFactorylessNewMacro(Self) itkCloneMacro(Self) mitkNewMacro2Param(Self, mitk::AffineTransform3D::Pointer, const bool); mitkNewMacro1Param(Self, mitk::AffineTransform3D::Pointer); /** * \brief Type that holds the position part of the tracking data */ typedef mitk::Point3D PositionType; /** * \brief Type that holds the orientation part of the tracking data */ typedef mitk::Quaternion OrientationType; /** * \brief type that holds the error characterization of the position and orientation measurements */ typedef itk::Matrix CovarianceMatrixType; /** * \brief type that holds the time at which the data was recorded */ typedef double TimeStampType; /** * \brief sets the position of the NavigationData object */ itkSetMacro(Position, PositionType); /** * \brief returns position of the NavigationData object */ itkGetConstMacro(Position, PositionType); /** * \brief sets the orientation of the NavigationData object */ itkSetMacro(Orientation, OrientationType); /** * \brief returns the orientation of the NavigationData object */ itkGetConstMacro(Orientation, OrientationType); /** * \brief returns true if the object contains valid data */ virtual bool IsDataValid() const; /** * \brief sets the dataValid flag of the NavigationData object indicating if the object contains valid data */ itkSetMacro(DataValid, bool); /** * \brief sets the IGT timestamp of the NavigationData object */ itkSetMacro(IGTTimeStamp, TimeStampType); /** * \brief gets the IGT timestamp of the NavigationData object */ itkGetConstMacro(IGTTimeStamp, TimeStampType); /** * \brief sets the HasPosition flag of the NavigationData object */ itkSetMacro(HasPosition, bool); /** * \brief gets the HasPosition flag of the NavigationData object */ itkGetConstMacro(HasPosition, bool); /** * \brief sets the HasOrientation flag of the NavigationData object */ itkSetMacro(HasOrientation, bool); /** * \brief gets the HasOrientation flag of the NavigationData object */ itkGetConstMacro(HasOrientation, bool); /** * \brief sets the 6x6 Error Covariance Matrix of the NavigationData object */ itkSetMacro(CovErrorMatrix, CovarianceMatrixType); /** * \brief gets the 6x6 Error Covariance Matrix of the NavigationData object */ itkGetConstMacro(CovErrorMatrix, CovarianceMatrixType); /** * \brief set the name of the NavigationData object */ itkSetStringMacro(Name); /** * \brief returns the name of the NavigationData object */ itkGetStringMacro(Name); /** * \brief Graft the data and information from one NavigationData to another. * * Copies the content of data into this object. * This is a convenience method to setup a second NavigationData object with all the meta * information of another NavigationData object. * Note that this method is different than just using two * SmartPointers to the same NavigationData object since separate DataObjects are * still maintained. */ virtual void Graft(const DataObject *data); /** * \brief copy meta data of a NavigationData object * * copies all meta data from NavigationData data to this object */ virtual void CopyInformation(const DataObject* data); /** * \brief Prints the object information to the given stream os. * \param os The stream which is used to print the output. * \param indent Defines the indentation of the output. */ void PrintSelf(std::ostream& os, itk::Indent indent) const; /** * Set the position part of m_CovErrorMatrix to I*error^2 * This means that all position variables are assumed to be independent */ void SetPositionAccuracy(mitk::ScalarType error); /** * Set the orientation part of m_CovErrorMatrix to I*error^2 * This means that all orientation variables are assumed to be independent */ void SetOrientationAccuracy(mitk::ScalarType error); /** * \brief Calculate AffineTransform3D from the transformation held by this NavigationData. * TODO: should throw an error if transformation is invalid. */ mitk::AffineTransform3D::Pointer GetAffineTransform3D() const; /** * \brief Calculate the RotationMatrix of this transformation. */ mitk::Matrix3D GetRotationMatrix() const; /** * \brief Transform by an affine transformation * * This method applies the affine transform given by self to a * given point, returning the transformed point. */ mitk::Point3D TransformPoint(const mitk::Point3D point) const; /** * Get inverse of the Transformation represented by this NavigationData. * @throws mitk::Exception in case the transformation is invalid (only case: quaternion is zero) */ mitk::NavigationData::Pointer GetInverse() const; /** Compose with another NavigationData * * This method composes self with another NavigationData of the * same dimension, 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. */ void Compose(const mitk::NavigationData::Pointer n, const bool pre = false); protected: mitkCloneMacro(Self); NavigationData(); /* * Copy constructor internally used. */ NavigationData(const mitk::NavigationData& toCopy); /** * Creates a NavigationData object from an affineTransform3D. * Caution: NavigationData doesn't support spacing, only translation and rotation. If the affine * transform includes spacing it cannot be converted to a NavigationData and an exception is thrown. * @param checkForRotationMatrix if this is true, the rotation matrix coming from the affineTransform is checked * for being a rotation matrix. If it isn't, an exception is thrown. Disable this check by * setting checkForRotationMatrix to false. * * @throws mitkException if checkForRotationMatrix is true and a non rotation matrix was introduced by * AffineTransform. */ NavigationData(mitk::AffineTransform3D::Pointer affineTransform3D, const bool checkForRotationMatrix = true); virtual ~NavigationData(); /** * \brief holds the position part of the tracking data */ PositionType m_Position; /** * \brief holds the orientation part of the tracking data */ OrientationType m_Orientation; /** * \brief A 6x6 covariance matrix parameterizing the Gaussian error * distribution of the measured position and orientation. * * The hasPosition/hasOrientation fields define which entries * are valid. */ CovarianceMatrixType m_CovErrorMatrix; ///< holds the error characterization of the position and orientation /** * \brief defines if position part of m_CovErrorMatrix is valid */ bool m_HasPosition; /** * \brief defines if orientation part of m_CovErrorMatrix is valid */ bool m_HasOrientation; /** * \brief defines if the object contains valid values */ bool m_DataValid; /** * \brief contains the time at which the tracking data was recorded */ TimeStampType m_IGTTimeStamp; /** * \brief name of the navigation data */ std::string m_Name; private: void ResetCovarianceValidity(); // pre = false static mitk::NavigationData::Pointer getComposition(const mitk::NavigationData::Pointer nd1, const mitk::NavigationData::Pointer nd2); }; } // namespace mitk #endif /* MITKNAVIGATIONDATA_H_HEADER_INCLUDED_ */ diff --git a/Modules/IGT/Testing/mitkInternalTrackingToolTest.cpp b/Modules/IGT/Testing/mitkInternalTrackingToolTest.cpp index 4ef454d2c4..cb631afce4 100644 --- a/Modules/IGT/Testing/mitkInternalTrackingToolTest.cpp +++ b/Modules/IGT/Testing/mitkInternalTrackingToolTest.cpp @@ -1,204 +1,185 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkInternalTrackingTool.h" #include "mitkTestingMacros.h" #include #include /**Documentation * NDIPassiveTool has a protected constructor and a protected itkFactorylessNewMacro * so that only it's friend class NDITrackingDevice is able to instantiate * tool objects. Therefore, we derive from NDIPassiveTool and add a * public itkFactorylessNewMacro, so that we can instantiate and test the class */ class InternalTrackingToolTestClass : public mitk::InternalTrackingTool { public: mitkClassMacro(InternalTrackingToolTestClass, InternalTrackingTool); /** make a public constructor, so that the test is able * to instantiate NDIPassiveTool */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) protected: InternalTrackingToolTestClass() : mitk::InternalTrackingTool() { } public: //these static methods are only to structure the test //please see them seperated from the upper part of the class static void TestBasicFunctionality() { // let's create an object of our class mitk::InternalTrackingTool::Pointer internalTrackingTool = InternalTrackingToolTestClass::New().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(internalTrackingTool.IsNotNull(),"Testing instantiation"); // test for Enable() internalTrackingTool->Enable(); MITK_TEST_CONDITION((internalTrackingTool->IsEnabled()==true),"Testing of Enable()"); srand(time(NULL)); // generate a random position to test Set/GetPosition() mitk::Point3D position; position[0] = rand()%1000; position[1] = rand()%1000; position[2] = rand()%1000; internalTrackingTool->SetPosition(position); mitk::Point3D returnedPosition; returnedPosition.Fill(0); internalTrackingTool->GetPosition(returnedPosition); MITK_TEST_CONDITION((position==returnedPosition),"Testing of Set/GetPosition()"); // generate a random orientation to test Set/GetOrientation() mitk::Quaternion orientation; orientation[0] = (rand()%1000)/1000.0; orientation[1] = (rand()%1000)/1000.0; orientation[2] = (rand()%1000)/1000.0; orientation[3] = (rand()%1000)/1000.0; internalTrackingTool->SetOrientation(orientation); mitk::Quaternion returnedOrientation(0,0,0,0); internalTrackingTool->GetOrientation(returnedOrientation); MITK_TEST_CONDITION((orientation==returnedOrientation),"Testing of Set/GetQuaternion()"); // test Set/GetTrackingError() float trackingError = rand()%2; internalTrackingTool->SetTrackingError(trackingError); MITK_TEST_CONDITION((internalTrackingTool->GetTrackingError()==trackingError),"Testing of Set/GetTrackingError()"); // test Set/GetDataValid() internalTrackingTool->SetDataValid(true); MITK_TEST_CONDITION((internalTrackingTool->IsDataValid()==true),"Testing of SetDataValid and IsDataValid() for parameter 'true'"); internalTrackingTool->SetDataValid(false); MITK_TEST_CONDITION((internalTrackingTool->IsDataValid()==false),"Testing of SetDataValid and IsDataValid() for parameter 'false'"); internalTrackingTool->Disable(); MITK_TEST_CONDITION((internalTrackingTool->IsEnabled()==false),"Testing of Disable()"); } static void TestTooltipFunctionality() { mitk::InternalTrackingTool::Pointer internalTrackingTool = InternalTrackingToolTestClass::New().GetPointer(); mitk::Point3D toolTipPos; mitk::FillVector3D(toolTipPos,1,1,1); mitk::Quaternion toolTipQuat = mitk::Quaternion(0,0,0,1); internalTrackingTool->SetToolTip(toolTipPos,toolTipQuat); mitk::Point3D positionInput; mitk::FillVector3D(positionInput,5,6,7); internalTrackingTool->SetPosition(positionInput); mitk::Point3D positionOutput; internalTrackingTool->GetPosition(positionOutput); MITK_TEST_CONDITION(((positionOutput[0] == 6)&& (positionOutput[0] == 6)&& (positionOutput[0] == 6)&& (positionOutput[0] == 6)), "Testing tooltip definition." ); } static void TestModiciationTimeCorrectness() { mitk::InternalTrackingTool::Pointer tool = InternalTrackingToolTestClass::New().GetPointer(); unsigned long mTime1 = tool->GetMTime(); mitk::Point3D position1; mitk::FillVector3D(position1, 1.1, 2.2, 3.3); tool->SetPosition(position1); MITK_TEST_CONDITION( mTime1 < tool->GetMTime(), "Testing MTime updated after initial position set" ); mitk::Quaternion quat1 = mitk::Quaternion(0,0,0.70710678118654757,0.70710678118654757); tool->SetOrientation(quat1); MITK_TEST_CONDITION( mTime1 < tool->GetMTime(), "Testing MTime updated after initial orientation set" ); unsigned long mTime2 = tool->GetMTime(); - tool->SetPosition(position1); - MITK_TEST_CONDITION( mTime2 == tool->GetMTime(), - "Testing MTime NOT updated after same initial position set" ); - - tool->SetOrientation(quat1); - MITK_TEST_CONDITION( mTime2 == tool->GetMTime(), - "Testing MTime NOT updated after same initial orientation set" ); - mitk::Point3D position2; mitk::FillVector3D(position2, 1.10001, 2.2, 3.3); tool->SetPosition(position2); MITK_TEST_CONDITION( mTime2 < tool->GetMTime(), "Testing MTime updated after new position set" ); unsigned long mTime3 = tool->GetMTime(); mitk::Quaternion quat2 = mitk::Quaternion(0.0, 0.0, 0.70710678118654757, 0.70710678118654757 + 0.00001); tool->SetOrientation(quat2); MITK_TEST_CONDITION( mTime3 < tool->GetMTime(), "Testing MTime updated after new orientation set" ); unsigned long mTime4 = tool->GetMTime(); mitk::Point3D position3; mitk::FillVector3D(position3, 1.10002, 2.2, 3.3); - tool->SetPosition(position3, 0.001); - MITK_TEST_CONDITION( mTime4 == tool->GetMTime(), - "Testing MTime NOT updated after position set within epsilon tolerance" ); - - mitk::Quaternion quat3 = mitk::Quaternion(0.0, - 0.0, - 0.70710678118654757, - 0.70710678118654757 + 0.00002); - tool->SetOrientation(quat3, 0.001); - MITK_TEST_CONDITION( mTime4 == tool->GetMTime(), - "Testing MTime NOT updated after orientation set within epsilon tolerance" ); } }; /** * Simple example for a test for the class "InternalTrackingTool". * * argc and argv are the command line parameters which were passed to * the ADD_TEST command in the CMakeLists.txt file. For the automatic * tests, argv is either empty for the simple tests or contains the filename * of a test image for the image tests (see CMakeLists.txt). */ int mitkInternalTrackingToolTest(int /* argc */, char* /*argv*/[]) { // always start with this! MITK_TEST_BEGIN("InternalTrackingTool") InternalTrackingToolTestClass::TestBasicFunctionality(); InternalTrackingToolTestClass::TestTooltipFunctionality(); InternalTrackingToolTestClass::TestModiciationTimeCorrectness(); // always end with this! MITK_TEST_END(); } diff --git a/Modules/IGT/Testing/mitkNavigationDataReferenceTransformFilterTest.cpp b/Modules/IGT/Testing/mitkNavigationDataReferenceTransformFilterTest.cpp index 1ac0b8eb24..fa314b06ee 100644 --- a/Modules/IGT/Testing/mitkNavigationDataReferenceTransformFilterTest.cpp +++ b/Modules/IGT/Testing/mitkNavigationDataReferenceTransformFilterTest.cpp @@ -1,226 +1,227 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkNavigationDataReferenceTransformFilter.h" #include "mitkNavigationData.h" #include "mitkTestingMacros.h" #include #include /**Documentation * test for the class "NavigationDataReferenceTransformFilter". */ int mitkNavigationDataReferenceTransformFilterTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("NavigationDataReferenceTransformFilter") // let's create an object of our class mitk::NavigationDataReferenceTransformFilter::Pointer myFilter = mitk::NavigationDataReferenceTransformFilter::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(myFilter.IsNotNull(),"Testing instantiation"); /*create helper objects: positions of the ND sources*/ mitk::Point3D sourcePos1,sourcePos2, sourcePos3, targetPos1, targetPos2, targetPos3; mitk::FillVector3D(sourcePos1, 11.1, 11.1, 11.1); mitk::FillVector3D(sourcePos2, 22.2, 22.2, 22.2); mitk::FillVector3D(sourcePos3, 33.3, 33.3, 33.3); mitk::FillVector3D(targetPos1, -1.1, -2.2, -3.3); mitk::FillVector3D(targetPos2, -4.4, -5.5, -6.6); mitk::FillVector3D(targetPos3, -7.7, -8.8, -9.9); /*create helper objects: orientations of the ND sources*/ mitk::NavigationData::OrientationType sourceOri1(0.1, 0.1, 0.1, 0.1); mitk::NavigationData::OrientationType sourceOri2(0.2, 0.2, 0.2, 0.2); mitk::NavigationData::OrientationType sourceOri3(0.3, 0.3, 0.3, 0.3); mitk::NavigationData::OrientationType targetOri1(0.4, 0.4, 0.4, 0.4); mitk::NavigationData::OrientationType targetOri2(0.5, 0.5, 0.5, 0.5); mitk::NavigationData::OrientationType targetOri3(0.6, 0.6, 0.6, 0.6); /*create helper objects: ND position accurancy and validity bool*/ mitk::ScalarType initialError(0.0); bool initialValid(true); /*create helper objects: NDs for the source and target NDs*/ mitk::NavigationData::Pointer snd1 = mitk::NavigationData::New(); snd1->SetPosition(sourcePos1); snd1->SetOrientation(sourceOri1); snd1->SetPositionAccuracy(initialError); snd1->SetDataValid(initialValid); mitk::NavigationData::Pointer snd2 = mitk::NavigationData::New(); snd2->SetPosition(sourcePos2); snd2->SetOrientation(sourceOri2); snd2->SetPositionAccuracy(initialError); snd2->SetDataValid(initialValid); mitk::NavigationData::Pointer snd3 = mitk::NavigationData::New(); snd3->SetPosition(sourcePos3); snd3->SetOrientation(sourceOri3); snd3->SetPositionAccuracy(initialError); snd3->SetDataValid(initialValid); mitk::NavigationData::Pointer tnd1 = mitk::NavigationData::New(); tnd1->SetPosition(targetPos1); tnd1->SetOrientation(targetOri1); tnd1->SetPositionAccuracy(initialError); tnd1->SetDataValid(initialValid); mitk::NavigationData::Pointer tnd2 = mitk::NavigationData::New(); tnd2->SetPosition(targetPos2); tnd2->SetOrientation(targetOri2); tnd2->SetPositionAccuracy(initialError); tnd2->SetDataValid(initialValid); mitk::NavigationData::Pointer tnd3 = mitk::NavigationData::New(); tnd3->SetPosition(targetPos3); tnd3->SetOrientation(targetOri3); tnd3->SetPositionAccuracy(initialError); tnd3->SetDataValid(initialValid); std::vector emptySourceNDs; std::vector oneSourceNDs; oneSourceNDs.push_back(snd1); std::vector twoSourceNDs; twoSourceNDs.push_back(snd1); twoSourceNDs.push_back(snd2); std::vector threeSourceNDs; threeSourceNDs.push_back(snd1); threeSourceNDs.push_back(snd2); threeSourceNDs.push_back(snd3); std::vector emptyTargetNDs; std::vector oneTargetNDs; oneTargetNDs.push_back(tnd1); std::vector twoTargetNDs; twoTargetNDs.push_back(tnd1); twoTargetNDs.push_back(tnd2); std::vector threeTargetNDs; threeTargetNDs.push_back(tnd1); threeTargetNDs.push_back(tnd2); threeTargetNDs.push_back(tnd3); // ------------------ setting no NDs ------------------ myFilter->SetSourceNavigationDatas(emptySourceNDs); MITK_TEST_CONDITION_REQUIRED(myFilter->GetSourceLandmarks()->IsEmpty() == true, "Testing behaviour if setting no source NDs"); myFilter->SetSourceNavigationDatas(emptyTargetNDs); MITK_TEST_CONDITION_REQUIRED(myFilter->GetTargetLandmarks()->IsEmpty() == true, "Testing behaviour if setting no target NDs"); // ------------------ setting one ND ------------------ myFilter->SetSourceNavigationDatas(oneSourceNDs); MITK_TEST_CONDITION_REQUIRED(myFilter->GetSourceLandmarks()->GetSize() == 3, "Testing if 3 source points are generated from one source ND"); myFilter->SetTargetNavigationDatas(oneTargetNDs); MITK_TEST_CONDITION_REQUIRED(myFilter->GetTargetLandmarks()->GetSize() == 3, "Testing if 3 target points are generated from one target ND"); // ------------------ setting two NDs ------------------ myFilter->SetSourceNavigationDatas(twoSourceNDs); MITK_TEST_CONDITION_REQUIRED(myFilter->GetSourceLandmarks()->GetSize() == 6, "Testing if 6 source points are generated from two source NDs"); myFilter->SetTargetNavigationDatas(twoTargetNDs); MITK_TEST_CONDITION_REQUIRED(myFilter->GetTargetLandmarks()->GetSize() == 6, "Testing if 6 target points are generated from two target NDs"); // ------------------ setting three NDs ------------------ myFilter->SetSourceNavigationDatas(threeSourceNDs); MITK_TEST_CONDITION_REQUIRED(myFilter->GetSourceLandmarks()->GetSize() == 3, "Testing if 3 source NDs are passed to 3 source points"); myFilter->SetTargetNavigationDatas(threeTargetNDs); MITK_TEST_CONDITION_REQUIRED(myFilter->GetTargetLandmarks()->GetSize() == 3, "Testing if 3 target NDs are passed to 3 target points"); // ------------------ setting different number of NDs for source and target ------------------ bool firstInitialize = myFilter->InitializeTransform(); myFilter->SetTargetNavigationDatas(twoTargetNDs); MITK_TEST_CONDITION_REQUIRED((firstInitialize == true && myFilter->InitializeTransform() == false), "Testing if initialization is denied, if different number of source and target NDs are set"); // ------------------ reinit of this filter ------------------ bool sourcePointsSet = myFilter->GetSourceLandmarks()->GetSize() > 0; bool targetPointsSet = myFilter->GetTargetLandmarks()->GetSize() > 0; MITK_TEST_CONDITION_REQUIRED(sourcePointsSet && targetPointsSet, "Testing if there are source and target landmarks set in the superclass"); myFilter->ReinitFilter(); bool sourcePointsCleared = myFilter->GetSourceLandmarks()->GetSize() == 0; bool targetPointsCleared = myFilter->GetTargetLandmarks()->GetSize() == 0; MITK_TEST_CONDITION_REQUIRED(sourcePointsCleared && targetPointsCleared, "Testing if reinit of filter was successful"); // ------------------ testing the point generation ------------------ myFilter->SetSourceNavigationDatas(oneSourceNDs); // set the ND with sourcePos1 and sourceOri1 for that the points will be generated itk::QuaternionRigidTransform::Pointer quaternionTransform = itk::QuaternionRigidTransform::New(); vnl_quaternion const vnlQuatIn(sourceOri1.x(), sourceOri1.y(), sourceOri1.z(), sourceOri1.r()); quaternionTransform->SetRotation(vnlQuatIn); mitk::Point3D pointA; mitk::Point3D pointB; mitk::Point3D pointC; //initializing three points with position(0|0|0) pointA.Fill(0); pointB.Fill(0); pointC.Fill(0); // changing position off all points in order to make them orthogonal pointA[0] = 1; pointB[1] = 1; pointC[2] = 1; // quaternion transform the points pointA = quaternionTransform->GetMatrix() * pointA; pointB = quaternionTransform->GetMatrix() * pointB; pointC = quaternionTransform->GetMatrix() * pointC; // now subtract them from the filter landmarks and compare them to the source pos + pointA = myFilter->GetSourceLandmarks()->GetPoint(0)-pointA; pointB = myFilter->GetSourceLandmarks()->GetPoint(1)-pointB; pointC = myFilter->GetSourceLandmarks()->GetPoint(2)-pointC; MITK_TEST_CONDITION_REQUIRED(mitk::Equal(sourcePos1,pointA,mitk::eps,true), "Testing if point generation of first point is correct"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(sourcePos1,pointB,mitk::eps,true), "Testing if point generation of second point is correct"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(sourcePos1,pointC,mitk::eps,true), "Testing if point generation of third point is correct"); // deleting helper objects myFilter = NULL; quaternionTransform = NULL; snd1 = NULL; snd2 = NULL; snd3 = NULL; tnd1 = NULL; tnd2 = NULL; tnd3 = NULL; // always end with this! MITK_TEST_END(); } diff --git a/Modules/IGT/Testing/mitkNavigationDataToMessageFilterTest.cpp b/Modules/IGT/Testing/mitkNavigationDataToMessageFilterTest.cpp index c0553c159f..c97fdada8a 100644 --- a/Modules/IGT/Testing/mitkNavigationDataToMessageFilterTest.cpp +++ b/Modules/IGT/Testing/mitkNavigationDataToMessageFilterTest.cpp @@ -1,229 +1,229 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkNavigationDataToMessageFilter.h" #include "mitkNavigationData.h" #include "mitkTestingMacros.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include // Receiver class remembers Messages received. // Will tell about received Messages when asked. class MessageReceiverClass { public: MessageReceiverClass(unsigned int numberOfNavigationDatas) { m_ReceivedData.resize(numberOfNavigationDatas); for (unsigned int i = 0; i < numberOfNavigationDatas; ++i) m_ReceivedData[i] = mitk::NavigationData::New(); m_MessagesReceived = 0; } void OnPositionChanged(mitk::NavigationData::PositionType v, unsigned int index) { m_ReceivedData[index]->SetPosition(v); ++m_MessagesReceived; } void OnOrientationChanged(mitk::NavigationData::OrientationType v, unsigned int index) { m_ReceivedData[index]->SetOrientation(v); ++m_MessagesReceived; } void OnErrorChanged(mitk::NavigationData::CovarianceMatrixType v, unsigned int index) { m_ReceivedData[index]->SetCovErrorMatrix(v); ++m_MessagesReceived; } void OnTimeStampChanged(mitk::NavigationData::TimeStampType v, unsigned int index) { m_ReceivedData[index]->SetIGTTimeStamp(v); ++m_MessagesReceived; } void OnDataValidChanged(bool v, unsigned int index) { m_ReceivedData[index]->SetDataValid(v); ++m_MessagesReceived; } std::vector m_ReceivedData; int m_MessagesReceived; }; /**Documentation * test for the class "NavigationDataToMessageFilter". */ int mitkNavigationDataToMessageFilterTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("NavigationDataToMessageFilter"); /* first tests with one input */ { // let's create an object of our class mitk::NavigationDataToMessageFilter::Pointer myFilter = mitk::NavigationDataToMessageFilter::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(myFilter.IsNotNull(),"Testing instantiation"); /* create helper objects: navigation data with position as origin, zero quaternion, zero error and data valid */ mitk::NavigationData::PositionType initialPos; mitk::FillVector3D(initialPos, 1.0, 2.0, 3.0); mitk::NavigationData::OrientationType initialOri(1.0, 2.0, 3.0, 4.0); mitk::NavigationData::Pointer nd1 = mitk::NavigationData::New(); nd1->SetPosition(initialPos); nd1->SetOrientation(initialOri); nd1->SetPositionAccuracy(11.111); nd1->SetIGTTimeStamp(64.46); nd1->SetDataValid(true); myFilter->SetInput(nd1); MITK_TEST_CONDITION(myFilter->GetInput() == nd1, "testing Set-/GetInput()"); mitk::NavigationData* output = myFilter->GetOutput(); MITK_TEST_CONDITION_REQUIRED(output != NULL, "Testing GetOutput()"); /* register message receiver */ MessageReceiverClass answers(1); myFilter->AddPositionChangedListener(mitk::MessageDelegate2(&answers, &MessageReceiverClass::OnPositionChanged)); myFilter->AddOrientationChangedListener(mitk::MessageDelegate2(&answers, &MessageReceiverClass::OnOrientationChanged)); myFilter->AddErrorChangedListener(mitk::MessageDelegate2(&answers, &MessageReceiverClass::OnErrorChanged)); myFilter->AddTimeStampChangedListener(mitk::MessageDelegate2(&answers, &MessageReceiverClass::OnTimeStampChanged)); myFilter->AddDataValidChangedListener(mitk::MessageDelegate2(&answers, &MessageReceiverClass::OnDataValidChanged)); output->Update(); // execute filter MITK_TEST_CONDITION( mitk::Equal(answers.m_ReceivedData[0]->GetPosition(), nd1->GetPosition()), "Testing PositionChanged message"); MITK_TEST_CONDITION( mitk::Equal(answers.m_ReceivedData[0]->GetOrientation(), nd1->GetOrientation()), "Testing OrientationChanged message"); MITK_TEST_CONDITION( answers.m_ReceivedData[0]->GetCovErrorMatrix() == nd1->GetCovErrorMatrix(), "Testing ErrorChanged message"); MITK_TEST_CONDITION( mitk::Equal(answers.m_ReceivedData[0]->GetIGTTimeStamp(), nd1->GetIGTTimeStamp()), "Testing TimeStampChanged message"); MITK_TEST_CONDITION( answers.m_ReceivedData[0]->IsDataValid() == nd1->IsDataValid(), "Testing PositionChanged message"); MITK_TEST_CONDITION( answers.m_MessagesReceived == 5, "Correct number of messages send?"); /* change one input parameter */ nd1->SetDataValid(false); output->Update(); // re-execute filter MITK_TEST_CONDITION( answers.m_ReceivedData[0]->IsDataValid() == nd1->IsDataValid(), "Testing PositionChanged message"); MITK_TEST_CONDITION( answers.m_MessagesReceived == 6, "only necessary messages send?"); // only datavalid message re-send /* changing two input parameters */ mitk::FillVector3D(initialPos, 11.0, 21.0, 31.0); nd1->SetPosition(initialPos); // change only one parameter nd1->SetIGTTimeStamp(55.55); // change only one parameter output->Update(); // re-execute filter MITK_TEST_CONDITION( mitk::Equal(answers.m_ReceivedData[0]->GetPosition(), nd1->GetPosition()), "Testing PositionChanged message"); MITK_TEST_CONDITION( mitk::Equal(answers.m_ReceivedData[0]->GetIGTTimeStamp(), nd1->GetIGTTimeStamp()), "Testing TimeStampChanged message"); MITK_TEST_CONDITION( answers.m_MessagesReceived == 8, "only necessary messages send?"); // only 2 new messages send /* try to add a second input */ //MITK_TEST_OUTPUT_NO_ENDL( << "Exception on adding second input? --> "); //mitk::NavigationData::Pointer nd2 = mitk::NavigationData::New(); //MITK_TEST_FOR_EXCEPTION(std::invalid_argument, myFilter->SetInput(1, nd2)); } /* now test with multiple inputs */ { MITK_TEST_OUTPUT( << "Now, perform tests with multiple inputs"); mitk::NavigationDataToMessageFilter::Pointer myFilter = mitk::NavigationDataToMessageFilter::New(); /* create helper objects: navigation data with position as origin, zero quaternion, zero error and data valid */ mitk::NavigationData::PositionType initialPos; mitk::FillVector3D(initialPos, 1.0, 1.0, 1.0); mitk::NavigationData::OrientationType initialOri(1.0, 1.0, 1.0, 1.0); mitk::NavigationData::Pointer nd0 = mitk::NavigationData::New(); nd0->SetPosition(initialPos); nd0->SetOrientation(initialOri); nd0->SetPositionAccuracy(11.111); nd0->SetIGTTimeStamp(64.46); nd0->SetDataValid(true); mitk::FillVector3D(initialPos, 2.0, 2.0, 2.0); mitk::NavigationData::OrientationType initialOri2(1.0, 1.0, 1.0, 1.0); mitk::NavigationData::Pointer nd1 = mitk::NavigationData::New(); nd1->SetPosition(initialPos); nd1->SetOrientation(initialOri2); nd1->SetPositionAccuracy(22.222); nd1->SetIGTTimeStamp(222.2); nd1->SetDataValid(true); myFilter->SetInput(0, nd0); myFilter->SetInput(1, nd1); MITK_TEST_CONDITION(myFilter->GetInput(0) == nd0, "testing Set-/GetInput(0)"); MITK_TEST_CONDITION(myFilter->GetInput(1) == nd1, "testing Set-/GetInput(1)"); mitk::NavigationData* output0 = myFilter->GetOutput(); mitk::NavigationData* output1 = myFilter->GetOutput(1); MITK_TEST_CONDITION_REQUIRED(output0 != NULL, "Testing GetOutput()"); MITK_TEST_CONDITION_REQUIRED(output1 != NULL, "Testing GetOutput(1)"); /* register message receiver */ MessageReceiverClass answers(2); myFilter->AddPositionChangedListener(mitk::MessageDelegate2(&answers, &MessageReceiverClass::OnPositionChanged)); myFilter->AddOrientationChangedListener(mitk::MessageDelegate2(&answers, &MessageReceiverClass::OnOrientationChanged)); myFilter->AddErrorChangedListener(mitk::MessageDelegate2(&answers, &MessageReceiverClass::OnErrorChanged)); myFilter->AddTimeStampChangedListener(mitk::MessageDelegate2(&answers, &MessageReceiverClass::OnTimeStampChanged)); myFilter->AddDataValidChangedListener(mitk::MessageDelegate2(&answers, &MessageReceiverClass::OnDataValidChanged)); output0->Update(); // execute filter. This should send messages for both inputs MITK_TEST_CONDITION( mitk::Equal(answers.m_ReceivedData[0]->GetPosition(), nd0->GetPosition()), "Testing PositionChanged message"); MITK_TEST_CONDITION( mitk::Equal(answers.m_ReceivedData[0]->GetOrientation(), nd0->GetOrientation()), "Testing OrientationChanged message"); MITK_TEST_CONDITION( answers.m_ReceivedData[0]->GetCovErrorMatrix() == nd0->GetCovErrorMatrix(), "Testing ErrorChanged message"); MITK_TEST_CONDITION( mitk::Equal(answers.m_ReceivedData[0]->GetIGTTimeStamp(), nd0->GetIGTTimeStamp()), "Testing TimeStampChanged message"); MITK_TEST_CONDITION( answers.m_ReceivedData[0]->IsDataValid() == nd0->IsDataValid(), "Testing PositionChanged message"); MITK_TEST_CONDITION( mitk::Equal(answers.m_ReceivedData[1]->GetPosition(), nd1->GetPosition()), "Testing PositionChanged message"); MITK_TEST_CONDITION( mitk::Equal(answers.m_ReceivedData[1]->GetOrientation(), nd1->GetOrientation()), "Testing OrientationChanged message"); MITK_TEST_CONDITION( answers.m_ReceivedData[1]->GetCovErrorMatrix() == nd1->GetCovErrorMatrix(), "Testing ErrorChanged message"); MITK_TEST_CONDITION( mitk::Equal(answers.m_ReceivedData[1]->GetIGTTimeStamp(), nd1->GetIGTTimeStamp()), "Testing TimeStampChanged message"); MITK_TEST_CONDITION( answers.m_ReceivedData[1]->IsDataValid() == nd1->IsDataValid(), "Testing PositionChanged message"); MITK_TEST_CONDITION( answers.m_MessagesReceived == 10, "Correct number of messages send?"); MITK_TEST_OUTPUT( << "answers.m_MessagesReceived = " << answers.m_MessagesReceived); /* change one input parameter */ nd0->SetDataValid(false); output0->Update(); // re-execute filter MITK_TEST_CONDITION( answers.m_ReceivedData[0]->IsDataValid() == nd0->IsDataValid(), "Testing PositionChanged message for input 0"); MITK_TEST_CONDITION( answers.m_MessagesReceived == 11, "only necessary messages send?"); // only datavalid message for input 0 re-send /* remove one listener and check that message is not send */ myFilter->RemoveTimeStampChangedListener(mitk::MessageDelegate2(&answers, &MessageReceiverClass::OnTimeStampChanged)); mitk::NavigationData::TimeStampType oldValue = nd1->GetIGTTimeStamp(); nd1->SetIGTTimeStamp(999.9); myFilter->Update(); MITK_TEST_CONDITION( ! mitk::Equal(answers.m_ReceivedData[1]->GetIGTTimeStamp(), nd1->GetIGTTimeStamp()), "Testing if TimeStamp message is _not_ send after RemoveListener (!= new value)"); MITK_TEST_CONDITION( mitk::Equal(answers.m_ReceivedData[1]->GetIGTTimeStamp(), oldValue), "Testing if TimeStamp message is _not_ send after RemoveListener (== old value)"); MITK_TEST_CONDITION( answers.m_MessagesReceived == 11, "no new messages send?"); // no new message send? /* other messages are still send? */ nd1->SetDataValid(false); myFilter->Update(); MITK_TEST_CONDITION( answers.m_ReceivedData[1]->IsDataValid() == nd1->IsDataValid(), "Other messages still send? ->Testing PositionChanged message for input 1 again"); MITK_TEST_CONDITION( answers.m_MessagesReceived == 12, "only necessary messages send?"); // only DataValid message for input 1 re-send /* check if other output still has its old value */ MITK_TEST_CONDITION( answers.m_ReceivedData[0]->IsDataValid() == nd0->IsDataValid(), "Testing PositionChanged message for input 0"); } // The end MITK_TEST_END(); } diff --git a/Modules/IGT/TrackingDevices/mitkClaronInterface.cpp b/Modules/IGT/TrackingDevices/mitkClaronInterface.cpp index 38ba997c30..b5c33bbb8d 100644 --- a/Modules/IGT/TrackingDevices/mitkClaronInterface.cpp +++ b/Modules/IGT/TrackingDevices/mitkClaronInterface.cpp @@ -1,291 +1,291 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include -#include +#include mitk::ClaronInterface::ClaronInterface() { isTracking = false; sprintf(calibrationDir,"No calibration dir set yet"); sprintf(markerDir,"No marker dir set yet"); } mitk::ClaronInterface::~ClaronInterface() { } void mitk::ClaronInterface::Initialize(std::string calibrationDir, std::string toolFilesDir) { sprintf(this->calibrationDir, calibrationDir.c_str()); sprintf(this->markerDir,toolFilesDir.c_str()); this->IdentifiedMarkers = 0; this->PoseXf = 0; this->CurrCamera = 0; this->IdentifyingCamera = 0; } bool mitk::ClaronInterface::StartTracking() { isTracking = false; MTC( Cameras_AttachAvailableCameras(calibrationDir) ); //Connect to camera if (Cameras_Count() < 1) { printf("No camera found!\n"); return false; } try { //Step 1: initialize cameras MTC(Cameras_HistogramEqualizeImagesSet(true)); //set the histogram equalizing MTC( Cameras_ItemGet(0, &CurrCamera) ); //Obtain a handle to the first/only camera in the array MITK_INFO< mitk::ClaronInterface::GetAllActiveTools() { //Set returnvalue std::vector returnValue; //Here, MTC internally maintains the measurement results. //Those results can be accessed until the next call to Markers_ProcessFrame, when they //are updated to reflect the next frame's content. //First, we will obtain the collection of the markers that were identified. MTC( Markers_IdentifiedMarkersGet(NULL, IdentifiedMarkers) ); //Now we iterate on the identified markers and add them to the returnvalue for (int j=1; j<=Collection_Count(IdentifiedMarkers); j++) { // Obtain the marker's handle, and use it to obtain the pose in the current camera's space // using our Xform3D object, PoseXf. mtHandle Marker = Collection_Int(IdentifiedMarkers, j); returnValue.push_back(Marker); } return returnValue; } void mitk::ClaronInterface::GrabFrame() { MTC( Cameras_GrabFrame(NULL) ); //Grab a frame MTC( Markers_ProcessFrame(NULL) ); //Process the frame(s) } std::vector mitk::ClaronInterface::GetTipPosition(mitk::claronToolHandle c) { std::vector returnValue; double Position[3]; mtHandle t2m = Xform3D_New(); // tooltip to marker xform handle mtHandle t2c = Xform3D_New(); // tooltip to camera xform handle mtHandle m2c = Xform3D_New(); // marker to camera xform handle //Get m2c MTC( Marker_Marker2CameraXfGet (c, CurrCamera, m2c, &IdentifyingCamera) ); //Get t2m MTC( Marker_Tooltip2MarkerXfGet (c, t2m )); //Transform both to t2c MTC(Xform3D_Concatenate(t2m,m2c,t2c)); //Get position MTC( Xform3D_ShiftGet(t2c, Position) ); // Here we have to negate the X- and Y-coordinates because of a bug of the // MTC-library. returnValue.push_back(-Position[0]); returnValue.push_back(-Position[1]); returnValue.push_back(Position[2]); return returnValue; } std::vector mitk::ClaronInterface::GetPosition(claronToolHandle c) { std::vector returnValue; double Position[3]; MTC( Marker_Marker2CameraXfGet (c, CurrCamera, PoseXf, &IdentifyingCamera) ); MTC( Xform3D_ShiftGet(PoseXf, Position) ); // Here we have to negate the X- and Y-coordinates because of a bug of the // MTC-library. returnValue.push_back(-Position[0]); returnValue.push_back(-Position[1]); returnValue.push_back(Position[2]); return returnValue; } std::vector mitk::ClaronInterface::GetTipQuaternions(claronToolHandle c) { std::vector returnValue; mtHandle t2m = Xform3D_New(); // tooltip to marker xform handle mtHandle t2c = Xform3D_New(); // tooltip to camera xform handle mtHandle m2c = Xform3D_New(); // marker to camera xform handle //Get m2c MTC( Marker_Marker2CameraXfGet (c, CurrCamera, m2c, &IdentifyingCamera) ); //Get t2m MTC( Marker_Tooltip2MarkerXfGet (c, t2m )); //Transform both to t2c MTC(Xform3D_Concatenate(t2m,m2c,t2c)); //get the Claron-Quaternion double Quarternions[4]; MTC( Xform3D_RotQuaternionsGet(t2c, Quarternions) ); mitk::Quaternion claronQuaternion; //note: claron quarternion has different order than the mitk quarternion claronQuaternion[3] = Quarternions[0]; claronQuaternion[0] = Quarternions[1]; claronQuaternion[1] = Quarternions[2]; claronQuaternion[2] = Quarternions[3]; - // Here we have to make a -90°-turn around the Y-axis because of a bug of the + // Here we have to make a -90�-turn around the Y-axis because of a bug of the // MTC-library. mitk::Quaternion minusNinetyDegreeY; minusNinetyDegreeY[3] = sqrt(2.0)/2.0; minusNinetyDegreeY[0] = 0; minusNinetyDegreeY[1] = -1.0/(sqrt(2.0)); minusNinetyDegreeY[2] = 0; //calculate the result... mitk::Quaternion erg = (minusNinetyDegreeY*claronQuaternion); returnValue.push_back(erg[3]); returnValue.push_back(erg[0]); returnValue.push_back(erg[1]); returnValue.push_back(erg[2]); return returnValue; } std::vector mitk::ClaronInterface::GetQuaternions(claronToolHandle c) { std::vector returnValue; double Quarternions[4]; MTC( Marker_Marker2CameraXfGet (c, CurrCamera, PoseXf, &IdentifyingCamera) ); MTC( Xform3D_RotQuaternionsGet(PoseXf, Quarternions) ); //here we have to compensate a bug in the MTC-lib. (difficult to understand) mitk::Quaternion claronQuaternion; //note: claron quarternion has different order than the mitk quarternion claronQuaternion[3] = Quarternions[0]; claronQuaternion[0] = Quarternions[1]; claronQuaternion[1] = Quarternions[2]; claronQuaternion[2] = Quarternions[3]; - // Here we have to make a -90°-turn around the Y-axis because of a bug of the + // Here we have to make a -90�-turn around the Y-axis because of a bug of the // MTC-library. mitk::Quaternion minusNinetyDegreeY; minusNinetyDegreeY[3] = sqrt(2.0)/2.0; minusNinetyDegreeY[0] = 0; minusNinetyDegreeY[1] = -1.0/(sqrt(2.0)); minusNinetyDegreeY[2] = 0; //calculate the result... mitk::Quaternion erg = (minusNinetyDegreeY*claronQuaternion); returnValue.push_back(erg[3]); returnValue.push_back(erg[0]); returnValue.push_back(erg[1]); returnValue.push_back(erg[2]); return returnValue; } const char* mitk::ClaronInterface::GetName(claronToolHandle c) { char MarkerName[MT_MAX_STRING_LENGTH]; MTC( Marker_NameGet(c, MarkerName, MT_MAX_STRING_LENGTH, 0) ); std::string* returnValue = new std::string(MarkerName); return returnValue->c_str(); } bool mitk::ClaronInterface::IsTracking() { return this->isTracking; } bool mitk::ClaronInterface::IsMicronTrackerInstalled() { return true; } diff --git a/Modules/IGT/TrackingDevices/mitkInternalTrackingTool.cpp b/Modules/IGT/TrackingDevices/mitkInternalTrackingTool.cpp index 754f40176f..6e13d9010c 100644 --- a/Modules/IGT/TrackingDevices/mitkInternalTrackingTool.cpp +++ b/Modules/IGT/TrackingDevices/mitkInternalTrackingTool.cpp @@ -1,273 +1,270 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkInternalTrackingTool.h" #include typedef itk::MutexLockHolder MutexLockHolder; mitk::InternalTrackingTool::InternalTrackingTool() : TrackingTool(), m_TrackingError(0.0f), m_Enabled(true), m_DataValid(false), m_ToolTipSet(false) { m_Position[0] = 0.0f; m_Position[1] = 0.0f; m_Position[2] = 0.0f; m_Orientation[0] = 0.0f; m_Orientation[1] = 0.0f; m_Orientation[2] = 0.0f; m_Orientation[3] = 0.0f; // this should not be necessary as the tools bring their own tooltip transformation m_ToolTip[0] = 0.0f; m_ToolTip[1] = 0.0f; m_ToolTip[2] = 0.0f; m_ToolTipRotation[0] = 0.0f; m_ToolTipRotation[1] = 0.0f; m_ToolTipRotation[2] = 0.0f; m_ToolTipRotation[3] = 1.0f; } mitk::InternalTrackingTool::~InternalTrackingTool() { } void mitk::InternalTrackingTool::PrintSelf(std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Position: " << m_Position << std::endl; os << indent << "Orientation: " << m_Orientation << std::endl; os << indent << "TrackingError: " << m_TrackingError << std::endl; os << indent << "Enabled: " << m_Enabled << std::endl; os << indent << "DataValid: " << m_DataValid << std::endl; os << indent << "ToolTip: " << m_ToolTip << std::endl; os << indent << "ToolTipRotation: " << m_ToolTipRotation << std::endl; os << indent << "ToolTipSet: " << m_ToolTipSet << std::endl; } void mitk::InternalTrackingTool::SetToolName(const char* _arg) { itkDebugMacro("setting m_ToolName to " << _arg); MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex if ( _arg && (_arg == this->m_ToolName) ) { return; } if (_arg) { this->m_ToolName= _arg; } else { this->m_ToolName= ""; } this->Modified(); } void mitk::InternalTrackingTool::SetToolName( const std::string _arg ) { this->SetToolName(_arg.c_str()); } void mitk::InternalTrackingTool::GetPosition(mitk::Point3D& position) const { MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex if (m_ToolTipSet) { // Compute the position of tool tip in the coordinate frame of the // tracking device: Rotate the position of the tip into the tracking // device coordinate frame then add to the position of the tracking // sensor vnl_vector pos_vnl = m_Position.GetVnlVector() + m_Orientation.rotate( m_ToolTip.GetVnlVector() ) ; position[0] = pos_vnl[0]; position[1] = pos_vnl[1]; position[2] = pos_vnl[2]; } else { position[0] = m_Position[0]; position[1] = m_Position[1]; position[2] = m_Position[2]; } this->Modified(); } -void mitk::InternalTrackingTool::SetPosition(mitk::Point3D position, mitk::ScalarType eps) +void mitk::InternalTrackingTool::SetPosition(mitk::Point3D position) { itkDebugMacro("setting m_Position to " << position); - if (!Equal(m_Position, position, eps)) - { - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex - m_Position = position; - this->Modified(); - } + + MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + m_Position = position; + this->Modified(); } void mitk::InternalTrackingTool::GetOrientation(mitk::Quaternion& orientation) const { MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex if (m_ToolTipSet) { // Compute the orientation of the tool tip in the coordinate frame of // the tracking device. // // * m_Orientation is the orientation of the sensor relative to the transmitter // * m_ToolTipRotation is the orientation of the tool tip relative to the sensor orientation = m_Orientation * m_ToolTipRotation; } else { orientation = m_Orientation; } } void mitk::InternalTrackingTool::SetToolTip(mitk::Point3D toolTipPosition, mitk::Quaternion orientation, mitk::ScalarType eps) { if ( !Equal(m_ToolTip, toolTipPosition, eps) || !Equal(m_ToolTipRotation, orientation, eps) ) { if( (toolTipPosition[0] == 0) && (toolTipPosition[1] == 0) && (toolTipPosition[2] == 0) && (orientation.x() == 0) && (orientation.y() == 0) && (orientation.z() == 0) && (orientation.r() == 1)) { m_ToolTipSet = false; } else { m_ToolTipSet = true; } m_ToolTip = toolTipPosition; m_ToolTipRotation = orientation; this->Modified(); } } -void mitk::InternalTrackingTool::SetOrientation(mitk::Quaternion orientation, mitk::ScalarType eps) +void mitk::InternalTrackingTool::SetOrientation(mitk::Quaternion orientation) { itkDebugMacro("setting m_Orientation to " << orientation); - if (!Equal(m_Orientation, orientation, eps)) - { - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex - m_Orientation = orientation; - this->Modified(); - } + + MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + m_Orientation = orientation; + this->Modified(); + } void mitk::InternalTrackingTool::SetTrackingError(float error) { itkDebugMacro("setting m_TrackingError to " << error); MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex if (error == m_TrackingError) { return; } m_TrackingError = error; this->Modified(); } float mitk::InternalTrackingTool::GetTrackingError() const { MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex float r = m_TrackingError; return r; } bool mitk::InternalTrackingTool::Enable() { MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex if (m_Enabled == false) { this->m_Enabled = true; this->Modified(); } return true; } bool mitk::InternalTrackingTool::Disable() { MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex if (m_Enabled == true) { this->m_Enabled = false; this->Modified(); } return true; } bool mitk::InternalTrackingTool::IsEnabled() const { MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex return m_Enabled; } bool mitk::InternalTrackingTool::IsTooltipSet() const { MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex return m_ToolTipSet; } bool mitk::InternalTrackingTool::IsDataValid() const { MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex return m_DataValid; } void mitk::InternalTrackingTool::SetDataValid(bool _arg) { itkDebugMacro("setting m_DataValid to " << _arg); if (this->m_DataValid != _arg) { MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex this->m_DataValid = _arg; this->Modified(); } } void mitk::InternalTrackingTool::SetErrorMessage(const char* _arg) { itkDebugMacro("setting m_ErrorMessage to " << _arg); MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex if ((_arg == NULL) || (_arg == this->m_ErrorMessage)) return; if (_arg != NULL) this->m_ErrorMessage = _arg; else this->m_ErrorMessage = ""; this->Modified(); } diff --git a/Modules/IGT/TrackingDevices/mitkInternalTrackingTool.h b/Modules/IGT/TrackingDevices/mitkInternalTrackingTool.h index 3f954c73fc..f583716123 100644 --- a/Modules/IGT/TrackingDevices/mitkInternalTrackingTool.h +++ b/Modules/IGT/TrackingDevices/mitkInternalTrackingTool.h @@ -1,80 +1,80 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKINTERNALTRACKINGTOOL_H_HEADER_INCLUDED_ #define MITKINTERNALTRACKINGTOOL_H_HEADER_INCLUDED_ #include #include -#include +#include #include namespace mitk { /**Documentation * \brief implements TrackingTool interface * * This class is a complete TrackingTool implementation. It can either be used directly by * TrackingDevices, or be subclassed for more specific implementations. * mitk::MicroBirdTrackingDevice uses this class to manage its tools. Other tracking devices * uses specialized versions of this class (e.g. mitk::NDITrackingTool) * * \ingroup IGT */ class MitkIGT_EXPORT InternalTrackingTool : public TrackingTool { friend class MicroBirdTrackingDevice; // Add all TrackingDevice subclasses that use InternalTrackingDevice directly public: mitkClassMacro(InternalTrackingTool, TrackingTool); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; virtual void GetPosition(Point3D& position) const; ///< returns the current position of the tool as an array of three floats (in the tracking device coordinate system) virtual void GetOrientation(Quaternion& orientation) const; ///< returns the current orientation of the tool as a quaternion (in the tracking device coordinate system) virtual bool Enable(); ///< enablea the tool, so that it will be tracked. Returns true if enabling was successfull virtual bool Disable(); ///< disables the tool, so that it will not be tracked anymore. Returns true if disabling was successfull virtual bool IsEnabled() const; ///< returns whether the tool is enabled or disabled virtual bool IsDataValid() const; ///< returns true if the current position data is valid (no error during tracking, tracking error below threshold, ...) virtual float GetTrackingError() const; ///< return one value that corresponds to the overall tracking error. The dimension of this value is specific to each tracking device virtual bool IsTooltipSet() const; ///< returns true if a tooltip is set, false if not virtual void SetToolName(const std::string _arg); ///< Sets the name of the tool virtual void SetToolName(const char* _arg); ///< Sets the name of the tool - virtual void SetPosition(Point3D position, ScalarType eps=0.0); ///< sets the position - virtual void SetOrientation(Quaternion orientation, ScalarType eps=0.0); ///< sets the orientation as a quaternion + virtual void SetPosition(Point3D position); ///< sets the position + virtual void SetOrientation(Quaternion orientation); ///< sets the orientation as a quaternion virtual void SetTrackingError(float error); ///< sets the tracking error virtual void SetDataValid(bool _arg); ///< sets if the tracking data (position & Orientation) is valid virtual void SetErrorMessage(const char* _arg); ///< sets the error message virtual void SetToolTip(Point3D toolTipPosition, Quaternion orientation = Quaternion(0,0,0,1), ScalarType eps=0.0); ///< defines a tool tip for this tool in tool coordinates. GetPosition() and GetOrientation() return the data of the tool tip if it is defined. By default no tooltip is defined. protected: itkFactorylessNewMacro(Self) itkCloneMacro(Self) InternalTrackingTool(); virtual ~InternalTrackingTool(); Point3D m_Position; ///< holds the position of the tool Quaternion m_Orientation; ///< holds the orientation of the tool float m_TrackingError; ///< holds the tracking error of the tool bool m_Enabled; ///< if true, tool is enabled and should receive tracking updates from the tracking device bool m_DataValid; ///< if true, data in m_Position and m_Orientation is valid, e.g. true tracking data Point3D m_ToolTip; Quaternion m_ToolTipRotation; bool m_ToolTipSet; }; } // namespace mitk #endif /* MITKINTERNALTRACKINGTOOL_H_HEADER_INCLUDED_ */ diff --git a/Modules/IGT/TrackingDevices/mitkTrackingTool.h b/Modules/IGT/TrackingDevices/mitkTrackingTool.h index 35ef812f6d..2792080c80 100644 --- a/Modules/IGT/TrackingDevices/mitkTrackingTool.h +++ b/Modules/IGT/TrackingDevices/mitkTrackingTool.h @@ -1,65 +1,65 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKTRACKINGTOOL_H_HEADER_INCLUDED_ #define MITKTRACKINGTOOL_H_HEADER_INCLUDED_ #include #include #include -#include +#include #include namespace mitk { /**Documentation * \brief Interface for all Tracking Tools * * Abstract class that defines the methods that are common for all tracking tools. * * \ingroup IGT */ class MitkIGT_EXPORT TrackingTool : public itk::Object { public: mitkClassMacro(TrackingTool, itk::Object); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; virtual void SetToolTip(Point3D toolTipPosition, Quaternion orientation, ScalarType eps=0.0) = 0; ///< defines a tool tip for this tool in tool coordinates. GetPosition() and GetOrientation() return the data of the tool tip if it is defined. By default no tooltip is defined. virtual void GetPosition(Point3D& position) const = 0; ///< returns the current position of the tool as an array of three floats (in the tracking device coordinate system) virtual void GetOrientation(Quaternion& orientation) const = 0; ///< returns the current orientation of the tool as a quaternion in a mitk::Point4D (in the tracking device coordinate system) virtual bool Enable() = 0; ///< enables the tool, so that it will be tracked virtual bool Disable() = 0; ///< disables the tool, so that it will not be tracked anymore virtual bool IsEnabled() const = 0; ///< returns whether the tool is enabled or disabled virtual bool IsDataValid() const = 0; ///< returns true if the current position data is valid (no error during tracking, tracking error below threshold, ...) virtual float GetTrackingError() const = 0; ///< returns one value that corresponds to the overall tracking error. virtual const char* GetToolName() const; ///< every tool has a name that can be used to identify it. virtual const char* GetErrorMessage() const; ///< if the data is not valid, ErrorMessage should contain a string explaining why it is invalid (the Set-method should be implemented in subclasses, it should not be accessible by the user) itkSetMacro(IGTTimeStamp, double); ///< Sets the IGT timestamp of the tracking tool object (time in milliseconds) itkGetConstMacro(IGTTimeStamp, double); ///< Gets the IGT timestamp of the tracking tool object (time in milliseconds). Returns 0 if the timestamp was not set. protected: TrackingTool(); virtual ~TrackingTool(); std::string m_ToolName; ///< every tool has a name that can be used to identify it. std::string m_ErrorMessage; ///< if a tool is invalid, this member should contain a human readable explanation of why it is invalid double m_IGTTimeStamp; ///< contains the time at which the tracking data was recorded itk::FastMutexLock::Pointer m_MyMutex; ///< mutex to control concurrent access to the tool }; } // namespace mitk #endif /* MITKTRACKINGTOOL_H_HEADER_INCLUDED_ */ diff --git a/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.h b/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.h index 9c5fb8dd69..01ff1322f4 100644 --- a/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.h +++ b/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.h @@ -1,67 +1,67 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKVirtualTrackingTool_H_HEADER_INCLUDED_ #define MITKVirtualTrackingTool_H_HEADER_INCLUDED_ #include #include -#include +#include #include #include namespace mitk { /**Documentation * \brief implements TrackingTool interface * * This class is a complete TrackingTool implementation. It can either be used directly by * TrackingDevices, or be subclassed for more specific implementations. * mitk::MicroBirdTrackingDevice uses this class to manage its tools. Other tracking devices * uses specialized versions of this class (e.g. mitk::NDITrackingTool) * * \ingroup IGT */ class MitkIGT_EXPORT VirtualTrackingTool : public InternalTrackingTool { public: mitkClassMacro(VirtualTrackingTool, InternalTrackingTool); friend class VirtualTrackingDevice; itkFactorylessNewMacro(Self) typedef itk::NonUniformBSpline<3> SplineType; ///< spline type used for tool path interpolation itkGetMacro(SplineLength, mitk::ScalarType); itkSetMacro(SplineLength, mitk::ScalarType); itkGetMacro(Velocity, mitk::ScalarType); itkSetMacro(Velocity, mitk::ScalarType); itkGetObjectMacro(Spline, SplineType); protected: itkCloneMacro(Self) VirtualTrackingTool(); virtual ~VirtualTrackingTool(); SplineType::Pointer m_Spline; mitk::ScalarType m_SplineLength; mitk::ScalarType m_Velocity; }; } // namespace mitk #endif /* MITKVirtualTrackingTool_H_HEADER_INCLUDED_ */ diff --git a/Modules/ImageStatistics/mitkIntensityProfile.cpp b/Modules/ImageStatistics/mitkIntensityProfile.cpp index 74090ec057..6b8f70f61e 100644 --- a/Modules/ImageStatistics/mitkIntensityProfile.cpp +++ b/Modules/ImageStatistics/mitkIntensityProfile.cpp @@ -1,337 +1,337 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include "mitkIntensityProfile.h" using namespace mitk; template -static void ReadPixel(const PixelType&, Image::Pointer image, const Index3D& index, ScalarType* returnValue) +static void ReadPixel(const PixelType&, Image::Pointer image, const itk::Index<3>& index, ScalarType* returnValue) { switch (image->GetDimension()) { case 2: { ImagePixelReadAccessor readAccess(image, image->GetSliceData(0)); *returnValue = readAccess.GetPixelByIndex(reinterpret_cast&>(index)); break; } case 3: { ImagePixelReadAccessor readAccess(image, image->GetVolumeData(0)); *returnValue = readAccess.GetPixelByIndex(index); break; } default: *returnValue = 0; break; } } static IntensityProfile::Pointer ComputeIntensityProfile(Image::Pointer image, itk::PolyLineParametricPath<3>::Pointer path) { IntensityProfile::Pointer intensityProfile = IntensityProfile::New(); itk::PolyLineParametricPath<3>::InputType input = path->StartOfInput(); BaseGeometry* imageGeometry = image->GetGeometry(); const PixelType pixelType = image->GetPixelType(); IntensityProfile::MeasurementVectorType measurementVector; itk::PolyLineParametricPath<3>::OffsetType offset; Point3D worldPoint; - Index3D index; + itk::Index<3> index; do { imageGeometry->IndexToWorld(path->Evaluate(input), worldPoint); imageGeometry->WorldToIndex(worldPoint, index); mitkPixelTypeMultiplex3(ReadPixel, pixelType, image, index, measurementVector.GetDataPointer()); intensityProfile->PushBack(measurementVector); offset = path->IncrementInput(input); } while ((offset[0] | offset[1] | offset[2]) != 0); return intensityProfile; } template static typename itk::InterpolateImageFunction::Pointer CreateInterpolateImageFunction(InterpolateImageFunction::Enum interpolator) { switch (interpolator) { case InterpolateImageFunction::NearestNeighbor: return itk::NearestNeighborInterpolateImageFunction::New().GetPointer(); case InterpolateImageFunction::Linear: return itk::LinearInterpolateImageFunction::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Blackman_3: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Blackman_4: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Blackman_5: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Cosine_3: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Cosine_4: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Cosine_5: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Hamming_3: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Hamming_4: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Hamming_5: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Lanczos_3: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Lanczos_4: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Lanczos_5: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Welch_3: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Welch_4: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Welch_5: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); default: return itk::NearestNeighborInterpolateImageFunction::New().GetPointer(); } } template static void ComputeIntensityProfile(itk::Image* image, itk::PolyLineParametricPath<3>::Pointer path, unsigned int numSamples, InterpolateImageFunction::Enum interpolator, IntensityProfile::Pointer intensityProfile) { typename itk::InterpolateImageFunction >::Pointer interpolateImageFunction = CreateInterpolateImageFunction >(interpolator); interpolateImageFunction->SetInputImage(image); const itk::PolyLineParametricPath<3>::InputType startOfInput = path->StartOfInput(); const itk::PolyLineParametricPath<3>::InputType delta = 1.0 / (numSamples - 1); IntensityProfile::MeasurementVectorType measurementVector; for (unsigned int i = 0; i < numSamples; ++i) { measurementVector[0] = interpolateImageFunction->EvaluateAtContinuousIndex(path->Evaluate(startOfInput + i * delta)); intensityProfile->PushBack(measurementVector); } } static IntensityProfile::Pointer ComputeIntensityProfile(Image::Pointer image, itk::PolyLineParametricPath<3>::Pointer path, unsigned int numSamples, InterpolateImageFunction::Enum interpolator) { IntensityProfile::Pointer intensityProfile = IntensityProfile::New(); AccessFixedDimensionByItk_n(image, ComputeIntensityProfile, 3, (path, numSamples, interpolator, intensityProfile)); return intensityProfile; } class AddPolyLineElementToPath { public: AddPolyLineElementToPath(const PlaneGeometry* planarFigureGeometry, const BaseGeometry* imageGeometry, itk::PolyLineParametricPath<3>::Pointer path) : m_PlanarFigureGeometry(planarFigureGeometry), m_ImageGeometry(imageGeometry), m_Path(path) { } void operator()(const PlanarFigure::PolyLineElement& polyLineElement) { m_PlanarFigureGeometry->Map(polyLineElement, m_WorldPoint); m_ImageGeometry->WorldToIndex(m_WorldPoint, m_ContinuousIndexPoint); m_Vertex.CastFrom(m_ContinuousIndexPoint); m_Path->AddVertex(m_Vertex); } private: const PlaneGeometry* m_PlanarFigureGeometry; const BaseGeometry* m_ImageGeometry; itk::PolyLineParametricPath<3>::Pointer m_Path; Point3D m_WorldPoint; Point3D m_ContinuousIndexPoint; itk::PolyLineParametricPath<3>::ContinuousIndexType m_Vertex; }; static itk::PolyLineParametricPath<3>::Pointer CreatePathFromPlanarFigure(BaseGeometry* imageGeometry, PlanarFigure* planarFigure) { itk::PolyLineParametricPath<3>::Pointer path = itk::PolyLineParametricPath<3>::New(); const PlanarFigure::PolyLineType polyLine = planarFigure->GetPolyLine(0); std::for_each(polyLine.begin(), polyLine.end(), AddPolyLineElementToPath(planarFigure->GetPlaneGeometry(), imageGeometry, path)); return path; } static void AddPointToPath(const BaseGeometry* imageGeometry, const Point3D& point, itk::PolyLineParametricPath<3>::Pointer path) { Point3D continuousIndexPoint; imageGeometry->WorldToIndex(point, continuousIndexPoint); itk::PolyLineParametricPath<3>::ContinuousIndexType vertex; vertex.CastFrom(continuousIndexPoint); path->AddVertex(vertex); } static itk::PolyLineParametricPath<3>::Pointer CreatePathFromPoints(BaseGeometry* imageGeometry, const Point3D& startPoint, const Point3D& endPoint) { itk::PolyLineParametricPath<3>::Pointer path = itk::PolyLineParametricPath<3>::New(); AddPointToPath(imageGeometry, startPoint, path); AddPointToPath(imageGeometry, endPoint, path); return path; } IntensityProfile::Pointer mitk::ComputeIntensityProfile(Image::Pointer image, PlanarFigure::Pointer planarFigure) { return ::ComputeIntensityProfile(image, CreatePathFromPlanarFigure(image->GetGeometry(), planarFigure)); } IntensityProfile::Pointer mitk::ComputeIntensityProfile(Image::Pointer image, PlanarLine::Pointer planarLine, unsigned int numSamples, InterpolateImageFunction::Enum interpolator) { return ::ComputeIntensityProfile(image, CreatePathFromPlanarFigure(image->GetGeometry(), planarLine.GetPointer()), numSamples, interpolator); } IntensityProfile::Pointer mitk::ComputeIntensityProfile(Image::Pointer image, const Point3D& startPoint, const Point3D& endPoint, unsigned int numSamples, InterpolateImageFunction::Enum interpolator) { return ::ComputeIntensityProfile(image, CreatePathFromPoints(image->GetGeometry(), startPoint, endPoint), numSamples, interpolator); } IntensityProfile::InstanceIdentifier mitk::ComputeGlobalMaximum(IntensityProfile::Pointer intensityProfile) { IntensityProfile::MeasurementType max = -vcl_numeric_limits::max(); IntensityProfile::InstanceIdentifier maxIndex = 0; IntensityProfile::ConstIterator end = intensityProfile->End(); IntensityProfile::MeasurementType measurement; for (IntensityProfile::ConstIterator it = intensityProfile->Begin(); it != end; ++it) { measurement = it.GetMeasurementVector()[0]; if (measurement > max) { max = measurement; maxIndex = it.GetInstanceIdentifier(); } } return maxIndex; } IntensityProfile::InstanceIdentifier mitk::ComputeGlobalMinimum(IntensityProfile::Pointer intensityProfile) { IntensityProfile::MeasurementType min = vcl_numeric_limits::max(); IntensityProfile::InstanceIdentifier minIndex = 0; IntensityProfile::ConstIterator end = intensityProfile->End(); IntensityProfile::MeasurementType measurement; for (IntensityProfile::ConstIterator it = intensityProfile->Begin(); it != end; ++it) { measurement = it.GetMeasurementVector()[0]; if (measurement < min) { min = measurement; minIndex = it.GetInstanceIdentifier(); } } return minIndex; } IntensityProfile::InstanceIdentifier mitk::ComputeCenterOfMaximumArea(IntensityProfile::Pointer intensityProfile, IntensityProfile::InstanceIdentifier radius) { const IntensityProfile::MeasurementType min = intensityProfile->GetMeasurementVector(ComputeGlobalMinimum(intensityProfile))[0]; const IntensityProfile::InstanceIdentifier areaWidth = 1 + 2 * radius; IntensityProfile::MeasurementType maxArea = 0; for (IntensityProfile::InstanceIdentifier i = 0; i < areaWidth; ++i) maxArea += intensityProfile->GetMeasurementVector(i)[0] - min; const IntensityProfile::InstanceIdentifier lastIndex = intensityProfile->Size() - areaWidth; IntensityProfile::InstanceIdentifier centerOfMaxArea = radius; IntensityProfile::MeasurementType area = maxArea; for (IntensityProfile::InstanceIdentifier i = 1; i <= lastIndex; ++i) { area += intensityProfile->GetMeasurementVector(i + areaWidth - 1)[0] - min; area -= intensityProfile->GetMeasurementVector(i - 1)[0] - min; if (area > maxArea) { maxArea = area; centerOfMaxArea = i + radius; // TODO: If multiple areas in the neighborhood have the same intensity chose the middle one instead of the first one. } } return centerOfMaxArea; } std::vector mitk::CreateVectorFromIntensityProfile(IntensityProfile::Pointer intensityProfile) { std::vector result; result.reserve(intensityProfile->Size()); IntensityProfile::ConstIterator end = intensityProfile->End(); for (IntensityProfile::ConstIterator it = intensityProfile->Begin(); it != end; ++it) result.push_back(it.GetMeasurementVector()[0]); return result; } IntensityProfile::Pointer mitk::CreateIntensityProfileFromVector(const std::vector& vector) { const IntensityProfile::InstanceIdentifier size = vector.size(); IntensityProfile::Pointer result = IntensityProfile::New(); result->Resize(size); for (IntensityProfile::InstanceIdentifier i = 0; i < size; ++i) result->SetMeasurement(i, 0, vector[i]); return result; } diff --git a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorAddOn.cpp b/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorAddOn.cpp index 13a3ab6f00..ee5de8f6a3 100644 --- a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorAddOn.cpp +++ b/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorAddOn.cpp @@ -1,61 +1,61 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include -#include +#include #include #include #include mitk::SpaceNavigatorAddOn* mitk::SpaceNavigatorAddOn::GetInstance() { // only needed for initializiation SpaceNavigatorDriver* spaceNavigatorDriver = SpaceNavigatorDriver::GetInstance(); static SpaceNavigatorAddOn instance; return &instance; } void mitk::SpaceNavigatorAddOn::DeviceChange (long device, long keys, long programmableKeys) { } void mitk::SpaceNavigatorAddOn::KeyDown (int keyCode) { mitk::Event* e = new mitk::Event(NULL, mitk::Type_SpaceNavigatorKeyDown, mitk::BS_LeftButton, keyCode, mitk::Key_none); mitk::StateEvent* se = new mitk::StateEvent(mitk::EIDSPACENAVIGATORKEYDOWN, e); this->ForwardEvent(se); } void mitk::SpaceNavigatorAddOn::KeyUp (int keyCode) { } void mitk::SpaceNavigatorAddOn::SensorInput( mitk::Vector3D translation, mitk::Vector3D rotation, mitk::ScalarType angle) { mitk::SpaceNavigatorEvent* e = new mitk::SpaceNavigatorEvent(mitk::BS_NoButton, translation, rotation, angle); mitk::StateEvent* se = new mitk::StateEvent(mitk::EIDSPACENAVIGATORINPUT, e); this->ForwardEvent(se); } diff --git a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorAddOn.h b/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorAddOn.h index 3cff894f56..0603a6bca8 100644 --- a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorAddOn.h +++ b/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorAddOn.h @@ -1,78 +1,78 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKSPACENAVIGATORADDON_H_HEADER_INCLUDED #define MITKSPACENAVIGATORADDON_H_HEADER_INCLUDED -#include +#include #include #include #include namespace mitk { /** * Documentation in the interface. * * @brief EventMapper addon for a 3DConnexion Space Navigator * @ingroup Interaction */ class mitkSpaceNavigator_EXPORT SpaceNavigatorAddOn : public EventMapperAddOn { public: // Singleton static SpaceNavigatorAddOn* GetInstance(); /** * @noimplement not needed */ void DeviceChange (long device, long keys, long programmableKeys); /** * If a button is pressed down an event is fired. * * @param keyCode the id to the key, that is pressed */ void KeyDown (int keyCode); /** * @noimplement not needed */ void KeyUp (int keyCode); /** * Reacts on any movement of the mouse and fires events accordingly. * * @param translation * the translation of the mouse as an 3D vector * @param rotation * the rotation of the mouse as an 3D vector * @param angle * the angle from the mouse as an scalar unit */ void SensorInput(mitk::Vector3D translation, mitk::Vector3D rotation, mitk::ScalarType angle); protected: private: }; // end class SpaceNavigatorAddOn } // end namespace mitk #endif // MITKSPACENAVIGATORADDON_H_HEADER_INCLUDED diff --git a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorDriver.h b/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorDriver.h index fedf2a62eb..a5c832b944 100644 --- a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorDriver.h +++ b/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorDriver.h @@ -1,96 +1,96 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #ifndef SpaceNavigatorDriver__h__ #define SpaceNavigatorDriver__h__ #ifdef SPACE_NAVIGATOR_MAIN_SOURCE #define _ATL_ATTRIBUTES 1 #define _WIN32_DCOM #include #include #include #include #include #include // if the error C2813 occurs // just remove /MP suffix: the problem is that // the #import statement is not yet compatible [05-11-2010] // check the microsoft homepage for further information #import "progid:TDxInput.Device" embedded_idl no_namespace using namespace ATL; -#include +#include #include using std::cout; using std::endl; [module(name="mitkInputDevices")]; [ event_receiver(com) ] class SpaceNavigatorDriver { public: static SpaceNavigatorDriver* GetInstance(); private: SpaceNavigatorDriver(); ~SpaceNavigatorDriver(); //methods for workaround of .net 2003 compiler Artikel-ID : 829749 //http://support.microsoft.com/kb/829749/de inline HRESULT HookEvent1(CComPtr device); inline HRESULT HookEvent2(); inline HRESULT HookEvent3(CComPtr device); inline HRESULT HookEvent4(); inline HRESULT HookEvent5(); #if 1 CComPtr m_pISensor; CComPtr m_pIKeyboard; #else ISensorPtr m_pISensor; IKeyboardPtr m_pIKeyboard; #endif public: // COM Event handlers HRESULT InitializeCOM(); HRESULT OnDeviceChange (long reserved); HRESULT OnKeyDown (int keyCode); HRESULT OnKeyUp (int keyCode); HRESULT OnSensorInput(void); HRESULT UninitializeCOM(); }; #else // SPACE_NAVIGATOR_MAIN_SOURCE class SpaceNavigatorDriver { public: static SpaceNavigatorDriver* GetInstance(); }; #endif #endif // SpaceNavigatorDriver__h__ diff --git a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorEvent.cpp b/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorEvent.cpp index 458c2ed902..a63748aa6e 100644 --- a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorEvent.cpp +++ b/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorEvent.cpp @@ -1,25 +1,25 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSpaceNavigatorEvent.h" #include "mitkInteractionConst.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" mitk::SpaceNavigatorEvent::SpaceNavigatorEvent(int buttonState, const Vector3D& translation, const Vector3D& rotation, const ScalarType& angle) : Event(NULL, mitk::Type_SpaceNavigatorInput, BS_NoButton, buttonState, Key_none), m_Translation(translation), m_Rotation(rotation), m_Angle(angle) { } diff --git a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorEvent.h b/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorEvent.h index f081bcc701..d32f68e025 100644 --- a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorEvent.h +++ b/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorEvent.h @@ -1,94 +1,94 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef SPACENAVIGATOREVENT_H_ #define SPACENAVIGATOREVENT_H_ #include #include -#include +#include #include namespace mitk { /** * Documentation * @brief Event on 3D Mouse input * * Seven coordinates exposed by the 3D Mouse: * 3-dimensional translation vector * 3-dimensional rotation achsis (length allways 1.0) * scalar rotation angle * * @ingroup Interaction */ class mitkSpaceNavigator_EXPORT SpaceNavigatorEvent : public Event { public: /** * @brief Constructor with all necessary arguments (for further information about the parameter:
* mitk::SpaceNavigatorAddOn. * * @param buttonState information from the Event * */ SpaceNavigatorEvent(int buttonState, const Vector3D& translation, const Vector3D& rotation, const ScalarType& angle); //Getter and Setter const Vector3D& GetTranslation() const { return m_Translation; } void SetTranslation(const Vector3D& translation) { m_Translation = translation; } const Vector3D& GetRotation() const { return m_Rotation; } void SetRotation(const Vector3D& rotation) { m_Rotation = rotation; } const ScalarType& GetAngle() const { return m_Angle; } void SetAngle(const ScalarType& angle) { m_Angle = angle; } protected: Vector3D m_Translation; Vector3D m_Rotation; ScalarType m_Angle; }; // end class SpaceNavigatorEvent } // end namespace mitk #endif /* SPACENAVIGATOREVENT_H_ */ diff --git a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorVtkCameraController.cpp b/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorVtkCameraController.cpp index 9cbf690990..d44eb112a0 100644 --- a/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorVtkCameraController.cpp +++ b/Modules/InputDevices/SpaceNavigator/mitkSpaceNavigatorVtkCameraController.cpp @@ -1,238 +1,238 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSpaceNavigatorVtkCameraController.h" #include "mitkVtkPropRenderer.h" #include "vtkCamera.h" #include "vtkRenderer.h" #include "vtkTransform.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkInteractionConst.h" #include "mitkStateEvent.h" #include "mitkGlobalInteraction.h" #include "vtkMath.h" #include const double TRANSLATIONSENSITIVITY = 2.0; const double ROTATIONSENSIVITY = 3.5; const double ANGLESENSITIVITY = 3.5; mitk::SpaceNavigatorVtkCameraController::SpaceNavigatorVtkCameraController() : CameraController("SpaceNavigatorInteraction") { //connect method this->OnSpaceNavigatorEvent to StateMachineEventMechanism CONNECT_ACTION(AcONSPACENAVIGATORMOUSEINPUT, OnSpaceNavigatorEvent); CONNECT_ACTION(AcONPACENAVIGATORKEYDOWN, OnSpaceNavigatorKeyDown); m_ClippingRangeIsSet = false; } mitk::SpaceNavigatorVtkCameraController::~SpaceNavigatorVtkCameraController() { } bool mitk::SpaceNavigatorVtkCameraController::OnSpaceNavigatorEvent(mitk::Action* a, const mitk::StateEvent* e) { //only if 3D rendering const mitk::BaseRenderer* br = mitk::GlobalInteraction::GetInstance()->GetFocus(); this->SetRenderer( br ); mitk::BaseRenderer::MapperSlotId id = ((mitk::BaseRenderer*)(br))->GetMapperID(); if (id != mitk::BaseRenderer::Standard3D) return true; //only if focused by the FocusManager if (this->GetRenderer() != mitk::GlobalInteraction::GetInstance()->GetFocus()) return true; //pre-checking for safety vtkRenderer* vtkRenderer = ((mitk::VtkPropRenderer*)this->GetRenderer())->GetVtkRenderer(); if (vtkRenderer == NULL) return false; vtkCamera* vtkCam = (vtkCamera*)vtkRenderer->GetActiveCamera(); if(!m_ClippingRangeIsSet) vtkCam->SetClippingRange(0.1, 1000000); const mitk::SpaceNavigatorEvent* snevent = dynamic_cast(e->GetEvent()); if (snevent == NULL) { MITK_ERROR <<"Wrong event for SpaceNavigatorVtkCameraController!"; return false; } //get the information from the mouse mitk::Vector3D translation = snevent->GetTranslation(); mitk::Vector3D rotation = snevent->GetRotation(); mitk::ScalarType angle = snevent->GetAngle(); //output for debug //std::cout<<"translation: "<GetDataStorage(); mitk::BoundingBox::Pointer bb = ds->ComputeBoundingBox(); mitk::BoundingBox::AccumulateType length = bb->GetDiagonalLength2(); if (length > 0.00001)//if length not zero sceneSensivity *= 100.0 / (sqrt(length)) ; //sensivity to adapt to mitk speed translation *= sceneSensivity * TRANSLATIONSENSITIVITY; rotation *= sceneSensivity * ROTATIONSENSIVITY; angle *= sceneSensivity * ANGLESENSITIVITY; //compute the global space coordinates from the relative mouse coordinate //first we need the position of the camera mitk::Vector3D camPosition; double camPositionTemp[3]; vtkCam->GetPosition(camPositionTemp); camPosition[0] = camPositionTemp[0]; camPosition[1] = camPositionTemp[1]; camPosition[2] = camPositionTemp[2]; //then the upvector of the camera mitk::Vector3D upCamVector; double upCamTemp[3]; vtkCam->GetViewUp(upCamTemp); upCamVector[0] = upCamTemp[0]; upCamVector[1] = upCamTemp[1]; upCamVector[2] = upCamTemp[2]; upCamVector.Normalize(); //then the vector to which the camera is heading at (focalpoint) mitk::Vector3D focalPoint; double focalPointTemp[3]; vtkCam->GetFocalPoint(focalPointTemp); focalPoint[0] = focalPointTemp[0]; focalPoint[1] = focalPointTemp[1]; focalPoint[2] = focalPointTemp[2]; mitk::Vector3D focalVector; focalVector = focalPoint - camPosition; focalVector.Normalize(); //orthogonal vector to focalVector and upCamVector mitk::Vector3D crossVector; crossVector = CrossProduct(upCamVector, focalVector); crossVector.Normalize(); //now we have the current orientation so we can adapt it according to the current information, which we got from the TDMouse //new position of the camera: //left/right = camPosition + crossVector * translation[0]; mitk::Vector3D vectorX = crossVector * -translation[0]; //changes the magnitude, not the direction double nextCamPosition[3]; nextCamPosition[0] = camPosition[0] + vectorX[0]; nextCamPosition[1] = camPosition[1] + vectorX[1]; nextCamPosition[2] = camPosition[2] + vectorX[2]; //now the up/down movement mitk::Vector3D vectorY = upCamVector * translation[1]; //changes the magnitude, not the direction nextCamPosition[0] += vectorY[0]; nextCamPosition[1] += vectorY[1]; nextCamPosition[2] += vectorY[2]; //forward/backward movement mitk::Vector3D vectorZ = focalVector * -translation[2]; //changes the magnitude, not the direction nextCamPosition[0] += vectorZ[0]; nextCamPosition[1] += vectorZ[1]; nextCamPosition[2] += vectorZ[2]; //set the next position double nextPosition[3]; nextPosition[0] = nextCamPosition[0]; nextPosition[1] = nextCamPosition[1]; nextPosition[2] = nextCamPosition[2]; vtkCam->SetPosition(nextPosition); //adapt the focal point the same way double currentFocalPoint[3], nextFocalPoint[3]; vtkCam->GetFocalPoint(currentFocalPoint); nextFocalPoint[0] = currentFocalPoint[0] + vectorX[0] + vectorY[0] + vectorZ[0]; nextFocalPoint[1] = currentFocalPoint[1] + vectorX[1] + vectorY[1] + vectorZ[1]; ; nextFocalPoint[2] = currentFocalPoint[2] + vectorX[2] + vectorY[2] + vectorZ[2]; vtkCam->SetFocalPoint(nextFocalPoint); //now adapt the rotation of the mouse and adapt the camera according to it //Pitch: //Rotate the focal point about the cross product of the view up vector and the direction of //projection, centered at the camera's position. vtkCam->Pitch(rotation[0]*angle); //Yaw: //Rotate the focal point about the view up vector centered at the camera's position. //Note that the view up vector is not necessarily perpendicular to the direction of projection. vtkCam->Yaw(rotation[1]*angle); //Roll: //Rotate the camera about the direction of projection. vtkCam->Roll(-rotation[2]*angle * 1.5);//*1.5 to speed up the rotation[2] a little bit //Recompute the ViewUp vector to force it to be perpendicular to camera->focalpoint vector. //Unless you are going to use Yaw or Azimuth on the camera, there is no need to do this. vtkCam->OrthogonalizeViewUp(); //no zooming, only translating to the front or back // dolly: Move the position of the camera along the direction // of projection. Moving towards the focal point (e.g., greater // than 1) is a dolly-in, moving away from the focal point // (e.g., less than 1) is a dolly-out. //double distance = ((tdevent->GetTranslation())[1] / 10.0);//make it less sensitive in comparison to translation and rotatipn //vtkCam->Dolly(1.0 + distance ); //Reset the camera clipping range based on the bounds of the visible actors. //This ensures that no props are cut off vtkRenderer->ResetCameraClippingRange(); mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(mitk::GlobalInteraction::GetInstance()->GetFocus()->GetRenderWindow()); return true; } bool mitk::SpaceNavigatorVtkCameraController::OnSpaceNavigatorKeyDown(mitk::Action* a, const mitk::StateEvent* e) { //reset the camera, so that the objects shown in the scene can be seen. const mitk::VtkPropRenderer* glRenderer = dynamic_cast(m_Renderer); if (glRenderer) { vtkRenderer* vtkRenderer = glRenderer->GetVtkRenderer(); mitk::DataStorage* ds = m_Renderer->GetDataStorage(); if (ds == NULL) return false; mitk::BoundingBox::Pointer bb = ds->ComputeBoundingBox(); mitk::Point3D middle =bb->GetCenter(); vtkRenderer->GetActiveCamera()->SetFocalPoint(middle[0],middle[1],middle[2]); vtkRenderer->ResetCamera(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } return false; } diff --git a/Modules/InputDevices/WiiMote/mitkWiiMoteAddOn.h b/Modules/InputDevices/WiiMote/mitkWiiMoteAddOn.h index fc4b1e048d..f2b3f4dff0 100644 --- a/Modules/InputDevices/WiiMote/mitkWiiMoteAddOn.h +++ b/Modules/InputDevices/WiiMote/mitkWiiMoteAddOn.h @@ -1,104 +1,104 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITK_WIIMOTEADDON_H_ #define MITK_WIIMOTEADDON_H_ // export macro #include // mitk -#include "mitkVector.h" // used for Point2D +#include "mitkNumericTypes.h" // used for Point2D #include namespace mitk { class WiiMoteThread; /** * Documentation in the interface. Some code snippets used here are from the demo
* application from gl.tter and therefore are under the same license as wiimote.cpp . * * @brief EventMapper addon for a Wiimote * @ingroup Interaction */ class mitkWiiMote_EXPORT WiiMoteAddOn : public EventMapperAddOn { public: WiiMoteAddOn(); ~WiiMoteAddOn(); // Singleton static WiiMoteAddOn* GetInstance(); /** * Starts all available Wiimotes. */ void ActivateWiiMotes(); /** * Disconnects all Wiimotes and stops the thread. */ void DeactivateWiiMotes(); /** * Creates suitable events, when the Wiimote sends IR data
* for movement and forwards it for further processing. */ void WiiMoteInput(const itk::EventObject& e); /** * Creates suitable events, when the Wiimote sends button events
* to trigger an action and forwards it for further processing. */ void WiiMoteButtonPressed(const itk::EventObject& e); /** * Creates suitable events, when the Wiimote sends button events
* to trigger an action and forwards it for further processing. */ void WiiMoteButtonReleased(const itk::EventObject& e); /** * Creates suitable events, when the Wiimote sends IR data
* to configure the sensitivity and forwards it for further processing. */ void WiiMoteCalibrationInput(const itk::EventObject& e); /** * @see mitk::WiiMoteThread::SetWiiMoteSurfaceIModus(bool activated) */ void SetWiiMoteSurfaceIModus(bool activated); /** * Creates suitable events, regardless what type of data the
* Wiimote sends for a surface interaction. Then forwards the
* transformed data for further processing. * */ void WiiMoteSurfaceInteractionInput(const itk::EventObject& e); protected: private: WiiMoteThread* m_WiiMoteThread; }; // end class WiiMoteAddOn } // end namespace mitk #endif // MITK_WIIMOTEADDON_H_ diff --git a/Modules/InputDevices/WiiMote/mitkWiiMoteButtonEvent.h b/Modules/InputDevices/WiiMote/mitkWiiMoteButtonEvent.h index a0a02d7356..6cb2c16fd5 100644 --- a/Modules/InputDevices/WiiMote/mitkWiiMoteButtonEvent.h +++ b/Modules/InputDevices/WiiMote/mitkWiiMoteButtonEvent.h @@ -1,58 +1,58 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITK_WIIMOTEBUTTONEVENT_H #define MITK_WIIMOTEBUTTONEVENT_H #include "mitkEvent.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkInteractionConst.h" #include namespace mitk { /** * This event type is used for button events triggered by the
* the Wiimote driver. */ class mitkWiiMote_EXPORT WiiMoteButtonEvent : public Event, itk::EventObject { public: typedef WiiMoteButtonEvent Self; typedef itk::EventObject Superclass; /** * Initializes a Wiimote Event, that stores additional information.
* * @see mitk::Event::Event(mitk::BaseRenderer* sender, int type, int button, int buttonState, int key); */ WiiMoteButtonEvent(int type, int button, int buttonState, int key); ~WiiMoteButtonEvent(); //itk::EventObject implementation const char * GetEventName() const; bool CheckEvent(const ::itk::EventObject* e) const; ::itk::EventObject* MakeObject() const; protected: private: }; // end class } // end namespace mitk #endif // MITK_WIIMOTEBUTTONEVENT_H diff --git a/Modules/InputDevices/WiiMote/mitkWiiMoteCalibrationEvent.h b/Modules/InputDevices/WiiMote/mitkWiiMoteCalibrationEvent.h index 9d8adb1ba7..4cf2b824cd 100644 --- a/Modules/InputDevices/WiiMote/mitkWiiMoteCalibrationEvent.h +++ b/Modules/InputDevices/WiiMote/mitkWiiMoteCalibrationEvent.h @@ -1,66 +1,66 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITK_WIIMOTECALIBRATIONEVENT_H #define MITK_WIIMOTECALIBRATIONEVENT_H #include #include "mitkEvent.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkInteractionConst.h" namespace mitk { class mitkWiiMote_EXPORT WiiMoteCalibrationEvent : public Event, itk::EventObject { public: typedef WiiMoteCalibrationEvent Self; typedef itk::EventObject Superclass; /** * Initializes a Wiimote Event, that stores additional information.
* Such as a the raw x and y coordinates of the IR input.
* * @param rawX * x coordinate of the IR sensor input * @param rawY * y coordinate of the IR sensor input */ WiiMoteCalibrationEvent(double rawX, double rawY); ~WiiMoteCalibrationEvent(); double GetXCoordinate() const; double GetYCoordinate() const; //itk::EventObject implementation const char * GetEventName() const; bool CheckEvent(const ::itk::EventObject* e) const; ::itk::EventObject* MakeObject() const; protected: private: double m_RawX; double m_RawY; }; } #endif // MITK_WIIMOTECALIBRATIONEVENT_H diff --git a/Modules/InputDevices/WiiMote/mitkWiiMoteIREvent.h b/Modules/InputDevices/WiiMote/mitkWiiMoteIREvent.h index b082166799..b375a8a6db 100644 --- a/Modules/InputDevices/WiiMote/mitkWiiMoteIREvent.h +++ b/Modules/InputDevices/WiiMote/mitkWiiMoteIREvent.h @@ -1,75 +1,75 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITK_WIIMOTEIREVENT_H #define MITK_WIIMOTEIREVENT_H #include "mitkEvent.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkInteractionConst.h" #include namespace mitk { /** * This event type is used for IR events triggered by the
* the Wiimote driver. */ class mitkWiiMote_EXPORT WiiMoteIREvent : public Event, itk::EventObject { public: typedef WiiMoteIREvent Self; typedef itk::EventObject Superclass; /** * Initializes a Wiimote Event, that stores additional information.
* Such as a vector and the time of the recording
* * @param inputData * the movement of the IR sensor computed in a vector * @param recordTime * the time at which the data was recorded */ WiiMoteIREvent( mitk::Vector2D inputData , double recordTime , int sliceNavigationValue); ~WiiMoteIREvent(); /** * Returns the current movement vector with the coordinates
* in the following order: x, y, z */ mitk::Vector2D GetMovementVector() const; double GetRecordTime() const; int GetSliceNavigationValue() const; //itk::EventObject implementation const char * GetEventName() const; bool CheckEvent(const ::itk::EventObject* e) const; ::itk::EventObject* MakeObject() const; private: mitk::Vector2D m_MovementVector; double m_RecordTime; int m_SliceNavigationValue; }; // end class } // end namespace mitk #endif // MITK_WIIMOTEIREVENT_H diff --git a/Modules/InputDevices/WiiMote/mitkWiiMoteInteractor.cpp b/Modules/InputDevices/WiiMote/mitkWiiMoteInteractor.cpp index 3bf009669c..c51fe833ec 100644 --- a/Modules/InputDevices/WiiMote/mitkWiiMoteInteractor.cpp +++ b/Modules/InputDevices/WiiMote/mitkWiiMoteInteractor.cpp @@ -1,776 +1,776 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include // mitk #include -#include +#include #include #include -#include +#include #include #include #include #include // vtk #include #include #include #include #include // vnl #include #include #define _USE_MATH_DEFINES // otherwise, constants will not work #include const double DELTATIME = 0.01; mitk::WiiMoteInteractor::WiiMoteInteractor(const char* type, DataNode* dataNode) : Interactor(type, dataNode) , m_OrientationX(0) , m_OrientationY(0) , m_OrientationZ(0) , m_xVelocity (0) , m_yVelocity (0) , m_zVelocity (0) , m_xAngle (0) , m_yAngle (0) , m_zAngle (0) , m_xValue (0) , m_yValue (0) , m_zValue (0) , m_InRotation(false) , m_TranslationMode(1) , m_OriginalGeometry(NULL) , m_SurfaceInteractionMode(1) { // save original geometry mitk::Geometry3D* temp = this->TransformCurrentDataInGeometry3D(); try { m_OriginalGeometry = dynamic_cast(temp->Clone().GetPointer()); } catch(...) { MITK_WARN << "Original geometry could not be stored!"; } // connect actions to methods CONNECT_ACTION(mitk::AcONWIIMOTEINPUT,OnWiiMoteInput); CONNECT_ACTION(mitk::AcONWIIMOTEBUTTONRELEASED,OnWiiMoteReleaseButton); CONNECT_ACTION(mitk::AcRESETVIEW,OnWiiMoteResetButton); } mitk::WiiMoteInteractor::~WiiMoteInteractor() { } bool mitk::WiiMoteInteractor::OnWiiMoteResetButton(Action* action, const mitk::StateEvent* stateEvent) { // resets the geometry, so that the // object will be returned to its // initial state try { mitk::Surface* surface = dynamic_cast(m_DataNode->GetData()); mitk::Geometry3D::Pointer temp = dynamic_cast(m_OriginalGeometry->Clone().GetPointer()); surface->SetGeometry(temp); if(surface == NULL) { MITK_WARN << "Original geometry could not be used for reset!"; } m_DataNode->SetData(surface); m_DataNode->Modified(); } catch(...) { MITK_ERROR << "Original geometry could not be retrieved"; } //reset the camera, so that the objects shown in the scene can be seen. const mitk::BaseRenderer* br = mitk::GlobalInteraction::GetInstance()->GetFocus(); const mitk::VtkPropRenderer* glRenderer = dynamic_cast(br); if (glRenderer) { vtkRenderer* vtkRenderer = glRenderer->GetVtkRenderer(); mitk::DataStorage* ds = br->GetDataStorage(); if (ds == NULL) return false; mitk::BoundingBox::Pointer bb = ds->ComputeBoundingBox(); mitk::Point3D middle = bb->GetCenter(); vtkRenderer->GetActiveCamera()->SetFocalPoint(middle[0],middle[1],middle[2]); vtkRenderer->ResetCamera(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } return false; } bool mitk::WiiMoteInteractor::OnWiiMoteInput(Action* action, const mitk::StateEvent* stateEvent) { const mitk::WiiMoteAllDataEvent* wiiMoteEvent; try { wiiMoteEvent = dynamic_cast(stateEvent->GetEvent()); } catch(...) { MITK_ERROR << "Event is not wiimote event and could not be transformed\n"; } m_SurfaceInteractionMode = wiiMoteEvent->GetSurfaceInteractionMode(); //this->FixedRotationAndTranslation(wiiMoteEvent); // -------------------- values for translation -------------------- float xAccel = wiiMoteEvent->GetXAcceleration(); float yAccel = wiiMoteEvent->GetYAcceleration(); float zAccel = wiiMoteEvent->GetZAcceleration(); float pitch = wiiMoteEvent->GetPitch(); float roll = wiiMoteEvent->GetRoll(); m_OrientationX = wiiMoteEvent->GetOrientationX(); m_OrientationY = wiiMoteEvent->GetOrientationY(); m_OrientationZ = wiiMoteEvent->GetOrientationZ(); // substracts the proportionate force // applied by gravity depending on the // orientation float sinP = sin(pitch/180.0 * M_PI); float cosP = cos(pitch/180.0 * M_PI); float sinR = sin(roll/180.0 * M_PI); float cosR = cos(roll/180.0 * M_PI); // x acceleration if(m_OrientationZ >= 0) { m_xValue = xAccel - sinR * cosP; } else { m_xValue = xAccel + sinR * cosP; } //// against drift //if(std::abs(xAccel) < 0.2) //{ // m_xValue = 0; //} // y acceleration m_yValue = yAccel + sinP; //// against drift //if(std::abs(yAccel) < 0.2) //{ // m_yValue = 0; //} // z acceleration m_zValue = zAccel - cosP * cosR; //// against drift //if(std::abs(zAccel) < 0.3) //{ // m_zValue = 0; //} m_xVelocity += m_xValue; m_yVelocity += m_yValue; m_zVelocity -= m_zValue; // -------------------- values for rotation -------------------- ScalarType pitchSpeed = wiiMoteEvent->GetPitchSpeed(); ScalarType rollSpeed = wiiMoteEvent->GetRollSpeed(); ScalarType yawSpeed = wiiMoteEvent->GetYawSpeed(); // x angle if(std::abs(pitchSpeed) > 50 && std::abs(pitchSpeed) < 1000) { if(m_SurfaceInteractionMode == 1) { m_xAngle = (pitchSpeed * DELTATIME); } else { m_xAngle = (-pitchSpeed * DELTATIME); } } else { m_xAngle = 0; } // y angle if(std::abs(rollSpeed) > 50 && std::abs(rollSpeed) < 1000) { m_yAngle = (rollSpeed * DELTATIME); } else { m_yAngle = 0; } // z angle if(std::abs(yawSpeed) > 50 && std::abs(yawSpeed) < 1000) { if(m_SurfaceInteractionMode == 1) { m_zAngle = (yawSpeed * DELTATIME); } else { m_zAngle = (-yawSpeed * DELTATIME); } } else { m_zAngle = 0; } // -------------------- rotation and translation -------------------- bool result = false; result = this->DynamicRotationAndTranslation(this->TransformCurrentDataInGeometry3D()); return result; } bool mitk::WiiMoteInteractor::OnWiiMoteReleaseButton(Action* action, const mitk::StateEvent* stateEvent) { m_xVelocity = 0; m_yVelocity = 0; m_zVelocity = 0; m_xValue = 0; m_yValue = 0; m_zValue = 0; m_xAngle = 0; m_yAngle = 0; m_zAngle = 0; // only for fixed translation m_InRotation = false; m_TranslationMode = 1; return true; } mitk::Geometry3D* mitk::WiiMoteInteractor::TransformCurrentDataInGeometry3D() { //checking corresponding Data; has to be a surface or a subclass mitk::Surface* surface = dynamic_cast(m_DataNode->GetData()); if ( surface == NULL ) { MITK_WARN<<"Wiimote Interactor got wrong type of data! Aborting interaction!\n"; return NULL; } Geometry3D* geometry = surface->GetUpdatedTimeGeometry()->GetGeometryForTimeStep( m_TimeStep ); return geometry; } vnl_matrix_fixed mitk::WiiMoteInteractor::ComputeCurrentCameraPosition( vtkCamera* vtkCamera ) { vnl_matrix_fixed cameraMat; //first we need the position of the camera mitk::Vector3D camPosition; double camPositionTemp[3]; vtkCamera->GetPosition(camPositionTemp); camPosition[0] = camPositionTemp[0]; camPosition[1] = camPositionTemp[1]; camPosition[2] = camPositionTemp[2]; //then the upvector of the camera mitk::Vector3D upCamVector; double upCamTemp[3]; vtkCamera->GetViewUp(upCamTemp); upCamVector[0] = upCamTemp[0]; upCamVector[1] = upCamTemp[1]; upCamVector[2] = upCamTemp[2]; upCamVector.Normalize(); //then the vector to which the camera is heading at (focalpoint) mitk::Vector3D focalPoint; double focalPointTemp[3]; vtkCamera->GetFocalPoint(focalPointTemp); focalPoint[0] = focalPointTemp[0]; focalPoint[1] = focalPointTemp[1]; focalPoint[2] = focalPointTemp[2]; mitk::Vector3D focalVector; focalVector = focalPoint - camPosition; focalVector.Normalize(); //orthogonal vector to focalVector and upCamVector mitk::Vector3D crossVector; crossVector = CrossProduct(upCamVector, focalVector); crossVector.Normalize(); cameraMat.put(0,0,crossVector[0]); cameraMat.put(1,0,crossVector[1]); cameraMat.put(2,0,crossVector[2]); cameraMat.put(3,0,0); cameraMat.put(0,1,focalVector[0]); cameraMat.put(1,1,focalVector[1]); cameraMat.put(2,1,focalVector[2]); cameraMat.put(3,1,0); cameraMat.put(0,2,upCamVector[0]); cameraMat.put(1,2,upCamVector[1]); cameraMat.put(2,2,upCamVector[2]); cameraMat.put(3,2,0); cameraMat.put(0,3,camPosition[0]); cameraMat.put(1,3,camPosition[1]); cameraMat.put(2,3,camPosition[2]); cameraMat.put(3,3,1); return cameraMat; } bool mitk::WiiMoteInteractor::DynamicRotationAndTranslation(Geometry3D* geometry) { // computation of the delta transformation if(m_SurfaceInteractionMode == 1) { // necessary because the wiimote has // a different orientation when loaded // as an object file ScalarType temp = m_yAngle; m_yAngle = m_zAngle; m_zAngle = temp; } //vnl_quaternion Rx(m_OrientationX // ,m_OrientationY // ,m_OrientationZ // , m_xAngle); //vnl_quaternion Ry(Rx.axis()[0] // , Rx.axis()[1] // , Rx.axis()[2] // , m_yAngle); //vnl_quaternion Rz(Ry.axis()[0] // , Ry.axis()[1] // , Ry.axis()[2] // , m_zAngle); vnl_quaternion q( vtkMath::RadiansFromDegrees( m_xAngle ), vtkMath::RadiansFromDegrees( m_yAngle ), vtkMath::RadiansFromDegrees( m_zAngle ) ); //q = Rz * Ry * Rx; //q.normalize(); vnl_matrix_fixed deltaTransformMat = q.rotation_matrix_transpose_4(); // fill translation column deltaTransformMat(0,3) = m_xVelocity; deltaTransformMat(1,3) = m_yVelocity; deltaTransformMat(2,3) = m_zVelocity; // invert matrix to apply // correct order for the transformation deltaTransformMat = vnl_inverse(deltaTransformMat); vtkMatrix4x4* deltaTransform = vtkMatrix4x4::New(); // copy into matrix for(size_t i=0; i<4; ++i) for(size_t j=0; j<4; ++j) deltaTransform->SetElement(i,j, deltaTransformMat(i,j)); vtkMatrix4x4* objectTransform = vtkMatrix4x4::New(); if(m_SurfaceInteractionMode == 2) { // additional computation for transformation // relative to the camera view // get renderer const RenderingManager::RenderWindowVector& renderWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for ( RenderingManager::RenderWindowVector::const_iterator iter = renderWindows.begin(); iter != renderWindows.end(); ++iter ) { if ( mitk::BaseRenderer::GetInstance((*iter))->GetMapperID() == BaseRenderer::Standard3D ) { m_BaseRenderer = mitk::BaseRenderer::GetInstance((*iter)); } } vtkCamera* camera = m_BaseRenderer->GetVtkRenderer()->GetActiveCamera(); //vtkMatrix4x4* cameraMat = vtkMatrix4x4::New(); vnl_matrix_fixed cameraMat; vnl_matrix_fixed objectMat; // copy object matrix for(size_t i=0; i<4; ++i) for(size_t j=0; j<4; ++j) objectMat.put(i,j, geometry->GetVtkTransform()->GetMatrix()->GetElement(i,j)); cameraMat = this->ComputeCurrentCameraPosition(camera); vnl_matrix_fixed newObjectMat; vnl_matrix_fixed objectToCameraMat; objectToCameraMat = vnl_inverse(cameraMat) * objectMat; newObjectMat = vnl_inverse(objectToCameraMat) * deltaTransformMat * objectToCameraMat * vnl_inverse(objectMat); newObjectMat = vnl_inverse(newObjectMat); newObjectMat.put(0,3,objectMat(0,3)+deltaTransformMat(0,3)); newObjectMat.put(1,3,objectMat(1,3)+deltaTransformMat(1,3)); newObjectMat.put(2,3,objectMat(2,3)+deltaTransformMat(2,3)); // copy result for(size_t i=0; i<4; ++i) for(size_t j=0; j<4; ++j) objectTransform->SetElement(i,j, newObjectMat(i,j)); } //copy m_vtkMatrix to m_VtkIndexToWorldTransform geometry->TransferItkToVtkTransform(); vtkTransform* vtkTransform = vtkTransform::New(); if(m_SurfaceInteractionMode == 1) { //m_VtkIndexToWorldTransform as vtkLinearTransform* vtkTransform->SetMatrix( geometry->GetVtkTransform()->GetMatrix() ); vtkTransform->Concatenate( deltaTransform ); geometry->SetIndexToWorldTransformByVtkMatrix( vtkTransform->GetMatrix() ); } else { geometry->SetIndexToWorldTransformByVtkMatrix( objectTransform ); } geometry->Modified(); m_DataNode->Modified(); vtkTransform->Delete(); objectTransform->Delete(); deltaTransform->Delete(); //update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } bool mitk::WiiMoteInteractor::FixedRotationAndTranslation(const mitk::WiiMoteAllDataEvent* wiiMoteEvent) { Geometry3D* geometry = this->TransformCurrentDataInGeometry3D(); m_OrientationX = wiiMoteEvent->GetOrientationX(); m_OrientationY = wiiMoteEvent->GetOrientationY(); m_OrientationZ = wiiMoteEvent->GetOrientationZ(); ScalarType pitchSpeed = wiiMoteEvent->GetPitchSpeed(); ScalarType rollSpeed = wiiMoteEvent->GetRollSpeed(); ScalarType yawSpeed = wiiMoteEvent->GetYawSpeed(); // angle x if(std::abs(pitchSpeed) < 200) pitchSpeed = 0; m_xAngle += (pitchSpeed / 1500); // angle y if(std::abs(rollSpeed) < 200) rollSpeed = 0; m_yAngle += (rollSpeed / 1500); // angle z if(std::abs(yawSpeed) < 200) yawSpeed = 0; m_zAngle += (yawSpeed / 1500); if( std::abs(pitchSpeed) > 200 || std::abs(rollSpeed) > 200 || std::abs(yawSpeed) > 200) { m_InRotation = true; //// depending on a combination of the //// orientation the angleX wil be altered //// because the range from roll is limited - //// range: -90° to 90° by the wiimote + //// range: -90� to 90� by the wiimote //if(wiiMoteEvent->GetOrientationZ() < 0) //{ // // value is positive // if(wiiMoteEvent->GetOrientationX() > 0) // { // // the degree measured decreases after it reaches // // in the "real" world the 90 degree angle // // (rotation to the right side) // // therefore it needs to artificially increased // // measured value drops -> computated angle increases // angleX = 90 - angleX; // // now add the "new" angle to 90 degree threshold // angleX += 90; // } // // value is negative // else if(wiiMoteEvent->GetOrientationX() < 0) // { // // the degree measured increases after it reaches // // in the "real" world -90 degree // // (rotation to the left side) // // therefore it needs to be artificially decreased // // (example -90 -> -70, but -110 is needed) // // measured value increases -> computated angle decreases // angleX = 90 + angleX; // // invert the algebraic sign, because it is the "negative" // // side of the rotation // angleX = -angleX; // // now add the negative value to the -90 degree threshold // // to decrease the value further // angleX -= 90; // } // else if(wiiMoteEvent->GetOrientationX() == 0) // { // // i.e. wiimote is flipped upside down // angleX = 180; // } //} //rotation vtkTransform *vtkTransform = vtkTransform::New(); //copy m_vtkMatrix to m_VtkIndexToWorldTransform geometry->TransferItkToVtkTransform(); //////m_VtkIndexToWorldTransform as vtkLinearTransform* vtkTransform->SetMatrix(geometry->GetVtkTransform()->GetMatrix()); // rotation from center is different // from rotation while translated // hence one needs the center of the object Point3D center = geometry->GetOrigin(); vtkTransform->PostMultiply(); vtkTransform->Translate(-center[0], -center[1], -center[2]); //vtkTransform->RotateWXYZ(angle, rotationVector[0], rotationVector[1], rotationVector[2]); vtkTransform->RotateX(m_xAngle); vtkTransform->RotateY(m_zAngle); vtkTransform->RotateZ(m_yAngle); vtkTransform->Translate(center[0], center[1], center[2]); vtkTransform->PreMultiply(); geometry->SetIndexToWorldTransformByVtkMatrix(vtkTransform->GetMatrix()); geometry->Modified(); // indicate modification of data tree node m_DataNode->Modified(); vtkTransform->Delete(); //update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } else if(!m_InRotation) { float xValue = wiiMoteEvent->GetXAcceleration(); float yValue = wiiMoteEvent->GetYAcceleration(); float zValue = wiiMoteEvent->GetZAcceleration(); float pitch = wiiMoteEvent->GetPitch(); float roll = wiiMoteEvent->GetRoll(); // substracts the proportionate force // applied by gravity depending on the // orientation float sinP = sin(pitch/180.0 * M_PI); float cosP = cos(pitch/180.0 * M_PI); float sinR = sin(roll/180.0 * M_PI); float cosR = cos(roll/180.0 * M_PI); // x acceleration if(m_OrientationZ >= 0) xValue = xValue - sinR * cosP; else xValue = xValue + sinR * cosP; // against drift if(std::abs(xValue) < 0.2) xValue = 0; // y acceleration yValue = yValue + sinP; // against drift if(std::abs(yValue) < 0.2) yValue = 0; // z acceleration zValue = zValue - cosP * cosR; // against drift if(std::abs(zValue) < 0.3) zValue = 0; // simple integration over time // resulting in velocity switch(m_TranslationMode) { case 1: m_xVelocity -= xValue; m_yVelocity -= yValue; m_zVelocity += zValue; // 1 = movement to the right // initially starts with negative acceleration // 2 = movement to the left // initially starts with positive acceleration if( m_xVelocity > 0 && xValue > 0 // 1 || m_xVelocity < 0 && xValue < 0) // 2 { m_xVelocity += xValue; } else if( m_xVelocity > 0 && xValue < 0 // 1 || m_xVelocity < 0 && xValue > 0) // 2 { m_xVelocity -= xValue; } break; case 3: m_yVelocity -= yValue; break; case 4: // 1 = movement up // initially starts with positive acceleration // 2 = movement down // initially starts with negative acceleration if( m_zVelocity > 0 && zValue < 0 // 1 || m_zVelocity < 0 && zValue > 0) // 2 { m_zVelocity -= zValue; } else if(m_zVelocity > 0 && zValue > 0 // 1 || m_zVelocity < 0 && zValue < 0) // 2 { m_zVelocity += zValue; } break; } // sets the mode of the translation // depending on the initial velocity if( std::abs(m_xVelocity) > std::abs(m_yVelocity) && std::abs(m_xVelocity) > std::abs(m_zVelocity) ) { m_TranslationMode = 2; m_yVelocity = 0; m_zVelocity = 0; } else if( std::abs(m_yVelocity) > std::abs(m_xVelocity) && std::abs(m_yVelocity) > std::abs(m_zVelocity) ) { m_TranslationMode = 3; m_xVelocity = 0; m_zVelocity = 0; } else if(std::abs(m_zVelocity) > std::abs(m_xVelocity) && std::abs(m_zVelocity) > std::abs(m_yVelocity) ) { m_TranslationMode = 4; m_xVelocity = 0; m_yVelocity = 0; } // translation mitk::Vector3D movementVector; movementVector.SetElement(0,m_xVelocity); movementVector.SetElement(1,m_yVelocity); movementVector.SetElement(2,m_zVelocity); geometry->Translate(movementVector); // indicate modification of data tree node m_DataNode->Modified(); // update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } return false; } diff --git a/Modules/InputDevices/WiiMote/mitkWiiMoteMultiIREvent.h b/Modules/InputDevices/WiiMote/mitkWiiMoteMultiIREvent.h index c4a83e37c3..76210072a4 100644 --- a/Modules/InputDevices/WiiMote/mitkWiiMoteMultiIREvent.h +++ b/Modules/InputDevices/WiiMote/mitkWiiMoteMultiIREvent.h @@ -1,53 +1,53 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITK_WIIMOTEMULTIIREVENT_H #define MITK_WIIMOTEMULITIREVENT_H #include #include "mitkEvent.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkInteractionConst.h" namespace mitk { class mitkInputDevices_EXPORT WiiMoteMultiIREvent : public Event, itk::EventObject { public: typedef WiiMoteMultiIREvent Self; typedef itk::EventObject Superclass; WiiMoteMultiIREvent(mitk::Point3D Coordinate3D); ~WiiMoteMultiIREvent(); mitk::Point3D Get3DCoordinate() const; //itk::EventObject implementation const char * GetEventName() const; bool CheckEvent(const ::itk::EventObject* e) const; ::itk::EventObject* MakeObject() const; protected: private: mitk::Point3D m_3DCoordinate; }; // end class } // end namspace #endif // MITK_WIIMOTEMULITIREVENT_H diff --git a/Modules/InputDevices/WiiMote/mitkWiiMoteThread.h b/Modules/InputDevices/WiiMote/mitkWiiMoteThread.h index 696471a70d..38ab192630 100644 --- a/Modules/InputDevices/WiiMote/mitkWiiMoteThread.h +++ b/Modules/InputDevices/WiiMote/mitkWiiMoteThread.h @@ -1,218 +1,218 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITK_WIIMOTEHTREAD_H #define MITK_WIIMOTEHTREAD_H #include "wiimote.h" // mitk #include "mitkCommon.h" #include "mitkCallbackFromGUIThread.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkKalmanFilter.h" // itk #include "itkObject.h" #include "itkMultiThreader.h" #include "itkFastMutexLock.h" #include "itksys/SystemTools.hxx" // used for GetTime() and Delay(); namespace mitk { // instead of include, enables this class to know the addon class WiiMoteAddOn; class WiiMoteThread : public itk::Object { public: // typedefs are used in context with CallbackFromGUIThread typedef itk::ReceptorMemberCommand ReceptorCommand; // eventually probs with Linux -> typename typedef ReceptorCommand::Pointer ReceptorCommandPointer; WiiMoteThread(); ~WiiMoteThread(); /** * Allows to set report types, detects extensions and responds to connect/disconnect
* events of extension, such as MotionPlus. * * NOTE: don't access the public state from the 'remote' object here, as it will * be out-of-date (it's only updated via RefreshState() calls, and these * are reserved for the main application so it can be sure the values * stay consistent between calls). Instead query 'new_state' only. * * @param remote * the old state of the connected Wii remote * @param changed * the state change of the Wii remote * @param newState * the new state, after the change is applied (i.e. new extension connected) * */ static void OnStateChange(wiimote &remote, state_change_flags changed, const wiimote_state &newState); /** * Starts the thread for the Wiimote. */ void Run(); /** * Helper function, because the itk::MultiThreader can only
* start a new thread with a static member function. */ static ITK_THREAD_RETURN_TYPE StartWiiMoteThread(void* data); /** * Connects the Wiimote and allows access to its functionality. */ void StartWiiMote(); /** * Stops the running thread. */ void StopWiiMote(); /** * Reconnects the Wiimote in case the connection is lost. */ void ReconnectWiiMote(); /** * Detects all available Wiimotes. * * TODO: more detailed regarding the mode and led lighting */ bool DetectWiiMotes(); /** * Disconnects all connected Wiimotes. */ void DisconnectWiiMotes(); /** * Reads incoming data from the IR camera. After processing the data
* (e.g. computations, assigning commands...) fires Wiimote events accordingly. * */ void WiiMoteIRInput(); /** * Reads incoming data from buttons. After processing the data
* (e.g. computations, assigning commands...) fires Wiimote events
* that indicate a button was pressed. * * @param buttonType * the type of button, that was used to trigger this event */ void WiiMoteButtonPressed(int buttonType); /** * Reads incoming data from buttons. After processing the data
* (e.g. computations, assigning commands...) fires Wiimote events
* that indicate a button release. * * @param buttonType * the type of button, that was used to trigger this event */ void WiiMoteButtonReleased(int buttonType); /** * Reads incoming data from the IR camera. Afterwards the raw x and y coordinates
* are stored in an event and fired as an event. * */ void WiiMoteCalibrationInput(); /** * Constantly refreshes the state of a single wiimote. Also changes between
* the calibration mode and the IR input mode through button push. * * TODO: more detailed explanation of calibration * */ void SingleWiiMoteUpdate(); /** * Constantly refreshes the state of multiple wiimotes. */ void MultiWiiMoteUpdate(); /** * Processes the different IR inputs from multiple wiimotes. */ void MultiWiiMoteIRInput(); /** * Sets the modus for the first connected Wiimote depending
* on the given parameter. * * @param activated * true, the Surface Interaction modus will be activated * false, the Surface Interaction modus will be deactivated */ void SetWiiMoteSurfaceIModus(bool activated); // TODO void SurfaceInteraction(); protected: private: // threading int m_ThreadID; itk::MultiThreader::Pointer m_MultiThreader; // mutex to control the flow of the method StartWiiMote() itk::FastMutexLock::Pointer m_WiiMoteThreadFinished; bool m_StopWiiMote; // access to the wiimote and parameter for callbackfromguithread ReceptorCommandPointer m_Command; // required for computation of movement Point2D m_LastReadData; double m_LastRecordTime; bool m_ReadDataOnce; // modes bool m_InCalibrationMode; bool m_SurfaceInteraction; bool m_ButtonBPressed; // Default: 1 = relative to object // 2 = relative to camera view int m_SurfaceInteractionMode; //store all connected Wiimotes wiimote m_WiiMotes[4]; int m_NumberDetectedWiiMotes; // used for measuring movement int m_TimeStep; // Kalman filter mitk::KalmanFilter::Pointer m_Kalman; }; } #endif // MITK_WIIMOTEHTREAD_H diff --git a/Modules/IpPicSupport/Testing/mitkPicFileReaderTest.cpp b/Modules/IpPicSupport/Testing/mitkPicFileReaderTest.cpp index 2a2c879816..2660661c04 100644 --- a/Modules/IpPicSupport/Testing/mitkPicFileReaderTest.cpp +++ b/Modules/IpPicSupport/Testing/mitkPicFileReaderTest.cpp @@ -1,186 +1,186 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include "mitkImage.h" #include "mitkPicFileReader.h" #include "mitkPicHelper.h" #include "mitkSlicedGeometry3D.h" #include #include #include #include int mitkPicFileReaderTest(int argc, char* argv[]) { MITK_TEST_BEGIN(mitkPicFileReaderTest) if(argc>=1) { if(itksys::SystemTools::FileLength(argv[1]) == 0) { mitk::PicFileReader::Pointer emptyFileReader = mitk::PicFileReader::New(); emptyFileReader->SetFileName(argv[1]); MITK_TEST_FOR_EXCEPTION(itk::ImageFileReaderException,emptyFileReader->Update()); } else { //independently read header of pic file mitkIpPicDescriptor *picheader=NULL; if(itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameExtension(argv[1])).find(".pic")!=std::string::npos) { picheader = mitkIpPicGetHeader(argv[1], NULL); } if(picheader==NULL) { std::cout<<"file not found/not a pic-file - test not applied [PASSED]"<SetFileName(argv[1]); reader->Update(); std::cout << "Testing IsInitialized(): "; if(reader->GetOutput()->IsInitialized()==false) { std::cout<<"[FAILED]"<GetOutput()->IsSliceSet(0)==false) { std::cout<<"[FAILED]"<GetOutput()->GetGeometry()==NULL) { std::cout<<"[FAILED]"<GetOutput()->GetTimeGeometry(); if(timeGeometry==NULL) { std::cout<<"[FAILED]"<GetGeometryForTimeStep(0).IsNull()) { std::cout<<"[FAILED]"<(timeGeometry->GetGeometryForTimeStep(0).GetPointer()); if(slicedgeometry==NULL) { std::cout<<"[FAILED]"<GetPlaneGeometry(0); if(geometry2d==NULL) { std::cout<<"[FAILED]"<GetExtent(0)-picheader->n[0])>mitk::eps) || (fabs(geometry2d->GetExtent(1)-picheader->n[1])>mitk::eps)) { std::cout<<"[FAILED]"<GetExtent(0)-picheader->n[0])>mitk::eps) || (fabs(slicedgeometry->GetExtent(1)-picheader->n[1])>mitk::eps) || (picheader->dim>2 && (fabs(slicedgeometry->GetExtent(2)-picheader->n[2])>mitk::eps)) ) { std::cout<<"[FAILED]"<GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).two_norm(); spacing[1] = slicedgeometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).two_norm(); spacing[2] = slicedgeometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).two_norm(); mitk::Vector3D readspacing=slicedgeometry->GetSpacing(); mitk::Vector3D dist = spacing-readspacing; if(dist.GetSquaredNorm()>mitk::eps) { std::cout<<"[FAILED]"<mitk::eps) { std::cout<<"[FAILED]"<dim==4) { std::cout << "4D dataset: Testing that timebounds are not infinite: "; - if((timeGeometry->GetTimeBounds(0)[0] == mitk::ScalarTypeNumericTraits::NonpositiveMin()) && - (timeGeometry->GetTimeBounds(0)[1] == mitk::ScalarTypeNumericTraits::max()) + if((timeGeometry->GetTimeBounds(0)[0] == itk::NumericTraits::NonpositiveMin()) && + (timeGeometry->GetTimeBounds(0)[1] == itk::NumericTraits::max()) ) { std::cout<<"[FAILED]"< -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include #include namespace mitk { class SlicedGeometry3D; //##Documentation //## @brief Internal class for managing references on sub-images //## @ingroup Data class MitkIpPicSupport_EXPORT PicHelper { public: static const char *GetNameOfClass() { return "PicHelper"; } static bool GetSpacing(const mitkIpPicDescriptor* pic, Vector3D & spacing); static bool SetSpacing(const mitkIpPicDescriptor* pic, SlicedGeometry3D* slicedgeometry); static bool GetTimeSpacing(const mitkIpPicDescriptor* pic, float& timeSpacing); static void InitializeEvenlySpaced(const mitkIpPicDescriptor* pic, unsigned int slices, SlicedGeometry3D* slicedgeometry); static bool SetPlaneGeometry(const mitkIpPicDescriptor* pic, int s, SlicedGeometry3D* slicedgeometry); /** * \deprecatedSince{2014_06} Please use SetPlaneGeometry */ DEPRECATED(static bool SetGeometry2D(const mitkIpPicDescriptor* pic, int s, SlicedGeometry3D* slicedgeometry)){return SetPlaneGeometry(pic,s,slicedgeometry);}; }; } // namespace mitk #endif /* MITKPICHELPER_H_HEADER_INCLUDED_C1F4DAB4 */ diff --git a/Modules/IpPicSupportIO/Testing/mitkPicFileReaderTest.cpp b/Modules/IpPicSupportIO/Testing/mitkPicFileReaderTest.cpp index 01cda35ab7..9ae7e5a5e5 100644 --- a/Modules/IpPicSupportIO/Testing/mitkPicFileReaderTest.cpp +++ b/Modules/IpPicSupportIO/Testing/mitkPicFileReaderTest.cpp @@ -1,189 +1,189 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include "mitkImage.h" #include "mitkPicFileReader.h" #include "mitkPicHelper.h" #include "mitkSlicedGeometry3D.h" #include #include #include #include int mitkPicFileReaderTest(int argc, char* argv[]) { MITK_TEST_BEGIN(mitkPicFileReaderTest) if(argc>=1) { if(itksys::SystemTools::FileLength(argv[1]) == 0) { mitk::PicFileReader::Pointer emptyFileReader = mitk::PicFileReader::New(); emptyFileReader->SetFileName(argv[1]); MITK_TEST_FOR_EXCEPTION(itk::ImageFileReaderException,emptyFileReader->Update()); } else { //independently read header of pic file mitkIpPicDescriptor *picheader=NULL; if(itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameExtension(argv[1])).find(".pic")!=std::string::npos) { picheader = mitkIpPicGetHeader(argv[1], NULL); } if(picheader==NULL) { std::cout<<"file not found/not a pic-file - test not applied [PASSED]"<SetFileName(argv[1]); reader->Update(); std::cout << "Testing IsInitialized(): "; if(reader->GetOutput()->IsInitialized()==false) { std::cout<<"[FAILED]"<GetOutput()->IsSliceSet(0)==false) { std::cout<<"[FAILED]"<GetOutput()->GetGeometry()==NULL) { std::cout<<"[FAILED]"<GetOutput()->GetTimeGeometry(); if(timeGeometry==NULL) { std::cout<<"[FAILED]"<GetGeometryForTimeStep(0).IsNull()) { std::cout<<"[FAILED]"<(timeGeometry->GetGeometryForTimeStep(0).GetPointer()); if(slicedgeometry==NULL) { std::cout<<"[FAILED]"<GetGeometry2D(0); if(geometry2d==NULL) { std::cout<<"[FAILED]"<GetExtent(0)-picheader->n[0])>mitk::eps) || (fabs(geometry2d->GetExtent(1)-picheader->n[1])>mitk::eps)) { std::cout<<"[FAILED]"<GetExtent(0)-picheader->n[0])>mitk::eps) || (fabs(slicedgeometry->GetExtent(1)-picheader->n[1])>mitk::eps) || (picheader->dim>2 && (fabs(slicedgeometry->GetExtent(2)-picheader->n[2])>mitk::eps)) ) { std::cout<<"[FAILED]"<GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).two_norm(); spacing[1] = slicedgeometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).two_norm(); spacing[2] = slicedgeometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).two_norm(); mitk::Vector3D readspacing=slicedgeometry->GetSpacing(); mitk::Vector3D dist = spacing-readspacing; if(dist.GetSquaredNorm()>mitk::eps) { std::cout<<"[FAILED]"<mitk::eps) { std::cout<<"[FAILED]"<dim==4) { std::cout << "4D dataset: Testing that timebounds are not infinite: "; - if((slicedgeometry->GetTimeBounds()[0] == mitk::ScalarTypeNumericTraits::NonpositiveMin()) && - (slicedgeometry->GetTimeBounds()[1] == mitk::ScalarTypeNumericTraits::max()) + if((slicedgeometry->GetTimeBounds()[0] == itk::NumericTraits::NonpositiveMin()) && + (slicedgeometry->GetTimeBounds()[1] == itk::NumericTraits::max()) ) { std::cout<<"[FAILED]"< #include const float selectedColor[]={1.0,0.0,0.6}; //for selected! mitk::MeshMapper2D::MeshMapper2D() { } mitk::MeshMapper2D::~MeshMapper2D() { } const mitk::Mesh *mitk::MeshMapper2D::GetInput(void) { return static_cast ( GetDataNode()->GetData() ); } // Return whether a point is "smaller" than the second static bool point3DSmaller( const mitk::Point3D& elem1, const mitk::Point3D& elem2 ) { if(elem1[0]!=elem2[0]) return elem1[0] < elem2[0]; if(elem1[1]!=elem2[1]) return elem1[1] < elem2[1]; return elem1[2] < elem2[2]; } void mitk::MeshMapper2D::Paint( mitk::BaseRenderer *renderer ) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if(!visible) return; // @FIXME: Logik fuer update bool updateNeccesary = true; if (updateNeccesary) { //aus GenerateData mitk::Mesh::Pointer input = const_cast(this->GetInput()); // Get the TimeGeometry of the input object const TimeGeometry* inputTimeGeometry = input->GetTimeGeometry(); if (( inputTimeGeometry == NULL ) || ( inputTimeGeometry->CountTimeSteps() == 0 ) ) { return; } // // get the world time // ScalarType time = renderer->GetTime(); // // convert the world time in time steps of the input object // int timeStep=0; - if ( time > ScalarTypeNumericTraits::NonpositiveMin() ) + if ( time > itk::NumericTraits::NonpositiveMin() ) timeStep = inputTimeGeometry->TimePointToTimeStep( time ); if ( inputTimeGeometry->IsValidTimeStep( timeStep ) == false ) { return; } mitk::Mesh::MeshType::Pointer itkMesh = input->GetMesh( timeStep ); if ( itkMesh.GetPointer() == NULL) { return; } mitk::DisplayGeometry::Pointer displayGeometry = renderer->GetDisplayGeometry(); assert(displayGeometry.IsNotNull()); const PlaneGeometry* worldplanegeometry = dynamic_cast(renderer->GetCurrentWorldPlaneGeometry()); //apply color and opacity read from the PropertyList ApplyColorAndOpacityProperties(renderer); vtkLinearTransform* transform = GetDataNode()->GetVtkTransform(); //List of the Points Mesh::DataType::PointsContainerConstIterator it, end; it=itkMesh->GetPoints()->Begin(); end=itkMesh ->GetPoints()->End(); //iterator on the additional data of each point Mesh::PointDataIterator dataIt;//, dataEnd; dataIt=itkMesh->GetPointData()->Begin(); //for switching back to old color after using selected color float unselectedColor[4]; glGetFloatv(GL_CURRENT_COLOR,unselectedColor); while(it!=end) { mitk::Point3D p, projected_p; float vtkp[3]; itk2vtk(it->Value(), vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); Vector3D diff=p-projected_p; if(diff.GetSquaredNorm()<4.0) { Point2D pt2d, tmp; displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); Vector2D horz,vert; horz[0]=5; horz[1]=0; vert[0]=0; vert[1]=5; //check if the point is to be marked as selected if (dataIt->Value().selected) { horz[0]=8; vert[1]=8; glColor3f(selectedColor[0],selectedColor[1],selectedColor[2]);//red switch (dataIt->Value().pointSpec) { case PTSTART: { //a quad glBegin (GL_LINE_LOOP); tmp=pt2d-horz+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz-vert; glVertex2dv(&tmp[0]); tmp=pt2d-horz-vert; glVertex2dv(&tmp[0]); glEnd (); } break; case PTUNDEFINED: { //a diamond around the point glBegin (GL_LINE_LOOP); tmp=pt2d-horz; glVertex2dv(&tmp[0]); tmp=pt2d+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz; glVertex2dv(&tmp[0]); tmp=pt2d-vert; glVertex2dv(&tmp[0]); glEnd (); } break; default: break; }//switch //the actual point glBegin (GL_POINTS); tmp=pt2d; glVertex2dv(&tmp[0]); glEnd (); } else //if not selected { glColor3f(unselectedColor[0],unselectedColor[1],unselectedColor[2]); switch (dataIt->Value().pointSpec) { case PTSTART: { //a quad glBegin (GL_LINE_LOOP); tmp=pt2d-horz+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz-vert; glVertex2dv(&tmp[0]); tmp=pt2d-horz-vert; glVertex2dv(&tmp[0]); glEnd (); } case PTUNDEFINED: { //drawing crosses glBegin (GL_LINES); tmp=pt2d-horz; glVertex2dv(&tmp[0]); tmp=pt2d+horz; glVertex2dv(&tmp[0]); tmp=pt2d-vert; glVertex2dv(&tmp[0]); tmp=pt2d+vert; glVertex2dv(&tmp[0]); glEnd (); } default: { break; } }//switch }//else } ++it; ++dataIt; } //now connect the lines inbetween mitk::Mesh::PointType thisPoint; thisPoint.Fill(0); Point2D *firstOfCell = NULL; Point2D *lastPoint = NULL; unsigned int lastPointId = 0; bool lineSelected = false; Point3D firstOfCell3D; Point3D lastPoint3D; bool first; mitk::Line line; std::vector intersectionPoints; double t; //iterate through all cells and then iterate through all indexes of points in that cell Mesh::CellIterator cellIt, cellEnd; Mesh::CellDataIterator cellDataIt;//, cellDataEnd; Mesh::PointIdIterator cellIdIt, cellIdEnd; cellIt = itkMesh->GetCells()->Begin(); cellEnd = itkMesh->GetCells()->End(); cellDataIt = itkMesh->GetCellData()->Begin(); while (cellIt != cellEnd) { unsigned int numOfPointsInCell = cellIt->Value()->GetNumberOfPoints(); if (numOfPointsInCell>1) { //iterate through all id's in the cell cellIdIt = cellIt->Value()->PointIdsBegin(); cellIdEnd = cellIt->Value()->PointIdsEnd(); firstOfCell3D = input->GetPoint(*cellIdIt,timeStep); intersectionPoints.clear(); intersectionPoints.reserve(numOfPointsInCell); first = true; while(cellIdIt != cellIdEnd) { lastPoint3D = thisPoint; thisPoint = input->GetPoint(*cellIdIt,timeStep); //search in data (vector<> selectedLines) if the index of the point is set. if so, then the line is selected. lineSelected = false; Mesh::SelectedLinesType selectedLines = cellDataIt->Value().selectedLines; //a line between 1(lastPoint) and 2(pt2d) has the Id 1, so look for the Id of lastPoint //since we only start, if we have more than one point in the cell, lastPointId is initiated with 0 Mesh::SelectedLinesIter position = std::find(selectedLines.begin(), selectedLines.end(), lastPointId); if (position != selectedLines.end()) { lineSelected = true; } mitk::Point3D p, projected_p; float vtkp[3]; itk2vtk(thisPoint, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); Vector3D diff=p-projected_p; if(diff.GetSquaredNorm()<4.0) { Point2D pt2d, tmp; displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); if (lastPoint == NULL) { //set the first point in the cell. This point in needed to close the polygon firstOfCell = new Point2D; *firstOfCell = pt2d; lastPoint = new Point2D; *lastPoint = pt2d; lastPointId = *cellIdIt; } else { if (lineSelected) { glColor3f(selectedColor[0],selectedColor[1],selectedColor[2]);//red //a line from lastPoint to thisPoint glBegin (GL_LINES); glVertex2dv(&(*lastPoint)[0]); glVertex2dv(&pt2d[0]); glEnd (); } else //if not selected { glColor3f(unselectedColor[0],unselectedColor[1],unselectedColor[2]); //drawing crosses glBegin (GL_LINES); glVertex2dv(&(*lastPoint)[0]); glVertex2dv(&pt2d[0]); glEnd (); } //to draw the line to the next in iteration step *lastPoint = pt2d; //and to search for the selection state of the line lastPointId = *cellIdIt; }//if..else }//if <4.0 //fill off-plane polygon part 1 if((!first) && (worldplanegeometry!=NULL)) { line.SetPoints(lastPoint3D, thisPoint); if(worldplanegeometry->IntersectionPointParam(line, t) && ((t>=0) && (t<=1)) ) { intersectionPoints.push_back(line.GetPoint(t)); } } ++cellIdIt; first=false; }//while cellIdIter //closed polygon? if ( cellDataIt->Value().closed ) { //close the polygon if needed if( firstOfCell != NULL ) { lineSelected = false; Mesh::SelectedLinesType selectedLines = cellDataIt->Value().selectedLines; Mesh::SelectedLinesIter position = std::find(selectedLines.begin(), selectedLines.end(), lastPointId); if (position != selectedLines.end())//found the index { glColor3f(selectedColor[0],selectedColor[1],selectedColor[2]);//red //a line from lastPoint to firstPoint glBegin (GL_LINES); glVertex2dv(&(*lastPoint)[0]); glVertex2dv(&(*firstOfCell)[0]); glEnd (); } else { glColor3f(unselectedColor[0],unselectedColor[1],unselectedColor[2]); glBegin (GL_LINES); glVertex2dv(&(*lastPoint)[0]); glVertex2dv(&(*firstOfCell)[0]); glEnd (); } } }//if closed //Axis-aligned bounding box(AABB) around the cell if selected and set in Property bool showBoundingBox; if (dynamic_cast(this->GetDataNode()->GetProperty("showBoundingBox")) == NULL) showBoundingBox = false; else showBoundingBox = dynamic_cast(this->GetDataNode()->GetProperty("showBoundingBox"))->GetValue(); if(showBoundingBox) { if (cellDataIt->Value().selected) { mitk::Mesh::DataType::BoundingBoxPointer aABB = input->GetBoundingBoxFromCell(cellIt->Index()); if (aABB.IsNotNull()) { mitk::Mesh::PointType min, max; min = aABB->GetMinimum(); max = aABB->GetMaximum(); //project to the displayed geometry Point2D min2D, max2D; Point3D p, projected_p; float vtkp[3]; itk2vtk(min, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, min2D); displayGeometry->WorldToDisplay(min2D, min2D); itk2vtk(max, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); Vector3D diff=p-projected_p; if(diff.GetSquaredNorm()<4.0) { displayGeometry->Map(projected_p, max2D); displayGeometry->WorldToDisplay(max2D, max2D); //draw the BoundingBox glColor3f(selectedColor[0],selectedColor[1],selectedColor[2]);//red //a line from lastPoint to firstPoint glBegin(GL_LINE_LOOP); glVertex2f(min2D[0], min2D[1]); glVertex2f(min2D[0], max2D[1]); glVertex2f(max2D[0], max2D[1]); glVertex2f(max2D[0], min2D[1]); glEnd(); }//draw bounding-box }//bounding-box exists }//cell selected }//show bounding-box //fill off-plane polygon part 2 if(worldplanegeometry!=NULL) { //consider line from last to first line.SetPoints(thisPoint, firstOfCell3D); if(worldplanegeometry->IntersectionPointParam(line, t) && ((t>=0) && (t<=1)) ) { intersectionPoints.push_back(line.GetPoint(t)); } std::sort(intersectionPoints.begin(), intersectionPoints.end(), point3DSmaller); std::vector::iterator it, end; end=intersectionPoints.end(); if((intersectionPoints.size()%2)!=0) { --end; //ensure even number of intersection-points } double p[2]; Point3D pt3d; Point2D pt2d; for ( it = intersectionPoints.begin( ); it != end; ++it ) { glBegin (GL_LINES); displayGeometry->Map(*it, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); p[0] = pt2d[0]; p[1] = pt2d[1]; glVertex2dv(p); ++it; displayGeometry->Map(*it, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); p[0] = pt2d[0]; p[1] = pt2d[1]; glVertex2dv(p); glEnd (); } if(it!=intersectionPoints.end()) { glBegin (GL_LINES); displayGeometry->Map(*it, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); p[0] = pt2d[0]; p[1] = pt2d[1]; glVertex2dv(p); p[0] = pt2d[0]; p[1] = pt2d[1]; glVertex2dv(p); glEnd (); } }//fill off-plane polygon part 2 }//if numOfPointsInCell>1 delete firstOfCell; delete lastPoint; lastPoint = NULL; firstOfCell = NULL; lastPointId = 0; ++cellIt; ++cellDataIt; } } } diff --git a/Modules/MapperExt/mitkVectorImageMapper2D.cpp b/Modules/MapperExt/mitkVectorImageMapper2D.cpp index 72d4c4853f..3c43734e93 100644 --- a/Modules/MapperExt/mitkVectorImageMapper2D.cpp +++ b/Modules/MapperExt/mitkVectorImageMapper2D.cpp @@ -1,531 +1,531 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkVectorImageMapper2D.h" //vtk related includes #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 //mitk related includes #include "mitkGL.h" #include "mitkBaseRenderer.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkAbstractTransformGeometry.h" #include const mitk::Image * mitk::VectorImageMapper2D::GetInput( void ) { if ( m_Image.IsNotNull() ) return m_Image; else return dynamic_cast( GetDataNode()->GetData() ); } void mitk::VectorImageMapper2D::Paint( mitk::BaseRenderer * renderer ) { //std::cout << "2d vector mapping..." << std::endl; bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) return ; mitk::Image::Pointer input = const_cast( this->GetInput() ); if ( input.IsNull() ) return ; mitk::PlaneGeometry::Pointer worldPlanePlaneGeometry = dynamic_cast< mitk::PlaneGeometry*>( const_cast( renderer->GetCurrentWorldPlaneGeometry() ) ); assert( worldPlanePlaneGeometry.IsNotNull() ); vtkImageData* vtkImage = input->GetVtkImageData( this->GetCurrentTimeStep( input, renderer ) ); // // set up the cutter orientation according to the current geometry of // the renderers plane // Point3D point; Vector3D normal; PlaneGeometry::ConstPointer worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); PlaneGeometry::ConstPointer worldPlaneGeometry = dynamic_cast( worldGeometry.GetPointer() ); if ( worldPlaneGeometry.IsNotNull() ) { // set up vtkPlane according to worldGeometry point = worldPlaneGeometry->GetOrigin(); normal = worldPlaneGeometry->GetNormal(); normal.Normalize(); m_Plane->SetTransform( (vtkAbstractTransform*)NULL ); } else { itkWarningMacro( << "worldPlaneGeometry is NULL!" ); return ; } double vp[ 3 ], vp_slice[ 3 ], vnormal[ 3 ]; vnl2vtk( point.GetVnlVector(), vp ); vnl2vtk( normal.GetVnlVector(), vnormal ); //std::cout << "Origin: " << vp[0] <<" "<< vp[1] <<" "<< vp[2] << std::endl; //std::cout << "Normal: " << vnormal[0] <<" "<< vnormal[1] <<" "<< vnormal[2] << std::endl; //normally, we would need to transform the surface and cut the transformed surface with the cutter. //This might be quite slow. Thus, the idea is, to perform an inverse transform of the plane instead. //@todo It probably does not work for scaling operations yet:scaling operations have to be //dealed with after the cut is performed by scaling the contour. vtkLinearTransform * vtktransform = GetDataNode() ->GetVtkTransform(); vtkTransform* world2vtk = vtkTransform::New(); world2vtk->Identity(); world2vtk->Concatenate(vtktransform->GetLinearInverse()); double myscale[3]; world2vtk->GetScale(myscale); world2vtk->PostMultiply(); world2vtk->Scale(1/myscale[0],1/myscale[1],1/myscale[2]); world2vtk->TransformPoint( vp, vp ); world2vtk->TransformNormalAtPoint( vp, vnormal, vnormal ); world2vtk->Delete(); // vtk works in axis align coords // thus the normal also must be axis align, since // we do not allow arbitrary cutting through volume // // vnormal should already be axis align, but in order // to get rid of precision effects, we set the two smaller // components to zero here int dims[3]; vtkImage->GetDimensions(dims); double spac[3]; vtkImage->GetSpacing(spac); vp_slice[0] = vp[0]; vp_slice[1] = vp[1]; vp_slice[2] = vp[2]; if(fabs(vnormal[0]) > fabs(vnormal[1]) && fabs(vnormal[0]) > fabs(vnormal[2]) ) { if(fabs(vp_slice[0]/spac[0]) < 0.4) vp_slice[0] = 0.4*spac[0]; if(fabs(vp_slice[0]/spac[0]) > (dims[0]-1)-0.4) vp_slice[0] = ((dims[0]-1)-0.4)*spac[0]; vnormal[1] = 0; vnormal[2] = 0; } if(fabs(vnormal[1]) > fabs(vnormal[0]) && fabs(vnormal[1]) > fabs(vnormal[2]) ) { if(fabs(vp_slice[1]/spac[1]) < 0.4) vp_slice[1] = 0.4*spac[1]; if(fabs(vp_slice[1]/spac[1]) > (dims[1]-1)-0.4) vp_slice[1] = ((dims[1]-1)-0.4)*spac[1]; vnormal[0] = 0; vnormal[2] = 0; } if(fabs(vnormal[2]) > fabs(vnormal[1]) && fabs(vnormal[2]) > fabs(vnormal[0]) ) { if(fabs(vp_slice[2]/spac[2]) < 0.4) vp_slice[2] = 0.4*spac[2]; if(fabs(vp_slice[2]/spac[2]) > (dims[2]-1)-0.4) vp_slice[2] = ((dims[2]-1)-0.4)*spac[2]; vnormal[0] = 0; vnormal[1] = 0; } m_Plane->SetOrigin( vp_slice ); m_Plane->SetNormal( vnormal ); vtkPolyData* cuttedPlane; if(!( (dims[0] == 1 && vnormal[0] != 0) || (dims[1] == 1 && vnormal[1] != 0) || (dims[2] == 1 && vnormal[2] != 0) )) { m_Cutter->SetCutFunction( m_Plane ); m_Cutter->SetInputData( vtkImage ); m_Cutter->GenerateCutScalarsOff();//! m_Cutter->Update(); cuttedPlane = m_Cutter->GetOutput(); } else { // cutting of a 2D-Volume does not work, // so we have to build up our own polydata object cuttedPlane = vtkPolyData::New(); vtkPoints* points = vtkPoints::New(); points->SetNumberOfPoints(vtkImage->GetNumberOfPoints()); for(int i=0; iGetNumberOfPoints(); i++) points->SetPoint(i, vtkImage->GetPoint(i)); cuttedPlane->SetPoints(points); vtkFloatArray* pointdata = vtkFloatArray::New(); int comps = vtkImage->GetPointData()->GetScalars()->GetNumberOfComponents(); pointdata->SetNumberOfComponents(comps); int tuples = vtkImage->GetPointData()->GetScalars()->GetNumberOfTuples(); pointdata->SetNumberOfTuples(tuples); for(int i=0; iSetTuple(i,vtkImage->GetPointData()->GetScalars()->GetTuple(i)); pointdata->SetName( "vector" ); cuttedPlane->GetPointData()->AddArray(pointdata); } if ( cuttedPlane->GetNumberOfPoints() != 0) { // // make sure, that we have point data with more than 1 component (as vectors) // vtkPointData * pointData = cuttedPlane->GetPointData(); if ( pointData == NULL ) { itkWarningMacro( << "no point data associated with cutters result!" ); return ; } if ( pointData->GetNumberOfArrays() == 0 ) { itkWarningMacro( << "point data returned by cutter doesn't have any arrays associated!" ); return ; } else if ( pointData->GetArray(0)->GetNumberOfComponents() <= 1) { itkWarningMacro( << "number of components <= 1!" ); return; } else if ( pointData->GetArrayName( 0 ) == NULL ) { pointData->GetArray( 0 ) ->SetName( "vector" ); //std::cout << "array name = vectors now" << std::endl; } //std::cout << " projecting..."<< std::endl; // // constrain the vectors to lie on the plane, which means to remove the vector component, // which is orthogonal to the plane. // vtkIdType numPoints, pointId; numPoints = cuttedPlane->GetNumberOfPoints(); vtkDataArray* inVectors = cuttedPlane->GetPointData()->GetVectors( "vector" ); assert( inVectors != NULL ); vtkFloatArray* vectorMagnitudes = vtkFloatArray::New(); vectorMagnitudes->SetName("vectorMagnitudes"); vectorMagnitudes->SetNumberOfComponents(1); vectorMagnitudes->SetNumberOfValues(numPoints); vectorMagnitudes->SetNumberOfTuples(numPoints); double inVector[ 3 ], outVector[3], wnormal[3]; //, tmpVector[ 3 ], outVector[ 3 ]; double k = 0.0; vnl2vtk( normal.GetVnlVector(), wnormal ); vtkMath::Normalize( wnormal ); bool normalizeVecs; m_DataNode->GetBoolProperty( "NormalizeVecs", normalizeVecs ); for ( pointId = 0; pointId < numPoints; ++pointId ) { inVectors->GetTuple( pointId, inVector ); if(normalizeVecs) { vnl_vector tmp(3); vtk2vnl(inVector, tmp); tmp.normalize(); vnl2vtk(tmp, inVector); } k = vtkMath::Dot( wnormal, inVector ); // Remove non orthogonal component. outVector[ 0 ] = inVector[ 0 ] - ( wnormal[ 0 ] * k ); outVector[ 1 ] = inVector[ 1 ] - ( wnormal[ 1 ] * k ); outVector[ 2 ] = inVector[ 2 ] - ( wnormal[ 2 ] * k ); inVectors->SetTuple( pointId, outVector ); // ?? this was set to norm(inVector) before, but outVector made more sense to me vectorMagnitudes->SetValue( pointId, vtkMath::Norm( outVector ) ); //std::cout << "method old: " << inVector[0] <<", " << inVector[1] << ", "<AddArray(vectorMagnitudes); pointData->CopyAllOn(); //pointData->PrintSelf(std::cout, vtkIndent(4)); //std::cout << " ...done!"<< std::endl; //std::cout << " glyphing..."<< std::endl; // call glyph2D to generate 2D glyphs for each of the // vectors vtkGlyphSource2D* glyphSource = vtkGlyphSource2D::New(); //glyphSource->SetGlyphTypeToDash(); glyphSource->DashOn(); //glyphSource->SetScale( 0.1 ); //glyphSource->SetScale2( .5 ); //glyphSource->SetCenter( 0.5, 0.5, 0.5 ); glyphSource->CrossOff(); //glyphSource->FilledOff(); //glyphSource->Update(); double spacing[3]; vtkImage->GetSpacing(spacing); double min = spacing[0]; min = min > spacing[1] ? spacing[1] : min; min = min > spacing[2] ? spacing[2] : min; float scale = 1; mitk::FloatProperty::Pointer mitkScaleProp = dynamic_cast(GetDataNode()->GetProperty("Scale")); if (mitkScaleProp.IsNotNull()) { scale = mitkScaleProp->GetValue(); } vtkMaskedGlyph3D* glyphGenerator = vtkMaskedGlyph3D::New(); glyphGenerator->SetSourceData(glyphSource->GetOutput() ); glyphGenerator->SetInput(cuttedPlane); glyphGenerator->SetInputArrayToProcess (1, 0,0, vtkDataObject::FIELD_ASSOCIATION_POINTS , "vector"); glyphGenerator->SetVectorModeToUseVector(); glyphGenerator->OrientOn(); glyphGenerator->SetScaleFactor( min*scale ); glyphGenerator->SetUseMaskPoints( true ); glyphGenerator->SetRandomMode( true ); glyphGenerator->SetMaximumNumberOfPoints( 128*128 ); glyphGenerator->Update(); /* vtkLookupTable* vtkLut = NULL; mitk::LookupTableProperty::Pointer mitkLutProp = dynamic_cast(GetDataNode()->GetProperty("LookupTable")); if (mitkLutProp.IsNotNull()) { vtkLut = mitkLutProp->GetLookupTable()->GetVtkLookupTable(); } */ mitk::Color color; mitk::ColorProperty::Pointer mitkColorProp = dynamic_cast(GetDataNode()->GetProperty("color")); if (mitkColorProp.IsNotNull()) { color = mitkColorProp->GetColor(); } else { color.SetRed(0); color.SetBlue(1); color.SetGreen(0); } float lwidth = 1; mitk::FloatProperty::Pointer mitkLWidthProp = dynamic_cast(GetDataNode()->GetProperty("LineWidth")); if (mitkLWidthProp.IsNotNull()) { lwidth = mitkLWidthProp->GetValue(); } vtkTransform* trafo = vtkTransform::New(); trafo->Identity(); trafo->Concatenate(vtktransform); trafo->PreMultiply(); double myscale[3]; trafo->GetScale(myscale); trafo->Scale(1/myscale[0],1/myscale[1],1/myscale[2]); this->PaintCells( glyphGenerator->GetOutput(), renderer->GetCurrentWorldPlaneGeometry(), renderer->GetDisplayGeometry(), trafo, renderer, NULL/*vtkLut*/, color, lwidth, spacing ); vectorMagnitudes->Delete(); glyphSource->Delete(); glyphGenerator->Delete(); trafo->Delete(); } else { std::cout << " no points cutted!"<< std::endl; } //std::cout << "...done!" << std::endl; } void mitk::VectorImageMapper2D::PaintCells( vtkPolyData* glyphs, const PlaneGeometry* worldGeometry, const DisplayGeometry* displayGeometry, vtkLinearTransform* vtktransform, mitk::BaseRenderer* /*renderer*/, vtkScalarsToColors *lut, mitk::Color color, float lwidth, double *spacing ) { vtkPoints * points = glyphs->GetPoints(); vtkPointData * vpointdata = glyphs->GetPointData(); vtkDataArray* vpointscalars = vpointdata->GetArray("vectorMagnitudes"); //vtkDataArray* vpointpositions = vpointdata->GetArray("pointPositions"); assert(vpointscalars != NULL); //std::cout << " Scalars range 2d:" << vpointscalars->GetRange()[0] << " " << vpointscalars->GetRange()[0] << std::endl; Point3D p; Point2D p2d; vtkIdList* idList; vtkCell* cell; double offset[3]; for (unsigned int i = 0; i < 3; ++i) { offset[i] = 0; } vtkIdType numCells = glyphs->GetNumberOfCells(); for ( vtkIdType cellId = 0; cellId < numCells; ++cellId ) { double vp[ 3 ]; cell = glyphs->GetCell( cellId ); idList = cell->GetPointIds(); int numPoints = idList->GetNumberOfIds(); if(numPoints == 1) { //take transformation via vtktransform into account double pos[ 3 ],vp_raster[3]; points->GetPoint( idList->GetId( 0 ), vp ); vp_raster[0] = vtkMath::Round(vp[0]/spacing[0])*spacing[0]; vp_raster[1] = vtkMath::Round(vp[1]/spacing[1])*spacing[1]; vp_raster[2] = vtkMath::Round(vp[2]/spacing[2])*spacing[2]; vtktransform->TransformPoint( vp_raster, pos ); offset[0] = pos[0] - vp[0]; offset[1] = pos[1] - vp[1]; offset[2] = pos[2] - vp[2]; } else { glLineWidth(lwidth); glBegin ( GL_LINE_LOOP ); for ( int pointNr = 0; pointNr < numPoints ;++pointNr ) { points->GetPoint( idList->GetId( pointNr ), vp ); vp[0] = vp[0] + offset[0]; vp[1] = vp[1] + offset[1]; vp[2] = vp[2] + offset[2]; double tmp[ 3 ]; vtktransform->TransformPoint( vp,tmp ); vtk2itk( vp, p ); //convert 3D point (in mm) to 2D point on slice (also in mm) worldGeometry->Map( p, p2d ); //convert point (until now mm and in worldcoordinates) to display coordinates (units ) displayGeometry->WorldToDisplay( p2d, p2d ); if ( lut != NULL ) { // color each point according to point data double * color; if ( vpointscalars != NULL ) { vpointscalars->GetComponent( pointNr, 0 ); color = lut->GetColor( vpointscalars->GetComponent( idList->GetId( pointNr ), 0 ) ); glColor3f( color[ 0 ], color[ 1 ], color[ 2 ] ); } } else { glColor3f( color.GetRed(), color.GetGreen(), color.GetBlue() ); } //std::cout << idList->GetId( pointNr )<< ": " << p2d[0]<< " "<< p2d[1] << std::endl; //draw the line glVertex2f( p2d[ 0 ], p2d[ 1 ] ); } glEnd (); } } } mitk::VectorImageMapper2D::VectorImageMapper2D() { m_LUT = NULL; m_Plane = vtkPlane::New(); m_Cutter = vtkCutter::New(); m_Cutter->SetCutFunction( m_Plane ); m_Cutter->GenerateValues( 1, 0, 1 ); } mitk::VectorImageMapper2D::~VectorImageMapper2D() { if ( m_LUT != NULL ) m_LUT->Delete(); if ( m_Plane != NULL ) m_Plane->Delete(); if ( m_Cutter != NULL ) m_Cutter->Delete(); } int mitk::VectorImageMapper2D::GetCurrentTimeStep( mitk::BaseData* data, mitk::BaseRenderer* renderer ) { // // get the TimeGeometry of the input object // const TimeGeometry * dataTimeGeometry = data->GetUpdatedTimeGeometry(); if ( ( dataTimeGeometry == NULL ) || ( dataTimeGeometry->CountTimeSteps() == 0 ) ) { itkWarningMacro( << "The given object is missing a mitk::TimeGeometry, or the number of time steps is 0!" ); return 0; } // // get the world time // ScalarType time = renderer->GetTime(); // // convert the world time to time steps of the input object // int timestep = 0; - if ( time > ScalarTypeNumericTraits::NonpositiveMin() ) + if ( time > itk::NumericTraits::NonpositiveMin() ) timestep = dataTimeGeometry->TimePointToTimeStep( time ); if ( dataTimeGeometry->IsValidTimeStep( timestep ) == false ) { itkWarningMacro( << timestep << " is not a valid time of the given data object!" ); return 0; } return timestep; } diff --git a/Modules/OpenCVVideoSupport/mitkUndistortCameraImage.h b/Modules/OpenCVVideoSupport/mitkUndistortCameraImage.h index a094e0f17a..ae51d11d03 100644 --- a/Modules/OpenCVVideoSupport/mitkUndistortCameraImage.h +++ b/Modules/OpenCVVideoSupport/mitkUndistortCameraImage.h @@ -1,130 +1,130 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkUndistortCameraImage_h #define __mitkUndistortCameraImage_h #include "mitkConfig.h" #include "mitkCommon.h" #include #include "itkObject.h" #include "itkObjectFactory.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "cv.h" /*! \brief UndistortCameraImage This class is used to undistort camera images. Before any undistortion the class has to be initialized using the functions: SetFocalLength(),SetPrinzipalPoint() and SetCameraDistortion(). After this you can either use UndistortPixel() to undistort a single pixel's coordinates or UndistortImage() to undistort an OpenCV image. A faster version of UndistortImage() is UndistortImageFast(), however, it has to be initialized once with SetUndistortImageFastInfo() instead of the Set... methods before use. \sa QmitkFunctionality \ingroup Functionalities */ namespace mitk { class MITK_OPENCVVIDEOSUPPORT_EXPORT UndistortCameraImage : public itk::Object { public: mitkClassMacro(UndistortCameraImage,itk::Object); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /// Initialization /// /* * Set the camera's intrinsic focal length */ void SetFocalLength(float fc_x, float fc_y) { m_fcX = fc_x; m_fcY = fc_y; } /* * Set the camera's intrinsic principal point */ void SetPrincipalPoint(float cc_x, float cc_y) { m_ccX = cc_x; m_ccY = cc_y; } /* * Set the camera's intrinsic distortion parameters */ void SetCameraDistortion(float kc1, float kc2, float kc3, float kc4) { m_distortionMatrixData[0] = kc1; m_distortionMatrixData[1] = kc2; m_distortionMatrixData[2] = kc3; m_distortionMatrixData[3] = kc4; } /* * Pre-Calculates matrices for the later use of UndistortImageFast() */ void InitRemapUndistortion(int sizeX, int sizeY); /// USAGE /// /* * Undistort a single pixel, returns undistorted pixel */ mitk::Point2D UndistortPixel(mitk::Point2D src); /* * Complete undistortion of an OpenCV image, including all calculations */ void UndistortImage(IplImage* src, IplImage* dst); /* * Complete undistortion of an OpenCV image, using pre-calculated matrices from SetUndistortImageFastInfo() * The use of only a source parameter will cause the source to be overwritten. * NOTE: Using the Fast undistortion methods does not require a initialization via the Set... methods. */ void UndistortImageFast( IplImage * src, IplImage* dst = NULL ); void SetUndistortImageFastInfo(float in_dF1, float in_dF2, float in_dPrincipalX, float in_dPrincipalY, float in_Dist[4], float ImageSizeX, float ImageSizeY); UndistortCameraImage(); virtual ~UndistortCameraImage(); protected: // principal point and focal length parameters float m_ccX, m_ccY, m_fcX, m_fcY; // undistortion parameters float m_distortionMatrixData[4]; // intrinsic camera parameters float m_intrinsicMatrixData[9]; // precalculated matrices for fast image undistortion with UndistortImageFast() CvMat * m_mapX, * m_mapY; // intrinsic and undistortion camera matrices CvMat m_intrinsicMatrix, m_distortionMatrix; // temp image IplImage * m_tempImage; CvMat *m_DistortionCoeffs; CvMat *m_CameraMatrix; }; } #endif diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.cpp index 6849893664..2f8c2d0892 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.cpp @@ -1,117 +1,117 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlanarArrow.h" #include "mitkPlaneGeometry.h" mitk::PlanarArrow::PlanarArrow() : FEATURE_ID_LENGTH( this->AddFeature( "Length", "mm" ) ) { // Directed arrow has two control points this->ResetNumberOfControlPoints( 2 ); m_ArrowTipScaleFactor = -1.0; this->SetNumberOfPolyLines( 1 ); this->SetNumberOfHelperPolyLines( 2 ); // Create helper polyline object (for drawing the orthogonal orientation line) m_HelperPolyLinesToBePainted->InsertElement( 0, false ); m_HelperPolyLinesToBePainted->InsertElement( 1, false ); } mitk::PlanarArrow::~PlanarArrow() { } void mitk::PlanarArrow::GeneratePolyLine() { this->ClearPolyLines(); this->AppendPointToPolyLine(0, this->GetControlPoint(0)); this->AppendPointToPolyLine(0, this->GetControlPoint(1)); } void mitk::PlanarArrow::GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight) { // Generate helper polyline (orientation line orthogonal to first line) // if the third control point is currently being set if ( this->GetNumberOfControlPoints() != 2 ) { m_HelperPolyLinesToBePainted->SetElement( 0, false ); m_HelperPolyLinesToBePainted->SetElement( 1, false ); return; } this->ClearHelperPolyLines(); m_HelperPolyLinesToBePainted->SetElement( 0, true ); m_HelperPolyLinesToBePainted->SetElement( 1, true ); //Fixed size depending on screen size for the angle float scaleFactor = 0.015; if ( m_ArrowTipScaleFactor > 0.0 ) { scaleFactor = m_ArrowTipScaleFactor; } double nonScalingLength = displayHeight * mmPerDisplayUnit * scaleFactor; // Calculate arrow peak const Point2D p1 = this->GetControlPoint( 0 ); const Point2D p2 = this->GetControlPoint( 1 ); Vector2D n1 = p1 - p2; n1.Normalize(); double degrees = 100.0; Vector2D temp; temp[0] = n1[0] * cos(degrees) - n1[1] * sin(degrees); temp[1] = n1[0] * sin(degrees) + n1[1] * cos(degrees); Vector2D temp2; temp2[0] = n1[0] * cos(-degrees) - n1[1] * sin(-degrees); temp2[1] = n1[0] * sin(-degrees) + n1[1] * cos(-degrees); this->AppendPointToHelperPolyLine(0, p1); - this->AppendPointToHelperPolyLine(0, p1 - temp * nonScalingLength); + this->AppendPointToHelperPolyLine(0, Point2D(p1 - temp * nonScalingLength)); this->AppendPointToHelperPolyLine(1, p1); - this->AppendPointToHelperPolyLine(1, p1 - temp2 * nonScalingLength); + this->AppendPointToHelperPolyLine(1, Point2D(p1 - temp2 * nonScalingLength)); } void mitk::PlanarArrow::EvaluateFeaturesInternal() { // Calculate line length const Point3D &p0 = this->GetWorldControlPoint( 0 ); const Point3D &p1 = this->GetWorldControlPoint( 1 ); double length = p0.EuclideanDistanceTo( p1 ); this->SetQuantity( FEATURE_ID_LENGTH, length ); } void mitk::PlanarArrow::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); } void mitk::PlanarArrow::SetArrowTipScaleFactor( float scale ) { m_ArrowTipScaleFactor = scale; } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarCross.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarCross.cpp index 98045a3501..be15c91a58 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarCross.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarCross.cpp @@ -1,344 +1,344 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlanarCross.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" mitk::PlanarCross::PlanarCross() : FEATURE_ID_LONGESTDIAMETER( this->AddFeature( "Longest Axis", "mm" ) ), FEATURE_ID_SHORTAXISDIAMETER( this->AddFeature( "Short Axis", "mm" ) ) { // Cross has two control points at the beginning this->ResetNumberOfControlPoints( 2 ); // Create property for SingleLineMode (default: false) this->SetProperty( "SingleLineMode", mitk::BoolProperty::New( false ) ); // Create helper polyline object (for drawing the orthogonal orientation line) this->SetNumberOfHelperPolyLines( 1 ); m_HelperPolyLinesToBePainted->InsertElement( 0, false ); } mitk::PlanarCross::~PlanarCross() { } void mitk::PlanarCross::SetSingleLineMode( bool singleLineMode ) { this->SetProperty( "SingleLineMode", mitk::BoolProperty::New( singleLineMode ) ); this->Modified(); } bool mitk::PlanarCross::GetSingleLineMode() const { mitk::BoolProperty* singleLineMode = dynamic_cast< mitk::BoolProperty* >( this->GetProperty( "SingleLineMode" ).GetPointer() ); if ( singleLineMode != NULL ) { return singleLineMode->GetValue(); } return false; } bool mitk::PlanarCross::ResetOnPointSelect() { if ( this->GetSingleLineMode() ) { // In single line mode --> nothing to reset return false; } switch ( m_SelectedControlPoint ) { default: // Nothing selected --> nothing to reset return false; case 0: { // Control point 0 selected: exchange points 0 and 1 Point2D tmpPoint = this->GetControlPoint( 0 ); this->SetControlPoint( 0, this->GetControlPoint( 1 ) ); this->SetControlPoint( 1, tmpPoint ); // FALLS THROUGH! } case 1: { // Control point 0 or 1 selected: reset number of control points to two this->ResetNumberOfControlPoints( 2 ); this->SelectControlPoint( 1 ); return true; } case 2: { // Control point 2 selected: replace point 0 with point 3 and point 1 with point 2 this->SetControlPoint( 0, this->GetControlPoint( 3 ) ); this->SetControlPoint( 1, this->GetControlPoint( 2 ) ); // Adjust selected control point, reset number of control points to two this->ResetNumberOfControlPoints( 2 ); this->SelectControlPoint( 1 ); return true; } case 3: { // Control point 3 selected: replace point 0 with point 2 and point 1 with point 3 this->SetControlPoint( 0, this->GetControlPoint( 2 ) ); this->SetControlPoint( 1, this->GetControlPoint( 3 ) ); // Adjust selected control point, reset number of control points to two this->ResetNumberOfControlPoints( 2 ); this->SelectControlPoint( 1 ); return true; } } } unsigned int mitk::PlanarCross::GetNumberOfFeatures() const { if ( this->GetSingleLineMode() || (this->GetNumberOfControlPoints() < 4) ) { return 1; } else { return 2; } } mitk::Point2D mitk::PlanarCross::ApplyControlPointConstraints( unsigned int index, const Point2D& point ) { // Apply spatial constraints from superclass and from this class until the resulting constrained // point converges. Although not an optimal implementation, this iterative approach // helps to respect both constraints from the superclass and from this class. Without this, // situations may occur where control points are constrained by the superclass, but again // moved out of the superclass bounds by the subclass, or vice versa. unsigned int count = 0; // ensures stop of approach if point does not converge in reasonable time Point2D confinedPoint = point; Point2D superclassConfinedPoint; do { superclassConfinedPoint = Superclass::ApplyControlPointConstraints( index, confinedPoint ); confinedPoint = this->InternalApplyControlPointConstraints( index, superclassConfinedPoint ); ++count; } while ( (confinedPoint.EuclideanDistanceTo( superclassConfinedPoint ) > mitk::eps) && (count < 32) ); return confinedPoint; } mitk::Point2D mitk::PlanarCross::InternalApplyControlPointConstraints( unsigned int index, const Point2D& point ) { // Apply constraints depending on current interaction state switch ( index ) { case 2: { // Check if 3rd control point is outside of the range (2D area) defined by the first // line (via the first two control points); if it is outside, clip it to the bounds const Point2D p1 = this->GetControlPoint( 0 ); const Point2D p2 = this->GetControlPoint( 1 ); Vector2D n1 = p2 - p1; n1.Normalize(); Vector2D v1 = point - p1; double dotProduct = n1 * v1; Point2D crossPoint = p1 + n1 * dotProduct;; Vector2D crossVector = point - crossPoint; if ( dotProduct < 0.0 ) { // Out-of-bounds on the left: clip point to left boundary return (p1 + crossVector); } else if ( dotProduct > p2.EuclideanDistanceTo( p1 ) ) { // Out-of-bounds on the right: clip point to right boundary return (p2 + crossVector); } else { // Pass back original point return point; } } case 3: { // Constrain 4th control point so that with the 3rd control point it forms // a line orthogonal to the first line (constraint 1); the 4th control point // must lie on the opposite side of the line defined by the first two control // points than the 3rd control point (constraint 2) const Point2D p1 = this->GetControlPoint( 0 ); const Point2D p2 = this->GetControlPoint( 1 ); const Point2D p3 = this->GetControlPoint( 2 ); // Calculate distance of original point from orthogonal line the corrected // point should lie on to project the point onto this line Vector2D n1 = p2 - p1; n1.Normalize(); Vector2D v1 = point - p3; double dotProduct1 = n1 * v1; Point2D pointOnLine = point - n1 * dotProduct1; // Project new point onto line [p1, p2] Vector2D v2 = pointOnLine - p1; double dotProduct2 = n1 * v2; Point2D crossingPoint = p1 + n1 * dotProduct2; // Determine whether the projected point on the line, or the crossing point should be // used (according to the second constraint in the comment above) if ( (pointOnLine.SquaredEuclideanDistanceTo( p3 ) > crossingPoint.SquaredEuclideanDistanceTo( p3 )) && (pointOnLine.SquaredEuclideanDistanceTo( p3 ) > pointOnLine.SquaredEuclideanDistanceTo( crossingPoint )) ) { return pointOnLine; } else { return crossingPoint; } } default: return point; } } void mitk::PlanarCross::GeneratePolyLine() { this->SetNumberOfPolyLines(1); this->ClearPolyLines(); if (this->GetNumberOfControlPoints() > 2) this->SetNumberOfPolyLines( 2 ); for (unsigned int i = 0; i < this->GetNumberOfControlPoints(); ++i) { if (i < 2) this->AppendPointToPolyLine(0, this->GetControlPoint(i)); if (i > 1) this->AppendPointToPolyLine(1, this->GetControlPoint(i)); } } void mitk::PlanarCross::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/) { // Generate helper polyline (orientation line orthogonal to first line) // if the third control point is currently being set if ( this->GetNumberOfControlPoints() != 3 ) { m_HelperPolyLinesToBePainted->SetElement( 0, false ); return; } m_HelperPolyLinesToBePainted->SetElement( 0, true ); this->ClearHelperPolyLines(); // Calculate cross point of first line (p1 to p2) and orthogonal line through // the third control point (p3) const Point2D p1 = this->GetControlPoint( 0 ); const Point2D p2 = this->GetControlPoint( 1 ); const Point2D p3 = this->GetControlPoint( 2 ); Vector2D n1 = p2 - p1; n1.Normalize(); Vector2D v1 = p3 - p1; Point2D crossPoint = p1 + n1 * (n1 * v1); Vector2D v2 = crossPoint - p3; if ( v2.GetNorm() < 1.0 ) { // If third point is on the first line, draw orthogonal "infinite" line // through cross point on line Vector2D v0; v0[0] = n1[1]; v0[1] = -n1[0]; - this->AppendPointToHelperPolyLine(0, p3 - v0 * 10000.0); - this->AppendPointToHelperPolyLine(0, p3 + v0 * 10000.0); + this->AppendPointToHelperPolyLine(0, Point2D(p3 - v0 * 10000.0)); + this->AppendPointToHelperPolyLine(0, Point2D(p3 + v0 * 10000.0)); } else { // Else, draw orthogonal line starting from third point and crossing the // first line, open-ended only on the other side this->AppendPointToHelperPolyLine(0, p3); - this->AppendPointToHelperPolyLine(0, p3 + v2 * 10000.0); + this->AppendPointToHelperPolyLine(0, Point2D(p3 + v2 * 10000.0)); } } void mitk::PlanarCross::EvaluateFeaturesInternal() { // Calculate length of first line const Point3D &p0 = this->GetWorldControlPoint( 0 ); const Point3D &p1 = this->GetWorldControlPoint( 1 ); double l1 = p0.EuclideanDistanceTo( p1 ); // Calculate length of second line double l2 = 0.0; if ( !this->GetSingleLineMode() && (this->GetNumberOfControlPoints() > 3) ) { const Point3D &p2 = this->GetWorldControlPoint( 2 ); const Point3D &p3 = this->GetWorldControlPoint( 3 ); l2 = p2.EuclideanDistanceTo( p3 ); } double longestDiameter; double shortAxisDiameter; if ( l1 > l2 ) { longestDiameter = l1; shortAxisDiameter = l2; } else { longestDiameter = l2; shortAxisDiameter = l1; } this->SetQuantity( FEATURE_ID_LONGESTDIAMETER, longestDiameter ); this->SetQuantity( FEATURE_ID_SHORTAXISDIAMETER, shortAxisDiameter ); } void mitk::PlanarCross::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); } diff --git a/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.h b/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.h index f96489301a..6a54d75fbe 100644 --- a/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.h +++ b/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.h @@ -1,205 +1,205 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPLANARFIGUREINTERACTOR_H_HEADER_INCLUDED #define MITKPLANARFIGUREINTERACTOR_H_HEADER_INCLUDED #include #include "mitkCommon.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkDataInteractor.h" #pragma GCC visibility push(default) #include #pragma GCC visibility pop namespace mitk { class DataNode; class PlaneGeometry; class DisplayGeometry; class PlanarFigure; class PositionEvent; class BaseRenderer; class InteractionPositionEvent; class StateMachineAction; #pragma GCC visibility push(default) // Define events for PlanarFigure interaction notifications itkEventMacro( PlanarFigureEvent, itk::AnyEvent ); itkEventMacro( StartPlacementPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( EndPlacementPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( SelectPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( StartInteractionPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( EndInteractionPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( StartHoverPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( EndHoverPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( ContextMenuPlanarFigureEvent, PlanarFigureEvent ); #pragma GCC visibility pop /** * \brief Interaction with mitk::PlanarFigure objects via control-points * * \ingroup Interaction */ class MitkPlanarFigure_EXPORT PlanarFigureInteractor : public DataInteractor { public: mitkClassMacro(PlanarFigureInteractor, DataInteractor); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** \brief Sets the amount of precision */ void SetPrecision( ScalarType precision ); /** \brief Sets the minimal distance between two control points. */ void SetMinimumPointDistance( ScalarType minimumDistance ); protected: PlanarFigureInteractor(); virtual ~PlanarFigureInteractor(); virtual void ConnectActionsAndFunctions(); //////// Conditions //////// bool CheckFigurePlaced( const InteractionEvent* interactionEvent ); bool CheckFigureHovering( const InteractionEvent* interactionEvent ); bool CheckControlPointHovering( const InteractionEvent* interactionEvent ); bool CheckSelection( const InteractionEvent* interactionEvent ); bool CheckPointValidity( const InteractionEvent* interactionEvent ); bool CheckFigureFinished( const InteractionEvent* interactionEvent ); bool CheckResetOnPointSelect( const InteractionEvent* interactionEvent ); bool CheckFigureOnRenderingGeometry( const InteractionEvent* interactionEvent ); bool CheckMinimalFigureFinished( const InteractionEvent* interactionEvent ); bool CheckFigureIsExtendable( const InteractionEvent* interactionEvent ); //////// Actions //////// bool FinalizeFigure( StateMachineAction*, InteractionEvent* interactionEvent ); bool MoveCurrentPoint(StateMachineAction*, InteractionEvent* interactionEvent); bool DeselectPoint(StateMachineAction*, InteractionEvent* interactionEvent); bool AddPoint(StateMachineAction*, InteractionEvent* interactionEvent); bool AddInitialPoint(StateMachineAction*, InteractionEvent* interactionEvent); bool StartHovering( StateMachineAction*, InteractionEvent* interactionEvent ); bool EndHovering( StateMachineAction*, InteractionEvent* interactionEvent ); bool SetPreviewPointPosition( StateMachineAction*, InteractionEvent* interactionEvent ); bool HidePreviewPoint( StateMachineAction*, InteractionEvent* interactionEvent ); bool HideControlPoints( StateMachineAction*, InteractionEvent* interactionEvent ); bool RemoveSelectedPoint(StateMachineAction*, InteractionEvent* interactionEvent); bool RequestContextMenu(StateMachineAction*, InteractionEvent* interactionEvent); bool SelectFigure( StateMachineAction*, InteractionEvent* interactionEvent ); bool SelectPoint( StateMachineAction*, InteractionEvent* interactionEvent ); bool EndInteraction( StateMachineAction*, InteractionEvent* interactionEvent ); /** \brief Used when clicking to determine if a point is too close to the previous point. */ bool IsMousePositionAcceptableAsNewControlPoint( const mitk::InteractionPositionEvent* positionEvent, const PlanarFigure* ); bool TransformPositionEventToPoint2D( const InteractionPositionEvent* positionEvent, const PlaneGeometry *planarFigureGeometry, Point2D &point2D ); bool TransformObjectToDisplay( const mitk::Point2D &point2D, mitk::Point2D &displayPoint, const mitk::PlaneGeometry *objectGeometry, const mitk::PlaneGeometry *rendererGeometry, const mitk::DisplayGeometry *displayGeometry ) const; /** \brief Returns true if the first specified point is in proximity of the line defined * the other two point; false otherwise. * * Proximity is defined as the rectangle around the line with pre-defined distance * from the line. */ bool IsPointNearLine( const mitk::Point2D& point, const mitk::Point2D& startPoint, const mitk::Point2D& endPoint, mitk::Point2D& projectedPoint ) const; /** \brief Returns true if the point contained in the passed event (in display coordinates) * is over the planar figure (with a pre-defined tolerance range); false otherwise. */ int IsPositionOverFigure( const InteractionPositionEvent* positionEvent, PlanarFigure *planarFigure, const PlaneGeometry *planarFigureGeometry, const PlaneGeometry *rendererGeometry, const DisplayGeometry *displayGeometry, Point2D& pointProjectedOntoLine) const; /** \brief Returns the index of the marker (control point) over which the point contained * in the passed event (in display coordinates) currently is; -1 if the point is not over * a marker. */ int IsPositionInsideMarker( const InteractionPositionEvent* positionEvent, const PlanarFigure *planarFigure, const PlaneGeometry *planarFigureGeometry, const PlaneGeometry *rendererGeometry, const DisplayGeometry *displayGeometry ) const; void LogPrintPlanarFigureQuantities( const PlanarFigure *planarFigure ); virtual void ConfigurationChanged(); private: /** \brief to store the value of precision to pick a point */ ScalarType m_Precision; /** \brief Store the minimal distance between two control points. */ ScalarType m_MinimumPointDistance; /** \brief True if the mouse is currently hovering over the image. */ bool m_IsHovering; bool m_LastPointWasValid; //mitk::PlanarFigure::Pointer m_PlanarFigure; }; } #endif // MITKPLANARFIGUREINTERACTOR_H_HEADER_INCLUDED diff --git a/Modules/Python/Testing/mitkCopyToPythonAsItkImageTest.cpp b/Modules/Python/Testing/mitkCopyToPythonAsItkImageTest.cpp index 956aba53c4..c9d0e5cd32 100644 --- a/Modules/Python/Testing/mitkCopyToPythonAsItkImageTest.cpp +++ b/Modules/Python/Testing/mitkCopyToPythonAsItkImageTest.cpp @@ -1,72 +1,72 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include int mitkCopyToPythonAsItkImageTest(int /*argc*/, char* argv[]) { MITK_TEST_BEGIN("mitkCopyToPythonAsItkImageTest") //get the context of the python module us::Module* module = us::ModuleRegistry::GetModule("MitkPython"); us::ModuleContext* context = module->GetModuleContext(); //get the service which is generated in the PythonModuleActivator us::ServiceReference serviceRef = context->GetServiceReference(); mitk::PythonService* pythonService = dynamic_cast( context->GetService(serviceRef) ); MITK_TEST_CONDITION(pythonService->IsItkPythonWrappingAvailable() == true, "Is Python available?"); mitk::Image::Pointer testImage = mitk::IOUtil::LoadImage(std::string(argv[1])); //give it a name in python std::string nameOfImageInPython("mitkImage"); MITK_TEST_CONDITION( pythonService->CopyToPythonAsItkImage( testImage, nameOfImageInPython) == true, "Valid image copied to python import should return true."); mitk::Image::Pointer pythonImage = pythonService->CopyItkImageFromPython(nameOfImageInPython); - mitk::Index3D index; + itk::Index<3> index; index[0] = 128; index[1] = 128; index[2] = 24; try{ // pic3D of type char mitk::ImagePixelReadAccessor pythonImageAccesor(pythonImage); //TODO Use the assert comparison methods once we have them implemented and remove GetPixelValueByIndex MITK_TEST_CONDITION( pythonImageAccesor.GetDimension(0) == 256, "Is the 1st dimension of Pic3D still 256?"); MITK_TEST_CONDITION( pythonImageAccesor.GetDimension(1) == 256, "Is the 2nd dimension of Pic3D still 256?"); MITK_TEST_CONDITION( pythonImageAccesor.GetDimension(2) == 49, "Is the 3rd dimension of Pic3D still 49?"); MITK_TEST_CONDITION( pythonImageAccesor.GetPixelByIndex(index) == 96, "Is the value of Pic3D at (128,128,24) still 96?"); }catch(...) { MITK_TEST_CONDITION( false, "Image is not readable! "); } MITK_TEST_END() } diff --git a/Modules/QtWidgets/QmitkStdMultiWidget.cpp b/Modules/QtWidgets/QmitkStdMultiWidget.cpp index c2c9b8b265..69f32ffe47 100644 --- a/Modules/QtWidgets/QmitkStdMultiWidget.cpp +++ b/Modules/QtWidgets/QmitkStdMultiWidget.cpp @@ -1,2200 +1,2200 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #define SMW_INFO MITK_INFO("widget.stdmulti") #include "QmitkStdMultiWidget.h" #include #include #include #include #include #include #include #include #include "mitkProperties.h" #include "mitkPlaneGeometryDataMapper2D.h" #include "mitkGlobalInteraction.h" #include "mitkDisplayInteractor.h" #include "mitkPointSet.h" #include "mitkPositionEvent.h" #include "mitkStateEvent.h" #include "mitkLine.h" #include "mitkInteractionConst.h" #include "mitkDataStorage.h" #include "mitkOverlayManager.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateProperty.h" #include "mitkStatusBar.h" #include "mitkImage.h" #include "mitkVtkLayerController.h" #include QmitkStdMultiWidget::QmitkStdMultiWidget(QWidget* parent, Qt::WindowFlags f, mitk::RenderingManager* renderingManager, mitk::BaseRenderer::RenderingMode::Type renderingMode) : QWidget(parent, f), mitkWidget1(NULL), mitkWidget2(NULL), mitkWidget3(NULL), mitkWidget4(NULL), levelWindowWidget(NULL), QmitkStdMultiWidgetLayout(NULL), m_Layout(LAYOUT_DEFAULT), m_PlaneMode(PLANE_MODE_SLICING), m_RenderingManager(renderingManager), m_GradientBackgroundFlag(true), m_TimeNavigationController(NULL), m_MainSplit(NULL), m_LayoutSplit(NULL), m_SubSplit1(NULL), m_SubSplit2(NULL), mitkWidget1Container(NULL), mitkWidget2Container(NULL), mitkWidget3Container(NULL), mitkWidget4Container(NULL), m_PendingCrosshairPositionEvent(false), m_CrosshairNavigationEnabled(false) { /****************************************************** * Use the global RenderingManager if none was specified * ****************************************************/ if (m_RenderingManager == NULL) { m_RenderingManager = mitk::RenderingManager::GetInstance(); } m_TimeNavigationController = m_RenderingManager->GetTimeNavigationController(); /*******************************/ //Create Widget manually /*******************************/ //create Layouts QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); QmitkStdMultiWidgetLayout->setContentsMargins(0,0,0,0); //Set Layout to widget this->setLayout(QmitkStdMultiWidgetLayout); // QmitkNavigationToolBar* toolBar = new QmitkNavigationToolBar(); // QmitkStdMultiWidgetLayout->addWidget( toolBar ); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter( Qt::Vertical, m_MainSplit ); m_MainSplit->addWidget( m_LayoutSplit ); //create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter( m_LayoutSplit ); m_SubSplit2 = new QSplitter( m_LayoutSplit ); //creae Widget Container mitkWidget1Container = new QWidget(m_SubSplit1); mitkWidget2Container = new QWidget(m_SubSplit1); mitkWidget3Container = new QWidget(m_SubSplit2); mitkWidget4Container = new QWidget(m_SubSplit2); mitkWidget1Container->setContentsMargins(0,0,0,0); mitkWidget2Container->setContentsMargins(0,0,0,0); mitkWidget3Container->setContentsMargins(0,0,0,0); mitkWidget4Container->setContentsMargins(0,0,0,0); //create Widget Layout QHBoxLayout *mitkWidgetLayout1 = new QHBoxLayout(mitkWidget1Container); QHBoxLayout *mitkWidgetLayout2 = new QHBoxLayout(mitkWidget2Container); QHBoxLayout *mitkWidgetLayout3 = new QHBoxLayout(mitkWidget3Container); QHBoxLayout *mitkWidgetLayout4 = new QHBoxLayout(mitkWidget4Container); mitkWidgetLayout1->setMargin(0); mitkWidgetLayout2->setMargin(0); mitkWidgetLayout3->setMargin(0); mitkWidgetLayout4->setMargin(0); //set Layout to Widget Container mitkWidget1Container->setLayout(mitkWidgetLayout1); mitkWidget2Container->setLayout(mitkWidgetLayout2); mitkWidget3Container->setLayout(mitkWidgetLayout3); mitkWidget4Container->setLayout(mitkWidgetLayout4); //set SizePolicy mitkWidget1Container->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); mitkWidget2Container->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); mitkWidget3Container->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); mitkWidget4Container->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); //insert Widget Container into the splitters m_SubSplit1->addWidget( mitkWidget1Container ); m_SubSplit1->addWidget( mitkWidget2Container ); m_SubSplit2->addWidget( mitkWidget3Container ); m_SubSplit2->addWidget( mitkWidget4Container ); // m_RenderingManager->SetGlobalInteraction( mitk::GlobalInteraction::GetInstance() ); //Create RenderWindows 1 mitkWidget1 = new QmitkRenderWindow(mitkWidget1Container, "stdmulti.widget1", NULL, m_RenderingManager,renderingMode); mitkWidget1->setMaximumSize(2000,2000); mitkWidget1->SetLayoutIndex( AXIAL ); mitkWidgetLayout1->addWidget(mitkWidget1); //Create RenderWindows 2 mitkWidget2 = new QmitkRenderWindow(mitkWidget2Container, "stdmulti.widget2", NULL, m_RenderingManager,renderingMode); mitkWidget2->setMaximumSize(2000,2000); mitkWidget2->setEnabled( TRUE ); mitkWidget2->SetLayoutIndex( SAGITTAL ); mitkWidgetLayout2->addWidget(mitkWidget2); //Create RenderWindows 3 mitkWidget3 = new QmitkRenderWindow(mitkWidget3Container, "stdmulti.widget3", NULL, m_RenderingManager,renderingMode); mitkWidget3->setMaximumSize(2000,2000); mitkWidget3->SetLayoutIndex( CORONAL ); mitkWidgetLayout3->addWidget(mitkWidget3); //Create RenderWindows 4 mitkWidget4 = new QmitkRenderWindow(mitkWidget4Container, "stdmulti.widget4", NULL, m_RenderingManager,renderingMode); mitkWidget4->setMaximumSize(2000,2000); mitkWidget4->SetLayoutIndex( THREE_D ); mitkWidgetLayout4->addWidget(mitkWidget4); //create SignalSlot Connection connect( mitkWidget1, SIGNAL( SignalLayoutDesignChanged(int) ), this, SLOT( OnLayoutDesignChanged(int) ) ); connect( mitkWidget1, SIGNAL( ResetView() ), this, SLOT( ResetCrosshair() ) ); connect( mitkWidget1, SIGNAL( ChangeCrosshairRotationMode(int) ), this, SLOT( SetWidgetPlaneMode(int) ) ); connect( this, SIGNAL(WidgetNotifyNewCrossHairMode(int)), mitkWidget1, SLOT(OnWidgetPlaneModeChanged(int)) ); connect( mitkWidget2, SIGNAL( SignalLayoutDesignChanged(int) ), this, SLOT( OnLayoutDesignChanged(int) ) ); connect( mitkWidget2, SIGNAL( ResetView() ), this, SLOT( ResetCrosshair() ) ); connect( mitkWidget2, SIGNAL( ChangeCrosshairRotationMode(int) ), this, SLOT( SetWidgetPlaneMode(int) ) ); connect( this, SIGNAL(WidgetNotifyNewCrossHairMode(int)), mitkWidget2, SLOT(OnWidgetPlaneModeChanged(int)) ); connect( mitkWidget3, SIGNAL( SignalLayoutDesignChanged(int) ), this, SLOT( OnLayoutDesignChanged(int) ) ); connect( mitkWidget3, SIGNAL( ResetView() ), this, SLOT( ResetCrosshair() ) ); connect( mitkWidget3, SIGNAL( ChangeCrosshairRotationMode(int) ), this, SLOT( SetWidgetPlaneMode(int) ) ); connect( this, SIGNAL(WidgetNotifyNewCrossHairMode(int)), mitkWidget3, SLOT(OnWidgetPlaneModeChanged(int)) ); connect( mitkWidget4, SIGNAL( SignalLayoutDesignChanged(int) ), this, SLOT( OnLayoutDesignChanged(int) ) ); connect( mitkWidget4, SIGNAL( ResetView() ), this, SLOT( ResetCrosshair() ) ); connect( mitkWidget4, SIGNAL( ChangeCrosshairRotationMode(int) ), this, SLOT( SetWidgetPlaneMode(int) ) ); connect( this, SIGNAL(WidgetNotifyNewCrossHairMode(int)), mitkWidget4, SLOT(OnWidgetPlaneModeChanged(int)) ); //Create Level Window Widget levelWindowWidget = new QmitkLevelWindowWidget( m_MainSplit ); //this levelWindowWidget->setObjectName(QString::fromUtf8("levelWindowWidget")); QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(levelWindowWidget->sizePolicy().hasHeightForWidth()); levelWindowWidget->setSizePolicy(sizePolicy); levelWindowWidget->setMaximumSize(QSize(50, 2000)); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //show mainSplitt and add to Layout m_MainSplit->show(); //resize Image. this->resize( QSize(364, 477).expandedTo(minimumSizeHint()) ); //Initialize the widgets. this->InitializeWidget(); //Activate Widget Menu this->ActivateMenuWidget( true ); } void QmitkStdMultiWidget::InitializeWidget() { m_PositionTracker = NULL; // transfer colors in WorldGeometry-Nodes of the associated Renderer QColor qcolor; //float color[3] = {1.0f,1.0f,1.0f}; mitk::DataNode::Pointer planeNode; mitk::IntProperty::Pointer layer; // of widget 1 planeNode = mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->GetCurrentWorldPlaneGeometryNode(); planeNode->SetColor(1.0,0.0,0.0); layer = mitk::IntProperty::New(1000); planeNode->SetProperty("layer",layer); // ... of widget 2 planeNode = mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow())->GetCurrentWorldPlaneGeometryNode(); planeNode->SetColor(0.0,1.0,0.0); layer = mitk::IntProperty::New(1000); planeNode->SetProperty("layer",layer); // ... of widget 3 planeNode = mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow())->GetCurrentWorldPlaneGeometryNode(); planeNode->SetColor(0.0,0.0,1.0); layer = mitk::IntProperty::New(1000); planeNode->SetProperty("layer",layer); // ... of widget 4 planeNode = mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->GetCurrentWorldPlaneGeometryNode(); planeNode->SetColor(1.0,1.0,0.0); layer = mitk::IntProperty::New(1000); planeNode->SetProperty("layer",layer); mitk::OverlayManager::Pointer OverlayManager = mitk::OverlayManager::New(); mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->SetOverlayManager(OverlayManager); mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow())->SetOverlayManager(OverlayManager); mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow())->SetOverlayManager(OverlayManager); mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->SetOverlayManager(OverlayManager); mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->SetMapperID(mitk::BaseRenderer::Standard3D); // Set plane mode (slicing/rotation behavior) to slicing (default) m_PlaneMode = PLANE_MODE_SLICING; // Set default view directions for SNCs mitkWidget1->GetSliceNavigationController()->SetDefaultViewDirection( mitk::SliceNavigationController::Axial ); mitkWidget2->GetSliceNavigationController()->SetDefaultViewDirection( mitk::SliceNavigationController::Sagittal ); mitkWidget3->GetSliceNavigationController()->SetDefaultViewDirection( mitk::SliceNavigationController::Frontal ); mitkWidget4->GetSliceNavigationController()->SetDefaultViewDirection( mitk::SliceNavigationController::Original ); /*************************************************/ //Write Layout Names into the viewers -- hardCoded //Info for later: //int view = this->GetRenderWindow1()->GetSliceNavigationController()->GetDefaultViewDirection(); //QString layoutName; //if( view == mitk::SliceNavigationController::Axial ) // layoutName = "Axial"; //else if( view == mitk::SliceNavigationController::Sagittal ) // layoutName = "Sagittal"; //else if( view == mitk::SliceNavigationController::Frontal ) // layoutName = "Coronal"; //else if( view == mitk::SliceNavigationController::Original ) // layoutName = "Original"; //if( view >= 0 && view < 4 ) // //write LayoutName --> Viewer 3D shoudn't write the layoutName. //Render Window 1 == axial m_CornerAnnotaions[0].cornerText = vtkCornerAnnotation::New(); m_CornerAnnotaions[0].cornerText->SetText(0, "Axial"); m_CornerAnnotaions[0].cornerText->SetMaximumFontSize(12); m_CornerAnnotaions[0].textProp = vtkTextProperty::New(); m_CornerAnnotaions[0].textProp->SetColor( 1.0, 0.0, 0.0 ); m_CornerAnnotaions[0].cornerText->SetTextProperty( m_CornerAnnotaions[0].textProp ); m_CornerAnnotaions[0].ren = vtkRenderer::New(); m_CornerAnnotaions[0].ren->AddActor(m_CornerAnnotaions[0].cornerText); m_CornerAnnotaions[0].ren->InteractiveOff(); mitk::VtkLayerController::GetInstance(this->GetRenderWindow1()->GetRenderWindow())->InsertForegroundRenderer(m_CornerAnnotaions[0].ren,true); //Render Window 2 == sagittal m_CornerAnnotaions[1].cornerText = vtkCornerAnnotation::New(); m_CornerAnnotaions[1].cornerText->SetText(0, "Sagittal"); m_CornerAnnotaions[1].cornerText->SetMaximumFontSize(12); m_CornerAnnotaions[1].textProp = vtkTextProperty::New(); m_CornerAnnotaions[1].textProp->SetColor( 0.0, 1.0, 0.0 ); m_CornerAnnotaions[1].cornerText->SetTextProperty( m_CornerAnnotaions[1].textProp ); m_CornerAnnotaions[1].ren = vtkRenderer::New(); m_CornerAnnotaions[1].ren->AddActor(m_CornerAnnotaions[1].cornerText); m_CornerAnnotaions[1].ren->InteractiveOff(); mitk::VtkLayerController::GetInstance(this->GetRenderWindow2()->GetRenderWindow())->InsertForegroundRenderer(m_CornerAnnotaions[1].ren,true); //Render Window 3 == coronal m_CornerAnnotaions[2].cornerText = vtkCornerAnnotation::New(); m_CornerAnnotaions[2].cornerText->SetText(0, "Coronal"); m_CornerAnnotaions[2].cornerText->SetMaximumFontSize(12); m_CornerAnnotaions[2].textProp = vtkTextProperty::New(); m_CornerAnnotaions[2].textProp->SetColor( 0.295, 0.295, 1.0 ); m_CornerAnnotaions[2].cornerText->SetTextProperty( m_CornerAnnotaions[2].textProp ); m_CornerAnnotaions[2].ren = vtkRenderer::New(); m_CornerAnnotaions[2].ren->AddActor(m_CornerAnnotaions[2].cornerText); m_CornerAnnotaions[2].ren->InteractiveOff(); mitk::VtkLayerController::GetInstance(this->GetRenderWindow3()->GetRenderWindow())->InsertForegroundRenderer(m_CornerAnnotaions[2].ren,true); /*************************************************/ // create a slice rotator // m_SlicesRotator = mitk::SlicesRotator::New(); // @TODO next line causes sure memory leak // rotator will be created nonetheless (will be switched on and off) m_SlicesRotator = mitk::SlicesRotator::New("slices-rotator"); m_SlicesRotator->AddSliceController( mitkWidget1->GetSliceNavigationController() ); m_SlicesRotator->AddSliceController( mitkWidget2->GetSliceNavigationController() ); m_SlicesRotator->AddSliceController( mitkWidget3->GetSliceNavigationController() ); // create a slice swiveller (using the same state-machine as SlicesRotator) m_SlicesSwiveller = mitk::SlicesSwiveller::New("slices-rotator"); m_SlicesSwiveller->AddSliceController( mitkWidget1->GetSliceNavigationController() ); m_SlicesSwiveller->AddSliceController( mitkWidget2->GetSliceNavigationController() ); m_SlicesSwiveller->AddSliceController( mitkWidget3->GetSliceNavigationController() ); //connect to the "time navigation controller": send time via sliceNavigationControllers m_TimeNavigationController->ConnectGeometryTimeEvent( mitkWidget1->GetSliceNavigationController() , false); m_TimeNavigationController->ConnectGeometryTimeEvent( mitkWidget2->GetSliceNavigationController() , false); m_TimeNavigationController->ConnectGeometryTimeEvent( mitkWidget3->GetSliceNavigationController() , false); m_TimeNavigationController->ConnectGeometryTimeEvent( mitkWidget4->GetSliceNavigationController() , false); mitkWidget1->GetSliceNavigationController() ->ConnectGeometrySendEvent(mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())); //reverse connection between sliceNavigationControllers and m_TimeNavigationController mitkWidget1->GetSliceNavigationController() ->ConnectGeometryTimeEvent(m_TimeNavigationController, false); mitkWidget2->GetSliceNavigationController() ->ConnectGeometryTimeEvent(m_TimeNavigationController, false); mitkWidget3->GetSliceNavigationController() ->ConnectGeometryTimeEvent(m_TimeNavigationController, false); mitkWidget4->GetSliceNavigationController() ->ConnectGeometryTimeEvent(m_TimeNavigationController, false); m_MouseModeSwitcher = mitk::MouseModeSwitcher::New(); m_LastLeftClickPositionSupplier = mitk::CoordinateSupplier::New("navigation", NULL); mitk::GlobalInteraction::GetInstance()->AddListener( m_LastLeftClickPositionSupplier ); // setup gradient background m_GradientBackground1 = mitk::GradientBackground::New(); m_GradientBackground1->SetRenderWindow( mitkWidget1->GetRenderWindow() ); m_GradientBackground1->Disable(); m_GradientBackground2 = mitk::GradientBackground::New(); m_GradientBackground2->SetRenderWindow( mitkWidget2->GetRenderWindow() ); m_GradientBackground2->Disable(); m_GradientBackground3 = mitk::GradientBackground::New(); m_GradientBackground3->SetRenderWindow( mitkWidget3->GetRenderWindow() ); m_GradientBackground3->Disable(); m_GradientBackground4 = mitk::GradientBackground::New(); m_GradientBackground4->SetRenderWindow( mitkWidget4->GetRenderWindow() ); m_GradientBackground4->SetGradientColors(0.1,0.1,0.1,0.5,0.5,0.5); m_GradientBackground4->Enable(); // setup the department logo rendering m_LogoRendering1 = mitk::ManufacturerLogo::New(); m_LogoRendering1->SetRenderWindow( mitkWidget1->GetRenderWindow() ); m_LogoRendering1->Disable(); m_LogoRendering2 = mitk::ManufacturerLogo::New(); m_LogoRendering2->SetRenderWindow( mitkWidget2->GetRenderWindow() ); m_LogoRendering2->Disable(); m_LogoRendering3 = mitk::ManufacturerLogo::New(); m_LogoRendering3->SetRenderWindow( mitkWidget3->GetRenderWindow() ); m_LogoRendering3->Disable(); m_LogoRendering4 = mitk::ManufacturerLogo::New(); m_LogoRendering4->SetRenderWindow( mitkWidget4->GetRenderWindow() ); m_LogoRendering4->Enable(); m_RectangleRendering1 = mitk::RenderWindowFrame::New(); m_RectangleRendering1->SetRenderWindow( mitkWidget1->GetRenderWindow() ); m_RectangleRendering1->Enable(1.0,0.0,0.0); m_RectangleRendering2 = mitk::RenderWindowFrame::New(); m_RectangleRendering2->SetRenderWindow( mitkWidget2->GetRenderWindow() ); m_RectangleRendering2->Enable(0.0,1.0,0.0); m_RectangleRendering3 = mitk::RenderWindowFrame::New(); m_RectangleRendering3->SetRenderWindow( mitkWidget3->GetRenderWindow() ); m_RectangleRendering3->Enable(0.0,0.0,1.0); m_RectangleRendering4 = mitk::RenderWindowFrame::New(); m_RectangleRendering4->SetRenderWindow( mitkWidget4->GetRenderWindow() ); m_RectangleRendering4->Enable(1.0,1.0,0.0); } QmitkStdMultiWidget::~QmitkStdMultiWidget() { DisablePositionTracking(); DisableNavigationControllerEventListening(); m_TimeNavigationController->Disconnect(mitkWidget1->GetSliceNavigationController()); m_TimeNavigationController->Disconnect(mitkWidget2->GetSliceNavigationController()); m_TimeNavigationController->Disconnect(mitkWidget3->GetSliceNavigationController()); m_TimeNavigationController->Disconnect(mitkWidget4->GetSliceNavigationController()); mitk::VtkLayerController::GetInstance(this->GetRenderWindow1()->GetRenderWindow())->RemoveRenderer( m_CornerAnnotaions[0].ren ); mitk::VtkLayerController::GetInstance(this->GetRenderWindow2()->GetRenderWindow())->RemoveRenderer( m_CornerAnnotaions[1].ren ); mitk::VtkLayerController::GetInstance(this->GetRenderWindow3()->GetRenderWindow())->RemoveRenderer( m_CornerAnnotaions[2].ren ); //Delete CornerAnnotation m_CornerAnnotaions[0].cornerText->Delete(); m_CornerAnnotaions[0].textProp->Delete(); m_CornerAnnotaions[0].ren->Delete(); m_CornerAnnotaions[1].cornerText->Delete(); m_CornerAnnotaions[1].textProp->Delete(); m_CornerAnnotaions[1].ren->Delete(); m_CornerAnnotaions[2].cornerText->Delete(); m_CornerAnnotaions[2].textProp->Delete(); m_CornerAnnotaions[2].ren->Delete(); } void QmitkStdMultiWidget::RemovePlanesFromDataStorage() { if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_Node.IsNotNull()) { if(m_DataStorage.IsNotNull()) { m_DataStorage->Remove(m_PlaneNode1); m_DataStorage->Remove(m_PlaneNode2); m_DataStorage->Remove(m_PlaneNode3); m_DataStorage->Remove(m_Node); } } } void QmitkStdMultiWidget::AddPlanesToDataStorage() { if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_Node.IsNotNull()) { if (m_DataStorage.IsNotNull()) { m_DataStorage->Add(m_Node); m_DataStorage->Add(m_PlaneNode1, m_Node); m_DataStorage->Add(m_PlaneNode2, m_Node); m_DataStorage->Add(m_PlaneNode3, m_Node); static_cast(m_PlaneNode1->GetMapper(mitk::BaseRenderer::Standard2D))->SetDatastorageAndGeometryBaseNode(m_DataStorage, m_Node); static_cast(m_PlaneNode2->GetMapper(mitk::BaseRenderer::Standard2D))->SetDatastorageAndGeometryBaseNode(m_DataStorage, m_Node); static_cast(m_PlaneNode3->GetMapper(mitk::BaseRenderer::Standard2D))->SetDatastorageAndGeometryBaseNode(m_DataStorage, m_Node); } } } void QmitkStdMultiWidget::changeLayoutTo2DImagesUp() { SMW_INFO << "changing layout to 2D images up... " << std::endl; //Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout ; //create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); //Set Layout to widget this->setLayout(QmitkStdMultiWidgetLayout); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter( Qt::Vertical, m_MainSplit ); m_MainSplit->addWidget( m_LayoutSplit ); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter( m_LayoutSplit ); m_SubSplit2 = new QSplitter( m_LayoutSplit ); //insert Widget Container into splitter top m_SubSplit1->addWidget( mitkWidget1Container ); m_SubSplit1->addWidget( mitkWidget2Container ); m_SubSplit1->addWidget( mitkWidget3Container ); //set SplitterSize for splitter top QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes( splitterSize ); //insert Widget Container into splitter bottom m_SubSplit2->addWidget( mitkWidget4Container ); //set SplitterSize for splitter m_LayoutSplit splitterSize.clear(); splitterSize.push_back(400); splitterSize.push_back(1000); m_LayoutSplit->setSizes( splitterSize ); //show mainSplitt m_MainSplit->show(); //show Widget if hidden if ( mitkWidget1->isHidden() ) mitkWidget1->show(); if ( mitkWidget2->isHidden() ) mitkWidget2->show(); if ( mitkWidget3->isHidden() ) mitkWidget3->show(); if ( mitkWidget4->isHidden() ) mitkWidget4->show(); //Change Layout Name m_Layout = LAYOUT_2D_IMAGES_UP; //update Layout Design List mitkWidget1->LayoutDesignListChanged( LAYOUT_2D_IMAGES_UP ); mitkWidget2->LayoutDesignListChanged( LAYOUT_2D_IMAGES_UP ); mitkWidget3->LayoutDesignListChanged( LAYOUT_2D_IMAGES_UP ); mitkWidget4->LayoutDesignListChanged( LAYOUT_2D_IMAGES_UP ); //update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutTo2DImagesLeft() { SMW_INFO << "changing layout to 2D images left... " << std::endl; //Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout ; //create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter( m_MainSplit ); m_MainSplit->addWidget( m_LayoutSplit ); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter( Qt::Vertical, m_LayoutSplit ); m_SubSplit2 = new QSplitter( m_LayoutSplit ); //insert Widget into the splitters m_SubSplit1->addWidget( mitkWidget1Container ); m_SubSplit1->addWidget( mitkWidget2Container ); m_SubSplit1->addWidget( mitkWidget3Container ); //set splitterSize of SubSplit1 QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes( splitterSize ); m_SubSplit2->addWidget( mitkWidget4Container ); //set splitterSize of Layout Split splitterSize.clear(); splitterSize.push_back(400); splitterSize.push_back(1000); m_LayoutSplit->setSizes( splitterSize ); //show mainSplitt and add to Layout m_MainSplit->show(); //show Widget if hidden if ( mitkWidget1->isHidden() ) mitkWidget1->show(); if ( mitkWidget2->isHidden() ) mitkWidget2->show(); if ( mitkWidget3->isHidden() ) mitkWidget3->show(); if ( mitkWidget4->isHidden() ) mitkWidget4->show(); //update Layout Name m_Layout = LAYOUT_2D_IMAGES_LEFT; //update Layout Design List mitkWidget1->LayoutDesignListChanged( LAYOUT_2D_IMAGES_LEFT ); mitkWidget2->LayoutDesignListChanged( LAYOUT_2D_IMAGES_LEFT ); mitkWidget3->LayoutDesignListChanged( LAYOUT_2D_IMAGES_LEFT ); mitkWidget4->LayoutDesignListChanged( LAYOUT_2D_IMAGES_LEFT ); //update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToDefault() { SMW_INFO << "changing layout to default... " << std::endl; //Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout ; //create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter( Qt::Vertical, m_MainSplit ); m_MainSplit->addWidget( m_LayoutSplit ); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter( m_LayoutSplit ); m_SubSplit2 = new QSplitter( m_LayoutSplit ); //insert Widget container into the splitters m_SubSplit1->addWidget( mitkWidget1Container ); m_SubSplit1->addWidget( mitkWidget2Container ); m_SubSplit2->addWidget( mitkWidget3Container ); m_SubSplit2->addWidget( mitkWidget4Container ); //set splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes( splitterSize ); m_SubSplit2->setSizes( splitterSize ); m_LayoutSplit->setSizes( splitterSize ); //show mainSplitt and add to Layout m_MainSplit->show(); //show Widget if hidden if ( mitkWidget1->isHidden() ) mitkWidget1->show(); if ( mitkWidget2->isHidden() ) mitkWidget2->show(); if ( mitkWidget3->isHidden() ) mitkWidget3->show(); if ( mitkWidget4->isHidden() ) mitkWidget4->show(); m_Layout = LAYOUT_DEFAULT; //update Layout Design List mitkWidget1->LayoutDesignListChanged( LAYOUT_DEFAULT ); mitkWidget2->LayoutDesignListChanged( LAYOUT_DEFAULT ); mitkWidget3->LayoutDesignListChanged( LAYOUT_DEFAULT ); mitkWidget4->LayoutDesignListChanged( LAYOUT_DEFAULT ); //update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToBig3D() { SMW_INFO << "changing layout to big 3D ..." << std::endl; //Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout ; //create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //add widget Splitter to main Splitter m_MainSplit->addWidget( mitkWidget4Container ); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //show mainSplitt and add to Layout m_MainSplit->show(); //show/hide Widgets mitkWidget1->hide(); mitkWidget2->hide(); mitkWidget3->hide(); if ( mitkWidget4->isHidden() ) mitkWidget4->show(); m_Layout = LAYOUT_BIG_3D; //update Layout Design List mitkWidget1->LayoutDesignListChanged( LAYOUT_BIG_3D ); mitkWidget2->LayoutDesignListChanged( LAYOUT_BIG_3D ); mitkWidget3->LayoutDesignListChanged( LAYOUT_BIG_3D ); mitkWidget4->LayoutDesignListChanged( LAYOUT_BIG_3D ); //update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToWidget1() { SMW_INFO << "changing layout to big Widget1 ..." << std::endl; //Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout ; //create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //add widget Splitter to main Splitter m_MainSplit->addWidget( mitkWidget1Container ); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //show mainSplitt and add to Layout m_MainSplit->show(); //show/hide Widgets if ( mitkWidget1->isHidden() ) mitkWidget1->show(); mitkWidget2->hide(); mitkWidget3->hide(); mitkWidget4->hide(); m_Layout = LAYOUT_WIDGET1; //update Layout Design List mitkWidget1->LayoutDesignListChanged( LAYOUT_WIDGET1 ); mitkWidget2->LayoutDesignListChanged( LAYOUT_WIDGET1 ); mitkWidget3->LayoutDesignListChanged( LAYOUT_WIDGET1 ); mitkWidget4->LayoutDesignListChanged( LAYOUT_WIDGET1 ); //update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToWidget2() { SMW_INFO << "changing layout to big Widget2 ..." << std::endl; //Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout ; //create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //add widget Splitter to main Splitter m_MainSplit->addWidget( mitkWidget2Container ); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //show mainSplitt and add to Layout m_MainSplit->show(); //show/hide Widgets mitkWidget1->hide(); if ( mitkWidget2->isHidden() ) mitkWidget2->show(); mitkWidget3->hide(); mitkWidget4->hide(); m_Layout = LAYOUT_WIDGET2; //update Layout Design List mitkWidget1->LayoutDesignListChanged( LAYOUT_WIDGET2 ); mitkWidget2->LayoutDesignListChanged( LAYOUT_WIDGET2 ); mitkWidget3->LayoutDesignListChanged( LAYOUT_WIDGET2 ); mitkWidget4->LayoutDesignListChanged( LAYOUT_WIDGET2 ); //update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToWidget3() { SMW_INFO << "changing layout to big Widget3 ..." << std::endl; //Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout ; //create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //add widget Splitter to main Splitter m_MainSplit->addWidget( mitkWidget3Container ); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //show mainSplitt and add to Layout m_MainSplit->show(); //show/hide Widgets mitkWidget1->hide(); mitkWidget2->hide(); if ( mitkWidget3->isHidden() ) mitkWidget3->show(); mitkWidget4->hide(); m_Layout = LAYOUT_WIDGET3; //update Layout Design List mitkWidget1->LayoutDesignListChanged( LAYOUT_WIDGET3 ); mitkWidget2->LayoutDesignListChanged( LAYOUT_WIDGET3 ); mitkWidget3->LayoutDesignListChanged( LAYOUT_WIDGET3 ); mitkWidget4->LayoutDesignListChanged( LAYOUT_WIDGET3 ); //update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToRowWidget3And4() { SMW_INFO << "changing layout to Widget3 and 4 in a Row..." << std::endl; //Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout ; //create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter( Qt::Vertical, m_MainSplit ); m_MainSplit->addWidget( m_LayoutSplit ); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //add Widgets to splitter m_LayoutSplit->addWidget( mitkWidget3Container ); m_LayoutSplit->addWidget( mitkWidget4Container ); //set Splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_LayoutSplit->setSizes( splitterSize ); //show mainSplitt and add to Layout m_MainSplit->show(); //show/hide Widgets mitkWidget1->hide(); mitkWidget2->hide(); if ( mitkWidget3->isHidden() ) mitkWidget3->show(); if ( mitkWidget4->isHidden() ) mitkWidget4->show(); m_Layout = LAYOUT_ROW_WIDGET_3_AND_4; //update Layout Design List mitkWidget1->LayoutDesignListChanged( LAYOUT_ROW_WIDGET_3_AND_4 ); mitkWidget2->LayoutDesignListChanged( LAYOUT_ROW_WIDGET_3_AND_4 ); mitkWidget3->LayoutDesignListChanged( LAYOUT_ROW_WIDGET_3_AND_4 ); mitkWidget4->LayoutDesignListChanged( LAYOUT_ROW_WIDGET_3_AND_4 ); //update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToColumnWidget3And4() { SMW_INFO << "changing layout to Widget3 and 4 in one Column..." << std::endl; //Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout ; //create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter( m_MainSplit ); m_MainSplit->addWidget( m_LayoutSplit ); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //add Widgets to splitter m_LayoutSplit->addWidget( mitkWidget3Container ); m_LayoutSplit->addWidget( mitkWidget4Container ); //set SplitterSize QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_LayoutSplit->setSizes( splitterSize ); //show mainSplitt and add to Layout m_MainSplit->show(); //show/hide Widgets mitkWidget1->hide(); mitkWidget2->hide(); if ( mitkWidget3->isHidden() ) mitkWidget3->show(); if ( mitkWidget4->isHidden() ) mitkWidget4->show(); m_Layout = LAYOUT_COLUMN_WIDGET_3_AND_4; //update Layout Design List mitkWidget1->LayoutDesignListChanged( LAYOUT_COLUMN_WIDGET_3_AND_4 ); mitkWidget2->LayoutDesignListChanged( LAYOUT_COLUMN_WIDGET_3_AND_4 ); mitkWidget3->LayoutDesignListChanged( LAYOUT_COLUMN_WIDGET_3_AND_4 ); mitkWidget4->LayoutDesignListChanged( LAYOUT_COLUMN_WIDGET_3_AND_4 ); //update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToRowWidgetSmall3andBig4() { SMW_INFO << "changing layout to Widget3 and 4 in a Row..." << std::endl; this->changeLayoutToRowWidget3And4(); m_Layout = LAYOUT_ROW_WIDGET_SMALL3_AND_BIG4; } void QmitkStdMultiWidget::changeLayoutToSmallUpperWidget2Big3and4() { SMW_INFO << "changing layout to Widget3 and 4 in a Row..." << std::endl; //Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout ; //create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter( Qt::Vertical, m_MainSplit ); m_MainSplit->addWidget( m_LayoutSplit ); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter( Qt::Vertical, m_LayoutSplit ); m_SubSplit2 = new QSplitter( m_LayoutSplit ); //insert Widget into the splitters m_SubSplit1->addWidget( mitkWidget2Container ); m_SubSplit2->addWidget( mitkWidget3Container ); m_SubSplit2->addWidget( mitkWidget4Container ); //set Splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit2->setSizes( splitterSize ); splitterSize.clear(); splitterSize.push_back(500); splitterSize.push_back(1000); m_LayoutSplit->setSizes( splitterSize ); //show mainSplitt m_MainSplit->show(); //show Widget if hidden mitkWidget1->hide(); if ( mitkWidget2->isHidden() ) mitkWidget2->show(); if ( mitkWidget3->isHidden() ) mitkWidget3->show(); if ( mitkWidget4->isHidden() ) mitkWidget4->show(); m_Layout = LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4; //update Layout Design List mitkWidget1->LayoutDesignListChanged( LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4 ); mitkWidget2->LayoutDesignListChanged( LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4 ); mitkWidget3->LayoutDesignListChanged( LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4 ); mitkWidget4->LayoutDesignListChanged( LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4 ); //update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutTo2x2Dand3DWidget() { SMW_INFO << "changing layout to 2 x 2D and 3D Widget" << std::endl; //Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout ; //create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter( m_MainSplit ); m_MainSplit->addWidget( m_LayoutSplit ); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter( Qt::Vertical, m_LayoutSplit ); m_SubSplit2 = new QSplitter( m_LayoutSplit ); //add Widgets to splitter m_SubSplit1->addWidget( mitkWidget1Container ); m_SubSplit1->addWidget( mitkWidget2Container ); m_SubSplit2->addWidget( mitkWidget4Container ); //set Splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes( splitterSize ); m_LayoutSplit->setSizes( splitterSize ); //show mainSplitt and add to Layout m_MainSplit->show(); //show/hide Widgets if ( mitkWidget1->isHidden() ) mitkWidget1->show(); if ( mitkWidget2->isHidden() ) mitkWidget2->show(); mitkWidget3->hide(); if ( mitkWidget4->isHidden() ) mitkWidget4->show(); m_Layout = LAYOUT_2X_2D_AND_3D_WIDGET; //update Layout Design List mitkWidget1->LayoutDesignListChanged( LAYOUT_2X_2D_AND_3D_WIDGET ); mitkWidget2->LayoutDesignListChanged( LAYOUT_2X_2D_AND_3D_WIDGET ); mitkWidget3->LayoutDesignListChanged( LAYOUT_2X_2D_AND_3D_WIDGET ); mitkWidget4->LayoutDesignListChanged( LAYOUT_2X_2D_AND_3D_WIDGET ); //update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToLeft2Dand3DRight2D() { SMW_INFO << "changing layout to 2D and 3D left, 2D right Widget" << std::endl; //Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout ; //create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter( m_MainSplit ); m_MainSplit->addWidget( m_LayoutSplit ); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter( Qt::Vertical, m_LayoutSplit ); m_SubSplit2 = new QSplitter( m_LayoutSplit ); //add Widgets to splitter m_SubSplit1->addWidget( mitkWidget1Container ); m_SubSplit1->addWidget( mitkWidget4Container ); m_SubSplit2->addWidget( mitkWidget2Container ); //set Splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes( splitterSize ); m_LayoutSplit->setSizes( splitterSize ); //show mainSplitt and add to Layout m_MainSplit->show(); //show/hide Widgets if ( mitkWidget1->isHidden() ) mitkWidget1->show(); if ( mitkWidget2->isHidden() ) mitkWidget2->show(); mitkWidget3->hide(); if ( mitkWidget4->isHidden() ) mitkWidget4->show(); m_Layout = LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET; //update Layout Design List mitkWidget1->LayoutDesignListChanged( LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET ); mitkWidget2->LayoutDesignListChanged( LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET ); mitkWidget3->LayoutDesignListChanged( LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET ); mitkWidget4->LayoutDesignListChanged( LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET ); //update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutTo2DUpAnd3DDown() { SMW_INFO << "changing layout to 2D up and 3D down" << std::endl; //Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout ; //create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout( this ); //Set Layout to widget this->setLayout(QmitkStdMultiWidgetLayout); //create main splitter m_MainSplit = new QSplitter( this ); QmitkStdMultiWidgetLayout->addWidget( m_MainSplit ); //create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter( Qt::Vertical, m_MainSplit ); m_MainSplit->addWidget( m_LayoutSplit ); //add LevelWindow Widget to mainSplitter m_MainSplit->addWidget( levelWindowWidget ); //create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter( m_LayoutSplit ); m_SubSplit2 = new QSplitter( m_LayoutSplit ); //insert Widget Container into splitter top m_SubSplit1->addWidget( mitkWidget1Container ); //set SplitterSize for splitter top QList splitterSize; // splitterSize.push_back(1000); // splitterSize.push_back(1000); // splitterSize.push_back(1000); // m_SubSplit1->setSizes( splitterSize ); //insert Widget Container into splitter bottom m_SubSplit2->addWidget( mitkWidget4Container ); //set SplitterSize for splitter m_LayoutSplit splitterSize.clear(); splitterSize.push_back(700); splitterSize.push_back(700); m_LayoutSplit->setSizes( splitterSize ); //show mainSplitt m_MainSplit->show(); //show/hide Widgets if ( mitkWidget1->isHidden() ) mitkWidget1->show(); mitkWidget2->hide(); mitkWidget3->hide(); if ( mitkWidget4->isHidden() ) mitkWidget4->show(); m_Layout = LAYOUT_2D_UP_AND_3D_DOWN; //update Layout Design List mitkWidget1->LayoutDesignListChanged( LAYOUT_2D_UP_AND_3D_DOWN ); mitkWidget2->LayoutDesignListChanged( LAYOUT_2D_UP_AND_3D_DOWN ); mitkWidget3->LayoutDesignListChanged( LAYOUT_2D_UP_AND_3D_DOWN ); mitkWidget4->LayoutDesignListChanged( LAYOUT_2D_UP_AND_3D_DOWN ); //update all Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::SetDataStorage( mitk::DataStorage* ds ) { mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->SetDataStorage(ds); mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow())->SetDataStorage(ds); mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow())->SetDataStorage(ds); mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->SetDataStorage(ds); m_DataStorage = ds; } void QmitkStdMultiWidget::Fit() { vtkRenderer * vtkrenderer; mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->GetDisplayGeometry()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow())->GetDisplayGeometry()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow())->GetDisplayGeometry()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->GetDisplayGeometry()->Fit(); int w = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->GetVtkRenderer(); if ( vtkrenderer!= NULL ) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow())->GetVtkRenderer(); if ( vtkrenderer!= NULL ) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow())->GetVtkRenderer(); if ( vtkrenderer!= NULL ) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->GetVtkRenderer(); if ( vtkrenderer!= NULL ) vtkrenderer->ResetCamera(); vtkObject::SetGlobalWarningDisplay(w); } void QmitkStdMultiWidget::InitPositionTracking() { //PoinSetNode for MouseOrientation m_PositionTrackerNode = mitk::DataNode::New(); m_PositionTrackerNode->SetProperty("name", mitk::StringProperty::New("Mouse Position")); m_PositionTrackerNode->SetData( mitk::PointSet::New() ); m_PositionTrackerNode->SetColor(1.0,0.33,0.0); m_PositionTrackerNode->SetProperty("layer", mitk::IntProperty::New(1001)); m_PositionTrackerNode->SetVisibility(true); m_PositionTrackerNode->SetProperty("inputdevice", mitk::BoolProperty::New(true) ); m_PositionTrackerNode->SetProperty("BaseRendererMapperID", mitk::IntProperty::New(0) );//point position 2D mouse m_PositionTrackerNode->SetProperty("baserenderer", mitk::StringProperty::New("N/A")); } void QmitkStdMultiWidget::AddDisplayPlaneSubTree() { // add the displayed planes of the multiwidget to a node to which the subtree // @a planesSubTree points ... float white[3] = {1.0f,1.0f,1.0f}; mitk::PlaneGeometryDataMapper2D::Pointer mapper; // ... of widget 1 m_PlaneNode1 = (mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow()))->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode1->SetColor(white, mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())); m_PlaneNode1->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode1->SetProperty("name", mitk::StringProperty::New("widget1Plane")); m_PlaneNode1->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode1->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode1->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 2 m_PlaneNode2 =( mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow()))->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode2->SetColor(white, mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())); m_PlaneNode2->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode2->SetProperty("name", mitk::StringProperty::New("widget2Plane")); m_PlaneNode2->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode2->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode2->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 3 m_PlaneNode3 = (mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow()))->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode3->SetColor(white, mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())); m_PlaneNode3->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode3->SetProperty("name", mitk::StringProperty::New("widget3Plane")); m_PlaneNode3->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode3->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode3->SetMapper(mitk::BaseRenderer::Standard2D, mapper); m_Node = mitk::DataNode::New(); m_Node->SetProperty("name", mitk::StringProperty::New("Widgets")); m_Node->SetProperty("helper object", mitk::BoolProperty::New(true)); } mitk::SliceNavigationController* QmitkStdMultiWidget::GetTimeNavigationController() { return m_TimeNavigationController; } void QmitkStdMultiWidget::EnableStandardLevelWindow() { levelWindowWidget->disconnect(this); levelWindowWidget->SetDataStorage(mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->GetDataStorage()); levelWindowWidget->show(); } void QmitkStdMultiWidget::DisableStandardLevelWindow() { levelWindowWidget->disconnect(this); levelWindowWidget->hide(); } // CAUTION: Legacy code for enabling Qt-signal-controlled view initialization. // Use RenderingManager::InitializeViews() instead. bool QmitkStdMultiWidget::InitializeStandardViews( const mitk::Geometry3D * geometry ) { return m_RenderingManager->InitializeViews( geometry ); } void QmitkStdMultiWidget::RequestUpdate() { m_RenderingManager->RequestUpdate(mitkWidget1->GetRenderWindow()); m_RenderingManager->RequestUpdate(mitkWidget2->GetRenderWindow()); m_RenderingManager->RequestUpdate(mitkWidget3->GetRenderWindow()); m_RenderingManager->RequestUpdate(mitkWidget4->GetRenderWindow()); } void QmitkStdMultiWidget::ForceImmediateUpdate() { m_RenderingManager->ForceImmediateUpdate(mitkWidget1->GetRenderWindow()); m_RenderingManager->ForceImmediateUpdate(mitkWidget2->GetRenderWindow()); m_RenderingManager->ForceImmediateUpdate(mitkWidget3->GetRenderWindow()); m_RenderingManager->ForceImmediateUpdate(mitkWidget4->GetRenderWindow()); } void QmitkStdMultiWidget::wheelEvent( QWheelEvent * e ) { emit WheelMoved( e ); } void QmitkStdMultiWidget::mousePressEvent(QMouseEvent * e) { if (e->button() == Qt::LeftButton) { mitk::Point3D pointValue = this->GetLastLeftClickPosition(); emit LeftMouseClicked(pointValue); } } void QmitkStdMultiWidget::moveEvent( QMoveEvent* e ) { QWidget::moveEvent( e ); // it is necessary to readjust the position of the overlays as the StdMultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } void QmitkStdMultiWidget::leaveEvent ( QEvent * /*e*/ ) { //set cursor back to initial state m_SlicesRotator->ResetMouseCursor(); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow1() const { return mitkWidget1; } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow2() const { return mitkWidget2; } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow3() const { return mitkWidget3; } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow4() const { return mitkWidget4; } const mitk::Point3D& QmitkStdMultiWidget::GetLastLeftClickPosition() const { return m_LastLeftClickPositionSupplier->GetCurrentPoint(); } const mitk::Point3D QmitkStdMultiWidget::GetCrossPosition() const { const mitk::PlaneGeometry *plane1 = mitkWidget1->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry *plane2 = mitkWidget2->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry *plane3 = mitkWidget3->GetSliceNavigationController()->GetCurrentPlaneGeometry(); mitk::Line3D line; if ( (plane1 != NULL) && (plane2 != NULL) && (plane1->IntersectionLine( plane2, line )) ) { mitk::Point3D point; if ( (plane3 != NULL) && (plane3->IntersectionPoint( line, point )) ) { return point; } } return m_LastLeftClickPositionSupplier->GetCurrentPoint(); } void QmitkStdMultiWidget::EnablePositionTracking() { if (!m_PositionTracker) { m_PositionTracker = mitk::PositionTracker::New("PositionTracker", NULL); } mitk::GlobalInteraction* globalInteraction = mitk::GlobalInteraction::GetInstance(); if (globalInteraction) { if(m_DataStorage.IsNotNull()) m_DataStorage->Add(m_PositionTrackerNode); globalInteraction->AddListener(m_PositionTracker); } } void QmitkStdMultiWidget::DisablePositionTracking() { mitk::GlobalInteraction* globalInteraction = mitk::GlobalInteraction::GetInstance(); if(globalInteraction) { if (m_DataStorage.IsNotNull()) m_DataStorage->Remove(m_PositionTrackerNode); globalInteraction->RemoveListener(m_PositionTracker); } } void QmitkStdMultiWidget::EnsureDisplayContainsPoint( mitk::DisplayGeometry* displayGeometry, const mitk::Point3D& p) { mitk::Point2D pointOnPlane; displayGeometry->Map( p, pointOnPlane ); // point minus origin < width or height ==> outside ? mitk::Vector2D pointOnRenderWindow_MM; pointOnRenderWindow_MM = pointOnPlane.GetVectorFromOrigin() - displayGeometry->GetOriginInMM(); mitk::Vector2D sizeOfDisplay( displayGeometry->GetSizeInMM() ); if ( sizeOfDisplay[0] < pointOnRenderWindow_MM[0] || 0 > pointOnRenderWindow_MM[0] || sizeOfDisplay[1] < pointOnRenderWindow_MM[1] || 0 > pointOnRenderWindow_MM[1] ) { // point is not visible -> move geometry mitk::Vector2D offset( (pointOnRenderWindow_MM - sizeOfDisplay / 2.0) / displayGeometry->GetScaleFactorMMPerDisplayUnit() ); displayGeometry->MoveBy( offset ); } } void QmitkStdMultiWidget::MoveCrossToPosition(const mitk::Point3D& newPosition) { // create a PositionEvent with the given position and // tell the slice navigation controllers to move there mitk::Point2D p2d; mitk::PositionEvent event( mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow()), 0, 0, 0, mitk::Key_unknown, p2d, newPosition ); mitk::StateEvent stateEvent(mitk::EIDLEFTMOUSEBTN, &event); mitk::StateEvent stateEvent2(mitk::EIDLEFTMOUSERELEASE, &event); switch ( m_PlaneMode ) { default: case PLANE_MODE_SLICING: mitkWidget1->GetSliceNavigationController()->HandleEvent( &stateEvent ); mitkWidget2->GetSliceNavigationController()->HandleEvent( &stateEvent ); mitkWidget3->GetSliceNavigationController()->HandleEvent( &stateEvent ); // just in case SNCs will develop something that depends on the mouse // button being released again mitkWidget1->GetSliceNavigationController()->HandleEvent( &stateEvent2 ); mitkWidget2->GetSliceNavigationController()->HandleEvent( &stateEvent2 ); mitkWidget3->GetSliceNavigationController()->HandleEvent( &stateEvent2 ); break; case PLANE_MODE_ROTATION: m_SlicesRotator->HandleEvent( &stateEvent ); // just in case SNCs will develop something that depends on the mouse // button being released again m_SlicesRotator->HandleEvent( &stateEvent2 ); break; case PLANE_MODE_SWIVEL: m_SlicesSwiveller->HandleEvent( &stateEvent ); // just in case SNCs will develop something that depends on the mouse // button being released again m_SlicesSwiveller->HandleEvent( &stateEvent2 ); break; } // determine if cross is now out of display // if so, move the display window EnsureDisplayContainsPoint( mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow()) ->GetDisplayGeometry(), newPosition ); EnsureDisplayContainsPoint( mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow()) ->GetDisplayGeometry(), newPosition ); EnsureDisplayContainsPoint( mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow()) ->GetDisplayGeometry(), newPosition ); // update displays m_RenderingManager->RequestUpdateAll(); } void QmitkStdMultiWidget::HandleCrosshairPositionEvent() { if(!m_PendingCrosshairPositionEvent) { m_PendingCrosshairPositionEvent=true; QTimer::singleShot(0,this,SLOT( HandleCrosshairPositionEventDelayed() ) ); } } mitk::DataNode::Pointer QmitkStdMultiWidget::GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes) { mitk::Point3D crosshairPos = this->GetCrossPosition(); mitk::DataNode::Pointer node; int maxlayer = -32768; if(nodes.IsNotNull()) { mitk::BaseRenderer* baseRenderer = this->mitkWidget1->GetSliceNavigationController()->GetRenderer(); // find node with largest layer, that is the node shown on top in the render window for (unsigned int x = 0; x < nodes->size(); x++) { if ( (nodes->at(x)->GetData()->GetGeometry() != NULL) && nodes->at(x)->GetData()->GetGeometry()->IsInside(crosshairPos) ) { int layer = 0; if(!(nodes->at(x)->GetIntProperty("layer", layer))) continue; if(layer > maxlayer) { if( static_cast(nodes->at(x))->IsVisible( baseRenderer ) ) { node = nodes->at(x); maxlayer = layer; } } } } } return node; } void QmitkStdMultiWidget::HandleCrosshairPositionEventDelayed() { m_PendingCrosshairPositionEvent = false; // find image with highest layer mitk::TNodePredicateDataType::Pointer isImageData = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->m_DataStorage->GetSubset(isImageData).GetPointer(); mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; mitk::Image::Pointer image; bool isBinary = false; node = this->GetTopLayerNode(nodes); if(node.IsNotNull()) { node->GetBoolProperty("binary",isBinary); if(isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = m_DataStorage->GetSources(node, NULL, true); if(!sourcenodes->empty()) { topSourceNode = this->GetTopLayerNode(sourcenodes); } if(topSourceNode.IsNotNull()) { image = dynamic_cast(topSourceNode->GetData()); } else { image = dynamic_cast(node->GetData()); } } else { image = dynamic_cast(node->GetData()); } } mitk::Point3D crosshairPos = this->GetCrossPosition(); std::string statusText; std::stringstream stream; - mitk::Index3D p; + itk::Index<3> p; mitk::BaseRenderer* baseRenderer = this->mitkWidget1->GetSliceNavigationController()->GetRenderer(); unsigned int timestep = baseRenderer->GetTimeStep(); if(image.IsNotNull() && (image->GetTimeSteps() > timestep )) { image->GetGeometry()->WorldToIndex(crosshairPos, p); stream.precision(2); stream<<"Position: <" << std::fixed < mm"; stream<<"; Index: <"< "; mitk::ScalarType pixelValue = image->GetPixelValueByIndex(p, timestep); if (fabs(pixelValue)>1000000 || fabs(pixelValue) < 0.01) { stream<<"; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: "<< std::scientific<< pixelValue <<" "; } else { stream<<"; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: "<< pixelValue <<" "; } } else { stream << "No image information at this position!"; } statusText = stream.str(); mitk::StatusBar::GetInstance()->DisplayGreyValueText(statusText.c_str()); } void QmitkStdMultiWidget::EnableNavigationControllerEventListening() { // Let NavigationControllers listen to GlobalInteraction mitk::GlobalInteraction *gi = mitk::GlobalInteraction::GetInstance(); // Listen for SliceNavigationController mitkWidget1->GetSliceNavigationController()->crosshairPositionEvent.AddListener( mitk::MessageDelegate( this, &QmitkStdMultiWidget::HandleCrosshairPositionEvent ) ); mitkWidget2->GetSliceNavigationController()->crosshairPositionEvent.AddListener( mitk::MessageDelegate( this, &QmitkStdMultiWidget::HandleCrosshairPositionEvent ) ); mitkWidget3->GetSliceNavigationController()->crosshairPositionEvent.AddListener( mitk::MessageDelegate( this, &QmitkStdMultiWidget::HandleCrosshairPositionEvent ) ); switch ( m_PlaneMode ) { default: case PLANE_MODE_SLICING: gi->AddListener( mitkWidget1->GetSliceNavigationController() ); gi->AddListener( mitkWidget2->GetSliceNavigationController() ); gi->AddListener( mitkWidget3->GetSliceNavigationController() ); gi->AddListener( mitkWidget4->GetSliceNavigationController() ); break; case PLANE_MODE_ROTATION: gi->AddListener( m_SlicesRotator ); break; case PLANE_MODE_SWIVEL: gi->AddListener( m_SlicesSwiveller ); break; } gi->AddListener( m_TimeNavigationController ); m_CrosshairNavigationEnabled = true; } void QmitkStdMultiWidget::DisableNavigationControllerEventListening() { // Do not let NavigationControllers listen to GlobalInteraction mitk::GlobalInteraction *gi = mitk::GlobalInteraction::GetInstance(); switch ( m_PlaneMode ) { default: case PLANE_MODE_SLICING: gi->RemoveListener( mitkWidget1->GetSliceNavigationController() ); gi->RemoveListener( mitkWidget2->GetSliceNavigationController() ); gi->RemoveListener( mitkWidget3->GetSliceNavigationController() ); gi->RemoveListener( mitkWidget4->GetSliceNavigationController() ); break; case PLANE_MODE_ROTATION: m_SlicesRotator->ResetMouseCursor(); gi->RemoveListener( m_SlicesRotator ); break; case PLANE_MODE_SWIVEL: m_SlicesSwiveller->ResetMouseCursor(); gi->RemoveListener( m_SlicesSwiveller ); break; } gi->RemoveListener( m_TimeNavigationController ); m_CrosshairNavigationEnabled = false; } int QmitkStdMultiWidget::GetLayout() const { return m_Layout; } bool QmitkStdMultiWidget::GetGradientBackgroundFlag() const { return m_GradientBackgroundFlag; } void QmitkStdMultiWidget::EnableGradientBackground() { // gradient background is by default only in widget 4, otherwise // interferences between 2D rendering and VTK rendering may occur. //m_GradientBackground1->Enable(); //m_GradientBackground2->Enable(); //m_GradientBackground3->Enable(); m_GradientBackground4->Enable(); m_GradientBackgroundFlag = true; } void QmitkStdMultiWidget::DisableGradientBackground() { //m_GradientBackground1->Disable(); //m_GradientBackground2->Disable(); //m_GradientBackground3->Disable(); m_GradientBackground4->Disable(); m_GradientBackgroundFlag = false; } void QmitkStdMultiWidget::EnableDepartmentLogo() { m_LogoRendering4->Enable(); } void QmitkStdMultiWidget::DisableDepartmentLogo() { m_LogoRendering4->Disable(); } bool QmitkStdMultiWidget::IsDepartmentLogoEnabled() const { return m_LogoRendering4->IsEnabled(); } bool QmitkStdMultiWidget::IsCrosshairNavigationEnabled() const { return m_CrosshairNavigationEnabled; } mitk::SlicesRotator * QmitkStdMultiWidget::GetSlicesRotator() const { return m_SlicesRotator; } mitk::SlicesSwiveller * QmitkStdMultiWidget::GetSlicesSwiveller() const { return m_SlicesSwiveller; } void QmitkStdMultiWidget::SetWidgetPlaneVisibility(const char* widgetName, bool visible, mitk::BaseRenderer *renderer) { if (m_DataStorage.IsNotNull()) { mitk::DataNode* n = m_DataStorage->GetNamedNode(widgetName); if (n != NULL) n->SetVisibility(visible, renderer); } } void QmitkStdMultiWidget::SetWidgetPlanesVisibility(bool visible, mitk::BaseRenderer *renderer) { SetWidgetPlaneVisibility("widget1Plane", visible, renderer); SetWidgetPlaneVisibility("widget2Plane", visible, renderer); SetWidgetPlaneVisibility("widget3Plane", visible, renderer); m_RenderingManager->RequestUpdateAll(); } void QmitkStdMultiWidget::SetWidgetPlanesLocked(bool locked) { //do your job and lock or unlock slices. GetRenderWindow1()->GetSliceNavigationController()->SetSliceLocked(locked); GetRenderWindow2()->GetSliceNavigationController()->SetSliceLocked(locked); GetRenderWindow3()->GetSliceNavigationController()->SetSliceLocked(locked); } void QmitkStdMultiWidget::SetWidgetPlanesRotationLocked(bool locked) { //do your job and lock or unlock slices. GetRenderWindow1()->GetSliceNavigationController()->SetSliceRotationLocked(locked); GetRenderWindow2()->GetSliceNavigationController()->SetSliceRotationLocked(locked); GetRenderWindow3()->GetSliceNavigationController()->SetSliceRotationLocked(locked); } void QmitkStdMultiWidget::SetWidgetPlanesRotationLinked( bool link ) { m_SlicesRotator->SetLinkPlanes( link ); m_SlicesSwiveller->SetLinkPlanes( link ); emit WidgetPlanesRotationLinked( link ); } void QmitkStdMultiWidget::SetWidgetPlaneMode( int userMode ) { MITK_DEBUG << "Changing crosshair mode to " << userMode; // first of all reset left mouse button interaction to default if PACS interaction style is active m_MouseModeSwitcher->SelectMouseMode( mitk::MouseModeSwitcher::MousePointer ); emit WidgetNotifyNewCrossHairMode( userMode ); int mode = m_PlaneMode; bool link = false; // Convert user interface mode to actual mode { switch(userMode) { case 0: mode = PLANE_MODE_SLICING; link = false; break; case 1: mode = PLANE_MODE_ROTATION; link = false; break; case 2: mode = PLANE_MODE_ROTATION; link = true; break; case 3: mode = PLANE_MODE_SWIVEL; link = false; break; } } // Slice rotation linked m_SlicesRotator->SetLinkPlanes( link ); m_SlicesSwiveller->SetLinkPlanes( link ); // Do nothing if mode didn't change if ( m_PlaneMode == mode ) { return; } mitk::GlobalInteraction *gi = mitk::GlobalInteraction::GetInstance(); // Remove listeners of previous mode switch ( m_PlaneMode ) { default: case PLANE_MODE_SLICING: // Notify MainTemplate GUI that this mode has been deselected emit WidgetPlaneModeSlicing( false ); gi->RemoveListener( mitkWidget1->GetSliceNavigationController() ); gi->RemoveListener( mitkWidget2->GetSliceNavigationController() ); gi->RemoveListener( mitkWidget3->GetSliceNavigationController() ); gi->RemoveListener( mitkWidget4->GetSliceNavigationController() ); break; case PLANE_MODE_ROTATION: // Notify MainTemplate GUI that this mode has been deselected emit WidgetPlaneModeRotation( false ); m_SlicesRotator->ResetMouseCursor(); gi->RemoveListener( m_SlicesRotator ); break; case PLANE_MODE_SWIVEL: // Notify MainTemplate GUI that this mode has been deselected emit WidgetPlaneModeSwivel( false ); m_SlicesSwiveller->ResetMouseCursor(); gi->RemoveListener( m_SlicesSwiveller ); break; } // Set new mode and add corresponding listener to GlobalInteraction m_PlaneMode = mode; switch ( m_PlaneMode ) { default: case PLANE_MODE_SLICING: // Notify MainTemplate GUI that this mode has been selected emit WidgetPlaneModeSlicing( true ); // Add listeners gi->AddListener( mitkWidget1->GetSliceNavigationController() ); gi->AddListener( mitkWidget2->GetSliceNavigationController() ); gi->AddListener( mitkWidget3->GetSliceNavigationController() ); gi->AddListener( mitkWidget4->GetSliceNavigationController() ); m_RenderingManager->InitializeViews(); break; case PLANE_MODE_ROTATION: // Notify MainTemplate GUI that this mode has been selected emit WidgetPlaneModeRotation( true ); // Add listener gi->AddListener( m_SlicesRotator ); break; case PLANE_MODE_SWIVEL: // Notify MainTemplate GUI that this mode has been selected emit WidgetPlaneModeSwivel( true ); // Add listener gi->AddListener( m_SlicesSwiveller ); break; } // Notify MainTemplate GUI that mode has changed emit WidgetPlaneModeChange(m_PlaneMode); } void QmitkStdMultiWidget::SetGradientBackgroundColors( const mitk::Color & upper, const mitk::Color & lower ) { m_GradientBackground1->SetGradientColors(upper[0], upper[1], upper[2], lower[0], lower[1], lower[2]); m_GradientBackground2->SetGradientColors(upper[0], upper[1], upper[2], lower[0], lower[1], lower[2]); m_GradientBackground3->SetGradientColors(upper[0], upper[1], upper[2], lower[0], lower[1], lower[2]); m_GradientBackground4->SetGradientColors(upper[0], upper[1], upper[2], lower[0], lower[1], lower[2]); m_GradientBackgroundFlag = true; } void QmitkStdMultiWidget::SetDepartmentLogoPath( const char * path ) { m_LogoRendering1->SetLogoSource(path); m_LogoRendering2->SetLogoSource(path); m_LogoRendering3->SetLogoSource(path); m_LogoRendering4->SetLogoSource(path); } void QmitkStdMultiWidget::SetWidgetPlaneModeToSlicing( bool activate ) { if ( activate ) { this->SetWidgetPlaneMode( PLANE_MODE_SLICING ); } } void QmitkStdMultiWidget::SetWidgetPlaneModeToRotation( bool activate ) { if ( activate ) { this->SetWidgetPlaneMode( PLANE_MODE_ROTATION ); } } void QmitkStdMultiWidget::SetWidgetPlaneModeToSwivel( bool activate ) { if ( activate ) { this->SetWidgetPlaneMode( PLANE_MODE_SWIVEL ); } } void QmitkStdMultiWidget::OnLayoutDesignChanged( int layoutDesignIndex ) { switch( layoutDesignIndex ) { case LAYOUT_DEFAULT: { this->changeLayoutToDefault(); break; } case LAYOUT_2D_IMAGES_UP: { this->changeLayoutTo2DImagesUp(); break; } case LAYOUT_2D_IMAGES_LEFT: { this->changeLayoutTo2DImagesLeft(); break; } case LAYOUT_BIG_3D: { this->changeLayoutToBig3D(); break; } case LAYOUT_WIDGET1: { this->changeLayoutToWidget1(); break; } case LAYOUT_WIDGET2: { this->changeLayoutToWidget2(); break; } case LAYOUT_WIDGET3: { this->changeLayoutToWidget3(); break; } case LAYOUT_2X_2D_AND_3D_WIDGET: { this->changeLayoutTo2x2Dand3DWidget(); break; } case LAYOUT_ROW_WIDGET_3_AND_4: { this->changeLayoutToRowWidget3And4(); break; } case LAYOUT_COLUMN_WIDGET_3_AND_4: { this->changeLayoutToColumnWidget3And4(); break; } case LAYOUT_ROW_WIDGET_SMALL3_AND_BIG4: { this->changeLayoutToRowWidgetSmall3andBig4(); break; } case LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4: { this->changeLayoutToSmallUpperWidget2Big3and4(); break; } case LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET: { this->changeLayoutToLeft2Dand3DRight2D(); break; } }; } void QmitkStdMultiWidget::UpdateAllWidgets() { mitkWidget1->resize( mitkWidget1Container->frameSize().width()-1, mitkWidget1Container->frameSize().height() ); mitkWidget1->resize( mitkWidget1Container->frameSize().width(), mitkWidget1Container->frameSize().height() ); mitkWidget2->resize( mitkWidget2Container->frameSize().width()-1, mitkWidget2Container->frameSize().height() ); mitkWidget2->resize( mitkWidget2Container->frameSize().width(), mitkWidget2Container->frameSize().height() ); mitkWidget3->resize( mitkWidget3Container->frameSize().width()-1, mitkWidget3Container->frameSize().height() ); mitkWidget3->resize( mitkWidget3Container->frameSize().width(), mitkWidget3Container->frameSize().height() ); mitkWidget4->resize( mitkWidget4Container->frameSize().width()-1, mitkWidget4Container->frameSize().height() ); mitkWidget4->resize( mitkWidget4Container->frameSize().width(), mitkWidget4Container->frameSize().height() ); } void QmitkStdMultiWidget::HideAllWidgetToolbars() { mitkWidget1->HideRenderWindowMenu(); mitkWidget2->HideRenderWindowMenu(); mitkWidget3->HideRenderWindowMenu(); mitkWidget4->HideRenderWindowMenu(); } void QmitkStdMultiWidget::ActivateMenuWidget( bool state ) { mitkWidget1->ActivateMenuWidget( state, this ); mitkWidget2->ActivateMenuWidget( state, this ); mitkWidget3->ActivateMenuWidget( state, this ); mitkWidget4->ActivateMenuWidget( state, this ); } bool QmitkStdMultiWidget::IsMenuWidgetEnabled() const { return mitkWidget1->GetActivateMenuWidgetFlag(); } void QmitkStdMultiWidget::ResetCrosshair() { if (m_DataStorage.IsNotNull()) { m_RenderingManager->InitializeViewsByBoundingObjects(m_DataStorage); //m_RenderingManager->InitializeViews( m_DataStorage->ComputeVisibleBoundingGeometry3D() ); // reset interactor to normal slicing this->SetWidgetPlaneMode(PLANE_MODE_SLICING); } } void QmitkStdMultiWidget::EnableColoredRectangles() { m_RectangleRendering1->Enable(1.0, 0.0, 0.0); m_RectangleRendering2->Enable(0.0, 1.0, 0.0); m_RectangleRendering3->Enable(0.0, 0.0, 1.0); m_RectangleRendering4->Enable(1.0, 1.0, 0.0); } void QmitkStdMultiWidget::DisableColoredRectangles() { m_RectangleRendering1->Disable(); m_RectangleRendering2->Disable(); m_RectangleRendering3->Disable(); m_RectangleRendering4->Disable(); } bool QmitkStdMultiWidget::IsColoredRectanglesEnabled() const { return m_RectangleRendering1->IsEnabled(); } mitk::MouseModeSwitcher* QmitkStdMultiWidget::GetMouseModeSwitcher() { return m_MouseModeSwitcher; } void QmitkStdMultiWidget::MouseModeSelected( mitk::MouseModeSwitcher::MouseMode mouseMode ) { if ( mouseMode == 0 ) { this->EnableNavigationControllerEventListening(); } else { this->DisableNavigationControllerEventListening(); } } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane1() { return this->m_PlaneNode1; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane2() { return this->m_PlaneNode2; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane3() { return this->m_PlaneNode3; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane(int id) { switch(id) { case 1: return this->m_PlaneNode1; break; case 2: return this->m_PlaneNode2; break; case 3: return this->m_PlaneNode3; break; default: return NULL; } } \ No newline at end of file diff --git a/Modules/QtWidgetsExt/QmitkHistogramJSWidget.cpp b/Modules/QtWidgetsExt/QmitkHistogramJSWidget.cpp index 12258d6568..453cce79dd 100644 --- a/Modules/QtWidgetsExt/QmitkHistogramJSWidget.cpp +++ b/Modules/QtWidgetsExt/QmitkHistogramJSWidget.cpp @@ -1,239 +1,239 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkHistogramJSWidget.h" #include "mitkPixelTypeMultiplex.h" #include #include #include "mitkRenderingManager.h" #include "mitkBaseRenderer.h" #include "mitkImageTimeSelector.h" #include "mitkExtractSliceFilter.h" QmitkHistogramJSWidget::QmitkHistogramJSWidget(QWidget *parent) : QWebView(parent) { // set histogram type to barchart in first instance m_UseLineGraph = false; m_Page = new QmitkJSWebPage(this); setPage(m_Page); // set html from source connect(page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(AddJSObject())); QUrl myUrl = QUrl("qrc:///QtWidgetsExt/Histogram.html"); setUrl(myUrl); // set Scrollbars to be always disabled page()->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); m_ParametricPath = ParametricPathType::New(); } QmitkHistogramJSWidget::~QmitkHistogramJSWidget() { } // adds an Object of Type QmitkHistogramJSWidget to the JavaScript, using QtWebkitBridge void QmitkHistogramJSWidget::AddJSObject() { page()->mainFrame()->addToJavaScriptWindowObject(QString("histogramData"), this); } // reloads WebView, everytime its size has been changed, so the size of the Histogram fits to the size of the widget void QmitkHistogramJSWidget::resizeEvent(QResizeEvent* resizeEvent) { QWebView::resizeEvent(resizeEvent); // workaround for Qt Bug: https://bugs.webkit.org/show_bug.cgi?id=75984 page()->mainFrame()->evaluateJavaScript("disconnectSignals()"); this->reload(); } // method to expose data to JavaScript by using properties void QmitkHistogramJSWidget::ComputeHistogram(HistogramType* histogram) { m_Histogram = histogram; HistogramConstIteratorType startIt = m_Histogram->End(); HistogramConstIteratorType endIt = m_Histogram->End(); HistogramConstIteratorType it = m_Histogram->Begin(); ClearData(); unsigned int i = 0; bool firstValue = false; // removes frequencies of 0, which are outside the first and last bin for (; it != m_Histogram->End(); ++it) { if (it.GetFrequency() > 0.0) { endIt = it; if (!firstValue) { firstValue = true; startIt = it; } } } ++endIt; // generating Lists of measurement and frequencies for (it = startIt ; it != endIt; ++it, ++i) { QVariant frequency = QVariant::fromValue(it.GetFrequency()); QVariant measurement = it.GetMeasurementVector()[0]; m_Frequency.insert(i, frequency); m_Measurement.insert(i, measurement); } m_IntensityProfile = false; this->SignalDataChanged(); } void QmitkHistogramJSWidget::ClearData() { m_Frequency.clear(); m_Measurement.clear(); } void QmitkHistogramJSWidget::ClearHistogram() { this->ClearData(); this->SignalDataChanged(); } QList QmitkHistogramJSWidget::GetFrequency() { return m_Frequency; } QList QmitkHistogramJSWidget::GetMeasurement() { return m_Measurement; } bool QmitkHistogramJSWidget::GetUseLineGraph() { return m_UseLineGraph; } void QmitkHistogramJSWidget::OnBarRadioButtonSelected() { if (m_UseLineGraph) { m_UseLineGraph = false; this->SignalGraphChanged(); } } void QmitkHistogramJSWidget::OnLineRadioButtonSelected() { if (!m_UseLineGraph) { m_UseLineGraph = true; this->SignalGraphChanged(); } } void QmitkHistogramJSWidget::SetImage(mitk::Image* image) { m_Image = image; } void QmitkHistogramJSWidget::SetPlanarFigure(const mitk::PlanarFigure* planarFigure) { m_PlanarFigure = planarFigure; } template -void ReadPixel(mitk::PixelType, mitk::Image::Pointer image, mitk::Index3D indexPoint, double& value) +void ReadPixel(mitk::PixelType, mitk::Image::Pointer image, itk::Index<3> indexPoint, double& value) { if (image->GetDimension() == 2) { mitk::ImagePixelReadAccessor readAccess(image, image->GetSliceData(0)); itk::Index<2> idx; idx[0] = indexPoint[0]; idx[1] = indexPoint[1]; value = readAccess.GetPixelByIndex(idx); } else if (image->GetDimension() == 3) { mitk::ImagePixelReadAccessor readAccess(image, image->GetVolumeData(0)); itk::Index<3> idx; idx[0] = indexPoint[0]; idx[1] = indexPoint[1]; idx[2] = indexPoint[2]; value = readAccess.GetPixelByIndex(idx); } else { //unhandled } } void QmitkHistogramJSWidget::ComputeIntensityProfile(unsigned int timeStep) { this->ClearData(); m_ParametricPath->Initialize(); if (m_PlanarFigure.IsNull()) { mitkThrow() << "PlanarFigure not set!"; } if (m_Image.IsNull()) { mitkThrow() << "Image not set!"; } mitk::Image::Pointer image; if (m_Image->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(m_Image); timeSelector->SetTimeNr(timeStep); timeSelector->Update(); image = timeSelector->GetOutput(); } else { image = m_Image; } mitk::IntensityProfile::Pointer intensityProfile = mitk::ComputeIntensityProfile(image, const_cast(m_PlanarFigure.GetPointer())); m_Frequency.clear(); m_Measurement.clear(); int i = -1; mitk::IntensityProfile::ConstIterator end = intensityProfile->End(); for (mitk::IntensityProfile::ConstIterator it = intensityProfile->Begin(); it != end; ++it) { m_Frequency.push_back(it.GetMeasurementVector()[0]); m_Measurement.push_back(++i); } m_IntensityProfile = true; m_UseLineGraph = true; this->SignalDataChanged(); } bool QmitkHistogramJSWidget::GetIntensityProfile() { return m_IntensityProfile; } diff --git a/Modules/QtWidgetsExt/QmitkSliceWidget.cpp b/Modules/QtWidgetsExt/QmitkSliceWidget.cpp index cdaa71d8e2..fa639ca1a8 100644 --- a/Modules/QtWidgetsExt/QmitkSliceWidget.cpp +++ b/Modules/QtWidgetsExt/QmitkSliceWidget.cpp @@ -1,289 +1,288 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkSliceWidget.h" #include "QmitkStepperAdapter.h" #include "mitkNodePredicateDataType.h" #include "mitkImage.h" #include #include #include QmitkSliceWidget::QmitkSliceWidget(QWidget* parent, const char* name, Qt::WindowFlags f) : QWidget(parent, f) { this->setupUi(this); if (name != 0) this->setObjectName(name); popUp = new QMenu(this); popUp->addAction("Axial"); popUp->addAction("Frontal"); popUp->addAction("Sagittal"); QObject::connect(popUp, SIGNAL(triggered(QAction*)), this, SLOT(ChangeView(QAction*)) ); setPopUpEnabled(false); m_SlicedGeometry = 0; m_View = mitk::SliceNavigationController::Axial; QHBoxLayout *hlayout = new QHBoxLayout(container); hlayout->setMargin(0); // create widget QString composedName("QmitkSliceWidget::"); if (!this->objectName().isEmpty()) composedName += this->objectName(); else composedName += "QmitkGLWidget"; m_RenderWindow = new QmitkRenderWindow(container, composedName); m_Renderer = m_RenderWindow->GetRenderer(); hlayout->addWidget(m_RenderWindow); new QmitkStepperAdapter(m_NavigatorWidget, m_RenderWindow->GetSliceNavigationController()->GetSlice(), "navigation"); SetLevelWindowEnabled(true); } mitk::VtkPropRenderer* QmitkSliceWidget::GetRenderer() { return m_Renderer; } QFrame* QmitkSliceWidget::GetSelectionFrame() { return SelectionFrame; } void QmitkSliceWidget::SetDataStorage( mitk::StandaloneDataStorage::Pointer storage) { m_DataStorage = storage; m_Renderer->SetDataStorage(m_DataStorage); } mitk::StandaloneDataStorage* QmitkSliceWidget::GetDataStorage() { return m_DataStorage; } void QmitkSliceWidget::SetData( mitk::DataStorage::SetOfObjects::ConstIterator it) { SetData(it->Value(), m_View); } void QmitkSliceWidget::SetData( mitk::DataStorage::SetOfObjects::ConstIterator it, mitk::SliceNavigationController::ViewDirection view) { SetData(it->Value(), view); } void QmitkSliceWidget::SetData(mitk::DataNode::Pointer node) { try { if (m_DataStorage.IsNotNull()) { m_DataStorage->Add(node); } } catch (...) { } SetData(node, m_View); } void QmitkSliceWidget::SetData(mitk::DataNode::Pointer node, mitk::SliceNavigationController::ViewDirection view) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image.IsNull()) { MITK_WARN << "QmitkSliceWidget data is not an image!"; return; } m_SlicedGeometry = image->GetSlicedGeometry(); this->InitWidget(view); } void QmitkSliceWidget::InitWidget( mitk::SliceNavigationController::ViewDirection viewDirection) { m_View = viewDirection; mitk::SliceNavigationController* controller = m_RenderWindow->GetSliceNavigationController(); if (viewDirection == mitk::SliceNavigationController::Axial) { controller->SetViewDirection( mitk::SliceNavigationController::Axial); } else if (viewDirection == mitk::SliceNavigationController::Frontal) { controller->SetViewDirection(mitk::SliceNavigationController::Frontal); } // init sagittal view else { controller->SetViewDirection(mitk::SliceNavigationController::Sagittal); } if (m_SlicedGeometry.IsNull()) { return; } mitk::BaseGeometry::Pointer geometry = static_cast (m_SlicedGeometry->Clone().GetPointer()); const mitk::BoundingBox::Pointer boundingbox = m_DataStorage->ComputeVisibleBoundingBox(GetRenderer(), NULL); if (boundingbox->GetPoints()->Size() > 0) { //let's see if we have data with a limited live-span ... mitk::TimeBounds timebounds = m_DataStorage->ComputeTimeBounds( GetRenderer(), NULL); mitk::ProportionalTimeGeometry::Pointer timeGeometry = mitk::ProportionalTimeGeometry::New(); timeGeometry->Initialize(geometry, 1); - if (timebounds[1] < mitk::ScalarTypeNumericTraits::max()) { timeGeometry->SetFirstTimePoint(timebounds[0]); timeGeometry->SetStepDuration(1.0); } if (const_cast (timeGeometry->GetBoundingBoxInWorld())->GetDiagonalLength2() >= mitk::eps) { controller->SetInputWorldTimeGeometry(timeGeometry); controller->Update(); } } GetRenderer()->GetDisplayGeometry()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdate( GetRenderer()->GetRenderWindow()); } void QmitkSliceWidget::UpdateGL() { GetRenderer()->GetDisplayGeometry()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdate( GetRenderer()->GetRenderWindow()); } void QmitkSliceWidget::mousePressEvent(QMouseEvent * e) { if (e->button() == Qt::RightButton && popUpEnabled) { popUp->popup(QCursor::pos()); } } void QmitkSliceWidget::wheelEvent(QWheelEvent * e) { int val = m_NavigatorWidget->GetPos(); if (e->orientation() * e->delta() > 0) { m_NavigatorWidget->SetPos(val + 1); } else { if (val > 0) m_NavigatorWidget->SetPos(val - 1); } } void QmitkSliceWidget::ChangeView(QAction* val) { if (val->text() == "Axial") { InitWidget(mitk::SliceNavigationController::Axial); } else if (val->text() == "Frontal") { InitWidget(mitk::SliceNavigationController::Frontal); } else if (val->text() == "Sagittal") { InitWidget(mitk::SliceNavigationController::Sagittal); } } void QmitkSliceWidget::setPopUpEnabled(bool b) { popUpEnabled = b; } QmitkSliderNavigatorWidget* QmitkSliceWidget::GetNavigatorWidget() { return m_NavigatorWidget; } void QmitkSliceWidget::SetLevelWindowEnabled(bool enable) { levelWindow->setEnabled(enable); if (!enable) { levelWindow->setMinimumWidth(0); levelWindow->setMaximumWidth(0); } else { levelWindow->setMinimumWidth(28); levelWindow->setMaximumWidth(28); } } bool QmitkSliceWidget::IsLevelWindowEnabled() { return levelWindow->isEnabled(); } QmitkRenderWindow* QmitkSliceWidget::GetRenderWindow() { return m_RenderWindow; } mitk::SliceNavigationController* QmitkSliceWidget::GetSliceNavigationController() const { return m_RenderWindow->GetSliceNavigationController(); } mitk::CameraRotationController* QmitkSliceWidget::GetCameraRotationController() const { return m_RenderWindow->GetCameraRotationController(); } mitk::BaseController* QmitkSliceWidget::GetController() const { return m_RenderWindow->GetController(); } diff --git a/Modules/SceneSerializationBase/BasePropertySerializer/mitkClippingPropertySerializer.cpp b/Modules/SceneSerializationBase/BasePropertySerializer/mitkClippingPropertySerializer.cpp index 2b370791da..e767aff7d3 100644 --- a/Modules/SceneSerializationBase/BasePropertySerializer/mitkClippingPropertySerializer.cpp +++ b/Modules/SceneSerializationBase/BasePropertySerializer/mitkClippingPropertySerializer.cpp @@ -1,100 +1,100 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkClippingPropertySerializer_h_included #define mitkClippingPropertySerializer_h_included #include "mitkBasePropertySerializer.h" #include "mitkClippingProperty.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include namespace mitk { class MitkSceneSerializationBase_EXPORT ClippingPropertySerializer : public BasePropertySerializer { public: mitkClassMacro( ClippingPropertySerializer, BasePropertySerializer ); itkFactorylessNewMacro(Self) itkCloneMacro(Self) virtual TiXmlElement* Serialize() { if (const ClippingProperty* prop = dynamic_cast(m_Property.GetPointer())) { TiXmlElement* element = new TiXmlElement("clipping"); if (prop->GetClippingEnabled()) element->SetAttribute("enabled", "true"); else element->SetAttribute("enabled", "false"); TiXmlElement* originElement = new TiXmlElement("origin"); const Point3D origin = prop->GetOrigin(); originElement->SetDoubleAttribute("x", origin[0]); originElement->SetDoubleAttribute("y", origin[1]); originElement->SetDoubleAttribute("z", origin[2]); element->LinkEndChild(originElement); TiXmlElement* normalElement = new TiXmlElement("normal"); const Vector3D normal = prop->GetNormal(); normalElement->SetDoubleAttribute("x", normal[0]); normalElement->SetDoubleAttribute("y", normal[1]); normalElement->SetDoubleAttribute("z", normal[2]); element->LinkEndChild(normalElement); return element; } else return NULL; } virtual BaseProperty::Pointer Deserialize(TiXmlElement* element) { if (!element) return NULL; bool enabled = std::string(element->Attribute("enabled")) == "true"; TiXmlElement* originElement = element->FirstChildElement("origin"); if (originElement == NULL) return NULL; Point3D origin; if ( originElement->QueryDoubleAttribute( "x", &origin[0] ) != TIXML_SUCCESS ) return NULL; if ( originElement->QueryDoubleAttribute( "y", &origin[1] ) != TIXML_SUCCESS ) return NULL; if ( originElement->QueryDoubleAttribute( "z", &origin[2] ) != TIXML_SUCCESS ) return NULL; TiXmlElement* normalElement = element->FirstChildElement("normal"); if (normalElement == NULL) return NULL; Vector3D normal; if ( normalElement->QueryDoubleAttribute( "x", &normal[0] ) != TIXML_SUCCESS ) return NULL; if ( normalElement->QueryDoubleAttribute( "y", &normal[1] ) != TIXML_SUCCESS ) return NULL; if ( normalElement->QueryDoubleAttribute( "z", &normal[2] ) != TIXML_SUCCESS ) return NULL; ClippingProperty::Pointer cp = ClippingProperty::New(origin, normal); cp->SetClippingEnabled(enabled); return cp.GetPointer(); } protected: ClippingPropertySerializer() {} virtual ~ClippingPropertySerializer() {} }; } // namespace // important to put this into the GLOBAL namespace (because it starts with 'namespace mitk') MITK_REGISTER_SERIALIZER(ClippingPropertySerializer); #endif diff --git a/Modules/Segmentation/DataManagement/mitkContour.h b/Modules/Segmentation/DataManagement/mitkContour.h index 7a2f13f583..56fb93476a 100644 --- a/Modules/Segmentation/DataManagement/mitkContour.h +++ b/Modules/Segmentation/DataManagement/mitkContour.h @@ -1,185 +1,186 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITK_CONTOUR_H_ #define _MITK_CONTOUR_H_ #include "mitkCommon.h" #include #include "mitkBaseData.h" #include #include namespace mitk { /** \brief Stores vertices for drawing a contour. \deprecated Use class mitk::ContourModel instead. \sa ContourModel */ class MitkSegmentation_EXPORT Contour : public BaseData { public: mitkClassMacro(Contour, BaseData); itkFactorylessNewMacro(Self) itkCloneMacro(Self) 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 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( const itk::DataObject *data); protected: mitkCloneMacro(Self); 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/Segmentation/DataManagement/mitkExtrudedContour.cpp b/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp index dd4ba243af..08db95c4bc 100644 --- a/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp +++ b/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp @@ -1,373 +1,373 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkExtrudedContour.h" -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include "mitkBaseProcess.h" #include "mitkProportionalTimeGeometry.h" #include #include #include #include #include #include #include #include #include #include //vtkButterflySubdivisionFilter * subdivs; #include #include #include #include #include mitk::ExtrudedContour::ExtrudedContour() : m_Contour(NULL), m_ClippingGeometry(NULL), m_AutomaticVectorGeneration(false) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(1); SetTimeGeometry(timeGeometry); FillVector3D(m_Vector, 0.0, 0.0, 1.0); m_RightVector.Fill(0.0); m_ExtrusionFilter = vtkLinearExtrusionFilter::New(); m_ExtrusionFilter->CappingOff(); m_ExtrusionFilter->SetExtrusionTypeToVectorExtrusion(); double vtkvector[3]={0,0,1}; // set extrusion vector m_ExtrusionFilter->SetVector(vtkvector); m_TriangleFilter = vtkTriangleFilter::New(); m_TriangleFilter->SetInputConnection(m_ExtrusionFilter->GetOutputPort()); m_SubdivisionFilter = vtkLinearSubdivisionFilter::New(); m_SubdivisionFilter->SetInputConnection(m_TriangleFilter->GetOutputPort()); m_SubdivisionFilter->SetNumberOfSubdivisions(4); m_ClippingBox = vtkPlanes::New(); m_ClipPolyDataFilter = vtkClipPolyData::New(); m_ClipPolyDataFilter->SetInputConnection(m_SubdivisionFilter->GetOutputPort()); m_ClipPolyDataFilter->SetClipFunction(m_ClippingBox); m_ClipPolyDataFilter->InsideOutOn(); m_Polygon = vtkPolygon::New(); m_ProjectionPlane = mitk::PlaneGeometry::New(); } mitk::ExtrudedContour::~ExtrudedContour() { m_ClipPolyDataFilter->Delete(); m_ClippingBox->Delete(); m_SubdivisionFilter->Delete(); m_TriangleFilter->Delete(); m_ExtrusionFilter->Delete(); m_Polygon->Delete(); } bool mitk::ExtrudedContour::IsInside(const Point3D& worldPoint) const { static double polygonNormal[3]={0.0,0.0,1.0}; // project point onto plane float xt[3]; itk2vtk(worldPoint, xt); xt[0] = worldPoint[0]-m_Origin[0]; xt[1] = worldPoint[1]-m_Origin[1]; xt[2] = worldPoint[2]-m_Origin[2]; float dist=xt[0]*m_Normal[0]+xt[1]*m_Normal[1]+xt[2]*m_Normal[2]; xt[0] -= dist*m_Normal[0]; xt[1] -= dist*m_Normal[1]; xt[2] -= dist*m_Normal[2]; double x[3]; x[0] = xt[0]*m_Right[0]+xt[1]*m_Right[1]+xt[2]*m_Right[2]; x[1] = xt[0]*m_Down[0] +xt[1]*m_Down[1] +xt[2]*m_Down[2]; x[2] = 0; // determine whether it's in the selection loop and then evaluate point // in polygon only if absolutely necessary. if ( x[0] >= this->m_ProjectedContourBounds[0] && x[0] <= this->m_ProjectedContourBounds[1] && x[1] >= this->m_ProjectedContourBounds[2] && x[1] <= this->m_ProjectedContourBounds[3] && this->m_Polygon->PointInPolygon(x, m_Polygon->Points->GetNumberOfPoints(), ((vtkDoubleArray *)this->m_Polygon->Points->GetData())->GetPointer(0), (double*)const_cast(this)->m_ProjectedContourBounds, polygonNormal) == 1 ) return true; else return false; } mitk::ScalarType mitk::ExtrudedContour::GetVolume() { return -1.0; } void mitk::ExtrudedContour::UpdateOutputInformation() { if ( this->GetSource() ) { this->GetSource()->UpdateOutputInformation(); } if(GetMTime() > m_LastCalculateExtrusionTime) { BuildGeometry(); BuildSurface(); } //if ( ( m_CalculateBoundingBox ) && ( m_PolyDataSeries.size() > 0 ) ) // CalculateBoundingBox(); } void mitk::ExtrudedContour::BuildSurface() { if(m_Contour.IsNull()) { SetVtkPolyData(NULL); return; } // set extrusion contour vtkPolyData *polyData = vtkPolyData::New(); vtkCellArray *polys = vtkCellArray::New(); polys->InsertNextCell(m_Polygon->GetPointIds()); polyData->SetPoints(m_Polygon->GetPoints()); //float vtkpoint[3]; //unsigned int i, numPts = m_Polygon->GetNumberOfPoints(); //for(i=0; im_Polygon->Points->GetPoint(i); // pointids[i]=loopPoints->InsertNextPoint(vtkpoint); //} //polys->InsertNextCell( i, pointids ); //delete [] pointids; //polyData->SetPoints( loopPoints ); polyData->SetPolys( polys ); polys->Delete(); m_ExtrusionFilter->SetInputData(polyData); polyData->Delete(); // set extrusion scale factor m_ExtrusionFilter->SetScaleFactor(GetGeometry()->GetExtentInMM(2)); SetVtkPolyData(m_SubdivisionFilter->GetOutput()); //if(m_ClippingGeometry.IsNull()) //{ // SetVtkPolyData(m_SubdivisionFilter->GetOutput()); //} //else //{ // m_ClipPolyDataFilter->SetInput(m_SubdivisionFilter->GetOutput()); // mitk::BoundingBox::BoundsArrayType bounds=m_ClippingGeometry->GetBounds(); // m_ClippingBox->SetBounds(bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]); // m_ClippingBox->SetTransform(GetGeometry()->GetVtkTransform()); // m_ClipPolyDataFilter->SetClipFunction(m_ClippingBox); // m_ClipPolyDataFilter->SetValue(0); // SetVtkPolyData(m_ClipPolyDataFilter->GetOutput()); //} m_LastCalculateExtrusionTime.Modified(); } void mitk::ExtrudedContour::BuildGeometry() { if(m_Contour.IsNull()) return; // Initialize(1); Vector3D nullvector; nullvector.Fill(0.0); float xProj[3]; unsigned int i; unsigned int numPts = 20; //m_Contour->GetNumberOfPoints(); mitk::Contour::PathPointer path = m_Contour->GetContourPath(); mitk::Contour::PathType::InputType cstart = path->StartOfInput(); mitk::Contour::PathType::InputType cend = path->EndOfInput(); mitk::Contour::PathType::InputType cstep = (cend-cstart)/numPts; mitk::Contour::PathType::InputType ccur; // Part I: guarantee/calculate legal vectors m_Vector.Normalize(); itk2vtk(m_Vector, m_Normal); // check m_Vector if(mitk::Equal(m_Vector, nullvector) || m_AutomaticVectorGeneration) { if ( m_AutomaticVectorGeneration == false) itkWarningMacro("Extrusion vector is 0 ("<< m_Vector << "); trying to use normal of polygon"); vtkPoints *loopPoints = vtkPoints::New(); //mitk::Contour::PointsContainerIterator pointsIt = m_Contour->GetPoints()->Begin(); double vtkpoint[3]; unsigned int i=0; for(i=0, ccur=cstart; iEvaluate(ccur), vtkpoint); loopPoints->InsertNextPoint(vtkpoint); } // Make sure points define a loop with a m_Normal vtkPolygon::ComputeNormal(loopPoints, m_Normal); loopPoints->Delete(); vtk2itk(m_Normal, m_Vector); if(mitk::Equal(m_Vector, nullvector)) { itkExceptionMacro("Cannot calculate normal of polygon"); } } // check m_RightVector if((mitk::Equal(m_RightVector, nullvector)) || (mitk::Equal(m_RightVector*m_Vector, 0.0)==false)) { if(mitk::Equal(m_RightVector, nullvector)) { itkDebugMacro("Right vector is 0. Calculating."); } else { itkWarningMacro("Right vector ("<InitializeStandardPlane(rightDV, downDV); // create vtkPolygon from contour and simultaneously determine 2D bounds of // contour projected on m_ProjectionPlane //mitk::Contour::PointsContainerIterator pointsIt = m_Contour->GetPoints()->Begin(); m_Polygon->Points->Reset(); m_Polygon->Points->SetNumberOfPoints(numPts); m_Polygon->PointIds->Reset(); m_Polygon->PointIds->SetNumberOfIds(numPts); mitk::Point2D pt2d; mitk::Point3D pt3d; mitk::Point2D min, max; - min.Fill(ScalarTypeNumericTraits::max()); - max.Fill(ScalarTypeNumericTraits::min()); + min.Fill(itk::NumericTraits::max()); + max.Fill(itk::NumericTraits::min()); xProj[2]=0.0; for(i=0, ccur=cstart; iEvaluate(ccur)); m_ProjectionPlane->Map(pt3d, pt2d); xProj[0]=pt2d[0]; if(pt2d[0]max[0]) max[0]=pt2d[0]; xProj[1]=pt2d[1]; if(pt2d[1]max[1]) max[1]=pt2d[1]; m_Polygon->Points->SetPoint(i, xProj); m_Polygon->PointIds->SetId(i, i); } // shift parametric origin to (0,0) for(i=0; im_Polygon->Points->GetPoint(i); pt[0]-=min[0]; pt[1]-=min[1]; itkDebugMacro( << i << ": (" << pt[0] << "," << pt[1] << "," << pt[2] << ")" ); } this->m_Polygon->GetBounds(m_ProjectedContourBounds); //m_ProjectedContourBounds[4]=-1.0; m_ProjectedContourBounds[5]=1.0; // calculate origin (except translation along the normal) and bounds // of m_ProjectionPlane: // origin is composed of the minimum x-/y-coordinates of the polygon, // bounds from the extent of the polygon, both after projecting on the plane mitk::Point3D origin; m_ProjectionPlane->Map(min, origin); ScalarType bounds[6]={0, max[0]-min[0], 0, max[1]-min[1], 0, 1}; m_ProjectionPlane->SetBounds(bounds); m_ProjectionPlane->SetOrigin(origin); // Part III: initialize geometry if(m_ClippingGeometry.IsNotNull()) { - ScalarType min_dist=ScalarTypeNumericTraits::max(), max_dist=ScalarTypeNumericTraits::min(), dist; + ScalarType min_dist=itk::NumericTraits::max(), max_dist=itk::NumericTraits::min(), dist; unsigned char i; for(i=0; i<8; ++i) { dist = m_ProjectionPlane->SignedDistance( m_ClippingGeometry->GetCornerPoint(i) ); if(distmax_dist) max_dist=dist; } //incorporate translation along the normal into origin origin = origin+m_Vector*min_dist; m_ProjectionPlane->SetOrigin(origin); bounds[5]=max_dist-min_dist; } else bounds[5]=20; itk2vtk(origin, m_Origin); mitk::BaseGeometry::Pointer g3d = GetGeometry( 0 ); assert( g3d.IsNotNull() ); g3d->SetBounds(bounds); g3d->SetIndexToWorldTransform(m_ProjectionPlane->GetIndexToWorldTransform()); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(g3d,1); SetTimeGeometry(timeGeometry); } unsigned long mitk::ExtrudedContour::GetMTime() const { unsigned long latestTime = Superclass::GetMTime(); if(m_Contour.IsNotNull()) { unsigned long localTime; localTime = m_Contour->GetMTime(); if(localTime > latestTime) latestTime = localTime; } return latestTime; } diff --git a/Modules/Segmentation/Interactions/mitkContourInteractor.h b/Modules/Segmentation/Interactions/mitkContourInteractor.h index f05795f9f5..8a4a2d5006 100644 --- a/Modules/Segmentation/Interactions/mitkContourInteractor.h +++ b/Modules/Segmentation/Interactions/mitkContourInteractor.h @@ -1,65 +1,65 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __ContourInteractor_H #define __ContourInteractor_H #include "mitkCommon.h" #include #include -#include +#include namespace mitk { //##Documentation //## @brief Interactor for the creation of an mitk::Contour //## @ingroup Interaction class MitkSegmentation_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/Segmentation/Interactions/mitkExtrudedContourInteractor.h b/Modules/Segmentation/Interactions/mitkExtrudedContourInteractor.h index 42b0119d9e..3b370de2f6 100644 --- a/Modules/Segmentation/Interactions/mitkExtrudedContourInteractor.h +++ b/Modules/Segmentation/Interactions/mitkExtrudedContourInteractor.h @@ -1,72 +1,72 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __ExtrudedContourInteractor_H #define __ExtrudedContourInteractor_H #include "mitkCommon.h" #include #include -#include +#include #include #include namespace mitk { //##Documentation //## @brief Interactor for the creation of an mitk::Contour //## @ingroup Interaction class MitkSegmentation_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/Segmentation/Interactions/mitkFastMarchingTool.xpm b/Modules/Segmentation/Interactions/mitkFastMarchingTool.xpm index 928a87dc59..ee5b02c0bd 100644 --- a/Modules/Segmentation/Interactions/mitkFastMarchingTool.xpm +++ b/Modules/Segmentation/Interactions/mitkFastMarchingTool.xpm @@ -1,53 +1,53 @@ -/* XPM */ -static const char * mitkFastMarchingTool_xpm[] = { -"48 48 2 1", -" c None", -". c #000000", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ...... ", -" .......... ", -" ........... ", -" ... ..... ", -" . ... ", -" ... ", -" ... ", -" ... ", -" ... ", -" .... ", -" .... ", -" ..... ", -" .... ", -" ... ", -" ... ", -" ... ", -" ", -" ", -" ", -" ... ", -" ... ", -" ... ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" "}; +/* XPM */ +static const char * mitkFastMarchingTool_xpm[] = { +"48 48 2 1", +" c None", +". c #000000", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ...... ", +" .......... ", +" ........... ", +" ... ..... ", +" . ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" .... ", +" .... ", +" ..... ", +" .... ", +" ... ", +" ... ", +" ... ", +" ", +" ", +" ", +" ... ", +" ... ", +" ... ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.xpm b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.xpm index e5c4a22a1a..8b0e20771b 100644 --- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.xpm +++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.xpm @@ -1,99 +1,99 @@ -/* XPM */ -static const char * mitkLiveWireTool2D_xpm[] = { -"24 24 72 1", -" c None", -". c #179FBF", -"+ c #01AAD2", -"@ c #00AAD3", -"# c #0BA4CA", -"$ c #14A0C1", -"% c #11A2C4", -"& c #06A7CE", -"* c #0AA5CB", -"= c #2895B1", -"- c #229AB5", -"; c #02A9D1", -"> c #0DA3C8", -", c #1F99B7", -"' c #2598B5", -") c #0CA4C9", -"! c #13A1C2", -"~ c #03A8D0", -"{ c #2795B1", -"] c #179EBF", -"^ c #04A9D0", -"/ c #1C9BBA", -"( c #2497B3", -"_ c #1E9AB9", -": c #209AB9", -"< c #13A0C3", -"[ c #03A9D1", -"} c #189DBE", -"| c #07A7CD", -"1 c #1A9CBC", -"2 c #1E9BBA", -"3 c #2699B4", -"4 c #2498B4", -"5 c #229AB6", -"6 c #0EA3C8", -"7 c #07A7CE", -"8 c #189EBE", -"9 c #1C9CBA", -"0 c #04A7CF", -"a c #11A1C5", -"b c #07A6CD", -"c c #09A6CC", -"d c #2A94AD", -"e c #1C9CBB", -"f c #1E9BB8", -"g c #259AB4", -"h c #05A7CE", -"i c #2E92A9", -"j c #0EA2C6", -"k c #3A8FA2", -"l c #1B9CBC", -"m c #1D9BB9", -"n c #169FC1", -"o c #00A9D2", -"p c #1D9BBA", -"q c #2B94AC", -"r c #16A0C1", -"s c #10A2C6", -"t c #209BB9", -"u c #1D9CBB", -"v c #179FC0", -"w c #000000", -"x c #219AB4", -"y c #FFFFFF", -"z c #F3F6F1", -"A c #189EC0", -"B c #00AAD2", -"C c #16A7BC", -"D c #17A0BF", -"E c #2299B7", -"F c #1D9BB8", -"G c #1A9CBD", -" .+@#$%&@*= ", -" -;>, '); ", -" !~{ &) ", -" ]^ /@ ", -" (@_ :@ ", -" <[ }| ", -" @1 +2 ", -" @345 67 ", -" +@890 a^ ", -" }bcdef gh|i ", -" jk&@l(mn^op ", -" qr+@@@@st ", -" u& ", -" 0vwwwwwwwww ", -" @xwwwyyzyzww ", -" ABwwwwzzzzzzww ", -" oCwwwwwwwwzzzw ", -" D@ wwzzw ", -" c@@@ wzzw ", -" EFG wwzzw ", -" wwwwwwwwzzzw ", -" wwwwzzzzzyww ", -" wwwwyyyyyww ", -" wwwwwwwwww "}; +/* XPM */ +static const char * mitkLiveWireTool2D_xpm[] = { +"24 24 72 1", +" c None", +". c #179FBF", +"+ c #01AAD2", +"@ c #00AAD3", +"# c #0BA4CA", +"$ c #14A0C1", +"% c #11A2C4", +"& c #06A7CE", +"* c #0AA5CB", +"= c #2895B1", +"- c #229AB5", +"; c #02A9D1", +"> c #0DA3C8", +", c #1F99B7", +"' c #2598B5", +") c #0CA4C9", +"! c #13A1C2", +"~ c #03A8D0", +"{ c #2795B1", +"] c #179EBF", +"^ c #04A9D0", +"/ c #1C9BBA", +"( c #2497B3", +"_ c #1E9AB9", +": c #209AB9", +"< c #13A0C3", +"[ c #03A9D1", +"} c #189DBE", +"| c #07A7CD", +"1 c #1A9CBC", +"2 c #1E9BBA", +"3 c #2699B4", +"4 c #2498B4", +"5 c #229AB6", +"6 c #0EA3C8", +"7 c #07A7CE", +"8 c #189EBE", +"9 c #1C9CBA", +"0 c #04A7CF", +"a c #11A1C5", +"b c #07A6CD", +"c c #09A6CC", +"d c #2A94AD", +"e c #1C9CBB", +"f c #1E9BB8", +"g c #259AB4", +"h c #05A7CE", +"i c #2E92A9", +"j c #0EA2C6", +"k c #3A8FA2", +"l c #1B9CBC", +"m c #1D9BB9", +"n c #169FC1", +"o c #00A9D2", +"p c #1D9BBA", +"q c #2B94AC", +"r c #16A0C1", +"s c #10A2C6", +"t c #209BB9", +"u c #1D9CBB", +"v c #179FC0", +"w c #000000", +"x c #219AB4", +"y c #FFFFFF", +"z c #F3F6F1", +"A c #189EC0", +"B c #00AAD2", +"C c #16A7BC", +"D c #17A0BF", +"E c #2299B7", +"F c #1D9BB8", +"G c #1A9CBD", +" .+@#$%&@*= ", +" -;>, '); ", +" !~{ &) ", +" ]^ /@ ", +" (@_ :@ ", +" <[ }| ", +" @1 +2 ", +" @345 67 ", +" +@890 a^ ", +" }bcdef gh|i ", +" jk&@l(mn^op ", +" qr+@@@@st ", +" u& ", +" 0vwwwwwwwww ", +" @xwwwyyzyzww ", +" ABwwwwzzzzzzww ", +" oCwwwwwwwwzzzw ", +" D@ wwzzw ", +" c@@@ wzzw ", +" EFG wwzzw ", +" wwwwwwwwzzzw ", +" wwwwzzzzzyww ", +" wwwwyyyyyww ", +" wwwwwwwwww "}; diff --git a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp index b7a97e7ca1..02184e6764 100644 --- a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp +++ b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp @@ -1,282 +1,282 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include int ObliquePlaneTestVolumeSize = 128; static void OverwriteObliquePlaneTest(mitk::Image* workingImage, mitk::Image* refImg) { /*==============TEST WITHOUT MITK CONVERTION=============================*/ /* ============= setup plane ============*/ int sliceindex = (int)(ObliquePlaneTestVolumeSize /2);//rand() % 32; bool isFrontside = true; bool isRotated = false; mitk::PlaneGeometry::Pointer obliquePlane = mitk::PlaneGeometry::New(); obliquePlane->InitializeStandardPlane(workingImage->GetGeometry(), mitk::PlaneGeometry::Axial, sliceindex, isFrontside, isRotated); mitk::Point3D origin = obliquePlane->GetOrigin(); mitk::Vector3D normal; normal = obliquePlane->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 obliquePlane->SetOrigin(origin); mitk::Vector3D rotationVector = obliquePlane->GetAxisVector(0); rotationVector.Normalize(); float degree = 45.0; mitk::RotationOperation* op = new mitk::RotationOperation(mitk::OpROTATE, obliquePlane->GetCenter(), rotationVector, degree); obliquePlane->ExecuteOperation(op); delete op; /* ============= extract slice ============*/ mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(); slicer->SetInput(workingImage); slicer->SetWorldGeometry(obliquePlane); slicer->SetVtkOutputRequest(true); slicer->Modified(); slicer->Update(); vtkSmartPointer slice = vtkSmartPointer::New(); slice = slicer->GetVtkOutput(); /* ============= overwrite slice ============*/ vtkSmartPointer resliceIdx = vtkSmartPointer::New(); mitk::ExtractSliceFilter::Pointer overwriter = mitk::ExtractSliceFilter::New(resliceIdx); resliceIdx->SetOverwriteMode(true); resliceIdx->SetInputSlice(slice); resliceIdx->Modified(); overwriter->SetInput(workingImage); overwriter->SetWorldGeometry(obliquePlane); overwriter->SetVtkOutputRequest(true); overwriter->Modified(); overwriter->Update(); typedef mitk::ImagePixelReadAccessor< unsigned short, 3 > ReadAccessorType; ReadAccessorType refImgReadAccessor( refImg ); ReadAccessorType workingImgReadAccessor( workingImage ); /* ============= check ref == working ============*/ bool areSame = true; - mitk::Index3D id; + itk::Index<3> id; id[0] = id[1] = id[2] = 0; for (int x = 0; x < ObliquePlaneTestVolumeSize ; ++x){ id[0] = x; for (int y = 0; y < ObliquePlaneTestVolumeSize ; ++y){ id[1] = y; for (int z = 0; z < ObliquePlaneTestVolumeSize ; ++z){ id[2] = z; areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id); if(!areSame) goto stop; } } } stop: MITK_TEST_CONDITION(areSame,"comparing images (no mitk convertion) [oblique]"); /*==============TEST WITH MITK CONVERTION=============================*/ /* ============= extract slice ============*/ mitk::ExtractSliceFilter::Pointer slicer2 = mitk::ExtractSliceFilter::New(); slicer2->SetInput(workingImage); slicer2->SetWorldGeometry(obliquePlane); slicer2->Modified(); slicer2->Update(); mitk::Image::Pointer sliceInMitk = slicer2->GetOutput(); vtkSmartPointer slice2 = vtkSmartPointer::New(); slice2 = sliceInMitk->GetVtkImageData(); /* ============= overwrite slice ============*/ vtkSmartPointer resliceIdx2 = vtkSmartPointer::New(); mitk::ExtractSliceFilter::Pointer overwriter2 = mitk::ExtractSliceFilter::New(resliceIdx2); resliceIdx2->SetOverwriteMode(true); resliceIdx2->SetInputSlice(slice2); resliceIdx2->Modified(); overwriter2->SetInput(workingImage); overwriter2->SetWorldGeometry(obliquePlane); overwriter2->SetVtkOutputRequest(true); overwriter2->Modified(); overwriter2->Update(); /* ============= check ref == working ============*/ areSame = true; id[0] = id[1] = id[2] = 0; for (int x = 0; x < ObliquePlaneTestVolumeSize ; ++x){ id[0] = x; for (int y = 0; y < ObliquePlaneTestVolumeSize ; ++y){ id[1] = y; for (int z = 0; z < ObliquePlaneTestVolumeSize ; ++z){ id[2] = z; areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id); if(!areSame) goto stop2; } } } stop2: MITK_TEST_CONDITION(areSame,"comparing images (with mitk convertion) [oblique]"); /*==============TEST EDIT WITHOUT MITK CONVERTION=============================*/ /* ============= edit slice ============*/ int idX = std::abs(ObliquePlaneTestVolumeSize -59); int idY = std::abs(ObliquePlaneTestVolumeSize -23); int idZ = 0; int component = 0; double val = 33.0; slice->SetScalarComponentFromDouble(idX,idY,idZ,component,val); mitk::Vector3D indx; indx[0] = idX; indx[1] = idY; indx[2] = idZ; sliceInMitk->GetGeometry()->IndexToWorld(indx, indx); /* ============= overwrite slice ============*/ vtkSmartPointer resliceIdx3 = vtkSmartPointer::New(); resliceIdx3->SetOverwriteMode(true); resliceIdx3->SetInputSlice(slice); mitk::ExtractSliceFilter::Pointer overwriter3 = mitk::ExtractSliceFilter::New(resliceIdx3); overwriter3->SetInput(workingImage); overwriter3->SetWorldGeometry(obliquePlane); overwriter3->SetVtkOutputRequest(true); overwriter3->Modified(); overwriter3->Update(); /* ============= check ============*/ areSame = true; int x,y,z; for ( x = 0; x < ObliquePlaneTestVolumeSize ; ++x){ id[0] = x; for ( y = 0; y < ObliquePlaneTestVolumeSize ; ++y){ id[1] = y; for ( z = 0; z < ObliquePlaneTestVolumeSize ; ++z){ id[2] = z; areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id); if(!areSame) goto stop3; } } } stop3: //MITK_INFO << "index: [" << x << ", " << y << ", " << z << "]"; //MITK_INFO << indx; MITK_TEST_CONDITION(x==idX && y==z,"overwrited the right index [oblique]"); } /*================ #BEGIN test main ================*/ int mitkOverwriteSliceFilterObliquePlaneTest(int argc, char* argv[]) { MITK_TEST_BEGIN("mitkOverwriteSliceFilterObliquePlaneTest") typedef itk::Image ImageType; typedef itk::ImageRegionConstIterator< ImageType > ImageIterator; ImageType::Pointer image = ImageType::New(); ImageType::IndexType start; start[0] = start[1] = start[2] = 0; ImageType::SizeType size; size[0] = size[1] = size[2] = ObliquePlaneTestVolumeSize ; ImageType::RegionType imgRegion; imgRegion.SetSize(size); imgRegion.SetIndex(start); image->SetRegions(imgRegion); image->SetSpacing(1.0); image->Allocate(); ImageIterator imageIterator( image, image->GetLargestPossibleRegion() ); imageIterator.GoToBegin(); unsigned short pixelValue = 0; //fill the image with distinct values while ( !imageIterator.IsAtEnd() ) { image->SetPixel(imageIterator.GetIndex(), pixelValue); ++imageIterator; ++pixelValue; } /* end setup itk image */ mitk::Image::Pointer refImage; CastToMitkImage(image, refImage); mitk::Image::Pointer workingImg; CastToMitkImage(image, workingImg); OverwriteObliquePlaneTest(workingImg, refImage); MITK_TEST_END() } diff --git a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp index ecd4edef8c..36f9f1a528 100644 --- a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp +++ b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp @@ -1,205 +1,205 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include int VolumeSize = 128; /*================ #BEGIN test main ================*/ int mitkOverwriteSliceFilterTest(int argc, char* argv[]) { MITK_TEST_BEGIN("mitkOverwriteSliceFilterTest") typedef itk::Image ImageType; typedef itk::ImageRegionConstIterator< ImageType > ImageIterator; ImageType::Pointer image = ImageType::New(); ImageType::IndexType start; start[0] = start[1] = start[2] = 0; ImageType::SizeType size; size[0] = size[1] = size[2] = VolumeSize; ImageType::RegionType imgRegion; imgRegion.SetSize(size); imgRegion.SetIndex(start); image->SetRegions(imgRegion); image->SetSpacing(1.0); image->Allocate(); ImageIterator imageIterator( image, image->GetLargestPossibleRegion() ); imageIterator.GoToBegin(); unsigned short pixelValue = 0; //fill the image with distinct values while ( !imageIterator.IsAtEnd() ) { image->SetPixel(imageIterator.GetIndex(), pixelValue); ++imageIterator; ++pixelValue; } /* end setup itk image */ mitk::Image::Pointer referenceImage; CastToMitkImage(image, referenceImage); mitk::Image::Pointer workingImage; CastToMitkImage(image, workingImage); typedef mitk::ImagePixelReadAccessor< unsigned short, 3 > ReadAccessorType; ReadAccessorType refImgReadAccessor( referenceImage ); ReadAccessorType workingImgReadAccessor( workingImage ); /* ============= setup plane ============*/ int sliceindex = 55;//rand() % 32; bool isFrontside = true; bool isRotated = false; mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(workingImage->GetGeometry(), mitk::PlaneGeometry::Axial, sliceindex, isFrontside, isRotated); mitk::Point3D origin = plane->GetOrigin(); mitk::Vector3D normal; normal = plane->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 plane->SetOrigin(origin); /* ============= extract slice ============*/ vtkSmartPointer resliceIdx = vtkSmartPointer::New(); mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(resliceIdx); slicer->SetInput(workingImage); slicer->SetWorldGeometry(plane); slicer->SetVtkOutputRequest(true); slicer->Modified(); slicer->Update(); vtkSmartPointer slice = vtkSmartPointer::New(); slice = slicer->GetVtkOutput(); /* ============= overwrite slice ============*/ resliceIdx->SetOverwriteMode(true); resliceIdx->Modified(); slicer->Modified(); slicer->Update();//implicit overwrite /* ============= check ref == working ============*/ bool areSame = true; - mitk::Index3D id; + itk::Index<3> id; id[0] = id[1] = id[2] = 0; for (int x = 0; x < VolumeSize; ++x){ id[0] = x; for (int y = 0; y < VolumeSize; ++y){ id[1] = y; for (int z = 0; z < VolumeSize; ++z){ id[2] = z; areSame = refImgReadAccessor.GetPixelByIndex( id ) == workingImgReadAccessor.GetPixelByIndex( id ); if(!areSame) goto stop; } } } stop: MITK_TEST_CONDITION(areSame,"test overwrite unmodified slice"); /* ============= edit slice ============*/ int idX = std::abs(VolumeSize-59); int idY = std::abs(VolumeSize-23); int idZ = 0; int component = 0; double val = 33.0; slice->SetScalarComponentFromDouble(idX,idY,idZ,component,val); /* ============= overwrite slice ============*/ vtkSmartPointer resliceIdx2 = vtkSmartPointer::New(); resliceIdx2->SetOverwriteMode(true); resliceIdx2->SetInputSlice(slice); mitk::ExtractSliceFilter::Pointer slicer2 = mitk::ExtractSliceFilter::New(resliceIdx2); slicer2->SetInput(workingImage); slicer2->SetWorldGeometry(plane); slicer2->SetVtkOutputRequest(true); slicer2->Modified(); slicer2->Update(); /* ============= check ============*/ areSame = true; int xx,yy,zz; for ( xx = 0; xx < VolumeSize; ++xx){ id[0] = xx; for ( yy = 0; yy < VolumeSize; ++yy){ id[1] = yy; for ( zz = 0; zz < VolumeSize; ++zz){ id[2] = zz; areSame = refImgReadAccessor.GetPixelByIndex( id ) == workingImgReadAccessor.GetPixelByIndex( id ); if(!areSame) goto stop2; } } } stop2: //MITK_INFO << "index: [" << x << ", " << y << ", " << z << "]"; MITK_TEST_CONDITION(xx==idX && yy==idY && zz==sliceindex,"test overwrite modified slice"); MITK_TEST_END() } diff --git a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp index cb50c0513c..3d241c0a49 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp @@ -1,848 +1,848 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkAdaptiveRegionGrowingToolGUI.h" #include "QmitkStdMultiWidget.h" #include #include "mitkNodePredicateDataType.h" #include "mitkGlobalInteraction.h" #include "mitkPointSetInteractor.h" #include "mitkProperties.h" #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkTransferFunctionProperty.h" #include "mitkImageTimeSelector.h" #include "mitkImageStatisticsHolder.h" #include #include #include #include #include "itkOrImageFilter.h" #include "mitkImageCast.h" #include "QmitkConfirmSegmentationDialog.h" #include "mitkPixelTypeMultiplex.h" #include "mitkImagePixelReadAccessor.h" MITK_TOOL_GUI_MACRO( , QmitkAdaptiveRegionGrowingToolGUI, "") QmitkAdaptiveRegionGrowingToolGUI::QmitkAdaptiveRegionGrowingToolGUI(QWidget* parent) : QmitkToolGUI(), m_MultiWidget(NULL), m_UseVolumeRendering(false), m_UpdateSuggestedThreshold(true), m_SuggestedThValue(0.0), m_DataStorage(NULL) { this->setParent(parent); m_Controls.setupUi(this); m_Controls.m_ThresholdSlider->setDecimals(1); m_Controls.m_ThresholdSlider->setSpinBoxAlignment(Qt::AlignVCenter); m_Controls.m_PreviewSlider->setEnabled(false); m_Controls.m_PreviewSlider->setSingleStep(0.5); //Not yet available //m_Controls.m_PreviewSlider->InvertedAppearance(true); this->CreateConnections(); this->SetDataNodeNames("labeledRGSegmentation","RGResult","RGFeedbackSurface"); connect( this, SIGNAL(NewToolAssociated(mitk::Tool*)), this, SLOT(OnNewToolAssociated(mitk::Tool*)) ); } QmitkAdaptiveRegionGrowingToolGUI::~QmitkAdaptiveRegionGrowingToolGUI() { //Removing the observer of the PointSet node if (m_RegionGrow3DTool->GetPointSetNode().IsNotNull()) { m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetAddObserverTag); } this->RemoveHelperNodes(); } void QmitkAdaptiveRegionGrowingToolGUI::OnNewToolAssociated(mitk::Tool* tool) { m_RegionGrow3DTool = dynamic_cast (tool); if(m_RegionGrow3DTool.IsNotNull()) { SetInputImageNode( this->m_RegionGrow3DTool->GetReferenceData() ); this->m_DataStorage = this->m_RegionGrow3DTool->GetDataStorage(); this->EnableControls(true); //Watch for point added or modified itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded); m_PointSetAddObserverTag = m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver( mitk::PointSetAddEvent(), pointAddedCommand); } else { this->EnableControls(false); } } void QmitkAdaptiveRegionGrowingToolGUI::RemoveHelperNodes() { mitk::DataNode::Pointer imageNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if( imageNode.IsNotNull() ) { m_DataStorage->Remove(imageNode); } } void QmitkAdaptiveRegionGrowingToolGUI::CreateConnections() { //Connecting GUI components connect( (QObject*) (m_Controls.m_pbRunSegmentation), SIGNAL(clicked()), this, SLOT(RunSegmentation())); connect( m_Controls.m_PreviewSlider, SIGNAL(valueChanged(double)), this, SLOT(ChangeLevelWindow(double))); connect( (QObject*) (m_Controls.m_pbConfirmSegementation), SIGNAL(clicked()), this, SLOT(ConfirmSegmentation())); connect( (QObject*) (m_Controls.m_cbVolumeRendering), SIGNAL(toggled(bool)), this, SLOT(UseVolumeRendering(bool) )); connect( m_Controls.m_ThresholdSlider, SIGNAL(maximumValueChanged(double)), this, SLOT(SetUpperThresholdValue(double))); connect( m_Controls.m_ThresholdSlider, SIGNAL(minimumValueChanged(double)), this, SLOT(SetLowerThresholdValue(double))); } void QmitkAdaptiveRegionGrowingToolGUI::SetDataNodeNames(std::string labledSegmentation, std::string binaryImage, std::string surface) { m_NAMEFORLABLEDSEGMENTATIONIMAGE = labledSegmentation; m_NAMEFORBINARYIMAGE = binaryImage; m_NAMEFORSURFACE = surface; } void QmitkAdaptiveRegionGrowingToolGUI::SetDataStorage(mitk::DataStorage* dataStorage) { m_DataStorage = dataStorage; } void QmitkAdaptiveRegionGrowingToolGUI::SetMultiWidget(QmitkStdMultiWidget* multiWidget) { m_MultiWidget = multiWidget; } void QmitkAdaptiveRegionGrowingToolGUI::SetInputImageNode(mitk::DataNode* node) { m_InputImageNode = node; mitk::Image* inputImage = dynamic_cast(m_InputImageNode->GetData()); if (inputImage) { mitk::ScalarType max = inputImage->GetStatistics()->GetScalarValueMax(); mitk::ScalarType min = inputImage->GetStatistics()->GetScalarValueMin(); m_Controls.m_ThresholdSlider->setMaximum(max); m_Controls.m_ThresholdSlider->setMinimum(min); // Just for initialization m_Controls.m_ThresholdSlider->setMaximumValue(max); m_Controls.m_ThresholdSlider->setMinimumValue(min); } } template static void AccessPixel(mitk::PixelType ptype, const mitk::Image::Pointer im, mitk::Point3D p, int & val) { mitk::ImagePixelReadAccessor access(im); val = access.GetPixelByWorldCoordinates(p); } void QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded() { if (m_RegionGrow3DTool.IsNull()) return; mitk::DataNode* node = m_RegionGrow3DTool->GetPointSetNode(); if (node != NULL) { mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNull()) { QMessageBox::critical(NULL, "QmitkAdaptiveRegionGrowingToolGUI", "PointSetNode does not contain a pointset"); return; } m_Controls.m_lblSetSeedpoint->setText(""); mitk::Image* image = dynamic_cast(m_InputImageNode->GetData()); mitk::Point3D seedPoint = pointSet->GetPointSet(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1") )->GetTimeStep())->GetPoints()->ElementAt(0); mitkPixelTypeMultiplex3(AccessPixel,image->GetChannelDescriptor().GetPixelType(),image,seedPoint,m_SeedpointValue); /* In this case the seedpoint is placed e.g. in the lung or bronchialtree * The lowerFactor sets the windowsize depending on the regiongrowing direction */ m_CurrentRGDirectionIsUpwards = true; if (m_SeedpointValue < -500) { m_CurrentRGDirectionIsUpwards = false; } // Initializing the region by the area around the seedpoint m_SeedPointValueMean = 0; - mitk::Index3D currentIndex, runningIndex; + itk::Index<3> currentIndex, runningIndex; mitk::ScalarType pixelValues[125]; unsigned int pos (0); image->GetGeometry(0)->WorldToIndex(seedPoint, currentIndex); runningIndex = currentIndex; for(int i = runningIndex[0]-2; i <= runningIndex[0]+2; i++) { for(int j = runningIndex[1]-2; j <= runningIndex[1]+2; j++) { for(int k = runningIndex[2]-2; k <= runningIndex[2]+2; k++) { currentIndex[0] = i; currentIndex[1] = j; currentIndex[2] = k; if(image->GetGeometry()->IsIndexInside(currentIndex)) { pixelValues[pos] = image->GetPixelValueByIndex(currentIndex); pos++; } else { pixelValues[pos] = -10000000; pos++; } } } } //Now calculation mean of the pixelValues unsigned int numberOfValues(0); for (unsigned int i = 0; i < 125; i++) { if(pixelValues[i] > -10000000) { m_SeedPointValueMean += pixelValues[i]; numberOfValues++; } } m_SeedPointValueMean = m_SeedPointValueMean/numberOfValues; /* * Here the upper- and lower threshold is calculated: * The windowSize is 20% of the maximum range of the intensity values existing in the current image * If the RG direction is upwards the lower TH is meanSeedValue-0.15*windowSize and upper TH is meanSeedValue+0.85*windowsSize * if the RG direction is downwards the lower TH is meanSeedValue-0.85*windowSize and upper TH is meanSeedValue+0.15*windowsSize */ mitk::ScalarType min = image->GetStatistics()->GetScalarValueMin(); mitk::ScalarType max = image->GetStatistics()->GetScalarValueMax(); mitk::ScalarType windowSize = max - min; windowSize = 0.15*windowSize; if (m_CurrentRGDirectionIsUpwards) { m_LOWERTHRESHOLD = m_SeedPointValueMean; if (m_SeedpointValue < m_SeedPointValueMean) m_LOWERTHRESHOLD = m_SeedpointValue; m_UPPERTHRESHOLD = m_SeedpointValue + windowSize; if (m_UPPERTHRESHOLD > max) m_UPPERTHRESHOLD = max; m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); } else { m_UPPERTHRESHOLD = m_SeedPointValueMean; if (m_SeedpointValue > m_SeedPointValueMean) m_UPPERTHRESHOLD = m_SeedpointValue; m_LOWERTHRESHOLD = m_SeedpointValue - windowSize; if (m_LOWERTHRESHOLD < min) m_LOWERTHRESHOLD = min; m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); } } } void QmitkAdaptiveRegionGrowingToolGUI::RunSegmentation() { if (m_InputImageNode.IsNull()) { QMessageBox::information( NULL, "Adaptive Region Growing functionality", "Please specify the image in Datamanager!"); return; } mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { QMessageBox::information( NULL, "Adaptive Region Growing functionality", "Please insert a seed point inside the image.\n\nFirst press the \"Define Seed Point\" button,\nthen click left mouse button inside the image."); return; } //safety if no pointSet or pointSet empty mitk::PointSet::Pointer seedPointSet = dynamic_cast (node->GetData()); if (seedPointSet.IsNull()) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( NULL, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } int timeStep = mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1") )->GetTimeStep(); if (!(seedPointSet->GetSize(timeStep))) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( NULL, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); mitk::PointSet::PointType seedPoint = seedPointSet->GetPointSet(timeStep)->GetPoints()->Begin().Value(); mitk::Image::Pointer orgImage = dynamic_cast (m_InputImageNode->GetData()); if (orgImage.IsNotNull()) { if (orgImage->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(orgImage); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); mitk::Image* timedImage = timeSelector->GetOutput(); AccessByItk_2( timedImage , StartRegionGrowing, timedImage->GetGeometry(), seedPoint); } else if (orgImage->GetDimension() == 3) { //QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); //set the cursor to waiting AccessByItk_2(orgImage, StartRegionGrowing, orgImage->GetGeometry(), seedPoint); //QApplication::restoreOverrideCursor();//reset cursor } else { QApplication::restoreOverrideCursor();//reset cursor QMessageBox::information( NULL, "Adaptive Region Growing functionality", "Only images of dimension 3 or 4 can be processed!"); return; } } EnableControls(true); // Segmentation ran successfully, so enable all controls. node->SetVisibility(true); QApplication::restoreOverrideCursor();//reset cursor } template void QmitkAdaptiveRegionGrowingToolGUI::StartRegionGrowing(itk::Image* itkImage, mitk::BaseGeometry* imageGeometry, mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedAdaptiveThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); typedef itk::MinimumMaximumImageCalculator MinMaxValueFilterType; if ( !imageGeometry->IsInside(seedPoint) ) { QApplication::restoreOverrideCursor();//reset cursor to be able to click ok with the regular mouse cursor QMessageBox::information( NULL, "Segmentation functionality", "The seed point is outside of the image! Please choose a position inside the image!"); return; } IndexType seedIndex; imageGeometry->WorldToIndex( seedPoint, seedIndex);// convert world coordinates to image indices if (m_SeedpointValue>m_UPPERTHRESHOLD || m_SeedpointValueSetGrowingDirectionIsUpwards( m_CurrentRGDirectionIsUpwards ); regionGrower->SetInput( itkImage ); regionGrower->AddSeed( seedIndex ); //In some cases we have to subtract 1 for the lower threshold and add 1 to the upper. //Otherwise no region growing is done. Maybe a bug in the ConnectiveAdaptiveThresholdFilter regionGrower->SetLower( m_LOWERTHRESHOLD-1 ); regionGrower->SetUpper( m_UPPERTHRESHOLD+1); try { regionGrower->Update(); } catch(itk::ExceptionObject &exc) { QMessageBox errorInfo; errorInfo.setWindowTitle("Adaptive RG Segmentation Functionality"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during region growing!"); errorInfo.setDetailedText(exc.what()); errorInfo.exec(); return; // can't work } catch( ... ) { QMessageBox::critical( NULL, "Adaptive RG Segmentation Functionality", "An error occurred during region growing!"); return; } mitk::Image::Pointer resultImage = mitk::ImportItkImage(regionGrower->GetOutput())->Clone(); //initialize slider m_Controls.m_PreviewSlider->setMinimum(m_LOWERTHRESHOLD); mitk::ScalarType max = m_LOWERTHRESHOLD+resultImage->GetStatistics()->GetScalarValueMax(); if (max < m_UPPERTHRESHOLD) m_Controls.m_PreviewSlider->setMaximum(max); else m_Controls.m_PreviewSlider->setMaximum(m_UPPERTHRESHOLD); this->m_DetectedLeakagePoint = regionGrower->GetLeakagePoint(); if(m_CurrentRGDirectionIsUpwards) { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean-1); } else { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean+1); } this->m_SliderInitialized = true; //create new node and then delete the old one if there is one mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetData( resultImage ); // set some properties newNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORLABLEDSEGMENTATIONIMAGE)); newNode->SetProperty("helper object", mitk::BoolProperty::New(true)); newNode->SetProperty("color", mitk::ColorProperty::New(0.0,1.0,0.0)); newNode->SetProperty("layer", mitk::IntProperty::New(1)); newNode->SetProperty("opacity", mitk::FloatProperty::New(0.7)); //delete the old image, if there was one: mitk::DataNode::Pointer binaryNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); m_DataStorage->Remove(binaryNode); // now add result to data tree m_DataStorage->Add( newNode, m_InputImageNode ); this->InitializeLevelWindow(); if(m_UseVolumeRendering) this->EnableVolumeRendering(true); m_UpdateSuggestedThreshold = true;// reset first stored threshold value //Setting progress to finished mitk::ProgressBar::GetInstance()->Progress(357); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::InitializeLevelWindow() { //get the preview from the datatree mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, NULL, "levelwindow"); mitk::ScalarType* level = new mitk::ScalarType(0.0); mitk::ScalarType* window = new mitk::ScalarType(1.0); int upper; if (m_CurrentRGDirectionIsUpwards) { upper = m_UPPERTHRESHOLD - m_SeedpointValue; } else { upper = m_SeedpointValue - m_LOWERTHRESHOLD; } tempLevelWindow.SetRangeMinMax(mitk::ScalarType(0), mitk::ScalarType(upper)); //get the suggested threshold from the detected leakage-point and adjust the slider if (m_CurrentRGDirectionIsUpwards) { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = m_UPPERTHRESHOLD - (m_SeedpointValue) + 0.5; } else { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = (m_SeedpointValue) - m_LOWERTHRESHOLD + 0.5; } tempLevelWindow.SetLevelWindow(*level, *window); newNode->SetLevelWindow(tempLevelWindow, NULL, "levelwindow"); //update the widgets mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_SliderInitialized = true; //inquiry need to fix bug#1828 static int lastSliderPosition = 0; if ((this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1) == lastSliderPosition) { this->ChangeLevelWindow(lastSliderPosition); } lastSliderPosition = this->m_SeedpointValue + this->m_DetectedLeakagePoint-1; if(m_MultiWidget) { this->m_MultiWidget->levelWindowWidget->GetManager()->SetAutoTopMostImage(false); this->m_MultiWidget->levelWindowWidget->GetManager()->SetLevelWindowProperty(static_cast(newNode->GetProperty("levelwindow"))); } if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int) (*level + 0.5));//lower threshold for labeled image } void QmitkAdaptiveRegionGrowingToolGUI::ChangeLevelWindow(double newValue) { if (m_SliderInitialized) { //do nothing, if no preview exists mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; mitk::LevelWindow tempLevelWindow; newNode->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 - newValue + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } else { level = newValue - m_LOWERTHRESHOLD +0.5; tempLevelWindow.SetLevelWindow(level, *window); } newNode->SetLevelWindow(tempLevelWindow, NULL, "levelwindow"); if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int) (level - 0.5));//lower threshold for labeled image newNode->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::DecreaseSlider() { //moves the slider one step to the left, when the "-"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->minimum()) { int newValue = this->m_Controls.m_PreviewSlider->value() - 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::IncreaseSlider() { //moves the slider one step to the right, when the "+"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->maximum()) { int newValue = this->m_Controls.m_PreviewSlider->value() + 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::ConfirmSegmentation() { //get image node if(m_InputImageNode.IsNull()) { QMessageBox::critical( NULL, "Adaptive region growing functionality", "Please specify the image in Datamanager!"); return; } //get image data mitk::Image::Pointer orgImage = dynamic_cast (m_InputImageNode->GetData()); if(orgImage.IsNull()) { QMessageBox::critical( NULL, "Adaptive region growing functionality", "No Image found!"); return; } //get labeled segmentation mitk::Image::Pointer labeledSeg = (mitk::Image*)m_DataStorage->GetNamedObject(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if(labeledSeg.IsNull()) { QMessageBox::critical( NULL, "Adaptive region growing functionality", "No Segmentation Preview found!"); return; } mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; QmitkConfirmSegmentationDialog dialog; QString segName = QString::fromStdString(m_RegionGrow3DTool->GetCurrentSegmentationName()); dialog.SetSegmentationName(segName); int result = dialog.exec(); switch(result) { case QmitkConfirmSegmentationDialog::CREATE_NEW_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(false); break; case QmitkConfirmSegmentationDialog::OVERWRITE_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(true); break; case QmitkConfirmSegmentationDialog::CANCEL_SEGMENTATION: return; } mitk::Image* img = dynamic_cast(newNode->GetData()); AccessByItk(img, ITKThresholding); // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); newNode->SetVisibility(false); m_Controls.m_cbVolumeRendering->setChecked(false); //TODO disable slider etc... } template void QmitkAdaptiveRegionGrowingToolGUI::ITKThresholding(itk::Image* itkImage) { mitk::Image::Pointer originalSegmentation = dynamic_cast(this->m_RegionGrow3DTool-> GetTargetSegmentationNode()->GetData()); int timeStep = mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1") )->GetTimeStep(); if (originalSegmentation) { typedef itk::Image InputImageType; typedef itk::Image SegmentationType; //select single 3D volume if we have more than one time step typename SegmentationType::Pointer originalSegmentationInITK = SegmentationType::New(); if(originalSegmentation->GetTimeGeometry()->CountTimeSteps() > 1) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput( originalSegmentation ); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); CastToItkImage( timeSelector->GetOutput(), originalSegmentationInITK ); } else //use original { CastToItkImage( originalSegmentation, originalSegmentationInITK ); } //Fill current preiview image in segmentation image originalSegmentationInITK->FillBuffer(0); itk::ImageRegionIterator itOutput( originalSegmentationInITK, originalSegmentationInITK->GetLargestPossibleRegion() ); itk::ImageRegionIterator itInput( itkImage, itkImage->GetLargestPossibleRegion() ); itOutput.GoToBegin(); itInput.GoToBegin(); //calculate threhold from slider value int currentTreshold = 0; if (m_CurrentRGDirectionIsUpwards) { currentTreshold = m_UPPERTHRESHOLD - m_Controls.m_PreviewSlider->value() + 1; } else { currentTreshold = m_Controls.m_PreviewSlider->value() - m_LOWERTHRESHOLD; } //iterate over image and set pixel in segmentation according to thresholded labeled image while( !itOutput.IsAtEnd() && !itInput.IsAtEnd() ) { //Use threshold slider to determine if pixel is set to 1 if( itInput.Value() != 0 && itInput.Value() > currentTreshold ) { itOutput.Set( 1 ); } ++itOutput; ++itInput; } //combine current working segmentation image with our region growing result originalSegmentation->SetVolume( (void*)(originalSegmentationInITK->GetPixelContainer()->GetBufferPointer()), timeStep); originalSegmentation->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::EnableControls(bool enable) { if (m_RegionGrow3DTool.IsNull()) return; // Check if seed point is already set, if not leave RunSegmentation disabled //if even m_DataStorage is NULL leave node NULL mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { this->m_Controls.m_pbRunSegmentation->setEnabled(false); } else { this->m_Controls.m_pbRunSegmentation->setEnabled(enable); } // Check if a segmentation exists, if not leave segmentation dependent disabled. //if even m_DataStorage is NULL leave node NULL node = m_DataStorage?m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE):NULL; if (node.IsNull()) { this->m_Controls.m_PreviewSlider->setEnabled(false); this->m_Controls.m_pbConfirmSegementation->setEnabled(false); } else { this->m_Controls.m_PreviewSlider->setEnabled(enable); this->m_Controls.m_pbConfirmSegementation->setEnabled(enable); } this->m_Controls.m_cbVolumeRendering->setEnabled(enable); } void QmitkAdaptiveRegionGrowingToolGUI::EnableVolumeRendering(bool enable) { mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if(node.IsNull()) return; if(m_MultiWidget) m_MultiWidget->SetWidgetPlanesVisibility(!enable); if (enable) { node->SetBoolProperty("volumerendering", enable); node->SetBoolProperty("volumerendering.uselod", true); } else { node->SetBoolProperty("volumerendering", enable); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::UpdateVolumeRenderingThreshold(int thValue) { mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); if (m_UpdateSuggestedThreshold) { m_SuggestedThValue = thValue; m_UpdateSuggestedThreshold = false; } // grayvalue->opacity { vtkPiecewiseFunction *f = tf->GetScalarOpacityFunction(); f->RemoveAllPoints(); f->AddPoint(0, 0); f->AddPoint(thValue+0.5, 0); f->AddPoint(thValue+1.5, 1); f->AddPoint(1000, 1); f->ClampingOn(); f->Modified(); } // grayvalue->color { float a = 255.0; vtkColorTransferFunction *ctf = tf->GetColorTransferFunction(); ctf->RemoveAllPoints(); //ctf->AddRGBPoint(-1000, 0.0, 0.0, 0.0); ctf->AddRGBPoint(m_SuggestedThValue+1, 203/a, 104/a, 102/a); ctf->AddRGBPoint(m_SuggestedThValue, 255/a, 0/a, 0/a); ctf->ClampingOn(); ctf->Modified(); } // GradientOpacityFunction { vtkPiecewiseFunction *gof = tf->GetGradientOpacityFunction(); gof->RemoveAllPoints(); gof->AddPoint(-10000, 1); gof->AddPoint(10000, 1); gof->ClampingOn(); gof->Modified(); } mitk::TransferFunctionProperty::Pointer tfp = mitk::TransferFunctionProperty::New(); tfp->SetValue(tf); node->SetProperty("TransferFunction", tfp); } void QmitkAdaptiveRegionGrowingToolGUI::UseVolumeRendering(bool on) { m_UseVolumeRendering = on; this->EnableVolumeRendering(on); } void QmitkAdaptiveRegionGrowingToolGUI::SetLowerThresholdValue( double lowerThreshold ) { m_LOWERTHRESHOLD = lowerThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::SetUpperThresholdValue( double upperThreshold) { m_UPPERTHRESHOLD = upperThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::Deactivated() { // make the segmentation preview node invisible mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if( node.IsNotNull() ) { node->SetVisibility(false); } // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); m_Controls.m_cbVolumeRendering->setChecked(false); } void QmitkAdaptiveRegionGrowingToolGUI::Activated() { } diff --git a/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp b/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp index c0430028d5..fc82af9c03 100644 --- a/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp +++ b/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp @@ -1,321 +1,321 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkComputeContourSetNormalsFilter.h" #include "mitkImagePixelReadAccessor.h" mitk::ComputeContourSetNormalsFilter::ComputeContourSetNormalsFilter() { m_MaxSpacing = 5; this->m_UseProgressBar = false; this->m_ProgressStepSize = 1; mitk::Surface::Pointer output = mitk::Surface::New(); this->SetNthOutput(0, output.GetPointer()); } mitk::ComputeContourSetNormalsFilter::~ComputeContourSetNormalsFilter() { } void mitk::ComputeContourSetNormalsFilter::GenerateData() { unsigned int numberOfInputs = this->GetNumberOfIndexedInputs(); this->CreateOutputsForAllInputs(numberOfInputs); //Iterating over each input for(unsigned int i = 0; i < numberOfInputs; i++) { //Getting the inputs polydata and polygons Surface* currentSurface = const_cast( this->GetInput(i) ); vtkPolyData* polyData = currentSurface->GetVtkPolyData(); vtkSmartPointer existingPolys = polyData->GetPolys(); vtkSmartPointer existingPoints = polyData->GetPoints(); existingPolys->InitTraversal(); vtkIdType* cell (NULL); vtkIdType cellSize (0); //The array that contains all the vertex normals of the current polygon vtkSmartPointer normals = vtkSmartPointer::New(); normals->SetNumberOfComponents(3); normals->SetNumberOfTuples(polyData->GetNumberOfPoints()); //If the current contour is an inner contour then the direction is -1 //A contour lies inside another one if the pixel values in the direction of the normal is 1 m_NegativeNormalCounter = 0; m_PositiveNormalCounter = 0; vtkIdType offSet (0); //Iterating over each polygon for( existingPolys->InitTraversal(); existingPolys->GetNextCell(cellSize, cell);) { if(cellSize < 3)continue; //First we calculate the current polygon's normal double polygonNormal[3] = {0.0}; double p1[3]; double p2[3]; double v1[3]; double v2[3]; existingPoints->GetPoint(cell[0], p1); unsigned int index = cellSize*0.5; existingPoints->GetPoint(cell[index], p2); v1[0] = p2[0]-p1[0]; v1[1] = p2[1]-p1[1]; v1[2] = p2[2]-p1[2]; for (vtkIdType k = 2; k < cellSize; k++) { index = cellSize*0.25; existingPoints->GetPoint(cell[index], p1); index = cellSize*0.75; existingPoints->GetPoint(cell[index], p2); v2[0] = p2[0]-p1[0]; v2[1] = p2[1]-p1[1]; v2[2] = p2[2]-p1[2]; vtkMath::Cross(v1,v2,polygonNormal); if (vtkMath::Norm(polygonNormal) != 0) break; } vtkMath::Normalize(polygonNormal); //Now we start computing the normal for each vertex double vertexNormalTemp[3]; existingPoints->GetPoint(cell[0], p1); existingPoints->GetPoint(cell[1], p2); v1[0] = p2[0]-p1[0]; v1[1] = p2[1]-p1[1]; v1[2] = p2[2]-p1[2]; vtkMath::Cross(v1,polygonNormal,vertexNormalTemp); vtkMath::Normalize(vertexNormalTemp); double vertexNormal[3]; for (vtkIdType j = 0; j < cellSize-2; j++) { existingPoints->GetPoint(cell[j+1], p1); existingPoints->GetPoint(cell[j+2], p2); v1[0] = p2[0]-p1[0]; v1[1] = p2[1]-p1[1]; v1[2] = p2[2]-p1[2]; vtkMath::Cross(v1,polygonNormal,vertexNormal); vtkMath::Normalize(vertexNormal); double finalNormal[3]; finalNormal[0] = (vertexNormal[0] + vertexNormalTemp[0])*0.5; finalNormal[1] = (vertexNormal[1] + vertexNormalTemp[1])*0.5; finalNormal[2] = (vertexNormal[2] + vertexNormalTemp[2])*0.5; //Here we determine the direction of the normal if (m_SegmentationBinaryImage) { Point3D worldCoord; worldCoord[0] = p1[0]+finalNormal[0]*m_MaxSpacing; worldCoord[1] = p1[1]+finalNormal[1]*m_MaxSpacing; worldCoord[2] = p1[2]+finalNormal[2]*m_MaxSpacing; double val = 0.0; mitk::ImagePixelReadAccessor readAccess(m_SegmentationBinaryImage); - mitk::Index3D idx; + itk::Index<3> idx; m_SegmentationBinaryImage->GetGeometry()->WorldToIndex(worldCoord, idx); val = readAccess.GetPixelByIndexSafe(idx); if (val == 0.0) { ++m_PositiveNormalCounter; } else { ++m_NegativeNormalCounter; } } vertexNormalTemp[0] = vertexNormal[0]; vertexNormalTemp[1] = vertexNormal[1]; vertexNormalTemp[2] = vertexNormal[2]; vtkIdType id = cell[j+1]; normals->SetTuple(id,finalNormal); } existingPoints->GetPoint(cell[0], p1); existingPoints->GetPoint(cell[1], p2); v1[0] = p2[0]-p1[0]; v1[1] = p2[1]-p1[1]; v1[2] = p2[2]-p1[2]; vtkMath::Cross(v1,polygonNormal,vertexNormal); vtkMath::Normalize(vertexNormal); vertexNormal[0] = (vertexNormal[0] + vertexNormalTemp[0])*0.5; vertexNormal[1] = (vertexNormal[1] + vertexNormalTemp[1])*0.5; vertexNormal[2] = (vertexNormal[2] + vertexNormalTemp[2])*0.5; vtkIdType id = cell[0]; normals->SetTuple(id,vertexNormal); id = cell[cellSize-1]; normals->SetTuple(id,vertexNormal); if(m_NegativeNormalCounter > m_PositiveNormalCounter) { for(vtkIdType n = 0; n < cellSize; n++) { double normal[3]; normals->GetTuple(offSet+n, normal); normal[0] = (-1)*normal[0]; normal[1] = (-1)*normal[1]; normal[2] = (-1)*normal[2]; normals->SetTuple(offSet+n, normal); } } m_NegativeNormalCounter = 0; m_PositiveNormalCounter = 0; offSet += cellSize; }//end for all cells Surface::Pointer surface = this->GetOutput(i); surface->GetVtkPolyData()->GetCellData()->SetNormals(normals); }//end for all inputs //Setting progressbar if (this->m_UseProgressBar) mitk::ProgressBar::GetInstance()->Progress(this->m_ProgressStepSize); } mitk::Surface::Pointer mitk::ComputeContourSetNormalsFilter::GetNormalsAsSurface() { //Just for debugging: vtkSmartPointer newPolyData = vtkSmartPointer::New(); vtkSmartPointer newLines = vtkSmartPointer::New(); vtkSmartPointer newPoints = vtkSmartPointer::New(); unsigned int idCounter (0); //Debug end for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs(); i++) { Surface* currentSurface = const_cast( this->GetOutput(i) ); vtkPolyData* polyData = currentSurface->GetVtkPolyData(); vtkSmartPointer currentCellNormals = vtkDoubleArray::SafeDownCast(polyData->GetCellData()->GetNormals()); vtkSmartPointer existingPolys = polyData->GetPolys(); vtkSmartPointer existingPoints = polyData->GetPoints(); existingPolys->InitTraversal(); vtkIdType* cell (NULL); vtkIdType cellSize (0); for( existingPolys->InitTraversal(); existingPolys->GetNextCell(cellSize, cell);) { for ( vtkIdType j = 0; j < cellSize; j++ ) { double currentNormal[3]; currentCellNormals->GetTuple(cell[j], currentNormal); vtkSmartPointer line = vtkSmartPointer::New(); line->GetPointIds()->SetNumberOfIds(2); double newPoint[3]; double p0[3]; existingPoints->GetPoint(cell[j], p0); newPoint[0] = p0[0] + currentNormal[0]; newPoint[1] = p0[1] + currentNormal[1]; newPoint[2] = p0[2] + currentNormal[2]; line->GetPointIds()->SetId(0, idCounter); newPoints->InsertPoint(idCounter, p0); idCounter++; line->GetPointIds()->SetId(1, idCounter); newPoints->InsertPoint(idCounter, newPoint); idCounter++; newLines->InsertNextCell(line); }//end for all points }//end for all cells }//end for all outputs newPolyData->SetPoints(newPoints); newPolyData->SetLines(newLines); newPolyData->BuildCells(); mitk::Surface::Pointer surface = mitk::Surface::New(); surface->SetVtkPolyData(newPolyData); return surface; } void mitk::ComputeContourSetNormalsFilter::SetMaxSpacing(double maxSpacing) { m_MaxSpacing = maxSpacing; } void mitk::ComputeContourSetNormalsFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); } void mitk::ComputeContourSetNormalsFilter::Reset() { for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); i++) { this->PopBackInput(); } this->SetNumberOfIndexedInputs(0); this->SetNumberOfIndexedOutputs(0); mitk::Surface::Pointer output = mitk::Surface::New(); this->SetNthOutput(0, output.GetPointer()); } void mitk::ComputeContourSetNormalsFilter::SetUseProgressBar(bool status) { this->m_UseProgressBar = status; } void mitk::ComputeContourSetNormalsFilter::SetProgressStepSize(unsigned int stepSize) { this->m_ProgressStepSize = stepSize; } diff --git a/Modules/ToFProcessing/Testing/mitkKinectReconstructionTest.cpp b/Modules/ToFProcessing/Testing/mitkKinectReconstructionTest.cpp index 3455c83d20..c166884f12 100644 --- a/Modules/ToFProcessing/Testing/mitkKinectReconstructionTest.cpp +++ b/Modules/ToFProcessing/Testing/mitkKinectReconstructionTest.cpp @@ -1,98 +1,98 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include -#include +#include #include #include #include #include #include /** * @brief mitkKinectReconstructionTest Testing method for the Kinect reconstruction mode. Specially meant for Kinect. * This tests loads a special data set from MITK-Data and compares it to a reference surface. * This test has no dependency to the mitkKinectModule, although it is thematically connected to it. */ int mitkKinectReconstructionTest(int argc , char* argv[]) { MITK_TEST_BEGIN("mitkKinectReconstructionTest"); MITK_TEST_CONDITION_REQUIRED(argc > 2, "Testing if enough arguments are set."); std::string calibrationFilePath(argv[1]); std::string kinectImagePath(argv[2]); mitk::ToFDistanceImageToSurfaceFilter::Pointer distToSurf = mitk::ToFDistanceImageToSurfaceFilter::New(); mitk::CameraIntrinsics::Pointer intrinsics = mitk::CameraIntrinsics::New(); //load our personal kinect calibration intrinsics->FromXMLFile(calibrationFilePath); MITK_TEST_CONDITION_REQUIRED(intrinsics.IsNotNull(), "Testing if a calibration file could be loaded."); distToSurf->SetCameraIntrinsics(intrinsics); distToSurf->SetReconstructionMode(mitk::ToFDistanceImageToSurfaceFilter::Kinect); //load a data set mitk::Image::Pointer kinectImage = mitk::IOUtil::LoadImage(kinectImagePath); MITK_TEST_CONDITION_REQUIRED(kinectImage.IsNotNull(), "Testing if a kinect image could be loaded."); distToSurf->SetInput(kinectImage); distToSurf->Update(); mitk::Surface::Pointer resultOfFilter = distToSurf->GetOutput(); MITK_TEST_CONDITION_REQUIRED(resultOfFilter.IsNotNull(), "Testing if any output was generated."); mitk::PointSet::Pointer resultPointSet = mitk::ToFTestingCommon::VtkPolyDataToMitkPointSet(resultOfFilter->GetVtkPolyData()); // generate ground truth data mitk::PointSet::Pointer groundTruthPointSet = mitk::PointSet::New(); mitk::ToFProcessingCommon::ToFPoint2D focalLength; focalLength[0] = intrinsics->GetFocalLengthX(); focalLength[1] = intrinsics->GetFocalLengthY(); mitk::ToFProcessingCommon::ToFPoint2D principalPoint; principalPoint[0] = intrinsics->GetPrincipalPointX(); principalPoint[1] = intrinsics->GetPrincipalPointY(); int xDimension = (int)kinectImage->GetDimension(0); int yDimension = (int)kinectImage->GetDimension(1); int pointCount = 0; mitk::ImagePixelReadAccessor imageAcces(kinectImage, kinectImage->GetSliceData(0)); for (int j=0; j pixel; pixel[0] = i; pixel[1] = j; mitk::ToFProcessingCommon::ToFScalarType distance = (double)imageAcces.GetPixelByIndex(pixel); mitk::Point3D currentPoint; currentPoint = mitk::ToFProcessingCommon::KinectIndexToCartesianCoordinates(i,j,distance,focalLength[0],focalLength[1],principalPoint[0],principalPoint[1]); if (distance>mitk::eps) { groundTruthPointSet->InsertPoint( pointCount, currentPoint ); pointCount++; } } } MITK_TEST_CONDITION_REQUIRED( mitk::ToFTestingCommon::PointSetsEqual(resultPointSet,groundTruthPointSet), "Testing if point sets are equal (with a small epsilon)."); MITK_TEST_END(); } diff --git a/Modules/ToFProcessing/Testing/mitkToFCompositeFilterTest.cpp b/Modules/ToFProcessing/Testing/mitkToFCompositeFilterTest.cpp index eef518f2b9..a04b4e4cf2 100644 --- a/Modules/ToFProcessing/Testing/mitkToFCompositeFilterTest.cpp +++ b/Modules/ToFProcessing/Testing/mitkToFCompositeFilterTest.cpp @@ -1,380 +1,380 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include /**Documentation * \brief test for the class "ToFCompositeFilter". * * Manually create filter pipeline and check if it is equivalent to composite filter */ typedef mitk::ToFProcessingCommon::ToFPoint2D ToFPoint2D; typedef mitk::ToFProcessingCommon::ToFScalarType ToFScalarType; typedef itk::Image ItkImageType_2D; typedef itk::Image ItkImageType_3D; typedef itk::ImageRegionIterator ItkImageRegionIteratorType2D; typedef itk::ImageRegionIterator ItkImageRegionIteratorType3D; typedef itk::BilateralImageFilter BilateralImageFilterType; typedef itk::ThresholdImageFilter ThresholdFilterType; typedef itk::MedianImageFilter MedianFilterType; static bool ApplyTemporalMedianFilter(mitk::Image::Pointer& image, ItkImageType_2D::Pointer& itkImage2D) { //initialize ITK output image unsigned int dimX = image->GetDimension(0); unsigned int dimY = image->GetDimension(1); unsigned int nbSlices = image->GetDimension(2); ItkImageType_2D::SizeType size; size[0] = dimX; size[1] = dimY; ItkImageType_2D::RegionType region; region.SetSize(size); ItkImageType_2D::SpacingType spacing; spacing[0] = 1.0; spacing[1] = 1.0; itkImage2D->SetRegions( region ); itkImage2D->SetSpacing ( spacing ); itkImage2D->Allocate(); //initialize median filtering std::vector allDistances; - mitk::Index3D curIdx3D; + itk::Index<3> curIdx3D; ItkImageType_2D::IndexType curIdx2D; //compute median over time for each (x,y) for(unsigned int i = 0; i imageAcces(image, image->GetVolumeData()); for(unsigned int k = 0; k < nbSlices; k++) { curIdx3D[2] = k; allDistances.push_back(imageAcces.GetPixelByIndex(curIdx3D)); } //sort distances and compute median std::sort(allDistances.begin(),allDistances.end()); unsigned int median_idx = nbSlices/2; if(nbSlices%2 == 1) //i.e., there is an odd number of slices { itkImage2D->SetPixel(curIdx2D,allDistances[median_idx]); } else { ToFScalarType upper = allDistances[median_idx]; ToFScalarType lower = allDistances[median_idx+1]; itkImage2D->SetPixel(curIdx2D,(upper+lower)/2.0); } } } return true; } static bool CompareImages(mitk::Image::Pointer image1, mitk::Image::Pointer image2) { unsigned int dimX = image1->GetDimension(0); unsigned int dimY = image1->GetDimension(1); //make sure images have the same dimensions if((dimX != image1->GetDimension(0)) || (dimY != image1->GetDimension(1))) return false; //compare all pixel values mitk::ImagePixelReadAccessor image1Acces(image1, image1->GetSliceData(0)); mitk::ImagePixelReadAccessor image2Acces(image2, image2->GetSliceData(0)); for(unsigned int i = 0; i idx; idx[0] = i; idx[1] = j; if(!(mitk::Equal(image1Acces.GetPixelByIndex(idx), image2Acces.GetPixelByIndex(idx)))) { return false; } } } //all pixels have identical values return true; } bool CreateRandomDistanceImage(unsigned int dimX, unsigned int dimY, ItkImageType_2D::Pointer& itkImage, mitk::Image::Pointer& mitkImage) //TODO warum ITK image? { //initialize ITK output image ItkImageType_2D::IndexType start; start[0] = 0; start[1] = 0; ItkImageType_2D::SizeType size; size[0] = dimX; size[1] = dimY; ItkImageType_2D::RegionType region; region.SetSize(size); region.SetIndex( start); ItkImageType_2D::SpacingType spacing; spacing[0] = 1.0; spacing[1] = 1.0; itkImage->SetRegions( region ); itkImage->SetSpacing ( spacing ); itkImage->Allocate(); ItkImageRegionIteratorType2D imageIterator(itkImage,itkImage->GetLargestPossibleRegion()); imageIterator.GoToBegin(); itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randomGenerator = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); while (!imageIterator.IsAtEnd()) { ToFScalarType pixelValue = randomGenerator->GetUniformVariate(0.0,1000.0); imageIterator.Set(pixelValue); ++imageIterator; } mitk::CastToMitkImage(itkImage,mitkImage); return true; } bool CreateRandomDistanceImageStack(unsigned int dimX, unsigned int dimY, unsigned int nbSlices, ItkImageType_3D::Pointer& itkImage, mitk::Image::Pointer& mitkImage) { //initialize ITK output image ItkImageType_3D::IndexType start; start[0] = 0; start[1] = 0; start[1] = 0; ItkImageType_3D::SizeType size; size[0] = dimX; size[1] = dimY; size[2] = nbSlices; ItkImageType_3D::RegionType region; region.SetSize(size); region.SetIndex( start); ItkImageType_3D::SpacingType spacing; spacing[0] = 1.0; spacing[1] = 1.0; spacing[2] = 1.0; itkImage->SetRegions( region ); itkImage->SetSpacing ( spacing ); itkImage->Allocate(); //assign random pixel values ItkImageRegionIteratorType3D imageIterator(itkImage,itkImage->GetLargestPossibleRegion()); imageIterator.GoToBegin(); itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randomGenerator = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); while (!imageIterator.IsAtEnd()) { ToFScalarType pixelValue = randomGenerator->GetUniformVariate(0.0,1000.0); imageIterator.Set(pixelValue); ++imageIterator; } //cast to MITK image mitk::CastToMitkImage(itkImage,mitkImage); return true; } int mitkToFCompositeFilterTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("ToFCompositeFilter"); //initialize composite filter mitk::ToFCompositeFilter::Pointer compositeFilter = mitk::ToFCompositeFilter::New(); //Initialize threshold filter ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New(); int threshold_min = 5; int threshold_max = 100; thresholdFilter->SetOutsideValue(0.0); thresholdFilter->SetLower(threshold_min); thresholdFilter->SetUpper(threshold_max); compositeFilter->SetThresholdFilterParameter(threshold_min, threshold_max); //Initialize spatial median filter MedianFilterType::Pointer medianFilter = MedianFilterType::New(); //Initialize bilateral filter BilateralImageFilterType::Pointer bilateralFilter = BilateralImageFilterType::New(); float domainSigma = 4; float rangeSigma = 50; float kernelRadius = 3; bilateralFilter->SetDomainSigma(domainSigma); bilateralFilter->SetRangeSigma(rangeSigma); bilateralFilter->SetRadius(kernelRadius); compositeFilter->SetBilateralFilterParameter(domainSigma,rangeSigma,kernelRadius); //Initialize pipeline ItkImageType_2D::Pointer itkInputImage = ItkImageType_2D::New(); mitk::Image::Pointer mitkInputImage = mitk::Image::New(); CreateRandomDistanceImage(100,100,itkInputImage,mitkInputImage); ItkImageType_2D::Pointer itkOutputImage; compositeFilter->SetInput(mitkInputImage); mitk::Image::Pointer mitkOutputImage = compositeFilter->GetOutput(); //------------------------------------------------------------------------------------------------------- //Apply first filter only (threshold) //standard variant thresholdFilter->SetInput(itkInputImage); itkOutputImage = thresholdFilter->GetOutput(); itkOutputImage->Update(); //variant with composite filter compositeFilter->SetApplyThresholdFilter(true); compositeFilter->SetApplyMedianFilter(false); compositeFilter->SetApplyTemporalMedianFilter(false); compositeFilter->SetApplyBilateralFilter(false); mitkOutputImage->Update(); //compare output mitk::Image::Pointer itkOutputImageConverted; mitk::CastToMitkImage(itkOutputImage,itkOutputImageConverted); bool pipelineSuccess = CompareImages(itkOutputImageConverted,mitkOutputImage); MITK_TEST_CONDITION_REQUIRED(pipelineSuccess,"Test threshold filter in pipeline"); //------------------------------------------------------------------------------------------------------- //Apply first and second filter //standard variant medianFilter->SetInput(thresholdFilter->GetOutput()); itkOutputImage = medianFilter->GetOutput(); itkOutputImage->Update(); //variant with composite filter compositeFilter->SetApplyMedianFilter(true); mitkOutputImage->Update(); //compare output mitk::CastToMitkImage(itkOutputImage,itkOutputImageConverted); pipelineSuccess = CompareImages(itkOutputImageConverted,mitkOutputImage); MITK_TEST_CONDITION_REQUIRED(pipelineSuccess,"Test threshold and median filter in pipeline"); //------------------------------------------------------------------------------------------------------- //Apply first three filters //standard variant bilateralFilter->SetInput(medianFilter->GetOutput()); itkOutputImage = bilateralFilter->GetOutput(); itkOutputImage->Update(); //variant with composite filter compositeFilter->SetApplyBilateralFilter(true); mitkOutputImage->Update(); //compare output mitk::CastToMitkImage(itkOutputImage,itkOutputImageConverted); pipelineSuccess = CompareImages(itkOutputImageConverted,mitkOutputImage); MITK_TEST_CONDITION_REQUIRED(pipelineSuccess,"Test threshold filter, bilateral filter and temporal median filter in pipeline"); //------------------------------------------------------------------------------------------------------- //Apply all filters //generate image stack ItkImageType_3D::Pointer itkInputImage3D = ItkImageType_3D::New(); mitk::Image::Pointer mitkImage3D = mitk::Image::New(); CreateRandomDistanceImageStack(100,100,12,itkInputImage3D,mitkImage3D); //standard variant ItkImageType_2D::Pointer medianFilteredImage = ItkImageType_2D::New(); ApplyTemporalMedianFilter(mitkImage3D,medianFilteredImage); thresholdFilter->SetInput(medianFilteredImage); itkOutputImage->Update(); //variant with composite filter compositeFilter->SetApplyTemporalMedianFilter(true); mitkOutputImage->Update(); //compare output mitk::CastToMitkImage(itkOutputImage,itkOutputImageConverted); pipelineSuccess = CompareImages(itkOutputImageConverted,mitkOutputImage); MITK_TEST_CONDITION_REQUIRED(pipelineSuccess,"Test all filters in pipeline"); //------------------------------------------------------------------------------------------------------- //Check set/get functions mitk::Image::Pointer newImage = mitk::Image::New(); mitk::Image::Pointer returnedImage; compositeFilter->SetInput(newImage); returnedImage = compositeFilter->GetInput(); MITK_TEST_CONDITION_REQUIRED(newImage == returnedImage,"Get/Set empty image"); compositeFilter->SetApplyTemporalMedianFilter(false); MITK_TEST_CONDITION_REQUIRED(compositeFilter->GetApplyTemporalMedianFilter()==false,"Get/Set ApplyTemporalMedianFilter"); compositeFilter->SetApplyMedianFilter(false); MITK_TEST_CONDITION_REQUIRED(compositeFilter->GetApplyMedianFilter()==false,"Get/Set ApplyMedianFilter"); compositeFilter->SetApplyThresholdFilter(false); MITK_TEST_CONDITION_REQUIRED(compositeFilter->GetApplyThresholdFilter()==false,"Get/Set ApplyThresholdFilter"); compositeFilter->SetApplyBilateralFilter(false); MITK_TEST_CONDITION_REQUIRED(compositeFilter->GetApplyBilateralFilter()==false,"Get/Set ApplyBilateralFilter"); //------------------------------------------------------------------------------------------------------- MITK_TEST_END(); } diff --git a/Modules/ToFProcessing/Testing/mitkToFDistanceImageToPointSetFilterTest.cpp b/Modules/ToFProcessing/Testing/mitkToFDistanceImageToPointSetFilterTest.cpp index 9e9237056b..bcd0823a7b 100644 --- a/Modules/ToFProcessing/Testing/mitkToFDistanceImageToPointSetFilterTest.cpp +++ b/Modules/ToFProcessing/Testing/mitkToFDistanceImageToPointSetFilterTest.cpp @@ -1,346 +1,346 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include /**Documentation * test for the class "ToFDistanceImageToPointSetFilter". */ mitk::PointSet::Pointer CreateTestPointSet() { mitk::PointSet::Pointer subSet = mitk::PointSet::New(); mitk::Point3D point; point[0] = 10; point[1] = 20; point[2] = 0; subSet->InsertPoint(0,point); point[0] = 100; point[1] = 150; point[2] = 0; subSet->InsertPoint(1,point); point[0] = 110; point[1] = 30; point[2] = 0; subSet->InsertPoint(2,point); point[0] = 40; point[1] = 200; point[2] = 0; subSet->InsertPoint(3,point); return subSet; } -std::vector CreateVectorPointSet() +std::vector > CreateVectorPointSet() { - std::vector subSet = std::vector(); - mitk::Index3D point; + std::vector > subSet = std::vector >(); + itk::Index<3> point; point[0] = 10; point[1] = 20; point[2] = 0; subSet.push_back(point); point[0] = 100; point[1] = 150; point[2] = 0; subSet.push_back(point); point[0] = 110; point[1] = 30; point[2] = 0; subSet.push_back(point); point[0] = 40; point[1] = 200; point[2] = 0; subSet.push_back(point); return subSet; } // Create image with pixelValue in every pixel except for the pixels in subSet, which get successively the values of distances inline static mitk::Image::Pointer CreateTestImageWithPointSet(float pixelValue, unsigned int dimX, unsigned int dimY, mitk::PointSet::Pointer subSet) { typedef itk::Image ItkImageType2D; typedef itk::ImageRegionIterator ItkImageRegionIteratorType2D; ItkImageType2D::Pointer image = ItkImageType2D::New(); ItkImageType2D::IndexType start; start[0] = 0; start[1] = 0; ItkImageType2D::SizeType size; size[0] = dimX; size[1] = dimY; ItkImageType2D::RegionType region; region.SetSize(size); region.SetIndex( start); ItkImageType2D::SpacingType spacing; spacing[0] = 1.0; spacing[1] = 1.0; image->SetRegions( region ); image->SetSpacing ( spacing ); image->Allocate(); //Obtaining image data from ToF camera// //Correlate inten values to PixelIndex// ItkImageRegionIteratorType2D imageIterator(image,image->GetLargestPossibleRegion()); imageIterator.GoToBegin(); while (!imageIterator.IsAtEnd()) { imageIterator.Set(pixelValue); ++imageIterator; } // distances varying from pixelValue std::vector distances; distances.push_back(50); distances.push_back(500); distances.push_back(2050); distances.push_back(300); // set the pixel values for the subset for(int i=0; iGetSize(); i++) { mitk::Point3D point = subSet->GetPoint(i); ItkImageType2D::IndexType index; index[0] = point[0]; index[1] = point[1]; float distance = distances.at(i); image->SetPixel(index,distance); } mitk::Image::Pointer mitkImage = mitk::Image::New(); mitk::CastToMitkImage(image,mitkImage); return mitkImage; } int mitkToFDistanceImageToPointSetFilterTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("ToFDistanceImageToPointSetFilter"); mitk::ToFDistanceImageToPointSetFilter::Pointer filter = mitk::ToFDistanceImageToPointSetFilter::New(); //create test sub set MITK_INFO<<"Create test pointset"; mitk::PointSet::Pointer subSet = CreateTestPointSet(); //create test image unsigned int dimX = 204; unsigned int dimY = 204; MITK_INFO<<"Create test image"; mitk::Image::Pointer image = CreateTestImageWithPointSet(1000.0f,dimX,dimY,subSet); //initialize intrinsic parameters //initialize intrinsic parameters with some arbitrary values mitk::ToFProcessingCommon::ToFPoint2D interPixelDistance; interPixelDistance[0] = 0.04564; interPixelDistance[1] = 0.0451564; mitk::ToFProcessingCommon::ToFScalarType focalLengthX = 295.78960; mitk::ToFProcessingCommon::ToFScalarType focalLengthY = 296.348535; mitk::ToFProcessingCommon::ToFScalarType focalLength = (focalLengthX*interPixelDistance[0]+focalLengthY*interPixelDistance[1])/2.0; mitk::ToFProcessingCommon::ToFScalarType k1=-0.36,k2=-0.14,p1=0.001,p2=-0.00; mitk::ToFProcessingCommon::ToFPoint2D principalPoint; principalPoint[0] = 103.576546; principalPoint[1] = 100.1532; mitk::CameraIntrinsics::Pointer cameraIntrinsics = mitk::CameraIntrinsics::New(); cameraIntrinsics->SetFocalLength(focalLengthX,focalLengthY); cameraIntrinsics->SetPrincipalPoint(principalPoint[0],principalPoint[1]); cameraIntrinsics->SetDistorsionCoeffs(k1,k2,p1,p2); // test SetCameraIntrinsics() filter->SetCameraIntrinsics(cameraIntrinsics); MITK_TEST_CONDITION_REQUIRED((focalLengthX==filter->GetCameraIntrinsics()->GetFocalLengthX()),"Testing SetCameraIntrinsics with focalLength"); mitk::ToFProcessingCommon::ToFPoint2D pp; pp[0] = filter->GetCameraIntrinsics()->GetPrincipalPointX(); pp[1] = filter->GetCameraIntrinsics()->GetPrincipalPointY(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(principalPoint,pp),"Testing SetCameraIntrinsics with principalPoint()"); // test SetInterPixelDistance() filter->SetInterPixelDistance(interPixelDistance); mitk::ToFProcessingCommon::ToFPoint2D ipD = filter->GetInterPixelDistance(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(ipD,interPixelDistance),"Testing Set/GetInterPixelDistance()"); // test SetReconstructionMode() filter->SetReconstructionMode(false); MITK_TEST_CONDITION_REQUIRED(filter->GetReconstructionMode() == false,"Testing Set/GetReconstructionMode()"); // test Set/GetInput() filter->SetInput(image); MITK_TEST_CONDITION_REQUIRED((image==filter->GetInput()),"Testing Set/GetInput()"); // test filter without subset (without using the interpixeldistance) MITK_INFO<<"Test filter without subset without using the interpixeldistance"; filter->SetReconstructionMode(true); mitk::PointSet::Pointer expectedResult = mitk::PointSet::New(); unsigned int counter = 0; mitk::ImagePixelReadAccessor imageAcces(image, image->GetSliceData(0)); for (unsigned int j=0; j index; index[0] = i; index[1] = j; float distance = imageAcces.GetPixelByIndex(index); mitk::Point3D coordinate = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(i,j,distance,focalLengthX,focalLengthY,principalPoint[0],principalPoint[1]); expectedResult->InsertPoint(counter,coordinate); counter++; } } filter->Update(); mitk::PointSet::Pointer result = filter->GetOutput(); MITK_TEST_CONDITION_REQUIRED((expectedResult->GetSize()==result->GetSize()),"Test if point set size is equal"); MITK_TEST_CONDITION_REQUIRED(mitk::ToFTestingCommon::PointSetsEqual(expectedResult,result),"Testing filter without subset"); // compare filter result with ToFDistanceImageToSurfaceFilter MITK_INFO<<"Compare filter result with ToFDistanceImageToSurfaceFilter"; mitk::ToFDistanceImageToSurfaceFilter::Pointer surfaceFilter = mitk::ToFDistanceImageToSurfaceFilter::New(); surfaceFilter->SetInput(image); surfaceFilter->SetInterPixelDistance(interPixelDistance); surfaceFilter->SetCameraIntrinsics(cameraIntrinsics); surfaceFilter->SetReconstructionMode(mitk::ToFDistanceImageToSurfaceFilter::WithOutInterPixelDistance); MITK_TEST_CONDITION_REQUIRED(filter->GetReconstructionMode() == mitk::ToFDistanceImageToSurfaceFilter::WithOutInterPixelDistance,"Testing Set/GetReconstructionMode()"); mitk::Surface::Pointer surface = surfaceFilter->GetOutput(); surface->Update(); // create point set from surface mitk::PointSet::Pointer pointSet = mitk::ToFTestingCommon::VtkPolyDataToMitkPointSet(surface->GetVtkPolyData()); //compare pointset against ground truth MITK_TEST_CONDITION_REQUIRED((pointSet->GetSize()==result->GetSize()),"Test if point set size is equal"); MITK_TEST_CONDITION_REQUIRED(mitk::ToFTestingCommon::PointSetsEqual(pointSet,result),"Compare with surface points"); // test filter without subset (with using the interpixeldistance) MITK_INFO<<"Test filter without subset with using the interpixeldistance"; filter->Modified(); filter->SetReconstructionMode(false); expectedResult = mitk::PointSet::New(); counter = 0; for (unsigned int j=0; j index; index[0] = i; index[1] = j; float distance = imageAcces.GetPixelByIndex(index); mitk::Point3D coordinate = mitk::ToFProcessingCommon::IndexToCartesianCoordinatesWithInterpixdist(i,j,distance,focalLength,interPixelDistance,principalPoint); expectedResult->InsertPoint(counter,coordinate); counter++; } } filter->Update(); result = filter->GetOutput(); MITK_TEST_CONDITION_REQUIRED((expectedResult->GetSize()==result->GetSize()),"Test if point set size is equal"); MITK_TEST_CONDITION_REQUIRED(mitk::ToFTestingCommon::PointSetsEqual(expectedResult,result),"Testing filter without subset"); // compare filter result with ToFDistanceImageToSurfaceFilter MITK_INFO<<"Compare filter result with ToFDistanceImageToSurfaceFilter"; surfaceFilter = mitk::ToFDistanceImageToSurfaceFilter::New(); surfaceFilter->SetInput(image); surfaceFilter->SetInterPixelDistance(interPixelDistance); surfaceFilter->SetCameraIntrinsics(cameraIntrinsics); surfaceFilter->SetReconstructionMode(mitk::ToFDistanceImageToSurfaceFilter::WithInterPixelDistance); MITK_TEST_CONDITION_REQUIRED(surfaceFilter->GetReconstructionMode() == mitk::ToFDistanceImageToSurfaceFilter::WithInterPixelDistance,"Testing Set/GetReconstructionMode()"); surface = surfaceFilter->GetOutput(); surface->Update(); // create point set from surface pointSet = mitk::ToFTestingCommon::VtkPolyDataToMitkPointSet(surface->GetVtkPolyData()); //compare against ground truth MITK_TEST_CONDITION_REQUIRED((pointSet->GetSize()==result->GetSize()),"Test if point set size is equal"); MITK_TEST_CONDITION_REQUIRED(mitk::ToFTestingCommon::PointSetsEqual(pointSet,result),"Compare with surface points"); // test filter with subset (without using the interpixeldistance) MITK_INFO<<"Test filter with subset without using the interpixeldistance"; filter = mitk::ToFDistanceImageToPointSetFilter::New(); filter->SetInput(image); filter->SetInterPixelDistance(interPixelDistance); filter->SetCameraIntrinsics(cameraIntrinsics); filter->SetReconstructionMode(true); expectedResult = mitk::PointSet::New(); counter = 0; for(int i=0; iGetSize(); i++) { mitk::Point3D point = subSet->GetPoint(i); itk::Index<2> index; index[0] = point[0]; index[1] = point[1]; float distance = imageAcces.GetPixelByIndex(index); mitk::Point3D coordinate = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(point[0],point[1], distance,focalLengthX,focalLengthY,principalPoint[0],principalPoint[1]); expectedResult->InsertPoint(counter,coordinate); counter++; } filter->SetSubset(subSet); filter->Modified(); filter->Update(); result = filter->GetOutput(); MITK_TEST_CONDITION_REQUIRED((expectedResult->GetSize()==result->GetSize()),"Test if point set size is equal"); MITK_TEST_CONDITION_REQUIRED(mitk::ToFTestingCommon::PointSetsEqual(expectedResult,result),"Testing filter with subset"); // test filter with subset (with using the interpixeldistance) MITK_INFO<<"Test filter with subset with using the interpixeldistance"; filter = mitk::ToFDistanceImageToPointSetFilter::New(); filter->SetInput(image); filter->SetInterPixelDistance(interPixelDistance); filter->SetCameraIntrinsics(cameraIntrinsics); filter->SetReconstructionMode(false); expectedResult = mitk::PointSet::New(); counter = 0; for(int i=0; iGetSize(); i++) { mitk::Point3D point = subSet->GetPoint(i); itk::Index<2> index; index[0] = point[0]; index[1] = point[1]; float distance = imageAcces.GetPixelByIndex(index); mitk::Point3D coordinate = mitk::ToFProcessingCommon::IndexToCartesianCoordinatesWithInterpixdist(point[0],point[1], distance,focalLength,interPixelDistance,principalPoint); expectedResult->InsertPoint(counter,coordinate); counter++; } filter->SetSubset(subSet); filter->Modified(); filter->Update(); result = filter->GetOutput(); MITK_TEST_CONDITION_REQUIRED((expectedResult->GetSize()==result->GetSize()),"Test if point set size is equal"); MITK_TEST_CONDITION_REQUIRED(mitk::ToFTestingCommon::PointSetsEqual(expectedResult,result),"Testing filter with subset"); // Test case to reproduce and check fix of bug 13933. - std::vector vecSubset = CreateVectorPointSet(); + std::vector > vecSubset = CreateVectorPointSet(); filter = mitk::ToFDistanceImageToPointSetFilter::New(); try { filter->SetSubset(vecSubset); } catch (...) { MITK_TEST_CONDITION_REQUIRED(false, "Caught an exception while setting point subset!"); } MITK_TEST_END(); -} \ No newline at end of file +} diff --git a/Modules/ToFProcessing/Testing/mitkToFDistanceImageToSurfaceFilterTest.cpp b/Modules/ToFProcessing/Testing/mitkToFDistanceImageToSurfaceFilterTest.cpp index 3971189dd9..e4c6980da7 100644 --- a/Modules/ToFProcessing/Testing/mitkToFDistanceImageToSurfaceFilterTest.cpp +++ b/Modules/ToFProcessing/Testing/mitkToFDistanceImageToSurfaceFilterTest.cpp @@ -1,392 +1,392 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include /** * @brief Test for the class "ToFDistanceImageToSurfaceFilter". */ typedef mitk::ToFProcessingCommon::ToFPoint2D ToFPoint2D; typedef mitk::ToFProcessingCommon::ToFPoint3D ToFPoint3D; typedef mitk::ToFProcessingCommon::ToFScalarType ToFScalarType; int mitkToFDistanceImageToSurfaceFilterTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("ToFDistanceImageToSurfaceFilter"); mitk::ToFDistanceImageToSurfaceFilter::Pointer filter = mitk::ToFDistanceImageToSurfaceFilter::New(); // create test image unsigned int dimX =204; unsigned int dimY =204; mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(dimX,dimY); //initialize intrinsic parameters with some arbitrary values ToFScalarType focalLengthX = 295.78960; ToFScalarType focalLengthY = 296.348535; ToFPoint2D focalLengthXY; focalLengthXY[0]=focalLengthX; focalLengthXY[1]=focalLengthY; ToFScalarType k1=-0.36,k2=-0.14,p1=0.001,p2=-0.00; ToFPoint2D principalPoint; principalPoint[0] = 103.576546; principalPoint[1] = 100.1532; mitk::CameraIntrinsics::Pointer cameraIntrinsics = mitk::CameraIntrinsics::New(); cameraIntrinsics->SetFocalLength(focalLengthX,focalLengthY); cameraIntrinsics->SetPrincipalPoint(principalPoint[0],principalPoint[1]); cameraIntrinsics->SetDistorsionCoeffs(k1,k2,p1,p2); // test SetCameraIntrinsics() filter->SetCameraIntrinsics(cameraIntrinsics); MITK_TEST_CONDITION_REQUIRED((focalLengthX==filter->GetCameraIntrinsics()->GetFocalLengthX()),"Testing SetCameraIntrinsics with focalLength"); ToFPoint2D pp; pp[0] = filter->GetCameraIntrinsics()->GetPrincipalPointX(); pp[1] = filter->GetCameraIntrinsics()->GetPrincipalPointY(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(principalPoint,pp),"Testing SetCameraIntrinsics with principalPoint()"); // test SetInterPixelDistance() ToFPoint2D interPixelDistance; interPixelDistance[0] = 0.04564; interPixelDistance[1] = 0.0451564; filter->SetInterPixelDistance(interPixelDistance); ToFPoint2D ipD = filter->GetInterPixelDistance(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(ipD,interPixelDistance),"Testing Set/GetInterPixelDistance()"); // test SetReconstructionMode() filter->SetReconstructionMode(mitk::ToFDistanceImageToSurfaceFilter::WithInterPixelDistance); MITK_TEST_CONDITION_REQUIRED(filter->GetReconstructionMode() == mitk::ToFDistanceImageToSurfaceFilter::WithInterPixelDistance,"Testing Set/GetReconstructionMode()"); // test Set/GetInput() filter->SetInput(image); MITK_TEST_CONDITION_REQUIRED((image==filter->GetInput()),"Testing Set/GetInput()"); // test filter without subset (without interpixeldistance) MITK_INFO<<"Test filter with subset without interpixeldistance "; filter->SetReconstructionMode(mitk::ToFDistanceImageToSurfaceFilter::WithOutInterPixelDistance); MITK_TEST_CONDITION_REQUIRED(filter->GetReconstructionMode() == mitk::ToFDistanceImageToSurfaceFilter::WithOutInterPixelDistance,"Testing Set/GetReconstructionMode()"); vtkSmartPointer expectedResult = vtkSmartPointer::New(); expectedResult->SetDataTypeToDouble(); unsigned int counter = 0; double* point = new double[3]; // MITK_INFO<<"Test"; // MITK_INFO<<"focal: "< index = {{ i, j }}; float distance = 0.0; try { mitk::ImagePixelReadAccessor readAccess(image, image->GetSliceData()); distance = readAccess.GetPixelByIndex(index); } catch(mitk::Exception& e) { MITK_ERROR << "Image read exception!" << e.what(); } ToFPoint3D coordinate = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(i,j,distance,focalLengthX,focalLengthY,principalPoint[0],principalPoint[1]); // if ((i==0)&&(j==0)) // { // MITK_INFO<<"Distance test: "<InsertPoint(pointID,point); } counter++; } } filter->Update(); mitk::Surface::Pointer resultSurface = filter->GetOutput(); vtkSmartPointer result = vtkSmartPointer::New(); result->SetDataTypeToDouble(); result = resultSurface->GetVtkPolyData()->GetPoints(); MITK_TEST_CONDITION_REQUIRED((expectedResult->GetNumberOfPoints()==result->GetNumberOfPoints()),"Test if number of points in surface is equal"); bool pointSetsEqual = true; for (unsigned int i=0; iGetNumberOfPoints(); i++) { double* expected = expectedResult->GetPoint(i); double* res = result->GetPoint(i); ToFPoint3D expectedPoint; expectedPoint[0] = expected[0]; expectedPoint[1] = expected[1]; expectedPoint[2] = expected[2]; ToFPoint3D resultPoint; resultPoint[0] = res[0]; resultPoint[1] = res[1]; resultPoint[2] = res[2]; if (!mitk::Equal(expectedPoint,resultPoint)) { // MITK_INFO << i; pointSetsEqual = false; } } MITK_TEST_CONDITION_REQUIRED(pointSetsEqual,"Testing filter without subset"); // test filter without subset (with interpixeldistance) MITK_INFO<<"Test filter with subset with interpixeldistance "; filter->SetReconstructionMode(mitk::ToFDistanceImageToSurfaceFilter::WithInterPixelDistance); MITK_TEST_CONDITION_REQUIRED(filter->GetReconstructionMode() == mitk::ToFDistanceImageToSurfaceFilter::WithInterPixelDistance,"Testing Set/GetReconstructionMode()"); // calculate focal length considering inter pixel distance ToFScalarType focalLength = (focalLengthX*interPixelDistance[0]+focalLengthY*interPixelDistance[1])/2.0; expectedResult = vtkSmartPointer::New(); expectedResult->SetDataTypeToDouble(); counter = 0; point = new double[3]; // MITK_INFO<<"Test"; // MITK_INFO<<"focal: "< index = {{ i, j }}; float distance = 0.0; try { mitk::ImagePixelReadAccessor readAccess(image, image->GetSliceData()); distance = readAccess.GetPixelByIndex(index); } catch(mitk::Exception& e) { MITK_ERROR << "Image read exception!" << e.what(); } ToFPoint3D coordinate = mitk::ToFProcessingCommon::IndexToCartesianCoordinatesWithInterpixdist(i,j,distance,focalLength,interPixelDistance,principalPoint); // if ((i==0)&&(j==0)) // { // MITK_INFO<<"Distance test: "<InsertPoint(pointID,point); } counter++; } } filter->Modified(); filter->Update(); resultSurface = filter->GetOutput(); result = vtkSmartPointer::New(); result->SetDataTypeToDouble(); result = resultSurface->GetVtkPolyData()->GetPoints(); MITK_TEST_CONDITION_REQUIRED((expectedResult->GetNumberOfPoints()==result->GetNumberOfPoints()),"Test if number of points in surface is equal"); pointSetsEqual = true; for (unsigned int i=0; iGetNumberOfPoints(); i++) { double* expected = expectedResult->GetPoint(i); double* res = result->GetPoint(i); ToFPoint3D expectedPoint; expectedPoint[0] = expected[0]; expectedPoint[1] = expected[1]; expectedPoint[2] = expected[2]; ToFPoint3D resultPoint; resultPoint[0] = res[0]; resultPoint[1] = res[1]; resultPoint[2] = res[2]; if (!mitk::Equal(expectedPoint,resultPoint)) { // MITK_INFO << i; MITK_INFO<<"expected: "<GetNumberOfPoints(); i++) { double* expected = expectedResult->GetPoint(i); double* res = result->GetPoint(i); ToFPoint3D expectedPoint; expectedPoint[0] = expected[0]; expectedPoint[1] = expected[1]; expectedPoint[2] = expected[2]; ToFPoint3D resultPoint; resultPoint[0] = res[0]; resultPoint[1] = res[1]; resultPoint[2] = res[2]; ToFPoint3D expectedPointBackward = mitk::ToFProcessingCommon::CartesianToIndexCoordinates(expectedPoint,focalLengthXY,principalPoint); ToFPoint3D resultPointBackward = mitk::ToFProcessingCommon::CartesianToIndexCoordinates(resultPoint,focalLengthXY,principalPoint); if (!mitk::Equal(expectedPointBackward,resultPointBackward)) { // MITK_INFO << i; // MITK_INFO<<"expected: "<GetNumberOfPoints(); i++) { double* expected = expectedResult->GetPoint(i); double* res = result->GetPoint(i); ToFPoint3D expectedPoint; expectedPoint[0] = expected[0]; expectedPoint[1] = expected[1]; expectedPoint[2] = expected[2]; ToFPoint3D resultPoint; resultPoint[0] = res[0]; resultPoint[1] = res[1]; resultPoint[2] = res[2]; ToFPoint3D expectedPointBackward = mitk::ToFProcessingCommon::CartesianToIndexCoordinatesWithInterpixdist(expectedPoint,focalLength,interPixelDistance,principalPoint); ToFPoint3D resultPointBackward = mitk::ToFProcessingCommon::CartesianToIndexCoordinatesWithInterpixdist(resultPoint,focalLength,interPixelDistance,principalPoint); if (!mitk::Equal(expectedPointBackward,resultPointBackward)) { // MITK_INFO << i; // MITK_INFO<<"expected: "<GetNumberOfPoints(); i++) { double* res = result->GetPoint(i); ToFPoint3D resultPoint; resultPoint[0] = res[0]; resultPoint[1] = res[1]; resultPoint[2] = res[2]; ToFPoint3D resultPointBackward = mitk::ToFProcessingCommon::CartesianToIndexCoordinates(resultPoint,focalLengthXY,principalPoint); itk::Index<2> index = {{ (int) (resultPointBackward[0]+0.5), (int) (resultPointBackward[1]+0.5) }}; float distanceBackward = 0.0; try { mitk::ImagePixelReadAccessor readAccess(image, image->GetSliceData()); distanceBackward = readAccess.GetPixelByIndex(index); } catch(mitk::Exception& e) { MITK_ERROR << "Image read exception!" << e.what(); } if (!mitk::Equal(distanceBackward,(float) resultPointBackward[2])) { MITK_INFO<<"expected: " << resultPointBackward[2]; MITK_INFO<<"result: "<< distanceBackward; compareToInput = false; } } MITK_TEST_CONDITION_REQUIRED(compareToInput,"Testing backward transformation compared to original image without interpixeldistance"); //Backwardtransformation test compare to original input with interpixeldistance compareToInput = true; for (unsigned int i=0; iGetNumberOfPoints(); i++) { double* res = result->GetPoint(i); ToFPoint3D resultPoint; resultPoint[0] = res[0]; resultPoint[1] = res[1]; resultPoint[2] = res[2]; ToFPoint3D resultPointBackward = mitk::ToFProcessingCommon::CartesianToIndexCoordinatesWithInterpixdist(resultPoint,focalLength,interPixelDistance,principalPoint); itk::Index<2> pixelIndex = {{ (int) (resultPointBackward[0]+0.5), (int) (resultPointBackward[1]+0.5) }}; float distanceBackward = 0.0; try { mitk::ImagePixelReadAccessor readAccess(image, image->GetSliceData()); distanceBackward = readAccess.GetPixelByIndex(pixelIndex); } catch(mitk::Exception& e) { MITK_ERROR << "Image read exception!" << e.what(); } if (!mitk::Equal(distanceBackward, (float) resultPointBackward[2])) { // MITK_INFO<<"expected: "<< image->GetPixelValueByIndex(pixelIndex); // MITK_INFO<<"result: "<< resultPoint; compareToInput = false; } } MITK_TEST_CONDITION_REQUIRED(compareToInput,"Testing backward transformation compared to original image with interpixeldistance"); //clean up delete point; // expectedResult->Delete(); MITK_TEST_END(); } diff --git a/Modules/ToFProcessing/Testing/mitkToFProcessingCommonTest.cpp b/Modules/ToFProcessing/Testing/mitkToFProcessingCommonTest.cpp index c7c914d4b1..7a4a432408 100644 --- a/Modules/ToFProcessing/Testing/mitkToFProcessingCommonTest.cpp +++ b/Modules/ToFProcessing/Testing/mitkToFProcessingCommonTest.cpp @@ -1,91 +1,91 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include /**Documentation * test for the class "ToFProcessingCommon". */ int mitkToFProcessingCommonTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("ToFProcessingCommon"); unsigned int i = 10; unsigned int j = 50; float distance = 1000; float focalLength = 10; mitk::Point2D focalLength_XY; focalLength_XY[0] = 200; focalLength_XY[1] = 200; mitk::Point2D interPixelDistance; interPixelDistance[0] = 0.05; interPixelDistance[1] = 0.05; mitk::Point2D principalPoint; principalPoint[0] = 100; principalPoint[1] = 100; // expected coordinate mitk::ToFProcessingCommon::ToFPoint3D expectedCoordinate; expectedCoordinate[0] = -400.0988; expectedCoordinate[1] = -222.2771; expectedCoordinate[2] = 889.1084; // resulting coordinate without using the interpixeldistance mitk::ToFProcessingCommon::ToFPoint3D resultingCoordinate = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(i,j,distance,focalLength_XY,principalPoint); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(expectedCoordinate,resultingCoordinate,1e-3),"Testing IndexToCartesianCoordinates()"); // resulting coordinate with using the interpixeldistance mitk::ToFProcessingCommon::ToFPoint3D resultingCoordinateInterpix = mitk::ToFProcessingCommon::IndexToCartesianCoordinatesWithInterpixdist(i,j,distance,focalLength,interPixelDistance,principalPoint); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(expectedCoordinate,resultingCoordinateInterpix,1e-3),"Testing IndexToCartesianCoordinatesWithInterpixdist()"); // expected index mitk::ToFProcessingCommon::ToFPoint3D expectedIndex; expectedIndex[0] = i; expectedIndex[1] = j; expectedIndex[2] = 1000; mitk::ToFProcessingCommon::ToFPoint3D resultingIndex = mitk::ToFProcessingCommon::CartesianToIndexCoordinates(expectedCoordinate,focalLength_XY,principalPoint); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(expectedIndex,resultingIndex,1e-3),"Testing CartesianToIndexCoordinates()"); mitk::ToFProcessingCommon::ToFPoint3D resultingIndexInterpix = mitk::ToFProcessingCommon::CartesianToIndexCoordinatesWithInterpixdist(expectedCoordinate,focalLength,interPixelDistance,principalPoint); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(expectedIndex,resultingIndexInterpix,1e-3),"Testing CartesianToIndexCoordinatesWithInterpixdist()"); //########## Kinect Reconstruction ############# mitk::ToFProcessingCommon::ToFPoint3D expectedKinectCoordinate; expectedKinectCoordinate[0] = -450.0; expectedKinectCoordinate[1] = -250.0; expectedKinectCoordinate[2] = 1000.0; - mitk::Index3D index; + itk::Index<3> index; index[0] = i; index[1] = j; index[2] = 0; mitk::ToFProcessingCommon::ToFPoint3D kinectReconstructionResult = mitk::ToFProcessingCommon::KinectIndexToCartesianCoordinates(index,distance, focalLength_XY,principalPoint); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(expectedKinectCoordinate,kinectReconstructionResult),"Compare the expected result with the result of reconstruction from KinectIndexToCartesianCoordinates()"); mitk::ToFProcessingCommon::ToFPoint3D kinectReconstructionResultBackward = mitk::ToFProcessingCommon::CartesianToKinectIndexCoordinates(kinectReconstructionResult, focalLength_XY, principalPoint); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(expectedIndex,kinectReconstructionResultBackward),"Transform everything back to distance image and compare it to the original input"); mitk::Point2D continuousIndex; continuousIndex[0] = i; continuousIndex[1] = j; mitk::ToFProcessingCommon::ToFPoint3D continuousKinectReconstructionResult = mitk::ToFProcessingCommon::ContinuousKinectIndexToCartesianCoordinates(continuousIndex,distance, focalLength_XY[0], focalLength_XY[1], principalPoint[0], principalPoint[1]); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(expectedKinectCoordinate,continuousKinectReconstructionResult),"Compare the expected result with the result of reconstruction from ContinuousKinectIndexToCartesianCoordinates(). Since the index is not continuous, the result has to be the same like for KinectIndexToCartesianCoordinates()."); //########## End Kinect Reconstruction ############# MITK_TEST_END(); } diff --git a/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.cpp b/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.cpp index 34c622fb0a..5cc2e92f51 100644 --- a/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.cpp +++ b/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.cpp @@ -1,218 +1,218 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkToFDistanceImageToPointSetFilter.h" #include "mitkImageDataItem.h" #include "mitkPointSet.h" #include #include "mitkToFProcessingCommon.h" mitk::ToFDistanceImageToPointSetFilter::ToFDistanceImageToPointSetFilter() : m_Subset(NULL), m_CameraIntrinsics(), m_InterPixelDistance() { m_InterPixelDistance.Fill(0.045); m_CameraIntrinsics = mitk::CameraIntrinsics::New(); m_CameraIntrinsics->SetFocalLength(5.9421434211923247e+02,5.9104053696870778e+02); m_CameraIntrinsics->SetPrincipalPoint(3.3930780975300314e+02,2.4273913761751615e+02); m_CameraIntrinsics->SetDistorsionCoeffs(-0.36874385358645773f,-0.14339503290129013,0.0033210108720361795,-0.004277703352074105); m_ReconstructionMode = true; } mitk::ToFDistanceImageToPointSetFilter::~ToFDistanceImageToPointSetFilter() { } void mitk::ToFDistanceImageToPointSetFilter::SetInput(const mitk::Image* distanceImage ) { this->SetInput(0,distanceImage); } void mitk::ToFDistanceImageToPointSetFilter::SetInput( unsigned int idx,const mitk::Image* distanceImage ) { if ((distanceImage == NULL) && (idx == this->GetNumberOfInputs() - 1)) // if the last input is set to NULL, reduce the number of inputs by one { this->SetNumberOfInputs(this->GetNumberOfInputs() - 1); } else { this->ProcessObject::SetNthInput(idx, const_cast(distanceImage)); // Process object is not const-correct so the const_cast is required here this->CreateOutputsForAllInputs(); } } mitk::Image* mitk::ToFDistanceImageToPointSetFilter::GetInput() { return this->GetInput(0); } mitk::Image* mitk::ToFDistanceImageToPointSetFilter::GetInput( unsigned int idx ) { if (this->GetNumberOfInputs() < 1) return NULL; return static_cast< mitk::Image*>(this->ProcessObject::GetInput(idx)); } -void mitk::ToFDistanceImageToPointSetFilter::SetSubset(std::vector subset) +void mitk::ToFDistanceImageToPointSetFilter::SetSubset(std::vector > subset) { // check if points of PointSet are inside the input image mitk::Image::Pointer input = this->GetInput(); unsigned int xDim = UINT_MAX; unsigned int yDim = UINT_MAX; if(input.IsNotNull() && input->IsInitialized()) { unsigned int xDim = input->GetDimension(0); unsigned int yDim = input->GetDimension(1); } bool pointSetValid = true; for (unsigned int i=0; i currentIndex = subset.at(i); if (currentIndex[0]<0||currentIndex[0]>xDim||currentIndex[1]<0||currentIndex[1]>yDim) { pointSetValid = false; } } if (pointSetValid) { m_Subset = subset; } else { MITK_ERROR<<"One or more indizes are located outside the image domain"; } } void mitk::ToFDistanceImageToPointSetFilter::SetSubset( mitk::PointSet::Pointer pointSet) { - std::vector subset; + std::vector > subset; for (int i=0; iGetSize(); i++) { mitk::Point3D currentPoint = pointSet->GetPoint(i); - mitk::Index3D currentIndex; + itk::Index<3> currentIndex; currentIndex[0] = currentPoint[0]; currentIndex[1] = currentPoint[1]; currentIndex[2] = currentPoint[2]; subset.push_back(currentIndex); } this->SetSubset(subset); } void mitk::ToFDistanceImageToPointSetFilter::GenerateData() { //calculate world coordinates mitk::ToFProcessingCommon::ToFPoint2D focalLengthInPixelUnits; mitk::ToFProcessingCommon::ToFScalarType focalLengthInMm; if (m_ReconstructionMode) { focalLengthInPixelUnits[0] = m_CameraIntrinsics->GetFocalLengthX(); focalLengthInPixelUnits[1] = m_CameraIntrinsics->GetFocalLengthY(); } else focalLengthInMm = (m_CameraIntrinsics->GetFocalLengthX()*m_InterPixelDistance[0]+m_CameraIntrinsics->GetFocalLengthY()*m_InterPixelDistance[1])/2.0; mitk::ToFProcessingCommon::ToFPoint2D principalPoint; principalPoint[0] = m_CameraIntrinsics->GetPrincipalPointX(); principalPoint[1] = m_CameraIntrinsics->GetPrincipalPointY(); mitk::PointSet::Pointer output = this->GetOutput(); assert(output); mitk::Image::Pointer input = this->GetInput(); assert(input); //compute subset of points if input PointSet is defined if (m_Subset.size()!=0) { mitk::ImagePixelReadAccessor imageAcces(input, input->GetSliceData(0)); for (unsigned int i=0; i currentIndex = m_Subset.at(i); itk::Index<2> index2D; index2D[0] = currentIndex[0]; index2D[1] = currentIndex[1]; mitk::ToFProcessingCommon::ToFScalarType distance = (double)imageAcces.GetPixelByIndex(index2D); mitk::Point3D currentPoint; if (m_ReconstructionMode) currentPoint = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(currentIndex,distance,focalLengthInPixelUnits,principalPoint); else currentPoint = mitk::ToFProcessingCommon::IndexToCartesianCoordinatesWithInterpixdist(currentIndex,distance,focalLengthInMm,m_InterPixelDistance,principalPoint); output->InsertPoint(i,currentPoint); } } else //compute PointSet holding cartesian coordinates for every image point { int xDimension = (int)input->GetDimension(0); int yDimension = (int)input->GetDimension(1); int pointCount = 0; mitk::ImagePixelReadAccessor imageAcces(input, input->GetSliceData(0)); for (int j=0; j pixel; pixel[0] = i; pixel[1] = j; mitk::ToFProcessingCommon::ToFScalarType distance = (double)imageAcces.GetPixelByIndex(pixel); mitk::Point3D currentPoint; if (m_ReconstructionMode) currentPoint = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(i,j,distance,focalLengthInPixelUnits,principalPoint); else currentPoint = mitk::ToFProcessingCommon::IndexToCartesianCoordinatesWithInterpixdist(i,j,distance,focalLengthInMm,m_InterPixelDistance,principalPoint); if (distance>mitk::eps) { output->InsertPoint( pointCount, currentPoint ); pointCount++; } } } } } void mitk::ToFDistanceImageToPointSetFilter::CreateOutputsForAllInputs() { this->SetNumberOfOutputs(this->GetNumberOfInputs()); // create outputs for all inputs for (unsigned int idx = 0; idx < this->GetNumberOfIndexedOutputs(); ++idx) if (this->GetOutput(idx) == NULL) { DataObjectPointer newOutput = this->MakeOutput(idx); this->SetNthOutput(idx, newOutput); } this->Modified(); } void mitk::ToFDistanceImageToPointSetFilter::GenerateOutputInformation() { this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); } void mitk::ToFDistanceImageToPointSetFilter::SetReconstructionMode(bool withoutInterpixdist) { this->m_ReconstructionMode = withoutInterpixdist; } bool mitk::ToFDistanceImageToPointSetFilter::GetReconstructionMode() { return (this->m_ReconstructionMode); } diff --git a/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.h b/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.h index af6f32e662..acc70d584f 100644 --- a/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.h +++ b/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.h @@ -1,139 +1,139 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkToFDistanceImageToPointSetFilter_h #define __mitkToFDistanceImageToPointSetFilter_h #include #include "mitkImage.h" #include "mitkPointSet.h" #include #include "mitkImageSource.h" #include #include namespace mitk { /** * @brief Converts a Time-of-Flight (ToF) distance image to a PointSet using the pinhole camera model for coordinate computation. * The intrinsic parameters of the camera (FocalLength, PrincipalPoint, InterPixelDistance) are set via SetIntrinsicParameters(). The * measured distance for each pixel corresponds to the distance between the object point and the corresponding image point on the * image plane. * If a subset of indizes of the image is defined via SetSubset(), the output PointSet will only contain the cartesian coordinates * of the corresponding 3D points. * * The coordinate conversion follows the model of a common pinhole camera where the origin of the camera * coordinate system (world coordinates) is at the pinhole * \image html ../Modules/ToFProcessing/Documentation/PinholeCameraModel.png * The definition of the image plane and its coordinate systems (pixel and mm) is depicted in the following image * \image html ../Modules/ToFProcessing/Documentation/ImagePlane.png * * @ingroup SurfaceFilters * @ingroup ToFProcessing */ class MitkToFProcessing_EXPORT ToFDistanceImageToPointSetFilter : public PointSetSource { public: mitkClassMacro( ToFDistanceImageToPointSetFilter , PointSetSource ); itkFactorylessNewMacro(Self) itkCloneMacro(Self) itkSetMacro(CameraIntrinsics,mitk::CameraIntrinsics::Pointer); itkGetMacro(CameraIntrinsics,mitk::CameraIntrinsics::Pointer); itkSetMacro(InterPixelDistance,mitk::ToFProcessingCommon::ToFPoint2D); itkGetMacro(InterPixelDistance,mitk::ToFProcessingCommon::ToFPoint2D); /*! \brief Sets the input of this filter \param distanceImage input is the distance image of e.g. a ToF camera */ virtual void SetInput(const Image* distanceImage); /*! \brief Sets the input of this filter at idx \param idx number of the current input \param distanceImage input is the distance image of e.g. a ToF camera */ virtual void SetInput(unsigned int idx,const Image* distanceImage); /*! \brief Returns the input of this filter */ Image* GetInput(); /*! \brief Returns the input with id idx of this filter */ Image* GetInput(unsigned int idx); /*! \brief If this subset is defined, the cartesian coordinates are only computed for the contained indizes. Make sure the indizes are contained in the input image \param subset index subset specified in index coordinates. */ - void SetSubset( std::vector subset); + void SetSubset( std::vector > subset); /*! \brief Sets the subset of indizes used for caluclation of output PointSet as a PointSet. Warning: make sure the points in your PointSet are index coordinates. \param PointSet specified in index coordinates. */ void SetSubset( mitk::PointSet::Pointer pointSet); /*! \brief Sets the reconstruction mode, if using no interpixeldistances and focal lenghts in pixel units (=true) or interpixeldistances and focal length in mm (=false) */ void SetReconstructionMode(bool withoutInterpixdist = true); /*! \brief Returns the reconstruction mode */ bool GetReconstructionMode(); protected: /*! \brief Standard constructor */ ToFDistanceImageToPointSetFilter(); /*! \brief Standard destructor */ ~ToFDistanceImageToPointSetFilter(); virtual void GenerateOutputInformation(); /*! \brief Method generating the output of this filter. Called in the updated process of the pipeline. This method generates the output of the ToFSurfaceSource: The generated surface of the 3d points */ virtual void GenerateData(); /** * \brief Create an output for each input * * This Method sets the number of outputs to the number of inputs * and creates missing outputs objects. * \warning any additional outputs that exist before the method is called are deleted */ void CreateOutputsForAllInputs(); - std::vector m_Subset; ///< If this subset is specified only the contained indizes are converted to cartesian coordinates + std::vector > m_Subset; ///< If this subset is specified only the contained indizes are converted to cartesian coordinates mitk::CameraIntrinsics::Pointer m_CameraIntrinsics; ///< Member holding the intrinsic parameters needed for PointSet calculation ToFProcessingCommon::ToFPoint2D m_InterPixelDistance; ///< distance in mm between two adjacent pixels on the ToF camera chip bool m_ReconstructionMode; ///< true = Reconstruction without interpixeldistance and with focal lengths in pixel units. false = Reconstruction with interpixeldistance and with focal length in mm. }; } //END mitk namespace #endif diff --git a/Modules/ToFProcessing/mitkToFProcessingCommon.h b/Modules/ToFProcessing/mitkToFProcessingCommon.h index 1d0c959a48..ed9ba61f9a 100644 --- a/Modules/ToFProcessing/mitkToFProcessingCommon.h +++ b/Modules/ToFProcessing/mitkToFProcessingCommon.h @@ -1,345 +1,345 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKTOFPROCESSINGCOMMON_H #define MITKTOFPROCESSINGCOMMON_H #include #include -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include namespace mitk { /** * @brief Helper class providing functions which are useful for multiple usage * * Currently the following methods are provided: *
    *
  • Conversion from 2D image coordinates to 3D world coordinates (IndexToCartesianCoordinates()) *
  • Conversion from 3D world coordinates to 2D image coordinates (CartesianToIndexCoordinates()) *
* The coordinate conversion follows the model of a common pinhole camera where the origin of the camera * coordinate system (world coordinates) is at the pinhole * \image html ../Modules/ToFProcessing/Documentation/PinholeCameraModel.png * The definition of the image plane and its coordinate systems (pixel and mm) is depicted in the following image * \image html ../Modules/ToFProcessing/Documentation/ImagePlane.png * @ingroup ToFProcessing */ class MitkToFProcessing_EXPORT ToFProcessingCommon { public: typedef double ToFScalarType; typedef itk::Point ToFPoint2D; typedef itk::Point ToFPoint3D; typedef itk::Vector ToFVector2D; typedef itk::Vector ToFVector3D; /*! \brief Convert index based distances to cartesian coordinates \param i index in x direction of image plane \param j index in y direction of image plane \param distance distance value at given index in mm \param focalLengthX focal length of optical system in pixel units in x-direction (mostly obtained from camera calibration) \param focalLengthY focal length of optical system in pixel units in y-direction (mostly obtained from camera calibration) \param principalPointX x coordinate of principal point on image plane in pixel \param principalPointY y coordinate of principal point on image plane in pixel \return cartesian coordinates for current index will be written here */ static ToFPoint3D IndexToCartesianCoordinates(unsigned int i, unsigned int j, ToFScalarType distance, ToFScalarType focalLengthX, ToFScalarType focalLengthY, ToFScalarType principalPointX, ToFScalarType principalPointY); /*! \brief Convert index based distances to cartesian coordinates \param i index in x direction of image plane \param j index in y direction of image plane \param distance distance value at given index in mm \param focalLength focal length of optical system in pixel units (mostly obtained from camera calibration) \param principalPoint coordinates of principal point on image plane in pixel \return cartesian coordinates for current index will be written here */ inline static ToFPoint3D IndexToCartesianCoordinates(unsigned int i, unsigned int j, ToFScalarType distance, ToFPoint2D focalLength, ToFPoint2D principalPoint) { return IndexToCartesianCoordinates(i,j,distance,focalLength[0],focalLength[1],principalPoint[0],principalPoint[1]); } /*! \brief Convert index based distances to cartesian coordinates \param index index coordinates \param distance distance value at given index in mm \param focalLength focal length of optical system in pixel units (mostly obtained from camera calibration) \param principalPoint coordinates of principal point on image plane in pixel \return cartesian coordinates for current index will be written here */ - inline static ToFPoint3D IndexToCartesianCoordinates(mitk::Index3D index, ToFScalarType distance, + inline static ToFPoint3D IndexToCartesianCoordinates(itk::Index<3> index, ToFScalarType distance, ToFPoint2D focalLength, ToFPoint2D principalPoint) { return IndexToCartesianCoordinates(index[0],index[1],distance,focalLength[0],focalLength[1],principalPoint[0], principalPoint[1]); } /*! \brief Convenience method to convert index based distances to cartesian coordinates using array as input \param i index in x direction of image plane \param j index in y direction of image plane \param distance distance value at given index in mm \param focalLength focal length of optical system in pixel units (mostly obtained from camera calibration) \param principalPoint coordinates of principal point on image plane in pixel \return cartesian coordinates for current index will be written here */ inline static ToFPoint3D IndexToCartesianCoordinates(unsigned int i, unsigned int j, ToFScalarType distance, ToFScalarType focalLength[2], ToFScalarType principalPoint[2]) { return IndexToCartesianCoordinates(i,j,distance,focalLength[0],focalLength[1],principalPoint[0],principalPoint[1]); } /*! \brief Convert index based distances to cartesian coordinates \param i index in x direction of image plane \param j index in y direction of image plane \param distance distance value at given index in mm \param focalLength focal length of optical system in mm (mostly obtained from camera calibration) \param interPixelDistanceX distance in x direction between adjacent pixels in mm \param interPixelDistanceY distance in y direction between adjacent pixels in mm \param principalPointX x coordinate of principal point on image plane in pixel \param principalPointY y coordinate of principal point on image plane in pixel \return cartesian coordinates for current index will be written here */ static ToFPoint3D IndexToCartesianCoordinatesWithInterpixdist(unsigned int i, unsigned int j, ToFScalarType distance, ToFScalarType focalLength, ToFScalarType interPixelDistanceX, ToFScalarType interPixelDistanceY, ToFScalarType principalPointX, ToFScalarType principalPointY); /*! \brief Convert index based distances to cartesian coordinates \param i index in x direction of image plane \param j index in y direction of image plane \param distance distance value at given index in mm \param focalLength focal length of optical system in mm (mostly obtained from camera calibration) \param interPixelDistance distance between adjacent pixels in mm \param principalPoint coordinates of principal point on image plane in pixel \return cartesian coordinates for current index will be written here */ inline static ToFPoint3D IndexToCartesianCoordinatesWithInterpixdist(unsigned int i, unsigned int j, ToFScalarType distance, ToFScalarType focalLength, ToFPoint2D interPixelDistance, ToFPoint2D principalPoint) { return IndexToCartesianCoordinatesWithInterpixdist(i,j,distance,focalLength,interPixelDistance[0],interPixelDistance[1],principalPoint[0],principalPoint[1]); } /*! \brief Convert index based distances to cartesian coordinates \param index index coordinates \param distance distance value at given index in mm \param focalLength focal length of optical system (mostly obtained from camera calibration) \param interPixelDistance distance between adjacent pixels in mm for x and y direction \param principalPoint coordinates of principal point on image plane in pixel \return cartesian coordinates for current index will be written here */ - inline static ToFPoint3D IndexToCartesianCoordinatesWithInterpixdist(mitk::Index3D index, ToFScalarType distance, ToFScalarType focalLength, + inline static ToFPoint3D IndexToCartesianCoordinatesWithInterpixdist(itk::Index<3> index, ToFScalarType distance, ToFScalarType focalLength, ToFPoint2D interPixelDistance, ToFPoint2D principalPoint) { return IndexToCartesianCoordinatesWithInterpixdist(index[0],index[1],distance,focalLength,interPixelDistance[0], interPixelDistance[1],principalPoint[0], principalPoint[1]); } /*! \brief Convenience method to convert index based distances to cartesian coordinates using array as input \param i index in x direction of image plane \param j index in y direction of image plane \param distance distance value at given index in mm \param focalLength focal length of optical system in mm (mostly obtained from camera calibration) \param interPixelDistance distance between adjacent pixels in mm \param principalPoint coordinates of principal point on image plane in pixel \return cartesian coordinates for current index will be written here */ inline static ToFPoint3D IndexToCartesianCoordinatesWithInterpixdist(unsigned int i, unsigned int j, ToFScalarType distance, ToFScalarType focalLength, ToFScalarType interPixelDistance[2], ToFScalarType principalPoint[2]) { return IndexToCartesianCoordinatesWithInterpixdist(i,j,distance,focalLength,interPixelDistance[0],interPixelDistance[1],principalPoint[0],principalPoint[1]); } /*! \brief Convert cartesian coordinates to index based distances \param cartesianPointX x coordinate of point (of a surface or point set) to convert in 3D coordinates \param cartesianPointY y coordinate of point (of a surface or point set) to convert in 3D coordinates \param cartesianPointZ z coordinate of point (of a surface or point set) to convert in 3D coordinates \param focalLengthX focal length of optical system in pixel units in x-direction (mostly obtained from camera calibration) \param focalLengthY focal length of optical system in pixel units in y-direction (mostly obtained from camera calibration) \param principalPointX x coordinate of principal point on image plane in pixel \param principalPointY y coordinate of principal point on image plane in pixel \param calculateDistance if this flag is set, the distance value is stored in the z position of the output otherwise z=0 \return a ToFPoint3D. (int)ToFPoint3D[0]+0.5 and (int)ToFPoint3D[0]+0.5 will return the x and y index coordinates. ToFPoint3D[2] contains the distance value */ static ToFPoint3D CartesianToIndexCoordinates(ToFScalarType cartesianPointX, ToFScalarType cartesianPointY,ToFScalarType cartesianPointZ, ToFScalarType focalLengthX, ToFScalarType focalLengthY, ToFScalarType principalPointX, ToFScalarType principalPointY, bool calculateDistance=true); /*! \brief Convenience method to convert cartesian coordinates to index based distances using arrays \param cartesianPoint point (of a surface or point set) to convert in 3D coordinates \param focalLength focal length of optical system in pixel units (mostly obtained from camera calibration) \param principalPoint coordinates of principal point on image plane in pixel \param calculateDistance if this flag is set, the distance value is stored in the z position of the output otherwise z=0 \return a ToFPoint3D. (int)ToFPoint3D[0]+0.5 and (int)ToFPoint3D[0]+0.5 will return the x and y index coordinates. ToFPoint3D[2] contains the distance value */ inline static ToFPoint3D CartesianToIndexCoordinates(ToFScalarType cartesianPoint[3], ToFScalarType focalLength[2], ToFScalarType principalPoint[2], bool calculateDistance=true) { return CartesianToIndexCoordinates(cartesianPoint[0],cartesianPoint[1],cartesianPoint[2],focalLength[0], focalLength[1], principalPoint[0],principalPoint[1],calculateDistance); } /*! \brief Convert cartesian coordinates to index based distances \param cartesianPoint point (of a surface or point set) to convert in 3D coordinates \param focalLength focal length of optical system in pixel units (mostly obtained from camera calibration) \param principalPoint coordinates of principal point on image plane in pixel \param calculateDistance if this flag is set, the distance value is stored in the z position of the output otherwise z=0 \return a ToFPoint3D. (int)ToFPoint3D[0]+0.5 and (int)ToFPoint3D[0]+0.5 will return the x and y index coordinates. ToFPoint3D[2] contains the distance value */ inline static ToFPoint3D CartesianToIndexCoordinates(ToFPoint3D cartesianPoint, ToFPoint2D focalLength, ToFPoint2D principalPoint, bool calculateDistance=true) { return CartesianToIndexCoordinates(cartesianPoint[0],cartesianPoint[1],cartesianPoint[2],focalLength[0], focalLength[1], principalPoint[0],principalPoint[1],calculateDistance); } /*! \brief Convert cartesian coordinates to index based distances \param cartesianPointX x coordinate of point (of a surface or point set) to convert in 3D coordinates \param cartesianPointY y coordinate of point (of a surface or point set) to convert in 3D coordinates \param cartesianPointZ z coordinate of point (of a surface or point set) to convert in 3D coordinates \param focalLength focal length of optical system in mm (mostly obtained from camera calibration) \param interPixelDistanceX distance in x direction between adjacent pixels in mm \param interPixelDistanceY distance in y direction between adjacent pixels in mm \param principalPointX x coordinate of principal point on image plane in pixel \param principalPointY y coordinate of principal point on image plane in pixel \param calculateDistance if this flag is set, the distance value is stored in the z position of the output otherwise z=0 \return a ToFPoint3D. (int)ToFPoint3D[0]+0.5 and (int)ToFPoint3D[0]+0.5 will return the x and y index coordinates. ToFPoint3D[2] contains the distance value */ static ToFPoint3D CartesianToIndexCoordinatesWithInterpixdist(ToFScalarType cartesianPointX, ToFScalarType cartesianPointY,ToFScalarType cartesianPointZ, ToFScalarType focalLength, ToFScalarType interPixelDistanceX, ToFScalarType interPixelDistanceY, ToFScalarType principalPointX, ToFScalarType principalPointY, bool calculateDistance=true); /*! \brief Convenience method to convert cartesian coordinates to index based distances using arrays \param cartesianPoint point (of a surface or point set) to convert in 3D coordinates \param focalLength focal length of optical system in mm (mostly obtained from camera calibration) \param interPixelDistance distance between adjacent pixels in mm for x and y direction \param principalPoint coordinates of principal point on image plane in pixel \param calculateDistance if this flag is set, the distance value is stored in the z position of the output otherwise z=0 \return a ToFPoint3D. (int)ToFPoint3D[0]+0.5 and (int)ToFPoint3D[0]+0.5 will return the x and y index coordinates. ToFPoint3D[2] contains the distance value */ inline static ToFPoint3D CartesianToIndexCoordinatesWithInterpixdist(ToFScalarType cartesianPoint[3], ToFScalarType focalLength, ToFScalarType interPixelDistance[2], ToFScalarType principalPoint[2], bool calculateDistance=true) { return CartesianToIndexCoordinatesWithInterpixdist(cartesianPoint[0],cartesianPoint[1],cartesianPoint[2],focalLength, interPixelDistance[0],interPixelDistance[1],principalPoint[0],principalPoint[1],calculateDistance); } /*! \brief Convert cartesian coordinates to index based distances \param cartesianPoint point (of a surface or point set) to convert in 3D coordinates \param focalLength focal length of optical system in mm (mostly obtained from camera calibration) \param interPixelDistance distance between adjacent pixels in mm for x and y direction \param principalPoint coordinates of principal point on image plane in pixel \param calculateDistance if this flag is set, the distance value is stored in the z position of the output otherwise z=0 \return a ToFPoint3D. (int)ToFPoint3D[0]+0.5 and (int)ToFPoint3D[0]+0.5 will return the x and y index coordinates. ToFPoint3D[2] contains the distance value */ inline static ToFPoint3D CartesianToIndexCoordinatesWithInterpixdist(ToFPoint3D cartesianPoint, ToFScalarType focalLength, ToFPoint2D interPixelDistance, ToFPoint2D principalPoint, bool calculateDistance=true) { return CartesianToIndexCoordinatesWithInterpixdist(cartesianPoint[0],cartesianPoint[1],cartesianPoint[2],focalLength, interPixelDistance[0],interPixelDistance[1],principalPoint[0],principalPoint[1],calculateDistance); } /** \ingroup KinectReconstruction * @{ * * @brief KinectIndexToCartesianCoordinates Convert a pixel (i,j) with value d to a 3D world point. This conversion is meant for Kinect and slightly different then ToF reconstruction. See also "Hacking the Kinect" - Jeff Kramer, Matt Parker, Daniel Herrera C., Nicolas Burrus, Florian Echtler, Chapter 7, Part 1 "Moving from Depth Map to Point Cloud. * @param i Pixel index i. * @param j Pixel index j. * @param distance Distance value d in mm as obtained from OpenNI. * @param focalLengthX Focallength from calibration. * @param focalLengthY Focallength from calibration. * @param principalPointX Principal point from calibration. * @param principalPointY Principal point from calibration. * @return a ToFPoint3D. The point in world coordinates (mm). */ static ToFProcessingCommon::ToFPoint3D KinectIndexToCartesianCoordinates(unsigned int i, unsigned int j, ToFScalarType distance, ToFScalarType focalLengthX, ToFScalarType focalLengthY, ToFScalarType principalPointX, ToFScalarType principalPointY); inline static ToFPoint3D KinectIndexToCartesianCoordinates(unsigned int i, unsigned int j, ToFScalarType distance, ToFScalarType focalLength[2], ToFScalarType principalPoint[2]) { return KinectIndexToCartesianCoordinates(i,j,distance,focalLength[0],focalLength[1],principalPoint[0],principalPoint[1]); } inline static ToFPoint3D KinectIndexToCartesianCoordinates(unsigned int i, unsigned int j, ToFScalarType distance, ToFPoint2D focalLength, ToFPoint2D principalPoint) { return KinectIndexToCartesianCoordinates(i,j,distance,focalLength[0],focalLength[1],principalPoint[0],principalPoint[1]); } - inline static ToFPoint3D KinectIndexToCartesianCoordinates(mitk::Index3D index, ToFScalarType distance, ToFPoint2D focalLength, ToFPoint2D principalPoint) + inline static ToFPoint3D KinectIndexToCartesianCoordinates(itk::Index<3> index, ToFScalarType distance, ToFPoint2D focalLength, ToFPoint2D principalPoint) { return KinectIndexToCartesianCoordinates(index[0],index[1],distance,focalLength[0],focalLength[1],principalPoint[0], principalPoint[1]); } /** @}*/ /** \ingroup KinectReconstructionInverse * @{ * @brief CartesianCoordinatesToKinectIndexCoordinates Transform a 3D world point back to distance image pixel coordinates. * @param cartesianPointX x value of the cartesian point. * @param cartesianPointY y value of the cartesian point. * @param cartesianPointZ z value of the cartesian point. * @param focalLengthX x value of the focal length (from calibration). * @param focalLengthY y value of the focal length (from calibration). * @param principalPointX x value of the principal point (from calibration). * @param principalPointY y value of the principal point (from calibration). * @param calculateDistance Do you want to compute also the distance of the distance image? For Kinect, this value is always the same in cartesian and index coordinates. * @return A ToFPoint3D containing the pixel indices (i,j) in [0] and [1] and (optionally) the distance value in [2] (or just 0.0). */ static ToFPoint3D CartesianToKinectIndexCoordinates(ToFScalarType cartesianPointX, ToFScalarType cartesianPointY, ToFScalarType cartesianPointZ, ToFScalarType focalLengthX, ToFScalarType focalLengthY, ToFScalarType principalPointX, ToFScalarType principalPointY, bool calculateDistance=true); inline static ToFProcessingCommon::ToFPoint3D CartesianToKinectIndexCoordinates(ToFPoint3D cartesianPoint, ToFPoint2D focalLength, ToFPoint2D principalPoint, bool calculateDistance=true) { return CartesianToKinectIndexCoordinates( cartesianPoint[0], cartesianPoint[1], cartesianPoint[2], focalLength[0], focalLength[1], principalPoint[0], principalPoint[1], calculateDistance); } /** @}*/ /** * @brief ContinuousKinectIndexToCartesianCoordinates This method is escpially meant for reconstructing a Kinect point * with continuous index coordinates (i.e. not exactly a pixel position, but a point interpolated between two pixels). * The only difference to KinectIndexToCartesianCoordinates() is that ContinuousKinectIndexToCartesianCoordinates does not * cast to unsigned int for the index. * @param continuousIndex The continuous coordinates (e.g. 0.5; 0.5). * @param distance Distance value d in mm as obtained from OpenNI. * @param focalLengthX x value of the focal length (from calibration). * @param focalLengthY y value of the focal length (from calibration) * @param principalPointX x value of the principal point (from calibration). * @param principalPointY y value of the principal point (from calibration). * @return a ToFPoint3D. The point in world coordinates (mm). */ static ToFProcessingCommon::ToFPoint3D ContinuousKinectIndexToCartesianCoordinates(mitk::Point2D continuousIndex, ToFScalarType distance, ToFScalarType focalLengthX, ToFScalarType focalLengthY, ToFScalarType principalPointX, ToFScalarType principalPointY); /** \brief Calculates the horizontal view angle of the camera with the given intrinsics \param intrinsics intrinsic parameters of the camera \param dimX dimension of the image in horizontal direction angle = atan(principalPoint[0]/focalLength[0]) + atan((dimX-principalPoint[0]/focalLength[0])) **/ static ToFScalarType CalculateViewAngle(mitk::CameraIntrinsics::Pointer intrinsics, unsigned int dimX); }; } #endif diff --git a/Modules/ToFProcessing/mitkToFTestingCommon.h b/Modules/ToFProcessing/mitkToFTestingCommon.h index b76b24f46c..90a7f33d1b 100644 --- a/Modules/ToFProcessing/mitkToFTestingCommon.h +++ b/Modules/ToFProcessing/mitkToFTestingCommon.h @@ -1,103 +1,103 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkToFTestingCOMMON_H #define mitkToFTestingCOMMON_H #include -#include "mitkVector.h" +#include "mitkNumericTypes.h" #include #include #include #include #include #include namespace mitk { class MitkToFProcessing_EXPORT ToFTestingCommon { public: /** * @brief PointSetsEqual Method two test if two point sets contain the same points. mitk::Equal is used for comparison of the points. * @param pointSet1 * @param pointSet2 * @return True if pointsets are equal. */ static bool PointSetsEqual(mitk::PointSet::Pointer pointSet1, mitk::PointSet::Pointer pointSet2) { bool pointSetsEqual = true; if (pointSet1->GetSize()==pointSet2->GetSize()) { for (int i=0; iGetSize(); i++) { mitk::Point3D expectedPoint = pointSet1->GetPoint(i); mitk::Point3D resultPoint = pointSet2->GetPoint(i); if (!mitk::Equal(expectedPoint,resultPoint)) { std::cout << std::endl; std::cout << std::setprecision(12) << "expected: " << expectedPoint; std::cout << std::endl; std::cout << std::setprecision(12) << "resultPoint: " << resultPoint; std::cout << std::endl; pointSetsEqual = false; } } } else { pointSetsEqual = false; MITK_INFO<<"Point sets have different size: "<GetSize()<<" vs. "<GetSize(); } return pointSetsEqual; } /** * @brief VtkPolyDatasEqual Convenience method for comparing the points of two vtkPolyData (using PointSetsEqual). * @param poly1 * @param poly2 * @return True if polydatas are equal. */ static bool VtkPolyDatasEqual( vtkSmartPointer poly1, vtkSmartPointer poly2 ) { return PointSetsEqual(VtkPolyDataToMitkPointSet(poly1), VtkPolyDataToMitkPointSet(poly2)); } /** * @brief VtkPolyDataToMitkPointSet Converts a vtkPolyData into an mitkPointSet * @param poly Input polydata. * @return mitk::PointSet::Pointer The resulting point set. */ static mitk::PointSet::Pointer VtkPolyDataToMitkPointSet( vtkSmartPointer poly ) { mitk::PointSet::Pointer result = mitk::PointSet::New(); int numberOfPoints = poly->GetNumberOfPoints(); for (int i=0; iGetPoint(i); mitk::Point3D point; point[0] = currentPoint[0]; point[1] = currentPoint[1]; point[2] = currentPoint[2]; result->InsertPoint(i,point); } return result; } }; } #endif diff --git a/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPart.h b/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPart.h index 8c6dd19e3c..7ddb753a10 100644 --- a/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPart.h +++ b/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPart.h @@ -1,190 +1,190 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKIRENDERWINDOWPART_H #define MITKIRENDERWINDOWPART_H #include #include #include #include -#include +#include #include #include class QmitkRenderWindow; namespace mitk { struct IRenderingManager; class SliceNavigationController; /** * \ingroup org_mitk_gui_common * * \brief Interface for a MITK Workbench Part providing a render window. * * This interface allows generic access to Workbench parts which provide some * kind of render window. The interface is intended to be implemented by * subclasses of berry::IWorkbenchPart. Usually, the interface is implemented * by a Workbench editor. * * A IRenderWindowPart provides zero or more QmitkRenderWindow instances which can * be controlled via this interface. QmitkRenderWindow instances have an associated * \e id, which is implementation specific. However, implementations should consider * to use one of the following ids for certain QmitkRenderWindow instances to maximize * reusability (they are free to map multiple ids to one QmitkRenderWindow internally): *
    *
  • axial
  • *
  • sagittal
  • *
  • coronal
  • *
  • 3d
  • *
* * \see ILinkedRenderWindowPart * \see IRenderWindowPartListener * \see QmitkAbstractRenderEditor */ struct MITK_GUI_COMMON_PLUGIN IRenderWindowPart { static const QString DECORATION_BORDER; // = "border" static const QString DECORATION_LOGO; // = "logo" static const QString DECORATION_MENU; // = "menu" static const QString DECORATION_BACKGROUND; // = "background; virtual ~IRenderWindowPart(); /** * Get the currently active (focused) render window. * Focus handling is implementation specific. * * \return The active QmitkRenderWindow instance; NULL * if no render window is active. */ virtual QmitkRenderWindow* GetActiveQmitkRenderWindow() const = 0; /** * Get all render windows with their ids. * * \return A hash map mapping the render window id to the QmitkRenderWindow instance. */ virtual QHash GetQmitkRenderWindows() const = 0; /** * Get a render window with a specific id. * * \param id The render window id. * \return The QmitkRenderWindow instance for id */ virtual QmitkRenderWindow* GetQmitkRenderWindow(const QString& id) const = 0; /** * Get the rendering manager used by this render window part. * * \return The current IRenderingManager instance or NULL * if no rendering manager is used. */ virtual mitk::IRenderingManager* GetRenderingManager() const = 0; /** * Request an update of all render windows. * * \param requestType Specifies the type of render windows for which an update * will be requested. */ virtual void RequestUpdate(mitk::RenderingManager::RequestType requestType = mitk::RenderingManager::REQUEST_UPDATE_ALL) = 0; /** * Force an immediate update of all render windows. * * \param requestType Specifies the type of render windows for which an immediate update * will be requested. */ virtual void ForceImmediateUpdate(mitk::RenderingManager::RequestType requestType = mitk::RenderingManager::REQUEST_UPDATE_ALL) = 0; /** * Get the SliceNavigationController for controlling time positions. * * \return A SliceNavigationController if the render window supports this * operation; otherwise returns NULL. */ virtual mitk::SliceNavigationController* GetTimeNavigationController() const = 0; /** * Get the selected position in the render window with id id * or in the active render window if id is NULL. * * \param id The render window id. * \return The currently selected position in world coordinates. */ virtual mitk::Point3D GetSelectedPosition(const QString& id = QString()) const = 0; /** * Set the selected position in the render window with id id * or in the active render window if id is NULL. * * \param pos The position in world coordinates which should be selected. * \param id The render window id in which the selection should take place. */ virtual void SetSelectedPosition(const mitk::Point3D& pos, const QString& id = QString()) = 0; /** * Enable \e decorations like colored borders, menu widgets, logos, text annotations, etc. * * Decorations are implementation specific. A set of standardized decoration names is listed * in GetDecorations(). * * \param enable If true enable the decorations specified in decorations, * otherwise disable them. * \param decorations A list of decoration names. If empty, all supported decorations are affected. * * \see GetDecorations() */ virtual void EnableDecorations(bool enable, const QStringList& decorations = QStringList()) = 0; /** * Return if a specific decoration is enabled. * * \return true if the decoration is enabled, false if it is disabled * or unknown. * * \see GetDecorations() */ virtual bool IsDecorationEnabled(const QString& decoration) const = 0; /** * Get a list of supported decorations. * * The following decoration names are standardized and should not be used for other decoration types: *
    *
  • \e DECORATION_BORDER Any border decorations like colored rectangles, etc. *
  • \e DECORATION_MENU Menus associated with render windows *
  • \e DECORATION_BACKGROUND All kinds of backgrounds (patterns, gradients, etc.) except for solid colored backgrounds *
  • \e DECORATION_LOGO Any kind of logo overlayed on the rendered scene *
* * \return A list of supported decoration names. */ virtual QStringList GetDecorations() const = 0; }; } Q_DECLARE_INTERFACE(mitk::IRenderWindowPart, "org.mitk.ui.IRenderWindowPart") #endif // MITKIRENDERWINDOWPART_H diff --git a/Plugins/org.mitk.gui.qt.tofutil/resources/iconGenerationView.xpm b/Plugins/org.mitk.gui.qt.tofutil/resources/iconGenerationView.xpm index f911df019c..ae07daaf77 100644 --- a/Plugins/org.mitk.gui.qt.tofutil/resources/iconGenerationView.xpm +++ b/Plugins/org.mitk.gui.qt.tofutil/resources/iconGenerationView.xpm @@ -1,3204 +1,3204 @@ -/* XPM */ -static char * C:\Users\iszatt\Desktop\icon ToFDeviceGeneration\icon2_xpm[] = { -"100 100 3101 2", -" c None", -". c #F3F3F3", -"+ c #ACACAC", -"@ c #7E7E7E", -"# c #7C7C7C", -"$ c #5E5E5E", -"% c #E3E3E4", -"& c #A1A1A3", -"* c #8B8B8A", -"= c #646464", -"- c #474747", -"; c #121212", -"> c #323232", -", c #6B6B6B", -"' c #EAEAEA", -") c #C9CACA", -"! c #98989A", -"~ c #777779", -"{ c #454547", -"] c #4A4A4A", -"^ c #2C2C2C", -"/ c #3B3B3B", -"( c #181818", -"_ c #1D1D1D", -": c #252526", -"< c #343435", -"[ c #C3C3C2", -"} c #D5D5D5", -"| c #B0B1B3", -"1 c #B8B9BC", -"2 c #707174", -"3 c #707276", -"4 c #414245", -"5 c #454647", -"6 c #444444", -"7 c #383838", -"8 c #2F2F2F", -"9 c #262626", -"0 c #202020", -"a c #1B1C1D", -"b c #19191B", -"c c #161618", -"d c #5D5D5D", -"e c #C8C8C8", -"f c #C5C7C9", -"g c #B0B6B9", -"h c #A1A4A8", -"i c #8F8F95", -"j c #48484D", -"k c #505153", -"l c #515152", -"m c #2B2B2B", -"n c #404040", -"o c #222222", -"p c #212223", -"q c #18191A", -"r c #232326", -"s c #131315", -"t c #727274", -"u c #BFBFBE", -"v c #D0D4D7", -"w c #BEC3C6", -"x c #B6BABD", -"y c #B2B6BC", -"z c #AEB4B9", -"A c #909499", -"B c #86888D", -"C c #57585C", -"D c #3F3F3F", -"E c #4B4B4A", -"F c #343434", -"G c #414141", -"H c #28292B", -"I c #2F3032", -"J c #232226", -"K c #343438", -"L c #201F22", -"M c #3D3D3F", -"N c #8F8F90", -"O c #B9B8BA", -"P c #C9C9CB", -"Q c #B8B8B8", -"R c #8B8D8E", -"S c #D3D7DA", -"T c #B9BDC1", -"U c #B3B7BB", -"V c #AAAEB2", -"W c #A3A9AD", -"X c #9FA3A8", -"Y c #83878B", -"Z c #747578", -"` c #5B5B5D", -" . c #434343", -".. c #505050", -"+. c #424242", -"@. c #373737", -"#. c #353637", -"$. c #323335", -"%. c #2F3133", -"&. c #323235", -"*. c #353537", -"=. c #3C3C3E", -"-. c #39393A", -";. c #302F30", -">. c #464546", -",. c #4A4A4C", -"'. c #F3F3F4", -"). c #D3D4D6", -"!. c #D0D2D5", -"~. c #E2E5E7", -"{. c #E2E4E6", -"]. c #4B4A4B", -"^. c #4F4F4F", -"/. c #BCBFBF", -"(. c #B6BABF", -"_. c #A7ACB0", -":. c #9FA4A8", -"<. c #9CA0A4", -"[. c #8B8F92", -"}. c #797A7D", -"|. c #6A6A6A", -"1. c #595958", -"2. c #454545", -"3. c #3D3D3D", -"4. c #363636", -"5. c #323334", -"6. c #303133", -"7. c #333436", -"8. c #3D3E40", -"9. c #333335", -"0. c #2C2C2D", -"a. c #3A3A3A", -"b. c #373738", -"c. c #3B3B3E", -"d. c #3B3B3F", -"e. c #A0A0A1", -"f. c #E5E7E8", -"g. c #D0D2D3", -"h. c #CED0D2", -"i. c #D7DADC", -"j. c #E3E6E8", -"k. c #E0E4E7", -"l. c #DADEE1", -"m. c #D7DCE0", -"n. c #D4DADC", -"o. c #DCE0E1", -"p. c #999898", -"q. c #2A2A2B", -"r. c #656668", -"s. c #B0B6BA", -"t. c #A1A5AA", -"u. c #999BA0", -"v. c #919297", -"w. c #757679", -"x. c #666667", -"y. c #5A5A5B", -"z. c #505051", -"A. c #3B3B3C", -"B. c #3C3C3C", -"C. c #393939", -"D. c #2F3031", -"E. c #363739", -"F. c #363638", -"G. c #313136", -"H. c #35353B", -"I. c #38383E", -"J. c #38373C", -"K. c #3D3C41", -"L. c #2F2F31", -"M. c #636363", -"N. c #BDBFBF", -"O. c #AEB2B4", -"P. c #B3B7BA", -"Q. c #CACED1", -"R. c #D1D6D9", -"S. c #D9DDE0", -"T. c #DBDFE2", -"U. c #D6DADD", -"V. c #D1D5D8", -"W. c #CED2D5", -"X. c #D3D6D8", -"Y. c #DADDDE", -"Z. c #616364", -"`. c #2C2D2D", -" + c #8D8F94", -".+ c #9B9EA4", -"++ c #919296", -"@+ c #7A7B7F", -"#+ c #6B6C6D", -"$+ c #4E4E51", -"%+ c #474748", -"&+ c #333333", -"*+ c #424241", -"=+ c #40403F", -"-+ c #222223", -";+ c #2D2E30", -">+ c #333438", -",+ c #37383C", -"'+ c #39383C", -")+ c #434247", -"!+ c #575659", -"~+ c #4D4D4D", -"{+ c #B1B1B1", -"]+ c #C6C6C7", -"^+ c #BBBDC0", -"/+ c #A4A6A9", -"(+ c #A6A9AC", -"_+ c #BDC1C4", -":+ c #BBBFC2", -"<+ c #BABEC1", -"[+ c #C8CCCF", -"}+ c #D2D6D9", -"|+ c #D2D7DA", -"1+ c #D5D9DC", -"2+ c #D4D7DA", -"3+ c #D2D5D7", -"4+ c #D7D9DB", -"5+ c #D4D6D9", -"6+ c #C9CED0", -"7+ c #BFC4C7", -"8+ c #939395", -"9+ c #3F3F41", -"0+ c #5B5D5F", -"a+ c #7D7E82", -"b+ c #8B8C8F", -"c+ c #6D6E70", -"d+ c #5A5A5C", -"e+ c #333332", -"f+ c #3E3E3D", -"g+ c #313133", -"h+ c #2E2E30", -"i+ c #2D2E31", -"j+ c #2E2F31", -"k+ c #343538", -"l+ c #3D3D40", -"m+ c #454648", -"n+ c #535353", -"o+ c #515151", -"p+ c #353534", -"q+ c #AAAAA9", -"r+ c #CCCDCC", -"s+ c #7F8184", -"t+ c #999B9E", -"u+ c #9A9C9F", -"v+ c #96989D", -"w+ c #9CA0A3", -"x+ c #A6ABAE", -"y+ c #AEB2B5", -"z+ c #ADB0B2", -"A+ c #B5B6B9", -"B+ c #B6B8BC", -"C+ c #C3C5C9", -"D+ c #CDCFD4", -"E+ c #CED1D4", -"F+ c #D7D8DB", -"G+ c #D1D3D5", -"H+ c #CFD1D3", -"I+ c #E0E0E2", -"J+ c #E4E5E7", -"K+ c #D4D6D8", -"L+ c #BCBFC3", -"M+ c #B5B8BC", -"N+ c #BBBEC2", -"O+ c #A8AAAC", -"P+ c #77787A", -"Q+ c #3B3C3F", -"R+ c #3A3B3C", -"S+ c #666768", -"T+ c #606062", -"U+ c #515153", -"V+ c #4A4A4B", -"W+ c #353536", -"X+ c #2E2E2F", -"Y+ c #2D2D2F", -"Z+ c #2A2B2B", -"`+ c #2F2F30", -" @ c #424243", -".@ c #514F4E", -"+@ c #535252", -"@@ c #48494A", -"#@ c #403F3D", -"$@ c #51514C", -"%@ c #434244", -"&@ c #424143", -"*@ c #E9E8EA", -"=@ c #646567", -"-@ c #565757", -";@ c #2C2D2F", -">@ c #7A7E7E", -",@ c #979B9F", -"'@ c #95989E", -")@ c #9DA1A5", -"!@ c #9FA3A6", -"~@ c #A0A4A7", -"{@ c #A9ABAD", -"]@ c #AEAFB1", -"^@ c #B2B3B5", -"/@ c #C0C1C5", -"(@ c #CECFD4", -"_@ c #D3D4D7", -":@ c #D2D3D5", -"<@ c #C9CACC", -"[@ c #CCCDCF", -"}@ c #D6D7D9", -"|@ c #DEDFE0", -"1@ c #D9DADC", -"2@ c #AEAFB4", -"3@ c #A7AAAE", -"4@ c #A3A7AA", -"5@ c #9EA2A5", -"6@ c #9D9FA2", -"7@ c #4D4E4E", -"8@ c #181918", -"9@ c #404041", -"0@ c #58585B", -"a@ c #323234", -"b@ c #444242", -"c@ c #43423F", -"d@ c #4B4B47", -"e@ c #504E50", -"f@ c #302C39", -"g@ c #100A20", -"h@ c #6A6871", -"i@ c #C0C0C0", -"j@ c #727475", -"k@ c #4D5153", -"l@ c #414547", -"m@ c #55585A", -"n@ c #606163", -"o@ c #424342", -"p@ c #191A19", -"q@ c #6B6F6F", -"r@ c #A2A7A9", -"s@ c #93989B", -"t@ c #9FA2A5", -"u@ c #A2A4A6", -"v@ c #ADAEB0", -"w@ c #BDBEC0", -"x@ c #C0C1C4", -"y@ c #CDCED0", -"z@ c #CECFD1", -"A@ c #D5D6D8", -"B@ c #D4D5D7", -"C@ c #CECFD0", -"D@ c #BFC0C2", -"E@ c #B5B6B7", -"F@ c #B1B2B5", -"G@ c #A8A9AD", -"H@ c #A0A3A7", -"I@ c #9A9EA1", -"J@ c #979A9D", -"K@ c #939498", -"L@ c #8A8B8B", -"M@ c #666767", -"N@ c #282828", -"O@ c #1C1C1E", -"P@ c #49494A", -"Q@ c #494949", -"R@ c #454548", -"S@ c #4F4F51", -"T@ c #4B4B4B", -"U@ c #3D3E3D", -"V@ c #555555", -"W@ c #434148", -"X@ c #2D2633", -"Y@ c #1B1327", -"Z@ c #201732", -"`@ c #120C1F", -" # c #969599", -".# c #C2C2C2", -"+# c #9A999D", -"@# c #393A3D", -"## c #303134", -"$# c #424346", -"%# c #45464A", -"&# c #4C4E52", -"*# c #505257", -"=# c #5B5E63", -"-# c #686C70", -";# c #636368", -"># c #0B0B0C", -",# c #484848", -"'# c #A1A2A3", -")# c #969799", -"!# c #9E9EA0", -"~# c #A1A2A4", -"{# c #A3A4A6", -"]# c #CBCCCE", -"^# c #CED0CF", -"/# c #D0D1D1", -"(# c #C3C4C4", -"_# c #C7C9CB", -":# c #BABBBF", -"<# c #B7B8BD", -"[# c #B0B1B4", -"}# c #ABACB0", -"|# c #A5A6AA", -"1# c #9C9FA4", -"2# c #93969B", -"3# c #898A8E", -"4# c #78797A", -"5# c #636365", -"6# c #070708", -"7# c #303030", -"8# c #4C4C4C", -"9# c #444445", -"0# c #3A3A3C", -"a# c #343331", -"b# c #5D5A59", -"c# c #585256", -"d# c #21162B", -"e# c #22162D", -"f# c #24182F", -"g# c #261A32", -"h# c #25192F", -"i# c #2D2531", -"j# c #AEAEAE", -"k# c #737373", -"l# c #252525", -"m# c #151515", -"n# c #585859", -"o# c #2C2C2E", -"p# c #16151A", -"q# c #424347", -"r# c #404146", -"s# c #46474B", -"t# c #494A4E", -"u# c #4F5155", -"v# c #55595E", -"w# c #717379", -"x# c #797B7E", -"y# c #2C2D2E", -"z# c #79797B", -"A# c #9B9B9D", -"B# c #9F9FA2", -"C# c #A0A1A3", -"D# c #BABBBD", -"E# c #C3C4C6", -"F# c #C9CBCC", -"G# c #C3C4C5", -"H# c #BABBBE", -"I# c #B9BABF", -"J# c #B3B4B8", -"K# c #AFB0B4", -"L# c #A7A8AC", -"M# c #9FA0A4", -"N# c #979A9F", -"O# c #919398", -"P# c #8E8F94", -"Q# c #636467", -"R# c #606162", -"S# c #5D5D5E", -"T# c #4C4C4B", -"U# c #0C0C0C", -"V# c #3D3D3C", -"W# c #333437", -"X# c #444248", -"Y# c #54505B", -"Z# c #2A2336", -"`# c #170B27", -" $ c #291C37", -".$ c #2A1D34", -"+$ c #281D32", -"@$ c #2A1E33", -"#$ c #251B2D", -"$$ c #4B454F", -"%$ c #5B5C5C", -"&$ c #696969", -"*$ c #000000", -"=$ c #0C0C0D", -"-$ c #161717", -";$ c #121214", -">$ c #38373B", -",$ c #35363A", -"'$ c #37383B", -")$ c #414247", -"!$ c #434448", -"~$ c #47484B", -"{$ c #4B4D52", -"]$ c #525559", -"^$ c #595C60", -"/$ c #62656B", -"($ c #6D6F74", -"_$ c #7A7E81", -":$ c #29282A", -"<$ c #48484A", -"[$ c #949597", -"}$ c #9FA0A2", -"|$ c #AAABAD", -"1$ c #BBBCBE", -"2$ c #BCBDC0", -"3$ c #B9B9BD", -"4$ c #B4B5B9", -"5$ c #B0B1B5", -"6$ c #ACADB1", -"7$ c #A6A7AB", -"8$ c #909297", -"9$ c #8D9196", -"0$ c #8F9296", -"a$ c #9D9EA2", -"b$ c #595A5C", -"c$ c #565655", -"d$ c #484846", -"e$ c #50504F", -"f$ c #161616", -"g$ c #353535", -"h$ c #3A3C3D", -"i$ c #2E263B", -"j$ c #221734", -"k$ c #2A1E38", -"l$ c #2E213A", -"m$ c #2E203A", -"n$ c #2E2139", -"o$ c #2D2136", -"p$ c #2E2236", -"q$ c #291F30", -"r$ c #69656D", -"s$ c #CDCDCE", -"t$ c #727374", -"u$ c #59595A", -"v$ c #191C1D", -"w$ c #131518", -"x$ c #131417", -"y$ c #151619", -"z$ c #121314", -"A$ c #0D0E10", -"B$ c #0B0C0E", -"C$ c #252629", -"D$ c #1F2021", -"E$ c #0F1010", -"F$ c #323337", -"G$ c #3A3B3F", -"H$ c #343539", -"I$ c #444547", -"J$ c #464749", -"K$ c #4B4C50", -"L$ c #505155", -"M$ c #58595D", -"N$ c #5D6064", -"O$ c #65696C", -"P$ c #696D71", -"Q$ c #757779", -"R$ c #6F6F71", -"S$ c #343537", -"T$ c #232324", -"U$ c #767675", -"V$ c #A7A8A9", -"W$ c #B2B3B7", -"X$ c #B9BABC", -"Y$ c #B8B9BB", -"Z$ c #B1B2B4", -"`$ c #A7A8AB", -" % c #A1A2A6", -".% c #97989C", -"+% c #8E8F93", -"@% c #9DA1A4", -"#% c #919496", -"$% c #6E6E6F", -"%% c #575757", -"&% c #373636", -"*% c #3A3B3B", -"=% c #111214", -"-% c #3B3C3E", -";% c #3A3C3E", -">% c #323036", -",% c #30223D", -"'% c #30233C", -")% c #31243E", -"!% c #32243E", -"~% c #31253D", -"{% c #31253B", -"]% c #32263A", -"^% c #34273C", -"/% c #33283A", -"(% c #747479", -"_% c #8B8A8C", -":% c #504F52", -"<% c #232427", -"[% c #1B1B1E", -"}% c #242528", -"|% c #1B1E21", -"1% c #17191D", -"2% c #16171C", -"3% c #0A0B0D", -"4% c #161719", -"5% c #1B1D1E", -"6% c #070908", -"7% c #252627", -"8% c #393A3F", -"9% c #16161A", -"0% c #444548", -"a% c #4A4B4D", -"b% c #4D4E52", -"c% c #525357", -"d% c #5B5D60", -"e% c #5F6366", -"f% c #64686A", -"g% c #68696B", -"h% c #717274", -"i% c #727375", -"j% c #585858", -"k% c #1A1A1A", -"l% c #A7A7AB", -"m% c #B3B4B7", -"n% c #B6B7B9", -"o% c #B3B4B6", -"p% c #B4B5B7", -"q% c #A9AAAC", -"r% c #AFB0B2", -"s% c #A4A5A8", -"t% c #B6B7BA", -"u% c #8A8B8D", -"v% c #5E5F61", -"w% c #494A4B", -"x% c #3D3E41", -"y% c #3B3D3F", -"z% c #34303B", -"A% c #362843", -"B% c #352841", -"C% c #362942", -"D% c #372A42", -"E% c #372B40", -"F% c #372C40", -"G% c #362A40", -"H% c #423847", -"I% c #6E6E71", -"J% c #464645", -"K% c #353539", -"L% c #343338", -"M% c #2B2B30", -"N% c #2D2E33", -"O% c #2C2D32", -"P% c #16161B", -"Q% c #1B1A20", -"R% c #1A1C21", -"S% c #15171D", -"T% c #0F1014", -"U% c #060709", -"V% c #191A1D", -"W% c #2C2D30", -"X% c #080808", -"Y% c #1A1B1A", -"Z% c #444549", -"`% c #414246", -" & c #515254", -".& c #494A4C", -"+& c #4E4F53", -"@& c #575B5E", -"#& c #5E6265", -"$& c #5E6062", -"%& c #808083", -"&& c #99999A", -"*& c #9F9F9F", -"=& c #202225", -"-& c #949598", -";& c #AAABAF", -">& c #949698", -",& c #202021", -"'& c #A3A4A7", -")& c #96979B", -"!& c #3E3E3E", -"~& c #2B2B2D", -"{& c #353142", -"]& c #392B47", -"^& c #3A2C46", -"/& c #3A2D47", -"(& c #3A2E47", -"_& c #3B2F47", -":& c #3C3045", -"<& c #3C3145", -"[& c #3A2D44", -"}& c #4A414D", -"|& c #A3A5A3", -"1& c #939298", -"2& c #212526", -"3& c #2B2C2B", -"4& c #3C3A3C", -"5& c #787982", -"6& c #30343B", -"7& c #22262D", -"8& c #24282E", -"9& c #1D2125", -"0& c #171A1F", -"a& c #191A1E", -"b& c #15161A", -"c& c #0D0D11", -"d& c #131517", -"e& c #1E1F21", -"f& c #313236", -"g& c #2A2A2F", -"h& c #3F4044", -"i& c #58595E", -"j& c #404145", -"k& c #505152", -"l& c #535357", -"m& c #5A5B5F", -"n& c #646368", -"o& c #767779", -"p& c #8F9092", -"q& c #919294", -"r& c #87888A", -"s& c #868789", -"t& c #A5A6A8", -"u& c #4C4D50", -"v& c #1A1C1C", -"w& c #6E7070", -"x& c #949496", -"y& c #A7A7A9", -"z& c #B9BCBF", -"A& c #A6AAAD", -"B& c #7A7D81", -"C& c #1E1E1E", -"D& c #2A2929", -"E& c #262726", -"F& c #2A2C2B", -"G& c #353635", -"H& c #3B3B3A", -"I& c #393A3A", -"J& c #3A3043", -"K& c #3D314B", -"L& c #3F324D", -"M& c #40334C", -"N& c #41344A", -"O& c #403349", -"P& c #423648", -"Q& c #413546", -"R& c #45394D", -"S& c #433F4B", -"T& c #E0DFE1", -"U& c #131413", -"V& c #484947", -"W& c #222326", -"X& c #252426", -"Y& c #5F6267", -"Z& c #6F7278", -"`& c #373A41", -" * c #181B20", -".* c #1E2126", -"+* c #191B20", -"@* c #14151A", -"#* c #141519", -"$* c #18191D", -"%* c #0E0F0F", -"&* c #111212", -"** c #1B1C1F", -"=* c #2D2F32", -"-* c #36373B", -";* c #3C3D41", -">* c #47484C", -",* c #4B4D4E", -"'* c #46484A", -")* c #65666A", -"!* c #717276", -"~* c #808185", -"{* c #818284", -"]* c #7E7F81", -"^* c #7A7B7D", -"/* c #808183", -"(* c #86888A", -"_* c #303131", -":* c #616365", -"<* c #141414", -"[* c #131313", -"}* c #101010", -"|* c #171817", -"1* c #252826", -"2* c #373938", -"3* c #434444", -"4* c #3C3A3F", -"5* c #3E3249", -"6* c #463751", -"7* c #453652", -"8* c #463852", -"9* c #473850", -"0* c #48394F", -"a* c #483A4E", -"b* c #47394B", -"c* c #493D4E", -"d* c #39343E", -"e* c #3A393D", -"f* c #212325", -"g* c #201F21", -"h* c #323438", -"i* c #4A4D53", -"j* c #6B6D74", -"k* c #424449", -"l* c #14171C", -"m* c #171A1E", -"n* c #1B1D21", -"o* c #111416", -"p* c #0F0F14", -"q* c #1C1D21", -"r* c #191A1C", -"s* c #303135", -"t* c #434447", -"u* c #48494B", -"v* c #48494C", -"w* c #7F8081", -"x* c #6F7074", -"y* c #76777B", -"z* c #727377", -"A* c #737476", -"B* c #747577", -"C* c #78797B", -"D* c #7A7A7C", -"E* c #868788", -"F* c #9C9D9F", -"G* c #7E8081", -"H* c #161617", -"I* c #0A0B0B", -"J* c #131314", -"K* c #121211", -"L* c #0B0B0B", -"M* c #0D0D0D", -"N* c #0F0F0F", -"O* c #111111", -"P* c #131412", -"Q* c #1B1C1B", -"R* c #262727", -"S* c #2D2D2D", -"T* c #1D1E1E", -"U* c #434443", -"V* c #3E3F3F", -"W* c #3B393F", -"X* c #433551", -"Y* c #4F3D59", -"Z* c #4E3D59", -"`* c #4D3D58", -" = c #4D3D55", -".= c #4E3E55", -"+= c #4D3F54", -"@= c #4C3F52", -"#= c #3F3443", -"$= c #868188", -"%= c #4E4D4F", -"&= c #212125", -"*= c #212222", -"== c #101012", -"-= c #616368", -";= c #484B50", -">= c #191B1F", -",= c #121217", -"'= c #1B1E22", -")= c #15151A", -"!= c #0A0B0F", -"~= c #1E1F23", -"{= c #292A2E", -"]= c #2D2E32", -"^= c #38393A", -"/= c #39393B", -"(= c #3F4042", -"_= c #4F5051", -":= c #6C6C6D", -"<= c #9C9C9D", -"[= c #696B6D", -"}= c #6B6C70", -"|= c #6B6C6F", -"1= c #696A6C", -"2= c #747473", -"3= c #898989", -"4= c #858586", -"5= c #565858", -"6= c #343636", -"7= c #131312", -"8= c #040404", -"9= c #121312", -"0= c #111110", -"a= c #5E5E5D", -"b= c #444545", -"c= c #1C1C1C", -"d= c #323433", -"e= c #3E403F", -"f= c #3E403E", -"g= c #403B43", -"h= c #4F3F5E", -"i= c #574463", -"j= c #554560", -"k= c #55445E", -"l= c #55445D", -"m= c #53455B", -"n= c #524458", -"o= c #544557", -"p= c #322A37", -"q= c #DDDBDD", -"r= c #201F24", -"s= c #1D1F1E", -"t= c #292A2D", -"u= c #292A2F", -"v= c #2D2D32", -"w= c #26282D", -"x= c #595C61", -"y= c #474A4F", -"z= c #181A1F", -"A= c #0B0B10", -"B= c #212327", -"C= c #25262A", -"D= c #28292D", -"E= c #2E2F33", -"F= c #2A2C2C", -"G= c #6B6C6B", -"H= c #525255", -"I= c #4F5153", -"J= c #55585B", -"K= c #56595C", -"L= c #5C5D61", -"M= c #616266", -"N= c #5D5E62", -"O= c #646568", -"P= c #6E6F70", -"Q= c #797A7A", -"R= c #171917", -"S= c #131512", -"T= c #0C0D0B", -"U= c #030303", -"V= c #11110F", -"W= c #0F0F10", -"X= c #1B1B1B", -"Y= c #3D3F3E", -"Z= c #413949", -"`= c #624E74", -" - c #5F4D6B", -".- c #604D69", -"+- c #614E69", -"@- c #604D68", -"#- c #5D4B64", -"$- c #594A5A", -"%- c #625661", -"&- c #4A494C", -"*- c #2B2A2C", -"=- c #1A1A1D", -"-- c #1F2121", -";- c #1A1B1D", -">- c #38393C", -",- c #26272B", -"'- c #1E2025", -")- c #46484D", -"!- c #46494D", -"~- c #1C1C21", -"{- c #171B1F", -"]- c #1D1F23", -"^- c #202124", -"/- c #232429", -"(- c #232428", -"_- c #28282D", -":- c #444645", -"<- c #595A59", -"[- c #37383A", -"}- c #4B4D4F", -"|- c #4F5255", -"1- c #525558", -"2- c #57595D", -"3- c #5B5C60", -"4- c #5B5C5D", -"5- c #555557", -"6- c #363738", -"7- c #090909", -"8- c #0E0E0E", -"9- c #161815", -"0- c #121412", -"a- c #131414", -"b- c #090809", -"c- c #0A0A0A", -"d- c #010101", -"e- c #020202", -"f- c #060606", -"g- c #292A2A", -"h- c #1F1F20", -"i- c #3C3D3D", -"j- c #3F413F", -"k- c #4F425C", -"l- c #725C83", -"m- c #6C5878", -"n- c #6D5875", -"o- c #6D5873", -"p- c #6A5870", -"q- c #67596B", -"r- c #5A555E", -"s- c #414346", -"t- c #999A9C", -"u- c #191919", -"v- c #1E2020", -"w- c #151719", -"x- c #282A2E", -"y- c #242428", -"z- c #2C2D31", -"A- c #38383D", -"B- c #24292D", -"C- c #1D1F22", -"D- c #1D1E22", -"E- c #27282B", -"F- c #2B2B2E", -"G- c #333435", -"H- c #353638", -"I- c #404143", -"J- c #434446", -"K- c #47484A", -"L- c #4C4D4F", -"M- c #565759", -"N- c #545558", -"O- c #404043", -"P- c #1F1F1F", -"Q- c #0F100F", -"R- c #1F2122", -"S- c #222421", -"T- c #1F2226", -"U- c #191A1A", -"V- c #161818", -"W- c #070807", -"X- c #171718", -"Y- c #16171A", -"Z- c #18181B", -"`- c #373736", -" ; c #3B3B3D", -".; c #69577A", -"+; c #80668C", -"@; c #806489", -"#; c #7E6883", -"$; c #736576", -"%; c #5E575F", -"&; c #4D4C4C", -"*; c #5B5B59", -"=; c #292929", -"-; c #1A1919", -";; c #1C1E1D", -">; c #151518", -",; c #27282C", -"'; c #2A2B2F", -"); c #28292C", -"!; c #202023", -"~; c #2C2C2F", -"{; c #303234", -"]; c #1F2024", -"^; c #373939", -"/; c #424444", -"(; c #212323", -"_; c #313234", -":; c #38393B", -"<; c #424345", -"[; c #2F3131", -"}; c #0B0C0C", -"|; c #252425", -"1; c #2E302C", -"2; c #333431", -"3; c #3D363E", -"4; c #232726", -"5; c #242625", -"6; c #202321", -"7; c #1E1F1F", -"8; c #070707", -"9; c #050505", -"0; c #211F22", -"a; c #282A2D", -"b; c #37353C", -"c; c #8E779D", -"d; c #8D7496", -"e; c #806F82", -"f; c #645E66", -"g; c #514C4D", -"h; c #5F5C58", -"i; c #7F7F78", -"j; c #191B1A", -"k; c #191C1B", -"l; c #18191B", -"m; c #323338", -"n; c #222328", -"o; c #2B2C2E", -"p; c #363639", -"q; c #2D3134", -"r; c #212528", -"s; c #222225", -"t; c #1A1C1A", -"u; c #282A2C", -"v; c #1C1C1F", -"w; c #202223", -"x; c #282A2B", -"y; c #272829", -"z; c #393A3C", -"A; c #46474A", -"B; c #343536", -"C; c #1B1B1C", -"D; c #111312", -"E; c #202024", -"F; c #222321", -"G; c #2B2D28", -"H; c #44433D", -"I; c #37313F", -"J; c #3A2E50", -"K; c #37294B", -"L; c #2C2E2C", -"M; c #282A29", -"N; c #262827", -"O; c #232425", -"P; c #4C4D4D", -"Q; c #383938", -"R; c #5B595D", -"S; c #908198", -"T; c #6A606D", -"U; c #504E4F", -"V; c #5B5B58", -"W; c #807F7B", -"X; c #161817", -"Y; c #151618", -"Z; c #2B2C2F", -"`; c #36383C", -" > c #1D1E21", -".> c #2A2C2F", -"+> c #2B2F33", -"@> c #2C2F32", -"#> c #1B1C20", -"$> c #17181A", -"%> c #1B1C1E", -"&> c #222325", -"*> c #2A2A2C", -"=> c #0B0C0D", -"-> c #030404", -";> c #2B2C2C", -">> c #1F1F17", -",> c #413F3F", -"'> c #423E4A", -")> c #261C3E", -"!> c #322450", -"~> c #362554", -"{> c #2F2345", -"]> c #323430", -"^> c #313232", -"/> c #303231", -"(> c #2B2D2C", -"_> c #606061", -":> c #787878", -"<> c #323231", -"[> c #1B1A1A", -"}> c #3F403C", -"|> c #817A7A", -"1> c #ABA9A8", -"2> c #B9B9B8", -"3> c #1F1D1B", -"4> c #171719", -"5> c #1A1C1B", -"6> c #0E0E10", -"7> c #212225", -"8> c #252628", -"9> c #3A3B40", -"0> c #23262A", -"a> c #313237", -"b> c #303237", -"c> c #202025", -"d> c #1D1E20", -"e> c #121315", -"f> c #2A2B2D", -"g> c #232325", -"h> c #09090C", -"i> c #030406", -"j> c #141518", -"k> c #2B2C2D", -"l> c #302E32", -"m> c #423C4E", -"n> c #281D42", -"o> c #281947", -"p> c #33264D", -"q> c #3B2B55", -"r> c #372A4A", -"s> c #32342E", -"t> c #333533", -"u> c #363837", -"v> c #2F302F", -"w> c #2A2A2A", -"x> c #313131", -"y> c #282825", -"z> c #3F3F40", -"A> c #30322D", -"B> c #52514F", -"C> c #444446", -"D> c #9B9C9E", -"E> c #838486", -"F> c #161615", -"G> c #1A1A19", -"H> c #171717", -"I> c #34373A", -"J> c #2D3033", -"K> c #2D2F33", -"L> c #1E201E", -"M> c #191B1B", -"N> c #181919", -"O> c #151514", -"P> c #1B1B1A", -"Q> c #29292B", -"R> c #292727", -"S> c #353237", -"T> c #2E2B40", -"U> c #271D42", -"V> c #251742", -"W> c #2F1F4A", -"X> c #312249", -"Y> c #35264F", -"Z> c #3A2B52", -"`> c #3E2C5C", -" , c #362D47", -"., c #353431", -"+, c #2E2E2E", -"@, c #232323", -"#, c #050504", -"$, c #060605", -"%, c #212121", -"&, c #232224", -"*, c #141514", -"=, c #353435", -"-, c #2F2F2D", -";, c #585A5F", -">, c #858789", -",, c #A4A5A5", -"', c #616161", -"), c #242424", -"!, c #141314", -"~, c #0F0E11", -"{, c #25252A", -"], c #26272C", -"^, c #303537", -"/, c #313538", -"(, c #26292C", -"_, c #212124", -":, c #212322", -"<, c #1B1D1C", -"[, c #191B19", -"}, c #181816", -"|, c #121210", -"1, c #010100", -"2, c #1A191C", -"3, c #2B2B29", -"4, c #2E2F2B", -"5, c #272431", -"6, c #2E2A40", -"7, c #1D1639", -"8, c #1F173F", -"9, c #281C45", -"0, c #2C1F47", -"a, c #30224A", -"b, c #32254D", -"c, c #382A52", -"d, c #3C2E57", -"e, c #433161", -"f, c #372F43", -"g, c #353533", -"h, c #020203", -"i, c #010102", -"j, c #1B1A1B", -"k, c #2C2C29", -"l, c #545651", -"m, c #5A5A58", -"n, c #484745", -"o, c #A7A7A7", -"p, c #1D1D1C", -"q, c #0D0C0F", -"r, c #1E1D22", -"s, c #191D20", -"t, c #303336", -"u, c #35373A", -"v, c #2F3033", -"w, c #242527", -"x, c #232524", -"y, c #1E1F20", -"z, c #1A1D1C", -"A, c #191A18", -"B, c #060604", -"C, c #29282F", -"D, c #201D2F", -"E, c #221D37", -"F, c #211B3C", -"G, c #1A163A", -"H, c #211A42", -"I, c #261C44", -"J, c #2C1E46", -"K, c #30214A", -"L, c #34254E", -"M, c #362851", -"N, c #3D2D57", -"O, c #42325C", -"P, c #493768", -"Q, c #37323F", -"R, c #363635", -"S, c #2B2B2C", -"T, c #323333", -"U, c #282929", -"V, c #1D1E1D", -"W, c #0C0A0A", -"X, c #1F1C1E", -"Y, c #3B3C3A", -"Z, c #383C43", -"`, c #2F2C2B", -" ' c #141615", -".' c #0A0A0B", -"+' c #28282B", -"@' c #242529", -"#' c #2A2C30", -"$' c #373B3E", -"%' c #191C1E", -"&' c #2E3034", -"*' c #232426", -"=' c #262729", -"-' c #202123", -";' c #1C1E1F", -">' c #131212", -",' c #080807", -"'' c #1E1E25", -")' c #1A1936", -"!' c #1C1736", -"~' c #1C1639", -"{' c #1E183D", -"]' c #1F1A3F", -"^' c #231C43", -"/' c #291B45", -"(' c #2E1F48", -"_' c #32234C", -":' c #372851", -"<' c #3C2C57", -"[' c #42305D", -"}' c #49345E", -"|' c #4F3A71", -"1' c #333039", -"2' c #383738", -"3' c #212422", -"4' c #272928", -"5' c #222423", -"6' c #2F3230", -"7' c #222221", -"8' c #020302", -"9' c #343947", -"0' c #1B2639", -"a' c #030000", -"b' c #353739", -"c' c #4F4F4D", -"d' c #050506", -"e' c #1C1B1F", -"f' c #1E1E22", -"g' c #292E2F", -"h' c #35393D", -"i' c #24252A", -"j' c #1F1F22", -"k' c #1D1D2E", -"l' c #181836", -"m' c #1B1733", -"n' c #1D1738", -"o' c #1F193E", -"p' c #21193F", -"q' c #271C44", -"r' c #2B1E47", -"s' c #31224B", -"t' c #392A53", -"u' c #3E2F5A", -"v' c #453360", -"w' c #4B3662", -"x' c #523F74", -"y' c #323034", -"z' c #0C0C0B", -"A' c #0D0C0C", -"B' c #35302F", -"C' c #31353E", -"D' c #3C7390", -"E' c #1B2126", -"F' c #070C07", -"G' c #303031", -"H' c #08090A", -"I' c #17181C", -"J' c #1F2222", -"K' c #1F2322", -"L' c #303338", -"M' c #24272B", -"N' c #27282A", -"O' c #212224", -"P' c #1E1E1F", -"Q' c #111011", -"R' c #1D1C2C", -"S' c #181739", -"T' c #1C1637", -"U' c #1D1739", -"V' c #1E193D", -"W' c #231841", -"X' c #2B1C45", -"Y' c #2E2048", -"Z' c #32244D", -"`' c #3C2C56", -" ) c #41325D", -".) c #483663", -"+) c #4F3A67", -"@) c #523F71", -"#) c #323230", -"$) c #0B0C0B", -"%) c #292B2A", -"&) c #2B2E2D", -"*) c #282B2A", -"=) c #2D2E2D", -"-) c #5B5B5B", -";) c #050606", -">) c #090807", -",) c #0B0A0C", -"') c #181514", -")) c #31373E", -"!) c #3E7497", -"~) c #85989C", -"{) c #272727", -"]) c #EBEBEB", -"^) c #1C1D1E", -"/) c #1F1F23", -"() c #35383C", -"_) c #353A3D", -":) c #22272A", -"<) c #111013", -"[) c #0F0D10", -"}) c #1A1B2F", -"|) c #1B163A", -"1) c #1C1738", -"2) c #1E183A", -"3) c #20173D", -"4) c #271B43", -"5) c #2C1D46", -"6) c #352550", -"7) c #392A56", -"8) c #3E2E5A", -"9) c #45345E", -"0) c #4C3A64", -"a) c #533D6B", -"b) c #4E3E65", -"c) c #323431", -"d) c #343634", -"e) c #2D2F2E", -"f) c #282A28", -"g) c #292A28", -"h) c #262A2B", -"i) c #3E3E3B", -"j) c #020201", -"k) c #0D0E0A", -"l) c #0D0E0E", -"m) c #090908", -"n) c #090708", -"o) c #58829C", -"p) c #A6BAC0", -"q) c #2E2C2C", -"r) c #565656", -"s) c #30302F", -"t) c #1C1D1F", -"u) c #1E1E20", -"v) c #191B1E", -"w) c #1F2225", -"x) c #262B2D", -"y) c #212629", -"z) c #27272B", -"A) c #121013", -"B) c #181A2C", -"C) c #1B173B", -"D) c #20173A", -"E) c #23183D", -"F) c #2A1C45", -"G) c #30234B", -"H) c #362751", -"I) c #3B2D55", -"J) c #403159", -"K) c #46365F", -"L) c #4E3B66", -"M) c #57426D", -"N) c #49395D", -"O) c #333430", -"P) c #2F3130", -"Q) c #151717", -"R) c #2D2F2F", -"S) c #546594", -"T) c #586A98", -"U) c #212221", -"V) c #020101", -"W) c #0B0C0A", -"X) c #140F08", -"Y) c #091015", -"Z) c #221E1D", -"`) c #5A829D", -" ! c #96BDCE", -".! c #565658", -"+! c #292A2C", -"@! c #404141", -"#! c #595959", -"$! c #676767", -"%! c #6D6D6D", -"&! c #6C6C6C", -"*! c #626262", -"=! c #525252", -"-! c #1F1F1E", -";! c #1A1C1E", -">! c #2B2D31", -",! c #2E3033", -"'! c #25272B", -")! c #24272A", -"!! c #0E0E0F", -"~! c #1A182D", -"{! c #1E1739", -"]! c #1F1537", -"^! c #23163C", -"/! c #27183F", -"(! c #2C1C46", -"_! c #2F214A", -":! c #392A51", -"~ c #47375E", -",~ c #4B3B61", -"'~ c #533F65", -")~ c #635078", -"!~ c #342D3B", -"~~ c #2D312D", -"{~ c #242623", -"]~ c #2D2D2C", -"^~ c #536593", -"/~ c #526493", -"(~ c #7786AD", -"_~ c #7D8BB1", -":~ c #5A6A97", -"<~ c #5B6C99", -"[~ c #5C6D9A", -"}~ c #536492", -"|~ c #576895", -"1~ c #42537B", -"2~ c #838383", -"3~ c #31302D", -"4~ c #18112C", -"5~ c #201539", -"6~ c #24163A", -"7~ c #28193F", -"8~ c #2C1D44", -"9~ c #2E2045", -"0~ c #392B4E", -"a~ c #3E2E51", -"b~ c #423158", -"c~ c #49385F", -"d~ c #523E66", -"e~ c #574763", -"f~ c #5C5764", -"g~ c #32342F", -"h~ c #272923", -"i~ c #556794", -"j~ c #5A80B3", -"k~ c #8591B3", -"l~ c #5C6C97", -"m~ c #566795", -"n~ c #60719E", -"o~ c #5E6F9B", -"p~ c #596A98", -"q~ c #556693", -"r~ c #6A7AA3", -"s~ c #6878A2", -"t~ c #546493", -"u~ c #717171", -"v~ c #757575", -"w~ c #828282", -"x~ c #666666", -"y~ c #32302F", -"z~ c #202221", -"A~ c #27292A", -"B~ c #25262B", -"C~ c #2B2B2F", -"D~ c #0B0D0D", -"E~ c #1A1B18", -"F~ c #190F31", -"G~ c #22153A", -"H~ c #25173A", -"I~ c #2A1C40", -"J~ c #2C1E42", -"K~ c #312248", -"L~ c #332549", -"M~ c #382A4D", -"N~ c #3C2A53", -"O~ c #493B5D", -"P~ c #4A4259", -"Q~ c #404142", -"R~ c #2C2F26", -"S~ c #556694", -"T~ c #7C8AAE", -"U~ c #7583A8", -"V~ c #526392", -"W~ c #8391B5", -"X~ c #A0ACC8", -"Y~ c #5B6C98", -"Z~ c #586895", -"`~ c #707070", -" { c #808080", -".{ c #5A5A5A", -"+{ c #27282D", -"@{ c #090B0B", -"#{ c #1B1C1A", -"${ c #1A1131", -"%{ c #21163A", -"&{ c #26173B", -"*{ c #2D1F43", -"={ c #312348", -"-{ c #34254B", -";{ c #3A2C51", -">{ c #463D59", -",{ c #3D3A44", -"'{ c #363735", -"){ c #434340", -"!{ c #5B6B99", -"~{ c #7F8EB3", -"{{ c #6D7DA5", -"]{ c #95A0BF", -"^{ c #7D8BB0", -"/{ c #5A6A98", -"({ c #516291", -"_{ c #546592", -":{ c #60709B", -"<{ c #7B89AF", -"[{ c #B8C1D7", -"}{ c #9EA9C5", -"|{ c #7886AD", -"1{ c #6B7AA3", -"2{ c #8B97B8", -"3{ c #727FA7", -"4{ c #5C5C5C", -"5{ c #727272", -"6{ c #7F7F7F", -"7{ c #3A3534", -"8{ c #222021", -"9{ c #1C1E23", -"0{ c #26272A", -"a{ c #090A0A", -"b{ c #1E201F", -"c{ c #191234", -"d{ c #23173A", -"e{ c #26193C", -"f{ c #291D3D", -"g{ c #2C1C41", -"h{ c #33244C", -"i{ c #3A314B", -"j{ c #3A373F", -"k{ c #343533", -"l{ c #4B4A49", -"m{ c #7784AC", -"n{ c #A1ACC8", -"o{ c #CBD3E5", -"p{ c #C5CEE2", -"q{ c #BEC8DE", -"r{ c #9FACCB", -"s{ c #7B8AB0", -"t{ c #5C6C98", -"u{ c #4E5F8E", -"v{ c #6C7AA3", -"w{ c #A8B2CB", -"x{ c #A3AEC8", -"y{ c #A7B1CB", -"z{ c #CFD6E5", -"A{ c #C9D0E1", -"B{ c #BBC4D9", -"C{ c #B0B9D2", -"D{ c #7E8CB0", -"E{ c #596A96", -"F{ c #526391", -"G{ c #5A6B98", -"H{ c #6E6E6E", -"I{ c #747474", -"J{ c #7B7B7B", -"K{ c #818181", -"L{ c #7D7D7D", -"M{ c #403D3D", -"N{ c #1E1D1E", -"O{ c #080A0B", -"P{ c #1C1E1E", -"Q{ c #1C1337", -"R{ c #23193B", -"S{ c #271A3C", -"T{ c #271A43", -"U{ c #322A49", -"V{ c #35363D", -"W{ c #2D2F30", -"X{ c #413F3D", -"Y{ c #707FA8", -"Z{ c #93A0C0", -"`{ c #A7B1CC", -" ] c #C7CFE0", -".] c #D4DBEA", -"+] c #D0D8E9", -"@] c #C9D1E5", -"#] c #BDC6DC", -"$] c #A6B1CB", -"%] c #8693B5", -"&] c #9EA8C5", -"*] c #BDC6DB", -"=] c #8794B6", -"-] c #8390B2", -";] c #B8C1D6", -">] c #C3CBDE", -",] c #B5BDD4", -"'] c #A0AAC6", -")] c #808FB4", -"!] c #7686AD", -"~] c #797979", -"{] c #423C39", -"]] c #1F1439", -"^] c #231839", -"/] c #261F3E", -"(] c #33323A", -"_] c #33342D", -":] c #31322F", -"<] c #54534B", -"[] c #6F7EA8", -"}] c #9BA8C6", -"|] c #AEB9D2", -"1] c #B1BBD5", -"2] c #D5DBEA", -"3] c #B2BCD2", -"4] c #9AA5C2", -"5] c #9EAAC5", -"6] c #95A1C0", -"7] c #CBD3E6", -"8] c #ADB8D4", -"9] c #6979A3", -"0] c #6978A2", -"a] c #7A88AE", -"b] c #8F9CBC", -"c] c #99A4C1", -"d] c #7080A7", -"e] c #8C99BA", -"f] c #8491B5", -"g] c #60719D", -"h] c #61729E", -"i] c #576794", -"j] c #4E5A7B", -"k] c #27292C", -"l] c #777777", -"m] c #1A1B1C", -"n] c #232328", -"o] c #19181F", -"p] c #241F40", -"q] c #292534", -"r] c #333331", -"s] c #2F322B", -"t] c #4E4A4B", -"u] c #7B8AB1", -"v] c #8B99BD", -"w] c #AFB9D2", -"x] c #B3BDD5", -"y] c #B8C2D9", -"z] c #AAB5D0", -"A] c #8B97B9", -"B] c #8D99BA", -"C] c #7785AB", -"D] c #BCC6DC", -"E] c #9AA6C9", -"F] c #A8B3D1", -"G] c #93A1C3", -"H] c #6E7DA7", -"I] c #8491B3", -"J] c #94A0BF", -"K] c #6777A1", -"L] c #586A97", -"M] c #5E6F9C", -"N] c #596894", -"O] c #546492", -"P] c #556591", -"Q] c #434B62", -"R] c #545454", -"S] c #101111", -"T] c #1F1E21", -"U] c #09090B", -"V] c #34332E", -"W] c #A3AECA", -"X] c #919EBF", -"Y] c #727FA8", -"Z] c #A2ADC7", -"`] c #B6BFD6", -" ^ c #C8CFE2", -".^ c #A8B3CE", -"+^ c #ACB5CE", -"@^ c #9BA7C5", -"#^ c #B9C2DA", -"$^ c #C2C9DE", -"%^ c #B0B9D4", -"&^ c #A9B2CE", -"*^ c #A9B4D0", -"=^ c #A9B5D2", -"-^ c #A4B1CD", -";^ c #A8B2CD", -">^ c #C9D0E3", -",^ c #95A2C3", -"'^ c #8390B5", -")^ c #6776A0", -"!^ c #7784AB", -"~^ c #8593B5", -"{^ c #5D6D9A", -"]^ c #5E6D99", -"^^ c #596896", -"/^ c #546187", -"(^ c #3F4557", -"_^ c #7A7A7A", -":^ c #5F5F5F", -"<^ c #444342", -"[^ c #464642", -"}^ c #8190B4", -"|^ c #97A3C3", -"1^ c #8A96BA", -"2^ c #7B88AD", -"3^ c #828FB3", -"4^ c #A5B0CA", -"5^ c #BDC5D9", -"6^ c #B1BBD4", -"7^ c #8F9CBB", -"8^ c #99A3C3", -"9^ c #D4DBEB", -"0^ c #DDE3EF", -"a^ c #D1D9E9", -"b^ c #D4DBEC", -"c^ c #C5CDE2", -"d^ c #ACB7D2", -"e^ c #A0ACCA", -"f^ c #BEC7DD", -"g^ c #C6CFE1", -"h^ c #B8C2DC", -"i^ c #B1BCD6", -"j^ c #919DBD", -"k^ c #6877A2", -"l^ c #566694", -"m^ c #5A6A96", -"n^ c #66749E", -"o^ c #566182", -"p^ c #3C4150", -"q^ c #464646", -"r^ c #62729F", -"s^ c #7686AE", -"t^ c #6B7AA5", -"u^ c #5F6F9A", -"v^ c #5D6D99", -"w^ c #8391B4", -"x^ c #97A4C3", -"y^ c #C8D0E1", -"z^ c #D3DAEB", -"A^ c #C2CBDD", -"B^ c #A8B2CC", -"C^ c #AAB5D1", -"D^ c #C0C9E0", -"E^ c #D6DDED", -"F^ c #BBC4DB", -"G^ c #B7C0D8", -"H^ c #C2CBE2", -"I^ c #C8D1E6", -"J^ c #97A3C2", -"K^ c #5C6D99", -"L^ c #5D6E9A", -"M^ c #576896", -"N^ c #586794", -"O^ c #828DAF", -"P^ c #68769C", -"Q^ c #464E63", -"R^ c #939FBF", -"S^ c #8D9BBE", -"T^ c #8693B6", -"U^ c #7785AA", -"V^ c #5E6E9A", -"W^ c #61709B", -"X^ c #7584AB", -"Y^ c #7281A8", -"Z^ c #BAC4DA", -"`^ c #C8D1E7", -" / c #ADB7D1", -"./ c #7B88AE", -"+/ c #6776A1", -"@/ c #7A88AF", -"#/ c #A2ADCB", -"$/ c #D6DCEB", -"%/ c #CAD3E5", -"&/ c #B9C3DA", -"*/ c #B3BCD6", -"=/ c #BAC3DB", -"-/ c #C5CDE1", -";/ c #B1BBD3", -">/ c #6A79A2", -",/ c #586A96", -"'/ c #5A6996", -")/ c #566693", -"!/ c #62709A", -"~/ c #7380A5", -"{/ c #4C597E", -"]/ c #303645", -"^/ c #151414", -"// c #1D1C1B", -"(/ c #8F9CBD", -"_/ c #94A0C0", -":/ c #818DB1", -"( c #201807", -",( c #261D09", -"'( c #251D09", -")( c #1F1807", -"!( c #171207", -"~( c #1A1815", -"{( c #5B6D99", -"]( c #7F8DB2", -"^( c #9EA9C8", -"/( c #8592B7", -"(( c #8491B6", -"_( c #6F7CA5", -":( c #7280A8", -"<( c #6B7AA4", -"[( c #C3CBE0", -"}( c #C9D1E3", -"|( c #D6DCEA", -"1( c #99A4C2", -"2( c #6D7CA5", -"3( c #63749F", -"4( c #6D7CA4", -"5( c #939EBE", -"6( c #A3AECC", -"7( c #A7B1CD", -"8( c #7785AC", -"9( c #7684AB", -"0( c #596997", -"a( c #586894", -"b( c #53638F", -"c( c #475270", -"d( c #2E3138", -"e( c #1B170D", -"f( c #251E09", -"g( c #2D230B", -"h( c #29200A", -"i( c #1E1806", -"j( c #140F07", -"k( c #1E1D1C", -"l( c #8B8B8B", -"m( c #868686", -"n( c #9CA7C4", -"o( c #8894B7", -"p( c #A5B0CC", -"q( c #8693B7", -"r( c #7F8DB0", -"s( c #7E8CAF", -"t( c #96A2C2", -"u( c #9DA8C6", -"v( c #CFD6E7", -"w( c #B5BED6", -"x( c #A8B1CD", -"y( c #B6BFD7", -"z( c #D5DBE9", -"A( c #C7CEE2", -"B( c #94A0C1", -"C( c #7A88AD", -"D( c #55648D", -"E( c #424B68", -"F( c #2A2C33", -"G( c #1E180D", -"H( c #2B220A", -"I( c #342A0D", -"J( c #34290D", -"K( c #30260C", -"L( c #2C230B", -"M( c #211A07", -"N( c #120C02", -"O( c #29292A", -"P( c #454646", -"Q( c #494B4F", -"R( c #4A4C51", -"S( c #5F6E9B", -"T( c #BAC2D7", -"U( c #BDC6D9", -"V( c #A3ADC7", -"W( c #AFBAD5", -"X( c #9CA7C6", -"Y( c #909CBB", -"Z( c #7886AB", -"`( c #9AA5C4", -" _ c #8D99BB", -"._ c #5E6D9A", -"+_ c #ACB6CF", -"@_ c #C6CEE3", -"#_ c #D8DEED", -"$_ c #D2D8EA", -"%_ c #E2E7F2", -"&_ c #D5DCEC", -"*_ c #C2CADF", -"=_ c #A7B2CE", -"-_ c #8592B8", -";_ c #6978A5", -">_ c #6D7CA7", -",_ c #6D7A9E", -"'_ c #4F576E", -")_ c #1F190B", -"!_ c #30250B", -"~_ c #3B2F0F", -"{_ c #3B2F10", -"]_ c #372C0E", -"^_ c #33290D", -"/_ c #30250C", -"(_ c #1C1608", -"__ c #1C1A17", -":_ c #373B46", -"<_ c #484A52", -"[_ c #464649", -"}_ c #363941", -"|_ c #5E6F9A", -"1_ c #9DA8C5", -"2_ c #AEB8D1", -"3_ c #B7C0D5", -"4_ c #ACB7D3", -"5_ c #7381A9", -"6_ c #6F7DA6", -"7_ c #8E9BBC", -"8_ c #6876A1", -"9_ c #7C89B0", -"0_ c #818FB5", -"a_ c #909DBE", -"b_ c #9CA7C5", -"c_ c #8F9BBC", -"d_ c #ADB7D2", -"e_ c #BCC6DE", -"f_ c #B2BCD5", -"g_ c #9EABC9", -"h_ c #5E6F9D", -"i_ c #6576A2", -"j_ c #6373A1", -"k_ c #58668F", -"l_ c #3D4660", -"m_ c #231C0C", -"n_ c #35290D", -"o_ c #403210", -"p_ c #403311", -"q_ c #3F3111", -"r_ c #382C0E", -"s_ c #30260B", -"t_ c #221A09", -"u_ c #171511", -"v_ c #383D4B", -"w_ c #5E6988", -"x_ c #767F96", -"y_ c #6C717D", -"z_ c #595C65", -"A_ c #575A61", -"B_ c #4D515B", -"C_ c #4E5362", -"D_ c #424652", -"E_ c #353A4C", -"F_ c #4A5678", -"G_ c #6978A3", -"H_ c #AEB9D3", -"I_ c #A5B1CF", -"J_ c #9FABC8", -"K_ c #98A4C2", -"L_ c #8B98BA", -"M_ c #9BA6C5", -"N_ c #A2ADC8", -"O_ c #9DA7C3", -"P_ c #6D7BA4", -"Q_ c #7382A9", -"R_ c #95A1C5", -"S_ c #95A1C3", -"T_ c #A4AFCD", -"U_ c #98A2C1", -"V_ c #7282AC", -"W_ c #7484B0", -"X_ c #6676A2", -"Y_ c #5E6C93", -"Z_ c #3C445A", -"`_ c #251D0B", -" : c #3A2D0D", -".: c #433511", -"+: c #423411", -"@: c #423511", -"#: c #3F3211", -"$: c #3A2D0F", -"%: c #18140C", -"&: c #2B2F3A", -"*: c #66718F", -"=: c #606F99", -"-: c #546393", -";: c #677499", -">: c #8790AB", -",: c #9EA5B9", -"': c #8690AC", -"): c #868FAE", -"!: c #6F799A", -"~: c #404554", -"{: c #383F54", -"]: c #576793", -"^: c #7F8DB3", -"/: c #7C8AB0", -"(: c #8A97BA", -"_: c #7080A9", -":: c #7382AA", -"<: c #9FAAC6", -"[: c #BAC3D9", -"}: c #97A2C1", -"|: c #7A87AD", -"1: c #A2ADCC", -"2: c #A0ABCB", -"3: c #9FAAC9", -"4: c #5A6B9B", -"5: c #6A7AA7", -"6: c #667499", -"7: c #384052", -"8: c #1C1B18", -"9: c #28200A", -"0: c #3D300F", -"a: c #463712", -"b: c #443612", -"c: c #453712", -"d: c #443511", -"e: c #413411", -"f: c #32280D", -"g: c #201A0F", -"h: c #2C2E38", -"i: c #6A738E", -"j: c #727FA5", -"k: c #6E7CA3", -"l: c #7B88AA", -"m: c #8A95B3", -"n: c #7D89AC", -"o: c #6C79A1", -"p: c #7684A8", -"q: c #64729C", -"r: c #747E99", -"s: c #40434E", -"t: c #5F6060", -"u: c #3A3C42", -"v: c #6576A0", -"w: c #6777A0", -"x: c #6F7EA6", -"y: c #8996BA", -"z: c #8A97BC", -"A: c #7C8AB1", -"B: c #97A2C3", -"C: c #8895B8", -"D: c #808DB3", -"E: c #8C99BB", -"F: c #99A4C4", -"G: c #939FC1", -"H: c #7B8BB2", -"I: c #BCC5DD", -"J: c #9CA9C8", -"K: c #6B7CA9", -"L: c #6E7FAB", -"M: c #697698", -"N: c #363B4A", -"O: c #1B1A13", -"P: c #403310", -"Q: c #483813", -"R: c #473813", -"S: c #463913", -"T: c #463813", -"U: c #433612", -"V: c #362A0D", -"W: c #221C0C", -"X: c #25282E", -"Y: c #495472", -"Z: c #68769E", -"`: c #717EA5", -" < c #9BA4BE", -".< c #8B96B4", -"+< c #6E7AA1", -"@< c #536393", -"#< c #6C7AA2", -"$< c #6D7BA2", -"%< c #62719B", -"&< c #7F8CAD", -"*< c #4A5062", -"=< c #7F7F80", -"-< c #4B4C4F", -";< c #596895", -">< c #5D6D98", -",< c #586995", -"'< c #5B6B97", -")< c #6575A0", -"!< c #6F7EA7", -"~< c #919EBE", -"{< c #B0BAD4", -"]< c #7987AE", -"^< c #7383AA", -"/< c #8E9BBE", -"(< c #919FC0", -"_< c #8E9BC0", -":< c #8C9ABE", -"<< c #6C7EAA", -"[< c #616D8D", -"}< c #343843", -"|< c #201C11", -"1< c #31270C", -"2< c #4A3B13", -"3< c #4B3B13", -"4< c #4A3A13", -"5< c #493A13", -"6< c #473913", -"7< c #392C0D", -"8< c #231C0B", -"9< c #25272C", -"0< c #566591", -"a< c #4F6190", -"b< c #63719C", -"c< c #8A95B4", -"d< c #7C88AA", -"e< c #586795", -"f< c #62719C", -"g< c #7A86AA", -"h< c #737EA4", -"i< c #7B88AB", -"j< c #7E89A8", -"k< c #444A5A", -"l< c #2A2B2C", -"m< c #51545D", -"n< c #5F6E99", -"o< c #5C6B98", -"p< c #5E6D98", -"q< c #5D6C98", -"r< c #606F9A", -"s< c #5A6A99", -"t< c #98A3C2", -"u< c #8492B5", -"v< c #8694B6", -"w< c #92A0C0", -"x< c #97A3C5", -"y< c #6F7FA9", -"z< c #8895BC", -"A< c #717FA7", -"B< c #55607F", -"C< c #353841", -"D< c #221E12", -"E< c #463812", -"F< c #4C3D15", -"G< c #4D3E15", -"H< c #4D3D14", -"I< c #4C3C13", -"J< c #4B3B14", -"K< c #493B13", -"L< c #3C2F0E", -"M< c #271E0C", -"N< c #26262A", -"O< c #404964", -"P< c #56658E", -"Q< c #808CAE", -"R< c #7784A8", -"S< c #67759E", -"T< c #6F7CA3", -"U< c #7783A8", -"V< c #8591B1", -"W< c #8E98B7", -"X< c #8D98B6", -"Y< c #96A0BC", -"Z< c #77819F", -"`< c #3F4451", -" [ c #272728", -".[ c #67686A", -"+[ c #4E5569", -"@[ c #5C6B96", -"#[ c #61719B", -"$[ c #5C6B97", -"%[ c #5F6E9A", -"&[ c #5E6C99", -"*[ c #6877A1", -"=[ c #6D7BA5", -"-[ c #61729D", -";[ c #7D8AB1", -">[ c #929FC0", -",[ c #7C8BB2", -"'[ c #7886A8", -")[ c #454F6C", -"![ c #2D2F37", -"~[ c #262010", -"{[ c #392E10", -"][ c #4A3B14", -"^[ c #4F3E15", -"/[ c #4F3F15", -"([ c #4E3E15", -"_[ c #4B3C14", -":[ c #40320F", -"<[ c #261F0B", -"[[ c #252427", -"}[ c #3E4761", -"|[ c #59678F", -"1[ c #ADB4CA", -"2[ c #B0B7CC", -"3[ c #8894B3", -"4[ c #7B89AC", -"5[ c #6F7DA4", -"6[ c #7B87AB", -"7[ c #9DA7C0", -"8[ c #9EA7C1", -"9[ c #98A3BC", -"0[ c #647297", -"a[ c #454C5F", -"b[ c #656667", -"c[ c #545966", -"d[ c #5B6B96", -"e[ c #5F6D98", -"f[ c #566593", -"g[ c #6778A4", -"h[ c #818FB6", -"i[ c #8D9ABC", -"j[ c #7D88A8", -"k[ c #4A536B", -"l[ c #28210F", -"m[ c #3E3210", -"n[ c #4F3F14", -"o[ c #504017", -"p[ c #514017", -"q[ c #514116", -"r[ c #514015", -"s[ c #443611", -"t[ c #2A200A", -"u[ c #252422", -"v[ c #3C465F", -"w[ c #51618C", -"x[ c #5B6B98", -"y[ c #7C88AB", -"z[ c #727EA5", -"A[ c #63739C", -"B[ c #576695", -"C[ c #5F6F99", -"D[ c #BDC3D4", -"E[ c #8F9AB8", -"F[ c #636C88", -"G[ c #3E424C", -"H[ c #5C5D5E", -"I[ c #535761", -"J[ c #515C7A", -"K[ c #5D6B97", -"L[ c #5F6E98", -"M[ c #5E6C98", -"N[ c #5C6A97", -"O[ c #5E6E99", -"P[ c #5E709D", -"Q[ c #697AA6", -"R[ c #6878A5", -"S[ c #5D6A92", -"T[ c #3F485E", -"U[ c #242423", -"V[ c #2A220D", -"W[ c #524215", -"X[ c #534216", -"Y[ c #544317", -"Z[ c #554217", -"`[ c #504016", -" } c #473812", -".} c #31270D", -"+} c #201D19", -"@} c #4B5064", -"#} c #657298", -"$} c #66759E", -"%} c #606E9A", -"&} c #7985AA", -"*} c #838EAF", -"=} c #717EA4", -"-} c #818DAD", -";} c #AFB7CC", -">} c #8490B1", -",} c #7E8AAD", -"'} c #9EA5BF", -")} c #676E83", -"!} c #353740", -"~} c #4D4E50", -"{} c #464A56", -"]} c #4A5574", -"^} c #505F8A", -"/} c #5D6C97", -"(} c #5B6A97", -"_} c #63729D", -":} c #5F6D9A", -"<} c #6979A4", -"[} c #7584AE", -"}} c #66749A", -"|} c #383E51", -"1} c #201E19", -"2} c #2F250C", -"3} c #483913", -"4} c #554417", -"5} c #564317", -"6} c #564417", -"7} c #574517", -"8} c #544316", -"9} c #524116", -"0} c #34290E", -"a} c #1F1C15", -"b} c #434858", -"c} c #67759F", -"d} c #6A779F", -"e} c #63729C", -"f} c #838FAF", -"g} c #7E8AAC", -"h} c #68779F", -"i} c #6A78A1", -"j} c #7885A9", -"k} c #8D97B5", -"l} c #A5ACC5", -"m} c #8A95B5", -"n} c #64739C", -"o} c #949EBB", -"p} c #B3BACD", -"q} c #9BA3B9", -"r} c #686E7D", -"s} c #363737", -"t} c #3C3E43", -"u} c #404659", -"v} c #4B587B", -"w} c #5C6A96", -"x} c #65739D", -"y} c #5E6D9B", -"z} c #707EA9", -"A} c #6C799A", -"B} c #343A48", -"C} c #1E1A11", -"D} c #362A0E", -"E} c #4C3C14", -"F} c #574617", -"G} c #584718", -"H} c #594718", -"I} c #584618", -"J} c #554317", -"K} c #4E3D14", -"L} c #211D15", -"M} c #3A4050", -"N} c #59678E", -"O} c #576693", -"P} c #5A6997", -"Q} c #616F9A", -"R} c #6A78A0", -"S} c #7481A5", -"T} c #6977A0", -"U} c #6E7CA2", -"V} c #A7AFC6", -"W} c #717FA5", -"X} c #9DA6C0", -"Y} c #B9C0D2", -"Z} c #9AA2B4", -"`} c #555C6C", -" | c #2F3137", -".| c #35363B", -"+| c #363A45", -"@| c #444C5E", -"#| c #4E5876", -"$| c #56648B", -"%| c #5B6995", -"&| c #62709C", -"*| c #64739B", -"=| c #545F7D", -"-| c #2D303C", -";| c #211B10", -">| c #3A2E0E", -",| c #584518", -"'| c #5A4818", -")| c #5B4818", -"!| c #5B4919", -"~| c #5A4819", -"{| c #574518", -"]| c #513F15", -"^| c #392D10", -"/| c #252218", -"(| c #383D4C", -"_| c #687492", -":| c #5A6995", -"<| c #576694", -"[| c #616F99", -"}| c #7986AA", -"|| c #6876A0", -"1| c #7986A9", -"2| c #9DA5C0", -"3| c #6C7AA1", -"4| c #64739D", -"5| c #A2ACC4", -"6| c #969FBC", -"7| c #8893B1", -"8| c #667498", -"9| c #77809B", -"0| c #687290", -"a| c #515E82", -"b| c #55658C", -"c| c #54648F", -"d| c #5B6A98", -"e| c #61729C", -"f| c #596792", -"g| c #48526D", -"h| c #2B2E34", -"i| c #251F10", -"j| c #3D3010", -"k| c #5C4919", -"l| c #5E4B19", -"m| c #5D4A19", -"n| c #544216", -"o| c #392D0F", -"p| c #282418", -"q| c #373B47", -"r| c #6A738F", -"s| c #67769F", -"t| c #546494", -"u| c #65749D", -"v| c #65739C", -"w| c #B5BBCF", -"x| c #7784A9", -"y| c #66749D", -"z| c #A6AFC7", -"A| c #A0A9C3", -"B| c #7581A6", -"C| c #8691B2", -"D| c #63729B", -"E| c #526189", -"F| c #424B63", -"G| c #292B2F", -"H| c #292211", -"I| c #5F4C1A", -"J| c #614D1A", -"K| c #604C19", -"L| c #5E4A19", -"M| c #5B4819", -"N| c #3E3110", -"O| c #262112", -"P| c #42444D", -"Q| c #727A93", -"R| c #7380A6", -"S| c #566594", -"T| c #62719A", -"U| c #5D6D97", -"V| c #8C97B5", -"W| c #ACB4C9", -"X| c #8591B2", -"Y| c #A1A9C3", -"Z| c #A1AAC2", -"`| c #7C87AA", -" 1 c #808BAD", -".1 c #5F6D99", -"+1 c #5A6A95", -"@1 c #566389", -"#1 c #3C455C", -"$1 c #2C2410", -"%1 c #463713", -"&1 c #5E4B1A", -"*1 c #604D1B", -"=1 c #624E1B", -"-1 c #634F1B", -";1 c #604C1A", -">1 c #5D4919", -",1 c #251F0F", -"'1 c #31343D", -")1 c #5F6A86", -"!1 c #808DAF", -"~1 c #606D99", -"{1 c #99A3BE", -"]1 c #6D7AA2", -"^1 c #A0A9C2", -"/1 c #8892B3", -"(1 c #98A2BD", -"_1 c #9DA6C1", -":1 c #737FA5", -"<1 c #5E6E98", -"[1 c #5A6B96", -"}1 c #5F6F98", -"|1 c #566287", -"11 c #3B4256", -"21 c #242320", -"31 c #2E250E", -"41 c #5C4918", -"51 c #634E1B", -"61 c #65511C", -"71 c #66511C", -"81 c #65501B", -"91 c #624F1A", -"01 c #5E4C1A", -"a1 c #241E0D", -"b1 c #292C33", -"c1 c #495576", -"d1 c #606F9B", -"e1 c #8490B2", -"f1 c #828EB0", -"g1 c #AEB5CB", -"h1 c #67759D", -"i1 c #7B86AA", -"j1 c #A9B1C8", -"k1 c #8792B2", -"l1 c #5B6996", -"m1 c #5C6C95", -"n1 c #556284", -"o1 c #323748", -"p1 c #1F1D16", -"q1 c #32290D", -"r1 c #4E3E13", -"s1 c #614E1B", -"t1 c #64501B", -"u1 c #68531C", -"v1 c #67521D", -"w1 c #67521C", -"x1 c #5B4918", -"y1 c #2A230E", -"z1 c #485375", -"A1 c #556492", -"B1 c #7985A9", -"C1 c #919BB8", -"D1 c #B1B8CD", -"E1 c #8A94B4", -"F1 c #8B95B5", -"G1 c #9EA8C0", -"H1 c #B2B9CD", -"I1 c #949EBA", -"J1 c #7280A5", -"K1 c #7A87AA", -"L1 c #707DA3", -"M1 c #5F709B", -"N1 c #2B2F3C", -"O1 c #1D1B0F", -"P1 c #5D4A18", -"Q1 c #66531C", -"R1 c #69551D", -"S1 c #6B561D", -"T1 c #69541D", -"U1 c #624F1B", -"V1 c #2E250F", -"W1 c #495370", -"X1 c #526390", -"Y1 c #6A79A1", -"Z1 c #97A1BD", -"`1 c #818DAE", -" 2 c #929CB9", -".2 c #8D97B6", -"+2 c #8893B3", -"@2 c #ABB3C9", -"#2 c #707EA5", -"$2 c #838EB0", -"%2 c #BEC5D5", -"&2 c #ACB3C9", -"*2 c #8F99B6", -"=2 c #6C79A0", -"-2 c #7683A8", -";2 c #7582A7", -">2 c #8B96B5", -",2 c #A8B1C6", -"'2 c #8B97B6", -")2 c #48526F", -"!2 c #242730", -"~2 c #1F190D", -"{2 c #6C571E", -"]2 c #6C581E", -"^2 c #6B571E", -"/2 c #64501C", -"(2 c #5F4B19", -"_2 c #4C3B13", -":2 c #2F260F", -"<2 c #272626", -"[2 c #4E566D", -"}2 c #50608C", -"|2 c #7E8BAD", -"12 c #818DAF", -"22 c #99A2BE", -"32 c #506191", -"42 c #8F99B7", -"52 c #9CA4BF", -"62 c #7582A6", -"72 c #8792B1", -"82 c #A8AFC6", -"92 c #C0C6D6", -"02 c #959FBB", -"a2 c #909AB8", -"b2 c #AAB1C8", -"c2 c #A2ABC3", -"d2 c #9EA7C0", -"e2 c #939DBA", -"f2 c #828EAF", -"g2 c #68779E", -"h2 c #AEB5CA", -"i2 c #A6ADC6", -"j2 c #161410", -"k2 c #211B0A", -"l2 c #3C2F0F", -"m2 c #6B551E", -"n2 c #6F581F", -"o2 c #70591F", -"p2 c #6E581E", -"q2 c #6A551D", -"r2 c #65501C", -"s2 c #31280F", -"t2 c #545B6F", -"u2 c #606E95", -"v2 c #516392", -"w2 c #8C97B6", -"x2 c #98A1BD", -"y2 c #7A86AB", -"z2 c #7F8BAE", -"A2 c #7D88AC", -"B2 c #8591B0", -"C2 c #7B87A9", -"D2 c #9BA4BF", -"E2 c #AEB6CA", -"F2 c #939CB9", -"G2 c #818CAE", -"H2 c #727FA4", -"I2 c #181511", -"J2 c #534215", -"K2 c #6D571E", -"L2 c #715B1F", -"M2 c #725C1F", -"N2 c #705A1F", -"O2 c #6C561E", -"P2 c #524115", -"Q2 c #332910", -"R2 c #272725", -"S2 c #3E465D", -"T2 c #828BA8", -"U2 c #7D89AD", -"V2 c #5F709A", -"W2 c #717FA4", -"X2 c #9AA4BF", -"Y2 c #9BA5BF", -"Z2 c #6D7AA1", -"`2 c #8994B3", -" 3 c #7F8BAD", -".3 c #6E7BA3", -"+3 c #7A87AB", -"@3 c #9FA7C1", -"#3 c #181611", -"$3 c #241D0D", -"%3 c #3B2E10", -"&3 c #67511C", -"*3 c #735C20", -"=3 c #755D20", -"-3 c #735C1F", -";3 c #69531D", -">3 c #554316", -",3 c #33280D", -"'3 c #252220", -")3 c #373F55", -"!3 c #536188", -"~3 c #6D7BA1", -"{3 c #7683A7", -"]3 c #616F9B", -"^3 c #7885AA", -"/3 c #909AB7", -"(3 c #707EA4", -"_3 c #7C89AB", -":3 c #9AA3BD", -"<3 c #C3C8D7", -"[3 c #99A3BD", -"}3 c #6B79A2", -"|3 c #8F99B8", -"13 c #201F1F", -"23 c #17140F", -"33 c #392D0E", -"43 c #504015", -"53 c #68521C", -"63 c #70581F", -"73 c #755E21", -"83 c #776021", -"93 c #634E1A", -"03 c #34280B", -"a3 c #1E1C17", -"b3 c #333B50", -"c3 c #4E5C83", -"d3 c #6F7DA2", -"e3 c #8590B1", -"f3 c #BFC5D5", -"g3 c #7381A6", -"h3 c #A4ADC4", -"i3 c #909BB9", -"j3 c #ADB5CA", -"k3 c #919DB9", -"l3 c #4F608F", -"m3 c #7D8AAC", -"n3 c #848FB0", -"o3 c #A1A9C2", -"p3 c #12100B", -"q3 c #1F1707", -"r3 c #382B0D", -"s3 c #67531C", -"t3 c #715A1F", -"u3 c #775F21", -"v3 c #796122", -"w3 c #776020", -"x3 c #725B1F", -"y3 c #6B551D", -"z3 c #624E1A", -"A3 c #3B2E0F", -"B3 c #19170D", -"C3 c #313644", -"D3 c #55638A", -"E3 c #526492", -"F3 c #6F7CA4", -"G3 c #9FA8C2", -"H3 c #0D0C07", -"I3 c #1A1406", -"J3 c #34290C", -"K3 c #493913", -"L3 c #594618", -"M3 c #796222", -"N3 c #7C6323", -"O3 c #796121", -"P3 c #725C20", -"Q3 c #524113", -"R3 c #362A0B", -"S3 c #262218", -"T3 c #555656", -"U3 c #64729D", -"V3 c #556592", -"W3 c #556493", -"X3 c #606F98", -"Y3 c #7884AA", -"Z3 c #B9C0D3", -"`3 c #A1AAC3", -" 4 c #929DB9", -".4 c #6978A0", -"+4 c #828FB0", -"@4 c #6B7AA1", -"#4 c #808DAE", -"$4 c #1D1C1D", -"%4 c #110F0C", -"&4 c #171205", -"*4 c #534316", -"=4 c #7B6322", -"-4 c #7E6523", -";4 c #7A6221", -">4 c #735B20", -",4 c #6A541D", -"'4 c #4E3C10", -")4 c #362A0C", -"!4 c #353126", -"~4 c #5A5B5A", -"{4 c #919BB7", -"]4 c #B8BED0", -"^4 c #CFD4E0", -"/4 c #8995B5", -"(4 c #B0B8CC", -"_4 c #98A1BC", -":4 c #536494", -"<4 c #100F0B", -"[4 c #141004", -"}4 c #251C08", -"|4 c #3A2D0E", -"14 c #7C6322", -"24 c #816623", -"34 c #7D6322", -"44 c #745D1F", -"54 c #69521D", -"64 c #5F4B1A", -"74 c #4D3C12", -"84 c #362B0F", -"94 c #2C271C", -"04 c #3C3B3A", -"a4 c #596A95", -"b4 c #838FB0", -"c4 c #808CAD", -"d4 c #939DB9", -"e4 c #7884A9", -"f4 c #7B87AA", -"g4 c #828DAE", -"h4 c #8993B3", -"i4 c #151513", -"j4 c #100E06", -"k4 c #1C1605", -"l4 c #433611", -"m4 c #6C571D", -"n4 c #7B6321", -"o4 c #816723", -"p4 c #67511B", -"q4 c #4A3B12", -"r4 c #2B2004", -"s4 c #363126", -"t4 c #626160", -"u4 c #7D88AB", -"v4 c #6E7BA2", -"w4 c #707CA3", -"x4 c #63719B", -"y4 c #939EBA", -"z4 c #7E89AB", -"A4 c #5C6C99", -"B4 c #100F0A", -"C4 c #251D08", -"D4 c #382C0F", -"E4 c #4D3C14", -"F4 c #614F1B", -"G4 c #755E20", -"H4 c #7E6522", -"I4 c #7B6121", -"J4 c #6F581E", -"K4 c #614E1A", -"L4 c #2D2309", -"M4 c #282419", -"N4 c #4C4B49", -"O4 c #919193", -"P4 c #66739D", -"Q4 c #6F7DA3", -"R4 c #7F8AAD", -"S4 c #707DA4", -"T4 c #7280A6", -"U4 c #67749D", -"V4 c #8E98B6", -"W4 c #0E0C06", -"X4 c #171204", -"Y4 c #534116", -"Z4 c #2C220A", -"`4 c #272319", -" 5 c #535452", -".5 c #848687", -"+5 c #727FA6", -"@5 c #6A789F", -"#5 c #7D89AB", -"$5 c #7481A6", -"%5 c #8C96B5", -"&5 c #6D7BA3", -"*5 c #110E09", -"=5 c #2E250B", -"-5 c #413410", -";5 c #4E3F15", -">5 c #352A0D", -",5 c #130F05", -"'5 c #7C87AC", -")5 c #96A0BB", -"!5 c #959FBA", -"~5 c #919BB9", -"{5 c #526393", -"]5 c #12110D", -"^5 c #181307", -"/5 c #483812", -"(5 c #413311", -"_5 c #372B0E", -":5 c #1C1505", -"<5 c #0F0E07", -"[5 c #7480A6", -"}5 c #919CB8", -"|5 c #7F8CAE", -"15 c #222323", -"25 c #14110B", -"35 c #201805", -"45 c #32270D", -"55 c #362B0E", -"65 c #31260C", -"75 c #211907", -"85 c #150F02", -"95 c #0C0A03", -"05 c #3C3C3D", -"a5 c #7C88AC", -"b5 c #8995B4", -"c5 c #6D7CA3", -"d5 c #737272", -"e5 c #454546", -"f5 c #1E1E1D", -"g5 c #090700", -"h5 c #100B00", -"i5 c #1A1303", -"j5 c #1D1504", -"k5 c #1B1404", -"l5 c #151002", -"m5 c #0C0800", -"n5 c #0C0A06", -"o5 c #232222", -"p5 c #383839", -"q5 c #302F2E", -"r5 c #1D1B19", -"s5 c #13110C", -"t5 c #13100A", -"u5 c #100D08", -"v5 c #0D0B07", -"w5 c #161613", -"x5 c #7B87AC", -"y5 c #6D6D6E", -"z5 c #5C5D5D", -" ", -" ", -" ", -" . + @ # $ ", -" % & * = - ; > - , ", -" ' ) ! ~ { ] ^ / ( _ : < [ ", -" } | 1 2 3 4 5 6 7 8 9 0 a b c d e ", -" f g h i j k l m n o 8 0 a p q r s t u ", -" v w x y z A B C D E F G m m ^ H I J K L M N O ", -" P Q $ R S T U V W X Y Z ` ...n +.@.#.$.%.&.*.=.-.;.>.,.'. ", -" ).!.~.{.P ].^./.(._.:.<.[.}.|.1. .2.3.4.5.6.7.8.9.0.a.4.b.c.d.e. ", -" f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.4.B.C.D.I 7.E.F.G.H.I.J.K.L.M. ", -" N.O.P.Q.R.S.T.l.U.V.R.W.X.Y.Z.`. +.+++@+#+` $+%+7 &+*+=+-+;+I 7.>+>+,+'+)+!+~+{+ ", -" ]+^+/+(+_+:+<+[+}+|+1+2+3+4+5+6+7+2+8+9+0+a+b+c+d+,.G +.3.e+f+g+h+i+j+k+l+m+n+o+o+p+q+ ", -" r+s+t+u+v+w+x+y+z+A+B+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+G B.W+X+Y+Z+`+ @.@+@@@#@$@%@&@ ", -" *@| =@-@;@>@,@'@)@!@~@{@]@^@/@(@_@:@<@[@}@|@1@1 2@3@4@5@6@7@8@9@0@^.+.B.b.g+a@G o+^.b@c@d@e@f@g@h@ ", -" i@j@k@l@m@n@o@p@q@r@s@w+t@u@v@w@x@y@z@A@B@C@y@D@E@F@G@H@I@J@K@L@M@N@O@P@Q@B.A.R@S@T@a.U@V@W@X@Y@Z@`@ # ", -" .# +#@###$#%#&#*#=#-#;#>#,#'#)#!#~#{#]@]#<@^#/#(#_#:#<#[#}#|#1#2#2#K@3#4#5#F.6#7#8#8#9#0#a#b#c#d#e#f#g#h#i# ", -" j#k#l#m#n#o#p#,+q#r#s#t#u#v#=#w#x#y#`+z#A#B#C#~#D#E#F#G#w@H#I#J#K#L#M#H@N#O#P#Q#R#S#d T#U#V#@#W#X#Y#Z#`# $.$+$@$#$$$ ", -" %$&$A.; *$=$-$>#;$>$,$'$)$!$~${$]$^$/$($_$d+:$<$[$}$!#|$1$2$2$3$4$5$6$7$L#M#8$9$0$a$b$n#c$d$e$f$g$h$$.i$j$k$l$m$n$o$p$q$r$ ", -" s$t$u$F.v$w$x$y$z$A$B$C$D$E$F$G$H$I$J$K$L$M$N$O$P$Q$R$S$T$U$V$G@W$D#X$Y$Z$v@`$ %.%+%@%~@#%$%%%- f+&%*%=%-%;%>%,%'%)%!%~%{%]%^%/%(% ", -" } _%:%,+<%[%}%|%1%2%=%3%4%5%6%7%8%9%,$0%$.a%a%b%c%d%e%f%g%h%i%j%k%9#l%J#m%n%o%p%q%r%s%7$t%u%v%w%3.C.7 F 7 L.q x%y%z%A%B%B%C%D%E%F%G%H%I% ", -" - J%K%L%M%N%O%P%Q%R%S%T%4%x$U%V%W%X%Y%Z%`%q# &;@.&+&c%@&^$#&$&Q#%&&&*&T$=&-&;&p%Z$>&P+,&'&)&$##./ F 4.g$g$@.!&k%~&8.;%{&]&^&/&(&_&:&<&[&}&|& ", -" 1&2&3&4&5&6&7&8&9&0&a&b&c&d&e&B$a&f&g&G$h&Z%s#i&j&@@k&l&m&n&o&p&q&r&s&t&u&v&w&x&y&z&A&B&%+C&D&E&F&G&@.@.@.4./ E$H&3.I&J&K&L&M&N&O&P&Q&R&S&T& ", -" U&V&W&X&Y&Z&`& *.*+*@*#*$*%*&*W&**=*,+-*;*`%>*K$i&,*'*)*!*~*{*]*^*/*]*(*.%4#_*=.x&:*#.D$<*[*}*|*1*2*U@4.8 g$@.}*3*B.4*5*6*7*8*9*0*a*b*c*d* ", -" l#e*f*g*h*i*j*k*l*m*n*o*p*q*r*r*s*f&s*E.d.4 t*u*v*%$w*x*y*z*h%A*B*C*D*E*)&F*G*H*I*J*K*L*M*N*O*P*Q*R*7 D @.7 S*T*U*V*W*X*Y*Z*`* =.=+=@=#=$= ", -" %=&=*===O%N%8%-=;=>=,='=)=!=~={=i+]=F$^=/=o#(=J$_=:=<=[=}=|=1=|=c+2=3=4=5=6=p 7=8=L*L*M*}*O*9=0=a=b=c=4.3.a.o d=e=f=g=h=i=j=k=l=m=n=o=p=q= ", -" Y+r=s=t=O%u=v=w=x=y=z=A=q*B=C=C=D=E=]=t=F=G=m+H=I=J=K=L=M=N=O=g%P=Q=a.E$R=S=T=X%U=M*U#N*N*; L*V=W=W=<*X=3.!&C&U@Y=e=Z=`= -.-+-@-#-$-%-&- ", -" *-=---;->-,-M%O%'-)-!-~-{-]-^-/-(-(-_-F$:-<-4#[-}-|-1-2-C 3-4-5-6-7-8-X=9-0-a-b-c-8-N*M*8-c-d-e-U=f-X%}*; g-h-f=i-j-k-l-m-n-o-p-q-r-s-t- ", -" S*u-v-w-F$>+x-y-z-(-A-q#B-C-D-**E-x-F-G-H-(=I-J-K-L- &M-N-O-P-Q-=%R-S-T-s=U-V-W-M*N*N*; 7-e-d-d-e-U=e-f-X-Y-Z-`-$# ;.;+;@;#;$;%;&;*; ", -" =;-;;;>;,;';,+);C=C=!;~;{;}%~=];^;/;(;_;H-:;8.<;.&L-I$[;V-};u-|;1;2;3;4;5;6;7;8;O*; ; M*8=d-d-*$d-e-e-9;T*L-8.0;a;b;c;d;e;f;g;h;i; ", -" F&j;k;l;m;n;o;p;q;r;s;,&t;u;^-v;w;x;y;;@7.z;m+A;B;C;N*D;E;F;G;H;I;J;K;L;F&M;N;f-m#; }*f-e-d-*$d-*$d-e-U=6#O;P;:%Q;R;S;T;U;V;W; ", -" L;X;j;Y;Z;`; >.>+>@>#>--t;$>%>Y;;-&>H ;+H-I-*>=>->m#;>`.>>,>'>)>!>~>{>]>^>/>(>X%c=f$X%7-8=d-d-d-*$*$e-e-e-f-N*_>:><>[>}>|>1>2> ", -" 3>4>5>6>7>8>9>0>a;a>b>c>;-d>$>4%e>d>f>o;g>h>i>j>k>8 m l>m>n>o>!>p>q>r>s>t>u>v>N*w>( U=c-e-d-e-d-*$d-e-U=8=X%x>y>=$z>A>E$B>C>D>E> ", -" F>G>H>s M%7>);I>J>r;K>s*v;L>M>N>O>P*K*8;8;P>Q>8 R>S>T>U>V>W>X>Y>Z>`> ,.,g$/ =;( +,@,<*8-8=d-d-e-d-e-U=U=f-; c=#,$,7-%,&,*,=,-,;,>,,, ", -" ',),!,~,{,D=];],^,/,(,C=_,:,<,[,},|,1,2,3,4,8>5,6,7,8,9,0,a,b,c,d,e,f,g,C.B.C&X=^ ^ m H>e-*$d-e-U=e-e-e-N*f$8=e-d-h,i,9=@.j,k,l,m,n, ", -" o,p,q,r,y-{==*s,t,u,v,w,x,y,z,A,K*B,0.C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,C.D H>P-S,m T,U,V,L**$8=U=U=U=8=( X%9;U=d-e-W,X,*$Y,Z,`, ' ", -" J%.'+'a&@'#'$'%'&'s**'='-';'c=>',''')'!'~'{']'^'/'('_':'<'['}'|'1'2'7 3.N*3'(>4'5'6'^>g$C&*$8=X%f-[*H>U=8=f-U=X%7'8'n,9'0'a'b' ", -" c'd'e'{=f'w;g'h'i'Z;*'='*'j'_ ; 7-k'l'm'n'o'p'q'r's'L,t'u'v'w'x'y'7 C.&+z'N;F&4'R*F&S*4.@.=;L*e-L*L*X%f-9;8=7-_ A'B'C'D'E'F'G' ", -" E H'v;I'';J'K'L'L'M'w,N'}%O'P'Q'L*R'S'T'U'V'W'X'Y'Z':'`' ).)+)@)#)@.4.^ $)3&(>%)&)*)=);>] -)k%8=8;M*z'f-;)>),)')))!)~)>'{)6 .G 3.F =;C&_ @,w>7# ", -" ])*+H*^)O@l;]=/)i'()_):)a;C=<%w;<)[)})|)1)2)3)4)5)K,6)7)8)9)0)a)b)c)d)F ),%*e)N;f)g)h)i) N*O*j)k)7-l)m)n)k+o)p)q)9 .r)j%-)j%..2.C.+,{){)8 4.a.4. ", -" s)X-t)u)v)]=';w)x)y)z)D=,-*'~,A)B)C)1)D)E)F)0,G)H)I)J)K)L)M)N)O)P)F P-Q)*)s=R) S)T) 4.U)V)W)X)Y)Z)`) !.!y,+!_;@!^.#!$!%!&!*!=!n &+8 F ,#+.g$7# ", -" -!Y%**X&;!>!,!'!)!D={={=C$!!!,~!{!]!^!/!(!_!L,:!&+G =!d m!|.n!o!/ c=F D > @,C&0 ", -" p!q!r!s!t!E=E=(-,;,;D=,;u!v!w!x!y!z!A!J,B!C!D!E!9)F!G!H!I!J!K!L!M!N! a!O!P!Q!R!S!T!U! V!W!X!Y! S*> Q@j%Z!$!o+> +.`! ~*!6 ^ l#w>+, ", -" .~m#p ';];C=C=C=,-D=';+~j;@~x!#~$~%~&~*~=~-~;~>~,~'~)~!~~~{~]~ ^~/~(~_~:~<~[~0!}~S!|~S)1~ {)F ,#n+^.6 - m!# 2~@ &$^.+.G @.^ ", -" 3~V,7><%^-W&,;D={=';B$Y%4~5~6~7~8~9~*~0~a~b~c~d~e~f~g~h~ }~i~j~k~l~m~n~o~p~q~r~s~S)t~ m @./ / - $ u~v~# w~# x~=!o!2.g$w> ", -" y~z~A~w,p B~,;{=C~D~E~F~G~H~I~J~K~L~M~N~O~P~'>Q~R~ S~m~|~T~U~V~T!R!0!o~W~X~Y~R!a!Z~R! =;9 +,n V@Z!, `~ ~@ {k#.{o!o!+.&+ ", -" e+O;d>D$}%],+{u=@{#{${%{&{I~*{={-{;{>{,{'{){ !{O!~{{{]{^{/{R!({_{:{<{[{}{|{1{2{3{a!m~ o l#7 ^.4{',Z!&!5{:> {6{&!o+T@T@!&x> ", -" 7{8{9{W&0{+!_-a{b{c{d{e{f{g{h{i{j{k{l{ m{n{o{p{q{r{s{t{u{v{w{x{y{z{A{B{C{D{E{F{c!S)G{ %,g$,#o+V@4{',$!H{I{J{K{L{',8#] - a.^ ", -" M{N{*'w,H t=O{P{Q{R{S{T{U{V{W{X{ Y{Z{`{ ].]+]@]#]$]%]&]*]=]-];]>],]']_~)]!]T!b!p~}~S~ P-C.,#] o+%%d M.&$`!v~# K{~]j%Q@,#+.g$=; ", -" {]e&*'H 0{I*5%]]^]/](]_]:]<] []}]|]1]p{2]3]4]5]6]7]8]9]Q!0]a]b]c]d]e]f]g]h]T!q~R!i]U!j]k]8 D ,#8#=!j%$ = |.u~l]L{w~u~o+2.2.D &+ ", -" 4'm]0{n]=$o]p]q]r]s]t] u]v]w]x]y]z]A]B]C]e]D]E]F]G]H]:~I]J]K]m~m~S~a!L]M]E{N]O]P]Q]&'F +.Q@o!R].{n!x~&!5{~] {K{$!8#6 6 / 8 ", -" S]h-T]U]8>X+V] W]X]Y]T~Z]`] ^.^+^@^#^$^%^&^*^=^-^;^>^,^'^)^!^~^{^S~t{]^^^i]/^(^f&C.2.] ^.r)4{*!m!H{I{_^2~J{:^,#G n F m ", -" <^;$[^ }^|^1^2^3^4^5^6^7^8^9^0^a^b^c^d^e^f^g^h^i^}{j^Z{k^}~i]l^0!m^n^o^p^a@i-q^T@o+j%$ = |.`~v~L{2~I{%%6 .3.+,m ", -" r^s^t^}~u^v^w^a]x^y^z^.]A^B^C^D^E^F^f^G^H^I^J^K^L^M^O]N^}~^~_{O^P^Q^t=+,G - 8#n+#!:^x~&!u~l] {6{%!.{%%V@Q@g$ ", -" R^S^T^U^V^W^X^|~Y^Z^`^ /./+/@/#/$/%/&/*/=/-/;/>/G{,/'/)/}~}~!/~/{/]/^///@. .,#^.R].{',$!&!k#_^L{l]I{~]:>:>- ", -" (/_/:/X^(,('()(!(~(&++.,#~+R].{:^#!,#- d ~]w~x~/ ", -" {(](^(/(((_(:(<(Q/T^[(}(|(1(2(3(4(5(@(q{6(7(8(9(`/0(Z~a(R!b(c(d(e(f(g(g(h('(i(j(k(F .] ^.n+~+B.4.n r)k#l(m(-)Q+ ", -" K^n(A{o(#(p(q(r(s(t(u(]{y/v(w(x(y(z(z^A(d^B(C(1/c!:~0]>/D(E(F(G(H(I(J(K(L(h(M(N(f$O(/ 6 q^G x>9 7#B.] n!~]@ :^P(Q(R(h&H- ", -" c!S(T(U(V(A(W(X(Y(Z(`( _._+_@_b^#_$_%_&_*_=_-_}/R!`/;_>_,_'_u=)_!_~_{_]_^_/_h((___:_<_[_z>A.a@);&>S*C.+.~+j%#!r)M.&$4{+.7.}_ ", -" i~|_1_2_3_a/4_+_5_6_((7_8_9_0_a_b_c_d_e_f_C^g_W~h_i_j_k_l_8>m_n_o_p_q_~_r_s_t_u_v_w_x_y_z_A_B_C_D_]=F / 7 &+a.n+~]l(:>r)C.z-E_F_", -" 0!Y~G_Z{H_I_J_;^K_L_M_X~N_O_P_*(t~Q_y(R_S_T_U_V_W_X_Y_Z_g*`_ :.:+:@:+:#:$:h(%:&:*:=:-:;:>:,:':):!:~:6.m {)S*B.^.|. {_^$!r)n C${:", -" ]:S~K^T!9!{{^:/:T^(:/(_:::<:[:}:f]|:<:1:2:H_3:}/4:5:6:7:8:9:0:a:b:c:b:d:e:f:g:h:i:j:n^k:l:m:n:o:p:q:r:s:,&{)g$B. .=!:^Z!|.5{l]t:u:", -" v^Z~'/Z~q~0!v:w:x:^{y:z:A:B:C:D:E:F:G:H:G^I:J:K:L:M:N:O:L(P:Q:R:S:T:c:U:V:W:X:Y:Z:`: <.<+ &+/ ,#R]$ m!5{# L{m<", -" n<%[&(i]&[e<*[=[-[-[)^;[>[J_^:,['[)[![~[{[][^[/[/[([G<_[:[<[[[}[|[o<}~%<1[2[3[4[5[6[7[8[9[0[a[o;),+,4.G ] R]$ m!%!b[c[ ", -" Q!'<;<'/d[l~=:%[e[$['l[m[n[o[p[q[r[/[G},}'})}!}m C.n ] ^.=!~}{}]}^} ", -" g/W^]^t{t{i]/}b!F//}(}x/_}:}'/%[r<(}t{<}[}}}|}1}2}3}4}5}6}7}6}8}9}5<0}a}b}|[u^c}d}e}:{>}f}g}h}i}j}k}l}m}n}o}p}q}r}s*9 8 F s}t}u}v}S) ", -" '/$[$[%<]^]^'|^[,|'|)|!|~|I}{|]|^|/|(|_|Z~*(D/:|<|Z~p<[|}|S<||_}m^1|2|W^3|4|5|6|7|8|9|0|a|b|c|m^d| ", -" S~i](}><$[i]&(>1I}s[,1'1)1!1~1q<'<:~i] ", -" M1v^F/N]j]N1O1r_r[P191Q1R1S1T171U1m|J2k},2'2S! ", -" )2!2~2~_r[m|t1T1{2]2^2u1/2(2_2:2<2[2}2R!9!Q!&(#2|21222n:32u|4252o}6272829202a2b2c2d2X}e2f2n:A[*|g2h2i2Q! ", -" j2k2l2W[(271m2n2o2p2q2r2=1/[s29 t2u2v2R!Z~q~U!Z~j:w2x2y2x/^^z2A2U3,3'3)3!3n<~3B1,}{3n}]3^3/3n:t{l^d[G2C1(3_3a2:3<3[3B2I1X2k1X3A3B3C3D3$[)/E3R!i]U!q~x[Q!&[Z23|m^&(e}W28[Y}/3Q4,4=1'4)4!4~4 W^]^i]U!b!N]O]}~l^)/G/{4]4^4W2k1x}g/D|4|}~x4v4%[y4z4=:q3 }L4M4N4O4 ]^P4j:Q4R4j}S4n:>}{3({[1n}T4j:62U4W202V4 1g< ", -" W4X4H(o_Y461x3x353'|H5C4,5n }$ '5e}4|)5!5q=!x~ x5^3$n/9 x>e5z5`~ W^V^f/ ", -" ", -" "}; +/* XPM */ +static char * C:\Users\iszatt\Desktop\icon ToFDeviceGeneration\icon2_xpm[] = { +"100 100 3101 2", +" c None", +". c #F3F3F3", +"+ c #ACACAC", +"@ c #7E7E7E", +"# c #7C7C7C", +"$ c #5E5E5E", +"% c #E3E3E4", +"& c #A1A1A3", +"* c #8B8B8A", +"= c #646464", +"- c #474747", +"; c #121212", +"> c #323232", +", c #6B6B6B", +"' c #EAEAEA", +") c #C9CACA", +"! c #98989A", +"~ c #777779", +"{ c #454547", +"] c #4A4A4A", +"^ c #2C2C2C", +"/ c #3B3B3B", +"( c #181818", +"_ c #1D1D1D", +": c #252526", +"< c #343435", +"[ c #C3C3C2", +"} c #D5D5D5", +"| c #B0B1B3", +"1 c #B8B9BC", +"2 c #707174", +"3 c #707276", +"4 c #414245", +"5 c #454647", +"6 c #444444", +"7 c #383838", +"8 c #2F2F2F", +"9 c #262626", +"0 c #202020", +"a c #1B1C1D", +"b c #19191B", +"c c #161618", +"d c #5D5D5D", +"e c #C8C8C8", +"f c #C5C7C9", +"g c #B0B6B9", +"h c #A1A4A8", +"i c #8F8F95", +"j c #48484D", +"k c #505153", +"l c #515152", +"m c #2B2B2B", +"n c #404040", +"o c #222222", +"p c #212223", +"q c #18191A", +"r c #232326", +"s c #131315", +"t c #727274", +"u c #BFBFBE", +"v c #D0D4D7", +"w c #BEC3C6", +"x c #B6BABD", +"y c #B2B6BC", +"z c #AEB4B9", +"A c #909499", +"B c #86888D", +"C c #57585C", +"D c #3F3F3F", +"E c #4B4B4A", +"F c #343434", +"G c #414141", +"H c #28292B", +"I c #2F3032", +"J c #232226", +"K c #343438", +"L c #201F22", +"M c #3D3D3F", +"N c #8F8F90", +"O c #B9B8BA", +"P c #C9C9CB", +"Q c #B8B8B8", +"R c #8B8D8E", +"S c #D3D7DA", +"T c #B9BDC1", +"U c #B3B7BB", +"V c #AAAEB2", +"W c #A3A9AD", +"X c #9FA3A8", +"Y c #83878B", +"Z c #747578", +"` c #5B5B5D", +" . c #434343", +".. c #505050", +"+. c #424242", +"@. c #373737", +"#. c #353637", +"$. c #323335", +"%. c #2F3133", +"&. c #323235", +"*. c #353537", +"=. c #3C3C3E", +"-. c #39393A", +";. c #302F30", +">. c #464546", +",. c #4A4A4C", +"'. c #F3F3F4", +"). c #D3D4D6", +"!. c #D0D2D5", +"~. c #E2E5E7", +"{. c #E2E4E6", +"]. c #4B4A4B", +"^. c #4F4F4F", +"/. c #BCBFBF", +"(. c #B6BABF", +"_. c #A7ACB0", +":. c #9FA4A8", +"<. c #9CA0A4", +"[. c #8B8F92", +"}. c #797A7D", +"|. c #6A6A6A", +"1. c #595958", +"2. c #454545", +"3. c #3D3D3D", +"4. c #363636", +"5. c #323334", +"6. c #303133", +"7. c #333436", +"8. c #3D3E40", +"9. c #333335", +"0. c #2C2C2D", +"a. c #3A3A3A", +"b. c #373738", +"c. c #3B3B3E", +"d. c #3B3B3F", +"e. c #A0A0A1", +"f. c #E5E7E8", +"g. c #D0D2D3", +"h. c #CED0D2", +"i. c #D7DADC", +"j. c #E3E6E8", +"k. c #E0E4E7", +"l. c #DADEE1", +"m. c #D7DCE0", +"n. c #D4DADC", +"o. c #DCE0E1", +"p. c #999898", +"q. c #2A2A2B", +"r. c #656668", +"s. c #B0B6BA", +"t. c #A1A5AA", +"u. c #999BA0", +"v. c #919297", +"w. c #757679", +"x. c #666667", +"y. c #5A5A5B", +"z. c #505051", +"A. c #3B3B3C", +"B. c #3C3C3C", +"C. c #393939", +"D. c #2F3031", +"E. c #363739", +"F. c #363638", +"G. c #313136", +"H. c #35353B", +"I. c #38383E", +"J. c #38373C", +"K. c #3D3C41", +"L. c #2F2F31", +"M. c #636363", +"N. c #BDBFBF", +"O. c #AEB2B4", +"P. c #B3B7BA", +"Q. c #CACED1", +"R. c #D1D6D9", +"S. c #D9DDE0", +"T. c #DBDFE2", +"U. c #D6DADD", +"V. c #D1D5D8", +"W. c #CED2D5", +"X. c #D3D6D8", +"Y. c #DADDDE", +"Z. c #616364", +"`. c #2C2D2D", +" + c #8D8F94", +".+ c #9B9EA4", +"++ c #919296", +"@+ c #7A7B7F", +"#+ c #6B6C6D", +"$+ c #4E4E51", +"%+ c #474748", +"&+ c #333333", +"*+ c #424241", +"=+ c #40403F", +"-+ c #222223", +";+ c #2D2E30", +">+ c #333438", +",+ c #37383C", +"'+ c #39383C", +")+ c #434247", +"!+ c #575659", +"~+ c #4D4D4D", +"{+ c #B1B1B1", +"]+ c #C6C6C7", +"^+ c #BBBDC0", +"/+ c #A4A6A9", +"(+ c #A6A9AC", +"_+ c #BDC1C4", +":+ c #BBBFC2", +"<+ c #BABEC1", +"[+ c #C8CCCF", +"}+ c #D2D6D9", +"|+ c #D2D7DA", +"1+ c #D5D9DC", +"2+ c #D4D7DA", +"3+ c #D2D5D7", +"4+ c #D7D9DB", +"5+ c #D4D6D9", +"6+ c #C9CED0", +"7+ c #BFC4C7", +"8+ c #939395", +"9+ c #3F3F41", +"0+ c #5B5D5F", +"a+ c #7D7E82", +"b+ c #8B8C8F", +"c+ c #6D6E70", +"d+ c #5A5A5C", +"e+ c #333332", +"f+ c #3E3E3D", +"g+ c #313133", +"h+ c #2E2E30", +"i+ c #2D2E31", +"j+ c #2E2F31", +"k+ c #343538", +"l+ c #3D3D40", +"m+ c #454648", +"n+ c #535353", +"o+ c #515151", +"p+ c #353534", +"q+ c #AAAAA9", +"r+ c #CCCDCC", +"s+ c #7F8184", +"t+ c #999B9E", +"u+ c #9A9C9F", +"v+ c #96989D", +"w+ c #9CA0A3", +"x+ c #A6ABAE", +"y+ c #AEB2B5", +"z+ c #ADB0B2", +"A+ c #B5B6B9", +"B+ c #B6B8BC", +"C+ c #C3C5C9", +"D+ c #CDCFD4", +"E+ c #CED1D4", +"F+ c #D7D8DB", +"G+ c #D1D3D5", +"H+ c #CFD1D3", +"I+ c #E0E0E2", +"J+ c #E4E5E7", +"K+ c #D4D6D8", +"L+ c #BCBFC3", +"M+ c #B5B8BC", +"N+ c #BBBEC2", +"O+ c #A8AAAC", +"P+ c #77787A", +"Q+ c #3B3C3F", +"R+ c #3A3B3C", +"S+ c #666768", +"T+ c #606062", +"U+ c #515153", +"V+ c #4A4A4B", +"W+ c #353536", +"X+ c #2E2E2F", +"Y+ c #2D2D2F", +"Z+ c #2A2B2B", +"`+ c #2F2F30", +" @ c #424243", +".@ c #514F4E", +"+@ c #535252", +"@@ c #48494A", +"#@ c #403F3D", +"$@ c #51514C", +"%@ c #434244", +"&@ c #424143", +"*@ c #E9E8EA", +"=@ c #646567", +"-@ c #565757", +";@ c #2C2D2F", +">@ c #7A7E7E", +",@ c #979B9F", +"'@ c #95989E", +")@ c #9DA1A5", +"!@ c #9FA3A6", +"~@ c #A0A4A7", +"{@ c #A9ABAD", +"]@ c #AEAFB1", +"^@ c #B2B3B5", +"/@ c #C0C1C5", +"(@ c #CECFD4", +"_@ c #D3D4D7", +":@ c #D2D3D5", +"<@ c #C9CACC", +"[@ c #CCCDCF", +"}@ c #D6D7D9", +"|@ c #DEDFE0", +"1@ c #D9DADC", +"2@ c #AEAFB4", +"3@ c #A7AAAE", +"4@ c #A3A7AA", +"5@ c #9EA2A5", +"6@ c #9D9FA2", +"7@ c #4D4E4E", +"8@ c #181918", +"9@ c #404041", +"0@ c #58585B", +"a@ c #323234", +"b@ c #444242", +"c@ c #43423F", +"d@ c #4B4B47", +"e@ c #504E50", +"f@ c #302C39", +"g@ c #100A20", +"h@ c #6A6871", +"i@ c #C0C0C0", +"j@ c #727475", +"k@ c #4D5153", +"l@ c #414547", +"m@ c #55585A", +"n@ c #606163", +"o@ c #424342", +"p@ c #191A19", +"q@ c #6B6F6F", +"r@ c #A2A7A9", +"s@ c #93989B", +"t@ c #9FA2A5", +"u@ c #A2A4A6", +"v@ c #ADAEB0", +"w@ c #BDBEC0", +"x@ c #C0C1C4", +"y@ c #CDCED0", +"z@ c #CECFD1", +"A@ c #D5D6D8", +"B@ c #D4D5D7", +"C@ c #CECFD0", +"D@ c #BFC0C2", +"E@ c #B5B6B7", +"F@ c #B1B2B5", +"G@ c #A8A9AD", +"H@ c #A0A3A7", +"I@ c #9A9EA1", +"J@ c #979A9D", +"K@ c #939498", +"L@ c #8A8B8B", +"M@ c #666767", +"N@ c #282828", +"O@ c #1C1C1E", +"P@ c #49494A", +"Q@ c #494949", +"R@ c #454548", +"S@ c #4F4F51", +"T@ c #4B4B4B", +"U@ c #3D3E3D", +"V@ c #555555", +"W@ c #434148", +"X@ c #2D2633", +"Y@ c #1B1327", +"Z@ c #201732", +"`@ c #120C1F", +" # c #969599", +".# c #C2C2C2", +"+# c #9A999D", +"@# c #393A3D", +"## c #303134", +"$# c #424346", +"%# c #45464A", +"&# c #4C4E52", +"*# c #505257", +"=# c #5B5E63", +"-# c #686C70", +";# c #636368", +"># c #0B0B0C", +",# c #484848", +"'# c #A1A2A3", +")# c #969799", +"!# c #9E9EA0", +"~# c #A1A2A4", +"{# c #A3A4A6", +"]# c #CBCCCE", +"^# c #CED0CF", +"/# c #D0D1D1", +"(# c #C3C4C4", +"_# c #C7C9CB", +":# c #BABBBF", +"<# c #B7B8BD", +"[# c #B0B1B4", +"}# c #ABACB0", +"|# c #A5A6AA", +"1# c #9C9FA4", +"2# c #93969B", +"3# c #898A8E", +"4# c #78797A", +"5# c #636365", +"6# c #070708", +"7# c #303030", +"8# c #4C4C4C", +"9# c #444445", +"0# c #3A3A3C", +"a# c #343331", +"b# c #5D5A59", +"c# c #585256", +"d# c #21162B", +"e# c #22162D", +"f# c #24182F", +"g# c #261A32", +"h# c #25192F", +"i# c #2D2531", +"j# c #AEAEAE", +"k# c #737373", +"l# c #252525", +"m# c #151515", +"n# c #585859", +"o# c #2C2C2E", +"p# c #16151A", +"q# c #424347", +"r# c #404146", +"s# c #46474B", +"t# c #494A4E", +"u# c #4F5155", +"v# c #55595E", +"w# c #717379", +"x# c #797B7E", +"y# c #2C2D2E", +"z# c #79797B", +"A# c #9B9B9D", +"B# c #9F9FA2", +"C# c #A0A1A3", +"D# c #BABBBD", +"E# c #C3C4C6", +"F# c #C9CBCC", +"G# c #C3C4C5", +"H# c #BABBBE", +"I# c #B9BABF", +"J# c #B3B4B8", +"K# c #AFB0B4", +"L# c #A7A8AC", +"M# c #9FA0A4", +"N# c #979A9F", +"O# c #919398", +"P# c #8E8F94", +"Q# c #636467", +"R# c #606162", +"S# c #5D5D5E", +"T# c #4C4C4B", +"U# c #0C0C0C", +"V# c #3D3D3C", +"W# c #333437", +"X# c #444248", +"Y# c #54505B", +"Z# c #2A2336", +"`# c #170B27", +" $ c #291C37", +".$ c #2A1D34", +"+$ c #281D32", +"@$ c #2A1E33", +"#$ c #251B2D", +"$$ c #4B454F", +"%$ c #5B5C5C", +"&$ c #696969", +"*$ c #000000", +"=$ c #0C0C0D", +"-$ c #161717", +";$ c #121214", +">$ c #38373B", +",$ c #35363A", +"'$ c #37383B", +")$ c #414247", +"!$ c #434448", +"~$ c #47484B", +"{$ c #4B4D52", +"]$ c #525559", +"^$ c #595C60", +"/$ c #62656B", +"($ c #6D6F74", +"_$ c #7A7E81", +":$ c #29282A", +"<$ c #48484A", +"[$ c #949597", +"}$ c #9FA0A2", +"|$ c #AAABAD", +"1$ c #BBBCBE", +"2$ c #BCBDC0", +"3$ c #B9B9BD", +"4$ c #B4B5B9", +"5$ c #B0B1B5", +"6$ c #ACADB1", +"7$ c #A6A7AB", +"8$ c #909297", +"9$ c #8D9196", +"0$ c #8F9296", +"a$ c #9D9EA2", +"b$ c #595A5C", +"c$ c #565655", +"d$ c #484846", +"e$ c #50504F", +"f$ c #161616", +"g$ c #353535", +"h$ c #3A3C3D", +"i$ c #2E263B", +"j$ c #221734", +"k$ c #2A1E38", +"l$ c #2E213A", +"m$ c #2E203A", +"n$ c #2E2139", +"o$ c #2D2136", +"p$ c #2E2236", +"q$ c #291F30", +"r$ c #69656D", +"s$ c #CDCDCE", +"t$ c #727374", +"u$ c #59595A", +"v$ c #191C1D", +"w$ c #131518", +"x$ c #131417", +"y$ c #151619", +"z$ c #121314", +"A$ c #0D0E10", +"B$ c #0B0C0E", +"C$ c #252629", +"D$ c #1F2021", +"E$ c #0F1010", +"F$ c #323337", +"G$ c #3A3B3F", +"H$ c #343539", +"I$ c #444547", +"J$ c #464749", +"K$ c #4B4C50", +"L$ c #505155", +"M$ c #58595D", +"N$ c #5D6064", +"O$ c #65696C", +"P$ c #696D71", +"Q$ c #757779", +"R$ c #6F6F71", +"S$ c #343537", +"T$ c #232324", +"U$ c #767675", +"V$ c #A7A8A9", +"W$ c #B2B3B7", +"X$ c #B9BABC", +"Y$ c #B8B9BB", +"Z$ c #B1B2B4", +"`$ c #A7A8AB", +" % c #A1A2A6", +".% c #97989C", +"+% c #8E8F93", +"@% c #9DA1A4", +"#% c #919496", +"$% c #6E6E6F", +"%% c #575757", +"&% c #373636", +"*% c #3A3B3B", +"=% c #111214", +"-% c #3B3C3E", +";% c #3A3C3E", +">% c #323036", +",% c #30223D", +"'% c #30233C", +")% c #31243E", +"!% c #32243E", +"~% c #31253D", +"{% c #31253B", +"]% c #32263A", +"^% c #34273C", +"/% c #33283A", +"(% c #747479", +"_% c #8B8A8C", +":% c #504F52", +"<% c #232427", +"[% c #1B1B1E", +"}% c #242528", +"|% c #1B1E21", +"1% c #17191D", +"2% c #16171C", +"3% c #0A0B0D", +"4% c #161719", +"5% c #1B1D1E", +"6% c #070908", +"7% c #252627", +"8% c #393A3F", +"9% c #16161A", +"0% c #444548", +"a% c #4A4B4D", +"b% c #4D4E52", +"c% c #525357", +"d% c #5B5D60", +"e% c #5F6366", +"f% c #64686A", +"g% c #68696B", +"h% c #717274", +"i% c #727375", +"j% c #585858", +"k% c #1A1A1A", +"l% c #A7A7AB", +"m% c #B3B4B7", +"n% c #B6B7B9", +"o% c #B3B4B6", +"p% c #B4B5B7", +"q% c #A9AAAC", +"r% c #AFB0B2", +"s% c #A4A5A8", +"t% c #B6B7BA", +"u% c #8A8B8D", +"v% c #5E5F61", +"w% c #494A4B", +"x% c #3D3E41", +"y% c #3B3D3F", +"z% c #34303B", +"A% c #362843", +"B% c #352841", +"C% c #362942", +"D% c #372A42", +"E% c #372B40", +"F% c #372C40", +"G% c #362A40", +"H% c #423847", +"I% c #6E6E71", +"J% c #464645", +"K% c #353539", +"L% c #343338", +"M% c #2B2B30", +"N% c #2D2E33", +"O% c #2C2D32", +"P% c #16161B", +"Q% c #1B1A20", +"R% c #1A1C21", +"S% c #15171D", +"T% c #0F1014", +"U% c #060709", +"V% c #191A1D", +"W% c #2C2D30", +"X% c #080808", +"Y% c #1A1B1A", +"Z% c #444549", +"`% c #414246", +" & c #515254", +".& c #494A4C", +"+& c #4E4F53", +"@& c #575B5E", +"#& c #5E6265", +"$& c #5E6062", +"%& c #808083", +"&& c #99999A", +"*& c #9F9F9F", +"=& c #202225", +"-& c #949598", +";& c #AAABAF", +">& c #949698", +",& c #202021", +"'& c #A3A4A7", +")& c #96979B", +"!& c #3E3E3E", +"~& c #2B2B2D", +"{& c #353142", +"]& c #392B47", +"^& c #3A2C46", +"/& c #3A2D47", +"(& c #3A2E47", +"_& c #3B2F47", +":& c #3C3045", +"<& c #3C3145", +"[& c #3A2D44", +"}& c #4A414D", +"|& c #A3A5A3", +"1& c #939298", +"2& c #212526", +"3& c #2B2C2B", +"4& c #3C3A3C", +"5& c #787982", +"6& c #30343B", +"7& c #22262D", +"8& c #24282E", +"9& c #1D2125", +"0& c #171A1F", +"a& c #191A1E", +"b& c #15161A", +"c& c #0D0D11", +"d& c #131517", +"e& c #1E1F21", +"f& c #313236", +"g& c #2A2A2F", +"h& c #3F4044", +"i& c #58595E", +"j& c #404145", +"k& c #505152", +"l& c #535357", +"m& c #5A5B5F", +"n& c #646368", +"o& c #767779", +"p& c #8F9092", +"q& c #919294", +"r& c #87888A", +"s& c #868789", +"t& c #A5A6A8", +"u& c #4C4D50", +"v& c #1A1C1C", +"w& c #6E7070", +"x& c #949496", +"y& c #A7A7A9", +"z& c #B9BCBF", +"A& c #A6AAAD", +"B& c #7A7D81", +"C& c #1E1E1E", +"D& c #2A2929", +"E& c #262726", +"F& c #2A2C2B", +"G& c #353635", +"H& c #3B3B3A", +"I& c #393A3A", +"J& c #3A3043", +"K& c #3D314B", +"L& c #3F324D", +"M& c #40334C", +"N& c #41344A", +"O& c #403349", +"P& c #423648", +"Q& c #413546", +"R& c #45394D", +"S& c #433F4B", +"T& c #E0DFE1", +"U& c #131413", +"V& c #484947", +"W& c #222326", +"X& c #252426", +"Y& c #5F6267", +"Z& c #6F7278", +"`& c #373A41", +" * c #181B20", +".* c #1E2126", +"+* c #191B20", +"@* c #14151A", +"#* c #141519", +"$* c #18191D", +"%* c #0E0F0F", +"&* c #111212", +"** c #1B1C1F", +"=* c #2D2F32", +"-* c #36373B", +";* c #3C3D41", +">* c #47484C", +",* c #4B4D4E", +"'* c #46484A", +")* c #65666A", +"!* c #717276", +"~* c #808185", +"{* c #818284", +"]* c #7E7F81", +"^* c #7A7B7D", +"/* c #808183", +"(* c #86888A", +"_* c #303131", +":* c #616365", +"<* c #141414", +"[* c #131313", +"}* c #101010", +"|* c #171817", +"1* c #252826", +"2* c #373938", +"3* c #434444", +"4* c #3C3A3F", +"5* c #3E3249", +"6* c #463751", +"7* c #453652", +"8* c #463852", +"9* c #473850", +"0* c #48394F", +"a* c #483A4E", +"b* c #47394B", +"c* c #493D4E", +"d* c #39343E", +"e* c #3A393D", +"f* c #212325", +"g* c #201F21", +"h* c #323438", +"i* c #4A4D53", +"j* c #6B6D74", +"k* c #424449", +"l* c #14171C", +"m* c #171A1E", +"n* c #1B1D21", +"o* c #111416", +"p* c #0F0F14", +"q* c #1C1D21", +"r* c #191A1C", +"s* c #303135", +"t* c #434447", +"u* c #48494B", +"v* c #48494C", +"w* c #7F8081", +"x* c #6F7074", +"y* c #76777B", +"z* c #727377", +"A* c #737476", +"B* c #747577", +"C* c #78797B", +"D* c #7A7A7C", +"E* c #868788", +"F* c #9C9D9F", +"G* c #7E8081", +"H* c #161617", +"I* c #0A0B0B", +"J* c #131314", +"K* c #121211", +"L* c #0B0B0B", +"M* c #0D0D0D", +"N* c #0F0F0F", +"O* c #111111", +"P* c #131412", +"Q* c #1B1C1B", +"R* c #262727", +"S* c #2D2D2D", +"T* c #1D1E1E", +"U* c #434443", +"V* c #3E3F3F", +"W* c #3B393F", +"X* c #433551", +"Y* c #4F3D59", +"Z* c #4E3D59", +"`* c #4D3D58", +" = c #4D3D55", +".= c #4E3E55", +"+= c #4D3F54", +"@= c #4C3F52", +"#= c #3F3443", +"$= c #868188", +"%= c #4E4D4F", +"&= c #212125", +"*= c #212222", +"== c #101012", +"-= c #616368", +";= c #484B50", +">= c #191B1F", +",= c #121217", +"'= c #1B1E22", +")= c #15151A", +"!= c #0A0B0F", +"~= c #1E1F23", +"{= c #292A2E", +"]= c #2D2E32", +"^= c #38393A", +"/= c #39393B", +"(= c #3F4042", +"_= c #4F5051", +":= c #6C6C6D", +"<= c #9C9C9D", +"[= c #696B6D", +"}= c #6B6C70", +"|= c #6B6C6F", +"1= c #696A6C", +"2= c #747473", +"3= c #898989", +"4= c #858586", +"5= c #565858", +"6= c #343636", +"7= c #131312", +"8= c #040404", +"9= c #121312", +"0= c #111110", +"a= c #5E5E5D", +"b= c #444545", +"c= c #1C1C1C", +"d= c #323433", +"e= c #3E403F", +"f= c #3E403E", +"g= c #403B43", +"h= c #4F3F5E", +"i= c #574463", +"j= c #554560", +"k= c #55445E", +"l= c #55445D", +"m= c #53455B", +"n= c #524458", +"o= c #544557", +"p= c #322A37", +"q= c #DDDBDD", +"r= c #201F24", +"s= c #1D1F1E", +"t= c #292A2D", +"u= c #292A2F", +"v= c #2D2D32", +"w= c #26282D", +"x= c #595C61", +"y= c #474A4F", +"z= c #181A1F", +"A= c #0B0B10", +"B= c #212327", +"C= c #25262A", +"D= c #28292D", +"E= c #2E2F33", +"F= c #2A2C2C", +"G= c #6B6C6B", +"H= c #525255", +"I= c #4F5153", +"J= c #55585B", +"K= c #56595C", +"L= c #5C5D61", +"M= c #616266", +"N= c #5D5E62", +"O= c #646568", +"P= c #6E6F70", +"Q= c #797A7A", +"R= c #171917", +"S= c #131512", +"T= c #0C0D0B", +"U= c #030303", +"V= c #11110F", +"W= c #0F0F10", +"X= c #1B1B1B", +"Y= c #3D3F3E", +"Z= c #413949", +"`= c #624E74", +" - c #5F4D6B", +".- c #604D69", +"+- c #614E69", +"@- c #604D68", +"#- c #5D4B64", +"$- c #594A5A", +"%- c #625661", +"&- c #4A494C", +"*- c #2B2A2C", +"=- c #1A1A1D", +"-- c #1F2121", +";- c #1A1B1D", +">- c #38393C", +",- c #26272B", +"'- c #1E2025", +")- c #46484D", +"!- c #46494D", +"~- c #1C1C21", +"{- c #171B1F", +"]- c #1D1F23", +"^- c #202124", +"/- c #232429", +"(- c #232428", +"_- c #28282D", +":- c #444645", +"<- c #595A59", +"[- c #37383A", +"}- c #4B4D4F", +"|- c #4F5255", +"1- c #525558", +"2- c #57595D", +"3- c #5B5C60", +"4- c #5B5C5D", +"5- c #555557", +"6- c #363738", +"7- c #090909", +"8- c #0E0E0E", +"9- c #161815", +"0- c #121412", +"a- c #131414", +"b- c #090809", +"c- c #0A0A0A", +"d- c #010101", +"e- c #020202", +"f- c #060606", +"g- c #292A2A", +"h- c #1F1F20", +"i- c #3C3D3D", +"j- c #3F413F", +"k- c #4F425C", +"l- c #725C83", +"m- c #6C5878", +"n- c #6D5875", +"o- c #6D5873", +"p- c #6A5870", +"q- c #67596B", +"r- c #5A555E", +"s- c #414346", +"t- c #999A9C", +"u- c #191919", +"v- c #1E2020", +"w- c #151719", +"x- c #282A2E", +"y- c #242428", +"z- c #2C2D31", +"A- c #38383D", +"B- c #24292D", +"C- c #1D1F22", +"D- c #1D1E22", +"E- c #27282B", +"F- c #2B2B2E", +"G- c #333435", +"H- c #353638", +"I- c #404143", +"J- c #434446", +"K- c #47484A", +"L- c #4C4D4F", +"M- c #565759", +"N- c #545558", +"O- c #404043", +"P- c #1F1F1F", +"Q- c #0F100F", +"R- c #1F2122", +"S- c #222421", +"T- c #1F2226", +"U- c #191A1A", +"V- c #161818", +"W- c #070807", +"X- c #171718", +"Y- c #16171A", +"Z- c #18181B", +"`- c #373736", +" ; c #3B3B3D", +".; c #69577A", +"+; c #80668C", +"@; c #806489", +"#; c #7E6883", +"$; c #736576", +"%; c #5E575F", +"&; c #4D4C4C", +"*; c #5B5B59", +"=; c #292929", +"-; c #1A1919", +";; c #1C1E1D", +">; c #151518", +",; c #27282C", +"'; c #2A2B2F", +"); c #28292C", +"!; c #202023", +"~; c #2C2C2F", +"{; c #303234", +"]; c #1F2024", +"^; c #373939", +"/; c #424444", +"(; c #212323", +"_; c #313234", +":; c #38393B", +"<; c #424345", +"[; c #2F3131", +"}; c #0B0C0C", +"|; c #252425", +"1; c #2E302C", +"2; c #333431", +"3; c #3D363E", +"4; c #232726", +"5; c #242625", +"6; c #202321", +"7; c #1E1F1F", +"8; c #070707", +"9; c #050505", +"0; c #211F22", +"a; c #282A2D", +"b; c #37353C", +"c; c #8E779D", +"d; c #8D7496", +"e; c #806F82", +"f; c #645E66", +"g; c #514C4D", +"h; c #5F5C58", +"i; c #7F7F78", +"j; c #191B1A", +"k; c #191C1B", +"l; c #18191B", +"m; c #323338", +"n; c #222328", +"o; c #2B2C2E", +"p; c #363639", +"q; c #2D3134", +"r; c #212528", +"s; c #222225", +"t; c #1A1C1A", +"u; c #282A2C", +"v; c #1C1C1F", +"w; c #202223", +"x; c #282A2B", +"y; c #272829", +"z; c #393A3C", +"A; c #46474A", +"B; c #343536", +"C; c #1B1B1C", +"D; c #111312", +"E; c #202024", +"F; c #222321", +"G; c #2B2D28", +"H; c #44433D", +"I; c #37313F", +"J; c #3A2E50", +"K; c #37294B", +"L; c #2C2E2C", +"M; c #282A29", +"N; c #262827", +"O; c #232425", +"P; c #4C4D4D", +"Q; c #383938", +"R; c #5B595D", +"S; c #908198", +"T; c #6A606D", +"U; c #504E4F", +"V; c #5B5B58", +"W; c #807F7B", +"X; c #161817", +"Y; c #151618", +"Z; c #2B2C2F", +"`; c #36383C", +" > c #1D1E21", +".> c #2A2C2F", +"+> c #2B2F33", +"@> c #2C2F32", +"#> c #1B1C20", +"$> c #17181A", +"%> c #1B1C1E", +"&> c #222325", +"*> c #2A2A2C", +"=> c #0B0C0D", +"-> c #030404", +";> c #2B2C2C", +">> c #1F1F17", +",> c #413F3F", +"'> c #423E4A", +")> c #261C3E", +"!> c #322450", +"~> c #362554", +"{> c #2F2345", +"]> c #323430", +"^> c #313232", +"/> c #303231", +"(> c #2B2D2C", +"_> c #606061", +":> c #787878", +"<> c #323231", +"[> c #1B1A1A", +"}> c #3F403C", +"|> c #817A7A", +"1> c #ABA9A8", +"2> c #B9B9B8", +"3> c #1F1D1B", +"4> c #171719", +"5> c #1A1C1B", +"6> c #0E0E10", +"7> c #212225", +"8> c #252628", +"9> c #3A3B40", +"0> c #23262A", +"a> c #313237", +"b> c #303237", +"c> c #202025", +"d> c #1D1E20", +"e> c #121315", +"f> c #2A2B2D", +"g> c #232325", +"h> c #09090C", +"i> c #030406", +"j> c #141518", +"k> c #2B2C2D", +"l> c #302E32", +"m> c #423C4E", +"n> c #281D42", +"o> c #281947", +"p> c #33264D", +"q> c #3B2B55", +"r> c #372A4A", +"s> c #32342E", +"t> c #333533", +"u> c #363837", +"v> c #2F302F", +"w> c #2A2A2A", +"x> c #313131", +"y> c #282825", +"z> c #3F3F40", +"A> c #30322D", +"B> c #52514F", +"C> c #444446", +"D> c #9B9C9E", +"E> c #838486", +"F> c #161615", +"G> c #1A1A19", +"H> c #171717", +"I> c #34373A", +"J> c #2D3033", +"K> c #2D2F33", +"L> c #1E201E", +"M> c #191B1B", +"N> c #181919", +"O> c #151514", +"P> c #1B1B1A", +"Q> c #29292B", +"R> c #292727", +"S> c #353237", +"T> c #2E2B40", +"U> c #271D42", +"V> c #251742", +"W> c #2F1F4A", +"X> c #312249", +"Y> c #35264F", +"Z> c #3A2B52", +"`> c #3E2C5C", +" , c #362D47", +"., c #353431", +"+, c #2E2E2E", +"@, c #232323", +"#, c #050504", +"$, c #060605", +"%, c #212121", +"&, c #232224", +"*, c #141514", +"=, c #353435", +"-, c #2F2F2D", +";, c #585A5F", +">, c #858789", +",, c #A4A5A5", +"', c #616161", +"), c #242424", +"!, c #141314", +"~, c #0F0E11", +"{, c #25252A", +"], c #26272C", +"^, c #303537", +"/, c #313538", +"(, c #26292C", +"_, c #212124", +":, c #212322", +"<, c #1B1D1C", +"[, c #191B19", +"}, c #181816", +"|, c #121210", +"1, c #010100", +"2, c #1A191C", +"3, c #2B2B29", +"4, c #2E2F2B", +"5, c #272431", +"6, c #2E2A40", +"7, c #1D1639", +"8, c #1F173F", +"9, c #281C45", +"0, c #2C1F47", +"a, c #30224A", +"b, c #32254D", +"c, c #382A52", +"d, c #3C2E57", +"e, c #433161", +"f, c #372F43", +"g, c #353533", +"h, c #020203", +"i, c #010102", +"j, c #1B1A1B", +"k, c #2C2C29", +"l, c #545651", +"m, c #5A5A58", +"n, c #484745", +"o, c #A7A7A7", +"p, c #1D1D1C", +"q, c #0D0C0F", +"r, c #1E1D22", +"s, c #191D20", +"t, c #303336", +"u, c #35373A", +"v, c #2F3033", +"w, c #242527", +"x, c #232524", +"y, c #1E1F20", +"z, c #1A1D1C", +"A, c #191A18", +"B, c #060604", +"C, c #29282F", +"D, c #201D2F", +"E, c #221D37", +"F, c #211B3C", +"G, c #1A163A", +"H, c #211A42", +"I, c #261C44", +"J, c #2C1E46", +"K, c #30214A", +"L, c #34254E", +"M, c #362851", +"N, c #3D2D57", +"O, c #42325C", +"P, c #493768", +"Q, c #37323F", +"R, c #363635", +"S, c #2B2B2C", +"T, c #323333", +"U, c #282929", +"V, c #1D1E1D", +"W, c #0C0A0A", +"X, c #1F1C1E", +"Y, c #3B3C3A", +"Z, c #383C43", +"`, c #2F2C2B", +" ' c #141615", +".' c #0A0A0B", +"+' c #28282B", +"@' c #242529", +"#' c #2A2C30", +"$' c #373B3E", +"%' c #191C1E", +"&' c #2E3034", +"*' c #232426", +"=' c #262729", +"-' c #202123", +";' c #1C1E1F", +">' c #131212", +",' c #080807", +"'' c #1E1E25", +")' c #1A1936", +"!' c #1C1736", +"~' c #1C1639", +"{' c #1E183D", +"]' c #1F1A3F", +"^' c #231C43", +"/' c #291B45", +"(' c #2E1F48", +"_' c #32234C", +":' c #372851", +"<' c #3C2C57", +"[' c #42305D", +"}' c #49345E", +"|' c #4F3A71", +"1' c #333039", +"2' c #383738", +"3' c #212422", +"4' c #272928", +"5' c #222423", +"6' c #2F3230", +"7' c #222221", +"8' c #020302", +"9' c #343947", +"0' c #1B2639", +"a' c #030000", +"b' c #353739", +"c' c #4F4F4D", +"d' c #050506", +"e' c #1C1B1F", +"f' c #1E1E22", +"g' c #292E2F", +"h' c #35393D", +"i' c #24252A", +"j' c #1F1F22", +"k' c #1D1D2E", +"l' c #181836", +"m' c #1B1733", +"n' c #1D1738", +"o' c #1F193E", +"p' c #21193F", +"q' c #271C44", +"r' c #2B1E47", +"s' c #31224B", +"t' c #392A53", +"u' c #3E2F5A", +"v' c #453360", +"w' c #4B3662", +"x' c #523F74", +"y' c #323034", +"z' c #0C0C0B", +"A' c #0D0C0C", +"B' c #35302F", +"C' c #31353E", +"D' c #3C7390", +"E' c #1B2126", +"F' c #070C07", +"G' c #303031", +"H' c #08090A", +"I' c #17181C", +"J' c #1F2222", +"K' c #1F2322", +"L' c #303338", +"M' c #24272B", +"N' c #27282A", +"O' c #212224", +"P' c #1E1E1F", +"Q' c #111011", +"R' c #1D1C2C", +"S' c #181739", +"T' c #1C1637", +"U' c #1D1739", +"V' c #1E193D", +"W' c #231841", +"X' c #2B1C45", +"Y' c #2E2048", +"Z' c #32244D", +"`' c #3C2C56", +" ) c #41325D", +".) c #483663", +"+) c #4F3A67", +"@) c #523F71", +"#) c #323230", +"$) c #0B0C0B", +"%) c #292B2A", +"&) c #2B2E2D", +"*) c #282B2A", +"=) c #2D2E2D", +"-) c #5B5B5B", +";) c #050606", +">) c #090807", +",) c #0B0A0C", +"') c #181514", +")) c #31373E", +"!) c #3E7497", +"~) c #85989C", +"{) c #272727", +"]) c #EBEBEB", +"^) c #1C1D1E", +"/) c #1F1F23", +"() c #35383C", +"_) c #353A3D", +":) c #22272A", +"<) c #111013", +"[) c #0F0D10", +"}) c #1A1B2F", +"|) c #1B163A", +"1) c #1C1738", +"2) c #1E183A", +"3) c #20173D", +"4) c #271B43", +"5) c #2C1D46", +"6) c #352550", +"7) c #392A56", +"8) c #3E2E5A", +"9) c #45345E", +"0) c #4C3A64", +"a) c #533D6B", +"b) c #4E3E65", +"c) c #323431", +"d) c #343634", +"e) c #2D2F2E", +"f) c #282A28", +"g) c #292A28", +"h) c #262A2B", +"i) c #3E3E3B", +"j) c #020201", +"k) c #0D0E0A", +"l) c #0D0E0E", +"m) c #090908", +"n) c #090708", +"o) c #58829C", +"p) c #A6BAC0", +"q) c #2E2C2C", +"r) c #565656", +"s) c #30302F", +"t) c #1C1D1F", +"u) c #1E1E20", +"v) c #191B1E", +"w) c #1F2225", +"x) c #262B2D", +"y) c #212629", +"z) c #27272B", +"A) c #121013", +"B) c #181A2C", +"C) c #1B173B", +"D) c #20173A", +"E) c #23183D", +"F) c #2A1C45", +"G) c #30234B", +"H) c #362751", +"I) c #3B2D55", +"J) c #403159", +"K) c #46365F", +"L) c #4E3B66", +"M) c #57426D", +"N) c #49395D", +"O) c #333430", +"P) c #2F3130", +"Q) c #151717", +"R) c #2D2F2F", +"S) c #546594", +"T) c #586A98", +"U) c #212221", +"V) c #020101", +"W) c #0B0C0A", +"X) c #140F08", +"Y) c #091015", +"Z) c #221E1D", +"`) c #5A829D", +" ! c #96BDCE", +".! c #565658", +"+! c #292A2C", +"@! c #404141", +"#! c #595959", +"$! c #676767", +"%! c #6D6D6D", +"&! c #6C6C6C", +"*! c #626262", +"=! c #525252", +"-! c #1F1F1E", +";! c #1A1C1E", +">! c #2B2D31", +",! c #2E3033", +"'! c #25272B", +")! c #24272A", +"!! c #0E0E0F", +"~! c #1A182D", +"{! c #1E1739", +"]! c #1F1537", +"^! c #23163C", +"/! c #27183F", +"(! c #2C1C46", +"_! c #2F214A", +":! c #392A51", +"~ c #47375E", +",~ c #4B3B61", +"'~ c #533F65", +")~ c #635078", +"!~ c #342D3B", +"~~ c #2D312D", +"{~ c #242623", +"]~ c #2D2D2C", +"^~ c #536593", +"/~ c #526493", +"(~ c #7786AD", +"_~ c #7D8BB1", +":~ c #5A6A97", +"<~ c #5B6C99", +"[~ c #5C6D9A", +"}~ c #536492", +"|~ c #576895", +"1~ c #42537B", +"2~ c #838383", +"3~ c #31302D", +"4~ c #18112C", +"5~ c #201539", +"6~ c #24163A", +"7~ c #28193F", +"8~ c #2C1D44", +"9~ c #2E2045", +"0~ c #392B4E", +"a~ c #3E2E51", +"b~ c #423158", +"c~ c #49385F", +"d~ c #523E66", +"e~ c #574763", +"f~ c #5C5764", +"g~ c #32342F", +"h~ c #272923", +"i~ c #556794", +"j~ c #5A80B3", +"k~ c #8591B3", +"l~ c #5C6C97", +"m~ c #566795", +"n~ c #60719E", +"o~ c #5E6F9B", +"p~ c #596A98", +"q~ c #556693", +"r~ c #6A7AA3", +"s~ c #6878A2", +"t~ c #546493", +"u~ c #717171", +"v~ c #757575", +"w~ c #828282", +"x~ c #666666", +"y~ c #32302F", +"z~ c #202221", +"A~ c #27292A", +"B~ c #25262B", +"C~ c #2B2B2F", +"D~ c #0B0D0D", +"E~ c #1A1B18", +"F~ c #190F31", +"G~ c #22153A", +"H~ c #25173A", +"I~ c #2A1C40", +"J~ c #2C1E42", +"K~ c #312248", +"L~ c #332549", +"M~ c #382A4D", +"N~ c #3C2A53", +"O~ c #493B5D", +"P~ c #4A4259", +"Q~ c #404142", +"R~ c #2C2F26", +"S~ c #556694", +"T~ c #7C8AAE", +"U~ c #7583A8", +"V~ c #526392", +"W~ c #8391B5", +"X~ c #A0ACC8", +"Y~ c #5B6C98", +"Z~ c #586895", +"`~ c #707070", +" { c #808080", +".{ c #5A5A5A", +"+{ c #27282D", +"@{ c #090B0B", +"#{ c #1B1C1A", +"${ c #1A1131", +"%{ c #21163A", +"&{ c #26173B", +"*{ c #2D1F43", +"={ c #312348", +"-{ c #34254B", +";{ c #3A2C51", +">{ c #463D59", +",{ c #3D3A44", +"'{ c #363735", +"){ c #434340", +"!{ c #5B6B99", +"~{ c #7F8EB3", +"{{ c #6D7DA5", +"]{ c #95A0BF", +"^{ c #7D8BB0", +"/{ c #5A6A98", +"({ c #516291", +"_{ c #546592", +":{ c #60709B", +"<{ c #7B89AF", +"[{ c #B8C1D7", +"}{ c #9EA9C5", +"|{ c #7886AD", +"1{ c #6B7AA3", +"2{ c #8B97B8", +"3{ c #727FA7", +"4{ c #5C5C5C", +"5{ c #727272", +"6{ c #7F7F7F", +"7{ c #3A3534", +"8{ c #222021", +"9{ c #1C1E23", +"0{ c #26272A", +"a{ c #090A0A", +"b{ c #1E201F", +"c{ c #191234", +"d{ c #23173A", +"e{ c #26193C", +"f{ c #291D3D", +"g{ c #2C1C41", +"h{ c #33244C", +"i{ c #3A314B", +"j{ c #3A373F", +"k{ c #343533", +"l{ c #4B4A49", +"m{ c #7784AC", +"n{ c #A1ACC8", +"o{ c #CBD3E5", +"p{ c #C5CEE2", +"q{ c #BEC8DE", +"r{ c #9FACCB", +"s{ c #7B8AB0", +"t{ c #5C6C98", +"u{ c #4E5F8E", +"v{ c #6C7AA3", +"w{ c #A8B2CB", +"x{ c #A3AEC8", +"y{ c #A7B1CB", +"z{ c #CFD6E5", +"A{ c #C9D0E1", +"B{ c #BBC4D9", +"C{ c #B0B9D2", +"D{ c #7E8CB0", +"E{ c #596A96", +"F{ c #526391", +"G{ c #5A6B98", +"H{ c #6E6E6E", +"I{ c #747474", +"J{ c #7B7B7B", +"K{ c #818181", +"L{ c #7D7D7D", +"M{ c #403D3D", +"N{ c #1E1D1E", +"O{ c #080A0B", +"P{ c #1C1E1E", +"Q{ c #1C1337", +"R{ c #23193B", +"S{ c #271A3C", +"T{ c #271A43", +"U{ c #322A49", +"V{ c #35363D", +"W{ c #2D2F30", +"X{ c #413F3D", +"Y{ c #707FA8", +"Z{ c #93A0C0", +"`{ c #A7B1CC", +" ] c #C7CFE0", +".] c #D4DBEA", +"+] c #D0D8E9", +"@] c #C9D1E5", +"#] c #BDC6DC", +"$] c #A6B1CB", +"%] c #8693B5", +"&] c #9EA8C5", +"*] c #BDC6DB", +"=] c #8794B6", +"-] c #8390B2", +";] c #B8C1D6", +">] c #C3CBDE", +",] c #B5BDD4", +"'] c #A0AAC6", +")] c #808FB4", +"!] c #7686AD", +"~] c #797979", +"{] c #423C39", +"]] c #1F1439", +"^] c #231839", +"/] c #261F3E", +"(] c #33323A", +"_] c #33342D", +":] c #31322F", +"<] c #54534B", +"[] c #6F7EA8", +"}] c #9BA8C6", +"|] c #AEB9D2", +"1] c #B1BBD5", +"2] c #D5DBEA", +"3] c #B2BCD2", +"4] c #9AA5C2", +"5] c #9EAAC5", +"6] c #95A1C0", +"7] c #CBD3E6", +"8] c #ADB8D4", +"9] c #6979A3", +"0] c #6978A2", +"a] c #7A88AE", +"b] c #8F9CBC", +"c] c #99A4C1", +"d] c #7080A7", +"e] c #8C99BA", +"f] c #8491B5", +"g] c #60719D", +"h] c #61729E", +"i] c #576794", +"j] c #4E5A7B", +"k] c #27292C", +"l] c #777777", +"m] c #1A1B1C", +"n] c #232328", +"o] c #19181F", +"p] c #241F40", +"q] c #292534", +"r] c #333331", +"s] c #2F322B", +"t] c #4E4A4B", +"u] c #7B8AB1", +"v] c #8B99BD", +"w] c #AFB9D2", +"x] c #B3BDD5", +"y] c #B8C2D9", +"z] c #AAB5D0", +"A] c #8B97B9", +"B] c #8D99BA", +"C] c #7785AB", +"D] c #BCC6DC", +"E] c #9AA6C9", +"F] c #A8B3D1", +"G] c #93A1C3", +"H] c #6E7DA7", +"I] c #8491B3", +"J] c #94A0BF", +"K] c #6777A1", +"L] c #586A97", +"M] c #5E6F9C", +"N] c #596894", +"O] c #546492", +"P] c #556591", +"Q] c #434B62", +"R] c #545454", +"S] c #101111", +"T] c #1F1E21", +"U] c #09090B", +"V] c #34332E", +"W] c #A3AECA", +"X] c #919EBF", +"Y] c #727FA8", +"Z] c #A2ADC7", +"`] c #B6BFD6", +" ^ c #C8CFE2", +".^ c #A8B3CE", +"+^ c #ACB5CE", +"@^ c #9BA7C5", +"#^ c #B9C2DA", +"$^ c #C2C9DE", +"%^ c #B0B9D4", +"&^ c #A9B2CE", +"*^ c #A9B4D0", +"=^ c #A9B5D2", +"-^ c #A4B1CD", +";^ c #A8B2CD", +">^ c #C9D0E3", +",^ c #95A2C3", +"'^ c #8390B5", +")^ c #6776A0", +"!^ c #7784AB", +"~^ c #8593B5", +"{^ c #5D6D9A", +"]^ c #5E6D99", +"^^ c #596896", +"/^ c #546187", +"(^ c #3F4557", +"_^ c #7A7A7A", +":^ c #5F5F5F", +"<^ c #444342", +"[^ c #464642", +"}^ c #8190B4", +"|^ c #97A3C3", +"1^ c #8A96BA", +"2^ c #7B88AD", +"3^ c #828FB3", +"4^ c #A5B0CA", +"5^ c #BDC5D9", +"6^ c #B1BBD4", +"7^ c #8F9CBB", +"8^ c #99A3C3", +"9^ c #D4DBEB", +"0^ c #DDE3EF", +"a^ c #D1D9E9", +"b^ c #D4DBEC", +"c^ c #C5CDE2", +"d^ c #ACB7D2", +"e^ c #A0ACCA", +"f^ c #BEC7DD", +"g^ c #C6CFE1", +"h^ c #B8C2DC", +"i^ c #B1BCD6", +"j^ c #919DBD", +"k^ c #6877A2", +"l^ c #566694", +"m^ c #5A6A96", +"n^ c #66749E", +"o^ c #566182", +"p^ c #3C4150", +"q^ c #464646", +"r^ c #62729F", +"s^ c #7686AE", +"t^ c #6B7AA5", +"u^ c #5F6F9A", +"v^ c #5D6D99", +"w^ c #8391B4", +"x^ c #97A4C3", +"y^ c #C8D0E1", +"z^ c #D3DAEB", +"A^ c #C2CBDD", +"B^ c #A8B2CC", +"C^ c #AAB5D1", +"D^ c #C0C9E0", +"E^ c #D6DDED", +"F^ c #BBC4DB", +"G^ c #B7C0D8", +"H^ c #C2CBE2", +"I^ c #C8D1E6", +"J^ c #97A3C2", +"K^ c #5C6D99", +"L^ c #5D6E9A", +"M^ c #576896", +"N^ c #586794", +"O^ c #828DAF", +"P^ c #68769C", +"Q^ c #464E63", +"R^ c #939FBF", +"S^ c #8D9BBE", +"T^ c #8693B6", +"U^ c #7785AA", +"V^ c #5E6E9A", +"W^ c #61709B", +"X^ c #7584AB", +"Y^ c #7281A8", +"Z^ c #BAC4DA", +"`^ c #C8D1E7", +" / c #ADB7D1", +"./ c #7B88AE", +"+/ c #6776A1", +"@/ c #7A88AF", +"#/ c #A2ADCB", +"$/ c #D6DCEB", +"%/ c #CAD3E5", +"&/ c #B9C3DA", +"*/ c #B3BCD6", +"=/ c #BAC3DB", +"-/ c #C5CDE1", +";/ c #B1BBD3", +">/ c #6A79A2", +",/ c #586A96", +"'/ c #5A6996", +")/ c #566693", +"!/ c #62709A", +"~/ c #7380A5", +"{/ c #4C597E", +"]/ c #303645", +"^/ c #151414", +"// c #1D1C1B", +"(/ c #8F9CBD", +"_/ c #94A0C0", +":/ c #818DB1", +"( c #201807", +",( c #261D09", +"'( c #251D09", +")( c #1F1807", +"!( c #171207", +"~( c #1A1815", +"{( c #5B6D99", +"]( c #7F8DB2", +"^( c #9EA9C8", +"/( c #8592B7", +"(( c #8491B6", +"_( c #6F7CA5", +":( c #7280A8", +"<( c #6B7AA4", +"[( c #C3CBE0", +"}( c #C9D1E3", +"|( c #D6DCEA", +"1( c #99A4C2", +"2( c #6D7CA5", +"3( c #63749F", +"4( c #6D7CA4", +"5( c #939EBE", +"6( c #A3AECC", +"7( c #A7B1CD", +"8( c #7785AC", +"9( c #7684AB", +"0( c #596997", +"a( c #586894", +"b( c #53638F", +"c( c #475270", +"d( c #2E3138", +"e( c #1B170D", +"f( c #251E09", +"g( c #2D230B", +"h( c #29200A", +"i( c #1E1806", +"j( c #140F07", +"k( c #1E1D1C", +"l( c #8B8B8B", +"m( c #868686", +"n( c #9CA7C4", +"o( c #8894B7", +"p( c #A5B0CC", +"q( c #8693B7", +"r( c #7F8DB0", +"s( c #7E8CAF", +"t( c #96A2C2", +"u( c #9DA8C6", +"v( c #CFD6E7", +"w( c #B5BED6", +"x( c #A8B1CD", +"y( c #B6BFD7", +"z( c #D5DBE9", +"A( c #C7CEE2", +"B( c #94A0C1", +"C( c #7A88AD", +"D( c #55648D", +"E( c #424B68", +"F( c #2A2C33", +"G( c #1E180D", +"H( c #2B220A", +"I( c #342A0D", +"J( c #34290D", +"K( c #30260C", +"L( c #2C230B", +"M( c #211A07", +"N( c #120C02", +"O( c #29292A", +"P( c #454646", +"Q( c #494B4F", +"R( c #4A4C51", +"S( c #5F6E9B", +"T( c #BAC2D7", +"U( c #BDC6D9", +"V( c #A3ADC7", +"W( c #AFBAD5", +"X( c #9CA7C6", +"Y( c #909CBB", +"Z( c #7886AB", +"`( c #9AA5C4", +" _ c #8D99BB", +"._ c #5E6D9A", +"+_ c #ACB6CF", +"@_ c #C6CEE3", +"#_ c #D8DEED", +"$_ c #D2D8EA", +"%_ c #E2E7F2", +"&_ c #D5DCEC", +"*_ c #C2CADF", +"=_ c #A7B2CE", +"-_ c #8592B8", +";_ c #6978A5", +">_ c #6D7CA7", +",_ c #6D7A9E", +"'_ c #4F576E", +")_ c #1F190B", +"!_ c #30250B", +"~_ c #3B2F0F", +"{_ c #3B2F10", +"]_ c #372C0E", +"^_ c #33290D", +"/_ c #30250C", +"(_ c #1C1608", +"__ c #1C1A17", +":_ c #373B46", +"<_ c #484A52", +"[_ c #464649", +"}_ c #363941", +"|_ c #5E6F9A", +"1_ c #9DA8C5", +"2_ c #AEB8D1", +"3_ c #B7C0D5", +"4_ c #ACB7D3", +"5_ c #7381A9", +"6_ c #6F7DA6", +"7_ c #8E9BBC", +"8_ c #6876A1", +"9_ c #7C89B0", +"0_ c #818FB5", +"a_ c #909DBE", +"b_ c #9CA7C5", +"c_ c #8F9BBC", +"d_ c #ADB7D2", +"e_ c #BCC6DE", +"f_ c #B2BCD5", +"g_ c #9EABC9", +"h_ c #5E6F9D", +"i_ c #6576A2", +"j_ c #6373A1", +"k_ c #58668F", +"l_ c #3D4660", +"m_ c #231C0C", +"n_ c #35290D", +"o_ c #403210", +"p_ c #403311", +"q_ c #3F3111", +"r_ c #382C0E", +"s_ c #30260B", +"t_ c #221A09", +"u_ c #171511", +"v_ c #383D4B", +"w_ c #5E6988", +"x_ c #767F96", +"y_ c #6C717D", +"z_ c #595C65", +"A_ c #575A61", +"B_ c #4D515B", +"C_ c #4E5362", +"D_ c #424652", +"E_ c #353A4C", +"F_ c #4A5678", +"G_ c #6978A3", +"H_ c #AEB9D3", +"I_ c #A5B1CF", +"J_ c #9FABC8", +"K_ c #98A4C2", +"L_ c #8B98BA", +"M_ c #9BA6C5", +"N_ c #A2ADC8", +"O_ c #9DA7C3", +"P_ c #6D7BA4", +"Q_ c #7382A9", +"R_ c #95A1C5", +"S_ c #95A1C3", +"T_ c #A4AFCD", +"U_ c #98A2C1", +"V_ c #7282AC", +"W_ c #7484B0", +"X_ c #6676A2", +"Y_ c #5E6C93", +"Z_ c #3C445A", +"`_ c #251D0B", +" : c #3A2D0D", +".: c #433511", +"+: c #423411", +"@: c #423511", +"#: c #3F3211", +"$: c #3A2D0F", +"%: c #18140C", +"&: c #2B2F3A", +"*: c #66718F", +"=: c #606F99", +"-: c #546393", +";: c #677499", +">: c #8790AB", +",: c #9EA5B9", +"': c #8690AC", +"): c #868FAE", +"!: c #6F799A", +"~: c #404554", +"{: c #383F54", +"]: c #576793", +"^: c #7F8DB3", +"/: c #7C8AB0", +"(: c #8A97BA", +"_: c #7080A9", +":: c #7382AA", +"<: c #9FAAC6", +"[: c #BAC3D9", +"}: c #97A2C1", +"|: c #7A87AD", +"1: c #A2ADCC", +"2: c #A0ABCB", +"3: c #9FAAC9", +"4: c #5A6B9B", +"5: c #6A7AA7", +"6: c #667499", +"7: c #384052", +"8: c #1C1B18", +"9: c #28200A", +"0: c #3D300F", +"a: c #463712", +"b: c #443612", +"c: c #453712", +"d: c #443511", +"e: c #413411", +"f: c #32280D", +"g: c #201A0F", +"h: c #2C2E38", +"i: c #6A738E", +"j: c #727FA5", +"k: c #6E7CA3", +"l: c #7B88AA", +"m: c #8A95B3", +"n: c #7D89AC", +"o: c #6C79A1", +"p: c #7684A8", +"q: c #64729C", +"r: c #747E99", +"s: c #40434E", +"t: c #5F6060", +"u: c #3A3C42", +"v: c #6576A0", +"w: c #6777A0", +"x: c #6F7EA6", +"y: c #8996BA", +"z: c #8A97BC", +"A: c #7C8AB1", +"B: c #97A2C3", +"C: c #8895B8", +"D: c #808DB3", +"E: c #8C99BB", +"F: c #99A4C4", +"G: c #939FC1", +"H: c #7B8BB2", +"I: c #BCC5DD", +"J: c #9CA9C8", +"K: c #6B7CA9", +"L: c #6E7FAB", +"M: c #697698", +"N: c #363B4A", +"O: c #1B1A13", +"P: c #403310", +"Q: c #483813", +"R: c #473813", +"S: c #463913", +"T: c #463813", +"U: c #433612", +"V: c #362A0D", +"W: c #221C0C", +"X: c #25282E", +"Y: c #495472", +"Z: c #68769E", +"`: c #717EA5", +" < c #9BA4BE", +".< c #8B96B4", +"+< c #6E7AA1", +"@< c #536393", +"#< c #6C7AA2", +"$< c #6D7BA2", +"%< c #62719B", +"&< c #7F8CAD", +"*< c #4A5062", +"=< c #7F7F80", +"-< c #4B4C4F", +";< c #596895", +">< c #5D6D98", +",< c #586995", +"'< c #5B6B97", +")< c #6575A0", +"!< c #6F7EA7", +"~< c #919EBE", +"{< c #B0BAD4", +"]< c #7987AE", +"^< c #7383AA", +"/< c #8E9BBE", +"(< c #919FC0", +"_< c #8E9BC0", +":< c #8C9ABE", +"<< c #6C7EAA", +"[< c #616D8D", +"}< c #343843", +"|< c #201C11", +"1< c #31270C", +"2< c #4A3B13", +"3< c #4B3B13", +"4< c #4A3A13", +"5< c #493A13", +"6< c #473913", +"7< c #392C0D", +"8< c #231C0B", +"9< c #25272C", +"0< c #566591", +"a< c #4F6190", +"b< c #63719C", +"c< c #8A95B4", +"d< c #7C88AA", +"e< c #586795", +"f< c #62719C", +"g< c #7A86AA", +"h< c #737EA4", +"i< c #7B88AB", +"j< c #7E89A8", +"k< c #444A5A", +"l< c #2A2B2C", +"m< c #51545D", +"n< c #5F6E99", +"o< c #5C6B98", +"p< c #5E6D98", +"q< c #5D6C98", +"r< c #606F9A", +"s< c #5A6A99", +"t< c #98A3C2", +"u< c #8492B5", +"v< c #8694B6", +"w< c #92A0C0", +"x< c #97A3C5", +"y< c #6F7FA9", +"z< c #8895BC", +"A< c #717FA7", +"B< c #55607F", +"C< c #353841", +"D< c #221E12", +"E< c #463812", +"F< c #4C3D15", +"G< c #4D3E15", +"H< c #4D3D14", +"I< c #4C3C13", +"J< c #4B3B14", +"K< c #493B13", +"L< c #3C2F0E", +"M< c #271E0C", +"N< c #26262A", +"O< c #404964", +"P< c #56658E", +"Q< c #808CAE", +"R< c #7784A8", +"S< c #67759E", +"T< c #6F7CA3", +"U< c #7783A8", +"V< c #8591B1", +"W< c #8E98B7", +"X< c #8D98B6", +"Y< c #96A0BC", +"Z< c #77819F", +"`< c #3F4451", +" [ c #272728", +".[ c #67686A", +"+[ c #4E5569", +"@[ c #5C6B96", +"#[ c #61719B", +"$[ c #5C6B97", +"%[ c #5F6E9A", +"&[ c #5E6C99", +"*[ c #6877A1", +"=[ c #6D7BA5", +"-[ c #61729D", +";[ c #7D8AB1", +">[ c #929FC0", +",[ c #7C8BB2", +"'[ c #7886A8", +")[ c #454F6C", +"![ c #2D2F37", +"~[ c #262010", +"{[ c #392E10", +"][ c #4A3B14", +"^[ c #4F3E15", +"/[ c #4F3F15", +"([ c #4E3E15", +"_[ c #4B3C14", +":[ c #40320F", +"<[ c #261F0B", +"[[ c #252427", +"}[ c #3E4761", +"|[ c #59678F", +"1[ c #ADB4CA", +"2[ c #B0B7CC", +"3[ c #8894B3", +"4[ c #7B89AC", +"5[ c #6F7DA4", +"6[ c #7B87AB", +"7[ c #9DA7C0", +"8[ c #9EA7C1", +"9[ c #98A3BC", +"0[ c #647297", +"a[ c #454C5F", +"b[ c #656667", +"c[ c #545966", +"d[ c #5B6B96", +"e[ c #5F6D98", +"f[ c #566593", +"g[ c #6778A4", +"h[ c #818FB6", +"i[ c #8D9ABC", +"j[ c #7D88A8", +"k[ c #4A536B", +"l[ c #28210F", +"m[ c #3E3210", +"n[ c #4F3F14", +"o[ c #504017", +"p[ c #514017", +"q[ c #514116", +"r[ c #514015", +"s[ c #443611", +"t[ c #2A200A", +"u[ c #252422", +"v[ c #3C465F", +"w[ c #51618C", +"x[ c #5B6B98", +"y[ c #7C88AB", +"z[ c #727EA5", +"A[ c #63739C", +"B[ c #576695", +"C[ c #5F6F99", +"D[ c #BDC3D4", +"E[ c #8F9AB8", +"F[ c #636C88", +"G[ c #3E424C", +"H[ c #5C5D5E", +"I[ c #535761", +"J[ c #515C7A", +"K[ c #5D6B97", +"L[ c #5F6E98", +"M[ c #5E6C98", +"N[ c #5C6A97", +"O[ c #5E6E99", +"P[ c #5E709D", +"Q[ c #697AA6", +"R[ c #6878A5", +"S[ c #5D6A92", +"T[ c #3F485E", +"U[ c #242423", +"V[ c #2A220D", +"W[ c #524215", +"X[ c #534216", +"Y[ c #544317", +"Z[ c #554217", +"`[ c #504016", +" } c #473812", +".} c #31270D", +"+} c #201D19", +"@} c #4B5064", +"#} c #657298", +"$} c #66759E", +"%} c #606E9A", +"&} c #7985AA", +"*} c #838EAF", +"=} c #717EA4", +"-} c #818DAD", +";} c #AFB7CC", +">} c #8490B1", +",} c #7E8AAD", +"'} c #9EA5BF", +")} c #676E83", +"!} c #353740", +"~} c #4D4E50", +"{} c #464A56", +"]} c #4A5574", +"^} c #505F8A", +"/} c #5D6C97", +"(} c #5B6A97", +"_} c #63729D", +":} c #5F6D9A", +"<} c #6979A4", +"[} c #7584AE", +"}} c #66749A", +"|} c #383E51", +"1} c #201E19", +"2} c #2F250C", +"3} c #483913", +"4} c #554417", +"5} c #564317", +"6} c #564417", +"7} c #574517", +"8} c #544316", +"9} c #524116", +"0} c #34290E", +"a} c #1F1C15", +"b} c #434858", +"c} c #67759F", +"d} c #6A779F", +"e} c #63729C", +"f} c #838FAF", +"g} c #7E8AAC", +"h} c #68779F", +"i} c #6A78A1", +"j} c #7885A9", +"k} c #8D97B5", +"l} c #A5ACC5", +"m} c #8A95B5", +"n} c #64739C", +"o} c #949EBB", +"p} c #B3BACD", +"q} c #9BA3B9", +"r} c #686E7D", +"s} c #363737", +"t} c #3C3E43", +"u} c #404659", +"v} c #4B587B", +"w} c #5C6A96", +"x} c #65739D", +"y} c #5E6D9B", +"z} c #707EA9", +"A} c #6C799A", +"B} c #343A48", +"C} c #1E1A11", +"D} c #362A0E", +"E} c #4C3C14", +"F} c #574617", +"G} c #584718", +"H} c #594718", +"I} c #584618", +"J} c #554317", +"K} c #4E3D14", +"L} c #211D15", +"M} c #3A4050", +"N} c #59678E", +"O} c #576693", +"P} c #5A6997", +"Q} c #616F9A", +"R} c #6A78A0", +"S} c #7481A5", +"T} c #6977A0", +"U} c #6E7CA2", +"V} c #A7AFC6", +"W} c #717FA5", +"X} c #9DA6C0", +"Y} c #B9C0D2", +"Z} c #9AA2B4", +"`} c #555C6C", +" | c #2F3137", +".| c #35363B", +"+| c #363A45", +"@| c #444C5E", +"#| c #4E5876", +"$| c #56648B", +"%| c #5B6995", +"&| c #62709C", +"*| c #64739B", +"=| c #545F7D", +"-| c #2D303C", +";| c #211B10", +">| c #3A2E0E", +",| c #584518", +"'| c #5A4818", +")| c #5B4818", +"!| c #5B4919", +"~| c #5A4819", +"{| c #574518", +"]| c #513F15", +"^| c #392D10", +"/| c #252218", +"(| c #383D4C", +"_| c #687492", +":| c #5A6995", +"<| c #576694", +"[| c #616F99", +"}| c #7986AA", +"|| c #6876A0", +"1| c #7986A9", +"2| c #9DA5C0", +"3| c #6C7AA1", +"4| c #64739D", +"5| c #A2ACC4", +"6| c #969FBC", +"7| c #8893B1", +"8| c #667498", +"9| c #77809B", +"0| c #687290", +"a| c #515E82", +"b| c #55658C", +"c| c #54648F", +"d| c #5B6A98", +"e| c #61729C", +"f| c #596792", +"g| c #48526D", +"h| c #2B2E34", +"i| c #251F10", +"j| c #3D3010", +"k| c #5C4919", +"l| c #5E4B19", +"m| c #5D4A19", +"n| c #544216", +"o| c #392D0F", +"p| c #282418", +"q| c #373B47", +"r| c #6A738F", +"s| c #67769F", +"t| c #546494", +"u| c #65749D", +"v| c #65739C", +"w| c #B5BBCF", +"x| c #7784A9", +"y| c #66749D", +"z| c #A6AFC7", +"A| c #A0A9C3", +"B| c #7581A6", +"C| c #8691B2", +"D| c #63729B", +"E| c #526189", +"F| c #424B63", +"G| c #292B2F", +"H| c #292211", +"I| c #5F4C1A", +"J| c #614D1A", +"K| c #604C19", +"L| c #5E4A19", +"M| c #5B4819", +"N| c #3E3110", +"O| c #262112", +"P| c #42444D", +"Q| c #727A93", +"R| c #7380A6", +"S| c #566594", +"T| c #62719A", +"U| c #5D6D97", +"V| c #8C97B5", +"W| c #ACB4C9", +"X| c #8591B2", +"Y| c #A1A9C3", +"Z| c #A1AAC2", +"`| c #7C87AA", +" 1 c #808BAD", +".1 c #5F6D99", +"+1 c #5A6A95", +"@1 c #566389", +"#1 c #3C455C", +"$1 c #2C2410", +"%1 c #463713", +"&1 c #5E4B1A", +"*1 c #604D1B", +"=1 c #624E1B", +"-1 c #634F1B", +";1 c #604C1A", +">1 c #5D4919", +",1 c #251F0F", +"'1 c #31343D", +")1 c #5F6A86", +"!1 c #808DAF", +"~1 c #606D99", +"{1 c #99A3BE", +"]1 c #6D7AA2", +"^1 c #A0A9C2", +"/1 c #8892B3", +"(1 c #98A2BD", +"_1 c #9DA6C1", +":1 c #737FA5", +"<1 c #5E6E98", +"[1 c #5A6B96", +"}1 c #5F6F98", +"|1 c #566287", +"11 c #3B4256", +"21 c #242320", +"31 c #2E250E", +"41 c #5C4918", +"51 c #634E1B", +"61 c #65511C", +"71 c #66511C", +"81 c #65501B", +"91 c #624F1A", +"01 c #5E4C1A", +"a1 c #241E0D", +"b1 c #292C33", +"c1 c #495576", +"d1 c #606F9B", +"e1 c #8490B2", +"f1 c #828EB0", +"g1 c #AEB5CB", +"h1 c #67759D", +"i1 c #7B86AA", +"j1 c #A9B1C8", +"k1 c #8792B2", +"l1 c #5B6996", +"m1 c #5C6C95", +"n1 c #556284", +"o1 c #323748", +"p1 c #1F1D16", +"q1 c #32290D", +"r1 c #4E3E13", +"s1 c #614E1B", +"t1 c #64501B", +"u1 c #68531C", +"v1 c #67521D", +"w1 c #67521C", +"x1 c #5B4918", +"y1 c #2A230E", +"z1 c #485375", +"A1 c #556492", +"B1 c #7985A9", +"C1 c #919BB8", +"D1 c #B1B8CD", +"E1 c #8A94B4", +"F1 c #8B95B5", +"G1 c #9EA8C0", +"H1 c #B2B9CD", +"I1 c #949EBA", +"J1 c #7280A5", +"K1 c #7A87AA", +"L1 c #707DA3", +"M1 c #5F709B", +"N1 c #2B2F3C", +"O1 c #1D1B0F", +"P1 c #5D4A18", +"Q1 c #66531C", +"R1 c #69551D", +"S1 c #6B561D", +"T1 c #69541D", +"U1 c #624F1B", +"V1 c #2E250F", +"W1 c #495370", +"X1 c #526390", +"Y1 c #6A79A1", +"Z1 c #97A1BD", +"`1 c #818DAE", +" 2 c #929CB9", +".2 c #8D97B6", +"+2 c #8893B3", +"@2 c #ABB3C9", +"#2 c #707EA5", +"$2 c #838EB0", +"%2 c #BEC5D5", +"&2 c #ACB3C9", +"*2 c #8F99B6", +"=2 c #6C79A0", +"-2 c #7683A8", +";2 c #7582A7", +">2 c #8B96B5", +",2 c #A8B1C6", +"'2 c #8B97B6", +")2 c #48526F", +"!2 c #242730", +"~2 c #1F190D", +"{2 c #6C571E", +"]2 c #6C581E", +"^2 c #6B571E", +"/2 c #64501C", +"(2 c #5F4B19", +"_2 c #4C3B13", +":2 c #2F260F", +"<2 c #272626", +"[2 c #4E566D", +"}2 c #50608C", +"|2 c #7E8BAD", +"12 c #818DAF", +"22 c #99A2BE", +"32 c #506191", +"42 c #8F99B7", +"52 c #9CA4BF", +"62 c #7582A6", +"72 c #8792B1", +"82 c #A8AFC6", +"92 c #C0C6D6", +"02 c #959FBB", +"a2 c #909AB8", +"b2 c #AAB1C8", +"c2 c #A2ABC3", +"d2 c #9EA7C0", +"e2 c #939DBA", +"f2 c #828EAF", +"g2 c #68779E", +"h2 c #AEB5CA", +"i2 c #A6ADC6", +"j2 c #161410", +"k2 c #211B0A", +"l2 c #3C2F0F", +"m2 c #6B551E", +"n2 c #6F581F", +"o2 c #70591F", +"p2 c #6E581E", +"q2 c #6A551D", +"r2 c #65501C", +"s2 c #31280F", +"t2 c #545B6F", +"u2 c #606E95", +"v2 c #516392", +"w2 c #8C97B6", +"x2 c #98A1BD", +"y2 c #7A86AB", +"z2 c #7F8BAE", +"A2 c #7D88AC", +"B2 c #8591B0", +"C2 c #7B87A9", +"D2 c #9BA4BF", +"E2 c #AEB6CA", +"F2 c #939CB9", +"G2 c #818CAE", +"H2 c #727FA4", +"I2 c #181511", +"J2 c #534215", +"K2 c #6D571E", +"L2 c #715B1F", +"M2 c #725C1F", +"N2 c #705A1F", +"O2 c #6C561E", +"P2 c #524115", +"Q2 c #332910", +"R2 c #272725", +"S2 c #3E465D", +"T2 c #828BA8", +"U2 c #7D89AD", +"V2 c #5F709A", +"W2 c #717FA4", +"X2 c #9AA4BF", +"Y2 c #9BA5BF", +"Z2 c #6D7AA1", +"`2 c #8994B3", +" 3 c #7F8BAD", +".3 c #6E7BA3", +"+3 c #7A87AB", +"@3 c #9FA7C1", +"#3 c #181611", +"$3 c #241D0D", +"%3 c #3B2E10", +"&3 c #67511C", +"*3 c #735C20", +"=3 c #755D20", +"-3 c #735C1F", +";3 c #69531D", +">3 c #554316", +",3 c #33280D", +"'3 c #252220", +")3 c #373F55", +"!3 c #536188", +"~3 c #6D7BA1", +"{3 c #7683A7", +"]3 c #616F9B", +"^3 c #7885AA", +"/3 c #909AB7", +"(3 c #707EA4", +"_3 c #7C89AB", +":3 c #9AA3BD", +"<3 c #C3C8D7", +"[3 c #99A3BD", +"}3 c #6B79A2", +"|3 c #8F99B8", +"13 c #201F1F", +"23 c #17140F", +"33 c #392D0E", +"43 c #504015", +"53 c #68521C", +"63 c #70581F", +"73 c #755E21", +"83 c #776021", +"93 c #634E1A", +"03 c #34280B", +"a3 c #1E1C17", +"b3 c #333B50", +"c3 c #4E5C83", +"d3 c #6F7DA2", +"e3 c #8590B1", +"f3 c #BFC5D5", +"g3 c #7381A6", +"h3 c #A4ADC4", +"i3 c #909BB9", +"j3 c #ADB5CA", +"k3 c #919DB9", +"l3 c #4F608F", +"m3 c #7D8AAC", +"n3 c #848FB0", +"o3 c #A1A9C2", +"p3 c #12100B", +"q3 c #1F1707", +"r3 c #382B0D", +"s3 c #67531C", +"t3 c #715A1F", +"u3 c #775F21", +"v3 c #796122", +"w3 c #776020", +"x3 c #725B1F", +"y3 c #6B551D", +"z3 c #624E1A", +"A3 c #3B2E0F", +"B3 c #19170D", +"C3 c #313644", +"D3 c #55638A", +"E3 c #526492", +"F3 c #6F7CA4", +"G3 c #9FA8C2", +"H3 c #0D0C07", +"I3 c #1A1406", +"J3 c #34290C", +"K3 c #493913", +"L3 c #594618", +"M3 c #796222", +"N3 c #7C6323", +"O3 c #796121", +"P3 c #725C20", +"Q3 c #524113", +"R3 c #362A0B", +"S3 c #262218", +"T3 c #555656", +"U3 c #64729D", +"V3 c #556592", +"W3 c #556493", +"X3 c #606F98", +"Y3 c #7884AA", +"Z3 c #B9C0D3", +"`3 c #A1AAC3", +" 4 c #929DB9", +".4 c #6978A0", +"+4 c #828FB0", +"@4 c #6B7AA1", +"#4 c #808DAE", +"$4 c #1D1C1D", +"%4 c #110F0C", +"&4 c #171205", +"*4 c #534316", +"=4 c #7B6322", +"-4 c #7E6523", +";4 c #7A6221", +">4 c #735B20", +",4 c #6A541D", +"'4 c #4E3C10", +")4 c #362A0C", +"!4 c #353126", +"~4 c #5A5B5A", +"{4 c #919BB7", +"]4 c #B8BED0", +"^4 c #CFD4E0", +"/4 c #8995B5", +"(4 c #B0B8CC", +"_4 c #98A1BC", +":4 c #536494", +"<4 c #100F0B", +"[4 c #141004", +"}4 c #251C08", +"|4 c #3A2D0E", +"14 c #7C6322", +"24 c #816623", +"34 c #7D6322", +"44 c #745D1F", +"54 c #69521D", +"64 c #5F4B1A", +"74 c #4D3C12", +"84 c #362B0F", +"94 c #2C271C", +"04 c #3C3B3A", +"a4 c #596A95", +"b4 c #838FB0", +"c4 c #808CAD", +"d4 c #939DB9", +"e4 c #7884A9", +"f4 c #7B87AA", +"g4 c #828DAE", +"h4 c #8993B3", +"i4 c #151513", +"j4 c #100E06", +"k4 c #1C1605", +"l4 c #433611", +"m4 c #6C571D", +"n4 c #7B6321", +"o4 c #816723", +"p4 c #67511B", +"q4 c #4A3B12", +"r4 c #2B2004", +"s4 c #363126", +"t4 c #626160", +"u4 c #7D88AB", +"v4 c #6E7BA2", +"w4 c #707CA3", +"x4 c #63719B", +"y4 c #939EBA", +"z4 c #7E89AB", +"A4 c #5C6C99", +"B4 c #100F0A", +"C4 c #251D08", +"D4 c #382C0F", +"E4 c #4D3C14", +"F4 c #614F1B", +"G4 c #755E20", +"H4 c #7E6522", +"I4 c #7B6121", +"J4 c #6F581E", +"K4 c #614E1A", +"L4 c #2D2309", +"M4 c #282419", +"N4 c #4C4B49", +"O4 c #919193", +"P4 c #66739D", +"Q4 c #6F7DA3", +"R4 c #7F8AAD", +"S4 c #707DA4", +"T4 c #7280A6", +"U4 c #67749D", +"V4 c #8E98B6", +"W4 c #0E0C06", +"X4 c #171204", +"Y4 c #534116", +"Z4 c #2C220A", +"`4 c #272319", +" 5 c #535452", +".5 c #848687", +"+5 c #727FA6", +"@5 c #6A789F", +"#5 c #7D89AB", +"$5 c #7481A6", +"%5 c #8C96B5", +"&5 c #6D7BA3", +"*5 c #110E09", +"=5 c #2E250B", +"-5 c #413410", +";5 c #4E3F15", +">5 c #352A0D", +",5 c #130F05", +"'5 c #7C87AC", +")5 c #96A0BB", +"!5 c #959FBA", +"~5 c #919BB9", +"{5 c #526393", +"]5 c #12110D", +"^5 c #181307", +"/5 c #483812", +"(5 c #413311", +"_5 c #372B0E", +":5 c #1C1505", +"<5 c #0F0E07", +"[5 c #7480A6", +"}5 c #919CB8", +"|5 c #7F8CAE", +"15 c #222323", +"25 c #14110B", +"35 c #201805", +"45 c #32270D", +"55 c #362B0E", +"65 c #31260C", +"75 c #211907", +"85 c #150F02", +"95 c #0C0A03", +"05 c #3C3C3D", +"a5 c #7C88AC", +"b5 c #8995B4", +"c5 c #6D7CA3", +"d5 c #737272", +"e5 c #454546", +"f5 c #1E1E1D", +"g5 c #090700", +"h5 c #100B00", +"i5 c #1A1303", +"j5 c #1D1504", +"k5 c #1B1404", +"l5 c #151002", +"m5 c #0C0800", +"n5 c #0C0A06", +"o5 c #232222", +"p5 c #383839", +"q5 c #302F2E", +"r5 c #1D1B19", +"s5 c #13110C", +"t5 c #13100A", +"u5 c #100D08", +"v5 c #0D0B07", +"w5 c #161613", +"x5 c #7B87AC", +"y5 c #6D6D6E", +"z5 c #5C5D5D", +" ", +" ", +" ", +" . + @ # $ ", +" % & * = - ; > - , ", +" ' ) ! ~ { ] ^ / ( _ : < [ ", +" } | 1 2 3 4 5 6 7 8 9 0 a b c d e ", +" f g h i j k l m n o 8 0 a p q r s t u ", +" v w x y z A B C D E F G m m ^ H I J K L M N O ", +" P Q $ R S T U V W X Y Z ` ...n +.@.#.$.%.&.*.=.-.;.>.,.'. ", +" ).!.~.{.P ].^./.(._.:.<.[.}.|.1. .2.3.4.5.6.7.8.9.0.a.4.b.c.d.e. ", +" f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.4.B.C.D.I 7.E.F.G.H.I.J.K.L.M. ", +" N.O.P.Q.R.S.T.l.U.V.R.W.X.Y.Z.`. +.+++@+#+` $+%+7 &+*+=+-+;+I 7.>+>+,+'+)+!+~+{+ ", +" ]+^+/+(+_+:+<+[+}+|+1+2+3+4+5+6+7+2+8+9+0+a+b+c+d+,.G +.3.e+f+g+h+i+j+k+l+m+n+o+o+p+q+ ", +" r+s+t+u+v+w+x+y+z+A+B+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+G B.W+X+Y+Z+`+ @.@+@@@#@$@%@&@ ", +" *@| =@-@;@>@,@'@)@!@~@{@]@^@/@(@_@:@<@[@}@|@1@1 2@3@4@5@6@7@8@9@0@^.+.B.b.g+a@G o+^.b@c@d@e@f@g@h@ ", +" i@j@k@l@m@n@o@p@q@r@s@w+t@u@v@w@x@y@z@A@B@C@y@D@E@F@G@H@I@J@K@L@M@N@O@P@Q@B.A.R@S@T@a.U@V@W@X@Y@Z@`@ # ", +" .# +#@###$#%#&#*#=#-#;#>#,#'#)#!#~#{#]@]#<@^#/#(#_#:#<#[#}#|#1#2#2#K@3#4#5#F.6#7#8#8#9#0#a#b#c#d#e#f#g#h#i# ", +" j#k#l#m#n#o#p#,+q#r#s#t#u#v#=#w#x#y#`+z#A#B#C#~#D#E#F#G#w@H#I#J#K#L#M#H@N#O#P#Q#R#S#d T#U#V#@#W#X#Y#Z#`# $.$+$@$#$$$ ", +" %$&$A.; *$=$-$>#;$>$,$'$)$!$~${$]$^$/$($_$d+:$<$[$}$!#|$1$2$2$3$4$5$6$7$L#M#8$9$0$a$b$n#c$d$e$f$g$h$$.i$j$k$l$m$n$o$p$q$r$ ", +" s$t$u$F.v$w$x$y$z$A$B$C$D$E$F$G$H$I$J$K$L$M$N$O$P$Q$R$S$T$U$V$G@W$D#X$Y$Z$v@`$ %.%+%@%~@#%$%%%- f+&%*%=%-%;%>%,%'%)%!%~%{%]%^%/%(% ", +" } _%:%,+<%[%}%|%1%2%=%3%4%5%6%7%8%9%,$0%$.a%a%b%c%d%e%f%g%h%i%j%k%9#l%J#m%n%o%p%q%r%s%7$t%u%v%w%3.C.7 F 7 L.q x%y%z%A%B%B%C%D%E%F%G%H%I% ", +" - J%K%L%M%N%O%P%Q%R%S%T%4%x$U%V%W%X%Y%Z%`%q# &;@.&+&c%@&^$#&$&Q#%&&&*&T$=&-&;&p%Z$>&P+,&'&)&$##./ F 4.g$g$@.!&k%~&8.;%{&]&^&/&(&_&:&<&[&}&|& ", +" 1&2&3&4&5&6&7&8&9&0&a&b&c&d&e&B$a&f&g&G$h&Z%s#i&j&@@k&l&m&n&o&p&q&r&s&t&u&v&w&x&y&z&A&B&%+C&D&E&F&G&@.@.@.4./ E$H&3.I&J&K&L&M&N&O&P&Q&R&S&T& ", +" U&V&W&X&Y&Z&`& *.*+*@*#*$*%*&*W&**=*,+-*;*`%>*K$i&,*'*)*!*~*{*]*^*/*]*(*.%4#_*=.x&:*#.D$<*[*}*|*1*2*U@4.8 g$@.}*3*B.4*5*6*7*8*9*0*a*b*c*d* ", +" l#e*f*g*h*i*j*k*l*m*n*o*p*q*r*r*s*f&s*E.d.4 t*u*v*%$w*x*y*z*h%A*B*C*D*E*)&F*G*H*I*J*K*L*M*N*O*P*Q*R*7 D @.7 S*T*U*V*W*X*Y*Z*`* =.=+=@=#=$= ", +" %=&=*===O%N%8%-=;=>=,='=)=!=~={=i+]=F$^=/=o#(=J$_=:=<=[=}=|=1=|=c+2=3=4=5=6=p 7=8=L*L*M*}*O*9=0=a=b=c=4.3.a.o d=e=f=g=h=i=j=k=l=m=n=o=p=q= ", +" Y+r=s=t=O%u=v=w=x=y=z=A=q*B=C=C=D=E=]=t=F=G=m+H=I=J=K=L=M=N=O=g%P=Q=a.E$R=S=T=X%U=M*U#N*N*; L*V=W=W=<*X=3.!&C&U@Y=e=Z=`= -.-+-@-#-$-%-&- ", +" *-=---;->-,-M%O%'-)-!-~-{-]-^-/-(-(-_-F$:-<-4#[-}-|-1-2-C 3-4-5-6-7-8-X=9-0-a-b-c-8-N*M*8-c-d-e-U=f-X%}*; g-h-f=i-j-k-l-m-n-o-p-q-r-s-t- ", +" S*u-v-w-F$>+x-y-z-(-A-q#B-C-D-**E-x-F-G-H-(=I-J-K-L- &M-N-O-P-Q-=%R-S-T-s=U-V-W-M*N*N*; 7-e-d-d-e-U=e-f-X-Y-Z-`-$# ;.;+;@;#;$;%;&;*; ", +" =;-;;;>;,;';,+);C=C=!;~;{;}%~=];^;/;(;_;H-:;8.<;.&L-I$[;V-};u-|;1;2;3;4;5;6;7;8;O*; ; M*8=d-d-*$d-e-e-9;T*L-8.0;a;b;c;d;e;f;g;h;i; ", +" F&j;k;l;m;n;o;p;q;r;s;,&t;u;^-v;w;x;y;;@7.z;m+A;B;C;N*D;E;F;G;H;I;J;K;L;F&M;N;f-m#; }*f-e-d-*$d-*$d-e-U=6#O;P;:%Q;R;S;T;U;V;W; ", +" L;X;j;Y;Z;`; >.>+>@>#>--t;$>%>Y;;-&>H ;+H-I-*>=>->m#;>`.>>,>'>)>!>~>{>]>^>/>(>X%c=f$X%7-8=d-d-d-*$*$e-e-e-f-N*_>:><>[>}>|>1>2> ", +" 3>4>5>6>7>8>9>0>a;a>b>c>;-d>$>4%e>d>f>o;g>h>i>j>k>8 m l>m>n>o>!>p>q>r>s>t>u>v>N*w>( U=c-e-d-e-d-*$d-e-U=8=X%x>y>=$z>A>E$B>C>D>E> ", +" F>G>H>s M%7>);I>J>r;K>s*v;L>M>N>O>P*K*8;8;P>Q>8 R>S>T>U>V>W>X>Y>Z>`> ,.,g$/ =;( +,@,<*8-8=d-d-e-d-e-U=U=f-; c=#,$,7-%,&,*,=,-,;,>,,, ", +" ',),!,~,{,D=];],^,/,(,C=_,:,<,[,},|,1,2,3,4,8>5,6,7,8,9,0,a,b,c,d,e,f,g,C.B.C&X=^ ^ m H>e-*$d-e-U=e-e-e-N*f$8=e-d-h,i,9=@.j,k,l,m,n, ", +" o,p,q,r,y-{==*s,t,u,v,w,x,y,z,A,K*B,0.C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,C.D H>P-S,m T,U,V,L**$8=U=U=U=8=( X%9;U=d-e-W,X,*$Y,Z,`, ' ", +" J%.'+'a&@'#'$'%'&'s**'='-';'c=>',''')'!'~'{']'^'/'('_':'<'['}'|'1'2'7 3.N*3'(>4'5'6'^>g$C&*$8=X%f-[*H>U=8=f-U=X%7'8'n,9'0'a'b' ", +" c'd'e'{=f'w;g'h'i'Z;*'='*'j'_ ; 7-k'l'm'n'o'p'q'r's'L,t'u'v'w'x'y'7 C.&+z'N;F&4'R*F&S*4.@.=;L*e-L*L*X%f-9;8=7-_ A'B'C'D'E'F'G' ", +" E H'v;I'';J'K'L'L'M'w,N'}%O'P'Q'L*R'S'T'U'V'W'X'Y'Z':'`' ).)+)@)#)@.4.^ $)3&(>%)&)*)=);>] -)k%8=8;M*z'f-;)>),)')))!)~)>'{)6 .G 3.F =;C&_ @,w>7# ", +" ])*+H*^)O@l;]=/)i'()_):)a;C=<%w;<)[)})|)1)2)3)4)5)K,6)7)8)9)0)a)b)c)d)F ),%*e)N;f)g)h)i) N*O*j)k)7-l)m)n)k+o)p)q)9 .r)j%-)j%..2.C.+,{){)8 4.a.4. ", +" s)X-t)u)v)]=';w)x)y)z)D=,-*'~,A)B)C)1)D)E)F)0,G)H)I)J)K)L)M)N)O)P)F P-Q)*)s=R) S)T) 4.U)V)W)X)Y)Z)`) !.!y,+!_;@!^.#!$!%!&!*!=!n &+8 F ,#+.g$7# ", +" -!Y%**X&;!>!,!'!)!D={={=C$!!!,~!{!]!^!/!(!_!L,:!&+G =!d m!|.n!o!/ c=F D > @,C&0 ", +" p!q!r!s!t!E=E=(-,;,;D=,;u!v!w!x!y!z!A!J,B!C!D!E!9)F!G!H!I!J!K!L!M!N! a!O!P!Q!R!S!T!U! V!W!X!Y! S*> Q@j%Z!$!o+> +.`! ~*!6 ^ l#w>+, ", +" .~m#p ';];C=C=C=,-D=';+~j;@~x!#~$~%~&~*~=~-~;~>~,~'~)~!~~~{~]~ ^~/~(~_~:~<~[~0!}~S!|~S)1~ {)F ,#n+^.6 - m!# 2~@ &$^.+.G @.^ ", +" 3~V,7><%^-W&,;D={=';B$Y%4~5~6~7~8~9~*~0~a~b~c~d~e~f~g~h~ }~i~j~k~l~m~n~o~p~q~r~s~S)t~ m @./ / - $ u~v~# w~# x~=!o!2.g$w> ", +" y~z~A~w,p B~,;{=C~D~E~F~G~H~I~J~K~L~M~N~O~P~'>Q~R~ S~m~|~T~U~V~T!R!0!o~W~X~Y~R!a!Z~R! =;9 +,n V@Z!, `~ ~@ {k#.{o!o!+.&+ ", +" e+O;d>D$}%],+{u=@{#{${%{&{I~*{={-{;{>{,{'{){ !{O!~{{{]{^{/{R!({_{:{<{[{}{|{1{2{3{a!m~ o l#7 ^.4{',Z!&!5{:> {6{&!o+T@T@!&x> ", +" 7{8{9{W&0{+!_-a{b{c{d{e{f{g{h{i{j{k{l{ m{n{o{p{q{r{s{t{u{v{w{x{y{z{A{B{C{D{E{F{c!S)G{ %,g$,#o+V@4{',$!H{I{J{K{L{',8#] - a.^ ", +" M{N{*'w,H t=O{P{Q{R{S{T{U{V{W{X{ Y{Z{`{ ].]+]@]#]$]%]&]*]=]-];]>],]']_~)]!]T!b!p~}~S~ P-C.,#] o+%%d M.&$`!v~# K{~]j%Q@,#+.g$=; ", +" {]e&*'H 0{I*5%]]^]/](]_]:]<] []}]|]1]p{2]3]4]5]6]7]8]9]Q!0]a]b]c]d]e]f]g]h]T!q~R!i]U!j]k]8 D ,#8#=!j%$ = |.u~l]L{w~u~o+2.2.D &+ ", +" 4'm]0{n]=$o]p]q]r]s]t] u]v]w]x]y]z]A]B]C]e]D]E]F]G]H]:~I]J]K]m~m~S~a!L]M]E{N]O]P]Q]&'F +.Q@o!R].{n!x~&!5{~] {K{$!8#6 6 / 8 ", +" S]h-T]U]8>X+V] W]X]Y]T~Z]`] ^.^+^@^#^$^%^&^*^=^-^;^>^,^'^)^!^~^{^S~t{]^^^i]/^(^f&C.2.] ^.r)4{*!m!H{I{_^2~J{:^,#G n F m ", +" <^;$[^ }^|^1^2^3^4^5^6^7^8^9^0^a^b^c^d^e^f^g^h^i^}{j^Z{k^}~i]l^0!m^n^o^p^a@i-q^T@o+j%$ = |.`~v~L{2~I{%%6 .3.+,m ", +" r^s^t^}~u^v^w^a]x^y^z^.]A^B^C^D^E^F^f^G^H^I^J^K^L^M^O]N^}~^~_{O^P^Q^t=+,G - 8#n+#!:^x~&!u~l] {6{%!.{%%V@Q@g$ ", +" R^S^T^U^V^W^X^|~Y^Z^`^ /./+/@/#/$/%/&/*/=/-/;/>/G{,/'/)/}~}~!/~/{/]/^///@. .,#^.R].{',$!&!k#_^L{l]I{~]:>:>- ", +" (/_/:/X^(,('()(!(~(&++.,#~+R].{:^#!,#- d ~]w~x~/ ", +" {(](^(/(((_(:(<(Q/T^[(}(|(1(2(3(4(5(@(q{6(7(8(9(`/0(Z~a(R!b(c(d(e(f(g(g(h('(i(j(k(F .] ^.n+~+B.4.n r)k#l(m(-)Q+ ", +" K^n(A{o(#(p(q(r(s(t(u(]{y/v(w(x(y(z(z^A(d^B(C(1/c!:~0]>/D(E(F(G(H(I(J(K(L(h(M(N(f$O(/ 6 q^G x>9 7#B.] n!~]@ :^P(Q(R(h&H- ", +" c!S(T(U(V(A(W(X(Y(Z(`( _._+_@_b^#_$_%_&_*_=_-_}/R!`/;_>_,_'_u=)_!_~_{_]_^_/_h((___:_<_[_z>A.a@);&>S*C.+.~+j%#!r)M.&$4{+.7.}_ ", +" i~|_1_2_3_a/4_+_5_6_((7_8_9_0_a_b_c_d_e_f_C^g_W~h_i_j_k_l_8>m_n_o_p_q_~_r_s_t_u_v_w_x_y_z_A_B_C_D_]=F / 7 &+a.n+~]l(:>r)C.z-E_F_", +" 0!Y~G_Z{H_I_J_;^K_L_M_X~N_O_P_*(t~Q_y(R_S_T_U_V_W_X_Y_Z_g*`_ :.:+:@:+:#:$:h(%:&:*:=:-:;:>:,:':):!:~:6.m {)S*B.^.|. {_^$!r)n C${:", +" ]:S~K^T!9!{{^:/:T^(:/(_:::<:[:}:f]|:<:1:2:H_3:}/4:5:6:7:8:9:0:a:b:c:b:d:e:f:g:h:i:j:n^k:l:m:n:o:p:q:r:s:,&{)g$B. .=!:^Z!|.5{l]t:u:", +" v^Z~'/Z~q~0!v:w:x:^{y:z:A:B:C:D:E:F:G:H:G^I:J:K:L:M:N:O:L(P:Q:R:S:T:c:U:V:W:X:Y:Z:`: <.<+ &+/ ,#R]$ m!5{# L{m<", +" n<%[&(i]&[e<*[=[-[-[)^;[>[J_^:,['[)[![~[{[][^[/[/[([G<_[:[<[[[}[|[o<}~%<1[2[3[4[5[6[7[8[9[0[a[o;),+,4.G ] R]$ m!%!b[c[ ", +" Q!'<;<'/d[l~=:%[e[$['l[m[n[o[p[q[r[/[G},}'})}!}m C.n ] ^.=!~}{}]}^} ", +" g/W^]^t{t{i]/}b!F//}(}x/_}:}'/%[r<(}t{<}[}}}|}1}2}3}4}5}6}7}6}8}9}5<0}a}b}|[u^c}d}e}:{>}f}g}h}i}j}k}l}m}n}o}p}q}r}s*9 8 F s}t}u}v}S) ", +" '/$[$[%<]^]^'|^[,|'|)|!|~|I}{|]|^|/|(|_|Z~*(D/:|<|Z~p<[|}|S<||_}m^1|2|W^3|4|5|6|7|8|9|0|a|b|c|m^d| ", +" S~i](}><$[i]&(>1I}s[,1'1)1!1~1q<'<:~i] ", +" M1v^F/N]j]N1O1r_r[P191Q1R1S1T171U1m|J2k},2'2S! ", +" )2!2~2~_r[m|t1T1{2]2^2u1/2(2_2:2<2[2}2R!9!Q!&(#2|21222n:32u|4252o}6272829202a2b2c2d2X}e2f2n:A[*|g2h2i2Q! ", +" j2k2l2W[(271m2n2o2p2q2r2=1/[s29 t2u2v2R!Z~q~U!Z~j:w2x2y2x/^^z2A2U3,3'3)3!3n<~3B1,}{3n}]3^3/3n:t{l^d[G2C1(3_3a2:3<3[3B2I1X2k1X3A3B3C3D3$[)/E3R!i]U!q~x[Q!&[Z23|m^&(e}W28[Y}/3Q4,4=1'4)4!4~4 W^]^i]U!b!N]O]}~l^)/G/{4]4^4W2k1x}g/D|4|}~x4v4%[y4z4=:q3 }L4M4N4O4 ]^P4j:Q4R4j}S4n:>}{3({[1n}T4j:62U4W202V4 1g< ", +" W4X4H(o_Y461x3x353'|H5C4,5n }$ '5e}4|)5!5q=!x~ x5^3$n/9 x>e5z5`~ W^V^f/ ", +" ", +" "}; diff --git a/Plugins/org.mitk.gui.qt.xnat/documentation/UserManual/icon.xpm b/Plugins/org.mitk.gui.qt.xnat/documentation/UserManual/icon.xpm index 83e48be4d8..9057c20bc6 100644 --- a/Plugins/org.mitk.gui.qt.xnat/documentation/UserManual/icon.xpm +++ b/Plugins/org.mitk.gui.qt.xnat/documentation/UserManual/icon.xpm @@ -1,21 +1,21 @@ -/* XPM */ -static const char * icon_xpm[] = { -"16 16 2 1", -" c #FF0000", -". c #000000", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" "}; +/* XPM */ +static const char * icon_xpm[] = { +"16 16 2 1", +" c #FF0000", +". c #000000", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/Plugins/org.mitk.gui.qt.xnat/resources/icon.xpm b/Plugins/org.mitk.gui.qt.xnat/resources/icon.xpm index 44c4146f2f..2dd3210698 100644 --- a/Plugins/org.mitk.gui.qt.xnat/resources/icon.xpm +++ b/Plugins/org.mitk.gui.qt.xnat/resources/icon.xpm @@ -1,1961 +1,1961 @@ -/* XPM */ -static char * C:\home\knorr\xnat\src\MITK\Plugins\org_mitk_gui_qt_xnatinterface\resources\icon_xpm[] = { -"77 80 1878 2", -" c None", -". c #E4D800", -"+ c #D8D000", -"@ c #F1E700", -"# c #EEE000", -"$ c #EED700", -"% c #EED800", -"& c #E5D500", -"* c #DDD600", -"= c #D7D700", -"- c #D2D500", -"; c #C8CC00", -"> c #BCB700", -", c #E2DC00", -"' c #E4CA00", -") c #E4D000", -"! c #E5D000", -"~ c #E4CD00", -"{ c #E0B800", -"] c #E2C300", -"^ c #E7D200", -"/ c #EADD00", -"( c #EBDF00", -"_ c #EBE000", -": c #EADC00", -"< c #E8DC00", -"[ c #E4DC00", -"} c #E3E000", -"| c #DEE200", -"1 c #D9DE00", -"2 c #CAD100", -"3 c #B9B500", -"4 c #C7BC00", -"5 c #D1C500", -"6 c #D6C200", -"7 c #D8B900", -"8 c #DAB700", -"9 c #DCBD00", -"0 c #E1C600", -"a c #E3CB00", -"b c #E4CC00", -"c c #E3CA00", -"d c #EEBC00", -"e c #EFC500", -"f c #F1D000", -"g c #F3D900", -"h c #F3DD00", -"i c #F2DF00", -"j c #F2E000", -"k c #F0E100", -"l c #EAE000", -"m c #EBE500", -"n c #E5E500", -"o c #D8D800", -"p c #C3C300", -"q c #DCC200", -"r c #CCBE00", -"s c #D9CA00", -"t c #DFCA00", -"u c #E1C500", -"v c #E5C300", -"w c #E7C900", -"x c #EDCF00", -"y c #EED300", -"z c #EFD400", -"A c #F0D400", -"B c #FFCF00", -"C c #FFD100", -"D c #FFD300", -"E c #FFD600", -"F c #FFDA00", -"G c #FCDE00", -"H c #FBDF00", -"I c #F6E100", -"J c #F2E100", -"K c #EEDF00", -"L c #E0D000", -"M c #C4B300", -"N c #0DAE00", -"O c #036C00", -"P c #037300", -"Q c #DAC500", -"R c #D3C400", -"S c #E6D500", -"T c #E9D200", -"U c #EBCE00", -"V c #EDCD00", -"W c #F2D200", -"X c #F4D600", -"Y c #F6D900", -"Z c #F7DA00", -"` c #F7D900", -" . c #FFD700", -".. c #FFD200", -"+. c #FFCE00", -"@. c #FCDC00", -"#. c #F8DE00", -"$. c #F3DA00", -"%. c #EACB00", -"&. c #D2A900", -"*. c #00B70C", -"=. c #00A50A", -"-. c #009C0B", -";. c #037700", -">. c #038900", -",. c #D2BE00", -"'. c #D0C300", -"). c #E6D700", -"!. c #EBD500", -"~. c #EDD200", -"{. c #F1D100", -"]. c #F4D300", -"^. c #F6D500", -"/. c #F6D600", -"(. c #F7D600", -"_. c #F7D500", -":. c #FBD900", -"<. c #FCD300", -"[. c #FDC900", -"}. c #FBC800", -"|. c #FCCF00", -"1. c #FAD900", -"2. c #F8DC00", -"3. c #EACF00", -"4. c #DCB500", -"5. c #0EB800", -"6. c #01A200", -"7. c #009F08", -"8. c #00A80E", -"9. c #00B813", -"0. c #00C314", -"a. c #017E00", -"b. c #009502", -"c. c #C2B600", -"d. c #DACF00", -"e. c #DFD100", -"f. c #E6D100", -"g. c #E9CE00", -"h. c #F4D500", -"i. c #F5D500", -"j. c #F3DE00", -"k. c #F5DD00", -"l. c #F4D800", -"m. c #F3CD00", -"n. c #F1CA00", -"o. c #F3CF00", -"p. c #F5D700", -"q. c #F2D700", -"r. c #E3C400", -"s. c #C89900", -"t. c #1FAF00", -"u. c #15A800", -"v. c #0BA500", -"w. c #03AF00", -"x. c #00BB05", -"y. c #00C30D", -"z. c #00CA12", -"A. c #00D216", -"B. c #018200", -"C. c #00AE02", -"D. c #BDB300", -"E. c #D7CF00", -"F. c #DED100", -"G. c #E4D100", -"H. c #E8CD00", -"I. c #ECCC00", -"J. c #EECE00", -"K. c #F0D100", -"L. c #F1D300", -"M. c #F2D300", -"N. c #F3D200", -"O. c #EDDD00", -"P. c #EDD800", -"Q. c #EDD400", -"R. c #ECD200", -"S. c #EBD200", -"T. c #E7CD00", -"U. c #CFB100", -"V. c #16E200", -"W. c #0FAC00", -"X. c #0FBC00", -"Y. c #0CBF00", -"Z. c #09C300", -"`. c #03C800", -" + c #00CB03", -".+ c #00CB06", -"++ c #00C807", -"@+ c #00C709", -"#+ c #038000", -"$+ c #B8AF00", -"%+ c #D4CC00", -"&+ c #DED200", -"*+ c #E2CE00", -"=+ c #E6CB00", -"-+ c #E7C800", -";+ c #EBCB00", -">+ c #EDCE00", -",+ c #EDD100", -"'+ c #EED000", -")+ c #EECF00", -"!+ c #E6DE00", -"~+ c #E7DE00", -"{+ c #E8DE00", -"]+ c #E8DB00", -"^+ c #E5D600", -"/+ c #DFCC00", -"(+ c #D6B900", -"_+ c #BA9600", -":+ c #04BC00", -"<+ c #049F00", -"[+ c #01AC00", -"}+ c #01CD00", -"|+ c #00C800", -"1+ c #00D300", -"2+ c #00D900", -"3+ c #00D800", -"4+ c #01CE00", -"5+ c #01C600", -"6+ c #02C000", -"7+ c #037B00", -"8+ c #B5AB00", -"9+ c #D2C900", -"0+ c #E1CC00", -"a+ c #E3C700", -"b+ c #E5C400", -"c+ c #E7C600", -"d+ c #EACC00", -"e+ c #EACE00", -"f+ c #EACD00", -"g+ c #E1CA00", -"h+ c #E5D200", -"i+ c #E8DA00", -"j+ c #E8DF00", -"k+ c #E1D300", -"l+ c #D6BF00", -"m+ c #BD9B00", -"n+ c #099300", -"o+ c #029800", -"p+ c #00B503", -"q+ c #00D006", -"r+ c #00DD00", -"s+ c #00E600", -"t+ c #00D000", -"u+ c #07CE00", -"v+ c #0AC700", -"w+ c #0CBB00", -"x+ c #057000", -"y+ c #B6A900", -"z+ c #D2C600", -"A+ c #DDCD00", -"B+ c #E1C800", -"C+ c #E2C200", -"D+ c #E3C100", -"E+ c #E8CA00", -"F+ c #E6CC00", -"G+ c #E1B800", -"H+ c #E6C400", -"I+ c #EAD300", -"J+ c #EBDB00", -"K+ c #E1CE00", -"L+ c #CDB200", -"M+ c #1C9E00", -"N+ c #118900", -"O+ c #08A800", -"P+ c #04C400", -"Q+ c #00C602", -"R+ c #00C900", -"S+ c #00E100", -"T+ c #00E000", -"U+ c #00E300", -"V+ c #00DC00", -"W+ c #00CF00", -"X+ c #00CA00", -"Y+ c #05CA00", -"Z+ c #08C200", -"`+ c #07B500", -" @ c #0A6200", -".@ c #BEAA00", -"+@ c #D5C400", -"@@ c #DCC700", -"#@ c #E0C500", -"$@ c #E2BF00", -"%@ c #E4C000", -"&@ c #E6C900", -"*@ c #E7CE00", -"=@ c #E5CD00", -"-@ c #ECB600", -";@ c #ECC100", -">@ c #EECD00", -",@ c #EED500", -"'@ c #EBD300", -")@ c #E3C800", -"!@ c #CEAC00", -"~@ c #11B000", -"{@ c #0FB700", -"]@ c #0BB600", -"^@ c #08B300", -"/@ c #05BE00", -"(@ c #03D000", -"_@ c #01D300", -":@ c #00C500", -"<@ c #00D200", -"[@ c #00CD00", -"}@ c #00CC02", -"|@ c #00C901", -"1@ c #00C101", -"2@ c #00B600", -"3@ c #C5AC00", -"4@ c #D8C300", -"5@ c #D8C100", -"6@ c #DFC200", -"7@ c #E2BE00", -"8@ c #E5BD00", -"9@ c #E6C800", -"0@ c #E6CE00", -"a@ c #F6C300", -"b@ c #F6C700", -"c@ c #F3CB00", -"d@ c #F2CF00", -"e@ c #D3AC00", -"f@ c #0C8100", -"g@ c #05DE00", -"h@ c #00C400", -"i@ c #08D400", -"j@ c #0CDB00", -"k@ c #0CD000", -"l@ c #0AC900", -"m@ c #0BCC00", -"n@ c #08C400", -"o@ c #06C200", -"p@ c #02C400", -"q@ c #00CB02", -"r@ c #00CB07", -"s@ c #00C80A", -"t@ c #00C409", -"u@ c #00C50A", -"v@ c #CAAD00", -"w@ c #D9C200", -"x@ c #D8BE00", -"y@ c #E1C100", -"z@ c #E4BD00", -"A@ c #E5BE00", -"B@ c #E7C300", -"C@ c #E5CB00", -"D@ c #E5CE00", -"E@ c #FCC700", -"F@ c #F9C700", -"G@ c #F7C900", -"H@ c #F4CB00", -"I@ c #F0CA00", -"J@ c #E8C300", -"K@ c #D9B000", -"L@ c #008D05", -"M@ c #198E00", -"N@ c #269800", -"O@ c #00E400", -"P@ c #00E700", -"Q@ c #00CB00", -"R@ c #13C500", -"S@ c #14BE00", -"T@ c #13BD00", -"U@ c #0DB900", -"V@ c #06BB00", -"W@ c #00C102", -"X@ c #00C209", -"Y@ c #00BB03", -"Z@ c #00C305", -"`@ c #00D011", -" # c #AD9F00", -".# c #CDC200", -"+# c #E2C400", -"@# c #E4C200", -"## c #E8C000", -"$# c #E9C100", -"%# c #EAC200", -"&# c #EABB00", -"*# c #EAC600", -"=# c #E0D300", -"-# c #D8CF00", -";# c #CABC00", -"># c #2FFF00", -",# c #19CF00", -"'# c #00C100", -")# c #00CA03", -"!# c #00DB19", -"~# c #00CE00", -"{# c #01C900", -"]# c #00C703", -"^# c #00C903", -"/# c #01D400", -"(# c #00CC00", -"_# c #02C300", -":# c #02B900", -"<# c #03B300", -"[# c #03B600", -"}# c #B3A500", -"|# c #C8BF00", -"1# c #D0C800", -"2# c #D6C800", -"3# c #DAC600", -"4# c #E1BE00", -"5# c #E3BE00", -"6# c #E7C000", -"7# c #E8C400", -"8# c #E4B700", -"9# c #EADA00", -"0# c #DFD200", -"a# c #C9BA00", -"b# c #19B400", -"c# c #14BB00", -"d# c #04D300", -"e# c #00DC15", -"f# c #00CB0A", -"g# c #01CC00", -"h# c #00C702", -"i# c #00D100", -"j# c #04C300", -"k# c #02B800", -"l# c #02B100", -"m# c #02B500", -"n# c #BDAE00", -"o# c #C0B800", -"p# c #CDC600", -"q# c #D3C600", -"r# c #DBBE00", -"s# c #DCB900", -"t# c #DEBB00", -"u# c #E4C500", -"v# c #E2B900", -"w# c #E6C300", -"x# c #EBD000", -"y# c #E1D000", -"z# c #C9BC00", -"A# c #ADAD00", -"B# c #009A01", -"C# c #0FAB00", -"D# c #0CC100", -"E# c #08D000", -"F# c #00D30A", -"G# c #00CE0E", -"H# c #00C802", -"I# c #00C603", -"J# c #00CA02", -"K# c #00C300", -"L# c #03C100", -"M# c #03B500", -"N# c #02B000", -"O# c #03B700", -"P# c #C9B600", -"Q# c #BAB100", -"R# c #CBC300", -"S# c #D2BF00", -"T# c #D5B700", -"U# c #D6B500", -"V# c #DABA00", -"W# c #DEC100", -"X# c #DFC400", -"Y# c #E7BF00", -"Z# c #EDC700", -"`# c #E4CB00", -" $ c #CCC900", -".$ c #A5C200", -"+$ c #009A17", -"@$ c #009C1E", -"#$ c #00A90E", -"$$ c #07C300", -"%$ c #0FD400", -"&$ c #00CD05", -"*$ c #00C601", -"=$ c #00C803", -"-$ c #00C700", -";$ c #00C600", -">$ c #00C200", -",$ c #02BE00", -"'$ c #03AD00", -")$ c #B8AD00", -"!$ c #C5BB00", -"~$ c #CCBD00", -"{$ c #CEB900", -"]$ c #CFB200", -"^$ c #D2B300", -"/$ c #D7B900", -"($ c #DAC100", -"_$ c #DBC500", -":$ c #E9BB00", -"<$ c #F0C700", -"[$ c #ECC900", -"}$ c #DEC500", -"|$ c #DBCF00", -"1$ c #CBD800", -"2$ c #8AC500", -"3$ c #2FA900", -"4$ c #008C11", -"5$ c #00B020", -"6$ c #00C914", -"7$ c #07D000", -"8$ c #0CC800", -"9$ c #09BE00", -"0$ c #02CD00", -"a$ c #01CB00", -"b$ c #00C502", -"c$ c #00C402", -"d$ c #00C503", -"e$ c #01C100", -"f$ c #03BB00", -"g$ c #03AE00", -"h$ c #03AA00", -"i$ c #02B700", -"j$ c #C5B100", -"k$ c #BDAD00", -"l$ c #C4B400", -"m$ c #CBB300", -"n$ c #CDB300", -"o$ c #D0B500", -"p$ c #D5BB00", -"q$ c #D9C100", -"r$ c #DAC300", -"s$ c #E7B200", -"t$ c #EEC000", -"u$ c #EAC800", -"v$ c #DECC00", -"w$ c #D3D800", -"x$ c #B1DB00", -"y$ c #66C000", -"z$ c #1AA100", -"A$ c #00AD13", -"B$ c #00C01C", -"C$ c #00CE08", -"D$ c #00D600", -"E$ c #03CD00", -"F$ c #0EC500", -"G$ c #10C000", -"H$ c #03C900", -"I$ c #00C501", -"J$ c #00C002", -"K$ c #00C202", -"L$ c #00C301", -"M$ c #00BE00", -"N$ c #01BE00", -"O$ c #02AA00", -"P$ c #01A600", -"Q$ c #02B400", -"R$ c #B39900", -"S$ c #C1A800", -"T$ c #CAB200", -"U$ c #D0B700", -"V$ c #D4BC00", -"W$ c #D7BE00", -"X$ c #DABF00", -"Y$ c #DBC100", -"Z$ c #EEAE00", -"`$ c #EAB700", -" % c #E1C400", -".% c #D4D400", -"+% c #B6D800", -"@% c #85CC00", -"#% c #46BB00", -"$% c #10AF00", -"%% c #00CD16", -"&% c #00C60B", -"*% c #06BF00", -"=% c #0DC600", -"-% c #02C600", -";% c #01C200", -">% c #00C001", -",% c #00BD03", -"'% c #00BE02", -")% c #00C000", -"!% c #00BF00", -"~% c #00BB00", -"{% c #02BB00", -"]% c #02A500", -"^% c #02A300", -"/% c #04B400", -"(% c #A98100", -"_% c #BC9B00", -":% c #CAB000", -"<% c #D3BC00", -"[% c #FBAE00", -"}% c #E5B000", -"|% c #D3C100", -"1% c #BFD700", -"2% c #90D300", -"3% c #4CBC00", -"4% c #21B900", -"5% c #01C800", -"6% c #00D80F", -"7% c #00DF05", -"8% c #08D300", -"9% c #06CB00", -"0% c #01BC00", -"a% c #02C500", -"b% c #01C000", -"c% c #00BD01", -"d% c #00BB01", -"e% c #00BC00", -"f% c #00BD00", -"g% c #00B900", -"h% c #03B900", -"i% c #03B200", -"j% c #03A200", -"k% c #019F00", -"l% c #03B100", -"m% c #A98300", -"n% c #DEC800", -"o% c #CAB300", -"p% c #DDC000", -"q% c #E0BE00", -"r% c #DBB500", -"s% c #ECA300", -"t% c #E4B400", -"u% c #CAC900", -"v% c #9BCF00", -"w% c #57C200", -"x% c #1DB800", -"y% c #04C100", -"z% c #00D40D", -"A% c #00D30C", -"B% c #00D507", -"C% c #00D400", -"D% c #00C604", -"E% c #00C40B", -"F% c #02C900", -"G% c #02C700", -"H% c #03C400", -"I% c #03BF00", -"J% c #00BA02", -"K% c #00B902", -"L% c #00BF01", -"M% c #00B700", -"N% c #00B800", -"O% c #00AC00", -"P% c #00A600", -"Q% c #029E00", -"R% c #029A00", -"S% c #01B000", -"T% c #003A82", -"U% c #CB9800", -"V% c #BEA400", -"W% c #CCB700", -"X% c #D6B800", -"Y% c #DBB600", -"Z% c #DEB200", -"`% c #DEA400", -" & c #D1B500", -".& c #A8C300", -"+& c #6EC200", -"@& c #2EBC00", -"#& c #08C500", -"$& c #00D206", -"%& c #00D509", -"&& c #00D104", -"*& c #04CB00", -"=& c #00C708", -"-& c #00C70F", -";& c #03C600", -">& c #04C200", -",& c #02BD00", -"'& c #00BA01", -")& c #00B702", -"!& c #00B300", -"~& c #02A900", -"{& c #039F00", -"]& c #029700", -"^& c #03AC00", -"/& c #00387D", -"(& c #003D91", -"_& c #0035D2", -":& c #B59B00", -"<& c #BFAA00", -"[& c #D2B500", -"}& c #D5AF00", -"|& c #DEB000", -"1& c #B6BE00", -"2& c #82C000", -"3& c #43B600", -"4& c #12BD00", -"5& c #00D503", -"6& c #00DF0E", -"7& c #00D406", -"8& c #00D308", -"9& c #00CC01", -"0& c #08C700", -"a& c #0BC400", -"b& c #00C70A", -"c& c #00C910", -"d& c #01C400", -"e& c #00B501", -"f& c #00B502", -"g& c #00B602", -"h& c #00B601", -"i& c #00B100", -"j& c #01B500", -"k& c #03A500", -"l& c #019800", -"m& c #038F00", -"n& c #00A100", -"o& c #00469B", -"p& c #0047A8", -"q& c #003DC9", -"r& c #0048BF", -"s& c #0065BA", -"t& c #C2A900", -"u& c #C0AF00", -"v& c #D5B900", -"w& c #D2AF00", -"x& c #D5AB00", -"y& c #ACC700", -"z& c #8CC100", -"A& c #5DB900", -"B& c #26B500", -"C& c #06C400", -"D& c #00DB11", -"E& c #00DF13", -"F& c #00D407", -"G& c #00CF04", -"H& c #04C900", -"I& c #0DC300", -"J& c #0BC000", -"K& c #00C303", -"L& c #00C609", -"M& c #00C70C", -"N& c #03BE00", -"O& c #01B900", -"P& c #00B301", -"Q& c #00B201", -"R& c #00B402", -"S& c #00B400", -"T& c #00AD00", -"U& c #01AF00", -"V& c #00A500", -"W& c #029D00", -"X& c #029400", -"Y& c #028D00", -"Z& c #01A300", -"`& c #004FAC", -" * c #0050C2", -".* c #0053EA", -"+* c #004EC7", -"@* c #0050AE", -"#* c #0065B7", -"$* c #C1AB00", -"%* c #BBA800", -"&* c #D1B600", -"** c #CFAF00", -"=* c #CFAB00", -"-* c #7ED300", -";* c #5EC000", -">* c #31AF00", -",* c #15BA00", -"'* c #00CA08", -")* c #00D211", -"!* c #00D10C", -"~* c #00CE07", -"{* c #06C700", -"]* c #0FC100", -"^* c #0FBF00", -"/* c #0ABE00", -"(* c #00C204", -"_* c #00C507", -":* c #03B800", -"<* c #01B200", -"[* c #00B101", -"}* c #00AF01", -"|* c #00AE00", -"1* c #00A800", -"2* c #029500", -"3* c #018E00", -"4* c #01A400", -"5* c #0052B5", -"6* c #0057D3", -"7* c #005FF6", -"8* c #0066FF", -"9* c #005EE3", -"0* c #0051BD", -"a* c #0054BD", -"b* c #C5AF00", -"c* c #A68F00", -"d* c #C0A500", -"e* c #C6AC00", -"f* c #CCAF00", -"g* c #54DB00", -"h* c #34C200", -"i* c #16B600", -"j* c #00D00F", -"k* c #00CD13", -"l* c #00C80B", -"m* c #00CD09", -"n* c #05C400", -"o* c #0AC000", -"p* c #0CBE00", -"q* c #08BD00", -"r* c #00AD02", -"s* c #00AE01", -"t* c #00A900", -"u* c #039C00", -"v* c #018F00", -"w* c #018500", -"x* c #039400", -"y* c #0054BA", -"z* c #005FE3", -"A* c #006FFF", -"B* c #0065FF", -"C* c #005FFA", -"D* c #0055D9", -"E* c #004CBC", -"F* c #0056BF", -"G* c #B39400", -"H* c #B09700", -"I* c #BCA600", -"J* c #C5B200", -"K* c #2ADA00", -"L* c #1ACD00", -"M* c #0FC600", -"N* c #00CE12", -"O* c #00CB18", -"P* c #00C810", -"Q* c #00CA09", -"R* c #06C100", -"S* c #08BE00", -"T* c #07BD00", -"U* c #02B300", -"V* c #02AF00", -"W* c #00AC02", -"X* c #00AA00", -"Y* c #01AB00", -"Z* c #01A100", -"`* c #039900", -" = c #009C00", -".= c #0062E9", -"+= c #0068FF", -"@= c #0067FF", -"#= c #005AFD", -"$= c #004DC5", -"%= c #00499B", -"&= c #004574", -"*= c #A78E00", -"== c #B4A200", -"-= c #C3B200", -";= c #0AD200", -">= c #0CD100", -",= c #09CE00", -"'= c #00C511", -")= c #00C614", -"!= c #00C50F", -"~= c #05C200", -"{= c #05C000", -"]= c #04BD00", -"^= c #05BB00", -"/= c #04BA00", -"(= c #00BA00", -"_= c #02AE00", -":= c #00AB00", -"<= c #00A400", -"[= c #009B00", -"}= c #039B00", -"|= c #019B00", -"1= c #02D400", -"2= c #007AFF", -"3= c #0062F5", -"4= c #005FF3", -"5= c #0060F1", -"6= c #0063F1", -"7= c #0065F0", -"8= c #0063F2", -"9= c #005DF4", -"0= c #0051F6", -"a= c #0044F6", -"b= c #0039F9", -"c= c #003DFF", -"d= c #72A900", -"e= c #74A500", -"f= c #00C411", -"g= c #00C611", -"h= c #00C712", -"i= c #00C80D", -"j= c #00C60A", -"k= c #00C206", -"l= c #00BF04", -"m= c #03BC00", -"n= c #06B900", -"o= c #06B500", -"p= c #05B600", -"q= c #01B600", -"r= c #00B903", -"s= c #13AA00", -"t= c #10AA00", -"u= c #0AAB00", -"v= c #06AC00", -"w= c #00AF06", -"x= c #00AD06", -"y= c #00A806", -"z= c #009F01", -"A= c #058E00", -"B= c #0A7E00", -"C= c #0F7400", -"D= c #176E00", -"E= c #0068EE", -"F= c #0057D6", -"G= c #005FF5", -"H= c #0063F5", -"I= c #0065F3", -"J= c #0069F1", -"K= c #0069F2", -"L= c #0063F4", -"M= c #005CF7", -"N= c #004FF7", -"O= c #0042EB", -"P= c #0039D8", -"Q= c #003FD5", -"R= c #54B200", -"S= c #519600", -"T= c #00C80F", -"U= c #00C90E", -"V= c #05BD00", -"W= c #00B305", -"X= c #00B405", -"Y= c #00B705", -"Z= c #08A900", -"`= c #07A800", -" - c #06AA00", -".- c #00B006", -"+- c #00AA08", -"@- c #009E06", -"#- c #008D00", -"$- c #079200", -"%- c #119D00", -"&- c #1A9F00", -"*- c #0055CD", -"=- c #0056D9", -"-- c #005BF0", -";- c #0068F7", -">- c #0069F5", -",- c #006CF2", -"'- c #006EF2", -")- c #006FF3", -"!- c #006CF6", -"~- c #0068F8", -"{- c #005FFC", -"]- c #0056F7", -"^- c #004EE3", -"/- c #0049C1", -"(- c #0058AD", -"_- c #0091AF", -":- c #0B7A00", -"<- c #00C404", -"[- c #00C509", -"}- c #00C70D", -"|- c #00B30C", -"1- c #00B212", -"2- c #00B411", -"3- c #00B505", -"4- c #00AA04", -"5- c #00A606", -"6- c #009F09", -"7- c #00980B", -"8- c #009009", -"9- c #008402", -"0- c #079500", -"a- c #10A200", -"b- c #0048B9", -"c- c #0053E1", -"d- c #0056F0", -"e- c #0068FC", -"f- c #0069F8", -"g- c #0070F5", -"h- c #006FF6", -"i- c #006CFA", -"j- c #005BB5", -"k- c #00699D", -"l- c #00A3A9", -"m- c #009971", -"n- c #00854D", -"o- c #00BE03", -"p- c #00C105", -"q- c #00B709", -"r- c #00B415", -"s- c #00B219", -"t- c #00B213", -"u- c #00B504", -"v- c #00B406", -"w- c #00B30A", -"x- c #00990D", -"y- c #008A0E", -"z- c #00850E", -"A- c #008A06", -"B- c #048F00", -"C- c #0043B6", -"D- c #0051E8", -"E- c #0057F5", -"F- c #0062FF", -"G- c #0065FE", -"H- c #006AFB", -"I- c #006EF9", -"J- c #0070F7", -"K- c #0070F8", -"L- c #006DFC", -"M- c #006AFF", -"N- c #0063FD", -"O- c #0067F5", -"P- c #006EE7", -"Q- c #0068BA", -"R- c #0077AA", -"S- c #00768D", -"T- c #008E96", -"U- c #00B019", -"V- c #00B311", -"W- c #00B906", -"X- c #03C300", -"Y- c #00B60D", -"Z- c #00B315", -"`- c #00B114", -" ; c #00B20C", -".; c #00B500", -"+; c #00B206", -"@; c #00AD0A", -"#; c #00A10D", -"$; c #009211", -"%; c #008811", -"&; c #00850F", -"*; c #0046C2", -"=; c #0056EF", -"-; c #005CFF", -";; c #0058FF", -">; c #005AFF", -",; c #0063FF", -"'; c #006CF9", -"); c #006BFC", -"!; c #0069FF", -"~; c #005FFF", -"{; c #0063FE", -"]; c #006AFE", -"^; c #0068DE", -"/; c #0073D4", -"(; c #006AB5", -"_; c #0080C2", -":; c #009B47", -"<; c #00A333", -"[; c #00AB13", -"}; c #05B800", -"|; c #00B60A", -"1; c #00B310", -"2; c #00B40F", -"3; c #00B000", -"4; c #00A700", -"5; c #009705", -"6; c #009509", -"7; c #009A10", -"8; c #0046C4", -"9; c #004ED5", -"0; c #004DFF", -"a; c #0051FF", -"b; c #0057FF", -"c; c #0060FF", -"d; c #0066FA", -"e; c #0069F9", -"f; c #0067FA", -"g; c #0061FF", -"h; c #005DFF", -"i; c #005CFD", -"j; c #005BEA", -"k; c #0070FC", -"l; c #006CDF", -"m; c #006DD9", -"n; c #008881", -"o; c #009062", -"p; c #009C2E", -"q; c #05B700", -"r; c #00B704", -"s; c #00B30E", -"t; c #00B20F", -"u; c #00B304", -"v; c #00A601", -"w; c #00A208", -"x; c #169D00", -"y; c #1B9A00", -"z; c #159700", -"A; c #119800", -"B; c #0047BF", -"C; c #0059E4", -"D; c #005BFE", -"E; c #0048FF", -"F; c #0053FF", -"G; c #0063FC", -"H; c #0065F9", -"I; c #0064F8", -"J; c #0061FC", -"K; c #005BFF", -"L; c #0054FC", -"M; c #0054FF", -"N; c #0057FB", -"O; c #005DF7", -"P; c #0050DE", -"Q; c #0064F1", -"R; c #0076B8", -"S; c #007F95", -"T; c #008D5B", -"U; c #009C20", -"V; c #0AAC00", -"W; c #07B200", -"X; c #04B500", -"Y; c #00B403", -"Z; c #00B20A", -"`; c #00B207", -" > c #00B200", -".> c #02AC00", -"+> c #08A600", -"@> c #049C00", -"#> c #009403", -"$> c #3C9400", -"%> c #449B00", -"&> c #429C00", -"*> c #3D9500", -"=> c #0044B0", -"-> c #0064F4", -";> c #0077FF", -">> c #004EFF", -",> c #0066FC", -"'> c #0067F8", -")> c #0066F7", -"!> c #0062F7", -"~> c #005DFC", -"{> c #0056F9", -"]> c #005AF4", -"^> c #005CF3", -"/> c #0063DC", -"(> c #006ABC", -"_> c #007689", -":> c #008351", -"<> c #00921F", -"[> c #009F04", -"}> c #04AB00", -"|> c #00B701", -"1> c #069E00", -"2> c #119900", -"3> c #169500", -"4> c #179100", -"5> c #668A00", -"6> c #729400", -"7> c #729500", -"8> c #719800", -"9> c #004EBE", -"0> c #0069F4", -"a> c #005EFF", -"b> c #0067FB", -"c> c #0065F2", -"d> c #0066F0", -"e> c #006FF9", -"f> c #0070FF", -"g> c #0064F5", -"h> c #0059F7", -"i> c #0058F5", -"j> c #0058F4", -"k> c #005AF2", -"l> c #005DF1", -"m> c #005FF1", -"n> c #005EF0", -"o> c #0060DF", -"p> c #0067BC", -"q> c #007290", -"r> c #007F5C", -"s> c #008F2A", -"t> c #009F02", -"u> c #03AB00", -"v> c #02B200", -"w> c #00AF00", -"x> c #03A700", -"y> c #059D00", -"z> c #0D9300", -"A> c #219000", -"B> c #339200", -"C> c #409800", -"D> c #908800", -"E> c #9B9300", -"F> c #9D9600", -"G> c #9D9400", -"H> c #0042A3", -"I> c #004DB6", -"J> c #0063E0", -"K> c #0074FF", -"L> c #0067F6", -"M> c #0068F5", -"N> c #006BF4", -"O> c #0071F9", -"P> c #0078FD", -"Q> c #0074F7", -"R> c #0068EC", -"S> c #005DE3", -"T> c #0068F1", -"U> c #0065EE", -"V> c #0061ED", -"W> c #0060EC", -"X> c #0060EB", -"Y> c #0060EA", -"Z> c #0060E8", -"`> c #0061E8", -" , c #005BEE", -"., c #005BED", -"+, c #005CE5", -"@, c #0061C8", -"#, c #006D98", -"$, c #007C57", -"%, c #009219", -"&, c #05A200", -"*, c #0DB400", -"=, c #04A700", -"-, c #069A00", -";, c #008A00", -">, c #128200", -",, c #328700", -"', c #529200", -"), c #659C00", -"!, c #B79000", -"~, c #BF9900", -"{, c #C09C00", -"], c #BD9900", -"^, c #003484", -"/, c #00459D", -"(, c #0054B6", -"_, c #0066D9", -":, c #006BE8", -"<, c #0074EF", -"[, c #0074EC", -"}, c #006FE7", -"|, c #006DE9", -"1, c #006EED", -"2, c #0070EF", -"3, c #006FF1", -"4, c #006AEF", -"5, c #0069F0", -"6, c #0065EA", -"7, c #0064E7", -"8, c #0062E5", -"9, c #0062E2", -"0, c #0063E1", -"a, c #0064E1", -"b, c #0057EE", -"c, c #0054F5", -"d, c #005AC1", -"e, c #006A81", -"f, c #008038", -"g, c #009201", -"h, c #0CA000", -"i, c #0B9300", -"j, c #0A8200", -"k, c #217A00", -"l, c #4E8200", -"m, c #719300", -"n, c #8CA300", -"o, c #D09C00", -"p, c #D5A400", -"q, c #D4A300", -"r, c #CA9E00", -"s, c #B79300", -"t, c #0055BE", -"u, c #0057B8", -"v, c #0058B0", -"w, c #005CB7", -"x, c #0061C1", -"y, c #0068CF", -"z, c #0072E0", -"A, c #0075EA", -"B, c #0071EC", -"C, c #006CEE", -"D, c #006DF5", -"E, c #0071FD", -"F, c #0067F2", -"G, c #0064EC", -"H, c #0063E2", -"I, c #0064DF", -"J, c #0064DE", -"K, c #0065DE", -"L, c #005CDB", -"M, c #0058E4", -"N, c #0052F2", -"O, c #004EEE", -"P, c #004FD3", -"Q, c #005CA1", -"R, c #006D61", -"S, c #007E2B", -"T, c #008B06", -"U, c #099000", -"V, c #148D00", -"W, c #268700", -"X, c #468100", -"Y, c #6D8700", -"Z, c #929600", -"`, c #B4AA00", -" ' c #D9A800", -".' c #D9AC00", -"+' c #D4A900", -"@' c #C8A100", -"#' c #B69400", -"$' c #006FD0", -"%' c #006CC5", -"&' c #0057B6", -"*' c #0055B6", -"=' c #0056BD", -"-' c #005CCD", -";' c #0062E3", -">' c #0060F3", -",' c #0057ED", -"'' c #005CF6", -")' c #005AF3", -"!' c #005BEC", -"~' c #005DE8", -"{' c #0061DF", -"]' c #0061DE", -"^' c #0062DB", -"/' c #005ED5", -"(' c #005ADA", -"_' c #0054DD", -":' c #004EDA", -"<' c #004BD0", -"[' c #0052B4", -"}' c #005D91", -"|' c #006965", -"1' c #007038", -"2' c #007B0F", -"3' c #1D8A00", -"4' c #479300", -"5' c #668E00", -"6' c #8A8F00", -"7' c #B29A00", -"8' c #CFA300", -"9' c #D3B200", -"0' c #CDAD00", -"a' c #C0A100", -"b' c #B19700", -"c' c #0048C2", -"d' c #0044BA", -"e' c #0047CC", -"f' c #004BE4", -"g' c #004EF4", -"h' c #004DFA", -"i' c #004CF8", -"j' c #004BF3", -"k' c #004BED", -"l' c #004FE7", -"m' c #0054E2", -"n' c #0058DE", -"o' c #005CDC", -"p' c #005FD8", -"q' c #005CCF", -"r' c #0058C6", -"s' c #0052BE", -"t' c #004CBD", -"u' c #004BBE", -"v' c #004EB7", -"w' c #00579C", -"x' c #00616A", -"y' c #00712B", -"z' c #2C8F00", -"A' c #569A00", -"B' c #799900", -"C' c #A59D00", -"D' c #C89C00", -"E' c #D99000", -"F' c #C6B700", -"G' c #C0B000", -"H' c #B6A500", -"I' c #AF9900", -"J' c #003BDE", -"K' c #003DE4", -"L' c #003DF3", -"M' c #003EF7", -"N' c #003EF5", -"O' c #0041EE", -"P' c #0045E8", -"Q' c #004BE2", -"R' c #0051DC", -"S' c #0057DC", -"T' c #0057D9", -"U' c #005EDA", -"V' c #005ECB", -"W' c #005BB6", -"X' c #0054A9", -"Y' c #004CAE", -"Z' c #0048C1", -"`' c #0048CD", -" ) c #004EBF", -".) c #005B90", -"+) c #006D3F", -"@) c #339300", -"#) c #61A300", -"$) c #87A400", -"%) c #B7A900", -"&) c #D49B00", -"*) c #DB8100", -"=) c #C1BC00", -"-) c #BDB900", -";) c #B8B100", -">) c #B0A600", -",) c #AE9C00", -"') c #003BAB", -")) c #002FA1", -"!) c #0030BA", -"~) c #0033CD", -"{) c #0042ED", -"]) c #0046D7", -"^) c #0052D2", -"/) c #005BCC", -"() c #0068CD", -"_) c #005FBC", -":) c #0052DC", -"<) c #0051D5", -"[) c #004FCC", -"}) c #0049C9", -"|) c #0045CC", -"1) c #0042D4", -"2) c #0040D5", -"3) c #0044C3", -"4) c #004594", -"5) c #005A68", -"6) c #0A6400", -"7) c #658D00", -"8) c #949800", -"9) c #BEA000", -"0) c #E3A200", -"a) c #D88B00", -"b) c #CB9000", -"c) c #C8AF00", -"d) c #C2B800", -"e) c #C0B900", -"f) c #A9A300", -"g) c #0039BC", -"h) c #0031AA", -"i) c #0036B2", -"j) c #0048D3", -"k) c #0056D8", -"l) c #0052BD", -"m) c #0062C8", -"n) c #0051D6", -"o) c #004AC6", -"p) c #0046C9", -"q) c #0042CF", -"r) c #0041D1", -"s) c #00449B", -"t) c #004E70", -"u) c #005D17", -"v) c #5D8B00", -"w) c #8A8D00", -"x) c #BC9E00", -"y) c #D69B00", -"z) c #DF9400", -"A) c #CC9100", -"B) c #CFA500", -"C) c #C7AE00", -"D) c #C0B500", -"E) c #BBB700", -"F) c #A7A100", -"G) c #003FAA", -"H) c #0040A8", -"I) c #0057C1", -"J) c #005CC9", -"K) c #0069D6", -"L) c #0051DB", -"M) c #004AC5", -"N) c #0045CB", -"O) c #0045CD", -"P) c #0045AA", -"Q) c #00437A", -"R) c #00544F", -"S) c #7A7E00", -"T) c #B69700", -"U) c #CA9700", -"V) c #E3A000", -"W) c #CC9400", -"X) c #CEA500", -"Y) c #BDB200", -"Z) c #B8B400", -"`) c #A29C00", -" ! c #005AC9", -".! c #004FB2", -"+! c #0055B3", -"@! c #005ABB", -"#! c #004FB4", -"$! c #0051D1", -"%! c #004FC9", -"&! c #0049C2", -"*! c #0047C1", -"=! c #0045C7", -"-! c #0045C5", -";! c #0044B9", -">! c #003B8E", -",! c #00417A", -"'! c #686900", -")! c #A58A00", -"!! c #C79C00", -"~! c #DCA600", -"{! c #CC9800", -"]! c #CEA600", -"^! c #BAB000", -"/! c #B3AE00", -"(! c #9D9900", -"_! c #004F8C", -":! c #004F92", -"~ c #BCAA00", -",~ c #B1A600", -"'~ c #A7A200", -")~ c #169F00", -"!~ c #0F7300", -"~~ c #138800", -"{~ c #00742F", -"]~ c #006B40", -"^~ c #00595B", -"/~ c #005180", -"(~ c #004EA2", -"_~ c #0045AD", -":~ c #0049BA", -"<~ c #0045BB", -"[~ c #003AAE", -"}~ c #0034A2", -"|~ c #002473", -"1~ c #6A6300", -"2~ c #BFA800", -"3~ c #BB9A00", -"4~ c #C3A700", -"5~ c #B5A300", -"6~ c #A89D00", -"7~ c #A69C00", -"8~ c #167600", -"9~ c #198500", -"0~ c #008613", -"a~ c #007A27", -"b~ c #006644", -"c~ c #005A6D", -"d~ c #00508F", -"e~ c #00439F", -"f~ c #0046AF", -"g~ c #0047B6", -"h~ c #0044B3", -"i~ c #003CA6", -"j~ c #003694", -"k~ c #002865", -"l~ c #666200", -"m~ c #958200", -"n~ c #B69D00", -"o~ c #C1A000", -"p~ c #C3A800", -"q~ c #AA9D00", -"r~ c #0E6E00", -"s~ c #0F7000", -"t~ c #039200", -"u~ c #00830F", -"v~ c #00722D", -"w~ c #006354", -"x~ c #005173", -"y~ c #00478D", -"z~ c #0042A0", -"A~ c #0049B3", -"B~ c #0042AE", -"C~ c #0040A4", -"D~ c #003788", -"E~ c #002756", -"F~ c #696300", -"G~ c #937F00", -"H~ c #AD9200", -"I~ c #BBA200", -"J~ c #AF9A00", -"K~ c #A79700", -"L~ c #006200", -"M~ c #005800", -"N~ c #0A9600", -"O~ c #008803", -"P~ c #007917", -"Q~ c #006C37", -"R~ c #005A57", -"S~ c #00547C", -"T~ c #00438F", -"U~ c #0049AE", -"V~ c #0040AD", -"W~ c #003FA6", -"X~ c #003584", -"Y~ c #00264F", -"Z~ c #686200", -"`~ c #937E00", -" { c #A98C00", -".{ c #BC9F00", -"+{ c #B59C00", -"@{ c #AC9600", -"#{ c #A79300", -"${ c #006009", -"%{ c #004707", -"&{ c #039100", -"*{ c #008900", -"={ c #007F07", -"-{ c #00761B", -";{ c #006437", -">{ c #006068", -",{ c #00477F", -"'{ c #0044A4", -"){ c #003EB2", -"!{ c #003BB1", -"~{ c #002F87", -"{{ c #002451", -"]{ c #696100", -"^{ c #967D00", -"/{ c #A98A00", -"({ c #BCA000", -"_{ c #B39A00", -":{ c #AF9700", -"<{ c #AB9300", -"[{ c #003E09", -"}{ c #008D0B", -"|{ c #008A05", -"1{ c #028600", -"2{ c #007B02", -"3{ c #006C18", -"4{ c #007352", -"5{ c #004F74", -"6{ c #003C98", -"7{ c #003BB8", -"8{ c #0039BD", -"9{ c #002B91", -"0{ c #002159", -"a{ c #685E00", -"b{ c #AD8A00", -"c{ c #B79B00", -"d{ c #B29800", -"e{ c #B49900", -"f{ c #033E00", -"g{ c #008716", -"h{ c #00890A", -"i{ c #058800", -"j{ c #098400", -"k{ c #027500", -"l{ c #008A3F", -"m{ c #00566D", -"n{ c #003C94", -"o{ c #0037BD", -"p{ c #0034C6", -"q{ c #00279A", -"r{ c #001F65", -"s{ c #665A00", -"t{ c #B08C00", -"u{ c #B09600", -"v{ c #B29600", -"w{ c #B99E00", -"x{ c #0F4600", -"y{ c #00811A", -"z{ c #00870E", -"A{ c #018600", -"B{ c #058100", -"C{ c #0E7E00", -"D{ c #009E31", -"E{ c #005D6A", -"F{ c #00439B", -"G{ c #0035C0", -"H{ c #0031CC", -"I{ c #0025A0", -"J{ c #001C6A", -"K{ c #645700", -"L{ c #977B00", -"M{ c #B28E00", -"N{ c #B69C00", -"O{ c #C4A600", -"P{ c #007600", -"Q{ c #029000", -"R{ c #008411", -"S{ c #00761C", -"T{ c #00570E", -"U{ c #00362F", -"V{ c #002C58", -"W{ c #004295", -"X{ c #003AA0", -"Y{ c #004097", -"Z{ c #00255B", -"`{ c #856300", -" ] c #B39300", -".] c #A79000", -"+] c #666F00", -"@] c #778200", -"#] c #1B9C00", -"$] c #007C00", -"%] c #008B18", -"&] c #00791B", -"*] c #005B0E", -"=] c #005A06", -"-] c #002F5A", -";] c #003F90", -">] c #0042A6", -",] c #003D8F", -"'] c #00265C", -")] c #604200", -"!] c #8D6900", -"~] c #AF9000", -"{] c #A89300", -"]] c #717C00", -"^] c #24C600", -"/] c #006900", -"(] c #008114", -"_] c #007817", -":] c #005C0A", -"<] c #005E0B", -"[] c #00355A", -"}] c #003887", -"|] c #003883", -"1] c #583F00", -"2] c #936D00", -"3] c #A48800", -"4] c #AB9700", -"5] c #026C00", -"6] c #00690A", -"7] c #007412", -"8] c #006209", -"9] c #00650D", -"0] c #003C5D", -"a] c #003781", -"b] c #003990", -"c] c #003477", -"d] c #584300", -"e] c #8E6D00", -"f] c #9C8000", -"g] c #AE9900", -"h] c #048000", -"i] c #006D0B", -"j] c #006A07", -"k] c #007012", -"l] c #004764", -"m] c #003D81", -"n] c #00317C", -"o] c #002E6D", -"p] c #654E00", -"q] c #856900", -"r] c #9A7E00", -"s] c #B8A200", -"t] c #005A05", -"u] c #006604", -"v] c #006A04", -"w] c #007615", -"x] c #00AB61", -"y] c #005168", -"z] c #003977", -"A] c #002B6C", -"B] c #002354", -"C] c #343300", -"D] c #6D5800", -"E] c #856B00", -"F] c #B49B00", -"G] c #007100", -"H] c #005E00", -"I] c #006300", -"J] c #007717", -"K] c #00A45B", -"L] c #005664", -"M] c #00346C", -"N] c #00245E", -"O] c #001833", -"P] c #463C00", -"Q] c #6F5E00", -"R] c #8F7600", -"S] c #9D8400", -"T] c #055A00", -"U] c #006000", -"V] c #007C1A", -"W] c #00A659", -"X] c #005961", -"Y] c #003462", -"Z] c #001C53", -"`] c #4F4400", -" ^ c #716200", -".^ c #A48D00", -"+^ c #0D8100", -"@^ c #006204", -"#^ c #00741A", -"$^ c #008745", -"%^ c #005A5B", -"&^ c #002C51", -"*^ c #00164E", -"=^ c #150F00", -"-^ c #574B00", -";^ c #8E8400", -">^ c #AB9D00", -",^ c #006906", -"'^ c #005102", -")^ c #008A46", -"!^ c #005953", -"~^ c #002747", -"{^ c #00062D", -"]^ c #231600", -"^^ c #675B00", -"/^ c #A7A300", -"(^ c #005C19", -"_^ c #006C33", -":^ c #004F41", -"<^ c #002138", -"[^ c #05001A", -"}^ c #301A00", -"|^ c #7C7200", -"1^ c #007B28", -"2^ c #004F21", -"3^ c #00402D", -"4^ c #001724", -"5^ c #130014", -"6^ c #005323", -"7^ c #00321B", -"8^ c #001215", -"9^ c #00310F", -" ", -" . + ", -" @ # $ % & * = - ; > ", -" , ' ) ! ~ { ] ^ / ( _ : < [ } | 1 2 3 ", -" 4 5 6 7 8 9 0 a b c d e f g h i j k l m n o p ", -" q r s t u v w x y z A B C D E F G H I J K L M N O P ", -" Q R S T U V W X Y Z ` F ...+.C .@.#.$.%.&. *.=.-.;.>. ", -" ,.'.).!.~.{.].^./.(._.:.:.<.[.}.|.1.2.3.4. 5.6.7.8.9.0.a.b. ", -" c.d.e.f.g.V f W h.i.^.j.k.l.m.n.o.p.q.r.s. t.u.v.w.x.y.z.A.B.C. ", -" D.E.F.G.H.I.J.K.L.M.N._ _ O.P.Q.R.S.T.U. V.W.X.Y.Z.`. +.+++@+#+ ", -" $+%+&+*+=+-+;+>+,+'+)+!+!+~+{+]+^+/+(+_+ :+<+[+}+|+1+2+3+1+4+5+6+7+ ", -" 8+9+F.0+a+b+c+d+e+f+f+g+h+i+j+~+k+l+m+ n+o+p+q+3+r+s+s+3+t+u+v+w+x+ ", -" y+z+A+B+C+D+b+E+H.T.F+G+H+I+J+i+K+L+ M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+`+ @ ", -" .@+@@@#@$@%@b+&@F+*@=@-@;@>@,@'@)@!@ ~@{@]@^@/@(@_@:@t+<@t+[@}@|@1@2@ ", -" 3@4@5@6@7@8@H+9@=+=@0@a@b@c@d@>+H+e@ f@g@R+h@i@j@k@l@m@n@o@p@q@r@s@t@u@ ", -" v@w@x@y@z@A@B@9@C@=@D@E@F@G@H@I@J@K@ L@M@N@3+O@P@2+Q@v+R@S@T@U@V@W@X@Y@Z@`@ ", -" #.#9+s @@+#@###$#%#&#*#f.=#-#;# >#,#'#)#!#~#[@Q@{#]#]#^#Q@[@/#(#:@_#:#<#[# ", -" }#|#1#2#3#6@4#5#6#7#8#v S.9#0#a# b#c#d#e#f#W+~#g#{#h#]#^#q@[@i#Q@:@j#k#l#m# ", -" n#o#p#q#4@r#s#t#D+u#v#w#x#% y#z#A# B#C#D#E#F#G#t+W+g#|@H#I#]#J#X+W+|+K#L#M#N#O# ", -" P#Q#R#'.S#T#U#V#W#X#Y#Z#d+`#A+ $.$ +$@$#$$$%$|+h@&$t+W+g#|+*$I#]#=$-$Q@;$>$,$l#'$O# ", -" )$!$~${$]$^$/$($_$:$<$[$}$|$1$2$3$4$5$6$<@1+7$8$9$W+0$a$-$b$c$d$*$:@-$>$e$f$g$h$i$ ", -" j$k$l$m$n$o$p$q$r$s$t$u$v$w$x$y$z$A$B$C$3+D$E$F$G$~#g#H$I$W@J$K$L$>$K#M$N$k#O$P$Q$ ", -" R$S$T$U$V$W$X$Y$Z$`$ %.%+%@%#%$%%%&%Q@r+V+-$*%=%(#R+-%;%>%,%'%)%!%!%~%{%Q$]%^%/% ", -" (%_%:%<%W$X$r#9 [%}%|%1%2%3%4%5%Z@6%7%D$t+8%9%0%X+5%a%b%c%Y@d%e%M$f%g%h%i%j%k%l% ", -" m%]$n%o%p%q%r%s%t%u%v%w%x%y%z%A%B%C%<@W+X+D%E%F%G%H%I%e%J%K%d%L%M%N%O%P%Q%R%S% ", -" T% U%V%W%w@X%Y%Z%`% &.&+&@&#&W+$&%&&&~#*&H$|+=&-&5%;&>&,&'&K%)&K%N%!&h%l#~&{&]&^& ", -" /&(&_& :&<&D@[&}&|&n$1&2&3&4&5&6&7&8&9&0&a&n@d$b&c&5+d&6+~%M%e&f&g&h&i&j&'$k&l&m&n& ", -" o&p&q&r&s& t&u&Q v&w&x&y&z&A&B&C&D&E&F&G&H&I&G$J&K&L&M&K#e$N&O&j&P&Q&R&S&T&U&V&W&X&Y&Z& ", -" `& *.*+*@*#* $*%*&***=*-*;*>*,*'*)*!*~* +{*]*^*/*)%(*_*'#M${%:*<*[*}*[*|*1*[+^%W&2*3*4* ", -" 5*6*7*8*9*0*a* b*c*d*e*f*g*h*i*n@j*k*l*m*|+n*o*p*q*N&!%!%M$f%g%j&S%|*r*s*T&t*g$k&u*v*w*x* ", -" y*z*A*8*B*C*D*E*F* G*H*I*J*K*L*M*R+N*O*P*Q*5+P+R*S*T*:+{%~%f%~%M%U*V*r*W*T&<*X*Y*Z*`*3*>. = ", -" y*.=A*8*+=@=#=$=%=&= *===-=;=>=,=I#'=)=!=K&d&H%~={=]=^=/=/=e%(=2@<*_=r*W*:=[*P%<=[=}=|=Z*1= ", -" 2=6*3=4=5=6=7=8=9=0=a=b=c= d=e=f=g=h=P*i=j=k=l=m=n=o=p=q=)&r=K%s=t=u=v=r*w=x=y=z=A=B=C=D= ", -" E=F=G=H=I=J=K=K=L=M=N=O=P=Q= R=S=_*j=T=c&U=@+L$e$V=i$!&W=X=Y=g%~%Z=`= -O%.-+-@-#-3*$-%-&- ", -" *-=---;->-,-'-)-!-~-{-]-^-/-(-_- :-d&<-[-}-b&*$:@;%,&M%|-1-2-3-(=~%T&O%4-5-6-7-8-9-0-a- ", -" b-c-d-e-f-!-g-g-h-i-+=@=@=9*j-k-l-m-n-f%o-p-<-;$;$h@'#f%q-r-s-t-u-N%g%v-w-#$x-y-z-A-B- ", -" C-D-E-F-G-H-I-J-K-L-M-+=N-O-P-Q-R-S-T-U-V-W-*%X-d&K#)%J%Y-Z-`- ;!&S&.;+;@;#;$;%;&; ", -" *;=;-;;;>;,;e-';';);!;!;~;{;];^;/;(;_;:;<;[;};/@6+!%e%|;1;2;+;i&3;|*|*4;z=5;6;7; ", -" 8;9;M=8*0;a;b;c;d;e;f;B*B*g;h;i;j;k;l;m;n;o;p;Y*q;f$g%r;s;t;u;!&i&O%v;w;x;y;z;A; ", -" B;C;D;A*E;0;F;-;G;H;I;J;K;L;M;K;N;O;P;Q;R;S;T;U;V;W;X;Y;Z;`; >3;.>+>@>#>$>%>&>*> ", -" =>->8*;>>>a;>;c;,>'>)>!>~>;;M;F;L;{>]>^>/>(>_>:><>[>}>S%!&|>m#}>1>2>3>4>5>6>7>8> ", -" 9>0>+=a>-;g;@=b>c>d>e>f>g>7*h>i>j>k>l>m>n>o>p>q>r>s>t>u>v>w>x>y>z>A>B>C>D>E>F>G> ", -" H>I>J>2=K>L>M>N>O>P>Q>R>S>T>U>V>W>X>Y>Z>`> ,.,+,@,#,$,%,&,*,=,-,;,>,,,',),!,~,{,], ", -" ^,/,(,_,:,<,[,},|,1,2,3,4,5,E=6,7,8,9,0,a,+,b,c,.*d,e,f,g,s=h,i,j,k,l,m,n,o,p,q,r,s, ", -" t,u,v,w,x,y,z,A,B,C,D,E,F,7=G,7,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,`, '.'+'@'#' ", -" $'%'&'*'='-';'Q;>',''')'!'~'9*{']'^'/'('_':'<'['}'|'1'2'3'4'5'6'7'8'9'^$0'a'b' ", -" n>c'd'e'f'g'h'i'j'k'l'm'n'o'L,p'q'r's't'u'v'w'x'y'z'A'B'C'D'E'a#F'G'H'I' ", -" a=J'K'L'M'N'O'P'Q'R'S'T'U'V'W'X'Y'Z'`' ).)+)@)#)$)%)&)*)=)-);)>),) ", -" ')))!)~){)])^)/)()_):)<)[)})|)1)2)3)4)5)6)7)8)9)0)a)b)8'c)d)e)f) ", -" g)h)i)j)k)-'l)m):)n)[)o)p)q)r)3)s)t)u)v)w)x)y)z)A)B)C)D)E)F) ", -" G)G)H)I)J)K)L)n)[)M)8;N)O)3)P)Q)R) S)T)U)V)W)X)3@Y)Z)`) ", -" !.!+!@!#!<)$!%!&!*!8;=!-!;!>!,! '!)!!!~!{!]!$*^!/!(! ", -" _!:!~,~'~ ", -" )~!~~~{~]~^~/~(~_~:~2!<~[~}~|~ 1~=~2~3~4~5~6~7~ ", -" 8~9~0~a~b~c~d~e~f~g~h~i~j~k~ l~m~n~o~p~==q~ ", -" r~s~t~u~v~w~x~y~z~A~B~C~D~E~ F~G~H~a'I~J~K~ ", -" L~M~N~O~P~Q~R~S~T~U~V~W~X~Y~ Z~`~ {.{+{@{#{ ", -" ${%{&{*{={-{;{>{,{'{){!{~{{{ ]{^{/{({_{:{<{ ", -" [{}{|{1{2{3{4{5{6{7{8{9{0{ a{^{b{c{d{e{ ", -" f{g{h{i{j{k{l{m{n{o{p{q{r{ s{^{t{u{v{w{ ", -" x{y{z{A{B{C{D{E{F{G{H{I{J{ K{L{M{R$N{O{ ", -" P{Q{R{S{T{ U{V{W{X{Y{Z{ `{ ].]+]@] ", -" #]$]%]&]*]=] -];]>],]'])]!]~]{]]] ", -" ^]/](]_]:]<] []}]z~|] 1]2]3]4] ", -" 5]6]7]8]9] 0]a]b]c] d]e]f]g] ", -" h]=]i]j]k] l]m]n]o] p]q]r]s] ", -" t]u]v]w]x]y]z]A]B]C]D]E]r]F] ", -" G]H]I]J]K]L]M]N]O]P]Q]R]S] ", -" T]U]V]W]X]Y]Z] `] ^.^ ", -" +^@^#^$^%^&^*^=^-^;^>^ ", -" ,^'^)^!^~^{^]^^^/^ ", -" (^_^:^<^[^}^|^ ", -" 1^2^3^4^5^ ", -" 6^7^8^ ", -" 9^ ", -" ", -" "}; +/* XPM */ +static char * C:\home\knorr\xnat\src\MITK\Plugins\org_mitk_gui_qt_xnatinterface\resources\icon_xpm[] = { +"77 80 1878 2", +" c None", +". c #E4D800", +"+ c #D8D000", +"@ c #F1E700", +"# c #EEE000", +"$ c #EED700", +"% c #EED800", +"& c #E5D500", +"* c #DDD600", +"= c #D7D700", +"- c #D2D500", +"; c #C8CC00", +"> c #BCB700", +", c #E2DC00", +"' c #E4CA00", +") c #E4D000", +"! c #E5D000", +"~ c #E4CD00", +"{ c #E0B800", +"] c #E2C300", +"^ c #E7D200", +"/ c #EADD00", +"( c #EBDF00", +"_ c #EBE000", +": c #EADC00", +"< c #E8DC00", +"[ c #E4DC00", +"} c #E3E000", +"| c #DEE200", +"1 c #D9DE00", +"2 c #CAD100", +"3 c #B9B500", +"4 c #C7BC00", +"5 c #D1C500", +"6 c #D6C200", +"7 c #D8B900", +"8 c #DAB700", +"9 c #DCBD00", +"0 c #E1C600", +"a c #E3CB00", +"b c #E4CC00", +"c c #E3CA00", +"d c #EEBC00", +"e c #EFC500", +"f c #F1D000", +"g c #F3D900", +"h c #F3DD00", +"i c #F2DF00", +"j c #F2E000", +"k c #F0E100", +"l c #EAE000", +"m c #EBE500", +"n c #E5E500", +"o c #D8D800", +"p c #C3C300", +"q c #DCC200", +"r c #CCBE00", +"s c #D9CA00", +"t c #DFCA00", +"u c #E1C500", +"v c #E5C300", +"w c #E7C900", +"x c #EDCF00", +"y c #EED300", +"z c #EFD400", +"A c #F0D400", +"B c #FFCF00", +"C c #FFD100", +"D c #FFD300", +"E c #FFD600", +"F c #FFDA00", +"G c #FCDE00", +"H c #FBDF00", +"I c #F6E100", +"J c #F2E100", +"K c #EEDF00", +"L c #E0D000", +"M c #C4B300", +"N c #0DAE00", +"O c #036C00", +"P c #037300", +"Q c #DAC500", +"R c #D3C400", +"S c #E6D500", +"T c #E9D200", +"U c #EBCE00", +"V c #EDCD00", +"W c #F2D200", +"X c #F4D600", +"Y c #F6D900", +"Z c #F7DA00", +"` c #F7D900", +" . c #FFD700", +".. c #FFD200", +"+. c #FFCE00", +"@. c #FCDC00", +"#. c #F8DE00", +"$. c #F3DA00", +"%. c #EACB00", +"&. c #D2A900", +"*. c #00B70C", +"=. c #00A50A", +"-. c #009C0B", +";. c #037700", +">. c #038900", +",. c #D2BE00", +"'. c #D0C300", +"). c #E6D700", +"!. c #EBD500", +"~. c #EDD200", +"{. c #F1D100", +"]. c #F4D300", +"^. c #F6D500", +"/. c #F6D600", +"(. c #F7D600", +"_. c #F7D500", +":. c #FBD900", +"<. c #FCD300", +"[. c #FDC900", +"}. c #FBC800", +"|. c #FCCF00", +"1. c #FAD900", +"2. c #F8DC00", +"3. c #EACF00", +"4. c #DCB500", +"5. c #0EB800", +"6. c #01A200", +"7. c #009F08", +"8. c #00A80E", +"9. c #00B813", +"0. c #00C314", +"a. c #017E00", +"b. c #009502", +"c. c #C2B600", +"d. c #DACF00", +"e. c #DFD100", +"f. c #E6D100", +"g. c #E9CE00", +"h. c #F4D500", +"i. c #F5D500", +"j. c #F3DE00", +"k. c #F5DD00", +"l. c #F4D800", +"m. c #F3CD00", +"n. c #F1CA00", +"o. c #F3CF00", +"p. c #F5D700", +"q. c #F2D700", +"r. c #E3C400", +"s. c #C89900", +"t. c #1FAF00", +"u. c #15A800", +"v. c #0BA500", +"w. c #03AF00", +"x. c #00BB05", +"y. c #00C30D", +"z. c #00CA12", +"A. c #00D216", +"B. c #018200", +"C. c #00AE02", +"D. c #BDB300", +"E. c #D7CF00", +"F. c #DED100", +"G. c #E4D100", +"H. c #E8CD00", +"I. c #ECCC00", +"J. c #EECE00", +"K. c #F0D100", +"L. c #F1D300", +"M. c #F2D300", +"N. c #F3D200", +"O. c #EDDD00", +"P. c #EDD800", +"Q. c #EDD400", +"R. c #ECD200", +"S. c #EBD200", +"T. c #E7CD00", +"U. c #CFB100", +"V. c #16E200", +"W. c #0FAC00", +"X. c #0FBC00", +"Y. c #0CBF00", +"Z. c #09C300", +"`. c #03C800", +" + c #00CB03", +".+ c #00CB06", +"++ c #00C807", +"@+ c #00C709", +"#+ c #038000", +"$+ c #B8AF00", +"%+ c #D4CC00", +"&+ c #DED200", +"*+ c #E2CE00", +"=+ c #E6CB00", +"-+ c #E7C800", +";+ c #EBCB00", +">+ c #EDCE00", +",+ c #EDD100", +"'+ c #EED000", +")+ c #EECF00", +"!+ c #E6DE00", +"~+ c #E7DE00", +"{+ c #E8DE00", +"]+ c #E8DB00", +"^+ c #E5D600", +"/+ c #DFCC00", +"(+ c #D6B900", +"_+ c #BA9600", +":+ c #04BC00", +"<+ c #049F00", +"[+ c #01AC00", +"}+ c #01CD00", +"|+ c #00C800", +"1+ c #00D300", +"2+ c #00D900", +"3+ c #00D800", +"4+ c #01CE00", +"5+ c #01C600", +"6+ c #02C000", +"7+ c #037B00", +"8+ c #B5AB00", +"9+ c #D2C900", +"0+ c #E1CC00", +"a+ c #E3C700", +"b+ c #E5C400", +"c+ c #E7C600", +"d+ c #EACC00", +"e+ c #EACE00", +"f+ c #EACD00", +"g+ c #E1CA00", +"h+ c #E5D200", +"i+ c #E8DA00", +"j+ c #E8DF00", +"k+ c #E1D300", +"l+ c #D6BF00", +"m+ c #BD9B00", +"n+ c #099300", +"o+ c #029800", +"p+ c #00B503", +"q+ c #00D006", +"r+ c #00DD00", +"s+ c #00E600", +"t+ c #00D000", +"u+ c #07CE00", +"v+ c #0AC700", +"w+ c #0CBB00", +"x+ c #057000", +"y+ c #B6A900", +"z+ c #D2C600", +"A+ c #DDCD00", +"B+ c #E1C800", +"C+ c #E2C200", +"D+ c #E3C100", +"E+ c #E8CA00", +"F+ c #E6CC00", +"G+ c #E1B800", +"H+ c #E6C400", +"I+ c #EAD300", +"J+ c #EBDB00", +"K+ c #E1CE00", +"L+ c #CDB200", +"M+ c #1C9E00", +"N+ c #118900", +"O+ c #08A800", +"P+ c #04C400", +"Q+ c #00C602", +"R+ c #00C900", +"S+ c #00E100", +"T+ c #00E000", +"U+ c #00E300", +"V+ c #00DC00", +"W+ c #00CF00", +"X+ c #00CA00", +"Y+ c #05CA00", +"Z+ c #08C200", +"`+ c #07B500", +" @ c #0A6200", +".@ c #BEAA00", +"+@ c #D5C400", +"@@ c #DCC700", +"#@ c #E0C500", +"$@ c #E2BF00", +"%@ c #E4C000", +"&@ c #E6C900", +"*@ c #E7CE00", +"=@ c #E5CD00", +"-@ c #ECB600", +";@ c #ECC100", +">@ c #EECD00", +",@ c #EED500", +"'@ c #EBD300", +")@ c #E3C800", +"!@ c #CEAC00", +"~@ c #11B000", +"{@ c #0FB700", +"]@ c #0BB600", +"^@ c #08B300", +"/@ c #05BE00", +"(@ c #03D000", +"_@ c #01D300", +":@ c #00C500", +"<@ c #00D200", +"[@ c #00CD00", +"}@ c #00CC02", +"|@ c #00C901", +"1@ c #00C101", +"2@ c #00B600", +"3@ c #C5AC00", +"4@ c #D8C300", +"5@ c #D8C100", +"6@ c #DFC200", +"7@ c #E2BE00", +"8@ c #E5BD00", +"9@ c #E6C800", +"0@ c #E6CE00", +"a@ c #F6C300", +"b@ c #F6C700", +"c@ c #F3CB00", +"d@ c #F2CF00", +"e@ c #D3AC00", +"f@ c #0C8100", +"g@ c #05DE00", +"h@ c #00C400", +"i@ c #08D400", +"j@ c #0CDB00", +"k@ c #0CD000", +"l@ c #0AC900", +"m@ c #0BCC00", +"n@ c #08C400", +"o@ c #06C200", +"p@ c #02C400", +"q@ c #00CB02", +"r@ c #00CB07", +"s@ c #00C80A", +"t@ c #00C409", +"u@ c #00C50A", +"v@ c #CAAD00", +"w@ c #D9C200", +"x@ c #D8BE00", +"y@ c #E1C100", +"z@ c #E4BD00", +"A@ c #E5BE00", +"B@ c #E7C300", +"C@ c #E5CB00", +"D@ c #E5CE00", +"E@ c #FCC700", +"F@ c #F9C700", +"G@ c #F7C900", +"H@ c #F4CB00", +"I@ c #F0CA00", +"J@ c #E8C300", +"K@ c #D9B000", +"L@ c #008D05", +"M@ c #198E00", +"N@ c #269800", +"O@ c #00E400", +"P@ c #00E700", +"Q@ c #00CB00", +"R@ c #13C500", +"S@ c #14BE00", +"T@ c #13BD00", +"U@ c #0DB900", +"V@ c #06BB00", +"W@ c #00C102", +"X@ c #00C209", +"Y@ c #00BB03", +"Z@ c #00C305", +"`@ c #00D011", +" # c #AD9F00", +".# c #CDC200", +"+# c #E2C400", +"@# c #E4C200", +"## c #E8C000", +"$# c #E9C100", +"%# c #EAC200", +"&# c #EABB00", +"*# c #EAC600", +"=# c #E0D300", +"-# c #D8CF00", +";# c #CABC00", +"># c #2FFF00", +",# c #19CF00", +"'# c #00C100", +")# c #00CA03", +"!# c #00DB19", +"~# c #00CE00", +"{# c #01C900", +"]# c #00C703", +"^# c #00C903", +"/# c #01D400", +"(# c #00CC00", +"_# c #02C300", +":# c #02B900", +"<# c #03B300", +"[# c #03B600", +"}# c #B3A500", +"|# c #C8BF00", +"1# c #D0C800", +"2# c #D6C800", +"3# c #DAC600", +"4# c #E1BE00", +"5# c #E3BE00", +"6# c #E7C000", +"7# c #E8C400", +"8# c #E4B700", +"9# c #EADA00", +"0# c #DFD200", +"a# c #C9BA00", +"b# c #19B400", +"c# c #14BB00", +"d# c #04D300", +"e# c #00DC15", +"f# c #00CB0A", +"g# c #01CC00", +"h# c #00C702", +"i# c #00D100", +"j# c #04C300", +"k# c #02B800", +"l# c #02B100", +"m# c #02B500", +"n# c #BDAE00", +"o# c #C0B800", +"p# c #CDC600", +"q# c #D3C600", +"r# c #DBBE00", +"s# c #DCB900", +"t# c #DEBB00", +"u# c #E4C500", +"v# c #E2B900", +"w# c #E6C300", +"x# c #EBD000", +"y# c #E1D000", +"z# c #C9BC00", +"A# c #ADAD00", +"B# c #009A01", +"C# c #0FAB00", +"D# c #0CC100", +"E# c #08D000", +"F# c #00D30A", +"G# c #00CE0E", +"H# c #00C802", +"I# c #00C603", +"J# c #00CA02", +"K# c #00C300", +"L# c #03C100", +"M# c #03B500", +"N# c #02B000", +"O# c #03B700", +"P# c #C9B600", +"Q# c #BAB100", +"R# c #CBC300", +"S# c #D2BF00", +"T# c #D5B700", +"U# c #D6B500", +"V# c #DABA00", +"W# c #DEC100", +"X# c #DFC400", +"Y# c #E7BF00", +"Z# c #EDC700", +"`# c #E4CB00", +" $ c #CCC900", +".$ c #A5C200", +"+$ c #009A17", +"@$ c #009C1E", +"#$ c #00A90E", +"$$ c #07C300", +"%$ c #0FD400", +"&$ c #00CD05", +"*$ c #00C601", +"=$ c #00C803", +"-$ c #00C700", +";$ c #00C600", +">$ c #00C200", +",$ c #02BE00", +"'$ c #03AD00", +")$ c #B8AD00", +"!$ c #C5BB00", +"~$ c #CCBD00", +"{$ c #CEB900", +"]$ c #CFB200", +"^$ c #D2B300", +"/$ c #D7B900", +"($ c #DAC100", +"_$ c #DBC500", +":$ c #E9BB00", +"<$ c #F0C700", +"[$ c #ECC900", +"}$ c #DEC500", +"|$ c #DBCF00", +"1$ c #CBD800", +"2$ c #8AC500", +"3$ c #2FA900", +"4$ c #008C11", +"5$ c #00B020", +"6$ c #00C914", +"7$ c #07D000", +"8$ c #0CC800", +"9$ c #09BE00", +"0$ c #02CD00", +"a$ c #01CB00", +"b$ c #00C502", +"c$ c #00C402", +"d$ c #00C503", +"e$ c #01C100", +"f$ c #03BB00", +"g$ c #03AE00", +"h$ c #03AA00", +"i$ c #02B700", +"j$ c #C5B100", +"k$ c #BDAD00", +"l$ c #C4B400", +"m$ c #CBB300", +"n$ c #CDB300", +"o$ c #D0B500", +"p$ c #D5BB00", +"q$ c #D9C100", +"r$ c #DAC300", +"s$ c #E7B200", +"t$ c #EEC000", +"u$ c #EAC800", +"v$ c #DECC00", +"w$ c #D3D800", +"x$ c #B1DB00", +"y$ c #66C000", +"z$ c #1AA100", +"A$ c #00AD13", +"B$ c #00C01C", +"C$ c #00CE08", +"D$ c #00D600", +"E$ c #03CD00", +"F$ c #0EC500", +"G$ c #10C000", +"H$ c #03C900", +"I$ c #00C501", +"J$ c #00C002", +"K$ c #00C202", +"L$ c #00C301", +"M$ c #00BE00", +"N$ c #01BE00", +"O$ c #02AA00", +"P$ c #01A600", +"Q$ c #02B400", +"R$ c #B39900", +"S$ c #C1A800", +"T$ c #CAB200", +"U$ c #D0B700", +"V$ c #D4BC00", +"W$ c #D7BE00", +"X$ c #DABF00", +"Y$ c #DBC100", +"Z$ c #EEAE00", +"`$ c #EAB700", +" % c #E1C400", +".% c #D4D400", +"+% c #B6D800", +"@% c #85CC00", +"#% c #46BB00", +"$% c #10AF00", +"%% c #00CD16", +"&% c #00C60B", +"*% c #06BF00", +"=% c #0DC600", +"-% c #02C600", +";% c #01C200", +">% c #00C001", +",% c #00BD03", +"'% c #00BE02", +")% c #00C000", +"!% c #00BF00", +"~% c #00BB00", +"{% c #02BB00", +"]% c #02A500", +"^% c #02A300", +"/% c #04B400", +"(% c #A98100", +"_% c #BC9B00", +":% c #CAB000", +"<% c #D3BC00", +"[% c #FBAE00", +"}% c #E5B000", +"|% c #D3C100", +"1% c #BFD700", +"2% c #90D300", +"3% c #4CBC00", +"4% c #21B900", +"5% c #01C800", +"6% c #00D80F", +"7% c #00DF05", +"8% c #08D300", +"9% c #06CB00", +"0% c #01BC00", +"a% c #02C500", +"b% c #01C000", +"c% c #00BD01", +"d% c #00BB01", +"e% c #00BC00", +"f% c #00BD00", +"g% c #00B900", +"h% c #03B900", +"i% c #03B200", +"j% c #03A200", +"k% c #019F00", +"l% c #03B100", +"m% c #A98300", +"n% c #DEC800", +"o% c #CAB300", +"p% c #DDC000", +"q% c #E0BE00", +"r% c #DBB500", +"s% c #ECA300", +"t% c #E4B400", +"u% c #CAC900", +"v% c #9BCF00", +"w% c #57C200", +"x% c #1DB800", +"y% c #04C100", +"z% c #00D40D", +"A% c #00D30C", +"B% c #00D507", +"C% c #00D400", +"D% c #00C604", +"E% c #00C40B", +"F% c #02C900", +"G% c #02C700", +"H% c #03C400", +"I% c #03BF00", +"J% c #00BA02", +"K% c #00B902", +"L% c #00BF01", +"M% c #00B700", +"N% c #00B800", +"O% c #00AC00", +"P% c #00A600", +"Q% c #029E00", +"R% c #029A00", +"S% c #01B000", +"T% c #003A82", +"U% c #CB9800", +"V% c #BEA400", +"W% c #CCB700", +"X% c #D6B800", +"Y% c #DBB600", +"Z% c #DEB200", +"`% c #DEA400", +" & c #D1B500", +".& c #A8C300", +"+& c #6EC200", +"@& c #2EBC00", +"#& c #08C500", +"$& c #00D206", +"%& c #00D509", +"&& c #00D104", +"*& c #04CB00", +"=& c #00C708", +"-& c #00C70F", +";& c #03C600", +">& c #04C200", +",& c #02BD00", +"'& c #00BA01", +")& c #00B702", +"!& c #00B300", +"~& c #02A900", +"{& c #039F00", +"]& c #029700", +"^& c #03AC00", +"/& c #00387D", +"(& c #003D91", +"_& c #0035D2", +":& c #B59B00", +"<& c #BFAA00", +"[& c #D2B500", +"}& c #D5AF00", +"|& c #DEB000", +"1& c #B6BE00", +"2& c #82C000", +"3& c #43B600", +"4& c #12BD00", +"5& c #00D503", +"6& c #00DF0E", +"7& c #00D406", +"8& c #00D308", +"9& c #00CC01", +"0& c #08C700", +"a& c #0BC400", +"b& c #00C70A", +"c& c #00C910", +"d& c #01C400", +"e& c #00B501", +"f& c #00B502", +"g& c #00B602", +"h& c #00B601", +"i& c #00B100", +"j& c #01B500", +"k& c #03A500", +"l& c #019800", +"m& c #038F00", +"n& c #00A100", +"o& c #00469B", +"p& c #0047A8", +"q& c #003DC9", +"r& c #0048BF", +"s& c #0065BA", +"t& c #C2A900", +"u& c #C0AF00", +"v& c #D5B900", +"w& c #D2AF00", +"x& c #D5AB00", +"y& c #ACC700", +"z& c #8CC100", +"A& c #5DB900", +"B& c #26B500", +"C& c #06C400", +"D& c #00DB11", +"E& c #00DF13", +"F& c #00D407", +"G& c #00CF04", +"H& c #04C900", +"I& c #0DC300", +"J& c #0BC000", +"K& c #00C303", +"L& c #00C609", +"M& c #00C70C", +"N& c #03BE00", +"O& c #01B900", +"P& c #00B301", +"Q& c #00B201", +"R& c #00B402", +"S& c #00B400", +"T& c #00AD00", +"U& c #01AF00", +"V& c #00A500", +"W& c #029D00", +"X& c #029400", +"Y& c #028D00", +"Z& c #01A300", +"`& c #004FAC", +" * c #0050C2", +".* c #0053EA", +"+* c #004EC7", +"@* c #0050AE", +"#* c #0065B7", +"$* c #C1AB00", +"%* c #BBA800", +"&* c #D1B600", +"** c #CFAF00", +"=* c #CFAB00", +"-* c #7ED300", +";* c #5EC000", +">* c #31AF00", +",* c #15BA00", +"'* c #00CA08", +")* c #00D211", +"!* c #00D10C", +"~* c #00CE07", +"{* c #06C700", +"]* c #0FC100", +"^* c #0FBF00", +"/* c #0ABE00", +"(* c #00C204", +"_* c #00C507", +":* c #03B800", +"<* c #01B200", +"[* c #00B101", +"}* c #00AF01", +"|* c #00AE00", +"1* c #00A800", +"2* c #029500", +"3* c #018E00", +"4* c #01A400", +"5* c #0052B5", +"6* c #0057D3", +"7* c #005FF6", +"8* c #0066FF", +"9* c #005EE3", +"0* c #0051BD", +"a* c #0054BD", +"b* c #C5AF00", +"c* c #A68F00", +"d* c #C0A500", +"e* c #C6AC00", +"f* c #CCAF00", +"g* c #54DB00", +"h* c #34C200", +"i* c #16B600", +"j* c #00D00F", +"k* c #00CD13", +"l* c #00C80B", +"m* c #00CD09", +"n* c #05C400", +"o* c #0AC000", +"p* c #0CBE00", +"q* c #08BD00", +"r* c #00AD02", +"s* c #00AE01", +"t* c #00A900", +"u* c #039C00", +"v* c #018F00", +"w* c #018500", +"x* c #039400", +"y* c #0054BA", +"z* c #005FE3", +"A* c #006FFF", +"B* c #0065FF", +"C* c #005FFA", +"D* c #0055D9", +"E* c #004CBC", +"F* c #0056BF", +"G* c #B39400", +"H* c #B09700", +"I* c #BCA600", +"J* c #C5B200", +"K* c #2ADA00", +"L* c #1ACD00", +"M* c #0FC600", +"N* c #00CE12", +"O* c #00CB18", +"P* c #00C810", +"Q* c #00CA09", +"R* c #06C100", +"S* c #08BE00", +"T* c #07BD00", +"U* c #02B300", +"V* c #02AF00", +"W* c #00AC02", +"X* c #00AA00", +"Y* c #01AB00", +"Z* c #01A100", +"`* c #039900", +" = c #009C00", +".= c #0062E9", +"+= c #0068FF", +"@= c #0067FF", +"#= c #005AFD", +"$= c #004DC5", +"%= c #00499B", +"&= c #004574", +"*= c #A78E00", +"== c #B4A200", +"-= c #C3B200", +";= c #0AD200", +">= c #0CD100", +",= c #09CE00", +"'= c #00C511", +")= c #00C614", +"!= c #00C50F", +"~= c #05C200", +"{= c #05C000", +"]= c #04BD00", +"^= c #05BB00", +"/= c #04BA00", +"(= c #00BA00", +"_= c #02AE00", +":= c #00AB00", +"<= c #00A400", +"[= c #009B00", +"}= c #039B00", +"|= c #019B00", +"1= c #02D400", +"2= c #007AFF", +"3= c #0062F5", +"4= c #005FF3", +"5= c #0060F1", +"6= c #0063F1", +"7= c #0065F0", +"8= c #0063F2", +"9= c #005DF4", +"0= c #0051F6", +"a= c #0044F6", +"b= c #0039F9", +"c= c #003DFF", +"d= c #72A900", +"e= c #74A500", +"f= c #00C411", +"g= c #00C611", +"h= c #00C712", +"i= c #00C80D", +"j= c #00C60A", +"k= c #00C206", +"l= c #00BF04", +"m= c #03BC00", +"n= c #06B900", +"o= c #06B500", +"p= c #05B600", +"q= c #01B600", +"r= c #00B903", +"s= c #13AA00", +"t= c #10AA00", +"u= c #0AAB00", +"v= c #06AC00", +"w= c #00AF06", +"x= c #00AD06", +"y= c #00A806", +"z= c #009F01", +"A= c #058E00", +"B= c #0A7E00", +"C= c #0F7400", +"D= c #176E00", +"E= c #0068EE", +"F= c #0057D6", +"G= c #005FF5", +"H= c #0063F5", +"I= c #0065F3", +"J= c #0069F1", +"K= c #0069F2", +"L= c #0063F4", +"M= c #005CF7", +"N= c #004FF7", +"O= c #0042EB", +"P= c #0039D8", +"Q= c #003FD5", +"R= c #54B200", +"S= c #519600", +"T= c #00C80F", +"U= c #00C90E", +"V= c #05BD00", +"W= c #00B305", +"X= c #00B405", +"Y= c #00B705", +"Z= c #08A900", +"`= c #07A800", +" - c #06AA00", +".- c #00B006", +"+- c #00AA08", +"@- c #009E06", +"#- c #008D00", +"$- c #079200", +"%- c #119D00", +"&- c #1A9F00", +"*- c #0055CD", +"=- c #0056D9", +"-- c #005BF0", +";- c #0068F7", +">- c #0069F5", +",- c #006CF2", +"'- c #006EF2", +")- c #006FF3", +"!- c #006CF6", +"~- c #0068F8", +"{- c #005FFC", +"]- c #0056F7", +"^- c #004EE3", +"/- c #0049C1", +"(- c #0058AD", +"_- c #0091AF", +":- c #0B7A00", +"<- c #00C404", +"[- c #00C509", +"}- c #00C70D", +"|- c #00B30C", +"1- c #00B212", +"2- c #00B411", +"3- c #00B505", +"4- c #00AA04", +"5- c #00A606", +"6- c #009F09", +"7- c #00980B", +"8- c #009009", +"9- c #008402", +"0- c #079500", +"a- c #10A200", +"b- c #0048B9", +"c- c #0053E1", +"d- c #0056F0", +"e- c #0068FC", +"f- c #0069F8", +"g- c #0070F5", +"h- c #006FF6", +"i- c #006CFA", +"j- c #005BB5", +"k- c #00699D", +"l- c #00A3A9", +"m- c #009971", +"n- c #00854D", +"o- c #00BE03", +"p- c #00C105", +"q- c #00B709", +"r- c #00B415", +"s- c #00B219", +"t- c #00B213", +"u- c #00B504", +"v- c #00B406", +"w- c #00B30A", +"x- c #00990D", +"y- c #008A0E", +"z- c #00850E", +"A- c #008A06", +"B- c #048F00", +"C- c #0043B6", +"D- c #0051E8", +"E- c #0057F5", +"F- c #0062FF", +"G- c #0065FE", +"H- c #006AFB", +"I- c #006EF9", +"J- c #0070F7", +"K- c #0070F8", +"L- c #006DFC", +"M- c #006AFF", +"N- c #0063FD", +"O- c #0067F5", +"P- c #006EE7", +"Q- c #0068BA", +"R- c #0077AA", +"S- c #00768D", +"T- c #008E96", +"U- c #00B019", +"V- c #00B311", +"W- c #00B906", +"X- c #03C300", +"Y- c #00B60D", +"Z- c #00B315", +"`- c #00B114", +" ; c #00B20C", +".; c #00B500", +"+; c #00B206", +"@; c #00AD0A", +"#; c #00A10D", +"$; c #009211", +"%; c #008811", +"&; c #00850F", +"*; c #0046C2", +"=; c #0056EF", +"-; c #005CFF", +";; c #0058FF", +">; c #005AFF", +",; c #0063FF", +"'; c #006CF9", +"); c #006BFC", +"!; c #0069FF", +"~; c #005FFF", +"{; c #0063FE", +"]; c #006AFE", +"^; c #0068DE", +"/; c #0073D4", +"(; c #006AB5", +"_; c #0080C2", +":; c #009B47", +"<; c #00A333", +"[; c #00AB13", +"}; c #05B800", +"|; c #00B60A", +"1; c #00B310", +"2; c #00B40F", +"3; c #00B000", +"4; c #00A700", +"5; c #009705", +"6; c #009509", +"7; c #009A10", +"8; c #0046C4", +"9; c #004ED5", +"0; c #004DFF", +"a; c #0051FF", +"b; c #0057FF", +"c; c #0060FF", +"d; c #0066FA", +"e; c #0069F9", +"f; c #0067FA", +"g; c #0061FF", +"h; c #005DFF", +"i; c #005CFD", +"j; c #005BEA", +"k; c #0070FC", +"l; c #006CDF", +"m; c #006DD9", +"n; c #008881", +"o; c #009062", +"p; c #009C2E", +"q; c #05B700", +"r; c #00B704", +"s; c #00B30E", +"t; c #00B20F", +"u; c #00B304", +"v; c #00A601", +"w; c #00A208", +"x; c #169D00", +"y; c #1B9A00", +"z; c #159700", +"A; c #119800", +"B; c #0047BF", +"C; c #0059E4", +"D; c #005BFE", +"E; c #0048FF", +"F; c #0053FF", +"G; c #0063FC", +"H; c #0065F9", +"I; c #0064F8", +"J; c #0061FC", +"K; c #005BFF", +"L; c #0054FC", +"M; c #0054FF", +"N; c #0057FB", +"O; c #005DF7", +"P; c #0050DE", +"Q; c #0064F1", +"R; c #0076B8", +"S; c #007F95", +"T; c #008D5B", +"U; c #009C20", +"V; c #0AAC00", +"W; c #07B200", +"X; c #04B500", +"Y; c #00B403", +"Z; c #00B20A", +"`; c #00B207", +" > c #00B200", +".> c #02AC00", +"+> c #08A600", +"@> c #049C00", +"#> c #009403", +"$> c #3C9400", +"%> c #449B00", +"&> c #429C00", +"*> c #3D9500", +"=> c #0044B0", +"-> c #0064F4", +";> c #0077FF", +">> c #004EFF", +",> c #0066FC", +"'> c #0067F8", +")> c #0066F7", +"!> c #0062F7", +"~> c #005DFC", +"{> c #0056F9", +"]> c #005AF4", +"^> c #005CF3", +"/> c #0063DC", +"(> c #006ABC", +"_> c #007689", +":> c #008351", +"<> c #00921F", +"[> c #009F04", +"}> c #04AB00", +"|> c #00B701", +"1> c #069E00", +"2> c #119900", +"3> c #169500", +"4> c #179100", +"5> c #668A00", +"6> c #729400", +"7> c #729500", +"8> c #719800", +"9> c #004EBE", +"0> c #0069F4", +"a> c #005EFF", +"b> c #0067FB", +"c> c #0065F2", +"d> c #0066F0", +"e> c #006FF9", +"f> c #0070FF", +"g> c #0064F5", +"h> c #0059F7", +"i> c #0058F5", +"j> c #0058F4", +"k> c #005AF2", +"l> c #005DF1", +"m> c #005FF1", +"n> c #005EF0", +"o> c #0060DF", +"p> c #0067BC", +"q> c #007290", +"r> c #007F5C", +"s> c #008F2A", +"t> c #009F02", +"u> c #03AB00", +"v> c #02B200", +"w> c #00AF00", +"x> c #03A700", +"y> c #059D00", +"z> c #0D9300", +"A> c #219000", +"B> c #339200", +"C> c #409800", +"D> c #908800", +"E> c #9B9300", +"F> c #9D9600", +"G> c #9D9400", +"H> c #0042A3", +"I> c #004DB6", +"J> c #0063E0", +"K> c #0074FF", +"L> c #0067F6", +"M> c #0068F5", +"N> c #006BF4", +"O> c #0071F9", +"P> c #0078FD", +"Q> c #0074F7", +"R> c #0068EC", +"S> c #005DE3", +"T> c #0068F1", +"U> c #0065EE", +"V> c #0061ED", +"W> c #0060EC", +"X> c #0060EB", +"Y> c #0060EA", +"Z> c #0060E8", +"`> c #0061E8", +" , c #005BEE", +"., c #005BED", +"+, c #005CE5", +"@, c #0061C8", +"#, c #006D98", +"$, c #007C57", +"%, c #009219", +"&, c #05A200", +"*, c #0DB400", +"=, c #04A700", +"-, c #069A00", +";, c #008A00", +">, c #128200", +",, c #328700", +"', c #529200", +"), c #659C00", +"!, c #B79000", +"~, c #BF9900", +"{, c #C09C00", +"], c #BD9900", +"^, c #003484", +"/, c #00459D", +"(, c #0054B6", +"_, c #0066D9", +":, c #006BE8", +"<, c #0074EF", +"[, c #0074EC", +"}, c #006FE7", +"|, c #006DE9", +"1, c #006EED", +"2, c #0070EF", +"3, c #006FF1", +"4, c #006AEF", +"5, c #0069F0", +"6, c #0065EA", +"7, c #0064E7", +"8, c #0062E5", +"9, c #0062E2", +"0, c #0063E1", +"a, c #0064E1", +"b, c #0057EE", +"c, c #0054F5", +"d, c #005AC1", +"e, c #006A81", +"f, c #008038", +"g, c #009201", +"h, c #0CA000", +"i, c #0B9300", +"j, c #0A8200", +"k, c #217A00", +"l, c #4E8200", +"m, c #719300", +"n, c #8CA300", +"o, c #D09C00", +"p, c #D5A400", +"q, c #D4A300", +"r, c #CA9E00", +"s, c #B79300", +"t, c #0055BE", +"u, c #0057B8", +"v, c #0058B0", +"w, c #005CB7", +"x, c #0061C1", +"y, c #0068CF", +"z, c #0072E0", +"A, c #0075EA", +"B, c #0071EC", +"C, c #006CEE", +"D, c #006DF5", +"E, c #0071FD", +"F, c #0067F2", +"G, c #0064EC", +"H, c #0063E2", +"I, c #0064DF", +"J, c #0064DE", +"K, c #0065DE", +"L, c #005CDB", +"M, c #0058E4", +"N, c #0052F2", +"O, c #004EEE", +"P, c #004FD3", +"Q, c #005CA1", +"R, c #006D61", +"S, c #007E2B", +"T, c #008B06", +"U, c #099000", +"V, c #148D00", +"W, c #268700", +"X, c #468100", +"Y, c #6D8700", +"Z, c #929600", +"`, c #B4AA00", +" ' c #D9A800", +".' c #D9AC00", +"+' c #D4A900", +"@' c #C8A100", +"#' c #B69400", +"$' c #006FD0", +"%' c #006CC5", +"&' c #0057B6", +"*' c #0055B6", +"=' c #0056BD", +"-' c #005CCD", +";' c #0062E3", +">' c #0060F3", +",' c #0057ED", +"'' c #005CF6", +")' c #005AF3", +"!' c #005BEC", +"~' c #005DE8", +"{' c #0061DF", +"]' c #0061DE", +"^' c #0062DB", +"/' c #005ED5", +"(' c #005ADA", +"_' c #0054DD", +":' c #004EDA", +"<' c #004BD0", +"[' c #0052B4", +"}' c #005D91", +"|' c #006965", +"1' c #007038", +"2' c #007B0F", +"3' c #1D8A00", +"4' c #479300", +"5' c #668E00", +"6' c #8A8F00", +"7' c #B29A00", +"8' c #CFA300", +"9' c #D3B200", +"0' c #CDAD00", +"a' c #C0A100", +"b' c #B19700", +"c' c #0048C2", +"d' c #0044BA", +"e' c #0047CC", +"f' c #004BE4", +"g' c #004EF4", +"h' c #004DFA", +"i' c #004CF8", +"j' c #004BF3", +"k' c #004BED", +"l' c #004FE7", +"m' c #0054E2", +"n' c #0058DE", +"o' c #005CDC", +"p' c #005FD8", +"q' c #005CCF", +"r' c #0058C6", +"s' c #0052BE", +"t' c #004CBD", +"u' c #004BBE", +"v' c #004EB7", +"w' c #00579C", +"x' c #00616A", +"y' c #00712B", +"z' c #2C8F00", +"A' c #569A00", +"B' c #799900", +"C' c #A59D00", +"D' c #C89C00", +"E' c #D99000", +"F' c #C6B700", +"G' c #C0B000", +"H' c #B6A500", +"I' c #AF9900", +"J' c #003BDE", +"K' c #003DE4", +"L' c #003DF3", +"M' c #003EF7", +"N' c #003EF5", +"O' c #0041EE", +"P' c #0045E8", +"Q' c #004BE2", +"R' c #0051DC", +"S' c #0057DC", +"T' c #0057D9", +"U' c #005EDA", +"V' c #005ECB", +"W' c #005BB6", +"X' c #0054A9", +"Y' c #004CAE", +"Z' c #0048C1", +"`' c #0048CD", +" ) c #004EBF", +".) c #005B90", +"+) c #006D3F", +"@) c #339300", +"#) c #61A300", +"$) c #87A400", +"%) c #B7A900", +"&) c #D49B00", +"*) c #DB8100", +"=) c #C1BC00", +"-) c #BDB900", +";) c #B8B100", +">) c #B0A600", +",) c #AE9C00", +"') c #003BAB", +")) c #002FA1", +"!) c #0030BA", +"~) c #0033CD", +"{) c #0042ED", +"]) c #0046D7", +"^) c #0052D2", +"/) c #005BCC", +"() c #0068CD", +"_) c #005FBC", +":) c #0052DC", +"<) c #0051D5", +"[) c #004FCC", +"}) c #0049C9", +"|) c #0045CC", +"1) c #0042D4", +"2) c #0040D5", +"3) c #0044C3", +"4) c #004594", +"5) c #005A68", +"6) c #0A6400", +"7) c #658D00", +"8) c #949800", +"9) c #BEA000", +"0) c #E3A200", +"a) c #D88B00", +"b) c #CB9000", +"c) c #C8AF00", +"d) c #C2B800", +"e) c #C0B900", +"f) c #A9A300", +"g) c #0039BC", +"h) c #0031AA", +"i) c #0036B2", +"j) c #0048D3", +"k) c #0056D8", +"l) c #0052BD", +"m) c #0062C8", +"n) c #0051D6", +"o) c #004AC6", +"p) c #0046C9", +"q) c #0042CF", +"r) c #0041D1", +"s) c #00449B", +"t) c #004E70", +"u) c #005D17", +"v) c #5D8B00", +"w) c #8A8D00", +"x) c #BC9E00", +"y) c #D69B00", +"z) c #DF9400", +"A) c #CC9100", +"B) c #CFA500", +"C) c #C7AE00", +"D) c #C0B500", +"E) c #BBB700", +"F) c #A7A100", +"G) c #003FAA", +"H) c #0040A8", +"I) c #0057C1", +"J) c #005CC9", +"K) c #0069D6", +"L) c #0051DB", +"M) c #004AC5", +"N) c #0045CB", +"O) c #0045CD", +"P) c #0045AA", +"Q) c #00437A", +"R) c #00544F", +"S) c #7A7E00", +"T) c #B69700", +"U) c #CA9700", +"V) c #E3A000", +"W) c #CC9400", +"X) c #CEA500", +"Y) c #BDB200", +"Z) c #B8B400", +"`) c #A29C00", +" ! c #005AC9", +".! c #004FB2", +"+! c #0055B3", +"@! c #005ABB", +"#! c #004FB4", +"$! c #0051D1", +"%! c #004FC9", +"&! c #0049C2", +"*! c #0047C1", +"=! c #0045C7", +"-! c #0045C5", +";! c #0044B9", +">! c #003B8E", +",! c #00417A", +"'! c #686900", +")! c #A58A00", +"!! c #C79C00", +"~! c #DCA600", +"{! c #CC9800", +"]! c #CEA600", +"^! c #BAB000", +"/! c #B3AE00", +"(! c #9D9900", +"_! c #004F8C", +":! c #004F92", +"~ c #BCAA00", +",~ c #B1A600", +"'~ c #A7A200", +")~ c #169F00", +"!~ c #0F7300", +"~~ c #138800", +"{~ c #00742F", +"]~ c #006B40", +"^~ c #00595B", +"/~ c #005180", +"(~ c #004EA2", +"_~ c #0045AD", +":~ c #0049BA", +"<~ c #0045BB", +"[~ c #003AAE", +"}~ c #0034A2", +"|~ c #002473", +"1~ c #6A6300", +"2~ c #BFA800", +"3~ c #BB9A00", +"4~ c #C3A700", +"5~ c #B5A300", +"6~ c #A89D00", +"7~ c #A69C00", +"8~ c #167600", +"9~ c #198500", +"0~ c #008613", +"a~ c #007A27", +"b~ c #006644", +"c~ c #005A6D", +"d~ c #00508F", +"e~ c #00439F", +"f~ c #0046AF", +"g~ c #0047B6", +"h~ c #0044B3", +"i~ c #003CA6", +"j~ c #003694", +"k~ c #002865", +"l~ c #666200", +"m~ c #958200", +"n~ c #B69D00", +"o~ c #C1A000", +"p~ c #C3A800", +"q~ c #AA9D00", +"r~ c #0E6E00", +"s~ c #0F7000", +"t~ c #039200", +"u~ c #00830F", +"v~ c #00722D", +"w~ c #006354", +"x~ c #005173", +"y~ c #00478D", +"z~ c #0042A0", +"A~ c #0049B3", +"B~ c #0042AE", +"C~ c #0040A4", +"D~ c #003788", +"E~ c #002756", +"F~ c #696300", +"G~ c #937F00", +"H~ c #AD9200", +"I~ c #BBA200", +"J~ c #AF9A00", +"K~ c #A79700", +"L~ c #006200", +"M~ c #005800", +"N~ c #0A9600", +"O~ c #008803", +"P~ c #007917", +"Q~ c #006C37", +"R~ c #005A57", +"S~ c #00547C", +"T~ c #00438F", +"U~ c #0049AE", +"V~ c #0040AD", +"W~ c #003FA6", +"X~ c #003584", +"Y~ c #00264F", +"Z~ c #686200", +"`~ c #937E00", +" { c #A98C00", +".{ c #BC9F00", +"+{ c #B59C00", +"@{ c #AC9600", +"#{ c #A79300", +"${ c #006009", +"%{ c #004707", +"&{ c #039100", +"*{ c #008900", +"={ c #007F07", +"-{ c #00761B", +";{ c #006437", +">{ c #006068", +",{ c #00477F", +"'{ c #0044A4", +"){ c #003EB2", +"!{ c #003BB1", +"~{ c #002F87", +"{{ c #002451", +"]{ c #696100", +"^{ c #967D00", +"/{ c #A98A00", +"({ c #BCA000", +"_{ c #B39A00", +":{ c #AF9700", +"<{ c #AB9300", +"[{ c #003E09", +"}{ c #008D0B", +"|{ c #008A05", +"1{ c #028600", +"2{ c #007B02", +"3{ c #006C18", +"4{ c #007352", +"5{ c #004F74", +"6{ c #003C98", +"7{ c #003BB8", +"8{ c #0039BD", +"9{ c #002B91", +"0{ c #002159", +"a{ c #685E00", +"b{ c #AD8A00", +"c{ c #B79B00", +"d{ c #B29800", +"e{ c #B49900", +"f{ c #033E00", +"g{ c #008716", +"h{ c #00890A", +"i{ c #058800", +"j{ c #098400", +"k{ c #027500", +"l{ c #008A3F", +"m{ c #00566D", +"n{ c #003C94", +"o{ c #0037BD", +"p{ c #0034C6", +"q{ c #00279A", +"r{ c #001F65", +"s{ c #665A00", +"t{ c #B08C00", +"u{ c #B09600", +"v{ c #B29600", +"w{ c #B99E00", +"x{ c #0F4600", +"y{ c #00811A", +"z{ c #00870E", +"A{ c #018600", +"B{ c #058100", +"C{ c #0E7E00", +"D{ c #009E31", +"E{ c #005D6A", +"F{ c #00439B", +"G{ c #0035C0", +"H{ c #0031CC", +"I{ c #0025A0", +"J{ c #001C6A", +"K{ c #645700", +"L{ c #977B00", +"M{ c #B28E00", +"N{ c #B69C00", +"O{ c #C4A600", +"P{ c #007600", +"Q{ c #029000", +"R{ c #008411", +"S{ c #00761C", +"T{ c #00570E", +"U{ c #00362F", +"V{ c #002C58", +"W{ c #004295", +"X{ c #003AA0", +"Y{ c #004097", +"Z{ c #00255B", +"`{ c #856300", +" ] c #B39300", +".] c #A79000", +"+] c #666F00", +"@] c #778200", +"#] c #1B9C00", +"$] c #007C00", +"%] c #008B18", +"&] c #00791B", +"*] c #005B0E", +"=] c #005A06", +"-] c #002F5A", +";] c #003F90", +">] c #0042A6", +",] c #003D8F", +"'] c #00265C", +")] c #604200", +"!] c #8D6900", +"~] c #AF9000", +"{] c #A89300", +"]] c #717C00", +"^] c #24C600", +"/] c #006900", +"(] c #008114", +"_] c #007817", +":] c #005C0A", +"<] c #005E0B", +"[] c #00355A", +"}] c #003887", +"|] c #003883", +"1] c #583F00", +"2] c #936D00", +"3] c #A48800", +"4] c #AB9700", +"5] c #026C00", +"6] c #00690A", +"7] c #007412", +"8] c #006209", +"9] c #00650D", +"0] c #003C5D", +"a] c #003781", +"b] c #003990", +"c] c #003477", +"d] c #584300", +"e] c #8E6D00", +"f] c #9C8000", +"g] c #AE9900", +"h] c #048000", +"i] c #006D0B", +"j] c #006A07", +"k] c #007012", +"l] c #004764", +"m] c #003D81", +"n] c #00317C", +"o] c #002E6D", +"p] c #654E00", +"q] c #856900", +"r] c #9A7E00", +"s] c #B8A200", +"t] c #005A05", +"u] c #006604", +"v] c #006A04", +"w] c #007615", +"x] c #00AB61", +"y] c #005168", +"z] c #003977", +"A] c #002B6C", +"B] c #002354", +"C] c #343300", +"D] c #6D5800", +"E] c #856B00", +"F] c #B49B00", +"G] c #007100", +"H] c #005E00", +"I] c #006300", +"J] c #007717", +"K] c #00A45B", +"L] c #005664", +"M] c #00346C", +"N] c #00245E", +"O] c #001833", +"P] c #463C00", +"Q] c #6F5E00", +"R] c #8F7600", +"S] c #9D8400", +"T] c #055A00", +"U] c #006000", +"V] c #007C1A", +"W] c #00A659", +"X] c #005961", +"Y] c #003462", +"Z] c #001C53", +"`] c #4F4400", +" ^ c #716200", +".^ c #A48D00", +"+^ c #0D8100", +"@^ c #006204", +"#^ c #00741A", +"$^ c #008745", +"%^ c #005A5B", +"&^ c #002C51", +"*^ c #00164E", +"=^ c #150F00", +"-^ c #574B00", +";^ c #8E8400", +">^ c #AB9D00", +",^ c #006906", +"'^ c #005102", +")^ c #008A46", +"!^ c #005953", +"~^ c #002747", +"{^ c #00062D", +"]^ c #231600", +"^^ c #675B00", +"/^ c #A7A300", +"(^ c #005C19", +"_^ c #006C33", +":^ c #004F41", +"<^ c #002138", +"[^ c #05001A", +"}^ c #301A00", +"|^ c #7C7200", +"1^ c #007B28", +"2^ c #004F21", +"3^ c #00402D", +"4^ c #001724", +"5^ c #130014", +"6^ c #005323", +"7^ c #00321B", +"8^ c #001215", +"9^ c #00310F", +" ", +" . + ", +" @ # $ % & * = - ; > ", +" , ' ) ! ~ { ] ^ / ( _ : < [ } | 1 2 3 ", +" 4 5 6 7 8 9 0 a b c d e f g h i j k l m n o p ", +" q r s t u v w x y z A B C D E F G H I J K L M N O P ", +" Q R S T U V W X Y Z ` F ...+.C .@.#.$.%.&. *.=.-.;.>. ", +" ,.'.).!.~.{.].^./.(._.:.:.<.[.}.|.1.2.3.4. 5.6.7.8.9.0.a.b. ", +" c.d.e.f.g.V f W h.i.^.j.k.l.m.n.o.p.q.r.s. t.u.v.w.x.y.z.A.B.C. ", +" D.E.F.G.H.I.J.K.L.M.N._ _ O.P.Q.R.S.T.U. V.W.X.Y.Z.`. +.+++@+#+ ", +" $+%+&+*+=+-+;+>+,+'+)+!+!+~+{+]+^+/+(+_+ :+<+[+}+|+1+2+3+1+4+5+6+7+ ", +" 8+9+F.0+a+b+c+d+e+f+f+g+h+i+j+~+k+l+m+ n+o+p+q+3+r+s+s+3+t+u+v+w+x+ ", +" y+z+A+B+C+D+b+E+H.T.F+G+H+I+J+i+K+L+ M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+`+ @ ", +" .@+@@@#@$@%@b+&@F+*@=@-@;@>@,@'@)@!@ ~@{@]@^@/@(@_@:@t+<@t+[@}@|@1@2@ ", +" 3@4@5@6@7@8@H+9@=+=@0@a@b@c@d@>+H+e@ f@g@R+h@i@j@k@l@m@n@o@p@q@r@s@t@u@ ", +" v@w@x@y@z@A@B@9@C@=@D@E@F@G@H@I@J@K@ L@M@N@3+O@P@2+Q@v+R@S@T@U@V@W@X@Y@Z@`@ ", +" #.#9+s @@+#@###$#%#&#*#f.=#-#;# >#,#'#)#!#~#[@Q@{#]#]#^#Q@[@/#(#:@_#:#<#[# ", +" }#|#1#2#3#6@4#5#6#7#8#v S.9#0#a# b#c#d#e#f#W+~#g#{#h#]#^#q@[@i#Q@:@j#k#l#m# ", +" n#o#p#q#4@r#s#t#D+u#v#w#x#% y#z#A# B#C#D#E#F#G#t+W+g#|@H#I#]#J#X+W+|+K#L#M#N#O# ", +" P#Q#R#'.S#T#U#V#W#X#Y#Z#d+`#A+ $.$ +$@$#$$$%$|+h@&$t+W+g#|+*$I#]#=$-$Q@;$>$,$l#'$O# ", +" )$!$~${$]$^$/$($_$:$<$[$}$|$1$2$3$4$5$6$<@1+7$8$9$W+0$a$-$b$c$d$*$:@-$>$e$f$g$h$i$ ", +" j$k$l$m$n$o$p$q$r$s$t$u$v$w$x$y$z$A$B$C$3+D$E$F$G$~#g#H$I$W@J$K$L$>$K#M$N$k#O$P$Q$ ", +" R$S$T$U$V$W$X$Y$Z$`$ %.%+%@%#%$%%%&%Q@r+V+-$*%=%(#R+-%;%>%,%'%)%!%!%~%{%Q$]%^%/% ", +" (%_%:%<%W$X$r#9 [%}%|%1%2%3%4%5%Z@6%7%D$t+8%9%0%X+5%a%b%c%Y@d%e%M$f%g%h%i%j%k%l% ", +" m%]$n%o%p%q%r%s%t%u%v%w%x%y%z%A%B%C%<@W+X+D%E%F%G%H%I%e%J%K%d%L%M%N%O%P%Q%R%S% ", +" T% U%V%W%w@X%Y%Z%`% &.&+&@&#&W+$&%&&&~#*&H$|+=&-&5%;&>&,&'&K%)&K%N%!&h%l#~&{&]&^& ", +" /&(&_& :&<&D@[&}&|&n$1&2&3&4&5&6&7&8&9&0&a&n@d$b&c&5+d&6+~%M%e&f&g&h&i&j&'$k&l&m&n& ", +" o&p&q&r&s& t&u&Q v&w&x&y&z&A&B&C&D&E&F&G&H&I&G$J&K&L&M&K#e$N&O&j&P&Q&R&S&T&U&V&W&X&Y&Z& ", +" `& *.*+*@*#* $*%*&***=*-*;*>*,*'*)*!*~* +{*]*^*/*)%(*_*'#M${%:*<*[*}*[*|*1*[+^%W&2*3*4* ", +" 5*6*7*8*9*0*a* b*c*d*e*f*g*h*i*n@j*k*l*m*|+n*o*p*q*N&!%!%M$f%g%j&S%|*r*s*T&t*g$k&u*v*w*x* ", +" y*z*A*8*B*C*D*E*F* G*H*I*J*K*L*M*R+N*O*P*Q*5+P+R*S*T*:+{%~%f%~%M%U*V*r*W*T&<*X*Y*Z*`*3*>. = ", +" y*.=A*8*+=@=#=$=%=&= *===-=;=>=,=I#'=)=!=K&d&H%~={=]=^=/=/=e%(=2@<*_=r*W*:=[*P%<=[=}=|=Z*1= ", +" 2=6*3=4=5=6=7=8=9=0=a=b=c= d=e=f=g=h=P*i=j=k=l=m=n=o=p=q=)&r=K%s=t=u=v=r*w=x=y=z=A=B=C=D= ", +" E=F=G=H=I=J=K=K=L=M=N=O=P=Q= R=S=_*j=T=c&U=@+L$e$V=i$!&W=X=Y=g%~%Z=`= -O%.-+-@-#-3*$-%-&- ", +" *-=---;->-,-'-)-!-~-{-]-^-/-(-_- :-d&<-[-}-b&*$:@;%,&M%|-1-2-3-(=~%T&O%4-5-6-7-8-9-0-a- ", +" b-c-d-e-f-!-g-g-h-i-+=@=@=9*j-k-l-m-n-f%o-p-<-;$;$h@'#f%q-r-s-t-u-N%g%v-w-#$x-y-z-A-B- ", +" C-D-E-F-G-H-I-J-K-L-M-+=N-O-P-Q-R-S-T-U-V-W-*%X-d&K#)%J%Y-Z-`- ;!&S&.;+;@;#;$;%;&; ", +" *;=;-;;;>;,;e-';';);!;!;~;{;];^;/;(;_;:;<;[;};/@6+!%e%|;1;2;+;i&3;|*|*4;z=5;6;7; ", +" 8;9;M=8*0;a;b;c;d;e;f;B*B*g;h;i;j;k;l;m;n;o;p;Y*q;f$g%r;s;t;u;!&i&O%v;w;x;y;z;A; ", +" B;C;D;A*E;0;F;-;G;H;I;J;K;L;M;K;N;O;P;Q;R;S;T;U;V;W;X;Y;Z;`; >3;.>+>@>#>$>%>&>*> ", +" =>->8*;>>>a;>;c;,>'>)>!>~>;;M;F;L;{>]>^>/>(>_>:><>[>}>S%!&|>m#}>1>2>3>4>5>6>7>8> ", +" 9>0>+=a>-;g;@=b>c>d>e>f>g>7*h>i>j>k>l>m>n>o>p>q>r>s>t>u>v>w>x>y>z>A>B>C>D>E>F>G> ", +" H>I>J>2=K>L>M>N>O>P>Q>R>S>T>U>V>W>X>Y>Z>`> ,.,+,@,#,$,%,&,*,=,-,;,>,,,',),!,~,{,], ", +" ^,/,(,_,:,<,[,},|,1,2,3,4,5,E=6,7,8,9,0,a,+,b,c,.*d,e,f,g,s=h,i,j,k,l,m,n,o,p,q,r,s, ", +" t,u,v,w,x,y,z,A,B,C,D,E,F,7=G,7,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,`, '.'+'@'#' ", +" $'%'&'*'='-';'Q;>',''')'!'~'9*{']'^'/'('_':'<'['}'|'1'2'3'4'5'6'7'8'9'^$0'a'b' ", +" n>c'd'e'f'g'h'i'j'k'l'm'n'o'L,p'q'r's't'u'v'w'x'y'z'A'B'C'D'E'a#F'G'H'I' ", +" a=J'K'L'M'N'O'P'Q'R'S'T'U'V'W'X'Y'Z'`' ).)+)@)#)$)%)&)*)=)-);)>),) ", +" ')))!)~){)])^)/)()_):)<)[)})|)1)2)3)4)5)6)7)8)9)0)a)b)8'c)d)e)f) ", +" g)h)i)j)k)-'l)m):)n)[)o)p)q)r)3)s)t)u)v)w)x)y)z)A)B)C)D)E)F) ", +" G)G)H)I)J)K)L)n)[)M)8;N)O)3)P)Q)R) S)T)U)V)W)X)3@Y)Z)`) ", +" !.!+!@!#!<)$!%!&!*!8;=!-!;!>!,! '!)!!!~!{!]!$*^!/!(! ", +" _!:!~,~'~ ", +" )~!~~~{~]~^~/~(~_~:~2!<~[~}~|~ 1~=~2~3~4~5~6~7~ ", +" 8~9~0~a~b~c~d~e~f~g~h~i~j~k~ l~m~n~o~p~==q~ ", +" r~s~t~u~v~w~x~y~z~A~B~C~D~E~ F~G~H~a'I~J~K~ ", +" L~M~N~O~P~Q~R~S~T~U~V~W~X~Y~ Z~`~ {.{+{@{#{ ", +" ${%{&{*{={-{;{>{,{'{){!{~{{{ ]{^{/{({_{:{<{ ", +" [{}{|{1{2{3{4{5{6{7{8{9{0{ a{^{b{c{d{e{ ", +" f{g{h{i{j{k{l{m{n{o{p{q{r{ s{^{t{u{v{w{ ", +" x{y{z{A{B{C{D{E{F{G{H{I{J{ K{L{M{R$N{O{ ", +" P{Q{R{S{T{ U{V{W{X{Y{Z{ `{ ].]+]@] ", +" #]$]%]&]*]=] -];]>],]'])]!]~]{]]] ", +" ^]/](]_]:]<] []}]z~|] 1]2]3]4] ", +" 5]6]7]8]9] 0]a]b]c] d]e]f]g] ", +" h]=]i]j]k] l]m]n]o] p]q]r]s] ", +" t]u]v]w]x]y]z]A]B]C]D]E]r]F] ", +" G]H]I]J]K]L]M]N]O]P]Q]R]S] ", +" T]U]V]W]X]Y]Z] `] ^.^ ", +" +^@^#^$^%^&^*^=^-^;^>^ ", +" ,^'^)^!^~^{^]^^^/^ ", +" (^_^:^<^[^}^|^ ", +" 1^2^3^4^5^ ", +" 6^7^8^ ", +" 9^ ", +" ", +" "}; diff --git a/Plugins/org.mitk.gui.qt.xnat/resources/xnat_search_icon.xpm b/Plugins/org.mitk.gui.qt.xnat/resources/xnat_search_icon.xpm index d518391531..0dc5e8b7e2 100644 --- a/Plugins/org.mitk.gui.qt.xnat/resources/xnat_search_icon.xpm +++ b/Plugins/org.mitk.gui.qt.xnat/resources/xnat_search_icon.xpm @@ -1,1600 +1,1600 @@ -/* XPM */ -static char * C:\home\knorr\xnat\src\MITK\Plugins\org_mitk_gui_qt_xnatinterface\resources\xnat_search_icon_xpm[] = { -"77 80 1517 2", -" c None", -". c #E4D800", -"+ c #D8D000", -"@ c #F1E700", -"# c #EEE000", -"$ c #EED700", -"% c #EED800", -"& c #E5D500", -"* c #DDD600", -"= c #D7D700", -"- c #D2D500", -"; c #C8CC00", -"> c #BCB700", -", c #E2DC00", -"' c #E4CA00", -") c #E4D000", -"! c #E5D000", -"~ c #E4CD00", -"{ c #E0B800", -"] c #E2C300", -"^ c #E7D200", -"/ c #EADD00", -"( c #EBDF00", -"_ c #EBE000", -": c #EADC00", -"< c #E8DC00", -"[ c #E4DC00", -"} c #E3E000", -"| c #DEE200", -"1 c #D9DE00", -"2 c #CAD100", -"3 c #B9B500", -"4 c #C7BC00", -"5 c #D1C500", -"6 c #D6C200", -"7 c #D8B900", -"8 c #DAB700", -"9 c #DCBD00", -"0 c #E1C600", -"a c #E3CB00", -"b c #E4CC00", -"c c #E3CA00", -"d c #EEBC00", -"e c #EFC500", -"f c #F1D000", -"g c #F3D900", -"h c #F3DD00", -"i c #F2DF00", -"j c #F2E000", -"k c #F0E100", -"l c #EAE000", -"m c #EBE500", -"n c #E5E500", -"o c #D8D800", -"p c #C3C300", -"q c #DCC200", -"r c #CCBE00", -"s c #D9CA00", -"t c #DFCA00", -"u c #E1C500", -"v c #E5C300", -"w c #E7C900", -"x c #EDCF00", -"y c #EED300", -"z c #EFD400", -"A c #F0D400", -"B c #FFCF00", -"C c #FFD100", -"D c #FFD300", -"E c #FFD600", -"F c #FFDA00", -"G c #FCDE00", -"H c #FBDF00", -"I c #F6E100", -"J c #F2E100", -"K c #EEDF00", -"L c #E0D000", -"M c #C4B300", -"N c #0DAE00", -"O c #036C00", -"P c #037300", -"Q c #DAC500", -"R c #D3C400", -"S c #E6D500", -"T c #E9D200", -"U c #EBCE00", -"V c #EDCD00", -"W c #F2D200", -"X c #F4D600", -"Y c #F6D900", -"Z c #F7DA00", -"` c #F7D900", -" . c #FFD700", -".. c #FFD200", -"+. c #FFCE00", -"@. c #FCDC00", -"#. c #F8DE00", -"$. c #F3DA00", -"%. c #EACB00", -"&. c #D2A900", -"*. c #00B70C", -"=. c #00A50A", -"-. c #009C0B", -";. c #037700", -">. c #038900", -",. c #D2BE00", -"'. c #D0C300", -"). c #E6D700", -"!. c #EBD500", -"~. c #EDD200", -"{. c #F1D100", -"]. c #F4D300", -"^. c #F6D500", -"/. c #F6D600", -"(. c #F7D600", -"_. c #F7D500", -":. c #FBD900", -"<. c #FCD300", -"[. c #FDC900", -"}. c #FBC800", -"|. c #FCCF00", -"1. c #FAD900", -"2. c #F8DC00", -"3. c #EACF00", -"4. c #DCB500", -"5. c #0EB800", -"6. c #01A200", -"7. c #009F08", -"8. c #00A80E", -"9. c #00B813", -"0. c #00C314", -"a. c #017E00", -"b. c #009502", -"c. c #C2B600", -"d. c #DACF00", -"e. c #DFD100", -"f. c #E6D100", -"g. c #E9CE00", -"h. c #F4D500", -"i. c #F5D500", -"j. c #F3DE00", -"k. c #F5DD00", -"l. c #F4D800", -"m. c #F3CD00", -"n. c #F1CA00", -"o. c #F3CF00", -"p. c #F5D700", -"q. c #F2D700", -"r. c #E3C400", -"s. c #C89900", -"t. c #1FAF00", -"u. c #15A800", -"v. c #0BA500", -"w. c #03AF00", -"x. c #00BB05", -"y. c #00C30D", -"z. c #00CA12", -"A. c #00D216", -"B. c #018200", -"C. c #00AE02", -"D. c #BDB300", -"E. c #D7CF00", -"F. c #DED100", -"G. c #E4D100", -"H. c #E8CD00", -"I. c #ECCC00", -"J. c #EECE00", -"K. c #F0D100", -"L. c #F1D300", -"M. c #F2D300", -"N. c #F3D200", -"O. c #EDDD00", -"P. c #EDD800", -"Q. c #EDD400", -"R. c #ECD200", -"S. c #EBD200", -"T. c #E7CD00", -"U. c #CFB100", -"V. c #16E200", -"W. c #0FAC00", -"X. c #0FBC00", -"Y. c #0CBF00", -"Z. c #09C300", -"`. c #03C800", -" + c #00CB03", -".+ c #00CB06", -"++ c #00C807", -"@+ c #00C709", -"#+ c #038000", -"$+ c #B8AF00", -"%+ c #D4CC00", -"&+ c #DED200", -"*+ c #E2CE00", -"=+ c #E6CB00", -"-+ c #E7C800", -";+ c #EBCB00", -">+ c #EDCE00", -",+ c #EDD100", -"'+ c #EED000", -")+ c #EECF00", -"!+ c #E6DE00", -"~+ c #E7DE00", -"{+ c #E8DE00", -"]+ c #E8DB00", -"^+ c #E5D600", -"/+ c #DFCC00", -"(+ c #D6B900", -"_+ c #BA9600", -":+ c #04BC00", -"<+ c #049F00", -"[+ c #01AC00", -"}+ c #01CD00", -"|+ c #00C800", -"1+ c #00D300", -"2+ c #00D900", -"3+ c #00D800", -"4+ c #01CE00", -"5+ c #01C600", -"6+ c #02C000", -"7+ c #037B00", -"8+ c #B5AB00", -"9+ c #D2C900", -"0+ c #E1CC00", -"a+ c #E3C700", -"b+ c #E5C400", -"c+ c #E7C600", -"d+ c #EACC00", -"e+ c #EACE00", -"f+ c #EACD00", -"g+ c #E1CA00", -"h+ c #E5D200", -"i+ c #E8DA00", -"j+ c #E8DF00", -"k+ c #E1D300", -"l+ c #D6BF00", -"m+ c #BD9B00", -"n+ c #099300", -"o+ c #029800", -"p+ c #00B503", -"q+ c #00D006", -"r+ c #00DD00", -"s+ c #00E600", -"t+ c #00D000", -"u+ c #07CE00", -"v+ c #0AC700", -"w+ c #0CBB00", -"x+ c #057000", -"y+ c #B6A900", -"z+ c #D2C600", -"A+ c #DDCD00", -"B+ c #E1C800", -"C+ c #E2C200", -"D+ c #E3C100", -"E+ c #E8CA00", -"F+ c #DBC200", -"G+ c #CDB600", -"H+ c #000000", -"I+ c #817800", -"J+ c #6F6900", -"K+ c #696000", -"L+ c #3F3700", -"M+ c #1C9E00", -"N+ c #118900", -"O+ c #08A800", -"P+ c #04C400", -"Q+ c #00C602", -"R+ c #00C900", -"S+ c #00E100", -"T+ c #00E000", -"U+ c #00E300", -"V+ c #00DC00", -"W+ c #00CF00", -"X+ c #00CA00", -"Y+ c #05CA00", -"Z+ c #08C200", -"`+ c #07B500", -" @ c #0A6200", -".@ c #BEAA00", -"+@ c #D5C400", -"@@ c #DCC700", -"#@ c #E0C500", -"$@ c #E2BF00", -"%@ c #E4C000", -"&@ c #3D3600", -"*@ c #574E00", -"=@ c #252302", -"-@ c #0F1102", -";@ c #1D1F06", -">@ c #060600", -",@ c #0C0D03", -"'@ c #000304", -")@ c #000101", -"!@ c #000102", -"~@ c #11B000", -"{@ c #0FB700", -"]@ c #0BB600", -"^@ c #08B300", -"/@ c #05BE00", -"(@ c #03D000", -"_@ c #01D300", -":@ c #00C500", -"<@ c #00D200", -"[@ c #00CD00", -"}@ c #00CC02", -"|@ c #00C901", -"1@ c #00C000", -"2@ c #00B400", -"3@ c #C5AC00", -"4@ c #D8C300", -"5@ c #D8C100", -"6@ c #DFC200", -"7@ c #E2BE00", -"8@ c #D1AC00", -"9@ c #816E00", -"0@ c #080700", -"a@ c #090900", -"b@ c #000203", -"c@ c #0B7902", -"d@ c #05DE00", -"e@ c #00C400", -"f@ c #08D400", -"g@ c #0CDB00", -"h@ c #0CD000", -"i@ c #0AC900", -"j@ c #0BCC00", -"k@ c #08C400", -"l@ c #06C200", -"m@ c #02C400", -"n@ c #00CB02", -"o@ c #00CB07", -"p@ c #00B708", -"q@ c #00A908", -"r@ c #CAAD00", -"s@ c #D9C200", -"t@ c #D8BE00", -"u@ c #D6B700", -"v@ c #897200", -"w@ c #473B00", -"x@ c #1A1600", -"y@ c #0C1209", -"z@ c #232911", -"A@ c #26270B", -"B@ c #3D3F0E", -"C@ c #585B14", -"D@ c #7D831D", -"E@ c #899021", -"F@ c #74791F", -"G@ c #001216", -"H@ c #00323E", -"I@ c #00262E", -"J@ c #002E28", -"K@ c #009001", -"L@ c #00E700", -"M@ c #00CB00", -"N@ c #13C500", -"O@ c #14BE00", -"P@ c #13BD00", -"Q@ c #0CB500", -"R@ c #05B200", -"S@ c #00B601", -"T@ c #00B808", -"U@ c #00AC02", -"V@ c #009F04", -"W@ c #00900B", -"X@ c #AB9D00", -"Y@ c #C8BD00", -"Z@ c #373400", -"`@ c #283013", -" # c #748025", -".# c #D69C00", -"+# c #EBAB00", -"@# c #EEAE00", -"## c #C0AF00", -"$# c #13811D", -"%# c #0B8430", -"&# c #006D3A", -"*# c #005A3E", -"=# c #00C700", -"-# c #00C402", -";# c #00BF02", -"># c #00C002", -",# c #00C300", -"'# c #00C200", -")# c #00B900", -"!# c #00A200", -"~# c #009700", -"{# c #019500", -"]# c #018C00", -"^# c #029000", -"/# c #029500", -"(# c #A69900", -"_# c #8D8801", -":# c #3F4917", -"<# c #818921", -"[# c #00A90E", -"}# c #06C100", -"|# c #007004", -"1# c #00AE01", -"2# c #009102", -"3# c #009301", -"4# c #009800", -"5# c #00A000", -"6# c #00A300", -"7# c #039F00", -"8# c #019600", -"9# c #019E00", -"0# c #01AA00", -"a# c #7B7302", -"b# c #313A0F", -"c# c #010201", -"d# c #070A04", -"e# c #34390E", -"f# c #7F841C", -"g# c #CFAF00", -"h# c #A8AB02", -"i# c #009904", -"j# c #00C707", -"k# c #008906", -"l# c #008802", -"m# c #008301", -"n# c #008D00", -"o# c #00A600", -"p# c #00B600", -"q# c #00BB00", -"r# c #02B800", -"s# c #02AD00", -"t# c #01A900", -"u# c #02AF00", -"v# c #030C0B", -"w# c #48672A", -"x# c #899A22", -"y# c #00971C", -"z# c #009823", -"A# c #00A514", -"B# c #00CA07", -"C# c #00CA02", -"D# c #00B203", -"E# c #00BD02", -"F# c #00BC02", -"G# c #00BF00", -"H# c #00BC00", -"I# c #00B800", -"J# c #01B500", -"K# c #01A800", -"L# c #02A600", -"M# c #02B100", -"N# c #101708", -"O# c #C18D00", -"P# c #1E7407", -"Q# c #006212", -"R# c #007C1C", -"S# c #008F14", -"T# c #001700", -"U# c #00C601", -"V# c #00C100", -"W# c #00BA00", -"X# c #00B700", -"Y# c #02A500", -"Z# c #02A300", -"`# c #01B400", -" $ c #000709", -".$ c #0B0F05", -"+$ c #CB9400", -"@$ c #0D5704", -"#$ c #007011", -"$$ c #000B02", -"%$ c #007D0C", -"&$ c #009401", -"*$ c #009500", -"=$ c #00B000", -"-$ c #01AF00", -";$ c #01A100", -">$ c #009F00", -",$ c #01B300", -"'$ c #000404", -")$ c #00181B", -"!$ c #3EAF08", -"~$ c #0EA408", -"{$ c #007B06", -"]$ c #009000", -"^$ c #008F00", -"/$ c #009C00", -"($ c #01AB00", -"_$ c #019C00", -":$ c #03B300", -"<$ c #837207", -"[$ c #00B301", -"}$ c #00AE00", -"|$ c #00AB00", -"1$ c #02AE00", -"2$ c #02A800", -"3$ c #029900", -"4$ c #02B000", -"5$ c #856E07", -"6$ c #807508", -"7$ c #00A104", -"8$ c #00B100", -"9$ c #00B200", -"0$ c #009D00", -"a$ c #019300", -"b$ c #00AF00", -"c$ c #003A82", -"d$ c #000405", -"e$ c #B08B08", -"f$ c #897D08", -"g$ c #827B08", -"h$ c #002B06", -"i$ c #029600", -"j$ c #019100", -"k$ c #02AB00", -"l$ c #00387D", -"m$ c #003D91", -"n$ c #0035D2", -"o$ c #8E8008", -"p$ c #676208", -"q$ c #007E0B", -"r$ c #008701", -"s$ c #009400", -"t$ c #029700", -"u$ c #029F00", -"v$ c #028900", -"w$ c #00469B", -"x$ c #0047A8", -"y$ c #0035B1", -"z$ c #00102A", -"A$ c #9F9107", -"B$ c #646308", -"C$ c #837C06", -"D$ c #81B807", -"E$ c #54B008", -"F$ c #008A06", -"G$ c #008A02", -"H$ c #009100", -"I$ c #018700", -"J$ c #00A100", -"K$ c #004FAC", -"L$ c #0050C2", -"M$ c #0048CA", -"N$ c #001721", -"O$ c #7B7407", -"P$ c #817B07", -"Q$ c #AE9E07", -"R$ c #BDA607", -"S$ c #BDA308", -"T$ c #72C708", -"U$ c #55B507", -"V$ c #00A806", -"W$ c #008102", -"X$ c #019A00", -"Y$ c #019400", -"Z$ c #018D00", -"`$ c #008700", -" % c #0052B5", -".% c #0057D3", -"+% c #9C9207", -"@% c #645D07", -"#% c #8E8107", -"$% c #B4A308", -"%% c #BCA808", -"&% c #4CCE07", -"*% c #2FB707", -"=% c #00920B", -"-% c #006503", -";% c #029E00", -">% c #03A500", -",% c #007F00", -"'% c #0054BA", -")% c #005FE3", -"!% c #0051B7", -"~% c #001F3F", -"{% c #002A67", -"]% c #004FBC", -"^% c #0058BE", -"/% c #635907", -"(% c #766D08", -"_% c #A99C08", -":% c #B7AC07", -"<% c #26CD07", -"[% c #17C108", -"}% c #006007", -"|% c #00220D", -"1% c #004506", -"2% c #008800", -"3% c #028A00", -"4% c #008600", -"5% c #028300", -"6% c #0062E9", -"7% c #001D44", -"8% c #00070E", -"9% c #002D79", -"0% c #0051C6", -"a% c #004C9B", -"b% c #004875", -"c% c #6E6408", -"d% c #B3AA07", -"e% c #09C608", -"f% c #0AC508", -"g% c #08C307", -"h% c #00BD0A", -"i% c #005F07", -"j% c #001408", -"k% c #00370A", -"l% c #006A00", -"m% c #007800", -"n% c #027A00", -"o% c #009200", -"p% c #009A00", -"q% c #007AFF", -"r% c #0062F5", -"s% c #001E4B", -"t% c #000E1C", -"u% c #0061F4", -"v% c #0054F2", -"w% c #0047F1", -"x% c #003DF4", -"y% c #0040FA", -"z% c #517F07", -"A% c #00BB17", -"B% c #048300", -"C% c #000703", -"D% c #012E0D", -"E% c #065201", -"F% c #0C6100", -"G% c #146000", -"H% c #0068EE", -"I% c #0057D6", -"J% c #005FF5", -"K% c #002153", -"L% c #000D1A", -"M% c #000205", -"N% c #000B1B", -"O% c #0066F4", -"P% c #005EF3", -"Q% c #0051F2", -"R% c #0045E7", -"S% c #003DD5", -"T% c #0042D3", -"U% c #367907", -"V% c #00BF0E", -"W% c #023F0E", -"X% c #0D7A01", -"Y% c #199D00", -"Z% c #0055CD", -"`% c #0056D9", -" & c #005BF0", -".& c #005BD5", -"+& c #00162C", -"@& c #003168", -"#& c #006FF6", -"$& c #006AF4", -"%& c #0060F7", -"&& c #0058F2", -"*& c #0051DF", -"=& c #004CBF", -"-& c #005AAC", -";& c #0092AF", -">& c #00C007", -",& c #049A00", -"'& c #07570E", -")& c #0048B9", -"!& c #0053E1", -"~& c #0056F0", -"{& c #001533", -"]& c #005FC6", -"^& c #0071F6", -"/& c #006DF6", -"(& c #0069F9", -"_& c #0068FA", -":& c #0060DF", -"<& c #005DB4", -"[& c #006A9D", -"}& c #007F84", -"|& c #00664F", -"1& c #008D5B", -"2& c #00B707", -"3& c #049000", -"4& c #013900", -"5& c #00181C", -"6& c #0043B6", -"7& c #0051E8", -"8& c #0057F5", -"9& c #0051D1", -"0& c #0061CA", -"a& c #0072F8", -"b& c #006EF8", -"c& c #006BF9", -"d& c #0069FA", -"e& c #0064F8", -"f& c #0068F0", -"g& c #006FE3", -"h& c #006BBB", -"i& c #005BEE", -"j& c #00596A", -"k& c #00A41E", -"l& c #000E10", -"m& c #0046C2", -"n& c #0056EF", -"o& c #005CFF", -"p& c #004FE5", -"q& c #00091B", -"r& c #006DF8", -"s& c #006AF9", -"t& c #0061FA", -"u& c #0064F9", -"v& c #006BDE", -"w& c #005187", -"x& c #005E8C", -"y& c #009D36", -"z& c #00A319", -"A& c #00610B", -"B& c #007D12", -"C& c #0046C4", -"D& c #004ED5", -"E& c #005CF7", -"F& c #0066FF", -"G& c #004DFF", -"H& c #00133D", -"I& c #0065EA", -"J& c #006AF8", -"K& c #0067FB", -"L& c #0066F9", -"M& c #0063FA", -"N& c #005FFA", -"O& c #005EF8", -"P& c #005DE8", -"Q& c #006EF0", -"R& c #00519D", -"S& c #008C62", -"T& c #009632", -"U& c #00A308", -"V& c #085205", -"W& c #001417", -"X& c #0047BF", -"Y& c #0059E4", -"Z& c #005BFE", -"`& c #006FFF", -" * c #0048FF", -".* c #000F32", -"+* c #004194", -"@* c #0066F5", -"#* c #005DF9", -"$* c #0056F7", -"%* c #0056FA", -"&* c #005DFA", -"** c #0059F6", -"=* c #0060F5", -"-* c #0046A0", -";* c #005A8A", -">* c #007D93", -",* c #00885B", -"'* c #009425", -")* c #09A408", -"!* c #317B07", -"~* c #061000", -"{* c #001013", -"]* c #0044B0", -"^* c #0064F4", -"/* c #0077FF", -"(* c #004EFF", -"_* c #0050FE", -":* c #0064EE", -"<* c #0065F6", -"[* c #005FF6", -"}* c #005AF9", -"|* c #0055FA", -"1* c #0056F6", -"2* c #0058F9", -"3* c #0064DE", -"4* c #004EA8", -"5* c #006AB8", -"6* c #007386", -"7* c #007E51", -"8* c #4B6807", -"9* c #5A7B06", -"0* c #0D1200", -"a* c #000F11", -"b* c #000B0D", -"c* c #000B0C", -"d* c #004EBE", -"e* c #0069F4", -"f* c #0068FF", -"g* c #005EFF", -"h* c #0061FF", -"i* c #003A90", -"j* c #002559", -"k* c #0059C8", -"l* c #0061F1", -"m* c #005BF2", -"n* c #005AF0", -"o* c #005AEF", -"p* c #005BED", -"q* c #0061D9", -"r* c #0066B5", -"s* c #006E8A", -"t* c #009157", -"u* c #307B08", -"v* c #636507", -"w* c #727206", -"x* c #908E04", -"y* c #908F08", -"z* c #0042A3", -"A* c #004DB6", -"B* c #0063E0", -"C* c #0074FF", -"D* c #0067F6", -"E* c #0068F5", -"F* c #006BF4", -"G* c #0069E8", -"H* c #0069F1", -"I* c #0067EC", -"J* c #0063E9", -"K* c #0062E7", -"L* c #0062E6", -"M* c #0061EA", -"N* c #005CE7", -"O* c #005BDB", -"P* c #005EBD", -"Q* c #005D6B", -"R* c #005E6C", -"S* c #006674", -"T* c #3F6907", -"U* c #8B7306", -"V* c #AE8F04", -"W* c #897506", -"X* c #001518", -"Y* c #003484", -"Z* c #00459D", -"`* c #0054B6", -" = c #0066D9", -".= c #006BE8", -"+= c #0074EF", -"@= c #0074EC", -"#= c #006FE7", -"$= c #006CE7", -"%= c #003F82", -"&= c #005BC6", -"*= c #0060D6", -"== c #0062DC", -"-= c #0065E2", -";= c #0062E2", -">= c #0058E7", -",= c #0054E9", -"'= c #0052DC", -")= c #0057B4", -"!= c #00677C", -"~= c #007D3B", -"{= c #008E08", -"]= c #11A308", -"^= c #659C00", -"/= c #3B5204", -"(= c #586A05", -"_= c #675203", -":= c #001A1E", -"<= c #002A30", -"[= c #0055BE", -"}= c #0057B8", -"|= c #0058B0", -"1= c #005CB7", -"2= c #0061C1", -"3= c #0068CF", -"4= c #0072E0", -"5= c #0074E8", -"6= c #005EC6", -"7= c #000C1A", -"8= c #00204A", -"9= c #003884", -"0= c #0065E4", -"a= c #0064DD", -"b= c #0064DF", -"c= c #0059DE", -"d= c #0052E6", -"e= c #004DE0", -"f= c #004FC9", -"g= c #005C9C", -"h= c #006B61", -"i= c #007B30", -"j= c #00880D", -"k= c #088D08", -"l= c #128908", -"m= c #238308", -"n= c #417E07", -"o= c #658407", -"p= c #899206", -"q= c #9C9603", -"r= c #0C0C01", -"s= c #020200", -"t= c #040806", -"u= c #202912", -"v= c #002F37", -"w= c #006FD0", -"x= c #006CC5", -"y= c #0057B6", -"z= c #0055B6", -"A= c #0056BD", -"B= c #005CCD", -"C= c #0062E3", -"D= c #004EB8", -"E= c #002C60", -"F= c #005DE3", -"G= c #0061DF", -"H= c #0054D4", -"I= c #004ECF", -"J= c #004AC1", -"K= c #004DA2", -"L= c #005581", -"M= c #005D5A", -"N= c #006135", -"O= c #006A13", -"P= c #177607", -"Q= c #397D07", -"R= c #547A05", -"S= c #747B03", -"T= c #968503", -"U= c #0D160E", -"V= c #242D11", -"W= c #585F17", -"X= c #005EF0", -"Y= c #0048C2", -"Z= c #0044BA", -"`= c #0047CC", -" - c #0043CC", -".- c #003DB1", -"+- c #0052D1", -"@- c #0059DB", -"#- c #004DB4", -"$- c #003D8E", -"%- c #005DCC", -"&- c #0058BF", -"*- c #0050B3", -"=- c #0046A6", -"-- c #00419C", -";- c #004190", -">- c #004577", -",- c #004951", -"'- c #005423", -")- c #206C04", -"!- c #3F7605", -"~- c #587506", -"{- c #717105", -"]- c #77670A", -"^- c #0F1407", -"/- c #1B2008", -"(- c #2C300B", -"_- c #0044F6", -":- c #003ADA", -"<- c #0038D0", -"[- c #0033B9", -"}- c #003773", -"|- c #002A62", -"1- c #000F25", -"2- c #002968", -"3- c #004CAF", -"4- c #005FC9", -"5- c #0058AD", -"6- c #004B93", -"7- c #004292", -"8- c #002258", -"9- c #003654", -"0- c #00613A", -"a- c #2C8403", -"b- c #0B1309", -"c- c #08100A", -"d- c #020201", -"e- c #17200D", -"f- c #003AA9", -"g- c #002B94", -"h- c #002993", -"i- c #00101E", -"j- c #00121D", -"k- c #000306", -"l- c #003FA9", -"m- c #001131", -"n- c #00323A", -"o- c #011305", -"p- c #30604C", -"q- c #1D6C68", -"r- c #03262B", -"s- c #001D24", -"t- c #041719", -"u- c #203217", -"v- c #000506", -"w- c #0035AF", -"x- c #002985", -"y- c #00121C", -"z- c #000507", -"A- c #000103", -"B- c #030400", -"C- c #1E1B07", -"D- c #1B1D0D", -"E- c #17676A", -"F- c #00A6CD", -"G- c #009CC3", -"H- c #009CC2", -"I- c #00333F", -"J- c #010606", -"K- c #000708", -"L- c #003488", -"M- c #00306D", -"N- c #003560", -"O- c #002F55", -"P- c #001727", -"Q- c #4D7327", -"R- c #2D310C", -"S- c #030200", -"T- c #0C0F07", -"U- c #095B68", -"V- c #00ABD6", -"W- c #00B0DB", -"X- c #00ADD6", -"Y- c #003D4C", -"Z- c #000F13", -"`- c #00181E", -" ; c #004AA3", -".; c #003B73", -"+; c #00305B", -"@; c #002E55", -"#; c #003869", -"$; c #003565", -"%; c #003566", -"&; c #002950", -"*; c #002B52", -"=; c #003D6B", -"-; c #004166", -";; c #4E7025", -">; c #2F300A", -",; c #110F02", -"'; c #040300", -"); c #010C0D", -"!; c #002B35", -"~; c #00667E", -"{; c #00AAD2", -"]; c #007D9C", -"^; c #00738F", -"/; c #001A21", -"(; c #000608", -"_; c #000202", -":; c #003E6F", -"<; c #003659", -"[; c #004389", -"}; c #0054A8", -"|; c #0053A5", -"1; c #0050A3", -"2; c #004FA3", -"3; c #004FA7", -"4; c #004FA8", -"5; c #004DA5", -"6; c #0049A0", -"7; c #00418F", -"8; c #003A78", -"9; c #47440A", -"0; c #382E01", -"a; c #070500", -"b; c #002E38", -"c; c #0096BB", -"d; c #00A9D2", -"e; c #00AFD9", -"f; c #00262F", -"g; c #005060", -"h; c #004C5B", -"i; c #002F38", -"j; c #002D54", -"k; c #0048BC", -"l; c #001E56", -"m; c #001B58", -"n; c #534700", -"o; c #5F5000", -"p; c #272000", -"q; c #030300", -"r; c #00080A", -"s; c #004D5F", -"t; c #00AAD3", -"u; c #00171C", -"v; c #000C0F", -"w; c #00643A", -"x; c #00532F", -"y; c #002B15", -"z; c #003E56", -"A; c #005481", -"B; c #004D8C", -"C; c #004696", -"D; c #00429B", -"E; c #003E9C", -"F; c #003FA1", -"G; c #0040AA", -"H; c #003EB0", -"I; c #0036B0", -"J; c #00268D", -"K; c #002081", -"L; c #685B00", -"M; c #9D8900", -"N; c #9F8700", -"O; c #423500", -"P; c #070701", -"Q; c #003B49", -"R; c #009DC3", -"S; c #00A8D1", -"T; c #0092B6", -"U; c #008CAF", -"V; c #004D62", -"W; c #002E3A", -"X; c #00191F", -"Y; c #00760D", -"Z; c #005A08", -"`; c #003B06", -" > c #004D40", -".> c #006267", -"+> c #00587E", -"@> c #005094", -"#> c #0048A6", -"$> c #0044AF", -"%> c #0045B7", -"&> c #0045C4", -"*> c #003FC4", -"=> c #0030A7", -"-> c #002284", -";> c #6D6000", -">> c #968300", -",> c #BEA600", -"'> c #8A7100", -")> c #3B3301", -"!> c #131808", -"~> c #041819", -"{> c #004556", -"]> c #008CAE", -"^> c #00ABD5", -"/> c #00AAD4", -"(> c #008FB2", -"_> c #00232B", -":> c #00090B", -"<> c #159800", -"[> c #0B5900", -"}> c #0B4F00", -"|> c #005924", -"1> c #00683E", -"2> c #005557", -"3> c #004D7A", -"4> c #004C9F", -"5> c #0040A0", -"6> c #003C9A", -"7> c #003791", -"8> c #0045BB", -"9> c #00277B", -"0> c #514C00", -"a> c #796A00", -"b> c #A49000", -"c> c #B09100", -"d> c #B99F00", -"e> c #4C4704", -"f> c #050904", -"g> c #011519", -"h> c #005A70", -"i> c #00AAD5", -"j> c #00ACD5", -"k> c #003745", -"l> c #000001", -"m> c #105700", -"n> c #0F5100", -"o> c #006B0F", -"p> c #007726", -"q> c #006141", -"r> c #005466", -"s> c #004880", -"t> c #00347C", -"u> c #00183E", -"v> c #403D00", -"w> c #655800", -"x> c #917D00", -"y> c #AF9100", -"z> c #BDA300", -"A> c #695E00", -"B> c #1E1D02", -"C> c #002E39", -"D> c #00647C", -"E> c #00ACD6", -"F> c #003F4E", -"G> c #000C0E", -"H> c #094D00", -"I> c #094800", -"J> c #027800", -"K> c #007F0E", -"L> c #006D2B", -"M> c #00594B", -"N> c #003850", -"O> c #002D59", -"P> c #003075", -"Q> c #003D96", -"R> c #003896", -"S> c #00368A", -"T> c #002F74", -"U> c #002149", -"V> c #5A5500", -"W> c #806F00", -"X> c #9E8500", -"Y> c #B49700", -"Z> c #B29B00", -"`> c #9B8900", -" , c #5C5300", -"., c #000406", -"+, c #004453", -"@, c #009CC1", -"#, c #00A8D2", -"$, c #00ABD4", -"%, c #004100", -"&, c #003C00", -"*, c #088000", -"=, c #008302", -"-, c #007316", -";, c #006332", -">, c #004542", -",, c #003F5D", -"', c #003A7D", -"), c #0046A9", -"!, c #0040AD", -"~, c #003FA6", -"{, c #003483", -"], c #00254E", -"^, c #676100", -"/, c #917C00", -"(, c #A58800", -"_, c #B49800", -":, c #A89100", -"<, c #9C8800", -"[, c #9A8800", -"}, c #001419", -"|, c #004B5D", -"1, c #00A9D3", -"2, c #00ABD3", -"3, c #007693", -"4, c #003440", -"5, c #003B05", -"6, c #003405", -"7, c #008300", -"8, c #007906", -"9, c #007019", -"0, c #006236", -"a, c #005F67", -"b, c #00457B", -"c, c #003F99", -"d, c #00379F", -"e, c #00339A", -"f, c #002875", -"g, c #001F47", -"h, c #5D5500", -"i, c #846E00", -"j, c #957900", -"k, c #A38B00", -"l, c #968100", -"m, c #8B7800", -"n, c #847100", -"o, c #0091B5", -"p, c #005C73", -"q, c #003207", -"r, c #00890A", -"s, c #008504", -"t, c #018100", -"u, c #007401", -"v, c #006316", -"w, c #00694A", -"x, c #004668", -"y, c #00317E", -"z, c #002D8C", -"A, c #002989", -"B, c #001F68", -"C, c #001944", -"D, c #514900", -"E, c #725F00", -"F, c #806600", -"G, c #867200", -"H, c #000E12", -"I, c #003542", -"J, c #005367", -"K, c #00ADD7", -"L, c #009BC0", -"M, c #00728E", -"N, c #023400", -"O, c #008515", -"P, c #008209", -"Q, c #047F00", -"R, c #077400", -"S, c #015A00", -"T, c #00612C", -"U, c #003C4C", -"V, c #002B6A", -"W, c #002DAB", -"X, c #00238C", -"Y, c #001C5D", -"Z, c #5F5400", -"`, c #8A7300", -" ' c #A28100", -".' c #A18900", -"+' c #A48A00", -"@' c #AB9200", -"#' c #004455", -"$' c #00A5CE", -"%' c #00AED8", -"&' c #0088A9", -"*' c #005063", -"=' c #00222A", -"-' c #000607", -";' c #0D3C00", -">' c #007317", -",' c #00640A", -"'' c #006000", -")' c #036000", -"!' c #0B6400", -"~' c #007F27", -"{' c #004B56", -"]' c #003882", -"^' c #002FAD", -"/' c #002FC7", -"(' c #0025A0", -"_' c #001B69", -":' c #635600", -"<' c #967A00", -"[' c #B18D00", -"}' c #B39900", -"|' c #B59B00", -"1' c #C3A500", -"2' c #00586D", -"3' c #007491", -"4' c #00596E", -"5' c #000F12", -"6' c #005C00", -"7' c #016300", -"8' c #005D0C", -"9' c #005C15", -"0' c #00500D", -"a' c #00342E", -"b' c #002B56", -"c' c #004193", -"d' c #003AA0", -"e' c #004097", -"f' c #00255B", -"g' c #856300", -"h' c #B39300", -"i' c #A79000", -"j' c #666F00", -"k' c #778200", -"l' c #000D10", -"m' c #0082A2", -"n' c #004253", -"o' c #00141B", -"p' c #157E00", -"q' c #007100", -"r' c #008517", -"s' c #00751A", -"t' c #005A0D", -"u' c #005A06", -"v' c #002F5A", -"w' c #003F90", -"x' c #0042A6", -"y' c #003D8F", -"z' c #00265C", -"A' c #604200", -"B' c #8D6900", -"C' c #AF9000", -"D' c #A89300", -"E' c #717C00", -"F' c #003B4A", -"G' c #0094B8", -"H' c #009DC2", -"I' c #0082A1", -"J' c #000D11", -"K' c #22BB00", -"L' c #006800", -"M' c #008114", -"N' c #007817", -"O' c #005C0A", -"P' c #005E0B", -"Q' c #00355A", -"R' c #003887", -"S' c #0042A0", -"T' c #003883", -"U' c #583F00", -"V' c #936D00", -"W' c #A48800", -"X' c #AB9700", -"Y' c #00171D", -"Z' c #00303C", -"`' c #00657D", -" ) c #007593", -".) c #003340", -"+) c #001114", -"@) c #026C00", -"#) c #00690A", -"$) c #007412", -"%) c #006209", -"&) c #00650D", -"*) c #003C5D", -"=) c #003781", -"-) c #003990", -";) c #003477", -">) c #584300", -",) c #8E6D00", -"') c #9C8000", -")) c #AE9900", -"!) c #000A0C", -"~) c #00A6CE", -"{) c #0085A5", -"]) c #000A0D", -"^) c #048000", -"/) c #006D0B", -"() c #006A07", -"_) c #007012", -":) c #004764", -"<) c #003D81", -"[) c #00317C", -"}) c #002E6D", -"|) c #654E00", -"1) c #856900", -"2) c #9A7E00", -"3) c #B8A200", -"4) c #001920", -"5) c #004759", -"6) c #007F9E", -"7) c #008DAE", -"8) c #006B84", -"9) c #001E26", -"0) c #000305", -"a) c #005A05", -"b) c #006604", -"c) c #006A04", -"d) c #007615", -"e) c #00AB61", -"f) c #005168", -"g) c #003977", -"h) c #002B6C", -"i) c #002354", -"j) c #343300", -"k) c #6D5800", -"l) c #856B00", -"m) c #B49B00", -"n) c #00151B", -"o) c #00647E", -"p) c #009BC1", -"q) c #007895", -"r) c #003846", -"s) c #001115", -"t) c #005E00", -"u) c #006300", -"v) c #007717", -"w) c #00A45B", -"x) c #005664", -"y) c #00346C", -"z) c #00245E", -"A) c #001833", -"B) c #463C00", -"C) c #6F5E00", -"D) c #8F7600", -"E) c #9D8400", -"F) c #00222B", -"G) c #006F8A", -"H) c #0087A7", -"I) c #0094B7", -"J) c #004152", -"K) c #00090C", -"L) c #055A00", -"M) c #007C1A", -"N) c #00A659", -"O) c #005961", -"P) c #003462", -"Q) c #001C53", -"R) c #4F4400", -"S) c #716200", -"T) c #A48D00", -"U) c #00232C", -"V) c #00556B", -"W) c #0087A8", -"X) c #00B1DC", -"Y) c #004353", -"Z) c #0D8100", -"`) c #006204", -" ! c #00741A", -".! c #008745", -"+! c #005A5B", -"@! c #002C51", -"#! c #00164E", -"$! c #150F00", -"%! c #574B00", -"&! c #8E8400", -"*! c #002129", -"=! c #007D9B", -"-! c #006983", -";! c #002D39", -">! c #006906", -",! c #005102", -"'! c #008A46", -")! c #005953", -"!! c #002747", -"~! c #00062D", -"{! c #231600", -"]! c #675B00", -"^! c #A7A300", -"/! c #002630", -"(! c #001B22", -"_! c #00242D", -":! c #00303B", -" ", -" , ' ) ! ~ { ] ^ / ( _ : < [ } | 1 2 3 ", -" 4 5 6 7 8 9 0 a b c d e f g h i j k l m n o p ", -" q r s t u v w x y z A B C D E F G H I J K L M N O P ", -" Q R S T U V W X Y Z ` F ...+.C .@.#.$.%.&. *.=.-.;.>. ", -" ,.'.).!.~.{.].^./.(._.:.:.<.[.}.|.1.2.3.4. 5.6.7.8.9.0.a.b. ", -" c.d.e.f.g.V f W h.i.^.j.k.l.m.n.o.p.q.r.s. t.u.v.w.x.y.z.A.B.C. ", -" D.E.F.G.H.I.J.K.L.M.N._ _ O.P.Q.R.S.T.U. V.W.X.Y.Z.`. +.+++@+#+ ", -" $+%+&+*+=+-+;+>+,+'+)+!+!+~+{+]+^+/+(+_+ :+<+[+}+|+1+2+3+1+4+5+6+7+ ", -" 8+9+F.0+a+b+c+d+e+f+f+g+h+i+j+~+k+l+m+ n+o+p+q+3+r+s+s+3+t+u+v+w+x+ ", -" y+z+A+B+C+D+b+E+H.F+G+H+H+H+I+J+K+L+ H+H+H+H+ M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+`+ @ ", -" .@+@@@#@$@%@b+H+&@*@=@H+H+H+-@;@>@,@'@)@H+H+H+!@H+H+H+~@{@]@^@/@(@_@:@t+<@t+[@}@|@1@2@ ", -" 3@4@5@6@7@8@9@H+0@a@H+H+H+H+H+H+H+H+H+H+H+H+H+H+b@H+c@d@R+e@f@g@h@i@j@k@l@m@n@o@@+p@q@ ", -" r@s@t@u@v@w@x@H+H+H+y@z@A@B@C@D@E@F@ G@H@I@J@H+H+H+K@L@2+M@v+N@O@P@Q@R@S@T@U@V@W@ ", -" X@Y@Z@H+H+H+H+`@ #.#+#@#@#@#@##### $#%#&#*#H+H+H+H+=#-#;#>#,#'#)#!#~#{#]#^#/# ", -" (#_#H+H+H+H+:#<#@#@#@#@#@#@#@##### [#}#}#}#H+H+H+H+|#1#b.2#3#4#5#6#!#7#8#9#0# ", -" a#b#H+c#d#e#f#@#@#@#@#@#@#g#g#g#h# i#j#}#}#}#}#H+H+H+H+k#l#m#m#n#o#p#q#r#s#t#u# ", -" H+H+v#w#x#@#@#@#@#@#@#@#@#g#g#g#g# y#z#A#B#}#C#C#C#}#H+H+H+H+D#E#F#q#G#H#I#J#K#L#M# ", -" H+H+H+N#O#@#@#@#@#@#@#@#@#@#g#g#g#g#P#Q#R#S#}#}#C#C#C#C#C#H+H+H+H+T#U#V#'#W#X#M#Y#Z#`# ", -" $H+H+.$H++$@#@#@#@#@#@#@#@#g#g#g#g#g#@$#$}#C#C#C#C#C#C#C#C#C#$$H+H+%$&$*$4#6#=$-$;$>$,$ ", -" H+H+'$)$H+@#@#@#@#@#@#@#@#g#g#g#g#g#!$~$}#C#C#C#C#C#C#C#C#C#C#}#H+H+H+{$]$^$/$[+($_$_$:$ ", -" H+H+'@H+H+<$@#@#@#@#@#@#g#g#g#g#g#g#}#}#}#C#C#C#C#C#}#C#C#C#C#C#H+H+H+H+[$}$|$1$2$3$4#4$ ", -" H+H+H+H+H+ 5$6$@#@#@#g#g#g#g#g#g#g#}#}#}#}#C#C#C#C#C#C#}#C#C#C#C#}#H+H+H+7$8$9$6#0${#a$b$ ", -" c$ H+H+H+d$H+H+ e$f$g$@#g#g#g#g#g#g#g#}#}#}#}#C#C#C#C#C#C#C#}#C#C#C#C#}#H+H+H+h$4#Y#;$;$i$j$k$ ", -" l$m$n$ H+H+H+H+H+ o$p$@#g#g#g#g#g#g#}#}#}#C#C#C#C#C#C#C#C#C#C#}#C#C#C#}#q$H+H+H+r$s$t$u$]$v$5# ", -" w$x$y$z$H+H+H+H+ A$B$C$g#g#g#g#D$E$}#}#C#C#C#C#C#C#C#C#C#C#C#}#C#C#C#C#F$H+H+H+G$s$H$8#]#I$J$ ", -" K$L$M$H+H+N$H+H+ O$P$Q$R$S$T$U$}#}#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#V$H+H+H+W$0$X$Y$Z$`$J$ ", -" %.%H+H+H+H+H+ +%@%#%$%%%&%*%}#}#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#=%H+H+-%;%>%i$`$,%^# ", -" '%)%!%~%H+H+{%]%^% /%(%_%:%<%[%}#}#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#}%|%H+1%2%4#3%4%5%~# ", -" '%6%7%8%H+H+9%0%a%b% c%##d%e%f%g%h%C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#i%j%H+k%l%m%n%o%p%}+ ", -" q%.%r%s%H+H+t%H+u%v%w%x%y% z%C#A%C#C#C#C#}#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#B%C%H+D%E%F%G% ", -" H%I%J%K%L%H+M%N%O%P%Q%R%S%T% U%}#V%C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#}#H+H+W%X%Y% ", -" Z%`% &.&+&H+H+@&#&$&%&&&*&=&-&;& }#>&C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#,&P H+H+'& ", -" )&!&~&{&H+H+H+]&^&/&(&_&_&:&<&[&}&|&1&2&}#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#}#3&4&H+H+5& ", -" 6&7&8&9&H+H+H+0&a&b&c&d&e&f&g&h&i&j&1&k&}#}#}#}#C#C#C#C#C#C#C#C#C#C#C#C#C#}#}#}#H+H+H+H+l& ", -" m&n&o&p&q&H+H+H+r&r&s&s&t&u&c&v&i&w&x&1&y&z&}#}#C#C#C#C#C#C#C#C#C#C#C#C#}#}#A&B&H+H+H+H+ ", -" C&D&E&F&G&H&H+H+H+I&J&K&L&M&N&O&P&Q&i&R&i&S&T&U&}#C#C#C#C#C#C#C#C#C#C#}#}#}#}#V& H+H+H+H+W& ", -" X&Y&Z&`& *.*H+H+H++*@*M&#*$*%*&***=*i&-*;*>*,*'*)*C#C#C#C#C#C#C#C#C#}#}#}#}#!*~*H+H+H+{*H+ ", -" ]*^*F&/*(*_*H+H+H+H+:*<*[*}*%*|*1*2*i&3*4*5*6*7*}#}#}#C#C#C#C#C#C#C#}#}#}#8*9*0*a*H+H+b*c* ", -" d*e*f*g*o&h*i*j*H+H+H+k*O%l*m*n*o*m*i&3*p*q*r*s*t*}#}#}#C#C#C#C#C#}#}#u*v*w*x*y*H+H+H+H+H+ ", -" z*A*B*q%C*D*E*F*G*H+H+H+H+H*I*J*K*L*M*i&3*p*N*O*P*Q*R*S*}#}#}#}#}#}#}#}#T*U*V*W*H+H+H+X*H+ ", -" Y*Z*`* =.=+=@=#=$=%=H+H+H+&=*===-=B*;=i&3*p*>=,='=)=!=~={=]=}#}#}#}#^=/=(=H+_=H+H+H+:=<=H+ ", -" [=}=|=1=2=3=4=5=6=7=H+H+H+8=9=0=a=b=i&3*p*c=d=e=f=g=h=i=j=k=l=m=n=o=p=q=H+r=s=t=u=v= ", -" w=x=y=z=A=B=C=D=E=H+H+H+H+F=)%G=i&3*p*.&H=I=J=K=L=M=N=O=P=Q=R=S=T=H+H+H+U=V=W= ", -" X=Y=Z=`= -.-H+H+H+H+H++-@-#-$-p*%-&-*-=---;->-,-'-)-!-~-{-]-H+H+H+^-/-(- ", -" _-:-<-[-H+H+H+H+}-|-1-2-3-4-5-6-7-8-H+H+9-0-a-H+H+H+H+H+H+b-c-d-e- ", -" f-g-h-H+H+i-j-k-H+H+H+H+H+l-m-H+H+H+H+n-o-H+H+H+H+H+p-q-r-s-t-u-v- ", -" w-x-H+H+H+y-z-A-H+H+H+H+H+H+H+H+H+H+H+H+H+B-C-D-E-F-G-H-I-J-K- ", -" L-M-H+N-O-P-H+H+H+H+H+H+H+H+H+H+H+X*Q-R-S-T-U-V-W-X-Y-G@Z-`- ", -" ;H+H+H+H+.;+;@;#;$;%;&;*;*;=;-; ;;>;,;';);!;~;{;];^;/;(;_; ", -" :;N-<;[;};|;1;2;3;4;5;6;7;8; 9;0;a;H+'@b;c;d;e;f;)@b@'@ ", -" g;h;i;j;k;k;k;k;k;k;k;k;k;l;m; n;o;p;q;H+r;s;X-t;H@u;v;(;'@ ", -" w;x;y;z;A;B;C;D;E;F;G;H;I;J;K; L;M;N;O;P;_;Q;R;S;T;U;V;W;X; ", -" Y;Z;`; >.>+>@>#>$>%>X&&>*>=>-> ;>>>,>'>)>!>~>{>]>^>/>R;(>_>:>H+ ", -" <>[>}>|>1>2>3>4>5>6>7>8>8>9>m; 0>a>b>c>d>e>f>g>h>i>t;j>t;k>H+l>H+ ", -" m>n>o>p>q>r>s>t>8>8>8>8>8>u> v>w>x>y>z>A>B>d$C>D>X-d;E>F>G>H+)@ ", -" H>I>J>K>L>M>N>O>P>Q>R>S>T>U> V>W>X>Y>Z>`> ,z-.,+,@,#,$,j>Q;K-b@!@ ", -" %,&,*,=,-,;,>,,,',),!,~,{,], ^,/,(,_,:,<,[,H+(;},|,H-1,2,3,4,d$H+)@ ", -" 5,6,5%7,8,9,0,a,b,c,d,e,f,g, h,i,j,k,l,m,n, H+v-b;o,$,$,t;p,X;z-_;l> ", -" q,r,s,t,u,v,w,x,y,z,A,B,C, D,E,F,G,n,G, H+d$H,I,J,K,$,L,M,`-v-d$H+ ", -" N,O,P,Q,R,S,T,U,V,g-W,X,Y, Z,`, '.'+'@' (;H+#'$'%'t;&'*'='-'_;H+ ", -" ;'>',''')'!'~'{']'^'/'('_' :'<'['}'|'1' H+b*`-2'3'$,$,E>4'5'v-., ", -" 6'7'8'9'0' a'b'c'd'e'f' g'h'i'j'k' l'G@h>W-d;$,m'n'o'z-)@ ", -" p'q'r's't'u' v'w'x'y'z'A'B'C'D'E' )@Z-F'G'H'$,t;I'I,J''@H+ ", -" K'L'M'N'O'P' Q'R'S'T' U'V'W'X' Y'Z'`'X-t;c; ).)+)b@H+ ", -" @)#)$)%)&) *)=)-);) >),)'))) '@!)J,~)X-t;K,{)I-])!@H+ ", -" ^)u'/)()_) :)<)[)}) |)1)2)3) (;4)5)6)$,S;7)8)9)0)_; ", -" a)b)c)d)e)f)g)h)i)j)k)l)2)m) z-n)o)p)p)q)`'r)s)-'d$ ", -" q't)u)v)w)x)y)z)A)B)C)D)E) !@v;F)#'G)H)I)3,J)K)'@)@ ", -" L)''M)N)O)P)Q) R)S)T) $])U)V)W)X)%'Y)v;b@'@ ", -" Z)`) !.!+!@!#!$!%!&!X@ b*v;*!|,=!-!;!z-{* ", -" >!,!'!)!!!~!{!]!^! -''@/!F'(!_!:!`- ", -" c #BCB700", +", c #E2DC00", +"' c #E4CA00", +") c #E4D000", +"! c #E5D000", +"~ c #E4CD00", +"{ c #E0B800", +"] c #E2C300", +"^ c #E7D200", +"/ c #EADD00", +"( c #EBDF00", +"_ c #EBE000", +": c #EADC00", +"< c #E8DC00", +"[ c #E4DC00", +"} c #E3E000", +"| c #DEE200", +"1 c #D9DE00", +"2 c #CAD100", +"3 c #B9B500", +"4 c #C7BC00", +"5 c #D1C500", +"6 c #D6C200", +"7 c #D8B900", +"8 c #DAB700", +"9 c #DCBD00", +"0 c #E1C600", +"a c #E3CB00", +"b c #E4CC00", +"c c #E3CA00", +"d c #EEBC00", +"e c #EFC500", +"f c #F1D000", +"g c #F3D900", +"h c #F3DD00", +"i c #F2DF00", +"j c #F2E000", +"k c #F0E100", +"l c #EAE000", +"m c #EBE500", +"n c #E5E500", +"o c #D8D800", +"p c #C3C300", +"q c #DCC200", +"r c #CCBE00", +"s c #D9CA00", +"t c #DFCA00", +"u c #E1C500", +"v c #E5C300", +"w c #E7C900", +"x c #EDCF00", +"y c #EED300", +"z c #EFD400", +"A c #F0D400", +"B c #FFCF00", +"C c #FFD100", +"D c #FFD300", +"E c #FFD600", +"F c #FFDA00", +"G c #FCDE00", +"H c #FBDF00", +"I c #F6E100", +"J c #F2E100", +"K c #EEDF00", +"L c #E0D000", +"M c #C4B300", +"N c #0DAE00", +"O c #036C00", +"P c #037300", +"Q c #DAC500", +"R c #D3C400", +"S c #E6D500", +"T c #E9D200", +"U c #EBCE00", +"V c #EDCD00", +"W c #F2D200", +"X c #F4D600", +"Y c #F6D900", +"Z c #F7DA00", +"` c #F7D900", +" . c #FFD700", +".. c #FFD200", +"+. c #FFCE00", +"@. c #FCDC00", +"#. c #F8DE00", +"$. c #F3DA00", +"%. c #EACB00", +"&. c #D2A900", +"*. c #00B70C", +"=. c #00A50A", +"-. c #009C0B", +";. c #037700", +">. c #038900", +",. c #D2BE00", +"'. c #D0C300", +"). c #E6D700", +"!. c #EBD500", +"~. c #EDD200", +"{. c #F1D100", +"]. c #F4D300", +"^. c #F6D500", +"/. c #F6D600", +"(. c #F7D600", +"_. c #F7D500", +":. c #FBD900", +"<. c #FCD300", +"[. c #FDC900", +"}. c #FBC800", +"|. c #FCCF00", +"1. c #FAD900", +"2. c #F8DC00", +"3. c #EACF00", +"4. c #DCB500", +"5. c #0EB800", +"6. c #01A200", +"7. c #009F08", +"8. c #00A80E", +"9. c #00B813", +"0. c #00C314", +"a. c #017E00", +"b. c #009502", +"c. c #C2B600", +"d. c #DACF00", +"e. c #DFD100", +"f. c #E6D100", +"g. c #E9CE00", +"h. c #F4D500", +"i. c #F5D500", +"j. c #F3DE00", +"k. c #F5DD00", +"l. c #F4D800", +"m. c #F3CD00", +"n. c #F1CA00", +"o. c #F3CF00", +"p. c #F5D700", +"q. c #F2D700", +"r. c #E3C400", +"s. c #C89900", +"t. c #1FAF00", +"u. c #15A800", +"v. c #0BA500", +"w. c #03AF00", +"x. c #00BB05", +"y. c #00C30D", +"z. c #00CA12", +"A. c #00D216", +"B. c #018200", +"C. c #00AE02", +"D. c #BDB300", +"E. c #D7CF00", +"F. c #DED100", +"G. c #E4D100", +"H. c #E8CD00", +"I. c #ECCC00", +"J. c #EECE00", +"K. c #F0D100", +"L. c #F1D300", +"M. c #F2D300", +"N. c #F3D200", +"O. c #EDDD00", +"P. c #EDD800", +"Q. c #EDD400", +"R. c #ECD200", +"S. c #EBD200", +"T. c #E7CD00", +"U. c #CFB100", +"V. c #16E200", +"W. c #0FAC00", +"X. c #0FBC00", +"Y. c #0CBF00", +"Z. c #09C300", +"`. c #03C800", +" + c #00CB03", +".+ c #00CB06", +"++ c #00C807", +"@+ c #00C709", +"#+ c #038000", +"$+ c #B8AF00", +"%+ c #D4CC00", +"&+ c #DED200", +"*+ c #E2CE00", +"=+ c #E6CB00", +"-+ c #E7C800", +";+ c #EBCB00", +">+ c #EDCE00", +",+ c #EDD100", +"'+ c #EED000", +")+ c #EECF00", +"!+ c #E6DE00", +"~+ c #E7DE00", +"{+ c #E8DE00", +"]+ c #E8DB00", +"^+ c #E5D600", +"/+ c #DFCC00", +"(+ c #D6B900", +"_+ c #BA9600", +":+ c #04BC00", +"<+ c #049F00", +"[+ c #01AC00", +"}+ c #01CD00", +"|+ c #00C800", +"1+ c #00D300", +"2+ c #00D900", +"3+ c #00D800", +"4+ c #01CE00", +"5+ c #01C600", +"6+ c #02C000", +"7+ c #037B00", +"8+ c #B5AB00", +"9+ c #D2C900", +"0+ c #E1CC00", +"a+ c #E3C700", +"b+ c #E5C400", +"c+ c #E7C600", +"d+ c #EACC00", +"e+ c #EACE00", +"f+ c #EACD00", +"g+ c #E1CA00", +"h+ c #E5D200", +"i+ c #E8DA00", +"j+ c #E8DF00", +"k+ c #E1D300", +"l+ c #D6BF00", +"m+ c #BD9B00", +"n+ c #099300", +"o+ c #029800", +"p+ c #00B503", +"q+ c #00D006", +"r+ c #00DD00", +"s+ c #00E600", +"t+ c #00D000", +"u+ c #07CE00", +"v+ c #0AC700", +"w+ c #0CBB00", +"x+ c #057000", +"y+ c #B6A900", +"z+ c #D2C600", +"A+ c #DDCD00", +"B+ c #E1C800", +"C+ c #E2C200", +"D+ c #E3C100", +"E+ c #E8CA00", +"F+ c #DBC200", +"G+ c #CDB600", +"H+ c #000000", +"I+ c #817800", +"J+ c #6F6900", +"K+ c #696000", +"L+ c #3F3700", +"M+ c #1C9E00", +"N+ c #118900", +"O+ c #08A800", +"P+ c #04C400", +"Q+ c #00C602", +"R+ c #00C900", +"S+ c #00E100", +"T+ c #00E000", +"U+ c #00E300", +"V+ c #00DC00", +"W+ c #00CF00", +"X+ c #00CA00", +"Y+ c #05CA00", +"Z+ c #08C200", +"`+ c #07B500", +" @ c #0A6200", +".@ c #BEAA00", +"+@ c #D5C400", +"@@ c #DCC700", +"#@ c #E0C500", +"$@ c #E2BF00", +"%@ c #E4C000", +"&@ c #3D3600", +"*@ c #574E00", +"=@ c #252302", +"-@ c #0F1102", +";@ c #1D1F06", +">@ c #060600", +",@ c #0C0D03", +"'@ c #000304", +")@ c #000101", +"!@ c #000102", +"~@ c #11B000", +"{@ c #0FB700", +"]@ c #0BB600", +"^@ c #08B300", +"/@ c #05BE00", +"(@ c #03D000", +"_@ c #01D300", +":@ c #00C500", +"<@ c #00D200", +"[@ c #00CD00", +"}@ c #00CC02", +"|@ c #00C901", +"1@ c #00C000", +"2@ c #00B400", +"3@ c #C5AC00", +"4@ c #D8C300", +"5@ c #D8C100", +"6@ c #DFC200", +"7@ c #E2BE00", +"8@ c #D1AC00", +"9@ c #816E00", +"0@ c #080700", +"a@ c #090900", +"b@ c #000203", +"c@ c #0B7902", +"d@ c #05DE00", +"e@ c #00C400", +"f@ c #08D400", +"g@ c #0CDB00", +"h@ c #0CD000", +"i@ c #0AC900", +"j@ c #0BCC00", +"k@ c #08C400", +"l@ c #06C200", +"m@ c #02C400", +"n@ c #00CB02", +"o@ c #00CB07", +"p@ c #00B708", +"q@ c #00A908", +"r@ c #CAAD00", +"s@ c #D9C200", +"t@ c #D8BE00", +"u@ c #D6B700", +"v@ c #897200", +"w@ c #473B00", +"x@ c #1A1600", +"y@ c #0C1209", +"z@ c #232911", +"A@ c #26270B", +"B@ c #3D3F0E", +"C@ c #585B14", +"D@ c #7D831D", +"E@ c #899021", +"F@ c #74791F", +"G@ c #001216", +"H@ c #00323E", +"I@ c #00262E", +"J@ c #002E28", +"K@ c #009001", +"L@ c #00E700", +"M@ c #00CB00", +"N@ c #13C500", +"O@ c #14BE00", +"P@ c #13BD00", +"Q@ c #0CB500", +"R@ c #05B200", +"S@ c #00B601", +"T@ c #00B808", +"U@ c #00AC02", +"V@ c #009F04", +"W@ c #00900B", +"X@ c #AB9D00", +"Y@ c #C8BD00", +"Z@ c #373400", +"`@ c #283013", +" # c #748025", +".# c #D69C00", +"+# c #EBAB00", +"@# c #EEAE00", +"## c #C0AF00", +"$# c #13811D", +"%# c #0B8430", +"&# c #006D3A", +"*# c #005A3E", +"=# c #00C700", +"-# c #00C402", +";# c #00BF02", +"># c #00C002", +",# c #00C300", +"'# c #00C200", +")# c #00B900", +"!# c #00A200", +"~# c #009700", +"{# c #019500", +"]# c #018C00", +"^# c #029000", +"/# c #029500", +"(# c #A69900", +"_# c #8D8801", +":# c #3F4917", +"<# c #818921", +"[# c #00A90E", +"}# c #06C100", +"|# c #007004", +"1# c #00AE01", +"2# c #009102", +"3# c #009301", +"4# c #009800", +"5# c #00A000", +"6# c #00A300", +"7# c #039F00", +"8# c #019600", +"9# c #019E00", +"0# c #01AA00", +"a# c #7B7302", +"b# c #313A0F", +"c# c #010201", +"d# c #070A04", +"e# c #34390E", +"f# c #7F841C", +"g# c #CFAF00", +"h# c #A8AB02", +"i# c #009904", +"j# c #00C707", +"k# c #008906", +"l# c #008802", +"m# c #008301", +"n# c #008D00", +"o# c #00A600", +"p# c #00B600", +"q# c #00BB00", +"r# c #02B800", +"s# c #02AD00", +"t# c #01A900", +"u# c #02AF00", +"v# c #030C0B", +"w# c #48672A", +"x# c #899A22", +"y# c #00971C", +"z# c #009823", +"A# c #00A514", +"B# c #00CA07", +"C# c #00CA02", +"D# c #00B203", +"E# c #00BD02", +"F# c #00BC02", +"G# c #00BF00", +"H# c #00BC00", +"I# c #00B800", +"J# c #01B500", +"K# c #01A800", +"L# c #02A600", +"M# c #02B100", +"N# c #101708", +"O# c #C18D00", +"P# c #1E7407", +"Q# c #006212", +"R# c #007C1C", +"S# c #008F14", +"T# c #001700", +"U# c #00C601", +"V# c #00C100", +"W# c #00BA00", +"X# c #00B700", +"Y# c #02A500", +"Z# c #02A300", +"`# c #01B400", +" $ c #000709", +".$ c #0B0F05", +"+$ c #CB9400", +"@$ c #0D5704", +"#$ c #007011", +"$$ c #000B02", +"%$ c #007D0C", +"&$ c #009401", +"*$ c #009500", +"=$ c #00B000", +"-$ c #01AF00", +";$ c #01A100", +">$ c #009F00", +",$ c #01B300", +"'$ c #000404", +")$ c #00181B", +"!$ c #3EAF08", +"~$ c #0EA408", +"{$ c #007B06", +"]$ c #009000", +"^$ c #008F00", +"/$ c #009C00", +"($ c #01AB00", +"_$ c #019C00", +":$ c #03B300", +"<$ c #837207", +"[$ c #00B301", +"}$ c #00AE00", +"|$ c #00AB00", +"1$ c #02AE00", +"2$ c #02A800", +"3$ c #029900", +"4$ c #02B000", +"5$ c #856E07", +"6$ c #807508", +"7$ c #00A104", +"8$ c #00B100", +"9$ c #00B200", +"0$ c #009D00", +"a$ c #019300", +"b$ c #00AF00", +"c$ c #003A82", +"d$ c #000405", +"e$ c #B08B08", +"f$ c #897D08", +"g$ c #827B08", +"h$ c #002B06", +"i$ c #029600", +"j$ c #019100", +"k$ c #02AB00", +"l$ c #00387D", +"m$ c #003D91", +"n$ c #0035D2", +"o$ c #8E8008", +"p$ c #676208", +"q$ c #007E0B", +"r$ c #008701", +"s$ c #009400", +"t$ c #029700", +"u$ c #029F00", +"v$ c #028900", +"w$ c #00469B", +"x$ c #0047A8", +"y$ c #0035B1", +"z$ c #00102A", +"A$ c #9F9107", +"B$ c #646308", +"C$ c #837C06", +"D$ c #81B807", +"E$ c #54B008", +"F$ c #008A06", +"G$ c #008A02", +"H$ c #009100", +"I$ c #018700", +"J$ c #00A100", +"K$ c #004FAC", +"L$ c #0050C2", +"M$ c #0048CA", +"N$ c #001721", +"O$ c #7B7407", +"P$ c #817B07", +"Q$ c #AE9E07", +"R$ c #BDA607", +"S$ c #BDA308", +"T$ c #72C708", +"U$ c #55B507", +"V$ c #00A806", +"W$ c #008102", +"X$ c #019A00", +"Y$ c #019400", +"Z$ c #018D00", +"`$ c #008700", +" % c #0052B5", +".% c #0057D3", +"+% c #9C9207", +"@% c #645D07", +"#% c #8E8107", +"$% c #B4A308", +"%% c #BCA808", +"&% c #4CCE07", +"*% c #2FB707", +"=% c #00920B", +"-% c #006503", +";% c #029E00", +">% c #03A500", +",% c #007F00", +"'% c #0054BA", +")% c #005FE3", +"!% c #0051B7", +"~% c #001F3F", +"{% c #002A67", +"]% c #004FBC", +"^% c #0058BE", +"/% c #635907", +"(% c #766D08", +"_% c #A99C08", +":% c #B7AC07", +"<% c #26CD07", +"[% c #17C108", +"}% c #006007", +"|% c #00220D", +"1% c #004506", +"2% c #008800", +"3% c #028A00", +"4% c #008600", +"5% c #028300", +"6% c #0062E9", +"7% c #001D44", +"8% c #00070E", +"9% c #002D79", +"0% c #0051C6", +"a% c #004C9B", +"b% c #004875", +"c% c #6E6408", +"d% c #B3AA07", +"e% c #09C608", +"f% c #0AC508", +"g% c #08C307", +"h% c #00BD0A", +"i% c #005F07", +"j% c #001408", +"k% c #00370A", +"l% c #006A00", +"m% c #007800", +"n% c #027A00", +"o% c #009200", +"p% c #009A00", +"q% c #007AFF", +"r% c #0062F5", +"s% c #001E4B", +"t% c #000E1C", +"u% c #0061F4", +"v% c #0054F2", +"w% c #0047F1", +"x% c #003DF4", +"y% c #0040FA", +"z% c #517F07", +"A% c #00BB17", +"B% c #048300", +"C% c #000703", +"D% c #012E0D", +"E% c #065201", +"F% c #0C6100", +"G% c #146000", +"H% c #0068EE", +"I% c #0057D6", +"J% c #005FF5", +"K% c #002153", +"L% c #000D1A", +"M% c #000205", +"N% c #000B1B", +"O% c #0066F4", +"P% c #005EF3", +"Q% c #0051F2", +"R% c #0045E7", +"S% c #003DD5", +"T% c #0042D3", +"U% c #367907", +"V% c #00BF0E", +"W% c #023F0E", +"X% c #0D7A01", +"Y% c #199D00", +"Z% c #0055CD", +"`% c #0056D9", +" & c #005BF0", +".& c #005BD5", +"+& c #00162C", +"@& c #003168", +"#& c #006FF6", +"$& c #006AF4", +"%& c #0060F7", +"&& c #0058F2", +"*& c #0051DF", +"=& c #004CBF", +"-& c #005AAC", +";& c #0092AF", +">& c #00C007", +",& c #049A00", +"'& c #07570E", +")& c #0048B9", +"!& c #0053E1", +"~& c #0056F0", +"{& c #001533", +"]& c #005FC6", +"^& c #0071F6", +"/& c #006DF6", +"(& c #0069F9", +"_& c #0068FA", +":& c #0060DF", +"<& c #005DB4", +"[& c #006A9D", +"}& c #007F84", +"|& c #00664F", +"1& c #008D5B", +"2& c #00B707", +"3& c #049000", +"4& c #013900", +"5& c #00181C", +"6& c #0043B6", +"7& c #0051E8", +"8& c #0057F5", +"9& c #0051D1", +"0& c #0061CA", +"a& c #0072F8", +"b& c #006EF8", +"c& c #006BF9", +"d& c #0069FA", +"e& c #0064F8", +"f& c #0068F0", +"g& c #006FE3", +"h& c #006BBB", +"i& c #005BEE", +"j& c #00596A", +"k& c #00A41E", +"l& c #000E10", +"m& c #0046C2", +"n& c #0056EF", +"o& c #005CFF", +"p& c #004FE5", +"q& c #00091B", +"r& c #006DF8", +"s& c #006AF9", +"t& c #0061FA", +"u& c #0064F9", +"v& c #006BDE", +"w& c #005187", +"x& c #005E8C", +"y& c #009D36", +"z& c #00A319", +"A& c #00610B", +"B& c #007D12", +"C& c #0046C4", +"D& c #004ED5", +"E& c #005CF7", +"F& c #0066FF", +"G& c #004DFF", +"H& c #00133D", +"I& c #0065EA", +"J& c #006AF8", +"K& c #0067FB", +"L& c #0066F9", +"M& c #0063FA", +"N& c #005FFA", +"O& c #005EF8", +"P& c #005DE8", +"Q& c #006EF0", +"R& c #00519D", +"S& c #008C62", +"T& c #009632", +"U& c #00A308", +"V& c #085205", +"W& c #001417", +"X& c #0047BF", +"Y& c #0059E4", +"Z& c #005BFE", +"`& c #006FFF", +" * c #0048FF", +".* c #000F32", +"+* c #004194", +"@* c #0066F5", +"#* c #005DF9", +"$* c #0056F7", +"%* c #0056FA", +"&* c #005DFA", +"** c #0059F6", +"=* c #0060F5", +"-* c #0046A0", +";* c #005A8A", +">* c #007D93", +",* c #00885B", +"'* c #009425", +")* c #09A408", +"!* c #317B07", +"~* c #061000", +"{* c #001013", +"]* c #0044B0", +"^* c #0064F4", +"/* c #0077FF", +"(* c #004EFF", +"_* c #0050FE", +":* c #0064EE", +"<* c #0065F6", +"[* c #005FF6", +"}* c #005AF9", +"|* c #0055FA", +"1* c #0056F6", +"2* c #0058F9", +"3* c #0064DE", +"4* c #004EA8", +"5* c #006AB8", +"6* c #007386", +"7* c #007E51", +"8* c #4B6807", +"9* c #5A7B06", +"0* c #0D1200", +"a* c #000F11", +"b* c #000B0D", +"c* c #000B0C", +"d* c #004EBE", +"e* c #0069F4", +"f* c #0068FF", +"g* c #005EFF", +"h* c #0061FF", +"i* c #003A90", +"j* c #002559", +"k* c #0059C8", +"l* c #0061F1", +"m* c #005BF2", +"n* c #005AF0", +"o* c #005AEF", +"p* c #005BED", +"q* c #0061D9", +"r* c #0066B5", +"s* c #006E8A", +"t* c #009157", +"u* c #307B08", +"v* c #636507", +"w* c #727206", +"x* c #908E04", +"y* c #908F08", +"z* c #0042A3", +"A* c #004DB6", +"B* c #0063E0", +"C* c #0074FF", +"D* c #0067F6", +"E* c #0068F5", +"F* c #006BF4", +"G* c #0069E8", +"H* c #0069F1", +"I* c #0067EC", +"J* c #0063E9", +"K* c #0062E7", +"L* c #0062E6", +"M* c #0061EA", +"N* c #005CE7", +"O* c #005BDB", +"P* c #005EBD", +"Q* c #005D6B", +"R* c #005E6C", +"S* c #006674", +"T* c #3F6907", +"U* c #8B7306", +"V* c #AE8F04", +"W* c #897506", +"X* c #001518", +"Y* c #003484", +"Z* c #00459D", +"`* c #0054B6", +" = c #0066D9", +".= c #006BE8", +"+= c #0074EF", +"@= c #0074EC", +"#= c #006FE7", +"$= c #006CE7", +"%= c #003F82", +"&= c #005BC6", +"*= c #0060D6", +"== c #0062DC", +"-= c #0065E2", +";= c #0062E2", +">= c #0058E7", +",= c #0054E9", +"'= c #0052DC", +")= c #0057B4", +"!= c #00677C", +"~= c #007D3B", +"{= c #008E08", +"]= c #11A308", +"^= c #659C00", +"/= c #3B5204", +"(= c #586A05", +"_= c #675203", +":= c #001A1E", +"<= c #002A30", +"[= c #0055BE", +"}= c #0057B8", +"|= c #0058B0", +"1= c #005CB7", +"2= c #0061C1", +"3= c #0068CF", +"4= c #0072E0", +"5= c #0074E8", +"6= c #005EC6", +"7= c #000C1A", +"8= c #00204A", +"9= c #003884", +"0= c #0065E4", +"a= c #0064DD", +"b= c #0064DF", +"c= c #0059DE", +"d= c #0052E6", +"e= c #004DE0", +"f= c #004FC9", +"g= c #005C9C", +"h= c #006B61", +"i= c #007B30", +"j= c #00880D", +"k= c #088D08", +"l= c #128908", +"m= c #238308", +"n= c #417E07", +"o= c #658407", +"p= c #899206", +"q= c #9C9603", +"r= c #0C0C01", +"s= c #020200", +"t= c #040806", +"u= c #202912", +"v= c #002F37", +"w= c #006FD0", +"x= c #006CC5", +"y= c #0057B6", +"z= c #0055B6", +"A= c #0056BD", +"B= c #005CCD", +"C= c #0062E3", +"D= c #004EB8", +"E= c #002C60", +"F= c #005DE3", +"G= c #0061DF", +"H= c #0054D4", +"I= c #004ECF", +"J= c #004AC1", +"K= c #004DA2", +"L= c #005581", +"M= c #005D5A", +"N= c #006135", +"O= c #006A13", +"P= c #177607", +"Q= c #397D07", +"R= c #547A05", +"S= c #747B03", +"T= c #968503", +"U= c #0D160E", +"V= c #242D11", +"W= c #585F17", +"X= c #005EF0", +"Y= c #0048C2", +"Z= c #0044BA", +"`= c #0047CC", +" - c #0043CC", +".- c #003DB1", +"+- c #0052D1", +"@- c #0059DB", +"#- c #004DB4", +"$- c #003D8E", +"%- c #005DCC", +"&- c #0058BF", +"*- c #0050B3", +"=- c #0046A6", +"-- c #00419C", +";- c #004190", +">- c #004577", +",- c #004951", +"'- c #005423", +")- c #206C04", +"!- c #3F7605", +"~- c #587506", +"{- c #717105", +"]- c #77670A", +"^- c #0F1407", +"/- c #1B2008", +"(- c #2C300B", +"_- c #0044F6", +":- c #003ADA", +"<- c #0038D0", +"[- c #0033B9", +"}- c #003773", +"|- c #002A62", +"1- c #000F25", +"2- c #002968", +"3- c #004CAF", +"4- c #005FC9", +"5- c #0058AD", +"6- c #004B93", +"7- c #004292", +"8- c #002258", +"9- c #003654", +"0- c #00613A", +"a- c #2C8403", +"b- c #0B1309", +"c- c #08100A", +"d- c #020201", +"e- c #17200D", +"f- c #003AA9", +"g- c #002B94", +"h- c #002993", +"i- c #00101E", +"j- c #00121D", +"k- c #000306", +"l- c #003FA9", +"m- c #001131", +"n- c #00323A", +"o- c #011305", +"p- c #30604C", +"q- c #1D6C68", +"r- c #03262B", +"s- c #001D24", +"t- c #041719", +"u- c #203217", +"v- c #000506", +"w- c #0035AF", +"x- c #002985", +"y- c #00121C", +"z- c #000507", +"A- c #000103", +"B- c #030400", +"C- c #1E1B07", +"D- c #1B1D0D", +"E- c #17676A", +"F- c #00A6CD", +"G- c #009CC3", +"H- c #009CC2", +"I- c #00333F", +"J- c #010606", +"K- c #000708", +"L- c #003488", +"M- c #00306D", +"N- c #003560", +"O- c #002F55", +"P- c #001727", +"Q- c #4D7327", +"R- c #2D310C", +"S- c #030200", +"T- c #0C0F07", +"U- c #095B68", +"V- c #00ABD6", +"W- c #00B0DB", +"X- c #00ADD6", +"Y- c #003D4C", +"Z- c #000F13", +"`- c #00181E", +" ; c #004AA3", +".; c #003B73", +"+; c #00305B", +"@; c #002E55", +"#; c #003869", +"$; c #003565", +"%; c #003566", +"&; c #002950", +"*; c #002B52", +"=; c #003D6B", +"-; c #004166", +";; c #4E7025", +">; c #2F300A", +",; c #110F02", +"'; c #040300", +"); c #010C0D", +"!; c #002B35", +"~; c #00667E", +"{; c #00AAD2", +"]; c #007D9C", +"^; c #00738F", +"/; c #001A21", +"(; c #000608", +"_; c #000202", +":; c #003E6F", +"<; c #003659", +"[; c #004389", +"}; c #0054A8", +"|; c #0053A5", +"1; c #0050A3", +"2; c #004FA3", +"3; c #004FA7", +"4; c #004FA8", +"5; c #004DA5", +"6; c #0049A0", +"7; c #00418F", +"8; c #003A78", +"9; c #47440A", +"0; c #382E01", +"a; c #070500", +"b; c #002E38", +"c; c #0096BB", +"d; c #00A9D2", +"e; c #00AFD9", +"f; c #00262F", +"g; c #005060", +"h; c #004C5B", +"i; c #002F38", +"j; c #002D54", +"k; c #0048BC", +"l; c #001E56", +"m; c #001B58", +"n; c #534700", +"o; c #5F5000", +"p; c #272000", +"q; c #030300", +"r; c #00080A", +"s; c #004D5F", +"t; c #00AAD3", +"u; c #00171C", +"v; c #000C0F", +"w; c #00643A", +"x; c #00532F", +"y; c #002B15", +"z; c #003E56", +"A; c #005481", +"B; c #004D8C", +"C; c #004696", +"D; c #00429B", +"E; c #003E9C", +"F; c #003FA1", +"G; c #0040AA", +"H; c #003EB0", +"I; c #0036B0", +"J; c #00268D", +"K; c #002081", +"L; c #685B00", +"M; c #9D8900", +"N; c #9F8700", +"O; c #423500", +"P; c #070701", +"Q; c #003B49", +"R; c #009DC3", +"S; c #00A8D1", +"T; c #0092B6", +"U; c #008CAF", +"V; c #004D62", +"W; c #002E3A", +"X; c #00191F", +"Y; c #00760D", +"Z; c #005A08", +"`; c #003B06", +" > c #004D40", +".> c #006267", +"+> c #00587E", +"@> c #005094", +"#> c #0048A6", +"$> c #0044AF", +"%> c #0045B7", +"&> c #0045C4", +"*> c #003FC4", +"=> c #0030A7", +"-> c #002284", +";> c #6D6000", +">> c #968300", +",> c #BEA600", +"'> c #8A7100", +")> c #3B3301", +"!> c #131808", +"~> c #041819", +"{> c #004556", +"]> c #008CAE", +"^> c #00ABD5", +"/> c #00AAD4", +"(> c #008FB2", +"_> c #00232B", +":> c #00090B", +"<> c #159800", +"[> c #0B5900", +"}> c #0B4F00", +"|> c #005924", +"1> c #00683E", +"2> c #005557", +"3> c #004D7A", +"4> c #004C9F", +"5> c #0040A0", +"6> c #003C9A", +"7> c #003791", +"8> c #0045BB", +"9> c #00277B", +"0> c #514C00", +"a> c #796A00", +"b> c #A49000", +"c> c #B09100", +"d> c #B99F00", +"e> c #4C4704", +"f> c #050904", +"g> c #011519", +"h> c #005A70", +"i> c #00AAD5", +"j> c #00ACD5", +"k> c #003745", +"l> c #000001", +"m> c #105700", +"n> c #0F5100", +"o> c #006B0F", +"p> c #007726", +"q> c #006141", +"r> c #005466", +"s> c #004880", +"t> c #00347C", +"u> c #00183E", +"v> c #403D00", +"w> c #655800", +"x> c #917D00", +"y> c #AF9100", +"z> c #BDA300", +"A> c #695E00", +"B> c #1E1D02", +"C> c #002E39", +"D> c #00647C", +"E> c #00ACD6", +"F> c #003F4E", +"G> c #000C0E", +"H> c #094D00", +"I> c #094800", +"J> c #027800", +"K> c #007F0E", +"L> c #006D2B", +"M> c #00594B", +"N> c #003850", +"O> c #002D59", +"P> c #003075", +"Q> c #003D96", +"R> c #003896", +"S> c #00368A", +"T> c #002F74", +"U> c #002149", +"V> c #5A5500", +"W> c #806F00", +"X> c #9E8500", +"Y> c #B49700", +"Z> c #B29B00", +"`> c #9B8900", +" , c #5C5300", +"., c #000406", +"+, c #004453", +"@, c #009CC1", +"#, c #00A8D2", +"$, c #00ABD4", +"%, c #004100", +"&, c #003C00", +"*, c #088000", +"=, c #008302", +"-, c #007316", +";, c #006332", +">, c #004542", +",, c #003F5D", +"', c #003A7D", +"), c #0046A9", +"!, c #0040AD", +"~, c #003FA6", +"{, c #003483", +"], c #00254E", +"^, c #676100", +"/, c #917C00", +"(, c #A58800", +"_, c #B49800", +":, c #A89100", +"<, c #9C8800", +"[, c #9A8800", +"}, c #001419", +"|, c #004B5D", +"1, c #00A9D3", +"2, c #00ABD3", +"3, c #007693", +"4, c #003440", +"5, c #003B05", +"6, c #003405", +"7, c #008300", +"8, c #007906", +"9, c #007019", +"0, c #006236", +"a, c #005F67", +"b, c #00457B", +"c, c #003F99", +"d, c #00379F", +"e, c #00339A", +"f, c #002875", +"g, c #001F47", +"h, c #5D5500", +"i, c #846E00", +"j, c #957900", +"k, c #A38B00", +"l, c #968100", +"m, c #8B7800", +"n, c #847100", +"o, c #0091B5", +"p, c #005C73", +"q, c #003207", +"r, c #00890A", +"s, c #008504", +"t, c #018100", +"u, c #007401", +"v, c #006316", +"w, c #00694A", +"x, c #004668", +"y, c #00317E", +"z, c #002D8C", +"A, c #002989", +"B, c #001F68", +"C, c #001944", +"D, c #514900", +"E, c #725F00", +"F, c #806600", +"G, c #867200", +"H, c #000E12", +"I, c #003542", +"J, c #005367", +"K, c #00ADD7", +"L, c #009BC0", +"M, c #00728E", +"N, c #023400", +"O, c #008515", +"P, c #008209", +"Q, c #047F00", +"R, c #077400", +"S, c #015A00", +"T, c #00612C", +"U, c #003C4C", +"V, c #002B6A", +"W, c #002DAB", +"X, c #00238C", +"Y, c #001C5D", +"Z, c #5F5400", +"`, c #8A7300", +" ' c #A28100", +".' c #A18900", +"+' c #A48A00", +"@' c #AB9200", +"#' c #004455", +"$' c #00A5CE", +"%' c #00AED8", +"&' c #0088A9", +"*' c #005063", +"=' c #00222A", +"-' c #000607", +";' c #0D3C00", +">' c #007317", +",' c #00640A", +"'' c #006000", +")' c #036000", +"!' c #0B6400", +"~' c #007F27", +"{' c #004B56", +"]' c #003882", +"^' c #002FAD", +"/' c #002FC7", +"(' c #0025A0", +"_' c #001B69", +":' c #635600", +"<' c #967A00", +"[' c #B18D00", +"}' c #B39900", +"|' c #B59B00", +"1' c #C3A500", +"2' c #00586D", +"3' c #007491", +"4' c #00596E", +"5' c #000F12", +"6' c #005C00", +"7' c #016300", +"8' c #005D0C", +"9' c #005C15", +"0' c #00500D", +"a' c #00342E", +"b' c #002B56", +"c' c #004193", +"d' c #003AA0", +"e' c #004097", +"f' c #00255B", +"g' c #856300", +"h' c #B39300", +"i' c #A79000", +"j' c #666F00", +"k' c #778200", +"l' c #000D10", +"m' c #0082A2", +"n' c #004253", +"o' c #00141B", +"p' c #157E00", +"q' c #007100", +"r' c #008517", +"s' c #00751A", +"t' c #005A0D", +"u' c #005A06", +"v' c #002F5A", +"w' c #003F90", +"x' c #0042A6", +"y' c #003D8F", +"z' c #00265C", +"A' c #604200", +"B' c #8D6900", +"C' c #AF9000", +"D' c #A89300", +"E' c #717C00", +"F' c #003B4A", +"G' c #0094B8", +"H' c #009DC2", +"I' c #0082A1", +"J' c #000D11", +"K' c #22BB00", +"L' c #006800", +"M' c #008114", +"N' c #007817", +"O' c #005C0A", +"P' c #005E0B", +"Q' c #00355A", +"R' c #003887", +"S' c #0042A0", +"T' c #003883", +"U' c #583F00", +"V' c #936D00", +"W' c #A48800", +"X' c #AB9700", +"Y' c #00171D", +"Z' c #00303C", +"`' c #00657D", +" ) c #007593", +".) c #003340", +"+) c #001114", +"@) c #026C00", +"#) c #00690A", +"$) c #007412", +"%) c #006209", +"&) c #00650D", +"*) c #003C5D", +"=) c #003781", +"-) c #003990", +";) c #003477", +">) c #584300", +",) c #8E6D00", +"') c #9C8000", +")) c #AE9900", +"!) c #000A0C", +"~) c #00A6CE", +"{) c #0085A5", +"]) c #000A0D", +"^) c #048000", +"/) c #006D0B", +"() c #006A07", +"_) c #007012", +":) c #004764", +"<) c #003D81", +"[) c #00317C", +"}) c #002E6D", +"|) c #654E00", +"1) c #856900", +"2) c #9A7E00", +"3) c #B8A200", +"4) c #001920", +"5) c #004759", +"6) c #007F9E", +"7) c #008DAE", +"8) c #006B84", +"9) c #001E26", +"0) c #000305", +"a) c #005A05", +"b) c #006604", +"c) c #006A04", +"d) c #007615", +"e) c #00AB61", +"f) c #005168", +"g) c #003977", +"h) c #002B6C", +"i) c #002354", +"j) c #343300", +"k) c #6D5800", +"l) c #856B00", +"m) c #B49B00", +"n) c #00151B", +"o) c #00647E", +"p) c #009BC1", +"q) c #007895", +"r) c #003846", +"s) c #001115", +"t) c #005E00", +"u) c #006300", +"v) c #007717", +"w) c #00A45B", +"x) c #005664", +"y) c #00346C", +"z) c #00245E", +"A) c #001833", +"B) c #463C00", +"C) c #6F5E00", +"D) c #8F7600", +"E) c #9D8400", +"F) c #00222B", +"G) c #006F8A", +"H) c #0087A7", +"I) c #0094B7", +"J) c #004152", +"K) c #00090C", +"L) c #055A00", +"M) c #007C1A", +"N) c #00A659", +"O) c #005961", +"P) c #003462", +"Q) c #001C53", +"R) c #4F4400", +"S) c #716200", +"T) c #A48D00", +"U) c #00232C", +"V) c #00556B", +"W) c #0087A8", +"X) c #00B1DC", +"Y) c #004353", +"Z) c #0D8100", +"`) c #006204", +" ! c #00741A", +".! c #008745", +"+! c #005A5B", +"@! c #002C51", +"#! c #00164E", +"$! c #150F00", +"%! c #574B00", +"&! c #8E8400", +"*! c #002129", +"=! c #007D9B", +"-! c #006983", +";! c #002D39", +">! c #006906", +",! c #005102", +"'! c #008A46", +")! c #005953", +"!! c #002747", +"~! c #00062D", +"{! c #231600", +"]! c #675B00", +"^! c #A7A300", +"/! c #002630", +"(! c #001B22", +"_! c #00242D", +":! c #00303B", +" ", +" , ' ) ! ~ { ] ^ / ( _ : < [ } | 1 2 3 ", +" 4 5 6 7 8 9 0 a b c d e f g h i j k l m n o p ", +" q r s t u v w x y z A B C D E F G H I J K L M N O P ", +" Q R S T U V W X Y Z ` F ...+.C .@.#.$.%.&. *.=.-.;.>. ", +" ,.'.).!.~.{.].^./.(._.:.:.<.[.}.|.1.2.3.4. 5.6.7.8.9.0.a.b. ", +" c.d.e.f.g.V f W h.i.^.j.k.l.m.n.o.p.q.r.s. t.u.v.w.x.y.z.A.B.C. ", +" D.E.F.G.H.I.J.K.L.M.N._ _ O.P.Q.R.S.T.U. V.W.X.Y.Z.`. +.+++@+#+ ", +" $+%+&+*+=+-+;+>+,+'+)+!+!+~+{+]+^+/+(+_+ :+<+[+}+|+1+2+3+1+4+5+6+7+ ", +" 8+9+F.0+a+b+c+d+e+f+f+g+h+i+j+~+k+l+m+ n+o+p+q+3+r+s+s+3+t+u+v+w+x+ ", +" y+z+A+B+C+D+b+E+H.F+G+H+H+H+I+J+K+L+ H+H+H+H+ M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+`+ @ ", +" .@+@@@#@$@%@b+H+&@*@=@H+H+H+-@;@>@,@'@)@H+H+H+!@H+H+H+~@{@]@^@/@(@_@:@t+<@t+[@}@|@1@2@ ", +" 3@4@5@6@7@8@9@H+0@a@H+H+H+H+H+H+H+H+H+H+H+H+H+H+b@H+c@d@R+e@f@g@h@i@j@k@l@m@n@o@@+p@q@ ", +" r@s@t@u@v@w@x@H+H+H+y@z@A@B@C@D@E@F@ G@H@I@J@H+H+H+K@L@2+M@v+N@O@P@Q@R@S@T@U@V@W@ ", +" X@Y@Z@H+H+H+H+`@ #.#+#@#@#@#@##### $#%#&#*#H+H+H+H+=#-#;#>#,#'#)#!#~#{#]#^#/# ", +" (#_#H+H+H+H+:#<#@#@#@#@#@#@#@##### [#}#}#}#H+H+H+H+|#1#b.2#3#4#5#6#!#7#8#9#0# ", +" a#b#H+c#d#e#f#@#@#@#@#@#@#g#g#g#h# i#j#}#}#}#}#H+H+H+H+k#l#m#m#n#o#p#q#r#s#t#u# ", +" H+H+v#w#x#@#@#@#@#@#@#@#@#g#g#g#g# y#z#A#B#}#C#C#C#}#H+H+H+H+D#E#F#q#G#H#I#J#K#L#M# ", +" H+H+H+N#O#@#@#@#@#@#@#@#@#@#g#g#g#g#P#Q#R#S#}#}#C#C#C#C#C#H+H+H+H+T#U#V#'#W#X#M#Y#Z#`# ", +" $H+H+.$H++$@#@#@#@#@#@#@#@#g#g#g#g#g#@$#$}#C#C#C#C#C#C#C#C#C#$$H+H+%$&$*$4#6#=$-$;$>$,$ ", +" H+H+'$)$H+@#@#@#@#@#@#@#@#g#g#g#g#g#!$~$}#C#C#C#C#C#C#C#C#C#C#}#H+H+H+{$]$^$/$[+($_$_$:$ ", +" H+H+'@H+H+<$@#@#@#@#@#@#g#g#g#g#g#g#}#}#}#C#C#C#C#C#}#C#C#C#C#C#H+H+H+H+[$}$|$1$2$3$4#4$ ", +" H+H+H+H+H+ 5$6$@#@#@#g#g#g#g#g#g#g#}#}#}#}#C#C#C#C#C#C#}#C#C#C#C#}#H+H+H+7$8$9$6#0${#a$b$ ", +" c$ H+H+H+d$H+H+ e$f$g$@#g#g#g#g#g#g#g#}#}#}#}#C#C#C#C#C#C#C#}#C#C#C#C#}#H+H+H+h$4#Y#;$;$i$j$k$ ", +" l$m$n$ H+H+H+H+H+ o$p$@#g#g#g#g#g#g#}#}#}#C#C#C#C#C#C#C#C#C#C#}#C#C#C#}#q$H+H+H+r$s$t$u$]$v$5# ", +" w$x$y$z$H+H+H+H+ A$B$C$g#g#g#g#D$E$}#}#C#C#C#C#C#C#C#C#C#C#C#}#C#C#C#C#F$H+H+H+G$s$H$8#]#I$J$ ", +" K$L$M$H+H+N$H+H+ O$P$Q$R$S$T$U$}#}#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#V$H+H+H+W$0$X$Y$Z$`$J$ ", +" %.%H+H+H+H+H+ +%@%#%$%%%&%*%}#}#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#=%H+H+-%;%>%i$`$,%^# ", +" '%)%!%~%H+H+{%]%^% /%(%_%:%<%[%}#}#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#}%|%H+1%2%4#3%4%5%~# ", +" '%6%7%8%H+H+9%0%a%b% c%##d%e%f%g%h%C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#i%j%H+k%l%m%n%o%p%}+ ", +" q%.%r%s%H+H+t%H+u%v%w%x%y% z%C#A%C#C#C#C#}#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#B%C%H+D%E%F%G% ", +" H%I%J%K%L%H+M%N%O%P%Q%R%S%T% U%}#V%C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#}#H+H+W%X%Y% ", +" Z%`% &.&+&H+H+@&#&$&%&&&*&=&-&;& }#>&C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#,&P H+H+'& ", +" )&!&~&{&H+H+H+]&^&/&(&_&_&:&<&[&}&|&1&2&}#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#}#3&4&H+H+5& ", +" 6&7&8&9&H+H+H+0&a&b&c&d&e&f&g&h&i&j&1&k&}#}#}#}#C#C#C#C#C#C#C#C#C#C#C#C#C#}#}#}#H+H+H+H+l& ", +" m&n&o&p&q&H+H+H+r&r&s&s&t&u&c&v&i&w&x&1&y&z&}#}#C#C#C#C#C#C#C#C#C#C#C#C#}#}#A&B&H+H+H+H+ ", +" C&D&E&F&G&H&H+H+H+I&J&K&L&M&N&O&P&Q&i&R&i&S&T&U&}#C#C#C#C#C#C#C#C#C#C#}#}#}#}#V& H+H+H+H+W& ", +" X&Y&Z&`& *.*H+H+H++*@*M&#*$*%*&***=*i&-*;*>*,*'*)*C#C#C#C#C#C#C#C#C#}#}#}#}#!*~*H+H+H+{*H+ ", +" ]*^*F&/*(*_*H+H+H+H+:*<*[*}*%*|*1*2*i&3*4*5*6*7*}#}#}#C#C#C#C#C#C#C#}#}#}#8*9*0*a*H+H+b*c* ", +" d*e*f*g*o&h*i*j*H+H+H+k*O%l*m*n*o*m*i&3*p*q*r*s*t*}#}#}#C#C#C#C#C#}#}#u*v*w*x*y*H+H+H+H+H+ ", +" z*A*B*q%C*D*E*F*G*H+H+H+H+H*I*J*K*L*M*i&3*p*N*O*P*Q*R*S*}#}#}#}#}#}#}#}#T*U*V*W*H+H+H+X*H+ ", +" Y*Z*`* =.=+=@=#=$=%=H+H+H+&=*===-=B*;=i&3*p*>=,='=)=!=~={=]=}#}#}#}#^=/=(=H+_=H+H+H+:=<=H+ ", +" [=}=|=1=2=3=4=5=6=7=H+H+H+8=9=0=a=b=i&3*p*c=d=e=f=g=h=i=j=k=l=m=n=o=p=q=H+r=s=t=u=v= ", +" w=x=y=z=A=B=C=D=E=H+H+H+H+F=)%G=i&3*p*.&H=I=J=K=L=M=N=O=P=Q=R=S=T=H+H+H+U=V=W= ", +" X=Y=Z=`= -.-H+H+H+H+H++-@-#-$-p*%-&-*-=---;->-,-'-)-!-~-{-]-H+H+H+^-/-(- ", +" _-:-<-[-H+H+H+H+}-|-1-2-3-4-5-6-7-8-H+H+9-0-a-H+H+H+H+H+H+b-c-d-e- ", +" f-g-h-H+H+i-j-k-H+H+H+H+H+l-m-H+H+H+H+n-o-H+H+H+H+H+p-q-r-s-t-u-v- ", +" w-x-H+H+H+y-z-A-H+H+H+H+H+H+H+H+H+H+H+H+H+B-C-D-E-F-G-H-I-J-K- ", +" L-M-H+N-O-P-H+H+H+H+H+H+H+H+H+H+H+X*Q-R-S-T-U-V-W-X-Y-G@Z-`- ", +" ;H+H+H+H+.;+;@;#;$;%;&;*;*;=;-; ;;>;,;';);!;~;{;];^;/;(;_; ", +" :;N-<;[;};|;1;2;3;4;5;6;7;8; 9;0;a;H+'@b;c;d;e;f;)@b@'@ ", +" g;h;i;j;k;k;k;k;k;k;k;k;k;l;m; n;o;p;q;H+r;s;X-t;H@u;v;(;'@ ", +" w;x;y;z;A;B;C;D;E;F;G;H;I;J;K; L;M;N;O;P;_;Q;R;S;T;U;V;W;X; ", +" Y;Z;`; >.>+>@>#>$>%>X&&>*>=>-> ;>>>,>'>)>!>~>{>]>^>/>R;(>_>:>H+ ", +" <>[>}>|>1>2>3>4>5>6>7>8>8>9>m; 0>a>b>c>d>e>f>g>h>i>t;j>t;k>H+l>H+ ", +" m>n>o>p>q>r>s>t>8>8>8>8>8>u> v>w>x>y>z>A>B>d$C>D>X-d;E>F>G>H+)@ ", +" H>I>J>K>L>M>N>O>P>Q>R>S>T>U> V>W>X>Y>Z>`> ,z-.,+,@,#,$,j>Q;K-b@!@ ", +" %,&,*,=,-,;,>,,,',),!,~,{,], ^,/,(,_,:,<,[,H+(;},|,H-1,2,3,4,d$H+)@ ", +" 5,6,5%7,8,9,0,a,b,c,d,e,f,g, h,i,j,k,l,m,n, H+v-b;o,$,$,t;p,X;z-_;l> ", +" q,r,s,t,u,v,w,x,y,z,A,B,C, D,E,F,G,n,G, H+d$H,I,J,K,$,L,M,`-v-d$H+ ", +" N,O,P,Q,R,S,T,U,V,g-W,X,Y, Z,`, '.'+'@' (;H+#'$'%'t;&'*'='-'_;H+ ", +" ;'>',''')'!'~'{']'^'/'('_' :'<'['}'|'1' H+b*`-2'3'$,$,E>4'5'v-., ", +" 6'7'8'9'0' a'b'c'd'e'f' g'h'i'j'k' l'G@h>W-d;$,m'n'o'z-)@ ", +" p'q'r's't'u' v'w'x'y'z'A'B'C'D'E' )@Z-F'G'H'$,t;I'I,J''@H+ ", +" K'L'M'N'O'P' Q'R'S'T' U'V'W'X' Y'Z'`'X-t;c; ).)+)b@H+ ", +" @)#)$)%)&) *)=)-);) >),)'))) '@!)J,~)X-t;K,{)I-])!@H+ ", +" ^)u'/)()_) :)<)[)}) |)1)2)3) (;4)5)6)$,S;7)8)9)0)_; ", +" a)b)c)d)e)f)g)h)i)j)k)l)2)m) z-n)o)p)p)q)`'r)s)-'d$ ", +" q't)u)v)w)x)y)z)A)B)C)D)E) !@v;F)#'G)H)I)3,J)K)'@)@ ", +" L)''M)N)O)P)Q) R)S)T) $])U)V)W)X)%'Y)v;b@'@ ", +" Z)`) !.!+!@!#!$!%!&!X@ b*v;*!|,=!-!;!z-{* ", +" >!,!'!)!!!~!{!]!^! -''@/!F'(!_!:!`- ", +" c #E9C900", -", c #EDC600", -"' c #F0C600", -") c #F2CB00", -"! c #F5D300", -"~ c #F6DA00", -"{ c #F6DD00", -"] c #F5DE00", -"^ c #F3DF00", -"/ c #EFE000", -"( c #EBE100", -"_ c #E6E000", -": c #DFDB00", -"< c #D5D200", -"[ c #CBC800", -"} c #CEC200", -"| c #D0C100", -"1 c #D4C000", -"2 c #DABE00", -"3 c #DFC000", -"4 c #E2C500", -"5 c #E5CB00", -"6 c #E7CD00", -"7 c #EBCB00", -"8 c #EEC800", -"9 c #F1C800", -"0 c #F4CD00", -"a c #F6D300", -"b c #F7D800", -"c c #F7DC00", -"d c #F6DE00", -"e c #F4DF00", -"f c #F0E000", -"g c #EBE000", -"h c #E6DF00", -"i c #E0DA00", -"j c #D6D200", -"k c #CAC700", -"l c #D2C400", -"m c #D2C500", -"n c #D4C500", -"o c #D9C400", -"p c #DFC400", -"q c #E4C500", -"r c #E7C900", -"s c #E9CE00", -"t c #ECD000", -"u c #EECF00", -"v c #F1CD00", -"w c #F6CF00", -"x c #F8D300", -"y c #F8D600", -"z c #F9D900", -"A c #F8DD00", -"B c #F1DD00", -"C c #EBDB00", -"D c #E6D800", -"E c #E0D500", -"F c #D7CF00", -"G c #D6C800", -"H c #D7CA00", -"I c #DACA00", -"J c #DFCB00", -"K c #E6CB00", -"L c #EBCD00", -"M c #F0D200", -"N c #F2D400", -"O c #F4D500", -"P c #F6D500", -"Q c #F8D500", -"R c #F9D400", -"S c #FAD200", -"T c #FBD600", -"U c #FBDB00", -"V c #F9DD00", -"W c #F3D900", -"X c #EBD200", -"Y c #E4CC00", -"Z c #DFCA00", -"` c #03AD07", -" . c #02B20B", -".. c #01B40E", -"+. c #01B20F", -"@. c #01A60C", -"#. c #D8CA00", -"$. c #DCCD00", -"%. c #DFCF00", -"&. c #E5D000", -"*. c #EBD100", -"=. c #F3D400", -"-. c #F4D600", -";. c #F6D700", -">. c #F7DA00", -",. c #F8DB00", -"'. c #F9DB00", -"). c #FAD700", -"!. c #FAD000", -"~. c #FBD300", -"{. c #FBD800", -"]. c #FADA00", -"^. c #E1BF00", -"/. c #DDBB00", -"(. c #0AAE02", -"_. c #05AF04", -":. c #02B107", -"<. c #01B40B", -"[. c #00B60E", -"}. c #00B40F", -"|. c #00AB0D", -"1. c #DCCF00", -"2. c #E0D000", -"3. c #E5D100", -"4. c #F2D300", -"5. c #F5D600", -"6. c #F6D900", -"7. c #F6DC00", -"8. c #F6D200", -"9. c #F4D400", -"0. c #EDCD00", -"a. c #E5C200", -"b. c #DDB600", -"c. c #0BC001", -"d. c #07BD02", -"e. c #04BB03", -"f. c #02BA05", -"g. c #01BA08", -"h. c #01BA0B", -"i. c #01B70C", -"j. c #01AD0A", -"k. c #D3C800", -"l. c #DACF00", -"m. c #DED000", -"n. c #E3D000", -"o. c #E8CE00", -"p. c #ECCE00", -"q. c #EFCF00", -"r. c #F1D100", -"s. c #F1D600", -"t. c #F0D900", -"u. c #EFDA00", -"v. c #EFDB00", -"w. c #EFD800", -"x. c #ECD400", -"y. c #E5CA00", -"z. c #E1C400", -"A. c #DBBA00", -"B. c #02B102", -"C. c #03B802", -"D. c #04C301", -"E. c #05CD00", -"F. c #05D000", -"G. c #03CE00", -"H. c #02CA01", -"I. c #02C702", -"J. c #03C304", -"K. c #03BF06", -"L. c #04BA07", -"M. c #04AF06", -"N. c #CFC700", -"O. c #D8CE00", -"P. c #E0CD00", -"Q. c #E9CA00", -"R. c #EDCE00", -"S. c #EED000", -"T. c #ECD200", -"U. c #EAD400", -"V. c #E9D700", -"W. c #E9DA00", -"X. c #EADD00", -"Y. c #E9DC00", -"Z. c #E4D400", -"`. c #DECA00", -" + c #DAC200", -".+ c #D6BB00", -"++ c #03AA02", -"@+ c #02B202", -"#+ c #02B902", -"$+ c #02C501", -"%+ c #02D100", -"&+ c #02D700", -"*+ c #01D700", -"=+ c #01D500", -"-+ c #02D200", -";+ c #03CB01", -">+ c #05C401", -",+ c #06BE02", -"'+ c #06B202", -")+ c #CFC500", -"!+ c #D8CC00", -"~+ c #DCCE00", -"{+ c #E0CB00", -"]+ c #E4C800", -"^+ c #E7C700", -"/+ c #EBCC00", -"(+ c #EBCE00", -"_+ c #EACF00", -":+ c #E9D000", -"<+ c #E8D100", -"[+ c #E8D600", -"}+ c #E9DB00", -"|+ c #E8DB00", -"1+ c #E3D300", -"2+ c #DBC600", -"3+ c #D6BD00", -"4+ c #D1B600", -"5+ c #04B201", -"6+ c #03B702", -"7+ c #03BE01", -"8+ c #03C701", -"9+ c #03D100", -"0+ c #02D600", -"a+ c #01D600", -"b+ c #01D300", -"c+ c #03CD01", -"d+ c #05C601", -"e+ c #06C001", -"f+ c #06B601", -"g+ c #D1C200", -"h+ c #D9CA00", -"i+ c #DDCB00", -"j+ c #E0C800", -"k+ c #E4C400", -"l+ c #E6C300", -"m+ c #E8CC00", -"n+ c #E9CC00", -"o+ c #EACA00", -"p+ c #EBCF00", -"q+ c #EDD500", -"r+ c #ECD600", -"s+ c #E6CF00", -"t+ c #DDC400", -"u+ c #000000", -"v+ c #0AB100", -"w+ c #08B700", -"x+ c #06BD01", -"y+ c #05C101", -"z+ c #04C601", -"A+ c #05CB00", -"B+ c #04D200", -"C+ c #02D300", -"D+ c #01D101", -"E+ c #00CF01", -"F+ c #02CA02", -"G+ c #03C503", -"H+ c #04C003", -"I+ c #05BA03", -"J+ c #D2C000", -"K+ c #D9C600", -"L+ c #DCC700", -"M+ c #E3C100", -"N+ c #E5C100", -"O+ c #E6C500", -"P+ c #EAC500", -"Q+ c #ECC400", -"R+ c #EEC900", -"S+ c #EFD200", -"T+ c #12AA00", -"U+ c #0CB100", -"V+ c #09B700", -"W+ c #08BD00", -"X+ c #07C400", -"Y+ c #06CB00", -"Z+ c #06CE00", -"`+ c #06CF00", -" @ c #05CE00", -".@ c #01CB01", -"+@ c #00C902", -"@@ c #01C603", -"#@ c #02C204", -"$@ c #02C005", -"%@ c #03BD05", -"&@ c #CFBC00", -"*@ c #D3C100", -"=@ c #D7C200", -"-@ c #DBC100", -";@ c #E0C100", -">@ c #E3C200", -",@ c #E5C900", -"'@ c #E6CA00", -")@ c #E8C600", -"!@ c #EAC200", -"~@ c #ECC100", -"{@ c #EECD00", -"]@ c #0FB203", -"^@ c #0CB502", -"/@ c #09B901", -"(@ c #07BC00", -"_@ c #06C000", -":@ c #06C700", -"<@ c #01C901", -"[@ c #00C602", -"}@ c #01C203", -"|@ c #02BF04", -"1@ c #02BE05", -"2@ c #C6B700", -"3@ c #C9B900", -"4@ c #CCBB00", -"5@ c #DBC300", -"6@ c #E1C500", -"7@ c #E4C600", -"8@ c #E5C600", -"9@ c #E8C200", -"0@ c #E9BF00", -"a@ c #EAC000", -"b@ c #EAC600", -"c@ c #EACE00", -"d@ c #0CC203", -"e@ c #09C407", -"f@ c #06C508", -"g@ c #05C406", -"h@ c #05C303", -"i@ c #04C300", -"j@ c #04C500", -"k@ c #04C801", -"l@ c #03CE01", -"m@ c #03CF01", -"n@ c #03D000", -"o@ c #02CB00", -"p@ c #01C801", -"q@ c #01C401", -"r@ c #02BF02", -"s@ c #02BB02", -"t@ c #02B903", -"u@ c #03B803", -"v@ c #BEB200", -"w@ c #C3B600", -"x@ c #CCBC00", -"y@ c #D7C400", -"z@ c #DEC800", -"A@ c #E2C700", -"B@ c #E4C300", -"C@ c #E6C100", -"D@ c #E8BF00", -"E@ c #E8BE00", -"F@ c #E8C000", -"G@ c #E7C600", -"H@ c #0BC903", -"I@ c #06CD08", -"J@ c #01D00B", -"K@ c #00CF09", -"L@ c #01CD05", -"M@ c #02C900", -"N@ c #01C903", -"O@ c #01CA02", -"P@ c #00CD01", -"Q@ c #01D000", -"R@ c #01CF00", -"S@ c #01CB00", -"T@ c #02C600", -"U@ c #02C200", -"V@ c #02BC00", -"W@ c #03B700", -"X@ c #03B501", -"Y@ c #03B301", -"Z@ c #BAAF00", -"`@ c #BFB300", -" # c #C8BB00", -".# c #D3C400", -"+# c #DBC700", -"@# c #DFC500", -"## c #E1C000", -"$# c #E4BE00", -"%# c #E6BD00", -"&# c #E7BD00", -"*# c #E7C000", -"=# c #0ACB02", -"-# c #05CF07", -";# c #01D20A", -"># c #00D209", -",# c #00CF05", -"'# c #00CB00", -")# c #00C901", -"!# c #00C703", -"~# c #00C803", -"{# c #00CC02", -"]# c #00CF00", -"^# c #00CE00", -"/# c #01CA00", -"(# c #02C500", -"_# c #02C000", -":# c #03BA00", -"<# c #03B500", -"[# c #03B200", -"}# c #03B100", -"|# c #BBAF00", -"1# c #BFB200", -"2# c #C7B900", -"3# c #DAC100", -"4# c #DCBD00", -"5# c #DEBB00", -"6# c #E2BB00", -"7# c #E5BD00", -"8# c #E7C100", -"9# c #E9C700", -"0# c #08CD01", -"a# c #05CF04", -"b# c #02D007", -"c# c #01CF06", -"d# c #01CE03", -"e# c #01CC00", -"f# c #00C802", -"g# c #00CA02", -"h# c #00CD00", -"i# c #00CC00", -"j# c #01C700", -"k# c #02BE00", -"l# c #02B800", -"m# c #02B400", -"n# c #02B100", -"o# c #BEB100", -"p# c #C1B300", -"q# c #C7B800", -"r# c #CEBD00", -"s# c #D2BF00", -"t# c #D4BC00", -"u# c #D6B900", -"v# c #D9B900", -"w# c #DEBA00", -"x# c #E3BE00", -"y# c #EAC700", -"z# c #05CD01", -"A# c #05CC03", -"B# c #03CB03", -"C# c #02CB01", -"D# c #00CA00", -"E# c #00C603", -"F# c #00C800", -"G# c #01C400", -"H# c #02B700", -"I# c #02B300", -"J# c #02B000", -"K# c #BFB100", -"L# c #C6B500", -"M# c #CAB800", -"N# c #CEB900", -"O# c #D0B900", -"P# c #D3B800", -"Q# c #D7B900", -"R# c #DDBA00", -"S# c #E3BC00", -"T# c #E7BF00", -"U# c #EBC500", -"V# c #ECCB00", -"W# c #05CF00", -"X# c #05CC00", -"Y# c #05C901", -"Z# c #03C900", -"`# c #01C900", -" $ c #01C800", -".$ c #00C403", -"+$ c #00C700", -"@$ c #00C500", -"#$ c #01C100", -"$$ c #02BD00", -"%$ c #02B900", -"&$ c #02B500", -"*$ c #02B200", -"=$ c #02AF00", -"-$ c #C4B100", -";$ c #C7B100", -">$ c #C9B200", -",$ c #CDB500", -"'$ c #D2B900", -")$ c #D8BB00", -"!$ c #DFBA00", -"~$ c #E7B900", -"{$ c #EABA00", -"]$ c #ECC200", -"^$ c #DBCF00", -"/$ c #02D101", -"($ c #05C600", -"_$ c #03C700", -":$ c #02C700", -"<$ c #01C600", -"[$ c #00C402", -"}$ c #00C102", -"|$ c #00C201", -"1$ c #00C400", -"2$ c #00C200", -"3$ c #01BE00", -"4$ c #03AE00", -"5$ c #03AD00", -"6$ c #C2A900", -"7$ c #C3A900", -"8$ c #C6AC00", -"9$ c #CBB200", -"0$ c #D2BA00", -"a$ c #D9BE00", -"b$ c #EAB600", -"c$ c #ECB500", -"d$ c #EABE00", -"e$ c #E2C900", -"f$ c #CDCF00", -"g$ c #A3CA00", -"h$ c #00D205", -"i$ c #02D000", -"j$ c #04CB00", -"k$ c #05C500", -"l$ c #01C101", -"m$ c #00BE02", -"n$ c #00BF01", -"o$ c #00C000", -"p$ c #00BE00", -"q$ c #01BA00", -"r$ c #03AF00", -"s$ c #03AB00", -"t$ c #C0A500", -"u$ c #C4A900", -"v$ c #CAB100", -"w$ c #D3BB00", -"x$ c #DAC000", -"y$ c #E3BB00", -"z$ c #EAB400", -"A$ c #E9B300", -"B$ c #E1BB00", -"C$ c #D1C600", -"D$ c #B6CD00", -"E$ c #89CA00", -"F$ c #53C300", -"G$ c #00D408", -"H$ c #00D404", -"I$ c #01D200", -"J$ c #02CF00", -"K$ c #03CA00", -"L$ c #04C401", -"M$ c #03C400", -"N$ c #02C400", -"O$ c #01BF01", -"P$ c #00BC02", -"Q$ c #00BB02", -"R$ c #00BC01", -"S$ c #00BD00", -"T$ c #00BB00", -"U$ c #01B700", -"V$ c #02AE00", -"W$ c #03A900", -"X$ c #03A800", -"Y$ c #03A700", -"Z$ c #BFA300", -"`$ c #C3A800", -" % c #DDC100", -".% c #E5BC00", -"+% c #E4B100", -"@% c #D1B800", -"#% c #B6C100", -"$% c #95C800", -"%% c #69C901", -"&% c #39C802", -"*% c #01D307", -"=% c #00D305", -"-% c #01D102", -";% c #02CE00", -">% c #02CC01", -",% c #03C903", -"'% c #03C604", -")% c #03C504", -"!% c #03C402", -"~% c #03C300", -"{% c #01BD01", -"]% c #00BA02", -"^% c #00B902", -"/% c #00BA01", -"(% c #00BA00", -"_% c #00B800", -":% c #01B400", -"<% c #02AB00", -"[% c #02A700", -"}% c #03A500", -"|% c #03A300", -"1% c #BEA400", -"2% c #CDB300", -"3% c #D8BD00", -"4% c #DEC100", -"5% c #E4BC00", -"6% c #E6B400", -"7% c #DBB200", -"8% c #BDB600", -"9% c #96BD00", -"0% c #70C300", -"a% c #49C802", -"b% c #22CD05", -"c% c #01D104", -"d% c #02CF03", -"e% c #03CC01", -"f% c #03C802", -"g% c #02C805", -"h% c #01C708", -"i% c #01C607", -"j% c #02C404", -"k% c #03C200", -"l% c #03BF00", -"m% c #01BB01", -"n% c #00B802", -"o% c #00B702", -"p% c #00B701", -"q% c #00B500", -"r% c #01B100", -"s% c #02A300", -"t% c #02A000", -"u% c #029E00", -"v% c #029D00", -"w% c #003EC4", -"x% c #BFA500", -"y% c #C4AA00", -"z% c #CEB200", -"A% c #D7BA00", -"B% c #DABD00", -"C% c #DCBA00", -"D% c #D9B600", -"E% c #C9B500", -"F% c #A5B700", -"G% c #78BA00", -"H% c #50BF00", -"I% c #2FC703", -"J% c #11CF07", -"K% c #02CE03", -"L% c #04C800", -"M% c #03C502", -"N% c #01C606", -"O% c #00C709", -"P% c #00C608", -"Q% c #02C304", -"R% c #03C001", -"S% c #03BD00", -"T% c #02B901", -"U% c #00B602", -"V% c #00B502", -"W% c #00B501", -"X% c #00B200", -"Y% c #01AE00", -"Z% c #02A900", -"`% c #02A400", -" & c #029C00", -".& c #029A00", -"+& c #029900", -"@& c #004AC8", -"#& c #004DD8", -"$& c #004EDB", -"%& c #0052D3", -"&& c #CAAE00", -"*& c #CEB000", -"=& c #D0B100", -"-& c #C9B600", -";& c #C0B900", -">& c #ACBC00", -",& c #85BB00", -"'& c #58BB00", -")& c #33BD01", -"!& c #1AC404", -"~& c #08CD08", -"{& c #04CB01", -"]& c #05C800", -"^& c #06C500", -"/& c #05C200", -"(& c #04C202", -"_& c #01C405", -":& c #00C507", -"<& c #00C406", -"[& c #01C103", -"}& c #02BB00", -"|& c #01B401", -"1& c #00B201", -"2& c #00AF00", -"3& c #01AB00", -"4& c #01A600", -"5& c #02A200", -"6& c #029700", -"7& c #029600", -"8& c #0050C0", -"9& c #0055DA", -"0& c #0057E6", -"a& c #0058E5", -"b& c #0058D9", -"c& c #0058C5", -"d& c #CCAB00", -"e& c #C8A700", -"f& c #C0A900", -"g& c #B5B100", -"h& c #A4BC00", -"i& c #8AC300", -"j& c #64C100", -"k& c #3ABD00", -"l& c #1ABC02", -"m& c #0AC205", -"n& c #03CA0A", -"o& c #05C801", -"p& c #07C200", -"q& c #04C001", -"r& c #02C102", -"s& c #00C204", -"t& c #00C103", -"u& c #01BE02", -"v& c #01B000", -"w& c #00AC00", -"x& c #01A800", -"y& c #01A400", -"z& c #029F00", -"A& c #029400", -"B& c #029300", -"C& c #0057CF", -"D& c #005BE4", -"E& c #005DEE", -"F& c #005DEB", -"G& c #005BDE", -"H& c #0059CD", -"I& c #0058C2", -"J& c #C7A202", -"K& c #C4A000", -"L& c #B9A400", -"M& c #A6B000", -"N& c #8BBE00", -"O& c #6DC700", -"P& c #49C501", -"Q& c #24BF02", -"R& c #09BD04", -"S& c #00C107", -"T& c #00C80B", -"U& c #06C400", -"V& c #07BF00", -"W& c #06BE00", -"X& c #05BE00", -"Y& c #02BF01", -"Z& c #00BF02", -"`& c #00BE01", -" * c #00AB00", -".* c #00A800", -"+* c #039C00", -"@* c #029800", -"#* c #029500", -"$* c #005EDD", -"%* c #0060EB", -"&* c #0060F2", -"** c #0060F0", -"=* c #005FE7", -"-* c #005DDC", -";* c #005AD5", -">* c #0054D4", -",* c #D29900", -"'* c #C1A400", -")* c #9AB300", -"!* c #72C000", -"~* c #53C602", -"{* c #35C604", -"]* c #17C306", -"^* c #02C108", -"/* c #00C40A", -"(* c #00C70B", -"_* c #06C100", -":* c #06BF00", -"<* c #05BD00", -"[* c #05BB00", -"}* c #04BB00", -"|* c #03BC00", -"1* c #02BC01", -"2* c #02BA00", -"3* c #03B300", -"4* c #01B001", -"5* c #00AD01", -"6* c #00A901", -"7* c #00A500", -"8* c #01A200", -"9* c #039D00", -"0* c #059900", -"a* c #069700", -"b* c #059700", -"c* c #039700", -"d* c #0063E2", -"e* c #0063E8", -"f* c #0063F0", -"g* c #0063F5", -"h* c #0063F4", -"i* c #0062F0", -"j* c #0061EB", -"k* c #005DE6", -"l* c #0056E1", -"m* c #004DDB", -"n* c #0047D9", -"o* c #86B900", -"p* c #55C000", -"q* c #3BC304", -"r* c #26C507", -"s* c #10C60A", -"t* c #01C70B", -"u* c #00C70C", -"v* c #05BC00", -"w* c #03B900", -"x* c #03B800", -"y* c #04B600", -"z* c #04B400", -"A* c #05B100", -"B* c #04B000", -"C* c #02B001", -"D* c #01B003", -"E* c #00AC03", -"F* c #00A601", -"G* c #01A000", -"H* c #099200", -"I* c #0A9100", -"J* c #089500", -"K* c #049C00", -"L* c #0063E7", -"M* c #0064EB", -"N* c #0065F2", -"O* c #0065F7", -"P* c #0065F6", -"Q* c #0064F4", -"R* c #0060EF", -"S* c #0059E7", -"T* c #0051E0", -"U* c #004BDF", -"V* c #0042E9", -"W* c #5CB900", -"X* c #38BD04", -"Y* c #27C007", -"Z* c #1AC309", -"`* c #0BC70C", -" = c #00C90D", -".= c #00C90C", -"+= c #00C60A", -"@= c #04BC00", -"#= c #04BA00", -"$= c #03B901", -"%= c #02B801", -"&= c #02B701", -"*= c #03B701", -"== c #05AE00", -"-= c #04AD01", -";= c #02AC03", -">= c #00AC04", -",= c #00A804", -"'= c #00A302", -")= c #019D01", -"!= c #049800", -"~= c #089300", -"{= c #0C8E00", -"]= c #0E8D00", -"^= c #0C9300", -"/= c #005CE1", -"(= c #0064F0", -"_= c #0067F7", -":= c #0069F9", -"<= c #0069F8", -"[= c #0069F6", -"}= c #0066F3", -"|= c #0060EE", -"1= c #0059EA", -"2= c #0054E8", -"3= c #004DEC", -"4= c #0046F0", -"5= c #0046E9", -"6= c #2AAF26", -"7= c #1EB80E", -"8= c #17BD05", -"9= c #0FC205", -"0= c #06C609", -"a= c #00C90B", -"b= c #00C909", -"c= c #00C607", -"d= c #03BA03", -"e= c #02B705", -"f= c #01B606", -"g= c #01B604", -"h= c #02B702", -"i= c #03B400", -"j= c #03B002", -"k= c #02AC04", -"l= c #02A805", -"m= c #01A406", -"n= c #00A006", -"o= c #009E05", -"p= c #029D03", -"q= c #039B01", -"r= c #069800", -"s= c #099300", -"t= c #0D8C00", -"u= c #0053D7", -"v= c #0059E0", -"w= c #0061EC", -"x= c #0067F6", -"y= c #006BF9", -"z= c #006DF8", -"A= c #006EF5", -"B= c #006DF4", -"C= c #0069F5", -"D= c #0064F5", -"E= c #0060F3", -"F= c #0059EE", -"G= c #0054E6", -"H= c #0056DA", -"I= c #0363CC", -"J= c #097AB2", -"K= c #0D9286", -"L= c #0DA54A", -"M= c #0BB01D", -"N= c #09B806", -"O= c #06BF02", -"P= c #03C405", -"Q= c #00C707", -"R= c #00C805", -"S= c #02B906", -"T= c #02B607", -"U= c #01B50A", -"V= c #00B40A", -"W= c #00B508", -"X= c #00B803", -"Y= c #01B800", -"Z= c #00B004", -"`= c #00A908", -" - c #00A209", -".- c #009A09", -"+- c #009408", -"@- c #019207", -"#- c #039505", -"$- c #079A01", -"%- c #0A9B00", -"&- c #004ED3", -"*- c #0054DC", -"=- c #005DEA", -"-- c #006AFA", -";- c #0070F6", -">- c #006EFA", -",- c #006BFD", -"'- c #0067FA", -")- c #0060F1", -"!- c #005AE5", -"~- c #005CD9", -"{- c #0067CE", -"]- c #0079BD", -"^- c #008B9D", -"/- c #009A69", -"(- c #00A536", -"_- c #00AF16", -":- c #00B80A", -"<- c #01BE07", -"[- c #01C306", -"}- c #01C502", -"|- c #01B708", -"1- c #00B30C", -"2- c #00B409", -"3- c #00B604", -"4- c #00B700", -"5- c #00B401", -"6- c #00AD05", -"7- c #00A609", -"8- c #009E0B", -"9- c #00940A", -"0- c #008C09", -"a- c #018A08", -"b- c #058E06", -"c- c #004FD6", -"d- c #0052DE", -"e- c #0058EC", -"f- c #005EF8", -"g- c #0062FC", -"h- c #0067FB", -"i- c #006BF8", -"j- c #006DF9", -"k- c #006DFC", -"l- c #006BFF", -"m- c #0068FD", -"n- c #0062F6", -"o- c #005DED", -"p- c #005EE4", -"q- c #0065DD", -"r- c #0071D1", -"s- c #007EB8", -"t- c #008A8E", -"u- c #00965F", -"v- c #00A13B", -"w- c #00AC21", -"x- c #00B60F", -"y- c #01BD03", -"z- c #02C100", -"A- c #01C200", -"B- c #00B50A", -"C- c #00B40B", -"D- c #00B30B", -"E- c #00B309", -"F- c #00B307", -"G- c #00B203", -"H- c #00B101", -"I- c #00AD02", -"J- c #01A705", -"K- c #02A108", -"L- c #029B09", -"M- c #01920A", -"N- c #00890A", -"O- c #01860A", -"P- c #0051D8", -"Q- c #0052DA", -"R- c #0052E1", -"S- c #0053EE", -"T- c #0054FA", -"U- c #0058FF", -"V- c #005EFE", -"W- c #0065FB", -"X- c #0068FB", -"Y- c #0068FC", -"Z- c #0067FD", -"`- c #0066FD", -" ; c #0062FB", -".; c #005EF3", -"+; c #0062EF", -"@; c #0068E6", -"#; c #0070D3", -"$; c #007AB3", -"%; c #00858D", -"&; c #009168", -"*; c #009E41", -"=; c #01AB1B", -"-; c #02B502", -";; c #00B40C", -">; c #00B308", -",; c #00B305", -"'; c #00B103", -"); c #00A902", -"!; c #01A502", -"~; c #05A003", -"{; c #089C04", -"]; c #099905", -"^; c #079407", -"/; c #0055DB", -"(; c #0054DD", -"_; c #0053E4", -":; c #0051F0", -"<; c #004FFC", -"[; c #0052FF", -"}; c #0059FF", -"|; c #0060FD", -"1; c #0064FC", -"2; c #0066FC", -"3; c #0065FC", -"4; c #0062FF", -"5; c #005FFF", -"6; c #005EFB", -"7; c #0060F4", -"8; c #0065E5", -"9; c #006DCE", -"0; c #0077B1", -"a; c #00828F", -"b; c #019062", -"c; c #019E31", -"d; c #02A90F", -"e; c #02B101", -"f; c #00B20C", -"g; c #00B10A", -"h; c #00B105", -"i; c #02AC00", -"j; c #05A701", -"k; c #08A202", -"l; c #0D9E02", -"m; c #149B02", -"n; c #199802", -"o; c #1B9702", -"p; c #0058DC", -"q; c #0058E0", -"r; c #0054F2", -"s; c #0052FE", -"t; c #0054FF", -"u; c #005AFF", -"v; c #0061FD", -"w; c #0066FB", -"x; c #0062FE", -"y; c #005DFF", -"z; c #005CFD", -"A; c #005CF8", -"B; c #005FEE", -"C; c #0065E0", -"D; c #006CCE", -"E; c #0075B2", -"F; c #008088", -"G; c #018D58", -"H; c #019832", -"I; c #02A31A", -"J; c #02AC0A", -"K; c #01B309", -"L; c #00B007", -"M; c #01AC03", -"N; c #09A400", -"O; c #129F01", -"P; c #1C9B01", -"Q; c #279801", -"R; c #349701", -"S; c #3E9601", -"T; c #449601", -"U; c #005BDB", -"V; c #005DE1", -"W; c #005CE8", -"X; c #005AF4", -"Y; c #005EFF", -"Z; c #0068F9", -"`; c #0063FA", -" > c #005BFB", -".> c #005AFA", -"+> c #005AF7", -"@> c #005BF2", -"#> c #005EED", -"$> c #0062E5", -"%> c #0068D3", -"&> c #0070B2", -"*> c #007A89", -"=> c #018462", -"-> c #01913E", -";> c #019E1B", -">> c #005DD8", -",> c #0060E0", -"'> c #0060E7", -")> c #005EF1", -"!> c #005DFB", -"~> c #0066F9", -"{> c #006BF7", -"]> c #0069F7", -"^> c #0064F7", -"/> c #005EF7", -"(> c #0059F6", -"_> c #0059F4", -":> c #0059F3", -"<> c #005CF4", -"[> c #005FE9", -"}> c #0063D1", -"|> c #006AB1", -"1> c #00728E", -"2> c #008061", -"3> c #019031", -"4> c #005DD1", -"5> c #0060D8", -"6> c #0060DD", -"7> c #005FE3", -"8> c #005EEA", -"9> c #0069F2", -"0> c #006BF3", -"a> c #006CF5", -"b> c #006CF6", -"c> c #006AF6", -"d> c #0061F5", -"e> c #005DF4", -"f> c #005CF2", -"g> c #005CF0", -"h> c #005BEF", -"i> c #005BF3", -"j> c #005CED", -"k> c #005FCD", -"l> c #0065B1", -"m> c #007183", -"n> c #00814E", -"o> c #005CC6", -"p> c #005FCB", -"q> c #005FCE", -"r> c #005FD0", -"s> c #005FD4", -"t> c #0061D9", -"u> c #0066E0", -"v> c #006BE8", -"w> c #006DED", -"x> c #006DF2", -"y> c #0067F5", -"z> c #0064F3", -"A> c #0062F1", -"B> c #0061ED", -"C> c #0061EA", -"D> c #0060E8", -"E> c #005FE8", -"F> c #005EE8", -"G> c #005AE6", -"H> c #0057E1", -"I> c #005ACF", -"J> c #0063A6", -"K> c #007172", -"L> c #079900", -"M> c #129600", -"N> c #218C00", -"O> c #358600", -"P> c #528A00", -"Q> c #779400", -"R> c #989D00", -"S> c #ADA100", -"T> c #BAA100", -"U> c #C2A000", -"V> c #C69F00", -"W> c #C49D00", -"X> c #005BBD", -"Y> c #005EC0", -"Z> c #005EC1", -"`> c #005EC2", -" , c #005FC4", -"., c #0062CA", -"+, c #0067D5", -"@, c #006BE0", -"#, c #006DE8", -"$, c #006BEE", -"%, c #0069F3", -"&, c #0064F1", -"*, c #0063ED", -"=, c #0063E9", -"-, c #0063E4", -";, c #0063E1", -">, c #0062DF", -",, c #0060DE", -"', c #005EDF", -"), c #0053EB", -"!, c #0058C0", -"~, c #006392", -"{, c #098C04", -"], c #198C00", -"^, c #308600", -"/, c #4B8400", -"(, c #6C8C00", -"_, c #949800", -":, c #B5A300", -"<, c #C6A700", -"[, c #CBA700", -"}, c #CCA500", -"|, c #CCA400", -"1, c #C7A000", -"2, c #005DBB", -"3, c #005EBD", -"4, c #005FBD", -"5, c #0060C0", -"6, c #0061C6", -"7, c #0064D1", -"8, c #0065DB", -"9, c #0065E3", -"0, c #0062E9", -"a, c #005DF0", -"b, c #005BED", -"c, c #005BEB", -"d, c #005FE2", -"e, c #0060DF", -"f, c #005FDB", -"g, c #005DDB", -"h, c #0057E0", -"i, c #0051E5", -"j, c #004FDE", -"k, c #0052C9", -"l, c #0058AA", -"m, c #0B8115", -"n, c #218606", -"o, c #408802", -"p, c #628B00", -"q, c #849200", -"r, c #A99B00", -"s, c #C6A300", -"t, c #D2A700", -"u, c #D1A800", -"v, c #CDA900", -"w, c #CAA800", -"x, c #C4A400", -"y, c #005BD2", -"z, c #0057D7", -"A, c #0051E2", -"B, c #004FE6", -"C, c #004DE9", -"D, c #004CEA", -"E, c #004CE9", -"F, c #004DE8", -"G, c #0052E5", -"H, c #005DDA", -"I, c #005CD8", -"J, c #0058D5", -"K, c #0053D1", -"L, c #0050CB", -"M, c #004EC6", -"N, c #004EBE", -"O, c #0C762E", -"P, c #298118", -"Q, c #528D0A", -"R, c #7A9701", -"S, c #9A9C00", -"T, c #B99E00", -"U, c #CF9F00", -"V, c #D5A300", -"W, c #CEA800", -"X, c #C5AC00", -"Y, c #C1AC00", -"Z, c #BCA800", -"`, c #0041D5", -" ' c #0041DB", -".' c #0041E0", -"+' c #0041E3", -"@' c #0040E4", -"#' c #0040E5", -"$' c #0042E5", -"%' c #0049E2", -"&' c #0050DE", -"*' c #0056DC", -"=' c #0059DB", -"-' c #005BD9", -";' c #005BD5", -">' c #0059CC", -",' c #0055C1", -"'' c #0051BB", -")' c #004CC2", -"!' c #0048CC", -"~' c #0C6B47", -"{' c #2E7C2A", -"]' c #5E9012", -"^' c #8B9E02", -"/' c #AAA200", -"(' c #C49F00", -"_' c #D49C00", -":' c #D5A000", -"<' c #BEAE00", -"[' c #B9AF00", -"}' c #B5AC00", -"|' c #0038D7", -"1' c #003ADD", -"2' c #003BDF", -"3' c #003CDE", -"4' c #003FDC", -"5' c #0046D9", -"6' c #004ED6", -"7' c #0053D4", -"8' c #0057D6", -"9' c #0059D8", -"0' c #0059D6", -"a' c #0057CC", -"b' c #0053C0", -"c' c #004FBA", -"d' c #004AC4", -"e' c #0045D2", -"f' c #0A5F5D", -"g' c #2E7239", -"h' c #61891A", -"i' c #8E9A03", -"j' c #AE9F00", -"k' c #C89D00", -"l' c #D89B00", -"m' c #D8A000", -"n' c #CBA800", -"o' c #BEAF00", -"p' c #B8B000", -"q' c #B4AD00", -"r' c #003BD4", -"s' c #003DD0", -"t' c #0042CE", -"u' c #0048CB", -"v' c #004EC9", -"w' c #0052CA", -"x' c #0054D1", -"y' c #0056D8", -"z' c #0057D9", -"A' c #0054D2", -"B' c #0050C7", -"C' c #004DC1", -"D' c #0049C8", -"E' c #045274", -"F' c #2A644A", -"G' c #5D7B21", -"H' c #898D04", -"I' c #A99600", -"J' c #C59900", -"K' c #D89C00", -"L' c #DAA200", -"M' c #CEA900", -"N' c #C1AE00", -"O' c #B4AC00", -"P' c #ADA700", -"Q' c #0046BB", -"R' c #004BBA", -"S' c #004FBB", -"T' c #0051BE", -"U' c #0053C7", -"V' c #0055D2", -"W' c #0055D7", -"X' c #004ECC", -"Y' c #004BC7", -"Z' c #0048CA", -"`' c #0046CE", -" ) c #00458B", -".) c #23565D", -"+) c #556D2B", -"@) c #808007", -"#) c #A28D00", -"$) c #C19600", -"%) c #D69D00", -"&) c #DAA400", -"*) c #CFAA00", -"=) c #C3AE00", -"-) c #BCAE00", -";) c #ACA700", -">) c #004DAA", -",) c #0050A8", -"') c #0052AB", -")) c #0054B5", -"!) c #0055C8", -"~) c #0052C8", -"{) c #004BC5", -"]) c #0049C7", -"^) c #0047CA", -"/) c #003DA1", -"() c #1C4B75", -"_) c #4A603B", -":) c #78760C", -"<) c #9E8700", -"[) c #BE9400", -"}) c #D29E00", -"|) c #D6A500", -"1) c #CDAA00", -"2) c #C3AD00", -"3) c #B5AB00", -"4) c #ACA800", -"5) c #005386", -"6) c #00548A", -"7) c #005698", -"8) c #0056A7", -"9) c #0056B1", -"0) c #0053B6", -"a) c #004CBE", -"b) c #004AC2", -"c) c #0049C5", -"d) c #003BB3", -"e) c #124291", -"f) c #385156", -"g) c #6B6817", -"h) c #9A8100", -"i) c #BB9300", -"j) c #CC9F00", -"k) c #CFA600", -"l) c #C8A900", -"m) c #C0AC00", -"n) c #BCAD00", -"o) c #B6AB00", -"p) c #00595E", -"q) c #005A73", -"r) c #005A87", -"s) c #005994", -"t) c #00559F", -"u) c #0051AB", -"v) c #004DB4", -"w) c #004ABE", -"x) c #003ABD", -"y) c #0A3BA7", -"z) c #224174", -"A) c #565627", -"B) c #937900", -"C) c #B79300", -"D) c #C8A600", -"E) c #BDAA00", -"F) c #BAAB00", -"G) c #026439", -"H) c #026451", -"I) c #016365", -"J) c #016074", -"K) c #015B84", -"L) c #005496", -"M) c #004FA6", -"N) c #004CB0", -"O) c #004BB8", -"P) c #003ABE", -"Q) c #0638AC", -"R) c #153780", -"S) c #8A7301", -"T) c #B09100", -"U) c #BE9E00", -"V) c #C2A500", -"W) c #BFA700", -"X) c #BAA800", -"Y) c #B8A900", -"Z) c #B5A900", -"`) c #077430", -" ! c #05703E", -".! c #036C4C", -"+! c #026361", -"@! c #01597C", -"#! c #005093", -"$! c #004CA4", -"%! c #004BB2", -"&! c #003AB6", -"*! c #0538A3", -"=! c #143878", -"-! c #827102", -";! c #A98F00", -">! c #B99D00", -",! c #BDA300", -"'! c #BAA500", -")! c #B6A500", -"!! c #B4A600", -"~! c #B2A600", -"{! c #0B8413", -"]! c #077D1C", -"^! c #057729", -"/! c #036C41", -"(! c #015E60", -"_! c #00537D", -":! c #004E95", -"~ c #062BAA", -",~ c #142F78", -"'~ c #3C4327", -")~ c #766600", -"!~ c #997F00", -"~~ c #A98B00", -"{~ c #B19200", -"]~ c #B69700", -"^~ c #017811", -"/~ c #027A0F", -"(~ c #038007", -"_~ c #048600", -":~ c #058503", -"<~ c #047A1B", -"[~ c #026A3F", -"}~ c #082CA7", -"|~ c #1C3171", -"1~ c #454323", -"2~ c #776400", -"3~ c #987E00", -"4~ c #A88B00", -"5~ c #B19300", -"6~ c #B79900", -"7~ c #BEA000", -"8~ c #067F0B", -"9~ c #05800B", -"0~ c #048207", -"a~ c #048405", -"b~ c #03810A", -"c~ c #027418", -"d~ c #026131", -"e~ c #0034AE", -"f~ c #0E3497", -"g~ c #2B3A5E", -"h~ c #54481A", -"i~ c #7A6200", -"j~ c #967C00", -"k~ c #A58C00", -"l~ c #AE9400", -"m~ c #B59900", -"n~ c #0B8902", -"o~ c #098704", -"p~ c #068408", -"q~ c #03800D", -"r~ c #017A13", -"s~ c #006C18", -"t~ c #005422", -"u~ c #003AA0", -"v~ c #143D84", -"w~ c #38424B", -"x~ c #5F4C13", -"y~ c #7D6000", -"z~ c #937A00", -"A~ c #A18D00", -"B~ c #AA9600", -"C~ c #0C8B00", -"D~ c #078308", -"E~ c #027B10", -"F~ c #007315", -"G~ c #006717", -"H~ c #00521B", -"I~ c #003B92", -"J~ c #183F75", -"K~ c #3F453F", -"L~ c #644F0F", -"M~ c #7E6100", -"N~ c #927900", -"O~ c #A18E00", -"P~ c #A99800", -"Q~ c #057C06", -"R~ c #01720B", -"S~ c #006C10", -"T~ c #006716", -"U~ c #005F22", -"V~ c #003886", -"W~ c #193C69", -"X~ c #414537", -"Y~ c #65500C", -"Z~ c #A38F00", -"`~ c #AE9900", -" { c #037102", -".{ c #006805", -"+{ c #00660A", -"@{ c #006816", -"#{ c #006D2C", -"${ c #003278", -"%{ c #1A365D", -"&{ c #42442F", -"*{ c #65530A", -"={ c #7E6300", -"-{ c #957A00", -";{ c #A88F00", -">{ c #B49B00", -",{ c #006200", -"'{ c #006306", -"){ c #006B17", -"!{ c #007632", -"~{ c #002C6A", -"{{ c #1E3350", -"]{ c #474627", -"^{ c #6A5908", -"/{ c #816800", -"({ c #967B00", -"_{ c #AA8F00", -":{ c #016200", -"<{ c #016504", -"[{ c #016F19", -"}{ c #007C35", -"|{ c #032658", -"1{ c #253241", -"2{ c #524F1F", -"3{ c #766705", -"4{ c #887300", -"5{ c #977F00", -"6{ c #036903", -"7{ c #02751C", -"8{ c #007F38", -"9{ c #072048", -"0{ c #2C3233", -"a{ c #5D5818", -"b{ c #827503", -"c{ c #928100", -"d{ c #056E02", -"e{ c #02781F", -"f{ c #0A1B3A", -"g{ c #2E2F2A", -"h{ c #615A13", -"i{ c #897E02", -"j{ c #9A8C00", -"k{ c #027423", -"l{ c #007636", -"m{ c #0D172E", -"n{ c #292523", -"o{ c #595012", -"p{ c #8A7F01", -"q{ c #006831", -"r{ c #101422", -"s{ c #1F161D", -"t{ c #005C2D", -"u{ c #00552B", -"v{ c #0D281F", -"w{ c #13111A", -"x{ c #140717", -"y{ c #005027", -"z{ c #063F22", -"A{ c #0E271B", -"B{ c #140F15", -" . + @ ", -" # $ % & * = - ; > , ' ) ! ~ { ] ^ / ( _ : < [ ", -" } | 1 2 3 4 5 6 7 8 9 0 a b c d e f g h i j k ", -" l m n o p q r s t u v 0 w x y z A d B C D E F ", -" G H I J K L u M N O P Q R S S T U V W X Y Z ` ...+.@. ", -" #.$.%.&.*.M =.-.;.>.,.'.).S !.~.{.].=.> ^./. (._.:.<.[.}.|. ", -" H 1.2.3.*.M 4.O 5.6.7.7.6.P 8.! 9.4.0.a.b. c.d.e.f.g.h.i.j. ", -" k.l.m.n.o.p.q.r.4.s.t.u.v.u.w.x.o.y.z.A. B.C.D.E.F.G.H.I.J.K.L.M. ", -" N.O.1.P.5 Q.7 R.S.T.U.V.W.X.Y.Z.`. +.+ ++@+#+$+%+&+*+=+-+;+>+,+'+ ", -" )+!+~+{+]+^+> /+(+_+:+<+[+}+|+1+2+3+4+ 5+6+7+8+9+0+*+a+b+c+d+e+f+ ", -" g+h+i+j+k+l+^+m+s n+Q.o+p+q+r+s+t+u+u+u+ v+w+x+y+z+A+F.B+C+D+E+F+G+H+I+ ", -" J+K+L+= M+N+O+K 6 > P+Q+R+q.S+u+u+u+u+u+u+u+ T+U+V+W+X+Y+Z+`+`+ @c+.@+@@@#@$@%@ ", -" &@*@=@-@;@>@q ,@'@)@!@~@, {@u+u+u+u+u+u+u+u+u+ ]@^@/@(@_@:@E.F.F.`+E.;+<@[@}@|@1@%@ ", -" 2@3@4@% 5@6@7@8@O+9@0@a@b@c@u+u+u+u+u+u+u+u+u+u+d@e@f@g@h@i@j@k@;+l@m@n@G.o@p@q@r@s@t@u@ ", -" v@w@x@y@z@A@B@C@D@E@F@G@u+u+u+u+u+u+u+u+u+u+u+H@I@J@K@L@H.M@p@N@O@P@Q@R@S@T@U@V@W@X@Y@ ", -" Z@`@ #.#+#@###$#%#&#*#G@u+u+u+u+u+u+u+u+u+u+u+=#-#;#>#,#P@'#)#!#~#{#]#^#/#(#_#:#<#[#}# ", -" |#1#2#| y@3#4#5#6#7#8#9#u+u+u+u+u+u+u+u+u+u+u+0#a#b#c#d#e#'#f#!#!#g#h#i#j#U@k#l#m#n#n# ", -" o#p#q#r#s#t#u#v#w#x#C@y#u+u+u+u+u+u+u+u+u+u+u+Z+z#A#B#C#S@D#f#E#E#f#D#F#G#_#V@H#I#n#J# ", -" K#p#L#M#N#O#P#Q#R#S#T#U#V#u+u+u+u+u+u+u+u+u+u+W#X#Y#Y#Z#`# $[@.$.$[@+$@$#$$$%$&$*$J#=$ ", -" -$;$>$,$'$)$!$~${$]$Q.^$u+u+u+u+u+u+u+u+/$n@X#:@($_$:$<$[$}$}$|$1$2$3$%$&$*$=$4$5$ ", -" 6$7$8$9$0$a$6#b$c$d$e$f$g$u+u+u+u+u+u+h$b+i$j$($k$j@(#G#l$m$m$n$o$p$q$&$*$r$5$s$s$ ", -" t$u$v$w$x$y$z$A$B$C$D$E$F$u+u+u+;#G$H$I$J$K$z+L$M$N$U@O$P$Q$R$S$T$U$*$V$s$W$X$Y$ ", -" Z$`$9$3+ %.%z$+%@%#%$%%%&%u+u+u+*%=%-%;%>%,%'%)%!%~%_#{%]%^%/%(%_%:%=$<%[%}%|%|% ", -" 1%7$2%3%4%5%6%7%8%9%0%a%b%u+u+u+c%d%e%Z#f%g%h%i%j%k%l%m%n%o%p%_%q%r%<%[%s%t%u%v% ", -" w% x%y%z%A%B%C%D%E%F%G%H%I%J%u+u+u+K%;+L%j@M%N%O%P%Q%R%S%T%U%V%W%q%X%Y%Z%`%t% &.&+& ", -" @&#&$&%& &&*&=&2%-&;&>&,&'&)&!&~&u+u+u+{&]&^&/&(&_&:&<&[&k#}&H#|&1&1&X%2&3&4&5&v%+&6&7& ", -" 8&9&0&a&b&c& d&e&f&g&h&i&j&k&l&m&n&u+u+u+o&^&p&_@q&r&s&t&u&}&l#m#r%v&2&2&w&x&y&z&.&7&A&B& ", -" C&D&E&F&G&H&I& J&K&L&M&N&O&P&Q&R&S&T&u+u+u+U&p&V&W&X&Y&Z&`&m%l#&$*$v&Y%w& *.*y&t%+*@*#*A&B& ", -" $*%*&***=*-*;*>* ,*'*)*!*~*{*]*^*/*(*u+u+u+_*:*<*[*}*|*1*2*W@<#3**$4*5*6*7*8*9*0*a*b*c*6& ", -" d*e*f*g*h*i*j*k*l*m*n* o*p*q*r*s*t*u*(*u+u+u+X&v*}*:#w*w*x*y*z*A*B*C*D*E*F*G* &a*H*I*J*K* ", -" L*M*N*O*O*P*Q*R*S*T*U*V* W*X*Y*Z*`* =.=+=u+u+u+@=#=$=%=&=*=y*z*A*==-=;=>=,='=)=!=~={=]=^= ", -" /==*(=_=:=<=[=}=|=1=2=3=4=5= 6=7=8=9=0=a=b=c=u+u+u+d=u@e=f=g=h=H#i=j=k=l=m=n=o=p=q=r=s=t= ", -" u=v=w=x=y=z=A=B=C=D=E=F=G=H=I=J=K=L=M=N=O=P=Q=R=!#u+u+u+S=T=U=V=W=X=Y=W%Z=`= -.-+-@-#-$-%- ", -" &-*-=-P*--z=;-;->-,-'-)-!-~-{-]-^-/-(-_-:-<-[-}-@$u+u+u+|-U=1-1-2-3-4-5-6-7-8-9-0-a-b- ", -" c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-A-u+u+u+B-C-D-E-F-G-H-I-J-K-L-M-N-O- u+u+u+u+u+u+ ", -" P-Q-R-S-T-U-V-W-X-Y-Z-`- ;f-.;+;@;#;$;%;&;*;=;-;}&k#u+u+u+;;D->;,;';I-);!;~;{;];^; u+u+u+u+u+u+u+u+ ", -" /;(;_;:;<;[;};|;1;2;3;1;4;5;V-6;7;8;9;0;a;b;c;d;e;H#u+u+u+f;g;h;4*i;j;k;l;m;n;o; u+u+u+u+u+u+u+u+u+u+ ", -" p;q;0&r;s;t;u;v;3;w;'-w;x;5;y;z;A;B;C;D;E;F;G;H;I;J;u+u+u+K;L;M;X$N;O;P;Q;R;S;T; u+u+u+u+u+u+u+u+u+u+ ", -" U;V;W;X;U-};Y;1;'-:=:=Z;`;6; >.>+>@>#>$>%>&>*>=>->;>u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+ ", -" >>,>'>)>!>V-g-~><={>{>]>^>/>+>(>_>:>X;<>[>}>|>1>2>3>u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+ ", -" 4>5>6>7>8>|=(=9>0>a>b>c>P*d>e>f>g>h>@>i>j>V;k>l>m>n>u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+ ", -" o>p>q>r>s>t>u>v>w>x>a>c>y>z>A>B>C>D>E>F>k*G>H>I>J>K>u+u+u+L>M>N>O>P>Q>R>S>T>U>V>W> u+u+u+u+u+u+u+u+u+u+ ", -" X>Y>Z>`> ,.,+,@,#,$,%,y>}=&,*,=,-,;,>,,,',a&),R-!,~,u+u+u+{,],^,/,(,_,:,<,[,},|,1, u+u+u+u+u+u+u+u+u+ ", -" 2,3,4,5,6,7,8,9,0,B;a,h>b,c,k*d,e,6>f,g,h,i,j,k,l,u+u+u+m,n,o,p,q,r,s,t,u,v,w,x, u+u+u+u+u+u+u+ ", -" y,z,*-A,B,C,D,E,F,G,H>G&-*H,I,J,K,L,M,N,u+u+u+O,P,Q,R,S,T,U,V,W,X,Y,Z, u+u+u+u+ ", -" `, '.'+'@'#'$'%'&'*'='-';'>',''')'!'u+u+u+~'{']'^'/'('_':'w,<'['}' ", -" |'1'2'2'3'4'5'6'7'8'9'0'a'b'c'd'e'u+u+u+f'g'h'i'j'k'l'm'n'o'p'q' ", -" r's't'u'v'w'x'y'z'A'B'C'D'e'u+u+u+E'F'G'H'I'J'K'L'M'N'Z@O'P' ", -" Q'R'S'T'U'V'W'%&X'Y'Z'`'u+u+u+ ).)+)@)#)$)%)&)*)=)-)}';) ", -" >),)'))),'!)~)M,{)])^)u+u+u+/)()_):)<)[)})|)1)2)-)3)4) ", -" 5)6)7)8)9)0)S'a)b)c)u+u+u+d)e)f)g)h)i)j)k)l)m)n)o) ", -" p)q)r)s)t)u)v)R'w)u+u+u+x)y)z)A)B)C)('D)7$E)F)o) ", -" G)H)I)J)K)L)M)N)O)u+u+u+P)Q)R) S)T)U)V)W)X)Y)Z) ", -" `) !.!+!@!#!$!%!u+u+u+&!*!=! -!;!>!,!'!)!!!~! u+u+u+u+ ", -" {!]!^!/!(!_!:!~,~'~)~!~~~{~]~>! u+u+u+u+u+u+u+u+u+u+ ", -" ^~/~(~_~:~<~[~u+u+u+;~}~|~1~2~3~4~5~6~7~ u+u+u+u+u+u+u+u+u+u+ ", -" 8~9~0~a~b~c~d~u+u+u+e~f~g~h~i~j~k~l~m~ u+u+u+u+u+u+u+u+ ", -" n~o~p~q~r~s~t~u+u+u+u~v~w~x~y~z~A~B~ u+u+u+u+u+u+ ", -" C~D~E~F~G~H~u+u+u+I~J~K~L~M~N~O~P~ ", -" Q~R~S~T~U~u+u+u+V~W~X~Y~M~B)Z~`~ ", -" {.{+{@{#{u+u+u+${%{&{*{={-{;{>{ ", -" ,{'{){!{u+u+u+~{{{]{^{/{({_{ ", -" :{<{[{}{u+u+u+|{1{2{3{4{5{ ", -" 6{7{8{u+u+u+9{0{a{b{c{ ", -" d{e{8{u+u+u+f{g{h{i{j{ ", -" k{l{u+u+u+m{n{o{p{ ", -" q{u+u+u+r{s{ ", -" t{u{u+v{w{x{ ", -" y{z{A{B{ ", -" ", -" "}; +/* XPM */ +static char * C:\Users\knorr\Desktop\Activitydiagram1_xpm[] = { +"80 77 1723 2", +" c None", +". c #DCD800", +"+ c #D3D000", +"@ c #CDC900", +"# c #CDC200", +"$ c #CFC100", +"% c #D3BF00", +"& c #D8BC00", +"* c #DDBE00", +"= c #E0C400", +"- c #E3CA00", +"; c #E6CC00", +"> c #E9C900", +", c #EDC600", +"' c #F0C600", +") c #F2CB00", +"! c #F5D300", +"~ c #F6DA00", +"{ c #F6DD00", +"] c #F5DE00", +"^ c #F3DF00", +"/ c #EFE000", +"( c #EBE100", +"_ c #E6E000", +": c #DFDB00", +"< c #D5D200", +"[ c #CBC800", +"} c #CEC200", +"| c #D0C100", +"1 c #D4C000", +"2 c #DABE00", +"3 c #DFC000", +"4 c #E2C500", +"5 c #E5CB00", +"6 c #E7CD00", +"7 c #EBCB00", +"8 c #EEC800", +"9 c #F1C800", +"0 c #F4CD00", +"a c #F6D300", +"b c #F7D800", +"c c #F7DC00", +"d c #F6DE00", +"e c #F4DF00", +"f c #F0E000", +"g c #EBE000", +"h c #E6DF00", +"i c #E0DA00", +"j c #D6D200", +"k c #CAC700", +"l c #D2C400", +"m c #D2C500", +"n c #D4C500", +"o c #D9C400", +"p c #DFC400", +"q c #E4C500", +"r c #E7C900", +"s c #E9CE00", +"t c #ECD000", +"u c #EECF00", +"v c #F1CD00", +"w c #F6CF00", +"x c #F8D300", +"y c #F8D600", +"z c #F9D900", +"A c #F8DD00", +"B c #F1DD00", +"C c #EBDB00", +"D c #E6D800", +"E c #E0D500", +"F c #D7CF00", +"G c #D6C800", +"H c #D7CA00", +"I c #DACA00", +"J c #DFCB00", +"K c #E6CB00", +"L c #EBCD00", +"M c #F0D200", +"N c #F2D400", +"O c #F4D500", +"P c #F6D500", +"Q c #F8D500", +"R c #F9D400", +"S c #FAD200", +"T c #FBD600", +"U c #FBDB00", +"V c #F9DD00", +"W c #F3D900", +"X c #EBD200", +"Y c #E4CC00", +"Z c #DFCA00", +"` c #03AD07", +" . c #02B20B", +".. c #01B40E", +"+. c #01B20F", +"@. c #01A60C", +"#. c #D8CA00", +"$. c #DCCD00", +"%. c #DFCF00", +"&. c #E5D000", +"*. c #EBD100", +"=. c #F3D400", +"-. c #F4D600", +";. c #F6D700", +">. c #F7DA00", +",. c #F8DB00", +"'. c #F9DB00", +"). c #FAD700", +"!. c #FAD000", +"~. c #FBD300", +"{. c #FBD800", +"]. c #FADA00", +"^. c #E1BF00", +"/. c #DDBB00", +"(. c #0AAE02", +"_. c #05AF04", +":. c #02B107", +"<. c #01B40B", +"[. c #00B60E", +"}. c #00B40F", +"|. c #00AB0D", +"1. c #DCCF00", +"2. c #E0D000", +"3. c #E5D100", +"4. c #F2D300", +"5. c #F5D600", +"6. c #F6D900", +"7. c #F6DC00", +"8. c #F6D200", +"9. c #F4D400", +"0. c #EDCD00", +"a. c #E5C200", +"b. c #DDB600", +"c. c #0BC001", +"d. c #07BD02", +"e. c #04BB03", +"f. c #02BA05", +"g. c #01BA08", +"h. c #01BA0B", +"i. c #01B70C", +"j. c #01AD0A", +"k. c #D3C800", +"l. c #DACF00", +"m. c #DED000", +"n. c #E3D000", +"o. c #E8CE00", +"p. c #ECCE00", +"q. c #EFCF00", +"r. c #F1D100", +"s. c #F1D600", +"t. c #F0D900", +"u. c #EFDA00", +"v. c #EFDB00", +"w. c #EFD800", +"x. c #ECD400", +"y. c #E5CA00", +"z. c #E1C400", +"A. c #DBBA00", +"B. c #02B102", +"C. c #03B802", +"D. c #04C301", +"E. c #05CD00", +"F. c #05D000", +"G. c #03CE00", +"H. c #02CA01", +"I. c #02C702", +"J. c #03C304", +"K. c #03BF06", +"L. c #04BA07", +"M. c #04AF06", +"N. c #CFC700", +"O. c #D8CE00", +"P. c #E0CD00", +"Q. c #E9CA00", +"R. c #EDCE00", +"S. c #EED000", +"T. c #ECD200", +"U. c #EAD400", +"V. c #E9D700", +"W. c #E9DA00", +"X. c #EADD00", +"Y. c #E9DC00", +"Z. c #E4D400", +"`. c #DECA00", +" + c #DAC200", +".+ c #D6BB00", +"++ c #03AA02", +"@+ c #02B202", +"#+ c #02B902", +"$+ c #02C501", +"%+ c #02D100", +"&+ c #02D700", +"*+ c #01D700", +"=+ c #01D500", +"-+ c #02D200", +";+ c #03CB01", +">+ c #05C401", +",+ c #06BE02", +"'+ c #06B202", +")+ c #CFC500", +"!+ c #D8CC00", +"~+ c #DCCE00", +"{+ c #E0CB00", +"]+ c #E4C800", +"^+ c #E7C700", +"/+ c #EBCC00", +"(+ c #EBCE00", +"_+ c #EACF00", +":+ c #E9D000", +"<+ c #E8D100", +"[+ c #E8D600", +"}+ c #E9DB00", +"|+ c #E8DB00", +"1+ c #E3D300", +"2+ c #DBC600", +"3+ c #D6BD00", +"4+ c #D1B600", +"5+ c #04B201", +"6+ c #03B702", +"7+ c #03BE01", +"8+ c #03C701", +"9+ c #03D100", +"0+ c #02D600", +"a+ c #01D600", +"b+ c #01D300", +"c+ c #03CD01", +"d+ c #05C601", +"e+ c #06C001", +"f+ c #06B601", +"g+ c #D1C200", +"h+ c #D9CA00", +"i+ c #DDCB00", +"j+ c #E0C800", +"k+ c #E4C400", +"l+ c #E6C300", +"m+ c #E8CC00", +"n+ c #E9CC00", +"o+ c #EACA00", +"p+ c #EBCF00", +"q+ c #EDD500", +"r+ c #ECD600", +"s+ c #E6CF00", +"t+ c #DDC400", +"u+ c #000000", +"v+ c #0AB100", +"w+ c #08B700", +"x+ c #06BD01", +"y+ c #05C101", +"z+ c #04C601", +"A+ c #05CB00", +"B+ c #04D200", +"C+ c #02D300", +"D+ c #01D101", +"E+ c #00CF01", +"F+ c #02CA02", +"G+ c #03C503", +"H+ c #04C003", +"I+ c #05BA03", +"J+ c #D2C000", +"K+ c #D9C600", +"L+ c #DCC700", +"M+ c #E3C100", +"N+ c #E5C100", +"O+ c #E6C500", +"P+ c #EAC500", +"Q+ c #ECC400", +"R+ c #EEC900", +"S+ c #EFD200", +"T+ c #12AA00", +"U+ c #0CB100", +"V+ c #09B700", +"W+ c #08BD00", +"X+ c #07C400", +"Y+ c #06CB00", +"Z+ c #06CE00", +"`+ c #06CF00", +" @ c #05CE00", +".@ c #01CB01", +"+@ c #00C902", +"@@ c #01C603", +"#@ c #02C204", +"$@ c #02C005", +"%@ c #03BD05", +"&@ c #CFBC00", +"*@ c #D3C100", +"=@ c #D7C200", +"-@ c #DBC100", +";@ c #E0C100", +">@ c #E3C200", +",@ c #E5C900", +"'@ c #E6CA00", +")@ c #E8C600", +"!@ c #EAC200", +"~@ c #ECC100", +"{@ c #EECD00", +"]@ c #0FB203", +"^@ c #0CB502", +"/@ c #09B901", +"(@ c #07BC00", +"_@ c #06C000", +":@ c #06C700", +"<@ c #01C901", +"[@ c #00C602", +"}@ c #01C203", +"|@ c #02BF04", +"1@ c #02BE05", +"2@ c #C6B700", +"3@ c #C9B900", +"4@ c #CCBB00", +"5@ c #DBC300", +"6@ c #E1C500", +"7@ c #E4C600", +"8@ c #E5C600", +"9@ c #E8C200", +"0@ c #E9BF00", +"a@ c #EAC000", +"b@ c #EAC600", +"c@ c #EACE00", +"d@ c #0CC203", +"e@ c #09C407", +"f@ c #06C508", +"g@ c #05C406", +"h@ c #05C303", +"i@ c #04C300", +"j@ c #04C500", +"k@ c #04C801", +"l@ c #03CE01", +"m@ c #03CF01", +"n@ c #03D000", +"o@ c #02CB00", +"p@ c #01C801", +"q@ c #01C401", +"r@ c #02BF02", +"s@ c #02BB02", +"t@ c #02B903", +"u@ c #03B803", +"v@ c #BEB200", +"w@ c #C3B600", +"x@ c #CCBC00", +"y@ c #D7C400", +"z@ c #DEC800", +"A@ c #E2C700", +"B@ c #E4C300", +"C@ c #E6C100", +"D@ c #E8BF00", +"E@ c #E8BE00", +"F@ c #E8C000", +"G@ c #E7C600", +"H@ c #0BC903", +"I@ c #06CD08", +"J@ c #01D00B", +"K@ c #00CF09", +"L@ c #01CD05", +"M@ c #02C900", +"N@ c #01C903", +"O@ c #01CA02", +"P@ c #00CD01", +"Q@ c #01D000", +"R@ c #01CF00", +"S@ c #01CB00", +"T@ c #02C600", +"U@ c #02C200", +"V@ c #02BC00", +"W@ c #03B700", +"X@ c #03B501", +"Y@ c #03B301", +"Z@ c #BAAF00", +"`@ c #BFB300", +" # c #C8BB00", +".# c #D3C400", +"+# c #DBC700", +"@# c #DFC500", +"## c #E1C000", +"$# c #E4BE00", +"%# c #E6BD00", +"&# c #E7BD00", +"*# c #E7C000", +"=# c #0ACB02", +"-# c #05CF07", +";# c #01D20A", +"># c #00D209", +",# c #00CF05", +"'# c #00CB00", +")# c #00C901", +"!# c #00C703", +"~# c #00C803", +"{# c #00CC02", +"]# c #00CF00", +"^# c #00CE00", +"/# c #01CA00", +"(# c #02C500", +"_# c #02C000", +":# c #03BA00", +"<# c #03B500", +"[# c #03B200", +"}# c #03B100", +"|# c #BBAF00", +"1# c #BFB200", +"2# c #C7B900", +"3# c #DAC100", +"4# c #DCBD00", +"5# c #DEBB00", +"6# c #E2BB00", +"7# c #E5BD00", +"8# c #E7C100", +"9# c #E9C700", +"0# c #08CD01", +"a# c #05CF04", +"b# c #02D007", +"c# c #01CF06", +"d# c #01CE03", +"e# c #01CC00", +"f# c #00C802", +"g# c #00CA02", +"h# c #00CD00", +"i# c #00CC00", +"j# c #01C700", +"k# c #02BE00", +"l# c #02B800", +"m# c #02B400", +"n# c #02B100", +"o# c #BEB100", +"p# c #C1B300", +"q# c #C7B800", +"r# c #CEBD00", +"s# c #D2BF00", +"t# c #D4BC00", +"u# c #D6B900", +"v# c #D9B900", +"w# c #DEBA00", +"x# c #E3BE00", +"y# c #EAC700", +"z# c #05CD01", +"A# c #05CC03", +"B# c #03CB03", +"C# c #02CB01", +"D# c #00CA00", +"E# c #00C603", +"F# c #00C800", +"G# c #01C400", +"H# c #02B700", +"I# c #02B300", +"J# c #02B000", +"K# c #BFB100", +"L# c #C6B500", +"M# c #CAB800", +"N# c #CEB900", +"O# c #D0B900", +"P# c #D3B800", +"Q# c #D7B900", +"R# c #DDBA00", +"S# c #E3BC00", +"T# c #E7BF00", +"U# c #EBC500", +"V# c #ECCB00", +"W# c #05CF00", +"X# c #05CC00", +"Y# c #05C901", +"Z# c #03C900", +"`# c #01C900", +" $ c #01C800", +".$ c #00C403", +"+$ c #00C700", +"@$ c #00C500", +"#$ c #01C100", +"$$ c #02BD00", +"%$ c #02B900", +"&$ c #02B500", +"*$ c #02B200", +"=$ c #02AF00", +"-$ c #C4B100", +";$ c #C7B100", +">$ c #C9B200", +",$ c #CDB500", +"'$ c #D2B900", +")$ c #D8BB00", +"!$ c #DFBA00", +"~$ c #E7B900", +"{$ c #EABA00", +"]$ c #ECC200", +"^$ c #DBCF00", +"/$ c #02D101", +"($ c #05C600", +"_$ c #03C700", +":$ c #02C700", +"<$ c #01C600", +"[$ c #00C402", +"}$ c #00C102", +"|$ c #00C201", +"1$ c #00C400", +"2$ c #00C200", +"3$ c #01BE00", +"4$ c #03AE00", +"5$ c #03AD00", +"6$ c #C2A900", +"7$ c #C3A900", +"8$ c #C6AC00", +"9$ c #CBB200", +"0$ c #D2BA00", +"a$ c #D9BE00", +"b$ c #EAB600", +"c$ c #ECB500", +"d$ c #EABE00", +"e$ c #E2C900", +"f$ c #CDCF00", +"g$ c #A3CA00", +"h$ c #00D205", +"i$ c #02D000", +"j$ c #04CB00", +"k$ c #05C500", +"l$ c #01C101", +"m$ c #00BE02", +"n$ c #00BF01", +"o$ c #00C000", +"p$ c #00BE00", +"q$ c #01BA00", +"r$ c #03AF00", +"s$ c #03AB00", +"t$ c #C0A500", +"u$ c #C4A900", +"v$ c #CAB100", +"w$ c #D3BB00", +"x$ c #DAC000", +"y$ c #E3BB00", +"z$ c #EAB400", +"A$ c #E9B300", +"B$ c #E1BB00", +"C$ c #D1C600", +"D$ c #B6CD00", +"E$ c #89CA00", +"F$ c #53C300", +"G$ c #00D408", +"H$ c #00D404", +"I$ c #01D200", +"J$ c #02CF00", +"K$ c #03CA00", +"L$ c #04C401", +"M$ c #03C400", +"N$ c #02C400", +"O$ c #01BF01", +"P$ c #00BC02", +"Q$ c #00BB02", +"R$ c #00BC01", +"S$ c #00BD00", +"T$ c #00BB00", +"U$ c #01B700", +"V$ c #02AE00", +"W$ c #03A900", +"X$ c #03A800", +"Y$ c #03A700", +"Z$ c #BFA300", +"`$ c #C3A800", +" % c #DDC100", +".% c #E5BC00", +"+% c #E4B100", +"@% c #D1B800", +"#% c #B6C100", +"$% c #95C800", +"%% c #69C901", +"&% c #39C802", +"*% c #01D307", +"=% c #00D305", +"-% c #01D102", +";% c #02CE00", +">% c #02CC01", +",% c #03C903", +"'% c #03C604", +")% c #03C504", +"!% c #03C402", +"~% c #03C300", +"{% c #01BD01", +"]% c #00BA02", +"^% c #00B902", +"/% c #00BA01", +"(% c #00BA00", +"_% c #00B800", +":% c #01B400", +"<% c #02AB00", +"[% c #02A700", +"}% c #03A500", +"|% c #03A300", +"1% c #BEA400", +"2% c #CDB300", +"3% c #D8BD00", +"4% c #DEC100", +"5% c #E4BC00", +"6% c #E6B400", +"7% c #DBB200", +"8% c #BDB600", +"9% c #96BD00", +"0% c #70C300", +"a% c #49C802", +"b% c #22CD05", +"c% c #01D104", +"d% c #02CF03", +"e% c #03CC01", +"f% c #03C802", +"g% c #02C805", +"h% c #01C708", +"i% c #01C607", +"j% c #02C404", +"k% c #03C200", +"l% c #03BF00", +"m% c #01BB01", +"n% c #00B802", +"o% c #00B702", +"p% c #00B701", +"q% c #00B500", +"r% c #01B100", +"s% c #02A300", +"t% c #02A000", +"u% c #029E00", +"v% c #029D00", +"w% c #003EC4", +"x% c #BFA500", +"y% c #C4AA00", +"z% c #CEB200", +"A% c #D7BA00", +"B% c #DABD00", +"C% c #DCBA00", +"D% c #D9B600", +"E% c #C9B500", +"F% c #A5B700", +"G% c #78BA00", +"H% c #50BF00", +"I% c #2FC703", +"J% c #11CF07", +"K% c #02CE03", +"L% c #04C800", +"M% c #03C502", +"N% c #01C606", +"O% c #00C709", +"P% c #00C608", +"Q% c #02C304", +"R% c #03C001", +"S% c #03BD00", +"T% c #02B901", +"U% c #00B602", +"V% c #00B502", +"W% c #00B501", +"X% c #00B200", +"Y% c #01AE00", +"Z% c #02A900", +"`% c #02A400", +" & c #029C00", +".& c #029A00", +"+& c #029900", +"@& c #004AC8", +"#& c #004DD8", +"$& c #004EDB", +"%& c #0052D3", +"&& c #CAAE00", +"*& c #CEB000", +"=& c #D0B100", +"-& c #C9B600", +";& c #C0B900", +">& c #ACBC00", +",& c #85BB00", +"'& c #58BB00", +")& c #33BD01", +"!& c #1AC404", +"~& c #08CD08", +"{& c #04CB01", +"]& c #05C800", +"^& c #06C500", +"/& c #05C200", +"(& c #04C202", +"_& c #01C405", +":& c #00C507", +"<& c #00C406", +"[& c #01C103", +"}& c #02BB00", +"|& c #01B401", +"1& c #00B201", +"2& c #00AF00", +"3& c #01AB00", +"4& c #01A600", +"5& c #02A200", +"6& c #029700", +"7& c #029600", +"8& c #0050C0", +"9& c #0055DA", +"0& c #0057E6", +"a& c #0058E5", +"b& c #0058D9", +"c& c #0058C5", +"d& c #CCAB00", +"e& c #C8A700", +"f& c #C0A900", +"g& c #B5B100", +"h& c #A4BC00", +"i& c #8AC300", +"j& c #64C100", +"k& c #3ABD00", +"l& c #1ABC02", +"m& c #0AC205", +"n& c #03CA0A", +"o& c #05C801", +"p& c #07C200", +"q& c #04C001", +"r& c #02C102", +"s& c #00C204", +"t& c #00C103", +"u& c #01BE02", +"v& c #01B000", +"w& c #00AC00", +"x& c #01A800", +"y& c #01A400", +"z& c #029F00", +"A& c #029400", +"B& c #029300", +"C& c #0057CF", +"D& c #005BE4", +"E& c #005DEE", +"F& c #005DEB", +"G& c #005BDE", +"H& c #0059CD", +"I& c #0058C2", +"J& c #C7A202", +"K& c #C4A000", +"L& c #B9A400", +"M& c #A6B000", +"N& c #8BBE00", +"O& c #6DC700", +"P& c #49C501", +"Q& c #24BF02", +"R& c #09BD04", +"S& c #00C107", +"T& c #00C80B", +"U& c #06C400", +"V& c #07BF00", +"W& c #06BE00", +"X& c #05BE00", +"Y& c #02BF01", +"Z& c #00BF02", +"`& c #00BE01", +" * c #00AB00", +".* c #00A800", +"+* c #039C00", +"@* c #029800", +"#* c #029500", +"$* c #005EDD", +"%* c #0060EB", +"&* c #0060F2", +"** c #0060F0", +"=* c #005FE7", +"-* c #005DDC", +";* c #005AD5", +">* c #0054D4", +",* c #D29900", +"'* c #C1A400", +")* c #9AB300", +"!* c #72C000", +"~* c #53C602", +"{* c #35C604", +"]* c #17C306", +"^* c #02C108", +"/* c #00C40A", +"(* c #00C70B", +"_* c #06C100", +":* c #06BF00", +"<* c #05BD00", +"[* c #05BB00", +"}* c #04BB00", +"|* c #03BC00", +"1* c #02BC01", +"2* c #02BA00", +"3* c #03B300", +"4* c #01B001", +"5* c #00AD01", +"6* c #00A901", +"7* c #00A500", +"8* c #01A200", +"9* c #039D00", +"0* c #059900", +"a* c #069700", +"b* c #059700", +"c* c #039700", +"d* c #0063E2", +"e* c #0063E8", +"f* c #0063F0", +"g* c #0063F5", +"h* c #0063F4", +"i* c #0062F0", +"j* c #0061EB", +"k* c #005DE6", +"l* c #0056E1", +"m* c #004DDB", +"n* c #0047D9", +"o* c #86B900", +"p* c #55C000", +"q* c #3BC304", +"r* c #26C507", +"s* c #10C60A", +"t* c #01C70B", +"u* c #00C70C", +"v* c #05BC00", +"w* c #03B900", +"x* c #03B800", +"y* c #04B600", +"z* c #04B400", +"A* c #05B100", +"B* c #04B000", +"C* c #02B001", +"D* c #01B003", +"E* c #00AC03", +"F* c #00A601", +"G* c #01A000", +"H* c #099200", +"I* c #0A9100", +"J* c #089500", +"K* c #049C00", +"L* c #0063E7", +"M* c #0064EB", +"N* c #0065F2", +"O* c #0065F7", +"P* c #0065F6", +"Q* c #0064F4", +"R* c #0060EF", +"S* c #0059E7", +"T* c #0051E0", +"U* c #004BDF", +"V* c #0042E9", +"W* c #5CB900", +"X* c #38BD04", +"Y* c #27C007", +"Z* c #1AC309", +"`* c #0BC70C", +" = c #00C90D", +".= c #00C90C", +"+= c #00C60A", +"@= c #04BC00", +"#= c #04BA00", +"$= c #03B901", +"%= c #02B801", +"&= c #02B701", +"*= c #03B701", +"== c #05AE00", +"-= c #04AD01", +";= c #02AC03", +">= c #00AC04", +",= c #00A804", +"'= c #00A302", +")= c #019D01", +"!= c #049800", +"~= c #089300", +"{= c #0C8E00", +"]= c #0E8D00", +"^= c #0C9300", +"/= c #005CE1", +"(= c #0064F0", +"_= c #0067F7", +":= c #0069F9", +"<= c #0069F8", +"[= c #0069F6", +"}= c #0066F3", +"|= c #0060EE", +"1= c #0059EA", +"2= c #0054E8", +"3= c #004DEC", +"4= c #0046F0", +"5= c #0046E9", +"6= c #2AAF26", +"7= c #1EB80E", +"8= c #17BD05", +"9= c #0FC205", +"0= c #06C609", +"a= c #00C90B", +"b= c #00C909", +"c= c #00C607", +"d= c #03BA03", +"e= c #02B705", +"f= c #01B606", +"g= c #01B604", +"h= c #02B702", +"i= c #03B400", +"j= c #03B002", +"k= c #02AC04", +"l= c #02A805", +"m= c #01A406", +"n= c #00A006", +"o= c #009E05", +"p= c #029D03", +"q= c #039B01", +"r= c #069800", +"s= c #099300", +"t= c #0D8C00", +"u= c #0053D7", +"v= c #0059E0", +"w= c #0061EC", +"x= c #0067F6", +"y= c #006BF9", +"z= c #006DF8", +"A= c #006EF5", +"B= c #006DF4", +"C= c #0069F5", +"D= c #0064F5", +"E= c #0060F3", +"F= c #0059EE", +"G= c #0054E6", +"H= c #0056DA", +"I= c #0363CC", +"J= c #097AB2", +"K= c #0D9286", +"L= c #0DA54A", +"M= c #0BB01D", +"N= c #09B806", +"O= c #06BF02", +"P= c #03C405", +"Q= c #00C707", +"R= c #00C805", +"S= c #02B906", +"T= c #02B607", +"U= c #01B50A", +"V= c #00B40A", +"W= c #00B508", +"X= c #00B803", +"Y= c #01B800", +"Z= c #00B004", +"`= c #00A908", +" - c #00A209", +".- c #009A09", +"+- c #009408", +"@- c #019207", +"#- c #039505", +"$- c #079A01", +"%- c #0A9B00", +"&- c #004ED3", +"*- c #0054DC", +"=- c #005DEA", +"-- c #006AFA", +";- c #0070F6", +">- c #006EFA", +",- c #006BFD", +"'- c #0067FA", +")- c #0060F1", +"!- c #005AE5", +"~- c #005CD9", +"{- c #0067CE", +"]- c #0079BD", +"^- c #008B9D", +"/- c #009A69", +"(- c #00A536", +"_- c #00AF16", +":- c #00B80A", +"<- c #01BE07", +"[- c #01C306", +"}- c #01C502", +"|- c #01B708", +"1- c #00B30C", +"2- c #00B409", +"3- c #00B604", +"4- c #00B700", +"5- c #00B401", +"6- c #00AD05", +"7- c #00A609", +"8- c #009E0B", +"9- c #00940A", +"0- c #008C09", +"a- c #018A08", +"b- c #058E06", +"c- c #004FD6", +"d- c #0052DE", +"e- c #0058EC", +"f- c #005EF8", +"g- c #0062FC", +"h- c #0067FB", +"i- c #006BF8", +"j- c #006DF9", +"k- c #006DFC", +"l- c #006BFF", +"m- c #0068FD", +"n- c #0062F6", +"o- c #005DED", +"p- c #005EE4", +"q- c #0065DD", +"r- c #0071D1", +"s- c #007EB8", +"t- c #008A8E", +"u- c #00965F", +"v- c #00A13B", +"w- c #00AC21", +"x- c #00B60F", +"y- c #01BD03", +"z- c #02C100", +"A- c #01C200", +"B- c #00B50A", +"C- c #00B40B", +"D- c #00B30B", +"E- c #00B309", +"F- c #00B307", +"G- c #00B203", +"H- c #00B101", +"I- c #00AD02", +"J- c #01A705", +"K- c #02A108", +"L- c #029B09", +"M- c #01920A", +"N- c #00890A", +"O- c #01860A", +"P- c #0051D8", +"Q- c #0052DA", +"R- c #0052E1", +"S- c #0053EE", +"T- c #0054FA", +"U- c #0058FF", +"V- c #005EFE", +"W- c #0065FB", +"X- c #0068FB", +"Y- c #0068FC", +"Z- c #0067FD", +"`- c #0066FD", +" ; c #0062FB", +".; c #005EF3", +"+; c #0062EF", +"@; c #0068E6", +"#; c #0070D3", +"$; c #007AB3", +"%; c #00858D", +"&; c #009168", +"*; c #009E41", +"=; c #01AB1B", +"-; c #02B502", +";; c #00B40C", +">; c #00B308", +",; c #00B305", +"'; c #00B103", +"); c #00A902", +"!; c #01A502", +"~; c #05A003", +"{; c #089C04", +"]; c #099905", +"^; c #079407", +"/; c #0055DB", +"(; c #0054DD", +"_; c #0053E4", +":; c #0051F0", +"<; c #004FFC", +"[; c #0052FF", +"}; c #0059FF", +"|; c #0060FD", +"1; c #0064FC", +"2; c #0066FC", +"3; c #0065FC", +"4; c #0062FF", +"5; c #005FFF", +"6; c #005EFB", +"7; c #0060F4", +"8; c #0065E5", +"9; c #006DCE", +"0; c #0077B1", +"a; c #00828F", +"b; c #019062", +"c; c #019E31", +"d; c #02A90F", +"e; c #02B101", +"f; c #00B20C", +"g; c #00B10A", +"h; c #00B105", +"i; c #02AC00", +"j; c #05A701", +"k; c #08A202", +"l; c #0D9E02", +"m; c #149B02", +"n; c #199802", +"o; c #1B9702", +"p; c #0058DC", +"q; c #0058E0", +"r; c #0054F2", +"s; c #0052FE", +"t; c #0054FF", +"u; c #005AFF", +"v; c #0061FD", +"w; c #0066FB", +"x; c #0062FE", +"y; c #005DFF", +"z; c #005CFD", +"A; c #005CF8", +"B; c #005FEE", +"C; c #0065E0", +"D; c #006CCE", +"E; c #0075B2", +"F; c #008088", +"G; c #018D58", +"H; c #019832", +"I; c #02A31A", +"J; c #02AC0A", +"K; c #01B309", +"L; c #00B007", +"M; c #01AC03", +"N; c #09A400", +"O; c #129F01", +"P; c #1C9B01", +"Q; c #279801", +"R; c #349701", +"S; c #3E9601", +"T; c #449601", +"U; c #005BDB", +"V; c #005DE1", +"W; c #005CE8", +"X; c #005AF4", +"Y; c #005EFF", +"Z; c #0068F9", +"`; c #0063FA", +" > c #005BFB", +".> c #005AFA", +"+> c #005AF7", +"@> c #005BF2", +"#> c #005EED", +"$> c #0062E5", +"%> c #0068D3", +"&> c #0070B2", +"*> c #007A89", +"=> c #018462", +"-> c #01913E", +";> c #019E1B", +">> c #005DD8", +",> c #0060E0", +"'> c #0060E7", +")> c #005EF1", +"!> c #005DFB", +"~> c #0066F9", +"{> c #006BF7", +"]> c #0069F7", +"^> c #0064F7", +"/> c #005EF7", +"(> c #0059F6", +"_> c #0059F4", +":> c #0059F3", +"<> c #005CF4", +"[> c #005FE9", +"}> c #0063D1", +"|> c #006AB1", +"1> c #00728E", +"2> c #008061", +"3> c #019031", +"4> c #005DD1", +"5> c #0060D8", +"6> c #0060DD", +"7> c #005FE3", +"8> c #005EEA", +"9> c #0069F2", +"0> c #006BF3", +"a> c #006CF5", +"b> c #006CF6", +"c> c #006AF6", +"d> c #0061F5", +"e> c #005DF4", +"f> c #005CF2", +"g> c #005CF0", +"h> c #005BEF", +"i> c #005BF3", +"j> c #005CED", +"k> c #005FCD", +"l> c #0065B1", +"m> c #007183", +"n> c #00814E", +"o> c #005CC6", +"p> c #005FCB", +"q> c #005FCE", +"r> c #005FD0", +"s> c #005FD4", +"t> c #0061D9", +"u> c #0066E0", +"v> c #006BE8", +"w> c #006DED", +"x> c #006DF2", +"y> c #0067F5", +"z> c #0064F3", +"A> c #0062F1", +"B> c #0061ED", +"C> c #0061EA", +"D> c #0060E8", +"E> c #005FE8", +"F> c #005EE8", +"G> c #005AE6", +"H> c #0057E1", +"I> c #005ACF", +"J> c #0063A6", +"K> c #007172", +"L> c #079900", +"M> c #129600", +"N> c #218C00", +"O> c #358600", +"P> c #528A00", +"Q> c #779400", +"R> c #989D00", +"S> c #ADA100", +"T> c #BAA100", +"U> c #C2A000", +"V> c #C69F00", +"W> c #C49D00", +"X> c #005BBD", +"Y> c #005EC0", +"Z> c #005EC1", +"`> c #005EC2", +" , c #005FC4", +"., c #0062CA", +"+, c #0067D5", +"@, c #006BE0", +"#, c #006DE8", +"$, c #006BEE", +"%, c #0069F3", +"&, c #0064F1", +"*, c #0063ED", +"=, c #0063E9", +"-, c #0063E4", +";, c #0063E1", +">, c #0062DF", +",, c #0060DE", +"', c #005EDF", +"), c #0053EB", +"!, c #0058C0", +"~, c #006392", +"{, c #098C04", +"], c #198C00", +"^, c #308600", +"/, c #4B8400", +"(, c #6C8C00", +"_, c #949800", +":, c #B5A300", +"<, c #C6A700", +"[, c #CBA700", +"}, c #CCA500", +"|, c #CCA400", +"1, c #C7A000", +"2, c #005DBB", +"3, c #005EBD", +"4, c #005FBD", +"5, c #0060C0", +"6, c #0061C6", +"7, c #0064D1", +"8, c #0065DB", +"9, c #0065E3", +"0, c #0062E9", +"a, c #005DF0", +"b, c #005BED", +"c, c #005BEB", +"d, c #005FE2", +"e, c #0060DF", +"f, c #005FDB", +"g, c #005DDB", +"h, c #0057E0", +"i, c #0051E5", +"j, c #004FDE", +"k, c #0052C9", +"l, c #0058AA", +"m, c #0B8115", +"n, c #218606", +"o, c #408802", +"p, c #628B00", +"q, c #849200", +"r, c #A99B00", +"s, c #C6A300", +"t, c #D2A700", +"u, c #D1A800", +"v, c #CDA900", +"w, c #CAA800", +"x, c #C4A400", +"y, c #005BD2", +"z, c #0057D7", +"A, c #0051E2", +"B, c #004FE6", +"C, c #004DE9", +"D, c #004CEA", +"E, c #004CE9", +"F, c #004DE8", +"G, c #0052E5", +"H, c #005DDA", +"I, c #005CD8", +"J, c #0058D5", +"K, c #0053D1", +"L, c #0050CB", +"M, c #004EC6", +"N, c #004EBE", +"O, c #0C762E", +"P, c #298118", +"Q, c #528D0A", +"R, c #7A9701", +"S, c #9A9C00", +"T, c #B99E00", +"U, c #CF9F00", +"V, c #D5A300", +"W, c #CEA800", +"X, c #C5AC00", +"Y, c #C1AC00", +"Z, c #BCA800", +"`, c #0041D5", +" ' c #0041DB", +".' c #0041E0", +"+' c #0041E3", +"@' c #0040E4", +"#' c #0040E5", +"$' c #0042E5", +"%' c #0049E2", +"&' c #0050DE", +"*' c #0056DC", +"=' c #0059DB", +"-' c #005BD9", +";' c #005BD5", +">' c #0059CC", +",' c #0055C1", +"'' c #0051BB", +")' c #004CC2", +"!' c #0048CC", +"~' c #0C6B47", +"{' c #2E7C2A", +"]' c #5E9012", +"^' c #8B9E02", +"/' c #AAA200", +"(' c #C49F00", +"_' c #D49C00", +":' c #D5A000", +"<' c #BEAE00", +"[' c #B9AF00", +"}' c #B5AC00", +"|' c #0038D7", +"1' c #003ADD", +"2' c #003BDF", +"3' c #003CDE", +"4' c #003FDC", +"5' c #0046D9", +"6' c #004ED6", +"7' c #0053D4", +"8' c #0057D6", +"9' c #0059D8", +"0' c #0059D6", +"a' c #0057CC", +"b' c #0053C0", +"c' c #004FBA", +"d' c #004AC4", +"e' c #0045D2", +"f' c #0A5F5D", +"g' c #2E7239", +"h' c #61891A", +"i' c #8E9A03", +"j' c #AE9F00", +"k' c #C89D00", +"l' c #D89B00", +"m' c #D8A000", +"n' c #CBA800", +"o' c #BEAF00", +"p' c #B8B000", +"q' c #B4AD00", +"r' c #003BD4", +"s' c #003DD0", +"t' c #0042CE", +"u' c #0048CB", +"v' c #004EC9", +"w' c #0052CA", +"x' c #0054D1", +"y' c #0056D8", +"z' c #0057D9", +"A' c #0054D2", +"B' c #0050C7", +"C' c #004DC1", +"D' c #0049C8", +"E' c #045274", +"F' c #2A644A", +"G' c #5D7B21", +"H' c #898D04", +"I' c #A99600", +"J' c #C59900", +"K' c #D89C00", +"L' c #DAA200", +"M' c #CEA900", +"N' c #C1AE00", +"O' c #B4AC00", +"P' c #ADA700", +"Q' c #0046BB", +"R' c #004BBA", +"S' c #004FBB", +"T' c #0051BE", +"U' c #0053C7", +"V' c #0055D2", +"W' c #0055D7", +"X' c #004ECC", +"Y' c #004BC7", +"Z' c #0048CA", +"`' c #0046CE", +" ) c #00458B", +".) c #23565D", +"+) c #556D2B", +"@) c #808007", +"#) c #A28D00", +"$) c #C19600", +"%) c #D69D00", +"&) c #DAA400", +"*) c #CFAA00", +"=) c #C3AE00", +"-) c #BCAE00", +";) c #ACA700", +">) c #004DAA", +",) c #0050A8", +"') c #0052AB", +")) c #0054B5", +"!) c #0055C8", +"~) c #0052C8", +"{) c #004BC5", +"]) c #0049C7", +"^) c #0047CA", +"/) c #003DA1", +"() c #1C4B75", +"_) c #4A603B", +":) c #78760C", +"<) c #9E8700", +"[) c #BE9400", +"}) c #D29E00", +"|) c #D6A500", +"1) c #CDAA00", +"2) c #C3AD00", +"3) c #B5AB00", +"4) c #ACA800", +"5) c #005386", +"6) c #00548A", +"7) c #005698", +"8) c #0056A7", +"9) c #0056B1", +"0) c #0053B6", +"a) c #004CBE", +"b) c #004AC2", +"c) c #0049C5", +"d) c #003BB3", +"e) c #124291", +"f) c #385156", +"g) c #6B6817", +"h) c #9A8100", +"i) c #BB9300", +"j) c #CC9F00", +"k) c #CFA600", +"l) c #C8A900", +"m) c #C0AC00", +"n) c #BCAD00", +"o) c #B6AB00", +"p) c #00595E", +"q) c #005A73", +"r) c #005A87", +"s) c #005994", +"t) c #00559F", +"u) c #0051AB", +"v) c #004DB4", +"w) c #004ABE", +"x) c #003ABD", +"y) c #0A3BA7", +"z) c #224174", +"A) c #565627", +"B) c #937900", +"C) c #B79300", +"D) c #C8A600", +"E) c #BDAA00", +"F) c #BAAB00", +"G) c #026439", +"H) c #026451", +"I) c #016365", +"J) c #016074", +"K) c #015B84", +"L) c #005496", +"M) c #004FA6", +"N) c #004CB0", +"O) c #004BB8", +"P) c #003ABE", +"Q) c #0638AC", +"R) c #153780", +"S) c #8A7301", +"T) c #B09100", +"U) c #BE9E00", +"V) c #C2A500", +"W) c #BFA700", +"X) c #BAA800", +"Y) c #B8A900", +"Z) c #B5A900", +"`) c #077430", +" ! c #05703E", +".! c #036C4C", +"+! c #026361", +"@! c #01597C", +"#! c #005093", +"$! c #004CA4", +"%! c #004BB2", +"&! c #003AB6", +"*! c #0538A3", +"=! c #143878", +"-! c #827102", +";! c #A98F00", +">! c #B99D00", +",! c #BDA300", +"'! c #BAA500", +")! c #B6A500", +"!! c #B4A600", +"~! c #B2A600", +"{! c #0B8413", +"]! c #077D1C", +"^! c #057729", +"/! c #036C41", +"(! c #015E60", +"_! c #00537D", +":! c #004E95", +"~ c #062BAA", +",~ c #142F78", +"'~ c #3C4327", +")~ c #766600", +"!~ c #997F00", +"~~ c #A98B00", +"{~ c #B19200", +"]~ c #B69700", +"^~ c #017811", +"/~ c #027A0F", +"(~ c #038007", +"_~ c #048600", +":~ c #058503", +"<~ c #047A1B", +"[~ c #026A3F", +"}~ c #082CA7", +"|~ c #1C3171", +"1~ c #454323", +"2~ c #776400", +"3~ c #987E00", +"4~ c #A88B00", +"5~ c #B19300", +"6~ c #B79900", +"7~ c #BEA000", +"8~ c #067F0B", +"9~ c #05800B", +"0~ c #048207", +"a~ c #048405", +"b~ c #03810A", +"c~ c #027418", +"d~ c #026131", +"e~ c #0034AE", +"f~ c #0E3497", +"g~ c #2B3A5E", +"h~ c #54481A", +"i~ c #7A6200", +"j~ c #967C00", +"k~ c #A58C00", +"l~ c #AE9400", +"m~ c #B59900", +"n~ c #0B8902", +"o~ c #098704", +"p~ c #068408", +"q~ c #03800D", +"r~ c #017A13", +"s~ c #006C18", +"t~ c #005422", +"u~ c #003AA0", +"v~ c #143D84", +"w~ c #38424B", +"x~ c #5F4C13", +"y~ c #7D6000", +"z~ c #937A00", +"A~ c #A18D00", +"B~ c #AA9600", +"C~ c #0C8B00", +"D~ c #078308", +"E~ c #027B10", +"F~ c #007315", +"G~ c #006717", +"H~ c #00521B", +"I~ c #003B92", +"J~ c #183F75", +"K~ c #3F453F", +"L~ c #644F0F", +"M~ c #7E6100", +"N~ c #927900", +"O~ c #A18E00", +"P~ c #A99800", +"Q~ c #057C06", +"R~ c #01720B", +"S~ c #006C10", +"T~ c #006716", +"U~ c #005F22", +"V~ c #003886", +"W~ c #193C69", +"X~ c #414537", +"Y~ c #65500C", +"Z~ c #A38F00", +"`~ c #AE9900", +" { c #037102", +".{ c #006805", +"+{ c #00660A", +"@{ c #006816", +"#{ c #006D2C", +"${ c #003278", +"%{ c #1A365D", +"&{ c #42442F", +"*{ c #65530A", +"={ c #7E6300", +"-{ c #957A00", +";{ c #A88F00", +">{ c #B49B00", +",{ c #006200", +"'{ c #006306", +"){ c #006B17", +"!{ c #007632", +"~{ c #002C6A", +"{{ c #1E3350", +"]{ c #474627", +"^{ c #6A5908", +"/{ c #816800", +"({ c #967B00", +"_{ c #AA8F00", +":{ c #016200", +"<{ c #016504", +"[{ c #016F19", +"}{ c #007C35", +"|{ c #032658", +"1{ c #253241", +"2{ c #524F1F", +"3{ c #766705", +"4{ c #887300", +"5{ c #977F00", +"6{ c #036903", +"7{ c #02751C", +"8{ c #007F38", +"9{ c #072048", +"0{ c #2C3233", +"a{ c #5D5818", +"b{ c #827503", +"c{ c #928100", +"d{ c #056E02", +"e{ c #02781F", +"f{ c #0A1B3A", +"g{ c #2E2F2A", +"h{ c #615A13", +"i{ c #897E02", +"j{ c #9A8C00", +"k{ c #027423", +"l{ c #007636", +"m{ c #0D172E", +"n{ c #292523", +"o{ c #595012", +"p{ c #8A7F01", +"q{ c #006831", +"r{ c #101422", +"s{ c #1F161D", +"t{ c #005C2D", +"u{ c #00552B", +"v{ c #0D281F", +"w{ c #13111A", +"x{ c #140717", +"y{ c #005027", +"z{ c #063F22", +"A{ c #0E271B", +"B{ c #140F15", +" . + @ ", +" # $ % & * = - ; > , ' ) ! ~ { ] ^ / ( _ : < [ ", +" } | 1 2 3 4 5 6 7 8 9 0 a b c d e f g h i j k ", +" l m n o p q r s t u v 0 w x y z A d B C D E F ", +" G H I J K L u M N O P Q R S S T U V W X Y Z ` ...+.@. ", +" #.$.%.&.*.M =.-.;.>.,.'.).S !.~.{.].=.> ^./. (._.:.<.[.}.|. ", +" H 1.2.3.*.M 4.O 5.6.7.7.6.P 8.! 9.4.0.a.b. c.d.e.f.g.h.i.j. ", +" k.l.m.n.o.p.q.r.4.s.t.u.v.u.w.x.o.y.z.A. B.C.D.E.F.G.H.I.J.K.L.M. ", +" N.O.1.P.5 Q.7 R.S.T.U.V.W.X.Y.Z.`. +.+ ++@+#+$+%+&+*+=+-+;+>+,+'+ ", +" )+!+~+{+]+^+> /+(+_+:+<+[+}+|+1+2+3+4+ 5+6+7+8+9+0+*+a+b+c+d+e+f+ ", +" g+h+i+j+k+l+^+m+s n+Q.o+p+q+r+s+t+u+u+u+ v+w+x+y+z+A+F.B+C+D+E+F+G+H+I+ ", +" J+K+L+= M+N+O+K 6 > P+Q+R+q.S+u+u+u+u+u+u+u+ T+U+V+W+X+Y+Z+`+`+ @c+.@+@@@#@$@%@ ", +" &@*@=@-@;@>@q ,@'@)@!@~@, {@u+u+u+u+u+u+u+u+u+ ]@^@/@(@_@:@E.F.F.`+E.;+<@[@}@|@1@%@ ", +" 2@3@4@% 5@6@7@8@O+9@0@a@b@c@u+u+u+u+u+u+u+u+u+u+d@e@f@g@h@i@j@k@;+l@m@n@G.o@p@q@r@s@t@u@ ", +" v@w@x@y@z@A@B@C@D@E@F@G@u+u+u+u+u+u+u+u+u+u+u+H@I@J@K@L@H.M@p@N@O@P@Q@R@S@T@U@V@W@X@Y@ ", +" Z@`@ #.#+#@###$#%#&#*#G@u+u+u+u+u+u+u+u+u+u+u+=#-#;#>#,#P@'#)#!#~#{#]#^#/#(#_#:#<#[#}# ", +" |#1#2#| y@3#4#5#6#7#8#9#u+u+u+u+u+u+u+u+u+u+u+0#a#b#c#d#e#'#f#!#!#g#h#i#j#U@k#l#m#n#n# ", +" o#p#q#r#s#t#u#v#w#x#C@y#u+u+u+u+u+u+u+u+u+u+u+Z+z#A#B#C#S@D#f#E#E#f#D#F#G#_#V@H#I#n#J# ", +" K#p#L#M#N#O#P#Q#R#S#T#U#V#u+u+u+u+u+u+u+u+u+u+W#X#Y#Y#Z#`# $[@.$.$[@+$@$#$$$%$&$*$J#=$ ", +" -$;$>$,$'$)$!$~${$]$Q.^$u+u+u+u+u+u+u+u+/$n@X#:@($_$:$<$[$}$}$|$1$2$3$%$&$*$=$4$5$ ", +" 6$7$8$9$0$a$6#b$c$d$e$f$g$u+u+u+u+u+u+h$b+i$j$($k$j@(#G#l$m$m$n$o$p$q$&$*$r$5$s$s$ ", +" t$u$v$w$x$y$z$A$B$C$D$E$F$u+u+u+;#G$H$I$J$K$z+L$M$N$U@O$P$Q$R$S$T$U$*$V$s$W$X$Y$ ", +" Z$`$9$3+ %.%z$+%@%#%$%%%&%u+u+u+*%=%-%;%>%,%'%)%!%~%_#{%]%^%/%(%_%:%=$<%[%}%|%|% ", +" 1%7$2%3%4%5%6%7%8%9%0%a%b%u+u+u+c%d%e%Z#f%g%h%i%j%k%l%m%n%o%p%_%q%r%<%[%s%t%u%v% ", +" w% x%y%z%A%B%C%D%E%F%G%H%I%J%u+u+u+K%;+L%j@M%N%O%P%Q%R%S%T%U%V%W%q%X%Y%Z%`%t% &.&+& ", +" @&#&$&%& &&*&=&2%-&;&>&,&'&)&!&~&u+u+u+{&]&^&/&(&_&:&<&[&k#}&H#|&1&1&X%2&3&4&5&v%+&6&7& ", +" 8&9&0&a&b&c& d&e&f&g&h&i&j&k&l&m&n&u+u+u+o&^&p&_@q&r&s&t&u&}&l#m#r%v&2&2&w&x&y&z&.&7&A&B& ", +" C&D&E&F&G&H&I& J&K&L&M&N&O&P&Q&R&S&T&u+u+u+U&p&V&W&X&Y&Z&`&m%l#&$*$v&Y%w& *.*y&t%+*@*#*A&B& ", +" $*%*&***=*-*;*>* ,*'*)*!*~*{*]*^*/*(*u+u+u+_*:*<*[*}*|*1*2*W@<#3**$4*5*6*7*8*9*0*a*b*c*6& ", +" d*e*f*g*h*i*j*k*l*m*n* o*p*q*r*s*t*u*(*u+u+u+X&v*}*:#w*w*x*y*z*A*B*C*D*E*F*G* &a*H*I*J*K* ", +" L*M*N*O*O*P*Q*R*S*T*U*V* W*X*Y*Z*`* =.=+=u+u+u+@=#=$=%=&=*=y*z*A*==-=;=>=,='=)=!=~={=]=^= ", +" /==*(=_=:=<=[=}=|=1=2=3=4=5= 6=7=8=9=0=a=b=c=u+u+u+d=u@e=f=g=h=H#i=j=k=l=m=n=o=p=q=r=s=t= ", +" u=v=w=x=y=z=A=B=C=D=E=F=G=H=I=J=K=L=M=N=O=P=Q=R=!#u+u+u+S=T=U=V=W=X=Y=W%Z=`= -.-+-@-#-$-%- ", +" &-*-=-P*--z=;-;->-,-'-)-!-~-{-]-^-/-(-_-:-<-[-}-@$u+u+u+|-U=1-1-2-3-4-5-6-7-8-9-0-a-b- ", +" c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-A-u+u+u+B-C-D-E-F-G-H-I-J-K-L-M-N-O- u+u+u+u+u+u+ ", +" P-Q-R-S-T-U-V-W-X-Y-Z-`- ;f-.;+;@;#;$;%;&;*;=;-;}&k#u+u+u+;;D->;,;';I-);!;~;{;];^; u+u+u+u+u+u+u+u+ ", +" /;(;_;:;<;[;};|;1;2;3;1;4;5;V-6;7;8;9;0;a;b;c;d;e;H#u+u+u+f;g;h;4*i;j;k;l;m;n;o; u+u+u+u+u+u+u+u+u+u+ ", +" p;q;0&r;s;t;u;v;3;w;'-w;x;5;y;z;A;B;C;D;E;F;G;H;I;J;u+u+u+K;L;M;X$N;O;P;Q;R;S;T; u+u+u+u+u+u+u+u+u+u+ ", +" U;V;W;X;U-};Y;1;'-:=:=Z;`;6; >.>+>@>#>$>%>&>*>=>->;>u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+ ", +" >>,>'>)>!>V-g-~><={>{>]>^>/>+>(>_>:>X;<>[>}>|>1>2>3>u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+ ", +" 4>5>6>7>8>|=(=9>0>a>b>c>P*d>e>f>g>h>@>i>j>V;k>l>m>n>u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+u+ ", +" o>p>q>r>s>t>u>v>w>x>a>c>y>z>A>B>C>D>E>F>k*G>H>I>J>K>u+u+u+L>M>N>O>P>Q>R>S>T>U>V>W> u+u+u+u+u+u+u+u+u+u+ ", +" X>Y>Z>`> ,.,+,@,#,$,%,y>}=&,*,=,-,;,>,,,',a&),R-!,~,u+u+u+{,],^,/,(,_,:,<,[,},|,1, u+u+u+u+u+u+u+u+u+ ", +" 2,3,4,5,6,7,8,9,0,B;a,h>b,c,k*d,e,6>f,g,h,i,j,k,l,u+u+u+m,n,o,p,q,r,s,t,u,v,w,x, u+u+u+u+u+u+u+ ", +" y,z,*-A,B,C,D,E,F,G,H>G&-*H,I,J,K,L,M,N,u+u+u+O,P,Q,R,S,T,U,V,W,X,Y,Z, u+u+u+u+ ", +" `, '.'+'@'#'$'%'&'*'='-';'>',''')'!'u+u+u+~'{']'^'/'('_':'w,<'['}' ", +" |'1'2'2'3'4'5'6'7'8'9'0'a'b'c'd'e'u+u+u+f'g'h'i'j'k'l'm'n'o'p'q' ", +" r's't'u'v'w'x'y'z'A'B'C'D'e'u+u+u+E'F'G'H'I'J'K'L'M'N'Z@O'P' ", +" Q'R'S'T'U'V'W'%&X'Y'Z'`'u+u+u+ ).)+)@)#)$)%)&)*)=)-)}';) ", +" >),)'))),'!)~)M,{)])^)u+u+u+/)()_):)<)[)})|)1)2)-)3)4) ", +" 5)6)7)8)9)0)S'a)b)c)u+u+u+d)e)f)g)h)i)j)k)l)m)n)o) ", +" p)q)r)s)t)u)v)R'w)u+u+u+x)y)z)A)B)C)('D)7$E)F)o) ", +" G)H)I)J)K)L)M)N)O)u+u+u+P)Q)R) S)T)U)V)W)X)Y)Z) ", +" `) !.!+!@!#!$!%!u+u+u+&!*!=! -!;!>!,!'!)!!!~! u+u+u+u+ ", +" {!]!^!/!(!_!:!~,~'~)~!~~~{~]~>! u+u+u+u+u+u+u+u+u+u+ ", +" ^~/~(~_~:~<~[~u+u+u+;~}~|~1~2~3~4~5~6~7~ u+u+u+u+u+u+u+u+u+u+ ", +" 8~9~0~a~b~c~d~u+u+u+e~f~g~h~i~j~k~l~m~ u+u+u+u+u+u+u+u+ ", +" n~o~p~q~r~s~t~u+u+u+u~v~w~x~y~z~A~B~ u+u+u+u+u+u+ ", +" C~D~E~F~G~H~u+u+u+I~J~K~L~M~N~O~P~ ", +" Q~R~S~T~U~u+u+u+V~W~X~Y~M~B)Z~`~ ", +" {.{+{@{#{u+u+u+${%{&{*{={-{;{>{ ", +" ,{'{){!{u+u+u+~{{{]{^{/{({_{ ", +" :{<{[{}{u+u+u+|{1{2{3{4{5{ ", +" 6{7{8{u+u+u+9{0{a{b{c{ ", +" d{e{8{u+u+u+f{g{h{i{j{ ", +" k{l{u+u+u+m{n{o{p{ ", +" q{u+u+u+r{s{ ", +" t{u{u+v{w{x{ ", +" y{z{A{B{ ", +" ", +" "};