-
Notifications
You must be signed in to change notification settings - Fork 60
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
Redesign and improve the Gradient type and friends #156
Comments
158: Make `Take` iterator for gradient inclusive of both end colors, add tests r=Ogeon a=okaneco As mentioned in the third point of #156, this PR includes the bounds of the gradient along with more equal distribution of colors returned from the iterator. In the current version of the iterator, `(self.from_head) / (self.len)` will approach 1 but never equal 1 which results in the upper gradient bound being excluded. The current behavior of `take()` is surprising if one expects the last color step to be the max color of the gradient. This PR increases step distance so the user receives an iterator of colors that evenly traverses from the minimum bound of the gradient to the maximum. The step is increased by subtracting 1 from `self.len` in the calculation of `i`. The calculation for `i` will then include - `0` when `self.from_head` is `0` - `1` when `self.from_head == self.len - 1`. The assignment of `i` has a conditional for when `self.len` is `1`. This avoids division by 0 which results in NaN parameters for the iterator color returned in the case of `take(1)`. The check needed to be added to the `DoubleEndedIterator`, otherwise it would NaN on `1` as well. The behavior of `take(1)` before this PR is to supply the minimum color in the gradient. Pictured below is an example of the difference between current behavior and this PR in 3 sets of Lch gradients. The 1st, 3rd, and 5th rows are the current implementation of the iterator and the others are from the PR with the final step being inclusive of the maximum gradient color specified. The gradients are no longer skewed towards the beginning. The penultimate steps are very close to the current behavior's final step. ![image of 6 stepped gradients](https://raw.githubusercontent.com/okaneco/images/master/inclusive_grad.png) --- `gradient::test::simple_slice` fails as a result of this PR. ``` ---- gradient::test::simple_slice stdout ---- thread 'gradient::test::simple_slice' panicked at 'assert_relative_eq!(t1, t2) left = Rgb { red: 0.8888888888888888, green: 0.0, blue: 0.1111111111111111, standard: PhantomData } right = Rgb { red: 0.875, green: 0.0, blue: 0.125, standard: PhantomData } ', palette/src/gradient.rs:453:13 ``` ```rust #[test] fn simple_slice() { let g1 = Gradient::new(vec![ LinSrgb::new(1.0, 0.0, 0.0), LinSrgb::new(0.0, 0.0, 1.0), ]); let g2 = g1.slice(..0.5); let v1: Vec<_> = g1.take(10).take(5).collect(); let v2: Vec<_> = g2.take(5).collect(); for (t1, t2) in v1.iter().zip(v2.iter()) { assert_relative_eq!(t1, t2); } } ``` Co-authored-by: okaneco <47607823+okaneco@users.noreply.github.com>
To learn more about [no_std], I looked into making gradient [no_std] enabled and built it successfully for The no_std_test fails to compile for Here are the changed files. |
I have never worked with the That and the idea of having support for more than just linear interpolation makes me wonder if it's worth the effort to get |
205: Generalizing gradients and add constant gradients r=Ogeon a=NicolasKlenert This pull request contains two commits: ### 1. Generalization of `Gradient` This allows creation of gradients from arrays. The inner collection type only has to be `ArrayLike`. For backward compatibility the default is still vector. This allows constant gradients and should also allow gradients to be supported for the next `#[no-std]` build. Other options to enable `#[no-std]` for gradients were briefly disscused in #156. ### 2. Adding some constant gradients This commit adds constant gradients. To be precise, it adds all 4 new matplotlib color gradients, which are perfectly perceptually-uniform, both in regular form and also when converted to black-and-white and therefore one of the most useful gradients. These are build the same way the named colors are built. A new feature-flag `named_gradients` is added to toggle said constants. This closes #62. ### Alterantives - The generalization of gradients can be achieved in a multiple of ways. Using a trait is just one of them and may be the wrong way to go. - However I think because this pull request doesn't have any breaking changes and gradients should be supporting arrays in future versions of the crate, it doesn't seem like this update of `Gradient` will cause more breaking API changes in the future than otherwise. Also constant gradients may be the only interaction with gradients a user needs, such that introducing them could reduce the number of users which actually relies on the generation of `Gradient` itself. - At the moment the 4 constant gradients are using linear interpolation but in nature these gradients are Spline-Interpolations of exaclty two points (and 2 controlpoints). If `Gradient` will support Spline-Inerpolation in the future and the exact controlpoints of these gradients can be found (I only found the colormaps), the gradients could be implemented more memory efficient. ### Remark These commits depend on const generics, which is a feature in beta but is planned to be stable on 2021-03-25 onwords (see [#79135](rust-lang/rust#79135)). Co-authored-by: Nicolas Klenert <klenert.nicolas@gmail.com> Co-authored-by: NicolasKlenert <Nicolas_Klenert@web.de> Co-authored-by: NicolasKlenert <klenert.nicolas@gmail.com>
It should have about the same API as today, where it still makes sense, but overall modernized. Also fulfill this:
#[no_std]
enabledTake
iterator should be inclusive, meaning both ends are part of the output for n > 1x..=y
, but other ranges may also make senseThe first two points may go hand in hand, by replacing the internal
Vec
with some input collection.The text was updated successfully, but these errors were encountered: