X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fcharts%2Fbox-whisker.c;h=ea1b4624516e9be0304d871e3deed1cd903d8247;hb=e9fc76ec1091b47b92aeb2143d6159dacf3b70b2;hp=33c445b09a61b0ef36714272bd221bfd2cd9f741;hpb=9bc7fd4e5d03e4960960d9b16339a680d6f9ae06;p=pspp-builds.git diff --git a/src/output/charts/box-whisker.c b/src/output/charts/box-whisker.c index 33c445b0..ea1b4624 100644 --- a/src/output/charts/box-whisker.c +++ b/src/output/charts/box-whisker.c @@ -17,49 +17,94 @@ #include +#include + #include #include -#include - -#include -#include +#include -#include +#include #include #include +#include +#include +#include /* Draw a box-and-whiskers plot */ +struct box + { + struct box_whisker *bw; + char *label; + }; + +struct boxplot + { + struct chart chart; + double y_min; + double y_max; + char *title; + struct box *boxes; + size_t n_boxes, boxes_allocated; + }; + +static const struct chart_class boxplot_chart_class; + +struct boxplot * +boxplot_create (double y_min, double y_max, const char *title) +{ + struct boxplot *boxplot = xmalloc (sizeof *boxplot); + chart_init (&boxplot->chart, &boxplot_chart_class); + boxplot->y_min = y_min; + boxplot->y_max = y_max; + boxplot->title = xstrdup (title); + boxplot->boxes = NULL; + boxplot->n_boxes = boxplot->boxes_allocated = 0; + return boxplot; +} + +void +boxplot_add_box (struct boxplot *boxplot, + struct box_whisker *bw, const char *label) +{ + struct box *box; + if (boxplot->n_boxes >= boxplot->boxes_allocated) + boxplot->boxes = x2nrealloc (boxplot->boxes, &boxplot->boxes_allocated, + sizeof *boxplot->boxes); + box = &boxplot->boxes[boxplot->n_boxes++]; + box->bw = bw; + box->label = xstrdup (label); +} + +struct chart * +boxplot_get_chart (struct boxplot *boxplot) +{ + return &boxplot->chart; +} + /* Draw an OUTLIER on the plot CH * at CENTRELINE */ static void -draw_case (struct chart *ch, double centreline, +draw_case (cairo_t *cr, const struct chart_geometry *geom, double centreline, const struct outlier *outlier) { + double y = geom->data_bottom + (outlier->value - geom->y_min) * geom->ordinate_scale; + chart_draw_marker (cr, centreline, y, + outlier->extreme ? MARKER_ASTERISK : MARKER_CIRCLE, + 20); -#define MARKER_CIRCLE 4 -#define MARKER_STAR 3 - - pl_fmarker_r(ch->lp, - centreline, - ch->data_bottom + (outlier->value - ch->y_min) * ch->ordinate_scale, - outlier->extreme ? MARKER_STAR : MARKER_CIRCLE, - 20); - - pl_moverel_r(ch->lp, 10,0); - - pl_alabel_r(ch->lp, 'l', 'c', ds_cstr (&outlier->label)); + cairo_move_to (cr, centreline + 10, y); + chart_label (cr, 'l', 'c', ds_cstr (&outlier->label)); } - -void -boxplot_draw_boxplot (struct chart *ch, - double box_centre, - double box_width, - const struct box_whisker *bw, - const char *name) +static void +boxplot_draw_box (cairo_t *cr, const struct chart_geometry *geom, + double box_centre, + double box_width, + const struct box_whisker *bw, + const char *name) { double whisker[2]; double hinge[3]; @@ -79,106 +124,139 @@ boxplot_draw_boxplot (struct chart *ch, box_whisker_whiskers (bw, whisker); box_whisker_hinges (bw, hinge); - box_bottom = ch->data_bottom + (hinge[0] - ch->y_min ) * ch->ordinate_scale; - - box_top = ch->data_bottom + (hinge[2] - ch->y_min ) * ch->ordinate_scale; + box_bottom = geom->data_bottom + (hinge[0] - geom->y_min ) * geom->ordinate_scale; - bottom_whisker = ch->data_bottom + (whisker[0] - ch->y_min) * - ch->ordinate_scale; + box_top = geom->data_bottom + (hinge[2] - geom->y_min ) * geom->ordinate_scale; - top_whisker = ch->data_bottom + (whisker[1] - ch->y_min) * ch->ordinate_scale; + bottom_whisker = geom->data_bottom + (whisker[0] - geom->y_min) * + geom->ordinate_scale; - pl_savestate_r(ch->lp); + top_whisker = geom->data_bottom + (whisker[1] - geom->y_min) * geom->ordinate_scale; /* Draw the box */ - pl_savestate_r (ch->lp); - pl_fillcolorname_r (ch->lp, ch->fill_colour); - pl_filltype_r (ch->lp,1); - pl_fbox_r (ch->lp, - box_left, - box_bottom, - box_right, - box_top); - - pl_restorestate_r (ch->lp); + cairo_rectangle (cr, + box_left, + box_bottom, + box_right - box_left, + box_top - box_bottom); + cairo_save (cr); + cairo_set_source_rgb (cr, + geom->fill_colour.red / 255.0, + geom->fill_colour.green / 255.0, + geom->fill_colour.blue / 255.0); + cairo_fill (cr); + cairo_restore (cr); + cairo_stroke (cr); /* Draw the median */ - pl_savestate_r (ch->lp); - pl_linewidth_r (ch->lp, 5); - pl_fline_r (ch->lp, - box_left, - ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale, - box_right, - ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale); - pl_restorestate_r (ch->lp); + cairo_save (cr); + cairo_set_line_width (cr, cairo_get_line_width (cr) * 5); + cairo_move_to (cr, + box_left, + geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale); + cairo_line_to (cr, + box_right, + geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale); + cairo_stroke (cr); + cairo_restore (cr); /* Draw the bottom whisker */ - pl_fline_r (ch->lp, - box_left, - bottom_whisker, - box_right, - bottom_whisker); + cairo_move_to (cr, box_left, bottom_whisker); + cairo_line_to (cr, box_right, bottom_whisker); + cairo_stroke (cr); /* Draw top whisker */ - pl_fline_r (ch->lp, - box_left, - top_whisker, - box_right, - top_whisker); - + cairo_move_to (cr, box_left, top_whisker); + cairo_line_to (cr, box_right, top_whisker); + cairo_stroke (cr); /* Draw centre line. (bottom half) */ - pl_fline_r (ch->lp, - box_centre, bottom_whisker, - box_centre, box_bottom); + cairo_move_to (cr, box_centre, bottom_whisker); + cairo_line_to (cr, box_centre, box_bottom); + cairo_stroke (cr); /* (top half) */ - pl_fline_r (ch->lp, - box_centre, top_whisker, - box_centre, box_top); + cairo_move_to (cr, box_centre, top_whisker); + cairo_line_to (cr, box_centre, box_top); + cairo_stroke (cr); outliers = box_whisker_outliers (bw); for (ll = ll_head (outliers); ll != ll_null (outliers); ll = ll_next (ll)) { const struct outlier *outlier = ll_data (ll, struct outlier, ll); - draw_case (ch, box_centre, outlier); + draw_case (cr, geom, box_centre, outlier); } /* Draw tick mark on x axis */ - draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, "%s", name); - - pl_restorestate_r(ch->lp); + draw_tick(cr, geom, TICK_ABSCISSA, box_centre - geom->data_left, "%s", name); } -void -boxplot_draw_yscale (struct chart *ch, double y_max, double y_min) +static void +boxplot_draw_yscale (cairo_t *cr, struct chart_geometry *geom, + double y_max, double y_min) { double y_tick; double d; - if ( !ch ) - return ; + geom->y_max = y_max; + geom->y_min = y_min; + + y_tick = chart_rounded_tick (fabs (geom->y_max - geom->y_min) / 5.0); - ch->y_max = y_max; - ch->y_min = y_min; + geom->y_min = (ceil (geom->y_min / y_tick) - 1.0) * y_tick; - y_tick = chart_rounded_tick (fabs(ch->y_max - ch->y_min) / 5.0); + geom->y_max = (floor (geom->y_max / y_tick) + 1.0) * y_tick; - ch->y_min = (ceil( ch->y_min / y_tick ) - 1.0 ) * y_tick; + geom->ordinate_scale = (fabs (geom->data_top - geom->data_bottom) + / fabs (geom->y_max - geom->y_min)); - ch->y_max = ( floor( ch->y_max / y_tick ) + 1.0 ) * y_tick; + for (d = geom->y_min; d <= geom->y_max; d += y_tick) + draw_tick (cr, geom, TICK_ORDINATE, + (d - geom->y_min) * geom->ordinate_scale, "%g", d); +} - ch->ordinate_scale = fabs(ch->data_top - ch->data_bottom) - / fabs(ch->y_max - ch->y_min) ; +static void +boxplot_chart_draw (const struct chart *chart, cairo_t *cr, + struct chart_geometry *geom) +{ + const struct boxplot *boxplot = (struct boxplot *) chart; + double box_width; + size_t i; - /* Move to data bottom-left */ - pl_move_r(ch->lp, - ch->data_left, ch->data_bottom); + boxplot_draw_yscale (cr, geom, boxplot->y_max, boxplot->y_min); + chart_write_title (cr, geom, "%s", boxplot->title); - for ( d = ch->y_min; d <= ch->y_max ; d += y_tick ) + box_width = (geom->data_right - geom->data_left) / boxplot->n_boxes / 2.0; + for (i = 0; i < boxplot->n_boxes; i++) { - draw_tick (ch, TICK_ORDINATE, (d - ch->y_min ) * ch->ordinate_scale, "%g", d); + const struct box *box = &boxplot->boxes[i]; + const double box_centre = (i * 2 + 1) * box_width + geom->data_left; + boxplot_draw_box (cr, geom, box_centre, box_width, box->bw, box->label); } } + +static void +boxplot_chart_destroy (struct chart *chart) +{ + struct boxplot *boxplot = (struct boxplot *) chart; + size_t i; + + free (boxplot->title); + for (i = 0; i < boxplot->n_boxes; i++) + { + struct box *box = &boxplot->boxes[i]; + struct statistic *statistic = &box->bw->parent.parent; + statistic->destroy (statistic); + free (box->label); + } + free (boxplot->boxes); + free (boxplot); +} + +static const struct chart_class boxplot_chart_class = + { + boxplot_chart_draw, + boxplot_chart_destroy + };