diff --git a/buildconfig/stubs/pygame/geometry.pyi b/buildconfig/stubs/pygame/geometry.pyi index 184eec2a99..5c4cb62387 100644 --- a/buildconfig/stubs/pygame/geometry.pyi +++ b/buildconfig/stubs/pygame/geometry.pyi @@ -75,5 +75,11 @@ class Circle: def collidecircle(self, x: float, y: float, r: float) -> bool: ... @overload def collidecircle(self, center: Coordinate, r: float) -> bool: ... + @overload + def update(self, circle: _CircleValue) -> None: ... + @overload + def update(self, x: float, y: float, r: float) -> None: ... + @overload + def update(self, center: Coordinate, r: float) -> None: ... def __copy__(self) -> Circle: ... copy = __copy__ diff --git a/docs/reST/ref/geometry.rst b/docs/reST/ref/geometry.rst index a7e58fdf5d..bb6e778b25 100644 --- a/docs/reST/ref/geometry.rst +++ b/docs/reST/ref/geometry.rst @@ -176,6 +176,25 @@ .. ## Circle.collidecircle ## + .. method:: update + + | :sl:`updates the circle position and radius` + | :sg:`update((x, y), radius) -> None` + | :sg:`update(x, y, radius) -> None` + + The `update` method allows you to set the position and radius of a `Circle` object in + place. This method takes either a tuple of (x, y) coordinates, two separate x and + y coordinates, and a radius as its arguments, and it always returns `None`. + + .. note:: + This method is equivalent(behaviour wise) to the following code: + :: + circle.x = x + circle.y = y + circle.r = radius + + .. ## Circle.update ## + .. method:: copy | :sl:`returns a copy of the circle` diff --git a/src_c/circle.c b/src_c/circle.c index f6fdd5ceeb..745bd37df1 100644 --- a/src_c/circle.c +++ b/src_c/circle.c @@ -261,6 +261,18 @@ pg_circle_str(pgCircleObject *self) return pg_circle_repr(self); } +static PyObject * +pg_circle_update(pgCircleObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + if (!pgCircle_FromObjectFastcall(args, nargs, &self->circle)) { + PyErr_SetString( + PyExc_TypeError, + "Circle.update requires a circle or CircleLike object"); + return NULL; + } + Py_RETURN_NONE; +} + static PyObject * pg_circle_collidepoint(pgCircleObject *self, PyObject *const *args, Py_ssize_t nargs) @@ -295,6 +307,8 @@ static struct PyMethodDef pg_circle_methods[] = { DOC_CIRCLE_COLLIDEPOINT}, {"collidecircle", (PyCFunction)pg_circle_collidecircle, METH_FASTCALL, DOC_CIRCLE_COLLIDECIRCLE}, + {"update", (PyCFunction)pg_circle_update, METH_FASTCALL, + DOC_CIRCLE_UPDATE}, {"__copy__", (PyCFunction)pg_circle_copy, METH_NOARGS, DOC_CIRCLE_COPY}, {"copy", (PyCFunction)pg_circle_copy, METH_NOARGS, DOC_CIRCLE_COPY}, {NULL, NULL, 0, NULL}}; diff --git a/src_c/doc/geometry_doc.h b/src_c/doc/geometry_doc.h index d747dddd88..b98a6b23d7 100644 --- a/src_c/doc/geometry_doc.h +++ b/src_c/doc/geometry_doc.h @@ -11,4 +11,5 @@ #define DOC_CIRCLE_CIRCUMFERENCE "circumference -> float\ncircumference of the circle" #define DOC_CIRCLE_COLLIDEPOINT "collidepoint((x, y)) -> bool\ncollidepoint(x, y) -> bool\ncollidepoint(Vector2) -> bool\ntest if a point is inside the circle" #define DOC_CIRCLE_COLLIDECIRCLE "collidecircle(Circle) -> bool\ncollidecircle(x, y, radius) -> bool\ncollidecircle((x, y), radius) -> bool\ntest if two circles collide" +#define DOC_CIRCLE_UPDATE "update((x, y), radius) -> None\nupdate(x, y, radius) -> None\nupdates the circle position and radius" #define DOC_CIRCLE_COPY "copy() -> Circle\nreturns a copy of the circle" diff --git a/test/geometry_test.py b/test/geometry_test.py index aed5ebba5e..d3f5f3ee01 100644 --- a/test/geometry_test.py +++ b/test/geometry_test.py @@ -549,6 +549,86 @@ def test_collidecircle(self): c5.collidecircle(c), "Expected False, circles should collide here" ) + def test_update(self): + """Ensures that updating the circle position + and dimension correctly updates position and dimension""" + c = Circle(0, 0, 10) + + c.update(5, 5, 3) + + self.assertEqual(c.x, 5.0) + self.assertEqual(c.y, 5.0) + self.assertEqual(c.r, 3.0) + self.assertEqual(c.r_sqr, 9.0) + + def test_update_argtype(self): + """tests if the function correctly handles incorrect types as parameters""" + invalid_types = (None, [], "1", (1,), Vector2(1, 1), 1, 0.2324) + + c = Circle(10, 10, 4) + + for value in invalid_types: + with self.assertRaises(TypeError): + c.update(value) + + def test_update_argnum(self): + c = Circle(10, 10, 4) + + # no params + with self.assertRaises(TypeError): + c.update() + + # too many params + with self.assertRaises(TypeError): + c.update(1, 1, 1, 1) + + def test_update_twice(self): + """Ensures that updating the circle position + and dimension correctly updates position and dimension""" + c = Circle(0, 0, 10) + + c.update(5, 5, 3) + c.update(0, 0, 10) + + self.assertEqual(c.x, 0.0) + self.assertEqual(c.y, 0.0) + self.assertEqual(c.r, 10) + self.assertEqual(c.r_sqr, 100) + + def test_update_inplace(self): + """Ensures that updating the circle to its position doesn't + move the circle to another position""" + c = Circle(0, 0, 10) + centerx = c.x + centery = c.y + c_r = c.r + c_r_sqr = c.r_sqr + + c.update(0, 0, 10) + + self.assertEqual(c.x, centerx) + self.assertEqual(c.y, centery) + self.assertEqual(c.r, c_r) + self.assertEqual(c.r_sqr, c_r_sqr) + + c.update(c) + + def test_selfupdate(self): + """Ensures that updating the circle to its position doesn't + move the circle to another position""" + c = Circle(0, 0, 10) + centerx = c.x + centery = c.y + c_r = c.r + c_r_sqr = c.r_sqr + + c.update(c) + + self.assertEqual(c.x, centerx) + self.assertEqual(c.y, centery) + self.assertEqual(c.r, c_r) + self.assertEqual(c.r_sqr, c_r_sqr) + if __name__ == "__main__": unittest.main()