#include "decimal.h"
#include <stdlib.h>
+#include "gl/xalloc.h"
+#include "gl/minmax.h"
+#include "gl/xvasprintf.h"
+
static const double standard_tick[] = {1, 2, 5, 10};
/* Adjust tick to be a sensible value
}
}
}
+
+/*
+ * Compute the optimum format string and the scaling
+ * for the tick drawing on a chart axis
+ * Input: max: the maximum value of the range
+ * min: the minimum value of the range
+ * nticks: the number of tick intervals (bins) on the axis
+ * Return: fs: format string for printf to print the tick value
+ * scale: scaling factor for the tick value
+ * The format string has to be freed after usage.
+ * An example format string and scalefactor:
+ * Non Scientific: "%.3lf", scale=1.00
+ * Scientific: "%.2lfe3", scale = 0.001
+ * Usage example:
+ * fs = chart_get_ticks_format(95359943.3,34434.9,8,&scale,&long);
+ * printf(fs,value*scale);
+ * free(fs);
+ */
+char *
+chart_get_ticks_format (const double max, const double min,
+ const unsigned int nticks, double *scale)
+{
+ assert(max > min);
+ double interval = (max - min)/nticks;
+ double logmax = log10(fmax(fabs(max),fabs(min)));
+ double logintv = log10(interval);
+ int logshift = 0;
+ char *format_string = NULL;
+ int nrdecs = 0;
+
+ if (logmax > 0.0 && logintv < 0.0)
+ {
+ nrdecs = MIN(6,(int)(fabs(logintv))+1);
+ logshift = 0;
+ format_string = xasprintf("%%.%dlf",nrdecs);
+ }
+ else if (logmax > 0.0) /*logintv is > 0*/
+ {
+ if (logintv < 3.0)
+ {
+ logshift = 0; /* No scientific format */
+ nrdecs = 0;
+ format_string = xstrdup("%.0lf");
+ }
+ else
+ {
+ logshift = (int)logmax;
+ nrdecs = MIN(6,(int)(logmax-logintv)+1);
+ format_string = xasprintf("%%.%dlfe%d",nrdecs,logshift);
+ }
+ }
+ else /* logmax and logintv are < 0 */
+ {
+ if (logmax > -3.0)
+ {
+ logshift = 0; /* No scientific format */
+ nrdecs = (int)(-logintv) + 1;
+ format_string = xasprintf("%%.%dlf",nrdecs);
+ }
+ else
+ {
+ logshift = (int)logmax-1;
+ nrdecs = MIN(6,(int)(logmax-logintv)+1);
+ format_string = xasprintf("%%.%dlfe%d",nrdecs,logshift);
+ }
+ }
+ *scale = pow(10.0,-(double)logshift);
+ return format_string;
+}
/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 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
void chart_get_scale (double high, double low,
struct decimal *lower, struct decimal *interval, int *n_ticks);
+char *
+chart_get_ticks_format (const double max, const double min, const unsigned int nticks,
+ double *scale);
#endif
cairo_line_to (cr, x2, y2);
cairo_stroke (cr);
}
+
+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;
+
+ desc = pango_font_description_from_string ("sans serif");
+ 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_text (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);
+}
/* PSPP - a program for statistical analysis.
- Copyright (C) 2009, 2011 Free Software Foundation, Inc.
+ Copyright (C) 2009, 2011, 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
void xrchart_draw_scatterplot (const struct chart_item *, cairo_t *,
struct xrchart_geometry *);
+/* Get the width and height of rendered label text */
+void xrchart_text_extents (cairo_t *cr, const struct xrchart_geometry *geom,
+ const char *utf8,
+ double *width, double *height);
#endif /* output/cairo-chart.h */
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
-#include "math/decimal.h"
+#include "math/chart-geometry.h"
#include "output/charts/plot-hist.h"
#include <float.h>
#include "output/cairo-chart.h"
#include "gl/xvasprintf.h"
+#include "gl/minmax.h"
#include "gettext.h"
#define _(msgid) gettext (msgid)
static void
hist_draw_bar (cairo_t *cr, const struct xrchart_geometry *geom,
- const gsl_histogram *h, int bar, bool label)
+ const gsl_histogram *h, int bar, const char *tick_format_string,
+ const double tickscale, const bool tickoversize)
{
double upper;
double lower;
cairo_restore (cr);
cairo_stroke (cr);
- if (label)
- {
- struct decimal decupper;
- struct decimal declower;
- struct decimal middle;
- decimal_from_double (&declower, lower);
- decimal_from_double (&decupper, upper);
- middle = declower;
- decimal_add (&middle, &decupper);
- decimal_int_divide (&middle, 2);
- char *str = decimal_to_string (&middle);
- draw_tick (cr, geom, SCALE_ABSCISSA, bins > 10,
- x_pos + width / 2.0, "%s", str);
- free (str);
- }
+ draw_tick (cr, geom, SCALE_ABSCISSA, tickoversize,
+ x_pos + width / 2.0, tick_format_string, (upper+lower)/2.0*tickscale);
+
}
void
struct histogram_chart *h = to_histogram_chart (chart_item);
int i;
int bins;
+ char *tick_format_string;
+ char *test_text;
+ double width, left_width, right_width, unused;
+ double tickscale;
+ bool tickoversize;
xrchart_write_title (cr, geom, _("HISTOGRAM"));
return;
}
- bins = gsl_histogram_bins (h->gsl_hist);
-
xrchart_write_yscale (cr, geom, 0, gsl_histogram_max_val (h->gsl_hist));
+ /* Draw the ticks and compute if the rendered tick text is wider than the bin */
+ bins = gsl_histogram_bins (h->gsl_hist);
+ tick_format_string = chart_get_ticks_format (gsl_histogram_max (h->gsl_hist),
+ gsl_histogram_min (h->gsl_hist),
+ bins,
+ &tickscale);
+ test_text = xasprintf(tick_format_string, gsl_histogram_max (h->gsl_hist)*tickscale);
+ xrchart_text_extents (cr, geom, test_text, &right_width, &unused);
+ free(test_text);
+ test_text = xasprintf(tick_format_string, gsl_histogram_min (h->gsl_hist)*tickscale);
+ xrchart_text_extents (cr, geom, test_text, &left_width, &unused);
+ free(test_text);
+ width = MAX(left_width, right_width);
+ tickoversize = width > 0.9 *
+ ((double)(geom->axis[SCALE_ABSCISSA].data_max - geom->axis[SCALE_ABSCISSA].data_min))/bins;
for (i = 0; i < bins; i++)
{
- hist_draw_bar (cr, geom, h->gsl_hist, i, true);
+ hist_draw_bar (cr, geom, h->gsl_hist, i, tick_format_string, tickscale, tickoversize);
}
+ free(tick_format_string);
histogram_write_legend (cr, geom, h->n, h->mean, h->stddev);
src/math/libpspp-math.la \
src/libpspp/liblibpspp.la \
src/libpspp-core.la \
- gl/libgl.la
+ gl/libgl.la
+check_PROGRAMS += tests/math/chart-get-ticks-format-test
+tests_math_chart_get_ticks_format_test_SOURCES = tests/math/chart-get-ticks-format-test.c
+tests_math_chart_get_ticks_format_test_LDADD = \
+ src/math/libpspp-math.la \
+ src/libpspp/liblibpspp.la \
+ src/libpspp-core.la \
+ gl/libgl.la
check_PROGRAMS += tests/math/decimal-test
tests_math_decimal_test_SOURCES = tests/math/decimal-test.c
AT_CHECK([../../math/chart-get-scale-test], [0], [ignore])
AT_CLEANUP
+
+
+AT_SETUP([Chart Ticks Format])
+
+AT_CHECK([../../math/chart-get-ticks-format-test], [0], [dnl
+max: 1000, min: 10, nticks: 10, fs: %.0lf, scale: 1, example: 505
+max: 10000, min: 10, nticks: 10, fs: %.0lf, scale: 1, example: 5005
+max: 100000, min: 10, nticks: 10, fs: %.2lfe5, scale: 1e-05, example: 0.50e5
+max: 1e+06, min: 10, nticks: 10, fs: %.2lfe6, scale: 1e-06, example: 0.50e6
+max: 1e+07, min: 10, nticks: 10, fs: %.2lfe7, scale: 1e-07, example: 0.50e7
+max: 1e+08, min: 10, nticks: 10, fs: %.2lfe8, scale: 1e-08, example: 0.50e8
+max: 0.1, min: 0.01, nticks: 10, fs: %.3lf, scale: 1, example: 0.055
+max: 1e-05, min: 1e-06, nticks: 10, fs: %.2lfe-6, scale: 1e+06, example: 5.50e-6
+max: 1.00001e-05, min: 1e-05, nticks: 10, fs: %.6lfe-5, scale: 100000, example: 1.000005e-5
+max: 1e+08, min: 1e+08, nticks: 10, fs: %.0lf, scale: 1, example: 100000005
+max: 100000, min: -500000, nticks: 10, fs: %.1lfe5, scale: 1e-05, example: -2.0e5
+max: 5, min: -5, nticks: 10, fs: %.0lf, scale: 1, example: 0
+max: 5, min: -4.999, nticks: 10, fs: %.1lf, scale: 1, example: 0.0
+max: 5, min: -4.999, nticks: 9, fs: %.0lf, scale: 1, example: 0
+max: 5, min: 0, nticks: 10, fs: %.1lf, scale: 1, example: 2.5
+max: 0, min: -5, nticks: 9, fs: %.1lf, scale: 1, example: -2.5
+max: 1.001e-95, min: 1e-95, nticks: 10, fs: %.5lfe-95, scale: 1e+95, example: 1.00050e-95
+max: 1.001e+98, min: 1e+98, nticks: 10, fs: %.5lfe98, scale: 1e-98, example: 1.00050e98
+max: 1.001e+33, min: 1e-22, nticks: 10, fs: %.2lfe33, scale: 1e-33, example: 0.50e33
+])
+
+AT_CLEANUP
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 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
+ 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/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "math/chart-geometry.h"
+#include "libpspp/compiler.h"
+
+struct range {
+ double max;
+ double min;
+ int nticks;
+};
+
+struct range tv[] = {
+ { 1000.0, 10.0, 10},
+ { 10000.0, 10.0, 10},
+ { 100000.0, 10.0, 10},
+ { 1000000.0, 10.0, 10},
+ { 10000000.0, 10.0, 10},
+ { 100000000.0, 10.0, 10},
+ { 0.1, 0.01, 10},
+ { 0.00001, 0.000001, 10},
+ { 0.0000100001, 0.00001, 10},
+ { 100000010.0, 100000000.0, 10},
+ { 100000.0, -500000.0, 10},
+ { 5.0, -5.0, 10},
+ { 5.0, -4.999, 10},
+ { 5.0, -4.999, 9},
+ { 5.0, 0.0, 10},
+ { 0.0, -5.0, 9},
+ { 1.001E-95, 1.0E-95, 10},
+ { 1.001E98, 1.0E98, 10},
+ { 1.001E33, 1.0E-22, 10},
+ { 0.0, 0.0, -1}
+};
+
+int
+main (int argc UNUSED, char **argv UNUSED)
+{
+ char *fs;
+ double scale;
+ int i = 0;
+ double max, min;
+ int nticks;
+
+ for(i=0;tv[i].nticks > 0;i++)
+ {
+ max = tv[i].max;
+ min = tv[i].min;
+ nticks = tv[i].nticks;
+ fs = chart_get_ticks_format (max, min, nticks, &scale);
+ printf("max: %lg, min: %lg, nticks: %d, fs: %s, scale: %lg, example: ",
+ max, min, nticks, fs, scale);
+ printf(fs,((max-min)/2.0+min)*scale);
+ printf("\n");
+ free(fs);
+ }
+
+ return 0;
+}