r246: More modifications for MSVC
[ctsim.git] / libctgraphics / ezplot.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **    EZPLOT                                    
5 **
6 **  This is part of the CTSim program
7 **  Copyright (C) 1983-2000 Kevin Rosenberg
8 **
9 **  $Id: ezplot.cpp,v 1.16 2000/12/06 01:46:43 kevin Exp $
10 **
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.
14 **
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.
19 **
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 ******************************************************************************/
24
25 #include "ezplot.h"
26 #include <algorithm>
27
28 #ifdef __GNUWIN32__
29 int snprintf (char *, size_t, const char*, ...);
30 #endif
31
32 // Defaults
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;
38
39
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)
42 {
43   x = new double [n];
44   y = new double [n];
45
46   int copyCount = n * sizeof(double);
47   memcpy (x, xData, copyCount);
48   memcpy (y, yData, copyCount);
49
50   m_iPointCount = n;
51   m_iColor = color;
52   m_iLineStyle = linestyle;
53   m_iSymbol = symbol;
54   m_iSymbolFreq = symfreq;
55 }
56
57 EZPlotCurve::~EZPlotCurve ()
58 {
59   delete x;
60   delete y;
61 }
62
63
64 void 
65 EZPlot::addCurve (const double *y, int n)
66 {
67   double* x = new double [n];
68
69   for (int i = 0; i < n; i++)
70     x[i] = i;
71
72   addCurve (x, y, n);
73   delete x;
74 }
75
76
77 void 
78 EZPlot::addCurve (const float *y, int n)
79 {
80   double* yDouble = new double [n];
81
82   for (int i = 0; i < n; i++)
83     yDouble[i] = y[i];
84
85   addCurve (yDouble, n);
86   delete yDouble;
87 }
88
89
90 void
91 EZPlot::addCurve (const float x[], const double y[], int num)
92 {
93   double* dx = new double [num];
94
95   for (int i = 0; i < num; i++)
96     dx[i] = x[i];
97
98   addCurve (dx, y, num);
99   delete dx;
100 }
101
102 void
103 EZPlot::addCurve (const double x[], const float y[], int num)
104 {
105   double* dy = new double [num];
106
107   for (int i = 0; i < num; i++)
108     dy[i] = y[i];
109
110   addCurve (x, dy, num);
111
112   delete dy;
113 }
114
115
116 void
117 EZPlot::addCurve (const double x[], const double y[], int num)
118 {
119   if (num < 1)
120     return;
121
122   EZPlotCurve* pCurve = new EZPlotCurve (x, y, num, o_color, o_linestyle, o_symbol, o_symfreq, c_legend);
123   m_vecCurves.push_back (pCurve);
124 }
125
126
127 EZPlot::~EZPlot ()
128 {
129   for (EZPlotCurveIterator i = m_vecCurves.begin(); i != m_vecCurves.end(); i++)
130     delete *i;
131 }
132
133 void
134 EZPlot::clearCurves ()
135 {
136   for (EZPlotCurveIterator i = m_vecCurves.begin(); i != m_vecCurves.end(); i++)    
137     delete *i;
138   m_vecCurves.erase (m_vecCurves.begin(), m_vecCurves.end());
139   initPlotSettings();
140 }
141
142
143 EZPlot::EZPlot (SGP& sgp)
144   : rSGP (sgp)
145 {
146     initPlotSettings();
147 }
148
149 void
150 EZPlot::initPlotSettings ()
151 {
152   charheight = DEF_CHARHEIGHT;
153   charwidth = DEF_CHARWIDTH;
154
155   c_xlabel = "";
156   c_ylabel =  "";
157   c_title = "";
158   c_legend = "";
159   
160   o_xporigin = 0.0;
161   o_yporigin = 0.0;
162   o_xlength  = 1.0;
163   o_ylength  = 1.0;
164   
165   o_xaxis = LINEAR;
166   o_yaxis = LINEAR;
167   
168   o_grid = FALSE;
169   o_box = FALSE;
170   
171   o_xmajortick = 10;
172   o_ymajortick =  8;
173   o_xminortick =  4;
174   o_yminortick =  4;
175   
176   o_color = DEF_CURVE_CLR;
177   o_symfreq = 1;
178   o_symbol = -1;
179   o_linestyle = SGP::LS_SOLID;
180   
181   o_xtlabel = TRUE;
182   o_ytlabel = TRUE;
183   o_xticks = BELOW;
184   o_yticks = LEFT;
185   
186   o_legendbox = INSIDE;
187   o_tag = FALSE;
188   
189   s_xtitle   = FALSE;
190   s_ytitle   = FALSE;
191   s_xcross   = FALSE;
192   s_ycross   = FALSE;
193   s_lxfrac   = FALSE;
194   s_lyfrac   = FALSE;
195   s_xlegend  = FALSE;
196   s_ylegend  = FALSE;
197   s_textsize = FALSE;
198   
199   clr_axis   = C_BLACK;         // set fixed colors 
200   clr_title  = C_CYAN;
201   clr_label  = C_CYAN;
202   clr_legend = C_RED;
203   clr_number = C_GREEN;
204   clr_grid   = C_LTGRAY;
205 }
206
207
208 /* NAME
209  *   plot               Plots all curves collected by addCurves ()
210  *
211  * SYNOPSIS
212  *   plot()
213  *
214  * DESCRIPTION
215  *   This routine plots the curves that have stored by addCurves().
216  *
217  * CALLS
218  *   drawAxes() & make_numfmt()
219  */
220
221 void
222 EZPlot::plot ()
223 {
224   if (m_vecCurves.size() <= 0)
225     return;
226
227   rSGP.setWindow (0., 0., 1., 1.);
228
229   if (s_textsize == TRUE) {
230     charheight = v_textsize;
231     charwidth = rSGP.getCharWidth();
232   } else {
233     charheight = rSGP.getCharHeight();
234     charwidth = rSGP.getCharWidth();
235   }
236   
237   const EZPlotCurve& firstCurve = *m_vecCurves[0];
238   double xmin = firstCurve.x[0];   // extent of curves in world coord
239   double xmax = xmin;       
240   double ymin = firstCurve.y[0];
241   double ymax = ymin;
242   
243   for (EZPlotCurveConstIterator iterCurve = m_vecCurves.begin(); iterCurve != m_vecCurves.end(); iterCurve++) {
244     const EZPlotCurve& curve = **iterCurve;
245     
246     for (int ip = 0; ip < curve.m_iPointCount; ip++) {
247       if (curve.x[ip] > xmax)
248         xmax = curve.x[ip];
249       else if (curve.x[ip] < xmin)
250         xmin = curve.x[ip];
251       if (curve.y[ip] > ymax)
252         ymax = curve.y[ip];
253       else if (curve.y[ip] < ymin)
254         ymin = curve.y[ip];
255     }
256   }
257   
258   // extend graph limits for user defined axis cross positions 
259   if (s_xcross == TRUE) {
260     if (v_xcross < xmin)
261       xmin = v_xcross;
262     else if (v_xcross > xmax)
263       xmax = v_xcross;
264   }
265     
266   if (s_ycross == TRUE) {
267     if (v_ycross < ymin)
268       ymin = v_ycross;
269     else if (v_ycross > ymax)
270       ymax = v_ycross;
271   }
272     
273   // find nice endpoints for axes 
274   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))
275     return;
276     
277   // check if user set x-axis extents 
278   if (s_xmin == TRUE) {
279     xgw_min = v_xmin;
280     x_nint = o_xmajortick - 1;
281   }
282   if (s_xmax == TRUE) {
283     xgw_max = v_xmax;
284     x_nint = o_xmajortick - 1;
285   }
286   
287   // check if user set y-axis extents 
288   if (s_ymin == TRUE) {
289     ygw_min = v_ymin;
290     y_nint = o_ymajortick - 1;
291   }
292   if (s_ymax == TRUE) {
293     ygw_max = v_ymax;
294     y_nint = o_ymajortick - 1;
295   }
296     
297   // calculate increment between major axis in world coordinates 
298   xw_tickinc = (xgw_max - xgw_min) / x_nint;
299   yw_tickinc = (ygw_max - ygw_min) / y_nint;
300     
301   // we have now calcuated xgw_min, xyw_max, ygw_min, & ygw_max 
302   
303   // set the number of decimal point to users' setting or default 
304   // Two formats for numbers: Fixed:    -nnn.f and  Exponent: -n.fffE+eee
305   if (s_lxfrac == TRUE)
306     x_frac = v_lxfrac;
307   else
308     x_frac = -1;
309     
310   if (s_lyfrac == TRUE)
311     y_frac = v_lyfrac;
312   else
313     y_frac = -1;
314     
315   make_numfmt (x_numfmt, &x_fldwid, &x_frac, xgw_min, xgw_max, x_nint);
316   make_numfmt (y_numfmt, &y_fldwid, &y_frac, ygw_min, ygw_max, y_nint);
317     
318   xtl_wid = x_fldwid * charwidth;               // calc size of tick labels 
319   ytl_wid = y_fldwid * charwidth;
320   tl_height = charheight;
321
322   // calculate the extent of the plot frame 
323   xp_min = o_xporigin;
324   yp_min = o_yporigin;
325   xp_max = xp_min + o_xlength;
326   yp_max = yp_min + o_ylength;
327
328   xp_min = clamp (xp_min, 0., 1.);
329   xp_max = clamp (xp_max, 0., 1.);
330   yp_min = clamp (yp_min, 0., 1.);
331   yp_max = clamp (yp_max, 0., 1.);
332   
333   xa_min = xp_min;              // extent of axes 
334   xa_max = xp_max;
335   ya_min = yp_min;
336   ya_max = yp_max;
337
338   // adjust frame for title 
339   if (c_title.length() > 0)
340     ya_max -= 2 * charheight;
341   title_row = ya_max + 2 * charheight;
342
343   // calculate legend box boundaries 
344   int max_leg = 0;                      // longest legend in characters 
345   int num_leg = 0;                      // number of legend titles 
346   for (EZPlotCurveConstIterator iterCurve2 = m_vecCurves.begin(); iterCurve2 != m_vecCurves.end(); iterCurve2++) {
347     const EZPlotCurve& curve = **iterCurve2;
348     int nLegend = curve.m_sLegend.length();
349     if (nLegend > 0) {
350       ++num_leg;
351       if (nLegend > max_leg)\r
352                 nLegend = max_leg;
353     }
354   }
355
356   if (num_leg > 0 && o_legendbox != NOLEGEND) {
357     double leg_width  = (max_leg + 2) * charwidth;      // size of legend box 
358     double leg_height = num_leg * 3 * charheight;
359     
360     if (s_xlegend == TRUE)
361       xl_max = v_xlegend;
362     else {
363       xl_max = xa_max;
364       if (o_legendbox == OUTSIDE)
365         xa_max -= (leg_width + 0.5 * charwidth);
366     }
367     xl_min = xl_max - leg_width;
368     
369     if (s_ylegend == TRUE)
370       yl_max = v_ylegend;
371     else
372       yl_max = ya_max;
373     
374     yl_min = yl_max - leg_height;
375
376     rSGP.setColor (clr_legend);
377     rSGP.drawRect (xl_min, yl_min, xl_max, yl_max);
378
379     int iLegend = 0;                    // current legend position 
380     for (EZPlotCurveIterator iterCurve = m_vecCurves.begin(); iterCurve != m_vecCurves.end(); iterCurve++) {
381         const EZPlotCurve& curve = **iterCurve;
382         
383         if (curve.m_sLegend.length() == 0)
384             continue;
385
386         double xmin = xl_min + 1.0 * charwidth;
387         double xmax = xl_max - 1.0 * charwidth;
388         double y = yl_max - (2.0 + iLegend * 3) * charheight;
389         
390         rSGP.moveAbs (xmin, y + 0.5 * charheight);
391         rSGP.drawText (curve.m_sLegend);
392         rSGP.setColor (curve.m_iColor);
393         if (curve.m_iLineStyle != SGP::LS_NOLINE) {
394             rSGP.setLineStyle (curve.m_iLineStyle);
395             rSGP.moveAbs (xmin, y);
396             rSGP.lineAbs (xmax, y);
397         }
398         if (curve.m_iSymbol > 0) {
399             double xinc = (xmax - xmin) / (5 - 1);
400             rSGP.setLineStyle (SGP::LS_SOLID);
401             for (int j = 0; j < 5; j++) {
402               rSGP.moveAbs (xmin + j * xinc, y);
403               symbol(curve.m_iSymbol, 0.5 * charwidth, 0.5 * charheight);
404             }
405         }
406         ++iLegend;      // move to next legend position 
407     }
408   }   // end legend printing 
409
410   // calculate the extent of the axes 
411
412   /*-------------------------*/
413   /* adjust frame for labels */
414   /*-------------------------*/
415   
416   // X-Label 
417   if (c_xlabel.length() > 0)
418     ya_min += 3.0 * charheight;
419   xlbl_row = xp_min;            // put x-label on bottom of plot frame 
420
421   // Y-Label 
422   if (c_ylabel.length() > 0)
423     xa_min += 3.0 * charwidth;  // reverse rSGP.setTextSize because writing text sideways 
424   ylbl_col = xp_min + 2 * charwidth;
425
426   /*------------------------------*/
427   /* adjust frame for tick labels */
428   /*------------------------------*/
429
430   // Calc offset of tick labels from axes 
431   if (o_xaxis == NOAXIS || o_xtlabel == FALSE)
432     xtl_ofs = 0.0;
433   else if (o_xticks == BELOW)
434     xtl_ofs = -1.5 * charheight;  // kr
435   else if (o_xticks == ABOVE)
436     xtl_ofs = 1.5 * charheight;
437   
438   if (o_yaxis == NOAXIS || o_ytlabel == FALSE)
439     ytl_ofs = 0.0;
440   else if (o_yticks == LEFT)
441     ytl_ofs = -(1 + y_fldwid) * charwidth;
442   else if (o_yticks == RIGHT)
443     ytl_ofs = 1.5 * charwidth;
444
445   xt_min = xa_min;
446   yt_min = ya_min;
447   xt_max = xa_max;
448   yt_max = ya_max;
449
450   // see if need to shrink axis extents and/or tick extents 
451   if (xtl_ofs != 0.0 && s_ycross == FALSE) {
452     if (o_xticks == BELOW) {
453       ya_min += 2.5 * charheight;
454       yt_min = ya_min;
455     } else if (o_xticks == ABOVE) {
456       ya_min += 0.0;
457       yt_min = ya_min + 2.5 * charheight;
458     }
459   } else   // noaxis, no t-labels, or user set cross 
460     yt_min = ya_min;
461   
462   if (ytl_ofs != 0.0 && s_xcross == FALSE) {
463     if (o_yticks == LEFT) {
464       xa_min += (1 + y_fldwid) * charwidth;
465       xt_min = xa_min;
466     } else if (o_yticks == RIGHT) {
467       xa_min += 0.0;
468       xt_min = xa_min + ytl_ofs + y_fldwid * charwidth;
469     }
470   } else
471     xt_min = xa_min;
472
473   // decrease size of graph, if necessary, to accommadate space 
474   // between axis boundary and boundary of ticks 
475   double x_added_ticks = -1; // number of tick spaces added to axis 
476   double y_added_ticks = -1;
477   if (o_xaxis == NOAXIS || o_xtlabel == FALSE)
478     x_added_ticks = 0;
479   if (o_yaxis == NOAXIS || o_ytlabel == FALSE)
480     y_added_ticks = 0;
481   
482   if (o_grid == TRUE) {
483     if (x_added_ticks < 0)
484       x_added_ticks = 2;
485     if (y_added_ticks < 0)
486       y_added_ticks = 2;
487   }
488   
489   if (x_added_ticks < 0) {
490     if (o_yticks == LEFT || s_ycross)
491       x_added_ticks = 1;
492     else
493       x_added_ticks = 2;
494   }
495   
496   if (y_added_ticks < 0) {
497     if (o_xticks == BELOW || s_xcross)
498       y_added_ticks = 1;
499     else
500       y_added_ticks = 2;
501   }
502   
503   xn_tickinc = (xt_max - xt_min) / (x_nint + x_added_ticks);
504   yn_tickinc = (yt_max - yt_min) / (y_nint + y_added_ticks);
505   
506   xt_min += 0.5 * x_added_ticks * xn_tickinc;
507   xt_max -= 0.5 * x_added_ticks * xn_tickinc;
508   yt_min += 0.5 * y_added_ticks * yn_tickinc;
509   yt_max -= 0.5 * y_added_ticks * yn_tickinc;
510   
511   xgn_min = xt_min;
512   xgn_max = xt_max;
513   ygn_min = yt_min;
514   ygn_max = yt_max;
515
516   //------------------------------------------------------------------------
517   
518   m_xWorldScale = (xgn_max - xgn_min) / (xgw_max - xgw_min);
519   m_yWorldScale = (ygn_max - ygn_min) / (ygw_max - ygw_min);
520
521   // PLOT CURVES 
522   
523   rSGP.setLineStyle (SGP::LS_SOLID);
524   drawAxes();
525
526   // size of symbol in NDC 
527   double symwidth = charwidth;
528   double symheight = charheight;
529   
530   for (EZPlotCurveIterator iterCurve3 = m_vecCurves.begin(); iterCurve3 != m_vecCurves.end(); iterCurve3++) {
531       const EZPlotCurve& curve = **iterCurve3;
532
533       rSGP.setColor (curve.m_iColor);
534
535       if (curve.m_iLineStyle != SGP::LS_NOLINE) {
536         rSGP.setLineStyle (curve.m_iLineStyle);
537         double x = convertWorldToNDC_X (curve.x[0]);
538         double y = convertWorldToNDC_Y (curve.y[0]);
539         rSGP.moveAbs (x, y);
540         for (int i = 1; i < curve.m_iPointCount; i++) {
541             x = convertWorldToNDC_X (curve.x[i]);
542             y = convertWorldToNDC_Y (curve.y[i]);
543             rSGP.lineAbs (x, y);
544         }
545       }
546       if (curve.m_iSymbol > 0) {
547         rSGP.setLineStyle (SGP::LS_SOLID);
548         double x = convertWorldToNDC_X (curve.x[0]);
549         double y = convertWorldToNDC_Y (curve.y[0]);
550         rSGP.moveAbs (x, y);
551         symbol (curve.m_iSymbol, symwidth, symheight);
552         for (int i = 1; i < curve.m_iPointCount; i++)
553           if (i % curve.m_iSymbolFreq == 0 || i == curve.m_iPointCount - 1) {
554             x = convertWorldToNDC_X (curve.x[i]);
555             y = convertWorldToNDC_Y (curve.y[i]);
556             rSGP.moveAbs (x, y);
557             symbol (curve.m_iSymbol, symwidth, symheight);
558           }
559       }
560   }
561
562 }
563
564
565 /* NAME
566  *   drawAxes                   INTERNAL routine to draw axis & label them
567  *
568  * SYNOPSIS
569  *   drawAxes()
570  */
571
572
573 void 
574 EZPlot::drawAxes()
575 {
576   double xticklen = 0, yticklen = 0; // length of ticks in NDC 
577   double minorinc;              // increment between minor axes 
578   double xaxispos, yaxispos;    // crossing of axes
579   double x, y, x2, y2;
580   bool axis_near;       // TRUE if axis too close to print t-label 
581   int i, j;
582   char str[256];
583   char *numstr;
584   
585   rSGP.setTextSize (charheight);
586   rSGP.setTextColor (1, -1);
587   
588   if (o_xticks == ABOVE)
589     xticklen = charheight;
590   else if (o_xticks == BELOW)
591     xticklen = -charheight;
592   
593   if (o_yticks == RIGHT)
594     yticklen = charwidth;
595   else if (o_yticks == LEFT)
596     yticklen = -charwidth;
597   
598   if (c_title.length() > 0) {
599     double wText, hText;
600     rSGP.setTextSize (charheight * 2.0);
601     rSGP.getTextExtent (c_title.c_str(), &wText, &hText);
602     rSGP.moveAbs (xa_min + (xa_max-xa_min)/2 - wText/2, title_row);
603     rSGP.setTextColor (clr_title, -1);
604     rSGP.drawText (c_title);
605     rSGP.setTextSize (charheight);
606   }
607   
608   if (o_grid == TRUE || o_box == TRUE) {
609     rSGP.setColor (clr_axis);
610     rSGP.moveAbs (xa_min, ya_min);
611     rSGP.lineAbs (xa_max, ya_min);
612     rSGP.lineAbs (xa_max, ya_max);
613     rSGP.lineAbs (xa_min, ya_max);
614     rSGP.lineAbs (xa_min, ya_min);
615   }
616   
617   // calculate position of axes 
618   
619   // x-axis 
620   if (s_ycross == TRUE) {       // convert users' world-coord 
621     xaxispos = convertWorldToNDC_Y (v_ycross);// axis to its position in NDC 
622     x = convertWorldToNDC_X (xgw_min);
623   } else
624     xaxispos = ya_min;
625   
626   // y-axis 
627   if (s_xcross == TRUE) {       // convert users' world-coord 
628     yaxispos = convertWorldToNDC_X (v_xcross);// axis to its NDC position 
629     y = convertWorldToNDC_Y (ygw_min);
630   } else
631     yaxispos = xa_min;
632   
633   /*-------------*/
634   /* draw x-axis */
635   /*-------------*/
636   
637   if (o_xaxis == LINEAR) {
638     // draw axis line 
639     
640     rSGP.setColor (clr_axis);
641     if (o_tag && !o_grid && !o_box && s_xcross) {
642       rSGP.moveAbs (xa_min, xaxispos - charheight);
643       rSGP.lineAbs (xa_min, xaxispos + charheight);
644     }
645     rSGP.moveAbs (xa_min, xaxispos);
646     rSGP.lineAbs (xa_max, xaxispos);
647     if (o_tag && !o_grid && !o_box) {
648       rSGP.moveAbs (xa_max, xaxispos - charheight);
649       rSGP.lineAbs (xa_max, xaxispos + charheight);
650     }
651     
652     if (o_grid == TRUE) {
653       rSGP.setColor (clr_grid);
654       for (i = 0; i <= x_nint; i++) {
655         rSGP.moveAbs (xt_min + xn_tickinc * i, ya_max);
656         rSGP.lineAbs (xt_min + xn_tickinc * i, ya_min);
657       }
658     }
659     rSGP.moveAbs (xa_min + (xa_max-xa_min)/2 - c_xlabel.length()*charwidth, xlbl_row);
660     rSGP.setTextSize (charheight * 2.0);
661     rSGP.setTextColor (clr_label, -1);
662     rSGP.drawText (c_xlabel);
663     rSGP.setTextSize (charheight);
664     minorinc = xn_tickinc / (o_xminortick + 1);
665
666     for (i = 0; i <= x_nint; i++) {
667       x = xt_min + xn_tickinc * i;
668       rSGP.setColor (clr_axis);
669       rSGP.moveAbs (x, xaxispos);
670       rSGP.lineAbs (x, xaxispos + xticklen);
671       if (i != x_nint)
672         for (j = 1; j <= o_xminortick; j++) {
673           x2 = x + minorinc * j;
674           rSGP.moveAbs (x2, xaxispos);
675           rSGP.lineAbs (x2, xaxispos + TICKRATIO * xticklen);
676         }
677       axis_near = FALSE;
678       if (xaxispos + xtl_ofs > ya_min && o_yaxis != NOAXIS) {
679         double xw = xgw_min + i * xw_tickinc;
680         double x = convertWorldToNDC_X (xw);
681         double d = x - yaxispos;
682         if (o_yticks == RIGHT && d >= 0  && d < 0.9 * xn_tickinc)
683           axis_near = TRUE;
684         if (o_yticks == LEFT && d <= 0 && d > -0.9 * xn_tickinc)
685           axis_near = TRUE;
686       }
687
688       if (o_xtlabel == TRUE && axis_near == FALSE) {
689         snprintf (str, sizeof(str), x_numfmt, xgw_min + xw_tickinc * i);
690         numstr = str_skip_head (str, " ");
691         rSGP.moveAbs (x-strlen(numstr)*charwidth/2, xaxispos + xtl_ofs);
692         rSGP.setTextColor (clr_number, -1);
693         rSGP.drawText (numstr);
694       }
695     }
696   }             // X - Axis 
697   
698   
699   /*--------*/
700   /* y-axis */
701   /*--------*/
702   
703   if (o_yaxis == LINEAR) {
704     
705     rSGP.setColor (clr_axis);
706     if (o_tag && !o_grid && !o_box && s_ycross) {
707       rSGP.moveAbs (yaxispos - charwidth, ya_min);
708       rSGP.lineAbs (yaxispos + charwidth, ya_min);
709     }
710     rSGP.moveAbs (yaxispos, ya_min);
711     rSGP.lineAbs (yaxispos, ya_max);
712     if (o_tag && !o_grid && !o_box) {
713       rSGP.moveAbs (yaxispos - charwidth, ya_max);
714       rSGP.lineAbs (yaxispos + charwidth, ya_max);
715     }
716     
717     if (o_grid == TRUE) {
718       rSGP.setColor (clr_grid);
719       for (i = 0; i <= y_nint; i++) {
720         y = yt_min + yn_tickinc * i;
721         rSGP.moveAbs (xa_max, y);
722         rSGP.lineAbs (xa_min, y);
723       }
724     }
725     rSGP.moveAbs (ylbl_col, ya_min + (ya_max-ya_min)/2 - c_ylabel.length()*charheight);
726     rSGP.setTextAngle (HALFPI);
727     rSGP.setTextSize (2 * charheight);
728     rSGP.setTextColor (clr_label, -1);
729     rSGP.drawText (c_ylabel);
730     rSGP.setTextAngle (0.0);
731     rSGP.setTextSize (charheight);
732     minorinc = yn_tickinc / (o_yminortick + 1);
733     
734     for (i = 0; i <= y_nint; i++) {
735       y = yt_min + yn_tickinc * i;
736       rSGP.setColor (clr_axis);
737       rSGP.moveAbs (yaxispos, y);
738       rSGP.lineAbs (yaxispos + yticklen, y);
739       if (i != y_nint)
740         for (j = 1; j <= o_yminortick; j++) {
741           y2 = y + minorinc * j;
742           rSGP.moveAbs (yaxispos, y2);
743           rSGP.lineAbs (yaxispos + TICKRATIO * yticklen, y2);
744         }
745       axis_near = FALSE;
746       if (yaxispos + ytl_ofs > xa_min && o_xaxis != NOAXIS) {
747         double yw = ygw_min + i * yw_tickinc;
748         double y = convertWorldToNDC_Y (yw);
749         double d = y - xaxispos;
750         if (o_xticks == ABOVE && d >= 0 && d < 0.9 * yn_tickinc)
751           axis_near = TRUE;
752         if (o_xticks == BELOW && d <= 0 && d > -0.9 * yn_tickinc)
753           axis_near = TRUE;
754       }
755       if (o_ytlabel == TRUE && axis_near == FALSE) {
756         snprintf (str, sizeof(str), y_numfmt, ygw_min + yw_tickinc * i);
757         rSGP.moveAbs (yaxispos + ytl_ofs, y + 0.5 * charheight);
758         rSGP.setTextColor (clr_number, -1);
759         rSGP.drawText (str);
760       }
761     }
762   }             // Y - Axis
763 }
764
765
766 void 
767 EZPlot::symbol (int sym, double symwidth, double symheight)
768 {
769   if (sym <= 0)
770     return;
771   
772   if (sym == SB_CROSS) {
773     rSGP.moveRel (-0.5 * symwidth, -0.5 * symheight);
774     rSGP.lineRel (symwidth, symheight);
775     rSGP.moveRel (-symwidth, 0.0);
776     rSGP.lineRel (symwidth, -symheight);
777     rSGP.moveRel (-0.5 * symwidth, 0.5 * symheight);
778   } else if (sym == SB_PLUS) {
779     rSGP.moveRel (-0.5 * symwidth, 0.0);
780     rSGP.lineRel (symwidth, 0.0);
781     rSGP.moveRel (-0.5 * symwidth, -0.5 * symheight);
782     rSGP.lineRel (0.0, symheight);
783     rSGP.moveRel (0.0, -0.5 * symheight);
784   } else if (sym == SB_BOX) {
785     rSGP.moveRel (-0.5 * symwidth, -0.5 * symheight);
786     rSGP.lineRel (symwidth, 0.0);
787     rSGP.lineRel (0.0, symheight);
788     rSGP.lineRel (-symwidth, 0.0);
789     rSGP.lineRel (0.0, -symheight);
790     rSGP.moveRel (0.5 * symwidth, 0.5 * symheight);
791   } else if (sym == SB_CIRCLE) {
792     rSGP.drawCircle (symwidth);
793   } else if (sym == SB_ERRORBAR) {
794     rSGP.moveRel (-0.5 * symwidth, 0.5 * symheight);
795     rSGP.lineRel (symwidth, 0.0);
796     rSGP.moveRel (-0.5 * symwidth, 0.0);
797     rSGP.lineRel (0.0, -symheight);
798     rSGP.moveRel (-0.5 * symwidth, 0.0);
799     rSGP.lineRel (symwidth, 0.0);
800     rSGP.moveRel (-0.5 * symwidth, 0.5 * symheight);
801   }
802 }
803
804
805
806 /* NAME
807  *    axis_scale                        calculates graph axis scaling
808  *
809  *  SYNOPSIS:
810  *    retval = axis_scale (min, max, nint, minp, maxp, nintp, 
811  *                         rec_total, rec_frac)
812  *
813  *    INPUT:
814  *      double min         Smallest value to plot
815  *      double max         Largest value to plot
816  *      int    nint        Number of intervals desired
817  *
818  *    OUTPUT:
819  *      int   retval       FALSE if illegal parameters, else TRUE
820  *      double *minp       Minimum graph value
821  *      double *maxp       Maximum graph value
822  *      int    *nintp      Number of intervals for graph
823  *      int    *rec_total  Recommended field width for printing out the number
824  *      int    *rec_frac   Recommended number of digits for print fraction
825  */
826
827 int 
828 EZPlot::axis_scale (double min, double max, int nint, double *minp, double *maxp, int *nintp)
829 {
830   if (min >= max || nint < 1) {
831     sys_error (ERR_WARNING, "Invalid params: min=%lf, max=%lf, num intervals=%d [axis_scale]", min, max, nint);
832     return (FALSE);
833   }
834   
835   double eps = 0.025;
836   double a = fabs(min);
837   if (fabs(min) < fabs(max))
838     a = fabs(max);
839   double scale = pow (10.0, floor(log10(a)));
840  loop:
841   double mina = min / scale;
842   double maxa = max / scale;
843   double d = (maxa - mina) / nint;
844   double j = d * eps;
845   double e = floor (log10(d));
846   double f = d / pow (10.0, e);
847   double v = 10.0;
848   if (f < sqrt(2.0))
849     v = 1.0;
850   else if (f < sqrt (10.0))
851     v = 2.0;
852   else if (f < sqrt (50.0))
853     v = 5.0;
854   double wdt = v * pow (10.0, e);
855   double g = floor (mina / wdt);
856     if (fabs(g + 1 - mina / wdt) < j)
857       g = g + 1;
858 #if 1
859     g++;
860 #endif
861   *minp = wdt * g;
862   double h = floor (maxa / wdt) + 1.0;
863   if (fabs(maxa / wdt + 1 - h) < j)
864      h = h - 1;
865 #if 1
866     h--;
867 #endif
868   *maxp = wdt * h;
869   *nintp = static_cast<int>(h - g);
870   if (fabs(*maxp) >= 10.0 || fabs(*minp) >= 10.0) {
871     scale = scale * 10.0;
872     goto loop;
873   }
874   
875   *minp *= scale;
876   *maxp *= scale;
877   
878   return (TRUE);
879 }
880
881
882 /* NAME
883  *   make_numfmt                Make a numeric format string
884  *
885  * SYNOPSIS
886  *   make_numfmt (fmtstr, fldwid, nfrac, min, max, nint)
887  *   char *fmtstr               Returned format string for printf()
888  *   int  *fldwid               Returned field width
889  *   int  *nfrac                If < 0, then calculate best number of
890  *                              fraction places & return that value
891  *                              If >= 0, then use that number of places
892  *   double min                 Minimum value
893  *   double max                 Maximum value
894  *   int nint                   Number of intervals between min & max
895  *
896  * DESCRIPTION
897  *   This  routine is written as an INTERNAL routine for EZPLOT
898  */
899
900 static inline double 
901 trunc (double x)
902 {
903   double integer;
904   
905   modf (x, &integer);
906   
907   return (integer);
908 }
909
910 void 
911 EZPlot::make_numfmt (char *fmtstr, int *fldwid, int *nfrac, double minval, double maxval, int nint)
912 {
913   int wid, frac, expon;
914
915   double delta = (maxval - minval) / nint;
916   double absmin = fabs(minval);
917   double absmax = fabs(maxval);\r
918   if (absmin > absmax)\r
919         absmax = absmin;
920   double logt = log10( absmax );
921
922   if (fabs(logt) >= 6) {                // use exponential format 
923     if (fabs(logt) > 99)
924       expon = 5;                //  E+102 
925     else
926       expon = 4;                //  E+00 
927     
928     if (*nfrac < 0) {           // calculate frac 
929       delta /= pow (10., floor(logt));  // scale delta 
930       frac = static_cast<int>(fabs(trunc(log10(delta)))) + 1;
931       if (frac < 1)
932         frac = 1;               // to be safe, add decimal pt 
933     } else                      // use users' frac 
934       frac = *nfrac;
935     
936     wid = 2 + frac + expon;
937     if (minval < 0. || maxval < 0.)
938       ++wid;
939     sprintf (fmtstr, "%s%d%s%d%s", "%", wid, ".", frac, "g");
940   } else {                      // use fixed format 
941     wid = static_cast<int>(trunc(logt)) + 1;
942     if (wid < 1)
943       wid = 1;
944     if (minval < 0. || maxval < 0.)
945       ++wid;
946     
947     if (*nfrac < 0) {           // calculate frac 
948       if (delta >= 0.999999)
949         frac = 1;               // add a decimal pt to be safe 
950       else
951         frac = static_cast<int>(fabs(trunc(log10(delta)))) + 1;
952     } else                      // use users' frac 
953       frac = *nfrac;
954     
955     wid += 1 + frac;
956     sprintf (fmtstr, "%s%d%s%d%s", "%", wid, ".", frac, "f");
957   }
958
959   *fldwid = wid;
960   *nfrac = frac;
961 }
962