/* $Id: plbox.c,v 1.3 2007/05/08 09:09:37 rice Exp $ Routines for drawing axes & box around the current viewport. Copyright (C) 2004 Joao Cardoso Copyright (C) 2004 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 General Library 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" 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(const char *opt, const char *label, PLFLT wx1, PLFLT wy1, PLFLT wx2, PLFLT wy2, PLFLT vmin, PLFLT vmax, PLFLT tick, PLINT nsub, PLINT nolast, PLINT *digits); static void plzbx(const char *opt, const char *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, const char *text); static void plztx(const char *opt, PLFLT dx, PLFLT dy, PLFLT wx, PLFLT wy1, PLFLT wy2, PLFLT disp, PLFLT pos, PLFLT just, const char *text); static void plform(PLFLT value, PLINT scale, PLINT prec, char *result, PLINT ll, PLINT lf); static void grid_box(const char *xopt, PLFLT xtick1, PLINT nxsub1, const char *yopt, PLFLT ytick1, PLINT nysub1); static void label_box(const char *xopt, PLFLT xtick1, const char *yopt, PLFLT ytick1); /*--------------------------------------------------------------------------*\ * 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(const char *xopt, PLFLT xtick, PLINT nxsub, const char *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. * * xx0 and yy0 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) edge of frame * c: Draw top (X) or right (Y) edge of frame * 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 * t: Draw major tick marks * s: Draw minor tick marks * v: (for Y only) Label vertically * * 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 xx0, PLFLT yy0, const char *xopt, PLFLT xtick, PLINT nxsub, const char *yopt, PLFLT ytick, PLINT nysub) { PLINT lax, lbx, lcx, lgx, lix, llx, lsx, ltx; PLINT lay, lby, lcy, lgy, liy, lly, lsy, lty; 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; 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; /* Convert world coordinates to physical */ xp0 = plP_wcpcx(xx0); yp0 = plP_wcpcy(yy0); /* Set plot options from input */ lax = plP_stsearch(xopt, 'a'); lbx = plP_stsearch(xopt, 'b'); lcx = plP_stsearch(xopt, 'c'); 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'); lay = plP_stsearch(yopt, 'a'); lby = plP_stsearch(yopt, 'b'); lcy = plP_stsearch(yopt, 'c'); 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'); /* 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; plgvpw(&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; lax = lax && vpwymi < yy0 && yy0 < vpwyma ; lay = lay && vpwxmi < xx0 && xx0 < vpwxma ; /* Calculate tick spacing */ if (ltx || lgx) pldtik(vpwxmi, vpwxma, &xtick1, &nxsub1); if (lty || lgy) pldtik(vpwymi, vpwyma, &ytick1, &nysub1); /* 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; } /* Draw the bottom edge of the box */ if (lbx) { plP_movphy(vppxmi, vppymi); if (ltx) { 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; } } plP_draphy(vppxma, vppymi); } /* Draw right-hand edge of box */ if (lcy) { plP_movphy(vppxma, vppymi); if (lty) { 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; } } plP_draphy(vppxma, vppyma); } /* Draw the top edge of the box */ if (lcx) { plP_movphy(vppxma, vppyma); if (ltx) { 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; } } plP_draphy(vppxmi, vppyma); } /* Draw left-hand edge of box */ if (lby) { plP_movphy(vppxmi, vppyma); if (lty) { 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; } } plP_draphy(vppxmi, vppymi); } /* Draw the horizontal axis */ if (lax) { plP_movphy(vppxmi, yp0); if (ltx) { 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), 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), yp0, xminor, xminor); } } } if (!BETW(tn, vpwxmi, vpwxma)) break; plxtik(plP_wcpcx(tn), yp0, xmajor, xmajor); tp = tn; } } plP_draphy(vppxma, yp0); } /* Draw the vertical axis */ if (lay) { plP_movphy(xp0, vppymi); if (lty) { 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(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(xp0, plP_wcpcy(temp), yminor, yminor); } } } if (!BETW(tn, vpwymi, vpwyma)) break; plytik(xp0, plP_wcpcy(tn), ymajor, ymajor); tp = tn; } } plP_draphy(xp0, vppyma); } /* 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(const char *xopt, const char *xlabel, PLFLT xtick, PLINT nsubx, const char *yopt, const char *ylabel, PLFLT ytick, PLINT nsuby, const char *zopt, const char *zlabel, PLFLT ztick, PLINT nsubz) { 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, *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, tx, ty, ux, uy, xmin, xmax, xtick, nsubx, 0, &xdigits); dx = ux - tx; dy = uy - ty; plzbx(zopt, zlabel, 1, dx, dy, ux, uy, plP_w3wcy(xmax, ymin, zmax), zmin, zmax, ztick, nsubz, &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, tx, ty, ux, uy, ymax, ymin, ytick, nsuby, 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, nsubz, &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, tx, ty, ux, uy, ymax, ymin, ytick, nsuby, 0, &ydigits); dx = ux - tx; dy = uy - ty; plzbx(zopt, zlabel, 1, dx, dy, ux, uy, plP_w3wcy(xmin, ymin, zmax), zmin, zmax, ztick, nsubz, &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, tx, ty, ux, uy, xmax, xmin, xtick, nsubx, 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, nsubz, &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, tx, ty, ux, uy, xmax, xmin, xtick, nsubx, 0, &xdigits); dx = ux - tx; dy = uy - ty; plzbx(zopt, zlabel, 1, dx, dy, ux, uy, plP_w3wcy(xmin, ymax, zmax), zmin, zmax, ztick, nsubz, &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, tx, ty, ux, uy, ymin, ymax, ytick, nsuby, 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, nsubz, &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, tx, ty, ux, uy, ymin, ymax, ytick, nsuby, 0, &ydigits); dx = ux - tx; dy = uy - ty; plzbx(zopt, zlabel, 1, dx, dy, ux, uy, plP_w3wcy(xmax, ymax, zmax), zmin, zmax, ztick, nsubz, &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, tx, ty, ux, uy, xmin, xmax, xtick, nsubx, 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, nsubz, &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 * 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 * t: Draw major tick marks * s: Draw minor tick marks * u: Write label on line \*--------------------------------------------------------------------------*/ static void plxybx(const char *opt, const char *label, PLFLT wx1, PLFLT wy1, PLFLT wx2, PLFLT wy2, PLFLT vmin_in, PLFLT vmax_in, PLFLT tick, PLINT nsub, PLINT nolast, PLINT *digits) { static char string[40]; PLINT lb, lf, li, ll, ln, ls, lt, lu; 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; (void) nolast; /* pmr: make it used */ 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'); lf = plP_stsearch(opt, 'f'); li = plP_stsearch(opt, 'i'); ll = plP_stsearch(opt, 'l'); ln = plP_stsearch(opt, 'n'); 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); 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); if (lt) { 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; } } plP_drawor(wx2, wy2); /* Label the line */ if (ln && lt) { pldprec(vmin, vmax, tick1, lf, &mode, &prec, *digits, &scale); pos = 1.0; height = 3.2; tcrit = tspace*tick1; tp = tick1 * (1. + floor(vmin / tick1)); for (tn = tp; BETW(tn, vmin, vmax); tn += tick1) { if(BETW(tn, vmin+tcrit, vmax-tcrit)) { plform(tn, scale, prec, string, ll, lf); 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 && mode) { sprintf(string, "(x10#u%d#d)", (int) scale); 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, const char *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 * 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 * s: Draw minor tick marks * t: Draw major tick marks * u: Writes left-hand label * v: Writes right-hand label \*--------------------------------------------------------------------------*/ static void plzbx(const char *opt, const char *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[40]; PLINT lb, lc, lf, li, ll, lm, ln, ls, lt, lu, lv; 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; 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'); 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'); 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); 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); if (lt) { 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; } } plP_drawor(wx, wy2); /* Label the line */ if ((ln || lm) && lt) { pldprec(vmin, vmax, tick1, lf, &mode, &prec, *digits, &scale); *digits = 0; tp = tick1 * floor(vmin / tick1); for (tn = tp + tick1; BETW(tn, vmin, vmax); tn += tick1) { plform(tn, scale, prec, string, ll, lf); 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 = strlen(string); *digits = MAX(*digits, lstring); } if (!ll && mode) { sprintf(string, "(x10#u%d#d)", (int) scale); 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(const char *opt, PLFLT dx, PLFLT dy, PLFLT wx, PLFLT wy1, PLFLT wy2, PLFLT disp, PLFLT pos, PLFLT just, const char *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(const char *xopt, PLFLT xtick1, PLINT nxsub1, const char *yopt, PLFLT ytick1, PLINT nysub1) { PLINT lgx, lhx, llx; PLINT lgy, lhy, lly; PLFLT vpwxmi, vpwxma, vpwymi, vpwyma; PLFLT vpwxmin, vpwxmax, vpwymin, vpwymax; PLFLT tn, temp, tcrit, tspace = 0.1; PLINT i; /* Set plot options from input */ lgx = plP_stsearch(xopt, 'g'); lhx = plP_stsearch(xopt, 'h'); llx = plP_stsearch(xopt, 'l'); lgy = plP_stsearch(yopt, 'g'); lhy = plP_stsearch(yopt, 'h'); lly = plP_stsearch(yopt, 'l'); plgvpw(&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) { for (tn = xtick1 * floor(vpwxmi/xtick1); 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) { tn = ytick1 * floor(vpwymi / ytick1 + tspace); for (tn = ytick1 * floor(vpwymi/ytick1); 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(const char *xopt, PLFLT xtick1, const char *yopt, PLFLT ytick1) { static char string[40]; PLINT lfx, lix, llx, lmx, lnx, ltx; PLINT lfy, liy, lly, lmy, lny, lty, lvy; PLFLT vpwxmi, vpwxma, vpwymi, vpwyma; PLFLT vpwxmin, vpwxmax, vpwymin, vpwymax; PLFLT pos, tn, tp, offset, height; /* Set plot options from input */ 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'); 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'); plgvpw(&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; /* Write horizontal label(s) */ if ((lmx || lnx) && ltx) { PLINT xmode, xprec, xdigmax, xdigits, xscale; plgxax(&xdigmax, &xdigits); pldprec(vpwxmi, vpwxma, xtick1, lfx, &xmode, &xprec, xdigmax, &xscale); tp = xtick1 * (1. + floor(vpwxmi / xtick1)); for (tn = tp; BETW(tn, vpwxmi, vpwxma); tn += xtick1) { plform(tn, xscale, xprec, string, llx, lfx); height = lix ? 1.75 : 1.5; pos = (vpwxmax > vpwxmin)? (tn - vpwxmi) / (vpwxma - vpwxmi): (vpwxma - tn) / (vpwxma - vpwxmi); if (lnx) plmtex("b", height, pos, 0.5, string); if (lmx) plmtex("t", height, pos, 0.5, string); } xdigits = 2; plsxax(xdigmax, xdigits); /* Write separate exponential label if mode = 1. */ if (!llx && xmode) { pos = 1.0; height = 3.2; sprintf(string, "(x10#u%d#d)", (int) xscale); if (lnx) plmtex("b", height, pos, 0.5, string); if (lmx) plmtex("t", height, pos, 0.5, string); } } /* Write vertical label(s) */ if ((lmy || lny) && lty) { PLINT ymode, yprec, ydigmax, ydigits, yscale; plgyax(&ydigmax, &ydigits); pldprec(vpwymi, vpwyma, ytick1, lfy, &ymode, &yprec, ydigmax, &yscale); ydigits = 0; tp = ytick1 * (1. + floor(vpwymi / ytick1)); for (tn = tp; BETW(tn, vpwymi, vpwyma); tn += ytick1) { plform(tn, yscale, yprec, string, lly, lfy); pos = (vpwymax > vpwymin)? (tn - vpwymi) / (vpwyma - vpwymi): (vpwyma - tn) / (vpwyma - vpwymi); if (lny) { if (lvy) { height = liy ? 1.0 : 0.5; plmtex("lv", height, pos, 1.0, string); } else { height = liy ? 1.75 : 1.5; plmtex("l", height, pos, 0.5, string); } } if (lmy) { if (lvy) { height = liy ? 1.0 : 0.5; plmtex("rv", height, pos, 0.0, string); } else { height = liy ? 1.75 : 1.5; 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 && ymode) { sprintf(string, "(x10#u%d#d)", (int) yscale); offset = 0.02; height = 2.0; if (lny) { pos = 0.0 - offset; plmtex("t", height, pos, 1.0, string); } if (lmy) { pos = 1.0 + offset; plmtex("t", height, pos, 0.0, string); } } } } /*--------------------------------------------------------------------------*\ * 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. \*--------------------------------------------------------------------------*/ static void plform(PLFLT value, PLINT scale, PLINT prec, char *string, PLINT ll, PLINT lf) { if (ll) { /* Logarithmic */ if (lf) { /* Fixed point, i.e. .1, 1, 10, etc */ int exponent = ROUND(value); value = pow(10.0, exponent); if (exponent < 0) { char form[10]; sprintf(form, "%%.%df", ABS(exponent)); sprintf(string, form, value); } else { sprintf(string, "%d", (int) value); } } else { /* Exponential, i.e. 10^-1, 10^0, 10^1, etc */ sprintf(string, "10#u%d", (int) ROUND(value)); } } else { /* Linear */ PLINT setpre, precis; char form[10], temp[30]; double scale2; 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; sprintf(form, "%%.%df", (int) prec); sprintf(temp, form, value); strcpy(string, temp); } }