rustgd / cgmath

A linear algebra and mathematics library for computer graphics.
https://docs.rs/cgmath
Apache License 2.0
1.12k stars 155 forks source link

Now there is only one one #513

Closed hayashi-stl closed 4 years ago

hayashi-stl commented 4 years ago

This fixes the multiple one found issue (one function mentioned in #354) with attempting to use Matrix4::one() or some other matrix's one() function when importing the prelude, which includes both Transform and One. The changes:

I know there was an attempt to do it in #386, but it was abandoned and closed.

There seems to be a few extra changes from running cargo fmt.

kvark commented 4 years ago

What is the problem (in more detail) that happens if you don't add the new trait?

hayashi-stl commented 4 years ago

If I wrote:

impl<P: EuclideanSpace, R: Rotation<P>> One for Decomposed<P::Diff, R>
where
    P::Scalar: BaseFloat,
    P::Diff: VectorSpace,
{
    fn one() -> Self {
        Decomposed {
            scale: P::Scalar::one(),
            rot: R::one(),
            disp: P::Diff::zero(),
        }
    }
}

then the compiler will complain that P is unconstrained.

This doesn't currently happen in cgmath, but imagine that there was some type AnotherPoint2 that implemented EuclideanSpace<Diff = Vector2<f64>>. Also imagine that some type RotationAny implemented both Rotation<Point2<f64>> and Rotation<AnotherPoint2<f64>>. Then Decomposed<Vector2<f64>, RotationAny> implements One twice...

Actually, I just realized that in this case, since R::one() is coming from One and not Rotation<P>, so I can rewrite the body as

Decomposed {
    scale: P::Diff::Scalar::one(),
    rot: <R as One>::one(),
    disp: P::Diff::zero(),
}

and see that there is no dependence on P. I can almost replace P: EuclideanSpace with V: VectorSpace and replace P::Diff with V, but that won't deal with Rotation requiring P as a parameter. So here are some possibilities:

kvark commented 4 years ago

Thank you for elaborating here! I like the (2) and (3) choices:

Write it as impl<V: VectorSpace, R: One> One for Decomposed<V, R>. This eliminates the EuclideanRotation trait, but is also more general on R than I'd like.

What is the concern here, exactly? Is the generosity going to be a footgun in some situation?

Move the associated type of EuclideanRotation into Rotation and remove the parameter on Rotation. This would make sense unless we want it to be possible for a type to implement Rotation for multiple Euclidean spaces.

cgmath needs to steer away from being generic into being more practical, so this constraint seems reasonable to me.

hayashi-stl commented 4 years ago

It turns out that the second option is blocked by a call to rotate_vector in the implementation of Mul for Decomposed. It would actually require R to implement Rotation. So option 3 is taken. I noticed a few other traits like Rotation2, Rotation3, and Transform that have type parameters that should be associated types instead. It wouldn't make sense for the same type to implement both Rotation2<f32> and Rotation2<f64>, for example.

kvark commented 4 years ago

I noticed a few other traits like Rotation2, Rotation3, and Transform that have type parameters that should be associated types instead. It wouldn't make sense for the same type to implement both Rotation2 and Rotation2, for example.

yes, agreed!

hayashi-stl commented 4 years ago

Should I save fixing those for another pull request or combine it with this one?

kvark commented 4 years ago

I think it would make sense to combine them, if you can. Thank you!

hayashi-stl commented 4 years ago

It turns out that Matrix3<S> implements both Transform<Point2<S>> and Transform<Point3<S>> since one of them allows translations and the other doesn't. However, Rotation2, Rotation3, Transform2, and Transform3 didn't need their bounds.

kvark commented 4 years ago

@aloucks @elrnv would you want to help reviewing these changes? They are pretty serious