output: Refactor implementation of charts.
authorBen Pfaff <blp@gnu.org>
Thu, 2 Jul 2009 23:59:08 +0000 (16:59 -0700)
committerBen Pfaff <blp@gnu.org>
Thu, 2 Jul 2009 23:59:08 +0000 (16:59 -0700)
This commit addresses a weakness of the charts implementation in PSPP, in
that charts can only be sent to a single output device.  This is because
the chart code calls into a single output driver to obtain a libplot
plotting context and then passes that back into the calling code, which
draws on it and passes it back to the output driver.  This commit refactors
the code so that, instead, a chart is an ADT that is reponsible for knowing
how to draw itself on a plotting device.  Then the function for outputting
a chart applies this function to each output device.

This is more complicated than necessary: we could just loop over the set of
output devices and draw a chart on each one in turn.  But this level of
complication is being introduced now so that later we can keep around
charts in memory as long as necessary for the GUI output engine to export
them on user demand.

This commit introduces a couple of regressions:

* libplot is now required, not optional.

* Box-whisker plots are disabled.

This regressions will be fixed by later commits.

21 files changed:
src/language/stats/examine.q
src/language/stats/frequencies.q
src/output/ascii.c
src/output/cairo.c
src/output/chart-provider.h [new file with mode: 0644]
src/output/chart.c
src/output/chart.h
src/output/charts/automake.mk
src/output/charts/cartesian.c
src/output/charts/cartesian.h
src/output/charts/piechart.c
src/output/charts/piechart.h
src/output/charts/plot-chart.c
src/output/charts/plot-chart.h
src/output/charts/plot-hist.c
src/output/charts/plot-hist.h
src/output/html.c
src/output/manager.c
src/output/manager.h
src/output/output.h
src/output/postscript.c

index f3fec17b9047af5b0f16b08eee28ffede82ec2b7..f0d831ba01201b726c858f303146469caa21ef26 100644 (file)
@@ -48,6 +48,7 @@
 #include <libpspp/misc.h>
 #include <libpspp/str.h>
 #include <math/moments.h>
+#include <output/chart-provider.h>
 #include <output/charts/box-whisker.h>
 #include <output/charts/cartesian.h>
 #include <output/manager.h>
@@ -320,26 +321,32 @@ cmd_examine (struct lexer *lexer, struct dataset *ds)
 };
 
 
+struct np_plot_chart
+  {
+    struct chart chart;
+    char *label;
+    struct casereader *data;
+
+    /* Copied directly from struct np. */
+    double y_min, y_max;
+    double dns_min, dns_max;
+
+    /* Calculated. */
+    double slope, intercept;
+    double y_first, y_last;
+    double x_lower, x_upper;
+    double slack;
+  };
+
+static const struct chart_class np_plot_chart_class;
+static const struct chart_class dnp_plot_chart_class;
+
 /* Plot the normal and detrended normal plots for RESULT.
    Label the plots with LABEL */
 static void
 np_plot (struct np *np, const char *label)
 {
-  double yfirst = 0, ylast = 0;
-
-  double x_lower;
-  double x_upper;
-  double slack;
-
-  /* Normal Plot */
-  struct chart *np_chart;
-
-  /* Detrended Normal Plot */
-  struct chart *dnp_chart;
-
-  /* The slope and intercept of the ideal normal probability line */
-  const double slope = 1.0 / np->stddev;
-  const double intercept = -np->mean / np->stddev;
+  struct np_plot_chart *np_plot, *dnp_plot;
 
   if ( np->n < 1.0 )
     {
@@ -347,56 +354,118 @@ np_plot (struct np *np, const char *label)
       return ;
     }
 
-  np_chart = chart_create ();
-  dnp_chart = chart_create ();
-
-  if ( !np_chart || ! dnp_chart )
-    return ;
+  np_plot = xmalloc (sizeof *np_plot);
+  chart_init (&np_plot->chart, &np_plot_chart_class);
+  np_plot->label = xstrdup (label);
+  np_plot->data = casewriter_make_reader (np->writer);
+  np_plot->y_min = np->y_min;
+  np_plot->y_max = np->y_max;
+  np_plot->dns_min = np->dns_min;
+  np_plot->dns_max = np->dns_max;
 
-  chart_write_title (np_chart, _("Normal Q-Q Plot of %s"), label);
-  chart_write_xlabel (np_chart, _("Observed Value"));
-  chart_write_ylabel (np_chart, _("Expected Normal"));
+  /* Slope and intercept of the ideal normal probability line. */
+  np_plot->slope = 1.0 / np->stddev;
+  np_plot->intercept = -np->mean / np->stddev;
 
-  chart_write_title (dnp_chart, _("Detrended Normal Q-Q Plot of %s"),
-                    label);
-  chart_write_xlabel (dnp_chart, _("Observed Value"));
-  chart_write_ylabel (dnp_chart, _("Dev from Normal"));
-
-  yfirst = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
-  ylast = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
+  np_plot->y_first = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
+  np_plot->y_last = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
 
   /* Need to make sure that both the scatter plot and the ideal fit into the
      plot */
-  x_lower = MIN (np->y_min, (yfirst - intercept) / slope) ;
-  x_upper = MAX (np->y_max, (ylast  - intercept) / slope) ;
-  slack = (x_upper - x_lower)  * 0.05 ;
+  np_plot->x_lower = MIN (
+    np->y_min, (np_plot->y_first - np_plot->intercept) / np_plot->slope);
+  np_plot->x_upper = MAX (
+    np->y_max, (np_plot->y_last  - np_plot->intercept) / np_plot->slope) ;
+  np_plot->slack = (np_plot->x_upper - np_plot->x_lower) * 0.05 ;
+
+  dnp_plot = xmemdup (np_plot, sizeof *np_plot);
+  chart_init (&dnp_plot->chart, &dnp_plot_chart_class);
+  dnp_plot->label = xstrdup (dnp_plot->label);
+  dnp_plot->data = casereader_clone (dnp_plot->data);
+
+  chart_submit (&np_plot->chart);
+  chart_submit (&dnp_plot->chart);
+}
 
-  chart_write_xscale (np_chart, x_lower - slack, x_upper + slack, 5);
-  chart_write_xscale (dnp_chart, np->y_min, np->y_max, 5);
+static void
+np_plot_chart_draw (const struct chart *chart, plPlotter *lp)
+{
+  const struct np_plot_chart *plot = (struct np_plot_chart *) chart;
+  struct chart_geometry geom;
+  struct casereader *data;
+  struct ccase *c;
 
-  chart_write_yscale (np_chart, yfirst, ylast, 5);
-  chart_write_yscale (dnp_chart, np->dns_min, np->dns_max, 5);
+  chart_geometry_init (lp, &geom);
+  chart_write_title (lp, &geom, _("Normal Q-Q Plot of %s"), plot->label);
+  chart_write_xlabel (lp, &geom, _("Observed Value"));
+  chart_write_ylabel (lp, &geom, _("Expected Normal"));
+  chart_write_xscale (lp, &geom,
+                      plot->x_lower - plot->slack,
+                      plot->x_upper + plot->slack, 5);
+  chart_write_yscale (lp, &geom, plot->y_first, plot->y_last, 5);
+
+  data = casereader_clone (plot->data);
+  for (; (c = casereader_read (data)) != NULL; case_unref (c))
+    chart_datum (lp, &geom, 0,
+                 case_data_idx (c, NP_IDX_Y)->f,
+                 case_data_idx (c, NP_IDX_NS)->f);
+  casereader_destroy (data);
+
+  chart_line (lp, &geom, plot->slope, plot->intercept,
+              plot->y_first, plot->y_last, CHART_DIM_Y);
+
+  chart_geometry_free (lp);
+}
 
