Implemented long variable names a la spss V12.
[pspp-builds.git] / src / plot-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 #include "alloc.h"
33 #include "som.h"
34 #include "output.h"
35
36
37 const char *data_colour[] = {
38   "brown",
39   "red",
40   "orange",
41   "yellow",
42   "green",
43   "blue",
44   "violet",
45   "grey",
46   "pink"
47 };
48
49
50
51 struct chart *
52 chart_create(void)
53 {
54   struct chart *chart;
55
56   struct outp_driver *d;
57
58   chart = xmalloc(sizeof(struct chart) );
59
60   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
61     {
62       assert(d->class->initialise_chart);
63       d->class->initialise_chart(d, chart);
64       break; /* KLUDGE!! */
65     }
66
67   if ( ! chart->lp ) 
68     return 0;
69
70   if (pl_openpl_r (chart->lp) < 0)      /* open Plotter */
71       return 0;
72
73   pl_fspace_r (chart->lp, 0.0, 0.0, 1000.0, 1000.0); /* set coordinate system */
74   pl_flinewidth_r (chart->lp, 0.25);    /* set line thickness */
75   pl_pencolorname_r (chart->lp, "black"); 
76
77   pl_erase_r (chart->lp);               /* erase graphics display */
78   pl_filltype_r(chart->lp,0);
79
80
81
82   pl_savestate_r(chart->lp);
83
84   /* Set default chartetry */
85   chart->data_top =   900;
86   chart->data_right = 800;
87   chart->data_bottom = 120;
88   chart->data_left = 150;
89   chart->abscissa_top = 70;
90   chart->ordinate_right = 120;
91   chart->title_bottom = 920;
92   chart->legend_left = 810;
93   chart->legend_right = 1000;
94   chart->font_size = 0;
95   strcpy(chart->fill_colour,"red");
96
97
98   /* Get default font size */
99   if ( !chart->font_size) 
100     chart->font_size = pl_fontsize_r(chart->lp, -1);
101
102   /* Draw the data area */
103   pl_box_r(chart->lp, 
104            chart->data_left, chart->data_bottom, 
105            chart->data_right, chart->data_top);
106
107   return chart;
108
109 }
110
111
112
113 /* Draw a tick mark at position
114    If label is non zero, then print it at the tick mark
115 */
116 void
117 draw_tick(struct chart *chart, 
118           enum tick_orientation orientation, 
119           double position, 
120           const char *label, ...)
121 {
122   const int tickSize = 10;
123
124   assert(chart);
125
126   pl_savestate_r(chart->lp);
127
128   pl_move_r(chart->lp, chart->data_left, chart->data_bottom);
129
130   if ( orientation == TICK_ABSCISSA ) 
131     pl_flinerel_r(chart->lp, position, 0, position, -tickSize);
132   else if (orientation == TICK_ORDINATE ) 
133       pl_flinerel_r(chart->lp, 0, position, -tickSize, position);
134   else
135     assert(0);
136
137   if ( label ) {
138     char buf[10];
139     va_list ap;
140     va_start(ap,label);
141     vsnprintf(buf,10,label,ap);
142
143     if ( orientation == TICK_ABSCISSA ) 
144       pl_alabel_r(chart->lp, 'c','t', buf);
145     else if (orientation == TICK_ORDINATE ) 
146       {
147         if ( fabs(position) < DBL_EPSILON )
148             pl_moverel_r(chart->lp, 0, 10);
149
150         pl_alabel_r(chart->lp, 'r','c', buf);
151       }
152
153     va_end(ap);
154   }
155     
156   pl_restorestate_r(chart->lp);
157 }
158
159
160
161
162 /* Write the title on a chart*/
163 void  
164 chart_write_title(struct chart *chart, const char *title, ...)
165 {
166   va_list ap;
167   char buf[100];
168
169   if ( ! chart ) 
170           return ;
171
172   pl_savestate_r(chart->lp);
173   pl_ffontsize_r(chart->lp,chart->font_size * 1.5);
174   pl_move_r(chart->lp,chart->data_left, chart->title_bottom);
175
176   va_start(ap,title);
177   vsnprintf(buf,100,title,ap);
178   pl_alabel_r(chart->lp,0,0,buf);
179   va_end(ap);
180
181   pl_restorestate_r(chart->lp);
182 }
183
184
185 extern struct som_table_class tab_table_class;
186
187 void
188 chart_submit(struct chart *chart)
189 {
190   struct som_entity s;
191
192   if ( ! chart ) 
193      return ;
194
195   pl_restorestate_r(chart->lp);
196
197   s.class = &tab_table_class;
198   s.ext = chart;
199   s.type = SOM_CHART;
200   som_submit (&s);
201   
202   if (pl_closepl_r (chart->lp) < 0)     /* close Plotter */
203     {
204       fprintf (stderr, "Couldn't close Plotter\n");
205     }
206
207   pl_deletepl_r(chart->lp);
208
209   pl_deleteplparams(chart->pl_params);
210
211   free(chart);
212
213 }
214
215
216 /* Set the scale for the abscissa */
217 void 
218 chart_write_xscale(struct chart *ch, double min, double max, int ticks)
219 {
220   double x;
221
222   const double tick_interval = 
223     chart_rounded_tick( (max - min) / (double) ticks);
224
225   assert ( ch );
226
227
228   ch->x_max = ceil( max / tick_interval ) * tick_interval ; 
229   ch->x_min = floor ( min / tick_interval ) * tick_interval ;
230
231
232   ch->abscissa_scale = fabs(ch->data_right - ch->data_left) / 
233     fabs(ch->x_max - ch->x_min);
234
235   for(x = ch->x_min ; x <= ch->x_max; x += tick_interval )
236     {
237       draw_tick (ch, TICK_ABSCISSA, 
238                  (x - ch->x_min) * ch->abscissa_scale, "%g", x);
239     }
240
241 }
242
243
244 /* Set the scale for the ordinate */
245 void 
246 chart_write_yscale(struct chart *ch, double smin, double smax, int ticks)
247 {
248   double y;
249
250   const double tick_interval = 
251     chart_rounded_tick( (smax - smin) / (double) ticks);
252
253
254   if ( !ch ) 
255           return;
256
257   ch->y_max = ceil  ( smax / tick_interval ) * tick_interval ; 
258   ch->y_min = floor ( smin / tick_interval ) * tick_interval ;
259
260   ch->ordinate_scale = 
261     fabs(ch->data_top -  ch->data_bottom) / fabs(ch->y_max - ch->y_min) ;
262
263   for(y = ch->y_min ; y <= ch->y_max; y += tick_interval )
264     {
265     draw_tick (ch, TICK_ORDINATE, 
266                (y - ch->y_min) * ch->ordinate_scale, "%g", y);
267     }
268
269 }
270