LinearTransform.cxx
Go to the documentation of this file.
1 
12 #include "LinearTransform.h"
13 
14 #include "axes/AxisModelBase.h"
15 #include "axes/AxisTick.h"
16 
17 #include <cmath>
18 #include <cstdio>
19 
20 using std::abs;
21 using std::max;
22 using std::vector;
23 
24 namespace hippodraw {
25 
31  : UnaryTransform ( -DBL_MAX, DBL_MAX )
32 {
33  m_name = "Linear";
34 }
35 
37 {
38 }
39 
41  : UnaryTransform ( lt )
42 {
43 }
44 
45 #ifdef CLONE_DEFECT
47 #else
49 #endif
50 {
51  return new LinearTransform ( *this );
52 }
53 
54 bool
56 isLinear () const
57 {
58  return true;
59 }
60 
61 void
63 transform ( double & ) const
64 {
65 }
66 
67 void
69 inverseTransform ( double & ) const
70 {
71 }
72 
73 void
75 transform ( std::vector < double > & ) const
76 {
77 }
78 
79 /* virtual */
80 void
82 validate ( Range & ) const
83 {
84  // Nothing to be done.
85 }
86 
87 const vector < AxisTick > &
90 {
91  setTickStep( axis );
92  setFirstTick( axis );
93 
94  return genTicks( axis );
95 }
96 
97 inline double FLT_EQUAL( double x, double y )
98 {
99  return ( (double)abs( x - y ) <= 2.0 * ( y * FLT_EPSILON + FLT_MIN ) );
100 }
101 
103 {
104  static float goodTicks[] = { 5.0, 4.0, 2.0, 1.0 };
105  int tickIndex;
106 
107  const Range & range = axis.getRange(false);
108  double rangeLength = range.length();
109 
110  double scale_factor = axis.getScaleFactor();
111  rangeLength *= scale_factor;
112  const int MIN_TICKS = 3;
113 
114  // The following algorithm determines the magnitude of the range...
115  double rmag = floor( log10( rangeLength ) );
116 
117  // ...and then decreases by one magnitude if it would allow less than
118  // 3 ticks (e.g., the range is 25000 and the magnitude is 10000. This
119  // would allow for 2.5 ticks while we in fact would rather have a
120  // magnitude of 1000, multiplied by a constant, as below).
121 
122  if( rangeLength / pow( 10.0, rmag ) < MIN_TICKS ) {
123  rmag--;
124  }
125 
126  axis.setRMag( rmag );
127 
128  double scalelow = range.low() * scale_factor;
129  double scalehigh = range.high() * scale_factor;
130 
131  // We will also need the largest magnitude for labels.
132  double pmag = max( floor( log10( abs ( scalehigh ) ) ),
133  floor( log10( abs ( scalelow ) ) ) );
134 
135  // This if statement changes the magnitude so that if the high or
136  // low is exactly a power of 10, we will give labels from
137  // [1,10] * 10^mag and not [0,1] * 10^mag.
138  if( pow( 10.0, pmag ) == scalehigh ||
139  pow( 10.0, pmag ) == scalelow ) pmag--;
140 
141  axis.setPMag( pmag );
142 
143  // Now we determine the above stated constant. The magnitude is already
144  // known, so we see what's the closest we can get to exactly 3 ticks
145  // under this magnitude. In the above example, a range of 25000 was
146  // given with a magnitude of 1000. This algorithm will recognize that
147  // 5.0 * 1000 will give 5 ticks, which is a good number to have. If
148  // the range was 12000 and magnitude 1000 then 5.0 * 1000 would give
149  // only 2 ticks, not enough. The loop would then proceed to 4.0 * 1000
150  // which will give exactly 3 ticks.
151  double tick_step = 0;
152  for( tickIndex = 0;
153  rangeLength /
154  ( tick_step = goodTicks[tickIndex] * pow( 10.0, rmag ) )
155  < MIN_TICKS;
156  tickIndex++ ){};
157 
158  axis.setTickStep( tick_step );
159 }
160 
161 
163 {
164  const Range & range = axis.getRange(true);
165  double low = range.low();
166  double tick_step = axis.getTickStep();
167 
168  // This sets the first tick as the low value rounded up to the
169  // nearest tick step. If the low value fell on a tick, then that is
170  // the first tick. Otherwise, it is the next tick inside the range
171  // of the data.
172  axis.setFirstTick( ceil( low / tick_step ) * tick_step );
173 }
174 
175 
178 const vector < AxisTick > &
181 {
182  double y = 0.0, ylabel;
183 
184  int num_ticks = 0;
185  m_ticks.clear();
186  double pmag = axis.getPMag();
187  double rmag = axis.getRMag();
188  double first_tick = axis.getFirstTick();
189  double tick_step = axis.getTickStep();
190  double scale_factor = axis.getScaleFactor();
191  double max_ticks = axis.getMaxTicks();
192 
193  // pmag will get set to 0 if it is less than or equal to 3. This
194  // is used later to determine scientific notation. However, m_rmag
195  // is still needed as the original magnitude for calculations such
196  // as decimal place notation, and rounding to nice numbers.
197 
198  // if( fabs( m_pmag ) <= 3.0 ) m_pmag = 0.0;
199  bool use_pmag = abs ( pmag ) > 3.0;
200 
201  axis.setUsePMag ( use_pmag );
202 
203  char pstr[10];
204  char labl[10];
205 
206  int decimals = 0;
207 
208  // The following if-else block decides the pstr string, which holds
209  // the number of decimal places in the label.
210 
211  // if( fabs( m_pmag ) > 3.0 ) {
212  if ( use_pmag ) {
213  // If we are greater than mag 3, we are showing scientific
214  // notation. How many decimals we show is determined by the
215  // difference between the range magnitude and the power magnitude.
216 
217  decimals = static_cast<int>( pmag - rmag );
218  // minumum 1 decimal in scientific notation
219 
220  if( !decimals ) decimals++;
221 
222  } else {
223 
224  if( rmag > 0.0 ){
225 
226  // If we are less than mag 3 and positive, then no decimal
227  // accuracy is needed.
228 
229  decimals = 0;
230 
231  } else {
232 
233  // If we are less than mag 3 and negative, then we are suddenly
234  // looking at decimal numbers not in scientific notation.
235  // Therefore we hold as many decimal places as the magnitude.
236 
237  decimals = static_cast<int>( abs( rmag ) );
238 
239  }
240 
241  }
242  // @todo decimals should never be negative here, but it does end up
243  // negative in some cases. See the "dirty fix" in Range.cxx, that
244  // dirty-fixed this problem too. But a better fix is needed.
245  if (decimals < 0) {
246  decimals = 0;
247  }
248 
249  sprintf( pstr, "%%1.%df", decimals );
250 
251  y = first_tick;
252  const Range & range = axis.getRange(false);
253  double range_high = range.high();
254  range_high *= scale_factor;
255  range_high += 100. * DBL_EPSILON;
256 
257  // while( y <= range_high || FLT_EQUAL( range_high, y ) ) {
258  while( y <= range_high ) {
259 
260  if( num_ticks >= max_ticks ) {
261 
262  // HERE So far, this has only occurred for empty histograms. The
263  //easy fix was to do nothing, but there ought to be a better
264  // way to handle this.
265 
266  return m_ticks;
267 
268  }
269 
270  // The following expression is used to round to the nearest nice
271  // number, and then return to the original magnitude.
272 
273  double value = floor( y / pow( 10.0, rmag ) + 0.5 ) *
274  pow( 10.0, rmag );
275 
276  // Now that the number is nice, we either keep the original magnitude
277  // or reduce it in order to express it in scientific notation.
278 
279  if ( use_pmag ) ylabel = value / pow( 10.0, pmag );
280  else ylabel = value;
281 
282  value /= scale_factor;
283  sprintf( labl, pstr, ylabel );
284  m_ticks.push_back( AxisTick ( value, labl ) );
285 
286  num_ticks++;
287  y += tick_step;
288 
289  }
290 
291  return m_ticks;
292 }
293 
295  const Range & limit )
296 {
297  //Because the low value, the high value, and the length value of the
298  //range were so frequently used, I added those three fields. There
299  //should be an improvement in performance.
300  double mylow, myhigh;
301 
302  //The use of a step field and of a mag field will be explained when
303  //they are first initialized.
304  double step, magnitude;
305 
306  const int N_NICE = 6;
307 #ifndef __STDC__
308  static
309 #endif
310  float nice[N_NICE] = { 1.0, 1.5, 2.0,
311  3.0, 5.0, 7.5 };
312 
313  const Range & init_range = axis.getRange ( false );
314  double low = init_range.low ();
315  double high = init_range.high ();
316 
317  if ( ( high - low ) < 10.* DBL_MIN ) { // all values in same bin
318  if ( low > 0.0 ) low *= 0.95;
319  else low *= 1.05;
320 
321  if ( low == 0. ) { // special case
322  high = low + 1000. * FLT_EPSILON; // large enough so tick algo works
323  }
324  else {
325  if ( high > 0.) high *= 1.05;
326  else high *= 0.95;
327  }
328 
329  axis.setRange ( low, high, low );
330  }
331  double range_length;
332 
333  int i;
334 
335  // This increases myhigh so that "myrange" covers the whole range
336  // and then some.
337 
338  mylow = low - 0.05*(high-low);
339  myhigh = high + 0.05*(high-low);
340 
341  range_length = myhigh - mylow;
342 
343  // We have now decided on a range. This tries to move low/high a
344  // little to end up on a nice number.
345 
346  // First checks if either end is near 0.0
347  //checking high against 22*low is equivalent to the
348  //original condition
349  if( low >= 0.0 && high > 22 * low ) {
350  Range range ( 0.0, range_length );
351  axis.setIntersectRange ( range, limit );
352  return axis.getRange( false );
353  }
354  if( high <= 0.0 && low < 22 * high ) {
355  Range range ( -range_length, 0.0 );
356  axis.setIntersectRange ( range, limit );
357  return axis.getRange( false );
358  }
359 
360  // magnitude is used to hold the magnitude of the high or low values.
361  magnitude = floor(log10(abs(range_length)));
362  float norm = range_length / pow(10., magnitude);
363  float r, x;
364 
365  float r_previous = 10;
366  for (i = 0; i < N_NICE; i++) {
367  r = abs(norm / nice[i] - 1);
368  if (r < r_previous) {
369  r_previous = r;
370  x = nice[i];
371  } else
372  break;
373  }
374  //takes as for the step one fifth of nice[i]
375  step = 0.2 * x * pow(10, magnitude - 1);
376  mylow = floor(mylow / step) * step;
377  myhigh = ceil(myhigh / step) * step;
378 
379  Range range ( mylow, myhigh, init_range.pos() );
380 
381  axis.setIntersectRange ( range, limit );
382 
383  return axis.getRange( false );
384 }
385 
386 } // namespace hippodraw
387 
std::vector< AxisTick > m_ticks
The ticks generated by this transform.
virtual const std::vector< AxisTick > & setTicks(AxisModelBase &axis)
Sets the vector of ticks along the axis to which this Linear transform is applied.
AxisTick class interface.
LinearTransform class interface.
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
virtual ~LinearTransform()
The virtual destructor.
A transform on one axis whose output is equal to the input.
double norm(const std::vector< double > &a)
Computes the two norm of the vector.
Definition: NumLinAlg.cxx:285
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.
double getMaxTicks() const
Returns the value for maximum number of ticks.
LinearTransform()
The default constructor.
virtual const Range & adjustValues(AxisModelBase &axis, const Range &limit)
Sets the range of given axis to be a new "nice" within the limits given.
const std::vector< AxisTick > & genTicks(AxisModelBase &)
virtual void inverseTransform(double &x) const
Inverse transforms the coordinate x.
virtual void transform(double &x) const
Transforms the coordinate @ x.
Namespace for HippoDraw.
Definition: AxesType.cxx:21
void setRange(double low, double high, double pos)
Sets the Range to the low and high values.
void setFirstTick(AxisModelBase &)
void setPMag(const double &pmag)
Sets the magnitude of the power of ten for the tick labels.
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.
hippodraw::AxisModelBase class interface
double FLT_EQUAL(double x, double y)
std::string m_name
Name of the Transform.
Definition: TransformBase.h:42
double getRMag() const
Sets the magnitude of the range.
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(AxisModelBase &)
void setTickStep(const double &t_step)
Sets the tick step.
Expresses a range of values.
Definition: Range.h:33
virtual void setUsePMag(const bool &use_p_mag)
Use to set the value of the member variable m_use_pmag.
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.
virtual void validate(Range &) const
The following function validates the range.
virtual LinearTransform * clone() const
Creates a new Transform object by copying an existing one.
double pos() const
Returns the first positive element in range.
Definition: Range.cxx:113
double getTickStep() const
Returns the tick step in the true coordinate system.

Generated for HippoDraw Class Library by doxygen