diff --git a/README.md b/README.md index d4708f9..1c04150 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,7 @@ You can also clone the repo and run `cargo run --bin gallery` to view the intera Smoothed Line Stacked Area Stacked Line +Step Line Temperature Change Two Value-Axes in Polar diff --git a/charming/src/element/mod.rs b/charming/src/element/mod.rs index 69819ab..e30c88d 100644 --- a/charming/src/element/mod.rs +++ b/charming/src/element/mod.rs @@ -40,6 +40,7 @@ pub mod smoothness; pub mod sort; pub mod split_area; pub mod split_line; +pub mod step; pub mod symbol; pub mod symbol_size; pub mod text_align; @@ -87,6 +88,7 @@ pub use shape::*; pub use sort::*; pub use split_area::*; pub use split_line::*; +pub use step::*; pub use symbol::*; pub use symbol_size::*; pub use text_align::*; diff --git a/charming/src/element/step.rs b/charming/src/element/step.rs new file mode 100644 index 0000000..836fa5d --- /dev/null +++ b/charming/src/element/step.rs @@ -0,0 +1,77 @@ +use serde::{de::Visitor, Deserialize, Serialize}; + +#[derive(Debug, PartialEq, PartialOrd, Clone)] +pub enum Step { + True, + False, + Start, + Middle, + End, +} + +impl From for Step { + fn from(value: bool) -> Self { + if value { + Step::True + } else { + Step::False + } + } +} + +impl Serialize for Step { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + Step::True => serializer.serialize_bool(true), + Step::False => serializer.serialize_bool(false), + Step::Start => serializer.serialize_str("start"), + Step::Middle => serializer.serialize_str("middle"), + Step::End => serializer.serialize_str("end"), + } + } +} + +impl<'de> Deserialize<'de> for Step { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct StepVisitor; + + impl Visitor<'_> for StepVisitor { + type Value = Step; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str( + "a boolean or a string containing either \"start\", \"middle\" or \"end\"", + ) + } + + fn visit_bool(self, v: bool) -> Result + where + E: serde::de::Error, + { + match v { + true => Ok(Step::True), + false => Ok(Step::False), + } + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + match v { + "start" => Ok(Step::Start), + "middle" => Ok(Step::Middle), + "end" => Ok(Step::End), + _ => Err(E::custom("unable to parse step type, invalid string")), + } + } + } + deserializer.deserialize_any(StepVisitor) + } +} diff --git a/charming/src/series/line.rs b/charming/src/series/line.rs index 97131ec..ba12e4a 100644 --- a/charming/src/series/line.rs +++ b/charming/src/series/line.rs @@ -4,7 +4,7 @@ use crate::{ datatype::{DataFrame, DataPoint}, element::{ smoothness::Smoothness, AreaStyle, CoordinateSystem, DimensionEncode, Emphasis, ItemStyle, - Label, LineStyle, MarkArea, MarkLine, MarkPoint, Symbol, SymbolSize, Tooltip, + Label, LineStyle, MarkArea, MarkLine, MarkPoint, Step, Symbol, SymbolSize, Tooltip, }, }; @@ -53,6 +53,9 @@ pub struct Line { #[serde(skip_serializing_if = "Option::is_none")] smooth: Option, + #[serde(skip_serializing_if = "Option::is_none")] + step: Option, + #[serde(skip_serializing_if = "Option::is_none")] connect_nulls: Option, @@ -107,6 +110,7 @@ impl Line { item_style: None, emphasis: None, smooth: None, + step: None, connect_nulls: None, mark_point: None, mark_line: None, @@ -187,6 +191,11 @@ impl Line { self } + pub fn step>(mut self, step: S) -> Self { + self.step = Some(step.into()); + self + } + pub fn connect_nulls(mut self, connect_nulls: bool) -> Self { self.connect_nulls = Some(connect_nulls); self diff --git a/gallery/src/lib.rs b/gallery/src/lib.rs index 76b4f78..319224d 100644 --- a/gallery/src/lib.rs +++ b/gallery/src/lib.rs @@ -122,6 +122,7 @@ lazy_static! { insert!(m, line, smoothed_line); insert!(m, line, stacked_area); insert!(m, line, stacked_line); + insert!(m, line, step_line); insert!(m, line, temperature_change); insert!(m, line, two_value_axes_in_polar); m diff --git a/gallery/src/line/mod.rs b/gallery/src/line/mod.rs index 12d3bdd..e36751c 100644 --- a/gallery/src/line/mod.rs +++ b/gallery/src/line/mod.rs @@ -13,5 +13,6 @@ pub mod rainfall_vs_evaporation; pub mod smoothed_line; pub mod stacked_area; pub mod stacked_line; +pub mod step_line; pub mod temperature_change; pub mod two_value_axes_in_polar; diff --git a/gallery/src/line/step_line.rs b/gallery/src/line/step_line.rs new file mode 100644 index 0000000..df1727a --- /dev/null +++ b/gallery/src/line/step_line.rs @@ -0,0 +1,46 @@ +use charming::{ + component::{Axis, Feature, Grid, Legend, SaveAsImage, Title, Toolbox}, + element::{AxisType, Step, Tooltip, Trigger}, + series::Line, + Chart, +}; + +pub fn chart() -> Chart { + Chart::new() + .title(Title::new().text("Step Line")) + .tooltip(Tooltip::new().trigger(Trigger::Axis)) + .legend(Legend::new()) + .grid( + Grid::new() + .left("3%") + .right("4%") + .bottom("3%") + .contain_label(true), + ) + .toolbox(Toolbox::new().feature(Feature::new().save_as_image(SaveAsImage::new()))) + .x_axis( + Axis::new() + .type_(AxisType::Category) + .boundary_gap(false) + .data(vec!["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]), + ) + .y_axis(Axis::new().type_(AxisType::Value)) + .series( + Line::new() + .name("Step Start") + .step(Step::Start) + .data(vec![120, 132, 101, 134, 90, 230, 210]), + ) + .series( + Line::new() + .name("Step Middle") + .step(Step::Middle) + .data(vec![220, 282, 201, 234, 290, 430, 410]), + ) + .series( + Line::new() + .name("Step End") + .step(Step::End) + .data(vec![450, 432, 401, 454, 590, 530, 510]), + ) +} diff --git a/img/line/step_line.svg b/img/line/step_line.svg new file mode 100644 index 0000000..023f9a2 --- /dev/null +++ b/img/line/step_line.svg @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + +0 +100 +200 +300 +400 +500 +600 +Mon +Tue +Wed +Thu +Fri +Sat +Sun + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Step Start + + + +Step Middle + + + +Step End + + + + +Step Line + + + + + + + + + + + + + \ No newline at end of file