From 95b929f0a22b338e2c614ee5bea52c77612a79df Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Sun, 29 Sep 2024 11:50:22 +0200 Subject: [PATCH 01/11] Added a first implementation of the geometry.Line class. Co-authored-by: Emc2356 <63981925+emc2356@users.noreply.github.com> Co-authored-by: NovialRiptide <35881688+novialriptide@users.noreply.github.com> Co-authored-by: ScriptLineStudios Co-authored-by: Avaxar <44055981+avaxar@users.noreply.github.com> Co-authored-by: maqa41 --- buildconfig/stubs/pygame/geometry.pyi | 55 +++- docs/reST/ref/geometry.rst | 120 +++++++++ src_c/_pygame.h | 2 +- src_c/doc/geometry_doc.h | 8 + src_c/geometry.c | 13 + src_c/geometry.h | 22 ++ src_c/line.c | 317 ++++++++++++++++++++++ test/geometry_test.py | 369 +++++++++++++++++++++++++- 8 files changed, 903 insertions(+), 3 deletions(-) create mode 100644 src_c/line.c diff --git a/buildconfig/stubs/pygame/geometry.pyi b/buildconfig/stubs/pygame/geometry.pyi index 14ab6563b0..d006463215 100644 --- a/buildconfig/stubs/pygame/geometry.pyi +++ b/buildconfig/stubs/pygame/geometry.pyi @@ -4,7 +4,7 @@ from typing import ( Callable, Protocol, Tuple, - List, + List, Sequence, ) from pygame import Rect, FRect @@ -13,13 +13,30 @@ from .rect import Rect, FRect from .math import Vector2 _CanBeCircle = Union[Circle, Tuple[Coordinate, float], SequenceLike[float]] +_CanBeLine = Union[ + Rect, + Line, + Tuple[float, float, float, float], + Tuple[Coordinate, Coordinate], + Coordinate, + Sequence[Coordinate], +] + class _HasCirclettribute(Protocol): # An object that has a circle attribute that is either a circle, or a function # that returns a circle circle: Union[_CanBeCircle, Callable[[], _CanBeCircle]] +class _HasLineAttribute(Protocol): + # An object that has a line attribute that is either a line, or a function + # that returns a line + line: Union[_CanBeLine, Callable[[], _CanBeLine]] + + _CircleValue = Union[_CanBeCircle, _HasCirclettribute] +_LineValue = Union[_CanBeLine, _HasLineAttribute] + _CanBeCollided = Union[Circle, Rect, FRect, Coordinate, Vector2] _CanBeIntersected = Union[Circle] @@ -115,3 +132,39 @@ class Circle: def as_frect(self) -> FRect: ... def __copy__(self) -> Circle: ... copy = __copy__ + +class Line: + @property + def xa(self) -> float: ... + @xa.setter + def xa(self, value: float) -> None: ... + @property + def ya(self) -> float: ... + @ya.setter + def ya(self, value: float) -> None: ... + @property + def xb(self) -> float: ... + @xb.setter + def xb(self, value: float) -> None: ... + @property + def yb(self) -> float: ... + @yb.setter + def yb(self, value: float) -> None: ... + @property + def a(self) -> Tuple[float, float]: ... + @a.setter + def a(self, value: Coordinate) -> None: ... + @property + def b(self) -> Tuple[float, float]: ... + @b.setter + def b(self, value: Coordinate) -> None: ... + @overload + def __init__(self, line: Line) -> None: ... + @overload + def __init__(self, xa: float, ya: float, xb: float, yb: float) -> None: ... + @overload + def __init__(self, first: Coordinate, second: Coordinate) -> None: ... + @overload + def __init__(self, single_arg: _LineValue) -> None: ... + def __copy__(self) -> Line: ... + copy = __copy__ diff --git a/docs/reST/ref/geometry.rst b/docs/reST/ref/geometry.rst index a7fc79e2f2..2029a6626a 100644 --- a/docs/reST/ref/geometry.rst +++ b/docs/reST/ref/geometry.rst @@ -393,3 +393,123 @@ .. ## Circle.copy ## .. ## pygame.Circle ## + + +.. class:: Line + + | :sl:`pygame object for representing a line` + | :sg:`Line((xa, ya), (xb, yb)) -> Line` + | :sg:`Line(xa, ya, xb, yb) -> Line` + + .. versionadded:: 2.5.2 + + The `Line` class provides many useful methods for collision testing, transformation and intersection. + A `Line` can be created from a combination of two pairs of coordinates that represent the start and end points. + Lines can also be created from python objects that are already a `Line` (effectively copying the line) or have an attribute named "line". + + Specifically, to construct a line you can pass the xa, ya, xb, and yb values as separate arguments or inside a sequence(list or tuple). + + Functions that require a `Line` argument may also accept these values as Lines: + + :: + + ((xa, ya), (xb, yb)) + (xa, ya, xb, yb) + (vector2, vector2) + + The `Line` class only stores the xa, ya, xb, and yb attributes, everything else is calculated + on the fly based on them. + + **You cannot create degenerate Lines(lines with the same start and end point). If you + try, the `Line` will not be created and an error will be raised.** + + **Line Attributes** + + ---- + + .. attribute:: xa + + | :sl:`x coordinate of the start point of the line` + | :sg:`xa -> float` + + The horizontal coordinate of the start point of the line. Reassigning it moves the line. + + .. versionadded:: 2.5.2 + + .. ## Line.xa ## + + .. attribute:: ya + + | :sl:`y coordinate of the start point of the line` + | :sg:`ya -> float` + + The vertical coordinate of the start point of the line. Reassigning it moves the line. + + .. versionadded:: 2.5.2 + + .. ## Line.ya ## + + .. attribute:: xb + + | :sl:`x coordinate of the end point of the line` + | :sg:`xb -> float` + + The horizontal coordinate of the end point of the line. Reassigning it moves the line. + + .. versionadded:: 2.5.2 + + .. ## Line.xb ## + + .. attribute:: yb + + | :sl:`y coordinate of the end point of the line` + | :sg:`yb -> float` + + The vertical coordinate of the end point of the line. Reassigning it moves the line. + + .. versionadded:: 2.5.2 + + .. ## Line.yb ## + + .. attribute:: a + + | :sl:`the first point of the line` + | :sg:`a -> (float, float)` + + It's a tuple containing the `xa` and `ya` attributes representing the line's first point. + It can be reassigned to move the `Line`. If reassigned the `xa` and `ya` attributes + will be changed to produce a `Line` with matching first point position. + The `xb` and `yb` attributes will not be affected. + + .. versionadded:: 2.5.2 + + .. ## Line.a ## + + .. attribute:: b + + | :sl:`the second point of the line` + | :sg:`b -> (float, float)` + + It's a tuple containing `xb` and `yb` attributes representing the line's second point. + It can be reassigned to move the `Line`. If reassigned the `xb` and `yb` attributes + will be changed to produce a `Line` with matching second point position. + The `xa` and `ya` attributes will not be affected. + + .. versionadded:: 2.5.2 + + .. ## Line.b ## + + **Line Methods** + + ---- + + .. method:: copy + + | :sl:`copies the line` + | :sg:`copy() -> Line` + + Returns a copy of this `Line`. + + .. versionadded:: 2.5.2 + + .. ## Line.copy ## diff --git a/src_c/_pygame.h b/src_c/_pygame.h index e87986d776..26a212fe74 100644 --- a/src_c/_pygame.h +++ b/src_c/_pygame.h @@ -536,6 +536,6 @@ typedef enum { #define PYGAMEAPI_BASE_NUMSLOTS 29 #define PYGAMEAPI_EVENT_NUMSLOTS 10 #define PYGAMEAPI_WINDOW_NUMSLOTS 1 -#define PYGAMEAPI_GEOMETRY_NUMSLOTS 1 +#define PYGAMEAPI_GEOMETRY_NUMSLOTS 2 #endif /* _PYGAME_INTERNAL_H */ diff --git a/src_c/doc/geometry_doc.h b/src_c/doc/geometry_doc.h index 89a7ef6f3d..9467b4c193 100644 --- a/src_c/doc/geometry_doc.h +++ b/src_c/doc/geometry_doc.h @@ -23,3 +23,11 @@ #define DOC_CIRCLE_ASRECT "as_rect() -> Rect\nreturns the smallest Rect containing the circle" #define DOC_CIRCLE_ASFRECT "as_frect() -> FRect\nreturns the smallest FRect containing the circle" #define DOC_CIRCLE_COPY "copy() -> Circle\ncopies the circle" +#define DOC_LINE "Line((xa, ya), (xb, yb)) -> Line\nLine(xa, ya, xb, yb) -> Line\npygame object for representing a line" +#define DOC_LINE_XA "xa -> float\nx coordinate of the start point of the line" +#define DOC_LINE_YA "ya -> float\ny coordinate of the start point of the line" +#define DOC_LINE_XB "xb -> float\nx coordinate of the end point of the line" +#define DOC_LINE_YB "yb -> float\ny coordinate of the end point of the line" +#define DOC_LINE_A "a -> (float, float)\nthe first point of the line" +#define DOC_LINE_B "b -> (float, float)\nthe second point of the line" +#define DOC_LINE_COPY "copy() -> Line\ncopies the line" diff --git a/src_c/geometry.c b/src_c/geometry.c index 4ccb0ad22d..d55eeb7504 100644 --- a/src_c/geometry.c +++ b/src_c/geometry.c @@ -1,4 +1,5 @@ #include "circle.c" +#include "line.c" #include "geometry_common.c" static PyMethodDef geometry_methods[] = {{NULL, NULL, 0, NULL}}; @@ -30,6 +31,10 @@ MODINIT_DEFINE(geometry) return NULL; } + if (PyType_Ready(&pgLine_Type) < 0) { + return NULL; + } + module = PyModule_Create(&_module); if (!module) { return NULL; @@ -42,7 +47,15 @@ MODINIT_DEFINE(geometry) return NULL; } + Py_INCREF(&pgLine_Type); + if (PyModule_AddObject(module, "Line", (PyObject *)&pgLine_Type)) { + Py_DECREF(&pgLine_Type); + Py_DECREF(module); + return NULL; + } + c_api[0] = &pgCircle_Type; + c_api[1] = &pgLine_Type; apiobj = encapsulate_api(c_api, "geometry"); if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj)) { Py_XDECREF(apiobj); diff --git a/src_c/geometry.h b/src_c/geometry.h index aa9cae6ca1..640dc19355 100644 --- a/src_c/geometry.h +++ b/src_c/geometry.h @@ -20,7 +20,29 @@ typedef struct { #define pgCircle_GETR(self) (pgCircle_CAST(self)->circle.r) #define pgCircle_Check(o) ((o)->ob_type == &pgCircle_Type) +typedef struct { + double xa, ya; + double xb, yb; +} pgLineBase; + +typedef struct { + PyObject_HEAD pgLineBase line; + PyObject *weakreflist; +} pgLineObject; + +#define pgLine_CAST(o) ((pgLineObject *)(o)) + +#define pgLine_GETLINE(o) (pgLine_CAST(o)->line) +#define pgLine_AsLine(o) (pgLine_CAST(o)->line) +#define pgLine_GETX1(self) (pgLine_CAST(self)->line.xa) +#define pgLine_GETY1(self) (pgLine_CAST(self)->line.ya) +#define pgLine_GETX2(self) (pgLine_CAST(self)->line.xb) +#define pgLine_GETY2(self) (pgLine_CAST(self)->line.yb) +#define pgLine_Check(o) ((o)->ob_type == &pgLine_Type) + static PyTypeObject pgCircle_Type; +static PyTypeObject pgLine_Type; + /* Constants */ /* PI */ diff --git a/src_c/line.c b/src_c/line.c new file mode 100644 index 0000000000..98f0d6d82f --- /dev/null +++ b/src_c/line.c @@ -0,0 +1,317 @@ +#include "doc/geometry_doc.h" +#include "geometry_common.h" + +#define IS_LINE_VALID(line) (line->xa != line->xb || line->ya != line->yb) + +static PyObject * +_pg_line_subtype_new4(PyTypeObject *type, double xa, double ya, double xb, + double yb) +{ + pgLineObject *line = (pgLineObject *)pgLine_Type.tp_new(type, NULL, NULL); + + if (line) { + line->line.xa = xa; + line->line.ya = ya; + line->line.xb = xb; + line->line.yb = yb; + } + return (PyObject *)line; +} + +static PyObject * +pg_line_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + pgLineObject *self = (pgLineObject *)type->tp_alloc(type, 0); + + if (self != NULL) + memset(&self->line, 0, sizeof(pgLineBase)); + + return (PyObject *)self; +} + +static void +pg_line_dealloc(pgLineObject *self) +{ + if (self->weakreflist != NULL) { + PyObject_ClearWeakRefs((PyObject *)self); + } + + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static int +pgLine_FromObject(PyObject *obj, pgLineBase *out) +{ + Py_ssize_t length; + + if (pgLine_Check(obj)) { + *out = ((pgLineObject *)obj)->line; + return 1; + } + if (pgSequenceFast_Check(obj)) { + length = PySequence_Fast_GET_SIZE(obj); + PyObject **farray = PySequence_Fast_ITEMS(obj); + + if (length == 4) { + if (!pg_DoubleFromObj(farray[0], &(out->xa)) || + !pg_DoubleFromObj(farray[1], &(out->ya)) || + !pg_DoubleFromObj(farray[2], &(out->xb)) || + !pg_DoubleFromObj(farray[3], &(out->yb))) { + return 0; + } + return IS_LINE_VALID(out); + } + else if (length == 2) { + if (!pg_TwoDoublesFromObj(farray[0], &(out->xa), &(out->ya)) || + !pg_TwoDoublesFromObj(farray[1], &(out->xb), &(out->yb))) { + PyErr_Clear(); + return 0; + } + return IS_LINE_VALID(out); + } + else if (length == 1) /*looks like an arg?*/ { + if (PyUnicode_Check(farray[0]) || + !pgLine_FromObject(farray[0], out)) { + return 0; + } + return IS_LINE_VALID(out); + } + } + if (PySequence_Check(obj)) { + length = PySequence_Length(obj); + if (length == 4) { + PyObject *tmp; + tmp = PySequence_GetItem(obj, 0); + if (!pg_DoubleFromObj(tmp, &(out->xa))) { + Py_DECREF(tmp); + return 0; + } + Py_DECREF(tmp); + tmp = PySequence_GetItem(obj, 1); + if (!pg_DoubleFromObj(tmp, &(out->ya))) { + Py_DECREF(tmp); + return 0; + } + Py_DECREF(tmp); + tmp = PySequence_GetItem(obj, 2); + if (!pg_DoubleFromObj(tmp, &(out->xb))) { + Py_DECREF(tmp); + return 0; + } + Py_DECREF(tmp); + tmp = PySequence_GetItem(obj, 3); + if (!pg_DoubleFromObj(tmp, &(out->yb))) { + Py_DECREF(tmp); + return 0; + } + Py_DECREF(tmp); + return IS_LINE_VALID(out); + } + else if (length == 2) { + PyObject *tmp; + tmp = PySequence_GetItem(obj, 0); + if (!pg_TwoDoublesFromObj(tmp, &(out->xa), &(out->ya))) { + Py_DECREF(tmp); + return 0; + } + Py_DECREF(tmp); + tmp = PySequence_GetItem(obj, 1); + if (!pg_TwoDoublesFromObj(tmp, &(out->xb), &(out->yb))) { + Py_DECREF(tmp); + return 0; + } + Py_DECREF(tmp); + return IS_LINE_VALID(out); + } + else if (PyTuple_Check(obj) && length == 1) /*looks like an arg?*/ { + PyObject *sub = PySequence_GetItem(obj, 0); + if (PyUnicode_Check(sub) || !pgLine_FromObject(sub, out)) { + Py_DECREF(sub); + return 0; + } + Py_DECREF(sub); + return IS_LINE_VALID(out); + } + else { + return 0; + } + } + if (PyObject_HasAttrString(obj, "line")) { + PyObject *lineattr; + lineattr = PyObject_GetAttrString(obj, "line"); + if (!lineattr) { + PyErr_Clear(); + return 0; + } + if (PyCallable_Check(lineattr)) /*call if it's a method*/ + { + PyObject *lineresult = PyObject_CallObject(lineattr, NULL); + Py_DECREF(lineattr); + if (!lineresult) { + PyErr_Clear(); + return 0; + } + lineattr = lineresult; + } + Py_DECREF(lineattr); + return pgLine_FromObject(lineattr, out); + } + return 0; +} + +static int +pg_line_init(pgLineObject *self, PyObject *args, PyObject *kwds) +{ + if (!pgLine_FromObject(args, &(self->line))) { + PyErr_SetString(PyExc_TypeError, + "Invalid line end points, expected 4 " + "numbers or 2 sequences of 2 numbers"); + return -1; + } + return 0; +} + +static PyObject * +pg_line_copy(pgLineObject *self, PyObject *_null) +{ + return _pg_line_subtype_new4(Py_TYPE(self), self->line.xa, self->line.ya, + self->line.xb, self->line.yb); +} + +static struct PyMethodDef pg_line_methods[] = { + {"__copy__", (PyCFunction)pg_line_copy, METH_NOARGS, DOC_LINE_COPY}, + {"copy", (PyCFunction)pg_line_copy, METH_NOARGS, DOC_LINE_COPY}, + {NULL, NULL, 0, NULL}}; + +static PyObject * +pg_line_repr(pgLineObject *self) +{ + PyObject *result, *xa, *ya, *xb, *yb; + + xa = PyFloat_FromDouble(self->line.xa); + if (!xa) { + return NULL; + } + ya = PyFloat_FromDouble(self->line.ya); + if (!ya) { + Py_DECREF(xa); + return NULL; + } + xb = PyFloat_FromDouble(self->line.xb); + if (!xb) { + Py_DECREF(xa); + Py_DECREF(ya); + return NULL; + } + yb = PyFloat_FromDouble(self->line.yb); + if (!yb) { + Py_DECREF(xa); + Py_DECREF(ya); + Py_DECREF(xb); + return NULL; + } + + result = + PyUnicode_FromFormat("", xa, ya, xb, yb); + + Py_DECREF(xa); + Py_DECREF(ya); + Py_DECREF(xb); + Py_DECREF(yb); + + return result; +} + +static PyObject * +pg_line_str(pgLineObject *self) +{ + return pg_line_repr(self); +} + +#define __LINE_GETSET_NAME(name) \ + static PyObject *pg_line_get##name(pgLineObject *self, void *closure) \ + { \ + return PyFloat_FromDouble(self->line.name); \ + } \ + static int pg_line_set##name(pgLineObject *self, PyObject *value, \ + void *closure) \ + { \ + double val; \ + DEL_ATTR_NOT_SUPPORTED_CHECK_NO_NAME(value); \ + if (pg_DoubleFromObj(value, &val)) { \ + self->line.name = val; \ + return 0; \ + } \ + PyErr_SetString(PyExc_TypeError, "Expected a number"); \ + return -1; \ + } + +__LINE_GETSET_NAME(xa) +__LINE_GETSET_NAME(ya) +__LINE_GETSET_NAME(xb) +__LINE_GETSET_NAME(yb) +#undef __LINE_GETSET_NAME + +static PyObject * +pg_line_geta(pgLineObject *self, void *closure) +{ + return pg_tuple_couple_from_values_double(self->line.xa, self->line.ya); +} + +static int +pg_line_seta(pgLineObject *self, PyObject *value, void *closure) +{ + double x, y; + DEL_ATTR_NOT_SUPPORTED_CHECK_NO_NAME(value); + if (pg_TwoDoublesFromObj(value, &x, &y)) { + self->line.xa = x; + self->line.ya = y; + return 0; + } + PyErr_SetString(PyExc_TypeError, "Expected a sequence of 2 numbers"); + return -1; +} + +static PyObject * +pg_line_getb(pgLineObject *self, void *closure) +{ + return pg_tuple_couple_from_values_double(self->line.xb, self->line.yb); +} + +static int +pg_line_setb(pgLineObject *self, PyObject *value, void *closure) +{ + double x, y; + DEL_ATTR_NOT_SUPPORTED_CHECK_NO_NAME(value); + if (pg_TwoDoublesFromObj(value, &x, &y)) { + self->line.xb = x; + self->line.yb = y; + return 0; + } + PyErr_SetString(PyExc_TypeError, "Expected a sequence of 2 numbers"); + return -1; +} + +static PyGetSetDef pg_line_getsets[] = { + {"xa", (getter)pg_line_getxa, (setter)pg_line_setxa, DOC_LINE_XA, NULL}, + {"ya", (getter)pg_line_getya, (setter)pg_line_setya, DOC_LINE_YA, NULL}, + {"xb", (getter)pg_line_getxb, (setter)pg_line_setxb, DOC_LINE_XB, NULL}, + {"yb", (getter)pg_line_getyb, (setter)pg_line_setyb, DOC_LINE_YB, NULL}, + {"a", (getter)pg_line_geta, (setter)pg_line_seta, DOC_LINE_A, NULL}, + {"b", (getter)pg_line_getb, (setter)pg_line_setb, DOC_LINE_B, NULL}, + {NULL, 0, NULL, NULL, NULL}}; + +static PyTypeObject pgLine_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "pygame.geometry.Line", + .tp_basicsize = sizeof(pgLineObject), + .tp_dealloc = (destructor)pg_line_dealloc, + .tp_repr = (reprfunc)pg_line_repr, + .tp_str = (reprfunc)pg_line_str, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = DOC_LINE, + .tp_weaklistoffset = offsetof(pgLineObject, weakreflist), + .tp_methods = pg_line_methods, + .tp_getset = pg_line_getsets, + .tp_init = (initproc)pg_line_init, + .tp_new = pg_line_new, +}; diff --git a/test/geometry_test.py b/test/geometry_test.py index e95d43ba46..d3c31abb6d 100644 --- a/test/geometry_test.py +++ b/test/geometry_test.py @@ -3,7 +3,7 @@ from math import sqrt from pygame import Vector2, Vector3, Rect, FRect -from pygame.geometry import Circle +from pygame.geometry import Circle, Line def float_range(a, b, step): @@ -1350,5 +1350,372 @@ def test_intersect(self): self.assertEqual([(14.0, 10.0)], c.intersect(c5)) +class LineTypeTest(unittest.TestCase): + class ClassWithLineAttrib: + def __init__(self, line): + self.line = line + + class ClassWithLineProperty: + def __init__(self, line): + self._line = line + + @property + def line(self): + return self._line + + class ClassWithLineFunction: + def __init__(self, line): + self._line = line + + def line(self): + return self._line + + def testConstruction_invalid_type(self): + """Checks whether passing wrong types to the constructor + raises the appropriate errors + """ + invalid_types = (None, [], "1", (1,), [1, 2, 3], Vector2(1, 1)) + + # Test xa + for value in invalid_types: + with self.assertRaises(TypeError): + Line(value, 0, 1, 2) + # Test ya + for value in invalid_types: + with self.assertRaises(TypeError): + Line(0, value, 1, 2) + # Test xb + for value in invalid_types: + with self.assertRaises(TypeError): + Line(0, 0, value, 2) + # Test yb + for value in invalid_types: + with self.assertRaises(TypeError): + Line(0, 1, 2, value) + + def testConstruction_invalid_arguments_number(self): + """Checks whether passing the wrong number of arguments to the constructor + raises the appropriate errors + """ + arguments = ( + (1,), # one non vec3 non circle arg + (1, 1), # two args + (1, 1, 1), # three args + (1, 1, 1, 1, 1), # five args + ) + + for arg_seq in arguments: + with self.assertRaises(TypeError): + Line(*arg_seq) + + def testConstructionXAYAXBYB_float(self): + """Tests the construction of a line with 4 float arguments""" + line = Line(1.0, 2.0, 3.0, 4.0) + + self.assertEqual(line.xa, 1.0) + self.assertEqual(line.ya, 2.0) + self.assertEqual(line.xb, 3.0) + self.assertEqual(line.yb, 4.0) + + def testConstructionTUP_XAYAXBYB_float(self): + """Tests the construction of a line with a tuple of 4 float arguments""" + line = Line((1.0, 2.0, 3.0, 4.0)) + + self.assertEqual(line.xa, 1.0) + self.assertEqual(line.ya, 2.0) + self.assertEqual(line.xb, 3.0) + self.assertEqual(line.yb, 4.0) + + def testConstructionXAYAXBYB_int(self): + """Tests the construction of a line with 4 int arguments""" + line = Line(1, 2, 3, 4) + + self.assertEqual(line.xa, 1.0) + self.assertEqual(line.ya, 2.0) + self.assertEqual(line.xb, 3.0) + self.assertEqual(line.yb, 4.0) + + def testConstructionTUP_XAYAXBYB_int(self): + """Tests the construction of a line with a tuple of 4 int arguments""" + line = Line((1, 2, 3, 4)) + + self.assertEqual(line.xa, 1.0) + self.assertEqual(line.ya, 2.0) + self.assertEqual(line.xb, 3.0) + self.assertEqual(line.yb, 4.0) + + def testConstruction_class_with_line_attrib(self): + """Tests the construction of a line with a class that has a line attribute""" + class_ = self.ClassWithLineAttrib(Line(1.1, 2.2, 3.3, 4.4)) + + line = Line(class_) + + self.assertEqual(line.xa, 1.1) + self.assertEqual(line.ya, 2.2) + self.assertEqual(line.xb, 3.3) + self.assertEqual(line.yb, 4.4) + + def testConstruction_class_with_line_property(self): + """Tests the construction of a line with a class that has a line property""" + class_ = self.ClassWithLineProperty(Line(1.1, 2.2, 3.3, 4.4)) + + line = Line(class_) + + self.assertEqual(line.xa, 1.1) + self.assertEqual(line.ya, 2.2) + self.assertEqual(line.xb, 3.3) + self.assertEqual(line.yb, 4.4) + + def testConstruction_class_with_line_function(self): + """Tests the construction of a line with a class that has a line function""" + class_ = self.ClassWithLineFunction(Line(1.1, 2.2, 3.3, 4.4)) + + line = Line(class_) + + self.assertEqual(line.xa, 1.1) + self.assertEqual(line.ya, 2.2) + self.assertEqual(line.xb, 3.3) + self.assertEqual(line.yb, 4.4) + + def testConstruction_degenerate(self): + """Ensures that you can't create degenerate lines (lines with zero length)""" + + # 4 args + with self.assertRaises(TypeError): + Line(1.0, 2.0, 1.0, 2.0) + with self.assertRaises(TypeError): + Line(1, 2, 1, 2) + + # 1 list arg 4 + with self.assertRaises(TypeError): + Line([1, 2, 1, 2]) + with self.assertRaises(TypeError): + Line([1.0, 2.0, 1.0, 2.0]) + + # 1 tuple arg 4 + with self.assertRaises(TypeError): + Line((1, 2, 1, 2)) + with self.assertRaises(TypeError): + Line((1.0, 2.0, 1.0, 2.0)) + + # two tuple args + with self.assertRaises(TypeError): + Line((1, 2), (1, 2)) + with self.assertRaises(TypeError): + Line((1.0, 2.0), (1.0, 2.0)) + + # two list args + with self.assertRaises(TypeError): + Line([1, 2], [1, 2]) + with self.assertRaises(TypeError): + Line([1.0, 2.0], [1.0, 2.0]) + + # one list two tuple args + with self.assertRaises(TypeError): + Line([1, 2], (1, 2)) + with self.assertRaises(TypeError): + Line((1, 2), [1, 2]) + with self.assertRaises(TypeError): + Line([1.0, 2.0], (1.0, 2.0)) + with self.assertRaises(TypeError): + Line((1.0, 2.0), [1.0, 2.0]) + + # one list two sub-tuples arg + with self.assertRaises(TypeError): + Line([(1, 2), (1, 2)]) + with self.assertRaises(TypeError): + Line([(1.0, 2.0), (1.0, 2.0)]) + + # one tuple two sub-lists arg + with self.assertRaises(TypeError): + Line(([1, 2], [1, 2])) + with self.assertRaises(TypeError): + Line(([1.0, 2.0], [1.0, 2.0])) + + def test_attrib_x1(self): + """a full test for the xa attribute""" + expected_x1 = 10.0 + expected_y1 = 2.0 + expected_x2 = 5.0 + expected_y2 = 6.0 + line = Line(1, expected_y1, expected_x2, expected_y2) + + line.xa = expected_x1 + + self.assertEqual(line.xa, expected_x1) + self.assertEqual(line.ya, expected_y1) + self.assertEqual(line.xb, expected_x2) + self.assertEqual(line.yb, expected_y2) + + line = Line(0, 0, 1, 0) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + line.xa = value + + line = Line(0, 0, 1, 0) + + with self.assertRaises(AttributeError): + del line.xa + + def test_attrib_y1(self): + """a full test for the ya attribute""" + expected_x1 = 10.0 + expected_y1 = 2.0 + expected_x2 = 5.0 + expected_y2 = 6.0 + line = Line(expected_x1, 1, expected_x2, expected_y2) + + line.ya = expected_y1 + + self.assertEqual(line.xa, expected_x1) + self.assertEqual(line.ya, expected_y1) + self.assertEqual(line.xb, expected_x2) + self.assertEqual(line.yb, expected_y2) + + line = Line(0, 0, 1, 0) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + line.ya = value + + line = Line(0, 0, 1, 0) + + with self.assertRaises(AttributeError): + del line.ya + + def test_attrib_x2(self): + """a full test for the ya attribute""" + expected_x1 = 10.0 + expected_y1 = 2.0 + expected_x2 = 5.0 + expected_y2 = 6.0 + line = Line(expected_x1, expected_y1, 1, expected_y2) + + line.xb = expected_x2 + + self.assertEqual(line.xa, expected_x1) + self.assertEqual(line.ya, expected_y1) + self.assertEqual(line.xb, expected_x2) + self.assertEqual(line.yb, expected_y2) + + line = Line(0, 0, 1, 0) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + line.xb = value + + line = Line(0, 0, 1, 0) + + with self.assertRaises(AttributeError): + del line.xb + + def test_attrib_y2(self): + """a full test for the ya attribute""" + expected_x1 = 10.0 + expected_y1 = 2.0 + expected_x2 = 5.0 + expected_y2 = 6.0 + line = Line(expected_x1, expected_y1, expected_x2, 1) + + line.yb = expected_y2 + + self.assertEqual(line.xa, expected_x1) + self.assertEqual(line.ya, expected_y1) + self.assertEqual(line.xb, expected_x2) + self.assertEqual(line.yb, expected_y2) + + line = Line(0, 0, 1, 0) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + line.yb = value + + line = Line(0, 0, 1, 0) + + with self.assertRaises(AttributeError): + del line.yb + + def test_attrib_a(self): + """a full test for the ya attribute""" + expected_x1 = 10.0 + expected_y1 = 2.0 + expected_x2 = 5.0 + expected_y2 = 6.0 + expected_a = expected_x1, expected_y1 + expected_b = expected_x2, expected_y2 + line = Line((0, 1), expected_b) + + line.a = expected_a + + self.assertEqual(line.a, expected_a) + self.assertEqual(line.b, expected_b) + + line = Line(0, 0, 1, 0) + + for value in (None, [], "1", (1,), [1, 2, 3], 1): + with self.assertRaises(TypeError): + line.a = value + + line = Line(0, 0, 1, 0) + + with self.assertRaises(AttributeError): + del line.a + + def test_attrib_b(self): + """a full test for the ya attribute""" + expected_x1 = 10.0 + expected_y1 = 2.0 + expected_x2 = 5.0 + expected_y2 = 6.0 + expected_a = expected_x1, expected_y1 + expected_b = expected_x2, expected_y2 + line = Line(expected_a, (0, 1)) + + line.b = expected_b + + self.assertEqual(line.a, expected_a) + self.assertEqual(line.b, expected_b) + + line = Line(0, 0, 1, 0) + + for value in (None, [], "1", (1,), [1, 2, 3], 1): + with self.assertRaises(TypeError): + line.b = value + + line = Line(0, 0, 1, 0) + + with self.assertRaises(AttributeError): + del line.b + + def test_meth_copy(self): + line = Line(1, 2, 3, 4) + # check 1 arg passed + with self.assertRaises(TypeError): + line.copy(10) + + line_2 = line.copy() + self.assertEqual(line.xa, line_2.xa) + self.assertEqual(line.yb, line_2.yb) + self.assertEqual(line.xb, line_2.xb) + self.assertEqual(line.yb, line_2.yb) + + self.assertIsNot(line, line_2) + + def test__str__(self): + """Checks whether the __str__ method works correctly.""" + l_str = "" + line = Line(10.1, 10.2, 4.3, 56.4) + self.assertEqual(str(line), l_str) + self.assertEqual(line.__str__(), l_str) + + def test__repr__(self): + """Checks whether the __repr__ method works correctly.""" + l_repr = "" + line = Line(10.1, 10.2, 4.3, 56.4) + self.assertEqual(repr(line), l_repr) + self.assertEqual(line.__repr__(), l_repr) + + if __name__ == "__main__": unittest.main() From 7c04b13c00948f323796efcfb26691cb17095be2 Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Sun, 29 Sep 2024 12:31:08 +0200 Subject: [PATCH 02/11] fixed geometry.pyi a bit, removed redundant tuple specification as covered by SequenceLike. --- buildconfig/stubs/pygame/geometry.pyi | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/buildconfig/stubs/pygame/geometry.pyi b/buildconfig/stubs/pygame/geometry.pyi index d006463215..77345f9d43 100644 --- a/buildconfig/stubs/pygame/geometry.pyi +++ b/buildconfig/stubs/pygame/geometry.pyi @@ -4,7 +4,7 @@ from typing import ( Callable, Protocol, Tuple, - List, Sequence, + List, ) from pygame import Rect, FRect @@ -16,10 +16,8 @@ _CanBeCircle = Union[Circle, Tuple[Coordinate, float], SequenceLike[float]] _CanBeLine = Union[ Rect, Line, - Tuple[float, float, float, float], - Tuple[Coordinate, Coordinate], Coordinate, - Sequence[Coordinate], + SequenceLike[Coordinate], ] From 05949cbc5e856b605485d534f08b9e24da0f780b Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Sun, 29 Sep 2024 12:33:01 +0200 Subject: [PATCH 03/11] more fixes and added FRect to _CanBeLine. --- buildconfig/stubs/pygame/geometry.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildconfig/stubs/pygame/geometry.pyi b/buildconfig/stubs/pygame/geometry.pyi index 77345f9d43..f4a8827959 100644 --- a/buildconfig/stubs/pygame/geometry.pyi +++ b/buildconfig/stubs/pygame/geometry.pyi @@ -7,14 +7,14 @@ from typing import ( List, ) -from pygame import Rect, FRect -from pygame.typing import Coordinate, RectLike, SequenceLike +from .typing import Coordinate, RectLike, SequenceLike from .rect import Rect, FRect from .math import Vector2 _CanBeCircle = Union[Circle, Tuple[Coordinate, float], SequenceLike[float]] _CanBeLine = Union[ Rect, + FRect, Line, Coordinate, SequenceLike[Coordinate], From ba9f4ece5856831ae68690e4eff20e63e3b5ebde Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Sun, 29 Sep 2024 12:46:51 +0200 Subject: [PATCH 04/11] last .pyi fixes. --- buildconfig/stubs/pygame/geometry.pyi | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/buildconfig/stubs/pygame/geometry.pyi b/buildconfig/stubs/pygame/geometry.pyi index f4a8827959..8633e92f53 100644 --- a/buildconfig/stubs/pygame/geometry.pyi +++ b/buildconfig/stubs/pygame/geometry.pyi @@ -157,12 +157,10 @@ class Line: @b.setter def b(self, value: Coordinate) -> None: ... @overload - def __init__(self, line: Line) -> None: ... - @overload def __init__(self, xa: float, ya: float, xb: float, yb: float) -> None: ... @overload - def __init__(self, first: Coordinate, second: Coordinate) -> None: ... + def __init__(self, a: Coordinate, b: Coordinate) -> None: ... @overload - def __init__(self, single_arg: _LineValue) -> None: ... + def __init__(self, line: _LineValue) -> None: ... def __copy__(self) -> Line: ... copy = __copy__ From 09efa8ba1e569ce9103de060afb7054d68d7bf77 Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Sun, 29 Sep 2024 13:06:08 +0200 Subject: [PATCH 05/11] removed some unused/duplicate defines. --- src_c/geometry.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src_c/geometry.h b/src_c/geometry.h index 640dc19355..f6adbb033e 100644 --- a/src_c/geometry.h +++ b/src_c/geometry.h @@ -15,9 +15,6 @@ typedef struct { #define pgCircle_CAST(o) ((pgCircleObject *)(o)) #define pgCircle_AsCircle(o) (pgCircle_CAST(o)->circle) -#define pgCircle_GETX(self) (pgCircle_CAST(self)->circle.x) -#define pgCircle_GETY(self) (pgCircle_CAST(self)->circle.y) -#define pgCircle_GETR(self) (pgCircle_CAST(self)->circle.r) #define pgCircle_Check(o) ((o)->ob_type == &pgCircle_Type) typedef struct { @@ -31,13 +28,7 @@ typedef struct { } pgLineObject; #define pgLine_CAST(o) ((pgLineObject *)(o)) - -#define pgLine_GETLINE(o) (pgLine_CAST(o)->line) #define pgLine_AsLine(o) (pgLine_CAST(o)->line) -#define pgLine_GETX1(self) (pgLine_CAST(self)->line.xa) -#define pgLine_GETY1(self) (pgLine_CAST(self)->line.ya) -#define pgLine_GETX2(self) (pgLine_CAST(self)->line.xb) -#define pgLine_GETY2(self) (pgLine_CAST(self)->line.yb) #define pgLine_Check(o) ((o)->ob_type == &pgLine_Type) static PyTypeObject pgCircle_Type; From 7c5310f407874a7d65f815900358be76411a209c Mon Sep 17 00:00:00 2001 From: Dan Lawrence Date: Sat, 5 Oct 2024 10:09:37 +0100 Subject: [PATCH 06/11] Fix merge errors --- buildconfig/stubs/pygame/geometry.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildconfig/stubs/pygame/geometry.pyi b/buildconfig/stubs/pygame/geometry.pyi index e155be650c..6bd8f62447 100644 --- a/buildconfig/stubs/pygame/geometry.pyi +++ b/buildconfig/stubs/pygame/geometry.pyi @@ -8,7 +8,6 @@ from typing import ( List, ) - from pygame import Rect, FRect from pygame.typing import Point, RectLike, SequenceLike from .rect import Rect, FRect @@ -28,6 +27,8 @@ class _HasCirclettribute(Protocol): # that returns a circle circle: Union[_CanBeCircle, Callable[[], _CanBeCircle]] +_CircleValue = Union[_CanBeCircle, _HasCirclettribute] + class _HasLineAttribute(Protocol): # An object that has a line attribute that is either a line, or a function # that returns a line @@ -149,7 +150,6 @@ class Circle: def as_frect(self) -> FRect: ... def copy(self) -> Circle: ... def __copy__(self) -> Circle: ... - copy = __copy__ class Line: @property From 502cc8914cce20def1fe3719582ae3268462fdb7 Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Sun, 6 Oct 2024 11:57:36 +0200 Subject: [PATCH 07/11] Now accepting degenerate lines, added some more __init__ tests. --- src_c/line.c | 38 +++++++++--------- test/geometry_test.py | 89 +++++++++++++++++-------------------------- 2 files changed, 52 insertions(+), 75 deletions(-) diff --git a/src_c/line.c b/src_c/line.c index 98f0d6d82f..407a56499e 100644 --- a/src_c/line.c +++ b/src_c/line.c @@ -1,8 +1,6 @@ #include "doc/geometry_doc.h" #include "geometry_common.h" -#define IS_LINE_VALID(line) (line->xa != line->xb || line->ya != line->yb) - static PyObject * _pg_line_subtype_new4(PyTypeObject *type, double xa, double ya, double xb, double yb) @@ -53,28 +51,28 @@ pgLine_FromObject(PyObject *obj, pgLineBase *out) PyObject **farray = PySequence_Fast_ITEMS(obj); if (length == 4) { - if (!pg_DoubleFromObj(farray[0], &(out->xa)) || - !pg_DoubleFromObj(farray[1], &(out->ya)) || - !pg_DoubleFromObj(farray[2], &(out->xb)) || - !pg_DoubleFromObj(farray[3], &(out->yb))) { + if (!pg_DoubleFromObj(farray[0], &out->xa) || + !pg_DoubleFromObj(farray[1], &out->ya) || + !pg_DoubleFromObj(farray[2], &out->xb) || + !pg_DoubleFromObj(farray[3], &out->yb)) { return 0; } - return IS_LINE_VALID(out); + return 1; } else if (length == 2) { - if (!pg_TwoDoublesFromObj(farray[0], &(out->xa), &(out->ya)) || - !pg_TwoDoublesFromObj(farray[1], &(out->xb), &(out->yb))) { + if (!pg_TwoDoublesFromObj(farray[0], &out->xa, &out->ya) || + !pg_TwoDoublesFromObj(farray[1], &out->xb, &out->yb)) { PyErr_Clear(); return 0; } - return IS_LINE_VALID(out); + return 1; } else if (length == 1) /*looks like an arg?*/ { if (PyUnicode_Check(farray[0]) || !pgLine_FromObject(farray[0], out)) { return 0; } - return IS_LINE_VALID(out); + return 1; } } if (PySequence_Check(obj)) { @@ -82,46 +80,46 @@ pgLine_FromObject(PyObject *obj, pgLineBase *out) if (length == 4) { PyObject *tmp; tmp = PySequence_GetItem(obj, 0); - if (!pg_DoubleFromObj(tmp, &(out->xa))) { + if (!pg_DoubleFromObj(tmp, &out->xa)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); tmp = PySequence_GetItem(obj, 1); - if (!pg_DoubleFromObj(tmp, &(out->ya))) { + if (!pg_DoubleFromObj(tmp, &out->ya)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); tmp = PySequence_GetItem(obj, 2); - if (!pg_DoubleFromObj(tmp, &(out->xb))) { + if (!pg_DoubleFromObj(tmp, &out->xb)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); tmp = PySequence_GetItem(obj, 3); - if (!pg_DoubleFromObj(tmp, &(out->yb))) { + if (!pg_DoubleFromObj(tmp, &out->yb)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); - return IS_LINE_VALID(out); + return 1; } else if (length == 2) { PyObject *tmp; tmp = PySequence_GetItem(obj, 0); - if (!pg_TwoDoublesFromObj(tmp, &(out->xa), &(out->ya))) { + if (!pg_TwoDoublesFromObj(tmp, &out->xa, &out->ya)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); tmp = PySequence_GetItem(obj, 1); - if (!pg_TwoDoublesFromObj(tmp, &(out->xb), &(out->yb))) { + if (!pg_TwoDoublesFromObj(tmp, &out->xb, &out->yb)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); - return IS_LINE_VALID(out); + return 1; } else if (PyTuple_Check(obj) && length == 1) /*looks like an arg?*/ { PyObject *sub = PySequence_GetItem(obj, 0); @@ -130,7 +128,7 @@ pgLine_FromObject(PyObject *obj, pgLineBase *out) return 0; } Py_DECREF(sub); - return IS_LINE_VALID(out); + return 1; } else { return 0; diff --git a/test/geometry_test.py b/test/geometry_test.py index 0ff8f97a48..cc0ae5084b 100644 --- a/test/geometry_test.py +++ b/test/geometry_test.py @@ -1693,6 +1693,40 @@ def testConstruction_invalid_type(self): with self.assertRaises(TypeError): Line(0, 1, 2, value) + # Test xa + for value in invalid_types: + with self.assertRaises(TypeError): + Line((value, 0), (1, 2)) + # Test ya + for value in invalid_types: + with self.assertRaises(TypeError): + Line((0, value), (1, 2)) + # Test xb + for value in invalid_types: + with self.assertRaises(TypeError): + Line((0, 0), (value, 2)) + # Test yb + for value in invalid_types: + with self.assertRaises(TypeError): + Line((0, 1), (2, value)) + + # Test xa + for value in invalid_types: + with self.assertRaises(TypeError): + Line(((value, 0), (1, 2))) + # Test ya + for value in invalid_types: + with self.assertRaises(TypeError): + Line(((0, value), (1, 2))) + # Test xb + for value in invalid_types: + with self.assertRaises(TypeError): + Line(((0, 0), (value, 2))) + # Test yb + for value in invalid_types: + with self.assertRaises(TypeError): + Line(((0, 1), (2, value))) + def testConstruction_invalid_arguments_number(self): """Checks whether passing the wrong number of arguments to the constructor raises the appropriate errors @@ -1777,61 +1811,6 @@ def testConstruction_class_with_line_function(self): self.assertEqual(line.xb, 3.3) self.assertEqual(line.yb, 4.4) - def testConstruction_degenerate(self): - """Ensures that you can't create degenerate lines (lines with zero length)""" - - # 4 args - with self.assertRaises(TypeError): - Line(1.0, 2.0, 1.0, 2.0) - with self.assertRaises(TypeError): - Line(1, 2, 1, 2) - - # 1 list arg 4 - with self.assertRaises(TypeError): - Line([1, 2, 1, 2]) - with self.assertRaises(TypeError): - Line([1.0, 2.0, 1.0, 2.0]) - - # 1 tuple arg 4 - with self.assertRaises(TypeError): - Line((1, 2, 1, 2)) - with self.assertRaises(TypeError): - Line((1.0, 2.0, 1.0, 2.0)) - - # two tuple args - with self.assertRaises(TypeError): - Line((1, 2), (1, 2)) - with self.assertRaises(TypeError): - Line((1.0, 2.0), (1.0, 2.0)) - - # two list args - with self.assertRaises(TypeError): - Line([1, 2], [1, 2]) - with self.assertRaises(TypeError): - Line([1.0, 2.0], [1.0, 2.0]) - - # one list two tuple args - with self.assertRaises(TypeError): - Line([1, 2], (1, 2)) - with self.assertRaises(TypeError): - Line((1, 2), [1, 2]) - with self.assertRaises(TypeError): - Line([1.0, 2.0], (1.0, 2.0)) - with self.assertRaises(TypeError): - Line((1.0, 2.0), [1.0, 2.0]) - - # one list two sub-tuples arg - with self.assertRaises(TypeError): - Line([(1, 2), (1, 2)]) - with self.assertRaises(TypeError): - Line([(1.0, 2.0), (1.0, 2.0)]) - - # one tuple two sub-lists arg - with self.assertRaises(TypeError): - Line(([1, 2], [1, 2])) - with self.assertRaises(TypeError): - Line(([1.0, 2.0], [1.0, 2.0])) - def test_attrib_x1(self): """a full test for the xa attribute""" expected_x1 = 10.0 From 14a3538fc1c2707b70ce4625d9727ca0b9168374 Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Sun, 6 Oct 2024 12:21:02 +0200 Subject: [PATCH 08/11] Move pgLine_FromObject to geometry_common to conform with circle. --- src_c/geometry_common.c | 129 ++++++++++++++++++++++++++++++++++++++++ src_c/geometry_common.h | 3 + src_c/line.c | 120 ------------------------------------- 3 files changed, 132 insertions(+), 120 deletions(-) diff --git a/src_c/geometry_common.c b/src_c/geometry_common.c index 07d84f0843..0eb79da2c4 100644 --- a/src_c/geometry_common.c +++ b/src_c/geometry_common.c @@ -147,6 +147,135 @@ pgCircle_FromObjectFastcall(PyObject *const *args, Py_ssize_t nargs, } } +int +pgLine_FromObject(PyObject *obj, pgLineBase *out) +{ + Py_ssize_t length; + + if (pgLine_Check(obj)) { + *out = ((pgLineObject *)obj)->line; + return 1; + } + + /* Paths for sequences */ + if (pgSequenceFast_Check(obj)) { + length = PySequence_Fast_GET_SIZE(obj); + PyObject **farray = PySequence_Fast_ITEMS(obj); + + if (length == 4) { + if (!pg_DoubleFromObj(farray[0], &out->xa) || + !pg_DoubleFromObj(farray[1], &out->ya) || + !pg_DoubleFromObj(farray[2], &out->xb) || + !pg_DoubleFromObj(farray[3], &out->yb)) { + return 0; + } + return 1; + } + else if (length == 2) { + if (!pg_TwoDoublesFromObj(farray[0], &out->xa, &out->ya) || + !pg_TwoDoublesFromObj(farray[1], &out->xb, &out->yb)) { + PyErr_Clear(); + return 0; + } + return 1; + } + else if (length == 1) /*looks like an arg?*/ { + if (PyUnicode_Check(farray[0]) || + !pgLine_FromObject(farray[0], out)) { + return 0; + } + return 1; + } + } + else if (PySequence_Check(obj)) { + length = PySequence_Length(obj); + if (length == 4) { + PyObject *tmp; + tmp = PySequence_GetItem(obj, 0); + if (!pg_DoubleFromObj(tmp, &out->xa)) { + Py_DECREF(tmp); + return 0; + } + Py_DECREF(tmp); + tmp = PySequence_GetItem(obj, 1); + if (!pg_DoubleFromObj(tmp, &out->ya)) { + Py_DECREF(tmp); + return 0; + } + Py_DECREF(tmp); + tmp = PySequence_GetItem(obj, 2); + if (!pg_DoubleFromObj(tmp, &out->xb)) { + Py_DECREF(tmp); + return 0; + } + Py_DECREF(tmp); + tmp = PySequence_GetItem(obj, 3); + if (!pg_DoubleFromObj(tmp, &out->yb)) { + Py_DECREF(tmp); + return 0; + } + Py_DECREF(tmp); + return 1; + } + else if (length == 2) { + PyObject *tmp; + tmp = PySequence_GetItem(obj, 0); + if (!pg_TwoDoublesFromObj(tmp, &out->xa, &out->ya)) { + Py_DECREF(tmp); + return 0; + } + Py_DECREF(tmp); + tmp = PySequence_GetItem(obj, 1); + if (!pg_TwoDoublesFromObj(tmp, &out->xb, &out->yb)) { + Py_DECREF(tmp); + return 0; + } + Py_DECREF(tmp); + return 1; + } + else if (PyTuple_Check(obj) && length == 1) /*looks like an arg?*/ { + PyObject *sub = PySequence_GetItem(obj, 0); + if (PyUnicode_Check(sub) || !pgLine_FromObject(sub, out)) { + Py_DECREF(sub); + return 0; + } + Py_DECREF(sub); + return 1; + } + else { + return 0; + } + } + + /* Path for objects that have a line attribute */ + PyObject *lineattr; + if (!(lineattr = PyObject_GetAttrString(obj, "line"))) { + PyErr_Clear(); + return 0; + } + + if (PyCallable_Check(lineattr)) /*call if it's a method*/ + { + PyObject *lineresult = PyObject_CallNoArgs(lineattr); + Py_DECREF(lineattr); + if (!lineresult) { + PyErr_Clear(); + return 0; + } + lineattr = lineresult; + } + + if (!pgLine_FromObject(lineattr, out)) { + PyErr_Clear(); + Py_DECREF(lineattr); + return 0; + } + + Py_DECREF(lineattr); + + return 1; +} + static inline int double_compare(double a, double b) { diff --git a/src_c/geometry_common.h b/src_c/geometry_common.h index 115f248768..8ecc9b259a 100644 --- a/src_c/geometry_common.h +++ b/src_c/geometry_common.h @@ -13,6 +13,9 @@ int pgCircle_FromObjectFastcall(PyObject *const *args, Py_ssize_t nargs, pgCircleBase *out); +int +pgLine_FromObject(PyObject *obj, pgLineBase *out); + static inline int double_compare(double a, double b); diff --git a/src_c/line.c b/src_c/line.c index 407a56499e..c14d6b8150 100644 --- a/src_c/line.c +++ b/src_c/line.c @@ -37,126 +37,6 @@ pg_line_dealloc(pgLineObject *self) Py_TYPE(self)->tp_free((PyObject *)self); } -static int -pgLine_FromObject(PyObject *obj, pgLineBase *out) -{ - Py_ssize_t length; - - if (pgLine_Check(obj)) { - *out = ((pgLineObject *)obj)->line; - return 1; - } - if (pgSequenceFast_Check(obj)) { - length = PySequence_Fast_GET_SIZE(obj); - PyObject **farray = PySequence_Fast_ITEMS(obj); - - if (length == 4) { - if (!pg_DoubleFromObj(farray[0], &out->xa) || - !pg_DoubleFromObj(farray[1], &out->ya) || - !pg_DoubleFromObj(farray[2], &out->xb) || - !pg_DoubleFromObj(farray[3], &out->yb)) { - return 0; - } - return 1; - } - else if (length == 2) { - if (!pg_TwoDoublesFromObj(farray[0], &out->xa, &out->ya) || - !pg_TwoDoublesFromObj(farray[1], &out->xb, &out->yb)) { - PyErr_Clear(); - return 0; - } - return 1; - } - else if (length == 1) /*looks like an arg?*/ { - if (PyUnicode_Check(farray[0]) || - !pgLine_FromObject(farray[0], out)) { - return 0; - } - return 1; - } - } - if (PySequence_Check(obj)) { - length = PySequence_Length(obj); - if (length == 4) { - PyObject *tmp; - tmp = PySequence_GetItem(obj, 0); - if (!pg_DoubleFromObj(tmp, &out->xa)) { - Py_DECREF(tmp); - return 0; - } - Py_DECREF(tmp); - tmp = PySequence_GetItem(obj, 1); - if (!pg_DoubleFromObj(tmp, &out->ya)) { - Py_DECREF(tmp); - return 0; - } - Py_DECREF(tmp); - tmp = PySequence_GetItem(obj, 2); - if (!pg_DoubleFromObj(tmp, &out->xb)) { - Py_DECREF(tmp); - return 0; - } - Py_DECREF(tmp); - tmp = PySequence_GetItem(obj, 3); - if (!pg_DoubleFromObj(tmp, &out->yb)) { - Py_DECREF(tmp); - return 0; - } - Py_DECREF(tmp); - return 1; - } - else if (length == 2) { - PyObject *tmp; - tmp = PySequence_GetItem(obj, 0); - if (!pg_TwoDoublesFromObj(tmp, &out->xa, &out->ya)) { - Py_DECREF(tmp); - return 0; - } - Py_DECREF(tmp); - tmp = PySequence_GetItem(obj, 1); - if (!pg_TwoDoublesFromObj(tmp, &out->xb, &out->yb)) { - Py_DECREF(tmp); - return 0; - } - Py_DECREF(tmp); - return 1; - } - else if (PyTuple_Check(obj) && length == 1) /*looks like an arg?*/ { - PyObject *sub = PySequence_GetItem(obj, 0); - if (PyUnicode_Check(sub) || !pgLine_FromObject(sub, out)) { - Py_DECREF(sub); - return 0; - } - Py_DECREF(sub); - return 1; - } - else { - return 0; - } - } - if (PyObject_HasAttrString(obj, "line")) { - PyObject *lineattr; - lineattr = PyObject_GetAttrString(obj, "line"); - if (!lineattr) { - PyErr_Clear(); - return 0; - } - if (PyCallable_Check(lineattr)) /*call if it's a method*/ - { - PyObject *lineresult = PyObject_CallObject(lineattr, NULL); - Py_DECREF(lineattr); - if (!lineresult) { - PyErr_Clear(); - return 0; - } - lineattr = lineresult; - } - Py_DECREF(lineattr); - return pgLine_FromObject(lineattr, out); - } - return 0; -} - static int pg_line_init(pgLineObject *self, PyObject *args, PyObject *kwds) { From e118159824b4e9b986a506678ea1ad4dba7e70e2 Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Tue, 8 Oct 2024 12:06:00 +0200 Subject: [PATCH 09/11] Now using PySequence_ITEM in pgLine_FromObject. --- src_c/geometry_common.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src_c/geometry_common.c b/src_c/geometry_common.c index 0eb79da2c4..4082a81f1e 100644 --- a/src_c/geometry_common.c +++ b/src_c/geometry_common.c @@ -153,7 +153,7 @@ pgLine_FromObject(PyObject *obj, pgLineBase *out) Py_ssize_t length; if (pgLine_Check(obj)) { - *out = ((pgLineObject *)obj)->line; + *out = pgLine_AsLine(obj); return 1; } @@ -191,25 +191,25 @@ pgLine_FromObject(PyObject *obj, pgLineBase *out) length = PySequence_Length(obj); if (length == 4) { PyObject *tmp; - tmp = PySequence_GetItem(obj, 0); + tmp = PySequence_ITEM(obj, 0); if (!pg_DoubleFromObj(tmp, &out->xa)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); - tmp = PySequence_GetItem(obj, 1); + tmp = PySequence_ITEM(obj, 1); if (!pg_DoubleFromObj(tmp, &out->ya)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); - tmp = PySequence_GetItem(obj, 2); + tmp = PySequence_ITEM(obj, 2); if (!pg_DoubleFromObj(tmp, &out->xb)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); - tmp = PySequence_GetItem(obj, 3); + tmp = PySequence_ITEM(obj, 3); if (!pg_DoubleFromObj(tmp, &out->yb)) { Py_DECREF(tmp); return 0; @@ -219,13 +219,13 @@ pgLine_FromObject(PyObject *obj, pgLineBase *out) } else if (length == 2) { PyObject *tmp; - tmp = PySequence_GetItem(obj, 0); + tmp = PySequence_ITEM(obj, 0); if (!pg_TwoDoublesFromObj(tmp, &out->xa, &out->ya)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); - tmp = PySequence_GetItem(obj, 1); + tmp = PySequence_ITEM(obj, 1); if (!pg_TwoDoublesFromObj(tmp, &out->xb, &out->yb)) { Py_DECREF(tmp); return 0; @@ -234,7 +234,7 @@ pgLine_FromObject(PyObject *obj, pgLineBase *out) return 1; } else if (PyTuple_Check(obj) && length == 1) /*looks like an arg?*/ { - PyObject *sub = PySequence_GetItem(obj, 0); + PyObject *sub = PySequence_ITEM(obj, 0); if (PyUnicode_Check(sub) || !pgLine_FromObject(sub, out)) { Py_DECREF(sub); return 0; From 6ecdc24aa9196fc5531ae6f7ab53de97e90f33ed Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Tue, 8 Oct 2024 21:27:00 +0200 Subject: [PATCH 10/11] remove doc about not accepting degenerate lines --- docs/reST/ref/geometry.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/reST/ref/geometry.rst b/docs/reST/ref/geometry.rst index 5b992fbba7..460b79157d 100644 --- a/docs/reST/ref/geometry.rst +++ b/docs/reST/ref/geometry.rst @@ -514,9 +514,6 @@ The `Line` class only stores the xa, ya, xb, and yb attributes, everything else is calculated on the fly based on them. - **You cannot create degenerate Lines(lines with the same start and end point). If you - try, the `Line` will not be created and an error will be raised.** - **Line Attributes** ---- From 81c21f54bcd73d225591147cc214190205ef2374 Mon Sep 17 00:00:00 2001 From: itzpr3d4t0r <103119829+itzpr3d4t0r@users.noreply.github.com> Date: Sat, 12 Oct 2024 11:48:37 +0200 Subject: [PATCH 11/11] Renamed (xa, ya, xb, yb) -> (ax, ay, bx, by). Removed Rect/Frect from canbeline stub. Added some more info about passing a Rect/Frect to construct a Line and about degenerate lines potentially making some methods not work. --- buildconfig/stubs/pygame/geometry.pyi | 32 +++-- docs/reST/ref/geometry.rst | 55 +++++---- src_c/doc/geometry_doc.h | 10 +- src_c/geometry.h | 4 +- src_c/geometry_common.c | 24 ++-- src_c/line.c | 84 ++++++------- test/geometry_test.py | 164 +++++++++++++------------- 7 files changed, 189 insertions(+), 184 deletions(-) diff --git a/buildconfig/stubs/pygame/geometry.pyi b/buildconfig/stubs/pygame/geometry.pyi index 6bd8f62447..a23e731c87 100644 --- a/buildconfig/stubs/pygame/geometry.pyi +++ b/buildconfig/stubs/pygame/geometry.pyi @@ -15,10 +15,8 @@ from .math import Vector2 _CanBeCircle = Union[Circle, Tuple[Point, float], SequenceLike[float]] _CanBeLine = Union[ - Rect, - FRect, Line, - Point, + SequenceLike[float], SequenceLike[Point], ] @@ -153,21 +151,21 @@ class Circle: class Line: @property - def xa(self) -> float: ... - @xa.setter - def xa(self, value: float) -> None: ... + def ax(self) -> float: ... + @ax.setter + def ax(self, value: float) -> None: ... @property - def ya(self) -> float: ... - @ya.setter - def ya(self, value: float) -> None: ... + def ay(self) -> float: ... + @ay.setter + def ay(self, value: float) -> None: ... @property - def xb(self) -> float: ... - @xb.setter - def xb(self, value: float) -> None: ... + def bx(self) -> float: ... + @bx.setter + def bx(self, value: float) -> None: ... @property - def yb(self) -> float: ... - @yb.setter - def yb(self, value: float) -> None: ... + def by(self) -> float: ... + @by.setter + def by(self, value: float) -> None: ... @property def a(self) -> Tuple[float, float]: ... @a.setter @@ -177,10 +175,10 @@ class Line: @b.setter def b(self, value: Point) -> None: ... @overload - def __init__(self, xa: float, ya: float, xb: float, yb: float) -> None: ... + def __init__(self, ax: float, ay: float, bx: float, by: float) -> None: ... @overload def __init__(self, a: Point, b: Point) -> None: ... @overload def __init__(self, line: _LineValue) -> None: ... def __copy__(self) -> Line: ... - copy = __copy__ + def copy(self) -> Line: ... diff --git a/docs/reST/ref/geometry.rst b/docs/reST/ref/geometry.rst index 460b79157d..c6b3b99021 100644 --- a/docs/reST/ref/geometry.rst +++ b/docs/reST/ref/geometry.rst @@ -492,8 +492,8 @@ .. class:: Line | :sl:`pygame object for representing a line` - | :sg:`Line((xa, ya), (xb, yb)) -> Line` - | :sg:`Line(xa, ya, xb, yb) -> Line` + | :sg:`Line((ax, ay), (bx, by)) -> Line` + | :sg:`Line(ax, ay, bx, by) -> Line` .. versionadded:: 2.5.2 @@ -501,76 +501,83 @@ A `Line` can be created from a combination of two pairs of coordinates that represent the start and end points. Lines can also be created from python objects that are already a `Line` (effectively copying the line) or have an attribute named "line". - Specifically, to construct a line you can pass the xa, ya, xb, and yb values as separate arguments or inside a sequence(list or tuple). + Specifically, to construct a `Line` you can pass the ax, ay, bx, and by values as separate + arguments or inside a sequence(list or tuple). + + As a special case you can also pass in `pygame.Rect` / `pygame.FRect`, in which case the + line will be created with (x, y, width, height) as the start and end points. + + You can create lines with the same start and end points, but beware that some methods may + not work as expected or error out. Functions that require a `Line` argument may also accept these values as Lines: :: - ((xa, ya), (xb, yb)) - (xa, ya, xb, yb) + ((ax, ay), (bx, by)) + (ax, ay, bx, by) (vector2, vector2) - The `Line` class only stores the xa, ya, xb, and yb attributes, everything else is calculated + The `Line` class only stores the ax, ay, bx, and by attributes, everything else is calculated on the fly based on them. **Line Attributes** ---- - .. attribute:: xa + .. attribute:: ax | :sl:`x coordinate of the start point of the line` - | :sg:`xa -> float` + | :sg:`ax -> float` The horizontal coordinate of the start point of the line. Reassigning it moves the line. .. versionadded:: 2.5.2 - .. ## Line.xa ## + .. ## Line.ax ## - .. attribute:: ya + .. attribute:: ay | :sl:`y coordinate of the start point of the line` - | :sg:`ya -> float` + | :sg:`ay -> float` The vertical coordinate of the start point of the line. Reassigning it moves the line. .. versionadded:: 2.5.2 - .. ## Line.ya ## + .. ## Line.ay ## - .. attribute:: xb + .. attribute:: bx | :sl:`x coordinate of the end point of the line` - | :sg:`xb -> float` + | :sg:`bx -> float` The horizontal coordinate of the end point of the line. Reassigning it moves the line. .. versionadded:: 2.5.2 - .. ## Line.xb ## + .. ## Line.bx ## - .. attribute:: yb + .. attribute:: by | :sl:`y coordinate of the end point of the line` - | :sg:`yb -> float` + | :sg:`by -> float` The vertical coordinate of the end point of the line. Reassigning it moves the line. .. versionadded:: 2.5.2 - .. ## Line.yb ## + .. ## Line.by ## .. attribute:: a | :sl:`the first point of the line` | :sg:`a -> (float, float)` - It's a tuple containing the `xa` and `ya` attributes representing the line's first point. - It can be reassigned to move the `Line`. If reassigned the `xa` and `ya` attributes + It's a tuple containing the `ax` and `ay` attributes representing the line's first point. + It can be reassigned to move the `Line`. If reassigned the `ax` and `ay` attributes will be changed to produce a `Line` with matching first point position. - The `xb` and `yb` attributes will not be affected. + The `bx` and `by` attributes will not be affected. .. versionadded:: 2.5.2 @@ -581,10 +588,10 @@ | :sl:`the second point of the line` | :sg:`b -> (float, float)` - It's a tuple containing `xb` and `yb` attributes representing the line's second point. - It can be reassigned to move the `Line`. If reassigned the `xb` and `yb` attributes + It's a tuple containing `bx` and `by` attributes representing the line's second point. + It can be reassigned to move the `Line`. If reassigned the `bx` and `by` attributes will be changed to produce a `Line` with matching second point position. - The `xa` and `ya` attributes will not be affected. + The `ax` and `ay` attributes will not be affected. .. versionadded:: 2.5.2 diff --git a/src_c/doc/geometry_doc.h b/src_c/doc/geometry_doc.h index 62527a3efc..dc2b2a0f1a 100644 --- a/src_c/doc/geometry_doc.h +++ b/src_c/doc/geometry_doc.h @@ -29,11 +29,11 @@ #define DOC_CIRCLE_ASRECT "as_rect() -> Rect\nreturns the smallest Rect containing the circle" #define DOC_CIRCLE_ASFRECT "as_frect() -> FRect\nreturns the smallest FRect containing the circle" #define DOC_CIRCLE_COPY "copy() -> Circle\ncopies the circle" -#define DOC_LINE "Line((xa, ya), (xb, yb)) -> Line\nLine(xa, ya, xb, yb) -> Line\npygame object for representing a line" -#define DOC_LINE_XA "xa -> float\nx coordinate of the start point of the line" -#define DOC_LINE_YA "ya -> float\ny coordinate of the start point of the line" -#define DOC_LINE_XB "xb -> float\nx coordinate of the end point of the line" -#define DOC_LINE_YB "yb -> float\ny coordinate of the end point of the line" +#define DOC_LINE "Line((ax, ay), (bx, by)) -> Line\nLine(ax, ay, bx, by) -> Line\npygame object for representing a line" +#define DOC_LINE_AX "ax -> float\nx coordinate of the start point of the line" +#define DOC_LINE_AY "ay -> float\ny coordinate of the start point of the line" +#define DOC_LINE_BX "bx -> float\nx coordinate of the end point of the line" +#define DOC_LINE_BY "by -> float\ny coordinate of the end point of the line" #define DOC_LINE_A "a -> (float, float)\nthe first point of the line" #define DOC_LINE_B "b -> (float, float)\nthe second point of the line" #define DOC_LINE_COPY "copy() -> Line\ncopies the line" diff --git a/src_c/geometry.h b/src_c/geometry.h index f6adbb033e..6750409ced 100644 --- a/src_c/geometry.h +++ b/src_c/geometry.h @@ -18,8 +18,8 @@ typedef struct { #define pgCircle_Check(o) ((o)->ob_type == &pgCircle_Type) typedef struct { - double xa, ya; - double xb, yb; + double ax, ay; + double bx, by; } pgLineBase; typedef struct { diff --git a/src_c/geometry_common.c b/src_c/geometry_common.c index 4082a81f1e..4e25c32d9e 100644 --- a/src_c/geometry_common.c +++ b/src_c/geometry_common.c @@ -163,17 +163,17 @@ pgLine_FromObject(PyObject *obj, pgLineBase *out) PyObject **farray = PySequence_Fast_ITEMS(obj); if (length == 4) { - if (!pg_DoubleFromObj(farray[0], &out->xa) || - !pg_DoubleFromObj(farray[1], &out->ya) || - !pg_DoubleFromObj(farray[2], &out->xb) || - !pg_DoubleFromObj(farray[3], &out->yb)) { + if (!pg_DoubleFromObj(farray[0], &out->ax) || + !pg_DoubleFromObj(farray[1], &out->ay) || + !pg_DoubleFromObj(farray[2], &out->bx) || + !pg_DoubleFromObj(farray[3], &out->by)) { return 0; } return 1; } else if (length == 2) { - if (!pg_TwoDoublesFromObj(farray[0], &out->xa, &out->ya) || - !pg_TwoDoublesFromObj(farray[1], &out->xb, &out->yb)) { + if (!pg_TwoDoublesFromObj(farray[0], &out->ax, &out->ay) || + !pg_TwoDoublesFromObj(farray[1], &out->bx, &out->by)) { PyErr_Clear(); return 0; } @@ -192,25 +192,25 @@ pgLine_FromObject(PyObject *obj, pgLineBase *out) if (length == 4) { PyObject *tmp; tmp = PySequence_ITEM(obj, 0); - if (!pg_DoubleFromObj(tmp, &out->xa)) { + if (!pg_DoubleFromObj(tmp, &out->ax)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); tmp = PySequence_ITEM(obj, 1); - if (!pg_DoubleFromObj(tmp, &out->ya)) { + if (!pg_DoubleFromObj(tmp, &out->ay)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); tmp = PySequence_ITEM(obj, 2); - if (!pg_DoubleFromObj(tmp, &out->xb)) { + if (!pg_DoubleFromObj(tmp, &out->bx)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); tmp = PySequence_ITEM(obj, 3); - if (!pg_DoubleFromObj(tmp, &out->yb)) { + if (!pg_DoubleFromObj(tmp, &out->by)) { Py_DECREF(tmp); return 0; } @@ -220,13 +220,13 @@ pgLine_FromObject(PyObject *obj, pgLineBase *out) else if (length == 2) { PyObject *tmp; tmp = PySequence_ITEM(obj, 0); - if (!pg_TwoDoublesFromObj(tmp, &out->xa, &out->ya)) { + if (!pg_TwoDoublesFromObj(tmp, &out->ax, &out->ay)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); tmp = PySequence_ITEM(obj, 1); - if (!pg_TwoDoublesFromObj(tmp, &out->xb, &out->yb)) { + if (!pg_TwoDoublesFromObj(tmp, &out->bx, &out->by)) { Py_DECREF(tmp); return 0; } diff --git a/src_c/line.c b/src_c/line.c index c14d6b8150..93987e946d 100644 --- a/src_c/line.c +++ b/src_c/line.c @@ -2,16 +2,16 @@ #include "geometry_common.h" static PyObject * -_pg_line_subtype_new4(PyTypeObject *type, double xa, double ya, double xb, - double yb) +_pg_line_subtype_new4(PyTypeObject *type, double ax, double ay, double bx, + double by) { pgLineObject *line = (pgLineObject *)pgLine_Type.tp_new(type, NULL, NULL); if (line) { - line->line.xa = xa; - line->line.ya = ya; - line->line.xb = xb; - line->line.yb = yb; + line->line.ax = ax; + line->line.ay = ay; + line->line.bx = bx; + line->line.by = by; } return (PyObject *)line; } @@ -52,8 +52,8 @@ pg_line_init(pgLineObject *self, PyObject *args, PyObject *kwds) static PyObject * pg_line_copy(pgLineObject *self, PyObject *_null) { - return _pg_line_subtype_new4(Py_TYPE(self), self->line.xa, self->line.ya, - self->line.xb, self->line.yb); + return _pg_line_subtype_new4(Py_TYPE(self), self->line.ax, self->line.ay, + self->line.bx, self->line.by); } static struct PyMethodDef pg_line_methods[] = { @@ -64,38 +64,38 @@ static struct PyMethodDef pg_line_methods[] = { static PyObject * pg_line_repr(pgLineObject *self) { - PyObject *result, *xa, *ya, *xb, *yb; + PyObject *result, *ax, *ay, *bx, *by; - xa = PyFloat_FromDouble(self->line.xa); - if (!xa) { + ax = PyFloat_FromDouble(self->line.ax); + if (!ax) { return NULL; } - ya = PyFloat_FromDouble(self->line.ya); - if (!ya) { - Py_DECREF(xa); + ay = PyFloat_FromDouble(self->line.ay); + if (!ay) { + Py_DECREF(ax); return NULL; } - xb = PyFloat_FromDouble(self->line.xb); - if (!xb) { - Py_DECREF(xa); - Py_DECREF(ya); + bx = PyFloat_FromDouble(self->line.bx); + if (!bx) { + Py_DECREF(ax); + Py_DECREF(ay); return NULL; } - yb = PyFloat_FromDouble(self->line.yb); - if (!yb) { - Py_DECREF(xa); - Py_DECREF(ya); - Py_DECREF(xb); + by = PyFloat_FromDouble(self->line.by); + if (!by) { + Py_DECREF(ax); + Py_DECREF(ay); + Py_DECREF(bx); return NULL; } result = - PyUnicode_FromFormat("", xa, ya, xb, yb); + PyUnicode_FromFormat("", ax, ay, bx, by); - Py_DECREF(xa); - Py_DECREF(ya); - Py_DECREF(xb); - Py_DECREF(yb); + Py_DECREF(ax); + Py_DECREF(ay); + Py_DECREF(bx); + Py_DECREF(by); return result; } @@ -124,16 +124,16 @@ pg_line_str(pgLineObject *self) return -1; \ } -__LINE_GETSET_NAME(xa) -__LINE_GETSET_NAME(ya) -__LINE_GETSET_NAME(xb) -__LINE_GETSET_NAME(yb) +__LINE_GETSET_NAME(ax) +__LINE_GETSET_NAME(ay) +__LINE_GETSET_NAME(bx) +__LINE_GETSET_NAME(by) #undef __LINE_GETSET_NAME static PyObject * pg_line_geta(pgLineObject *self, void *closure) { - return pg_tuple_couple_from_values_double(self->line.xa, self->line.ya); + return pg_tuple_couple_from_values_double(self->line.ax, self->line.ay); } static int @@ -142,8 +142,8 @@ pg_line_seta(pgLineObject *self, PyObject *value, void *closure) double x, y; DEL_ATTR_NOT_SUPPORTED_CHECK_NO_NAME(value); if (pg_TwoDoublesFromObj(value, &x, &y)) { - self->line.xa = x; - self->line.ya = y; + self->line.ax = x; + self->line.ay = y; return 0; } PyErr_SetString(PyExc_TypeError, "Expected a sequence of 2 numbers"); @@ -153,7 +153,7 @@ pg_line_seta(pgLineObject *self, PyObject *value, void *closure) static PyObject * pg_line_getb(pgLineObject *self, void *closure) { - return pg_tuple_couple_from_values_double(self->line.xb, self->line.yb); + return pg_tuple_couple_from_values_double(self->line.bx, self->line.by); } static int @@ -162,8 +162,8 @@ pg_line_setb(pgLineObject *self, PyObject *value, void *closure) double x, y; DEL_ATTR_NOT_SUPPORTED_CHECK_NO_NAME(value); if (pg_TwoDoublesFromObj(value, &x, &y)) { - self->line.xb = x; - self->line.yb = y; + self->line.bx = x; + self->line.by = y; return 0; } PyErr_SetString(PyExc_TypeError, "Expected a sequence of 2 numbers"); @@ -171,10 +171,10 @@ pg_line_setb(pgLineObject *self, PyObject *value, void *closure) } static PyGetSetDef pg_line_getsets[] = { - {"xa", (getter)pg_line_getxa, (setter)pg_line_setxa, DOC_LINE_XA, NULL}, - {"ya", (getter)pg_line_getya, (setter)pg_line_setya, DOC_LINE_YA, NULL}, - {"xb", (getter)pg_line_getxb, (setter)pg_line_setxb, DOC_LINE_XB, NULL}, - {"yb", (getter)pg_line_getyb, (setter)pg_line_setyb, DOC_LINE_YB, NULL}, + {"ax", (getter)pg_line_getax, (setter)pg_line_setax, DOC_LINE_AX, NULL}, + {"ay", (getter)pg_line_getay, (setter)pg_line_setay, DOC_LINE_AY, NULL}, + {"bx", (getter)pg_line_getbx, (setter)pg_line_setbx, DOC_LINE_BX, NULL}, + {"by", (getter)pg_line_getby, (setter)pg_line_setby, DOC_LINE_BY, NULL}, {"a", (getter)pg_line_geta, (setter)pg_line_seta, DOC_LINE_A, NULL}, {"b", (getter)pg_line_getb, (setter)pg_line_setb, DOC_LINE_B, NULL}, {NULL, 0, NULL, NULL, NULL}}; diff --git a/test/geometry_test.py b/test/geometry_test.py index cc0ae5084b..d2b5fceb50 100644 --- a/test/geometry_test.py +++ b/test/geometry_test.py @@ -1676,53 +1676,53 @@ def testConstruction_invalid_type(self): """ invalid_types = (None, [], "1", (1,), [1, 2, 3], Vector2(1, 1)) - # Test xa + # Test ax for value in invalid_types: with self.assertRaises(TypeError): Line(value, 0, 1, 2) - # Test ya + # Test ay for value in invalid_types: with self.assertRaises(TypeError): Line(0, value, 1, 2) - # Test xb + # Test bx for value in invalid_types: with self.assertRaises(TypeError): Line(0, 0, value, 2) - # Test yb + # Test by for value in invalid_types: with self.assertRaises(TypeError): Line(0, 1, 2, value) - # Test xa + # Test ax for value in invalid_types: with self.assertRaises(TypeError): Line((value, 0), (1, 2)) - # Test ya + # Test ay for value in invalid_types: with self.assertRaises(TypeError): Line((0, value), (1, 2)) - # Test xb + # Test bx for value in invalid_types: with self.assertRaises(TypeError): Line((0, 0), (value, 2)) - # Test yb + # Test by for value in invalid_types: with self.assertRaises(TypeError): Line((0, 1), (2, value)) - # Test xa + # Test ax for value in invalid_types: with self.assertRaises(TypeError): Line(((value, 0), (1, 2))) - # Test ya + # Test ay for value in invalid_types: with self.assertRaises(TypeError): Line(((0, value), (1, 2))) - # Test xb + # Test bx for value in invalid_types: with self.assertRaises(TypeError): Line(((0, 0), (value, 2))) - # Test yb + # Test by for value in invalid_types: with self.assertRaises(TypeError): Line(((0, 1), (2, value))) @@ -1742,41 +1742,41 @@ def testConstruction_invalid_arguments_number(self): with self.assertRaises(TypeError): Line(*arg_seq) - def testConstructionXAYAXBYB_float(self): + def testConstructionaxabyxby_float(self): """Tests the construction of a line with 4 float arguments""" line = Line(1.0, 2.0, 3.0, 4.0) - self.assertEqual(line.xa, 1.0) - self.assertEqual(line.ya, 2.0) - self.assertEqual(line.xb, 3.0) - self.assertEqual(line.yb, 4.0) + self.assertEqual(line.ax, 1.0) + self.assertEqual(line.ay, 2.0) + self.assertEqual(line.bx, 3.0) + self.assertEqual(line.by, 4.0) - def testConstructionTUP_XAYAXBYB_float(self): + def testConstructionTUP_axabyxby_float(self): """Tests the construction of a line with a tuple of 4 float arguments""" line = Line((1.0, 2.0, 3.0, 4.0)) - self.assertEqual(line.xa, 1.0) - self.assertEqual(line.ya, 2.0) - self.assertEqual(line.xb, 3.0) - self.assertEqual(line.yb, 4.0) + self.assertEqual(line.ax, 1.0) + self.assertEqual(line.ay, 2.0) + self.assertEqual(line.bx, 3.0) + self.assertEqual(line.by, 4.0) - def testConstructionXAYAXBYB_int(self): + def testConstructionaxabyxby_int(self): """Tests the construction of a line with 4 int arguments""" line = Line(1, 2, 3, 4) - self.assertEqual(line.xa, 1.0) - self.assertEqual(line.ya, 2.0) - self.assertEqual(line.xb, 3.0) - self.assertEqual(line.yb, 4.0) + self.assertEqual(line.ax, 1.0) + self.assertEqual(line.ay, 2.0) + self.assertEqual(line.bx, 3.0) + self.assertEqual(line.by, 4.0) - def testConstructionTUP_XAYAXBYB_int(self): + def testConstructionTUP_axabyxby_int(self): """Tests the construction of a line with a tuple of 4 int arguments""" line = Line((1, 2, 3, 4)) - self.assertEqual(line.xa, 1.0) - self.assertEqual(line.ya, 2.0) - self.assertEqual(line.xb, 3.0) - self.assertEqual(line.yb, 4.0) + self.assertEqual(line.ax, 1.0) + self.assertEqual(line.ay, 2.0) + self.assertEqual(line.bx, 3.0) + self.assertEqual(line.by, 4.0) def testConstruction_class_with_line_attrib(self): """Tests the construction of a line with a class that has a line attribute""" @@ -1784,10 +1784,10 @@ def testConstruction_class_with_line_attrib(self): line = Line(class_) - self.assertEqual(line.xa, 1.1) - self.assertEqual(line.ya, 2.2) - self.assertEqual(line.xb, 3.3) - self.assertEqual(line.yb, 4.4) + self.assertEqual(line.ax, 1.1) + self.assertEqual(line.ay, 2.2) + self.assertEqual(line.bx, 3.3) + self.assertEqual(line.by, 4.4) def testConstruction_class_with_line_property(self): """Tests the construction of a line with a class that has a line property""" @@ -1795,10 +1795,10 @@ def testConstruction_class_with_line_property(self): line = Line(class_) - self.assertEqual(line.xa, 1.1) - self.assertEqual(line.ya, 2.2) - self.assertEqual(line.xb, 3.3) - self.assertEqual(line.yb, 4.4) + self.assertEqual(line.ax, 1.1) + self.assertEqual(line.ay, 2.2) + self.assertEqual(line.bx, 3.3) + self.assertEqual(line.by, 4.4) def testConstruction_class_with_line_function(self): """Tests the construction of a line with a class that has a line function""" @@ -1806,117 +1806,117 @@ def testConstruction_class_with_line_function(self): line = Line(class_) - self.assertEqual(line.xa, 1.1) - self.assertEqual(line.ya, 2.2) - self.assertEqual(line.xb, 3.3) - self.assertEqual(line.yb, 4.4) + self.assertEqual(line.ax, 1.1) + self.assertEqual(line.ay, 2.2) + self.assertEqual(line.bx, 3.3) + self.assertEqual(line.by, 4.4) def test_attrib_x1(self): - """a full test for the xa attribute""" + """a full test for the ax attribute""" expected_x1 = 10.0 expected_y1 = 2.0 expected_x2 = 5.0 expected_y2 = 6.0 line = Line(1, expected_y1, expected_x2, expected_y2) - line.xa = expected_x1 + line.ax = expected_x1 - self.assertEqual(line.xa, expected_x1) - self.assertEqual(line.ya, expected_y1) - self.assertEqual(line.xb, expected_x2) - self.assertEqual(line.yb, expected_y2) + self.assertEqual(line.ax, expected_x1) + self.assertEqual(line.ay, expected_y1) + self.assertEqual(line.bx, expected_x2) + self.assertEqual(line.by, expected_y2) line = Line(0, 0, 1, 0) for value in (None, [], "1", (1,), [1, 2, 3]): with self.assertRaises(TypeError): - line.xa = value + line.ax = value line = Line(0, 0, 1, 0) with self.assertRaises(AttributeError): - del line.xa + del line.ax def test_attrib_y1(self): - """a full test for the ya attribute""" + """a full test for the ay attribute""" expected_x1 = 10.0 expected_y1 = 2.0 expected_x2 = 5.0 expected_y2 = 6.0 line = Line(expected_x1, 1, expected_x2, expected_y2) - line.ya = expected_y1 + line.ay = expected_y1 - self.assertEqual(line.xa, expected_x1) - self.assertEqual(line.ya, expected_y1) - self.assertEqual(line.xb, expected_x2) - self.assertEqual(line.yb, expected_y2) + self.assertEqual(line.ax, expected_x1) + self.assertEqual(line.ay, expected_y1) + self.assertEqual(line.bx, expected_x2) + self.assertEqual(line.by, expected_y2) line = Line(0, 0, 1, 0) for value in (None, [], "1", (1,), [1, 2, 3]): with self.assertRaises(TypeError): - line.ya = value + line.ay = value line = Line(0, 0, 1, 0) with self.assertRaises(AttributeError): - del line.ya + del line.ay def test_attrib_x2(self): - """a full test for the ya attribute""" + """a full test for the ay attribute""" expected_x1 = 10.0 expected_y1 = 2.0 expected_x2 = 5.0 expected_y2 = 6.0 line = Line(expected_x1, expected_y1, 1, expected_y2) - line.xb = expected_x2 + line.bx = expected_x2 - self.assertEqual(line.xa, expected_x1) - self.assertEqual(line.ya, expected_y1) - self.assertEqual(line.xb, expected_x2) - self.assertEqual(line.yb, expected_y2) + self.assertEqual(line.ax, expected_x1) + self.assertEqual(line.ay, expected_y1) + self.assertEqual(line.bx, expected_x2) + self.assertEqual(line.by, expected_y2) line = Line(0, 0, 1, 0) for value in (None, [], "1", (1,), [1, 2, 3]): with self.assertRaises(TypeError): - line.xb = value + line.bx = value line = Line(0, 0, 1, 0) with self.assertRaises(AttributeError): - del line.xb + del line.bx def test_attrib_y2(self): - """a full test for the ya attribute""" + """a full test for the ay attribute""" expected_x1 = 10.0 expected_y1 = 2.0 expected_x2 = 5.0 expected_y2 = 6.0 line = Line(expected_x1, expected_y1, expected_x2, 1) - line.yb = expected_y2 + line.by = expected_y2 - self.assertEqual(line.xa, expected_x1) - self.assertEqual(line.ya, expected_y1) - self.assertEqual(line.xb, expected_x2) - self.assertEqual(line.yb, expected_y2) + self.assertEqual(line.ax, expected_x1) + self.assertEqual(line.ay, expected_y1) + self.assertEqual(line.bx, expected_x2) + self.assertEqual(line.by, expected_y2) line = Line(0, 0, 1, 0) for value in (None, [], "1", (1,), [1, 2, 3]): with self.assertRaises(TypeError): - line.yb = value + line.by = value line = Line(0, 0, 1, 0) with self.assertRaises(AttributeError): - del line.yb + del line.by def test_attrib_a(self): - """a full test for the ya attribute""" + """a full test for the ay attribute""" expected_x1 = 10.0 expected_y1 = 2.0 expected_x2 = 5.0 @@ -1942,7 +1942,7 @@ def test_attrib_a(self): del line.a def test_attrib_b(self): - """a full test for the ya attribute""" + """a full test for the ay attribute""" expected_x1 = 10.0 expected_y1 = 2.0 expected_x2 = 5.0 @@ -1974,10 +1974,10 @@ def test_meth_copy(self): line.copy(10) line_2 = line.copy() - self.assertEqual(line.xa, line_2.xa) - self.assertEqual(line.yb, line_2.yb) - self.assertEqual(line.xb, line_2.xb) - self.assertEqual(line.yb, line_2.yb) + self.assertEqual(line.ax, line_2.ax) + self.assertEqual(line.by, line_2.by) + self.assertEqual(line.bx, line_2.bx) + self.assertEqual(line.by, line_2.by) self.assertIsNot(line, line_2)