SkillAgentSearch skills...

Rdog

Porting zdog pseudo-3d engine to R for reasons...

Install / Use

/learn @oganm/Rdog
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Rdog <img src="man/figures/logo.gif" align="right" height="150"/>

This is a port of the zdog pseudo 3D engine for R.

Table of Contents

Installation

devtools::install_github('oganm/rdog')

Install the latest Rstudio to make sure it works well with the built in viewer. Some elements appears to misbehave in older versions.

Why?

As the zdog’s author states

Zdog is a 3D JavaScript engine for canvas and SVG. With Zdog, you can design and render simple 3D models on the Web. Zdog is a pseudo-3D engine. Its geometries exist in 3D space, but are rendered as flat shapes. This makes Zdog special.

Can rdog make renderings that look better than rayshader or rayrender? No. We have no perspective, no distance blurring, no lighting simulation, no shadows. You have objects in a 3D space and they tend to look cute with minimal effort. It has a nice natural aesthetic that I happen to like.

One advantage is that output of rdog is an htmlwidget and the rendering is done through javascript objects by the browser. Images are rendered into a canvas or an svg context. This is particularly nice for shiny apps since rendering is outsourced to the users’ browser and the rendered objects can be manipulated with relative ease through javascript or wrapper functions provided in the package.

Basic usage

Taking a look at the zdog api will give you a good idea how things work. Variable names and defaults are preserved. The only major difference is the use of piping to create a single illustration object with all the elements.

An animated illustration can be created by adding elements to an illustration using %>%. Each shape can be added to the illustartion itself (default), or to another existing shape which will be used as a reference point. Here a box is created placed on y = -20. The next element is an ellipse which is added to the box which places it in the middle of the box. All shape functions start with shape

To add text, a font should be added first. zfont_font(id = 'font') creates one with the default font, “Roboto-Regular”. Different fonts can be used if you have the ttf files.

Finally, animations are also added to elements or the entire illustration. Here animation_rotate is added to the ellipse and animation_ease_in is added to the box which makes them rotate at different rates. animation_none will not move the objects but creates the loop required for dragRotate to work.

illustration('illo',width = 250,height = 250, dragRotate = TRUE) %>% 
    shape_box(id ='cornell',
              width = 150,
              height = 150,
              depth = 150,
              translate = c(y = '-20'),
              rotate = c(x = -tau/20,y = tau/16),
              stroke = 1,
              color = '#C25',
              leftFace = 'red',
              rightFace =  'darkgreen',
              topFace =  'white',
              bottomFace =  'white',
              frontFace =  FALSE,
              rearFace= 'lightgray') %>% 
    shape_ellipse(
        addTo = 'cornell',
        id = "ellipse",
        diameter = 80,
        stroke = 20,
        color = '#636',fill = FALSE
    ) %>% 
    zfont_font(id = 'font') %>% 
    zfont_text(zfont = 'font', text = 'Cornell Box',fontSize = 24,translate = c(y = 120),textAlign = 'center') %>% 
    animation_rotate(addTo = 'ellipse',id = 'rotate',rotate = c(y = 0.05)) %>% 
    animation_ease_in(id = 'ease',radiansPerCycle = tau/2,addTo='cornell',framesPerCycle = 200,power = 3) %>% 
    record_gif(duration = 10)

<!-- -->

record_gif is not required for interactive usage or html renderings. By default, the output is an htmlwidget, that can be automatically displayed in the viewer. This doesn’t work with github_document’s due to restrictions on github so rendering into a gif or an image (save_image) is necesary.

anchors are invisible elements that one can add shapes to and be used as a reference point.

illustration('illo') %>% 
  anchor(id ='anchor', translate = c(x = 100)) %>% 
  shape_shape(addTo = 'anchor', translate = c(y = - 100), stroke = 20) %>% 
  save_image()

<!-- -->

copy and copy_graph functions can be used to duplicate objects. copy_graph will also copy any children object that an object has. You can change properties of the object you copy by passing additional arguments.

illustration('illo') %>% 
  shape_shape(id ='point1', translate = c(x = 100),stroke = 20, color = 'blue') %>% 
  shape_shape(addTo = 'point1', translate = c(y = - 100), stroke = 20, color = 'red') %>%
  copy(id = 'copyOfPoint1', what = 'point1',color = 'darkgreen',translate = c(x = -100)) %>%
  save_image()

<!-- -->

