A toolkit for working with phylogenetic data.
v0.19.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
color_bar.cpp
Go to the documentation of this file.
1 /*
2  Genesis - A toolkit for working with phylogenetic data.
3  Copyright (C) 2014-2018 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 
40 
51 
52 #include <algorithm>
53 #include <cassert>
54 #include <cstdlib>
55 #include <stdexcept>
56 
57 namespace genesis {
58 namespace utils {
59 
60 // =================================================================================================
61 // Local Helper Functions
62 // =================================================================================================
63 
64 std::pair<SvgGradientLinear, SvgGroup> make_svg_color_bar_gradient(
65  SvgColorBarSettings const& settings,
66  ColorMap const& map,
67  ColorNormalization const& norm,
68  std::string const& id
69 ) {
70  // Use a gradient ID with randomness so that we get a different one for each palette.
71  std::string const gradient_id = ( id.empty()
72  ? "PaletteGradient_" + std::to_string( std::rand() )
73  : id
74  );
75 
76  // Depending on the orientation, set gradient points.
77  SvgPoint point_1;
78  SvgPoint point_2;
79  switch( settings.direction ) {
81  point_1 = SvgPoint( 0.0, 1.0 );
82  point_2 = SvgPoint( 0.0, 0.0 );
83  break;
84  }
86  point_1 = SvgPoint( 0.0, 0.0 );
87  point_2 = SvgPoint( 0.0, 1.0 );
88  break;
89  }
91  point_1 = SvgPoint( 0.0, 0.0 );
92  point_2 = SvgPoint( 1.0, 0.0 );
93  break;
94  }
96  point_1 = SvgPoint( 1.0, 0.0 );
97  point_2 = SvgPoint( 0.0, 0.0 );
98  break;
99  }
100  default: {
101  throw std::runtime_error( "Invalid SvgPalette direction." );
102  }
103  }
104 
105  // Get the gradient list depending on the norm type.
106  auto const norm_gradient = color_stops( map, norm );
107 
108  // Fill gradient with the colors, add it to a group as a colored rect.
109  auto grad = SvgGradientLinear( gradient_id, point_1, point_2 );
110  for( auto const& g : norm_gradient ) {
111  if( g.first < 0.0 || g.first > 1.0 ) {
112  throw std::runtime_error( "Color Normalization gradient out of [ 0.0, 1.0 ]" );
113  }
114  grad.add_stop({ g.first, g.second });
115  }
116 
117  // Make group
118  SvgGroup group;
119  group << SvgRect(
120  0.0, 0.0, settings.width, settings.height,
121  SvgStroke(),
122  // SvgStroke( SvgStroke::Type::kNone ),
123  SvgFill( gradient_id )
124  );
125 
126  return { grad, group };
127 }
128 
129 std::pair<SvgGradientLinear, SvgGroup> make_svg_color_bar_discrete(
130  SvgColorBarSettings const& settings,
131  std::map<double, Color> const& stops
132 ) {
133  // Fill a group with colors for the stops.
134  SvgGroup group;
135  for( auto it = stops.begin(); it != stops.end(); ++it ) {
136 
137  // Get and check iterator values.
138  auto const& pos = it->first;
139  auto const& color = it->second;
140  if( pos < 0.0 || pos > 1.0 ) {
141  throw std::runtime_error( "Color Normalization stops out of [ 0.0, 1.0 ]" );
142  }
143 
144  // If we are at the upper bound, we do not need to draw another box.
145  if( pos == 1.0 ) {
146  continue;
147  }
148 
149  // We need the next one for calculating the end of the box.
150  // We init next pos to 1, which is used if there is no 1.0 stop in the stops list,
151  // meaning that the `continue` statement above is never used.
152  // In the other case (there is a stop 1.0), next will always point to a valid element,
153  // so that next pos is set to it.
154  auto next = it;
155  ++next;
156  double next_pos = 1.0;
157  if( next != stops.end() ) {
158  next_pos = next->first;
159  }
160  assert( next_pos > pos );
161 
162  // Calculate the box.
163  double x = 0.0;
164  double y = 0.0;
165  double w = settings.width;
166  double h = settings.height;
167  switch( settings.direction ) {
169  y = settings.height - next_pos * settings.height;
170  h = ( next_pos - pos ) * settings.height;
171  break;
172  }
174  y = pos * settings.height;
175  h = ( next_pos - pos ) * settings.height;
176  break;
177  }
179  x = pos * settings.width;
180  w = ( next_pos - pos ) * settings.width;
181  break;
182  }
184  x = settings.width - next_pos * settings.width;
185  w = ( next_pos - pos ) * settings.width;
186  break;
187  }
188  default: {
189  throw std::runtime_error( "Invalid SvgPalette direction." );
190  }
191  }
192 
193  // Make the box.
194  group << SvgRect(
195  x, y, w, h,
197  SvgFill( color )
198  );
199  }
200 
201  // Add a black line around the bar
202  group << SvgRect(
203  0.0, 0.0, settings.width, settings.height,
204  SvgStroke( Color( 0.0, 0.0, 0.0 )),
206  );
207 
208  return { SvgGradientLinear(), group };
209 }
210 
212  SvgColorBarSettings const& settings,
213  ColorMap const& map,
214  ColorNormalization const& norm,
215  SvgGroup& group
216 ) {
217  // Helper function to make a tick mark with line and text
218  // at a relative position [ 0.0, 1.0 ] along the rect.
219  auto make_tick = [&]( double rel_pos, std::string label ){
220  assert( 0.0 <= rel_pos && rel_pos <= 1.0 );
221 
222  // Get positions for needed elements.
223  double v = -1.0;
224  double h = -1.0;
225  switch( settings.direction ) {
227  v = settings.height - ( rel_pos * settings.height );
228  break;
229  }
231  v = rel_pos * settings.height;
232  break;
233  }
235  h = rel_pos * settings.width;
236  break;
237  }
239  h = settings.width - ( rel_pos * settings.width );
240  break;
241  }
242  default: {
243  throw std::runtime_error( "Invalid SvgPalette direction." );
244  }
245  }
246 
247  // Set elements.
248  SvgPoint line1_p1;
249  SvgPoint line1_p2;
250  SvgPoint line2_p1;
251  SvgPoint line2_p2;
252  SvgPoint text_p;
253  switch( settings.direction ) {
256  {
257  assert( v > -1.0 );
258  line1_p1 = SvgPoint( 0.0, v );
259  line1_p2 = SvgPoint( settings.width * 0.15, v );
260  line2_p1 = SvgPoint( settings.width * 0.85, v );
261  line2_p2 = SvgPoint( settings.width, v );
262  text_p = SvgPoint( settings.width * 1.05, v );
263  break;
264  }
267  {
268  assert( h > -1.0 );
269  line1_p1 = SvgPoint( h, 0.0 );
270  line1_p2 = SvgPoint( h, settings.height * 0.15 );
271  line2_p1 = SvgPoint( h, settings.height * 0.85 );
272  line2_p2 = SvgPoint( h, settings.height );
273  text_p = SvgPoint( h, settings.height * 1.05 );
274  break;
275  }
276  default: {
277  throw std::runtime_error( "Invalid SvgPalette direction." );
278  }
279  }
280 
281  // Draw lines and text. Lines only for inners, as we already have a box around the scale.
282  if( rel_pos != 0.0 && rel_pos != 1.0 ) {
283  group << SvgLine( line1_p1, line1_p2 );
284  group << SvgLine( line2_p1, line2_p2 );
285  }
286  if( settings.with_labels ) {
287  if( rel_pos == 1.0 && map.clip_over() ) {
288  label = "≥ " + label;
289  }
290  if( rel_pos == 0.0 && map.clip_under() ) {
291  label = "≤ " + label;
292  }
293  auto text_s = SvgText( label, text_p );
294  // text_s.dominant_baseline = SvgText::DominantBaseline::kMiddle;
295  // text_s.alignment_baseline = SvgText::AlignmentBaseline::kMiddle;
296  // text_s.dy = "0.33em";
297  group << text_s;
298  }
299  };
300 
301  // Make tickmarks and labels.
302  if( settings.with_tickmarks ) {
303  auto const tickmarks = color_tickmarks( norm, settings.num_ticks );
304  for( auto const& tick : tickmarks ) {
305  if( tick.first < 0.0 || tick.first > 1.0 ) {
306  throw std::runtime_error( "Color Normalization tickmark out of [ 0.0, 1.0 ]" );
307  }
308  make_tick( tick.first, tick.second );
309  }
310  }
311 }
312 
313 // =================================================================================================
314 // Svg Color Bar
315 // =================================================================================================
316 
317 std::pair<SvgGradientLinear, SvgGroup> make_svg_color_bar(
318  SvgColorBarSettings const& settings,
319  ColorMap const& map,
320  ColorNormalization const& norm,
321  std::string const& id
322 ) {
323 
324  if( map.palette().size() < 2 ) {
325  throw std::runtime_error(
326  "Cannot make SvgPalette with a ColorMap of less than two colors."
327  );
328  }
329  if( ! norm.is_valid() ) {
330  throw std::runtime_error(
331  "Invaid ColorNormalization settings."
332  );
333  }
334 
335  // Prepare result.
336  auto result = std::pair<SvgGradientLinear, SvgGroup>();
337 
338  // We have an ugly special case for boundary norms, where we do not want to display
339  // a gradient, but discrete color bars instead...
340  auto norm_boundary = dynamic_cast<ColorNormalizationBoundary const*>( &norm );
341  if( norm_boundary ) {
342  auto const norm_gradient = color_stops( map, norm );
343  result = make_svg_color_bar_discrete( settings, norm_gradient );
344  } else {
345  result = make_svg_color_bar_gradient( settings, map, norm, id );
346  }
347 
348  // Add the tickmarks to the bar/
349  make_svg_color_bar_tickmarks( settings, map, norm, result.second );
350 
351  return result;
352 }
353 
355  ColorMap const& map,
356  std::vector<std::string> const& labels
357 ) {
358  SvgGroup group;
359 
360  for( size_t i = 0; i < labels.size(); ++i ) {
361  group << SvgRect(
362  0.0, i * 15.0, 10.0, 10.0,
364  SvgFill( map.color(i) )
365  );
366  group << SvgText( labels[i], SvgPoint( 20.0, i * 15.0 + 10.0 ));
367  }
368 
369  return group;
370 }
371 
372 } // namespace utils
373 } // namespace genesis
bool clip_over() const
Clip (clamp) values greater than max() to be inside [ min, max ].
Definition: map.hpp:131
std::pair< SvgGradientLinear, SvgGroup > make_svg_color_bar_gradient(SvgColorBarSettings const &settings, ColorMap const &map, ColorNormalization const &norm, std::string const &id)
Definition: color_bar.cpp:64
void make_svg_color_bar_tickmarks(SvgColorBarSettings const &settings, ColorMap const &map, ColorNormalization const &norm, SvgGroup &group)
Definition: color_bar.cpp:211
size_t size() const
Return the size of the map, that is, the number of colors in the list.
Definition: map.hpp:238
Store a list of colors and offer them as a map for values in range [ 0.0, 1.0 ].
Definition: map.hpp:61
Color color(size_t index) const
Return a particular color from the palette, module the palette size.
Definition: map.hpp:249
std::pair< SvgGradientLinear, SvgGroup > make_svg_color_bar_discrete(SvgColorBarSettings const &settings, std::map< double, Color > const &stops)
Definition: color_bar.cpp:129
SvgGroup make_svg_color_list(ColorMap const &map, std::vector< std::string > const &labels)
Definition: color_bar.cpp:354
std::string to_string(T const &v)
Return a string representation of a given value.
Definition: string.hpp:373
Header of Color class.
Provides some commonly used string utility functions.
std::map< double, Color > color_stops(ColorMap const &map, ColorNormalization const &norm)
Definition: helpers.cpp:52
Base class for color normalization.
bool clip_under() const
Clip (clamp) values less than min() to be inside [ min, max ].
Definition: map.hpp:121
std::map< double, std::string > color_tickmarks(ColorNormalization const &norm, size_t num_ticks)
Definition: helpers.cpp:156
std::pair< SvgGradientLinear, SvgGroup > make_svg_color_bar(SvgColorBarSettings const &settings, ColorMap const &map, ColorNormalization const &norm, std::string const &id)
Definition: color_bar.cpp:317
Color normalization that maps to discrete intervals.
bool is_valid() const
Return whether ranges and other values are correct.
std::unordered_set< std::string > labels(SequenceSet const &set)
Return a set of all labels of the SequenceSet.
Definition: labels.cpp:62
Color operators and functions.
ColorMap & palette(std::vector< Color > const &value)
Definition: map.hpp:209