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