# [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:
>
>>Hi
>>
>>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
return(tr[,3]/tr[,4])
}

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

```