R-CMD-check

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.path = "man/figures/README-",
  out.width = "100%", 
  fig.width=12, fig.height=8
)

Work in progress to get constrained and conforming Delaunay triangulation for shapes in R with CGAL. We have

We need:

We can now hit this more easily thanks to cgal4h thanks to Ahmadou Dicko.

laridae came out of a need for constrained triangulation for a topology-in-R project. That effort has moved on somewhat, proving the case by using RTriangle and then bedding down the normalization model in the hypertidy/silicate package.

R now has cgal4h providing the library infrastructure for CGAL and euclid providing numerically robust geometric primitives (it's unclear if laridae or whatever it becomes will use euclid).

RTriangle is really fast, but it's not as fast as CGAL. CGAL can also be used to update a triangulation, which means (I think) that we could build an unconstrained triangulation from all the coordinates, and then add in any segments, even unclosed linear paths. At any rate, being able to update a mesh has a lot of applications, especially for neighbouring shapes, and for on-demand (extent or zoom dependent level of detail) tasks.

The interest (which seems miniscule ...) in constrained triangulations is discussed here along with the overall landscape in R.

https://github.com/r-spatial/discuss/issues/6

Installation

remotes::install_github("hypertidy/laridae")
## not universe ready yet
#install.packages("laridae", repos = "https://hypertidy.r-universe.dev")

Triangulation

Triangulate with CGAL via laridae. The function tri_xy performs an exact Delaunay triangulation on all vertices, returning a triplet-index for each triangle (zero-based in CGAL).

The variants tri_xy1() and tri_xy2() work slightly differently illustrating CGAL usage in C++ (thanks to Mark Padgham). 'xy1' order is not trivial ... but we ignore that for now.

library(laridae)
x <- c(0, 0, 1)
y <- c(0, 1, 1)
plot(x, y, pch = "+")
(idx0 <- tri_xy(x, y))
(idx1 <- tri_xy1(x, y))
(idx2 <- tri_xy2(x, y))

polygon(cbind(x, y)[idx0, ])

x    <- c(2.3,3.0,7.0,1.0,3.0,8.0)
y    <- c(2.3,3.0,2.0,5.0,8.0,9.0)
idx <- tri_xy(x, y)
plot(x, y, pch = "+", cex = 1.5)
polygon(cbind(x, y)[rbind(matrix(idx, 3L), NA), ], lty = 3)

Some timings, to show we aren't wildly off-base and that CGAL wins for raw unconstrained Delaunay triangulation.

set.seed(90)
x <- rnorm(1e3, sd = 4)
y <- rnorm(1e3, sd = 2)
library(laridae)

# plot a matrix xy as points
# and add the triangulation indexed
# by structural triplet row-identifiers
poly_index <- function(xy, index, ...) {
  plot(xy, ...)
  ## assume index is 0,1,2,0,1,2,0,1,...
  ii <- c(rbind(matrix(index, nrow = 3), NA_integer_))
  ## have forgetten why polypath fails, so just use polygon
  polygon(xy[ii, 1], xy[ii, 2])
}


ps <- RTriangle::pslg(P = cbind(x, y))

microbenchmark::microbenchmark(
  ind_t <- tri_xy(x, y), 
  ind_t1 <- tri_xy1(x, y), 
  ind_t2 <- tri_xy2(x, y), 
  RT <- RTriangle::triangulate(ps)
)
length(ind_t)
length(ind_t1)
length(ind_t2)
length(RT$T)


p <- par(mfrow = c(2, 2), mar = rep(0, 4))
poly_index(cbind(x, y), ind_t, pch = ".")
## can't work as order is not aligned, but still fun
poly_index(cbind(x, y), ind_t1, pch = ".")  
poly_index(cbind(x, y), ind_t2, pch = ".")
plot(RT)
par(p)

Constrained triangulation

Currently laridae only has vertex-output and "reporting" of the result. I can't yet see how to

The only other implementation in R is in RTriangle, so we use that for comparison.

sfx <- silicate::inlandwaters
sc <- silicate::SC(sfx)
X <- sc$vertex$x_
Y <- sc$vertex$y_
i0 <- match(sc$edge$.vx0, sc$vertex$vertex_)
i1 <- match(sc$edge$.vx1, sc$vertex$vertex_)

system.time(insert_constraint(X, Y, i0 , i1))
system.time(segment_constraint(sc))

## insert_mesh actually returns the vertices (which might include new ones)
system.time(xy_out <- insert_mesh(X, Y, i0 , i1))
plot(xy_out, pch = ".")
## compare RTriangle, it's fast if we don't include pslg() time
ps <- RTriangle::pslg(cbind(X, Y), S = cbind(i0, i1))
system.time({
  tr <- RTriangle::triangulate(ps, D = TRUE)
})


plot(tr$P, pch= ".")
segments(tr$P[tr$E[,1],1], tr$P[tr$E[,1],2], 
         tr$P[tr$E[,2],1], tr$P[tr$E[,2],2])

str(tr)

History

Was originally called cgalgris.

Code of Conduct

Please note that the laridae project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.



r-gris/cgalgris documentation built on Nov. 5, 2022, 8:02 p.m.