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