Home > Mobile >  Interactively identify 3D object in rgl plot
Interactively identify 3D object in rgl plot

Time:02-22

I want to identify 3d cylinders in an rgl plot to obtain one attribute of the nearest / selected cylinder. I tried using labels to simply spell out the attribute, but I work on data with more than 10.000 cylinders. Therefore, it gets so crowded that the labels are unreadable and it takes ages to render.

I tried to understand the documentation of rgl and I guess the solution to my issue is selecting the cylinder in the plot manually. I believe the function selectpoints3d() is probably the way to go. I believe it returns all vertices within the drawn rectangle, but I don't know how to go back to the cylinder data? I could calculate which cylinder is closest to the mean of the selected vertices, but this seems like a "quick & dirty" way to do the job.

Is there a better way to go? I noticed the argument value=FALSE to get the indices only, but I don't know how to go back to the cylinders.

Here is some dummy data and my code:

# dummy data
cylinder <- data.frame(
  start_X = rep(1:3, 2)*2,
  start_Y = rep(1:2, each = 3)*2,
  start_Z = 0,
  end_X = rep(1:3, 2)*2   round(runif(6, -1, 1), 2),
  end_Y = rep(1:2, each = 3)*2   round(runif(6, -1, 1), 2),
  end_Z = 0.5,
  radius = 0.25,
  attribute = sample(letters[1:6], 6)
)

# calculate centers
cylinder$center_X <- rowMeans(cylinder[,c("start_X", "end_X")])
cylinder$center_Y <- rowMeans(cylinder[,c("start_Y", "end_Y")])
cylinder$center_Z <- rowMeans(cylinder[,c("start_Z", "end_Z")])

# create cylinders
cylinder_list <- list()
for (i in 1:nrow(cylinder)) {
  cylinder_list[[i]] <- cylinder3d(
    center = cbind(
      c(cylinder$start_X[i], cylinder$end_X[i]),
      c(cylinder$start_Y[i], cylinder$end_Y[i]),
      c(cylinder$start_Z[i], cylinder$end_Z[i])),
    radius = cylinder$radius[i],
    closed = -2)
}

# plot cylinders
open3d()
par3d()
shade3d(shapelist3d(cylinder_list, plot = FALSE), col = "blue")
text3d(cylinder$center_X 0.5, cylinder$center_Y 0.5, cylinder$center_Z 0.5, cylinder$attribute, color="red")

# get attribute
nearby <- selectpoints3d(value=TRUE, button = "right")
nearby <- colMeans(nearby)
cylinder$dist <- sqrt(
  (nearby["x"]-cylinder$center_X)**2  
  (nearby["y"]-cylinder$center_Y)**2  
  (nearby["z"]-cylinder$center_Z)**2)
cylinder$attribute[which.min(cylinder$dist)]

CodePudding user response:

If you call selectpoints3d(value = FALSE), you get two columns. The first column is the id of the object that was found. Your cylinders get two ids each. One way to mark the cylinders is to use "tags". For example, this modification of your code:

# dummy data
cylinder <- data.frame(
  start_X = rep(1:3, 2)*2,
  start_Y = rep(1:2, each = 3)*2,
  start_Z = 0,
  end_X = rep(1:3, 2)*2   round(runif(6, -1, 1), 2),
  end_Y = rep(1:2, each = 3)*2   round(runif(6, -1, 1), 2),
  end_Z = 0.5,
  radius = 0.25,
  attribute = sample(letters[1:6], 6)
)

# calculate centers
cylinder$center_X <- rowMeans(cylinder[,c("start_X", "end_X")])
cylinder$center_Y <- rowMeans(cylinder[,c("start_Y", "end_Y")])
cylinder$center_Z <- rowMeans(cylinder[,c("start_Z", "end_Z")])

# create cylinders
cylinder_list <- list()
for (i in 1:nrow(cylinder)) {
  cylinder_list[[i]] <- cylinder3d(
    center = cbind(
      c(cylinder$start_X[i], cylinder$end_X[i]),
      c(cylinder$start_Y[i], cylinder$end_Y[i]),
      c(cylinder$start_Z[i], cylinder$end_Z[i])),
    radius = cylinder$radius[i],
    closed = -2)

  # Add tag here:

  cylinder_list[[i]]$material$tag <- cylinder$attribute[i]
}

# plot cylinders
open3d()
par3d()
shade3d(shapelist3d(cylinder_list, plot = FALSE), col = "blue")
text3d(cylinder$center_X 0.5, cylinder$center_Y 0.5, cylinder$center_Z 0.5, cylinder$attribute, color="red")

# Don't get values, get the ids

nearby <- selectpoints3d(value=FALSE, button = "right", closest = FALSE)
ids <- nearby[, "id"]

# Convert them to tags.  If you select one of the labels, you'll get
# a blank in the list of tags, because we didn't tag the text.

unique(tagged3d(id = ids))

When I was trying this, I found that using closest = TRUE in selectpoints3d seemed to get too many ids; there may be a bug there.

  • Related