/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <config.h>
-#include <float.h>
+#include <output/charts/piechart.h>
+
#include <assert.h>
+#include <float.h>
+#include <gsl/gsl_math.h>
#include <math.h>
#include <stdio.h>
-
-#include <output/charts/piechart.h>
-#include <output/charts/plot-chart.h>
-
-#include <output/chart.h>
-#include <libpspp/str.h>
#include <data/value-labels.h>
+#include <libpspp/str.h>
+#include <output/charts/plot-chart.h>
+#include <output/chart-provider.h>
#include "minmax.h"
+struct piechart
+ {
+ struct chart chart;
+ char *title;
+ struct slice *slices;
+ int n_slices;
+ };
-/* Pie charts of course need to know Pi :) */
-#ifndef M_PI
-#define M_PI ( 22.0 / 7.0 )
-#endif
-
-
+static const struct chart_class piechart_class;
/* Draw a single slice of the pie */
static void
-draw_segment(struct chart *ch,
+draw_segment(cairo_t *,
double centre_x, double centre_y,
double radius,
double start_angle, double segment_angle,
- const char *colour) ;
+ const struct chart_colour *) ;
-/* Draw a piechart */
-void
-piechart_plot(const char *title, const struct slice *slices, int n_slices)
+/* Creates and returns a chart that will render a piechart with
+ the given TITLE and the N_SLICES described in SLICES. */
+struct chart *
+piechart_create (const char *title, const struct slice *slices, int n_slices)
{
+ struct piechart *pie;
int i;
- double total_magnetude=0;
- struct chart *ch = chart_create();
+ pie = xmalloc (sizeof *pie);
+ chart_init (&pie->chart, &piechart_class);
+ pie->title = xstrdup (title);
+ pie->slices = xnmalloc (n_slices, sizeof *pie->slices);
+ for (i = 0; i < n_slices; i++)
+ {
+ const struct slice *src = &slices[i];
+ struct slice *dst = &pie->slices[i];
- const double left_label = ch->data_left +
- (ch->data_right - ch->data_left)/10.0;
+ ds_init_string (&dst->label, &src->label);
+ dst->magnitude = src->magnitude;
+ }
+ pie->n_slices = n_slices;
+ return &pie->chart;
+}
- const double right_label = ch->data_right -
- (ch->data_right - ch->data_left)/10.0;
+static void
+piechart_draw (const struct chart *chart, cairo_t *cr,
+ struct chart_geometry *geom)
+{
+ struct piechart *pie = (struct piechart *) chart;
+ double total_magnitude;
+ double left_label, right_label;
+ double centre_x, centre_y;
+ double radius;
+ double angle;
+ int i;
- const double centre_x = (ch->data_right + ch->data_left ) / 2.0 ;
- const double centre_y = (ch->data_top + ch->data_bottom ) / 2.0 ;
+ centre_x = (geom->data_right + geom->data_left) / 2.0 ;
+ centre_y = (geom->data_top + geom->data_bottom) / 2.0 ;
- const double radius = MIN(
- 5.0 / 12.0 * (ch->data_top - ch->data_bottom),
- 1.0 / 4.0 * (ch->data_right - ch->data_left)
- );
+ left_label = geom->data_left + (geom->data_right - geom->data_left)/10.0;
+ right_label = geom->data_right - (geom->data_right - geom->data_left)/10.0;
+ radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
+ 1.0 / 4.0 * (geom->data_right - geom->data_left));
- chart_write_title(ch, title);
+ radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
+ 1.0 / 4.0 * (geom->data_right - geom->data_left));
- for (i = 0 ; i < n_slices ; ++i )
- total_magnetude += slices[i].magnetude;
+ chart_write_title (cr, geom, "%s", pie->title);
- for (i = 0 ; i < n_slices ; ++i )
- {
- static double angle=0.0;
+ total_magnitude = 0.0;
+ for (i = 0; i < pie->n_slices; i++)
+ total_magnitude += pie->slices[i].magnitude;
+ angle = 0.0;
+ for (i = 0; i < pie->n_slices ; ++i )
+ {
const double segment_angle =
- slices[i].magnetude / total_magnetude * 2 * M_PI ;
+ pie->slices[i].magnitude / total_magnitude * 2 * M_PI ;
const double label_x = centre_x -
radius * sin(angle + segment_angle/2.0);
radius * cos(angle + segment_angle/2.0);
/* Fill the segment */
- draw_segment(ch,
- centre_x, centre_y, radius,
- angle, segment_angle,
- data_colour[i % N_CHART_COLOURS]);
+ draw_segment (cr,
+ centre_x, centre_y, radius,
+ angle, segment_angle,
+ &data_colour[i % N_CHART_COLOURS]);
/* Now add the labels */
if ( label_x < centre_x )
{
- pl_line_r(ch->lp, label_x, label_y,
- left_label, label_y );
- pl_moverel_r(ch->lp,0,5);
- pl_alabel_r (ch->lp, 0, 0, ds_cstr (&slices[i].label));
+ cairo_move_to (cr, label_x, label_y);
+ cairo_line_to (cr, left_label, label_y);
+ cairo_stroke (cr);
+ cairo_move_to (cr, left_label, label_y + 5);
+ chart_label (cr, 'l', 'x', ds_cstr (&pie->slices[i].label));
}
else
{
- pl_line_r(ch->lp,
- label_x, label_y,
- right_label, label_y
- );
- pl_moverel_r(ch->lp,0,5);
- pl_alabel_r (ch->lp, 'r', 0, ds_cstr (&slices[i].label));
+ cairo_move_to (cr, label_x, label_y);
+ cairo_line_to (cr, right_label, label_y);
+ cairo_stroke (cr);
+ cairo_move_to (cr, right_label, label_y + 5);
+ chart_label (cr, 'r', 'x', ds_cstr (&pie->slices[i].label));
}
angle += segment_angle;
-
}
/* Draw an outline to the pie */
- pl_filltype_r(ch->lp,0);
- pl_fcircle_r (ch->lp, centre_x, centre_y, radius);
-
- chart_submit(ch);
+ cairo_arc (cr, centre_x, centre_y, radius, 0, 2 * M_PI);
+ cairo_stroke (cr);
}
+/* Draw a single slice of the pie */
static void
-fill_segment(struct chart *ch,
- double x0, double y0,
- double radius,
- double start_angle, double segment_angle) ;
-
-
-/* Fill a segment with the current fill colour */
-static void
-fill_segment(struct chart *ch,
+draw_segment(cairo_t *cr,
double x0, double y0,
double radius,
- double start_angle, double segment_angle)
+ double start_angle, double segment_angle,
+ const struct chart_colour *colour)
{
-
- const double start_x = x0 - radius * sin(start_angle);
- const double start_y = y0 + radius * cos(start_angle);
-
- const double stop_x =
- x0 - radius * sin(start_angle + segment_angle);
-
- const double stop_y =
- y0 + radius * cos(start_angle + segment_angle);
-
- assert(segment_angle <= 2 * M_PI);
- assert(segment_angle >= 0);
-
- if ( segment_angle > M_PI )
- {
- /* Then we must draw it in two halves */
- fill_segment(ch, x0, y0, radius, start_angle, segment_angle / 2.0 );
- fill_segment(ch, x0, y0, radius, start_angle + segment_angle / 2.0,
- segment_angle / 2.0 );
- }
- else
- {
- pl_move_r(ch->lp, x0, y0);
-
- pl_cont_r(ch->lp, stop_x, stop_y);
- pl_cont_r(ch->lp, start_x, start_y);
-
- pl_arc_r(ch->lp,
- x0, y0,
- stop_x, stop_y,
- start_x, start_y
- );
-
- pl_endpath_r(ch->lp);
- }
+ cairo_move_to (cr, x0, y0);
+ cairo_arc (cr, x0, y0, radius, start_angle, start_angle + segment_angle);
+ cairo_line_to (cr, x0, y0);
+ cairo_save (cr);
+ cairo_set_source_rgb (cr,
+ colour->red / 255.0,
+ colour->green / 255.0,
+ colour->blue / 255.0);
+ cairo_fill_preserve (cr);
+ cairo_restore (cr);
+ cairo_stroke (cr);
}
-
-
-/* Draw a single slice of the pie */
static void
-draw_segment(struct chart *ch,
- double x0, double y0,
- double radius,
- double start_angle, double segment_angle,
- const char *colour)
+piechart_destroy (struct chart *chart)
{
- const double start_x = x0 - radius * sin(start_angle);
- const double start_y = y0 + radius * cos(start_angle);
-
- pl_savestate_r(ch->lp);
-
- pl_savestate_r(ch->lp);
- pl_colorname_r(ch->lp, colour);
-
- pl_pentype_r(ch->lp,1);
- pl_filltype_r(ch->lp,1);
-
- fill_segment(ch, x0, y0, radius, start_angle, segment_angle);
- pl_restorestate_r(ch->lp);
-
- /* Draw line dividing segments */
- pl_pentype_r(ch->lp, 1);
- pl_fline_r(ch->lp, x0, y0, start_x, start_y);
-
+ struct piechart *pie = (struct piechart *) chart;
+ int i;
- pl_restorestate_r(ch->lp);
+ free (pie->title);
+ for (i = 0; i < pie->n_slices; i++)
+ {
+ struct slice *slice = &pie->slices[i];
+ ds_destroy (&slice->label);
+ }
+ free (pie->slices);
+ free (pie);
}
+static const struct chart_class piechart_class =
+ {
+ piechart_draw,
+ piechart_destroy
+ };