The three dimensional model of the acetylcholine molecule shown below is interactive, when your browser supports WebGL. You can use your mouse to rotate and zoom the model. In this post I will show you the main steps required to create such an interactive 3D model in R: easy peasy (more or less).
Drag mouse to rotate model. Use mouse wheel or middle button to zoom it.
There are several R packages available for cheminformatics. However, as far as I could tell, there aren't any packages that are directly able to create 3D visualisations of molecules. Therefore, we need the capabilities of the rcdk package to load molecule files and 3D rendering capabilities of the rgl package.
Although it is possible to derive a 3D structure from a SMILES code (see e.g., the SMILES translator from the Computer-Aided Drug Design Group), I couldn't find such functionality in R packages. Therefore, we use an existing 3D model of the acetylcholine molecule from PubChem in this example. The first part of the script downloads the molecule and then loads it into the working memory.
# required packages | |
require(rcdk) | |
require(rgl) | |
# download 3D structure of acetylcholine from Pubchem: | |
download.file("https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/187/record/SDF/?record_type=3d&response_type=save&response_basename=Structure3D_CID_187", | |
"acetylcholine.sdf", mode = "wb") | |
# load the downloaded file: | |
molfile <- load.molecules("acetylcholine.sdf")[[1]] |
# create a data.frame with atom properties | |
atom.props <- data.frame( | |
symbol = c("O", "N", "C", "H"), | |
vanderwaals = c(1.52, 1.55, 1.7, 1.2), #Van der Waals radii of atoms | |
colour = c("red", "blue", grey(0.1), "white") | |
) | |
# get atoms from the molecule | |
atoms <- get.atoms(molfile) | |
# get 3D locations of atoms as read from the file | |
atom_dat <- data.frame(do.call('rbind', lapply(atoms, get.point3d))) | |
names(atom_dat) <- c("x", "y", "z") | |
# merge with atom info | |
atom_dat$symbol <- unlist(lapply(atoms, get.symbol)) | |
atom_dat <- merge(atom_dat, atom.props, all.x = T, all.y = F) |
# simply plot the atoms as coloured spheres. | |
# multiply the Van der Waals radii with 0.75 | |
# to reduce overlap of atoms. | |
mapply(function(x, y, z, vdw, col){ | |
spheres3d(x, y, z, + vdw*0.75, col = col, add = T) | |
}, atom_dat$x, atom_dat$y, atom_dat$z, | |
atom_dat$vanderwaals, atom_dat$colour) | |
# Save as an interactive model on web page | |
writeWebGL() | |
# Open in default browser: | |
browseURL(paste("file://", file.path(getwd(), "webGL/index.html"), sep = "")) | |
# Tested in Chrome on Windows 7 machine with success | |
# Tested in IE on Windows 7 machine with limited success | |
# Tested in Chrome on Android device without success |
Although writeWebGL produces an html file, which is ready to go and can be uploaded to your website directly, it may need some editing when embedding it in a custom template. This requires some knowledge of the html and JavaScript languages. I won't be explaining the modifications I made here.
As a bonus, if your more a fan of the ball and stick model, below the code to add sticks to your molecule model.
################################# | |
# ball and stick model | |
################################# | |
# get bond info | |
bonds <- get.bonds(molfile) | |
bonds.atoms <- data.frame(do.call('rbind', lapply(bonds, function(x){ | |
atoms <- get.atoms(x) | |
start <- get.point3d(atoms[[1]]) | |
end <- get.point3d(atoms[[2]]) | |
return (list(start.x = start[1], start.y = start[2], start.z = start[3], | |
end.x = end[1], end.y = end[2], end.z = end[3])) | |
}))) | |
# open rgl device | |
open3d() | |
# plot sticks | |
mapply(function(x1, y1, z1, x2, y2, z2){ | |
shade3d(cylinder3d(data.frame(x = c(x1, x2), y = c(y1, y2), z = c(z1, z2)), | |
radius = 0.1, sides = 16, | |
e2=rbind(c(1,0,0),c(1,0,0))), col="grey") | |
}, bonds.atoms$start.x, bonds.atoms$start.y, bonds.atoms$start.z, | |
bonds.atoms$end.x, bonds.atoms$end.y, bonds.atoms$end.z) | |
# plot balls | |
mapply(function(x, y, z, vdw, col){ | |
spheres3d(x, y, z, vdw*0.25, col = col, add = T) | |
}, atom_dat$x, atom_dat$y, atom_dat$z, | |
atom_dat$vanderwaals, atom_dat$colour) |