5
votes

I am plotting some point data using plot3d(). I would like to bring my y axis tick labels a little closer to my y axis tick marks.

The best way I can think of doing this is to

1) plot the data first, without drawing the axes

2) call on axis3d() to draw the y axis and tick marks but suppress labels from being drawn.

3) query the current position of each tick mark in 3D space. Store positions in a vector.

4) use mtext3d() to add labels at positions based on an adjustment to the vector

I am having a problem at step 3. I don't know how to query the position of each tick mark. par3d() allows you to query a number of graphical parameters, is there something similar I can use to get the position each y axis tick?

Am I approaching this wrong? Probably.

Here is an example piece of code, without text added for y axis labels....

require(rgl)
x <- rnorm(5)
y <- rnorm(5)
z <- rnorm(5)
open3d()
plot3d(x,y,z,axes=F,xlab="",ylab="",zlab="")
par3d(ignoreExtent=TRUE)
par3d(FOV=0)
par3d(userMatrix=rotationMatrix(0,1,0,0))
axis3d('y',nticks=5,labels = FALSE)
par3d(zoom=1)
par3d(windowRect=c(580,60,1380,900))
2

2 Answers

2
votes

One way to do this is to explicitely define the tick locations before drawing the axis, instead of querying them after drawing the axis. Then you can use the line=option of mtext3d to control the distance of the tick labels from the axis like this:

require(rgl)
rgl.close()
x <- rnorm(5)
y <- rnorm(5)
z <- rnorm(5)
open3d()
plot3d(x,y,z,axes=F,xlab="",ylab="",zlab="")
par3d(ignoreExtent=TRUE)
par3d(FOV=0)
par3d(userMatrix=rotationMatrix(0,1,0,0))
par3d(zoom=1)
par3d(windowRect=c(580,60,1380,900))

# and here is the trick:
my.ticks <- pretty(y, n=5)
axis3d('y', at=my.ticks, labels=rep("", 5))
mtext3d(paste(my.ticks), at=my.ticks, edge='y', line=.6)
1
votes

I found that the easiest way to control both the label position and tick length in axis3d is to rewrite the function with a couple of extra parameters ticksize and lab_dist added which can be used to overwrite the default values embedded in the function. The default values of ticksize = 0.05, and lab_dist = 3 reproduce the behaviour of the original axis3d.

To get smaller ticks and closer labels, you can then call it with e.g.

axis3('y', nticks=5, labels = FALSE, ticksize = 0.03, lab_dist = 2)

The new function looks like this:

axis3 <- function (edge, at = NULL, labels = TRUE, tick = TRUE, line = 0, 
          pos = NULL, nticks = 5, ticksize = 0.05, lab_dist = 3, ...) 
{
  save <- par3d(skipRedraw = TRUE, ignoreExtent = TRUE)
  on.exit(par3d(save))
  ranges <- rgl:::.getRanges()
  edge <- c(strsplit(edge, "")[[1]], "-", "-")[1:3]
  coord <- match(toupper(edge[1]), c("X", "Y", "Z"))
  if (coord == 2) 
    edge[1] <- edge[2]
  else if (coord == 3) 
    edge[1:2] <- edge[2:3]
  range <- ranges[[coord]]
  if (is.null(at)) {
    at <- pretty(range, nticks)
    at <- at[at >= range[1] & at <= range[2]]
  }
  if (is.logical(labels)) {
    if (labels) 
      labels <- format(at)
    else labels <- NA
  }
  mpos <- matrix(NA, 3, length(at))
  if (edge[1] == "+") 
    mpos[1, ] <- ranges$x[2]
  else mpos[1, ] <- ranges$x[1]
  if (edge[2] == "+") 
    mpos[2, ] <- ranges$y[2]
  else mpos[2, ] <- ranges$y[1]
  if (edge[3] == "+") 
    mpos[3, ] <- ranges$z[2]
  else mpos[3, ] <- ranges$z[1]
  ticksize <- ticksize * (mpos[, 1] - c(mean(ranges$x), mean(ranges$y), 
                                    mean(ranges$z)))
  ticksize[coord] <- 0
  if (!is.null(pos)) 
    mpos <- matrix(pos, 3, length(at))
  mpos[coord, ] <- at
  x <- c(mpos[1, 1], mpos[1, length(at)])
  y <- c(mpos[2, 1], mpos[2, length(at)])
  z <- c(mpos[3, 1], mpos[3, length(at)])
  if (tick) {
    x <- c(x, as.double(rbind(mpos[1, ], mpos[1, ] + ticksize[1])))
    y <- c(y, as.double(rbind(mpos[2, ], mpos[2, ] + ticksize[2])))
    z <- c(z, as.double(rbind(mpos[3, ], mpos[3, ] + ticksize[3])))
  }
  result <- c(ticks = segments3d(x, y, z, ...))
  if (!all(is.na(labels))) 
    result <- c(result, labels = text3d(mpos[1, ] + lab_dist * ticksize[1], 
                                        mpos[2, ] + lab_dist * ticksize[2], 
                                        mpos[3, ] + lab_dist * ticksize[3], 
                                        labels, ...))
  lowlevel(result)
}