Skip to content

Commit 0209311

Browse files
authored
Optimized Rect multi-collision methods (#2786)
* optimized rect multi-collision methods * fix * Addressed reviews, added more comments, swapped Py_BuildValue for PyTuple_Pack. * addressed review * addressed reviews
1 parent dfaa76e commit 0209311

File tree

1 file changed

+89
-18
lines changed

1 file changed

+89
-18
lines changed

src_c/rect_impl.h

+89-18
Original file line numberDiff line numberDiff line change
@@ -1292,17 +1292,47 @@ RectExport_colliderect(RectObject *self, PyObject *const *args,
12921292
return PyBool_FromLong(_pg_do_rects_intersect(&self->r, argrect));
12931293
}
12941294

1295+
#ifndef OPTIMIZED_COLLIDERECT_SETUP
1296+
/* This macro is used to optimize the colliderect function. It calculates
1297+
* the left, top, right and bottom values of the calling rect only once
1298+
* and uses them in the OPTIMIZED_COLLIDERECT macro. */
1299+
#define OPTIMIZED_COLLIDERECT_SETUP \
1300+
const PrimitiveType left = MIN(srect->x, srect->x + srect->w); \
1301+
const PrimitiveType top = MIN(srect->y, srect->y + srect->h); \
1302+
const PrimitiveType right = MAX(srect->x, srect->x + srect->w); \
1303+
const PrimitiveType bottom = MAX(srect->y, srect->y + srect->h);
1304+
#endif
1305+
1306+
#ifndef OPTIMIZED_COLLIDERECT
1307+
/* This macro is used to optimize the colliderect function. Makes use of
1308+
* precalculated values to avoid unnecessary calculations. It also checks
1309+
* whether the other rect has 0 width or height, in which case we don't
1310+
* collide. */
1311+
#define OPTIMIZED_COLLIDERECT(r) \
1312+
(r->w && r->h && left < MAX(r->x, r->x + r->w) && \
1313+
top < MAX(r->y, r->y + r->h) && right > MIN(r->x, r->x + r->w) && \
1314+
bottom > MIN(r->y, r->y + r->h))
1315+
#endif
1316+
12951317
static PyObject *
12961318
RectExport_collidelist(RectObject *self, PyObject *arg)
12971319
{
12981320
InnerRect *argrect, *srect = &self->r, temp;
12991321
int loop;
13001322

1323+
/* If the calling rect has 0 width or height, it cannot collide with
1324+
* anything, hence return -1 directly. */
1325+
if (srect->w == 0 || srect->h == 0) {
1326+
return PyLong_FromLong(-1);
1327+
}
1328+
13011329
if (!PySequence_Check(arg)) {
13021330
return RAISE(PyExc_TypeError,
13031331
"Argument must be a sequence of rectstyle objects.");
13041332
}
13051333

1334+
OPTIMIZED_COLLIDERECT_SETUP;
1335+
13061336
/* If the sequence is a fast sequence, we can use the faster
13071337
* PySequence_Fast_ITEMS() function to get the items. */
13081338
if (pgSequenceFast_Check(arg)) {
@@ -1313,16 +1343,17 @@ RectExport_collidelist(RectObject *self, PyObject *arg)
13131343
PyExc_TypeError,
13141344
"Argument must be a sequence of rectstyle objects.");
13151345
}
1316-
if (_pg_do_rects_intersect(srect, argrect)) {
1346+
1347+
if (OPTIMIZED_COLLIDERECT(argrect)) {
13171348
return PyLong_FromLong(loop);
13181349
}
13191350
}
13201351
}
13211352
/* If the sequence is not a fast sequence, we have to use the slower
1322-
* PySequence_GetItem() function to get the items. */
1353+
* PySequence_ITEM() function to get the items. */
13231354
else {
13241355
for (loop = 0; loop < PySequence_Length(arg); loop++) {
1325-
PyObject *obj = PySequence_GetItem(arg, loop);
1356+
PyObject *obj = PySequence_ITEM(arg, loop);
13261357

13271358
if (!obj || !(argrect = RectFromObject(obj, &temp))) {
13281359
Py_XDECREF(obj);
@@ -1333,7 +1364,7 @@ RectExport_collidelist(RectObject *self, PyObject *arg)
13331364

13341365
Py_DECREF(obj);
13351366

1336-
if (_pg_do_rects_intersect(srect, argrect)) {
1367+
if (OPTIMIZED_COLLIDERECT(argrect)) {
13371368
return PyLong_FromLong(loop);
13381369
}
13391370
}
@@ -1358,6 +1389,14 @@ RectExport_collidelistall(RectObject *self, PyObject *arg)
13581389
return NULL;
13591390
}
13601391

1392+
/* If the calling rect has 0 width or height, it cannot collide with
1393+
* anything, hence return an empty list directly. */
1394+
if (srect->w == 0 || srect->h == 0) {
1395+
return ret;
1396+
}
1397+
1398+
OPTIMIZED_COLLIDERECT_SETUP;
1399+
13611400
/* If the sequence is a fast sequence, we can use the faster
13621401
* PySequence_Fast_ITEMS() function to get the items. */
13631402
if (pgSequenceFast_Check(arg)) {
@@ -1370,7 +1409,7 @@ RectExport_collidelistall(RectObject *self, PyObject *arg)
13701409
"Argument must be a sequence of rectstyle objects.");
13711410
}
13721411

1373-
if (_pg_do_rects_intersect(srect, argrect)) {
1412+
if (OPTIMIZED_COLLIDERECT(argrect)) {
13741413
PyObject *num = PyLong_FromLong(loop);
13751414
if (!num) {
13761415
Py_DECREF(ret);
@@ -1400,7 +1439,7 @@ RectExport_collidelistall(RectObject *self, PyObject *arg)
14001439

14011440
Py_DECREF(obj);
14021441

1403-
if (_pg_do_rects_intersect(srect, argrect)) {
1442+
if (OPTIMIZED_COLLIDERECT(argrect)) {
14041443
PyObject *num = PyLong_FromLong(loop);
14051444
if (!num) {
14061445
Py_DECREF(ret);
@@ -1452,7 +1491,7 @@ static PyObject *
14521491
RectExport_collideobjectsall(RectObject *self, PyObject *args,
14531492
PyObject *kwargs)
14541493
{
1455-
InnerRect *argrect;
1494+
InnerRect *argrect, *srect = &self->r;
14561495
InnerRect temp;
14571496
Py_ssize_t size;
14581497
int loop;
@@ -1485,14 +1524,22 @@ RectExport_collideobjectsall(RectObject *self, PyObject *args,
14851524
return NULL;
14861525
}
14871526

1527+
/* If the calling rect has 0 width or height, it cannot collide with
1528+
* anything, hence return an empty list directly. */
1529+
if (srect->w == 0 || srect->h == 0) {
1530+
return ret;
1531+
}
1532+
1533+
OPTIMIZED_COLLIDERECT_SETUP;
1534+
14881535
size = PySequence_Length(list);
14891536
if (size == -1) {
14901537
Py_DECREF(ret);
14911538
return NULL;
14921539
}
14931540

14941541
for (loop = 0; loop < size; ++loop) {
1495-
obj = PySequence_GetItem(list, loop);
1542+
obj = PySequence_ITEM(list, loop);
14961543

14971544
if (!obj) {
14981545
Py_DECREF(ret);
@@ -1506,7 +1553,7 @@ RectExport_collideobjectsall(RectObject *self, PyObject *args,
15061553
return NULL;
15071554
}
15081555

1509-
if (_pg_do_rects_intersect(&self->r, argrect)) {
1556+
if (OPTIMIZED_COLLIDERECT(argrect)) {
15101557
if (0 != PyList_Append(ret, obj)) {
15111558
Py_DECREF(ret);
15121559
Py_DECREF(obj);
@@ -1522,7 +1569,7 @@ RectExport_collideobjectsall(RectObject *self, PyObject *args,
15221569
static PyObject *
15231570
RectExport_collideobjects(RectObject *self, PyObject *args, PyObject *kwargs)
15241571
{
1525-
InnerRect *argrect;
1572+
InnerRect *argrect, *srect = &self->r;
15261573
InnerRect temp;
15271574
Py_ssize_t size;
15281575
int loop;
@@ -1549,13 +1596,21 @@ RectExport_collideobjects(RectObject *self, PyObject *args, PyObject *kwargs)
15491596
"Key function must be callable with one argument.");
15501597
}
15511598

1599+
/* If the calling rect has 0 width or height, it cannot collide with
1600+
* anything, hence return None directly. */
1601+
if (srect->w == 0 || srect->h == 0) {
1602+
Py_RETURN_NONE;
1603+
}
1604+
1605+
OPTIMIZED_COLLIDERECT_SETUP;
1606+
15521607
size = PySequence_Length(list);
15531608
if (size == -1) {
15541609
return NULL;
15551610
}
15561611

15571612
for (loop = 0; loop < size; ++loop) {
1558-
obj = PySequence_GetItem(list, loop);
1613+
obj = PySequence_ITEM(list, loop);
15591614

15601615
if (!obj) {
15611616
return NULL;
@@ -1567,7 +1622,7 @@ RectExport_collideobjects(RectObject *self, PyObject *args, PyObject *kwargs)
15671622
return NULL;
15681623
}
15691624

1570-
if (_pg_do_rects_intersect(&self->r, argrect)) {
1625+
if (OPTIMIZED_COLLIDERECT(argrect)) {
15711626
return obj;
15721627
}
15731628
Py_DECREF(obj);
@@ -1579,7 +1634,7 @@ RectExport_collideobjects(RectObject *self, PyObject *args, PyObject *kwargs)
15791634
static PyObject *
15801635
RectExport_collidedict(RectObject *self, PyObject *args, PyObject *kwargs)
15811636
{
1582-
InnerRect *argrect, temp;
1637+
InnerRect *argrect, temp, *srect = &self->r;
15831638
Py_ssize_t loop = 0;
15841639
Py_ssize_t values = 0; /* Defaults to expecting keys as rects. */
15851640
PyObject *dict, *key, *val;
@@ -1596,6 +1651,14 @@ RectExport_collidedict(RectObject *self, PyObject *args, PyObject *kwargs)
15961651
return RAISE(PyExc_TypeError, "first argument must be a dict");
15971652
}
15981653

1654+
/* If the calling rect has 0 width or height, it cannot collide with
1655+
* anything, hence return None directly. */
1656+
if (srect->w == 0 || srect->h == 0) {
1657+
Py_RETURN_NONE;
1658+
}
1659+
1660+
OPTIMIZED_COLLIDERECT_SETUP;
1661+
15991662
while (PyDict_Next(dict, &loop, &key, &val)) {
16001663
if (values) {
16011664
if (!(argrect = RectFromObject(val, &temp))) {
@@ -1609,8 +1672,8 @@ RectExport_collidedict(RectObject *self, PyObject *args, PyObject *kwargs)
16091672
}
16101673
}
16111674

1612-
if (_pg_do_rects_intersect(&self->r, argrect)) {
1613-
ret = Py_BuildValue("(OO)", key, val);
1675+
if (OPTIMIZED_COLLIDERECT(argrect)) {
1676+
ret = PyTuple_Pack(2, key, val);
16141677
break;
16151678
}
16161679
}
@@ -1624,7 +1687,7 @@ RectExport_collidedict(RectObject *self, PyObject *args, PyObject *kwargs)
16241687
static PyObject *
16251688
RectExport_collidedictall(RectObject *self, PyObject *args, PyObject *kwargs)
16261689
{
1627-
InnerRect *argrect, temp;
1690+
InnerRect *argrect, temp, *srect = &self->r;
16281691
Py_ssize_t loop = 0;
16291692
Py_ssize_t values = 0; /* Defaults to expecting keys as rects. */
16301693
PyObject *dict, *key, *val;
@@ -1645,6 +1708,14 @@ RectExport_collidedictall(RectObject *self, PyObject *args, PyObject *kwargs)
16451708
if (!ret)
16461709
return NULL;
16471710

1711+
/* If the calling rect has 0 width or height, it cannot collide with
1712+
* anything, hence return an empty list directly. */
1713+
if (srect->w == 0 || srect->h == 0) {
1714+
return ret;
1715+
}
1716+
1717+
OPTIMIZED_COLLIDERECT_SETUP;
1718+
16481719
while (PyDict_Next(dict, &loop, &key, &val)) {
16491720
if (values) {
16501721
if (!(argrect = RectFromObject(val, &temp))) {
@@ -1660,8 +1731,8 @@ RectExport_collidedictall(RectObject *self, PyObject *args, PyObject *kwargs)
16601731
}
16611732
}
16621733

1663-
if (_pg_do_rects_intersect(&self->r, argrect)) {
1664-
PyObject *num = Py_BuildValue("(OO)", key, val);
1734+
if (OPTIMIZED_COLLIDERECT(argrect)) {
1735+
PyObject *num = PyTuple_Pack(2, key, val);
16651736
if (!num) {
16661737
Py_DECREF(ret);
16671738
return NULL;

0 commit comments

Comments
 (0)