PLplot: pldtik.c Source File

PLplot 5.10.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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 }