Least Square Conformal Mapping
- 3次元空間に埋め込まれた曲面を三角形分割し、曲面の周囲に2次元座標を与え、曲面の非周辺点に共形変換に近くなるように2次元座標を与える方法。「共形変換に近い」かどうかの基準は最小二乗法を採用し、その解を求めるにあたって線形代数的に一意に最適化する
- 2次元平面と共形変換
- まずは、平面に格子を作って描く。
# xy二次元平面のグリッド点を作る x1 <- y1 <- seq(from=-10,to=10,length=31) X <- as.matrix(expand.grid(x1,y1)) #X <- X %*% matrix(c(cos(pi/6),sin(pi/6),-sin(pi/6),cos(pi/6)),2,2) # 点の数 n.v <- length(X[,1]) # 格子のエッジリストを作る distX <- as.matrix(dist(X,method="manhattan")) distX <- abs(distX-abs(x1[1]-x1[2])) < 0.01 lattice.edge <- which(distX,arr.ind=TRUE) # 正方形周囲の点と内部の点とを分ける v.fix <- which(apply((X-10)*(X+10),1,prod)==0) v.free <- which(apply((X-10)*(X+10),1,prod)!=0) col <- rep(1,n.v) col[v.fix] <- 2 plot(X,pch=20,col=col) segments(X[lattice.edge[,1],1],X[lattice.edge[,1],2],X[lattice.edge[,2],1],X[lattice.edge[,2],2])
- 今、この平面をぐにゃりと曲げる。曲げるにあたって、複素関数を使う。
- 二次元平面の点$(x,y)$を$w=x + i y$と見立て、$f(w)$と言う複素関数の実部と虚部とを変形後の$(x,y)座標とすることにする。
$f(w)=(w+p)^k$という関数にしてみよう。
p <- 12+1i*7;k <- 0.4 w <- X[,1] + 1i*X[,2] new.w <- (w+p)^k #new.w <- w new.X <- cbind(Re(new.w),Im(new.w)) plot(new.X,pch=20,col=col) segments(new.X[lattice.edge[,1],1],new.X[lattice.edge[,1],2],new.X[lattice.edge[,2],1],new.X[lattice.edge[,2],2])
- この変形の特徴は、各所で角度が保存されていることであり、共形変換を角度保存の変換と呼ぶ。
- 3次元空間に埋め込まれた曲面
- 上の例では、2次元平面内で複素数を用いて格子をぐにゃりと曲げたが、二次元平面内に収まっている。これに3次元座標も与えてやると、3次元空間内の曲面ができる。
p <- 12+1i*7;k <- 0.4 w <- X[,1] + 1i*X[,2] new.w <- (w+p)^k #new.w <- w new.z <- 0.4*exp(-0.02*Mod(w)^2) new.z <- 0.4*exp(-0.1*(X[,1]^2+X[,2]^2)) new.X <- cbind(Re(new.w),Im(new.w),new.z) plot3d(new.X,col=col) segments3d(new.X[c(t(lattice.edge)),])
- 三角化
- 格子は座標系の変形を視覚化するのに有用だが、局面データの離散的な取扱いのためには、三角形メッシュにするのが好都合である。
- Rではgeometryパッケージを使うと三角化が可能である。
library(geometry) tc <- delaunayn(X[,1:2]) n.t <- length(tc[,1]) # 三角形の個数 plot(new.X,col=col,pch=20) segments(new.X[tc[,1],1],new.X[tc[,1],2],new.X[tc[,2],1],new.X[tc[,2],2]) segments(new.X[tc[,2],1],new.X[tc[,2],2],new.X[tc[,3],1],new.X[tc[,3],2]) segments(new.X[tc[,3],1],new.X[tc[,3],2],new.X[tc[,1],1],new.X[tc[,1],2]) plot3d(new.X,col=col) triangles3d(new.X[c(t(tc)),],col=rep(2,n.t))
- 三角形の面積とベクトルのクロス積と面の向き
- 3次元空間に三角形があり、3頂点座標が$(x_1,y_1,z_1),(x_2,y_2,z_2),(x_3,y_3,z_3)$であるとする。
- 三角形の頂点の並べ方を$(v1,v2,v3)$とみるとき、頂点v1を基点として、$v2-v1$ベクトルと$v3-v1$ベクトルのクロス積を計算すると、その絶対値は三角形の面積の2倍(2つのベクトルが張る平行四辺形の面積)となり、その符号は2ベクトルの外積ベクトルが2ベクトルと右手系か左手系の関係になるかを示す。したがって、以下に示すように、$(v1,v2,v3)$の順で考える場合と、$(v1,v3,v2)$で考える場合とで、クロス積の値は符号が反転する。
v1 <- c(0,0,0);v2 <- c(2,0,0);v3 <- c(1,2,3) v21 <- v2 - v1 v31 <- v3 - v2 # (v1,v2,v3)順 tmp <- extprod3d(v21,v31) A1 <- sum(tmp) # (v1,v3,v2)順 tmp <- extprod3d(v31,v21) A2 <- sum(tmp) print(A1) print(A2)
- 4元数の虚部を用いて3次元ベクトルを表し、同様に符号付き面積を計算することもできる。
- 4元数に慣れる意味で、その計算も行っておく。
library(onion) H <- c(Hi,Hj,Hk) # 4元数虚部基底 v1q <- sum(v1*H);v2q <- sum(v2*H);v3q <- sum(v3*H) v21q <- v2q - v1q v31q <- v3q - v2q # (v1,v2,v3)順 tmp <- v21q * v31q A1q <- i(tmp) + j(tmp) + k(tmp) # (v1,v3,v2)順 tmp <- v31q * v21q A2q <- i(tmp) + j(tmp) + k(tmp) print(A1q) print(A2q)
- すべての三角形について符号付き面積を計算してみる。
my.area.tri <- function(vs){ v1 <- vs[1,];v2 <- vs[2,];v3 <- vs[3,] v21 <- v2 - v1 v31 <- v3 - v1 tmp <- sum(extprod3d(v21,v31)) sum(tmp) }
- 符号付き面積の符号で三角形を塗り分けてみると、以下のように、まだらになる。
As <- rep(0,n.t) for(i in 1:n.t){ As[i] <- my.area.tri(new.X[tc[i,],]) } plot3d(new.X) col <- rep(2.5,n.t) + sign(As)*0.5 triangles3d(new.X[c(t(tc)),],col=col)
- すべての三角形の向きをそろえたいので、この符号付き面積の値をもとに、裏向きの三角形の頂点順序を変えることにする。変えたあとで、面積を計算しなおそう。
ura.tri <- which(As<0) tc[ura.tri,2:3] <- tc[ura.tri,c(3,2)] As <- rep(0,n.t) for(i in 1:n.t){ As[i] <- my.area.tri(new.X[tc[i,],]) } plot3d(new.X) col <- rep(2.5,n.t) + sign(As)*0.5 triangles3d(new.X[c(t(tc)),],col=col)
- 最小二乗法による法線ベクトル推定と共形変換推定 その1
- 複素数行列を作る
- サイズは(2x面の数)x(2x点の数)とすることで、実行列として作る
- 面と点とのペアごとに2x2複素行列を取り、それを配置したもの。
my.orthobasis <- function(q){ q21 <- q[2]-q[1] q31 <- q[3]-q[1] v1 <- q21 v3 <- Im(v1*q31) v2 <- Im(v3*v1) c(v1/Mod(v1),v2/Mod(v2),v3/Mod(v3)) } v1 <- c(0,0,0);v2 <- c(2,0,0);v3 <- c(1,2,3) H <- c(Hi,Hj,Hk) # 4元数虚部基底 v1q <- sum(v1*H);v2q <- sum(v2*H);v3q <- sum(v3*H) q <- c(v1q,v2q,v3q) q.basis <- my.orthobasis(q) q.basis t(q.basis) %*% q.basis # 正規直交基底であることの確認
# 頂点座標の四元数表現 Xq <- new.X[,1]*Hi + new.X[,2]*Hj + new.X[,3]*Hk basis.tri <- list() for(i in 1:n.t){ s <- tc[i,] q <- Xq[s] basis.tri[[i]] <- my.orthobasis(q) }
- 疎行列を使う
library(Matrix) M <- Matrix(0,2*n.t,2*n.v) for(i in 1:n.t){ s <- tc[i,] b <- basis.tri[[i]] A <- As[i] v32 <- Xq[s[3]]-Xq[s[2]] v13 <- Xq[s[1]]-Xq[s[3]] v21 <- Xq[s[2]]-Xq[s[1]] tmp1 <- Re(v32*b[1]) tmp2 <- Re(v32*b[2]) M[(1+(i-1)*2):(i*2),(1+(s[1]-1)*2):(s[1]*2)] <- c(tmp1,tmp2,-tmp2,tmp1)/sqrt(A) tmp1 <- Re(v13*b[1]) tmp2 <- Re(v13*b[2]) M[(1+(i-1)*2):(i*2),(1+(s[2]-1)*2):(s[2]*2)] <- c(tmp1,tmp2,-tmp2,tmp1)/sqrt(A) tmp1 <- Re(v21*b[1]) tmp2 <- Re(v21*b[2]) M[(1+(i-1)*2):(i*2),(1+(s[3]-1)*2):(s[3]*2)] <- c(tmp1,tmp2,-tmp2,tmp1)/sqrt(A) } col.fix <- sort(c(1+(v.fix-1)*2,v.fix*2)) col.free <- sort(c(1+(v.free-1)*2,v.free*2)) M.fix <- M[,col.fix] M.free <- M[,col.free] b.fix <- Matrix(c(t(new.X[v.fix,1:2])),ncol=1) b.free <- solve(t(M.free)%*%M.free)%*%t(M.free)%*%(-M.fix%*%b.fix)
- 2次元座標を作る
X.c <- new.X[,1:2] X.c[v.free,] <- matrix(b.free,byrow=TRUE,ncol=2) ans <- X.c col2 <- rep(1,n.v) col2[v.fix] <- 2 plot(ans,pch=20,col=col2) segments(ans[lattice.edge[,1],1],ans[lattice.edge[,1],2],ans[lattice.edge[,2],1],ans[lattice.edge[,2],2]) col3 <- apply(ans%/%0.5,1,sum)%%2 +1 plot3d(new.X,col=col3,size=10,pch=15)