1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2011 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "output/cairo-chart.h"
23 #include "math/box-whisker.h"
24 #include "math/chart-geometry.h"
25 #include "output/charts/boxplot.h"
27 /* Draw an OUTLIER on the plot CH
31 draw_case (cairo_t *cr, const struct xrchart_geometry *geom, double centreline,
32 const struct outlier *outlier)
34 double y = geom->axis[SCALE_ORDINATE].data_min + (outlier->value - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale;
35 xrchart_draw_marker (cr, centreline, y,
36 outlier->extreme ? XRMARKER_ASTERISK : XRMARKER_CIRCLE,
39 cairo_move_to (cr, centreline + 10, y);
40 xrchart_label (cr, 'l', 'c', geom->font_size, ds_cstr (&outlier->label));
44 boxplot_draw_box (cairo_t *cr, const struct xrchart_geometry *geom,
47 const struct box_whisker *bw,
54 const struct ll_list *outliers;
56 const double box_left = box_centre - box_width / 2.0;
58 const double box_right = box_centre + box_width / 2.0;
62 double bottom_whisker;
65 box_whisker_whiskers (bw, whisker);
66 box_whisker_hinges (bw, hinge);
68 box_bottom = geom->axis[SCALE_ORDINATE].data_min + (hinge[0] - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale;
70 box_top = geom->axis[SCALE_ORDINATE].data_min + (hinge[2] - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale;
72 bottom_whisker = geom->axis[SCALE_ORDINATE].data_min + (whisker[0] - geom->axis[SCALE_ORDINATE].min) *
73 geom->axis[SCALE_ORDINATE].scale;
75 top_whisker = geom->axis[SCALE_ORDINATE].data_min + (whisker[1] - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale;
82 box_top - box_bottom);
84 cairo_set_source_rgb (cr,
85 geom->fill_colour.red / 255.0,
86 geom->fill_colour.green / 255.0,
87 geom->fill_colour.blue / 255.0);
94 cairo_set_line_width (cr, cairo_get_line_width (cr) * 5);
97 geom->axis[SCALE_ORDINATE].data_min + (hinge[1] - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale);
100 geom->axis[SCALE_ORDINATE].data_min + (hinge[1] - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale);
104 /* Draw the bottom whisker */
105 cairo_move_to (cr, box_left, bottom_whisker);
106 cairo_line_to (cr, box_right, bottom_whisker);
109 /* Draw top whisker */
110 if (! isnan (top_whisker))
112 cairo_move_to (cr, box_left, top_whisker);
113 cairo_line_to (cr, box_right, top_whisker);
117 /* Draw centre line. */
118 if (! isnan (bottom_whisker) && ! isnan (box_bottom))
121 cairo_move_to (cr, box_centre, bottom_whisker);
122 cairo_line_to (cr, box_centre, box_bottom);
126 if (! isnan (top_whisker) && ! isnan (box_top))
129 cairo_move_to (cr, box_centre, top_whisker);
130 cairo_line_to (cr, box_centre, box_top);
134 outliers = box_whisker_outliers (bw);
135 for (ll = ll_head (outliers);
136 ll != ll_null (outliers); ll = ll_next (ll))
138 const struct outlier *outlier = ll_data (ll, struct outlier, ll);
139 draw_case (cr, geom, box_centre, outlier);
142 /* Draw tick mark on x axis */
143 draw_tick (cr, geom, SCALE_ABSCISSA, false,
144 box_centre - geom->axis[SCALE_ABSCISSA].data_min, "%s", name);
148 xrchart_draw_boxplot (const struct chart *chart, cairo_t *cr,
149 struct xrchart_geometry *geom)
151 const struct boxplot *boxplot = to_boxplot (chart);
155 if (! xrchart_write_yscale (cr, geom, boxplot->y_min, boxplot->y_max))
158 xrchart_write_title (cr, geom, "%s", chart->title);
160 box_width = (geom->axis[SCALE_ABSCISSA].data_max - geom->axis[SCALE_ABSCISSA].data_min) / boxplot->n_boxes / 2.0;
161 for (i = 0; i < boxplot->n_boxes; i++)
163 const struct boxplot_box *box = &boxplot->boxes[i];
164 const double box_centre = (i * 2 + 1) * box_width + geom->axis[SCALE_ABSCISSA].data_min;
165 boxplot_draw_box (cr, geom, box_centre, box_width, box->bw, box->label);