Added framework for charts
authorJohn Darrington <john@darrington.wattle.id.au>
Fri, 29 Oct 2004 06:02:44 +0000 (06:02 +0000)
committerJohn Darrington <john@darrington.wattle.id.au>
Fri, 29 Oct 2004 06:02:44 +0000 (06:02 +0000)
src/Makefile.am
src/barchart.c [new file with mode: 0644]
src/box-whisker.c [new file with mode: 0644]
src/cartesian.c [new file with mode: 0644]
src/chart.c [new file with mode: 0644]
src/chart.h [new file with mode: 0644]
src/frequencies.q
src/histogram.c [new file with mode: 0644]
src/piechart.c [new file with mode: 0644]

index c4ad149a94274cc2a09f4c31d1340ffb382b2329..3a85747427c17cb3c4ae795112f771fb361c6f43 100644 (file)
@@ -71,10 +71,18 @@ split-file.c str.c str.h subclist.c subclist.h \
 sysfile-info.c tab.c tab.h temporary.c stat.h \
 title.c  val.h val-labs.c value-labels.c value-labels.h                \
 var-labs.c var.h vars-atr.c vars-prs.c vector.c version.c version.h    \
-vfm.c vfm.h vfmP.h weight.c
+vfm.c vfm.h vfmP.h weight.c \
+barchart.c \
+box-whisker.c \
+cartesian.c \
+chart.c \
+chart.h \
+histogram.c \
+piechart.c 
 
 pspp_LDADD = ../lib/julcal/libjulcal.a         \
        ../lib/misc/libmisc.a                   \
+       -lplot \
        @LIBINTL@
 
 version.c:
