diff --git a/Core/Code/Rendering/mitkPlaneGeometryDataMapper2D.cpp b/Core/Code/Rendering/mitkPlaneGeometryDataMapper2D.cpp index 47457b85a0..58ff73839b 100644 --- a/Core/Code/Rendering/mitkPlaneGeometryDataMapper2D.cpp +++ b/Core/Code/Rendering/mitkPlaneGeometryDataMapper2D.cpp @@ -1,350 +1,407 @@ /*=================================================================== 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 "mitkPlaneGeometryDataMapper2D.h" //mitk includes #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkVtkPropRenderer.h" #include "mitkPointSet.h" #include "mitkPlaneGeometry.h" #include "mitkPlaneOrientationProperty.h" #include "mitkLine.h" //vtk includes #include #include #include #include #include #include #include #include #include #include #include #include mitk::PlaneGeometryDataMapper2D::AllInstancesContainer mitk::PlaneGeometryDataMapper2D::s_AllInstances; // input for this mapper ( = point set) const mitk::PlaneGeometryData* mitk::PlaneGeometryDataMapper2D::GetInput() const { return static_cast< PlaneGeometryData * >(GetDataNode()->GetData()); } // constructor PlaneGeometryDataMapper2D mitk::PlaneGeometryDataMapper2D::PlaneGeometryDataMapper2D() : m_RenderOrientationArrows( false ), m_ArrowOrientationPositive( true ), m_DepthValue(1.0f) { s_AllInstances.insert(this); } // destructor mitk::PlaneGeometryDataMapper2D::~PlaneGeometryDataMapper2D() { s_AllInstances.erase(this); } // returns propassembly vtkProp* mitk::PlaneGeometryDataMapper2D::GetVtkProp(mitk::BaseRenderer * renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); -// return ls->m_CrosshairAssembly; - return ls->m_CrosshairActor; + return ls->m_CrosshairAssembly; } void mitk::PlaneGeometryDataMapper2D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); // The PlaneGeometryDataMapper2D mapper is special in that the rendering of // OTHER PlaneGeometryDatas affects how we render THIS PlaneGeometryData // (for the gap at the point where they intersect). A change in any of the // other PlaneGeometryData nodes could mean that we render ourself // differently, so we check for that here. bool generateDataRequired = false; for (AllInstancesContainer::iterator it = s_AllInstances.begin(); it != s_AllInstances.end(); ++it) { generateDataRequired = ls->IsGenerateDataRequired(renderer, this, (*it)->GetDataNode()); if (generateDataRequired) break; } // if (!generateDataRequired) return; ls->UpdateGenerateDataTime(); // Collect all other PlaneGeometryDatas that are being mapped by this mapper m_OtherPlaneGeometries.clear(); for (AllInstancesContainer::iterator it = s_AllInstances.begin(); it != s_AllInstances.end(); ++it) { Self *otherInstance = *it; // Skip ourself if (otherInstance == this) continue; mitk::DataNode *otherNode = otherInstance->GetDataNode(); if (!otherNode) continue; // Skip other PlaneGeometryData nodes that are not visible on this renderer if (!otherNode->IsVisible(renderer)) continue; PlaneGeometryData* otherData = dynamic_cast(otherNode->GetData()); if (!otherData) continue; PlaneGeometry* otherGeometry = dynamic_cast(otherData->GetPlaneGeometry()); if ( otherGeometry && !dynamic_cast(otherData->GetPlaneGeometry()) ) { m_OtherPlaneGeometries.push_back(otherNode); } } CreateVtkCrosshair(renderer); ApplyAllProperties(renderer); } void mitk::PlaneGeometryDataMapper2D::CreateVtkCrosshair(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if(!visible) return; PlaneGeometryData::Pointer input = const_cast< PlaneGeometryData * >(this->GetInput()); mitk::DataNode* geometryDataNode = renderer->GetCurrentWorldPlaneGeometryNode(); const PlaneGeometryData* rendererWorldPlaneGeometryData = dynamic_cast< PlaneGeometryData * >(geometryDataNode->GetData()); // intersecting with ourself? if ( input.IsNull() || input.GetPointer() == rendererWorldPlaneGeometryData) { return; // do rectangle stuff! } const PlaneGeometry *inputPlaneGeometry = dynamic_cast< const PlaneGeometry * >( input->GetPlaneGeometry() ); const PlaneGeometry* worldPlaneGeometry = dynamic_cast< const PlaneGeometry* >( rendererWorldPlaneGeometryData->GetPlaneGeometry() ); if ( worldPlaneGeometry && dynamic_cast(worldPlaneGeometry)==NULL && inputPlaneGeometry && dynamic_cast(input->GetPlaneGeometry() )==NULL && inputPlaneGeometry->GetReferenceGeometry() ) { const BaseGeometry *referenceGeometry = inputPlaneGeometry->GetReferenceGeometry(); // calculate intersection of the plane data with the border of the // world geometry rectangle Point3D point1, point2; Line3D crossLine; // Calculate the intersection line of the input plane with the world plane if ( worldPlaneGeometry->IntersectionLine( inputPlaneGeometry, crossLine ) ) { Point3D boundingBoxMin, boundingBoxMax; boundingBoxMin = referenceGeometry->GetBoundingBox()->GetMinimum(); boundingBoxMax = referenceGeometry->GetBoundingBox()->GetMaximum(); referenceGeometry->IndexToWorld(boundingBoxMin,boundingBoxMin); referenceGeometry->IndexToWorld(boundingBoxMax,boundingBoxMax); // Then, clip this line with the (transformed) bounding box of the // reference geometry. crossLine.BoxLineIntersection( boundingBoxMin[0], boundingBoxMin[1], boundingBoxMin[2], boundingBoxMax[0], boundingBoxMax[1], boundingBoxMax[2], crossLine.GetPoint(), crossLine.GetDirection(), point1, point2 ); crossLine.SetPoints(point1,point2); vtkSmartPointer lines = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer linesPolyData = vtkSmartPointer::New(); // Now iterate through all other lines displayed in this window and // calculate the positions of intersection with the line to be // rendered; these positions will be stored in lineParams to form a // gap afterwards. NodesVectorType::iterator otherPlanesIt = m_OtherPlaneGeometries.begin(); NodesVectorType::iterator otherPlanesEnd = m_OtherPlaneGeometries.end(); std::vector intersections; intersections.push_back(point1); otherPlanesIt = m_OtherPlaneGeometries.begin(); int gapsize = 32; this->GetDataNode()->GetPropertyValue( "Gap size",gapsize, NULL ); ScalarType lineLength = point1.EuclideanDistanceTo(point2); DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry(); ScalarType gapinmm = gapsize * displayGeometry->GetScaleFactorMMPerDisplayUnit(); float gapSizeParam = gapinmm / lineLength; while ( otherPlanesIt != otherPlanesEnd ) { PlaneGeometry *otherPlane = static_cast< PlaneGeometry * >( static_cast< PlaneGeometryData * >((*otherPlanesIt)->GetData() )->GetPlaneGeometry() ); if (otherPlane != inputPlaneGeometry && otherPlane != worldPlaneGeometry) { Point3D planeIntersection; otherPlane->IntersectionPoint(crossLine,planeIntersection); ScalarType sectionLength = point1.EuclideanDistanceTo(planeIntersection); ScalarType lineValue = sectionLength/lineLength; if(lineValue-gapSizeParam > 0.0) intersections.push_back(crossLine.GetPoint(lineValue-gapSizeParam)); else intersections.pop_back(); if(lineValue+gapSizeParam < 1.0) intersections.push_back(crossLine.GetPoint(lineValue+gapSizeParam)); } ++otherPlanesIt; } if(intersections.size()%2 == 1) intersections.push_back(point2); if(intersections.empty()) { this->DrawLine(point1,point2,lines,points); } else for(unsigned int i = 0 ; i< intersections.size()-1 ; i+=2) { this->DrawLine(intersections[i],intersections[i+1],lines,points); } // Add the points to the dataset linesPolyData->SetPoints(points); // Add the lines to the dataset linesPolyData->SetLines(lines); // Visualize vtkSmartPointer mapper = vtkSmartPointer::New(); mapper->SetInputData(linesPolyData); LocalStorage* ls = m_LSH.GetLocalStorage(renderer); ls->m_CrosshairActor->SetMapper(mapper); - ls->m_CrosshairAssembly->AddPart(ls->m_CrosshairActor); + // Determine if we should draw the area covered by the thick slicing, default is false. + // This will also show the area of slices that do not have thick slice mode enabled + bool showAreaOfThickSlicing = false; + GetDataNode()->GetBoolProperty( "reslice.thickslices.showarea", showAreaOfThickSlicing ); + + // get the normal of the inputPlaneGeometry + Vector3D normal = inputPlaneGeometry->GetNormal(); + // determine the pixelSpacing in that direction + double thickSliceDistance = SlicedGeometry3D::CalculateSpacing( referenceGeometry->GetSpacing(), normal ); + + IntProperty *intProperty=0; + if( GetDataNode()->GetProperty( intProperty, "reslice.thickslices.num" ) && intProperty ) + thickSliceDistance *= intProperty->GetValue()+0.5; + else + showAreaOfThickSlicing = false; + + // not the nicest place to do it, but we have the width of the visible bloc in MM here + // so we store it in this fancy property + GetDataNode()->SetFloatProperty( "reslice.thickslices.sizeinmm", thickSliceDistance*2 ); + + if ( showAreaOfThickSlicing ) + { + vtkSmartPointer helperlines = vtkSmartPointer::New(); + vtkSmartPointer helperlinesPolyData = vtkSmartPointer::New(); + // vectorToHelperLine defines how to reach the helperLine from the mainLine + Vector3D vectorToHelperLine; + vectorToHelperLine = normal; + vectorToHelperLine.Normalize(); + // got the right direction, so we multiply the width + vectorToHelperLine *= thickSliceDistance; + + this->DrawLine(point1 - vectorToHelperLine, point2 - vectorToHelperLine,helperlines,points); + this->DrawLine(point1 + vectorToHelperLine, point2 + vectorToHelperLine,helperlines,points); + + // Add the points to the dataset + helperlinesPolyData->SetPoints(points); + + // Add the lines to the dataset + helperlinesPolyData->SetLines(helperlines); + + // Visualize + vtkSmartPointer helperLinesmapper = vtkSmartPointer::New(); + helperLinesmapper->SetInputData(helperlinesPolyData); + + ls->m_CrosshairActor->GetProperty()->SetLineStipplePattern(0xf0f0); + ls->m_CrosshairActor->GetProperty()->SetLineStippleRepeatFactor(1); + + ls->m_CrosshairHelperLineActor->SetMapper(helperLinesmapper); + ls->m_CrosshairAssembly->AddPart(ls->m_CrosshairHelperLineActor); + } + else + { + ls->m_CrosshairAssembly->RemovePart(ls->m_CrosshairHelperLineActor); + ls->m_CrosshairActor->GetProperty()->SetLineStipplePattern(0xffff); + } } } } void mitk::PlaneGeometryDataMapper2D::DrawLine( mitk::Point3D p0,mitk::Point3D p1, vtkCellArray* lines, vtkPoints* points ) { vtkIdType pidStart = points->InsertNextPoint(p0[0],p0[1], p0[2]); vtkIdType pidEnd = points->InsertNextPoint(p1[0],p1[1], p1[2]); vtkSmartPointer lineVtk = vtkSmartPointer::New(); lineVtk->GetPointIds()->SetId(0,pidStart); lineVtk->GetPointIds()->SetId(1,pidEnd); lines->InsertNextCell(lineVtk); } int mitk::PlaneGeometryDataMapper2D::DetermineThickSliceMode( DataNode * dn, int &thickSlicesNum ) { int thickSlicesMode = 0; // determine the state and the extend of the thick-slice mode mitk::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=0; if(thickSlicesNum > 10) thickSlicesNum=10; } if ( thickSlicesMode == 0 ) thickSlicesNum = 0; return thickSlicesMode; } void mitk::PlaneGeometryDataMapper2D::ApplyAllProperties( BaseRenderer *renderer ) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); Superclass::ApplyColorAndOpacityProperties(renderer, ls->m_CrosshairActor); + Superclass::ApplyColorAndOpacityProperties(renderer, ls->m_CrosshairHelperLineActor); PlaneOrientationProperty* decorationProperty; this->GetDataNode()->GetProperty( decorationProperty, "decoration", renderer ); if ( decorationProperty != NULL ) { if ( decorationProperty->GetPlaneDecoration() == PlaneOrientationProperty::PLANE_DECORATION_POSITIVE_ORIENTATION ) { m_RenderOrientationArrows = true; m_ArrowOrientationPositive = true; } else if ( decorationProperty->GetPlaneDecoration() == PlaneOrientationProperty::PLANE_DECORATION_NEGATIVE_ORIENTATION ) { m_RenderOrientationArrows = true; m_ArrowOrientationPositive = false; } else { m_RenderOrientationArrows = false; } } } void mitk::PlaneGeometryDataMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty( "Line width", mitk::IntProperty::New(2), renderer, overwrite ); node->AddProperty( "Gap size", mitk::IntProperty::New(32), renderer, overwrite ); node->AddProperty( "decoration", mitk::PlaneOrientationProperty ::New(PlaneOrientationProperty::PLANE_DECORATION_NONE), renderer, overwrite ); Superclass::SetDefaultProperties(node, renderer, overwrite); } void mitk::PlaneGeometryDataMapper2D::UpdateVtkTransform(mitk::BaseRenderer *renderer) { } mitk::PlaneGeometryDataMapper2D::LocalStorage::LocalStorage() { m_CrosshairAssembly = vtkSmartPointer ::New(); m_CrosshairActor = vtkSmartPointer ::New(); + m_CrosshairHelperLineActor = vtkSmartPointer ::New(); + m_CrosshairAssembly->AddPart(m_CrosshairActor); + m_CrosshairAssembly->AddPart(m_CrosshairHelperLineActor); } // destructor LocalStorage mitk::PlaneGeometryDataMapper2D::LocalStorage::~LocalStorage() { } diff --git a/Core/Code/Rendering/mitkPlaneGeometryDataMapper2D.h b/Core/Code/Rendering/mitkPlaneGeometryDataMapper2D.h index 5afd7d629f..e5622b601f 100644 --- a/Core/Code/Rendering/mitkPlaneGeometryDataMapper2D.h +++ b/Core/Code/Rendering/mitkPlaneGeometryDataMapper2D.h @@ -1,135 +1,137 @@ /*=================================================================== 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 MITKPlaneGeometryDataMapper2D_H_HEADER_INCLUDED_C1902626 #define MITKPlaneGeometryDataMapper2D_H_HEADER_INCLUDED_C1902626 #include #include "mitkVtkMapper.h" #include "mitkBaseRenderer.h" #include "mitkPointSetShapeProperty.h" #include #include "mitkWeakPointer.h" class vtkActor; class vtkPropAssembly; class vtkPolyData; class vtkPolyDataMapper; class vtkGlyphSource2D; class vtkGlyph3D; class vtkFloatArray; class vtkCellArray; namespace mitk { class PointSet; /** * @brief Vtk-based 2D mapper * @ingroup Mapper */ class MITK_CORE_EXPORT PlaneGeometryDataMapper2D : public VtkMapper { public: mitkClassMacro(PlaneGeometryDataMapper2D, VtkMapper); itkFactorylessNewMacro(Self) itkCloneMacro(Self) virtual const mitk::PlaneGeometryData* GetInput() const; /** \brief returns the a prop assembly */ virtual vtkProp* GetVtkProp(mitk::BaseRenderer* renderer); /** Applies properties specific to this mapper */ virtual void ApplyAllProperties( BaseRenderer *renderer ); virtual void UpdateVtkTransform(mitk::BaseRenderer *renderer); /** \brief set the default properties for this mapper */ static void SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer = NULL, bool overwrite = false); /** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */ class LocalStorage : public mitk::Mapper::BaseLocalStorage { public: /* constructor */ LocalStorage(); /* destructor */ ~LocalStorage(); // actor vtkSmartPointer m_CrosshairActor; + vtkSmartPointer m_CrosshairHelperLineActor; vtkSmartPointer m_CrosshairAssembly; + }; /** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */ mitk::LocalStorageHandler m_LSH; protected: /* constructor */ PlaneGeometryDataMapper2D(); /* destructor */ virtual ~PlaneGeometryDataMapper2D(); /* \brief Applies the color and opacity properties and calls CreateVTKRenderObjects */ virtual void GenerateDataForRenderer(mitk::BaseRenderer* renderer); void CreateVtkCrosshair(BaseRenderer *renderer); /** * \brief Returns the thick slice mode for the given datanode. * * This method returns the value of the 'reslice.thickslices' property for * the given datanode. * '0': thick slice mode disabled * '1': thick slice mode enabled * * The variable 'thickSlicesNum' contains the value of the 'reslice.thickslices.num' * property that defines how many slices are shown at once. */ int DetermineThickSliceMode( DataNode * dn, int &thickSlicesNum ); void DrawLine(Point3D p0, Point3D p1, vtkCellArray *lines, vtkPoints *points); // member variables holding the current value of the properties used in this mapper typedef std::vector NodesVectorType; NodesVectorType m_OtherPlaneGeometries; typedef std::set AllInstancesContainer; static AllInstancesContainer s_AllInstances; bool m_RenderOrientationArrows; bool m_ArrowOrientationPositive; mitk::ScalarType m_DepthValue; }; } // namespace mitk #endif /* MITKPlaneGeometryDataMapper2D_H_HEADER_INCLUDED_C1902626 */