diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarDoubleEllipse.h b/Modules/PlanarFigure/DataManagement/mitkPlanarDoubleEllipse.h index 37a9eb0f42..ff67568439 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarDoubleEllipse.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarDoubleEllipse.h @@ -1,61 +1,61 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkPlanarDoubleEllipse_h #define mitkPlanarDoubleEllipse_h #include #include namespace mitk { class MitkPlanarFigure_EXPORT PlanarDoubleEllipse : public PlanarFigure { public: mitkClassMacro(PlanarDoubleEllipse, PlanarFigure); itkFactorylessNewMacro(Self) itkCloneMacro(Self) unsigned int GetNumberOfSegments() const; void SetNumberOfSegments(unsigned int numSegments); virtual unsigned int GetMaximumNumberOfControlPoints() const; virtual unsigned int GetMinimumNumberOfControlPoints() const; virtual bool SetControlPoint(unsigned int index, const Point2D& point, bool createIfDoesNotExist = true); const unsigned int FEATURE_ID_MAJOR_AXIS; const unsigned int FEATURE_ID_MINOR_AXIS; const unsigned int FEATURE_ID_THICKNESS; protected: PlanarDoubleEllipse(); virtual ~PlanarDoubleEllipse(); - mitkCloneMacro(Self); + mitkCloneMacro(Self) virtual mitk::Point2D ApplyControlPointConstraints(unsigned int index, const Point2D& point); virtual void EvaluateFeaturesInternal(); virtual void GenerateHelperPolyLine(double, unsigned int); virtual void GeneratePolyLine(); private: unsigned int m_NumberOfSegments; bool m_ConstrainCircle; bool m_ConstrainThickness; }; } #endif diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.cpp index 8b1ffda3a7..730ada4928 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.cpp @@ -1,275 +1,280 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include "mitkPlanarEllipse.h" #include "mitkGeometry2D.h" #include "mitkProperties.h" #include mitk::PlanarEllipse::PlanarEllipse() - : m_MinRadius(0), + : FEATURE_ID_MAJOR_AXIS(Superclass::AddFeature("Major Axis", "mm")), + FEATURE_ID_MINOR_AXIS(Superclass::AddFeature("Minor Axis", "mm")), + m_MinRadius(0), m_MaxRadius(100), m_MinMaxRadiusContraintsActive(false), m_TreatAsCircle(true) { // Ellipse has three control points this->ResetNumberOfControlPoints( 4 ); this->SetNumberOfPolyLines( 2 ); this->SetProperty( "closed", mitk::BoolProperty::New(true) ); } mitk::PlanarEllipse::~PlanarEllipse() { } bool mitk::PlanarEllipse::SetControlPoint( unsigned int index, const Point2D &point, bool createIfDoesNotExist ) { if(index == 0) // moving center point and control points accordingly { const Point2D ¢erPoint = GetControlPoint( 0 ); Point2D boundaryPoint1 = GetControlPoint( 1 ); Point2D boundaryPoint2 = GetControlPoint( 2 ); Point2D boundaryPoint3 = GetControlPoint( 3 ); vnl_vector vec = (point.GetVnlVector() - centerPoint.GetVnlVector()); boundaryPoint1[0] += vec[0]; boundaryPoint1[1] += vec[1]; boundaryPoint2[0] += vec[0]; boundaryPoint2[1] += vec[1]; boundaryPoint3[0] += vec[0]; boundaryPoint3[1] += vec[1]; PlanarFigure::SetControlPoint( 0, point, createIfDoesNotExist ); PlanarFigure::SetControlPoint( 1, boundaryPoint1, createIfDoesNotExist ); PlanarFigure::SetControlPoint( 2, boundaryPoint2, createIfDoesNotExist ); PlanarFigure::SetControlPoint( 3, boundaryPoint3, createIfDoesNotExist ); return true; } else if (index < 3) { PlanarFigure::SetControlPoint( index, point, createIfDoesNotExist ); int otherIndex = index+1; if (otherIndex > 2) otherIndex = 1; const Point2D ¢erPoint = GetControlPoint( 0 ); Point2D otherPoint = GetControlPoint( otherIndex ); Point2D point3 = GetControlPoint( 3 ); Vector2D vec1 = point - centerPoint; Vector2D vec2; if (index == 1 && m_TreatAsCircle ) { float x = vec1[0]; vec2[0] = vec1[1]; vec2[1] = x; if (index==1) vec2[0] *= -1; else vec2[1] *= -1; otherPoint = centerPoint+vec2; PlanarFigure::SetControlPoint( otherIndex, otherPoint, createIfDoesNotExist ); float r = centerPoint.EuclideanDistanceTo(otherPoint); // adjust additional third control point Point2D p3 = this->GetControlPoint(3); Vector2D vec3; vec3[0] = p3[0]-centerPoint[0]; vec3[1] = p3[1]-centerPoint[1]; if (vec3[0]!=0 || vec3[1]!=0) { vec3.Normalize(); vec3 *= r; } else { vec3[0] = r; vec3[1] = 0; } point3 = centerPoint + vec3; PlanarFigure::SetControlPoint( 3, point3, createIfDoesNotExist ); } else if ( vec1.GetNorm() > 0 ) { float r = centerPoint.EuclideanDistanceTo(otherPoint); float x = vec1[0]; vec2[0] = vec1[1]; vec2[1] = x; if (index==1) vec2[0] *= -1; else vec2[1] *= -1; vec2.Normalize(); vec2 *= r; if ( vec2.GetNorm() > 0 ) { otherPoint = centerPoint+vec2; PlanarFigure::SetControlPoint( otherIndex, otherPoint, createIfDoesNotExist ); } // adjust third control point Vector2D vec3 = point3 - centerPoint; vec3.Normalize(); double r1 = centerPoint.EuclideanDistanceTo( GetControlPoint( 1 ) ); double r2 = centerPoint.EuclideanDistanceTo( GetControlPoint( 2 ) ); Point2D newPoint = centerPoint + vec3*std::max(r1, r2); PlanarFigure::SetControlPoint( 3, newPoint, createIfDoesNotExist ); m_TreatAsCircle = false; } return true; } else if (index == 3) { Point2D centerPoint = GetControlPoint( 0 ); Vector2D vec3 = point - centerPoint; vec3.Normalize(); double r1 = centerPoint.EuclideanDistanceTo( GetControlPoint( 1 ) ); double r2 = centerPoint.EuclideanDistanceTo( GetControlPoint( 2 ) ); Point2D newPoint = centerPoint + vec3*std::max(r1, r2); PlanarFigure::SetControlPoint( index, newPoint, createIfDoesNotExist ); m_TreatAsCircle = false; return true; } return false; } void mitk::PlanarEllipse::PlaceFigure( const mitk::Point2D &point ) { PlanarFigure::PlaceFigure( point ); m_SelectedControlPoint = 1; } mitk::Point2D mitk::PlanarEllipse::ApplyControlPointConstraints(unsigned int index, const Point2D &point) { return point; Point2D indexPoint; this->GetGeometry2D()->WorldToIndex( point, indexPoint ); BoundingBox::BoundsArrayType bounds = this->GetGeometry2D()->GetBounds(); if ( indexPoint[0] < bounds[0] ) { indexPoint[0] = bounds[0]; } if ( indexPoint[0] > bounds[1] ) { indexPoint[0] = bounds[1]; } if ( indexPoint[1] < bounds[2] ) { indexPoint[1] = bounds[2]; } if ( indexPoint[1] > bounds[3] ) { indexPoint[1] = bounds[3]; } Point2D constrainedPoint; this->GetGeometry2D()->IndexToWorld( indexPoint, constrainedPoint ); if(m_MinMaxRadiusContraintsActive) { if( index != 0) { const Point2D ¢erPoint = this->GetControlPoint(0); double euclideanDinstanceFromCenterToPoint1 = centerPoint.EuclideanDistanceTo(point); Vector2D vectorProjectedPoint; vectorProjectedPoint = point - centerPoint; vectorProjectedPoint.Normalize(); if( euclideanDinstanceFromCenterToPoint1 > m_MaxRadius ) { vectorProjectedPoint *= m_MaxRadius; constrainedPoint = centerPoint; constrainedPoint += vectorProjectedPoint; } else if( euclideanDinstanceFromCenterToPoint1 < m_MinRadius ) { vectorProjectedPoint *= m_MinRadius; constrainedPoint = centerPoint; constrainedPoint += vectorProjectedPoint; } } } return constrainedPoint; } void mitk::PlanarEllipse::GeneratePolyLine() { // clear the PolyLine-Contrainer, it will be reconstructed soon enough... this->ClearPolyLines(); const Point2D ¢erPoint = GetControlPoint( 0 ); const Point2D &boundaryPoint1 = GetControlPoint( 1 ); const Point2D &boundaryPoint2 = GetControlPoint( 2 ); Vector2D dir = boundaryPoint1 - centerPoint; dir.Normalize(); vnl_matrix_fixed rot; // differentiate between clockwise and counterclockwise rotation int start = 0; int end = 64; if (dir[1]<0) { dir[0] = -dir[0]; start = -32; end = 32; } // construct rotation matrix to align ellipse with control point vector rot[0][0] = dir[0]; rot[1][1] = rot[0][0]; rot[1][0] = sin(acos(rot[0][0])); rot[0][1] = -rot[1][0]; double radius1 = centerPoint.EuclideanDistanceTo( boundaryPoint1 ); double radius2 = centerPoint.EuclideanDistanceTo( boundaryPoint2 ); // Generate poly-line with 64 segments for ( int t = start; t < end; ++t ) { double alpha = (double) t * vnl_math::pi / 32.0; // construct the new polyline point ... vnl_vector_fixed< float, 2 > vec; vec[0] = radius1 * cos( alpha ); vec[1] = radius2 * sin( alpha ); vec = rot*vec; Point2D polyLinePoint; polyLinePoint[0] = centerPoint[0] + vec[0]; polyLinePoint[1] = centerPoint[1] + vec[1]; // ... and append it to the PolyLine. // No extending supported here, so we can set the index of the PolyLineElement to '0' this->AppendPointToPolyLine(0, polyLinePoint); } this->AppendPointToPolyLine(1, centerPoint); this->AppendPointToPolyLine(1, this->GetControlPoint(3)); } void mitk::PlanarEllipse::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/) { // A circle does not require a helper object } void mitk::PlanarEllipse::EvaluateFeaturesInternal() { + Point2D centerPoint = this->GetControlPoint(0); + this->SetQuantity(FEATURE_ID_MAJOR_AXIS, 2 * centerPoint.EuclideanDistanceTo(this->GetControlPoint(1))); + this->SetQuantity(FEATURE_ID_MINOR_AXIS, 2 * centerPoint.EuclideanDistanceTo(this->GetControlPoint(2))); } void mitk::PlanarEllipse::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.h b/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.h index 3b6f9fdcde..88c033c55c 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.h @@ -1,137 +1,140 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITK_PLANAR_ELLIPSE_H_ #define _MITK_PLANAR_ELLIPSE_H_ #include "mitkPlanarFigure.h" #include namespace mitk { class Geometry2D; /** * \brief Implementation of PlanarFigure representing a circle * through two control points */ class MitkPlanarFigure_EXPORT PlanarEllipse : public PlanarFigure { public: mitkClassMacro( PlanarEllipse, PlanarFigure ) itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** \brief Place figure in its minimal configuration (a point at least) * onto the given 2D geometry. * * Must be implemented in sub-classes. */ virtual void PlaceFigure( const Point2D &point ); bool SetControlPoint( unsigned int index, const Point2D &point, bool createIfDoesNotExist = true ); /** \brief Ellipse has 3 control points per definition. */ unsigned int GetMinimumNumberOfControlPoints() const { return 4; } /** \brief Ellipse has 3 control points per definition. */ unsigned int GetMaximumNumberOfControlPoints() const { return 4; } /** \brief Sets the minimum radius */ void SetMinimumRadius( double radius ) { m_MinRadius = radius; } /** \brief Gets the minimum radius */ double GetMinimumRadius() { return m_MinRadius; } /** \brief Sets the maximum radius */ void SetMaximumRadius( double radius ) { m_MaxRadius = radius; } /** \brief Gets the minimum radius */ double GetMaximumRadius() { return m_MaxRadius; } void ActivateMinMaxRadiusContstraints( bool active ) { m_MinMaxRadiusContraintsActive = active; } /** \brief Treat ellipse as circle (equal radii) */ void SetTreatAsCircle( bool active ) { m_TreatAsCircle = active; } + const unsigned int FEATURE_ID_MAJOR_AXIS; + const unsigned int FEATURE_ID_MINOR_AXIS; + protected: PlanarEllipse(); virtual ~PlanarEllipse(); mitkCloneMacro(Self); /** \brief Generates the poly-line representation of the planar figure. */ virtual void GeneratePolyLine(); /** \brief Generates the poly-lines that should be drawn the same size regardless of zoom.*/ virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight); /** \brief Spatially constrain control points of second (orthogonal) line */ virtual Point2D ApplyControlPointConstraints( unsigned int index, const Point2D& point ); /** \brief Calculates feature quantities of the planar figure. */ virtual void EvaluateFeaturesInternal(); virtual void PrintSelf( std::ostream &os, itk::Indent indent ) const; //Member variables: double m_MinRadius; double m_MaxRadius; bool m_MinMaxRadiusContraintsActive; bool m_TreatAsCircle; private: }; } // namespace mitk #endif //_MITK_PLANAR_ELLIPSE_H_ diff --git a/Modules/PlanarFigure/IO/mitkPlanarFigureSerializer.h b/Modules/PlanarFigure/IO/mitkPlanarFigureSerializer.h index 657c6a5930..94d4d1319c 100644 --- a/Modules/PlanarFigure/IO/mitkPlanarFigureSerializer.h +++ b/Modules/PlanarFigure/IO/mitkPlanarFigureSerializer.h @@ -1,40 +1,40 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkPlanarFigureSerializer_h_included #define mitkPlanarFigureSerializer_h_included #include #include "mitkBaseDataSerializer.h" namespace mitk { /** \brief Serializes mitk::Surface for mitk::SceneIO */ class MitkPlanarFigure_EXPORT PlanarFigureSerializer : public BaseDataSerializer { public: - mitkClassMacro( PlanarFigureSerializer, BaseDataSerializer ); + mitkClassMacro( PlanarFigureSerializer, BaseDataSerializer ) itkFactorylessNewMacro(Self) itkCloneMacro(Self) virtual std::string Serialize(); protected: PlanarFigureSerializer(); virtual ~PlanarFigureSerializer(); }; } // namespace #endif diff --git a/Modules/PlanarFigure/IO/mitkPlanarFigureSubclassesSerializer.cpp b/Modules/PlanarFigure/IO/mitkPlanarFigureSubclassesSerializer.cpp index b26d34dcca..af9ed19341 100644 --- a/Modules/PlanarFigure/IO/mitkPlanarFigureSubclassesSerializer.cpp +++ b/Modules/PlanarFigure/IO/mitkPlanarFigureSubclassesSerializer.cpp @@ -1,56 +1,57 @@ /*=================================================================== 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 mitkPlanarFiguresSubclassesSerializer_h_included #define mitkPlanarFiguresSubclassesSerializer_h_included #include "mitkPlanarFigureSerializer.h" #define MITK_REGISTER_PF_SUB_SERIALIZER(classname) \ \ namespace mitk \ { \ \ class MitkPlanarFigure_EXPORT classname ## Serializer : public PlanarFigureSerializer \ { \ public: \ \ mitkClassMacro( classname ## Serializer, PlanarFigureSerializer ) \ itkFactorylessNewMacro(Self) \ itkCloneMacro(Self) \ \ protected: \ \ classname ## Serializer () {} \ virtual ~classname ## Serializer () {} \ }; \ \ } \ \ MITK_REGISTER_SERIALIZER( classname ## Serializer ); -MITK_REGISTER_PF_SUB_SERIALIZER(PlanarAngle); -MITK_REGISTER_PF_SUB_SERIALIZER(PlanarCircle); -MITK_REGISTER_PF_SUB_SERIALIZER(PlanarCross); -MITK_REGISTER_PF_SUB_SERIALIZER(PlanarFourPointAngle); -MITK_REGISTER_PF_SUB_SERIALIZER(PlanarLine); -MITK_REGISTER_PF_SUB_SERIALIZER(PlanarPolygon); -MITK_REGISTER_PF_SUB_SERIALIZER(PlanarRectangle); -MITK_REGISTER_PF_SUB_SERIALIZER(PlanarEllipse); -MITK_REGISTER_PF_SUB_SERIALIZER(PlanarDoubleEllipse); -MITK_REGISTER_PF_SUB_SERIALIZER(PlanarBezierCurve); +MITK_REGISTER_PF_SUB_SERIALIZER(PlanarAngle) +MITK_REGISTER_PF_SUB_SERIALIZER(PlanarCircle) +MITK_REGISTER_PF_SUB_SERIALIZER(PlanarCross) +MITK_REGISTER_PF_SUB_SERIALIZER(PlanarFourPointAngle) +MITK_REGISTER_PF_SUB_SERIALIZER(PlanarLine) +MITK_REGISTER_PF_SUB_SERIALIZER(PlanarPolygon) +MITK_REGISTER_PF_SUB_SERIALIZER(PlanarRectangle) +MITK_REGISTER_PF_SUB_SERIALIZER(PlanarEllipse) +MITK_REGISTER_PF_SUB_SERIALIZER(PlanarDoubleEllipse) +MITK_REGISTER_PF_SUB_SERIALIZER(PlanarBezierCurve) +MITK_REGISTER_PF_SUB_SERIALIZER(PlanarSubdivisionPolygon) #endif diff --git a/Modules/QtWidgetsExt/resources/PlanarEllipse_48.png b/Modules/QtWidgetsExt/resources/PlanarEllipse_48.png new file mode 100644 index 0000000000..58207234b2 Binary files /dev/null and b/Modules/QtWidgetsExt/resources/PlanarEllipse_48.png differ diff --git a/Modules/QtWidgetsExt/resources/PlanarSubdivisionPolygon_48.png b/Modules/QtWidgetsExt/resources/PlanarSubdivisionPolygon_48.png new file mode 100644 index 0000000000..ce89e19058 Binary files /dev/null and b/Modules/QtWidgetsExt/resources/PlanarSubdivisionPolygon_48.png differ diff --git a/Modules/QtWidgetsExt/resources/QtWidgetsExt.qrc b/Modules/QtWidgetsExt/resources/QtWidgetsExt.qrc index de5c63d03e..2d4d6d0fbc 100644 --- a/Modules/QtWidgetsExt/resources/QtWidgetsExt.qrc +++ b/Modules/QtWidgetsExt/resources/QtWidgetsExt.qrc @@ -1,44 +1,46 @@ btnAddPointSet.png btnClear.xpm btnCube.xpm btnCylinder.xpm btnDown.xpm btnEllipsoid.xpm btnLoad.xpm btnMoveDown.png btnMoveUp.png btnPyramid.xpm btnRemovePoint.png btnSave.xpm btnSetPoints.png btnSetPoints.xpm btnSetPointsManually.xpm btnSetSeedPoint.xpm btnSwapSets.png btnUp.xpm cross.png d3.v2.js Histogram.css Histogram.html Histogram.js icon_seedpoint.png Logo_mbiATdkfz_gross.png Logo_mbiATdkfz_small.png logo_mint-medical.png ModuleView.png QmitkStandardViewsDialogBar.xpm PlanarAngle_48.png PlanarBezierCurve_48.png PlanarCircle_48.png PlanarDoubleEllipse_48.png + PlanarEllipse_48.png PlanarFourPointAngle_48.png PlanarLine_48.png PlanarPath_48.png PlanarPolygon_48.png PlanarRectangle_48.png + PlanarSubdivisionPolygon_48.png play.xpm stop.xpm diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/resources/ellipse.png b/Plugins/org.mitk.gui.qt.measurementtoolbox/resources/ellipse.png new file mode 100644 index 0000000000..794c9779ea Binary files /dev/null and b/Plugins/org.mitk.gui.qt.measurementtoolbox/resources/ellipse.png differ diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/resources/measurement.qrc b/Plugins/org.mitk.gui.qt.measurementtoolbox/resources/measurement.qrc index 0ba2667534..e0c10fc710 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/resources/measurement.qrc +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/resources/measurement.qrc @@ -1,17 +1,19 @@ angle.png arrow.png circle.png + ellipse.png four-point-angle.png - line.png - measurement.png - path.png - polygon.png - rectangle.png - text.png + line.png + measurement.png + path.png + polygon.png + rectangle.png + text.png doubleellipse.png beziercurve.png + subdivisionpolygon.png diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/resources/subdivisionpolygon.png b/Plugins/org.mitk.gui.qt.measurementtoolbox/resources/subdivisionpolygon.png new file mode 100644 index 0000000000..1a7e82a5e2 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.measurementtoolbox/resources/subdivisionpolygon.png differ diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp index 652391506d..0dfb7db6f3 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp @@ -1,914 +1,960 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #define MEASUREMENT_DEBUG MITK_DEBUG("QmitkMeasurementView") << __LINE__ << ": " #include "QmitkMeasurementView.h" #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include "mitkPluginActivator.h" #include "usModuleRegistry.h" template static T* GetService() { ctkPluginContext* context = mitk::PluginActivator::GetContext(); ctkServiceReference serviceRef = context->getServiceReference(); return serviceRef ? context->getService(serviceRef) : NULL; } struct QmitkPlanarFigureData { QmitkPlanarFigureData() : m_Figure(0), m_EndPlacementObserverTag(0), m_SelectObserverTag(0), m_StartInteractionObserverTag(0), m_EndInteractionObserverTag(0) { } mitk::PlanarFigure* m_Figure; unsigned int m_EndPlacementObserverTag; unsigned int m_SelectObserverTag; unsigned int m_StartInteractionObserverTag; unsigned int m_EndInteractionObserverTag; }; struct QmitkMeasurementViewData { QmitkMeasurementViewData() : m_LineCounter(0), m_PathCounter(0), m_AngleCounter(0), - m_FourPointAngleCounter(0), m_EllipseCounter(0), m_DoubleEllipseCounter(0), - m_RectangleCounter(0), m_PolygonCounter(0), m_BezierCurveCounter(0), - m_UnintializedPlanarFigure(false) + m_FourPointAngleCounter(0), m_CircleCounter(0), m_EllipseCounter(0), + m_DoubleEllipseCounter(0), m_RectangleCounter(0), m_PolygonCounter(0), + m_BezierCurveCounter(0), m_SubdivisionPolygonCounter(0), m_UnintializedPlanarFigure(false) { } // internal vars unsigned int m_LineCounter; unsigned int m_PathCounter; unsigned int m_AngleCounter; unsigned int m_FourPointAngleCounter; + unsigned int m_CircleCounter; unsigned int m_EllipseCounter; unsigned int m_DoubleEllipseCounter; unsigned int m_RectangleCounter; unsigned int m_PolygonCounter; unsigned int m_BezierCurveCounter; + unsigned int m_SubdivisionPolygonCounter; QList m_CurrentSelection; std::map m_DataNodeToPlanarFigureData; mitk::WeakPointer m_SelectedImageNode; bool m_UnintializedPlanarFigure; // WIDGETS QWidget* m_Parent; QLabel* m_SelectedImageLabel; QAction* m_DrawLine; QAction* m_DrawPath; QAction* m_DrawAngle; QAction* m_DrawFourPointAngle; - QAction* m_DrawDoubleEllipse; QAction* m_DrawRectangle; QAction* m_DrawPolygon; + QAction* m_DrawCircle; QAction* m_DrawEllipse; + QAction* m_DrawDoubleEllipse; QAction* m_DrawBezierCurve; + QAction* m_DrawSubdivisionPolygon; QToolBar* m_DrawActionsToolBar; QActionGroup* m_DrawActionsGroup; QTextBrowser* m_SelectedPlanarFiguresText; QPushButton* m_CopyToClipboard; QGridLayout* m_Layout; }; const std::string QmitkMeasurementView::VIEW_ID = "org.mitk.views.measurement"; QmitkMeasurementView::QmitkMeasurementView() : d( new QmitkMeasurementViewData ) { } QmitkMeasurementView::~QmitkMeasurementView() { this->RemoveAllInteractors(); delete d; } void QmitkMeasurementView::CreateQtPartControl(QWidget* parent) { d->m_Parent = parent; // image label QLabel* selectedImageLabel = new QLabel("Reference Image: "); d->m_SelectedImageLabel = new QLabel; d->m_SelectedImageLabel->setStyleSheet("font-weight: bold;"); d->m_DrawActionsToolBar = new QToolBar; d->m_DrawActionsGroup = new QActionGroup(this); d->m_DrawActionsGroup->setExclusive(true); //# add actions MEASUREMENT_DEBUG << "Draw Line"; QAction* currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/line.png"), "Draw Line"); currentAction->setCheckable(true); d->m_DrawLine = currentAction; d->m_DrawActionsToolBar->addAction(currentAction); d->m_DrawActionsGroup->addAction(currentAction); MEASUREMENT_DEBUG << "Draw Path"; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/path.png"), "Draw Path"); currentAction->setCheckable(true); d->m_DrawPath = currentAction; d->m_DrawActionsToolBar->addAction(currentAction); d->m_DrawActionsGroup->addAction(currentAction); MEASUREMENT_DEBUG << "Draw Angle"; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/angle.png"), "Draw Angle"); currentAction->setCheckable(true); d->m_DrawAngle = currentAction; d->m_DrawActionsToolBar->addAction(currentAction); d->m_DrawActionsGroup->addAction(currentAction); MEASUREMENT_DEBUG << "Draw Four Point Angle"; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/four-point-angle.png"), "Draw Four Point Angle"); currentAction->setCheckable(true); d->m_DrawFourPointAngle = currentAction; d->m_DrawActionsToolBar->addAction(currentAction); d->m_DrawActionsGroup->addAction(currentAction); MEASUREMENT_DEBUG << "Draw Circle"; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/circle.png"), "Draw Circle"); currentAction->setCheckable(true); + d->m_DrawCircle = currentAction; + d->m_DrawActionsToolBar->addAction(currentAction); + d->m_DrawActionsGroup->addAction(currentAction); + + MEASUREMENT_DEBUG << "Draw Ellipse"; + currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/ellipse.png"), "Draw Ellipse"); + currentAction->setCheckable(true); d->m_DrawEllipse = currentAction; d->m_DrawActionsToolBar->addAction(currentAction); d->m_DrawActionsGroup->addAction(currentAction); + MEASUREMENT_DEBUG << "Draw Double Ellipse"; + currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/doubleellipse.png"), "Draw Double Ellipse"); + currentAction->setCheckable(true); + d->m_DrawDoubleEllipse = currentAction; + d->m_DrawActionsToolBar->addAction(currentAction); + d->m_DrawActionsGroup->addAction(currentAction); + MEASUREMENT_DEBUG << "Draw Rectangle"; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/rectangle.png"), "Draw Rectangle"); currentAction->setCheckable(true); d->m_DrawRectangle = currentAction; d->m_DrawActionsToolBar->addAction(currentAction); d->m_DrawActionsGroup->addAction(currentAction); MEASUREMENT_DEBUG << "Draw Polygon"; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/polygon.png"), "Draw Polygon"); currentAction->setCheckable(true); d->m_DrawPolygon = currentAction; d->m_DrawActionsToolBar->addAction(currentAction); d->m_DrawActionsGroup->addAction(currentAction); - MEASUREMENT_DEBUG << "Draw Double Ellipse"; - currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/doubleellipse.png"), "Draw Double Ellipse"); + MEASUREMENT_DEBUG << "Draw Bezier Curve"; + currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/beziercurve.png"), "Draw Bezier Curve"); currentAction->setCheckable(true); - d->m_DrawDoubleEllipse = currentAction; + d->m_DrawBezierCurve = currentAction; d->m_DrawActionsToolBar->addAction(currentAction); d->m_DrawActionsGroup->addAction(currentAction); - MEASUREMENT_DEBUG << "Draw Bezier Curve"; - currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/beziercurve.png"), "Draw Bezier Curve"); + MEASUREMENT_DEBUG << "Draw Subdivision Polygon"; + currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/subdivisionpolygon.png"), "Draw Subdivision Polygon"); currentAction->setCheckable(true); - d->m_DrawBezierCurve = currentAction; + d->m_DrawSubdivisionPolygon = currentAction; d->m_DrawActionsToolBar->addAction(currentAction); d->m_DrawActionsGroup->addAction(currentAction); // planar figure details text d->m_SelectedPlanarFiguresText = new QTextBrowser; // copy to clipboard button d->m_CopyToClipboard = new QPushButton("Copy to Clipboard"); d->m_Layout = new QGridLayout; d->m_Layout->addWidget(selectedImageLabel, 0, 0, 1, 1); d->m_Layout->addWidget(d->m_SelectedImageLabel, 0, 1, 1, 1); d->m_Layout->addWidget(d->m_DrawActionsToolBar, 1, 0, 1, 2); d->m_Layout->addWidget(d->m_SelectedPlanarFiguresText, 2, 0, 1, 2); d->m_Layout->addWidget(d->m_CopyToClipboard, 3, 0, 1, 2); d->m_Parent->setLayout(d->m_Layout); // create connections this->CreateConnections(); // readd interactors and observers this->AddAllInteractors(); } void QmitkMeasurementView::CreateConnections() { QObject::connect( d->m_DrawLine, SIGNAL( triggered(bool) ), this, SLOT( ActionDrawLineTriggered(bool) ) ); QObject::connect( d->m_DrawPath, SIGNAL( triggered(bool) ), this, SLOT( ActionDrawPathTriggered(bool) ) ); QObject::connect( d->m_DrawAngle, SIGNAL( triggered(bool) ), this, SLOT( ActionDrawAngleTriggered(bool) ) ); QObject::connect( d->m_DrawFourPointAngle, SIGNAL( triggered(bool) ), this, SLOT( ActionDrawFourPointAngleTriggered(bool) ) ); + QObject::connect( d->m_DrawCircle, SIGNAL( triggered(bool) ), this, SLOT( ActionDrawCircleTriggered(bool) ) ); QObject::connect( d->m_DrawEllipse, SIGNAL( triggered(bool) ), this, SLOT( ActionDrawEllipseTriggered(bool) ) ); QObject::connect( d->m_DrawDoubleEllipse, SIGNAL( triggered(bool) ), this, SLOT( ActionDrawDoubleEllipseTriggered(bool) ) ); QObject::connect( d->m_DrawRectangle, SIGNAL( triggered(bool) ), this, SLOT( ActionDrawRectangleTriggered(bool) ) ); QObject::connect( d->m_DrawPolygon, SIGNAL( triggered(bool) ), this, SLOT( ActionDrawPolygonTriggered(bool) ) ); QObject::connect( d->m_DrawBezierCurve, SIGNAL( triggered(bool) ), this, SLOT( ActionDrawBezierCurveTriggered(bool) ) ); + QObject::connect( d->m_DrawSubdivisionPolygon, SIGNAL( triggered(bool) ), this, SLOT( ActionDrawSubdivisionPolygonTriggered(bool) ) ); QObject::connect( d->m_CopyToClipboard, SIGNAL( clicked(bool) ), this, SLOT( CopyToClipboard(bool) ) ); } void QmitkMeasurementView::NodeAdded( const mitk::DataNode* node ) { // add observer for selection in renderwindow mitk::PlanarFigure* figure = dynamic_cast(node->GetData()); bool isPositionMarker (false); node->GetBoolProperty("isContourMarker", isPositionMarker); if( figure && !isPositionMarker ) { MEASUREMENT_DEBUG << "figure added. will add interactor if needed."; mitk::PlanarFigureInteractor::Pointer figureInteractor = dynamic_cast(node->GetDataInteractor().GetPointer() ); mitk::DataNode* nonConstNode = const_cast( node ); if(figureInteractor.IsNull()) { figureInteractor = mitk::PlanarFigureInteractor::New(); us::Module* planarFigureModule = us::ModuleRegistry::GetModule( "MitkPlanarFigure" ); figureInteractor->LoadStateMachine("PlanarFigureInteraction.xml", planarFigureModule ); figureInteractor->SetEventConfig( "PlanarFigureConfig.xml", planarFigureModule ); figureInteractor->SetDataNode( nonConstNode ); // nonConstNode->SetBoolProperty( "planarfigure.isextendable", true ); } else { // just to be sure that the interactor is not added twice // mitk::GlobalInteraction::GetInstance()->RemoveInteractor(figureInteractor); } MEASUREMENT_DEBUG << "adding interactor to globalinteraction"; // mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); MEASUREMENT_DEBUG << "will now add observers for planarfigure"; QmitkPlanarFigureData data; data.m_Figure = figure; // add observer for event when figure has been placed typedef itk::SimpleMemberCommand< QmitkMeasurementView > SimpleCommandType; SimpleCommandType::Pointer initializationCommand = SimpleCommandType::New(); initializationCommand->SetCallbackFunction( this, &QmitkMeasurementView::PlanarFigureInitialized ); data.m_EndPlacementObserverTag = figure->AddObserver( mitk::EndPlacementPlanarFigureEvent(), initializationCommand ); // add observer for event when figure is picked (selected) typedef itk::MemberCommand< QmitkMeasurementView > MemberCommandType; MemberCommandType::Pointer selectCommand = MemberCommandType::New(); selectCommand->SetCallbackFunction( this, &QmitkMeasurementView::PlanarFigureSelected ); data.m_SelectObserverTag = figure->AddObserver( mitk::SelectPlanarFigureEvent(), selectCommand ); // add observer for event when interaction with figure starts SimpleCommandType::Pointer startInteractionCommand = SimpleCommandType::New(); startInteractionCommand->SetCallbackFunction( this, &QmitkMeasurementView::DisableCrosshairNavigation); data.m_StartInteractionObserverTag = figure->AddObserver( mitk::StartInteractionPlanarFigureEvent(), startInteractionCommand ); // add observer for event when interaction with figure starts SimpleCommandType::Pointer endInteractionCommand = SimpleCommandType::New(); endInteractionCommand->SetCallbackFunction( this, &QmitkMeasurementView::EnableCrosshairNavigation); data.m_EndInteractionObserverTag = figure->AddObserver( mitk::EndInteractionPlanarFigureEvent(), endInteractionCommand ); // adding to the map of tracked planarfigures d->m_DataNodeToPlanarFigureData[nonConstNode] = data; } this->CheckForTopMostVisibleImage(); } void QmitkMeasurementView::NodeChanged(const mitk::DataNode* node) { // DETERMINE IF WE HAVE TO RENEW OUR DETAILS TEXT (ANY NODE CHANGED IN OUR SELECTION?) bool renewText = false; for( int i=0; i < d->m_CurrentSelection.size(); ++i ) { if( node == d->m_CurrentSelection.at(i) ) { renewText = true; break; } } if(renewText) { MEASUREMENT_DEBUG << "Selected nodes changed. Refreshing text."; this->UpdateMeasurementText(); } this->CheckForTopMostVisibleImage(); } void QmitkMeasurementView::CheckForTopMostVisibleImage(mitk::DataNode* _NodeToNeglect) { d->m_SelectedImageNode = this->DetectTopMostVisibleImage().GetPointer(); if( d->m_SelectedImageNode.GetPointer() == _NodeToNeglect ) d->m_SelectedImageNode = 0; if( d->m_SelectedImageNode.IsNotNull() && d->m_UnintializedPlanarFigure == false ) { MEASUREMENT_DEBUG << "Reference image found"; d->m_SelectedImageLabel->setText( QString::fromStdString( d->m_SelectedImageNode->GetName() ) ); d->m_DrawActionsToolBar->setEnabled(true); MEASUREMENT_DEBUG << "Updating Measurement text"; } else { MEASUREMENT_DEBUG << "No reference image available. Will disable actions for creating new planarfigures"; if( d->m_UnintializedPlanarFigure == false ) d->m_SelectedImageLabel->setText( "No visible image available." ); d->m_DrawActionsToolBar->setEnabled(false); } } void QmitkMeasurementView::NodeRemoved(const mitk::DataNode* node) { MEASUREMENT_DEBUG << "node removed from data storage"; mitk::DataNode* nonConstNode = const_cast(node); std::map::iterator it = d->m_DataNodeToPlanarFigureData.find(nonConstNode); bool isFigureFinished = false; bool isPlaced = false; if( it != d->m_DataNodeToPlanarFigureData.end() ) { QmitkPlanarFigureData& data = it->second; // remove observers data.m_Figure->RemoveObserver( data.m_EndPlacementObserverTag ); data.m_Figure->RemoveObserver( data.m_SelectObserverTag ); data.m_Figure->RemoveObserver( data.m_StartInteractionObserverTag ); data.m_Figure->RemoveObserver( data.m_EndInteractionObserverTag ); MEASUREMENT_DEBUG << "removing from the list of tracked planar figures"; isFigureFinished = data.m_Figure->GetPropertyList()->GetBoolProperty("initiallyplaced",isPlaced); if (!isFigureFinished) { // if the property does not yet exist or is false, drop the datanode PlanarFigureInitialized(); // normally called when a figure is finished, to reset all buttons } d->m_DataNodeToPlanarFigureData.erase( it ); } mitk::TNodePredicateDataType::Pointer isPlanarFigure = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = GetDataStorage()->GetDerivations(node,isPlanarFigure); for (unsigned int x = 0; x < nodes->size(); x++) { mitk::PlanarFigure* planarFigure = dynamic_cast (nodes->at(x)->GetData()); if (planarFigure != NULL) { isFigureFinished = planarFigure->GetPropertyList()->GetBoolProperty("initiallyplaced",isPlaced); if (!isFigureFinished) { // if the property does not yet exist or is false, drop the datanode GetDataStorage()->Remove(nodes->at(x)); if( !d->m_DataNodeToPlanarFigureData.empty() ) { std::map::iterator it2 = d->m_DataNodeToPlanarFigureData.find(nodes->at(x)); //check if returned it2 valid if( it2 != d->m_DataNodeToPlanarFigureData.end() ) { d->m_DataNodeToPlanarFigureData.erase( it2 );// removing planar figure from tracked figure list PlanarFigureInitialized(); // normally called when a figure is finished, to reset all buttons EnableCrosshairNavigation(); } } } } } this->CheckForTopMostVisibleImage(nonConstNode); } void QmitkMeasurementView::PlanarFigureSelected( itk::Object* object, const itk::EventObject& ) { MEASUREMENT_DEBUG << "planar figure " << object << " selected"; std::map::iterator it = d->m_DataNodeToPlanarFigureData.begin(); d->m_CurrentSelection.clear(); while( it != d->m_DataNodeToPlanarFigureData.end()) { mitk::DataNode* node = it->first; QmitkPlanarFigureData& data = it->second; if( data.m_Figure == object ) { MITK_DEBUG << "selected node found. enabling selection"; node->SetSelected(true); d->m_CurrentSelection.push_back( node ); } else { node->SetSelected(false); } ++it; } this->UpdateMeasurementText(); this->RequestRenderWindowUpdate(); } void QmitkMeasurementView::PlanarFigureInitialized() { MEASUREMENT_DEBUG << "planar figure initialized"; d->m_UnintializedPlanarFigure = false; d->m_DrawActionsToolBar->setEnabled(true); d->m_DrawLine->setChecked(false); d->m_DrawPath->setChecked(false); d->m_DrawAngle->setChecked(false); d->m_DrawFourPointAngle->setChecked(false); + d->m_DrawCircle->setChecked(false); d->m_DrawEllipse->setChecked(false); + d->m_DrawDoubleEllipse->setChecked(false); d->m_DrawRectangle->setChecked(false); d->m_DrawPolygon->setChecked(false); - d->m_DrawDoubleEllipse->setChecked(false); d->m_DrawBezierCurve->setChecked(false); + d->m_DrawSubdivisionPolygon->setChecked(false); } void QmitkMeasurementView::SetFocus() { d->m_SelectedImageLabel->setFocus(); } void QmitkMeasurementView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList &nodes) { MEASUREMENT_DEBUG << "Determine the top most visible image"; MEASUREMENT_DEBUG << "The PlanarFigure interactor will take the currently visible PlaneGeometry from the slice navigation controller"; this->CheckForTopMostVisibleImage(); MEASUREMENT_DEBUG << "refreshing selection and detailed text"; d->m_CurrentSelection = nodes; this->UpdateMeasurementText(); // bug 16600: deselecting all planarfigures by clicking on datamanager when no node is selected if(d->m_CurrentSelection.size() == 0) { mitk::TNodePredicateDataType::Pointer isPlanarFigure = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer planarFigures = this->GetDataStorage()->GetSubset( isPlanarFigure ); // setting all planar figures which are not helper objects not selected for(mitk::DataStorage::SetOfObjects::ConstIterator it=planarFigures->Begin(); it!=planarFigures->End(); it++) { mitk::DataNode* node = it.Value(); bool isHelperObject(false); node->GetBoolProperty("helper object", isHelperObject); if(!isHelperObject) { node->SetSelected(false); } } } for( int i=d->m_CurrentSelection.size()-1; i>= 0; --i) { mitk::DataNode* node = d->m_CurrentSelection.at(i); mitk::PlanarFigure* _PlanarFigure = dynamic_cast (node->GetData()); // the last selected planar figure if (_PlanarFigure && _PlanarFigure->GetGeometry2D()) { QmitkRenderWindow* selectedRenderWindow = 0; bool PlanarFigureInitializedWindow = false; mitk::ILinkedRenderWindowPart* linkedRenderWindow = dynamic_cast(this->GetRenderWindowPart()); if(! linkedRenderWindow ) { return; } QmitkRenderWindow* RenderWindow1 = linkedRenderWindow->GetQmitkRenderWindow( "axial") ; QmitkRenderWindow* RenderWindow2 = linkedRenderWindow->GetQmitkRenderWindow( "sagittal") ; QmitkRenderWindow* RenderWindow3 = linkedRenderWindow->GetQmitkRenderWindow( "coronal") ; QmitkRenderWindow* RenderWindow4 = linkedRenderWindow->GetQmitkRenderWindow( "3d") ; if (node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow1->GetRenderer())) { selectedRenderWindow = RenderWindow1; } if (!selectedRenderWindow && node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow2->GetRenderer())) { selectedRenderWindow = RenderWindow2; } if (!selectedRenderWindow && node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow,RenderWindow3->GetRenderer())) { selectedRenderWindow = RenderWindow3; } if (!selectedRenderWindow && node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow4->GetRenderer())) { selectedRenderWindow = RenderWindow4; } const mitk::PlaneGeometry* _PlaneGeometry = dynamic_cast (_PlanarFigure->GetGeometry2D()); mitk::VnlVector normal = _PlaneGeometry->GetNormalVnl(); mitk::Geometry2D::ConstPointer worldGeometry1 = RenderWindow1->GetRenderer()->GetCurrentWorldGeometry2D(); mitk::PlaneGeometry::ConstPointer _Plane1 = dynamic_cast( worldGeometry1.GetPointer() ); mitk::VnlVector normal1 = _Plane1->GetNormalVnl(); mitk::Geometry2D::ConstPointer worldGeometry2 = RenderWindow2->GetRenderer()->GetCurrentWorldGeometry2D(); mitk::PlaneGeometry::ConstPointer _Plane2 = dynamic_cast( worldGeometry2.GetPointer() ); mitk::VnlVector normal2 = _Plane2->GetNormalVnl(); mitk::Geometry2D::ConstPointer worldGeometry3 = RenderWindow3->GetRenderer()->GetCurrentWorldGeometry2D(); mitk::PlaneGeometry::ConstPointer _Plane3 = dynamic_cast( worldGeometry3.GetPointer() ); mitk::VnlVector normal3 = _Plane3->GetNormalVnl(); normal[0] = fabs(normal[0]); normal[1] = fabs(normal[1]); normal[2] = fabs(normal[2]); normal1[0] = fabs(normal1[0]); normal1[1] = fabs(normal1[1]); normal1[2] = fabs(normal1[2]); normal2[0] = fabs(normal2[0]); normal2[1] = fabs(normal2[1]); normal2[2] = fabs(normal2[2]); normal3[0] = fabs(normal3[0]); normal3[1] = fabs(normal3[1]); normal3[2] = fabs(normal3[2]); double ang1 = angle(normal, normal1); double ang2 = angle(normal, normal2); double ang3 = angle(normal, normal3); if(ang1 < ang2 && ang1 < ang3) { selectedRenderWindow = RenderWindow1; } else { if(ang2 < ang3) { selectedRenderWindow = RenderWindow2; } else { selectedRenderWindow = RenderWindow3; } } // re-orient view if (selectedRenderWindow) { const mitk::Point3D& centerP = _PlaneGeometry->GetOrigin(); selectedRenderWindow->GetSliceNavigationController()->ReorientSlices(centerP, _PlaneGeometry->GetNormal()); } } break; } this->RequestRenderWindowUpdate(); } void QmitkMeasurementView::ActionDrawLineTriggered(bool checked) { Q_UNUSED(checked) mitk::PlanarLine::Pointer figure = mitk::PlanarLine::New(); QString qString = QString("Line%1").arg(++d->m_LineCounter); this->AddFigureToDataStorage(figure, qString); MEASUREMENT_DEBUG << "PlanarLine initialized..."; } void QmitkMeasurementView::ActionDrawPathTriggered(bool checked) { Q_UNUSED(checked) mitk::IPropertyFilters* propertyFilters = GetService(); if (propertyFilters != NULL) { mitk::PropertyFilter filter; filter.AddEntry("ClosedPlanarPolygon", mitk::PropertyFilter::Blacklist); propertyFilters->AddFilter(filter, "PlanarPolygon"); } mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); figure->ClosedOff(); QString qString = QString("Path%1").arg(++d->m_PathCounter); mitk::DataNode::Pointer node = this->AddFigureToDataStorage(figure, qString); mitk::BoolProperty::Pointer closedProperty = mitk::BoolProperty::New( false ); node->SetProperty("ClosedPlanarPolygon", closedProperty); node->SetProperty("planarfigure.isextendable",mitk::BoolProperty::New(true)); MEASUREMENT_DEBUG << "PlanarPath initialized..."; } void QmitkMeasurementView::ActionDrawAngleTriggered(bool checked) { Q_UNUSED(checked) mitk::PlanarAngle::Pointer figure = mitk::PlanarAngle::New(); QString qString = QString("Angle%1").arg(++d->m_AngleCounter); this->AddFigureToDataStorage(figure, qString); MEASUREMENT_DEBUG << "PlanarAngle initialized..."; } void QmitkMeasurementView::ActionDrawFourPointAngleTriggered(bool checked) { Q_UNUSED(checked) mitk::PlanarFourPointAngle::Pointer figure = mitk::PlanarFourPointAngle::New(); QString qString = QString("Four Point Angle%1").arg(++d->m_FourPointAngleCounter); this->AddFigureToDataStorage(figure, qString); MEASUREMENT_DEBUG << "PlanarFourPointAngle initialized..."; } -void QmitkMeasurementView::ActionDrawEllipseTriggered(bool checked) +void QmitkMeasurementView::ActionDrawCircleTriggered(bool checked) { Q_UNUSED(checked) mitk::PlanarCircle::Pointer figure = mitk::PlanarCircle::New(); - QString qString = QString("Circle%1").arg(++d->m_EllipseCounter); + QString qString = QString("Circle%1").arg(++d->m_CircleCounter); this->AddFigureToDataStorage(figure, qString); MEASUREMENT_DEBUG << "PlanarCircle initialized..."; } +void QmitkMeasurementView::ActionDrawEllipseTriggered(bool checked) +{ + Q_UNUSED(checked) + + mitk::PlanarEllipse::Pointer figure = mitk::PlanarEllipse::New(); + QString qString = QString("Ellipse%1").arg(++d->m_EllipseCounter); + this->AddFigureToDataStorage(figure, qString); + + MEASUREMENT_DEBUG << "PlanarEllipse initialized..."; +} + void QmitkMeasurementView::ActionDrawDoubleEllipseTriggered(bool checked) { Q_UNUSED(checked) mitk::PlanarDoubleEllipse::Pointer figure = mitk::PlanarDoubleEllipse::New(); QString qString = QString("DoubleEllipse%1").arg(++d->m_DoubleEllipseCounter); this->AddFigureToDataStorage(figure, qString); MEASUREMENT_DEBUG << "PlanarDoubleEllipse initialized..."; } void QmitkMeasurementView::ActionDrawBezierCurveTriggered(bool checked) { Q_UNUSED(checked) mitk::PlanarBezierCurve::Pointer figure = mitk::PlanarBezierCurve::New(); QString qString = QString("BezierCurve%1").arg(++d->m_BezierCurveCounter); this->AddFigureToDataStorage(figure, qString); MEASUREMENT_DEBUG << "PlanarBezierCurve initialized..."; } +void QmitkMeasurementView::ActionDrawSubdivisionPolygonTriggered(bool checked) +{ + Q_UNUSED(checked) + + mitk::PlanarSubdivisionPolygon::Pointer figure = mitk::PlanarSubdivisionPolygon::New(); + QString qString = QString("SubdivisionPolygon%1").arg(++d->m_SubdivisionPolygonCounter); + this->AddFigureToDataStorage(figure, qString); + + MEASUREMENT_DEBUG << "PlanarSubdivisionPolygon initialized..."; +} + void QmitkMeasurementView::ActionDrawRectangleTriggered(bool checked) { Q_UNUSED(checked) mitk::PlanarRectangle::Pointer figure = mitk::PlanarRectangle::New(); QString qString = QString("Rectangle%1").arg(++d->m_RectangleCounter); this->AddFigureToDataStorage(figure, qString); MEASUREMENT_DEBUG << "PlanarRectangle initialized..."; } void QmitkMeasurementView::ActionDrawPolygonTriggered(bool checked) { Q_UNUSED(checked) mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); figure->ClosedOn(); QString qString = QString("Polygon%1").arg(++d->m_PolygonCounter); mitk::DataNode::Pointer node = this->AddFigureToDataStorage(figure, qString); node->SetProperty("planarfigure.isextendable",mitk::BoolProperty::New(true)); MEASUREMENT_DEBUG << "PlanarPolygon initialized..."; } void QmitkMeasurementView::CopyToClipboard( bool checked ) { Q_UNUSED(checked) MEASUREMENT_DEBUG << "Copying current Text to clipboard..."; QString clipboardText = d->m_SelectedPlanarFiguresText->toPlainText(); QApplication::clipboard()->setText(clipboardText, QClipboard::Clipboard); } mitk::DataNode::Pointer QmitkMeasurementView::AddFigureToDataStorage( mitk::PlanarFigure* figure, const QString& name) { // add as MEASUREMENT_DEBUG << "Adding new figure to datastorage..."; if( d->m_SelectedImageNode.IsNull() ) { MITK_ERROR << "No reference image available"; return 0; } mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetName(name.toStdString()); newNode->SetData(figure); // set as selected newNode->SetSelected( true ); this->GetDataStorage()->Add(newNode, d->m_SelectedImageNode); // set all others in selection as deselected for( int i=0; im_CurrentSelection.size(); ++i) d->m_CurrentSelection.at(i)->SetSelected(false); d->m_CurrentSelection.clear(); d->m_CurrentSelection.push_back( newNode ); this->UpdateMeasurementText(); this->DisableCrosshairNavigation(); d->m_DrawActionsToolBar->setEnabled(false); d->m_UnintializedPlanarFigure = true; return newNode; } void QmitkMeasurementView::UpdateMeasurementText() { d->m_SelectedPlanarFiguresText->clear(); QString infoText; QString plainInfoText; int j = 1; mitk::PlanarFigure* _PlanarFigure = 0; mitk::PlanarAngle* planarAngle = 0; mitk::PlanarFourPointAngle* planarFourPointAngle = 0; mitk::DataNode::Pointer node = 0; for (int i=0; im_CurrentSelection.size(); ++i, ++j) { plainInfoText.clear(); node = d->m_CurrentSelection.at(i); _PlanarFigure = dynamic_cast (node->GetData()); if( !_PlanarFigure ) continue; if(j>1) infoText.append("
"); infoText.append(QString("%1
").arg(QString::fromStdString( node->GetName()))); plainInfoText.append(QString("%1").arg(QString::fromStdString( node->GetName()))); planarAngle = dynamic_cast (_PlanarFigure); if(!planarAngle) { planarFourPointAngle = dynamic_cast (_PlanarFigure); } double featureQuantity = 0.0; for (unsigned int k = 0; k < _PlanarFigure->GetNumberOfFeatures(); ++k) { if ( !_PlanarFigure->IsFeatureActive( k ) ) continue; featureQuantity = _PlanarFigure->GetQuantity(k); if ((planarAngle && k == planarAngle->FEATURE_ID_ANGLE) || (planarFourPointAngle && k == planarFourPointAngle->FEATURE_ID_ANGLE)) featureQuantity = featureQuantity * 180 / vnl_math::pi; infoText.append( QString("%1: %2 %3") .arg(QString( _PlanarFigure->GetFeatureName(k))) .arg(featureQuantity, 0, 'f', 2) .arg(QString(_PlanarFigure->GetFeatureUnit(k)))); plainInfoText.append( QString("\n%1: %2 %3") .arg(QString(_PlanarFigure->GetFeatureName(k))) .arg( featureQuantity, 0, 'f', 2) .arg(QString( _PlanarFigure->GetFeatureUnit(k)))); if(k+1 != _PlanarFigure->GetNumberOfFeatures()) infoText.append("
"); } if (j != d->m_CurrentSelection.size()) infoText.append("
"); } d->m_SelectedPlanarFiguresText->setHtml(infoText); } void QmitkMeasurementView::AddAllInteractors() { MEASUREMENT_DEBUG << "Adding interactors and observers to all planar figures"; mitk::DataStorage::SetOfObjects::ConstPointer planarFigures = this->GetAllPlanarFigures(); for(mitk::DataStorage::SetOfObjects::ConstIterator it=planarFigures->Begin(); it!=planarFigures->End(); it++) { this->NodeAdded( it.Value() ); } } void QmitkMeasurementView::RemoveAllInteractors() { MEASUREMENT_DEBUG << "Removing interactors and observers from all planar figures"; mitk::DataStorage::SetOfObjects::ConstPointer planarFigures = this->GetAllPlanarFigures(); for(mitk::DataStorage::SetOfObjects::ConstIterator it=planarFigures->Begin(); it!=planarFigures->End(); it++) { this->NodeRemoved( it.Value() ); } } mitk::DataNode::Pointer QmitkMeasurementView::DetectTopMostVisibleImage() { // get all images from the data storage which are not a segmentation mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer isNotBinary = mitk::NodePredicateNot::New( isBinary ); mitk::NodePredicateAnd::Pointer isNormalImage = mitk::NodePredicateAnd::New( isImage, isNotBinary ); mitk::DataStorage::SetOfObjects::ConstPointer Images = this->GetDataStorage()->GetSubset( isNormalImage ); mitk::DataNode::Pointer currentNode; int maxLayer = itk::NumericTraits::min(); // iterate over selection for (mitk::DataStorage::SetOfObjects::ConstIterator sofIt = Images->Begin(); sofIt != Images->End(); ++sofIt) { mitk::DataNode::Pointer node = sofIt->Value(); if ( node.IsNull() ) continue; if (node->IsVisible(NULL) == false) continue; // we also do not want to assign planar figures to helper objects ( even if they are of type image ) if (node->GetProperty("helper object")) continue; int layer = 0; node->GetIntProperty("layer", layer); if ( layer < maxLayer ) { continue; } else { maxLayer = layer; currentNode = node; } } return currentNode; } void QmitkMeasurementView::EnableCrosshairNavigation() { MEASUREMENT_DEBUG << "EnableCrosshairNavigation"; // enable the crosshair navigation if (mitk::ILinkedRenderWindowPart* linkedRenderWindow = dynamic_cast(this->GetRenderWindowPart())) { MEASUREMENT_DEBUG << "enabling linked navigation"; linkedRenderWindow->EnableLinkedNavigation(true); linkedRenderWindow->EnableSlicingPlanes(true); } } void QmitkMeasurementView::DisableCrosshairNavigation() { MEASUREMENT_DEBUG << "DisableCrosshairNavigation"; // disable the crosshair navigation during the drawing if (mitk::ILinkedRenderWindowPart* linkedRenderWindow = dynamic_cast(this->GetRenderWindowPart())) { MEASUREMENT_DEBUG << "disabling linked navigation"; linkedRenderWindow->EnableLinkedNavigation(false); linkedRenderWindow->EnableSlicingPlanes(false); } } mitk::DataStorage::SetOfObjects::ConstPointer QmitkMeasurementView::GetAllPlanarFigures() const { mitk::TNodePredicateDataType::Pointer isPlanarFigure = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isNotHelperObject = mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(false)); mitk::NodePredicateAnd::Pointer isNotHelperButPlanarFigure = mitk::NodePredicateAnd::New( isPlanarFigure, isNotHelperObject ); return this->GetDataStorage()->GetSubset( isPlanarFigure ); } diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.h b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.h index 23ee65621b..4a272ce02e 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.h +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.h @@ -1,88 +1,90 @@ /*=================================================================== 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 QMITK_MEASUREMENT_H__INCLUDED #define QMITK_MEASUREMENT_H__INCLUDED #include #include /// forward declarations struct QmitkMeasurementViewData; namespace mitk { class PlanarFigure; } /// /// A view for doing measurements in digital images by means of /// mitk::Planarfigures which can represent drawing primitives (Lines, circles, ...). /// The view consists of only three main elements: /// 1. A toolbar for activating PlanarFigure drawing /// 2. A textbrowser which shows details for the selected PlanarFigures /// 3. A button for copying all details to the clipboard /// class QmitkMeasurementView : public QmitkAbstractView { Q_OBJECT public: static const std::string VIEW_ID; QmitkMeasurementView(); virtual ~QmitkMeasurementView(); void CreateQtPartControl(QWidget* parent); void SetFocus(); virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList &nodes); void NodeAdded(const mitk::DataNode* node); void NodeChanged(const mitk::DataNode* node); void NodeRemoved(const mitk::DataNode* node); void PlanarFigureSelected( itk::Object* object, const itk::EventObject& ); protected slots: ///# draw actions void ActionDrawLineTriggered( bool checked = false ); void ActionDrawPathTriggered( bool checked = false ); void ActionDrawAngleTriggered( bool checked = false ); void ActionDrawFourPointAngleTriggered( bool checked = false ); + void ActionDrawCircleTriggered( bool checked = false ); void ActionDrawEllipseTriggered( bool checked = false ); void ActionDrawDoubleEllipseTriggered( bool checked = false ); void ActionDrawRectangleTriggered( bool checked = false ); void ActionDrawPolygonTriggered( bool checked = false ); void ActionDrawBezierCurveTriggered( bool checked = false ); + void ActionDrawSubdivisionPolygonTriggered( bool checked = false ); void CopyToClipboard( bool checked = false ); private: void CreateConnections(); mitk::DataNode::Pointer AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name); void UpdateMeasurementText(); void AddAllInteractors(); void RemoveAllInteractors(); mitk::DataNode::Pointer DetectTopMostVisibleImage(); void EnableCrosshairNavigation(); void DisableCrosshairNavigation(); void PlanarFigureInitialized(); void CheckForTopMostVisibleImage(mitk::DataNode* _NodeToNeglect=0); mitk::DataStorage::SetOfObjects::ConstPointer GetAllPlanarFigures() const; QmitkMeasurementViewData* d; }; #endif // QMITK_MEASUREMENT_H__INCLUDED diff --git a/Plugins/org.mitk.planarfigure/src/internal/mitkPlanarFigureActivator.cpp b/Plugins/org.mitk.planarfigure/src/internal/mitkPlanarFigureActivator.cpp index b3618a7b94..5cb9e6022d 100644 --- a/Plugins/org.mitk.planarfigure/src/internal/mitkPlanarFigureActivator.cpp +++ b/Plugins/org.mitk.planarfigure/src/internal/mitkPlanarFigureActivator.cpp @@ -1,75 +1,83 @@ /*=================================================================== 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 "mitkPlanarFigureActivator.h" #include "mitkPlanarFigureObjectFactory.h" #include "QmitkNodeDescriptorManager.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateProperty.h" #include "mitkNodePredicateAnd.h" #include void mitk::PlanarFigureActivator::start(ctkPluginContext* /*context*/) { QmitkNodeDescriptorManager* descriptorManager = QmitkNodeDescriptorManager::GetInstance(); // Adding "PlanarLine" mitk::NodePredicateDataType::Pointer isPlanarLine = mitk::NodePredicateDataType::New("PlanarLine"); descriptorManager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("PlanarLine"), QString(":/QtWidgetsExt/PlanarLine_48.png"), isPlanarLine, descriptorManager)); // Adding "PlanarCircle" mitk::NodePredicateDataType::Pointer isPlanarCircle = mitk::NodePredicateDataType::New("PlanarCircle"); descriptorManager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("PlanarCircle"), QString(":/QtWidgetsExt/PlanarCircle_48.png"), isPlanarCircle, descriptorManager)); + // Adding "PlanarEllipse" + mitk::NodePredicateDataType::Pointer isPlanarEllipse = mitk::NodePredicateDataType::New("PlanarEllipse"); + descriptorManager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("PlanarEllipse"), QString(":/QtWidgetsExt/PlanarEllipse_48.png"), isPlanarEllipse, descriptorManager)); + // Adding "PlanarAngle" mitk::NodePredicateDataType::Pointer isPlanarAngle = mitk::NodePredicateDataType::New("PlanarAngle"); descriptorManager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("PlanarAngle"), QString(":/QtWidgetsExt/PlanarAngle_48.png"), isPlanarAngle, descriptorManager)); // Adding "PlanarFourPointAngle" mitk::NodePredicateDataType::Pointer isPlanarFourPointAngle = mitk::NodePredicateDataType::New("PlanarFourPointAngle"); descriptorManager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("PlanarFourPointAngle"), QString(":/QtWidgetsExt/PlanarFourPointAngle_48.png"), isPlanarFourPointAngle, descriptorManager)); // Adding "PlanarRectangle" mitk::NodePredicateDataType::Pointer isPlanarRectangle = mitk::NodePredicateDataType::New("PlanarRectangle"); descriptorManager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("PlanarRectangle"), QString(":/QtWidgetsExt/PlanarRectangle_48.png"), isPlanarRectangle, descriptorManager)); // Adding "PlanarPolygon" mitk::NodePredicateDataType::Pointer isPlanarPolygon = mitk::NodePredicateDataType::New("PlanarPolygon"); descriptorManager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("PlanarPolygon"), QString(":/QtWidgetsExt/PlanarPolygon_48.png"), isPlanarPolygon, descriptorManager)); // Adding "PlanarPath" mitk::NodePredicateProperty::Pointer isNotClosedPolygon = mitk::NodePredicateProperty::New("ClosedPlanarPolygon", mitk::BoolProperty::New(false)); mitk::NodePredicateAnd::Pointer isPlanarPath = mitk::NodePredicateAnd::New(isNotClosedPolygon, isPlanarPolygon); descriptorManager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("PlanarPath"), QString(":/QtWidgetsExt/PlanarPath_48.png"), isPlanarPath, descriptorManager)); // Adding "PlanarDoubleEllipse" mitk::NodePredicateDataType::Pointer isPlanarDoubleEllipse = mitk::NodePredicateDataType::New("PlanarDoubleEllipse"); descriptorManager->AddDescriptor(new QmitkNodeDescriptor("PlanarDoubleEllipse", ":/QtWidgetsExt/PlanarDoubleEllipse_48.png", isPlanarDoubleEllipse, descriptorManager)); // Adding "PlanarBezierCurve" mitk::NodePredicateDataType::Pointer isPlanarBezierCurve = mitk::NodePredicateDataType::New("PlanarBezierCurve"); descriptorManager->AddDescriptor(new QmitkNodeDescriptor("PlanarBezierCurve", ":/QtWidgetsExt/PlanarBezierCurve_48.png", isPlanarBezierCurve, descriptorManager)); + + // Adding "PlanarSubdivisionPolygon" + mitk::NodePredicateDataType::Pointer isPlanarSubdivisionPolygon = mitk::NodePredicateDataType::New("PlanarSubdivisionPolygon"); + descriptorManager->AddDescriptor(new QmitkNodeDescriptor("PlanarSubdivisionPolygon", ":/QtWidgetsExt/PlanarSubdivisionPolygon_48.png", isPlanarSubdivisionPolygon, descriptorManager)); } void mitk::PlanarFigureActivator::stop(ctkPluginContext* /*context*/) { } Q_EXPORT_PLUGIN2(org_mitk_planarfigure, mitk::PlanarFigureActivator)