├── distmesh ├── dellipse.mexa64 ├── dellipse.mexw32 ├── dellipse.mexw64 ├── dsegment.mexa64 ├── dsegment.mexw32 ├── dsegment.mexw64 ├── dellipse.mexmaci64 ├── dellipsoid.mexa64 ├── dellipsoid.mexw32 ├── dellipsoid.mexw64 ├── dsegment.mexmaci64 ├── trisurfupd.mexa64 ├── trisurfupd.mexw32 ├── trisurfupd.mexw64 ├── dellipsoid.mexmaci64 ├── trisurfupd.mexmaci64 ├── ddiff.m ├── dunion.m ├── dintersect.m ├── huniform.m ├── pshift.m ├── dcircle.m ├── protate.m ├── dmatrix.m ├── dsphere.m ├── hmatrix.m ├── drectangle.m ├── dmatrix3d.m ├── hmatrix3d.m ├── dblock.m ├── uniformity.m ├── drectangle0.m ├── circumcenter.m ├── boundedges.m ├── fixmesh.m ├── surftri.m ├── simpvol.m ├── dpoly.m ├── COPYRIGHT.TXT ├── mkt2t.m ├── bndproj.m ├── uniref.m ├── dexpr.m ├── meshdemond.m ├── dsegment.cpp ├── simpplot.m ├── dellipse.cpp ├── meshdemo2d.m ├── simpqual.m ├── demo_distmesh.m ├── dellipsoid.cpp ├── distmeshnd.m ├── distmesh2doriginal.m ├── distmeshsurface.m ├── trisurfupd.cpp └── distmesh2d.m ├── roughness_output └── README.md ├── rec_output └── README.md ├── code ├── eigenvalues_k_35_even_theta_50.mat ├── eigenvalues_k_40_even_theta_10.mat ├── face_area.m ├── f2v.m ├── SCH_range_reconstruction_icosahedron_patch.m ├── SCH_reconstruction_icosahedron_dome.m ├── SCH_reconstruction_math.m ├── cotangent_laplacian.m ├── gpp_clean_mesh.m ├── paraview_patch.m ├── beltrami_coefficient.m ├── generalized_laplacian.m ├── linear_beltrami_solver.m ├── angle_distortion.m ├── stereographic.m ├── SH_basis_new.m ├── hypergeom_2F1.m ├── SCH_analysis.m ├── HSH_basis_new.m ├── clean_mesh.m ├── SH_basis_range.m ├── HSH_basis_range.m ├── taylor_2f1.m ├── meshboundaries.m ├── uniform_spherical_cap_grid.m └── spherical_cap_harmonic_basis.m ├── optimum_half_angle └── README.md ├── stlTools ├── stlRead.m ├── stlAddVerts.m ├── stlPlot.m ├── stlDelVerts.m ├── stlDelVerts2.m ├── stlSlimVerts.m ├── geodesicdome.m ├── stlGetVerts.m ├── stlDemo.m ├── stlGetFormat.m ├── stlReadAscii.m ├── readme.txt ├── stlReadBinary.m └── icosphere.m ├── spherical_parametrisation ├── extension │ ├── evaluate_energy.m │ ├── face_area.m │ ├── compute_vertex_normal.m │ ├── area_distortion.m │ ├── mesh_laplacian.m │ ├── mobius_area_correction_spherical.m │ ├── iterative_spherical_area_preserving_map.m │ └── spherical_area_preserving_map.m ├── mfile │ ├── plot_mesh.m │ ├── cotangent_laplacian.m │ ├── beltrami_coefficient.m │ ├── linear_beltrami_solver.m │ ├── spherical_tutte_map.m │ ├── angle_distortion.m │ └── spherical_conformal_map.m └── parametrize_surface.m ├── README.md ├── generating_artificial_surfaces ├── surface_generator.py └── Brownian_field.m ├── Main_optimum_half_angle_finder.m ├── Main_HSH_roughness_projection.m └── Main_SH_roughness_projection.m /distmesh/dellipse.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/dellipse.mexa64 -------------------------------------------------------------------------------- /distmesh/dellipse.mexw32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/dellipse.mexw32 -------------------------------------------------------------------------------- /distmesh/dellipse.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/dellipse.mexw64 -------------------------------------------------------------------------------- /distmesh/dsegment.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/dsegment.mexa64 -------------------------------------------------------------------------------- /distmesh/dsegment.mexw32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/dsegment.mexw32 -------------------------------------------------------------------------------- /distmesh/dsegment.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/dsegment.mexw64 -------------------------------------------------------------------------------- /roughness_output/README.md: -------------------------------------------------------------------------------- 1 | This folder where we drop the data and meshes from the roughness projection process. 2 | -------------------------------------------------------------------------------- /distmesh/dellipse.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/dellipse.mexmaci64 -------------------------------------------------------------------------------- /distmesh/dellipsoid.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/dellipsoid.mexa64 -------------------------------------------------------------------------------- /distmesh/dellipsoid.mexw32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/dellipsoid.mexw32 -------------------------------------------------------------------------------- /distmesh/dellipsoid.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/dellipsoid.mexw64 -------------------------------------------------------------------------------- /distmesh/dsegment.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/dsegment.mexmaci64 -------------------------------------------------------------------------------- /distmesh/trisurfupd.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/trisurfupd.mexa64 -------------------------------------------------------------------------------- /distmesh/trisurfupd.mexw32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/trisurfupd.mexw32 -------------------------------------------------------------------------------- /distmesh/trisurfupd.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/trisurfupd.mexw64 -------------------------------------------------------------------------------- /distmesh/dellipsoid.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/dellipsoid.mexmaci64 -------------------------------------------------------------------------------- /distmesh/trisurfupd.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/distmesh/trisurfupd.mexmaci64 -------------------------------------------------------------------------------- /rec_output/README.md: -------------------------------------------------------------------------------- 1 | This folder where the reconstruction data and meshes are dropped after the analysis or reconstruction. 2 | -------------------------------------------------------------------------------- /code/eigenvalues_k_35_even_theta_50.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/code/eigenvalues_k_35_even_theta_50.mat -------------------------------------------------------------------------------- /code/eigenvalues_k_40_even_theta_10.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eesd-epfl/spherical-cap-harmonics/HEAD/code/eigenvalues_k_40_even_theta_10.mat -------------------------------------------------------------------------------- /optimum_half_angle/README.md: -------------------------------------------------------------------------------- 1 | This folder where we drop the parameterization data and meshes from the process of finding optimal \theta c angle. 2 | -------------------------------------------------------------------------------- /distmesh/ddiff.m: -------------------------------------------------------------------------------- 1 | function d=ddiff(d1,d2), d=max(d1,-d2); 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | -------------------------------------------------------------------------------- /distmesh/dunion.m: -------------------------------------------------------------------------------- 1 | function d=dunion(d1,d2), d=min(d1,d2); 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | -------------------------------------------------------------------------------- /distmesh/dintersect.m: -------------------------------------------------------------------------------- 1 | function d=dintersect(d1,d2), d=max(d1,d2); 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | -------------------------------------------------------------------------------- /distmesh/huniform.m: -------------------------------------------------------------------------------- 1 | function h=huniform(p,varargin) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | h=ones(size(p,1),1); 6 | -------------------------------------------------------------------------------- /distmesh/pshift.m: -------------------------------------------------------------------------------- 1 | function p=pshift(p,x0,y0) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | p(:,1)=p(:,1)+x0; 6 | p(:,2)=p(:,2)+y0; 7 | -------------------------------------------------------------------------------- /distmesh/dcircle.m: -------------------------------------------------------------------------------- 1 | function d=dcircle(p,xc,yc,r) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | d=sqrt((p(:,1)-xc).^2+(p(:,2)-yc).^2)-r; 6 | -------------------------------------------------------------------------------- /distmesh/protate.m: -------------------------------------------------------------------------------- 1 | function p=protate(p,phi) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | A=[cos(phi),-sin(phi);sin(phi),cos(phi)]; 6 | p=p*A; 7 | -------------------------------------------------------------------------------- /distmesh/dmatrix.m: -------------------------------------------------------------------------------- 1 | function d=dmatrix(p,xx,yy,dd,varargin) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | d=interp2(xx,yy,dd,p(:,1),p(:,2),'*linear'); 6 | -------------------------------------------------------------------------------- /distmesh/dsphere.m: -------------------------------------------------------------------------------- 1 | function d=dsphere(p,xc,yc,zc,r) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | d=sqrt((p(:,1)-xc).^2+(p(:,2)-yc).^2+(p(:,3)-zc).^2)-r; 6 | -------------------------------------------------------------------------------- /distmesh/hmatrix.m: -------------------------------------------------------------------------------- 1 | function h=hmatrix(p,xx,yy,dd,hh,varargin) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | h=interp2(xx,yy,hh,p(:,1),p(:,2),'*linear'); 6 | -------------------------------------------------------------------------------- /distmesh/drectangle.m: -------------------------------------------------------------------------------- 1 | function d=drectangle(p,x1,x2,y1,y2) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | d=-min(min(min(-y1+p(:,2),y2-p(:,2)),-x1+p(:,1)),x2-p(:,1)); 6 | -------------------------------------------------------------------------------- /distmesh/dmatrix3d.m: -------------------------------------------------------------------------------- 1 | function d=dmatrix3d(p,xx,yy,zz,dd,varargin) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | d=interpn(xx,yy,zz,dd,p(:,1),p(:,2),p(:,3),'*linear'); 6 | -------------------------------------------------------------------------------- /distmesh/hmatrix3d.m: -------------------------------------------------------------------------------- 1 | function h=hmatrix3d(p,xx,yy,zz,dd,hh,varargin) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | h=interpn(xx,yy,zz,hh,p(:,1),p(:,2),p(:,3),'*linear'); 6 | -------------------------------------------------------------------------------- /distmesh/dblock.m: -------------------------------------------------------------------------------- 1 | function d=dblock(p,x1,x2,y1,y2,z1,z2) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | d=-min(min(min(min(min(-z1+p(:,3),z2-p(:,3)),-y1+p(:,2)),y2-p(:,2)),-x1+p(:,1)),x2-p(:,1)); 6 | -------------------------------------------------------------------------------- /distmesh/uniformity.m: -------------------------------------------------------------------------------- 1 | function u=uniformity(p,t,fh,varargin) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | [pc,r]=circumcenter(p,t); 6 | hc=feval(fh,pc,varargin{:}); 7 | 8 | sz=r./hc; 9 | u=std(sz)/mean(sz); 10 | -------------------------------------------------------------------------------- /stlTools/stlRead.m: -------------------------------------------------------------------------------- 1 | function [v, f, n, name] = stlRead(fileName) 2 | %STLREAD reads any STL file not depending on its format 3 | %V are the vertices 4 | %F are the faces 5 | %N are the normals 6 | %NAME is the name of the STL object (NOT the name of the STL file) 7 | 8 | format = stlGetFormat(fileName); 9 | if strcmp(format,'ascii') 10 | [v,f,n,name] = stlReadAscii(fileName); 11 | elseif strcmp(format,'binary') 12 | [v,f,n,name] = stlReadBinary(fileName); 13 | end -------------------------------------------------------------------------------- /distmesh/drectangle0.m: -------------------------------------------------------------------------------- 1 | function d=drectangle0(p,x1,x2,y1,y2) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | d1=y1-p(:,2); 6 | d2=-y2+p(:,2); 7 | d3=x1-p(:,1); 8 | d4=-x2+p(:,1); 9 | 10 | d5=sqrt(d1.^2+d3.^2); 11 | d6=sqrt(d1.^2+d4.^2); 12 | d7=sqrt(d2.^2+d3.^2); 13 | d8=sqrt(d2.^2+d4.^2); 14 | 15 | d=-min(min(min(-d1,-d2),-d3),-d4); 16 | 17 | ix=d1>0 & d3>0; 18 | d(ix)=d5(ix); 19 | ix=d1>0 & d4>0; 20 | d(ix)=d6(ix); 21 | ix=d2>0 & d3>0; 22 | d(ix)=d7(ix); 23 | ix=d2>0 & d4>0; 24 | d(ix)=d8(ix); 25 | -------------------------------------------------------------------------------- /distmesh/circumcenter.m: -------------------------------------------------------------------------------- 1 | function [pc,r]=circumcenter(p,t) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | nt=size(t,1); 6 | pc=zeros(nt,2); 7 | r=zeros(nt,1); 8 | 9 | for it=1:nt 10 | ct=t(it,:); 11 | dp1=p(ct(2),:)-p(ct(1),:); 12 | dp2=p(ct(3),:)-p(ct(1),:); 13 | 14 | mid1=(p(ct(2),:)+p(ct(1),:))/2; 15 | mid2=(p(ct(3),:)+p(ct(1),:))/2; 16 | 17 | s=[-dp1(2),dp2(2);dp1(1),-dp2(1)]\[-mid1+mid2]'; 18 | 19 | cpc=mid1+s(1)*[-dp1(2),dp1(1)]; 20 | cr=norm(p(ct(1),:)-cpc); 21 | 22 | pc(it,:)=cpc; 23 | r(it,1)=cr; 24 | end 25 | -------------------------------------------------------------------------------- /stlTools/stlAddVerts.m: -------------------------------------------------------------------------------- 1 | function [vnew, fnew] = stlAddVerts(v, f, list) 2 | %STLADDVERTS adds new vertices (and consequently, new faces) to a STL object 3 | %V is the Nx3 array of vertices 4 | %F is the Mx3 array of faces 5 | %LIST is the list of vertices to be added to the object 6 | %VNEW is the new array of vertices 7 | %FNEW is the new array of faces 8 | 9 | % triangulation just with the slice 10 | faces = delaunay(list(:,1),list(:,2)); % calculate new faces 11 | % update object 12 | nvert = length(v); % number of original vertices 13 | v = [v; list]; % update vertices with the ones in the list 14 | f = [f; faces+nvert]; % update faces with the new ones 15 | [vnew,fnew] = stlSlimVerts(v,f); % clear repeated vertices -------------------------------------------------------------------------------- /distmesh/boundedges.m: -------------------------------------------------------------------------------- 1 | function e=boundedges(p,t) 2 | %BOUNDEDGES Find boundary edges from triangular mesh 3 | % E=BOUNDEDGES(P,T) 4 | 5 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 6 | 7 | % Form all edges, non-duplicates are boundary edges 8 | edges=[t(:,[1,2]); 9 | t(:,[1,3]); 10 | t(:,[2,3])]; 11 | node3=[t(:,3);t(:,2);t(:,1)]; 12 | edges=sort(edges,2); 13 | [foo,ix,jx]=unique(edges,'rows'); 14 | vec=histc(jx,1:max(jx)); 15 | qx=find(vec==1); 16 | e=edges(ix(qx),:); 17 | node3=node3(ix(qx)); 18 | 19 | % Orientation 20 | v1=p(e(:,2),:)-p(e(:,1),:); 21 | v2=p(node3,:)-p(e(:,1),:); 22 | ix=find(v1(:,1).*v2(:,2)-v1(:,2).*v2(:,1)>0); 23 | e(ix,[1,2])=e(ix,[2,1]); 24 | -------------------------------------------------------------------------------- /stlTools/stlPlot.m: -------------------------------------------------------------------------------- 1 | function stlPlot(v, f, name) 2 | %STLPLOT is an easy way to plot an STL object 3 | %V is the Nx3 array of vertices 4 | %F is the Mx3 array of faces 5 | %NAME is the name of the object, that will be displayed as a title 6 | 7 | figure; 8 | object.vertices = v; 9 | object.faces = f; 10 | patch(object,'FaceColor', [0.8 0.8 1.0], ... 11 | 'EdgeColor', 'none', ... 12 | 'FaceLighting', 'gouraud', ... 13 | 'AmbientStrength', 0.15); 14 | 15 | % Add a camera light, and tone down the specular highlighting 16 | camlight('headlight'); 17 | material('dull'); 18 | 19 | % Fix the axes scaling, and set a nice view angle 20 | axis('image'); 21 | view([-135 35]); 22 | grid on; 23 | title(name); 24 | -------------------------------------------------------------------------------- /distmesh/fixmesh.m: -------------------------------------------------------------------------------- 1 | function [p,t,pix]=fixmesh(p,t,ptol) 2 | %FIXMESH Remove duplicated/unused nodes and fix element orientation. 3 | % [P,T]=FIXMESH(P,T) 4 | 5 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 6 | 7 | if nargin<3, ptol=1024*eps; end 8 | if nargin>=2 & (isempty(p) | isempty(t)), pix=1:size(p,1); return; end 9 | 10 | snap=max(max(p,[],1)-min(p,[],1),[],2)*ptol; 11 | [foo,ix,jx]=unique(round(p/snap)*snap,'rows'); 12 | p=p(ix,:); 13 | 14 | if nargin>=2 15 | t=reshape(jx(t),size(t)); 16 | 17 | [pix,ix1,jx1]=unique(t); 18 | t=reshape(jx1,size(t)); 19 | p=p(pix,:); 20 | pix=ix(pix); 21 | 22 | if size(t,2)==size(p,2)+1 23 | flip=simpvol(p,t)<0; 24 | t(flip,[1,2])=t(flip,[2,1]); 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /code/face_area.m: -------------------------------------------------------------------------------- 1 | function fa = face_area(f,v) 2 | % Compute the area of every face of a triangle mesh. 3 | % 4 | % If you use this code in your own work, please cite the following paper: 5 | % [1] P. T. Choi, K. C. Lam, and L. M. Lui, 6 | % "FLASH: Fast Landmark Aligned Spherical Harmonic Parameterization for Genus-0 Closed Brain Surfaces." 7 | % SIAM Journal on Imaging Sciences, vol. 8, no. 1, pp. 67-94, 2015. 8 | % 9 | % Copyright (c) 2013-2018, Gary Pui-Tung Choi 10 | % https://scholar.harvard.edu/choi 11 | 12 | v12 = v(f(:,2),:) - v(f(:,1),:); 13 | v23 = v(f(:,3),:) - v(f(:,2),:); 14 | v31 = v(f(:,1),:) - v(f(:,3),:); 15 | 16 | a = sqrt(dot(v12,v12,2)); 17 | b = sqrt(dot(v23,v23,2)); 18 | c = sqrt(dot(v31,v31,2)); 19 | 20 | s = (a+b+c)/2; 21 | fa = sqrt(s.*(s-a).*(s-b).*(s-c)); 22 | -------------------------------------------------------------------------------- /distmesh/surftri.m: -------------------------------------------------------------------------------- 1 | function tri=surftri(p,t) 2 | %SURFTRI Find surface triangles from tetrahedra mesh 3 | % TRI=SURFTRI(P,T) 4 | 5 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 6 | 7 | % Form all faces, non-duplicates are surface triangles 8 | faces=[t(:,[1,2,3]); 9 | t(:,[1,2,4]); 10 | t(:,[1,3,4]); 11 | t(:,[2,3,4])]; 12 | node4=[t(:,4);t(:,3);t(:,2);t(:,1)]; 13 | faces=sort(faces,2); 14 | [foo,ix,jx]=unique(faces,'rows'); 15 | vec=histc(jx,1:max(jx)); 16 | qx=find(vec==1); 17 | tri=faces(ix(qx),:); 18 | node4=node4(ix(qx)); 19 | 20 | % Orientation 21 | v1=p(tri(:,2),:)-p(tri(:,1),:); 22 | v2=p(tri(:,3),:)-p(tri(:,1),:); 23 | v3=p(node4,:)-p(tri(:,1),:); 24 | ix=find(dot(cross(v1,v2,2),v3,2)>0); 25 | tri(ix,[2,3])=tri(ix,[3,2]); 26 | -------------------------------------------------------------------------------- /spherical_parametrisation/extension/evaluate_energy.m: -------------------------------------------------------------------------------- 1 | function E = evaluate_energy(v, M) 2 | % Evaluate the spring energy with the weighted Laplacian matrix M. 3 | % 4 | % If you use this code in your own work, please cite the following paper: 5 | % [1] P. T. Choi, K. C. Lam, and L. M. Lui, 6 | % "FLASH: Fast Landmark Aligned Spherical Harmonic Parameterization for Genus-0 Closed Brain Surfaces." 7 | % SIAM Journal on Imaging Sciences, vol. 8, no. 1, pp. 67-94, 2015. 8 | % 9 | % Copyright (c) 2013-2018, Gary Pui-Tung Choi 10 | % https://scholar.harvard.edu/choi 11 | 12 | [row, col, value] = find(M); 13 | diagonal = find(row>=col); 14 | row(diagonal) = []; 15 | col(diagonal) = []; 16 | value(diagonal) = []; 17 | d = v(row,1:3)-v(col,1:3); 18 | E = sum(value.*(d(:,1).^2 + d(:,2).^2 + d(:,3).^2)); 19 | E = abs(sum(E))/2; -------------------------------------------------------------------------------- /spherical_parametrisation/extension/face_area.m: -------------------------------------------------------------------------------- 1 | function fa = face_area(f,v) 2 | % Compute the area of every face of a triangle mesh. 3 | % 4 | % If you use this code in your own work, please cite the following paper: 5 | % [1] P. T. Choi, K. C. Lam, and L. M. Lui, 6 | % "FLASH: Fast Landmark Aligned Spherical Harmonic Parameterization for Genus-0 Closed Brain Surfaces." 7 | % SIAM Journal on Imaging Sciences, vol. 8, no. 1, pp. 67-94, 2015. 8 | % 9 | % Copyright (c) 2013-2018, Gary Pui-Tung Choi 10 | % https://scholar.harvard.edu/choi 11 | 12 | v12 = v(f(:,2),:) - v(f(:,1),:); 13 | v23 = v(f(:,3),:) - v(f(:,2),:); 14 | v31 = v(f(:,1),:) - v(f(:,3),:); 15 | 16 | a = sqrt(dot(v12,v12,2)); 17 | b = sqrt(dot(v23,v23,2)); 18 | c = sqrt(dot(v31,v31,2)); 19 | 20 | s = (a+b+c)/2; 21 | fa = sqrt(s.*(s-a).*(s-b).*(s-c)); 22 | -------------------------------------------------------------------------------- /distmesh/simpvol.m: -------------------------------------------------------------------------------- 1 | function v=simpvol(p,t) 2 | %SIMPVOL Simplex volume. 3 | % V=SIMPVOL(P,T) 4 | 5 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 6 | 7 | switch size(p,2) 8 | case 1 9 | d12=p(t(:,2),:)-p(t(:,1),:); 10 | v=d12; 11 | case 2 12 | d12=p(t(:,2),:)-p(t(:,1),:); 13 | d13=p(t(:,3),:)-p(t(:,1),:); 14 | v=(d12(:,1).*d13(:,2)-d12(:,2).*d13(:,1))/2; 15 | case 3 16 | d12=p(t(:,2),:)-p(t(:,1),:); 17 | d13=p(t(:,3),:)-p(t(:,1),:); 18 | d14=p(t(:,4),:)-p(t(:,1),:); 19 | v=dot(cross(d12,d13,2),d14,2)/6; 20 | otherwise 21 | v=zeros(size(t,1),1); 22 | for ii=1:size(t,1) 23 | A=zeros(size(p,2)+1); 24 | A(:,1)=1; 25 | for jj=1:size(p,2)+1 26 | A(jj,2:end)=p(t(ii,jj),:); 27 | end 28 | v(ii)=det(A); 29 | end 30 | v=v/factorial(size(p,2)); 31 | end 32 | -------------------------------------------------------------------------------- /code/f2v.m: -------------------------------------------------------------------------------- 1 | function S = f2v(v,f) 2 | 3 | % Compute the face to vertex interpolation matrix. 4 | % 5 | % If you use this code in your own work, please cite the following paper: 6 | % [1] P. T. Choi and L. M. Lui, 7 | % "Fast Disk Conformal Parameterization of Simply-Connected Open Surfaces." 8 | % Journal of Scientific Computing, 65(3), pp. 1065-1090, 2015. 9 | % 10 | % Copyright (c) 2014-2018, Gary Pui-Tung Choi 11 | % https://scholar.harvard.edu/choi 12 | 13 | ring = vertexAttachments(TriRep(f,v)); 14 | nv = length(v); nf = length(f); 15 | II = cellfun(@times,ring,num2cell(zeros(nv,1)),'UniformOutput',0); 16 | II = cell2mat(cellfun(@plus,II,num2cell(1:nv)','UniformOutput',0)')'; 17 | JJ = cell2mat(ring')'; 18 | avg = cellfun(@length,ring); 19 | S = sparse(II,JJ,ones(length(JJ),1),nv,nf); 20 | S = sparse(1:nv,1:nv,1./avg)*S; 21 | 22 | end -------------------------------------------------------------------------------- /stlTools/stlDelVerts.m: -------------------------------------------------------------------------------- 1 | function [vnew, fnew] = stlDelVerts(v, f, list) 2 | %STLDELVERT removes a list of vertices from STL files 3 | %V is the Nx3 array of vertices 4 | %F is the Mx3 array of faces 5 | %LIST are the vertices (rows) to delete, where length(LIST) < N 6 | %VNEW is the new array of vertices 7 | %FNEW is the new array of faces 8 | 9 | % find (on the global set) the position (rows) of the vertices to be deleted 10 | [~,vdel] = ismember(list,v,'rows'); 11 | 12 | % delete vertices and get new tags 13 | vnew = v; 14 | tags = 1:length(v); 15 | vnew(vdel,:) = []; 16 | tags(vdel) = []; 17 | 18 | % delete faces 19 | fnew = f; 20 | [fdel,~] = find(ismember(f,vdel)); % find the position (rows) of the faces to delete 21 | fnew(fdel,:) = []; 22 | 23 | % rename faces, as some of the vertices have been deleted 24 | flist = reshape(fnew,numel(fnew),1); 25 | [~,ind] = ismember(flist,tags); 26 | fnew = reshape(ind,numel(flist)/3,3); -------------------------------------------------------------------------------- /stlTools/stlDelVerts2.m: -------------------------------------------------------------------------------- 1 | function [vnew, fnew] = stlDelVerts2(v, f, list) 2 | %STLDELVERT removes a list of vertices from STL files 3 | %V is the Nx3 array of vertices 4 | %F is the Mx3 array of faces 5 | %LIST are the vertices (rows) to delete, where length(LIST) < N for index 6 | %of vertex 7 | %VNEW is the new array of vertices 8 | %FNEW is the new array of faces 9 | 10 | % find (on the global set) the position (rows) of the vertices to be deleted 11 | % [~,vdel] = ismember(list,v','rows'); 12 | 13 | % delete vertices and get new tags 14 | vnew = v; 15 | tags = 1:length(v); 16 | vnew(list,:) = []; 17 | tags(list) = []; 18 | 19 | % delete faces 20 | fnew = f; 21 | [fdel,~] = find(ismember(f,list)); % find the position (rows) of the faces to delete 22 | fnew(fdel,:) = []; 23 | 24 | % rename faces, as some of the vertices have been deleted 25 | flist = reshape(fnew,numel(fnew),1); 26 | [~,ind] = ismember(flist,tags); 27 | fnew = reshape(ind,numel(flist)/3,3); -------------------------------------------------------------------------------- /distmesh/dpoly.m: -------------------------------------------------------------------------------- 1 | function d=dpoly(p,pv) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | np=size(p,1); 6 | nvs=size(pv,1)-1; 7 | 8 | ds=dsegment(p,pv); 9 | %ds=zeros(np,nvs); 10 | %for iv=1:nvs 11 | % ds(:,iv)=donesegment(p,pv(iv:iv+1,:)); 12 | %end 13 | d=min(ds,[],2); 14 | 15 | d=(-1).^(inpolygon(p(:,1),p(:,2),pv(:,1),pv(:,2))).*d; 16 | 17 | % MEXED 18 | 19 | %function ds=donesegment(p,pv) 20 | % 21 | %e=ones(size(p,1),1); 22 | % 23 | %v=diff(pv,1); 24 | %w=p-e*pv(1,:); 25 | % 26 | %c1=sum(w.*v(e,:),2); 27 | %c2=sum(v(e,:).^2,2); 28 | % 29 | %ds=0*e; 30 | % 31 | %ix=c1<=0; 32 | %ds(ix)=sqrt(sum((p(ix,:)-pv(1*ones(sum(ix),1),:)).^2,2)); 33 | % 34 | %ix=c1>=c2; 35 | %ds(ix)=sqrt(sum((p(ix,:)-pv(2*ones(sum(ix),1),:)).^2,2)); 36 | % 37 | %ix=c1>0 & c2>c1; 38 | %nix=sum(ix); 39 | %if nix>0 40 | % Pb=ones(nix,1)*pv(1,:)+c1(ix)./c2(ix)*v; 41 | % ds(ix)=sqrt(sum((p(ix,:)-Pb).^2,2)); 42 | %end 43 | -------------------------------------------------------------------------------- /stlTools/stlSlimVerts.m: -------------------------------------------------------------------------------- 1 | function [vnew, fnew]= stlSlimVerts(v, f) 2 | % PATCHSLIM removes duplicate vertices in surface meshes. 3 | % 4 | % This function finds and removes duplicate vertices. 5 | % 6 | % USAGE: [v, f]=patchslim(v, f) 7 | % 8 | % Where v is the vertex list and f is the face list specifying vertex 9 | % connectivity. 10 | % 11 | % v contains the vertices for all triangles [3*n x 3]. 12 | % f contains the vertex lists defining each triangle face [n x 3]. 13 | % 14 | % This will reduce the size of typical v matrix by about a factor of 6. 15 | % 16 | % For more information see: 17 | % http://www.esmonde-white.com/home/diversions/matlab-program-for-loading-stl-files 18 | % 19 | % Francis Esmonde-White, May 2010 20 | 21 | if ~exist('v','var') 22 | error('The vertex list (v) must be specified.'); 23 | end 24 | if ~exist('f','var') 25 | error('The vertex connectivity of the triangle faces (f) must be specified.'); 26 | end 27 | 28 | [vnew, indexm, indexn] = unique(v, 'rows'); 29 | fnew = indexn(f); 30 | -------------------------------------------------------------------------------- /stlTools/geodesicdome.m: -------------------------------------------------------------------------------- 1 | function [v, f] = geodesicdome(N, theta_c) 2 | % Authors: Mahmoud Shaqfa, Gary Choi, Katrin Beyer 3 | % Generate a Geodesic Dome for the spherical caps 4 | % theta_c in radians 5 | % N: the number of refinements 6 | 7 | if nargin == 0 8 | close all 9 | % Test codes 10 | N = 3; 11 | theta_c = deg2rad(90); 12 | end 13 | 14 | [v, f] = icosphere(N); 15 | v = [v(:,1) - mean(v(:,1)), v(:,2) - mean(v(:,2)), v(:,3) - mean(v(:,3))]; 16 | 17 | % Find unneeded vertices 18 | x_c = cos(theta_c); 19 | list = find(v(v(:, 3) < x_c)); 20 | 21 | % Delete unneeded vertices 22 | [v, f] = stlDelVerts2(v, f, list'); 23 | 24 | % Rotate about the z-axis 25 | theta=pi/2; 26 | R = [cos(theta), 0, sin(theta) 27 | 0 1 0 28 | -sin(theta), 0, cos(theta)]; 29 | v = v*R; 30 | 31 | if nargin == 0 32 | % Test codes 33 | cd .. 34 | addpath('/Spherical Cap Harmonic Analysis') 35 | addpath('/stlTools') 36 | paraview_patch(v, f) 37 | axis on 38 | xlabel('x'); ylabel('y'); zlabel('z') 39 | cd stlTools/ 40 | end 41 | end -------------------------------------------------------------------------------- /code/SCH_range_reconstruction_icosahedron_patch.m: -------------------------------------------------------------------------------- 1 | function recursive_reconstruction = SCH_range_reconstruction_icosahedron_patch(k_range, qm_k, theta_c, eigen_table, thetas, phis, N_eps) 2 | % Icosahedron dome reconstruction 3 | k_min = min(k_range); 4 | k_max = max(k_range); 5 | 6 | C_mat = zeros(length(thetas), (k_max+1)^2); 7 | reconstruction = zeros(length(thetas), 3); 8 | recursive_reconstruction = zeros(length(thetas), 3, k_max + 1); 9 | for n = k_min:k_max 10 | C_mat(:, n^2 + 1:n^2 + 2*n + 1) = spherical_cap_harmonic_basis(n, theta_c, ... 11 | eigen_table, thetas, phis, N_eps); 12 | for ii = n^2 + 1:n^2 + 2*n + 1 13 | reconstruction(:, 1) = reconstruction(:, 1) + C_mat(:, ii) .* qm_k(ii, 1); 14 | reconstruction(:, 2) = reconstruction(:, 2) + C_mat(:, ii) .* qm_k(ii, 2); 15 | reconstruction(:, 3) = reconstruction(:, 3) + C_mat(:, ii) .* qm_k(ii, 3); 16 | end 17 | recursive_reconstruction(:, :, n+1) = reconstruction; 18 | fprintf("\nCompleted: %3.2f %% for n = %3.0f", ((n+1)^2/(k_max + 1)^2*100), n) 19 | end 20 | end -------------------------------------------------------------------------------- /spherical_parametrisation/mfile/plot_mesh.m: -------------------------------------------------------------------------------- 1 | function plot_mesh(v,f,arg3) 2 | % Plot a mesh. 3 | % 4 | % Input: 5 | % v: nv x 3 vertex coordinates 6 | % f: nf x 3 triangulations 7 | % arg3 (optional): nv x 1 quantity defined on vertices 8 | % 9 | % If you use this code in your own work, please cite the following paper: 10 | % [1] P. T. Choi, K. C. Lam, and L. M. Lui, 11 | % "FLASH: Fast Landmark Aligned Spherical Harmonic Parameterization for Genus-0 Closed Brain Surfaces." 12 | % SIAM Journal on Imaging Sciences, vol. 8, no. 1, pp. 67-94, 2015. 13 | % 14 | % Copyright (c) 2013-2018, Gary Pui-Tung Choi 15 | % https://scholar.harvard.edu/choi 16 | 17 | figure; 18 | 19 | if nargin < 3 20 | patch('Faces',f,'Vertices',v,'FaceColor',[0.6,1,1],'LineWidth',0.5); 21 | 22 | else 23 | patch('Faces',f,'Vertices',v,'FaceColor','flat','FaceVertexCData',arg3,... 24 | 'EdgeColor','none', 'LineWidth',0.5); 25 | colormap('Copper'); 26 | shading interp; 27 | set(gcf,'color','w'); 28 | 29 | end 30 | axis equal tight off 31 | ax = gca; ax.Clipping = 'off'; -------------------------------------------------------------------------------- /code/SCH_reconstruction_icosahedron_dome.m: -------------------------------------------------------------------------------- 1 | function recursive_reconstruction = SCH_reconstruction_icosahedron_dome(K_max, qm_k, theta_c, eigen_table, thetas, phis, N_eps) 2 | % function [reconstruction, recursive_reconstruction] = SCH_reconstruction_icosahedron_dome(K_max, qm_k, theta_c, eigen_table, thetas, phis) 3 | % Icosahedron dome reconstruction 4 | 5 | C_mat = zeros(length(thetas), (K_max+1)^2); 6 | reconstruction = zeros(length(thetas), 3); 7 | recursive_reconstruction = zeros(length(thetas), 3, K_max+1); 8 | for n = 0:K_max 9 | C_mat(:, n^2 + 1:n^2 + 2*n + 1) = spherical_cap_harmonic_basis(n, theta_c, ... 10 | eigen_table, thetas, phis, N_eps); 11 | for ii = n^2 + 1:n^2 + 2*n + 1 12 | reconstruction(:, 1) = reconstruction(:, 1) + C_mat(:, ii) .* qm_k(ii, 1); 13 | reconstruction(:, 2) = reconstruction(:, 2) + C_mat(:, ii) .* qm_k(ii, 2); 14 | reconstruction(:, 3) = reconstruction(:, 3) + C_mat(:, ii) .* qm_k(ii, 3); 15 | end 16 | recursive_reconstruction(:, :, n+1) = reconstruction; 17 | fprintf("\nCompleted: %3.2f %% for n = %3.0f", ((n+1)^2/(K_max+1)^2*100), n) 18 | end 19 | end -------------------------------------------------------------------------------- /code/SCH_reconstruction_math.m: -------------------------------------------------------------------------------- 1 | function reconstruction = SCH_reconstruction_math(K_max, qm_k, resolution, theta_c, eigen_table, N_eps) 2 | % Mathematical reconstruction 3 | 4 | sample_phi_range = linspace(-pi, pi, resolution); 5 | sample_theta_range = linspace(0, theta_c-0.01, resolution); 6 | [sample_theta, sample_phi] = meshgrid(sample_theta_range, sample_phi_range); 7 | 8 | temp_sz = size(sample_theta); temp_sz(3) = (K_max+1)^2; 9 | C_mat = zeros(temp_sz); 10 | temp_sz2 = size(sample_theta); temp_sz2(3) = 3; 11 | reconstruction = zeros(temp_sz2); 12 | for n = 0:K_max 13 | C_mat(:, :, n^2 + 1:n^2 + 2*n + 1) = spherical_cap_harmonic_basis(n, theta_c, ... 14 | eigen_table, sample_theta, sample_phi, N_eps); 15 | for ii = n^2 + 1:n^2 + 2*n + 1 16 | reconstruction(:, :, 1) = reconstruction(:, :, 1) + C_mat(:, :, ii) .* qm_k(ii, 1); 17 | reconstruction(:, :, 2) = reconstruction(:, :, 2) + C_mat(:, :, ii) .* qm_k(ii, 2); 18 | reconstruction(:, :, 3) = reconstruction(:, :, 3) + C_mat(:, :, ii) .* qm_k(ii, 3); 19 | end 20 | fprintf("\nCompleted: %3.2f %% for n = %3.0f", ((n+1)^2/(K_max+1)^2*100), n) 21 | end 22 | end -------------------------------------------------------------------------------- /distmesh/COPYRIGHT.TXT: -------------------------------------------------------------------------------- 1 | DistMesh is a collection of MATLAB functions for generation and 2 | manipulation of unstructured meshes. DistMesh is Copyright (C) 2004-2012 3 | Per-Olof Persson. 4 | 5 | This program is free software; you can redistribute it and/or modify it 6 | under the terms of the GNU General Public License as published by the 7 | Free Software Foundation; either version 2 of the License, or (at your 8 | option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, but 11 | WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | If you use DistMesh in any program or publication, please acknowledge 20 | its authors by adding a reference to: Per-Olof Persson and Gilbert 21 | Strang, "A Simple Mesh Generator in MATLAB," SIAM Review Vol. 46 (2) 22 | 2004. 23 | -------------------------------------------------------------------------------- /distmesh/mkt2t.m: -------------------------------------------------------------------------------- 1 | function [t2t,t2n]=mkt2t(t) 2 | %MKT2T Compute element connectivities from element indices. 3 | % [T2T,T2N]=MKT2T(T) 4 | 5 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 6 | 7 | nt=size(t,1); 8 | dim=size(t,2)-1; 9 | 10 | switch dim 11 | case 1 12 | edges=[t(:,2) 13 | t(:,1)]; 14 | case 2 15 | edges=[t(:,[2,3]) 16 | t(:,[3,1]) 17 | t(:,[1,2])]; 18 | case 3 19 | edges=[t(:,[2,3,4]) 20 | t(:,[3,4,1]) 21 | t(:,[4,1,2]) 22 | t(:,[1,2,3])]; 23 | end 24 | 25 | ts=[repmat(int32(1:nt),1,dim+1); kron(int32(1:(dim+1)),ones(1,nt,'int32'))]'; 26 | 27 | edges=sort(edges,2); 28 | [foo,foo,jx]=unique(edges,'rows'); 29 | 30 | [jx,ix]=sort(jx); 31 | ts=ts(ix,:); 32 | 33 | ix=find(diff(jx)==0); 34 | ts1=ts(ix,:); 35 | ts2=ts(ix+1,:); 36 | 37 | t2t=zeros(nt,dim+1,'int32'); 38 | t2t(ts1(:,1)+nt*(ts1(:,2)-1))=ts2(:,1); 39 | t2t(ts2(:,1)+nt*(ts2(:,2)-1))=ts1(:,1); 40 | 41 | if nargout>=2 42 | t2n=zeros(nt,dim+1,'int32'); 43 | t2n(ts1(:,1)+nt*(ts1(:,2)-1))=ts2(:,2); 44 | t2n(ts2(:,1)+nt*(ts2(:,2)-1))=ts1(:,2); 45 | end 46 | -------------------------------------------------------------------------------- /distmesh/bndproj.m: -------------------------------------------------------------------------------- 1 | function p=bndproj(p,t,fd,varargin) 2 | %BNDPROJ Project boundary points to true boundary 3 | % P=BNDPROJ(P,T,FD,FDARGS) 4 | 5 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 6 | 7 | deps=sqrt(eps)*max(max(p)-min(p)); 8 | 9 | if size(p,2)==2 10 | e=boundedges(p,t); 11 | e=unique(e(:)); 12 | 13 | d=feval(fd,p(e,:),varargin{:}); 14 | dgradx=(feval(fd,[p(e,1)+deps,p(e,2)],varargin{:})-d)/deps; 15 | dgrady=(feval(fd,[p(e,1),p(e,2)+deps],varargin{:})-d)/deps; 16 | dgrad2=dgradx.^2+dgrady.^2; 17 | dgrad2(dgrad2==0)=1; 18 | p(e,:)=p(e,:)-[d.*dgradx./dgrad2,d.*dgrady./dgrad2]; 19 | elseif size(p,2)==3 20 | if size(t,2)==3 21 | tri=t; 22 | else 23 | tri=surftri(p,t); 24 | end 25 | tri=unique(tri(:)); 26 | 27 | d=feval(fd,p(tri,:),varargin{:}); 28 | dgradx=(feval(fd,[p(tri,1)+deps,p(tri,2),p(tri,3)],varargin{:})-d)/deps; 29 | dgrady=(feval(fd,[p(tri,1),p(tri,2)+deps,p(tri,3)],varargin{:})-d)/deps; 30 | dgradz=(feval(fd,[p(tri,1),p(tri,2),p(tri,3)+deps],varargin{:})-d)/deps; 31 | dgrad2=dgradx.^2+dgrady.^2+dgradz.^2; 32 | dgrad2(dgrad2==0)=1; 33 | p(tri,:)=p(tri,:)-[d.*dgradx./dgrad2,d.*dgrady./dgrad2,d.*dgradz./dgrad2]; 34 | end 35 | -------------------------------------------------------------------------------- /distmesh/uniref.m: -------------------------------------------------------------------------------- 1 | function [p,t]=uniref(p,t,nref,fd,varargin) 2 | %UNIREF Uniform mesh refinement 3 | % [P,T]=UNIREF(P,T,NREF,FD,FDARGS) 4 | 5 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 6 | 7 | if nargin<3, nref=1; end 8 | if nargin<4, fd={}; end 9 | 10 | dim=size(t,2)-1; 11 | 12 | for iref=1:nref 13 | np=size(p,1); 14 | nt=size(t,1); 15 | switch dim 16 | case 1 17 | pmid=(p(t(:,1),:)+p(t(:,2),:))/2; 18 | t1=t(:,1); 19 | t2=t(:,2); 20 | t12=(1:nt)'+np; 21 | t=[t1,t12; 22 | t12,t2]; 23 | p=[p;pmid]; 24 | case 2 25 | pair=[t(:,[1,2]);t(:,[1,3]);t(:,[2,3])]; 26 | [pair,pairi,pairj]=unique(sort(pair,2),'rows'); 27 | pmid=(p(pair(:,1),:)+p(pair(:,2),:))/2; 28 | t1=t(:,1); 29 | t2=t(:,2); 30 | t3=t(:,3); 31 | t12=pairj(1:nt)+np; 32 | t13=pairj(nt+1:2*nt)+np; 33 | t23=pairj(2*nt+1:3*nt)+np; 34 | 35 | t=[t1,t12,t13; 36 | t12,t23,t13; 37 | t2,t23,t12; 38 | t3,t13,t23]; 39 | p=[p;pmid]; 40 | case 3 41 | error('Not implemented.'); 42 | end 43 | 44 | if ~isempty(fd) 45 | for iproj=1:5 46 | p=bndproj(p,t,fd,varargin{:}); 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /distmesh/dexpr.m: -------------------------------------------------------------------------------- 1 | function d=dexpr(p,fin,nit,alpha) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | if nargin<3, nit=20; end 6 | if nargin<4, alpha=0.1; end 7 | 8 | fx=inline(vectorize(maple(['diff(',fin,',x)'])),'x','y'); 9 | fy=inline(vectorize(maple(['diff(',fin,',y)'])),'x','y'); 10 | fxx=inline(vectorize(maple(['diff(',fin,',x$2)'])),'x','y'); 11 | fyy=inline(vectorize(maple(['diff(',fin,',y$2)'])),'x','y'); 12 | fxy=inline(vectorize(maple(['diff(diff(',fin,',x),y)'])),'x','y'); 13 | f=inline(vectorize(fin),'x','y'); 14 | 15 | x0=p(:,1); 16 | y0=p(:,2); 17 | x=x0; 18 | y=y0; 19 | for it=1:nit 20 | cf=feval(f,x,y); 21 | cfx=feval(fx,x,y); 22 | cfy=feval(fy,x,y); 23 | cfxx=feval(fxx,x,y); 24 | cfxy=feval(fxy,x,y); 25 | cfyy=feval(fyy,x,y); 26 | 27 | F1=cf; 28 | F2=(x-x0).*cfy-(y-y0).*cfx; 29 | J11=cfx; 30 | J12=cfy; 31 | J21=cfy+(x-x0).*cfxy-(y-y0).*cfxx; 32 | J22=-cfx-(y-y0).*cfxy+(x-x0).*cfyy; 33 | 34 | detJ=J11.*J22-J12.*J21; 35 | detJ(detJ==0)=inf; 36 | 37 | x=x-alpha*(J22.*F1-J21.*F2)./detJ; 38 | y=y-alpha*(-J12.*F1+J11.*F2)./detJ; 39 | end 40 | 41 | d=sqrt((x-x0).^2+(y-y0).^2).*sign(feval(f,x0,y0)); 42 | -------------------------------------------------------------------------------- /stlTools/stlGetVerts.m: -------------------------------------------------------------------------------- 1 | function list = stlGetVerts(v, f, mode) 2 | %GETVERTS returns the vertices that are 'opened' or 'closed' depending on 3 | %the 'mode'. An 'open' vertice is the one that defines an open side. An 4 | %open side is the one that only takes part of one triangle 5 | %V is the Nx3 array of vertices 6 | %F is the Mx3 array of faces 7 | %MODE can be 'opened' or 'closed' depending of the kind of vertices to list 8 | %LIST is the list of 'opened' or 'closed' vertices 9 | 10 | sides = sort([[f(:,1) f(:,2)]; ... 11 | [f(:,2) f(:,3)]; ... 12 | [f(:,3) f(:,1)]],2); 13 | 14 | [C,ia,ic] = unique(sides,'rows'); 15 | ind_all = sort(ic); % open and closed sides 16 | ind_rep = find(diff(ind_all) == 0); 17 | ind_cls = ind_all(ind_rep); % closed sides 18 | sides_cls = C(ind_cls,:); 19 | ind_rep = [ind_rep; ind_rep+1]; 20 | ind_opn = ind_all; 21 | ind_opn(ind_rep) = []; % open sides 22 | sides_opn = C(ind_opn,:); 23 | 24 | switch mode, 25 | case'opened', 26 | list = v(unique(sides_opn(:)),:); 27 | case 'closed', 28 | list = v(unique(sides_cls(:)),:); 29 | otherwise, 30 | error('getVerts:InvalidMode','The ''mode'' valid values are ''opened'' or ''closed'''); 31 | end -------------------------------------------------------------------------------- /code/cotangent_laplacian.m: -------------------------------------------------------------------------------- 1 | function L = cotangent_laplacian(v,f) 2 | % Compute the cotagent Laplacian of a mesh. 3 | % 4 | % If you use this code in your own work, please cite the following paper: 5 | % [1] P. T. Choi and L. M. Lui, 6 | % "Fast Disk Conformal Parameterization of Simply-Connected Open Surfaces." 7 | % Journal of Scientific Computing, 65(3), pp. 1065-1090, 2015. 8 | % 9 | % Copyright (c) 2014-2018, Gary Pui-Tung Choi 10 | % https://scholar.harvard.edu/choi 11 | 12 | 13 | nv = length(v); 14 | 15 | f1 = f(:,1); f2 = f(:,2); f3 = f(:,3); 16 | 17 | l1 = sqrt(sum((v(f2,:) - v(f3,:)).^2,2)); 18 | l2 = sqrt(sum((v(f3,:) - v(f1,:)).^2,2)); 19 | l3 = sqrt(sum((v(f1,:) - v(f2,:)).^2,2)); 20 | 21 | s = (l1 + l2 + l3)*0.5; 22 | area = sqrt( s.*(s-l1).*(s-l2).*(s-l3)); 23 | 24 | cot12 = (l1.^2 + l2.^2 - l3.^2)./area/2; 25 | cot23 = (l2.^2 + l3.^2 - l1.^2)./area/2; 26 | cot31 = (l1.^2 + l3.^2 - l2.^2)./area/2; 27 | diag1 = -cot12-cot31; diag2 = -cot12-cot23; diag3 = -cot31-cot23; 28 | 29 | II = [f1; f2; f2; f3; f3; f1; f1; f2; f3]; 30 | JJ = [f2; f1; f3; f2; f1; f3; f1; f2; f3]; 31 | V = [cot12; cot12; cot23; cot23; cot31; cot31; diag1; diag2; diag3]; 32 | L = sparse(II,JJ,V,nv,nv); 33 | 34 | end -------------------------------------------------------------------------------- /distmesh/meshdemond.m: -------------------------------------------------------------------------------- 1 | function meshdemond 2 | %MESHDEMOND distmeshnd examples. 3 | 4 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 5 | 6 | rand('state',1); % Always the same results 7 | set(gcf,'rend','opengl'); 8 | 9 | disp('(9) 3-D Unit ball') 10 | fd=inline('sqrt(sum(p.^2,2))-1','p'); 11 | [p,t]=distmeshnd(fd,@huniform,0.2,[-1,-1,-1;1,1,1],[]); 12 | post(p,t) 13 | 14 | disp('(10) Cylinder with hole') 15 | [p,t]=distmeshnd(@fd10,@fh10,0.1,[-1,-1,-1;1,1,1],[]); 16 | post(p,t) 17 | 18 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 19 | 20 | function post(p,t) 21 | 22 | disp(sprintf(' (press any key)')) 23 | disp(' ') 24 | pause 25 | 26 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 27 | 28 | function d=fd10(p) 29 | 30 | r=sqrt(p(:,1).^2+p(:,2).^2); 31 | z=p(:,3); 32 | 33 | d1=r-1; 34 | d2=z-1; 35 | d3=-z-1; 36 | d4=sqrt(d1.^2+d2.^2); 37 | d5=sqrt(d1.^2+d3.^2); 38 | d=dintersect(dintersect(d1,d2),d3); 39 | ix=d1>0 & d2>0; 40 | d(ix)=d4(ix); 41 | ix=d1>0 & d3>0; 42 | d(ix)=d5(ix); 43 | 44 | d=ddiff(d,dsphere(p,0,0,0,0.5)); 45 | 46 | function h=fh10(p) 47 | 48 | h1=4*sqrt(sum(p.^2,2))-1; 49 | h=min(h1,2); 50 | -------------------------------------------------------------------------------- /spherical_parametrisation/mfile/cotangent_laplacian.m: -------------------------------------------------------------------------------- 1 | function L = cotangent_laplacian(v,f) 2 | % Compute the cotangent Laplacian of a mesh. 3 | % 4 | % If you use this code in your own work, please cite the following paper: 5 | % [1] P. T. Choi, K. C. Lam, and L. M. Lui, 6 | % "FLASH: Fast Landmark Aligned Spherical Harmonic Parameterization for Genus-0 Closed Brain Surfaces." 7 | % SIAM Journal on Imaging Sciences, vol. 8, no. 1, pp. 67-94, 2015. 8 | % 9 | % Copyright (c) 2013-2018, Gary Pui-Tung Choi 10 | % https://scholar.harvard.edu/choi 11 | 12 | nv = length(v); 13 | 14 | f1 = f(:,1); f2 = f(:,2); f3 = f(:,3); 15 | 16 | l1 = sqrt(sum((v(f2,:) - v(f3,:)).^2,2)); 17 | l2 = sqrt(sum((v(f3,:) - v(f1,:)).^2,2)); 18 | l3 = sqrt(sum((v(f1,:) - v(f2,:)).^2,2)); 19 | 20 | s = (l1 + l2 + l3)*0.5; 21 | area = sqrt( s.*(s-l1).*(s-l2).*(s-l3)); 22 | 23 | cot12 = (l1.^2 + l2.^2 - l3.^2)./area/2; 24 | cot23 = (l2.^2 + l3.^2 - l1.^2)./area/2; 25 | cot31 = (l1.^2 + l3.^2 - l2.^2)./area/2; 26 | diag1 = -cot12-cot31; diag2 = -cot12-cot23; diag3 = -cot31-cot23; 27 | 28 | II = [f1; f2; f2; f3; f3; f1; f1; f2; f3]; 29 | JJ = [f2; f1; f3; f2; f1; f3; f1; f2; f3]; 30 | V = [cot12; cot12; cot23; cot23; cot31; cot31; diag1; diag2; diag3]; 31 | L = sparse(II,JJ,V,nv,nv); 32 | 33 | end -------------------------------------------------------------------------------- /code/gpp_clean_mesh.m: -------------------------------------------------------------------------------- 1 | %% clean mesh 2 | % Clean mesh by removing unreferenced vertex, and renumber vertex index in 3 | % face. 4 | % 5 | %% Syntax 6 | % [face_new,vertex_new,father] = clean_mesh(face,vertex) 7 | % 8 | %% Description 9 | % face : double array, nf x 3, connectivity of mesh 10 | % vertex: double array, nv x 3, vertex of mesh, there may have unreferenced 11 | % vertex 12 | % 13 | % face_new : double array, nf x 3, connectivity of new mesh after clean 14 | % vertex_new: double array, nv' x 3, vertex of new mesh. vertex number may 15 | % less than original mesh 16 | % father : double array, nv' x 1, father indicates the vertex on original 17 | % mesh that new vertex comes from. 18 | % 19 | %% Contribution 20 | % Author : Wen Cheng Feng 21 | % Created: 2014/03/17 22 | % Revised: 2014/03/24 by Wen, add doc 23 | % 24 | % Copyright 2014 Computational Geometry Group 25 | % Department of Mathematics, CUHK 26 | % http://www.math.cuhk.edu.hk/~lmlui 27 | 28 | function [face_new,vertex_new,father] = gpp_clean_mesh(face,vertex) 29 | % remove unreferenced vertex 30 | father = unique(face); 31 | index = zeros(max(father),1); 32 | index(father) = (1:size(father,1)); 33 | face_new = index(face); 34 | vertex_new = vertex(father,:); 35 | -------------------------------------------------------------------------------- /spherical_parametrisation/extension/compute_vertex_normal.m: -------------------------------------------------------------------------------- 1 | function Nv = compute_vertex_normal(v,f) 2 | % Compute the vertex normal of a mesh. 3 | % 4 | % If you use this code in your own work, please cite the following paper: 5 | % [1] P. T. Choi, K. C. Lam, and L. M. Lui, 6 | % "FLASH: Fast Landmark Aligned Spherical Harmonic Parameterization for Genus-0 Closed Brain Surfaces." 7 | % SIAM Journal on Imaging Sciences, vol. 8, no. 1, pp. 67-94, 2015. 8 | % 9 | % Copyright (c) 2013-2018, Gary Pui-Tung Choi 10 | % https://scholar.harvard.edu/choi 11 | 12 | nv = length(v); 13 | nf = length(f); 14 | 15 | f1 = f(:,1); f2 = f(:,2); f3 = f(:,3); 16 | e1 = v(f2,:) - v(f1,:); 17 | e2 = v(f3,:) - v(f1,:); 18 | 19 | % take cross product for each pair of successive edges 20 | cross12 = cross(e1,e2); 21 | area = abs(1/2*(cross12(:,1).^2+cross12(:,2).^2+cross12(:,3).^2).^(1/2)); 22 | % obtain face normal 23 | Nf = [(1./(2*area)).*cross12(:,1), (1./(2*area)).*cross12(:,2), (1./(2*area)).*cross12(:,3)]; 24 | 25 | 26 | row = [f3; f1; f2]; 27 | col = [1:nf, 1:nf, 1:nf]'; 28 | val = [area; area; area]'; 29 | M = sparse(row,col,val,nv,nf); 30 | % normalize 31 | vertex_area_sum = sum(M,2); 32 | [Mrow,Mcol,Mval] = find(M); 33 | M = sparse(Mrow,Mcol,Mval./vertex_area_sum(Mrow),nv,nf); 34 | 35 | % obtain vertex normal 36 | Nv = M*Nf; -------------------------------------------------------------------------------- /code/paraview_patch.m: -------------------------------------------------------------------------------- 1 | function fig = paraview_patch(v, f, maps) 2 | % Plot STL meshes with Paraview maps. 3 | % Author: Mahmoud Shaqfa (EPFL) 4 | 5 | fig = figure; 6 | % set(fig,'renderer','painters'); % To save vectorized SVG and PDF formats 7 | 8 | if nargin < 3 9 | patch('Faces',f,'Vertices',v,'FaceColor',[42,63,188]./255,'LineWidth',0.5); 10 | else 11 | patch('Faces',f,'Vertices',v,'FaceColor','interp','FaceVertexCData',maps,... 12 | 'EdgeColor','k', 'LineWidth', 0.1, 'LineStyle', '-'); 13 | % patch('Faces',f,'Vertices',v,'FaceColor','interp','FaceVertexCData',maps,... 14 | % 'EdgeColor','k', 'LineStyle', 'none'); 15 | 16 | % Define a color map similar to Paraview's shades 17 | red_color = zeros(1, 255); green_color = zeros(1, 255); blue_color = zeros(1, 255); 18 | red_color(1:127) = linspace(42,220,127); red_color(128:255) = linspace(220,174,127+1); 19 | green_color(1:127) = linspace(63,220,127); green_color(128:255) = linspace(220,0,127+1); 20 | blue_color(1:127) = linspace(181,220,127); blue_color(128:255) = linspace(220,22,127+1); 21 | 22 | ParaviewMap = [red_color', green_color', blue_color']./255; 23 | colormap(ParaviewMap); 24 | set(gcf,'color','w'); 25 | cb = colorbar; 26 | set(cb,'position',[.15 .1 .05 .2]) 27 | end 28 | axis equal tight off 29 | view(45, 45); 30 | gca.Clipping = 'off'; -------------------------------------------------------------------------------- /code/beltrami_coefficient.m: -------------------------------------------------------------------------------- 1 | function mu = beltrami_coefficient(v, f, map) 2 | % Compute the Beltrami coefficient of a mapping. 3 | % 4 | % If you use this code in your own work, please cite the following paper: 5 | % [1] P. T. Choi and L. M. Lui, 6 | % "Fast Disk Conformal Parameterization of Simply-Connected Open Surfaces." 7 | % Journal of Scientific Computing, 65(3), pp. 1065-1090, 2015. 8 | % 9 | % Copyright (c) 2014-2018, Gary Pui-Tung Choi 10 | % https://scholar.harvard.edu/choi 11 | 12 | 13 | nf = length(f); 14 | Mi = reshape([1:nf;1:nf;1:nf], [1,3*nf]); 15 | Mj = reshape(f', [1,3*nf]); 16 | 17 | e1 = v(f(:,3),1:2) - v(f(:,2),1:2); 18 | e2 = v(f(:,1),1:2) - v(f(:,3),1:2); 19 | e3 = v(f(:,2),1:2) - v(f(:,1),1:2); 20 | 21 | area = (-e2(:,1).*e1(:,2) + e1(:,1).*e2(:,2))'/2; 22 | area = [area;area;area]; 23 | 24 | Mx = reshape([e1(:,2),e2(:,2),e3(:,2)]'./area /2 , [1, 3*nf]); 25 | My = -reshape([e1(:,1),e2(:,1),e3(:,1)]'./area /2 , [1, 3*nf]); 26 | 27 | Dx = sparse(Mi,Mj,Mx); 28 | Dy = sparse(Mi,Mj,My); 29 | 30 | dXdu = Dx*map(:,1); 31 | dXdv = Dy*map(:,1); 32 | dYdu = Dx*map(:,2); 33 | dYdv = Dy*map(:,2); 34 | dZdu = Dx*map(:,3); 35 | dZdv = Dy*map(:,3); 36 | 37 | E = dXdu.^2 + dYdu.^2 + dZdu.^2; 38 | G = dXdv.^2 + dYdv.^2 + dZdv.^2; 39 | F = dXdu.*dXdv + dYdu.*dYdv + dZdu.*dZdv; 40 | mu = (E - G + 2 * 1i * F) ./ (E + G + 2*sqrt(E.*G - F.^2)); 41 | end 42 | -------------------------------------------------------------------------------- /spherical_parametrisation/mfile/beltrami_coefficient.m: -------------------------------------------------------------------------------- 1 | function mu = beltrami_coefficient(v, f, map) 2 | % Compute the Beltrami coefficient of a mapping. 3 | % 4 | % If you use this code in your own work, please cite the following paper: 5 | % [1] P. T. Choi, K. C. Lam, and L. M. Lui, 6 | % "FLASH: Fast Landmark Aligned Spherical Harmonic Parameterization for Genus-0 Closed Brain Surfaces." 7 | % SIAM Journal on Imaging Sciences, vol. 8, no. 1, pp. 67-94, 2015. 8 | % 9 | % Copyright (c) 2013-2018, Gary Pui-Tung Choi 10 | % https://scholar.harvard.edu/choi 11 | 12 | nf = length(f); 13 | Mi = reshape([1:nf;1:nf;1:nf], [1,3*nf]); 14 | Mj = reshape(f', [1,3*nf]); 15 | 16 | e1 = v(f(:,3),1:2) - v(f(:,2),1:2); 17 | e2 = v(f(:,1),1:2) - v(f(:,3),1:2); 18 | e3 = v(f(:,2),1:2) - v(f(:,1),1:2); 19 | 20 | area = (-e2(:,1).*e1(:,2) + e1(:,1).*e2(:,2))'/2; 21 | area = [area;area;area]; 22 | 23 | Mx = reshape([e1(:,2),e2(:,2),e3(:,2)]'./area /2 , [1, 3*nf]); 24 | My = -reshape([e1(:,1),e2(:,1),e3(:,1)]'./area /2 , [1, 3*nf]); 25 | 26 | Dx = sparse(Mi,Mj,Mx); 27 | Dy = sparse(Mi,Mj,My); 28 | 29 | dXdu = Dx*map(:,1); 30 | dXdv = Dy*map(:,1); 31 | dYdu = Dx*map(:,2); 32 | dYdv = Dy*map(:,2); 33 | dZdu = Dx*map(:,3); 34 | dZdv = Dy*map(:,3); 35 | 36 | E = dXdu.^2 + dYdu.^2 + dZdu.^2; 37 | G = dXdv.^2 + dYdv.^2 + dZdv.^2; 38 | F = dXdu.*dXdv + dYdu.*dYdv + dZdu.*dZdv; 39 | mu = (E - G + 2 * 1i * F) ./ (E + G + 2*sqrt(E.*G - F.^2)); 40 | end 41 | -------------------------------------------------------------------------------- /distmesh/dsegment.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 2 | 3 | #include "mex.h" 4 | #include 5 | 6 | #define p(i,j) p[(i)+np*(j)] 7 | #define pv(i,j) pv[(i)+nvs*(j)] 8 | #define ds(i,j) ds[(i)+np*(j)] 9 | 10 | template inline T sqr(T x) { return x*x; } 11 | template inline T dot2(T *a,T *b) { return a[0]*b[0]+a[1]*b[1]; } 12 | template inline T length(T x,T y) { return sqrt(sqr(x)+sqr(y)); } 13 | 14 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 15 | { 16 | int np=mxGetM(prhs[0]); 17 | int nvs=mxGetM(prhs[1]); 18 | double *p=mxGetPr(prhs[0]); 19 | double *pv=mxGetPr(prhs[1]); 20 | 21 | plhs[0]=mxCreateDoubleMatrix(np,nvs-1,mxREAL); 22 | double *ds=mxGetPr(plhs[0]); 23 | 24 | for (int iv=0; iv=c2) 37 | ds(ip,iv)=length(p(ip,0)-pv(iv+1,0),p(ip,1)-pv(iv+1,1)); 38 | else 39 | ds(ip,iv)=length(p(ip,0)-(pv(iv,0)+c1/c2*v[0]), 40 | p(ip,1)-(pv(iv,1)+c1/c2*v[1])); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /stlTools/stlDemo.m: -------------------------------------------------------------------------------- 1 | %% STLDEMO shows how to use the functions included in the toolbox STLTOOLS 2 | 3 | %% EXAMPLE 1.- How to cut a sphere and close the base to get a semisphere 4 | 5 | % load an ascii STL sample file (STLGETFORMAT and STLREADASCII) 6 | [vertices,faces,normals,name] = stlRead('sphere300faces.stl'); 7 | stlPlot(vertices,faces,name); 8 | 9 | % the sphere is centered in the origin 10 | % now we get a list of vertices to be deleted if (x,y,z<0) 11 | minZ = 0; 12 | [rows, ~] = find(vertices(:,3) < minZ); 13 | list = vertices(rows,:); 14 | 15 | % if we delete the list of vertices with z<0, we get an opened semisphere 16 | % (as the base is not closed) 17 | [newv,newf] = stlDelVerts(vertices,faces,list); 18 | stlPlot(newv,newf,name); 19 | 20 | % the next step is to identify a new list with the faces that are opened 21 | % (that means all the sides that belong only to a unique triangle) 22 | list = stlGetVerts(newv,newf,'opened'); 23 | 24 | % finally we generate all the new faces that are needed just to close the 25 | % base of the semisphere 26 | [vsemi,fsemi] = stlAddVerts(newv,newf,list); 27 | stlPlot(vsemi,fsemi,'closed semisphere'); 28 | 29 | %% EXAMPLE 2.- How to get a section of a femur 30 | 31 | [vertices,faces,normals,name] = stlRead('femur_binary.stl'); 32 | stlPlot(vertices,faces,name); 33 | 34 | minX = 1.2; 35 | [rows, ~] = find(vertices(:,1) < minX); 36 | list = vertices(rows,:); 37 | 38 | [newv,newf] = stlDelVerts(vertices,faces,list); 39 | stlPlot(newv,newf,'section of the femur'); 40 | -------------------------------------------------------------------------------- /code/generalized_laplacian.m: -------------------------------------------------------------------------------- 1 | function A = generalized_laplacian(v,f,mu) 2 | 3 | % Compute the generalized Laplacian. 4 | % 5 | % If you use this code in your own work, please cite the following paper: 6 | % [1] P. T. Choi and L. M. Lui, 7 | % "Fast Disk Conformal Parameterization of Simply-Connected Open Surfaces." 8 | % Journal of Scientific Computing, 65(3), pp. 1065-1090, 2015. 9 | % 10 | % Copyright (c) 2014-2018, Gary Pui-Tung Choi 11 | % https://scholar.harvard.edu/choi 12 | 13 | af = (1-2*real(mu)+abs(mu).^2)./(1.0-abs(mu).^2); 14 | bf = -2*imag(mu)./(1.0-abs(mu).^2); 15 | gf = (1+2*real(mu)+abs(mu).^2)./(1.0-abs(mu).^2); 16 | 17 | f0 = f(:,1); f1 = f(:,2); f2 = f(:,3); 18 | 19 | uxv0 = v(f1,2) - v(f2,2); 20 | uyv0 = v(f2,1) - v(f1,1); 21 | uxv1 = v(f2,2) - v(f0,2); 22 | uyv1 = v(f0,1) - v(f2,1); 23 | uxv2 = v(f0,2) - v(f1,2); 24 | uyv2 = v(f1,1) - v(f0,1); 25 | 26 | l = [sqrt(sum(uxv0.^2 + uyv0.^2,2)), sqrt(sum(uxv1.^2 + uyv1.^2,2)), sqrt(sum(uxv2.^2 + uyv2.^2,2))]; 27 | s = sum(l,2)*0.5; 28 | 29 | area = sqrt(s.*(s-l(:,1)).*(s-l(:,2)).*(s-l(:,3))); 30 | 31 | v00 = (af.*uxv0.*uxv0 + 2*bf.*uxv0.*uyv0 + gf.*uyv0.*uyv0)./area; 32 | v11 = (af.*uxv1.*uxv1 + 2*bf.*uxv1.*uyv1 + gf.*uyv1.*uyv1)./area; 33 | v22 = (af.*uxv2.*uxv2 + 2*bf.*uxv2.*uyv2 + gf.*uyv2.*uyv2)./area; 34 | v01 = (af.*uxv1.*uxv0 + bf.*uxv1.*uyv0 + bf.*uxv0.*uyv1 + gf.*uyv1.*uyv0)./area; 35 | v12 = (af.*uxv2.*uxv1 + bf.*uxv2.*uyv1 + bf.*uxv1.*uyv2 + gf.*uyv2.*uyv1)./area; 36 | v20 = (af.*uxv0.*uxv2 + bf.*uxv0.*uyv2 + bf.*uxv2.*uyv0 + gf.*uyv0.*uyv2)./area; 37 | 38 | I = [f0;f1;f2;f0;f1;f1;f2;f2;f0]; 39 | J = [f0;f1;f2;f1;f0;f2;f1;f0;f2]; 40 | V = [v00;v11;v22;v01;v01;v12;v12;v20;v20]/2; 41 | A = sparse(I,J,-V); -------------------------------------------------------------------------------- /spherical_parametrisation/parametrize_surface.m: -------------------------------------------------------------------------------- 1 | function map2 = parametrize_surface(input_file, output_file) 2 | % Demo for mapping a stone surface onto a sphere conformally 3 | % 4 | % Please refer to the following papers: 5 | % 6 | % spherical_conformal_map: 7 | % [1] P. T. Choi, K. C. Lam, and L. M. Lui, 8 | % "FLASH: Fast Landmark Aligned Spherical Harmonic Parameterization for Genus-0 Closed Brain Surfaces." 9 | % SIAM Journal on Imaging Sciences, vol. 8, no. 1, pp. 67-94, 2015. 10 | % 11 | % mobius_area_correction_spherical: 12 | % [2] G. P. T. Choi, Y. Leung-Liu, X. Gu, and L. M. Lui, 13 | % "Parallelizable global conformal parameterization of simply-connected surfaces via partial welding." 14 | % SIAM Journal on Imaging Sciences, 2020. 15 | 16 | if nargin == 0 17 | input_file = 'test_stone.stl'; 18 | output_file = strcat(pwd, '/test_stone_output.stl'); 19 | elseif nargin == 1 20 | output_file = strcat(pwd, '/test_stone_output.stl'); 21 | end 22 | %% Read the input file 23 | [v,f] = stlRead(input_file); 24 | % plot_mesh(v,f); 25 | 26 | %% spherical conformal map [1] 27 | % map = spherical_conformal_map(v,f); 28 | % 29 | % plot_mesh(map,f); 30 | % 31 | % % evaluate the angle distortion 32 | % angle_distortion(v,f,map); 33 | 34 | %% Spherical conformal map [1] combined with the method in our latest work [2] 35 | % the Mobius area correction method further reduces the area distortion of the spherical parameterization while preserving the conformality 36 | map = spherical_conformal_map(v,f); 37 | map2 = mobius_area_correction_spherical(v,f,map); 38 | % plot_mesh(map2,f); 39 | 40 | % evaluate the angle distortion 41 | % angle_distortion(v,f,map2); 42 | stlWrite(output_file, f, map2,'mode','ascii') 43 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spherical-cap-harmonics 2 | This release is part of the paper: 3 | "Spherical Cap Harmonic Analysis (SCHA) for Characterising the Morphology of Rough Surface Patches" 4 | By: Mahmoud Shaqfa, Gary P. T. Choi, Katrin Beyer 5 | 6 | See the article (open access): https://www.sciencedirect.com/science/article/pii/S0032591021006720 7 | 8 | The Matlab codes include implementations for: 9 | - Spherical Cap Harmonics (SCH) 10 | - Spherical Harmonics (SH) 11 | - Hemispherical Harmonics (HSH) 12 | 13 | The code should be readily avilable for running on Matlab (never tested for Octave). The *.m* files that start with *Main_..* are the main interfaces proposed by the authors for running the suggested topics in the paper. They already contain exhaustive comments on the how-to-use. For instructions on the usage of these files please refer to the paper or [Mahmoud Shaqfa](mailto:mahmoud.shaqfa@epfl.ch?subject=[GitHub]%20SCH%20source%20code). 14 | 15 | These codes deal with morphological reconstruction of convex and nonconvex open and closed surfaces. This paper is focusing mainly on dealing with rough surfaces but it also can be used for different applications such as medical imaging. 16 | 17 | A video render of the reconstruction process for the geometry in Fig. 9 of the abovementioned paper can be found here: 18 | 19 | [![Visulaize](http://img.youtube.com/vi/X77XdFxrbOI/0.jpg)](http://www.youtube.com/watch?v=X77XdFxrbOI) 20 | 21 | Please refer to the code and video by citing the paper as follows: 22 | 23 | Mahmoud Shaqfa, Gary P.T. Choi, Katrin Beyer (2021), 24 | Spherical cap harmonic analysis (SCHA) for characterising the morphology of rough surface patches, 25 | Powder Technology, 26 | Volume 393, 27 | Pages 837-856, 28 | ISSN 0032-5910, https://doi.org/10.1016/j.powtec.2021.07.081. 29 | -------------------------------------------------------------------------------- /spherical_parametrisation/extension/area_distortion.m: -------------------------------------------------------------------------------- 1 | function distortion = area_distortion(v,f,map) 2 | 3 | % Calculate and visualize the area distortion log(area_map/area_v) 4 | % 5 | % Input: 6 | % v: nv x 3 vertex coordinates of a genus-0 triangle mesh 7 | % f: nf x 3 triangulations of a genus-0 triangle mesh 8 | % map: nv x 3 vertex coordinates of the mapping result 9 | % 10 | % Output: 11 | % distortion: 3*nf x 1 area differences 12 | % 13 | % If you use this code in your own work, please cite the following paper: 14 | % [1] G. P. T. Choi, Y. Leung-Liu, X. Gu, and L. M. Lui, 15 | % "Parallelizable global conformal parameterization of simply-connected surfaces via partial welding." 16 | % SIAM Journal on Imaging Sciences, 2020. 17 | % 18 | % Copyright (c) 2018-2020, Gary Pui-Tung Choi 19 | % https://scholar.harvard.edu/choi 20 | 21 | nv = length(v); 22 | nv2 = length(map); 23 | 24 | if nv ~= nv2 25 | error('Error: The two meshes are of different size.'); 26 | end 27 | 28 | if size(v,2) == 1 29 | v = [real(v),imag(v),zeros(length(v),1)]; 30 | elseif size(v,2) == 2 31 | v = [v,zeros(length(v),1)]; 32 | end 33 | 34 | if size(map,2) == 1 35 | map = [real(map),imag(map),zeros(length(map),1)]; 36 | elseif size(map,2) == 2 37 | map = [map,zeros(length(map),1)]; 38 | end 39 | 40 | % calculate area of v 41 | area_v = face_area(f,v); 42 | v = v*sqrt(4*pi/sum(area_v)); 43 | area_v = face_area(f,v); 44 | 45 | % calculate area of map 46 | area_map = face_area(f,map); 47 | 48 | % calculate the area ratio 49 | distortion = log(area_map./area_v); 50 | 51 | % histogram 52 | figure; 53 | histogram(distortion,30); 54 | xlim([-5 5]) 55 | title('Area Distortion'); 56 | 57 | xlabel('log(final area/initial area)') 58 | ylabel('Number of faces') 59 | set(gca,'FontSize',12); 60 | -------------------------------------------------------------------------------- /stlTools/stlGetFormat.m: -------------------------------------------------------------------------------- 1 | function format = stlGetFormat(fileName) 2 | %STLGETFORMAT identifies the format of the STL file and returns 'binary' or 3 | %'ascii' 4 | 5 | fid = fopen(fileName); 6 | % Check the file size first, since binary files MUST have a size of 84+(50*n) 7 | fseek(fid,0,1); % Go to the end of the file 8 | fidSIZE = ftell(fid); % Check the size of the file 9 | if rem(fidSIZE-84,50) > 0 10 | format = 'ascii'; 11 | else 12 | % Files with a size of 84+(50*n), might be either ascii or binary... 13 | % Read first 80 characters of the file. 14 | % For an ASCII file, the data should begin immediately (give or take a few 15 | % blank lines or spaces) and the first word must be 'solid'. 16 | % For a binary file, the first 80 characters contains the header. 17 | % It is bad practice to begin the header of a binary file with the word 18 | % 'solid', so it can be used to identify whether the file is ASCII or 19 | % binary. 20 | fseek(fid,0,-1); % go to the beginning of the file 21 | header = strtrim(char(fread(fid,80,'uchar')')); % trim leading and trailing spaces 22 | isSolid = strcmp(header(1:min(5,length(header))),'solid'); % take first 5 char 23 | fseek(fid,-80,1); % go to the end of the file minus 80 characters 24 | tail = char(fread(fid,80,'uchar')'); 25 | isEndSolid = findstr(tail,'endsolid'); 26 | 27 | % Double check by reading the last 80 characters of the file. 28 | % For an ASCII file, the data should end (give or take a few 29 | % blank lines or spaces) with 'endsolid '. 30 | % If the last 80 characters contains the word 'endsolid' then this 31 | % confirms that the file is indeed ASCII. 32 | if isSolid & isEndSolid 33 | format = 'ascii'; 34 | else 35 | format = 'binary'; 36 | end 37 | end 38 | fclose(fid); -------------------------------------------------------------------------------- /stlTools/stlReadAscii.m: -------------------------------------------------------------------------------- 1 | function [v, f, n, name] = stlReadAscii(fileName) 2 | %STLREADASCII reads a STL file written in ASCII format 3 | %V are the vertices 4 | %F are the faces 5 | %N are the normals 6 | %NAME is the name of the STL object (NOT the name of the STL file) 7 | 8 | %====================== 9 | % STL ascii file format 10 | %====================== 11 | % ASCII STL files have the following structure. Technically each facet 12 | % could be any 2D shape, but in practice only triangular facets tend to be 13 | % used. The present code ONLY works for meshes composed of triangular 14 | % facets. 15 | % 16 | % solid object_name 17 | % facet normal x y z 18 | % outer loop 19 | % vertex x y z 20 | % vertex x y z 21 | % vertex x y z 22 | % endloop 23 | % endfacet 24 | % 25 | % 26 | % 27 | % endsolid object_name 28 | 29 | fid = fopen(fileName); 30 | cellcontent = textscan(fid,'%s','delimiter','\n'); % read all the file and put content in cells 31 | content = cellcontent{:}(logical(~strcmp(cellcontent{:},''))); % remove all blank lines 32 | fclose(fid); 33 | 34 | % read the STL name 35 | line1 = char(content(1)); 36 | if (size(line1,2) >= 7) 37 | name = line1(7:end); 38 | else 39 | name = 'Unnamed Object'; 40 | end 41 | 42 | % read the vector normals 43 | normals = char(content(logical(strncmp(content,'facet normal',12)))); 44 | n = str2num(normals(:,13:end)); 45 | 46 | % read the vertex coordinates (vertices) 47 | vertices = char(content(logical(strncmp(content,'vertex',6)))); 48 | v = str2num(vertices(:,7:end)); 49 | nvert = size(vertices,1); % number of vertices 50 | nfaces = sum(strcmp(content,'endfacet')); % number of faces 51 | if (nvert == 3*nfaces) 52 | f = reshape(1:nvert,[3 nfaces])'; % create faces 53 | end 54 | 55 | % slim the file (delete duplicated vertices) 56 | [v,f] = stlSlimVerts(v,f); -------------------------------------------------------------------------------- /distmesh/simpplot.m: -------------------------------------------------------------------------------- 1 | function simpplot(p,t,expr,bcol,icol,nodes,tris) 2 | 3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 4 | 5 | dim=size(p,2); 6 | switch dim 7 | case 2 8 | if nargin<4 | isempty(bcol), bcol=[.8,.9,1]; end 9 | if nargin<5 | isempty(icol), icol=[0,0,0]; end 10 | if nargin<6, nodes=0; end 11 | if nargin<7, tris=0; end 12 | 13 | trimesh(t,p(:,1),p(:,2),0*p(:,1),'facecolor',bcol,'edgecolor','k'); 14 | if nodes==1 15 | line(p(:,1),p(:,2),'linest','none','marker','.','col',icol,'markers',24); 16 | elseif nodes==2 17 | for ip=1:size(p,1) 18 | txtpars={'fontname','times','fontsize',12}; 19 | text(p(ip,1),p(ip,2),num2str(ip),txtpars{:}); 20 | end 21 | end 22 | if tris==2 23 | for it=1:size(t,1) 24 | pmid=mean(p(t(it,:),:),1); 25 | txtpars={'fontname','times','fontsize',12,'horizontala','center'}; 26 | text(pmid(1),pmid(2),num2str(it),txtpars{:}); 27 | end 28 | end 29 | view(2) 30 | axis equal 31 | axis off 32 | ax=axis;axis(ax*1.001); 33 | case 3 34 | if nargin<4 | isempty(bcol), bcol=[.8,.9,1]; end 35 | if nargin<5 | isempty(icol), icol=[.9,.8,1]; end 36 | 37 | if size(t,2)==4 38 | tri1=surftri(p,t); 39 | if nargin>2 & ~isempty(expr) 40 | incl=find(eval(expr)); 41 | t=t(any(ismember(t,incl),2),:); 42 | tri1=tri1(any(ismember(tri1,incl),2),:); 43 | tri2=surftri(p,t); 44 | tri2=setdiff(tri2,tri1,'rows'); 45 | h=trimesh(tri2,p(:,1),p(:,2),p(:,3)); 46 | set(h,'facecolor',icol,'edgecolor','k'); 47 | hold on 48 | end 49 | else 50 | tri1=t; 51 | if nargin>2 & ~isempty(expr) 52 | incl=find(eval(expr)); 53 | tri1=tri1(any(ismember(tri1,incl),2),:); 54 | end 55 | end 56 | h=trimesh(tri1,p(:,1),p(:,2),p(:,3)); 57 | hold off 58 | set(h,'facecolor',bcol,'edgecolor','k'); 59 | axis equal 60 | cameramenu 61 | otherwise 62 | error('Unimplemented dimension.'); 63 | end 64 | -------------------------------------------------------------------------------- /code/linear_beltrami_solver.m: -------------------------------------------------------------------------------- 1 | function map = linear_beltrami_solver(v,f,mu,landmark,target) 2 | % Linear Beltrami solver. 3 | % 4 | % If you use this code in your own work, please cite the following paper: 5 | % [1] P. T. Choi and L. M. Lui, 6 | % "Fast Disk Conformal Parameterization of Simply-Connected Open Surfaces." 7 | % Journal of Scientific Computing, 65(3), pp. 1065-1090, 2015. 8 | % 9 | % Copyright (c) 2014-2018, Gary Pui-Tung Choi 10 | % https://scholar.harvard.edu/choi 11 | 12 | af = (1-2*real(mu)+abs(mu).^2)./(1.0-abs(mu).^2); 13 | bf = -2*imag(mu)./(1.0-abs(mu).^2); 14 | gf = (1+2*real(mu)+abs(mu).^2)./(1.0-abs(mu).^2); 15 | 16 | f0 = f(:,1); f1 = f(:,2); f2 = f(:,3); 17 | 18 | uxv0 = v(f1,2) - v(f2,2); 19 | uyv0 = v(f2,1) - v(f1,1); 20 | uxv1 = v(f2,2) - v(f0,2); 21 | uyv1 = v(f0,1) - v(f2,1); 22 | uxv2 = v(f0,2) - v(f1,2); 23 | uyv2 = v(f1,1) - v(f0,1); 24 | 25 | l = [sqrt(sum(uxv0.^2 + uyv0.^2,2)), sqrt(sum(uxv1.^2 + uyv1.^2,2)), sqrt(sum(uxv2.^2 + uyv2.^2,2))]; 26 | s = sum(l,2)*0.5; 27 | 28 | area = sqrt(s.*(s-l(:,1)).*(s-l(:,2)).*(s-l(:,3))); 29 | 30 | v00 = (af.*uxv0.*uxv0 + 2*bf.*uxv0.*uyv0 + gf.*uyv0.*uyv0)./area; 31 | v11 = (af.*uxv1.*uxv1 + 2*bf.*uxv1.*uyv1 + gf.*uyv1.*uyv1)./area; 32 | v22 = (af.*uxv2.*uxv2 + 2*bf.*uxv2.*uyv2 + gf.*uyv2.*uyv2)./area; 33 | v01 = (af.*uxv1.*uxv0 + bf.*uxv1.*uyv0 + bf.*uxv0.*uyv1 + gf.*uyv1.*uyv0)./area; 34 | v12 = (af.*uxv2.*uxv1 + bf.*uxv2.*uyv1 + bf.*uxv1.*uyv2 + gf.*uyv2.*uyv1)./area; 35 | v20 = (af.*uxv0.*uxv2 + bf.*uxv0.*uyv2 + bf.*uxv2.*uyv0 + gf.*uyv0.*uyv2)./area; 36 | 37 | I = [f0;f1;f2;f0;f1;f1;f2;f2;f0]; 38 | J = [f0;f1;f2;f1;f0;f2;f1;f0;f2]; 39 | V = [v00;v11;v22;v01;v01;v12;v12;v20;v20]/2; 40 | A = sparse(I,J,-V); 41 | 42 | targetc = target(:,1) + 1i*target(:,2); 43 | b = -A(:,landmark)*targetc; 44 | b(landmark) = targetc; 45 | A(landmark,:) = 0; A(:,landmark) = 0; 46 | A = A + sparse(landmark,landmark,ones(length(landmark),1), size(A,1), size(A,2)); 47 | map = A\b; 48 | map = [real(map),imag(map)]; 49 | 50 | end 51 | -------------------------------------------------------------------------------- /spherical_parametrisation/mfile/linear_beltrami_solver.m: -------------------------------------------------------------------------------- 1 | function map = linear_beltrami_solver(v,f,mu,landmark,target) 2 | % Linear Beltrami solver. 3 | % 4 | % If you use this code in your own work, please cite the following paper: 5 | % [1] P. T. Choi, K. C. Lam, and L. M. Lui, 6 | % "FLASH: Fast Landmark Aligned Spherical Harmonic Parameterization for Genus-0 Closed Brain Surfaces." 7 | % SIAM Journal on Imaging Sciences, vol. 8, no. 1, pp. 67-94, 2015. 8 | % 9 | % Copyright (c) 2013-2018, Gary Pui-Tung Choi 10 | % https://scholar.harvard.edu/choi 11 | 12 | af = (1-2*real(mu)+abs(mu).^2)./(1.0-abs(mu).^2); 13 | bf = -2*imag(mu)./(1.0-abs(mu).^2); 14 | gf = (1+2*real(mu)+abs(mu).^2)./(1.0-abs(mu).^2); 15 | 16 | f0 = f(:,1); f1 = f(:,2); f2 = f(:,3); 17 | 18 | uxv0 = v(f1,2) - v(f2,2); 19 | uyv0 = v(f2,1) - v(f1,1); 20 | uxv1 = v(f2,2) - v(f0,2); 21 | uyv1 = v(f0,1) - v(f2,1); 22 | uxv2 = v(f0,2) - v(f1,2); 23 | uyv2 = v(f1,1) - v(f0,1); 24 | 25 | l = [sqrt(sum(uxv0.^2 + uyv0.^2,2)), sqrt(sum(uxv1.^2 + uyv1.^2,2)), sqrt(sum(uxv2.^2 + uyv2.^2,2))]; 26 | s = sum(l,2)*0.5; 27 | 28 | area = sqrt(s.*(s-l(:,1)).*(s-l(:,2)).*(s-l(:,3))); 29 | 30 | v00 = (af.*uxv0.*uxv0 + 2*bf.*uxv0.*uyv0 + gf.*uyv0.*uyv0)./area; 31 | v11 = (af.*uxv1.*uxv1 + 2*bf.*uxv1.*uyv1 + gf.*uyv1.*uyv1)./area; 32 | v22 = (af.*uxv2.*uxv2 + 2*bf.*uxv2.*uyv2 + gf.*uyv2.*uyv2)./area; 33 | v01 = (af.*uxv1.*uxv0 + bf.*uxv1.*uyv0 + bf.*uxv0.*uyv1 + gf.*uyv1.*uyv0)./area; 34 | v12 = (af.*uxv2.*uxv1 + bf.*uxv2.*uyv1 + bf.*uxv1.*uyv2 + gf.*uyv2.*uyv1)./area; 35 | v20 = (af.*uxv0.*uxv2 + bf.*uxv0.*uyv2 + bf.*uxv2.*uyv0 + gf.*uyv0.*uyv2)./area; 36 | 37 | I = [f0;f1;f2;f0;f1;f1;f2;f2;f0]; 38 | J = [f0;f1;f2;f1;f0;f2;f1;f0;f2]; 39 | V = [v00;v11;v22;v01;v01;v12;v12;v20;v20]/2; 40 | A = sparse(I,J,-V); 41 | 42 | targetc = target(:,1) + 1i*target(:,2); 43 | b = -A(:,landmark)*targetc; 44 | b(landmark) = targetc; 45 | A(landmark,:) = 0; A(:,landmark) = 0; 46 | A = A + sparse(landmark,landmark,ones(length(landmark),1), size(A,1), size(A,2)); 47 | map = A\b; 48 | map = [real(map),imag(map)]; 49 | 50 | end 51 | -------------------------------------------------------------------------------- /distmesh/dellipse.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 2 | 3 | #include "mex.h" 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | template inline T sqr(T x) { return x*x; } 10 | typedef ptrdiff_t szint; 11 | 12 | void roots(double *pol,double *rr,double *ri,szint n); 13 | double dellipse(double x0,double y0,double a,double b); 14 | 15 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 16 | { 17 | szint np=mxGetM(prhs[0]); 18 | double *p=mxGetPr(prhs[0]); 19 | double *axes=mxGetPr(prhs[1]); 20 | plhs[0]=mxCreateDoubleMatrix(np,1,mxREAL); 21 | double *d=mxGetPr(plhs[0]); 22 | 23 | for (int ip=0; ip 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | template inline T sqr(T x) { return x*x; } 10 | typedef ptrdiff_t szint; 11 | 12 | void roots(double *pol,double *rr,double *ri,szint n); 13 | double dellipsoid(double x0,double y0,double z0,double a,double b,double c); 14 | 15 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 16 | { 17 | int np=mxGetM(prhs[0]); 18 | double *p=mxGetPr(prhs[0]); 19 | double *axes=mxGetPr(prhs[1]); 20 | plhs[0]=mxCreateDoubleMatrix(np,1,mxREAL); 21 | double *d=mxGetPr(plhs[0]); 22 | 23 | for (int ip=0; ip1)|(H<0) % Hurst parameter error check 31 | error('Hurst parameter must be between 0 and 1') 32 | end 33 | 34 | R=2; % [0,R]^2 grid, may have to extract only [0,R/2]^2 35 | m=n; % size of grid is m*n; covariance matrix is m^2*n^2 36 | tx=[1:n]/n*R; ty=[1:m]/m*R; % create grid for field 37 | Rows=zeros(m,n); 38 | for i=1:n 39 | for j=1:m % rows of blocks of cov matrix 40 | Rows(j,i)=rho([tx(i),ty(j)],[tx(1),ty(1)],R,2*H); 41 | end 42 | end 43 | BlkCirc_row=[Rows, Rows(:,end-1:-1:2); 44 | Rows(end-1:-1:2,:), Rows(end-1:-1:2,end-1:-1:2)]; 45 | % compute eigen-values 46 | lam=real(fft2(BlkCirc_row))/(4*(m-1)*(n-1)); 47 | lam=sqrt(lam); 48 | % generate field with covariance given by block circular matrix 49 | Z=complex(randn(2*(m-1),2*(n-1)),randn(2*(m-1),2*(n-1))); 50 | F=fft2(lam.*Z); 51 | F=F(1:m,1:n); % extract sub-block with desired covariance 52 | [out,c0,c2]=rho([0,0],[0,0],R,2*H); 53 | field1=real(F); field2=imag(F); % two independent fields 54 | field1=field1-field1(1,1); % set field zero at origin 55 | field2=field2-field2(1,1); % set field zero at origin 56 | % make correction for embedding with a term c2*r^2 57 | field1=field1 + kron(ty'*randn,tx*randn)*sqrt(2*c2); 58 | field2=field2 + kron(ty'*randn,tx*randn)*sqrt(2*c2); 59 | [X,Y]=meshgrid(tx,ty); 60 | field1((X.^2+Y.^2)>1)=nan;field2((X.^2+Y.^2)>1)=nan; 61 | field1=field1(1:n/2,1:m/2);field2=field2(1:n/2,1:m/2); 62 | tx=tx(1:n/2);ty=ty(1:m/2); 63 | if nargout==0 64 | surf(tx,ty,field1,'EdgeColor','none') 65 | title('Fractional Gaussian Field on Unit Disk') 66 | colormap bone 67 | stl_name = ['input_geom/fractal_surface_H_', num2str(H), '.stl']; 68 | stlWrite(stl_name, tx,ty, field1) 69 | [v, f] = stlRead(stl_name); 70 | paraview_patch(v,f, v(:,3)) 71 | end 72 | end 73 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 74 | function [out,c0,c2]=rho(x,y,R,alpha) 75 | % embedding of covariance function on a [0,R]^2 grid 76 | if alpha<=1.5 % alpha=2*H, where H is the Hurst parameter 77 | beta=0;c2=alpha/2;c0=1-alpha/2; 78 | else % parameters ensure piecewise function twice differentiable 79 | beta=alpha*(2-alpha)/(3*R*(R^2-1)); c2=(alpha-beta*(R-1)^2*(R+2))/2; 80 | c0=beta*(R-1)^3+1-c2; 81 | end 82 | % create continuous isotropic function 83 | r=sqrt((x(1)-y(1))^2+(x(2)-y(2))^2); 84 | if r<=1 85 | out=c0-r^alpha+c2*r^2; 86 | elseif r<=R 87 | out=beta*(R-r)^3/r; 88 | else 89 | out=0; 90 | end 91 | end -------------------------------------------------------------------------------- /code/stereographic.m: -------------------------------------------------------------------------------- 1 | %*************************************************************************% 2 | % All rights reserved (C) to the authors: Mahmoud SHAQFA, Gary CHOI % 3 | % and Katrin BEYER % 4 | % % 5 | % M. Shaqfa Contact: % 6 | % Earthquake Engineering and Structural Dynamics Laboratory (EESD), % 7 | % School of Architecture, Civil and Environmental Engineering (ENAC), % 8 | % Ecole polytechnique federale de Lausanne (EPFL), % 9 | % CH-1015 Lausanne, Switzerland. % 10 | % Tel.: +41 21 69 33297 % 11 | % Email: mahmoud.shaqfa@epfl.ch % 12 | % % 13 | % G. Choi Contact: % 14 | % Department of Mathematics, Massachusetts Institute of Technology (MIT) % 15 | % Cambridge, MA, USA % 16 | % Email: ptchoi@mit.edu % 17 | % % 18 | % K. Beyer Contact: % 19 | % Email: katrin.beyer@epfl.ch % 20 | %*************************************************************************% 21 | % This code includes implementations for: % 22 | % - Spherical Cap Harmonics (SCH) % 23 | % - Spherical Harmonics (SH) % 24 | % - HemiSpherical Harmonics (HSH) % 25 | % This code is part of the paper: "Spherical Cap Harmonic Analysis(SCHA)..% 26 | % for Characterising the Morphology of Rough Surface Patches" % 27 | % % 28 | %*************************************************************************% 29 | % This library is free software; you can redistribute it and/or modify % 30 | % it under the terms of the GNU Lesser General Public License as published% 31 | % by the Free Software Foundation; either version 2.1 of the License, or % 32 | % (at your option) any later version. % 33 | % % 34 | % This library is distributed in the hope that it will be useful, % 35 | % but WITHOUT ANY WARRANTY; without even the implied warranty of % 36 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % 37 | % See the GNU Lesser General Public License for more details. % 38 | % You should have received a copy of the GNU Lesser General Public License% 39 | % along with this library; if not, write to the Free Software Foundation, % 40 | % Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA % 41 | %*************************************************************************% 42 | % Author of this file: Gary Choi 43 | function v = stereographic(u) 44 | % STEREOGRAPHIC Stereographic projection. 45 | % v = STEREOGRAPHIC(u), for N-by-2 matrix, projects points in plane to sphere 46 | % ; for N-by-3 matrix, projects points on sphere to plane 47 | if size(u, 2) == 1 48 | u = [real(u), imag(u)]; 49 | end 50 | x = u(:,1); 51 | y = u(:,2); 52 | if size(u,2) < 3 53 | z = 1 + x.^2 + y.^2; 54 | v = [2*x ./ z, 2*y ./ z, (-1 + x.^2 + y.^2) ./ z]; 55 | else 56 | z = u(:,3); 57 | v = [x ./ (1-z), y ./ (1-z)]; 58 | end 59 | end -------------------------------------------------------------------------------- /spherical_parametrisation/extension/iterative_spherical_area_preserving_map.m: -------------------------------------------------------------------------------- 1 | function map = iterative_spherical_area_preserving_map(v,f) 2 | % A method for computing spherical area-preserving map of a genus-0 closed surface 3 | % 4 | % Remark: 5 | % This code is used only for demonstrating a possible extension of our 6 | % spherical conformal (or tutte) map to minimize other distortions. 7 | % The key idea is to use a simple variation of our proposed linear method 8 | % to obtain an initial spherical map, and then solve an energy minimization 9 | % problem to obtain a more area-preserving map. 10 | % Note that the computation is not optimized, and it may take longer time 11 | % than the other linear methods that we proposed 12 | % (spherical_conformal_map/spherical_tutte_map/spherical_area_preserving_map). 13 | % 14 | % Input: 15 | % v: nv x 3 vertex coordinates of a genus-0 triangle mesh 16 | % f: nf x 3 triangulations of a genus-0 triangle mesh 17 | % 18 | % Output: 19 | % map: nv x 3 vertex coordinates of the spherical parameterization 20 | % 21 | % If you use this code in your own work, please cite the following paper: 22 | % [1] P. T. Choi, K. C. Lam, and L. M. Lui, 23 | % "FLASH: Fast Landmark Aligned Spherical Harmonic Parameterization for Genus-0 Closed Brain Surfaces." 24 | % SIAM Journal on Imaging Sciences, vol. 8, no. 1, pp. 67-94, 2015. 25 | % 26 | % Copyright (c) 2013-2018, Gary Pui-Tung Choi 27 | % https://scholar.harvard.edu/choi 28 | 29 | %% set-up 30 | % one may need to change the parameters below to achieve better convergence in the energy minimization scheme 31 | threshold = 1e-6; % in case the final result is not good enough, set a smaller value 32 | max_num_step = 500; % in case the final result is not good enough, set a larger value 33 | dt = 1e-4; % initial guess of time step for line search 34 | 35 | % normalize v 36 | v = v*sqrt(4*pi/sum(face_area(f,v))); 37 | 38 | %% Step 1: Initialization using a simple variation of our proposed linear method 39 | S = spherical_area_preserving_map(v,f); 40 | % depending on applications, one may consider using other versions of our proposed linear method 41 | % S = spherical_conformal_map(v,f); 42 | % S = spherical_tutte_map(f); 43 | 44 | %% Step 2: Local area distortion energy minimization 45 | % We consider minimizing the Chi energy (see Desbrun et al., Eurographics 46 | % 2002) on the sphere. The energy is related to the local area distortion 47 | % of the spherical parameterization. 48 | 49 | % One may change the matrix below to other mesh Laplacian matrices for 50 | % minimizing other types of energy 51 | M = mesh_laplacian(v,f,'authalic'); 52 | 53 | E_old = evaluate_energy(S,M); 54 | step = 0; 55 | fprintf('Iteration #%i: Energy = %.8f\n', [step,E_old]); 56 | 57 | % We use the steepest descent approach to minimize the energy on the sphere 58 | options = optimoptions(@fminunc,'Display','off','Algorithm','quasi-newton'); 59 | 60 | while step < max_num_step 61 | % Compute the absolute derivative 62 | dS = -M*S; 63 | N = compute_vertex_normal(S,f); 64 | dS_dot_N = sum(dS.*N,2); 65 | dS_perp = repmat(dS_dot_N,1,3).*N; 66 | 67 | % projection of the derivative onto the sphere 68 | dS = dS - dS_perp; 69 | 70 | % update S with line search 71 | E = @(k) evaluate_energy(S-k*dS,M); 72 | dt_opt = fminunc(E,dt,options); 73 | S = S - dS*dt_opt; 74 | 75 | % normalize S to ensure a spherical shape 76 | S = [S(:,1)-mean(S(:,1)), S(:,2)-mean(S(:,2)), S(:,3)-mean(S(:,3))]; 77 | S = S./repmat(sqrt(sum(S.^2,2)),1,3); 78 | 79 | step = step + 1; 80 | E_new = evaluate_energy(S,M); 81 | fprintf('Iteration #%i: Energy = %.8f\n', [step,E_new]); 82 | if abs(E_new - E_old) < threshold 83 | break; 84 | end 85 | if E_new - E_old > 1e4 86 | error('The algorithm diverges. Double check the gradient or the step size.'); 87 | end 88 | E_old = E_new; 89 | dt = dt_opt; 90 | end 91 | fprintf('Final Energy = %.8f\n', E_new); 92 | map = S; -------------------------------------------------------------------------------- /code/SH_basis_new.m: -------------------------------------------------------------------------------- 1 | %*************************************************************************% 2 | % All rights reserved (C) to the authors: Mahmoud SHAQFA, Gary CHOI % 3 | % and Katrin BEYER % 4 | % % 5 | % M. Shaqfa Contact: % 6 | % Earthquake Engineering and Structural Dynamics Laboratory (EESD), % 7 | % School of Architecture, Civil and Environmental Engineering (ENAC), % 8 | % Ecole polytechnique federale de Lausanne (EPFL), % 9 | % CH-1015 Lausanne, Switzerland. % 10 | % Tel.: +41 21 69 33297 % 11 | % Email: mahmoud.shaqfa@epfl.ch % 12 | % % 13 | % G. Choi Contact: % 14 | % Department of Mathematics, Massachusetts Institute of Technology (MIT) % 15 | % Cambridge, MA, USA % 16 | % Email: ptchoi@mit.edu % 17 | % % 18 | % K. Beyer Contact: % 19 | % Email: katrin.beyer@epfl.ch % 20 | %*************************************************************************% 21 | % This code includes implementations for: % 22 | % - Spherical Cap Harmonics (SCH) % 23 | % - Spherical Harmonics (SH) % 24 | % - HemiSpherical Harmonics (HSH) % 25 | % This code is part of the paper: "Spherical Cap Harmonic Analysis(SCHA)..% 26 | % for Characterising the Morphology of Rough Surface Patches" % 27 | % % 28 | %*************************************************************************% 29 | % This library is free software; you can redistribute it and/or modify % 30 | % it under the terms of the GNU Lesser General Public License as published% 31 | % by the Free Software Foundation; either version 2.1 of the License, or % 32 | % (at your option) any later version. % 33 | % % 34 | % This library is distributed in the hope that it will be useful, % 35 | % but WITHOUT ANY WARRANTY; without even the implied warranty of % 36 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % 37 | % See the GNU Lesser General Public License for more details. % 38 | % You should have received a copy of the GNU Lesser General Public License% 39 | % along with this library; if not, write to the Free Software Foundation, % 40 | % Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA % 41 | %*************************************************************************% 42 | % Author of this file: Mahmoud S. Shaqfa 43 | function Y_mat = SH_basis_new(phi, theta, degree) 44 | 45 | Y_mat = zeros(size(theta,1), (degree+1)^2); 46 | 47 | P_cos = cos(theta); 48 | 49 | for n = 0:degree 50 | Pn = legendre(n, P_cos)'; 51 | for m = -n:1:n 52 | norm = sqrt(((2*n+1) * factorial(n-abs(m)))/(4 * pi * factorial(n+abs(m)))); 53 | if m < 0 54 | Y_mat(:, n^2 + n + m + 1) = Pn(:, abs(m) + 1) .* exp(1i .* abs(m) .* phi) .* norm; 55 | elseif m == 0 56 | Y_mat(:, n^2 + n + m + 1) = Pn(:, abs(m)+1) .* norm; 57 | elseif m > 0 58 | Y_mat(:, n^2 + n + m + 1) = (-1)^abs(m) .* conj(Pn(:, abs(m) + 1) .* exp(1i .* abs(m) .* phi) .* norm); 59 | end 60 | end 61 | end -------------------------------------------------------------------------------- /code/hypergeom_2F1.m: -------------------------------------------------------------------------------- 1 | %*************************************************************************% 2 | % All rights reserved (C) to the authors: Mahmoud SHAQFA, Gary CHOI % 3 | % and Katrin BEYER % 4 | % % 5 | % M. Shaqfa Contact: % 6 | % Earthquake Engineering and Structural Dynamics Laboratory (EESD), % 7 | % School of Architecture, Civil and Environmental Engineering (ENAC), % 8 | % Ecole polytechnique federale de Lausanne (EPFL), % 9 | % CH-1015 Lausanne, Switzerland. % 10 | % Tel.: +41 21 69 33297 % 11 | % Email: mahmoud.shaqfa@epfl.ch % 12 | % % 13 | % G. Choi Contact: % 14 | % Department of Mathematics, Massachusetts Institute of Technology (MIT) % 15 | % Cambridge, MA, USA % 16 | % Email: ptchoi@mit.edu % 17 | % % 18 | % K. Beyer Contact: % 19 | % Email: katrin.beyer@epfl.ch % 20 | %*************************************************************************% 21 | % This code includes implementations for: % 22 | % - Spherical Cap Harmonics (SCH) % 23 | % - Spherical Harmonics (SH) % 24 | % - HemiSpherical Harmonics (HSH) % 25 | % This code is part of the paper: "Spherical Cap Harmonic Analysis(SCHA)..% 26 | % for Characterising the Morphology of Rough Surface Patches" % 27 | % % 28 | %*************************************************************************% 29 | % This library is free software; you can redistribute it and/or modify % 30 | % it under the terms of the GNU Lesser General Public License as published% 31 | % by the Free Software Foundation; either version 2.1 of the License, or % 32 | % (at your option) any later version. % 33 | % % 34 | % This library is distributed in the hope that it will be useful, % 35 | % but WITHOUT ANY WARRANTY; without even the implied warranty of % 36 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % 37 | % See the GNU Lesser General Public License for more details. % 38 | % You should have received a copy of the GNU Lesser General Public License% 39 | % along with this library; if not, write to the Free Software Foundation, % 40 | % Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA % 41 | %*************************************************************************% 42 | % Author of this file: Mahmoud S. Shaqfa 43 | 44 | function y = hypergeom_2F1(k,m,l,x,N_eps1,N_eps2) 45 | % This is a hybrid hypergeometric function 2F1. 46 | % For a, b, or c <= 20, we use Taylor power series, for the rest we use the 47 | % RK-integration method, 48 | % The implementation is vectorized and re-written from the codes associated 49 | % with the paper: 50 | % "Numerical methods for the computation of the confluent and Gauss 51 | % hypergeometric functions", John W. Pearson, Sheehan Olver & Mason A. Porter. 52 | 53 | if nargin == 5 54 | N_eps2 = N_eps1; 55 | N_eps1 = 2000; 56 | end 57 | a = m-l; b = m+l+1; c = m+1; 58 | if k <= 12 || k == m 59 | y = taylor_2f1(a, b, c, x, N_eps1); 60 | else 61 | % y = deivpdp2f1(a, b, c, x, N_eps2); 62 | y = hypergeom([m-l, m+l+1], m+1, x); 63 | end 64 | end -------------------------------------------------------------------------------- /code/SCH_analysis.m: -------------------------------------------------------------------------------- 1 | %*************************************************************************% 2 | % All rights reserved (C) to the authors: Mahmoud SHAQFA, Gary CHOI % 3 | % and Katrin BEYER % 4 | % % 5 | % M. Shaqfa Contact: % 6 | % Earthquake Engineering and Structural Dynamics Laboratory (EESD), % 7 | % School of Architecture, Civil and Environmental Engineering (ENAC), % 8 | % Ecole polytechnique federale de Lausanne (EPFL), % 9 | % CH-1015 Lausanne, Switzerland. % 10 | % Tel.: +41 21 69 33297 % 11 | % Email: mahmoud.shaqfa@epfl.ch % 12 | % % 13 | % G. Choi Contact: % 14 | % Department of Mathematics, Massachusetts Institute of Technology (MIT) % 15 | % Cambridge, MA, USA % 16 | % Email: ptchoi@mit.edu % 17 | % % 18 | % K. Beyer Contact: % 19 | % Email: katrin.beyer@epfl.ch % 20 | %*************************************************************************% 21 | % This code includes implementations for: % 22 | % - Spherical Cap Harmonics (SCH) % 23 | % - Spherical Harmonics (SH) % 24 | % - HemiSpherical Harmonics (HSH) % 25 | % This code is part of the paper: "Spherical Cap Harmonic Analysis(SCHA)..% 26 | % for Characterising the Morphology of Rough Surface Patches" % 27 | % % 28 | %*************************************************************************% 29 | % This library is free software; you can redistribute it and/or modify % 30 | % it under the terms of the GNU Lesser General Public License as published% 31 | % by the Free Software Foundation; either version 2.1 of the License, or % 32 | % (at your option) any later version. % 33 | % % 34 | % This library is distributed in the hope that it will be useful, % 35 | % but WITHOUT ANY WARRANTY; without even the implied warranty of % 36 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % 37 | % See the GNU Lesser General Public License for more details. % 38 | % You should have received a copy of the GNU Lesser General Public License% 39 | % along with this library; if not, write to the Free Software Foundation, % 40 | % Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA % 41 | %*************************************************************************% 42 | % Author of this file: Mahmoud S. Shaqfa 43 | function [qm_k, C_mat] = SCH_analysis(K_max, theta_c, eigen_table, thetas, phis, vertices, N_eps) 44 | % This function to expand the coefficients from the provided surface 45 | % patches. 46 | % K_max: the maximum of index degree for the expnasion. 47 | % All angles' inputs are in radians only. 48 | C_mat = zeros(size(thetas,1), (K_max+1)^2); 49 | fprintf("\n\n Compiling the basis functions...\n") 50 | % Combine basis fucntions 51 | for n = 0:K_max 52 | C_mat(:, n^2 + 1:n^2 + 2*n + 1) = spherical_cap_harmonic_basis(n, theta_c, eigen_table, thetas, phis, N_eps); 53 | fprintf("\nCompleted: %3.2f %% for n = %3.0f", ((n+1)^2/(K_max+1)^2*100), n) 54 | end 55 | 56 | % Least Square Fitting (LSF) Method 57 | fprintf("\n\n Computing the Least Square Fit...\n") 58 | qm_k = (C_mat' * C_mat) \ C_mat' * vertices; 59 | end -------------------------------------------------------------------------------- /distmesh/distmeshnd.m: -------------------------------------------------------------------------------- 1 | function [p,t]=distmeshnd(fdist,fh,h,box,fix,varargin) 2 | %DISTMESHND N-D Mesh Generator using Distance Functions. 3 | % [P,T]=DISTMESHND(FDIST,FH,H,BOX,FIX,FDISTPARAMS) 4 | % 5 | % P: Node positions (NxNDIM) 6 | % T: Triangle indices (NTx(NDIM+1)) 7 | % FDIST: Distance function 8 | % FH: Edge length function 9 | % H: Smallest edge length 10 | % BOX: Bounding box [xmin,xmax;ymin,ymax; ...] (NDIMx2) 11 | % FIX: Fixed node positions (NFIXxNDIM) 12 | % FDISTPARAMS: Additional parameters passed to FDIST 13 | % 14 | % Example: Unit ball 15 | % dim=3; 16 | % d=inline('sqrt(sum(p.^2,2))-1','p'); 17 | % [p,t]=distmeshnd(d,@huniform,0.2,[-ones(1,dim);ones(1,dim)],[]); 18 | % 19 | % See also: DISTMESH2D, DELAUNAYN, TRIMESH, MESHDEMOND. 20 | 21 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 22 | 23 | dim=size(box,2); 24 | ptol=.001; ttol=.1; L0mult=1+.4/2^(dim-1); deltat=.1; geps=1e-1*h; deps=sqrt(eps)*h; 25 | 26 | % 1. Create initial distribution in bounding box 27 | if dim==1 28 | p=(box(1):h:box(2))'; 29 | else 30 | cbox=cell(1,dim); 31 | for ii=1:dim 32 | cbox{ii}=box(1,ii):h:box(2,ii); 33 | end 34 | pp=cell(1,dim); 35 | [pp{:}]=ndgrid(cbox{:}); 36 | p=zeros(prod(size(pp{1})),dim); 37 | for ii=1:dim 38 | p(:,ii)=pp{ii}(:); 39 | end 40 | end 41 | 42 | % 2. Remove points outside the region, apply the rejection method 43 | p=p(feval(fdist,p,varargin{:})ttol*h 54 | p0=p; 55 | t=delaunayn(p); 56 | pmid=zeros(size(t,1),dim); 57 | for ii=1:dim+1 58 | pmid=pmid+p(t(:,ii),:)/(dim+1); 59 | end 60 | t=t(feval(fdist,pmid,varargin{:})<-geps,:); 61 | % 4. Describe each edge by a unique pair of nodes 62 | pair=zeros(0,2); 63 | localpairs=nchoosek(1:dim+1,2); 64 | for ii=1:size(localpairs,1) 65 | pair=[pair;t(:,localpairs(ii,:))]; 66 | end 67 | pair=unique(sort(pair,2),'rows'); 68 | % 5. Graphical output of the current mesh 69 | if dim==2 70 | trimesh(t,p(:,1),p(:,2),zeros(N,1)) 71 | view(2),axis equal,axis off,drawnow 72 | elseif dim==3 73 | if mod(count,5)==0 74 | simpplot(p,t,'p(:,2)>0'); 75 | title(['Retriangulation #',int2str(count)]) 76 | drawnow 77 | end 78 | else 79 | disp(sprintf('Retriangulation #%d',count)) 80 | end 81 | count=count+1; 82 | end 83 | 84 | % 6. Move mesh points based on edge lengths L and forces F 85 | bars=p(pair(:,1),:)-p(pair(:,2),:); 86 | L=sqrt(sum(bars.^2,2)); 87 | L0=feval(fh,(p(pair(:,1),:)+p(pair(:,2),:))/2); 88 | L0=L0*L0mult*(sum(L.^dim)/sum(L0.^dim))^(1/dim); 89 | F=max(L0-L,0); 90 | Fbar=[bars,-bars].*repmat(F./L,1,2*dim); 91 | dp=full(sparse(pair(:,[ones(1,dim),2*ones(1,dim)]), ... 92 | ones(size(pair,1),1)*[1:dim,1:dim], ... 93 | Fbar,N,dim)); 94 | dp(1:size(fix,1),:)=0; 95 | p=p+deltat*dp; 96 | 97 | % 7. Bring outside points back to the boundary 98 | d=feval(fdist,p,varargin{:}); ix=d>0; 99 | gradd=zeros(sum(ix),dim); 100 | for ii=1:dim 101 | a=zeros(1,dim); 102 | a(ii)=deps; 103 | d1x=feval(fdist,p(ix,:)+ones(sum(ix),1)*a,varargin{:}); 104 | gradd(:,ii)=(d1x-d(ix))/deps; 105 | end 106 | p(ix,:)=p(ix,:)-d(ix)*ones(1,dim).*gradd; 107 | 108 | % 8. Termination criterion 109 | maxdp=max(deltat*sqrt(sum(dp(d<-geps,:).^2,2))); 110 | if maxdp 1000 113 | break; 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /code/HSH_basis_new.m: -------------------------------------------------------------------------------- 1 | %*************************************************************************% 2 | % All rights reserved (C) to the authors: Mahmoud SHAQFA, Gary CHOI % 3 | % and Katrin BEYER % 4 | % % 5 | % M. Shaqfa Contact: % 6 | % Earthquake Engineering and Structural Dynamics Laboratory (EESD), % 7 | % School of Architecture, Civil and Environmental Engineering (ENAC), % 8 | % Ecole polytechnique federale de Lausanne (EPFL), % 9 | % CH-1015 Lausanne, Switzerland. % 10 | % Tel.: +41 21 69 33297 % 11 | % Email: mahmoud.shaqfa@epfl.ch % 12 | % % 13 | % G. Choi Contact: % 14 | % Department of Mathematics, Massachusetts Institute of Technology (MIT) % 15 | % Cambridge, MA, USA % 16 | % Email: ptchoi@mit.edu % 17 | % % 18 | % K. Beyer Contact: % 19 | % Email: katrin.beyer@epfl.ch % 20 | %*************************************************************************% 21 | % This code includes implementations for: % 22 | % - Spherical Cap Harmonics (SCH) % 23 | % - Spherical Harmonics (SH) % 24 | % - HemiSpherical Harmonics (HSH) % 25 | % This code is part of the paper: "Spherical Cap Harmonic Analysis(SCHA)..% 26 | % for Characterising the Morphology of Rough Surface Patches" % 27 | % % 28 | %*************************************************************************% 29 | % This library is free software; you can redistribute it and/or modify % 30 | % it under the terms of the GNU Lesser General Public License as published% 31 | % by the Free Software Foundation; either version 2.1 of the License, or % 32 | % (at your option) any later version. % 33 | % % 34 | % This library is distributed in the hope that it will be useful, % 35 | % but WITHOUT ANY WARRANTY; without even the implied warranty of % 36 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % 37 | % See the GNU Lesser General Public License for more details. % 38 | % You should have received a copy of the GNU Lesser General Public License% 39 | % along with this library; if not, write to the Free Software Foundation, % 40 | % Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA % 41 | %*************************************************************************% 42 | % Author of this file: Mahmoud S. Shaqfa 43 | function Y_mat = HSH_basis_new(phi, theta, degree) 44 | 45 | % This can be faster- needs a bit of vectorisation 46 | Y_mat = zeros(size(theta,1), (degree+1)^2); 47 | 48 | P_cos = 2 * cos(theta) - 1; % To consider the upper part; the lower one takes 2x+1 49 | 50 | for n = 0:degree 51 | Pn = legendre(n, P_cos)'; 52 | for m = -n:1:n 53 | norm = sqrt(((2*n+1) * factorial(n-abs(m)))/(2 * pi * factorial(n+abs(m)))); 54 | if m < 0 55 | Y_mat(:, n^2 + n + m + 1) = Pn(:, abs(m) + 1) .* exp(1i .* abs(m) .* phi) .* norm; 56 | elseif m == 0 57 | Y_mat(:, n^2 + n + m + 1) = Pn(:, abs(m)+1) .* norm; 58 | elseif m > 0 59 | Y_mat(:, n^2 + n + m + 1) = (-1)^abs(m) .* conj(Pn(:, abs(m) + 1) .* exp(1i .* m .* phi) .* norm); 60 | end 61 | end 62 | end -------------------------------------------------------------------------------- /distmesh/distmesh2doriginal.m: -------------------------------------------------------------------------------- 1 | function [p,t]=distmesh2doriginal(fd,fh,h0,bbox,pfix,varargin) 2 | %DISTMESH2D 2-D Mesh Generator using Distance Functions. 3 | % [P,T]=DISTMESH2D(FD,FH,H0,BBOX,PFIX,FPARAMS) 4 | % 5 | % P: Node positions (Nx2) 6 | % T: Triangle indices (NTx3) 7 | % FD: Distance function d(x,y) 8 | % FH: Scaled edge length function h(x,y) 9 | % H0: Initial edge length 10 | % BBOX: Bounding box [xmin,ymin; xmax,ymax] 11 | % PFIX: Fixed node positions (NFIXx2) 12 | % FPARAMS: Additional parameters passed to FD and FH 13 | % 14 | % Example: (Uniform Mesh on Unit Circle) 15 | % fd=inline('sqrt(sum(p.^2,2))-1','p'); 16 | % [p,t]=distmesh2d(fd,@huniform,0.2,[-1,-1;1,1],[]); 17 | % 18 | % Example: (Rectangle with circular hole, refined at circle boundary) 19 | % fd=inline('ddiff(drectangle(p,-1,1,-1,1),dcircle(p,0,0,0.5))','p'); 20 | % fh=inline('min(4*sqrt(sum(p.^2,2))-1,2)','p'); 21 | % [p,t]=distmesh2d(fd,fh,0.05,[-1,-1;1,1],[-1,-1;-1,1;1,-1;1,1]); 22 | % 23 | % See also: MESHDEMO2D, DISTMESHND, DELAUNAYN, TRIMESH. 24 | 25 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 26 | 27 | dptol=.001; ttol=.1; Fscale=1.2; deltat=.2; geps=.001*h0; deps=sqrt(eps)*h0; 28 | 29 | % 1. Create initial distribution in bounding box (equilateral triangles) 30 | [x,y]=meshgrid(bbox(1,1):h0:bbox(2,1),bbox(1,2):h0*sqrt(3)/2:bbox(2,2)); 31 | x(2:2:end,:)=x(2:2:end,:)+h0/2; % Shift even rows 32 | p=[x(:),y(:)]; % List of node coordinates 33 | 34 | % 2. Remove points outside the region, apply the rejection method 35 | p=p(feval(fd,p,varargin{:})ttol % Any large movement? 44 | pold=p; % Save current positions 45 | t=delaunayn(p); % List of triangles 46 | pmid=(p(t(:,1),:)+p(t(:,2),:)+p(t(:,3),:))/3; % Compute centroids 47 | t=t(feval(fd,pmid,varargin{:})<-geps,:); % Keep interior triangles 48 | % 4. Describe each bar by a unique pair of nodes 49 | bars=[t(:,[1,2]);t(:,[1,3]);t(:,[2,3])]; % Interior bars duplicated 50 | bars=unique(sort(bars,2),'rows'); % Bars as node pairs 51 | % 5. Graphical output of the current mesh 52 | trimesh(t,p(:,1),p(:,2),zeros(N,1)) 53 | view(2),axis equal,axis off,drawnow 54 | end 55 | 56 | % 6. Move mesh points based on bar lengths L and forces F 57 | barvec=p(bars(:,1),:)-p(bars(:,2),:); % List of bar vectors 58 | L=sqrt(sum(barvec.^2,2)); % L = Bar lengths 59 | hbars=feval(fh,(p(bars(:,1),:)+p(bars(:,2),:))/2,varargin{:}); 60 | L0=hbars*Fscale*sqrt(sum(L.^2)/sum(hbars.^2)); % L0 = Desired lengths 61 | F=max(L0-L,0); % Bar forces (scalars) 62 | Fvec=F./L*[1,1].*barvec; % Bar forces (x,y components) 63 | Ftot=full(sparse(bars(:,[1,1,2,2]),ones(size(F))*[1,2,1,2],[Fvec,-Fvec],N,2)); 64 | Ftot(1:size(pfix,1),:)=0; % Force = 0 at fixed points 65 | p=p+deltat*Ftot; % Update node positions 66 | 67 | % 7. Bring outside points back to the boundary 68 | d=feval(fd,p,varargin{:}); ix=d>0; % Find points outside (d>0) 69 | dgradx=(feval(fd,[p(ix,1)+deps,p(ix,2)],varargin{:})-d(ix))/deps; % Numerical 70 | dgrady=(feval(fd,[p(ix,1),p(ix,2)+deps],varargin{:})-d(ix))/deps; % gradient 71 | p(ix,:)=p(ix,:)-[d(ix).*dgradx,d(ix).*dgrady]; % Project back to boundary 72 | 73 | % 8. Termination criterion: All interior nodes move less than dptol (scaled) 74 | if max(sqrt(sum(deltat*Ftot(d<-geps,:).^2,2))/h0)=unref_v(i)) = f(f>=unref_v(i))-1; 49 | end 50 | v = v(setdiff(1:length(v),unref_v),1:3); 51 | end 52 | %% remove extra faces 53 | [row,~] = find(f >length(v)); 54 | f(row,:) = []; 55 | %% remove repeated faces 56 | [~,unirow] = unique(sort(f,2), 'rows'); 57 | f = f(unirow,1:3); 58 | %% remove zero area surfaces 59 | e1 = v(f(:,3),:) - v(f(:,2),:); 60 | e2 = v(f(:,1),:) - v(f(:,3),:); 61 | a = cross(e1,e2); 62 | area = ((a(:,1).^2 + a(:,2).^2 + a(:,3).^2).^(1/2))/2; 63 | f = f(area>1e-15,1:3); 64 | 65 | if nargin == 3 66 | for i = length(v):-1:1 67 | count = sum(sum((f == i))); 68 | if count == 1 69 | %% face with 1 connection 70 | [row, ~] = find(f == i); 71 | f(row,:) = []; 72 | v(i,:) = []; 73 | f(f>=i) = f(f>=i)-1; 74 | end 75 | end 76 | end -------------------------------------------------------------------------------- /distmesh/distmeshsurface.m: -------------------------------------------------------------------------------- 1 | function [p,t]=distmeshsurface(fd,fh,h0,bbox,varargin) 2 | %DISTMESHSURFACE 3-D Surface Mesh Generator using Distance Functions. 3 | % [P,T]=DISTMESHSURFACE(FD,FH,H0,BBOX,FPARAMS) 4 | % 5 | % NOTE: 6 | % Doesn't work too well for nonuniform size functions... need a 7 | % better initial grid or density control. Also no support for 8 | % fixed points, again because of the initial grid. 9 | % 10 | % P: Node positions (Nx2) 11 | % T: Triangle indices (NTx3) 12 | % FD: Distance function d(x,y) 13 | % FH: Scaled edge length function h(x,y) 14 | % H0: Initial edge length 15 | % BBOX: Bounding box [xmin,ymin; xmax,ymax] 16 | % FPARAMS: Additional parameters passed to FD and FH 17 | % 18 | % Example: (Uniform Mesh on Unit Sphere) 19 | % fd=@(p) dsphere(p,0,0,0,1); 20 | % [p,t]=distmeshsurface(fd,@huniform,0.2,1.1*[-1,-1,-1;1,1,1]); 21 | % 22 | % Example: (Graded Mesh on Unit Sphere) 23 | % fd=@(p) dsphere(p,0,0,0,1); 24 | % fh=@(p) 0.05+0.5*dsphere(p,0,0,1,0); 25 | % [p,t]=distmeshsurface(fd,fh,0.15,1.1*[-1,-1,-1;1,1,1]); 26 | % 27 | % Example: (Uniform Mesh on Torus) 28 | % fd=@(p) (sum(p.^2,2)+.8^2-.2^2).^2-4*.8^2*(p(:,1).^2+p(:,2).^2); 29 | % [p,t]=distmeshsurface(fd,@huniform,0.1,[-1.1,-1.1,-.25;1.1,1.1,.25]); 30 | % 31 | % Example: (Uniform Mesh on Ellipsoid) 32 | % fd=@(p) p(:,1).^2/4+p(:,2).^2/1+p(:,3).^2/1.5^2-1; 33 | % [p,t]=distmeshsurface(fd,@huniform,0.2,[-2.1,-1.1,-1.6; 2.1,1.1,1.6]); 34 | 35 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 36 | 37 | dptol=1e-4; ttol=.1; Fscale=1.2; deltat=.2; deps=sqrt(eps)*h0; 38 | 39 | % 1. Create initial distribution in bounding box (isosurface from grid) 40 | [x,y,z]=ndgrid(bbox(1,1):h0:bbox(2,1),bbox(1,2):h0:bbox(2,2),bbox(1,3):h0:bbox(2,3)); 41 | pv=isosurface(x,y,z,reshape(fd([x(:),y(:),z(:)],varargin{:}),size(x)),0); 42 | p=pv.vertices; 43 | t=pv.faces; 44 | 45 | % Connectivities (for trisurfupd) 46 | [t2t,t2n]=mkt2t(t); 47 | t2t=int32(t2t-1)'; t2n=int8(t2n-1)'; 48 | 49 | N=size(p,1); % Number of points N 50 | pold=inf; % For first iteration 51 | while 1 52 | p0=p; 53 | % 3. Retriangulation 54 | if max(sqrt(sum((p-pold).^2,2))/h0)>ttol % Any large movement? 55 | pold=p; % Save current positions 56 | [t,t2t,t2n]=trisurfupd(int32(t-1)',t2t,t2n,p'); % Update triangles 57 | t=double(t+1)'; 58 | pmid=(p(t(:,1),:)+p(t(:,2),:)+p(t(:,3),:))/3; % Compute centroids 59 | % 4. Describe each bar by a unique pair of nodes 60 | bars=[t(:,[1,2]);t(:,[1,3]);t(:,[2,3])]; % Interior bars duplicated 61 | bars=unique(sort(bars,2),'rows'); % Bars as node pairs 62 | % 5. Graphical output of the current mesh 63 | clf,patch('faces',t,'vertices',p,'facecol',[.8,.9,1],'edgecol','k'); 64 | axis equal;axis off;view(3);cameramenu;drawnow 65 | end 66 | 67 | % 6. Move mesh points based on bar lengths L and forces F 68 | barvec=p(bars(:,1),:)-p(bars(:,2),:); % List of bar vectors 69 | L=sqrt(sum(barvec.^2,2)); % L = Bar lengths 70 | hbars=feval(fh,(p(bars(:,1),:)+p(bars(:,2),:))/2,varargin{:}); 71 | L0=hbars*Fscale*sqrt(sum(L.^2)/sum(hbars.^2)); % L0 = Desired lengths 72 | F=max(L0-L,0); % Bar forces (scalars) 73 | Fvec=F./L*[1,1,1].*barvec; % Bar forces (x,y,z components) 74 | Ftot=full(sparse(bars(:,[1,1,1,2,2,2]),ones(size(F))*[1,2,3,1,2,3],[Fvec,-Fvec],N,3)); 75 | p=p+deltat*Ftot; % Update node positions 76 | 77 | % 7. Bring all points back to the boundary 78 | d=feval(fd,p,varargin{:}); 79 | dgradx=(feval(fd,[p(:,1)+deps,p(:,2),p(:,3)],varargin{:})-d)/deps; % Numerical 80 | dgrady=(feval(fd,[p(:,1),p(:,2)+deps,p(:,3)],varargin{:})-d)/deps; % gradient 81 | dgradz=(feval(fd,[p(:,1),p(:,2),p(:,3)+deps],varargin{:})-d)/deps; % 82 | dgrad2=dgradx.^2+dgrady.^2+dgradz.^2; 83 | p=p-[d.*dgradx./dgrad2,d.*dgrady./dgrad2,d.*dgradz./dgrad2]; % Project back to boundary 84 | 85 | % 8. Termination criterion: All nodes move less than dptol (scaled) 86 | if max(sqrt(sum((p-p0).^2,2))/h0) 0 60 | Y_mat(:, n^2 + n + m + 1) = (-1)^abs(m) .* conj(Pn(:, abs(m) + 1) .* exp(1i .* m .* phi) .* norm); 61 | end 62 | reconstruction(:, 1) = reconstruction(:, 1) + Y_mat(:, n^2 + n + m + 1) .* qm_k(n^2 + n + m + 1, 1); 63 | reconstruction(:, 2) = reconstruction(:, 2) + Y_mat(:, n^2 + n + m + 1) .* qm_k(n^2 + n + m + 1, 2); 64 | reconstruction(:, 3) = reconstruction(:, 3) + Y_mat(:, n^2 + n + m + 1) .* qm_k(n^2 + n + m + 1, 3); 65 | end 66 | end -------------------------------------------------------------------------------- /code/HSH_basis_range.m: -------------------------------------------------------------------------------- 1 | %*************************************************************************% 2 | % All rights reserved (C) to the authors: Mahmoud SHAQFA, Gary CHOI % 3 | % and Katrin BEYER % 4 | % % 5 | % M. Shaqfa Contact: % 6 | % Earthquake Engineering and Structural Dynamics Laboratory (EESD), % 7 | % School of Architecture, Civil and Environmental Engineering (ENAC), % 8 | % Ecole polytechnique federale de Lausanne (EPFL), % 9 | % CH-1015 Lausanne, Switzerland. % 10 | % Tel.: +41 21 69 33297 % 11 | % Email: mahmoud.shaqfa@epfl.ch % 12 | % % 13 | % G. Choi Contact: % 14 | % Department of Mathematics, Massachusetts Institute of Technology (MIT) % 15 | % Cambridge, MA, USA % 16 | % Email: ptchoi@mit.edu % 17 | % % 18 | % K. Beyer Contact: % 19 | % Email: katrin.beyer@epfl.ch % 20 | %*************************************************************************% 21 | % This code includes implementations for: % 22 | % - Spherical Cap Harmonics (SCH) % 23 | % - Spherical Harmonics (SH) % 24 | % - HemiSpherical Harmonics (HSH) % 25 | % This code is part of the paper: "Spherical Cap Harmonic Analysis(SCHA)..% 26 | % for Characterising the Morphology of Rough Surface Patches" % 27 | % % 28 | %*************************************************************************% 29 | % This library is free software; you can redistribute it and/or modify % 30 | % it under the terms of the GNU Lesser General Public License as published% 31 | % by the Free Software Foundation; either version 2.1 of the License, or % 32 | % (at your option) any later version. % 33 | % % 34 | % This library is distributed in the hope that it will be useful, % 35 | % but WITHOUT ANY WARRANTY; without even the implied warranty of % 36 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % 37 | % See the GNU Lesser General Public License for more details. % 38 | % You should have received a copy of the GNU Lesser General Public License% 39 | % along with this library; if not, write to the Free Software Foundation, % 40 | % Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA % 41 | %*************************************************************************% 42 | % Author of this file: Mahmoud S. Shaqfa 43 | function reconstruction = HSH_basis_range(phi, theta, k_range, qm_k) 44 | k_min = min(k_range); 45 | k_max = max(k_range); 46 | Y_mat = zeros(size(theta,1), (k_max+1)^2); 47 | reconstruction = zeros(length(theta), 3); 48 | 49 | P_cos = cos(theta); 50 | 51 | for n = k_min:k_max 52 | Pn = legendre(n, P_cos)'; 53 | for m = -n:1:n 54 | norm = sqrt(((2*n+1) * factorial(n-abs(m)))/(2 * pi * factorial(n+abs(m)))); 55 | if m < 0 56 | Y_mat(:, n^2 + n + m + 1) = Pn(:, abs(m) + 1) .* exp(1i .* abs(m) .* phi) .* norm; 57 | elseif m == 0 58 | Y_mat(:, n^2 + n + m + 1) = Pn(:, abs(m)+1) .* norm; 59 | elseif m > 0 60 | Y_mat(:, n^2 + n + m + 1) = (-1)^abs(m) .* conj(Pn(:, abs(m) + 1) .* exp(1i .* m .* phi) .* norm); 61 | end 62 | reconstruction(:, 1) = reconstruction(:, 1) + Y_mat(:, n^2 + n + m + 1) .* qm_k(n^2 + n + m + 1, 1); 63 | reconstruction(:, 2) = reconstruction(:, 2) + Y_mat(:, n^2 + n + m + 1) .* qm_k(n^2 + n + m + 1, 2); 64 | reconstruction(:, 3) = reconstruction(:, 3) + Y_mat(:, n^2 + n + m + 1) .* qm_k(n^2 + n + m + 1, 3); 65 | end 66 | end -------------------------------------------------------------------------------- /spherical_parametrisation/extension/spherical_area_preserving_map.m: -------------------------------------------------------------------------------- 1 | function map = spherical_area_preserving_map(v,f) 2 | % A linear method for computing spherical area-preserving map of a genus-0 closed surface 3 | % 4 | % Remark: 5 | % This code is a simple variation of spherical_conformal_map. Here we 6 | % replace the cotangent Laplacian by the locally authalic Laplacian. 7 | % Note that this approach aims to reduce the local area distortion, while 8 | % the global area distortion is not necessarily minimized. 9 | % This code can be used as an initialization in iterative_spherical_area_preserving_map 10 | % for further reducing the local area distortion. 11 | % 12 | % Input: 13 | % v: nv x 3 vertex coordinates of a genus-0 triangle mesh 14 | % f: nf x 3 triangulations of a genus-0 triangle mesh 15 | % 16 | % Output: 17 | % map: nv x 3 vertex coordinates of the spherical parameterization 18 | % 19 | % If you use this code in your own work, please cite the following paper: 20 | % [1] P. T. Choi, K. C. Lam, and L. M. Lui, 21 | % "FLASH: Fast Landmark Aligned Spherical Harmonic Parameterization for Genus-0 Closed Brain Surfaces." 22 | % SIAM Journal on Imaging Sciences, vol. 8, no. 1, pp. 67-94, 2015. 23 | % 24 | % Copyright (c) 2013-2018, Gary Pui-Tung Choi 25 | % https://scholar.harvard.edu/choi 26 | 27 | %% Check whether the input mesh is genus-0 28 | if length(v)-3*length(f)/2+length(f) ~= 2 29 | error('The mesh is not a genus-0 closed surface.\n'); 30 | end 31 | 32 | v = v*sqrt(4*pi/sum(face_area(f,v))); 33 | 34 | %% Find the most regular triangle as the "big triangle" 35 | temp = v(reshape(f',1,length(f)*3),1:3); 36 | e1 = sqrt(sum((temp(2:3:end,1:3) - temp(3:3:end,1:3))'.^2))'; 37 | e2 = sqrt(sum((temp(1:3:end,1:3) - temp(3:3:end,1:3))'.^2))'; 38 | e3 = sqrt(sum((temp(1:3:end,1:3) - temp(2:3:end,1:3))'.^2))'; 39 | regularity = abs(e1./(e1+e2+e3)-1/3)+... 40 | abs(e2./(e1+e2+e3)-1/3)+abs(e3./(e1+e2+e3)-1/3); 41 | [~,bigtri] = min(regularity); 42 | 43 | % In case the spherical parameterization result is not evenly distributed, 44 | % try to change bigtri to the id of some other triangles with good quality 45 | 46 | %% North pole step: Compute spherical map by solving laplace equation on a big triangle 47 | nv = size(v,1); 48 | % One may change the matrix below to other mesh Laplacian matrices for 49 | % minimizing other types of energy 50 | M = mesh_laplacian(v,f,'authalic'); 51 | 52 | p1 = f(bigtri,1); 53 | p2 = f(bigtri,2); 54 | p3 = f(bigtri,3); 55 | 56 | fixed = [p1,p2,p3]; 57 | [mrow,mcol,mval] = find(M(fixed,:)); 58 | M = M - sparse(fixed(mrow),mcol,mval,nv,nv) + sparse(fixed,fixed,[1,1,1],nv,nv); 59 | 60 | % set the boundary condition for big triangle 61 | x1 = 0; y1 = 0; x2 = 1; y2 = 0; % arbitrarily set the two points 62 | a = v(p2,1:3) - v(p1,1:3); 63 | b = v(p3,1:3) - v(p1,1:3); 64 | sin1 = (norm(cross(a,b),2))/(norm(a,2)*norm(b,2)); 65 | ori_h = norm(b,2)*sin1; 66 | ratio = norm([x1-x2,y1-y2],2)/norm(a,2); 67 | y3 = ori_h*ratio; % compute the coordinates of the third vertex 68 | x3 = sqrt(norm(b,2)^2*ratio^2-y3^2); 69 | 70 | % Solve the Laplace equation to obtain a harmonic map 71 | c = zeros(nv,1); c(p1) = x1; c(p2) = x2; c(p3) = x3; 72 | d = zeros(nv,1); d(p1) = y1; d(p2) = y2; d(p3) = y3; 73 | z = M \ complex(c,d); 74 | z = z-mean(z); 75 | 76 | % inverse stereographic projection 77 | S = [2*real(z)./(1+abs(z).^2), 2*imag(z)./(1+abs(z).^2), (-1+abs(z).^2)./(1+abs(z).^2)]; 78 | 79 | %% Find optimal big triangle size 80 | w = complex(S(:,1)./(1+S(:,3)), S(:,2)./(1+S(:,3))); 81 | 82 | % find the index of the southernmost triangle 83 | [~, index] = sort(abs(z(f(:,1)))+abs(z(f(:,2)))+abs(z(f(:,3)))); 84 | inner = index(1); 85 | if inner == bigtri 86 | inner = index(2); 87 | end 88 | 89 | % Compute the size of the northern most and the southern most triangles 90 | NorthTriSide = (abs(z(f(bigtri,1))-z(f(bigtri,2))) + ... 91 | abs(z(f(bigtri,2))-z(f(bigtri,3))) + ... 92 | abs(z(f(bigtri,3))-z(f(bigtri,1))))/3; 93 | 94 | SouthTriSide = (abs(w(f(inner,1))-w(f(inner,2))) + ... 95 | abs(w(f(inner,2))-w(f(inner,3))) + ... 96 | abs(w(f(inner,3))-w(f(inner,1))))/3; 97 | 98 | % rescale to get the best distribution 99 | z = z*(sqrt(NorthTriSide*SouthTriSide))/(NorthTriSide); 100 | 101 | % inverse stereographic projection 102 | S = [2*real(z)./(1+abs(z).^2), 2*imag(z)./(1+abs(z).^2), (-1+abs(z).^2)./(1+abs(z).^2)]; 103 | 104 | if sum(sum(isnan(S))) ~= 0 105 | % if harmonic map fails due to very bad triangulations, use tutte map 106 | S = spherical_tutte_map(f,bigtri); 107 | end 108 | 109 | map = S; 110 | 111 | end -------------------------------------------------------------------------------- /code/taylor_2f1.m: -------------------------------------------------------------------------------- 1 | %*************************************************************************% 2 | % All rights reserved (C) to the authors: Mahmoud SHAQFA, Gary CHOI % 3 | % and Katrin BEYER % 4 | % % 5 | % M. Shaqfa Contact: % 6 | % Earthquake Engineering and Structural Dynamics Laboratory (EESD), % 7 | % School of Architecture, Civil and Environmental Engineering (ENAC), % 8 | % Ecole polytechnique federale de Lausanne (EPFL), % 9 | % CH-1015 Lausanne, Switzerland. % 10 | % Tel.: +41 21 69 33297 % 11 | % Email: mahmoud.shaqfa@epfl.ch % 12 | % % 13 | % G. Choi Contact: % 14 | % Department of Mathematics, Massachusetts Institute of Technology (MIT) % 15 | % Cambridge, MA, USA % 16 | % Email: ptchoi@mit.edu % 17 | % % 18 | % K. Beyer Contact: % 19 | % Email: katrin.beyer@epfl.ch % 20 | %*************************************************************************% 21 | % This code includes implementations for: % 22 | % - Spherical Cap Harmonics (SCH) % 23 | % - Spherical Harmonics (SH) % 24 | % - HemiSpherical Harmonics (HSH) % 25 | % This code is part of the paper: "Spherical Cap Harmonic Analysis(SCHA)..% 26 | % for Characterising the Morphology of Rough Surface Patches" % 27 | % % 28 | %*************************************************************************% 29 | % This library is free software; you can redistribute it and/or modify % 30 | % it under the terms of the GNU Lesser General Public License as published% 31 | % by the Free Software Foundation; either version 2.1 of the License, or % 32 | % (at your option) any later version. % 33 | % % 34 | % This library is distributed in the hope that it will be useful, % 35 | % but WITHOUT ANY WARRANTY; without even the implied warranty of % 36 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % 37 | % See the GNU Lesser General Public License for more details. % 38 | % You should have received a copy of the GNU Lesser General Public License% 39 | % along with this library; if not, write to the Free Software Foundation, % 40 | % Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA % 41 | %*************************************************************************% 42 | % This file was re-written and modified by: Mahmoud S. Shaqfa 43 | 44 | function b1 = taylor_2f1(a, b, c, z, N_eps) 45 | 46 | % This is a modified version of the hypergeometric function 2F1 by Mahmoud 47 | % S. Shaqfa. The license belgongs to the original authors John Pearson. 48 | %-------------------------------------------------------------------------% 49 | % By John Pearson % 50 | % University of Oxford % 51 | % Part of MSc dissertation 'Computation of Hypergeometric Functions' % 52 | %-------------------------------------------------------------------------% 53 | 54 | % Computes the confluent hypergeometric function 2F1(a,b;c;z) using Taylor% 55 | % series method (a) of Section 4.2. % 56 | 57 | %-------------------------------------------------------------------------% 58 | % Input: ar=Re(a) % 59 | % N_eps= Number of terms to compute % 60 | % Output: h=Computed value of 2F1(a,b;c;z) % 61 | %-------------------------------------------------------------------------% 62 | % Initialise vector of individual terms and sum of terms computed thus far 63 | 64 | a1=1;b1=1; 65 | for j=1:N_eps 66 | % Update value of a1, current term, and b1, sum of all terms in terms 67 | % of previous values 68 | a1=(a+j-1).*(b+j-1)./(c+j-1).*z./j.*a1; 69 | b1=b1+a1; 70 | end -------------------------------------------------------------------------------- /code/meshboundaries.m: -------------------------------------------------------------------------------- 1 | %*************************************************************************% 2 | % All rights reserved (C) to the authors: Mahmoud SHAQFA, Gary CHOI % 3 | % and Katrin BEYER % 4 | % % 5 | % M. Shaqfa Contact: % 6 | % Earthquake Engineering and Structural Dynamics Laboratory (EESD), % 7 | % School of Architecture, Civil and Environmental Engineering (ENAC), % 8 | % Ecole polytechnique federale de Lausanne (EPFL), % 9 | % CH-1015 Lausanne, Switzerland. % 10 | % Tel.: +41 21 69 33297 % 11 | % Email: mahmoud.shaqfa@epfl.ch % 12 | % % 13 | % G. Choi Contact: % 14 | % Department of Mathematics, Massachusetts Institute of Technology (MIT) % 15 | % Cambridge, MA, USA % 16 | % Email: ptchoi@mit.edu % 17 | % % 18 | % K. Beyer Contact: % 19 | % Email: katrin.beyer@epfl.ch % 20 | %*************************************************************************% 21 | % This code includes implementations for: % 22 | % - Spherical Cap Harmonics (SCH) % 23 | % - Spherical Harmonics (SH) % 24 | % - HemiSpherical Harmonics (HSH) % 25 | % This code is part of the paper: "Spherical Cap Harmonic Analysis(SCHA)..% 26 | % for Characterising the Morphology of Rough Surface Patches" % 27 | % % 28 | %*************************************************************************% 29 | % This library is free software; you can redistribute it and/or modify % 30 | % it under the terms of the GNU Lesser General Public License as published% 31 | % by the Free Software Foundation; either version 2.1 of the License, or % 32 | % (at your option) any later version. % 33 | % % 34 | % This library is distributed in the hope that it will be useful, % 35 | % but WITHOUT ANY WARRANTY; without even the implied warranty of % 36 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % 37 | % See the GNU Lesser General Public License for more details. % 38 | % You should have received a copy of the GNU Lesser General Public License% 39 | % along with this library; if not, write to the Free Software Foundation, % 40 | % Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA % 41 | %*************************************************************************% 42 | % Author of this file: Gary Choi 43 | function [bds, outer] = meshboundaries(f) 44 | 45 | % MESHBOUNDARIES Mesh Boundaries 46 | % [bds, n] = MESHBOUNDARIES(f) extracts boundaries of mesh into cells 47 | 48 | %% this version is a little bit faster 49 | % g = sparse(f, f(:, [2, 3, 1]), 1); 50 | % [ii, jj, ~] = find(g+g' == 1); 51 | % h = digraph(ii, jj); 52 | % bds = {}; 53 | % visited = true(max(f(:)), 1); 54 | % visited(ii) = false; 55 | % n = 0; 56 | % while 1 57 | % kk = find(~visited); 58 | % if isempty(kk) 59 | % break 60 | % end 61 | % n = n+1; 62 | % bds{n} = dfsearch(h, kk(1)); 63 | % visited(bds{n}) = true; 64 | % end 65 | %% this version is older but works 66 | nv = max(f(:)); 67 | v = zeros(nv, 2); 68 | tr = triangulation(f, v); 69 | fe = tr.freeBoundary; 70 | if isempty(fe) 71 | bds = {}; 72 | outer = []; 73 | return; 74 | end 75 | [~, order] = sort(fe(:, 1)); 76 | fe = fe(order, :); 77 | ex = zeros(nv, 1); 78 | vs = false(nv, 1); 79 | nfe = size(fe, 1); 80 | for i = 1 : nfe 81 | ex(fe(i, 1)) = fe(i, 2); 82 | end 83 | n = 0; 84 | for i = unique(fe(:))' 85 | if ~vs(i) 86 | n = n + 1; 87 | [bds{n}, vs] = dfs(ex, vs, i); 88 | end 89 | end 90 | %% 91 | m = zeros(n, 1); 92 | for i = 1 : n 93 | m(i) = size(bds{i}, 1); 94 | end 95 | [~, id] = sort(m, 'descend'); 96 | bds = bds(id); 97 | outer = bds{1}; 98 | 99 | function [bd, vs] = dfs(ex, vs, i) 100 | vs(i) = 1; 101 | bd = i; 102 | if ~vs(ex(i)) 103 | [bd, vs] = dfs(ex, vs, ex(i)); 104 | bd = [i; bd]; 105 | end 106 | -------------------------------------------------------------------------------- /distmesh/trisurfupd.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 2 | 3 | #include "mex.h" 4 | #include 5 | #include 6 | #include 7 | 8 | template inline T sqr(T x) { return x*x; } 9 | template inline T length(T *x) { return sqrt(sqr(x[0])+sqr(x[1])+sqr(x[2])); } 10 | template inline T dot(T *x,T *y) { return x[0]*y[0]+x[1]*y[1]+x[2]*y[2]; } 11 | template inline void cross(T *v1,T *v2,T *n) { n[0]=v1[1]*v2[2]-v1[2]*v2[1]; 12 | n[1]=v1[2]*v2[0]-v1[0]*v2[2]; 13 | n[2]=v1[0]*v2[1]-v1[1]*v2[0]; } 14 | const char mod3x1[3]={1,2,0}; 15 | const char mod3x2[3]={2,0,1}; 16 | 17 | double triarea(double *p1,double *p2,double *p3) 18 | { 19 | double d12x=p2[0]-p1[0]; 20 | double d12y=p2[1]-p1[1]; 21 | double d13x=p3[0]-p1[0]; 22 | double d13y=p3[1]-p1[1]; 23 | return (d12x*d13y-d12y*d13x)/2.0; 24 | } 25 | 26 | void trinormal3(double *p1,double *p2,double *p3,double *n) 27 | { 28 | double d12[3]={ p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2] }; 29 | double d13[3]={ p3[0]-p1[0], p3[1]-p1[1], p3[2]-p1[2] }; 30 | cross(d12,d13,n); 31 | 32 | double nnorm=length(n); 33 | n[0]/=nnorm; n[1]/=nnorm; n[2]/=nnorm; 34 | } 35 | 36 | double triqual3(double *p1,double *p2,double *p3) 37 | { 38 | double n[3]; 39 | double d12[3]={ p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2] }; 40 | double d13[3]={ p3[0]-p1[0], p3[1]-p1[1], p3[2]-p1[2] }; 41 | double d23[3]={ p3[0]-p2[0], p3[1]-p2[1], p3[2]-p2[2] }; 42 | cross(d12,d13,n); 43 | double vol=length(n)/2.0; 44 | double den=dot(d12,d12)+dot(d13,d13)+dot(d23,d23); 45 | return 6.928203230275509*vol/den; 46 | } 47 | 48 | void tupdate(double *p,int *t,int *t2t,char *t2n, 49 | int np,int nt) 50 | { 51 | for (int t1=0; t1=0) { 55 | double q1=triqual3(&p[3*t[0+3*t1]],&p[3*t[1+3*t1]],&p[3*t[2+3*t1]]); 56 | double q2=triqual3(&p[3*t[0+3*t2]],&p[3*t[1+3*t2]],&p[3*t[2+3*t2]]); 57 | double minqold=std::min(q1,q2); 58 | if (minqold<0.9) { 59 | char n2=t2n[n1+3*t1]; 60 | char tix11=mod3x1[n1]; 61 | char tix12=mod3x2[n1]; 62 | char tix21=mod3x1[n2]; 63 | char tix22=mod3x2[n2]; 64 | 65 | int newt[2][3]={t[0+3*t1],t[1+3*t1],t[2+3*t1], 66 | t[0+3*t2],t[1+3*t2],t[2+3*t2]}; 67 | 68 | // Swap edge 69 | newt[0][tix12]=newt[1][n2]; 70 | newt[1][tix22]=newt[0][n1]; 71 | 72 | double q3=triqual3(&p[3*newt[0][0]],&p[3*newt[0][1]],&p[3*newt[0][2]]); 73 | double q4=triqual3(&p[3*newt[1][0]],&p[3*newt[1][1]],&p[3*newt[1][2]]); 74 | double minqnew=std::min(q3,q4); 75 | 76 | if (minqnew>minqold+.025) { 77 | bool flip; 78 | double normal1[3],normal2[3],normal3[3],normal4[3]; 79 | trinormal3(&p[3*t[0+3*t1]],&p[3*t[1+3*t1]],&p[3*t[2+3*t1]],normal1); 80 | trinormal3(&p[3*t[0+3*t2]],&p[3*t[1+3*t2]],&p[3*t[2+3*t2]],normal2); 81 | trinormal3(&p[3*newt[0][0]],&p[3*newt[0][1]],&p[3*newt[0][2]],normal3); 82 | trinormal3(&p[3*newt[1][0]],&p[3*newt[1][1]],&p[3*newt[1][2]],normal4); 83 | flip=dot(normal1,normal2)>0 && dot(normal3,normal4)>0; 84 | if (flip) { 85 | int nbt; 86 | char nbn; 87 | 88 | // Insert new triangles 89 | memcpy(t+3*t1,newt[0],3*sizeof(int)); 90 | memcpy(t+3*t2,newt[1],3*sizeof(int)); 91 | 92 | // Update t2t and t2n 93 | nbt=t2t[tix21+3*t2]; 94 | nbn=t2n[tix21+3*t2]; 95 | t2t[n1+3*t1]=nbt; 96 | t2n[n1+3*t1]=nbn; 97 | if (nbt>=0) { 98 | t2t[nbn+3*nbt]=t1; 99 | t2n[nbn+3*nbt]=n1; 100 | } 101 | 102 | nbt=t2t[tix11+3*t1]; 103 | nbn=t2n[tix11+3*t1]; 104 | t2t[n2+3*t2]=nbt; 105 | t2n[n2+3*t2]=nbn; 106 | if (nbt>=0) { 107 | t2t[nbn+3*nbt]=t2; 108 | t2n[nbn+3*nbt]=n2; 109 | } 110 | 111 | t2t[tix11+3*t1]=t2; 112 | t2n[tix11+3*t1]=tix21; 113 | t2t[tix21+3*t2]=t1; 114 | t2n[tix21+3*t2]=tix11; 115 | } 116 | } 117 | } 118 | } 119 | } 120 | } 121 | 122 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 123 | { 124 | plhs[0]=mxDuplicateArray(prhs[0]); 125 | plhs[1]=mxDuplicateArray(prhs[1]); 126 | plhs[2]=mxDuplicateArray(prhs[2]); 127 | int *t=(int*)mxGetPr(plhs[0]); 128 | int *t2t=(int*)mxGetPr(plhs[1]); 129 | char *t2n=(char*)mxGetPr(plhs[2]); 130 | int nt=mxGetN(prhs[0]); 131 | 132 | double *p=mxGetPr(prhs[3]); 133 | int np=mxGetN(prhs[3]); 134 | 135 | tupdate(p,t,t2t,t2n,np,nt); 136 | } 137 | -------------------------------------------------------------------------------- /spherical_parametrisation/mfile/spherical_conformal_map.m: -------------------------------------------------------------------------------- 1 | function map = spherical_conformal_map(v,f) 2 | % A linear method for computing spherical conformal map of a genus-0 closed surface 3 | % 4 | % Input: 5 | % v: nv x 3 vertex coordinates of a genus-0 triangle mesh 6 | % f: nf x 3 triangulations of a genus-0 triangle mesh 7 | % 8 | % Output: 9 | % map: nv x 3 vertex coordinates of the spherical conformal parameterization 10 | % 11 | % If you use this code in your own work, please cite the following paper: 12 | % [1] P. T. Choi, K. C. Lam, and L. M. Lui, 13 | % "FLASH: Fast Landmark Aligned Spherical Harmonic Parameterization for Genus-0 Closed Brain Surfaces." 14 | % SIAM Journal on Imaging Sciences, vol. 8, no. 1, pp. 67-94, 2015. 15 | % 16 | % Copyright (c) 2013-2018, Gary Pui-Tung Choi 17 | % https://scholar.harvard.edu/choi 18 | 19 | %% Check whether the input mesh is genus-0 20 | if length(v)-3*length(f)/2+length(f) ~= 2 21 | error('The mesh is not a genus-0 closed surface.\n'); 22 | end 23 | 24 | %% Find the most regular triangle as the "big triangle" 25 | temp = v(reshape(f',1,length(f)*3),1:3); 26 | e1 = sqrt(sum((temp(2:3:end,1:3) - temp(3:3:end,1:3))'.^2))'; 27 | e2 = sqrt(sum((temp(1:3:end,1:3) - temp(3:3:end,1:3))'.^2))'; 28 | e3 = sqrt(sum((temp(1:3:end,1:3) - temp(2:3:end,1:3))'.^2))'; 29 | regularity = abs(e1./(e1+e2+e3)-1/3)+... 30 | abs(e2./(e1+e2+e3)-1/3)+abs(e3./(e1+e2+e3)-1/3); 31 | [~,bigtri] = min(regularity); 32 | 33 | % In case the spherical parameterization result is not evenly distributed, 34 | % try to change bigtri to the id of some other triangles with good quality 35 | 36 | %% North pole step: Compute spherical map by solving laplace equation on a big triangle 37 | nv = size(v,1); 38 | M = cotangent_laplacian(v,f); 39 | 40 | p1 = f(bigtri,1); 41 | p2 = f(bigtri,2); 42 | p3 = f(bigtri,3); 43 | 44 | fixed = [p1,p2,p3]; 45 | [mrow,mcol,mval] = find(M(fixed,:)); 46 | M = M - sparse(fixed(mrow),mcol,mval,nv,nv) + sparse(fixed,fixed,[1,1,1],nv,nv); 47 | 48 | % set the boundary condition for big triangle 49 | x1 = 0; y1 = 0; x2 = 1; y2 = 0; % arbitrarily set the two points 50 | a = v(p2,1:3) - v(p1,1:3); 51 | b = v(p3,1:3) - v(p1,1:3); 52 | sin1 = (norm(cross(a,b),2))/(norm(a,2)*norm(b,2)); 53 | ori_h = norm(b,2)*sin1; 54 | ratio = norm([x1-x2,y1-y2],2)/norm(a,2); 55 | y3 = ori_h*ratio; % compute the coordinates of the third vertex 56 | x3 = sqrt(norm(b,2)^2*ratio^2-y3^2); 57 | 58 | % Solve the Laplace equation to obtain a harmonic map 59 | c = zeros(nv,1); c(p1) = x1; c(p2) = x2; c(p3) = x3; 60 | d = zeros(nv,1); d(p1) = y1; d(p2) = y2; d(p3) = y3; 61 | z = M \ complex(c,d); 62 | z = z-mean(z); 63 | 64 | % inverse stereographic projection 65 | S = [2*real(z)./(1+abs(z).^2), 2*imag(z)./(1+abs(z).^2), (-1+abs(z).^2)./(1+abs(z).^2)]; 66 | 67 | %% Find optimal big triangle size 68 | w = complex(S(:,1)./(1+S(:,3)), S(:,2)./(1+S(:,3))); 69 | 70 | % find the index of the southernmost triangle 71 | [~, index] = sort(abs(z(f(:,1)))+abs(z(f(:,2)))+abs(z(f(:,3)))); 72 | inner = index(1); 73 | if inner == bigtri 74 | inner = index(2); 75 | end 76 | 77 | % Compute the size of the northern most and the southern most triangles 78 | NorthTriSide = (abs(z(f(bigtri,1))-z(f(bigtri,2))) + ... 79 | abs(z(f(bigtri,2))-z(f(bigtri,3))) + ... 80 | abs(z(f(bigtri,3))-z(f(bigtri,1))))/3; 81 | 82 | SouthTriSide = (abs(w(f(inner,1))-w(f(inner,2))) + ... 83 | abs(w(f(inner,2))-w(f(inner,3))) + ... 84 | abs(w(f(inner,3))-w(f(inner,1))))/3; 85 | 86 | % rescale to get the best distribution 87 | z = z*(sqrt(NorthTriSide*SouthTriSide))/(NorthTriSide); 88 | 89 | % inverse stereographic projection 90 | S = [2*real(z)./(1+abs(z).^2), 2*imag(z)./(1+abs(z).^2), (-1+abs(z).^2)./(1+abs(z).^2)]; 91 | 92 | if sum(sum(isnan(S))) ~= 0 93 | % if harmonic map fails due to very bad triangulations, use tutte map 94 | S = spherical_tutte_map(f,bigtri); 95 | end 96 | 97 | %% South pole step 98 | [~,I] = sort(S(:,3)); 99 | 100 | % number of points near the south pole to be fixed 101 | % simply set it to be 1/10 of the total number of vertices (can be changed) 102 | % In case the spherical parameterization is not good, change 10 to 103 | % something smaller (e.g. 2) 104 | fixnum = max(round(length(v)/10),3); 105 | fixed = I(1:min(length(v),fixnum)); 106 | 107 | % south pole stereographic projection 108 | P = [S(:,1)./(1+S(:,3)), S(:,2)./(1+S(:,3))]; 109 | 110 | % compute the Beltrami coefficient 111 | mu = beltrami_coefficient(P, f, v); 112 | 113 | % compose the map with another quasi-conformal map to cancel the distortion 114 | map = linear_beltrami_solver(P,f,mu,fixed,P(fixed,:)); 115 | 116 | if sum(sum(isnan(map))) ~= 0 117 | % if the result has NaN entries, then most probably the number of 118 | % boundary constraints is not large enough 119 | 120 | % increase the number of boundary constrains and run again 121 | fixnum = fixnum*5; % again, this number can be changed 122 | fixed = I(1:min(length(v),fixnum)); 123 | map = linear_beltrami_solver(P,f,mu,fixed,P(fixed,:)); 124 | 125 | if sum(sum(isnan(map))) ~= 0 126 | map = P; % use the old result 127 | end 128 | end 129 | 130 | z = complex(map(:,1),map(:,2)); 131 | 132 | % inverse south pole stereographic projection 133 | map = [2*real(z)./(1+abs(z).^2), 2*imag(z)./(1+abs(z).^2), -(abs(z).^2-1)./(1+abs(z).^2)]; 134 | 135 | end -------------------------------------------------------------------------------- /code/uniform_spherical_cap_grid.m: -------------------------------------------------------------------------------- 1 | %*************************************************************************% 2 | % All rights reserved (C) to the authors: Mahmoud SHAQFA, Gary CHOI % 3 | % and Katrin BEYER % 4 | % % 5 | % M. Shaqfa Contact: % 6 | % Earthquake Engineering and Structural Dynamics Laboratory (EESD), % 7 | % School of Architecture, Civil and Environmental Engineering (ENAC), % 8 | % Ecole polytechnique federale de Lausanne (EPFL), % 9 | % CH-1015 Lausanne, Switzerland. % 10 | % Tel.: +41 21 69 33297 % 11 | % Email: mahmoud.shaqfa@epfl.ch % 12 | % % 13 | % G. Choi Contact: % 14 | % Department of Mathematics, Massachusetts Institute of Technology (MIT) % 15 | % Cambridge, MA, USA % 16 | % Email: ptchoi@mit.edu % 17 | % % 18 | % K. Beyer Contact: % 19 | % Email: katrin.beyer@epfl.ch % 20 | %*************************************************************************% 21 | % This code includes implementations for: % 22 | % - Spherical Cap Harmonics (SCH) % 23 | % - Spherical Harmonics (SH) % 24 | % - HemiSpherical Harmonics (HSH) % 25 | % This code is part of the paper: "Spherical Cap Harmonic Analysis(SCHA)..% 26 | % for Characterising the Morphology of Rough Surface Patches" % 27 | % % 28 | %*************************************************************************% 29 | % This library is free software; you can redistribute it and/or modify % 30 | % it under the terms of the GNU Lesser General Public License as published% 31 | % by the Free Software Foundation; either version 2.1 of the License, or % 32 | % (at your option) any later version. % 33 | % % 34 | % This library is distributed in the hope that it will be useful, % 35 | % but WITHOUT ANY WARRANTY; without even the implied warranty of % 36 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % 37 | % See the GNU Lesser General Public License for more details. % 38 | % You should have received a copy of the GNU Lesser General Public License% 39 | % along with this library; if not, write to the Free Software Foundation, % 40 | % Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA % 41 | %*************************************************************************% 42 | % Authors of this file: Mahmoud S. Shaqfa and Gary Choi 43 | 44 | function [v,f] = uniform_spherical_cap_grid(theta_c, edge_length, resolution, reconstruction_mesh_option) 45 | % Authors Mahmoud S. Shaqfa, Gary Choi, Katrin Beyer 46 | % This function to generate a uniform grid on a spherical cap or a 47 | % We used the DISTMESH Library: http://persson.berkeley.edu/distmesh/ 48 | % for generating a uniform mesh on a unit disk. 49 | % prescribed half-angle \theta_c. 50 | % option 1 gives you reconstruction by edge length and 2 gives you 51 | % reconstruction by number of even elements used. 52 | % edge_length: the desired length of each edge on a unit disk 53 | % resolution 54 | %% Create a uniform spherical cap grid 55 | if reconstruction_mesh_option == 1 56 | % Planar uniform mesh generation on a unit disk 57 | fd=@(p) sqrt(sum(p.^2,2))-1; 58 | [p,f]=distmesh2d(fd,@huniform,edge_length,[-1,-1;1,1],[]); 59 | else 60 | samples = linspace(-1, 1, resolution); 61 | [sample_phi_1, sample_phi_2] = meshgrid(samples, samples); 62 | a = sample_phi_1; b = sample_phi_2; 63 | A = zeros(size(a)); B = zeros(size(a)); 64 | 65 | indx = find(and(abs(a) >= abs(b), and(b ~= 0, a~=0))); 66 | A(indx) = 2.* a(indx) ./sqrt(pi) .* cos(b(indx).*pi./(4.*a(indx))); 67 | B(indx) = 2.* a(indx) ./sqrt(pi) .* sin(b(indx).*pi./(4.*a(indx))); 68 | 69 | indx2 = find(and(abs(a) < abs(b), and(b ~= 0, a~=0))); 70 | A(indx2) = 2.* b(indx2) ./sqrt(pi) .* sin(a(indx2).*pi./(4.*b(indx2))); 71 | B(indx2) = 2.* b(indx2) ./sqrt(pi) .* cos(a(indx2).*pi./(4.*b(indx2))); 72 | 73 | stlWrite('tmp_dome.stl', A, B, zeros(size(A))) 74 | [p, f] = stlRead('tmp_dome.stl'); 75 | delete('tmp_dome.stl'); 76 | 77 | p = p./max(abs(p(:, 1))); % Unit disk 78 | end 79 | % Rescale the disk based on the area of the desired spherical cap domain 80 | % Note that surface area of spherical cap with height h = 2*pi*h, and hence 81 | % the desired area is 2*pi*(1-z_lim) 82 | p = p*sqrt(2*(1-cos(theta_c))); % Scale it for a spherical cap 83 | % Lambert equi-area projection 84 | v = -[sqrt(1-(p(:,1).^2+p(:,2).^2)/4).*p(:,1), ... 85 | sqrt(1-(p(:,1).^2+p(:,2).^2)/4).*p(:,2), ... 86 | -1+(p(:,1).^2+p(:,2).^2)/2]; 87 | end -------------------------------------------------------------------------------- /code/spherical_cap_harmonic_basis.m: -------------------------------------------------------------------------------- 1 | %*************************************************************************% 2 | % All rights reserved (C) to the authors: Mahmoud SHAQFA, Gary CHOI % 3 | % and Katrin BEYER % 4 | % % 5 | % M. Shaqfa Contact: % 6 | % Earthquake Engineering and Structural Dynamics Laboratory (EESD), % 7 | % School of Architecture, Civil and Environmental Engineering (ENAC), % 8 | % Ecole polytechnique federale de Lausanne (EPFL), % 9 | % CH-1015 Lausanne, Switzerland. % 10 | % Tel.: +41 21 69 33297 % 11 | % Email: mahmoud.shaqfa@epfl.ch % 12 | % % 13 | % G. Choi Contact: % 14 | % Department of Mathematics, Massachusetts Institute of Technology (MIT) % 15 | % Cambridge, MA, USA % 16 | % Email: ptchoi@mit.edu % 17 | % % 18 | % K. Beyer Contact: % 19 | % Email: katrin.beyer@epfl.ch % 20 | %*************************************************************************% 21 | % This code includes implementations for: % 22 | % - Spherical Cap Harmonics (SCH) % 23 | % - Spherical Harmonics (SH) % 24 | % - HemiSpherical Harmonics (HSH) % 25 | % This code is part of the paper: "Spherical Cap Harmonic Analysis(SCHA)..% 26 | % for Characterising the Morphology of Rough Surface Patches" % 27 | % % 28 | %*************************************************************************% 29 | % This library is free software; you can redistribute it and/or modify % 30 | % it under the terms of the GNU Lesser General Public License as published% 31 | % by the Free Software Foundation; either version 2.1 of the License, or % 32 | % (at your option) any later version. % 33 | % % 34 | % This library is distributed in the hope that it will be useful, % 35 | % but WITHOUT ANY WARRANTY; without even the implied warranty of % 36 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % 37 | % See the GNU Lesser General Public License for more details. % 38 | % You should have received a copy of the GNU Lesser General Public License% 39 | % along with this library; if not, write to the Free Software Foundation, % 40 | % Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA % 41 | %*************************************************************************% 42 | % Author of this file: Mahmoud S. Shaqfa 43 | function SCH_basis = spherical_cap_harmonic_basis(K, theta_c, eigen_table, thetas, phis, N_eps) 44 | % This function returns the complete basis that includes Legendre and 45 | % Fourier for \theta and \phi. 46 | % It returns a table for positive and negative orders for a certain index K 47 | % The number of the columns in the matrix is 2K+1 for (-m:1:m) orders 48 | % Note: thetas and phis matrix msut be identical in size 49 | % All the angles here must be in radians. 50 | % For our applications the following variables shall be fixed: 51 | % thetas and phis must be square matrix or column matrix (row matrix will give you an error!) 52 | BC = "even"; 53 | normalization_method = "Hainse"; 54 | 55 | % Initializing and filling up 56 | ms = 0:K; 57 | 58 | % Check inputs' size (1D and 2D matrix) 59 | sz = length(size(thetas)); % For reconstruction matricies only 60 | if isempty(find(size(thetas) == 1, 1)) 61 | sz = sz + 1; 62 | end 63 | if sz == 2 % For normal analysis or reconstruction using geodesic domes 64 | SCH_basis = zeros(length(thetas), (2*K+1)); 65 | positive_ms = repmat(ms, length(thetas), 1); 66 | % Compute and fill up the postive orders 67 | SCH_basis(:, K+1:end) = fractional_associated_Legendre_functions(theta_c,... 68 | K, BC, eigen_table, thetas, normalization_method, N_eps) .* exp(1i .* positive_ms .* repmat(phis, 1, K+1)); 69 | % Compute and fill up the negative orders 70 | SCH_basis(:, 1:K) = flip(conj(SCH_basis(:, K+2:end)) .* (-1).^ positive_ms(:, 2:end), 2); 71 | elseif sz == 3 % For the meshgrid reconstruction case only or 3D render of the basis 72 | temp_sz = size(thetas); temp_sz(3) = (2*K+1); 73 | SCH_basis = zeros(temp_sz); 74 | % Prepare the ms 75 | positive_ms = zeros(temp_sz(1), temp_sz(2), K+1); 76 | for ii = ms 77 | positive_ms(:, :, ii+1) = ones(temp_sz(1), temp_sz(2)) .* ii; 78 | end 79 | % Compute and fill up the postive orders 80 | SCH_basis(:, :, K+1:end) = fractional_associated_Legendre_functions(deg2rad(theta_c),... 81 | K, BC, eigen_table, thetas, normalization_method, N_eps) .* exp(1i .* positive_ms .* phis); 82 | % Compute and fill up the negative orders 83 | SCH_basis(:, :, 1:K) = flip(conj(SCH_basis(:, :, K+2:end)) .* (-1).^ positive_ms(:, :, 2:end), 3); 84 | end 85 | end -------------------------------------------------------------------------------- /stlTools/icosphere.m: -------------------------------------------------------------------------------- 1 | function [vv,ff] = icosphere(varargin) 2 | %ICOSPHERE Generate icosphere. 3 | % Create a unit geodesic sphere created by subdividing a regular 4 | % icosahedron with normalised vertices. 5 | % 6 | % [V,F] = ICOSPHERE(N) generates to matrices containing vertex and face 7 | % data so that patch('Faces',F,'Vertices',V) produces a unit icosphere 8 | % with N subdivisions. 9 | % 10 | % FV = ICOSPHERE(N) generates an FV structure for using with patch. 11 | % 12 | % ICOSPHERE(N) and just ICOSPHERE display the icosphere as a patch on the 13 | % current axes and does not return anything. 14 | % 15 | % ICOSPHERE uses N = 3. 16 | % 17 | % ICOSPHERE(AX,...) plots into AX instead of GCA. 18 | % 19 | % See also SPHERE. 20 | % 21 | % Based on C# code by Andres Kahler 22 | % http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html 23 | % 24 | % Wil O.C. Ward 19/03/2015 25 | % University of Nottingham, UK 26 | % Parse possible axes input 27 | if nargin > 2 28 | error('Too many input variables, must be 0, 1 or 2.'); 29 | end 30 | [cax,args,nargs] = axescheck(varargin{:}); 31 | n = 3; % default number of sub-divisions 32 | if nargs > 0, n = args{1}; end % override based on input 33 | % generate regular unit icosahedron (20 faced polyhedron) 34 | [v,f] = icosahedron(); % size(v) = [12,3]; size(f) = [20,3]; 35 | % recursively subdivide triangle faces 36 | for gen = 1:n 37 | f_ = zeros(size(f,1)*4,3); 38 | for i = 1:size(f,1) % for each triangle 39 | tri = f(i,:); 40 | % calculate mid points (add new points to v) 41 | [a,v] = getMidPoint(tri(1),tri(2),v); 42 | [b,v] = getMidPoint(tri(2),tri(3),v); 43 | [c,v] = getMidPoint(tri(3),tri(1),v); 44 | % generate new subdivision triangles 45 | nfc = [tri(1),a,c; 46 | tri(2),b,a; 47 | tri(3),c,b; 48 | a,b,c]; 49 | % replace triangle with subdivision 50 | idx = 4*(i-1)+1:4*i; 51 | f_(idx,:) = nfc; 52 | end 53 | f = f_; % update 54 | end 55 | % remove duplicate vertices 56 | [v,b,ix] = unique(v,'rows'); clear b % b dummy / compatibility 57 | % reassign faces to trimmed vertex list and remove any duplicate faces 58 | f = unique(ix(f),'rows'); 59 | switch(nargout) 60 | case 0 % no output 61 | cax = newplot(cax); % draw to given axis (or gca) 62 | showSphere(cax,f,v); 63 | case 1 % return fv structure for patch 64 | vv = struct('Vertices',v,'Faces',f,... 65 | 'VertexNormals',v,'FaceVertexCData',v(:,3)); 66 | case 2 % return vertices and faces 67 | vv = v; ff = f; 68 | otherwise 69 | error('Too many output variables, must be 0, 1 or 2.'); 70 | end 71 | end 72 | function [i,v] = getMidPoint(t1,t2,v) 73 | %GETMIDPOINT calculates point between two vertices 74 | % Calculate new vertex in sub-division and normalise to unit length 75 | % then find or add it to v and return index 76 | % 77 | % Wil O.C. Ward 19/03/2015 78 | % University of Nottingham, UK 79 | % get vertice positions 80 | p1 = v(t1,:); p2 = v(t2,:); 81 | % calculate mid point (on unit sphere) 82 | pm = (p1 + p2) ./ 2; 83 | pm = pm./norm(pm); 84 | % add to vertices list, return index 85 | i = size(v,1) + 1; 86 | v = [v;pm]; 87 | end 88 | function [v,f] = icosahedron() 89 | %ICOSAHEDRON creates unit regular icosahedron 90 | % Returns 12 vertex and 20 face values. 91 | % 92 | % Wil O.C. Ward 19/03/2015 93 | % University of Nottingham, UK 94 | t = (1+sqrt(5)) / 2; 95 | % create vertices 96 | v = [-1, t, 0; % v1 97 | 1, t, 0; % v2 98 | -1,-t, 0; % v3 99 | 1,-t, 0; % v4 100 | 0,-1, t; % v5 101 | 0, 1, t; % v6 102 | 0,-1,-t; % v7 103 | 0, 1,-t; % v8 104 | t, 0,-1; % v9 105 | t, 0, 1; % v10 106 | -t, 0,-1; % v11 107 | -t, 0, 1];% v12 108 | % normalise vertices to unit size 109 | v = v./norm(v(1,:)); 110 | % create faces 111 | f = [ 1,12, 6; % f1 112 | 1, 6, 2; % f2 113 | 1, 2, 8; % f3 114 | 1, 8,11; % f4 115 | 1,11,12; % f5 116 | 2, 6,10; % f6 117 | 6,12, 5; % f7 118 | 12,11, 3; % f8 119 | 11, 8, 7; % f9 120 | 8, 2, 9; % f10 121 | 4,10, 5; % f11 122 | 4, 5, 3; % f12 123 | 4, 3, 7; % f13 124 | 4, 7, 9; % f14 125 | 4, 9,10; % f15 126 | 5,10, 6; % f16 127 | 3, 5,12; % f17 128 | 7, 3,11; % f18 129 | 9, 7, 8; % f19 130 | 10, 9, 2];% f20 131 | end 132 | function showSphere(cax,f,v) 133 | %SHOWSPHERE displays icosphere given faces and vertices. 134 | % Displays a patch surface on the axes, cax, and sets the view. 135 | % 136 | % Properties: 137 | % - vertex normals == vertex vectors 138 | % - no backface lighting 139 | % - colour data matches z value of vertex 140 | % - material properties match default SURF 141 | % 142 | % Wil O.C. Ward 19/03/2015 143 | % University of Nottingham, UK 144 | % set some axes properties if not held 145 | if ~ishold(cax) 146 | az = -37.5; el = 30; 147 | view(az,el) 148 | grid 149 | end 150 | % create patch object on cax 151 | patch('Faces',f,'Vertices',v,... 152 | 'VertexNormals',v,... 153 | 'LineWidth',0.5,'FaceLighting','phong',... 154 | 'BackFaceLighting','unlit',... 155 | 'AmbientStrength',0.3,'DiffuseStrength',0.6,... 156 | 'SpecularExponent',10,'SpecularStrength',0.9,... 157 | 'FaceColor','flat','CData',v(:,3),... 158 | 'Parent',cax,'Tag','Icosphere'); 159 | end -------------------------------------------------------------------------------- /Main_optimum_half_angle_finder.m: -------------------------------------------------------------------------------- 1 | %*************************************************************************% 2 | % All rights reserved (C) to the authors: Mahmoud SHAQFA, Gary CHOI % 3 | % and Katrin BEYER % 4 | % % 5 | % M. Shaqfa Contact: % 6 | % Earthquake Engineering and Structural Dynamics Laboratory (EESD), % 7 | % School of Architecture, Civil and Environmental Engineering (ENAC), % 8 | % Ecole polytechnique federale de Lausanne (EPFL), % 9 | % CH-1015 Lausanne, Switzerland. % 10 | % Tel.: +41 21 69 33297 % 11 | % Email: mahmoud.shaqfa@epfl.ch % 12 | % % 13 | % G. Choi Contact: % 14 | % Department of Mathematics, Massachusetts Institute of Technology (MIT) % 15 | % Cambridge, MA, USA % 16 | % Email: ptchoi@mit.edu % 17 | % % 18 | % K. Beyer Contact: % 19 | % Email: katrin.beyer@epfl.ch % 20 | %*************************************************************************% 21 | % This code includes implementations for: % 22 | % - Spherical Cap Harmonics (SCH) % 23 | % - Spherical Harmonics (SH) % 24 | % - HemiSpherical Harmonics (HSH) % 25 | % This code is part of the paper: "Spherical Cap Harmonic Analysis(SCHA)..% 26 | % for Characterising the Morphology of Rough Surface Patches" % 27 | % % 28 | %*************************************************************************% 29 | % This library is free software; you can redistribute it and/or modify % 30 | % it under the terms of the GNU Lesser General Public License as published% 31 | % by the Free Software Foundation; either version 2.1 of the License, or % 32 | % (at your option) any later version. % 33 | % % 34 | % This library is distributed in the hope that it will be useful, % 35 | % but WITHOUT ANY WARRANTY; without even the implied warranty of % 36 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % 37 | % See the GNU Lesser General Public License for more details. % 38 | % You should have received a copy of the GNU Lesser General Public License% 39 | % along with this library; if not, write to the Free Software Foundation, % 40 | % Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA % 41 | %*************************************************************************% 42 | % Author of this file: Mahmoud S. Shaqfa 43 | % function Main_optimum_half_angle_finder 44 | % This function to solve for the optimum half-angle (\theta_c) by 45 | % minimizing area and angluar distortion 46 | %% Load paths 47 | format long 48 | clc; close all; clear; tic 49 | addpath('code') 50 | addpath('stlTools') 51 | addpath('input_geom') 52 | %% The PSS inputs 53 | silent_mode = false; 54 | PSS_plot_results = true; 55 | LB = deg2rad(7); % Lower bound in radians for \theta_c 56 | UB = deg2rad(120);% Upper bound in radians for \theta_c 57 | pop_size = 20; 58 | iterations = 5; 59 | acceptance_rate = 0.97; 60 | %% Input surface 61 | surface_name = 'half_rock_mesh_manifold.stl'; % Best answer 100 degrees... 62 | surface_name = "test_rough_surface.stl"; % Best answer 8.6 degrees... 63 | 64 | plot_figures = true; 65 | 66 | % Load the STL file 67 | [v,f] = stlRead(surface_name); 68 | % Clean the STL mesh (if needed) 69 | v_old = v; 70 | f_old = f; 71 | [v,f] = clean_mesh(v_old,f_old,1); 72 | while length(v) ~= length(v_old) 73 | v_old = v; 74 | f_old = f; 75 | [v,f] = clean_mesh(v_old,f_old,1); 76 | end 77 | clear v_old f_old 78 | % Normalize the vertices 79 | v = [v(:,1) - mean(v(:,1)), v(:,2) - mean(v(:,2)), v(:,3) - mean(v(:,3))]; 80 | if plot_figures 81 | height_map = v(:, 3); 82 | height_map = sqrt(v(:, 3).^2 + v(:, 2).^2 + v(:, 1).^2); 83 | paraview_patch(v, f, height_map) 84 | view([50 20]) 85 | ff = gcf; 86 | title('Input surface') 87 | end 88 | %% The core of the solver 89 | objective_fun = @(theta_c) optimum_half_angle_objective(v, f, theta_c); 90 | 91 | results_struct = PSS_optimizer(objective_fun, LB, UB, pop_size, iterations, ... 92 | acceptance_rate, PSS_plot_results, silent_mode); 93 | 94 | % Save parametrised mesh 95 | best_theta_c = results_struct.best_solution; 96 | optimum_eval = results_struct.evaluations_history(end); 97 | map = spherical_cap_conformal_map(v, f, cos(best_theta_c), false); 98 | stlWrite(strcat('optimum_half_angle/para_th_', num2str(rad2deg(best_theta_c)), '_eval_',... 99 | num2str(optimum_eval), '_', surface_name), f, map,'mode','ascii') 100 | if plot_figures 101 | height_map = sqrt(v(:, 3).^2 + v(:, 2).^2 + v(:, 1).^2); 102 | paraview_patch(map, f, height_map) 103 | view([50 20]) 104 | title(['Output surface, \theta_{c} = ', num2str(rad2deg(best_theta_c))]) 105 | ff = gcf; 106 | end 107 | disp(['Best half-angle obtained is: ', num2str(rad2deg(best_theta_c))]) 108 | toc 109 | % end 110 | %% The objective function definition 111 | function optimum_eval = optimum_half_angle_objective(v, f, theta_c) 112 | [~, optimum_eval] = spherical_cap_conformal_map(v, f, cos(theta_c), true); 113 | end -------------------------------------------------------------------------------- /Main_HSH_roughness_projection.m: -------------------------------------------------------------------------------- 1 | %*************************************************************************% 2 | % All rights reserved (C) to the authors: Mahmoud SHAQFA, Gary CHOI % 3 | % and Katrin BEYER % 4 | % % 5 | % M. Shaqfa Contact: % 6 | % Earthquake Engineering and Structural Dynamics Laboratory (EESD), % 7 | % School of Architecture, Civil and Environmental Engineering (ENAC), % 8 | % Ecole polytechnique federale de Lausanne (EPFL), % 9 | % CH-1015 Lausanne, Switzerland. % 10 | % Tel.: +41 21 69 33297 % 11 | % Email: mahmoud.shaqfa@epfl.ch % 12 | % % 13 | % G. Choi Contact: % 14 | % Department of Mathematics, Massachusetts Institute of Technology (MIT) % 15 | % Cambridge, MA, USA % 16 | % Email: ptchoi@mit.edu % 17 | % % 18 | % K. Beyer Contact: % 19 | % Email: katrin.beyer@epfl.ch % 20 | %*************************************************************************% 21 | % This code includes implementations for: % 22 | % - Spherical Cap Harmonics (SCH) % 23 | % - Spherical Harmonics (SH) % 24 | % - HemiSpherical Harmonics (HSH) % 25 | % This code is part of the paper: "Spherical Cap Harmonic Analysis(SCHA)..% 26 | % for Characterising the Morphology of Rough Surface Patches" % 27 | % % 28 | %*************************************************************************% 29 | % This library is free software; you can redistribute it and/or modify % 30 | % it under the terms of the GNU Lesser General Public License as published% 31 | % by the Free Software Foundation; either version 2.1 of the License, or % 32 | % (at your option) any later version. % 33 | % % 34 | % This library is distributed in the hope that it will be useful, % 35 | % but WITHOUT ANY WARRANTY; without even the implied warranty of % 36 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % 37 | % See the GNU Lesser General Public License for more details. % 38 | % You should have received a copy of the GNU Lesser General Public License% 39 | % along with this library; if not, write to the Free Software Foundation, % 40 | % Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA % 41 | %*************************************************************************% 42 | % Author of this file: Mahmoud S. Shaqfa 43 | % This file is for the roughness projection on a Hemispherical harmonics (open objects). 44 | %% Paths 45 | % function Main_HSH_roughness_projection 46 | close all; 47 | clear; clc; tic 48 | addpath('code') 49 | addpath('stlTools') 50 | addpath('rec_output') 51 | addpath('geodesic_domes') 52 | addpath('input_geom/donor_meshes/') 53 | addpath('analysis_results') 54 | %% Reconstruction range for k \in [start, end] 55 | starting_degree = 9; 56 | ending_degree = 10; 57 | scale_factor = 0.01; 58 | plot_figures = true; 59 | %% Load the analysis reults from HSH or SCH only 60 | analysis_mat_file = 'rec_k_10_HSH_half_rock_mesh_manifold.mat'; 61 | load(analysis_mat_file); close all; 62 | %% The donor mesh is the mesh that provides us with the shape and we project 63 | % roughness on it extracted from the SCH or HSH analyses. 64 | 65 | donor_mesh = false; % if false it will load a unit sphere as a donor mesh 66 | % Geodesic dome STL reconstruction 67 | icosahedron_dome_refinement = 5; 68 | 69 | % donor_mesh_name = 'cube_refined_sample_1.stl'; 70 | % donor_mesh_name = 'cube_refined_sample_2.stl'; 71 | donor_mesh_name = 'cube_refined_sample_3.stl'; 72 | 73 | if donor_mesh 74 | [v, f] = stlRead(donor_mesh_name); 75 | else 76 | [v, f] = icosphere(icosahedron_dome_refinement); 77 | end 78 | v = [v(:,1) - mean(v(:,1)), v(:,2) - mean(v(:,2)), v(:,3) - mean(v(:,3))]; 79 | %% Specify (select) the group of vertices to be parametrized 80 | filter_query = strcat('selected_verts = find(v(v(:,2)>(0.8-0.0001)));'); % for a sphere 81 | % filter_query = strcat('selected_verts = find(v(v(:,3)>(0.5-0.0001)));'); % for the donor mesh 82 | eval(filter_query); 83 | 84 | if isempty(selected_verts) 85 | error('The selected vetices list must be specified.'); 86 | end 87 | 88 | selected_groups = zeros(length(v(:, 1)), 1); 89 | selected_groups(selected_verts) = 1; 90 | %% Project roughness and render results 91 | if plot_figures 92 | paraview_patch(v, f, selected_groups) 93 | title('Input donor mesh') 94 | axis on 95 | xlabel('x'); ylabel('y'); zlabel('z') 96 | view([50 20]) 97 | end 98 | 99 | % Solve for \phi and \theta from the donor mesh patch on a hemisphere 100 | map = spherical_map_with_prescribed_cap(v, f, selected_verts, cosd(90)); 101 | stlWrite(['rec_output/para_output_HSH_', surface_name], f, map,'mode','ascii') 102 | 103 | if plot_figures 104 | paraview_patch(map, f) 105 | title('Input donor mesh parametrisation on a unit hemisphere') 106 | end 107 | 108 | selected_map = map(selected_verts, :); 109 | thetas = acos(selected_map(:,3)); % [0, theta_c] 110 | phis = atan2(selected_map(:,2),selected_map(:,1)); % [-pi, pi] 111 | k_range = [starting_degree, ending_degree]; 112 | 113 | reconstruction = real(HSH_basis_range(phis, thetas, k_range, qm_k)); 114 | v(selected_verts, :) = v(selected_verts, :) + scale_factor .* reconstruction; 115 | 116 | if plot_figures 117 | paraview_patch(v, f, selected_groups) 118 | title('Output of the roughened mesh') 119 | end 120 | %% Save the resulted STL file 121 | stlWrite(['roughness_output/rec_l_', num2str(starting_degree), '_to_',... 122 | num2str(ending_degree), '_SH_', surface_name]... 123 | , f, v,'mode','ascii') 124 | % end -------------------------------------------------------------------------------- /distmesh/distmesh2d.m: -------------------------------------------------------------------------------- 1 | function [p,t]=distmesh2d(fd,fh,h0,bbox,pfix,varargin) 2 | %DISTMESH2D 2-D Mesh Generator using Distance Functions. 3 | % [P,T]=DISTMESH2D(FD,FH,H0,BBOX,PFIX,FPARAMS) 4 | % 5 | % P: Node positions (Nx2) 6 | % T: Triangle indices (NTx3) 7 | % FD: Distance function d(x,y) 8 | % FH: Scaled edge length function h(x,y) 9 | % H0: Initial edge length 10 | % BBOX: Bounding box [xmin,ymin; xmax,ymax] 11 | % PFIX: Fixed node positions (NFIXx2) 12 | % FPARAMS: Additional parameters passed to FD and FH 13 | % 14 | % Example: (Uniform Mesh on Unit Circle) 15 | % fd=@(p) sqrt(sum(p.^2,2))-1; 16 | % [p,t]=distmesh2d(fd,@huniform,0.2,[-1,-1;1,1],[]); 17 | % 18 | % Example: (Rectangle with circular hole, refined at circle boundary) 19 | % fd=@(p) ddiff(drectangle(p,-1,1,-1,1),dcircle(p,0,0,0.5)); 20 | % fh=@(p) 0.05+0.3*dcircle(p,0,0,0.5); 21 | % [p,t]=distmesh2d(fd,fh,0.05,[-1,-1;1,1],[-1,-1;-1,1;1,-1;1,1]); 22 | % 23 | % Example: (Polygon) 24 | % pv=[-0.4 -0.5;0.4 -0.2;0.4 -0.7;1.5 -0.4;0.9 0.1; 25 | % 1.6 0.8;0.5 0.5;0.2 1;0.1 0.4;-0.7 0.7;-0.4 -0.5]; 26 | % [p,t]=distmesh2d(@dpoly,@huniform,0.1,[-1,-1; 2,1],pv,pv); 27 | % 28 | % Example: (Ellipse) 29 | % fd=@(p) p(:,1).^2/2^2+p(:,2).^2/1^2-1; 30 | % [p,t]=distmesh2d(fd,@huniform,0.2,[-2,-1;2,1],[]); 31 | % 32 | % Example: (Square, with size function point and line sources) 33 | % fd=@(p) drectangle(p,0,1,0,1); 34 | % fh=@(p) min(min(0.01+0.3*abs(dcircle(p,0,0,0)), ... 35 | % 0.025+0.3*abs(dpoly(p,[0.3,0.7; 0.7,0.5]))),0.15); 36 | % [p,t]=distmesh2d(fd,fh,0.01,[0,0;1,1],[0,0;1,0;0,1;1,1]); 37 | % 38 | % Example: (NACA0012 airfoil) 39 | % hlead=0.01; htrail=0.04; hmax=2; circx=2; circr=4; 40 | % a=.12/.2*[0.2969,-0.1260,-0.3516,0.2843,-0.1036]; 41 | % 42 | % fd=@(p) ddiff(dcircle(p,circx,0,circr),(abs(p(:,2))-polyval([a(5:-1:2),0],p(:,1))).^2-a(1)^2*p(:,1)); 43 | % fh=@(p) min(min(hlead+0.3*dcircle(p,0,0,0),htrail+0.3*dcircle(p,1,0,0)),hmax); 44 | % 45 | % fixx=1-htrail*cumsum(1.3.^(0:4)'); 46 | % fixy=a(1)*sqrt(fixx)+polyval([a(5:-1:2),0],fixx); 47 | % fix=[[circx+[-1,1,0,0]*circr; 0,0,circr*[-1,1]]'; 0,0; 1,0; fixx,fixy; fixx,-fixy]; 48 | % box=[circx-circr,-circr; circx+circr,circr]; 49 | % h0=min([hlead,htrail,hmax]); 50 | % 51 | % [p,t]=distmesh2d(fd,fh,h0,box,fix); 52 | 53 | % 54 | % See also: MESHDEMO2D, DISTMESHND, DELAUNAYN, TRIMESH. 55 | 56 | % distmesh2d.m v1.1 57 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details. 58 | 59 | dptol=.001; 60 | ttol=.1; Fscale=1.2; deltat=.2; geps=.001*h0; deps=sqrt(eps)*h0; 61 | densityctrlfreq=30; 62 | 63 | % 1. Create initial distribution in bounding box (equilateral triangles) 64 | [x,y]=meshgrid(bbox(1,1):h0:bbox(2,1),bbox(1,2):h0*sqrt(3)/2:bbox(2,2)); 65 | x(2:2:end,:)=x(2:2:end,:)+h0/2; % Shift even rows 66 | p=[x(:),y(:)]; % List of node coordinates 67 | 68 | % 2. Remove points outside the region, apply the rejection method 69 | p=p(feval(fd,p,varargin{:})ttol % Any large movement? 84 | pold=p; % Save current positions 85 | t=delaunayn(p); % List of triangles 86 | pmid=(p(t(:,1),:)+p(t(:,2),:)+p(t(:,3),:))/3; % Compute centroids 87 | t=t(feval(fd,pmid,varargin{:})<-geps,:); % Keep interior triangles 88 | % 4. Describe each bar by a unique pair of nodes 89 | bars=[t(:,[1,2]);t(:,[1,3]);t(:,[2,3])]; % Interior bars duplicated 90 | bars=unique(sort(bars,2),'rows'); % Bars as node pairs 91 | % 5. Graphical output of the current mesh 92 | % cla,patch('vertices',p,'faces',t,'edgecol','k','facecol',[.8,.9,1]); 93 | % drawnow 94 | end 95 | 96 | % 6. Move mesh points based on bar lengths L and forces F 97 | barvec=p(bars(:,1),:)-p(bars(:,2),:); % List of bar vectors 98 | L=sqrt(sum(barvec.^2,2)); % L = Bar lengths 99 | hbars=feval(fh,(p(bars(:,1),:)+p(bars(:,2),:))/2,varargin{:}); 100 | L0=hbars*Fscale*sqrt(sum(L.^2)/sum(hbars.^2)); % L0 = Desired lengths 101 | 102 | % Density control - remove points that are too close 103 | if mod(count,densityctrlfreq)==0 & any(L0>2*L) 104 | p(setdiff(reshape(bars(L0>2*L,:),[],1),1:nfix),:)=[]; 105 | N=size(p,1); pold=inf; 106 | continue; 107 | end 108 | 109 | F=max(L0-L,0); % Bar forces (scalars) 110 | Fvec=F./L*[1,1].*barvec; % Bar forces (x,y components) 111 | Ftot=full(sparse(bars(:,[1,1,2,2]),ones(size(F))*[1,2,1,2],[Fvec,-Fvec],N,2)); 112 | Ftot(1:size(pfix,1),:)=0; % Force = 0 at fixed points 113 | p=p+deltat*Ftot; % Update node positions 114 | 115 | % 7. Bring outside points back to the boundary 116 | d=feval(fd,p,varargin{:}); ix=d>0; % Find points outside (d>0) 117 | dgradx=(feval(fd,[p(ix,1)+deps,p(ix,2)],varargin{:})-d(ix))/deps; % Numerical 118 | dgrady=(feval(fd,[p(ix,1),p(ix,2)+deps],varargin{:})-d(ix))/deps; % gradient 119 | dgrad2=dgradx.^2+dgrady.^2; 120 | p(ix,:)=p(ix,:)-[d(ix).*dgradx./dgrad2,d(ix).*dgrady./dgrad2]; % Project 121 | 122 | % 8. Termination criterion: All interior nodes move less than dptol (scaled) 123 | % if min(abs(compute_angle(p,t))*180/pi) > 15, break; end 124 | if max(sqrt(sum(deltat*Ftot(d<-geps,:).^2,2))/h0) 1500 127 | break; 128 | end 129 | end 130 | 131 | % Clean up and plot final mesh 132 | [p,t]=fixmesh(p,t); 133 | % simpplot(p,t) 134 | -------------------------------------------------------------------------------- /Main_SH_roughness_projection.m: -------------------------------------------------------------------------------- 1 | %*************************************************************************% 2 | % All rights reserved (C) to the authors: Mahmoud SHAQFA, Gary CHOI % 3 | % and Katrin BEYER % 4 | % % 5 | % M. Shaqfa Contact: % 6 | % Earthquake Engineering and Structural Dynamics Laboratory (EESD), % 7 | % School of Architecture, Civil and Environmental Engineering (ENAC), % 8 | % Ecole polytechnique federale de Lausanne (EPFL), % 9 | % CH-1015 Lausanne, Switzerland. % 10 | % Tel.: +41 21 69 33297 % 11 | % Email: mahmoud.shaqfa@epfl.ch % 12 | % % 13 | % G. Choi Contact: % 14 | % Department of Mathematics, Massachusetts Institute of Technology (MIT) % 15 | % Cambridge, MA, USA % 16 | % Email: ptchoi@mit.edu % 17 | % % 18 | % K. Beyer Contact: % 19 | % Email: katrin.beyer@epfl.ch % 20 | %*************************************************************************% 21 | % This code includes implementations for: % 22 | % - Spherical Cap Harmonics (SCH) % 23 | % - Spherical Harmonics (SH) % 24 | % - HemiSpherical Harmonics (HSH) % 25 | % This code is part of the paper: "Spherical Cap Harmonic Analysis(SCHA)..% 26 | % for Characterising the Morphology of Rough Surface Patches" % 27 | % % 28 | %*************************************************************************% 29 | % This library is free software; you can redistribute it and/or modify % 30 | % it under the terms of the GNU Lesser General Public License as published% 31 | % by the Free Software Foundation; either version 2.1 of the License, or % 32 | % (at your option) any later version. % 33 | % % 34 | % This library is distributed in the hope that it will be useful, % 35 | % but WITHOUT ANY WARRANTY; without even the implied warranty of % 36 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % 37 | % See the GNU Lesser General Public License for more details. % 38 | % You should have received a copy of the GNU Lesser General Public License% 39 | % along with this library; if not, write to the Free Software Foundation, % 40 | % Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA % 41 | %*************************************************************************% 42 | % Author of this file: Mahmoud S. Shaqfa 43 | % This file is for the roughness projection on a spherical harmonics (closed objects). 44 | %% Paths 45 | % function Main_SH_roughness_projection 46 | close all; 47 | clear; clc; tic 48 | addpath('code') 49 | addpath('stlTools') 50 | addpath('rec_output') 51 | addpath('input_geom/donor_meshes/') 52 | addpath('analysis_results') 53 | addpath('spherical_parametrisation') 54 | addpath('spherical_parametrisation/extension/') 55 | addpath('spherical_parametrisation/mfile/') 56 | %% Reconstruction range for k \in [start, end] 57 | starting_degree = 5; 58 | ending_degree = 10; 59 | scale_factor = 0.01; 60 | plot_figures = true; 61 | %% Load the analysis reults from HSH or SCH only 62 | analysis_mat_file = 'rec_l_10_SH_rock_mesh_manifold.mat'; 63 | load(analysis_mat_file); close all; 64 | %% The donor mesh is the mesh that provides us with the shape and we project 65 | % roughness on it extracted from the SCH or HSH analyses. 66 | 67 | donor_mesh = false; % if false it will load a unit sphere as a donor mesh 68 | icosahedron_dome_refinement = 4; 69 | 70 | % donor_mesh_name = 'cube_refined_sample_1.stl'; 71 | % donor_mesh_name = 'cube_refined_sample_2.stl'; 72 | donor_mesh_name = 'cube_refined_sample_3.stl'; 73 | 74 | if donor_mesh 75 | [v, f] = stlRead(donor_mesh_name); 76 | else 77 | [v, f] = icosphere(icosahedron_dome_refinement); 78 | end 79 | v = [v(:,1) - mean(v(:,1)), v(:,2) - mean(v(:,2)), v(:,3) - mean(v(:,3))]; 80 | %% Specify (select) the group of vertices to be parametrized 81 | filter_query = strcat('selected_verts = find(v(v(:,1)>(0.8-0.0001)));'); % for a sphere 82 | % filter_query = strcat('selected_verts = find(v(v(:,3)>(0.5-0.0001)));'); % for the donor mesh 83 | % filter_query = strcat('selected_verts = find(v(v(:,3)>0));'); % for the hemisphere mesh 84 | % filter_query = strcat('selected_verts = [];'); % for the no selection 85 | eval(filter_query); 86 | 87 | if isempty(selected_verts) 88 | selected_verts = 1:length(v); 89 | end 90 | 91 | selected_groups = zeros(length(v(:, 1)), 1); 92 | selected_groups(selected_verts) = 1; 93 | %% Project roughness and render results 94 | if plot_figures 95 | paraview_patch(v, f, selected_groups) 96 | title('Input donor mesh') 97 | axis on 98 | xlabel('x'); ylabel('y'); zlabel('z') 99 | view([50 20]) 100 | end 101 | 102 | % Solve for \phi and \theta from the donor mesh patch on a sphere 103 | map_ = spherical_conformal_map(v,f); 104 | map = mobius_area_correction_spherical(v,f,map_); 105 | stlWrite(['rec_output/para_output_SH_', surface_name], f, map,'mode','ascii') 106 | 107 | if plot_figures 108 | paraview_patch(map, f) 109 | title('Input donor mesh parametrisation on a unit sphere') 110 | end 111 | 112 | selected_map = map(selected_verts, :); 113 | thetas = acos(selected_map(:,3)); % [0, theta_c] 114 | phis = atan2(selected_map(:,2),selected_map(:,1)); % [-pi, pi] 115 | k_range = [starting_degree, ending_degree]; 116 | 117 | reconstruction = real(SH_basis_range(phis, thetas, k_range, qm_k)); 118 | v(selected_verts, :) = v(selected_verts, :) + scale_factor*reconstruction; 119 | 120 | if plot_figures 121 | paraview_patch(v, f, selected_groups) 122 | title('Output of the roughened mesh') 123 | end 124 | %% Save the resulted STL file 125 | stlWrite(['roughness_output/rec_l_', num2str(starting_degree), '_to_',... 126 | num2str(ending_degree), '_SH_', surface_name]... 127 | , f, v,'mode','ascii') 128 | 129 | % end --------------------------------------------------------------------------------