Skip to content

Commit

Permalink
implemented geometry.regular_polygon (#73)
Browse files Browse the repository at this point in the history
* implemented `Polygon.normal_polygon`

* added more tests for `Polygon.normal_polygon`

* formatted code and more tests

* handled some reviews

* more reviews...

* added `geometry.regular_polygon`

* moved regular_polygon implementation in geometry.c

* removed declaration of regular_polygon

* handled some reviews
  • Loading branch information
Emc2356 authored Sep 25, 2022
1 parent 19e06c9 commit 7105d34
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 1 deletion.
4 changes: 4 additions & 0 deletions geometry.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,7 @@ class Polygon:
def __init__(self, polygon: Polygon) -> None: ...
def __copy__(self) -> "Polygon": ...
copy = __copy__

def regular_polygon(
sides: int, center: Coordinate, radius: float, angle: float = 0
) -> Polygon: ...
68 changes: 67 additions & 1 deletion src_c/geometry.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,73 @@

#define PYGAMEAPI_GEOMETRY_NUMSLOTS 21

static PyMethodDef _pg_module_methods[] = {{NULL, NULL, 0, NULL}};
static PyObject *
geometry_regular_polygon(PyObject *_null, PyObject *const *args,
Py_ssize_t nargs)
{
int sides;
double radius;
double angle = 0;
double Cx, Cy;

if (nargs < 3 || nargs > 4) {
return RAISE(PyExc_TypeError,
"invalid number of arguments, expected 3 or 4 arguments");
}
sides = PyLong_AsLong(args[0]);
if (PyErr_Occurred()) {
return NULL;
}

if (sides < 3) {
if (sides < 0) {
return RAISE(PyExc_ValueError,
"the sides can not be a negative number");
}
return RAISE(PyExc_ValueError, "polygons need at least 3 sides");
}

if (!pg_TwoDoublesFromObj(args[1], &Cx, &Cy)) {
return RAISE(PyExc_TypeError,
"the second parameter must be a sequence of 2 numbers");
}

if (!pg_DoubleFromObj(args[2], &radius)) {
return RAISE(PyExc_TypeError, "the third parameter must be a number");
}
if (nargs == 4) {
if (!pg_DoubleFromObj(args[3], &angle)) {
return RAISE(PyExc_TypeError,
"the forth parameter must be a number");
}
angle *= PI / 180.0;
}

double *vertices = PyMem_New(double, sides * 2);
if (!vertices) {
return RAISE(PyExc_MemoryError,
"cannot allocate memory for the polygon vertices");
}

int loop;
double fac = TAU / sides;
for (loop = 0; loop < sides; loop++) {
double ang = angle + fac * loop;
vertices[loop * 2] = Cx + radius * cos(ang);
vertices[loop * 2 + 1] = Cy + radius * sin(ang);
}

PyObject *ret = pgPolygon_New2(vertices, sides);
PyMem_Free(vertices);

return ret;
}


static PyMethodDef _pg_module_methods[] = {
{"regular_polygon", (PyCFunction)geometry_regular_polygon, METH_FASTCALL,
NULL},
{NULL, NULL, 0, NULL}};

MODINIT_DEFINE(geometry)
{
Expand Down
65 changes: 65 additions & 0 deletions test/test_polygon.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

from pygame import Vector2, Vector3

import geometry
from geometry import Polygon

import math

p1 = (12.0, 12.0)
p2 = (32.0, 43.0)
p3 = (22.0, 4.0)
Expand Down Expand Up @@ -124,6 +127,68 @@ def test_copy_invalid_args(self):
with self.assertRaises(TypeError):
po.copy(*value)

def test_static_normal_polygon(self):
center = (150.5, 100.1)
radius = 50.2
sides = 10
angle = 20.6

polygon_pg = geometry.regular_polygon(sides, center, radius, angle)
vertices_pg = polygon_pg.vertices

vertices = []

for i in range(sides):
vertices.append(
(
center[0]
+ radius * math.cos(math.radians(angle) + math.pi * 2 * i / sides),
center[1]
+ radius * math.sin(math.radians(angle) + math.pi * 2 * i / sides),
)
)

self.assertEqual(vertices_pg, vertices)

invalid_types = [
None,
[],
"1",
"123",
(1,),
[1, 2, 3],
[p1, p2, p3, 32],
[p1, p2, "(1, 1)"],
]

for invalid_type in invalid_types + [(1, 2)]:
with self.assertRaises(TypeError):
geometry.regular_polygon(invalid_type, (1, 2.2), 5.5, 1)

for invalid_type in invalid_types:
with self.assertRaises(TypeError):
geometry.regular_polygon(5, invalid_type, 5.5, 1)

for invalid_type in invalid_types + [(1, 2)]:
with self.assertRaises(TypeError):
geometry.regular_polygon(5, (1, 2.2), invalid_type, 1)

for invalid_type in invalid_types + [(1, 2)]:
with self.assertRaises(TypeError):
geometry.regular_polygon(5, (1, 2.2), 5.5, invalid_type)

with self.assertRaises(TypeError):
geometry.regular_polygon(1, (1, 2.2), 5.5, 1, 5)

with self.assertRaises(TypeError):
geometry.regular_polygon()

with self.assertRaises(ValueError):
geometry.regular_polygon(-1, center, radius, angle)

with self.assertRaises(ValueError):
geometry.regular_polygon(2, center, radius, angle)

def test_copy_return_type(self):
"""Checks whether the copy method returns a polygon."""
po = Polygon([p1, p2, p3, p4])
Expand Down

0 comments on commit 7105d34

Please sign in to comment.