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 }
virtual LogTransform * clone() const
Creates a new Transform object by copying an existing one.
unsigned int i
virtual const std::vector< AxisTick > & setTicks(AxisModelBase &axis)
Sets the vector of ticks along the axis to which this Log transform is applied.
void setFirstTick(AxisModelBase &)
double high() const
Returns the maximum of the range object.
Definition: Range.cxx:100
A class to maintain tick coordinates and string values.
Definition: AxisTick.h:29
void setTickStep(AxisModelBase &)
A transform that transforms coordinates from one coordinate system to another.
Definition: TransformBase.h:35
void setFirstTick(const double &first_tick)
Sets the value for first tick step.
A UnaryTransform which takes the log of the coordinate.
Definition: LogTransform.h:28
double getMaxTicks() const
Returns the value for maximum number of ticks.
virtual ~LogTransform()
The virtual destructor.
void setLow(double x)
Sets the minimum of the range object.
Definition: Range.cxx:93
virtual void validate(Range &) const
Validates the Range, and adjusts the argument Range so that the values are positive.
const Range & adjustLogValues(AxisModelBase &axis)
LogTransform()
The default constructor.
void setRange(double low, double high, double pos)
Sets the Range to the low and high values.
virtual void transform(double &x) const
Transforms the coordinate @ x.
const std::vector< AxisTick > & genTicks(AxisModelBase &)
void setHigh(double x)
Sets the maximum of the range object.
Definition: Range.cxx:106
double FLT_EQUAL(double x, double y)
const Range & adjustValues(AxisModelBase &axis, const Range &limit)
Sets the range of given axis to be a new &quot;nice&quot; within the limits given.
void setPMag(const double &pmag)
Sets the magnitude of the power of ten for the tick labels.
double prevStep(double current, AxisModelBase &axis)
virtual bool isLinear() const
The AxisModelBase class maintains the Range and scaling of an axis.
Definition: AxisModelBase.h:33
double length() const
Returns the length of the range object.
Definition: Range.h:156
double getScaleFactor() const
Returns the scale factor.
LogTransform class interface.
std::string m_name
Name of the Transform.
Definition: TransformBase.h:42
hippodraw::AxisModelBase class interface
double nextStep(double current, AxisModelBase &axis)
double getPMag() const
Returns the magnitude of the power of ten for the tick labels.
const Range & getRange(bool scaled) const
Returns the range represented by this AxisModel.
A transform that transforms coordinates in one dimension from one coordinate system to another...
void setRMag(const double &rmag)
Sets the magnitude of the range.
double low() const
Returns the minimum of the range object.
Definition: Range.cxx:87
void setTickStep(const double &t_step)
Sets the tick step.
Expresses a range of values.
Definition: Range.h:33
void setIntersectRange(const Range &, const Range &)
Sets the Range to overlap of the two ranges.
double getFirstTick() const
Returns the value for the first tick step.
double pos() const
Returns the first positive element in range.
Definition: Range.cxx:113
std::vector< AxisTick > m_ticks
The ticks generated by this transform.
Definition: LogTransform.h:34
virtual void inverseTransform(double &x) const
Inverse transforms the coordinate x.
AxisTick class interface.
double getTickStep() const
Returns the tick step in the true coordinate system.

Generated for HippoDraw Class Library by doxygen