1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2004, 2009 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/>. */
20 #include <output/charts/piechart.h>
24 #include <gsl/gsl_math.h>
28 #include <data/value-labels.h>
29 #include <libpspp/str.h>
30 #include <output/charts/plot-chart.h>
31 #include <output/chart-provider.h>
43 static const struct chart_class piechart_class;
45 /* Draw a single slice of the pie */
47 draw_segment(plPlotter *,
48 double centre_x, double centre_y,
50 double start_angle, double segment_angle,
51 const struct chart_colour *) ;
55 /* Creates and returns a chart that will render a piechart with
56 the given TITLE and the N_SLICES described in SLICES. */
58 piechart_create (const char *title, const struct slice *slices, int n_slices)
63 pie = xmalloc (sizeof *pie);
64 chart_init (&pie->chart, &piechart_class);
65 pie->title = xstrdup (title);
66 pie->slices = xnmalloc (n_slices, sizeof *pie->slices);
67 for (i = 0; i < n_slices; i++)
69 const struct slice *src = &slices[i];
70 struct slice *dst = &pie->slices[i];
72 ds_init_string (&dst->label, &src->label);
73 dst->magnitude = src->magnitude;
75 pie->n_slices = n_slices;
80 piechart_draw (const struct chart *chart, plPlotter *lp,
81 struct chart_geometry *geom)
83 struct piechart *pie = (struct piechart *) chart;
84 double total_magnitude;
85 double left_label, right_label;
86 double centre_x, centre_y;
91 left_label = geom->data_left + (geom->data_right - geom->data_left)/10.0;
92 right_label = geom->data_right - (geom->data_right - geom->data_left)/10.0;
94 centre_x = (geom->data_right + geom->data_left) / 2.0 ;
95 centre_y = (geom->data_top + geom->data_bottom) / 2.0 ;
97 radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
98 1.0 / 4.0 * (geom->data_right - geom->data_left));
100 chart_write_title (lp, geom, "%s", pie->title);
102 total_magnitude = 0.0;
103 for (i = 0; i < pie->n_slices; i++)
104 total_magnitude += pie->slices[i].magnitude;
107 for (i = 0; i < pie->n_slices ; ++i )
109 const double segment_angle =
110 pie->slices[i].magnitude / total_magnitude * 2 * M_PI ;
112 const double label_x = centre_x -
113 radius * sin(angle + segment_angle/2.0);
115 const double label_y = centre_y +
116 radius * cos(angle + segment_angle/2.0);
118 /* Fill the segment */
120 centre_x, centre_y, radius,
121 angle, segment_angle,
122 &data_colour[i % N_CHART_COLOURS]);
124 /* Now add the labels */
125 if ( label_x < centre_x )
127 pl_line_r (lp, label_x, label_y, left_label, label_y );
128 pl_moverel_r (lp, 0, 5);
129 pl_alabel_r (lp, 0, 0, ds_cstr (&pie->slices[i].label));
133 pl_line_r (lp, label_x, label_y, right_label, label_y);
134 pl_moverel_r (lp, 0, 5);
135 pl_alabel_r (lp, 'r', 0, ds_cstr (&pie->slices[i].label));
138 angle += segment_angle;
141 /* Draw an outline to the pie */
142 pl_filltype_r (lp,0);
143 pl_fcircle_r (lp, centre_x, centre_y, radius);
146 /* Fill a segment with the current fill colour */
148 fill_segment(plPlotter *lp,
149 double x0, double y0,
151 double start_angle, double segment_angle)
154 const double start_x = x0 - radius * sin(start_angle);
155 const double start_y = y0 + radius * cos(start_angle);
157 const double stop_x =
158 x0 - radius * sin(start_angle + segment_angle);
160 const double stop_y =
161 y0 + radius * cos(start_angle + segment_angle);
163 assert(segment_angle <= 2 * M_PI);
164 assert(segment_angle >= 0);
166 if ( segment_angle > M_PI )
168 /* Then we must draw it in two halves */
169 fill_segment(lp, x0, y0, radius, start_angle, segment_angle / 2.0 );
170 fill_segment(lp, x0, y0, radius, start_angle + segment_angle / 2.0,
171 segment_angle / 2.0 );
175 pl_move_r(lp, x0, y0);
177 pl_cont_r(lp, stop_x, stop_y);
178 pl_cont_r(lp, start_x, start_y);
190 /* Draw a single slice of the pie */
192 draw_segment(plPlotter *lp,
193 double x0, double y0,
195 double start_angle, double segment_angle,
196 const struct chart_colour *colour)
198 const double start_x = x0 - radius * sin(start_angle);
199 const double start_y = y0 + radius * cos(start_angle);
204 pl_color_r(lp, colour->red * 257, colour->green * 257, colour->blue * 257);
209 fill_segment(lp, x0, y0, radius, start_angle, segment_angle);
210 pl_restorestate_r(lp);
212 /* Draw line dividing segments */
214 pl_fline_r(lp, x0, y0, start_x, start_y);
217 pl_restorestate_r(lp);
221 piechart_destroy (struct chart *chart)
223 struct piechart *pie = (struct piechart *) chart;
227 for (i = 0; i < pie->n_slices; i++)
229 struct slice *slice = &pie->slices[i];
230 ds_destroy (&slice->label);
236 static const struct chart_class piechart_class =