PLplot 5.9.6
pldtik.c
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 }
 All Data Structures Files Functions