3fceaf20f5b016bec4d798cdcd298206e87ebd2b
[pspp-builds.git] / src / output / charts / piechart.c
1 /* PSPP - draws pie charts of sample statistics
2
3 Copyright (C) 2004 Free Software Foundation, Inc.
4 Written by John Darrington <john@darrington.wattle.id.au>
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA. */
20
21
22 #include "config.h"
23 #include "piechart.h"
24 #include "chart.h"
25 #include <float.h>
26 #include <assert.h>
27 #include <math.h>
28 #include <stdio.h>
29 #include "str.h"
30 #include "value-labels.h"
31 #include "misc.h"
32 #include "plot-chart.h"
33
34 /* Pie charts of course need to know Pi :) */
35 #ifndef M_PI
36 #define M_PI ( 22.0 / 7.0 ) 
37 #endif
38
39
40
41 /* Draw a single slice of the pie */
42 static void
43 draw_segment(struct chart *ch, 
44              double centre_x, double centre_y, 
45              double radius,
46              double start_angle, double segment_angle,
47              const char *colour) ;
48
49
50
51 /* Draw a piechart */
52 void
53 piechart_plot(const char *title, const struct slice *slices, int n_slices)
54 {
55   int i;
56   double total_magnetude=0;
57
58   struct chart *ch = chart_create();
59
60   const double left_label = ch->data_left + 
61     (ch->data_right - ch->data_left)/10.0;
62
63   const double right_label = ch->data_right - 
64     (ch->data_right - ch->data_left)/10.0;
65
66   const double centre_x = (ch->data_right + ch->data_left ) / 2.0 ;
67   const double centre_y = (ch->data_top + ch->data_bottom ) / 2.0 ;
68
69   const double radius = min( 
70                             5.0 / 12.0 * (ch->data_top - ch->data_bottom),
71                             1.0 / 4.0 * (ch->data_right - ch->data_left)
72                             );
73
74
75   chart_write_title(ch, title);
76
77   for (i = 0 ; i < n_slices ; ++i ) 
78     total_magnetude += slices[i].magnetude;
79
80   for (i = 0 ; i < n_slices ; ++i ) 
81     {
82       static double angle=0.0;
83
84       const double segment_angle = 
85         slices[i].magnetude / total_magnetude * 2 * M_PI ;
86
87       const double label_x = centre_x - 
88         radius * sin(angle + segment_angle/2.0);
89
90       const double label_y = centre_y + 
91         radius * cos(angle + segment_angle/2.0);
92
93       /* Fill the segment */
94       draw_segment(ch,
95                    centre_x, centre_y, radius, 
96                    angle, segment_angle,
97                    data_colour[i]);
98         
99       /* Now add the labels */
100       if ( label_x < centre_x ) 
101         {
102           pl_line_r(ch->lp, label_x, label_y,
103                     left_label, label_y );
104           pl_moverel_r(ch->lp,0,5);
105           pl_alabel_r(ch->lp,0,0,slices[i].label);
106         }
107       else
108         {
109           pl_line_r(ch->lp, 
110                     label_x, label_y,
111                     right_label, label_y
112                     );
113           pl_moverel_r(ch->lp,0,5);
114           pl_alabel_r(ch->lp,'r',0,slices[i].label);
115         }
116
117       angle += segment_angle;
118
119     }
120
121   /* Draw an outline to the pie */
122   pl_filltype_r(ch->lp,0);
123   pl_fcircle_r (ch->lp, centre_x, centre_y, radius);
124
125   chart_submit(ch);
126 }
127
128 static void
129 fill_segment(struct chart *ch, 
130              double x0, double y0, 
131              double radius,
132              double start_angle, double segment_angle) ;
133
134
135 /* Fill a segment with the current fill colour */
136 static void
137 fill_segment(struct chart *ch, 
138              double x0, double y0, 
139              double radius,
140              double start_angle, double segment_angle)
141 {
142
143   const double start_x  = x0 - radius * sin(start_angle);
144   const double start_y  = y0 + radius * cos(start_angle);
145
146   const double stop_x   = 
147     x0 - radius * sin(start_angle + segment_angle); 
148
149   const double stop_y   = 
150     y0 + radius * cos(start_angle + segment_angle);
151
152   assert(segment_angle <= 2 * M_PI);
153   assert(segment_angle >= 0);
154
155   if ( segment_angle > M_PI ) 
156     {
157       /* Then we must draw it in two halves */
158       fill_segment(ch, x0, y0, radius, start_angle, segment_angle / 2.0 );
159       fill_segment(ch, x0, y0, radius, start_angle + segment_angle / 2.0,
160                    segment_angle / 2.0 );
161     }
162   else
163     {
164       pl_move_r(ch->lp, x0, y0);
165
166       pl_cont_r(ch->lp, stop_x, stop_y);
167       pl_cont_r(ch->lp, start_x, start_y);
168
169       pl_arc_r(ch->lp,
170                x0, y0,
171                stop_x, stop_y,
172                start_x, start_y
173                );
174
175       pl_endpath_r(ch->lp);
176     }
177 }
178
179
180
181 /* Draw a single slice of the pie */
182 static void
183 draw_segment(struct chart *ch, 
184              double x0, double y0, 
185              double radius,
186              double start_angle, double segment_angle, 
187              const char *colour)
188 {
189   const double start_x  = x0 - radius * sin(start_angle);
190   const double start_y  = y0 + radius * cos(start_angle);
191
192   pl_savestate_r(ch->lp);
193
194   pl_savestate_r(ch->lp);
195   pl_colorname_r(ch->lp, colour);
196   
197   pl_pentype_r(ch->lp,1);
198   pl_filltype_r(ch->lp,1);
199
200   fill_segment(ch, x0, y0, radius, start_angle, segment_angle);
201   pl_restorestate_r(ch->lp);
202
203   /* Draw line dividing segments */
204   pl_pentype_r(ch->lp, 1);
205   pl_fline_r(ch->lp, x0, y0, start_x, start_y);
206         
207
208   pl_restorestate_r(ch->lp);
209 }
210