Skip to content

Commit 1bfc29a

Browse files
authored
added is_line, is_circle, is_polygon (#193)
1 parent 86094e9 commit 1bfc29a

File tree

4 files changed

+206
-1
lines changed

4 files changed

+206
-1
lines changed

docs/geometry.rst

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,60 @@ such as raycasting and general utility functions.
223223

224224
.. ## geometry.rect_to_polygon ##
225225
226+
.. method:: is_line
227+
228+
| :sl:`Checks if the given object is a geometry.Line`
229+
| :sg:`is_line(obj) -> bool`
230+
231+
This function checks if the given object is a geometry.Line.
232+
It returns True if the object is a geometry.Line, and False otherwise.
233+
234+
.. note::
235+
236+
If the python object subclasses the geometry.Line class, this function will
237+
return False. Note that this function is equivalent to isinstance(obj, Line).
238+
Using that isinstance check is better for typechecking with mypy, and more
239+
explicit - so it’s recommended to use that instead of is_line.
240+
Utilizing is_line can save an unwanted Line import.
241+
242+
.. ## geometry.is_line ##
243+
244+
.. method:: is_circle
245+
246+
| :sl:`Checks if the given object is a geometry.Circle`
247+
| :sg:`is_circle(obj) -> bool`
248+
249+
This function checks if the given object is a geometry.Circle.
250+
It returns True if the object is a geometry.Circle, and False otherwise.
251+
252+
.. note::
253+
254+
If the python object subclasses the geometry.Circle class, this function will
255+
return False. Note that this function is equivalent to isinstance(obj, Circle).
256+
Using that isinstance check is better for typechecking with mypy, and more
257+
explicit - so it’s recommended to use that instead of is_circle.
258+
Utilizing is_circle can save an unwanted Circle import.
259+
260+
.. ## geometry.is_circle ##
261+
262+
.. method:: is_polygon
263+
264+
| :sl:`Checks if the given object is a geometry.Polygon`
265+
| :sg:`is_polygon(obj) -> bool`
266+
267+
This function checks if the given object is a geometry.Polygon.
268+
It returns True if the object is a geometry.Polygon, and False otherwise.
269+
270+
.. note::
271+
272+
If the python object subclasses the geometry.Polygon class, this function will
273+
return False. Note that this function is equivalent to isinstance(obj, Polygon).
274+
Using that isinstance check is better for typechecking with mypy, and more
275+
explicit - so it’s recommended to use that instead of is_polygon.
276+
Utilizing is_polygon can save an unwanted Polygon import.
277+
278+
.. ## geometry.is_polygon ##
279+
226280
.. method:: multiraycast
227281

228282
| :sl:`Returns a list of intersection points between a sequence of rays and a sequence of colliders`
@@ -245,4 +299,4 @@ such as raycasting and general utility functions.
245299
The function returns a list of tuples containing the closest intersection point to
246300
the ray's origin, or None if it couldn't find one.
247301

248-
.. ## geometry.multiraycast ##
302+
.. ## geometry.multiraycast ##

geometry.pyi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,3 +263,6 @@ def multiraycast(
263263
colliders: Sequence[Union[Rect, Circle, Line]],
264264
) -> Sequence[Optional[Tuple[float, float]]]: ...
265265
def rect_to_polygon(rect: Rect) -> Polygon: ...
266+
def is_line(obj) -> bool: ...
267+
def is_circle(obj) -> bool: ...
268+
def is_polygon(obj) -> bool: ...

src_c/geometry.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,12 +429,42 @@ geometry_rect_to_polygon(PyObject *_null, PyObject *arg)
429429
tmp->y + tmp->h / 2);
430430
}
431431

432+
static PyObject *
433+
geometry_is_line(PyObject *_null, PyObject *arg)
434+
{
435+
if (pgLine_Check(arg)) {
436+
Py_RETURN_TRUE;
437+
}
438+
Py_RETURN_FALSE;
439+
}
440+
441+
static PyObject *
442+
geometry_is_circle(PyObject *_null, PyObject *arg)
443+
{
444+
if (pgCircle_Check(arg)) {
445+
Py_RETURN_TRUE;
446+
}
447+
Py_RETURN_FALSE;
448+
}
449+
450+
static PyObject *
451+
geometry_is_polygon(PyObject *_null, PyObject *arg)
452+
{
453+
if (pgPolygon_Check(arg)) {
454+
Py_RETURN_TRUE;
455+
}
456+
Py_RETURN_FALSE;
457+
}
458+
432459
static PyMethodDef _pg_module_methods[] = {
433460
{"regular_polygon", (PyCFunction)geometry_regular_polygon, METH_FASTCALL,
434461
NULL},
435462
{"multiraycast", (PyCFunction)geometry_multiraycast, METH_FASTCALL, NULL},
436463
{"raycast", (PyCFunction)pg_raycast, METH_FASTCALL, NULL},
437464
{"rect_to_polygon", (PyCFunction)geometry_rect_to_polygon, METH_O, NULL},
465+
{"is_line", (PyCFunction)geometry_is_line, METH_O, NULL},
466+
{"is_circle", (PyCFunction)geometry_is_circle, METH_O, NULL},
467+
{"is_polygon", (PyCFunction)geometry_is_polygon, METH_O, NULL},
438468
{NULL, NULL, 0, NULL}};
439469

