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

new angle and angle_rad property in vector2 #3222

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

AntoineMamou
Copy link
Contributor

This PR follows this one : #3216

I make the changes needed and add two properties :
angle_rad : Returns the vector’s angle in radians relative to the positive x-axis.
angle : Returns the angle in degrees, normalized to (180,180].

@AntoineMamou AntoineMamou requested a review from a team as a code owner November 12, 2024 17:43
Copy link
Member

@oddbookworm oddbookworm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs documentation, and there are a few issues I see offhand. But thanks for submitting! 🎉

src_c/math.c Outdated
pgVector *vec = (pgVector *)self;

if (vec->coords[0] == 0.0 && vec->coords[1] == 0.0) {
return PyFloat_FromDouble(0.0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d rather an exception get raised here. The zero vector is special and getting it’s angle makes no sense

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I thought it should be 0 because of the initial issue #3195, but I can change and raise an exception

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oddbookworm
I strongly think the angle properties should be consistent with the existing behavior of .as_polar(), .angle_to(), .rotate() and more importantly python math.atan2() (which is based off the a C standard) and polar coordinates.
These functions don't raise errors and (for not nan args) don't return nan, and most actually do not document this special case.

Copy link
Member

@oddbookworm oddbookworm Nov 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't agree that we should silently just say the angle of the zero vector is zero. I don't like those functions treating that special case in the way they do either. And actually, the C standard does not guarantee that atan2(0, 0) returns 0. Here's a snippet from the C standard
image

link to standard I used

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One reason I hate that behavior is because it can lead to very confusing behavior. Consider the case where you're just continuously scaling down a vector without changing it's direction. At some point, you'll go from a constant angle to 0, regardless of what the starting angle is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @aatle and @oddbookworm,
I saw on discord that there is many vision for what should be done for the zero vector. And I wanted to know if people agree on one of these in order to maybe change my code

Copy link
Contributor

@aatle aatle Nov 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @AntoineMamou, it has been settled that we will imitate python's math.atan2() function behavior. (Returns certain value depending on sign of zeroes, for zero vector.)
See https://github.com/python/cpython/blob/ed81971e6b26c34445f06850192b34458b029337/Lib/test/test_math.py#L327 for the relevant test cases.
Here's the old CPython 3.7.2 implementation for special cases: https://github.com/python/cpython/blob/v3.7.2/Modules/mathmodule.c#L566-L600
Recent CPython relies on a library, with atan2 implemented here:
https://github.com/rust-lang/libm/blob/master/src/math/atan2.rs#L20-L31

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @aatle, so if we want to imitate math.atan2(), for the case where x=y=0, it should return 0, right ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I'm using atan2 to find the angle, so basically I can skip the if statement ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something here

src_c/math.c Outdated
static PyObject *
vector_get_angle(pgVector *self, void *closure)
{
pgVector *vec = (pgVector *)self;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this cast to the type it already is?

src_c/math.c Outdated

double angle = atan2(vec->coords[1], vec->coords[0]) * 180.0 / M_PI;

if (angle > 180.0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make constants instead of reusing the same hardcoded numbers

src_c/math.c Outdated
}

static PyObject *
vector_get_angle_rad(pgVector *self, void *closure)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just reuse the function you already have instead of writing the same code twice. Since you already got the output in a form you like, just convert it from degrees to radians

src_c/math.c Outdated
@@ -2585,6 +2631,8 @@ static PyMethodDef vector2_methods[] = {
static PyGetSetDef vector2_getsets[] = {
{"x", (getter)vector_getx, (setter)vector_setx, NULL, NULL},
{"y", (getter)vector_gety, (setter)vector_sety, NULL, NULL},
{"angle", (getter)vector_get_angle, NULL, NULL, NULL},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing the docs component of these structs, which should be generated in the docs header, which is generated from the documentation updates you need to make

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to make a doc in the file math.rst, right ? I'm not understanding everything about the doc yet. But I'm working on it !

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

@yunline yunline added New API This pull request may need extra debate as it adds a new class or function to pygame math pygame.math labels Nov 13, 2024
@AntoineMamou
Copy link
Contributor Author

I see that two checks were not successful but I don't understand why. Can someone help me ?

Comment on lines 224 to 225
angle: float
angle_rad: float
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
angle: float
angle_rad: float
@property
def angle(self) -> float: ...
@property
def angle_rad(self) -> float: ...

These are properties and they are read-only.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@property doesn't say that it's a read-only attribute, right ?

| :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].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My mistake, the interval is actually inclusive of -180. So, [-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].

Usage: Accessing `angle` provides the current angle of the vector in degrees within the specified range.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reword "specified range". The word 'specified' implies that the user provides it.

src_c/math.c Outdated
Comment on lines 1285 to 1290
if (angle_deg > 180.0) {
angle_deg -= 360.0;
}
else if (angle_deg <= -180.0) {
angle_deg += 360.0;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems unnecessary. The radians is within [-pi, pi], so the degrees will be within [-180, 180].

src_c/math.c Outdated
}

static PyObject *
vector_get_angle_rad(pgVector *self, void *closure)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest creating a helper function _pg_atan2 that takes doubles and returns double. That way, one doesn't have to convert back to double and then to pyobject when doing more operations on it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where I should make the documentations for _pg_atan2 ?

Copy link
Contributor

@aatle aatle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.
Thanks for implementing my feature request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
math pygame.math New API This pull request may need extra debate as it adds a new class or function to pygame
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants