@omrips/viewstl 2.0.0-rc.3 → 2.0.0-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +19 -170
  2. package/build/{v2/THIRD-PARTY-NOTICES.txt → THIRD-PARTY-NOTICES.txt} +1006 -1006
  3. package/build/{v2/cad → cad}/occt-import-js.source.txt +19 -19
  4. package/build/chunks/archive-JU75RPTU.js +1 -0
  5. package/build/{v2/chunks/cad-VY42CMP4.js → chunks/cad-4JYHORYZ.js} +1 -1
  6. package/build/{v2/chunks/chunk-JH3H3JWA.js → chunks/chunk-OP2L7L4N.js} +1 -1
  7. package/build/{v2/chunks/chunk-ZMK3UZZE.js → chunks/chunk-PQYEV7WH.js} +1 -1
  8. package/build/{v2/chunks/chunk-R3HNTB5A.js → chunks/chunk-Y6VY64LY.js} +1 -1
  9. package/build/{v2/chunks/export-7OP4ZGGJ.js → chunks/export-VM6OQCQJ.js} +3 -3
  10. package/build/{v2/chunks/export-cad-QPBT756V.js → chunks/export-cad-QXCB6UDU.js} +1 -1
  11. package/build/{v2/chunks/export-zip-EDCWH5PX.js → chunks/export-zip-PVXETVLK.js} +1 -1
  12. package/build/{v2/chunks/print-check-2PU7CSL3.js → chunks/print-check-NJ4IONC5.js} +1 -1
  13. package/build/{v2/chunks/scene-export-KQBS7VRJ.js → chunks/scene-export-ZIXMUNLI.js} +1 -1
  14. package/build/chunks/scene-reader-JGHERXR2.js +1 -0
  15. package/build/{v2/node → node}/analysis-worker.js +11 -11
  16. package/build/{v2/node → node}/index.cjs +7 -7
  17. package/build/{v2/node → node}/index.js +6 -6
  18. package/build/stl_viewer.min.js +51 -0
  19. package/build/{v2/viewstl.cjs → viewstl.cjs} +752 -125
  20. package/build/{v2/viewstl.d.cts → viewstl.d.cts} +50 -0
  21. package/build/{v2/viewstl.d.ts → viewstl.d.ts} +50 -0
  22. package/build/{v2/viewstl.js → viewstl.js} +752 -125
  23. package/build/viewstl.min.js +51 -0
  24. package/build/{v2/viewstl.worker.js → viewstl.worker.js} +1 -1
  25. package/docs/releases/2.0.0-rc.5.md +46 -0
  26. package/docs/v2-compatibility.md +5 -5
  27. package/docs/v2-usage.md +128 -21
  28. package/docs/v2-verification.md +158 -0
  29. package/legacy/README.md +14 -8
  30. package/package.json +18 -35
  31. package/build/v2/README.md +0 -15
  32. package/build/v2/chunks/archive-VGS4J4Z7.js +0 -1
  33. package/build/v2/chunks/scene-reader-APUJOPUR.js +0 -1
  34. package/build/v2/stl_viewer.min.js +0 -51
  35. package/build/v2/viewstl.min.js +0 -51
  36. /package/build/{v2/cad → cad}/occt-import-js.js +0 -0
  37. /package/build/{v2/cad → cad}/occt-import-js.wasm +0 -0
  38. /package/build/{v2/cad → cad}/prepare-cad.source.txt +0 -0
  39. /package/build/{v2/chunks/chunk-U33KANV7.js → chunks/chunk-IK3WL53C.js} +0 -0
  40. /package/build/{v2/chunks/chunk-JS56KC3I.js → chunks/chunk-W5KVLGVR.js} +0 -0
  41. /package/build/{v2/chunks → chunks}/chunk-ZSV4PTIA.js +0 -0
  42. /package/build/{v2/node → node}/index.d.cts +0 -0
  43. /package/build/{v2/node → node}/index.d.ts +0 -0
