diff --git a/CHANGELOG.md b/CHANGELOG.md index a5ed842b..0ed9a42a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,11 +15,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a - [[#417](https://github.com/plotly/plotly.rs/issues/417)] Add `ScatterMap` (MapLibre `map` subplot) trace type — the modern counterpart to `ScatterMapbox` - [[#418](https://github.com/plotly/plotly.rs/issues/418)] Add native point clustering to `ScatterMap` via a `Cluster` option - [[#421](https://github.com/plotly/plotly.rs/pull/421)] Backfill trace attributes on trace types to bring them to parity with plotly.js 3.7 +- [[#422](https://github.com/plotly/plotly.rs/issues/422)] Add `Indicator`, `Histogram2d`, `Icicle` trace types ### Changed - [[#406](https://github.com/plotly/plotly.rs/issues/406)] Upgrade bundled plotly.js from 3.0.1 to 3.6.0 - [[#419](https://github.com/plotly/plotly.rs/issues/419)] Upgrade bundled plotly.js from 3.6.0 to 3.7.0 +- [[#422](https://github.com/plotly/plotly.rs/issues/422)] Add layout-level `uirevision` to preserve UI state (zoom/pan/selection) across re-renders (complements the trace-level `uirevision` added in #421) ## [0.14.1] - 2026-02-15 diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md index 02e3609f..974fa378 100644 --- a/docs/book/src/SUMMARY.md +++ b/docs/book/src/SUMMARY.md @@ -18,11 +18,14 @@ - [Sankey Diagrams](./recipes/basic_charts/sankey_diagrams.md) - [Treemap Charts](./recipes/basic_charts/treemap_charts.md) - [Sunburst Charts](./recipes/basic_charts/sunburst_charts.md) + - [Icicle Charts](./recipes/basic_charts/icicle_charts.md) + - [Indicator Charts](./recipes/basic_charts/indicator_charts.md) - [Statistical Charts](./recipes/statistical_charts.md) - [Error Bars](./recipes/statistical_charts/error_bars.md) - [Box Plots](./recipes/statistical_charts/box_plots.md) - [Violin Plots](./recipes/statistical_charts/violin_plots.md) - [Histograms](./recipes/statistical_charts/histograms.md) + - [2D Histograms](./recipes/statistical_charts/histogram2d.md) - [Scientific Charts](./recipes/scientific_charts.md) - [Contour Plots](./recipes/scientific_charts/contour_plots.md) - [Heatmaps](./recipes/scientific_charts/heatmaps.md) diff --git a/docs/book/src/recipes/basic_charts.md b/docs/book/src/recipes/basic_charts.md index ca8204c9..bfcc9365 100644 --- a/docs/book/src/recipes/basic_charts.md +++ b/docs/book/src/recipes/basic_charts.md @@ -11,3 +11,5 @@ Pie Charts | [![Pie Charts](./img/pie_charts.png)](./basic_charts/pie_charts.md) Sankey Diagrams | [![Sankey Diagrams](./img/basic_sankey.png)](./basic_charts/sankey_diagrams.md) Treemap Charts | [Treemap Charts](./basic_charts/treemap_charts.md) Sunburst Charts | [Sunburst Charts](./basic_charts/sunburst_charts.md) +Icicle Charts | [Icicle Charts](./basic_charts/icicle_charts.md) +Indicator Charts | [Indicator Charts](./basic_charts/indicator_charts.md) diff --git a/docs/book/src/recipes/basic_charts/icicle_charts.md b/docs/book/src/recipes/basic_charts/icicle_charts.md new file mode 100644 index 00000000..cd8fe66b --- /dev/null +++ b/docs/book/src/recipes/basic_charts/icicle_charts.md @@ -0,0 +1,27 @@ +# Icicle Charts + +The following imports have been used to produce the plots below: + +```rust,no_run +use plotly::color::NamedColor; +use plotly::common::{Line, Orientation}; +use plotly::icicle::{BranchValues, Leaf, Marker, PathBar, Side, Tiling}; +use plotly::{Icicle, Plot}; +``` + +The `to_inline_html` method is used to produce the html plot displayed in this page. + +## Basic Icicle +```rust,no_run +{{#include ../../../../../examples/basic_charts/src/main.rs:basic_icicle}} +``` + +{{#include ../../../../../examples/basic_charts/output/inline_basic_icicle.html}} + + +## Styled Icicle with Tiling and Path Bar +```rust,no_run +{{#include ../../../../../examples/basic_charts/src/main.rs:styled_icicle}} +``` + +{{#include ../../../../../examples/basic_charts/output/inline_styled_icicle.html}} diff --git a/docs/book/src/recipes/basic_charts/indicator_charts.md b/docs/book/src/recipes/basic_charts/indicator_charts.md new file mode 100644 index 00000000..41c5c685 --- /dev/null +++ b/docs/book/src/recipes/basic_charts/indicator_charts.md @@ -0,0 +1,31 @@ +# Indicator Charts + +The following imports have been used to produce the plots below: + +```rust,no_run +use plotly::color::NamedColor; +use plotly::common::{Domain, Line}; +use plotly::layout::Layout; +use plotly::traces::indicator::{ + Align, Delta, DeltaPosition, Direction, Gauge, GaugeAxis, GaugeBar, GaugeShape, + IndicatorTitle, Mode, Number, Step, Threshold, +}; +use plotly::{Indicator, Plot}; +``` + +The `to_inline_html` method is used to produce the html plot displayed in this page. + +## Basic Indicator with Number and Delta +```rust,no_run +{{#include ../../../../../examples/basic_charts/src/main.rs:basic_indicator}} +``` + +{{#include ../../../../../examples/basic_charts/output/inline_basic_indicator.html}} + + +## Indicator Gauge +```rust,no_run +{{#include ../../../../../examples/basic_charts/src/main.rs:indicator_gauge}} +``` + +{{#include ../../../../../examples/basic_charts/output/inline_indicator_gauge.html}} diff --git a/docs/book/src/recipes/img/basic_histogram2d.png b/docs/book/src/recipes/img/basic_histogram2d.png new file mode 100644 index 00000000..9305f524 Binary files /dev/null and b/docs/book/src/recipes/img/basic_histogram2d.png differ diff --git a/docs/book/src/recipes/statistical_charts.md b/docs/book/src/recipes/statistical_charts.md index 136aa7bc..7a210865 100644 --- a/docs/book/src/recipes/statistical_charts.md +++ b/docs/book/src/recipes/statistical_charts.md @@ -8,3 +8,4 @@ Error Bars |[![Scatter Plots](./img/error_bars.png)](./statistical_charts/error_ Box Plots | [![Line Charts](./img/box_plot.png)](./statistical_charts/box_plots.md) Violin Plots | [![Violin Plots](./img/violin_plot.png)](./statistical_charts/violin_plots.md) Histograms | [![Scatter Plots](./img/overlaid_histogram.png)](./statistical_charts/histograms.md) +2D Histograms | [![2D Histograms](./img/basic_histogram2d.png)](./statistical_charts/histogram2d.md) diff --git a/docs/book/src/recipes/statistical_charts/histogram2d.md b/docs/book/src/recipes/statistical_charts/histogram2d.md new file mode 100644 index 00000000..6fea6406 --- /dev/null +++ b/docs/book/src/recipes/statistical_charts/histogram2d.md @@ -0,0 +1,35 @@ +# 2D Histograms + +The following imports have been used to produce the plots below: + +```rust,no_run +use plotly::common::{ColorScale, ColorScalePalette}; +use plotly::histogram::{Bins, HistFunc, HistNorm}; +use plotly::layout::{Axis, Layout}; +use plotly::{Histogram2d, Plot}; +``` + +The `to_inline_html` method is used to produce the html plot displayed in this page. + +## Basic 2D Histogram +```rust,no_run +{{#include ../../../../../examples/statistical_charts/src/main.rs:basic_histogram2d}} +``` + +{{#include ../../../../../examples/statistical_charts/output/inline_basic_histogram2d.html}} + + +## Styled 2D Histogram +```rust,no_run +{{#include ../../../../../examples/statistical_charts/src/main.rs:styled_histogram2d}} +``` + +{{#include ../../../../../examples/statistical_charts/output/inline_styled_histogram2d.html}} + + +## 2D Histogram with Per-Sample Aggregation +```rust,no_run +{{#include ../../../../../examples/statistical_charts/src/main.rs:histogram2d_aggregation}} +``` + +{{#include ../../../../../examples/statistical_charts/output/inline_histogram2d_aggregation.html}} diff --git a/examples/basic_charts/src/main.rs b/examples/basic_charts/src/main.rs index e1b169fc..9af85696 100644 --- a/examples/basic_charts/src/main.rs +++ b/examples/basic_charts/src/main.rs @@ -7,6 +7,10 @@ use plotly::{ ColorScale, ColorScalePalette, DashType, Domain, Fill, Font, HoverInfo, Line, LineShape, Marker, Mode, Orientation, Pattern, PatternShape, }, + icicle::{ + BranchValues as IcicleBranchValues, Leaf as IcicleLeaf, Marker as IcicleMarker, + PathBar as IciclePathBar, Side as IcicleSide, Tiling as IcicleTiling, + }, layout::{ AngularAxis, Annotation, Axis, AxisRange, BarMode, CategoryOrder, Layout, LayoutGrid, LayoutPolar, Legend, PolarAxisAttributes, PolarAxisTicks, PolarDirection, RadialAxis, @@ -14,11 +18,15 @@ use plotly::{ }, sankey::{Line as SankeyLine, Link, Node}, sunburst::{InsideTextOrientation, Leaf}, + traces::indicator::{ + Align, Delta, DeltaPosition, Direction, Gauge, GaugeAxis, GaugeBar, GaugeShape, + IndicatorTitle, Mode as IndicatorMode, Number, Step, Threshold, + }, traces::table::{ Align as TableAlign, Cells, Fill as TableFill, Font as TableFont, Header, Line as TableLine, }, treemap::{BranchValues, Marker as TreemapMarker, Packing, PathBar, Side, Tiling}, - Bar, Pie, Plot, Sankey, Scatter, ScatterPolar, Sunburst, Table, Treemap, + Bar, Icicle, Indicator, Pie, Plot, Sankey, Scatter, ScatterPolar, Sunburst, Table, Treemap, }; use plotly_utils::write_example_to_html; use rand_distr::{Distribution, Normal, Uniform}; @@ -1147,6 +1155,137 @@ fn styled_sunburst(show: bool, file_name: &str) { } // ANCHOR_END: styled_sunburst +// Icicle Charts +// ANCHOR: basic_icicle +fn basic_icicle(show: bool, file_name: &str) { + let labels = vec![ + "Eve", "Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura", + ]; + let parents = vec![ + "", "Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve", + ]; + let trace = Icicle::new(labels, parents) + .values(vec![10.0, 14.0, 12.0, 10.0, 2.0, 6.0, 6.0, 4.0, 4.0]) + .text_info("label+value"); + + let mut plot = Plot::new(); + plot.add_trace(trace); + + let path = write_example_to_html(&plot, file_name); + if show { + plot.show_html(path); + } +} +// ANCHOR_END: basic_icicle + +// ANCHOR: styled_icicle +fn styled_icicle(show: bool, file_name: &str) { + let labels = vec![ + "Total", + "Tech", + "Health", + "Finance", + "Software", + "Hardware", + "Pharma", + "Devices", + "Banking", + "Insurance", + ]; + let parents = vec![ + "", "Total", "Total", "Total", "Tech", "Tech", "Health", "Health", "Finance", "Finance", + ]; + let trace = Icicle::new(labels, parents) + .values(vec![0.0, 0.0, 0.0, 0.0, 40.0, 30.0, 25.0, 15.0, 20.0, 18.0]) + .branch_values(IcicleBranchValues::Remainder) + .marker(IcicleMarker::new().line(Line::new().width(1.0).color(NamedColor::White))) + .tiling( + IcicleTiling::new() + .orientation(Orientation::Horizontal) + .pad(2.0), + ) + .path_bar(IciclePathBar::new().visible(true).side(IcicleSide::Top)) + .leaf(IcicleLeaf::new().opacity(0.7)) + .text_info("label+value+percent parent"); + + let mut plot = Plot::new(); + plot.add_trace(trace); + + let path = write_example_to_html(&plot, file_name); + if show { + plot.show_html(path); + } +} +// ANCHOR_END: styled_icicle + +// Indicator Charts +// ANCHOR: basic_indicator +fn basic_indicator(show: bool, file_name: &str) { + let trace = Indicator::new(120.0) + .mode(IndicatorMode::NumberAndDelta) + .align(Align::Center) + .title(IndicatorTitle::new().text("Revenue")) + .number(Number::new().prefix("$").value_format(".2f")) + .delta( + Delta::new() + .reference(100.0) + .relative(true) + .position(DeltaPosition::Top) + .increasing(Direction::new().color(NamedColor::Green).symbol("▲")) + .decreasing(Direction::new().color(NamedColor::Red).symbol("▼")), + ) + .domain(Domain::new()); + + let layout = Layout::new().title("Basic Indicator"); + let mut plot = Plot::new(); + plot.set_layout(layout); + plot.add_trace(trace); + + let path = write_example_to_html(&plot, file_name); + if show { + plot.show_html(path); + } +} +// ANCHOR_END: basic_indicator + +// ANCHOR: indicator_gauge +fn indicator_gauge(show: bool, file_name: &str) { + let trace = Indicator::new(270.0) + .mode(IndicatorMode::Gauge) + .title(IndicatorTitle::new().text("Speed")) + .gauge( + Gauge::new() + .shape(GaugeShape::Angular) + .axis(GaugeAxis::new().range([0.0, 500.0])) + .bar(GaugeBar::new().color("darkblue").thickness(0.75)) + .background_color(NamedColor::White) + .border_color(NamedColor::Gray) + .border_width(1.0) + .steps(vec![ + Step::new().range([0.0, 250.0]).color("lightgray"), + Step::new().range([250.0, 400.0]).color("gray"), + ]) + .threshold( + Threshold::new() + .line(Line::new().color(NamedColor::Red).width(4.0)) + .thickness(0.75) + .value(490.0), + ), + ) + .domain(Domain::new()); + + let layout = Layout::new().title("Indicator Gauge"); + let mut plot = Plot::new(); + plot.set_layout(layout); + plot.add_trace(trace); + + let path = write_example_to_html(&plot, file_name); + if show { + plot.show_html(path); + } +} +// ANCHOR_END: indicator_gauge + // ANCHOR: set_lower_or_upper_bound_on_axis fn set_lower_or_upper_bound_on_axis(show: bool, file_name: &str) { use std::fs::File; @@ -1312,6 +1451,14 @@ fn main() { basic_sunburst(false, "basic_sunburst"); styled_sunburst(false, "styled_sunburst"); + // Icicle Charts + basic_icicle(false, "basic_icicle"); + styled_icicle(false, "styled_icicle"); + + // Indicator Charts + basic_indicator(false, "basic_indicator"); + indicator_gauge(false, "indicator_gauge"); + // Set Lower or Upper Bound on Axis set_lower_or_upper_bound_on_axis(false, "set_lower_or_upper_bound_on_axis"); } diff --git a/examples/statistical_charts/src/main.rs b/examples/statistical_charts/src/main.rs index 2b4d2da9..deb9425a 100644 --- a/examples/statistical_charts/src/main.rs +++ b/examples/statistical_charts/src/main.rs @@ -4,11 +4,13 @@ use ndarray::Array; use plotly::{ box_plot::{BoxMean, BoxPoints}, color::{NamedColor, Rgb, Rgba}, - common::{ErrorData, ErrorType, Line, Marker, Mode, Orientation}, + common::{ + ColorScale, ColorScalePalette, ErrorData, ErrorType, Line, Marker, Mode, Orientation, + }, histogram::{Bins, Cumulative, HistFunc, HistNorm}, layout::{Axis, BarMode, BoxMode, Layout, Margin, ViolinMode}, violin::{MeanLine, ViolinBox, ViolinPoints, ViolinSide}, - Bar, BoxPlot, Histogram, Plot, Scatter, Violin, + Bar, BoxPlot, Histogram, Histogram2d, Plot, Scatter, Violin, }; use plotly_utils::write_example_to_html; use rand_distr::{Distribution, Normal, Uniform}; @@ -785,6 +787,87 @@ fn specify_binning_function(show: bool, file_name: &str) { } // ANCHOR_END: specify_binning_function +// 2D Histograms +// ANCHOR: basic_histogram2d +fn basic_histogram2d(show: bool, file_name: &str) { + let n = 5_000; + let x = sample_normal_distribution(n, 0.0, 1.0); + let y = sample_normal_distribution(n, 0.0, 1.0); + + let trace = Histogram2d::new(x, y).name("2d histogram"); + let mut plot = Plot::new(); + plot.add_trace(trace); + + let path = write_example_to_html(&plot, file_name); + if show { + plot.show_html(path); + } +} +// ANCHOR_END: basic_histogram2d + +// ANCHOR: styled_histogram2d +fn styled_histogram2d(show: bool, file_name: &str) { + let n = 2_000; + let x = sample_uniform_distribution(n, -3.0, 3.0); + let y = sample_uniform_distribution(n, -3.0, 3.0); + + let trace = Histogram2d::new(x, y) + .name("styled 2d histogram") + .hist_func(HistFunc::Count) + .hist_norm(HistNorm::Probability) + .auto_bin_x(false) + .auto_bin_y(false) + .n_bins_x(20) + .n_bins_y(20) + .x_bins(Bins::new(-3.0, 3.0, 0.3)) + .y_bins(Bins::new(-3.0, 3.0, 0.3)) + .color_scale(ColorScale::Palette(ColorScalePalette::Viridis)) + .show_scale(true); + + let layout = Layout::new() + .title("Styled 2D Histogram") + .x_axis(Axis::new().title("x")) + .y_axis(Axis::new().title("y")); + + let mut plot = Plot::new(); + plot.set_layout(layout); + plot.add_trace(trace); + + let path = write_example_to_html(&plot, file_name); + if show { + plot.show_html(path); + } +} +// ANCHOR_END: styled_histogram2d + +// ANCHOR: histogram2d_aggregation +fn histogram2d_aggregation(show: bool, file_name: &str) { + let x = vec![1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0]; + let y = vec![1.0, 2.0, 1.0, 2.0, 3.0, 2.0, 3.0]; + let z = vec![10.0, 20.0, 15.0, 25.0, 30.0, 5.0, 10.0]; + + let trace = Histogram2d::new_xyz(x, y, z) + .name("sum by bin") + .hist_func(HistFunc::Sum) + .color_scale(ColorScale::Palette(ColorScalePalette::Blues)) + .show_scale(true); + + let layout = Layout::new() + .title("2D Histogram with Per-Sample Aggregation") + .x_axis(Axis::new().title("x")) + .y_axis(Axis::new().title("y")); + + let mut plot = Plot::new(); + plot.set_layout(layout); + plot.add_trace(trace); + + let path = write_example_to_html(&plot, file_name); + if show { + plot.show_html(path); + } +} +// ANCHOR_END: histogram2d_aggregation + fn main() { // Change false to true on any of these lines to display the example. @@ -837,4 +920,9 @@ fn main() { cumulative_histogram(false, "cumulative_histogram"); normalized_histogram(false, "normalized_histogram"); specify_binning_function(false, "specify_binning_function"); + + // 2D Histograms + basic_histogram2d(false, "basic_histogram2d"); + styled_histogram2d(false, "styled_histogram2d"); + histogram2d_aggregation(false, "histogram2d_aggregation"); } diff --git a/plotly/src/common/mod.rs b/plotly/src/common/mod.rs index 05345710..b02792fc 100644 --- a/plotly/src/common/mod.rs +++ b/plotly/src/common/mod.rs @@ -224,8 +224,11 @@ pub enum PlotType { Contour, HeatMap, Histogram, + Histogram2d, Histogram2dContour, + Icicle, Image, + Indicator, Mesh3D, Ohlc, Sankey, diff --git a/plotly/src/layout/mod.rs b/plotly/src/layout/mod.rs index 1aef0bb0..027e3075 100644 --- a/plotly/src/layout/mod.rs +++ b/plotly/src/layout/mod.rs @@ -7,6 +7,7 @@ use update_menu::UpdateMenu; use crate::color::Color; use crate::common::{Calendar, ColorScale, Font, Label, Orientation, Title}; +use crate::private::NumOrString; pub mod themes; pub mod update_menu; @@ -257,6 +258,12 @@ pub struct LayoutFields { color_axis: Option, #[serde(rename = "modebar")] mode_bar: Option, + /// Controls persistence of user-driven UI changes (zoom, pan, selection, + /// legend visibility) across `Plotly.react`/`newPlot` calls: state is kept + /// while `uirevision` is unchanged. Trace-level `uirevision` overrides per + /// trace. + #[serde(rename = "uirevision")] + ui_revision: Option, /// Determines the mode of hover interactions. If "closest", a single /// hoverlabel will appear for the "closest" point within the /// `hoverdistance`. If "x" (or "y"), multiple hoverlabels will appear for @@ -654,6 +661,12 @@ mod tests { assert_eq!(to_value(template).unwrap(), expected); } + #[test] + fn serialize_layout_ui_revision() { + let layout = Layout::new().ui_revision("forever"); + assert_eq!(to_value(layout).unwrap()["uirevision"], json!("forever")); + } + #[test] fn serialize_layout() { let layout = Layout::new() diff --git a/plotly/src/lib.rs b/plotly/src/lib.rs index 7086da40..abe8e813 100644 --- a/plotly/src/lib.rs +++ b/plotly/src/lib.rs @@ -60,14 +60,16 @@ pub use layout::Layout; pub use plot::{Plot, Trace, Traces}; // Also provide easy access to modules which contain additional trace-specific types pub use traces::{ - box_plot, choropleth, choropleth_map, contour, density_map, heat_map, histogram, image, mesh3d, - sankey, scatter, scatter3d, scatter_map, scatter_mapbox, sunburst, surface, treemap, violin, + box_plot, choropleth, choropleth_map, contour, density_map, heat_map, histogram, histogram2d, + icicle, image, indicator, mesh3d, sankey, scatter, scatter3d, scatter_map, scatter_mapbox, + sunburst, surface, treemap, violin, }; // Bring the different trace types into the top-level scope pub use traces::{ Bar, BoxPlot, Candlestick, Choropleth, ChoroplethMap, Contour, DensityMap, DensityMapbox, - HeatMap, Histogram, Image, Mesh3D, Ohlc, Pie, Sankey, Scatter, Scatter3D, ScatterGeo, - ScatterMap, ScatterMapbox, ScatterPolar, Sunburst, Surface, Table, Treemap, Violin, + HeatMap, Histogram, Histogram2d, Icicle, Image, Indicator, Mesh3D, Ohlc, Pie, Sankey, Scatter, + Scatter3D, ScatterGeo, ScatterMap, ScatterMapbox, ScatterPolar, Sunburst, Surface, Table, + Treemap, Violin, }; pub trait Restyle: serde::Serialize {} diff --git a/plotly/src/traces/histogram2d.rs b/plotly/src/traces/histogram2d.rs new file mode 100644 index 00000000..22f54791 --- /dev/null +++ b/plotly/src/traces/histogram2d.rs @@ -0,0 +1,309 @@ +//! 2D histogram trace + +use plotly_derive::FieldSetter; +use serde::Serialize; + +use crate::{ + common::{ + Calendar, ColorBar, ColorScale, Dim, Font, HoverInfo, Label, LegendGroupTitle, PlotType, + Visible, XAxisId, YAxisId, + }, + private::{NumOrString, NumOrStringCollection}, + traces::heat_map::Smoothing, + traces::histogram::{Bins, HistFunc, HistNorm}, + Trace, +}; + +/// Construct a 2D histogram trace. +/// +/// A `Histogram2d` bins raw `x`/`y` samples into a 2D grid and renders the +/// binned counts as a heatmap. Provide raw `x`/`y` data and let Plotly.js bin +/// it via `hist_func`/`n_bins_x`/`n_bins_y`/`x_bins`/`y_bins`. Optionally +/// supply per-sample aggregation values via `z` (a 1D array aligned with +/// `x`/`y`) and a `hist_func` such as [`HistFunc::Sum`] or +/// [`HistFunc::Average`] to aggregate those values within each bin instead of +/// counting samples. +/// +/// Note: unlike [`HeatMap`](crate::HeatMap), `z` here is per-sample aggregation +/// data aligned with `x`/`y`, not a pre-binned 2D matrix. For a pre-computed +/// matrix, use [`HeatMap`](crate::HeatMap). +/// +/// # Examples +/// +/// ``` +/// use plotly::Histogram2d; +/// +/// let trace = Histogram2d::new(vec![1.0, 2.0, 2.0], vec![1.0, 1.0, 3.0]); +/// +/// let expected = serde_json::json!({ +/// "type": "histogram2d", +/// "x": [1.0, 2.0, 2.0], +/// "y": [1.0, 1.0, 3.0], +/// }); +/// +/// assert_eq!(serde_json::to_value(trace).unwrap(), expected); +/// ``` +#[serde_with::skip_serializing_none] +#[derive(Serialize, Debug, Clone, FieldSetter)] +#[field_setter(box_self, kind = "trace")] +pub struct Histogram2d +where + X: Serialize + Clone, + Y: Serialize + Clone, + Z: Serialize + Clone, +{ + #[field_setter(default = "PlotType::Histogram2d")] + r#type: PlotType, + name: Option, + visible: Option, + #[serde(rename = "showlegend")] + show_legend: Option, + #[serde(rename = "legendgroup")] + legend_group: Option, + #[serde(rename = "legendgrouptitle")] + legend_group_title: Option, + opacity: Option, + x: Option>, + y: Option>, + z: Option>, + #[serde(rename = "xaxis")] + x_axis: Option, + #[serde(rename = "yaxis")] + y_axis: Option, + /// Specifies the binning function used for this histogram trace. + #[serde(rename = "histfunc")] + hist_func: Option, + /// Specifies the type of normalization used for this histogram trace. + #[serde(rename = "histnorm")] + hist_norm: Option, + /// Determines whether or not the x-axis bin attributes are picked by an + /// algorithm. + #[serde(rename = "autobinx")] + auto_bin_x: Option, + /// Determines whether or not the y-axis bin attributes are picked by an + /// algorithm. + #[serde(rename = "autobiny")] + auto_bin_y: Option, + /// Specifies the maximum number of desired bins along the x axis. + #[serde(rename = "nbinsx")] + n_bins_x: Option, + /// Specifies the maximum number of desired bins along the y axis. + #[serde(rename = "nbinsy")] + n_bins_y: Option, + /// Sets the binning across the x axis. + #[serde(rename = "xbins")] + x_bins: Option, + /// Sets the binning across the y axis. + #[serde(rename = "ybins")] + y_bins: Option, + #[serde(rename = "autocolorscale")] + auto_color_scale: Option, + #[serde(rename = "colorbar")] + color_bar: Option, + #[serde(rename = "colorscale")] + color_scale: Option, + #[serde(rename = "reversescale")] + reverse_scale: Option, + #[serde(rename = "showscale")] + show_scale: Option, + zauto: Option, + zmin: Option, + zmax: Option, + zmid: Option, + #[serde(rename = "zhoverformat")] + zhover_format: Option, + zsmooth: Option, + #[serde(rename = "xgap")] + x_gap: Option, + #[serde(rename = "ygap")] + y_gap: Option, + #[serde(rename = "xcalendar")] + x_calendar: Option, + #[serde(rename = "ycalendar")] + y_calendar: Option, + #[serde(rename = "xhoverformat")] + x_hover_format: Option, + #[serde(rename = "yhoverformat")] + y_hover_format: Option, + #[serde(rename = "textfont")] + text_font: Option, + #[serde(rename = "texttemplate")] + text_template: Option>, + #[serde(rename = "texttemplatefallback")] + text_template_fallback: Option>, + #[serde(rename = "hoverinfo")] + hover_info: Option, + #[serde(rename = "hoverlabel")] + hover_label: Option