Skip to content

Conversation

@lilizoey
Copy link
Member

@lilizoey lilizoey commented Feb 16, 2023

Add bool as glam type
Add glamconv/glamtype for Vector2/3/4
Add assert_eq/ne_approx
Add lerp_angle

Split from #124

@Bromeon Bromeon added quality-of-life No new functionality, but improves ergonomics/internals c: core Core components labels Feb 16, 2023
Copy link
Member

@Bromeon Bromeon left a comment

Choose a reason for hiding this comment

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

Thanks for the split!

Are the Index<usize> and IndexMut<usize> traits really necessary? We have already Index<Vector2Axis>, and the regular .x/.y/.z access. I don't want to provide 3 ways to achieve the same thing unless there's a very good reason.

If this is truly needed, we should look at the concrete use case. E.g. if it's for a for loop, we could provide something like Vector2Axis::values() instead.

Comment on lines 58 to 59
($(,)?) => {};
($($T:ty),+ $(,)?) => {$(
Copy link
Member

Choose a reason for hiding this comment

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

What's the reason behind this? I'd rather repeat the macro invocation than making the macro more complex.

Repetition patterns are mostly useful for token ranges inside the macro body, not for repeating the entire macro.

Copy link
Member Author

Choose a reason for hiding this comment

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

i can change it if you want, but it's not that uncommon afaik. the standard library does it a fair bit, see for instance https://doc.rust-lang.org/src/core/cmp.rs.html#1370.

Copy link
Member

Choose a reason for hiding this comment

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

Interesting, thanks for that link! 👍

I think it's easier if we use separate calls though, as we do that already in several other places (the impl macros for vectors, From/ToVariant, etc)

Comment on lines 126 to 127
let difference = (to - from) % (2.0 * PI);
let distance = (2.0 * difference) % (2.0 * PI) - difference;
Copy link
Member

Choose a reason for hiding this comment

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

See TAU

Comment on lines +380 to +367
impl GlamType for Vec3A {
type Mapped = Vector3;

fn to_front(&self) -> Self::Mapped {
Vector3::new(self.x, self.y, self.z)
}

fn from_front(mapped: &Self::Mapped) -> Self {
Vec3A::new(mapped.x, mapped.y, mapped.z)
}
}
Copy link
Member

Choose a reason for hiding this comment

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

We are not using Vec3A at the moment, so this is dead code.

Copy link
Member Author

@lilizoey lilizoey Feb 16, 2023

Choose a reason for hiding this comment

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

we will be because the Transform3D will use Affine3A, since there is no Affine3, which uses Vec3A internally

Copy link
Member

Choose a reason for hiding this comment

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

Ok, we then need to see if Vec3A <-> Vector3 conversions are not necessary all over the place 🤔 otherwise we might not gain much from using glam. But I guess it's a good start.

}
}

impl super::glam_helpers::GlamConv for Vector3 {
Copy link
Member

Choose a reason for hiding this comment

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

GlamConv could be imported.

Comment on lines 7 to 10
use std::{
fmt,
ops::{Index, IndexMut},
};
Copy link
Member

Choose a reason for hiding this comment

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

IIRC we don't use this import style elsewhere, maybe keep it flat for now (i.e. two use statements).

I think there are nightly rustfmt configs to tweak that, but so far we don't have automation on this 🙂

Copy link
Member Author

Choose a reason for hiding this comment

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

i didn't do this manually so i think this is something rust-analyzer + rustfmt concocted

@lilizoey
Copy link
Member Author

lilizoey commented Feb 16, 2023

Are the Index and IndexMut traits really necessary?

The main usecase is to index into matrices like mat[0][2] instead of something like mat[0].z or mat.x.z.

As an example:

pub fn transposed(self) -> Self {
    Self {
        rows: [
            Vector3::new(self[0][0], self[1][0], self[2][0]),
            Vector3::new(self[0][1], self[1][1], self[2][1]),
            Vector3::new(self[0][2], self[1][2], self[2][2]),
        ],
    }
}

@Bromeon
Copy link
Member

Bromeon commented Feb 17, 2023

The main usecase is to index into matrices like mat[0][2] instead of something like mat[0].z or mat.x.z.

In this case you only use literals, so you don't benefit from having the numbers (except for syntactical reasons). For example, the code you showed adds 9 extra bound checks -- which might be optimized away, or might not.

If we really need random access syntax later, we can still consider something at the matrix level. But a general philosophy I'd like to follow is to start with a slim, non-redundant API and only expand when there is a significant use case that cannot be easily solved with existing APIs🙂

Regarding transposed() in particular, we could also give access to column vectors like in GDNative's Basis:

pub fn transposed(self) -> Self {
    Self {
        rows: [self.a(), self.b(), self.c()]
    }
}

@lilizoey
Copy link
Member Author

I think i just find myself often getting frustrated when i'm not able to just index normally into things that are just lists of homogeneous values. Like i can't really think of a case from the std where something that is mentally just a list of values doesn't have a way to index into them without a good reason. Closest are probably tuples, but there it's just .index instead of [index], and also tuples are heterogeneous. String also doesn't have straightforward indexing, but that's because of UTF-8. Glam itself has indexing defined for its vectors too.

I'll remove them for now but that's where it comes from i think.

For matrices at least i think we should have indexing though, mainly because godot does it.

@lilizoey lilizoey force-pushed the glam-vector-tweaks branch 2 times, most recently from 293fe3f to e1aaa3f Compare February 17, 2023 17:34
@Bromeon
Copy link
Member

Bromeon commented Feb 17, 2023

I think i just find myself often getting frustrated when i'm not able to just index normally into things that are just lists of homogeneous values. Like i can't really think of a case from the std where something that is mentally just a list of values doesn't have a way to index into them without a good reason.

But a 2D/3D vector is not just a list of values -- it's a geometric object.

Slices in Rust are located at a much lower abstraction level. Nothing would prevent us from modeling Vector3 as [f32; 3] + extension trait, but it's impractical and less intuitive.

And there is a good reason. I mentioned two of them already, but there are more:

