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