Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add (F)Rect.collidecircle() #2886

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
1 change: 0 additions & 1 deletion buildconfig/Setup.Android.SDL2.in
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ display src_c/display.c $(SDL) $(DEBUG)
event src_c/event.c $(SDL) $(DEBUG)
key src_c/key.c $(SDL) $(DEBUG)
mouse src_c/mouse.c $(SDL) $(DEBUG)
rect src_c/rect.c src_c/pgcompat_rect.c $(SDL) $(DEBUG)
rwobject src_c/rwobject.c $(SDL) $(DEBUG)
surface src_c/simd_blitters_sse2.c src_c/simd_blitters_avx2.c src_c/surface.c src_c/alphablit.c src_c/surface_fill.c src_c/simd_surface_fill_avx2.c src_c/simd_surface_fill_sse2.c $(SDL) $(DEBUG)
surflock src_c/surflock.c $(SDL) $(DEBUG)
Expand Down
1 change: 0 additions & 1 deletion buildconfig/Setup.Emscripten.SDL2.in
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ pixelcopy src_c/void.c
pixelarray src_c/void.c
surface src_c/void.c
surflock src_c/void.c
rect src_c/void.c
rwobject src_c/void.c
system src_c/void.c
window src_c/void.c
Expand Down
1 change: 0 additions & 1 deletion buildconfig/Setup.SDL2.in
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ display src_c/display.c $(SDL) $(DEBUG)
event src_c/event.c $(SDL) $(DEBUG)
key src_c/key.c $(SDL) $(DEBUG)
mouse src_c/mouse.c $(SDL) $(DEBUG)
rect src_c/rect.c src_c/pgcompat_rect.c $(SDL) $(DEBUG)
rwobject src_c/rwobject.c $(SDL) $(DEBUG)
surface src_c/simd_blitters_sse2.c src_c/simd_blitters_avx2.c src_c/surface.c src_c/alphablit.c src_c/surface_fill.c src_c/simd_surface_fill_avx2.c src_c/simd_surface_fill_sse2.c $(SDL) $(DEBUG)
surflock src_c/surflock.c $(SDL) $(DEBUG)
Expand Down
5 changes: 2 additions & 3 deletions buildconfig/stubs/gen_stubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@

# pygame classes that are autoimported into main namespace are kept in this dict
PG_AUTOIMPORT_CLASSES = {
"rect": ["Rect", "FRect"],
"surface": ["Surface", "SurfaceType"],
"color": ["Color"],
"pixelarray": ["PixelArray"],
Expand All @@ -71,8 +70,8 @@
"joystick": ["Joystick"],
"window": ["Window"],
"base": ["__version__"], # need an explicit import
# uncomment below line if Circle is added to the base namespace later
# "geometry": ["Circle"],
# Add "Circle" to the list if Circle is added to the base namespace later
"geometry": ["Rect", "FRect"],
}

# pygame modules from which __init__.py does the equivalent of
Expand Down
13 changes: 11 additions & 2 deletions buildconfig/stubs/pygame/geometry.pyi
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

