Skip to content

Commit

Permalink
ENH: Add Clone() and MakeDeepCopy() to PointSetBase
Browse files Browse the repository at this point in the history
`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).
  • Loading branch information
N-Dekker committed Oct 31, 2024
1 parent 033a711 commit cac63ce
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 0 deletions.
12 changes: 12 additions & 0 deletions Modules/Core/Common/include/itkPointSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,18 @@ class ITK_TEMPLATE_EXPORT PointSet : public PointSetBase<typename TMeshTraits::P
void
PrintSelf(std::ostream & os, Indent indent) const override;

private:
void
DetachPointData() override
{
if (m_PointDataContainer)
{
// Make a new copy of the point data, detached from the original one.
const auto pointData = PointDataContainer::New();
pointData->CastToSTLContainer() = m_PointDataContainer->CastToSTLConstContainer();
m_PointDataContainer = pointData;
}
}
}; // End Class: PointSet
} // end namespace itk

Expand Down
25 changes: 25 additions & 0 deletions Modules/Core/Common/include/itkPointSetBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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

Expand Down
54 changes: 54 additions & 0 deletions Modules/Core/Common/test/itkPointSetGTest.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,42 @@ TestSetPointsByCoordinates(TPointSet & pointSet)
}
}
}

template <typename TPointSet>
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<const TPointSet &>(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


Expand Down Expand Up @@ -113,3 +149,21 @@ TEST(PointSet, GraftDoesShallowCopy)

check(*pointSet);
}


TEST(PointSet, MakeDeepCopy)
{
// First check an empty point set:
TestMakeDeepCopy(*itk::PointSet<int>::New());

// Then check a non-empty 2-D point set with `double` data:
using PixelType = double;
using PointSetType = itk::PointSet<PixelType, 2>;
using PointType = PointSetType::PointType;

const auto pointSet = PointSetType::New();
pointSet->SetPoints(itk::MakeVectorContainer<PointType>({ PointType(), itk::MakeFilled<PointType>(1.0f) }));
pointSet->SetPointData(itk::MakeVectorContainer<PixelType>({ 0.0, 1.0, 2.0 }));

TestMakeDeepCopy(*pointSet);
}

0 comments on commit cac63ce

Please sign in to comment.