Skip to content

Commit 3a00d0f

Browse files
authored
Merge branch 'master' into feature/print-methods
2 parents 6e6fc73 + f7e5f95 commit 3a00d0f

32 files changed

Lines changed: 455 additions & 180 deletions

DESCRIPTION

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ Suggests:
4747
MASS,
4848
alphashape3d,
4949
Morpho,
50-
plotly
50+
plotly,
51+
readobj
5152
License: GPL-3
5253
LazyData: yes
5354
Collate:
@@ -76,6 +77,7 @@ Collate:
7677
'neuron-io-fiji.R'
7778
'neuron-io-neuroml.R'
7879
'neuron-io.R'
80+
'neuron-mesh.R'
7981
'neuron-plot.R'
8082
'neuronlist.R'
8183
'neuronlist_interactive_3d.R'

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ S3method(subset,hxsurf)
153153
S3method(subset,neuron)
154154
S3method(subset,neuronlist)
155155
S3method(summary,dotprops)
156+
S3method(summary,mesh3d)
156157
S3method(summary,neuron)
157158
S3method(summary,neuronlist)
158159
S3method(tail,neuronlist)

R/cmtkreg.R

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ cmtkreg.filetype <- function(x) {
144144
#' }
145145
#' @importFrom rgl plot3d
146146
#' @export
147-
plot3d.cmtkreg <- function(x, ..., plotengine = getOption('nat.plotengine')) {
147+
plot3d.cmtkreg <- function(x, ..., gridlines = FALSE, plotengine = getOption('nat.plotengine')) {
148148
plotengine <- check_plotengine(plotengine)
149149
if (plotengine == 'plotly') {
150150
psh <- openplotlyscene()$plotlyscenehandle
@@ -172,8 +172,15 @@ plot3d.cmtkreg <- function(x, ..., plotengine = getOption('nat.plotengine')) {
172172
psh <- psh %>%
173173
plotly::add_trace(data = plotdata, x = ~X, y = ~Y , z = ~Z,
174174
hoverinfo = "none",type = 'scatter3d', mode = 'markers',
175-
opacity = opacity, marker=list(color = 'black', size = 3)) %>%
176-
plotly::layout(showlegend = FALSE, scene=list(camera=.plotly3d$camera))
175+
opacity = opacity, marker=list(color = 'black', size = 3))
176+
177+
psh <- psh %>% plotly::layout(showlegend = FALSE, scene=list(camera=.plotly3d$camera))
178+
if(gridlines == FALSE){
179+
psh <- psh %>% plotly::layout(scene = list(xaxis=.plotly3d$xaxis,
180+
yaxis=.plotly3d$yaxis,
181+
zaxis=.plotly3d$zaxis))
182+
}
183+
177184
assign("plotlyscenehandle", psh, envir=.plotly3d)
178185
psh
179186
}

R/dotprops.R

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,8 @@ all.equal.dotprops<-function(target, current, check.attributes=FALSE,
324324
#' }
325325
plot3d.dotprops<-function(x, scalevecs=1.0, alpharange=NULL, color='black',
326326
PlotPoints=FALSE, PlotVectors=TRUE, UseAlpha=FALSE,
327-
..., plotengine = getOption('nat.plotengine')){
327+
..., gridlines = FALSE,
328+
plotengine = getOption('nat.plotengine')){
328329
# rgl's generic plot3d will dispatch on this
329330
if (!is.null(alpharange))
330331
x=subset(x,x$alpha<=alpharange[2] & x$alpha>=alpharange[1])
@@ -379,8 +380,14 @@ plot3d.dotprops<-function(x, scalevecs=1.0, alpharange=NULL, color='black',
379380
if (plotengine == 'rgl'){
380381
invisible(rlist)
381382
} else {
382-
psh <- psh %>%
383-
plotly::layout(showlegend = FALSE, scene=list(camera=.plotly3d$camera))
383+
384+
psh <- psh %>% plotly::layout(showlegend = FALSE, scene=list(camera=.plotly3d$camera))
385+
if(gridlines == FALSE){
386+
psh <- psh %>% plotly::layout(scene = list(xaxis=.plotly3d$xaxis,
387+
yaxis=.plotly3d$yaxis,
388+
zaxis=.plotly3d$zaxis))
389+
}
390+
384391
assign("plotlyscenehandle", psh, envir=.plotly3d)
385392
psh
386393
}

R/hxsurf.R

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ write.hxsurf <- function(surf, filename) {
376376
#' plot3d(MBL.surf, alpha=0.3,
377377
#' materials=grep("VL", MBL.surf$RegionList, value = TRUE, invert = TRUE))
378378
#' }
379-
plot3d.hxsurf<-function(x, materials=NULL, col=NULL, ...,
379+
plot3d.hxsurf<-function(x, materials=NULL, col=NULL, gridlines = FALSE, ...,
380380
plotengine = getOption('nat.plotengine')){
381381
plotengine <- check_plotengine(plotengine)
382382
if (plotengine == 'rgl'){
@@ -427,9 +427,12 @@ plot3d.hxsurf<-function(x, materials=NULL, col=NULL, ...,
427427
if (plotengine == 'rgl'){
428428
invisible(rlist)
429429
} else {
430-
psh <- psh %>%
431-
plotly::layout(showlegend = FALSE,
432-
scene=list(camera=.plotly3d$camera))
430+
psh <- psh %>% plotly::layout(showlegend = FALSE, scene=list(camera=.plotly3d$camera))
431+
if(gridlines == FALSE){
432+
psh <- psh %>% plotly::layout(scene = list(xaxis=.plotly3d$xaxis,
433+
yaxis=.plotly3d$yaxis,
434+
zaxis=.plotly3d$zaxis))
435+
}
433436
assign("plotlyscenehandle", psh, envir=.plotly3d)
434437
psh
435438
}

R/neuron-io.R

Lines changed: 67 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,53 @@
11
#' Read a single neuron from a file
2-
#'
3-
#' @details This function will handle \code{neuron} and \code{dotprops} objects
4-
#' saved in R .rds or .rda format by default. Additional file formats can be
2+
#'
3+
#' @details This function will handle \code{neuron} and \code{dotprops} objects
4+
#' saved in R .rds or .rda format by default. Additional file formats can be
55
#' registered using \code{fileformats}.
6-
#'
7-
#' At the moment the following formats are supported using file readers
6+
#'
7+
#' At the moment the following formats are supported using file readers
88
#' already included with the nat package: \itemize{
9-
#'
10-
#' \item \bold{swc} See \code{\link{read.neuron.swc}}. SWC files can also
11-
#' return an \code{\link{ngraph}} object containing the neuron structure in a
12-
#' (permissive) general graph format that also contains the 3D positions for
9+
#'
10+
#' \item \bold{swc} See \code{\link{read.neuron.swc}}. SWC files can also
11+
#' return an \code{\link{ngraph}} object containing the neuron structure in a
12+
#' (permissive) general graph format that also contains the 3D positions for
1313
#' each vertex.
14-
#'
14+
#'
1515
#' \item \bold{neuroml} See \code{\link{read.neuron.neuroml}}
16-
#'
16+
#'
1717
#' \item \bold{fijitraces} See \code{\link{read.neuron.fiji}}. The file format
18-
#' used by the \href{http://fiji.sc/Simple_Neurite_Tracer}{Simple Neurite
18+
#' used by the \href{http://fiji.sc/Simple_Neurite_Tracer}{Simple Neurite
1919
#' Tracer} plugin of Fiji/ImageJ.
20-
#'
21-
#' \item \bold{hxlineset,hxskel} Two distinct fileformats used by Amira.
22-
#' \code{hxlineset} is the generic one, \code{hxskel} is used by the
20+
#'
21+
#' \item \bold{hxlineset,hxskel} Two distinct fileformats used by Amira.
22+
#' \code{hxlineset} is the generic one, \code{hxskel} is used by the
2323
#' hxskeletonize extension of Schmitt and Evers (see refs).
24-
#'
25-
#' \item \bold{rda,rds} Native R cross-platform binary formats (see
26-
#' \code{\link{load}, \link{readRDS}}). Note that RDS only contains a single
24+
#'
25+
#' \item \bold{rda,rds} Native R cross-platform binary formats (see
26+
#' \code{\link{load}, \link{readRDS}}). Note that RDS only contains a single
2727
#' unnamed neuron, whereas rda contains one or more named neurons.
28-
#'
28+
#'
29+
#' \item \bold{obj,ply} 3D Mesh formats encoding surface models of neurons.
30+
#' These depend on the suggested package \code{\link[Rvcg]{Rvcg}} (for 'ply'
31+
#' format) and \code{\link[readobj]{readobj}} (for Wavefront 'obj' format).
32+
#'
2933
#' }
3034
#' @export
31-
#' @param f Path to file. This can be a URL, in which case the file is
35+
#' @param f Path to file. This can be a URL, in which case the file is
3236
#' downloaded to a temporary location before reading.
33-
#' @param format The file format of the neuron. When \code{format=NULL}, the
34-
#' default, \code{read.neuron} will infer the file format from the extension
37+
#' @param format The file format of the neuron. When \code{format=NULL}, the
38+
#' default, \code{read.neuron} will infer the file format from the extension
3539
#' or file header (aka magic) using the \code{fileformats} registry.
36-
#' @param class The class of the returned object - presently either
40+
#' @param class The class of the returned object - presently either
3741
#' \code{"neuron"} or \code{"ngraph"}
3842
#' @param ... additional arguments passed to format-specific readers
3943
#' @seealso \code{\link{write.neuron}}, \code{\link{read.neurons}},
4044
#' \code{\link{fileformats}}
41-
#' @references Schmitt, S. and Evers, J. F. and Duch, C. and Scholz, M. and
42-
#' Obermayer, K. (2004). New methods for the computer-assisted 3-D
43-
#' reconstruction of neurons from confocal image stacks. Neuroimage 4,
44-
#' 1283--98.
45+
#' @references Schmitt, S. and Evers, J. F. and Duch, C. and Scholz, M. and
46+
#' Obermayer, K. (2004). New methods for the computer-assisted 3-D
47+
#' reconstruction of neurons from confocal image stacks. Neuroimage 4,
48+
#' 1283--98.
4549
#' \href{http://dx.doi.org/10.1016/j.neuroimage.2004.06.047}{doi:10.1016/j.neuroimage.2004.06.047}
46-
#'
50+
#'
4751
#' @examples
4852
#' \dontrun{
4953
#' # note that we override the default NeuronName field
@@ -98,7 +102,7 @@ read.neuron<-function(f, format=NULL, class=c("neuron", "ngraph"), ...){
98102
#' Read one or more neurons from file to a neuronlist in memory
99103
#'
100104
#' @details This function will cope with the same set of file formats offered by
101-
#' \code{read.neuron}.
105+
#' \code{\link{read.neuron}}.
102106
#'
103107
#' If the \code{paths} argument specifies a (single) directory then all files
104108
#' in that directory will be read unless an optional regex pattern is also
@@ -594,7 +598,7 @@ is.swc<-function(f, TrustSuffix=TRUE) {
594598
all(sapply(first_line[c("X", "Y", "Z", "W")], is.numeric))
595599
}
596600

597-
#' Write out a neuron in any of the file formats we know about
601+
#' Write out a neuron skeleton or mesh in any of the file formats we know about
598602
#'
599603
#' If file is not specified the neuron's InputFileName field will be checked
600604
#' (for a dotprops object it will be the \code{'file'} attribute). If this is
@@ -612,14 +616,17 @@ is.swc<-function(f, TrustSuffix=TRUE) {
612616
#' \code{normalise.ids=NA} will normalise \code{PointNo} vertex ids only when
613617
#' a vertex is connected (by the \code{Parent} field) to a vertex that had not
614618
#' yet been defined. Many readers make the assumption that this is true. When
615-
#' \code{normalise.ids=F} the vertex ids will not be touched.
619+
#' \code{normalise.ids=FALSE} the vertex ids will not be touched.
616620
#'
617621
#' @param n A neuron
618622
#' @param file Path to output file
619-
#' @param dir Path to directory (this will replace dirname(file) if specified)
623+
#' @param dir Path to directory (this will replace \code{dirname(file)} if
624+
#' specified)
620625
#' @param format Unique abbreviation of one of the registered file formats for
621-
#' neurons including 'swc', 'hxlineset', 'hxskel', if no format can be extracted from
622-
#' the filename and the ext parameter, then it defaults to 'swc'
626+
#' neurons including 'swc', 'hxlineset', 'hxskel' (skeletons) and 'ply', 'obj'
627+
#' (neuron meshes). If no format can be extracted from the filename or the
628+
#' \code{ext} parameter, then it defaults to 'swc' for skeletons and 'ply' for
629+
#' meshes.
623630
#' @param ext Will replace the default extension for the filetype and should
624631
#' include the period e.g. \code{ext='.amiramesh'} or \code{ext='_reg.swc'}.
625632
#' The special value of ext=NA will prevent the extension from being changed
@@ -635,12 +642,14 @@ is.swc<-function(f, TrustSuffix=TRUE) {
635642
#' # show the currently registered file formats that we can write
636643
#' fileformats(class='neuron', write=TRUE)
637644
#' \dontrun{
638-
#' # write out "myneuron.swc" in SWC format
645+
#' # write neuron to "myneuron.swc" in SWC format
639646
#' write.neuron(Cell07PNs[[1]], file='myneuron.swc')
640-
#' # write out "myneuron.swc" in SWC format, normalising the integer ids that
641-
#' # label every node (this is required by some SWC readers e.g. Fiji)
647+
#' # write in SWC format, normalising the integer ids that label every node
648+
#' # (this is required by some SWC readers e.g. Fiji)
642649
#' write.neuron(Cell07PNs[[1]], file='myneuron.swc', normalise.ids=TRUE)
643-
#'
650+
#' # write out "myneuron.swc" in SWC format withour the final extension
651+
#' write.neuron(Cell07PNs[[1]], file='myneuron.swc')
652+
644653
#' # write out "myneuron.amiramesh" in Amira hxlineset format
645654
#' write.neuron(Cell07PNs[[1]], format = 'hxlineset', file='myneuron.amiramesh')
646655
#'
@@ -670,13 +679,18 @@ write.neuron<-function(n, file=NULL, dir=NULL, format=NULL, ext=NULL,
670679
if(!(length(ext) && is.na(ext)))
671680
file=tools::file_path_sans_ext(file)
672681
}
673-
682+
if(!is.null(format)) {
683+
# TODO - one day it should be possible to have one file format associated
684+
# with different R classes
685+
if(format=='obj') format='neuron.obj'
686+
else if(format=='ply') format='neuron.ply'
687+
}
674688
fw=try(getformatwriter(format=format, file=file, ext=ext, class='neuron'), silent = T)
675689
if(inherits(fw, 'try-error')) {
676690
if(is.null(format)){
677-
format='swc'
691+
format <- if(inherits(n, 'mesh3d')) 'neuron.ply' else 'swc'
678692
fw=getformatwriter(format=format, file=file, ext=ext, class='neuron')
679-
warning('write.neuron: using default format="swc"')
693+
warning('write.neuron: using default format="',format,'"')
680694
} else {
681695
# rethrow the error
682696
stop(fw)
@@ -784,10 +798,19 @@ write.dotprops.swc<-function(x, file, ...) {
784798
#' write.neurons(Cell07PNs, dir="testwn", format='swc')
785799
#' # write some neurons in swc format for picky software
786800
#' write.neurons(Cell07PNs, dir="testwn", format='swc', normalise.ids=TRUE)
801+
#' # write some neurons in swc format and zip them up
802+
#' write.neurons(Cell07PNs, dir="testwn.zip", format='swc')
787803
#'
788804
#' # write some neurons in Amira hxlineset format
789805
#' write.neurons(Cell07PNs, dir="testwn", format='hxlineset')
790806
#'
807+
#' # write some neuron meshes in Stanford ply format (the default for meshes)
808+
#' write.neurons(myneurons, dir="testwn")
809+
#' # specify the format to avoid a warning. Write to a zip file.
810+
#' write.neurons(myneurons, dir="testmeshes.zip", format='ply')
811+
#' # Wavefront obj format
812+
#' write.neurons(myneurons, dir="testwn", format='obj')
813+
#'
791814
#' # organise new files in directory hierarchy by glomerulus and Scored.By field
792815
#' write.neurons(Cell07PNs,dir="testwn",
793816
#' subdir=file.path(Glomerulus,Scored.By),format='hxlineset')
@@ -859,10 +882,11 @@ write.neurons<-function(nl, dir, format=NULL, subdir=NULL, INDICES=names(nl),
859882
}
860883
if(!file.exists(thisdir)) dir.create(thisdir, recursive=TRUE)
861884
file=files[nn]
862-
if(!isTRUE(nzchar(file)) && is.neuron(n) && is.null(n$InputFileName)){
885+
if(!isTRUE(nchar(file)>0)){
863886
# the filename was not specified explicitly and we can't figure it out
864887
# from field inside the neuron, so set to name of object in neuronlist
865-
file=nn
888+
if(!is.neuron(n) || is.null(n$InputFileName))
889+
file=nn
866890
}
867891
if(interactive())
868892
pb$tick()

R/neuron-mesh.R

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# internal function
2+
# read a mesh representing a neuron and return a mesh3d object
3+
read.neuron.mesh <- function(x, ...) {
4+
ext=tools::file_ext(x)
5+
6+
if(ext=="ply") {
7+
if(!requireNamespace('Rvcg', quietly = TRUE))
8+
stop("Please install suggested library Rvcg to read .ply files!")
9+
Rvcg::vcgPlyRead(x, updateNormals=F, ...)
10+
} else if(ext=="obj") {
11+
if(!requireNamespace('readobj', quietly = TRUE))
12+
stop("Please install suggested library readobj to read .obj files!")
13+
res=readobj::read.obj(x, convert.rgl = TRUE)
14+
if(length(res)>1)
15+
warning("Only reading 1/",length(res)," objects in: ",x)
16+
res[[1]]
17+
} else {
18+
stop("Unrecognised mesh file format!")
19+
}
20+
}
21+
22+
is.ply<-function(f=NULL, bytes=NULL) {
23+
if(!is.null(bytes) && is.character(f) && length(f)>1)
24+
stop("Can only check bytes for a single file")
25+
tocheck=if(is.null(bytes)) f else bytes
26+
generic_magic_check(tocheck, "ply")
27+
}
28+
29+
write.neuron.ply <- function(x, file, binary=TRUE, ...) {
30+
write.neuron.mesh(x, file=file, format="ply", binary=binary, ...)
31+
}
32+
33+
write.neuron.obj <- function(x, file, ... ) {
34+
write.neuron.mesh(x, file=file, format="obj", ...)
35+
}
36+
37+
write.neuron.mesh <- function(x, file, format=c("ply", "obj"), ...) {
38+
if(!requireNamespace('Rvcg', quietly = TRUE))
39+
stop("Please install suggested library Rvcg to write .",format," files!")
40+
if(!inherits(x, 'mesh3d')) {
41+
x=tryCatch(as.mesh3d(x), error=function(e) stop("Unable to convert x to mesh3d object! Only neuron meshes can be written in ",format," format!"))
42+
}
43+
if(format=="ply")
44+
Rvcg::vcgPlyWrite(x, filename=file, writeCol = F, writeNormals = F, ...)
45+
else if(format=="obj")
46+
Rvcg::vcgObjWrite(x, filename=file, writeNormals=F, ...)
47+
else stop("Unknown format")
48+
49+
}

R/neuron-plot.R

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
plot3d.neuron<-function(x, WithLine=TRUE, NeuronNames=FALSE, WithNodes=TRUE,
6060
WithAllPoints=FALSE, WithText=FALSE, PlotSubTrees=TRUE,
6161
add=TRUE, col=NULL, soma=FALSE, ...,
62+
gridlines = FALSE,
6263
plotengine = getOption('nat.plotengine')){
6364
plotengine <- check_plotengine(plotengine)
6465
if (!add)
@@ -212,8 +213,13 @@ plot3d.neuron<-function(x, WithLine=TRUE, NeuronNames=FALSE, WithNodes=TRUE,
212213
if (plotengine == 'rgl'){
213214
invisible(rglreturnlist)
214215
} else{
215-
psh <- psh %>%
216-
plotly::layout(showlegend = FALSE, scene=list(camera=.plotly3d$camera))
216+
psh <- psh %>% plotly::layout(showlegend = FALSE, scene=list(camera=.plotly3d$camera))
217+
if(gridlines == FALSE){
218+
psh <- psh %>% plotly::layout(scene = list(xaxis=.plotly3d$xaxis,
219+
yaxis=.plotly3d$yaxis,
220+
zaxis=.plotly3d$zaxis))
221+
}
222+
217223
assign("plotlyscenehandle", psh, envir=.plotly3d)
218224
psh
219225
}
@@ -546,7 +552,7 @@ plot.neuron <- function(x, WithLine=TRUE, WithNodes=TRUE, WithAllPoints=FALSE,
546552
#' }
547553
#'
548554
plot3d.boundingbox <- function(x, col='black',
549-
plotengine = getOption('nat.plotengine'), ...) {
555+
gridlines = FALSE, plotengine = getOption('nat.plotengine'), ...) {
550556
plotengine <- check_plotengine(plotengine)
551557
pts <- matrix(c(
552558
c(x[1, 1], x[1, 2], x[1, 3]),
@@ -580,6 +586,11 @@ plot3d.boundingbox <- function(x, col='black',
580586
x = ~X, y = ~Y , z = ~Z,
581587
hoverinfo = "none", type = 'scatter3d', mode = 'lines',
582588
opacity = opacity, line=list(color = col, width = width))
589+
if(gridlines == FALSE){
590+
psh <- psh %>% plotly::layout(scene = list(xaxis=.plotly3d$xaxis,
591+
yaxis=.plotly3d$yaxis,
592+
zaxis=.plotly3d$zaxis))
593+
}
583594
.plotly3d$plotlyscenehandle <- psh
584595
psh
585596
}

0 commit comments

Comments
 (0)