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