diff --git a/examples/3d_charts/Cargo.toml b/examples/3d_charts/Cargo.toml index 21a4f1ce..48058077 100644 --- a/examples/3d_charts/Cargo.toml +++ b/examples/3d_charts/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" [dependencies] ndarray = "0.15.6" +rand = "0.8.5" plotly = { path = "../../plotly" } diff --git a/examples/3d_charts/src/main.rs b/examples/3d_charts/src/main.rs index 87653b57..e575daf1 100644 --- a/examples/3d_charts/src/main.rs +++ b/examples/3d_charts/src/main.rs @@ -3,10 +3,11 @@ use ndarray::Array; use plotly::{ color::Rgb, - common::{ColorScale, ColorScalePalette, Font, Marker, MarkerSymbol, Mode, Title}, + common::{ColorBar, ColorScale, ColorScalePalette, Font, Marker, MarkerSymbol, Mode, Title}, layout::{Axis, Camera, Layout, LayoutScene, Legend, Margin, ProjectionType}, Mesh3D, Plot, Scatter3D, Surface, }; +use rand::Rng; // 3D Scatter Plots fn simple_scatter3d_plot() { @@ -41,10 +42,11 @@ fn customized_scatter3d_plot() { .map(|i| (i.abs() * 25f64) as usize) .collect(), ) - .color_scale(ColorScale::Palette(ColorScalePalette::Viridis)), + .color_scale(ColorScale::Palette(ColorScalePalette::Viridis)) + .color_array(z.clone()), ); - let trace2 = Scatter3D::new(t, z, y) + let trace2 = Scatter3D::new(t, z.clone(), y) .name("Helix 2") .mode(Mode::Markers) .marker( @@ -55,7 +57,8 @@ fn customized_scatter3d_plot() { .map(|i| (i.abs() * 25f64) as usize) .collect(), ) - .color_scale(ColorScale::Palette(ColorScalePalette::Viridis)), + .color_scale(ColorScale::Palette(ColorScalePalette::Viridis)) + .color_array(z), ); let mut plot = Plot::new(); @@ -161,6 +164,71 @@ fn mesh_3d_plot() { plot.show(); } +fn colorscale_plot() { + let mut plot = Plot::new(); + + let x = (0..100) + .map(|x| ((x - 50) as f64) / 100f64) + .collect::>(); + + let y = x.clone(); + + let iproduct = |x: &[f64], y: &[f64]| -> Vec<(f64, f64)> { + let mut result = Vec::new(); + for x in x { + for y in y { + result.push((*x, *y)); + } + } + result + }; + + let ((x, y), z): ((Vec, Vec), Vec) = iproduct(&x, &y) + .into_iter() + .map(|(x, y)| ((x, y), -(x.powi(2) + y.powi(2)) + 0.5)) + .unzip(); + + let color: Vec = z.clone().into_iter().rev().map(|x| x as f32).collect(); + let _color: Vec = (0..z.len()).collect(); + let _color: Vec = (0..z.len()).map(|x| x as u8).collect(); + let _color: Vec = { + let mut rng = rand::thread_rng(); + (0..z.len()).map(|_| rng.gen_range(0..100)).collect() + }; + + let color_max = color.iter().fold(f64::MIN, |acc, x| acc.max(*x as f64)); + + let colorscale = ColorScalePalette::YlGnBu; + + let marker = Marker::new() + .color_array(color) + .color_scale(plotly::common::ColorScale::Palette(colorscale.clone())) + .cauto(false) + .cmax(color_max * 1.5) + .color_bar(ColorBar::new()); + + let scatter = Scatter3D::new(x, y, z).mode(Mode::Markers).marker(marker); + + plot.add_trace(scatter); + + let layout = Layout::new() + .font(Font::new().size(18).family("Palatino-Linotype")) + .title(format!("Colorscale: {colorscale:?}").as_str().into()) + .width(1200) + .height(1000) + .scene( + LayoutScene::new() + .aspect_mode(plotly::layout::AspectMode::Data) + .x_axis(Axis::new().tick_format(".1f")) + .y_axis(Axis::new().tick_format(".1f")) + .z_axis(Axis::new().tick_format(".1f")), + ); + + plot.set_layout(layout); + + plot.show(); +} + fn main() { // Uncomment any of these lines to display the example. @@ -168,6 +236,7 @@ fn main() { // simple_scatter3d_plot(); // simple_line3d_plot(); // customized_scatter3d_plot(); + // colorscale_plot(); // Surface Plots // surface_plot(); diff --git a/plotly/src/common/color.rs b/plotly/src/common/color.rs index 06121ef8..1d7a03af 100644 --- a/plotly/src/common/color.rs +++ b/plotly/src/common/color.rs @@ -33,6 +33,17 @@ impl Color for &'static str {} impl Color for String {} impl Color for Rgb {} impl Color for Rgba {} +impl Color for f64 {} +impl Color for f32 {} +impl Color for u64 {} +impl Color for u32 {} +impl Color for u16 {} +impl Color for u8 {} +impl Color for i64 {} +impl Color for i32 {} +impl Color for i16 {} +impl Color for i8 {} +impl Color for usize {} /// ColorArray is only used internally to provide a helper method for converting /// Vec to Vec>, as we would otherwise fall foul of @@ -290,6 +301,20 @@ mod tests { assert_eq!(to_value(color).unwrap(), json!("any_arbitrary_string")); } + #[test] + fn test_serialize_numbers() { + assert_eq!(to_value(1f64).unwrap(), json!(1f64)); + assert_eq!(to_value(1f32).unwrap(), json!(1f32)); + assert_eq!(to_value(1i64).unwrap(), json!(1i64)); + assert_eq!(to_value(1i32).unwrap(), json!(1i32)); + assert_eq!(to_value(1i16).unwrap(), json!(1i16)); + assert_eq!(to_value(1i8).unwrap(), json!(1i8)); + assert_eq!(to_value(1u64).unwrap(), json!(1u64)); + assert_eq!(to_value(1u32).unwrap(), json!(1u32)); + assert_eq!(to_value(1u16).unwrap(), json!(1u16)); + assert_eq!(to_value(1u8).unwrap(), json!(1u8)); + } + #[test] #[rustfmt::skip] fn test_serialize_named_color() {