colourTools.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2019 OpenCFD Ltd.
9-------------------------------------------------------------------------------
10License
11 This file is part of OpenFOAM.
12
13 OpenFOAM is free software: you can redistribute it and/or modify it
14 under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25
26Note
27 Some implementation details from VTK vtkColorTransferFunction.cxx
28 vtkMath.cxx
29
30\*---------------------------------------------------------------------------*/
31
32#include "colourTools.H"
34
35using namespace Foam::constant;
36
37// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
38
39namespace Foam
40{
41
42static constexpr scalar oneThird = 1.0 / 3.0;
43static constexpr scalar oneSixth = 1.0 / 6.0;
44static constexpr scalar twoThird = 2.0 / 3.0;
45static constexpr scalar fiveSixth = 5.0 / 6.0;
46
47
48// Compute HSV from RGB
49static inline void RGB_to_HSV
50(
51 const scalar r, const scalar g, const scalar b,
52 scalar& h, scalar& s, scalar& v
53)
54{
55 scalar cmin = r, cmax = r;
56
57 if (g > cmax)
58 {
59 cmax = g;
60 }
61 else if (g < cmin)
62 {
63 cmin = g;
64 }
65 if (b > cmax)
66 {
67 cmax = b;
68 }
69 else if (b < cmin)
70 {
71 cmin = b;
72 }
73
74 v = cmax;
75 s = (v > 0.0) ? ((cmax - cmin) / cmax) : 0.0;
76
77 if (s > 0.0)
78 {
79 if (r == cmax)
80 {
81 h = oneSixth * (g - b) / (cmax - cmin);
82 }
83 else if (g == cmax)
84 {
85 h = oneThird + oneSixth * (b - r) / (cmax - cmin);
86 }
87 else
88 {
89 h = twoThird + oneSixth * (r - g) / (cmax - cmin);
90 }
91
92 if (h < 0.0)
93 {
94 h += 1.0;
95 }
96 }
97 else
98 {
99 h = 0.0;
100 }
101}
102
103
104// Compute HSV to RGB
105static inline void HSV_to_RGB
106(
107 const scalar h, const scalar s, const scalar v,
108 scalar& r, scalar& g, scalar& b
109)
110{
111 if (h > oneSixth && h <= oneThird)
112 {
113 // green/red
114 g = 1.0;
115 r = (oneThird - h) / oneSixth;
116 b = 0.0;
117 }
118 else if (h > oneThird && h <= 0.5)
119 {
120 // green/blue
121 g = 1.0;
122 b = (h - oneThird) / oneSixth;
123 r = 0.0;
124 }
125 else if (h > 0.5 && h <= twoThird)
126 {
127 // blue/green
128 b = 1.0;
129 g = (twoThird - h) / oneSixth;
130 r = 0.0;
131 }
132 else if (h > twoThird && h <= fiveSixth)
133 {
134 // blue/red
135 b = 1.0;
136 r = (h - twoThird) / oneSixth;
137 g = 0.0;
138 }
139 else if (h > fiveSixth && h <= 1.0)
140 {
141 // red/blue
142 r = 1.0;
143 b = (1.0 - h) / oneSixth;
144 g = 0.0;
145 }
146 else
147 {
148 // red/green
149 r = 1.0;
150 g = h / oneSixth;
151 b = 0.0;
152 }
153
154 // Add saturation
155 r = (s * r + (1.0 - s));
156 g = (s * g + (1.0 - s));
157 b = (s * b + (1.0 - s));
158
159 r *= v;
160 g *= v;
161 b *= v;
162}
163
164
165// Intermediate calculation to XYZ
166static inline scalar to_XYZ(scalar val)
167{
168 const scalar p3 = pow3(val);
169 return (p3 > 0.008856 ? p3 : (val - 16.0 / 116.0) / 7.787);
170}
171
172
173// Intermediate calculation from XYZ
174static inline scalar from_XYZ(scalar val)
175{
176 return (val > 0.008856) ? cbrt(val) : (7.787 * val) + (16.0 / 116.0);
177}
178
179// Observer= 2 deg Illuminant= D65
180static constexpr scalar ref_X = 0.9505;
181static constexpr scalar ref_Y = 1.000;
182static constexpr scalar ref_Z = 1.089;
183
184static inline void LAB_to_XYZ
185(
186 const scalar L, const scalar a, const scalar b,
187 scalar& x, scalar& y, scalar& z
188)
189{
190 const scalar var_Y = (L + 16.0) / 116.0;
191 const scalar var_X = a / 500 + var_Y;
192 const scalar var_Z = var_Y - b / 200;
193
194 x = ref_X * to_XYZ(var_X);
195 y = ref_Y * to_XYZ(var_Y);
196 z = ref_Z * to_XYZ(var_Z);
197}
198
199
200static inline void XYZ_to_LAB
201(
202 const scalar x, const scalar y, const scalar z,
203 scalar& L, scalar& a, scalar& b
204)
205{
206 const scalar var_X = from_XYZ(x / ref_X);
207 const scalar var_Y = from_XYZ(y / ref_Y);
208 const scalar var_Z = from_XYZ(z / ref_Z);
209
210 L = (116 * var_Y) - 16;
211 a = 500 * (var_X - var_Y);
212 b = 200 * (var_Y - var_Z);
213}
214
215
216
217// "Gamma correction" specified by the sRGB color space.
218
219static inline scalar gamma_from_xyz(const scalar val)
220{
221 return
222 (
223 val > 0.0031308
224 ? (1.055 * (pow(val, 1.0/2.4)) - 0.055)
225 : 12.92 * val
226 );
227}
228
229
230static inline scalar gamma_to_xyz(const scalar val)
231{
232 return
233 (
234 val > 0.04045
235 ? (pow((val + 0.055) / 1.055, 2.4))
236 : val / 12.92
237 );
238}
239
240
241
242static inline void XYZ_to_RGB
243(
244 const scalar x, const scalar y, const scalar z,
245 scalar& r, scalar& g, scalar& b
246)
247{
248 r = gamma_from_xyz(x * 3.2406 + y * -1.5372 + z * -0.4986);
249 g = gamma_from_xyz(x * -0.9689 + y * 1.8758 + z * 0.0415);
250 b = gamma_from_xyz(x * 0.0557 + y * -0.2040 + z * 1.0570);
251
252 // Clip colour range
253 scalar cmax = r;
254 if (cmax < g) cmax = g;
255 if (cmax < b) cmax = b;
256 if (cmax > 1.0)
257 {
258 r /= cmax;
259 g /= cmax;
260 b /= cmax;
261 }
262
263 if (r < 0) r = 0;
264 if (g < 0) g = 0;
265 if (b < 0) b = 0;
266}
267
268
269static inline void RGB_to_XYZ
270(
271 scalar r, scalar g, scalar b,
272 scalar& x, scalar& y, scalar& z
273)
274{
275 r = gamma_to_xyz(r);
276 g = gamma_to_xyz(g);
277 b = gamma_to_xyz(b);
278
279 x = r * 0.4124 + g * 0.3576 + b * 0.1805;
280 y = r * 0.2126 + g * 0.7152 + b * 0.0722;
281 z = r * 0.0193 + g * 0.1192 + b * 0.9505;
282}
283
284
285
286//- Convert to special polar version of CIELAB
287// (for creating continuous diverging color maps).
288inline void labToMsh(const vector& lab, vector& msh)
289{
290 const scalar& L = lab[0];
291 const scalar& a = lab[1];
292 const scalar& b = lab[2];
293
294 msh[0] = sqrt(L*L + a*a + b*b);
295 msh[1] = (msh[0] > 0.001) ? acos(L / msh[0]) : 0.0;
296 msh[2] = (msh[1] > 0.001) ? atan2(b,a) : 0.0;
297}
298
299
300//- Convert from special polar version of CIELAB
301inline void mshToLab(const vector& msh, vector& lab)
302{
303 lab[0] = msh[0]*cos(msh[1]);
304 lab[1] = msh[0]*sin(msh[1])*cos(msh[2]);
305 lab[2] = msh[0]*sin(msh[1])*sin(msh[2]);
306}
307
308
309// Return the smallest angle between the two
310static inline scalar angleDiff(scalar angle1, scalar angle2)
311{
312 scalar adiff = angle1 - angle1;
313 if (adiff < 0.0) adiff = -adiff;
314
315 while (adiff >= mathematical::twoPi) adiff -= mathematical::twoPi;
316 if (adiff > mathematical::pi) adiff = (mathematical::twoPi - adiff);
317 return adiff;
318}
319
320
321// For the case when interpolating from a saturated color to an unsaturated
322// color, find a hue for the unsaturated color that makes sense.
323static inline scalar adjustHue(const vector& msh, scalar unsatM)
324{
325 if (msh[0] >= unsatM - 0.1)
326 {
327 // The best we can do is hold hue constant.
328 return msh[2];
329 }
330
331 // This equation is designed to make the perceptual change of the
332 // interpolation to be close to constant.
333 const scalar hueSpin =
334 msh[1]*sqrt(unsatM*unsatM - msh[0]*msh[0]) / (msh[0]*sin(msh[1]));
335
336 // Spin hue away from 0 except in purple hues.
337 if (msh[2] > -0.3*mathematical::pi)
338 {
339 return msh[2] + hueSpin;
340 }
341 else
342 {
343 return msh[2] - hueSpin;
344 }
345}
346
347} // End namespace Foam
348
349
350// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
351
353{
354 RGB_to_HSV(rgb[0], rgb[1], rgb[2], hsv[0], hsv[1], hsv[2]);
355}
356
358{
359 HSV_to_RGB(hsv[0], hsv[1], hsv[2], rgb[0], rgb[1], rgb[2]);
360}
361
362
364{
365 RGB_to_XYZ(rgb[0], rgb[1], rgb[2], xyz[0], xyz[1], xyz[2]);
366}
367
369{
370 XYZ_to_RGB(xyz[0], xyz[1], xyz[2], rgb[0], rgb[1], rgb[2]);
371}
372
373
375{
376 LAB_to_XYZ(lab[0], lab[1], lab[2], xyz[0], xyz[1], xyz[2]);
377}
378
379
381{
382 XYZ_to_LAB(xyz[0], xyz[1], xyz[2], lab[0], lab[1], lab[2]);
383}
384
385
387{
388 vector xyz;
389 RGB_to_XYZ(rgb[0], rgb[1], rgb[2], xyz[0], xyz[1], xyz[2]);
390 XYZ_to_LAB(xyz[0], xyz[1], xyz[2], lab[0], lab[1], lab[2]);
391}
392
393
395{
396 vector xyz;
397 labToXyz(lab, xyz);
398 xyzToRgb(xyz, rgb);
399}
400
401
403(
404 scalar s,
405 const vector& rgb1,
406 const vector& rgb2,
407 vector& result
408)
409{
410 vector lab1, lab2;
411 rgbToLab(rgb1, lab1);
412 rgbToLab(rgb2, lab2);
413
414 vector msh1, msh2;
415 labToMsh(lab1, msh1);
416 labToMsh(lab2, msh2);
417
418 // If the endpoints are distinct saturated colors,
419 // then place white in between them.
420 if
421 (
422 msh1[1] > 0.05
423 && msh2[1] > 0.05
424 && angleDiff(msh1[2], msh2[2]) > mathematical::pi/3.0
425 )
426 {
427 // Insert the white midpoint by setting one end to white and
428 // adjusting the scalar value.
429
430 scalar Mmid = std::max(msh1[0], msh2[0]);
431 Mmid = std::max(scalar(88.0), Mmid);
432 if (s < 0.5)
433 {
434 msh2[0] = Mmid; msh2[1] = 0; msh2[2] = 0;
435 s = 2.0*s;
436 }
437 else
438 {
439 msh1[0] = Mmid; msh1[1] = 0; msh1[2] = 0;
440 s = 2.0*s - 1.0;
441 }
442 }
443
444 // If one color has no saturation, then its hue value is invalid.
445 // In this case, we want to set it to something logical so the
446 // interpolation of hue makes sense.
447 if ((msh1[1] < 0.05) && (msh2[1] > 0.05))
448 {
449 msh1[2] = adjustHue(msh2, msh1[0]);
450 }
451 else if ((msh2[1] < 0.05) && (msh1[1] > 0.05))
452 {
453 msh2[2] = adjustHue(msh1, msh2[0]);
454 }
455
456 // Msh tmp
457 vector mshTmp((1-s)*msh1 + s*msh2);
458
459 // Convert back to RGB
460 vector lab;
461 mshToLab(mshTmp, lab);
462 labToRgb(lab, result);
463}
464
465
467(
468 scalar s,
469 const vector& rgb1,
470 const vector& rgb2,
471 vector& result
472)
473{
474 vector hsv1, hsv2;
475 rgbToHsv(rgb1, hsv1);
476 rgbToHsv(rgb2, hsv2);
477
478 // Wrap HSV?
479 if (hsv1[0] - hsv2[0] > 0.5 || hsv2[0] - hsv1[0] > 0.5)
480 {
481 if (hsv1[0] > hsv2[0])
482 {
483 hsv1[0] -= 1.0;
484 }
485 else
486 {
487 hsv2[0] -= 1.0;
488 }
489 }
490
491 vector hsvTmp((1-s)*hsv1 + s*hsv2);
492
493 if (hsvTmp[0] < 0.0)
494 {
495 hsvTmp[0] += 1.0;
496 }
497
498 // Convert back to RGB
499 hsvToRgb(hsvTmp, result);
500}
501
502
503// ************************************************************************* //
scalar y
const uniformDimensionedVectorField & g
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
void xyzToRgb(const vector &xyz, vector &rgb)
Convert XYZ to RGB.
Definition: colourTools.C:368
void hsvToRgb(const vector &hsv, vector &rgb)
Convert HSV to RGB.
Definition: colourTools.C:357
void rgbToHsv(const vector &rgb, vector &hsv)
Convert RGB to HSV.
Definition: colourTools.C:352
void rgbToXyz(const vector &rgb, vector &xyz)
Convert RGB to XYZ.
Definition: colourTools.C:363
void rgbToLab(const vector &rgb, vector &lab)
Convert RGB to LAB.
Definition: colourTools.C:386
void labToXyz(const vector &lab, vector &xyz)
Convert LAB to XYZ.
Definition: colourTools.C:374
void labToRgb(const vector &lab, vector &rgb)
Convert LAB to RGB.
Definition: colourTools.C:394
void interpolateDiverging(scalar s, const vector &rgb1, const vector &rgb2, vector &result)
Interpolate RGB values with diverging color map.
Definition: colourTools.C:403
void xyzToLab(const vector &xyz, vector &lab)
Convert XYZ to LAB.
Definition: colourTools.C:380
void interpolateHSV(scalar s, const vector &rgb1, const vector &rgb2, vector &result)
Interpolate RGB values in HSV colourspace.
Definition: colourTools.C:467
constexpr scalar pi(M_PI)
constexpr scalar twoPi(2 *M_PI)
Different types of constants.
Namespace for OpenFOAM.
static void XYZ_to_LAB(const scalar x, const scalar y, const scalar z, scalar &L, scalar &a, scalar &b)
Definition: colourTools.C:201
static constexpr scalar ref_X
Definition: colourTools.C:180
static constexpr scalar oneThird
Definition: colourTools.C:42
static void RGB_to_XYZ(scalar r, scalar g, scalar b, scalar &x, scalar &y, scalar &z)
Definition: colourTools.C:270
dimensionedScalar pow3(const dimensionedScalar &ds)
dimensionedScalar sin(const dimensionedScalar &ds)
void labToMsh(const vector &lab, vector &msh)
Convert to special polar version of CIELAB.
Definition: colourTools.C:288
static void LAB_to_XYZ(const scalar L, const scalar a, const scalar b, scalar &x, scalar &y, scalar &z)
Definition: colourTools.C:185
static void RGB_to_HSV(const scalar r, const scalar g, const scalar b, scalar &h, scalar &s, scalar &v)
Definition: colourTools.C:50
static constexpr scalar ref_Z
Definition: colourTools.C:182
static scalar gamma_from_xyz(const scalar val)
Definition: colourTools.C:219
static constexpr scalar oneSixth
Definition: colourTools.C:43
static void HSV_to_RGB(const scalar h, const scalar s, const scalar v, scalar &r, scalar &g, scalar &b)
Definition: colourTools.C:106
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
dimensionedScalar atan2(const dimensionedScalar &x, const dimensionedScalar &y)
static constexpr scalar twoThird
Definition: colourTools.C:44
static scalar from_XYZ(scalar val)
Definition: colourTools.C:174
dimensionedScalar sqrt(const dimensionedScalar &ds)
static constexpr scalar fiveSixth
Definition: colourTools.C:45
static constexpr scalar ref_Y
Definition: colourTools.C:181
static scalar angleDiff(scalar angle1, scalar angle2)
Definition: colourTools.C:310
void mshToLab(const vector &msh, vector &lab)
Convert from special polar version of CIELAB.
Definition: colourTools.C:301
static void XYZ_to_RGB(const scalar x, const scalar y, const scalar z, scalar &r, scalar &g, scalar &b)
Definition: colourTools.C:243
static scalar to_XYZ(scalar val)
Definition: colourTools.C:166
dimensionedScalar cbrt(const dimensionedScalar &ds)
static scalar gamma_to_xyz(const scalar val)
Definition: colourTools.C:230
dimensionedScalar cos(const dimensionedScalar &ds)
dimensionedScalar acos(const dimensionedScalar &ds)
static scalar adjustHue(const vector &msh, scalar unsatM)
Definition: colourTools.C:323
volScalarField & h
volScalarField & b
Definition: createFields.H:27
const vector L(dict.get< vector >("L"))