xref: /petsc/src/sys/classes/draw/utils/cmap.c (revision 9371c9d470a9602b6d10a8bf50c9b2280a79e45a)
100d931feSLisandro Dalcin #include <petscsys.h> /*I "petscsys.h" I*/
200d931feSLisandro Dalcin #include <petscdraw.h>
300d931feSLisandro Dalcin 
400d931feSLisandro Dalcin /*
500d931feSLisandro Dalcin     Set up a color map, using uniform separation in hue space.
600d931feSLisandro Dalcin     Map entries are Red, Green, Blue.
700d931feSLisandro Dalcin     Values are "gamma" corrected.
800d931feSLisandro Dalcin  */
900d931feSLisandro Dalcin 
1000d931feSLisandro Dalcin /*
1100d931feSLisandro Dalcin    Gamma is a monitor dependent value.  The value here is an
1200d931feSLisandro Dalcin    approximate that gives somewhat better results than Gamma = 1.
1300d931feSLisandro Dalcin  */
1400d931feSLisandro Dalcin static PetscReal Gamma = 2.0;
1500d931feSLisandro Dalcin 
16*9371c9d4SSatish Balay PetscErrorCode PetscDrawUtilitySetGamma(PetscReal g) {
1700d931feSLisandro Dalcin   PetscFunctionBegin;
1800d931feSLisandro Dalcin   Gamma = g;
1900d931feSLisandro Dalcin   PetscFunctionReturn(0);
2000d931feSLisandro Dalcin }
2100d931feSLisandro Dalcin 
22*9371c9d4SSatish Balay static inline double PetscHlsHelper(double m1, double m2, double h) {
2383b97956SLisandro Dalcin   while (h > 1.0) h -= 1.0;
2483b97956SLisandro Dalcin   while (h < 0.0) h += 1.0;
2583b97956SLisandro Dalcin   if (h < 1 / 6.0) return m1 + (m2 - m1) * h * 6;
2683b97956SLisandro Dalcin   if (h < 1 / 2.0) return m2;
2783b97956SLisandro Dalcin   if (h < 2 / 3.0) return m1 + (m2 - m1) * (2 / 3.0 - h) * 6;
2800d931feSLisandro Dalcin   return m1;
2900d931feSLisandro Dalcin }
3000d931feSLisandro Dalcin 
31*9371c9d4SSatish Balay static inline void PetscHlsToRgb(double h, double l, double s, double *r, double *g, double *b) {
3283b97956SLisandro Dalcin   if (s > 0.0) {
3383b97956SLisandro Dalcin     double m2 = l <= 0.5 ? l * (1.0 + s) : l + s - (l * s);
3483b97956SLisandro Dalcin     double m1 = 2 * l - m2;
3583b97956SLisandro Dalcin     *r        = PetscHlsHelper(m1, m2, h + 1 / 3.);
3683b97956SLisandro Dalcin     *g        = PetscHlsHelper(m1, m2, h);
3783b97956SLisandro Dalcin     *b        = PetscHlsHelper(m1, m2, h - 1 / 3.);
3800d931feSLisandro Dalcin   } else {
3983b97956SLisandro Dalcin     /* ignore hue */
4083b97956SLisandro Dalcin     *r = *g = *b = l;
4100d931feSLisandro Dalcin   }
4200d931feSLisandro Dalcin }
4300d931feSLisandro Dalcin 
44*9371c9d4SSatish Balay static inline void PetscGammaCorrect(double *r, double *g, double *b) {
4583b97956SLisandro Dalcin   PetscReal igamma = 1 / Gamma;
4683b97956SLisandro Dalcin   *r               = (double)PetscPowReal((PetscReal)*r, igamma);
4783b97956SLisandro Dalcin   *g               = (double)PetscPowReal((PetscReal)*g, igamma);
4883b97956SLisandro Dalcin   *b               = (double)PetscPowReal((PetscReal)*b, igamma);
4983b97956SLisandro Dalcin }
5083b97956SLisandro Dalcin 
51*9371c9d4SSatish Balay static PetscErrorCode PetscDrawCmap_Hue(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[]) {
5283b97956SLisandro Dalcin   int    i;
5383b97956SLisandro Dalcin   double maxhue = 212.0 / 360, lightness = 0.5, saturation = 1.0;
5400d931feSLisandro Dalcin 
5500d931feSLisandro Dalcin   PetscFunctionBegin;
5600d931feSLisandro Dalcin   for (i = 0; i < mapsize; i++) {
5783b97956SLisandro Dalcin     double hue = maxhue * (double)i / (mapsize - 1), r, g, b;
5883b97956SLisandro Dalcin     PetscHlsToRgb(hue, lightness, saturation, &r, &g, &b);
5983b97956SLisandro Dalcin     PetscGammaCorrect(&r, &g, &b);
6083b97956SLisandro Dalcin     R[i] = (unsigned char)(255 * PetscMin(r, 1.0));
6183b97956SLisandro Dalcin     G[i] = (unsigned char)(255 * PetscMin(g, 1.0));
6283b97956SLisandro Dalcin     B[i] = (unsigned char)(255 * PetscMin(b, 1.0));
6300d931feSLisandro Dalcin   }
6400d931feSLisandro Dalcin   PetscFunctionReturn(0);
6500d931feSLisandro Dalcin }
6600d931feSLisandro Dalcin 
67*9371c9d4SSatish Balay static PetscErrorCode PetscDrawCmap_Gray(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[]) {
6800d931feSLisandro Dalcin   int i;
6900d931feSLisandro Dalcin   PetscFunctionBegin;
7000d931feSLisandro Dalcin   for (i = 0; i < mapsize; i++) R[i] = G[i] = B[i] = (unsigned char)((255.0 * i) / (mapsize - 1));
7100d931feSLisandro Dalcin   PetscFunctionReturn(0);
7200d931feSLisandro Dalcin }
7300d931feSLisandro Dalcin 
74*9371c9d4SSatish Balay static PetscErrorCode PetscDrawCmap_Jet(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[]) {
7500d931feSLisandro Dalcin   int          i;
7600d931feSLisandro Dalcin   const double knots[] = {0, 1 / 8., 3 / 8., 5 / 8., 7 / 8., 1};
7700d931feSLisandro Dalcin 
7800d931feSLisandro Dalcin   PetscFunctionBegin;
7900d931feSLisandro Dalcin   for (i = 0; i < mapsize; i++) {
8000d931feSLisandro Dalcin     double u = (double)i / (mapsize - 1);
81*9371c9d4SSatish Balay     double m, r = 0, g = 0, b = 0;
82*9371c9d4SSatish Balay     int    k = 0;
8300d931feSLisandro Dalcin     while (k < 4 && u > knots[k + 1]) k++;
8400d931feSLisandro Dalcin     m = (u - knots[k]) / (knots[k + 1] - knots[k]);
8500d931feSLisandro Dalcin     switch (k) {
86*9371c9d4SSatish Balay     case 0:
87*9371c9d4SSatish Balay       r = 0;
88*9371c9d4SSatish Balay       g = 0;
89*9371c9d4SSatish Balay       b = (m + 1) / 2;
90*9371c9d4SSatish Balay       break;
91*9371c9d4SSatish Balay     case 1:
92*9371c9d4SSatish Balay       r = 0;
93*9371c9d4SSatish Balay       g = m;
94*9371c9d4SSatish Balay       b = 1;
95*9371c9d4SSatish Balay       break;
96*9371c9d4SSatish Balay     case 2:
97*9371c9d4SSatish Balay       r = m;
98*9371c9d4SSatish Balay       g = 1;
99*9371c9d4SSatish Balay       b = 1 - m;
100*9371c9d4SSatish Balay       break;
101*9371c9d4SSatish Balay     case 3:
102*9371c9d4SSatish Balay       r = 1;
103*9371c9d4SSatish Balay       g = 1 - m;
104*9371c9d4SSatish Balay       b = 0;
105*9371c9d4SSatish Balay       break;
106*9371c9d4SSatish Balay     case 4:
107*9371c9d4SSatish Balay       r = 1 - m / 2;
108*9371c9d4SSatish Balay       g = 0;
109*9371c9d4SSatish Balay       b = 0;
110*9371c9d4SSatish Balay       break;
11100d931feSLisandro Dalcin     }
11200d931feSLisandro Dalcin     R[i] = (unsigned char)(255 * PetscMin(r, 1.0));
11300d931feSLisandro Dalcin     G[i] = (unsigned char)(255 * PetscMin(g, 1.0));
11400d931feSLisandro Dalcin     B[i] = (unsigned char)(255 * PetscMin(b, 1.0));
11500d931feSLisandro Dalcin   }
11600d931feSLisandro Dalcin   PetscFunctionReturn(0);
11700d931feSLisandro Dalcin }
11800d931feSLisandro Dalcin 
119*9371c9d4SSatish Balay static PetscErrorCode PetscDrawCmap_Hot(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[]) {
12000d931feSLisandro Dalcin   int          i;
12100d931feSLisandro Dalcin   const double knots[] = {0, 3 / 8., 3 / 4., 1};
12200d931feSLisandro Dalcin 
12300d931feSLisandro Dalcin   PetscFunctionBegin;
12400d931feSLisandro Dalcin   for (i = 0; i < mapsize; i++) {
12500d931feSLisandro Dalcin     double u = (double)i / (mapsize - 1);
126*9371c9d4SSatish Balay     double m, r = 0, g = 0, b = 0;
127*9371c9d4SSatish Balay     int    k = 0;
12800d931feSLisandro Dalcin     while (k < 2 && u > knots[k + 1]) k++;
12900d931feSLisandro Dalcin     m = (u - knots[k]) / (knots[k + 1] - knots[k]);
13000d931feSLisandro Dalcin     switch (k) {
131*9371c9d4SSatish Balay     case 0:
132*9371c9d4SSatish Balay       r = m;
133*9371c9d4SSatish Balay       g = 0;
134*9371c9d4SSatish Balay       b = 0;
135*9371c9d4SSatish Balay       break;
136*9371c9d4SSatish Balay     case 1:
137*9371c9d4SSatish Balay       r = 1;
138*9371c9d4SSatish Balay       g = m;
139*9371c9d4SSatish Balay       b = 0;
140*9371c9d4SSatish Balay       break;
141*9371c9d4SSatish Balay     case 2:
142*9371c9d4SSatish Balay       r = 1;
143*9371c9d4SSatish Balay       g = 1;
144*9371c9d4SSatish Balay       b = m;
145*9371c9d4SSatish Balay       break;
14600d931feSLisandro Dalcin     }
14700d931feSLisandro Dalcin     R[i] = (unsigned char)(255 * PetscMin(r, 1.0));
14800d931feSLisandro Dalcin     G[i] = (unsigned char)(255 * PetscMin(g, 1.0));
14900d931feSLisandro Dalcin     B[i] = (unsigned char)(255 * PetscMin(b, 1.0));
15000d931feSLisandro Dalcin   }
15100d931feSLisandro Dalcin   PetscFunctionReturn(0);
15200d931feSLisandro Dalcin }
15300d931feSLisandro Dalcin 
154*9371c9d4SSatish Balay static PetscErrorCode PetscDrawCmap_Bone(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[]) {
15500d931feSLisandro Dalcin   int i;
15600d931feSLisandro Dalcin   PetscFunctionBegin;
15700d931feSLisandro Dalcin   (void)PetscDrawCmap_Hot(mapsize, R, G, B);
15800d931feSLisandro Dalcin   for (i = 0; i < mapsize; i++) {
15900d931feSLisandro Dalcin     double u = (double)i / (mapsize - 1);
16000d931feSLisandro Dalcin     double r = (7 * u + B[i] / 255.0) / 8;
16100d931feSLisandro Dalcin     double g = (7 * u + G[i] / 255.0) / 8;
16200d931feSLisandro Dalcin     double b = (7 * u + R[i] / 255.0) / 8;
16300d931feSLisandro Dalcin     R[i]     = (unsigned char)(255 * PetscMin(r, 1.0));
16400d931feSLisandro Dalcin     G[i]     = (unsigned char)(255 * PetscMin(g, 1.0));
16500d931feSLisandro Dalcin     B[i]     = (unsigned char)(255 * PetscMin(b, 1.0));
16600d931feSLisandro Dalcin   }
16700d931feSLisandro Dalcin   PetscFunctionReturn(0);
16800d931feSLisandro Dalcin }
16900d931feSLisandro Dalcin 
170575a0592SBarry Smith #include "../src/sys/classes/draw/utils/cmap/coolwarm.h"
171575a0592SBarry Smith #include "../src/sys/classes/draw/utils/cmap/parula.h"
172575a0592SBarry Smith #include "../src/sys/classes/draw/utils/cmap/viridis.h"
173575a0592SBarry Smith #include "../src/sys/classes/draw/utils/cmap/plasma.h"
174575a0592SBarry Smith #include "../src/sys/classes/draw/utils/cmap/inferno.h"
175575a0592SBarry Smith #include "../src/sys/classes/draw/utils/cmap/magma.h"
17600d931feSLisandro Dalcin 
17700d931feSLisandro Dalcin static struct {
17800d931feSLisandro Dalcin   const char *name;
17900d931feSLisandro Dalcin   const unsigned char (*data)[3];
18000d931feSLisandro Dalcin   PetscErrorCode (*cmap)(int, unsigned char[], unsigned char[], unsigned char[]);
18100d931feSLisandro Dalcin } PetscDrawCmapTable[] = {
18200d931feSLisandro Dalcin   {"hue",      NULL,                   PetscDrawCmap_Hue }, /* varying hue with constant lightness and saturation */
18300d931feSLisandro Dalcin   {"gray",     NULL,                   PetscDrawCmap_Gray}, /* black to white with shades of gray */
18400d931feSLisandro Dalcin   {"bone",     NULL,                   PetscDrawCmap_Bone}, /* black to white with gray-blue shades */
18500d931feSLisandro Dalcin   {"jet",      NULL,                   PetscDrawCmap_Jet }, /* rainbow-like colormap from NCSA, University of Illinois */
18600d931feSLisandro Dalcin   {"hot",      NULL,                   PetscDrawCmap_Hot }, /* black-body radiation */
18700d931feSLisandro Dalcin   {"coolwarm", PetscDrawCmap_coolwarm, NULL              }, /* ParaView default (Cool To Warm with Diverging interpolation) */
18800d931feSLisandro Dalcin   {"parula",   PetscDrawCmap_parula,   NULL              }, /* MATLAB (default since R2014b) */
18900d931feSLisandro Dalcin   {"viridis",  PetscDrawCmap_viridis,  NULL              }, /* matplotlib 1.5 (default since 2.0) */
19000d931feSLisandro Dalcin   {"plasma",   PetscDrawCmap_plasma,   NULL              }, /* matplotlib 1.5 */
19100d931feSLisandro Dalcin   {"inferno",  PetscDrawCmap_inferno,  NULL              }, /* matplotlib 1.5 */
19200d931feSLisandro Dalcin   {"magma",    PetscDrawCmap_magma,    NULL              }, /* matplotlib 1.5 */
19300d931feSLisandro Dalcin };
19400d931feSLisandro Dalcin 
195*9371c9d4SSatish Balay PetscErrorCode PetscDrawUtilitySetCmap(const char colormap[], int mapsize, unsigned char R[], unsigned char G[], unsigned char B[]) {
19600d931feSLisandro Dalcin   int         i, j;
197dd39110bSPierre Jolivet   const char *cmap_name_list[PETSC_STATIC_ARRAY_LENGTH(PetscDrawCmapTable)];
19800d931feSLisandro Dalcin   PetscInt    id = 0, count = (PetscInt)(sizeof(cmap_name_list) / sizeof(char *));
19900d931feSLisandro Dalcin   PetscBool   reverse = PETSC_FALSE, brighten = PETSC_FALSE;
20000d931feSLisandro Dalcin   PetscReal   beta = 0;
20100d931feSLisandro Dalcin 
20200d931feSLisandro Dalcin   PetscFunctionBegin;
20300d931feSLisandro Dalcin   for (i = 0; i < count; i++) cmap_name_list[i] = PetscDrawCmapTable[i].name;
20400d931feSLisandro Dalcin   if (colormap && colormap[0]) {
20500d931feSLisandro Dalcin     PetscBool match = PETSC_FALSE;
2069566063dSJacob Faibussowitsch     for (id = 0; !match && id < count; id++) PetscCall(PetscStrcasecmp(colormap, cmap_name_list[id], &match));
20728b400f6SJacob Faibussowitsch     PetscCheck(match, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Colormap '%s' not found", colormap);
20800d931feSLisandro Dalcin   }
2099566063dSJacob Faibussowitsch   PetscCall(PetscOptionsGetEList(NULL, NULL, "-draw_cmap", cmap_name_list, count, &id, NULL));
2109566063dSJacob Faibussowitsch   PetscCall(PetscOptionsGetBool(NULL, NULL, "-draw_cmap_reverse", &reverse, NULL));
2119566063dSJacob Faibussowitsch   PetscCall(PetscOptionsGetReal(NULL, NULL, "-draw_cmap_brighten", &beta, &brighten));
212cc73adaaSBarry Smith   PetscCheck(!brighten || (beta > (PetscReal)-1 && beta < (PetscReal) + 1), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "brighten parameter %g must be in the range (-1,1)", (double)beta);
21300d931feSLisandro Dalcin 
21400d931feSLisandro Dalcin   if (PetscDrawCmapTable[id].cmap) {
2159566063dSJacob Faibussowitsch     PetscCall(PetscDrawCmapTable[id].cmap(mapsize, R, G, B));
21600d931feSLisandro Dalcin   } else {
21700d931feSLisandro Dalcin     const unsigned char(*rgb)[3] = PetscDrawCmapTable[id].data;
21808401ef6SPierre Jolivet     PetscCheck(mapsize == 256 - PETSC_DRAW_BASIC_COLORS, PETSC_COMM_SELF, PETSC_ERR_SUP, "Colormap '%s' with size %d not supported", cmap_name_list[id], mapsize);
219*9371c9d4SSatish Balay     for (i = 0; i < mapsize; i++) {
220*9371c9d4SSatish Balay       R[i] = rgb[i][0];
221*9371c9d4SSatish Balay       G[i] = rgb[i][1];
222*9371c9d4SSatish Balay       B[i] = rgb[i][2];
223*9371c9d4SSatish Balay     }
22400d931feSLisandro Dalcin   }
22500d931feSLisandro Dalcin 
22600d931feSLisandro Dalcin   if (reverse) {
227*9371c9d4SSatish Balay     i = 0;
228*9371c9d4SSatish Balay     j = mapsize - 1;
22900d931feSLisandro Dalcin     while (i < j) {
230*9371c9d4SSatish Balay #define SWAP(a, i, j) \
231*9371c9d4SSatish Balay   do { \
232*9371c9d4SSatish Balay     unsigned char t = a[i]; \
233*9371c9d4SSatish Balay     a[i]            = a[j]; \
234*9371c9d4SSatish Balay     a[j]            = t; \
235*9371c9d4SSatish Balay   } while (0)
23600d931feSLisandro Dalcin       SWAP(R, i, j);
23700d931feSLisandro Dalcin       SWAP(G, i, j);
23800d931feSLisandro Dalcin       SWAP(B, i, j);
23900d931feSLisandro Dalcin #undef SWAP
240*9371c9d4SSatish Balay       i++;
241*9371c9d4SSatish Balay       j--;
24200d931feSLisandro Dalcin     }
24300d931feSLisandro Dalcin   }
24400d931feSLisandro Dalcin 
24500d931feSLisandro Dalcin   if (brighten) {
24600d931feSLisandro Dalcin     PetscReal gamma = (beta > 0.0) ? (1 - beta) : (1 / (1 + beta));
24700d931feSLisandro Dalcin     for (i = 0; i < mapsize; i++) {
24800d931feSLisandro Dalcin       PetscReal r = PetscPowReal((PetscReal)R[i] / 255, gamma);
24900d931feSLisandro Dalcin       PetscReal g = PetscPowReal((PetscReal)G[i] / 255, gamma);
25000d931feSLisandro Dalcin       PetscReal b = PetscPowReal((PetscReal)B[i] / 255, gamma);
25100d931feSLisandro Dalcin       R[i]        = (unsigned char)(255 * PetscMin(r, (PetscReal)1.0));
25200d931feSLisandro Dalcin       G[i]        = (unsigned char)(255 * PetscMin(g, (PetscReal)1.0));
25300d931feSLisandro Dalcin       B[i]        = (unsigned char)(255 * PetscMin(b, (PetscReal)1.0));
25400d931feSLisandro Dalcin     }
25500d931feSLisandro Dalcin   }
25600d931feSLisandro Dalcin   PetscFunctionReturn(0);
25700d931feSLisandro Dalcin }
258