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