-  {
-    struct casereader *reader = casewriter_make_reader (np->writer);
-    struct ccase *c;
-    while ((c = casereader_read (reader)) != NULL)
-      {
-       chart_datum (np_chart, 0, case_data_idx (c, NP_IDX_Y)->f, case_data_idx (c, NP_IDX_NS)->f);
-       chart_datum (dnp_chart, 0, case_data_idx (c, NP_IDX_Y)->f, case_data_idx (c, NP_IDX_DNS)->f);
+static void
+dnp_plot_chart_draw (const struct chart *chart, plPlotter *lp)
+{
+  const struct np_plot_chart *plot = (struct np_plot_chart *) chart;
+  struct chart_geometry geom;
+  struct casereader *data;
+  struct ccase *c;
 
-       case_unref (c);
-      }
-    casereader_destroy (reader);
-  }
+  chart_geometry_init (lp, &geom);
+  chart_write_title (lp, &geom, _("Detrended Normal Q-Q Plot of %s"),
+                     plot->label);
+  chart_write_xlabel (lp, &geom, _("Observed Value"));
+  chart_write_ylabel (lp, &geom, _("Dev from Normal"));
+  chart_write_xscale (lp, &geom, plot->y_min, plot->y_max, 5);
+  chart_write_yscale (lp, &geom, plot->dns_min, plot->dns_max, 5);
+
+  data = casereader_clone (plot->data);
+  for (; (c = casereader_read (data)) != NULL; case_unref (c))
+    chart_datum (lp, &geom, 0, case_data_idx (c, NP_IDX_Y)->f,
+                 case_data_idx (c, NP_IDX_DNS)->f);
+  casereader_destroy (data);
 
-  chart_line (dnp_chart, 0, 0, np->y_min, np->y_max , CHART_DIM_X);
-  chart_line (np_chart, slope, intercept, yfirst, ylast , CHART_DIM_Y);
+  chart_line (lp, &geom, 0, 0, plot->y_min, plot->y_max, CHART_DIM_X);
 
-  chart_submit (np_chart);
-  chart_submit (dnp_chart);
+  chart_geometry_free (lp);
 }
 
+static void
+np_plot_chart_destroy (struct chart *chart)
+{
+  struct np_plot_chart *plot = (struct np_plot_chart *) chart;
+
+  casereader_destroy (plot->data);
+  free (plot->label);
+  free (plot);
+}
+
+static const struct chart_class np_plot_chart_class =
+  {
+    np_plot_chart_draw,
+    np_plot_chart_destroy
+  };
+
+static const struct chart_class dnp_plot_chart_class =
+  {
+    dnp_plot_chart_draw,
+    np_plot_chart_destroy
+  };
+
 
 static void
 show_npplot (const struct variable **dependent_var,
@@ -448,8 +517,16 @@ show_histogram (const struct variable **dependent_var,
          struct string str;
          const struct factor_result *result =
            ll_data (ll, struct factor_result, ll);
+          struct histogram *histogram;
           double mean, var, n;
 
+          histogram = (struct histogram *) result->metrics[v].histogram;
+          if (histogram == NULL)
+            {
+              /* Probably all values are SYSMIS. */
+              continue;
+            }
+
          ds_init_empty (&str);
          ds_put_format (&str, "%s ", var_get_name (dependent_var[v]));
 
@@ -457,9 +534,8 @@ show_histogram (const struct variable **dependent_var,
 
           moments1_calculate ((struct moments1 *) result->metrics[v].moments,
                               &n, &mean, &var, NULL,  NULL);
-         histogram_plot ((struct histogram *) result->metrics[v].histogram,
-                         ds_cstr (&str),
-                         n, mean, sqrt (var), false);
+          chart_submit (histogram_chart_create (histogram, ds_cstr (&str),
+                                                n, mean, sqrt (var), false));
 
          ds_destroy (&str);
        }
@@ -473,6 +549,7 @@ show_boxplot_groups (const struct variable **dependent_var,
                     int n_dep_var,
                     const struct xfactor *fctr)
 {
+#if 0
   int v;
 
   for (v = 0; v < n_dep_var; ++v)
@@ -550,6 +627,7 @@ show_boxplot_groups (const struct variable **dependent_var,
 
       chart_submit (ch);
     }
+#endif
 }
 
 
@@ -561,6 +639,7 @@ show_boxplot_variables (const struct variable **dependent_var,
                        )
 
 {
+#if 0
   int v;
   struct ll *ll;
   const struct ll_list *result_list = &fctr->result_list;
@@ -630,6 +709,7 @@ show_boxplot_variables (const struct variable **dependent_var,
 
       chart_submit (ch);
     }
+#endif
 }
 
 
index 91ae67282863ea94a9ed4ebaaef9d2c4f9ccb561..77b1d5f0ab9533321e8f909dbb5141a669cf2f14 100644 (file)
@@ -610,11 +610,12 @@ postcalc (const struct dataset *ds)
 
          hist = freq_tab_to_hist (ft,v);
 
-         histogram_plot (hist, var_to_string(v),
+          chart_submit (histogram_chart_create (
+                          hist, var_to_string(v),
                          vf->tab.valid_cases,
                          d[frq_mean],
                          d[frq_stddev],
-                         normal);
+                         normal));
 
          statistic_destroy ((struct statistic *)hist);
        }
@@ -1532,14 +1533,12 @@ do_piechart(const struct variable *var, const struct freq_tab *frq_tab)
 
   slices = freq_tab_to_slice_array(frq_tab, var, &n_slices);
 
-  piechart_plot(var_to_string(var), slices, n_slices);
+  chart_submit (piechart_create (var_to_string(var), slices, n_slices));
 
   for (i = 0 ; i < n_slices ; ++i )
-    {
-      ds_destroy (&slices[i].label);
-    }
+    ds_destroy (&slices[i].label);
 
-  free(slices);
+  free (slices);
 }
 
 
index 5a33a6611bfdd8d6f4c7fc4a602ff1a7c92e8717..c01e36cc3d91ecdbfd9c0960bc9106daa865a729 100644 (file)
 #include <libpspp/pool.h>
 #include <libpspp/start-date.h>
 #include <libpspp/version.h>
+#include <output/chart-provider.h>
+#include <output/chart.h>
+#include <output/output.h>
 
-#include "chart.h"
 #include "error.h"
 #include "minmax.h"
-#include "output.h"
 #include "xalloc.h"
 
 #include "gettext.h"
@@ -174,7 +175,7 @@ ascii_open_driver (const char *name, int types, struct substring options)
   x->page_number = 0;
   x->lines = NULL;
   x->line_cap = 0;
-  x->chart_cnt = 0;
+  x->chart_cnt = 1;
 
   if (!outp_parse_options (this->name, options, handle_option, this))
     goto error;
@@ -609,15 +610,6 @@ ascii_line (struct outp_driver *this,
     }
 }
 
-static void
-ascii_submit (struct outp_driver *this UNUSED, struct som_entity *s)
-{
-  extern struct som_table_class tab_table_class;
-
-  assert (s->class == &tab_table_class);
-  assert (s->type == SOM_CHART);
-}
-
 static void
 text_draw (struct outp_driver *this,
            enum outp_font font,
@@ -872,19 +864,24 @@ ascii_flush (struct outp_driver *this)
 }
 
 static void
-ascii_chart_initialise (struct outp_driver *this, struct chart *ch)
+ascii_output_chart (struct outp_driver *this, const struct chart *chart)
 {
   struct ascii_driver_ext *x = this->ext;
   struct outp_text t;
+  char *file_name;
+  plPlotter *lp;
   char *text;
 
   if (x->chart_type == NULL)
     return;
 
-  /* Initialize chart. */
-  chart_init_separate (ch, x->chart_type, x->chart_file_name, ++x->chart_cnt);
-  if (ch->file_name == NULL)
+  /* Draw chart in separate file. */
+  if (!chart_create_file (x->chart_type, x->chart_file_name, x->chart_cnt,
+                          NULL, &file_name, &lp))
     return;
+  x->chart_cnt++;
+  chart_draw (chart, lp);
+  pl_deletepl_r (lp);
 
   /* Mention chart in output.
      First advance current position. */
@@ -901,7 +898,7 @@ ascii_chart_initialise (struct outp_driver *this, struct chart *ch)
     }
 
   /* Then write the text. */
-  text = xasprintf ("See %s for a chart.", ch->file_name);
+  text = xasprintf ("See %s for a chart.", file_name);
   t.font = OUTP_FIXED;
   t.justification = OUTP_LEFT;
   t.string = ss_cstr (text);
@@ -912,17 +909,10 @@ ascii_chart_initialise (struct outp_driver *this, struct chart *ch)
   ascii_text_draw (this, &t);
   this->cp_y++;
 
+  free (file_name);
   free (text);
 }
 
-static void
-ascii_chart_finalise (struct outp_driver *this, struct chart *ch)
-{
-  struct ascii_driver_ext *x = this->ext;
-  if (x->chart_type != NULL)
-    chart_finalise_separate (ch);
-}
-
 const struct outp_class ascii_class =
 {
   "ascii",
@@ -935,12 +925,11 @@ const struct outp_class ascii_class =
   ascii_close_page,
   ascii_flush,
 
-  ascii_submit,
+  ascii_output_chart,
+
+  NULL,                         /* submit */
 
   ascii_line,
   ascii_text_metrics,
   ascii_text_draw,
-
-  ascii_chart_initialise,
-  ascii_chart_finalise
 };
