// Anatomical fsaverage surface geometry; colors are the actual model outputs. export function corticalSurface(canvas,model){ const gl=canvas.getContext('webgl',{antialias:true,alpha:false}); if(!gl)throw Error('This browser cannot render the cortical surface. Enable WebGL or use another browser.'); const vertex=`attribute vec3 position;attribute vec3 normal;attribute float value;uniform float angle;uniform float amplitude;uniform float aspect;varying vec3 shade;void main(){float c=cos(angle),s=sin(angle);mat3 r=mat3(c,0.,-s,0.,1.,0.,s,0.,c);vec3 p=r*position;vec3 n=normalize(r*normal);float v=clamp(value/amplitude,-1.,1.);vec3 pale=vec3(.86,.88,.82);vec3 color=v<0.?mix(pale,vec3(.22,.54,.72),-v):mix(pale,vec3(.91,.40,.20),v);float light=.48+.52*abs(dot(n,normalize(vec3(-.4,.7,1.))));shade=color*light;float depth=2.6-p.z;gl_Position=vec4(p.x*2.5/aspect,p.y*2.5,depth-1.,depth);gl_PointSize=2.;}`; const fragment=`precision mediump float;varying vec3 shade;void main(){gl_FragColor=vec4(shade,1.);}`; const shader=(type,text)=>{const s=gl.createShader(type);gl.shaderSource(s,text);gl.compileShader(s);if(!gl.getShaderParameter(s,gl.COMPILE_STATUS))throw Error('Surface shader compilation failed.');return s}; const program=gl.createProgram();gl.attachShader(program,shader(gl.VERTEX_SHADER,vertex));gl.attachShader(program,shader(gl.FRAGMENT_SHADER,fragment));gl.linkProgram(program);gl.useProgram(program); const vertices=model.vertices,n=vertices.length;const mins=[Infinity,Infinity,Infinity],maxs=[-Infinity,-Infinity,-Infinity];for(const p of vertices)for(let i=0;i<3;i++){mins[i]=Math.min(mins[i],p[i]);maxs[i]=Math.max(maxs[i],p[i])}const center=mins.map((v,i)=>(v+maxs[i])/2),extent=Math.max(...maxs.map((v,i)=>v-mins[i]))/2; const pos=new Float32Array(n*3);vertices.forEach((p,i)=>{pos[i*3]=(p[0]-center[0])/extent;pos[i*3+1]=(p[2]-center[2])/extent;pos[i*3+2]=-(p[1]-center[1])/extent}); const normals=new Float32Array(n*3);for(const face of model.faces||[]){const[a,b,c]=face.map(i=>i*3),u=[pos[b]-pos[a],pos[b+1]-pos[a+1],pos[b+2]-pos[a+2]],v=[pos[c]-pos[a],pos[c+1]-pos[a+1],pos[c+2]-pos[a+2]],cross=[u[1]*v[2]-u[2]*v[1],u[2]*v[0]-u[0]*v[2],u[0]*v[1]-u[1]*v[0]];for(const k of [a,b,c])for(let j=0;j<3;j++)normals[k+j]+=cross[j]} for(let i=0;i