1 /*****************************************************************************
6 ** This is part of the CTSim program
7 ** Copyright (C) 1983-2000 Kevin Rosenberg
9 ** $Id: ezplot.cpp,v 1.15 2000/09/09 09:31:12 kevin Exp $
11 ** This program is free software; you can redistribute it and/or modify
12 ** it under the terms of the GNU General Public License (version 2) as
13 ** published by the Free Software Foundation.
15 ** This program is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ** GNU General Public License for more details.
20 ** You should have received a copy of the GNU General Public License
21 ** along with this program; if not, write to the Free Software
22 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 ******************************************************************************/
29 int snprintf (char *, size_t, const char*, ...);
33 static const double TICKRATIO = 0.4; // ratio of minor to major tick lengths
34 static const int MAXNUMFMT = 15; // maximum length of a numeric format
35 static const double DEF_CHARHEIGHT = (1./43.); //size of characters in NDC
36 static const double DEF_CHARWIDTH = (1./80.); // size of characters in NDC
37 static const int DEF_CURVE_CLR = C_RED;
40 EZPlotCurve::EZPlotCurve (const double* xData, const double* yData, int n, int color, int linestyle, int symbol, int symfreq, const string& legend)
41 : x(NULL), y(NULL), m_sLegend (legend)
46 int copyCount = n * sizeof(double);
47 memcpy (x, xData, copyCount);
48 memcpy (y, yData, copyCount);
52 m_iLineStyle = linestyle;
54 m_iSymbolFreq = symfreq;
57 EZPlotCurve::~EZPlotCurve ()
65 EZPlot::addCurve (const double *y, int n)
69 for (int i = 0; i < n; i++)
77 EZPlot::addCurve (const float *y, int n)
81 for (int i = 0; i < n; i++)
84 addCurve (yDouble, n);
89 EZPlot::addCurve (const float x[], const double y[], int num)
93 for (int i = 0; i < num; i++)
96 addCurve (dx, y, num);
100 EZPlot::addCurve (const double x[], const float y[], int num)
104 for (int i = 0; i < num; i++)
107 addCurve (x, dy, num);
112 EZPlot::addCurve (const double x[], const double y[], int num)
117 EZPlotCurve* pCurve = new EZPlotCurve (x, y, num, o_color, o_linestyle, o_symbol, o_symfreq, c_legend);
118 m_vecCurves.push_back (pCurve);
124 for (EZPlotCurveIterator i = m_vecCurves.begin(); i != m_vecCurves.end(); i++)
129 EZPlot::clearCurves ()
131 for (EZPlotCurveIterator i = m_vecCurves.begin(); i != m_vecCurves.end(); i++)
133 m_vecCurves.erase (m_vecCurves.begin(), m_vecCurves.end());
138 EZPlot::EZPlot (SGP& sgp)
145 EZPlot::initPlotSettings ()
147 charheight = DEF_CHARHEIGHT;
148 charwidth = DEF_CHARWIDTH;
171 o_color = DEF_CURVE_CLR;
174 o_linestyle = SGP::LS_SOLID;
181 o_legendbox = INSIDE;
194 clr_axis = C_BLACK; // set fixed colors
198 clr_number = C_GREEN;
204 * plot Plots all curves collected by addCurves ()
210 * This routine plots the curves that have stored by addCurves().
213 * drawAxes() & make_numfmt()
219 if (m_vecCurves.size() <= 0)
222 rSGP.setWindow (0., 0., 1., 1.);
224 if (s_textsize == TRUE) {
225 charheight = v_textsize;
226 charwidth = rSGP.getCharWidth();
228 charheight = rSGP.getCharHeight();
229 charwidth = rSGP.getCharWidth();
232 const EZPlotCurve& firstCurve = *m_vecCurves[0];
233 double xmin = firstCurve.x[0]; // extent of curves in world coord
235 double ymin = firstCurve.y[0];
238 for (EZPlotCurveConstIterator iterCurve = m_vecCurves.begin(); iterCurve != m_vecCurves.end(); iterCurve++) {
239 const EZPlotCurve& curve = **iterCurve;
241 for (int ip = 0; ip < curve.m_iPointCount; ip++) {
242 if (curve.x[ip] > xmax)
244 else if (curve.x[ip] < xmin)
246 if (curve.y[ip] > ymax)
248 else if (curve.y[ip] < ymin)
253 // extend graph limits for user defined axis cross positions
254 if (s_xcross == TRUE) {
257 else if (v_xcross > xmax)
261 if (s_ycross == TRUE) {
264 else if (v_ycross > ymax)
268 // find nice endpoints for axes
269 if (! axis_scale (xmin, xmax, o_xmajortick - 1, &xgw_min, &xgw_max, &x_nint) || ! axis_scale (ymin, ymax, o_ymajortick - 1, &ygw_min, &ygw_max, &y_nint))
272 // check if user set x-axis extents
273 if (s_xmin == TRUE) {
275 x_nint = o_xmajortick - 1;
277 if (s_xmax == TRUE) {
279 x_nint = o_xmajortick - 1;
282 // check if user set y-axis extents
283 if (s_ymin == TRUE) {
285 y_nint = o_ymajortick - 1;
287 if (s_ymax == TRUE) {
289 y_nint = o_ymajortick - 1;
292 // calculate increment between major axis in world coordinates
293 xw_tickinc = (xgw_max - xgw_min) / x_nint;
294 yw_tickinc = (ygw_max - ygw_min) / y_nint;
296 // we have now calcuated xgw_min, xyw_max, ygw_min, & ygw_max
298 // set the number of decimal point to users' setting or default
299 // Two formats for numbers: Fixed: -nnn.f and Exponent: -n.fffE+eee
300 if (s_lxfrac == TRUE)
305 if (s_lyfrac == TRUE)
310 make_numfmt (x_numfmt, &x_fldwid, &x_frac, xgw_min, xgw_max, x_nint);
311 make_numfmt (y_numfmt, &y_fldwid, &y_frac, ygw_min, ygw_max, y_nint);
313 xtl_wid = x_fldwid * charwidth; // calc size of tick labels
314 ytl_wid = y_fldwid * charwidth;
315 tl_height = charheight;
317 // calculate the extent of the plot frame
320 xp_max = xp_min + o_xlength;
321 yp_max = yp_min + o_ylength;
323 xp_min = clamp (xp_min, 0., 1.);
324 xp_max = clamp (xp_max, 0., 1.);
325 yp_min = clamp (yp_min, 0., 1.);
326 yp_max = clamp (yp_max, 0., 1.);
328 xa_min = xp_min; // extent of axes
333 // adjust frame for title
334 if (c_title.length() > 0)
335 ya_max -= 2 * charheight;
336 title_row = ya_max + 2 * charheight;
338 // calculate legend box boundaries
339 int max_leg = 0; // longest legend in characters
340 int num_leg = 0; // number of legend titles
341 for (EZPlotCurveConstIterator iterCurve = m_vecCurves.begin(); iterCurve != m_vecCurves.end(); iterCurve++) {
342 const EZPlotCurve& curve = **iterCurve;
343 int nLegend = curve.m_sLegend.length();
346 max_leg = max (max_leg, nLegend);
350 if (num_leg > 0 && o_legendbox != NOLEGEND) {
351 double leg_width = (max_leg + 2) * charwidth; // size of legend box
352 double leg_height = num_leg * 3 * charheight;
354 if (s_xlegend == TRUE)
358 if (o_legendbox == OUTSIDE)
359 xa_max -= (leg_width + 0.5 * charwidth);
361 xl_min = xl_max - leg_width;
363 if (s_ylegend == TRUE)
368 yl_min = yl_max - leg_height;
370 rSGP.setColor (clr_legend);
371 rSGP.drawRect (xl_min, yl_min, xl_max, yl_max);
373 int iLegend = 0; // current legend position
374 for (EZPlotCurveIterator iterCurve = m_vecCurves.begin(); iterCurve != m_vecCurves.end(); iterCurve++) {
375 const EZPlotCurve& curve = **iterCurve;
377 if (curve.m_sLegend.length() == 0)
380 double xmin = xl_min + 1.0 * charwidth;
381 double xmax = xl_max - 1.0 * charwidth;
382 double y = yl_max - (2.0 + iLegend * 3) * charheight;
384 rSGP.moveAbs (xmin, y + 0.5 * charheight);
385 rSGP.drawText (curve.m_sLegend);
386 rSGP.setColor (curve.m_iColor);
387 if (curve.m_iLineStyle != SGP::LS_NOLINE) {
388 rSGP.setLineStyle (curve.m_iLineStyle);
389 rSGP.moveAbs (xmin, y);
390 rSGP.lineAbs (xmax, y);
392 if (curve.m_iSymbol > 0) {
393 double xinc = (xmax - xmin) / (5 - 1);
394 rSGP.setLineStyle (SGP::LS_SOLID);
395 for (int j = 0; j < 5; j++) {
396 rSGP.moveAbs (xmin + j * xinc, y);
397 symbol(curve.m_iSymbol, 0.5 * charwidth, 0.5 * charheight);
400 ++iLegend; // move to next legend position
402 } // end legend printing
404 // calculate the extent of the axes
406 /*-------------------------*/
407 /* adjust frame for labels */
408 /*-------------------------*/
411 if (c_xlabel.length() > 0)
412 ya_min += 3.0 * charheight;
413 xlbl_row = xp_min; // put x-label on bottom of plot frame
416 if (c_ylabel.length() > 0)
417 xa_min += 3.0 * charwidth; // reverse rSGP.setTextSize because writing text sideways
418 ylbl_col = xp_min + 2 * charwidth;
420 /*------------------------------*/
421 /* adjust frame for tick labels */
422 /*------------------------------*/
424 // Calc offset of tick labels from axes
425 if (o_xaxis == NOAXIS || o_xtlabel == FALSE)
427 else if (o_xticks == BELOW)
428 xtl_ofs = -1.5 * charheight; // kr
429 else if (o_xticks == ABOVE)
430 xtl_ofs = 1.5 * charheight;
432 if (o_yaxis == NOAXIS || o_ytlabel == FALSE)
434 else if (o_yticks == LEFT)
435 ytl_ofs = -(1 + y_fldwid) * charwidth;
436 else if (o_yticks == RIGHT)
437 ytl_ofs = 1.5 * charwidth;
444 // see if need to shrink axis extents and/or tick extents
445 if (xtl_ofs != 0.0 && s_ycross == FALSE) {
446 if (o_xticks == BELOW) {
447 ya_min += 2.5 * charheight;
449 } else if (o_xticks == ABOVE) {
451 yt_min = ya_min + 2.5 * charheight;
453 } else // noaxis, no t-labels, or user set cross
456 if (ytl_ofs != 0.0 && s_xcross == FALSE) {
457 if (o_yticks == LEFT) {
458 xa_min += (1 + y_fldwid) * charwidth;
460 } else if (o_yticks == RIGHT) {
462 xt_min = xa_min + ytl_ofs + y_fldwid * charwidth;
467 // decrease size of graph, if necessary, to accommadate space
468 // between axis boundary and boundary of ticks
469 double x_added_ticks = -1; // number of tick spaces added to axis
470 double y_added_ticks = -1;
471 if (o_xaxis == NOAXIS || o_xtlabel == FALSE)
473 if (o_yaxis == NOAXIS || o_ytlabel == FALSE)
476 if (o_grid == TRUE) {
477 if (x_added_ticks < 0)
479 if (y_added_ticks < 0)
483 if (x_added_ticks < 0) {
484 if (o_yticks == LEFT || s_ycross)
490 if (y_added_ticks < 0) {
491 if (o_xticks == BELOW || s_xcross)
497 xn_tickinc = (xt_max - xt_min) / (x_nint + x_added_ticks);
498 yn_tickinc = (yt_max - yt_min) / (y_nint + y_added_ticks);
500 xt_min += 0.5 * x_added_ticks * xn_tickinc;
501 xt_max -= 0.5 * x_added_ticks * xn_tickinc;
502 yt_min += 0.5 * y_added_ticks * yn_tickinc;
503 yt_max -= 0.5 * y_added_ticks * yn_tickinc;
510 //------------------------------------------------------------------------
512 m_xWorldScale = (xgn_max - xgn_min) / (xgw_max - xgw_min);
513 m_yWorldScale = (ygn_max - ygn_min) / (ygw_max - ygw_min);
517 rSGP.setLineStyle (SGP::LS_SOLID);
520 // size of symbol in NDC
521 double symwidth = charwidth;
522 double symheight = charheight;
524 for (EZPlotCurveIterator iterCurve = m_vecCurves.begin(); iterCurve != m_vecCurves.end(); iterCurve++) {
525 const EZPlotCurve& curve = **iterCurve;
527 rSGP.setColor (curve.m_iColor);
529 if (curve.m_iLineStyle != SGP::LS_NOLINE) {
530 rSGP.setLineStyle (curve.m_iLineStyle);
531 double x = convertWorldToNDC_X (curve.x[0]);
532 double y = convertWorldToNDC_Y (curve.y[0]);
534 for (int i = 1; i < curve.m_iPointCount; i++) {
535 x = convertWorldToNDC_X (curve.x[i]);
536 y = convertWorldToNDC_Y (curve.y[i]);
540 if (curve.m_iSymbol > 0) {
541 rSGP.setLineStyle (SGP::LS_SOLID);
542 double x = convertWorldToNDC_X (curve.x[0]);
543 double y = convertWorldToNDC_Y (curve.y[0]);
545 symbol (curve.m_iSymbol, symwidth, symheight);
546 for (int i = 1; i < curve.m_iPointCount; i++)
547 if (i % curve.m_iSymbolFreq == 0 || i == curve.m_iPointCount - 1) {
548 x = convertWorldToNDC_X (curve.x[i]);
549 y = convertWorldToNDC_Y (curve.y[i]);
551 symbol (curve.m_iSymbol, symwidth, symheight);
560 * drawAxes INTERNAL routine to draw axis & label them
570 double xticklen = 0, yticklen = 0; // length of ticks in NDC
571 double minorinc; // increment between minor axes
572 double xaxispos, yaxispos; // crossing of axes
574 bool axis_near; // TRUE if axis too close to print t-label
579 rSGP.setTextSize (charheight);
580 rSGP.setTextColor (1, -1);
582 if (o_xticks == ABOVE)
583 xticklen = charheight;
584 else if (o_xticks == BELOW)
585 xticklen = -charheight;
587 if (o_yticks == RIGHT)
588 yticklen = charwidth;
589 else if (o_yticks == LEFT)
590 yticklen = -charwidth;
592 if (c_title.length() > 0) {
594 rSGP.setTextSize (charheight * 2.0);
595 rSGP.getTextExtent (c_title.c_str(), &wText, &hText);
596 rSGP.moveAbs (xa_min + (xa_max-xa_min)/2 - wText/2, title_row);
597 rSGP.setTextColor (clr_title, -1);
598 rSGP.drawText (c_title);
599 rSGP.setTextSize (charheight);
602 if (o_grid == TRUE || o_box == TRUE) {
603 rSGP.setColor (clr_axis);
604 rSGP.moveAbs (xa_min, ya_min);
605 rSGP.lineAbs (xa_max, ya_min);
606 rSGP.lineAbs (xa_max, ya_max);
607 rSGP.lineAbs (xa_min, ya_max);
608 rSGP.lineAbs (xa_min, ya_min);
611 // calculate position of axes
614 if (s_ycross == TRUE) { // convert users' world-coord
615 xaxispos = convertWorldToNDC_Y (v_ycross);// axis to its position in NDC
616 x = convertWorldToNDC_X (xgw_min);
621 if (s_xcross == TRUE) { // convert users' world-coord
622 yaxispos = convertWorldToNDC_X (v_xcross);// axis to its NDC position
623 y = convertWorldToNDC_Y (ygw_min);
631 if (o_xaxis == LINEAR) {
634 rSGP.setColor (clr_axis);
635 if (o_tag && !o_grid && !o_box && s_xcross) {
636 rSGP.moveAbs (xa_min, xaxispos - charheight);
637 rSGP.lineAbs (xa_min, xaxispos + charheight);
639 rSGP.moveAbs (xa_min, xaxispos);
640 rSGP.lineAbs (xa_max, xaxispos);
641 if (o_tag && !o_grid && !o_box) {
642 rSGP.moveAbs (xa_max, xaxispos - charheight);
643 rSGP.lineAbs (xa_max, xaxispos + charheight);
646 if (o_grid == TRUE) {
647 rSGP.setColor (clr_grid);
648 for (i = 0; i <= x_nint; i++) {
649 rSGP.moveAbs (xt_min + xn_tickinc * i, ya_max);
650 rSGP.lineAbs (xt_min + xn_tickinc * i, ya_min);
653 rSGP.moveAbs (xa_min + (xa_max-xa_min)/2 - c_xlabel.length()*charwidth, xlbl_row);
654 rSGP.setTextSize (charheight * 2.0);
655 rSGP.setTextColor (clr_label, -1);
656 rSGP.drawText (c_xlabel);
657 rSGP.setTextSize (charheight);
658 minorinc = xn_tickinc / (o_xminortick + 1);
660 for (i = 0; i <= x_nint; i++) {
661 x = xt_min + xn_tickinc * i;
662 rSGP.setColor (clr_axis);
663 rSGP.moveAbs (x, xaxispos);
664 rSGP.lineAbs (x, xaxispos + xticklen);
666 for (j = 1; j <= o_xminortick; j++) {
667 x2 = x + minorinc * j;
668 rSGP.moveAbs (x2, xaxispos);
669 rSGP.lineAbs (x2, xaxispos + TICKRATIO * xticklen);
672 if (xaxispos + xtl_ofs > ya_min && o_yaxis != NOAXIS) {
673 double xw = xgw_min + i * xw_tickinc;
674 double x = convertWorldToNDC_X (xw);
675 double d = x - yaxispos;
676 if (o_yticks == RIGHT && d >= 0 && d < 0.9 * xn_tickinc)
678 if (o_yticks == LEFT && d <= 0 && d > -0.9 * xn_tickinc)
682 if (o_xtlabel == TRUE && axis_near == FALSE) {
683 snprintf (str, sizeof(str), x_numfmt, xgw_min + xw_tickinc * i);
684 numstr = str_skip_head (str, " ");
685 rSGP.moveAbs (x-strlen(numstr)*charwidth/2, xaxispos + xtl_ofs);
686 rSGP.setTextColor (clr_number, -1);
687 rSGP.drawText (numstr);
697 if (o_yaxis == LINEAR) {
699 rSGP.setColor (clr_axis);
700 if (o_tag && !o_grid && !o_box && s_ycross) {
701 rSGP.moveAbs (yaxispos - charwidth, ya_min);
702 rSGP.lineAbs (yaxispos + charwidth, ya_min);
704 rSGP.moveAbs (yaxispos, ya_min);
705 rSGP.lineAbs (yaxispos, ya_max);
706 if (o_tag && !o_grid && !o_box) {
707 rSGP.moveAbs (yaxispos - charwidth, ya_max);
708 rSGP.lineAbs (yaxispos + charwidth, ya_max);
711 if (o_grid == TRUE) {
712 rSGP.setColor (clr_grid);
713 for (i = 0; i <= y_nint; i++) {
714 y = yt_min + yn_tickinc * i;
715 rSGP.moveAbs (xa_max, y);
716 rSGP.lineAbs (xa_min, y);
719 rSGP.moveAbs (ylbl_col, ya_min + (ya_max-ya_min)/2 - c_ylabel.length()*charheight);
720 rSGP.setTextAngle (HALFPI);
721 rSGP.setTextSize (2 * charheight);
722 rSGP.setTextColor (clr_label, -1);
723 rSGP.drawText (c_ylabel);
724 rSGP.setTextAngle (0.0);
725 rSGP.setTextSize (charheight);
726 minorinc = yn_tickinc / (o_yminortick + 1);
728 for (i = 0; i <= y_nint; i++) {
729 y = yt_min + yn_tickinc * i;
730 rSGP.setColor (clr_axis);
731 rSGP.moveAbs (yaxispos, y);
732 rSGP.lineAbs (yaxispos + yticklen, y);
734 for (j = 1; j <= o_yminortick; j++) {
735 y2 = y + minorinc * j;
736 rSGP.moveAbs (yaxispos, y2);
737 rSGP.lineAbs (yaxispos + TICKRATIO * yticklen, y2);
740 if (yaxispos + ytl_ofs > xa_min && o_xaxis != NOAXIS) {
741 double yw = ygw_min + i * yw_tickinc;
742 double y = convertWorldToNDC_Y (yw);
743 double d = y - xaxispos;
744 if (o_xticks == ABOVE && d >= 0 && d < 0.9 * yn_tickinc)
746 if (o_xticks == BELOW && d <= 0 && d > -0.9 * yn_tickinc)
749 if (o_ytlabel == TRUE && axis_near == FALSE) {
750 snprintf (str, sizeof(str), y_numfmt, ygw_min + yw_tickinc * i);
751 rSGP.moveAbs (yaxispos + ytl_ofs, y + 0.5 * charheight);
752 rSGP.setTextColor (clr_number, -1);
761 EZPlot::symbol (int sym, double symwidth, double symheight)
766 if (sym == SB_CROSS) {
767 rSGP.moveRel (-0.5 * symwidth, -0.5 * symheight);
768 rSGP.lineRel (symwidth, symheight);
769 rSGP.moveRel (-symwidth, 0.0);
770 rSGP.lineRel (symwidth, -symheight);
771 rSGP.moveRel (-0.5 * symwidth, 0.5 * symheight);
772 } else if (sym == SB_PLUS) {
773 rSGP.moveRel (-0.5 * symwidth, 0.0);
774 rSGP.lineRel (symwidth, 0.0);
775 rSGP.moveRel (-0.5 * symwidth, -0.5 * symheight);
776 rSGP.lineRel (0.0, symheight);
777 rSGP.moveRel (0.0, -0.5 * symheight);
778 } else if (sym == SB_BOX) {
779 rSGP.moveRel (-0.5 * symwidth, -0.5 * symheight);
780 rSGP.lineRel (symwidth, 0.0);
781 rSGP.lineRel (0.0, symheight);
782 rSGP.lineRel (-symwidth, 0.0);
783 rSGP.lineRel (0.0, -symheight);
784 rSGP.moveRel (0.5 * symwidth, 0.5 * symheight);
785 } else if (sym == SB_CIRCLE) {
786 rSGP.drawCircle (symwidth);
787 } else if (sym == SB_ERRORBAR) {
788 rSGP.moveRel (-0.5 * symwidth, 0.5 * symheight);
789 rSGP.lineRel (symwidth, 0.0);
790 rSGP.moveRel (-0.5 * symwidth, 0.0);
791 rSGP.lineRel (0.0, -symheight);
792 rSGP.moveRel (-0.5 * symwidth, 0.0);
793 rSGP.lineRel (symwidth, 0.0);
794 rSGP.moveRel (-0.5 * symwidth, 0.5 * symheight);
801 * axis_scale calculates graph axis scaling
804 * retval = axis_scale (min, max, nint, minp, maxp, nintp,
805 * rec_total, rec_frac)
808 * double min Smallest value to plot
809 * double max Largest value to plot
810 * int nint Number of intervals desired
813 * int retval FALSE if illegal parameters, else TRUE
814 * double *minp Minimum graph value
815 * double *maxp Maximum graph value
816 * int *nintp Number of intervals for graph
817 * int *rec_total Recommended field width for printing out the number
818 * int *rec_frac Recommended number of digits for print fraction
822 EZPlot::axis_scale (double min, double max, int nint, double *minp, double *maxp, int *nintp)
824 if (min >= max || nint < 1) {
825 sys_error (ERR_WARNING, "Invalid params: min=%lf, max=%lf, num intervals=%d [axis_scale]", min, max, nint);
830 double a = fabs(min);
831 if (fabs(min) < fabs(max))
833 double scale = pow (10.0, floor(log10(a)));
835 double mina = min / scale;
836 double maxa = max / scale;
837 double d = (maxa - mina) / nint;
839 double e = floor (log10(d));
840 double f = d / pow (10.0, e);
844 else if (f < sqrt (10.0))
846 else if (f < sqrt (50.0))
848 double wdt = v * pow (10.0, e);
849 double g = floor (mina / wdt);
850 if (fabs(g + 1 - mina / wdt) < j)
856 double h = floor (maxa / wdt) + 1.0;
857 if (fabs(maxa / wdt + 1 - h) < j)
863 *nintp = static_cast<int>(h - g);
864 if (fabs(*maxp) >= 10.0 || fabs(*minp) >= 10.0) {
865 scale = scale * 10.0;
877 * make_numfmt Make a numeric format string
880 * make_numfmt (fmtstr, fldwid, nfrac, min, max, nint)
881 * char *fmtstr Returned format string for printf()
882 * int *fldwid Returned field width
883 * int *nfrac If < 0, then calculate best number of
884 * fraction places & return that value
885 * If >= 0, then use that number of places
886 * double min Minimum value
887 * double max Maximum value
888 * int nint Number of intervals between min & max
891 * This routine is written as an INTERNAL routine for EZPLOT
905 EZPlot::make_numfmt (char *fmtstr, int *fldwid, int *nfrac, double minval, double maxval, int nint)
907 int wid, frac, expon;
909 double delta = (maxval - minval) / nint;
910 double absmin = fabs(minval);
911 double absmax = fabs(maxval);
912 double logt = log10( max(absmin, absmax) );
914 if (fabs(logt) >= 6) { // use exponential format
920 if (*nfrac < 0) { // calculate frac
921 delta /= pow (10., floor(logt)); // scale delta
922 frac = static_cast<int>(fabs(trunc(log10(delta)))) + 1;
924 frac = 1; // to be safe, add decimal pt
925 } else // use users' frac
928 wid = 2 + frac + expon;
929 if (minval < 0. || maxval < 0.)
931 sprintf (fmtstr, "%s%d%s%d%s", "%", wid, ".", frac, "g");
932 } else { // use fixed format
933 wid = static_cast<int>(trunc(logt)) + 1;
936 if (minval < 0. || maxval < 0.)
939 if (*nfrac < 0) { // calculate frac
940 if (delta >= 0.999999)
941 frac = 1; // add a decimal pt to be safe
943 frac = static_cast<int>(fabs(trunc(log10(delta)))) + 1;
944 } else // use users' frac
948 sprintf (fmtstr, "%s%d%s%d%s", "%", wid, ".", frac, "f");