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

Added geometry.Line base #3131

Merged
merged 12 commits into from
Oct 12, 2024
Merged
53 changes: 51 additions & 2 deletions buildconfig/stubs/pygame/geometry.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,34 @@ 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[
damusss marked this conversation as resolved.
Show resolved Hide resolved
Rect,
FRect,
Line,
Coordinate,
SequenceLike[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):
MyreMylar marked this conversation as resolved.
Show resolved Hide resolved
# 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]

Expand Down Expand Up @@ -115,3 +130,37 @@ class Circle:
def as_frect(self) -> FRect: ...
def __copy__(self) -> Circle: ...
copy = __copy__
MyreMylar marked this conversation as resolved.
Show resolved Hide resolved

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, xa: float, ya: float, xb: float, yb: float) -> None: ...
@overload
def __init__(self, a: Coordinate, b: Coordinate) -> None: ...
@overload
def __init__(self, line: _LineValue) -> None: ...
def __copy__(self) -> Line: ...
copy = __copy__
itzpr3d4t0r marked this conversation as resolved.
Show resolved Hide resolved
120 changes: 120 additions & 0 deletions docs/reST/ref/geometry.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
itzpr3d4t0r marked this conversation as resolved.
Show resolved Hide resolved
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 ##
2 changes: 1 addition & 1 deletion src_c/_pygame.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
8 changes: 8 additions & 0 deletions src_c/doc/geometry_doc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
13 changes: 13 additions & 0 deletions src_c/geometry.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "circle.c"
#include "line.c"
#include "geometry_common.c"

static PyMethodDef geometry_methods[] = {{NULL, NULL, 0, NULL}};
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down
22 changes: 22 additions & 0 deletions src_c/geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
Loading
Loading