X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fcairo-chart.c;h=802aa07bd2a177ea39e12b8b26921999685f459c;hb=621b1074dec940ae9ebfb1732f1ffa44d5d493b8;hp=a776eab3fc7aed926cb2de4986a235a60017fa8d;hpb=f6e966338eadedb167338c71185f53a4b9830331;p=pspp diff --git a/src/output/cairo-chart.c b/src/output/cairo-chart.c index a776eab3fc..802aa07bd2 100644 --- a/src/output/cairo-chart.c +++ b/src/output/cairo-chart.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 2004, 2009, 2010 Free Software Foundation, Inc. + Copyright (C) 2004, 2009, 2010, 2011, 2014, 2015 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 @@ -17,12 +17,12 @@ #include #include "output/cairo-chart.h" +#include "math/chart-geometry.h" #include #include #include #include -#include #include #include #include @@ -35,9 +35,8 @@ #include "output/cairo.h" #include "output/chart-item.h" -#include "error.h" -#include "xalloc.h" -#include "xvasprintf.h" +#include "gl/xalloc.h" +#include "gl/xvasprintf.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -46,13 +45,14 @@ void xrchart_geometry_init (cairo_t *cr, struct xrchart_geometry *geom, double width, double length) { - /* Set default chartetry. */ - geom->data_top = 0.900 * length; - geom->data_right = 0.800 * width; - geom->data_bottom = 0.120 * length; - geom->data_left = 0.150 * width; - geom->abscissa_top = 0.070 * length; - geom->ordinate_right = 0.120 * width; + /* Set default chart geometry. */ + geom->axis[SCALE_ORDINATE].data_max = 0.900 * length; + geom->axis[SCALE_ORDINATE].data_min = 0.200 * length; + + geom->axis[SCALE_ABSCISSA].data_min = 0.150 * width; + geom->axis[SCALE_ABSCISSA].data_max = 0.800 * width; + geom->abscissa_bottom = 0.070 * length; + geom->ordinate_left = 0.050 * width; geom->title_bottom = 0.920 * length; geom->legend_left = 0.810 * width; geom->legend_right = width; @@ -61,15 +61,13 @@ xrchart_geometry_init (cairo_t *cr, struct xrchart_geometry *geom, geom->dataset = NULL; geom->n_datasets = 0; - geom->fill_colour.red = 255; - geom->fill_colour.green = 0; - geom->fill_colour.blue = 0; + geom->fill_colour = data_colour[0]; cairo_set_line_width (cr, 1.0); - cairo_rectangle (cr, geom->data_left, geom->data_bottom, - geom->data_right - geom->data_left, - geom->data_top - geom->data_bottom); + cairo_rectangle (cr, geom->axis[SCALE_ABSCISSA].data_min, geom->axis[SCALE_ORDINATE].data_min, + geom->axis[SCALE_ABSCISSA].data_max - geom->axis[SCALE_ABSCISSA].data_min, + geom->axis[SCALE_ORDINATE].data_max - geom->axis[SCALE_ORDINATE].data_min); cairo_stroke (cr); } @@ -101,19 +99,42 @@ pango_layout_get_baseline (PangoLayout *layout) } #endif - - +/* + These colours come from: + http://tango.freedesktop.org/static/cvs/tango-art-tools/palettes/Tango-Palette.gpl */ const struct xrchart_colour data_colour[XRCHART_N_COLOURS] = { - { 165, 42, 42 }, /* brown */ - { 255, 0, 0 }, /* red */ - { 255, 165, 0 }, /* orange */ - { 255, 255, 0 }, /* yellow */ - { 0, 255, 0 }, /* green */ - { 0, 0, 255 }, /* blue */ - { 238, 130, 238 }, /* violet */ - { 190, 190, 190 }, /* grey */ - { 255, 192, 203 }, /* pink */ + {252, 233, 79}, /* Butter 1 */ + {138, 226, 52}, /* Chameleon 1 */ + {252, 175, 62}, /* Orange 1 */ + {114, 159, 207}, /* Sky Blue 1 */ + {173, 127, 168}, /* Plum 1 */ + {233, 185, 110}, /* Chocolate 1 */ + {239, 41, 41}, /* Scarlet Red 1 */ + {238, 238, 236}, /* Aluminium 1 */ + + {237, 212, 0}, /* Butter 2 */ + {115, 210, 22}, /* Chameleon 2 */ + {245, 121, 0}, /* Orange 2 */ + {52, 101, 164}, /* Sky Blue 2 */ + {117, 80, 123}, /* Plum 2 */ + {193, 125, 17}, /* Chocolate 2 */ + {204, 0, 0}, /* Scarlet Red 2 */ + + {136, 138, 133}, /* Aluminium 4 */ + + {196, 160, 0}, /* Butter 3 */ + {78, 154, 6}, /* Chameleon 3 */ + {206, 92, 0}, /* Orange 3 */ + {32, 74, 135}, /* Sky Blue 3 */ + {92, 53, 102}, /* Plum 3 */ + {143, 89, 2}, /* Chocolate 3 */ + {164, 0, 0}, /* Scarlet Red 3 */ + {85, 87, 83}, /* Aluminium 5 */ + + {211, 215, 207}, /* Aluminium 2 */ + {186, 189, 182}, /* Aluminium 3 */ + {46, 52, 54}, /* Aluminium 6 */ }; void @@ -150,14 +171,14 @@ xrchart_draw_marker (cairo_t *cr, double x, double y, } void -xrchart_label (cairo_t *cr, int horz_justify, int vert_justify, - double font_size, const char *string) +xrchart_label_rotate (cairo_t *cr, int horz_justify, int vert_justify, + double font_size, const char *string, double angle) { PangoFontDescription *desc; PangoLayout *layout; double x, y; - desc = pango_font_description_from_string ("sans serif"); + desc = pango_font_description_from_string ("Sans"); if (desc == NULL) { cairo_new_path (cr); @@ -166,6 +187,7 @@ xrchart_label (cairo_t *cr, int horz_justify, int vert_justify, pango_font_description_set_absolute_size (desc, font_size * PANGO_SCALE); cairo_save (cr); + cairo_rotate (cr, angle); cairo_get_current_point (cr, &x, &y); cairo_translate (cr, x, y); cairo_move_to (cr, 0, 0); @@ -173,7 +195,7 @@ xrchart_label (cairo_t *cr, int horz_justify, int vert_justify, layout = pango_cairo_create_layout (cr); pango_layout_set_font_description (layout, desc); - pango_layout_set_text (layout, string, -1); + pango_layout_set_markup (layout, string, -1); if (horz_justify != 'l') { int width_pango; @@ -214,26 +236,63 @@ xrchart_label (cairo_t *cr, int horz_justify, int vert_justify, pango_font_description_free (desc); } +void +xrchart_label (cairo_t *cr, int horz_justify, int vert_justify, + double font_size, const char *string) +{ + xrchart_label_rotate (cr, horz_justify, vert_justify, font_size, string, 0); +} + + /* Draw a tick mark at position - If label is non zero, then print it at the tick mark + If label is non null, then print it at the tick mark */ +static void +draw_tick_internal (cairo_t *cr, const struct xrchart_geometry *geom, + enum tick_orientation orientation, + bool rotated, + double position, + const char *s); + void draw_tick (cairo_t *cr, const struct xrchart_geometry *geom, enum tick_orientation orientation, + bool rotated, double position, const char *label, ...) +{ + va_list ap; + char *s; + va_start (ap, label); + s = xvasprintf (label, ap); + + if (fabs (position) < DBL_EPSILON) + position = 0; + + draw_tick_internal (cr, geom, orientation, rotated, position, s); + free (s); + va_end (ap); +} + + +static void +draw_tick_internal (cairo_t *cr, const struct xrchart_geometry *geom, + enum tick_orientation orientation, + bool rotated, + double position, + const char *s) { const int tickSize = 10; double x, y; - cairo_move_to (cr, geom->data_left, geom->data_bottom); + cairo_move_to (cr, geom->axis[SCALE_ABSCISSA].data_min, geom->axis[SCALE_ORDINATE].data_min); - if (orientation == TICK_ABSCISSA) + if (orientation == SCALE_ABSCISSA) { cairo_rel_move_to (cr, position, 0); cairo_rel_line_to (cr, 0, -tickSize); } - else if (orientation == TICK_ORDINATE) + else if (orientation == SCALE_ORDINATE) { cairo_rel_move_to (cr, 0, position); cairo_rel_line_to (cr, -tickSize, 0); @@ -244,25 +303,21 @@ draw_tick (cairo_t *cr, const struct xrchart_geometry *geom, cairo_stroke (cr); - if (label != NULL) + if (s != NULL) { - va_list ap; - char *s; - cairo_move_to (cr, x, y); - va_start (ap, label); - s = xvasprintf (label, ap); - if (orientation == TICK_ABSCISSA) - xrchart_label (cr, 'c', 't', geom->font_size, s); - else if (orientation == TICK_ORDINATE) + if (orientation == SCALE_ABSCISSA) + { + if ( rotated) + xrchart_label_rotate (cr, 'l', 'c', geom->font_size, s, -G_PI_4); + else + xrchart_label (cr, 'c', 't', geom->font_size, s); + } + else if (orientation == SCALE_ORDINATE) { - if (fabs (position) < DBL_EPSILON) - cairo_rel_move_to (cr, 0, 10); xrchart_label (cr, 'r', 'c', geom->font_size, s); } - free (s); - va_end (ap); } } @@ -276,7 +331,7 @@ xrchart_write_title (cairo_t *cr, const struct xrchart_geometry *geom, char *s; cairo_save (cr); - cairo_move_to (cr, geom->data_left, geom->title_bottom); + cairo_move_to (cr, geom->axis[SCALE_ABSCISSA].data_min, geom->title_bottom); va_start(ap, title); s = xvasprintf (title, ap); @@ -287,56 +342,106 @@ xrchart_write_title (cairo_t *cr, const struct xrchart_geometry *geom, cairo_restore (cr); } +static void +xrchart_text_extents (cairo_t *cr, const struct xrchart_geometry *geom, + const char *utf8, + double *width, double *height) +{ + PangoFontDescription *desc; + PangoLayout *layout; + int width_pango; + int height_pango; -/* Set the scale for the abscissa */ -void -xrchart_write_xscale (cairo_t *cr, struct xrchart_geometry *geom, - double min, double max, int ticks) + desc = pango_font_description_from_string ("Sans"); + if (desc == NULL) + return; + pango_font_description_set_absolute_size (desc, geom->font_size * PANGO_SCALE); + layout = pango_cairo_create_layout (cr); + pango_layout_set_font_description (layout, desc); + pango_layout_set_markup (layout, utf8, -1); + pango_layout_get_size (layout, &width_pango, &height_pango); + *width = (double) width_pango / PANGO_SCALE; + *height = (double) height_pango / PANGO_SCALE; + g_object_unref (layout); + pango_font_description_free (desc); +} + +static void +xrchart_write_scale (cairo_t *cr, struct xrchart_geometry *geom, + double smin, double smax, enum tick_orientation orient) { - double x; + int s; + int ticks; - const double tick_interval = - chart_rounded_tick ((max - min) / (double) ticks); + double interval; + double lower; + double upper; + double tickscale; + char *tick_format_string; + bool tickoversize = false; - 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); + chart_get_scale (smax, smin, &lower, &interval, &ticks); - for (x = geom->x_min; x <= geom->x_max; x += tick_interval) - draw_tick (cr, geom, TICK_ABSCISSA, - (x - geom->x_min) * geom->abscissa_scale, "%g", x); -} + tick_format_string = chart_get_ticks_format (lower, interval, ticks, &tickscale); + + upper = lower + interval * (ticks+1); + + geom->axis[orient].max = upper; + geom->axis[orient].min = lower; + + geom->axis[orient].scale = (fabs (geom->axis[orient].data_max - geom->axis[orient].data_min) + / fabs (geom->axis[orient].max - geom->axis[orient].min)); + + if (orient == SCALE_ABSCISSA) + { + char *test_text; + double lower_txt_width, upper_txt_width, unused, width; + test_text = xasprintf(tick_format_string, upper*tickscale); + xrchart_text_extents (cr, geom, test_text, &upper_txt_width, &unused); + free(test_text); + test_text = xasprintf(tick_format_string, lower*tickscale); + xrchart_text_extents (cr, geom, test_text, &lower_txt_width, &unused); + free(test_text); + width = MAX(lower_txt_width, upper_txt_width); + tickoversize = width > 0.9 * + ((double)(geom->axis[SCALE_ABSCISSA].data_max - geom->axis[SCALE_ABSCISSA].data_min))/(ticks+1); + } + + double pos = lower; + for (s = 0 ; s <= ticks; ++s) + { + draw_tick (cr, geom, orient, tickoversize, + s * interval * geom->axis[orient].scale, + tick_format_string, pos*tickscale); + pos += interval; + } + free(tick_format_string); +} /* Set the scale for the ordinate */ void xrchart_write_yscale (cairo_t *cr, struct xrchart_geometry *geom, - double smin, double smax, int ticks) + double smin, double smax) { - double y; - - const double tick_interval = - chart_rounded_tick ((smax - smin) / (double) ticks); - - geom->y_max = ceil (smax / tick_interval) * tick_interval; - geom->y_min = floor (smin / tick_interval) * tick_interval; - - geom->ordinate_scale = - (fabs (geom->data_top - geom->data_bottom) - / fabs (geom->y_max - geom->y_min)); + xrchart_write_scale (cr, geom, smin, smax, SCALE_ORDINATE); +} - for (y = geom->y_min; y <= geom->y_max; y += tick_interval) - draw_tick (cr, geom, TICK_ORDINATE, - (y - geom->y_min) * geom->ordinate_scale, "%g", y); +/* Set the scale for the abscissa */ +void +xrchart_write_xscale (cairo_t *cr, struct xrchart_geometry *geom, + double smin, double smax) +{ + xrchart_write_scale (cr, geom, smin, smax, SCALE_ABSCISSA); } + /* Write the abscissa label */ void xrchart_write_xlabel (cairo_t *cr, const struct xrchart_geometry *geom, const char *label) { - cairo_move_to (cr, geom->data_left, geom->abscissa_top); + cairo_move_to (cr, geom->axis[SCALE_ABSCISSA].data_min, geom->abscissa_bottom); xrchart_label (cr, 'l', 't', geom->font_size, label); } @@ -346,9 +451,9 @@ xrchart_write_ylabel (cairo_t *cr, const struct xrchart_geometry *geom, const char *label) { cairo_save (cr); - cairo_translate (cr, -geom->data_bottom, -geom->ordinate_right); - cairo_move_to (cr, 0, 0); + cairo_translate (cr, geom->ordinate_left, geom->axis[SCALE_ORDINATE].data_min); cairo_rotate (cr, M_PI / 2.0); + xrchart_label (cr, 'l', 'x', geom->font_size, label); cairo_restore (cr); } @@ -362,7 +467,7 @@ xrchart_write_legend (cairo_t *cr, const struct xrchart_geometry *geom) const int xpad = 10; const int ypad = 10; const int swatch = 20; - const int legend_top = geom->data_top; + const int legend_top = geom->axis[SCALE_ORDINATE].data_max; const int legend_bottom = legend_top - (vstep * geom->n_datasets + 2 * ypad ); @@ -425,10 +530,10 @@ void xrchart_datum (cairo_t *cr, const struct xrchart_geometry *geom, int dataset UNUSED, double x, double y) { - 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; + double x_pos = (x - geom->axis[SCALE_ABSCISSA].min) * geom->axis[SCALE_ABSCISSA].scale + geom->axis[SCALE_ABSCISSA].data_min; + double y_pos = (y - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale + geom->axis[SCALE_ORDINATE].data_min; - xrchart_draw_marker (cr, x_pos, y_pos, XRMARKER_SQUARE, 15); + xrchart_draw_marker (cr, x_pos, y_pos, XRMARKER_CIRCLE, 10); } void @@ -444,10 +549,10 @@ void xrchart_vector (cairo_t *cr, struct xrchart_geometry *geom, double x, double y) { const double x_pos = - (x - geom->x_min) * geom->abscissa_scale + geom->data_left ; + (x - geom->axis[SCALE_ABSCISSA].min) * geom->axis[SCALE_ABSCISSA].scale + geom->axis[SCALE_ABSCISSA].data_min ; const double y_pos = - (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom ; + (y - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale + geom->axis[SCALE_ORDINATE].data_min ; if (geom->in_path) cairo_line_to (cr, x_pos, y_pos); @@ -488,12 +593,13 @@ xrchart_line(cairo_t *cr, const struct xrchart_geometry *geom, y2 = slope * x2 + intercept; } - 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; + y1 = (y1 - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale + geom->axis[SCALE_ORDINATE].data_min; + y2 = (y2 - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale + geom->axis[SCALE_ORDINATE].data_min; + x1 = (x1 - geom->axis[SCALE_ABSCISSA].min) * geom->axis[SCALE_ABSCISSA].scale + geom->axis[SCALE_ABSCISSA].data_min; + x2 = (x2 - geom->axis[SCALE_ABSCISSA].min) * geom->axis[SCALE_ABSCISSA].scale + geom->axis[SCALE_ABSCISSA].data_min; cairo_move_to (cr, x1, y1); cairo_line_to (cr, x2, y2); cairo_stroke (cr); } +