#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>
};
+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 )
{
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,
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]));
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);
}
int n_dep_var,
const struct xfactor *fctr)
{
+#if 0
int v;
for (v = 0; v < n_dep_var; ++v)
chart_submit (ch);
}
+#endif
}
)
{
+#if 0
int v;
struct ll *ll;
const struct ll_list *result_list = &fctr->result_list;
chart_submit (ch);
}
+#endif
}
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);
}
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);
}
#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"
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;
}
}
-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,
}
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. */
}
/* 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);
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",
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
};
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
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. */
xr_close_page,
NULL,
- xr_submit,
+ NULL,
+
+ NULL,
xr_line,
xr_text_metrics,
xr_text_draw,
-
- xr_chart_initialise,
- xr_chart_finalise
};
--- /dev/null
+/* 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 */
/* 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;
}
/* 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 */
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 \
/* 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;
/* 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.
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;
}
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);
}
/* 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
{
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);
#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,
-/* 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);
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)
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,
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
+ };
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
/* 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/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>
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);
}
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
#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, ¬_used);
+ gsl_histogram_get_range (h->gsl_hist, 0, &x_min, ¬_used);
range = not_used - x_min;
- gsl_histogram_get_range (hist->gsl_hist, bins - 1, ¬_used, &x_max);
+ gsl_histogram_get_range (h->gsl_hist, bins - 1, ¬_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
+ };
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 */
#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"
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);
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 ) ;
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 ();
}
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. */
NULL,
NULL,
+ html_output_chart,
+
html_submit,
NULL,
NULL,
NULL,
- html_initialise_chart,
- html_finalise_chart
};
void *r;
outp_open_page (d);
- if (d->class->special || t->type == SOM_CHART)
+ if (d->class->special)
{
d->class->submit (d, t);
return;
enum som_type
{
- SOM_TABLE,
- SOM_CHART
+ SOM_TABLE
} ;
/* Entity (Table or Chart) . */
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 *);
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. */
#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"
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. */
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);
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 ();
}
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);
ps_close_page,
NULL,
+ ps_output_chart,
+
ps_submit,
ps_line,
ps_text_metrics,
ps_text_draw,
-
- ps_chart_initialise,
- ps_chart_finalise
};