diff --git a/lib/matplotlib/tests/baseline_images/test_axes/preset_clip_paths.png b/lib/matplotlib/tests/baseline_images/test_axes/preset_clip_paths.png index e6beb32ed024..a18c12f1ff68 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/preset_clip_paths.png and b/lib/matplotlib/tests/baseline_images/test_axes/preset_clip_paths.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/sticky_tolerance_cf.png b/lib/matplotlib/tests/baseline_images/test_axes/sticky_tolerance_cf.png index a2e185c2769d..3629e8200e02 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/sticky_tolerance_cf.png and b/lib/matplotlib/tests/baseline_images/test_axes/sticky_tolerance_cf.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/double_cbar.png b/lib/matplotlib/tests/baseline_images/test_colorbar/double_cbar.png index adfd661fb6e5..3222573486b5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_colorbar/double_cbar.png and b/lib/matplotlib/tests/baseline_images/test_colorbar/double_cbar.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_submerged_with_colorbar.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_submerged_with_colorbar.png index f53c2b8035a9..cb16fa6e8614 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_submerged_with_colorbar.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_submerged_with_colorbar.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_contour/contour_datetime_axis.png b/lib/matplotlib/tests/baseline_images/test_contour/contour_datetime_axis.png index 7cdc21e820b9..073224906f21 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_contour/contour_datetime_axis.png and b/lib/matplotlib/tests/baseline_images/test_contour/contour_datetime_axis.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_colors_and_levels.png b/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_colors_and_levels.png index b01bcb239535..10d08641af95 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_colors_and_levels.png and b/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_colors_and_levels.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_contour/contourf_hatch_colors.png b/lib/matplotlib/tests/baseline_images/test_contour/contourf_hatch_colors.png index 18d949773ded..38781b8f4c66 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_contour/contourf_hatch_colors.png and b/lib/matplotlib/tests/baseline_images/test_contour/contourf_hatch_colors.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.png b/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.png index f7ac255e56a6..7bb40b258e21 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.png and b/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.png differ diff --git a/lib/matplotlib/tests/test_agg.py b/lib/matplotlib/tests/test_agg.py index c55f27e03a41..9b34f8fee09c 100644 --- a/lib/matplotlib/tests/test_agg.py +++ b/lib/matplotlib/tests/test_agg.py @@ -13,8 +13,9 @@ from matplotlib.backends.backend_agg import RendererAgg from matplotlib.figure import Figure from matplotlib.image import imread +from matplotlib.patches import PathPatch, Polygon from matplotlib.path import Path -from matplotlib.testing.decorators import image_comparison +from matplotlib.testing.decorators import check_figures_equal, image_comparison from matplotlib.transforms import IdentityTransform @@ -387,3 +388,40 @@ def test_non_tuple_rgbaface(): fig.add_subplot(projection="3d").scatter( [0, 1, 2], [0, 1, 2], path_effects=[patheffects.Stroke(linewidth=4)]) fig.canvas.draw() + + +@check_figures_equal() +def test_path_autosnap(fig_test, fig_ref): + axt = fig_test.subplots() + axr = fig_ref.subplots() + + square = np.array([[0, 0], [1, 0], [1, 1], [0, 1]]) + triangle1 = np.array([[0, 2], [1, 2], [0, 3]]) + triangle2 = np.array([[0, 4], [1, 4], [1, 3]]) + + # A square should autosnap + axt.add_patch(Polygon(square, snap=None)) + axr.add_patch(Polygon(square, snap=True)) + + # A triangle with the diagonal as one of the line segments should not autosnap + axt.add_patch(Polygon(triangle1, snap=None)) + axr.add_patch(Polygon(triangle1, snap=False)) + + # A triangle with the diagonal as the closing line segment should not autosnap + axt.add_patch(Polygon(triangle2, snap=None)) + axr.add_patch(Polygon(triangle2, snap=False)) + + # A path of multiple polygons should autosnap if each polygon would autosnap + multiple1 = Path.make_compound_path(Polygon(square + [[2, 0]]).get_path(), + Polygon(square + [[2, 2]]).get_path()) + axt.add_patch(PathPatch(multiple1, edgecolor='none', snap=None)) + axr.add_patch(PathPatch(multiple1, edgecolor='none', snap=True)) + + # A path of multiple polygons should not autosnap if any polygon would not autosnap + multiple2 = Path.make_compound_path(Polygon(square + [[4, 0]]).get_path(), + Polygon(triangle1 + [[4, 0]]).get_path()) + axt.add_patch(PathPatch(multiple2, edgecolor='none', snap=None)) + axr.add_patch(PathPatch(multiple2, edgecolor='none', snap=False)) + + axt.autoscale_view() + axr.autoscale_view() diff --git a/src/path_converters.h b/src/path_converters.h index 1482aeed95f8..d8e08f26dc09 100644 --- a/src/path_converters.h +++ b/src/path_converters.h @@ -560,7 +560,7 @@ class PathSnapper { // If this contains only straight horizontal or vertical lines, it should be // snapped to the nearest pixels - double x0 = 0, y0 = 0, x1 = 0, y1 = 0; + double xi = 0, yi = 0, x0 = 0, y0 = 0, x1 = 0, y1 = 0; unsigned code; switch (snap_mode) { @@ -574,6 +574,10 @@ class PathSnapper return false; } + // Store the initial vertex in case the path gets closed + xi = x0; + yi = y0; + while ((code = path.vertex(&x1, &y1)) != agg::path_cmd_stop) { switch (code) { case agg::path_cmd_curve3: @@ -583,6 +587,17 @@ class PathSnapper if (fabs(x0 - x1) >= 1e-4 && fabs(y0 - y1) >= 1e-4) { return false; } + break; + case (agg::path_cmd_end_poly | agg::path_flags_close): + if (fabs(x0 - xi) >= 1e-4 && fabs(y0 - yi) >= 1e-4) { + return false; + } + break; + case agg::path_cmd_move_to: + // Update the initial vertex for a new sub-path + xi = x1; + yi = y1; + break; } x0 = x1; y0 = y1;