/*========================================================================= * * 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 #include // For ptrdiff_t. #include // For bidirectional_iterator_tag and reverse_iterator. #include // 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 size = { {2, 3} }; const ZeroBasedIndexRange indexRange{ size }; for (const Index 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 class IndexRange final { public: static constexpr unsigned int Dimension = VDimension; using SizeType = Size; using IndexType = Index; 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. constexpr operator IndexType() const { return IndexType(); } }; // When BeginAtZero is true, use zero as minimum index, otherwise use itk::Index. using MinIndexType = std::conditional_t; // 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; using const_reverse_iterator = std::reverse_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 > explicit IndexRange(const ImageRegion & 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, "std::enable_if 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(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 using ImageRegionIndexRange = IndexRange; template using ZeroBasedIndexRange = IndexRange; } // namespace itk #endif