const VI_BG=924203,VI_VALVES=["MV","AV"],VI_OPEN_PHASES={MV:[0,5,6],AV:[2,3]},VI_BODY_CM={MV_leaflet_0:2.5,MV_leaflet_1:1.8,TV_leaflet_0:1.2,TV_leaflet_1:1,TV_leaflet_2:1},VI_ORBIT={MV:{yaw:0,pitch:0},AV:{yaw:0,pitch:0}},VI_PITCH_MAX=1.5,viTurned=o=>VI_ORBIT[o].yaw!==0||VI_ORBIT[o].pitch!==0;function viMean(o){const n=[0,0,0],c=o.length/3;for(let r=0;r<o.length;r+=3)n[0]+=o[r],n[1]+=o[r+1],n[2]+=o[r+2];return[n[0]/c,n[1]/c,n[2]/c]}function viPlaneNormal(o){const n=viMean(o),c=[0,0,0,0,0,0];for(let r=0;r<o.length;r+=3){const p=o[r]-n[0],h=o[r+1]-n[1],a=o[r+2]-n[2];c[0]+=p*p,c[1]+=p*h,c[2]+=p*a,c[3]+=h*h,c[4]+=h*a,c[5]+=a*a}return viMinEigenvector(c)}function viBodyGeometry(o,n){if(n.every(a=>a))return o;const c=o.index?o.index.array:null,r=c?c.length:o.attributes.position.count,p=[];for(let a=0;a<r;a+=3){const i=c?c[a]:a,s=c?c[a+1]:a+1,y=c?c[a+2]:a+2;n[i]&&n[s]&&n[y]&&p.push(i,s,y)}const h=new THREE.BufferGeometry;for(const a in o.attributes)h.setAttribute(a,o.attributes[a]);return h.morphAttributes=o.morphAttributes,h.morphTargetsRelative=o.morphTargetsRelative,h.setIndex(p),h.boundingSphere=o.boundingSphere,h.boundingBox=o.boundingBox,h}function viMinEigenvector(o){const n=[[o[0],o[1],o[2]],[o[1],o[3],o[4]],[o[2],o[4],o[5]]],c=[[1,0,0],[0,1,0],[0,0,1]];for(let p=0;p<32&&!(n[0][1]*n[0][1]+n[0][2]*n[0][2]+n[1][2]*n[1][2]<1e-20);p++)for(let a=0;a<3;a++)for(let i=a+1;i<3;i++){if(Math.abs(n[a][i])<1e-18)continue;const s=(n[i][i]-n[a][a])/(2*n[a][i]),y=(s>=0?1:-1)/(Math.abs(s)+Math.sqrt(s*s+1)),b=1/Math.sqrt(y*y+1),I=y*b;for(let m=0;m<3;m++){const M=n[m][a],R=n[m][i];n[m][a]=b*M-I*R,n[m][i]=I*M+b*R}for(let m=0;m<3;m++){const M=n[a][m],R=n[i][m];n[a][m]=b*M-I*R,n[i][m]=I*M+b*R}for(let m=0;m<3;m++){const M=c[m][a],R=c[m][i];c[m][a]=b*M-I*R,c[m][i]=I*M+b*R}}let r=0;for(let p=1;p<3;p++)n[p][p]<n[r][r]&&(r=p);return new THREE.Vector3(c[0][r],c[1][r],c[2][r]).normalize()}function createValveInset(o,n){const c=o.userData.parts&&o.userData.parts.valves&&o.userData.parts.valves.userData.valves,r=new THREE.Scene;r.environment=n||null,r.environmentIntensity=.65;const p=new THREE.Mesh(new THREE.SphereGeometry(1,32,16),new GFX.M.Basic({color:924203,side:THREE.BackSide,depthWrite:!1}));p.frustumCulled=!1,p.renderOrder=-1,r.add(p),r.add(new THREE.HemisphereLight(15923199,1845822,.85));const h=new THREE.DirectionalLight(16120319,2.3),a=new THREE.DirectionalLight(12572402,.7);r.add(h,h.target,a,a.target),o.updateMatrixWorld(!0);const i={};for(const l of VI_VALVES){const e=c&&c[l],t=e&&e.userData.leaflets;if(!t||!t.length)continue;const d=t.map(v=>{const u=v.geometry.attributes.position,V=VI_BODY_CM[v.name],f=new Uint8Array(u.count).fill(1);if(V!=null){let H=1/0;for(let S=0;S<u.count;S++)H=Math.min(H,u.getZ(S));for(let S=0;S<u.count;S++)f[S]=u.getZ(S)-H<=V?1:0}return f}),w=[];t.forEach((v,u)=>{const V=v.geometry.attributes.position;for(let f=0;f<V.count;f++)d[u][f]&&w.push(V.getX(f),V.getY(f),V.getZ(f))});const E=viMean(w),g=!t.some(v=>VI_BODY_CM[v.name]!=null)?viPlaneNormal(w):new THREE.Vector3(0,0,1);g.z<0&&g.negate();const x=t.map((v,u)=>{const V=new GFX.M.Physical({color:v.material.color.getHex(),roughness:.38,metalness:0,clearcoat:.45,clearcoatRoughness:.4,sheen:.3,sheenRoughness:.6,sheenColor:new THREE.Color(16120063)}),f=new THREE.Mesh(viBodyGeometry(v.geometry,d[u]),V);return f.morphTargetInfluences=v.morphTargetInfluences,f.morphTargetDictionary=v.morphTargetDictionary,f.matrixAutoUpdate=!1,f.frustumCulled=!1,f.name="inset_"+v.name,r.add(f),f});i[l]={leaves:t,frame:t[0].parent,proxies:x,pts:w,center:new THREE.Vector3(E[0],E[1],E[2]),axis:g,radius:0}}if(i.MV&&i.AV){const l=i.AV.axis.clone().transformDirection(i.AV.frame.matrixWorld);i.MV.axis=l.transformDirection(new THREE.Matrix4().copy(i.MV.frame.matrixWorld).invert())}for(const l in i){const e=i[l],t=e.pts,d=e.axis,w=e.center;let E=0;for(let T=0;T<t.length;T+=3){const g=t[T]-w.x,x=t[T+1]-w.y,v=t[T+2]-w.z,u=g*d.x+x*d.y+v*d.z;E=Math.max(E,Math.hypot(g-u*d.x,x-u*d.y,v-u*d.z))}e.radius=E,e.pts=null}const s=new THREE.PerspectiveCamera(30,1,.01,100),y={frames:0,views:[],skipped:"init",dirs:{}},b=new THREE.Vector2,I=new THREE.Vector3,m=new THREE.Vector3,M=new THREE.Vector3,R=new THREE.Vector3,C=new THREE.Vector3,k=new THREE.Vector3,_=new THREE.Vector3;function N(l,e){const t=i[l],d=t.frame.matrixWorld,w=l==="MV"&&i.AV?i.AV:t,E=I.copy(t.center).applyMatrix4(d),T=m.copy(w.axis).transformDirection(w.frame.matrixWorld);C.setFromMatrixScale(d);const g=t.radius*(C.x+C.y+C.z)/3,x=THREE.MathUtils.degToRad(s.fov/2),v=Math.min(x,Math.atan(Math.tan(x)*e)),u=g*1.22/Math.tan(v);M.set(0,0,1).transformDirection(o.matrixWorld),M.addScaledVector(T,-M.dot(T)),M.lengthSq()<1e-10&&M.set(0,1,0).addScaledVector(T,-T.y),M.normalize(),R.crossVectors(M,T).normalize();const V=VI_ORBIT[l],f=Math.cos(V.yaw),H=Math.sin(V.yaw),S=Math.cos(V.pitch),D=Math.sin(V.pitch);k.copy(T).multiplyScalar(f).addScaledVector(R,H),R.multiplyScalar(f).addScaledVector(T,-H),_.copy(M).multiplyScalar(S).addScaledVector(k,-D),k.multiplyScalar(S).addScaledVector(M,D),s.position.copy(E).addScaledVector(k,u),s.up.copy(_),s.lookAt(E),s.aspect=e,s.near=u*.05,s.far=u*8,s.updateProjectionMatrix(),y.dirs[l]=[-k.x,-k.y,-k.z],p.position.copy(E),p.scale.setScalar(u*3),p.updateMatrixWorld(),h.position.copy(s.position).addScaledVector(_,u*.6).addScaledVector(R,-u*.4),h.target.position.copy(E),a.position.copy(E).addScaledVector(k,-u).addScaledVector(_,-u*.5),a.target.position.copy(E);for(const z of[h,h.target,a,a.target])z.updateMatrixWorld()}function P(l){const e=document.querySelector(".valve-inset"),t=e&&!e.classList.contains("collapsed")&&e.getClientRects().length?e.querySelectorAll(".vi-view"):[],d=[];for(const g of t){if(!i[g.dataset.valve]||getComputedStyle(g).visibility!=="visible")continue;const x=g.getBoundingClientRect();x.width>=16&&x.height>=16&&d.push({valve:g.dataset.valve,r:x})}if(!d.length){y.skipped=e?"hidden":"no-card";return}for(const g in i){const x=i[g];x.leaves.forEach((v,u)=>{const V=x.proxies[u];V.matrix.copy(v.matrixWorld),V.matrixWorldNeedsUpdate=!0,V.material.color.copy(v.material.color)})}const w=l.domElement.getBoundingClientRect();l.getSize(b);const E=l.autoClear,T=l.autoClearColor;l.autoClear=!0,l.autoClearColor=!1,l.setScissorTest(!0);for(const{valve:g,r:x}of d){const v=x.top-w.top,u=x.left-w.left,V=x.width,f=x.height,H=l.isWebGPURenderer?v:b.y-v-f;l.setViewport(u,H,V,f),l.setScissor(u,H,V,f);for(const S in i)for(const D of i[S].proxies)D.visible=S===g;N(g,V/f),l.render(r,s)}l.setScissorTest(!1),l.setViewport(0,0,b.x,b.y),l.autoClear=E,l.autoClearColor=T,y.frames++,y.views=d.map(g=>g.valve),y.skipped=""}function A(){r.traverse(l=>{l.material&&l.material.dispose()}),p.geometry.dispose()}return{render:P,dispose:A,stats:y,info:i}}function viMode(o){const n=o&&o.valveMod||{};return n.MV?"MV":n.AV?"AV":"both"}function ValveInsetCard({state:o,lang:n,t:c}){const[r,p]=React.useState(!1),[h,a]=React.useState(()=>({MV:viTurned("MV"),AV:viTurned("AV")})),i=React.useRef(null),s=(e,t)=>n==="en"?t:e,y=viMode(o),b=y==="both"?["MV","AV"]:[y],I={MV:s("\u4E8C\u5C16\u74E3","Mitral"),AV:s("\u4E3B\u52A8\u8109\u74E3","Aortic")},m={MV:s("\u540C\u4E3B\u52A8\u8109\u74E3\u955C\u5934 \xB7 \u524D\u65B9\u5728\u4E0A","same angle as aortic \xB7 anterior up"),AV:s("\u4E3B\u52A8\u8109\u9762\u89C2 \xB7 \u524D\u65B9\u5728\u4E0A","from the aorta \xB7 anterior up")},M=e=>t=>{i.current||t.button>0||(t.currentTarget.setPointerCapture(t.pointerId),i.current={v:e,id:t.pointerId,x:t.clientX,y:t.clientY,h:Math.max(80,t.currentTarget.clientHeight),moved:!1})},R=e=>{const t=i.current;if(!t||e.pointerId!==t.id)return;const d=VI_ORBIT[t.v],w=e.clientX-t.x,E=e.clientY-t.y;d.yaw-=2*Math.PI*w/t.h,d.pitch=Math.max(-VI_PITCH_MAX,Math.min(VI_PITCH_MAX,d.pitch+2*Math.PI*E/t.h)),t.x=e.clientX,t.y=e.clientY,(w||E)&&(t.moved=!0)},C=e=>{const t=i.current;!t||e.pointerId!==t.id||(i.current=null,t.moved&&a(d=>({...d,[t.v]:viTurned(t.v)})))},k=e=>{VI_ORBIT[e].yaw=0,VI_ORBIT[e].pitch=0,a(t=>({...t,[e]:!1}))},_=getPhaseIndex(c),N=e=>{const t=(o.valveMod||{})[e]||{};return VI_OPEN_PHASES[e].includes(_)?t.open!=null?{k:"limit",s:s(`\u5F00\u653E\u53D7\u9650\uFF08\u7EA6\u6B63\u5E38\u7684 ${Math.round(t.open*100)}%\uFF09`,`opens only ~${Math.round(t.open*100)}%`)}:{k:"open",s:s("\u5F00\u653E","open")}:t.leak!=null?{k:"leak",s:s("\u5173\u4E0D\u4E25 \xB7 \u4E2D\u592E\u7559\u7F1D","fails to close \xB7 central gap")}:{k:"shut",s:s("\u5173\u95ED","closed")}};let P,A;if(y==="both")P=s("\u74E3\u819C\u7279\u5199 \xB7 \u6B63\u5E38\u5BF9\u7167","Valve close-up \xB7 normal reference"),A=s("\u4E8C\u5C16\u74E3\u8212\u5F20\u671F\u5F00\u3001\u6536\u7F29\u671F\u5173\uFF1B\u4E3B\u52A8\u8109\u74E3\u6B63\u597D\u76F8\u53CD \u2014\u2014 \u4E24\u4E2A\u74E3\u6B64\u5F00\u5F7C\u5173\uFF0C\u4E0D\u4F1A\u540C\u65F6\u5F00\u653E","The mitral valve opens in diastole and closes in systole; the aortic valve does the opposite, so the two are never open together");else{P=s(`\u74E3\u819C\u7279\u5199 \xB7 ${o.zh}`,`Valve close-up \xB7 ${o.en}`);const e=o.valveMod[y],t=(o.calcified||[]).includes(y),d=e.open!=null?Math.round(e.open*100):null;y==="MV"?A=d!=null?s(`\u8212\u5F20\u671F\u74E3\u53E3\u53EA\u5F00\u5230\u6B63\u5E38\u7684\u7EA6 ${d}%${t?"\uFF0C\u74E3\u53F6\u589E\u539A\u3001\u9499\u5316\uFF08\u6697\u9EC4\uFF09":""}`,`In diastole the orifice opens to only ~${d}% of normal${t?"; the leaflets are thickened and calcified (dull yellow)":""}`):s("\u6536\u7F29\u671F\u4E24\u4E2A\u74E3\u53F6\u5408\u4E0D\u62E2\uFF0C\u4E2D\u592E\u7559\u4E00\u6761\u7F1D\uFF1A\u8840\u4ECE\u8FD9\u91CC\u53CD\u6D41\u56DE\u5DE6\u623F","In systole the two leaflets fail to meet, leaving a central gap: blood leaks back into the left atrium"):A=d!=null?s(`\u6536\u7F29\u671F\u74E3\u53E3\u53EA\u5F00\u5230\u6B63\u5E38\u7684\u7EA6 ${d}%${t?"\uFF0C\u74E3\u53F6\u9499\u5316\uFF08\u6697\u9EC4\uFF09":""}`,`In systole the orifice opens to only ~${d}% of normal${t?"; the cusps are calcified (dull yellow)":""}`):s("\u8212\u5F20\u671F\u4E09\u4E2A\u74E3\u53F6\u5408\u4E0D\u62E2\uFF0C\u4E2D\u592E\u7559\u4E00\u6761\u7F1D\uFF1A\u8840\u4ECE\u8FD9\u91CC\u53CD\u6D41\u56DE\u5DE6\u5BA4","In diastole the three cusps fail to meet, leaving a central gap: blood leaks back into the left ventricle")}const l=PHASES[_];return React.createElement("div",{className:`valve-inset ${r?"collapsed":""}`,"data-vi-mode":y},React.createElement("div",{className:"vi-head"},React.createElement("span",{className:"vi-title"},P),React.createElement("button",{className:"vi-toggle",title:r?s("\u5C55\u5F00","Expand"):s("\u6536\u8D77","Collapse"),onClick:()=>p(e=>!e)},r?"\u25B8":"\u2013")),!r&&React.createElement(React.Fragment,null,React.createElement("div",{className:`vi-views n${b.length}`},b.map(e=>{const t=N(e);return React.createElement("div",{key:e,className:"vi-view","data-valve":e,title:s("\u62D6\u52A8\u8F6C\u52A8\u955C\u5934 \xB7 \u53CC\u51FB\u590D\u4F4D","Drag to rotate \xB7 double-click to reset"),onPointerDown:M(e),onPointerMove:R,onPointerUp:C,onPointerCancel:C,onDoubleClick:()=>k(e)},React.createElement("div",{className:"vi-lbl"},React.createElement("b",null,I[e]),React.createElement("span",null,h[e]?s("\u5DF2\u8F6C\u52A8","rotated"):m[e])),React.createElement("div",{className:`vi-stat ${t.k}`},React.createElement("i",null),t.s),h[e]?React.createElement("button",{className:"vi-reset",onPointerDown:d=>d.stopPropagation(),onClick:()=>k(e)},s("\u21BA \u590D\u4F4D","\u21BA reset")):React.createElement("span",{className:"vi-hint"},s("\u27F3 \u62D6\u52A8","\u27F3 drag")))})),React.createElement("div",{className:"vi-foot"},React.createElement("div",{className:"vi-find"},A),React.createElement("div",{className:"vi-now"},s("\u6B64\u523B\uFF1A","Now: "),n==="en"?l.en:l.zh))))}