illustration('illo') %>% 
  shape_shape(id ='point1', translate = c(x = 100),stroke = 20, color = 'blue') %>% 
  shape_shape(addTo = 'point1', translate = c(y = - 100), stroke = 20, color = 'red') %>%
  copy_graph(id = 'copyOfPoint1', what = 'point1',color = 'darkgreen',translate = c(x = -100)) %>%
  save_image()

<!-- -->

Rendering SVG paths

Paths from svg files can also be displayed. Chess bishop icon used as an example here was made by by Skoll under CC BY 3.0.

# get an svg file
svgFile = system.file('chess-bishop.svg',package = 'rdog')
# parse the svg file
svg = XML::xmlParse(svgFile) %>% XML::xmlToList()
# extract path
path = svg$g$path['d']

# animate and record gif
illustration('illo',width = 256,height = 256) %>%
    svg_path_to_shape(svgWidth = 512, svgHeight = 512,stroke = 1,svgPath = path,scale = .7,fill =FALSE,closed = FALSE)   %>% 
    save_image()

<!-- -->

stroke variable can be used to give some depth to svg paths.

illustration('illo',width = 256,height = 256, rotate = c(y =tau/10, x = -tau/10)) %>%
    svg_path_to_shape(svgWidth = 512, svgHeight = 512,stroke =5,svgPath = path,scale = .7,fill =FALSE,closed = FALSE)   %>% 
    save_image()

<!-- -->

Note that setting fill = TRUE will cause you to lose enclosed shapes in an svg path

illustration('illo',width = 256,height = 256, rotate = c(y =tau/10, x = -tau/10)) %>%
    svg_path_to_shape(svgWidth = 512, svgHeight = 512,stroke =5,svgPath = path,scale = .7,fill =TRUE,closed = FALSE)   %>% 
    save_image()

<!-- -->

But some fiddling can still generate a decent looking 3D structure

rd = illustration('illo',width = 256,height = 256 ,rotate = c(y =tau/15, x = -tau/15))
colfunc <- colorRampPalette(c("gray60","gray20"))

zAxis = seq(from = -25, to=25 ,by = 1)
for(i in seq_along(zAxis)){
    rd %<>%
        svg_path_to_shape(svgWidth = 512,svgHeight = 512,
                          translate = c(z = zAxis[i]),color = colfunc(length(zAxis))[i],
                          stroke = 2,svgPath = path,scale = .7,fill =FALSE,closed = FALSE)
}

zAxis2 = seq(from = -15, to=15 ,by = 1)
for(i in seq_along(zAxis2)){
    rd %<>%
        svg_path_to_shape(svgWidth = 512,svgHeight = 512,
                          translate = c(z = zAxis2[i]),color = colfunc(length(zAxis))[zAxis %in% zAxis2[i]],
                          stroke = 2,svgPath = path,scale = .7,fill =TRUE,closed = FALSE)
}

rd %>% save_image()

<!-- -->

Rendering 3D STL files

Stl files can be read and rendered using the stl_to_shape function. Note that adding objects with too many polygons will likely hurt performance drastically.

While not necessary, you can use binary_to_ascii_stl function to convert any binary stl to ascii stl files. This conversion happens internally if you don’t so it’s every so slightly more efficient to use it.

GEB Logo used in this example was made by guru, licensed under Creative Commons - Attribution - Share Alike license. The origin of the logo is “Gödel, Escher, Bach”.

stl = system.file('GEB.stl',package = 'rdog')

stl_bounds = get_stl_bounds(stl)

# we need to subtract 20 from the z axis to center this file
stl_bounds
## $x
## [1] -20  20
## 
## $y
## [1] -20  20
## 
## $z
## [1]  0 40
# center_stl function can be used to center the objects automatically
center_stl(stl_bounds)
##   x   y   z 
##   0   0 -20
# use objectOffset to center the variable
illustration(width = 150,height = 150,displayType = 'canvas',scale = 2,rotate = c(y = tau/2)) %>%
  stl_to_shape(stl = stl,colorAxis = 'z',colorMin = 'gray90',colorMax = 'black',
               objectOffset = center_stl(stl_bounds),
               stroke = 1) %>% 
  animation_rotate(rotate =c(y = .01,x = .02)) %>% record_gif(duration = 10)

<!-- -->

Let’s take a look at the platonic solids.

platonic = list(
  tetrahedron =  readLines(system.file('tetrahedron.stl',package = '
View on GitHub
GitHub Stars33
CategoryDevelopment
Updated9mo ago
Forks0

Languages

R

Security Score

67/100

Audited on Jun 14, 2025

No findings