PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
pldtik.c
Go to the documentation of this file.
1 // Determine tick spacing and mode (fixed or floating) of
2 // numeric axis labels.
3 //
4 // Copyright (C) 2004-2014 Alan W. Irwin
5 //
6 // This file is part of PLplot.
7 //
8 // PLplot is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU Library General Public License as published
10 // by the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // PLplot is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Library General Public License for more details.
17 //
18 // You should have received a copy of the GNU Library General Public License
19 // along with PLplot; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //
22 
23 #include "plplotP.h"
24 
25 //--------------------------------------------------------------------------
26 // void pldtik()
27 //
28 // Determine tick spacing: works out a "nice" interval (if tick == 0) such
29 // that there are between 3 and 7.5 major tick intervals in the input
30 // range vmin to vmax. The recommended number of subticks is returned in
31 // "nsubt" unless the routine is entered with a non-zero value of "nsubt".
32 // n.b. big change: now returns only positive values of tick and nsubt
33 //--------------------------------------------------------------------------
34 
35 void
36 pldtik( PLFLT vmin, PLFLT vmax, PLFLT *tick, PLINT *nsubt, PLBOOL ld )
37 {
38  PLFLT t1, t2, tick_reasonable;
39  PLINT np, ns;
40  // Unnecessarily set factor to quiet -O3 -Wuninitialized warnings.
41  PLFLT factor = 0.0;
42 
43 
44  if ( ld )
45  {
46  // Check suitable units for tick spacing
47  pldtfac( vmin, vmax, &factor, NULL );
48 
49  *tick = *tick / factor;
50  vmin = vmin / factor;
51  vmax = vmax / factor;
52  }
53 
54 // Magnitude of min/max difference to get tick spacing
55 
56  t1 = (PLFLT) log10( ABS( vmax - vmin ) );
57  np = (PLINT) floor( t1 );
58  t1 = t1 - np;
59 
60 // Get tick spacing.
61 
62  if ( t1 > 0.7781512503 )
63  {
64  t2 = 2.0;
65  ns = 4;
66  }
67  else if ( t1 > 0.4771212549 )
68  {
69  t2 = 1.0;
70  ns = 5;
71  }
72  else if ( t1 > 0.1760912591 )
73  {
74  t2 = 5.0;
75  ns = 5;
76  np = np - 1;
77  }
78  else
79  {
80  t2 = 2.0;
81  ns = 4;
82  np = np - 1;
83  }
84 
85 // Now compute reasonable tick spacing
86 
87  tick_reasonable = t2 * pow( 10.0, (double) np );
88  if ( *tick == 0 )
89  {
90  *tick = t2 * pow( 10.0, (double) np );
91  }
92  else
93  {
94  *tick = ABS( *tick );
95  if ( *tick < 1.e-4 * tick_reasonable )
96  {
97  plexit( "pldtik: magnitude of specified tick spacing is much too small" );
98  return;
99  }
100  }
101  if ( *nsubt == 0 )
102  *nsubt = ns;
103 
104  *nsubt = ABS( *nsubt );
105 
106  if ( ld )
107  {
108  *tick = *tick * factor;
109  }
110 }
111 
112 //--------------------------------------------------------------------------
113 // PLFLT pldtfac()
114 //
115 // Calculate factor to convert a date/time interval in seconds
116 // into a more natural units (minutes, hours, days, week, years).
117 // Also optionally calculate the sensible start time for counting ticks
118 // from (e.g. beginning of day, beginning of year).
119 // Used to calculate sensible tick and label spacings.
120 //--------------------------------------------------------------------------
121 void
122 pldtfac( PLFLT vmin, PLFLT vmax, PLFLT *factor, PLFLT *start )
123 {
124  PLFLT diff;
125  PLINT year, month, day, hour, min;
126  PLFLT sec;
127 
128  diff = vmax - vmin;
129 
130  if ( start != NULL )
131  {
132  plbtime( &year, &month, &day, &hour, &min, &sec, vmin );
133  }
134 
135  if ( diff < 3.0 * 60.0 )
136  {
137  // Seconds
138  *factor = 1.0;
139  if ( start != NULL )
140  {
141  sec = 0.;
142  plctime( year, month, day, hour, min, sec, start );
143  }
144  }
145  else if ( diff < 3.0 * 60.0 * 60.0 )
146  {
147  // Minutes
148  *factor = 60.0;
149  if ( start != NULL )
150  {
151  sec = 0.;
152  min = 0;
153  plctime( year, month, day, hour, min, sec, start );
154  }
155  }
156  else if ( diff < 3.0 * 60.0 * 60.0 * 24.0 )
157  {
158  // Hours
159  *factor = 60.0 * 60.0;
160  if ( start != NULL )
161  {
162  sec = 0.;
163  min = 0;
164  hour = 0;
165  plctime( year, month, day, hour, min, sec, start );
166  }
167  }
168  else if ( diff < 3.0 * 60.0 * 60.0 * 24.0 * 7.0 )
169  {
170  // Days
171  *factor = 60.0 * 60.0 * 24.0;
172  if ( start != NULL )
173  {
174  sec = 0.;
175  min = 0;
176  hour = 0;
177  plctime( year, month, day, hour, min, sec, start );
178  }
179  }
180  else if ( diff < 3.0 * 60.0 * 60.0 * 24.0 * 365 )
181  {
182  // Weeks
183  *factor = 60.0 * 60.0 * 24.0 * 7.0;
184  if ( start != NULL )
185  {
186  sec = 0.;
187  min = 0;
188  hour = 0;
189  plctime( year, month, day, hour, min, sec, start );
190  }
191  }
192  else
193  {
194  // Years
195  *factor = 60.0 * 60.0 * 24.0 * 365.25;
196  if ( start != NULL )
197  {
198  sec = 0.;
199  min = 0;
200  hour = 0;
201  day = 0;
202  month = 0;
203  plctime( year, month, day, hour, min, sec, start );
204  }
205  }
206 }
207 
208 //--------------------------------------------------------------------------
209 // void pldprec()
210 //
211 // Determine precision: the output variable "mode" is set to 0 if labels
212 // are to be written in floating-point format, or to 1 if they are to be
213 // written in scientific format. For mode = 1, the exponent will be
214 // placed at:
215 //
216 // top left for vertical axis on left
217 // top right for vertical axis on right
218 // bottom right for horizontal axis
219 //
220 // The digmax flag can be set by the user, and represents the maximum
221 // number of digits a label may occupy including sign and decimal point.
222 // digmin, calculated internally, is the maximum number of digits
223 // labels at vmin and vmax would occupy if floating point.
224 // If digmax<0, it is disregarded,
225 // and if digmax=0 the default value is used. For digmax>0, mode=1 is
226 // chosen if there is insufficient room for the label within the specified
227 // # of digits (digmin > digfix, where digfix is determined from digmax with
228 // fuzz factors).
229 //
230 // In the case of mode=0, the actual # of digits will become too large
231 // when the magnitude of the labels become too large. The mode=1 case
232 // offers the greatest precision for the smallest field length.
233 //
234 // The determination of maximum length for fixed point quantities is
235 // complicated by the fact that very long fixed point representations look
236 // much worse than the same sized floating point representation. Further,
237 // a fixed point number with a large negative exponent will actually gain
238 // in precision when written as floating point. Thus we use certain fuzz
239 // factors to get 'digfix' from 'digmax', however it will always be true
240 // that digfix<=digmax.
241 //
242 // Finally, if 'digmax' is set, 'prec' is reduced in size if necessary so
243 // that the labels fit the requested field length, where prec is the number of
244 // places after the decimal place.
245 //--------------------------------------------------------------------------
246 
247 #define MIN_FLTDIG 3 // disregarded if fractional part is 0
248 #define DIGMAX_DEF 5
249 
250 void
251 pldprec( PLFLT vmin, PLFLT vmax, PLFLT tick, PLINT lf,
252  PLINT *mode, PLINT *prec, PLINT digmax, PLINT *scale )
253 {
254  PLFLT chosen, notchosen, vmod, t0;
255  PLINT msd, notmsd, np, digmin, digfix;
256 
257  *mode = 0;
258  *scale = 0;
259 
260  // Default xdigmax, ydigmax and zdigmax set in c_plinit so this is
261  // only an emergency measure in case of some internal PLplot
262  // logic error.
263  if ( digmax == 0 )
264  digmax = DIGMAX_DEF;
265  // No modification of digfix from digmax value.
266  digfix = digmax;
267 // Choose vmin or vmax depending on magnitudes of vmin and vmax.
268  chosen = ( ABS( vmax ) >= ABS( vmin ) ) ? vmax : vmin;
269  notchosen = ( ABS( vmax ) >= ABS( vmin ) ) ? vmin : vmax;
270 // Magnitute of chosen to get number of significant digits
271 
272  if ( ABS( chosen ) > 0. )
273  {
274  vmod = ABS( chosen );
275  t0 = (PLFLT) log10( vmod );
276  msd = (PLINT) floor( t0 );
277  }
278  else
279  {
280  // this branch occurs only when 0. --- 0. range put in
281  vmod = 1.;
282  t0 = (PLFLT) log10( vmod );
283  msd = (PLINT) floor( t0 );
284  }
285 
286  if ( ABS( notchosen ) > 0. )
287  notmsd = (PLINT) floor( (PLFLT) log10( ABS( notchosen ) ) );
288  else
289  notmsd = msd;
290  // Autoselect the mode flag
291  // 'digmin' is the minimum number of places taken up by the label
292 
293  if ( msd >= 0 )
294  {
295  // n.b. no decimal point in the minimal case
296  digmin = msd + 1;
297  }
298  else
299  {
300  // adjust digmin to account for leading 0 and decimal point
301  digmin = -msd + 2;
302  }
303 // adjust digmin to account for sign on the chosen end of axis or sign on the
304 // nonchosen end of axis if notmsd = msd or (msd <= 0 and notmsd < 0)
305 // For the latter case the notchosen label starts with "-0."
306 // For checking for the latter case, the notmsd < 0 condition is redundant
307 // since notmsd <= msd always and the equal part is selected by the first
308 // condition.
309 //
310  if ( chosen < 0. || ( notchosen < 0. && ( notmsd == msd || msd <= 0 ) ) )
311  digmin = digmin + 1;
312 
313  if ( digmin > digfix && !lf )
314  {
315  *mode = 1;
316  *scale = msd;
317  }
318 
319 // Establish precision.
320 // It must be fine enough to resolve the tick spacing
321 
322  np = (PLINT) floor( log10( ABS( tick ) ) );
323 
324  if ( *mode != 0 )
325  *prec = msd - np;
326  else
327  *prec = MAX( -np, 0 );
328 
329 // One last hack required: if exponent < 0, i.e. number has leading '0.',
330 // it's better to change to floating point form if the number of digits
331 // is insufficient to represent the tick spacing.
332 //
333  if ( *mode == 0 && digmax > 0 && !lf )
334  {
335  if ( t0 < 0.0 )
336  {
337  if ( digmax - 2 - *prec < 0 )
338  {
339  *mode = 1;
340  *scale = msd;
341  }
342  }
343  else
344  *prec = MAX( MIN( *prec, digmax - msd - 1 ), 0 );
345  }
346  if ( *mode != 0 )
347  {
348  *prec = msd - np;
349  *prec = MAX( MIN( *prec, MAX( digmax - 1, MIN_FLTDIG ) ), 0 );
350  }
351 }