Search code examples
rggplot2rglggraphtidygraph

3D pipe/tube plots in R - creating plots of tree roots


I'm trying to create 3D plots of simulated tree roots in R. Here is an example of a root system growing over time:

image 1

This is essentially a 3D network of cylinders, where the cylinder diameter (and, optionally, color) represents the size of the root. The available data includes:

  • x, y, z of the root centroid
  • direction of "parent" root (e.g. +x, -x, +y, -y, +z, -z), although this information could be captured in several different ways, including by calculating the x, y, z of the parent directly prior to plotting.
  • size of root

Example 3D data is here, but here is my first attempt at it in just 2D using ggplot2::geom_spoke:

dat <- data.frame(x = c(0,1,-1,0,1,-1),
              y = c(-1,-1,-1,-2,-2,-2),
              biomass = c(3,1.5,1.5,1,1,1), 
              parent.dir = c("+y","-x","+x","+y","+y","+y"))
dat$parent.dir <- as.numeric(as.character(factor(dat$parent.dir, 
                                             levels = c("-x", "+x", "-y", "+y"),
                                             labels = c(pi, 0, pi*3/2, pi/2)))) 

ggplot(dat, aes(x = x, y = y)) +
  geom_point(x = 0, y = 0, size = 20) +
  geom_spoke(radius = 1,
         aes(angle = parent.dir,
             size  = biomass)) + 
  coord_equal()

enter image description here

I prefer a solution based in the ggplot2 framework, but I realize that there are not a ton of 3D options for ggplot2. One interesting approach could be to creatively utilize the concept of network graphs via the ggraph and tidygraph packages. While those packages only operate in 2D as far as I know, their developer has also had some interesting related ideas in 3D that could also be applied.

The rgl library in seems to be the go-to for 3D plots in R, but an rgl solution just seems so much more complex and lacks the other benefits of ggplot2, such as faceting by year as in the example, easily adjusting scales, etc.

Example data is here:


Solution

  • I don't understand the format of your data so I'm sure this isn't the display you want, but it shows how to draw a bunch of cylinders in rgl:

    root <- read.csv("~/temp/root.csv")
    segments <- data.frame(row.names = unique(root$parent.direction),
                           x = c(-1,0,1,0,0),
                           y = c(0,1,0,0,-1),
                           z = c(0,0,0,0.2,0))
    library(rgl)
    open3d()
    for (i in seq_len(nrow(root))) {
            rbind(root[i,2:4], 
                  root[i,2:4] - segments[root$parent.direction[i],]) %>%
            cylinder3d(radius = root$size[i]^0.3, closed = -2, sides = 20) %>%
            shade3d(col = "green")
    }
    decorate3d() 
    

    This gives the following display (rotatable in the original):

    Lots of cylinders

    You can pass each cylinder through addNormals if you want it to look smooth, or use sides = <some big number> in the cylinder3d to make them look rounder.