-
Notifications
You must be signed in to change notification settings - Fork 205
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
Add AffineOps trait to transform an entire geometry (plus some tweaks) #871
Conversation
Actually - hold off. I'm going to think about naming a bit more... |
|
||
/// Apply `transform` immutably, outputting a new geometry. | ||
#[must_use] | ||
fn affine_transform(&self, transform: &AffineTransform<T>) -> Self; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Originally I wanted to do:
mutable: affine_transform
immutable: affine_transformed
But it just conflicts too much with our existing implementation conventions. I'm going to follow up with another PR (update: see #872) to add mutable flavors with consistent naming for Scale/Skew/Rotate/Translate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok - this is ready for review!
@@ -127,6 +183,10 @@ impl<T: CoordNum> AffineTransform<T> { | |||
) | |||
} | |||
|
|||
pub fn is_identity(&self) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This came in handy while trying to skip application in my code base.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we get a brief example (or explanation as a doc comment)?
@@ -141,8 +201,8 @@ impl<T: CoordNum> AffineTransform<T> { | |||
/// xoff = origin.x - (origin.x * xfact) | |||
/// yoff = origin.y - (origin.y * yfact) | |||
/// ``` | |||
pub fn scale(xfact: T, yfact: T, origin: Point<T>) -> Self { | |||
let (x0, y0) = origin.x_y(); | |||
pub fn scale(xfact: T, yfact: T, origin: impl Into<Coordinate<T>>) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this is controversial - but otherwise this is always kind of a papercut for me at the call site.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC using Into
in function signatures can lead to less informative error messages but I can't find the ref now and I would hope that the signature makes it obvious what's going on, so 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sometimes I daydream about getting rid of one of Point/Coord... 🔥
fn affine_transform_mut(&mut self, transform: &AffineTransform<T>); | ||
|
||
/// Apply `transform` immutably, outputting a new geometry. | ||
#[must_use] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL about must_use
. This is so good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, using a builder pattern on something which is Copy
makes it very easy to forget that this is wrong:
let mut transform = AffineTransform::identity();
transorm.translated(1.0, 2.0);
geometry.affine_transform(&transform);
It should be:
let mut transform = AffineTransform::identity();
transform = transorm.translated(1.0, 2.0);
geometry.affine_transform(&transform);
And now the user will at least get a warning to clue them in.
@@ -13,7 +66,7 @@ use std::fmt; | |||
/// `AffineTransform` is a row-major matrix. | |||
/// 2D affine transforms require six matrix parameters: | |||
/// | |||
/// `[a, b, d, e, xoff, yoff]` | |||
/// `[a, b, xoff, d, e, yoff]` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes. I knew I'd forgotten to update something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great. The only addition I'd like to see is a short note in the docs (maybe using a ## Performance
header?) for algorithm::Scale
and co., noting that chaining them isn't as efficient as using a chained AffineTransform
and affine_transform(_mut)
. WDYT?
- mark AffineTransform builder methods as must_use - default AffineTransform to f64 - accept coordinates for origin param - add AffineTransformation.is_identity - update docs
I added the new docs in e2e17fc and then squashed all my commits, which were a bit messy. |
Thanks, LGTM! Merge when you're ready. |
bors r=urschrei Thanks for the review! |
Build succeeded: |
872: Add helpful methods to affine backed traits r=urschrei a=michaelkirk - [x] I agree to follow the project's [code of conduct](https://github.com/georust/geo/blob/main/CODE_OF_CONDUCT.md). - [x] I added an entry to `CHANGES.md` if knowledge of this change could be valuable to users. --- Leverages the new `AffineOps::affine_transform` trait to drive our various affine affiliated traits. This doubles down on the `do_the_thing() -> Self` | `do_the_thing_mut()` naming convention from #871 Also, I was able to generically re-implement the Rotate trait in terms of other traits, which was neat. And I added some useful (I think) helper methods to our other Affine driven traits. FIXES #287 (somewhat incidentally) Co-authored-by: Michael Kirk <michael.code@endoftheworl.de>
CHANGES.md
if knowledge of this change could be valuable to users.As discussed in this comment, adds the ability to compose a transformation and apply the thing to an entire geometry (saving users the hassle of having to map_coords themselves:
I also included some tweaks based on my experience using
AffineTransform
in another project.