Main Functions
At the most basic level, the functions within RGL can be classified into 6 categories:
- Device Management Functions
- rgl.open()
- rgl.close()
- Scene management functions
- rgl.clear(“shapes”) - removes all the shapes from the scene
- Export functions
- rgl.snapshot(filename)
- writeWebGL() - creates a webpage for interactive plot
- rglwidget() - allows you to create an interactive plot within RMD files
- Shape functions
- rgl.lines(x,y,z)
- rgl.spheres(x,y,z)
- rgl.cylinder(x,y,z)
- rgl.quads(x,y,z)
- surface.3d(x,y,z)
- persp3d.(x,y,z)
- spin3d(x,y,z)
- play(3d)
- and more
- Environment functions
- rgl.viewpoint(theta,phi,zoom)
- rgl.bg3d()
Points and Spheres
At the most basic level, we can create 3d scatterplots and overlay some planes. But first, we need to open an rgl device in order to plot. We can then fix our background, change our viewpoint, and finally draw shapes. In order to draw this graph in html output, we need to call the widget: rglwidget()
#open a device
rgl.open()
#Fix the background color
bg3d(color="white")
#Fix the viewpoint
rgl.viewpoint(theta=0,phi=15,zoom=1.5)
#Create a 3D axis
rgl_add_axes(x,y,z)
#Generate the output and push to screen
rglwidget()
(insert graphic here)
Now we can do some more complex output, leveraging what we just used, but adding in a scatterplot using rgl.spheres(), calculating a regression on two variables, and creating a plane that corresponds to our predicted values for z using surface3d(). Usage of those functions will look something like this:
#Where x,y,z are vectors of points, so in our case, just input the column of our datafile
rgl.spheres(x,y,z,radius=0.2)
#We create a grid of x,y points, feed it into our regression and get z.pred. Then we change the transparency of the plane using alpha so it doesn't block out our points.
surface3d(x.pred,y.pred,z.pred,alpha=0.5)
rglwidget()
(insert graphic here)
Before we generate our plots, lets create a function that will allow us to customize our preferred setup for these scatterplots and illustrate some more functions:
#Want to add my own function for generating axes
rgl_add_axes <- function(x,y,z, axis.col="black", xlab="",ylab="",
zlab="", show.plane=TRUE,show.bbox=FALSE){
lim <- function(x){c(-max(abs(x)),max(abs(x))*1.1)}
xlim <- lim(x); ylim<-lim(y); zlim<-lim(z)
rgl.lines(xlim,c(0,0),c(0,0),color=axis.col)
rgl.lines(c(0,0),ylim,c(0,0),color=axis.col)
rgl.lines(c(0,0),c(0,0),zlim,color=axis.col)
axes <-rbind(c(xlim[2],0,0),c(0,ylim[2],0),c(0,0,zlim[2]))
rgl.points(axes,color=axis.col,size=2)
rgl.texts(axes,text=c(xlab,ylab,zlab),color=axis.col,
adj = c(0.5,-0.8),size=2)
if(show.plane){
xlim <- xlim/1.1; zlim<-zlim /1.1;
rgl.quads(x=rep(xlim,each=2), y = c(0,0,0,0),
z=c(zlim[1],zlim[2],zlim[2],zlim[1]), alpha=0.1)
}
}
And here is a 3D scatterplot of European Stock Exchange Daily Percentage Changes from 1990:
rgl.spheres(x,y,z,r=0.1, color="#D95F02")
fit <- lm(z ~ x + y)
grid.lines=25
x.pred <-seq(-5.0,5.0,length.out=grid.lines)
y.pred <-seq(-5.0,5.0,length.out=grid.lines)
xy <- expand.grid(x=x.pred,y=y.pred)
z.pred <- matrix(predict(fit,newdata=xy),nrow=grid.lines, ncol=grid.lines)
surface3d(x.pred, y.pred, z.pred, color="steelblue", alpha=0.5, lit=FALSE,
front="lines", back="lines")
surface3d(x.pred, y.pred, z.pred, color="steelblue", alpha=0.5, lit=FALSE)
rglwidget()
(insert graphic here)
Surfaces
We can do more than just create scatterplots, we can also generate complex 3d surfaces. Here we’ve created some reprentatives of spherical harmonics:
Spherical Harmonics (l=4)
rgl.open()
theta<-seq(from = 0, to = PI, by = PI/320)
phi<-seq(from = 0, to = 2*PI, by = PI/160)
m<-length(phi)
n<-length(theta);
Phi<-matrix(rep(phi,each=n), nrow=n)
Theta<-matrix(rep(theta,m),nrow=n)
r = Re(3/8*sqrt(35/(PI))*exp(-3i*Phi)*(sin(theta)^3)*cos(Theta))
x = r*cos(Phi)
y = r*sin(Phi)
z = r*cos(Theta)
surface3d(x,y,z, color = terrain.colors(n))
rglwidget()
(insert graphic here)
Spherical Harmonics (l=4)
rgl.open()
r = Re(3/16*sqrt(5/(PI))*exp(-1i*Phi)*sin(Theta)*(7*(cos(theta)^3)-3*cos(theta)))
x = r*cos(Phi)
y = r*sin(Phi)
z = r*cos(Theta)
surface3d(x,y,z, color = terrain.colors(n))
play3d(spin3d(axis=c(0,0,1), rpm = 8), duration = 5)
rglwidget()
(insert graphic here)
Spherical Harmonics (l=5)
rgl.open()
r = Re(3/16*sqrt(385/(2*PI))*exp(-4i*Phi)*(sin(theta)^4)*cos(Theta))
x = r*cos(Phi)
y = r*sin(Phi)
z = r*cos(Theta)
surface3d(x,y,z, color = terrain.colors(n))
rglwidget()
(insert graphic here)
Cylinders and Perspectives
Knots in Space
rgl.open()
bg3d(color="white")
theta<-seq(0,2*PI,len=50)
knot<-cylinder3d(center = cbind(sin(theta) + 3*sin(2*theta),
2*sin(3*theta), cos(theta)-2*cos(2*theta)),
e1 = cbind(cos(theta) + 4*cos(2*theta), 6*cos(3*theta),
sin(theta) + 3*sin(2*theta)),
radius = 0.9,
closed=TRUE)
shade3d(addNormals(subdivision3d(knot,depth=2)),
col=terrain.colors(length(theta)))
rglwidget()
(insert graphic here)
Spinning Globe
Using spin3d() and playwidget(), we can also generate animations of our 3d plots:
rgl.open()
rgl.viewpoint(theta=10,phi=-90,zoom=1.3)
lat <- matrix(seq(90,-90,len=50)*PI/180, 50, 50, byrow=TRUE)
long <- matrix(seq(-180,180,len=50)*PI/180, 50,50)
r <- 6378.1
x <- r*cos(lat)*cos(long)
y <- r*cos(lat)*sin(long)
z <-r*sin(lat)
lim <- function(x){
c(-max(abs(x)), max(abs(x))) * 2.0
}
plot1 <- persp3d(x,y,z,col="white",
texture=system.file("textures/worldsmall.png",package="rgl"),
specular="black", axes=FALSE)
plot2 <- rgl.lines(c(0,0),c(0,0),lim(z),color="black")
plot3 <- spin3d()
rglwidget() %>%
playwidget(par3dinterpControl(plot3,0,12,steps=40),step=0.01)
(insert graphic here)
3D Scatterplot
We will be putting together many of rgl’s main functions to generate a 3D scatterplot from the iris dataset:
#start rgl
rgl.open()
#rgl.init()
rgl.bg(color = "white") # Setup the background color
shapelist3d(octahedron3d(), x, y, z, size = .17,
color = group.col[as.numeric(groups)])
rgl_add_axes(x, y, z)
rgl.bbox(xlen = 0, ylen = 0, zlen = 0, color = c('grey100'))
axis3d('x', pos=c( NA, 0, 0 ), col = "black")
axis3d('y', pos=c( 0, NA, 0 ), col = "black")
axis3d('z', pos=c( 0, 0, NA ), col = "black")
# Compute ellipse for each group
for (i in 1:length(levs)) {
group <- levs[i]
selected <- groups == group
xx <- x[selected]; yy <- y[selected]; zz <- z[selected]
ellips <- ellipse3d(cov(cbind(xx,yy,zz)),
centre=c(mean(xx), mean(yy), mean(zz)), level = 0.95)
shade3d(ellips, col = group.col[i], alpha = 0.1, lit = FALSE)
# show group labels and move them out to the left
texts3d(mean(xx),mean(yy), mean(zz), text = group,
col= group.col[i], adj=c(1,1), justify="left",cex = 1)
}
aspect3d(1,1,1)
rglwidget()
(insert graphic here)