diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 214a7600..23cc65b1 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -40,7 +40,11 @@ jobs: with: r-version: ${{ matrix.config.r }} http-user-agent: ${{ matrix.config.http-user-agent }} - use-public-rspm: true + # Public RSPM (Posit Package Manager) binaries only on Linux, where + # they are the big win. macOS/Windows fall back to CRAN binaries: + # P3M's macOS arm64 Rvcg build currently fails to load with a missing + # libomp symbol (___kmpc_dispatch_init_4u), and CRAN's build is clean. + use-public-rspm: ${{ startsWith(matrix.config.os, 'ubuntu') }} - uses: r-lib/actions/setup-r-dependencies@v2 with: diff --git a/DESCRIPTION b/DESCRIPTION index fa709df5..03397319 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: nat Type: Package Title: NeuroAnatomy Toolbox for Analysis of 3D Image Data -Version: 1.11.0 +Version: 1.11.1 Authors@R: c( person("Gregory","Jefferis", email="jefferis@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-0587-9355")), diff --git a/NEWS.md b/NEWS.md index d35a0571..a2eab7ad 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,13 @@ +# nat 1.11.1 + +* `pointsinside()` now uses natcpp's (>= 0.3.2) generalised winding-number test + for logical results when the natcpp package is installed. This is independent + of face normals, so it is robust to inconsistently oriented meshes that could + previously give spurious inside/outside classifications with the normal-based + `Rvcg` test. The `Rvcg` path remains the fallback and still handles + `rval="distance"` and `rval="mesh3d"`; set `options(nat.use_natcpp=FALSE)` to + force it. + # nat 1.11.0 This release bundles together a large number of changes and is targeting a CRAN diff --git a/R/hxsurf.R b/R/hxsurf.R index 4e812bd6..5506516d 100644 --- a/R/hxsurf.R +++ b/R/hxsurf.R @@ -613,9 +613,18 @@ NULL #' testing repeatedly against the same surface, it may make sense to #' pre-convert. #' -#' \code{pointsinside} depends on the face normals for each face pointing out -#' of the object (see example). The face normals are defined by the order of -#' the three vertices making up a triangular face. You can flip the face +#' When the \href{https://natverse.org/natcpp/}{natcpp} package (>= 0.3.2) is +#' installed, logical results (\code{rval="logical"} or +#' \code{"consistent_logical"}) use its generalised (solid-angle) +#' winding-number test. This does not depend on face normals and is therefore +#' robust to inconsistently oriented faces, so the face-normal caveats below +#' apply only to the \code{Rvcg} fallback (used when natcpp is unavailable and +#' for \code{rval="distance"} or \code{"mesh3d"}). Set +#' \code{options(nat.use_natcpp=FALSE)} to force the \code{Rvcg} path. +#' +#' The \code{Rvcg} fallback depends on the face normals for each face pointing +#' out of the object (see example). The face normals are defined by the order +#' of the three vertices making up a triangular face. You can flip the face #' normal for a face by permuting the vertices (i.e. 1,2,3 -> 1,3,2). If you #' find for a given surface that points are outside when you expect them to be #' inside then the face normals are probably all the wrong way round. You can @@ -719,14 +728,23 @@ pointsinside.default<-function(x, surf, ..., rval=c('logical','distance', stop("Only logical return values are currently possible ", "with boundingbox objects!") } - - if(!requireNamespace('Rvcg', quietly = TRUE)) - stop("Please install suggested library Rvcg to use pointsinside") - + if(!inherits(surf,'mesh3d')) { surf=as.mesh3d(surf, ...) } + # Fast, normal-independent path for logical results: natcpp's generalised + # winding-number test (>= 0.3.2, method="auto"). Unlike the Rvcg signed + # distance it does not depend on face normals, so it avoids spurious results + # near thin/sharp features. distance and mesh3d still require Rvcg below. + if(rval=='consistent_logical' && !is.null(surf$it) && + use_natcpp(version='0.3.2')) { + return(natcpp::c_pointsinside(xyzmatrix(x), xyzmatrix(surf), t(surf$it))) + } + + if(!requireNamespace('Rvcg', quietly = TRUE)) + stop("Please install suggested library Rvcg to use pointsinside") + pts=xyzmatrix(x) rmesh=Rvcg::vcgClostKD(pts, surf, sign = TRUE) switch(rval, diff --git a/R/utils.R b/R/utils.R index 246642fb..bc6b65a5 100644 --- a/R/utils.R +++ b/R/utils.R @@ -19,7 +19,12 @@ nat_progress <- function (x, max = 100, message = NULL) { # to check if we should use natcpp # always=TRUE => use if installed even if option says otherwise -use_natcpp <- function(always=FALSE) { +# version => optional minimum natcpp version required (character or numeric_version) +use_natcpp <- function(always=FALSE, version=NULL) { opcheck <- isTRUE(always) || !isFALSE(getOption('nat.use_natcpp')) - opcheck && requireNamespace('natcpp', quietly = TRUE) + if(!opcheck || !requireNamespace('natcpp', quietly = TRUE)) + return(FALSE) + if(!is.null(version) && utils::packageVersion('natcpp') < version) + return(FALSE) + TRUE } diff --git a/man/pointsinside.Rd b/man/pointsinside.Rd index 49f9d48e..8d1304b0 100644 --- a/man/pointsinside.Rd +++ b/man/pointsinside.Rd @@ -40,9 +40,18 @@ Note that \code{hxsurf} surface objects will be converted to testing repeatedly against the same surface, it may make sense to pre-convert. - \code{pointsinside} depends on the face normals for each face pointing out - of the object (see example). The face normals are defined by the order of - the three vertices making up a triangular face. You can flip the face + When the \href{https://natverse.org/natcpp/}{natcpp} package (>= 0.3.2) is + installed, logical results (\code{rval="logical"} or + \code{"consistent_logical"}) use its generalised (solid-angle) + winding-number test. This does not depend on face normals and is therefore + robust to inconsistently oriented faces, so the face-normal caveats below + apply only to the \code{Rvcg} fallback (used when natcpp is unavailable and + for \code{rval="distance"} or \code{"mesh3d"}). Set + \code{options(nat.use_natcpp=FALSE)} to force the \code{Rvcg} path. + + The \code{Rvcg} fallback depends on the face normals for each face pointing + out of the object (see example). The face normals are defined by the order + of the three vertices making up a triangular face. You can flip the face normal for a face by permuting the vertices (i.e. 1,2,3 -> 1,3,2). If you find for a given surface that points are outside when you expect them to be inside then the face normals are probably all the wrong way round. You can