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