gptoolbox\mesh\arap.m
先以一个三角形为例
“arap.m”
###Covariance Matrix
data.CSM = covariance_scatter_matrix(ref_V,ref_F,‘Energy’,energy);
In function covariance_scatter_matrix, it constructed a covarience scatter matrix :
C S M = ( k x 0 0 0 k y 0 0 0 k z ) T CSM =\begin{pmatrix}kx&0&0\\0&ky&0\\0&0&kz\end{pmatrix}^T CSM=⎝⎛kx000ky000kz⎠⎞T, in which k x kx kx is a block matrix build by arap_linear_block looks like:
k x = ( w 12 ( x 1 − x 2 ) + w 13 ( x 1 − x 3 ) w 12 ( x 1 − x 2 ) w 13 ( x 1 − x 3 ) w 21 ( x 2 − x 1 ) w 21 ( x 2 − x 1 ) + w 23 ( x 2 − x 3 ) w 23 ( x 2 − x 3 ) ) w 31 ( x 3 − x 1 ) w 32 ( x 3 − x 2 ) w 31 ( x 3 − x 1 ) + w 32 ( x 3 − x 2 ) ) kx = \begin{pmatrix}w_{12}(x_1 - x_2)+w_{13}(x_1 - x_3)&w_{12}(x_1 - x_2)&w_{13}(x_1 - x_3)\\w_{21}(x_2 - x_1)&w_{21}(x_2 - x_1)+w_{23}(x_2 - x_3)&w_{23}(x_2 - x_3)) \\w_{31}(x_3-x_1)&w_{32}(x_3-x_2)&w_{31}(x_3-x_1)+w_{32}(x_3-x_2)\end{pmatrix} kx=⎝⎛w12(x1−x2)+w13(x1−x3)w21(x2−x1)w31(x3−x1)w12(x1−x2)w21(x2−x1)+w23(x2−x3)w32(x3−x2)w13(x1−x3)w23(x2−x3))w31(x3−x1)+w32(x3−x2)⎠⎞
For internal two dimension, the column dimension is related to the each vertex in the mesh while the row dimension is about the neighbors of each vertex. Then the covarience scatter matrix is constructed by k x , k y , k z kx, ky, kz kx,ky,kz on its diagonal which runs over x , y , z x,y,z x,y,z coordinates respectively. The transport is to negative the non-diagonal element. Therefore,
k x T U x = ( w 12 ( x 1 − x 2 ) ( x 1 ′ − x 2 ′ ) + w 13 ( x 1 − x 3 ) ( x 1 ′ − x 3 ′ ) w 21 ( x 2 − x 1 ) ( x 2 ′ − x 1 ′ ) + w 23 ( x 2 − x 3 ) ( x 2 ′ − x 3 ′ ) w 31 ( x 3 − x 1 ) ( x 3 ′ − x 1 ′ ) + w 32 ( x 3 − x 2 ) ( x 3 ′ − x 2 ′ ) ) kx^TU_x = \begin{pmatrix}w_{12}(x_1 - x_2)(x'_1 - x'_2)+w_{13}(x_1 - x_3)(x'_1 - x'_3)\\w_{21}(x_2 - x_1)(x'_2 - x'_1)+w_{23}(x_2 - x_3)(x'_2 - x'_3)\\w_{31}(x_3-x_1)(x'_3-x'_1)+w_{32}(x_3-x_2)(x'_3-x'_2)\end{pmatrix} kxTUx=⎝⎛w12(x1−x2)(x1′−x2′)+w13(x1−x3)(x1′−x3′)w21(x2−x1)(x2′−x1′)+w23(x2−x3)(x2′−x3′)w31(x3−x1)(x3′−x1′)+w32(x3−x2)(x3′−x2′)⎠⎞
k x T U y = ( w 12 ( x 1 − x 2 ) ( y 1 ′ − y 2 ′ ) + w 13 ( x 1 − x 3 ) ( y 1 ′ − y 3 ′ ) w 21 ( x 2 − x 1 ) ( y 2 ′ − y 1 ′ ) + w 23 ( x 2 − x 3 ) ( y 2 ′ − y 3 ′ ) w 31 ( x 3 − x 1 ) ( y 3 ′ − y 1 ′ ) + w 32 ( x 3 − x 2 ) ( y 3 ′ − y 2 ′ ) ) kx^TU_y = \begin{pmatrix}w_{12}(x_1 - x_2)(y'_1 - y'_2)+w_{13}(x_1 - x_3)(y'_1 - y'_3)\\w_{21}(x_2 - x_1)(y'_2 - y'_1)+w_{23}(x_2 - x_3)(y'_2 - y'_3)\\w_{31}(x_3-x_1)(y'_3-y'_1)+w_{32}(x_3-x_2)(y'_3-y'_2)\end{pmatrix} kxTUy=⎝⎛w12(x1−x2)(y1′−y2′)+w13(x1−x3)(y1′−y3′)w21(x2−x1)(y2′−y1′)+w23(x2−x3)(y2′−y3′)w31(x3−x1)(y3′−y1′)+w32(x3−x2)(y3′−y2′)⎠⎞
k x T U z = ( w 12 ( x 1 − x 2 ) ( z 1 ′ − z 2 ′ ) + w 13 ( x 1 − x 3 ) ( z 1 ′ − z 3 ′ ) w 21 ( x 2 − x 1 ) ( z 2 ′ − z 1 ′ ) + w 23 ( x 2 − x 3 ) ( z 2 ′ − z 3 ′ ) w 31 ( x 3 − x 1 ) ( z 3 ′ − z 1 ′ ) + w 32 ( x 3 − x 2 ) ( z 3 ′ − z 2 ′ ) ) kx^TU_z = \begin{pmatrix}w_{12}(x_1 - x_2)(z'_1 - z'_2)+w_{13}(x_1 - x_3)(z'_1 - z'_3)\\w_{21}(x_2 - x_1)(z'_2 - z'_1)+w_{23}(x_2 - x_3)(z'_2 - z'_3)\\w_{31}(x_3-x_1)(z'_3-z'_1)+w_{32}(x_3-x_2)(z'_3-z'_2)\end{pmatrix} kxTUz=⎝⎛w12(x1−x2)(z1′−z2′)+w13(x1−x3)(z1′−z3′)w21(x2−x1)(z2′−z1′)+w23(x2−x3)(z2′−z3′)w31(x3−x1)(z3′−z1′)+w32(x3−x2)(z3′−z2′)⎠⎞
k y T U x = ( w 12 ( y 1 − y 2 ) ( x 1 ′ − x 2 ′ ) + w 13 ( y 1 − y 3 ) ( x 1 ′ − x 3 ′ ) w 21 ( y 2 − y 1 ) ( x 2 ′ − x 1 ′ ) + w 23 ( y 2 − y 3 ) ( x 2 ′ − x 3 ′ ) w 31 ( y 3 − y 1 ) ( x 3 ′ − x 1 ′ ) + w 32 ( y 3 − y 2 ) ( x 3 ′ − x 2 ′ ) ) ky^TU_x = \begin{pmatrix}w_{12}(y_1 - y_2)(x'_1 - x'_2)+w_{13}(y_1 - y_3)(x'_1 - x'_3)\\w_{21}(y_2 - y_1)(x'_2 - x'_1)+w_{23}(y_2 - y_3)(x'_2 - x'_3)\\w_{31}(y_3-y_1)(x'_3-x'_1)+w_{32}(y_3-y_2)(x'_3-x'_2)\end{pmatrix} kyTUx=⎝⎛w12(y1−y2)(x1′−x2′)+w13(y1−y3)(x1′−x3′)w21(y2−y1)(x2′−x1′)+w23(y2−y3)(x2′−x3′)w31(y3−y1)(x3′−x1′)+w32(y3−y2)(x3′−x2′)⎠⎞
k y T U y = ( w 12 ( y 1 − y 2 ) ( y 1 ′ − y 2 ′ ) + w 13 ( y 1 − y 3 ) ( y 1 ′ − y 3 ′ ) w 21 ( y 2 − y 1 ) ( y 2 ′ − y 1 ′ ) + w 23 ( y 2 − y 3 ) ( y 2 ′ − y 3 ′ ) w 31 ( y 3 − y 1 ) ( y 3 ′ − y 1 ′ ) + w 32 ( y 3 − y 2 ) ( y 3 ′ − y 2 ′ ) ) ky^TU_y = \begin{pmatrix}w_{12}(y_1 - y_2)(y'_1 - y'_2)+w_{13}(y_1 - y_3)(y'_1 - y'_3)\\w_{21}(y_2 - y_1)(y'_2 - y'_1)+w_{23}(y_2 - y_3)(y'_2 - y'_3)\\w_{31}(y_3-y_1)(y'_3-y'_1)+w_{32}(y_3-y_2)(y'_3-y'_2)\end{pmatrix} kyTUy=⎝⎛w12(y1−y2)(y1′−y2′)+w13(y1−y3)(y1′−y3′)w21(y2−y1)(y2′−y1′)+w23(y2−y3)(y2′−y3′)w31(y3−y1)(y3′−y1′)+w32(y3−y2)(y3′−y2′)⎠⎞
k y T U z = ( w 12 ( y 1 − y 2 ) ( z 1 ′ − z 2 ′ ) + w 13 ( y 1 − y 3 ) ( z 1 ′ − z 3 ′ ) w 21 ( y 2 − y 1 ) ( z 2 ′ − z 1 ′ ) + w 23 ( y 2 − y 3 ) ( z 2 ′ − z 3 ′ ) w 31 ( y 3 − y 1 ) ( z 3 ′ − z 1 ′ ) + w 32 ( y 3 − y 2 ) ( z 3 ′ − z 2 ′ ) ) ky^TU_z = \begin{pmatrix}w_{12}(y_1 - y_2)(z'_1 - z'_2)+w_{13}(y_1 - y_3)(z'_1 - z'_3)\\w_{21}(y_2 - y_1)(z'_2 - z'_1)+w_{23}(y_2 - y_3)(z'_2 - z'_3)\\w_{31}(y_3-y_1)(z'_3-z'_1)+w_{32}(y_3-y_2)(z'_3-z'_2)\end{pmatrix} kyTUz=⎝⎛w12(y1−y2)(z1′−z2′)+w13(y1−y3)(z1′−z3′)w21(y2−y1)(z2′−z1′)+w23(y2−y3)(z2′−z3′)w31(y3−y1)(z3′−z1′)+w32(y3−y2)(z3′−z2′)⎠⎞
k z T U x = ( w 12 ( z 1 − z 2 ) ( x 1 ′ − x 2 ′ ) + w 13 ( z 1 − z 3 ) ( x 1 ′ − x 3 ′ ) w 21 ( z 2 − z 1 ) ( x 2 ′ − x 1 ′ ) + w 23 ( z 2 − z 3 ) ( x 2 ′ − x 3 ′ ) w 31 ( z 3 − z 1 ) ( x 3 ′ − x 1 ′ ) + w 32 ( z 3 − z 2 ) ( x 3 ′ − x 2 ′ ) ) kz^TU_x = \begin{pmatrix}w_{12}(z_1 - z_2)(x'_1 - x'_2)+w_{13}(z_1 - z_3)(x'_1 - x'_3)\\w_{21}(z_2 - z_1)(x'_2 - x'_1)+w_{23}(z_2 - z_3)(x'_2 - x'_3)\\w_{31}(z_3-z_1)(x'_3-x'_1)+w_{32}(z_3-z_2)(x'_3-x'_2)\end{pmatrix} kzTUx=⎝⎛w12(z1−z2)(x1′−x2′)+w13(z1−z3)(x1′−x3′)w21(z2−z1)(x2′−x1′)+w23(z2−z3)(x2′−x3′)w31(z3−z1)(x3′−x1′)+w32(z3−z2)(x3′−x2′)⎠⎞
k z T U y = ( w 12 ( z 1 − z 2 ) ( y 1 ′ − y 2 ′ ) + w 13 ( z 1 − z 3 ) ( y 1 ′ − y 3 ′ ) w 21 ( z 2 − z 1 ) ( y 2 ′ − y 1 ′ ) + w 23 ( z 2 − z 3 ) ( y 2 ′ − y 3 ′ ) w 31 ( z 3 − z 1 ) ( y 3 ′ − y 1 ′ ) + w 32 ( z 3 − z 2 ) ( y 3 ′ − y 2 ′ ) ) kz^TU_y = \begin{pmatrix}w_{12}(z_1 - z_2)(y'_1 - y'_2)+w_{13}(z_1 - z_3)(y'_1 - y'_3)\\w_{21}(z_2 - z_1)(y'_2 - y'_1)+w_{23}(z_2 - z_3)(y'_2 - y'_3)\\w_{31}(z_3-z_1)(y'_3-y'_1)+w_{32}(z_3-z_2)(y'_3-y'_2)\end{pmatrix} kzTUy=⎝⎛w12(z1−z2)(y1′−y2′)+w13(z1−z3)(y1′−y3′)w21(z2−z1)(y2′−y1′)+w23(z2−z3)(y2′−y3′)w31(z3−z1)(y3′−y1′)+w32(z3−z2)(y3′−y2′)⎠⎞
k z T U z = ( w 12 ( z 1 − z 2 ) ( z 1 ′ − z 2 ′ ) + w 13 ( z 1 − z 3 ) ( z 1 ′ − z 3 ′ ) w 21 ( z 2 − z 1 ) ( z 2 ′ − z 1 ′ ) + w 23 ( z 2 − z 3 ) ( z 2 ′ − z 3 ′ ) w 31 ( z 3 − z 1 ) ( z 3 ′ − z 1 ′ ) + w 32 ( z 3 − z 2 ) ( z 3 ′ − z 2 ′ ) ) kz^TU_z = \begin{pmatrix}w_{12}(z_1 - z_2)(z'_1 - z'_2)+w_{13}(z_1 - z_3)(z'_1 - z'_3)\\w_{21}(z_2 - z_1)(z'_2 - z'_1)+w_{23}(z_2 - z_3)(z'_2 - z'_3)\\w_{31}(z_3-z_1)(z'_3-z'_1)+w_{32}(z_3-z_2)(z'_3-z'_2)\end{pmatrix} kzTUz=⎝⎛w12(z1−z2)(z1′−z2′)+w13(z1−z3)(z1′−z3′)w21(z2−z1)(z2′−z1′)+w23(z2−z3)(z2′−z3′)w31(z3−z1)(z3′−z1′)+w32(z3−z2)(z3′−z2′)⎠⎞
C S M ∗ U = ( k x T U x k x T U y k x T U z k y T U x k y T U y k y T U z k z T U x k z T U y k z T U z ) CSM*U = \begin{pmatrix}kx^TU_x&kx^TU_y&kx^TU_z\\ky^TU_x&ky^TU_y&ky^TU_z\\kz^TU_x&kz^TU_y&kz^TU_z \end{pmatrix} CSM∗U=⎝⎛kxTUxkyTUxkzTUxkxTUykyTUykzTUykxTUzkyTUzkzTUz⎠⎞
Therefore, the first dimension of C S M ∗ U CSM*U CSM∗U goes through each vertex(直接下去挨着的3个). Regarding all the vertex as a group, the second dimension iterates x , y , z x,y,z x,y,z of e i j e_{ij} eij and the third dimension traverses the x , y , z x,y,z x,y,z of e i j ′ e'_{ij} eij′, which are the column and row dimension of covariance matrix S i S_i Si
% compute covariance matrix elements
S = zeros(size(data.CSM,1),dim);
S(:,1:dim) = data.CSM*repmat(U,dim,1);
% dim by dim by n list of covariance matrices
SS = permute(reshape(S,[size(data.CSM,1)/dim dim dim]),[2 3 1]);
Hence, using permute function , we change the dimension going through the vertex to the last so that covariance matrix are obtained for each vertex.
###Right Hand Side
Then let us look at the right hand side of formula (8)
% precompute rhs premultiplier
if ~isfield(data,'K') || isempty(data.K)
[~,data.K] = arap_rhs(ref_V,ref_F,[],'Energy',energy);
%if flat
%data.K = repdiag(ref_map,dim) * data.K;
%end
end
%B = arap_rhs(V,F,R);
Rcol = reshape(permute(R,[3 1 2]),size(data.K,2),1);
Bcol = data.K * Rcol;
B = reshape(Bcol,[size(Bcol,1)/dim dim]);
In function arap_rhs, it constructs a matrix K,
% K #V*dim by #(F|V)dimdim matrix such that:
% b = K * reshape(permute(R,[3 1 2]),size(V,1)*size(V,2)*size(V,2),1);
At first, matrix K K K is composed of K X , K Y , K Z KX, KY, KZ KX,KY,KZ, it is denoted as
K = ( K X 0 0 K Y 0 0 K Z 0 0 0 K X 0 0 K Y 0 0 K Z 0 0 0 K X 0 0 K Y 0 0 K Z ) K = \begin{pmatrix}KX&0&0&KY&0&0&KZ&0&0\\0&KX&0&0&KY&0&0&KZ&0\\0&0&KX&0&0&KY&0&0&KZ& \end{pmatrix} K=⎝⎛KX000KX000KXKY000KY000KYKZ000KZ000KZ⎠⎞
For better explaining, we introduce an equation:
( X 0 0 Y 0 0 Z 0 0 0 X 0 0 Y 0 0 Z 0 0 0 X 0 0 Y 0 0 Z ) ( 1 2 3 4 5 6 7 8 9 ) = ( 1 4 7 2 5 8 3 6 9 ) ( X Y Z ) \begin{pmatrix}X&0&0&Y&0&0&Z&0&0\\0&X&0&0&Y&0&0&Z&0\\0&0&X&0&0&Y&0&0&Z& \end{pmatrix}\begin{pmatrix}1\\2\\3\\4\\5\\6\\7\\8\\9 \end{pmatrix} = \begin{pmatrix}1&4&7\\2&5&8\\3&6&9 \end{pmatrix}\begin{pmatrix}X\\Y\\Z \end{pmatrix} ⎝⎛X000X000XY000Y000YZ000Z000Z⎠⎞⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎛123456789⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎞=⎝⎛123456789⎠⎞⎝⎛XYZ⎠⎞
Looking at the left hand side and substituting X X X with K X KX KX, the column(vertical) dimension goes through vertices in mesh, the row(horizotal) dimension travels through vertices for finding out neighbors of each vertex. For 1 1 1 in the column vector, we can extend it vertically to make the rotation elements going through each vertex.
consequently, the result is a column vector, the first dimension goes though vertices(组内), the second dimension goes through x , y , z x,y,z x,y,z coordinates(组间).
B = reshape(Bcol,[size(Bcol,1)/dim dim]);
This line enable the first dimension to go through vertices and the second dimension to go through x , y , z x,y,z x,y,z coordinates.
###Minimization process
[U,data.preF] = min_quad_with_fixed( ...
-0.5*data.L+DQ+alpha_tik*speye(size(data.L)), ...
-B+Dl,b,bc,Aeq,Beq,data.preF);
searching upwards, we find
data.L = cotmatrix(V,F);
The details of cotmatrix can be found at http://blog.csdn.net/seamanj/article/details/53774501
DQ = sparse(size(V,1),size(V,1));
Dl = sparse(size(V,1),dim);
% Tikhonov regularization alpha
alpha_tik = 0;
Next, we solve the formula (9)
In code it uses minimizing quadratic energy method to solve the equation. Integrating both sides with respect to unknown Z Z Z, the object function is to minimize
1 2 Z T L Z − Z T B \frac{1}{2}Z^TLZ-Z^TB 21ZTLZ−ZTB
Note that there is a minus symbol before 0.5, that’s because the cotmatrix has negative enties on diagonal elements, which is the negative of laplacian matrix.
最后看下这个函数 arap_linear_block
这个函数充当两个角色
即可以算CSM 也可以算K,K用于线性化R, 而CSM是用来算covariance scatter matrix
K中block的转置就是CSM中的block, 为了满足CSM, 矩阵按行相加应该得到一个0行向量, 而要满足K只需要列中对应的部分相同, 则就可以让R相加
主要讲下spokes 和 spokes-and-rims 两种方法的区别
为了让block即满足CSM又要满足K, 我们将每个三角形的权值拆开, 每条边正反各迭代一次
算右边:
spokes中每条边只受两个旋转矩阵的影响, 即两个端点处旋转的影响
而spokes-and-rims 每 条边受以三个旋转矩阵的影响 .
算R:
spokes中算 R 1 R_1 R1 ,对于每个三角形只需要算 w 12 e 12 e ′ 12 T + w 13 e 13 e ′ 13 T w_{12}e_{12}{e'}^T_{12}+w_{13}e_{13}{e'}^T_{13} w12e12e′12T+w13e13e′13T,权值拆开后就是 cot 3 e 12 e ′ 12 T + cot 2 e 13 e ′ 13 T \cot3e_{12}{e'}^T_{12}+\cot2e_{13}{e'}^T_{13} cot3e12e′12T+cot2e13e′13T,
updated on 6/8/2019: 注意 K X KX KX 与 R R R相乘时, R的linear的排列顺序为 先点的维数, 然后再R的维数, ( R 1 ⋯ n ) 1 ( R 1 ⋯ n ) 2 ⋯ ( R 1 ⋯ n ) 9 (R_{1\cdots n})_1 (R_{1\cdots n})_2 \cdots (R_{1\cdots n})_9 (R1⋯n)1(R1⋯n)2⋯(R1⋯n)9
拿R的第一维为例
K X × ( R 1 ⋯ n ) 1 KX \times (R_{1 \cdots n})_1 KX×(R1⋯n)1, 得到的是 n × 1 n \times 1 n×1的向量, 第 r r r行对应的是与点 v r v_r vr相关的边的旋转
而spokes-and-rims中算 R 1 R_1 R1,对于每个三角形需要算 w 12 e 12 e ′ 12 T + w 13 e 13 e ′ 13 T + w 23 e 23 e ′ 23 T w_{12}e_{12}{e'}^T_{12}+w_{13}e_{13}{e'}^T_{13}+w_{23}e_{23}{e'}^T_{23} w12e12e′12T+w13e13e′13T+w23e23e′23T,权值拆开后就是 cot 3 e 12 e ′ 12 T + cot 2 e 13 e ′ 13 T + cot 1 e 23 e ′ 23 T \cot3e_{12}{e'}^T_{12}+\cot2e_{13}{e'}^T_{13}+\cot1e_{23}{e'}^T_{23} cot3e12e′12T+cot2e13e′13T+cot1e23e′23T
function K = spokes_linear_block(V,F,d)
% % Computes a matrix K such that K * R computes
% % 鈭?wij * 0.5 * (V(i,d)-V(j,d)) * (Ri + Rj)
% % j鈭圢(i)
% %
% % Inputs:
% % V #V by dim list of coordinates
% % F #F by 3 list of triangle indices into V
% % d index into columns of V
% % Output:
% % K #V by #V matrix
% %
% E = edges(F);
% % Build upper part of adjacency matrix where instead of a 1 for edge from i
% % to j we have the difference of position in dimension d
% A = sparse(E(:,1),E(:,2),V(E(:,1),d)-V(E(:,2),d),size(V,1),size(V,1));
% % anti-symmetric, or considers direction of edges
% A = A-A';
% % Multiply with cotangent weights (don't worry about diagonal begin wrong
% % since in A 0it's all zeros
% L = cotmatrix(V,F);
% K = L.*A;
% % correct the diagonal (notice that the sign is positive
% K = K + diag(sum(K,2));
% K = 0.5*K;%权值在cotmatrix.m已除2,这里除2是匹配8式的0.5
% triangles
C = cotangent(V,F);
i1 = F(:,1); i2 = F(:,2); i3 = F(:,3);
I = [i1;i2;i2;i3;i3;i1;i1;i2;i3];
J = [i2;i1;i3;i2;i1;i3;i1;i2;i3];
v = [ ...
C(:,3).*(V(i1,d)-V(i2,d)) ; ...
C(:,3).*(V(i2,d)-V(i1,d)); ...
C(:,1).*(V(i2,d)-V(i3,d)); ...
C(:,1).*(V(i3,d)-V(i2,d)); ...
C(:,2).*(V(i3,d)-V(i1,d)) ; ...
C(:,2).*(V(i1,d)-V(i3,d)); ...
... % diagonal
C(:,3).*(V(i1,d)-V(i2,d)) + C(:,2).*(V(i1,d)-V(i3,d)); ...
C(:,1).*(V(i2,d)-V(i3,d)) + C(:,3).*(V(i2,d)-V(i1,d)); ...
C(:,2).*(V(i3,d)-V(i1,d)) + C(:,1).*(V(i3,d)-V(i2,d)); ...
];
% construct and divide by 3 so laplacian can be used as is
K = sparse(I,J,v,n,n)/2;
end
function K = spokes_and_rims_linear_block(V,F,d)
% Computes a matrix K such that K * R computes
% 鈭? -2*(cot(aij) + cot(bij) * (V(i,d)-V(j,d)) * (Ri + Rj) +
% -2*cot(aij) * (V(i,d)-V(j,d)) * Raij +
% -2*cot(bij) * (V(i,d)-V(j,d)) * Rbij
% j鈭圢(i)
%
% where: vj
% / | \
% / | \
% / | \
% / | \
% aij | bij
% \ | /
% \ | /
% \fij|gij/
% \ | /
% vi
%
% Inputs:
% V #V by dim list of coordinates
% F #F by 3 list of triangle indices into V
% d index into columns of V
% Output:
% K #V by #F matrix
%
if simplex_size == 3
% triangles
C = cotangent(V,F);
i1 = F(:,1); i2 = F(:,2); i3 = F(:,3);
I = [i1;i2;i2;i3;i3;i1;i1;i2;i3];
J = [i2;i1;i3;i2;i1;i3;i1;i2;i3];
v = [ ...
C(:,3).*(V(i1,d)-V(i2,d)) + C(:,2).*(V(i1,d)-V(i3,d)); ...
-C(:,3).*(V(i1,d)-V(i2,d)) + C(:,1).*(V(i2,d)-V(i3,d)); ...
C(:,1).*(V(i2,d)-V(i3,d)) + C(:,3).*(V(i2,d)-V(i1,d)); ...
-C(:,1).*(V(i2,d)-V(i3,d)) + C(:,2).*(V(i3,d)-V(i1,d)); ...
C(:,2).*(V(i3,d)-V(i1,d)) + C(:,1).*(V(i3,d)-V(i2,d)); ...
-C(:,2).*(V(i3,d)-V(i1,d)) + C(:,3).*(V(i1,d)-V(i2,d)); ...
... % diagonal
C(:,3).*(V(i1,d)-V(i2,d)) - C(:,2).*(V(i3,d)-V(i1,d)); ...
C(:,1).*(V(i2,d)-V(i3,d)) - C(:,3).*(V(i1,d)-V(i2,d)); ...
C(:,2).*(V(i3,d)-V(i1,d)) - C(:,1).*(V(i2,d)-V(i3,d)); ...
];
% construct and divide by 3 so laplacian can be used as is
K = sparse(I,J,v,n,n)/3;
elseif simplex_size == 4
% tetrahedra
assert(false)
end
end