Skip to content

Commit 8580c87

Browse files
authored
Merge pull request #246 from Nick-Pearson/pattern-fill
Expose pattern fill api for histograms and bar charts
2 parents 80cf0de + 35be83a commit 8580c87

File tree

2 files changed

+216
-3
lines changed

2 files changed

+216
-3
lines changed

examples/basic_charts/src/main.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use plotly::{
55
color::{NamedColor, Rgb, Rgba},
66
common::{
77
ColorScale, ColorScalePalette, DashType, Fill, Font, Line, LineShape, Marker, Mode,
8-
Orientation,
8+
Orientation, Pattern, PatternShape,
99
},
1010
layout::{Axis, BarMode, CategoryOrder, Layout, Legend, TicksDirection, TraceOrder},
1111
sankey::{Line as SankeyLine, Link, Node},
@@ -633,6 +633,35 @@ fn category_order_bar_chart() {
633633
plot.show();
634634
}
635635

636+
fn bar_chart_with_pattern_fills() {
637+
let animals1 = vec!["giraffes", "orangutans", "monkeys"];
638+
let trace1 = Bar::new(animals1, vec![20, 14, 23]).name("SF Zoo").marker(
639+
Marker::new().line(Line::new().width(1.0)).pattern(
640+
Pattern::new()
641+
.shape(PatternShape::LeftDiagonalLine)
642+
.solidity(0.1),
643+
),
644+
);
645+
646+
let animals2 = vec!["giraffes", "orangutans", "monkeys"];
647+
let trace2 = Bar::new(animals2, vec![12, 18, 29]).name("LA Zoo").marker(
648+
Marker::new().line(Line::new().width(1.0)).pattern(
649+
Pattern::new()
650+
.shape(PatternShape::RightDiagonalLine)
651+
.solidity(0.5),
652+
),
653+
);
654+
655+
let layout = Layout::new().bar_mode(BarMode::Group);
656+
657+
let mut plot = Plot::new();
658+
plot.add_trace(trace1);
659+
plot.add_trace(trace2);
660+
plot.set_layout(layout);
661+
662+
plot.show();
663+
}
664+
636665
// Sankey Diagrams
637666
fn basic_sankey_diagram() {
638667
// https://plotly.com/javascript/sankey-diagram/#basic-sankey-diagram
@@ -709,6 +738,7 @@ fn main() {
709738
// stacked_bar_chart();
710739
// table_chart();
711740
// category_order_bar_chart();
741+
// bar_chart_with_pattern_fills();
712742

713743
// Sankey Diagrams
714744
// basic_sankey_diagram();

plotly/src/common/mod.rs

Lines changed: 185 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,115 @@ pub enum AxisSide {
10431043
Right,
10441044
}
10451045

1046+
#[derive(Serialize, Debug, Clone)]
1047+
pub enum PatternShape {
1048+
#[serde(rename = "")]
1049+
None,
1050+
#[serde(rename = "-")]
1051+
HorizonalLine,
1052+
#[serde(rename = "|")]
1053+
VerticalLine,
1054+
#[serde(rename = "/")]
1055+
RightDiagonalLine,
1056+
#[serde(rename = "\\")]
1057+
LeftDiagonalLine,
1058+
#[serde(rename = "+")]
1059+
Cross,
1060+
#[serde(rename = "x")]
1061+
DiagonalCross,
1062+
#[serde(rename = ".")]
1063+
Dot,
1064+
}
1065+
1066+
#[derive(Serialize, Debug, Clone)]
1067+
#[serde(rename_all = "lowercase")]
1068+
pub enum PatternFillMode {
1069+
Replace,
1070+
Overlay,
1071+
}
1072+
1073+
#[serde_with::skip_serializing_none]
1074+
#[derive(Serialize, Clone, Debug, Default)]
1075+
pub struct Pattern {
1076+
shape: Option<Dim<PatternShape>>,
1077+
#[serde(rename = "fillmode")]
1078+
fill_mode: Option<PatternFillMode>,
1079+
#[serde(rename = "bgcolor")]
1080+
background_color: Option<Dim<Box<dyn Color>>>,
1081+
#[serde(rename = "fgcolor")]
1082+
foreground_color: Option<Dim<Box<dyn Color>>>,
1083+
#[serde(rename = "fgopacity")]
1084+
foreground_opacity: Option<f64>,
1085+
size: Option<Dim<f64>>,
1086+
solidity: Option<Dim<f64>>,
1087+
}
1088+
1089+
impl Pattern {
1090+
pub fn new() -> Self {
1091+
Default::default()
1092+
}
1093+
1094+
pub fn shape(mut self, shape: PatternShape) -> Self {
1095+
self.shape = Some(Dim::Scalar(shape));
1096+
self
1097+
}
1098+
1099+
pub fn shape_array(mut self, shape: Vec<PatternShape>) -> Self {
1100+
self.shape = Some(Dim::Vector(shape));
1101+
self
1102+
}
1103+
1104+
pub fn fill_mode(mut self, fill_mode: PatternFillMode) -> Self {
1105+
self.fill_mode = Some(fill_mode);
1106+
self
1107+
}
1108+
1109+
pub fn background_color<C: Color>(mut self, color: C) -> Self {
1110+
self.background_color = Some(Dim::Scalar(Box::new(color)));
1111+
self
1112+
}
1113+
1114+
pub fn background_color_array<C: Color>(mut self, colors: Vec<C>) -> Self {
1115+
self.background_color = Some(Dim::Vector(ColorArray(colors).into()));
1116+
self
1117+
}
1118+
1119+
pub fn foreground_color<C: Color>(mut self, color: C) -> Self {
1120+
self.foreground_color = Some(Dim::Scalar(Box::new(color)));
1121+
self
1122+
}
1123+
1124+
pub fn foreground_color_array<C: Color>(mut self, colors: Vec<C>) -> Self {
1125+
self.foreground_color = Some(Dim::Vector(ColorArray(colors).into()));
1126+
self
1127+
}
1128+
1129+
pub fn foreground_opacity(mut self, opacity: f64) -> Self {
1130+
self.foreground_opacity = Some(opacity);
1131+
self
1132+
}
1133+
1134+
pub fn size(mut self, size: f64) -> Self {
1135+
self.size = Some(Dim::Scalar(size));
1136+
self
1137+
}
1138+
1139+
pub fn size_array(mut self, size: Vec<f64>) -> Self {
1140+
self.size = Some(Dim::Vector(size));
1141+
self
1142+
}
1143+
1144+
pub fn solidity(mut self, solidity: f64) -> Self {
1145+
self.solidity = Some(Dim::Scalar(solidity));
1146+
self
1147+
}
1148+
1149+
pub fn solidity_array(mut self, solidity: Vec<f64>) -> Self {
1150+
self.solidity = Some(Dim::Vector(solidity));
1151+
self
1152+
}
1153+
}
1154+
10461155
#[serde_with::skip_serializing_none]
10471156
#[derive(Serialize, Clone, Debug, Default)]
10481157
pub struct Marker {
@@ -1076,6 +1185,7 @@ pub struct Marker {
10761185
color_bar: Option<ColorBar>,
10771186
#[serde(rename = "outliercolor")]
10781187
outlier_color: Option<Box<dyn Color>>,
1188+
pattern: Option<Pattern>,
10791189
}
10801190

10811191
impl Marker {
@@ -1192,6 +1302,11 @@ impl Marker {
11921302
self.outlier_color = Some(Box::new(outlier_color));
11931303
self
11941304
}
1305+
1306+
pub fn pattern(mut self, pattern: Pattern) -> Self {
1307+
self.pattern = Some(pattern);
1308+
self
1309+
}
11951310
}
11961311

11971312
#[serde_with::skip_serializing_none]
@@ -2132,6 +2247,63 @@ mod tests {
21322247
assert_eq!(to_value(tick_format_stop).unwrap(), expected);
21332248
}
21342249

2250+
#[test]
2251+
fn test_serialize_pattern_shape() {
2252+
assert_eq!(to_value(PatternShape::None).unwrap(), json!(""));
2253+
assert_eq!(to_value(PatternShape::HorizonalLine).unwrap(), json!("-"));
2254+
assert_eq!(to_value(PatternShape::VerticalLine).unwrap(), json!("|"));
2255+
assert_eq!(
2256+
to_value(PatternShape::RightDiagonalLine).unwrap(),
2257+
json!("/")
2258+
);
2259+
assert_eq!(
2260+
to_value(PatternShape::LeftDiagonalLine).unwrap(),
2261+
json!("\\")
2262+
);
2263+
assert_eq!(to_value(PatternShape::Cross).unwrap(), json!("+"));
2264+
assert_eq!(to_value(PatternShape::DiagonalCross).unwrap(), json!("x"));
2265+
assert_eq!(to_value(PatternShape::Dot).unwrap(), json!("."));
2266+
}
2267+
2268+
#[test]
2269+
fn test_serialize_pattern_fill_mode() {
2270+
assert_eq!(
2271+
to_value(PatternFillMode::Replace).unwrap(),
2272+
json!("replace")
2273+
);
2274+
assert_eq!(
2275+
to_value(PatternFillMode::Overlay).unwrap(),
2276+
json!("overlay")
2277+
);
2278+
}
2279+
2280+
#[test]
2281+
fn test_serialize_pattern() {
2282+
let pattern = Pattern::new()
2283+
.shape_array(vec![
2284+
PatternShape::HorizonalLine,
2285+
PatternShape::VerticalLine,
2286+
])
2287+
.fill_mode(PatternFillMode::Overlay)
2288+
.background_color_array(vec![NamedColor::Black, NamedColor::Blue])
2289+
.foreground_color_array(vec![NamedColor::Red, NamedColor::Green])
2290+
.foreground_opacity(0.9)
2291+
.size_array(vec![10.0, 20.0])
2292+
.solidity_array(vec![0.1, 0.2]);
2293+
2294+
let expected = json!({
2295+
"shape": ["-", "|"],
2296+
"fillmode": "overlay",
2297+
"bgcolor": ["black", "blue"],
2298+
"fgcolor": ["red", "green"],
2299+
"fgopacity": 0.9,
2300+
"size": [10.0, 20.0],
2301+
"solidity": [0.1, 0.2]
2302+
});
2303+
2304+
assert_eq!(to_value(pattern).unwrap(), expected);
2305+
}
2306+
21352307
#[test]
21362308
fn test_serialize_marker() {
21372309
let marker = Marker::new()
@@ -2155,7 +2327,13 @@ mod tests {
21552327
.reverse_scale(true)
21562328
.show_scale(true)
21572329
.color_bar(ColorBar::new())
2158-
.outlier_color("#FFFFFF");
2330+
.outlier_color("#FFFFFF")
2331+
.pattern(
2332+
Pattern::new()
2333+
.shape(PatternShape::Cross)
2334+
.foreground_color(NamedColor::Red)
2335+
.size(10.0),
2336+
);
21592337

21602338
let expected = json!({
21612339
"symbol": "circle",
@@ -2177,7 +2355,12 @@ mod tests {
21772355
"autocolorscale": true,
21782356
"reversescale": true,
21792357
"showscale": true,
2180-
"outliercolor": "#FFFFFF"
2358+
"outliercolor": "#FFFFFF",
2359+
"pattern": {
2360+
"shape": "+",
2361+
"fgcolor": "red",
2362+
"size": 10.0
2363+
}
21812364
});
21822365

21832366
assert_eq!(to_value(marker).unwrap(), expected);

0 commit comments

Comments
 (0)