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