Add physically plausible Black Body color maps.
[gencolormap.git] / cmdline.cpp
1 /*
2  * Copyright (C) 2015, 2016 Computer Graphics Group, University of Siegen
3  * Written by Martin Lambers <martin.lambers@uni-siegen.de>
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to deal
7  * in the Software without restriction, including without limitation the rights
8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  * copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23
24 #include <vector>
25 #include <cstdio>
26 #include <cstdlib>
27 #include <cstring>
28 #include <cmath>
29
30 #include <getopt.h>
31 extern char *optarg;
32 extern int optind;
33
34 #include "colormap.hpp"
35
36 enum type {
37     brewer_seq = 0,
38     brewer_div = 1,
39     brewer_qual = 2,
40     isolum_seq = 3,
41     isolum_div = 4,
42     isolum_qual = 5,
43     blackbody = 6,
44     cubehelix = 7,
45     moreland = 8,
46     mcnames = 9
47 };
48
49 int main(int argc, char* argv[])
50 {
51     bool print_version = false;
52     bool print_help = false;
53     int type = -1;
54     int n = -1;
55     float hue = -1.0f;
56     float divergence = -1.0f;
57     float contrast = -1.0f;
58     float saturation = -1.0f;
59     float brightness = -1.0f;
60     float warmth = -1.0f;
61     float luminance = -1.0f;
62     float temperature = -1.0f;
63     float range = -1.0f;
64     float rotations = NAN;
65     float gamma = -1.0f;
66     bool have_color0 = false;
67     unsigned char color0[3];
68     bool have_color1 = false;
69     unsigned char color1[3];
70     float periods = NAN;
71     struct option options[] = {
72         { "version",     no_argument,       0, 'v' },
73         { "help",        no_argument,       0, 'H' },
74         { "type",        required_argument, 0, 't' },
75         { "n",           required_argument, 0, 'n' },
76         { "hue",         required_argument, 0, 'h' },
77         { "divergence",  required_argument, 0, 'd' },
78         { "contrast",    required_argument, 0, 'c' },
79         { "saturation",  required_argument, 0, 's' },
80         { "brightness",  required_argument, 0, 'b' },
81         { "warmth",      required_argument, 0, 'w' },
82         { "luminance",   required_argument, 0, 'l' },
83         { "temperature", required_argument, 0, 'T' },
84         { "range",       required_argument, 0, 'R' },
85         { "rotations",   required_argument, 0, 'r' },
86         { "gamma",       required_argument, 0, 'g' },
87         { "color0",      required_argument, 0, 'A' },
88         { "color1",      required_argument, 0, 'O' },
89         { "periods",     required_argument, 0, 'p' },
90         { 0, 0, 0, 0 }
91     };
92
93     for (;;) {
94         int c = getopt_long(argc, argv, "vHt:n:h:d:c:s:b:w:l:T:R:r:g:A:O:p:", options, NULL);
95         if (c == -1)
96             break;
97         switch (c) {
98         case 'v':
99             print_version = true;
100             break;
101         case 'H':
102             print_help = true;
103             break;
104         case 't':
105             type = (strcmp(optarg, "brewer-sequential") == 0 ? brewer_seq
106                     : strcmp(optarg, "brewer-diverging") == 0 ? brewer_div
107                     : strcmp(optarg, "brewer-qualitative") == 0 ? brewer_qual
108                     : strcmp(optarg, "isoluminant-sequential") == 0 ? isolum_seq
109                     : strcmp(optarg, "isoluminant-divergent") == 0 ? isolum_div
110                     : strcmp(optarg, "isoluminant-qualitative") == 0 ? isolum_qual
111                     : strcmp(optarg, "blackbody") == 0 ? blackbody
112                     : strcmp(optarg, "cubehelix") == 0 ? cubehelix
113                     : strcmp(optarg, "moreland") == 0 ? moreland
114                     : strcmp(optarg, "mcnames") == 0 ? mcnames
115                     : -2);
116             break;
117         case 'n':
118             n = atoi(optarg);
119             break;
120         case 'h':
121             hue = atof(optarg) * M_PI / 180.0;
122             break;
123         case 'd':
124             divergence = atof(optarg) * M_PI / 180.0;
125             break;
126         case 'c':
127             contrast = atof(optarg);
128             break;
129         case 's':
130             saturation = atof(optarg);
131             break;
132         case 'b':
133             brightness = atof(optarg);
134             break;
135         case 'w':
136             warmth = atof(optarg);
137             break;
138         case 'l':
139             luminance = atof(optarg);
140             break;
141         case 'T':
142             temperature = atof(optarg);
143             break;
144         case 'R':
145             range = atof(optarg);
146             break;
147         case 'r':
148             rotations = atof(optarg);
149             break;
150         case 'g':
151             gamma = atof(optarg);
152             break;
153         case 'A':
154             std::sscanf(optarg, "%hhu,%hhu,%hhu", color0 + 0, color0 + 1, color0 + 2);
155             have_color0 = true;
156             break;
157         case 'O':
158             std::sscanf(optarg, "%hhu,%hhu,%hhu", color1 + 0, color1 + 1, color1 + 2);
159             have_color1 = true;
160             break;
161         case 'p':
162             periods = atof(optarg);
163             break;
164         default:
165             return 1;
166         }
167     }
168
169     if (print_version) {
170         printf("gencolormap version 0.3\n"
171                 "Copyright (C) 2016 Computer Graphics Group, University of Siegen.\n"
172                 "Written by Martin Lambers <martin.lambers@uni-siegen.de>.\n"
173                 "This is free software under the terms of the MIT/Expat License.\n"
174                 "There is NO WARRANTY, to the extent permitted by law.\n");
175         return 0;
176     }
177
178     if (print_help) {
179         printf("Usage: %s\n"
180                 "  Common options, required for all types:\n"
181                 "    -n|--n=N                         Set number of colors in the map\n"
182                 "  Brewer-like color maps:\n"
183                 "    -t|--type=brewer-sequential       Generate a sequential color map\n"
184                 "    -t|--type=brewer-diverging        Generate a diverging color map\n"
185                 "    -t|--type=brewer-qualitative      Generate a qualitative color map\n"
186                 "    [-h|--hue=H]                      Set default hue in [0,360] degrees\n"
187                 "    [-c|--contrast=C]                 Set contrast in [0,1]\n"
188                 "    [-s|--saturation=S]               Set saturation in [0,1]\n"
189                 "    [-b|--brightness=B]               Set brightness in [0,1]\n"
190                 "    [-w|--warmth=W]                   Set warmth in [0,1] for seq. and div. maps\n"
191                 "    [-d|--divergence=D]               Set divergence in deg. for div. and qual. maps\n"
192                 "  Isoluminant color maps:\n"
193                 "    -t|--type=isoluminant-sequential  Generate a sequential color map\n"
194                 "    -t|--type=isoluminant-diverging   Generate a diverging color map\n"
195                 "    -t|--type=isoluminant-qualitative Generate a qualitative color map\n"
196                 "    [-l|--luminance=L]                Set luminance in [0,1]\n"
197                 "    [-s|--saturation=S]               Set saturation in [0,1]\n"
198                 "    [-h|--hue=H]                      Set default hue in [0,360] degrees\n"
199                 "    [-d|--divergence=D]               Set divergence in deg. for div. and qual. maps\n"
200                 "  Black Body color maps:\n"
201                 "    -t|--type=blackbody               Generate a Black Body color map\n"
202                 "    [-T|--temperature=T]              Start temperature of the map in Kelvin\n"
203                 "    [-R|--range=R]                    Range of temperatures of the map in Kelvin\n"
204                 "  CubeHelix color maps:\n"
205                 "    -t|--type=cubehelix               Generate a CubeHelix color map\n"
206                 "    [-r|--rotations=R]                Set number of rotations, in (-infty,infty)\n"
207                 "    [-s|--saturation=S]               Set saturation, in [0,1]\n"
208                 "    [-g|--gamma=G]                    Set gamma correction, in (0,infty)\n"
209                 "  Moreland diverging color maps:\n"
210                 "    -t|--type=moreland                Generate a Moreland diverging color map\n"
211                 "    [-A|--color0=sr,sg,sb             Set the first color as sRGB values in [0,255]\n"
212                 "    [-O|--color1=sr,sg,sb             Set the last color as sRGB values in [0,255]\n"
213                 "  McNames sequential color maps:\n"
214                 "    -t|--type=mcnames                 Generate a McNames sequential color map\n"
215                 "    [-p|--periods=P]                  Set the number of periods in (0, infty)\n"
216                 "Generates a color map and prints it to standard output as sRGB triplets.\n"
217                 "Report bugs to <martin.lambers@uni-siegen.de>.\n", argv[0]);
218         return 0;
219     }
220
221     if (type < 0) {
222         fprintf(stderr, "Invalid or missing option -t|--type.\n");
223         return 1;
224     }
225     if (n < 2) {
226         fprintf(stderr, "Invalid or missing option -n|--n.\n");
227         return 1;
228     }
229     if (hue < 0.0f) {
230         if (type == brewer_seq)
231             hue = ColorMap::BrewerSequentialDefaultHue;
232         else if (type == brewer_div)
233             hue = ColorMap::BrewerDivergingDefaultHue;
234         else if (type == brewer_qual)
235             hue = ColorMap::BrewerQualitativeDefaultHue;
236         else if (type == isolum_seq)
237             hue = ColorMap::IsoluminantSequentialDefaultHue;
238         else if (type == isolum_div)
239             hue = ColorMap::IsoluminantDivergingDefaultHue;
240         else if (type == isolum_qual)
241             hue = ColorMap::IsoluminantQualitativeDefaultHue;
242         else if (type == cubehelix)
243             hue = ColorMap::CubeHelixDefaultHue;
244     }
245     if (divergence < 0.0f) {
246         if (type == brewer_div)
247             divergence = ColorMap::BrewerDivergingDefaultDivergence;
248         else if (type == brewer_qual)
249             divergence = ColorMap::BrewerQualitativeDefaultDivergence;
250         else if (type == isolum_div)
251             divergence = ColorMap::IsoluminantDivergingDefaultDivergence;
252         else if (type == isolum_qual)
253             divergence = ColorMap::IsoluminantQualitativeDefaultDivergence;
254     }
255     if (contrast < 0.0f) {
256         if (type == brewer_seq)
257             contrast = (n <= 9 ? ColorMap::BrewerSequentialDefaultContrastForSmallN(n)
258                     : ColorMap::BrewerSequentialDefaultContrast);
259         else if (type == brewer_div)
260             contrast = (n <= 9 ? ColorMap::BrewerDivergingDefaultContrastForSmallN(n)
261                     : ColorMap::BrewerDivergingDefaultContrast);
262         else if (type == brewer_qual)
263             contrast = ColorMap::BrewerQualitativeDefaultContrast;
264     }
265     if (saturation < 0.0f) {
266         if (type == brewer_seq)
267             saturation = ColorMap::BrewerSequentialDefaultSaturation;
268         else if (type == brewer_div)
269             saturation = ColorMap::BrewerDivergingDefaultSaturation;
270         else if (type == brewer_qual)
271             saturation = ColorMap::BrewerQualitativeDefaultSaturation;
272         else if (type == isolum_seq)
273             saturation = ColorMap::IsoluminantSequentialDefaultSaturation;
274         else if (type == isolum_div)
275             saturation = ColorMap::IsoluminantDivergingDefaultSaturation;
276         else if (type == isolum_qual)
277             saturation = ColorMap::IsoluminantQualitativeDefaultSaturation;
278         else if (type == cubehelix)
279             saturation = ColorMap::CubeHelixDefaultSaturation;
280     }
281     if (brightness < 0.0f) {
282         if (type == brewer_seq)
283             brightness = ColorMap::BrewerSequentialDefaultBrightness;
284         else if (type == brewer_div)
285             brightness = ColorMap::BrewerDivergingDefaultBrightness;
286         else if (type == brewer_qual)
287             brightness = ColorMap::BrewerQualitativeDefaultBrightness;
288     }
289     if (warmth < 0.0f) {
290         if (type == brewer_seq)
291             warmth = ColorMap::BrewerSequentialDefaultWarmth;
292         else if (type == brewer_div)
293             warmth = ColorMap::BrewerDivergingDefaultWarmth;
294     }
295     if (luminance < 0.0f) {
296         if (type == isolum_seq)
297             luminance = ColorMap::IsoluminantSequentialDefaultLuminance;
298         else if (type == isolum_div)
299             luminance = ColorMap::IsoluminantDivergingDefaultLuminance;
300         else if (type == isolum_qual)
301             luminance = ColorMap::IsoluminantQualitativeDefaultLuminance;
302     }
303     if (temperature < 0.0f) {
304         if (type == blackbody)
305             temperature = ColorMap::BlackBodyDefaultTemperature;
306     }
307     if (range < 0.0f) {
308         if (type == blackbody)
309             range = ColorMap::BlackBodyDefaultRange;
310     }
311     if (std::isnan(rotations)) {
312         if (type == cubehelix)
313             rotations = ColorMap::CubeHelixDefaultRotations;
314     }
315     if (gamma < 0.0f) {
316         if (type == cubehelix)
317             gamma = ColorMap::CubeHelixDefaultGamma;
318     }
319     if (!have_color0) {
320         if (type == moreland) {
321             color0[0] = ColorMap::MorelandDefaultR0;
322             color0[1] = ColorMap::MorelandDefaultG0;
323             color0[2] = ColorMap::MorelandDefaultB0;
324         }
325     }
326     if (!have_color1) {
327         if (type == moreland) {
328             color1[0] = ColorMap::MorelandDefaultR1;
329             color1[1] = ColorMap::MorelandDefaultG1;
330             color1[2] = ColorMap::MorelandDefaultB1;
331         }
332     }
333     if (std::isnan(periods)) {
334         if (type == mcnames)
335             periods = ColorMap::McNamesDefaultPeriods;
336     }
337
338     std::vector<unsigned char> colormap(3 * n);
339     switch (type) {
340     case brewer_seq:
341         ColorMap::BrewerSequential(n, &(colormap[0]), hue, contrast, saturation, brightness, warmth);
342         break;
343     case brewer_div:
344         ColorMap::BrewerDiverging(n, &(colormap[0]), hue, divergence, contrast, saturation, brightness, warmth);
345         break;
346     case brewer_qual:
347         ColorMap::BrewerQualitative(n, &(colormap[0]), hue, divergence, contrast, saturation, brightness);
348         break;
349     case isolum_seq:
350         ColorMap::IsoluminantSequential(n, &(colormap[0]), luminance, saturation, hue);
351         break;
352     case isolum_div:
353         ColorMap::IsoluminantDiverging(n, &(colormap[0]), luminance, saturation, hue, divergence);
354         break;
355     case isolum_qual:
356         ColorMap::IsoluminantQualitative(n, &(colormap[0]), luminance, saturation, hue, divergence);
357         break;
358     case blackbody:
359         ColorMap::BlackBody(n, &(colormap[0]), temperature, range);
360         break;
361     case cubehelix:
362         ColorMap::CubeHelix(n, &(colormap[0]), hue, rotations, saturation, gamma);
363         break;
364     case moreland:
365         ColorMap::Moreland(n, &(colormap[0]),
366                 color0[0], color0[1], color0[2],
367                 color1[0], color1[1], color1[2]);
368         break;
369     case mcnames:
370         ColorMap::McNames(n, &(colormap[0]), periods);
371         break;
372     }
373
374     for (int i = 0; i < n; i++) {
375         printf("%d, %d, %d\n", colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]);
376     }
377
378     return 0;
379 }