// Routines for drawing axes & box around the current viewport. // // Copyright (C) 2004 Joao Cardoso // Copyright (C) 2004-2015 Alan W. Irwin // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #include "plplotP.h" #define STRING_LEN 40 #define FORMAT_LEN 10 #define TEMP_LEN 30 #define N_EDGE_SEGMENTS 50 static PLFLT xlog[8] = { 0.301030, 0.477121, 0.602060, 0.698970, 0.778151, 0.845098, 0.903090, 0.954243 }; // Static function prototypes static void plxybx( PLCHAR_VECTOR opt, PLCHAR_VECTOR label, PLINT axis, PLFLT wx1, PLFLT wy1, PLFLT wx2, PLFLT wy2, PLFLT vmin, PLFLT vmax, PLFLT tick, PLINT nsub, PLINT nolast, PLINT *digits ); static void plzbx( PLCHAR_VECTOR opt, PLCHAR_VECTOR label, PLINT right, PLFLT dx, PLFLT dy, PLFLT wx, PLFLT wy1, PLFLT wy2, PLFLT vmin, PLFLT vmax, PLFLT tick, PLINT nsub, PLINT *digits ); static void plxytx( PLFLT wx1, PLFLT wy1, PLFLT wx2, PLFLT wy2, PLFLT disp, PLFLT pos, PLFLT just, PLCHAR_VECTOR text ); static void plztx( PLCHAR_VECTOR opt, PLFLT dx, PLFLT dy, PLFLT wx, PLFLT wy1, PLFLT wy2, PLFLT disp, PLFLT pos, PLFLT just, PLCHAR_VECTOR text ); static void plform( PLINT axis, PLFLT value, PLINT scale, PLINT prec, char *result, PLINT len, PLBOOL ll, PLBOOL lf, PLBOOL lo ); static void grid_box( PLCHAR_VECTOR xopt, PLFLT xtick1, PLINT nxsub1, PLCHAR_VECTOR yopt, PLFLT ytick1, PLINT nysub1 ); static void label_box( PLCHAR_VECTOR xopt, PLFLT xtick1, PLCHAR_VECTOR yopt, PLFLT ytick1 ); static void plP_default_label_log( PLINT axis, PLFLT value, char *string, PLINT len, void *data ); static void plP_default_label_log_fixed( PLINT axis, PLFLT value, char *string, PLINT len, void *data ); static void plP_default_label( PLINT axis, PLFLT value, char *string, PLINT len, void *data ); static PLCHAR_VECTOR plgesc_string( void ); //-------------------------------------------------------------------------- // void plbox() // // This draws a box around the current viewport, complete with axes, ticks, // numeric labels, and grids, according to input specification. Just a // front-end to plaxes(), which allows arbitrary placement of coordinate // axes when plotted (here the origin is at 0,0). See the documentation for // plaxes() for more info. //-------------------------------------------------------------------------- void c_plbox( PLCHAR_VECTOR xopt, PLFLT xtick, PLINT nxsub, PLCHAR_VECTOR yopt, PLFLT ytick, PLINT nysub ) { c_plaxes( 0.0, 0.0, xopt, xtick, nxsub, yopt, ytick, nysub ); } //-------------------------------------------------------------------------- // void plaxes() // // This draws a box around the current viewport, complete with axes, // ticks, numeric labels, and grids, according to input specification. // // x0 and y0 specify the origin of the axes. // // xopt and yopt are character strings which define the box as follows: // // a: Draw axis (X is horizontal line Y=0, Y is vertical line X=0) // b: Draw bottom (X) or left (Y) frame of box // c: Draw top (X) or right (Y) frame of box // d: Interpret axis as a date/time when writing labels // f: Always use fixed point numeric labels // g: Draws a grid at the major tick interval // h: Draws a grid at the minor tick interval // i: Inverts tick marks // l: Logarithmic axes, major ticks at decades, minor ticks at units // n: Write numeric label at conventional location // m: Write numeric label at unconventional location // o: Label text is generated by a user-defined function // t: Draw major tick marks // s: Draw minor tick marks // u: like b (including all side effects such as tick marks and numerical // labels for those) except exclude drawing the edge. // w: like c (including all side effects such as tick marks and numerical // labels for those) except exclude drawing the edge. // v: (for Y only) Label vertically // x: like t (including the side effect of the numerical labels for the major // ticks) except exclude drawing the major and minor tick marks. // // xtick, ytick are the major tick intervals required, zero for // automatic selection // // nxsub, nysub are the number of subtick intervals in a major tick // interval //-------------------------------------------------------------------------- void c_plaxes( PLFLT x0, PLFLT y0, PLCHAR_VECTOR xopt, PLFLT xtick, PLINT nxsub, PLCHAR_VECTOR yopt, PLFLT ytick, PLINT nysub ) { PLBOOL lax, lbx, lcx, ldx, lgx, lix, llx, lsx, ltx, lux, lwx, lxx; PLBOOL lay, lby, lcy, ldy, lgy, liy, lly, lsy, lty, luy, lwy, lxy; PLINT xmajor, xminor, ymajor, yminor; PLINT i, i1x, i2x, i3x, i4x, i1y, i2y, i3y, i4y; PLINT nxsub1, nysub1; PLINT lxmin, lxmax, lymin, lymax; PLINT pxmin, pxmax, pymin, pymax; PLINT vppxmi, vppxma, vppymi, vppyma; PLFLT xtick1, ytick1, vpwxmi, vpwxma, vpwymi, vpwyma; PLFLT vpwxmin, vpwxmax, vpwymin, vpwymax; PLFLT xp0, yp0, tn, tp, temp; PLFLT factor, tstart; if ( plsc->level < 3 ) { plabort( "plbox: Please set up window first" ); return; } // Open the clip limits to the subpage limits plP_gclp( &lxmin, &lxmax, &lymin, &lymax ); plP_gphy( &pxmin, &pxmax, &pymin, &pymax ); plP_sclp( pxmin, pxmax, pymin, pymax ); vppxmi = plsc->vppxmi; vppxma = plsc->vppxma; vppymi = plsc->vppymi; vppyma = plsc->vppyma; if ( plsc->if_boxbb ) { // Bounding-box limits for the box in mm before corrections // for decorations are applied. plsc->boxbb_xmin = plsc->vppxmi / plsc->xpmm; plsc->boxbb_xmax = plsc->vppxma / plsc->xpmm; plsc->boxbb_ymin = plsc->vppymi / plsc->ypmm; plsc->boxbb_ymax = plsc->vppyma / plsc->ypmm; } // Set plot options from input lax = plP_stsearch( xopt, 'a' ); lbx = plP_stsearch( xopt, 'b' ); lcx = plP_stsearch( xopt, 'c' ); ldx = plP_stsearch( xopt, 'd' ); lgx = plP_stsearch( xopt, 'g' ); lix = plP_stsearch( xopt, 'i' ); llx = plP_stsearch( xopt, 'l' ); lsx = plP_stsearch( xopt, 's' ); ltx = plP_stsearch( xopt, 't' ); lux = plP_stsearch( xopt, 'u' ); lwx = plP_stsearch( xopt, 'w' ); lxx = plP_stsearch( xopt, 'x' ); lay = plP_stsearch( yopt, 'a' ); lby = plP_stsearch( yopt, 'b' ); lcy = plP_stsearch( yopt, 'c' ); ldy = plP_stsearch( yopt, 'd' ); lgy = plP_stsearch( yopt, 'g' ); liy = plP_stsearch( yopt, 'i' ); lly = plP_stsearch( yopt, 'l' ); lsy = plP_stsearch( yopt, 's' ); lty = plP_stsearch( yopt, 't' ); luy = plP_stsearch( yopt, 'u' ); lwy = plP_stsearch( yopt, 'w' ); lxy = plP_stsearch( yopt, 'x' ); // Tick and subtick sizes in device coords xmajor = MAX( ROUND( plsc->majht * plsc->ypmm ), 1 ); ymajor = MAX( ROUND( plsc->majht * plsc->xpmm ), 1 ); xminor = MAX( ROUND( plsc->minht * plsc->ypmm ), 1 ); yminor = MAX( ROUND( plsc->minht * plsc->xpmm ), 1 ); nxsub1 = nxsub; nysub1 = nysub; xtick1 = llx ? 1.0 : xtick; ytick1 = lly ? 1.0 : ytick; plP_xgvpw( &vpwxmin, &vpwxmax, &vpwymin, &vpwymax ); // vpwxmi always numerically less than vpwxma, and // similarly for vpwymi vpwxmi = ( vpwxmax > vpwxmin ) ? vpwxmin : vpwxmax; vpwxma = ( vpwxmax > vpwxmin ) ? vpwxmax : vpwxmin; vpwymi = ( vpwymax > vpwymin ) ? vpwymin : vpwymax; vpwyma = ( vpwymax > vpwymin ) ? vpwymax : vpwymin; // Plot axes only if they are inside viewport. lax = lax && vpwymi < y0 && y0 < vpwyma; lay = lay && vpwxmi < x0 && x0 < vpwxma; // Calculate tick spacing if ( ltx || lgx || lxx ) pldtik( vpwxmi, vpwxma, &xtick1, &nxsub1, ldx ); if ( lty || lgy || lxy ) pldtik( vpwymi, vpwyma, &ytick1, &nysub1, ldy ); // n.b. large change; xtick1, nxsub1, ytick1, nysub1 always positive. // Set up tick variables if ( lix ) { i1x = xminor; i2x = 0; i3x = xmajor; i4x = 0; } else { i1x = 0; i2x = xminor; i3x = 0; i4x = xmajor; } if ( liy ) { i1y = yminor; i2y = 0; i3y = ymajor; i4y = 0; } else { i1y = 0; i2y = yminor; i3y = 0; i4y = ymajor; } if ( plsc->if_boxbb ) { // Carefully follow logic below (and above) for the case where // an inverted major tick mark is written (in the X direction // for a Y axis and vice versa). Ignore minor tick marks // which are assumed to be smaller. Ignore axes and grids // which are all contained within the viewport. if ( lix && ( lbx || lux ) && ( ltx && !lxx ) ) plsc->boxbb_ymin -= xmajor / plsc->ypmm; if ( liy && ( lcy || lwy ) && ( lty && !lxy ) ) plsc->boxbb_xmax += ymajor / plsc->xpmm; if ( lix && ( lcx || lwx ) && ( ltx && !lxx ) ) plsc->boxbb_ymax += xmajor / plsc->ypmm; if ( liy && ( lby || luy ) && ( lty && !lxy ) ) plsc->boxbb_xmin -= ymajor / plsc->xpmm; } else { // Draw the bottom frame of the box if ( lbx || lux ) { if ( !lux ) { plP_movphy( vppxmi, vppymi ); plP_draphy( vppxma, vppymi ); } if ( ltx && !lxx ) { if ( ldx ) { pldtfac( vpwxmi, vpwxma, &factor, &tstart ); tp = xtick1 * ( floor( ( vpwxmi - tstart ) / xtick1 ) ) + tstart; } else tp = xtick1 * floor( vpwxmi / xtick1 ); for (;; ) { tn = tp + xtick1; if ( lsx ) { if ( llx ) { for ( i = 0; i <= 7; i++ ) { temp = tp + xlog[i]; if ( BETW( temp, vpwxmi, vpwxma ) ) plxtik( plP_wcpcx( temp ), vppymi, i1x, i2x ); } } else { for ( i = 1; i <= nxsub1 - 1; i++ ) { temp = tp + i * xtick1 / nxsub1; if ( BETW( temp, vpwxmi, vpwxma ) ) plxtik( plP_wcpcx( temp ), vppymi, i1x, i2x ); } } } if ( !BETW( tn, vpwxmi, vpwxma ) ) break; plxtik( plP_wcpcx( tn ), vppymi, i3x, i4x ); tp = tn; } } } // Draw the right-hand frame of box if ( lcy || lwy ) { if ( !lwy ) { plP_movphy( vppxma, vppymi ); plP_draphy( vppxma, vppyma ); } if ( lty && !lxy ) { if ( ldy ) { pldtfac( vpwymi, vpwyma, &factor, &tstart ); tp = ytick1 * ( floor( ( vpwymi - tstart ) / ytick1 ) ) + tstart; } else tp = ytick1 * floor( vpwymi / ytick1 ); for (;; ) { tn = tp + ytick1; if ( lsy ) { if ( lly ) { for ( i = 0; i <= 7; i++ ) { temp = tp + xlog[i]; if ( BETW( temp, vpwymi, vpwyma ) ) plytik( vppxma, plP_wcpcy( temp ), i2y, i1y ); } } else { for ( i = 1; i <= nysub1 - 1; i++ ) { temp = tp + i * ytick1 / nysub1; if ( BETW( temp, vpwymi, vpwyma ) ) plytik( vppxma, plP_wcpcy( temp ), i2y, i1y ); } } } if ( !BETW( tn, vpwymi, vpwyma ) ) break; plytik( vppxma, plP_wcpcy( tn ), i4y, i3y ); tp = tn; } } } // Draw the top frame of the box if ( lcx || lwx ) { if ( !lwx ) { plP_movphy( vppxma, vppyma ); plP_draphy( vppxmi, vppyma ); } if ( ltx && !lxx ) { if ( ldx ) { pldtfac( vpwxmi, vpwxma, &factor, &tstart ); tp = xtick1 * ( floor( ( vpwxma - tstart ) / xtick1 ) + 1 ) + tstart; } else tp = xtick1 * ( floor( vpwxma / xtick1 ) + 1 ); for (;; ) { tn = tp - xtick1; if ( lsx ) { if ( llx ) { for ( i = 7; i >= 0; i-- ) { temp = tn + xlog[i]; if ( BETW( temp, vpwxmi, vpwxma ) ) plxtik( plP_wcpcx( temp ), vppyma, i2x, i1x ); } } else { for ( i = nxsub1 - 1; i >= 1; i-- ) { temp = tn + i * xtick1 / nxsub1; if ( BETW( temp, vpwxmi, vpwxma ) ) plxtik( plP_wcpcx( temp ), vppyma, i2x, i1x ); } } } if ( !BETW( tn, vpwxmi, vpwxma ) ) break; plxtik( plP_wcpcx( tn ), vppyma, i4x, i3x ); tp = tn; } } } // Draw the left-hand frame of box if ( lby || luy ) { if ( !luy ) { plP_movphy( vppxmi, vppyma ); plP_draphy( vppxmi, vppymi ); } if ( lty && !lxy ) { if ( ldy ) { pldtfac( vpwymi, vpwyma, &factor, &tstart ); tp = ytick1 * ( floor( ( vpwyma - tstart ) / ytick1 ) + 1 ) + tstart; } else tp = ytick1 * ( floor( vpwyma / ytick1 ) + 1 ); for (;; ) { tn = tp - ytick1; if ( lsy ) { if ( lly ) { for ( i = 7; i >= 0; i-- ) { temp = tn + xlog[i]; if ( BETW( temp, vpwymi, vpwyma ) ) plytik( vppxmi, plP_wcpcy( temp ), i1y, i2y ); } } else { for ( i = nysub1 - 1; i >= 1; i-- ) { temp = tn + i * ytick1 / nysub1; if ( BETW( temp, vpwymi, vpwyma ) ) plytik( vppxmi, plP_wcpcy( temp ), i1y, i2y ); } } } if ( !BETW( tn, vpwymi, vpwyma ) ) break; plytik( vppxmi, plP_wcpcy( tn ), i3y, i4y ); tp = tn; } } } // Draw the horizontal axis. if ( lax ) { // Convert world coordinates to physical yp0 = plP_wcpcy( y0 ); plP_movphy( vppxmi, (PLINT) yp0 ); plP_draphy( vppxma, (PLINT) yp0 ); if ( ltx && !lxx ) { tp = xtick1 * floor( vpwxmi / xtick1 ); for (;; ) { tn = tp + xtick1; if ( lsx ) { if ( llx ) { for ( i = 0; i <= 7; i++ ) { temp = tp + xlog[i]; if ( BETW( temp, vpwxmi, vpwxma ) ) plxtik( plP_wcpcx( temp ), (PLINT) yp0, xminor, xminor ); } } else { for ( i = 1; i <= nxsub1 - 1; i++ ) { temp = tp + i * xtick1 / nxsub1; if ( BETW( temp, vpwxmi, vpwxma ) ) plxtik( plP_wcpcx( temp ), (PLINT) yp0, xminor, xminor ); } } } if ( !BETW( tn, vpwxmi, vpwxma ) ) break; plxtik( plP_wcpcx( tn ), (PLINT) yp0, xmajor, xmajor ); tp = tn; } } } // Draw the vertical axis. if ( lay ) { // Convert world coordinates to physical xp0 = plP_wcpcx( x0 ); plP_movphy( (PLINT) xp0, vppymi ); plP_draphy( (PLINT) xp0, vppyma ); if ( lty && !lxy ) { tp = ytick1 * floor( vpwymi / ytick1 ); for (;; ) { tn = tp + ytick1; if ( lsy ) { if ( lly ) { for ( i = 0; i <= 7; i++ ) { temp = tp + xlog[i]; if ( BETW( temp, vpwymi, vpwyma ) ) plytik( (PLINT) xp0, plP_wcpcy( temp ), yminor, yminor ); } } else { for ( i = 1; i <= nysub1 - 1; i++ ) { temp = tp + i * ytick1 / nysub1; if ( BETW( temp, vpwymi, vpwyma ) ) plytik( (PLINT) xp0, plP_wcpcy( temp ), yminor, yminor ); } } } if ( !BETW( tn, vpwymi, vpwyma ) ) break; plytik( (PLINT) xp0, plP_wcpcy( tn ), ymajor, ymajor ); tp = tn; } } } // Draw grids. grid_box( xopt, xtick1, nxsub1, yopt, ytick1, nysub1 ); } // Write labels. label_box( xopt, xtick1, yopt, ytick1 ); // Restore the clip limits to viewport edge plP_sclp( lxmin, lxmax, lymin, lymax ); } //-------------------------------------------------------------------------- // void plbox3() // // This is the 3-d analogue of plbox(). //-------------------------------------------------------------------------- void c_plbox3( PLCHAR_VECTOR xopt, PLCHAR_VECTOR xlabel, PLFLT xtick, PLINT nxsub, PLCHAR_VECTOR yopt, PLCHAR_VECTOR ylabel, PLFLT ytick, PLINT nysub, PLCHAR_VECTOR zopt, PLCHAR_VECTOR zlabel, PLFLT ztick, PLINT nzsub ) { PLFLT dx, dy, tx, ty, ux, uy; PLFLT xmin, xmax, ymin, ymax, zmin, zmax, zscale; PLFLT cxx, cxy, cyx, cyy, cyz; PLINT ln; PLINT *zbflg, *zbcol; PLFLT *zbwidth; PLFLT *zbtck; PLINT xdigmax, xdigits; PLINT ydigmax, ydigits; PLINT zdigmax, zdigits; if ( plsc->level < 3 ) { plabort( "plbox3: Please set up window first" ); return; } plP_gw3wc( &cxx, &cxy, &cyx, &cyy, &cyz ); plP_gdom( &xmin, &xmax, &ymin, &ymax ); plP_grange( &zscale, &zmin, &zmax ); plgxax( &xdigmax, &xdigits ); plgyax( &ydigmax, &ydigits ); plgzax( &zdigmax, &zdigits ); xdigits = xdigmax; ydigits = ydigmax; zdigits = zdigmax; // We have to wait until after the plot is drawn to draw back // grid so store this stuff. plP_gzback( &zbflg, &zbcol, &zbtck, &zbwidth ); *zbflg = plP_stsearch( zopt, 'd' ); if ( *zbflg ) { *zbtck = ztick; // save tick spacing *zbcol = plsc->icol0; // and color *zbwidth = plsc->width; // and line width } if ( cxx >= 0.0 && cxy <= 0.0 ) { ln = plP_stsearch( xopt, 'n' ); tx = plP_w3wcx( xmin, ymin, zmin ); ty = plP_w3wcy( xmin, ymin, zmin ); ux = plP_w3wcx( xmax, ymin, zmin ); uy = plP_w3wcy( xmax, ymin, zmin ); plxybx( xopt, xlabel, PL_X_AXIS, tx, ty, ux, uy, xmin, xmax, xtick, nxsub, 0, &xdigits ); dx = ux - tx; dy = uy - ty; plzbx( zopt, zlabel, 1, dx, dy, ux, uy, plP_w3wcy( xmax, ymin, zmax ), zmin, zmax, ztick, nzsub, &zdigits ); tx = plP_w3wcx( xmin, ymax, zmin ); ty = plP_w3wcy( xmin, ymax, zmin ); ux = plP_w3wcx( xmin, ymin, zmin ); uy = plP_w3wcy( xmin, ymin, zmin ); plxybx( yopt, ylabel, PL_Y_AXIS, tx, ty, ux, uy, ymax, ymin, ytick, nysub, ln, &ydigits ); dx = ux - tx; dy = uy - ty; // restore zdigits to initial value for second call zdigits = zdigmax; plzbx( zopt, zlabel, 0, dx, dy, tx, ty, plP_w3wcy( xmin, ymax, zmax ), zmin, zmax, ztick, nzsub, &zdigits ); } else if ( cxx <= 0.0 && cxy <= 0.0 ) { ln = plP_stsearch( yopt, 'n' ); tx = plP_w3wcx( xmin, ymax, zmin ); ty = plP_w3wcy( xmin, ymax, zmin ); ux = plP_w3wcx( xmin, ymin, zmin ); uy = plP_w3wcy( xmin, ymin, zmin ); plxybx( yopt, ylabel, PL_Y_AXIS, tx, ty, ux, uy, ymax, ymin, ytick, nysub, 0, &ydigits ); dx = ux - tx; dy = uy - ty; plzbx( zopt, zlabel, 1, dx, dy, ux, uy, plP_w3wcy( xmin, ymin, zmax ), zmin, zmax, ztick, nzsub, &zdigits ); tx = plP_w3wcx( xmax, ymax, zmin ); ty = plP_w3wcy( xmax, ymax, zmin ); ux = plP_w3wcx( xmin, ymax, zmin ); uy = plP_w3wcy( xmin, ymax, zmin ); plxybx( xopt, xlabel, PL_X_AXIS, tx, ty, ux, uy, xmax, xmin, xtick, nxsub, ln, &xdigits ); dx = ux - tx; dy = uy - ty; // restore zdigits to initial value for second call zdigits = zdigmax; plzbx( zopt, zlabel, 0, dx, dy, tx, ty, plP_w3wcy( xmax, ymax, zmax ), zmin, zmax, ztick, nzsub, &zdigits ); } else if ( cxx <= 0.0 && cxy >= 0.0 ) { ln = plP_stsearch( xopt, 'n' ); tx = plP_w3wcx( xmax, ymax, zmin ); ty = plP_w3wcy( xmax, ymax, zmin ); ux = plP_w3wcx( xmin, ymax, zmin ); uy = plP_w3wcy( xmin, ymax, zmin ); plxybx( xopt, xlabel, PL_X_AXIS, tx, ty, ux, uy, xmax, xmin, xtick, nxsub, 0, &xdigits ); dx = ux - tx; dy = uy - ty; plzbx( zopt, zlabel, 1, dx, dy, ux, uy, plP_w3wcy( xmin, ymax, zmax ), zmin, zmax, ztick, nzsub, &zdigits ); tx = plP_w3wcx( xmax, ymin, zmin ); ty = plP_w3wcy( xmax, ymin, zmin ); ux = plP_w3wcx( xmax, ymax, zmin ); uy = plP_w3wcy( xmax, ymax, zmin ); plxybx( yopt, ylabel, PL_Y_AXIS, tx, ty, ux, uy, ymin, ymax, ytick, nysub, ln, &ydigits ); dx = ux - tx; dy = uy - ty; // restore zdigits to initial value for second call zdigits = zdigmax; plzbx( zopt, zlabel, 0, dx, dy, tx, ty, plP_w3wcy( xmax, ymin, zmax ), zmin, zmax, ztick, nzsub, &zdigits ); } else if ( cxx >= 0.0 && cxy >= 0.0 ) { ln = plP_stsearch( yopt, 'n' ); tx = plP_w3wcx( xmax, ymin, zmin ); ty = plP_w3wcy( xmax, ymin, zmin ); ux = plP_w3wcx( xmax, ymax, zmin ); uy = plP_w3wcy( xmax, ymax, zmin ); plxybx( yopt, ylabel, PL_X_AXIS, tx, ty, ux, uy, ymin, ymax, ytick, nysub, 0, &ydigits ); dx = ux - tx; dy = uy - ty; plzbx( zopt, zlabel, 1, dx, dy, ux, uy, plP_w3wcy( xmax, ymax, zmax ), zmin, zmax, ztick, nzsub, &zdigits ); tx = plP_w3wcx( xmin, ymin, zmin ); ty = plP_w3wcy( xmin, ymin, zmin ); ux = plP_w3wcx( xmax, ymin, zmin ); uy = plP_w3wcy( xmax, ymin, zmin ); plxybx( xopt, xlabel, PL_X_AXIS, tx, ty, ux, uy, xmin, xmax, xtick, nxsub, ln, &xdigits ); dx = ux - tx; dy = uy - ty; // restore zdigits to initial value for second call zdigits = zdigmax; plzbx( zopt, zlabel, 0, dx, dy, tx, ty, plP_w3wcy( xmin, ymin, zmax ), zmin, zmax, ztick, nzsub, &zdigits ); } plsxax( xdigmax, xdigits ); plsyax( ydigmax, ydigits ); plszax( zdigmax, zdigits ); } //-------------------------------------------------------------------------- // Support routines for 3d box draw. //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- // void plxybx() // // This draws a sloping line from (wx1,wy1) to (wx2,wy2) which represents an // axis of a 3-d graph with data values from "vmin" to "vmax". Depending on // "opt", vertical ticks and/or subticks are placed on the line at major tick // interval "tick" with "nsub" subticks between major ticks. If "tick" and/or // "nsub" is zero, automatic tick positions are computed // // b: Draw box boundary // d: Interpret axis as a date/time when writing labels // f: Always use fixed point numeric labels // i: Inverts tick marks (i.e. drawn downwards) // l: Logarithmic axes, major ticks at decades, minor ticks at units // n: Write numeric label // o: Use custom label function // s: Draw minor tick marks // t: Draw major tick marks // u: Write label on line //-------------------------------------------------------------------------- static void plxybx( PLCHAR_VECTOR opt, PLCHAR_VECTOR label, PLINT axis, PLFLT wx1, PLFLT wy1, PLFLT wx2, PLFLT wy2, PLFLT vmin_in, PLFLT vmax_in, PLFLT tick, PLINT nsub, PLINT PL_UNUSED( nolast ), PLINT *digits ) { static char string[STRING_LEN]; PLINT lb, ld, lf, li, ll, ln, ls, lt, lu, lo; PLINT major, minor, mode, prec, scale; PLINT i, i1, i2, i3, i4; PLINT nsub1; PLFLT pos, tn, tp, temp, height, tick1, vmin, vmax; // Note that 'tspace' is the minimim distance away (in fractional number // of ticks) from the boundary that an X or Y numerical label can be drawn. PLFLT dwx, dwy, lambda, tcrit, tspace = 0.1; PLCHAR_VECTOR esc_string = plgesc_string(); PLFLT tstart, factor; PLCHAR_VECTOR timefmt = NULL; vmin = ( vmax_in > vmin_in ) ? vmin_in : vmax_in; vmax = ( vmax_in > vmin_in ) ? vmax_in : vmin_in; dwx = wx2 - wx1; dwy = wy2 - wy1; // Tick and subtick sizes in device coords major = MAX( ROUND( plsc->majht * plsc->ypmm ), 1 ); minor = MAX( ROUND( plsc->minht * plsc->ypmm ), 1 ); tick1 = tick; nsub1 = nsub; lb = plP_stsearch( opt, 'b' ); ld = plP_stsearch( opt, 'd' ); lf = plP_stsearch( opt, 'f' ); li = plP_stsearch( opt, 'i' ); ll = plP_stsearch( opt, 'l' ); ln = plP_stsearch( opt, 'n' ); lo = plP_stsearch( opt, 'o' ); ls = plP_stsearch( opt, 's' ); lt = plP_stsearch( opt, 't' ); lu = plP_stsearch( opt, 'u' ); if ( lu ) plxytx( wx1, wy1, wx2, wy2, 3.2, 0.5, 0.5, label ); if ( !lb ) return; if ( ll ) tick1 = ( vmax > vmin ) ? 1.0 : -1.0; if ( lt ) pldtik( vmin, vmax, &tick1, &nsub1, ld ); if ( li ) { i1 = minor; i2 = 0; i3 = major; i4 = 0; } else { i1 = 0; i2 = minor; i3 = 0; i4 = major; } // Draw the line plP_movwor( wx1, wy1 ); plP_drawor( wx2, wy2 ); if ( lt ) { if ( ld ) { pldtfac( vmin, vmax, &factor, &tstart ); tp = tick1 * ( floor( ( vmin - tstart ) / tick1 ) ) + tstart; } else tp = tick1 * floor( vmin / tick1 ); for (;; ) { tn = tp + tick1; if ( ls ) { if ( ll ) { for ( i = 0; i <= 7; i++ ) { temp = tp + xlog[i]; if ( BETW( temp, vmin, vmax ) ) { lambda = ( vmax_in > vmin_in ) ? ( temp - vmin ) / ( vmax - vmin ) : ( vmax - temp ) / ( vmax - vmin ); plxtik( plP_wcpcx( (PLFLT) ( wx1 + lambda * dwx ) ), plP_wcpcy( (PLFLT) ( wy1 + lambda * dwy ) ), i1, i2 ); } } } else { for ( i = 1; i <= nsub1 - 1; i++ ) { temp = tp + i * ( tn - tp ) / nsub1; if ( BETW( temp, vmin, vmax ) ) { lambda = ( vmax_in > vmin_in ) ? ( temp - vmin ) / ( vmax - vmin ) : ( vmax - temp ) / ( vmax - vmin ); plxtik( plP_wcpcx( (PLFLT) ( wx1 + lambda * dwx ) ), plP_wcpcy( (PLFLT) ( wy1 + lambda * dwy ) ), i1, i2 ); } } } } temp = tn; if ( !BETW( temp, vmin, vmax ) ) break; lambda = ( vmax_in > vmin_in ) ? ( temp - vmin ) / ( vmax - vmin ) : ( vmax - temp ) / ( vmax - vmin ); plxtik( plP_wcpcx( (PLFLT) ( wx1 + lambda * dwx ) ), plP_wcpcy( (PLFLT) ( wy1 + lambda * dwy ) ), i3, i4 ); tp = tn; } } // Label the line if ( ln && lt ) { pldprec( vmin, vmax, tick1, lf, &mode, &prec, *digits, &scale ); timefmt = plP_gtimefmt(); pos = 1.0; height = 3.2; tcrit = tspace * tick1; if ( ld ) { pldtfac( vmin, vmax, &factor, &tstart ); tp = tick1 * ( 1. + floor( ( vmin - tstart ) / tick1 ) ) + tstart; } else tp = tick1 * ( 1. + floor( vmin / tick1 ) ); for ( tn = tp; BETW( tn, vmin, vmax ); tn += tick1 ) { if ( BETW( tn, vmin + tcrit, vmax - tcrit ) ) { if ( ld ) strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig ); else plform( axis, tn, scale, prec, string, STRING_LEN, ll, lf, lo ); pos = ( vmax_in > vmin_in ) ? ( tn - vmin ) / ( vmax - vmin ) : ( vmax - tn ) / ( vmax - vmin ); plxytx( wx1, wy1, wx2, wy2, 1.5, pos, 0.5, string ); } } *digits = 2; if ( !ll && !lo && !ld && mode ) { snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) scale, esc_string ); plxytx( wx1, wy1, wx2, wy2, height, 1.0, 0.5, string ); } } } //-------------------------------------------------------------------------- // void plxytx() // // Prints out text along a sloping axis joining world coordinates // (wx1,wy1) to (wx2,wy2). Parameters are as for plmtext. //-------------------------------------------------------------------------- static void plxytx( PLFLT wx1, PLFLT wy1, PLFLT wx2, PLFLT wy2, PLFLT disp, PLFLT pos, PLFLT just, PLCHAR_VECTOR text ) { PLINT x, y, refx, refy; PLFLT shift, cc, ss, wx, wy; PLFLT xdv, ydv, xmm, ymm, refxmm, refymm, xform[4], diag; PLFLT dispx, dispy; PLFLT chrdef, chrht; cc = plsc->wmxscl * ( wx2 - wx1 ); ss = plsc->wmyscl * ( wy2 - wy1 ); diag = sqrt( cc * cc + ss * ss ); cc /= diag; ss /= diag; wx = wx1 + pos * ( wx2 - wx1 ); wy = wy1 + pos * ( wy2 - wy1 ); xform[0] = cc; xform[1] = 0.0; xform[2] = ss; xform[3] = 1.0; xdv = plP_wcdcx( wx ); ydv = plP_wcdcy( wy ); dispx = 0.; dispy = -disp; plgchr( &chrdef, &chrht ); shift = ( just == 0.0 ) ? 0.0 : plstrl( text ) * just; xmm = plP_dcmmx( xdv ) + dispx * chrht; ymm = plP_dcmmy( ydv ) + dispy * chrht; refxmm = xmm - shift * xform[0]; refymm = ymm - shift * xform[2]; x = plP_mmpcx( xmm ); y = plP_mmpcy( ymm ); refx = plP_mmpcx( refxmm ); refy = plP_mmpcy( refymm ); plP_text( 0, just, xform, x, y, refx, refy, text ); } //-------------------------------------------------------------------------- // void plzbx() // // This draws a vertical line from (wx,wy1) to (wx,wy2) which represents the // vertical axis of a 3-d graph with data values from "vmin" to "vmax". // Depending on "opt", ticks and/or subticks are placed on the line at major // tick interval "tick" with "nsub" subticks between major ticks. If "tick" // and/or "nsub" is zero, automatic tick positions are computed // // b: Draws left-hand axis // c: Draws right-hand axis // N.B. d is already used for another purpose (back grid) in zopt // before this routine is called so chose e here to be as close to d // as possible without interfering with the historical use of d. // e: Interpret axis as a date/time when writing labels // f: Always use fixed point numeric labels // i: Inverts tick marks (i.e. drawn to the left) // l: Logarithmic axes, major ticks at decades, minor ticks at units // m: Write numeric label on right axis // n: Write numeric label on left axis // o: Use custom label function // s: Draw minor tick marks // t: Draw major tick marks // u: Writes left-hand label // v: Writes right-hand label //-------------------------------------------------------------------------- static void plzbx( PLCHAR_VECTOR opt, PLCHAR_VECTOR label, PLINT right, PLFLT dx, PLFLT dy, PLFLT wx, PLFLT wy1, PLFLT wy2, PLFLT vmin_in, PLFLT vmax_in, PLFLT tick, PLINT nsub, PLINT *digits ) { static char string[STRING_LEN]; PLINT lb, lc, ld, lf, li, ll, lm, ln, ls, lt, lu, lv, lo; PLINT i, mode, prec, scale; PLINT nsub1, lstring; PLFLT pos, tn, tp, temp, height, tick1; PLFLT dwy, lambda, diag, major, minor, xmajor, xminor; PLFLT ymajor, yminor, dxm, dym, vmin, vmax; PLCHAR_VECTOR esc_string = plgesc_string(); PLFLT tstart, factor; PLCHAR_VECTOR timefmt = NULL; vmin = ( vmax_in > vmin_in ) ? vmin_in : vmax_in; vmax = ( vmax_in > vmin_in ) ? vmax_in : vmin_in; dwy = wy2 - wy1; // Tick and subtick sizes in device coords major = plsc->majht; minor = plsc->minht; tick1 = tick; nsub1 = nsub; lb = plP_stsearch( opt, 'b' ); lc = plP_stsearch( opt, 'c' ); ld = plP_stsearch( opt, 'e' ); lf = plP_stsearch( opt, 'f' ); li = plP_stsearch( opt, 'i' ); ll = plP_stsearch( opt, 'l' ); lm = plP_stsearch( opt, 'm' ); ln = plP_stsearch( opt, 'n' ); lo = plP_stsearch( opt, 'o' ); ls = plP_stsearch( opt, 's' ); lt = plP_stsearch( opt, 't' ); lu = plP_stsearch( opt, 'u' ); lv = plP_stsearch( opt, 'v' ); if ( lu && !right ) plztx( "h", dx, dy, wx, wy1, wy2, 5.0, 0.5, 0.5, label ); if ( lv && right ) plztx( "h", dx, dy, wx, wy1, wy2, -5.0, 0.5, 0.5, label ); if ( right && !lc ) return; if ( !right && !lb ) return; if ( ll ) tick1 = 1.0; if ( lt ) pldtik( vmin, vmax, &tick1, &nsub1, ld ); if ( ( li && !right ) || ( !li && right ) ) { minor = -minor; major = -major; } dxm = dx * plsc->wmxscl; dym = dy * plsc->wmyscl; diag = sqrt( dxm * dxm + dym * dym ); xminor = minor * dxm / diag; xmajor = major * dxm / diag; yminor = minor * dym / diag; ymajor = major * dym / diag; // Draw the line plP_movwor( wx, wy1 ); plP_drawor( wx, wy2 ); if ( lt ) { if ( ld ) { pldtfac( vmin, vmax, &factor, &tstart ); tp = tick1 * ( floor( ( vmin - tstart ) / tick1 ) ) + tstart; } else tp = tick1 * floor( vmin / tick1 ); for (;; ) { tn = tp + tick1; if ( ls ) { if ( ll ) { for ( i = 0; i <= 7; i++ ) { temp = tp + xlog[i]; if ( BETW( temp, vmin, vmax ) ) { lambda = ( vmax_in > vmin_in ) ? ( temp - vmin ) / ( vmax - vmin ) : ( vmax - temp ) / ( vmax - vmin ); plstik( plP_wcmmx( wx ), plP_wcmmy( (PLFLT) ( wy1 + lambda * dwy ) ), xminor, yminor ); } } } else { for ( i = 1; i <= nsub1 - 1; i++ ) { temp = tp + i * tick1 / nsub1; if ( BETW( temp, vmin, vmax ) ) { lambda = ( vmax_in > vmin_in ) ? ( temp - vmin ) / ( vmax - vmin ) : ( vmax - temp ) / ( vmax - vmin ); plstik( plP_wcmmx( wx ), plP_wcmmy( (PLFLT) ( wy1 + lambda * dwy ) ), xminor, yminor ); } } } } temp = tn; if ( !BETW( temp, vmin, vmax ) ) break; lambda = ( vmax_in > vmin_in ) ? ( temp - vmin ) / ( vmax - vmin ) : ( vmax - temp ) / ( vmax - vmin ); plstik( plP_wcmmx( wx ), plP_wcmmy( (PLFLT) ( wy1 + lambda * dwy ) ), xmajor, ymajor ); tp = tn; } } // Label the line if ( ( ln || lm ) && lt ) { pldprec( vmin, vmax, tick1, lf, &mode, &prec, *digits, &scale ); timefmt = plP_gtimefmt(); *digits = 0; if ( ld ) { pldtfac( vmin, vmax, &factor, &tstart ); tp = tick1 * ( floor( ( vmin - tstart ) / tick1 ) ) + tstart; } else tp = tick1 * floor( vmin / tick1 ); for ( tn = tp + tick1; BETW( tn, vmin, vmax ); tn += tick1 ) { if ( ld ) strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig ); else plform( PL_Z_AXIS, tn, scale, prec, string, STRING_LEN, ll, lf, lo ); pos = ( vmax_in > vmin_in ) ? ( tn - vmin ) / ( vmax - vmin ) : ( vmax - tn ) / ( vmax - vmin ); if ( ln && !right ) plztx( "v", dx, dy, wx, wy1, wy2, 0.5, pos, 1.0, string ); if ( lm && right ) plztx( "v", dx, dy, wx, wy1, wy2, -0.5, pos, 0.0, string ); lstring = (PLINT) strlen( string ); *digits = MAX( *digits, lstring ); } if ( !ll && !lo && !ld && mode ) { snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) scale, esc_string ); pos = 1.15; height = 0.5; if ( ln && !right ) { plztx( "v", dx, dy, wx, wy1, wy2, height, pos, 0.5, string ); } if ( lm && right ) { plztx( "v", dx, dy, wx, wy1, wy2, (PLFLT) -height, pos, 0.5, string ); } } } } //-------------------------------------------------------------------------- // void plztx() // // Prints out text along a vertical axis for a 3d plot joining // world coordinates (wx,wy1) to (wx,wy2). //-------------------------------------------------------------------------- static void plztx( PLCHAR_VECTOR opt, PLFLT dx, PLFLT dy, PLFLT wx, PLFLT wy1, PLFLT wy2, PLFLT disp, PLFLT pos, PLFLT just, PLCHAR_VECTOR text ) { PLINT refx = 0, refy = 0, x = 0, y = 0, vert = 0; PLFLT shift, cc, ss, wy; PLFLT xdv, ydv, xmm, ymm, refxmm, refymm, xform[4], diag; PLFLT dispx, dispy; PLFLT chrdef, chrht; cc = plsc->wmxscl * dx; ss = plsc->wmyscl * dy; diag = sqrt( cc * cc + ss * ss ); cc /= diag; ss /= diag; wy = wy1 + pos * ( wy2 - wy1 ); if ( plP_stsearch( opt, 'v' ) ) vert = 0; else if ( plP_stsearch( opt, 'h' ) ) vert = 1; if ( vert ) { xform[0] = 0.0; xform[1] = -cc; xform[2] = 1.0; xform[3] = -ss; } else { xform[0] = cc; xform[1] = 0.0; xform[2] = ss; xform[3] = 1.0; } xdv = plP_wcdcx( wx ); ydv = plP_wcdcy( wy ); dispx = -disp * cc; dispy = -disp * ss; plgchr( &chrdef, &chrht ); shift = ( just == 0.0 ) ? 0.0 : plstrl( text ) * just; xmm = plP_dcmmx( xdv ) + dispx * chrht; ymm = plP_dcmmy( ydv ) + dispy * chrht; refxmm = xmm - shift * xform[0]; refymm = ymm - shift * xform[2]; x = plP_mmpcx( xmm ); y = plP_mmpcy( ymm ); refx = plP_mmpcx( refxmm ); refy = plP_mmpcy( refymm ); plP_text( 0, just, xform, x, y, refx, refy, text ); } //-------------------------------------------------------------------------- // void grid_box() // // Draws grids at tick locations (major and/or minor). // // Note that 'tspace' is the minimim distance away (in fractional number // of ticks or subticks) from the boundary a grid line can be drawn. If // you are too close, it looks bad. //-------------------------------------------------------------------------- static void grid_box( PLCHAR_VECTOR xopt, PLFLT xtick1, PLINT nxsub1, PLCHAR_VECTOR yopt, PLFLT ytick1, PLINT nysub1 ) { PLINT lgx, lhx, llx, ldx; PLINT lgy, lhy, lly, ldy; PLFLT vpwxmi, vpwxma, vpwymi, vpwyma; PLFLT vpwxmin, vpwxmax, vpwymin, vpwymax; PLFLT tn, temp, tcrit, tspace = 0.1; PLFLT tstart, factor; PLINT i; // Set plot options from input lgx = plP_stsearch( xopt, 'g' ); lhx = plP_stsearch( xopt, 'h' ); llx = plP_stsearch( xopt, 'l' ); ldx = plP_stsearch( xopt, 'd' ); lgy = plP_stsearch( yopt, 'g' ); lhy = plP_stsearch( yopt, 'h' ); lly = plP_stsearch( yopt, 'l' ); ldy = plP_stsearch( yopt, 'd' ); plP_xgvpw( &vpwxmin, &vpwxmax, &vpwymin, &vpwymax ); // n.b. large change; vpwxmi always numerically less than vpwxma, and // similarly for vpwymi vpwxmi = ( vpwxmax > vpwxmin ) ? vpwxmin : vpwxmax; vpwxma = ( vpwxmax > vpwxmin ) ? vpwxmax : vpwxmin; vpwymi = ( vpwymax > vpwymin ) ? vpwymin : vpwymax; vpwyma = ( vpwymax > vpwymin ) ? vpwymax : vpwymin; // Draw grid in x direction. if ( lgx ) { if ( ldx ) { pldtfac( vpwxmi, vpwxma, &factor, &tstart ); tn = xtick1 * ( floor( ( vpwxmi - tstart ) / xtick1 ) ) + tstart; } else { tn = xtick1 * floor( vpwxmi / xtick1 ); } for (; tn <= vpwxma; tn += xtick1 ) { if ( lhx ) { if ( llx ) { PLFLT otemp = tn; for ( i = 0; i <= 7; i++ ) { temp = tn + xlog[i]; tcrit = ( temp - otemp ) * tspace; otemp = temp; if ( BETW( temp, vpwxmi + tcrit, vpwxma - tcrit ) ) pljoin( temp, vpwymi, temp, vpwyma ); } } else { for ( i = 1; i <= nxsub1 - 1; i++ ) { temp = tn + i * xtick1 / nxsub1; tcrit = xtick1 / nxsub1 * tspace; if ( BETW( temp, vpwxmi + tcrit, vpwxma - tcrit ) ) pljoin( temp, vpwymi, temp, vpwyma ); } } } tcrit = xtick1 * tspace; if ( BETW( tn, vpwxmi + tcrit, vpwxma - tcrit ) ) pljoin( tn, vpwymi, tn, vpwyma ); } } // Draw grid in y direction if ( lgy ) { if ( ldy ) { pldtfac( vpwymi, vpwyma, &factor, &tstart ); tn = ytick1 * ( floor( ( vpwymi - tstart ) / ytick1 ) ) + tstart; } else { tn = ytick1 * floor( vpwymi / ytick1 ); } for (; tn <= vpwyma; tn += ytick1 ) { if ( lhy ) { if ( lly ) { PLFLT otemp = tn; for ( i = 0; i <= 7; i++ ) { temp = tn + xlog[i]; tcrit = ( temp - otemp ) * tspace; otemp = temp; if ( BETW( temp, vpwymi + tcrit, vpwyma - tcrit ) ) pljoin( vpwxmi, temp, vpwxma, temp ); } } else { for ( i = 1; i <= nysub1 - 1; i++ ) { temp = tn + i * ytick1 / nysub1; tcrit = ytick1 / nysub1 * tspace; if ( BETW( temp, vpwymi + tcrit, vpwyma - tcrit ) ) pljoin( vpwxmi, temp, vpwxma, temp ); } } } tcrit = ytick1 * tspace; if ( BETW( tn, vpwymi + tcrit, vpwyma - tcrit ) ) pljoin( vpwxmi, tn, vpwxma, tn ); } } } //-------------------------------------------------------------------------- // void label_box() // // Writes numeric labels on side(s) of box. //-------------------------------------------------------------------------- static void label_box( PLCHAR_VECTOR xopt, PLFLT xtick1, PLCHAR_VECTOR yopt, PLFLT ytick1 ) { static char string[STRING_LEN]; PLBOOL ldx, lfx, lix, llx, lmx, lnx, ltx, lox, lxx; PLBOOL ldy, lfy, liy, lly, lmy, lny, lty, lvy, loy, lxy; PLFLT vpwxmi, vpwxma, vpwymi, vpwyma; PLFLT vpwxmin, vpwxmax, vpwymin, vpwymax; PLFLT tn, tp, offset; PLFLT factor, tstart; PLCHAR_VECTOR timefmt = NULL; PLFLT default_mm, char_height_mm, height_mm; PLFLT string_length_mm = 0.0, pos_mm = 0.0; // Assume label data is for placement of exponents if no custom // label function is provided. PLBOOL custom_exponent_placement = !plsc->label_func && plsc->label_data; // pos, height, and just are unnecessarily set to quiet // -O3 -Wuninitialized warnings that are obvious false alarms from // the clarity of the code associated with the true or false // result for custom_exponent_placement. PLFLT pos = 0.0, height = 0.0, just = 0.0; PLCHAR_VECTOR esc_string = plgesc_string(); plgchr( &default_mm, &char_height_mm ); // Set plot options from input ldx = plP_stsearch( xopt, 'd' ); lfx = plP_stsearch( xopt, 'f' ); lix = plP_stsearch( xopt, 'i' ); llx = plP_stsearch( xopt, 'l' ); lmx = plP_stsearch( xopt, 'm' ); lnx = plP_stsearch( xopt, 'n' ); ltx = plP_stsearch( xopt, 't' ); lox = plP_stsearch( xopt, 'o' ); lxx = plP_stsearch( xopt, 'x' ); ldy = plP_stsearch( yopt, 'd' ); lfy = plP_stsearch( yopt, 'f' ); liy = plP_stsearch( yopt, 'i' ); lly = plP_stsearch( yopt, 'l' ); lmy = plP_stsearch( yopt, 'm' ); lny = plP_stsearch( yopt, 'n' ); lty = plP_stsearch( yopt, 't' ); lvy = plP_stsearch( yopt, 'v' ); loy = plP_stsearch( yopt, 'o' ); lxy = plP_stsearch( yopt, 'x' ); plP_xgvpw( &vpwxmin, &vpwxmax, &vpwymin, &vpwymax ); // vpwxmi always numerically less than vpwxma, and // similarly for vpwymi vpwxmi = ( vpwxmax > vpwxmin ) ? vpwxmin : vpwxmax; vpwxma = ( vpwxmax > vpwxmin ) ? vpwxmax : vpwxmin; vpwymi = ( vpwymax > vpwymin ) ? vpwymin : vpwymax; vpwyma = ( vpwymax > vpwymin ) ? vpwymax : vpwymin; // Write label(s) for horizontal axes. if ( ( lmx || lnx ) && ( ltx || lxx ) ) { PLINT xmode, xprec, xdigmax, xdigits, xscale; plgxax( &xdigmax, &xdigits ); pldprec( vpwxmi, vpwxma, xtick1, lfx, &xmode, &xprec, xdigmax, &xscale ); timefmt = plP_gtimefmt(); if ( ldx ) { pldtfac( vpwxmi, vpwxma, &factor, &tstart ); tp = xtick1 * ( 1. + floor( ( vpwxmi - tstart ) / xtick1 ) ) + tstart; } else { tp = xtick1 * ( 1. + floor( vpwxmi / xtick1 ) ); } height = lix ? 1.75 : 1.5; if ( plsc->if_boxbb ) { // For horizontal axes, height of zero corresponds to // character centred on edge so should add 0.5 to height // to obtain bounding box edge in direction away from // edge. However, experimentally found 0.7 gave a better // looking result. height_mm = ( height + 0.7 ) * char_height_mm; if ( lnx ) plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi / plsc->ypmm - height_mm ); if ( lmx ) plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma / plsc->ypmm + height_mm ); } for ( tn = tp; BETW( tn, vpwxmi, vpwxma ); tn += xtick1 ) { if ( ldx ) { strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig ); } else { plform( PL_X_AXIS, tn, xscale, xprec, string, STRING_LEN, llx, lfx, lox ); } pos = ( vpwxmax > vpwxmin ) ? ( tn - vpwxmi ) / ( vpwxma - vpwxmi ) : ( vpwxma - tn ) / ( vpwxma - vpwxmi ); if ( plsc->if_boxbb ) { string_length_mm = plstrl( string ); pos_mm = ( plsc->vppxmi + pos * ( plsc->vppxma - plsc->vppxmi ) ) / plsc->xpmm; } if ( lnx ) { // Bottom axis. if ( plsc->if_boxbb ) { plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, pos_mm - 0.5 * string_length_mm ); plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, pos_mm + 0.5 * string_length_mm ); } else { plmtex( "b", height, pos, 0.5, string ); } } if ( lmx ) { // Top axis. if ( plsc->if_boxbb ) { plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, pos_mm - 0.5 * string_length_mm ); plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, pos_mm + 0.5 * string_length_mm ); } else { plmtex( "t", height, pos, 0.5, string ); } } } xdigits = 2; plsxax( xdigmax, xdigits ); // Write separate exponential label if mode = 1. if ( !llx && !ldx && !lox && xmode ) { if ( custom_exponent_placement ) { height = ( (PLLabelDefaults *) plsc->label_data )->exp_label_disp; pos = ( (PLLabelDefaults *) plsc->label_data )->exp_label_pos; just = ( (PLLabelDefaults *) plsc->label_data )->exp_label_just; } else { height = 3.2; pos = 1.0; just = 0.5; } snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) xscale, esc_string ); if ( lnx ) { // Bottom axis exponent. if ( plsc->if_boxbb ) { // For horizontal axes, height of zero corresponds // to character centred on edge so should add 0.5 // to height to obtain bounding box edge in // direction away from edge if no exponent. Add // an additional offset to make exponent fit. height_mm = ( height + 0.9 ) * char_height_mm; plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi / plsc->ypmm - height_mm ); string_length_mm = plstrl( string ); pos_mm = ( plsc->vppxmi + pos * ( plsc->vppxma - plsc->vppxmi ) ) / plsc->xpmm; plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, pos_mm - 0.5 * string_length_mm ); plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, pos_mm + 0.5 * string_length_mm ); } else { plmtex( "b", height, pos, just, string ); } } if ( lmx ) { // Top axis exponent. if ( plsc->if_boxbb ) { // For horizontal axes, height of zero corresponds // to character centred on edge so should add 0.5 // to height to obtain bounding box edge in // direction away from edge if no exponent. Add // an additional offset to make exponent fit. height_mm = ( height + 1.4 ) * char_height_mm; plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma / plsc->ypmm + height_mm ); string_length_mm = plstrl( string ); pos_mm = ( plsc->vppxmi + pos * ( plsc->vppxma - plsc->vppxmi ) ) / plsc->xpmm; plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, pos_mm - 0.5 * string_length_mm ); plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, pos_mm + 0.5 * string_length_mm ); } else { plmtex( "t", height, pos, just, string ); } } } } // Write label(s) for vertical axes. if ( ( lmy || lny ) && ( lty || lxy ) ) { PLINT ymode, yprec, ydigmax, ydigits, yscale; plgyax( &ydigmax, &ydigits ); pldprec( vpwymi, vpwyma, ytick1, lfy, &ymode, &yprec, ydigmax, &yscale ); ydigits = 0; if ( ldy ) { pldtfac( vpwymi, vpwyma, &factor, &tstart ); tp = ytick1 * ( 1. + floor( ( vpwymi - tstart ) / ytick1 ) ) + tstart; } else { tp = ytick1 * ( 1. + floor( vpwymi / ytick1 ) ); } for ( tn = tp; BETW( tn, vpwymi, vpwyma ); tn += ytick1 ) { if ( ldy ) { strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig ); } else { plform( PL_Y_AXIS, tn, yscale, yprec, string, STRING_LEN, lly, lfy, loy ); } pos = ( vpwymax > vpwymin ) ? ( tn - vpwymi ) / ( vpwyma - vpwymi ) : ( vpwyma - tn ) / ( vpwyma - vpwymi ); if ( lny ) { if ( lvy ) { // Left axis with text written perpendicular to edge. height = liy ? 1.0 : 0.5; if ( plsc->if_boxbb ) { // For vertical axes with text written // perpendicular to edge, height of zero // corresponds character centred on edge so // should add 0.5 to height to obtain bounding // box edge in direction away from edge, and // that value apparently works. height_mm = ( height + 0.0 ) * char_height_mm; string_length_mm = plstrl( string ); plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi / plsc->xpmm - height_mm - string_length_mm ); pos_mm = ( plsc->vppymi + pos * ( plsc->vppyma - plsc->vppymi ) ) / plsc->ypmm; // Expected offset is 0.5, but adjust to improve // look of result. plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, pos_mm - 0.6 * char_height_mm ); plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, pos_mm + 0.7 * char_height_mm ); } else { plmtex( "lv", height, pos, 1.0, string ); } } else { // Left axis with text written parallel to edge. height = liy ? 1.75 : 1.5; if ( plsc->if_boxbb ) { // For vertical axes with text written // parallel to edge, height of zero // corresponds to character centred on edge so // should add 0.5 to height to obtain bounding // box edge in direction away from edge, // However, experimentally found 0.8 gave a // better looking result. height_mm = ( height + 0.8 ) * char_height_mm; plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi / plsc->xpmm - height_mm ); pos_mm = ( plsc->vppymi + pos * ( plsc->vppyma - plsc->vppymi ) ) / plsc->ypmm; string_length_mm = plstrl( string ); plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, pos_mm - 0.5 * string_length_mm ); plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, pos_mm + 0.5 * string_length_mm ); } else { plmtex( "l", height, pos, 0.5, string ); } } } if ( lmy ) { if ( lvy ) { // Right axis with text written perpendicular to edge. height = liy ? 1.0 : 0.5; if ( plsc->if_boxbb ) { // For vertical axes with text written // perpendicular to edge, height of zero // corresponds character centred on edge so // should add 0.5 to height to obtain bounding // box edge in direction away from edge, and // that value apparently works. height_mm = ( height + 0.0 ) * char_height_mm; string_length_mm = plstrl( string ); plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma / plsc->xpmm + height_mm + string_length_mm ); pos_mm = ( plsc->vppymi + pos * ( plsc->vppyma - plsc->vppymi ) ) / plsc->ypmm; // Expected offset is 0.5, but adjust to improve // look of result. plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, pos_mm - 0.6 * char_height_mm ); plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, pos_mm + 0.7 * char_height_mm ); } else { plmtex( "rv", height, pos, 0.0, string ); } } else { // Right axis with text written parallel to edge. height = liy ? 1.75 : 1.5; if ( plsc->if_boxbb ) { // For vertical axes with text written // parallel to edge, height of zero // corresponds to character centred on edge so // should add 0.5 to height to obtain bounding // box edge in direction away from edge, // However, experimentally found 0.8 gave a // better looking result. height_mm = ( height + 0.8 ) * char_height_mm; plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma / plsc->xpmm + height_mm ); pos_mm = ( plsc->vppymi + pos * ( plsc->vppyma - plsc->vppymi ) ) / plsc->ypmm; string_length_mm = plstrl( string ); plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, pos_mm - 0.5 * string_length_mm ); plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, pos_mm + 0.5 * string_length_mm ); } else { plmtex( "r", height, pos, 0.5, string ); } } } ydigits = MAX( ydigits, (PLINT) strlen( string ) ); } if ( !lvy ) ydigits = 2; plsyax( ydigmax, ydigits ); // Write separate exponential label if mode = 1. if ( !lly && !ldy && !loy && ymode ) { snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) yscale, esc_string ); if ( custom_exponent_placement ) { height = ( (PLLabelDefaults *) plsc->label_data )->exp_label_disp; pos = ( (PLLabelDefaults *) plsc->label_data )->exp_label_pos; just = ( (PLLabelDefaults *) plsc->label_data )->exp_label_just; } if ( lvy ) { offset = 0.1; // more space to clear labels in "v" mode } else { offset = 0.02; } // Left axis exponent if ( lny ) { if ( !custom_exponent_placement ) { height = 3.2; pos = 1.0 + offset; just = 0.5; } if ( plsc->if_boxbb ) { // For horizontal axes, height of zero corresponds // to character centred on edge so should add 0.5 // to height to obtain bounding box edge in // direction away from edge if no exponent. Add // an additional offset to make exponent fit. height_mm = ( height + 1.4 ) * char_height_mm; plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma / plsc->ypmm + height_mm ); string_length_mm = plstrl( string ); pos_mm = ( plsc->vppxmi + pos * ( plsc->vppxma - plsc->vppxmi ) ) / plsc->xpmm; plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, pos_mm - string_length_mm ); } else { if ( lvy ) { plmtex( "lv", height, pos, just, string ); } else { plmtex( "l", height, pos, just, string ); } } } // Right axis exponent. if ( lmy ) { if ( !custom_exponent_placement ) { height = 3.4; // Extra space for superscript pos = 1.0 + offset; just = 0.5; } if ( plsc->if_boxbb ) { // For horizontal axes, height of zero corresponds // to character centred on edge so should add 0.5 // to height to obtain bounding box edge in // direction away from edge if no exponent. Add // an additional offset to make exponent fit. height_mm = ( height + 1.4 ) * char_height_mm; plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma / plsc->ypmm + height_mm ); string_length_mm = plstrl( string ); pos_mm = ( plsc->vppxmi + pos * ( plsc->vppxma - plsc->vppxmi ) ) / plsc->xpmm; plsc->boxbb_xmax = MAX( plsc->boxbb_xmin, pos_mm + string_length_mm ); } else { if ( lvy ) { plmtex( "rv", height, pos, just, string ); } else { plmtex( "r", height, pos, just, string ); } } } } } } //-------------------------------------------------------------------------- // void label_box_custom() // // Writes numeric labels on side(s) of box in custom locations //-------------------------------------------------------------------------- void label_box_custom( PLCHAR_VECTOR xopt, PLINT n_xticks, PLFLT_VECTOR xticks, PLCHAR_VECTOR yopt, PLINT n_yticks, PLFLT_VECTOR yticks ) { static char string[STRING_LEN]; PLBOOL ldx, lfx, lix, llx, lmx, lnx, ltx, lox, lxx; PLBOOL ldy, lfy, liy, lly, lmy, lny, lty, lvy, loy, lxy; PLFLT vpwxmi, vpwxma, vpwymi, vpwyma; PLFLT vpwxmin, vpwxmax, vpwymin, vpwymax; PLFLT tn, offset; PLCHAR_VECTOR timefmt; PLINT i; PLINT xdigmax, xdigits, xdigmax_old, xdigits_old; PLINT ydigmax, ydigits, ydigmax_old, ydigits_old; PLINT lxmin, lxmax, lymin, lymax; PLINT pxmin, pxmax, pymin, pymax; PLFLT default_mm, char_height_mm, height_mm; PLFLT string_length_mm = 0.0, pos_mm = 0.0; // Assume label data is for placement of exponents if no custom // label function is provided. PLBOOL custom_exponent_placement = !plsc->label_func && plsc->label_data; // pos, height, and just are unnecessarily set to quiet // -O3 -Wuninitialized warnings that are obvious false alarms from // the clarity of the code associated with the true or false // result for custom_exponent_placement. PLFLT pos = 0.0, height = 0.0, just = 0.0; PLCHAR_VECTOR esc_string = plgesc_string(); plgchr( &default_mm, &char_height_mm ); // Save some parameters plgxax( &xdigmax, &xdigits ); plgyax( &ydigmax, &ydigits ); xdigmax_old = xdigmax; xdigits_old = xdigits; ydigmax_old = ydigmax; ydigits_old = ydigits; // Open the clip limits to the subpage limits plP_gclp( &lxmin, &lxmax, &lymin, &lymax ); plP_gphy( &pxmin, &pxmax, &pymin, &pymax ); plP_sclp( pxmin, pxmax, pymin, pymax ); // Set plot options from input ldx = plP_stsearch( xopt, 'd' ); lfx = plP_stsearch( xopt, 'f' ); lix = plP_stsearch( xopt, 'i' ); llx = plP_stsearch( xopt, 'l' ); lmx = plP_stsearch( xopt, 'm' ); lnx = plP_stsearch( xopt, 'n' ); ltx = plP_stsearch( xopt, 't' ); lox = plP_stsearch( xopt, 'o' ); lxx = plP_stsearch( xopt, 'x' ); ldy = plP_stsearch( yopt, 'd' ); lfy = plP_stsearch( yopt, 'f' ); liy = plP_stsearch( yopt, 'i' ); lly = plP_stsearch( yopt, 'l' ); lmy = plP_stsearch( yopt, 'm' ); lny = plP_stsearch( yopt, 'n' ); lty = plP_stsearch( yopt, 't' ); lvy = plP_stsearch( yopt, 'v' ); loy = plP_stsearch( yopt, 'o' ); lxy = plP_stsearch( yopt, 'x' ); plP_xgvpw( &vpwxmin, &vpwxmax, &vpwymin, &vpwymax ); // n.b. large change; vpwxmi always numerically less than vpwxma, and // similarly for vpwymi vpwxmi = ( vpwxmax > vpwxmin ) ? vpwxmin : vpwxmax; vpwxma = ( vpwxmax > vpwxmin ) ? vpwxmax : vpwxmin; vpwymi = ( vpwymax > vpwymin ) ? vpwymin : vpwymax; vpwyma = ( vpwymax > vpwymin ) ? vpwymax : vpwymin; if ( plsc->if_boxbb ) { PLINT xmajor = MAX( ROUND( plsc->majht * plsc->ypmm ), 1 ); PLINT ymajor = MAX( ROUND( plsc->majht * plsc->xpmm ), 1 ); // Bounding-box limits for the box in mm before corrections // for decorations are applied. plsc->boxbb_xmin = plsc->vppxmi / plsc->xpmm; plsc->boxbb_xmax = plsc->vppxma / plsc->xpmm; plsc->boxbb_ymin = plsc->vppymi / plsc->ypmm; plsc->boxbb_ymax = plsc->vppyma / plsc->ypmm; // Carefully follow logic below for the case where // an inverted major tick mark is written (in the X direction // for a Y axis and vice versa). Ignore minor tick marks // which are assumed to be smaller. if ( lix && ( lmx || lnx ) && ( ltx && !lxx ) ) { plsc->boxbb_ymin -= xmajor / plsc->ypmm; plsc->boxbb_ymax += xmajor / plsc->ypmm; } if ( liy && ( lmy || lny ) && ( lty && !lxy ) ) { plsc->boxbb_xmin -= ymajor / plsc->xpmm; plsc->boxbb_xmax += ymajor / plsc->xpmm; } } // Write label(s) for horizontal axes. if ( ( lmx || lnx ) && ( ltx || lxx ) ) { PLINT xmode, xprec, xscale; PLFLT x_spacing, x_spacing_tmp; // Determine spacing between ticks // Use the x-size of the window x_spacing = vpwxma - vpwxmi; if ( n_xticks > 1 ) { // Use the smallest space between ticks for ( i = 1; i < n_xticks; i++ ) { x_spacing_tmp = fabs( xticks[i] - xticks[i - 1] ); x_spacing = MIN( x_spacing, x_spacing_tmp ); } } plgxax( &xdigmax, &xdigits ); pldprec( vpwxmi, vpwxma, x_spacing, lfx, &xmode, &xprec, xdigmax, &xscale ); timefmt = plP_gtimefmt(); height = lix ? 1.75 : 1.5; if ( plsc->if_boxbb ) { // For horizontal axes, height of zero corresponds to // character centred on edge so should add 0.5 to height // to obtain bounding box edge in direction away from // edge. However, experimentally found 0.7 gave a better // looking result. height_mm = ( height + 0.7 ) * char_height_mm; if ( lnx ) plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi / plsc->ypmm - height_mm ); if ( lmx ) plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma / plsc->ypmm + height_mm ); } // Loop through all of the tick marks for ( i = 0; i < n_xticks; i++ ) { tn = xticks[i]; if ( BETW( tn, vpwxmi, vpwxma ) ) { if ( !lxx && !plsc->if_boxbb ) { plwxtik( tn, vpwymin, FALSE, !lix ); plwxtik( tn, vpwymax, FALSE, lix ); } if ( ldx ) { strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig ); } else { plform( PL_X_AXIS, tn, xscale, xprec, string, STRING_LEN, llx, lfx, lox ); } pos = ( vpwxmax > vpwxmin ) ? ( tn - vpwxmi ) / ( vpwxma - vpwxmi ) : ( vpwxma - tn ) / ( vpwxma - vpwxmi ); if ( plsc->if_boxbb ) { string_length_mm = plstrl( string ); pos_mm = ( plsc->vppxmi + pos * ( plsc->vppxma - plsc->vppxmi ) ) / plsc->xpmm; } if ( lnx ) { // Bottom axis. if ( plsc->if_boxbb ) { plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, pos_mm - 0.5 * string_length_mm ); plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, pos_mm + 0.5 * string_length_mm ); } else { plmtex( "b", height, pos, 0.5, string ); } } if ( lmx ) { // Top axis. if ( plsc->if_boxbb ) { plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, pos_mm - 0.5 * string_length_mm ); plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, pos_mm + 0.5 * string_length_mm ); } else { plmtex( "t", height, pos, 0.5, string ); } } } } xdigits = 2; plsxax( xdigmax, xdigits ); // Write separate exponential label if mode = 1. if ( !llx && !ldx && !lox && xmode ) { if ( custom_exponent_placement ) { height = ( (PLLabelDefaults *) plsc->label_data )->exp_label_disp; pos = ( (PLLabelDefaults *) plsc->label_data )->exp_label_pos; just = ( (PLLabelDefaults *) plsc->label_data )->exp_label_just; } else { height = 3.2; pos = 1.0; just = 0.5; } snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) xscale, esc_string ); if ( lnx ) { // Bottom axis exponent. if ( plsc->if_boxbb ) { // For horizontal axes, height of zero corresponds // to character centred on edge so should add 0.5 // to height to obtain bounding box edge in // direction away from edge if no exponent. Add // an additional offset to make exponent fit. height_mm = ( height + 0.9 ) * char_height_mm; plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi / plsc->ypmm - height_mm ); string_length_mm = plstrl( string ); pos_mm = ( plsc->vppxmi + pos * ( plsc->vppxma - plsc->vppxmi ) ) / plsc->xpmm; plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, pos_mm - 0.5 * string_length_mm ); plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, pos_mm + 0.5 * string_length_mm ); } else { plmtex( "b", height, pos, just, string ); } } if ( lmx ) { // Top axis exponent. if ( plsc->if_boxbb ) { // For horizontal axes, height of zero corresponds // to character centred on edge so should add 0.5 // to height to obtain bounding box edge in // direction away from edge if no exponent. Add // an additional offset to make exponent fit. height_mm = ( height + 1.4 ) * char_height_mm; plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma / plsc->ypmm + height_mm ); string_length_mm = plstrl( string ); pos_mm = ( plsc->vppxmi + pos * ( plsc->vppxma - plsc->vppxmi ) ) / plsc->xpmm; plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, pos_mm - 0.5 * string_length_mm ); plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, pos_mm + 0.5 * string_length_mm ); } else { plmtex( "t", height, pos, just, string ); } } } } // Write label(s) for vertical axes. if ( ( lmy || lny ) && ( lty || lxy ) ) { PLINT ymode, yprec, yscale; PLFLT y_spacing, y_spacing_tmp; // Determine spacing between ticks // Use the y-size of the window y_spacing = vpwyma - vpwymi; if ( n_yticks > 1 ) { // Use the smallest space between ticks for ( i = 1; i < n_yticks; i++ ) { y_spacing_tmp = fabs( yticks[i] - yticks[i - 1] ); y_spacing = MIN( y_spacing, y_spacing_tmp ); } } plgyax( &ydigmax, &ydigits ); pldprec( vpwymi, vpwyma, y_spacing, lfy, &ymode, &yprec, ydigmax, &yscale ); timefmt = plP_gtimefmt(); ydigits = 0; for ( i = 0; i < n_yticks; i++ ) { tn = yticks[i]; if ( BETW( tn, vpwymi, vpwyma ) ) { if ( !lxy && !plsc->if_boxbb ) { plwytik( vpwxmin, tn, FALSE, !liy ); plwytik( vpwxmax, tn, FALSE, liy ); } if ( ldy ) { strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig ); } else { plform( PL_Y_AXIS, tn, yscale, yprec, string, STRING_LEN, lly, lfy, loy ); } pos = ( vpwymax > vpwymin ) ? ( tn - vpwymi ) / ( vpwyma - vpwymi ) : ( vpwyma - tn ) / ( vpwyma - vpwymi ); if ( lny ) { if ( lvy ) { // Left axis with text written perpendicular to edge. height = liy ? 1.0 : 0.5; if ( plsc->if_boxbb ) { // For vertical axes with text written // perpendicular to edge, height of zero // corresponds character centred on edge so // should add 0.5 to height to obtain bounding // box edge in direction away from edge, and // that value apparently works. height_mm = ( height + 0.0 ) * char_height_mm; string_length_mm = plstrl( string ); plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi / plsc->xpmm - height_mm - string_length_mm ); pos_mm = ( plsc->vppymi + pos * ( plsc->vppyma - plsc->vppymi ) ) / plsc->ypmm; // Expected offset is 0.5, but adjust to improve // look of result. plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, pos_mm - 0.6 * char_height_mm ); plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, pos_mm + 0.7 * char_height_mm ); } else { plmtex( "lv", height, pos, 1.0, string ); } } else { // Left axis with text written parallel to edge. height = liy ? 1.75 : 1.5; if ( plsc->if_boxbb ) { // For vertical axes with text written // parallel to edge, height of zero // corresponds to character centred on edge so // should add 0.5 to height to obtain bounding // box edge in direction away from edge, // However, experimentally found 0.8 gave a // better looking result. height_mm = ( height + 0.8 ) * char_height_mm; plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi / plsc->xpmm - height_mm ); pos_mm = ( plsc->vppymi + pos * ( plsc->vppyma - plsc->vppymi ) ) / plsc->ypmm; string_length_mm = plstrl( string ); plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, pos_mm - 0.5 * string_length_mm ); plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, pos_mm + 0.5 * string_length_mm ); } else { plmtex( "l", height, pos, 0.5, string ); } } } if ( lmy ) { if ( lvy ) { // Right axis with text written perpendicular to edge. height = liy ? 1.0 : 0.5; if ( plsc->if_boxbb ) { // For vertical axes with text written // perpendicular to edge, height of zero // corresponds character centred on edge so // should add 0.5 to height to obtain bounding // box edge in direction away from edge, and // that value apparently works. height_mm = ( height + 0.0 ) * char_height_mm; string_length_mm = plstrl( string ); plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma / plsc->xpmm + height_mm + string_length_mm ); pos_mm = ( plsc->vppymi + pos * ( plsc->vppyma - plsc->vppymi ) ) / plsc->ypmm; // Expected offset is 0.5, but adjust to improve // look of result. plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, pos_mm - 0.6 * char_height_mm ); plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, pos_mm + 0.7 * char_height_mm ); } else { plmtex( "rv", height, pos, 0.0, string ); } } else { // Right axis with text written parallel to edge. height = liy ? 1.75 : 1.5; if ( plsc->if_boxbb ) { // For vertical axes with text written // parallel to edge, height of zero // corresponds to character centred on edge so // should add 0.5 to height to obtain bounding // box edge in direction away from edge, // However, experimentally found 0.8 gave a // better looking result. height_mm = ( height + 0.8 ) * char_height_mm; plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma / plsc->xpmm + height_mm ); pos_mm = ( plsc->vppymi + pos * ( plsc->vppyma - plsc->vppymi ) ) / plsc->ypmm; string_length_mm = plstrl( string ); plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, pos_mm - 0.5 * string_length_mm ); plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, pos_mm + 0.5 * string_length_mm ); } else { plmtex( "r", height, pos, 0.5, string ); } } } ydigits = MAX( ydigits, (PLINT) strlen( string ) ); } } if ( !lvy ) ydigits = 2; plsyax( ydigmax, ydigits ); // Write separate exponential label if mode = 1. if ( !lly && !ldy && !loy && ymode ) { snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) yscale, esc_string ); if ( custom_exponent_placement ) { height = ( (PLLabelDefaults *) plsc->label_data )->exp_label_disp; pos = ( (PLLabelDefaults *) plsc->label_data )->exp_label_pos; just = ( (PLLabelDefaults *) plsc->label_data )->exp_label_just; } if ( lvy ) { offset = 0.1; // more space to clear labels in "v" mode } else { offset = 0.02; } // Left axis exponent. if ( lny ) { if ( !custom_exponent_placement ) { height = 3.2; pos = 1.0 + offset; just = 0.5; } if ( plsc->if_boxbb ) { // For horizontal axes, height of zero corresponds // to character centred on edge so should add 0.5 // to height to obtain bounding box edge in // direction away from edge if no exponent. Add // an additional offset to make exponent fit. height_mm = ( height + 1.4 ) * char_height_mm; plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma / plsc->ypmm + height_mm ); string_length_mm = plstrl( string ); pos_mm = ( plsc->vppxmi + pos * ( plsc->vppxma - plsc->vppxmi ) ) / plsc->xpmm; plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, pos_mm - string_length_mm ); } else { if ( lvy ) { plmtex( "lv", height, pos, just, string ); } else { plmtex( "l", height, pos, just, string ); } } } // Right axis exponent. if ( lmy ) { if ( !custom_exponent_placement ) { height = 3.4; // Extra space for superscript pos = 1.0 + offset; just = 0.5; } if ( plsc->if_boxbb ) { // For horizontal axes, height of zero corresponds // to character centred on edge so should add 0.5 // to height to obtain bounding box edge in // direction away from edge if no exponent. Add // an additional offset to make exponent fit. height_mm = ( height + 1.4 ) * char_height_mm; plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma / plsc->ypmm + height_mm ); string_length_mm = plstrl( string ); pos_mm = ( plsc->vppxmi + pos * ( plsc->vppxma - plsc->vppxmi ) ) / plsc->xpmm; plsc->boxbb_xmax = MAX( plsc->boxbb_xmin, pos_mm + string_length_mm ); } else { if ( lvy ) { plmtex( "rv", height, pos, just, string ); } else { plmtex( "r", height, pos, just, string ); } } } } } // Restore saved parameters plsxax( xdigmax_old, xdigits_old ); plsyax( ydigmax_old, ydigits_old ); // Restore the clip limits to viewport edge plP_sclp( lxmin, lxmax, lymin, lymax ); } //-------------------------------------------------------------------------- // // Default labeling functions for PLplot // // These are the functions which are used internally by PLplot under various // conditions. // // They have been separated out for use in other PLplot functions and // potential exposure in the PLplot API. // //-------------------------------------------------------------------------- void plP_default_label_log( PLINT PL_UNUSED( axis ), PLFLT value, char *string, PLINT len, void * PL_UNUSED( data ) ) { PLCHAR_VECTOR esc_string = plgesc_string(); // Exponential, i.e. 10^-1, 10^0, 10^1, etc snprintf( string, (size_t) len, "10%su%d", esc_string, (int) ROUND( value ) ); } void plP_default_label_log_fixed( PLINT PL_UNUSED( axis ), PLFLT value, char *string, PLINT len, void * PL_UNUSED( data ) ) { // Fixed point, i.e. .1, 1, 10, etc int exponent = ROUND( value ); value = pow( 10.0, exponent ); if ( exponent < 0 ) { char form[FORMAT_LEN]; snprintf( form, FORMAT_LEN, "%%.%df", ABS( exponent ) ); snprintf( string, (size_t) len, form, value ); } else { snprintf( string, (size_t) len, "%d", (int) value ); } } void plP_default_label( PLINT PL_UNUSED( axis ), PLFLT value, char *string, PLINT len, void *data ) { PLINT scale, prec; PLINT setpre, precis; char form[FORMAT_LEN], temp[TEMP_LEN]; double scale2; scale = ( (PLINT *) data )[0]; prec = ( (PLINT *) data )[1]; plP_gprec( &setpre, &precis ); if ( setpre ) prec = precis; if ( scale ) value /= pow( 10., (double) scale ); // This is necessary to prevent labels like "-0.0" on some systems scale2 = pow( 10., prec ); value = floor( ( value * scale2 ) + .5 ) / scale2; snprintf( form, FORMAT_LEN, "%%.%df", (int) prec ); snprintf( temp, TEMP_LEN, form, value ); strncpy( string, temp, (size_t) ( len - 1 ) ); string[len - 1] = '\0'; } //-------------------------------------------------------------------------- // void plform() // // Formats a PLFLT value in one of the following formats. // // If ll (logarithmic), then: // // - If lf (fixed), then used fixed point notation, i.e. .1, 1, 10, etc, // with unnecessary trailing .'s or 0's removed. // // - If !lf (default), then use exponential notation, i.e. 10^-1, etc. // // If !ll (linear), then: // // - If scale == 0, use fixed point format with "prec" places after the // decimal point. // // - If scale == 1, use scientific notation with one place before the // decimal point and "prec" places after. In this case, the value // must be divided by 10^scale. // // The axis argument is included to support PLplot's custom axis labeling. It // is passed on to the custom labeling function if it exists. Otherwise, it // is ignored. //-------------------------------------------------------------------------- static void plform( PLINT axis, PLFLT value, PLINT scale, PLINT prec, char *string, PLINT len, PLBOOL ll, PLBOOL lf, PLBOOL lo ) { // Check to see if a custom labeling function is defined. If not, // use default. if ( lo && plsc->label_func ) { ( *plsc->label_func )( axis, value, string, len, plsc->label_data ); } else { if ( lo ) { plwarn( "Custom axis labels requested without a labeling function \ - using default." ); } if ( ll ) { // Logarithmic if ( lf ) { // Fixed point, i.e. .1, 1, 10, etc plP_default_label_log_fixed( axis, value, string, len, NULL ); } else { // Exponential, i.e. 10^-1, 10^0, 10^1, etc plP_default_label_log( axis, value, string, len, NULL ); } } else { // Linear PLINT scale_prec[2] = { scale, prec }; plP_default_label( axis, value, string, len, (void *) scale_prec ); } } } //-------------------------------------------------------------------------- // plslabelfunc // // Formats a PLFLT value in one of the following formats. // // label_func - A pointer to a function which will provide a string to use // as the label for the given floating point value. // Pass this as NULL to clear the custom function and reset it to // the default PLplot labeling function. // // label_data - Extra data to pass to the label function. // // The label_func function arguments are, in order: // // axis: PL_X_AXIS, PL_Y_AXIS or PL_Z_AXIS to indicate which axis is being // labeled // value: The value at this position on the axis // string: The resulting label string should be stored here // data: A pointer to whatever extra information the custom plotting function // requires // //-------------------------------------------------------------------------- void c_plslabelfunc( PLLABEL_FUNC_callback label_func, PLPointer label_data ) { plsc->label_func = label_func; plsc->label_data = label_data; } static PLCHAR_VECTOR plgesc_string( void ) { static PLCHAR_VECTOR esc_strings = { "!\0#\0$\0%\0&\0*\0@\0^\0~\0" }; int d; // Follow plgesc logic here which is to set the default escape // if plsc->esc is in its initial state. if ( plsc->esc == '\0' ) plsc->esc = '#'; switch ( plsc->esc ) { case '!': d = 0; break; case '#': d = 1; break; case '$': d = 2; break; case '%': d = 3; break; case '&': d = 4; break; case '*': d = 5; break; case '@': d = 6; break; case '^': d = 7; break; case '~': d = 8; break; default: plwarn( "plgesc_string: Invalid escape character, assuming '#' instead" ); d = 1; break; } return &( esc_strings[d * 2] ); }