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

AxisModelLog.cxx

Go to the documentation of this file.
00001 
00015 #ifdef _MSC_VER
00016 // Includes max() and min() missing from MicroSoft Visual C++.
00017 #include "msdevstudio/MSconfig.h"
00018 #endif //_MSC_VER
00019 
00020 #include "AxisModelLog.h"
00021 
00022 #include "AxisTick.h"
00023 
00024 #include <algorithm>
00025 
00026 #include <cassert>
00027 #include <cmath>
00028 #include <cstdio>
00029 
00030 using std::abs;
00031 using std::max;
00032 using std::min;
00033 using std::string;
00034 using std::vector;
00035 
00036 AxisModelLog::AxisModelLog ( AxisLoc label_loc, 
00037                              AxisLoc scale_loc )
00038   : AxisModelBase ( label_loc, scale_loc )
00039 {    
00040 }
00041 
00042 AxisModelLog::AxisModelLog ( const AxisModelBase & axis_model )
00043   : AxisModelBase ( axis_model )
00044 {
00045   // We just changed to log.  Therefore assure positive minimum.
00046   adjustLogValues();
00047 }
00048 
00049 AxisModelLog::~AxisModelLog()
00050 {
00051 }
00052 
00053 /* virtual */
00054 AxisModelBase * AxisModelLog::clone() const
00055 {
00056   return new AxisModelLog( *this );
00057 }
00058 
00059 inline double FLT_EQUAL( double x, double y )
00060 {
00061   return ( (double)fabs( x - y ) <= 2.0 * ( y * FLT_EPSILON + FLT_MIN ) );
00062 }
00063 
00064 bool AxisModelLog::isLog() const 
00065 { 
00066   return true; 
00067 }
00068 
00069 void AxisModelLog::setTickStep()
00070 {
00071   const Range & range = getRange(true);
00072   double low = range.low();
00073   double high = range.high();
00074   double rangeMag = high / low;
00075   
00076   // The following algorithm determines the magnitude of the range.
00077   m_rmag = floor( log10( rangeMag ) );
00078 
00079   // This is used to determine the first tick.
00080   m_pmag = ceil( log10( low ) );
00081 
00082   // Now we find the magnitude between ticks, getting the minimum
00083   // number of ticks without going below 4.
00084   double m_tmag = floor( m_rmag / 3.0 );
00085 
00086   m_tick_step = pow( 10.0, m_tmag );
00087 }
00088 
00089 const Range & 
00090 AxisModelLog::adjustValues ( const Range & limit )
00091 {
00092 
00093   //Because the low value, the high value, and the length value of the
00094   //range were so frequently used, I added those three fields. There 
00095   //should be an improvement in performance.
00096   
00097   double mylow, myhigh;
00098 
00099   // We want to make sure that this is autoscaled. Therefore, to 
00100   // be on the safe side we set the minimum range to 0, so that 
00101   // the minimum positive value is used.
00102   //   Range log( 0.0, getRange().high(), getRange().pos() );
00103   //   setRange( log );
00104   
00105   adjustLogValues();
00106   setTickStep(); // Needed for nextStep() and prevStep().
00107 
00108   const Range & init_range = getRange(false);
00109   double low = init_range.low();
00110   double high = init_range.high();
00111 
00112   myhigh = mylow = pow( 10.0, m_pmag );
00113 
00114   // This decreases mylow so that "myrange" covers the whole range
00115   // and then some.
00116   while( mylow >= low * m_scale_factor ) {
00117     mylow = prevStep( mylow );
00118   }
00119   
00120   // This increases myhigh so that "myrange" covers the whole range
00121   // and then some.
00122   while( myhigh <= high * m_scale_factor ) {
00123     myhigh = nextStep( myhigh );
00124   }
00125   
00126   // If the range has a magnitude < 10.0, reduce the minimum of the
00127   // range by one tick mark.
00128   if( myhigh / mylow < 10.0 ) {
00129     mylow = prevStep( mylow );
00130   }
00131   
00132   // If the range still has a magnitude < 10.0, increase the maximum
00133   // of the range by one tick mark until the magnitude is 10.0.
00134   while( myhigh / mylow < 10.0 ) {
00135     myhigh = nextStep( myhigh );
00136   }
00137 
00138   myhigh /= m_scale_factor;
00139   mylow  /= m_scale_factor;
00140 
00141   Range new_range ( mylow, myhigh, init_range.pos() );
00142   
00143   // Compare the newrange with init_range. If new range is too wide
00144   // compared to init_range, then do not set newrange.
00145 
00146   double new_width = new_range.length();
00147   double init_width = init_range.length();
00148   
00149   if ( new_width > init_width * 10 ){  // This 10 is a hack. Could be any
00150                                        // decent number.
00151     if ( low < 0 ) {
00152       low *= 1.05;    // This 5% is also a hack.
00153     }
00154     else{
00155       low *= 0.95;
00156     }
00157     
00158     if ( high < 0 ){
00159       high *= 0.95;
00160     }
00161     else{
00162       high *= 1.05;
00163     }
00164     
00165     Range newRange ( low, high, init_range.pos() );
00166     setIntersectRange ( newRange, limit );
00167     return m_range;
00168   
00169   }
00170 
00171   setIntersectRange ( new_range, limit );
00172   return m_range;
00173   
00174   //  // The following sets the range too wide.  Oded, what did you have
00175   //  // in mind with this?
00176   //  mylow = getRange(false).low() * sqrt( getRange(false).low() / 
00177   //                                      getRange(false).high()     );
00178   //  myhigh = getRange(false).high() * sqrt( getRange(false).high() / 
00179   //                                        getRange(false).low()    );
00180   //  Range range(mylow, myhigh, getRange(false).pos());
00181   //  setIntersectRange ( range, limit );
00182   //  return m_range;
00183 
00184 }
00185 
00186 const Range & 
00187 AxisModelLog::adjustLogValues ()
00188 {
00189   const Range & r = getRange(false);
00190   double low = r.low();
00191   double high = r.high();
00192   double pos = r.pos();
00193   
00194   if( low > 0.0 ) return r;
00195 
00196   if( pos == high ) { // Will give no range
00197 //     setRange ( pos / 10.0, pos * 10.0, pos );
00198     double l = pos / 10.0;
00199     double h = pos * 10.0;
00200     setRange ( l, h, pos );
00201 
00202     return m_range;
00203   }
00204   if( pos == DBL_MAX || pos <= 0.0 ) { // No positive values!!!
00205     setRange ( 0.01, 100.0, 1.0 );
00206     return m_range;
00207   }
00208   setRange ( pos, high, pos );
00209 
00210   return m_range;
00211 }
00212 
00213 double AxisModelLog::nextStep ( double current )
00214 {
00215   double tick_step = getTickStep(); // Must already be called
00216   if( tick_step == 1.0 ) {
00217     int base = static_cast<int>( current /
00218                                  pow( 10.0, floor( log10( current ) ) ) );
00219     // Look! I used a switch statement in C++!!!!!  What this does
00220     // is go through and add the intermediate 2 and 5 ticks if the
00221     // powers of 10 alone would not have given the minimum number of
00222     // ticks.  m_tick_step is completely ignored if the flag is
00223     // true, since it is assumed to be 0.
00224     
00227     switch( base ) {
00228     case 1:
00229       current *= 2.0;
00230       break;
00231     case 2:
00232       current /= 2.0;
00233       current *= 5.0;
00234       break;
00235     case 3:
00236       current /= 4.0;
00237       current *= 5.0;
00238       break;
00239     case 4: // a 5 becomes a 4 sometimes because of round of error
00240     case 5:
00241       current *= 2.0;
00242       break;
00243     default:
00244       assert ( false );
00245     }
00246   } else {
00247     current *= tick_step;
00248   }
00249   return current;
00250 }
00251 
00254 double AxisModelLog::prevStep ( double current )
00255 {
00256   double tick_step = getTickStep(); // It must already be called.
00257   if( tick_step == 1.0 ) {
00258     int base = static_cast<int>( current /
00259                                  pow( 10.0, floor( log10( current ) ) ) );
00260     // Look! I used a switch statement in C++!!!!!  What this does
00261     // is go through and add the intermediate 2 and 5 ticks if the
00262     // powers of 10 alone would not have given the minimum number of
00263     // ticks.  m_tick_step is completely ignored if the flag is
00264     // true, since it is assumed to be 0.
00265     
00266     switch( base ) {
00267     case 1:
00268       current /= 2.0;
00269       break;
00270     case 2:
00271       current /= 2.0;
00272       break;
00273     case 4:
00274       current /= 5.0;
00275       current *= 2.0;
00276       break;
00277     case 5:
00278       current /= 5.0;
00279       current *= 2.0;
00280       break;
00281     default:
00282       assert ( false );
00283     }
00284   } else {
00285     current /= tick_step;
00286   }
00287   return current;
00288 }
00289 
00293 Range AxisModelLog::calcLow ( int parm, bool dragging )
00294 {
00295   startDragging ( dragging );
00296 
00297   double low = m_start_range.low ();
00298   double high = m_start_range.high ();
00299   double k = log10 ( high / low );
00300 
00301   double x = ( parm - 50 ) / 50.0;
00302 
00303   double new_low = low * pow ( 10.0, k * x );
00304 
00305   new_low = max ( new_low, 10.0 * DBL_EPSILON );
00306   new_low = min ( new_low,  high - 100.0 * DBL_EPSILON );
00307 
00308   if( abs( new_low - m_range.high() ) < 0.0001 ) return m_range;
00309 
00310   return Range ( new_low, high, m_range.pos() );
00311 }
00312 
00316 Range AxisModelLog::calcHigh ( int parm, bool dragging )
00317 {
00318   startDragging ( dragging );
00319 
00320   double low = m_start_range.low ();
00321   double high = m_start_range.high ();
00322   double k = log10 ( high / low );
00323 
00324   double multiplier = ( parm - 50 ) / 50.0;
00325 
00326   double new_high = high * pow ( 10.0, k * multiplier );
00327 
00328   if( abs( new_high - m_range.low() ) < 0.0001 ) return m_range;
00329 
00330   return Range ( low, new_high, m_range.pos() );
00331 }

Generated for HippoDraw-1.14.8.5 by doxygen 1.4.3