From cac63ce1d617366004ba1b2ff5fe3036a4c291b4 Mon Sep 17 00:00:00 2001 From: Niels Dekker Date: Tue, 29 Oct 2024 18:00:57 +0100 Subject: [PATCH] ENH: Add Clone() and MakeDeepCopy() to PointSetBase `PointSetBase::MakeDeepCopy()` allows making a "deep copy" of a point set (rather than `PointSet::Graft`, which does a "shallow copy", or `Clone()`, which does not copy at all). --- Modules/Core/Common/include/itkPointSet.h | 12 +++++ Modules/Core/Common/include/itkPointSetBase.h | 25 +++++++++ Modules/Core/Common/test/itkPointSetGTest.cxx | 54 +++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/Modules/Core/Common/include/itkPointSet.h b/Modules/Core/Common/include/itkPointSet.h index 51829cbd6c7..c4741e547bf 100644 --- a/Modules/Core/Common/include/itkPointSet.h +++ b/Modules/Core/Common/include/itkPointSet.h @@ -162,6 +162,18 @@ class ITK_TEMPLATE_EXPORT PointSet : public PointSetBaseCastToSTLContainer() = m_PointDataContainer->CastToSTLConstContainer(); + m_PointDataContainer = pointData; + } + } }; // End Class: PointSet } // end namespace itk diff --git a/Modules/Core/Common/include/itkPointSetBase.h b/Modules/Core/Common/include/itkPointSetBase.h index d8c86ee9900..ca2aa1378aa 100644 --- a/Modules/Core/Common/include/itkPointSetBase.h +++ b/Modules/Core/Common/include/itkPointSetBase.h @@ -67,6 +67,8 @@ class ITK_TEMPLATE_EXPORT PointSetBase : public DataObject /** \see LightObject::GetNameOfClass() */ itkOverrideGetNameOfClassMacro(PointSetBase); + itkCloneMacro(Self); + /** Convenient type alias obtained from TPointsContainer template parameter. */ using PointType = typename TPointsContainer::Element; using CoordRepType = typename PointType::CoordRepType; @@ -190,6 +192,25 @@ class ITK_TEMPLATE_EXPORT PointSetBase : public DataObject itkGetConstMacro(BufferedRegion, RegionType); + + /** Returns a "deep copy" of this point set, having copied its points and point data. */ + Pointer + MakeDeepCopy() const + { + const auto clone = this->Clone(); + clone->Graft(this); + + if (m_PointsContainer) + { + // Make a new copy of the points, detached from the original one. + const auto points = TPointsContainer::New(); + points->CastToSTLContainer() = m_PointsContainer->CastToSTLConstContainer(); + clone->m_PointsContainer = points; + } + clone->DetachPointData(); + return clone; + } + protected: /** Default-constructor, to be used by derived classes. */ PointSetBase() = default; @@ -215,6 +236,10 @@ class ITK_TEMPLATE_EXPORT PointSetBase : public DataObject RegionType m_RequestedNumberOfRegions{}; RegionType m_BufferedRegion{ -1 }; RegionType m_RequestedRegion{ -1 }; + +private: + virtual void + DetachPointData() = 0; }; } // end namespace itk diff --git a/Modules/Core/Common/test/itkPointSetGTest.cxx b/Modules/Core/Common/test/itkPointSetGTest.cxx index 1a5d305aa65..e5fb738f66d 100644 --- a/Modules/Core/Common/test/itkPointSetGTest.cxx +++ b/Modules/Core/Common/test/itkPointSetGTest.cxx @@ -73,6 +73,42 @@ TestSetPointsByCoordinates(TPointSet & pointSet) } } } + +template +void +TestMakeDeepCopy(const TPointSet & pointSet) +{ + const auto deepCopyPointSetBase = pointSet.MakeDeepCopy(); + + // Check that MakeDeepCopy() did not return null, by using itk::Deref(ptr). + const auto & deepCopyPointSet = dynamic_cast(itk::Deref(deepCopyPointSetBase.get())); + + if (const auto * const pointData = pointSet.GetPointData()) + { + const auto * const deepCopyPointData = deepCopyPointSet.GetPointData(); + + ASSERT_NE(deepCopyPointData, nullptr); + EXPECT_NE(deepCopyPointData, pointData) << "It should not just be a shallow copy!"; + EXPECT_EQ(deepCopyPointData->CastToSTLConstContainer(), pointData->CastToSTLConstContainer()); + } + else + { + EXPECT_EQ(deepCopyPointSet.GetPointData(), nullptr); + } + + if (const auto * const points = pointSet.GetPoints()) + { + const auto * const deepCopyPoints = deepCopyPointSetBase->GetPoints(); + + ASSERT_NE(deepCopyPoints, nullptr); + EXPECT_NE(deepCopyPoints, points) << "It should not just be a shallow copy!"; + EXPECT_EQ(deepCopyPoints->CastToSTLConstContainer(), points->CastToSTLConstContainer()); + } + else + { + EXPECT_EQ(deepCopyPointSet.GetPoints(), nullptr); + } +} } // namespace @@ -113,3 +149,21 @@ TEST(PointSet, GraftDoesShallowCopy) check(*pointSet); } + + +TEST(PointSet, MakeDeepCopy) +{ + // First check an empty point set: + TestMakeDeepCopy(*itk::PointSet::New()); + + // Then check a non-empty 2-D point set with `double` data: + using PixelType = double; + using PointSetType = itk::PointSet; + using PointType = PointSetType::PointType; + + const auto pointSet = PointSetType::New(); + pointSet->SetPoints(itk::MakeVectorContainer({ PointType(), itk::MakeFilled(1.0f) })); + pointSet->SetPointData(itk::MakeVectorContainer({ 0.0, 1.0, 2.0 })); + + TestMakeDeepCopy(*pointSet); +}