1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
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  *         http://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 #ifndef itkOffset_h
19 #define itkOffset_h
20 
21 #include "itkSize.h"
22 #include "itkMath.h"
23 
24 namespace itk
25 {
26 
27 /**
28  * \struct Offset
29  * \brief Represent a n-dimensional offset between two n-dimensional indexes of n-dimensional image.
30  *
31  * Offset is a templated class to represent a multi-dimensional offset,
32  * i.e. (i,j,k,...). Offset is templated over the dimension of the space.
33  * ITK assumes the first element of a size (bounds) is the fastest moving index.
34  *
35  * For efficiency, Offset does not define a default constructor, a
36  * copy constructor, or an operator=. We rely on the compiler to provide
37  * efficient bitwise copies.
38  *
39  * Offset is an "aggregate" class.  Its data is public (m_InternalArray)
40  * allowing for fast and convenient instantiations/assignments.
41  *
42  * The following syntax for assigning an aggregate type like this is allowed/suggested:
43  *
44  *
45  * Offset<3> var{{ 256, 256, 20 }}; // Also prevent narrowing conversions
46  * Offset<3> var = {{ 256, 256, 20 }};
47  *
48  * The doubled braces {{ and }} are required to prevent `gcc -Wall'
49  * (and perhaps other compilers) from complaining about a partly
50  * bracketed initializer.
51  *
52  * As an aggregate type that is intended to provide highest performance
53  * characteristics, this class is not appropriate to inherit from,
54  * so setting this struct as final.
55  *
56  * \sa Index
57  * \ingroup ImageAccess
58  * \ingroup ITKCommon
59  *
60  * \wiki
61  * \wikiexample{SimpleOperations/Offset,Add an offset to a pixel index}
62  * \endwiki
63  */
64 
65 template <unsigned int VDimension = 2>
66 struct ITK_TEMPLATE_EXPORT Offset final
67 {
68 public:
69   // Using the `rule of zero` to this aggregate type
70   // C++20 changes the definition of aggregate such that classes with any user-declared ctors are no longer aggregates.
71 
72   /** Standard class type aliases. */
73   using Self = Offset;
74 
75   /** Compatible Offset and value type alias. */
76   using OffsetType = Offset<VDimension>;
77   using OffsetValueType = ::itk::OffsetValueType;
78 
79   /** Dimension constant */
80   static constexpr unsigned int Dimension = VDimension;
81 
82   /** Get the dimension (size) of the index. */
GetOffsetDimensionfinal83   static constexpr unsigned int GetOffsetDimension()
84   {
85     return VDimension;
86   }
87 
88 
89   /** Add two offsets. */
90   const Self operator+(const Self & vec) const
91   {
92     Self result;
93 
94     for( unsigned int i = 0; i < VDimension; i++ )
95       {
96       result[i] = m_InternalArray[i] + vec.m_InternalArray[i];
97       }
98     return result;
99   }
100 
101   /** Add a size to an offset.  */
102   const Self operator+(const Size<VDimension> & sz) const
103   {
104     Self result;
105 
106     for( unsigned int i = 0; i < VDimension; i++ )
107       {
108       result[i] = m_InternalArray[i] + sz[i];
109       }
110     return result;
111   }
112 
113   /** Increment index by a size.  */
114   const Self & operator+=(const Size<VDimension> & sz)
115   {
116     for( unsigned int i = 0; i < VDimension; i++ )
117       {
118       m_InternalArray[i] += sz[i];
119       }
120     return *this;
121   }
122 
123   /** Decrement index by a size.  */
124   const Self & operator-=(const Size<VDimension> & sz)
125   {
126     for( unsigned int i = 0; i < VDimension; i++ )
127       {
128       m_InternalArray[i] -= sz[i];
129       }
130     return *this;
131   }
132 
133   /** Subtract two offsets. */
134   const Self operator-(const Self & vec)
135   {
136     Self result;
137 
138     for( unsigned int i = 0; i < VDimension; i++ )
139       {
140       result[i] = m_InternalArray[i] - vec.m_InternalArray[i];
141       }
142     return result;
143   }
144 
145   /** Increment offset by an offset.  */
146   const Self & operator+=(const Self & vec)
147   {
148     for( unsigned int i = 0; i < VDimension; i++ )
149       {
150       m_InternalArray[i] += vec.m_InternalArray[i];
151       }
152     return *this;
153   }
154 
155   /** Decrement offset by an offset.  */
156   const Self & operator-=(const Self & vec)
157   {
158     for( unsigned int i = 0; i < VDimension; i++ )
159       {
160       m_InternalArray[i] -= vec.m_InternalArray[i];
161       }
162     return *this;
163   }
164 
165 
166   /** Get the offset. This provides a read only pointer to the offset.
167    * \sa SetOffset() */
GetOffsetfinal168   const OffsetValueType * GetOffset() const
169   {
170     return m_InternalArray;
171   }
172 
173   /** Set the index.
174    * Try to prototype this function so that val has to point to a block of
175    * memory that is the appropriate size.
176    * \sa GetOffset() */
SetOffsetfinal177   void SetOffset(const OffsetValueType val[VDimension])
178   {
179     std::copy(val,
180               val + VDimension,
181               m_InternalArray);
182   }
183 
184   /** Sets the value of one of the elements.
185    * This method is mainly intended to facilitate the access to elements
186    * from Tcl and Python where C++ notation is not very convenient.
187    * \warning No bound checking is performed.
188    * \sa SetIndex()
189    * \sa GetElement() */
SetElementfinal190   void SetElement(unsigned long element, OffsetValueType val)
191   {
192     m_InternalArray[element] = val;
193   }
194 
195   /** Gets the value of one of the elements.
196    * This method is mainly intended to facilitate the access to elements
197    * from Tcl and Python where C++ notation is not very convenient.
198    * \warning No bound checking is performed
199    * \sa GetIndex()
200    * \sa SetElement() */
GetElementfinal201   OffsetValueType GetElement(unsigned long element) const
202   {
203     return m_InternalArray[element];
204   }
205 
206   /** Set one value for the offset in all dimensions.  Useful for initializing
207    * an offset to zero. */
Fillfinal208   void Fill(OffsetValueType value)
209   {
210     std::fill_n(begin(), size(), value);
211   }                                        // MATCH std::array assign, ITK Fill
212 
213   /** Offset is an "aggregate" class.  Its data is public (m_InternalArray)
214    * allowing for fast and convenient instantiations/assignments.
215    * ( See main class documentation for an example of initialization)
216    */
217   /*
218    *  Ask the compiler to align a type to the maximum useful alignment for the target
219    *  machine you are compiling for. Whenever you leave out the alignment factor in an
220    *  aligned attribute specification, the compiler automatically sets the alignment
221    *  for the type to the largest alignment that is ever used for any data type on
222    *  the target machine you are compiling for. Doing this can often make copy
223    *  operations more efficient, because the compiler can use whatever instructions
224    *  copy the biggest chunks of memory when performing copies to or from the variables
225    *  that have types that you have aligned this way.
226    */
227   static_assert( VDimension > 0, "Error: Only positive value sized VDimension allowed" );
228   alignas(OffsetValueType) OffsetValueType m_InternalArray[VDimension];
229 
230   /** Copy values from a FixedArray by rounding each one of the components */
231   template <typename TCoordRep>
CopyWithRoundfinal232   inline void CopyWithRound(const FixedArray<TCoordRep, VDimension> & point)
233   {
234     for( unsigned int i = 0; i < VDimension; ++i )
235       {
236       m_InternalArray[i] = Math::Round<OffsetValueType>(point[i]);
237       }
238   }
239 
240   /** Copy values from a FixedArray by casting each one of the components */
241   template <typename TCoordRep>
CopyWithCastfinal242   inline void CopyWithCast(const FixedArray<TCoordRep, VDimension> & point)
243   {
244     for( unsigned int i = 0; i < VDimension; ++i )
245       {
246       m_InternalArray[i] = static_cast<OffsetValueType>( point[i] );
247       }
248   }
249 
250   /** Return a basis vector of the form [0, ..., 0, 1, 0, ... 0] where the "1"
251    * is positioned in the location specified by the parameter "dim". Valid
252    * values of "dim" are 0, ..., VDimension-1. */
253   static Self GetBasisOffset(unsigned int dim);
254 
255 
256   // ======================= Mirror the access pattern behavior of the std::array class
257   /**
258    * Mirror the std::array type aliases and member function
259    * so that the Offset class can be treated as a container
260    * class in a way that is similar to the std::array.
261    */
262   using value_type = ::itk::OffsetValueType;
263   using reference = value_type &;
264   using const_reference = const value_type &;
265   using iterator = value_type *;
266   using const_iterator = const value_type *;
267   using size_type = unsigned int;
268   using difference_type = std::ptrdiff_t;
269   using reverse_iterator = std::reverse_iterator<iterator>;
270   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
271 
272   /**
273    * Mirror behavior of the std::array manipulations
274    * See std::array for documentation on these methods
275    */
assignfinal276   void assign(const value_type & newValue)
277   {
278     std::fill_n(begin(), size(), newValue);
279   }
280 
swapfinal281   void swap(Offset & other)
282   {
283     std::swap(m_InternalArray, other.m_InternalArray);
284   }
285 
beginfinal286   iterator begin()
287   {
288     return iterator(&m_InternalArray[0]);
289   }
290 
beginfinal291   const_iterator begin() const
292   {
293     return const_iterator(&m_InternalArray[0]);
294   }
295 
endfinal296   iterator end()
297   {
298     return iterator(&m_InternalArray[VDimension]);
299   }
300 
endfinal301   const_iterator end() const
302   {
303     return const_iterator(&m_InternalArray[VDimension]);
304   }
305 
rbeginfinal306   reverse_iterator rbegin()
307   {
308     return reverse_iterator(end() );
309   }
310 
rbeginfinal311   const_reverse_iterator rbegin() const
312   {
313     return const_reverse_iterator(end() );
314   }
315 
rendfinal316   reverse_iterator rend()
317   {
318     return reverse_iterator(begin() );
319   }
320 
rendfinal321   const_reverse_iterator rend() const
322   {
323     return const_reverse_iterator(begin() );
324   }
325 
sizefinal326   constexpr size_type size() const
327   {
328     return VDimension;
329   }
330 
max_sizefinal331   constexpr size_type max_size() const
332   {
333     return VDimension;
334   }
335 
emptyfinal336   constexpr bool empty() const
337   {
338     return false;
339   }
340 
341   reference operator[](size_type pos)
342   {
343     return m_InternalArray[pos];
344   }
345 
346   const_reference operator[](size_type pos) const
347   {
348     return m_InternalArray[pos];
349   }
350 
atfinal351   reference at(size_type pos)
352   {
353     ExceptionThrowingBoundsCheck(pos); return m_InternalArray[pos];
354   }
355 
atfinal356   const_reference at(size_type pos) const
357   {
358     ExceptionThrowingBoundsCheck(pos); return m_InternalArray[pos];
359   }
360 
frontfinal361   reference front()
362   {
363     return *begin();
364   }
365 
frontfinal366   const_reference front() const
367   {
368     return *begin();
369   }
370 
backfinal371   reference back()
372   {
373     return VDimension ? *(end() - 1) : *end();
374   }
375 
backfinal376   const_reference back() const
377   {
378     return VDimension ? *(end() - 1) : *end();
379   }
380 
datafinal381   OffsetValueType * data()
382   {
383     return &m_InternalArray[0];
384   }
385 
datafinal386   const OffsetValueType * data() const
387   {
388     return &m_InternalArray[0];
389   }
390 
391 private:
ExceptionThrowingBoundsCheckfinal392   void ExceptionThrowingBoundsCheck(size_type pos) const
393   {
394     if( pos >= VDimension )
395       {
396       throw std::out_of_range("array::ExceptionThrowingBoundsCheck");
397       }
398   }
399 
400 };  //------------ End struct Offset
401 
402 template <unsigned int VDimension>
403 Offset<VDimension>
404 Offset<VDimension>
GetBasisOffset(unsigned int dim)405 ::GetBasisOffset(unsigned int dim)
406 {
407   Self ind;
408 
409   memset(ind.m_InternalArray, 0, sizeof( OffsetValueType ) * VDimension);
410   ind.m_InternalArray[dim] = 1;
411   return ind;
412 }
413 
414 template <unsigned int VDimension>
415 std::ostream & operator<<(std::ostream & os, const Offset<VDimension> & ind)
416 {
417   os << "[";
418   unsigned int dimlim = VDimension - 1;
419   for( unsigned int i = 0; i < dimlim; ++i )
420     {
421     os << ind[i] << ", ";
422     }
423   if( VDimension >= 1 )
424     {
425     os << ind[VDimension - 1];
426     }
427   os << "]";
428   return os;
429 }
430 
431 // ======================= Mirror the access pattern behavior of the std::array class
432 // Array comparisons.
433 template <unsigned int VDimension>
434 inline bool
435 operator==(const Offset<VDimension> & one, const Offset<VDimension> & two)
436 {
437   return std::equal(one.begin(), one.end(), two.begin() );
438 }
439 
440 template <unsigned int VDimension>
441 inline bool
442 operator!=(const Offset<VDimension> & one, const Offset<VDimension> & two)
443 {
444   return !(one == two);
445 }
446 
447 template <unsigned int VDimension>
448 inline bool
449 operator<(const Offset<VDimension> & one, const Offset<VDimension> & two)
450 {
451   return std::lexicographical_compare(one.begin(), one.end(),
452                                       two.begin(), two.end() );
453 }
454 
455 template <unsigned int VDimension>
456 inline bool
457 operator>(const Offset<VDimension> & one, const Offset<VDimension> & two)
458 {
459   return two < one;
460 }
461 
462 template <unsigned int VDimension>
463 inline bool
464 operator<=(const Offset<VDimension> & one, const Offset<VDimension> & two)
465 {
466   return !(one > two);
467 }
468 
469 template <unsigned int VDimension>
470 inline bool
471 operator>=(const Offset<VDimension> & one, const Offset<VDimension> & two)
472 {
473   return !(one < two);
474 }
475 
476 // Specialized algorithms [6.2.2.2].
477 template <unsigned int VDimension>
478 inline void
swap(Offset<VDimension> & one,Offset<VDimension> & two)479 swap(Offset<VDimension> & one, Offset<VDimension> & two)
480 {
481   std::swap(one.m_InternalArray, two.m_InternalArray);
482 }
483 
484 // static constexpr definition explicitly needed in C++11
485 template< unsigned int VDimension >
486 constexpr unsigned int Offset<VDimension>::Dimension;
487 
488 } // end namespace itk
489 
490 #endif
491