ARB
ValueTuple.cxx
Go to the documentation of this file.
1 // ============================================================ //
2 // //
3 // File : ValueTuple.cxx //
4 // Purpose : Abstract value usable for shading items //
5 // //
6 // Coded by Ralf Westram (coder@reallysoft.de) in June 2016 //
7 // http://www.arb-home.de/ //
8 // //
9 // ============================================================ //
10 
11 #include "item_shader.h"
12 
13 // --------------------------------------------------------------------------------
14 
15 #ifdef UNIT_TESTS
16 #ifndef TEST_UNIT_H
17 #include <test_unit.h>
18 #endif
19 
20 __ATTR__REDUCED_OPTIMIZE__NO_GCSE void TEST_shaded_values() {
21  // -------------------------------
22  // NoTuple (basic test):
23 
25  TEST_REJECT(undef->is_defined());
26  TEST_REJECT(undef->clone()->is_defined());
27  TEST_EXPECT_EQUAL(undef->inspect(), "<undef>");
28 
29  // -----------------------------------
30  // LinearTuple (basic test):
31 
32  {
33  ShadedValue val0 = ValueTuple::make(0.0);
34  TEST_EXPECT(val0->is_defined());
35  TEST_EXPECT(val0->clone()->is_defined());
36  TEST_EXPECT_EQUAL(val0->inspect(), "(0.000)");
37 
38  ShadedValue val1 = ValueTuple::make(1.0);
39  TEST_EXPECT_EQUAL(val1->inspect(), "(1.000)");
40 
41  // LinearTuple (mix):
42  ShadedValue half = val0->mix(0.50, *val1);
43  TEST_EXPECT_EQUAL(half->inspect(), "(0.500)");
44 
45  TEST_EXPECT_EQUAL(val0->mix(0.25, *val1)->inspect(), "(0.750)");
46  TEST_EXPECT_EQUAL(val0->mix(0.60, *val1)->inspect(), "(0.400)");
47 
48  TEST_EXPECT_EQUAL(half->mix(0.25, *val1)->inspect(), "(0.875)");
49  TEST_EXPECT_EQUAL(half->mix(0.50, *val1)->inspect(), "(0.750)");
50  TEST_EXPECT_EQUAL(half->mix(0.75, *val1)->inspect(), "(0.625)");
51 
52  // mix LinearTuple with NoTuple:
53  TEST_EXPECT_EQUAL(undef->mix(INFINITY, *half)->inspect(), "(0.500)");
54  TEST_EXPECT_EQUAL(undef->mix(INFINITY, *half)->inspect(), "(0.500)");
55  TEST_EXPECT_EQUAL(half->mix(INFINITY, *undef)->inspect(), "(0.500)");
56  }
57 
58  // -----------------------------------
59  // PlanarTuple (basic test):
60 
61  {
62  ShadedValue pval0 = ValueTuple::make(0, 0);
63  TEST_EXPECT(pval0->is_defined());
64  TEST_EXPECT(pval0->clone()->is_defined());
65  TEST_EXPECT_EQUAL(pval0->inspect(), "(0.000,0.000)");
66 
67  // PlanarTuple (mixing):
68  ShadedValue px = ValueTuple::make(1, 0);
69  ShadedValue py = ValueTuple::make(0, 1);
70 
71  TEST_EXPECT_EQUAL(px->mix(INFINITY, *undef)->inspect(), "(1.000,0.000)");
72  TEST_EXPECT_EQUAL(undef->mix(INFINITY, *px)->inspect(), "(1.000,0.000)");
73  TEST_EXPECT_EQUAL(px->mix(0.5, *py)->inspect(), "(0.500,0.500)");
74 
75  // PlanarTuple (partially defined):
76  ShadedValue PonlyX = ValueTuple::make(0.5, NAN);
77  ShadedValue PonlyY = ValueTuple::make(NAN, 0.5);
78 
79  TEST_EXPECT(PonlyX->is_defined());
80  TEST_EXPECT(PonlyY->is_defined());
81 
82  TEST_EXPECT_EQUAL(PonlyX->inspect(), "(0.500,nan)");
83  TEST_EXPECT_EQUAL(PonlyY->inspect(), "(nan,0.500)");
84 
85  TEST_EXPECT_EQUAL(PonlyX->mix(INFINITY, *PonlyY)->inspect(), "(0.500,0.500)"); // mixed without using ratio
86  TEST_EXPECT_EQUAL(PonlyX->mix(0.5, *px)->inspect(), "(0.750,0.000)");
87  TEST_EXPECT_EQUAL(PonlyY->mix(0.5, *py)->inspect(), "(0.000,0.750)");
88  TEST_EXPECT_EQUAL(PonlyX->mix(0.5, *py)->inspect(), "(0.250,1.000)");
89  TEST_EXPECT_EQUAL(PonlyY->mix(0.5, *px)->inspect(), "(1.000,0.250)");
90  TEST_EXPECT_EQUAL(PonlyY->mix(0.25,*px)->inspect(), "(1.000,0.125)"); // ratio only affects y-coord (x-coord is undef in PonlyY!)
91  TEST_EXPECT_EQUAL(PonlyY->mix(0.75,*px)->inspect(), "(1.000,0.375)");
92  }
93 
94  // ------------------------------------
95  // SpatialTuple (basic test):
96 
97  {
98  ShadedValue sval0 = ValueTuple::make(0, 0, 0);
99  TEST_EXPECT(sval0->is_defined());
100  TEST_EXPECT(sval0->clone()->is_defined());
101  TEST_EXPECT_EQUAL(sval0->inspect(), "(0.000,0.000,0.000)");
102 
103  // SpatialTuple (mixing):
104  ShadedValue px = ValueTuple::make(1, 0, 0);
105  ShadedValue py = ValueTuple::make(0, 1, 0);
106  ShadedValue pz = ValueTuple::make(0, 0, 1);
107 
108  TEST_EXPECT_EQUAL(px->mix(INFINITY, *undef)->inspect(), "(1.000,0.000,0.000)");
109  TEST_EXPECT_EQUAL(undef->mix(INFINITY, *px)->inspect(), "(1.000,0.000,0.000)");
110  TEST_EXPECT_EQUAL(px->mix(0.5, *py)->inspect(), "(0.500,0.500,0.000)");
111  TEST_EXPECT_EQUAL(px->mix(0.5, *py)->mix(2/3.0, *pz)->inspect(), "(0.333,0.333,0.333)");
112 
113  // SpatialTuple (partially defined):
114  ShadedValue PonlyX = ValueTuple::make(0.5, NAN, NAN);
115  ShadedValue PonlyY = ValueTuple::make(NAN, 0.5, NAN);
116  ShadedValue PonlyZ = ValueTuple::make(NAN, NAN, 0.5);
117 
118  TEST_EXPECT(PonlyX->is_defined());
119  TEST_EXPECT(PonlyY->is_defined());
120  TEST_EXPECT(PonlyZ->is_defined());
121 
122  TEST_EXPECT_EQUAL(PonlyX->inspect(), "(0.500,nan,nan)");
123  TEST_EXPECT_EQUAL(PonlyY->inspect(), "(nan,0.500,nan)");
124  TEST_EXPECT_EQUAL(PonlyZ->inspect(), "(nan,nan,0.500)");
125 
126  ShadedValue pxy = PonlyX->mix(INFINITY, *PonlyY); // mixed without using ratio
127  ShadedValue pyz = PonlyY->mix(INFINITY, *PonlyZ);
128  ShadedValue pzx = PonlyZ->mix(INFINITY, *PonlyX);
129 
130  TEST_EXPECT_EQUAL(pxy->inspect(), "(0.500,0.500,nan)");
131  TEST_EXPECT_EQUAL(pyz->inspect(), "(nan,0.500,0.500)");
132  TEST_EXPECT_EQUAL(pzx->inspect(), "(0.500,nan,0.500)");
133 
134  TEST_EXPECT_EQUAL(pxy->mix(INFINITY, *PonlyZ)->inspect(), "(0.500,0.500,0.500)");
135  TEST_EXPECT_EQUAL(pyz->mix(INFINITY, *PonlyX)->inspect(), "(0.500,0.500,0.500)");
136  TEST_EXPECT_EQUAL(pzx->mix(INFINITY, *PonlyY)->inspect(), "(0.500,0.500,0.500)");
137 
138  TEST_EXPECT_EQUAL(pxy->mix(0.5, *pyz)->inspect(), "(0.500,0.500,0.500)");
139  TEST_EXPECT_EQUAL(pyz->mix(0.5, *pzx)->inspect(), "(0.500,0.500,0.500)");
140  TEST_EXPECT_EQUAL(pzx->mix(0.5, *pxy)->inspect(), "(0.500,0.500,0.500)");
141 
142  TEST_EXPECT_EQUAL(pxy->mix(0.5, *px)->inspect(), "(0.750,0.250,0.000)");
143  TEST_EXPECT_EQUAL(pyz->mix(0.5, *px)->inspect(), "(1.000,0.250,0.250)");
144  TEST_EXPECT_EQUAL(pzx->mix(0.5, *px)->inspect(), "(0.750,0.000,0.250)");
145 
146  TEST_EXPECT_EQUAL(pxy->mix(0.5, *py)->inspect(), "(0.250,0.750,0.000)");
147  TEST_EXPECT_EQUAL(pyz->mix(0.5, *py)->inspect(), "(0.000,0.750,0.250)");
148  TEST_EXPECT_EQUAL(pzx->mix(0.5, *py)->inspect(), "(0.250,1.000,0.250)");
149 
150  TEST_EXPECT_EQUAL(pxy->mix(0.5, *pz)->inspect(), "(0.250,0.250,1.000)");
151  TEST_EXPECT_EQUAL(pyz->mix(0.5, *pz)->inspect(), "(0.000,0.250,0.750)");
152  TEST_EXPECT_EQUAL(pzx->mix(0.5, *pz)->inspect(), "(0.250,0.000,0.750)");
153 
154  TEST_EXPECT_EQUAL(pxy->mix(0.25, *pz)->inspect(), "(0.125,0.125,1.000)"); // ratio does not affect z-coord (only defined in pz)
155  TEST_EXPECT_EQUAL(pyz->mix(0.25, *pz)->inspect(), "(0.000,0.125,0.875)");
156  TEST_EXPECT_EQUAL(pzx->mix(0.25, *pz)->inspect(), "(0.125,0.000,0.875)");
157  }
158 
159  // --------------------------------------
160  // test NAN leads to undefined:
161 
162  ShadedValue novalue = ValueTuple::make(NAN);
163  TEST_REJECT(novalue->is_defined());
164 
165  ShadedValue noValuePair = ValueTuple::make(NAN, NAN);
166  TEST_REJECT(noValuePair->is_defined());
167 
168  ShadedValue noValueTriple = ValueTuple::make(NAN, NAN, NAN);
169  TEST_REJECT(noValueTriple->is_defined());
170 }
171 
172 void TEST_value_color_mapping() {
173  // tests mapping from ShadedValue into range_offset (with different phasing)
174 
175  const float NOSHIFT = 0.0;
176 
177  Phaser dontPhase;
178 
179  Phaser halfPhase (0.5, false, NOSHIFT, NOSHIFT);
180  Phaser halfPhasePreShiftPos(0.5, false, +0.25, NOSHIFT);
181  Phaser halfPhasePreShiftNeg(0.5, false, +0.75, NOSHIFT); // +75% should act like -25%
182  Phaser halfPhasePstShiftPos(0.5, false, NOSHIFT, +0.25);
183  Phaser halfPhasePstShiftNeg(0.5, false, NOSHIFT, +0.75);
184 
185  Phaser twoPhase (2.0, false, NOSHIFT, NOSHIFT);
186  Phaser alt3Phase(3.0, true, NOSHIFT, NOSHIFT);
187  Phaser alt4Phase(4.0, true, NOSHIFT, NOSHIFT);
188 
189  {
190  ShadedValue val0 = ValueTuple::make(0.0);
191  ShadedValue half = ValueTuple::make(0.5);
192  ShadedValue val1 = ValueTuple::make(1.0);
193 
194  TEST_EXPECT_EQUAL(val0->range_offset(dontPhase), 0);
195  TEST_EXPECT_EQUAL(val0->range_offset(halfPhase), 0);
196  TEST_EXPECT_EQUAL(val0->range_offset(halfPhasePreShiftPos), AW_RANGE_COLORS*3/8);
197  TEST_EXPECT_EQUAL(val0->range_offset(halfPhasePreShiftNeg), AW_RANGE_COLORS*1/8);
198  TEST_EXPECT_EQUAL(val0->range_offset(halfPhasePstShiftPos), AW_RANGE_COLORS*1/4);
199  TEST_EXPECT_EQUAL(val0->range_offset(halfPhasePstShiftNeg), AW_RANGE_COLORS*3/4);
200 
201  TEST_EXPECT_EQUAL(val1->range_offset(dontPhase), AW_RANGE_COLORS-1);
202  TEST_EXPECT_EQUAL(val1->range_offset(halfPhase), AW_RANGE_COLORS/2);
203  TEST_EXPECT_EQUAL(val1->range_offset(halfPhasePreShiftPos), AW_RANGE_COLORS*3/8);
204  TEST_EXPECT_EQUAL(val1->range_offset(halfPhasePreShiftNeg), AW_RANGE_COLORS*1/8);
205  TEST_EXPECT_EQUAL(val1->range_offset(halfPhasePstShiftPos), AW_RANGE_COLORS*3/4);
206  TEST_EXPECT_EQUAL(val1->range_offset(halfPhasePstShiftNeg), AW_RANGE_COLORS*1/4);
207 
208  TEST_EXPECT_EQUAL(half->range_offset(dontPhase), AW_RANGE_COLORS/2);
209  TEST_EXPECT_EQUAL(half->range_offset(twoPhase), AW_RANGE_COLORS-1);
210  TEST_EXPECT_EQUAL(half->range_offset(alt3Phase), AW_RANGE_COLORS/2);
211  TEST_EXPECT_EQUAL(half->range_offset(alt4Phase), 0);
212  TEST_EXPECT_EQUAL(half->range_offset(halfPhase), AW_RANGE_COLORS/4);
213  TEST_EXPECT_EQUAL(half->range_offset(halfPhasePreShiftPos), AW_RANGE_COLORS*1/8);
214  TEST_EXPECT_EQUAL(half->range_offset(halfPhasePreShiftNeg), AW_RANGE_COLORS*3/8);
215  TEST_EXPECT_EQUAL(half->range_offset(halfPhasePstShiftPos), AW_RANGE_COLORS*2/4);
216  TEST_EXPECT_EQUAL(half->range_offset(halfPhasePstShiftNeg), AW_RANGE_COLORS*4/4 - 1);
217  }
218 
219 #define PQS(c) (AW_PLANAR_COLORS*(c)/4)
220 #define PLANAR_QUARTER_SHIFT(x,y) (PQS(x)*AW_PLANAR_COLORS + PQS(y))
221 
222 #if 0
223  // document results of PLANAR_QUARTER_SHIFT:
224 #define DOCQS(x,y,res) TEST_EXPECT_EQUAL(PLANAR_QUARTER_SHIFT(x,y), res)
225 
226  DOCQS(0,0,0); DOCQS(1,0,1024); DOCQS(2,0,2048); DOCQS(3,0,3072); DOCQS(4,0,4096);
227  DOCQS(0,1,16); DOCQS(1,1,1040); DOCQS(2,1,2064); DOCQS(3,1,3088); DOCQS(4,1,4112);
228  DOCQS(0,2,32); DOCQS(1,2,1056); DOCQS(2,2,2080); DOCQS(3,2,3104); DOCQS(4,2,4128);
229  DOCQS(0,3,48); DOCQS(1,3,1072); DOCQS(2,3,2096); DOCQS(3,3,3120); DOCQS(4,3,4144);
230  DOCQS(0,4,64); DOCQS(1,4,1088); DOCQS(2,4,2112); DOCQS(3,4,3136); DOCQS(4,4,4160);
231 
232 #endif
233 
234  {
235  ShadedValue pval0 = ValueTuple::make(0, 0);
236  ShadedValue px = ValueTuple::make(1, 0);
237  ShadedValue py = ValueTuple::make(0, 1);
238  ShadedValue PonlyX = ValueTuple::make(0.5, NAN);
239  ShadedValue PonlyY = ValueTuple::make(NAN, 0.5);
240 
241  TEST_EXPECT_EQUAL(pval0->range_offset(dontPhase), 0);
242  TEST_EXPECT_EQUAL(pval0->range_offset(twoPhase), 0);
243  TEST_EXPECT_EQUAL(pval0->range_offset(alt3Phase), 0);
244  TEST_EXPECT_EQUAL(pval0->range_offset(alt4Phase), 0);
245 
246  TEST_EXPECT_EQUAL(px->range_offset(dontPhase), (AW_PLANAR_COLORS-1)*AW_PLANAR_COLORS);
247  TEST_EXPECT_EQUAL(py->range_offset(dontPhase), AW_PLANAR_COLORS-1);
248 
249  // partly undefined values produce a valid range-offset:
250  TEST_EXPECT_EQUAL(PonlyX->range_offset(dontPhase), (AW_PLANAR_COLORS*AW_PLANAR_COLORS)/2);
251  TEST_EXPECT_EQUAL(PonlyY->range_offset(dontPhase), AW_PLANAR_COLORS/2);
252 
253  // test 2-dim-phase-scale and -shift:
254  TEST_EXPECT_EQUAL(px->range_offset(twoPhase), PLANAR_QUARTER_SHIFT(4, 0) - AW_PLANAR_COLORS); // -1*AW_PLANAR_COLORS due to limited range!
255  TEST_EXPECT_EQUAL(px->range_offset(halfPhase), PLANAR_QUARTER_SHIFT(2, 0));
256  TEST_EXPECT_EQUAL(px->range_offset(halfPhasePreShiftPos), PLANAR_QUARTER_SHIFT(3, 3)/2);
257  TEST_EXPECT_EQUAL(px->range_offset(halfPhasePreShiftNeg), PLANAR_QUARTER_SHIFT(1, 1)/2);
258  TEST_EXPECT_EQUAL(px->range_offset(halfPhasePstShiftPos), PLANAR_QUARTER_SHIFT(3, 1));
259  TEST_EXPECT_EQUAL(px->range_offset(halfPhasePstShiftNeg), PLANAR_QUARTER_SHIFT(1, 3));
260 
261  TEST_EXPECT_EQUAL(py->range_offset(twoPhase), PLANAR_QUARTER_SHIFT(0, 4) - 1); // -1 due to limited range!
262  TEST_EXPECT_EQUAL(py->range_offset(halfPhase), PLANAR_QUARTER_SHIFT(0, 2));
263  TEST_EXPECT_EQUAL(py->range_offset(halfPhasePreShiftPos), PLANAR_QUARTER_SHIFT(3, 3)/2);
264  TEST_EXPECT_EQUAL(py->range_offset(halfPhasePreShiftNeg), PLANAR_QUARTER_SHIFT(1, 1)/2);
265  TEST_EXPECT_EQUAL(py->range_offset(halfPhasePstShiftPos), PLANAR_QUARTER_SHIFT(1, 3));
266  TEST_EXPECT_EQUAL(py->range_offset(halfPhasePstShiftNeg), PLANAR_QUARTER_SHIFT(3, 1));
267 
268  // test 2-dim-phase-scale and -shift (nan-content):
269  TEST_EXPECT_EQUAL(PonlyX->range_offset(twoPhase), PLANAR_QUARTER_SHIFT(4,-4)); // = PLANAR_QUARTER_SHIFT(4, 0) - AW_PLANAR_COLORS
270  TEST_EXPECT_EQUAL(PonlyX->range_offset(halfPhase), PLANAR_QUARTER_SHIFT(1, 0));
271  TEST_EXPECT_EQUAL(PonlyX->range_offset(halfPhasePreShiftPos), PLANAR_QUARTER_SHIFT(1, 0)/2);
272  TEST_EXPECT_EQUAL(PonlyX->range_offset(halfPhasePreShiftNeg), PLANAR_QUARTER_SHIFT(3, 0)/2);
273  TEST_EXPECT_EQUAL(PonlyX->range_offset(halfPhasePstShiftPos), PLANAR_QUARTER_SHIFT(2, 0));
274  TEST_EXPECT_EQUAL(PonlyX->range_offset(halfPhasePstShiftNeg), PLANAR_QUARTER_SHIFT(4,-4));
275 
276  TEST_EXPECT_EQUAL(PonlyY->range_offset(twoPhase), PLANAR_QUARTER_SHIFT(0, 4) - 1);
277  TEST_EXPECT_EQUAL(PonlyY->range_offset(halfPhase), PLANAR_QUARTER_SHIFT(0, 1));
278  TEST_EXPECT_EQUAL(PonlyY->range_offset(halfPhasePreShiftPos), PLANAR_QUARTER_SHIFT(0, 1)/2);
279  TEST_EXPECT_EQUAL(PonlyY->range_offset(halfPhasePreShiftNeg), PLANAR_QUARTER_SHIFT(0, 3)/2);
280  TEST_EXPECT_EQUAL(PonlyY->range_offset(halfPhasePstShiftPos), PLANAR_QUARTER_SHIFT(0, 2));
281  TEST_EXPECT_EQUAL(PonlyY->range_offset(halfPhasePstShiftNeg), PLANAR_QUARTER_SHIFT(0, 4) - 1);
282  }
283 
284  {
285  ShadedValue sval0 = ValueTuple::make(0, 0, 0);
286 
287  ShadedValue px = ValueTuple::make(1, 0, 0);
288  ShadedValue py = ValueTuple::make(0, 1, 0);
289  ShadedValue pz = ValueTuple::make(0, 0, 1);
290 
291  ShadedValue PonlyX = ValueTuple::make(0.5, NAN, NAN);
292  ShadedValue PonlyY = ValueTuple::make(NAN, 0.5, NAN);
293  ShadedValue PonlyZ = ValueTuple::make(NAN, NAN, 0.5);
294 
295  TEST_EXPECT_EQUAL(sval0->range_offset(dontPhase), 0);
296 
298  TEST_EXPECT_EQUAL(py->range_offset(dontPhase), (AW_SPATIAL_COLORS-1)*AW_SPATIAL_COLORS);
299  TEST_EXPECT_EQUAL(pz->range_offset(dontPhase), AW_SPATIAL_COLORS-1);
300 
301  // partly undefined values produce a valid range-offset:
302  TEST_EXPECT_EQUAL(PonlyX->range_offset(dontPhase), (AW_SPATIAL_COLORS*AW_SPATIAL_COLORS*AW_SPATIAL_COLORS)/2);
303  TEST_EXPECT_EQUAL(PonlyY->range_offset(dontPhase), (AW_SPATIAL_COLORS*AW_SPATIAL_COLORS)/2);
304  TEST_EXPECT_EQUAL(PonlyZ->range_offset(dontPhase), AW_SPATIAL_COLORS/2);
305 
306  // skipping tests for phasing SpatialTuple (hardly can understand test-results for PlanarTuple)
307  }
308 }
309 TEST_PUBLISH(TEST_value_color_mapping);
310 
311 #endif // UNIT_TESTS
312 
313 // --------------------------------------------------------------------------------
314 
315 inline int CHECKED_RANGE_OFFSET(int off) {
316  is_assert(off>=0 && off<AW_RANGE_COLORS);
317  return off;
318 }
319 
320 template<int RANGE_SIZE>
321 inline int fixed_range_offset(float val) {
322  is_assert(val>=0.0 && val<=1.0); // val is output of Phaser
323  int off = val>=1.0 ? RANGE_SIZE-1 : val*RANGE_SIZE;
324  is_assert(off>=0 && off<RANGE_SIZE);
325  return off;
326 }
327 
328 // --------------------------------------------------------------------------------
329 
330 #ifdef UNIT_TESTS
331 #ifndef TEST_UNIT_H
332 #include <test_unit.h>
333 #endif
334 
335 void TEST_range_mapping() {
336  TEST_EXPECT_EQUAL(fixed_range_offset<4>(1.00), 3);
337  TEST_EXPECT_EQUAL(fixed_range_offset<4>(0.75), 3);
338  TEST_EXPECT_EQUAL(fixed_range_offset<4>(0.74), 2);
339  TEST_EXPECT_EQUAL(fixed_range_offset<4>(0.50), 2);
340  TEST_EXPECT_EQUAL(fixed_range_offset<4>(0.49), 1);
341  TEST_EXPECT_EQUAL(fixed_range_offset<4>(0.25), 1);
342  TEST_EXPECT_EQUAL(fixed_range_offset<4>(0.24), 0);
343  TEST_EXPECT_EQUAL(fixed_range_offset<4>(0.00), 0);
344 
345  TEST_EXPECT_EQUAL(fixed_range_offset<3>(1.0), 2);
346  TEST_EXPECT_EQUAL(fixed_range_offset<3>(0.67), 2);
347  TEST_EXPECT_EQUAL(fixed_range_offset<3>(0.66), 1);
348  TEST_EXPECT_EQUAL(fixed_range_offset<3>(0.34), 1);
349  TEST_EXPECT_EQUAL(fixed_range_offset<3>(0.33), 0);
350  TEST_EXPECT_EQUAL(fixed_range_offset<3>(0.0), 0);
351 }
352 
353 #endif // UNIT_TESTS
354 
355 // --------------------------------------------------------------------------------
356 
357 
358 class NoTuple: public ValueTuple {
359 public:
360  NoTuple() {}
362 
363  bool is_defined() const OVERRIDE { return false; }
364  ShadedValue clone() const OVERRIDE { return new NoTuple; }
365  int range_offset(const Phaser&) const OVERRIDE { is_assert(0); return -9999999; } // defines no range offset
366 
367 #if defined(UNIT_TESTS)
368  const char *inspect() const OVERRIDE { return "<undef>"; }
369 #endif
370 
371  // mixer:
372  ShadedValue reverse_mix(float, const LinearTuple& other) const OVERRIDE;
373  ShadedValue reverse_mix(float, const PlanarTuple& other) const OVERRIDE;
374  ShadedValue reverse_mix(float, const SpatialTuple& other) const OVERRIDE;
375  ShadedValue mix(float, const ValueTuple& other) const OVERRIDE { return other.clone(); }
376 };
377 
378 class LinearTuple FINAL_TYPE : public ValueTuple {
379  float val;
380 
381 public:
382  LinearTuple(float val_) : val(val_) {
383  is_assert(!is_nan_or_inf(val));
384  }
386 
387  bool is_defined() const OVERRIDE { return true; }
388  ShadedValue clone() const OVERRIDE { return new LinearTuple(val); }
389  int range_offset(const Phaser& phaser) const OVERRIDE { // returns int-offset into range [0 .. AW_RANGE_COLORS[
390  return CHECKED_RANGE_OFFSET(fixed_range_offset<AW_RANGE_COLORS>(phaser.rephase(val)));
391  }
392 
393 #if defined(UNIT_TESTS)
394  const char *inspect() const OVERRIDE {
395  static SmartCharPtr buf;
396  buf = GBS_global_string_copy("(%.3f)", val);
397  return &*buf;
398  }
399 #endif
400 
401  // mixer:
402  ShadedValue reverse_mix(float other_ratio, const LinearTuple& other) const OVERRIDE {
403  return new LinearTuple(other_ratio*other.val + (1-other_ratio)*val);
404  }
405  ShadedValue mix(float my_ratio, const ValueTuple& other) const OVERRIDE { return other.reverse_mix(my_ratio, *this); }
406 };
407 
408 inline float mix_floats(float me, float my_ratio, float other) {
409  if (is_nan(me)) return other;
410  if (is_nan(other)) return me;
411  return my_ratio*me + (1-my_ratio)*other;
412 }
413 
414 class PlanarTuple FINAL_TYPE : public ValueTuple {
415  float val1, val2;
416 
417 public:
418  PlanarTuple(float val1_, float val2_) :
419  val1(val1_),
420  val2(val2_)
421  {
422  is_assert(!is_inf(val1));
423  is_assert(!is_inf(val2));
424  is_assert(!(is_nan(val1) && is_nan(val2))); // only NAN is unwanted
425  }
427 
428  bool is_defined() const OVERRIDE { return true; }
429  ShadedValue clone() const OVERRIDE { return new PlanarTuple(val1, val2); }
430  int range_offset(const Phaser& phaser) const OVERRIDE { // returns int-offset into range [0 .. AW_RANGE_COLORS[
431  int c1 = is_nan(val1) ? 0 : fixed_range_offset<AW_PLANAR_COLORS>(phaser.rephase(val1));
432  int c2 = is_nan(val2) ? 0 : fixed_range_offset<AW_PLANAR_COLORS>(phaser.rephase(val2));
433  return CHECKED_RANGE_OFFSET(c1*AW_PLANAR_COLORS + c2);
434  }
435 
436 #if defined(UNIT_TESTS)
437  const char *inspect() const OVERRIDE {
438  static SmartCharPtr buf;
439  buf = GBS_global_string_copy("(%.3f,%.3f)", val1, val2);
440  return &*buf;
441  }
442 #endif
443 
444  // mixer:
445  ShadedValue reverse_mix(float other_ratio, const PlanarTuple& other) const OVERRIDE {
446  return new PlanarTuple(mix_floats(other.val1, other_ratio, val1),
447  mix_floats(other.val2, other_ratio, val2));
448  }
449  ShadedValue mix(float my_ratio, const ValueTuple& other) const OVERRIDE { return other.reverse_mix(my_ratio, *this); }
450 };
451 
452 class SpatialTuple FINAL_TYPE : public ValueTuple {
453  float val1, val2, val3;
454 
455 public:
456  SpatialTuple(float val1_, float val2_, float val3_) :
457  val1(val1_),
458  val2(val2_),
459  val3(val3_)
460  {
461  is_assert(!is_inf(val1));
462  is_assert(!is_inf(val2));
463  is_assert(!is_inf(val3));
464  is_assert(!(is_nan(val1) && is_nan(val2) && is_nan(val3))); // only NAN is unwanted
465  }
467 
468  bool is_defined() const OVERRIDE { return true; }
469  ShadedValue clone() const OVERRIDE { return new SpatialTuple(val1, val2, val3); }
470  int range_offset(const Phaser& phaser) const OVERRIDE { // returns int-offset into range [0 .. AW_RANGE_COLORS[
471  int c1 = is_nan(val1) ? 0 : fixed_range_offset<AW_SPATIAL_COLORS>(phaser.rephase(val1));
472  int c2 = is_nan(val2) ? 0 : fixed_range_offset<AW_SPATIAL_COLORS>(phaser.rephase(val2));
473  int c3 = is_nan(val3) ? 0 : fixed_range_offset<AW_SPATIAL_COLORS>(phaser.rephase(val3));
475  }
476 
477 #if defined(UNIT_TESTS)
478  const char *inspect() const OVERRIDE {
479  static SmartCharPtr buf;
480  buf = GBS_global_string_copy("(%.3f,%.3f,%.3f)", val1, val2, val3);
481  return &*buf;
482  }
483 #endif
484 
485  // mixer:
486  ShadedValue reverse_mix(float other_ratio, const SpatialTuple& other) const OVERRIDE {
487  return new SpatialTuple(mix_floats(other.val1, other_ratio, val1),
488  mix_floats(other.val2, other_ratio, val2),
489  mix_floats(other.val3, other_ratio, val3));
490  }
491  ShadedValue mix(float my_ratio, const ValueTuple& other) const OVERRIDE { return other.reverse_mix(my_ratio, *this); }
492 };
493 
494 
495 // ---------------------------------
496 // mixer (late definition)
497 
498 ShadedValue NoTuple::reverse_mix(float, const LinearTuple& other) const { return other.clone(); }
499 ShadedValue NoTuple::reverse_mix(float, const PlanarTuple& other) const { return other.clone(); }
500 ShadedValue NoTuple::reverse_mix(float, const SpatialTuple& other) const { return other.clone(); }
501 
502 // -----------------
503 // factory
504 
506  return new NoTuple;
507 }
509  return is_nan(f) ? undefined() : new LinearTuple(f);
510 }
511 ShadedValue ValueTuple::make(float f1, float f2) {
512  return (is_nan(f1) && is_nan(f2)) ? undefined() : new PlanarTuple(f1, f2);
513 }
514 ShadedValue ValueTuple::make(float f1, float f2, float f3) {
515  return (is_nan(f1) && is_nan(f2) && is_nan(f3)) ? undefined() : new SpatialTuple(f1, f2, f3);
516 }
517 
~SpatialTuple() OVERRIDE
Definition: ValueTuple.cxx:466
static ShadedValue make(float f)
Definition: ValueTuple.cxx:508
~LinearTuple() OVERRIDE
Definition: ValueTuple.cxx:385
CONSTEXPR_INLINE bool is_nan(const T &n)
Definition: arbtools.h:177
LinearTuple(float val_)
Definition: ValueTuple.cxx:382
ShadedValue clone() const OVERRIDE
Definition: ValueTuple.cxx:388
PlanarTuple(float val1_, float val2_)
Definition: ValueTuple.cxx:418
CONSTEXPR_INLINE bool is_nan_or_inf(const T &n)
Definition: arbtools.h:178
#define TEST_PUBLISH(testfunction)
Definition: test_unit.h:1517
#define AW_PLANAR_COLORS
#define TEST_EXPECT(cond)
Definition: test_unit.h:1328
CONSTEXPR_INLINE bool is_inf(const T &n)
Definition: arbtools.h:180
int range_offset(const Phaser &phaser) const OVERRIDE
Definition: ValueTuple.cxx:389
Generic smart pointer.
Definition: smartptr.h:149
#define TEST_REJECT(cond)
Definition: test_unit.h:1330
ShadedValue reverse_mix(float other_ratio, const PlanarTuple &other) const OVERRIDE
Definition: ValueTuple.cxx:445
~NoTuple() OVERRIDE
Definition: ValueTuple.cxx:361
ShadedValue reverse_mix(float other_ratio, const SpatialTuple &other) const OVERRIDE
Definition: ValueTuple.cxx:486
float mix_floats(float me, float my_ratio, float other)
Definition: ValueTuple.cxx:408
int CHECKED_RANGE_OFFSET(int off)
Definition: ValueTuple.cxx:315
xml element
#define AW_RANGE_COLORS
ShadedValue reverse_mix(float other_ratio, const LinearTuple &other) const OVERRIDE
Definition: ValueTuple.cxx:402
#define OVERRIDE
Definition: cxxforward.h:112
ShadedValue reverse_mix(float, const LinearTuple &other) const OVERRIDE
Definition: ValueTuple.cxx:498
int fixed_range_offset(float val)
Definition: ValueTuple.cxx:321
int range_offset(const Phaser &) const OVERRIDE
Definition: ValueTuple.cxx:365
ShadedValue mix(float, const ValueTuple &other) const OVERRIDE
Definition: ValueTuple.cxx:375
static ShadedValue undefined()
Definition: ValueTuple.cxx:505
ShadedValue clone() const OVERRIDE
Definition: ValueTuple.cxx:364
ShadedValue mix(float my_ratio, const ValueTuple &other) const OVERRIDE
Definition: ValueTuple.cxx:405
~PlanarTuple() OVERRIDE
Definition: ValueTuple.cxx:426
#define __ATTR__REDUCED_OPTIMIZE__NO_GCSE
Definition: test_unit.h:88
SpatialTuple(float val1_, float val2_, float val3_)
Definition: ValueTuple.cxx:456
bool is_defined() const OVERRIDE
Definition: ValueTuple.cxx:387
#define AW_SPATIAL_COLORS
bool is_defined() const OVERRIDE
Definition: ValueTuple.cxx:363
#define TEST_EXPECT_EQUAL(expr, want)
Definition: test_unit.h:1294
char * GBS_global_string_copy(const char *templat,...)
Definition: arb_msg.cxx:194
#define is_assert(cond)
Definition: item_shader.cxx:14