2

I have this code: (I found somewhere on stackoverflow, just using it to illustrate my idea.)

x<-runif(20) 
y<-runif(20) 
z<-runif(20, -5,5) 

library(rgl) 
plot3d(x,y,z)

fit <- lm(z ~ poly(x,2) +poly(y,2) + x:y )

xnew <- seq(min(x), max(x), len=20) 
ynew <- seq(min(y), max(y), len=20) 
df <- expand.grid(x = xnew, 
                  y = ynew) 

df$z <- predict(fit, newdata=df)

surface3d(xnew, ynew, df$z, col="red")

enter image description here

My question is, how do I make the color a gradient of greens above zero, and reds below zero? Am I being clear?

I found this post: Formatting of persp3d plot

Which helps do the gradient, but I cannot figure out how to make it dependent on the positive/negative values of z, and how to restrict it to green and red, respectively.

Thanks for any help!

EDIT:

I tried to do some things below, adding this:

colors<-rep("red", length(df$z))
colors[(df$z > 0)] <- colorRampPalette(c("yellow","green"))(sum(df$z > 0))
colors[(df$z < 0)] <- colorRampPalette(c("red","yellow"))(sum(df$z < 0))

surface3d(xnew, ynew, df$z, col=colors)

Which produced this:

enter image description here

It looks like the gradient is along the wrong axis. It should be yellow near z = 0, getting more red aas z gets more negative, and more green as z gets more positive. But it is not doing that, applying the gradient (seemingly) along the y-axis.

Thoughts?

Community
  • 1
  • 1
lukehawk
  • 1,423
  • 3
  • 22
  • 48

3 Answers3

1

You can use the values of z tested against 0 to return a vector of 1's and 0's (and then coercing to 2's and 1's with a 1+-operation):

surface3d(xnew, ynew, df$z, col=c("red","blue")[1+(df$z > 0)] )
rgl.snapshot("zerocol.png",top=FALSE)

You do see a fair amount of jagginess at the color threshold because your grid is rather coarse. I see that I used the colors in a manner slightly different than requested, but the principle is to put the color name in the order of FALSE-z first and TRUE-z second (1+TRUE == 2). Cool saddle fit on my random draw.

enter image description here

IRTFM
  • 258,963
  • 21
  • 364
  • 487
  • Thanks! I am rolling with this, but still trying to figure out the gradient. – lukehawk Feb 11 '16 at 20:45
  • You mean the fuzzy blending of colors near the threshold? If I spin it a bit it seems that the borders get turned into step functions at the threshold. – IRTFM Feb 11 '16 at 20:52
  • No, sorry. I mean a gradual transition from red (most negative) to yellow (0), and another from yellow (0) to green (most positive). Does that make sense? – lukehawk Feb 11 '16 at 20:54
  • The 3d rotations while looking at the borders being turned into step functions at the threshold, makes me think the fancy lighting effects on flat surfaces are changing the apparent colors (hue?). You might try seeing if the RGL lighting functions can reduce the fuzz. – IRTFM Feb 11 '16 at 20:59
0

Here is a similar method which will highlight green>0 and red<0:

colors<-rep("red", length(df$z))
colors[(df$z>0.0)]<-"green"
surface3d(xnew, ynew, df$z, col=colors)

Define a array with the base color, change the color of the elements >0 and then plot.

Edit: To add a gradient check out the code sample for the rgl.surface command. This might be something closer to what you are looking for:

zlenpos<- max(df$z)+1
zlenneg<- 0-min(df$z)
colorscale<-rainbow(zlenpos+zlenneg, start = 0, end = 2/6)
colors<-colorscale[df$z - min(df$z) + 1]
surface3d(xnew, ynew, df$z, col=colors)

This performs the gradient from red to yellow to green but the yellow close to the zero of z. I'll leave this up to others to refine the details.

Dave2e
  • 22,192
  • 18
  • 42
  • 50
0

If your surface is specified as a function of x and y rather than a matrix, you can use the persp3d.function method: it will automatically work out the appropriate colours based on z values. See the first example in ?persp3d.function.

If your z is a matrix, you'll have to duplicate what persp3d.function does. You can see the source using rgl:::persp3d.function.

Generally for high level things like this you'll want to use persp3d; surface3d does the low level plotting of the surface.

user2554330
  • 37,248
  • 4
  • 43
  • 90