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 }

Generated for HippoDraw Class Library by doxygen