6

It would be convenient to interactively select a decent viewpoint using rgl and then adopt the same orientation in a lattice 3d-plot. For example, given the following plot using a non-informative viewpoint.

library(lattice)
wireframe(volcano, screen = list(x=0, y=0, z=0))

enter image description here

The same can be opened in rgl by

library(rgl)
persp3d(volcano)
view3d(0, 0)

enter image description here

Interactively it is easy to rotate the plot to an informative view.

enter image description here

The matrix giving the current rgl viewpoint in can be extracted by

p <- par3d()
p$userMatrix

How can this matrix be converted into corresponding x,y,z screen parameters to replicate the view in lattice?

UPDATE 1

I tried out 42's conversion below. The code shows the rgl plot and the corresponding lattice plot per row. If I implemented it correctly (see code below), there appears to still be an issue.

enter image description here

# convert rgl viewpoint into lattice 
# screen orientation
rgl_to_lattice_viewpoint <- function()
{
  p <- par3d()
  rotm <- p$userMatrix
  B = 360*atan(rotm[1,2]/rotm[2,2])/(2*pi)
  P = 360*asin(-rotm[3,2])/(2*pi)
  H = 360*atan(rotm[3,1]/rotm[3,3])/(2*pi)
  list(x=-B, y=-P, z=-H)
}


# read and plot PNG image 
plot_png <- function(f)
{
  img <- readPNG(f)
  rimg <- as.raster(img)      # raster multilayer object
  plot(NULL, xlim=c(0,1), ylim=c(0,1), xlab = "", ylab = "", 
       asp=1, frame=F, xaxt="n", yaxt="n")
  rasterImage(rimg, 0, 0, 1, 1) 
}


# create rgl snapshot with random rotation and 
# corresponding lattice wireframe plot
lattice_plus_rgl_plot <- function()
{
  # rgl plot random rotation
  persp3d(volcano, col = "green3")
  theta <- sample(-180:180, 1)
  phi <- sample(-90:90, 1)
  view3d(theta, phi, fov=40)
  v <- rgl_to_lattice_viewpoint()
  f <- tempfile(fileext = ".png")
  rgl.snapshot(f)
  rgl.close()

  # lattice plot
  f2 <- tempfile(fileext = ".png")
  png(f2)
    print(wireframe(volcano, screen = v))
  dev.off()

  # plot both
  plot_png(f)  
  plot_png(f2)  
}

# CREATE SOME PLOTS 

library(rgl)
library(lattice)
library(png)
par(mfrow=c(3,2), mar=c(0,0,0,0))
replicate(3, lattice_plus_rgl_plot())
Mark Heckmann
  • 10,943
  • 4
  • 56
  • 88

2 Answers2

1

I used the answer to this question for conversion from a rotation matrix to angles: Conversion euler to matrix and matrix to euler . I admit to concern that I see another somewhat different answer here: How to calculate the angle from Roational matrix . (My linear algebra is not good enough to determine which of these is correct.)

p <- par3d()
rotm <- p$userMatrix
B = 360*atan(rotm[1,2]/rotm[2,2])/(2*pi)
P = 360*asin(-rotm[3,2])/(2*pi)
H = 360*atan(rotm[3,1]/rotm[3,3])/(2*pi)

> print(list(B,P,H))
[[1]]
[1] 41.54071

[[2]]
[1] 40.28412

[[3]]
[1] 41.24902

At that point I had already rotated the RGL-object to roughly the "viewing point" that you had suggested. I discovered by experimentation that the negative values supplied to the wireframe call delivered apparently correct results. "Viewer rotation angles" are plausibly seen as the negative for "object rotation angles".

 png(); print(wireframe(volcano, screen = list(x=-B, y=-P, z=-H)) ); dev.off()

enter image description here

There is a rotate.wireframe function in the TeachingDemos package but it does not play well with concurrently running rgl plots. (No plot was apparent until I closed the rgl device.) It also seemed kind of buggy when running on a Mac (thick black line across the lattice plot). It uses the X11/XQuartz facilities to manage interaction via tk/tcl functions and I was unable to reproduce the plots from the angles being displayed. Looking at the code I'm not able to understand why that should be so. But your mileage may vary.

Community
  • 1
  • 1
IRTFM
  • 258,963
  • 21
  • 364
  • 487
  • Thanks! :) I edited my post (UPDATE 1) where I tried out the conversion you suggested. There appears to be an issue given I made no mistake. – Mark Heckmann May 04 '16 at 09:11
1

This version of your function uses conversions from the orientlib package, and makes the rotation matrix an argument:

rgl_to_lattice_viewpoint <- function(rotm = par3d("userMatrix"))
{
  e <- -orientlib::eulerzyx(orientlib::rotmatrix(rotm[1:3, 1:3]))@x*180/pi
  list(z = e[1], y = e[2], x = e[3])
}

Note that the z, y, x order is essential.

Using it in place of your function, I get this output: rgl and lattice plots

These get the rotation right. I don't know if it's also possible to get the perspective to match.

Edited to add: rgl version 0.95.1468, so far available only on R-forge, contains a version of this function and one for base graphics as well.

user2554330
  • 37,248
  • 4
  • 43
  • 90