index f99f7f1d5317e1430f5bdfb719f7d6fee5b22d71..9384232841c304f72019abdf403e2449e62dad07 100644 (file)
@@ -502,18 +502,6 @@ xr_close_page (struct outp_driver *this)
   struct xr_driver_ext *x = this->ext;
   cairo_show_page (x->cairo);
 }
-
-static void
-xr_submit (struct outp_driver *this UNUSED, struct som_entity *s)
-{
-  switch (s->type)
-    {
-    case SOM_CHART:
-      break;
-    default:
-      NOT_REACHED ();
-    }
-}
 \f
 /* Draws a line from (x0,y0) to (x1,y1). */
 static void
@@ -840,24 +828,6 @@ xr_text_draw (struct outp_driver *this, const struct outp_text *t)
   text (this, t, true, NULL, NULL);
 }
 \f
-static void
-xr_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
-{
-#ifdef NO_CHARTS
-  ch->lp = NULL;
-#else
-  /* XXX libplot doesn't support Cairo yet. */
-#endif
-}
-
-static void
-xr_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
-{
-#ifndef NO_CHARTS
-  /* XXX libplot doesn't support Cairo yet. */
-#endif
-}
-\f
 /* Attempts to load FONT, initializing its other members based on
    its 'string' member and the information in THIS.  Returns true
    if successful, otherwise false. */
@@ -910,12 +880,11 @@ const struct outp_class cairo_class =
   xr_close_page,
   NULL,
 
-  xr_submit,
+  NULL,
+
+  NULL,
 
   xr_line,
   xr_text_metrics,
   xr_text_draw,
-
-  xr_chart_initialise,
-  xr_chart_finalise
 };
diff --git a/src/output/chart-provider.h b/src/output/chart-provider.h
new file mode 100644 (file)
index 0000000..4b36c55
--- /dev/null
@@ -0,0 +1,74 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
+
+   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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHART_PROVIDER_H
+#define OUTPUT_CHART_PROVIDER_H 1
+
+#include <stdbool.h>
+#include <output/chart.h>
+
+struct chart_class
+  {
+    void (*draw) (const struct chart *, plPlotter *);
+    void (*destroy) (struct chart *);
+  };
+
+struct chart
+  {
+    const struct chart_class *class;
+    int ref_cnt;
+  };
+
+void chart_init (struct chart *, const struct chart_class *);
+bool chart_create_file (const char *type, const char *file_name_tmpl,
+                        int number, plPlotterParams *,
+                        char **file_namep, plPlotter **lpp);
+
+/* The geometry of a chart. */
+struct chart_geometry
+  {
+    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];
+
+    /* Stuff Particular to Cartesians (and Boxplots ) */
+    double ordinate_scale;
+    double abscissa_scale;
+    double x_min;
+    double x_max;
+    double y_min;
+    double y_max;
+  };
+
+void chart_geometry_init (plPlotter *, struct chart_geometry *);
+void chart_geometry_free (plPlotter *);
+
+#endif /* output/chart-provider.h */
index 49819e9fe03cc4073242ae97d05b77365c7e78a2..b096a7d2195fc632c863a9e01a7319ea3dceeb80 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
 
    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
 #include <config.h>
 
 #include <output/chart.h>
+#include <output/chart-provider.h>
 
 #include <assert.h>
 #include <errno.h>
 #include <float.h>
 #include <math.h>
+#include <plot.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include <plot.h>
-
 #include <libpspp/str.h>
 #include <output/manager.h>
 #include <output/output.h>
 
 extern struct som_table_class tab_table_class;
 
-struct chart *
-chart_create(void)
+void
+chart_init (struct chart *chart, const struct chart_class *class)
 {
-  struct chart *chart;
-  struct outp_driver *d;
-
-  d = outp_drivers (NULL);
-  if (d == NULL)
-    return NULL;
-
-  chart = xmalloc (sizeof *chart);
-  chart->lp = NULL;
-  d->class->initialise_chart(d, chart);
-  if (!chart->lp)
-    {
-      free (chart);
-      return NULL;
-    }
-
-  if (pl_openpl_r (chart->lp) < 0)      /* open Plotter */
-    return NULL;
-
-  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);
+  chart->class = class;
+  chart->ref_cnt = 1;
+}
 
-  /* 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");
+void
+chart_geometry_init (plPlotter *lp, struct chart_geometry *geom)
+{
+  /* Start output page. */
+  pl_openpl_r (lp);
+
+  /* Set coordinate system. */
+  pl_fspace_r (lp, 0.0, 0.0, 1000.0, 1000.0);
+
+  /* Set line thickness. */
+  pl_flinewidth_r (lp, 0.25);
+  pl_pencolorname_r (lp, "black");
+
+  /* Erase graphics display. */
+  pl_erase_r (lp);
+
+  pl_filltype_r (lp, 0);
+  pl_savestate_r(lp);
+
+  /* Set default chartetry. */
+  geom->data_top = 900;
+  geom->data_right = 800;
+  geom->data_bottom = 120;
+  geom->data_left = 150;
+  geom->abscissa_top = 70;
+  geom->ordinate_right = 120;
+  geom->title_bottom = 920;
+  geom->legend_left = 810;
+  geom->legend_right = 1000;
+  geom->font_size = 0;
+  strcpy (geom->fill_colour, "red");
 
   /* Get default font size */
-  if ( !chart->font_size)
-    chart->font_size = pl_fontsize_r(chart->lp, -1);
+  if (!geom->font_size)
+    geom->font_size = pl_fontsize_r (lp, -1);
 
   /* Draw the data area */
-  pl_box_r(chart->lp,
-          chart->data_left, chart->data_bottom,
-          chart->data_right, chart->data_top);
-
-  return chart;
+  pl_box_r (lp,
+           geom->data_left, geom->data_bottom,
+           geom->data_right, geom->data_top);
 }
 
 void
-chart_submit(struct chart *chart)
+chart_geometry_free (plPlotter *lp)
 {
-  struct som_entity s;
-  struct outp_driver *d;
-
-  if ( ! chart )
-     return ;
+  if (pl_closepl_r (lp) < 0)
+    fprintf (stderr, "Couldn't close Plotter\n");
+}
 
-  pl_restorestate_r(chart->lp);
+void
+chart_draw (const struct chart *chart, plPlotter *lp)
+{
+  chart->class->draw (chart, lp);
+}
 
-  s.class = &tab_table_class;
-  s.ext = chart;
-  s.type = SOM_CHART;
-  som_submit (&s);
+struct chart *
+chart_ref (const struct chart *chart_)
+{
+  struct chart *chart = (struct chart *) chart_;
+  chart->ref_cnt++;
+  return chart;
+}
 
-  if (pl_closepl_r (chart->lp) < 0)     /* close Plotter */
-    {
-      fprintf (stderr, "Couldn't close Plotter\n");
-    }
+void
+chart_unref (struct chart *chart)
+{
+  assert (chart->ref_cnt > 0);
+  if (--chart->ref_cnt == 0)
+    chart->class->destroy (chart);
+}
 
-  pl_deletepl_r(chart->lp);
+void
+chart_submit (struct chart *chart)
+{
+  struct outp_driver *d;
 
-  pl_deleteplparams(chart->pl_params);
+  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
+    if (d->class->output_chart != NULL)
+      d->class->output_chart (d, chart);
 
-  d = outp_drivers (NULL);
-  d->class->finalise_chart(d, chart);
-  free(chart);
+  chart_unref (chart);
 }
 
