output: Use Cairo and Pango to draw charts, instead of libplot.
[pspp-builds.git] / src / output / charts / np-plot.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2004, 2008, 2009 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <output/charts/np-plot.h>
20
21 #include <gsl/gsl_cdf.h>
22
23 #include <data/casereader.h>
24 #include <data/casewriter.h>
25 #include <libpspp/message.h>
26 #include <math/np.h>
27 #include <output/chart-provider.h>
28 #include <output/charts/cartesian.h>
29 #include <output/charts/plot-chart.h>
30
31 #include "gl/minmax.h"
32
33 #include "gettext.h"
34 #define _(msgid) gettext (msgid)
35
36 /* An NP or DNP plot. */
37 struct np_plot_chart
38   {
39     struct chart chart;
40     char *label;
41     struct casereader *data;
42
43     /* Copied directly from struct np. */
44     double y_min, y_max;
45     double dns_min, dns_max;
46
47     /* Calculated. */
48     double slope, intercept;
49     double y_first, y_last;
50     double x_lower, x_upper;
51     double slack;
52   };
53
54 static const struct chart_class np_plot_chart_class;
55 static const struct chart_class dnp_plot_chart_class;
56
57 static struct chart *
58 make_np_plot (const struct chart_class *class,
59               const struct np *np, const struct casereader *reader,
60               const char *label)
61 {
62   struct np_plot_chart *npp;
63
64   if (np->n < 1.0)
65     return NULL;
66
67   npp = xmalloc (sizeof *npp);
68   chart_init (&npp->chart, class);
69   npp->label = xstrdup (label);
70   npp->data = casereader_clone (reader);
71   npp->y_min = np->y_min;
72   npp->y_max = np->y_max;
73   npp->dns_min = np->dns_min;
74   npp->dns_max = np->dns_max;
75
76   /* Slope and intercept of the ideal normal probability line. */
77   npp->slope = 1.0 / np->stddev;
78   npp->intercept = -np->mean / np->stddev;
79
80   npp->y_first = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
81   npp->y_last = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
82
83   /* Need to make sure that both the scatter plot and the ideal fit into the
84      plot. */
85   npp->x_lower = MIN (np->y_min, (npp->y_first - npp->intercept) / npp->slope);
86   npp->x_upper = MAX (np->y_max, (npp->y_last  - npp->intercept) / npp->slope);
87   npp->slack = (npp->x_upper - npp->x_lower) * 0.05;
88
89   return &npp->chart;
90 }
91
92 /* Creates and returns a normal probability plot corresponding to
93    the calculations in NP and the data in READER, and label the
94    plot with LABEL.  The data in READER must have Y-values in
95    value index NP_IDX_Y and NS-values in value index NP_IDX_NS.
96
97    Returns a null pointer if the data set is empty.
98
99    The caller retains ownership of NP and READER. */
100 struct chart *
101 np_plot_create (const struct np *np, const struct casereader *reader,
102                 const char *label)
103 {
104   return make_np_plot (&np_plot_chart_class, np, reader, label);
105 }
106
107 /* Creates and returns a detrended normal probability plot
108    corresponding to the calculations in NP and the data in
109    READER, and label the plot with LABEL.  The data in READER
110    must have Y-values in value index NP_IDX_Y and DNS-values in
111    value index NP_IDX_DNS.
112
113    Returns a null pointer if the data set is empty.
114
115    The caller retains ownership of NP and READER. */
116 struct chart *
117 dnp_plot_create (const struct np *np, const struct casereader *reader,
118                  const char *label)
119 {
120   return make_np_plot (&dnp_plot_chart_class, np, reader, label);
121 }
122
123 static void
124 np_plot_chart_draw (const struct chart *chart, cairo_t *cr,
125                     struct chart_geometry *geom)
126 {
127   const struct np_plot_chart *npp = (struct np_plot_chart *) chart;
128   struct casereader *data;
129   struct ccase *c;
130
131   chart_write_title (cr, geom, _("Normal Q-Q Plot of %s"), npp->label);
132   chart_write_xlabel (cr, geom, _("Observed Value"));
133   chart_write_ylabel (cr, geom, _("Expected Normal"));
134   chart_write_xscale (cr, geom,
135                       npp->x_lower - npp->slack,
136                       npp->x_upper + npp->slack, 5);
137   chart_write_yscale (cr, geom, npp->y_first, npp->y_last, 5);
138
139   data = casereader_clone (npp->data);
140   for (; (c = casereader_read (data)) != NULL; case_unref (c))
141     chart_datum (cr, geom, 0,
142                  case_data_idx (c, NP_IDX_Y)->f,
143                  case_data_idx (c, NP_IDX_NS)->f);
144   casereader_destroy (data);
145
146   chart_line (cr, geom, npp->slope, npp->intercept,
147               npp->y_first, npp->y_last, CHART_DIM_Y);
148 }
149
150 static void
151 dnp_plot_chart_draw (const struct chart *chart, cairo_t *cr,
152                      struct chart_geometry *geom)
153 {
154   const struct np_plot_chart *dnpp = (struct np_plot_chart *) chart;
155   struct casereader *data;
156   struct ccase *c;
157
158   chart_write_title (cr, geom, _("Detrended Normal Q-Q Plot of %s"),
159                      dnpp->label);
160   chart_write_xlabel (cr, geom, _("Observed Value"));
161   chart_write_ylabel (cr, geom, _("Dev from Normal"));
162   chart_write_xscale (cr, geom, dnpp->y_min, dnpp->y_max, 5);
163   chart_write_yscale (cr, geom, dnpp->dns_min, dnpp->dns_max, 5);
164
165   data = casereader_clone (dnpp->data);
166   for (; (c = casereader_read (data)) != NULL; case_unref (c))
167     chart_datum (cr, geom, 0, case_data_idx (c, NP_IDX_Y)->f,
168                  case_data_idx (c, NP_IDX_DNS)->f);
169   casereader_destroy (data);
170
171   chart_line (cr, geom, 0, 0, dnpp->y_min, dnpp->y_max, CHART_DIM_X);
172 }
173
174 static void
175 np_plot_chart_destroy (struct chart *chart)
176 {
177   struct np_plot_chart *npp = (struct np_plot_chart *) chart;
178
179   casereader_destroy (npp->data);
180   free (npp->label);
181   free (npp);
182 }
183
184 static const struct chart_class np_plot_chart_class =
185   {
186     np_plot_chart_draw,
187     np_plot_chart_destroy
188   };
189
190 static const struct chart_class dnp_plot_chart_class =
191   {
192     dnp_plot_chart_draw,
193     np_plot_chart_destroy
194   };