9d298cd26ab376b8a230d4c0c4e17521b6b7095f
[pspp] / src / output / charts / plot-chart.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2004, 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/plot-chart.h>
20
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <float.h>
26 #include <assert.h>
27 #include <math.h>
28
29 #include <libpspp/assertion.h>
30 #include <libpspp/str.h>
31 #include <math/chart-geometry.h>
32 #include <output/chart-provider.h>
33 #include <output/manager.h>
34 #include <output/output.h>
35
36 #include "xalloc.h"
37
38 const struct chart_colour data_colour[N_CHART_COLOURS] =
39   {
40     { 165, 42, 42 },            /* brown */
41     { 255, 0, 0 },              /* red */
42     { 255, 165, 0 },            /* orange */
43     { 255, 255, 0 },            /* yellow */
44     { 0, 255, 0 },              /* green */
45     { 0, 0, 255 },              /* blue */
46     { 238, 130, 238 },          /* violet */
47     { 190, 190, 190 },          /* grey */
48     { 255, 192, 203 },          /* pink */
49   };
50
51
52
53 /* Draw a tick mark at position
54    If label is non zero, then print it at the tick mark
55 */
56 void
57 draw_tick (plPlotter *lp, const struct chart_geometry *geom,
58            enum tick_orientation orientation,
59            double position,
60            const char *label, ...)
61 {
62   const int tickSize = 10;
63
64   pl_savestate_r (lp);
65   pl_move_r (lp, geom->data_left, geom->data_bottom);
66
67   if (orientation == TICK_ABSCISSA)
68     pl_flinerel_r (lp, position, 0, position, -tickSize);
69   else if (orientation == TICK_ORDINATE)
70     pl_flinerel_r (lp, 0, position, -tickSize, position);
71   else
72     NOT_REACHED ();
73
74   if (label != NULL)
75     {
76       va_list ap;
77       char *s;
78
79       va_start (ap, label);
80       s = xvasprintf (label, ap);
81       if (orientation == TICK_ABSCISSA)
82         pl_alabel_r (lp, 'c', 't', s);
83       else if (orientation == TICK_ORDINATE)
84         {
85           if (fabs (position) < DBL_EPSILON)
86             pl_moverel_r (lp, 0, 10);
87           pl_alabel_r (lp, 'r', 'c', s);
88         }
89       free (s);
90       va_end (ap);
91     }
92
93   pl_restorestate_r (lp);
94 }
95
96
97 /* Write the title on a chart*/
98 void
99 chart_write_title (plPlotter *lp, const struct chart_geometry *geom,
100                    const char *title, ...)
101 {
102   va_list ap;
103   char *s;
104
105   pl_savestate_r (lp);
106   pl_ffontsize_r (lp, geom->font_size * 1.5);
107   pl_move_r (lp, geom->data_left, geom->title_bottom);
108
109   va_start(ap, title);
110   s = xvasprintf (title, ap);
111   pl_alabel_r (lp, 0, 0, s);
112   free (s);
113   va_end (ap);
114
115   pl_restorestate_r (lp);
116 }
117
118
119 /* Set the scale for the abscissa */
120 void
121 chart_write_xscale (plPlotter *lp, struct chart_geometry *geom,
122                     double min, double max, int ticks)
123 {
124   double x;
125
126   const double tick_interval =
127     chart_rounded_tick ((max - min) / (double) ticks);
128
129   geom->x_max = ceil (max / tick_interval) * tick_interval;
130   geom->x_min = floor (min / tick_interval) * tick_interval;
131   geom->abscissa_scale = fabs(geom->data_right - geom->data_left) /
132     fabs(geom->x_max - geom->x_min);
133
134   for (x = geom->x_min; x <= geom->x_max; x += tick_interval)
135     draw_tick (lp, geom, TICK_ABSCISSA,
136                (x - geom->x_min) * geom->abscissa_scale, "%g", x);
137 }
138
139
140 /* Set the scale for the ordinate */
141 void
142 chart_write_yscale (plPlotter *lp, struct chart_geometry *geom,
143                     double smin, double smax, int ticks)
144 {
145   double y;
146
147   const double tick_interval =
148     chart_rounded_tick ((smax - smin) / (double) ticks);
149
150   geom->y_max = ceil (smax / tick_interval) * tick_interval;
151   geom->y_min = floor (smin / tick_interval) * tick_interval;
152
153   geom->ordinate_scale =
154     (fabs (geom->data_top - geom->data_bottom)
155      / fabs (geom->y_max - geom->y_min));
156
157   for (y = geom->y_min; y <= geom->y_max; y += tick_interval)
158     draw_tick (lp, geom, TICK_ORDINATE,
159                (y - geom->y_min) * geom->ordinate_scale, "%g", y);
160 }
161
162 /* Write the abscissa label */
163 void
164 chart_write_xlabel (plPlotter *lp, const struct chart_geometry *geom,
165                     const char *label)
166 {
167   pl_savestate_r (lp);
168   pl_move_r (lp, geom->data_left, geom->abscissa_top);
169   pl_alabel_r (lp, 0, 't', label);
170   pl_restorestate_r (lp);
171 }
172
173 /* Write the ordinate label */
174 void
175 chart_write_ylabel (plPlotter *lp, const struct chart_geometry *geom,
176                     const char *label)
177 {
178   pl_savestate_r (lp);
179   pl_move_r (lp, geom->data_bottom, geom->ordinate_right);
180   pl_textangle_r (lp, 90);
181   pl_alabel_r (lp, 0, 0, label);
182   pl_restorestate_r(lp);
183 }