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