from typing import (
overload,
Union,
Expand All @@ -7,11 +8,19 @@ from typing import (
Sequence,
)

from pygame import Rect, FRect
from ._common import Coordinate, RectValue
from .rect import Rect, FRect
from .rect import Rect as Rect_, FRect as FRect_
from .math import Vector2

class Rect(Rect_):
...

class FRect(FRect_):
...

RectType = Rect
FRectType = FRect

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

class _HasCirclettribute(Protocol):
Expand Down
7 changes: 7 additions & 0 deletions buildconfig/stubs/pygame/rect.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ from typing import (
)

from ._common import Coordinate, Literal, RectValue, SupportsIndex, Sequence
from .geometry import _CircleValue

if sys.version_info >= (3, 11):
from typing import Self
Expand Down Expand Up @@ -249,6 +250,12 @@ class _GenericRect(Collection[_N]):
def colliderect(
self, left: float, top: float, width: float, height: float, /
) -> bool: ...
@overload
def collidecircle(self, circle: _CircleValue, /) -> bool: ...
@overload
def collidecircle(self, pos: Coordinate, r: float, /) -> bool: ...
@overload
def collidecircle(self, x: float, y: float, r: float, /) -> bool: ...
def collidelist(self, rect_list: Sequence[_RectTypeCompatible_co], /) -> int: ...
def collidelistall(self, rect_list: Sequence[_RectTypeCompatible_co], /) -> List[int]: ...
def collideobjectsall(
Expand Down
18 changes: 18 additions & 0 deletions docs/reST/ref/rect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,24 @@

.. ## Rect.colliderect ##

.. method:: collidecircle

| :sl:`checks if a circle intersects the rectangle`
| :sg:`collidecircle(Circle) -> bool`
| :sg:`collidecircle((x, y), r) -> bool`
| :sg:`collidecircle(x, y, r) -> bool`

Returns `True` if any portion of the `Circle` overlaps with the rectangle,
`False` otherwise.

.. note ::
For collision detection between a rect and a line the :meth:`clipline`
method can be used.
damusss marked this conversation as resolved.
Show resolved Hide resolved

.. versionadded:: 2.5.0

.. ## Rect.collidecircle ##

.. method:: collidelist

| :sl:`test if one rectangle in a list intersects`
Expand Down
3 changes: 1 addition & 2 deletions src_c/_pygame.h
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,6 @@ typedef enum {
* Remember to keep these constants up to date.
*/

#define PYGAMEAPI_RECT_NUMSLOTS 10
#define PYGAMEAPI_JOYSTICK_NUMSLOTS 3
#define PYGAMEAPI_DISPLAY_NUMSLOTS 2
#define PYGAMEAPI_SURFACE_NUMSLOTS 4
Expand All @@ -535,6 +534,6 @@ typedef enum {
#define PYGAMEAPI_BASE_NUMSLOTS 29
#define PYGAMEAPI_EVENT_NUMSLOTS 10
#define PYGAMEAPI_WINDOW_NUMSLOTS 1
#define PYGAMEAPI_GEOMETRY_NUMSLOTS 1
#define PYGAMEAPI_GEOMETRY_NUMSLOTS 11

#endif /* _PYGAME_INTERNAL_H */
1 change: 1 addition & 0 deletions src_c/doc/rect_doc.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#define DOC_RECT_CONTAINS "contains(rect, /) -> bool\ntest if one rectangle is inside another"
#define DOC_RECT_COLLIDEPOINT "collidepoint(x, y, /) -> bool\ncollidepoint((x, y), /) -> bool\ntest if a point is inside a rectangle"
#define DOC_RECT_COLLIDERECT "colliderect(rect, /) -> bool\ntest if two rectangles overlap"
#define DOC_RECT_COLLIDECIRCLE "collidecircle(Circle) -> bool\ncollidecircle((x, y), r) -> bool\ncollidecircle(x, y, r) -> bool\nchecks if a circle intersects the rectangle"
#define DOC_RECT_COLLIDELIST "collidelist(list, /) -> index\ntest if one rectangle in a list intersects"
#define DOC_RECT_COLLIDELISTALL "collidelistall(list, /) -> indices\ntest if all rectangles in a list intersect"
#define DOC_RECT_COLLIDEOBJECTS "collideobjects(rect_list) -> object\ncollideobjects(obj_list, key=func) -> object\ntest if any object in a list intersects"
Expand Down
44 changes: 40 additions & 4 deletions src_c/geometry.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include "pgcompat_rect.c"
#include "rect.c"
#include "circle.c"
#include "geometry_common.c"

Expand All @@ -21,12 +23,11 @@ MODINIT_DEFINE(geometry)
return NULL;
}

import_pygame_rect();
if (PyErr_Occurred()) {
if (PyType_Ready(&pgCircle_Type) < 0) {
return NULL;
}

if (PyType_Ready(&pgCircle_Type) < 0) {
if (PyType_Ready(&pgRect_Type) < 0 || PyType_Ready(&pgFRect_Type) < 0) {
return NULL;
}

Expand All @@ -35,14 +36,49 @@ MODINIT_DEFINE(geometry)
return NULL;
}

Py_INCREF(&pgRect_Type);
if (PyModule_AddObject(module, "RectType", (PyObject *)&pgRect_Type)) {
Py_DECREF(&pgRect_Type);
Py_DECREF(module);
return NULL;
}
Py_INCREF(&pgRect_Type);
if (PyModule_AddObject(module, "Rect", (PyObject *)&pgRect_Type)) {
Py_DECREF(&pgRect_Type);
Py_DECREF(module);
return NULL;
}
Py_INCREF(&pgFRect_Type);
if (PyModule_AddObject(module, "FRectType", (PyObject *)&pgFRect_Type)) {
Py_DECREF(&pgFRect_Type);
Py_DECREF(module);
return NULL;
}
Py_INCREF(&pgFRect_Type);
if (PyModule_AddObject(module, "FRect", (PyObject *)&pgFRect_Type)) {
Py_DECREF(&pgFRect_Type);
Py_DECREF(module);
return NULL;
}

Py_INCREF(&pgCircle_Type);
if (PyModule_AddObject(module, "Circle", (PyObject *)&pgCircle_Type)) {
Py_DECREF(&pgCircle_Type);
Py_DECREF(module);
return NULL;
}

c_api[0] = &pgCircle_Type;
c_api[0] = &pgRect_Type;
c_api[1] = pgRect_New;
c_api[2] = pgRect_New4;
c_api[3] = pgRect_FromObject;
c_api[4] = pgRect_Normalize;
c_api[5] = &pgFRect_Type;
c_api[6] = pgFRect_New;
c_api[7] = pgFRect_New4;
c_api[8] = pgFRect_FromObject;
c_api[9] = pgFRect_Normalize;
c_api[10] = &pgCircle_Type;
apiobj = encapsulate_api(c_api, "geometry");
if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj)) {
Py_XDECREF(apiobj);
Expand Down
47 changes: 26 additions & 21 deletions src_c/include/_pygame.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ typedef struct pg_bufferinfo_s {
#define import_pygame_base() IMPORT_PYGAME_MODULE(base)
#endif /* ~PYGAMEAPI_BASE_INTERNAL */

#ifndef PYGAMEAPI_GEOMETRY_INTERNAL

#define import_pygame_geometry() IMPORT_PYGAME_MODULE(geometry)

#endif /* ~PYGAMEAPI_GEOMETRY_INTERNAL */

typedef struct {
PyObject_HEAD SDL_Rect r;
PyObject *weakreflist;
Expand All @@ -204,35 +210,42 @@ typedef struct {

#define pgRect_AsRect(x) (((pgRectObject *)x)->r)
#define pgFRect_AsRect(x) (((pgFRectObject *)x)->r)

#ifndef PYGAMEAPI_RECT_INTERNAL
#define pgRect_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(rect, 0))

#define pgRect_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(geometry, 0))

#define pgRect_Check(x) ((x)->ob_type == &pgRect_Type)
#define pgRect_New (*(PyObject * (*)(SDL_Rect *)) PYGAMEAPI_GET_SLOT(rect, 1))
#define pgRect_New \
(*(PyObject * (*)(SDL_Rect *)) PYGAMEAPI_GET_SLOT(geometry, 1))

#define pgRect_New4 \
(*(PyObject * (*)(int, int, int, int)) PYGAMEAPI_GET_SLOT(rect, 2))
(*(PyObject * (*)(int, int, int, int)) PYGAMEAPI_GET_SLOT(geometry, 2))

#define pgRect_FromObject \
(*(SDL_Rect * (*)(PyObject *, SDL_Rect *)) PYGAMEAPI_GET_SLOT(rect, 3))
(*(SDL_Rect * (*)(PyObject *, SDL_Rect *)) PYGAMEAPI_GET_SLOT(geometry, 3))

#define pgRect_Normalize (*(void (*)(SDL_Rect *))PYGAMEAPI_GET_SLOT(rect, 4))
#define pgRect_Normalize \
(*(void (*)(SDL_Rect *))PYGAMEAPI_GET_SLOT(geometry, 4))

#define pgFRect_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(rect, 5))
#define pgFRect_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(geometry, 5))

#define pgFRect_Check(x) ((x)->ob_type == &pgFRect_Type)
#define pgFRect_New \
(*(PyObject * (*)(SDL_FRect *)) PYGAMEAPI_GET_SLOT(rect, 6))
(*(PyObject * (*)(SDL_FRect *)) PYGAMEAPI_GET_SLOT(geometry, 6))

#define pgFRect_New4 \
(*(PyObject * (*)(float, float, float, float)) PYGAMEAPI_GET_SLOT(rect, 7))
#define pgFRect_New4 \
(*(PyObject * (*)(float, float, float, float)) \
PYGAMEAPI_GET_SLOT(geometry, 7))

#define pgFRect_FromObject \
(*(SDL_FRect * (*)(PyObject *, SDL_FRect *)) PYGAMEAPI_GET_SLOT(rect, 8))
#define pgFRect_FromObject \
(*(SDL_FRect * (*)(PyObject *, SDL_FRect *)) \
PYGAMEAPI_GET_SLOT(geometry, 8))

#define pgFRect_Normalize (*(void (*)(SDL_FRect *))PYGAMEAPI_GET_SLOT(rect, 9))
#define pgFRect_Normalize \
(*(void (*)(SDL_FRect *))PYGAMEAPI_GET_SLOT(geometry, 9))

#define import_pygame_rect() IMPORT_PYGAME_MODULE(rect)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider just replacing all calls to import_pygame_rect() with import_pygame_geometry() and removing import_pygame_rect() ? It is internal API only so we are likely only confusing ourselves in the future by keeping import_pygame_rect() around and just having it import geometry.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't do that as I didn't know if doing that would cause too many internal changes, but I initially wanted to rename it, and I can do it if it's needed. Update me if you think I should change it, maybe based on some other contributor's opinion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not essential to this PR IMHO, I guess we'll see how everyone else feels about moving Rect/Frect and shimming.

#define import_pygame_rect() IMPORT_PYGAME_MODULE(geometry)
#endif /* ~PYGAMEAPI_RECT_INTERNAL */

/*
Expand Down Expand Up @@ -500,12 +513,6 @@ typedef struct pgColorObject pgColorObject;
#define import_pygame_math() IMPORT_PYGAME_MODULE(math)
#endif /* PYGAMEAPI_MATH_INTERNAL */

#ifndef PYGAMEAPI_GEOMETRY_INTERNAL

#define import_pygame_geometry() IMPORT_PYGAME_MODULE(geometry)

#endif /* ~PYGAMEAPI_GEOMETRY_INTERNAL */

/*
* Window module
*/
Expand All @@ -531,7 +538,6 @@ typedef struct {
*/
#ifdef PYGAME_H
PYGAMEAPI_DEFINE_SLOTS(base);
PYGAMEAPI_DEFINE_SLOTS(rect);
PYGAMEAPI_DEFINE_SLOTS(joystick);
PYGAMEAPI_DEFINE_SLOTS(display);
PYGAMEAPI_DEFINE_SLOTS(surface);
Expand All @@ -545,7 +551,6 @@ PYGAMEAPI_DEFINE_SLOTS(window);
PYGAMEAPI_DEFINE_SLOTS(geometry);
#else /* ~PYGAME_H */
PYGAMEAPI_EXTERN_SLOTS(base);
PYGAMEAPI_EXTERN_SLOTS(rect);
PYGAMEAPI_EXTERN_SLOTS(joystick);
PYGAMEAPI_EXTERN_SLOTS(display);
PYGAMEAPI_EXTERN_SLOTS(surface);
Expand Down
9 changes: 0 additions & 9 deletions src_c/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,6 @@ mouse = py.extension_module(
subdir: pg,
)

rect = py.extension_module(
'rect',
['rect.c', 'pgcompat_rect.c'],
c_args: warnings_error,
dependencies: pg_base_deps,
install: true,
subdir: pg,
)

rwobject = py.extension_module(
'rwobject',
'rwobject.c',
Expand Down
Loading
Loading