AxisModelLog.cxx

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

Generated for HippoDraw Class Library by doxygen