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