diff --git a/lib/matplotlib/hatch.py b/lib/matplotlib/hatch.py index 9ec88776cfd3..8ad68e8623fa 100644 --- a/lib/matplotlib/hatch.py +++ b/lib/matplotlib/hatch.py @@ -133,7 +133,7 @@ class SmallCircles(Circles): size = 0.2 def __init__(self, hatch, density): - self.num_rows = (hatch.count('o')) * density + self.num_rows = int((hatch.count('o')) * density) super().__init__(hatch, density) @@ -141,7 +141,7 @@ class LargeCircles(Circles): size = 0.35 def __init__(self, hatch, density): - self.num_rows = (hatch.count('O')) * density + self.num_rows = int((hatch.count('O')) * density) super().__init__(hatch, density) @@ -150,7 +150,7 @@ class SmallFilledCircles(Circles): filled = True def __init__(self, hatch, density): - self.num_rows = (hatch.count('.')) * density + self.num_rows = int((hatch.count('.')) * density) super().__init__(hatch, density) @@ -159,7 +159,7 @@ class Stars(Shapes): filled = True def __init__(self, hatch, density): - self.num_rows = (hatch.count('*')) * density + self.num_rows = int((hatch.count('*')) * density) path = Path.unit_regular_star(5) self.shape_vertices = path.vertices self.shape_codes = np.full(len(self.shape_vertices), Path.LINETO, @@ -196,13 +196,18 @@ def _validate_hatch_pattern(hatch): ) -def get_path(hatchpattern, density=6): +def get_path(hatchpattern, density=6.0): """ Given a hatch specifier, *hatchpattern*, generates Path to render the hatch in a unit square. *density* is the number of lines per unit square. """ - density = int(density) + if int(density) != density: + _api.warn_external("Passing a floating point density will result in " + "a behavior change due to float to int conversion." + f"Value density ({density}) will be used " + f"instead of {int(density)} to calculate lines", + category=FutureWarning) patterns = [hatch_type(hatchpattern, density) for hatch_type in _hatch_types] diff --git a/lib/matplotlib/hatch.pyi b/lib/matplotlib/hatch.pyi index 348cf5214984..63b5ed2f41c4 100644 --- a/lib/matplotlib/hatch.pyi +++ b/lib/matplotlib/hatch.pyi @@ -8,54 +8,54 @@ class HatchPatternBase: ... class HorizontalHatch(HatchPatternBase): num_lines: int num_vertices: int - def __init__(self, hatch: str, density: int) -> None: ... + def __init__(self, hatch: str, density: float) -> None: ... def set_vertices_and_codes(self, vertices: ArrayLike, codes: ArrayLike) -> None: ... class VerticalHatch(HatchPatternBase): num_lines: int num_vertices: int - def __init__(self, hatch: str, density: int) -> None: ... + def __init__(self, hatch: str, density: float) -> None: ... def set_vertices_and_codes(self, vertices: ArrayLike, codes: ArrayLike) -> None: ... class NorthEastHatch(HatchPatternBase): num_lines: int num_vertices: int - def __init__(self, hatch: str, density: int) -> None: ... + def __init__(self, hatch: str, density: float) -> None: ... def set_vertices_and_codes(self, vertices: ArrayLike, codes: ArrayLike) -> None: ... class SouthEastHatch(HatchPatternBase): num_lines: int num_vertices: int - def __init__(self, hatch: str, density: int) -> None: ... + def __init__(self, hatch: str, density: float) -> None: ... def set_vertices_and_codes(self, vertices: ArrayLike, codes: ArrayLike) -> None: ... class Shapes(HatchPatternBase): filled: bool num_shapes: int num_vertices: int - def __init__(self, hatch: str, density: int) -> None: ... + def __init__(self, hatch: str, density: float) -> None: ... def set_vertices_and_codes(self, vertices: ArrayLike, codes: ArrayLike) -> None: ... class Circles(Shapes): shape_vertices: np.ndarray shape_codes: np.ndarray - def __init__(self, hatch: str, density: int) -> None: ... + def __init__(self, hatch: str, density: float) -> None: ... class SmallCircles(Circles): size: float num_rows: int - def __init__(self, hatch: str, density: int) -> None: ... + def __init__(self, hatch: str, density: float) -> None: ... class LargeCircles(Circles): size: float num_rows: int - def __init__(self, hatch: str, density: int) -> None: ... + def __init__(self, hatch: str, density: float) -> None: ... class SmallFilledCircles(Circles): size: float filled: bool num_rows: int - def __init__(self, hatch: str, density: int) -> None: ... + def __init__(self, hatch: str, density: float) -> None: ... class Stars(Shapes): size: float @@ -63,6 +63,6 @@ class Stars(Shapes): num_rows: int shape_vertices: np.ndarray shape_codes: np.ndarray - def __init__(self, hatch: str, density: int) -> None: ... + def __init__(self, hatch: str, density: float) -> None: ... -def get_path(hatchpattern: str, density: int = ...) -> Path: ... +def get_path(hatchpattern: str, density: float = ...) -> Path: ... diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py index e72eb1a9ca73..b7faec395124 100644 --- a/lib/matplotlib/path.py +++ b/lib/matplotlib/path.py @@ -1024,7 +1024,7 @@ def wedge(cls, theta1, theta2, n=None): @staticmethod @lru_cache(8) - def hatch(hatchpattern, density=6): + def hatch(hatchpattern, density=6.0): """ Given a hatch specifier, *hatchpattern*, generates a `Path` that can be used in a repeated hatching pattern. *density* is the diff --git a/lib/matplotlib/tests/test_path.py b/lib/matplotlib/tests/test_path.py index 8c0c32dc133b..2cdc78c77477 100644 --- a/lib/matplotlib/tests/test_path.py +++ b/lib/matplotlib/tests/test_path.py @@ -539,3 +539,13 @@ def test_cleanup_closepoly(): cleaned = p.cleaned(remove_nans=True) assert len(cleaned) == 1 assert cleaned.codes[0] == Path.STOP + + +def test_hatch_density_integer_to_float(): + with pytest.warns(FutureWarning, + match="behavior change due to float to int conversion."): + Path.hatch("x", 6.6) + + +def test_hatch_density_integer(): + Path.hatch("x", 2.0)