What I want to achieve is, to show a 3D graph in rgl, rotate it into the view I would like to show and then save it to a file. I know that I can do this with the rgl.snapshot
function like this:
library(rgl)
x <- runif(20)
y <- runif(20)
z <- runif(20)
plot3d(x, y, z)
rgl.snapshot("rgl.snapshot.png")
The problem is, that rgl.snapshot
produces a file in the screen resolution, hence not high resolution enough for print. I have no way to influence the resolution the file get`s saved in. In general even better would be if I would be able to save the file in a vector format like pdf or svg.
My Idea was to save the rotation of the current view and use it with another function that produces a non-interactive 3d scatterplot like scatter3D
from the plot3D
package. To save the rotation matrix I did the following:
rotationMatrix <- rgl.projection()
You can also do it like this:
rotationMatrix <- par3d()$modelMatrix
The rotation matrix looks like this:
$model
[,1] [,2] [,3] [,4]
[1,] 0.9584099 0.0000000 0.0000000 -0.4726846
[2,] 0.0000000 0.3436644 0.9792327 -0.6819317
[3,] 0.0000000 -0.9442102 0.3564116 -3.6946754
[4,] 0.0000000 0.0000000 0.0000000 1.0000000
$proj
[,1] [,2] [,3] [,4]
[1,] 3.732051 0.000000 0.000000 0.00000
[2,] 0.000000 3.732051 0.000000 0.00000
[3,] 0.000000 0.000000 -3.863703 -14.36357
[4,] 0.000000 0.000000 -1.000000 0.00000
$view
x y width height
0 0 256 256
Now my question is how I get from this rotation matrix to the arguments phi
and theta
which are used by the scatter3D
function.
library(plot3D)
# phi = ?
# theta = ?
pdf("scatter3D.pdf")
scatter3D(x, y, z, pch=20, phi = 20, theta =30, col="black")
dev.off()
I know there is math to extract a rotation angle from a rotation matrix. I don't really get how to apply it in my case. Especially since the matrix has 4 rows and columns. I would expect 3 of each... Next problem is that scatter3D
uses only two rotation axes (theta gives the azimuthal direction and phi the colatitude), so I would have to convert from a 3-axis rotation to the same rotation resulting from a two-axis rotation. I think the rotation axis of phi is defined by the rotation of theta.
If there is another way to save a rgl snapshot in a lossless format I would be happy to learn about it!
The latest (R-forge only; see How do I install the latest version of rgl? for how to get it) version of rgl has a function rglToBase() that returns the phi and theta values you'd need. There's also rgl.postscript() as mentioned in my Apr 24 comment that saves in a lossless format (but can't save everything).
Edited to add: A very new addition is the writeASY()
function. This writes out Asymptote source code to draw the image in various formats, mainly intended for LaTeX documents. See http://asymptote.sourceforge.net. This is still a little limited (subscenes aren't supported, surface lighting isn't perfect, etc.) but it's getting there. Suggestions would be welcome.