vspline 1.1.0
Generic C++11 Code for Uniform B-Splines
domain.h
Go to the documentation of this file.
1/************************************************************************/
2/* */
3/* vspline - a set of generic tools for creation and evaluation */
4/* of uniform b-splines */
5/* */
6/* Copyright 2015 - 2023 by Kay F. Jahnke */
7/* */
8/* The git repository for this software is at */
9/* */
10/* https://bitbucket.org/kfj/vspline */
11/* */
12/* Please direct questions, bug reports, and contributions to */
13/* */
14/* kfjahnke+vspline@gmail.com */
15/* */
16/* Permission is hereby granted, free of charge, to any person */
17/* obtaining a copy of this software and associated documentation */
18/* files (the "Software"), to deal in the Software without */
19/* restriction, including without limitation the rights to use, */
20/* copy, modify, merge, publish, distribute, sublicense, and/or */
21/* sell copies of the Software, and to permit persons to whom the */
22/* Software is furnished to do so, subject to the following */
23/* conditions: */
24/* */
25/* The above copyright notice and this permission notice shall be */
26/* included in all copies or substantial portions of the */
27/* Software. */
28/* */
29/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
30/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
31/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
32/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
33/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
34/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
35/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
36/* OTHER DEALINGS IN THE SOFTWARE. */
37/* */
38/************************************************************************/
39
40/*! \file domain.h
41
42 \brief code to perform combined scaling and translation on coordinates
43
44 This is what might be called collateral code, class domain is not
45 currently used in vspline.
46
47 A common requirement is to map coordinates in one range to another
48 range, effectively performing a combined scaling and translation.
49 Given incoming coordinates in a range [ in_low , in_high ] and a
50 desired range for outgoing coordinates of [ out_low , out_high ],
51 and an incoming coordinate c, a vspline::domain performs this
52 operation:
53
54 c' = ( c - in_low ) * scale + out_low
55
56 where
57
58 scale = ( out_high - out_low ) / ( in_high - in_low )
59
60 The code can handle arbitrary dimensions, float and double coordinate
61 elementary types, and, optionally, it can perform vectorized operations
62 on vectorized coordinates.
63
64 vspline::domain is derived from vspline::unary_functor and can be
65 used like any other vspline::unary_functor. A common use case would
66 be to access a vspline::evaluator with a different coordinate range
67 than the spline's 'natural' coordinates (assuming a 1D spline of floats):
68
69 auto _ev = vspline::make_safe_evaluator ( bspl ) ;
70 auto ev = vspline::domain ( bspl , 0 , 100 ) + _ev ;
71
72 ev.eval ( coordinate , result ) ;
73
74 Here, the domain is built over the spline with an incoming range
75 of [ 0 , 100 ], so evaluating at 100 will be equivalent to evaluating
76 _ev at bspl.upper_limit().
77*/
78
79#ifndef VSPLINE_DOMAIN_H
80#define VSPLINE_DOMAIN_H
81
82#include "unary_functor.h"
83#include <assert.h>
84
85namespace vspline
86{
87/// class domain is a coordinate transformation functor. It provides
88/// a handy way to translate an arbitrary range of incoming coordinates
89/// to an arbitrary range of outgoing coordinates. This is done with a
90/// linear translation function. if the source range is [s0,s1] and the
91/// target range is [t0,t1], the translation function s->t is:
92///
93/// t = ( s - s0 ) * ( t1 - t0 ) / ( s1 - s0 ) + t0
94///
95/// In words: the target coordinate's distance from the target range's
96/// lower bound is proportional to the source coordinate's distance
97/// from the source range's lower bound. Note that this is *not* a
98/// gate function: the domain will accept any incoming value and
99/// perform the shift/scale operation on it; incoming values outside
100/// [ in_low , in_high ] will produce outgoing values outside
101/// [ out_low , out_high ].
102///
103/// The first constructor takes s0, s1, t0 and t1. With this functor,
104/// arbitrary mappings of the form given above can be achieved.
105/// The second constructor takes a vspline::bspline object to obtain
106/// t0 and t1. These are taken as the spline's 'true' range, depending
107/// on it's boundary conditions: for periodic splines, this is [0...M],
108/// for REFLECT Bcs it's [-0.5,M-0,5], and for 'normal' splines it's
109/// [0,M-1]. s0 and s1, the start and end of the domain's coordinate range,
110/// can be passed in and default to 0 and 1, which constitutes 'normalized'
111/// spline coordinates, where 0 is mapped to the lower end of the 'true'
112/// range and 1 to the upper.
113///
114/// class domain is especially useful for situations where several b-splines
115/// cover the same data in different resolution, like in image pyramids.
116/// If these different splines are all evaluated with a domain chained to the
117/// evaluator which uses a common domain range, they can all be accessed with
118/// identical coordinates, even if the spline shapes don't match isotropically.
119/// Note, though, that domain objects created with b-splines with REFLECT
120/// or PERIODIC boundary conditions won't match unless the splines have the
121/// same size because the spline's bounds won't coincide for differently
122/// sized splines. You may want to create the domain object from coordinate
123/// values in this case.
124///
125/// The evaluation routine in class domain_type makes sure that incoming
126/// values in [ in_low , in_high ] will never produce outgoing values
127/// outside [ out_low , out_high ]. If this guarantee is not needed, the
128/// 'raw' evaluation routine _eval can be used instead. with _eval, output
129/// may overshoot out_high slightly.
130///
131/// I should mention libeinspline here, which has this facility as a fixed
132/// feature in it's spline types. I decided to keep it separate and create
133/// class domain instead for those cases where the functionality is needed.
134
135template < typename coordinate_type ,
137 >
139: public vspline::unary_functor < coordinate_type ,
140 coordinate_type ,
141 _vsize > ,
142 public vspline::callable
143 < domain_type < coordinate_type , _vsize > ,
144 coordinate_type ,
145 coordinate_type ,
146 _vsize
147 >
148{
151 _vsize >
153
154 using base_type::dim_in ;
155 using base_type::vsize ;
156 using typename base_type::in_type ;
157 using typename base_type::in_ele_type ;
158 using typename base_type::out_type ;
159 using typename base_type::in_v ;
160 using typename base_type::in_ele_v ;
161 using typename base_type::out_v ;
162
165 typedef typename vigra::TinyVector < rc_type , dim_in > limit_type ;
166
167 // internally, we work with definite TinyVectors, in order
168 // to code consistently for any dimensionality:
169
172
173 /// constructor taking the lower and upper fix points
174 /// for incoming and outgoing values. If coordinate_type
175 /// is fundamental, assigning to the limit_type members
176 /// produces the desired TinyVectors of one fundamental.
177
178 domain_type ( const coordinate_type & _in_low ,
179 const coordinate_type & _in_high ,
180 const coordinate_type & _out_low ,
181 const coordinate_type & _out_high )
182 : in_low ( _in_low ) ,
183 out_low ( _out_low ) ,
184 in_high ( _in_high ) ,
185 out_high ( _out_high ) ,
186 scale ( ( _out_high - _out_low ) / ( _in_high - _in_low ) )
187 { }
188
189 // KFJ 2019-04-17 Modified arguments to domain_type's c'tor to
190 // take a bspline object as lower reference, as upper reference
191 // and for both references. The previous code taking a single
192 // bspine object was confusing, because it took the spline and the
193 // limits 'the other way round'. Also removed defaults for limit
194 // arguments.
195
196 /// constructor taking the fix points for outgoing values
197 /// from a bspline object, and the incoming lower and upper
198 /// fix points explicitly
199
200 template < class bspl_type >
201 domain_type ( const bspl_type & bspl ,
202 const coordinate_type & _out_low ,
203 const coordinate_type & _out_high )
204 : in_low ( bspl.lower_limit() ) ,
205 in_high ( bspl.upper_limit() ) ,
206 out_low ( _out_low ) ,
207 out_high ( _out_high ) ,
208 scale ( ( _out_high - _out_low )
209 / ( bspl.upper_limit() - bspl.lower_limit() ) )
210 { }
211
212 template < class bspl_type >
213 domain_type ( const coordinate_type & _in_low ,
214 const coordinate_type & _in_high ,
215 const bspl_type & bspl )
216 : in_low ( _in_low ) ,
217 in_high ( _in_high ) ,
218 out_low ( bspl.lower_limit() ) ,
219 out_high ( bspl.upper_limit() ) ,
220 scale ( ( bspl.upper_limit() - bspl.lower_limit() )
221 / ( _in_high - _in_low ) )
222 { }
223
224 template < class bspl_type_1 , class bspl_type_2 >
225 domain_type ( const bspl_type_1 & bspl_in ,
226 const bspl_type_2 & bspl_out )
227 : in_low ( bspl_in.lower_limit() ) ,
228 in_high ( bspl_in.upper_limit() ) ,
229 out_low ( bspl_out.lower_limit() ) ,
230 out_high ( bspl_out.upper_limit() ) ,
231 scale ( ( bspl_out.upper_limit() - bspl_out.lower_limit() )
232 / ( bspl_in.upper_limit() - bspl_in.lower_limit() ) )
233 { }
234
235private:
236
237 /// 'polish' simply checks if in is == in_high and returns
238 /// out_high for that case. This looks silly, but depending
239 /// on the scaling factor, out may overshoot out_high if
240 /// in == in_high. Hence this routine.
241
242 void polish ( const in_type & in ,
243 out_type & out ,
244 int d
245 ) const
246 {
247 out = ( in == in_high[d] ) ? out_high[d] : out ;
248 }
249
250 /// now the vectorized version. Here we can use
251 /// 'proper' vector code with masking: the equality
252 /// test yields a SIMD mask.
253
254 template < typename T , int N >
255 void polish ( const vspline::simdized_type < T , N > & in ,
257 int d
258 ) const
259 {
260 out ( in == in_high[d] ) = out_high[d] ;
261 }
262
263public:
264
265 // next we have variants of eval(). Depending on circumstances,
266 // we have a fair number of distinct cases to deal with: we may
267 // receive vectorized or unvectorized data, possibly of several
268 // dimensions, and the vector data may or may not use Vc SIMD types.
269
270 /// eval for unvectorized data. we dispatch on in_type, the incoming
271 /// coordinate, being a 'naked' fundamental or a TinyVector
272
273 void eval ( const in_type & in ,
274 out_type & out ) const
275 {
276 eval ( in , out , std::is_fundamental < in_type > () ) ;
277 }
278
279 /// eval dispatch for fundamental, 1D coordinates
280
281 void eval ( const in_type & in ,
282 out_type & out ,
283 std::true_type // in_type is a naked fundamental
284 ) const
285 {
286 if ( in == in_high[0] )
287 out = out_high[0] ;
288 else
289 out = ( in - in_low[0] ) * scale[0] + out_low[0] ;
290 }
291
292 /// eval dispatch for TinyVectors of fundamentals
293 /// - unvectorized nD cordinates
294
295 void eval ( const in_type & in ,
296 out_type & out ,
297 std::false_type // in_type is a TinyVector
298 ) const
299 {
300 for ( int d = 0 ; d < dim_in ; d++ )
301 {
302 if ( in[d] == in_high[d] )
303 out[d] = out_high[d] ;
304 else
305 out[d] = ( in[d] - in_low[d] ) * scale[d] + out_low[d] ;
306 }
307 }
308
309 /// eval for vectorized data.
310 /// if in_type is a 'naked' fundamental, crd_type is a 'naked' ele_v,
311 /// otherwise it's a TinyVector of ele_v. So we dispatch:
312
313 template < class crd_type >
314 void eval ( const crd_type & in ,
315 crd_type & out ) const
316 {
317 eval ( in , out , std::is_fundamental < in_type > () ) ;
318 }
319
320 /// eval dispatch for vectorized coordinates, 1D case
321
322 template < typename = std::enable_if < ( vsize > 1 ) > >
323 void eval ( const in_v & in ,
324 out_v & out ,
325 std::true_type // crd_type is a 'naked' ele_v
326 ) const
327 {
328 out = ( in - in_low[0] ) * scale[0] + out_low[0] ;
329
330 // we dispatch to one of two variants of 'polish', depending on
331 // whether crd_type, which is a vspline::vector, internally uses
332 // a Vc type or a TinyVector:
333
334 polish < rc_type , vsize > ( in , out , 0 ) ;
335 }
336
337 /// eval dispatch for vectorized coordinates, nD case. This iterates over
338 /// the dimensions and performs the 1D operation for each component.
339
340 template < typename = std::enable_if < ( vsize > 1 ) > >
341 void eval ( const in_v & in ,
342 out_v & out ,
343 std::false_type
344 ) const
345 {
346 typedef typename in_v::value_type component_type ;
347
348 for ( int d = 0 ; d < dim_in ; d++ )
349 {
350 out[d] = ( in[d] - in_low[d] ) * scale[d] + out_low[d] ;
351 polish < rc_type , vsize > ( in[d] , out[d] , d ) ;
352 }
353 }
354
355} ;
356
357/// factory function to create a domain_type type object from the
358/// desired lower and upper fix point for incoming coordinates and
359/// the lower and upper fix point for outgoing coordinates.
360/// the resulting functor maps incoming coordinates in the range of
361/// [in_low,in_high] to coordinates in the range of [out_low,out_high]
362
363template < class coordinate_type ,
366domain ( const coordinate_type & in_low ,
367 const coordinate_type & in_high ,
368 const coordinate_type & out_low ,
369 const coordinate_type & out_high )
370{
372 ( in_low , in_high , out_low , out_high ) ;
373}
374
375/// factory function to create a domain_type type object
376/// from the desired lower and upper reference point for incoming
377/// coordinates and a vspline::bspline object providing the lower
378/// and upper reference for outgoing coordinates
379/// the resulting functor maps incoming coordinates in the range of
380/// [ in_low , in_high ] to coordinates in the range of
381/// [ bspl.lower_limit() , bspl.upper_limit() ]
382
383template < class coordinate_type ,
384 class spline_type ,
387domain ( const coordinate_type & in_low ,
388 const coordinate_type & in_high ,
389 const spline_type & bspl )
390{
392 ( in_low , in_high , bspl ) ;
393}
394
395/// factory function to create a domain_type type object
396/// from a vspline::bspline object providing the lower
397/// and upper reference for incomoing coordinates and the
398/// desired lower and upper reference point for outgoing coordinates
399/// the resulting functor maps incoming coordinates in the range of
400/// [ bspl.lower_limit() , bspl.upper_limit() ]
401/// to coordinates in the range of [ out_low , out_high ]
402
403template < class coordinate_type ,
404 class spline_type ,
407domain ( const spline_type & bspl ,
408 const coordinate_type & out_low ,
409 const coordinate_type & out_high )
410{
412 ( bspl , out_low , out_high ) ;
413}
414
415/// factory function to create a domain_type type object
416/// from a vspline::bspline object providing the lower
417/// and upper reference for incomoing coordinates and a
418/// vspline::bspline object providing the lower
419/// and upper reference for outgoing coordinates
420/// the resulting functor maps incoming coordinates in the range of
421/// [ bspl_in.lower_limit() , bspl_in.upper_limit() ]
422/// to outgoing coordinates in the range of
423/// [ bspl_out.lower_limit() , bspl_out.upper_limit() ]
424
425template < class coordinate_type ,
426 class spline_type ,
429domain ( const spline_type & bspl_in ,
430 const spline_type & bspl_out )
431{
433 ( bspl_in , bspl_out ) ;
434}
435
436} ; // namespace vspline
437
438#endif // #ifndef VSPLINE_DOMAIN_H
vigra::TinyVector< float, 2 > coordinate_type
Definition: ca_correct.cc:107
vspline::bspline< pixel_type, 2 > spline_type
Definition: ca_correct.cc:111
Definition: basis.h:79
vspline::domain_type< coordinate_type, _vsize > domain(const coordinate_type &in_low, const coordinate_type &in_high, const coordinate_type &out_low, const coordinate_type &out_high)
factory function to create a domain_type type object from the desired lower and upper fix point for i...
Definition: domain.h:366
typename vector_traits< T, N > ::type simdized_type
this alias is used as a shorthand to pick the vectorized type for a given type T and a size N from 'v...
Definition: vector.h:459
class bspline now builds on class bspline_base, adding coefficient storage, while bspline_base provid...
Definition: bspline.h:499
mixin 'callable' is used with CRTP: it serves as additional base to unary functors which are meant to...
class domain is a coordinate transformation functor. It provides a handy way to translate an arbitrar...
Definition: domain.h:148
void eval(const in_v &in, out_v &out, std::false_type) const
eval dispatch for vectorized coordinates, nD case. This iterates over the dimensions and performs the...
Definition: domain.h:341
base_type::in_ele_type rc_type
Definition: domain.h:163
base_type::in_nd_ele_type nd_rc_type
Definition: domain.h:164
domain_type(const coordinate_type &_in_low, const coordinate_type &_in_high, const coordinate_type &_out_low, const coordinate_type &_out_high)
constructor taking the lower and upper fix points for incoming and outgoing values....
Definition: domain.h:178
void eval(const in_v &in, out_v &out, std::true_type) const
eval dispatch for vectorized coordinates, 1D case
Definition: domain.h:323
void eval(const in_type &in, out_type &out, std::false_type) const
eval dispatch for TinyVectors of fundamentals
Definition: domain.h:295
const limit_type in_high
Definition: domain.h:170
domain_type(const bspl_type_1 &bspl_in, const bspl_type_2 &bspl_out)
Definition: domain.h:225
const limit_type out_high
Definition: domain.h:170
void eval(const in_type &in, out_type &out) const
eval for unvectorized data. we dispatch on in_type, the incoming coordinate, being a 'naked' fundamen...
Definition: domain.h:273
vspline::unary_functor< coordinate_type, coordinate_type, _vsize > base_type
Definition: domain.h:152
void eval(const in_type &in, out_type &out, std::true_type) const
eval dispatch for fundamental, 1D coordinates
Definition: domain.h:281
const limit_type scale
Definition: domain.h:171
vigra::TinyVector< rc_type, dim_in > limit_type
Definition: domain.h:165
const limit_type out_low
Definition: domain.h:170
domain_type(const coordinate_type &_in_low, const coordinate_type &_in_high, const bspl_type &bspl)
Definition: domain.h:213
const limit_type in_low
Definition: domain.h:170
void eval(const crd_type &in, crd_type &out) const
eval for vectorized data. if in_type is a 'naked' fundamental, crd_type is a 'naked' ele_v,...
Definition: domain.h:314
domain_type(const bspl_type &bspl, const coordinate_type &_out_low, const coordinate_type &_out_high)
constructor taking the fix points for outgoing values from a bspline object, and the incoming lower a...
Definition: domain.h:201
class unary_functor provides a functor object which offers a system of types for concrete unary funct...
vector_traits< coordinate_type, vsize >::type in_v
vectorized in_type and out_type. vspline::vector_traits supplies these types so that multidimensional...
vspline::vector_traits< coordinate_type >::ele_type in_ele_type
vector_traits< coordinate_type, vsize >::type out_v
vector_traits< coordinate_type, vsize >::ele_v in_ele_v
a simdized type of the elementary type of result_type, which is used for coefficients and results....
with the definition of 'simd_traits', we can proceed to implement 'vector_traits': struct vector_trai...
Definition: vector.h:344
interface definition for unary functors