-void
-chart_init_separate (struct chart *ch, const char *type,
-                     const char *file_name_tmpl, int number)
+bool
+chart_create_file (const char *type, const char *file_name_tmpl, int number,
+                   plPlotterParams *params, char **file_namep, plPlotter **lpp)
 {
-  FILE *fp;
+  char *file_name = NULL;
+  FILE *fp = NULL;
   int number_pos;
+  plPlotter *lp;
 
   number_pos = strchr (file_name_tmpl, '#') - file_name_tmpl;
-  ch->file_name = xasprintf ("%.*s%d%s",
-                             number_pos, file_name_tmpl,
-                             number,
-                             file_name_tmpl + number_pos + 1);
-  fp = fopen (ch->file_name, "wb");
+  file_name = xasprintf ("%.*s%d%s", number_pos, file_name_tmpl,
+                         number, file_name_tmpl + number_pos + 1);
+
+  fp = fopen (file_name, "wb");
   if (fp == NULL)
     {
-      error (0, errno, _("creating \"%s\""), ch->file_name);
-      free (ch->file_name);
-      ch->file_name = NULL;
-      return;
+      error (0, errno, _("creating \"%s\""), file_name);
+      goto error;
     }
 
-  ch->pl_params = pl_newplparams ();
-  ch->lp = pl_newpl_r (type, 0, fp, stderr, ch->pl_params);
-}
+  if (params != NULL)
+    lp = pl_newpl_r (type, 0, fp, stderr, params);
+  else
+    {
+      params = pl_newplparams ();
+      lp = pl_newpl_r (type, 0, fp, stderr, params);
+      pl_deleteplparams (params);
+    }
+  if (lp == NULL)
+    goto error;
 
-void
-chart_finalise_separate (struct chart *ch)
-{
-  free (ch->file_name);
+  *file_namep = file_name;
+  *lpp = lp;
+  return true;
+
+error:
+  if (fp != NULL)
+    {
+      fclose (fp);
+      if (file_name != NULL)
+        unlink (file_name);
+    }
+  free (file_name);
+  *file_namep = NULL;
+  *lpp = NULL;
+  return false;
 }
index 9586eb1e068aba5f480b758a79db4463c8ae465a..93762dbd4fb311df363d56d40cb23b583879806f 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2009 Free Software Foundation, Inc.
 
    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
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-#include <float.h>
-#include <assert.h>
-#include <math.h>
-
-#include <math/chart-geometry.h>
-#include <libpspp/str.h>
-#include "manager.h"
-#include "output.h"
-
-#include "xalloc.h"
-
-#ifndef CHART_H
-#define CHART_H
+#ifndef OUTPUT_CHART_H
+#define OUTPUT_CHART_H 1
 
 #ifndef NO_CHARTS
+#include <stdio.h>              /* Required by <plot.h>. */
 #include <plot.h>
 #endif
 
-struct chart {
-
-#ifndef NO_CHARTS
-  plPlotter *lp ;
-  plPlotterParams *pl_params;
-#else
-  void *lp;
-#endif
-  char *file_name;
-  FILE *file;
-
-  /* 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 ;
+struct chart;
 
-  int title_bottom ;
+void chart_draw (const struct chart *, plPlotter *);
+struct chart *chart_ref (const struct chart *);
+void chart_unref (struct chart *);
 
-  int legend_left ;
-  int legend_right ;
+void chart_submit (struct chart *);
 
-
-  /* Default font size for the plot (if zero, then use plotter default) */
-  int font_size;
-
-  char fill_colour[10];
-
-  /* Stuff Particular to Cartesians (and Boxplots ) */
-  double ordinate_scale;
-  double abscissa_scale;
-  double x_min;
-  double x_max;
-  double y_min;
-  double y_max;
-};
-
-
-
-struct chart * chart_create(void);
-void chart_submit(struct chart *ch);
-
-/* Helper functions for output drivers that put each chart into a
-   separate file. */
-void chart_init_separate (struct chart *, const char *type,
-                          const char *file_name_tmpl, int number);
-
-void chart_finalise_separate (struct chart *);
-
-#endif
+#endif /* output/chart.h */
index ab0ff51047a4ffeb608770c3b088df8843077204..e260f3283a9facf7208957ae82924b76aa831107 100644 (file)
@@ -3,10 +3,6 @@
 noinst_LTLIBRARIES += src/output/charts/libcharts.la
 
 chart_sources = \
-       src/output/charts/barchart.c \
-       src/output/charts/barchart.h \
-       src/output/charts/box-whisker.c \
-       src/output/charts/box-whisker.h \
        src/output/charts/cartesian.c \
        src/output/charts/cartesian.h \
        src/output/charts/piechart.c \
index 75c49aa88c9d4fc84ec56d9439ae8560f3e2a09b..ffa6e3c5323c5002d90b7c6868e67a88ef94c936 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
 
    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
 
 #include <config.h>
 
+#include <output/charts/cartesian.h>
+
 #include <math.h>
 #include <assert.h>
 
 #include <output/chart.h>
-
+#include <output/chart-provider.h>
 #include <output/charts/plot-chart.h>
-#include <output/charts/cartesian.h>
 #include <libpspp/compiler.h>
 
