1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
|
/*=========================================================================
*
* Copyright NumFOCUS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
#ifndef itkIndexRange_h
#define itkIndexRange_h
#include <cassert>
#include <cstddef> // For ptrdiff_t.
#include <iterator> // For bidirectional_iterator_tag and reverse_iterator.
#include <type_traits> // For conditional and enable_if.
#include "itkImageRegion.h"
#include "itkIndex.h"
#include "itkSize.h"
namespace itk
{
/**
* \class IndexRange
*
* Modern C++11 range, supporting efficient iteration over the indices of
* an image grid space.
*
* The following example prints all indices of an 2-D grid space of size 2x3.
\code
constexpr unsigned int Dimension = 2;
const Size<Dimension> size = { {2, 3} };
const ZeroBasedIndexRange<Dimension> indexRange{ size };
for (const Index<Dimension> index : indexRange)
{
std::cout << index;
}
// Output: "[0, 0][1, 0][0, 1][1, 1][0, 2][1, 2]"
\endcode
*
* The indices from IndexRange can also be used as consecutive locations
* of a ShapedImageNeighborhoodRange, for example:
\code
for (const auto index : indexRange)
{
shapedImageNeighborhoodRange.SetLocation(index);
for (const PixelType neighborPixel : shapedImageNeighborhoodRange)
{
// Process neighbor pixel...
}
}
\endcode
*
* IndexRange is designed to conform to Standard C++ Iterator requirements,
* so that it can be used in range-based for loop, and its iterators can be
* passed to Standard C++ algorithms.
*
* \author Niels Dekker, LKEB, Leiden University Medical Center
*
* \see ShapedImageNeighborhoodRange
* \ingroup ImageIterators
* \ingroup ITKCommon
*/
template <unsigned int VDimension, bool VBeginAtZero>
class IndexRange final
{
public:
static constexpr unsigned int Dimension = VDimension;
using SizeType = Size<VDimension>;
using IndexType = Index<VDimension>;
class const_iterator final
{
public:
// Types conforming the iterator requirements of the C++ standard library:
using difference_type = ptrdiff_t;
using value_type = IndexType;
using reference = const IndexType &;
using pointer = const IndexType *;
using iterator_category = std::bidirectional_iterator_tag;
/** Default-constructor, as required for any C++11 Forward Iterator.
* \note The other five "special member functions" (copy-constructor,
* copy-assignment operator, move-constructor, move-assignment operator,
* and destructor) are defaulted implicitly, following the C++ "Rule of Zero".
*/
const_iterator() = default;
/** Returns a reference to the current index. */
reference operator*() const noexcept { return m_Index; }
/** Returns a pointer to the current index. */
pointer operator->() const noexcept { return &(**this); }
/** Prefix increment ('++it'). */
const_iterator &
operator++() noexcept
{
for (unsigned int i = 0; i < (VDimension - 1); ++i)
{
auto & indexValue = m_Index[i];
++indexValue;
if (indexValue <= m_MaxIndex[i])
{
return *this;
}
indexValue = m_MinIndex[i];
}
++m_Index.back();
return *this;
}
/** Postfix increment ('it++').
* \note Usually prefix increment ('++it') is preferable. */
const_iterator
operator++(int) noexcept
{
auto result = *this;
++(*this);
return result;
}
/** Prefix decrement ('--it'). */
const_iterator &
operator--() noexcept
{
for (unsigned int i = 0; i < (VDimension - 1); ++i)
{
auto & indexValue = m_Index[i];
--indexValue;
if (indexValue >= m_MinIndex[i])
{
return *this;
}
indexValue = m_MaxIndex[i];
}
--m_Index.back();
return *this;
}
/** Postfix increment ('it--').
* \note Usually prefix increment ('--it') is preferable. */
const_iterator
operator--(int) noexcept
{
auto result = *this;
--(*this);
return result;
}
/** Returns (it1 == it2) for iterators it1 and it2. Note that these iterators
* should be from the same range. This operator does not support comparing iterators
* from different ranges. */
friend bool
operator==(const const_iterator & lhs, const const_iterator & rhs) noexcept
{
assert(lhs.m_MaxIndex == rhs.m_MaxIndex);
return lhs.m_Index == rhs.m_Index;
}
/** Returns (it1 != it2) for iterators it1 and it2. */
friend bool
operator!=(const const_iterator & lhs, const const_iterator & rhs) noexcept
{
// Implemented just like the corresponding std::rel_ops operator.
return !(lhs == rhs);
}
/** Returns (it1 < it2) for iterators it1 and it2. */
friend bool
operator<(const const_iterator & lhs, const const_iterator & rhs) noexcept
{
for (unsigned int i = VDimension; i > 0; --i)
{
const auto difference = lhs.m_Index[i - 1] - rhs.m_Index[i - 1];
if (difference < 0)
{
return true;
}
if (difference > 0)
{
break;
}
}
return false;
}
/** Returns (it1 > it2) for iterators it1 and it2. */
friend bool
operator>(const const_iterator & lhs, const const_iterator & rhs) noexcept
{
// Implemented just like the corresponding std::rel_ops operator.
return rhs < lhs;
}
/** Returns (it1 <= it2) for iterators it1 and it2. */
friend bool
operator<=(const const_iterator & lhs, const const_iterator & rhs) noexcept
{
// Implemented just like the corresponding std::rel_ops operator.
return !(rhs < lhs);
}
/** Returns (it1 >= it2) for iterators it1 and it2. */
friend bool
operator>=(const const_iterator & lhs, const const_iterator & rhs) noexcept
{
// Implemented just like the corresponding std::rel_ops operator.
return !(lhs < rhs);
}
private:
friend class IndexRange;
// Represents an N-dimensional index that is always zero
// Aims towards zero runtime overhead.
struct ZeroIndex
{
// The "index" operator.
constexpr IndexValueType operator[](unsigned int) const { return 0; }
// Implicitly converts to a default-initialized itk::Index<N>.
constexpr operator IndexType() const { return IndexType(); }
};
// When BeginAtZero is true, use zero as minimum index, otherwise use itk::Index<N>.
using MinIndexType = std::conditional_t<VBeginAtZero, ZeroIndex, IndexType>;
// Private constructor, only used by friend class IndexRange.
const_iterator(const IndexType & index, const MinIndexType & minIndex, const IndexType & maxIndex) noexcept
: // Note: Use parentheses instead of curly braces to initialize data members,
// to avoid AppleClang 6.0.0.6000056 compilation error, "no viable conversion..."
m_Index(index)
, m_MinIndex(minIndex)
, m_MaxIndex(maxIndex)
{}
// IndexRange::const_iterator data members:
// Current (N-dimensional) index.
IndexType m_Index;
// Minimum (N-dimensional) index.
MinIndexType m_MinIndex;
// Maximum (N-dimensional) index.
IndexType m_MaxIndex;
};
using iterator = const_iterator;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
/** Explicitly defaulted default-constructor. Constructs an empty range.
* \note The other five "special member functions" (copy-constructor,
* copy-assignment operator, move-constructor, move-assignment operator,
* and destructor) are implicitly defaulted, following the C++ "Rule of Zero".
*/
IndexRange() = default;
/** Constructs a range of indices for the specified grid size.
*/
explicit IndexRange(const SizeType & gridSize)
: // Note: Use parentheses instead of curly braces to initialize data members,
// to avoid AppleClang 6.0.0.6000056 compile errors, "no viable conversion..."
m_MinIndex()
, m_MaxIndex(CalculateMaxIndex(typename iterator::MinIndexType(), gridSize))
{}
/** Constructs a range of indices for the specified image region.
* \note This function is unavailable when VBeginAtZero is true (in
* case there is a substitution failure, and C++ "SFINAE" kicks in).
*/
template <bool VIsSubstitutionFailure = VBeginAtZero,
typename TVoid = std::enable_if_t<!VIsSubstitutionFailure>>
explicit IndexRange(const ImageRegion<VDimension> & imageRegion)
: // Note: Use parentheses instead of curly braces to initialize data members,
// to avoid AppleClang 6.0.0.6000056 compile errors, "no viable conversion..."
m_MinIndex(imageRegion.GetIndex())
, m_MaxIndex(CalculateMaxIndex(imageRegion.GetIndex(), imageRegion.GetSize()))
{
// Three compile-time asserts, just to check if SFINAE worked properly:
static_assert(!VIsSubstitutionFailure,
"This template should (of course) be instantiated without substitution failure.");
static_assert(std::is_same_v<TVoid, void>,
"std::enable_if<!VIsSubstitutionFailure> should yield void, by definition.");
static_assert(!VBeginAtZero, "This constructor should only be is available when VBeginAtZero is false.");
}
/** Returns an iterator to the first index. */
iterator
begin() const noexcept
{
return iterator(m_MinIndex, m_MinIndex, m_MaxIndex);
}
/** Returns an 'end iterator' for this range. */
iterator
end() const noexcept
{
IndexType index = m_MinIndex;
index.back() = m_MaxIndex.back() + 1;
return iterator(index, m_MinIndex, m_MaxIndex);
}
/** Returns a const iterator to the first index.
* Provides only read-only access to the index data. */
const_iterator
cbegin() const noexcept
{
return this->begin();
}
/** Returns a const 'end iterator' for this range. */
const_iterator
cend() const noexcept
{
return this->end();
}
/** Returns a reverse 'begin iterator' for this range. */
reverse_iterator
rbegin() const noexcept
{
return reverse_iterator(this->end());
}
/** Returns a reverse 'end iterator' for this range. */
reverse_iterator
rend() const noexcept
{
return reverse_iterator(this->begin());
}
/** Returns a const reverse 'begin iterator' for this range. */
const_reverse_iterator
crbegin() const noexcept
{
return this->rbegin();
}
/** Returns a const reverse 'end iterator' for this range. */
const_reverse_iterator
crend() const noexcept
{
return this->rend();
}
/** Returns the size of the range, that is the number of indices. */
size_t
size() const noexcept
{
size_t result = 1;
for (unsigned int i = 0; i < VDimension; ++i)
{
result *= ((m_MaxIndex[i] + 1) - m_MinIndex[i]);
}
return result;
}
/** Tells whether the range is empty. */
bool
empty() const noexcept
{
// When an IndexRange is empty, each index value of m_MaxIndex is less than the corresponding
// index value of m_MinIndex. And vice versa: when an IndexRange is non-empty, each index value
// of m_MaxIndex is greater than or equal to the corresponding index value of m_MinIndex.
// Note that the range contains one element when m_MaxIndex == m_MinIndex.
return m_MaxIndex[0] < m_MinIndex[0];
}
private:
using MinIndexType = typename iterator::MinIndexType;
static IndexType
CalculateMaxIndex(const MinIndexType & minIndex, const SizeType & size)
{
const bool sizeHasZeroValue = [&size] {
for (const auto sizeValue : size)
{
if (sizeValue == 0)
{
return true;
}
}
return false;
}();
// Treat any size that has a zero value equally.
const SizeType normalizedSize = sizeHasZeroValue ? SizeType{ { 0 } } : size;
IndexType index;
for (unsigned int i = 0; i < VDimension; ++i)
{
index[i] = minIndex[i] + static_cast<IndexValueType>(normalizedSize[i]) - 1;
}
return index;
}
// IndexRange data members:
// Minimum (N-dimensional) index.
MinIndexType m_MinIndex{};
// Maximum (N-dimensional) index.
IndexType m_MaxIndex = IndexType::Filled(-1);
};
template <unsigned int VDimension>
using ImageRegionIndexRange = IndexRange<VDimension, false>;
template <unsigned int VDimension>
using ZeroBasedIndexRange = IndexRange<VDimension, true>;
} // namespace itk
#endif
|