LogTransform.cxx
Go to the documentation of this file.
1 
12 #ifdef _MSC_VER
13 #include "msdevstudio/MSconfig.h"
14 #endif
15 
17 
18 #include "axes/AxisModelBase.h"
19 #include "axes/AxisTick.h"
20 
21 #include <algorithm>
22 #include <vector>
23 
24 #include <cmath>
25 #include <cstdio>
26 
27 #include <cassert>
28 
29 #ifdef ITERATOR_MEMBER_DEFECT
30 using namespace std;
31 #else
32 using std::abs;
33 using std::log10;
34 using std::pow;
35 using std::transform;
36 using std::vector;
37 #endif
38 
39 using namespace hippodraw;
40 
41 LogTransform::LogTransform ()
42  : UnaryTransform ( 10 * DBL_MIN, DBL_MAX )
43 {
44  m_name = "Log";
45 }
46 
48 {
49 }
50 
52  : UnaryTransform ( lt )
53 {
54 }
55 
56 #ifdef CLONE_DEFECT
58 #else
60 #endif
61 {
62  return new LogTransform ( *this );
63 }
64 
65 bool
67 isLinear () const
68 {
69  return false;
70 }
71 
72 void LogTransform::transform ( double & x ) const
73 {
74  x = log10 ( x );
75 }
76 
77 void LogTransform::inverseTransform ( double & x ) const
78 {
79  x = pow ( 10.0, x );
80 }
81 
82 void
84 transform ( std::vector < double > & x ) const
85 {
86 // #ifdef TRANSFORM_DEFECT
87  vector< double >::iterator first = x.begin();
88  for ( ; first != x.end(); ++first ) {
89  *first = log10 ( *first );
90  }
91 // #else
92 
93  // The following doesn't work for MS BC++ nor gcc 3.0. It does work
94  // with older versions of gcc and Sun CC, however. So, who is right?
95 
96 // std::transform ( x.begin (), x.end (), x.begin (), log10 );
97 // #endif
98 }
99 
100 /* virtual */
101 void LogTransform::validate ( Range & range ) const
102 {
103  double lo = range.low ();
104  double hi = range.high ();
105 
106  if ( hi <= 0.0 ) hi = 1.0;
107 // if ( lo <= 0.0 ) lo = hi - 0.99 * hi;
108  if ( lo <= 0.0 ) lo = range.pos();
109 
110  range.setLow ( lo );
111  range.setHigh ( hi );
112 }
113 
114 /* virtual */
115 const vector < AxisTick > &
118 {
119  setTickStep( axis );
120  setFirstTick( axis );
121 
122  return genTicks( axis );
123 }
124 
126 {
127  const Range & range = axis.getRange(true);
128  double low = range.low();
129  double high = range.high();
130  double rangeMag = high / low;
131 
132  // The following algorithm determines the magnitude of the range.
133  double rmag = floor( log10( rangeMag ) );
134 
135  // This is used to determine the first tick.
136  double pmag = ceil( log10( low ) );
137 
138  // Now we find the magnitude between ticks, getting the minimum
139  // number of ticks without going below 4.
140  double tmag = floor( rmag / 3.0 );
141 
142  double tick_step = pow( 10.0, tmag );
143 
144  axis.setRMag( rmag );
145  axis.setPMag( pmag );
146  axis.setTickStep( tick_step );
147 
148 }
149 
151 {
152  const Range & range = axis.getRange(true);
153  double low = range.low();
154 
155  //double high = range.high();
156 
157  // This sets the first tick as the low value rounded up to the
158  // nearest power of 10.
159  double pmag = axis.getPMag();
160  double first_tick = pow( 10.0, pmag );
161  double tmp = 0.0;
162  while( ( tmp = prevStep( first_tick, axis )) >= low ) {
163  first_tick = tmp;
164  }
165 
166  axis.setFirstTick( first_tick );
167 }
168 
173 inline double FLT_EQUAL( double x, double y )
174 {
175  return ( (double)abs( x - y ) <= 2.0 * ( y * FLT_EPSILON + FLT_MIN ) );
176 }
177 
181 const vector < AxisTick > &
184 {
185  double ylabel;
186 
187  int num_ticks = 0;
188 
189  m_ticks.clear ();
190 
191  // mag is used for scientific notation.
192  double mag;
193 
194  char pstr[10];
195  char labl[10];
196 
197  double first_tick = axis.getFirstTick();
198  double tick_step = axis.getTickStep();
199  double scale_factor = axis.getScaleFactor();
200  double max_ticks = axis.getMaxTicks();
201  const Range & range = axis.getRange(true);
202 
203  double range_low = range.low();
204  double range_high = range.high();
205 
206  double last_tick = first_tick;//pow( 10.0, ceil( log10( range_high ) ) );
207  double tmp = 0.0;
208  while ( ( tmp = nextStep( last_tick, axis ) ) <= range_high ) {
209  last_tick = tmp;
210  }
211 
212  // The flag for scientific notation activation.
213  bool sci_not =
214  ( floor( log10( last_tick ) ) > 3.0 ) ||
215  ( floor ( log10 ( first_tick ) ) ) < -3.0;
216  if( sci_not ) {
217  sprintf( pstr, "%%1.0fe%%d" );
218  }
219 
220  double value = first_tick / tick_step;
221  bool fresh = true;
222  while( value <= range_high || FLT_EQUAL( range_high, value ) ) {
223  if( num_ticks >= max_ticks ) {
224  // So far, this has only occurred for empty histograms. The
225  //easy fix was to do nothing, but there ought to be a better
226  // way to handle this.
227  return m_ticks;
228  }
229 
230  if( !fresh ) {
231  value = nextStep( value, axis );
232  } else {
233  value *= tick_step;
234  fresh = false;
235  }
236 
237  if( value > range_high ) break;
238  if( sci_not ) {
239  mag = floor( log10( value ) );
240  ylabel = value / pow( 10.0, mag );
241  sprintf( pstr, "%%1.0fe%%d" );
242  sprintf( labl, pstr, ylabel, static_cast<int>( mag ) );
243  } else {
244  ylabel = value;
245  double tmp = floor( log10( value ) );
246  if( tmp > 0.0 ) tmp = 0.0;
247  tmp = fabs( tmp );
248  sprintf( pstr, "%%1.%df", static_cast<int>( tmp ) );
249  sprintf( labl, pstr, ylabel );
250  }
251 
252  double y = value / scale_factor;
253  m_ticks.push_back( AxisTick ( y, labl ) );
254 
255  num_ticks++;
256  }
257 
258  if ( num_ticks < 3 )
259  {
260 
261  m_ticks.clear();
262 
263  double xx = (log10(range_high) - log10(range_low)) / 4;
264  double yy = log10(range_low);
265 
266  for(int i=1; i<4; i++)
267  {
268 
269  value = pow (10.0, xx * i + yy);
270 
271  if( value > range_high ) continue;
272 
273  double tmp = floor( log10( value ) );
274  if( tmp > 0.0 ) tmp = 0.0;
275  tmp = fabs( tmp );
276  if (tmp == 0.0)
277  {
278  value = floor(value);
279  }
280 
281  sprintf( pstr, "%%1.%df", static_cast<int>( tmp ) );
282  sprintf( labl, pstr, value);
283 
284  double y = value / scale_factor;
285 
286  m_ticks.push_back( AxisTick ( y, labl ) );
287  }
288  }
289 
290  return m_ticks;
291 }
292 
293 double LogTransform::nextStep ( double current, AxisModelBase & axis )
294 {
295  double tick_step = axis.getTickStep(); // Must already be called
296  if( tick_step == 1.0 ) {
297  int bottom = static_cast<int>( current /
298  pow( 10.0, floor( log10( current ) ) ) );
299  // Look! I used a switch statement in C++!!!!! What this does
300  // is go through and add the intermediate 2 and 5 ticks if the
301  // powers of 10 alone would not have given the minimum number of
302  // ticks. m_tick_step is completely ignored if the flag is
303  // true, since it is assumed to be 0.
304 
307  switch( bottom ) {
308  case 1:
309  current *= 2.0;
310  break;
311  case 2:
312  current /= 2.0;
313  current *= 5.0;
314  break;
315  case 3:
316  current /= 4.0;
317  current *= 5.0;
318  break;
319  case 4: // a 5 becomes a 4 sometimes because of round of error
320  case 5:
321  current *= 2.0;
322  break;
323  default:
324  current *= 2.0;
325 // assert ( false );
326  }
327  } else {
328  current *= tick_step;
329  }
330  return current;
331 }
332 
335 double LogTransform::prevStep ( double current, AxisModelBase & axis )
336 {
337  double tick_step = axis.getTickStep(); // It must already be called.
338  if( tick_step == 1.0 ) {
339  int base = static_cast<int>( current /
340  pow( 10.0, floor( log10( current ) ) ) );
341  // Look! I used a switch statement in C++!!!!! What this does
342  // is go through and add the intermediate 2 and 5 ticks if the
343  // powers of 10 alone would not have given the minimum number of
344  // ticks. m_tick_step is completely ignored if the flag is
345  // true, since it is assumed to be 0.
346 
347  switch( base ) {
348  case 1:
349  current /= 2.0;
350  break;
351  case 2:
352  current /= 2.0;
353  break;
354  case 4:
355  current /= 5.0;
356  current *= 2.0;
357  break;
358  case 5:
359  current /= 5.0;
360  current *= 2.0;
361  break;
362  default:
363  assert ( false );
364  }
365  } else {
366  current /= tick_step;
367  }
368  return current;
369 }
370 
371 const Range &
373 {
374  //Because the low value, the high value, and the length value of the
375  //range were so frequently used, I added those three fields. There
376  //should be an improvement in performance.
377 
378  double mylow, myhigh;
379 
380  // We want to make sure that this is autoscaled. Therefore, to
381  // be on the safe side we set the minimum range to 0, so that
382  // the minimum positive value is used.
383  // Range log( 0.0, getRange().high(), getRange().pos() );
384  // setRange( log );
385 
386  adjustLogValues( axis );
387  setTickStep( axis ); // For tick steps are needed by nextStep() & prevStep().
388 
389  const Range & init_range = axis.getRange(false);
390  double low = init_range.low();
391  double high = init_range.high();
392 
393  myhigh = mylow = pow( 10.0, axis.getPMag() );
394 
395  // This decreases mylow so that "myrange" covers the whole range
396  // and then some.
397  double scale_factor = axis.getScaleFactor();
398  while( mylow >= low * scale_factor ) {
399  mylow = prevStep( mylow, axis );
400  }
401 
402  // This increases myhigh so that "myrange" covers the whole range
403  // and then some.
404  while( myhigh <= high * scale_factor ) {
405  myhigh = nextStep( myhigh, axis );
406  }
407 
408  // If the range has a magnitude < 10.0, reduce the minimum of the
409  // range by one tick mark.
410  if( myhigh / mylow < 10.0 ) {
411  mylow = prevStep( mylow, axis );
412  }
413 
414  // If the range still has a magnitude < 10.0, increase the maximum
415  // of the range by one tick mark until the magnitude is 10.0.
416  while( myhigh / mylow < 10.0 ) {
417  myhigh = nextStep( myhigh, axis );
418  }
419 
420  myhigh /= scale_factor;
421  mylow /= scale_factor;
422 
423  Range new_range ( mylow, myhigh, init_range.pos() );
424 
425  // Compare the newrange with init_range. If new range is too wide
426  // compared to init_range, then do not set newrange.
427 
428  double new_width = new_range.length();
429  double init_width = init_range.length();
430 
431  if ( new_width > init_width * 10 ){ // This 10 is a hack. Could be any
432  // decent number.
433  if ( low < 0 ) {
434  low *= 1.05; // This 5% is also a hack.
435  }
436  else{
437  low *= 0.95;
438  }
439 
440  if ( high < 0 ){
441  high *= 0.95;
442  }
443  else{
444  high *= 1.05;
445  }
446 
447  Range newRange ( low, high, init_range.pos() );
448  axis.setIntersectRange ( newRange, limit );
449  return axis.getRange( false );
450 
451  }
452 
453  axis.setIntersectRange ( new_range, limit );
454 
455  return axis.getRange( false );
456 }
457 
458 
460 {
461  const Range & r = axis.getRange( false );
462  double low = r.low();
463  double high = r.high();
464  double pos = r.pos();
465 
466  if( low > 0.0 ) return r;
467 
468  if( pos == high ) { // Will give no range
469  double l = pos / 10.0;
470  double h = pos * 10.0;
471  axis.setRange ( l, h, pos );
472 
473  return axis.getRange( false );
474  }
475  if( pos == DBL_MAX || pos <= 0.0 ) { // No positive values!!!
476  axis.setRange ( 0.01, 100.0, 1.0 );
477  return axis.getRange( false );
478  }
479  if ( low <= 0.0 ) axis.setRange ( 0.5 * pos, high, pos );
480  else axis.setRange ( pos, high, pos );
481 
482  return axis.getRange( false );
483 }

Generated for HippoDraw Class Library by doxygen