A library for working with phylogenetic and population genetic data.
v0.32.0
helpers.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 
42 
43 #include <cassert>
44 #include <stdexcept>
45 
46 namespace genesis {
47 namespace utils {
48 
49 // =================================================================================================
50 // Gradients
51 // =================================================================================================
52 
53 std::map<double, Color> color_stops( ColorMap const& map, ColorNormalization const& norm )
54 {
55  // Super duper ugly code.
56  // Need to do linear last, because the other two are derived from it.
57 
58  auto norm_log = dynamic_cast<ColorNormalizationLogarithmic const*>( &norm );
59  if( norm_log ) {
60  return color_stops( map, *norm_log );
61  }
62  auto norm_div = dynamic_cast<ColorNormalizationDiverging const*>( &norm );
63  if( norm_div ) {
64  return color_stops( map, *norm_div );
65  }
66  auto norm_bnd = dynamic_cast<ColorNormalizationBoundary const*>( &norm );
67  if( norm_bnd ) {
68  return color_stops( map, *norm_bnd );
69  }
70  auto norm_lin = dynamic_cast<ColorNormalizationLinear const*>( &norm );
71  if( norm_lin ) {
72  return color_stops( map, *norm_lin );
73  }
74 
75  return std::map<double, Color>{};
76 }
77 
78 std::map<double, Color> color_stops( ColorMap const& map, ColorNormalizationLinear const& norm )
79 {
80  (void) norm;
81  std::map<double, Color> result;
82  for( size_t i = 0; i < map.size(); ++i ) {
83  auto const offset = static_cast<double>( i ) / static_cast<double>( map.size() - 1 );
84  result[ offset ] = map.color( i );
85  }
86  return result;
87 }
88 
89 std::map<double, Color> color_stops( ColorMap const& map, ColorNormalizationLogarithmic const& norm )
90 {
91  return color_stops( map, dynamic_cast<ColorNormalizationLinear const&>( norm ));
92 }
93 
94 std::map<double, Color> color_stops( ColorMap const& map, ColorNormalizationDiverging const& norm )
95 {
96  std::map<double, Color> result;
97 
98  // Get the fractions of the lower and upper half,
99  // which are needed to scale the colors in a diverging palette correctly.
100  // For example, a palette with 5, 15, 20 for min, mid and max gets
101  // fractions 2/3 and 1/3 here.
102  auto const frac_lower = ( norm.mid_value() - norm.min_value() ) / ( norm.max_value() - norm.min_value() );
103  auto const frac_upper = ( norm.max_value() - norm.mid_value() ) / ( norm.max_value() - norm.min_value() );
104 
105  // Divide the palette in two, so that the mixed mid color counts as half a step
106  // in palettes with even number of colors.
107  auto const scale = 2.0 / static_cast<double>( map.size() - 1 );
108 
109  // Lower half.
110  for( size_t i = 0; i < map.size() / 2; ++i ) {
111  auto const offset = scale * frac_lower * static_cast<double>( i );
112  result[ offset ] = map.color( i );
113  }
114 
115  // For an even number of colors, we need to add a mixed middle color.
116  if( map.size() % 2 == 0 ) {
117  auto const mid_idx = map.size() / 2;
118  auto const mid_color = interpolate(
119  map.color( mid_idx - 1 ), map.color( mid_idx ), 0.5
120  );
121  result[ frac_lower ] = mid_color;
122  }
123 
124  // Upper half, including mid if uneven number of colors.
125  for( size_t i = map.size() / 2; i < map.size(); ++i ) {
126 
127  // Step away from end: We go backwards.
128  auto const step = static_cast<double>( map.size() - i - 1 );
129 
130  // Offset, as before, just going backwards again, so that we end up in right order.
131  auto const offset = 1.0 - ( scale * frac_upper * step );
132  result[ offset ] = map.color( i );
133  }
134 
135  return result;
136 }
137 
138 std::map<double, Color> color_stops( ColorMap const& map, ColorNormalizationBoundary const& norm )
139 {
140  std::map<double, Color> result;
141 
142  // Get range.
143  auto const min = norm.boundaries().front();
144  auto const max = norm.boundaries().back();
145  auto const len = max - min;
146 
147  for( auto const& bound : norm.boundaries() ) {
148  result[ ( bound - min ) / len ] = map( norm, bound );
149  }
150  return result;
151 }
152 
153 // =================================================================================================
154 // Tickmarks
155 // =================================================================================================
156 
157 std::map<double, std::string> color_tickmarks( ColorNormalization const& norm, size_t num_ticks )
158 {
159  // Super duper ugly code.
160  // Need to do linear last, because the other two are derived from it.
161 
162  auto norm_log = dynamic_cast<ColorNormalizationLogarithmic const*>( &norm );
163  if( norm_log ) {
164  return color_tickmarks( *norm_log, num_ticks );
165  }
166  auto norm_div = dynamic_cast<ColorNormalizationDiverging const*>( &norm );
167  if( norm_div ) {
168  return color_tickmarks( *norm_div, num_ticks );
169  }
170  auto norm_bnd = dynamic_cast<ColorNormalizationBoundary const*>( &norm );
171  if( norm_bnd ) {
172  return color_tickmarks( *norm_bnd, num_ticks );
173  }
174  auto norm_lin = dynamic_cast<ColorNormalizationLinear const*>( &norm );
175  if( norm_lin ) {
176  return color_tickmarks( *norm_lin, num_ticks );
177  }
178 
179  return std::map<double, std::string>{};
180 }
181 
182 std::map<double, std::string> color_tickmarks( ColorNormalizationLinear const& norm, size_t num_ticks )
183 {
184  std::map<double, std::string> result;
185  auto tm = Tickmarks();
186  auto const tm_labels = tm.linear_labels( norm.min_value(), norm.max_value(), num_ticks );
187  for( auto const& tm_label : tm_labels ) {
188  result[ tm_label.relative_position ] = to_string_nice( tm_label.label );
189  }
190  return result;
191 }
192 
193 std::map<double, std::string> color_tickmarks( ColorNormalizationLogarithmic const& norm, size_t num_ticks )
194 {
195  std::map<double, std::string> result;
196 
197  // Don't use them for log scale.
198  (void) num_ticks;
199 
200  auto tm = Tickmarks();
201  auto const tm_labels_u = tm.logarithmic_labels( norm.min_value(), norm.max_value(), norm.base() );
202  for( auto const& tm_label : tm_labels_u ) {
203  auto label = ( norm.exponential_labels()
204  ? to_string_nice( norm.base() ) + "^" + to_string_nice(
205  std::log( tm_label.label ) / std::log( norm.base() )
206  )
207  : to_string_nice( tm_label.label )
208  );
209  result[ tm_label.relative_position ] = label;
210  }
211 
212  return result;
213 }
214 
215 std::map<double, std::string> color_tickmarks( ColorNormalizationDiverging const& norm, size_t num_ticks )
216 {
217  std::map<double, std::string> result;
218  auto tm = Tickmarks();
219 
220  // Get the fractions of the lower and upper half,
221  // which are needed to scale the colors in a diverging palette correctly.
222  // For example, a palette with 5, 15, 20 for min, mid and max gets
223  // fractions 2/3 and 1/3 here.
224  auto const frac_lower = ( norm.mid_value() - norm.min_value() ) / ( norm.max_value() - norm.min_value() );
225  auto const frac_upper = ( norm.max_value() - norm.mid_value() ) / ( norm.max_value() - norm.min_value() );
226  if( frac_lower <= 0.0 || frac_upper <= 0.0 ) {
227  throw std::runtime_error( "Invalid color normalization for calculating tickmarks." );
228  }
229 
230  // Lower half.
231  tm.include_max = false;
232  auto const tm_labels_l = tm.linear_labels(
233  norm.min_value(), norm.mid_value(), static_cast<size_t>( frac_lower * num_ticks )
234  );
235  for( auto const& tm_label : tm_labels_l ) {
236  auto const pos = frac_lower * tm_label.relative_position;
237  result[ pos ] = to_string_nice( tm_label.label );
238  }
239 
240  // In cases where the mid value is a nice tickmark number (0 for example),
241  // it will be included in the tickmarks, although it is the upper limit for
242  // the lower half (that is, equal to the max for the half).
243  // Thus, we already have a tickmark for the mid value, and now do not need it again
244  // when making the upper half ticks. So, exclude the min for the upper half in this case.
245  if( tm_labels_l.size() > 0 && tm_labels_l.back().relative_position == 1.0 ) {
246  tm.include_min = false;
247  }
248 
249  // Upper half.
250  tm.include_max = true;
251  auto const tm_labels_u = tm.linear_labels(
252  norm.mid_value(), norm.max_value(), static_cast<size_t>( frac_upper * num_ticks )
253  );
254  for( auto const& tm_label : tm_labels_u ) {
255  auto const pos = frac_lower + frac_upper * tm_label.relative_position;
256  result[ pos ] = to_string_nice( tm_label.label );
257  }
258 
259  return result;
260 }
261 
262 std::map<double, std::string> color_tickmarks( ColorNormalizationBoundary const& norm, size_t num_ticks )
263 {
264  // Ignore. We use the number of boundaries coming from the normalization.
265  (void) num_ticks;
266 
267  // Get range.
268  auto const min = norm.boundaries().front();
269  auto const max = norm.boundaries().back();
270  auto const len = max - min;
271 
272  std::map<double, std::string> result;
273  for( auto const& bound : norm.boundaries() ) {
274  result[ ( bound - min ) / len ] = to_string_nice( bound );
275  }
276  return result;
277 }
278 
279 } // namespace utils
280 } // namespace genesis
genesis::utils::ColorMap
Store a list of colors and offer them as a map for values in range [ 0.0, 1.0 ].
Definition: map.hpp:61
genesis::utils::ColorNormalizationLogarithmic
Color normalization for a logarithmic scale.
Definition: norm_logarithmic.hpp:52
genesis::utils::color_stops
std::map< double, Color > color_stops(ColorMap const &map, ColorNormalization const &norm)
Definition: helpers.cpp:53
genesis::utils::ColorNormalization
Base class for color normalization.
Definition: normalization.hpp:52
map.hpp
genesis::utils::ColorNormalizationLogarithmic::base
double base() const
Definition: norm_logarithmic.hpp:106
helpers.hpp
genesis::utils::ColorNormalizationDiverging
Color normalization for a diverging scale.
Definition: norm_diverging.hpp:71
genesis::utils::ColorNormalizationLogarithmic::exponential_labels
bool exponential_labels() const
Definition: norm_logarithmic.hpp:117
genesis::utils::scale
void scale(Histogram &h, double factor)
Definition: operations.cpp:54
genesis::utils::offset
void offset(Histogram &h, double value)
Definition: operations.cpp:47
genesis::utils::Tickmarks
Helper class to find "nice" tickmark intervals for creating scales and axes.
Definition: tickmarks.hpp:48
genesis::utils::color_tickmarks
std::map< double, std::string > color_tickmarks(ColorNormalization const &norm, size_t num_ticks)
Definition: helpers.cpp:157
string.hpp
Provides some commonly used string utility functions.
genesis::utils::ColorNormalizationLinear::min_value
double min_value() const
Minimum value, that is, where to begin the color scale.
Definition: norm_linear.hpp:115
genesis::utils::ColorNormalizationLinear
Default Color normalization, using a sequential linear scaling in the range [ min,...
Definition: norm_linear.hpp:59
genesis::utils::ColorNormalizationBoundary
Color normalization that maps to discrete intervals.
Definition: norm_boundary.hpp:75
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
norm_diverging.hpp
functions.hpp
Color operators and functions.
genesis::utils::ColorNormalizationDiverging::mid_value
double mid_value() const
Mid-point value, that is, where the middle value of a diverging_color() is.
Definition: norm_diverging.hpp:140
color.hpp
Header of Color class.
genesis::utils::ColorNormalizationBoundary::boundaries
std::vector< double > const & boundaries() const
Return the boundaries currently set.
Definition: norm_boundary.hpp:118
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::to_string_nice
std::string to_string_nice(T const &v)
Return a string representation of a given value.
Definition: string.hpp:637
norm_linear.hpp
norm_boundary.hpp
normalization.hpp
genesis::utils::ColorNormalizationLinear::max_value
double max_value() const
Minimum value, that is, where to end the color scale.
Definition: norm_linear.hpp:123
genesis::utils::ColorMap::size
size_t size() const
Return the size of the map, that is, the number of colors in the list.
Definition: map.hpp:248
norm_logarithmic.hpp
genesis::utils::ColorMap::color
Color color(size_t index) const
Return a particular color from the palette, module the palette size.
Definition: map.hpp:259