diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.cpp index 52e58d4351..b690dc5b3b 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.cpp @@ -1,266 +1,285 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 18029 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkPlanarPolygon.h" #include "mitkGeometry2D.h" #include "mitkProperties.h" // stl related includes #include mitk::PlanarPolygon::PlanarPolygon() : FEATURE_ID_CIRCUMFERENCE( this->AddFeature( "Circumference", "mm" ) ), FEATURE_ID_AREA( this->AddFeature( "Area", "mm2" ) ) { // Polygon has at least two control points this->ResetNumberOfControlPoints( 2 ); this->SetNumberOfPolyLines( 1 ); // Polygon is closed by default this->SetProperty( "closed", mitk::BoolProperty::New( true ) ); this->SetProperty( "subdivision", mitk::BoolProperty::New( false ) ); } mitk::PlanarPolygon::~PlanarPolygon() { } void mitk::PlanarPolygon::SetClosed( bool closed ) { this->SetProperty( "closed", mitk::BoolProperty::New( closed ) ); if ( !closed ) { // For non-closed polygons: use "Length" as feature name; disable area this->SetFeatureName( FEATURE_ID_CIRCUMFERENCE, "Length" ); this->DeactivateFeature( FEATURE_ID_AREA ); } else { // For closed polygons: use "Circumference" as feature name; enable area this->SetFeatureName( FEATURE_ID_CIRCUMFERENCE, "Circumference" ); this->ActivateFeature( FEATURE_ID_AREA ); } this->Modified(); } void mitk::PlanarPolygon::GeneratePolyLine() { this->ClearPolyLines(); for ( int i=0; iAppendPointToPolyLine( 0, elem ); } } void mitk::PlanarPolygon::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/) { // A polygon does not require helper objects } void mitk::PlanarPolygon::EvaluateFeaturesInternal() { // Calculate circumference double circumference = 0.0; unsigned int i,j; - for ( i = 0; i < this->GetNumberOfControlPoints() - 1; ++i ) + + ControlPointListType polyLinePoints; + polyLinePoints.clear(); + PolyLineType::iterator iter; + for( iter = m_PolyLines[0].begin(); iter != m_PolyLines[0].end(); ++iter ) { - circumference += this->GetWorldControlPoint( i ).EuclideanDistanceTo( - this->GetWorldControlPoint( i + 1 ) ); + polyLinePoints.push_back((*iter).Point); + } + + if(polyLinePoints.empty()) + return; + + for ( i = 0; i <(polyLinePoints.size()-1); ++i ) + { + circumference += polyLinePoints[i].EuclideanDistanceTo( + polyLinePoints[i + 1] ); } if ( this->IsClosed() ) { - circumference += this->GetWorldControlPoint( i ).EuclideanDistanceTo( - this->GetWorldControlPoint( 0 ) ); + circumference += polyLinePoints[i].EuclideanDistanceTo( + polyLinePoints.front() ); } this->SetQuantity( FEATURE_ID_CIRCUMFERENCE, circumference ); // Calculate polygon area (if closed) double area = 0.0; bool intersection = false; if ( this->IsClosed() && (this->GetGeometry2D() != NULL) ) { // does PlanarPolygon overlap/intersect itself? - unsigned int numberOfPoints = (unsigned int)GetNumberOfControlPoints(); + unsigned int numberOfPoints = polyLinePoints.size(); if( numberOfPoints >= 4) { for ( i = 0; i < (numberOfPoints - 1); ++i ) { // line 1 - Point2D p0 = this->GetControlPoint( i ); - Point2D p1 = this->GetControlPoint(i + 1); + Point2D p0 = polyLinePoints[i]; + Point2D p1 = polyLinePoints[i + 1]; // check for intersection with all other lines for (j = i+1; j < (numberOfPoints - 1); ++j ) { - Point2D p2 = this->GetControlPoint(j); - Point2D p3 = this->GetControlPoint(j + 1); + Point2D p2 = polyLinePoints[j]; + Point2D p3 = polyLinePoints[j + 1]; intersection = CheckForLineIntersection(p0,p1,p2,p3); if (intersection) break; } if (intersection) break; // only because the inner loop might have changed "intersection" // last line from p_x to p_0 - Point2D p2 = this->GetControlPoint(0); - Point2D p3 = this->GetControlPoint(numberOfPoints - 1); + Point2D p2 = polyLinePoints.front(); + Point2D p3 = polyLinePoints.back(); intersection = CheckForLineIntersection(p0,p1,p2,p3); if (intersection) break; } } // calculate area - for ( i = 0; i < this->GetNumberOfControlPoints(); ++i ) + for ( i = 0; i < polyLinePoints.size(); ++i ) { - Point2D p0 = this->GetControlPoint( i ); - Point2D p1 = this->GetControlPoint( (i + 1) % this->GetNumberOfControlPoints() ); + Point2D p0 = polyLinePoints[i]; + Point2D p1 = polyLinePoints[ (i + 1) % polyLinePoints.size() ]; area += p0[0] * p1[1] - p1[0] * p0[1]; } area /= 2.0; } // set area if appropiate (i.e. closed and not intersected) if(this->IsClosed() && !intersection) { SetQuantity( FEATURE_ID_AREA, fabs( area ) ); this->ActivateFeature( FEATURE_ID_AREA ); } else { SetQuantity( FEATURE_ID_AREA, 0 ); this->DeactivateFeature( FEATURE_ID_AREA ); } } void mitk::PlanarPolygon::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); if (this->IsClosed()) os << indent << "Polygon is closed\n"; else os << indent << "Polygon is not closed\n"; } // based on // http://flassari.is/2008/11/line-line-intersection-in-cplusplus/ bool mitk::PlanarPolygon::CheckForLineIntersection( const mitk::Point2D& p1, const mitk::Point2D& p2, const mitk::Point2D& p3, const mitk::Point2D& p4, Point2D& intersection ) const { // do not check for intersections with control points if(p1 == p2 || p1 == p3 || p1 == p4 || p2 == p3 || p2 == p4 || p3 == p4) return false; // Store the values for fast access and easy // equations-to-code conversion double x1 = p1[0], x2 = p2[0], x3 = p3[0], x4 = p4[0]; double y1 = p1[1], y2 = p2[1], y3 = p3[1], y4 = p4[1]; double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); // If d is zero, there is no intersection //if (d < mitk::eps) return false; if (d == 0) return false; // Get the x and y double pre = (x1*y2 - y1*x2); double post = (x3*y4 - y3*x4); double x = ( pre * (x3 - x4) - (x1 - x2) * post ) / d; double y = ( pre * (y3 - y4) - (y1 - y2) * post ) / d; double tolerance = 0.001; // Check if the x coordinates are within both lines, including tolerance if ( x < ( std::min(x1, x2) - tolerance ) || x > ( std::max(x1, x2) + tolerance ) || x < ( std::min(x3, x4) - tolerance ) || x > ( std::max(x3, x4) + tolerance ) ) { return false; } // Check if the y coordinates are within both lines, including tolerance if ( y < ( std::min(y1, y2) - tolerance ) || y > ( std::max(y1, y2) + tolerance ) || y < ( std::min(y3, y4) - tolerance ) || y > ( std::max(y3, y4) + tolerance ) ) { return false; } // point of intersection Point2D ret; ret[0] = x; ret[1] = y; intersection = ret; return true; } bool mitk::PlanarPolygon::CheckForLineIntersection( const mitk::Point2D& p1, const mitk::Point2D& p2, const mitk::Point2D& p3, const mitk::Point2D& p4 ) const { mitk::Point2D intersection; return mitk::PlanarPolygon::CheckForLineIntersection( p1, p2, p3, p4, intersection ); } std::vector mitk::PlanarPolygon::CheckForLineIntersection( const mitk::Point2D& p1, const mitk::Point2D& p2 ) const { std::vector intersectionList; + ControlPointListType polyLinePoints; + PolyLineType tempList = m_PolyLines[0]; + PolyLineType::iterator iter; + for( iter = tempList.begin(); iter != tempList.end(); ++iter ) + { + polyLinePoints.push_back((*iter).Point); + } - for ( int i=0; iGetNumberOfControlPoints()-1; i++ ) + for ( int i=0; iGetControlPoint( i ); - mitk::Point2D pnt2 = this->GetControlPoint( i+1 ); + mitk::Point2D pnt1 = polyLinePoints[i]; + mitk::Point2D pnt2 = polyLinePoints[i+1]; mitk::Point2D intersection; if ( mitk::PlanarPolygon::CheckForLineIntersection( p1, p2, pnt1, pnt2, intersection ) ) { intersectionList.push_back( intersection ); } } if ( this->IsClosed() ) { mitk::Point2D intersection, lastControlPoint, firstControlPoint; - lastControlPoint = this->GetControlPoint(this->GetNumberOfControlPoints()-1); - firstControlPoint = this->GetControlPoint(0); + lastControlPoint = polyLinePoints.back(); + firstControlPoint = polyLinePoints.front(); if ( mitk::PlanarPolygon::CheckForLineIntersection( lastControlPoint, firstControlPoint, p1, p2, intersection ) ) { intersectionList.push_back( intersection ); } } return intersectionList; } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp index 586aa4a3f2..3cd0d1dd81 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp @@ -1,301 +1,168 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 18029 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkPlanarSubdivisionPolygon.h" #include "mitkGeometry2D.h" #include "mitkProperties.h" // stl related includes #include mitk::PlanarSubdivisionPolygon::PlanarSubdivisionPolygon() -: FEATURE_ID_CIRCUMFERENCE( this->AddFeature( "Circumference", "mm" ) ), - FEATURE_ID_AREA( this->AddFeature( "Area", "mm2" ) ) { - // Polygon has at least two control points - this->ResetNumberOfControlPoints( 2 ); - this->SetNumberOfPolyLines( 1 ); - - // Polygon is closed by default + // Polygon is subdivision (in contrast to parent class PlanarPolygon this->SetProperty( "closed", mitk::BoolProperty::New( true ) ); this->SetProperty( "subdivision", mitk::BoolProperty::New( true ) ); + + // Other properties are inherited / already initialized by parent class PlanarPolygon } mitk::PlanarSubdivisionPolygon::~PlanarSubdivisionPolygon() { } void mitk::PlanarSubdivisionPolygon::GeneratePolyLine() { this->ClearPolyLines(); ControlPointListType m_SubdivisionPoints = m_ControlPoints; if( m_ControlPoints.size() >= GetMinimumNumberOfControlPoints() ){ for( unsigned int i=0; i < GetSubdivisionRounds(); i++ ) { // Indices unsigned int p_here, p_prev, p_next, p_nextnext; p_prev = m_SubdivisionPoints.size() -1; p_here = 0; p_next = 1; p_nextnext = 2; double distanceToSubPoint; double distanceToPointLeft; double distanceToPointRight; Point2D newPoint; // Keep cycling our array indices forward until they wrap around at the end while(true) { // Get distance to neighbors distanceToSubPoint = m_SubdivisionPoints[p_here].EuclideanDistanceTo(m_SubdivisionPoints[p_next]); /* * Only calculate distance to left and right subdivisionPoints if we are the "major" / last control point being dragged around * * Cause: If any point is in a 20 pixel(?) radius to the point being dragged around, * the PlanarFigureInteractor tries to insert the new point on this existing polyline (between subdivision points!!) * rather than creating a new point at the end of the line. */ if (p_here == m_SubdivisionPoints.size() - 1) { distanceToPointLeft = m_SubdivisionPoints[p_here].EuclideanDistanceTo(m_SubdivisionPoints[0]); distanceToPointRight = m_SubdivisionPoints[p_here].EuclideanDistanceTo(m_SubdivisionPoints[p_here - 1]); } // distance to next subPoint has to be > 2 // distance from main control point has to be > 20 (see above comment) if( distanceToSubPoint > 2.0 && ( p_here != m_SubdivisionPoints.size() || distanceToPointRight > 20 && distanceToPointLeft > 20 )) { double x,y; // Create new subdivision point according to formula // p_new = (0.5 + tension) * (p_here + p_next) - tension * (p_prev + p_nextnext) x = (0.5 + GetTensionParameter()) * (double)( m_SubdivisionPoints[p_here][0] + m_SubdivisionPoints[p_next][0] ) - GetTensionParameter() * (double)( m_SubdivisionPoints[p_prev][0] + m_SubdivisionPoints[p_nextnext][0]); y = (0.5 + GetTensionParameter()) * (double)( m_SubdivisionPoints[p_here][1] + m_SubdivisionPoints[p_next][1] ) - GetTensionParameter() * (double)( m_SubdivisionPoints[p_prev][1] + m_SubdivisionPoints[p_nextnext][1]); newPoint[0] = x; newPoint[1] = y; m_SubdivisionPoints.insert(m_SubdivisionPoints.begin() + p_next, newPoint); // The new point gets inserted between p_here and p_next, so our array indices are outdated -> advance them p_here++; p_next++; p_nextnext++; } // Advance array indices and wrap them around at the end p_prev = p_here; if(p_here >= (m_SubdivisionPoints.size() -1 )) { p_here = 0; break; } else { p_here += 1; } if((p_next >= m_SubdivisionPoints.size() - 1)) { p_next = 0; } else { p_next += 1; } if((p_nextnext >= m_SubdivisionPoints.size() - 1 )) { p_nextnext = 0; if(!this->IsClosed()){ break; } } else { p_nextnext += 1; } } } } // The polyline needs to know between which control points each subdivision point is. // The first point is generally on the line between the last and first control point int lastControlPointIndex = m_SubdivisionPoints.size() - 1; for ( unsigned int i=0; iAppendPointToPolyLine( 0, elem ); } } - -void mitk::PlanarSubdivisionPolygon::EvaluateFeaturesInternal() -{ - // Calculate circumference - double circumference = 0.0; - unsigned int i,j; - - // We need to construct our subdivisionPoints-array out of the existing polyline, because subdivision points aren't stored in the class - ControlPointListType m_SubdivisionPoints; - m_SubdivisionPoints.clear(); - PolyLineType::iterator iter; - for( iter = m_PolyLines[0].begin(); iter != m_PolyLines[0].end(); ++iter ) - { - m_SubdivisionPoints.push_back((*iter).Point); - } - - if(m_SubdivisionPoints.size() < 3){ - return; - } - - for ( i = 0; i < (m_SubdivisionPoints.size() - 1); ++i ) - { - circumference += m_SubdivisionPoints[i].EuclideanDistanceTo(m_SubdivisionPoints[i + 1]); - } - - if ( this->IsClosed() ) - { - circumference += m_SubdivisionPoints.back().EuclideanDistanceTo( m_SubdivisionPoints.front() ); - } - - this->SetQuantity( FEATURE_ID_CIRCUMFERENCE, circumference ); - - // Calculate polygon area (if closed) - double area = 0.0; - bool intersection = false; - if ( this->IsClosed() && (this->GetGeometry2D() != NULL) ) - { - // does PlanarPolygon overlap/intersect itself? - unsigned int numberOfPoints = m_SubdivisionPoints.size(); - if( numberOfPoints >= GetMinimumNumberOfControlPoints()) - { - for ( i=0; iIsClosed() && !intersection) - { - SetQuantity( FEATURE_ID_AREA, fabs( area ) ); - this->ActivateFeature( FEATURE_ID_AREA ); - } - else - { - SetQuantity( FEATURE_ID_AREA, 0 ); - this->DeactivateFeature( FEATURE_ID_AREA ); - } -} - -std::vector mitk::PlanarSubdivisionPolygon::CheckForLineIntersection( const mitk::Point2D& p1, const mitk::Point2D& p2 ) const -{ - std::vector intersectionList; - - ControlPointListType m_SubdivisionPoints; - PolyLineType tempList = m_PolyLines[0]; - PolyLineType::iterator iter; - for( iter = tempList.begin(); iter != tempList.end(); ++iter ) - { - m_SubdivisionPoints.push_back((*iter).Point); - } - - for ( unsigned int i=0; iIsClosed() ) - { - mitk::Point2D intersection, lastControlPoint, firstControlPoint; - lastControlPoint = m_SubdivisionPoints.back(); - firstControlPoint = m_SubdivisionPoints.front(); - - if ( mitk::PlanarSubdivisionPolygon::CheckForLineIntersection( lastControlPoint, - firstControlPoint, p1, p2, intersection ) ) - { - intersectionList.push_back( intersection ); - } - } - - return intersectionList; -} diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h index daade3a3dc..8ea40f9cdc 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h @@ -1,97 +1,90 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 17258 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _MITK_PLANAR_SUBDIVISION_POLYGON_H_ #define _MITK_PLANAR_SUBDIVISION_POLYGON_H_ #include "mitkPlanarFigure.h" #include "PlanarFigureExports.h" #include "mitkPlanarPolygon.h" namespace mitk { class Geometry2D; /** * \brief Implementation of PlanarFigure representing a polygon * with two or more control points */ class PlanarFigure_EXPORT PlanarSubdivisionPolygon : public PlanarPolygon { public: mitkClassMacro( PlanarSubdivisionPolygon, PlanarFigure ); itkNewMacro( Self ); /** \brief Subdivision Polygon has 3 control points per definition. */ unsigned int GetMinimumNumberOfControlPoints() const { return 3; } /** \brief Polygon maximum number of control points is principally not limited. */ unsigned int GetMaximumNumberOfControlPoints() const { return 1000; } /** \brief How many times should we generate a round of subdivisions? */ unsigned int GetSubdivisionRounds() const { return 5; } /** \brief Parameter w_tension defines the tension. * the higher w_tension, the lower the "tension" on points. * Rule: 0 < w_tension < 0.1 * 0.0625 (1 / 16) seems to be a good value. */ float GetTensionParameter() const { return 0.0625; } std::vector CheckForLineIntersection( const Point2D& p1, const Point2D& p2 ) const; void IncreaseSubdivisions(); void DecreaseSubdivisions(); protected: PlanarSubdivisionPolygon(); virtual ~PlanarSubdivisionPolygon(); /** \brief Generates the poly-line representation of the planar figure. */ virtual void GeneratePolyLine(); - /** \brief Calculates feature quantities of the planar figure. */ - virtual void EvaluateFeaturesInternal(); - - using PlanarPolygon::CheckForLineIntersection; - - const unsigned int FEATURE_ID_CIRCUMFERENCE; - const unsigned int FEATURE_ID_AREA; private: }; } // namespace mitk #endif //_MITK_PLANAR_SUBDIVISION_POLYGON_H_