From a87b2d82e52bb3454cc514b339a07eb12d8e1f0b Mon Sep 17 00:00:00 2001 From: Adrian Holfter Date: Mon, 11 Jul 2011 14:04:51 +0200 Subject: [PATCH 1/3] Implemented new class PlanarSubdivisionPolygon. --- .../DataManagement/mitkPlanarFigure.cpp | 2 +- .../DataManagement/mitkPlanarPolygon.cpp | 1 + .../mitkPlanarSubdivisionPolygon.cpp | 297 ++++++++++++++++++++ .../DataManagement/mitkPlanarSubdivisionPolygon.h | 97 +++++++ Modules/PlanarFigure/IO/mitkPlanarFigureReader.cpp | 5 + Modules/PlanarFigure/files.cmake | 1 + 6 files changed, 402 insertions(+), 1 deletions(-) create mode 100644 Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp create mode 100644 Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarFigure.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarFigure.cpp index d06f759..6ffec6e 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarFigure.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarFigure.cpp @@ -619,7 +619,7 @@ void mitk::PlanarFigure::DeepCopy(Self::Pointer oldFigure) //Notice to get typeid polymorph you have to use the *operator if(typeid(*oldFigure) != typeid(*this)) { - itkExceptionMacro( << "DeepCopy(): Inconsistent type of source and destination figure!" ); + itkExceptionMacro( << "DeepCopy(): Inconsistent type of source (" << typeid(*oldFigure).name() << ") and destination figure (" << typeid(*this).name() << ")!" ); return; } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.cpp index bd0d945..7bf3c61 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.cpp @@ -34,6 +34,7 @@ mitk::PlanarPolygon::PlanarPolygon() // Polygon is closed by default this->SetProperty( "closed", mitk::BoolProperty::New( true ) ); + this->SetProperty( "subdivision", mitk::BoolProperty::New( false ) ); } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp new file mode 100644 index 0000000..cd72df8 --- /dev/null +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp @@ -0,0 +1,297 @@ +/*========================================================================= + +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 + this->SetProperty( "closed", mitk::BoolProperty::New( true ) ); + this->SetProperty( "subdivision", mitk::BoolProperty::New( true ) ); +} + + +mitk::PlanarSubdivisionPolygon::~PlanarSubdivisionPolygon() +{ +} + +void mitk::PlanarSubdivisionPolygon::GeneratePolyLine() +{ + this->ClearPolyLines(); + ControlPointListType m_SubdivisionPoints = m_ControlPoints; + + // Later, we have to know which points are control points + ControlPointListType m_ControlPointsCopy = 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; + + // Just in case the poly isn't closed. It should be, but who knows... + if(this->IsClosed()) + { + p_here = 0; + p_prev = m_SubdivisionPoints.size() - 1; + p_next = 1; + p_nextnext = 2; + }else{ + p_prev = 0; + p_here = 1; + p_next = 2; + p_nextnext = 3; + } + + double distanceToSubPoint; + double distanceToPointLeft; + double distanceToPointRight; + + Point2D newPoint; + + while(true) + { + // Get distance to neighbors + distanceToSubPoint = m_SubdivisionPoints[p_here].EuclideanDistanceTo(m_SubdivisionPoints[p_next]); + + // Only calculate distance to control points if we are the "major" / last control point being dragged around + if (p_here == m_SubdivisionPoints.size() - 1) + { + unsigned int prev = 0; + unsigned int next = ( m_SubdivisionPoints.size() > 1 ? p_here - 1 : 0 ); + 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 + if( distanceToSubPoint > 2.0 && ( p_here != m_SubdivisionPoints.size() || distanceToPointRight > 20 && distanceToPointLeft > 20 )) + { + double x,y; + + 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 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; + } + + } + } + } + + + int lastControlPoint = 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; + + 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 new file mode 100644 index 0000000..4348ae6 --- /dev/null +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h @@ -0,0 +1,97 @@ +/*========================================================================= + +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 4 control points per definition. */ + unsigned int GetMinimumNumberOfControlPoints() const + { + return 4; + } + + + /** \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_ diff --git a/Modules/PlanarFigure/IO/mitkPlanarFigureReader.cpp b/Modules/PlanarFigure/IO/mitkPlanarFigureReader.cpp index 83a8875..60458ef 100644 --- a/Modules/PlanarFigure/IO/mitkPlanarFigureReader.cpp +++ b/Modules/PlanarFigure/IO/mitkPlanarFigureReader.cpp @@ -25,6 +25,7 @@ PURPOSE. See the above copyright notices for more information. #include "mitkPlanarCross.h" #include "mitkPlanarFourPointAngle.h" #include "mitkPlanarPolygon.h" +#include "mitkPlanarSubdivisionPolygon.h" #include "mitkPlanarRectangle.h" #include "mitkPlaneGeometry.h" @@ -164,6 +165,10 @@ void mitk::PlanarFigureReader::GenerateData() { planarFigure = mitk::PlanarPolygon::New(); } + else if (type == "PlanarSubdivisionPolygon") + { + planarFigure = mitk::PlanarSubdivisionPolygon::New(); + } else if (type == "PlanarRectangle") { planarFigure = mitk::PlanarRectangle::New(); diff --git a/Modules/PlanarFigure/files.cmake b/Modules/PlanarFigure/files.cmake index 931d605..4572a68 100644 --- a/Modules/PlanarFigure/files.cmake +++ b/Modules/PlanarFigure/files.cmake @@ -11,6 +11,7 @@ SET(CPP_FILES DataManagement/mitkPlanarLine.cpp DataManagement/mitkPlanarArrow.cpp DataManagement/mitkPlanarPolygon.cpp + DataManagement/mitkPlanarSubdivisionPolygon.cpp DataManagement/mitkPlanarRectangle.cpp DataManagement/mitkPlanarFigureControlPointStyleProperty.cpp IO/mitkPlanarFigureIOFactory.cpp -- 1.7.0.4 From 95b443f4763fff4830945d4f3dbf37a540d118ef Mon Sep 17 00:00:00 2001 From: Adrian Holfter Date: Tue, 12 Jul 2011 10:55:11 +0200 Subject: [PATCH 2/3] Created unittest for PlanarSubdivisionPolygon and added it to IOTest --- .../DataManagement/mitkPlanarSubdivisionPolygon.h | 4 +- Modules/PlanarFigure/Testing/files.cmake | 1 + .../Testing/mitkPlanarFigureIOTest.cpp | 25 +++ .../Testing/mitkPlanarSubdivisionPolygonTest.cpp | 174 ++++++++++++++++++++ 4 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 Modules/PlanarFigure/Testing/mitkPlanarSubdivisionPolygonTest.cpp diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h index 4348ae6..15fcec7 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h @@ -39,10 +39,10 @@ public: itkNewMacro( Self ); - /** \brief Subdivision Polygon has 4 control points per definition. */ + /** \brief Subdivision Polygon has 3 control points per definition. */ unsigned int GetMinimumNumberOfControlPoints() const { - return 4; + return 3; } diff --git a/Modules/PlanarFigure/Testing/files.cmake b/Modules/PlanarFigure/Testing/files.cmake index 5eae54b..9f34bf1 100644 --- a/Modules/PlanarFigure/Testing/files.cmake +++ b/Modules/PlanarFigure/Testing/files.cmake @@ -1,6 +1,7 @@ SET(MODULE_TESTS mitkPlanarCrossTest.cpp mitkPlanarPolygonTest.cpp + mitkPlanarSubdivisionPolygonTest.cpp mitkPlanarFigureIOTest.cpp mitkPlanarFigureObjectFactoryTest.cpp mitkPlanarArrowTest.cpp diff --git a/Modules/PlanarFigure/Testing/mitkPlanarFigureIOTest.cpp b/Modules/PlanarFigure/Testing/mitkPlanarFigureIOTest.cpp index 8257e67..2c59c3e 100644 --- a/Modules/PlanarFigure/Testing/mitkPlanarFigureIOTest.cpp +++ b/Modules/PlanarFigure/Testing/mitkPlanarFigureIOTest.cpp @@ -17,6 +17,7 @@ PURPOSE. See the above copyright notices for more information. #include "mitkPlanarFourPointAngle.h" #include "mitkPlanarLine.h" #include "mitkPlanarPolygon.h" +#include "mitkPlanarSubdivisionPolygon.h" #include "mitkPlanarRectangle.h" #include "mitkPlanarFigureWriter.h" @@ -99,6 +100,16 @@ public: planarPolygon->AddControlPoint( p2 ); planarPolygon->AddControlPoint( p3 ); planarFigures.push_back( planarPolygon.GetPointer() ); + + // Create PlanarSubdivisionPolygon + mitk::PlanarSubdivisionPolygon::Pointer planarSubdivisionPolygon = mitk::PlanarSubdivisionPolygon::New(); + planarSubdivisionPolygon->SetClosed( false ); + planarSubdivisionPolygon->SetGeometry2D( planeGeometry ); + planarSubdivisionPolygon->PlaceFigure( p0 ); + planarSubdivisionPolygon->SetCurrentControlPoint( p1 ); + planarSubdivisionPolygon->AddControlPoint( p2 ); + planarSubdivisionPolygon->AddControlPoint( p3 ); + planarFigures.push_back( planarSubdivisionPolygon.GetPointer() ); // Create PlanarRectangle mitk::PlanarRectangle::Pointer planarRectangle = mitk::PlanarRectangle::New(); @@ -183,6 +194,16 @@ public: planarPolygonPrecise->AddControlPoint( p3precise ); planarFigures.push_back( planarPolygonPrecise.GetPointer() ); + // Create PlanarSubdivisionPolygon + mitk::PlanarSubdivisionPolygon::Pointer planarSubdivisionPolygonPrecise = mitk::PlanarSubdivisionPolygon::New(); + planarSubdivisionPolygonPrecise->SetClosed( false ); + planarSubdivisionPolygonPrecise->SetGeometry2D( preciseGeometry ); + planarSubdivisionPolygonPrecise->PlaceFigure( p0precise ); + planarSubdivisionPolygonPrecise->SetCurrentControlPoint( p1precise ); + planarSubdivisionPolygonPrecise->AddControlPoint( p2precise ); + planarSubdivisionPolygonPrecise->AddControlPoint( p3precise ); + planarFigures.push_back( planarSubdivisionPolygonPrecise.GetPointer() ); + // Create PlanarRectangle mitk::PlanarRectangle::Pointer planarRectanglePrecise = mitk::PlanarRectangle::New(); planarRectanglePrecise->SetGeometry2D( preciseGeometry ); @@ -218,6 +239,10 @@ public: { copiedFigure = mitk::PlanarPolygon::New(); } + if(strcmp((*it1)->GetNameOfClass(), "PlanarSubdivisionPolygon") == 0) + { + copiedFigure = mitk::PlanarSubdivisionPolygon::New(); + } if(strcmp((*it1)->GetNameOfClass(), "PlanarCross") == 0) { copiedFigure = mitk::PlanarCross::New(); diff --git a/Modules/PlanarFigure/Testing/mitkPlanarSubdivisionPolygonTest.cpp b/Modules/PlanarFigure/Testing/mitkPlanarSubdivisionPolygonTest.cpp new file mode 100644 index 0000000..93307c6 --- /dev/null +++ b/Modules/PlanarFigure/Testing/mitkPlanarSubdivisionPolygonTest.cpp @@ -0,0 +1,174 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date: 2010-03-15 11:12:36 +0100 (Mo, 15 Mrz 2010) $ +Version: $Revision: 21745 $ + +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 "mitkTestingMacros.h" +#include "mitkPlanarSubdivisionPolygon.h" +#include "mitkPlaneGeometry.h" + + +class mitkPlanarSubdivisionPolygonTestClass +{ + +public: + + +static void TestPlanarSubdivisionPolygonPlacement( mitk::PlanarSubdivisionPolygon::Pointer planarSubdivisionPolygon ) +{ + // Test for correct minimum number of control points in cross-mode + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetMinimumNumberOfControlPoints() == 3, "Minimum number of control points" ); + + // Test for correct maximum number of control points in cross-mode + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetMaximumNumberOfControlPoints() == 1000, "Maximum number of control points" ); + + // Test for correct rounds of subdivisionPoints + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetSubdivisionRounds() == 5, "Subdivision point generation depth" ); + + // Test for correct tension parameter + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetTensionParameter() == 0.0625, "Tension parameter" ); + + // Initial placement of planarSubdivisionPolygon + mitk::Point2D p0; + p0[0] = 25.0; p0[1] = 25.0; + planarSubdivisionPolygon->PlaceFigure( p0 ); + + // Add second control point + mitk::Point2D p1; + p1[0] = 75.0; p1[1] = 25.0; + planarSubdivisionPolygon->SetControlPoint(1, p1 ); + + // Add third control point + mitk::Point2D p2; + p2[0] = 75.0; p2[1] = 75.0; + planarSubdivisionPolygon->AddControlPoint( p2 ); + + // Add fourth control point + mitk::Point2D p3; + p3[0] = 25.0; p3[1] = 75.0; + planarSubdivisionPolygon->AddControlPoint( p3 ); + + // Test for number of control points + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetNumberOfControlPoints() == 4, "Number of control points after placement" ); + + // Test if PlanarFigure is closed + MITK_TEST_CONDITION( planarSubdivisionPolygon->IsClosed(), "Test if property 'closed' is set by default" ); + + // Test for number of polylines + const mitk::PlanarFigure::PolyLineType polyLine0 = planarSubdivisionPolygon->GetPolyLine( 0 ); + mitk::PlanarFigure::PolyLineType::const_iterator iter = polyLine0.begin(); + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetPolyLinesSize() == 1, "Number of polylines after placement" ); + + // Test if subdivision point count is correct + MITK_TEST_CONDITION( polyLine0.size() == 128, "correct number of subdivision points for this depth level" ); + + // Test if control points are in correct order between subdivision points + bool correctPoint = true; + iter = polyLine0.begin(); + advance(iter, 31); + if( iter->Point != p0 ){ correctPoint = false; } + advance(iter, 32); + if( iter->Point != p1 ){ correctPoint = false; } + advance(iter, 32); + if( iter->Point != p2 ){ correctPoint = false; } + advance(iter, 32); + if( iter->Point != p3 ){ correctPoint = false; } + MITK_TEST_CONDITION( correctPoint, "Test if control points are in correct order in polyline" ); + + // Test if a picked point has the correct coordinates + mitk::Point2D testPoint; + testPoint[0] = 50.000; + testPoint[1] = 18.750; + iter = polyLine0.begin(); + advance(iter, 47); + MITK_TEST_CONDITION( iter->Point == testPoint, "Test if subdivision points are calculated correctly" ); + + // Test for number of measurement features + /* + Does not work yet + + planarSubdivisionPolygon->EvaluateFeatures(); + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetNumberOfFeatures() == 2, "Number of measurement features" ); + + // Test for correct feature evaluation + double length0 = 4 * 50.0; // circumference + MITK_TEST_CONDITION( fabs( planarSubdivisionPolygon->GetQuantity( 0 ) - length0) < mitk::eps, "Size of longest diameter" ); + + double length1 = 50.0 * 50.0 ; // area + MITK_TEST_CONDITION( fabs( planarSubdivisionPolygon->GetQuantity( 1 ) - length1) < mitk::eps, "Size of short axis diameter" ); + */ +} + +static void TestPlanarSubdivisionPolygonEditing( mitk::PlanarSubdivisionPolygon::Pointer planarSubdivisionPolygon ) +{ + int initialNumberOfControlPoints = planarSubdivisionPolygon->GetNumberOfControlPoints(); + + mitk::Point2D pnt; + pnt[0] = 75.0; pnt[1] = 25.0; + planarSubdivisionPolygon->AddControlPoint( pnt); + + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetNumberOfControlPoints() == initialNumberOfControlPoints+1, "A new control-point shall be added" ); + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetControlPoint( planarSubdivisionPolygon->GetNumberOfControlPoints()-1 ) == pnt, "Control-point shall be added at the end." ); + + + planarSubdivisionPolygon->RemoveControlPoint( 3 ); + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetNumberOfControlPoints() == initialNumberOfControlPoints, "A control-point has been removed" ); + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetControlPoint( 3 ) == pnt, "It shall be possible to remove any control-point." ); + + planarSubdivisionPolygon->RemoveControlPoint( 0 ); + planarSubdivisionPolygon->RemoveControlPoint( 0 ); + planarSubdivisionPolygon->RemoveControlPoint( 0 ); + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetNumberOfControlPoints() == 3, "Control-points cannot be removed if only three points remain." ); + + mitk::Point2D pnt1; + pnt1[0] = 33.0; pnt1[1] = 33.0; + planarSubdivisionPolygon->AddControlPoint( pnt1, 0 ); + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetNumberOfControlPoints() == 4, "A control-point has been added" ); + MITK_TEST_CONDITION( planarSubdivisionPolygon->GetControlPoint( 0 ) == pnt1, "It shall be possible to insert a control-point at any position." ); + +} + +}; +/** + * mitkplanarSubdivisionPolygonTest tests the methods and behavior of mitk::planarSubdivisionPolygon with sub-tests: + * + * 1. Instantiation and basic tests, including feature evaluation + * + */ +int mitkPlanarSubdivisionPolygonTest(int /* argc */, char* /*argv*/[]) +{ + // always start with this! + MITK_TEST_BEGIN("planarSubdivisionPolygon") + + // create PlaneGeometry on which to place the planarSubdivisionPolygon + mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); + planeGeometry->InitializeStandardPlane( 100.0, 100.0 ); + + // ************************************************************************** + // 1. Instantiation and basic tests, including feature evaluation + mitk::PlanarSubdivisionPolygon::Pointer planarSubdivisionPolygon = mitk::PlanarSubdivisionPolygon::New(); + planarSubdivisionPolygon->SetGeometry2D( planeGeometry ); + + // first test: did this work? + MITK_TEST_CONDITION_REQUIRED( planarSubdivisionPolygon.IsNotNull(), "Testing instantiation" ); + + // Test placement of planarSubdivisionPolygon by control points + mitkPlanarSubdivisionPolygonTestClass::TestPlanarSubdivisionPolygonPlacement( planarSubdivisionPolygon ); + + mitkPlanarSubdivisionPolygonTestClass::TestPlanarSubdivisionPolygonEditing( planarSubdivisionPolygon ); + + // always end with this! + MITK_TEST_END(); +} -- 1.7.0.4 From a1fc9ee6e2b779ea855efcdbc2a2c045836e1711 Mon Sep 17 00:00:00 2001 From: Adrian Holfter Date: Thu, 14 Jul 2011 16:42:07 +0200 Subject: [PATCH 3/3] Fixes in SubdivisionPolygon class and unittest. --- .../mitkPlanarSubdivisionPolygon.cpp | 58 +++++++++++--------- .../Testing/mitkPlanarSubdivisionPolygonTest.cpp | 18 ++++++- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp index cd72df8..6872393 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp @@ -46,9 +46,6 @@ void mitk::PlanarSubdivisionPolygon::GeneratePolyLine() this->ClearPolyLines(); ControlPointListType m_SubdivisionPoints = m_ControlPoints; - // Later, we have to know which points are control points - ControlPointListType m_ControlPointsCopy = m_ControlPoints; - if( m_ControlPoints.size() >= GetMinimumNumberOfControlPoints() ){ for( unsigned int i=0; i < GetSubdivisionRounds(); i++ ) @@ -56,19 +53,10 @@ void mitk::PlanarSubdivisionPolygon::GeneratePolyLine() // Indices unsigned int p_here, p_prev, p_next, p_nextnext; - // Just in case the poly isn't closed. It should be, but who knows... - if(this->IsClosed()) - { - p_here = 0; - p_prev = m_SubdivisionPoints.size() - 1; - p_next = 1; - p_nextnext = 2; - }else{ - p_prev = 0; - p_here = 1; - p_next = 2; - p_nextnext = 3; - } + p_prev = m_SubdivisionPoints.size() -1; + p_here = 0; + p_next = 1; + p_nextnext = 2; double distanceToSubPoint; double distanceToPointLeft; @@ -76,26 +64,33 @@ void mitk::PlanarSubdivisionPolygon::GeneratePolyLine() 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 control points if we are the "major" / last control point being dragged around + /* + * 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) { - unsigned int prev = 0; - unsigned int next = ( m_SubdivisionPoints.size() > 1 ? p_here - 1 : 0 ); 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 + // 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] ) @@ -112,7 +107,7 @@ void mitk::PlanarSubdivisionPolygon::GeneratePolyLine() p_nextnext++; } - // Advance array indices and wrap around at the end + // Advance array indices and wrap them around at the end p_prev = p_here; if(p_here >= (m_SubdivisionPoints.size() -1 )) @@ -151,19 +146,27 @@ void mitk::PlanarSubdivisionPolygon::GeneratePolyLine() } - int lastControlPoint = m_SubdivisionPoints.size() - 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 ); } } @@ -174,6 +177,7 @@ void mitk::PlanarSubdivisionPolygon::EvaluateFeaturesInternal() 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; diff --git a/Modules/PlanarFigure/Testing/mitkPlanarSubdivisionPolygonTest.cpp b/Modules/PlanarFigure/Testing/mitkPlanarSubdivisionPolygonTest.cpp index 93307c6..2632b29 100644 --- a/Modules/PlanarFigure/Testing/mitkPlanarSubdivisionPolygonTest.cpp +++ b/Modules/PlanarFigure/Testing/mitkPlanarSubdivisionPolygonTest.cpp @@ -88,12 +88,28 @@ static void TestPlanarSubdivisionPolygonPlacement( mitk::PlanarSubdivisionPolygo MITK_TEST_CONDITION( correctPoint, "Test if control points are in correct order in polyline" ); // Test if a picked point has the correct coordinates + correctPoint = true; + mitk::Point2D testPoint; testPoint[0] = 50.000; testPoint[1] = 18.750; iter = polyLine0.begin(); advance(iter, 47); - MITK_TEST_CONDITION( iter->Point == testPoint, "Test if subdivision points are calculated correctly" ); + if( (iter->Point[0] - testPoint[0]) + (iter->Point[1] - testPoint[1]) > mitk::eps ){ correctPoint = false; } + + testPoint[0] = 20.96007347106933593750; + testPoint[1] = 58.74700927734375000000; + iter = polyLine0.begin(); + advance(iter, 10); + if( (iter->Point[0] - testPoint[0]) + (iter->Point[1] - testPoint[1]) > mitk::eps ){ correctPoint = false; } + + testPoint[0] = 76.96900177001953125000; + testPoint[1] = 30.05101013183593750000; + iter = polyLine0.begin(); + advance(iter, 67); + if( (iter->Point[0] - testPoint[0]) + (iter->Point[1] - testPoint[1]) > mitk::eps ){ correctPoint = false; } + + MITK_TEST_CONDITION( correctPoint, "Test if subdivision points are calculated correctly" ) // Test for number of measurement features /* -- 1.7.0.4