-
-
 struct dataset
 {
   int n_data;
@@ -46,24 +45,15 @@ static const struct dataset dataset[DATASETS] =
 
 /* Plot a data point */
 void
-chart_datum(struct chart *ch, int dataset UNUSED, double x, double y)
+chart_datum (plPlotter *lp, const struct chart_geometry *geom,
+             int dataset UNUSED, double x, double y)
 {
-  if ( ! ch )
-    return ;
-
-  {
-    const double x_pos =
-      (x - ch->x_min) * ch->abscissa_scale + ch->data_left ;
+  double x_pos = (x - geom->x_min) * geom->abscissa_scale + geom->data_left;
+  double y_pos = (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
 
-    const double y_pos =
-      (y - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-
-    pl_savestate_r(ch->lp);
-
-    pl_fmarker_r(ch->lp, x_pos, y_pos, 6, 15);
-
-    pl_restorestate_r(ch->lp);
-  }
+  pl_savestate_r (lp);
+  pl_fmarker_r (lp, x_pos, y_pos, 6, 15);
+  pl_restorestate_r (lp);
 }
 
 /* Draw a line with slope SLOPE and intercept INTERCEPT.
@@ -72,20 +62,17 @@ chart_datum(struct chart *ch, int dataset UNUSED, double x, double y)
    y axis otherwise the x axis
 */
 void
-chart_line(struct chart *ch, double slope, double intercept,
+chart_line(plPlotter *lp, const struct chart_geometry *geom,
+           double slope, double intercept,
           double limit1, double limit2, enum CHART_DIM lim_dim)
 {
   double x1, y1;
-  double x2, y2 ;
-
-  if ( ! ch )
-    return ;
-
+  double x2, y2;
 
   if ( lim_dim == CHART_DIM_Y )
     {
-      x1 = ( limit1 - intercept ) / slope ;
-      x2 = ( limit2 - intercept ) / slope ;
+      x1 = ( limit1 - intercept ) / slope;
+      x2 = ( limit2 - intercept ) / slope;
       y1 = limit1;
       y2 = limit2;
     }
@@ -97,15 +84,12 @@ chart_line(struct chart *ch, double slope, double intercept,
       y2 = slope * x2 + intercept;
     }
 
-  y1 = (y1 - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-  y2 = (y2 - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-  x1 = (x1 - ch->x_min) * ch->abscissa_scale + ch->data_left ;
-  x2 = (x2 - ch->x_min) * ch->abscissa_scale + ch->data_left ;
-
-  pl_savestate_r(ch->lp);
-
-  pl_fline_r(ch->lp, x1, y1, x2, y2);
-
-  pl_restorestate_r(ch->lp);
+  y1 = (y1 - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
+  y2 = (y2 - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
+  x1 = (x1 - geom->x_min) * geom->abscissa_scale + geom->data_left;
+  x2 = (x2 - geom->x_min) * geom->abscissa_scale + geom->data_left;
 
+  pl_savestate_r (lp);
+  pl_fline_r (lp, x1, y1, x2, y2);
+  pl_restorestate_r (lp);
 }
index 40e3f71455f5886813eee2d3e63e04d55b3fe095..15fb8840b1b80a667519d530d09c38a80bab83d3 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
 
    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
 #ifndef CARTESIAN_H
 #define CARTESIAN_H
 
+#include <stdio.h>
+#include <plot.h>
+
+#include <libpspp/compiler.h>
 
 enum CHART_DIM
   {
@@ -26,17 +30,19 @@ enum CHART_DIM
     CHART_DIM_Y
   };
 
-
+struct chart_geometry;
 
 /* Plot a data point */
-void chart_datum(struct chart *ch, int dataset UNUSED, double x, double y);
+void chart_datum(plPlotter *, const struct chart_geometry *,
+                 int dataset UNUSED, double x, double y);
 
 /* Draw a line with slope SLOPE and intercept INTERCEPT.
    between the points limit1 and limit2.
    If lim_dim is CHART_DIM_Y then the limit{1,2} are on the
    y axis otherwise the x axis
 */
-void chart_line(struct chart *ch, double slope, double intercept,
+void chart_line(plPlotter *, const struct chart_geometry *,
+                double slope, double intercept,
                double limit1, double limit2, enum CHART_DIM lim_dim);
 
 
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
+  };
index 288a44e638b8bf29a73cb521c19246182780d0f2..39a0c2d5d28e7c05766b93e62a4f0d91b6f4e292 100644 (file)
@@ -24,9 +24,8 @@ struct slice {
   double magnitude;
 };
 
-/* Draw a piechart */
-void piechart_plot(const char *title,
-                  const struct slice *slices, int n_slices);
+struct chart *piechart_create (const char *title,
+                               const struct slice *, int n_slices);
 
 #endif
 
index 3b4f1b377a12a624ceb929d00c617bb4bd581f30..75f21b66d051b6171b559c05d29032af5504d622 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
 
    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
@@ -16,6 +16,8 @@
 
 #include <config.h>
 
+#include <output/charts/plot-chart.h>
+
 #include <stdio.h>
 #include <plot.h>
 #include <stdarg.h>
 #include <assert.h>
 #include <math.h>
 
-#include <output/charts/plot-chart.h>
-
-#include <math/chart-geometry.h>
-
-
-
-#include <libpspp/str.h>
 #include <libpspp/assertion.h>
+#include <libpspp/str.h>
+#include <math/chart-geometry.h>
+#include <output/chart-provider.h>
 #include <output/manager.h>
 #include <output/output.h>
 
@@ -57,156 +55,130 @@ const char *const data_colour[N_CHART_COLOURS] =
    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, ...)
+draw_tick (plPlotter *lp, const struct chart_geometry *geom,
+           enum tick_orientation orientation,
+           double position,
+           const char *label, ...)
 {
   const int tickSize = 10;
 
-  assert(chart);
-
-  pl_savestate_r(chart->lp);
-
-  pl_move_r(chart->lp, chart->data_left, chart->data_bottom);
+  pl_savestate_r (lp);
+  pl_move_r (lp, geom->data_left, geom->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);
+  if (orientation == TICK_ABSCISSA)
+    pl_flinerel_r (lp, position, 0, position, -tickSize);
+  else if (orientation == TICK_ORDINATE)
+    pl_flinerel_r (lp, 0, position, -tickSize, position);
   else
     NOT_REACHED ();
 
-  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);
-  }
+  if (label != NULL)
+    {
+      va_list ap;
+      char *s;
+
+      va_start (ap, label);
+      s = xvasprintf (label, ap);
+      if (orientation == TICK_ABSCISSA)
+        pl_alabel_r (lp, 'c', 't', s);
+      else if (orientation == TICK_ORDINATE)
+        {
+          if (fabs (position) < DBL_EPSILON)
+           pl_moverel_r (lp, 0, 10);
+          pl_alabel_r (lp, 'r', 'c', s);
+        }
+      free (s);
+      va_end (ap);
+    }
 
-  pl_restorestate_r(chart->lp);
+  pl_restorestate_r (lp);
 }
 
 
 /* Write the title on a chart*/
 void
-chart_write_title(struct chart *chart, const char *title, ...)
+chart_write_title (plPlotter *lp, const struct chart_geometry *geom,
+                   const char *title, ...)
 {
   va_list ap;
-  char buf[100];
+  char *s;
 
-  if ( ! chart )
-         return ;
+  pl_savestate_r (lp);
+  pl_ffontsize_r (lp, geom->font_size * 1.5);
+  pl_move_r (lp, geom->data_left, geom->title_bottom);
 
-  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);
+  va_start(ap, title);
+  s = xvasprintf (title, ap);
+  pl_alabel_r (lp, 0, 0, s);
+  free (s);
+  va_end (ap);
 
-  va_start(ap,title);
-  vsnprintf(buf,100,title,ap);
-  pl_alabel_r(chart->lp,0,0,buf);
-  va_end(ap);
-
-  pl_restorestate_r(chart->lp);
+  pl_restorestate_r (lp);
 }
 
 
 /* Set the scale for the abscissa */
 void
-chart_write_xscale(struct chart *ch, double min, double max, int ticks)
+chart_write_xscale (plPlotter *lp, struct chart_geometry *geom,
+                    double min, double max, int ticks)
 {
   double x;
 
   const double tick_interval =
-    chart_rounded_tick( (max - min) / (double) ticks);
-
-  assert ( ch );
+    chart_rounded_tick ((max - min) / (double) ticks);
 
+  geom->x_max = ceil (max / tick_interval) * tick_interval;
+  geom->x_min = floor (min / tick_interval) * tick_interval;
+  geom->abscissa_scale = fabs(geom->data_right - geom->data_left) /
+    fabs(geom->x_max - geom->x_min);
 
-  ch->x_max = ceil( max / tick_interval ) * tick_interval ;
-  ch->x_min = floor ( min / tick_interval ) * tick_interval ;
-
-
-  ch->abscissa_scale = fabs(ch->data_right - ch->data_left) /
-    fabs(ch->x_max - ch->x_min);
-
-  for(x = ch->x_min ; x <= ch->x_max; x += tick_interval )
-    {
-      draw_tick (ch, TICK_ABSCISSA,
-                (x - ch->x_min) * ch->abscissa_scale, "%g", x);
-    }
-
+  for (x = geom->x_min; x <= geom->x_max; x += tick_interval)
+    draw_tick (lp, geom, TICK_ABSCISSA,
+               (x - geom->x_min) * geom->abscissa_scale, "%g", x);
 }
 
 
 /* Set the scale for the ordinate */
 void
-chart_write_yscale(struct chart *ch, double smin, double smax, int ticks)
+chart_write_yscale (plPlotter *lp, struct chart_geometry *geom,
+                    double smin, double smax, int ticks)
 {
   double y;
 
   const double tick_interval =
-    chart_rounded_tick( (smax - smin) / (double) ticks);
-
-  if ( !ch )
-         return;
+    chart_rounded_tick ((smax - smin) / (double) ticks);
 
-  ch->y_max = ceil  ( smax / tick_interval ) * tick_interval ;
-  ch->y_min = floor ( smin / tick_interval ) * tick_interval ;
+  geom->y_max = ceil (smax / tick_interval) * tick_interval;
+  geom->y_min = floor (smin / tick_interval) * tick_interval;
 
-  ch->ordinate_scale =
-    fabs(ch->data_top -  ch->data_bottom) / fabs(ch->y_max - ch->y_min) ;
+  geom->ordinate_scale =
+    (fabs (geom->data_top - geom->data_bottom)
+     / fabs (geom->y_max - geom->y_min));
 
-  for(y = ch->y_min ; y <= ch->y_max; y += tick_interval )
-    {
-    draw_tick (ch, TICK_ORDINATE,
-              (y - ch->y_min) * ch->ordinate_scale, "%g", y);
-    }
+  for (y = geom->y_min; y <= geom->y_max; y += tick_interval)
+    draw_tick (lp, geom, TICK_ORDINATE,
+              (y - geom->y_min) * geom->ordinate_scale, "%g", y);
 }
 
-
 /* Write the abscissa label */
 void
-chart_write_xlabel(struct chart *ch, const char *label)
+chart_write_xlabel (plPlotter *lp, const struct chart_geometry *geom,
+                    const char *label)
 {
-  if ( ! ch )
-    return ;
-
-  pl_savestate_r(ch->lp);
-
-  pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
-  pl_alabel_r(ch->lp,0,'t',label);
-
-  pl_restorestate_r(ch->lp);
-
+  pl_savestate_r (lp);
+  pl_move_r (lp, geom->data_left, geom->abscissa_top);
+  pl_alabel_r (lp, 0, 't', label);
+  pl_restorestate_r (lp);
 }
 
-
-
 /* Write the ordinate label */
 void
-chart_write_ylabel(struct chart *ch, const char *label)
+chart_write_ylabel (plPlotter *lp, const struct chart_geometry *geom,
+                    const char *label)
 {
-  if ( ! ch )
-    return ;
-
-  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, label);
-
-  pl_restorestate_r(ch->lp);
+  pl_savestate_r (lp);
+  pl_move_r (lp, geom->data_bottom, geom->ordinate_right);
+  pl_textangle_r (lp, 90);
+  pl_alabel_r (lp, 0, 0, label);
+  pl_restorestate_r(lp);
 }
index 9a4df52052392cf1769dc1d2bade682a95af1e18..59d385bb569b6c433a30a19569ec1e451717bcb0 100644 (file)
@@ -45,32 +45,39 @@ enum tick_orientation
     TICK_ORDINATE
   };
 
+struct chart_geometry;
+
 
 /* Draw a tick mark at position
    If label is non zero, then print it at the tick mark
 */
-void draw_tick(struct chart *chart,
+void draw_tick(plPlotter *, const struct chart_geometry *,
          enum tick_orientation orientation,
          double position,
               const char *label, ...)
-  PRINTF_FORMAT (4, 5);
+  PRINTF_FORMAT (5, 6);
 
 
 /* Write the title on a chart*/
-void   chart_write_title(struct chart *chart, const char *title, ...)
-  PRINTF_FORMAT (2, 3);
+void   chart_write_title(plPlotter *, const struct chart_geometry *,
+                         const char *title, ...)
+  PRINTF_FORMAT (3, 4);
 
 
 /* Set the scale for the abscissa */
-void  chart_write_xscale(struct chart *ch, double min, double max, int ticks);
+void  chart_write_xscale(plPlotter *, struct chart_geometry *,
+                         double min, double max, int ticks);
 
 
 /* Set the scale for the ordinate */
-void  chart_write_yscale(struct chart *ch, double smin, double smax, int ticks);
+void  chart_write_yscale(plPlotter *, struct chart_geometry *,
+                         double smin, double smax, int ticks);
 
-void chart_write_xlabel(struct chart *ch, const char *label) ;
+void chart_write_xlabel(plPlotter *, const struct chart_geometry *,
+                        const char *label) ;
 
 /* Write the ordinate label */
-void  chart_write_ylabel(struct chart *ch, const char *label);
+void  chart_write_ylabel(plPlotter *, const struct chart_geometry *,
+                         const char *label);
 
 #endif
index b90d57ac3cb01df951fe52e04f1381871c0b8423..ab3b988daaf8c9b9bb8e98eeaace831f0ac09e68 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <output/charts/plot-hist.h>
 #include <output/charts/plot-chart.h>
+#include <output/chart-provider.h>
 
 #include <data/variable.h>
 #include <libpspp/hash.h>
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
+static const struct chart_class histogram_chart_class;
+
 /* Write the legend of the chart */
 static void
-histogram_write_legend (struct chart *ch, double n, double mean, double stddev)
+histogram_write_legend (plPlotter *lp, const struct chart_geometry *geom,
+                        double n, double mean, double stddev)
 {
-  double y;
-  char buf[100];
-
-  if (!ch)
-    return ;
-
-  y = ch->data_bottom;
-  pl_savestate_r (ch->lp);
+  double y = geom->data_bottom;
+  pl_savestate_r (lp);
 
   if (n != SYSMIS)
     {
-      sprintf (buf, "N = %.2f", n);
-      pl_move_r (ch->lp, ch->legend_left, y);
-      pl_alabel_r (ch->lp, 0, 'b', buf);
-      y += ch->font_size * 1.5;
+      char *buf = xasprintf ("N = %.2f", n);
+      pl_move_r (lp, geom->legend_left, y);
+      pl_alabel_r (lp, 0, 'b', buf);
+      y += geom->font_size * 1.5;
+      free (buf);
     }
 
   if (mean != SYSMIS)
     {
-      sprintf (buf, "Mean = %.1f", mean);
-      pl_fmove_r (ch->lp,ch->legend_left, y);
-      pl_alabel_r (ch->lp, 0, 'b', buf);
-      y += ch->font_size * 1.5;
+      char *buf = xasprintf ("Mean = %.1f", mean);
+      pl_fmove_r (lp,geom->legend_left, y);
+      pl_alabel_r (lp, 0, 'b', buf);
+      y += geom->font_size * 1.5;
+      free (buf);
     }
 
   if (stddev != SYSMIS)
     {
-      sprintf (buf, "Std. Dev = %.2f", stddev);
-      pl_fmove_r (ch->lp, ch->legend_left, y);
-      pl_alabel_r (ch->lp, 0, 'b', buf);
+      char *buf = xasprintf ("Std. Dev = %.2f", stddev);
+      pl_fmove_r (lp, geom->legend_left, y);
+      pl_alabel_r (lp, 0, 'b', buf);
+      free (buf);
     }
 
-  pl_restorestate_r (ch->lp);
+  pl_restorestate_r (lp);
 }
 
-static void hist_draw_bar (struct chart *ch, const struct histogram *hist, int bar);
-
-
 static void
-hist_draw_bar (struct chart *ch, const struct histogram *hist, int bar)
+hist_draw_bar (plPlotter *lp, const struct chart_geometry *geom,
+               const gsl_histogram *h, int bar)
 {
-  if (!ch)
-    return ;
+  double upper;
+  double lower;
+  double height;
 
-  {
-    double upper;
-    double lower;
-    double height;
+  const size_t bins = gsl_histogram_bins (h);
+  const double x_pos = (geom->data_right - geom->data_left) * bar / (double) bins ;
+  const double width = (geom->data_right - geom->data_left) / (double) bins ;
 
-    const size_t bins = gsl_histogram_bins (hist->gsl_hist);
-    const double x_pos = (ch->data_right - ch->data_left) * bar / (double) bins ;
-    const double width = (ch->data_right - ch->data_left) / (double) bins ;
+  assert ( 0 == gsl_histogram_get_range (h, bar, &lower, &upper));
 
-    assert ( 0 == gsl_histogram_get_range (hist->gsl_hist, bar, &lower, &upper));
+  assert ( upper >= lower);
 
-    assert ( upper >= lower);
+  height = gsl_histogram_get (h, bar) *
+    (geom->data_top - geom->data_bottom) / gsl_histogram_max_val (h);
 
-    height = gsl_histogram_get (hist->gsl_hist, bar) *
-     (ch->data_top - ch->data_bottom) / gsl_histogram_max_val (hist->gsl_hist);
+  pl_savestate_r (lp);
+  pl_move_r (lp,geom->data_left, geom->data_bottom);
+  pl_fillcolorname_r (lp, geom->fill_colour);
+  pl_filltype_r (lp,1);
 
-    pl_savestate_r (ch->lp);
-    pl_move_r (ch->lp,ch->data_left, ch->data_bottom);
-    pl_fillcolorname_r (ch->lp, ch->fill_colour);
-    pl_filltype_r (ch->lp,1);
 
+  pl_fboxrel_r (lp,
+                x_pos, 0,
+                x_pos + width, height);
 
-    pl_fboxrel_r (ch->lp,
-                x_pos, 0,
-                x_pos + width, height);
+  pl_restorestate_r (lp);
 
-    pl_restorestate_r (ch->lp);
-
-    draw_tick (ch, TICK_ABSCISSA,
-               x_pos + width / 2.0, "%g", (upper + lower) / 2.0);
-  }
+  draw_tick (lp, geom, TICK_ABSCISSA,
+             x_pos + width / 2.0, "%g", (upper + lower) / 2.0);
 }
 
-
+struct histogram_chart
+  {
+    struct chart chart;
+    gsl_histogram *gsl_hist;
+    char *label;
+    double n;
+    double mean;
+    double stddev;
+    bool show_normal;
+  };
 
 /* Plots a histogram of the data in HIST with the given LABEL.
    Labels the histogram with each of N, MEAN, and STDDEV that is
    not SYSMIS.  If all three are not SYSMIS and SHOW_NORMAL is
    true, also draws a normal curve on the histogram. */
-void
-histogram_plot (const struct histogram *hist,
-                const char *label,
-                double n, double mean, double stddev,
-                bool show_normal)
+struct chart *
+histogram_chart_create (const struct histogram *hist, const char *label,
+                        double n, double mean, double stddev,
+                        bool show_normal)
+{
+  struct histogram_chart *h;
+
+  h = xmalloc (sizeof *h);
+  chart_init (&h->chart, &histogram_chart_class);
+  h->gsl_hist = hist->gsl_hist ? gsl_histogram_clone (hist->gsl_hist) : NULL;
+  h->label = xstrdup (label);
+  h->n = n;
+  h->mean = mean;
+  h->stddev = stddev;
+  h->show_normal = show_normal;
+  return &h->chart;
+}
+
+static void
+histogram_chart_draw (const struct chart *chart, plPlotter *lp)
 {
+  struct histogram_chart *h = (struct histogram_chart *) chart;
+  struct chart_geometry geom;
   int i;
   int bins;
 
-  struct chart *ch = chart_create ();
+  chart_geometry_init (lp, &geom);
 
-  chart_write_title (ch, _("HISTOGRAM"));
+  chart_write_title (lp, &geom, _("HISTOGRAM"));
 
-  chart_write_ylabel (ch, _("Frequency"));
-  chart_write_xlabel (ch, label);
+  chart_write_ylabel (lp, &geom, _("Frequency"));
+  chart_write_xlabel (lp, &geom, h->label);
 
-  if ( ! hist ) /* If this happens, probably all values are SYSMIS */
+  if (h->gsl_hist == NULL)
     {
-      chart_submit (ch);
+      /* Probably all values are SYSMIS. */
       return;
     }
-  else
-    {
-      bins = gsl_histogram_bins (hist->gsl_hist);
-    }
 
-  chart_write_yscale (ch, 0, gsl_histogram_max_val (hist->gsl_hist), 5);
+  bins = gsl_histogram_bins (h->gsl_hist);
+
+  chart_write_yscale (lp, &geom, 0, gsl_histogram_max_val (h->gsl_hist), 5);
 
-  for ( i = 0 ; i < bins ; ++i )
-    hist_draw_bar (ch, hist, i);
+  for (i = 0; i < bins; i++)
+    hist_draw_bar (lp, &geom, h->gsl_hist, i);
 
-  histogram_write_legend (ch, n, mean, stddev);
+  histogram_write_legend (lp, &geom, h->n, h->mean, h->stddev);
 
-  if (show_normal && n != SYSMIS && mean != SYSMIS && stddev != SYSMIS)
+  if (h->show_normal
+      && h->n != SYSMIS && h->mean != SYSMIS && h->stddev != SYSMIS)
     {
       /* Draw the normal curve */
+      double d;
+      double x_min, x_max, not_used;
+      double abscissa_scale;
+      double ordinate_scale;
+      double range;
 
-      double d ;
-      double x_min, x_max, not_used ;
-      double abscissa_scale ;
-      double ordinate_scale ;
-      double range ;
-
-      gsl_histogram_get_range (hist->gsl_hist, 0, &x_min, &not_used);
+      gsl_histogram_get_range (h->gsl_hist, 0, &x_min, &not_used);
       range = not_used - x_min;
-      gsl_histogram_get_range (hist->gsl_hist, bins - 1, &not_used, &x_max);
+      gsl_histogram_get_range (h->gsl_hist, bins - 1, &not_used, &x_max);
 
-      abscissa_scale = (ch->data_right - ch->data_left) / (x_max - x_min);
-      ordinate_scale = (ch->data_top - ch->data_bottom) /
-       gsl_histogram_max_val (hist->gsl_hist) ;
+      abscissa_scale = (geom.data_right - geom.data_left) / (x_max - x_min);
+      ordinate_scale = (geom.data_top - geom.data_bottom) /
+       gsl_histogram_max_val (h->gsl_hist);
 
-      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)
+      pl_move_r (lp, geom.data_left, geom.data_bottom);
+      for (d = geom.data_left;
+          d <= geom.data_right;
+          d += (geom.data_right - geom.data_left) / 100.0)
        {
-         const double x = (d - ch->data_left) / abscissa_scale + x_min ;
-         const double y = n * range *
-           gsl_ran_gaussian_pdf (x - mean, stddev);
+         const double x = (d - geom.data_left) / abscissa_scale + x_min;
+         const double y = h->n * range *
+           gsl_ran_gaussian_pdf (x - h->mean, h->stddev);
 
-         pl_fcont_r (ch->lp,  d,  ch->data_bottom  + y * ordinate_scale);
+         pl_fcont_r (lp,  d,  geom.data_bottom  + y * ordinate_scale);
 
        }
-      pl_endpath_r (ch->lp);
+      pl_endpath_r (lp);
     }
 
-  chart_submit (ch);
+  chart_geometry_free (lp);
 }
 
 
+static void
+histogram_chart_destroy (struct chart *chart)
+{
+  struct histogram_chart *h = (struct histogram_chart *) chart;
+  if (h->gsl_hist != NULL)
+    gsl_histogram_free (h->gsl_hist);
+  free (h->label);
+  free (h);
+}
+
+static const struct chart_class histogram_chart_class =
+  {
+    histogram_chart_draw,
+    histogram_chart_destroy
+  };
index 74b10bd3cfd98bd3eec5434d3c22ebedfe2295f0..1e5b59edb5d8458badf707b3f6c21a424afbc3c2 100644 (file)
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-#ifndef PLOT_HIST_H
-#define PLOT_HIST_H
+#ifndef OUTPUT_PLOT_HIST_H
+#define OUTPUT_PLOT_HIST_H
 
 #include <stdbool.h>
 
 struct chart;
-struct moments1;
 struct histogram;
 
-/* Plots a histogram of the data in HIST with the given LABEL.
-   Labels the histogram with each of N, MEAN, and STDDEV that is
-   not SYSMIS.  If all three are not SYSMIS and SHOW_NORMAL is
-   true, also draws a normal curve on the histogram. */
-void
-histogram_plot (const struct histogram *hist,
-                const char *label,
-                double n, double mean, double stddev,
-                bool show_normal);
-
-#endif
+/* Creates and returns a new chart that depicts a histogram of
+   the data in HIST with the given LABEL.  Labels the histogram
+   with each of N, MEAN, and STDDEV that is not SYSMIS.  If all
+   three are not SYSMIS and SHOW_NORMAL is true, also draws a
+   normal curve on the histogram. */
+struct chart *histogram_chart_create (const struct histogram *hist,
+                                      const char *label,
+                                      double n, double mean, double stddev,
+                                      bool show_normal);
+
+#endif /* output/plot-hist.h */
index bfe0850d5f8f9bc3d53133ea52399ae8d93b7312..8c1a653017273921fde64e07b685178a7f4f6bbd 100644 (file)
 #include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
 #include <data/file-name.h>
-#include "error.h"
-#include "output.h"
-#include "manager.h"
-#include "table.h"
+#include <output/chart-provider.h>
+#include <output/output.h>
+#include <output/manager.h>
+#include <output/table.h>
 #include <libpspp/version.h>
 
+#include "error.h"
 #include "xalloc.h"
 
 #include "gettext.h"
@@ -63,7 +64,7 @@ html_open_driver (const char *name, int types, struct substring options)
   x->file_name = xstrdup ("pspp.html");
   x->chart_file_name = xstrdup ("pspp-#.png");
   x->file = NULL;
-  x->chart_cnt = 0;
+  x->chart_cnt = 1;
 
   outp_parse_options (name, options, handle_option, this);
 
@@ -203,11 +204,30 @@ handle_option (void *this_, const char *key, const struct string *val)
 
 static void output_tab_table (struct outp_driver *, struct tab_table *);
 
+static void
+html_output_chart (struct outp_driver *this, const struct chart *chart)
+{
+  struct html_driver_ext *x = this->ext;
+  char *file_name;
+  plPlotter *lp;
+
+  /* Draw chart in separate file. */
+  if (!chart_create_file ("png", x->chart_file_name, x->chart_cnt,
+                          NULL, &file_name, &lp))
+    return;
+  x->chart_cnt++;
+  chart_draw (chart, lp);
+  pl_deletepl_r (lp);
+
+  link_image (x->file, file_name);
+
+  free (file_name);
+}
+
 static void
 html_submit (struct outp_driver *this, struct som_entity *s)
 {
   extern struct som_table_class tab_table_class;
-  struct html_driver_ext *x = this->ext;
 
   assert (s->class == &tab_table_class ) ;
 
@@ -216,9 +236,6 @@ html_submit (struct outp_driver *this, struct som_entity *s)
     case SOM_TABLE:
       output_tab_table ( this, (struct tab_table *) s->ext);
       break;
-    case SOM_CHART:
-      link_image (x->file, ((struct chart *)s->ext)->file_name);
-      break;
     default:
       NOT_REACHED ();
     }
@@ -365,19 +382,6 @@ output_tab_table (struct outp_driver *this, struct tab_table *t)
   fputs ("</TABLE>\n\n", x->file);
 }
 
-static void
-html_initialise_chart (struct outp_driver *this UNUSED, struct chart *ch)
-{
-  struct html_driver_ext *x = this->ext;
-  chart_init_separate (ch, "png", x->chart_file_name, ++x->chart_cnt);
-}
-
-static void
-html_finalise_chart(struct outp_driver *d UNUSED, struct chart *ch)
-{
-  chart_finalise_separate (ch);
-}
-
 
 
 /* HTML driver class. */
@@ -393,11 +397,11 @@ const struct outp_class html_class =
     NULL,
     NULL,
 
+    html_output_chart,
+
     html_submit,
 
     NULL,
     NULL,
     NULL,
-    html_initialise_chart,
-    html_finalise_chart
   };
