From a2a6b8af5db269c50f42d713caf4d1da08d29547 Mon Sep 17 00:00:00 2001 From: alceal Date: Fri, 1 Nov 2024 17:28:41 +0100 Subject: [PATCH 1/2] chore: Refactor code --- src/common/layout.rs | 59 +++++++------------ src/common/line.rs | 4 +- src/common/mark.rs | 4 +- src/components/axis.rs | 106 +++++++++------------------------- src/components/color.rs | 9 ++- src/components/exponent.rs | 2 +- src/components/line.rs | 3 +- src/components/mod.rs | 2 + src/components/orientation.rs | 2 +- src/components/shape.rs | 3 +- src/components/text.rs | 15 +++++ src/components/tick.rs | 59 +++++++++++++++++++ src/lib.rs | 3 +- src/plots/barplot.rs | 4 +- src/plots/boxplot.rs | 4 +- 15 files changed, 150 insertions(+), 129 deletions(-) create mode 100644 src/components/tick.rs diff --git a/src/common/layout.rs b/src/common/layout.rs index df9e84b..58a0b74 100644 --- a/src/common/layout.rs +++ b/src/common/layout.rs @@ -1,6 +1,5 @@ use plotly::{ - color::Rgb as RgbPlotly, - common::{Font, Title}, + common::Font, layout::{Axis as AxisPlotly, Legend as LegendPlotly}, Layout as LayoutPlotly, }; @@ -21,7 +20,7 @@ pub(crate) trait Layout { let mut layout = LayoutPlotly::new(); if let Some(title) = plot_title { - layout = layout.title(Self::set_title(title)); + layout = layout.title(title.to_plotly()); } layout = layout.x_axis(Self::set_axis(x_title, x_axis)); @@ -30,23 +29,12 @@ pub(crate) trait Layout { layout } - fn set_title(title: Text) -> Title { - Title::with_text(title.content) - .font( - Font::new() - .family(title.font.as_str()) - .size(title.size) - .color(RgbPlotly::new(title.color.0, title.color.1, title.color.2)), - ) - .x(title.x) - .y(title.y) - } - + // TODO: Move axis functions to Axis struct like colorbar fn set_axis(title: Option, format: Option<&Axis>) -> AxisPlotly { let mut axis = AxisPlotly::new(); if let Some(title) = title { - axis = axis.title(Self::set_title(title)); + axis = axis.title(title.to_plotly()); } if let Some(format) = format { @@ -56,11 +44,12 @@ pub(crate) trait Layout { axis } + // TODO: Move legend functions to Axis struct like colorbar fn set_legend(title: Option, format: Option<&Legend>) -> LegendPlotly { let mut legend = LegendPlotly::new(); if let Some(title) = title { - legend = legend.title(Self::set_title(title)); + legend = legend.title(title.to_plotly()); } if let Some(format) = format { @@ -76,15 +65,15 @@ pub(crate) trait Layout { } if let Some(axis_position) = &format.axis_side { - axis = axis.side(axis_position.get_side()); + axis = axis.side(axis_position.to_plotly()); } if let Some(axis_type) = &format.axis_type { - axis = axis.type_(axis_type.get_type()); + axis = axis.type_(axis_type.to_plotly()); } if let Some(color) = format.value_color { - axis = axis.color(RgbPlotly::new(color.0, color.1, color.2)); + axis = axis.color(color.to_plotly()); } if let Some(range) = &format.value_range { @@ -96,7 +85,7 @@ pub(crate) trait Layout { } if let Some(exponent) = &format.value_exponent { - axis = axis.exponent_format(exponent.get_exponent()); + axis = axis.exponent_format(exponent.to_plotly()); } if let Some(range_values) = &format.tick_values { @@ -108,7 +97,7 @@ pub(crate) trait Layout { } if let Some(tick_direction) = &format.tick_direction { - axis = axis.ticks(tick_direction.get_direction()); + axis = axis.ticks(tick_direction.to_plotly_tickdirection()); } if let Some(tick_length) = format.tick_length { @@ -119,8 +108,8 @@ pub(crate) trait Layout { axis = axis.tick_width(tick_width.to_owned()); } - if let Some(tick_color) = format.tick_color { - axis = axis.tick_color(RgbPlotly::new(tick_color.0, tick_color.1, tick_color.2)); + if let Some(color) = format.tick_color { + axis = axis.tick_color(color.to_plotly()); } if let Some(tick_angle) = format.tick_angle { @@ -135,8 +124,8 @@ pub(crate) trait Layout { axis = axis.show_line(show_line.to_owned()); } - if let Some(line_color) = format.line_color { - axis = axis.line_color(RgbPlotly::new(line_color.0, line_color.1, line_color.2)); + if let Some(color) = format.line_color { + axis = axis.line_color(color.to_plotly()); } if let Some(line_width) = format.line_width { @@ -147,8 +136,8 @@ pub(crate) trait Layout { axis = axis.show_grid(show_grid.to_owned()); } - if let Some(grid_color) = format.grid_color { - axis = axis.grid_color(RgbPlotly::new(grid_color.0, grid_color.1, grid_color.2)); + if let Some(color) = format.grid_color { + axis = axis.grid_color(color.to_plotly()); } if let Some(grid_width) = format.grid_width { @@ -159,12 +148,8 @@ pub(crate) trait Layout { axis = axis.zero_line(show_zero_line.to_owned()); } - if let Some(zero_line_color) = format.zero_line_color { - axis = axis.zero_line_color(RgbPlotly::new( - zero_line_color.0, - zero_line_color.1, - zero_line_color.2, - )); + if let Some(color) = format.zero_line_color { + axis = axis.zero_line_color(color.to_plotly()); } if let Some(zero_line_width) = format.zero_line_width { @@ -180,11 +165,11 @@ pub(crate) trait Layout { fn set_legend_format(mut legend: LegendPlotly, format: &Legend) -> LegendPlotly { if let Some(color) = format.background_color { - legend = legend.background_color(RgbPlotly::new(color.0, color.1, color.2)); + legend = legend.background_color(color.to_plotly()); } if let Some(color) = format.border_color { - legend = legend.border_color(RgbPlotly::new(color.0, color.1, color.2)); + legend = legend.border_color(color.to_plotly()); } if let Some(width) = format.border_width { @@ -196,7 +181,7 @@ pub(crate) trait Layout { } if let Some(orientation) = &format.orientation { - legend = legend.orientation(orientation.get_orientation()); + legend = legend.orientation(orientation.to_plotly()); } if let Some(x) = format.x { diff --git a/src/common/line.rs b/src/common/line.rs index 0c2e9e2..edb65a2 100644 --- a/src/common/line.rs +++ b/src/common/line.rs @@ -30,13 +30,13 @@ pub(crate) trait Line { index: usize, ) -> LinePlotly { if let Some(style) = style { - line = line.dash(style.get_line_type()); + line = line.dash(style.to_plotly()); return line; } if let Some(styles) = styles { if let Some(style) = styles.get(index) { - line = line.dash(style.get_line_type()); + line = line.dash(style.to_plotly()); } } diff --git a/src/common/mark.rs b/src/common/mark.rs index fd43288..fde5ff3 100644 --- a/src/common/mark.rs +++ b/src/common/mark.rs @@ -65,13 +65,13 @@ pub(crate) trait Marker { index: usize, ) -> MarkerPlotly { if let Some(shape) = shape { - marker = marker.symbol(shape.get_shape()); + marker = marker.symbol(shape.to_plotly()); return marker; } if let Some(shapes) = shapes { if let Some(shape) = shapes.get(index) { - marker = marker.symbol(shape.get_shape()); + marker = marker.symbol(shape.to_plotly()); } } diff --git a/src/components/axis.rs b/src/components/axis.rs index 1048779..675c7c4 100644 --- a/src/components/axis.rs +++ b/src/components/axis.rs @@ -1,9 +1,6 @@ -use plotly::{ - common::AxisSide as AxisSidePlotly, - layout::{AxisType as AxisTypePlotly, TicksDirection}, -}; +use plotly::{common::AxisSide as AxisSidePlotly, layout::AxisType as AxisTypePlotly}; -use crate::components::{Rgb, ValueExponent}; +use crate::components::{Rgb, TickDirection, ValueExponent}; /// A structure representing a customizable axis. /// @@ -177,9 +174,9 @@ impl Axis { /// /// # Argument /// - /// * `tick_values` - A vector of `f64` values representing the tick values. - pub fn tick_values(mut self, tick_values: Vec) -> Self { - self.tick_values = Some(tick_values); + /// * `values` - A vector of `f64` values representing the tick values. + pub fn tick_values(mut self, values: Vec) -> Self { + self.tick_values = Some(values); self } @@ -187,9 +184,9 @@ impl Axis { /// /// # Argument /// - /// * `tick_labels` - A vector of values that can be converted into `String`, representing the tick labels. - pub fn tick_labels(mut self, tick_labels: Vec>) -> Self { - self.tick_labels = Some(tick_labels.into_iter().map(|x| x.into()).collect()); + /// * `labels` - A vector of values that can be converted into `String`, representing the tick labels. + pub fn tick_labels(mut self, labels: Vec>) -> Self { + self.tick_labels = Some(labels.into_iter().map(|x| x.into()).collect()); self } @@ -197,9 +194,9 @@ impl Axis { /// /// # Argument /// - /// * `tick_direction` - A `TickDirection` enum value representing the direction of the ticks. - pub fn tick_direction(mut self, tick_direction: TickDirection) -> Self { - self.tick_direction = Some(tick_direction); + /// * `direction` - A `TickDirection` enum value representing the direction of the ticks. + pub fn tick_direction(mut self, direction: TickDirection) -> Self { + self.tick_direction = Some(direction); self } @@ -207,9 +204,9 @@ impl Axis { /// /// # Argument /// - /// * `tick_length` - A `usize` value representing the length of the ticks. - pub fn tick_length(mut self, tick_length: usize) -> Self { - self.tick_length = Some(tick_length); + /// * `length` - A `usize` value representing the length of the ticks. + pub fn tick_length(mut self, length: usize) -> Self { + self.tick_length = Some(length); self } @@ -217,9 +214,9 @@ impl Axis { /// /// # Argument /// - /// * `tick_width` - A `usize` value representing the width of the ticks. - pub fn tick_width(mut self, tick_width: usize) -> Self { - self.tick_width = Some(tick_width); + /// * `width` - A `usize` value representing the width of the ticks. + pub fn tick_width(mut self, width: usize) -> Self { + self.tick_width = Some(width); self } @@ -227,9 +224,9 @@ impl Axis { /// /// # Argument /// - /// * `tick_color` - An `Rgb` struct representing the color of the ticks. - pub fn tick_color(mut self, tick_color: Rgb) -> Self { - self.tick_color = Some(tick_color); + /// * `color` - An `Rgb` struct representing the color of the ticks. + pub fn tick_color(mut self, color: Rgb) -> Self { + self.tick_color = Some(color); self } @@ -237,9 +234,9 @@ impl Axis { /// /// # Argument /// - /// * `tick_angle` - A `f64` value representing the angle of the ticks in degrees. - pub fn tick_angle(mut self, tick_angle: f64) -> Self { - self.tick_angle = Some(tick_angle); + /// * `angle` - A `f64` value representing the angle of the ticks in degrees. + pub fn tick_angle(mut self, angle: f64) -> Self { + self.tick_angle = Some(angle); self } @@ -247,9 +244,9 @@ impl Axis { /// /// # Argument /// - /// * `tick_font` - A value that can be converted into a `String`, representing the font name for the tick labels. - pub fn tick_font(mut self, tick_font: impl Into) -> Self { - self.tick_font = Some(tick_font.into()); + /// * `font` - A value that can be converted into a `String`, representing the font name for the tick labels. + pub fn tick_font(mut self, font: impl Into) -> Self { + self.tick_font = Some(font.into()); self } @@ -400,7 +397,7 @@ pub enum AxisSide { } impl AxisSide { - pub(crate) fn get_side(&self) -> AxisSidePlotly { + pub(crate) fn to_plotly(&self) -> AxisSidePlotly { match self { AxisSide::Top => AxisSidePlotly::Top, AxisSide::Bottom => AxisSidePlotly::Bottom, @@ -465,7 +462,7 @@ pub enum AxisType { } impl AxisType { - pub(crate) fn get_type(&self) -> AxisTypePlotly { + pub(crate) fn to_plotly(&self) -> AxisTypePlotly { match self { AxisType::Default => AxisTypePlotly::Default, AxisType::Linear => AxisTypePlotly::Linear, @@ -476,50 +473,3 @@ impl AxisType { } } } - -/// Enumeration representing the direction of axis ticks. -/// -/// # Example -/// -/// ```rust -/// use plotlars::{Axis, Plot, ScatterPlot, TickDirection}; -/// -/// let x = vec![1]; -/// let y = vec![1]; -/// -/// let dataset = DataFrame::new(vec![ -/// Series::new("x".into(), x), -/// Series::new("y".into(), y), -/// ]).unwrap(); -/// -/// ScatterPlot::builder() -/// .data(&dataset) -/// .x("x") -/// .y("y") -/// .x_axis( -/// &Axis::new() -/// .tick_direction(TickDirection::OutSide) -/// ) -/// .y_axis( -/// &Axis::new() -/// .tick_direction(TickDirection::InSide) -/// ) -/// .build() -/// .plot(); -/// ``` -/// -/// ![Example](https://imgur.com/9DSwJnx.png) -#[derive(Clone)] -pub enum TickDirection { - OutSide, - InSide, -} - -impl TickDirection { - pub(crate) fn get_direction(&self) -> TicksDirection { - match self { - TickDirection::OutSide => TicksDirection::Outside, - TickDirection::InSide => TicksDirection::Inside, - } - } -} diff --git a/src/components/color.rs b/src/components/color.rs index a58eeee..95d86cc 100644 --- a/src/components/color.rs +++ b/src/components/color.rs @@ -1,4 +1,4 @@ -use plotly::color::Color; +use plotly::color::{Color, Rgb as RgbPlotly}; use serde::Serialize; /// A structure representing an RGB color with red, green, and blue components. @@ -52,4 +52,11 @@ pub struct Rgb( pub u8, ); +impl Rgb { + #[allow(clippy::wrong_self_convention)] + pub(crate) fn to_plotly(&self) -> RgbPlotly { + RgbPlotly::new(self.0, self.1, self.2) + } +} + impl Color for Rgb {} diff --git a/src/components/exponent.rs b/src/components/exponent.rs index b6ebdc5..cf0b89f 100644 --- a/src/components/exponent.rs +++ b/src/components/exponent.rs @@ -42,7 +42,7 @@ pub enum ValueExponent { } impl ValueExponent { - pub(crate) fn get_exponent(&self) -> ExponentFormat { + pub(crate) fn to_plotly(&self) -> ExponentFormat { match self { ValueExponent::None => ExponentFormat::None, ValueExponent::SmallE => ExponentFormat::SmallE, diff --git a/src/components/line.rs b/src/components/line.rs index a02d5ac..b84e4d9 100644 --- a/src/components/line.rs +++ b/src/components/line.rs @@ -47,7 +47,8 @@ pub enum Line { } impl Line { - pub(crate) fn get_line_type(&self) -> DashType { + #[allow(clippy::wrong_self_convention)] + pub(crate) fn to_plotly(&self) -> DashType { match self { Line::Solid => DashType::Solid, Line::Dot => DashType::Dot, diff --git a/src/components/mod.rs b/src/components/mod.rs index 3c3154b..b6a7ce3 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -6,6 +6,7 @@ pub(crate) mod line; pub(crate) mod orientation; pub(crate) mod shape; pub(crate) mod text; +pub(crate) mod tick; pub(crate) use axis::Axis; pub(crate) use color::Rgb; @@ -15,3 +16,4 @@ pub(crate) use line::Line; pub(crate) use orientation::Orientation; pub(crate) use shape::Shape; pub(crate) use text::Text; +pub(crate) use tick::TickDirection; diff --git a/src/components/orientation.rs b/src/components/orientation.rs index c2c7854..42a8e29 100644 --- a/src/components/orientation.rs +++ b/src/components/orientation.rs @@ -53,7 +53,7 @@ pub enum Orientation { } impl Orientation { - pub(crate) fn get_orientation(&self) -> OrientationPlotly { + pub(crate) fn to_plotly(&self) -> OrientationPlotly { match self { Self::Horizontal => OrientationPlotly::Horizontal, Self::Vertical => OrientationPlotly::Vertical, diff --git a/src/components/shape.rs b/src/components/shape.rs index 65d7dad..323f6c2 100644 --- a/src/components/shape.rs +++ b/src/components/shape.rs @@ -215,7 +215,8 @@ pub enum Shape { } impl Shape { - pub(crate) fn get_shape(&self) -> MarkerSymbol { + #[allow(clippy::wrong_self_convention)] + pub(crate) fn to_plotly(&self) -> MarkerSymbol { match self { Shape::Circle => MarkerSymbol::Circle, Shape::CircleOpen => MarkerSymbol::CircleOpen, diff --git a/src/components/text.rs b/src/components/text.rs index fe09944..c0151b7 100644 --- a/src/components/text.rs +++ b/src/components/text.rs @@ -1,3 +1,5 @@ +use plotly::common::{Font, Title}; + use crate::components::Rgb; /// A structure representing text with customizable content, font, size, and color. @@ -47,6 +49,7 @@ use crate::components::Rgb; /// .plot(); /// ``` /// ![Example](https://imgur.com/4outoUQ.png) +#[derive(Clone)] pub struct Text { pub(crate) content: String, pub(crate) font: String, @@ -139,6 +142,18 @@ impl Text { self.y = y; self } + + pub(crate) fn to_plotly(&self) -> Title { + Title::with_text(&self.content) + .font( + Font::new() + .family(self.font.as_str()) + .size(self.size) + .color(self.color.to_plotly()), + ) + .x(self.x) + .y(self.y) + } } impl From<&str> for Text { diff --git a/src/components/tick.rs b/src/components/tick.rs new file mode 100644 index 0000000..229376b --- /dev/null +++ b/src/components/tick.rs @@ -0,0 +1,59 @@ +use plotly::{common::Ticks, layout::TicksDirection}; + +/// Enumeration representing the direction of axis ticks. +/// +/// # Example +/// +/// ```rust +/// use plotlars::{Axis, Plot, ScatterPlot, TickDirection}; +/// +/// let x = vec![1]; +/// let y = vec![1]; +/// +/// let dataset = DataFrame::new(vec![ +/// Series::new("x".into(), x), +/// Series::new("y".into(), y), +/// ]).unwrap(); +/// +/// ScatterPlot::builder() +/// .data(&dataset) +/// .x("x") +/// .y("y") +/// .x_axis( +/// &Axis::new() +/// .tick_direction(TickDirection::OutSide) +/// ) +/// .y_axis( +/// &Axis::new() +/// .tick_direction(TickDirection::InSide) +/// ) +/// .build() +/// .plot(); +/// ``` +/// +/// ![Example](https://imgur.com/9DSwJnx.png) +#[derive(Clone)] +pub enum TickDirection { + OutSide, + InSide, + None, +} + +impl TickDirection { + pub(crate) fn to_plotly_tickdirection(&self) -> TicksDirection { + match self { + TickDirection::OutSide => TicksDirection::Outside, + TickDirection::InSide => TicksDirection::Inside, + TickDirection::None => TicksDirection::Outside, + } + } + + #[allow(dead_code)] + pub(crate) fn to_plotly_ticks(&self) -> Ticks { + match self { + TickDirection::OutSide => Ticks::Outside, + TickDirection::InSide => Ticks::Inside, + TickDirection::None => Ticks::None, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 7f85a5a..9e95566 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ mod components; mod plots; pub use crate::common::plot::Plot; -pub use crate::components::axis::{Axis, AxisSide, AxisType, TickDirection}; +pub use crate::components::axis::{Axis, AxisSide, AxisType}; pub use crate::components::color::Rgb; pub use crate::components::exponent::ValueExponent; pub use crate::components::legend::Legend; @@ -14,6 +14,7 @@ pub use crate::components::line::Line; pub use crate::components::orientation::Orientation; pub use crate::components::shape::Shape; pub use crate::components::text::Text; +pub use crate::components::tick::TickDirection; pub use crate::plots::barplot::BarPlot; pub use crate::plots::boxplot::BoxPlot; pub use crate::plots::histogram::Histogram; diff --git a/src/plots/barplot.rs b/src/plots/barplot.rs index d380b0c..e9ba924 100644 --- a/src/plots/barplot.rs +++ b/src/plots/barplot.rs @@ -248,7 +248,7 @@ impl BarPlot { let mut trace = Bar::default() .x(labels) .y(values) - .orientation(orientation.get_orientation()); + .orientation(orientation.to_plotly()); if let Some(error) = error { let error = Self::get_numeric_column(data, error) @@ -271,7 +271,7 @@ impl BarPlot { let mut trace = Bar::default() .x(values) .y(labels) - .orientation(orientation.get_orientation()); + .orientation(orientation.to_plotly()); if let Some(error) = error { let error = Self::get_numeric_column(data, error) diff --git a/src/plots/boxplot.rs b/src/plots/boxplot.rs index bdf98dc..c26f9d0 100644 --- a/src/plots/boxplot.rs +++ b/src/plots/boxplot.rs @@ -271,7 +271,7 @@ impl BoxPlot { let mut trace = BoxPlotly::default() .x(category_data) .y(value_data) - .orientation(orientation.get_orientation()); + .orientation(orientation.to_plotly()); if let Some(all) = box_points { if all { @@ -301,7 +301,7 @@ impl BoxPlot { let mut trace = BoxPlotly::default() .x(value_data) .y(category_data) - .orientation(orientation.get_orientation()); + .orientation(orientation.to_plotly()); if let Some(all) = box_points { if all { From 221d4c1a4898ed018cdfbea6403e803681ce81d3 Mon Sep 17 00:00:00 2001 From: alceal Date: Fri, 1 Nov 2024 17:29:41 +0100 Subject: [PATCH 2/2] feat: Add HeatMap --- data/heatmap.csv | 201 ++++++++++++++++++ src/components/colorbar.rs | 404 +++++++++++++++++++++++++++++++++++++ src/components/mod.rs | 2 + src/components/palette.rs | 81 ++++++++ src/components/tick.rs | 1 - src/lib.rs | 3 + src/plots/heatmap.rs | 270 +++++++++++++++++++++++++ src/plots/mod.rs | 1 + 8 files changed, 962 insertions(+), 1 deletion(-) create mode 100644 data/heatmap.csv create mode 100644 src/components/colorbar.rs create mode 100644 src/components/palette.rs create mode 100644 src/plots/heatmap.rs diff --git a/data/heatmap.csv b/data/heatmap.csv new file mode 100644 index 0000000..71d65c4 --- /dev/null +++ b/data/heatmap.csv @@ -0,0 +1,201 @@ +x,y,z +x1,y1,5795 +x1,y2,-9140 +x1,y3,-4610 +x1,y4,1964 +x1,y5,1284 +x1,y6,-3735 +x1,y7,6850 +x1,y8,-5574 +x1,y9,4423 +x1,y10,1363 +x2,y1,6023 +x2,y2,-1678 +x2,y3,-8315 +x2,y4,-9231 +x2,y5,-7567 +x2,y6,-4689 +x2,y7,-4949 +x2,y8,-3580 +x2,y9,7568 +x2,y10,9769 +x3,y1,-3604 +x3,y2,-1334 +x3,y3,8942 +x3,y4,8431 +x3,y5,-7253 +x3,y6,-9811 +x3,y7,9118 +x3,y8,-6995 +x3,y9,-8101 +x3,y10,-8733 +x4,y1,7912 +x4,y2,1394 +x4,y3,-6444 +x4,y4,-6110 +x4,y5,-1162 +x4,y6,4502 +x4,y7,627 +x4,y8,-1208 +x4,y9,555 +x4,y10,253 +x5,y1,-1567 +x5,y2,233 +x5,y3,1016 +x5,y4,-7388 +x5,y5,5787 +x5,y6,7159 +x5,y7,2206 +x5,y8,-1774 +x5,y9,4541 +x5,y10,-6848 +x6,y1,-8415 +x6,y2,-6057 +x6,y3,9457 +x6,y4,-8979 +x6,y5,1653 +x6,y6,805 +x6,y7,3417 +x6,y8,-2011 +x6,y9,-308 +x6,y10,2990 +x7,y1,-3127 +x7,y2,-4325 +x7,y3,-9839 +x7,y4,-5703 +x7,y5,-9005 +x7,y6,1534 +x7,y7,-2371 +x7,y8,-8984 +x7,y9,-1471 +x7,y10,7262 +x8,y1,-732 +x8,y2,2185 +x8,y3,-3669 +x8,y4,-1429 +x8,y5,-2792 +x8,y6,-4724 +x8,y7,8446 +x8,y8,6448 +x8,y9,6216 +x8,y10,-1994 +x9,y1,-7432 +x9,y2,-7973 +x9,y3,-7305 +x9,y4,5422 +x9,y5,-4742 +x9,y6,-3264 +x9,y7,-9609 +x9,y8,3986 +x9,y9,2666 +x9,y10,-4108 +x10,y1,-6439 +x10,y2,-3816 +x10,y3,9483 +x10,y4,-1608 +x10,y5,3067 +x10,y6,5265 +x10,y7,9488 +x10,y8,-7546 +x10,y9,1837 +x10,y10,4039 +x11,y1,9115 +x11,y2,965 +x11,y3,-238 +x11,y4,-4944 +x11,y5,4948 +x11,y6,-1890 +x11,y7,3773 +x11,y8,7412 +x11,y9,-9498 +x11,y10,-3090 +x12,y1,2685 +x12,y2,-9794 +x12,y3,7868 +x12,y4,5934 +x12,y5,7247 +x12,y6,9174 +x12,y7,-1245 +x12,y8,2383 +x12,y9,8141 +x12,y10,4820 +x13,y1,-2426 +x13,y2,-3626 +x13,y3,-8322 +x13,y4,9626 +x13,y5,-8941 +x13,y6,6198 +x13,y7,-86 +x13,y8,9541 +x13,y9,817 +x13,y10,921 +x14,y1,-211 +x14,y2,6312 +x14,y3,1252 +x14,y4,-7307 +x14,y5,3931 +x14,y6,-6373 +x14,y7,6157 +x14,y8,173 +x14,y9,8047 +x14,y10,230 +x15,y1,5707 +x15,y2,1494 +x15,y3,-8694 +x15,y4,-3224 +x15,y5,-526 +x15,y6,-2474 +x15,y7,-4470 +x15,y8,-6252 +x15,y9,3545 +x15,y10,-9337 +x16,y1,-8002 +x16,y2,-2006 +x16,y3,7879 +x16,y4,-6696 +x16,y5,8237 +x16,y6,3808 +x16,y7,-3415 +x16,y8,7675 +x16,y9,9965 +x16,y10,1649 +x17,y1,-8364 +x17,y2,7082 +x17,y3,-5263 +x17,y4,4555 +x17,y5,3877 +x17,y6,-9146 +x17,y7,-4145 +x17,y8,-2608 +x17,y9,3949 +x17,y10,8091 +x18,y1,-4209 +x18,y2,-5069 +x18,y3,9894 +x18,y4,-9798 +x18,y5,1447 +x18,y6,2688 +x18,y7,-5611 +x18,y8,-7673 +x18,y9,-1996 +x18,y10,9315 +x19,y1,-2223 +x19,y2,-9803 +x19,y3,-8070 +x19,y4,1774 +x19,y5,5087 +x19,y6,-661 +x19,y7,1589 +x19,y8,8895 +x19,y9,5708 +x19,y10,7043 +x20,y1,-7189 +x20,y2,4243 +x20,y3,-3454 +x20,y4,-8014 +x20,y5,-1662 +x20,y6,1411 +x20,y7,-7089 +x20,y8,-8266 +x20,y9,8227 +x20,y10,-1320 diff --git a/src/components/colorbar.rs b/src/components/colorbar.rs new file mode 100644 index 0000000..dc683bf --- /dev/null +++ b/src/components/colorbar.rs @@ -0,0 +1,404 @@ +use plotly::common::{ColorBar as ColorBarPlotly, Font}; + +use crate::components::{Orientation, Rgb, Text, TickDirection, ValueExponent}; + +/// A structure representing a color bar component for visualizations. +/// +/// # Example +/// +/// ```rust +/// use plotlars::{ColorBar, HeatMap, Orientation, Palette, Plot, Text, ValueExponent}; +/// +/// let dataset = LazyCsvReader::new("data/heatmap.csv") +/// .finish() +/// .unwrap() +/// .collect() +/// .unwrap(); +/// +/// HeatMap::builder() +/// .data(&dataset) +/// .x("x") +/// .y("y") +/// .z("z") +/// .color_bar( +/// &ColorBar::new() +/// .orientation(Orientation::Horizontal) +/// .length(290) +/// .value_exponent(ValueExponent::None) +/// .separate_thousands(true) +/// .tick_length(5) +/// .tick_step(2500.0) +/// .tick_angle(90.0) +/// .y(-0.6) +/// ) +/// .color_scale(Palette::Viridis) +/// .build() +/// .plot(); +/// ``` +/// +/// ![Example](https://imgur.com/yZ4KFEU.png) +#[derive(Clone, Default)] +pub struct ColorBar { + pub(crate) background_color: Option, + pub(crate) border_color: Option, + pub(crate) border_width: Option, + pub(crate) tick_step: Option, + pub(crate) value_exponent: Option, + pub(crate) length: Option, + pub(crate) n_ticks: Option, + pub(crate) orientation: Option, + pub(crate) outline_color: Option, + pub(crate) outline_width: Option, + pub(crate) separate_thousands: Option, + pub(crate) width: Option, + pub(crate) tick_angle: Option, + pub(crate) tick_color: Option, + pub(crate) tick_font: Option, + pub(crate) tick_length: Option, + pub(crate) tick_labels: Option>, + pub(crate) tick_values: Option>, + pub(crate) tick_width: Option, + pub(crate) tick_direction: Option, + pub(crate) title: Option, + pub(crate) x: Option, + pub(crate) y: Option, +} + +impl ColorBar { + /// Creates a new `ColorBar` instance with default settings. + pub fn new() -> Self { + Self::default() + } + + /// Sets the background color of the color bar. + /// + /// # Arguments + /// + /// * `color` - An `Rgb` value representing the desired background color. + pub fn background_color(mut self, color: Rgb) -> Self { + self.background_color = Some(color); + self + } + + /// Sets the border color of the color bar. + /// + /// # Arguments + /// + /// * `color` - An `Rgb` value representing the desired border color. + pub fn border_color(mut self, color: Rgb) -> Self { + self.border_color = Some(color); + self + } + + /// Sets the width of the color bar's border. + /// + /// # Arguments + /// + /// * `width` - A `usize` value specifying the border width in pixels. + pub fn border_width(mut self, width: usize) -> Self { + self.border_width = Some(width); + self + } + + /// Sets the step size between ticks on the color bar. + /// + /// # Arguments + /// + /// * `step` - A `f64` value representing the step size between ticks. + pub fn tick_step(mut self, step: f64) -> Self { + self.tick_step = Some(step); + self + } + + /// Sets the exponent format for values on the axis. + /// + /// # Argument + /// + /// * `exponent` - A `ValueExponent` enum value representing the exponent format. + pub fn value_exponent(mut self, exponent: ValueExponent) -> Self { + self.value_exponent = Some(exponent); + self + } + + /// Sets the length of the color bar. + /// + /// # Arguments + /// + /// * `length` - A `usize` value specifying the length in pixels. + pub fn length(mut self, length: usize) -> Self { + self.length = Some(length); + self + } + + /// Sets the number of ticks on the color bar. + /// + /// # Arguments + /// + /// * `n` - A `usize` value representing the number of ticks. + pub fn n_ticks(mut self, n: usize) -> Self { + self.n_ticks = Some(n); + self + } + + /// Sets the orientation of the color bar. + /// + /// # Arguments + /// + /// * `orientation` - An `Orientation` enum value specifying the orientation (e.g., horizontal or vertical). + pub fn orientation(mut self, orientation: Orientation) -> Self { + self.orientation = Some(orientation); + self + } + + /// Sets the outline color of the color bar. + /// + /// # Arguments + /// + /// * `color` - An `Rgb` value representing the desired outline color. + pub fn outline_color(mut self, color: Rgb) -> Self { + self.outline_color = Some(color); + self + } + + /// Sets the outline width of the color bar. + /// + /// # Arguments + /// + /// * `width` - A `usize` value specifying the outline width in pixels. + pub fn outline_width(mut self, width: usize) -> Self { + self.outline_width = Some(width); + self + } + + /// Specifies whether to separate thousands in tick labels. + /// + /// # Arguments + /// + /// * `separate_thousands` - A `bool` indicating whether to separate thousands. + pub fn separate_thousands(mut self, separate_thousands: bool) -> Self { + self.separate_thousands = Some(separate_thousands); + self + } + + /// Sets the width of the color bar. + /// + /// # Arguments + /// + /// * `width` - A `usize` value specifying the width in pixels. + pub fn width(mut self, width: usize) -> Self { + self.width = Some(width); + self + } + + /// Sets the angle of the tick labels on the color bar. + /// + /// # Arguments + /// + /// * `angle` - A `f64` value representing the angle in degrees. + pub fn tick_angle(mut self, angle: f64) -> Self { + self.tick_angle = Some(angle); + self + } + + /// Sets the color of the ticks on the color bar. + /// + /// # Arguments + /// + /// * `color` - An `Rgb` value representing the tick color. + pub fn tick_color(mut self, color: Rgb) -> Self { + self.tick_color = Some(color); + self + } + + /// Sets the font of the tick labels on the color bar. + /// + /// # Arguments + /// + /// * `font` - A string representing the font family. + pub fn tick_font(mut self, font: impl Into) -> Self { + self.tick_font = Some(font.into()); + self + } + + /// Sets the length of the axis ticks. + /// + /// # Argument + /// + /// * `length` - A `usize` value representing the length of the ticks. + pub fn tick_length(mut self, length: usize) -> Self { + self.tick_length = Some(length); + self + } + + /// Sets the tick labels for the axis. + /// + /// # Argument + /// + /// * `labels` - A vector of values that can be converted into `String`, representing the tick labels. + pub fn tick_labels(mut self, labels: Vec>) -> Self { + self.tick_labels = Some(labels.into_iter().map(|x| x.into()).collect()); + self + } + + /// Sets the tick values for the axis. + /// + /// # Argument + /// + /// * `values` - A vector of `f64` values representing the tick values. + pub fn tick_values(mut self, values: Vec) -> Self { + self.tick_values = Some(values); + self + } + + /// Sets the width of the ticks on the color bar. + /// + /// # Arguments + /// + /// * `width` - A `usize` value specifying the tick width in pixels. + pub fn tick_width(mut self, width: usize) -> Self { + self.tick_width = Some(width); + self + } + + /// Sets the direction of the axis ticks. + /// + /// # Argument + /// + /// * `direction` - A `TickDirection` enum value representing the direction of the ticks. + pub fn tick_direction(mut self, direction: TickDirection) -> Self { + self.tick_direction = Some(direction); + self + } + + /// Sets the title of the color bar. + /// + /// # Arguments + /// + /// * `title` - A value that can be converted into `Text`, representing the title. + pub fn title>(mut self, title: T) -> Self { + self.title = Some(title.into()); + self + } + + /// Sets the x-coordinate position of the text. + /// + /// # Argument + /// + /// * `x` - A `f64` value specifying the horizontal position. + pub fn x(mut self, x: f64) -> Self { + self.x = Some(x); + self + } + + /// Sets the y-coordinate position of the text. + /// + /// # Argument + /// + /// * `y` - A `f64` value specifying the vertical position. + pub fn y(mut self, y: f64) -> Self { + self.y = Some(y); + self + } + + pub(crate) fn to_plotly(&self) -> ColorBarPlotly { + let mut color_bar = ColorBarPlotly::new(); + + if let Some(color) = &self.background_color { + color_bar = color_bar.background_color(color.to_plotly()); + } + + if let Some(color) = &self.border_color { + color_bar = color_bar.border_color(color.to_plotly()); + } + + if let Some(width) = self.border_width { + color_bar = color_bar.border_width(width); + } + + if let Some(step) = self.tick_step { + color_bar = color_bar.dtick(step); + } + + if let Some(value_exponent) = &self.value_exponent { + color_bar = color_bar.exponent_format(value_exponent.to_plotly()); + } + + if let Some(length) = self.length { + color_bar = color_bar + .len_mode(plotly::common::ThicknessMode::Pixels) + .len(length); + } + + if let Some(n_ticks) = self.n_ticks { + color_bar = color_bar.n_ticks(n_ticks); + } + + if let Some(orientation) = &self.orientation { + color_bar = color_bar.orientation(orientation.to_plotly()); + } + + if let Some(color) = self.outline_color { + color_bar = color_bar.outline_color(color.to_plotly()); + } + + if let Some(width) = self.outline_width { + color_bar = color_bar.outline_width(width); + } + + if let Some(separate_thousands) = self.separate_thousands { + color_bar = color_bar.separate_thousands(separate_thousands); + } + + if let Some(width) = self.width { + color_bar = color_bar + .thickness_mode(plotly::common::ThicknessMode::Pixels) + .thickness(width); + } + + if let Some(angle) = self.tick_angle { + color_bar = color_bar.tick_angle(angle); + } + + if let Some(color) = self.tick_color { + color_bar = color_bar.tick_color(color.to_plotly()); + } + + if let Some(font) = &self.tick_font { + color_bar = color_bar.tick_font(Font::new().family(font.as_str())); + } + + if let Some(length) = self.tick_length { + color_bar = color_bar.tick_len(length); + } + + if let Some(labels) = &self.tick_labels { + color_bar = color_bar.tick_text(labels.to_owned()) + } + + if let Some(values) = &self.tick_values { + color_bar = color_bar.tick_vals(values.to_owned()); + } + + if let Some(width) = self.tick_width { + color_bar = color_bar.tick_width(width); + } + + if let Some(tick_direction) = &self.tick_direction { + color_bar = color_bar.ticks(tick_direction.to_plotly_ticks()); + } + + if let Some(title) = &self.title { + color_bar = color_bar.title(title.to_plotly()); + } + + if let Some(x) = self.x { + color_bar = color_bar.x(x); + } + + if let Some(y) = self.y { + color_bar = color_bar.y(y); + } + + color_bar + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs index b6a7ce3..301246a 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,9 +1,11 @@ pub(crate) mod axis; pub(crate) mod color; +pub(crate) mod colorbar; pub(crate) mod exponent; pub(crate) mod legend; pub(crate) mod line; pub(crate) mod orientation; +pub(crate) mod palette; pub(crate) mod shape; pub(crate) mod text; pub(crate) mod tick; diff --git a/src/components/palette.rs b/src/components/palette.rs new file mode 100644 index 0000000..bad6130 --- /dev/null +++ b/src/components/palette.rs @@ -0,0 +1,81 @@ +use plotly::common::{ColorScale, ColorScalePalette}; + +/// +/// +/// # Example +/// +/// ```rust +/// use plotlars::{ColorBar, HeatMap, Palette, Plot, Text, ValueExponent}; +/// +/// let dataset = LazyCsvReader::new("../data/heatmap.csv") +/// .finish() +/// .unwrap() +/// .collect() +/// .unwrap(); +/// +/// HeatMap::builder() +/// .data(&dataset) +/// .x("x") +/// .y("y") +/// .z("z") +/// .color_bar( +/// &ColorBar::new() +/// .length(290) +/// .value_exponent(ValueExponent::None) +/// .separate_thousands(true) +/// .tick_length(5) +/// .tick_step(2500.0) +/// ) +/// .color_scale(Palette::Portland) +/// .build() +/// .plot(); +/// ``` +/// +/// ![Example](https://imgur.com/E9LHPAy.png) +#[derive(Clone, Copy)] +pub enum Palette { + Greys, + YlGnBu, + Greens, + YlOrRd, + Bluered, + RdBu, + Reds, + Blues, + Picnic, + Rainbow, + Portland, + Jet, + Hot, + Blackbody, + Earth, + Electric, + Viridis, + Cividis, +} + +impl Palette { + #[allow(clippy::wrong_self_convention)] + pub(crate) fn to_plotly(&self) -> ColorScale { + match self { + Palette::Greys => ColorScale::Palette(ColorScalePalette::Greys), + Palette::YlGnBu => ColorScale::Palette(ColorScalePalette::YlGnBu), + Palette::Greens => ColorScale::Palette(ColorScalePalette::Greens), + Palette::YlOrRd => ColorScale::Palette(ColorScalePalette::YlOrRd), + Palette::Bluered => ColorScale::Palette(ColorScalePalette::Bluered), + Palette::RdBu => ColorScale::Palette(ColorScalePalette::RdBu), + Palette::Reds => ColorScale::Palette(ColorScalePalette::Reds), + Palette::Blues => ColorScale::Palette(ColorScalePalette::Blues), + Palette::Picnic => ColorScale::Palette(ColorScalePalette::Picnic), + Palette::Rainbow => ColorScale::Palette(ColorScalePalette::Rainbow), + Palette::Portland => ColorScale::Palette(ColorScalePalette::Portland), + Palette::Jet => ColorScale::Palette(ColorScalePalette::Jet), + Palette::Hot => ColorScale::Palette(ColorScalePalette::Hot), + Palette::Blackbody => ColorScale::Palette(ColorScalePalette::Blackbody), + Palette::Earth => ColorScale::Palette(ColorScalePalette::Earth), + Palette::Electric => ColorScale::Palette(ColorScalePalette::Electric), + Palette::Viridis => ColorScale::Palette(ColorScalePalette::Viridis), + Palette::Cividis => ColorScale::Palette(ColorScalePalette::Cividis), + } + } +} diff --git a/src/components/tick.rs b/src/components/tick.rs index 229376b..5d7efcb 100644 --- a/src/components/tick.rs +++ b/src/components/tick.rs @@ -48,7 +48,6 @@ impl TickDirection { } } - #[allow(dead_code)] pub(crate) fn to_plotly_ticks(&self) -> Ticks { match self { TickDirection::OutSide => Ticks::Outside, diff --git a/src/lib.rs b/src/lib.rs index 9e95566..0aa3d8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,15 +8,18 @@ mod plots; pub use crate::common::plot::Plot; pub use crate::components::axis::{Axis, AxisSide, AxisType}; pub use crate::components::color::Rgb; +pub use crate::components::colorbar::ColorBar; pub use crate::components::exponent::ValueExponent; pub use crate::components::legend::Legend; pub use crate::components::line::Line; pub use crate::components::orientation::Orientation; +pub use crate::components::palette::Palette; pub use crate::components::shape::Shape; pub use crate::components::text::Text; pub use crate::components::tick::TickDirection; pub use crate::plots::barplot::BarPlot; pub use crate::plots::boxplot::BoxPlot; +pub use crate::plots::heatmap::HeatMap; pub use crate::plots::histogram::Histogram; pub use crate::plots::lineplot::LinePlot; pub use crate::plots::scatterplot::ScatterPlot; diff --git a/src/plots/heatmap.rs b/src/plots/heatmap.rs new file mode 100644 index 0000000..5d8d90a --- /dev/null +++ b/src/plots/heatmap.rs @@ -0,0 +1,270 @@ +use bon::bon; + +use plotly::{HeatMap as HeatMapPlotly, Layout as LayoutPlotly, Trace}; + +use polars::frame::DataFrame; +use serde::Serialize; + +use crate::{ + common::{Layout, Marker, Plot, Polar}, + components::{Axis, Text}, + ColorBar, Palette, +}; + +/// A structure representing a heat map. +/// +/// The `HeatMap` struct enables the creation of heat map visualizations with options for color scaling, +/// axis customization, legend adjustments, and data value formatting. Users can customize the color +/// scale, adjust the color bar, and set titles for the plot and axes, as well as format ticks and scales +/// for improved data readability. +/// +/// # Arguments +/// +/// * `data` - A reference to the `DataFrame` containing the data to be plotted. +/// * `x` - A string slice specifying the column name for x-axis values. +/// * `y` - A string slice specifying the column name for y-axis values. +/// * `z` - A string slice specifying the column name for z-axis values, which are represented by the color intensity. +/// * `auto_color_scale` - An optional boolean for enabling automatic color scaling based on data. +/// * `color_bar` - An optional reference to a `ColorBar` struct for customizing the color bar appearance. +/// * `color_scale` - An optional `Palette` enum for specifying the color scale (e.g., Viridis). +/// * `reverse_scale` - An optional boolean to reverse the color scale direction. +/// * `show_scale` - An optional boolean to display the color scale on the plot. +/// * `plot_title` - An optional `Text` struct for setting the title of the plot. +/// * `x_title` - An optional `Text` struct for labeling the x-axis. +/// * `y_title` - An optional `Text` struct for labeling the y-axis. +/// * `x_axis` - An optional reference to an `Axis` struct for customizing x-axis appearance. +/// * `y_axis` - An optional reference to an `Axis` struct for customizing y-axis appearance. +/// +/// # Example +/// +/// ```rust +/// use plotlars::{ColorBar, HeatMap, Palette, Plot, Text, ValueExponent}; +/// +/// let dataset = LazyCsvReader::new("data/heatmap.csv") +/// .finish() +/// .unwrap() +/// .collect() +/// .unwrap(); +/// +/// HeatMap::builder() +/// .data(&dataset) +/// .x("x") +/// .y("y") +/// .z("z") +/// .color_bar( +/// &ColorBar::new() +/// .length(290) +/// .value_exponent(ValueExponent::None) +/// .separate_thousands(true) +/// .tick_length(5) +/// .tick_step(2500.0) +/// ) +/// .plot_title( +/// Text::from("Heat Map") +/// .font("Arial") +/// .size(18) +/// ) +/// .color_scale(Palette::Viridis) +/// .build() +/// .plot(); +/// ``` +/// +/// ![Example](https://imgur.com/5uFih4M.png) +pub struct HeatMap { + traces: Vec>, + layout: LayoutPlotly, +} + +#[bon] +impl HeatMap { + #[builder(on(String, into), on(Text, into))] + pub fn new( + data: &DataFrame, + x: &str, + y: &str, + z: &str, + auto_color_scale: Option, + color_bar: Option<&ColorBar>, + color_scale: Option, + reverse_scale: Option, + show_scale: Option, + plot_title: Option, + x_title: Option, + y_title: Option, + x_axis: Option<&Axis>, + y_axis: Option<&Axis>, + ) -> Self { + let legend = None; + let legend_title = None; + + let layout = Self::create_layout( + plot_title, + x_title, + y_title, + legend_title, + x_axis, + y_axis, + legend, + ); + + let traces = Self::create_traces( + data, + x, + y, + z, + auto_color_scale, + color_bar, + color_scale, + reverse_scale, + show_scale, + ); + + Self { traces, layout } + } + + #[allow(clippy::too_many_arguments)] + fn create_traces( + data: &DataFrame, + x: &str, + y: &str, + z: &str, + auto_color_scale: Option, + color_bar: Option<&ColorBar>, + color_scale: Option, + reverse_scale: Option, + show_scale: Option, + ) -> Vec> { + let mut traces: Vec> = Vec::new(); + let trace = Self::create_trace( + data, + x, + y, + z, + auto_color_scale, + color_bar, + color_scale, + reverse_scale, + show_scale, + ); + traces.push(trace); + traces + } + + #[allow(clippy::too_many_arguments)] + fn create_trace( + data: &DataFrame, + x: &str, + y: &str, + z: &str, + auto_color_scale: Option, + color_bar: Option<&ColorBar>, + color_scale: Option, + reverse_scale: Option, + show_scale: Option, + ) -> Box { + let x = Self::get_string_column(data, x); + let y = Self::get_string_column(data, y); + let z = Self::get_numeric_column(data, z); + + let mut heat_map = HeatMapPlotly::default().x(x).y(y).z(z); + + heat_map = Self::set_auto_color_scale(heat_map, auto_color_scale); + heat_map = Self::set_color_bar(heat_map, color_bar); + heat_map = Self::set_color_scale(heat_map, color_scale); + heat_map = Self::set_reverse_scale(heat_map, reverse_scale); + heat_map = Self::set_show_scale(heat_map, show_scale); + heat_map + } + + fn set_auto_color_scale( + mut heat_map: Box>, + auto_color_scale: Option, + ) -> Box> + where + X: Serialize + Clone, + Y: Serialize + Clone, + Z: Serialize + Clone, + { + if let Some(auto_color_scale) = auto_color_scale { + heat_map = heat_map.auto_color_scale(auto_color_scale); + } + + heat_map + } + + fn set_color_bar( + mut heat_map: Box>, + color_bar: Option<&ColorBar>, + ) -> Box> + where + X: Serialize + Clone, + Y: Serialize + Clone, + Z: Serialize + Clone, + { + if let Some(color_bar) = color_bar { + heat_map = heat_map.color_bar(color_bar.to_plotly()) + } + + heat_map + } + + fn set_color_scale( + mut heat_map: Box>, + color_scale: Option, + ) -> Box> + where + X: Serialize + Clone, + Y: Serialize + Clone, + Z: Serialize + Clone, + { + if let Some(color_scale) = color_scale { + heat_map = heat_map.color_scale(color_scale.to_plotly()); + } + + heat_map + } + + fn set_reverse_scale( + mut heat_map: Box>, + reverse_scale: Option, + ) -> Box> + where + X: Serialize + Clone, + Y: Serialize + Clone, + Z: Serialize + Clone, + { + if let Some(reverse_scale) = reverse_scale { + heat_map = heat_map.reverse_scale(reverse_scale); + } + heat_map + } + + fn set_show_scale( + mut heat_map: Box>, + show_scale: Option, + ) -> Box> + where + X: Serialize + Clone, + Y: Serialize + Clone, + Z: Serialize + Clone, + { + if let Some(show_scale) = show_scale { + heat_map = heat_map.show_scale(show_scale); + } + heat_map + } +} + +impl Layout for HeatMap {} +impl Marker for HeatMap {} +impl Polar for HeatMap {} + +impl Plot for HeatMap { + fn get_layout(&self) -> &LayoutPlotly { + &self.layout + } + + fn get_traces(&self) -> &Vec> { + &self.traces + } +} diff --git a/src/plots/mod.rs b/src/plots/mod.rs index 4648e15..e68e8b1 100644 --- a/src/plots/mod.rs +++ b/src/plots/mod.rs @@ -1,5 +1,6 @@ pub(crate) mod barplot; pub(crate) mod boxplot; +pub(crate) mod heatmap; pub(crate) mod histogram; pub(crate) mod lineplot; pub(crate) mod scatterplot;