# Re: [R] distance to eye in persp()

From: Duncan Murdoch <murdoch_at_stats.uwo.ca>
Date: Mon 19 Sep 2005 - 22:06:34 EST

(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

R-help@stat.math.ethz.ch mailing list