Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upAdd RigidTransform3D type #328
Conversation
| /// Construct a new rigid transformation, where the `rotation` applies first | ||
| pub fn new(rotation: TypedRotation3D<T, U, U>, translation: TypedVector3D<T, U>) -> Self { | ||
| impl<T: Float + ApproxEq<T>, Src, Dst> TypedRigidTransform3D<T, Src, Dst> { | ||
| /// Construct a new rigid transformation, where the `rotation` applies first\ |
This comment has been minimized.
This comment has been minimized.
| impl<T: Float + ApproxEq<T>, Src, Dst> TypedRigidTransform3D<T, Src, Dst> { | ||
| /// Construct a new rigid transformation, where the `rotation` applies first\ | ||
| #[inline] | ||
| pub fn new(rotation: TypedRotation3D<T, Src, Dst>, translation: TypedVector3D<T, Dst>) -> Self { |
This comment has been minimized.
This comment has been minimized.
pcwalton
Mar 28, 2019
Contributor
I kind of wonder if it might be easier if we had from_translated_rotation for this and from_rotated_transform for "reversed"? Up to you. I'm not sure whether the wordiness would help.
| let rigid = RigidTransform3D::new(rotation, translation); | ||
| assert!(rigid | ||
| .to_transform() | ||
| .approx_eq(&translation.to_transform().pre_mul(&rotation.to_transform()))); |
This comment has been minimized.
This comment has been minimized.
pcwalton
Mar 28, 2019
Contributor
Wait, it says in your comment above that RigidTransform3D is Translation * Rotation. But you have Rotation * Translation written here. translation.pre_mul(rotation) is rotation.post_mul(translation) and A.post_mul(B) is AB. See the definition of post_mul here: https://github.com/servo/euclid/blob/master/src/transform3d.rs#L259 (each entry is row of A times column of B).
Am I missing something or is this backwards?
This comment has been minimized.
This comment has been minimized.
Manishearth
Mar 28, 2019
•
Author
Member
The type signatures of pre_mul and post_mul are backwards, then:
pub fn post_mul<NewDst>(&self, mat: &TypedTransform3D<T, Dst, NewDst>) -> TypedTransform3D<T, Src, NewDst>i.e. Transform<Src, Dst>.post_mul(Transform<Dst, NewDst>) = Transform<Src, Dst> * Transform<Dst, NewDst> = Transform<Src, NewDst>. This doesn't make sense, if we were to transform a vector V the transformation would be Transform<Src, Dst> * Transform<Dst, NewDst> * Vector<?> , which forces the vector to be Dst, transforming it to NewDst and then somehow transforming it to Dst from Src, which it is not.
This all makes sense if the multiplication is backwards.
aside: post_mul and pre_mul are confusing method names because it's not clear if a.post_mul(b) means "a postmultiplied by b" or "postmultiply a to b"
This comment has been minimized.
This comment has been minimized.
Manishearth
Mar 28, 2019
Author
Member
I think I got confused enough times with pre_mul and post_mul that I've verified what they do multiple times (we really should write the matrix math down in the docs) and I may have made a mistake with this, but also the typed units seem to work
This comment has been minimized.
This comment has been minimized.
asajeffrey
Mar 28, 2019
Member
Just for laughs, Pathfinder and Euclid have pre_mul and post_mul swapped.
This comment has been minimized.
This comment has been minimized.
pcwalton
Mar 28, 2019
Contributor
So are the units wrong or are the names wrong? AB is each row of A times each column of B. That corresponds to A.post_mul(B) as written in the code fragment I linked, right?
I think I'm going to abolish the pre and post stuff in Pathfinder. It's so confusing.
This comment has been minimized.
This comment has been minimized.
pcwalton
Mar 28, 2019
Contributor
Typically I multiply matrices by column vectors in that order. i.e. Ma = b, where a and b are column vectors.
This comment has been minimized.
This comment has been minimized.
Manishearth
Mar 28, 2019
Author
Member
Yes, that is what everyone does, but this is not what Euclid is doing, assuming it's using row-major order. Euclid is doing aM=b with row vectors.
This comment has been minimized.
This comment has been minimized.
Manishearth
Mar 28, 2019
Author
Member
Also please don't take my word for anything I say about euclid's code here, I've been looking at this enough that it's quite possible that I'm missing something and just reading things wrong.
This comment has been minimized.
This comment has been minimized.
Manishearth
Mar 28, 2019
Author
Member
Oh the documentation of post_mul matches its units (and the way I use it, i.e. a.post_mul(b) = ba) but not the code:
/// Returns the multiplication of the two matrices such that mat's transformation
/// applies after self's transformation.
pub fn post_mul<NewDst>(&self, mat: &TypedTransform3D<T, Dst, NewDst>) -> TypedTransform3D<T, Src, NewDst> {
TypedTransform3D::row_major("mat's transform applies after self's transform" means mat * self, which matches the units, and my interpretation. But not the code.
Increasingly convinced the code is column major
This comment has been minimized.
This comment has been minimized.
Manishearth
Mar 28, 2019
Author
Member
pub fn create_translation(x: T, y: T, z: T) -> Self {
let (_0, _1): (T, T) = (Zero::zero(), One::one());
TypedTransform3D::row_major(
_1, _0, _0, _0,
_0, _1, _0, _0,
_0, _0, _1, _0,
x, y, z, _1
)
}oh hey the translation matrices are wrong too :)
|
Amusingly, even though the transforms code is wrong, this code is correct, because it doesn't use any of the affected APIs, and deals purely in transforms. (which is why it typechecks and passes tests) |
In retrospect I think that /// This is equivalent to first translating and then rotating p.
let transform = translation.post_transform(rotation);
let result = transform.transform_point3d(p); |
|
Honestly that doesn't clear things up too much since you have to know that this is talking in terms of transforms. I and @pcwalton discussed that with Gankro on twitter yesterday -- both of us are used to using matrices for graphics and find those method names (as used in Gecko) confusing. pre_transform and post_transform also have the same problem as pre_mul/post_mul -- you can read it as an active or passive sentence, and it means opposite things. Gankro's suggestion The problem here is really the documentation. |
|
There's also
I'm very keen on improving the documentation. Don't hesitate to point me to the places where it falls short (having written a big chunk of this stuff I'm not necessarily in a good spot to see where the confusing areas are). Are you thinking about a paragraph on the structs, or more examples in the methods ? |
|
Updated with row-vector math |
|
Almost done, just had a couple more questions. |
| } | ||
| } | ||
|
|
||
| /// Decompose this into a position and an orientation to be applied in the opposite order |
This comment has been minimized.
This comment has been minimized.
pcwalton
Mar 29, 2019
Contributor
Change "position" to "translation" and "orientation" to "rotation" for consistency?
| // = (R' * R'^-1) * T' * R' * T2 | ||
| // = R' * (R'^-1 * T' * R') * T2 | ||
| // = R' * T'' * T2 | ||
| // = R * T'' |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
I kinda want to pick method names like |
|
(addressed) |
|
OK, r=me. This is obviously correct code and I'm tired of fighting GitHub reviews :) |
|
hah @bors-servo r=pcwalton |
|
|
Add RigidTransform3D type I need this for implementing WebXR in Servo. The type can be implemented out-of-tree as well, but it feels appropriate to have it here. I wasn't quite sure what to name it or if I should have called it `TypedRigidTransform3D`. I haven't written the 2D version because I don't need it, but it should actually be a straightforward copy-paste with the 3s changed to 2s (since the matrix math doesn't change). I'll add it on request. The tests do help assure me that the math is right, but I would like to have some scrutiny there. If this is going to take a while to review, let me know and I can try landing a temporary version of this in Servo so I can continue with my work. r? @pcwalton @nical <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/euclid/328) <!-- Reviewable:end -->
|
@bors-servo r=pcwalton |
|
|
Add RigidTransform3D type I need this for implementing WebXR in Servo. The type can be implemented out-of-tree as well, but it feels appropriate to have it here. I wasn't quite sure what to name it or if I should have called it `TypedRigidTransform3D`. I haven't written the 2D version because I don't need it, but it should actually be a straightforward copy-paste with the 3s changed to 2s (since the matrix math doesn't change). I'll add it on request. The tests do help assure me that the math is right, but I would like to have some scrutiny there. If this is going to take a while to review, let me know and I can try landing a temporary version of this in Servo so I can continue with my work. r? @pcwalton @nical <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/euclid/328) <!-- Reviewable:end -->
|
|
Update XR code to use rigid transforms and new pose/transform stuff from the spec This updates our XR code to use euclid's new [RigidTransform3D type](servo/euclid#328), which is more efficent and convenient to work with. It additionally brings us up to speed with the spec: - `XRViewerPose` was made a subclass of `XRPose` (immersive-web/webxr#496) - `XRView.viewMatrix` was removed in favor of `XRRigidTransform.inverse.matrix` (immersive-web/webxr#531) - `XRRigidTransform.inverse` is an attribute (immersive-web/webxr#560) - `XRRigidTransform` now validates positions in its constructor (immersive-web/webxr#568) Furthermore, it adds support for `XRRigidTransform.matrix`. While fixing this I also noticed that our view matrix code was incorrect, we calculated view matrices as `pose.to_column_major_array()`, whereas it *should* be `pose.inverse().to_row_major_array()` (since Euclid uses row vectors, whenever the spec says it wants a column major array we should use `.to_row_major_array()` since all web specs implicitly use column vectors). For 3DOF devices poses are mostly rotations anyway, so the effective transpose behaved _like_ an inversion, but was incorrect. This PR gets rid of `view.viewMatrix` anyway, however I felt like I should mention this discrepancy, since otherwise the replacement of `view.viewMatrix` with `view.transform.inverse.matrix` doesn't make sense r? @jdm <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23159) <!-- Reviewable:end -->
Update XR code to use rigid transforms and new pose/transform stuff from the spec This updates our XR code to use euclid's new [RigidTransform3D type](servo/euclid#328), which is more efficent and convenient to work with. It additionally brings us up to speed with the spec: - `XRViewerPose` was made a subclass of `XRPose` (immersive-web/webxr#496) - `XRView.viewMatrix` was removed in favor of `XRRigidTransform.inverse.matrix` (immersive-web/webxr#531) - `XRRigidTransform.inverse` is an attribute (immersive-web/webxr#560) - `XRRigidTransform` now validates positions in its constructor (immersive-web/webxr#568) Furthermore, it adds support for `XRRigidTransform.matrix`. While fixing this I also noticed that our view matrix code was incorrect, we calculated view matrices as `pose.to_column_major_array()`, whereas it *should* be `pose.inverse().to_row_major_array()` (since Euclid uses row vectors, whenever the spec says it wants a column major array we should use `.to_row_major_array()` since all web specs implicitly use column vectors). For 3DOF devices poses are mostly rotations anyway, so the effective transpose behaved _like_ an inversion, but was incorrect. This PR gets rid of `view.viewMatrix` anyway, however I felt like I should mention this discrepancy, since otherwise the replacement of `view.viewMatrix` with `view.transform.inverse.matrix` doesn't make sense r? @jdm <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23159) <!-- Reviewable:end -->
Update XR code to use rigid transforms and new pose/transform stuff from the spec This updates our XR code to use euclid's new [RigidTransform3D type](servo/euclid#328), which is more efficent and convenient to work with. It additionally brings us up to speed with the spec: - `XRViewerPose` was made a subclass of `XRPose` (immersive-web/webxr#496) - `XRView.viewMatrix` was removed in favor of `XRRigidTransform.inverse.matrix` (immersive-web/webxr#531) - `XRRigidTransform.inverse` is an attribute (immersive-web/webxr#560) - `XRRigidTransform` now validates positions in its constructor (immersive-web/webxr#568) Furthermore, it adds support for `XRRigidTransform.matrix`. While fixing this I also noticed that our view matrix code was incorrect, we calculated view matrices as `pose.to_column_major_array()`, whereas it *should* be `pose.inverse().to_row_major_array()` (since Euclid uses row vectors, whenever the spec says it wants a column major array we should use `.to_row_major_array()` since all web specs implicitly use column vectors). For 3DOF devices poses are mostly rotations anyway, so the effective transpose behaved _like_ an inversion, but was incorrect. This PR gets rid of `view.viewMatrix` anyway, however I felt like I should mention this discrepancy, since otherwise the replacement of `view.viewMatrix` with `view.transform.inverse.matrix` doesn't make sense r? @jdm <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23159) <!-- Reviewable:end -->
Update XR code to use rigid transforms and new pose/transform stuff from the spec This updates our XR code to use euclid's new [RigidTransform3D type](servo/euclid#328), which is more efficent and convenient to work with. It additionally brings us up to speed with the spec: - `XRViewerPose` was made a subclass of `XRPose` (immersive-web/webxr#496) - `XRView.viewMatrix` was removed in favor of `XRRigidTransform.inverse.matrix` (immersive-web/webxr#531) - `XRRigidTransform.inverse` is an attribute (immersive-web/webxr#560) - `XRRigidTransform` now validates positions in its constructor (immersive-web/webxr#568) Furthermore, it adds support for `XRRigidTransform.matrix`. While fixing this I also noticed that our view matrix code was incorrect, we calculated view matrices as `pose.to_column_major_array()`, whereas it *should* be `pose.inverse().to_row_major_array()` (since Euclid uses row vectors, whenever the spec says it wants a column major array we should use `.to_row_major_array()` since all web specs implicitly use column vectors). For 3DOF devices poses are mostly rotations anyway, so the effective transpose behaved _like_ an inversion, but was incorrect. This PR gets rid of `view.viewMatrix` anyway, however I felt like I should mention this discrepancy, since otherwise the replacement of `view.viewMatrix` with `view.transform.inverse.matrix` doesn't make sense r? @jdm <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23159) <!-- Reviewable:end -->
Manishearth commentedMar 26, 2019
•
edited
I need this for implementing WebXR in Servo. The type can be implemented out-of-tree as well, but it feels appropriate to have it here.
I wasn't quite sure what to name it or if I should have called it
TypedRigidTransform3D. I haven't written the 2D version because I don't need it, but it should actually be a straightforward copy-paste with the 3s changed to 2s (since the matrix math doesn't change). I'll add it on request.The tests do help assure me that the math is right, but I would like to have some scrutiny there.
If this is going to take a while to review, let me know and I can try landing a temporary version of this in Servo so I can continue with my work.
r? @pcwalton @nical
This change is