Skip to content

Commit 1262326

Browse files
committed
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.
1 parent b05aa16 commit 1262326

File tree

3 files changed

+16
-16
lines changed

3 files changed

+16
-16
lines changed

lib/matplotlib/markers.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -277,13 +277,13 @@ def _set_pixel(self):
277277
# Ideally, you'd want -0.5, -0.5 here, but then the snapping
278278
# algorithm in the Agg backend will round this to a 2x2
279279
# rectangle from (-1, -1) to (1, 1). By offsetting it
280-
# slightly, we can force it to be (0, -1) to (1, 0), which
281-
# both makes it only be a single pixel and places it correctly
282-
# with 1-width stroking (i.e. the ticks). This hack is the
283-
# best of a number of bad alternatives, mainly because the
280+
# slightly, we can force it to be (0, 0) to (1, 1), which both
281+
# makes it only be a single pixel and places it correctly
282+
# aligned to 1-width stroking (i.e. the ticks). This hack is
283+
# the best of a number of bad alternatives, mainly because the
284284
# backends are not aware of what marker is actually being used
285285
# beyond just its path data.
286-
self._transform = Affine2D().translate(-0.49999, -0.50001)
286+
self._transform = Affine2D().translate(-0.49999, -0.49999)
287287
self._snap_threshold = None
288288

289289
def _set_point(self):

src/_backend_agg.cpp

+9-9
Original file line numberDiff line numberDiff line change
@@ -433,10 +433,10 @@ RendererAgg::set_clipbox(const Py::Object& cliprect, R& rasterizer)
433433
double l, b, r, t;
434434
if (py_convert_bbox(cliprect.ptr(), l, b, r, t))
435435
{
436-
rasterizer.clip_box(std::max(int(mpl_round(l)), 0),
437-
std::max(int(height) - int(mpl_round(b)), 0),
438-
std::min(int(mpl_round(r)), int(width)),
439-
std::min(int(height) - int(mpl_round(t)), int(height)));
436+
rasterizer.clip_box(std::max(int(floor(l - 0.5)), 0),
437+
std::max(int(floor(height - b - 0.5)), 0),
438+
std::min(int(floor(r - 0.5)), int(width)),
439+
std::min(int(floor(height - t - 0.5)), int(height)));
440440
}
441441
else
442442
{
@@ -650,7 +650,7 @@ RendererAgg::draw_markers(const Py::Tuple& args)
650650
// Deal with the difference in y-axis direction
651651
marker_trans *= agg::trans_affine_scaling(1.0, -1.0);
652652
trans *= agg::trans_affine_scaling(1.0, -1.0);
653-
trans *= agg::trans_affine_translation(0.0, (double)height);
653+
trans *= agg::trans_affine_translation(0.5, (double)height + 0.5);
654654

655655
PathIterator marker_path(marker_path_obj);
656656
transformed_path_t marker_path_transformed(marker_path, marker_trans);
@@ -736,8 +736,8 @@ RendererAgg::draw_markers(const Py::Tuple& args)
736736
continue;
737737
}
738738

739-
x = (double)(int)x;
740-
y = (double)(int)y;
739+
x = floor(x);
740+
y = floor(y);
741741

742742
// Cull points outside the boundary of the image.
743743
// Values that are too large may overflow and create
@@ -772,8 +772,8 @@ RendererAgg::draw_markers(const Py::Tuple& args)
772772
continue;
773773
}
774774

775-
x = (double)(int)x;
776-
y = (double)(int)y;
775+
x = floor(x);
776+
y = floor(y);
777777

778778
// Cull points outside the boundary of the image.
779779
// Values that are too large may overflow and create

src/path_converters.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -494,8 +494,8 @@ class PathSnapper
494494
code = m_source->vertex(x, y);
495495
if (m_snap && agg::is_vertex(code))
496496
{
497-
*x = mpl_round(*x) + m_snap_value;
498-
*y = mpl_round(*y) + m_snap_value;
497+
*x = floor(*x - m_snap_value) + m_snap_value;
498+
*y = floor(*y - m_snap_value) + m_snap_value;
499499
}
500500
return code;
501501
}

0 commit comments

Comments
 (0)