diff --git a/Modules/Core/include/mitkPointSetVtkMapper2D.h b/Modules/Core/include/mitkPointSetVtkMapper2D.h index f8520be771..31f3cfdced 100644 --- a/Modules/Core/include/mitkPointSetVtkMapper2D.h +++ b/Modules/Core/include/mitkPointSetVtkMapper2D.h @@ -1,238 +1,239 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkPointSetVtkMapper2D_h #define mitkPointSetVtkMapper2D_h #include "mitkBaseRenderer.h" #include "mitkLocalStorageHandler.h" #include "mitkVtkMapper.h" #include #include // VTK #include 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 for PointSet * * Due to the need of different colors for selected * and unselected points and the facts, that we also have a contour and * labels for the points, the vtk structure is build up the following way: * * We have three PolyData, one selected, and one unselected and one * for a contour between the points. Each one is connected to an own * PolyDataMapper and an Actor. The different color for the unselected and * selected state and for the contour is read from properties. * * This mapper has several additional functionalities, such as rendering * a contour between points, calculating and displaying distances or angles * between points. * * @section mitkPointSetVtkMapper2D_point_rep Point Representation * * The points are displayed as small glyphs of configurable shape * (see property "PointSet.2D.shape"). The size of these glyphs * is given in world units. That means, the size or shape of those * glyphs is independent of the BaseGeometry object that you assign * to the PointSet. As for all other objects, _positions_ of points * will be transformed into the world via the Geometry's index-to-world * transform. * * Then the three Actors are combined inside a vtkPropAssembly and this * object is returned in GetProp() and so hooked up into the rendering * pipeline. * * @section mitkPointSetVtkMapper2D_propertires Applicable Properties * * Properties that can be set for point sets and influence the PointSetVTKMapper2D are: * * - \b "line width": (IntProperty 2) // line width of the line from one point to another * - \b "point line width": (IntProperty 1) // line width of the cross marking a point * - \b "point 2D size": (FloatProperty 6) // size of the glyph marking a point (diameter, in world * units!) * - \b "show contour": (BoolProperty false) // enable contour rendering between points (lines) * - \b "close contour": (BoolProperty false) // if enabled, the open strip is closed (first point * connected with last point) * - \b "show points": (BoolProperty true) // show or hide points * - \b "show distances": (BoolProperty false) // show or hide distance measure * - \b "distance decimal digits": (IntProperty 2) // set the number of decimal digits to be shown when * rendering the distance information * - \b "show angles": (BoolProperty false) // show or hide angle measurement * - \b "show distant lines": (BoolProperty false) // show the line between to points from a distant view * (equals "always on top" option) * - \b "layer": (IntProperty 1) // default is drawing pointset above images (they have a * default layer of 0) * - \b "PointSet.2D.shape" (EnumerationProperty Cross) // provides different shapes marking a point * 0 = "None", 1 = "Vertex", 2 = "Dash", 3 = "Cross", 4 = "ThickCross", 5 = "Triangle", 6 = "Square", 7 = * "Circle", * 8 = "Diamond", 9 = "Arrow", 10 = "ThickArrow", 11 = "HookedArrow", 12 = "Cross" * - \b "PointSet.2D.fill shape": (BoolProperty false) // fill or do not fill the glyph shape * - \b "Pointset.2D.distance to plane": (FloatProperty 4.0) //In the 2D render window, points are rendered which lie * within a certain distance * to the current plane. They are projected on the current * plane and scaled according to their distance. * Point markers appear smaller as the plane moves away * from * their true location. * The distance threshold can be adjusted by this float * property, which ables the user to delineate the points * that lie exactly on the plane. (+/- rounding error) * * Other Properties used here but not defined in this class: * * - \b "selectedcolor": (ColorProperty (1.0f, 0.0f, 0.0f)) // default color of the selected pointset e.g. the * current * point is red * - \b "contourcolor" : (ColorProperty (1.0f, 0.0f, 0.0f)) // default color for the contour is red * - \b "color": (ColorProperty (1.0f, 1.0f, 0.0f)) // default color of the (unselected) pointset is yellow * - \b "opacity": (FloatProperty 1.0) // opacity of point set, contours * - \b "label": (StringProperty nullptr) // a label can be defined for each point, which is rendered in proximity * to * the point * * @ingroup Mapper */ class MITKCORE_EXPORT PointSetVtkMapper2D : public VtkMapper { public: mitkClassMacro(PointSetVtkMapper2D, VtkMapper); itkFactorylessNewMacro(Self); itkCloneMacro(Self); virtual const mitk::PointSet *GetInput() const; /** \brief returns the a prop assembly */ vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; /** \brief set the default properties for this mapper */ static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, 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() override; // points vtkSmartPointer m_UnselectedPoints; vtkSmartPointer m_SelectedPoints; vtkSmartPointer m_ContourPoints; // scales vtkSmartPointer m_UnselectedScales; vtkSmartPointer m_SelectedScales; // distances vtkSmartPointer m_DistancesBetweenPoints; // lines vtkSmartPointer m_ContourLines; // glyph source (provides different shapes for the points) vtkSmartPointer m_UnselectedGlyphSource2D; vtkSmartPointer m_SelectedGlyphSource2D; // glyph vtkSmartPointer m_UnselectedGlyph3D; vtkSmartPointer m_SelectedGlyph3D; // polydata vtkSmartPointer m_VtkUnselectedPointListPolyData; vtkSmartPointer m_VtkSelectedPointListPolyData; vtkSmartPointer m_VtkContourPolyData; // actor vtkSmartPointer m_UnselectedActor; vtkSmartPointer m_SelectedActor; vtkSmartPointer m_ContourActor; vtkSmartPointer m_VtkTextActor; std::vector> m_VtkTextLabelActors; std::vector> m_VtkTextDistanceActors; std::vector> m_VtkTextAngleActors; // mappers vtkSmartPointer m_VtkUnselectedPolyDataMapper; vtkSmartPointer m_VtkSelectedPolyDataMapper; vtkSmartPointer m_VtkContourPolyDataMapper; // propassembly vtkSmartPointer m_PropAssembly; }; /** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */ mitk::LocalStorageHandler m_LSH; protected: /* constructor */ PointSetVtkMapper2D(); /* destructor */ ~PointSetVtkMapper2D() override; /* \brief Applies the color and opacity properties and calls CreateVTKRenderObjects */ void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override; /* \brief Called in mitk::Mapper::Update * If TimeGeometry or time step is not valid of point set: reset mapper so that nothing is * displayed e.g. toggle visiblity of the propassembly */ void ResetMapper(BaseRenderer *renderer) override; /* \brief Fills the vtk objects, thus it is only called when the point set has been changed. * This function iterates over the input point set and determines the glyphs which lie in a specific * range around the current slice. Those glyphs are rendered using a specific shape defined in vtk glyph source * to mark each point. The shape can be changed in MITK using the property "PointSet.2D.shape". * * There were issues when rendering vtk glyphs in the 2D-render windows. By default, the glyphs are * rendered within the x-y plane in each 2D-render window, so you would only see them from the * side in the saggital and coronal 2D-render window. The solution to this is to rotate the glyphs in order * to be ortogonal to the current view vector. To achieve this, the rotation (vtktransform) of the current * PlaneGeometry is applied to the orienation of the glyphs. */ virtual void CreateVTKRenderObjects(mitk::BaseRenderer *renderer); // member variables holding the current value of the properties used in this mapper bool m_ShowContour; // "show contour" property bool m_CloseContour; // "close contour" property bool m_ShowPoints; // "show points" property bool m_ShowDistances; // "show distances" property int m_DistancesDecimalDigits; // "distance decimal digits" property bool m_ShowAngles; // "show angles" property bool m_ShowDistantLines; // "show distant lines" property int m_LineWidth; // "line width" property int m_PointLineWidth; // "point line width" property float m_Point2DSize; // "point 2D size" property int m_IDShapeProperty; // ID for mitkPointSetShape Enumeration Property "Pointset.2D.shape" bool m_FillShape; // "Pointset.2D.fill shape" property float m_DistanceToPlane; // "Pointset.2D.distance to plane" property + bool m_FixedSizeOnScreen; // "Pointset.2D.fixed size on screen" property }; } // namespace mitk #endif /* mitkPointSetVtkMapper2D_h */ diff --git a/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp index a94ac6b040..3df46593c5 100644 --- a/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp @@ -1,745 +1,781 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkPointSetVtkMapper2D.h" // mitk includes #include "mitkVtkPropRenderer.h" #include #include #include #include // vtk includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include +namespace +{ + double GetScreenResolution(const mitk::BaseRenderer* renderer) + { + if (nullptr == renderer) + return 1.0; + + mitk::Point2D pD1, pD2; + pD1[0] = 0.0; + pD1[1] = 0.0; + pD2[0] = 0.0; + pD2[1] = 1.0; + + // Calculate world coordinates of in-plane screen pixels (0, 0) and (0, 1). + mitk::Point3D pW1, pW2; + renderer->DisplayToWorld(pD1, pW1); + renderer->DisplayToWorld(pD2, pW2); + + // For 2D renderers, the distance between these points is the screen resolution. + return pW1.EuclideanDistanceTo(pW2); + } +} + // constructor LocalStorage mitk::PointSetVtkMapper2D::LocalStorage::LocalStorage() { // points m_UnselectedPoints = vtkSmartPointer::New(); m_SelectedPoints = vtkSmartPointer::New(); m_ContourPoints = vtkSmartPointer::New(); // scales m_UnselectedScales = vtkSmartPointer::New(); m_SelectedScales = vtkSmartPointer::New(); // distances m_DistancesBetweenPoints = vtkSmartPointer::New(); // lines m_ContourLines = vtkSmartPointer::New(); // glyph source (provides the different shapes) m_UnselectedGlyphSource2D = vtkSmartPointer::New(); m_SelectedGlyphSource2D = vtkSmartPointer::New(); // glyphs m_UnselectedGlyph3D = vtkSmartPointer::New(); m_SelectedGlyph3D = vtkSmartPointer::New(); // polydata m_VtkUnselectedPointListPolyData = vtkSmartPointer::New(); m_VtkSelectedPointListPolyData = vtkSmartPointer::New(); m_VtkContourPolyData = vtkSmartPointer::New(); // actors m_UnselectedActor = vtkSmartPointer::New(); m_SelectedActor = vtkSmartPointer::New(); m_ContourActor = vtkSmartPointer::New(); // mappers m_VtkUnselectedPolyDataMapper = vtkSmartPointer::New(); m_VtkSelectedPolyDataMapper = vtkSmartPointer::New(); m_VtkContourPolyDataMapper = vtkSmartPointer::New(); // propassembly m_PropAssembly = vtkSmartPointer::New(); } // destructor LocalStorage mitk::PointSetVtkMapper2D::LocalStorage::~LocalStorage() { } // input for this mapper ( = point set) const mitk::PointSet *mitk::PointSetVtkMapper2D::GetInput() const { return static_cast(GetDataNode()->GetData()); } // constructor PointSetVtkMapper2D mitk::PointSetVtkMapper2D::PointSetVtkMapper2D() : m_ShowContour(false), m_CloseContour(false), m_ShowPoints(true), m_ShowDistances(false), m_DistancesDecimalDigits(1), m_ShowAngles(false), m_ShowDistantLines(false), m_LineWidth(1), m_PointLineWidth(1), m_Point2DSize(6), m_IDShapeProperty(mitk::PointSetShapeProperty::CROSS), m_FillShape(false), - m_DistanceToPlane(4.0f) + m_DistanceToPlane(4.0f), + m_FixedSizeOnScreen(false) { } // destructor mitk::PointSetVtkMapper2D::~PointSetVtkMapper2D() { } // reset mapper so that nothing is displayed e.g. toggle visiblity of the propassembly void mitk::PointSetVtkMapper2D::ResetMapper(BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_PropAssembly->VisibilityOff(); } // returns propassembly vtkProp *mitk::PointSetVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_PropAssembly; } static bool makePerpendicularVector2D(const mitk::Vector2D &in, mitk::Vector2D &out) { // The dot product of orthogonal vectors is zero. // In two dimensions the slopes of perpendicular lines are negative reciprocals. if ((fabs(in[0]) > 0) && ((fabs(in[0]) > fabs(in[1])) || (in[1] == 0))) { // negative reciprocal out[0] = -in[1] / in[0]; out[1] = 1; out.Normalize(); return true; } else if (fabs(in[1]) > 0) { out[0] = 1; // negative reciprocal out[1] = -in[0] / in[1]; out.Normalize(); return true; } else return false; } void mitk::PointSetVtkMapper2D::CreateVTKRenderObjects(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); unsigned i = 0; // The vtk text actors need to be removed manually from the propassembly // since the same vtk text actors are not overwriten within this function, // but new actors are added to the propassembly each time this function is executed. // Thus, the actors from the last call must be removed in the beginning. for (i = 0; i < ls->m_VtkTextLabelActors.size(); i++) { if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextLabelActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextLabelActors.at(i)); } for (i = 0; i < ls->m_VtkTextDistanceActors.size(); i++) { if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextDistanceActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextDistanceActors.at(i)); } for (i = 0; i < ls->m_VtkTextAngleActors.size(); i++) { if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextAngleActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextAngleActors.at(i)); } // initialize polydata here, otherwise we have update problems when // executing this function again ls->m_VtkUnselectedPointListPolyData = vtkSmartPointer::New(); ls->m_VtkSelectedPointListPolyData = vtkSmartPointer::New(); ls->m_VtkContourPolyData = vtkSmartPointer::New(); // get input point set and update the PointSet mitk::PointSet::Pointer input = const_cast(this->GetInput()); // only update the input data, if the property tells us to bool update = true; this->GetDataNode()->GetBoolProperty("updateDataOnRender", update); if (update == true) input->Update(); int timestep = this->GetTimestep(); mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet(timestep); if (itkPointSet.GetPointer() == nullptr) { ls->m_PropAssembly->VisibilityOff(); return; } // iterator for point set mitk::PointSet::PointsContainer::Iterator pointsIter = itkPointSet->GetPoints()->Begin(); // PointDataContainer has additional information to each point, e.g. whether // it is selected or not mitk::PointSet::PointDataContainer::Iterator pointDataIter; pointDataIter = itkPointSet->GetPointData()->Begin(); // check if the list for the PointDataContainer is the same size as the PointsContainer. // If not, then the points were inserted manually and can not be visualized according to the PointData // (selected/unselected) bool pointDataBroken = (itkPointSet->GetPointData()->Size() != itkPointSet->GetPoints()->Size()); if (itkPointSet->GetPointData()->size() == 0 || pointDataBroken) { ls->m_PropAssembly->VisibilityOff(); return; } ls->m_PropAssembly->VisibilityOn(); // empty point sets, cellarrays, scalars ls->m_UnselectedPoints->Reset(); ls->m_SelectedPoints->Reset(); ls->m_ContourPoints->Reset(); ls->m_ContourLines->Reset(); ls->m_UnselectedScales->Reset(); ls->m_SelectedScales->Reset(); ls->m_DistancesBetweenPoints->Reset(); ls->m_VtkTextLabelActors.clear(); ls->m_VtkTextDistanceActors.clear(); ls->m_VtkTextAngleActors.clear(); ls->m_UnselectedScales->SetNumberOfComponents(3); ls->m_SelectedScales->SetNumberOfComponents(3); int NumberContourPoints = 0; bool pointsOnSameSideOfPlane = false; const int text2dDistance = 10; // initialize points with a random start value // current point in point set itk::Point point = pointsIter->Value(); mitk::Point3D p = point; // currently visited point mitk::Point3D lastP = point; // last visited point (predecessor in point set of "point") mitk::Vector3D vec; // p - lastP mitk::Vector3D lastVec; // lastP - point before lastP vec.Fill(0.0); lastVec.Fill(0.0); mitk::Point2D pt2d; pt2d[0] = point[0]; // projected_p in display coordinates pt2d[1] = point[1]; mitk::Point2D lastPt2d = pt2d; // last projected_p in display coordinates (predecessor in point set of "pt2d") mitk::Point2D preLastPt2d = pt2d; // projected_p in display coordinates before lastPt2 const mitk::PlaneGeometry *geo2D = renderer->GetCurrentWorldPlaneGeometry(); + double resolution = GetScreenResolution(renderer); vtkLinearTransform *dataNodeTransform = input->GetGeometry()->GetVtkTransform(); int count = 0; for (pointsIter = itkPointSet->GetPoints()->Begin(); pointsIter != itkPointSet->GetPoints()->End(); pointsIter++) { lastP = p; // valid for number of points count > 0 preLastPt2d = lastPt2d; // valid only for count > 1 lastPt2d = pt2d; // valid for number of points count > 0 lastVec = vec; // valid only for counter > 1 // get current point in point set point = pointsIter->Value(); // transform point { float vtkp[3]; itk2vtk(point, vtkp); dataNodeTransform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, point); } p[0] = point[0]; p[1] = point[1]; p[2] = point[2]; renderer->WorldToDisplay(p, pt2d); vec = p - lastP; // valid only for counter > 0 // compute distance to current plane float dist = geo2D->Distance(point); + // measure distance in screen pixel units if requested + if (m_FixedSizeOnScreen) + { + dist /= resolution; + } // draw markers on slices a certain distance away from the points // location according to the tolerance threshold (m_DistanceToPlane) if (dist < m_DistanceToPlane) { // is point selected or not? if (pointDataIter->Value().selected) { ls->m_SelectedPoints->InsertNextPoint(point[0], point[1], point[2]); // point is scaled according to its distance to the plane - ls->m_SelectedScales->InsertNextTuple3(std::max(0.0f, m_Point2DSize - (2 * dist)), 0, 0); + ls->m_SelectedScales->InsertNextTuple3( + std::max(0.0f, m_Point2DSize - (2 * dist)), 0, 0); } else { ls->m_UnselectedPoints->InsertNextPoint(point[0], point[1], point[2]); // point is scaled according to its distance to the plane - ls->m_UnselectedScales->InsertNextTuple3(std::max(0.0f, m_Point2DSize - (2 * dist)), 0, 0); + ls->m_UnselectedScales->InsertNextTuple3( + std::max(0.0f, m_Point2DSize - (2 * dist)), 0, 0); } //---- LABEL -----// // paint label for each point if available if (dynamic_cast(this->GetDataNode()->GetProperty("label")) != nullptr) { const char *pointLabel = dynamic_cast(this->GetDataNode()->GetProperty("label"))->GetValue(); std::string l = pointLabel; if (input->GetSize() > 1) { std::stringstream ss; ss << pointsIter->Index(); l.append(ss.str()); } ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetDisplayPosition(pt2d[0] + text2dDistance, pt2d[1] + text2dDistance); ls->m_VtkTextActor->SetInput(l.c_str()); ls->m_VtkTextActor->GetTextProperty()->SetOpacity(100); float unselectedColor[4] = {1.0, 1.0, 0.0, 1.0}; // check if there is a color property GetDataNode()->GetColor(unselectedColor); ls->m_VtkTextActor->GetTextProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]); ls->m_VtkTextLabelActors.push_back(ls->m_VtkTextActor); } } // draw contour, distance text and angle text in render window // lines between points, which intersect the current plane, are drawn if (m_ShowContour && count > 0) { ScalarType distance = renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(point); ScalarType lastDistance = renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(lastP); pointsOnSameSideOfPlane = (distance * lastDistance) > 0.5; // Points must be on different side of plane in order to draw a contour. // If "show distant lines" is enabled this condition is disregarded. if (!pointsOnSameSideOfPlane || m_ShowDistantLines) { vtkSmartPointer line = vtkSmartPointer::New(); ls->m_ContourPoints->InsertNextPoint(lastP[0], lastP[1], lastP[2]); line->GetPointIds()->SetId(0, NumberContourPoints); NumberContourPoints++; ls->m_ContourPoints->InsertNextPoint(point[0], point[1], point[2]); line->GetPointIds()->SetId(1, NumberContourPoints); NumberContourPoints++; ls->m_ContourLines->InsertNextCell(line); if (m_ShowDistances) // calculate and print distance between adjacent points { float distancePoints = point.EuclideanDistanceTo(lastP); std::stringstream buffer; buffer << std::fixed << std::setprecision(m_DistancesDecimalDigits) << distancePoints << " mm"; // compute desired display position of text Vector2D vec2d = pt2d - lastPt2d; makePerpendicularVector2D(vec2d, vec2d); // text is rendered within text2dDistance perpendicular to current line Vector2D pos2d = (lastPt2d.GetVectorFromOrigin() + pt2d.GetVectorFromOrigin()) * 0.5 + vec2d * text2dDistance; ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetDisplayPosition(pos2d[0], pos2d[1]); ls->m_VtkTextActor->SetInput(buffer.str().c_str()); ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0); ls->m_VtkTextDistanceActors.push_back(ls->m_VtkTextActor); } if (m_ShowAngles && count > 1) // calculate and print angle between connected lines { std::stringstream buffer; buffer << angle(vec.GetVnlVector(), -lastVec.GetVnlVector()) * 180 / vnl_math::pi << "°"; // compute desired display position of text Vector2D vec2d = pt2d - lastPt2d; // first arm enclosing the angle vec2d.Normalize(); Vector2D lastVec2d = lastPt2d - preLastPt2d; // second arm enclosing the angle lastVec2d.Normalize(); vec2d = vec2d - lastVec2d; // vector connecting both arms vec2d.Normalize(); // middle between two vectors that enclose the angle Vector2D pos2d = lastPt2d.GetVectorFromOrigin() + vec2d * text2dDistance * text2dDistance; ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetDisplayPosition(pos2d[0], pos2d[1]); ls->m_VtkTextActor->SetInput(buffer.str().c_str()); ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0); ls->m_VtkTextAngleActors.push_back(ls->m_VtkTextActor); } } } if (pointDataIter != itkPointSet->GetPointData()->End()) { pointDataIter++; count++; } } // add each single text actor to the assembly for (i = 0; i < ls->m_VtkTextLabelActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextLabelActors.at(i)); } for (i = 0; i < ls->m_VtkTextDistanceActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextDistanceActors.at(i)); } for (i = 0; i < ls->m_VtkTextAngleActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextAngleActors.at(i)); } //---- CONTOUR -----// // create lines between the points which intersect the plane if (m_ShowContour) { // draw line between first and last point which is rendered if (m_CloseContour && NumberContourPoints > 1) { vtkSmartPointer closingLine = vtkSmartPointer::New(); closingLine->GetPointIds()->SetId(0, 0); // index of first point closingLine->GetPointIds()->SetId(1, NumberContourPoints - 1); // index of last point ls->m_ContourLines->InsertNextCell(closingLine); } ls->m_VtkContourPolyData->SetPoints(ls->m_ContourPoints); ls->m_VtkContourPolyData->SetLines(ls->m_ContourLines); ls->m_VtkContourPolyDataMapper->SetInputData(ls->m_VtkContourPolyData); ls->m_ContourActor->SetMapper(ls->m_VtkContourPolyDataMapper); ls->m_ContourActor->GetProperty()->SetLineWidth(m_LineWidth); ls->m_PropAssembly->AddPart(ls->m_ContourActor); } // the point set must be transformed in order to obtain the appropriate glyph orientation // according to the current view vtkSmartPointer transform = vtkSmartPointer::New(); vtkSmartPointer a, b = vtkSmartPointer::New(); a = geo2D->GetVtkTransform()->GetMatrix(); b->DeepCopy(a); // delete transformation from matrix, only take orientation b->SetElement(3, 3, 1); b->SetElement(2, 3, 0); b->SetElement(1, 3, 0); b->SetElement(0, 3, 0); b->SetElement(3, 2, 0); b->SetElement(3, 1, 0); b->SetElement(3, 0, 0); Vector3D spacing = geo2D->GetSpacing(); // If you find a way to simplyfy the following, feel free to change! b->SetElement(0, 0, b->GetElement(0, 0) / spacing[0]); b->SetElement(1, 0, b->GetElement(1, 0) / spacing[0]); b->SetElement(2, 0, b->GetElement(2, 0) / spacing[0]); b->SetElement(1, 1, b->GetElement(1, 1) / spacing[1]); b->SetElement(2, 1, b->GetElement(2, 1) / spacing[1]); b->SetElement(0, 2, b->GetElement(0, 2) / spacing[2]); b->SetElement(1, 2, b->GetElement(1, 2) / spacing[2]); b->SetElement(2, 2, b->GetElement(2, 2) / spacing[2]); transform->SetMatrix(b); //---- UNSELECTED POINTS -----// // apply properties to glyph ls->m_UnselectedGlyphSource2D->SetGlyphType(m_IDShapeProperty); if (m_FillShape) ls->m_UnselectedGlyphSource2D->FilledOn(); else ls->m_UnselectedGlyphSource2D->FilledOff(); // apply transform vtkSmartPointer transformFilterU = vtkSmartPointer::New(); transformFilterU->SetInputConnection(ls->m_UnselectedGlyphSource2D->GetOutputPort()); transformFilterU->SetTransform(transform); ls->m_VtkUnselectedPointListPolyData->SetPoints(ls->m_UnselectedPoints); ls->m_VtkUnselectedPointListPolyData->GetPointData()->SetVectors(ls->m_UnselectedScales); // apply transform of current plane to glyphs ls->m_UnselectedGlyph3D->SetSourceConnection(transformFilterU->GetOutputPort()); ls->m_UnselectedGlyph3D->SetInputData(ls->m_VtkUnselectedPointListPolyData); + ls->m_UnselectedGlyph3D->SetScaleFactor(m_FixedSizeOnScreen ? resolution : 1.0); ls->m_UnselectedGlyph3D->SetScaleModeToScaleByVector(); ls->m_UnselectedGlyph3D->SetVectorModeToUseVector(); ls->m_VtkUnselectedPolyDataMapper->SetInputConnection(ls->m_UnselectedGlyph3D->GetOutputPort()); ls->m_UnselectedActor->SetMapper(ls->m_VtkUnselectedPolyDataMapper); ls->m_UnselectedActor->GetProperty()->SetLineWidth(m_PointLineWidth); ls->m_PropAssembly->AddPart(ls->m_UnselectedActor); //---- SELECTED POINTS -----// ls->m_SelectedGlyphSource2D->SetGlyphTypeToDiamond(); ls->m_SelectedGlyphSource2D->CrossOn(); ls->m_SelectedGlyphSource2D->FilledOff(); // apply transform vtkSmartPointer transformFilterS = vtkSmartPointer::New(); transformFilterS->SetInputConnection(ls->m_SelectedGlyphSource2D->GetOutputPort()); transformFilterS->SetTransform(transform); ls->m_VtkSelectedPointListPolyData->SetPoints(ls->m_SelectedPoints); ls->m_VtkSelectedPointListPolyData->GetPointData()->SetVectors(ls->m_SelectedScales); // apply transform of current plane to glyphs ls->m_SelectedGlyph3D->SetSourceConnection(transformFilterS->GetOutputPort()); ls->m_SelectedGlyph3D->SetInputData(ls->m_VtkSelectedPointListPolyData); + ls->m_SelectedGlyph3D->SetScaleFactor(m_FixedSizeOnScreen ? resolution : 1.0); ls->m_SelectedGlyph3D->SetScaleModeToScaleByVector(); ls->m_SelectedGlyph3D->SetVectorModeToUseVector(); ls->m_VtkSelectedPolyDataMapper->SetInputConnection(ls->m_SelectedGlyph3D->GetOutputPort()); ls->m_SelectedActor->SetMapper(ls->m_VtkSelectedPolyDataMapper); ls->m_SelectedActor->GetProperty()->SetLineWidth(m_PointLineWidth); ls->m_PropAssembly->AddPart(ls->m_SelectedActor); } void mitk::PointSetVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { const mitk::DataNode *node = GetDataNode(); if (node == nullptr) return; LocalStorage *ls = m_LSH.GetLocalStorage(renderer); // check whether the input data has been changed bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode()); // toggle visibility bool visible = true; node->GetVisibility(visible, renderer, "visible"); if (!visible) { ls->m_UnselectedActor->VisibilityOff(); ls->m_SelectedActor->VisibilityOff(); ls->m_ContourActor->VisibilityOff(); ls->m_PropAssembly->VisibilityOff(); return; } else { ls->m_PropAssembly->VisibilityOn(); } node->GetBoolProperty("show contour", m_ShowContour, renderer); node->GetBoolProperty("close contour", m_CloseContour, renderer); node->GetBoolProperty("show points", m_ShowPoints, renderer); node->GetBoolProperty("show distances", m_ShowDistances, renderer); node->GetIntProperty("distance decimal digits", m_DistancesDecimalDigits, renderer); node->GetBoolProperty("show angles", m_ShowAngles, renderer); node->GetBoolProperty("show distant lines", m_ShowDistantLines, renderer); node->GetIntProperty("line width", m_LineWidth, renderer); node->GetIntProperty("point line width", m_PointLineWidth, renderer); if (!node->GetFloatProperty( "point 2D size", m_Point2DSize, renderer)) // re-defined to float 2015-08-13, keep a fallback { int oldPointSize = m_Point2DSize; if (node->GetIntProperty("point 2D size", oldPointSize, renderer)) { m_Point2DSize = oldPointSize; } } node->GetBoolProperty("Pointset.2D.fill shape", m_FillShape, renderer); node->GetFloatProperty("Pointset.2D.distance to plane", m_DistanceToPlane, renderer); + node->GetBoolProperty("Pointset.2D.fixed size on screen", m_FixedSizeOnScreen, renderer); mitk::PointSetShapeProperty::Pointer shape = dynamic_cast(this->GetDataNode()->GetProperty("Pointset.2D.shape", renderer)); if (shape.IsNotNull()) { m_IDShapeProperty = shape->GetPointSetShape(); } // check for color props and use it for rendering of selected/unselected points and contour // due to different params in VTK (double/float) we have to convert float opacity = 1.0; GetDataNode()->GetOpacity(opacity, renderer); // apply color and opacity if (m_ShowPoints) { float unselectedColor[4]; double selectedColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red ls->m_UnselectedActor->VisibilityOn(); ls->m_SelectedActor->VisibilityOn(); // check if there is a color property GetDataNode()->GetColor(unselectedColor); // get selected color property if (dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) != nullptr) { mitk::Color tmpColor = dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) ->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; // alpha value } else if (dynamic_cast( this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("selectedcolor")) != nullptr) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("selectedcolor")) ->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; // alpha value } ls->m_SelectedActor->GetProperty()->SetColor(selectedColor); ls->m_SelectedActor->GetProperty()->SetOpacity(opacity); ls->m_UnselectedActor->GetProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]); ls->m_UnselectedActor->GetProperty()->SetOpacity(opacity); } else { ls->m_UnselectedActor->VisibilityOff(); ls->m_SelectedActor->VisibilityOff(); } if (m_ShowContour) { double contourColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red ls->m_ContourActor->VisibilityOn(); // get contour color property if (dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) != nullptr) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) ->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } else if (dynamic_cast( this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("contourcolor")) != nullptr) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("contourcolor")) ->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } ls->m_ContourActor->GetProperty()->SetColor(contourColor); ls->m_ContourActor->GetProperty()->SetOpacity(opacity); } else { ls->m_ContourActor->VisibilityOff(); } if (needGenerateData) { // create new vtk render objects (e.g. a circle for a point) this->CreateVTKRenderObjects(renderer); } } void mitk::PointSetVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("line width", mitk::IntProperty::New(2), renderer, overwrite); node->AddProperty("point line width", mitk::IntProperty::New(1), renderer, overwrite); node->AddProperty("point 2D size", mitk::FloatProperty::New(6), renderer, overwrite); node->AddProperty("show contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("close contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("show points", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("show distances", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("distance decimal digits", mitk::IntProperty::New(2), renderer, overwrite); node->AddProperty("show angles", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("show distant lines", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(1), renderer, overwrite); node->AddProperty("Pointset.2D.fill shape", mitk::BoolProperty::New(false), renderer, overwrite); // fill or do not fill the glyph shape mitk::PointSetShapeProperty::Pointer pointsetShapeProperty = mitk::PointSetShapeProperty::New(); node->AddProperty("Pointset.2D.shape", pointsetShapeProperty, renderer, overwrite); node->AddProperty("Pointset.2D.distance to plane", mitk::FloatProperty::New(4.0f), renderer, overwrite); // show the point at a certain distance above/below the 2D imaging plane. + node->AddProperty("Pointset.2D.fixed size on screen", mitk::BoolProperty::New(false), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); }