index f418cd0d06ca6848e56250a8d53a420d327f54f7..9b9013933ae87ed3fde10e6d93e83441a41a8ad2 100644 (file)
@@ -219,7 +219,7 @@ output_entity (struct outp_driver *d, struct som_entity *t)
   void *r;
 
   outp_open_page (d);
-  if (d->class->special || t->type == SOM_CHART)
+  if (d->class->special)
     {
       d->class->submit (d, t);
       return;
index 8ed104bad417aa67e481c84e65d006b0ad230828..e7276982fc5c5a064b89cc8e0af364d43283a38f 100644 (file)
@@ -37,8 +37,7 @@
 
 enum som_type
   {
-    SOM_TABLE,
-    SOM_CHART
+    SOM_TABLE
   } ;
 
 /* Entity (Table or Chart) . */
index 09dddc772ffe7e55120fabff5350a6362d8e9eb9..ec9ae87d968391947dcc4f788998e209a73dd99b 100644 (file)
@@ -74,6 +74,8 @@ struct outp_class
 
     void (*flush) (struct outp_driver *);
 
+    void (*output_chart) (struct outp_driver *, const struct chart *);
+
     /* special != 0 only. */
     void (*submit) (struct outp_driver *, struct som_entity *);
 
@@ -84,8 +86,6 @@ struct outp_class
     void (*text_metrics) (struct outp_driver *, const struct outp_text *,
                           int *width, int *height);
     void (*text_draw) (struct outp_driver *, const struct outp_text *);
-    void (*initialise_chart)(struct outp_driver *, struct chart *);
-    void (*finalise_chart)(struct outp_driver *, struct chart *);
   };
 
 /* Device types. */
