PLplot 5.9.6
|
00001 /* $Id$ 00002 * 00003 * Determines tick spacing and mode (fixed or floating) of 00004 * numeric axis labels. 00005 * 00006 * Copyright (C) 2004 Alan W. Irwin 00007 * 00008 * This file is part of PLplot. 00009 * 00010 * PLplot is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Library Public License as published 00012 * by the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. 00014 * 00015 * PLplot is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 * GNU Library General Public License for more details. 00019 * 00020 * You should have received a copy of the GNU Library General Public License 00021 * along with PLplot; if not, write to the Free Software 00022 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00023 */ 00024 00025 #include "plplotP.h" 00026 00027 /*----------------------------------------------------------------------*\ 00028 * void pldtik() 00029 * 00030 * Determine tick spacing: works out a "nice" interval (if tick == 0) such 00031 * that there are between 3 and 7.5 major tick intervals in the input 00032 * range vmin to vmax. The recommended number of subticks is returned in 00033 * "nsubt" unless the routine is entered with a non-zero value of "nsubt". 00034 * n.b. big change: now returns only positive values of tick and nsubt 00035 \*----------------------------------------------------------------------*/ 00036 00037 void 00038 pldtik( PLFLT vmin, PLFLT vmax, PLFLT *tick, PLINT *nsubt, PLBOOL ld ) 00039 { 00040 PLFLT t1, t2, tick_reasonable; 00041 PLINT np, ns; 00042 PLFLT factor; 00043 00044 00045 if ( ld ) 00046 { 00047 /* Check suitable units for tick spacing */ 00048 pldtfac( vmin, vmax, &factor, NULL ); 00049 00050 *tick = *tick / factor; 00051 vmin = vmin / factor; 00052 vmax = vmax / factor; 00053 } 00054 00055 /* Magnitude of min/max difference to get tick spacing */ 00056 00057 t1 = (PLFLT) log10( ABS( vmax - vmin ) ); 00058 np = (PLINT) floor( t1 ); 00059 t1 = t1 - np; 00060 00061 /* Get tick spacing. */ 00062 00063 if ( t1 > 0.7781512503 ) 00064 { 00065 t2 = 2.0; 00066 ns = 4; 00067 } 00068 else if ( t1 > 0.4771212549 ) 00069 { 00070 t2 = 1.0; 00071 ns = 5; 00072 } 00073 else if ( t1 > 0.1760912591 ) 00074 { 00075 t2 = 5.0; 00076 ns = 5; 00077 np = np - 1; 00078 } 00079 else 00080 { 00081 t2 = 2.0; 00082 ns = 4; 00083 np = np - 1; 00084 } 00085 00086 /* Now compute reasonable tick spacing */ 00087 00088 tick_reasonable = t2 * pow( 10.0, (double) np ); 00089 if ( *tick == 0 ) 00090 { 00091 *tick = t2 * pow( 10.0, (double) np ); 00092 } 00093 else 00094 { 00095 *tick = ABS( *tick ); 00096 if ( *tick < 1.e-4 * tick_reasonable ) 00097 { 00098 plexit( "pldtik: magnitude of specified tick spacing is much too small" ); 00099 return; 00100 } 00101 } 00102 if ( *nsubt == 0 ) 00103 *nsubt = ns; 00104 00105 *nsubt = ABS( *nsubt ); 00106 00107 if ( ld ) 00108 { 00109 *tick = *tick * factor; 00110 } 00111 } 00112 00113 /*----------------------------------------------------------------------*\ 00114 * PLFLT pldtfac() 00115 * 00116 * Calculate factor to convert a date/time interval in seconds 00117 * into a more natural units (minutes, hours, days, week, years). 00118 * Also optionally calculate the sensible start time for counting ticks 00119 * from (e.g. beginning of day, beginning of year). 00120 * Used to calculate sensible tick and label spacings. 00121 \*----------------------------------------------------------------------*/ 00122 void 00123 pldtfac( PLFLT vmin, PLFLT vmax, PLFLT *factor, PLFLT *start ) 00124 { 00125 PLFLT diff; 00126 PLINT year, month, day, hour, min; 00127 PLFLT sec; 00128 00129 diff = vmax - vmin; 00130 00131 if ( start != NULL ) 00132 { 00133 plbtime( &year, &month, &day, &hour, &min, &sec, vmin ); 00134 } 00135 00136 if ( diff < 3.0 * 60.0 ) 00137 { 00138 /* Seconds */ 00139 *factor = 1.0; 00140 if ( start != NULL ) 00141 { 00142 sec = 0.; 00143 plctime( year, month, day, hour, min, sec, start ); 00144 } 00145 } 00146 else if ( diff < 3.0 * 60.0 * 60.0 ) 00147 { 00148 /* Minutes */ 00149 *factor = 60.0; 00150 if ( start != NULL ) 00151 { 00152 sec = 0.; 00153 min = 0; 00154 plctime( year, month, day, hour, min, sec, start ); 00155 } 00156 } 00157 else if ( diff < 3.0 * 60.0 * 60.0 * 24.0 ) 00158 { 00159 /* Hours */ 00160 *factor = 60.0 * 60.0; 00161 if ( start != NULL ) 00162 { 00163 sec = 0.; 00164 min = 0; 00165 hour = 0; 00166 plctime( year, month, day, hour, min, sec, start ); 00167 } 00168 } 00169 else if ( diff < 3.0 * 60.0 * 60.0 * 24.0 * 7.0 ) 00170 { 00171 /* Days */ 00172 *factor = 60.0 * 60.0 * 24.0; 00173 if ( start != NULL ) 00174 { 00175 sec = 0.; 00176 min = 0; 00177 hour = 0; 00178 plctime( year, month, day, hour, min, sec, start ); 00179 } 00180 } 00181 else if ( diff < 3.0 * 60.0 * 60.0 * 24.0 * 365 ) 00182 { 00183 /* Weeks */ 00184 *factor = 60.0 * 60.0 * 24.0 * 7.0; 00185 if ( start != NULL ) 00186 { 00187 sec = 0.; 00188 min = 0; 00189 hour = 0; 00190 plctime( year, month, day, hour, min, sec, start ); 00191 } 00192 } 00193 else 00194 { 00195 /* Years */ 00196 *factor = 60.0 * 60.0 * 24.0 * 365.25; 00197 if ( start != NULL ) 00198 { 00199 sec = 0.; 00200 min = 0; 00201 hour = 0; 00202 day = 0; 00203 month = 0; 00204 plctime( year, month, day, hour, min, sec, start ); 00205 } 00206 } 00207 } 00208 00209 /*----------------------------------------------------------------------*\ 00210 * void pldprec() 00211 * 00212 * Determine precision: the output variable "mode" is set to 0 if labels 00213 * are to be written in floating-point format, or to 1 if they are to be 00214 * written in scientific format. For mode = 1, the exponent will be 00215 * placed at: 00216 * 00217 * top left for vertical axis on left 00218 * top right for vertical axis on right 00219 * bottom right for horizontal axis 00220 * 00221 * The digmax flag can be set by the user, and represents the maximum 00222 * number of digits a label may occupy including sign and decimal point. 00223 * digmin, calculated internally, is the maximum number of digits 00224 * labels at vmin and vmax would occupy if floating point. 00225 * If digmax<0, it is disregarded, 00226 * and if digmax=0 the default value is used. For digmax>0, mode=1 is 00227 * chosen if there is insufficient room for the label within the specified 00228 * # of digits (digmin > digfix, where digfix is determined from digmax with 00229 * fuzz factors). 00230 * 00231 * In the case of mode=0, the actual # of digits will become too large 00232 * when the magnitude of the labels become too large. The mode=1 case 00233 * offers the greatest precision for the smallest field length. 00234 * 00235 * The determination of maximum length for fixed point quantities is 00236 * complicated by the fact that very long fixed point representations look 00237 * much worse than the same sized floating point representation. Further, 00238 * a fixed point number with a large negative exponent will actually gain 00239 * in precision when written as floating point. Thus we use certain fuzz 00240 * factors to get 'digfix' from 'digmax', however it will always be true 00241 * that digfix<=digmax. 00242 * 00243 * Finally, if 'digmax' is set, 'prec' is reduced in size if necessary so 00244 * that the labels fit the requested field length, where prec is the number of 00245 * places after the decimal place. 00246 \*----------------------------------------------------------------------*/ 00247 00248 #define MIN_FLTDIG 3 /* disregarded if fractional part is 0 */ 00249 #define DIGMAX_DEF 5 00250 00251 void 00252 pldprec( PLFLT vmin, PLFLT vmax, PLFLT tick, PLINT lf, 00253 PLINT *mode, PLINT *prec, PLINT digmax, PLINT *scale ) 00254 { 00255 PLFLT chosen, notchosen, vmod, t0; 00256 PLINT msd, notmsd, np, digmin, digfix; 00257 00258 *mode = 0; 00259 *scale = 0; 00260 00261 /* Default xdigmax, ydigmax and zdigmax set in c_plinit so this is 00262 * only an emergency measure in case of some internal PLplot 00263 * logic error. */ 00264 if ( digmax == 0 ) 00265 digmax = DIGMAX_DEF; 00266 /* No modification of digfix from digmax value. */ 00267 digfix = digmax; 00268 /* Choose vmin or vmax depending on magnitudes of vmin and vmax. */ 00269 chosen = ( ABS( vmax ) >= ABS( vmin ) ) ? vmax : vmin; 00270 notchosen = ( ABS( vmax ) >= ABS( vmin ) ) ? vmin : vmax; 00271 /* Magnitute of chosen to get number of significant digits */ 00272 00273 if ( ABS( chosen ) > 0. ) 00274 { 00275 vmod = ABS( chosen ); 00276 t0 = (PLFLT) log10( vmod ); 00277 msd = (PLINT) floor( t0 ); 00278 } 00279 else 00280 { 00281 /* this branch occurs only when 0. --- 0. range put in */ 00282 vmod = 1.; 00283 t0 = (PLFLT) log10( vmod ); 00284 msd = (PLINT) floor( t0 ); 00285 } 00286 00287 if ( ABS( notchosen ) > 0. ) 00288 notmsd = (PLINT) floor( (PLFLT) log10( ABS( notchosen ) ) ); 00289 else 00290 notmsd = msd; 00291 /* Autoselect the mode flag */ 00292 /* 'digmin' is the minimum number of places taken up by the label */ 00293 00294 if ( msd >= 0 ) 00295 { 00296 /* n.b. no decimal point in the minimal case */ 00297 digmin = msd + 1; 00298 } 00299 else 00300 { 00301 /* adjust digmin to account for leading 0 and decimal point */ 00302 digmin = -msd + 2; 00303 } 00304 /* adjust digmin to account for sign on the chosen end of axis or sign on the 00305 * nonchosen end of axis if notmsd = msd or (msd <= 0 and notmsd < 0) 00306 * For the latter case the notchosen label starts with "-0." 00307 * For checking for the latter case, the notmsd < 0 condition is redundant 00308 * since notmsd <= msd always and the equal part is selected by the first 00309 * condition. 00310 */ 00311 if ( chosen < 0. || ( notchosen < 0. && ( notmsd == msd || msd <= 0 ) ) ) 00312 digmin = digmin + 1; 00313 00314 if ( digmin > digfix && !lf ) 00315 { 00316 *mode = 1; 00317 *scale = msd; 00318 } 00319 00320 /* Establish precision. */ 00321 /* It must be fine enough to resolve the tick spacing */ 00322 00323 np = (PLINT) floor( log10( ABS( tick ) ) ); 00324 00325 if ( *mode != 0 ) 00326 *prec = msd - np; 00327 else 00328 *prec = MAX( -np, 0 ); 00329 00330 /* One last hack required: if exponent < 0, i.e. number has leading '0.', 00331 * it's better to change to floating point form if the number of digits 00332 * is insufficient to represent the tick spacing. 00333 */ 00334 if ( *mode == 0 && digmax > 0 && !lf ) 00335 { 00336 if ( t0 < 0.0 ) 00337 { 00338 if ( digmax - 2 - *prec < 0 ) 00339 { 00340 *mode = 1; 00341 *scale = msd; 00342 } 00343 } 00344 else 00345 *prec = MAX( MIN( *prec, digmax - msd - 1 ), 0 ); 00346 } 00347 if ( *mode != 0 ) 00348 { 00349 *prec = msd - np; 00350 *prec = MAX( MIN( *prec, MAX( digmax - 1, MIN_FLTDIG ) ), 0 ); 00351 } 00352 }