@@ -0,0 +1,51 @@
1
+ (()=>{function oi(t){let e=typeof t=="number"&&Number.isFinite(t)&&t>=.25&&t<=128,i=(e?t:2)*1073741824,o=Math.floor(i/2);return{budget:o,reported:e,estimated:i,limits:{max_memory_bytes:o,max_file_bytes:Math.floor(o/3),max_triangles:Math.max(1,Math.floor((o-64*1048576)/224)),max_uncompressed_bytes:Math.max(1,Math.floor((o-64*1048576)/4))}}}async function ni(t){if(/\.(step|stp|iges|igs)$/i.test(t.name))return 128*1048576+(t.size||0)*40;if(/\.stl$/i.test(t.name)&&t.size>=84&&t.slice){let e=await t.slice(0,84).arrayBuffer(),i=new DataView(e).getUint32(80,!0);if(i&&84+i*50===t.size)return 64*1048576+i*224}return 64*1048576+(t.size||0)*3}var $e=["x","y","z"],Ie=t=>$e.map(e=>t[e]),kt=t=>Object.fromEntries($e.map((e,i)=>[e,t[i]])),ai=(t,e)=>t.reduce((i,o,s)=>i+o*e[s],0),li=t=>{let e=Math.hypot(...t)||1;return t.map(i=>i/e)},ri=(t,e)=>Array.from({length:9},(i,o)=>[0,1,2].reduce((s,n)=>s+t[Math.floor(o/3)*3+n]*e[n*3+o%3],0)),ci=(t,e)=>[0,1,2].map(i=>ai(t.slice(i*3,i*3+3),e));function Ce(t={x:0,y:0,z:0}){let[e,i,o]=Ie(t).map(m=>m%360*Math.PI/180),s=Math.cos(e),n=Math.sin(e),r=Math.cos(i),d=Math.sin(i),l=Math.cos(o),c=Math.sin(o);return[r*l,-r*c,d,s*c+n*l*d,s*l-n*d*c,-n*r,n*c-s*l*d,n*l+s*d*c,s*r]}var At=t=>t.min.map((e,i)=>(e+t.max[i])/2);function di(t){return kt(At(t.bounds).map((e,i)=>e+t.position[$e[i]]))}function mi(t){return Math.max(Math.hypot(...t.bounds.max.map((e,i)=>e-t.bounds.min[i]))/2,1e-6)}function Le(t){let e=At(t.bounds),i=ci(Ce(t.rotation),e);return kt(e.map((o,s)=>t.position[$e[s]]+o-i[s]))}function St(t){if(!t.rotation||!Object.values(t.rotation).some(n=>n%360))return t.bounds;let e=At(t.bounds),i=Ce(t.rotation),o=[1/0,1/0,1/0],s=[-1/0,-1/0,-1/0];for(let n=0;n<8;n++)ci(i,e.map((d,l)=>t.bounds[n&1<<l?"max":"min"][l]-d)).forEach((d,l)=>{o[l]=Math.min(o[l],d+e[l]),s[l]=Math.max(s[l],d+e[l])});return{min:o,max:s}}function si(t,e){let[i,o,s]=li(t),n=Math.cos(e),r=Math.sin(e),d=1-n;return[d*i*i+n,d*i*o-r*s,d*i*s+r*o,d*i*o+r*s,d*o*o+n,d*o*s-r*i,d*i*s-r*o,d*o*s+r*i,d*s*s+n]}function hi(t,e,i,o,s){let n=Math.PI/Math.max(1,s),r=ri(si(e.right,o*n),ri(si(e.up,i*n),Ce(t))),d=Math.asin(Math.max(-1,Math.min(1,r[2]))),l=Math.abs(Math.cos(d))>1e-10;return{x:(l?Math.atan2(-r[5],r[8]):Math.atan2(r[7],r[4]))*180/Math.PI,y:d*180/Math.PI,z:(l?Math.atan2(-r[1],r[0]):0)*180/Math.PI}}function Pe(t,e,i){if(i===0)return{...t.position};let o=Ie(e.position),s=Ie(t.center),n=s.map((x,f)=>x-o[f]),r=li(Ie(e.target).map((x,f)=>x-o[f])),d=Math.hypot(...n),l=d>1e-9?n.map(x=>x/d):r,c=Math.max(t.radius||1e-6,1e-6)*1.1/Math.max(.05,ai(l,r)),m=(d||c)*Math.exp(Math.max(-6,Math.min(6,i))),g=i<0?Math.max(Math.min(d,c),m):Math.max(d,Math.min(1e12,m));return kt(Ie(t.position).map((x,f)=>x+l[f]*(g-d)))}function ui(t,e){if(e==="custom")return t.map(n=>({id:n.id,...n.position||{x:0,y:0,z:0}}));if(e==="original")return t.map(n=>({id:n.id,...n.originalPosition||{x:0,y:0,z:0}}));t=t.map(n=>({...n,bounds:St(n)}));let i=t.map(n=>n.bounds.max[0]-n.bounds.min[0]),o=t.reduce((n,r)=>Math.max(n,...r.bounds.max.map((d,l)=>d-r.bounds.min[l])),1)*.12,s=-(i.reduce((n,r)=>n+r,0)+o*Math.max(0,t.length-1))/2;return t.map((n,r)=>{let d={id:n.id,x:s-n.bounds.min[0],y:-(n.bounds.min[1]+n.bounds.max[1])/2,z:-n.bounds.min[2]};return s+=i[r]+o,d})}function fi(t,e){let i=new Map(e.map(o=>[o.id,o.z]));return t.length?t.reduce((o,s)=>Math.min(o,St(s).min[2]+(i.get(s.id)||0)),1/0):0}var et=t=>[t.x,t.y,t.z],pi=(t,e)=>t.map((i,o)=>i-e[o]),io=(t,e)=>t.reduce((i,o,s)=>i+o*e[s],0),zt=(t,e)=>[t[1]*e[2]-t[2]*e[1],t[2]*e[0]-t[0]*e[2],t[0]*e[1]-t[1]*e[0]],Rt=t=>{let e=Math.hypot(...t);return t.map(i=>i/(e||1))};function oo(t,e,i){let o=et(t.position),s=et(t.target),n=Rt(pi(o,s)),r=Rt(zt(et(t.up),n));Math.hypot(...r)<.5&&(r=Rt(zt(Math.abs(n[1])<.9?[0,1,0]:[1,0,0],n)));let d=zt(n,r),l=io(pi(o,et(e)),n),c=2*Math.max(l,1e-6)*Math.tan(Math.PI/8)/Math.max(i,1);return{right:r,up:d,scale:c}}function no(t,e,i,o){return Object.fromEntries(["x","y","z"].map((s,n)=>[s,t[s]+(e.right[n]*i-e.up[n]*o)*e.scale]))}function _i(t,{getModel:e,getCamera:i,getTool:o=()=>"move",onMove:s,onRotate:n,onStart:r,onEnd:d}){let l=!1,c=null,m=new Set,g=[],x=(u,y)=>{t.addEventListener(u,y,{capture:!0,passive:!1}),g.push(()=>t.removeEventListener(u,y,!0))},f=u=>{u.preventDefault(),u.stopImmediatePropagation()},p=(u=!1)=>{if(!c)return;let y=c;c=null,u&&(y.tool==="rotate"?n?.(y.id,y.rotation):s(y.id,y.position)),t.classList.remove("moving-model"),t.hasPointerCapture(y.pointer)&&t.releasePointerCapture(y.pointer),d?.(y.id,u)};x("pointerdown",u=>{if(!l||u.button!==0||u.shiftKey)return;if(f(u),c||u.isPrimary===!1){m.add(u.pointerId);return}let y=e();y&&(c={tool:o(),rotation:{...y.rotation||{x:0,y:0,z:0}},model:{...y,position:{...y.position},center:{...y.center}},camera:i(),height:t.getBoundingClientRect().height,id:y.id,position:{...y.position},pointer:u.pointerId,x:u.clientX,y:u.clientY,basis:oo(i(),y.center,t.getBoundingClientRect().height)},r?.(y.id),t.setPointerCapture(u.pointerId),t.classList.add("moving-model"),t.focus({preventScroll:!0}))}),x("pointermove",u=>{if(m.has(u.pointerId)){f(u);return}if(!c||c.pointer!==u.pointerId)return;f(u);let y=u.clientX-c.x,_=u.clientY-c.y;c.tool==="rotate"?n?.(c.id,hi(c.rotation,c.basis,y,_,c.height)):c.tool==="depth"?s(c.id,Pe(c.model,c.camera,_*3/Math.max(1,c.height))):s(c.id,no(c.position,c.basis,y,_))});for(let u of["pointerup","pointercancel","lostpointercapture"])x(u,y=>{if(m.delete(y.pointerId)){f(y);return}!c||c.pointer!==y.pointerId||(f(y),p(u!=="pointerup"))});x("wheel",u=>{if(!l||(f(u),c))return;let y=e();if(!y)return;let _=u.deltaY*(u.deltaMode===1?16:u.deltaMode===2?t.getBoundingClientRect().height:1);s(y.id,Pe(y,i(),Math.max(-.5,Math.min(.5,_*.0015))))});for(let u of["keydown","dblclick"])x(u,y=>{c&&y.key!=="Escape"&&f(y)});return{setEnabled(u){p(!0),m.clear(),l=u,t.classList.toggle("model-move-ready",u)},dispose(){p(!0),g.forEach(u=>u())}}}function It(t){let e=Array.from(t?.types||[],i=>i.toLowerCase());return e.includes("files")||t?.files?.length?"files":e.includes("text/uri-list")||e.includes("text/x-moz-url")?"url":e.includes("text/plain")?"text":null}function gi(t){if(It(t)==="files")return[];let e=t?.getData("text/uri-list")?.trim(),i=!e&&t?.getData("text/x-moz-url")?.trim(),o=!e&&!i&&t?.getData("text/plain")?.trim(),s;if(e)s=e.split(/\r?\n/).map(r=>r.trim()).filter(r=>r&&!r.startsWith("#"));else if(i)s=[i.split(/\r?\n/)[0]];else if(o){if(s=o.split(/\r?\n/).map(r=>r.trim()).filter(Boolean),!s.every(r=>/^https?:\/\/\S+$/i.test(r)))return[]}else return[];let n=new Set;for(let r of s){let d;try{d=new URL(r)}catch{}if(!d||!["https:","http:"].includes(d.protocol)||d.username||d.password)throw new Error("Drop a public HTTP or HTTPS file URL without login credentials.");d.hash="",n.add(d.href)}return[...n]}var ro=["flat","smooth","wireframe","edges","mesh","xray","studio","orientation"];function tt(t,e){if(!ro.includes(t))throw new Error("Invalid display mode");if(typeof e!="boolean")throw new Error("smooth_shading must be a boolean");return{display:t,smooth_shading:t==="smooth"||e}}function Ct(t){return{smooth:(t.smooth_shading||t.display==="smooth")&&t.display!=="wireframe",edges:t.view_edges||t.display==="edges",wire:t.display==="wireframe",edgeAngle:t.display==="edges"?30:1}}function it(t){if(t==null)return null;if(!["x","y","z"].includes(t.axis))throw new Error("Section axis must be x, y or z");if(!Number.isFinite(t.position)||t.position<0||t.position>1)throw new Error("Section position must be between 0 and 1");if(t.flip!==void 0&&typeof t.flip!="boolean")throw new Error("Section flip must be a boolean");return{axis:t.axis,position:t.position,flip:t.flip??!1}}function ot(t,e){if(!e)return[0,0,0,0];let i=["x","y","z"].indexOf(e.axis),o=e.flip?-1:1,s=[0,0,0,0];return s[i]=o,s[3]=-o*(t.min[i]+(t.max[i]-t.min[i])*e.position),s}var re=(t,e)=>t.map((i,o)=>i+e[o]),H=(t,e)=>t.map((i,o)=>i-e[o]),oe=(t,e)=>t.map(i=>i*e),ie=(t,e)=>t[0]*e[0]+t[1]*e[1]+t[2]*e[2],ne=(t,e)=>[t[1]*e[2]-t[2]*e[1],t[2]*e[0]-t[0]*e[2],t[0]*e[1]-t[1]*e[0]],q=t=>oe(t,1/(Math.hypot(...t)||1));function Te(t,e){let i=new Float64Array(16);for(let o=0;o<4;o++)for(let s=0;s<4;s++)for(let n=0;n<4;n++)i[o*4+s]+=t[n*4+s]*e[o*4+n];return i}function se(t,e,i=1){let o=t[3]*e[0]+t[7]*e[1]+t[11]*e[2]+t[15]*i;return[0,1,2].map(s=>(t[s]*e[0]+t[4+s]*e[1]+t[8+s]*e[2]+t[12+s]*i)/(i&&o?o:1))}function so(t,e,i){let o=Math.cos(t),s=Math.sin(t),n=Math.cos(e),r=Math.sin(e),d=Math.cos(i),l=Math.sin(i);return new Float64Array([n*d,o*l+s*d*r,s*l-o*d*r,0,-n*l,o*d-s*l*r,s*d+o*l*r,0,r,-s*n,o*n,0,0,0,0,1])}function J(t){let e=so(t.rotationx,t.rotationy,t.rotationz),i=t.scaleVector;for(let s=0;s<3;s++)for(let n=0;n<3;n++)e[s*4+n]*=i[s];let o=t.center||[0,0,0];for(let s=0;s<3;s++)e[12+s]=[t.x,t.y,t.z][s]-e[s]*o[0]-e[4+s]*o[1]-e[8+s]*o[2];return e}function ye(t){let e=Array.from({length:4},(i,o)=>[...Array.from({length:4},(s,n)=>t[n*4+o]),...Array.from({length:4},(s,n)=>+(n===o))]);for(let i=0;i<4;i++){let o=i;for(let n=i+1;n<4;n++)Math.abs(e[n][i])>Math.abs(e[o][i])&&(o=n);if(Math.abs(e[o][i])<1e-30)throw new Error("Singular transform");[e[o],e[i]]=[e[i],e[o]];let s=e[i][i];e[i]=e[i].map(n=>n/s);for(let n=0;n<4;n++)if(n!==i){let r=e[n][i];e[n]=e[n].map((d,l)=>d-r*e[i][l])}}return new Float64Array(Array.from({length:16},(i,o)=>e[o%4][4+Math.floor(o/4)]))}function nt(t,e){let i=q(H(t.position,t.target)),o=q(ne(t.up,i));Math.hypot(...o)<.5&&(o=q(ne(Math.abs(i[1])<.9?[0,1,0]:[1,0,0],i)));let s=ne(i,o),n=t.position,r=new Float64Array([o[0],s[0],i[0],0,o[1],s[1],i[1],0,o[2],s[2],i[2],0,-ie(o,n),-ie(s,n),-ie(i,n),1]),d=1/Math.tan(Math.PI/8),l=t.near,c=t.far,m=new Float64Array([d/e,0,0,0,0,d,0,0,0,0,(c+l)/(l-c),-1,0,0,2*c*l/(l-c),0]);return Te(m,r)}function rt(t,e,i){let o=q(e),s=Math.cos(i),n=Math.sin(i);return re(re(oe(t,s),oe(ne(o,t),n)),oe(o,ie(o,t)*(1-s)))}function Lt(t,e,i,o){let s=0,n=1/0;for(let r=0;r<3;r++){if(Math.abs(e[r])<1e-15){if(t[r]<i[r]||t[r]>o[r])return!1;continue}let d=(i[r]-t[r])/e[r],l=(o[r]-t[r])/e[r];if(d>l&&([d,l]=[l,d]),s=Math.max(s,d),n=Math.min(n,l),n<s)return!1}return!0}function yi(t,e,i,o){let s=[i[o],i[o+1],i[o+2]],n=H([i[o+3],i[o+4],i[o+5]],s),r=H([i[o+6],i[o+7],i[o+8]],s),d=ne(e,r),l=ie(n,d);if(Math.abs(l)<1e-12)return 1/0;let c=H(t,s),m=ie(c,d)/l;if(m<0||m>1)return 1/0;let g=ne(c,n),x=ie(e,g)/l;if(x<0||m+x>1)return 1/0;let f=ie(r,g)/l;return f>=0?f:1/0}function bi(t){return t?t.triangles*224+(t.smoothNormals?.byteLength||0)*4+((t.edges?.byteLength||0)+(t.wire?.byteLength||0))*2:0}function Q(t,e,i="model"){if(e>(t.max_memory_bytes??Number.MAX_SAFE_INTEGER))throw new Error(`Estimated memory for this ${i} exceeds the device safety budget. Use a smaller model or a device with more memory.`)}function vi(t,e){return 67108864+t*(224+(e.smooth?1536:0)+(e.edges||e.wire?2048:0))}var wi=Object.freeze({max_file_bytes:256*1024*1024,max_triangles:5e6,max_uncompressed_bytes:256*1024*1024,max_archive_entries:1024,max_memory_bytes:Number.MAX_SAFE_INTEGER});function xi(t={}){let e={...wi};for(let i of Object.keys(e))if(t[i]!==void 0){if(!Number.isSafeInteger(t[i])||t[i]<1)throw new Error(`Invalid ${i}`);e[i]=t[i]}return e}function X(t){if(typeof t!="string"||!/^#[\da-f]{6}$/i.test(t))throw new Error("Color must be #RRGGBB");return[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)/255)}function st(t,e=null,i=wi){if(!(t instanceof Float32Array)||!t.length||t.length%9)throw new Error("Geometry must contain complete triangles");if(t.length/9>i.max_triangles)throw new Error("Triangle limit exceeded");if(Q(i,67108864+t.length/9*224),e&&(!(e instanceof Float32Array)||e.length!==t.length))throw new Error("Invalid vertex colors");let o={min:[1/0,1/0,1/0],max:[-1/0,-1/0,-1/0]};for(let c=0;c<t.length;c++){let m=t[c],g=c%3;if(!Number.isFinite(m))throw new Error("Non-finite vertex coordinate");if(o.min[g]=Math.min(o.min[g],m),o.max[g]=Math.max(o.max[g],m),e&&(!Number.isFinite(e[c])||e[c]<0||e[c]>1))throw new Error("Invalid vertex color")}let s=new Float32Array(t.length),n=new Float32Array(Math.ceil(t.length/9/64)*6),r=0,d=0,l=0;for(let c=0;c<t.length;c+=9){let m=t[c+3]-t[c],g=t[c+4]-t[c+1],x=t[c+5]-t[c+2],f=t[c+6]-t[c],p=t[c+7]-t[c+1],u=t[c+8]-t[c+2],y=g*u-x*p,_=x*f-m*u,E=m*p-g*f,z=Math.hypot(y,_,E);r+=z/2,d+=(t[c]*y+t[c+1]*_+t[c+2]*E)/6,z||l++;for(let b=0;b<9;b+=3)s[c+b]=y/(z||1),s[c+b+1]=_/(z||1),s[c+b+2]=E/(z||1);let k=Math.floor(c/9/64)*6;c/9%64===0&&n.set([1/0,1/0,1/0,-1/0,-1/0,-1/0],k);for(let b=0;b<9;b++){let S=b%3;n[k+S]=Math.min(n[k+S],t[c+b]),n[k+3+S]=Math.max(n[k+3+S],t[c+b])}}if(!Number.isFinite(r)||!Number.isFinite(d))throw new Error("Geometry magnitude exceeds numeric limits");return{positions:t,normals:s,colors:e,blocks:n,bounds:o,triangles:t.length/9,area:r,volume:Math.abs(d),degenerateTriangles:l}}function Ei(t,e,i,o=[0,0,0,0]){if(!Lt(e,i,t.bounds.min,t.bounds.max))return 1/0;let s=1/0;for(let n=0;n<t.blocks.length;n+=6){if(!Lt(e,i,t.blocks.subarray(n,n+3),t.blocks.subarray(n+3,n+6)))continue;let r=n/6*64*9,d=Math.min(r+576,t.positions.length);for(let l=r;l<d;l+=9){let c=yi(e,i,t.positions,l);c<s&&o[3]+o[0]*(e[0]+i[0]*c)+o[1]*(e[1]+i[1]*c)+o[2]*(e[2]+i[2]*c)<=0&&(s=c)}}return s}function Mi(t,e){if(e[0]===e[1]&&e[1]===e[2])return{area:t.area*e[0]*e[0],volume:t.volume*Math.abs(e[0]**3)};let i=0,o=t.positions;for(let s=0;s<o.length;s+=9){let n=(o[s+3]-o[s])*e[0],r=(o[s+4]-o[s+1])*e[1],d=(o[s+5]-o[s+2])*e[2],l=(o[s+6]-o[s])*e[0],c=(o[s+7]-o[s+1])*e[1],m=(o[s+8]-o[s+2])*e[2];i+=Math.hypot(r*m-d*c,d*l-n*m,n*c-r*l)/2}return{area:i,volume:t.volume*Math.abs(e[0]*e[1]*e[2])}}function Pt(t,e=[1,1,1],i=[0,0,0]){let o=new ArrayBuffer(84+t.triangles*50),s=new DataView(o),n=t.positions;new Uint8Array(o).set(new TextEncoder().encode("Binary STL by ViewSTL 2")),s.setUint32(80,t.triangles,!0);for(let r=0;r<t.triangles;r++){let d=84+r*50,l=Array.from({length:9},(f,p)=>(n[r*9+p]-i[p%3])*e[p%3]),c=[l[3]-l[0],l[4]-l[1],l[5]-l[2]],m=[l[6]-l[0],l[7]-l[1],l[8]-l[2]],g=[c[1]*m[2]-c[2]*m[1],c[2]*m[0]-c[0]*m[2],c[0]*m[1]-c[1]*m[0]],x=Math.hypot(...g)||1;for(let f=0;f<3;f++)s.setFloat32(d+f*4,g[f]/x,!0);for(let f=0;f<9;f++)s.setFloat32(d+12+f*4,l[f],!0);if(t.colors){let f=[0,1,2].map(p=>Math.round(t.colors[r*9+p]*31));s.setUint16(d+48,32768|f[0]<<10|f[1]<<5|f[2],!0)}}return o}var Oe=["x","y","z","rotationx","rotationy","rotationz","scalex","scaley","scalez"],ae=t=>t.startsWith("scale"),me=(t,e,i=!1)=>{if(typeof t!="number"||!Number.isFinite(t)||Math.abs(t)>Number.MAX_SAFE_INTEGER||i&&t<=0)throw new Error(`Invalid animation ${e}`);return t},at=(t,e)=>{if(!t||typeof t!="object"||Array.isArray(t))throw new Error(`Invalid animation ${e}`);return t};function ki(t){return Object.fromEntries(Oe.map(e=>[e,me(t[e]??(ae(e)?t.scale??1:0),e,ae(e))]))}function Fe(t){at(t,"definition");let e={},i=new Set;for(let o of["delta","exact"]){if(t[o]==null)continue;let s=at(t[o],o),n={},r=me(s.msec??300,"duration",!0);if(s.loop!==void 0&&typeof s.loop!="boolean")throw new Error("Animation loop must be a boolean");let d=o==="delta"&&!!s.loop;for(let l of Oe){let c=s[l]??(ae(l)?s.scale:void 0);if(c!==void 0){if(i.has(l))throw new Error(`Animation delta/exact conflict: ${l}`);if(me(c,l,o==="exact"&&ae(l)),d&&ae(l)&&c<0)throw new Error("Looping scale deltas cannot be negative");n[l]=c,i.add(l)}}if(!Object.keys(n).length)throw new Error("Animation track needs a transform");e[o]={...n,msec:r,...d?{loop:!0}:{}}}if(!Object.keys(e).length)throw new Error("Animation needs delta or exact");return e}function Ne(t){if(at(t,"playback state"),t.version!==1||typeof t.paused!="boolean")throw new Error("Invalid animation playback state version or paused flag");let e=me(t.elapsed_ms,"elapsed time");if(e<0)throw new Error("Invalid animation elapsed time");at(t.origin,"origin");for(let i of Oe)me(t.origin[i],i,ae(i));if(!Array.isArray(t.center)||t.center.length!==3)throw new Error("Invalid animation pivot");return{version:1,elapsed_ms:e,paused:t.paused,origin:ki(t.origin),center:t.center.map(i=>me(i,"pivot"))}}function De(t,e,i){let o=Fe(t),s=i==null?null:Ne(i),n=s?.origin??ki(e),r=Object.entries(o).map(([l,c])=>{let m=Object.entries(c).filter(([g])=>Oe.includes(g));if(l==="delta")for(let[g,x]of m)me(n[g]+x,`${g} endpoint`,ae(g));return{mode:l,values:m,duration:c.msec,loop:!!c.loop,done:!1}}),d={animation:o,origin:n,tracks:r,elapsed_ms:s?.elapsed_ms??0,paused:s?.paused??!1,last_time:null};return lt({...d,tracks:r.map(l=>({...l}))},{}),d}function lt(t,e){let i=[];for(let s of t.tracks){if(s.done)continue;let n=t.elapsed_ms/s.duration,r=s.loop?n:Math.min(n,1);for(let[d,l]of s.values){let c=t.origin[d],m=s.mode==="delta"?c+l*r:c*(1-r)+l*r;i.push([d,me(m,`${d} result`,ae(d))])}}let o=!1;for(let[s,n]of i)o||=ae(s)&&e[s]!==n,e[s]=n;for(let s of t.tracks)!s.loop&&t.elapsed_ms>=s.duration&&(s.done=!0);return o&&(e._stats=null),t.tracks.some(s=>!s.done)}function Ai(t,e,i){return t.paused?!0:(t.last_time!==null&&(t.elapsed_ms=me(t.elapsed_ms+Math.max(0,i-t.last_time),"elapsed time")),t.last_time=i,lt(t,e))}function Si(t,e=!1){return{status:t.paused?"paused":"running",elapsed_ms:t.elapsed_ms,suspended:e,tracks:t.tracks.filter(i=>!i.done).map(i=>({mode:i.mode,msec:i.duration,loop:i.loop,progress:i.loop?t.elapsed_ms%i.duration/i.duration:Math.min(1,t.elapsed_ms/i.duration)}))}}function zi(t){let e=t._animation;if(!e)return null;let i=t.unitScale??1,o=structuredClone(e.animation),s={...e.origin};for(let r of Oe.filter(ae)){s[r]*=i;for(let d of Object.values(o))d[r]!==void 0&&(d[r]*=i)}let n=Ne({version:1,origin:s,elapsed_ms:e.elapsed_ms,paused:e.paused,center:[...t.center]});return{animation:Fe(o),animation_state:n}}var be=t=>String(t||"").match(/\.(vsb|vsj)(?:[?#]|$)/i)?.[1].toLowerCase()||"";function Ri(t,e){let i;try{i=decodeURIComponent(t.split(/[?#]/)[0]).replaceAll("\\","/")}catch{throw new Error("Invalid scene filename encoding")}let o=[...new Set(e)],s=o.filter(r=>(r.webkitRelativePath||r.name)===i);if(s.length===1)return s[0];let n=o.filter(r=>r.name===i.split("/").pop());if(n.length>1||s.length>1)throw new Error("More than one supplied file matches "+i);return n[0]||null}function Tt(t,e){let i;try{i=e?new URL(t,e):new URL(t)}catch{return null}if(!["http:","https:"].includes(i.protocol)||i.username||i.password)throw new Error("Scene references must be HTTP/HTTPS URLs without login credentials");return i.hash="",i.href}var Ot=["save-button","save-dialog","save-form","save-fields","save-name","save-format","save-scope","save-colors","save-colors-row","save-format-note","save-cad-note","save-progress","save-status","save-error","save-submit","save-cancel","save-close"];function Ii({$:t,listen:e,window:i,getState:o,getViewer:s,onOpen:n,announce:r}){let d=t("save-dialog"),l=null,c=!1,m="",g=()=>o(),x=()=>!c&&!g().busy&&g().models.length>0,f=()=>["vsb","vsj"].includes(t("save-format").value),p=()=>{t("save-error").hidden=!0,t("save-error").textContent=""};function u(){let k=t("save-format").value;(!t("save-name").value||t("save-name").value===m)&&y(),t("save-mesh-note")&&(t("save-mesh-note").hidden=f()),t("save-scene-note")&&(t("save-scene-note").hidden=!f()),t("save-colors-row").hidden=!["obj","3mf"].includes(k),t("save-cad-note").hidden=!["step","iges"].includes(k),t("save-format-note").textContent=t("save-format").selectedOptions[0]?.dataset.note||"",t("save-submit").textContent=k==="obj"&&t("save-colors").checked?"Download ZIP":"Download file",_()}function y(){let{models:k,selectedId:b}=g();t("save-name").value=f()?"viewstl-scene":(k.find(S=>S.id===b)?.name||"model").replace(/\.[^.]+$/,""),m=t("save-name").value}function _(){t("save-button").disabled=!x()||!!l,t("save-fields").disabled=!!l,t("save-submit").disabled=!x()||!!l,t("save-progress").hidden=!l,t("save-cancel").textContent=l?"Cancel saving":"Cancel";let{models:k,selectedId:b}=g(),S=f()?`Entire scene \xB7 ${k.length} ${k.length===1?"model":"models"}`:"Selected model: "+(k.find(P=>P.id===b)?.name||"model");t("save-scope").textContent!==S&&(t("save-scope").textContent=S)}function E(){if(l){let k=l;l=null,k.abort(),r("Saving cancelled.")}t("save-status").textContent="",_()}function z(){E(),d.open&&d.close()}return e(t("save-button"),"click",()=>{!x()||l||(n(),p(),t("save-status").textContent="",y(),u(),_(),d.showModal())}),e(t("save-format"),"change",()=>{p(),u()}),e(t("save-colors"),"change",u),e(t("save-cancel"),"click",z),e(t("save-close"),"click",z),e(d,"cancel",k=>{k.preventDefault(),z()}),e(d,"close",()=>{d.open||E()}),e(t("save-form"),"submit",async k=>{if(k.preventDefault(),l||!x())return;p();let{selectedId:b}=g(),S=new i.AbortController;l=S,t("save-progress").removeAttribute("value"),t("save-status").textContent="Preparing your file\u2026",_();try{let P={format:t("save-format").value,filename:t("save-name").value.trim()||"model",model_ids:[b],units:"mm",apply_transforms:!0,colors:t("save-colors").checked,signal:S.signal,on_progress({loaded:O,total:B,phase:R}){if(l!==S||S.signal.aborted)return;let T=B?Math.min(100,Math.floor(100*O/B)):0;t("save-progress").value=T,t("save-status").textContent=R==="packaging"?"Packing your scene\u2026":T===100?"Finishing your file\u2026":`Saving\u2026 ${T}%`}},v=f()?await s().download_scene({format:P.format,filename:P.filename,signal:P.signal,on_progress:P.on_progress,force_basename:!0}):await s().download_models(P);if(l!==S||c)return;l=null,z(),r(v.filename+" ready to download.")}catch(P){if(l!==S||c)return;l=null,t("save-status").textContent="",P?.name!=="AbortError"&&(t("save-error").textContent=P?.message||"This file could not be saved. Try another format.",t("save-error").hidden=!1)}finally{!c&&(!l||l===S)&&_()}}),u(),_(),{sync:_,close:z,dispose(){z(),c=!0}}}var Ft=["print-button","print-panel","print-close","print-form","print-fields","print-scope","print-level","print-units","print-fdm","print-wall","print-angle","print-bed-x","print-bed-y","print-bed-z","print-run","print-cancel","print-status","print-results","print-clear","print-target"];function Ci({$:t,listen:e,window:i,getState:o,getViewer:s,onOpen:n,announce:r}){let d=null,l=null,c=null,m=!1,g="",x="",f=!1,p=t("print-panel"),u=p.ownerDocument,y=new Set,_={issues_found:"Issues found",incomplete:"Check incomplete",no_issues_found:"No issues found in completed checks"},E=(R,T)=>{let C=u.createElement(R);return C.textContent=T,C},z=()=>t("print-scope").value==="all"?o().models.map(R=>R.id):[o().selectedId],k=()=>JSON.stringify(z()),b=()=>{s()?.clear_printability(),t("print-clear").hidden=!0};function S(){if(m)return;let R=o();if(t("print-button").disabled=R.busy||!R.models.length,p.hidden)return;for(let C of y)(!R.models.some(L=>L.id===C)||s()?.get_model_animation_state(C)?.status!=="paused")&&y.delete(C);t("print-resume")&&(t("print-resume").hidden=!y.size,t("print-resume").disabled=!!d||R.busy),t("print-animation-note")&&(t("print-animation-note").hidden=!y.size);let T=f;f=R.busy||!R.models.length,t("print-run").disabled=!!d||f,x!==k()&&(t("print-units").checked=!1,x=k()),t("print-target").textContent=t("print-scope").value==="all"?`All ${R.models.length} models, checked separately`:R.models.find(C=>C.id===R.selectedId)?.name||"No model selected",d&&(f||g!==k())&&v(),l&&(!s()?.is_printability_current(l)||g!==k())&&(l=null,t("print-units").checked=!1,b(),t("print-results").replaceChildren(),t("print-status").textContent="Model or selection changed. Run the check again."),f?(R.models.length||(l=null,b(),t("print-results").replaceChildren()),t("print-status").textContent=R.busy?"Opening models. Checks will be available when loading finishes.":"Open a model to run a print check."):T&&!d&&(t("print-status").textContent=l?_[l.summary]:"Ready to check. Choose Run check to analyze the current model selection.")}function P(R){let T=[];for(let C of R.models){let L=u.createElement("section");L.append(E("h3",o().models.find(A=>A.id===C.model_id)?.name||C.name)),L.append(E("p",`${C.dimensions_mm?.map(A=>Number(A.toPrecision(5))).join(" \xD7 ")||"Unknown size"} mm \xB7 ${(C.elapsed_ms/1e3).toFixed(2)} s`)),C.volume_valid&&L.append(E("p",`Enclosed volume at current mm scale: ${Number(C.volume_mm3.toPrecision(6))} mm\xB3`));for(let A of C.checks){let I=u.createElement("details");I.dataset.check=A.code,I.dataset.status=A.status;let N={pass:"Passed",issue:A.severity==="info"?"Information":"Review",inconclusive:"Inconclusive",not_run:"Not run"}[A.status];if(I.append(E("summary",`${N} \xB7 ${A.label}${A.count?` (${A.count})`:""}${A.estimated?" \u2014 estimate":""}`)),I.append(E("p",A.message)),A.area_mm2!=null&&I.append(E("p",`Area: ${Number(A.area_mm2.toPrecision(5))} mm\xB2`)),A.minimum_sampled_mm!=null&&I.append(E("p",`Smallest sampled distance: ${Number(A.minimum_sampled_mm.toPrecision(5))} mm`)),A.triangle_ids.length||A.edge_vertices.length){let Z=E("button","Show locations");Z.type="button",Z.dataset.issue=A.code,Z.dataset.model=C.model_id,I.append(Z),I.append(E("p","Highlights show a bounded sample, including edges hidden behind surfaces."))}L.append(I)}T.push(L)}t("print-results").replaceChildren(...T)}function v(){d?.abort()}function O(R=!0){v(),p.hidden=!0,t("workspace")?.classList.remove("print-check-open"),b(),i.clearInterval(c),c=null,t("print-button").setAttribute("aria-expanded","false"),R&&!m&&(t("print-button").offsetParent&&!t("print-button").disabled?t("print-button"):t("info-button").offsetParent?t("info-button"):t("sample-button"))?.focus()}function B(){v(),l=null,b(),t("print-results").replaceChildren(),t("print-status").textContent="Settings changed. Run the check again.",S()}return e(t("print-button"),"click",()=>{if(!p.hidden){n(),S(),p.scrollTop=0,t("print-run").focus();return}n(),p.hidden=!1,t("workspace")?.classList.add("print-check-open"),t("print-button").setAttribute("aria-expanded","true"),S(),c=i.setInterval(S,400),t("print-run").focus()}),e(t("print-close"),"click",()=>O()),e(p,"keydown",R=>{R.key==="Escape"&&(R.stopPropagation(),R.preventDefault(),O())}),e(t("print-fields"),"change",B),e(t("print-clear"),"click",b),e(t("print-cancel"),"click",v),t("print-resume")&&e(t("print-resume"),"click",()=>{if(!(d||o().busy)){for(let R of y)o().models.some(T=>T.id===R)&&s().get_model_animation_state(R)?.status==="paused"&&s().resume_model_animation(R);y.clear(),l=null,b(),t("print-results").replaceChildren(),t("print-status").textContent="Animation resumed. Pause and run the check again for the new pose.",S()}}),e(t("print-results"),"click",R=>{let T=R.target.closest("button[data-issue]");if(!(!T||!l))try{s().show_printability(l,{issue:T.dataset.issue,model_id:Number(T.dataset.model)}),t("print-clear").hidden=!1}catch(C){t("print-status").textContent=C.message}}),e(t("print-form"),"submit",async R=>{if(R.preventDefault(),d||o().busy||!o().models.length)return;let T=new i.AbortController;d=T,l=null,b(),t("print-results").replaceChildren(),t("print-status").textContent="Checking on your device\u2026",t("print-fields").disabled=!0,t("print-run").disabled=!0,t("print-cancel").hidden=!1;try{let C=null;if(t("print-fdm").checked){C={technology:"fdm"},t("print-wall").value&&(C.min_wall_mm=Number(t("print-wall").value)),t("print-angle").value&&(C.overhang_degrees=Number(t("print-angle").value));let A=["x","y","z"].map(I=>t("print-bed-"+I).value);if(A.some(Boolean)){if(!A.every(Boolean))throw new Error("Enter all three build-volume dimensions, or leave all three empty.");C.build_volume_mm=A.map(Number)}}g=k();for(let A of z())s().get_model_animation_state(A)?.status==="running"&&(s().pause_model_animation(A),y.add(A));S();let L=await s().check_printability({model_ids:z(),level:t("print-level").value,units_confirmed:t("print-units").checked,profile:C,signal:T.signal,on_progress:A=>{d===T&&(t("print-status").textContent=`${A.phase} \xB7 model ${(A.model_index??0)+1} of ${A.total_models}`)}});if(m||T.signal.aborted)return;l=L,P(l),t("print-status").textContent=_[l.summary],r(_[l.summary])}catch(C){m||(t("print-status").textContent=C.name==="AbortError"?"Check cancelled. Your model is unchanged.":C.message)}finally{d===T&&(d=null),m||(t("print-fields").disabled=!1,t("print-run").disabled=!1,t("print-cancel").hidden=!0,S())}}),S(),{sync:S,close:O,dispose(){m=!0,O(!1),l=null,y.clear()}}}var Nt=["animation-details","animation-fields","animation-axis","animation-seconds","animation-direction","animation-play","animation-pause","animation-stop","animation-status","animation-error"],je=["x","y","z"];function he(t,e){if(!t||!e?.geometry)return;t.bounds=Object.fromEntries(["min","max"].map(o=>[o,e.geometry.bounds[o].map((s,n)=>(s-e.center[n])*e.scaleVector[n])])),t.rotation=Object.fromEntries(je.map(o=>[o,e["rotation"+o]*180/Math.PI%360])),t.position={x:0,y:0,z:0};let i=Le(t);for(let o of je)t.position[o]=e[o]-i[o]}function Li(t,e){he(t,e);let i=e.geometry.bounds.min.map((r,d)=>(r+e.geometry.bounds.max[d])/2),o=i.map((r,d)=>(r-e.center[d])*e.scaleVector[d]),s=Ce(t.rotation),n=t.originalPosition||{x:0,y:0,z:0};t.originalPosition=Object.fromEntries(je.map((r,d)=>[r,n[r]+o[d]]));for(let r=0;r<3;r++)e[je[r]]+=o.reduce((d,l,c)=>d+s[r*3+c]*l,0);e.center=i,t.animationPivot=!0,he(t,e)}function lo(t,e,i){if(!je.includes(t)||!Number.isFinite(e)||e<.1||e>3600||!["1","-1"].includes(i))throw new Error("Choose an axis and a duration from 0.1 to 3,600 seconds.");return{delta:{["rotation"+t]:2*Math.PI*Number(i),msec:e*1e3,loop:!0}}}function Pi({$:t,listen:e,window:i,getState:o,getViewer:s,beforePlay:n,refreshPose:r,announce:d}){let l=new Map,c=null,m=null,g=!1,x=()=>{let u=o();return{...u,playback:u.selectedId==null?null:s()?.get_model_animation_state(u.selectedId)}};function f(){if(g)return;let u=x(),y=u.models.some(b=>b.id===u.selectedId);for(let b of l.keys())u.models.some(S=>S.id===b)||l.delete(b);if(t("animation-details").hidden=!y,t("animation-fields").disabled=u.busy||!y,c!==u.selectedId){c=u.selectedId;let b=l.get(c);if(!b){b={axis:"z",seconds:5,direction:"1"};let S=s()?._models.get(c)?.animation,P=Object.entries(S?.delta||{}).filter(([v,O])=>v.startsWith("rotation")&&O!==0);S?.delta?.loop&&!S.exact&&P.length===1&&(b={axis:P[0][0].slice(-1),seconds:S.delta.msec*2*Math.PI/Math.abs(P[0][1])/1e3,direction:P[0][1]<0?"-1":"1"}),l.set(c,b)}t("animation-axis").value=b.axis,t("animation-seconds").value=b.seconds,t("animation-direction").value=b.direction,t("animation-error").hidden=!0}let _=u.playback?.status||"idle",E=["running","paused"].includes(_);t("animation-play").textContent=E?"Restart spin":"Play",t("animation-pause").textContent=_==="paused"?"Resume":"Pause",t("animation-pause").disabled=!E,t("animation-stop").disabled=!E;let k=`${{idle:"Ready",running:"Playing",paused:"Paused",stopped:"Stopped",completed:"Finished"}[_]}. ${E?"Stop keeps the current pose.":"Spin the selected model around its center."}`;t("animation-status").textContent!==k&&(t("animation-status").textContent=k),_==="running"&&!m&&(m=i.setInterval(()=>{!g&&!i.document.hidden&&r()},250)),_!=="running"&&m&&(i.clearInterval(m),m=null)}function p(u=!1){let y=x();if(!(y.busy||y.selectedId==null))try{let _={axis:t("animation-axis").value,seconds:t("animation-seconds").valueAsNumber,direction:t("animation-direction").value},E=lo(_.axis,_.seconds,_.direction);n(y.selectedId),s().animate_model(y.selectedId,E),u&&y.playback?.status==="paused"&&s().pause_model_animation(y.selectedId),l.set(y.selectedId,_),t("animation-error").hidden=!0,d(u&&y.playback?.status==="paused"?"Spin updated. Still paused.":"Animation playing."),f()}catch(_){t("animation-error").textContent=_.message,t("animation-error").hidden=!1}}e(t("animation-play"),"click",()=>p()),e(t("animation-pause"),"click",()=>{let u=x();u.busy||u.selectedId==null||(s()[u.playback?.status==="paused"?"resume_model_animation":"pause_model_animation"](u.selectedId),f())}),e(t("animation-stop"),"click",()=>{let u=x();!u.busy&&u.selectedId!=null&&s().animate_model(u.selectedId,null),f()});for(let u of["animation-axis","animation-seconds","animation-direction"])e(t(u),"change",()=>{["running","paused"].includes(x().playback?.status)?p(!0):l.set(c,{axis:t("animation-axis").value,seconds:t("animation-seconds").valueAsNumber,direction:t("animation-direction").value})});return f(),{sync:f,dispose(){g=!0,i.clearInterval(m),l.clear()}}}var co=`#version 300 es
2
+ precision highp float;
3
+ out vec2 screen;
4
+ void main(){vec2 p=vec2((gl_VertexID<<1)&2,gl_VertexID&2);screen=p*2.0-1.0;gl_Position=vec4(screen,0,1);}`,mo=`#version 300 es
5
+ precision highp float;
6
+ in vec2 screen;
7
+ uniform mat4 inverseVP;uniform mat4 vp;uniform vec3 eye;uniform float height;uniform float spacing;uniform vec3 color;
8
+ out vec4 result;
9
+ float line(vec2 p,float stepSize){vec2 v=p/stepSize;vec2 d=abs(fract(v-0.5)-0.5)/max(fwidth(v),vec2(0.00001));return 1.0-min(min(d.x,d.y),1.0);}
10
+ void main(){
11
+ vec4 a=inverseVP*vec4(screen,-1,1),b=inverseVP*vec4(screen,1,1);vec3 nearPoint=a.xyz/a.w,farPoint=b.xyz/b.w;
12
+ vec3 ray=farPoint-nearPoint;if(abs(ray.z)<0.0000001)discard;
13
+ float t=(height-nearPoint.z)/ray.z;if(t<0.0)discard;
14
+ vec3 p=nearPoint+t*ray;vec4 clip=vp*vec4(p,1);if(clip.w<=0.0)discard;
15
+ float pixel=max(length(dFdx(p.xy)),length(dFdy(p.xy)));
16
+ float level=log(max(pixel*50.0/spacing,0.000001))/log(10.0);
17
+ float stepSize=spacing*pow(10.0,floor(level));float mixLevel=smoothstep(0.0,1.0,fract(level));
18
+ float alpha=max(line(p.xy,stepSize)*(1.0-mixLevel)*0.16,line(p.xy,stepSize*10.0)*0.30);
19
+ alpha*=smoothstep(0.015,0.12,abs(ray.z)/length(ray));
20
+ if(alpha<0.003)discard;
21
+ gl_FragDepth=clamp(clip.z/clip.w*0.5+0.5+0.000001,0.0,0.999999);
22
+ result=vec4(color,alpha);
23
+ }`,ct=class{constructor(e){this.gl=e;let i=[],o;try{for(let[s,n]of[[e.VERTEX_SHADER,co],[e.FRAGMENT_SHADER,mo]]){let r=e.createShader(s);if(i.push(r),e.shaderSource(r,n),e.compileShader(r),!e.getShaderParameter(r,e.COMPILE_STATUS))throw Error("Grid shader: "+e.getShaderInfoLog(r))}if(o=e.createProgram(),i.forEach(s=>e.attachShader(o,s)),e.linkProgram(o),!e.getProgramParameter(o,e.LINK_STATUS))throw Error("Grid program: "+e.getProgramInfoLog(o));if(this.program=o,this.vao=e.createVertexArray(),!this.vao)throw Error("Graphics memory unavailable for grid");this.uniforms=Object.fromEntries(["inverseVP","vp","eye","height","spacing","color"].map(s=>[s,e.getUniformLocation(o,s)]))}catch(s){throw o&&e.deleteProgram(o),s}finally{i.forEach(s=>e.deleteShader(s))}}draw(e,i,o,s){let n=this.gl,r=this.uniforms;n.useProgram(this.program),n.bindVertexArray(this.vao),n.uniformMatrix4fv(r.inverseVP,!1,new Float32Array(ye(e))),n.uniformMatrix4fv(r.vp,!1,new Float32Array(e)),n.uniform3fv(r.eye,i.position),n.uniform1f(r.height,o.height),n.uniform1f(r.spacing,o.spacing);let d=s==="transparent"?[1,1,1]:X(s),l=d[0]*.2126+d[1]*.7152+d[2]*.0722<.45;n.uniform3fv(r.color,l?[.68,.77,.81]:[.37,.46,.5]),n.enable(n.BLEND),n.blendFuncSeparate(n.SRC_ALPHA,n.ONE_MINUS_SRC_ALPHA,n.ONE,n.ONE_MINUS_SRC_ALPHA),n.depthMask(!1),n.drawArrays(n.TRIANGLES,0,3),n.depthMask(!0),n.disable(n.BLEND)}dispose(){this.gl.deleteProgram(this.program),this.gl.deleteVertexArray(this.vao)}};function Ti(t){let e=Math.max(Math.max(...t.max.map((s,n)=>s-t.min[n]))*.015,...[...t.min,...t.max].map(s=>Math.abs(s)*2e-7),1e-7),i=s=>[0,1,2].map(n=>s&1<<n?t.max[n]+e:t.min[n]-e),o=[];for(let s=0;s<8;s++)for(let n=0;n<3;n++)s&1<<n||o.push(...i(s),...i(s|1<<n));return new Float32Array(o)}var ho=`#version 300 es
24
+ precision highp float;
25
+ layout(location=0) in vec3 position;
26
+ layout(location=1) in vec3 normal;
27
+ layout(location=2) in vec3 color;
28
+ uniform mat4 mvp; uniform mat4 modelMatrix; uniform mat3 normalMatrix;
29
+ out vec3 vNormal; out vec3 vColor; out vec3 vPosition; out vec3 vWorld; out vec3 vBary;
30
+ void main(){vNormal=normalMatrix*normal;vColor=color;vPosition=position;
31
+ vWorld=(modelMatrix*vec4(position,1.0)).xyz;
32
+ int corner=gl_VertexID%3;vBary=corner==0?vec3(1,0,0):(corner==1?vec3(0,1,0):vec3(0,0,1));
33
+ gl_Position=mvp*vec4(position,1.0);}`,uo=`#version 300 es
34
+ precision highp float;
35
+ in vec3 vNormal;in vec3 vColor;in vec3 vPosition;in vec3 vWorld;in vec3 vBary;
36
+ uniform vec3 tint;uniform vec3 light;uniform float opacity;uniform bool unlit;uniform bool useColors;
37
+ uniform vec3 eye;uniform vec4 section;uniform int appearance;
38
+ out vec4 result;
39
+ void main(){float cut=dot(vec4(vPosition,1.0),section);if(cut>0.0)discard;
40
+ vec3 n=normalize(vNormal);if(!gl_FrontFacing)n=-n;
41
+ float l=unlit?1.0:0.32+0.55*max(dot(n,light),0.0)+0.13*max(dot(n,normalize(vec3(-0.7,-0.3,0.8))),0.0);
42
+ vec3 base=tint*(useColors?vColor:vec3(1));vec3 color=base*l;float alpha=opacity;
43
+ vec3 view=normalize(eye-vWorld);
44
+ if(!unlit){
45
+ if(appearance==1){vec3 w=max(fwidth(vBary),vec3(0.000001));vec3 edge=smoothstep(vec3(0.0),w*1.15,vBary);color=mix(vec3(0.12,0.18,0.21),color,min(min(edge.x,edge.y),edge.z));}
46
+ if(appearance==2){float rim=pow(1.0-abs(dot(n,view)),2.0);color=mix(base*0.7,base, rim);alpha*=0.12+0.28*rim;}
47
+ if(appearance==3){vec3 key=normalize(light+vec3(-0.4,0.5,0.6));float shine=pow(max(dot(n,normalize(key+view)),0.0),36.0);float rim=pow(1.0-max(dot(n,view),0.0),3.0);color=base*(0.45+0.5*max(dot(n,key),0.0))+vec3(0.26)*shine+vec3(0.08)*rim;}
48
+ if(appearance==4)color=(gl_FrontFacing?vec3(0.12,0.55,0.72):vec3(0.9,0.24,0.32))*(0.55+0.45*l);
49
+ if(dot(section.xyz,section.xyz)>0.0 && abs(cut)<max(fwidth(cut)*1.2,0.0000001)){color=vec3(0.93,0.57,0.17);alpha=opacity;}
50
+ }
51
+ result=vec4(color,alpha);}`,dt=class{constructor(e,i,o,s){if(this.canvas=e,this.domElement=e,this.resources=new Map,this.frames=0,this.lost=!1,this.gl=e.getContext("webgl2",{alpha:!0,antialias:!0,premultipliedAlpha:!1,preserveDrawingBuffer:!1}),!this.gl)throw new Error("WebGL2 is unavailable. Enable browser graphics acceleration or try another browser/device.");this.lostHandler=n=>{n.preventDefault(),this.lost=!0,this.resources.clear(),this.gridResource=null,this.infiniteGrid=null,this.selectionResource=null,this.diagnosticResource=null,i()},this.restoredHandler=()=>{try{this.initialize(),this.lost=!1,o()}catch(n){s(n)}},e.addEventListener("webglcontextlost",this.lostHandler),e.addEventListener("webglcontextrestored",this.restoredHandler),this.initialize()}initialize(){let e=this.gl,i=[],o;try{for(let[s,n]of[[e.VERTEX_SHADER,ho],[e.FRAGMENT_SHADER,uo]]){let r=e.createShader(s);if(i.push(r),e.shaderSource(r,n),e.compileShader(r),!e.getShaderParameter(r,e.COMPILE_STATUS))throw new Error(`Shader compilation failed: ${e.getShaderInfoLog(r)}`)}o=e.createProgram();for(let s of i)e.attachShader(o,s);if(e.linkProgram(o),!e.getProgramParameter(o,e.LINK_STATUS))throw new Error(`Shader link failed: ${e.getProgramInfoLog(o)}`);this.program=o,this.uniforms=Object.fromEntries(["mvp","modelMatrix","normalMatrix","tint","light","eye","opacity","unlit","section","appearance","useColors"].map(s=>[s,e.getUniformLocation(o,s)]))}catch(s){throw o&&e.deleteProgram(o),s}finally{for(let s of i)e.deleteShader(s)}}upload(e,i,o){let s=this.gl,n=s.createVertexArray(),r=[];if(!n)throw new Error("Graphics memory is unavailable for this model.");s.bindVertexArray(n);try{for(let[d,l]of[[0,e],[1,i],[2,o]])if(l){let c=s.createBuffer();if(!c)throw new Error("Graphics memory is unavailable for this model.");r.push(c),s.bindBuffer(s.ARRAY_BUFFER,c),s.bufferData(s.ARRAY_BUFFER,l,s.STATIC_DRAW);let m=s.getError();if(m!==s.NO_ERROR)throw new Error(m===s.OUT_OF_MEMORY?"Not enough graphics memory for this model. Try closing other tabs or using another device.":"The browser could not upload this model to the graphics device.");s.enableVertexAttribArray(d),s.vertexAttribPointer(d,3,s.FLOAT,!1,0,0)}return s.bindVertexArray(null),{vao:n,buffers:r,count:e.length/3}}catch(d){s.bindVertexArray(null);for(let l of r)s.deleteBuffer(l);throw s.deleteVertexArray(n),d}}free(e){if(e){for(let i of e.buffers)this.gl.deleteBuffer(i);this.gl.deleteVertexArray(e.vao)}}remove(e){this.diagnostic?.model===e&&this.setDiagnostic(null);let i=this.resources.get(e);if(i)for(let o of Object.values(i))this.free(o);this.resources.delete(e)}clear(){this.setDiagnostic(null),this.setSelection(null);for(let e of this.resources.keys())this.remove(e);this.free(this.gridResource),this.gridResource=null,this.infiniteGrid?.dispose(),this.infiniteGrid=null}get liveBuffers(){let e=(this.gridResource?.buffers.length||0)+(this.selectionResource?.buffers.length||0)+(this.diagnosticResource?.buffers.length||0);for(let i of this.resources.values())for(let o of Object.values(i))e+=o.buffers.length;return e}setDiagnostic(e){this.free(this.diagnosticResource),this.diagnosticResource=null,this.diagnostic=e}drawDiagnostic(e){let i=this.gl,o=this.uniforms,s=this.diagnostic,n=J(s.model);this.diagnosticResource??=this.upload(s.positions),i.uniformMatrix4fv(o.mvp,!1,new Float32Array(Te(e,n))),i.uniformMatrix4fv(o.modelMatrix,!1,new Float32Array(n)),i.uniform4fv(o.section,[0,0,0,0]),i.uniform1i(o.appearance,0),i.uniform1i(o.useColors,0),i.uniform1i(o.unlit,1),i.uniform3fv(o.tint,[.85,.16,.06]),i.uniform1f(o.opacity,1),i.bindVertexArray(this.diagnosticResource.vao),i.disable(i.DEPTH_TEST),i.drawArrays(i.LINES,0,this.diagnosticResource.count),i.enable(i.DEPTH_TEST)}setSelection(e){this.selection!==e&&(this.free(this.selectionResource),this.selectionResource=null,this.selection=e)}drawSelection(e,i){let o=this.gl,s=this.uniforms,n=J(this.selection);this.selectionResource??=this.upload(Ti(this.selection.geometry.bounds)),o.uniformMatrix4fv(s.mvp,!1,new Float32Array(Te(e,n))),o.uniformMatrix4fv(s.modelMatrix,!1,new Float32Array(n)),o.uniformMatrix3fv(s.normalMatrix,!1,new Float32Array([1,0,0,0,1,0,0,0,1])),o.uniform4fv(s.section,[0,0,0,0]),o.uniform1i(s.appearance,0),o.uniform1i(s.useColors,0),o.uniform1i(s.unlit,1);let r=i==="transparent"?[1,1,1]:X(i);o.uniform3fv(s.tint,r[0]+r[1]+r[2]<1.5?[.42,.81,.96]:[.08,.43,.65]),o.bindVertexArray(this.selectionResource.vao),o.disable(o.DEPTH_TEST),o.uniform1f(s.opacity,.18),o.drawArrays(o.LINES,0,this.selectionResource.count),o.enable(o.DEPTH_TEST),o.uniform1f(s.opacity,.9),o.drawArrays(o.LINES,0,this.selectionResource.count)}resource(e,i){let o=this.resources.get(e);if(o||(o={},this.resources.set(e,o)),!o[i]){let s=e.geometry;o[i]=i==="edges"||i==="wire"?this.upload(s[i]):this.upload(s.positions,i==="smooth"?s.smoothNormals:s.normals,s.colors)}return o[i]}setGrid(e){this.grid=e,this.free(this.gridResource),this.gridResource=null}resize(e,i,o=1){let s=this.gl,n=s.getParameter(s.MAX_RENDERBUFFER_SIZE);this.canvas.width=Math.min(n,Math.max(1,Math.round(e*o))),this.canvas.height=Math.min(n,Math.max(1,Math.round(i*o)))}draw(e,i,o,s=!0){if(this.lost)return;let n=this.gl,r=this.uniforms;n.viewport(0,0,this.canvas.width,this.canvas.height),n.clearColor(...o==="transparent"?[0,0,0,0]:[...X(o),1]),n.depthMask(!0),n.clear(n.COLOR_BUFFER_BIT|n.DEPTH_BUFFER_BIT),n.enable(n.DEPTH_TEST),n.disable(n.CULL_FACE),n.useProgram(this.program),n.vertexAttrib3f(1,0,0,1),n.vertexAttrib3f(2,1,1,1),n.uniform3fv(r.light,q(H(i.position,i.target))),n.uniform3fv(r.eye,i.position);let d=nt(i,this.canvas.width/this.canvas.height),l=e.filter(f=>f.geometry&&f.opacity>0&&f.color!=="transparent"),c=f=>{let p=se(J(f),f.geometry.bounds.min.map((y,_)=>(y+f.geometry.bounds.max[_])/2)),u=H(p,i.position);return ie(u,u)},m=l.filter(f=>f.opacity>=1&&f.display!=="xray"),g=l.filter(f=>f.opacity<1||f.display==="xray").sort((f,p)=>c(p)-c(f)),x=f=>{let p=J(f),u=ye(p),y=f.geometry;n.uniformMatrix4fv(r.mvp,!1,new Float32Array(Te(d,p))),n.uniformMatrix4fv(r.modelMatrix,!1,new Float32Array(p)),n.uniform4fv(r.section,ot(y.bounds,f.section)),n.uniform1i(r.appearance,{mesh:1,xray:2,studio:3,orientation:4}[f.display]||0),n.uniformMatrix3fv(r.normalMatrix,!1,new Float32Array([u[0],u[4],u[8],u[1],u[5],u[9],u[2],u[6],u[10]])),n.uniform3fv(r.tint,f.color&&f.color!=="transparent"?X(f.color):y.colors?[1,1,1]:X("#909090")),n.uniform1f(r.opacity,f.opacity),n.uniform1i(r.useColors,+(!f.color&&!!y.colors));let _=f.display==="wireframe"&&y.wire,E=_?"wire":(f.smooth_shading||f.display==="smooth")&&y.smoothNormals?"smooth":"flat";n.uniform1i(r.unlit,+!!_);let z=f.view_edges||f.display==="edges";z&&(n.enable(n.POLYGON_OFFSET_FILL),n.polygonOffset(1,1));let k=this.resource(f,E);if(n.bindVertexArray(k.vao),f.display==="xray"?(n.enable(n.CULL_FACE),n.cullFace(n.FRONT),n.drawArrays(n.TRIANGLES,0,k.count),n.cullFace(n.BACK),n.drawArrays(n.TRIANGLES,0,k.count),n.disable(n.CULL_FACE)):n.drawArrays(_?n.LINES:n.TRIANGLES,0,k.count),n.disable(n.POLYGON_OFFSET_FILL),z&&y.edges){n.uniform1i(r.useColors,0),n.uniform3fv(r.tint,[.08,.09,.11]),n.uniform1i(r.unlit,1);let b=this.resource(f,"edges");n.bindVertexArray(b.vao),n.drawArrays(n.LINES,0,b.count)}};n.disable(n.BLEND);for(let f of m)x(f);if(this.grid?.infinite)this.infiniteGrid??=new ct(n),this.infiniteGrid.draw(d,i,this.grid,o),n.useProgram(this.program);else if(this.grid){if(!this.gridResource){let{size:f,divisions:p}=this.grid,u=[];for(let y=0;y<=p;y++){let _=-f/2+f*y/p;u.push(-f/2,_,0,f/2,_,0,_,-f/2,0,_,f/2,0)}this.gridResource=this.upload(new Float32Array(u))}n.uniformMatrix4fv(r.mvp,!1,new Float32Array(d)),n.uniform4fv(r.section,[0,0,0,0]),n.uniform1i(r.appearance,0),n.uniform1i(r.useColors,0),n.uniform3fv(r.tint,[.7,.73,.78]),n.uniform1f(r.opacity,1),n.uniform1i(r.unlit,1),n.bindVertexArray(this.gridResource.vao),n.drawArrays(n.LINES,0,this.gridResource.count)}n.enable(n.BLEND),n.blendFuncSeparate(n.SRC_ALPHA,n.ONE_MINUS_SRC_ALPHA,n.ONE,n.ONE_MINUS_SRC_ALPHA),n.depthMask(!1);for(let f of g)x(f);s&&this.selection&&l.includes(this.selection)&&this.drawSelection(d,o),s&&this.diagnostic&&e.includes(this.diagnostic.model)&&this.drawDiagnostic(d),n.depthMask(!0),n.disable(n.BLEND),n.bindVertexArray(null),this.frames++}dispose(){this.canvas.removeEventListener("webglcontextlost",this.lostHandler),this.canvas.removeEventListener("webglcontextrestored",this.restoredHandler),this.clear(),this.gl.deleteProgram(this.program),this.gl.getExtension("WEBGL_lose_context")?.loseContext()}};var mt=class{constructor(e,i,o,s,n=0){this.enabled=!0,this.noZoom=!1,this.type=n,this.camera=i,this.invalidate=o,this.pointers=new Map,this.cleanups=[],this.canvas=e;let r=(l,c,m)=>{e.addEventListener(l,c,m),this.cleanups.push(()=>e.removeEventListener(l,c,m))};r("pointerdown",l=>{this.enabled&&(l.preventDefault(),e.focus({preventScroll:!0}),e.setPointerCapture(l.pointerId),this.pointers.set(l.pointerId,{x:l.clientX,y:l.clientY,button:l.button}),s(l,l.pointerType==="touch"?20:l.button===2?3:1))}),r("pointermove",l=>{let c=this.pointers.get(l.pointerId);if(!c||!this.enabled)return;let m={x:l.clientX,y:l.clientY,button:c.button};if(this.pointers.size===2){let g=[...this.pointers.entries()].find(([p])=>p!==l.pointerId)[1],x=Math.hypot(c.x-g.x,c.y-g.y),f=Math.hypot(m.x-g.x,m.y-g.y);x>0&&f>0&&this.zoom(x/f),this.pan((m.x-c.x)/2,(m.y-c.y)/2)}else c.button===2||l.shiftKey||c.button===1?this.pan(m.x-c.x,m.y-c.y):this.orbit((m.x-c.x)/Math.max(1,e.clientHeight)*Math.PI,(m.y-c.y)/Math.max(1,e.clientHeight)*Math.PI);this.pointers.set(l.pointerId,m)});let d=l=>{this.pointers.delete(l.pointerId),e.hasPointerCapture(l.pointerId)&&e.releasePointerCapture(l.pointerId)};r("pointerup",d),r("pointercancel",d),r("lostpointercapture",l=>this.pointers.delete(l.pointerId)),r("wheel",l=>{if(!this.enabled||this.noZoom)return;l.preventDefault();let c=l.deltaY*(l.deltaMode===1?16:l.deltaMode===2?e.clientHeight:1);this.zoom(Math.exp(Math.max(-1,Math.min(1,c*.001))))},{passive:!1}),r("dblclick",l=>{this.enabled&&s(l,11)}),r("contextmenu",l=>l.preventDefault())}orbit(e,i){let o=this.camera,s=H(o.position,o.target),n=q(o.up);s=rt(s,n,-e);let r=q(ne(n,s)),d=rt(s,r,-i);if(this.type===1)s=d,o.up=q(rt(n,r,-i));else{let l=q(d),c=l.reduce((m,g,x)=>m+g*n[x],0);Math.abs(c)<.9995&&(s=d)}o.position=re(o.target,s),this.invalidate()}zoom(e){if(this.noZoom)return;let i=this.camera,o=H(i.position,i.target),s=Math.hypot(...o),n=Math.max(1e-6,Math.min(1e15,s*e));i.position=re(i.target,oe(o,n/(s||1))),this.invalidate()}pan(e,i){let o=this.camera,s=H(o.position,o.target),n=q(ne(o.up,s)),r=q(ne(s,n)),d=2*Math.hypot(...s)*Math.tan(Math.PI/8)/Math.max(1,this.canvas.clientHeight),l=re(oe(n,-e*d),oe(r,i*d));o.target=re(o.target,l),o.position=re(o.position,l),this.invalidate()}dispose(){for(let e of this.cleanups)e();this.pointers.clear()}};var ht=class{constructor(e,{count:i=2,timeout:o=12e4,factory:s}={}){this.url=e,this.count=Math.max(1,Math.min(4,i)),this.timeout=o,this.factory=s,this.queue=[],this.active=new Set,this.next=0,this.disposed=!1}run(e,i,o=[]){if(this.disposed)return Promise.reject(new Error("Viewer disposed"));let s=++this.next,n,r=new Promise((d,l)=>{n={id:s,task:e,progress:i,transfer:o,resolve:d,reject:l},this.queue.push(n)});return r.cancel=()=>this.cancel(n),this.pump(),r}cancel(e){e.done||(e.done=!0,this.queue=this.queue.filter(i=>i!==e),clearTimeout(e.timer),e.worker?.terminate(),this.active.delete(e),e.reject(new DOMException("Loading cancelled","AbortError")),this.pump())}pump(){for(;!this.disposed&&this.active.size<this.count&&this.queue.length;){let e=this.queue.shift();this.active.add(e);let i=(o,s)=>{e.done||(e.done=!0,clearTimeout(e.timer),e.worker?.terminate(),this.active.delete(e),o?e.reject(o):e.resolve(s),this.pump())};try{e.worker=this.factory?this.factory():new Worker(this.url,{type:"module",name:"ViewSTL parser"}),e.worker.onmessage=({data:o})=>{if(o.id===e.id){if(o.progress){e.progress?.(o.progress);return}i(o.error?new Error(o.error):null,o.result)}},e.worker.onerror=o=>{o.preventDefault(),i(new Error(o.message||"Worker failed; check worker URL and Content Security Policy"))},e.worker.onmessageerror=()=>i(new Error("Invalid worker response")),e.timer=setTimeout(()=>i(new Error("File processing timed out")),this.timeout),e.worker.postMessage({id:e.id,task:e.task},e.transfer)}catch(o){i(o)}}}clear(){for(let e of[...this.queue,...this.active])this.cancel(e)}dispose(){this.disposed=!0,this.clear()}};function Oi(t,e={}){let i=new AbortController,o=i.signal,s=this._generation,n=[],r=new Map,d=new Set,l=this._models.size>0,c,m=0,g,x=!1,f,p=()=>{i.abort(),c?.cancel?.();for(let[E,z]of r)this._models.get(E)===z&&this.remove_model(E)},u=()=>{if(o.aborted||s!==this._generation||this.killsign)throw new DOMException("Scene opening cancelled","AbortError")},y=E=>e.on_progress?.(E);e.signal?.addEventListener("abort",p,{once:!0}),e.signal?.aborted&&p(),o.addEventListener("abort",()=>c?.cancel?.(),{once:!0}),this._aborters.add(i);let _=(async()=>{await this.when_ready,u(),g=this.get_camera_state();let E=e.format||be(typeof t=="string"?t:t.name);if(!["vsb","vsj"].includes(E))throw new Error("Choose VSB or VSJ for a scene");let z,k;if(typeof t=="string"){if(z=Tt(t,document.baseURI),!this.options.url_import_endpoint)throw new Error("Configure url_import_endpoint for scene URL imports");if(k=new URL(this.options.url_import_endpoint,document.baseURI).href,new URL(k).origin!==location.origin)throw new Error("The URL relay must use the same origin as this page")}let b=this.limits.max_memory_bytes-this._occupiedMemory();Q(this.limits,this._occupiedMemory()+67108864,"scene opening");let S={...this.limits,max_memory_bytes:b,max_file_bytes:Math.min(this.limits.max_file_bytes,Math.floor((b-67108864)/4))};m=Math.max(0,b-67108864),this._sceneBytes=(this._sceneBytes||0)+m,c=this._pool.run({kind:"read-scene",source:t,remote:z,relay:k,format:E,name:`scene.${E}`,limits:S},L=>{try{y({...L,phase:"reading-scene"})}catch(A){f=A,c.cancel()}});let P;try{P=await c}catch(L){throw f||L}c=null,u();let{scene:v,files:O,base:B}=P;this._sceneBytes-=m,m=Object.values(O||{}).reduce((L,A)=>L+A.byteLength,0)+JSON.stringify(v).length*2,this._sceneBytes+=m;let R=e.files||[],T=[],C=v.models.map(L=>{let A={...L};if(O)A.local_file=new File([O[L.filename]],L.filename);else{let I=Ri(L.filename,R);if(I)A.local_file=I,d.add(I);else{let N=Tt(L.filename,z?B:void 0);N?A.remote_url=N:T.push(L.filename)}}if(A.remote_url&&!this.options.url_import_endpoint)throw new Error("Scene URL references require a configured URL relay");return A});if(T.length)throw new Error("Choose the VSJ together with these model files: "+T.slice(0,8).join(", ")+(T.length>8?"\u2026":""));if(C.length+this._models.size>this.limits.max_archive_entries)throw new Error("Scene model count limit exceeded");for(let L=0;L<C.length;L++){u(),y({phase:"opening-model",loaded:L,total:C.length}),u();let A=C[L],I=this.max_model_id+1;this.add_model({...A,id:I,animation:null,animation_state:null});let N=this._models.get(I);if(!N)throw new Error(this.error||"Invalid scene model");if(n.push(I),r.set(I,N),N._sceneImportPending=!0,await this._pending.get(I),u(),this._models.get(I)!==N||!N.geometry)throw new Error(this.error||"A scene model could not be opened");let Z=v.center_models??this.center_models?N.geometry.bounds.min.map((U,Y)=>(U+N.geometry.bounds.max[Y])/2):[0,0,0],ke=N.center.map((U,Y)=>U-Z[Y]),W=se(J({...N,x:0,y:0,z:0,center:[0,0,0],scaleVector:N.scaleVector}),ke);if(A.animation?N.center=[...A.animation_state?.center??Z]:["x","y","z"].forEach((U,Y)=>N[U]+=W[Y]),[...this._models.values()].reduce((U,Y)=>U+(Y.geometry?.triangles||0),0)>this.limits.max_triangles)throw new Error("Scene triangle limit exceeded")}y({phase:"complete",loaded:C.length,total:C.length}),u();for(let L=0;L<n.length;L++){let A=r.get(n[L]),I=C[L];I.animation&&this._startAnimation(A,I.animation,I.animation_state),A._sceneImportPending=!1}return v.bg_color!==void 0&&this.set_bg_color(v.bg_color),v.infinite_grid?this.set_infinite_grid(!0,v.infinite_grid.spacing,v.infinite_grid.height):this.set_grid(!!v.grid),v.auto_rotate!==void 0&&this.set_auto_rotate(v.auto_rotate),this._restoreCamera=v.camera_state||(l?g:null),x=!0,{model_ids:n,scene:v,used_files:[...d]}})().catch(E=>{throw p(),E}).finally(()=>{e.signal?.removeEventListener("abort",p),this._aborters.delete(i),this._sceneBytes=(this._sceneBytes||0)-m,this._sceneLoads.delete(_),s===this._generation&&!this.killsign&&(!x&&g&&(this._restoreCamera=g),this._finishSession())});return this._sessionActive=!0,this._sceneLoads.add(_),_}function Fi(t={}){let e=t.level??"basic";if(!["basic","detailed"].includes(e))throw new Error("Choose basic or detailed print checks");let i=(n,r,d=1e9)=>{if(!Number.isFinite(n)||n<=0||n>d)throw new Error(`Invalid ${r}`);return n},o=null;if(t.profile!=null){if(t.profile.technology!=="fdm")throw new Error("Only FDM profiles are supported");o={technology:"fdm"};for(let n of["min_wall_mm","line_width_mm"])t.profile[n]!=null&&(o[n]=i(t.profile[n],n));if(t.profile.overhang_degrees!=null&&(o.overhang_degrees=i(t.profile.overhang_degrees,"overhang angle",89.9)),t.profile.build_volume_mm!=null){if(!Array.isArray(t.profile.build_volume_mm)||t.profile.build_volume_mm.length!==3)throw new Error("Build volume requires X, Y and Z");o.build_volume_mm=t.profile.build_volume_mm.map(n=>i(n,"build volume"))}}let s=i(t.max_time_ms??3e4,"check time limit",12e4);return{level:e,profile:o,apply_transforms:t.apply_transforms!==!1,units_confirmed:t.units_confirmed===!0,max_time_ms:s}}var Ni=t=>64*1024**2+t*2400,_e=t=>JSON.stringify([t.x,t.y,t.z,t.rotationx,t.rotationy,t.rotationz,t.scaleVector,t.center,t.units,t.unitScale]);var Be=new WeakMap;function ut(t,e){let i=Be.get(e);return!!i&&i.viewer.deref()===t&&i.models.every(({model:o,geometry:s,signature:n})=>{let r=o.deref();return r&&t._models.get(r.id)===r&&r.geometry===s.deref()&&_e(r)===n})}async function Di(t={}){await this.when_ready;let e=Fi(t);if(this.killsign)throw new Error("Viewer disposed");let i=t.model_ids;if(i==="selected"&&(i=this.renderer?.selection?[this.renderer.selection.id]:[]),i??=[...this._models.keys()],!Array.isArray(i)||!i.length||new Set(i).size!==i.length||i.some(p=>!Number.isSafeInteger(p)))throw new Error("Choose one or more unique model IDs");if(i.length>this.limits.max_archive_entries)throw new Error("Print check model limit exceeded");let o=i.map(p=>{let u=this._model(p);if(!u.geometry||u._sceneImportPending)throw new Error("Wait for models to finish opening");return{model:u,geometry:u.geometry,signature:_e(u)}}),s=JSON.stringify([i,e]);if(t.signal?.aborted)throw new DOMException("Print check cancelled","AbortError");if(this._printCache?.key===s&&ut(this,this._printCache.report)){let p=structuredClone(this._printCache.report);return Be.set(p,Be.get(this._printCache.report)),p}if(this._printJobs?.size)throw new Error("A print check is already running");this._printJobs??=new Map;let n=new AbortController,r=()=>n.abort();t.signal?.addEventListener("abort",r,{once:!0}),this._aborters.add(n),this._printJobs.set(n,new Set(o.map(p=>p.model)));let d,l=0,c,m=()=>d?.cancel?.();n.signal.addEventListener("abort",m);let g=()=>{if(n.signal.aborted||this.killsign)throw new DOMException("Print check cancelled","AbortError");if(o.some(({model:p,geometry:u,signature:y})=>this._models.get(p.id)!==p||p.geometry!==u||_e(p)!==y))throw new Error("Model changed. Run the print check again.")},x=p=>{g(),t.on_progress?.(p)},f=[];try{this._printCache=null;let p=o.length*256*1024;Q(this.limits,this._occupiedMemory()+p,"print report"),l=p,this._printBytes=(this._printBytes||0)+l;for(let E=0;E<o.length;E++){g();let{model:z,geometry:k}=o[E],b=Ni(k.triangles),S=this._occupiedMemory();Q(this.limits,S+b,"print check"),this._printBytes+=b,l+=b;let P=new Float32Array(k.positions.length);for(let O=0;O<P.length;O+=262144)g(),P.set(k.positions.subarray(O,O+262144),O),O+262144<P.length&&await new Promise(B=>setTimeout(B,0));let v=e.apply_transforms?z:{...z,x:0,y:0,z:0,rotationx:0,rotationy:0,rotationz:0,center:[0,0,0],scaleVector:[z.unitScale,z.unitScale,z.unitScale]};x({phase:"Starting",model_id:z.id,model_index:E,total_models:o.length}),d=this._pool.run({kind:"print-check",model:{id:z.id,name:z.orig_filename||z.filename||`Model ${z.id}`,positions:P,matrix:J(v),source_units:k.units||null},options:e,limits:{...this.limits,max_memory_bytes:this.limits.max_memory_bytes-S}},O=>{try{x({...O,model_id:z.id,model_index:E,total_models:o.length})}catch(B){c=B,n.abort()}},[P.buffer]);try{f.push(await d)}catch(O){throw c||O}finally{d=null,this._printBytes-=b,l-=b}}g();let u={version:1,model_ids:i.slice(),settings:e,models:f,summary:f.some(E=>E.summary==="issues_found")?"issues_found":f.some(E=>E.summary==="incomplete")?"incomplete":"no_issues_found",scope:"Each model checked separately; no cross-model union or collision analysis.",estimated:!!e.profile},y={viewer:new WeakRef(this),models:o.map(({model:E,geometry:z,signature:k})=>({model:new WeakRef(E),geometry:new WeakRef(z),signature:k}))};Be.set(u,y);let _=structuredClone(u);return Be.set(_,y),JSON.stringify(_).length<2*1024*1024&&this._occupiedMemory()+4*1024**2<=this.limits.max_memory_bytes&&(this._printCache={key:s,report:_}),x({phase:"Complete",total_models:o.length}),u}finally{d?.cancel?.(),this._printBytes=(this._printBytes||0)-l,t.signal?.removeEventListener("abort",r),this._aborters.delete(n),this._printJobs.delete(n)}}function ji(t,{issue:e,model_id:i=t.model_ids[0]}={}){if(!ut(this,t))throw new Error("Print report is stale. Run the check again.");let o=t.models.find(l=>l.model_id===i),s=o?.checks.find(l=>l.code===e);if(!s)throw new Error("Unknown print check issue");let n=this._model(i),r=n.geometry.positions,d=[];if(s.edge_vertices?.length)d.push(...s.edge_vertices.slice(0,1536));else for(let l of s.triangle_ids.slice(0,128))if(!(!Number.isInteger(l)||l<0||l>=n.geometry.triangles))for(let c=0;c<3;c++)d.push(...r.subarray(l*9+c*3,l*9+c*3+3),...r.subarray(l*9+(c+1)%3*3,l*9+(c+1)%3*3+3));if(d.some(l=>!Number.isFinite(l)))throw new Error("Invalid print highlight");return this.renderer.setDiagnostic(d.length?{model:n,positions:new Float32Array(d),signature:_e(n)}:null),this._invalidate(),d.length/6}function Dt(t,e){return`${String(t||"scene").split(/[\\/]/).pop().split(/[?#]/)[0].replace(/[<>:"|*\x00-\x1f\x7f]/g,"_").replace(/\.(?:stl|obj|3mf|step|stp|iges|igs|zip|vsb|vsj)$/i,"").replace(/[. ]+$/g,"").slice(0,160)||"scene"}.${e}`}function Bi(t={}){let e={stp:"step",igs:"iges"},i=String(t.format||"").toLowerCase(),o=e[i]||i;if(!["stl","obj","3mf","step","iges"].includes(o))throw new Error("Export format must be stl, obj, 3mf, step or iges");let s=t.units??"mm";if(!["mm","inch"].includes(s))throw new Error("Export units must be mm or inch");for(let d of["apply_transforms","colors"])if(t[d]!==void 0&&typeof t[d]!="boolean")throw new Error(`${d} must be a boolean`);if(t.on_progress!==void 0&&typeof t.on_progress!="function")throw new Error("on_progress must be a function");let n=t.colors!==!1&&["obj","3mf"].includes(o),r=o==="obj"&&n?"zip":o;return{format:o,units:s,colors:n,apply_transforms:t.apply_transforms!==!1,filename:Dt(t.filename,r)}}function Ui(t,e){let i=t.reduce((s,n)=>s+n.geometry.triangles,0);return 67108864+i*{stl:192,obj:2400,"3mf":3e3,step:12e3,iges:4e3}[e.format]+t.length*4096}async function Vi(t,e,i=()=>{},o){let s,n=0;if(t instanceof ArrayBuffer){if(t.byteLength>e)throw new Error("File size limit exceeded");return i(t.byteLength,t.byteLength),t}if(t instanceof Blob){if(n=t.size,n>e)throw new Error("File size limit exceeded");if(o?.aborted)throw new DOMException("Loading cancelled","AbortError");i(0,n);let g=await t.arrayBuffer();if(o?.aborted)throw new DOMException("Loading cancelled","AbortError");return i(n,n),g}else{let g=new URL(String(t),globalThis.location?.href);if(!["http:","https:","blob:"].includes(g.protocol))throw new Error("Unsupported file URL protocol");let x=await fetch(g,{signal:o,credentials:"same-origin"});if(!x.ok)throw new Error(`HTTP ${x.status}: ${g.pathname}`);n=Number(x.headers.get("content-length"))||0,s=x.body}if(n>e)throw new Error("File size limit exceeded");if(!s)throw new Error("Empty response body");let r=s.getReader(),d=[],l=0;try{for(;;){if(o?.aborted)throw new DOMException("Loading cancelled","AbortError");let{done:g,value:x}=await r.read();if(g)break;if(l+=x.length,l>e)throw new Error("File size limit exceeded");d.push(x),i(l,Math.max(n,l))}}catch(g){throw await r.cancel().catch(()=>{}),g}finally{r.releaseLock()}let c=new Uint8Array(l),m=0;for(let g of d)c.set(g,m),m+=g.length;return i(l,l),c.buffer}function Gi(t,e){if(!t?.isMesh&&!t?.geometry)throw new Error("Expected a Three.js mesh");if(t.isSkinnedMesh||t.isInstancedMesh||t.children?.length||t.morphTargetInfluences?.some(Boolean))throw new Error("Only a static standalone Three.js mesh is supported");let i=t.material;if(Array.isArray(i)||i?.isShaderMaterial||i?.map)throw new Error("Multiple, textured or shader materials need an application adapter");let o=t.geometry,s,n=null;if(o?.attributes?.position){let r=o.attributes.position,d=o.index,l=d?.count??r.count;if(l%3||l/3>e.max_triangles)throw new Error("Invalid or oversized mesh");s=new Float32Array(l*3),o.attributes.color&&(n=new Float32Array(l*3));for(let c=0;c<l;c++){let m=d?d.getX(c):c;for(let[g,x]of["getX","getY","getZ"].entries())s[c*3+g]=r[x](m),n&&(n[c*3+g]=o.attributes.color[x](m))}}else if(o?.vertices&&o?.faces){if(o.faces.length>e.max_triangles)throw new Error("Triangle limit exceeded");s=new Float32Array(o.faces.length*9),i?.vertexColors&&(n=new Float32Array(s.length)),o.faces.forEach((r,d)=>{for(let[l,c]of[r.a,r.b,r.c].entries()){let m=o.vertices[c];if(!m)throw new Error("Invalid mesh index");s.set([m.x,m.y,m.z],d*9+l*3),n&&n.set([r.color.r,r.color.g,r.color.b],d*9+l*3)}})}else throw new Error("Unsupported Three.js geometry");return st(s,n,e)}function Hi(t,e){if(!e?.Mesh||!e?.BufferGeometry||!e?.BufferAttribute)throw new Error("get_model_mesh requires options.three or globalThis.THREE; use get_model_data for native geometry");let i=t.geometry,o=new e.BufferGeometry,s=i.positions.slice(),n=t.scaleVector,r=t.center;for(let m=0;m<s.length;m++)s[m]=(s[m]-r[m%3])*n[m%3];let d=(m,g)=>{let x=new e.BufferAttribute(g,3);o.setAttribute?o.setAttribute(m,x):o.addAttribute(m,x)};d("position",s),i.colors&&!t.color&&d("color",i.colors.slice()),o.computeVertexNormals();let l=new e.MeshLambertMaterial({color:t.color==="transparent"?"#909090":t.color||(i.colors?"#ffffff":"#909090"),vertexColors:i.colors&&!t.color?e.VertexColors??!0:!1,side:e.DoubleSide,transparent:t.opacity<1||t.color==="transparent",opacity:t.color==="transparent"?0:t.opacity,wireframe:t.display==="wireframe",flatShading:t.display!=="smooth"}),c=new e.Mesh(o,l);return c.position.set(t.x,t.y,t.z),c.rotation.set(t.rotationx,t.rotationy,t.rotationz),c}var jt=["x","y","z","rotationx","rotationy","rotationz","scalex","scaley","scalez"],fo=["id","filename","remote_url","remote_format","orig_filename","local_file","mesh","data","x","y","z","display","color","scale","scalex","scaley","scalez","rotationx","rotationy","rotationz","opacity","view_edges","smooth_shading","section","units","animation","animation_state"],K=(t,e)=>{if(typeof t!="number"||!Number.isFinite(t))throw new Error(`${e} must be a finite number`);return t},Me=(t,e)=>{if(K(t,e),t<=0)throw new Error(`${e} must be positive`);return t},Bt=(t,e)=>[K(t.x,`${e}.x`),K(t.y,`${e}.y`),K(t.z,`${e}.z`)],ft=t=>({x:t[0],y:t[1],z:t[2]}),pt=t=>String(t||"model.stl").split(/[\\/]/).pop().split(/[?#]/)[0];function Ue(t,e){let i=document.createElement("a"),o=URL.createObjectURL(t);i.href=o,i.download=pt(e),i.click(),setTimeout(()=>URL.revokeObjectURL(o),1e3)}function qi(t){let e={};for(let i of fo)Object.hasOwn(t,i)&&(e[i]=t[i]);return e}var ve=class t{static version="2.0.0-rc.5";static workerURL=null;constructor(e,i={}){if(!e?.appendChild)throw new TypeError("StlViewer requires a DOM container");this.parent_element=e,this.options={...i},this.error=null,this.ready=!1,this.killsign=!1,this.models=[],this.models_ref=Object.create(null),this.models_count=0,this.max_model_id=-1,this.load_status=Object.create(null),this.load_session=0,this._models=new Map,this._pending=new Map,this._jobs=new Map,this._derived=new Set,this._memoryReservations=new Map,this._exportBytes=0,this._loadTail=Promise.resolve(),this._sceneLoads=new Set,this._aborters=new Set,this._generation=0,this._sessionActive=!1,this._frame=0,this._lastTime=0,this.grid=null,this._gridAuto=!1,this.camera={position:[0,0,100],target:[0,0,0],up:[0,1,0],near:.01,far:1e4},this.when_ready=new Promise((o,s)=>{this._readyResolve=o,this._readyReject=s}),this.when_ready.catch(()=>{});for(let o of["ready_callback","all_loaded_callback","model_loaded_callback","load_error_callback","loading_progress_callback","on_model_drop","animation_state_callback","animation_complete_callback"])this[o]=i[o]??null;this.onmousedown_callback=i.on_model_mousedown??i.on_model_mouseclick??null,queueMicrotask(()=>this._initialize())}_initialize(){if(!this.killsign)try{let e=this.options;if(this.limits=xi(e),this.bg_color=e.bg_color??e.bgcolor??"transparent",this.bg_color!=="transparent"&&X(this.bg_color),e.worker_count!==void 0&&(!Number.isInteger(e.worker_count)||e.worker_count<1||e.worker_count>4))throw new Error("worker_count must be 1\u20134");if(e.worker_timeout!==void 0&&Me(e.worker_timeout,"worker_timeout"),e.pixel_ratio!==void 0&&Me(e.pixel_ratio,"pixel_ratio"),e.controls!==void 0&&e.controls!==0&&e.controls!==1)throw new Error("controls must be 0 or 1");this.center_models=e.center_models??!0,this.auto_resize=e.auto_resize??!0,this.auto_rotate=e.auto_rotate??!1,this.mouse_zoom=e.mouse_zoom??!0,this.send_no_model_click_event=e.send_no_model_click_event??!1,this.zoom=e.zoom??-1,this.canvas_width=e.width??e.canvas_width??"100%",this.canvas_height=e.height??e.canvas_height??"100%",this.canvas=document.createElement("canvas"),this.canvas.style.cssText="display:block;touch-action:none;outline-offset:3px;",this._setSizeStyle(),this.canvas.tabIndex=0,this.canvas.setAttribute("aria-label","3D model viewer. Drag to rotate, shift-drag to pan, scroll to zoom."),this.parent_element.appendChild(this.canvas),this.renderer=new dt(this.canvas,()=>{this._suspendAnimations(!0),this._message("Graphics interrupted. Waiting for the browser to restore rendering\u2026")},()=>{this._removeMessage(),this._suspendAnimations(this.canvas.ownerDocument.hidden),this._invalidate()},o=>this._error(o)),this.controls=new mt(this.canvas,this.camera,()=>this._invalidate(),(o,s)=>this._pick(o,s),e.controls??0),this.controls.noZoom=!this.mouse_zoom;let i=e.worker_url??t.workerURL;if(e.worker_url===void 0&&typeof e.load_three_files=="string"&&(i=new URL("viewstl.worker.js",new URL(e.load_three_files||"./",document.baseURI)).href),!i&&!e.worker_factory)throw new Error("Specify worker_url when using the CommonJS build");this._pool=new ht(i,{count:e.worker_count??2,timeout:e.worker_timeout??12e4,factory:e.worker_factory}),this._drop=o=>{o.preventDefault();let s=[...o.dataTransfer.files];for(let n of s)this._callback("on_model_drop",n.name),this.add_model({local_file:n})},this._drag=o=>o.preventDefault(),this.set_drag_and_drop(e.allow_drag_and_drop??!0),this.set_auto_resize(this.auto_resize),this.do_resize(),this.camera.position=[e.camerax??0,e.cameray??0,this.zoom>=0?Math.max(this.zoom,1e-6):e.cameraz??100],e.camera_state&&(this.set_camera_state(e.camera_state),this._restoreCamera=e.camera_state),e.grid&&this.set_grid(!0),this._key=o=>{if(!this.controls.enabled)return;let s={ArrowLeft:()=>this.controls.orbit(-.12,0),ArrowRight:()=>this.controls.orbit(.12,0),ArrowUp:()=>this.controls.orbit(0,-.12),ArrowDown:()=>this.controls.orbit(0,.12),"+":()=>this.controls.zoom(.9),"-":()=>this.controls.zoom(1.1),f:()=>this.set_auto_zoom()};s[o.key]&&(o.preventDefault(),s[o.key]())},this.canvas.addEventListener("keydown",this._key),this._visibility=()=>this._suspendAnimations(this.canvas.ownerDocument.hidden||this.renderer.lost),this.canvas.ownerDocument.addEventListener("visibilitychange",this._visibility),this._animationSuspended=this.canvas.ownerDocument.hidden,this.ready=!0,this._readyResolve(this),this._callback("ready_callback"),e.models&&this.add_models(e.models),this._invalidate()}catch(e){this.ready=!1,this._readyReject(e),this._error(e),this._message(e.message),this._pool?.dispose(),this.controls?.dispose(),this.renderer?.dispose(),this._resizeObserver?.disconnect(),this._visibility&&this.canvas.ownerDocument.removeEventListener("visibilitychange",this._visibility)}}_callback(e,...i){let o=this[e];if(typeof o=="function")try{o(...i)}catch(s){console.error(`ViewSTL ${e} callback failed`,s)}}_error(e){return this.error=e?.message??String(e),this._callback("load_error_callback",this.error),null}_message(e){this._removeMessage(),this._messageElement=document.createElement("p"),this._messageElement.setAttribute("role","status"),this._messageElement.textContent=e,this.parent_element.appendChild(this._messageElement)}_removeMessage(){this._messageElement?.remove(),this._messageElement=null}_setSizeStyle(){this.canvas.style.width=typeof this.canvas_width=="number"?`${this.canvas_width}px`:this.canvas_width,this.canvas.style.height=typeof this.canvas_height=="number"?`${this.canvas_height}px`:this.canvas_height}_guard(e){if(this.killsign)return null;try{return e()}catch(i){return this._error(i)}}_model(e){let i=this._models.get(Number(e));if(!i)throw new Error(`Model id not found: ${e}`);return i}_invalidate(){let e=this.renderer?.diagnostic;e&&_e(e.model)!==e.signature&&this.renderer.setDiagnostic(null),!(this.killsign||!this.renderer||this.renderer.lost||this._frame||this._animationSuspended)&&(this._frame=requestAnimationFrame(i=>{this._frame=0,this._render(i)}))}_render(e){let i=this._lastTime?Math.min((e-this._lastTime)/1e3,.1):0;this._lastTime=e;let o=!1;for(let n of this._models.values())n.geometry&&!n._sceneImportPending&&n._animation&&!n._animation.paused&&(this._animate(n,e),o||=!!n._animation&&!n._animation.paused);this.auto_rotate&&this.controls.enabled&&this.controls.orbit(i*.3,0);let s=this.renderer.diagnostic;s&&_e(s.model)!==s.signature&&this.renderer.setDiagnostic(null),this._clip();try{this.renderer.draw([...this._models.values()].filter(n=>!n._sceneImportPending),this.camera,this.bg_color)}catch(n){this.renderer.clear(),this._error(n);return}o||this.auto_rotate&&this.controls.enabled?this._invalidate():this._lastTime=0}_bounds(){let e=[1/0,1/0,1/0],i=[-1/0,-1/0,-1/0],o=0;for(let s of this._models.values())if(s.geometry&&!s._sceneImportPending){o++;let{bounds:n}=s.geometry,r=J(s);for(let d=0;d<8;d++){let l=se(r,[0,1,2].map(c=>n[d&1<<c?"max":"min"][c]));for(let c=0;c<3;c++)e[c]=Math.min(e[c],l[c]),i[c]=Math.max(i[c],l[c])}}return o?{min:e,max:i,center:e.map((s,n)=>(s+i[n])/2),radius:Math.max(Math.hypot(...H(i,e))/2,1e-6)}:null}_clip(){let e=this._bounds(),i=Math.hypot(...H(this.camera.position,e?.center??this.camera.target)),o=e?.radius??10;this.camera.near=Math.max(1e-6,Math.min(o*.001,Math.max(i-o,1e-6)*.1)),this.camera.far=Math.max(this.camera.near*100,i+o*4,100)}_progress(e,i){!this._models.has(e)||this.killsign||(this.load_status[e]=i,this._callback("loading_progress_callback",this.load_status,this.load_session))}add_models(e){return this._guard(()=>{if(!Array.isArray(e))throw new Error("models must be an array");for(let i of e)this.add_model(i)})}add_model(e){return this._guard(()=>{if(!e||typeof e!="object")throw new Error("Invalid model");let i=qi(e),o=i.filename??i.local_file?.name??"model.stl";if(/\.vsj(?:[?#]|$)/i.test(o))return this.load_vsj(i.local_file??o);if(/\.vsb(?:[?#]|$)/i.test(o))return this.load_vsb(i.local_file??o);let s=i.id===void 0||i.id===-1?this.max_model_id+1:i.id;if(!Number.isSafeInteger(s)||s<0||this._models.has(s))throw new Error(`Invalid or duplicate model id: ${s}`);let n={...i,id:s,filename:i.filename??i.local_file?.name??"",x:0,y:0,z:0,rotationx:0,rotationy:0,rotationz:0,scalex:1,scaley:1,scalez:1,opacity:1,units:"mm",display:"flat",view_edges:!1,color:null,unitScale:1,center:[0,0,0],geometry:null};for(let l of jt)n[l]=i[l]??(l.startsWith("scale")?i.scale??1:0);for(let l of jt)K(n[l],l),l.startsWith("scale")&&Me(n[l],l);if(n.opacity=i.opacity??i.mesh?.material?.opacity??1,K(n.opacity,"opacity"),n.opacity<0||n.opacity>1)throw new Error("Opacity must be between 0 and 1");if(n.display=i.display??"flat",Object.assign(n,tt(n.display,i.smooth_shading??n.display==="smooth")),n.section=it(i.section),n.units=i.units??"mm",!["mm","inch"].includes(n.units))throw new Error("Invalid units");if(n.color=i.color??(i.mesh?.material?.color?.getHexString?"#"+i.mesh.material.color.getHexString():null),n.color&&n.color!=="transparent"&&X(n.color),n.view_edges=!!i.view_edges,n.animation=i.animation==null?null:Fe(i.animation),n.animation_state=i.animation_state==null?null:Ne(i.animation_state),n.animation_state&&!n.animation)throw new Error("Playback state needs an animation");n.animation&&De(n.animation,n,n.animation_state),Object.defineProperty(n,"scaleVector",{get:()=>[n.scalex*n.unitScale,n.scaley*n.unitScale,n.scalez*n.unitScale]}),this.max_model_id=Math.max(s,this.max_model_id),this._models.set(s,n),this.models_ref[s]=this.models.length,this.models.push(n),this.models_count=this._models.size,this._sessionActive=!0,this.load_status[s]={loaded:0,total:0};let r=this._generation,d=this._loadModel(n,i,r).then(()=>{r!==this._generation||this._models.get(s)!==n||(this._pending.delete(s),this._finishSession(),this._callback("model_loaded_callback",s))}).catch(l=>{r===this._generation&&this._models.get(s)===n&&(this._pending.delete(s),this._deleteModel(n),l.name!=="AbortError"&&this._error(l),this._finishSession())}).finally(()=>{this._jobs.delete(n)});this._pending.set(s,d)})}_occupiedMemory(e){let i=(this._exportBytes||0)+(this._sceneBytes||0)+(this._printBytes||0)+(this._printCache?4194304:0);for(let o of this._models.values())if(o!==e){let s=this._memoryReservations?.get(o);i+=Math.max(bi(o.geometry),[...s?.values()||[]].reduce((n,r)=>n+r.bytes,0))}return i}_reserveMemory(e,i,o="derive"){this._memoryReservations??=new Map;let s=this._memoryReservations.get(e);s||this._memoryReservations.set(e,s=new Map);let n={};return s.set(n,{bytes:i,kind:o}),()=>{s.delete(n),!s.size&&this._memoryReservations.get(e)===s&&this._memoryReservations.delete(e)}}async _loadModel(e,i,o){let s=()=>{};if(this.options.max_memory_bytes!==void 0){let n=this._loadTail;this._loadTail=new Promise(r=>{s=r}),await n}try{await this._readModel(e,i,o)}finally{s()}}async _readModel(e,i,o){if(await this.when_ready,this.killsign||o!==this._generation||this._models.get(e.id)!==e)return;let s=Math.max(1,this.limits.max_memory_bytes-this._occupiedMemory(e)),n={...this.limits,max_memory_bytes:s};Q(n,67108864,"scene");let r=this.limits.max_memory_bytes<Number.MAX_SAFE_INTEGER?this._reserveMemory(e,s-67108864,"parse"):()=>{};try{let d=Ct(e);if(i.mesh)e.geometry=Gi(i.mesh,n);else if(i.data)e.geometry=st(new Float32Array(i.data.positions),i.data.colors?new Float32Array(i.data.colors):null,n);else{let l=i.local_file??i.filename??i.remote_url;if(!l)throw new Error("A filename, local_file or mesh is required");let c;if(i.remote_url){let g=new URL(i.remote_url);if(!["https:","http:"].includes(g.protocol)||g.username||g.password)throw new Error("Use a public HTTP or HTTPS file URL without login credentials.");if(!this.options.url_import_endpoint)throw new Error("Configure url_import_endpoint to import remote URLs through a relay.");if(c=new URL(this.options.url_import_endpoint,document.baseURI).href,new URL(c).origin!==location.origin)throw new Error("The URL relay must use the same origin as this page.")}let m=this._pool.run({kind:"parse",remote:i.remote_url,format:i.remote_format,relay:c,source:typeof l=="string"?new URL(l,document.baseURI).href:l,name:e.filename,limits:n,features:d.smooth||d.edges||d.wire?d:null},g=>this._progress(e.id,g));this._jobs.set(e,m),e.geometry=await m,i.remote_url&&(e.filename=e.geometry.source_name,delete e.remote_url,delete e.remote_format)}if(this.killsign||o!==this._generation||this._models.get(e.id)!==e){e.geometry=null;return}this.center_models&&(e.center=e.geometry.bounds.min.map((l,c)=>(l+e.geometry.bounds.max[c])/2)),(i.mesh||i.data)&&(d.smooth||d.edges||d.wire)&&await this._derive(e,d),e.animation_state&&(e.center=[...e.animation_state.center]),e.animation&&!e._animation&&this._startAnimation(e,e.animation,e.animation_state),delete e.animation_state,delete e.mesh,delete e.data,this._invalidate()}finally{r()}}_finishSession(){this.killsign||this._pending.size||this._sceneLoads.size||!this._sessionActive||(this._sessionActive=!1,this._restoreCamera?(this.set_camera_state(this._restoreCamera),this._restoreCamera=null):this.zoom===-1&&this.set_auto_zoom(),this._gridAuto&&this.set_grid(!0),this.load_session++,this._callback("all_loaded_callback"),this._invalidate())}_deleteModel(e){this._stopAnimation(e,"removed");for(let[i,o]of this._printJobs||[])o.has(e)&&i.abort();this._printCache=null,this.renderer?.selection===e&&this.renderer.setSelection(null),this.renderer?.remove(e),this._jobs.get(e)?.cancel?.();for(let i of e._deriveJobs||[])i.cancel?.();this._models.delete(e.id),this._memoryReservations.delete(e),this.models[this.models_ref[e.id]]=null,delete this.models_ref[e.id],delete this.load_status[e.id],this.models_count=this._models.size,this._invalidate()}remove_model(e){return this._guard(()=>{let i=this._model(e);this._deleteModel(i),this._pending.delete(i.id),this._finishSession()})}clean(){if(!this.killsign){this._generation++,this._printCache=null,this._pool?.clear();for(let e of this._aborters)e.abort();this._aborters.clear();for(let e of this._models.values())this._stopAnimation(e,"clean");this._models.clear(),this._pending.clear(),this._jobs.clear(),this._derived.clear(),this._memoryReservations.clear(),this._sceneLoads.clear(),this.models=[],this.models_ref=Object.create(null),this.models_count=0,this.max_model_id=-1,this.load_status=Object.create(null),this._sessionActive=!1,this.renderer?.clear(),this._restoreCamera=null,this._invalidate()}}dispose(){if(!this.killsign){this.clean(),this.killsign=!0,this.ready=!1,this._readyReject(new Error("Viewer disposed")),cancelAnimationFrame(this._frame),this._frame=0,this._pool?.dispose(),this.controls?.dispose(),this._resizeObserver?.disconnect(),this._windowResize&&window.removeEventListener("resize",this._windowResize),this.renderer?.dispose(),this.canvas&&(this._visibility&&this.canvas.ownerDocument.removeEventListener("visibilitychange",this._visibility),this.canvas.removeEventListener("drop",this._drop),this.canvas.removeEventListener("dragover",this._drag),this.canvas.removeEventListener("keydown",this._key),this.canvas.remove()),this._removeMessage();for(let e of Object.keys(this))e.endsWith("_callback")&&(this[e]=null);this.options={}}}async when_idle(){for(await this.when_ready;this._pending.size||this._sceneLoads.size||this._derived.size;)await Promise.allSettled([...this._pending.values(),...this._sceneLoads,...this._derived]);return this}_derive(e,i){let o=this._occupiedMemory(e)+[...this._memoryReservations?.get(e)?.values()||[]].filter(m=>m.kind==="derive").reduce((m,g)=>m+g.bytes,0),s=vi(e.geometry.triangles,{...i,smooth:i.smooth||!!e.geometry.smoothNormals,edges:i.edges||!!e.geometry.edges,wire:i.wire||!!e.geometry.wire});Q(this.limits,o+s,"view mode");let n=this._reserveMemory(e,s-67108864),r=this._generation,d=e._deriveRevision=(e._deriveRevision||0)+1,l;try{let m=e.geometry.positions.slice();l=this._pool.run({kind:"derive",positions:m,features:i,limits:{...this.limits,max_memory_bytes:this.limits.max_memory_bytes-o}},null,[m.buffer])}catch(m){throw n(),m}(e._deriveJobs??=new Set).add(l);let c=l.then(m=>!this.killsign&&r===this._generation&&d===e._deriveRevision&&this._models.get(e.id)===e?(Object.assign(e.geometry,m),this.renderer.remove(e),this._invalidate(),!0):!1).catch(m=>{m.name!=="AbortError"&&r===this._generation&&this._error(m)}).finally(()=>{n(),e._deriveJobs.delete(l),this._derived.delete(c)});return this._derived.add(c),c}set_view(e,i){return this._guard(()=>{let o=this._model(e),s=o._requestedView||o,n=tt(i.display??s.display,i.smooth_shading??s.smooth_shading),r=Ct({...o,...n}),d=o.geometry,c=d&&(r.smooth&&!d.smoothNormals||r.wire&&!d.wire||r.edges&&(!d.edges||d.edgeAngle!==r.edgeAngle))?this._derive(o,r):null,m=(o._viewRevision||0)+1;o._viewRevision=m,o._requestedView=n;let g=x=>{this._models.get(e)!==o||o._viewRevision!==m||(x&&Object.assign(o,n),delete o._requestedView,this._invalidate())};if(!c)g(!0);else{let x=c.then(g).finally(()=>this._derived.delete(x));this._derived.add(x)}})}set_display(e,i){return this.set_view(e,{display:i,...i==="flat"||i==="smooth"?{smooth_shading:i==="smooth"}:{}})}set_smooth(e,i){return this._guard(()=>{let o=this._model(e),s=o._requestedView||o;return this.set_view(e,{display:s.display==="smooth"?"flat":s.display,smooth_shading:i})})}set_section(e,i){return this._guard(()=>{this._model(e).section=it(i),this._invalidate()})}set_color(e,i){return this._guard(()=>{i!==null&&i!=="transparent"&&X(i);let o=this._model(e);o.color=i,this._invalidate()})}set_opacity(e,i){return this._guard(()=>{if(K(i,"opacity"),i<0||i>1)throw new Error("Opacity must be between 0 and 1");this._model(e).opacity=i,this._invalidate()})}set_position(e,i,o,s){return this._guard(()=>{let n=this._model(e);[i,o,s].forEach(r=>{r!==void 0&&K(r,"position")}),this._stopAnimation(n,"manual"),[i,o,s].forEach((r,d)=>{r!==void 0&&(n[["x","y","z"][d]]=K(r,"position"))}),this._invalidate()})}set_scale(e,i,o=i,s=i,n=!1){return this._guard(()=>{let r=this._model(e);if([i,o,s].forEach(d=>Me(d,"scale")),n){if(i/r.scalex!==o/r.scaley||i/r.scalex!==s/r.scalez)throw new Error("keep_prev_scale requires a uniform geometry factor");this._stopAnimation(r,"manual"),r.unitScale*=i/r.scalex}else this._stopAnimation(r,"manual"),[r.scalex,r.scaley,r.scalez]=[i,o,s];r._stats=null,this._invalidate()})}set_selection(e){return this._guard(()=>{this.renderer?.setSelection(e===null?null:this._model(e)),this._invalidate()})}set_rotation(e,i,o,s,n=!1){return this._guard(()=>{let r=this._model(e);[i,o,s].forEach(d=>{d!==void 0&&K(d,"rotation")}),this._stopAnimation(r,"manual"),[i,o,s].forEach((d,l)=>{let c=["rotationx","rotationy","rotationz"][l];d!==void 0&&(r[c]=K(d,"rotation")+(n?r[c]:0))}),this._invalidate()})}rotate(e,i,o,s){return this.set_rotation(e,i,o,s,!0)}set_edges(e,i){return this._guard(()=>{let o=this._model(e);o.view_edges=!!i,i&&o.geometry&&!o.geometry.edges&&this._derive(o,{edges:!0}),this._invalidate()})}set_grid(e,i,o=10){return this._guard(()=>{if(this._gridAuto=!!e&&i===void 0,!e)this.grid=null,this.renderer?.setGrid(null);else{let s=this._bounds();if(i??=s&&Math.max(...H(s.max,s.min))*1.2||10,Me(i,"grid size"),!Number.isInteger(o)||o<1||o>1e3)throw new Error("Grid divisions must be 1\u20131000");this.grid={size:i,divisions:o},this.renderer?.setGrid(this.grid)}this._invalidate()})}set_infinite_grid(e,i=1,o=0){return this._guard(()=>{Me(i,"grid spacing"),K(o,"grid height"),this._gridAuto=!1,this.grid=e?{infinite:!0,spacing:i,height:o}:null,this.renderer?.setGrid(this.grid),this._invalidate()})}set_model_units(e,i,o=!1){return this._guard(()=>{if(!["mm","inch"].includes(i))throw new Error("Units must be mm or inch");let s=this._model(e);this._stopAnimation(s,"manual"),o&&s.units!==i&&(s.unitScale*=i==="inch"?25.4:1/25.4),s.units=i,s._stats=null,this._invalidate()})}set_bg_color(e){return this._guard(()=>{e!=="transparent"&&X(e),this.bg_color=e,this._invalidate()})}set_center_models(e){this.center_models=!!e}set_zoom(e=-1){return this._guard(()=>{if(K(e,"zoom"),e===-1)this.zoom=-1,this.set_auto_zoom();else{if(e<0)throw new Error("Zoom must be non-negative or -1");this.zoom=e,this.camera.position[2]=Math.max(e,1e-6),this._invalidate()}})}set_auto_zoom(){let e=this._bounds();if(!e)return;this.zoom=-1;let i=(this.canvas?.clientWidth||1)/(this.canvas?.clientHeight||1),o=Math.min(Math.PI/8,Math.atan(Math.tan(Math.PI/8)*i)),s=q(H(this.camera.position,this.camera.target));this.camera.target=e.center,this.camera.position=re(e.center,oe(Math.hypot(...s)?s:[0,0,1],e.radius/Math.sin(o)*1.08)),this._invalidate()}set_auto_rotate(e){this.auto_rotate=!!e,this._invalidate()}set_mouse_zoom(e){this.mouse_zoom=!!e,this.controls&&(this.controls.noZoom=!e)}set_auto_resize(e){this.auto_resize=!!e,this._resizeObserver?.disconnect(),this._windowResize&&window.removeEventListener("resize",this._windowResize),!(!e||!this.canvas)&&(typeof ResizeObserver<"u"&&(this._resizeObserver=new ResizeObserver(()=>this.do_resize()),this._resizeObserver.observe(this.parent_element)),this._windowResize=()=>this.do_resize(),window.addEventListener("resize",this._windowResize))}do_resize(){if(!this.renderer||this.killsign)return;let e=this.canvas.getBoundingClientRect();this.renderer.resize(e.width||this.parent_element.clientWidth||300,e.height||this.parent_element.clientHeight||150,this.options.pixel_ratio??Math.min(globalThis.devicePixelRatio||1,2)),this._invalidate()}set_on_model_mousedown(e){this.onmousedown_callback=e}set_on_model_mouseclick(e){this.set_on_model_mousedown(e)}set_drag_and_drop(e){this.allow_drag_and_drop=!!e,this.canvas&&(this.canvas.removeEventListener("drop",this._drop),this.canvas.removeEventListener("dragover",this._drag),e&&(this.canvas.addEventListener("drop",this._drop),this.canvas.addEventListener("dragover",this._drag)))}get_camera_state(){return{position:ft(this.camera.position),up:ft(this.camera.up),target:ft(this.camera.target)}}set_camera_state(e){return this._guard(()=>{let i=e.position?Bt(e.position,"position"):this.camera.position,o=e.up?Bt(e.up,"up"):this.camera.up,s=e.target?Bt(e.target,"target"):this.camera.target;if(!Math.hypot(...o)||!Math.hypot(...H(i,s)))throw new Error("Degenerate camera state");this.camera.position=i,this.camera.up=q(o),this.camera.target=s,this._invalidate()})}set_camera(e,i,o){return this.set_camera_state({position:{x:e??this.camera.position[0],y:i??this.camera.position[1],z:o??this.camera.position[2]}})}pick_model(e,i){if(!this.ready||this.killsign||!Number.isFinite(e)||!Number.isFinite(i))return null;let o=this.canvas.getBoundingClientRect();if(!o.width||!o.height||e<o.left||e>o.right||i<o.top||i>o.bottom)return null;let s=ye(nt(this.camera,o.width/o.height)),n=se(s,[(e-o.left)/o.width*2-1,1-(i-o.top)/o.height*2,1]),r=q(H(n,this.camera.position)),d=null,l=1/0;for(let c of this._models.values())if(c.geometry&&c.opacity>0&&c.color!=="transparent"){let m=ye(J(c)),g=Ei(c.geometry,se(m,this.camera.position),se(m,r,0),ot(c.geometry.bounds,c.section));g<l&&(l=g,d=c.id)}return d===null?null:{id:d,distance:l}}_pick(e,i){if(!this.onmousedown_callback)return;let o=this.pick_model(e.clientX,e.clientY);(o||this.send_no_model_click_event)&&this._callback("onmousedown_callback",o?.id??null,e,o?.distance??0,i)}get_model_info(e){return this._guard(()=>{let i=this._model(e),o=i.geometry;if(!o)return null;let s=i.scaleVector,n=s.join(",");(!i._stats||i._stats.key!==n)&&(i._stats={key:n,...Mi(o,s)});let r=i.units==="inch"?1/25.4:1;return{name:i.filename,orig_filename:i.orig_filename??null,position:{x:i.x,y:i.y,z:i.z},dims:ft(o.bounds.min.map((d,l)=>(o.bounds.max[l]-d)*s[l])),rotation:{x:i.rotationx,y:i.rotationy,z:i.rotationz},display:i.display,smooth_shading:i.smooth_shading,section:i.section?{...i.section}:null,color:i.color??(o.colors?"#ffffff":"#909090"),scale:{x:i.scalex,y:i.scaley,z:i.scalez},volume:i._stats.volume*r**3,area:i._stats.area*r**2,triangles:o.triangles,bounds:{min:[...o.bounds.min],max:[...o.bounds.max]},has_colors:!!o.colors,source_units:o.units||null,units:i.units,opacity:i.opacity,file_bytes:o.file_bytes??null}})}get_model_data(e){return this._guard(()=>{let i=this._model(e);return i.geometry?{positions:i.geometry.positions.slice(),colors:i.geometry.colors?.slice()??null}:null})}get_model_mesh(e){return this._guard(()=>{let i=this._model(e);return i.geometry?Hi(i,this.options.three??globalThis.THREE):null})}get_stl_bin(e){return this._guard(()=>{let i=this._model(e);return i.geometry?Pt(i.geometry,i.scaleVector,i.center):null})}check_printability(e={}){return Di.call(this,e)}is_printability_current(e){return ut(this,e)}show_printability(e,i){return ji.call(this,e,i)}clear_printability(){this.renderer?.setDiagnostic(null),this._invalidate()}async export_models(e={}){let i=Bi(e),o=e.signal,s=()=>{throw new DOMException("Export cancelled","AbortError")};if(o?.aborted&&s(),await this.when_ready,o?.aborted&&s(),this.killsign)throw new Error("Viewer disposed");let n=e.model_ids;if(n==="selected"){let f=this.renderer?.selection;n=f?[f.id]:[]}else n===void 0&&(n=[...this._models.keys()]);if(!Array.isArray(n)||n.some(f=>!Number.isSafeInteger(f))||new Set(n).size!==n.length)throw new Error("model_ids must be an array of unique model IDs or 'selected'");if(!n.length)throw new Error("No models to export");let r=n.map(f=>{let p=this._model(f);if(!p.geometry)throw new Error(`Model ${f} has not finished loading`);return p});if(r.reduce((f,p)=>f+p.geometry.triangles,0)>this.limits.max_triangles)throw new Error("Export triangle limit exceeded");let l=Ui(r,i),c=this._occupiedMemory();Q(this.limits,c+l,"export"),this._exportBytes=(this._exportBytes||0)+l;let m,g,x;try{let f=r.map(u=>{let y=i.apply_transforms?u:{...u,center:[0,0,0],x:0,y:0,z:0,rotationx:0,rotationy:0,rotationz:0,scaleVector:[u.unitScale,u.unitScale,u.unitScale]},_=J(y),E=i.units==="inch"?1/25.4:1;for(let z=0;z<4;z++)for(let k=0;k<3;k++)_[z*4+k]*=E;return{id:u.id,name:pt(u.filename),matrix:_,positions:u.geometry.positions.slice(),colors:i.colors?u.geometry.colors?.slice():null,color:i.colors&&u.color&&u.color!=="transparent"?X(u.color):null,opacity:u.color==="transparent"?0:u.opacity}}),p=f.flatMap(u=>[u.positions.buffer,...u.colors?[u.colors.buffer]:[]]);m=this._pool.run({kind:"export",models:f,options:i,limits:{...this.limits,max_memory_bytes:this.limits.max_memory_bytes-c}},u=>{try{e.on_progress?.(u)}catch(y){x=y,m.cancel()}},p),g=()=>m.cancel(),o?.addEventListener("abort",g,{once:!0}),o?.aborted&&g();try{return await m}catch(u){throw x||u}}finally{g&&o?.removeEventListener("abort",g),this._exportBytes-=l}}async download_models(e={}){let i=await this.export_models(e);if(e.signal?.aborted)throw new DOMException("Export cancelled","AbortError");return Ue(i.blob,i.filename),i}download_model(e,i){let o=this.get_stl_bin(e);o&&Ue(new Blob([o],{type:"model/stl"}),i??this._model(e).filename??"model.stl")}async export_scene(e={}){let i=String(e.format||"").toLowerCase();if(!["vsb","vsj"].includes(i))throw new Error("Scene format must be vsb or vsj");if(e.force_basename!==void 0&&typeof e.force_basename!="boolean")throw new Error("force_basename must be a boolean");if(e.on_progress!==void 0&&typeof e.on_progress!="function")throw new Error("on_progress must be a function");let o=e.signal,s=()=>{if(o?.aborted)throw new DOMException("Export cancelled","AbortError")};if(s(),await this.when_ready,s(),this.killsign)throw new Error("Viewer disposed");if(this._pending.size||this._sceneLoads.size)throw new Error("Wait for models to finish opening before saving the scene");let n=[...this._models.values()];if(!n.length)throw new Error("No models to export");if(n.some(p=>!p.geometry))throw new Error("A model has not finished loading");if(n.length+(i==="vsb"?1:0)>this.limits.max_archive_entries)throw new Error("Scene model count limit exceeded");let r=n.reduce((p,u)=>p+u.geometry.triangles,0);if(r>this.limits.max_triangles)throw new Error("Scene triangle limit exceeded");let d=this.get_vsj(!0,!!e.force_basename,i==="vsb"),l=JSON.stringify(d),c=new Blob([l]).size;if(c>this.limits.max_file_bytes)throw new Error("Scene manifest size limit exceeded");if(i==="vsb"&&c+n.length*84+r*50>this.limits.max_uncompressed_bytes)throw new Error("Scene archive limit exceeded");let m=67108864+l.length*8+(i==="vsb"?r*512:0);Q(this.limits,this._occupiedMemory()+m,"scene export"),this._exportBytes+=m;let g,x,f;try{let p=i==="vsb"?n.map((y,_)=>({id:y.id,triangles:y.geometry.triangles,positions:y.geometry.positions.slice(),colors:y.geometry.colors?.slice()||null,scale:d.models[_].animation_state?[1,1,1]:y.scaleVector,center:d.models[_].animation_state?[0,0,0]:[...y.center]})):null;e.on_progress?.({loaded:0,total:n.length,phase:"exporting"}),s();let u;if(i==="vsj")u=new Blob([l],{type:"application/json"}),e.on_progress?.({loaded:n.length,total:n.length,phase:"complete"});else{g=this._pool.run({kind:"export-scene",scene:d,models:p,limits:this.limits},y=>{try{e.on_progress?.(y)}catch(_){f=_,g.cancel()}},p.flatMap(y=>[y.positions.buffer,...y.colors?[y.colors.buffer]:[]])),x=()=>g.cancel(),o?.addEventListener("abort",x,{once:!0}),o?.aborted&&x();try{u=await g}catch(y){throw f||y}}return s(),{blob:u,format:i,filename:Dt(e.filename,i),model_ids:n.map(y=>y.id),triangles:r,representation:"scene"}}finally{x&&o?.removeEventListener("abort",x),this._exportBytes-=m}}async download_scene(e={}){let i=await this.export_scene(e);if(e.signal?.aborted||this.killsign)throw new DOMException("Export cancelled","AbortError");return Ue(i.blob,i.filename),i}get_vsj(e=!1,i=!1,o=!1){let s={canvas_height:this.canvas_height,bg_color:this.bg_color,camera_state:this.get_camera_state(),auto_rotate:this.auto_rotate,mouse_zoom:this.mouse_zoom,auto_resize:this.auto_resize,center_models:this.center_models,models:[]};this.grid&&(s.grid=!0,this.grid.infinite&&(s.infinite_grid={spacing:this.grid.spacing,height:this.grid.height}));for(let n of this._models.values())if(n.geometry){let r={id:o?-1:n.id,filename:o?`${n.id}.stl`:i?pt(n.filename):n.filename,units:n.units,display:n.display,opacity:n.opacity,view_edges:n.view_edges,smooth_shading:n.smooth_shading,section:n.section?{...n.section}:null};o&&(r.orig_filename=pt(n.orig_filename||n.filename));for(let l of jt)l.startsWith("scale")||(r[l]=n[l]);(!o||n._animation)&&(r.scalex=n.scalex*n.unitScale,r.scaley=n.scaley*n.unitScale,r.scalez=n.scalez*n.unitScale),n.color&&(r.color=n.color);let d=zi(n);if(d)Object.assign(r,d);else if(!o){let l=this.center_models?n.geometry.bounds.min.map((m,g)=>(m+n.geometry.bounds.max[g])/2):[0,0,0],c=se(J(n),l);["x","y","z"].forEach((m,g)=>r[m]=c[g])}s.models.push(r)}return e?s:JSON.stringify(s)}download_vsj(e="1.vsj"){Ue(new Blob([this.get_vsj()],{type:"application/json"}),e)}get_vsb(){return{vsj:this.get_vsj(!0,!0,!0),files:[...this._models.values()].filter(e=>e.geometry).map(e=>({id:e.id,bin:e._animation?Pt(e.geometry,[1,1,1],[0,0,0]):this.get_stl_bin(e.id)}))}}async get_vsb_blob(){await this.when_ready;let e=this.get_vsb(),i=this._pool.run({kind:"zip",scene:e,limits:this.limits},null,e.files.map(o=>o.bin));return new Blob([await i],{type:"application/zip"})}download_vsb(e="1.vsb"){return this.get_vsb_blob().then(i=>Ue(i,/\.vsb$/i.test(e)?e:`${e}.vsb`)).catch(i=>this._error(i))}import_scene(e,i={}){return Oi.call(this,e,i)}_sceneLoad(e,i){let o=this._generation;this._sessionActive=!0;let s=(async()=>{if(await this.when_ready,o!==this._generation||this.killsign)return;let n,r,d=document.baseURI;if(typeof e=="string"&&(d=new URL(e,document.baseURI).href),i){if(r=await this._pool.run({kind:"unzip",source:typeof e=="string"?d:e,limits:this.limits}),!Object.hasOwn(r,"json_data.vsj"))throw new Error("VSB manifest json_data.vsj is missing");n=JSON.parse(new TextDecoder().decode(r["json_data.vsj"]))}else{let c=new AbortController;this._aborters.add(c);try{n=JSON.parse(new TextDecoder().decode(await Vi(typeof e=="string"?d:e,this.limits.max_file_bytes,()=>{},c.signal)))}finally{this._aborters.delete(c)}}if(o!==this._generation||this.killsign)return;if(!n||!Array.isArray(n.models)||n.models.length>this.limits.max_archive_entries)throw new Error("Invalid or oversized scene manifest");n.bg_color!==void 0&&this.set_bg_color(n.bg_color),n.center_models!==void 0&&this.set_center_models(n.center_models),n.auto_rotate!==void 0&&this.set_auto_rotate(n.auto_rotate),n.mouse_zoom!==void 0&&this.set_mouse_zoom(n.mouse_zoom),n.auto_resize!==void 0&&this.set_auto_resize(n.auto_resize),n.canvas_height!==void 0&&(this.canvas_height=n.canvas_height,this._setSizeStyle(),this.do_resize()),n.infinite_grid?this.set_infinite_grid(!0,n.infinite_grid.spacing,n.infinite_grid.height):n.grid&&this.set_grid(!0),n.camera_state&&(this._restoreCamera=n.camera_state);let l=[];try{for(let c of n.models){if(!c||typeof c!="object")throw new Error("Invalid scene model");let m=qi(c);if(delete m.mesh,delete m.data,delete m.local_file,r){if(typeof m.filename!="string"||!Object.hasOwn(r,m.filename))throw new Error("VSB model file is missing");m.local_file=new File([r[m.filename]],m.filename)}else m.filename&&(m.filename=new URL(m.filename,d).href);let g=m.id===void 0||m.id===-1?this.max_model_id+1:m.id;if(this._models.has(g))throw new Error(`Invalid or duplicate model id: ${g}`);this.add_model({...m,id:g,animation:null,animation_state:null});let x=this._models.get(g);if(!x)throw new Error(this.error||"Invalid scene model");x._sceneImportPending=!0,l.push({m:x,model:m,pending:this._pending.get(g)})}if(await Promise.all(l.map(c=>c.pending)),o!==this._generation||this.killsign)return;for(let{m:c,model:m}of l){if(this._models.get(c.id)!==c||!c.geometry)throw new Error("A scene model could not be opened");if(m.animation)De(m.animation,c,m.animation_state);else if(m.animation_state)throw new Error("Playback state needs an animation")}for(let{m:c,model:m}of l)m.animation&&this._startAnimation(c,m.animation,m.animation_state),c._sceneImportPending=!1}catch(c){for(let{m}of l)this._models.get(m.id)===m&&this.remove_model(m.id);throw c}})().catch(n=>{o===this._generation&&n.name!=="AbortError"&&this._error(n)}).finally(()=>{this._sceneLoads.delete(s),this._finishSession()});this._sceneLoads.add(s)}load_vsj(e){return this._sceneLoad(e,!1)}load_vsb(e){return this._sceneLoad(e,!0)}animate_model(e,i){return this._guard(()=>{let o=this._model(e);if(!i){this._stopAnimation(o,"stopped"),this._invalidate();return}this._startAnimation(o,i),this._invalidate()})}_startAnimation(e,i,o){let s=De(i,e,o);e._animation&&this._stopAnimation(e,"replaced"),o&&(e.center=[...o.center]),e.animation=s.animation,e._animation=s,delete e.animation_state,e._animationResult=null,lt(s,e)?this._animationEvent(e,o?"restored":"started"):this._completeAnimation(e)}_animationEvent(e,i){let o=this._animationState(e);queueMicrotask(()=>{this._callback("animation_state_callback",e.id,o,i),i==="completed"&&this._callback("animation_complete_callback",e.id,o)})}_animationState(e){return e._animation?Si(e._animation,!!this._animationSuspended):{...e._animationResult??{status:"idle",elapsed_ms:0},suspended:!1,tracks:[]}}_stopAnimation(e,i){!e._animation&&!e.animation||(e._animationResult={status:"stopped",elapsed_ms:e._animation?.elapsed_ms??0},e._animation=null,e.animation=null,delete e.animation_state,this._animationEvent(e,i))}_completeAnimation(e){e._animationResult={status:"completed",elapsed_ms:e._animation.elapsed_ms},e._animation=null,e.animation=null,this._animationEvent(e,"completed")}pause_model_animation(e){return this._guard(()=>{let i=this._model(e);!i._animation||i._animation.paused||(i._animation.paused=!0,i._animation.last_time=null,this._animationEvent(i,"paused"),this._invalidate())})}resume_model_animation(e){return this._guard(()=>{let i=this._model(e);!i._animation||!i._animation.paused||(i._animation.paused=!1,i._animation.last_time=null,this._animationEvent(i,"resumed"),this._invalidate())})}get_model_animation_state(e){return this._guard(()=>this._animationState(this._model(e)))}_suspendAnimations(e){this._animationSuspended=!!e,this._lastTime=0;for(let i of this._models.values())i._animation&&(i._animation.last_time=null);e?(cancelAnimationFrame(this._frame),this._frame=0):this._invalidate()}_animate(e,i){if(!(this._animationSuspended||!e._animation))try{Ai(e._animation,e,i)||this._completeAnimation(e)}catch(o){this._stopAnimation(e,"error"),this._error(o)}}async get_image_blob(e="image/png"){await this.when_idle(),this._clip(),this.renderer.draw([...this._models.values()],this.camera,this.bg_color,!1);let i=new Promise((o,s)=>this.canvas.toBlob(n=>n?o(n):s(new Error("Screenshot failed")),e));return this._invalidate(),i}};var Ut=new WeakMap,po=["background-color","background-value","cancel-load","clear-scene","close-colors","close-info","close-model","close-scene","close-section","close-view","color-mode-note","colors-button","colors-panel","depth-controls","depth-model-button","dismiss-notice","drop-message","drop-overlay","empty-state","file-heading","file-input","filename","finish-move","fit-button","grid-button","image-button","info-button","info-dimensions","info-panel","info-size","info-triangles","live-status","loading-label","loading-state","memory-budget","model-color","model-color-value","model-controls","model-list","model-tool","move-controls","move-gesture-message","move-mode","move-mode-name","move-model-button","notice","notice-message","open-label","position-details","position-error","position-units","position-x","position-y","position-z","reset-position","reset-rotation","restore-color","rotate-controls","rotate-model-button","rotation-error","rotation-x","rotation-y","rotation-z","sample-button","scene-arrangement","scene-button","scene-count","scene-panel","section-axis","section-button","section-enabled","section-flip","section-panel","section-position","section-value","smooth-shading","unit-note","view-button","view-description","view-label","view-panel","view-status","viewer","workspace"];function Wi(t,e={}){if(!t?.querySelectorAll)throw new TypeError("A Document or HTMLElement root is required");if(Ut.has(t))throw new Error("ViewSTL app is already mounted on this root");let i=t.ownerDocument||t,o=i.defaultView,s=o.URL,n=new Map([...t.querySelectorAll("[id]")].map(a=>[a.id,a])),r=a=>n.get((e.id_prefix||"")+a),d=po.filter(a=>!r(a));if(Ot.some(a=>r(a))&&d.push(...Ot.filter(a=>!r(a))),Ft.some(a=>r(a))&&d.push(...Ft.filter(a=>!r(a))),Nt.some(a=>r(a))&&d.push(...Nt.filter(a=>!r(a))),d.length)throw new Error("Missing ViewSTL controls: "+d.join(", "));let l=new o.AbortController,c=t===i?o:t,m=!1,g=null,x=null,f=()=>{if(m)throw new Error("ViewSTL app is disposed")},p=(a,h,w,M={})=>a.addEventListener(h,w,{...M,signal:l.signal}),u=oi(o.navigator.deviceMemory),y=["view","section","info","scene","colors"],_=new Map,E=[],z=["#64a5b2","#dd766b","#edb14c","#7ca984","#927fbc","#5a8ec7","#89949d"],k,b,S=0,P=0,v=null,O=0,B=null,R,T,C,L,A=!1,I=!1,N=!1,Z=0,ke=1,W=!1,U="move",Y,Vt,ge="transparent",Yi={flat:"Solid",edges:"Solid + edges",wireframe:"Wireframe",xray:"X-ray",mesh:"Mesh overlay",studio:"Studio",orientation:"Face orientation"},we=[...t.querySelectorAll('[name="view-mode"]')];e.id_prefix&&we.forEach(a=>a.name=e.id_prefix+"view-mode");let _t=e.sample_url,Ve=new Intl.NumberFormat(void 0,{maximumFractionDigits:2});function j(a){m||(r("live-status").textContent=a)}function le(a){m||(r("notice-message").textContent=a,r("notice").hidden=!1)}function Ae(){b?.set_selection(!I&&_.has(v)&&(W||!r("scene-panel").hidden)?v:null)}function Gt(a){r(a+"-panel").hidden=!0,r(a+"-button").setAttribute("aria-expanded","false"),Ae()}function ue(){y.forEach(Gt)}function Ge(a){let h=r(a+"-panel").hidden;ue(),r(a+"-panel").hidden=!h,r(a+"-button").setAttribute("aria-expanded",String(h)),Ae()}function Se(a){return a==null?"Sample model":a<1024**2?`${Ve.format(a/1024)} KiB`:a<1024**3?`${Ve.format(a/1024**2)} MiB`:`${Ve.format(a/1024**3)} GiB`}function gt(){let a=_.get(v),h=/^#[a-f0-9]{6}$/i.test(a?.color)?a.color:"#ffffff";r("model-color").value=h,r("model-color-value").textContent=a?.color===null?"File colors":h.toUpperCase(),r("restore-color").textContent=a?.embedded?"Restore file colors":"Restore original color",t.querySelectorAll("[data-model-color]").forEach(w=>w.setAttribute("aria-pressed",String(w.dataset.modelColor===a?.color))),t.querySelectorAll("[data-background]").forEach(w=>w.setAttribute("aria-pressed",String(w.dataset.background===ge))),r("background-color").value=ge==="transparent"?"#fafbfb":ge,r("background-value").textContent=ge==="transparent"?"Default":ge.toUpperCase()}function Ht(){he(_.get(v),b?._models.get(v));let a=v===null?null:b.get_model_info(v),h=a?.display==="smooth"?"flat":a?.display||"flat";for(let F of we)F.checked=F.value===h,F.disabled=I;r("view-label").textContent=Yi[h],r("smooth-shading").checked=a?.smooth_shading||!1,r("smooth-shading").disabled=I||h==="wireframe",r("view-status").textContent=h==="wireframe"?"Smooth shading is available in surface views.":"Shading changes the appearance, not the geometry.";let w=h==="orientation"?"Blue: front faces \xB7 Red: back faces":"";r("view-description").textContent=w,r("view-description").hidden=!w,r("color-mode-note").hidden=h!=="orientation";let M=a?.section;r("section-enabled").checked=!!M,r("section-axis").value=M?.axis||"z",r("section-position").value=(M?.position??.5)*100,r("section-value").textContent=r("section-position").value+"%",r("section-flip").setAttribute("aria-pressed",String(M?.flip||!1)),r("section-button").setAttribute("aria-pressed",String(!!M)),t.querySelectorAll("[data-selected-name]").forEach(F=>F.textContent=_.get(v)?.name||""),gt(),Ye(),R?.sync(),T?.sync(),C?.sync()}function qt(){if(v===null)return;let a=b.get_model_info(v),h=_.get(v),w=h.units==="mm"?" mm":"";r("info-triangles").textContent=a.triangles.toLocaleString(),r("info-dimensions").textContent=[a.dims.x,a.dims.y,a.dims.z].map(M=>Ve.format(M)).join(" \xD7 ")+w,r("info-size").textContent=Se(h.size),r("unit-note").textContent=w?"Dimensions are shown in millimetres.":"Dimensions are in model units. STL and OBJ files do not specify physical units."}function Wt(){r("model-list").replaceChildren(...[..._.values()].map(a=>{let h=i.createElement("li"),w=i.createElement("button"),M=i.createElement("button"),F=i.createElement("button");return h.className="model-row",h.classList.toggle("selected",a.id===v),w.className="model-swatch",w.dataset.colorModel=a.id,w.style.backgroundColor=a.color||"#b7c5c9",w.setAttribute("aria-label","Color for "+a.name),w.title="Change color",M.className="model-pick",M.dataset.selectModel=a.id,M.textContent=a.name,M.title=a.name,M.setAttribute("aria-pressed",String(a.id===v)),F.className="icon-button",F.dataset.removeModel=a.id,F.textContent="\xD7",F.setAttribute("aria-label","Remove "+a.name),h.append(w,M,F),h})),r("scene-count").textContent=_.size,r("scene-arrangement").disabled=_.size===0}function xe(){let a=_.size>0;r("workspace").classList.toggle("has-models",a),r("viewer").hidden=!a&&!I,r("empty-state").hidden=a||I,r("file-heading").hidden=!a,r("model-controls").hidden=!a,r("filename").textContent=_.get(v)?.name||"",r("filename").title=r("filename").textContent,r("open-label").textContent=a?"Add models":"Open files",r("drop-message").textContent=a?"Add to your scene.":"Drop it here.",Wt(),Ht(),qt(),a||ue(),Ae()}function He(a){a&&R?.close(),!m&&(a&&G(!1),I=a,r("loading-state").hidden=!a,r("workspace").setAttribute("aria-busy",String(a)),xe())}function qe(a){R?.close(),_.has(a)&&(G(!1),P++,v=a,xe())}function Ji(){return ui([..._.values()],r("scene-arrangement").value)}function We(){b?.set_infinite_grid(N,ke,Z),r("grid-button").setAttribute("aria-pressed",String(N))}function yt(){G(!1);for(let h of _.values())he(h,b?._models.get(h.id));if(r("scene-arrangement").value==="custom")return;for(let h of _.values())te(h.id);if(r("scene-arrangement").value==="original")for(let h of _.values())h.rotation={...h.originalRotation||{x:0,y:0,z:0}};let a=Ji();for(let h of a)_.get(h.id).position={x:h.x,y:h.y,z:h.z},bt(_.get(h.id));(r("scene-arrangement").value!=="custom"||!_.size)&&(Z=fi([..._.values()],a)),Ye(),We(),b?.set_auto_zoom()}function Ye(a=!1){let h=_.get(v);for(let w of["x","y","z"]){let M=r("position-"+w);(!a||i.activeElement!==M)&&(M.value=Number((h?.position?.[w]||0).toPrecision(12))),M.disabled=!h,M.removeAttribute("aria-invalid")}r("position-error").hidden=!0,r("position-units").textContent=h?.animationPivot?"Position of the model center in the scene.":h?.units==="mm"?"Offset from file coordinates, in mm.":"Offset from file coordinates, in model units.",r("move-model-button").disabled=!h||I,r("reset-position").disabled=!h,Jt(a);for(let w of["rotate-model-button","depth-model-button","depth-nearer","depth-farther"])r(w).disabled=!h||I}function ze(a,h,w=!0){return!_.has(a)||!Object.values(h).every(M=>Number.isFinite(M)&&Math.abs(M)<=1e12)?!1:(_.get(a).position={...h},bt(_.get(a)),r("scene-arrangement").value="custom",a===v&&w&&Ye(),!0)}function bt(a){let h=Le(a),w=a.rotation;b.set_rotation(a.id,...["x","y","z"].map(M=>w[M]%360*Math.PI/180)),b.set_position(a.id,h.x,h.y,h.z)}function Yt(){te(v);let a=_.get(v);return a?{...a,center:di(a),radius:mi(a)}:null}function Jt(a=!1){let h=_.get(v);for(let w of["x","y","z"]){let M=r("rotation-"+w);(!a||i.activeElement!==M)&&(M.value=Number((h?.rotation?.[w]||0).toPrecision(10))),M.disabled=!h,M.removeAttribute("aria-invalid")}r("rotation-error").hidden=!0,r("reset-rotation").disabled=!h}function Je(a,h,w=!0){return!_.has(a)||!Object.values(h).every(M=>Number.isFinite(M)&&Math.abs(M)<=36e4)?!1:(_.get(a).rotation={...h},bt(_.get(a)),r("scene-arrangement").value="custom",w&&a===v&&Jt(),!0)}let Xi={move:"Move",rotate:"Rotate",depth:"Depth"},Xt={move:"Drag to move the selected model",rotate:"Drag to rotate the selected model",depth:"Drag up for nearer, down for farther"};function Ki(){b?.canvas&&(b.canvas.dataset.modelTool=U),t.querySelectorAll("[data-transform-tab]").forEach(a=>a.setAttribute("aria-pressed",String(a.dataset.transformTab===U)));for(let a of["move","rotate","depth"])r(a+"-controls").hidden=a!==U,r(a+"-model-button").setAttribute("aria-pressed",String(W&&a===U));r("model-tool").value=U,r("move-gesture-message").textContent=Xt[U]+" \xB7 Scroll to adjust depth"}function G(a,h=U){Y?.setEnabled(!1),U=h,W=!!a&&v!==null&&!I,W&&te(v),Y?.setEnabled(W),Ki(),r("move-mode").hidden=!W,r("move-mode-name").textContent=_.get(v)?.name||"",r("workspace").classList.toggle("moving-model-mode",W),b?.canvas&&b.canvas.setAttribute("aria-label",W?Xt[U]+". Scroll changes only its depth. Escape exits.":"3D model viewer. Drag to rotate, shift-drag to pan, scroll to zoom."),W&&(ue(),b.canvas.focus({preventScroll:!0}),j(Xi[U]+" tool for "+_.get(v).name+". Done or Escape returns to viewing.")),Ae()}for(let a of["x","y","z"]){let h=r("rotation-"+a);p(h,"input",()=>{v===null||!h.value.trim()||(te(v),Je(v,{..._.get(v).rotation,[a]:h.valueAsNumber},!1)&&(h.removeAttribute("aria-invalid"),r("rotation-error").hidden=!0))}),p(h,"change",()=>{v!==null&&(te(v),!h.value.trim()||!Je(v,{..._.get(v).rotation,[a]:h.valueAsNumber})?(h.setAttribute("aria-invalid","true"),r("rotation-error").hidden=!1):j("Model rotation updated."))})}p(r("reset-rotation"),"click",()=>{G(!1),v!==null&&(te(v),Je(v,{..._.get(v).originalRotation||{x:0,y:0,z:0}}),j("Selected model rotation reset."))});for(let a of["move","rotate","depth"])p(r(a+"-model-button"),"click",()=>G(!W||U!==a,a));for(let[a,h]of[["depth-nearer",-.12],["depth-farther",.12]])p(r(a),"click",()=>{let w=Yt();w&&(ze(w.id,Pe(w,b.get_camera_state(),h)),j(h<0?"Selected model moved nearer.":"Selected model moved farther."))});t.querySelectorAll("[data-transform-tab]").forEach(a=>p(a,"click",()=>G(!1,a.dataset.transformTab))),p(r("model-tool"),"change",()=>G(!0,r("model-tool").value));for(let a of["x","y","z"]){let h=r("position-"+a);p(h,"input",()=>{v===null||!h.value.trim()||(te(v),ze(v,{..._.get(v).position,[a]:h.valueAsNumber},!1)&&(h.removeAttribute("aria-invalid"),r("position-error").hidden=!0))}),p(h,"change",()=>{v!==null&&(te(v),!h.value.trim()||!ze(v,{..._.get(v).position,[a]:h.valueAsNumber})?(h.setAttribute("aria-invalid","true"),r("position-error").hidden=!1):j("Model position updated."))})}p(r("finish-move"),"click",()=>{G(!1),b?.canvas.focus({preventScroll:!0})}),p(r("reset-position"),"click",()=>{G(!1),v!==null&&(te(v),ze(v,{..._.get(v).originalPosition||{x:0,y:0,z:0}}),j("Selected model restored to its original position."))}),p(o,"blur",()=>{W&&G(!1)});function Xe(){S++,E.length=0,L?.abort(),B!==null&&(b?.remove_model(B),B=null),He(!1),j("Opening cancelled.")}function Kt(){R?.close(),G(!1),Xe(),P++,b?.clean(),_.clear(),v=null,N=!1,Z=0,ke=1,r("scene-arrangement").value="original",We(),r("notice").hidden=!0,xe(),j("Scene cleared.")}function vt(a){if(!_.has(a))return;R?.close();let h=_.get(a).name;G(!1),b.remove_model(a),_.delete(a),P++,v===a&&(v=[..._.keys()].at(-1)??null),yt(),xe(),j(h+" removed.")}function te(a){_.has(a)&&(he(_.get(a),b?._models.get(a)),b?.animate_model(a,null))}function Zt(){m||v===null||(he(_.get(v),b?._models.get(v)),!r("scene-panel").hidden&&r("position-details").open&&Ye(!0))}function Zi(a,h,w){m||(w==="restored"&&h.status==="running"&&o.matchMedia?.("(prefers-reduced-motion: reduce)").matches&&b.pause_model_animation(a),he(_.get(a),b?._models.get(a)),["started","restored","resumed"].includes(w)&&(r("scene-arrangement").value="custom"),a===v&&Zt(),C?.sync(),T?.sync(),e.animation_state_callback?.(a,h,w))}async function Qt(){return k||(k=Promise.resolve().then(async()=>{if(m)throw new Error("ViewSTL app is disposed");if(b=new ve(r("viewer"),{...u.limits,worker_count:1,worker_timeout:e.worker_timeout??6e5,worker_url:e.worker_url,worker_factory:e.worker_factory,url_import_endpoint:e.url_import_endpoint,center_models:!1,bg_color:ge,allow_drag_and_drop:!1,animation_state_callback:Zi,animation_complete_callback:(...a)=>{m||e.animation_complete_callback?.(...a)},camera_state:{position:{x:90,y:-130,z:90},target:{x:0,y:0,z:0},up:{x:0,y:0,z:1}},loading_progress_callback:a=>{if(m||!I||L)return;let h=a[B];r("loading-label").textContent=h?.phase==="loading-cad"?"Loading the CAD reader\u2026":h?.phase==="parsing-cad"?"Preparing CAD surfaces\u2026":h?.loaded&&!h.total?`Downloading your file\u2026 ${Se(h.loaded)}`:h?.total&&h.loaded<h.total?`Reading your file\u2026 ${Math.min(99,Math.floor(100*h.loaded/h.total))}%`:"Preparing your model\u2026"},model_loaded_callback:a=>{x=a},load_error_callback:a=>{le(/limit exceeded|safety budget/i.test(a)?`This scene or view mode exceeds the estimated memory budget for this device (${Se(u.budget)}). Remove a model, use Solid view without smoothing, or try a device with more memory.`:`Couldn\u2019t display this model. ${a}`)}}),await b.when_ready,m)throw new Error("ViewSTL app is disposed");return Y=_i(b.canvas,{getCamera:()=>b.get_camera_state(),getModel:Yt,getTool:()=>U,onRotate:(a,h)=>Je(a,h),onMove:(a,h)=>ze(a,h),onStart:()=>{Vt=r("scene-arrangement").value},onEnd:(a,h)=>{h&&(r("scene-arrangement").value=Vt),j(h?"Move cancelled.":"Model updated.")}}),p(b.canvas,"dblclick",a=>{if(I||a.button!==0)return;let h=b.pick_model(a.clientX,a.clientY);!h||!_.has(h.id)||(a.preventDefault(),qe(h.id),ue(),r("scene-panel").hidden=!1,r("scene-button").setAttribute("aria-expanded","true"),r("position-details").open=!0,Ae(),j(_.get(h.id).name+" selected. Choose Move, Rotate or Depth."))}),b}).catch(a=>{throw Y?.dispose(),Y=null,b?.dispose(),b=null,k=null,a})),k}function Ke(a){f();let h=[];for(let M of a){if(!/\.(stl|obj|3mf|step|stp|iges|igs|vsb|vsj)$/i.test(M.name)){le("Choose STL, OBJ, 3MF, STEP/STP, IGES/IGS, VSB or VSJ files.");continue}if(M.size===0){le("This file is empty. Choose a file containing a 3D model.");continue}h.push(M)}let w=h.filter(M=>be(M.name));if(w.length){let M={files:h.filter(F=>!be(F.name)),used:new Set,failed:!1};for(let F of w)E.push({name:F.name,sceneFile:F,batch:M});E.push({remaining:M})}else E.push(...h);return E.length&&!A&&(g=$t()),g||Promise.resolve()}function wt(a,h=""){if(f(),!e.url_import_endpoint)throw new Error("URL imports are not configured.");let w=new s(a.trim());if(!["https:","http:"].includes(w.protocol)||w.username||w.password)throw new Error("Use a public HTTP or HTTPS file URL without login credentials.");if(h&&!/^(stl|obj|3mf|step|stp|iges|igs|vsb|vsj)$/.test(h))throw new Error("Choose a supported 3D format.");w.hash="";let M=decodeURIComponent(w.pathname.split("/").at(-1)||"Remote model").slice(0,200);return E.push({name:M,remote:w.href,format:h,size:0,scene:["vsb","vsj"].includes(h)?h:be(M)}),A||(g=$t()),g||Promise.resolve()}r("url-form")&&r("url-dialog")&&p(r("url-form"),"submit",a=>{a.preventDefault();try{let h=wt(r("url-input").value,r("url-format").value);r("url-input").value="",r("url-error").hidden=!0,r("url-dialog").close(),h.catch(w=>le(w.message))}catch(h){r("url-error").textContent=h.message,r("url-error").hidden=!1}});async function $t(){if(!A){A=!0;try{for(;E.length;){let a=E.shift(),h=S;if(a.remaining){a.remaining.failed||E.unshift(...a.remaining.files.filter(w=>!a.remaining.used.has(w)));continue}He(!0),r("notice").hidden=!0,r("loading-label").textContent="Opening "+a.name+"\u2026",j("Opening "+a.name);try{if(a.sceneFile||a.scene){await Qi(a,h);continue}let w=await ni(a);if(h!==S)continue;let M=[..._.values()].reduce((V,pe)=>V+pe.estimated,0);if(w+M>u.budget||a.size>u.limits.max_file_bytes)throw Error(`Adding this file would exceed the estimated ${Se(u.budget)} scene memory budget. Remove a model or try a device with more memory.`);let F=await Qt();if(h!==S)continue;F.do_resize(),O=Math.max(O,F.max_model_id+1);let ee=O++;if(B=ee,x=null,F.add_model({id:ee,display:"flat",...a.remote?{remote_url:a.remote,remote_format:a.format}:a.url?{filename:a.url}:{local_file:a}}),await F.when_idle(),h!==S)continue;B=null;let ce=x===ee?F.get_model_info(ee):null;if(!ce){j(a.name+" could not be opened.");continue}a.remote&&(a.name=ce.name,a.size=ce.file_bytes,delete a.remote);let fe=ce.has_colors,D=fe?null:z[_.size%z.length];D&&F.set_color(ee,D),_.set(ee,{id:ee,name:a.name,size:a.size,units:ce.source_units||(/\.3mf$/i.test(a.name)?"mm":null),bounds:ce.bounds,position:{x:0,y:0,z:0},rotation:{x:0,y:0,z:0},embedded:fe,originalColor:D,color:D,estimated:ce.triangles*224,sourceFile:a instanceof o.File?a:null}),v=ee,P++,yt(),xe(),j(a.name+" is ready."),F.canvas.focus({preventScroll:!0})}catch(w){a.batch&&(a.batch.failed=!0),h===S&&(B!==null&&(b?.remove_model(B),B=null),le(/WebGL/i.test(w.message)?"3D viewing needs WebGL 2. Try enabling hardware acceleration in your browser, or use another device.":w.message))}}}finally{A=!1,He(!1)}}}async function Qi(a,h){let w=await Qt();if(h!==S)return;let M=new o.AbortController;L=M;try{let F=a.batch?.files||[],ee=[...new Set([..._.values()].map(D=>D.sourceFile).filter(Boolean))],ce=[...F,...a.sceneFile?ee.filter(D=>!F.some(V=>V.name===D.name)&&ee.filter(V=>V.name===D.name).length===1):[]],fe=await w.import_scene(a.sceneFile||a.remote,{format:a.scene||be(a.name),signal:M.signal,files:ce,on_progress(D){h!==S||m||(r("loading-label").textContent=D.phase==="opening-model"?`Opening scene model ${D.loaded+1} of ${D.total}\u2026`:D.phase==="complete"?"Restoring scene\u2026":D.total?`Reading scene\u2026 ${Math.min(99,Math.floor(D.loaded/D.total*100))}%`:"Reading scene\u2026")}});if(h!==S)return;for(let D of fe.used_files)a.batch?.used.add(D);for(let D of fe.model_ids){let V=w._models.get(D),pe=V.geometry,$i=Object.fromEntries(["min","max"].map(Ee=>[Ee,pe.bounds[Ee].map((to,ii)=>(to-V.center[ii])*V.scaleVector[ii])])),de={id:D,name:String(V.orig_filename||V.filename).split(/[\\/]/).pop().split(/[?#]/)[0],size:pe.file_bytes??V.local_file?.size??null,units:pe.units||V.units,bounds:$i,position:{x:0,y:0,z:0},rotation:{x:V.rotationx*180/Math.PI,y:V.rotationy*180/Math.PI,z:V.rotationz*180/Math.PI},embedded:!!pe.colors,color:V.color||(pe.colors?null:"#909090"),estimated:pe.triangles*224,sourceFile:fe.used_files.includes(V.local_file)?V.local_file:null},eo=Le(de);for(let Ee of["x","y","z"])de.position[Ee]=V[Ee]-eo[Ee];de.originalPosition={...de.position},de.originalRotation={...de.rotation},de.originalColor=de.color,_.set(D,de)}O=Math.max(O,w.max_model_id+1),v=fe.model_ids.at(-1),P++,r("scene-arrangement").value="custom",N=!!w.grid,Z=w.grid?.height||0,ke=w.grid?.spacing||1,r("grid-button").setAttribute("aria-pressed",String(N)),Et(w.bg_color),xe(),j(a.name+" opened. "+fe.model_ids.length+" models added."),w.canvas.focus({preventScroll:!0})}finally{L===M&&(L=null)}}t.querySelectorAll("[data-open-file]").forEach(a=>p(a,"click",()=>r("file-input").click())),p(r("file-input"),"change",()=>{let a=[...r("file-input").files];r("file-input").value="",Ke(a)}),r("sample-button").hidden=!_t,p(r("sample-button"),"click",()=>{_t&&Ke([{name:e.sample_name||"Sample.stl",url:_t}])}),p(r("close-model"),"click",()=>vt(v)),p(r("cancel-load"),"click",Xe),p(r("clear-scene"),"click",Kt),p(r("dismiss-notice"),"click",()=>{r("notice").hidden=!0}),p(r("fit-button"),"click",()=>b?.set_auto_zoom()),p(r("scene-arrangement"),"change",yt),p(r("model-list"),"click",a=>{let h=a.target.closest("button");h&&(h.dataset.removeModel!==void 0?vt(Number(h.dataset.removeModel)):h.dataset.selectModel!==void 0?qe(Number(h.dataset.selectModel)):h.dataset.colorModel!==void 0&&(qe(Number(h.dataset.colorModel)),Ge("colors")))});async function ei(){if(v===null||I)return;let a=++P,h=we.find(w=>w.checked).value;we.forEach(w=>{w.disabled=!0}),r("smooth-shading").disabled=!0,r("view-status").textContent="Preparing view\u2026",r("notice").hidden=!0;try{b.set_view(v,{display:h,smooth_shading:r("smooth-shading").checked}),await b.when_idle()}finally{a===P&&(Ht(),j(r("view-label").textContent+" view ready."))}}we.forEach(a=>p(a,"change",ei)),p(r("smooth-shading"),"change",ei);for(let a of y)p(r(a+"-button"),"click",()=>{a==="info"&&qt(),Ge(a),a==="section"&&!r("section-panel").hidden&&!r("section-enabled").checked&&(r("section-enabled").checked=!0,Re())}),p(r("close-"+a),"click",()=>{Gt(a),a==="colors"&&Ge("view"),r(a+"-button").focus()});function Re(){if(v===null)return;let a=r("section-enabled").checked;b.set_section(v,a?{axis:r("section-axis").value,position:Number(r("section-position").value)/100,flip:r("section-flip").getAttribute("aria-pressed")==="true"}:null),r("section-value").textContent=r("section-position").value+"%",r("section-button").setAttribute("aria-pressed",String(a))}p(r("section-enabled"),"change",Re),p(r("section-axis"),"change",Re),p(r("section-position"),"input",Re),p(r("section-flip"),"click",()=>{r("section-flip").setAttribute("aria-pressed",String(r("section-flip").getAttribute("aria-pressed")!=="true")),Re()}),p(r("grid-button"),"click",()=>{N=!N,We()});function xt(a){v!==null&&(b.set_color(v,a),_.get(v).color=a,gt(),Wt())}p(r("model-color"),"input",()=>xt(r("model-color").value)),p(r("restore-color"),"click",()=>xt(_.get(v)?.originalColor)),t.querySelectorAll("[data-model-color]").forEach(a=>p(a,"click",()=>xt(a.dataset.modelColor)));function Et(a){ge=a;let h=a==="transparent"?[255,255,255]:[1,3,5].map(w=>parseInt(a.slice(w,w+2),16));r("workspace").classList.toggle("dark-background",h[0]*.2126+h[1]*.7152+h[2]*.0722<115),b?.set_bg_color(a),gt()}p(r("background-color"),"input",()=>Et(r("background-color").value)),t.querySelectorAll("[data-background]").forEach(a=>p(a,"click",()=>Et(a.dataset.background))),p(r("image-button"),"click",async()=>{if(v===null)return;let a=_.size>1?"viewstl-scene.png":_.get(v).name.replace(/\.[^.]+$/,"")+".png";try{let h=await b.get_image_blob("image/png");if(m)return;let w=s.createObjectURL(h),M=i.createElement("a");M.href=w,M.download=a,M.click(),setTimeout(()=>s.revokeObjectURL(w),1e3),j("Image ready to download.")}catch{le("The image could not be saved. Please try again.")}});let Ze=0,Mt=a=>{let h=It(a.dataTransfer);return h==="files"?h:!e.url_import_endpoint||a.target?.isContentEditable||a.target?.closest?.("input, textarea")?null:h},Qe=()=>{Ze=0,r("drop-overlay").hidden=!0};p(c,"dragenter",a=>{Mt(a)&&(a.preventDefault(),Ze++,r("drop-overlay").hidden=!1)}),p(c,"dragover",a=>{Mt(a)&&(a.preventDefault(),a.dataTransfer.dropEffect="copy")}),p(c,"dragleave",a=>{Ze>0&&--Ze===0&&Qe()}),p(c,"drop",a=>{let h=Mt(a);if(Qe(),!!h){a.preventDefault();try{if(h==="files")Ke([...a.dataTransfer.files]).catch(w=>le(w.message));else for(let w of gi(a.dataTransfer))wt(w).catch(M=>le(M.message))}catch(w){le(w.message)}}}),p(c,"dragend",Qe),p(o,"blur",Qe),t.querySelectorAll("[data-dialog]").forEach(a=>p(a,"click",()=>r(a.dataset.dialog).showModal())),t.querySelectorAll("dialog").forEach(a=>p(a,"click",h=>{if(h.target===a){let w=a.getBoundingClientRect();(h.clientX<w.left||h.clientX>w.right||h.clientY<w.top||h.clientY>w.bottom)&&(a===r("save-dialog")?R.close():a.close())}})),p(t,"keydown",a=>{if(a.key!=="Escape"||r("save-dialog")?.open)return;if(W){G(!1),j("Move tool closed.");return}let h=y.find(w=>!r(w+"-panel").hidden);h&&(ue(),h==="colors"&&Ge("view"),r(h+"-button").focus())}),p(t,"pointerdown",a=>{a.target.closest(".view-panel,.info-panel,.toolbar,.scene-heading-button,.move-mode")||ue()}),r("memory-budget").textContent=`Scene memory budget: ${Se(u.budget)}. Half of ${u.reported?"the memory estimate reported by your browser":"a conservative 2 GiB estimate because this browser does not report device memory"} is kept in reserve. This is an estimate, not a guarantee of available memory.`,r("scene-arrangement").value="original",r("notice").hidden=!0,r("drop-overlay").hidden=!0,r("save-button")&&(R=Ii({$:r,listen:p,window:o,getState:()=>({busy:I,selectedId:v,models:[..._.values()]}),getViewer:()=>b,onOpen:()=>{G(!1),ue()},announce:j})),r("print-button")&&(T=Ci({$:r,listen:p,window:o,getState:()=>({busy:I,selectedId:v,models:[..._.values()]}),getViewer:()=>b,onOpen:()=>{G(!1),ue()},announce:j})),r("animation-details")&&(C=Pi({$:r,listen:p,window:o,getState:()=>({busy:I,selectedId:v,models:[..._.values()]}),getViewer:()=>b,beforePlay(a){G(!1),te(a),Li(_.get(a),b._models.get(a)),r("scene-arrangement").value="custom"},refreshPose:Zt,announce:j})),We(),He(!1),G(!1);let ti={openFiles:Ke,openURL:wt,cancelLoading(){f(),Xe()},clear(){f(),Kt()},select(a){f(),qe(a)},remove(a){f(),vt(a)},getState(){f();for(let a of _.values())he(a,b?._models.get(a.id));return{selectedId:v,busy:I,arrangement:r("scene-arrangement").value,memoryBudget:u.budget,models:[..._.values()].map(a=>({id:a.id,name:a.name,size:a.size,units:a.units,position:{...a.position},rotation:{...a.rotation},color:a.color,animation:b.get_model_animation_state(a.id)}))}},dispose(){m||(R?.dispose(),T?.dispose(),C?.dispose(),Xe(),G(!1),P++,m=!0,l.abort(),Y?.dispose(),b?.dispose(),_.clear(),e.id_prefix&&we.forEach(a=>a.name="view-mode"),Ut.delete(t))}};return Ut.set(t,ti),ti}var _o=globalThis.document?.currentScript;ve.workerURL=new URL("viewstl.worker.js",_o?.src||document.baseURI).href;globalThis.StlViewer=ve;globalThis.mountViewstlApp=Wi;})();
@@ -1 +1 @@
1
- import{a as E,c as A,d as R}from"./chunks/chunk-JH3H3JWA.js";import{a as b,b as I}from"./chunks/chunk-JS56KC3I.js";import"./chunks/chunk-U33KANV7.js";function S(t,e,o,r=0){if(t>o.max_triangles)throw new Error("Triangle limit exceeded");b(o,67108864+r+t*224);let f=new Float32Array(t*9),n=null,d=new TextDecoder().decode(e),s=d.indexOf("COLOR="),a=s>=0&&s+10<=80,l=/^Binary colored STL by viewstl\.com/i.test(d),c=a?[...e.subarray(s+6,s+9)].map(i=>i/255):[144/255,144/255,144/255];return{write(i,w,x){for(let m=0;m<9;m++)f[x*9+m]=i.getFloat32(w+12+m*4,!0);let y=i.getUint16(w+48,!0),L=a||l||!!(y&32768);if(L&&!n){n=new Float32Array(t*9);for(let m=0;m<n.length;m++)n[m]=c[m%3]}if(L){let m=a&&y&32768?c:(a||l?[y&31,y>>5&31,y>>10&31]:[y>>10&31,y>>5&31,y&31]).map(g=>g/31);for(let g=0;g<9;g++)n[x*9+g]=m[g%3]}},finish(){return{...A(f,n,o),format:"stl-binary"}}}}async function D(t,e=E,o=()=>{},r=65536){if(t.size>e.max_file_bytes)throw new Error("File size limit exceeded");if(!Number.isSafeInteger(r)||r<1)throw new Error("Invalid chunk size");if(t.size<84)return null;let f=await t.slice(0,84).arrayBuffer(),n=new DataView(f).getUint32(80,!0);if(!n||84+n*50!==t.size)return null;let d=S(n,new Uint8Array(f,0,80),e);for(let s=0;s<n;s+=r){let a=Math.min(n,s+r),l=await t.slice(84+s*50,84+a*50).arrayBuffer();if(l.byteLength!==(a-s)*50)throw new Error("Truncated binary STL");let c=new DataView(l);for(let i=s;i<a;i++)d.write(c,(i-s)*50,i);o(84+a*50,t.size)}return d.finish()}function V(t,e=E){if(!(t instanceof ArrayBuffer)||t.byteLength>e.max_file_bytes)throw new Error("Invalid or oversized STL");let o=new DataView(t),r=t.byteLength>=84?o.getUint32(80,!0):0,f=r>0&&84+r*50===t.byteLength,n,d=null;if(f){let s=S(r,new Uint8Array(t,0,80),e,t.byteLength);for(let a=0;a<r;a++)s.write(o,84+a*50,a);return s.finish()}else{b(e,67108864+t.byteLength*3);let s=new TextDecoder().decode(t);if(!/^\s*solid(?:\s|$)/i.test(s)||!/\bendsolid\b/i.test(s))throw new Error("Invalid or truncated STL");let a=/\bvertex\s+(\S+)\s+(\S+)\s+(\S+)/gi,l=0,c;for(;a.exec(s);){if(l++,l>e.max_triangles*3)throw new Error("Triangle limit exceeded");b(e,67108864+t.byteLength*3+l/3*224)}if(!l||l%3||(s.match(/\bendfacet\b/gi)||[]).length!==l/3)throw new Error("Incomplete ASCII STL triangles");n=new Float32Array(l*3),a.lastIndex=0;let i=0;for(;c=a.exec(s);)for(let w=1;w<=3;w++)n[i++]=Number(c[w])}return{...A(n,d,e),format:f?"stl-binary":"stl-ascii"}}function C(t,e=E){if(t.byteLength>e.max_file_bytes)throw new Error("File size limit exceeded");b(e,67108864+t.byteLength*3);let o=new TextDecoder().decode(t),r=[],f=[];for(let[d]of o.matchAll(/[^\r\n]+/g)){let s=d.trim().split(/\s+/);if(!(!s[0]||s[0].startsWith("#"))){if(s[0]==="v"){if(s.length<4)throw new Error("Invalid OBJ vertex");let a=s.slice(1,4).map(Number);if(a.some(l=>!Number.isFinite(l)))throw new Error("Invalid OBJ coordinate");if(r.push(a),b(e,67108864+t.byteLength*3+r.length*128+f.length/3*320),r.length>e.max_triangles*3)throw new Error("Vertex limit exceeded")}else if(s[0]==="f"){let a=s.findIndex(c=>c.startsWith("#"));if(a>=0&&(s.length=a),s.length<4)throw new Error("Invalid OBJ face");let l=s.slice(1).map(c=>{let i=Number(c.split("/")[0]),w=i<0?r.length+i:i-1;if(!Number.isInteger(i)||i===0||w<0||w>=r.length)throw new Error("Invalid OBJ index");return w});for(let c=1;c<l.length-1;c++)if(f.push(l[0],l[c],l[c+1]),b(e,67108864+t.byteLength*3+r.length*128+f.length/3*320),f.length/3>e.max_triangles)throw new Error("Triangle limit exceeded")}}}let n=new Float32Array(f.length*3);return f.forEach((d,s)=>n.set(r[d],s*3)),{...A(n,null,e),format:"obj"}}function v(t,e="",o=E){return/\.obj(?:[?#]|$)/i.test(e)?C(t,o):V(t,o)}async function j(t,e,o=()=>{},r){let f,n=0;if(t instanceof ArrayBuffer){if(t.byteLength>e)throw new Error("File size limit exceeded");return o(t.byteLength,t.byteLength),t}if(t instanceof Blob){if(n=t.size,n>e)throw new Error("File size limit exceeded");if(r?.aborted)throw new DOMException("Loading cancelled","AbortError");o(0,n);let i=await t.arrayBuffer();if(r?.aborted)throw new DOMException("Loading cancelled","AbortError");return o(n,n),i}else{let i=new URL(String(t),globalThis.location?.href);if(!["http:","https:","blob:"].includes(i.protocol))throw new Error("Unsupported file URL protocol");let w=await fetch(i,{signal:r,credentials:"same-origin"});if(!w.ok)throw new Error(`HTTP ${w.status}: ${i.pathname}`);n=Number(w.headers.get("content-length"))||0,f=w.body}if(n>e)throw new Error("File size limit exceeded");if(!f)throw new Error("Empty response body");let d=f.getReader(),s=[],a=0;try{for(;;){if(r?.aborted)throw new DOMException("Loading cancelled","AbortError");let{done:i,value:w}=await d.read();if(i)break;if(a+=w.length,a>e)throw new Error("File size limit exceeded");s.push(w),o(a,Math.max(n,a))}}catch(i){throw await d.cancel().catch(()=>{}),i}finally{d.releaseLock()}let l=new Uint8Array(a),c=0;for(let i of s)l.set(i,c),c+=i.length;return o(a,a),l.buffer}var G=/^(stl|obj|3mf|step|stp|iges|igs|vsb|vsj)$/;async function N(t,e=()=>{}){let o=await fetch(t.relay,{method:"POST",credentials:"same-origin",cache:"no-store",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:t.remote,format:t.format||"",max_bytes:t.limits.max_file_bytes})});if(!o.ok){let i=await o.json().catch(()=>null);throw new Error(typeof i?.error=="string"?i.error:`URL import failed (HTTP ${o.status}).`)}let r=o.headers.get("x-viewstl-format")||"";if(!G.test(r)||!o.body)throw await o.body?.cancel(),new Error("The URL relay returned an invalid response.");let f;try{f=decodeURIComponent(o.headers.get("x-viewstl-filename")||"")}catch{}f=f?.replace(/[\x00-\x1f\x7f/\\]/g,"").slice(0,200)||`download.${r}`;let n=Number(o.headers.get("content-length"))||0,d=t.limits,a=/^(step|stp|iges|igs)$/.test(r)?Math.min(d.max_file_bytes,Math.max(0,Math.floor(((d.max_memory_bytes??Number.MAX_SAFE_INTEGER)-128*1024**2)/40))):d.max_file_bytes;if(n>a)throw await o.body.cancel(),new Error("File size limit exceeded for this device.");let l;try{l=await J(o.body,{...d,max_file_bytes:a},r,n,e)}catch(i){throw i instanceof TypeError?new Error("The download stopped before it completed. Try again or open a local copy."):i}let c;try{c=decodeURIComponent(o.headers.get("x-viewstl-base-url")||"")}catch{}return{...l,name:f,format:r,base:c}}async function J(t,e,o,r,f=()=>{}){let n=t.getReader(),d=0,s=o!=="stl",a=null,l=0,c=0,i=[],w=0,x=new Uint8Array(0),y=84,L=[],m=null,g=0,O=h=>{if(b(e,67108864+Math.max(d,r)*3,"remote file"),r){if(m??=new Uint8Array(r),g+h.length>r)throw new Error("The download length changed.");m.set(h,g),g+=h.length}else L.push(h)},U=h=>{if(y){let p=Math.min(y,h.length);h=h.subarray(p),y-=p}if(x.length){let p=new Uint8Array(x.length+h.length);p.set(x),p.set(h,x.length),h=p}let _=h.length-h.length%50,T=new DataView(h.buffer,h.byteOffset,_);for(let p=0;p<_;p+=50){if(84+(l+1)*50>c)throw new Error("The STL download contains extra data.");a.write(T,p,l++)}x=h.slice(_)};try{for(;;){let{value:h,done:_}=await n.read();if(_)break;if(d+=h.length,d>e.max_file_bytes)throw new Error("File size limit exceeded.");if(s)a?U(h):O(h);else if(i.push(h),w+=h.length,w>=84){let T=new Uint8Array(84),p=0;for(let M of i){let B=M.subarray(0,84-p);if(T.set(B,p),p+=B.length,p===84)break}let z=new DataView(T.buffer).getUint32(80,!0);c=84+z*50,z&&(r?c===r:c<=e.max_file_bytes)&&(a=S(z,T.subarray(0,80),e)),s=!0;for(let M of i)a?U(M):O(M);i=[]}f(d,r)}if(!d||r&&d!==r)throw new Error("The download is empty or incomplete.");if(a){if(d!==c||x.length||l*50+84!==c)throw new Error("The STL download is incomplete.");return{geometry:a.finish(),size:d}}for(let h of i)O(h);if(!m){m=new Uint8Array(d);for(let h of L)m.set(h,g),g+=h.length}return{buffer:m.buffer,size:d}}catch(h){throw await n.cancel().catch(()=>{}),h}finally{n.releaseLock()}}function $(t,e=new Set){if(t instanceof ArrayBuffer)e.add(t);else if(ArrayBuffer.isView(t))e.add(t.buffer);else if(t&&typeof t=="object")for(let o of Object.values(t))$(o,e);return[...e]}self.onmessage=async({data:{id:t,task:e}})=>{try{let o=e.limits||E,r;if(e.kind==="derive")b(o,I(e.positions.length/9,e.features),"view mode"),r=R(e.positions,e.features);else if(e.kind==="print-check")r=(await import("./chunks/print-check-2PU7CSL3.js")).checkMesh(e.model,e.options,o,f=>self.postMessage({id:t,progress:f}));else if(e.kind==="export")r=await(await import("./chunks/export-7OP4ZGGJ.js")).exportGeometry(e.models,e.options,o,f=>self.postMessage({id:t,progress:f}));else if(e.kind==="export-scene")r=(await import("./chunks/scene-export-KQBS7VRJ.js")).exportSceneArchive(e.scene,e.models,o,f=>self.postMessage({id:t,progress:f}));else if(e.kind==="zip")r=(await import("./chunks/archive-VGS4J4Z7.js")).zipScene(e.scene,o);else{let f=(c,i)=>self.postMessage({id:t,progress:{loaded:c,total:i}}),n=e.remote?await N(e,f):null,d=n?`download.${n.format}`:e.name;n?.geometry&&(r=n.geometry);let s=/\.(step|stp|iges|igs)(?:[?#]|$)/i.test(d),a=s?Math.min(o.max_file_bytes,Math.max(0,Math.floor(((o.max_memory_bytes??Number.MAX_SAFE_INTEGER)-128*1024**2)/40))):o.max_file_bytes;e.kind==="parse"&&e.source instanceof Blob&&/\.stl(?:[?#]|$)/i.test(e.name)&&(r=await D(e.source,o,f));let l=r?null:n?.buffer??await j(e.source,a,f);if(e.kind==="read-scene"){let c=n?.format||e.format;if(!["vsb","vsj"].includes(c))throw new Error("The URL does not contain a VSB or VSJ scene");r={...(await import("./chunks/scene-reader-APUJOPUR.js")).readSceneDocument(l,c,o),format:c,base:n?.base||e.remote||null}}else if(e.kind==="unzip"){let c=(await import("./chunks/archive-VGS4J4Z7.js")).unzipChecked(l,o);r=Object.fromEntries([...c].map(([i,w])=>[i,w.buffer]))}else r??=s?await(await import("./chunks/cad-VY42CMP4.js")).parseCAD(l,d,o,c=>self.postMessage({id:t,progress:{loaded:l.byteLength,total:l.byteLength,phase:c}})):/\.3mf(?:[?#]|$)/i.test(d)?(await import("./chunks/archive-VGS4J4Z7.js")).parse3MF(l,o):v(l,d,o),n&&Object.assign(r,{source_name:n.name,file_bytes:n.size}),e.features&&(b(o,I(r.triangles,e.features),"view mode"),Object.assign(r,R(r.positions,e.features)))}self.postMessage({id:t,result:r},$(r))}catch(o){self.postMessage({id:t,error:o instanceof RangeError?"The browser could not allocate enough memory for this model. Try closing other tabs or using a device with more memory.":o?.message||String(o)})}};
1
+ import{a as E,c as A,d as R}from"./chunks/chunk-OP2L7L4N.js";import{a as b,b as I}from"./chunks/chunk-W5KVLGVR.js";import"./chunks/chunk-IK3WL53C.js";function S(t,e,o,r=0){if(t>o.max_triangles)throw new Error("Triangle limit exceeded");b(o,67108864+r+t*224);let f=new Float32Array(t*9),n=null,d=new TextDecoder().decode(e),s=d.indexOf("COLOR="),a=s>=0&&s+10<=80,l=/^Binary colored STL by viewstl\.com/i.test(d),c=a?[...e.subarray(s+6,s+9)].map(i=>i/255):[144/255,144/255,144/255];return{write(i,w,x){for(let m=0;m<9;m++)f[x*9+m]=i.getFloat32(w+12+m*4,!0);let y=i.getUint16(w+48,!0),L=a||l||!!(y&32768);if(L&&!n){n=new Float32Array(t*9);for(let m=0;m<n.length;m++)n[m]=c[m%3]}if(L){let m=a&&y&32768?c:(a||l?[y&31,y>>5&31,y>>10&31]:[y>>10&31,y>>5&31,y&31]).map(g=>g/31);for(let g=0;g<9;g++)n[x*9+g]=m[g%3]}},finish(){return{...A(f,n,o),format:"stl-binary"}}}}async function D(t,e=E,o=()=>{},r=65536){if(t.size>e.max_file_bytes)throw new Error("File size limit exceeded");if(!Number.isSafeInteger(r)||r<1)throw new Error("Invalid chunk size");if(t.size<84)return null;let f=await t.slice(0,84).arrayBuffer(),n=new DataView(f).getUint32(80,!0);if(!n||84+n*50!==t.size)return null;let d=S(n,new Uint8Array(f,0,80),e);for(let s=0;s<n;s+=r){let a=Math.min(n,s+r),l=await t.slice(84+s*50,84+a*50).arrayBuffer();if(l.byteLength!==(a-s)*50)throw new Error("Truncated binary STL");let c=new DataView(l);for(let i=s;i<a;i++)d.write(c,(i-s)*50,i);o(84+a*50,t.size)}return d.finish()}function V(t,e=E){if(!(t instanceof ArrayBuffer)||t.byteLength>e.max_file_bytes)throw new Error("Invalid or oversized STL");let o=new DataView(t),r=t.byteLength>=84?o.getUint32(80,!0):0,f=r>0&&84+r*50===t.byteLength,n,d=null;if(f){let s=S(r,new Uint8Array(t,0,80),e,t.byteLength);for(let a=0;a<r;a++)s.write(o,84+a*50,a);return s.finish()}else{b(e,67108864+t.byteLength*3);let s=new TextDecoder().decode(t);if(!/^\s*solid(?:\s|$)/i.test(s)||!/\bendsolid\b/i.test(s))throw new Error("Invalid or truncated STL");let a=/\bvertex\s+(\S+)\s+(\S+)\s+(\S+)/gi,l=0,c;for(;a.exec(s);){if(l++,l>e.max_triangles*3)throw new Error("Triangle limit exceeded");b(e,67108864+t.byteLength*3+l/3*224)}if(!l||l%3||(s.match(/\bendfacet\b/gi)||[]).length!==l/3)throw new Error("Incomplete ASCII STL triangles");n=new Float32Array(l*3),a.lastIndex=0;let i=0;for(;c=a.exec(s);)for(let w=1;w<=3;w++)n[i++]=Number(c[w])}return{...A(n,d,e),format:f?"stl-binary":"stl-ascii"}}function C(t,e=E){if(t.byteLength>e.max_file_bytes)throw new Error("File size limit exceeded");b(e,67108864+t.byteLength*3);let o=new TextDecoder().decode(t),r=[],f=[];for(let[d]of o.matchAll(/[^\r\n]+/g)){let s=d.trim().split(/\s+/);if(!(!s[0]||s[0].startsWith("#"))){if(s[0]==="v"){if(s.length<4)throw new Error("Invalid OBJ vertex");let a=s.slice(1,4).map(Number);if(a.some(l=>!Number.isFinite(l)))throw new Error("Invalid OBJ coordinate");if(r.push(a),b(e,67108864+t.byteLength*3+r.length*128+f.length/3*320),r.length>e.max_triangles*3)throw new Error("Vertex limit exceeded")}else if(s[0]==="f"){let a=s.findIndex(c=>c.startsWith("#"));if(a>=0&&(s.length=a),s.length<4)throw new Error("Invalid OBJ face");let l=s.slice(1).map(c=>{let i=Number(c.split("/")[0]),w=i<0?r.length+i:i-1;if(!Number.isInteger(i)||i===0||w<0||w>=r.length)throw new Error("Invalid OBJ index");return w});for(let c=1;c<l.length-1;c++)if(f.push(l[0],l[c],l[c+1]),b(e,67108864+t.byteLength*3+r.length*128+f.length/3*320),f.length/3>e.max_triangles)throw new Error("Triangle limit exceeded")}}}let n=new Float32Array(f.length*3);return f.forEach((d,s)=>n.set(r[d],s*3)),{...A(n,null,e),format:"obj"}}function v(t,e="",o=E){return/\.obj(?:[?#]|$)/i.test(e)?C(t,o):V(t,o)}async function j(t,e,o=()=>{},r){let f,n=0;if(t instanceof ArrayBuffer){if(t.byteLength>e)throw new Error("File size limit exceeded");return o(t.byteLength,t.byteLength),t}if(t instanceof Blob){if(n=t.size,n>e)throw new Error("File size limit exceeded");if(r?.aborted)throw new DOMException("Loading cancelled","AbortError");o(0,n);let i=await t.arrayBuffer();if(r?.aborted)throw new DOMException("Loading cancelled","AbortError");return o(n,n),i}else{let i=new URL(String(t),globalThis.location?.href);if(!["http:","https:","blob:"].includes(i.protocol))throw new Error("Unsupported file URL protocol");let w=await fetch(i,{signal:r,credentials:"same-origin"});if(!w.ok)throw new Error(`HTTP ${w.status}: ${i.pathname}`);n=Number(w.headers.get("content-length"))||0,f=w.body}if(n>e)throw new Error("File size limit exceeded");if(!f)throw new Error("Empty response body");let d=f.getReader(),s=[],a=0;try{for(;;){if(r?.aborted)throw new DOMException("Loading cancelled","AbortError");let{done:i,value:w}=await d.read();if(i)break;if(a+=w.length,a>e)throw new Error("File size limit exceeded");s.push(w),o(a,Math.max(n,a))}}catch(i){throw await d.cancel().catch(()=>{}),i}finally{d.releaseLock()}let l=new Uint8Array(a),c=0;for(let i of s)l.set(i,c),c+=i.length;return o(a,a),l.buffer}var G=/^(stl|obj|3mf|step|stp|iges|igs|vsb|vsj)$/;async function N(t,e=()=>{}){let o=await fetch(t.relay,{method:"POST",credentials:"same-origin",cache:"no-store",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:t.remote,format:t.format||"",max_bytes:t.limits.max_file_bytes})});if(!o.ok){let i=await o.json().catch(()=>null);throw new Error(typeof i?.error=="string"?i.error:`URL import failed (HTTP ${o.status}).`)}let r=o.headers.get("x-viewstl-format")||"";if(!G.test(r)||!o.body)throw await o.body?.cancel(),new Error("The URL relay returned an invalid response.");let f;try{f=decodeURIComponent(o.headers.get("x-viewstl-filename")||"")}catch{}f=f?.replace(/[\x00-\x1f\x7f/\\]/g,"").slice(0,200)||`download.${r}`;let n=Number(o.headers.get("content-length"))||0,d=t.limits,a=/^(step|stp|iges|igs)$/.test(r)?Math.min(d.max_file_bytes,Math.max(0,Math.floor(((d.max_memory_bytes??Number.MAX_SAFE_INTEGER)-128*1024**2)/40))):d.max_file_bytes;if(n>a)throw await o.body.cancel(),new Error("File size limit exceeded for this device.");let l;try{l=await J(o.body,{...d,max_file_bytes:a},r,n,e)}catch(i){throw i instanceof TypeError?new Error("The download stopped before it completed. Try again or open a local copy."):i}let c;try{c=decodeURIComponent(o.headers.get("x-viewstl-base-url")||"")}catch{}return{...l,name:f,format:r,base:c}}async function J(t,e,o,r,f=()=>{}){let n=t.getReader(),d=0,s=o!=="stl",a=null,l=0,c=0,i=[],w=0,x=new Uint8Array(0),y=84,L=[],m=null,g=0,O=h=>{if(b(e,67108864+Math.max(d,r)*3,"remote file"),r){if(m??=new Uint8Array(r),g+h.length>r)throw new Error("The download length changed.");m.set(h,g),g+=h.length}else L.push(h)},U=h=>{if(y){let p=Math.min(y,h.length);h=h.subarray(p),y-=p}if(x.length){let p=new Uint8Array(x.length+h.length);p.set(x),p.set(h,x.length),h=p}let _=h.length-h.length%50,T=new DataView(h.buffer,h.byteOffset,_);for(let p=0;p<_;p+=50){if(84+(l+1)*50>c)throw new Error("The STL download contains extra data.");a.write(T,p,l++)}x=h.slice(_)};try{for(;;){let{value:h,done:_}=await n.read();if(_)break;if(d+=h.length,d>e.max_file_bytes)throw new Error("File size limit exceeded.");if(s)a?U(h):O(h);else if(i.push(h),w+=h.length,w>=84){let T=new Uint8Array(84),p=0;for(let M of i){let B=M.subarray(0,84-p);if(T.set(B,p),p+=B.length,p===84)break}let z=new DataView(T.buffer).getUint32(80,!0);c=84+z*50,z&&(r?c===r:c<=e.max_file_bytes)&&(a=S(z,T.subarray(0,80),e)),s=!0;for(let M of i)a?U(M):O(M);i=[]}f(d,r)}if(!d||r&&d!==r)throw new Error("The download is empty or incomplete.");if(a){if(d!==c||x.length||l*50+84!==c)throw new Error("The STL download is incomplete.");return{geometry:a.finish(),size:d}}for(let h of i)O(h);if(!m){m=new Uint8Array(d);for(let h of L)m.set(h,g),g+=h.length}return{buffer:m.buffer,size:d}}catch(h){throw await n.cancel().catch(()=>{}),h}finally{n.releaseLock()}}function $(t,e=new Set){if(t instanceof ArrayBuffer)e.add(t);else if(ArrayBuffer.isView(t))e.add(t.buffer);else if(t&&typeof t=="object")for(let o of Object.values(t))$(o,e);return[...e]}self.onmessage=async({data:{id:t,task:e}})=>{try{let o=e.limits||E,r;if(e.kind==="derive")b(o,I(e.positions.length/9,e.features),"view mode"),r=R(e.positions,e.features);else if(e.kind==="print-check")r=(await import("./chunks/print-check-NJ4IONC5.js")).checkMesh(e.model,e.options,o,f=>self.postMessage({id:t,progress:f}));else if(e.kind==="export")r=await(await import("./chunks/export-VM6OQCQJ.js")).exportGeometry(e.models,e.options,o,f=>self.postMessage({id:t,progress:f}));else if(e.kind==="export-scene")r=(await import("./chunks/scene-export-ZIXMUNLI.js")).exportSceneArchive(e.scene,e.models,o,f=>self.postMessage({id:t,progress:f}));else if(e.kind==="zip")r=(await import("./chunks/archive-JU75RPTU.js")).zipScene(e.scene,o);else{let f=(c,i)=>self.postMessage({id:t,progress:{loaded:c,total:i}}),n=e.remote?await N(e,f):null,d=n?`download.${n.format}`:e.name;n?.geometry&&(r=n.geometry);let s=/\.(step|stp|iges|igs)(?:[?#]|$)/i.test(d),a=s?Math.min(o.max_file_bytes,Math.max(0,Math.floor(((o.max_memory_bytes??Number.MAX_SAFE_INTEGER)-128*1024**2)/40))):o.max_file_bytes;e.kind==="parse"&&e.source instanceof Blob&&/\.stl(?:[?#]|$)/i.test(e.name)&&(r=await D(e.source,o,f));let l=r?null:n?.buffer??await j(e.source,a,f);if(e.kind==="read-scene"){let c=n?.format||e.format;if(!["vsb","vsj"].includes(c))throw new Error("The URL does not contain a VSB or VSJ scene");r={...(await import("./chunks/scene-reader-JGHERXR2.js")).readSceneDocument(l,c,o),format:c,base:n?.base||e.remote||null}}else if(e.kind==="unzip"){let c=(await import("./chunks/archive-JU75RPTU.js")).unzipChecked(l,o);r=Object.fromEntries([...c].map(([i,w])=>[i,w.buffer]))}else r??=s?await(await import("./chunks/cad-4JYHORYZ.js")).parseCAD(l,d,o,c=>self.postMessage({id:t,progress:{loaded:l.byteLength,total:l.byteLength,phase:c}})):/\.3mf(?:[?#]|$)/i.test(d)?(await import("./chunks/archive-JU75RPTU.js")).parse3MF(l,o):v(l,d,o),n&&Object.assign(r,{source_name:n.name,file_bytes:n.size}),e.features&&(b(o,I(r.triangles,e.features),"view mode"),Object.assign(r,R(r.positions,e.features)))}self.postMessage({id:t,result:r},$(r))}catch(o){self.postMessage({id:t,error:o instanceof RangeError?"The browser could not allocate enough memory for this model. Try closing other tabs or using a device with more memory.":o?.message||String(o)})}};
@@ -0,0 +1,46 @@
1
+ # ViewSTL v2.00 — 2.0.0-rc.5
2
+
3
+ This GitHub release brings the native JavaScript/WebGL 2 plugin to the repository's
4
+ default `main` branch. It includes the current browser viewer and Node.js analysis
5
+ API, model exports, print checks, animation and VSB/VSJ playback persistence.
6
+
7
+ ## Layout and legacy users
8
+
9
+ - v2 source is in `src/`; its complete distribution is in `build/`.
10
+ - The previous `master` branch and existing version tags remain unchanged.
11
+ - `legacy/v1/` preserves all 23 files from the pre-v2 GitHub tree, with a hash
12
+ manifest and the original README. The separate local v1.14 snapshot is retained
13
+ with its original provenance and 14-file manifest.
14
+ - Existing explicit `master` URLs continue to address the legacy files. The new
15
+ default branch, new clones and this release's downloads provide v2.
16
+ - Integrations that depend on Three.js internals or older rendering fallbacks need
17
+ migration. See [usage and compatibility](../v2-usage.md#existing-api-and-intentional-differences).
18
+
19
+ ## Downloads
20
+
21
+ - `viewstl-2.0.0-rc.5.zip`: ready-to-host `build/`, README, license, notices and docs.
22
+ Copy the whole build folder, including its worker, chunks and CAD assets.
23
+ - `omrips-viewstl-2.0.0-rc.5.tgz`: installable package for browser/Node applications.
24
+ Run `npm install ./omrips-viewstl-2.0.0-rc.5.tgz` after downloading.
25
+ - `SHA256SUMS`: checksums for both download archives.
26
+
27
+ This remains a release candidate. The npm distribution is also available with
28
+ `npm install viewstl`; `npm install @omrips/viewstl` receives the same plugin for
29
+ existing scoped users. Both names use the `latest` tag, so a version suffix is
30
+ optional. The original GitHub archives remain unchanged. The website, Go URL
31
+ relay and deployment infrastructure are not included in this repository update.
32
+
33
+ ## Animation and scenes
34
+
35
+ The plugin exposes start, pause, resume, stop and state callbacks for elapsed-time
36
+ model animation. VSB/VSJ preserve definitions, phase, paused/running state, origin
37
+ and pivot. VSB carries the meshes; VSJ requires its referenced files. Ordinary
38
+ model exports and PNG capture a pose, not animation playback.
39
+
40
+ ## Verification
41
+
42
+ The release layout is checked with unit tests, browser and Node TypeScript
43
+ consumers, both legacy manifests, actual browser harnesses and installation of
44
+ the packed distribution. Exact results and remaining coverage limits are recorded
45
+ in [verification](../v2-verification.md). Desktop Chromium checks do not establish
46
+ physical mobile, Safari or Firefox support.
@@ -1,6 +1,6 @@
1
1
  # Native WebGL2 plugin: compatibility requirements
2
2
 
3
- Status: v2 implemented; release candidate `2.0.0-rc.3`. This document records the original requirements;
3
+ Status: v2 implemented; release candidate `2.0.0-rc.5`. This document records the requirements, updated for the approved main-branch layout;
4
4
  see [usage and intentional differences](v2-usage.md) for the implemented
5
5
  contract and [verification](v2-verification.md) for actual test coverage.
6
6
 
@@ -16,10 +16,10 @@ var viewer = new StlViewer(container, {
16
16
  });
17
17
  ```
18
18
 
19
- - Develop on `codex/native-webgl2`.
20
- - Keep legacy source, builds and dependency files at their existing paths.
21
- - Put new implementation files under `src/v2/`.
22
- - Keep new artifacts under `build/v2/`.
19
+ - Develop v2 on `main` after release preparation on `codex/publish-plugin-v2`.
20
+ - Keep the legacy `master` branch and existing tags unchanged. Preserve its complete tree under `legacy/v1/` on `main`.
21
+ - Put new implementation files under `src/`.
22
+ - Keep new artifacts under `build/`.
23
23
  - Maintain classic script-tag usage and the global `StlViewer` entry point.
24
24
  An ES module entry point can be provided additionally.
25
25
  - Existing production site, DNS and hosting remain outside this preparation.