Ricci flow

MikuHatsune2016-06-10

読んでる。Comput Vis Image Underst. 2013 Sep 1;117(9):1107-1118.
Ricci flow という、Willmore flow とはまた違う条件で微分幾何学的物体変換を行う。
その前に下準備。
 
以前やった(離散)ガウス曲率は、1-ring下の三角形の角度と、A_{mixed}と呼ばれる傘の面積で計算していたが、これはMeyer method というらしい。
本来は、ガウス曲率は角度だけで計算できて
K_i=2\pi-\sum_i\theta_i
となる。しかし、この場合は、論文の図にもあるけれども、理想的な球体のうえにランダムに点を発生させて三角形を作った時に、ガウス曲率の濃淡ができてしまう。というわけで、疎密に合わせてガウス曲率を補正したい。
というわけでA_{mixed}で割る、らしい。
 
半径r の理想的な球を作っているので、ガウス曲率は\frac{1}{r^2}=1である。
ほとんどが1の周辺に分布している。

 
1からかけ離れたガウス曲率をもつ頂点をプロットした。赤が高いガウス曲率、青が低い。
ズームしてみると、すごい鋭角な三角形をもつ頂点が、低いガウス曲率になっている様子。
計算はあっているようである。

 
理想的な球を作り、点を配置し、三角形を作る。

library(rgl)
library(onion)
my.sphere.tri.mesh <- function(n.psi=30){
  thetas <- list()
  psis <- seq(from=-pi/2,to=pi/2,length=n.psi)
  #psis <- sort(runif(n.psi, min=-pi/2, max=pi/2))
    d.psis <- psis[2]-psis[1]
    hs <- sin(psis)
    rs <- sqrt(1-hs^2)
    ls <- 2*pi*rs
    n.thetas <- floor(ls/d.psis)
    thetas[[1]] <- c(2*pi)
    for(i in 2:(n.psi-1)){
        thetas[[i]] <- seq(from=0,to=2*pi,length=n.thetas[i]+1)
        thetas[[i]] <- thetas[[i]][-(n.thetas[i]+1)]
    }
    thetas[[n.psi]] <- c(2*pi)
    sapply(thetas,length)

    bridge <- list()
    for(i in 1:(n.psi-1)){
        a <- c(thetas[[i]],2*pi)
        b <- c(thetas[[i+1]],2*pi)
        bridge[[i]] <- matrix(c(1,1),1,2)
        loop <- TRUE
        while(loop){
            n.r <- nrow(bridge[[i]])
            id.a <- bridge[[i]][n.r,1] + 1
            id.b <- bridge[[i]][n.r,2] + 1
            if(id.a > length(thetas[[i]]) & id.b > length(thetas[[i+1]])){
                if(id.a-1!=1 & id.b-1!=1){
                    bridge[[i]] <- rbind(bridge[[i]],c(1,id.b-1))
                }

                loop <- FALSE
            }else{
                if(id.a > length(thetas[[i]])){
                    tmp <- c(id.a-1,id.b)
                }else if(id.b > length(thetas[[i+1]])){
                    tmp <- c(id.a,id.b-1)
                }else{
                    if(a[id.a] < b[id.b]){
                        tmp <- c(id.a,id.b-1)
                    }else{
                        tmp <- c(id.a-1,id.b)
                    }
                }
                bridge[[i]] <- rbind(bridge[[i]],tmp)
            }
        }
    }
    xyz <- matrix(0,0,3)
    edge <- matrix(0,0,2)
    triangles <- matrix(0,0,3)
  n.triangles <- rep(0,n.psi-1)
    for(i in 1:n.psi){
        n.r <- nrow(xyz)
        if(i > 1){
            pre <- (n.r-length(thetas[[i-1]])+1):n.r
            post <- (n.r+1):(n.r+length(thetas[[i]]))
            edge <- rbind(edge,cbind(post,c(post[-1],post[1])))
            br <- bridge[[i-1]]
            new.edge <- cbind(pre[br[,1]],post[br[,2]])
            edge <- rbind(edge,new.edge)
            tmp.tri <- cbind(new.edge,rbind(new.edge[-1,],new.edge[1,]))
            tmp <- apply(tmp.tri,1,unique)
            triangles <- rbind(triangles,t(tmp))
      n.triangles[i-1] <- length(tmp[1,])
        }
        psi <- psis[i]
        theta <- thetas[[i]]
        xyz <- rbind(xyz,cbind(cos(psi) * cos(theta),cos(psi)*sin(theta),sin(psi)))

    }
    return(list(xyz=xyz,edge=edge,triangles=triangles,n.psi=n.psi,thetas=thetas,n.triangles=n.triangles))
}
knit_hooks$set(rgl = hook_rgl)
n.psi <- 25
sp.mesh <- my.sphere.tri.mesh(n.psi)
spxyz <- sp.mesh$xyz
apply(spxyz^2,1,sum)
xx <- 0.8
yy <- 0.1
large.xy1 <- which(spxyz[,1]>xx & spxyz[,2]<=yy)
large.xy2 <- which(spxyz[,1]>xx & spxyz[,2]>yy)

