935c6eb0fed9784024c54e571e53936e07c09c7b
[pspp-builds.git] / src / output / charts / piechart.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2004, 2009 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 <output/charts/piechart.h>
21
22 #include <assert.h>
23 #include <float.h>
24 #include <gsl/gsl_math.h>
25 #include <math.h>
26 #include <stdio.h>
27
28 #include <data/value-labels.h>
29 #include <libpspp/cast.h>
30 #include <libpspp/str.h>
31 #include <output/charts/plot-chart.h>
32 #include <output/chart-provider.h>
33
34 #include "minmax.h"
35
36 struct piechart
37   {
38     struct chart chart;
39     char *title;
40     struct slice *slices;
41     int n_slices;
42   };
43
44 static const struct chart_class piechart_class;
45
46 /* Draw a single slice of the pie */
47 static void
48 draw_segment(cairo_t *,
49              double centre_x, double centre_y,
50              double radius,
51              double start_angle, double segment_angle,
52              const struct chart_colour *) ;
53
54
55
56 /* Creates and returns a chart that will render a piechart with
57    the given TITLE and the N_SLICES described in SLICES. */
58 struct chart *
59 piechart_create (const char *title, const struct slice *slices, int n_slices)
60 {
61   struct piechart *pie;
62   int i;
63
64   pie = xmalloc (sizeof *pie);
65   chart_init (&pie->chart, &piechart_class);
66   pie->title = xstrdup (title);
67   pie->slices = xnmalloc (n_slices, sizeof *pie->slices);
68   for (i = 0; i < n_slices; i++)
69     {
70       const struct slice *src = &slices[i];
71       struct slice *dst = &pie->slices[i];
72
73       ds_init_string (&dst->label, &src->label);
74       dst->magnitude = src->magnitude;
75     }
76   pie->n_slices = n_slices;
77   return &pie->chart;
78 }
79
80 static void
81 piechart_draw (const struct chart *chart, cairo_t *cr,
82                struct chart_geometry *geom)
83 {
84   const struct piechart *pie = UP_CAST (chart, struct piechart, chart);
85   double total_magnitude;
86   double left_label, right_label;
87   double centre_x, centre_y;
88   double radius;
89   double angle;
90   int i;
91
92   centre_x = (geom->data_right + geom->data_left) / 2.0 ;
93   centre_y = (geom->data_top + geom->data_bottom) / 2.0 ;
94
95   left_label = geom->data_left + (geom->data_right - geom->data_left)/10.0;
96   right_label = geom->data_right - (geom->data_right - geom->data_left)/10.0;
97
98   radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
99                 1.0 / 4.0 * (geom->data_right - geom->data_left));
100
101   radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
102                 1.0 / 4.0 * (geom->data_right - geom->data_left));
103
104   chart_write_title (cr, geom, "%s", pie->title);
105
106   total_magnitude = 0.0;
107   for (i = 0; i < pie->n_slices; i++)
108     total_magnitude += pie->slices[i].magnitude;
109
110   angle = 0.0;
111   for (i = 0; i < pie->n_slices ; ++i )
112     {
113       const double segment_angle =
114         pie->slices[i].magnitude / total_magnitude * 2 * M_PI ;
115
116       const double label_x = centre_x -
117         radius * sin(angle + segment_angle/2.0);
118
119       const double label_y = centre_y +
120         radius * cos(angle + segment_angle/2.0);
121
122       /* Fill the segment */
123       draw_segment (cr,
124                     centre_x, centre_y, radius,
125                     angle, segment_angle,
126                     &data_colour[i % N_CHART_COLOURS]);
127
128       /* Now add the labels */
129       if ( label_x < centre_x )
130         {
131           cairo_move_to (cr, label_x, label_y);
132           cairo_line_to (cr, left_label, label_y);
133           cairo_stroke (cr);
134           cairo_move_to (cr, left_label, label_y + 5);
135           chart_label (cr, 'l', 'x', geom->font_size,
136                        ds_cstr (&pie->slices[i].label));
137         }
138       else
139         {
140           cairo_move_to (cr, label_x, label_y);
141           cairo_line_to (cr, right_label, label_y);
142           cairo_stroke (cr);
143           cairo_move_to (cr, right_label, label_y + 5);
144           chart_label (cr, 'r', 'x', geom->font_size,
145                        ds_cstr (&pie->slices[i].label));
146         }
147
148       angle += segment_angle;
149     }
150
151   /* Draw an outline to the pie */
152   cairo_arc (cr, centre_x, centre_y, radius, 0, 2 * M_PI);
153   cairo_stroke (cr);
154 }
155
156 /* Draw a single slice of the pie */
157 static void
158 draw_segment(cairo_t *cr,
159              double x0, double y0,
160              double radius,
161              double start_angle, double segment_angle,
162              const struct chart_colour *colour)
163 {
164   cairo_move_to (cr, x0, y0);
165   cairo_arc (cr, x0, y0, radius, start_angle, start_angle + segment_angle);
166   cairo_line_to (cr, x0, y0);
167   cairo_save (cr);
168   cairo_set_source_rgb (cr,
169                         colour->red / 255.0,
170                         colour->green / 255.0,
171                         colour->blue / 255.0);
172   cairo_fill_preserve (cr);
173   cairo_restore (cr);
174   cairo_stroke (cr);
175 }
176
177 static void
178 piechart_destroy (struct chart *chart)
179 {
180   struct piechart *pie = UP_CAST (chart, struct piechart, chart);
181   int i;
182
183   free (pie->title);
184   for (i = 0; i < pie->n_slices; i++)
185     {
186       struct slice *slice = &pie->slices[i];
187       ds_destroy (&slice->label);
188     }
189   free (pie->slices);
190   free (pie);
191 }
192
193 static const struct chart_class piechart_class =
194   {
195     piechart_draw,
196     piechart_destroy
197   };