This is the final step of adjusting the axis ranges so that
the ticks aligne nicely with integer pixel boundaries. Notably
we don't round the ticks at all while drawing them---instead
we only adjust the axis ranges so that when we draw the ticks
at their numerical positions they will look good.
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
+#define _ISOC99_SOURCE /* for round() */
+#define _XOPEN_SOURCE 500
+
#include "acre.h"
#include "xmalloc.h"
#include "acre.h"
#include "xmalloc.h"
return data_range * pixel_step / floor (pixel_step);
}
return data_range * pixel_step / floor (pixel_step);
}
+/* Setup a transformation in acre->cr such that data values plotted
+ * will appear where they should within the chart.
+ */
+static void
+_set_transform_to_data_space (acre_t *acre)
+{
+ cairo_t *cr = acre->cr;
+
+ cairo_translate (cr,
+ acre->chart.x,
+ acre->chart.y + acre->chart.height);
+ cairo_scale (cr,
+ acre->chart.width / (acre->x_axis.max - acre->x_axis.min),
+ - acre->chart.height /(acre->y_axis.max - acre->y_axis.min));
+ cairo_translate (cr, -acre->x_axis.min, -acre->y_axis.min);
+}
+
static void
_compute_axis_ranges (acre_t *acre)
{
unsigned int d, i;
acre_data_t *data;
static void
_compute_axis_ranges (acre_t *acre)
{
unsigned int d, i;
acre_data_t *data;
- double x_range, new_x_range;
- double y_range, new_y_range;
+ double x_range, new_x_range, x_adjust;
+ double y_range, new_y_range, y_adjust;
+ cairo_t *cr = acre->cr;
/* First, simply find the extrema of the data. */
for (d = 0; d < acre->num_data; d++) {
/* First, simply find the extrema of the data. */
for (d = 0; d < acre->num_data; d++) {
acre->y_axis.min -= (new_y_range - y_range) / 2.0;
acre->y_axis.max += (new_y_range - y_range) / 2.0;
acre->y_axis.min -= (new_y_range - y_range) / 2.0;
acre->y_axis.max += (new_y_range - y_range) / 2.0;
-/* Setup a transformation in acre->cr such that data values plotted
- * will appear where they should within the chart.
- */
-static void
-_set_transform_to_data_space (acre_t *acre)
-{
- cairo_t *cr = acre->cr;
+ /* Finally, we also translate the axis ranges slightly so that the
+ * ticks land on half-integer device-pixel positions.
+ */
+ cairo_save (cr);
+ {
+ _set_transform_to_data_space (acre);
- cairo_translate (cr,
- acre->chart.x,
- acre->chart.y + acre->chart.height);
- cairo_scale (cr,
- acre->chart.width / (acre->x_axis.max - acre->x_axis.min),
- - acre->chart.height /(acre->y_axis.max - acre->y_axis.min));
- cairo_translate (cr, -acre->x_axis.min, -acre->y_axis.min);
+ x_adjust = 0.0;
+ y_adjust = 0.0;
+ cairo_user_to_device (cr, &x_adjust, &y_adjust);
+ x_adjust = (round (x_adjust + 0.5) - 0.5) - x_adjust;
+ y_adjust = (round (y_adjust + 0.5) - 0.5) - y_adjust;
+ cairo_device_to_user_distance (cr, &x_adjust, &y_adjust);
+
+ acre->x_axis.min -= x_adjust;
+ acre->x_axis.max -= x_adjust;
+
+ acre->y_axis.min -= y_adjust;
+ acre->y_axis.max -= y_adjust;
+ }
+ cairo_restore (cr);
{
cairo_identity_matrix (cr);
cairo_rel_line_to (cr, 0, 0.5);
{
cairo_identity_matrix (cr);
cairo_rel_line_to (cr, 0, 0.5);
- cairo_rel_line_to (cr, 0, -ACRE_TICK_SIZE);
+ cairo_rel_line_to (cr, 0, -ACRE_TICK_SIZE-0.5);
cairo_set_line_width (cr, 1.0);
cairo_stroke (cr);
}
cairo_set_line_width (cr, 1.0);
cairo_stroke (cr);
}
{
cairo_identity_matrix (cr);
cairo_rel_line_to (cr, -0.5, 0);
{
cairo_identity_matrix (cr);
cairo_rel_line_to (cr, -0.5, 0);
- cairo_rel_line_to (cr, ACRE_TICK_SIZE, 0);
+ cairo_rel_line_to (cr, ACRE_TICK_SIZE+0.5, 0);
cairo_set_line_width (cr, 1.0);
cairo_stroke (cr);
}
cairo_set_line_width (cr, 1.0);
cairo_stroke (cr);
}