R/display_3D_stack.R

Defines functions display.3D.stack

Documented in display.3D.stack

#' Display in 3D the selected planes of an \pkg{espadon} class volume
#' @description The \code{display.3D.stack} function displays in 3D the requested 
#' cutting planes of a "volume" class object.
#' @param vol "volume" class object to display.
#' @param k.idx vector of cutting plane numbers to be displayed, to be chosen in 
#' \code{vol$k.idx}. By default \code{k.idx} is a vector of 10 uniformly 
#' distributed cutting planes in the volume.
#' @param display.ref Character string. Pseudonym of the frame of reference used 
#' for display.
#' @param T.MAT "t.mat" class object, created by \link[espadon]{load.patient.from.Rdcm} 
#' or \link[espadon]{load.T.MAT}. If \code{T.MAT} is \code{NULL}, \code{vol} must 
#' be displayed in \code{display.ref = vol$ref.pseudo}.
#' @param col Vector, representing the color palette of the image. Transparent 
#' colors are not represented. 
#' @param breaks  One of :
#' \itemize{
#' \item \code{NULL} : The minimum and the maximum value of the \code{vol} define 
#' the range.
#' \item Vector giving the breakpoints of each color.
#' }
#' @param cube Boolean. If \code{TRUE} (default), the "volume" edges are displayed.
#' @param border Boolean. If \code{TRUE} (default), the borders of the planes defined 
#' in \code{k.idx} are displayed.
#' @param ktext Boolean. If \code{TRUE} (default), the  selected cutting plane numbers 
#' are displayed.
#' @param line.col Color of cube, planes and texts displayed.
#' @param line.lwd Line width of the border and cube, by default at 1. 
#' @param cex Numeric character expansion factor of displayed plan numbers.

#' @return  Returns a display of the \code{k.idx} cutting planes of \code{vol}, 
#' in the current \pkg{RGL} window if it exists, in a new window otherwise. The 
#' colors of the palettes are managed by \code{col} and \code{breaks}.  

#' @examples
#' # loading of toy-patient objects (decrease dxyz for better result)
#' step <- 4
#' patient <- toy.load.patient (modality = "ct", dxyz = rep (step, 3))
#' 
#' # display o 3 planes
#' rgl::open3d()
#' display.3D.stack (patient$ct[[1]],
#'                   col = pal.RVV (200, alpha = c(rep(0,90), rep (1, 110))))

