Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 39 additions & 1 deletion lib/matplotlib/tests/test_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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()
17 changes: 16 additions & 1 deletion src/path_converters.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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:
Expand All @@ -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;
Comment thread
QuLogic marked this conversation as resolved.
break;
}
x0 = x1;
y0 = y1;
Expand Down
Loading