Skip to content

Commit f43b1b8

Browse files
N-Dekkerdzenanz
authored andcommitted
ENH: Add PointSet::SetPointsByCoordinates(coordinates)
Aims to provide a safe alternative to `PointSet::SetPoints(PointsVectorContainer *)`.
1 parent a995532 commit f43b1b8

File tree

4 files changed

+137
-0
lines changed

4 files changed

+137
-0
lines changed

Modules/Core/Common/include/itkPointSet.h

+4
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ class ITK_TEMPLATE_EXPORT PointSet : public DataObject
169169
void
170170
SetPoints(PointsVectorContainer *);
171171

172+
/** Sets the points by specifying its coordinates. */
173+
void
174+
SetPointsByCoordinates(const std::vector<CoordRepType> & coordinates);
175+
172176
/** Get the points container. */
173177
PointsContainer *
174178
GetPoints();

Modules/Core/Common/include/itkPointSet.hxx

+50
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,56 @@ PointSet<TPixelType, VDimension, TMeshTraits>::SetPoints(PointsVectorContainer *
8181
this->Modified();
8282
}
8383

84+
85+
template <typename TPixelType, unsigned int VDimension, typename TMeshTraits>
86+
void
87+
PointSet<TPixelType, VDimension, TMeshTraits>::SetPointsByCoordinates(const std::vector<CoordRepType> & coordinates)
88+
{
89+
itkDebugMacro("Setting the points to the specified coordinates");
90+
91+
const size_t numberOfCoordinates = coordinates.size();
92+
93+
if (numberOfCoordinates % PointDimension != 0)
94+
{
95+
itkExceptionMacro("Number of specified coordinates incompatible with the point dimension");
96+
}
97+
98+
const size_t numberOfPoints = numberOfCoordinates / PointDimension;
99+
100+
if (m_PointsContainer == nullptr)
101+
{
102+
m_PointsContainer = PointsContainer::New();
103+
}
104+
105+
using STLContainerType = typename PointsContainer::STLContainerType;
106+
107+
STLContainerType & points = m_PointsContainer->CastToSTLContainer();
108+
points.clear();
109+
110+
if constexpr (std::is_same_v<STLContainerType, std::vector<PointType>>)
111+
{
112+
// STLContainerType is either an std::vector or an std::map. Only when it is an std::vector, it should be resized.
113+
// std::map does not have a resize function.
114+
points.resize(numberOfPoints);
115+
}
116+
else
117+
{
118+
static_assert(std::is_same_v<STLContainerType, std::map<PointIdentifier, PointType>>);
119+
}
120+
121+
auto coordinateIterator = coordinates.cbegin();
122+
123+
for (PointIdentifier pointIdentifier{}; pointIdentifier < numberOfPoints; ++pointIdentifier)
124+
{
125+
PointType & point = points[pointIdentifier];
126+
std::copy_n(coordinateIterator, PointDimension, point.begin());
127+
coordinateIterator += PointDimension;
128+
}
129+
130+
this->Modified();
131+
}
132+
133+
84134
template <typename TPixelType, unsigned int VDimension, typename TMeshTraits>
85135
auto
86136
PointSet<TPixelType, VDimension, TMeshTraits>::GetPoints() -> PointsContainer *

Modules/Core/Common/test/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1748,6 +1748,7 @@ set(ITKCommonGTests
17481748
itkOffsetGTest.cxx
17491749
itkOptimizerParametersGTest.cxx
17501750
itkPointGTest.cxx
1751+
itkPointSetGTest.cxx
17511752
itkRGBAPixelGTest.cxx
17521753
itkRGBPixelGTest.cxx
17531754
itkShapedImageNeighborhoodRangeGTest.cxx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
19+
// First include the header file to be tested:
20+
#include "itkPointSet.h"
21+
#include "../../QuadEdgeMesh/include/itkQuadEdgeMeshTraits.h"
22+
#include <gtest/gtest.h>
23+
#include <algorithm> // For equal.
24+
#include <numeric> // For iota.
25+
26+
namespace
27+
{
28+
template <typename TPointSet>
29+
void
30+
TestSetPointsByCoordinates(TPointSet & pointSet)
31+
{
32+
using CoordRepType = typename TPointSet::CoordRepType;
33+
34+
static constexpr auto PointDimension = TPointSet::PointDimension;
35+
36+
for (unsigned int numberOfCoordinates{ 1 }; numberOfCoordinates < PointDimension; ++numberOfCoordinates)
37+
{
38+
// SetPointsByCoordinates is expected to throw an exception when the specified number of coordinates is not a
39+
// multiple of PointDimension.
40+
EXPECT_THROW(pointSet.SetPointsByCoordinates(std::vector<CoordRepType>(numberOfCoordinates)), itk::ExceptionObject);
41+
}
42+
43+
for (const unsigned int numberOfPoints : { 2, 1, 0 })
44+
{
45+
std::vector<CoordRepType> coordinates(numberOfPoints * PointDimension);
46+
47+
// Just make sure that all coordinates have different values, for the purpose of the test.
48+
std::iota(coordinates.begin(), coordinates.end(), CoordRepType());
49+
{
50+
const auto modifiedTime = pointSet.GetMTime();
51+
pointSet.SetPointsByCoordinates(coordinates);
52+
EXPECT_GT(pointSet.GetMTime(), modifiedTime);
53+
}
54+
55+
using PointsContainerType = typename TPointSet::PointsContainer;
56+
using PointIdentifier = typename TPointSet::PointIdentifier;
57+
using PointType = typename TPointSet::PointType;
58+
59+
const typename PointsContainerType::ConstPointer points = pointSet.GetPoints();
60+
61+
ASSERT_NE(points, nullptr);
62+
ASSERT_EQ(points->size(), numberOfPoints);
63+
64+
const typename PointsContainerType::STLContainerType & stlContainer = points->CastToSTLConstContainer();
65+
auto coordinateIterator = coordinates.cbegin();
66+
67+
for (PointIdentifier pointIdentifier{}; pointIdentifier < numberOfPoints; ++pointIdentifier)
68+
{
69+
const PointType & point = stlContainer.at(pointIdentifier);
70+
EXPECT_TRUE(std::equal(point.cbegin(), point.cend(), coordinateIterator));
71+
coordinateIterator += PointDimension;
72+
}
73+
}
74+
}
75+
} // namespace
76+
77+
78+
TEST(PointSet, SetPointsByCoordinates)
79+
{
80+
TestSetPointsByCoordinates(*itk::PointSet<int>::New());
81+
TestSetPointsByCoordinates(*itk::PointSet<double, 2, itk::QuadEdgeMeshTraits<double, 2, bool, bool>>::New());
82+
}

0 commit comments

Comments
 (0)