Use conditional paint on a flat surface
I am using plotly via R for the first time and am trying to create a surface from a mesh and paint it based on a calculation.
For example, I would like to use a surface from data(volcano)
as in
library(plotly) plot_ly(z = ~volcano) %>% add_surface()
But instead of a color based on the z (height) value, let's say what I wanted to color based on the distance from my house on a small meza (20.60).
house_loc <- c(20,60,150) # (x,y,z) of my house
dist_to_house <- Vectorize(function(x,y,z){sqrt(sum( (c(x,y,z)-house_loc)^2 ))})
So far I have tried:
color_me <-function(x){
colorRampPalette(c('tan','blue')
)(24L)[findInterval(x,seq(0,1,length.out=25),
all.inside=TRUE)]
}
library(dplyr)
library(reshape2)
volcano %>%
melt( varnames=c('y','x'),value.name='z' ) %>%
mutate( d = dist_to_house(x, y, z) ,
d_rel = d/max(d),
d_color = color_me(d_rel)
) -> df
plot_ly(df,
type='scatter3d',
mode='none', # no markers, just surface
x=~x,
y=~y,
z=~z,
surfaceaxis=2,
surfacecolor=~d_color) # last argument seems not to work
What exactly returns:
The desired result would be to dye the landscape tan around the home and fade to blue in areas further away from the home.
A somewhat related question uses code mesh3d
found elsewhere and does not explain how to compute (i, j, k)
source to share
Your code is pretty much all you need, just use a graph surface
and use your distance array like color
.
library(plotly)
library(dplyr)
library(reshape2)
house_loc <- c(20,60,150)
dist_to_house <- Vectorize(function(x,y,z){sqrt(sum( (c(x,y,z)-house_loc)^2 ))})
volcano %>%
melt( varnames=c('y','x'),value.name='z' ) %>%
mutate( d = dist_to_house(x, y, z) ,
d_rel = d/max(d)
) -> df
color <- df$d_rel
dim(color) <- dim(volcano)
plot_ly(df,
type='surface',
z=volcano,
surfacecolor=color,
colors=c('tan','blue'))
source to share
In addition to the graph surface
(see the accepted answer) we can also make the graph mesh3d
and avoid the rearrangement step (back to grid) that the graph requires.
However, the scale is still incorrect (range is shown z
, not d_rel
)
plot_ly(df,
type='mesh3d',
x = ~x,
y = ~y,
z = ~z,
intensity=~d_rel,
colors = colorRamp(c("tan", "blue"))
)
Counter-intuitively, it is intensity=
, not color=
, which appears to be driving the conditional coloring.
I initially avoided mesh3d
it because I thought I needed to create a triangular mesh (something or something else) and had no idea how to do it, but in this case it is automatically handled.
source to share