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