output: Add initial support for PSPP output via Cairo.
[pspp] / src / output / charts / box-whisker.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2004, 2008 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17
18 #include <config.h>
19
20 #include <math.h>
21 #include <assert.h>
22 #include <libpspp/misc.h>
23
24 #include <output/charts/box-whisker.h>
25 #include <output/charts/plot-chart.h>
26
27 #include <output/chart.h>
28 #include <math/chart-geometry.h>
29 #include <math/box-whisker.h>
30
31 /* Draw a box-and-whiskers plot
32 */
33
34 /* Draw an OUTLIER on the plot CH
35  * at CENTRELINE
36  */
37 static void
38 draw_case (struct chart *ch, double centreline,
39            const struct outlier *outlier)
40 {
41
42 #define MARKER_CIRCLE 4
43 #define MARKER_STAR 3
44
45   pl_fmarker_r(ch->lp,
46                centreline,
47                ch->data_bottom + (outlier->value - ch->y_min) * ch->ordinate_scale,
48                outlier->extreme ? MARKER_STAR : MARKER_CIRCLE,
49                20);
50
51   pl_moverel_r(ch->lp, 10,0);
52
53   pl_alabel_r(ch->lp, 'l', 'c', ds_cstr (&outlier->label));
54 }
55
56
57 void
58 boxplot_draw_boxplot (struct chart *ch,
59                       double box_centre,
60                       double box_width,
61                       const struct box_whisker *bw,
62                       const char *name)
63 {
64   double whisker[2];
65   double hinge[3];
66   struct ll *ll;
67
68   const struct ll_list *outliers;
69
70   const double box_left = box_centre - box_width / 2.0;
71
72   const double box_right = box_centre + box_width / 2.0;
73
74   double box_bottom ;
75   double box_top ;
76   double bottom_whisker ;
77   double top_whisker ;
78
79   box_whisker_whiskers (bw, whisker);
80   box_whisker_hinges (bw, hinge);
81
82   box_bottom = ch->data_bottom + (hinge[0] - ch->y_min ) * ch->ordinate_scale;
83
84   box_top = ch->data_bottom + (hinge[2] - ch->y_min ) * ch->ordinate_scale;
85
86   bottom_whisker = ch->data_bottom + (whisker[0] - ch->y_min) *
87     ch->ordinate_scale;
88
89   top_whisker = ch->data_bottom + (whisker[1] - ch->y_min) * ch->ordinate_scale;
90
91   pl_savestate_r(ch->lp);
92
93   /* Draw the box */
94   pl_savestate_r (ch->lp);
95   pl_fillcolorname_r (ch->lp, ch->fill_colour);
96   pl_filltype_r (ch->lp,1);
97   pl_fbox_r (ch->lp,
98             box_left,
99             box_bottom,
100             box_right,
101             box_top);
102
103   pl_restorestate_r (ch->lp);
104
105   /* Draw the median */
106   pl_savestate_r (ch->lp);
107   pl_linewidth_r (ch->lp, 5);
108   pl_fline_r (ch->lp,
109              box_left,
110              ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale,
111              box_right,
112              ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale);
113   pl_restorestate_r (ch->lp);
114
115   /* Draw the bottom whisker */
116   pl_fline_r (ch->lp,
117              box_left,
118              bottom_whisker,
119              box_right,
120              bottom_whisker);
121
122   /* Draw top whisker */
123   pl_fline_r (ch->lp,
124              box_left,
125              top_whisker,
126              box_right,
127              top_whisker);
128
129
130   /* Draw centre line.
131      (bottom half) */
132   pl_fline_r (ch->lp,
133              box_centre, bottom_whisker,
134              box_centre, box_bottom);
135
136   /* (top half) */
137   pl_fline_r (ch->lp,
138              box_centre, top_whisker,
139              box_centre, box_top);
140
141   outliers = box_whisker_outliers (bw);
142   for (ll = ll_head (outliers);
143        ll != ll_null (outliers); ll = ll_next (ll))
144     {
145       const struct outlier *outlier = ll_data (ll, struct outlier, ll);
146       draw_case (ch, box_centre, outlier);
147     }
148
149   /* Draw  tick  mark on x axis */
150   draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, name);
151
152   pl_restorestate_r(ch->lp);
153 }
154
155 void
156 boxplot_draw_yscale (struct chart *ch, double y_max, double y_min)
157 {
158   double y_tick;
159   double d;
160
161   if ( !ch )
162      return ;
163
164   ch->y_max  = y_max;
165   ch->y_min  = y_min;
166
167   y_tick = chart_rounded_tick (fabs(ch->y_max - ch->y_min) / 5.0);
168
169   ch->y_min = (ceil( ch->y_min  / y_tick ) - 1.0  ) * y_tick;
170
171   ch->y_max = ( floor( ch->y_max  / y_tick ) + 1.0  ) * y_tick;
172
173   ch->ordinate_scale = fabs(ch->data_top - ch->data_bottom)
174     / fabs(ch->y_max - ch->y_min) ;
175
176   /* Move to data bottom-left */
177   pl_move_r(ch->lp,
178             ch->data_left, ch->data_bottom);
179
180   for ( d = ch->y_min; d <= ch->y_max ; d += y_tick )
181     {
182       draw_tick (ch, TICK_ORDINATE, (d - ch->y_min ) * ch->ordinate_scale, "%g", d);
183     }
184 }