diff --git a/src/roboticstoolbox/mobile/DistanceTransformPlanner.py b/src/roboticstoolbox/mobile/DistanceTransformPlanner.py index 951965d7..344ea5e7 100644 --- a/src/roboticstoolbox/mobile/DistanceTransformPlanner.py +++ b/src/roboticstoolbox/mobile/DistanceTransformPlanner.py @@ -9,7 +9,7 @@ from spatialmath import base from scipy.ndimage import * import matplotlib.pyplot as plt -from matplotlib import cm +from matplotlib import colormaps from roboticstoolbox.mobile.PlannerBase import PlannerBase @@ -324,9 +324,7 @@ def distancexform(occgrid, goal, metric="cityblock", animate=False, summary=Fals plt.ylabel("y") ax = plt.gca() plt.pause(0.001) - cmap = cm.get_cmap("gray") - cmap.set_bad("red") - cmap.set_over("white") + cmap = colormaps.get_cmap("gray").with_extremes(bad="red", over="white") h = plt.imshow(display, cmap=cmap) plt.colorbar(label="distance") else: diff --git a/src/roboticstoolbox/robot/RobotPlottingMPL.py b/src/roboticstoolbox/robot/RobotPlottingMPL.py index 201f066f..f4c353ca 100644 --- a/src/roboticstoolbox/robot/RobotPlottingMPL.py +++ b/src/roboticstoolbox/robot/RobotPlottingMPL.py @@ -62,12 +62,12 @@ def linkcolormap(self, linkcolors: list[Any] | str = "viridis"): """ - from matplotlib import cm, colors + from matplotlib import colormaps, colors - if isinstance(linkcolors, list) and len(linkcolors) == self.n: # type: ignore[attr-defined] # pragma: nocover + if isinstance(linkcolors, list) and len(linkcolors) == self.n: # type: ignore[attr-defined] return colors.ListedColormap(linkcolors) - else: # pragma: nocover - return cm.get_cmap(linkcolors, 6) # type: ignore[arg-type] + else: + return colormaps.get_cmap(linkcolors).resampled(6) # ------------------------------------------------------------------ # Ellipse creation diff --git a/tests/test_backend_capabilities.py b/tests/test_backend_capabilities.py index 9bfb7d76..e71de0e9 100644 --- a/tests/test_backend_capabilities.py +++ b/tests/test_backend_capabilities.py @@ -112,6 +112,20 @@ def test_robot_has_linkcolormap(self): robot = self._make_robot() self.assertTrue(hasattr(robot, "linkcolormap")) + def test_linkcolormap_from_name(self): + # Regression test: linkcolormap() used matplotlib.cm.get_cmap(), + # removed in matplotlib 3.9, so any named colormap raised + # AttributeError instead of returning a 6-entry colormap. + robot = self._make_robot() + cmap = robot.linkcolormap("inferno") + self.assertEqual(cmap.N, 6) + + def test_linkcolormap_from_list(self): + robot = self._make_robot() + colors = ["red", "g", (0, 0.5, 0), "#0f8040", "yellow", "cyan"] + cmap = robot.linkcolormap(colors) + self.assertEqual(cmap.N, len(colors)) + def test_mixin_present_in_mro(self): import roboticstoolbox as rtb from roboticstoolbox.robot.RobotPlottingMPL import RobotPlottingMPLMixin diff --git a/tests/test_distance_transform_plot.py b/tests/test_distance_transform_plot.py index 2c2780b7..d6b75a01 100644 --- a/tests/test_distance_transform_plot.py +++ b/tests/test_distance_transform_plot.py @@ -25,3 +25,17 @@ def test_distance_transform_next_before_plan_raises(): with pytest.raises(ValueError, match="No distance map computed"): planner.next((0, 0)) + + +def test_distancexform_animate(): + # Regression test: the animate path used matplotlib.cm.get_cmap(), + # removed in matplotlib 3.9, so plan(animate=True) raised + # AttributeError before computing the distance map. + floorplan = np.zeros((10, 10), dtype=int) + floorplan[4:7, 4:7] = 1 + + planner = rtb.DistanceTransformPlanner(floorplan, inflate=1) + planner.plan((8, 8), animate=True) + + assert planner.distancemap is not None + plt.close("all")