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

Optimized Rect multi-collision methods #2786

Merged
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 95 additions & 17 deletions src_c/rect_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1292,17 +1292,39 @@ RectExport_colliderect(RectObject *self, PyObject *const *args,
return PyBool_FromLong(_pg_do_rects_intersect(&self->r, argrect));
}

#ifndef OPTIMIZED_COLLIDERECT
/* This macro is used to optimize the colliderect function. Makes use of
* precalculated values to avoid unnecessary calculations. It also checks
* whether the other rect has 0 width or height, in which case we don't
* collide. */
#define OPTIMIZED_COLLIDERECT(r) \
(r->w && r->h && left < MAX(r->x, r->x + r->w) && \
top < MAX(r->y, r->y + r->h) && right > MIN(r->x, r->x + r->w) && \
bottom > MIN(r->y, r->y + r->h))
#endif

static PyObject *
RectExport_collidelist(RectObject *self, PyObject *arg)
{
InnerRect *argrect, *srect = &self->r, temp;
int loop;

/* If the calling rect has 0 width or height, it cannot collide with
* anything, hence return -1 directly. */
if (srect->w == 0 || srect->h == 0) {
return PyLong_FromLong(-1);
}

if (!PySequence_Check(arg)) {
return RAISE(PyExc_TypeError,
"Argument must be a sequence of rectstyle objects.");
}

const PrimitiveType left = MIN(srect->x, srect->x + srect->w);
const PrimitiveType top = MIN(srect->y, srect->y + srect->h);
const PrimitiveType right = MAX(srect->x, srect->x + srect->w);
const PrimitiveType bottom = MAX(srect->y, srect->y + srect->h);
itzpr3d4t0r marked this conversation as resolved.
Show resolved Hide resolved

/* If the sequence is a fast sequence, we can use the faster
* PySequence_Fast_ITEMS() function to get the items. */
if (pgSequenceFast_Check(arg)) {
Expand All @@ -1313,7 +1335,8 @@ RectExport_collidelist(RectObject *self, PyObject *arg)
PyExc_TypeError,
"Argument must be a sequence of rectstyle objects.");
}
if (_pg_do_rects_intersect(srect, argrect)) {

if (OPTIMIZED_COLLIDERECT(argrect)) {
return PyLong_FromLong(loop);
}
}
Expand All @@ -1322,7 +1345,7 @@ RectExport_collidelist(RectObject *self, PyObject *arg)
* PySequence_GetItem() function to get the items. */
itzpr3d4t0r marked this conversation as resolved.
Show resolved Hide resolved
else {
for (loop = 0; loop < PySequence_Length(arg); loop++) {
PyObject *obj = PySequence_GetItem(arg, loop);
PyObject *obj = PySequence_ITEM(arg, loop);
itzpr3d4t0r marked this conversation as resolved.
Show resolved Hide resolved

if (!obj || !(argrect = RectFromObject(obj, &temp))) {
Py_XDECREF(obj);
Expand All @@ -1333,7 +1356,7 @@ RectExport_collidelist(RectObject *self, PyObject *arg)

Py_DECREF(obj);

if (_pg_do_rects_intersect(srect, argrect)) {
if (OPTIMIZED_COLLIDERECT(argrect)) {
return PyLong_FromLong(loop);
}
}
Expand All @@ -1358,6 +1381,17 @@ RectExport_collidelistall(RectObject *self, PyObject *arg)
return NULL;
}

/* If the calling rect has 0 width or height, it cannot collide with
* anything, hence return an empty list directly. */
if (srect->w == 0 || srect->h == 0) {
return ret;
}
itzpr3d4t0r marked this conversation as resolved.
Show resolved Hide resolved

const PrimitiveType left = MIN(srect->x, srect->x + srect->w);
const PrimitiveType top = MIN(srect->y, srect->y + srect->h);
const PrimitiveType right = MAX(srect->x, srect->x + srect->w);
const PrimitiveType bottom = MAX(srect->y, srect->y + srect->h);

/* If the sequence is a fast sequence, we can use the faster
* PySequence_Fast_ITEMS() function to get the items. */
if (pgSequenceFast_Check(arg)) {
Expand All @@ -1370,7 +1404,7 @@ RectExport_collidelistall(RectObject *self, PyObject *arg)
"Argument must be a sequence of rectstyle objects.");
}

if (_pg_do_rects_intersect(srect, argrect)) {
if (OPTIMIZED_COLLIDERECT(argrect)) {
PyObject *num = PyLong_FromLong(loop);
if (!num) {
Py_DECREF(ret);
Expand Down Expand Up @@ -1400,7 +1434,7 @@ RectExport_collidelistall(RectObject *self, PyObject *arg)

Py_DECREF(obj);

if (_pg_do_rects_intersect(srect, argrect)) {
if (OPTIMIZED_COLLIDERECT(argrect)) {
PyObject *num = PyLong_FromLong(loop);
if (!num) {
Py_DECREF(ret);
Expand Down Expand Up @@ -1452,7 +1486,7 @@ static PyObject *
RectExport_collideobjectsall(RectObject *self, PyObject *args,
PyObject *kwargs)
{
InnerRect *argrect;
InnerRect *argrect, *srect = &self->r;
InnerRect temp;
Py_ssize_t size;
int loop;
Expand Down Expand Up @@ -1485,14 +1519,25 @@ RectExport_collideobjectsall(RectObject *self, PyObject *args,
return NULL;
}

/* If the calling rect has 0 width or height, it cannot collide with
* anything, hence return an empty list directly. */
if (srect->w == 0 || srect->h == 0) {
return ret;
}

const PrimitiveType left = MIN(srect->x, srect->x + srect->w);
const PrimitiveType top = MIN(srect->y, srect->y + srect->h);
const PrimitiveType right = MAX(srect->x, srect->x + srect->w);
const PrimitiveType bottom = MAX(srect->y, srect->y + srect->h);

size = PySequence_Length(list);
if (size == -1) {
Py_DECREF(ret);
return NULL;
}

for (loop = 0; loop < size; ++loop) {
obj = PySequence_GetItem(list, loop);
obj = PySequence_ITEM(list, loop);

if (!obj) {
Py_DECREF(ret);
Expand All @@ -1506,7 +1551,7 @@ RectExport_collideobjectsall(RectObject *self, PyObject *args,
return NULL;
}

if (_pg_do_rects_intersect(&self->r, argrect)) {
if (OPTIMIZED_COLLIDERECT(argrect)) {
if (0 != PyList_Append(ret, obj)) {
Py_DECREF(ret);
Py_DECREF(obj);
Expand All @@ -1522,7 +1567,7 @@ RectExport_collideobjectsall(RectObject *self, PyObject *args,
static PyObject *
RectExport_collideobjects(RectObject *self, PyObject *args, PyObject *kwargs)
{
InnerRect *argrect;
InnerRect *argrect, *srect = &self->r;
InnerRect temp;
Py_ssize_t size;
int loop;
Expand All @@ -1549,13 +1594,24 @@ RectExport_collideobjects(RectObject *self, PyObject *args, PyObject *kwargs)
"Key function must be callable with one argument.");
}

/* If the calling rect has 0 width or height, it cannot collide with
* anything, hence return None directly. */
if (srect->w == 0 || srect->h == 0) {
Py_RETURN_NONE;
}

const PrimitiveType left = MIN(srect->x, srect->x + srect->w);
const PrimitiveType top = MIN(srect->y, srect->y + srect->h);
const PrimitiveType right = MAX(srect->x, srect->x + srect->w);
const PrimitiveType bottom = MAX(srect->y, srect->y + srect->h);

size = PySequence_Length(list);
if (size == -1) {
return NULL;
}

for (loop = 0; loop < size; ++loop) {
obj = PySequence_GetItem(list, loop);
obj = PySequence_ITEM(list, loop);

if (!obj) {
return NULL;
Expand All @@ -1567,7 +1623,7 @@ RectExport_collideobjects(RectObject *self, PyObject *args, PyObject *kwargs)
return NULL;
}

if (_pg_do_rects_intersect(&self->r, argrect)) {
if (OPTIMIZED_COLLIDERECT(argrect)) {
return obj;
}
Py_DECREF(obj);
Expand All @@ -1579,7 +1635,7 @@ RectExport_collideobjects(RectObject *self, PyObject *args, PyObject *kwargs)
static PyObject *
RectExport_collidedict(RectObject *self, PyObject *args, PyObject *kwargs)
{
InnerRect *argrect, temp;
InnerRect *argrect, temp, *srect = &self->r;
Py_ssize_t loop = 0;
Py_ssize_t values = 0; /* Defaults to expecting keys as rects. */
PyObject *dict, *key, *val;
Expand All @@ -1596,6 +1652,17 @@ RectExport_collidedict(RectObject *self, PyObject *args, PyObject *kwargs)
return RAISE(PyExc_TypeError, "first argument must be a dict");
}

/* If the calling rect has 0 width or height, it cannot collide with
* anything, hence return None directly. */
if (srect->w == 0 || srect->h == 0) {
Py_RETURN_NONE;
}

const PrimitiveType left = MIN(srect->x, srect->x + srect->w);
const PrimitiveType top = MIN(srect->y, srect->y + srect->h);
const PrimitiveType right = MAX(srect->x, srect->x + srect->w);
const PrimitiveType bottom = MAX(srect->y, srect->y + srect->h);

while (PyDict_Next(dict, &loop, &key, &val)) {
if (values) {
if (!(argrect = RectFromObject(val, &temp))) {
Expand All @@ -1609,8 +1676,8 @@ RectExport_collidedict(RectObject *self, PyObject *args, PyObject *kwargs)
}
}

if (_pg_do_rects_intersect(&self->r, argrect)) {
ret = Py_BuildValue("(OO)", key, val);
if (OPTIMIZED_COLLIDERECT(argrect)) {
ret = PyTuple_Pack(2, key, val);
break;
}
}
Expand All @@ -1624,7 +1691,7 @@ RectExport_collidedict(RectObject *self, PyObject *args, PyObject *kwargs)
static PyObject *
RectExport_collidedictall(RectObject *self, PyObject *args, PyObject *kwargs)
{
InnerRect *argrect, temp;
InnerRect *argrect, temp, *srect = &self->r;
Py_ssize_t loop = 0;
Py_ssize_t values = 0; /* Defaults to expecting keys as rects. */
PyObject *dict, *key, *val;
Expand All @@ -1645,6 +1712,17 @@ RectExport_collidedictall(RectObject *self, PyObject *args, PyObject *kwargs)
if (!ret)
return NULL;

/* If the calling rect has 0 width or height, it cannot collide with
* anything, hence return an empty list directly. */
if (srect->w == 0 || srect->h == 0) {
return ret;
}

const PrimitiveType left = MIN(srect->x, srect->x + srect->w);
const PrimitiveType top = MIN(srect->y, srect->y + srect->h);
const PrimitiveType right = MAX(srect->x, srect->x + srect->w);
const PrimitiveType bottom = MAX(srect->y, srect->y + srect->h);

while (PyDict_Next(dict, &loop, &key, &val)) {
if (values) {
if (!(argrect = RectFromObject(val, &temp))) {
Expand All @@ -1660,8 +1738,8 @@ RectExport_collidedictall(RectObject *self, PyObject *args, PyObject *kwargs)
}
}

if (_pg_do_rects_intersect(&self->r, argrect)) {
PyObject *num = Py_BuildValue("(OO)", key, val);
if (OPTIMIZED_COLLIDERECT(argrect)) {
PyObject *num = PyTuple_Pack(2, key, val);
if (!num) {
Py_DECREF(ret);
return NULL;
Expand Down
Loading