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

Line collidelist() / collidelistall() #220

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions docs/geometry.rst
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ other objects.

collideswith: Checks if the line collides with the given object.

collidelist: Checks if the line collides with any of the given objects.

collidelistall: Checks if the line collides with all of the given objects.

as_circle: Returns a circle which fully encloses the line.

as_rect: Returns the smallest rectangle that contains the line.
Expand Down
46 changes: 46 additions & 0 deletions docs/line.rst
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,52 @@ Line Methods

.. ## Line.collideswith ##

.. method:: collidelist

| :sl:`test if a list of objects collide with the line`
| :sg:`collidelist(colliders) -> int`

The `collidelist` method tests whether a given list of shapes or points collides
(overlaps) with this `Line` 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 `Line` 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.

.. ## Line.collidelist ##

.. method:: collidelistall

| :sl:`test if all objects in a list collide with the line`
| :sg:`collidelistall(colliders) -> list`

The `collidelistall` method tests whether a given list of shapes or points collides
(overlaps) with this `Line` 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 `Line` 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.

.. ## Line.collidelistall ##


.. method:: as_circle

Expand Down
2 changes: 2 additions & 0 deletions geometry.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ class Line(Sequence[float]):
@overload
def collidecircle(self, x: float, y: float, r: float) -> bool: ...
def collidepolygon(self, polygon: Polygon, only_edges: bool = False) -> bool: ...
def collidelist(self, colliders: Sequence[_CanBeCollided]) -> int: ...
def collidelistall(self, colliders: Sequence[_CanBeCollided]) -> List[int]: ...
def as_circle(self) -> Circle: ...
def as_rect(self) -> Rect: ...
@overload
Expand Down
173 changes: 158 additions & 15 deletions src_c/line.c
Original file line number Diff line number Diff line change
Expand Up @@ -393,41 +393,182 @@ pg_line_is_perpendicular(pgLineObject *self, PyObject *const *args,
return PyBool_FromLong(dot == 0);
}

static PyObject *
pg_line_collideswith(pgLineObject *self, PyObject *arg)
static int
_pg_line_collideswith(pgLineBase *sline, PyObject *arg)
{
int result = 0;
if (pgLine_Check(arg)) {
result = pgCollision_LineLine(&self->line, &pgLine_AsLine(arg));
return pgCollision_LineLine(sline, &pgLine_AsLine(arg));
}
else if (pgRect_Check(arg)) {
result = pgCollision_RectLine(&pgRect_AsRect(arg), &self->line);
return pgCollision_RectLine(&pgRect_AsRect(arg), sline);
}
else if (pgCircle_Check(arg)) {
result = pgCollision_LineCircle(&self->line, &pgCircle_AsCircle(arg));
return pgCollision_LineCircle(sline, &pgCircle_AsCircle(arg));
}
else if (pgPolygon_Check(arg)) {
result =
pgCollision_PolygonLine(&pgPolygon_AsPolygon(arg), &self->line, 0);
return pgCollision_PolygonLine(&pgPolygon_AsPolygon(arg), sline, 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_LinePoint(&self->line, x, y);
}
else {
return RAISE(PyExc_TypeError,
"Invalid shape argument, must be a CircleType, RectType, "
"LineType, PolygonType or a sequence of 2 numbers");
return pgCollision_LinePoint(sline, x, y);
}

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_line_collideswith(pgLineObject *self, PyObject *arg)
{
int result = _pg_line_collideswith(&self->line, arg);
if (result == -1) {
return NULL;
}
return PyBool_FromLong(result);
}

static PyObject *
pg_line_collidelist(pgLineObject *self, PyObject *arg)
{
Py_ssize_t i;
pgLineBase *sline = &self->line;
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++) {
if ((colliding = _pg_line_collideswith(sline, items[i])) == -1) {
/*invalid shape*/
return NULL;
}

if (colliding) {
return PyLong_FromSsize_t(i);
}
}
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_line_collideswith(sline, 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_line_collidelistall(pgLineObject *self, PyObject *arg)
{
PyObject *ret, **items;
Py_ssize_t i;
pgLineBase *sline = &self->line;
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++) {
if ((colliding = _pg_line_collideswith(sline, items[i])) == -1) {
/*invalid shape*/
Py_DECREF(ret);
return NULL;
}

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;
}

/* general sequence path */
for (i = 0; i < PySequence_Length(arg); i++) {
PyObject *obj = PySequence_GetItem(arg, i);
if (!obj) {
Py_DECREF(ret);
return NULL;
}

if ((colliding = _pg_line_collideswith(sline, obj)) == -1) {
/*invalid shape*/
Py_DECREF(obj);
Py_DECREF(ret);
return NULL;
}
Py_DECREF(obj);

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;
}

static PyObject *
pg_line_move(pgLineObject *self, PyObject *const *args, Py_ssize_t nargs)
{
Expand Down Expand Up @@ -758,6 +899,8 @@ static struct PyMethodDef pg_line_methods[] = {
{"collideswith", (PyCFunction)pg_line_collideswith, METH_O, NULL},
{"collidepolygon", (PyCFunction)pg_line_collidepolygon, METH_FASTCALL,
NULL},
{"collidelist", (PyCFunction)pg_line_collidelist, METH_O, NULL},
{"collidelistall", (PyCFunction)pg_line_collidelistall, METH_O, NULL},
{"as_rect", (PyCFunction)pg_line_as_rect, METH_NOARGS, NULL},
{"update", (PyCFunction)pg_line_update, METH_FASTCALL, NULL},
{"move", (PyCFunction)pg_line_move, METH_FASTCALL, NULL},
Expand Down
Loading