Surface::surface_color has the wrong type: it should be a 2D numeric array, not Vec<Box<dyn Color>> #440
Description
Description
In plotly/src/traces/surface.rs, the surfacecolor attribute of the Surface trace is defined as:
#[serde(rename = "surfacecolor")]
surface_color: Option<Vec<Box<dyn Color>>>,This doesn't match plotly.js. There, surface.surfacecolor is a data array:
Sets the surface color values, used for setting a color scale independent of
z.
The naming in this case is misleading and might have resulted in the current state. Plotly.js expects a 2D numeric matrix with the same shape as z and not some of the common string color representations that Plotly.js can digest. The numeric values are mapped through colorscale (and cmin/cmax/cmid) in place of z. A common use is e.g. coloring a surface by a second scalar field, e.g. showing temperature on a height map.
The current type causes two problems:
- It's only one-dimensional.
Coloris implemented for numeric types such asf64, so.surface_color(vec![0.1, 0.2, ...])compiles. But it can't express[[...], [...]], becauseVec<f64>doesn't implementColor. - It suggests CSS colors. Because of the
Colorbound, users and the existing unit test pass values like"#123456". Plotly.js doesn't accept those for this attribute.
Reproduction
use plotly::{Plot, Surface};
fn main() {
let z = vec![vec![0.0, 1.0], vec![1.0, 2.0]];
let c = vec![vec![2.0, 0.0], vec![0.0, 2.0]];
let trace = Surface::new(z)
.x(vec![0.0, 1.0])
.y(vec![0.0, 1.0])
.surface_color(c); // does not compile: `Vec<{float}>` does not implement `Color`
let mut plot = Plot::new();
plot.add_trace(trace);
plot.show();
}The existing unit test serialize_surface also expects JSON that plotly.js treats as invalid for this attribute:
.surface_color(vec!["#123456"])
// ...
"surfacecolor": ["#123456"],Expected behaviour
You can pass a 2D numeric array with the same shape as z, and it serializes as:
"surfacecolor": [[2.0, 0.0], [0.0, 2.0]]Proposed fix
Change the field to a 2D numeric array, for example:
#[serde(rename = "surfacecolor")]
surface_color: Option<Vec<Vec<f64>>>,and update the serialize_surface test:
.surface_color(vec![vec![0.0, 1.0]])
// ...
"surfacecolor": [[0.0, 1.0]],Considerations
- A 4th generic parameter
Surface<X, Y, Z, C = f64>withsurface_color: Option<Vec<Vec<C>>>. This is could be more flexible. Default type parameters on structs are allowed, but they aren't used when inferring the type ofSurface::new(...), so some existing call sites could still stop compiling. - Reuse
Z, i.e.Option<Vec<Vec<Z>>>. This is simpler, but it forces the color values to have the same type asz.
Environment
plotlycrate version: 0.14.1- Rust version: rustc 1.98.1
- OS: Windows
This would change the public API and might therefore be a breaking change. As this a fix to feature that was not working consider which level of release version this should result in.
I'm happy to open a PR once we agree on the preferred type.
Activity
andrei-ng commentedon Oct 9, 2026
I need to check. Will come back on this asap