AxisModelLog.cxx
Go to the documentation of this file.
1 
16 #ifdef _MSC_VER
17 // Includes max() and min() missing from MicroSoft Visual C++.
18 #include "msdevstudio/MSconfig.h"
19 #endif //_MSC_VER
20 
21 #include "AxisModelLog.h"
22 
23 #include "AxisTick.h"
24 
25 #include <algorithm>
26 
27 #include <cassert>
28 #include <cmath>
29 #include <cstdio>
30 
31 using std::abs;
32 using std::max;
33 using std::min;
34 using std::string;
35 using std::vector;
36 
37 using namespace hippodraw;
38 
40  AxisLoc scale_loc )
41  : AxisModelBase ( label_loc, scale_loc )
42 {
43 }
44 
46  : AxisModelBase ( axis_model )
47 {
48  // We just changed to log. Therefore assure positive minimum.
50 }
51 
53 {
54 }
55 
56 /* virtual */
58 {
59  return new AxisModelLog( *this );
60 }
61 bool AxisModelLog::isLog() const
62 {
63  return true;
64 }
65 
67 {
68  const Range & range = getRange(true);
69  double low = range.low();
70  double high = range.high();
71  double rangeMag = high / low;
72 
73  // The following algorithm determines the magnitude of the range.
74  m_rmag = floor( log10( rangeMag ) );
75 
76  // This is used to determine the first tick.
77  m_pmag = ceil( log10( low ) );
78 
79  // Now we find the magnitude between ticks, getting the minimum
80  // number of ticks without going below 4.
81  double m_tmag = floor( m_rmag / 3.0 );
82 
83  m_tick_step = pow( 10.0, m_tmag );
84 }
85 
86 const Range &
88 {
89 
90  //Because the low value, the high value, and the length value of the
91  //range were so frequently used, I added those three fields. There
92  //should be an improvement in performance.
93 
94  double mylow, myhigh;
95 
96  // We want to make sure that this is autoscaled. Therefore, to
97  // be on the safe side we set the minimum range to 0, so that
98  // the minimum positive value is used.
99  // Range log( 0.0, getRange().high(), getRange().pos() );
100  // setRange( log );
101 
102  adjustLogValues();
103  setTickStep(); // Needed for nextStep() and prevStep().
104 
105  const Range & init_range = getRange(false);
106  double low = init_range.low();
107  double high = init_range.high();
108 
109  myhigh = mylow = pow( 10.0, m_pmag );
110 
111  // This decreases mylow so that "myrange" covers the whole range
112  // and then some.
113  while( mylow >= low * m_scale_factor ) {
114  mylow = prevStep( mylow );
115  }
116 
117  // This increases myhigh so that "myrange" covers the whole range
118  // and then some.
119  while( myhigh <= high * m_scale_factor ) {
120  myhigh = nextStep( myhigh );
121  }
122 
123  // If the range has a magnitude < 10.0, reduce the minimum of the
124  // range by one tick mark.
125  if( myhigh / mylow < 10.0 ) {
126  mylow = prevStep( mylow );
127  }
128 
129  // If the range still has a magnitude < 10.0, increase the maximum
130  // of the range by one tick mark until the magnitude is 10.0.
131  while( myhigh / mylow < 10.0 ) {
132  myhigh = nextStep( myhigh );
133  }
134 
135  myhigh /= m_scale_factor;
136  mylow /= m_scale_factor;
137 
138  Range new_range ( mylow, myhigh, init_range.pos() );
139 
140  // Compare the newrange with init_range. If new range is too wide
141  // compared to init_range, then do not set newrange.
142 
143  double new_width = new_range.length();
144  double init_width = init_range.length();
145 
146  if ( new_width > init_width * 10 ){ // This 10 is a hack. Could be any
147  // decent number.
148  if ( low < 0 ) {
149  low *= 1.05; // This 5% is also a hack.
150  }
151  else{
152  low *= 0.95;
153  }
154 
155  if ( high < 0 ){
156  high *= 0.95;
157  }
158  else{
159  high *= 1.05;
160  }
161 
162  Range newRange ( low, high, init_range.pos() );
163  setIntersectRange ( newRange, limit );
164  return m_range;
165 
166  }
167 
168  setIntersectRange ( new_range, limit );
169  return m_range;
170 
171  // // The following sets the range too wide. Oded, what did you have
172  // // in mind with this?
173  // mylow = getRange(false).low() * sqrt( getRange(false).low() /
174  // getRange(false).high() );
175  // myhigh = getRange(false).high() * sqrt( getRange(false).high() /
176  // getRange(false).low() );
177  // Range range(mylow, myhigh, getRange(false).pos());
178  // setIntersectRange ( range, limit );
179  // return m_range;
180 
181 }
182 
183 const Range &
185 {
186  const Range & r = getRange(false);
187  double low = r.low();
188  double high = r.high();
189  double pos = r.pos();
190 
191  if( low > 0.0 ) return r;
192 
193  if( pos == high ) { // Will give no range
194 // setRange ( pos / 10.0, pos * 10.0, pos );
195  double l = pos / 10.0;
196  double h = pos * 10.0;
197  setRange ( l, h, pos );
198 
199  return m_range;
200  }
201  if( pos == DBL_MAX || pos <= 0.0 ) { // No positive values!!!
202  setRange ( 0.01, 100.0, 1.0 );
203  return m_range;
204  }
205  setRange ( pos, high, pos );
206 
207  return m_range;
208 }
209 
210 double AxisModelLog::nextStep ( double current )
211 {
212  double tick_step = getTickStep(); // Must already be called
213  if( tick_step == 1.0 ) {
214  int base = static_cast<int>( current /
215  pow( 10.0, floor( log10( current ) ) ) );
216  // Look! I used a switch statement in C++!!!!! What this does
217  // is go through and add the intermediate 2 and 5 ticks if the
218  // powers of 10 alone would not have given the minimum number of
219  // ticks. m_tick_step is completely ignored if the flag is
220  // true, since it is assumed to be 0.
221 
224  switch( base ) {
225  case 1:
226  current *= 2.0;
227  break;
228  case 2:
229  current /= 2.0;
230  current *= 5.0;
231  break;
232  case 3:
233  current /= 4.0;
234  current *= 5.0;
235  break;
236  case 4: // a 5 becomes a 4 sometimes because of round of error
237  case 5:
238  current *= 2.0;
239  break;
240  default:
241  assert ( false );
242  }
243  } else {
244  current *= tick_step;
245  }
246  return current;
247 }
248 
251 double AxisModelLog::prevStep ( double current )
252 {
253  double tick_step = getTickStep(); // It must already be called.
254  if( tick_step == 1.0 ) {
255  int base = static_cast<int>( current /
256  pow( 10.0, floor( log10( current ) ) ) );
257  // Look! I used a switch statement in C++!!!!! What this does
258  // is go through and add the intermediate 2 and 5 ticks if the
259  // powers of 10 alone would not have given the minimum number of
260  // ticks. m_tick_step is completely ignored if the flag is
261  // true, since it is assumed to be 0.
262 
263  switch( base ) {
264  case 1:
265  current /= 2.0;
266  break;
267  case 2:
268  current /= 2.0;
269  break;
270  case 4:
271  current /= 5.0;
272  current *= 2.0;
273  break;
274  case 5:
275  current /= 5.0;
276  current *= 2.0;
277  break;
278  default:
279  assert ( false );
280  }
281  } else {
282  current /= tick_step;
283  }
284  return current;
285 }
286 
290 Range AxisModelLog::calcLow ( int parm, bool dragging )
291 {
292  startDragging ( dragging );
293 
294  double low = m_start_range.low ();
295  double high = m_start_range.high ();
296  double k = log10 ( high / low );
297 
298  double x = ( parm - 50 ) / 50.0;
299 
300  double new_low = low * pow ( 10.0, k * x );
301 
302  new_low = max ( new_low, 10.0 * DBL_EPSILON );
303  new_low = min ( new_low, high - 100.0 * DBL_EPSILON );
304 
305  if( abs( new_low - m_range.high() ) < 0.0001 ) return m_range;
306 
307  return Range ( new_low, high, m_range.pos() );
308 }
309 
313 Range AxisModelLog::calcHigh ( int parm, bool dragging )
314 {
315  startDragging ( dragging );
316 
317  double low = m_start_range.low ();
318  double high = m_start_range.high ();
319  double k = log10 ( high / low );
320 
321  double multiplier = ( parm - 50 ) / 50.0;
322 
323  double new_high = high * pow ( 10.0, k * multiplier );
324 
325  if( abs( new_high - m_range.low() ) < 0.0001 ) return m_range;
326 
327  return Range ( low, new_high, m_range.pos() );
328 }
double high() const
Returns the maximum of the range object.
Definition: Range.cxx:100
double m_rmag
The value of the power of ten of the range to be used to determine how many decimal places are needed...
Definition: AxisModelBase.h:97
double nextStep(double cur)
This returns the next step for the ticks.
virtual const Range & adjustValues(const Range &)
Adjusts the range on the axis to nice values within the Range limit.
Range m_start_range
The starting range before start of dragging events.
Definition: AxisModelBase.h:46
virtual AxisModelBase * clone() const
The clone function returns an object of its own kind which is a copy of this object at this moment...
void setRange(double low, double high, double pos)
Sets the Range to the low and high values.
AxisModelLog class interface.
AxisModelLog(AxisLoc label, AxisLoc scale)
The constructor sets the location of the ticks, labels, and scale.
virtual Range calcHigh(int parm, bool dragging=false)
AxisLoc
The base class for the binner hierarchy.
Definition: AxisLoc.h:17
virtual Range calcLow(int parm, bool dragging=false)
virtual const Range & adjustLogValues()
This function makes log values logical on a logarithmic scale.
The AxisModelBase class maintains the Range and scaling of an axis.
Definition: AxisModelBase.h:33
double length() const
Returns the length of the range object.
Definition: Range.h:156
virtual void setTickStep()
Sets the tick step, and the magnitude power magnitude (the greatest magnitude that either the maximum...
~AxisModelLog()
The destructor.
double m_tick_step
The distance between two consecutive ticks.
Definition: AxisModelBase.h:74
void startDragging(bool dragging)
Sets the member m_start_dragging to the current range if dragging is starting.
double m_pmag
The value of the power of ten to be used to multiply the tick labels.
Definition: AxisModelBase.h:88
const Range & getRange(bool scaled) const
Returns the range represented by this AxisModel.
double low() const
Returns the minimum of the range object.
Definition: Range.cxx:87
Expresses a range of values.
Definition: Range.h:33
double prevStep(double cur)
This returns the previous step for the ticks.
bool isLog() const
Returns true because this class represents a log scale.
Range m_range
The current range of the axis.
Definition: AxisModelBase.h:38
void setIntersectRange(const Range &, const Range &)
Sets the Range to overlap of the two ranges.
double pos() const
Returns the first positive element in range.
Definition: Range.cxx:113
AxisTick class interface.
double getTickStep() const
Returns the tick step in the true coordinate system.
double m_scale_factor
The scale factor.
Definition: AxisModelBase.h:62

Generated for HippoDraw Class Library by doxygen