  • you don't pay for bound checks when you don't need them. *
  • you don't have 3x redundant APIs v.x, v[Axis::X] and v[0]. 2x is enough, I think.
  • accessing via the other 2 ways does not put you at risk of panic, i.e. is more type-safe.

I understand the desire to have that syntax, but for the use case described it seems to be based mostly on syntactical preference.


* Edit: we conducted some further analysis on performance in this Godbolt example. With #[inline] and in release mode (rustc -O), the index access is optimized away indeed. Without #[inline] the function is retained in the binary but still inlined, this might not work across crate boundaries though.

Copy link
Member

@Bromeon Bromeon left a comment

Choose a reason for hiding this comment

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

Thanks for the update!

bors try

Comment on lines +125 to +145
pub fn lerp_angle(from: f32, to: f32, weight: f32) -> f32 {
let difference = (to - from) % TAU;
let distance = (2.0 * difference) % TAU - difference;
from + distance * weight
}
Copy link
Member

Choose a reason for hiding this comment

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

Is this wrapping correctly?
Is the output in [0, TAU] or [-PI, PI] range or something else?

Some docs would be nice 🙂

Copy link
Member Author

Choose a reason for hiding this comment

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

i just copied the code from godot, so i didn't super look into it closely.
https://github.com/godotengine/godot/blob/master/core/math/math_funcs.h#L389

bors bot added a commit that referenced this pull request Feb 19, 2023
@bors
Copy link
Contributor

bors bot commented Feb 19, 2023

try

Build succeeded:

Add glamconv/glamtype for Vector2/3/4
Add lerp_angle
Add assert_eq_approx/assert_ne_approx
@Bromeon
Copy link
Member

Bromeon commented Feb 19, 2023

Thanks for the docs! Is it necessary to always pass is_equal_approx to the macro, due to macro hygiene? 🤔

@lilizoey
Copy link
Member Author

Thanks for the docs! Is it necessary to always pass is_equal_approx to the macro, due to macro hygiene? thinking

If not it wont know what function to use. We could make it use the one for floats by default, but i decided not to.

The main alternative would be to create a trait for is_equal_approx and have the macro use that, similar to how assert_eq uses PartialEq. But that'd require a lot more work than this current impl. And it'd likely be better to just use approx at that point.

@Bromeon
Copy link
Member

Bromeon commented Feb 19, 2023

If not it wont know what function to use. We could make it use the one for floats by default, but i decided not to.

But it's only about the name, no? As long as an is_equal_approx function is in scope, it can call that.
Since it's a macro, it can do "poor man's ducktyping" and a trait is not really needed.

@lilizoey
Copy link
Member Author

If not it wont know what function to use. We could make it use the one for floats by default, but i decided not to.

But it's only about the name, no? As long as an is_equal_approx function is in scope, it can call that. Since it's a macro, it can do "poor man's ducktyping" and a trait is not really needed.

Not really, because the is_equal_approx function for f32 isn't implemented on f32 but is a standalone function, whereas for all the other types it is an inherent function.

@Bromeon
Copy link
Member

Bromeon commented Feb 24, 2023

Thanks!

bors r+

@bors
Copy link
Contributor

bors bot commented Feb 24, 2023

Build succeeded:

@bors bors bot merged commit b3b9e0e into godot-rust:master Feb 24, 2023
bors bot added a commit that referenced this pull request Mar 4, 2023
124: Implement the matrix types r=Bromeon a=sayaks

Implement the four matrix types godot has: `Basis`, `Transform2D`, `Transform3D`, `Projection`.

`Transform2D` in godot consists of an array of 3 `Vector2`s, where the first two are the basis.
This means godot has some methods named `Transform2D::basis_<something>()`. I
decided to instead make a struct `Basis2D`. so now any method like the above would be
called `trans.basis.<something>()` instead.

`Projection` has a lot of intricate math for many of its functions so it is largely
a wrapper around `InnerProjection`, except for some methods that were
easy to reimplement.

Depends on #128 

Co-authored-by: Lili Zoey <[email protected]>
@lilizoey lilizoey deleted the glam-vector-tweaks branch April 16, 2023 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c: core Core components quality-of-life No new functionality, but improves ergonomics/internals

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants