raymolecule
is an R package to parse and render molecules in 3D.
Rendering is powered by two packages:
rayrender package, a pathtracer for R, and
rayvertex, a rasterizer for R.
raymolecule
currently supports and parses SDF (structure-data file)
and PDB (Protein Data Bank) files and returns a rayrender
scene, which
we then pathtrace and visualize in R. This initial release of the
package only supports visualizing atoms and bonds.
You can install the released version of raymolecule from Github:
install.packages("remotes")
remotes::install_github("tylermorganwall/raymolecule")
raymolecule
includes several example SDF files for the following
molecules: “benzene”, “buckyball”, “caffeine”, “capsaicin”,
“cinnemaldehyde”, “geraniol”, “luciferin”, “morphine”, “penicillin”,
“pfoa”, “skatole”, “tubocurarine_chloride”. You can get the file path to
these example files using the get_example_molecule()
function. We pass
this path to the read_sdf()
file to parse the file and extract the
atom coordinates and bond information in a list. raymolecule
also
includes the ability to fetch molecules from PubChem using the
get_molecule()
function. The magrittr pipe is automatically imported
in the package, so we will use it to pass the output of each function to
the input of the next.
Here’s the format of the data:
library(raymolecule)
get_example_molecule("benzene") |>
read_sdf()
#> $atoms
#> x y z type index
#> 1 -1.2131 -0.6884 0e+00 C 1
#> 2 -1.2028 0.7064 1e-04 C 2
#> 3 -0.0103 -1.3948 0e+00 C 3
#> 4 0.0104 1.3948 -1e-04 C 4
#> 5 1.2028 -0.7063 0e+00 C 5
#> 6 1.2131 0.6884 0e+00 C 6
#> 7 -2.1577 -1.2244 0e+00 H 7
#> 8 -2.1393 1.2564 1e-04 H 8
#> 9 -0.0184 -2.4809 -1e-04 H 9
#> 10 0.0184 2.4808 0e+00 H 10
#> 11 2.1394 -1.2563 1e-04 H 11
#> 12 2.1577 1.2245 0e+00 H 12
#>
#> $bonds
#> from to number
#> 1 1 2 2
#> 2 1 3 1
#> 3 1 7 1
#> 4 2 4 1
#> 5 2 8 1
#> 6 3 5 2
#> 7 3 9 1
#> 8 4 6 2
#> 9 4 10 1
#> 10 5 6 1
#> 11 5 11 1
#> 12 6 12 1
Alternatively, you can fetch any molecule from PubChem by passing either the molecule name. You can also fetch a molecule using the official compound ID (CID), in case you have a specific molecule with a long name or unique isoform:
str(get_molecule("estradiol"))
#> List of 2
#> $ atoms:'data.frame': 44 obs. of 5 variables:
#> ..$ x : num [1:44] 5.061 -5.857 2.664 1.984 0.515 ...
#> ..$ y : num [1:44] 1.15 0.148 0.584 -0.695 -0.78 ...
#> ..$ z : num [1:44] 0.2674 0.4388 0.1481 -0.3789 0.0353 ...
#> ..$ type : chr [1:44] "O" "O" "C" "C" ...
#> ..$ index: int [1:44] 1 2 3 4 5 6 7 8 9 10 ...
#> $ bonds:'data.frame': 47 obs. of 3 variables:
#> ..$ from : num [1:47] 1 1 2 2 3 3 3 3 4 4 ...
#> ..$ to : num [1:47] 7 40 20 44 4 7 8 13 5 9 ...
#> ..$ number: num [1:47] 1 1 1 1 1 1 1 1 1 1 ...
str(get_molecule(5757)) #this is the CID for estradiol (aka estrogen)
#> List of 2
#> $ atoms:'data.frame': 44 obs. of 5 variables:
#> ..$ x : num [1:44] 5.061 -5.857 2.664 1.984 0.515 ...
#> ..$ y : num [1:44] 1.15 0.148 0.584 -0.695 -0.78 ...
#> ..$ z : num [1:44] 0.2674 0.4388 0.1481 -0.3789 0.0353 ...
#> ..$ type : chr [1:44] "O" "O" "C" "C" ...
#> ..$ index: int [1:44] 1 2 3 4 5 6 7 8 9 10 ...
#> $ bonds:'data.frame': 47 obs. of 3 variables:
#> ..$ from : num [1:47] 1 1 2 2 3 3 3 3 4 4 ...
#> ..$ to : num [1:47] 7 40 20 44 4 7 8 13 5 9 ...
#> ..$ number: num [1:47] 1 1 1 1 1 1 1 1 1 1 ...
We can then pass the list from get_example_molecule() |> read_sdf()
or
from get_molecule()
to the generate_full_scene()
,
generate_atom_scene()
, or generate_bond_scene()
functions to convert
this representation to a rayrender/rayvertex scene. This can then be
passed on the render_model()
function, which will call rayrender’s
render_scene()
or rayvertex’s rasterize_scene()
functions to render
it. This function automatically ensures the molecule is centered and in
frame, and sets up lighting, and can accept arguments to rotate the
molecule. For more rendering options, see rayrender::render_scene()
and rayvertex::rasterize_scene()
.
#Specify a width, height, and number of samples for the image (more samples == less noise)
get_example_molecule("caffeine") |>
read_sdf() |>
generate_full_scene() |>
render_model(width=800,height=800,samples=1000, clamp_value=10)
#> Warning in render_scene(scene = scene, fov = fov, lookfrom = c(0, 0, widest * :
#> "sobol_blue" sample method only valid for `samples` than or equal to
#> 256--switching to `sample_method = "sobol"`
#Light from both bottom and top
get_example_molecule("cinnemaldehyde") |>
read_sdf() |>
generate_full_scene() |>
render_model(lights="both",width=800,height=800,samples=1000,clamp_value=10)
#> Warning in render_scene(scene = scene, fov = fov, lookfrom = c(0, 0, widest * :
#> "sobol_blue" sample method only valid for `samples` than or equal to
#> 256--switching to `sample_method = "sobol"`
#Rotate the molecule and add a non-zero aperture setting to get depth of field effect
get_example_molecule("penicillin") |>
read_sdf() |>
generate_full_scene() |>
render_model(lights="both",width=800,height=800,samples=1000,angle=c(0,30,0),aperture=3,
clamp_value=10)
#> Warning in render_scene(scene = scene, fov = fov, lookfrom = c(0, 0, widest * :
#> "sobol_blue" sample method only valid for `samples` than or equal to
#> 256--switching to `sample_method = "sobol"`
We can use rayvertex
to render images much more quickly and noise
free, as well as include a toon cel-shading effect.
library(rayvertex)
#Render a basic example with rayvertex
get_example_molecule("tubocurarine_chloride") |>
read_sdf() |>
generate_full_scene(pathtrace=FALSE) |>
render_model(width=800,height=800,background="grey66")
#Customize the material with toon shading
shiny_toon_material = material_list(type="toon_phong",
toon_levels=,
toon_outline_width=0.1)
get_example_molecule("morphine") |>
read_sdf() |>
generate_full_scene(pathtrace=FALSE, material_vertex = shiny_toon_material) |>
render_model(width=800,height=800,background="grey66")
#Customize the lights with rayvertex
get_example_molecule("skatole") |>
read_sdf() |>
generate_full_scene(pathtrace=FALSE) |>
render_model(width=800,height=800,angle=c(0,30,0), background="grey66",
lights = directional_light(c(0,1,1)) |>
add_light(directional_light(c(0,-1,0),color="red")))
You can turn off lighting in render_model()
and customize the scene
(adding different objects or lights) by using rayrender’s add_object()
function or rayvertex’s add_shape()
function. If you use
rayrender::render_scene()
/rayvertex::rasterize_scene()
instead of
render_model()
, you have to set up the camera position and field of
view yourself.
library(rayrender)
buckyball = get_example_molecule("buckyball") |>
read_sdf() |>
generate_full_scene()
#Add custom lighting
buckyball |>
add_object(sphere(y=12,radius=3,material=light(color="white", intensity=50))) |>
add_object(sphere(y=-12,radius=3,material=light(color="red", intensity=50))) |>
render_model(lights="none",width=800,height=800,samples=1000, clamp_value=10)
#> Warning in render_scene(scene = scene, fov = fov, lookfrom = c(0, 0, widest * :
#> "sobol_blue" sample method only valid for `samples` than or equal to
#> 256--switching to `sample_method = "sobol"`
#Generate ground underneath the model and use a light to cast a shadow
generate_ground(depth=-4,material=diffuse(sigma=90)) |>
add_object(buckyball) |>
add_object(sphere(y=8,material=light(intensity=100))) |>
render_scene(width=800,height=800,samples=1000,aperture=1,fov=30,lookfrom = c(0,1,30),
clamp_value = 10)
#> Warning in render_scene(add_object(add_object(generate_ground(depth = -4, :
#> "sobol_blue" sample method only valid for `samples` than or equal to
#> 256--switching to `sample_method = "sobol"`