A library for working with phylogenetic and population genetic data.
v0.32.0
utils/color/functions.cpp
Go to the documentation of this file.
1 /*
2  Genesis - A toolkit for working with phylogenetic data.
3  Copyright (C) 2014-2023 Lucas Czech
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  Contact:
19  Lucas Czech <lczech@carnegiescience.edu>
20  Department of Plant Biology, Carnegie Institution For Science
21  260 Panama Street, Stanford, CA 94305, USA
22 */
23 
32 
33 #include <algorithm>
34 #include <cassert>
35 #include <cmath>
36 #include <iomanip>
37 #include <ostream>
38 #include <sstream>
39 #include <stdexcept>
40 #include <string>
41 
45 
46 namespace genesis {
47 namespace utils {
48 
49 // =================================================================================================
50 // Color Conversion
51 // =================================================================================================
52 
53 Color color_from_bytes( unsigned char r, unsigned char g, unsigned char b, unsigned char a )
54 {
55  Color c;
56  c.r_byte(r);
57  c.g_byte(g);
58  c.b_byte(b);
59  c.a_byte(a);
60  return c;
61 }
62 
63 Color color_from_hex( std::string const& hex_color, std::string const& prefix )
64 {
65  // Check for correct prefix and trim it.
66  if (hex_color.substr(0, prefix.size()) != prefix) {
67  throw std::invalid_argument("String does not start with given prefix.");
68  }
69  auto const h = utils::trim( hex_color.substr( prefix.size() ));
70 
71  // Check for incorrect chars, as std::stoul (used later for the actual conversion)
72  // might just end parsing instead of throwing...
73  if( h.find_first_not_of("0123456789abcdefABCDEF") != std::string::npos ) {
74  throw std::invalid_argument("Expects string with six or eight hexadecimal digits.");
75  }
76 
77  // Take a two-byte position in h, range [0-3], and parse the to bytes into a double.
78  auto hex_parse = [&h] (size_t pos) {
79  // We select the two-digit substring at the given position,
80  // which resolves to one of the three two-hex-digit color substrings in h.
81  auto s = h.substr(pos * 2, 2);
82  auto v = std::stoul(s, nullptr, 16);
83 
84  // We just fed two chars with a hex value into the conversion, there cannot be a result out
85  // of char range from this.
86  assert(v < 256);
87  return static_cast<double>(v) / 255.0;
88  };
89 
90  // Check for correct input size, and convert accordingly.
91  if( h.size() == 6 ) {
92  return Color( hex_parse(0), hex_parse(1), hex_parse(2) );
93  } else if( h.size() == 8 ) {
94  return Color( hex_parse(0), hex_parse(1), hex_parse(2), hex_parse(3) );
95  } else {
96  throw std::invalid_argument("Expects string with six or eight hexadecimal digits.");
97  }
98 }
99 
100 std::string color_to_hex(
101  Color const& c , std::string const& prefix, bool uppercase, bool with_alpha
102 ) {
103  std::stringstream stream;
104  stream << prefix;
105 
106  auto hex_print = [&stream, uppercase] (unsigned char v) {
107  if (uppercase) {
108  stream << std::setfill ('0') << std::setw(2) << std::hex << std::uppercase
109  << static_cast<int>(v);
110  } else {
111  stream << std::setfill ('0') << std::setw(2) << std::hex << std::nouppercase
112  << static_cast<int>(v);
113  }
114  };
115  hex_print( c.r_byte() );
116  hex_print( c.g_byte() );
117  hex_print( c.b_byte() );
118  if( with_alpha ) {
119  hex_print( c.a_byte() );
120  }
121 
122  return stream.str();
123 }
124 
125 // =================================================================================================
126 // Color Operators
127 // =================================================================================================
128 
129 std::ostream& operator<< (std::ostream& os, Color const& color)
130 {
131  os << "( " << std::to_string( color.r() )
132  << ", " << std::to_string( color.g() )
133  << ", " << std::to_string( color.b() )
134  << ", " << std::to_string( color.a() ) << " )";
135  return os;
136 }
137 
138 Color resolve_color_string( std::string const& color_str )
139 {
140  auto const str = trim( color_str );
141 
142  // Check if it is a hex color string.
143  if( starts_with( str, "#" ) ) {
144  return color_from_hex( str );
145  }
146 
147  return color_from_name( str );
148 }
149 
150 // =================================================================================================
151 // Color Gradients
152 // =================================================================================================
153 
154 Color interpolate (Color const& color1, Color const& color2, double fraction)
155 {
156  // Helper function that linearily interpolates between two values.
157  auto interpolate_values = []( double d1, double d2, double fraction )
158  {
159  return (1.0 - fraction) * d1 + fraction * d2;
160  };
161 
162  fraction = std::min( std::max( 0.0, fraction ), 1.0 );
163  double r = interpolate_values( color1.r(), color2.r(), fraction );
164  double g = interpolate_values( color1.g(), color2.g(), fraction );
165  double b = interpolate_values( color1.b(), color2.b(), fraction );
166  double a = interpolate_values( color1.a(), color2.a(), fraction );
167  return Color( r, g, b, a );
168 }
169 
170 Color gradient ( std::map<double, Color> const& ranges, double value )
171 {
172  // Check range sanity.
173  if( ranges.size() < 2 ) {
174  throw std::invalid_argument("Gradient range needs to contain at least two colors.");
175  }
176 
177  // Get range boundaries.
178  double const min = ranges.begin()->first;
179  double const max = ranges.rbegin()->first;
180 
181  // Ensure the correct interval.
182  value = std::min( std::max( min, value ), max );
183 
184  // Set hi_bound to the next bigger item in ranges that comes after the value position.
185  // lo_bound then is the one before it. Now, the range between them includes value.
186  // In case value is exactly equal to a value in ranges, it will be stored in lo_bound.
187  auto hi_bound = ranges.upper_bound( value );
188  auto lo_bound = std::prev( hi_bound );
189  assert( lo_bound != ranges.end() );
190 
191  if( hi_bound == ranges.end() ) {
192  // This is the boundary case that occurs when value is max.
193  // Assert the case and return the last color of the range.
194  assert( value == max );
195  return ranges.rbegin()->second;
196  }
197 
198  // Adjust value to the new interval between lo and hi, and return the interpolated color.
199  value = ( value - lo_bound->first ) / ( hi_bound->first - lo_bound->first );
200  return interpolate( lo_bound->second, hi_bound->second, value );
201 }
202 
203 Color heat_gradient (double percentage)
204 {
205  Color red { 1.0, 0.0, 0.0 };
206  Color yellow { 1.0, 1.0, 0.0 };
207  Color green { 0.0, 1.0, 0.0 };
208 
209  percentage = std::min(std::max(0.0, percentage), 1.0);
210 
211  if (percentage < 0.5) {
212  return interpolate(green, yellow, percentage / 0.5);
213  }
214  return interpolate(yellow, red, (percentage - 0.5) / 0.5);
215 }
216 
217 } // namespace utils
218 } // namespace genesis
genesis::utils::Color
Definition: color.hpp:47
genesis::utils::color_from_hex
Color color_from_hex(std::string const &hex_color, std::string const &prefix)
Create a Color given a hex color string in the format "#003366[ff]".
Definition: utils/color/functions.cpp:63
genesis::utils::Color::r
double r() const
Definition: color.hpp:109
genesis::utils::color_from_name
Color color_from_name(std::string const &name)
Return the color represented by the given name, which is (currently) a shortcut for color_from_name_w...
Definition: names.cpp:1518
genesis::utils::operator<<
std::ostream & operator<<(std::ostream &os, Color const &color)
Write a textual representation of the Color the a stream, in the format "(r, g, b,...
Definition: utils/color/functions.cpp:129
genesis::utils::trim
std::string trim(std::string const &s, std::string const &delimiters)
Return a copy of the input string, with trimmed white spaces (or any other delimiters).
Definition: string.cpp:827
genesis::utils::Color::b
double b() const
Definition: color.hpp:119
genesis::utils::starts_with
bool starts_with(std::string const &text, std::string const &prefix)
Return whether a string starts with another string, i.e., check for a prefix.
Definition: string.cpp:136
genesis::utils::Color::a_byte
unsigned char a_byte() const
Definition: color.hpp:144
genesis::population::to_string
std::string to_string(GenomeLocus const &locus)
Definition: function/genome_locus.hpp:52
string.hpp
Provides some commonly used string utility functions.
genesis::utils::Color::a
double a() const
Definition: color.hpp:124
genesis::utils::gradient
Color gradient(std::map< double, Color > const &ranges, double value)
Returns a Color that is created using a color gradient.
Definition: utils/color/functions.cpp:170
genesis
Container namespace for all symbols of genesis in order to keep them separate when used as a library.
Definition: placement/formats/edge_color.cpp:42
functions.hpp
Color operators and functions.
genesis::utils::Color::b_byte
unsigned char b_byte() const
Definition: color.hpp:139
names.hpp
genesis::utils::heat_gradient
Color heat_gradient(double percentage)
Return a Color that represents a heat gradient for a given percentage value.
Definition: utils/color/functions.cpp:203
color.hpp
Header of Color class.
genesis::utils::interpolate
Color interpolate(Color const &color1, Color const &color2, double fraction)
Linearily interpolate between two Colors.
Definition: utils/color/functions.cpp:154
genesis::utils::Color::g_byte
unsigned char g_byte() const
Definition: color.hpp:134
genesis::utils::Color::g
double g() const
Definition: color.hpp:114
genesis::utils::Color::r_byte
unsigned char r_byte() const
Definition: color.hpp:129
genesis::utils::color_from_bytes
Color color_from_bytes(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
Create a Color given three or four values in the range [ 0, 255 ] for each of the components red,...
Definition: utils/color/functions.cpp:53
genesis::utils::resolve_color_string
Color resolve_color_string(std::string const &color_str)
Resolve a string representing a color.
Definition: utils/color/functions.cpp:138
genesis::utils::color_to_hex
std::string color_to_hex(Color const &c, std::string const &prefix, bool uppercase, bool with_alpha)
Return a hex string representation of a Color in the format "#003366[ff]".
Definition: utils/color/functions.cpp:100