diff --git a/src/barchart.c b/src/barchart.c
new file mode 100644 (file)
index 0000000..e47a541
--- /dev/null
@@ -0,0 +1,253 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2004 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+
+#include <stdio.h>
+#include <plot.h>
+#include <stdarg.h>
+#include <math.h>
+#include "chart.h"
+
+#define CATAGORIES 6
+#define SUB_CATAGORIES 3
+
+
+
+static const    double x_min = 0;
+static const    double x_max = 15.0;
+
+static const char *cat_labels[] = 
+  {
+    "Age",
+    "Intelligence",
+    "Wealth",
+    "Emotional",
+    "cat 5",
+    "cat 6",
+    "cat 7",
+    "cat 8",
+    "cat 9",
+    "cat 10",
+    "cat 11"
+  };
+
+
+
+
+/* Subcatagories */
+static const double data1[] =
+{
+  28,83,
+  34,
+  29,13,
+   9,4,
+   3,3,
+   2,0, 
+   1,0,
+   0,
+   1,1
+};
+
+
+static const double data2[] =
+{
+  45,13,
+   9,4,
+   3,43,
+   2,0, 
+   1,20,
+   0,0,
+  1,1,
+  0,0
+};
+
+static const double data3[] =
+  {
+    23,18,
+    0, 45,23, 9, 40, 24,4, 8
+  };
+
+
+static const char subcat_name[]="Gender";
+
+
+struct subcat {
+  const double *data;
+  char   *label;
+};
+
+static const struct subcat sub_catagory[SUB_CATAGORIES] = 
+  {
+    {data1, "male"},
+    {data2, "female"},
+    {data3, "47xxy"} 
+  };
+
+
+
+static const    double y_min = 0;
+static const    double y_max = 120.0;
+static const    double y_tick = 20.0;
+
+
+
+static void write_legend(struct chart *chart) ;
+
+
+void
+draw_barchart(struct chart *ch, const char *title, 
+             const char *xlabel, const char *ylabel, enum bar_opts opt)
+{
+
+  double d;
+  int i;
+
+  double interval_size = fabs(ch->data_right - ch->data_left) / ( CATAGORIES );
+  
+  double bar_width = interval_size / 1.1 ;
+
+  if ( opt != BAR_STACKED ) 
+      bar_width /= SUB_CATAGORIES;
+
+  double ordinate_scale = fabs(ch->data_top -  ch->data_bottom) / fabs(y_max - y_min) ;
+
+  /* Move to data bottom-left */
+  pl_move_r(ch->lp, ch->data_left, ch->data_bottom);
+
+  pl_savestate_r(ch->lp);
+  pl_filltype_r(ch->lp,1);
+
+  /* Draw the data */
+  for (i = 0 ; i < CATAGORIES ; ++i ) 
+    {
+      int sc;
+      double ystart=0.0;
+      double x = i * interval_size;
+
+      pl_savestate_r(ch->lp);
+
+      draw_tick (ch, TICK_ABSCISSA, x + (interval_size/2 ), 
+                cat_labels[i]);
+
+      for(sc = 0 ; sc < SUB_CATAGORIES ; ++sc ) 
+       {
+         
+         pl_savestate_r(ch->lp);
+         pl_fillcolorname_r(ch->lp,data_colour[sc]);
+         
+         switch ( opt )
+           {
+           case BAR_GROUPED:
+             pl_fboxrel_r(ch->lp, 
+                          x + (sc * bar_width ), 0,
+                          x + (sc + 1) * bar_width, 
+                            sub_catagory[sc].data[i] * ordinate_scale );
+             break;
+             
+
+           case BAR_STACKED:
+
+             pl_fboxrel_r(ch->lp, 
+                          x, ystart, 
+                          x + bar_width, 
+                          ystart + sub_catagory[sc].data[i] * ordinate_scale );
+
+             ystart +=    sub_catagory[sc].data[i] * ordinate_scale ; 
+
+             break;
+
+           default:
+             break;
+           }
+         pl_restorestate_r(ch->lp);
+       }
+
+      pl_restorestate_r(ch->lp);
+    }
+  pl_restorestate_r(ch->lp);
+
+  for ( d = y_min; d <= y_max ; d += y_tick )
+    {
+
+      draw_tick (ch, TICK_ORDINATE,
+                (d - y_min ) * ordinate_scale, "%g", d);
+      
+    }
+
+  /* Write the abscissa label */
+  pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
+  pl_alabel_r(ch->lp,0,'t',xlabel);
+
+  /* Write the ordinate label */
+  pl_savestate_r(ch->lp);
+  pl_move_r(ch->lp,ch->data_bottom, ch->ordinate_right);
+  pl_textangle_r(ch->lp,90);
+  pl_alabel_r(ch->lp,0,0,ylabel);
+  pl_restorestate_r(ch->lp);
+
+
+  chart_write_title(ch, title);
+
+  write_legend(ch);
+  
+
+}
+
+
+
+
+
+static void
+write_legend(struct chart *chart)
+{
+  int sc;
+
+  pl_savestate_r(chart->lp);
+
+  pl_filltype_r(chart->lp,1);
+
+  pl_move_r(chart->lp, chart->legend_left, 
+           chart->data_bottom + chart->font_size * SUB_CATAGORIES * 1.5);
+
+  pl_alabel_r(chart->lp,0,'b',subcat_name);
+
+  for (sc = 0 ; sc < SUB_CATAGORIES ; ++sc ) 
+    {
+      pl_fmove_r(chart->lp,
+                chart->legend_left,
+                chart->data_bottom + chart->font_size * sc  * 1.5);
+
+      pl_savestate_r(chart->lp);    
+      pl_fillcolorname_r(chart->lp,data_colour[sc]);
+      pl_fboxrel_r (chart->lp,
+                   0,0,
+                   chart->font_size, chart->font_size);
+      pl_restorestate_r(chart->lp);    
+
+      pl_fmove_r(chart->lp,
+                chart->legend_left + chart->font_size * 1.5,
+                chart->data_bottom + chart->font_size * sc  * 1.5);
+
+      pl_alabel_r(chart->lp,'l','b',sub_catagory[sc].label);
+    }
+
+
+  pl_restorestate_r(chart->lp);    
+}
diff --git a/src/box-whisker.c b/src/box-whisker.c
new file mode 100644 (file)
index 0000000..fa52476
--- /dev/null
@@ -0,0 +1,218 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2004 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+
+#include "chart.h"
+#include <math.h>
+
+/* Draw a box-and-whiskers plot
+*/
+
+struct data_stats 
+{
+  double ptile0  ;
+  double ptile25 ;
+  double median  ;
+  double ptile75 ;
+
+  double ptile100;
+
+  double outlier ;
+};
+
+
+const struct data_stats stats1 = {
+  40,
+  45,
+  54,
+  60,
+  70,
+
+  33
+};
+
+const struct data_stats stats2 = {
+  30,
+  40,
+  45,
+  54,
+  60,
+
+
+  72
+};
+
+
+
+
+
+static const double y_min = 25;
+static const double y_max = 75;
+static const double y_tick = 10;
+
+
+
+#define min(A,B) ((A>B)?B:A)
+
+
+void draw_box_and_whiskers(struct chart *ch,
+                          double box_centre, const struct data_stats *s, 
+                          const char *name);
+
+
+static double ordinate_scale;
+
+void
+draw_box_whisker_chart(struct chart *ch, const char *title)
+{
+  double d;
+
+  ordinate_scale = fabs(ch->data_top -  ch->data_bottom) / fabs(y_max - y_min) ;
+
+
+  chart_write_title(ch, title);
+
+  
+
+  /* Move to data bottom-left */
+  pl_move_r(ch->lp, 
+                 ch->data_left, ch->data_bottom);
+
+  for ( d = y_min; d <= y_max ; d += y_tick )
+    {
+      draw_tick (ch, TICK_ORDINATE, (d - y_min ) * ordinate_scale, "%g", d);
+    }
+
+  draw_box_and_whiskers(ch,
+                       ch->data_left  + 1.0/4.0 * (ch->data_right - ch->data_left) ,
+                       &stats1,"Stats1"
+                       );
+
+  draw_box_and_whiskers(ch,
+                       ch->data_left + 3.0/4.0 * (ch->data_right - ch->data_left),
+                       &stats2,"Stats2"
+                       );
+
+
+}
+
+
+void 
+draw_box_and_whiskers(struct chart *ch,
+                     double box_centre, const struct data_stats *s,
+                     const char *name)
+{
+
+  const double box_width = (ch->data_right - ch->data_left) / 4.0;
+
+  const double box_left = box_centre - box_width / 2.0;
+
+  const double box_right = box_centre + box_width / 2.0;
+
+
+  const double box_bottom = 
+    ch->data_bottom + ( s->ptile25 - y_min ) * ordinate_scale;
+
+
+  const double box_top = 
+    ch->data_bottom + ( s->ptile75 - y_min ) * ordinate_scale;
+
+
+  const double iq_range = s->ptile75 - s->ptile25;
+
+  const double bottom_whisker = 
+    ch->data_bottom + (min(s->ptile0,s->ptile25 + iq_range*1.5)  - y_min ) * 
+    ordinate_scale;
+
+  const double top_whisker =
+    ch->data_bottom + (min(s->ptile100,s->ptile75 + iq_range*1.5)  - y_min ) * 
+    ordinate_scale;
+       
+  pl_savestate_r(ch->lp);
+
+
+  /* Draw the box */
+  pl_savestate_r(ch->lp);
+  pl_fillcolorname_r(ch->lp,ch->fill_colour);
+  pl_filltype_r(ch->lp,1);
+  pl_fbox_r(ch->lp, 
+           box_left,
+           box_bottom,
+           box_right,
+           box_top);
+
+  pl_restorestate_r(ch->lp);
+
+
+  
+  /* Draw the median */
+  pl_savestate_r(ch->lp);
+  pl_linewidth_r(ch->lp,5);
+  pl_fline_r(ch->lp, 
+            box_left, 
+            ch->data_bottom + ( s->median - y_min ) * ordinate_scale,
+            box_right,   
+            ch->data_bottom + ( s->median - y_min ) * ordinate_scale);
+  pl_restorestate_r(ch->lp);
+
+
+  /* Draw the bottom whisker */
+  pl_fline_r(ch->lp, 
+            box_left, 
+            bottom_whisker,
+            box_right,   
+            bottom_whisker);
+
+  /* Draw top whisker */
+  pl_fline_r(ch->lp, 
+            box_left, 
+            top_whisker,
+            box_right,   
+            top_whisker);
+
+
+  /* Draw centre line.
+     (bottom half) */
+  pl_fline_r(ch->lp, 
+            box_centre, bottom_whisker,
+            box_centre, box_bottom);
+
+  /* (top half) */
+  pl_fline_r(ch->lp, 
+            box_centre, top_whisker,
+            box_centre, box_top);
+
+
+  /* Draw an outlier */
+  pl_fcircle_r(ch->lp,
+              box_centre, 
+              ch->data_bottom + (s->outlier - y_min ) * ordinate_scale,
+              5);
+
+  pl_moverel_r(ch->lp, 10,0);
+  pl_alabel_r(ch->lp,'l','c',"123");
+
+
+  /* Draw  tick  mark on x axis */
+  draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, name);
+
+  pl_restorestate_r(ch->lp);
+
+}
+
diff --git a/src/cartesian.c b/src/cartesian.c
new file mode 100644 (file)
index 0000000..7fc7467
--- /dev/null
@@ -0,0 +1,313 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2004 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+
+#include <math.h>
+#include "chart.h"
+#include <assert.h>
+
+
+
+static const    double y_min = 15;
+static const    double y_max = 120.0;
+static const    double y_tick = 20.0;
+
+
+
+static const double x_min = -11.0;
+static const double x_max = 19.0;
+static const double x_tick = 5.0;
+
+
+struct datum 
+{
+  double x;
+  double y;
+};
+
+
+static const struct datum data1[]=
+  {
+    { -8.0, 29 },
+    { -3.7, 45 },
+    { -3.3, 67 },
+    { -0.8, 89 },
+    { -0.2, 93 },
+    { 1.0,  100},
+    { 2.3,  103},
+    { 4.0,  103.4},
+    { 5.2,  104},
+    { 5.9,  106},
+    { 10.3, 106},
+    { 13.8, 108},
+    { 15.8, 109},
+  };
+
+
+
+
+static const struct datum data2[]=
+  {
+    { -9.1, 20 },
+    { -8.2, 17 },
+    { -5.0, 19 },
+    { -3.7, 25 },
+    { -1.6, 49 },
+    { -1.3, 61 },
+    { -1.1, 81 },
+    { 3.5,  91},
+    { 5.4,  93},
+    { 9.3,  94},
+    { 14.3,  92}
+  };
+
+
+
+
+struct dataset
+{
+  const struct datum *data;
+  int n_data;
+  char *label;
+};
+
+
+
+#define DATASETS 2
+
+static const struct dataset dataset[DATASETS] = 
+  {
+    {data1, 13, "male"},
+    {data2, 11, "female"},
+  };
+
+
+
+typedef void (*plot_func) (struct chart *ch,  const struct dataset *dataset);
+
+
+void plot_line(struct chart *ch,  const struct dataset *dataset);
+
+void plot_scatter(struct chart *ch,  const struct dataset *dataset);
+
+
+
+static void
+write_legend(struct chart *chart, const char *heading, int n);
+
+void draw_cartesian(struct chart *ch, const char *title, 
+                   const char *xlabel, const char *ylabel, plot_func pf);
+
+
+
+void
+draw_scatterplot(struct chart *ch, const char *title, 
+                const char *xlabel, const char *ylabel)
+{
+  draw_cartesian(ch, title, xlabel, ylabel, plot_scatter);
+}
+
+
+void
+draw_lineplot(struct chart *ch, const char *title, 
+                const char *xlabel, const char *ylabel)
+{
+  draw_cartesian(ch, title, xlabel, ylabel, plot_scatter);
+}
+
+
+void
+draw_cartesian(struct chart *ch, const char *title, 
+                const char *xlabel, const char *ylabel, plot_func pf)
+{
+  double x;
+  double y;
+  
+
+  int d;
+
+
+  const double ordinate_scale = 
+    fabs(ch->data_top -  ch->data_bottom) 
+    / fabs(y_max - y_min) ;
+
+
+  const double abscissa_scale =
+    fabs(ch->data_right - ch->data_left) 
+    / 
+    fabs(x_max - x_min);
+
+
+  /* Move to data bottom-left */
+  pl_move_r(ch->lp, ch->data_left, ch->data_bottom);
+
+  pl_savestate_r(ch->lp);
+
+
+  for(x = x_tick * ceil(x_min / x_tick )  ; 
+      x < x_max;    
+      x += x_tick )
+      draw_tick (ch, TICK_ABSCISSA, (x - x_min) * abscissa_scale, "%g", x);
+
+  for(y = y_tick * ceil(y_min / y_tick )  ; 
+      y < y_max;    
+      y += y_tick )
+      draw_tick (ch, TICK_ORDINATE, (y - y_min) * ordinate_scale, "%g", y);
+
+  pl_savestate_r(ch->lp);
+
+  for (d = 0 ; d < DATASETS ; ++d ) 
+    {
+      pl_pencolorname_r(ch->lp,data_colour[d]);
+      pf(ch, &dataset[d]);
+    }
+  
+  pl_restorestate_r(ch->lp);
+
+  /* Write the abscissa label */
+  pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
+  pl_alabel_r(ch->lp,0,'t',xlabel);
+
+  /* Write the ordinate label */
+  pl_savestate_r(ch->lp);
+  pl_move_r(ch->lp,ch->data_bottom, ch->ordinate_right);
+  pl_textangle_r(ch->lp,90);
+  pl_alabel_r(ch->lp,0,0,ylabel);
+  pl_restorestate_r(ch->lp);
+
+
+  chart_write_title(ch, title);
+
+  write_legend(ch,"Key:",DATASETS);
+
+  pl_restorestate_r(ch->lp);
+
+}
+
+
+
+
+static void
+write_legend(struct chart *chart, const char *heading, 
+            int n)
+{
+  int ds;
+
+  pl_savestate_r(chart->lp);
+
+  pl_filltype_r(chart->lp,1);
+
+  pl_move_r(chart->lp, chart->legend_left, 
+           chart->data_bottom + chart->font_size * n * 1.5);
+
+  pl_alabel_r(chart->lp,0,'b',heading);
+
+  for (ds = 0 ; ds < n ; ++ds ) 
+    {
+      pl_fmove_r(chart->lp,
+                chart->legend_left,
+                chart->data_bottom + chart->font_size * ds  * 1.5);
+
+      pl_savestate_r(chart->lp);    
+      pl_fillcolorname_r(chart->lp,data_colour[ds]);
+      pl_fboxrel_r (chart->lp,
+                   0,0,
+                   chart->font_size, chart->font_size);
+      pl_restorestate_r(chart->lp);    
+
+      pl_fmove_r(chart->lp,
+                chart->legend_left + chart->font_size * 1.5,
+                chart->data_bottom + chart->font_size * ds  * 1.5);
+
+      pl_alabel_r(chart->lp,'l','b',dataset[ds].label);
+    }
+
+
+  pl_restorestate_r(chart->lp);    
+}
+
+
+
+void
+plot_line(struct chart *ch,  const struct dataset *dataset)
+{
+  int i;
+
+  const struct datum *data = dataset->data;
+
+  const double ordinate_scale = 
+    fabs(ch->data_top -  ch->data_bottom) 
+    / fabs(y_max - y_min) ;
+
+
+  const double abscissa_scale =
+    fabs(ch->data_right - ch->data_left) 
+    / 
+    fabs(x_max - x_min);
+
+
+  for( i = 0 ; i < dataset->n_data ; ++i ) 
+    {
+      const double x = 
+       (data[i].x - x_min) * abscissa_scale + ch->data_left ; 
+      const double y = 
+       (data[i].y - y_min) * ordinate_scale + ch->data_bottom;
+
+      if (i == 0 ) 
+       pl_move_r(ch->lp, x, y );
+      else
+       pl_fcont_r(ch->lp, x, y);
+    }
+  pl_endpath_r(ch->lp);
+
+}
+
+
+
+
+void
+plot_scatter(struct chart *ch,  const struct dataset *dataset)
+{
+  int i;
+
+  const struct datum *data = dataset->data;
+
+  const double ordinate_scale = 
+    fabs(ch->data_top -  ch->data_bottom) 
+    / fabs(y_max - y_min) ;
+
+
+  const double abscissa_scale =
+    fabs(ch->data_right - ch->data_left) 
+    / 
+    fabs(x_max - x_min);
+
+
+  for( i = 0 ; i < dataset->n_data ; ++i ) 
+    {
+      const double x = 
+       (data[i].x - x_min) * abscissa_scale + ch->data_left ; 
+      const double y = 
+       (data[i].y - y_min) * ordinate_scale + ch->data_bottom;
+
+      pl_fmarker_r(ch->lp, x, y, 6, 15);
+    }
+  
+}
diff --git a/src/chart.c b/src/chart.c
new file mode 100644 (file)
index 0000000..abf4e70
--- /dev/null
@@ -0,0 +1,173 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2004 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+
+#include <stdio.h>
+#include <plot.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+#include <assert.h>
+#include <math.h>
+
+#include "chart.h"
+
+
+const char *data_colour[] = {
+  "brown",
+  "red",
+  "orange",
+  "yellow",
+  "green",
+  "blue",
+  "violet",
+  "grey",
+  "pink"
+};
+
+
+
+int
+chart_initialise(struct chart *chart)
+{
+
+  chart->pl_params = pl_newplparams();
+
+  chart->lp = pl_newpl_r ("X",0,stdout,stderr,chart->pl_params);
+
+  if (pl_openpl_r (chart->lp) < 0)      /* open Plotter */
+      return 1;
+
+  pl_fspace_r (chart->lp, 0.0, 0.0, 1000.0, 1000.0); /* set coordinate system */
+  pl_flinewidth_r (chart->lp, 0.25);    /* set line thickness */
+  pl_pencolorname_r (chart->lp, "black"); 
+
+  pl_erase_r (chart->lp);               /* erase graphics display */
+  pl_filltype_r(chart->lp,0);
+
+
+
+  pl_savestate_r(chart->lp);
+
+  /* Set default chartetry */
+  chart->data_top =   900;
+  chart->data_right = 800;
+  chart->data_bottom = 120;
+  chart->data_left = 150;
+  chart->abscissa_top = 70;
+  chart->ordinate_right = 120;
+  chart->title_bottom = 920;
+  chart->legend_left = 810;
+  chart->legend_right = 1000;
+  chart->font_size = 0;
+  strcpy(chart->fill_colour,"red");
+
+
+  /* Get default font size */
+  if ( !chart->font_size) 
+    chart->font_size = pl_fontsize_r(chart->lp, -1);
+
+  /* Draw the data area */
+  pl_box_r(chart->lp, 
+          chart->data_left, chart->data_bottom, 
+          chart->data_right, chart->data_top);
+
+  return 0;
+
+}
+
+
+
+/* Draw a tick mark at position
+   If label is non zero, then print it at the tick mark
+*/
+void
+draw_tick(struct chart *chart, 
+         enum tick_orientation orientation, 
+         double position, 
+         const char *label, ...)
+{
+  const int tickSize = 10;
+
+  pl_savestate_r(chart->lp);
+
+  pl_move_r(chart->lp, chart->data_left, chart->data_bottom);
+
+  if ( orientation == TICK_ABSCISSA ) 
+    pl_flinerel_r(chart->lp, position, 0, position, -tickSize);
+  else if (orientation == TICK_ORDINATE ) 
+      pl_flinerel_r(chart->lp, 0, position, -tickSize, position);
+  else
+    assert(0);
+
+  if ( label ) {
+    char buf[10];
+    va_list ap;
+    va_start(ap,label);
+    vsnprintf(buf,10,label,ap);
+
+    if ( orientation == TICK_ABSCISSA ) 
+      pl_alabel_r(chart->lp, 'c','t', buf);
+    else if (orientation == TICK_ORDINATE ) 
+      {
+       if ( fabs(position) < DBL_EPSILON )
+           pl_moverel_r(chart->lp, 0, 10);
+
+       pl_alabel_r(chart->lp, 'r','c', buf);
+      }
+
+    va_end(ap);
+  }
+    
+  pl_restorestate_r(chart->lp);
+}
+
+
+
+
+void  
+chart_write_title(struct chart *chart, const char *title)
+{
+  /* Write the title */
+  pl_savestate_r(chart->lp);
+  pl_ffontsize_r(chart->lp,chart->font_size * 1.5);
+  pl_move_r(chart->lp,chart->data_left, chart->title_bottom);
+  pl_alabel_r(chart->lp,0,0,title);
+  pl_restorestate_r(chart->lp);
+}
+
+
+
+void
+chart_finalise(struct chart *chart)
+{
+  pl_restorestate_r(chart->lp);
+
+  if (pl_closepl_r (chart->lp) < 0)     /* close Plotter */
+    {
+      fprintf (stderr, "Couldn't close Plotter\n");
+    }
+
+
+  pl_deletepl_r(chart->lp);
+
+  pl_deleteplparams(chart->pl_params);
+
+}
+
diff --git a/src/chart.h b/src/chart.h
new file mode 100644 (file)
index 0000000..47c6c7d
--- /dev/null
@@ -0,0 +1,179 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2004 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+
+#ifndef CHART_H
+#define CHART_H
+
+#include <stdio.h>
+#include <plot.h>
+#include "var.h"
+
+
+/* Array of standard colour names */
+extern const char *data_colour[];
+
+
+struct chart {
+
+  plPlotter *lp ;
+  plPlotterParams *pl_params;
+
+  /* The geometry of the chart 
+     See diagram at the foot of this file.
+   */
+  
+  int data_top   ;
+  int data_right ;
+  int data_bottom;
+  int data_left  ;
+
+  int abscissa_top;
+
+  int ordinate_right ;
+
+  int title_bottom ;
+
+  int legend_left ;
+  int legend_right ;
+
+  
+  /* Default font size for the plot (if zero, then use plotter default) */
+  int font_size; 
+
+  char fill_colour[10];
+
+};
+
+
+int  chart_initialise(struct chart *ch);
+
+void chart_finalise(struct chart *ch);
+
+
+
+void chart_write_title(struct chart *ch, 
+                     const char *title);
+
+enum tick_orientation {
+  TICK_ABSCISSA=0,
+  TICK_ORDINATE
+};
+
+void draw_tick(struct chart *ch, enum tick_orientation orientation, 
+              double position, const char *label, ...);
+
+
+
+enum  bar_opts {
+  BAR_GROUPED =  0,
+  BAR_STACKED,
+  BAR_RANGE
+};
+
+
+void draw_barchart(struct chart *ch, const char *title, 
+                  const char *xlabel, const char *ylabel, enum bar_opts opt);
+
+void draw_box_whisker_chart(struct chart *ch, const char *title);
+
+
+
+struct normal_curve
+{
+  double N ;
+  double mean ;
+  double stddev ;
+};
+
+
+void draw_histogram(struct chart *ch, 
+                   const struct variable *v,
+                   const char *title, 
+                   struct normal_curve *norm,
+                   int show_normal);
+
+
+
+void draw_piechart(struct chart *ch, const struct variable *v);
+
+void draw_scatterplot(struct chart *ch, const char *title, 
+                     const char *xlabel, const char *ylabel);
+
+
+void draw_lineplot(struct chart *ch, const char *title, 
+                     const char *xlabel, const char *ylabel);
+
+
+
+#endif
+
+#if 0
+The anatomy of a chart is as follows.
+
++-------------------------------------------------------------+
+|           +----------------------------------+             |
+|           |                                  |             |
+|           |          Title                   |             |
+|           |                                  |             |
+|                   +----------------------------------+             |
+|+----------++----------------------------------++-----------+|
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+|| Ordinate ||           Data                  ||  Legend   ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||
+||         ||                                  ||           ||       
+|+----------++----------------------------------++-----------+|          --  
+|           +----------------------------------+             | -  ^  data_bottom
+|           |          Abscissa                |             | ^  |             
+|           |                                  |             | | abscissa_top
+|           +----------------------------------+             | v  v  
++-------------------------------------------------------------+ ----  
+                                               
+ordinate_right                                 ||           |
+|           |                                   ||          |
+|<--------->|                                   ||          |
+|            |                                  ||          |
+| data_left  |                                  ||          |
+|<---------->|                                  ||          |
+|                                               ||          |
+|               data_right                      ||          |
+|<--------------------------------------------->||          |
+|                 legend_left                   |           |
+|<---------------------------------------------->|          |
+|                   legend_right                            |
+|<---------------------------------------------------------->|
+                                                            
+#endif
index a6b82f6f6fa65db22deba1efd7f11430978f6e94..9eb171724565e65338b51d1c79de87256acabbbd 100644 (file)
@@ -47,6 +47,7 @@
 #include "var.h"
 #include "vfm.h"
 #include "settings.h"
