diff --git a/inst/essais/Enneper.R b/inst/essais/Enneper.R index b146ba5..e5c1a53 100644 --- a/inst/essais/Enneper.R +++ b/inst/essais/Enneper.R @@ -1,7 +1,7 @@ library(cgalMeshes) library(rgl) -n <- 3 +n <- 2 Enneper <- function(phi, r) { rbind( r*cos(phi) - r^(2*n-1)*cos((2*n-1)*phi)/(2*n-1), @@ -11,7 +11,9 @@ Enneper <- function(phi, r) { } rmesh <- parametricMesh( - Enneper, urange = c(0, 2*pi), vrange = c(0, 1.3), - periodic = c(TRUE, FALSE), nu = 200, nv = 100 + Enneper, urange = c(0, 2*pi), vrange = c(0, 1.2), + periodic = c(TRUE, FALSE), nu = 200, nv = 100, clean = TRUE ) -rmesh <- Rvcg::vcgClean(rmesh, sel = 0) +#rmesh <- Rvcg::vcgClean(rmesh, sel = 0) + +shade3d(rmesh) \ No newline at end of file diff --git a/inst/essais/essai-param-ARAP-Enneper.R b/inst/essais/essai-param-ARAP-Enneper.R new file mode 100644 index 0000000..5bf7b1a --- /dev/null +++ b/inst/essais/essai-param-ARAP-Enneper.R @@ -0,0 +1,156 @@ +setwd("C:/SL/MyPackages/cgalMeshes/inst/trash") + +library(cgalMeshes) +library(rgl) + +n <- 2 +Enneper <- function(phi, r) { + rbind( + r*cos(phi) - r^(2*n-1)*cos((2*n-1)*phi)/(2*n-1), + r*sin(phi) + r^(2*n-1)*sin((2*n-1)*phi)/(2*n-1), + 2*r^n*cos(n*phi)/n + ) +} + +rmesh <- parametricMesh( + Enneper, urange = c(0, 2*pi), vrange = c(0, 1.2), + periodic = c(TRUE, FALSE), nu = 512L, nv = 128L, clean = TRUE +) + +shade3d(rmesh, col = "deeppink4") +bbox3d() + +# convert to CGAL mesh #### +mesh <- cgalMesh$new(rmesh) + +# take a look at the edge lengths +edges <- mesh$getEdges() +summary(edges[["length"]]) + +# add vertices in order that the checkeboard has regular lines #### +mesh$isotropicRemeshing(0.008, iterations = 3, relaxSteps = 2) + +# compute mesh parameterization #### +UV <- mesh$parameterization(method = "ARAP", lambda = 1000) + +png("EnneperARAP_UVspace.png", width = 384, height = 384) +opar <- par(mar = c(4, 4, 0, 0)) +plot(UV, asp = 1, pch = 19, xlab = "u", ylab = "v", axes = FALSE) +axis(1, at = seq(-3.5, 1.5, by = 0.5)) +axis(2, at = seq(-1.5, 2.5, by = 0.5)) +par(opar) +dev.off() + + +vs <- mesh$getVertices() +vsz <- vs[, 3L] +odec <- order(vsz, decreasing = TRUE) +vz1 <- odec[1L] +vz2 <- odec[2L] + +uv1 <- UV[vz1, ] +uv2 <- UV[vz2, ] +alpha <- atan2(uv2[1L]-uv1[1L], uv2[2L]-uv1[2L]) + +rotation <- function(alpha, uv) { + t(rbind( + c(cos(alpha), -sin(alpha)), + c(sin(alpha), cos(alpha)) + ) %*% t(uv)) +} + +UVrot <- rotation(alpha, UV) +Urot <- UVrot[, 1L] +Vrot <- UVrot[, 2L] + +png("EnneperARAP_UVspaceWithTheTwoExtremeZPoints.png", width = 384, height = 384) +opar <- par(mar = c(4, 4, 0, 0)) +plot(UV, asp = 1, pch = ".", xlab = "u", ylab = "v", axes = FALSE) +axis(1, at = seq(-3.5, 1.5, by = 0.5)) +axis(2, at = seq(-1.5, 2.5, by = 0.5)) +points(UV[c(v1,v2),], col = c("red", "green"), pch = 19, cex = 2) +par(opar) +dev.off() + + +# ARAP checkerboard #### +# U <- UV[, 1L] +# V <- UV[, 2L] +# Un <- (U - min(U)) / (max(U) - min(U)) +# Vn <- (V - min(V)) / (max(V) - min(V)) +# checkerboard <- ifelse( +# (floor(5 * U) %% 2) == (floor(5 * V) %% 2), +# "yellow", "navy" +# ) + +Un <- (Urot - min(Urot)) / (max(Urot) - min(Urot)) +Vn <- (Vrot - min(Vrot)) / (max(Vrot) - min(Vrot)) +checkerboard <- ifelse( # c'est ça le bon + (floor(5 * Un) %% 2) == (floor(5 * Vn) %% 2), + "yellow", "navy" +) + +png("Enneper_UVcheckerboard_ARAP.png", width = 384, height = 384) +opar <- par(mar = c(4, 4, 0, 0)) +plot( + U, V, asp = 1, pch = ".", xlab = "u", ylab = "v", + axes = FALSE, col = checkerboard +) +axis(1, at = seq(-3.5, 1.5, by = 0.5)) +axis(2, at = seq(-1.5, 2.5, by = 0.5)) +par(opar) +dev.off() + +# add normals, convert to 'rgl' mesh, and add colors #### +mesh$computeNormals() +rmesh <- mesh$getMesh() +rmesh[["material"]] <- list("color" = checkerboard) +b <- getBoundary3d(rmesh, sorted = TRUE, color = "black") + +# plot #### +library(rgl) +open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.8) +bg3d("#363940") +par3d(userMatrix = um) +shade3d(rmesh, meshColor = "vertices", polygon_offset = 1) +shade3d(b, lwd = 4) + +snapshot3d(sprintf("EnneperWithCheckerboard_ARAP.png"), webshot = FALSE) +saveRDS(um, "userMatrix.rds") + +# animation #### +M <- par3d("userMatrix") +movie3d( + par3dinterp( + time = seq(0, 1, len = 9), + userMatrix = list( + M, + rotate3d(M, pi, 1, 0, 0), + rotate3d(M, pi, 1, 1, 0), + rotate3d(M, pi, 1, 1, 1), + rotate3d(M, pi, 0, 1, 1), + rotate3d(M, pi, 0, 1, 0), + rotate3d(M, pi, 1, 0, 1), + rotate3d(M, pi, 0, 0, 1), + M + ) + ), + fps = 100, + duration = 1, + dir = ".", + movie = "zzpic", + convert = FALSE, webshot = FALSE +) + +# mount animation +library(gifski) +gifski( + png_files = Sys.glob("zzpic*.png"), + gif_file = "TennisBallWithCheckerboard_FourCorners.gif", + width = 512, + height = 512, + delay = 1/8 +) +file.remove(Sys.glob("zzpic*.png")) + + diff --git a/inst/essais/essai-param-fourCorners-Enneper.R b/inst/essais/essai-param-fourCorners-Enneper.R new file mode 100644 index 0000000..a08daf4 --- /dev/null +++ b/inst/essais/essai-param-fourCorners-Enneper.R @@ -0,0 +1,112 @@ +setwd("C:/SL/MyPackages/cgalMeshes/inst/trash") + +library(cgalMeshes) +library(rgl) + +n <- 2 +Enneper <- function(phi, r) { + rbind( + r*cos(phi) - r^(2*n-1)*cos((2*n-1)*phi)/(2*n-1), + r*sin(phi) + r^(2*n-1)*sin((2*n-1)*phi)/(2*n-1), + 2*r^n*cos(n*phi)/n + ) +} + +rmesh <- parametricMesh( + Enneper, urange = c(0, 2*pi), vrange = c(0, 1.2), + periodic = c(TRUE, FALSE), nu = 512L, nv = 128L, clean = TRUE +) + +shade3d(rmesh, col = "deeppink4") +bbox3d() + +# convert to CGAL mesh #### +mesh <- cgalMesh$new(rmesh) + +# take a look at the edge lengths +edges <- mesh$getEdges() +summary(edges[["length"]]) + +# add vertices in order that the checkeboard has regular lines #### +mesh$isotropicRemeshing(0.008, iterations = 3, relaxSteps = 2) + +# compute mesh parameterization #### +# first, find the four corners +vs <- mesh$getVertices() +vsx <- vs[, 1L] +oinc <- order(vsx) +odec <- order(vsx, decreasing = TRUE) +v1 <- oinc[1L] +v2 <- oinc[2L] +v3 <- odec[2L] +v4 <- odec[1L] + +open3d() +view3d(0, 0) +shade3d(rmesh) +points3d(rbind(vs[v1, ]), col = "red", size = 12) +points3d(rbind(vs[v2, ]), col = "green", size = 12) +points3d(rbind(vs[v3, ]), col = "blue", size = 12) +points3d(rbind(vs[v4, ]), col = "black", size = 12) + +UV <- mesh$parameterization(method = "DCP", corners = c(v1, v2, v3, v4)) + +# square checkerboard #### +checkerboard <- ifelse( + (floor(5 * UV[, 1L]) %% 2) == (floor(5 * UV[, 2L]) %% 2), + "yellow", "navy" +) + +# add normals, convert to 'rgl' mesh, and add colors #### +mesh$computeNormals() +rmesh <- mesh$getMesh() +rmesh[["material"]] <- list("color" = checkerboard) +b <- getBoundary3d(rmesh, sorted = TRUE, color = "black") + +# plot #### +library(rgl) +open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.8) +bg3d("#363940") +par3d(userMatrix = um) +shade3d(rmesh, meshColor = "vertices", polygon_offset = 1) +shade3d(b, lwd = 4) + +snapshot3d(sprintf("EnneperWithCheckerboard_FourCorners.png"), webshot = FALSE) +saveRDS(um, "userMatrix.rds") + +# animation #### +M <- par3d("userMatrix") +movie3d( + par3dinterp( + time = seq(0, 1, len = 9), + userMatrix = list( + M, + rotate3d(M, pi, 1, 0, 0), + rotate3d(M, pi, 1, 1, 0), + rotate3d(M, pi, 1, 1, 1), + rotate3d(M, pi, 0, 1, 1), + rotate3d(M, pi, 0, 1, 0), + rotate3d(M, pi, 1, 0, 1), + rotate3d(M, pi, 0, 0, 1), + M + ) + ), + fps = 100, + duration = 1, + dir = ".", + movie = "zzpic", + convert = FALSE, webshot = FALSE +) + +# mount animation +library(gifski) +gifski( + png_files = Sys.glob("zzpic*.png"), + gif_file = "TennisBallWithCheckerboard_FourCorners.gif", + width = 512, + height = 512, + delay = 1/8 +) +file.remove(Sys.glob("zzpic*.png")) + + diff --git a/inst/essais/essai_conformalTorus.R b/inst/essais/essai_conformalTorus.R index 350b0ce..e001b43 100644 --- a/inst/essais/essai_conformalTorus.R +++ b/inst/essais/essai_conformalTorus.R @@ -149,3 +149,61 @@ tube <- addNormals( shade3d(tube, color = "black") snapshot3d("Villarceau.png", webshot = FALSE) + + + +################################################################################ + +f <- function(beta, theta0, phi = pi/4) { + d <- (1 - sin(beta) * sin(phi)) + rbind( + cos(theta0 + beta) * cos(phi) / d, + sin(theta0 + beta) * cos(phi) / d, + cos(beta) * sin(phi) / d + ) +} + +mesh <- parametricMesh( + f, c(0, 2*pi), c(0, 2*pi), c(TRUE, TRUE), + nu = 256, nv = 256 +) + +f <- function(u, v) { # clifford + rbind( + cospi(u) / (sqrt(2) - sinpi(v)), + sinpi(u) / (sqrt(2) - sinpi(v)), + cospi(v) / (sqrt(2) - sinpi(v)) + ) +} + +N <- 256 + +mesh <- parametricMesh( + f, c(0, 2), c(0, 2), c(TRUE, TRUE), + nu = 256, nv = 256 +) + +x_ <- seq(0, 1, length.out = N) +UV <- as.matrix( + expand.grid(U = x_, V = x_) +) + +rotation <- function(alpha, uv) { + t(rbind( + c(cos(alpha), -sin(alpha)), + c(sin(alpha), cos(alpha)) + ) %*% t(uv)) +} + +UVrot <- rotation(pi/4, UV) + +K <- 4*sqrt(2) +rotatedCheckerboardColors <- ifelse( + (floor(K*UVrot[, 1L]) %% 2) == (floor(K*UVrot[, 2L]) %% 2), + "yellow", "navy" +) + +mesh$material <- list(color = rotatedCheckerboardColors) +open3d(windowRect = 50 + c(0, 0, 512, 512)) +view3d(-15, -25, zoom = 0.7) +shade3d(mesh, polygon_offset = 1) diff --git a/vignettes/parameterizations.Rmd b/vignettes/parameterizations.Rmd index ba724f3..87033c7 100644 --- a/vignettes/parameterizations.Rmd +++ b/vignettes/parameterizations.Rmd @@ -116,7 +116,7 @@ checkerboard: the lengths are not preserved, the angles are not preserved. A parameterization which preserves the angles is called a *conformal* parameterization. Let's see an example. I know a conformal parameterization -of a torus: +of the torus: ```{r conformalTorus} # the conformal torus mesh @@ -171,6 +171,7 @@ rotatedCheckerboardColors <- ifelse( ``` ```{r plotRotatedCheckerboard, eval=FALSE} +# plot the rotated checkerboard opar <- par(mar = c(0, 0, 0, 0)) plot( UV, col = rotatedCheckerboardColors, asp = 1, pch = ".", @@ -181,6 +182,8 @@ par(opar) ![](rotatedCheckerboard.png) +Now we assign the colors to the vertices of the torus mesh, and we plot. + ```{r} # assign the rotated checkerboard colors to the torus mesh TorusMesh[["material"]] <- list("color" = rotatedCheckerboardColors) @@ -193,7 +196,7 @@ view3d(0, -40, zoom = 0.7) shade3d(TorusMesh, meshColor = "vertices") ``` -![](conformalTorus.png) +![](conformalTorus.gif) The parameterization is conformal. It is not very obvious to see, but the right angles are not distorted @@ -202,7 +205,7 @@ In fact, the boundaries of the yellow and blue squares form the *Villarceau circles* of the torus, shown in black on the following picture, and which are known to meet at right angles. - +![](Villarceau.png)