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