+#include "chart.h"
 
 #include "debug-print.h"
 
@@ -63,6 +64,9 @@
      barchart(ba_)=:minimum(d:min),
            :maximum(d:max),
            scale:freq(*n:freq,"%s>0")/percent(*n:pcnt,"%s>0");
+     piechart(pie_)=:minimum(d:min),
+           :maximum(d:max),
+           missing:missing/!nomissing;
      histogram(hi_)=:minimum(d:min),
            :maximum(d:max),
            scale:freq(*n:freq,"%s>0")/percent(*n:pcnt,"%s>0"),
@@ -148,6 +152,7 @@ enum
     GFT_NONE,                  /* Don't draw graphs. */
     GFT_BAR,                   /* Draw bar charts. */
     GFT_HIST,                  /* Draw histograms. */
+    GFT_PIE,                    /* Draw piechart */
     GFT_HBAR                   /* Draw bar charts or histograms at our discretion. */
   };
 
@@ -155,7 +160,8 @@ enum
 static struct cmd_frequencies cmd;
 
 /* Summary of the barchart, histogram, and hbar subcommands. */
-static int chart;              /* NONE/BAR/HIST/HBAR. */
+/* FIXME: These should not be mututally exclusive */
+static int chart;              /* NONE/BAR/HIST/HBAR/PIE. */
 static double min, max;                /* Minimum, maximum on y axis. */
 static int format;             /* FREQ/PERCENT: Scaling of y axis. */
 static double scale, incr;     /* FIXME */
