r3dplotly

Solid of revolution around Y axis 3D plots in R - multiple plots with plotly


I am trying to plot 2 solids of revolution using plotly in R, and would like to obtain an image with both of the plots side by side. However, the plots seem to appear on top of each others instead.

I start creating a 3D scatter plot following the very useful example found in this answer.

In my case, I wanted to use a gaussian curve and rotate it around the Y axis, instead of X axis as in the example, so I modified the code as below:

#my test with gaussian

r <- function(x,sigma,mu) 1/(sqrt(2*pi)*sigma)*(exp(-1*(x-mu)^2/2/(sigma^2)))

#centre distribution on 0
mu=0

#choose a sigma
sigma=1

#interval of interest on the x
int <- c(-5, 5)

# number of points along the x-axis
nx <- 1000

# number of points along the rotation
ntheta <- 100

# set x points and get corresponding radii = calculates the value of the function for all x points
coords <- data_frame(x = seq(int[1], int[2], length.out = nx), r = r(x, sigma, mu))

# for each r: rotate x to get xi and z coordinates
# edit: ensure 0 and pi are both amongst the angles used
coords %<>%
  rowwise() %>%
  do(data_frame(r = .$r, x = .$x,
                theta = seq(0, pi, length.out = ntheta / 2 + 1) %>%
                  c(pi + .[-c(1, length(.))]))) %>%
  
  ungroup %>%
  mutate(xi = x * sin(theta), z = x * cos(theta))

# plot points to make sure the coordinates define the desired shape
coords %>%
  plot_ly(x = ~xi, y = ~r, z = ~z, color = ~r)

#make a clean data frame with the things I need to plot so I don't get confused
X <- coords$xi
Y <- coords$r
Z <- coords$z

df1 <- data.frame(X, Y, Z)
head(df1)

#create the 3D plot with plotly

figsigma1 <- plot_ly(df1,
        type = 'scatter3d',
        mode='markers',
        x = ~X, 
        y = ~Y,
        z = ~Z,
        marker = list(
          color= ~Y,
          colorscale = "Viridis",
          cmax = 0.4,
          cmin = 0,
          colorbar=list(
            title='Intensity (AU)'
          ),
          reversescale =F
        )
)

figsigma1




#now everything is repeated to create another plot where the function has sigma=2

r <- function(x,sigma,mu) 1/(sqrt(2*pi)*sigma)*(exp(-1*(x-mu)^2/2/(sigma^2)))

#centre distribution on 0
mu=0

#choose a sigma
sigma=2

#interval of interest on the x
int <- c(-5, 5)

# number of points along the x-axis
nx <- 1000

# number of points along the rotation
ntheta <- 100

# set x points and get corresponding radii = calculates the value of the function for all x points
coords <- data_frame(x = seq(int[1], int[2], length.out = nx), r = r(x, sigma, mu))

# for each r: rotate x to get xi and z coordinates
# edit: ensure 0 and pi are both amongst the angles used
coords %<>%
  rowwise() %>%
  do(data_frame(r = .$r, x = .$x,
                theta = seq(0, pi, length.out = ntheta / 2 + 1) %>%
                  c(pi + .[-c(1, length(.))]))) %>%
  
  ungroup %>%
  mutate(xi = x * sin(theta), z = x * cos(theta))

# plot points to make sure the coordinates define the desired shape
coords %>%
  plot_ly(x = ~xi, y = ~r, z = ~z, color = ~r)

#make a clean data frame with the things I need to plot so I don't get confused
X <- coords$xi
Y <- coords$r
Z <- coords$z

df2 <- data.frame(X, Y, Z)
head(df2)

figsigma2 <- plot_ly(df2,
                     type = 'scatter3d',
                     mode='markers',
                     x = ~X, 
                     y = ~Y,
                     z = ~Z,
                     marker = list(
                       color= ~Y,
                       colorscale = "Viridis",
                       cmax = 0.4,
                       cmin = 0,
                       colorbar=list(
                         title='Intensity (AU)'
                       ),
                       reversescale =F
                     )
)

figsigma2

This gives me the plots I wanted

figsigma1

figsigma2

At this point I wanted to have the two plots side by side, so I tried the below:


main_plot <- subplot(figsigma1, figsigma2)
main_plot

However, this seems to plot them on top of each other instead of side by side... any suggestion?


Solution

  • You can do

    library(htmltools)
    browsable(
      div(style = "display: flex; flex-wrap: wrap; justify-content: center", 
        div(figsigma1, style = "width: 50%;"), 
        div(figsigma2, style = "width: 50%;")
      )
    )
    

    as described here - it's more flexible if you know your css.

    Or a bit more concise

    install.packages("manipulateWidget") 
    manipulateWidget::combineWidgets(figsigma1, figsigma2, nrow = 1)
    

    as per this great answer.