1 // Copyright 2009-2020 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 
4 #pragma once
5 
6 // ospray
7 #include <sstream>
8 #include <vector>
9 #include "../common.h"
10 #include "../math/range.h"
11 #include "for_each.h"
12 
13 namespace rkcommon {
14   namespace array3D {
15 
16     /*! ABSTRACTION for a 3D array of data */
17     template <typename value_t>
18     struct Array3D
19     {
20       virtual ~Array3D() = default;
21 
22       /*! return size (ie, "dimensions") of volume */
23       virtual vec3i size() const = 0;
24 
25       /*! get cell value at given location, but ensure that location
26         is actually a valid cell ID inside the volume (clamps to
27         nearest cell in volume if 'where' is outside) */
28       virtual value_t get(const vec3i &where) const = 0;
29 
30       /*! get the range/interval of all cell values in the given
31         begin/end region of the volume */
getValueRangeArray3D32       range_t<value_t> getValueRange(const vec3i &begin, const vec3i &end) const
33       {
34         range_t<value_t> v = get(begin);
35         for_each(begin, end, [&](const vec3i &idx) { v.extend(get(idx)); });
36         return v;
37       }
38 
39       /*! get value range over entire volume */
getValueRangeArray3D40       range_t<value_t> getValueRange() const
41       {
42         return getValueRange(vec3i(0), size());
43       }
44 
45       /*! returns number of elements (as 64-bit int) across all dimensions */
46       virtual size_t numElements() const = 0;
47     };
48 
49     /*! implementation for an actual array3d that stores a 3D array of values */
50     template <typename value_t>
51     struct ActualArray3D : public Array3D<value_t>
52     {
53       ActualArray3D(const vec3i &dims, void *externalMem = nullptr);
~ActualArray3DActualArray3D54       ~ActualArray3D() override
55       {
56         if (valuesAreMine)
57           delete[] value;
58       }
59 
60       /*! return size (ie, "dimensions") of volume */
61       vec3i size() const override;
62 
63       /*! get cell value at location
64 
65         \warning 'where' MUST be a valid cell location */
66       value_t get(const vec3i &where) const override;
67 
68       /*! set cell value at location to given value
69 
70         \warning 'where' MUST be a valid cell location */
71       void set(const vec3i &where, const value_t &t);
72 
73       void clear(const value_t &t);
74 
75       /*! returns number of elements (as 64-bit int) across all dimensions */
76       size_t numElements() const override;
77 
78       /* compute the (1D) linear array index for a (3D) grid coordinate */
indexOfActualArray3D79       size_t indexOf(const vec3i &pos) const
80       {
81         return pos.x + size_t(dims.x) * (pos.y + size_t(dims.y) * pos.z);
82       }
83 
84       const vec3i dims;
85       value_t *value;
86       // bool that specified whether it was us that alloc'ed this mem,
87       // and thus, whether we should free it upon termination.
88       bool valuesAreMine;
89     };
90 
91     /*! shifts another array3d by a given amount */
92     template <typename value_t>
93     struct IndexShiftedArray3D : public Array3D<value_t>
94     {
IndexShiftedArray3DIndexShiftedArray3D95       IndexShiftedArray3D(std::shared_ptr<Array3D<value_t>> _actual,
96                           const vec3i &_shift)
97           : actual(_actual), shift(_shift)
98       {
99       }
100 
101       /*! return size (ie, "dimensions") of volume */
sizeIndexShiftedArray3D102       vec3i size() const override
103       {
104         return actual->size();
105       }
106 
107       /*! get cell value at location
108 
109         \warning 'where' MUST be a valid cell location */
getIndexShiftedArray3D110       value_t get(const vec3i &where) const override
111       {
112         return actual->get((where + size() + shift) % size());
113       }
114 
115       /*! set cell value at location to given value
116 
117         \warning 'where' MUST be a valid cell location */
setIndexShiftedArray3D118       void set(const vec3i &, const value_t &)
119       {
120         throw std::runtime_error("cannot 'set' in a IndexShiftArray3D");
121       }
122 
123       /*! returns number of elements (as 64-bit int) across all dimensions */
numElementsIndexShiftedArray3D124       size_t numElements() const override
125       {
126         return actual->numElements();
127       }
128 
129       const vec3i shift;
130       const std::shared_ptr<Array3D<value_t>> actual;
131     };
132 
133     /*! implemnetaiton of a wrapper class that makes an actual array3d
134       of one type look like that of another type */
135     template <typename in_t, typename out_t>
136     struct Array3DAccessor : public Array3D<out_t>
137     {
138       Array3DAccessor(std::shared_ptr<Array3D<in_t>> actual);
139 
140       /*! return size (ie, "dimensions") of volume */
141       vec3i size() const override;
142 
143       /*! get cell value at location
144 
145         \warning 'where' MUST be a valid cell location */
146       out_t get(const vec3i &where) const override;
147 
148       /*! returns number of elements (as 64-bit int) across all dimensions */
149       size_t numElements() const override;
150 
151      private:
152       //! the actual 3D array we're wrapping around
153       const std::shared_ptr<Array3D<in_t>> actual;
154     };
155 
156     /*! wrapper class that generates an artificially larger data set by
157       simply repeating the given input */
158     template <typename T>
159     struct Array3DRepeater : public Array3D<T>
160     {
161       Array3DRepeater(const std::shared_ptr<Array3D<T>> &actual,
162                       const vec3i &repeatedSize);
163 
164       /*! return size (ie, "dimensions") of volume */
165       vec3i size() const override;
166 
167       /*! get cell value at location
168 
169         \warning 'where' MUST be a valid cell location */
170       T get(const vec3i &where) const override;
171 
172       /*! returns number of elements (as 64-bit int) across all dimensions */
173       size_t numElements() const override;
174 
175       const vec3i repeatedSize;
176       const std::shared_ptr<Array3D<T>> actual;
177     };
178 
179     /*! implements a sub-set of another array3d */
180     template <typename value_t>
181     struct SubBoxArray3D : public Array3D<value_t>
182     {
SubBoxArray3DSubBoxArray3D183       SubBoxArray3D(const std::shared_ptr<Array3D<value_t>> &actual,
184                     const box3i &clipBox)
185           : clipBox(clipBox), actual(actual)
186       {
187         assert(actual);
188         assert(clipBox.upper.x <= actual->size().x);
189         assert(clipBox.upper.y <= actual->size().y);
190         assert(clipBox.upper.z <= actual->size().z);
191       }
192 
193       /*! return size (ie, "dimensions") of volume */
sizeSubBoxArray3D194       vec3i size() const override
195       {
196         return clipBox.size();
197       }
198 
199       /*! get cell value at location
200 
201         \warning 'where' MUST be a valid cell location */
getSubBoxArray3D202       value_t get(const vec3i &where) const override
203       {
204         return actual->get(where + clipBox.lower);
205       }
206 
207       /*! set cell value at location to given value
208 
209         \warning 'where' MUST be a valid cell location */
setSubBoxArray3D210       void set(const vec3i &, const value_t &)
211       {
212         throw std::runtime_error("cannot 'set' in a SubBoxArray3D");
213       }
214 
215       /*! returns number of elements (as 64-bit int) across all dimensions */
numElementsSubBoxArray3D216       size_t numElements() const override
217       {
218         vec3i dims = clipBox.size();
219         return size_t(dims.x) * size_t(dims.y) * size_t(dims.z);
220       }
221 
222       const box3i clipBox;
223       const std::shared_ptr<Array3D<value_t>> actual;
224     };
225 
226     /*! implements a array3d that's composed of multiple individual slices */
227     template <typename value_t>
228     struct MultiSliceArray3D : public Array3D<value_t>
229     {
MultiSliceArray3DMultiSliceArray3D230       MultiSliceArray3D(
231           const std::vector<std::shared_ptr<Array3D<value_t>>> &slice)
232           : slice(slice)
233       {
234       }
235 
236       /*! return size (ie, "dimensions") of volume */
sizeMultiSliceArray3D237       vec3i size() const override
238       {
239         return vec3i(slice[0]->size().x, slice[0]->size().y, slice.size());
240       }
241 
242       /*! get cell value at location
243 
244         \warning 'where' MUST be a valid cell location */
getMultiSliceArray3D245       value_t get(const vec3i &where) const override
246       {
247         return slice[clamp(where.z, 0, (int)slice.size() - 1)]->get(
248             vec3i(where.x, where.y, 0));
249       }
250 
251       /*! set cell value at location to given value
252 
253         \warning 'where' MUST be a valid cell location */
setMultiSliceArray3D254       void set(const vec3i &, const value_t &)
255       {
256         throw std::runtime_error("cannot 'set' in a MultiSliceArray3D");
257       }
258 
259       /*! returns number of elements (as 64-bit int) across all dimensions */
numElementsMultiSliceArray3D260       size_t numElements() const override
261       {
262         return slice[0]->numElements() * slice.size();
263       }
264 
265       const std::vector<std::shared_ptr<Array3D<value_t>>> slice;
266     };
267 
268 #ifndef _WIN32
269     /*! load raw file with given dimensions. the 'type' of the raw
270       file (uint8,float,...) is given through the function's
271       template parameter */
272     template <typename T>
273     std::shared_ptr<Array3D<T>> RKCOMMON_INTERFACE
274     loadRAW(const std::string &fileName, const vec3i &dims);
275 
276     /*! load raw file with given dimensions. the 'type' of the raw
277       file (uint8,float,...) is given through the function's
278       template parameter */
279     template <typename T>
280     std::shared_ptr<Array3D<T>> RKCOMMON_INTERFACE
281     mmapRAW(const std::string &fileName, const vec3i &dims);
282 #endif
283 
284     // Inlined definitions ////////////////////////////////////////////////////
285 
286     // ActualArray3D //
287 
288     template <typename T>
size()289     inline vec3i ActualArray3D<T>::size() const
290     {
291       return dims;
292     }
293 
294     template <typename T>
get(const vec3i & _where)295     inline T ActualArray3D<T>::get(const vec3i &_where) const
296     {
297       assert(value != nullptr);
298       const vec3i where = max(vec3i(0), min(_where, dims - vec3i(1)));
299       size_t index =
300           where.x + size_t(dims.x) * (where.y + size_t(dims.y) * (where.z));
301       assert(value);
302       assert(index < numElements());
303       const T v = value[index];
304       return v;
305     }
306 
307     template <typename T>
numElements()308     inline size_t ActualArray3D<T>::numElements() const
309     {
310       return size_t(dims.x) * size_t(dims.y) * size_t(dims.z);
311     }
312 
313     template <typename T>
ActualArray3D(const vec3i & dims,void * externalMem)314     inline ActualArray3D<T>::ActualArray3D(const vec3i &dims, void *externalMem)
315         : dims(dims),
316           value((T *)externalMem),
317           valuesAreMine(externalMem == nullptr)
318     {
319       try {
320         if (!value) {
321           const size_t numVoxels = longProduct(dims);
322           value                  = new T[numVoxels];
323         }
324       } catch (const std::bad_alloc &) {
325         std::stringstream ss;
326         ss << "could not allocate memory for Array3D of dimensions " << dims
327            << " (in Array3D::Array3D())";
328         throw std::runtime_error(ss.str());
329       }
330     }
331 
332     template <typename T>
set(const vec3i & where,const T & t)333     inline void ActualArray3D<T>::set(const vec3i &where, const T &t)
334     {
335       value[longIndex(where, size())] = t;
336     }
337 
338     template <typename T>
clear(const T & t)339     inline void ActualArray3D<T>::clear(const T &t)
340     {
341       for_each(size(), [&](const vec3i &idx) { set(idx, t); });
342     }
343 
344     // Array3DAccessor //
345 
346     template <typename in_t, typename out_t>
Array3DAccessor(std::shared_ptr<Array3D<in_t>> actual)347     inline Array3DAccessor<in_t, out_t>::Array3DAccessor(
348         std::shared_ptr<Array3D<in_t>> actual)
349         : actual(actual)
350     {
351     }
352 
353     template <typename in_t, typename out_t>
size()354     inline vec3i Array3DAccessor<in_t, out_t>::size() const
355     {
356       return actual->size();
357     }
358 
359     template <typename in_t, typename out_t>
get(const vec3i & where)360     inline out_t Array3DAccessor<in_t, out_t>::get(const vec3i &where) const
361     {
362       return (out_t)actual->get(where);
363     }
364 
365     template <typename in_t, typename out_t>
numElements()366     inline size_t Array3DAccessor<in_t, out_t>::numElements() const
367     {
368       assert(actual);
369       return actual->numElements();
370     }
371 
372     // Array3DRepeater //
373 
374     template <typename T>
Array3DRepeater(const std::shared_ptr<Array3D<T>> & actual,const vec3i & repeatedSize)375     inline Array3DRepeater<T>::Array3DRepeater(
376         const std::shared_ptr<Array3D<T>> &actual, const vec3i &repeatedSize)
377         : repeatedSize(repeatedSize), actual(actual)
378     {
379     }
380 
381     template <typename T>
size()382     inline vec3i Array3DRepeater<T>::size() const
383     {
384       return repeatedSize;
385     }
386 
387     template <typename T>
get(const vec3i & _where)388     inline T Array3DRepeater<T>::get(const vec3i &_where) const
389     {
390       vec3i where(_where.x % repeatedSize.x,
391                   _where.y % repeatedSize.y,
392                   _where.z % repeatedSize.z);
393 
394       if ((_where.x / repeatedSize.x) % 2)
395         where.x = repeatedSize.x - 1 - where.x;
396       if ((_where.y / repeatedSize.y) % 2)
397         where.y = repeatedSize.y - 1 - where.y;
398       if ((_where.z / repeatedSize.z) % 2)
399         where.z = repeatedSize.z - 1 - where.z;
400 
401       return actual->get(where);
402     }
403 
404     template <typename T>
numElements()405     inline size_t Array3DRepeater<T>::numElements() const
406     {
407       return size_t(repeatedSize.x) * size_t(repeatedSize.y) *
408              size_t(repeatedSize.z);
409     }
410 
411   }  // namespace array3D
412 }  // namespace rkcommon
413