izzi
SVG SUBSET C++ API
Loading...
Searching...
No Matches
a60-svg-color.h
Go to the documentation of this file.
1// svg color -*- mode: C++ -*-
2
3// Copyright (C) 2014-2025 Benjamin De Kosnik <b.dekosnik@gmail.com>
4
5// This file is part of the alpha60-MiL SVG library. This library is
6// free software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the Free
8// Software Foundation; either version 3, or (at your option) any
9// later version.
10
11// This library is distributed in the hope that it will be useful, but
12// WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14// General Public License for more details.
15
16#ifndef MiL_SVG_COLOR_H
17#define MiL_SVG_COLOR_H 1
18
19#include <map>
20#include <algorithm>
21#include <random>
22#include <iomanip>
23
24
25namespace svg {
26
27/**
28 HUE
29
30 color = color enum (and name string conversion)
31
32 color_qi = color quantified as RGB integral values 0-255
33 (similar to Scalar in OpenCV).
34
35 color_qf = color quantified as Hue Saturation Value (HSV) decimal values 0-1
36
37 spectrum = color spectrum as finite array of color enum values
38*/
39
40/// Color enumerated as types.
41enum class color
42{
44
45 // 13 tints / shades
50 gray25, // gainsboro
55 gray75, // slategray
59
63
64 // 15 yellow
65 kanzoiro, // daylily light orange
66 kohakuiro, // amber
67 kinsusutake, // golden-gray bamoo
80
81 // 7 orange
89
90 // 17 red
93 ginshu, // gray red
94 akabeni, // pure crimson
95 akebonoiro, // dawn color
98 benikaba, // red birch
99 benitobi, // red kite bird
100 ake, // scarlet/blood
108
109 // 4 brown
115
116 // 20 green
117 byakuroku, // whitish green
118 usumoegi, // pale onion
119 moegi, // onion green
120 hiwamoegi, // siskin sprout
123 aotakeiro, // green bamboo
124 seiheki, // blue green
125 seijiiro, // celadon
126 yanagizome, // willow dye
138
139 // 35 blue
143 hanada, // blue silk
144 ruriiro, // lapis
147 asagiiro, // light blue
149 rurikon, // dark blue lapis
175
176 // 32 purple
178 murasaki, // purple
181 futaai, // dark indigo
182 benimidori, // stained red/violet
183 redwisteria, // dusty rose
184 botan, // tree peony
185 kokimurasaki, // deep purple
186 usuiro, // thin
210
211 // STOS
215
216 // Must be last, so can be cast to int for size.
218};
219
220/// Total number of enumerated colors.
221constexpr uint color_max_size = static_cast<uint>(color::last);
222
223/// Convert color to RGB color value string.
224const std::string&
226{
227 using enum_map_type = std::map<color, std::string>;
228
229 static enum_map_type enum_map;
230 if (enum_map.empty())
231 {
232 enum_map[color::white] = "rgb(255, 255, 255)";
233 enum_map[color::black] = "rgb(0, 0, 0)";
234 enum_map[color::gray90] = "rgb(25, 25, 25)";
235 enum_map[color::gray80] = "rgb(50, 50, 50)";
236 enum_map[color::gray75] = "rgb(64, 64, 64)";
237 enum_map[color::gray70] = "rgb(77, 77, 77)";
238 enum_map[color::gray60] = "rgb(100, 100, 100)";
239 enum_map[color::gray50] = "rgb(128, 128, 128)";
240 enum_map[color::gray40] = "rgb(150, 150, 150)";
241 enum_map[color::gray30] = "rgb(180, 180, 180)";
242 enum_map[color::gray25] = "rgb(191, 191, 191)";
243 enum_map[color::gray20] = "rgb(200, 200, 200)";
244 enum_map[color::gray10] = "rgb(230, 230, 230)";
245
246 enum_map[color::wcag_lgray] = "rgb(148, 148, 148)"; // LG TXT on white 3:1
247 enum_map[color::wcag_gray] = "rgb(118, 118, 118)"; // min on white 4.5:1
248 enum_map[color::wcag_dgray] = "rgb(46, 46, 46)"; // on white 13.6:1
249
250 enum_map[color::command] = "rgb(255, 0, 171)";
251 enum_map[color::science] = "rgb(150, 230, 191)";
252 enum_map[color::engineering] = "rgb(161, 158, 178)";
253
254 enum_map[color::kissmepink] = "rgb(255, 59, 241)";
255
256 enum_map[color::red] = "rgb(255, 0, 0)";
257 enum_map[color::green] = "rgb(0, 255, 0)";
258 enum_map[color::blue] = "rgb(0, 0, 255)";
259
260 enum_map[color::asamablue] = "rgb(1, 137, 255)";
261 enum_map[color::asamaorange] = "rgb(236, 75, 37)";
262 enum_map[color::asamapink] = "rgb(200, 56, 81)";
263
264 // Yellows
265 enum_map[color::kanzoiro] = "rgb(255, 137, 54)";
266 enum_map[color::kohakuiro] = "rgb(202, 105, 36)";
267 enum_map[color::kinsusutake] = "rgb(125, 78, 45)";
268 enum_map[color::daylily] = "rgb(255, 137, 54)";
269 enum_map[color::goldenyellow] = "rgb(255, 164, 0)";
270 enum_map[color::hellayellow] = "rgb(255, 255, 0)";
271 enum_map[color::antiquewhite] = "rgb(250, 235, 215)";
272 enum_map[color::lemonchiffon] = "rgb(255, 250, 205)";
273 enum_map[color::goldenrod] = "rgb(250, 250, 210)";
274 enum_map[color::navajowhite] = "rgb(255, 222, 173)";
275
276 enum_map[color::ivory] = "rgb(255, 255, 240)";
277 enum_map[color::gold] = "rgb(255, 215, 0)";
278
279 enum_map[color::duboisyellow1] = "rgb(255, 255, 5)";
280 enum_map[color::duboisyellow2] = "rgb(255, 234, 18)";
281 enum_map[color::duboisyellow3] = "rgb(255, 215, 1)";
282
283 // Orange
284 enum_map[color::orange] = "rgb(255, 165, 0)";
285 enum_map[color::orangered] = "rgb(255, 69, 0)";
286 enum_map[color::redorange] = "rgb(220, 48, 35)";
287 enum_map[color::darkorange] = "rgb(255, 140, 17)";
288 enum_map[color::dutchorange] = "rgb(250, 155, 30)";
289 enum_map[color::internationalorange] = "rgb(255, 79, 0)";
290
291 // Brown
292 enum_map[color::duboisbrown1] = "rgb(128, 5, 5)";
293 enum_map[color::duboisbrown2] = "rgb(134, 90, 61)";
294 enum_map[color::duboisbrown3] = "rgb(81, 55, 42)";
295 enum_map[color::duboisbrown4] = "rgb(197, 146, 37)";
296 enum_map[color::duboisbrown5] ="rgb(255, 240, 200)";
297
298 // Reds
299 enum_map[color::foreigncrimson] = "rgb(201, 31, 55)";
300 enum_map[color::ginshu] = "rgb(188, 45, 41)";
301 enum_map[color::akabeni] = "rgb(195, 39,43)";
302 enum_map[color::akebonoiro] = "rgb(250, 123, 98)";
303
304 enum_map[color::ochre] = "rgb(255, 78, 32)";
305 enum_map[color::sohi] = "rgb(227, 92, 56)";
306 enum_map[color::benikaba] = "rgb(157, 43, 34)";
307 enum_map[color::benitobi] = "rgb(145, 50, 40)";
308 enum_map[color::ake] = "rgb(207, 58, 36)";
309
310 enum_map[color::crimson] = "rgb(220, 20, 60)";
311 enum_map[color::tomato] = "rgb(255, 99, 71)";
312 enum_map[color::coral] = "rgb(255, 127, 80)";
313 enum_map[color::salmon] = "rgb(250, 128, 114)";
314
315 enum_map[color::duboisred1] = "rgb(255, 29, 16)";
316 enum_map[color::duboisred2] = "rgb(249,110, 11)";
317 enum_map[color::duboisred3] = "rgb(215, 25, 50)";
318
319 // Greens
320 enum_map[color::byakuroku] = "rgb(165, 186, 147)";
321 enum_map[color::usumoegi] = "rgb(141, 178, 85)";
322 enum_map[color::moegi] = "rgb(91, 137, 48)";
323 enum_map[color::hiwamoegi] = "rgb(122, 148, 46)";
324 enum_map[color::midori] = "rgb(42, 96, 59)";
325 enum_map[color::rokusho] = "rgb(64, 122, 82)";
326 enum_map[color::aotakeiro] = "rgb(0, 100, 66)";
327 enum_map[color::seiheki] = "rgb(58, 105, 96)";
328 enum_map[color::seijiiro] = "rgb(129, 156, 139)";
329 enum_map[color::yanagizome] = "rgb(140, 158, 94)";
330
331 enum_map[color::chartreuse] = "rgb(127, 255, 0)";
332 enum_map[color::greenyellow] = "rgb(173, 255, 47)";
333 enum_map[color::limegreen] = "rgb(50, 205, 50)";
334 enum_map[color::springgreen] = "rgb(0, 255, 127)";
335 enum_map[color::aquamarine] = "rgb(127, 255, 212)";
336
337 enum_map[color::duboisgreen1] = "rgb(5, 255, 5)";
338 enum_map[color::duboisgreen2] = "rgb(127, 225, 15)";
339 enum_map[color::duboisgreen3] = "rgb(16, 114, 9)";
340 enum_map[color::duboisgreen4] = "rgb(0, 148, 16)";
341 enum_map[color::duboisgreen5] = "rgb(24, 57, 30)";
342
343 // Blues
344 enum_map[color::ultramarine] = "rgb(93, 140, 174)";
345 enum_map[color::shinbashiiro] = "rgb(0, 108, 127)";
346 enum_map[color::hanada] = "rgb(4, 79, 103)";
347 enum_map[color::ruriiro] = "rgb(31, 71, 136)";
348 enum_map[color::bellflower] = "rgb(25, 31, 69)";
349 enum_map[color::navy] = "rgb(0, 49, 113)";
350 enum_map[color::asagiiro] = "rgb(72, 146, 155)";
351 enum_map[color::indigo] = "rgb(38, 67, 72)";
352 enum_map[color::rurikon] = "rgb(27, 41, 75)";
353 enum_map[color::cyan] = "rgb(0, 255, 255)";
354
355 enum_map[color::lightcyan] = "rgb(224, 255, 255)";
356 enum_map[color::powderblue] = "rgb(176, 224, 230)";
357 enum_map[color::steelblue] = "rgb(70, 130, 237)";
358 enum_map[color::cornflowerblue] = "rgb(100, 149, 237)";
359 enum_map[color::deepskyblue] = "rgb(0, 191, 255)";
360 enum_map[color::dodgerblue] = "rgb(30, 144, 255)";
361 enum_map[color::lightblue] = "rgb(173, 216, 230)";
362 enum_map[color::skyblue] = "rgb(135, 206, 235)";
363 enum_map[color::lightskyblue] = "rgb(173, 206, 250)";
364 enum_map[color::midnightblue] = "rgb(25, 25, 112)";
365
366 enum_map[color::mediumblue] = "rgb(0, 0, 205)";
367 enum_map[color::royalblue] = "rgb(65, 105, 225)";
368 enum_map[color::darkslateblue] = "rgb(72, 61, 139)";
369 enum_map[color::slateblue] = "rgb(106, 90, 205)";
370 enum_map[color::azure] = "rgb(240, 255, 255)";
371 enum_map[color::crayolacerulean] = "rgb(29, 172, 214)";
372
373 enum_map[color::duboisblue1] = "rgb(37, 42, 255)";
374 enum_map[color::duboisblue2] = "rgb(100, 150, 245)";
375 enum_map[color::duboisblue3] = "rgb(74, 87, 129)";
376 enum_map[color::duboisblue4] = "rgb(49, 64, 103)";
377
378 enum_map[color::blueprintlight] = "rgb(0, 25, 166)";
379 enum_map[color::blueprint] = "rgb(0, 20, 132)";
380 enum_map[color::blueprintdark] = "rgb(0, 16, 106)";
381
382 // Purples
383 enum_map[color::wisteria] = "rgb(135, 95, 154)";
384 enum_map[color::murasaki] = "rgb(79, 40, 75)";
385 enum_map[color::ayameiro] = "rgb(118, 53, 104)";
386 enum_map[color::peony] = "rgb(164, 52, 93)";
387 enum_map[color::futaai] = "rgb(97, 78, 110)";
388 enum_map[color::benimidori] = "rgb(120, 119, 155)";
389 enum_map[color::redwisteria] = "rgb(187, 119, 150)";
390 enum_map[color::botan] = "rgb(164, 52, 93)";
391 enum_map[color::kokimurasaki] = "rgb(58, 36, 59)";
392 enum_map[color::usuiro] = "rgb(168, 124, 160)";
393
394 enum_map[color::blueviolet] = "rgb(138, 43, 226)";
395 enum_map[color::darkmagenta] = "rgb(139, 0, 139)";
396 enum_map[color::darkviolet] = "rgb(148, 0, 211)";
397 enum_map[color::thistle] = "rgb(216, 191, 216)";
398 enum_map[color::plum] = "rgb(221, 160, 221)";
399 enum_map[color::violet] = "rgb(238, 130, 238)";
400 enum_map[color::magenta] = "rgb(255, 0, 255)";
401 enum_map[color::dfuschia] = "rgb(255, 35, 255)";
402 enum_map[color::deeppink] = "rgb(255, 20, 147)";
403 enum_map[color::hotpink] = "rgb(255, 105, 180)";
404 enum_map[color::pink] = "rgb(255, 192, 203)";
405
406 enum_map[color::palevioletred] = "rgb(219, 112, 147)";
407 enum_map[color::mediumvioletred] = "rgb(199, 21, 133)";
408 enum_map[color::lavender] = "rgb(230, 230, 250)";
409 enum_map[color::orchid] = "rgb(218, 112, 214)";
410 enum_map[color::mediumorchid] = "rgb(186, 85, 211)";
411 enum_map[color::darkestmagenta] = "rgb(180, 0, 180)";
412 enum_map[color::mediumpurple] = "rgb(147, 112, 219)";
413 enum_map[color::purple] = "rgb(128, 0, 128)";
414 enum_map[color::dustyrose] = "rgb(191, 136, 187)";
415 enum_map[color::atmosphericp] = "rgb(228, 210, 231)";
416
417 enum_map[color::none] = "rgb(1, 0, 0)";
418 enum_map[color::last] = "rgb(0, 0, 1)";
419
420 // Error check to make sure all the colors have names/values.
421 if (enum_map.size() != color_max_size + 1)
422 {
423 string m("to_string(color)::color map size fail ");
424 m += k::newline;
425 m += std::to_string(enum_map.size());
426 m += " not equal to named colors of size ";
427 m += k::newline;
428 m += std::to_string(color_max_size);
429 throw std::runtime_error(m);
430 }
431 }
432 return enum_map[e];
433}
434
435
436/// Color quantified as integral RGB components in the range [0,255].
437/// aka like Scalar in OpenCV.
439{
440 using itype = unsigned short;
441
445
446 // Return "rgb(64, 64, 64)";
447
448 static string
450 {
451 std::ostringstream oss;
452 oss << "rgb(" << s.r << ',' << s.g << ',' << s.b << ")";
453 return oss.str();
454 }
455
456 // From "rgb(64, 64, 64)";
457 static color_qi
458 from_string(string s)
459 {
460 // Kill rgb() enclosing, if be.
461 if (s.empty() || s.size() < 5 || s[0] != 'r')
462 {
463 string m("color_qi::from_string input is not in rbg form: ");
464 m += s;
465 m += k::newline;
466 throw std::runtime_error(m);
467 }
468 else
469 {
470 s.pop_back();
471 s = s.substr(4);
472 }
473
474 // String stream which eats whitespace and knows number separation.
475 std::istringstream iss(s);
476 iss >> std::skipws;
477
478 char c(0);
479
480 ushort rs(0);
481 iss >> rs;
482 itype r = static_cast<itype>(rs);
483 iss >> c;
484
485 ushort gs(0);
486 iss >> gs;
487 itype g = static_cast<itype>(gs);
488 iss >> c;
489
490 ushort bs(0);
491 iss >> bs;
492 itype b = static_cast<itype>(bs);
493
494 return color_qi(r, g, b);
495 }
496
497 color_qi() = default;
498 color_qi(const color_qi&) = default;
499 color_qi& operator=(const color_qi&) = default;
500
501 // auto operator<=>(const color_qi&) const = default;
502
503 color_qi(itype ra, itype ga, itype ba) : r(ra), g(ga), b(ba) { }
504
506 {
508 r = klr.r;
509 b = klr.b;
510 g = klr.g;
511 }
512};
513
514inline bool
515operator==(const color_qi& c1, const color_qi& c2)
516{
517 const bool t1 = c1.r == c2.r;
518 const bool t2 = c1.g == c2.g;
519 const bool t3 = c1.b == c2.b;
520 return t1 && t2 && t3;
521}
522
523
524/// Convert color_qi to string.
525const std::string
527{ return color_qi::to_string(klr); }
528
529
530/**
531 Color quantified as floating point HSV components in the range [0,1].
532
533 https://web.archive.org/web/20150303174723/
534 http://en.literateprograms.org/RGB_to_HSV_color_space_conversion_(C)
535*/
537{
538 using ftype = float;
539
540 ftype h; /// Hue degree between 0.0 and 360.0
541 ftype s; /// Saturation between 0.0 (gray) and 1.0
542 ftype v; /// Value between 0.0 (black) and 1.0
543
544 color_qf() = default;
545 color_qf(const color_qf&) = default;
546 color_qf& operator=(const color_qf&) = default;
547
548 // auto operator<=>(const color_qf&) const = default;
549
550 color_qf(ftype vh, ftype vs, ftype vv) : h(vh), s(vs), v(vv) { }
551
552 // Conversion constructor, convert from RGB to HSV.
553 color_qf(const color_qi& cqi)
554 {
555 // Start by converting rgb to decimal range.
556 // color_qi assumes [0,255], color_qf converts to range [0,1]
557 auto [r, g, b] = cqi;
558 double rf = scale_value_on_range(r, 0, 255, 0, 1);
559 double gf = scale_value_on_range(g, 0, 255, 0, 1);
560 double bf = scale_value_on_range(b, 0, 255, 0, 1);
561 std::initializer_list<double> l1 = { rf, gf, bf };
562 double rgb_max = std::max(l1);
563
564 // Value first.
565 v = rgb_max;
566 if (v != 0)
567 {
568 double nrf(rf / v);
569 double ngf(gf / v);
570 double nbf(bf / v);
571 std::initializer_list<double> l2 = { nrf, ngf, nbf };
572 double nrgb_min = std::min(l2);
573 double nrgb_max = std::max(l2);
574
575 // Saturation second.
576 s = nrgb_max - nrgb_min;
577 if (s != 0)
578 {
579 // Hue third.
580 double srf((nrf - nrgb_min) / s);
581 double sgf((ngf - nrgb_min) / s);
582 double sbf((nbf - nrgb_min) / s);
583 std::initializer_list<double> l3 = { srf, sgf, sbf };
584 double srgb_max = std::max(l3);
585
586 if (srgb_max == srf)
587 {
588 h = 0.0 + 60.0 * (sgf - sbf);
589 if (h < 0.0)
590 h += 360.0;
591 }
592 else if (srgb_max == sgf)
593 {
594 h = 120.0 + (60.0 * (sbf - srf));
595 }
596 else
597 {
598 // srgb_max == sbf
599 h = 240.0 + (60.0 * (srf - sgf));
600 }
601 }
602 else
603 h = 0;
604 }
605 else
606 {
607 h = 0;
608 s = 0;
609 }
610 }
611
613 {
614 color_qi klr(e);
615 *this = color_qf(klr);
616 }
617
618 static string
620 {
621 std::ostringstream oss;
622 oss << std::fixed << std::setprecision(2);
623 oss << "hsv(" << s.h << ',' << s.s << ',' << s.v << ")";
624 return oss.str();
625 }
626
627 /// Back to RGB
628 /// https://www.rapidtables.com/convert/color/hsv-to-rgb.html
629 const color_qi
631 {
632 const ftype c = v * s;
633 const ftype x = c * (1.0 - std::fabs(std::fmod(h / 60.0, 2) - 1.0));
634 const ftype m = v - c;
635
636 float r, g, b;
637 if (h >= 0 && h < 60)
638 {
639 r = c, g = c, b = 0;
640 }
641 else if (h >= 60 && h < 120)
642 {
643 r = x, g = c, b = 0;
644 }
645 else if (h >= 120 && h < 180)
646 {
647 r = 0, g = c, b = x;
648 }
649 else if (h >= 180 && h < 240)
650 {
651 r = 0, g = x, b = c;
652 }
653 else if (h >= 240 && h < 300)
654 {
655 r = x, g = 0, b = c;
656 }
657 else
658 {
659 r = c, g = 0, b = x;
660 }
661
662 auto lpixel = [m] (float ff) { return color_qi::itype((ff + m) * 255); };
663
664 return color_qi(lpixel(r), lpixel(g), lpixel(b));
665 }
666};
667
668
669/// Convert color_qf to string.
670const std::string
672{ return color_qf::to_string(klr); }
673
674
675/// Less than compare for color_qf
676bool
678{
679 const bool eqh = k1.h == k2.h;
680 const bool lth = k1.h < k2.h;
681 const bool lts = k1.v < k2.v;
682
683 if (eqh)
684 return lts;
685 else
686 return lth;
687};
688
689bool
691{
692 const bool eqh = k1.h == k2.h;
693 const bool lth = k1.h < k2.h;
694
695 if (eqh)
696 {
697 const color_qf::ftype k1hyp = std::sqrt((k1.s * k1.s) + (k1.v * k1.v));
698 const color_qf::ftype k2hyp = std::sqrt((k2.s * k2.s) + (k2.v * k2.v));
699 const bool ltothers = k1hyp < k2hyp;
700 return ltothers;
701 }
702 else
703 return lth;
704};
705
708{
709 using ftype = color_qf::ftype;
710 const ftype sdist = std::abs(k1.s - k2.s);
711 const ftype vdist = std::abs(k1.v - k2.v);
712
713 const ftype habs = std::abs(k1.h - k2.h);
714 const ftype hdist = habs > 180 ? 360 - habs : habs;
715
716 // Euclidean distance.
717 //return std::sqrt(hdist*hdist + sdist*sdist + vdist*vdist);
718
719 // Weighted.
720 //return hdist*hweight + sdist*sweight + vdist*vweight;
721 return hdist*1.2 + sdist*2 + vdist*3;
722}
723
724
725/// Default compare distances from k1,k2 to black
726bool
727color_qf_lt_v(const color_qf& k1, const color_qf& k2)
728{
729 using ftype = color_qf::ftype;
730
731 const color_qf originklr = color::black;
732
733 // Compute distance from both arguments to compare color.
734 ftype d1 = color_qf_distance(k1, originklr);
735 ftype d2 = color_qf_distance(k2, originklr);
736 const bool ret = d1 < d2;
737 return ret;
738};
739
740
741/// Forwarding function.
742inline bool
743color_qf_lt(const color_qf& k1, const color_qf& k2)
744{ return color_qf_lt_v(k1, k2); };
745
746
747inline bool
748operator==(const color_qf& c1, const color_qf& c2)
749{
750 const bool t1 = c1.h == c2.h;
751 const bool t2 = c1.s == c2.s;
752 const bool t3 = c1.v == c2.v;
753 return t1 && t2 && t3;
754}
755
756inline bool
757operator<(const color_qf& c1, const color_qf& c2)
758{ return color_qf_lt(c1, c2); }
759
760
761/// Return a variant on saturation/value only.
762color_qf
764{
765 color_qf ret(k);
766 static std::mt19937_64 rg(std::random_device{}());
767 auto distr = std::uniform_real_distribution<>(0.5, 1);
768
769 // saturation 0.5 to 1, aka more saturated.
770 double stry = distr(rg);
771 if (ret.s < stry)
772 ret.s = stry;
773
774 // value 0.5 to 1, aka less dark
775 double vtry = distr(rg);
776 if (ret.v < vtry)
777 ret.v = vtry;
778
779 return ret;
780}
781
782
783/**
784 Combine color a with color b in percentages ad and ab, respectively.
785
786 To average, constrain paramters ad and ab such that: ad + ab == 2.
787
788 Like so:
789 ushort ur = (a.r + b.r) / 2;
790 ushort ug = (a.g + b.g) / 2;
791 ushort ub = (a.b + b.b) / 2;
792*/
793color_qi
794combine_color_qi(const color_qi& a, const double ad,
795 const color_qi& b, const double bd)
796{
797 double denom = ad + bd;
798 double ur = ((a.r * ad) + (b.r * bd)) / denom;
799 double ug = ((a.g * ad) + (b.g * bd)) / denom;
800 double ub = ((a.b * ad) + (b.b * bd)) / denom;
801
802 using itype = color_qi::itype;
803 itype cr = static_cast<itype>(ur);
804 itype cg = static_cast<itype>(ug);
805 itype cb = static_cast<itype>(ub);
806 return color_qi { cr, cg, cb };
807}
808
809/// Average two colors, return the result.
810color_qi
812{ return combine_color_qi(a, 1.0, b, 1.0); }
813
814
815/// Types for Color iteration and combinatorics.
816using color_qis = std::vector<color_qi>;
817using color_qfs = std::vector<color_qf>;
818
819} // namespace svg
820
821#endif
constexpr uint color_max_size
Total number of enumerated colors.
color
Color enumerated as types.
unsigned short ushort
Base integer type: positive and negative, signed integral value.
Definition a60-svg.h:56
double scale_value_on_range(const ssize_type value, const ssize_type min, const ssize_type max, const ssize_type nfloor, const ssize_type nceil)
Scale value from min to max on range (nfloor, nceil).
Definition a60-svg.h:216
std::vector< color_qf > color_qfs
const string to_string(const unit e)
std::vector< color_qi > color_qis
Types for Color iteration and combinatorics.
bool operator<(const color_qf &c1, const color_qf &c2)
color_qi average_color_qi(const color_qi &a, const color_qi &b)
Average two colors, return the result.
color_qf mutate_color_qf(const color_qf &k)
Return a variant on saturation/value only.
bool color_qf_lt_hue_v2(const color_qf &k1, const color_qf &k2)
bool operator==(const color_qi &c1, const color_qi &c2)
color_qi combine_color_qi(const color_qi &a, const double ad, const color_qi &b, const double bd)
color_qf::ftype color_qf_distance(const color_qf &k1, const color_qf &k2)
bool color_qf_lt_v(const color_qf &k1, const color_qf &k2)
Default compare distances from k1,k2 to black.
bool color_qf_lt(const color_qf &k1, const color_qf &k2)
Forwarding function.
unsigned int uint
Definition a60-svg.h:57
bool color_qf_lt_hue_v1(const color_qf &k1, const color_qf &k2)
Less than compare for color_qf.
ftype v
Saturation between 0.0 (gray) and 1.0.
const color_qi to_color_qi()
Back to RGB https://www.rapidtables.com/convert/color/hsv-to-rgb.html.
color_qf(const color e)
color_qf(ftype vh, ftype vs, ftype vv)
color_qf()=default
Value between 0.0 (black) and 1.0.
ftype s
Hue degree between 0.0 and 360.0.
color_qf & operator=(const color_qf &)=default
color_qf(const color_qf &)=default
static string to_string(color_qf s)
color_qf(const color_qi &cqi)
Color quantified as integral RGB components in the range [0,255]. aka like Scalar in OpenCV.
color_qi(const color_qi &)=default
color_qi & operator=(const color_qi &)=default
color_qi()=default
static string to_string(color_qi s)
color_qi(itype ra, itype ga, itype ba)
unsigned short itype
static color_qi from_string(string s)
color_qi(const color e)