Skip to content

Commit

Permalink
Merge pull request #35 from dipterix/custom-electrode-geom
Browse files Browse the repository at this point in the history
added custom electrode shape support
  • Loading branch information
dipterix authored Mar 19, 2024
2 parents fe5f2c8 + b53c072 commit 7a59b8b
Show file tree
Hide file tree
Showing 33 changed files with 2,310 additions and 363 deletions.
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
^inst/threeBrainJS/package
^inst/threeBrainJS/webpack
^inst/threeBrainJS/\.npmignore$
^inst/prototypes/.*\.R$
^build\.sh$
^CRAN-RELEASE$
^\.travis\.yml$
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ jobs:
r-version: ${{ matrix.config.r }}
http-user-agent: ${{ matrix.config.http-user-agent }}
use-public-rspm: true
extra-repositories: |
https://rave-ieeg.r-universe.dev
- uses: r-lib/actions/setup-r-dependencies@v2
with:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/pkgdown.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ jobs:
- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true
extra-repositories: |
https://rave-ieeg.r-universe.dev
- uses: r-lib/actions/setup-r-dependencies@v2
with:
Expand Down
20 changes: 10 additions & 10 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: threeBrain
Type: Package
Title: 3D Brain Visualization
Version: 1.0.1.9109
Title: Your Advanced 3D Brain Visualization
Version: 1.0.1.9200
Authors@R: c(
person("Zhengjia", "Wang", email = "[email protected]", role = c("aut", "cre", "cph")),
person("John", "Magnotti", email = "[email protected]", role = c("aut", "res")),
Expand All @@ -18,14 +18,14 @@ Description: A fast, interactive cross-platform, and easy to share
a web browser with 'WebGL2' support (for example, 'Chrome', 'Firefox',
'Safari'), and can be inserted into any websites. The 'R-shiny'
support allows the 3D viewer to be dynamically generated from reactive user
inputs. This feature has been fully adopted by 'RAVE'
inputs. Please check the publication by Wang, Magnoti, Zhang,
and Beauchamp (2023, <doi:10.1523/ENEURO.0328-23.2023>) for electrode
localization. This viewer has been fully adopted by 'RAVE'
<https://openwetware.org/wiki/RAVE>, an interactive toolbox to
analyze 'iEEG' data. Documentation about 'threeBrain' is provided
by <https://dipterix.org/threeBrain/> and several vignettes included
in this package. To cite the package, please check our 'NeuroImage' paper
by Magnotti, Wang, and Beauchamp (2020, <doi:10.1016/j.neuroimage.2020.117341>),
or see 'citation("threeBrain")' for details.
License: GPL-3 | file LICENSE
analyze 'iEEG' data by Magnotti, Wang, and Beauchamp (2020,
<doi:10.1016/j.neuroimage.2020.117341>). Please check
'citation("threeBrain")' for details.
License: MPL-2.0 | file LICENSE
Encoding: UTF-8
RoxygenNote: 7.3.1
Roxygen: list(r6 = FALSE)
Expand All @@ -38,7 +38,6 @@ Imports:
grDevices,
graphics,
dipsaus,
ravetools,
xml2,
servr,
png,
Expand All @@ -55,5 +54,6 @@ Suggests:
knitr,
rmarkdown,
DT,
ravetools,
htmltools
VignetteBuilder: knitr
422 changes: 357 additions & 65 deletions LICENSE

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export(get_ijk2ras)
export(import_from_freesurfer)
export(import_fs)
export(import_suma)
export(list_electrode_prototypes)
export(load_colormap)
export(localization_module)
export(merge_brain)
Expand Down
18 changes: 18 additions & 0 deletions R/aaa.R
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,24 @@ freesurferformats::write.nifti2

cache_version <- 0.1

DEFAULT_COLOR_DISCRETE <- c(
# default RAVE colors
"orange", "dodgerblue3", "darkgreen", "orangered", "brown", "purple3",

# polychrome
"#5A5156", "#E4E1E3", "#F6222E", "#FE00FA", "#16FF32", "#3283FE",
"#FEAF16", "#B00068", "#1CFFCE", "#90AD1C", "#2ED9FF", "#DEA0FD",
"#AA0DFE", "#F8A19F", "#325A9B", "#C4451C", "#1C8356", "#85660D",
"#B10DA1", "#FBE426", "#1CBE4F", "#FA0087", "#FC1CBF", "#F7E1A0",
"#C075A6", "#782AB6", "#AAF400", "#BDCDFF", "#822E1C", "#B5EFB5",
"#7ED7D1", "#1C7F93", "#D85FF7", "#683B79", "#66B0FF", "#3B00FB"
)

DEFAULT_COLOR_CONTINUOUS <- c(
"#053061", "#2166ac", "#4393c3", "#92c5de", "#d1e5f0",
"#ffffff", "#fddbc7", "#f4a582", "#d6604d", "#b2182b", "#67001f"
)

#' @title Setup Package, Install Environment
#' @author Zhengjia Wang
#' @param continued logical, there are two phases of setting up environment. You
Expand Down
57 changes: 43 additions & 14 deletions R/class_animations.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ KeyFrame <- R6::R6Class(
target = '.material.color',

initialize = function(name, time, value, dtype = 'continuous', target = '.material.color', ...){
value <- unname(value)

if( dtype == 'continuous' ){
private$.dtype <- 'continuous'
value <- as.numeric(value)
if( !is.list(value) ) {
value <- as.numeric(value)
}
sel <- !is.na(value)
time <- time[ sel ]
value <- value[ sel ]
Expand All @@ -28,8 +31,27 @@ KeyFrame <- R6::R6Class(
private$.dtype <- 'discrete'

# If is factor, then do not remake factor as we need to keep the levels
if(!is.factor(value)){
value <- factor(value, ...)
if( is.list(value) ) {
if(length(value)) {

# get levels
lv <- lapply(unname(value), function(v) {
if(is.factor(v)) {
return(levels(v))
}
unique(as.character(unlist(v)))
})
lv <- sort(unique(unlist(lv)))
lv <- lv[!is.na(lv)]

value <- lapply(unname(value), function(v) {
factor(as.character(v), levels = lv)
})
}
} else {
if(!is.factor(value)){
value <- factor(value, levels = as.character(sort(unique(value), decreasing = FALSE)))
}
}
}

Expand Down Expand Up @@ -61,7 +83,7 @@ KeyFrame <- R6::R6Class(

json_cache(path = path, data = structure(list(self$to_list()), names = name), ...)
if(self$is_continuous){
private$.values <- range(private$.values)
private$.values <- range(unlist(private$.values))
}else{
private$.values <- unique(private$.values)
}
Expand All @@ -82,10 +104,14 @@ KeyFrame <- R6::R6Class(
return(rg)
},
value_range = function(){
if( self$is_continuous ){ range(private$.values) }else{ NULL }
if( self$is_continuous ){ range(unlist(private$.values), na.rm = TRUE) }else{ NULL }
},
value_names = function(){
if( !self$is_continuous ){ levels(private$.values) }else{ NULL }
if( self$is_continuous ) { return(NULL) }
if(is.list(private$.values) && length(private$.values) >= 1) {
return( levels(private$.values[[1]]) )
}
return(levels(private$.values))
}
)
)
Expand Down Expand Up @@ -154,7 +180,7 @@ ColorMap <- R6::R6Class(
hard_range = numeric(0),
value_names = NULL,
n_colors = 64,
colors = c('navyblue', '#e2e2e2', 'red'),
colors = DEFAULT_COLOR_CONTINUOUS,

initialize = function(name, ..., .list = NULL, symmetric = NULL, alias = NULL){

Expand Down Expand Up @@ -197,10 +223,10 @@ ColorMap <- R6::R6Class(

if( length(self$value_names) ){
self$value_type <- 'discrete'
self$colors <- grDevices::palette()
self$colors <- DEFAULT_COLOR_DISCRETE
}else{
self$value_type <- 'continuous'
self$colors <- c('navyblue', '#e2e2e2', 'red')
self$colors <- DEFAULT_COLOR_CONTINUOUS
}

self$set_colors()
Expand All @@ -218,10 +244,9 @@ ColorMap <- R6::R6Class(
# discrete, ncolors must equals to number of colors must equal to value_names
self$n_colors <- length( self$value_names )
if( self$n_colors > length(colors) ){
self$colors <- grDevices::colorRampPalette(colors)(self$n_colors)
}else{
self$colors <- colors[seq_len(self$n_colors)]
colors <- rep(colors, ceiling(self$n_colors / length(colors)))
}
self$colors <- colors[seq_len(self$n_colors)]
}
},

Expand All @@ -231,9 +256,13 @@ ColorMap <- R6::R6Class(
ncols <- max(16 , 2^ceiling(log2(self$n_colors)) )
if( self$value_type == 'continuous' ){
colors <- grDevices::colorRampPalette(self$colors)(ncols)
color_keys <- seq( self$value_range[1], self$value_range[2], length.out = ncols )
value_range <- self$value_range
if(!all(is.finite(value_range))) {
value_range <- c(-1, 1)
}
color_keys <- seq( value_range[1], value_range[2], length.out = ncols )
}else{
colors <- grDevices::colorRampPalette(self$colors)( self$n_colors )
colors <- col2hexStr(self$colors)
if(length(colors) < ncols) {
colors <- c(colors, rep("#000000", ncols - length(colors)))
}
Expand Down
30 changes: 24 additions & 6 deletions R/class_brain.R
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Brain2 <- R6::R6Class(
Norig = diag(rep(1, 4)),
Torig = diag(rep(1, 4)),

initialize = function(subject_code, xfm, Norig, Torig){
initialize = function(subject_code, xfm, Norig, Torig, base_path = NULL){
stopifnot2( length(xfm) == 16 && length(dim(xfm)) == 2 && sum(dim(xfm)) == 8,
msg = 'xfm must be 4x4 matrix')
stopifnot2( length(Norig) == 16 && length(dim(Norig)) == 2 && sum(dim(Norig)) == 8,
Expand All @@ -66,6 +66,11 @@ Brain2 <- R6::R6Class(
group = GeomGroup$new(name = sprintf('_internal_group_data_%s', subject_code)),
name = sprintf('_misc_%s', subject_code)
)

if(length(base_path) == 1 && !is.na(base_path) && is.character(base_path) &&
file.exists(base_path)) {
self$base_path <- base_path
}
},

add_surface = function(
Expand Down Expand Up @@ -229,14 +234,22 @@ Brain2 <- R6::R6Class(
if(!inherits(atlas, 'brain-atlas')) {

stopifnot2(is.character(atlas), msg = 'atlas must be a brain-atlas object or valid atlas name from FreeSurfer folder')
atlas <- gsub("_", "+", atlas)
path_atlases <- file.path( self$base_path, "mri", as.vector(rbind(
sprintf("%s.mgz", atlas),
sprintf("%s.nii.gz", atlas),
sprintf("%s.nii", atlas)
)) )
atlas_path <- path_atlases[file.exists(path_atlases)]
if(!length(atlas_path)) { return() }
if(!length(atlas_path)) {
atlas <- gsub("_", "+", atlas)
path_atlases <- file.path( self$base_path, "mri", as.vector(rbind(
sprintf("%s.mgz", atlas),
sprintf("%s.nii.gz", atlas),
sprintf("%s.nii", atlas)
)) )
atlas_path <- path_atlases[file.exists(path_atlases)]
if(!length(atlas_path)) { return() }
}
atlas_path <- atlas_path[[ 1 ]]

atlas_geom <- VolumeGeom2$new(
Expand Down Expand Up @@ -276,13 +289,18 @@ Brain2 <- R6::R6Class(
)
},

set_electrodes = function(electrodes, coord_sys = c("tkrRAS", "scannerRAS", "MNI305", "MNI152"), ...){
set_electrodes = function(electrodes, coord_sys = c("tkrRAS", "scannerRAS", "MNI305", "MNI152"), ...,
priority = c("prototype", "sphere", "both")){
coord_sys <- match.arg(coord_sys)
priority <- match.arg(priority)
if( missing(electrodes) ) {
electrodes <- self$electrodes$raw_table
}
if( R6::is.R6(electrodes) && 'brain-electrodes' %in% class(electrodes)){
self$electrodes <- electrodes
self$electrodes$set_brain( self )
}else{
self$electrodes$set_electrodes( electrodes, coord_sys = coord_sys, ... )
self$electrodes$set_electrodes( electrodes, coord_sys = coord_sys, priority = priority, ... )
}
},

Expand Down Expand Up @@ -440,7 +458,7 @@ Brain2 <- R6::R6Class(

if( isTRUE(electrodes) && !is.null(self$electrodes) ){
# self$electrodes$set_values()
geoms <- c(geoms, self$electrodes$objects)
geoms <- c(geoms, unique(unlist(self$electrodes$objects)), unique(unlist(self$electrodes$objects2)))
}

geoms <- c(geoms, self$misc)
Expand Down
Loading

0 comments on commit 7a59b8b

Please sign in to comment.