I made a 3D movie with ggplot2 once - here's how I did it
August 18, 2017
Some time ago (last year actually 😳) I had a blast
developing a feature for ggforce
which had been on my mind for far to long than
its limited utility warranted. The idea was to showcase the new facetting
extension powers I’d added to ggplot2
by making a facetting function that
created a stereoscopic pair of plots that would simulate 3D. To procrastinate
and show off I made a little animated video with the feature and posted it on
Twitter, promising I’d write about it someday. Now (again, one year later), I
think the world is finally ready to see what went through my R console to make
that little animation. While I’ve been very timely with this blog post, the
feature is still not available on CRAN, so you’ll need to install the GitHub
version of ggforce
to follow along.
Setup
The goal is to create a spinning hollow cube so we’ll need to define the cube
somehow. This was way before ggraph
was published, and tidygraph
was not
even a thought in my head, so while it may make sense to handle the cube as a
network, I did it the hard way:
Now that we have our data, we need to create some transformation functions.
Currently, if plotted in ggplot2
it would just look like a square as,
unsurprisingly, the third dimension would get lost. What we need is a
projection of three dimension down to two. This is a bit hairy, and when I
tried to achieve this back in the days by setting up a transformation matrix
manually I failed miserably (if you are knowledgable in this and want to explain
it to me - please reach out on twitter). In the end I took the shortcut and used
the persp()
function, which, besides the side effect of plotting stuff in 3D,
also returns the transformation matrix invisibly:
Having a look at the transformation matrix I can safely say that I have no idea what’s going on:
But that doesn’t matter - the only thing required is that I know how to use it.
The trans3d()
provides the means for taking in three dimensional points, and a
transformation matrix, and outputting two dimensional points:
With a bit of imagination we can see the 3D cube. Lets draw it with line segments as well - we add the added information of depth, which is simply the value in the dimension that gets dropped in the transformation.
We can of course improve the illusion even more. Using geom_link2()
from
ggforce
, we can add a gradient size to the lines, based on the depth of the
endpoints (remember, these were added in the to_grid()
function).
What can we do more? Well, we could decide to rotate it a bit. Let’s, make a function that rotates it around the vertical axis:
We now have all the ingredients to make an animation of a spinning cube - we really only need to animate a 90 degree spin as it is selfrepeating. For added fiz we’ll improve the depth perception by simulating a bit of haze by greying parts more distant:
That’s all fine and well, but this is just a regular and boring 2D video. Let us
update it to the brave new world, where Avatar has taught us that 3D is not
tacky: Enter facet_stereo()
.
That is all it takes…