output: Refactor implementation of charts.
[pspp-builds.git] / src / output / charts / piechart.c
index 97910010c69101d16c21f607cc13a369aad0f3b7..4b7b1400e9c42e1bd95d1a9d4310ea9939ccc5e9 100644 (file)
 
 #include <config.h>
 
-#include <float.h>
+#include <output/charts/piechart.h>
+
 #include <assert.h>
+#include <float.h>
 #include <gsl/gsl_math.h>
 #include <math.h>
 #include <stdio.h>
 
-
-#include <output/charts/piechart.h>
-#include <output/charts/plot-chart.h>
-
-#include <output/chart.h>
-#include <libpspp/str.h>
 #include <data/value-labels.h>
+#include <libpspp/str.h>
+#include <output/charts/plot-chart.h>
+#include <output/chart-provider.h>
 
 #include "minmax.h"
 
+struct piechart
+  {
+    struct chart chart;
+    char *title;
+    struct slice *slices;
+    int n_slices;
+  };
+
+static const struct chart_class piechart_class;
+
 /* Draw a single slice of the pie */
 static void
-draw_segment(struct chart *ch,
+draw_segment(plPlotter *,
             double centre_x, double centre_y,
             double radius,
             double start_angle, double segment_angle,
@@ -43,42 +52,64 @@ draw_segment(struct chart *ch,
 
 
 
-/* Draw a piechart */
-void
-piechart_plot(const char *title, const struct slice *slices, int n_slices)
+/* Creates and returns a chart that will render a piechart with
+   the given TITLE and the N_SLICES described in SLICES. */
+struct chart *
+piechart_create (const char *title, const struct slice *slices, int n_slices)
 {
+  struct piechart *pie;
   int i;
-  double total_magnitude=0;
 
-  struct chart *ch = chart_create();
+  pie = xmalloc (sizeof *pie);
+  chart_init (&pie->chart, &piechart_class);
+  pie->title = xstrdup (title);
+  pie->slices = xnmalloc (n_slices, sizeof *pie->slices);
+  for (i = 0; i < n_slices; i++)
+    {
+      const struct slice *src = &slices[i];
+      struct slice *dst = &pie->slices[i];
 
-  const double left_label = ch->data_left +
-    (ch->data_right - ch->data_left)/10.0;
+      ds_init_string (&dst->label, &src->label);
+      dst->magnitude = src->magnitude;
+    }
+  pie->n_slices = n_slices;
+  return &pie->chart;
+}
 
-  const double right_label = ch->data_right -
-    (ch->data_right - ch->data_left)/10.0;
+static void
+piechart_draw (const struct chart *chart, plPlotter *lp)
+{
+  struct piechart *pie = (struct piechart *) chart;
+  struct chart_geometry geom;
+  double total_magnitude;
+  double left_label, right_label;
+  double centre_x, centre_y;
+  double radius;
+  double angle;
+  int i;
 
-  const double centre_x = (ch->data_right + ch->data_left ) / 2.0 ;
-  const double centre_y = (ch->data_top + ch->data_bottom ) / 2.0 ;
+  chart_geometry_init (lp, &geom);
 
-  const double radius = MIN(
-                           5.0 / 12.0 * (ch->data_top - ch->data_bottom),
-                           1.0 / 4.0 * (ch->data_right - ch->data_left)
-                           );
+  left_label = geom.data_left + (geom.data_right - geom.data_left)/10.0;
+  right_label = geom.data_right - (geom.data_right - geom.data_left)/10.0;
 
-  double angle;
+  centre_x = (geom.data_right + geom.data_left) / 2.0 ;
+  centre_y = (geom.data_top + geom.data_bottom) / 2.0 ;
 
+  radius = MIN (5.0 / 12.0 * (geom.data_top - geom.data_bottom),
+                1.0 / 4.0 * (geom.data_right - geom.data_left));
 
-  chart_write_title(ch, "%s", title);
+  chart_write_title (lp, &geom, "%s", pie->title);
 
-  for (i = 0 ; i < n_slices ; ++i )
-    total_magnitude += slices[i].magnitude;
+  total_magnitude = 0.0;
+  for (i = 0; i < pie->n_slices; i++)
+    total_magnitude += pie->slices[i].magnitude;
 
   angle = 0.0;
-  for (i = 0 ; i < n_slices ; ++i )
+  for (i = 0; i < pie->n_slices ; ++i )
     {
       const double segment_angle =
-       slices[i].magnitude / total_magnitude * 2 * M_PI ;
+       pie->slices[i].magnitude / total_magnitude * 2 * M_PI ;
 
       const double label_x = centre_x -
        radius * sin(angle + segment_angle/2.0);
@@ -87,50 +118,38 @@ piechart_plot(const char *title, const struct slice *slices, int n_slices)
        radius * cos(angle + segment_angle/2.0);
 
       /* Fill the segment */
-      draw_segment(ch,
-                  centre_x, centre_y, radius,
-                  angle, segment_angle,
-                  data_colour[i % N_CHART_COLOURS]);
+      draw_segment (lp,
+                    centre_x, centre_y, radius,
+                    angle, segment_angle,
+                    data_colour[i % N_CHART_COLOURS]);
 
       /* 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, ds_cstr (&slices[i].label));
+         pl_line_r (lp, label_x, label_y, left_label, label_y );
+         pl_moverel_r (lp, 0, 5);
+         pl_alabel_r (lp, 0, 0, ds_cstr (&pie->slices[i].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, ds_cstr (&slices[i].label));
+         pl_line_r (lp, label_x, label_y, right_label, label_y);
+         pl_moverel_r (lp, 0, 5);
+         pl_alabel_r (lp, 'r', 0, ds_cstr (&pie->slices[i].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);
+  pl_filltype_r (lp,0);
+  pl_fcircle_r (lp, centre_x, centre_y, radius);
 
-  chart_submit(ch);
+  chart_geometry_free (lp);
 }
 
-static 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 */
 static void
-fill_segment(struct chart *ch,
+fill_segment(plPlotter *lp,
             double x0, double y0,
             double radius,
             double start_angle, double segment_angle)
@@ -151,32 +170,30 @@ fill_segment(struct chart *ch,
   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,
+      fill_segment(lp, x0, y0, radius, start_angle, segment_angle / 2.0 );
+      fill_segment(lp, x0, y0, radius, start_angle + segment_angle / 2.0,
                   segment_angle / 2.0 );
     }
   else
     {
-      pl_move_r(ch->lp, x0, y0);
+      pl_move_r(lp, x0, y0);
 
-      pl_cont_r(ch->lp, stop_x, stop_y);
-      pl_cont_r(ch->lp, start_x, start_y);
+      pl_cont_r(lp, stop_x, stop_y);
+      pl_cont_r(lp, start_x, start_y);
 
-      pl_arc_r(ch->lp,
+      pl_arc_r(lp,
               x0, y0,
               stop_x, stop_y,
               start_x, start_y
               );
 
-      pl_endpath_r(ch->lp);
+      pl_endpath_r(lp);
     }
 }
 
-
-
 /* Draw a single slice of the pie */
 static void
-draw_segment(struct chart *ch,
+draw_segment(plPlotter *lp,
             double x0, double y0,
             double radius,
             double start_angle, double segment_angle,
@@ -185,22 +202,43 @@ draw_segment(struct chart *ch,
   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(lp);
 
-  pl_savestate_r(ch->lp);
-  pl_colorname_r(ch->lp, colour);
+  pl_savestate_r(lp);
+  pl_colorname_r(lp, colour);
 
-  pl_pentype_r(ch->lp,1);
-  pl_filltype_r(ch->lp,1);
+  pl_pentype_r(lp,1);
+  pl_filltype_r(lp,1);
 
-  fill_segment(ch, x0, y0, radius, start_angle, segment_angle);
-  pl_restorestate_r(ch->lp);
+  fill_segment(lp, x0, y0, radius, start_angle, segment_angle);
+  pl_restorestate_r(lp);
 
   /* Draw line dividing segments */
-  pl_pentype_r(ch->lp, 1);
-  pl_fline_r(ch->lp, x0, y0, start_x, start_y);
+  pl_pentype_r(lp, 1);
+  pl_fline_r(lp, x0, y0, start_x, start_y);
+
 
+  pl_restorestate_r(lp);
+}
 
-  pl_restorestate_r(ch->lp);
+static void
+piechart_destroy (struct chart *chart)
+{
+  struct piechart *pie = (struct piechart *) chart;
+  int i;
+
+  free (pie->title);
+  for (i = 0; i < pie->n_slices; i++)
+    {
+      struct slice *slice = &pie->slices[i];
+      ds_destroy (&slice->label);
+    }
+  free (pie->slices);
+  free (pie);
 }
 
+static const struct chart_class piechart_class =
+  {
+    piechart_draw,
+    piechart_destroy
+  };