Add perceptually uniform rainbow 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,
38     brewer_div,
39     brewer_qual,
40     isolum_seq,
41     isolum_div,
42     isolum_qual,
43     unirainbow,
44     blackbody,
45     cubehelix,
46     moreland,
47     mcnames
48 };
49
50 int main(int argc, char* argv[])
51 {
52     bool print_version = false;
53     bool print_help = false;
54     int type = -1;
55     int n = -1;
56     float hue = -1.0f;
57     float divergence = -1.0f;
58     float contrast = -1.0f;
59     float saturation = -1.0f;
60     float brightness = -1.0f;
61     float warmth = -1.0f;
62     float luminance = -1.0f;
63     float rotations = NAN;
64     float temperature = -1.0f;
65     float range = -1.0f;
66     float gamma = -1.0f;
67     bool have_color0 = false;
68     unsigned char color0[3];
69     bool have_color1 = false;
70     unsigned char color1[3];
71     float periods = NAN;
72     struct option options[] = {
73         { "version",     no_argument,       0, 'v' },
74         { "help",        no_argument,       0, 'H' },
75         { "type",        required_argument, 0, 't' },
76         { "n",           required_argument, 0, 'n' },
77         { "hue",         required_argument, 0, 'h' },
78         { "divergence",  required_argument, 0, 'd' },
79         { "contrast",    required_argument, 0, 'c' },
80         { "saturation",  required_argument, 0, 's' },
81         { "brightness",  required_argument, 0, 'b' },
82         { "warmth",      required_argument, 0, 'w' },
83         { "luminance",   required_argument, 0, 'l' },
84         { "rotations",   required_argument, 0, 'r' },
85         { "temperature", required_argument, 0, 'T' },
86         { "range",       required_argument, 0, 'R' },
87         { "gamma",       required_argument, 0, 'g' },
88         { "color0",      required_argument, 0, 'A' },
89         { "color1",      required_argument, 0, 'O' },
90         { "periods",     required_argument, 0, 'p' },
91         { 0, 0, 0, 0 }
92     };
93
94     for (;;) {
95         int c = getopt_long(argc, argv, "vHt:n:h:d:c:s:b:w:l:T:R:r:g:A:O:p:", options, NULL);
96         if (c == -1)
97             break;
98         switch (c) {
99         case 'v':
100             print_version = true;
101             break;
102         case 'H':
103             print_help = true;
104             break;
105         case 't':
106             type = (strcmp(optarg, "brewer-sequential") == 0 ? brewer_seq
107                     : strcmp(optarg, "brewer-diverging") == 0 ? brewer_div
108                     : strcmp(optarg, "brewer-qualitative") == 0 ? brewer_qual
109                     : strcmp(optarg, "isoluminant-sequential") == 0 ? isolum_seq
110                     : strcmp(optarg, "isoluminant-divergent") == 0 ? isolum_div
111                     : strcmp(optarg, "isoluminant-qualitative") == 0 ? isolum_qual
112                     : strcmp(optarg, "uniformrainbow") == 0 ? unirainbow
113                     : strcmp(optarg, "blackbody") == 0 ? blackbody
114                     : strcmp(optarg, "cubehelix") == 0 ? cubehelix
115                     : strcmp(optarg, "moreland") == 0 ? moreland
116                     : strcmp(optarg, "mcnames") == 0 ? mcnames
117                     : -2);
118             break;
119         case 'n':
120             n = atoi(optarg);
121             break;
122         case 'h':
123             hue = atof(optarg) * M_PI / 180.0;
124             break;
125         case 'd':
126             divergence = atof(optarg) * M_PI / 180.0;
127             break;
128         case 'c':
129             contrast = atof(optarg);
130             break;
131         case 's':
132             saturation = atof(optarg);
133             break;
134         case 'b':
135             brightness = atof(optarg);
136             break;
137         case 'w':
138             warmth = atof(optarg);
139             break;
140         case 'l':
141             luminance = atof(optarg);
142             break;
143         case 'r':
144             rotations = atof(optarg);
145             break;
146         case 'T':
147             temperature = atof(optarg);
148             break;
149         case 'R':
150             range = atof(optarg);
151             break;
152         case 'g':
153             gamma = atof(optarg);
154             break;
155         case 'A':
156             std::sscanf(optarg, "%hhu,%hhu,%hhu", color0 + 0, color0 + 1, color0 + 2);
157             have_color0 = true;
158             break;
159         case 'O':
160             std::sscanf(optarg, "%hhu,%hhu,%hhu", color1 + 0, color1 + 1, color1 + 2);
161             have_color1 = true;
162             break;
163         case 'p':
164             periods = atof(optarg);
165             break;
166         default:
167             return 1;
168         }
169     }
170
171     if (print_version) {
172         printf("gencolormap version 0.3\n"
173                 "Copyright (C) 2016 Computer Graphics Group, University of Siegen.\n"
174                 "Written by Martin Lambers <martin.lambers@uni-siegen.de>.\n"
175                 "This is free software under the terms of the MIT/Expat License.\n"
176                 "There is NO WARRANTY, to the extent permitted by law.\n");
177         return 0;
178     }
179
180     if (print_help) {
181         printf("Usage: %s\n"
182                 "  Common options, required for all types:\n"
183                 "    -n|--n=N                         Set number of colors in the map\n"
184                 "  Brewer-like color maps:\n"
185                 "    -t|--type=brewer-sequential       Generate a sequential color map\n"
186                 "    -t|--type=brewer-diverging        Generate a diverging color map\n"
187                 "    -t|--type=brewer-qualitative      Generate a qualitative color map\n"
188                 "    [-h|--hue=H]                      Set default hue in [0,360] degrees\n"
189                 "    [-c|--contrast=C]                 Set contrast in [0,1]\n"
190                 "    [-s|--saturation=S]               Set saturation in [0,1]\n"
191                 "    [-b|--brightness=B]               Set brightness in [0,1]\n"
192                 "    [-w|--warmth=W]                   Set warmth in [0,1] for seq. and div. maps\n"
193                 "    [-d|--divergence=D]               Set divergence in deg. for div. and qual. maps\n"
194                 "  Isoluminant color maps:\n"
195                 "    -t|--type=isoluminant-sequential  Generate a sequential color map\n"
196                 "    -t|--type=isoluminant-diverging   Generate a diverging color map\n"
197                 "    -t|--type=isoluminant-qualitative Generate a qualitative color map\n"
198                 "    [-l|--luminance=L]                Set luminance in [0,1]\n"
199                 "    [-s|--saturation=S]               Set saturation in [0,1]\n"
200                 "    [-h|--hue=H]                      Set default hue in [0,360] degrees\n"
201                 "    [-d|--divergence=D]               Set divergence in deg. for div. and qual. maps\n"
202                 "  Uniform Rainbow color maps:\n"
203                 "    -t|--type=uniformrainbow          Generate a uniform rainbow color map\n"
204                 "    [-h|--hue=H]                      Set start hue in [0,360] degrees\n"
205                 "    [-r|--rotations=R]                Set number of rotations, in (-infty,infty)\n"
206                 "    [-s|--saturation=S]               Set saturation, in [0,1]\n"
207                 "  Black Body color maps:\n"
208                 "    -t|--type=blackbody               Generate a Black Body color map\n"
209                 "    [-T|--temperature=T]              Start temperature of the map in Kelvin\n"
210                 "    [-R|--range=R]                    Range of temperatures of the map in Kelvin\n"
211                 "  CubeHelix color maps:\n"
212                 "    -t|--type=cubehelix               Generate a CubeHelix color map\n"
213                 "    [-h|--hue=H]                      Set start hue in [0,180] degrees\n"
214                 "    [-r|--rotations=R]                Set number of rotations, in (-infty,infty)\n"
215                 "    [-s|--saturation=S]               Set saturation, in [0,1]\n"
216                 "    [-g|--gamma=G]                    Set gamma correction, in (0,infty)\n"
217                 "  Moreland diverging color maps:\n"
218                 "    -t|--type=moreland                Generate a Moreland diverging color map\n"
219                 "    [-A|--color0=sr,sg,sb             Set the first color as sRGB values in [0,255]\n"
220                 "    [-O|--color1=sr,sg,sb             Set the last color as sRGB values in [0,255]\n"
221                 "  McNames sequential color maps:\n"
222                 "    -t|--type=mcnames                 Generate a McNames sequential color map\n"
223                 "    [-p|--periods=P]                  Set the number of periods in (0, infty)\n"
224                 "Generates a color map and prints it to standard output as sRGB triplets.\n"
225                 "Report bugs to <martin.lambers@uni-siegen.de>.\n", argv[0]);
226         return 0;
227     }
228
229     if (type < 0) {
230         fprintf(stderr, "Invalid or missing option -t|--type.\n");
231         return 1;
232     }
233     if (n < 2) {
234         fprintf(stderr, "Invalid or missing option -n|--n.\n");
235         return 1;
236     }
237     if (hue < 0.0f) {
238         if (type == brewer_seq)
239             hue = ColorMap::BrewerSequentialDefaultHue;
240         else if (type == brewer_div)
241             hue = ColorMap::BrewerDivergingDefaultHue;
242         else if (type == brewer_qual)
243             hue = ColorMap::BrewerQualitativeDefaultHue;
244         else if (type == isolum_seq)
245             hue = ColorMap::IsoluminantSequentialDefaultHue;
246         else if (type == isolum_div)
247             hue = ColorMap::IsoluminantDivergingDefaultHue;
248         else if (type == isolum_qual)
249             hue = ColorMap::IsoluminantQualitativeDefaultHue;
250         else if (type == unirainbow)
251             hue = ColorMap::UniformRainbowDefaultHue;
252         else if (type == cubehelix)
253             hue = ColorMap::CubeHelixDefaultHue;
254     }
255     if (divergence < 0.0f) {
256         if (type == brewer_div)
257             divergence = ColorMap::BrewerDivergingDefaultDivergence;
258         else if (type == brewer_qual)
259             divergence = ColorMap::BrewerQualitativeDefaultDivergence;
260         else if (type == isolum_div)
261             divergence = ColorMap::IsoluminantDivergingDefaultDivergence;
262     }
263     if (contrast < 0.0f) {
264         if (type == brewer_seq)
265             contrast = (n <= 9 ? ColorMap::BrewerSequentialDefaultContrastForSmallN(n)
266                     : ColorMap::BrewerSequentialDefaultContrast);
267         else if (type == brewer_div)
268             contrast = (n <= 9 ? ColorMap::BrewerDivergingDefaultContrastForSmallN(n)
269                     : ColorMap::BrewerDivergingDefaultContrast);
270         else if (type == brewer_qual)
271             contrast = ColorMap::BrewerQualitativeDefaultContrast;
272     }
273     if (saturation < 0.0f) {
274         if (type == brewer_seq)
275             saturation = ColorMap::BrewerSequentialDefaultSaturation;
276         else if (type == brewer_div)
277             saturation = ColorMap::BrewerDivergingDefaultSaturation;
278         else if (type == brewer_qual)
279             saturation = ColorMap::BrewerQualitativeDefaultSaturation;
280         else if (type == isolum_seq)
281             saturation = ColorMap::IsoluminantSequentialDefaultSaturation;
282         else if (type == isolum_div)
283             saturation = ColorMap::IsoluminantDivergingDefaultSaturation;
284         else if (type == isolum_qual)
285             saturation = ColorMap::IsoluminantQualitativeDefaultSaturation;
286         else if (type == unirainbow)
287             saturation = ColorMap::UniformRainbowDefaultSaturation;
288         else if (type == cubehelix)
289             saturation = ColorMap::CubeHelixDefaultSaturation;
290     }
291     if (brightness < 0.0f) {
292         if (type == brewer_seq)
293             brightness = ColorMap::BrewerSequentialDefaultBrightness;
294         else if (type == brewer_div)
295             brightness = ColorMap::BrewerDivergingDefaultBrightness;
296         else if (type == brewer_qual)
297             brightness = ColorMap::BrewerQualitativeDefaultBrightness;
298     }
299     if (warmth < 0.0f) {
300         if (type == brewer_seq)
301             warmth = ColorMap::BrewerSequentialDefaultWarmth;
302         else if (type == brewer_div)
303             warmth = ColorMap::BrewerDivergingDefaultWarmth;
304     }
305     if (luminance < 0.0f) {
306         if (type == isolum_seq)
307             luminance = ColorMap::IsoluminantSequentialDefaultLuminance;
308         else if (type == isolum_div)
309             luminance = ColorMap::IsoluminantDivergingDefaultLuminance;
310         else if (type == isolum_qual)
311             luminance = ColorMap::IsoluminantQualitativeDefaultLuminance;
312     }
313     if (std::isnan(rotations)) {
314         if (type == unirainbow)
315             rotations = ColorMap::UniformRainbowDefaultRotations;
316         else if (type == cubehelix)
317             rotations = ColorMap::CubeHelixDefaultRotations;
318     }
319     if (temperature < 0.0f) {
320         if (type == blackbody)
321             temperature = ColorMap::BlackBodyDefaultTemperature;
322     }
323     if (range < 0.0f) {
324         if (type == blackbody)
325             range = ColorMap::BlackBodyDefaultRange;
326     }
327     if (gamma < 0.0f) {
328         if (type == cubehelix)
329             gamma = ColorMap::CubeHelixDefaultGamma;
330     }
331     if (!have_color0) {
332         if (type == moreland) {
333             color0[0] = ColorMap::MorelandDefaultR0;
334             color0[1] = ColorMap::MorelandDefaultG0;
335             color0[2] = ColorMap::MorelandDefaultB0;
336         }
337     }
338     if (!have_color1) {
339         if (type == moreland) {
340             color1[0] = ColorMap::MorelandDefaultR1;
341             color1[1] = ColorMap::MorelandDefaultG1;
342             color1[2] = ColorMap::MorelandDefaultB1;
343         }
344     }
345     if (std::isnan(periods)) {
346         if (type == mcnames)
347             periods = ColorMap::McNamesDefaultPeriods;
348     }
349
350     std::vector<unsigned char> colormap(3 * n);
351     switch (type) {
352     case brewer_seq:
353         ColorMap::BrewerSequential(n, &(colormap[0]), hue, contrast, saturation, brightness, warmth);
354         break;
355     case brewer_div:
356         ColorMap::BrewerDiverging(n, &(colormap[0]), hue, divergence, contrast, saturation, brightness, warmth);
357         break;
358     case brewer_qual:
359         ColorMap::BrewerQualitative(n, &(colormap[0]), hue, divergence, contrast, saturation, brightness);
360         break;
361     case isolum_seq:
362         ColorMap::IsoluminantSequential(n, &(colormap[0]), luminance, saturation, hue);
363         break;
364     case isolum_div:
365         ColorMap::IsoluminantDiverging(n, &(colormap[0]), luminance, saturation, hue, divergence);
366         break;
367     case isolum_qual:
368         ColorMap::IsoluminantQualitative(n, &(colormap[0]), luminance, saturation, hue);
369         break;
370     case unirainbow:
371         ColorMap::UniformRainbow(n, &(colormap[0]), hue, rotations, saturation);
372         break;
373     case blackbody:
374         ColorMap::BlackBody(n, &(colormap[0]), temperature, range);
375         break;
376     case cubehelix:
377         ColorMap::CubeHelix(n, &(colormap[0]), hue, rotations, saturation, gamma);
378         break;
379     case moreland:
380         ColorMap::Moreland(n, &(colormap[0]),
381                 color0[0], color0[1], color0[2],
382                 color1[0], color1[1], color1[2]);
383         break;
384     case mcnames:
385         ColorMap::McNames(n, &(colormap[0]), periods);
386         break;
387     }
388
389     for (int i = 0; i < n; i++) {
390         printf("%d, %d, %d\n", colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]);
391     }
392
393     return 0;
394 }