Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

LogTransform.cxx

Go to the documentation of this file.
00001 
00012 #ifdef _MSC_VER
00013 #include "msdevstudio/MSconfig.h"
00014 #endif
00015 
00016 #include "transforms/LogTransform.h"
00017 
00018 #include "axes/AxisModelBase.h"
00019 #include "axes/AxisTick.h"
00020 
00021 #include <algorithm>
00022 #include <vector>
00023 
00024 #include <cmath>
00025 #include <cstdio>
00026 
00027 #include <cassert>
00028 
00029 #ifdef ITERATOR_MEMBER_DEFECT
00030 using namespace std;
00031 #else
00032 using std::abs;
00033 using std::log10;
00034 using std::pow;
00035 using std::transform;
00036 using std::vector;
00037 #endif
00038 
00039 LogTransform::LogTransform ()
00040   : UnaryTransform ( 10 * DBL_EPSILON, DBL_MAX )
00041 {
00042   m_name = "Log";
00043 }
00044 
00045 LogTransform::~LogTransform ()
00046 {
00047 }
00048 
00049 LogTransform::LogTransform ( const LogTransform & lt )
00050   : UnaryTransform ( lt )
00051 {
00052 }
00053 
00054 #ifdef CLONE_DEFECT
00055 TransformBase * LogTransform::clone () const
00056 #else
00057 LogTransform  * LogTransform::clone () const
00058 #endif
00059 {
00060   return new LogTransform ( *this );
00061 }
00062 
00063 bool
00064 LogTransform::
00065 isLinear () const
00066 {
00067   return false;
00068 }
00069 
00070 void LogTransform::transform ( double & x ) const
00071 {
00072   x = x <= DBL_EPSILON  ? -DBL_MAX : log10 ( x );
00073 }
00074 
00075 void LogTransform::inverseTransform ( double & x ) const
00076 {
00077   x = pow ( 10.0, x );
00078 }
00079 
00080 void
00081 LogTransform::
00082 transform ( std::vector < double > & x ) const
00083 {
00084 //  #ifdef TRANSFORM_DEFECT
00085   vector< double >::iterator first = x.begin();
00086   for ( ; first != x.end(); ++first ) {
00087     *first = *first <= DBL_EPSILON ? -DBL_MAX : log10 ( *first );
00088   }
00089 //  #else
00090 
00091   // The following doesn't work for MS BC++ nor gcc 3.0.  It does work
00092   // with older versions of gcc and Sun CC, however.  So, who is right?
00093 
00094 //    std::transform ( x.begin (), x.end (), x.begin (), log10 );
00095 //  #endif
00096 }
00097 
00098 /* virtual */
00099 void LogTransform::validate ( Range & range ) const
00100 {
00101   double lo = range.low ();
00102   double hi = range.high ();
00103 
00104   if ( hi <= 0.0 ) hi = 1.0;
00105 //   if ( lo <= 0.0 ) lo = hi - 0.99 * hi;
00106   if ( lo <= 0.0 ) lo = range.pos();
00107 
00108   range.setLow ( lo );
00109   range.setHigh ( hi );
00110 }
00111 
00112 /* virtual */
00113 const vector < AxisTick > &
00114 LogTransform::
00115 setTicks ( AxisModelBase & axis )
00116 {
00117   setTickStep( axis );
00118   setFirstTick( axis );
00119 
00120   return genTicks( axis );
00121 }
00122 
00123 void LogTransform::setTickStep( AxisModelBase & axis )
00124 {
00125   const Range & range = axis.getRange(true);
00126   double low = range.low();
00127   double high = range.high();
00128   double rangeMag = high / low;
00129   
00130   // The following algorithm determines the magnitude of the range.
00131   double rmag = floor( log10( rangeMag ) );
00132 
00133   // This is used to determine the first tick.
00134   double pmag = ceil( log10( low ) );
00135 
00136   // Now we find the magnitude between ticks, getting the minimum
00137   // number of ticks without going below 4.
00138   double tmag = floor( rmag / 3.0 );
00139 
00140   double tick_step = pow( 10.0, tmag );
00141 
00142   axis.setRMag( rmag );
00143   axis.setPMag( pmag );
00144   axis.setTickStep( tick_step );
00145   
00146 }
00147 
00148 void LogTransform::setFirstTick( AxisModelBase & axis )
00149 {
00150   const Range & range = axis.getRange(true);
00151   double low = range.low();
00152   
00153   //double high = range.high();
00154   
00155   // This sets the first tick as the low value rounded up to the
00156   // nearest power of 10.
00157   double pmag = axis.getPMag();
00158   double first_tick = pow( 10.0, pmag );
00159   double tmp = 0.0;
00160   while( ( tmp = prevStep( first_tick, axis )) >= low ) {
00161     first_tick = tmp;
00162   }
00163 
00164   axis.setFirstTick( first_tick );
00165 }
00166 
00171 inline double FLT_EQUAL( double x, double y )
00172 {
00173   return ( (double)abs( x - y ) <= 2.0 * ( y * FLT_EPSILON + FLT_MIN ) );
00174 }
00175 
00179 const vector < AxisTick > &
00180 LogTransform::
00181 genTicks ( AxisModelBase & axis )
00182 {
00183   double ylabel;
00184 
00185   int num_ticks = 0;
00186 
00187   m_ticks.clear ();
00188   
00189   // mag is used for scientific notation.
00190   double mag;
00191 
00192   char pstr[10];
00193   char labl[10];
00194   
00195   double first_tick = axis.getFirstTick();
00196   double tick_step  = axis.getTickStep();
00197   double scale_factor = axis.getScaleFactor();
00198   double max_ticks    = axis.getMaxTicks();
00199   const Range & range = axis.getRange(true);
00200 
00201   double range_low = range.low();
00202   double range_high = range.high();
00203 
00204   double last_tick = first_tick;//pow( 10.0, ceil( log10( range_high ) ) );
00205   double tmp = 0.0;
00206   while ( ( tmp = nextStep( last_tick, axis ) ) <= range_high ) {
00207     last_tick = tmp;
00208   }
00209 
00210   // The flag for scientific notation activation.
00211   bool sci_not = 
00212     ( floor( log10( last_tick ) ) > 3.0 ) ||
00213     ( floor ( log10 ( first_tick ) ) ) < -3.0;
00214   if( sci_not ) {
00215     sprintf( pstr, "%%1.0fe%%d" );
00216   }
00217 
00218   double value = first_tick / tick_step;
00219   bool fresh = true;
00220   while( value <= range_high || FLT_EQUAL( range_high, value ) ) {
00221     if( num_ticks >= max_ticks ) {
00222       // So far, this has only occurred for empty histograms. The
00223       //easy fix was to do nothing, but there ought to be a better
00224       // way to handle this.
00225       return m_ticks;
00226     }
00227 
00228     if( !fresh ) {
00229       value = nextStep( value, axis );
00230     } else {
00231       value *= tick_step;
00232       fresh = false;
00233     }
00234 
00235     if( value > range_high ) break;
00236     if( sci_not ) {
00237       mag = floor( log10( value ) );
00238       ylabel = value / pow( 10.0, mag );
00239       sprintf( pstr, "%%1.0fe%%d" );
00240       sprintf( labl, pstr, ylabel, static_cast<int>( mag ) );
00241     } else {
00242       ylabel = value;
00243       double tmp = floor( log10( value ) );
00244       if( tmp > 0.0 ) tmp = 0.0;
00245       tmp = fabs( tmp );
00246       sprintf( pstr, "%%1.%df", static_cast<int>( tmp ) );
00247       sprintf( labl, pstr, ylabel );
00248     } 
00249     
00250     double y = value / scale_factor;
00251     m_ticks.push_back( AxisTick ( y, labl ) );
00252     
00253     num_ticks++;
00254   }
00255 
00256   if ( num_ticks < 3 )
00257     {
00258 
00259       m_ticks.clear();
00260       
00261       double xx = (log10(range_high) - log10(range_low)) / 4;
00262       double yy = log10(range_low);
00263     
00264       for(int i=1; i<4; i++)
00265         {
00266         
00267           value = pow (10.0, xx * i + yy);
00268           
00269           if( value > range_high ) continue;
00270           
00271           double tmp = floor( log10( value ) );
00272           if( tmp > 0.0 ) tmp = 0.0;
00273           tmp = fabs( tmp );
00274           if (tmp == 0.0)
00275             {
00276               value = floor(value);
00277             }
00278       
00279           sprintf( pstr, "%%1.%df", static_cast<int>( tmp ) );
00280           sprintf( labl, pstr, value); 
00281       
00282           double y = value / scale_factor;
00283           
00284           m_ticks.push_back( AxisTick ( y, labl ) );
00285         }
00286     }
00287 
00288   return m_ticks;
00289 }
00290 
00291 double LogTransform::nextStep ( double current, AxisModelBase & axis )
00292 {
00293   double tick_step = axis.getTickStep(); // Must already be called
00294   if( tick_step == 1.0 ) {
00295     int bottom = static_cast<int>( current /
00296                                  pow( 10.0, floor( log10( current ) ) ) );
00297     // Look! I used a switch statement in C++!!!!!  What this does
00298     // is go through and add the intermediate 2 and 5 ticks if the
00299     // powers of 10 alone would not have given the minimum number of
00300     // ticks.  m_tick_step is completely ignored if the flag is
00301     // true, since it is assumed to be 0.
00302     
00305     switch( bottom ) {
00306     case 1:
00307       current *= 2.0;
00308       break;
00309     case 2:
00310       current /= 2.0;
00311       current *= 5.0;
00312       break;
00313     case 3:
00314       current /= 4.0;
00315       current *= 5.0;
00316       break;
00317     case 4: // a 5 becomes a 4 sometimes because of round of error
00318     case 5:
00319       current *= 2.0;
00320       break;
00321     default:
00322       current *= 2.0;
00323 //       assert ( false );
00324     }
00325   } else {
00326     current *= tick_step;
00327   }
00328   return current;
00329 }
00330 
00333 double LogTransform::prevStep ( double current, AxisModelBase & axis )
00334 {
00335   double tick_step = axis.getTickStep(); // It must already be called.
00336   if( tick_step == 1.0 ) {
00337     int base = static_cast<int>( current /
00338                                  pow( 10.0, floor( log10( current ) ) ) );
00339     // Look! I used a switch statement in C++!!!!!  What this does
00340     // is go through and add the intermediate 2 and 5 ticks if the
00341     // powers of 10 alone would not have given the minimum number of
00342     // ticks.  m_tick_step is completely ignored if the flag is
00343     // true, since it is assumed to be 0.
00344     
00345     switch( base ) {
00346     case 1:
00347       current /= 2.0;
00348       break;
00349     case 2:
00350       current /= 2.0;
00351       break;
00352     case 4:
00353       current /= 5.0;
00354       current *= 2.0;
00355       break;
00356     case 5:
00357       current /= 5.0;
00358       current *= 2.0;
00359       break;
00360     default:
00361       assert ( false );
00362     }
00363   } else {
00364     current /= tick_step;
00365   }
00366   return current;
00367 }
00368 
00369 const Range &
00370 LogTransform::adjustValues ( AxisModelBase & axis, const Range & limit )
00371 {
00372   //Because the low value, the high value, and the length value of the
00373   //range were so frequently used, I added those three fields. There 
00374   //should be an improvement in performance.
00375   
00376   double mylow, myhigh;
00377 
00378   // We want to make sure that this is autoscaled. Therefore, to 
00379   // be on the safe side we set the minimum range to 0, so that 
00380   // the minimum positive value is used.
00381   //   Range log( 0.0, getRange().high(), getRange().pos() );
00382   //   setRange( log );
00383   
00384   adjustLogValues( axis );
00385   setTickStep( axis ); // For tick steps are needed by nextStep() & prevStep().
00386 
00387   const Range & init_range = axis.getRange(false);
00388   double low = init_range.low();
00389   double high = init_range.high();
00390 
00391   myhigh = mylow = pow( 10.0, axis.getPMag() );
00392 
00393   // This decreases mylow so that "myrange" covers the whole range
00394   // and then some.
00395   double scale_factor = axis.getScaleFactor();
00396   while( mylow >= low * scale_factor ) {
00397     mylow = prevStep( mylow, axis );
00398   }
00399   
00400   // This increases myhigh so that "myrange" covers the whole range
00401   // and then some.
00402   while( myhigh <= high * scale_factor ) {
00403     myhigh = nextStep( myhigh, axis );
00404   }
00405   
00406   // If the range has a magnitude < 10.0, reduce the minimum of the
00407   // range by one tick mark.
00408   if( myhigh / mylow < 10.0 ) {
00409     mylow = prevStep( mylow, axis );
00410   }
00411   
00412   // If the range still has a magnitude < 10.0, increase the maximum
00413   // of the range by one tick mark until the magnitude is 10.0.
00414   while( myhigh / mylow < 10.0 ) {
00415     myhigh = nextStep( myhigh, axis );
00416   }
00417 
00418   myhigh /= scale_factor;
00419   mylow  /= scale_factor;
00420 
00421   Range new_range ( mylow, myhigh, init_range.pos() );
00422   
00423   // Compare the newrange with init_range. If new range is too wide
00424   // compared to init_range, then do not set newrange.
00425 
00426   double new_width = new_range.length();
00427   double init_width = init_range.length();
00428   
00429   if ( new_width > init_width * 10 ){  // This 10 is a hack. Could be any
00430                                        // decent number.
00431     if ( low < 0 ) {
00432       low *= 1.05;    // This 5% is also a hack.
00433     }
00434     else{
00435       low *= 0.95;
00436     }
00437     
00438     if ( high < 0 ){
00439       high *= 0.95;
00440     }
00441     else{
00442       high *= 1.05;
00443     }
00444     
00445     Range newRange ( low, high, init_range.pos() );
00446     axis.setIntersectRange ( newRange, limit );
00447     return axis.getRange( false );
00448   
00449   }
00450 
00451   axis.setIntersectRange ( new_range, limit );
00452 
00453   return axis.getRange( false );
00454 }
00455 
00456 
00457 const Range &  LogTransform::adjustLogValues ( AxisModelBase & axis )
00458 {
00459   const Range & r = axis.getRange( false );
00460   double low = r.low();
00461   double high = r.high();
00462   double pos = r.pos();
00463   
00464   if( low > 0.0 ) return r;
00465 
00466   if( pos == high ) { // Will give no range
00467     double l = pos / 10.0;
00468     double h = pos * 10.0;
00469     axis.setRange ( l, h, pos );
00470 
00471     return axis.getRange( false );
00472   }
00473   if( pos == DBL_MAX || pos <= 0.0 ) { // No positive values!!!
00474     axis.setRange ( 0.01, 100.0, 1.0 );
00475     return axis.getRange( false );
00476   }
00477   if ( low <= 0.0 ) axis.setRange ( 0.5 * pos, high, pos );
00478   else  axis.setRange ( pos, high, pos );
00479 
00480   return axis.getRange( false );
00481 }

Generated for HippoDraw-1.14.8.5 by doxygen 1.4.3