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