From 455627f3f40a22a7da3fc3a48a3c0fd8b787e23d Mon Sep 17 00:00:00 2001 From: Ching-Wei Kang Date: Thu, 5 Nov 2026 20:38:09 -0600 Subject: [PATCH] fix(spline): validate interpolation time span Validate the endpoint span instead of the absolute final time so negative and offset time ranges are accepted. Report equal endpoints clearly before normalization and cover both normalized and unnormalized regressions. Signed-off-by: Ching-Wei Kang --- spatialmath/spline.py | 4 ++-- tests/test_spline.py | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/spatialmath/spline.py b/spatialmath/spline.py index c0a99cad..cd244d2e 100644 --- a/spatialmath/spline.py +++ b/spatialmath/spline.py @@ -105,9 +105,9 @@ def __init__( self.control_poses = control_poses self.timepoints = np.array(timepoints) - if self.timepoints[-1] < self._e: + if self.timepoints[-1] - self.timepoints[0] < self._e: raise ValueError( - "Difference between start and end timepoints is less than {self._e}" + f"Difference between start and end timepoints is less than {self._e}" ) if len(self.control_poses) != len(self.timepoints): diff --git a/tests/test_spline.py b/tests/test_spline.py index e1fb56c9..f584093c 100644 --- a/tests/test_spline.py +++ b/tests/test_spline.py @@ -73,6 +73,29 @@ def test_small_delta_t(self): np.linspace(0, InterpSplineSE3._e, len(self.waypoints)), self.waypoints ) + @pytest.mark.parametrize( + "start, end", + [ + (-5, -1), + (10, 20), + ], + ) + @pytest.mark.parametrize("normalize_time", [False, True]) + def test_nonzero_offset_time_range(self, start, end, normalize_time): + InterpSplineSE3( + np.linspace(start, end, len(self.waypoints)), + self.waypoints, + normalize_time=normalize_time, + ) + + def test_zero_time_span(self): + with pytest.raises(ValueError, match=str(InterpSplineSE3._e)): + InterpSplineSE3( + np.full(len(self.waypoints), -1.0), + self.waypoints, + normalize_time=True, + ) + @pytest.mark.skipif( plt.get_backend().lower() == "agg" or os.environ.get("CI") == "true", reason="animate=True busy-waits on a timer callback that never "