vspline 1.1.0
Generic C++11 Code for Uniform B-Splines
map.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 map.h
41
42 \brief code to handle out-of-bounds coordinates.
43
44 Incoming coordinates may not be inside the range which can be evaluated
45 by a functor. There is no one correct way of dealing with out-of-bounds
46 coordinates, so I provide a few common ways of doing it.
47
48 If the 'standard' gate types don't suffice, the classes provided here
49 can serve as templates.
50
51 The basic type handling the operation is a 'gate type', which 'treats'
52 a single value or single simdized value. For nD coordinates, we use a
53 set of these gate_type objects, one for each component; each one may be
54 of a distinct type specific to the axis the component belongs to.
55
56 Application of the gates is via a 'mapper' object, which contains
57 the gate_type objects and applies them to the components in turn.
58
59 The mapper object is a functor which converts an arbitrary incoming
60 coordinate into a 'treated' coordinate (or, with REJECT mode, may
61 throw an out_of_bounds exception).
62
63 mapper objects are derived from vspline::unary_functor, so they fit in
64 well with other code in vspline and can easily be combined with other
65 unary_functor objects, or used stand-alone. They are used inside vspline
66 to implement the factory function vspline::make_safe_evaluator, which
67 chains a suitable mapper and an evaluator to create an object allowing
68 safe evaluation of a b-spline with arbitrary coordinates where out-of-range
69 coordinates are mapped to the defined range in a way fitting the b-spline's
70 boundary conditions.
71*/
72
73#ifndef VSPLINE_MAP_H
74#define VSPLINE_MAP_H
75
76#include "unary_functor.h"
77#include <assert.h>
78
79namespace vspline
80{
81
82/// class pass_gate passes it's input to it's output unmodified.
83
84template < typename rc_type ,
85 size_t _vsize = vspline::vector_traits < rc_type > :: size
86 >
88: public vspline::unary_functor < rc_type , rc_type , _vsize >
89{
90 template < class T >
91 void eval ( const T & c ,
92 T & result ) const
93 {
94 result = c ;
95 }
96} ;
97
98/// factory function to create a pass_gate type functor
99
100template < typename rc_type ,
101 size_t _vsize = vspline::vector_traits < rc_type > :: size
102 >
105{
107}
108
109/// reject_gate throws vspline::out_of_bounds for invalid coordinates
110
111template < typename rc_type ,
112 size_t _vsize = vspline::vector_traits < rc_type > :: size
113 >
115: public vspline::unary_functor < rc_type , rc_type , _vsize >
116{
119
121 rc_type _upper )
122 : lower ( _lower ) ,
123 upper ( _upper )
124 { } ;
125
126 void eval ( const rc_type & c ,
127 rc_type & result ) const
128 {
129 if ( c < lower || c > upper )
130 throw vspline::out_of_bounds() ;
131 result = c ;
132 }
133
134 /// vectorized evaluation function. This is enabled only if vsize > 1
135 /// to guard against cases where vectorization is used but vsize is 1.
136 /// Without the enable_if, we'd end up with two overloads with the
137 /// same signature, since in_v and out_v collapse to in_type and out_type
138 /// with vsize 1.
139
141
142 using typename base_type::in_v ;
143 using typename base_type::out_v ;
144
145 template < typename = std::enable_if < ( _vsize > 1 ) > >
146 void eval ( const in_v & c ,
147 out_v & result ) const
148 {
149 if ( any_of ( ( c < lower )
150 | ( c > upper ) ) )
151 throw vspline::out_of_bounds() ;
152 result = c ;
153 }
154
155} ;
156
157/// factory function to create a reject_gate type functor given
158/// a lower and upper limit for the allowed range.
159
160template < typename rc_type ,
161 size_t _vsize = vspline::vector_traits < rc_type > :: size
162 >
164reject ( rc_type lower , rc_type upper )
165{
166 return vspline::reject_gate < rc_type , _vsize > ( lower , upper ) ;
167}
168
169/// clamp gate clamps out-of-bounds values. clamp_gate takes
170/// four arguments: the lower and upper limit of the gate, and
171/// the values which are returned if the input is outside the
172/// range: 'lfix' if it is below 'lower' and 'ufix' if it is
173/// above 'upper'
174
175template < typename rc_type ,
176 size_t _vsize = vspline::vector_traits < rc_type > :: size
177 >
179: public vspline::unary_functor < rc_type , rc_type , _vsize >
180{
181
184 const rc_type lfix ;
185 const rc_type ufix ;
186
188 rc_type _upper ,
189 rc_type _lfix ,
190 rc_type _ufix )
191 : lower ( _lower <= _upper ? _lower : _upper ) ,
192 upper ( _upper >= _lower ? _upper : _lower ) ,
193 lfix ( _lower <= _upper ? _lfix : _ufix ) ,
194 ufix ( _upper >= _lower ? _ufix : _lfix )
195 {
196 assert ( lower <= upper ) ;
197 } ;
198
199 /// simplified constructor, gate clamps to _lower and _upper
200
202 rc_type _upper )
203 : clamp_gate ( _lower , _upper , _lower , _upper )
204 { } ;
205
206 void eval ( const rc_type & c ,
207 rc_type & result ) const
208 {
209 if ( c < lower )
210 result = lfix ;
211 else if ( c > upper )
212 result = ufix ;
213 else
214 result = c ;
215 }
216
218
219 using typename base_type::in_v ;
220 using typename base_type::out_v ;
221
222 template < typename = std::enable_if < ( _vsize > 1 ) > >
223 void eval ( const in_v & c ,
224 out_v & result ) const
225 {
226 result = c ;
227 result ( c < lower ) = lfix ;
228 result ( c > upper ) = ufix ;
229 }
230
231} ;
232
233/// factory function to create a clamp_gate type functor given
234/// a lower and upper limit for the allowed range, and, optionally,
235/// the values to use if incoming coordinates are out-of-range
236
237template < typename rc_type ,
238 size_t _vsize = vspline::vector_traits < rc_type > :: size
239 >
241clamp ( rc_type lower , rc_type upper ,
242 rc_type lfix , rc_type rfix )
243{
245 ( lower , upper , lfix , rfix ) ;
246}
247
248template < typename rc_type ,
249 size_t _vsize = vspline::vector_traits < rc_type > :: size
250 >
252clamp ( rc_type lower , rc_type upper )
253{
255 ( lower , upper ) ;
256}
257
258/// vectorized fmod function using std::trunc, which is fast, but
259/// checking the result to make sure it's always <= rhs.
260
261template <typename rc_v>
262rc_v v_fmod ( rc_v lhs ,
263 const typename rc_v::value_type & rhs )
264{
265 rc_v help ( lhs ) ;
266 help /= rhs ;
267 help = trunc ( help ) ;
268 help *= rhs ;
269 lhs -= help ;
270 // due to arithmetic imprecision, result may come out >= rhs
271 // so we doublecheck and set result to 0 when this occurs
272 lhs ( abs(lhs) >= abs(rhs) ) = 0 ;
273 return lhs ;
274}
275
276/// mirror gate 'folds' coordinates into the range. From the infinite
277/// number of mirror images resulting from mirroring the input on the
278/// bounds, the only one inside the range is picked as the result.
279/// When using this gate type with splines with MIRROR boundary conditions,
280/// if the shape of the core for the axis in question is M, _lower would be
281/// passed 0 and _upper M-1.
282/// For splines with REFLECT boundary conditions, we'd pass -0.5 to
283/// _lower and M-0.5 to upper, since here we mirror 'between bounds'
284/// and the defined range is wider.
285///
286/// Note how this mode of 'mirroring' allows use of arbitrary coordinates,
287/// rather than limiting the range of acceptable input to the first reflection,
288/// as some implementations do.
289
290template < typename rc_type ,
291 size_t _vsize = vspline::vector_traits < rc_type > :: size
292 >
294: public vspline::unary_functor < rc_type , rc_type , _vsize >
295{
298
300 rc_type _upper )
301 : lower ( _lower <= _upper ? _lower : _upper ) ,
302 upper ( _upper >= _lower ? _upper : _lower )
303 {
304 assert ( lower < upper ) ;
305 } ;
306
307 void eval ( const rc_type & c ,
308 rc_type & result ) const
309 {
310 rc_type cc ( c - lower ) ;
311 auto w = upper - lower ;
312
313 cc = std::abs ( cc ) ; // left mirror, v is now >= 0
314
315 if ( cc >= w )
316 {
317 cc = fmod ( cc , 2 * w ) ; // map to one full period
318 cc -= w ; // center
319 cc = std::abs ( cc ) ; // map to half period
320 cc = w - cc ; // flip
321 }
322
323 result = cc + lower ;
324 }
325
327
328 using typename base_type::in_v ;
329 using typename base_type::out_v ;
330
331 template < typename = std::enable_if < ( _vsize > 1 ) > >
332 void eval ( const in_v & c ,
333 out_v & result ) const
334 {
335 in_v cc ( c - lower ) ;
336 auto w = upper - lower ;
337
338 cc = abs ( cc ) ; // left mirror, v is now >= 0
339
340 auto mask = ( cc >= w ) ;
341 if ( any_of ( mask ) )
342 {
343 auto cm = v_fmod ( cc , 2 * w ) ; // map to one full period
344 cm -= w ; // center
345 cm = abs ( cm ) ; // map to half period
346 cm = in_v(w) - cm ; // flip
347 cc ( mask ) = cm ;
348 }
349
350 result = cc + lower ;
351 }
352
353} ;
354
355/// factory function to create a mirror_gate type functor given
356/// a lower and upper limit for the allowed range.
357
358template < typename rc_type ,
359 size_t _vsize = vspline::vector_traits < rc_type > :: size
360 >
362mirror ( rc_type lower , rc_type upper )
363{
364 return vspline::mirror_gate < rc_type , _vsize > ( lower , upper ) ;
365}
366
367/// the periodic mapping also folds the incoming value into the allowed range.
368/// The resulting value will be ( N * period ) from the input value and inside
369/// the range, period being upper - lower.
370/// For splines done with PERIODIC boundary conditions, if the shape of
371/// the core for this axis is M, we'd pass 0 to _lower and M to _upper.
372
373template < typename rc_type ,
374 size_t _vsize = vspline::vector_traits < rc_type > :: size
375 >
377: public vspline::unary_functor < rc_type , rc_type , _vsize >
378{
381
383 rc_type _upper )
384 : lower ( _lower <= _upper ? _lower : _upper ) ,
385 upper ( _upper >= _lower ? _upper : _lower )
386 {
387 assert ( lower < upper ) ;
388 } ;
389
390 void eval ( const rc_type & c ,
391 rc_type & result ) const
392 {
393 rc_type cc = c - lower ;
394 auto w = upper - lower ;
395
396 if ( ( cc < 0 ) || ( cc >= w ) )
397 {
398 cc = fmod ( cc , w ) ;
399 if ( cc < 0 )
400 cc += w ;
401 // due to arithmetic imprecision, even though cc < 0
402 // cc+w may come out == w, so we need to test again:
403 if ( cc >= w )
404 cc = 0 ;
405 }
406
407 result = cc + lower ;
408 }
409
411
412 using typename base_type::in_v ;
413 using typename base_type::out_v ;
414
415 template < typename = std::enable_if < ( _vsize > 1 ) > >
416 void eval ( const in_v & c ,
417 out_v & result ) const
418 {
419 in_v cc ;
420
421 cc = c - lower ;
422 auto w = upper - lower ;
423
424 auto mask_below = ( cc < 0 ) ;
425 auto mask_above = ( cc >= w ) ;
426 auto mask_any = mask_above | mask_below ;
427
428 if ( any_of ( mask_any ) )
429 {
430 auto cm = v_fmod ( cc , w ) ;
431 cm ( mask_below ) = ( cm + w ) ;
432 // due to arithmetic imprecision, even though cc < 0
433 // cc+w may come out == w, so we need to test again:
434 cm ( cm >= w ) = 0 ;
435 cc ( mask_any ) = cm ;
436 }
437
438 result = cc + lower ;
439 }
440} ;
441
442/// factory function to create a periodic_gate type functor given
443/// a lower and upper limit for the allowed range.
444
445template < typename rc_type ,
446 size_t _vsize = vspline::vector_traits < rc_type > :: size
447 >
449periodic ( rc_type lower , rc_type upper )
450{
451 return vspline::periodic_gate < rc_type , _vsize > ( lower , upper ) ;
452}
453
454/// finally we define class mapper which is initialized with a set of
455/// gate objects (of arbitrary type) which are applied to each component
456/// of an incoming nD coordinate in turn.
457/// The trickery with the variadic template argument list is necessary,
458/// because we want to be able to combine arbitrary gate types (which
459/// have distinct types) to make the mapper as efficient as possible.
460/// the only requirement for a gate type is that it has to provide the
461/// necessary eval() functions.
462
463template < typename nd_rc_type ,
464 size_t _vsize ,
465 class ... gate_types >
467: public vspline::unary_functor < nd_rc_type , nd_rc_type , _vsize >
468{
469 typedef typename vspline::unary_functor
470 < nd_rc_type , nd_rc_type , _vsize > base_type ;
471
472 typedef typename base_type::in_type in_type ;
473 typedef typename base_type::out_type out_type ;
474
475 enum { vsize = _vsize } ;
476
477 enum { dimension = vigra::ExpandElementResult < nd_rc_type > :: size } ;
478
479 // we hold the 1D mappers in a tuple
480
481 typedef std::tuple < gate_types... > mvec_type ;
482
483 // mvec holds the 1D gate objects passed to the constructor
484
486
487 // the constructor receives gate objects
488
489 map_functor ( gate_types ... args )
490 : mvec ( args... )
491 { } ;
492
493 // constructor variant taking a tuple of gates
494
495 map_functor ( const mvec_type & _mvec )
496 : mvec ( _mvec )
497 { } ;
498
499 // to handle the application of the 1D gates, we use a recursive
500 // helper type which applies the 1D gate for a specific axis and
501 // then recurses to the next axis until axis 0 is reached.
502 // We also pass 'dimension' as template argument, so we can specialize
503 // for 1D operation (see below)
504
505 template < int level , int dimension , typename nd_coordinate_type >
506 struct _map
507 {
508 void operator() ( const mvec_type & mvec ,
509 const nd_coordinate_type & in ,
510 nd_coordinate_type & out ) const
511 {
512 std::get<level>(mvec).eval ( in[level] , out[level] ) ;
513 _map < level - 1 , dimension , nd_coordinate_type >() ( mvec , in , out ) ;
514 }
515 } ;
516
517 // at level 0 the recursion ends
518
519 template < int dimension , typename nd_coordinate_type >
520 struct _map < 0 , dimension , nd_coordinate_type >
521 {
522 void operator() ( const mvec_type & mvec ,
523 const nd_coordinate_type & in ,
524 nd_coordinate_type & out ) const
525 {
526 std::get<0>(mvec).eval ( in[0] , out[0] ) ;
527 }
528 } ;
529
530 // here's the specialization for 1D operation
531
532 template < typename coordinate_type >
533 struct _map < 0 , 1 , coordinate_type >
534 {
535 void operator() ( const mvec_type & mvec ,
536 const coordinate_type & in ,
537 coordinate_type & out ) const
538 {
539 std::get<0>(mvec).eval ( in , out ) ;
540 }
541 } ;
542
543 // now we define eval for unvectorized and vectorized operation
544 // by simply delegating to struct _map at the top level.
545
546 template < class in_type , class out_type >
547 void eval ( const in_type & in ,
548 out_type & out ) const
549 {
550 _map < dimension - 1 , dimension , in_type >() ( mvec , in , out ) ;
551 }
552
553} ;
554
555/// factory function to create a mapper type functor given
556/// a set of gate_type objects. Please see vspline::make_safe_evaluator
557/// for code to automatically create a mapper object suitable for a
558/// specific vspline::bspline.
559
560template < typename nd_rc_type ,
561 size_t _vsize = vspline::vector_traits < nd_rc_type > :: size ,
562 class ... gate_types >
563vspline::map_functor < nd_rc_type , _vsize , gate_types... >
564mapper ( gate_types ... args )
565{
566 return vspline::map_functor < nd_rc_type , _vsize , gate_types... >
567 ( args... ) ;
568}
569
570} ; // namespace vspline
571
572#endif // #ifndef VSPLINE_MAP_H
vigra::TinyVector< float, 2 > coordinate_type
Definition: ca_correct.cc:107
double rc_type
Definition: eval.cc:94
Definition: basis.h:79
vspline::reject_gate< rc_type, _vsize > reject(rc_type lower, rc_type upper)
factory function to create a reject_gate type functor given a lower and upper limit for the allowed r...
Definition: map.h:164
bool any_of(simd_type< T, vsize > arg)
Definition: simd_type.h:910
vspline::periodic_gate< rc_type, _vsize > periodic(rc_type lower, rc_type upper)
factory function to create a periodic_gate type functor given a lower and upper limit for the allowed...
Definition: map.h:449
vspline::map_functor< nd_rc_type, _vsize, gate_types... > mapper(gate_types ... args)
factory function to create a mapper type functor given a set of gate_type objects....
Definition: map.h:564
vspline::clamp_gate< rc_type, _vsize > clamp(rc_type lower, rc_type upper, rc_type lfix, rc_type rfix)
factory function to create a clamp_gate type functor given a lower and upper limit for the allowed ra...
Definition: map.h:241
rc_v v_fmod(rc_v lhs, const typename rc_v::value_type &rhs)
vectorized fmod function using std::trunc, which is fast, but checking the result to make sure it's a...
Definition: map.h:262
vspline::pass_gate< rc_type, _vsize > pass()
factory function to create a pass_gate type functor
Definition: map.h:104
vspline::mirror_gate< rc_type, _vsize > mirror(rc_type lower, rc_type upper)
factory function to create a mirror_gate type functor given a lower and upper limit for the allowed r...
Definition: map.h:362
clamp gate clamps out-of-bounds values. clamp_gate takes four arguments: the lower and upper limit of...
Definition: map.h:180
vector_traits< IN, vsize >::type in_v
vectorized in_type and out_type. vspline::vector_traits supplies these types so that multidimensional...
const rc_type lower
Definition: map.h:182
void eval(const in_v &c, out_v &result) const
Definition: map.h:223
void eval(const rc_type &c, rc_type &result) const
Definition: map.h:206
clamp_gate(rc_type _lower, rc_type _upper, rc_type _lfix, rc_type _ufix)
Definition: map.h:187
vector_traits< OUT, vsize >::type out_v
const rc_type lfix
Definition: map.h:184
clamp_gate(rc_type _lower, rc_type _upper)
simplified constructor, gate clamps to _lower and _upper
Definition: map.h:201
vspline::unary_functor< rc_type, rc_type, _vsize > base_type
Definition: map.h:217
const rc_type upper
Definition: map.h:183
const rc_type ufix
Definition: map.h:185
void operator()(const mvec_type &mvec, const nd_coordinate_type &in, nd_coordinate_type &out) const
Definition: map.h:508
finally we define class mapper which is initialized with a set of gate objects (of arbitrary type) wh...
Definition: map.h:468
base_type::out_type out_type
Definition: map.h:473
vspline::unary_functor< nd_rc_type, nd_rc_type, _vsize > base_type
Definition: map.h:470
void eval(const in_type &in, out_type &out) const
Definition: map.h:547
map_functor(const mvec_type &_mvec)
Definition: map.h:495
base_type::in_type in_type
Definition: map.h:472
std::tuple< gate_types... > mvec_type
Definition: map.h:481
map_functor(gate_types ... args)
Definition: map.h:489
const mvec_type mvec
Definition: map.h:485
mirror gate 'folds' coordinates into the range. From the infinite number of mirror images resulting f...
Definition: map.h:295
vector_traits< IN, vsize >::type in_v
vectorized in_type and out_type. vspline::vector_traits supplies these types so that multidimensional...
const rc_type lower
Definition: map.h:296
const rc_type upper
Definition: map.h:297
vector_traits< OUT, vsize >::type out_v
void eval(const in_v &c, out_v &result) const
Definition: map.h:332
void eval(const rc_type &c, rc_type &result) const
Definition: map.h:307
vspline::unary_functor< rc_type, rc_type, _vsize > base_type
Definition: map.h:326
mirror_gate(rc_type _lower, rc_type _upper)
Definition: map.h:299
out_of_bounds is thrown by mapping mode REJECT for out-of-bounds coordinates this exception is left w...
Definition: common.h:317
class pass_gate passes it's input to it's output unmodified.
Definition: map.h:89
void eval(const T &c, T &result) const
Definition: map.h:91
the periodic mapping also folds the incoming value into the allowed range. The resulting value will b...
Definition: map.h:378
const rc_type upper
Definition: map.h:380
vector_traits< IN, vsize >::type in_v
vectorized in_type and out_type. vspline::vector_traits supplies these types so that multidimensional...
vspline::unary_functor< rc_type, rc_type, _vsize > base_type
Definition: map.h:410
vector_traits< OUT, vsize >::type out_v
periodic_gate(rc_type _lower, rc_type _upper)
Definition: map.h:382
void eval(const rc_type &c, rc_type &result) const
Definition: map.h:390
void eval(const in_v &c, out_v &result) const
Definition: map.h:416
const rc_type lower
Definition: map.h:379
reject_gate throws vspline::out_of_bounds for invalid coordinates
Definition: map.h:116
void eval(const in_v &c, out_v &result) const
Definition: map.h:146
vector_traits< IN, vsize >::type in_v
vectorized in_type and out_type. vspline::vector_traits supplies these types so that multidimensional...
const rc_type lower
Definition: map.h:117
vector_traits< OUT, vsize >::type out_v
void eval(const rc_type &c, rc_type &result) const
Definition: map.h:126
const rc_type upper
Definition: map.h:118
vspline::unary_functor< rc_type, rc_type, _vsize > base_type
vectorized evaluation function. This is enabled only if vsize > 1 to guard against cases where vector...
Definition: map.h:140
reject_gate(rc_type _lower, rc_type _upper)
Definition: map.h:120
class unary_functor provides a functor object which offers a system of types for concrete unary funct...
vector_traits< IN, vsize >::type in_v
vectorized in_type and out_type. vspline::vector_traits supplies these types so that multidimensional...
vector_traits< OUT, vsize >::type out_v
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