Skip to content

Commit 7699c03

Browse files
itzpr3d4t0rEmc2356andrewhong04ScriptLineStudiosavaxar
authored
Add Circle collideswith() (#2661)
* Added Circle.collideswith() Co-authored-by: Emc2356 <[email protected]> Co-authored-by: NovialRiptide <[email protected]> Co-authored-by: ScriptLineStudios <[email protected]> Co-authored-by: Avaxar <[email protected]> * fixed docs * added missing versionadded tags * fixed test indent and error msgs * capitalize R in FRect * addressed review --------- Co-authored-by: Emc2356 <[email protected]> Co-authored-by: NovialRiptide <[email protected]> Co-authored-by: ScriptLineStudios <[email protected]> Co-authored-by: Avaxar <[email protected]>
1 parent acb6155 commit 7699c03

File tree

5 files changed

+117
-0
lines changed

5 files changed

+117
-0
lines changed

buildconfig/stubs/pygame/geometry.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ from typing import (
99

1010
from pygame import Rect, FRect
1111
from ._common import Coordinate, RectValue
12+
from .rect import Rect, FRect
13+
from .math import Vector2
1214

1315
_CanBeCircle = Union[Circle, Tuple[Coordinate, float], Sequence[float]]
1416

@@ -18,6 +20,7 @@ class _HasCirclettribute(Protocol):
1820
circle: Union[_CanBeCircle, Callable[[], _CanBeCircle]]
1921

2022
_CircleValue = Union[_CanBeCircle, _HasCirclettribute]
23+
_CanBeCollided = Union[Circle, Rect, FRect, Coordinate, Vector2]
2124

2225
class Circle:
2326
@property
@@ -90,6 +93,7 @@ class Circle:
9093
def colliderect(self, x: float, y: float, w: float, h: float, /) -> bool: ...
9194
@overload
9295
def colliderect(self, topleft: Coordinate, size: Coordinate, /) -> bool: ...
96+
def collideswith(self, other: _CanBeCollided, /) -> bool: ...
9397
@overload
9498
def update(self, circle: _CircleValue, /) -> None: ...
9599
@overload

docs/reST/ref/geometry.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,27 @@
262262

263263
.. ## Circle.colliderect ##
264264
265+
.. method:: collideswith
266+
267+
| :sl:`check if a shape or point collides with the circle`
268+
| :sg:`collideswith(circle, /) -> bool`
269+
| :sg:`collideswith(rect, /) -> bool`
270+
| :sg:`collideswith((x, y), /) -> bool`
271+
| :sg:`collideswith(vector2, /) -> bool`
272+
273+
The `collideswith` method checks if a shape or point overlaps with a `Circle` object.
274+
It takes a single argument which can be a `Circle`, `Rect`, `FRect`, or a point.
275+
It returns `True` if there's an overlap, and `False` otherwise.
276+
277+
.. note::
278+
The shape argument must be an actual shape object (`Circle`, `Rect`, or `FRect`).
279+
You can't pass a tuple or list of coordinates representing the shape (except for a point),
280+
because the shape type can't be determined from the coordinates alone.
281+
282+
.. versionadded:: 2.5.0
283+
284+
.. ## Circle.collideswith ##
285+
265286
.. method:: update
266287

267288
| :sl:`updates the circle position and radius`

src_c/circle.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,44 @@ pg_circle_rotate_ip(pgCircleObject *self, PyObject *const *args,
481481
Py_RETURN_NONE;
482482
}
483483

484+
static PyObject *
485+
pg_circle_collideswith(pgCircleObject *self, PyObject *arg)
486+
{
487+
int result = 0;
488+
pgCircleBase *scirc = &self->circle;
489+
if (pgCircle_Check(arg)) {
490+
result = pgCollision_CircleCircle(&pgCircle_AsCircle(arg), scirc);
491+
}
492+
else if (pgRect_Check(arg)) {
493+
SDL_Rect *argrect = &pgRect_AsRect(arg);
494+
result = pgCollision_RectCircle((double)argrect->x, (double)argrect->y,
495+
(double)argrect->w, (double)argrect->h,
496+
scirc);
497+
}
498+
else if (pgFRect_Check(arg)) {
499+
SDL_FRect *argrect = &pgFRect_AsRect(arg);
500+
result = pgCollision_RectCircle((double)argrect->x, (double)argrect->y,
501+
(double)argrect->w, (double)argrect->h,
502+
scirc);
503+
}
504+
else if (PySequence_Check(arg)) {
505+
double x, y;
506+
if (!pg_TwoDoublesFromObj(arg, &x, &y)) {
507+
return RAISE(
508+
PyExc_TypeError,
509+
"Invalid point argument, must be a sequence of two numbers");
510+
}
511+
result = pgCollision_CirclePoint(scirc, x, y);
512+
}
513+
else {
514+
return RAISE(PyExc_TypeError,
515+
"Invalid shape argument, must be a Circle, Rect / FRect, "
516+
"Line, Polygon or a sequence of two numbers");
517+
}
518+
519+
return PyBool_FromLong(result);
520+
}
521+
484522
static PyObject *
485523
pg_circle_as_rect(pgCircleObject *self, PyObject *_null)
486524
{
@@ -515,6 +553,8 @@ static struct PyMethodDef pg_circle_methods[] = {
515553
DOC_CIRCLE_COLLIDERECT},
516554
{"update", (PyCFunction)pg_circle_update, METH_FASTCALL,
517555
DOC_CIRCLE_UPDATE},
556+
{"collideswith", (PyCFunction)pg_circle_collideswith, METH_O,
557+
DOC_CIRCLE_COLLIDESWITH},
518558
{"as_rect", (PyCFunction)pg_circle_as_rect, METH_NOARGS,
519559
DOC_CIRCLE_ASRECT},
520560
{"as_frect", (PyCFunction)pg_circle_as_frect, METH_NOARGS,

src_c/doc/geometry_doc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define DOC_CIRCLE_MOVE "move((x, y), /) -> Circle\nmove(x, y, /) -> Circle\nmove(vector2, /) -> Circle\nmoves the circle by a given amount"
1515
#define DOC_CIRCLE_MOVEIP "move_ip((x, y), /) -> None\nmove_ip(x, y, /) -> None\nmove_ip(vector2, /) -> None\nmoves the circle by a given amount, in place"
1616
#define DOC_CIRCLE_COLLIDERECT "colliderect(rect, /) -> bool\ncolliderect((x, y, width, height), /) -> bool\ncolliderect(x, y, width, height, /) -> bool\ncolliderect((x, y), (width, height), /) -> bool\nchecks if a rectangle intersects the circle"
17+
#define DOC_CIRCLE_COLLIDESWITH "collideswith(circle, /) -> bool\ncollideswith(rect, /) -> bool\ncollideswith((x, y), /) -> bool\ncollideswith(vector2, /) -> bool\ncheck if a shape or point collides with the circle"
1718
#define DOC_CIRCLE_UPDATE "update((x, y), radius, /) -> None\nupdate(x, y, radius, /) -> None\nupdates the circle position and radius"
1819
#define DOC_CIRCLE_ROTATE "rotate(angle, rotation_point=Circle.center, /) -> Circle\nrotate(angle, /) -> Circle\nrotates the circle"
1920
#define DOC_CIRCLE_ROTATEIP "rotate_ip(angle, rotation_point=Circle.center, /) -> None\nrotate_ip(angle, /) -> None\nrotates the circle in place"

test/geometry_test.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,57 @@ def test_colliderect(self):
625625
self.assertTrue(c3.colliderect(0.4, 0.0, 1, 1), msgt)
626626
self.assertTrue(c3.colliderect((0.4, 0.0), (1, 1)), msgt)
627627

628+
def test_collideswith_argtype(self):
629+
"""tests if the function correctly handles incorrect types as parameters"""
630+
invalid_types = (None, [], "1", (1,), Vector3(1, 1, 1), 1)
631+
632+
c = Circle(10, 10, 4)
633+
634+
for value in invalid_types:
635+
with self.assertRaises(TypeError):
636+
c.collideswith(value)
637+
638+
def test_collideswith_argnum(self):
639+
c = Circle(10, 10, 4)
640+
args = [tuple(range(x)) for x in range(2, 4)]
641+
642+
# no params
643+
with self.assertRaises(TypeError):
644+
c.collideswith()
645+
646+
# too many params
647+
for arg in args:
648+
with self.assertRaises(TypeError):
649+
c.collideswith(*arg)
650+
651+
def test_collideswith(self):
652+
"""Ensures the collideswith function correctly registers collisions with circles, lines, rects and points"""
653+
c = Circle(0, 0, 5)
654+
655+
# circle
656+
c2 = Circle(0, 10, 15)
657+
c3 = Circle(100, 100, 1)
658+
self.assertTrue(c.collideswith(c2))
659+
self.assertFalse(c.collideswith(c3))
660+
661+
# rect
662+
r = Rect(0, 0, 10, 10)
663+
r2 = Rect(50, 0, 10, 10)
664+
self.assertTrue(c.collideswith(r))
665+
self.assertFalse(c.collideswith(r2))
666+
667+
# rect
668+
r = FRect(0, 0, 10, 10)
669+
r2 = FRect(50, 0, 10, 10)
670+
self.assertTrue(c.collideswith(r))
671+
self.assertFalse(c.collideswith(r2))
672+
673+
# point
674+
p = (0, 0)
675+
p2 = (50, 0)
676+
self.assertTrue(c.collideswith(p))
677+
self.assertFalse(c.collideswith(p2))
678+
628679
def test_update(self):
629680
"""Ensures that updating the circle position
630681
and dimension correctly updates position and dimension"""

0 commit comments

Comments
 (0)