A toolkit for working with phylogenetic data.
v0.18.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
tickmarks.cpp
Go to the documentation of this file.
1 /*
2  Genesis - A toolkit for working with phylogenetic data.
3  Copyright (C) 2014-2017 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 <lucas.czech@h-its.org>
20  Exelixis Lab, Heidelberg Institute for Theoretical Studies
21  Schloss-Wolfsbrunnenweg 35, D-69118 Heidelberg, Germany
22 */
23 
32 
34 
35 #include <algorithm>
36 #include <cassert>
37 #include <cmath>
38 
39 namespace genesis {
40 namespace utils {
41 
42 // ================================================================================================
43 // Tickmarks
44 // ================================================================================================
45 
46 double Tickmarks::step_size( double interval_size, size_t target_steps )
47 {
48  // Adapted from
49  // http://stackoverflow.com/questions/361681/algorithm-for-nice-grid-line-intervals-on-a-graph
50 
51  // Calculate an initial guess at step size.
52  auto step_guess = interval_size / static_cast<double>( target_steps );
53 
54  // Get the magnitude of the step size.
55  auto mag = std::floor( std::log10( step_guess ));
56  auto mag_pow = std::pow( 10, mag );
57 
58  // Calculate most significant digit (MSD) of the new step size.
59  auto mag_msd = static_cast<int>( step_guess / mag_pow + 0.5 );
60 
61  // Promote the MSD to either 1, 2, 5 or 10.
62  if( mag_msd > 5 ) {
63  mag_msd = 10;
64  } else if( mag_msd > 2 ) {
65  mag_msd = 5;
66  } else if( mag_msd > 1 ) {
67  mag_msd = 2;
68  } else {
69  assert( mag_msd == 1 );
70  }
71 
72  return static_cast<double>( mag_msd ) * mag_pow;
73 }
74 
75 std::vector<double> Tickmarks::linear_ticks( double min, double max, size_t target_steps )
76 {
77  auto res = std::vector<double>();
78 
79  // Get step size.
80  auto interval_size = max - min;
81  auto step_sz = step_size( interval_size, target_steps );
82 
83  // Calculate first tick position, so that it is the largest multiple of the step size
84  // that is below the min.
85  auto tick = step_sz * std::floor( min / step_sz );
86 
87  // Determine whether we want to start before or after the min.
88  if( ! undershoot_at_min ) {
89  tick += step_sz;
90  }
91 
92  // Add ticks to the list.
93  while( tick <= max ) {
94  res.push_back( tick );
95  tick += step_sz;
96  }
97 
98  // Determine whether we want to stop before or after the max.
99  if( overshoot_at_max ) {
100  res.push_back( tick );
101  }
102 
103  // Add min and max if needed.
104  if( include_min ) {
105  res.push_back( min );
106  }
107  if( include_max ) {
108  res.push_back( max );
109  }
110 
111  // Cleanup duplicate entries and those that are close by. We do not need ticks that are
112  // too close to each other.
113  // It is easier to do this than to check for duplicate entries in each addition step...
114  std::sort( res.begin(), res.end() );
115  auto uniq_end = std::unique( res.begin(), res.end(), [&]( double lhs, double rhs ){
116  return almost_equal_relative( lhs, rhs, relative_epsilon );
117  });
118  res.resize( std::distance( res.begin(), uniq_end ));
119 
120  return res;
121 }
122 
123 std::vector<Tickmarks::LabeledTick> Tickmarks::linear_labels(
124  double min, double max, size_t target_steps
125 ) {
126  auto res = std::vector<LabeledTick>();
127 
128  auto ticks = linear_ticks( min, max, target_steps );
129  auto range = max - min;
130 
131  for( auto const& tick : ticks ) {
132  auto rel_pos = ( tick - min ) / range;
133  res.emplace_back( rel_pos, tick );
134  }
135  return res;
136 }
137 
138 std::vector<Tickmarks::LabeledTick> Tickmarks::logarithmic_labels( double max )
139 {
140  auto res = std::vector<LabeledTick>();
141 
142  size_t pow_i = 0;
143  double pow_v = 0.0;
144  do {
145  // Get the order of magnitude corresponding to the current pow_i.
146  pow_v = std::log10( std::pow( 10, pow_i )) / std::log10( max );
147 
148  // If this is in the range (or one above, if we want to overshoot), add it to the result.
149  if( pow_v <= 1.0 || overshoot_at_max ) {
150  res.emplace_back( pow_v, std::pow( 10, pow_i ) );
151  }
152 
153  // Next order of magnitude.
154  ++pow_i;
155  } while( pow_v <= 1.0 );
156 
157  // Possibly include max. If so, we need to sort, as we might have added the overshoot before.
158  if( include_max ) {
159  res.emplace_back( 1, max );
160  std::sort( res.begin(), res.end(), []( LabeledTick lhs, LabeledTick rhs ){
161  return lhs.relative_position < rhs.relative_position;
162  });
163  }
164 
165  return res;
166 }
167 
168 } // namespace utils
169 } // namespace genesis
std::vector< double > linear_ticks(double min, double max, size_t target_steps)
Calculate a set of ticks that linearily span from min to max in approximately target_steps many steps...
Definition: tickmarks.cpp:75
bool undershoot_at_min
Should the lowest value in the resulting list of ticks be below the provided min value (true) or not ...
Definition: tickmarks.hpp:147
double relative_epsilon
Relative epsilon used to exclude two tickmarks that are too close to each other.
Definition: tickmarks.hpp:158
static double step_size(double interval_size, size_t target_steps)
Calculate a step size that fills the interval_size in approximately target_steps many steps...
Definition: tickmarks.cpp:46
bool overshoot_at_max
Should the highest value in the resulting list of ticks be above the provided max value (true) or not...
Definition: tickmarks.hpp:153
std::vector< LabeledTick > linear_labels(double min, double max, size_t target_steps)
Return a set of labels with relative positions between min and max, where the labels correspond to th...
Definition: tickmarks.cpp:123
bool include_max
Should the provided max value be included in the resulting list of ticks or not.
Definition: tickmarks.hpp:141
bool include_min
Should the provided min value be included in the resulting list of ticks or not.
Definition: tickmarks.hpp:136
std::vector< LabeledTick > logarithmic_labels(double max)
Return a set of labels with relative positions between min and max, where the labels correspond to lo...
Definition: tickmarks.cpp:138
bool almost_equal_relative(double lhs, double rhs, double max_rel_diff=std::numeric_limits< double >::epsilon())
Check whether two doubles are almost equal, using a relative epsilon to compare them.
Definition: common.hpp:277