chart: Fix format string issues in chart_write_title callers.
[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 <float.h>
21 #include <assert.h>
22 #include <gsl/gsl_math.h>
23 #include <math.h>
24 #include <stdio.h>
25
26
27 #include <output/charts/piechart.h>
28 #include <output/charts/plot-chart.h>
29
30 #include <output/chart.h>
31 #include <libpspp/str.h>
32 #include <data/value-labels.h>
33
34 #include "minmax.h"
35
36 /* Draw a single slice of the pie */
37 static void
38 draw_segment(struct chart *ch,
39              double centre_x, double centre_y,
40              double radius,
41              double start_angle, double segment_angle,
42              const char *colour) ;
43
44
45
46 /* Draw a piechart */
47 void
48 piechart_plot(const char *title, const struct slice *slices, int n_slices)
49 {
50   int i;
51   double total_magnitude=0;
52
53   struct chart *ch = chart_create();
54
55   const double left_label = ch->data_left +
56     (ch->data_right - ch->data_left)/10.0;
57
58   const double right_label = ch->data_right -
59     (ch->data_right - ch->data_left)/10.0;
60
61   const double centre_x = (ch->data_right + ch->data_left ) / 2.0 ;
62   const double centre_y = (ch->data_top + ch->data_bottom ) / 2.0 ;
63
64   const double radius = MIN(
65                             5.0 / 12.0 * (ch->data_top - ch->data_bottom),
66                             1.0 / 4.0 * (ch->data_right - ch->data_left)
67                             );
68
69   double angle;
70
71
72   chart_write_title(ch, "%s", title);
73
74   for (i = 0 ; i < n_slices ; ++i )
75     total_magnitude += slices[i].magnitude;
76
77   angle = 0.0;
78   for (i = 0 ; i < n_slices ; ++i )
79     {
80       const double segment_angle =
81         slices[i].magnitude / total_magnitude * 2 * M_PI ;
82
83       const double label_x = centre_x -
84         radius * sin(angle + segment_angle/2.0);
85
86       const double label_y = centre_y +
87         radius * cos(angle + segment_angle/2.0);
88
89       /* Fill the segment */
90       draw_segment(ch,
91                    centre_x, centre_y, radius,
92                    angle, segment_angle,
93                    data_colour[i % N_CHART_COLOURS]);
94
95       /* Now add the labels */
96       if ( label_x < centre_x )
97         {
98           pl_line_r(ch->lp, label_x, label_y,
99                     left_label, label_y );
100           pl_moverel_r(ch->lp,0,5);
101           pl_alabel_r (ch->lp, 0, 0, ds_cstr (&slices[i].label));
102         }
103       else
104         {
105           pl_line_r(ch->lp,
106                     label_x, label_y,
107                     right_label, label_y
108                     );
109           pl_moverel_r(ch->lp,0,5);
110           pl_alabel_r (ch->lp, 'r', 0, ds_cstr (&slices[i].label));
111         }
112
113       angle += segment_angle;
114
115     }
116
117   /* Draw an outline to the pie */
118   pl_filltype_r(ch->lp,0);
119   pl_fcircle_r (ch->lp, centre_x, centre_y, radius);
120
121   chart_submit(ch);
122 }
123
124 static void
125 fill_segment(struct chart *ch,
126              double x0, double y0,
127              double radius,
128              double start_angle, double segment_angle) ;
129
130
131 /* Fill a segment with the current fill colour */
132 static void
133 fill_segment(struct chart *ch,
134              double x0, double y0,
135              double radius,
136              double start_angle, double segment_angle)
137 {
138
139   const double start_x  = x0 - radius * sin(start_angle);
140   const double start_y  = y0 + radius * cos(start_angle);
141
142   const double stop_x   =
143     x0 - radius * sin(start_angle + segment_angle);
144
145   const double stop_y   =
146     y0 + radius * cos(start_angle + segment_angle);
147
148   assert(segment_angle <= 2 * M_PI);
149   assert(segment_angle >= 0);
150
151   if ( segment_angle > M_PI )
152     {
153       /* Then we must draw it in two halves */
154       fill_segment(ch, x0, y0, radius, start_angle, segment_angle / 2.0 );
155       fill_segment(ch, x0, y0, radius, start_angle + segment_angle / 2.0,
156                    segment_angle / 2.0 );
157     }
158   else
159     {
160       pl_move_r(ch->lp, x0, y0);
161
162       pl_cont_r(ch->lp, stop_x, stop_y);
163       pl_cont_r(ch->lp, start_x, start_y);
164
165       pl_arc_r(ch->lp,
166                x0, y0,
167                stop_x, stop_y,
168                start_x, start_y
169                );
170
171       pl_endpath_r(ch->lp);
172     }
173 }
174
175
176
177 /* Draw a single slice of the pie */
178 static void
179 draw_segment(struct chart *ch,
180              double x0, double y0,
181              double radius,
182              double start_angle, double segment_angle,
183              const char *colour)
184 {
185   const double start_x  = x0 - radius * sin(start_angle);
186   const double start_y  = y0 + radius * cos(start_angle);
187
188   pl_savestate_r(ch->lp);
189
190   pl_savestate_r(ch->lp);
191   pl_colorname_r(ch->lp, colour);
192
193   pl_pentype_r(ch->lp,1);
194   pl_filltype_r(ch->lp,1);
195
196   fill_segment(ch, x0, y0, radius, start_angle, segment_angle);
197   pl_restorestate_r(ch->lp);
198
199   /* Draw line dividing segments */
200   pl_pentype_r(ch->lp, 1);
201   pl_fline_r(ch->lp, x0, y0, start_x, start_y);
202
203
204   pl_restorestate_r(ch->lp);
205 }
206