From 23f426f7181081fa37ab2f3a9e774fb2948efa34 Mon Sep 17 00:00:00 2001 From: AntoineMamou Date: Sat, 23 Nov 2024 18:42:02 +0100 Subject: [PATCH] adding helper function _pg_atan2 + docs modification --- buildconfig/stubs/pygame/math.pyi | 6 ++- docs/reST/ref/math.rst | 12 ++--- src_c/doc/math_doc.h | 4 +- src_c/math.c | 82 +++++++++++++++++-------------- 4 files changed, 56 insertions(+), 48 deletions(-) diff --git a/buildconfig/stubs/pygame/math.pyi b/buildconfig/stubs/pygame/math.pyi index b08b7b6b51..97fb7155dd 100644 --- a/buildconfig/stubs/pygame/math.pyi +++ b/buildconfig/stubs/pygame/math.pyi @@ -221,8 +221,10 @@ class Vector2(_GenericVector): xy: Vector2 yx: Vector2 yy: Vector2 - angle: float - angle_rad: float + @property + def angle(self) -> float: ... + @property + def angle_rad(self) -> float: ... @overload def __init__( self: _TVec, diff --git a/docs/reST/ref/math.rst b/docs/reST/ref/math.rst index fb7db3cadf..0580c5ee81 100644 --- a/docs/reST/ref/math.rst +++ b/docs/reST/ref/math.rst @@ -618,21 +618,21 @@ Multiple coordinates can be set using slices or swizzling .. attribute:: angle - | :sl:`Gives the angle of the vector in degrees, relative to the X-axis, normalized to the interval (-180, 180].` + | :sl:`Gives the angle of the vector in degrees, relative to the X-axis, normalized to the interval [-180, 180].` Read-only attribute representing the angle of the vector in degrees relative to the X-axis. This angle is normalized to - the interval (-180, 180]. + the interval [-180, 180]. - Usage: Accessing `angle` provides the current angle of the vector in degrees within the specified range. + Usage: Accessing `angle` provides the current angle of the vector in degrees within the predefined range of [-180, 180]. .. attribute:: angle_rad - | :sl:`Gives the angle of the vector in radians, relative to the X-axis, normalized to the interval (-π, π].` + | :sl:`Gives the angle of the vector in radians, relative to the X-axis, normalized to the interval [-π, π].` Read-only attribute representing the angle of the vector in radians relative to the X-axis. This value is equivalent - to the `angle` attribute converted to radians and is normalized to the interval (-π, π]. + to the `angle` attribute converted to radians and is normalized to the interval [-π, π]. - Usage: Accessing `angle_rad` provides the current angle of the vector in radians within the specified range. + Usage: Accessing `angle_rad` provides the current angle of the vector in radians within the predefined range of [-π, π]. .. ## pygame.math.Vector2 ## diff --git a/src_c/doc/math_doc.h b/src_c/doc/math_doc.h index 75f06f4a5e..548465487d 100644 --- a/src_c/doc/math_doc.h +++ b/src_c/doc/math_doc.h @@ -40,8 +40,8 @@ #define DOC_MATH_VECTOR2_CLAMPMAGNITUDEIP "clamp_magnitude_ip(max_length, /) -> None\nclamp_magnitude_ip(min_length, max_length, /) -> None\nClamps the vector's magnitude between max_length and min_length" #define DOC_MATH_VECTOR2_UPDATE "update() -> None\nupdate(int) -> None\nupdate(float) -> None\nupdate(Vector2) -> None\nupdate(x, y) -> None\nupdate((x, y)) -> None\nSets the coordinates of the vector." #define DOC_MATH_VECTOR2_EPSILON "Determines the tolerance of vector calculations." -#define DOC_MATH_VECTOR2_ANGLE "Gives the angle of the vector in degrees, relative to the X-axis, normalized to the interval (-180, 180]." -#define DOC_MATH_VECTOR2_ANGLERAD "Gives the angle of the vector in radians, relative to the X-axis, normalized to the interval (-π, π]." +#define DOC_MATH_VECTOR2_ANGLE "Gives the angle of the vector in degrees, relative to the X-axis, normalized to the interval [-180, 180]." +#define DOC_MATH_VECTOR2_ANGLERAD "Gives the angle of the vector in radians, relative to the X-axis, normalized to the interval [-π, π]." #define DOC_MATH_VECTOR3 "Vector3() -> Vector3(0, 0, 0)\nVector3(int) -> Vector3\nVector3(float) -> Vector3\nVector3(Vector3) -> Vector3\nVector3(x, y, z) -> Vector3\nVector3((x, y, z)) -> Vector3\na 3-Dimensional Vector" #define DOC_MATH_VECTOR3_DOT "dot(Vector3, /) -> float\ncalculates the dot- or scalar-product with the other vector" #define DOC_MATH_VECTOR3_CROSS "cross(Vector3, /) -> Vector3\ncalculates the cross- or vector-product" diff --git a/src_c/math.c b/src_c/math.c index 4025b7bfc9..10238b6b6b 100644 --- a/src_c/math.c +++ b/src_c/math.c @@ -145,6 +145,9 @@ _vector_coords_from_string(PyObject *str, char **delimiter, double *coords, static void _vector_move_towards_helper(Py_ssize_t dim, double *origin_coords, double *target_coords, double max_distance); +static double +_pg_atan2(double y, double x); + /* generic vector functions */ static PyObject * pgVector_NEW(Py_ssize_t dim); @@ -637,6 +640,40 @@ vector_dealloc(pgVector *self) Py_TYPE(self)->tp_free((PyObject *)self); } +/* + *Returns rhe arctangent of the quotient y / x, in radians, considering the + *following special cases: atan2((anything), NaN ) is NaN; atan2(NAN , + *(anything) ) is NaN; atan2(+-0, +(anything but NaN)) is +-0 ; atan2(+-0, + *-(anything but NaN)) is +-pi ; atan2(+-(anything but 0 and NaN), 0) is + *+-pi/2; atan2(+-(anything but INF and NaN), +INF) is +-0 ; atan2(+-(anything + *but INF and NaN), -INF) is +-pi; atan2(+-INF,+INF ) is +-pi/4 ; + * atan2(+-INF,-INF ) is +-3pi/4; + * atan2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2; + * + */ +static double +_pg_atan2(double y, double x) +{ + if (Py_IS_NAN(x) || Py_IS_NAN(y)) { + return Py_NAN; + } + + if (Py_IS_INFINITY(y)) { + if (Py_IS_INFINITY(x)) { + return copysign((copysign(1., x) == 1.) ? 0.25 * Py_MATH_PI + : 0.75 * Py_MATH_PI, + y); + } + return copysign(0.5 * Py_MATH_PI, y); + } + + if (Py_IS_INFINITY(x) || y == 0.) { + return copysign((copysign(1., x) == 1.) ? 0. : Py_MATH_PI, y); + } + + return atan2(y, x); +} + /********************************************** * Generic vector PyNumber emulation routines **********************************************/ @@ -1276,51 +1313,20 @@ vector_setz(pgVector *self, PyObject *value, void *closure) } static PyObject * -vector_get_angle(pgVector *self, void *closure) +vector_get_angle_rad(pgVector *self, void *closure) { - PyObject *angle_obj = vector_get_angle_rad(self, closure); - double angle_rad = PyFloat_AsDouble(angle_obj); - double angle_deg = angle_rad * RAD_TO_DEG; - - if (angle_deg > 180.0) { - angle_deg -= 360.0; - } - else if (angle_deg <= -180.0) { - angle_deg += 360.0; - } + double angle_rad = _pg_atan2(self->coords[1], self->coords[0]); - return PyFloat_FromDouble(angle_deg); + return PyFloat_FromDouble(angle_rad); } static PyObject * -vector_get_angle_rad(pgVector *self, void *closure) +vector_get_angle(pgVector *self, void *closure) { - pgVector *vec = self; - double x = vec->coords[0]; - double y = vec->coords[1]; - - if (Py_IS_NAN(x) || Py_IS_NAN(y)) { - return PyFloat_FromDouble(Py_NAN); - } - - if (Py_IS_INFINITY(y)) { - if (Py_IS_INFINITY(x)) { - if (copysign(1., x) == 1.) - return PyFloat_FromDouble(copysign(0.25 * Py_MATH_PI, y)); - else - return PyFloat_FromDouble(copysign(0.75 * Py_MATH_PI, y)); - } - return PyFloat_FromDouble(copysign(0.5 * Py_MATH_PI, y)); - } - - if (Py_IS_INFINITY(x) || y == 0.) { - if (copysign(1., x) == 1.) - return PyFloat_FromDouble(copysign(0., y)); - else - return PyFloat_FromDouble(copysign(Py_MATH_PI, y)); - } + double angle_rad = _pg_atan2(self->coords[1], self->coords[0]); + double angle_deg = angle_rad * RAD_TO_DEG; - return PyFloat_FromDouble(atan2(y, x)); + return PyFloat_FromDouble(angle_deg); } static PyObject *