@@ -174,6 +180,8 @@ static struct pool *gen_pool;       /* General mode. */
 
 static void determine_charts (void);
 
+static void calc_stats (struct variable * v, double d[frq_n_stats]);
+
 static void precalc (void *);
 static int calc (struct ccase *, void *);
 static void postcalc (void *);
@@ -267,7 +275,8 @@ internal_cmd_frequencies (void)
 static void
 determine_charts (void)
 {
-  int count = (!!cmd.sbc_histogram) + (!!cmd.sbc_barchart) + (!!cmd.sbc_hbar);
+  int count = (!!cmd.sbc_histogram) + (!!cmd.sbc_barchart) + 
+    (!!cmd.sbc_hbar) + (!!cmd.sbc_piechart);
 
   if (!count)
     {
@@ -285,6 +294,8 @@ determine_charts (void)
     chart = GFT_HIST;
   else if (cmd.sbc_barchart)
     chart = GFT_BAR;
+  else if (cmd.sbc_piechart)
+    chart = GFT_PIE;
   else
     chart = GFT_HBAR;
 
@@ -328,7 +339,7 @@ determine_charts (void)
          format = FRQ_PERCENT;
          scale = cmd.ba_pcnt;
        }
-      if (cmd.hi_norm)
+      if (cmd.hi_norm != FRQ_NONORMAL )
        normal = 1;
       if (cmd.hi_incr == FRQ_INCREMENT)
        incr = cmd.hi_inc;
@@ -385,9 +396,10 @@ calc (struct ccase *c, void *aux UNUSED)
        {
          case FRQM_GENERAL:
            {
+
              /* General mode. */
              struct freq **fpp = (struct freq **) hsh_probe (ft->data, val);
-         
+
              if (*fpp != NULL)
                (*fpp)->c += weight;
              else
@@ -504,7 +516,40 @@ postcalc (void *aux UNUSED)
       if (n_stats)
        dump_statistics (v, !dumped_freq_tab);
 
+
+      if ( chart == GFT_HIST) 
+       {
+         struct chart ch;
+         double d[frq_n_stats];
+         struct frequencies_proc *frq = &v->p.frq;
+         
+         struct normal_curve norm;
+         norm.N = frq->tab.total_cases ;
+
+         calc_stats(v,d);
+         norm.mean = d[frq_mean];
+         norm.stddev = d[frq_stddev];
+
+         chart_initialise(&ch);
+         draw_histogram(&ch, v_variables[i], "HISTOGRAM",&norm,normal);
+         chart_finalise(&ch);
+       }
+
+
+      if ( chart == GFT_PIE) 
+       {
+         struct chart ch;
+
+         chart_initialise(&ch);
+         
+         draw_piechart(&ch, v_variables[i]);
+
+         chart_finalise(&ch);
+       }
+
+
       cleanup_freq_tab (v);
+
     }
 }
 
@@ -1102,6 +1147,7 @@ dump_full (struct variable * v)
 
   tab_title (t, 1, "%s: %s", v->name, v->label ? v->label : "");
   tab_submit (t);
+
 }
 
 /* Sets the widths of all the columns and heights of all the rows in
diff --git a/src/histogram.c b/src/histogram.c
new file mode 100644 (file)
index 0000000..d440341
--- /dev/null
@@ -0,0 +1,258 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2004 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#include <stdio.h>
+#include <plot.h>
+#include <stdarg.h>
+#include <math.h>
+#include "hash.h"
+
+#include "var.h"
+#include "chart.h"
+
+/* Number of bins in which to divide data */
+#define BINS 15
+
+/* The approximate no of ticks on the y axis */
+#define YTICKS 10
+
+#define M_PI ( 22.0 / 7.0 )
+
+
+static double gaussian(double x, double mu, double sigma ) ;
+
+
+static double
+gaussian(double x, double mu, double sigma )
+{
+  return (exp( - (( x - mu )* (x - mu) / (2.0 * sigma * sigma)  ))
+    / ( sigma * sqrt( M_PI * 2.0) ))  ;
+}
+
+
+/* Adjust tick to be a sensible value */
+void adjust_tick(double *tick);
+
+
+/* Write the legend of the chart */
+static void
+write_legend(struct chart *ch, struct normal_curve *norm)
+{
+  char buf[100];
+  pl_savestate_r(ch->lp);
+
+  sprintf(buf,"N = %.2f",norm->N);
+  pl_move_r(ch->lp, ch->legend_left, ch->data_bottom);
+  pl_alabel_r(ch->lp,0,'b',buf);
+
+  sprintf(buf,"Mean = %.1f",norm->mean);
+  pl_fmove_r(ch->lp,ch->legend_left,ch->data_bottom + ch->font_size * 1.5);
+  pl_alabel_r(ch->lp,0,'b',buf);
+
+  sprintf(buf,"Std. Dev = %.2f",norm->stddev);
+  pl_fmove_r(ch->lp,ch->legend_left,ch->data_bottom + ch->font_size * 1.5 * 2);
+  pl_alabel_r(ch->lp,0,'b',buf);
+
+  pl_restorestate_r(ch->lp);    
+}
+
+
+
+
+/* Draw a histogram.
+   If show_normal is non zero then superimpose a normal curve
+*/
+void
+draw_histogram(struct chart *ch, 
+              const struct variable *var,
+              const char *title, 
+              struct normal_curve *norm,
+              int show_normal)
+{
+
+  double d;
+  int count;
+
+  double x_min = DBL_MAX;
+  double x_max = -DBL_MAX;
+  double y_min = DBL_MAX;
+  double y_max = -DBL_MAX;
+  
+  double y_tick ;
+
+
+  double ordinate_values[BINS];
+
+  const struct freq_tab *frq_tab = &var->p.frq.tab ;
+
+  struct hsh_iterator hi;
+  struct hsh_table *fh = frq_tab->data;
+  struct freq *frq;
+
+  double interval_size = fabs(ch->data_right - ch->data_left) / ( BINS );
+
+  /* Find out the extremes of the x value 
+     FIXME: These don't need to be calculated here, since the 
+     calling routine should know them */
+
+  for ( frq = hsh_first(fh,&hi); frq != 0; frq = hsh_next(fh,&hi) ) 
+    {
+      if ( frq->v.f < x_min ) x_min = frq->v.f ;
+      if ( frq->v.f > x_max ) x_max = frq->v.f ;
+    }
+
+  double x_interval = fabs(x_max - x_min) / ( BINS - 1);
+
+  
+  double abscissa_scale = 
+    fabs( (ch->data_right -  ch->data_left) / (x_max - x_min) ) ;
+
+
+  /* Find out the bin values */
+  for ( count = 0, d = x_min; d <= x_max ; d += x_interval, ++count )
+    {
+
+      double y = 0;
+
+      for ( frq = hsh_first(fh,&hi); frq != 0; frq = hsh_next(fh,&hi) ) 
+       {
+         if ( frq->v.f >= d && frq->v.f < d + x_interval )
+           y += frq->c;
+       }
+
+      ordinate_values[count] = y ;
+
+      if ( y > y_max ) y_max = y ;
+      if ( y < y_min ) y_min = y;
+    }
+
+  y_tick = ( y_max - y_min ) / (double) (YTICKS - 1) ;
+
+  adjust_tick(&y_tick);
+
+  y_min = floor( y_min / y_tick ) * y_tick ;
+  y_max = ceil( y_max / y_tick ) * y_tick ;
+
+  double ordinate_scale =
+    fabs(ch->data_top -  ch->data_bottom) / fabs(y_max - y_min) ;
+  
+
+  /* Move to data bottom-left */
+  pl_move_r(ch->lp, ch->data_left, ch->data_bottom);
+
+  pl_savestate_r(ch->lp);
+  pl_fillcolorname_r(ch->lp, ch->fill_colour); 
+  pl_filltype_r(ch->lp,1);
+
+  /* Draw the histogram */
+  for ( count = 0, d = x_min; d <= x_max ; d += x_interval, ++count )
+    {
+      const double x = count * interval_size ;
+      pl_savestate_r(ch->lp);
+
+      draw_tick (ch, TICK_ABSCISSA, x + (interval_size / 2.0 ), "%.1f",d);
+
+      pl_fboxrel_r(ch->lp, x, 0, x + interval_size, 
+                  ordinate_values[count] * ordinate_scale );
+
+      pl_restorestate_r(ch->lp);
+
+    }
+  pl_restorestate_r(ch->lp);
+
+  /* Put the y axis on */
+  for ( d = y_min; d <= y_max ; d += y_tick )
+    {
+      draw_tick (ch, TICK_ORDINATE, (d - y_min ) * ordinate_scale, "%g", d);
+    }
+
+  /* Write the abscissa label */
+  pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
+  pl_alabel_r(ch->lp,0,'t',var->label ? var->label : var->name);
+
+  /* Write the ordinate label */
+  pl_savestate_r(ch->lp);
+  pl_move_r(ch->lp,ch->data_bottom, ch->ordinate_right);
+  pl_textangle_r(ch->lp,90);
+  pl_alabel_r(ch->lp,0,0,"Frequency");
+
+  pl_restorestate_r(ch->lp);
+
+
+  chart_write_title(ch, title);
+
+
+  /* Write the legend */
+  write_legend(ch,norm);
+
+
+  if ( show_normal  )
+  {
+    /* Draw the normal curve */
+    double d;
+
+    pl_move_r(ch->lp, ch->data_left, ch->data_bottom);    
+    for( d = ch->data_left; 
+        d <= ch->data_right ; 
+        d += (ch->data_right - ch->data_left) / 100.0)
+      {
+       const double x = (d  - ch->data_left  - interval_size / 2.0  ) / 
+         abscissa_scale  + x_min ; 
+       
+       pl_fcont_r(ch->lp,  d,
+                  ch->data_bottom +
+                  norm->N * gaussian(x, norm->mean, norm->stddev) 
+                  * ordinate_scale);
+
+      }
+    pl_endpath_r(ch->lp);
+  }
+
+}
+
+
+
+double 
+log10(double x)
+{
+  return log(x) / log(10.0) ;
+}
+
+  
+/* Adjust tick to be a sensible value */
+void
+adjust_tick(double *tick)
+{
+    int i;
+    const double standard_ticks[] = {1, 2, 5};
+
+    const double factor = pow(10,ceil(log10(standard_ticks[0] / *tick))) ;
+
+    for (i = 2  ; i >=0 ; --i) 
+      {
+       if ( *tick > standard_ticks[i] / factor ) 
+         {
+           *tick = standard_ticks[i] / factor ;
+           break;
+         }
+      }
+    
+  }
+
diff --git a/src/piechart.c b/src/piechart.c
new file mode 100644 (file)
index 0000000..29e0c79
--- /dev/null
@@ -0,0 +1,212 @@
+/* PSPP - draws pie charts of sample statistics
+
+Copyright (C) 2004 Free Software Foundation, Inc.
+Written by John Darrington <john@darrington.wattle.id.au>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+
+#include "chart.h"
+#include <float.h>
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include "value-labels.h"
+
+
+/* Pie charts of course need to know Pi :) */
+#define M_PI ( 22.0 / 7.0 ) 
+
+
+#define min(A,B) ((A>B)?B:A)
+
+
+/* Draw a single slice of the pie */
+void
+draw_segment(struct chart *ch, 
+            double centre_x, double centre_y, 
+            double radius,
+            double start_angle, double segment_angle,
+            const char *colour) ;
+
+
+/* Draw a pie chart */
+void
+draw_piechart(struct chart *ch, const struct variable *var)
+{
+  int i;
+
+  const struct freq_tab *frq_tab = &var->p.frq.tab ;
+
+  const int n_data = frq_tab->n_valid;
+  const double left_label = ch->data_left + 
+    (ch->data_right - ch->data_left)/10.0;
+
+  const double right_label = ch->data_right - 
+    (ch->data_right - ch->data_left)/10.0;
+
+  const double centre_x = (ch->data_right + ch->data_left ) / 2.0 ;
+  const double centre_y = (ch->data_top + ch->data_bottom ) / 2.0 ;
+
+  const double radius = min( 
+                           5.0 / 12.0 * (ch->data_top - ch->data_bottom),
+                           1.0 / 4.0 * (ch->data_right - ch->data_left)
+                           );
+
+
+  chart_write_title(ch, var->label ? var->label: var->name);
+
+    
+  for (i = 0 ; i < n_data ; ++i ) 
+    {
+      static double angle=0.0;
+      const struct freq frq = frq_tab->valid[i];
+
+      const double segment_angle = 
+       frq.c / frq_tab->valid_cases * 2 * M_PI ;
+
+      char *label = val_labs_find (var->val_labs, frq.v );
+      if ( !label ) 
+       {
+         static char l[20];
+         snprintf(l,20,"%g",frq.v.f);
+         label = l;
+       }
+
+      const double label_x = centre_x - 
+       radius * sin(angle + segment_angle/2.0);
+
+      const double label_y = centre_y + 
+       radius * cos(angle + segment_angle/2.0);
+
+      /* Fill the segment */
+      draw_segment(ch,
+                  centre_x, centre_y, radius, 
+                  angle, segment_angle,
+                  data_colour[i]);
+       
+      /* Now add the labels */
+      if ( label_x < centre_x ) 
+       {
+         pl_line_r(ch->lp, label_x, label_y,
+                   left_label, label_y );
+         pl_moverel_r(ch->lp,0,5);
+         pl_alabel_r(ch->lp,0,0,label);
+       }
+      else
+       {
+         pl_line_r(ch->lp, 
+                   label_x, label_y,
+                   right_label, label_y
+                   );
+         pl_moverel_r(ch->lp,0,5);
+         pl_alabel_r(ch->lp,'r',0,label);
+       }
+
+      angle += segment_angle;
+
+    }
+
+  /* Draw an outline to the pie */
+  pl_filltype_r(ch->lp,0);
+  pl_fcircle_r (ch->lp, centre_x, centre_y, radius);
+
+}
+
+
+
+void
+fill_segment(struct chart *ch, 
+            double x0, double y0, 
+            double radius,
+            double start_angle, double segment_angle) ;
+
+
+/* Fill a segment with the current fill colour */
+void
+fill_segment(struct chart *ch, 
+            double x0, double y0, 
+            double radius,
+            double start_angle, double segment_angle)
+{
+
+  const double start_x  = x0 - radius * sin(start_angle);
+  const double start_y  = y0 + radius * cos(start_angle);
+
+  const double stop_x   = 
+    x0 - radius * sin(start_angle + segment_angle); 
+
+  const double stop_y   = 
+    y0 + radius * cos(start_angle + segment_angle);
+
+  assert(segment_angle <= 2 * M_PI);
+  assert(segment_angle >= 0);
+
+  if ( segment_angle > M_PI ) 
+    {
+      /* Then we must draw it in two halves */
+      fill_segment(ch, x0, y0, radius, start_angle, segment_angle / 2.0 );
+      fill_segment(ch, x0, y0, radius, start_angle + segment_angle / 2.0,
+                  segment_angle / 2.0 );
+    }
+  else
+    {
+      pl_move_r(ch->lp, x0, y0);
+
+      pl_cont_r(ch->lp, stop_x, stop_y);
+      pl_cont_r(ch->lp, start_x, start_y);
+
+      pl_arc_r(ch->lp,
+              x0, y0,
+              stop_x, stop_y,
+              start_x, start_y
+              );
+
+      pl_endpath_r(ch->lp);
+    }
+}
+
+
+
+/* Draw a single slice of the pie */
+void
+draw_segment(struct chart *ch, 
+            double x0, double y0, 
+            double radius,
+            double start_angle, double segment_angle, 
+            const char *colour)
+{
+  const double start_x  = x0 - radius * sin(start_angle);
+  const double start_y  = y0 + radius * cos(start_angle);
+
+  pl_savestate_r(ch->lp);
+
+  pl_savestate_r(ch->lp);
+  pl_colorname_r(ch->lp, colour);
+  
+  pl_pentype_r(ch->lp,1);
+  pl_filltype_r(ch->lp,1);
+
+  fill_segment(ch, x0, y0, radius, start_angle, segment_angle);
+  pl_restorestate_r(ch->lp);
+
+  /* Draw line dividing segments */
+  pl_pentype_r(ch->lp, 1);
+  pl_fline_r(ch->lp, x0, y0, start_x, start_y);
+       
+
+  pl_restorestate_r(ch->lp);
+}