CLAM-Development  1.4.0
RulerTicks.hxx
Go to the documentation of this file.
1 #ifndef RulerTicks_hxx
2 #define RulerTicks_hxx
3 
4 #include <cmath>
5 #include "Assert.hxx"
6 namespace CLAM
7 {
8 
9 /*
10  Implements the logic to place graphically several markers
11  in a ruler so that the markers are placed with a given
12  minimal separation and corresponds to round numbers
13  on the mapped domain interval.
14  So, given a domain value range, a pixel size and a minimal pixel gap,
15  it gives you the domain offset for the first tick and the intertick gap
16  also in domain units.
17  Chosen round numbers are series so that the last non zero
18  decimal steps in 1, 2 or 5 jumps and include 0. For instance:
19  - 3.1, 3.2, 3.3...
20  - 22, 24, 26...
21  - 0.35, 0.40, 0.45...
22 */
24 {
25  mutable bool _needsUpdate;
26 
27  double _min;
28  double _max;
29  double _minGap;
30  double _width;
31 
32  mutable double _markOffset;
33  mutable double _markGap;
34  public:
36  : _needsUpdate(true)
37  , _min(0)
38  , _max(1)
39  , _minGap(5)
40  , _width(100)
41  , _markOffset(666)
42  , _markGap(69)
43  {
44  }
46  void setRange(double min, double max)
47  {
48  CLAM_ASSERT(min<max, "RulerTicks: Empty or inverse order range");
49  _min = min;
50  _max = max;
51  _needsUpdate = true;
52  }
54  void setWidth(double width)
55  {
56  CLAM_ASSERT(width>0, "RulerTicks: Pixel width should be greater than zero");
57  _width = width;
58  _needsUpdate = true;
59  }
61  void setMinGap(double minGap)
62  {
63  _minGap = minGap;
64  _needsUpdate = true;
65  }
66  double markOffset() const
67  {
68  if (_needsUpdate) update();
69  return _markOffset;
70  }
71  double markGap() const
72  {
73  if (_needsUpdate) update();
74  return _markGap;
75  }
76  void update() const
77  {
78  double mappedMinGap = std::fabs(_minGap*(_max-_min)/_width);
79  _markGap=1;
80  if (mappedMinGap>=1)
81  {
82  while (true)
83  {
84  if (_markGap>=mappedMinGap) break;
85  if (_markGap*2>=mappedMinGap) {_markGap*=2; break;}
86  if (_markGap*5>=mappedMinGap) {_markGap*=5; break;}
87  _markGap*=10;
88  }
89  }
90  else
91  {
92  while (true)
93  {
94  if (_markGap<1e-8) {_markGap=1e-8; break;}
95  if (_markGap<mappedMinGap) {break;}
96  if (_markGap/2<mappedMinGap) {_markGap/=2; break;}
97  if (_markGap/5<mappedMinGap) {_markGap/=5; break;}
98  _markGap/=10;
99  }
100  }
101  double ceil = std::ceil(_min/_markGap);
102  _markOffset = (std::fabs(ceil)<1e-5)? 0. : ceil*_markGap;
103 
104  _needsUpdate = false;
105  }
107  double tickValue(unsigned i) const
108  {
109  if (_needsUpdate) update();
110  return _markOffset+_markGap*i;
111  }
113  double pixelTickPos(unsigned i) const
114  {
115  return toPixel(tickValue(i));
116  }
117  // Returns the pixel position of a given domain value
118  double toPixel(double value) const
119  {
120  return (value-_min)*_width/(_max-_min);
121  }
122  // Returns the number of ticks that will appear
123  unsigned nTicks() const
124  {
125  if (_needsUpdate) update();
126  return std::ceil((_max-_markOffset)/_markGap);
127  }
128 };
129 
130 } // namespace CLAM
131 
132 #endif//RulerTicks_hxx
133