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