diff --git a/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.cpp b/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.cpp index 5d42144101..310e33db68 100644 --- a/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.cpp +++ b/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.cpp @@ -1,713 +1,713 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkGeometry2DDataVtkMapper3D.h" #include "mitkImageMapperGL2D.h" #include "mitkLookupTableProperty.h" #include "mitkSmartPointerProperty.h" #include "mitkSurface.h" #include "mitkVtkRepresentationProperty.h" #include "mitkWeakPointerProperty.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateOr.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { Geometry2DDataVtkMapper3D::Geometry2DDataVtkMapper3D() : m_NormalsActorAdded(false), m_DataStorage(NULL) { m_EdgeTuber = vtkTubeFilter::New(); m_EdgeMapper = vtkPolyDataMapper::New(); m_SurfaceCreator = Geometry2DDataToSurfaceFilter::New(); m_SurfaceCreatorBoundingBox = BoundingBox::New(); m_SurfaceCreatorPointsContainer = BoundingBox::PointsContainer::New(); m_Edges = vtkFeatureEdges::New(); m_Edges->BoundaryEdgesOn(); m_Edges->FeatureEdgesOff(); m_Edges->NonManifoldEdgesOff(); m_Edges->ManifoldEdgesOff(); m_EdgeTransformer = vtkTransformPolyDataFilter::New(); m_NormalsTransformer = vtkTransformPolyDataFilter::New(); m_EdgeActor = vtkActor::New(); m_BackgroundMapper = vtkPolyDataMapper::New(); m_BackgroundActor = vtkActor::New(); m_Prop3DAssembly = vtkAssembly::New(); m_ImageAssembly = vtkAssembly::New(); m_SurfaceCreatorBoundingBox->SetPoints( m_SurfaceCreatorPointsContainer ); m_Cleaner = vtkCleanPolyData::New(); m_Cleaner->PieceInvariantOn(); m_Cleaner->ConvertLinesToPointsOn(); m_Cleaner->ConvertPolysToLinesOn(); m_Cleaner->ConvertStripsToPolysOn(); m_Cleaner->PointMergingOn(); // Make sure that the FeatureEdge algorithm is initialized with a "valid" // (though empty) input vtkPolyData *emptyPolyData = vtkPolyData::New(); m_Cleaner->SetInput( emptyPolyData ); emptyPolyData->Delete(); m_Edges->SetInput(m_Cleaner->GetOutput()); m_EdgeTransformer->SetInput( m_Edges->GetOutput() ); m_EdgeTuber->SetInput( m_EdgeTransformer->GetOutput() ); m_EdgeTuber->SetVaryRadiusToVaryRadiusOff(); m_EdgeTuber->SetNumberOfSides( 12 ); m_EdgeTuber->CappingOn(); m_EdgeMapper->SetInput( m_EdgeTuber->GetOutput() ); m_EdgeMapper->ScalarVisibilityOff(); m_BackgroundMapper->SetInput(emptyPolyData); m_EdgeActor->SetMapper( m_EdgeMapper ); m_BackgroundActor->GetProperty()->SetAmbient( 0.5 ); m_BackgroundActor->GetProperty()->SetColor( 0.0, 0.0, 0.0 ); m_BackgroundActor->GetProperty()->SetOpacity( 1.0 ); m_BackgroundActor->SetMapper( m_BackgroundMapper ); vtkProperty * backfaceProperty = m_BackgroundActor->MakeProperty(); backfaceProperty->SetColor( 0.0, 0.0, 0.0 ); m_BackgroundActor->SetBackfaceProperty( backfaceProperty ); backfaceProperty->Delete(); m_FrontHedgeHog = vtkHedgeHog::New(); m_BackHedgeHog = vtkHedgeHog::New(); m_FrontNormalsMapper = vtkPolyDataMapper::New(); m_FrontNormalsMapper->SetInput( m_FrontHedgeHog->GetOutput() ); m_BackNormalsMapper = vtkPolyDataMapper::New(); m_Prop3DAssembly->AddPart( m_EdgeActor ); m_Prop3DAssembly->AddPart( m_ImageAssembly ); m_FrontNormalsActor = vtkActor::New(); m_FrontNormalsActor->SetMapper(m_FrontNormalsMapper); m_BackNormalsActor = vtkActor::New(); m_BackNormalsActor->SetMapper(m_BackNormalsMapper); m_DefaultLookupTable = vtkLookupTable::New(); m_DefaultLookupTable->SetTableRange( -1024.0, 4096.0 ); m_DefaultLookupTable->SetSaturationRange( 0.0, 0.0 ); m_DefaultLookupTable->SetHueRange( 0.0, 0.0 ); m_DefaultLookupTable->SetValueRange( 0.0, 1.0 ); m_DefaultLookupTable->Build(); m_DefaultLookupTable->SetTableValue( 0, 0.0, 0.0, 0.0, 0.0 ); m_ImageMapperDeletedCommand = MemberCommandType::New(); m_ImageMapperDeletedCommand->SetCallbackFunction( this, &Geometry2DDataVtkMapper3D::ImageMapperDeletedCallback ); } Geometry2DDataVtkMapper3D::~Geometry2DDataVtkMapper3D() { m_ImageAssembly->Delete(); m_Prop3DAssembly->Delete(); m_EdgeTuber->Delete(); m_EdgeMapper->Delete(); m_EdgeTransformer->Delete(); m_Cleaner->Delete(); m_Edges->Delete(); m_NormalsTransformer->Delete(); m_EdgeActor->Delete(); m_BackgroundMapper->Delete(); m_BackgroundActor->Delete(); m_DefaultLookupTable->Delete(); m_FrontNormalsMapper->Delete(); m_FrontNormalsActor->Delete(); m_FrontHedgeHog->Delete(); m_BackNormalsMapper->Delete(); m_BackNormalsActor->Delete(); m_BackHedgeHog->Delete(); // Delete entries in m_ImageActors list one by one m_ImageActors.clear(); LookupTablePropertiesList::iterator it; for(it = m_LookupTableProperties.begin(); it != m_LookupTableProperties.end();++it) { if ( it->second.LookupTableSource != NULL ) { it->second.LookupTableSource->Delete(); it->second.LookupTableSource = NULL; } } m_DataStorage = NULL; } vtkProp* Geometry2DDataVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { if ( (this->GetDataNode() != NULL ) && (m_ImageAssembly != NULL) ) { // Do not transform the entire Prop3D assembly, but only the image part // here. The colored frame is transformed elsewhere (via m_EdgeTransformer), // since only vertices should be transformed there, not the poly data // itself, to avoid distortion for anisotropic datasets. m_ImageAssembly->SetUserTransform( this->GetDataNode()->GetVtkTransform() ); } return m_Prop3DAssembly; } void Geometry2DDataVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { m_ImageAssembly->SetUserTransform( this->GetDataNode()->GetVtkTransform(this->GetTimestep()) ); } const Geometry2DData* Geometry2DDataVtkMapper3D::GetInput() { return static_cast ( GetData() ); } void Geometry2DDataVtkMapper3D::SetDataStorageForTexture(mitk::DataStorage* storage) { if(storage != NULL && m_DataStorage != storage ) { m_DataStorage = storage; this->Modified(); } } void Geometry2DDataVtkMapper3D::ImageMapperDeletedCallback( itk::Object *caller, const itk::EventObject& /*event*/ ) { ImageMapperGL2D *imageMapper = dynamic_cast< ImageMapperGL2D * >( caller ); if ( (imageMapper != NULL) ) { if ( m_ImageActors.count( imageMapper ) > 0) { m_ImageActors[imageMapper].m_Sender = NULL; // sender is already destroying itself m_ImageActors.erase( imageMapper ); } if ( m_LookupTableProperties.count( imageMapper ) > 0 ) { m_LookupTableProperties[imageMapper].LookupTableSource->Delete(); m_LookupTableProperties.erase( imageMapper ); } } } - void Geometry2DDataVtkMapper3D::GenerateData(BaseRenderer* renderer) + void Geometry2DDataVtkMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) { SetVtkMapperImmediateModeRendering(m_EdgeMapper); SetVtkMapperImmediateModeRendering(m_BackgroundMapper); // Remove all actors from the assembly, and re-initialize it with the // edge actor m_ImageAssembly->GetParts()->RemoveAllItems(); if ( !this->IsVisible(renderer) ) { // visibility has explicitly to be set in the single actors // due to problems when using cell picking: // even if the assembly is invisible, the renderer contains // references to the assemblies parts. During picking the // visibility of each part is checked, and not only for the // whole assembly. m_ImageAssembly->VisibilityOff(); m_EdgeActor->VisibilityOff(); return; } // visibility has explicitly to be set in the single actors // due to problems when using cell picking: // even if the assembly is invisible, the renderer contains // references to the assemblies parts. During picking the // visibility of each part is checked, and not only for the // whole assembly. m_ImageAssembly->VisibilityOn(); m_EdgeActor->VisibilityOn(); Geometry2DData::Pointer input = const_cast< Geometry2DData * >(this->GetInput()); if (input.IsNotNull() && (input->GetGeometry2D() != NULL)) { SmartPointerProperty::Pointer surfacecreatorprop; surfacecreatorprop = dynamic_cast< SmartPointerProperty * >(GetDataNode()->GetProperty("surfacegeometry", renderer)); if ( (surfacecreatorprop.IsNull()) || (surfacecreatorprop->GetSmartPointer().IsNull()) || ((m_SurfaceCreator = dynamic_cast (surfacecreatorprop->GetSmartPointer().GetPointer())).IsNull() ) ) { m_SurfaceCreator->PlaceByGeometryOn(); surfacecreatorprop = SmartPointerProperty::New( m_SurfaceCreator ); GetDataNode()->SetProperty("surfacegeometry", surfacecreatorprop); } m_SurfaceCreator->SetInput(input); int res; if (GetDataNode()->GetIntProperty("xresolution", res, renderer)) { m_SurfaceCreator->SetXResolution(res); } if (GetDataNode()->GetIntProperty("yresolution", res, renderer)) { m_SurfaceCreator->SetYResolution(res); } double tubeRadius = 1.0; // Radius of tubular edge surrounding plane // Clip the Geometry2D with the reference geometry bounds (if available) if ( input->GetGeometry2D()->HasReferenceGeometry() ) { Geometry3D *referenceGeometry = input->GetGeometry2D()->GetReferenceGeometry(); BoundingBox::PointType boundingBoxMin, boundingBoxMax; boundingBoxMin = referenceGeometry->GetBoundingBox()->GetMinimum(); boundingBoxMax = referenceGeometry->GetBoundingBox()->GetMaximum(); if ( referenceGeometry->GetImageGeometry() ) { for ( unsigned int i = 0; i < 3; ++i ) { boundingBoxMin[i] -= 0.5; boundingBoxMax[i] -= 0.5; } } m_SurfaceCreatorPointsContainer->CreateElementAt( 0 ) = boundingBoxMin; m_SurfaceCreatorPointsContainer->CreateElementAt( 1 ) = boundingBoxMax; m_SurfaceCreatorBoundingBox->ComputeBoundingBox(); m_SurfaceCreator->SetBoundingBox( m_SurfaceCreatorBoundingBox ); tubeRadius = referenceGeometry->GetDiagonalLength() / 450.0; } // If no reference geometry is available, clip with the current global // bounds else if (m_DataStorage.IsNotNull()) { m_SurfaceCreator->SetBoundingBox(m_DataStorage->ComputeVisibleBoundingBox(NULL, "includeInBoundingBox")); tubeRadius = sqrt( m_SurfaceCreator->GetBoundingBox()->GetDiagonalLength2() ) / 450.0; } // Calculate the surface of the Geometry2D m_SurfaceCreator->Update(); Surface *surface = m_SurfaceCreator->GetOutput(); // Check if there's something to display, otherwise return if ( (surface->GetVtkPolyData() == 0 ) || (surface->GetVtkPolyData()->GetNumberOfCells() == 0) ) { m_ImageAssembly->VisibilityOff(); return; } // add a graphical representation of the surface normals if requested DataNode* node = this->GetDataNode(); bool displayNormals = false; bool colorTwoSides = false; bool invertNormals = false; node->GetBoolProperty("draw normals 3D", displayNormals, renderer); node->GetBoolProperty("color two sides", colorTwoSides, renderer); node->GetBoolProperty("invert normals", invertNormals, renderer); //if we want to draw the display normals or render two sides we have to get the colors if( displayNormals || colorTwoSides ) { //get colors float frontColor[3] = { 0.0, 0.0, 1.0 }; node->GetColor( frontColor, renderer, "front color" ); float backColor[3] = { 1.0, 0.0, 0.0 }; node->GetColor( backColor, renderer, "back color" ); if ( displayNormals ) { m_NormalsTransformer->SetInput( surface->GetVtkPolyData() ); m_NormalsTransformer->SetTransform(node->GetVtkTransform(this->GetTimestep()) ); m_FrontHedgeHog->SetInput( m_NormalsTransformer->GetOutput() ); m_FrontHedgeHog->SetVectorModeToUseNormal(); m_FrontHedgeHog->SetScaleFactor( invertNormals ? 1.0 : -1.0 ); m_FrontNormalsActor->GetProperty()->SetColor( frontColor[0], frontColor[1], frontColor[2] ); m_BackHedgeHog->SetInput( m_NormalsTransformer->GetOutput() ); m_BackHedgeHog->SetVectorModeToUseNormal(); m_BackHedgeHog->SetScaleFactor( invertNormals ? -1.0 : 1.0 ); m_BackNormalsActor->GetProperty()->SetColor( backColor[0], backColor[1], backColor[2] ); //if there is no actor added yet, add one if ( !m_NormalsActorAdded ) { m_Prop3DAssembly->AddPart( m_FrontNormalsActor ); m_Prop3DAssembly->AddPart( m_BackNormalsActor ); m_NormalsActorAdded = true; } } //if we don't want to display normals AND there is an actor added remove the actor else if ( m_NormalsActorAdded ) { m_Prop3DAssembly->RemovePart( m_FrontNormalsActor ); m_Prop3DAssembly->RemovePart( m_BackNormalsActor ); m_NormalsActorAdded = false; } if ( colorTwoSides ) { if ( !invertNormals ) { m_BackgroundActor->GetProperty()->SetColor( backColor[0], backColor[1], backColor[2] ); m_BackgroundActor->GetBackfaceProperty()->SetColor( frontColor[0], frontColor[1], frontColor[2] ); } else { m_BackgroundActor->GetProperty()->SetColor( frontColor[0], frontColor[1], frontColor[2] ); m_BackgroundActor->GetBackfaceProperty()->SetColor( backColor[0], backColor[1], backColor[2] ); } } } // Add black background for all images (which may be transparent) m_BackgroundMapper->SetInput( surface->GetVtkPolyData() ); m_ImageAssembly->AddPart( m_BackgroundActor ); LayerSortedActorList layerSortedActors; // Traverse the data tree to find nodes resliced by ImageMapperGL2D mitk::NodePredicateOr::Pointer p = mitk::NodePredicateOr::New(); //use a predicate to get all data nodes which are "images" or inherit from mitk::Image mitk::TNodePredicateDataType< mitk::Image >::Pointer predicateAllImages = mitk::TNodePredicateDataType< mitk::Image >::New(); mitk::DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetSubset(predicateAllImages); //process all found images for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode *node = it->Value(); if (node != NULL) this->ProcessNode(node, renderer, surface, layerSortedActors); } // Add all image actors to the assembly, sorted according to // layer property LayerSortedActorList::iterator actorIt; for ( actorIt = layerSortedActors.begin(); actorIt != layerSortedActors.end(); ++actorIt ) { m_ImageAssembly->AddPart( actorIt->second ); } // Configurate the tube-shaped frame: size according to the surface // bounds, color as specified in the plane's properties vtkPolyData *surfacePolyData = surface->GetVtkPolyData(); m_Cleaner->SetInput(surfacePolyData); m_EdgeTransformer->SetTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep()) ); // Adjust the radius according to extent m_EdgeTuber->SetRadius( tubeRadius ); // Get the plane's color and set the tube properties accordingly ColorProperty::Pointer colorProperty; colorProperty = dynamic_cast(this->GetDataNode()->GetProperty( "color" )); if ( colorProperty.IsNotNull() ) { const Color& color = colorProperty->GetColor(); m_EdgeActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue()); } else { m_EdgeActor->GetProperty()->SetColor( 1.0, 1.0, 1.0 ); } m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep()) ); } VtkRepresentationProperty* representationProperty; this->GetDataNode()->GetProperty(representationProperty, "material.representation", renderer); if ( representationProperty != NULL ) m_BackgroundActor->GetProperty()->SetRepresentation( representationProperty->GetVtkRepresentation() ); } void Geometry2DDataVtkMapper3D::ProcessNode( DataNode * node, BaseRenderer* renderer, Surface * surface, LayerSortedActorList &layerSortedActors ) { if ( node != NULL ) { //we need to get the information from the 2D mapper to render the texture on the 3D plane ImageMapperGL2D *imageMapper = dynamic_cast< ImageMapperGL2D * >( node->GetMapper(1) ); //GetMapper(1) provides the 2D mapper for the data node //if there is a 2D mapper, which is not the standard image mapper... if(!imageMapper && node->GetMapper(1)) { //... check if it is the composite mapper std::string cname(node->GetMapper(1)->GetNameOfClass()); if(!cname.compare("CompositeMapper")) //string.compare returns 0 if the two strings are equal. { //get the standard image mapper. //This is a special case in MITK and does only work for the CompositeMapper. imageMapper = dynamic_cast( node->GetMapper(3) ); } } if ( (node->IsVisible(renderer)) && imageMapper ) { WeakPointerProperty::Pointer rendererProp = dynamic_cast< WeakPointerProperty * >(GetDataNode()->GetPropertyList()->GetProperty("renderer")); if ( rendererProp.IsNotNull() ) { BaseRenderer::Pointer planeRenderer = dynamic_cast< BaseRenderer * >(rendererProp->GetWeakPointer().GetPointer()); if ( planeRenderer.IsNotNull() ) { // If it has not been initialized already in a previous pass, // generate an actor, a lookup table and a texture object to // render the image associated with the ImageMapperGL2D. vtkActor *imageActor; vtkDataSetMapper *dataSetMapper = NULL; vtkLookupTable *lookupTable; vtkTexture *texture; if ( m_ImageActors.count( imageMapper ) == 0 ) { dataSetMapper = vtkDataSetMapper::New(); //Enable rendering without copying the image. dataSetMapper->ImmediateModeRenderingOn(); lookupTable = vtkLookupTable::New(); lookupTable->DeepCopy( m_DefaultLookupTable ); texture = vtkTexture::New(); texture->SetLookupTable( lookupTable ); texture->RepeatOff(); imageActor = vtkActor::New(); imageActor->GetProperty()->SetAmbient( 0.5 ); imageActor->SetMapper( dataSetMapper ); imageActor->SetTexture( texture ); // Make imageActor the sole owner of the mapper and texture // objects lookupTable->UnRegister( NULL ); dataSetMapper->UnRegister( NULL ); texture->UnRegister( NULL ); // Store the actor so that it may be accessed in following // passes. m_ImageActors[imageMapper].Initialize(imageActor, imageMapper, m_ImageMapperDeletedCommand); } else { // Else, retrieve the actor and associated objects from the // previous pass. imageActor = m_ImageActors[imageMapper].m_Actor; dataSetMapper = (vtkDataSetMapper *)imageActor->GetMapper(); texture = imageActor->GetTexture(); //BUG (#1551) added dynamic cast for VTK5.2 support #if ( ( VTK_MAJOR_VERSION >= 5 ) && ( VTK_MINOR_VERSION>=2) ) lookupTable = dynamic_cast(texture->GetLookupTable()); #else lookupTable = texture->GetLookupTable(); #endif } // Set poly data new each time its object changes (e.g. when // switching between planar and curved geometries) if ( (dataSetMapper != NULL) && (dataSetMapper->GetInput() != surface->GetVtkPolyData()) ) { dataSetMapper->SetInput( surface->GetVtkPolyData() ); } imageActor->GetMapper()->GetInput()->Update(); imageActor->GetMapper()->Update(); // We have to do this before GenerateAllData() is called // since there may be no RendererInfo for renderer yet, // thus GenerateAllData won't update the (non-existing) // RendererInfo for renderer. By calling GetRendererInfo // a RendererInfo will be created for renderer (if it does not // exist yet). imageMapper->GetRendererInfo( planeRenderer ); imageMapper->GenerateAllData(); // ensure the right openGL context, as 3D widgets may render and take their plane texture from 2D image mappers renderer->GetRenderWindow()->MakeCurrent(); // Retrieve and update image to be mapped const ImageMapperGL2D::RendererInfo *rit = imageMapper->GetRendererInfo( planeRenderer ); if(rit->m_Image != NULL) { rit->m_Image->Update(); //set the 2D image as texture for the 3D plane texture->SetInput( rit->m_Image ); //default level window ScalarType windowMin = 0.0; ScalarType windowMax = 255.0; LevelWindow levelWindow; bool binary = false; node->GetBoolProperty( "binary", binary, renderer ); // check for "use color" bool useColor = false; node->GetBoolProperty( "use color", useColor, planeRenderer ); // VTK (mis-)interprets unsigned char (binary) images as color images; // So, we must manually turn on their mapping through a (gray scale) lookup table; texture->SetMapColorScalarsThroughLookupTable(binary); //if we have a binary image, the range is just 0 to 1 if( binary ) { windowMin = 0; windowMax = 1; useColor = true; } // check for level-window-prop and use it if it exists if( !binary && ( node->GetLevelWindow( levelWindow, planeRenderer, "levelWindow" ) || node->GetLevelWindow( levelWindow, planeRenderer ) ) ) { windowMin = levelWindow.GetLowerWindowBound(); windowMax = levelWindow.GetUpperWindowBound(); } vtkLookupTable *lookupTableSource; // check for LookupTable LookupTableProperty::Pointer lookupTableProp; lookupTableProp = dynamic_cast< LookupTableProperty * >(node->GetPropertyList()->GetProperty( "LookupTable" )); // If there is a lookup table supplied and we don't // want to use the color property, use it; //otherwise, use the default grayscale table if ( lookupTableProp.IsNotNull() && !useColor ) { lookupTableSource = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); } else { lookupTableSource = m_DefaultLookupTable; } LookupTableProperties &lutProperties = m_LookupTableProperties[imageMapper]; // If there has been some change since the last pass which // makes it necessary to re-build the lookup table, do it. if ( (lutProperties.LookupTableSource != lookupTableSource) || (lutProperties.windowMin != windowMin) || (lutProperties.windowMax != windowMax) ) { // Note the values for the next pass (lutProperties is a // reference to the list entry!) if ( lutProperties.LookupTableSource != NULL ) { lutProperties.LookupTableSource->Delete(); } lutProperties.LookupTableSource = lookupTableSource; lutProperties.LookupTableSource->Register( NULL ); lutProperties.windowMin = windowMin; lutProperties.windowMax = windowMax; lookupTable->DeepCopy( lookupTableSource ); lookupTable->SetRange( windowMin, windowMax ); } //get the color float rgb[3] = { 1.0, 1.0, 1.0 }; node->GetColor( rgb, renderer ); // Apply color property (of the node, not of the plane) // if we want to use the color if(useColor) { imageActor->GetProperty()->SetColor( rgb[0], rgb[1], rgb[2] ); } else //else default color = white to avoid site effects from the lookuptable { imageActor->GetProperty()->SetColor( 1, 1, 1 ); } // Apply opacity property (of the node, not of the plane) float opacity = 0.999; node->GetOpacity( opacity, renderer ); imageActor->GetProperty()->SetOpacity( opacity ); // Set texture interpolation on/off bool textureInterpolation = node->IsOn( "texture interpolation", renderer ); texture->SetInterpolate( textureInterpolation ); // Store this actor to be added to the actor assembly, sort // by layer int layer = 1; node->GetIntProperty( "layer", layer ); layerSortedActors.insert(std::pair< int, vtkActor * >( layer, imageActor ) ); } } } } } } void Geometry2DDataVtkMapper3D::ActorInfo::Initialize(vtkActor* actor, itk::Object* sender, itk::Command* command) { m_Actor = actor; m_Sender = sender; // Get informed when ImageMapper object is deleted, so that // the data structures built here can be deleted as well m_ObserverID = sender->AddObserver( itk::DeleteEvent(), command ); } Geometry2DDataVtkMapper3D::ActorInfo::ActorInfo() : m_Actor(NULL), m_Sender(NULL), m_ObserverID(0) { } Geometry2DDataVtkMapper3D::ActorInfo::~ActorInfo() { if(m_Sender != NULL) { m_Sender->RemoveObserver(m_ObserverID); } if(m_Actor != NULL) { m_Actor->Delete(); } } } // namespace mitk diff --git a/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h b/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h index a19efee794..fd05eb282a 100644 --- a/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h +++ b/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h @@ -1,234 +1,234 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITKGEOMETRY2DDATAVTKMAPPER3D_H_HEADER_INCLUDED_C196C71F #define MITKGEOMETRY2DDATAVTKMAPPER3D_H_HEADER_INCLUDED_C196C71F #include "mitkCommon.h" #include "mitkVtkMapper3D.h" #include "mitkDataStorage.h" #include "mitkGeometry2DDataToSurfaceFilter.h" #include "mitkWeakPointer.h" #include #include class vtkActor; class vtkPolyDataMapper; class vtkDataSetMapper; class vtkLookupTable; class vtkAssembly; class vtkFeatureEdges; class vtkTubeFilter; class vtkTransformPolyDataFilter; class vtkHedgeHog; namespace mitk { class Geometry2DData; class BaseRenderer; class ImageMapperGL2D; class DataStorage; /** * \brief Vtk-based mapper to display a Geometry2D in a 3D window * \ingroup Mapper * * Uses a Geometry2DDataToSurfaceFilter object to create a vtkPolyData representation of a given Geometry2D instance. * Geometry2D may either contain a common flat plane or a curved plane (ThinPlateSplineCurvedGeometry). * * The vtkPolyData object is then decorated by a colored tube on the edges and by image textures if possible * (currently this requires that there is a 2D render window rendering the same geometry as this mapper). * * Properties that influence rendering are: * * - \b "color": (ColorProperty) Color of the tubed frame. * - \b "xresolution": (FloatProperty) Resolution (=number of tiles) in x direction. Only relevant for ThinPlateSplineCurvedGeometry * - \b "yresolution": (FloatProperty) Resolution (=number of tiles) in y direction. Only relevant for ThinPlateSplineCurvedGeometry * - \b "draw normals 3D": (BoolProperty) If true, a vtkHedgeHog is used to display normals for the generated surface object. Useful to distinguish front and back of a plane. Hedgehogs are colored according to "front color" and "back color" * - \b "color two sides": (BoolProperty) If true, front and back side of the plane are colored differently ("front color" and "back color") * - \b "invert normals": (BoolProperty) Inverts front/back for display. * - \b "front color": (ColorProperty) Color for front side of the plane * - \b "back color": (ColorProperty) Color for back side of the plane * - \b "material.representation": (BoolProperty) Choose the representation to draw the mesh in (Surface, Wireframe, Point Cloud) * - \b "surfacegeometry": TODO: Add documentation * - \b "LookupTable": (LookupTableProperty) Set the lookuptable to render with. * * Note: The following properties are set for each image individually, and thus, also influence the rendering of this mapper: * * - \b "texture interpolation": (BoolProperty) Turn on/off the texture interpolation of each image * - \b "use color": (BoolProperty) Decide whether we want to use the color property or a lookuptable. * - \b "binary": (BoolProperty) Binary image handling: Color the value=1.0 with the color property and make the background (value=0.0) of the image translucent. * - \b "layer": (IntProperty) Controls what image is considered "on top" of another. In the case that two should inhabit the same space, higher layer occludes lower layer. * - \b "opacity": (FloatProperty) Set the opacity for each rendered image. * - \b "color": (FloatProperty) Set the color for each rendered image. * * The internal filter pipeline which combines a (sometimes deformed) 2D surface * with a nice frame and image textures is illustrated in the following sketch: * * \image html mitkGeometry2DDataVtkMapper3D.png "Internal filter pipeline" * */ class MITK_CORE_EXPORT Geometry2DDataVtkMapper3D : public VtkMapper3D { public: mitkClassMacro(Geometry2DDataVtkMapper3D, VtkMapper3D); itkNewMacro(Geometry2DDataVtkMapper3D); /** * Overloaded since the displayed color-frame of the image mustn't be * transformed after generation of poly data, but before (vertex coordinates * only) */ virtual vtkProp *GetVtkProp(mitk::BaseRenderer *renderer); virtual void UpdateVtkTransform(mitk::BaseRenderer *renderer); /** * \brief Get the Geometry2DData to map */ virtual const Geometry2DData *GetInput(); /** * \brief All images found when traversing the (sub-) tree starting at * \a iterator which are resliced by an ImageMapperGL2D will be mapped. * This method is used to set the data storage to traverse. This offers * the possibility to use this mapper for other data storages (not only * the default data storage). */ virtual void SetDataStorageForTexture(mitk::DataStorage* storage); protected: typedef std::multimap< int, vtkActor * > LayerSortedActorList; Geometry2DDataVtkMapper3D(); virtual ~Geometry2DDataVtkMapper3D(); - virtual void GenerateData(BaseRenderer* renderer); + virtual void GenerateDataForRenderer(BaseRenderer* renderer); void ProcessNode( DataNode * node, BaseRenderer* renderer, Surface * surface, LayerSortedActorList &layerSortedActors ); void ImageMapperDeletedCallback( itk::Object *caller, const itk::EventObject &event ); /** \brief general PropAssembly to hold the entire scene */ vtkAssembly *m_Prop3DAssembly; /** \brief PropAssembly to hold the planes */ vtkAssembly *m_ImageAssembly; Geometry2DDataToSurfaceFilter::Pointer m_SurfaceCreator; BoundingBox::Pointer m_SurfaceCreatorBoundingBox; BoundingBox::PointsContainer::Pointer m_SurfaceCreatorPointsContainer; /** \brief Edge extractor for tube-shaped frame */ vtkFeatureEdges *m_Edges; /** \brief Filter to apply object transform to the extracted edges */ vtkTransformPolyDataFilter *m_EdgeTransformer; /** \brief Source to create the tube-shaped frame */ vtkTubeFilter *m_EdgeTuber; /** \brief Mapper for the tube-shaped frame */ vtkPolyDataMapper *m_EdgeMapper; /** \brief Actor for the tube-shaped frame */ vtkActor *m_EdgeActor; /** \brief Mapper for black plane background */ vtkPolyDataMapper *m_BackgroundMapper; /** \brief Actor for black plane background */ vtkActor *m_BackgroundActor; /** \brief Transforms the suface before applying the glyph filter */ vtkTransformPolyDataFilter* m_NormalsTransformer; /** \brief Mapper for normals representation (thin lines) */ vtkPolyDataMapper* m_FrontNormalsMapper; vtkPolyDataMapper* m_BackNormalsMapper; /** \brief Generates lines for surface normals */ vtkHedgeHog* m_FrontHedgeHog; vtkHedgeHog* m_BackHedgeHog; /** \brief Actor to hold the normals arrows */ vtkActor* m_FrontNormalsActor; vtkActor* m_BackNormalsActor; /** Cleans the polyline in order to avoid phantom boundaries */ vtkCleanPolyData *m_Cleaner; /** Internal flag, if actors for normals are already added to m_Prop3DAssembly*/ bool m_NormalsActorAdded; /** \brief The DataStorage defines which part of the data tree is traversed for renderering. */ mitk::WeakPointer m_DataStorage; /** A default grayscale lookup-table, used for reference */ vtkLookupTable *m_DefaultLookupTable; class MITK_CORE_EXPORT ActorInfo { public: vtkActor * m_Actor; // we do not need a smart-pointer, because we delete our // connection, when the referenced mapper is destroyed itk::Object* m_Sender; unsigned long m_ObserverID; void Initialize(vtkActor* actor, itk::Object* sender, itk::Command* command); ActorInfo(); ~ActorInfo(); }; /** \brief List holding the vtkActor to map the image into 3D for each * ImageMapper */ typedef std::map< ImageMapperGL2D *, ActorInfo > ActorList; ActorList m_ImageActors; struct LookupTableProperties { LookupTableProperties() : LookupTableSource( NULL ), windowMin( 0.0 ), windowMax( 4096.0 ) {} vtkLookupTable *LookupTableSource; vtkFloatingPointType windowMin; vtkFloatingPointType windowMax; }; typedef std::map< ImageMapperGL2D *, LookupTableProperties > LookupTablePropertiesList; /** \brief List holding some lookup table properties of the previous pass */ LookupTablePropertiesList m_LookupTableProperties; // responsiblity to remove the observer upon its destruction typedef itk::MemberCommand< Geometry2DDataVtkMapper3D > MemberCommandType; MemberCommandType::Pointer m_ImageMapperDeletedCommand; }; } // namespace mitk #endif /* MITKGEOMETRY2DDATAVTKMAPPER3D_H_HEADER_INCLUDED_C196C71F */ diff --git a/Core/Code/Rendering/mitkImageMapperGL2D.cpp b/Core/Code/Rendering/mitkImageMapperGL2D.cpp index 8bc256e8c6..0425dae128 100644 --- a/Core/Code/Rendering/mitkImageMapperGL2D.cpp +++ b/Core/Code/Rendering/mitkImageMapperGL2D.cpp @@ -1,1392 +1,1392 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkImageMapperGL2D.h" #include "widget.h" #include "picimage.h" #include "pic2vtk.h" #include "mitkTimeSlicedGeometry.h" #include "mitkPlaneGeometry.h" #include "mitkDataNode.h" #include "mitkVtkPropRenderer.h" #include "mitkLookupTableProperty.h" #include "mitkProperties.h" #include "mitkLevelWindowProperty.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkVolumeCalculator.h" #include "mitkImageSliceSelector.h" #include "mitkAbstractTransformGeometry.h" #include "mitkDataNodeFactory.h" #include "mitkResliceMethodProperty.h" #include #include #include #include #include #include #include #include #include #include #include "vtkMitkThickSlicesFilter.h" #include "itkRGBAPixel.h" int mitk::ImageMapperGL2D::numRenderer = 0; mitk::ImageMapperGL2D::ImageMapperGL2D() { } mitk::ImageMapperGL2D::~ImageMapperGL2D() { this->Clear(); this->InvokeEvent( itk::DeleteEvent() ); } void mitk::ImageMapperGL2D::Paint( mitk::BaseRenderer *renderer ) { if ( !this->IsVisible( renderer ) ) { return; } this->Update( renderer ); RendererInfo &rendererInfo = this->AccessRendererInfo( renderer ); iil4mitkPicImage *image = rendererInfo.Get_iil4mitkImage(); if ( ( image == NULL ) || ( image->image() == NULL ) ) { return; } const mitk::DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry(); Vector2D topLeft = displayGeometry->GetOriginInMM(); Vector2D bottomRight = topLeft + displayGeometry->GetSizeInMM(); topLeft[0] *= rendererInfo.m_PixelsPerMM[0]; topLeft[1] *= rendererInfo.m_PixelsPerMM[1]; bottomRight[0] *= rendererInfo.m_PixelsPerMM[0]; bottomRight[1] *= rendererInfo.m_PixelsPerMM[1]; topLeft += rendererInfo.m_Overlap; bottomRight += rendererInfo.m_Overlap; Vector2D diag = ( topLeft - bottomRight ); //float size = diag.GetNorm(); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( topLeft[0], bottomRight[0], topLeft[1], bottomRight[1], 0.0, 1.0 ); glMatrixMode( GL_MODELVIEW ); glDepthMask(GL_FALSE); // Define clipping planes to clip the image according to the bounds // correlating to the current world geometry. The "extent" of the bounds // needs to be enlarged by an "overlap" factor, in order to make the // remaining area are large enough to cover rotated planes also. // // Note that this can be improved on, by not merely using a large enough // rectangle for clipping, but using the side surfaces of the transformed // 3D bounds as clipping planes instead. This would clip even rotates // planes at their exact intersection lines with the 3D bounding box. //GLdouble eqn0[4] = { 1.0, 0.0, 0.0, 0.0 }; //GLdouble eqn1[4] = { -1.0, 0.0, 0.0, rendererInfo.m_Extent[0] // + 2.0 * rendererInfo.m_Overlap[0]/* - rendererInfo.m_PixelsPerMM[0]*/ }; //MITK_INFO << "X: " << rendererInfo.m_Extent[0] // + 2.0 * rendererInfo.m_Overlap[0] - rendererInfo.m_PixelsPerMM[0] << std::endl; //GLdouble eqn2[4] = { 0.0, 1.0, 0.0, 0.0 }; //GLdouble eqn3[4] = { 0.0, -1.0, 0.0, rendererInfo.m_Extent[1] // + 2.0 * rendererInfo.m_Overlap[1]/* - rendererInfo.m_PixelsPerMM[1]*/ }; //MITK_INFO << "Y:" << rendererInfo.m_Extent[1] // + 2.0 * rendererInfo.m_Overlap[1] - rendererInfo.m_PixelsPerMM[1] << std::endl; // IW commented out the previous lines and reverted to rev. 9358 // (version before rev. 9443) See bug #625 GLdouble eqn0[4] = {0.0, 1.0, 0.0, 0.0}; GLdouble eqn1[4] = {1.0, 0.0, 0.0, 0.0}; GLdouble eqn2[4] = {-1.0, 0.0 , 0.0, image->width()}; GLdouble eqn3[4] = {0, -1.0, 0.0, image->height() }; glClipPlane( GL_CLIP_PLANE0, eqn0 ); glEnable( GL_CLIP_PLANE0 ); glClipPlane( GL_CLIP_PLANE1, eqn1 ); glEnable( GL_CLIP_PLANE1 ); glClipPlane( GL_CLIP_PLANE2, eqn2 ); glEnable( GL_CLIP_PLANE2 ); glClipPlane( GL_CLIP_PLANE3, eqn3 ); glEnable( GL_CLIP_PLANE3 ); // Render the image image->setInterpolation( rendererInfo.m_TextureInterpolation ); image->display( renderer->GetRenderWindow() ); // Disable the utilized clipping planes glDisable( GL_CLIP_PLANE0 ); glDisable( GL_CLIP_PLANE1 ); glDisable( GL_CLIP_PLANE2 ); glDisable( GL_CLIP_PLANE3 ); // display volume property, if it exists and should be displayed bool shouldShowVolume = false, binary = false; float segmentationVolume = -1.0; mitk::DataNode *node = this->GetDataNode(); mitk::Image* mitkimage = dynamic_cast(node->GetData()); // Check if a volume in ml can be drawn in the image. // This is the case if: // 1. The property "showVolume" is true AND [ // 2.1 The image has a volume stored as property (3D case) OR // 2.2 The image is 3D or 4D and binary, so the volume can be calculated ] if ( (node->GetBoolProperty("showVolume", shouldShowVolume)) && (shouldShowVolume) && ( (node->GetFloatProperty("volume", segmentationVolume) ) || (mitkimage != NULL && mitkimage->GetDimension() >= 3 && node->GetBoolProperty("binary", binary) && binary) ) ) { // calculate screen position for text by searching for the object border mitkIpPicDescriptor* pic = image->image(); // search object border in current slice unsigned int s_x = 0; unsigned int s_y = 0; unsigned int s_n = 0; for(unsigned int y=0;yn[1];y++) for(unsigned int x=0;xn[0];x++) { bool set=false; switch ( pic->bpe ) { case 8: { mitkIpInt1_t *current = static_cast< mitkIpInt1_t *>( pic->data ); current += y*pic->n[0] + x; if(current[0]) set=true; break; } case 16: { mitkIpInt2_t *current = static_cast< mitkIpInt2_t *>( pic->data ); current += y*pic->n[0] + x; if(current[0]) set=true; break; } case 24: { mitkIpInt1_t *current = static_cast< mitkIpInt1_t *>( pic->data ); current += ( y*pic->n[0] + x )*3; if(current[0]||current[1]||current[2]) set=true; break; } } if(set) { if ( x > s_x ) s_x = x; if ( y > s_y ) s_y = y; s_n++; } } // if an object has been found, draw annotation if ( s_n>0 ) { // make sure a segmentation volume is present if( segmentationVolume <= 0 ) { // if not, check if the image is truly binary if( mitkimage->GetScalarValueMax( renderer->GetTimeStep() ) == 1 ) { // if yes, get the volume from image statistics segmentationVolume = mitk::VolumeCalculator::ComputeVolume( mitkimage->GetSlicedGeometry()->GetSpacing(), mitkimage->GetCountOfMaxValuedVoxelsNoRecompute(renderer->GetTimeStep())); } } // create text std::stringstream volumeString; volumeString << std::fixed << std::setprecision(1) << segmentationVolume; std::string unit; if (node->GetStringProperty("volume annotation unit", unit)) { volumeString << " " << unit; } else { volumeString << " ml"; } // draw text mitk::VtkPropRenderer* OpenGLrenderer = dynamic_cast( renderer ); //calc index pos Point2D pt2D; pt2D[0] = s_x; pt2D[1] = s_y; //calc index pos with spacing const Geometry2D *worldGeometry = renderer->GetCurrentWorldGeometry2D(); //calc display coord worldGeometry->IndexToWorld( pt2D, pt2D ); displayGeometry->WorldToDisplay( pt2D, pt2D ); mitk::ColorProperty::Pointer annotationColorProp; mitk::Color annotationColor; annotationColor.Set(0,1,0); if (node->GetProperty(annotationColorProp, "volume annotation color")) { annotationColor = annotationColorProp->GetColor(); } bool hover = false; bool selected = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); if(hover) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("binaryimage.hoveringcolor", renderer)); if(colorprop.IsNotNull()) annotationColor = colorprop->GetColor(); } if(selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("binaryimage.selectedcolor", renderer)); if(colorprop.IsNotNull()) annotationColor = colorprop->GetColor(); } OpenGLrenderer->WriteSimpleText(volumeString.str(), pt2D[0]+1, pt2D[1]-1,0,0,0); //this is a shadow OpenGLrenderer->WriteSimpleText(volumeString.str(), pt2D[0] , pt2D[1] ,annotationColor.GetRed() ,annotationColor.GetGreen() ,annotationColor.GetBlue()); } } //glPushMatrix(); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( 0.0, displayGeometry->GetDisplayWidth(), 0.0, displayGeometry->GetDisplayHeight(), 0.0, 1.0 ); glDepthMask(GL_TRUE); //glMatrixMode( GL_MODELVIEW ); //glPopMatrix(); } const mitk::ImageMapperGL2D::InputImageType * mitk::ImageMapperGL2D::GetInput( void ) { return static_cast< const mitk::ImageMapperGL2D::InputImageType * >( this->GetData() ); } int mitk::ImageMapperGL2D::GetAssociatedChannelNr( mitk::BaseRenderer *renderer ) { RendererInfo &rendererInfo = this->AccessRendererInfo( renderer ); return rendererInfo.GetRendererID(); } void -mitk::ImageMapperGL2D::GenerateData( mitk::BaseRenderer *renderer ) +mitk::ImageMapperGL2D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { mitk::Image *input = const_cast< mitk::ImageMapperGL2D::InputImageType * >( this->GetInput() ); input->Update(); if ( input == NULL ) { return; } RendererInfo &rendererInfo = this->AccessRendererInfo( renderer ); rendererInfo.Squeeze(); iil4mitkPicImage *image = new iil4mitkPicImage( 512 ); rendererInfo.Set_iil4mitkImage( image ); this->ApplyProperties( renderer ); const Geometry2D *worldGeometry = renderer->GetCurrentWorldGeometry2D(); if( ( worldGeometry == NULL ) || ( !worldGeometry->IsValid() ) || ( !worldGeometry->GetReferenceGeometry() )) { return; } // check if there is something to display. if ( ! input->IsVolumeSet( this->GetTimestep() ) ) return; Image::RegionType requestedRegion = input->GetLargestPossibleRegion(); requestedRegion.SetIndex( 3, this->GetTimestep() ); requestedRegion.SetSize( 3, 1 ); requestedRegion.SetSize( 4, 1 ); input->SetRequestedRegion( &requestedRegion ); input->Update(); vtkImageData* inputData = input->GetVtkImageData( this->GetTimestep() ); if ( inputData == NULL ) { return; } vtkFloatingPointType spacing[3]; inputData->GetSpacing( spacing ); // how big the area is in physical coordinates: widthInMM x heightInMM pixels mitk::ScalarType widthInMM, heightInMM; // where we want to sample Point3D origin; Vector3D right, bottom, normal; Vector3D rightInIndex, bottomInIndex; // take transform of input image into account const TimeSlicedGeometry *inputTimeGeometry = input->GetTimeSlicedGeometry(); const Geometry3D* inputGeometry = inputTimeGeometry->GetGeometry3D( this->GetTimestep() ); ScalarType mmPerPixel[2]; // Bounds information for reslicing (only reuqired if reference geometry // is present) vtkFloatingPointType bounds[6]; bool boundsInitialized = false; for ( int i = 0; i < 6; ++i ) { bounds[i] = 0.0; } // Do we have a simple PlaneGeometry? if ( dynamic_cast< const PlaneGeometry * >( worldGeometry ) != NULL ) { const PlaneGeometry *planeGeometry = static_cast< const PlaneGeometry * >( worldGeometry ); origin = planeGeometry->GetOrigin(); right = planeGeometry->GetAxisVector( 0 ); bottom = planeGeometry->GetAxisVector( 1 ); normal = planeGeometry->GetNormal(); bool inPlaneResampleExtentByGeometry = false; GetDataNode()->GetBoolProperty( "in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer ); if ( inPlaneResampleExtentByGeometry ) { // Resampling grid corresponds to the current world geometry. This // means that the spacing of the output 2D image depends on the // currently selected world geometry, and *not* on the image itself. rendererInfo.m_Extent[0] = worldGeometry->GetExtent( 0 ); rendererInfo.m_Extent[1] = worldGeometry->GetExtent( 1 ); } else { // Resampling grid corresponds to the input geometry. This means that // the spacing of the output 2D image is directly derived from the // associated input image, regardless of the currently selected world // geometry. inputGeometry->WorldToIndex( right, rightInIndex ); inputGeometry->WorldToIndex( bottom, bottomInIndex ); rendererInfo.m_Extent[0] = rightInIndex.GetNorm(); rendererInfo.m_Extent[1] = bottomInIndex.GetNorm(); } // Get the extent of the current world geometry and calculate resampling // spacing therefrom. widthInMM = worldGeometry->GetExtentInMM( 0 ); heightInMM = worldGeometry->GetExtentInMM( 1 ); mmPerPixel[0] = widthInMM / rendererInfo.m_Extent[0]; mmPerPixel[1] = heightInMM / rendererInfo.m_Extent[1]; right.Normalize(); bottom.Normalize(); normal.Normalize(); origin += right * ( mmPerPixel[0] * 0.5 ); origin += bottom * ( mmPerPixel[1] * 0.5 ); widthInMM -= mmPerPixel[0]; heightInMM -= mmPerPixel[1]; // Use inverse transform of the input geometry for reslicing the 3D image rendererInfo.m_Reslicer->SetResliceTransform( inputGeometry->GetVtkTransform()->GetLinearInverse() ); // Set background level to TRANSLUCENT (see Geometry2DDataVtkMapper3D) rendererInfo.m_Reslicer->SetBackgroundLevel( -32768 ); // If a reference geometry does exist (as would usually be the case for // PlaneGeometry), store it in rendererInfo so that we have access to it // in Paint. // // Note: this is currently not strictly required, but could facilitate // correct plane clipping. if ( worldGeometry->GetReferenceGeometry() ) { rendererInfo.m_ReferenceGeometry = worldGeometry->GetReferenceGeometry(); // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. boundsInitialized = this->CalculateClippedPlaneBounds( rendererInfo.m_ReferenceGeometry, planeGeometry, bounds ); } } // Do we have an AbstractTransformGeometry? else if ( dynamic_cast< const AbstractTransformGeometry * >( worldGeometry ) ) { const mitk::AbstractTransformGeometry* abstractGeometry = dynamic_cast< const AbstractTransformGeometry * >(worldGeometry); rendererInfo.m_Extent[0] = abstractGeometry->GetParametricExtent(0); rendererInfo.m_Extent[1] = abstractGeometry->GetParametricExtent(1); widthInMM = abstractGeometry->GetParametricExtentInMM(0); heightInMM = abstractGeometry->GetParametricExtentInMM(1); mmPerPixel[0] = widthInMM / rendererInfo.m_Extent[0]; mmPerPixel[1] = heightInMM / rendererInfo.m_Extent[1]; origin = abstractGeometry->GetPlane()->GetOrigin(); right = abstractGeometry->GetPlane()->GetAxisVector(0); right.Normalize(); bottom = abstractGeometry->GetPlane()->GetAxisVector(1); bottom.Normalize(); normal = abstractGeometry->GetPlane()->GetNormal(); normal.Normalize(); // Use a combination of the InputGeometry *and* the possible non-rigid // AbstractTransformGeometry for reslicing the 3D Image vtkGeneralTransform *composedResliceTransform = vtkGeneralTransform::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate( inputGeometry->GetVtkTransform()->GetLinearInverse() ); composedResliceTransform->Concatenate( abstractGeometry->GetVtkAbstractTransform() ); rendererInfo.m_Reslicer->SetResliceTransform( composedResliceTransform ); composedResliceTransform->UnRegister( NULL ); // decrease RC // Set background level to BLACK instead of translucent, to avoid // boundary artifacts (see Geometry2DDataVtkMapper3D) rendererInfo.m_Reslicer->SetBackgroundLevel( -1023 ); } else { return; } // Make sure that the image to display has a certain minimum size. if ( (rendererInfo.m_Extent[0] <= 2) && (rendererInfo.m_Extent[1] <= 2) ) { return; } // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ( (input->GetDimension() >= 3) && (input->GetDimension(2) > 1) ) { VtkResliceInterpolationProperty *resliceInterpolationProperty; this->GetDataNode()->GetProperty( resliceInterpolationProperty, "reslice interpolation" ); int interpolationMode = VTK_RESLICE_NEAREST; if ( resliceInterpolationProperty != NULL ) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch ( interpolationMode ) { case VTK_RESLICE_NEAREST: rendererInfo.m_Reslicer->SetInterpolationModeToNearestNeighbor(); break; case VTK_RESLICE_LINEAR: rendererInfo.m_Reslicer->SetInterpolationModeToLinear(); break; case VTK_RESLICE_CUBIC: rendererInfo.m_Reslicer->SetInterpolationModeToCubic(); break; } } else { rendererInfo.m_Reslicer->SetInterpolationModeToNearestNeighbor(); } int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if( inputData->GetNumberOfScalarComponents() == 1 ) // for now only single component are allowed { DataNode *dn=renderer->GetCurrentWorldGeometry2DNode(); if(dn) { ResliceMethodProperty *resliceMethodEnumProperty=0; if( dn->GetProperty( resliceMethodEnumProperty, "reslice.thickslices" ) && resliceMethodEnumProperty ) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty=0; if( dn->GetProperty( intProperty, "reslice.thickslices.num" ) && intProperty ) { thickSlicesNum = intProperty->GetValue(); if(thickSlicesNum < 1) thickSlicesNum=1; if(thickSlicesNum > 100) thickSlicesNum=100; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } rendererInfo.m_UnitSpacingImageFilter->SetInput( inputData ); rendererInfo.m_Reslicer->SetInput( rendererInfo.m_UnitSpacingImageFilter->GetOutput() ); //rendererInfo.m_Reslicer->SetOutputOrigin( 0.0, 0.0, 0.0 ); //rendererInfo.m_Reslicer->SetOutputDimensionality( 3 ); rendererInfo.m_PixelsPerMM[0] = 1.0 / mmPerPixel[0]; rendererInfo.m_PixelsPerMM[1] = 1.0 / mmPerPixel[1]; //calulate the originArray and the orientations for the reslice-filter double originArray[3]; itk2vtk( origin, originArray ); rendererInfo.m_Reslicer->SetResliceAxesOrigin( originArray ); double cosines[9]; // direction of the X-axis of the sampled result vnl2vtk( right.Get_vnl_vector(), cosines ); // direction of the Y-axis of the sampled result vnl2vtk( bottom.Get_vnl_vector(), cosines + 3 ); // normal of the plane vnl2vtk( normal.Get_vnl_vector(), cosines + 6 ); rendererInfo.m_Reslicer->SetResliceAxesDirectionCosines( cosines ); int xMin, xMax, yMin, yMax; if ( boundsInitialized ) { // Calculate output extent (integer values) xMin = static_cast< int >( bounds[0] / mmPerPixel[0] + 0.5 ); xMax = static_cast< int >( bounds[1] / mmPerPixel[0] + 0.5 ); yMin = static_cast< int >( bounds[2] / mmPerPixel[1] + 0.5 ); yMax = static_cast< int >( bounds[3] / mmPerPixel[1] + 0.5 ); // Calculate the extent by which the maximal plane (due to plane rotation) // overlaps the regular plane size. rendererInfo.m_Overlap[0] = -xMin; rendererInfo.m_Overlap[1] = -yMin; } else { // If no reference geometry is available, we also don't know about the // maximum plane size; so the overlap is just ignored rendererInfo.m_Overlap.Fill( 0.0 ); xMin = yMin = 0; xMax = static_cast< int >( rendererInfo.m_Extent[0] - rendererInfo.m_PixelsPerMM[0] + 0.5 ); yMax = static_cast< int >( rendererInfo.m_Extent[1] - rendererInfo.m_PixelsPerMM[1] + 0.5 ); } // Disallow huge dimensions if ( (xMax-xMin) * (yMax-yMin) > 4096*4096 ) { return; } // Calculate dataset spacing in plane z direction (NOT spacing of current // world geometry) double dataZSpacing = 1.0; normal.Normalize(); Vector3D normInIndex; inputGeometry->WorldToIndex( normal, normInIndex ); if(thickSlicesMode > 0) { dataZSpacing = 1.0 / normInIndex.GetNorm(); rendererInfo.m_Reslicer->SetOutputDimensionality( 3 ); rendererInfo.m_Reslicer->SetOutputExtent( xMin, xMax-1, yMin, yMax-1, -thickSlicesNum, 0+thickSlicesNum ); } else { rendererInfo.m_Reslicer->SetOutputDimensionality( 2 ); rendererInfo.m_Reslicer->SetOutputExtent( xMin, xMax-1, yMin, yMax-1, 0, 0 ); } rendererInfo.m_Reslicer->SetOutputOrigin( 0.0, 0.0, 0.0 ); rendererInfo.m_Reslicer->SetOutputSpacing( mmPerPixel[0], mmPerPixel[1], dataZSpacing ); // xMax and yMax are meant exclusive until now, whereas // SetOutputExtent wants an inclusive bound. Thus, we need // to subtract 1. // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. // The reslicing result is used both for 2D and for 3D mapping. 2D mapping // currently uses PIC data structures, while 3D mapping uses VTK data. Thus, // the reslicing result needs to be stored twice. // 1. Check the result vtkImageData* reslicedImage = 0; if(thickSlicesMode>0) { rendererInfo.m_TSFilter->SetThickSliceMode( thickSlicesMode-1 ); rendererInfo.m_TSFilter->SetInput( rendererInfo.m_Reslicer->GetOutput() ); rendererInfo.m_TSFilter->Modified(); rendererInfo.m_TSFilter->Update(); reslicedImage = rendererInfo.m_TSFilter->GetOutput(); } else { rendererInfo.m_Reslicer->Modified(); rendererInfo.m_Reslicer->Update(); reslicedImage = rendererInfo.m_Reslicer->GetOutput(); } if((reslicedImage == NULL) || (reslicedImage->GetDataDimension() < 1)) { MITK_WARN << "reslicer returned empty image"; return; } // 2. Convert the resampling result to PIC image format mitkIpPicDescriptor *pic = Pic2vtk::convert( reslicedImage ); if (pic == NULL) { return; } bool imageIs2D = true; if ( pic->dim == 1 ) { pic->dim = 2; pic->n[1] = 1; imageIs2D = false; } assert( pic->dim == 2 ); rendererInfo.m_Pic = pic; if ( pic->bpe == 24 && reslicedImage->GetScalarType()==VTK_UNSIGNED_CHAR ) // RGB image m_iil4mitkMode = iil4mitkImage::RGB; else if ( pic->bpe == 32 && reslicedImage->GetScalarType()==VTK_UNSIGNED_CHAR ) // RGBA image m_iil4mitkMode = iil4mitkImage::RGBA; image->setImage( pic, m_iil4mitkMode ); image->setInterpolation( false ); image->setRegion( 0, 0, pic->n[0], pic->n[1] ); // 3. Store the result in a VTK image if ( imageIs2D ) { if ( rendererInfo.m_Image == NULL ) { rendererInfo.m_Image = vtkImageData::New();//reslicedImage; } rendererInfo.m_Image->DeepCopy( reslicedImage ); rendererInfo.m_Image->Update(); } else { if ( rendererInfo.m_Image != NULL ) { rendererInfo.m_Image->Delete(); } rendererInfo.m_Image = NULL; } // We have been modified rendererInfo.m_LastUpdateTime.Modified(); } double mitk::ImageMapperGL2D::CalculateSpacing( const mitk::Geometry3D *geometry, const mitk::Vector3D &d ) const { // The following can be derived from the ellipsoid equation // // 1 = x^2/a^2 + y^2/b^2 + z^2/c^2 // // where (a,b,c) = spacing of original volume (ellipsoid radii) // and (x,y,z) = scaled coordinates of vector d (according to ellipsoid) // const mitk::Vector3D &spacing = geometry->GetSpacing(); double scaling = d[0]*d[0] / (spacing[0] * spacing[0]) + d[1]*d[1] / (spacing[1] * spacing[1]) + d[2]*d[2] / (spacing[2] * spacing[2]); scaling = sqrt( scaling ); return ( sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] ) / scaling ); } bool mitk::ImageMapperGL2D ::LineIntersectZero( vtkPoints *points, int p1, int p2, vtkFloatingPointType *bounds ) { vtkFloatingPointType point1[3]; vtkFloatingPointType point2[3]; points->GetPoint( p1, point1 ); points->GetPoint( p2, point2 ); if ( (point1[2] * point2[2] <= 0.0) && (point1[2] != point2[2]) ) { double x, y; x = ( point1[0] * point2[2] - point1[2] * point2[0] ) / ( point2[2] - point1[2] ); y = ( point1[1] * point2[2] - point1[2] * point2[1] ) / ( point2[2] - point1[2] ); if ( x < bounds[0] ) { bounds[0] = x; } if ( x > bounds[1] ) { bounds[1] = x; } if ( y < bounds[2] ) { bounds[2] = y; } if ( y > bounds[3] ) { bounds[3] = y; } bounds[4] = bounds[5] = 0.0; return true; } return false; } bool mitk::ImageMapperGL2D ::CalculateClippedPlaneBounds( const Geometry3D *boundingGeometry, const PlaneGeometry *planeGeometry, vtkFloatingPointType *bounds ) { // Clip the plane with the bounding geometry. To do so, the corner points // of the bounding box are transformed by the inverse transformation // matrix, and the transformed bounding box edges derived therefrom are // clipped with the plane z=0. The resulting min/max values are taken as // bounds for the image reslicer. const mitk::BoundingBox *boundingBox = boundingGeometry->GetBoundingBox(); mitk::BoundingBox::PointType bbMin = boundingBox->GetMinimum(); mitk::BoundingBox::PointType bbMax = boundingBox->GetMaximum(); mitk::BoundingBox::PointType bbCenter = boundingBox->GetCenter(); vtkPoints *points = vtkPoints::New(); if(boundingGeometry->GetImageGeometry()) { points->InsertPoint( 0, bbMin[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 1, bbMin[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 2, bbMin[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 3, bbMin[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 4, bbMax[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 5, bbMax[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 6, bbMax[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 7, bbMax[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); } else { points->InsertPoint( 0, bbMin[0], bbMin[1], bbMin[2] ); points->InsertPoint( 1, bbMin[0], bbMin[1], bbMax[2] ); points->InsertPoint( 2, bbMin[0], bbMax[1], bbMax[2] ); points->InsertPoint( 3, bbMin[0], bbMax[1], bbMin[2] ); points->InsertPoint( 4, bbMax[0], bbMin[1], bbMin[2] ); points->InsertPoint( 5, bbMax[0], bbMin[1], bbMax[2] ); points->InsertPoint( 6, bbMax[0], bbMax[1], bbMax[2] ); points->InsertPoint( 7, bbMax[0], bbMax[1], bbMin[2] ); } vtkPoints *newPoints = vtkPoints::New(); vtkTransform *transform = vtkTransform::New(); transform->Identity(); transform->Concatenate( planeGeometry->GetVtkTransform()->GetLinearInverse() ); transform->Concatenate( boundingGeometry->GetVtkTransform() ); transform->TransformPoints( points, newPoints ); bounds[0] = bounds[2] = 10000000.0; bounds[1] = bounds[3] = -10000000.0; bounds[4] = bounds[5] = 0.0; this->LineIntersectZero( newPoints, 0, 1, bounds ); this->LineIntersectZero( newPoints, 1, 2, bounds ); this->LineIntersectZero( newPoints, 2, 3, bounds ); this->LineIntersectZero( newPoints, 3, 0, bounds ); this->LineIntersectZero( newPoints, 0, 4, bounds ); this->LineIntersectZero( newPoints, 1, 5, bounds ); this->LineIntersectZero( newPoints, 2, 6, bounds ); this->LineIntersectZero( newPoints, 3, 7, bounds ); this->LineIntersectZero( newPoints, 4, 5, bounds ); this->LineIntersectZero( newPoints, 5, 6, bounds ); this->LineIntersectZero( newPoints, 6, 7, bounds ); this->LineIntersectZero( newPoints, 7, 4, bounds ); // clean up vtk data points->Delete(); newPoints->Delete(); transform->Delete(); if ( (bounds[0] > 9999999.0) || (bounds[2] > 9999999.0) || (bounds[1] < -9999999.0) || (bounds[3] < -9999999.0) ) { return false; } else { // The resulting bounds must be adjusted by the plane spacing, since we // we have so far dealt with index coordinates const float *planeSpacing = planeGeometry->GetFloatSpacing(); bounds[0] *= planeSpacing[0]; bounds[1] *= planeSpacing[0]; bounds[2] *= planeSpacing[1]; bounds[3] *= planeSpacing[1]; bounds[4] *= planeSpacing[2]; bounds[5] *= planeSpacing[2]; return true; } } void mitk::ImageMapperGL2D::GenerateAllData() { RendererInfoMap::iterator it, end = m_RendererInfo.end(); for ( it = m_RendererInfo.begin(); it != end; ++it) { this->Update( it->first ); } } void mitk::ImageMapperGL2D::Clear() { RendererInfoMap::iterator it, end = m_RendererInfo.end(); for ( it = m_RendererInfo.begin(); it != end; ++it ) { it->second.RemoveObserver(); it->second.Squeeze(); } m_RendererInfo.clear(); } void mitk::ImageMapperGL2D::ApplyProperties(mitk::BaseRenderer* renderer) { RendererInfo &rendererInfo = this->AccessRendererInfo( renderer ); iil4mitkPicImage *image = rendererInfo.Get_iil4mitkImage(); assert( image != NULL ); float rgba[4]= { 1.0f, 1.0f, 1.0f, 1.0f }; float opacity = 1.0f; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection bool hover = false; bool selected = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); if(hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("binaryimage.hoveringcolor", renderer)); if(colorprop.IsNotNull()) memcpy(rgba, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); else GetColor( rgba, renderer ); } if(selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("binaryimage.selectedcolor", renderer)); if(colorprop.IsNotNull()) memcpy(rgba, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); else GetColor( rgba, renderer ); } if(!hover && !selected) { GetColor( rgba, renderer ); } // check for opacity prop and use it for rendering if it exists GetOpacity( opacity, renderer ); rgba[3] = opacity; // check for interpolation properties bool textureInterpolation = false; GetDataNode()->GetBoolProperty( "texture interpolation", textureInterpolation, renderer ); rendererInfo.m_TextureInterpolation = textureInterpolation; mitk::LevelWindow levelWindow; mitk::LevelWindow opacLevelWindow; bool binary = false; this->GetDataNode()->GetBoolProperty( "binary", binary, renderer ); if ( binary ) { image->setExtrema(0, 1); image->setOpacityExtrema( 0.0, 255.0 ); image->setBinary(true); bool binaryOutline = false; if ( this->GetInput()->GetPixelType().GetBpe() <= 8 ) { if (this->GetDataNode()->GetBoolProperty( "outline binary", binaryOutline, renderer )) { image->setOutline(binaryOutline); float binaryOutlineWidth(1.0); if (this->GetDataNode()->GetFloatProperty( "outline width", binaryOutlineWidth, renderer )) { image->setOutlineWidth(binaryOutlineWidth); } } } else { //this->GetDataNode()->SetBoolProperty( "outline binary", false, renderer ); //this->GetDataNode()->SetFloatProperty( "opacity", 0.3, renderer ); //set opacity //rgba[3] = 0.3; MITK_WARN << "Type of all binary images should be (un)signed char. Outline does not work on other pixel types!"; } } else { if( !this->GetLevelWindow( levelWindow, renderer, "levelWindow" ) ) { this->GetLevelWindow( levelWindow, renderer ); } image->setExtrema( levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound() ); // obtain opacity level window if( this->GetLevelWindow( opacLevelWindow, renderer, "opaclevelwindow" ) ) { image->setOpacityExtrema( opacLevelWindow.GetLowerWindowBound(), opacLevelWindow.GetUpperWindowBound() ); } else { image->setOpacityExtrema( 0.0, 255.0 ); } } bool useColor = false; GetDataNode()->GetBoolProperty( "use color", useColor, renderer ); mitk::LookupTableProperty::Pointer LookupTableProp; if ( !useColor ) { LookupTableProp = dynamic_cast( this->GetDataNode()->GetProperty("LookupTable")); if ( LookupTableProp.IsNull() ) { useColor = true; } } if ( useColor || binary ) { // If lookup table use is NOT requested (or we have a binary image...): m_iil4mitkMode = iil4mitkImage::INTENSITY_ALPHA; image->setColor( rgba[0], rgba[1], rgba[2], rgba[3] ); } else { // If lookup table use is requested: m_iil4mitkMode = iil4mitkImage::COLOR_ALPHA; // only update the lut, when the properties have changed... if ( LookupTableProp->GetLookupTable()->GetMTime() <= this->GetDataNode()->GetPropertyList()->GetMTime() ) { LookupTableProp->GetLookupTable()->ChangeOpacityForAll( opacity ); LookupTableProp->GetLookupTable()->ChangeOpacity(0, 0.0); } image->setColors(LookupTableProp->GetLookupTable()->GetRawLookupTable()); } } void mitk::ImageMapperGL2D::Update(mitk::BaseRenderer* renderer) { mitk::Image* data = const_cast( this->GetInput() ); if ( data == NULL ) { return; } if ( !IsVisible(renderer) ) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep( renderer ); // Check if time step is valid const TimeSlicedGeometry *dataTimeGeometry = data->GetTimeSlicedGeometry(); if ( ( dataTimeGeometry == NULL ) || ( dataTimeGeometry->GetTimeSteps() == 0 ) || ( !dataTimeGeometry->IsValidTime( this->GetTimestep() ) ) ) { return; } const DataNode *node = this->GetDataNode(); RendererInfo& rendererInfo = AccessRendererInfo( renderer ); iil4mitkPicImage* image = rendererInfo.Get_iil4mitkImage(); data->UpdateOutputInformation(); if ( (image == NULL) || (rendererInfo.m_LastUpdateTime < node->GetMTime()) || (rendererInfo.m_LastUpdateTime < data->GetPipelineMTime()) || (rendererInfo.m_LastUpdateTime < renderer->GetCurrentWorldGeometry2DUpdateTime()) || (rendererInfo.m_LastUpdateTime < renderer->GetDisplayGeometryUpdateTime()) ) { - this->GenerateData( renderer ); + this->GenerateDataForRenderer( renderer ); } else if ( rendererInfo.m_LastUpdateTime < renderer->GetCurrentWorldGeometry2D()->GetMTime() ) { - this->GenerateData( renderer ); + this->GenerateDataForRenderer( renderer ); } else if ( (rendererInfo.m_LastUpdateTime < node->GetPropertyList()->GetMTime()) || (rendererInfo.m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ) { - this->GenerateData( renderer ); + this->GenerateDataForRenderer( renderer ); // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time rendererInfo.m_LastUpdateTime.Modified(); } } void mitk::ImageMapperGL2D ::DeleteRendererCallback( itk::Object *object, const itk::EventObject & ) { mitk::BaseRenderer *renderer = dynamic_cast< mitk::BaseRenderer* >( object ); if ( renderer ) { m_RendererInfo.erase( renderer ); } } mitk::ImageMapperGL2D::RendererInfo ::RendererInfo() : m_RendererID(-1), m_iil4mitkImage(NULL), m_Renderer(NULL), m_Pic(NULL), m_UnitSpacingImageFilter( NULL ), m_Reslicer( NULL ), m_TSFilter( NULL ), m_Image(NULL), m_ReferenceGeometry(NULL), m_TextureInterpolation(true), m_ObserverID( 0 ) { m_PixelsPerMM.Fill(0); }; mitk::ImageMapperGL2D::RendererInfo ::~RendererInfo() { this->Squeeze(); if ( m_UnitSpacingImageFilter != NULL ) { m_UnitSpacingImageFilter->Delete(); } if ( m_Reslicer != NULL ) { m_Reslicer->Delete(); } if ( m_TSFilter != NULL ) { m_TSFilter->Delete(); } if ( m_Image != NULL ) { m_Image->Delete(); } } void mitk::ImageMapperGL2D::RendererInfo ::Set_iil4mitkImage( iil4mitkPicImage *iil4mitkImage ) { assert( iil4mitkImage != NULL ); delete m_iil4mitkImage; m_iil4mitkImage = iil4mitkImage; } void mitk::ImageMapperGL2D::RendererInfo::Squeeze() { delete m_iil4mitkImage; m_iil4mitkImage = NULL; if ( m_Pic != NULL ) { mitkIpPicFree(m_Pic); m_Pic = NULL; } if ( m_Image != NULL ) { m_Image->Delete(); m_Image = NULL; } } void mitk::ImageMapperGL2D::RendererInfo::RemoveObserver() { if ( m_ObserverID != 0 ) { // m_ObserverID has to be decreased by one. Was incremented by one after creation to make the test m_ObserverID != 0 possible. m_Renderer->RemoveObserver( m_ObserverID-1 ); } } void mitk::ImageMapperGL2D::RendererInfo::Initialize( int rendererID, mitk::BaseRenderer *renderer, unsigned long observerID ) { // increase ID by one to avoid 0 ID, has to be decreased before remove of the observer m_ObserverID = observerID+1; assert(rendererID>=0); assert(m_RendererID<0); m_RendererID = rendererID; m_Renderer = renderer; m_Image = vtkImageData::New(); m_Reslicer = vtkImageReslice::New(); m_TSFilter = vtkMitkThickSlicesFilter::New(); m_Reslicer->ReleaseDataFlagOn(); m_TSFilter->ReleaseDataFlagOn(); m_UnitSpacingImageFilter = vtkImageChangeInformation::New(); m_UnitSpacingImageFilter->SetOutputSpacing( 1.0, 1.0, 1.0 ); } void mitk::ImageMapperGL2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty( "use color", mitk::BoolProperty::New( true ), renderer, overwrite ); node->AddProperty( "outline binary", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "outline width", mitk::FloatProperty::New( 1.0 ), renderer, overwrite ); if(image->IsRotated()) node->AddProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC) ); else node->AddProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ); node->AddProperty( "texture interpolation", mitk::BoolProperty::New( mitk::DataNodeFactory::m_TextureInterpolationActive ) ); // set to user configurable default value (see global options) node->AddProperty( "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ); node->AddProperty( "bounding box", mitk::BoolProperty::New( false ) ); bool isBinaryImage(false); if ( ! node->GetBoolProperty("binary", isBinaryImage) ) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2)/2); sliceSelector->SetTimeNr(image->GetDimension(3)/2); sliceSelector->SetChannelNr(image->GetDimension(4)/2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); if ( centralSliceImage.IsNotNull() && centralSliceImage->IsInitialized() ) { minValue = centralSliceImage->GetScalarValueMin(); maxValue = centralSliceImage->GetScalarValueMax(); min2ndValue = centralSliceImage->GetScalarValue2ndMin(); max2ndValue = centralSliceImage->GetScalarValue2ndMax(); } if ( minValue == maxValue ) { // centralSlice is strange, lets look at all data minValue = image->GetScalarValueMin(); maxValue = image->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetScalarValue2ndMaxNoRecompute(); } isBinaryImage = ( maxValue == min2ndValue && minValue == max2ndValue ); } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty( "opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite ); node->AddProperty( "color", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.selectedcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.selectedannotationcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.hoveringcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.hoveringannotationcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binary", mitk::BoolProperty::New( true ), renderer, overwrite ); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty( "opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite ); node->AddProperty( "color", ColorProperty::New(1.0,1.0,1.0), renderer, overwrite ); node->AddProperty( "binary", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); } if(image.IsNotNull() && image->IsInitialized()) { if((overwrite) || (node->GetProperty("levelwindow", renderer)==NULL)) { mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); mitk::LevelWindow levelwindow; levelwindow.SetAuto( image, true, true ); levWinProp->SetLevelWindow( levelwindow ); node->SetProperty( "levelwindow", levWinProp, renderer ); } if(((overwrite) || (node->GetProperty("opaclevelwindow", renderer)==NULL)) && (image->GetPixelType().GetItkTypeId() && *(image->GetPixelType().GetItkTypeId()) == typeid(itk::RGBAPixel))) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0,255); opaclevwin.SetWindowBounds(0,255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty( "opaclevelwindow", prop, renderer ); } if((overwrite) || (node->GetProperty("LookupTable", renderer)==NULL)) { // add a default rainbow lookup table for color mapping mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); vtkLookupTable* vtkLut = mitkLut->GetVtkLookupTable(); vtkLut->SetHueRange(0.6667, 0.0); vtkLut->SetTableRange(0.0, 20.0); vtkLut->Build(); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty( "LookupTable", mitkLutProp ); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Core/Code/Rendering/mitkImageMapperGL2D.h b/Core/Code/Rendering/mitkImageMapperGL2D.h index f628ed3341..d7aea797f8 100644 --- a/Core/Code/Rendering/mitkImageMapperGL2D.h +++ b/Core/Code/Rendering/mitkImageMapperGL2D.h @@ -1,298 +1,298 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITKIMAGEMAPPER2D_H_HEADER_INCLUDED_C10E906E #define MITKIMAGEMAPPER2D_H_HEADER_INCLUDED_C10E906E #include "mitkCommon.h" #include "mitkGLMapper2D.h" #include "mitkBaseRenderer.h" #include #include #include #include #include class iil4mitkPicImage; class Vtk2itk; class vtkImageReslice; class vtkLookupTable; class vtkGeneralTransform; class vtkImageChangeInformation; class vtkPoints; class vtkMitkThickSlicesFilter; namespace mitk { /** \brief Mapper to resample and display 2D slices of a 3D image. * * Currently implemented for mapping on PlaneGeometry and * AbstractTransformGeometry. The resulting 2D image (by reslicing the * underlying 3D input image appropriately) can either be directly rendered * in a 2D view or just be calculated to be used later on by another * rendering entity, e.g. in texture mapping in a 3D view. * * This results in a flipped version when used for texture mapping. Furthermore, * not the complete rectangular area described by the Geometry2D from the renderer * is resampled, @em if the Geometry2D is larger than the image dimension in the * requested direction. This results in a stretched version when used for texture * mapping. * * Properties that can be set for images and influence the imageMapper2D are: * * - \b "modality": (mitkModalityProperty) Modality of the image * - \b "opacity": (FloatProperty) Opacity of the image * - \b "color": (ColorProperty) Color of the image * - \b "use color": (BoolProperty) Use the color of the image or not * - \b "binary": (BoolProperty) is the image a binary image or not * - \b "outline binary": (BoolProperty) show outline of the image or not * - \b "texture interpolation": (BoolProperty) texture interpolation of the image * - \b "reslice interpolation": (VtkResliceInterpolationProperty) reslice interpolation of the image * - \b "in plane resample extent by geometry": (BoolProperty) Do it or not * - \b "bounding box": (BoolProperty) Is the Bounding Box of the image shown or not * - \b "layer": (IntProperty) Layer of the image * - \b "volume annotation color": (ColorProperty) color of the volume annotation * - \b "volume annotation unit": (StringProperty) annotation unit as string (does not implicit convert the unit!) unit is ml/cm3 * The default properties are: * - \b "opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite ) * - \b "color", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ) * - \b "use color", mitk::BoolProperty::New( true ), renderer, overwrite ) * - \b "binary", mitk::BoolProperty::New( true ), renderer, overwrite ) * - \b "outline binary", mitk::BoolProperty::New( false ), renderer, overwrite ) * - \b "texture interpolation", mitk::BoolProperty::New( mitk::DataNodeFactory::m_TextureInterpolationActive ) ) * - \b "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ) * - \b "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ) * - \b "bounding box", mitk::BoolProperty::New( false ) ) * - \b "layer", mitk::IntProperty::New(10), renderer, overwrite) * If the modality-property is set for an image, the mapper uses modality-specific default properties, * e.g. color maps, if they are defined. * \ingroup Mapper */ class MITK_CORE_EXPORT ImageMapperGL2D : public GLMapper2D { public: /** Standard class typedefs. */ mitkClassMacro( ImageMapperGL2D,GLMapper2D ); /** Method for creation through the object factory. */ itkNewMacro(Self); /** Some convenient typedefs. */ typedef mitk::Image InputImageType; typedef InputImageType::Pointer InputImagePointer; typedef InputImageType::ConstPointer InputImageConstPointer; typedef SlicedData::RegionType InputImageRegionType; /** \brief Get the Image to map */ const InputImageType *GetInput(void); /** \brief Calls Update() for all associated renderers. */ virtual void GenerateAllData(); /** \brief Renders the (priorly) resampled image onto the screen. */ virtual void Paint( mitk::BaseRenderer *renderer ); /** \brief Checks whether this mapper needs to update itself and generate * data. */ virtual void Update(mitk::BaseRenderer * renderer); virtual void ApplyProperties(mitk::BaseRenderer* renderer); /** \brief Internal storage class for data needed for rendering into a * renderer */ class MITK_CORE_EXPORT RendererInfo { /** \brief internal id of the renderer the data is stored for */ int m_RendererID; /** \brief stored iil4mitkPicImage containing the texture to display for * 2D rendering (cf. m_Image) */ iil4mitkPicImage* m_iil4mitkImage; mitk::BaseRenderer* m_Renderer; public: /** \brief timestamp of last update of stored data */ itk::TimeStamp m_LastUpdateTime; /** \brief stored data as a mitkIpPicDescriptor */ mitkIpPicDescriptor *m_Pic; /** \brief number of pixels per mm in x- and y-direction of the resampled */ Vector2D m_PixelsPerMM; /** \brief Extent (in pixels) of the image */ Vector2D m_Extent; /** \brief Overlap (in pixels) to ensure coverage of rotated images also */ Vector2D m_Overlap; /** \brief Using unit spacing for resampling makes life easier */ vtkImageChangeInformation *m_UnitSpacingImageFilter; /** \brief The actual reslicer (one per renderer) */ vtkImageReslice *m_Reslicer; /** \brief Thickslices post filtering */ vtkMitkThickSlicesFilter *m_TSFilter; /** \brief Extracted image for 3D rendering (cf. m_iil4mitkImage) */ vtkImageData *m_Image; /** \brief Reference geometry associated with the world geometry */ const Geometry3D *m_ReferenceGeometry; bool m_TextureInterpolation; /** \brief stores the id of the observer for delete event of renderer */ unsigned long m_ObserverID; RendererInfo(); ~RendererInfo(); inline bool IsInitialized() const { return m_RendererID >= 0; } void Initialize( int rendererID, mitk::BaseRenderer *renderer, unsigned long observerID ); void Set_iil4mitkImage(iil4mitkPicImage* iil4mitkImage); inline iil4mitkPicImage* Get_iil4mitkImage() const { return m_iil4mitkImage; } inline int GetRendererID() const { return m_RendererID; } void RemoveObserver(); void Squeeze(); }; // RendererInfo /** \brief Get the internal id of the renderer * \sa RendererInfo */ virtual int GetAssociatedChannelNr( mitk::BaseRenderer *renderer ); /** \brief Get the RendererInfo for \a renderer */ const RendererInfo *GetRendererInfo( mitk::BaseRenderer *renderer ) { return &this->AccessRendererInfo(renderer); } /** \brief Release memory allocated for buffering */ virtual void Clear(); static void SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer = NULL, bool overwrite = false); protected: ImageMapperGL2D(); virtual ~ImageMapperGL2D(); /** Does the actual resampling, without rendering the image yet. */ - virtual void GenerateData(mitk::BaseRenderer *renderer); + virtual void GenerateDataForRenderer(mitk::BaseRenderer *renderer); /** \brief Get the RendererInfo for @a renderer */ inline RendererInfo & AccessRendererInfo( mitk::BaseRenderer* renderer ) { RendererInfo& rendererInfo = m_RendererInfo[renderer]; if(rendererInfo.IsInitialized()==false) { // Add observer for renderer reset events (RendererInfo will // automatically be removed from list when a Renderer is deleted) // // Note: observer ID is passed to rendererInfo, which will take // responsiblity to remove the observer upon its destruction typedef itk::MemberCommand< ImageMapperGL2D > MemberCommandType; MemberCommandType::Pointer deleteRendererCommand = MemberCommandType::New(); deleteRendererCommand->SetCallbackFunction( this, &ImageMapperGL2D::DeleteRendererCallback ); unsigned long observerID = renderer->AddObserver( BaseRenderer::RendererResetEvent(), deleteRendererCommand ); // Initialize RendererInfo rendererInfo.Initialize( ImageMapperGL2D::numRenderer++, renderer, observerID ); } return rendererInfo; } void DeleteRendererCallback( itk::Object *object, const itk::EventObject & ); double CalculateSpacing( const mitk::Geometry3D *geometry, const mitk::Vector3D &d ) const; bool LineIntersectZero( vtkPoints *points, int p1, int p2, vtkFloatingPointType *bounds ); bool CalculateClippedPlaneBounds( const Geometry3D *boundingGeometry, const PlaneGeometry *planeGeometry, vtkFloatingPointType *bounds ); /** \brief Number of renderers data is stored for * \todo General concept for keeping data for rendering required * \todo static? */ static int numRenderer; protected: typedef std::map RendererInfoMap; /** \brief Map of instances of RendererInfo * \sa RendererInfo */ RendererInfoMap m_RendererInfo; vtkGeneralTransform *m_ComposedResliceTransform; private: int m_iil4mitkMode; }; } // namespace mitk #endif /* MITKIMAGEMAPPER2D_H_HEADER_INCLUDED_C10E906E */