Display multiple cylinders efficiently in RGL Plot
I am trying to plot cylinder models of trees in R, which consist of several thousand cylinders. Currently, I am trying to use the rgl
package for this task. Until now, I looped through all cylinders and added them separately to the 3D Plot.
Here an example with dummy data:
# dummy data
cylinder <- data.frame(start_X = rep(0:2, each = 2),
start_Y = rep(0:1, 3),
start_Z = 0,
end_X = rep(0:2, each = 2),
end_Y = rep(0:1, 3),
end_Z = 1)
radii <- seq(0.1, 0.5, length.out = 6)
# plotting
open3d()
for (i in 1:nrow(cylinder)) {
cyl <- cylinder3d(
center = cbind(
c(cylinder$start_X[i], cylinder$end_X[i]),
c(cylinder$start_Y[i], cylinder$end_Y[i]),
c(cylinder$start_Z[i], cylinder$end_Z[i])),
radius = radii[i],
closed = -2)
shade3d(cyl, col = "steelblue")
}
axes3d(edges = c("x", "y", "z"))
However, this approach takes ages when trying to plot several thousand cylinders. Is there a way to hand over several cylinder objects at once to the shade3d()
function? Can I create several separate cylinders in one call of cylinder3d()
? Or am I possibly using wrong functions or an inappropriate R package?
Solution 1:
There are quite a few possibilities.
The easiest change is to tell rgl
not to update the display until you are finished updating it using the par3d(skipRedraw=TRUE)
option. You need to run par3d(skipRedraw=FALSE)
when you want to see the updates. That often makes enough of a speedup.
If all of your cylinders were the same shape (but maybe rescaled), you could use sprites3d
; but you are varying the radius separately from the height, so that's not possible.
To do what you asked for (put them all into one shade3d
call) you could make a shape list. For example, after editing to allow separate colors,
cylinder <- data.frame(start_X = rep(0:2, each = 2),
start_Y = rep(0:1, 3),
start_Z = 0,
end_X = rep(0:2, each = 2),
end_Y = rep(0:1, 3),
end_Z = 1)
radii <- seq(0.1, 0.5, length.out = 6)
colors <- rainbow(6)
library(rgl)
thelist <- lapply(1:6, function(i) {
cyl <- cylinder3d(
center = cbind(
c(cylinder$start_X[i], cylinder$end_X[i]),
c(cylinder$start_Y[i], cylinder$end_Y[i]),
c(cylinder$start_Z[i], cylinder$end_Z[i])),
radius = radii[i],
closed = -2)
cyl$material$color <- colors[i]
cyl
})
# plotting
open3d()
shade3d(shapelist3d(thelist, plot = FALSE))
axes3d(edges = c("x", "y", "z"))
The shapelist3d()
function can take a list of meshes as above, or take a single mesh and with other arguments reshape and duplicate it. There probably won't be much difference in the speed.