index 1fdc8d692ad482766e113e7a9d90105273724d4f..bec23de6efcaeb058bbe7356312255815770376d 100644 (file)
@@ -24,6 +24,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <data/file-name.h>
 #include <libpspp/assertion.h>
 #include <libpspp/bit-vector.h>
 #include <libpspp/compiler.h>
 #include <libpspp/misc.h>
 #include <libpspp/start-date.h>
 #include <libpspp/version.h>
+#include <output/afm.h>
+#include <output/chart-provider.h>
+#include <output/chart.h>
+#include <output/manager.h>
+#include <output/output.h>
 
-#include <data/file-name.h>
-
-#include "afm.h"
-#include "chart.h"
 #include "error.h"
-#include "manager.h"
-#include "output.h"
-
 #include "intprops.h"
 #include "minmax.h"
 #include "xalloc.h"
@@ -105,6 +104,8 @@ struct ps_driver_ext
 
     struct font *fonts[OUTP_FONT_CNT];
     int last_font;              /* Index of last font set with setfont. */
+
+    int doc_num;                /* %%DocumentNumber counter. */
   };
 
 /* Transform logical y-ordinate Y into a page ordinate. */
@@ -151,6 +152,7 @@ ps_open_driver (const char *name, int types, struct substring options)
   x->line_width = PSUS / 144;
   for (i = 0; i < OUTP_FONT_CNT; i++)
     x->fonts[i] = NULL;
