Added a signal handler for SIGFPE
[pspp-builds.git] / src / chart.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2004 Free Software Foundation, Inc.
3    Written by John Darrington <john@darrington.wattle.id.au>
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <plot.h>
23 #include <stdarg.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <float.h>
27 #include <assert.h>
28 #include <math.h>
29
30 #include "chart.h"
31 #include "str.h"
32
33
34 const char *data_colour[] = {
35   "brown",
36   "red",
37   "orange",
38   "yellow",
39   "green",
40   "blue",
41   "violet",
42   "grey",
43   "pink"
44 };
45
46
47
48 int
49 chart_initialise(struct chart *chart)
50 {
51
52   chart->pl_params = pl_newplparams();
53
54   chart->lp = pl_newpl_r ("X",0,stdout,stderr,chart->pl_params);
55
56   if (pl_openpl_r (chart->lp) < 0)      /* open Plotter */
57       return 1;
58
59   pl_fspace_r (chart->lp, 0.0, 0.0, 1000.0, 1000.0); /* set coordinate system */
60   pl_flinewidth_r (chart->lp, 0.25);    /* set line thickness */
61   pl_pencolorname_r (chart->lp, "black"); 
62
63   pl_erase_r (chart->lp);               /* erase graphics display */
64   pl_filltype_r(chart->lp,0);
65
66
67
68   pl_savestate_r(chart->lp);
69
70   /* Set default chartetry */
71   chart->data_top =   900;
72   chart->data_right = 800;
73   chart->data_bottom = 120;
74   chart->data_left = 150;
75   chart->abscissa_top = 70;
76   chart->ordinate_right = 120;
77   chart->title_bottom = 920;
78   chart->legend_left = 810;
79   chart->legend_right = 1000;
80   chart->font_size = 0;
81   strcpy(chart->fill_colour,"red");
82
83
84   /* Get default font size */
85   if ( !chart->font_size) 
86     chart->font_size = pl_fontsize_r(chart->lp, -1);
87
88   /* Draw the data area */
89   pl_box_r(chart->lp, 
90            chart->data_left, chart->data_bottom, 
91            chart->data_right, chart->data_top);
92
93   return 0;
94
95 }
96
97
98
99 /* Draw a tick mark at position
100    If label is non zero, then print it at the tick mark
101 */
102 void
103 draw_tick(struct chart *chart, 
104           enum tick_orientation orientation, 
105           double position, 
106           const char *label, ...)
107 {
108   const int tickSize = 10;
109
110   pl_savestate_r(chart->lp);
111
112   pl_move_r(chart->lp, chart->data_left, chart->data_bottom);
113
114   if ( orientation == TICK_ABSCISSA ) 
115     pl_flinerel_r(chart->lp, position, 0, position, -tickSize);
116   else if (orientation == TICK_ORDINATE ) 
117       pl_flinerel_r(chart->lp, 0, position, -tickSize, position);
118   else
119     assert(0);
120
121   if ( label ) {
122     char buf[10];
123     va_list ap;
124     va_start(ap,label);
125     vsnprintf(buf,10,label,ap);
126
127     if ( orientation == TICK_ABSCISSA ) 
128       pl_alabel_r(chart->lp, 'c','t', buf);
129     else if (orientation == TICK_ORDINATE ) 
130       {
131         if ( fabs(position) < DBL_EPSILON )
132             pl_moverel_r(chart->lp, 0, 10);
133
134         pl_alabel_r(chart->lp, 'r','c', buf);
135       }
136
137     va_end(ap);
138   }
139     
140   pl_restorestate_r(chart->lp);
141 }
142
143
144
145
146 /* Write the title on a chart*/
147 void  
148 chart_write_title(struct chart *chart, const char *title, ...)
149 {
150   va_list ap;
151   char buf[100];
152
153   pl_savestate_r(chart->lp);
154   pl_ffontsize_r(chart->lp,chart->font_size * 1.5);
155   pl_move_r(chart->lp,chart->data_left, chart->title_bottom);
156
157   va_start(ap,title);
158   vsnprintf(buf,100,title,ap);
159   pl_alabel_r(chart->lp,0,0,buf);
160   va_end(ap);
161
162   pl_restorestate_r(chart->lp);
163 }
164
165
166
167 void
168 chart_finalise(struct chart *chart)
169 {
170   pl_restorestate_r(chart->lp);
171
172   if (pl_closepl_r (chart->lp) < 0)     /* close Plotter */
173     {
174       fprintf (stderr, "Couldn't close Plotter\n");
175     }
176
177
178   pl_deletepl_r(chart->lp);
179
180   pl_deleteplparams(chart->pl_params);
181
182 }
183
184
185
186 /* Adjust tick to be a sensible value 
187    ie:  ... 0.1,0.2,0.5,   1,2,5,  10,20,50 ... */
188 double
189 chart_rounded_tick(double tick)
190 {
191
192   int i;
193
194   double diff = DBL_MAX;
195   double t = tick;
196     
197   static const double standard_ticks[] = {1, 2, 5, 10};
198
199   const double factor = pow(10,ceil(log10(standard_ticks[0] / tick))) ;
200
201   for (i = 3  ; i >= 0 ; --i) 
202     {
203       const double d = fabs( tick - standard_ticks[i] / factor ) ;
204
205       if ( d < diff ) 
206         {
207           diff = d;
208           t = standard_ticks[i] / factor ;
209         }
210     }
211
212   return t;
213     
214 }
215
216
217 /* Set the scale for the abscissa */
218 void 
219 chart_write_xscale(struct chart *ch, double min, double max, int ticks)
220 {
221   double x;
222
223   const double tick_interval = 
224     chart_rounded_tick( (max - min) / (double) ticks);
225
226   ch->x_max = ceil( max / tick_interval ) * tick_interval ; 
227   ch->x_min = floor ( min / tick_interval ) * tick_interval ;
228
229   ch->abscissa_scale = fabs(ch->data_right - ch->data_left) / 
230     fabs(ch->x_max - ch->x_min);
231
232   for(x = ch->x_min ; x <= ch->x_max; x += tick_interval )
233     {
234       draw_tick (ch, TICK_ABSCISSA, 
235                  (x - ch->x_min) * ch->abscissa_scale, "%g", x);
236     }
237
238 }
239
240
241 /* Set the scale for the ordinate */
242 void 
243 chart_write_yscale(struct chart *ch, double smin, double smax, int ticks)
244 {
245   double y;
246
247   const double tick_interval = 
248     chart_rounded_tick( (smax - smin) / (double) ticks);
249
250   ch->y_max = ceil  ( smax / tick_interval ) * tick_interval ; 
251   ch->y_min = floor ( smin / tick_interval ) * tick_interval ;
252
253   ch->ordinate_scale = 
254     fabs(ch->data_top -  ch->data_bottom) / fabs(ch->y_max - ch->y_min) ;
255
256   for(y = ch->y_min ; y <= ch->y_max; y += tick_interval )
257     {
258     draw_tick (ch, TICK_ORDINATE, 
259                (y - ch->y_min) * ch->ordinate_scale, "%g", y);
260     }
261
262 }
263