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 2d and 3d rotations to euclid. #226
Conversation
d3f2248
to
c2e9c16
|
The PR is now in a pretty good shape (tests and doc included). Other methods could be added, like converting back into euler angles, and also extracting the rotation as quaternion from a matrix (which appears to be used in Gecko), but this PR is already big enough as it is. r? @kvark |
|
Great work! A few notes to address. |
|
|
||
| /// Creates the identity rotation. | ||
| #[inline] | ||
| pub fn identity() -> Self where T: Zero { |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
nical
Sep 27, 2017
Author
Collaborator
True that. Let's add it for this type and the other ones as a followup.
| pub fn transform_point(&self, point: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> { | ||
| let a = self.angle; | ||
| point2( | ||
| point.x * Float::cos(a) - point.y * Float::sin(a), |
This comment has been minimized.
This comment has been minimized.
| pub fn euler(roll: Radians<T>, pitch: Radians<T>, yaw: Radians<T>) -> Self { | ||
| let half = T::one() / (T::one() + T::one()); | ||
|
|
||
| let (sy, cy) = Float::sin_cos(half * yaw.get()); |
This comment has been minimized.
This comment has been minimized.
| #[inline] | ||
| pub fn is_normalized(&self) -> bool { | ||
| // TODO: we might need to relax the threshold here, because of floating point imprecision. | ||
| (self.i * self.i + self.j * self.j + self.k * self.k + self.r *self.r).approx_eq(&T::one()) |
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| // For robustness, stay within the domain of acos. | ||
| dot = Float::min(Float::max(dot, -one), one); |
This comment has been minimized.
This comment has been minimized.
kvark
Sep 27, 2017
Member
dot is non-negative at this point, so need to clamp against the lower bound
| /// | ||
| /// The input point must be use the unit Src, and the returned point has the unit Dst. | ||
| #[inline] | ||
| pub fn transform_point(&self, point: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> { |
This comment has been minimized.
This comment has been minimized.
kvark
Sep 27, 2017
Member
why is it transform_point as opposed to rotate_point? I'd expect transform_* to be reserved for typed transformations
This comment has been minimized.
This comment has been minimized.
| T::approx_epsilon() | ||
| } | ||
|
|
||
| fn approx_eq(&self, other: &Self) -> bool { |
This comment has been minimized.
This comment has been minimized.
kvark
Sep 27, 2017
Member
should we try to inverse the signs of one of them if they are equal otherwise?
This comment has been minimized.
This comment has been minimized.
| let rm90 = Rotation2D::new(Radians::new(-FRAC_PI_2)); | ||
| let r180 = Rotation2D::new(Radians::new(PI)); | ||
|
|
||
| assert!(ri.transform_point(&point2(1.0, 2.0)).approx_eq(&point2(1.0, 2.0))); |
This comment has been minimized.
This comment has been minimized.
| // q2 = numpy.quaternion(0, 1, 0, 0) | ||
| // quaternion.slerp_evaluate(q1, q2, .0) | ||
|
|
||
| assert!(q1.slerp(&q2, 0.0).approx_eq(&q1)); |
This comment has been minimized.
This comment has been minimized.
kvark
Sep 27, 2017
Member
would be nice to also have tests for the code paths of A) quaternions being very close to each other, and B) quaternions in different hemispheres
| use std::f32::consts::{PI, FRAC_PI_2}; | ||
| let rotations = [ | ||
| Rotation3D::identity(), | ||
| Rotation3D::around_x(Radians::new(FRAC_PI_2)), |
This comment has been minimized.
This comment has been minimized.
|
First round of review fixes in, I need to think about the missing tests. |
|
Note: I just renamed |
|
Added some more tests (by messing with with numpy) and squashed the commits. |
|
Thanks, looks better now. Still a few points to address. |
| fn approx_eq(&self, other: &Self) -> bool { | ||
| ( | ||
| self.i.approx_eq(&other.i) | ||
| && self.j.approx_eq(&other.j) |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
nical
Sep 27, 2017
Author
Collaborator
D'oh! It would if approx_eq was sensibly implemented for the vector type, but the latter implements ApproxEq instead of ApproxEq as it should.
I'll fix that later but that's techinally a breaking change so I'll do it whenever we have another breaking change to justify it.
| T::approx_epsilon() | ||
| } | ||
|
|
||
| fn approx_eq(&self, other: &Self) -> bool { |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
I also last-minute-added pre_rotate and post_rotate in TypedRotation2D, which I had forgotten, and a static method |
|
Looks like everything is addressed now? |
|
I believe it is ready. |
|
@bors-servo r=kvark |
|
|
Add 2d and 3d rotations to euclid. The 2d and 3d rotation APIs are modeled using the same principles as the transform types: Use simple terminology but don't try to hide what's happening under the hood (for example that the 3d rotation is a quaternion). This gives something that looks like this: ```rust let r1 = Rotation3D::quaternion(x, y, z, w); // I speak quats fluently let r2 = Rotation3D::euler(raw, pitch, yaw); let r3 = r1.post_rotate(&r2); // just like Transform3D::post_rotate let r4 = Rotation3D::around_y(foo); let p = r3.transform_point(&point2(1.0, 2.0)); ``` Some of the `Transform3D` APIs dealing with rotations should be modified to use the rotation type, but I'd like to postpone doing breaking changes. I also added a 2d rotation type with the same type of API represented by a simple angle in radians (which I think is the simplest and most convenient representation for 2d). I need 2d rotations in a few places in lyon, some of which are SVG stuff that servo/webrender will eventually need as well. I am pretty happy with the look of the API and how it follows the same philosophy as other euclid types. I think that with the addition of a [Translation](#214) type the whole thing converges towards a coherent (and opinionated) design that has its merits and that should satisfy a different niche from the cgmath and nalgebra crowds. <!-- 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/226) <!-- Reviewable:end -->
|
|
nical commentedSep 14, 2017
•
edited by larsbergstrom
The 2d and 3d rotation APIs are modeled using the same principles as the transform types: Use simple terminology but don't try to hide what's happening under the hood (for example that the 3d rotation is a quaternion).
This gives something that looks like this:
Some of the
Transform3DAPIs dealing with rotations should be modified to use the rotation type, but I'd like to postpone doing breaking changes.I also added a 2d rotation type with the same type of API represented by a simple angle in radians (which I think is the simplest and most convenient representation for 2d). I need 2d rotations in a few places in lyon, some of which are SVG stuff that servo/webrender will eventually need as well.
I am pretty happy with the look of the API and how it follows the same philosophy as other euclid types.
I think that with the addition of a Translation type the whole thing converges towards a coherent (and opinionated) design that has its merits and that should satisfy a different niche from the cgmath and nalgebra crowds.
This change is