function vertex1 = compute_parameterization(vertex,face, options)
% compute_parameterization - compute a planar parameterization
%
% vertex1 = compute_parameterization(vertex,face, options);
%
% options.method can be:
% 'parameterization': solve classical parameterization, the boundary
% is specified by options.boundary which is either 'circle',
% 'square' or 'triangle'. The kind of laplacian used is specified
% by options.laplacian which is either 'combinatorial' or 'conformal'
% 'freeboundary': same be leave the boundary free.
% 'flattening': solve spectral embedding using a laplacian
% specified by options.laplacian.
% 'isomap': use Isomap approach.
% 'drawing': to draw a graph whose adjacency is options.A.
% Then leave vertex and face empty.
%
% Copyright (c) 2007 Gabriel Peyre
options.null = 0;
method = getoptions(options, 'method', 'parameterization');
laplacian = getoptions(options, 'laplacian', 'conformal');
boundary = getoptions(options, 'boundary', 'square');
ndim = getoptions(options, 'ndim', 2);
[vertex,face] = check_face_vertex(vertex,face);
options.symmetrize=1;
options.normalize=0;
switch lower(method)
case 'parameterization'
vertex1 = compute_parametrization_boundary(vertex,face,laplacian,boundary, options);
case 'freeboundary'
vertex1 = compute_parameterization_free(vertex,face, laplacian, options);
case 'drawing'
if isfield(options, 'A')
A = options.A;
else
error('You must specify options.A.');
end
L = compute_mesh_laplacian(vertex,face,'combinatorial',options);
[U,S,V] = eigs(L, ndim+1, 'SM');
if abs(S(1))<eps
vertex1 = U(:,end-ndim+1:end);
elseif abs(S(ndim))<eps
vertex1 = U(:,1:ndim);
else
error('Problem with Laplacian matrix');
end
case 'flattening'
L = compute_mesh_laplacian(vertex,face,laplacian,options);
[U,S] = eig(full(L));
vertex1 = U(:,2:3);
if 0
[U,S,V] = eigs(L, ndim+1, 'SM');
if abs(S(1))<1e-9
vertex1 = U(:,end-ndim+1:end);
elseif abs(S(ndim))<eps
vertex1 = U(:,1:ndim);
else
error('Problem with Laplacian matrix');
end
end
case 'isomap'
A = triangulation2adjacency(face);
% build distance graph
D = build_euclidean_weight_matrix(A,vertex,Inf);
vertex1 = isomap(D,ndim, options);
otherwise
error('Unknown method.');
end
if size(vertex1,2)<size(vertex1,1)
vertex1 = vertex1';
end
if ndim==2
vertex1 = rectify_embedding(vertex(1:ndim,:),vertex1);
end
function xy_spec1 = rectify_embedding(xy,xy_spec)
% rectify_embedding - try to match the embeding
% with another one.
% Use the Y coord to select 2 base points.
%
% xy_spec = rectify_embedding(xy,xy_spec);
%
% Copyright (c) 2003 Gabriel Peyr?
I = find( xy(2,:)==max(xy(2,:)) );
n1 = I(1);
I = find( xy(2,:)==min(xy(2,:)) );
n2 = I(1);
v1 = xy(:,n1)-xy(:,n2);
v2 = xy_spec(:,n1)-xy_spec(:,n2);
theta = acos( dot(v1,v2)/sqrt(dot(v1,v1)*dot(v2,v2)) );
theta = theta * sign( det([v1 v2]) );
M = [cos(theta) sin(theta); -sin(theta) cos(theta)];
xy_spec1 = M*xy_spec;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function xy = compute_parameterization_free(vertex,face, laplacian, options)
n = size(vertex,2);
% extract boundary
boundary = compute_boundary(face,options);
nbound = length(boundary);
options.symmetrize=1;
options.normalize=0;
% compute Laplacian
if ~isstr(laplacian)
L = laplacian;
laplacian = 'user';
else
L = compute_mesh_laplacian(vertex,face,laplacian,options);
end
A = sparse(2*n,2*n);
A(1:end/2,1:end/2) = L;
A(end/2+1:end,end/2+1:end) = L;
% set up boundary conditions
for s = 1:nbound
% neighboring values
s1 = mod(s-2,nbound)+1;
s2 = mod(s,nbound)+1;
i = boundary(s);
i1 = boundary(s1);
i2 = boundary(s2);
e = 1;
A(i,i1+n) = +e;
A(i,i2+n) = -e;
A(i+n,i1) = -e;
A(i+n,i2) = +e;
end
% set up pinned vertices
i1 = boundary(1);
i2 = boundary(round(end/2));
A(i1,:) = 0; A(i1+n,:) = 0;
A(i1,i1) = 1; A(i1+n,i1+n) = 1;
A(i2,:) = 0; A(i2+n,:) = 0;
A(i2,i2) = 1; A(i2+n,i2+n) = 1;
p1 = [0 0]; p2 = [1 0]; % position of pinned vertices
y = zeros(2*n,1);
y(i1) = p1(1); y(i1+n) = p1(2);
y(i2) = p2(1); y(i2+n) = p2(2);
% solve for the position
xy = A\y;
xy = reshape(xy, [n 2])';
function xy = compute_parametrization_boundary(vertex,face,laplacian,boundary_type,options)
% compute_parametrization - compute a planar parameterization
% of a given disk-like triangulated manifold.
%
% compute_parametrization(vertex,face,laplacian,boundary_type);
%
% 'laplacian' is either 'combinatorial', 'conformal' or 'authalic'
% 'boundary_type' is either 'circle', 'square' or 'triangle'
%
% Copyright (c) 2004 Gabriel Peyr?
if size(vertex,2)>size(vertex,1)
vertex = vertex';
end
if size(face,2)>size(face,1)
face = face';
end
nface = size(face,1);
nvert = max(max(face));
options.symmetrize=1;
options.normalize=0;
if nargin<2
error('Not enough arguments.');
end
if nargin<3
laplacian = 'conformal';
end
if nargin<4
boundary_type = 'circle';
end
if ~isstr(laplacian)
L = laplacian;
laplacian = 'user';
else
% compute Laplacian
L = compute_mesh_laplacian(vertex,face,laplacian,options);
end
% compute the boundary
boundary = compute_boundary(face, options);
% compute the position of the boundary vertex
nbound = length(boundary);
xy_boundary = zeros(nbound,2);
% compute total length
d = 0;
ii = boundary(end); % last index
for i=boundary
d = d + norme( vertex(i,:)-vertex(ii,:) );
ii = i;
end
% vertices along the boundary
vb = vertex(boundary,:);
% compute the length of the boundary
sel = [2:nbound 1];
D = cumsum( sqrt( sum( (vb(sel,:)-vb).^2, 2 ) ) );
d = D(end);
% curvilinear abscice
t = (D-D(1))/d; t = t(:);
switch lower(boundary_type)
case 'circle'
xy_boundary = [cos(2*pi*t),sin(2*pi*t)];
case 'square'
t = t*4;
[tmp,I] = min(abs(t-1)); t(I) = 1;
[tmp,I] = min(abs(t-2)); t(I) = 2;
[tmp,I] = min(abs(t-3)); t(I) = 3;
xy_boundary = [];
I = find(t<1); nI = length(I);
xy_boundary = [xy_boundary; [t(I),t(I)*0]];
I = find(t>=1 & t<2); nI = length(I);
xy_boundary = [xy_boundary; [t(I)*0+1,t(I)-1]];
I = find(t>=2 & t<3); nI = length(I);
xy_boundary = [xy_boundary; [3-t(I),t(I)*0+1]];
I = find(t>=3); nI = length(I);
xy_boundary = [xy_boundary; [t(I)*0,4-t(I)]];
case 'triangle'
t = t*3;
[tmp,I] = min(abs(t-1)); t(I) = 1;
[tmp,I] = min(abs(t-2)); t(I) = 2;
xy_boundary = [];
I = find(t<1); nI = length(I);
xy_boundary = [xy_boundary; [t(I),t(I)*0]];
I = find(t>=1 & t<2); nI = length(I);
xy_boundary = [xy_boundary; (t(I)-1)*[0.5,sqrt(2)/2] + (2-t(I))*[1,0] ];
I = find(t>=2 & t<3); nI = length(I);
xy_boundary = [xy_boundary; (3-t(I))*[0.5,sqrt(2)/2] ];
end
% set up the matrix
M = L;
for i=boundary
M(i,:) = zeros(1,nvert);
M(i,i) = 1;
end
% solve the system
xy = zeros(nvert,2);
for coord = 1:2
% compute right hand side
x = zeros(nvert,1);
x(boundary) = xy_boundary(:,coord);
xy(:,coord) = M\x;
end
function y = norme( x );
y = sqrt( sum(x(:).^2) );