440470
MODINIT_DEFINE(geometry)

test/test_geometry.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import unittest
22
import pygame
33
import geometry
4+
from geometry import Line, Circle, Polygon, is_line, is_circle, is_polygon
5+
from pygame import Rect
46

57

68
def _get_vertices_from_rect(rect: pygame.Rect):
@@ -76,6 +78,122 @@ def test_rect_to_polygon_invalid_arg_type(self):
7678
with self.assertRaises(TypeError):
7779
geometry.rect_to_polygon(invalid_rect)
7880

81+
def test_is_line(self):
82+
"""Test is_line function"""
83+
84+
class TestLine(Line):
85+
pass
86+
87+
items = [
88+
(Line(0, 0, 1, 1), True),
89+
(Circle(0, 0, 1), False),
90+
(Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]), False),
91+
(Rect(0, 0, 1, 1), False),
92+
(TestLine(0, 0, 1, 1), False),
93+
(1, False),
94+
(1.0, False),
95+
(True, False),
96+
(False, False),
97+
(None, False),
98+
("", False),
99+
("string", False),
100+
([], False),
101+
([1], False),
102+
((1,), False),
103+
({}, False),
104+
]
105+
for obj, expected in items:
106+
self.assertEqual(expected, is_line(obj))
107+
108+
# Check that the function doesn't modify the line
109+
# =================================================
110+
l = Line(0, 0, 1, 1)
111+
start, end = l.a, l.b
112+
113+
self.assertTrue(is_line(l))
114+
self.assertEqual(start, l.a)
115+
self.assertEqual(end, l.b)
116+
# =================================================
117+
118+
def test_is_circle(self):
119+
"""Test is_circle function"""
120+
121+
class TestCircle(Circle):
122+
pass
123+
124+
items = [
125+
(Line(0, 0, 1, 1), False),
126+
(Circle(0, 0, 1), True),
127+
(Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]), False),
128+
(Rect(0, 0, 1, 1), False),
129+
(TestCircle(0, 0, 1), False),
130+
(1, False),
131+
(1.0, False),
132+
(True, False),
133+
(False, False),
134+
(None, False),
135+
("", False),
136+
("string", False),
137+
([], False),
138+
([1], False),
139+
((1,), False),
140+
({}, False),
141+
]
142+
for obj, expected in items:
143+
self.assertEqual(expected, is_circle(obj))
144+
145+
# Check that the function doesn't modify the circle
146+
# =================================================
147+
c = Circle(0, 0, 1)
148+
center = c.center
149+
radius = c.r
150+
151+
self.assertTrue(is_circle(c))
152+
self.assertEqual(center, c.center)
153+
self.assertEqual(radius, c.r)
154+
# =================================================
155+
156+
def test_is_polygon(self):
157+
"""Test is_polygon function"""
158+
159+
class TestPolygon(Polygon):
160+
pass
161+
162+
items = [
163+
(Line(0, 0, 1, 1), False),
164+
(Circle(0, 0, 1), False),
165+
(Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]), True),
166+
(Rect(0, 0, 1, 1), False),
167+
(TestPolygon([(0, 0), (1, 0), (1, 1), (0, 1)]), False),
168+
(1, False),
169+
(1.0, False),
170+
(True, False),
171+
(False, False),
172+
(None, False),
173+
("", False),
174+
("string", False),
175+
([], False),
176+
([1], False),
177+
((1,), False),
178+
({}, False),
179+
]
180+
for obj, expected in items:
181+
self.assertEqual(expected, is_polygon(obj))
182+
183+
# Check that the function doesn't modify the polygon
184+
# =================================================
185+
p = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
186+
verts = p.vertices
187+
verts_num = p.verts_num
188+
cx, cy = p.center
189+
190+
self.assertTrue(is_polygon(p))
191+
self.assertEqual(verts, p.vertices)
192+
self.assertEqual(verts_num, p.verts_num)
193+
self.assertEqual(cx, p.c_x)
194+
self.assertEqual(cy, p.c_y)
195+
# =================================================
196+
79197

80198
if __name__ == "__main__":
81199
unittest.main()

0 commit comments

Comments
 (0)