Skip to content

Commit 00f8e6a

Browse files
Merge #205
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>
2 parents ba00c41 + 23dd0b3 commit 00f8e6a

File tree

5 files changed

+1231
-66
lines changed

5 files changed

+1231
-66
lines changed

palette/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ categories = ["graphics", "multimedia::images", "no-std"]
1515
build = "build/main.rs"
1616

1717
[features]
18-
default = ["named_from_str", "std"]
18+
default = ["named_from_str", "named_gradients", "std"]
1919
named_from_str = ["named", "phf", "phf_codegen", "std"]
2020
named = []
21+
named_gradients = ["std"]
2122
random = ["rand"]
2223
serializing = ["serde", "std"]
2324

palette/build/named.rs

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
use std::fs::File;
22

3-
#[cfg(feature = "named")]
43
pub fn build() {
5-
use std::io::{BufRead, BufReader, Write};
64
use std::path::Path;
75

86
let out_dir = ::std::env::var("OUT_DIR").unwrap();
97
let dest_path = Path::new(&out_dir).join("named.rs");
8+
let mut writer = File::create(dest_path).expect("couldn't create named.rs");
9+
build_colors(&mut writer);
10+
let dest_path = Path::new(&out_dir).join("named_gradients.rs");
11+
let mut writer = File::create(dest_path).expect("couldn't create named_gradients.rs");
12+
build_gradients(&mut writer);
13+
}
14+
15+
#[cfg(feature = "named")]
16+
pub fn build_colors(writer: &mut File) {
17+
use std::io::{BufRead, BufReader, Write};
1018

1119
let reader =
1220
BufReader::new(File::open("build/svg_colors.txt").expect("could not open svg_colors.txt"));
13-
let mut writer = File::create(dest_path).expect("couldn't create named.rs");
1421
let mut entries = vec![];
1522

1623
for line in reader.lines() {
@@ -40,7 +47,56 @@ pub fn build() {
4047
entries.push((name.to_owned(), name.to_uppercase()));
4148
}
4249

43-
gen_from_str(&mut writer, &entries)
50+
gen_from_str(writer, &entries)
51+
}
52+
53+
#[cfg(feature = "named_gradients")]
54+
pub fn build_gradients(writer: &mut File) {
55+
use std::io::{BufRead, BufReader, Write};
56+
57+
let reader =
58+
BufReader::new(File::open("build/svg_gradients_mpl.txt").expect("could not open svg_gradients_mpl.txt"));
59+
60+
let mut line_iter = reader.lines();
61+
while let Some(Ok(line)) = line_iter.next(){
62+
//empty lines are allowed
63+
if line.is_empty() {continue;}
64+
let mut parts = line.split_whitespace();
65+
//every line should have the same info: name type number_of_colors [\n red green blue]^number_of_colors
66+
let name = parts.next().expect("couldn't get the color name");
67+
let color_type = parts.next().expect("couldn't get the type of the colors");
68+
//we assume that color_type is a rgb type
69+
let color_type = format!("crate::rgb::{}", color_type);
70+
let number_of_colors : usize = parts.next().expect("couldn't get the number of colors")
71+
.parse().unwrap_or_else(|_| panic!("couldn't parse the number of colors for color {}", name));
72+
writeln!(writer, "/// New matplotlib colormap by Nathaniel J. Smith, Stefan van der Walt, and (in the case of viridis) Eric Firing.").unwrap();
73+
writeln!(writer, "/// This gradient is perfectly perceptually-uniform, both in regular form and also when converted to black-and-white.").unwrap();
74+
writeln!(writer, "/// The colormap is released under the CC0 license public domain dedication.").unwrap();
75+
write!(writer,
76+
"pub const {0}: crate::gradient::Gradient<{1}, [(f32,{1});{2}]> = crate::gradient::Gradient([",
77+
name.to_uppercase(), color_type, number_of_colors).unwrap();
78+
for i in 0..number_of_colors {
79+
let color = line_iter
80+
.next()
81+
.unwrap_or_else(|| panic!("less lines than stated colors in gradient {}", name))
82+
.unwrap_or_else(|_| panic!("couldn't read the {}th line of color {}", i, name));
83+
let mut rgb = color.split(",");
84+
let red: f32 = rgb
85+
.next()
86+
.and_then(|r| r.trim().parse().ok())
87+
.unwrap_or_else(|| panic!("couldn't get the {}th red-value for {}", i, name));
88+
let green: f32 = rgb
89+
.next()
90+
.and_then(|r| r.trim().parse().ok())
91+
.unwrap_or_else(|| panic!("couldn't get the {}th green-value for {}", i, name));
92+
let blue: f32 = rgb
93+
.next()
94+
.and_then(|r| r.trim().parse().ok())
95+
.unwrap_or_else(|| panic!("couldn't get the {}th blue-value for {}", i, name));
96+
write!(writer, "({:.10},{}{{red: {}, green: {}, blue: {}, standard: ::core::marker::PhantomData}}),", (i as f32/number_of_colors as f32), color_type, red, green, blue).unwrap();
97+
}
98+
write!(writer, "], ::core::marker::PhantomData);\n").unwrap();
99+
}
44100
}
45101

46102
#[cfg(feature = "named_from_str")]
@@ -60,8 +116,12 @@ fn gen_from_str(writer: &mut File, entries: &[(String, String)]) {
60116
}
61117

62118
#[cfg(not(feature = "named"))]
63-
pub fn build() {}
119+
pub fn build_colors(_writer: &mut File) {}
64120

65121
#[allow(unused)]
66122
#[cfg(not(feature = "named_from_str"))]
67123
fn gen_from_str(_writer: &mut File, _entries: &[(String, String)]) {}
124+
125+
#[allow(unused)]
126+
#[cfg(not(feature = "named_gradients"))]
127+
pub fn build_gradients(_writer: &mut File) {}

0 commit comments

Comments
 (0)