+  x->doc_num = 0;
 
   outp_parse_options (this->name, options, handle_option, this);
 
@@ -601,13 +603,82 @@ ps_close_page (struct outp_driver *this)
          x->file);
 }
 
+static void
+ps_output_chart (struct outp_driver *this, const struct chart *chart)
+{
+  struct ps_driver_ext *x = this->ext;
+  plPlotterParams *params;
+  int x_origin, y_origin;
+  char buf[BUFSIZ];
+  char *page_size;
+  plPlotter *lp;
+  FILE *file;
+  int size;
+
+  /* Create temporary file for chart. */
+  file = tmpfile ();
+  if (file == NULL)
+    {
+      error (0, errno, _("failed to create temporary file"));
+      return;
+    }
+
+  /* Create plotter for chart. */
+  size = this->width < this->length ? this->width : this->length;
+  x_origin = x->left_margin + (size - this->width) / 2;
+  y_origin = x->bottom_margin + (size - this->length) / 2;
+  page_size = xasprintf ("a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
+                         (double) size / PSUS, (double) size / PSUS,
+                         (double) x_origin / PSUS, (double) y_origin / PSUS);
+
+  params = pl_newplparams ();
+  pl_setplparam (params, "PAGESIZE", page_size);
+  free (page_size);
+  lp = pl_newpl_r ("ps", 0, file, stderr, params);
+  pl_deleteplparams (params);
+
+  if (lp == NULL)
+    {
+      fclose (file);
+      return;
+    }
+
+  /* Draw chart and free plotter. */
+  chart_draw (chart, lp);
+  pl_deletepl_r (lp);
+
+  /* Write prologue for chart. */
+  outp_eject_page (this);
+  fprintf (x->file,
+           "/sp save def\n"
+           "%d %d translate 1000 dup scale\n"
+           "userdict begin\n"
+           "/showpage { } def\n"
+           "0 setgray 0 setlinecap 1 setlinewidth\n"
+           "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
+           "%%%%BeginDocument: %d\n",
+           -x->left_margin, -x->bottom_margin,
+           x->doc_num++);
+
+  /* Copy chart into output file. */
+  rewind (file);
+  while (fwrite (buf, 1, fread (buf, 1, sizeof buf, file), x->file))
+    continue;
+  fclose (file);
+
+  /* Write epilogue for chart. */
+  fputs ("%%EndDocument\n"
+         "end\n"
+         "sp restore\n",
+         x->file);
+  outp_close_page (this);
+}
+
 static void
 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
 {
   switch (s->type)
     {
-    case SOM_CHART:
-      break;
     default:
       NOT_REACHED ();
     }
@@ -1087,72 +1158,6 @@ ps_text_draw (struct outp_driver *this, const struct outp_text *t)
   text (this, t, true, NULL, NULL);
 }
 \f
-static void
-ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
-{
-#ifdef NO_CHARTS
-  ch->lp = NULL;
-#else
-  struct ps_driver_ext *x = this->ext;
-  char page_size[128];
-  int size;
-  int x_origin, y_origin;
-
-  ch->file = tmpfile ();
-  if (ch->file == NULL)
-    {
-      ch->lp = NULL;
-      return;
-    }
-
-  size = this->width < this->length ? this->width : this->length;
-  x_origin = x->left_margin + (size - this->width) / 2;
-  y_origin = x->bottom_margin + (size - this->length) / 2;
-
-  snprintf (page_size, sizeof page_size,
-            "a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
-            (double) size / PSUS, (double) size / PSUS,
-            (double) x_origin / PSUS, (double) y_origin / PSUS);
-
-  ch->pl_params = pl_newplparams ();
-  pl_setplparam (ch->pl_params, "PAGESIZE", page_size);
-  ch->lp = pl_newpl_r ("ps", NULL, ch->file, stderr, ch->pl_params);
-#endif
-}
-
-static void
-ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
-{
-#ifndef NO_CHARTS
-  struct ps_driver_ext *x = this->ext;
-  char buf[BUFSIZ];
-  static int doc_num = 0;
-
-  outp_eject_page (this);
-  fprintf (x->file,
-           "/sp save def\n"
-           "%d %d translate 1000 dup scale\n"
-           "userdict begin\n"
-           "/showpage { } def\n"
-           "0 setgray 0 setlinecap 1 setlinewidth\n"
-           "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
-           "%%%%BeginDocument: %d\n",
-           -x->left_margin, -x->bottom_margin,
-           doc_num++);
-
-  rewind (ch->file);
-  while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file))
-    continue;
-  fclose (ch->file);
-
-  fputs ("%%EndDocument\n"
-         "end\n"
-         "sp restore\n",
-         x->file);
-  outp_close_page (this);
-#endif
-}
-\f
 static void embed_font (struct outp_driver *this, struct font *font);
 static void reencode_font (struct outp_driver *this, struct font *font);
 
@@ -1440,12 +1445,11 @@ const struct outp_class postscript_class =
   ps_close_page,
   NULL,
 
+  ps_output_chart,
+
   ps_submit,
 
   ps_line,
   ps_text_metrics,
   ps_text_draw,
-
-  ps_chart_initialise,
-  ps_chart_finalise
 };