[R] distance to eye in persp()

Duncan Murdoch murdoch at stats.uwo.ca
Mon Sep 19 14:06:34 CEST 2005

(Ted Harding) wrote:
> On 19-Sep-05 Robin Hankin wrote:
>>the manpage for persp() has a wonderful section where a the
>>trans3d()  function is used with points() and lines() to add
>>red dots and a green sinusoid to the Mexican hat surface.
>>Does anyone have a way to tell what distance  a point is from
>>the eye/camera?
>>Take the following line:
>>lines (trans3d(x, y=10, z= 6 + sin(x), pm = res), col = 3)
>>Is there a function like trans3d() that returns a vector of
>>distances from the x,y,z point to the camera?  I want this so
>>I can plot clouds of points with the further ones in smaller
>>plotsizes, and perhaps even fading to white (as though viewed
>>through fog).
> Wonderfully put! That's what statistics is about!
> I think you may have to write your own. This is possible given
> the values for the parameters xlim, ylim, zlim, r, theta, phi
> (default as defined in ?persp, or explicitly user-defined),
> since you can then determine the 3D coordinates of the "Eye"
> relative to the (X,Y,Z) axes being plotted, after which the
> distance to a particular (x,y,z) point is trivial.
> E.g.
> 1. Coordinates of Eye relative to the centre of the box
>    xE <- r*sin(theta + pi)*cos(phi)
>    yE <- r*cos(theta + pi)*cos(phi)
>    zE <- r*sin(phi)
> 2. Centre of box relative to real (0,0,0)
>    xC <- mean(xlim); yC <-mean(ylim); xC <- mean(zlim)
> 3. Coordinates of (x,y,z) relative to Eye
>    x1 <- x - xE - xC; y1 <- y - yE - yC; z1 <- z - zE - zC
> 4. Distance from Eye to (x,y,z)
>    d = sqrt(x1^2 + y1^2 + z1^2)
> (Hoping I've not got anything the wrong way round there!)

I think you forgot the rescaling to [0,1], but it's probably even easier 
than that.  persp returns the 4x4 transformation matrix used to take 
user coordinates into screen coordinates.  trans3d uses that matrix to 
extract the screen x and screen y coordinates (extend the 3-vector to 
homogeneous coordinates by appending a 1, multiply by the projection 
matrix, convert back to Euclidean coordinates by dividing by the last 
coordinate).  You can probably just do what trans3d does, but keep the 
z-coordinate, to get a reasonable measure of depth.  i.e.

depth3d <- function(x,y,z, pmat) {
   tr <- cbind(x, y, z, 1) %*% pmat

This is sufficient for doing fog calculations.  For perspective 
shrinkage, you'll need to say where the user's eye is in these 
coordinates (or just use depths as distances).

Duncan Murdoch

More information about the R-help mailing list