Complete re-implementation of the EXAMINE command.
[pspp-builds.git] / 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
67   const struct ll_list *outliers;
68
69   const double box_left = box_centre - box_width / 2.0;
70
71   const double box_right = box_centre + box_width / 2.0;
72
73   double box_bottom ;
74   double box_top ;
75   double bottom_whisker ;
76   double top_whisker ;
77
78   box_whisker_whiskers (bw, whisker);
79   box_whisker_hinges (bw, hinge);
80
81   box_bottom = ch->data_bottom + (hinge[0] - ch->y_min ) * ch->ordinate_scale;
82
83   box_top = ch->data_bottom + (hinge[2] - ch->y_min ) * ch->ordinate_scale;
84
85   bottom_whisker = ch->data_bottom + (whisker[0] - ch->y_min) *
86     ch->ordinate_scale;
87
88   top_whisker = ch->data_bottom + (whisker[1] - ch->y_min) * ch->ordinate_scale;
89
90   pl_savestate_r(ch->lp);
91
92   /* Draw the box */
93   pl_savestate_r (ch->lp);
94   pl_fillcolorname_r (ch->lp, ch->fill_colour);
95   pl_filltype_r (ch->lp,1);
96   pl_fbox_r (ch->lp,
97             box_left,
98             box_bottom,
99             box_right,
100             box_top);
101
102   pl_restorestate_r (ch->lp);
103
104   /* Draw the median */
105   pl_savestate_r (ch->lp);
106   pl_linewidth_r (ch->lp, 5);
107   pl_fline_r (ch->lp,
108              box_left,
109              ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale,
110              box_right,
111              ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale);
112   pl_restorestate_r (ch->lp);
113
114   /* Draw the bottom whisker */
115   pl_fline_r (ch->lp,
116              box_left,
117              bottom_whisker,
118              box_right,
119              bottom_whisker);
120
121   /* Draw top whisker */
122   pl_fline_r (ch->lp,
123              box_left,
124              top_whisker,
125              box_right,
126              top_whisker);
127
128
129   /* Draw centre line.
130      (bottom half) */
131   pl_fline_r (ch->lp,
132              box_centre, bottom_whisker,
133              box_centre, box_bottom);
134
135   /* (top half) */
136   pl_fline_r (ch->lp,
137              box_centre, top_whisker,
138              box_centre, box_top);
139
140   outliers = box_whisker_outliers (bw);
141   for (struct ll *ll = ll_head (outliers);
142        ll != ll_null (outliers); ll = ll_next (ll))
143     {
144       const struct outlier *outlier = ll_data (ll, struct outlier, ll);
145       draw_case (ch, box_centre, outlier);
146     }
147
148   /* Draw  tick  mark on x axis */
149   draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, name);
150
151   pl_restorestate_r(ch->lp);
152 }
153
154 void
155 boxplot_draw_yscale (struct chart *ch, double y_max, double y_min)
156 {
157   double y_tick;
158   double d;
159
160   if ( !ch )
161      return ;
162
163   ch->y_max  = y_max;
164   ch->y_min  = y_min;
165
166   y_tick = chart_rounded_tick (fabs(ch->y_max - ch->y_min) / 5.0);
167
168   ch->y_min = (ceil( ch->y_min  / y_tick ) - 1.0  ) * y_tick;
169
170   ch->y_max = ( floor( ch->y_max  / y_tick ) + 1.0  ) * y_tick;
171
172   ch->ordinate_scale = fabs(ch->data_top - ch->data_bottom)
173     / fabs(ch->y_max - ch->y_min) ;
174
175   /* Move to data bottom-left */
176   pl_move_r(ch->lp,
177             ch->data_left, ch->data_bottom);
178
179   for ( d = ch->y_min; d <= ch->y_max ; d += y_tick )
180     {
181       draw_tick (ch, TICK_ORDINATE, (d - ch->y_min ) * ch->ordinate_scale, "%g", d);
182     }
183 }