Loading...
Searching...
No Matches
precompute.h
Go to the documentation of this file.
1#ifndef _BBM_PRECOMPUTE_H_
2#define _BBM_PRECOMPUTE_H_
3
4#include <array>
5#include <numeric>
6#include "concepts/util.h"
7#include "util/named.h"
8#include "util/constfor.h"
9#include "core/error.h"
10
11/************************************************************************/
12/*! \file precompute.h
13
14 \brief Helper structures and methods for storing precomputed compiled data
15
16*************************************************************************/
17
18namespace bbm {
19
20 /**********************************************************************/
21 /*! \brief std::array wrapper for precomputed data with optional remapping of the indices.
22
23 \tparam T = underlying data type
24 \tparam DIM = array of size of each dimension. E.g., {2,3} yields a 2 x 3
25 data structure, where '3' is the inner dimension.
26
27 \tparam MAP = either left empty, or a functor that maps the coordinates to
28 the indices. The number of functors needs to be either '0' (all are
29 identity mappings) or DIM.size(). The functors, if provided, can take
30 one coordinate at the time and map it to an index (one index per MAP),
31 or it can take all coordinates at once, and map it to an index (one
32 index per MAP). In both cases, the number of MAPs needs to equal
33 to DIM.size().
34 ***********************************************************************/
35
36 template<typename T, std::array DIM, typename... MAP> requires (sizeof...(MAP) == DIM.size() || sizeof...(MAP) == 0)
37 struct tab : public std::array<T, std::accumulate(std::begin(DIM), std::end(DIM), 1, std::multiplies<>())>
38 {
39 using base_type = std::array<T, std::accumulate(std::begin(DIM), std::end(DIM), 1, std::multiplies<>())>;
40 public:
41 //! \brief value type
42 using value = T;
43
44 //! \brief sizes of each dimension.
45 static constexpr auto sizes(void) { return DIM; }
46
47 //! \brief number of dimensions.
48 static constexpr auto dim(void) { return DIM.size(); }
49
50 private:
51 /********************************************************************/
52 /*! \brief PRIVATE method to map a coordinate to an array of indices.
53 *******************************************************************/
54 template<typename... Cs> requires (sizeof...(Cs) == dim())
55 static constexpr auto map(Cs&&... coord)
56 {
57 // Case 1: No MAP has been given; assume coord == index
58 if constexpr (sizeof...(MAP) == 0)
59 {
60 using type = decltype((coord + ...));
61 return std::array{ bbm::cast<type>(coord)...};
62 }
63 else
64 {
65 // Case 2: MAP uses all coord... at once to produce an index
66 if constexpr ((concepts::lambda<MAP, Cs...> && ...))
67 {
68 using type = decltype((MAP()(coord...) + ...));
69 return std::array{ bbm::cast<type>(MAP()(coord...))... };
70 }
71
72 // Case 3: MAP only uses a single coord at the time to produce an index
73 else if constexpr((concepts::lambda<MAP, Cs> && ...))
74 {
75 using type = decltype((MAP()(coord) + ...));
76 return std::array{ bbm::cast<type>(MAP()(coord))... };
77 }
78
79 // Otherwise invalid signature
80 else static_assert(dependent_false_v<MAP...>, BBM_INVALID_LAMBDA);
81 }
82 }
83
84 /********************************************************************/
85 /*! \brief PRIVATE: linearizes an array of indices (per dimension) to a
86 single index in the underlying std::array. Indices outside the range
87 are clamped to 0 or DIM-1.
88 ********************************************************************/
89 template<typename I>
90 static constexpr auto index(const std::array<I,dim()>& mapped)
91 {
92 index_t<I> idx = 0;
93 CONSTFOR(i, dim(),
94 {
95 idx = bbm::cast<index_t<I>>(bbm::clamp(mapped[i],0,DIM[i]-1)) + idx*DIM[i];
96 });
97 return idx;
98 }
99
100 /********************************************************************/
101 /*! \brief PRIVATE: returns false if any index lies outside the
102 corresponding dimension's size.
103 ********************************************************************/
104 template<typename I>
105 static constexpr auto valid(const std::array<I,dim()>& mapped)
106 {
107 index_mask_t<I> mask = true;
108 CONSTFOR(i, dim(), { mask &= bbm::cast<index_mask_t<I>>((mapped[i] < DIM[i]) && (mapped[i] >= 0)); });
109 return mask;
110 }
111
112 /********************************************************************/
113 /*! \brief PRIVATE: given an array of indices and a mask, return the
114 corresponding stored value and update the mask if the requested value
115 lies inside the underlying std::array (true) or outside (false). When
116 'false' is return the lookup value will be clamped to lie inside the
117 data-range.
118 ********************************************************************/
119 template<typename RET, typename I, typename Mask> requires std::convertible_to<index_mask_t<I>, Mask>
120 inline constexpr auto _lookup(const std::array<I,dim()>& mapped, Mask& mask) const
121 {
122 mask &= valid(mapped);
123 auto idx = index(mapped);
124 return bbm::lookup<RET>(*this, idx); // all are made valid
125 }
126
127 /********************************************************************/
128 /*! \brief PRIVATE: Similar to _lookup, except that the each value is the
129 interpolation result of the floor and ceil of each index. (e.g.,
130 linear interpolation in 1D, bilinear in 2D, etc...)
131 ********************************************************************/
132 template<typename RET, size_t IDX, typename A, typename Mask>
133 inline constexpr RET _interpolate(const A& mapped, Mask& mask) const
134 {
135 auto a_idx(mapped); a_idx[IDX] = bbm::floor(mapped[IDX]);
136 auto b_idx(mapped); b_idx[IDX] = bbm::ceil(mapped[IDX]);
137 auto weight = mapped[IDX] - bbm::floor(mapped[IDX]);
138
139 if constexpr (IDX == DIM.size()-1) return bbm::lerp( _lookup<RET>(a_idx, mask), _lookup<RET>(b_idx, mask), weight );
140 else return bbm::lerp( _interpolate<RET,IDX+1>(a_idx, mask), _interpolate<RET,IDX+1>(b_idx, mask), weight );
141 }
142
143 public:
144 /********************************************************************/
145 /*! @{ \name Direct Access by indices (per dimension); throws exception if out of range!
146 *******************************************************************/
147 template<typename... Is> requires (sizeof...(Is) == dim())
148 inline T& operator()(Is&&... indices) { return base_type::operator[]( index(std::array<size_t, dim()>{size_t(indices)...})); }
149
150 template<typename... Is> requires (sizeof...(Is) == dim())
151 inline const T& operator()(Is&&... indices) const { return base_type::operator[](index(std::array<size_t, dim()>{size_t(indices)...})); }
152 //! @}
153
154 /********************************************************************/
155 /*! \brief Lookup the value in the data structure given a set of
156 coordinates for each dimension. Uses nearest neighbor lookup if the
157 coordinates do not exactly correspond to an integer position in the
158 data structure.
159
160 \tparam RET = return type (must be specified)
161 \param coords = a coordinate per dimension
162 \returns a named tuple, with the resulting lookup "value" and a "valid"
163 indicating whether the value is valid.
164
165 Note: the coordinate is first converted to indices with the MAP functors.
166 *********************************************************************/
167 template<typename RET, typename... Cs> requires (sizeof...(Cs) == dim())
168 inline constexpr auto lookup(Cs&&... coords) const
169 {
170 mask_t<RET> mask = true;
171 RET value = _lookup<RET>( map(std::forward<Cs>(coords)...), mask );
172 return bbm::make_named<"value", "valid">(value, mask);
173 }
174
175 //! \brief Lookup with the native type
176 template<typename... Cs> requires (sizeof...(Cs) == dim())
177 inline constexpr auto lookup(Cs&&... coords) const { return lookup<T>(std::forward<Cs>(coords)...); }
178
179 /********************************************************************/
180 /*! \brief Lookup the value in the data structure given a set of
181 coordinates. In contrast to 'lookup' this method will linearly
182 interpolate the values of the corresponding neighoring integer indices.
183 In 1D this is just a linear interpolation, in 2D this corresponds to a
184 bilinear interpolation, etc... Thus DIM^2 lookup are interpolated.
185
186 \tparam RET = return type (must be specified)
187 \param coords = a coordinate per dimension
188 \returns a named tuple, with the resulting interpolated lookup "value"
189 and a "valid" indicating whether the value is valid.
190
191 Note: the coordinate is first converted to indices with the MAP functors.
192 *********************************************************************/
193 template<typename RET, typename... Cs> requires (sizeof...(Cs) == dim())
194 inline constexpr auto interpolate(Cs&&... coords) const
195 {
196 mask_t<RET> mask = true;
197 RET value = _interpolate<RET,0>( map(std::forward<Cs>(coords)...), mask );
198 return bbm::make_named<"value", "valid">(value, mask);
199 }
200
201 //! \brief interpolate with the native type
202 template<typename... Cs> requires (sizeof...(Cs) == dim())
203 inline constexpr auto interpolate(Cs&&... coords) const { return interpolate<T>(std::forward<Cs>(coords)...); }
204 };
205
206} // end bbm namespace
207
208#endif /* _BBM_PRECOMPUTE_H_ */
Check if lambda/functor takes a set of arguments.
Definition: util.h:185
Complile-time for loop.
#define CONSTFOR(IDX, NUMITR,...)
HELPER MACRO.
Definition: constfor.h:63
Predefined exceptions for common errors.
#define BBM_INVALID_LAMBDA
Definition: error.h:53
Definition: aggregatebsdf.h:29
constexpr named< anonymize_t< T >, NAMES... > make_named(T &&t)
Make a named of a gettable type (with size == #NAMES); renames if the type is a named container.
Definition: named.h:293
static constexpr bool dependent_false_v
Definition: error.h:33
std::array wrapper for precomputed data with optional remapping of the indices.
Definition: precompute.h:38
constexpr auto interpolate(Cs &&... coords) const
Lookup the value in the data structure given a set of coordinates. In contrast to 'lookup' this metho...
Definition: precompute.h:194
constexpr auto lookup(Cs &&... coords) const
Lookup the value in the data structure given a set of coordinates for each dimension....
Definition: precompute.h:168
static constexpr auto dim(void)
number of dimensions.
Definition: precompute.h:48
constexpr auto _lookup(const std::array< I, dim()> &mapped, Mask &mask) const
PRIVATE: given an array of indices and a mask, return the corresponding stored value and update the m...
Definition: precompute.h:120
constexpr RET _interpolate(const A &mapped, Mask &mask) const
PRIVATE: Similar to _lookup, except that the each value is the interpolation result of the floor and ...
Definition: precompute.h:133
constexpr auto interpolate(Cs &&... coords) const
interpolate with the native type
Definition: precompute.h:203
static constexpr auto map(Cs &&... coord)
PRIVATE method to map a coordinate to an array of indices.
Definition: precompute.h:55
static constexpr auto valid(const std::array< I, dim()> &mapped)
PRIVATE: returns false if any index lies outside the corresponding dimension's size.
Definition: precompute.h:105
constexpr auto lookup(Cs &&... coords) const
Lookup with the native type.
Definition: precompute.h:177
T value
value type
Definition: precompute.h:42
static constexpr auto index(const std::array< I, dim()> &mapped)
PRIVATE: linearizes an array of indices (per dimension) to a single index in the underlying std::arra...
Definition: precompute.h:90
std::array< T, std::accumulate(std::begin(DIM), std::end(DIM), 1, std::multiplies<>())> base_type
Definition: precompute.h:39
static constexpr auto sizes(void)
sizes of each dimension.
Definition: precompute.h:45
A wrapper for STL containers such as tuple, pair, and array. These containers force the programmer to...
Additional basic helper concepts.