PLplot 5.15.0
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
35void
36pldtik( 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//--------------------------------------------------------------------------
121void
122pldtfac( 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
250void
251pldprec( 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}
#define MIN(a, b)
Definition: dsplint.c:29
#define MAX(a, b)
Definition: dsplint.c:28
void plexit(PLCHAR_VECTOR errormsg)
Definition: plctrl.c:1958
void pldtik(PLFLT vmin, PLFLT vmax, PLFLT *tick, PLINT *nsubt, PLBOOL ld)
Definition: pldtik.c:36
#define DIGMAX_DEF
Definition: pldtik.c:248
void pldprec(PLFLT vmin, PLFLT vmax, PLFLT tick, PLINT lf, PLINT *mode, PLINT *prec, PLINT digmax, PLINT *scale)
Definition: pldtik.c:251
void pldtfac(PLFLT vmin, PLFLT vmax, PLFLT *factor, PLFLT *start)
Definition: pldtik.c:122
#define MIN_FLTDIG
Definition: pldtik.c:247
#define ABS(a)
Definition: plplotP.h:199
float PLFLT
Definition: plplot.h:163
#define plctime
Definition: plplot.h:708
int PLINT
Definition: plplot.h:181
PLINT PLBOOL
Definition: plplot.h:204
#define plbtime
Definition: plplot.h:699
int min(int a, int b)