diff --git a/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp b/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp index ff3ac6b625..3656bf83a6 100755 --- a/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp +++ b/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp @@ -1,238 +1,238 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include #include mitk::ContourModelUtils::ContourModelUtils() { } mitk::ContourModelUtils::~ContourModelUtils() { } mitk::ContourModel::Pointer mitk::ContourModelUtils::ProjectContourTo2DSlice( const Image *slice, const ContourModel *contourIn3D, bool, bool) { if (nullptr == slice || nullptr == contourIn3D) return nullptr; auto projectedContour = ContourModel::New(); projectedContour->Initialize(*contourIn3D); auto sliceGeometry = slice->GetGeometry(); auto numberOfTimesteps = static_cast(contourIn3D->GetTimeSteps()); for (decltype(numberOfTimesteps) t = 0; t < numberOfTimesteps; ++t) { auto iter = contourIn3D->Begin(t); auto end = contourIn3D->End(t); while (iter != end) { const auto ¤tPointIn3D = (*iter)->Coordinates; Point3D projectedPointIn2D; projectedPointIn2D.Fill(0.0); sliceGeometry->WorldToIndex(currentPointIn3D, projectedPointIn2D); projectedContour->AddVertex(projectedPointIn2D, t); ++iter; } } return projectedContour; } mitk::ContourModel::Pointer mitk::ContourModelUtils::BackProjectContourFrom2DSlice( const BaseGeometry *sliceGeometry, const ContourModel *contourIn2D, bool) { if (nullptr == sliceGeometry || nullptr == contourIn2D) return nullptr; auto worldContour = ContourModel::New(); worldContour->Initialize(*contourIn2D); auto numberOfTimesteps = static_cast(contourIn2D->GetTimeSteps()); for (decltype(numberOfTimesteps) t = 0; t < numberOfTimesteps; ++t) { auto iter = contourIn2D->Begin(t); auto end = contourIn2D->End(t); while (iter != end) { const auto ¤tPointIn2D = (*iter)->Coordinates; Point3D worldPointIn3D; worldPointIn3D.Fill(0.0); sliceGeometry->IndexToWorld(currentPointIn2D, worldPointIn3D); worldContour->AddVertex(worldPointIn3D, t); ++iter; } } return worldContour; } void mitk::ContourModelUtils::FillContourInSlice( const ContourModel *projectedContour, Image *sliceImage, const Image* workingImage, int paintingPixelValue) { FillContourInSlice(projectedContour, 0, sliceImage, workingImage, paintingPixelValue); } void mitk::ContourModelUtils::FillContourInSlice( const ContourModel *projectedContour, unsigned int contourTimeStep, Image *sliceImage, const Image* workingImage, int paintingPixelValue) { if (nullptr == projectedContour) { mitkThrow() << "Cannot fill contour in slice. Passed contour is invalid"; } if (nullptr == sliceImage) { mitkThrow() << "Cannot fill contour in slice. Passed slice is invalid"; } auto contourModelFilter = mitk::ContourModelToSurfaceFilter::New(); contourModelFilter->SetInput(projectedContour); contourModelFilter->Update(); auto surface = mitk::Surface::New(); surface = contourModelFilter->GetOutput(); if (nullptr == surface->GetVtkPolyData(contourTimeStep)) { MITK_WARN << "Could not create surface from contour model."; return; } auto surface2D = vtkSmartPointer::New(); surface2D->SetPoints(surface->GetVtkPolyData(contourTimeStep)->GetPoints()); surface2D->SetLines(surface->GetVtkPolyData(contourTimeStep)->GetLines()); auto image = vtkSmartPointer::New(); image->DeepCopy(sliceImage->GetVtkImageData()); const double FOREGROUND_VALUE = 255.0; const double BACKGROUND_VALUE = 0.0; vtkIdType count = image->GetNumberOfPoints(); for (decltype(count) i = 0; i < count; ++i) image->GetPointData()->GetScalars()->SetTuple1(i, FOREGROUND_VALUE); auto polyDataToImageStencil = vtkSmartPointer::New(); // Set a minimal tolerance, so that clipped pixels will be added to contour as well. polyDataToImageStencil->SetTolerance(mitk::eps); polyDataToImageStencil->SetInputData(surface2D); polyDataToImageStencil->Update(); auto imageStencil = vtkSmartPointer::New(); imageStencil->SetInputData(image); imageStencil->SetStencilConnection(polyDataToImageStencil->GetOutputPort()); imageStencil->ReverseStencilOff(); imageStencil->SetBackgroundValue(BACKGROUND_VALUE); imageStencil->Update(); vtkSmartPointer filledImage = imageStencil->GetOutput(); vtkSmartPointer resultImage = sliceImage->GetVtkImageData(); FillSliceInSlice(filledImage, resultImage, workingImage, paintingPixelValue); sliceImage->SetVolume(resultImage->GetScalarPointer()); } void mitk::ContourModelUtils::FillSliceInSlice( vtkSmartPointer filledImage, vtkSmartPointer resultImage, const Image* image, int paintingPixelValue) { auto labelImage = dynamic_cast(image); auto numberOfPoints = filledImage->GetNumberOfPoints(); if (nullptr == labelImage) { for (decltype(numberOfPoints) i = 0; i < numberOfPoints; ++i) { if (1 < filledImage->GetPointData()->GetScalars()->GetTuple1(i)) resultImage->GetPointData()->GetScalars()->SetTuple1(i, paintingPixelValue); } } else { auto backgroundValue = labelImage->GetExteriorLabel()->GetValue(); if (paintingPixelValue != backgroundValue) { for (decltype(numberOfPoints) i = 0; i < numberOfPoints; ++i) { if (1 < filledImage->GetPointData()->GetScalars()->GetTuple1(i)) { auto existingValue = resultImage->GetPointData()->GetScalars()->GetTuple1(i); if (!labelImage->GetLabel(existingValue, labelImage->GetActiveLayer())->GetLocked()) resultImage->GetPointData()->GetScalars()->SetTuple1(i, paintingPixelValue); } } } else { auto activePixelValue = labelImage->GetActiveLabel(labelImage->GetActiveLayer())->GetValue(); for (decltype(numberOfPoints) i = 0; i < numberOfPoints; ++i) { if (1 < filledImage->GetPointData()->GetScalars()->GetTuple1(i)) { if (resultImage->GetPointData()->GetScalars()->GetTuple1(i) == activePixelValue) resultImage->GetPointData()->GetScalars()->SetTuple1(i, paintingPixelValue); } } } } } mitk::ContourModel::Pointer mitk::ContourModelUtils::MoveZerothContourTimeStep(const ContourModel *contour, unsigned int t) { if (nullptr == contour) return nullptr; auto resultContour = ContourModel::New(); resultContour->Expand(t + 1); std::for_each(contour->Begin(), contour->End(), [&resultContour, t](ContourElement::VertexType *vertex) { - resultContour->AddVertex(vertex, t); + resultContour->AddVertex(*vertex, t); }); return resultContour; } int mitk::ContourModelUtils::GetActivePixelValue(const Image* workingImage) { auto labelSetImage = dynamic_cast(workingImage); int activePixelValue = 1; if (nullptr != labelSetImage) { activePixelValue = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer())->GetValue(); } return activePixelValue; } diff --git a/Modules/ContourModel/DataManagement/mitkContourElement.cpp b/Modules/ContourModel/DataManagement/mitkContourElement.cpp index 1542d5a74d..97b3becdc2 100644 --- a/Modules/ContourModel/DataManagement/mitkContourElement.cpp +++ b/Modules/ContourModel/DataManagement/mitkContourElement.cpp @@ -1,471 +1,389 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include -mitk::ContourElement::ContourElement() +mitk::ContourElement::ConstVertexIterator mitk::ContourElement::ConstIteratorBegin() const { - this->m_Vertices = new VertexListType(); - this->m_IsClosed = false; + return this->begin(); } -mitk::ContourElement::ContourElement(const mitk::ContourElement &other) - : itk::LightObject(), m_Vertices(other.m_Vertices), m_IsClosed(other.m_IsClosed) +mitk::ContourElement::ConstVertexIterator mitk::ContourElement::ConstIteratorEnd() const { + return this->end(); } -mitk::ContourElement::~ContourElement() +mitk::ContourElement::VertexIterator mitk::ContourElement::IteratorBegin() { - delete this->m_Vertices; + return this->begin(); } -void mitk::ContourElement::AddVertex(const mitk::Point3D &vertex, bool isControlPoint) +mitk::ContourElement::VertexIterator mitk::ContourElement::IteratorEnd() { - this->m_Vertices->push_back(new VertexType(vertex, isControlPoint)); + return this->end(); } -void mitk::ContourElement::AddVertex(VertexType &vertex) +mitk::ContourElement::ConstVertexIterator mitk::ContourElement::begin() const { - this->m_Vertices->push_back(&vertex); + return this->m_Vertices.begin(); } -void mitk::ContourElement::AddVertexAtFront(const mitk::Point3D &vertex, bool isControlPoint) +mitk::ContourElement::ConstVertexIterator mitk::ContourElement::end() const { - this->m_Vertices->push_front(new VertexType(vertex, isControlPoint)); + return this->m_Vertices.end(); } -void mitk::ContourElement::AddVertexAtFront(VertexType &vertex) +mitk::ContourElement::VertexIterator mitk::ContourElement::begin() { - this->m_Vertices->push_front(&vertex); + return this->m_Vertices.begin(); } -void mitk::ContourElement::InsertVertexAtIndex(const mitk::Point3D &vertex, bool isControlPoint, int index) +mitk::ContourElement::VertexIterator mitk::ContourElement::end() +{ + return this->m_Vertices.end(); +} + +mitk::ContourElement::ContourElement(const mitk::ContourElement &other) + : itk::LightObject(), m_IsClosed(other.m_IsClosed) +{ + for (const auto& v : m_Vertices) + { + m_Vertices.push_back(new ContourModelVertex(*v)); + } +} + +void mitk::ContourElement::AddVertex(const mitk::Point3D &vertex, bool isControlPoint) +{ + this->m_Vertices.push_back(new VertexType(vertex, isControlPoint)); +} + +void mitk::ContourElement::AddVertexAtFront(const mitk::Point3D &vertex, bool isControlPoint) +{ + this->m_Vertices.push_front(new VertexType(vertex, isControlPoint)); +} + +void mitk::ContourElement::InsertVertexAtIndex(const mitk::Point3D &vertex, bool isControlPoint, VertexSizeType index) { if (index >= 0 && this->GetSize() > index) { - auto _where = this->m_Vertices->begin(); + auto _where = this->m_Vertices.begin(); _where += index; - this->m_Vertices->insert(_where, new VertexType(vertex, isControlPoint)); + this->m_Vertices.insert(_where, new VertexType(vertex, isControlPoint)); } } -void mitk::ContourElement::SetVertexAt(int pointId, const Point3D &point) +void mitk::ContourElement::SetVertexAt(VertexSizeType pointId, const Point3D &point) { if (pointId >= 0 && this->GetSize() > pointId) { - this->m_Vertices->at(pointId)->Coordinates = point; + this->m_Vertices.at(pointId)->Coordinates = point; } } -void mitk::ContourElement::SetVertexAt(int pointId, const VertexType *vertex) +void mitk::ContourElement::SetVertexAt(VertexSizeType pointId, const VertexType *vertex) { + if (nullptr == vertex) + { + mitkThrow() << "Cannot set vertex. Passed vertex instance is invalid. Index to set: " << pointId; + } + if (pointId >= 0 && this->GetSize() > pointId) { - this->m_Vertices->at(pointId)->Coordinates = vertex->Coordinates; - this->m_Vertices->at(pointId)->IsControlPoint = vertex->IsControlPoint; + this->m_Vertices.at(pointId)->Coordinates = vertex->Coordinates; + this->m_Vertices.at(pointId)->IsControlPoint = vertex->IsControlPoint; } } -mitk::ContourElement::VertexType *mitk::ContourElement::GetVertexAt(int index) +mitk::ContourElement::VertexType *mitk::ContourElement::GetVertexAt(VertexSizeType index) { - return this->m_Vertices->at(index); + return this->m_Vertices.at(index); } -bool mitk::ContourElement::IsEmpty() +const mitk::ContourElement::VertexType* mitk::ContourElement::GetVertexAt(VertexSizeType index) const { - return this->m_Vertices->empty(); + return this->m_Vertices.at(index); +} + +bool mitk::ContourElement::IsEmpty() const +{ + return this->m_Vertices.empty(); } mitk::ContourElement::VertexType *mitk::ContourElement::GetVertexAt(const mitk::Point3D &point, float eps) { /* current version iterates over the whole deque - should some kind of an octree with spatial query*/ if (eps > 0) { // currently no method with better performance is available return BruteForceGetVertexAt(point, eps); } // if eps < 0 return nullptr; } -mitk::ContourElement::VertexType *mitk::ContourElement::BruteForceGetVertexAt(const mitk::Point3D &point, float eps) +mitk::ContourElement::VertexType *mitk::ContourElement::BruteForceGetVertexAt(const mitk::Point3D &point, double eps) { if (eps > 0) { std::deque> nearestlist; - ConstVertexIterator it = this->m_Vertices->begin(); + ConstVertexIterator it = this->m_Vertices.begin(); - ConstVertexIterator end = this->m_Vertices->end(); + ConstVertexIterator end = this->m_Vertices.end(); while (it != end) { mitk::Point3D currentPoint = (*it)->Coordinates; double distance = currentPoint.EuclideanDistanceTo(point); if (distance < eps) { // if list is emtpy, add point to list if (nearestlist.size() < 1) { nearestlist.push_front(std::pair((*it)->Coordinates.EuclideanDistanceTo(point), (*it))); } // found an approximate point - check if current is closer then first in nearestlist else if (distance < nearestlist.front().first) { // found even closer vertex nearestlist.push_front(std::pair((*it)->Coordinates.EuclideanDistanceTo(point), (*it))); } } // if distance > eps it++; } // while if (nearestlist.size() > 0) { /*++++++++++++++++++++ return the nearest active point if one was found++++++++++++++++++*/ auto it = nearestlist.begin(); auto end = nearestlist.end(); while (it != end) { if ((*it).second->IsControlPoint) { return (*it).second; } it++; } /*---------------------------------------------------------------------------------------*/ // return closest point return nearestlist.front().second; } } return nullptr; } -/*mitk::ContourElement::VertexType* mitk::ContourElement::OptimizedGetVertexAt(const mitk::Point3D &point, float eps) +const mitk::ContourElement::VertexListType *mitk::ContourElement::GetVertexList() const { - if( (eps > 0) && (this->m_Vertices->size()>0) ) - { - int k = 1; - int dim = 3; - int nPoints = this->m_Vertices->size(); - ANNpointArray pointsArray; - ANNpoint queryPoint; - ANNidxArray indexArray; - ANNdistArray distanceArray; - ANNkd_tree* kdTree; - - queryPoint = annAllocPt(dim); - pointsArray = annAllocPts(nPoints, dim); - indexArray = new ANNidx[k]; - distanceArray = new ANNdist[k]; - - - int i = 0; - - //fill points array with our control points - for(VertexIterator it = this->m_Vertices->begin(); it != this->m_Vertices->end(); it++, i++) - { - mitk::Point3D cur = (*it)->Coordinates; - pointsArray[i][0]= cur[0]; - pointsArray[i][1]= cur[1]; - pointsArray[i][2]= cur[2]; - } - - //create the kd tree - kdTree = new ANNkd_tree(pointsArray,nPoints, dim); - - //fill mitk::Point3D into ANN query point - queryPoint[0] = point[0]; - queryPoint[1] = point[1]; - queryPoint[2] = point[2]; - - //k nearest neighbour search - kdTree->annkSearch(queryPoint, k, indexArray, distanceArray, eps); - - VertexType* ret = nullptr; - - try - { - ret = this->m_Vertices->at(indexArray[0]); - } - catch(std::out_of_range ex) - { - //ret stays nullptr - return ret; - } - - //clean up ANN - delete [] indexArray; - delete [] distanceArray; - delete kdTree; - annClose(); - - return ret; - } - return nullptr; + return &(this->m_Vertices); } -*/ -mitk::ContourElement::VertexListType *mitk::ContourElement::GetVertexList() -{ - return this->m_Vertices; -} - -bool mitk::ContourElement::IsClosed() +bool mitk::ContourElement::IsClosed() const { return this->m_IsClosed; } -bool mitk::ContourElement::IsNearContour(const mitk::Point3D &point, float eps) +bool mitk::ContourElement::IsNearContour(const mitk::Point3D &point, float eps) const { - ConstVertexIterator it1 = this->m_Vertices->begin(); - ConstVertexIterator it2 = this->m_Vertices->begin(); + ConstVertexIterator it1 = this->m_Vertices.begin(); + ConstVertexIterator it2 = this->m_Vertices.begin(); it2++; // it2 runs one position ahead - ConstVertexIterator end = this->m_Vertices->end(); + ConstVertexIterator end = this->m_Vertices.end(); int counter = 0; for (; it1 != end; it1++, it2++, counter++) { if (it2 == end) - it2 = this->m_Vertices->begin(); + it2 = this->m_Vertices.begin(); mitk::Point3D v1 = (*it1)->Coordinates; mitk::Point3D v2 = (*it2)->Coordinates; const float l2 = v1.SquaredEuclideanDistanceTo(v2); mitk::Vector3D p_v1 = point - v1; mitk::Vector3D v2_v1 = v2 - v1; double tc = (p_v1 * v2_v1) / l2; // take into account we have line segments and not (infinite) lines if (tc < 0.0) tc = 0.0; if (tc > 1.0) tc = 1.0; mitk::Point3D crossPoint = v1 + v2_v1 * tc; double distance = point.SquaredEuclideanDistanceTo(crossPoint); if (distance < eps) { return true; } } return false; } void mitk::ContourElement::Close() { this->m_IsClosed = true; } void mitk::ContourElement::Open() { this->m_IsClosed = false; } void mitk::ContourElement::SetClosed(bool isClosed) { isClosed ? this->Close() : this->Open(); } -mitk::ContourElement::VertexListType *mitk::ContourElement::GetControlVertices() +mitk::ContourElement::VertexListType mitk::ContourElement::GetControlVertices() const { - auto newVertices = new VertexListType(); - - auto it = this->m_Vertices->begin(); - auto end = this->m_Vertices->end(); + VertexListType controlVertices; - while (it != end) - { - if ((*it)->IsControlPoint) - { - newVertices->push_back((*it)); - } - it++; - } + std::copy_if(this->m_Vertices.begin(), this->m_Vertices.end(), std::back_inserter(controlVertices), [](const VertexType* v) {return v->IsControlPoint; }); - return newVertices; + return controlVertices; } -void mitk::ContourElement::Concatenate(mitk::ContourElement *other, bool check) +void mitk::ContourElement::Concatenate(const mitk::ContourElement *other, bool check) { if (other->GetSize() > 0) { - ConstVertexIterator otherIt = other->m_Vertices->begin(); - ConstVertexIterator otherEnd = other->m_Vertices->end(); - while (otherIt != otherEnd) + for (const auto& sourceVertex : other->m_Vertices) { if (check) { - ConstVertexIterator thisIt = this->m_Vertices->begin(); - ConstVertexIterator thisEnd = this->m_Vertices->end(); + auto finding = std::find_if(this->m_Vertices.begin(), this->m_Vertices.end(), [sourceVertex](const VertexType* v) {return sourceVertex->Coordinates == v->Coordinates; }); - bool found = false; - while (thisIt != thisEnd) + if (finding == this->m_Vertices.end()) { - if ((*thisIt)->Coordinates == (*otherIt)->Coordinates) - { - found = true; - break; - } - - thisIt++; + this->m_Vertices.push_back(new ContourModelVertex(*sourceVertex)); } - if (!found) - this->m_Vertices->push_back(*otherIt); } else { - this->m_Vertices->push_back(*otherIt); + this->m_Vertices.push_back(new ContourModelVertex(*sourceVertex)); } - otherIt++; } } } -bool mitk::ContourElement::RemoveVertex(const VertexType *vertex) +mitk::ContourElement::VertexSizeType mitk::ContourElement::GetIndex(const VertexType* vertex) const { - auto it = this->m_Vertices->begin(); + VertexSizeType result = NPOS; - auto end = this->m_Vertices->end(); + auto finding = std::find(this->m_Vertices.begin(), this->m_Vertices.end(), vertex); - // search for vertex and remove it if exists - while (it != end) + if (finding != this->m_Vertices.end()) { - if ((*it) == vertex) - { - this->m_Vertices->erase(it); - return true; - } - - it++; + result = finding - this->m_Vertices.begin(); } - return false; + return result; } -int mitk::ContourElement::GetIndex(const VertexType *vertex) +bool mitk::ContourElement::RemoveVertex(const VertexType *vertex) { - auto it = this->m_Vertices->begin(); - - auto end = this->m_Vertices->end(); + auto finding = std::find(this->m_Vertices.begin(), this->m_Vertices.end(), vertex); - int index = 0; + return RemoveVertexByIterator(finding); +} - // search for vertex - while (it != end) +bool mitk::ContourElement::RemoveVertexAt(VertexSizeType index) +{ + if (index >= 0 && index < this->m_Vertices.size()) { - if ((*it) == vertex) - { - return index; - } - - it++; - ++index; + auto delIter = this->m_Vertices.begin() + index; + return RemoveVertexByIterator(delIter); } - return -1; // not found + return false; } -bool mitk::ContourElement::RemoveVertexAt(int index) +bool mitk::ContourElement::RemoveVertexAt(const mitk::Point3D &point, double eps) { - if (index >= 0 && static_cast(index) < this->m_Vertices->size()) - { - this->m_Vertices->erase(this->m_Vertices->begin() + index); - return true; - } - else + if (eps > 0) { - return false; + auto finding = std::find_if(this->m_Vertices.begin(), this->m_Vertices.end(), [point, eps](const VertexType* v) {return v->Coordinates.EuclideanDistanceTo(point) < eps; }); + + return RemoveVertexByIterator(finding); } + return false; } -bool mitk::ContourElement::RemoveVertexAt(mitk::Point3D &point, float eps) +bool mitk::ContourElement::RemoveVertexByIterator(VertexListType::iterator& iter) { - /* current version iterates over the whole deque - should be some kind of an octree with spatial query*/ - - if (eps > 0) + if (iter != this->m_Vertices.end()) { - auto it = this->m_Vertices->begin(); - - auto end = this->m_Vertices->end(); - - while (it != end) - { - mitk::Point3D currentPoint = (*it)->Coordinates; - - if (currentPoint.EuclideanDistanceTo(point) < eps) - { - // approximate point found - // now erase it - this->m_Vertices->erase(it); - return true; - } - - it++; - } + delete* iter; + this->m_Vertices.erase(iter); + return true; } + return false; } void mitk::ContourElement::Clear() { - this->m_Vertices->clear(); + for (auto vertex : m_Vertices) + { + delete vertex; + } + this->m_Vertices.clear(); } + //---------------------------------------------------------------------- void mitk::ContourElement::RedistributeControlVertices(const VertexType *selected, int period) { int counter = 0; - auto _where = this->m_Vertices->begin(); + auto _where = this->m_Vertices.begin(); if (selected != nullptr) { - while (_where != this->m_Vertices->end()) + auto finding = std::find(this->m_Vertices.begin(), this->m_Vertices.end(), selected); + + if (finding != this->m_Vertices.end()) { - if ((*_where) == selected) - { - break; - } - _where++; + _where = finding; } } auto _iter = _where; - while (_iter != this->m_Vertices->end()) + while (_iter != this->m_Vertices.end()) { div_t divresult; divresult = div(counter, period); (*_iter)->IsControlPoint = (divresult.rem == 0); counter++; _iter++; } _iter = _where; counter = 0; - while (_iter != this->m_Vertices->begin()) + while (_iter != this->m_Vertices.begin()) { div_t divresult; divresult = div(counter, period); (*_iter)->IsControlPoint = (divresult.rem == 0); counter++; _iter--; } } diff --git a/Modules/ContourModel/DataManagement/mitkContourElement.h b/Modules/ContourModel/DataManagement/mitkContourElement.h index b9d6c1d3d9..f302ddc574 100644 --- a/Modules/ContourModel/DataManagement/mitkContourElement.h +++ b/Modules/ContourModel/DataManagement/mitkContourElement.h @@ -1,231 +1,248 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _mitkContourElement_H_ #define _mitkContourElement_H_ #include "mitkCommon.h" #include #include -//#include - #include namespace mitk { /** \brief Represents a contour in 3D space. A ContourElement is consisting of linked vertices implicitely defining the contour. They are stored in a double ended queue making it possible to add vertices at front and end of the contour and to iterate in both directions. To mark a vertex as a special one it can be set as a control point. - \note It is highly not recommend to use this class directly as no secure mechanism is used here. - Use mitk::ContourModel instead providing some additional features. + \note This class assumes that it manages its vertices. So if a vertex instance is added to this + class the ownership of the vertex is transfered to the ContourElement instance. + The ContourElement instance takes care of deleting vertex instances if needed. + It is highly not recommend to use this class directly as it is designed as a internal class of + ContourModel. Therefore it is adviced to use ContourModel if contour representations are needed in + MITK. */ class MITKCONTOURMODEL_EXPORT ContourElement : public itk::LightObject { public: mitkClassMacroItkParent(ContourElement, itk::LightObject); itkFactorylessNewMacro(Self); itkCloneMacro(Self); - // Data container representing vertices - - /** \brief Represents a single vertex of contour. - */ - struct ContourModelVertex + /** \brief Represents a single vertex of a contour. + */ + struct ContourModelVertex { - ContourModelVertex(const mitk::Point3D &point, bool active = false) : IsControlPoint(active), Coordinates(point) {} - ContourModelVertex(const ContourModelVertex &other) + ContourModelVertex(const mitk::Point3D& point, bool active = false) : IsControlPoint(active), Coordinates(point) {}; + ContourModelVertex(const ContourModelVertex& other) : IsControlPoint(other.IsControlPoint), Coordinates(other.Coordinates) { - } + }; /** \brief Treat point special. */ bool IsControlPoint; /** \brief Coordinates in 3D space. */ mitk::Point3D Coordinates; }; - // END Data container representing vertices - typedef ContourModelVertex VertexType; - typedef std::deque VertexListType; - typedef VertexListType::iterator VertexIterator; - typedef VertexListType::const_iterator ConstVertexIterator; + using VertexType = ContourModelVertex; + using VertexListType = std::deque; + using VertexIterator = VertexListType::iterator; + using ConstVertexIterator = VertexListType::const_iterator; + using VertexSizeType = VertexListType::size_type; - // start of inline methods + /**Indicates an invalid index.*/ + static const VertexSizeType NPOS = -1; + + /** \brief Return a const iterator a the front. + */ + ConstVertexIterator ConstIteratorBegin() const; + /** \brief Return a const iterator a the end. + */ + ConstVertexIterator ConstIteratorEnd() const; + /** \brief Return an iterator a the front. + */ + VertexIterator IteratorBegin(); + /** \brief Return an iterator a the end. + */ + VertexIterator IteratorEnd(); /** \brief Return a const iterator a the front. + * For easier support of stl functionality. */ - virtual ConstVertexIterator ConstIteratorBegin() { return this->m_Vertices->begin(); } + ConstVertexIterator begin() const; /** \brief Return a const iterator a the end. + * For easier support of stl functionality. */ - virtual ConstVertexIterator ConstIteratorEnd() { return this->m_Vertices->end(); } + ConstVertexIterator end() const; /** \brief Return an iterator a the front. + * For easier support of stl functionality. */ - virtual VertexIterator IteratorBegin() { return this->m_Vertices->begin(); } + VertexIterator begin(); /** \brief Return an iterator a the end. + * For easier support of stl functionality. */ - virtual VertexIterator IteratorEnd() { return this->m_Vertices->end(); } + VertexIterator end(); + /** \brief Returns the number of contained vertices. */ - virtual int GetSize() { return this->m_Vertices->size(); } - // end of inline methods + VertexSizeType GetSize() const { return this->m_Vertices.size(); } /** \brief Add a vertex at the end of the contour \param point - coordinates in 3D space. \param isControlPoint - is the vertex a special control point. */ - virtual void AddVertex(const mitk::Point3D &point, bool isControlPoint); - - /** \brief Add a vertex at the end of the contour - \param vertex - a contour element vertex. - */ - virtual void AddVertex(VertexType &vertex); + void AddVertex(const mitk::Point3D &point, bool isControlPoint); /** \brief Add a vertex at the front of the contour \param point - coordinates in 3D space. \param isControlPoint - is the vertex a control point. */ - virtual void AddVertexAtFront(const mitk::Point3D &point, bool isControlPoint); - - /** \brief Add a vertex at the front of the contour - \param vertex - a contour element vertex. - */ - virtual void AddVertexAtFront(VertexType &vertex); + void AddVertexAtFront(const mitk::Point3D &point, bool isControlPoint); /** \brief Add a vertex at a given index of the contour \param point - coordinates in 3D space. \param isControlPoint - is the vertex a special control point. \param index - the index to be inserted at. */ - virtual void InsertVertexAtIndex(const mitk::Point3D &point, bool isControlPoint, int index); + void InsertVertexAtIndex(const mitk::Point3D &point, bool isControlPoint, VertexSizeType index); /** \brief Set coordinates a given index. \param pointId Index of vertex. \param point Coordinates. */ - virtual void SetVertexAt(int pointId, const mitk::Point3D &point); + void SetVertexAt(VertexSizeType pointId, const mitk::Point3D &point); - /** \brief Set vertex a given index. + /** \brief Set vertex a given index (by copying the values). \param pointId Index of vertex. \param vertex Vertex. + \pre Passed vertex is a valid instance */ - virtual void SetVertexAt(int pointId, const VertexType *vertex); + void SetVertexAt(VertexSizeType pointId, const VertexType* vertex); /** \brief Returns the vertex a given index \param index + \pre index must be valid. */ - virtual VertexType *GetVertexAt(int index); + VertexType* GetVertexAt(VertexSizeType index); + const VertexType* GetVertexAt(VertexSizeType index) const; /** \brief Returns the approximate nearest vertex a given posoition in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ - virtual VertexType *GetVertexAt(const mitk::Point3D &point, float eps); + VertexType *GetVertexAt(const mitk::Point3D &point, float eps); /** \brief Returns the index of the given vertex within the contour. \param vertex - the vertex to be searched. - \return index of vertex. -1 if not found. + \return index of vertex. Returns NPOS (==-1) if not found. */ - virtual int GetIndex(const VertexType *vertex); + VertexSizeType GetIndex(const VertexType *vertex) const; /** \brief Returns the container of the vertices. */ - VertexListType *GetVertexList(); + const VertexListType *GetVertexList() const; /** \brief Returns whether the contour element is empty. */ - bool IsEmpty(); + bool IsEmpty() const; /** \brief Returns if the conour is closed or not. */ - virtual bool IsClosed(); + bool IsClosed() const; /** \brief Returns whether a given point is near a contour, according to eps. \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ - virtual bool IsNearContour(const mitk::Point3D &point, float eps); + bool IsNearContour(const mitk::Point3D &point, float eps) const; /** \brief Close the contour. Connect first with last element. */ - virtual void Close(); + void Close(); /** \brief Open the contour. Disconnect first and last element. */ - virtual void Open(); + void Open(); /** \brief Set the contours IsClosed property. \param isClosed - true = closed; false = open; */ - virtual void SetClosed(bool isClosed); + void SetClosed(bool isClosed); /** \brief Concatenate the contuor with a another contour. - All vertices of the other contour will be added after last vertex. + All vertices of the other contour will be cloned and added after last vertex. \param other - the other contour - \param check - set it true to avoid intersections + \param check - set it true to avoid adding of vertices that are already in the source contour */ - void Concatenate(mitk::ContourElement *other, bool check); + void Concatenate(const mitk::ContourElement *other, bool check); /** \brief Remove the given vertex from the container if exists. \param vertex - the vertex to be removed. */ - virtual bool RemoveVertex(const VertexType *vertex); + bool RemoveVertex(const VertexType *vertex); /** \brief Remove a vertex at given index within the container if exists. \param index - the index where the vertex should be removed. */ - virtual bool RemoveVertexAt(int index); + bool RemoveVertexAt(VertexSizeType index); /** \brief Remove the approximate nearest vertex at given position in 3D space if one exists. \param point - query point in 3D space. \param eps - error bound for search algorithm. */ - virtual bool RemoveVertexAt(mitk::Point3D &point, float eps); + bool RemoveVertexAt(const mitk::Point3D &point, double eps); /** \brief Clear the storage container. */ - virtual void Clear(); + void Clear(); /** \brief Returns the approximate nearest vertex a given posoition in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ - VertexType *BruteForceGetVertexAt(const mitk::Point3D &point, float eps); + VertexType *BruteForceGetVertexAt(const mitk::Point3D &point, double eps); - VertexListType *GetControlVertices(); + VertexListType GetControlVertices() const; /** \brief Uniformly redistribute control points with a given period (in number of vertices) \param vertex - the vertex around which the redistribution is done. \param period - number of vertices between control points. */ void RedistributeControlVertices(const VertexType *vertex, int period); protected: mitkCloneMacro(Self); - ContourElement(); + ContourElement() = default; ContourElement(const mitk::ContourElement &other); - ~ContourElement() override; + ~ContourElement() = default; + + /** Internal helper function to correctly remove the element indicated by the iterator + from the list. After the call the iterator is invalid. + Caller of the function must ensure that the iterator is valid!. + \result Indicates if the element indicated by the iterator was removed. If iterator points to end it returns false.*/ + bool RemoveVertexByIterator(VertexListType::iterator& iter); - VertexListType *m_Vertices; // double ended queue with vertices - bool m_IsClosed; + VertexListType m_Vertices; // double ended queue with vertices + bool m_IsClosed = false; }; } // namespace mitk #endif // _mitkContourElement_H_ diff --git a/Modules/ContourModel/DataManagement/mitkContourModel.cpp b/Modules/ContourModel/DataManagement/mitkContourModel.cpp index d4153c9d7a..c0f632de46 100644 --- a/Modules/ContourModel/DataManagement/mitkContourModel.cpp +++ b/Modules/ContourModel/DataManagement/mitkContourModel.cpp @@ -1,624 +1,628 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include mitk::ContourModel::ContourModel() : m_UpdateBoundingBox(true) { // set to initial state this->InitializeEmpty(); } mitk::ContourModel::ContourModel(const ContourModel &other) : BaseData(other), m_ContourSeries(other.m_ContourSeries), m_lineInterpolation(other.m_lineInterpolation) { m_SelectedVertex = nullptr; } mitk::ContourModel::~ContourModel() { m_SelectedVertex = nullptr; this->m_ContourSeries.clear(); // TODO check destruction } void mitk::ContourModel::AddVertex(const Point3D &vertex, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->AddVertex(vertex, false, timestep); } } void mitk::ContourModel::AddVertex(const Point3D &vertex, bool isControlPoint, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertex(vertex, isControlPoint); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } -void mitk::ContourModel::AddVertex(VertexType &vertex, TimeStepType timestep) +void mitk::ContourModel::AddVertex(const VertexType &vertex, TimeStepType timestep) { - if (!this->IsEmptyTimeStep(timestep)) - { - this->m_ContourSeries[timestep]->AddVertex(vertex); - this->InvokeEvent(ContourModelSizeChangeEvent()); - this->Modified(); - this->m_UpdateBoundingBox = true; - } + this->AddVertex(vertex.Coordinates, vertex.IsControlPoint); } -void mitk::ContourModel::AddVertex(const VertexType *vertex, TimeStepType timestep) -{ - if (vertex != nullptr) - { - this->m_ContourSeries[timestep]->AddVertex(*const_cast(vertex)); - } -} - -void mitk::ContourModel::AddVertexAtFront(Point3D &vertex, TimeStepType timestep) +void mitk::ContourModel::AddVertexAtFront(const Point3D &vertex, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->AddVertexAtFront(vertex, false, timestep); } } -void mitk::ContourModel::AddVertexAtFront(Point3D &vertex, bool isControlPoint, TimeStepType timestep) +void mitk::ContourModel::AddVertexAtFront(const Point3D &vertex, bool isControlPoint, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertexAtFront(vertex, isControlPoint); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } -void mitk::ContourModel::AddVertexAtFront(VertexType &vertex, TimeStepType timestep) +void mitk::ContourModel::AddVertexAtFront(const VertexType &vertex, TimeStepType timestep) { - if (!this->IsEmptyTimeStep(timestep)) - { - this->m_ContourSeries[timestep]->AddVertexAtFront(vertex); - this->InvokeEvent(ContourModelSizeChangeEvent()); - this->Modified(); - this->m_UpdateBoundingBox = true; - } + this->AddVertexAtFront(vertex.Coordinates, vertex.IsControlPoint, timestep); } bool mitk::ContourModel::SetVertexAt(int pointId, const Point3D &point, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (pointId >= 0 && this->m_ContourSeries[timestep]->GetSize() > pointId) { this->m_ContourSeries[timestep]->SetVertexAt(pointId, point); this->Modified(); this->m_UpdateBoundingBox = true; return true; } return false; } return false; } bool mitk::ContourModel::SetVertexAt(int pointId, const VertexType *vertex, TimeStepType timestep) { if (vertex == nullptr) return false; if (!this->IsEmptyTimeStep(timestep)) { if (pointId >= 0 && this->m_ContourSeries[timestep]->GetSize() > pointId) { this->m_ContourSeries[timestep]->SetVertexAt(pointId, vertex); this->Modified(); this->m_UpdateBoundingBox = true; return true; } return false; } return false; } -void mitk::ContourModel::InsertVertexAtIndex(Point3D &vertex, int index, bool isControlPoint, TimeStepType timestep) +void mitk::ContourModel::InsertVertexAtIndex(const Point3D &vertex, int index, bool isControlPoint, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (index >= 0 && this->m_ContourSeries[timestep]->GetSize() > index) { this->m_ContourSeries[timestep]->InsertVertexAtIndex(vertex, isControlPoint, index); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } } +void mitk::ContourModel::UpdateContour(const ContourModel* sourceModel, TimeStepType destinationTimeStep, TimeStepType sourceTimeStep) +{ + if (nullptr == sourceModel) + { + mitkThrow() << "Cannot update contour. Passed source model is invalid."; + } + + if (!sourceModel->GetTimeGeometry()->IsValidTimeStep(sourceTimeStep)) + { + mitkThrow() << "Cannot update contour. Source contour time geometry does not support passed time step. Invalid time step: " << sourceTimeStep; + } + + if (!this->GetTimeGeometry()->IsValidTimeStep(destinationTimeStep)) + { + MITK_WARN << "Cannot update contour. Contour time geometry does not support passed time step. Invalid time step: " << destinationTimeStep; + return; + } + + this->Clear(destinationTimeStep); + + std::for_each(sourceModel->Begin(sourceTimeStep), sourceModel->End(sourceTimeStep), [this, destinationTimeStep](ContourElement::VertexType* vertex) { + this->m_ContourSeries[destinationTimeStep]->AddVertex(vertex->Coordinates, vertex->IsControlPoint); + }); + + this->InvokeEvent(ContourModelSizeChangeEvent()); + this->Modified(); + this->m_UpdateBoundingBox = true; +} + bool mitk::ContourModel::IsEmpty(TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsEmpty(); } return true; } bool mitk::ContourModel::IsEmpty() const { return this->IsEmpty(0); } int mitk::ContourModel::GetNumberOfVertices(TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetSize(); } return -1; } const mitk::ContourModel::VertexType *mitk::ContourModel::GetVertexAt(int index, TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetVertexAt(index); } return nullptr; } int mitk::ContourModel::GetIndex(const VertexType *vertex, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetIndex(vertex); } return -1; } void mitk::ContourModel::Close(TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->Close(); this->InvokeEvent(ContourModelClosedEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::Open(TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->Open(); this->InvokeEvent(ContourModelClosedEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::SetClosed(bool isClosed, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->SetClosed(isClosed); this->InvokeEvent(ContourModelClosedEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } bool mitk::ContourModel::IsEmptyTimeStep(unsigned int t) const { return (this->m_ContourSeries.size() <= t); } bool mitk::ContourModel::IsNearContour(Point3D &point, float eps, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsNearContour(point, eps); } return false; } void mitk::ContourModel::Concatenate(ContourModel *other, TimeStepType timestep, bool check) { if (!this->IsEmptyTimeStep(timestep)) { if (!this->m_ContourSeries[timestep]->IsClosed()) { this->m_ContourSeries[timestep]->Concatenate(other->m_ContourSeries[timestep], check); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } } mitk::ContourModel::VertexIterator mitk::ContourModel::Begin(TimeStepType timestep) const { return this->IteratorBegin(timestep); } mitk::ContourModel::VertexIterator mitk::ContourModel::IteratorBegin(TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IteratorBegin(); } else { mitkThrow() << "No iterator at invalid timestep " << timestep << ". There are only " << this->GetTimeSteps() << " timesteps available."; } } mitk::ContourModel::VertexIterator mitk::ContourModel::End(TimeStepType timestep) const { return this->IteratorEnd(timestep); } mitk::ContourModel::VertexIterator mitk::ContourModel::IteratorEnd(TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IteratorEnd(); } else { mitkThrow() << "No iterator at invalid timestep " << timestep << ". There are only " << this->GetTimeSteps() << " timesteps available."; } } bool mitk::ContourModel::IsClosed(int timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsClosed(); } return false; } bool mitk::ContourModel::SelectVertexAt(Point3D &point, float eps, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetVertexAt(point, eps); } return this->m_SelectedVertex != nullptr; } bool mitk::ContourModel::SelectVertexAt(int index, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep) && index >= 0) { return (this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetVertexAt(index)); } return false; } bool mitk::ContourModel::SetControlVertexAt(Point3D &point, float eps, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { VertexType *vertex = this->m_ContourSeries[timestep]->GetVertexAt(point, eps); if (vertex != nullptr) { vertex->IsControlPoint = true; return true; } } return false; } bool mitk::ContourModel::SetControlVertexAt(int index, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep) && index >= 0) { VertexType *vertex = this->m_ContourSeries[timestep]->GetVertexAt(index); if (vertex != nullptr) { vertex->IsControlPoint = true; return true; } } return false; } bool mitk::ContourModel::RemoveVertex(const VertexType *vertex, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (this->m_ContourSeries[timestep]->RemoveVertex(vertex)) { this->Modified(); this->m_UpdateBoundingBox = true; this->InvokeEvent(ContourModelSizeChangeEvent()); return true; } } return false; } bool mitk::ContourModel::RemoveVertexAt(int index, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (this->m_ContourSeries[timestep]->RemoveVertexAt(index)) { this->Modified(); this->m_UpdateBoundingBox = true; this->InvokeEvent(ContourModelSizeChangeEvent()); return true; } } return false; } bool mitk::ContourModel::RemoveVertexAt(Point3D &point, float eps, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (this->m_ContourSeries[timestep]->RemoveVertexAt(point, eps)) { this->Modified(); this->m_UpdateBoundingBox = true; this->InvokeEvent(ContourModelSizeChangeEvent()); return true; } } return false; } void mitk::ContourModel::ShiftSelectedVertex(Vector3D &translate) { if (this->m_SelectedVertex) { this->ShiftVertex(this->m_SelectedVertex, translate); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::ShiftContour(Vector3D &translate, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { - VertexListType *vList = this->m_ContourSeries[timestep]->GetVertexList(); - auto it = vList->begin(); - auto end = vList->end(); - // shift all vertices - while (it != end) + for (auto vertex : *(this->m_ContourSeries[timestep])) { - this->ShiftVertex((*it), translate); - it++; + this->ShiftVertex(vertex, translate); } this->Modified(); this->m_UpdateBoundingBox = true; this->InvokeEvent(ContourModelShiftEvent()); } } void mitk::ContourModel::ShiftVertex(VertexType *vertex, Vector3D &vector) { vertex->Coordinates[0] += vector[0]; vertex->Coordinates[1] += vector[1]; vertex->Coordinates[2] += vector[2]; } void mitk::ContourModel::Clear(TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { // clear data at timestep this->m_ContourSeries[timestep]->Clear(); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::Expand(unsigned int timeSteps) { std::size_t oldSize = this->m_ContourSeries.size(); if (static_cast(timeSteps) > oldSize) { Superclass::Expand(timeSteps); // insert contours for each new timestep for (std::size_t i = oldSize; i < static_cast(timeSteps); i++) { m_ContourSeries.push_back(ContourElement::New()); } this->InvokeEvent(ContourModelExpandTimeBoundsEvent()); } } void mitk::ContourModel::SetRequestedRegionToLargestPossibleRegion() { // no support for regions } bool mitk::ContourModel::RequestedRegionIsOutsideOfTheBufferedRegion() { // no support for regions return false; } bool mitk::ContourModel::VerifyRequestedRegion() { // no support for regions return true; } void mitk::ContourModel::SetRequestedRegion(const itk::DataObject * /*data*/) { // no support for regions } void mitk::ContourModel::Clear() { // clear data and set to initial state again this->ClearData(); this->InitializeEmpty(); this->Modified(); this->m_UpdateBoundingBox = true; } void mitk::ContourModel::RedistributeControlVertices(int period, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->RedistributeControlVertices(this->GetSelectedVertex(), period); this->InvokeEvent(ContourModelClosedEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::ClearData() { // call the superclass, this releases the data of BaseData Superclass::ClearData(); // clear out the time resolved contours this->m_ContourSeries.clear(); } void mitk::ContourModel::Initialize() { this->InitializeEmpty(); this->Modified(); this->m_UpdateBoundingBox = true; } void mitk::ContourModel::Initialize(const ContourModel &other) { TimeStepType numberOfTimesteps = other.GetTimeGeometry()->CountTimeSteps(); this->InitializeTimeGeometry(numberOfTimesteps); for (TimeStepType currentTimestep = 0; currentTimestep < numberOfTimesteps; currentTimestep++) { this->m_ContourSeries.push_back(ContourElement::New()); this->SetClosed(other.IsClosed(currentTimestep), currentTimestep); } m_SelectedVertex = nullptr; this->m_lineInterpolation = other.m_lineInterpolation; this->Modified(); this->m_UpdateBoundingBox = true; } void mitk::ContourModel::InitializeEmpty() { // clear data at timesteps this->m_ContourSeries.resize(0); this->m_ContourSeries.push_back(ContourElement::New()); // set number of timesteps to one this->InitializeTimeGeometry(1); m_SelectedVertex = nullptr; this->m_lineInterpolation = ContourModel::LINEAR; } void mitk::ContourModel::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } if (this->m_UpdateBoundingBox) { // update the bounds of the geometry according to the stored vertices ScalarType mitkBounds[6]; // calculate the boundingbox at each timestep typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::PointsContainer PointsContainer; int timesteps = this->GetTimeSteps(); // iterate over the timesteps for (int currenTimeStep = 0; currenTimeStep < timesteps; currenTimeStep++) { if (dynamic_cast(this->GetGeometry(currenTimeStep))) { // do not update bounds for 2D geometries, as they are unfortunately defined with min bounds 0! return; } else { // we have a 3D geometry -> let's update bounds // only update bounds if the contour was modified if (this->GetMTime() > this->GetGeometry(currenTimeStep)->GetBoundingBox()->GetMTime()) { mitkBounds[0] = 0.0; mitkBounds[1] = 0.0; mitkBounds[2] = 0.0; mitkBounds[3] = 0.0; mitkBounds[4] = 0.0; mitkBounds[5] = 0.0; BoundingBoxType::Pointer boundingBox = BoundingBoxType::New(); PointsContainer::Pointer points = PointsContainer::New(); auto it = this->IteratorBegin(currenTimeStep); auto end = this->IteratorEnd(currenTimeStep); // fill the boundingbox with the points while (it != end) { Point3D currentP = (*it)->Coordinates; BoundingBoxType::PointType p; p.CastFrom(currentP); points->InsertElement(points->Size(), p); it++; } // construct the new boundingBox boundingBox->SetPoints(points); boundingBox->ComputeBoundingBox(); BoundingBoxType::BoundsArrayType tmp = boundingBox->GetBounds(); mitkBounds[0] = tmp[0]; mitkBounds[1] = tmp[1]; mitkBounds[2] = tmp[2]; mitkBounds[3] = tmp[3]; mitkBounds[4] = tmp[4]; mitkBounds[5] = tmp[5]; // set boundingBox at current timestep BaseGeometry *geometry3d = this->GetGeometry(currenTimeStep); geometry3d->SetBounds(mitkBounds); } } } this->m_UpdateBoundingBox = false; } GetTimeGeometry()->Update(); } void mitk::ContourModel::ExecuteOperation(Operation * /*operation*/) { // not supported yet } diff --git a/Modules/ContourModel/DataManagement/mitkContourModel.h b/Modules/ContourModel/DataManagement/mitkContourModel.h index 0811d21613..854eefbb97 100644 --- a/Modules/ContourModel/DataManagement/mitkContourModel.h +++ b/Modules/ContourModel/DataManagement/mitkContourModel.h @@ -1,443 +1,436 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _MITK_CONTOURMODEL_H_ #define _MITK_CONTOURMODEL_H_ #include "mitkBaseData.h" #include "mitkCommon.h" #include #include namespace mitk { /** \brief ContourModel is a structure of linked vertices defining a contour in 3D space. The vertices are stored in a mitk::ContourElement is stored for each timestep. The contour line segments are implicitly defined by the given linked vertices. By default two control points are are linked by a straight line.It is possible to add vertices at front and end of the contour and to iterate in both directions. Points are specified containing coordinates and additional (data) information, see mitk::ContourElement. For accessing a specific vertex either an index or a position in 3D Space can be used. The vertices are best accessed by using a VertexIterator. Interaction with the contour is thus available without any mitk interactor class using the api of ContourModel. It is possible to shift single vertices also as shifting the whole contour. A contour can be either open like a single curved line segment or closed. A closed contour can for example represent a jordan curve. \section mitkContourModelDisplayOptions Display Options The default mappers for this data structure are mitk::ContourModelGLMapper2D and mitk::ContourModelMapper3D. See these classes for display options which can can be set via properties. */ class MITKCONTOURMODEL_EXPORT ContourModel : public BaseData { public: mitkClassMacro(ContourModel, BaseData); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /*+++++++++++++++ typedefs +++++++++++++++++++++++++++++++*/ typedef ContourElement::VertexType VertexType; typedef ContourElement::VertexListType VertexListType; typedef ContourElement::VertexIterator VertexIterator; typedef ContourElement::ConstVertexIterator ConstVertexIterator; typedef std::vector ContourModelSeries; /*+++++++++++++++ END typedefs ++++++++++++++++++++++++++++*/ /** \brief Possible interpolation of the line segments between control points */ enum LineSegmentInterpolation { LINEAR, B_SPLINE }; /*++++++++++++++++ inline methods +++++++++++++++++++++++*/ /** \brief Get the current selected vertex. */ VertexType *GetSelectedVertex() { return this->m_SelectedVertex; } /** \brief Deselect vertex. */ void Deselect() { this->m_SelectedVertex = nullptr; } /** \brief Set selected vertex as control point */ void SetSelectedVertexAsControlPoint(bool isControlPoint = true) { if (this->m_SelectedVertex) { m_SelectedVertex->IsControlPoint = isControlPoint; this->Modified(); } } /** \brief Set the interpolation of the line segments between control points. */ void SetLineSegmentInterpolation(LineSegmentInterpolation interpolation) { this->m_lineInterpolation = interpolation; this->Modified(); } /** \brief Get the interpolation of the line segments between control points. */ LineSegmentInterpolation GetLineSegmentInterpolation() { return this->m_lineInterpolation; } /*++++++++++++++++ END inline methods +++++++++++++++++++++++*/ /** \brief Add a vertex to the contour at given timestep. The vertex is added at the end of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertex(const Point3D &vertex, TimeStepType timestep = 0); /** \brief Add a vertex to the contour at given timestep. - The vertex is added at the end of contour. - + A copy of the passed vertex is added at the end of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) - @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ - void AddVertex(VertexType &vertex, TimeStepType timestep = 0); - - /** \brief Add a vertex to the contour at given timestep. - The vertex is added at the end of contour. - - \param vertex - coordinate representation of a control point - \param timestep - the timestep at which the vertex will be add ( default 0) - - @note Adding a vertex to a timestep which exceeds the timebounds of the contour - will not be added, the TimeSlicedGeometry will not be expanded. - */ - void AddVertex(const VertexType *vertex, TimeStepType timestep = 0); + void AddVertex(const VertexType &vertex, TimeStepType timestep = 0); /** \brief Add a vertex to the contour. - \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) \param isControlPoint - specifies the vertex to be handled in a special way (e.g. control points will be rendered). - - @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ - void AddVertex(const Point3D &vertex, bool isControlPoint, TimeStepType timestep = 0); + void AddVertex(const Point3D& vertex, bool isControlPoint, TimeStepType timestep = 0); + + /** Clears the contour of destinationTimeStep and copies + the contour of the passed source model at the sourceTimeStep. + @pre soureModel must point to a valid instance + @pre sourceTimePoint must be valid + @note Updateing a vertex to a timestep which exceeds the timebounds of the contour + will not be added, the TimeGeometry will not be expanded. + */ + void UpdateContour(const ContourModel* sourceModel, TimeStepType destinationTimeStep, TimeStepType sourceTimeStep); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. The vertex is added at the FRONT of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ - void AddVertexAtFront(Point3D &vertex, TimeStepType timestep = 0); + void AddVertexAtFront(const Point3D &vertex, TimeStepType timestep = 0); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. The vertex is added at the FRONT of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ - void AddVertexAtFront(VertexType &vertex, TimeStepType timestep = 0); + void AddVertexAtFront(const VertexType &vertex, TimeStepType timestep = 0); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) \param isControlPoint - specifies the vertex to be handled in a special way (e.g. control points will be rendered). @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ - void AddVertexAtFront(Point3D &vertex, bool isControlPoint, TimeStepType timestep = 0); + void AddVertexAtFront(const Point3D &vertex, bool isControlPoint, TimeStepType timestep = 0); /** \brief Insert a vertex at given index. */ - void InsertVertexAtIndex(Point3D &vertex, int index, bool isControlPoint = false, TimeStepType timestep = 0); + void InsertVertexAtIndex(const Point3D &vertex, int index, bool isControlPoint = false, TimeStepType timestep = 0); /** \brief Set a coordinates for point at given index. */ bool SetVertexAt(int pointId, const Point3D &point, TimeStepType timestep = 0); - /** \brief Set a coordinates for point at given index. + /** \brief Set a coordinates and control state for point at given index. */ bool SetVertexAt(int pointId, const VertexType *vertex, TimeStepType timestep = 0); /** \brief Return if the contour is closed or not. */ bool IsClosed(int timestep = 0) const; /** \brief Concatenate two contours. The starting control point of the other will be added at the end of the contour. \param other \param timestep - the timestep at which the vertex will be add ( default 0) \param check - check for intersections ( default false) */ void Concatenate(ContourModel *other, TimeStepType timestep = 0, bool check = false); /** \brief Returns a const VertexIterator at the start element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator Begin(TimeStepType timestep = 0) const; /** \brief Returns a const VertexIterator at the start element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator IteratorBegin(TimeStepType timestep = 0) const; /** \brief Returns a const VertexIterator at the end element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator End(TimeStepType timestep = 0) const; /** \brief Returns a const VertexIterator at the end element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator IteratorEnd(TimeStepType timestep = 0) const; /** \brief Close the contour. The last control point will be linked with the first point. */ virtual void Close(TimeStepType timestep = 0); /** \brief Set isClosed to false contour. The link between the last control point the first point will be removed. */ virtual void Open(TimeStepType timestep = 0); /** \brief Set closed property to given boolean. false - The link between the last control point the first point will be removed. true - The last control point will be linked with the first point. */ virtual void SetClosed(bool isClosed, TimeStepType timestep = 0); /** \brief Returns the number of vertices at a given timestep. \param timestep - default = 0 */ int GetNumberOfVertices(TimeStepType timestep = 0) const; /** \brief Returns whether the contour model is empty at a given timestep. \param timestep - default = 0 */ virtual bool IsEmpty(TimeStepType timestep) const; /** \brief Returns whether the contour model is empty. */ bool IsEmpty() const override; /** \brief Returns the vertex at the index position within the container. */ virtual const VertexType *GetVertexAt(int index, TimeStepType timestep = 0) const; /** \brief Remove a vertex at given timestep within the container. \return index of vertex. -1 if not found. */ int GetIndex(const VertexType *vertex, TimeStepType timestep = 0); /** \brief Check if there isn't something at this timestep. */ bool IsEmptyTimeStep(unsigned int t) const override; /** \brief Check if mouse cursor is near the contour. */ virtual bool IsNearContour(Point3D &point, float eps, TimeStepType timestep); /** \brief Mark a vertex at an index in the container as selected. */ bool SelectVertexAt(int index, TimeStepType timestep = 0); /** \brief Mark a vertex at an index in the container as control point. */ bool SetControlVertexAt(int index, TimeStepType timestep = 0); /** \brief Mark a vertex at a given position in 3D space. \param point - query point in 3D space \param eps - radius for nearest neighbour search (error bound). \param timestep - search at this timestep @return true = vertex found; false = no vertex found */ bool SelectVertexAt(Point3D &point, float eps, TimeStepType timestep = 0); /* \pararm point - query point in 3D space \pararm eps - radius for nearest neighbour search (error bound). \pararm timestep - search at this timestep @return true = vertex found; false = no vertex found */ bool SetControlVertexAt(Point3D &point, float eps, TimeStepType timestep = 0); /** \brief Remove a vertex at given index within the container. @return true = the vertex was successfuly removed; false = wrong index. */ bool RemoveVertexAt(int index, TimeStepType timestep = 0); /** \brief Remove a vertex at given timestep within the container. @return true = the vertex was successfuly removed. */ bool RemoveVertex(const VertexType *vertex, TimeStepType timestep = 0); /** \brief Remove a vertex at a query position in 3D space. The vertex to be removed will be search by nearest neighbour search. Note that possibly no vertex at this position and eps is stored inside the contour. @return true = the vertex was successfuly removed; false = no vertex found. */ bool RemoveVertexAt(Point3D &point, float eps, TimeStepType timestep = 0); /** \brief Shift the currently selected vertex by a translation vector. \param translate - the translation vector. */ void ShiftSelectedVertex(Vector3D &translate); /** \brief Shift the whole contour by a translation vector at given timestep. \param translate - the translation vector. \param timestep - at this timestep the contour will be shifted. */ void ShiftContour(Vector3D &translate, TimeStepType timestep = 0); /** \brief Clear the storage container at given timestep. All control points are removed at timestep. */ virtual void Clear(TimeStepType timestep); /** \brief Initialize all data objects */ void Initialize() override; /** \brief Initialize object with specs of other contour. Note: No data will be copied. */ void Initialize(const ContourModel &other); /*++++++++++++++++++ method inherit from base data +++++++++++++++++++++++++++*/ /** \brief Inherit from base data - no region support available for contourModel objects. */ void SetRequestedRegionToLargestPossibleRegion() override; /** \brief Inherit from base data - no region support available for contourModel objects. */ bool RequestedRegionIsOutsideOfTheBufferedRegion() override; /** \brief Inherit from base data - no region support available for contourModel objects. */ bool VerifyRequestedRegion() override; /** \brief Inherit from base data - no region support available for contourModel objects. */ void SetRequestedRegion(const itk::DataObject *data) override; /** - \brief Expand the timebounds of the TimeGeometry to given number of timesteps. + \brief Expand the contour model and its TimeGeometry to given number of timesteps. */ void Expand(unsigned int timeSteps) override; /** \brief Update the OutputInformation of a ContourModel object The BoundingBox of the contour will be updated, if necessary. */ void UpdateOutputInformation() override; /** \brief Clear the storage container. The object is set to initial state. All control points are removed and the number of timesteps are set to 1. */ void Clear() override; /** \brief overwrite if the Data can be called by an Interactor (StateMachine). */ void ExecuteOperation(Operation *operation) override; /** \brief Redistributes ontrol vertices with a given period (as number of vertices) \param period - the number of vertices between control points. \param timestep - at this timestep all lines will be rebuilt. */ virtual void RedistributeControlVertices(int period, TimeStepType timestep); protected: mitkCloneMacro(Self); ContourModel(); ContourModel(const ContourModel &other); ~ContourModel() override; // inherit from BaseData. called by Clear() void ClearData() override; // inherit from BaseData. Initial state of a contour with no vertices and a single timestep. void InitializeEmpty() override; // Shift a vertex - void ShiftVertex(VertexType *vertex, Vector3D &vector); + static void ShiftVertex(VertexType *vertex, Vector3D &vector); // Storage with time resolved support. ContourModelSeries m_ContourSeries; // The currently selected vertex. VertexType *m_SelectedVertex; // The interpolation of the line segment between control points. LineSegmentInterpolation m_lineInterpolation; // only update the bounding geometry if necessary bool m_UpdateBoundingBox; }; itkEventMacro(ContourModelEvent, itk::AnyEvent); itkEventMacro(ContourModelShiftEvent, ContourModelEvent); itkEventMacro(ContourModelSizeChangeEvent, ContourModelEvent); itkEventMacro(ContourModelAddEvent, ContourModelSizeChangeEvent); itkEventMacro(ContourModelRemoveEvent, ContourModelSizeChangeEvent); itkEventMacro(ContourModelExpandTimeBoundsEvent, ContourModelEvent); itkEventMacro(ContourModelClosedEvent, ContourModelEvent); } #endif diff --git a/Modules/ContourModel/Testing/CMakeLists.txt b/Modules/ContourModel/Testing/CMakeLists.txt index 263e573340..153cd81e2e 100644 --- a/Modules/ContourModel/Testing/CMakeLists.txt +++ b/Modules/ContourModel/Testing/CMakeLists.txt @@ -1,2 +1 @@ MITK_CREATE_MODULE_TESTS() -#mitkAddCustomModuleTest(mitkSegmentationInterpolationTest mitkSegmentationInterpolationTest ${MITK_DATA_DIR}/interpolation_test_manual.nrrd ${MITK_DATA_DIR}/interpolation_test_result.nrrd) diff --git a/Modules/ContourModel/Testing/files.cmake b/Modules/ContourModel/Testing/files.cmake index 8d8021a143..804bb86385 100644 --- a/Modules/ContourModel/Testing/files.cmake +++ b/Modules/ContourModel/Testing/files.cmake @@ -1,13 +1,14 @@ set(MODULE_TESTS + mitkContourElementTest.cpp mitkContourModelTest.cpp mitkContourModelIOTest.cpp mitkContourModelSetTest.cpp ) set(MODULE_IMAGE_TESTS ) set(MODULE_CUSTOM_TESTS ) set(MODULE_TESTIMAGE ) diff --git a/Modules/ContourModel/Testing/mitkContourElementTest.cpp b/Modules/ContourModel/Testing/mitkContourElementTest.cpp new file mode 100644 index 0000000000..9408ad5988 --- /dev/null +++ b/Modules/ContourModel/Testing/mitkContourElementTest.cpp @@ -0,0 +1,344 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkContourElement.h" + +#include "mitkTestFixture.h" +#include "mitkTestingMacros.h" +#include + +class mitkContourElementTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkContourElementTestSuite); + // Test the append method + MITK_TEST(Iterator); + MITK_TEST(AddVertex); + MITK_TEST(AddVertexAtFront); + MITK_TEST(InsertVertexAtIndex); + MITK_TEST(GetSetVertexAt); + MITK_TEST(OpenAndClose); + MITK_TEST(OtherGetters); + MITK_TEST(Concatenate); + MITK_TEST(RemoveVertex); + MITK_TEST(Clear); + MITK_TEST(GetControlVertices); + MITK_TEST(RedistributeControlVertices); + MITK_TEST(Others); + + CPPUNIT_TEST_SUITE_END(); + +private: + mitk::ContourElement::Pointer m_Contour1to4; + mitk::ContourElement::Pointer m_Contour5to6; + mitk::ContourElement::Pointer m_Contour_empty; + mitk::Point3D m_p1; + mitk::Point3D m_p2; + mitk::Point3D m_p3; + mitk::Point3D m_p4; + mitk::Point3D m_p5; + mitk::Point3D m_p6; + mitk::Point3D m_p7; + +public: + static mitk::Point3D GeneratePoint(double val) + { + return mitk::Point3D(val); + } + + void setUp() override + { + m_p1 = GeneratePoint(1); + m_p2 = GeneratePoint(2); + m_p3 = GeneratePoint(3); + m_p4 = GeneratePoint(4); + m_p5 = GeneratePoint(5); + m_p6 = GeneratePoint(6); + m_p7 = GeneratePoint(7); + + m_Contour1to4 = mitk::ContourElement::New(); + m_Contour5to6 = mitk::ContourElement::New(); + m_Contour_empty = mitk::ContourElement::New(); + + m_Contour1to4->AddVertex(m_p1, true); + m_Contour1to4->AddVertex(m_p2, false); + m_Contour1to4->AddVertex(m_p3, true); + m_Contour1to4->AddVertex(m_p4, false); + + m_Contour5to6->AddVertex(m_p5, false); + m_Contour5to6->AddVertex(m_p6, true); + + } + + void tearDown() override {} + + void Iterator() + { + mitk::ContourElement::ConstPointer constcontour = m_Contour5to6; + CPPUNIT_ASSERT_MESSAGE("Begin does not point to correct element", m_Contour1to4->IteratorBegin().operator*()->Coordinates == m_p1); + CPPUNIT_ASSERT_MESSAGE("Begin does not point to correct element", m_Contour1to4->ConstIteratorBegin().operator*()->Coordinates == m_p1); + CPPUNIT_ASSERT_MESSAGE("Begin does not point to correct element", m_Contour5to6->begin().operator*()->Coordinates == m_p5); + CPPUNIT_ASSERT_MESSAGE("Begin does not point to correct element", constcontour->begin().operator*()->Coordinates == m_p5); + + CPPUNIT_ASSERT_MESSAGE("End does not point to correct element", m_Contour_empty->ConstIteratorBegin() == m_Contour_empty->ConstIteratorEnd()); + CPPUNIT_ASSERT_MESSAGE("End does not point to correct element", m_Contour_empty->IteratorBegin() == m_Contour_empty->IteratorEnd()); + CPPUNIT_ASSERT_MESSAGE("End does not point to correct element", m_Contour_empty->begin() == m_Contour_empty->end()); + } + + void AddVertex() + { + m_Contour_empty->AddVertex(m_p1, false); + CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->Coordinates == m_p1); + CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour_empty->GetSize() == 1); + + m_Contour_empty->AddVertex(m_p7, true); + CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->Coordinates == m_p1); + CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(1)->Coordinates == m_p7); + CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(1)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour_empty->GetSize() == 2); + } + + void AddVertexAtFront() + { + m_Contour_empty->AddVertexAtFront(m_p1, false); + CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->Coordinates == m_p1); + CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour_empty->GetSize() == 1); + + m_Contour_empty->AddVertexAtFront(m_p7, true); + CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(1)->Coordinates == m_p1); + CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(1)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->Coordinates == m_p7); + CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour_empty->GetSize() == 2); + } + + void InsertVertexAtIndex() + { + mitk::Point3D outOfBountPoint; + m_Contour1to4->InsertVertexAtIndex(m_p5, false, 0); + m_Contour1to4->InsertVertexAtIndex(m_p7, true, 2); + m_Contour1to4->InsertVertexAtIndex(outOfBountPoint, true, 6); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->Coordinates == m_p5); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1)->Coordinates == m_p1); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(2)->Coordinates == m_p7); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(2)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(3)->Coordinates == m_p2); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(3)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 6); + } + + void GetSetVertexAt() + { + mitk::ContourElement::ConstPointer constcontour = m_Contour1to4; + + auto v0 = m_Contour1to4->GetVertexAt(0); + auto v1 = m_Contour1to4->GetVertexAt(1); + + CPPUNIT_ASSERT(v0->Coordinates == m_p1); + CPPUNIT_ASSERT(v0->IsControlPoint == true); + CPPUNIT_ASSERT(v1->Coordinates == m_p2); + CPPUNIT_ASSERT(v1->IsControlPoint == false); + CPPUNIT_ASSERT(constcontour->GetVertexAt(0)->Coordinates == m_p1); + CPPUNIT_ASSERT(constcontour->GetVertexAt(0)->IsControlPoint == true); + CPPUNIT_ASSERT(constcontour->GetVertexAt(1)->Coordinates == m_p2); + CPPUNIT_ASSERT(constcontour->GetVertexAt(1)->IsControlPoint == false); + + m_Contour1to4->SetVertexAt(0, m_p7); + CPPUNIT_ASSERT(v0->Coordinates == m_p7); + CPPUNIT_ASSERT(v0->IsControlPoint == true); + + m_Contour1to4->SetVertexAt(1, m_Contour5to6->GetVertexAt(1)); + CPPUNIT_ASSERT(v1->Coordinates == m_Contour5to6->GetVertexAt(1)->Coordinates); + CPPUNIT_ASSERT(v1->IsControlPoint == m_Contour5to6->GetVertexAt(1)->IsControlPoint); + CPPUNIT_ASSERT(v1 != m_Contour5to6->GetVertexAt(1)); + + mitk::Point3D search = GeneratePoint(6.05); + + auto finding = m_Contour1to4->GetVertexAt(search, 0.); + CPPUNIT_ASSERT(nullptr == finding); + + finding = m_Contour1to4->GetVertexAt(search, 0.1); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1) == finding); + } + + void OpenAndClose() + { + CPPUNIT_ASSERT(!m_Contour1to4->IsClosed()); + CPPUNIT_ASSERT(!m_Contour_empty->IsClosed()); + m_Contour1to4->Close(); + m_Contour_empty->SetClosed(true); + CPPUNIT_ASSERT(m_Contour1to4->IsClosed()); + CPPUNIT_ASSERT(m_Contour_empty->IsClosed()); + m_Contour1to4->Open(); + m_Contour_empty->SetClosed(false); + CPPUNIT_ASSERT(!m_Contour1to4->IsClosed()); + CPPUNIT_ASSERT(!m_Contour_empty->IsClosed()); + } + + void OtherGetters() + { + CPPUNIT_ASSERT(m_Contour1to4->GetIndex(m_Contour1to4->GetVertexAt(0)) == 0); + CPPUNIT_ASSERT(m_Contour1to4->GetIndex(m_Contour1to4->GetVertexAt(1)) == 1); + CPPUNIT_ASSERT(m_Contour1to4->GetIndex(m_Contour1to4->GetVertexAt(3)) == 3); + CPPUNIT_ASSERT(m_Contour1to4->GetIndex(m_Contour5to6->GetVertexAt(0)) == mitk::ContourElement::NPOS); + } + + void Concatenate() + { + m_Contour5to6->Concatenate(m_Contour1to4, true); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->Coordinates == m_p5); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(1)->Coordinates == m_p6); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(1)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(2)->Coordinates == m_p1); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(2)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(3)->Coordinates == m_p2); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(3)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(4)->Coordinates == m_p3); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(4)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(5)->Coordinates == m_p4); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(5)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour5to6->GetSize() == 6); + + m_Contour_empty->AddVertex(m_p1, false); + m_Contour5to6->Concatenate(m_Contour_empty, true); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->Coordinates == m_p5); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(1)->Coordinates == m_p6); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(1)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(2)->Coordinates == m_p1); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(2)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(3)->Coordinates == m_p2); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(3)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(4)->Coordinates == m_p3); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(4)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(5)->Coordinates == m_p4); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(5)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour5to6->GetSize() == 6); + + m_Contour5to6->Concatenate(m_Contour_empty, false); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->Coordinates == m_p5); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(1)->Coordinates == m_p6); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(1)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(2)->Coordinates == m_p1); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(2)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(3)->Coordinates == m_p2); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(3)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(4)->Coordinates == m_p3); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(4)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(5)->Coordinates == m_p4); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(5)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(6)->Coordinates == m_p1); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(6)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour5to6->GetSize() == 7); + } + + void RemoveVertex() + { + CPPUNIT_ASSERT(m_Contour1to4->RemoveVertex(m_Contour1to4->GetVertexAt(0))); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->Coordinates == m_p2); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 3); + CPPUNIT_ASSERT(!m_Contour1to4->RemoveVertex(m_Contour5to6->GetVertexAt(0))); + CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 3); + + CPPUNIT_ASSERT(m_Contour1to4->RemoveVertexAt(1)); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->Coordinates == m_p2); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1)->Coordinates == m_p4); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 2); + + CPPUNIT_ASSERT(!m_Contour1to4->RemoveVertexAt(2)); + CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 2); + + mitk::Point3D search = GeneratePoint(6.05); + + CPPUNIT_ASSERT(!m_Contour1to4->RemoveVertexAt(search,0.1)); + CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 2); + + CPPUNIT_ASSERT(m_Contour5to6->RemoveVertexAt(search, 0.1)); + CPPUNIT_ASSERT(m_Contour5to6->GetSize() == 1); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->Coordinates == m_p5); + CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->IsControlPoint == false); + } + + void Clear() + { + m_Contour1to4->Clear(); + CPPUNIT_ASSERT(m_Contour1to4->IsEmpty()); + m_Contour_empty->Clear(); + CPPUNIT_ASSERT(m_Contour_empty->IsEmpty()); + } + + void GetControlVertices() + { + auto controlVs = m_Contour1to4->GetControlVertices(); + CPPUNIT_ASSERT(controlVs.size() == 2); + CPPUNIT_ASSERT(controlVs[0] == m_Contour1to4->GetVertexAt(0)); + CPPUNIT_ASSERT(controlVs[1] == m_Contour1to4->GetVertexAt(2)); + + controlVs = m_Contour_empty->GetControlVertices(); + CPPUNIT_ASSERT(controlVs.empty()); + } + + void RedistributeControlVertices() + { + //just adding more nodes to better check the redistribution + m_Contour1to4->Concatenate(m_Contour1to4, false); + m_Contour1to4->Concatenate(m_Contour1to4, false); + + m_Contour1to4->RedistributeControlVertices(nullptr, 3); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(2)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(3)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(4)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(5)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(6)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(7)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(8)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(9)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(10)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(11)->IsControlPoint == false); + + m_Contour1to4->RedistributeControlVertices(m_Contour1to4->GetVertexAt(4), 4); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(2)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(3)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(4)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(5)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(6)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(7)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(8)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(9)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(10)->IsControlPoint == false); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(11)->IsControlPoint == false); + } + + void Others() + { + CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 4); + CPPUNIT_ASSERT(m_Contour_empty->GetSize() == 0); + CPPUNIT_ASSERT(!m_Contour1to4->IsEmpty()); + CPPUNIT_ASSERT(m_Contour_empty->IsEmpty()); + } + +}; + +MITK_TEST_SUITE_REGISTRATION(mitkContourElement) diff --git a/Modules/ContourModel/Testing/mitkContourModelTest.cpp b/Modules/ContourModel/Testing/mitkContourModelTest.cpp index e2e47448f7..a04e7318c3 100644 --- a/Modules/ContourModel/Testing/mitkContourModelTest.cpp +++ b/Modules/ContourModel/Testing/mitkContourModelTest.cpp @@ -1,445 +1,464 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include // Add a vertex to the contour and see if size changed static void TestAddVertex() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); - MITK_TEST_CONDITION(contour->GetNumberOfVertices() > 0, "Add a Vertex, size increased"); + MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 1, "Add a Vertex, size increased"); + MITK_TEST_CONDITION(contour->GetVertexAt(0)->Coordinates == p, "Added vertex has the correct value."); + + mitk::Point3D outOfTimeBoundPoint; + outOfTimeBoundPoint[0] = outOfTimeBoundPoint[1] = outOfTimeBoundPoint[2] = 1; + + contour->AddVertex(outOfTimeBoundPoint, mitk::TimeStepType(1)); + + MITK_TEST_CONDITION(contour->GetTimeSteps() == 1, "Add a vertex to an unsupported time step has not changed geometry."); + MITK_TEST_CONDITION(contour->IsEmptyTimeStep(1), "Add a vertex to an unsupported time step has not added an contour element."); + MITK_TEST_CONDITION(contour->GetNumberOfVertices(1) == -1, "Add a vertex to an unsupported time step has not added an contour element."); } // Select a vertex by index. successful if the selected vertex member of the contour is no longer set to null static void TestSelectVertexAtIndex() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); contour->SelectVertexAt(0); MITK_TEST_CONDITION(contour->GetSelectedVertex() != nullptr, "Vertex was selected at index"); } // Select a vertex by worldposition. successful if the selected vertex member of the contour is no longer set to null static void TestSelectVertexAtWorldposition() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); // same point is used here so the epsilon can be chosen very small contour->SelectVertexAt(p, 0.01); MITK_TEST_CONDITION(contour->GetSelectedVertex() != nullptr, "Vertex was selected at position"); } // Move a vertex by a translation vector static void TestMoveSelectedVertex() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); // Same point is used here so the epsilon can be chosen very small contour->SelectVertexAt(p, 0.01); mitk::Vector3D v; v[0] = 1; v[1] = 3; v[2] = -1; contour->ShiftSelectedVertex(v); const mitk::ContourModel::VertexType *vertex = contour->GetSelectedVertex(); bool correctlyMoved = false; correctlyMoved = (vertex->Coordinates)[0] == (v[0]) && (vertex->Coordinates)[1] == (v[1]) && (vertex->Coordinates)[2] == (v[2]); MITK_TEST_CONDITION(correctlyMoved, "Vertex has been moved"); } // Test to move the whole contour /* static void TestMoveContour() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D p2; p2[0] = p2[1] = p2[2] = 0; contour->AddVertex(p2); mitk::Vector3D v; v[0] = 1; v[1] = 3; v[2] = -1; contour->ShiftContour(v); mitk::ContourModel::VertexIterator it = contour->IteratorBegin(); mitk::ContourModel::VertexIterator end = contour->IteratorEnd(); bool correctlyMoved = false; while(it != end) { correctlyMoved &= (*it)->Coordinates[0] == (v[0]) && (*it)->Coordinates[1] == (v[1]) && (*it)->Coordinates[2] == (v[2]); } MITK_TEST_CONDITION(correctlyMoved, "Contour has been moved"); } */ // Remove a vertex by index static void TestRemoveVertexAtIndex() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); contour->RemoveVertexAt(0); MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 0, "removed vertex"); } // Remove a vertex by position static void TestRemoveVertexAtWorldPosition() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); contour->RemoveVertexAt(p, 0.01); MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 0, "removed vertex"); } // Check closeable contour static void TestIsclosed() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D p2; p2[0] = p2[1] = p2[2] = 1; contour->AddVertex(p2); contour->Close(); MITK_TEST_CONDITION(contour->IsClosed(), "closed contour"); // no vertices should be added to a closed contour int oldNumberOfVertices = contour->GetNumberOfVertices(); mitk::Point3D p3; p3[0] = p3[1] = p3[2] = 4; contour->AddVertex(p3); int newNumberOfVertices = contour->GetNumberOfVertices(); MITK_TEST_CONDITION(oldNumberOfVertices != newNumberOfVertices, "vertices added to closed contour"); } // Test concatenating two contours static void TestConcatenate() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D p2; p2[0] = p2[1] = p2[2] = 1; contour->AddVertex(p2); mitk::ContourModel::Pointer contour2 = mitk::ContourModel::New(); mitk::Point3D p3; p3[0] = -2; p3[1] = 10; p3[2] = 0; contour2->AddVertex(p3); mitk::Point3D p4; p4[0] = -3; p4[1] = 6; p4[2] = -5; contour2->AddVertex(p4); contour->Concatenate(contour2); MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 4, "two contours were concatenated"); } // Try to select a vertex at position (within a epsilon of course) where no vertex is. // So the selected verted member should be null. static void TestSelectVertexAtWrongPosition() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); MITK_TEST_CONDITION_REQUIRED(contour->GetSelectedVertex() == nullptr, "selected vertex is nullptr"); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D p2; p2[0] = p2[1] = p2[2] = 2; contour->SelectVertexAt(p2, 0.1); MITK_TEST_CONDITION(contour->GetSelectedVertex() == nullptr, "Vertex was not selected"); } static void TestInsertVertex() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D p2; p2[0] = p2[1] = p2[2] = 1; contour->AddVertex(p2); mitk::Point3D pointToInsert; pointToInsert[0] = pointToInsert[1] = pointToInsert[2] = 10; contour->InsertVertexAtIndex(pointToInsert, 1); MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 3, "test insert vertex"); MITK_TEST_CONDITION(contour->GetVertexAt(1)->Coordinates == pointToInsert, "compare inserted vertex"); + + mitk::Point3D outOfTimeBoundPoint; + outOfTimeBoundPoint[0] = outOfTimeBoundPoint[1] = outOfTimeBoundPoint[2] = 1; + + contour->InsertVertexAtIndex(outOfTimeBoundPoint, 4, false, mitk::TimeStepType(1)); + + MITK_TEST_CONDITION(contour->GetTimeSteps() == 1, "Insert a vertex to an unsupported time step has not changed geometry."); + MITK_TEST_CONDITION(contour->IsEmptyTimeStep(1), "Insert a vertex to an unsupported time step has not added an contour element."); + MITK_TEST_CONDITION(contour->GetNumberOfVertices(1) == -1, "Insert a vertex to an unsupported time step has not added an contour element."); } // try to access an invalid timestep static void TestInvalidTimeStep() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D p2; p2[0] = p2[1] = p2[2] = 1; contour->AddVertex(p2); mitk::TimeStepType invalidTimeStep = 42; MITK_TEST_CONDITION_REQUIRED(contour->IsEmptyTimeStep(invalidTimeStep), "invalid timestep required"); MITK_TEST_FOR_EXCEPTION(std::exception, contour->IteratorBegin(-1)); contour->Close(invalidTimeStep); MITK_TEST_CONDITION(contour->IsClosed() == false, "test close for timestep 0"); MITK_TEST_CONDITION(contour->IsClosed(invalidTimeStep) == false, "test close at invalid timestep"); contour->SetClosed(true, invalidTimeStep); MITK_TEST_CONDITION(contour->GetNumberOfVertices(invalidTimeStep) == -1, "test number of vertices at invalid timestep"); contour->AddVertex(p2, invalidTimeStep); MITK_TEST_CONDITION(contour->GetNumberOfVertices(invalidTimeStep) == -1, "test add vertex at invalid timestep"); contour->InsertVertexAtIndex(p2, 0, false, invalidTimeStep); MITK_TEST_CONDITION(contour->GetNumberOfVertices(invalidTimeStep) == -1, "test insert vertex at invalid timestep"); MITK_TEST_CONDITION(contour->SelectVertexAt(0, invalidTimeStep) == false, "test select vertex at invalid timestep"); MITK_TEST_CONDITION(contour->RemoveVertexAt(0, invalidTimeStep) == false, "test remove vertex at invalid timestep"); } static void TestEmptyContour() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); MITK_TEST_CONDITION(contour->IteratorBegin() == contour->IteratorEnd(), "test iterator of emtpy contour"); MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 0, "test numberof vertices of empty contour"); } static void TestSetVertices() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D newCoordinates; newCoordinates[0] = newCoordinates[1] = newCoordinates[2] = 1; contour->SetVertexAt(0, newCoordinates); MITK_TEST_CONDITION(mitk::Equal(contour->GetVertexAt(0)->Coordinates, newCoordinates), "set coordinates"); mitk::ContourModel::Pointer contour2 = mitk::ContourModel::New(); mitk::Point3D p3; p3[0] = -2; p3[1] = 10; p3[2] = 0; contour2->AddVertex(p3); mitk::Point3D p4; p4[0] = -3; p4[1] = 6; p4[2] = -5; contour2->AddVertex(p4); contour->AddVertex(p); - - contour->SetVertexAt(1, contour2->GetVertexAt(1)); + contour->SetVertexAt(1, contour2->GetVertexAt(1), 0); MITK_TEST_CONDITION( mitk::Equal(contour->GetVertexAt(1)->Coordinates, contour2->GetVertexAt(1)->Coordinates), "Use setter and getter combination"); } static void TestContourModelAPI() { mitk::ContourModel::Pointer contour1 = mitk::ContourModel::New(); mitk::Point3D p1; p1[0] = -2; p1[1] = 10; p1[2] = 0; contour1->AddVertex(p1); // adding vertices should always copy the content and not store pointers or references. MITK_TEST_CONDITION(&p1 != &(contour1->GetVertexAt(0)->Coordinates), "copied point"); mitk::Point3D p2; p2[0] = -3; p2[1] = 6; p2[2] = -5; contour1->AddVertex(p2); // test use of setter and getter with const and non-const pointers const mitk::ContourModel::VertexType *vertex = contour1->GetVertexAt(1); MITK_TEST_CONDITION(contour1->GetIndex(vertex) == 1, "Get index"); auto *nonConstVertex = const_cast(vertex); MITK_TEST_CONDITION(contour1->GetIndex(nonConstVertex) == 1, "Get index non-const"); mitk::ContourModel::Pointer contour2 = mitk::ContourModel::New(); - contour2->AddVertex(contour1->GetVertexAt(0)); + contour2->AddVertex(*(contour1->GetVertexAt(0))); MITK_TEST_CONDITION(contour2->GetNumberOfVertices() == 1, "Add call with another contour"); } static void TestClear() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); + contour->Expand(3); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); p[0] = p[1] = p[2] = 1; contour->AddVertex(p, mitk::TimeStepType(1)); p[0] = p[1] = p[2] = 2; contour->AddVertex(p, mitk::TimeStepType(2)); contour->Clear(1); MITK_TEST_CONDITION(contour->GetTimeSteps() == 3, "Check time step count stays 3."); MITK_TEST_CONDITION(!contour->IsEmpty(0), "Check time step 0 is not empty."); MITK_TEST_CONDITION(contour->IsEmpty(1), "Check time step 1 is empty."); MITK_TEST_CONDITION(!contour->IsEmpty(2), "Check time step 2 is not empty."); - MITK_TEST_CONDITION(contour->GetVertexAt(2)->Coordinates == p, "compare if vertex at t == 2 is still the same"); + MITK_TEST_CONDITION(contour->GetVertexAt(0,2)->Coordinates == p, "compare if vertex at t == 2 is still the same"); contour->Clear(); MITK_TEST_CONDITION(contour->GetTimeSteps() == 1, "Check time step count stays 1."); MITK_TEST_CONDITION(contour->IsEmpty(0), "Check time step 0 is empty."); } int mitkContourModelTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("mitkContourModelTest") TestAddVertex(); TestSelectVertexAtIndex(); TestSelectVertexAtWorldposition(); TestMoveSelectedVertex(); TestRemoveVertexAtIndex(); TestRemoveVertexAtWorldPosition(); TestIsclosed(); TestConcatenate(); TestInvalidTimeStep(); TestInsertVertex(); TestEmptyContour(); TestSetVertices(); TestSelectVertexAtWrongPosition(); TestContourModelAPI(); TestClear(); MITK_TEST_END() }