#' @export
#' @importFrom rgl mesh3d wire3d segments3d texts3d material3d
#' @importFrom methods is
display.3D.stack <- function(vol,  
                             k.idx = unique(vol$k.idx[seq(1,vol$n.ijk[3], length.out = 10)]), 
                             display.ref = vol$ref.pseudo, 
                             T.MAT = NULL,  
                             col = grey.colors(10, start = 0, end = 1, alpha = c(rep(0,1),rep(1,9))),
                             breaks = NULL,
                             cube = TRUE, 
                             border = TRUE,
                             ktext = TRUE,
                             line.col = "#379DA2",
                             line.lwd = 1,
                             cex = 1){
  
  if (!is(vol, "volume")) 
    stop("vol should be a volume class object.")

  if (is.null(vol$vol3D.data)) 
    stop("empty vol$vol3D.data.")

  M <- get.rigid.M(T.MAT, vol$ref.pseudo, display.ref)
  if (is.null(M)) stop("cannot display anything in selected frame of reference.")

  imin <- as.numeric(vol$min.pixel)
  imax <- as.numeric(vol$max.pixel) 

  if (!is.null(breaks) & !any(is.na(breaks))) {
    if (length(breaks) != length(col) + 1) stop("length(breaks) must be equal to length(col) + 1.")
  } else {
    breaks <- .pixel.scale(imin, imax, length(col))
  }
  if (breaks[1] > imin) breaks[1] <- imin - 1
  if (breaks[length(breaks)] < imax) breaks[length(breaks)] <- imax + 1
  
  color  <- substr(col,1,7)
  alpha <- substr(col,8,9)
  if (nchar(alpha[1]) == 0) alpha = rep("ff",length(col))
  cube_disp <- vol$cube.idx
  cube_disp[c(1:3,6:7,11,13,15,17:18,22,29)] <- cube_disp[c(1:3,6:7,11,13,15,17:18,22,29)] - 0.5
  cube_disp[c(5,9:10,14,19,21,23,25:27,30:31)] <- cube_disp[c(5,9:10,14,19,21,23,25:27,30:31)] + 0.5
  
  if (cube & !any(apply(vol$cube.idx == 0 ,1, all))) {

    line <- unique(do.call(rbind.data.frame, lapply(1:ncol(vol$cube.idx), function(i) 
      data.frame(a = rep(i,3), b = which(apply(sweep(vol$cube.idx, 1, vol$cube.idx[,i]) == 0,2,sum) == 3)))))

    pt <- (M %*% vol$xyz.from.ijk %*% cube_disp)[1:3,]
    for (i in 1:nrow(line)) rgl::segments3d(t(pt[ ,c(line[i,1], line[i,2])]), color = line.col, lwd = line.lwd)
  }

  for (k in k.idx) {
    p <- cube_disp[,1:4]
    p[3,] <- k
    pt <- (M %*% vol$xyz.from.ijk %*% p)[1:3,]
    if (border) {    
      segments3d(t(pt[,c(1,2)]), color = line.col, lwd = line.lwd)
      segments3d(t(pt[,c(2,3)]), color = line.col, lwd = line.lwd)
      segments3d(t(pt[,c(3,4)]), color = line.col, lwd = line.lwd)
      segments3d(t(pt[,c(4,1)]), color = line.col, lwd = line.lwd)
      
      if (ktext) texts3d(t(pt[,c(1)]), texts = paste0("k=",k," "), 
                         color = line.col,cex = cex, adj = 1)  
    }
      
    idx <- which(k == vol$k.idx)
    map <-  array(vol$vol3D.data[ , ,idx],dim = vol$n.ijk[1:2])
    
    if (!all(is.na(map))) {
      cut.map <- cut(as.numeric(map), breaks, include.lowest = TRUE)
      alpha.layer <-  matrix(alpha[cut.map], nrow = nrow(map))
      map.layer <- matrix(color[cut.map], nrow = nrow(map))
      ind <- which(alpha.layer != "00", arr.ind = T) 
      
      if (nrow(ind) > 0) {
        pt.ijk <- as.matrix(cbind(as.data.frame(ind) - 1,k = vol$k.idx[idx], t = 1))
        
        pt <- (as.matrix(cbind(as.data.frame(ind) - 1,k = vol$k.idx[idx],t = 1)) %*% t(M %*% vol$xyz.from.ijk))
        A <- (as.matrix(sweep(pt.ijk,2, c( 0.5,  0.5, 0, 0))) %*% t(M %*% vol$xyz.from.ijk))
        B <- (as.matrix(sweep(pt.ijk,2, c(-0.5,  0.5, 0, 0))) %*% t(M %*% vol$xyz.from.ijk))
        C <- (as.matrix(sweep(pt.ijk,2, c(-0.5, -0.5, 0, 0))) %*% t(M %*% vol$xyz.from.ijk))
        D <- (as.matrix(sweep(pt.ijk,2, c( 0.5, -0.5, 0, 0))) %*% t(M %*% vol$xyz.from.ijk))
        
        mesh <- list()
        mesh$vb <- t((rbind(A,B,C,D)))
        mesh$qu <- rbind(1:nrow(A),(1:nrow(A)) + nrow(A), (1:nrow(A)) + 2 * nrow(A), (1:nrow(A)) + 3 * nrow(A))
        material <- material3d()
        material$specular = "black"
        material$color <- as.character(map.layer[ind])
        material$lit <- FALSE
        m <- mesh3d(vertices = mesh$vb, quads = mesh$qu , meshColor = "faces", material = material)
        shade3d(m)
      }
    }
  }
}

Try the espadon package in your browser

Any scripts or data that you put into this service are public.

espadon documentation built on May 8, 2026, 9:07 a.m.