A library for working with phylogenetic and population genetic data.
v0.32.0
axis.cpp
Go to the documentation of this file.
1 /*
2  Genesis - A toolkit for working with phylogenetic data.
3  Copyright (C) 2014-2024 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 
40 
41 #include <cassert>
42 #include <cmath>
43 #include <stdexcept>
44 
45 namespace genesis {
46 namespace utils {
47 
48 // =================================================================================================
49 // Svg Axis
50 // =================================================================================================
51 
53  SvgAxisSettings const& settings,
54  std::map<double, std::string> const& labels,
55  std::string const& name
56 ) {
57  SvgGroup group;
58 
59  // Draw the actual axis line
60  auto ax_end_x = 0.0;
61  auto ax_end_y = 0.0;
62  switch( settings.position ) {
65  {
66  ax_end_y = - settings.length;
67  break;
68  }
71  {
72  ax_end_x = settings.length;
73  break;
74  }
75  default: {
76  throw std::runtime_error( "Invalid SvgAxisSettings::Position" );
77  }
78  }
79  auto axis_line = SvgLine( 0.0, 0.0, ax_end_x, ax_end_y );
80  axis_line.stroke.line_cap = SvgStroke::LineCap::kSquare;
81  group << std::move( axis_line );
82 
83  // Draw all axis ticks
84  for( auto const& label : labels ) {
85  double const eps = 0.0000001;
86  if( !std::isfinite(label.first) || label.first < 0.0 - eps || label.first > 1.0 + eps ) {
87  throw std::runtime_error( "Svg axis label position out of [ 0.0, 1.0 ]" );
88  }
89 
90  // Find the direction away from the axis where to put the tick and the label,
91  // using svg coordinate directions.
92  double direction = 1.0;
93  if(
96  ) {
97  direction = -1.0;
98  }
99 
100  // Find the coordinates
101  SvgPoint line_p1;
102  SvgPoint line_p2;
103  SvgPoint text_p;
104  switch( settings.position ) {
107  {
108  auto const y_coord = - label.first * settings.length;
109  line_p1 = SvgPoint( 0.0, y_coord );
110  line_p2 = SvgPoint( 1.0 * direction * settings.tick_size, y_coord );
111  text_p = SvgPoint( 1.5 * direction * settings.tick_size, y_coord );
112  break;
113  }
116  {
117  auto const x_coord = label.first * settings.length;
118  line_p1 = SvgPoint( x_coord, 0.0 );
119  line_p2 = SvgPoint( x_coord, 1.0 * direction * settings.tick_size );
120  text_p = SvgPoint( x_coord, 2.5 * direction * settings.tick_size );
121  break;
122  }
123  }
124 
125  // Draw the lines and place the text
126  auto tick_line = SvgLine( line_p1, line_p2 );
127  tick_line.stroke.line_cap = SvgStroke::LineCap::kSquare;
128  group << std::move( tick_line );
129  auto label_text = SvgText( label.second );
130  label_text.font.size = settings.label_text_size;
131  label_text.transform.append( SvgTransform::Translate( text_p ));
132  if( settings.position == SvgAxisSettings::Position::kLeft ) {
133  label_text.anchor = SvgText::Anchor::kEnd;
134  }
135  if(
138  ) {
139  label_text.anchor = SvgText::Anchor::kMiddle;
140  }
141  // if( settings.position == SvgAxisSettings::Position::kBottom ) {
142  // label_text.transform.append( SvgTransform::Rotate( 90.0 ));
143  // }
144  group << std::move( label_text );
145  }
146 
147  // Move the group by the offset, and add the name if given. We always create the name,
148  // but only add it if it's non-empty. That's easier than checking for emptyness every time...
149  // (we are not expecting to create millions of axes, so that wastefulnes seems acceotable)
150  auto name_text = SvgText( name );
151  name_text.font.size = settings.axis_text_size;
152  name_text.anchor = SvgText::Anchor::kMiddle;
153  if( !std::isfinite( settings.offset ) || settings.offset < 0.0 ) {
154  throw std::runtime_error( "Invalid axis offset " + std::to_string( settings.offset ));
155  }
156  switch( settings.position ) {
158  {
159  name_text.transform.append( SvgTransform::Translate(
160  group.bounding_box().top_left.x - 1.5 * settings.tick_size,
161  - 0.5 * settings.length
162  ));
163  name_text.transform.append( SvgTransform::Rotate( -90.0 ));
164  group.transform.append( SvgTransform::Translate( -settings.offset, 0.0 ));
165  break;
166  }
168  {
169  name_text.transform.append( SvgTransform::Translate(
170  group.bounding_box().bottom_right.x + 1.5 * settings.tick_size,
171  - 0.5 * settings.length
172  ));
173  name_text.transform.append( SvgTransform::Rotate( -90.0 ));
174  group.transform.append( SvgTransform::Translate( settings.offset, 0.0 ));
175  break;
176  }
178  {
179  name_text.transform.append( SvgTransform::Translate(
180  0.5 * settings.length,
181  group.bounding_box().top_left.y - 1.5 * settings.tick_size
182  ));
183  group.transform.append( SvgTransform::Translate( 0.0, -settings.offset ));
184  break;
185  }
187  {
188  name_text.transform.append( SvgTransform::Translate(
189  0.5 * settings.length,
190  group.bounding_box().bottom_right.y + 1.5 * settings.tick_size
191  ));
192  group.transform.append( SvgTransform::Translate( 0.0, settings.offset ));
193  break;
194  }
195  }
196  if( ! name.empty() ) {
197  group << std::move( name_text );
198  }
199  return group;
200 }
201 
203  SvgAxisSettings const& settings,
204  std::vector<Tickmarks::LabeledTick> const& labels,
205  std::string const& name,
206  bool round_labels
207 ) {
208  std::map<double, std::string> ticks;
209  for( auto const& tick : labels ) {
210  ticks[ tick.relative_position ] = to_string_nice(
211  round_labels ? std::round( tick.label ) : tick.label
212  );
213  }
214  return make_svg_axis( settings, ticks, name );
215 }
216 
217 } // namespace utils
218 } // namespace genesis
genesis::utils::SvgPoint::x
double x
Definition: utils/formats/svg/helper.hpp:69
helper.hpp
genesis::utils::SvgTransform::append
void append(Transformation &&t)
Definition: attributes.cpp:353
genesis::utils::SvgStroke::LineCap::kSquare
@ kSquare
genesis::utils::SvgTransform::Rotate
Definition: attributes.hpp:298
genesis::utils::SvgPoint::y
double y
Definition: utils/formats/svg/helper.hpp:70
shapes.hpp
genesis::utils::SvgText::Anchor::kEnd
@ kEnd
genesis::utils::SvgLine
Definition: shapes.hpp:50
genesis::utils::SvgBox::top_left
SvgPoint top_left
Definition: utils/formats/svg/helper.hpp:204
axis.hpp
genesis::utils::SvgBox::bottom_right
SvgPoint bottom_right
Definition: utils/formats/svg/helper.hpp:205
genesis::utils::SvgAxisSettings::Position::kLeft
@ kLeft
genesis::utils::SvgAxisSettings
Definition: axis.hpp:49
genesis::utils::SvgAxisSettings::Position::kRight
@ kRight
genesis::population::to_string
std::string to_string(GenomeLocus const &locus)
Definition: function/genome_locus.hpp:52
genesis::utils::SvgPoint
Definition: utils/formats/svg/helper.hpp:57
string.hpp
Provides some commonly used string utility functions.
genesis::utils::SvgTransform::Translate
Definition: attributes.hpp:252
genesis::utils::SvgAxisSettings::offset
double offset
Offset from the origin for the axis.
Definition: axis.hpp:88
genesis::sequence::labels
std::unordered_set< std::string > labels(SequenceSet const &set)
Return a set of all labels of the SequenceSet.
Definition: labels.cpp:64
genesis::utils::SvgAxisSettings::tick_size
double tick_size
Length of the tick lines drawn from the axis to mark the tick labels.
Definition: axis.hpp:93
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
genesis::utils::SvgText::Anchor::kMiddle
@ kMiddle
genesis::utils::SvgAxisSettings::position
Position position
Position of the axis, relative to the plot that it is describing.
Definition: axis.hpp:72
genesis::utils::SvgAxisSettings::axis_text_size
double axis_text_size
Size of the text of the axis name.
Definition: axis.hpp:103
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
genesis::utils::SvgText
Definition: text.hpp:47
genesis::utils::SvgAxisSettings::Position::kBottom
@ kBottom
genesis::utils::make_svg_axis
SvgGroup make_svg_axis(SvgAxisSettings const &settings, std::map< double, std::string > const &labels, std::string const &name)
Simple helper to make an axis.
Definition: axis.cpp:52
genesis::utils::SvgAxisSettings::length
double length
Length of the axis.
Definition: axis.hpp:80
text.hpp
tickmarks.hpp
genesis::utils::SvgGroup::bounding_box
SvgBox bounding_box() const
Definition: group.cpp:50
attributes.hpp
genesis::utils::SvgGroup::transform
SvgTransform transform
Definition: group.hpp:159
genesis::utils::SvgAxisSettings::label_text_size
double label_text_size
Size of the text of the tick labels.
Definition: axis.hpp:98
genesis::utils::SvgGroup
Definition: group.hpp:50
object.hpp
genesis::utils::SvgAxisSettings::Position::kTop
@ kTop