source: trunk/SL/ITEM_SHADER/ValueTuple.cxx

Last change on this file was 18647, checked in by westram, 4 years ago
  • reduced optimisation for TEST_remapping() and TEST_shaded_values().
File size: 23.0 KB
Line 
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
24    ShadedValue undef = ValueTuple::undefined();
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
172void 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
297        TEST_EXPECT_EQUAL(px->range_offset(dontPhase), (AW_SPATIAL_COLORS-1)*AW_SPATIAL_COLORS*AW_SPATIAL_COLORS);
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}
309TEST_PUBLISH(TEST_value_color_mapping);
310
311#endif // UNIT_TESTS
312
313// --------------------------------------------------------------------------------
314
315inline int CHECKED_RANGE_OFFSET(int off) {
316    is_assert(off>=0 && off<AW_RANGE_COLORS);
317    return off;
318}
319
320template<int RANGE_SIZE>
321inline 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
335void 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
358class NoTuple: public ValueTuple {
359public:
360    NoTuple() {}
361    ~NoTuple() OVERRIDE {}
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
378class LinearTuple FINAL_TYPE : public ValueTuple {
379    float val;
380
381public:
382    LinearTuple(float val_) : val(val_) {
383        is_assert(!is_nan_or_inf(val));
384    }
385    ~LinearTuple() OVERRIDE {}
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
408inline 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
414class PlanarTuple FINAL_TYPE : public ValueTuple {
415    float val1, val2;
416
417public:
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    }
426    ~PlanarTuple() OVERRIDE {}
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
452class SpatialTuple FINAL_TYPE : public ValueTuple {
453    float val1, val2, val3;
454
455public:
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    }
466    ~SpatialTuple() OVERRIDE {}
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));
474        return CHECKED_RANGE_OFFSET((c1*AW_SPATIAL_COLORS + c2)*AW_SPATIAL_COLORS + c3);
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
498ShadedValue NoTuple::reverse_mix(float, const LinearTuple&  other) const { return other.clone(); }
499ShadedValue NoTuple::reverse_mix(float, const PlanarTuple&  other) const { return other.clone(); }
500ShadedValue NoTuple::reverse_mix(float, const SpatialTuple& other) const { return other.clone(); }
501
502// -----------------
503//      factory
504
505ShadedValue ValueTuple::undefined() {
506    return new NoTuple;
507}
508ShadedValue ValueTuple::make(float f) {
509    return is_nan(f) ? undefined() : new LinearTuple(f);
510}
511ShadedValue ValueTuple::make(float f1, float f2) {
512    return (is_nan(f1) && is_nan(f2)) ? undefined() : new PlanarTuple(f1, f2);
513}
514ShadedValue 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
Note: See TracBrowser for help on using the repository browser.