From 2de394eb4485593710af8ed0fd079be078798db5 Mon Sep 17 00:00:00 2001 From: Andrew Coffey Date: Sun, 12 Oct 2025 09:37:34 -0500 Subject: [PATCH 1/3] Added do_clamp to vector lerping --- buildconfig/stubs/pygame/math.pyi | 6 +++++- docs/reST/ref/math.rst | 6 ++++++ src_c/doc/math_doc.h | 2 +- src_c/math.c | 9 ++++++--- test/math_test.py | 8 ++++++++ 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/buildconfig/stubs/pygame/math.pyi b/buildconfig/stubs/pygame/math.pyi index 98b84ec340..9afaf6f2d8 100644 --- a/buildconfig/stubs/pygame/math.pyi +++ b/buildconfig/stubs/pygame/math.pyi @@ -82,7 +82,11 @@ class _GenericVector(Collection[float]): self: _TVec, other: Union[SequenceLike[float], _TVec], / ) -> float: ... def lerp( - self: _TVec, other: Union[SequenceLike[float], _TVec], value: float, / + self: _TVec, + other: Union[SequenceLike[float], _TVec], + value: float, + do_clamp: bool = True, + /, ) -> _TVec: ... def slerp( self: _TVec, other: Union[SequenceLike[float], _TVec], value: float, / diff --git a/docs/reST/ref/math.rst b/docs/reST/ref/math.rst index 0fd3fffdab..7751e80aa0 100644 --- a/docs/reST/ref/math.rst +++ b/docs/reST/ref/math.rst @@ -386,12 +386,18 @@ Conversion can be combined with swizzling or slicing to create a new order | :sl:`returns a linear interpolation to the given vector.` | :sg:`lerp(Vector2, float, /) -> Vector2` + | :sg:`lerp(Vector2, float, bool, /) -> Vector2` Returns a Vector which is a linear interpolation between self and the given Vector. The second parameter determines how far between self and other the result is going to be. It must be a value between ``0`` and ``1`` where ``0`` means self and ``1`` means other will be returned. + .. versionchanged:: 2.5.7 The ``do_clamp`` parameter is added with a default + value of ``True``. When it's ``True``, the second parameter is limited + to the closed interval ``[0, 1]``. When it's ``False``, the second parameter + is not limited and the lerp will extend beyond the original two vectors. + .. ## Vector2.lerp ## .. method:: slerp diff --git a/src_c/doc/math_doc.h b/src_c/doc/math_doc.h index 548465487d..952cf7d640 100644 --- a/src_c/doc/math_doc.h +++ b/src_c/doc/math_doc.h @@ -22,7 +22,7 @@ #define DOC_MATH_VECTOR2_DISTANCESQUAREDTO "distance_squared_to(Vector2, /) -> float\ncalculates the squared Euclidean distance to a given vector." #define DOC_MATH_VECTOR2_MOVETOWARDS "move_towards(Vector2, float, /) -> Vector2\nreturns a vector moved toward the target by a given distance." #define DOC_MATH_VECTOR2_MOVETOWARDSIP "move_towards_ip(Vector2, float, /) -> None\nmoves the vector toward its target at a given distance." -#define DOC_MATH_VECTOR2_LERP "lerp(Vector2, float, /) -> Vector2\nreturns a linear interpolation to the given vector." +#define DOC_MATH_VECTOR2_LERP "lerp(Vector2, float, /) -> Vector2\nlerp(Vector2, float, bool, /) -> Vector2\nreturns a linear interpolation to the given vector." #define DOC_MATH_VECTOR2_SLERP "slerp(Vector2, float, /) -> Vector2\nreturns a spherical interpolation to the given vector." #define DOC_MATH_VECTOR2_SMOOTHSTEP "smoothstep(Vector2, float, /) -> Vector2\nreturns a smooth interpolation to the given vector." #define DOC_MATH_VECTOR2_ELEMENTWISE "elementwise() -> VectorElementwiseProxy\nThe next operation will be performed elementwise." diff --git a/src_c/math.c b/src_c/math.c index 540fcd0129..17453410ef 100644 --- a/src_c/math.c +++ b/src_c/math.c @@ -1708,16 +1708,19 @@ vector_lerp(pgVector *self, PyObject *args) pgVector *ret; double t; double other_coords[VECTOR_MAX_SIZE]; + int do_clamp = 1; - if (!PyArg_ParseTuple(args, "Od:Vector.lerp", &other, &t)) { + if (!PyArg_ParseTuple(args, "Od|p:Vector.lerp", &other, &t, &do_clamp)) { return NULL; } if (!pg_VectorCoordsFromObjOldDontUseInNewCode(other, other_coords, self->dim)) { return RAISE(PyExc_TypeError, "Expected Vector as argument 1"); } - if (t < 0 || t > 1) { - return RAISE(PyExc_ValueError, "Argument 2 must be in range [0, 1]"); + if ((t < 0 || t > 1) && do_clamp > 0) { + return RAISE(PyExc_ValueError, + "Argument 2 must be in range [0, 1] when do_clamp is set " + "to True (the default value)"); } ret = _vector_subtype_new(self); diff --git a/test/math_test.py b/test/math_test.py index b7b91f73c8..7d87217b56 100644 --- a/test/math_test.py +++ b/test/math_test.py @@ -1139,14 +1139,18 @@ def test_lerp(self): v2 = Vector2(10, 10) self.assertEqual(v1.lerp(v2, 0.5), (5, 5)) self.assertRaises(ValueError, lambda: v1.lerp(v2, 2.5)) + self.assertEqual(v1.lerp(v2, 2.5, False), (25, 25)) + self.assertEqual(v1.lerp(v2, -1, False), (-10, -10)) v1 = Vector2(0, 0) v2 = Vector2(10, 10) self.assertEqual(v1.lerp(v2, 0.1), (1, 1)) + self.assertEqual(v1.lerp(v2, 10, False), (100, 100)) v1 = Vector2(-10, -5) v2 = Vector2(10, 10) self.assertEqual(v1.lerp(v2, 0.5), (0, 2.5)) + self.assertEqual(v1.lerp(v2, -10, False), (-210, -155)) def test_smoothstep(self): v1 = Vector2(0, 0) @@ -2892,14 +2896,18 @@ def test_lerp(self): v2 = Vector3(10, 10, 10) self.assertEqual(v1.lerp(v2, 0.5), (5, 5, 5)) self.assertRaises(ValueError, lambda: v1.lerp(v2, 2.5)) + self.assertEqual(v1.lerp(v2, 2.5, False), (25, 25, 25)) + self.assertEqual(v1.lerp(v2, -1, False), (-10, -10, -10)) v1 = Vector3(0, 0, 0) v2 = Vector3(10, 10, 10) self.assertEqual(v1.lerp(v2, 0.1), (1, 1, 1)) + self.assertEqual(v1.lerp(v2, 10, False), (100, 100, 100)) v1 = Vector3(-10, -5, -20) v2 = Vector3(10, 10, -20) self.assertEqual(v1.lerp(v2, 0.5), (0, 2.5, -20)) + self.assertEqual(v1.lerp(v2, -10, False), (-210, -155, -20)) def test_smoothstep(self): v1 = Vector3(0, 0, 0) From 631cab3e052161d8a5086cefb1f96f9e7af057d9 Mon Sep 17 00:00:00 2001 From: Andrew Coffey Date: Sun, 12 Oct 2025 09:52:24 -0500 Subject: [PATCH 2/3] Added versionchanged note to Vector3.lerp, oops --- docs/reST/ref/math.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/reST/ref/math.rst b/docs/reST/ref/math.rst index 7751e80aa0..585e6b7e37 100644 --- a/docs/reST/ref/math.rst +++ b/docs/reST/ref/math.rst @@ -881,12 +881,18 @@ Conversion can be combined with swizzling or slicing to create a new order | :sl:`returns a linear interpolation to the given vector.` | :sg:`lerp(Vector3, float, /) -> Vector3` + | :sg:`lerp(Vector3, float, bool, /)` Returns a Vector which is a linear interpolation between self and the given Vector. The second parameter determines how far between self an other the result is going to be. It must be a value between ``0`` and ``1``, where ``0`` means self and ``1`` means other will be returned. + .. versionchanged:: 2.5.7 The ``do_clamp`` parameter is added with a default + value of ``True``. When it's ``True``, the second parameter is limited + to the closed interval ``[0, 1]``. When it's ``False``, the second parameter + is not limited and the lerp will extend beyond the original two vectors. + .. ## Vector3.lerp ## .. method:: slerp From 73ed3dc736f3bc38754c8f825c86d76f35888553 Mon Sep 17 00:00:00 2001 From: Andrew Coffey Date: Sun, 12 Oct 2025 10:01:50 -0500 Subject: [PATCH 3/3] Updated docs to fix stubs and clarify docs a bit --- docs/reST/ref/math.rst | 18 ++++++++++-------- src_c/doc/math_doc.h | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/reST/ref/math.rst b/docs/reST/ref/math.rst index 585e6b7e37..2b29e725ce 100644 --- a/docs/reST/ref/math.rst +++ b/docs/reST/ref/math.rst @@ -385,13 +385,14 @@ Conversion can be combined with swizzling or slicing to create a new order .. method:: lerp | :sl:`returns a linear interpolation to the given vector.` - | :sg:`lerp(Vector2, float, /) -> Vector2` - | :sg:`lerp(Vector2, float, bool, /) -> Vector2` + | :sg:`lerp(Vector2, float, do_clamp=True, /) -> Vector2` Returns a Vector which is a linear interpolation between self and the given Vector. The second parameter determines how far between self and other the result is going to be. It must be a value between ``0`` and ``1`` - where ``0`` means self and ``1`` means other will be returned. + when ``do_clamp`` is ``True`` where ``0`` means self and ``1`` means other + will be returned. When ``do_clamp`` is ``False``, the lerp will extend out + linearly when the second parameter is outside of the nominal range. .. versionchanged:: 2.5.7 The ``do_clamp`` parameter is added with a default value of ``True``. When it's ``True``, the second parameter is limited @@ -880,13 +881,14 @@ Conversion can be combined with swizzling or slicing to create a new order .. method:: lerp | :sl:`returns a linear interpolation to the given vector.` - | :sg:`lerp(Vector3, float, /) -> Vector3` - | :sg:`lerp(Vector3, float, bool, /)` + | :sg:`lerp(Vector3, float, do_clamp=True, /) -> Vector3` Returns a Vector which is a linear interpolation between self and the - given Vector. The second parameter determines how far between self an - other the result is going to be. It must be a value between ``0`` and - ``1``, where ``0`` means self and ``1`` means other will be returned. + given Vector. The second parameter determines how far between self and + other the result is going to be. It must be a value between ``0`` and ``1`` + when ``do_clamp`` is ``True`` where ``0`` means self and ``1`` means other + will be returned. When ``do_clamp`` is ``False``, the lerp will extend out + linearly when the second parameter is outside of the nominal range. .. versionchanged:: 2.5.7 The ``do_clamp`` parameter is added with a default value of ``True``. When it's ``True``, the second parameter is limited diff --git a/src_c/doc/math_doc.h b/src_c/doc/math_doc.h index 952cf7d640..9a5d102a1e 100644 --- a/src_c/doc/math_doc.h +++ b/src_c/doc/math_doc.h @@ -22,7 +22,7 @@ #define DOC_MATH_VECTOR2_DISTANCESQUAREDTO "distance_squared_to(Vector2, /) -> float\ncalculates the squared Euclidean distance to a given vector." #define DOC_MATH_VECTOR2_MOVETOWARDS "move_towards(Vector2, float, /) -> Vector2\nreturns a vector moved toward the target by a given distance." #define DOC_MATH_VECTOR2_MOVETOWARDSIP "move_towards_ip(Vector2, float, /) -> None\nmoves the vector toward its target at a given distance." -#define DOC_MATH_VECTOR2_LERP "lerp(Vector2, float, /) -> Vector2\nlerp(Vector2, float, bool, /) -> Vector2\nreturns a linear interpolation to the given vector." +#define DOC_MATH_VECTOR2_LERP "lerp(Vector2, float, do_clamp=True, /) -> Vector2\nreturns a linear interpolation to the given vector." #define DOC_MATH_VECTOR2_SLERP "slerp(Vector2, float, /) -> Vector2\nreturns a spherical interpolation to the given vector." #define DOC_MATH_VECTOR2_SMOOTHSTEP "smoothstep(Vector2, float, /) -> Vector2\nreturns a smooth interpolation to the given vector." #define DOC_MATH_VECTOR2_ELEMENTWISE "elementwise() -> VectorElementwiseProxy\nThe next operation will be performed elementwise." @@ -59,7 +59,7 @@ #define DOC_MATH_VECTOR3_DISTANCESQUAREDTO "distance_squared_to(Vector3, /) -> float\ncalculates the squared Euclidean distance to a given vector." #define DOC_MATH_VECTOR3_MOVETOWARDS "move_towards(Vector3, float, /) -> Vector3\nreturns a vector moved toward the target by a given distance." #define DOC_MATH_VECTOR3_MOVETOWARDSIP "move_towards_ip(Vector3, float, /) -> None\nmoves the vector toward its target at a given distance." -#define DOC_MATH_VECTOR3_LERP "lerp(Vector3, float, /) -> Vector3\nreturns a linear interpolation to the given vector." +#define DOC_MATH_VECTOR3_LERP "lerp(Vector3, float, do_clamp=True, /) -> Vector3\nreturns a linear interpolation to the given vector." #define DOC_MATH_VECTOR3_SLERP "slerp(Vector3, float, /) -> Vector3\nreturns a spherical interpolation to the given vector." #define DOC_MATH_VECTOR3_SMOOTHSTEP "smoothstep(Vector3, float, /) -> Vector3\nreturns a smooth interpolation to the given vector." #define DOC_MATH_VECTOR3_ELEMENTWISE "elementwise() -> VectorElementwiseProxy\nThe next operation will be performed elementwise."