From f072a374c830af06d431a9b8ddafacea0f2e3f64 Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Sat, 22 Jul 2023 15:31:33 +0200 Subject: [PATCH 1/4] added circle collidelist/collidelistall --- docs/circle.rst | 46 ++++++++++++ docs/geometry.rst | 4 ++ geometry.pyi | 2 + src_c/circle.c | 169 ++++++++++++++++++++++++++++++++++++++++---- test/test_circle.py | 110 ++++++++++++++++++++++++++++ 5 files changed, 316 insertions(+), 15 deletions(-) diff --git a/docs/circle.rst b/docs/circle.rst index ab561983..1ef173e9 100644 --- a/docs/circle.rst +++ b/docs/circle.rst @@ -298,6 +298,52 @@ Circle Methods .. ## Circle.collideswith ## + .. method:: collidelist + + | :sl:`test if a list of objects collide with the circle` + | :sg:`collidelist(colliders) -> int` + + The `collidelist` method tests whether a given list of shapes or points collides + (overlaps) with this `Circle` object. The function takes in a single argument, which + must be a list of `Line`, `Circle`, `Rect`, `Polygon`, tuple or list containing the + x and y coordinates of a point, or `Vector2` objects. The function returns the index + of the first shape or point in the list that collides with the `Circle` object, or + -1 if there is no collision. + + .. note:: + It is important to note that the shapes must be actual shape objects, such as + `Line`, `Circle`, `Polygon`, or `Rect` instances. It is not possible to pass a tuple + or list of coordinates representing the shape as an argument(except for a point), + because the type of shape represented by the coordinates cannot be determined. + For example, a tuple with the format (a, b, c, d) could represent either a `Line` + or a `Rect` object, and there is no way to determine which is which without + explicitly passing a `Line` or `Rect` object as an argument. + + .. ## Circle.collidelist ## + + .. method:: collidelistall + + | :sl:`test if all objects in a list collide with the circle` + | :sg:`collidelistall(colliders) -> list` + + The `collidelistall` method tests whether a given list of shapes or points collides + (overlaps) with this `Circle` object. The function takes in a single argument, which + must be a list of `Line`, `Circle`, `Rect`, `Polygon`, tuple or list containing the + x and y coordinates of a point, or `Vector2` objects. The function returns a list + containing the indices of all the shapes or points in the list that collide with + the `Circle` object, or an empty list if there is no collision. + + .. note:: + It is important to note that the shapes must be actual shape objects, such as + `Line`, `Circle`, `Polygon`, or `Rect` instances. It is not possible to pass a tuple + or list of coordinates representing the shape as an argument(except for a point), + because the type of shape represented by the coordinates cannot be determined. + For example, a tuple with the format (a, b, c, d) could represent either a `Line` + or a `Rect` object, and there is no way to determine which is which without + explicitly passing a `Line` or `Rect` object as an argument. + + .. ## Circle.collidelistall ## + .. method:: contains | :sl:`test if a shape or point is inside the circle` diff --git a/docs/geometry.rst b/docs/geometry.rst index 190fad51..4f061284 100644 --- a/docs/geometry.rst +++ b/docs/geometry.rst @@ -58,6 +58,10 @@ performing transformations and checking for collisions with other objects. collideswith: Checks if the circle collides with the given object. + collidelist: Checks if the circle collides with any of the given objects. + + collidelistall: Checks if the circle collides with all of the given objects. + contains: Checks if the circle fully contains the given object. rotate: Rotates the circle by the given amount. diff --git a/geometry.pyi b/geometry.pyi index abfbf029..fdd9fa48 100644 --- a/geometry.pyi +++ b/geometry.pyi @@ -176,6 +176,8 @@ class Circle: @overload def colliderect(self, x: int, y: int, w: int, h: int) -> bool: ... def collideswith(self, other: _CanBeCollided) -> bool: ... + def collidelist(self, colliders: Sequence[_CanBeCollided]) -> int: ... + def collidelistall(self, colliders: Sequence[_CanBeCollided]) -> List[int]: ... def __copy__(self) -> Circle: ... copy = __copy__ diff --git a/src_c/circle.c b/src_c/circle.c index 7b93772d..a31bfe7b 100644 --- a/src_c/circle.c +++ b/src_c/circle.c @@ -320,37 +320,44 @@ pg_circle_colliderect(pgCircleObject *self, PyObject *const *args, return PyBool_FromLong(pgCollision_RectCircle(&temp, &self->circle)); } -static PyObject * -pg_circle_collideswith(pgCircleObject *self, PyObject *arg) +static int +_pg_circle_collideswith(pgCircleBase *scirc, PyObject *arg) { - int result = 0; - pgCircleBase *scirc = &self->circle; if (pgCircle_Check(arg)) { - result = pgCollision_CircleCircle(&pgCircle_AsCircle(arg), scirc); + return pgCollision_CircleCircle(&pgCircle_AsCircle(arg), scirc); } else if (pgRect_Check(arg)) { - result = pgCollision_RectCircle(&pgRect_AsRect(arg), scirc); + return pgCollision_RectCircle(&pgRect_AsRect(arg), scirc); } else if (pgLine_Check(arg)) { - result = pgCollision_LineCircle(&pgLine_AsLine(arg), scirc); + return pgCollision_LineCircle(&pgLine_AsLine(arg), scirc); } else if (pgPolygon_Check(arg)) { - result = - pgCollision_CirclePolygon(scirc, &pgPolygon_AsPolygon(arg), 0); + return pgCollision_CirclePolygon(scirc, &pgPolygon_AsPolygon(arg), 0); } else if (PySequence_Check(arg)) { double x, y; if (!pg_TwoDoublesFromObj(arg, &x, &y)) { - return RAISE( + PyErr_SetString( PyExc_TypeError, "Invalid point argument, must be a sequence of 2 numbers"); + return -1; } - result = pgCollision_CirclePoint(scirc, x, y); + return pgCollision_CirclePoint(scirc, x, y); } - else { - return RAISE(PyExc_TypeError, - "Invalid shape argument, must be a CircleType, RectType, " - "LineType, PolygonType or a sequence of 2 numbers"); + + PyErr_SetString(PyExc_TypeError, + "Invalid shape argument, must be a CircleType, RectType, " + "LineType, PolygonType or a sequence of 2 numbers"); + return -1; +} + +static PyObject * +pg_circle_collideswith(pgCircleObject *self, PyObject *arg) +{ + int result = _pg_circle_collideswith(&self->circle, arg); + if (result == -1) { + return NULL; } return PyBool_FromLong(result); @@ -591,6 +598,136 @@ pg_circle_rotate_ip(pgCircleObject *self, PyObject *const *args, Py_RETURN_NONE; } +static PyObject * +pg_circle_collidelist(pgCircleObject *self, PyObject *arg) +{ + Py_ssize_t i; + pgCircleBase *scirc = &self->circle; + int colliding; + + if (!PySequence_Check(arg)) { + return RAISE(PyExc_TypeError, "Argument must be a sequence"); + } + + /* fast path */ + if (PySequence_FAST_CHECK(arg)) { + PyObject **items = PySequence_Fast_ITEMS(arg); + for (i = 0; i < PySequence_Fast_GET_SIZE(arg); i++) { + colliding = _pg_circle_collideswith(scirc, items[i]); + if (colliding == 1) { + return PyLong_FromSsize_t(i); + } + else if (colliding == -1) { + return NULL; + } + } + return PyLong_FromLong(-1); + } + + /* general sequence path */ + for (i = 0; i < PySequence_Length(arg); i++) { + PyObject *obj = PySequence_GetItem(arg, i); + if (!obj) { + return NULL; + } + + colliding = _pg_circle_collideswith(scirc, obj); + Py_DECREF(obj); + + if (colliding == 1) { + return PyLong_FromSsize_t(i); + } + else if (colliding == -1) { + return NULL; + } + } + + return PyLong_FromLong(-1); +} + +static PyObject * +pg_circle_collidelistall(pgCircleObject *self, PyObject *arg) +{ + PyObject *ret, **items; + Py_ssize_t i; + pgCircleBase *scirc = &self->circle; + int colliding; + + if (!PySequence_Check(arg)) { + return RAISE(PyExc_TypeError, "Argument must be a sequence"); + } + + ret = PyList_New(0); + if (!ret) { + return NULL; + } + + /* fast path */ + if (PySequence_FAST_CHECK(arg)) { + PyObject **items = PySequence_Fast_ITEMS(arg); + + for (i = 0; i < PySequence_Fast_GET_SIZE(arg); i++) { + colliding = _pg_circle_collideswith(scirc, items[i]); + + if (colliding == 1) { + PyObject *num = PyLong_FromSsize_t(i); + if (!num) { + Py_DECREF(ret); + return NULL; + } + + if (PyList_Append(ret, num)) { + Py_DECREF(num); + Py_DECREF(ret); + return NULL; + } + Py_DECREF(num); + } + /*invalid shape*/ + else if (colliding == -1) { + Py_DECREF(ret); + return NULL; + } + } + + return ret; + } + + /* general sequence path */ + for (i = 0; i < PySequence_Length(arg); i++) { + PyObject *obj = PySequence_GetItem(arg, i); + if (!obj) { + Py_DECREF(ret); + return NULL; + } + + colliding = _pg_circle_collideswith(scirc, obj); + Py_DECREF(obj); + + if (colliding == 1) { + PyObject *num = PyLong_FromSsize_t(i); + if (!num) { + Py_DECREF(ret); + return NULL; + } + + if (PyList_Append(ret, num)) { + Py_DECREF(num); + Py_DECREF(ret); + return NULL; + } + Py_DECREF(num); + } + /*invalid shape*/ + else if (colliding == -1) { + Py_DECREF(ret); + return NULL; + } + } + + return ret; +} + static struct PyMethodDef pg_circle_methods[] = { {"collidecircle", (PyCFunction)pg_circle_collidecircle, METH_FASTCALL, NULL}, @@ -600,6 +737,8 @@ static struct PyMethodDef pg_circle_methods[] = { {"collideswith", (PyCFunction)pg_circle_collideswith, METH_O, NULL}, {"collidepolygon", (PyCFunction)pg_circle_collidepolygon, METH_FASTCALL, NULL}, + {"collidelist", (PyCFunction)pg_circle_collidelist, METH_O, NULL}, + {"collidelistall", (PyCFunction)pg_circle_collidelistall, METH_O, NULL}, {"as_rect", (PyCFunction)pg_circle_as_rect, METH_NOARGS, NULL}, {"update", (PyCFunction)pg_circle_update, METH_FASTCALL, NULL}, {"move", (PyCFunction)pg_circle_move, METH_FASTCALL, NULL}, diff --git a/test/test_circle.py b/test/test_circle.py index 5d494ed9..a8c9d307 100644 --- a/test/test_circle.py +++ b/test/test_circle.py @@ -1374,6 +1374,116 @@ def assert_approx_equal(circle1, circle2, eps=1e-12): c.rotate_ip(angle, center) assert_approx_equal(c, rotate_circle(c, angle, center)) + def test_collidelist_argtype(self): + """Tests if the function correctly handles incorrect types as parameters""" + + invalid_types = (None, "1", (1,), 1, (1, 2, 3), True, False) + + c = Circle(10, 10, 4) + + for value in invalid_types: + with self.assertRaises(TypeError): + c.collidelist(value) + + def test_collidelist_argnum(self): + """Tests if the function correctly handles incorrect number of parameters""" + c = Circle(10, 10, 4) + + circles = [(Circle(10, 10, 4), Circle(10, 10, 4))] + + with self.assertRaises(TypeError): + c.collidelist() + + with self.assertRaises(TypeError): + c.collidelist(circles, 1) + + def test_collidelist_return_type(self): + """Tests if the function returns the correct type""" + c = Circle(10, 10, 4) + + objects = [ + Circle(10, 10, 4), + Rect(10, 10, 4, 4), + Line(10, 10, 4, 4), + Polygon([(10, 10), (34, 10), (4, 43)]), + ] + + for object in objects: + self.assertIsInstance(c.collidelist([object]), int) + + def test_collidelist(self): + """Ensures that the collidelist method correctly determines if a circle is + contained within the circle""" + c = Circle(10, 10, 4) + + circles = [Circle(1000, 1000, 2), Circle(5, 10, 5), Circle(16, 10, 7)] + rects = [Rect(1000, 1000, 4, 4), Rect(1000, 200, 5, 5), Rect(5, 10, 7, 3)] + lines = [Line(10, 10, 4, 4), Line(100, 100, 553, 553), Line(136, 110, 324, 337)] + polygons = [ + Polygon([(100, 100), (34, 10), (4, 43)]), + Polygon([(20, 10), (34, 10), (4, 43)]), + Polygon([(10, 10), (34, 10), (4, 43)]), + ] + expected = [1, 2, 0, 2] + + for objects, expected in zip([circles, rects, lines, polygons], expected): + self.assertEqual(c.collidelist(objects), expected) + + def test_collidelistall_argtype(self): + """Tests if the function correctly handles incorrect types as parameters""" + + invalid_types = (None, "1", (1,), 1, (1, 2, 3), True, False) + + c = Circle(10, 10, 4) + + for value in invalid_types: + with self.assertRaises(TypeError): + c.collidelistall(value) + + def test_collidelistall_argnum(self): + """Tests if the function correctly handles incorrect number of parameters""" + c = Circle(10, 10, 4) + + circles = [(Circle(10, 10, 4), Circle(10, 10, 4))] + + with self.assertRaises(TypeError): + c.collidelistall() + + with self.assertRaises(TypeError): + c.collidelistall(circles, 1) + + def test_collidelistall_return_type(self): + """Tests if the function returns the correct type""" + c = Circle(10, 10, 4) + + objects = [ + Circle(10, 10, 4), + Rect(10, 10, 4, 4), + Line(10, 10, 4, 4), + Polygon([(10, 10), (34, 10), (4, 43)]), + ] + + for object in objects: + self.assertIsInstance(c.collidelistall([object]), list) + + def test_collidelistall(self): + """Ensures that the collidelistall method correctly determines if a circle is + contained within the circle""" + c = Circle(10, 10, 4) + + circles = [Circle(1000, 1000, 2), Circle(5, 10, 5), Circle(16, 10, 7)] + rects = [Rect(1000, 1000, 4, 4), Rect(1000, 200, 5, 5), Rect(5, 10, 7, 3)] + lines = [Line(10, 10, 4, 4), Line(0, 0, 553, 553), Line(5, 5, 10, 11)] + polygons = [ + Polygon([(100, 100), (34, 10), (4, 43)]), + Polygon([(20, 10), (34, 10), (4, 43)]), + Polygon([(10, 10), (34, 10), (4, 43)]), + ] + expected = [[1, 2], [2], [0, 1, 2], [2]] + + for objects, expected in zip([circles, rects, lines, polygons], expected): + self.assertEqual(c.collidelistall(objects), expected) + if __name__ == "__main__": unittest.main() From 73a042745542dbb00532c711bb1883296bc5eeb5 Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Sat, 22 Jul 2023 16:23:10 +0200 Subject: [PATCH 2/4] corrected two comments and reduced indentation levels --- src_c/circle.c | 88 +++++++++++++++++++++++---------------------- test/test_circle.py | 6 ++-- 2 files changed, 48 insertions(+), 46 deletions(-) diff --git a/src_c/circle.c b/src_c/circle.c index a31bfe7b..87374c56 100644 --- a/src_c/circle.c +++ b/src_c/circle.c @@ -613,13 +613,13 @@ pg_circle_collidelist(pgCircleObject *self, PyObject *arg) if (PySequence_FAST_CHECK(arg)) { PyObject **items = PySequence_Fast_ITEMS(arg); for (i = 0; i < PySequence_Fast_GET_SIZE(arg); i++) { - colliding = _pg_circle_collideswith(scirc, items[i]); - if (colliding == 1) { - return PyLong_FromSsize_t(i); - } - else if (colliding == -1) { + if ((colliding = _pg_circle_collideswith(scirc, items[i])) == -1) { + /*invalid shape*/ return NULL; } + if (colliding) { + return PyLong_FromSsize_t(i); + } } return PyLong_FromLong(-1); } @@ -631,15 +631,16 @@ pg_circle_collidelist(pgCircleObject *self, PyObject *arg) return NULL; } - colliding = _pg_circle_collideswith(scirc, obj); + if ((colliding = _pg_circle_collideswith(scirc, obj)) == -1) { + /*invalid shape*/ + Py_DECREF(obj); + return NULL; + } Py_DECREF(obj); - if (colliding == 1) { + if (colliding) { return PyLong_FromSsize_t(i); } - else if (colliding == -1) { - return NULL; - } } return PyLong_FromLong(-1); @@ -667,27 +668,28 @@ pg_circle_collidelistall(pgCircleObject *self, PyObject *arg) PyObject **items = PySequence_Fast_ITEMS(arg); for (i = 0; i < PySequence_Fast_GET_SIZE(arg); i++) { - colliding = _pg_circle_collideswith(scirc, items[i]); - - if (colliding == 1) { - PyObject *num = PyLong_FromSsize_t(i); - if (!num) { - Py_DECREF(ret); - return NULL; - } - - if (PyList_Append(ret, num)) { - Py_DECREF(num); - Py_DECREF(ret); - return NULL; - } - Py_DECREF(num); + if ((colliding = _pg_circle_collideswith(scirc, items[i])) == -1) { + /*invalid shape*/ + Py_DECREF(ret); + return NULL; } - /*invalid shape*/ - else if (colliding == -1) { + + if (!colliding) { + continue; + } + + PyObject *num = PyLong_FromSsize_t(i); + if (!num) { Py_DECREF(ret); return NULL; } + + if (PyList_Append(ret, num)) { + Py_DECREF(num); + Py_DECREF(ret); + return NULL; + } + Py_DECREF(num); } return ret; @@ -701,28 +703,30 @@ pg_circle_collidelistall(pgCircleObject *self, PyObject *arg) return NULL; } - colliding = _pg_circle_collideswith(scirc, obj); + if ((colliding = _pg_circle_collideswith(scirc, obj)) == -1) { + /*invalid shape*/ + Py_DECREF(ret); + Py_DECREF(obj); + return NULL; + } Py_DECREF(obj); - if (colliding == 1) { - PyObject *num = PyLong_FromSsize_t(i); - if (!num) { - Py_DECREF(ret); - return NULL; - } + if (!colliding) { + continue; + } - if (PyList_Append(ret, num)) { - Py_DECREF(num); - Py_DECREF(ret); - return NULL; - } - Py_DECREF(num); + PyObject *num = PyLong_FromSsize_t(i); + if (!num) { + Py_DECREF(ret); + return NULL; } - /*invalid shape*/ - else if (colliding == -1) { + + if (PyList_Append(ret, num)) { + Py_DECREF(num); Py_DECREF(ret); return NULL; } + Py_DECREF(num); } return ret; diff --git a/test/test_circle.py b/test/test_circle.py index a8c9d307..b05d198f 100644 --- a/test/test_circle.py +++ b/test/test_circle.py @@ -1412,8 +1412,7 @@ def test_collidelist_return_type(self): self.assertIsInstance(c.collidelist([object]), int) def test_collidelist(self): - """Ensures that the collidelist method correctly determines if a circle is - contained within the circle""" + """Ensures that the collidelist method works correctly""" c = Circle(10, 10, 4) circles = [Circle(1000, 1000, 2), Circle(5, 10, 5), Circle(16, 10, 7)] @@ -1467,8 +1466,7 @@ def test_collidelistall_return_type(self): self.assertIsInstance(c.collidelistall([object]), list) def test_collidelistall(self): - """Ensures that the collidelistall method correctly determines if a circle is - contained within the circle""" + """Ensures that the collidelistall method works correctly""" c = Circle(10, 10, 4) circles = [Circle(1000, 1000, 2), Circle(5, 10, 5), Circle(16, 10, 7)] From f032a65f526577b95e00f7e331ed4f5ea1ce274f Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Mon, 27 May 2024 14:42:49 +0200 Subject: [PATCH 3/4] made _pg_circle_collideswith PG_FORCEINLINE --- src_c/circle.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/src_c/circle.c b/src_c/circle.c index 87374c56..58c9e68b 100644 --- a/src_c/circle.c +++ b/src_c/circle.c @@ -320,7 +320,7 @@ pg_circle_colliderect(pgCircleObject *self, PyObject *const *args, return PyBool_FromLong(pgCollision_RectCircle(&temp, &self->circle)); } -static int +static PG_FORCEINLINE int _pg_circle_collideswith(pgCircleBase *scirc, PyObject *arg) { if (pgCircle_Check(arg)) { @@ -732,6 +732,99 @@ pg_circle_collidelistall(pgCircleObject *self, PyObject *arg) return ret; } +static void +_pg_rotate_circle_helper(pgCircleBase *circle, double angle, double rx, + double ry) +{ + if (angle == 0.0 || fmod(angle, 360.0) == 0.0) { + return; + } + + double x = circle->x - rx; + double y = circle->y - ry; + + const double angle_rad = DEG_TO_RAD(angle); + + double cos_theta = cos(angle_rad); + double sin_theta = sin(angle_rad); + + circle->x = rx + x * cos_theta - y * sin_theta; + circle->y = ry + x * sin_theta + y * cos_theta; +} + +static PyObject * +pg_circle_rotate(pgCircleObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + if (!nargs || nargs > 2) { + return RAISE(PyExc_TypeError, "rotate requires 1 or 2 arguments"); + } + + pgCircleBase *circle = &self->circle; + double angle, rx, ry; + + rx = circle->x; + ry = circle->y; + + if (!pg_DoubleFromObj(args[0], &angle)) { + return RAISE(PyExc_TypeError, + "Invalid angle argument, must be numeric"); + } + + if (nargs != 2) { + return _pg_circle_subtype_new(Py_TYPE(self), circle); + } + + if (!pg_TwoDoublesFromObj(args[1], &rx, &ry)) { + return RAISE(PyExc_TypeError, + "Invalid rotation point argument, must be a sequence of " + "2 numbers"); + } + + PyObject *circle_obj = _pg_circle_subtype_new(Py_TYPE(self), circle); + if (!circle_obj) { + return NULL; + } + + _pg_rotate_circle_helper(&pgCircle_AsCircle(circle_obj), angle, rx, ry); + + return circle_obj; +} + +static PyObject * +pg_circle_rotate_ip(pgCircleObject *self, PyObject *const *args, + Py_ssize_t nargs) +{ + if (!nargs || nargs > 2) { + return RAISE(PyExc_TypeError, "rotate requires 1 or 2 arguments"); + } + + pgCircleBase *circle = &self->circle; + double angle, rx, ry; + + rx = circle->x; + ry = circle->y; + + if (!pg_DoubleFromObj(args[0], &angle)) { + return RAISE(PyExc_TypeError, + "Invalid angle argument, must be numeric"); + } + + if (nargs != 2) { + /* just return None */ + Py_RETURN_NONE; + } + + if (!pg_TwoDoublesFromObj(args[1], &rx, &ry)) { + return RAISE(PyExc_TypeError, + "Invalid rotation point argument, must be a sequence " + "of 2 numbers"); + } + + _pg_rotate_circle_helper(circle, angle, rx, ry); + + Py_RETURN_NONE; +} + static struct PyMethodDef pg_circle_methods[] = { {"collidecircle", (PyCFunction)pg_circle_collidecircle, METH_FASTCALL, NULL}, From 27183954cf68b64463937d3abfd8afa502ebce5e Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Mon, 27 May 2024 18:02:51 +0200 Subject: [PATCH 4/4] remove excess rotate function --- src_c/circle.c | 93 -------------------------------------------------- 1 file changed, 93 deletions(-) diff --git a/src_c/circle.c b/src_c/circle.c index 58c9e68b..8b9934e6 100644 --- a/src_c/circle.c +++ b/src_c/circle.c @@ -732,99 +732,6 @@ pg_circle_collidelistall(pgCircleObject *self, PyObject *arg) return ret; } -static void -_pg_rotate_circle_helper(pgCircleBase *circle, double angle, double rx, - double ry) -{ - if (angle == 0.0 || fmod(angle, 360.0) == 0.0) { - return; - } - - double x = circle->x - rx; - double y = circle->y - ry; - - const double angle_rad = DEG_TO_RAD(angle); - - double cos_theta = cos(angle_rad); - double sin_theta = sin(angle_rad); - - circle->x = rx + x * cos_theta - y * sin_theta; - circle->y = ry + x * sin_theta + y * cos_theta; -} - -static PyObject * -pg_circle_rotate(pgCircleObject *self, PyObject *const *args, Py_ssize_t nargs) -{ - if (!nargs || nargs > 2) { - return RAISE(PyExc_TypeError, "rotate requires 1 or 2 arguments"); - } - - pgCircleBase *circle = &self->circle; - double angle, rx, ry; - - rx = circle->x; - ry = circle->y; - - if (!pg_DoubleFromObj(args[0], &angle)) { - return RAISE(PyExc_TypeError, - "Invalid angle argument, must be numeric"); - } - - if (nargs != 2) { - return _pg_circle_subtype_new(Py_TYPE(self), circle); - } - - if (!pg_TwoDoublesFromObj(args[1], &rx, &ry)) { - return RAISE(PyExc_TypeError, - "Invalid rotation point argument, must be a sequence of " - "2 numbers"); - } - - PyObject *circle_obj = _pg_circle_subtype_new(Py_TYPE(self), circle); - if (!circle_obj) { - return NULL; - } - - _pg_rotate_circle_helper(&pgCircle_AsCircle(circle_obj), angle, rx, ry); - - return circle_obj; -} - -static PyObject * -pg_circle_rotate_ip(pgCircleObject *self, PyObject *const *args, - Py_ssize_t nargs) -{ - if (!nargs || nargs > 2) { - return RAISE(PyExc_TypeError, "rotate requires 1 or 2 arguments"); - } - - pgCircleBase *circle = &self->circle; - double angle, rx, ry; - - rx = circle->x; - ry = circle->y; - - if (!pg_DoubleFromObj(args[0], &angle)) { - return RAISE(PyExc_TypeError, - "Invalid angle argument, must be numeric"); - } - - if (nargs != 2) { - /* just return None */ - Py_RETURN_NONE; - } - - if (!pg_TwoDoublesFromObj(args[1], &rx, &ry)) { - return RAISE(PyExc_TypeError, - "Invalid rotation point argument, must be a sequence " - "of 2 numbers"); - } - - _pg_rotate_circle_helper(circle, angle, rx, ry); - - Py_RETURN_NONE; -} - static struct PyMethodDef pg_circle_methods[] = { {"collidecircle", (PyCFunction)pg_circle_collidecircle, METH_FASTCALL, NULL},