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 Circle as_rect()/as_frect() #2634

Merged
merged 6 commits into from
Feb 12, 2024
Merged
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
3 changes: 3 additions & 0 deletions buildconfig/stubs/pygame/geometry.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ from typing import (
Sequence,
)

from pygame import Rect, FRect
from ._common import Coordinate, RectValue

_CanBeCircle = Union[Circle, Tuple[Coordinate, float], Sequence[float]]
Expand Down Expand Up @@ -95,5 +96,7 @@ class Circle:
def update(self, x: float, y: float, r: float, /) -> None: ...
@overload
def update(self, center: Coordinate, r: float, /) -> None: ...
def as_rect(self) -> Rect: ...
def as_frect(self) -> FRect: ...
def __copy__(self) -> Circle: ...
copy = __copy__
42 changes: 42 additions & 0 deletions docs/reST/ref/geometry.rst
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,48 @@

.. ## Circle.update ##

.. method:: as_rect

| :sl:`returns the smallest pygame.Rect object that contains the circle`
| :sg:`as_rect() -> Rect`

The `as_rect` method returns a `pygame.Rect` object that represents the smallest
rectangle that completely contains the `Circle` object. This means that the `Rect`
object returned by as_rect will have dimensions such that it completely encloses
the `Circle`, with no part of the `Circle` extending outside of the `Rect`.

.. note::
This method is equivalent(behaviour wise) to the following code:

.. code-block:: python

Rect(circle.x - circle.r, circle.y - circle.r, circle.r * 2, circle.r * 2)

.. versionadded:: 2.5.0

.. ## Circle.as_rect ##

.. method:: as_frect

| :sl:`returns the smallest pygame.FRect object that contains the circle`
| :sg:`as_frect() -> FRect`

The `as_frect` method returns a `pygame.FRect` object that represents the smallest
rectangle that completely contains the `Circle` object. This means that the `FRect`
object returned by as_rect will have dimensions such that it completely encloses
the `Circle`, with no part of the `Circle` extending outside of the `FRect`.

.. note::
This method is equivalent(behaviour wise) to the following code:

.. code-block:: python

FRect(circle.x - circle.r, circle.y - circle.r, circle.r * 2, circle.r * 2)

.. versionadded:: 2.5.0

.. ## Circle.as_frect ##

Copy link
Member

Choose a reason for hiding this comment

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

From now on, using versionadded tags may be a good idea. Both these functions need a versionadded 2.5.0 tag.

Actually now that I see this, the entire module is missing it. Maybe one note at the top of this doc saying the module was added in 2.4.0 should be added?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, I'll add the versionadded for these methods in this PR and all the others in a separate one.

.. method:: copy

| :sl:`returns a copy of the circle`
Expand Down
26 changes: 26 additions & 0 deletions src_c/circle.c
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,28 @@ pg_circle_colliderect(pgCircleObject *self, PyObject *const *args,
return PyBool_FromLong(pgCollision_RectCircle(x, y, w, h, &self->circle));
}

static PyObject *
pg_circle_as_rect(pgCircleObject *self, PyObject *_null)
{
pgCircleBase *scirc = &self->circle;
int diameter = (int)(scirc->r * 2.0);
int x = (int)(scirc->x - scirc->r);
int y = (int)(scirc->y - scirc->r);

return pgRect_New4(x, y, diameter, diameter);
}

static PyObject *
pg_circle_as_frect(pgCircleObject *self, PyObject *_null)
{
pgCircleBase *scirc = &self->circle;
double diameter = scirc->r * 2.0;
double x = scirc->x - scirc->r;
double y = scirc->y - scirc->r;

return pgFRect_New4((float)x, (float)y, (float)diameter, (float)diameter);
}

static struct PyMethodDef pg_circle_methods[] = {
{"collidepoint", (PyCFunction)pg_circle_collidepoint, METH_FASTCALL,
DOC_CIRCLE_COLLIDEPOINT},
Expand All @@ -400,6 +422,10 @@ static struct PyMethodDef pg_circle_methods[] = {
DOC_CIRCLE_COLLIDERECT},
{"update", (PyCFunction)pg_circle_update, METH_FASTCALL,
DOC_CIRCLE_UPDATE},
{"as_rect", (PyCFunction)pg_circle_as_rect, METH_NOARGS,
DOC_CIRCLE_ASRECT},
{"as_frect", (PyCFunction)pg_circle_as_frect, METH_NOARGS,
DOC_CIRCLE_ASFRECT},
{"__copy__", (PyCFunction)pg_circle_copy, METH_NOARGS, DOC_CIRCLE_COPY},
{"copy", (PyCFunction)pg_circle_copy, METH_NOARGS, DOC_CIRCLE_COPY},
{NULL, NULL, 0, NULL}};
Expand Down
2 changes: 2 additions & 0 deletions src_c/doc/geometry_doc.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@
#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"
#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"
#define DOC_CIRCLE_UPDATE "update((x, y), radius, /) -> None\nupdate(x, y, radius, /) -> None\nupdates the circle position and radius"
#define DOC_CIRCLE_ASRECT "as_rect() -> Rect\nreturns the smallest pygame.Rect object that contains the circle"
#define DOC_CIRCLE_ASFRECT "as_frect() -> FRect\nreturns the smallest pygame.FRect object that contains the circle"
#define DOC_CIRCLE_COPY "copy() -> Circle\nreturns a copy of the circle"
26 changes: 26 additions & 0 deletions test/geometry_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,32 @@ def test_selfupdate(self):
self.assertEqual(c.r, c_r)
self.assertEqual(c.r_sqr, c_r_sqr)

def test_as_rect_invalid_args(self):
c = Circle(0, 0, 10)

invalid_args = [None, [], "1", (1,), Vector2(1, 1), 1]

with self.assertRaises(TypeError):
for arg in invalid_args:
c.as_rect(arg)

def test_as_rect(self):
c = Circle(0, 0, 10)
self.assertEqual(c.as_rect(), Rect(-10, -10, 20, 20))

def test_as_frect_invalid_args(self):
c = Circle(0, 0, 10)

invalid_args = [None, [], "1", (1,), Vector2(1, 1), 1]

with self.assertRaises(TypeError):
for arg in invalid_args:
c.as_frect(arg)

def test_as_frect(self):
c = Circle(0, 0, 10)
self.assertEqual(c.as_frect(), FRect(-10, -10, 20, 20))

def test_circle_richcompare(self):
"""Ensures that the circle correctly compares itself to other circles"""
c = Circle(0, 0, 10)
Expand Down
Loading