spxyz[large.xy1,1] <- xx + ((spxyz[large.xy1,1]-xx)*10)^2
spxyz[large.xy2,1] <- xx + ((spxyz[large.xy2,1]-xx)*10)^2
spxyz[large.xy2,2] <- yy + ((spxyz[large.xy2,1]-yy)*10)^5
plot3d(spxyz)
segments3d(spxyz[c(t(sp.mesh$edge)),])
mesh.tri <- tmesh3d(t(spxyz),t(sp.mesh$triangles),homogeneous=FALSE)

# 打点して
plot3d(sp.mesh$xyz)
# 三角形メッシュオブジェクトを作り
mesh.tri <- tmesh3d(t(sp.mesh$xyz),t(sp.mesh$triangles),homogeneous=FALSE)
# 三角形を灰色で塗る
shade3d(mesh.tri,col="gray")

四元数でごそごそしていたので、ガウス曲率の計算に四元数を入力する仕様になっている。

# cumpute Gaussian curvature of j_th vertex
# input: index of vertices of quanterion vertices object
gaussian_curvature <- function(j, vertices, faces.v){
  #for(j in seq(n_vertices)){
    plane <- t(faces.v[, apply(faces.v==j, 2, any)])
    # shuffle the order of summation of triangle areas
    p <- p1 <- 1 # start
    s <- c(j, setdiff(plane[p,], j)[1]) # 2nd vertex
    p1 <- c(p1, setdiff(which(apply(plane == tail(s, 1), 1, any)), p))
    while(p < nrow(plane) - 1){
      s <- c(s, setdiff(plane[tail(p1, 1),], s))
      p1 <- c(p1, which(sapply(apply(plane, 1, setdiff, s), length) == 1)[2])
      p <- p + 1
    }
    plane <- plane[p1,] # ordered sequentially

    circum_mat <- matrix(1, 3, 3)
    diag(circum_mat) <- -1
    theta_j <- rep(0, nrow(plane))
    circumcenter <- matrix(0, nrow(plane), 3)
    for(p in seq(nrow(plane))){
      hogemat <- t(as.matrix(vertices[plane[p,]])[-1,])
      rname <- plane[p,]
      e12 <- sweep(hogemat[!(rname %in% j),], 2, hogemat[ (rname %in% j),], "-")
      theta_j[p] <- acos( e12[1,] %*% e12[2,]/prod(sqrt(rowSums(e12^2))) )
      if(theta_j[p] < pi/2){ # non-obtuse angle compute circumcenter point
        abc <- mapply(function(k) sum(apply(hogemat[c(k%%3+1, (k+1)%%3+1),], 2, diff)^2), seq(3))
        # abc <- rowSums(rbind(apply(e12, 2, diff), e12)^2)
        abc2 <- circum_mat %*% abc * abc
        circumcenter[p,] <- colSums(diag(c(abc2)) %*% hogemat)/sum(abc2)
        # circumcenter <- (abc2[1]*hogemat[1,] + abc2[2]*hogemat[2,] + abc2[3]*hogemat[3,])/sum(abc2)
      } else { # obtuse angle
        circumcenter[p,] <- colSums(hogemat[!(rname %in% j),])/2 # center point
      }
    }
    mat <- sweep(circumcenter, 2, as.numeric(vertices[j])[-1], "-") # vector from vertex j_th
    n <- nrow(circumcenter)
    Amixed <- mapply(function(k) sqrt(prod(rowSums(mat[c(k, k%%n+1),]^2)) - (mat[k,]%*%mat[k%%n+1,])^2) / 2, seq(n))
    Gcurvature <- (2*pi - sum(theta_j))/sum(Amixed, na.rm=TRUE)
  #}
  return(Gcurvature)
}

# すべての頂点について計算
b <- mapply(function(j) gaussian_curvature(j, vertices, faces.v), seq(length(vertices)))
cut0 <- cut(b, quantile(b, c(0, 0.02, 0.98, 1)))
cols <- bluered(length(levels(cut0)))[cut0]

plot3d(mesh.tri, type="dits", axes=FALSE, xlab="", ylab="", zlab="")
shade3d(mesh.tri, col="grey")
wire3d(mesh.tri)
spheres3d(t(mesh.tri$vb), col=cols, radius=0.02)