From 1262326c00074fb497816242c9aa0e7787bd5cda Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Thu, 1 Mar 2012 10:52:54 -0500 Subject: [PATCH] Further refinement of marker placement and snapping. Since paths (particularly marker paths, which are centered at (0, 0)) can cross the origin, it is better to round using floor() than round() (which rounds toward 0). Markers are placed correctly with respect to the snapping of the axes that contains them. The clipbox calculation has been adjusted to match the snapping of the axes rectangle. --- lib/matplotlib/markers.py | 10 +++++----- src/_backend_agg.cpp | 18 +++++++++--------- src/path_converters.h | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/matplotlib/markers.py b/lib/matplotlib/markers.py index fea9150d1a71..a6588c7c67f5 100644 --- a/lib/matplotlib/markers.py +++ b/lib/matplotlib/markers.py @@ -277,13 +277,13 @@ def _set_pixel(self): # Ideally, you'd want -0.5, -0.5 here, but then the snapping # algorithm in the Agg backend will round this to a 2x2 # rectangle from (-1, -1) to (1, 1). By offsetting it - # slightly, we can force it to be (0, -1) to (1, 0), which - # both makes it only be a single pixel and places it correctly - # with 1-width stroking (i.e. the ticks). This hack is the - # best of a number of bad alternatives, mainly because the + # slightly, we can force it to be (0, 0) to (1, 1), which both + # makes it only be a single pixel and places it correctly + # aligned to 1-width stroking (i.e. the ticks). This hack is + # the best of a number of bad alternatives, mainly because the # backends are not aware of what marker is actually being used # beyond just its path data. - self._transform = Affine2D().translate(-0.49999, -0.50001) + self._transform = Affine2D().translate(-0.49999, -0.49999) self._snap_threshold = None def _set_point(self): diff --git a/src/_backend_agg.cpp b/src/_backend_agg.cpp index f7a986fc2f95..96a9402ac0a8 100644 --- a/src/_backend_agg.cpp +++ b/src/_backend_agg.cpp @@ -433,10 +433,10 @@ RendererAgg::set_clipbox(const Py::Object& cliprect, R& rasterizer) double l, b, r, t; if (py_convert_bbox(cliprect.ptr(), l, b, r, t)) { - rasterizer.clip_box(std::max(int(mpl_round(l)), 0), - std::max(int(height) - int(mpl_round(b)), 0), - std::min(int(mpl_round(r)), int(width)), - std::min(int(height) - int(mpl_round(t)), int(height))); + rasterizer.clip_box(std::max(int(floor(l - 0.5)), 0), + std::max(int(floor(height - b - 0.5)), 0), + std::min(int(floor(r - 0.5)), int(width)), + std::min(int(floor(height - t - 0.5)), int(height))); } else { @@ -650,7 +650,7 @@ RendererAgg::draw_markers(const Py::Tuple& args) // Deal with the difference in y-axis direction marker_trans *= agg::trans_affine_scaling(1.0, -1.0); trans *= agg::trans_affine_scaling(1.0, -1.0); - trans *= agg::trans_affine_translation(0.0, (double)height); + trans *= agg::trans_affine_translation(0.5, (double)height + 0.5); PathIterator marker_path(marker_path_obj); transformed_path_t marker_path_transformed(marker_path, marker_trans); @@ -736,8 +736,8 @@ RendererAgg::draw_markers(const Py::Tuple& args) continue; } - x = (double)(int)x; - y = (double)(int)y; + x = floor(x); + y = floor(y); // Cull points outside the boundary of the image. // Values that are too large may overflow and create @@ -772,8 +772,8 @@ RendererAgg::draw_markers(const Py::Tuple& args) continue; } - x = (double)(int)x; - y = (double)(int)y; + x = floor(x); + y = floor(y); // Cull points outside the boundary of the image. // Values that are too large may overflow and create diff --git a/src/path_converters.h b/src/path_converters.h index c45ff862c6f6..faacf15ad924 100644 --- a/src/path_converters.h +++ b/src/path_converters.h @@ -494,8 +494,8 @@ class PathSnapper code = m_source->vertex(x, y); if (m_snap && agg::is_vertex(code)) { - *x = mpl_round(*x) + m_snap_value; - *y = mpl_round(*y) + m_snap_value; + *x = floor(*x - m_snap_value) + m_snap_value; + *y = floor(*y - m_snap_value) + m_snap_value; } return code; }