1 /* acre - A cairo-based library for creating plots and charts.
3 * Copyright © 2009 Carl Worth <cworth@cworth.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 typedef struct _acre_data_point_2d {
29 } acre_data_point_2d_t;
34 acre_data_point_2d_t *points;
35 unsigned int points_size;
36 unsigned int num_points;
45 unsigned int data_size;
46 unsigned int num_data;
48 /* Data for drawing. */
51 /* Total size including labels. */
55 /* Position and size of chart alone. */
59 /* Create a new, empty plot. */
65 acre = xmalloc (sizeof (acre_t));
68 acre->x_axis_label = NULL;
69 acre->y_axis_label = NULL;
80 acre_destroy (acre_t *acre)
85 free (acre->x_axis_label);
86 free (acre->y_axis_label);
88 for (i = 0; i < acre->num_data; i++)
89 acre_data_destroy (acre->data[i]);
97 acre_set_title (acre_t *acre, const char *title)
101 acre->title = strdup (title);
105 acre_set_x_axis_label (acre_t *acre, const char *label)
107 free (acre->x_axis_label);
109 acre->x_axis_label = strdup (label);
113 acre_set_y_axis_label (acre_t *acre, const char *label)
115 free (acre->y_axis_label);
117 acre->y_axis_label = strdup (label);
120 /* Add a dataset to the plot. The plot assumes ownership of the
121 * dataset so it is not necessary to call acre_data_destroy on it. */
123 acre_add_data (acre_t *acre, acre_data_t *data)
125 if (acre->num_data >= acre->data_size) {
126 acre->data_size *= 2;
127 if (acre->data_size == 0)
129 acre->data = xrealloc_ab (acre->data,
131 sizeof (acre_data_t *));
134 acre->data[acre->num_data] = data;
138 #define ACRE_FONT_FAMILY "sans"
139 #define ACRE_FONT_SIZE 12
140 #define ACRE_TITLE_FONT_SIZE 32
141 #define ACRE_PAD (ACRE_FONT_SIZE)
142 #define ACRE_TICK_SIZE 4
145 _draw_title_and_labels (acre_t *acre)
147 cairo_t *cr = acre->cr;
148 PangoFontDescription *acre_font, *title_font;
149 PangoLayout *title_layout, *x_axis_layout, *y_axis_layout;
150 int title_width, title_height;
151 int x_axis_width, x_axis_height;
152 int y_axis_width, y_axis_height;
153 PangoRectangle new_chart;
155 acre_font = pango_font_description_new ();
156 pango_font_description_set_family (acre_font, ACRE_FONT_FAMILY);
157 pango_font_description_set_absolute_size (acre_font,
158 ACRE_FONT_SIZE * PANGO_SCALE);
160 title_font = pango_font_description_new ();
161 pango_font_description_set_family (title_font, ACRE_FONT_FAMILY);
162 pango_font_description_set_absolute_size (title_font,
163 ACRE_TITLE_FONT_SIZE * PANGO_SCALE);
165 title_layout = pango_cairo_create_layout (cr);
166 pango_layout_set_font_description (title_layout, title_font);
167 pango_layout_set_text (title_layout, acre->title, -1);
168 pango_layout_set_alignment (title_layout, PANGO_ALIGN_CENTER);
170 x_axis_layout = pango_cairo_create_layout (cr);
171 pango_layout_set_font_description (x_axis_layout, acre_font);
172 pango_layout_set_text (x_axis_layout, acre->x_axis_label, -1);
173 pango_layout_set_alignment (x_axis_layout, PANGO_ALIGN_CENTER);
175 y_axis_layout = pango_cairo_create_layout (cr);
176 pango_layout_set_font_description (y_axis_layout, acre_font);
177 pango_layout_set_text (y_axis_layout, acre->y_axis_label, -1);
178 pango_layout_set_alignment (y_axis_layout, PANGO_ALIGN_CENTER);
180 /* Iterate with the layout of the title and axis labels until they
181 * are stable, (this requires iteration since we don't know what
182 * to set their widths to in advance due to the wrapping of the
183 * other elements). */
186 acre->chart.width = acre->width;
187 acre->chart.height = acre->height;
189 pango_layout_set_width (title_layout, acre->chart.width * PANGO_SCALE);
190 pango_layout_set_width (x_axis_layout, acre->chart.width * PANGO_SCALE);
191 pango_layout_set_width (y_axis_layout, acre->chart.height * PANGO_SCALE);
193 pango_layout_get_pixel_size (title_layout, &title_width, &title_height);
194 pango_layout_get_pixel_size (x_axis_layout, &x_axis_width, &x_axis_height);
195 pango_layout_get_pixel_size (y_axis_layout, &y_axis_width, &y_axis_height);
197 new_chart.x = ACRE_PAD + y_axis_height +
198 ACRE_PAD + ACRE_FONT_SIZE;
199 new_chart.width = acre->width - acre->chart.x - ACRE_PAD;
201 new_chart.y = ACRE_PAD + title_height + ACRE_PAD;
202 new_chart.height = acre->height - acre->chart.y - (ACRE_FONT_SIZE + ACRE_PAD + x_axis_height + ACRE_PAD);
204 if (new_chart.x == acre->chart.x &&
205 new_chart.y == acre->chart.y &&
206 new_chart.width == acre->chart.width &&
207 new_chart.height == acre->chart.height)
212 acre->chart.x = new_chart.x;
213 acre->chart.y = new_chart.y;
214 acre->chart.width = new_chart.width;
215 acre->chart.height = new_chart.height;
218 cairo_set_source_rgb (cr, 0, 0, 0);
220 cairo_move_to (cr, acre->chart.x, ACRE_PAD);
221 pango_cairo_show_layout (cr, title_layout);
225 cairo_translate (cr, ACRE_PAD, acre->chart.y + acre->chart.height);
226 cairo_rotate (cr, - M_PI / 2.0);
227 cairo_move_to (cr, 0, 0);
228 pango_cairo_show_layout (cr, y_axis_layout);
232 cairo_move_to (cr, acre->chart.x,
233 acre->chart.y + acre->chart.height +
234 ACRE_FONT_SIZE + ACRE_PAD);
235 pango_cairo_show_layout (cr, x_axis_layout);
238 /* Draw the plot to the given cairo context within a user-space
239 * rectangle from (0, 0) to (width, height). This size includes all
240 * space for extra-plot elements (such as the title, the axis labels,
244 acre_draw (acre_t *acre, cairo_t *cr, int width, int height)
248 acre->height = height;
252 cairo_set_source_rgb (cr, 1, 1, 1);
256 _draw_title_and_labels (acre);
259 acre->chart.x - 0.5, acre->chart.y - 0.5,
260 acre->chart.width + 1.0, acre->chart.height + 1.0);
261 cairo_set_line_width (cr, 1.0);
265 /* Create a new dataset---a collection of (x, y) datapoints. A single
266 * plot can contain multiple datasets, (see acre_add_data). */
268 acre_data_create (void)
272 data = xmalloc (sizeof (acre_data_t));
277 data->points_size = 0;
278 data->num_points = 0;
283 /* Destroy an acre dataset. Do not call this function if the dataset
284 * has been added to an acre_t plot with acre_add_data. */
286 acre_data_destroy (acre_data_t *data)
293 /* Set the label for this dataset (to appear in the plot's key). */
295 acre_data_set_name (acre_data_t *data, const char *name)
299 data->name = strdup (name);
302 /* Add a datapoint to the given dataset. */
304 acre_data_add_point_2d (acre_data_t *data, double x, double y)
306 if (data->num_points >= data->points_size) {
307 data->points_size *= 2;
308 if (data->points_size == 0)
309 data->points_size = 16;
310 data->points = xrealloc_ab (data->points,
312 sizeof (acre_data_point_2d_t));
315 data->points[data->num_points].x = x;
316 data->points[data->num_points].y = y;