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