ae90a7e3933b66c1abdea019f7f824e89e31450c
[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   assert(chart);
170
171   pl_savestate_r(chart->lp);
172   pl_ffontsize_r(chart->lp,chart->font_size * 1.5);
173   pl_move_r(chart->lp,chart->data_left, chart->title_bottom);
174
175   va_start(ap,title);
176   vsnprintf(buf,100,title,ap);
177   pl_alabel_r(chart->lp,0,0,buf);
178   va_end(ap);
179
180   pl_restorestate_r(chart->lp);
181 }
182
183
184 extern struct som_table_class tab_table_class;
185
186 void
187 chart_submit(struct chart *chart)
188 {
189   struct som_entity s;
190
191   if ( ! chart ) 
192      return ;
193
194   pl_restorestate_r(chart->lp);
195
196   s.class = &tab_table_class;
197   s.ext = chart;
198   s.type = SOM_CHART;
199   som_submit (&s);
200   
201   if (pl_closepl_r (chart->lp) < 0)     /* close Plotter */
202     {
203       fprintf (stderr, "Couldn't close Plotter\n");
204     }
205
206   pl_deletepl_r(chart->lp);
207
208   pl_deleteplparams(chart->pl_params);
209
210   free(chart);
211
212 }
213
214
215 /* Set the scale for the abscissa */
216 void 
217 chart_write_xscale(struct chart *ch, double min, double max, int ticks)
218 {
219   double x;
220
221   const double tick_interval = 
222     chart_rounded_tick( (max - min) / (double) ticks);
223
224   assert ( ch );
225
226
227   ch->x_max = ceil( max / tick_interval ) * tick_interval ; 
228   ch->x_min = floor ( min / tick_interval ) * tick_interval ;
229
230
231   ch->abscissa_scale = fabs(ch->data_right - ch->data_left) / 
232     fabs(ch->x_max - ch->x_min);
233
234   for(x = ch->x_min ; x <= ch->x_max; x += tick_interval )
235     {
236       draw_tick (ch, TICK_ABSCISSA, 
237                  (x - ch->x_min) * ch->abscissa_scale, "%g", x);
238     }
239
240 }
241
242
243 /* Set the scale for the ordinate */
244 void 
245 chart_write_yscale(struct chart *ch, double smin, double smax, int ticks)
246 {
247   double y;
248
249   const double tick_interval = 
250     chart_rounded_tick( (smax - smin) / (double) ticks);
251
252
253   assert (ch) ;
254
255
256   ch->y_max = ceil  ( smax / tick_interval ) * tick_interval ; 
257   ch->y_min = floor ( smin / tick_interval ) * tick_interval ;
258
259   ch->ordinate_scale = 
260     fabs(ch->data_top -  ch->data_bottom) / fabs(ch->y_max - ch->y_min) ;
261
262   for(y = ch->y_min ; y <= ch->y_max; y += tick_interval )
263     {
264     draw_tick (ch, TICK_ORDINATE, 
265                (y - ch->y_min) * ch->ordinate_scale, "%g", y);
266     }
267
268 }
269