@omrips/viewstl 2.0.0-rc.5 → 2.0.0-rc.6
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.
- package/README.md +3 -3
- package/build/THIRD-PARTY-NOTICES.txt +1006 -1006
- package/build/cad/occt-import-js.source.txt +19 -19
- package/build/chunks/archive-QK7XZSVT.js +1 -0
- package/build/chunks/{cad-4JYHORYZ.js → cad-QGVO6UGR.js} +1 -1
- package/build/chunks/chunk-6AW5U52H.js +1 -0
- package/build/chunks/{chunk-Y6VY64LY.js → chunk-CVGQTUJ5.js} +2 -2
- package/build/chunks/chunk-FUXP2W57.js +1 -0
- package/build/chunks/chunk-LFTKNL2V.js +1 -0
- package/build/chunks/{chunk-PQYEV7WH.js → chunk-T6PPRDOE.js} +1 -1
- package/build/chunks/chunk-V6KYGP62.js +1 -0
- package/build/chunks/{export-VM6OQCQJ.js → export-2CFMTPJ3.js} +3 -3
- package/build/chunks/{export-cad-QXCB6UDU.js → export-cad-XOXIE726.js} +1 -1
- package/build/chunks/{export-zip-PVXETVLK.js → export-zip-I2ADLFJI.js} +1 -1
- package/build/chunks/measure-3LDALPCN.js +1 -0
- package/build/chunks/print-check-WWHTT5PW.js +1 -0
- package/build/chunks/{scene-export-ZIXMUNLI.js → scene-export-FPPL466I.js} +1 -1
- package/build/chunks/scene-reader-WXTRELY3.js +1 -0
- package/build/node/analysis-worker.js +2 -1
- package/build/stl_viewer.min.js +5 -5
- package/build/viewstl.cjs +1134 -392
- package/build/viewstl.d.cts +48 -0
- package/build/viewstl.d.ts +48 -0
- package/build/viewstl.js +1134 -392
- package/build/viewstl.min.js +5 -5
- package/build/viewstl.worker.js +1 -1
- package/docs/releases/2.0.0-rc.6.md +7 -0
- package/docs/v2-usage.md +24 -0
- package/docs/v2-verification.md +28 -0
- package/package.json +2 -2
- package/build/chunks/archive-JU75RPTU.js +0 -1
- package/build/chunks/chunk-IK3WL53C.js +0 -1
- package/build/chunks/chunk-OP2L7L4N.js +0 -1
- package/build/chunks/print-check-NJ4IONC5.js +0 -1
- package/build/chunks/scene-reader-JGHERXR2.js +0 -1
- package/docs/releases/2.0.0-rc.5.md +0 -46
package/build/viewstl.worker.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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)})}};
|
|
1
|
+
import{a as E,c as A,d as R}from"./chunks/chunk-FUXP2W57.js";import{a as b,b as I}from"./chunks/chunk-W5KVLGVR.js";import"./chunks/chunk-6AW5U52H.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 l=new Float32Array(t*9),n=null,d=new TextDecoder().decode(e),s=d.indexOf("COLOR="),a=s>=0&&s+10<=80,f=/^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++)l[x*9+m]=i.getFloat32(w+12+m*4,!0);let y=i.getUint16(w+48,!0),L=a||f||!!(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||f?[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(l,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 l=await t.slice(0,84).arrayBuffer(),n=new DataView(l).getUint32(80,!0);if(!n||84+n*50!==t.size)return null;let d=S(n,new Uint8Array(l,0,80),e);for(let s=0;s<n;s+=r){let a=Math.min(n,s+r),f=await t.slice(84+s*50,84+a*50).arrayBuffer();if(f.byteLength!==(a-s)*50)throw new Error("Truncated binary STL");let c=new DataView(f);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,l=r>0&&84+r*50===t.byteLength,n,d=null;if(l){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,f=0,c;for(;a.exec(s);){if(f++,f>e.max_triangles*3)throw new Error("Triangle limit exceeded");b(e,67108864+t.byteLength*3+f/3*224)}if(!f||f%3||(s.match(/\bendfacet\b/gi)||[]).length!==f/3)throw new Error("Incomplete ASCII STL triangles");n=new Float32Array(f*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:l?"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=[],l=[];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(f=>!Number.isFinite(f)))throw new Error("Invalid OBJ coordinate");if(r.push(a),b(e,67108864+t.byteLength*3+r.length*128+l.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 f=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<f.length-1;c++)if(l.push(f[0],f[c],f[c+1]),b(e,67108864+t.byteLength*3+r.length*128+l.length/3*320),l.length/3>e.max_triangles)throw new Error("Triangle limit exceeded")}}}let n=new Float32Array(l.length*3);return l.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 l,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,l=w.body}if(n>e)throw new Error("File size limit exceeded");if(!l)throw new Error("Empty response body");let d=l.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 f=new Uint8Array(a),c=0;for(let i of s)f.set(i,c),c+=i.length;return o(a,a),f.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 l;try{l=decodeURIComponent(o.headers.get("x-viewstl-filename")||"")}catch{}l=l?.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 f;try{f=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{...f,name:l,format:r,base:c}}async function J(t,e,o,r,l=()=>{}){let n=t.getReader(),d=0,s=o!=="stl",a=null,f=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+(f+1)*50>c)throw new Error("The STL download contains extra data.");a.write(T,p,f++)}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=[]}l(d,r)}if(!d||r&&d!==r)throw new Error("The download is empty or incomplete.");if(a){if(d!==c||x.length||f*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==="measure")r=(await import("./chunks/measure-3LDALPCN.js")).measure(e,l=>self.postMessage({id:t,progress:l}));else if(e.kind==="print-check")r=(await import("./chunks/print-check-WWHTT5PW.js")).checkMesh(e.model,e.options,o,l=>self.postMessage({id:t,progress:l}));else if(e.kind==="export")r=await(await import("./chunks/export-2CFMTPJ3.js")).exportGeometry(e.models,e.options,o,l=>self.postMessage({id:t,progress:l}));else if(e.kind==="export-scene")r=(await import("./chunks/scene-export-FPPL466I.js")).exportSceneArchive(e.scene,e.models,o,l=>self.postMessage({id:t,progress:l}));else if(e.kind==="zip")r=(await import("./chunks/archive-QK7XZSVT.js")).zipScene(e.scene,o);else{let l=(c,i)=>self.postMessage({id:t,progress:{loaded:c,total:i}}),n=e.remote?await N(e,l):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,l));let f=r?null:n?.buffer??await j(e.source,a,l);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-WXTRELY3.js")).readSceneDocument(f,c,o),format:c,base:n?.base||e.remote||null}}else if(e.kind==="unzip"){let c=(await import("./chunks/archive-QK7XZSVT.js")).unzipChecked(f,o);r=Object.fromEntries([...c].map(([i,w])=>[i,w.buffer]))}else r??=s?await(await import("./chunks/cad-QGVO6UGR.js")).parseCAD(f,d,o,c=>self.postMessage({id:t,progress:{loaded:f.byteLength,total:f.byteLength,phase:c}})):/\.3mf(?:[?#]|$)/i.test(d)?(await import("./chunks/archive-QK7XZSVT.js")).parse3MF(f,o):v(f,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,7 @@
|
|
|
1
|
+
# ViewSTL 2.0.0-rc.6
|
|
2
|
+
|
|
3
|
+
Adds center-preserving resizing, explicit file-unit correction, independent display units, model duplication, seven Z-up camera presets, named scene camera bookmarks, and on-demand surface area / validated volume measurements. Optional application controls are implemented in the plugin; the website remains static markup and styles plus its initializer.
|
|
4
|
+
|
|
5
|
+
VSB and VSJ retain size baselines, corrected units, display units, duplicate labels and bookmarks alongside existing animation state. Older scene manifests remain supported. Local models and measurements stay on the device. Legacy v1 files and releases remain unchanged.
|
|
6
|
+
|
|
7
|
+
New APIs: `get_model_size`, `resize_model`, `reset_model_size`, `set_file_units`, `duplicate_model`, `set_display_units`, `set_camera_view`, `get_camera_bookmarks`, `set_camera_bookmarks`, and `measure_model`. See the usage documentation for units and validation limits.
|
package/docs/v2-usage.md
CHANGED
|
@@ -949,3 +949,27 @@ Escape with focus inside the panel dismisses it. Reopening it from Info reveals
|
|
|
949
949
|
and focuses the existing panel. Loading or changing the checked selection cancels
|
|
950
950
|
an active check; stale results are cleared. Loading and empty scenes disable Run
|
|
951
951
|
and show the current availability without closing the panel.
|
|
952
|
+
|
|
953
|
+
## Model size, units, duplicates, cameras and measurements (rc.6)
|
|
954
|
+
|
|
955
|
+
All operations below are implemented inside the plugin. The optional site controls remain a thin HTML/CSS host.
|
|
956
|
+
|
|
957
|
+
`viewer.resize_model(id, [2, 2, 2])` doubles the model about its world-space bounds center, preserving rotation and placement. Positive finite XYZ factors are required; independent factors allow nonuniform scaling. It stops only that model's animation. `get_model_size(id)` returns dimensions and the reset baseline in millimetres. `reset_model_size(id)` restores that baseline while retaining file-unit corrections.
|
|
958
|
+
|
|
959
|
+
`viewer.set_file_units(id, 'inch')` reinterprets a unitless STL/OBJ mesh (default mm) without moving its center. Switching back reverses the conversion. Explicit 3MF/STEP/IGES units are normalized to mm during parsing and cannot be reinterpreted. `set_display_units('inch')` changes the optional app's size/Info readouts only; scene coordinates, position controls and the grid remain in mm. Existing `set_model_units` and `set_scale` retain their legacy semantics.
|
|
960
|
+
|
|
961
|
+
`const copy = viewer.duplicate_model(id)` returns a new ID at the same pose, with independent appearance, transforms and metadata. Immutable geometry and matching GPU buffers are shared; memory admission remains conservative. The source reference remains unchanged for VSJ, while `display_name` identifies the copy. Copied animation definitions and phase are retained but paused; the original keeps playing.
|
|
962
|
+
|
|
963
|
+
`viewer.set_camera_view('top')` fits the scene from front/back/left/right/top/bottom/isometric using Z up and the existing perspective projection. It never rotates the models. Camera bookmarks are explicit data:
|
|
964
|
+
|
|
965
|
+
`viewer.set_camera_bookmarks([{name: 'Detail', camera: viewer.get_camera_state()}])`
|
|
966
|
+
|
|
967
|
+
Use `get_camera_bookmarks()` to obtain a defensive copy and `set_camera_state(bookmark.camera)` to restore one. Up to 32 uniquely named views (80 characters per name) are supported. They are kept in memory and in scene exports, not localStorage.
|
|
968
|
+
|
|
969
|
+
`const {area_mm2, volume_mm3, volume_valid, volume_reason} = await viewer.measure_model(id)`
|
|
970
|
+
|
|
971
|
+
Measurements use workers and support `signal`, `max_time_ms` (default 30 seconds) and `on_progress`. Area includes all triangles at current scale. Volume is nullable: detailed topology, intersections and shell-orientation checks must establish a closed volume. An open/invalid mesh or an inconclusive/time-limited check never becomes a plausible-looking volume. Results are cached by mesh and scale; position/rotation do not invalidate them and uniform resizing updates cached measurements algebraically. Nonuniform area is recalculated in the worker. Memory limits can reject the calculation before buffers are copied. This is a bounded mesh analysis, not a guarantee of printability.
|
|
972
|
+
|
|
973
|
+
VSB/VSJ retain size baselines, file-unit interpretation, display units, duplicate labels and bookmarks. VSB still embeds geometry; VSJ still needs the referenced source files. No second unit conversion occurs during import. Appending scenes merges bookmark names with numbered suffixes; a combined total above 32 rejects the incoming scene. Old files without the new optional fields continue to load. The `size_state.unit_factor` serialization field preserves source-unit correction separately from resize factors for exports with `apply_transforms:false`.
|
|
974
|
+
|
|
975
|
+
The optional app control group is listed in `src/app-tools.js` (`toolsControls`). Provide the whole group or omit it when embedding older markup. The preview's `site/index.html` is a complete example. Models contains collapsed Size & units and Duplicate; View contains collapsed Camera & saved views; Info contains area and the volume calculation. No server is involved for local files or measurements.
|
package/docs/v2-verification.md
CHANGED
|
@@ -3,6 +3,26 @@
|
|
|
3
3
|
This file records dated checks. Older sections describe the alpha at the time
|
|
4
4
|
of each check; release-candidate results are recorded separately below.
|
|
5
5
|
|
|
6
|
+
## npm names and installation — 2026-11-07
|
|
7
|
+
|
|
8
|
+
Published `viewstl@2.0.0-rc.5` and `@omrips/viewstl@2.0.0-rc.5` with `latest`
|
|
9
|
+
pointing to rc.5. The existing scoped `next` tag was also advanced to rc.5.
|
|
10
|
+
Both READMEs document `npm install viewstl` and retain the scoped alternative.
|
|
11
|
+
All existing scoped versions remain available.
|
|
12
|
+
|
|
13
|
+
All 136 unit tests, strict browser/Node type checks and 37 legacy integrity
|
|
14
|
+
checks pass again. Both packed distributions pass Node ESM/CommonJS type checks
|
|
15
|
+
and runtime checks for browser exports, worker/CAD assets and STL/OBJ/3MF/STEP/IGES
|
|
16
|
+
analysis. Fresh public-registry installs without a version suffix pass those
|
|
17
|
+
runtime checks for both package names. Public tarball bytes and SHA-512 integrity
|
|
18
|
+
match the tested local archives; each package contains 40 files.
|
|
19
|
+
|
|
20
|
+
All 31 files under `build/` match the previously tested GitHub rc.5 distribution
|
|
21
|
+
byte for byte. No renderer changes were made and browser/device tests were not
|
|
22
|
+
rerun for this packaging update. The GitHub browser evidence below still applies
|
|
23
|
+
to those exact runtime bytes. Evidence: local `test-results/npm-stage.json` and
|
|
24
|
+
its referenced staging directory. See [maintainer workflow](npm-releases.md).
|
|
25
|
+
|
|
6
26
|
## GitHub root-layout release 2.0.0-rc.5 — 2026-11-07
|
|
7
27
|
|
|
8
28
|
The release worktree places the v2 sources/distribution in `src/` and `build/`.
|
|
@@ -627,3 +647,11 @@ explicit close from an empty scene. The panel remains open throughout unrelated
|
|
|
627
647
|
activity while stale reports and active jobs are handled safely. All 112 Node
|
|
628
648
|
tests, strict types and 14 legacy hashes pass again. Evidence: sibling website
|
|
629
649
|
`results/print-persistent/` and `results/deploy-20261017-5b21747c4c8f/`.
|
|
650
|
+
|
|
651
|
+
## 2026-11-07 — rc.6 model tools
|
|
652
|
+
|
|
653
|
+
146 unit tests pass, as do strict browser/Node TypeScript checks and the 37-file legacy hash guard. Actual Windows Chrome 156 runs against the site built from the candidate npm tarball: 25 new model-tools integration checks, 25 animation regressions and 96 existing site regressions pass. New coverage includes locked/nonuniform resize, invalid input recovery, mm/inch separation, copy animation isolation, source references, shared geometry/GPU cleanup, camera presets, bookmark validation/merge, static and animated VSB/VSJ round trips, reset baselines, explicit 3MF/CAD units, and malformed-scene rollback. Surface area and enclosed volume use known closed/open fixtures; source-unit corrections also survive untransformed mesh export.
|
|
654
|
+
|
|
655
|
+
Manual desktop checks cover size inputs, keyboard Apply and on-demand measurements. Narrow widths are viewport emulation, not physical phone tests. No physical touch device, Safari/Firefox or broad large-CAD stress testing is claimed. The bounded detailed mesh checker may return an unavailable/inconclusive volume; source coordinates without units remain assumed mm until corrected.
|
|
656
|
+
|
|
657
|
+
Website evidence is retained outside its public build in `Viewstl/results/rc6/`; local-only reproducible browser harnesses are `site/qa/model-tools.html`, `animation.html` and `index.html`. Generated assets and the whole npm distribution are packaged together.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omrips/viewstl",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.6",
|
|
4
4
|
"description": "Local-first STL, OBJ, 3MF, STEP and IGES viewer with native WebGL2 and transferable worker geometry",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"docs/v2-usage.md",
|
|
63
63
|
"docs/v2-compatibility.md",
|
|
64
64
|
"docs/v2-verification.md",
|
|
65
|
-
"docs/releases/2.0.0-rc.
|
|
65
|
+
"docs/releases/2.0.0-rc.6.md",
|
|
66
66
|
"legacy/README.md",
|
|
67
67
|
"THIRD-PARTY-NOTICES.txt"
|
|
68
68
|
],
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a,b,c,d}from"./chunk-Y6VY64LY.js";import"./chunk-ZSV4PTIA.js";import"./chunk-OP2L7L4N.js";import"./chunk-W5KVLGVR.js";import"./chunk-IK3WL53C.js";export{d as parse3MF,a as safePath,b as unzipChecked,c as zipScene};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var f=Object.create;var s=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var i=Object.getOwnPropertyNames;var l=Object.getPrototypeOf,u=Object.prototype.hasOwnProperty;var x=(t,r)=>()=>{try{return r||t((r={exports:{}}).exports,r),r.exports}catch(o){throw r=0,o}};var h=(t,r,o,e)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of i(r))!u.call(t,n)&&n!==o&&s(t,n,{get:()=>r[n],enumerable:!(e=c(r,n))||e.enumerable});return t};var M=(t,r,o)=>(o=t!=null?f(l(t)):{},h(r||!t||!t.__esModule?s(o,"default",{value:t,enumerable:!0}):o,t));var A=(t,r)=>t.map((o,e)=>o-r[e]),y=(t,r)=>t.map(o=>o*r);var w=(t,r)=>[t[1]*r[2]-t[2]*r[1],t[2]*r[0]-t[0]*r[2],t[0]*r[1]-t[1]*r[0]],F=t=>y(t,1/(Math.hypot(...t)||1)),I=()=>new Float64Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);function d(t,r){let o=new Float64Array(16);for(let e=0;e<4;e++)for(let n=0;n<4;n++)for(let a=0;a<4;a++)o[e*4+n]+=t[a*4+n]*r[e*4+a];return o}function _(t,r,o=1){let e=t[3]*r[0]+t[7]*r[1]+t[11]*r[2]+t[15]*o;return[0,1,2].map(n=>(t[n]*r[0]+t[4+n]*r[1]+t[8+n]*r[2]+t[12+n]*o)/(o&&e?e:1))}export{x as a,M as b,A as c,w as d,F as e,I as f,d as g,_ as h};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as k}from"./chunk-W5KVLGVR.js";var z=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 O(e){if(typeof e!="string"||!/^#[\da-f]{6}$/i.test(e))throw new Error("Color must be #RRGGBB");return[1,3,5].map(i=>parseInt(e.slice(i,i+2),16)/255)}function U(e,i=null,I=z){if(!(e instanceof Float32Array)||!e.length||e.length%9)throw new Error("Geometry must contain complete triangles");if(e.length/9>I.max_triangles)throw new Error("Triangle limit exceeded");if(k(I,67108864+e.length/9*224),i&&(!(i instanceof Float32Array)||i.length!==e.length))throw new Error("Invalid vertex colors");let u={min:[1/0,1/0,1/0],max:[-1/0,-1/0,-1/0]};for(let r=0;r<e.length;r++){let f=e[r],t=r%3;if(!Number.isFinite(f))throw new Error("Non-finite vertex coordinate");if(u.min[t]=Math.min(u.min[t],f),u.max[t]=Math.max(u.max[t],f),i&&(!Number.isFinite(i[r])||i[r]<0||i[r]>1))throw new Error("Invalid vertex color")}let y=new Float32Array(e.length),b=new Float32Array(Math.ceil(e.length/9/64)*6),h=0,s=0,l=0;for(let r=0;r<e.length;r+=9){let f=e[r+3]-e[r],t=e[r+4]-e[r+1],c=e[r+5]-e[r+2],n=e[r+6]-e[r],o=e[r+7]-e[r+1],x=e[r+8]-e[r+2],w=t*x-c*o,M=c*n-f*x,g=f*o-t*n,E=Math.hypot(w,M,g);h+=E/2,s+=(e[r]*w+e[r+1]*M+e[r+2]*g)/6,E||l++;for(let a=0;a<9;a+=3)y[r+a]=w/(E||1),y[r+a+1]=M/(E||1),y[r+a+2]=g/(E||1);let m=Math.floor(r/9/64)*6;r/9%64===0&&b.set([1/0,1/0,1/0,-1/0,-1/0,-1/0],m);for(let a=0;a<9;a++){let d=a%3;b[m+d]=Math.min(b[m+d],e[r+a]),b[m+3+d]=Math.max(b[m+3+d],e[r+a])}}if(!Number.isFinite(h)||!Number.isFinite(s))throw new Error("Geometry magnitude exceeds numeric limits");return{positions:e,normals:y,colors:i,blocks:b,bounds:u,triangles:e.length/9,area:h,volume:Math.abs(s),degenerateTriangles:l}}function j(e,{smooth:i=!1,edges:I=!1,wire:u=!1,edgeAngle:y=1}={}){let b=new Map,h=new Uint32Array(e.length/3),s=[],l=[];for(let t=0;t<e.length;t+=3){let c=`${e[t]},${e[t+1]},${e[t+2]}`,n=b.get(c);n===void 0&&(n=s.length,b.set(c,n),s.push([e[t],e[t+1],e[t+2]]),l.push([0,0,0])),h[t/3]=n}let r=new Map;for(let t=0;t<e.length;t+=9){let c=e[t+3]-e[t],n=e[t+4]-e[t+1],o=e[t+5]-e[t+2],x=e[t+6]-e[t],w=e[t+7]-e[t+1],M=e[t+8]-e[t+2],g=[n*M-o*w,o*x-c*M,c*w-n*x],E=Math.hypot(...g)||1;for(let m=0;m<3;m++)if(i)for(let a=0;a<3;a++)l[h[t/3+m]][a]+=g[a];if(I||u)for(let m=0;m<3;m++){let a=h[t/3+m],d=h[t/3+(m+1)%3],A=a<d?`${a}:${d}`:`${d}:${a}`,F=g.map(v=>v/E),_=r.get(A);_?(_.count++,_.n.reduce((v,N,T)=>v+N*F[T],0)<Math.cos(y*Math.PI/180)&&(_.sharp=!0)):r.set(A,{a,b:d,n:F,count:1,sharp:!1})}}let f={};if(i){f.smoothNormals=new Float32Array(e.length);for(let t=0;t<h.length;t++){let c=l[h[t]],n=Math.hypot(...c)||1;f.smoothNormals.set(c.map(o=>o/n),t*3)}}for(let[t,c,n]of[[I,"edges",o=>o.count!==2||o.sharp],[u,"wire",()=>!0]])if(t){let o=[...r.values()].filter(n),x=new Float32Array(o.length*6);o.forEach((w,M)=>{x.set(s[w.a],M*6),x.set(s[w.b],M*6+3)}),f[c]=x,c==="edges"&&(f.edgeAngle=y)}return f}function D(e,i=[1,1,1],I=[0,0,0]){let u=new ArrayBuffer(84+e.triangles*50),y=new DataView(u),b=e.positions;new Uint8Array(u).set(new TextEncoder().encode("Binary STL by ViewSTL 2")),y.setUint32(80,e.triangles,!0);for(let h=0;h<e.triangles;h++){let s=84+h*50,l=Array.from({length:9},(n,o)=>(b[h*9+o]-I[o%3])*i[o%3]),r=[l[3]-l[0],l[4]-l[1],l[5]-l[2]],f=[l[6]-l[0],l[7]-l[1],l[8]-l[2]],t=[r[1]*f[2]-r[2]*f[1],r[2]*f[0]-r[0]*f[2],r[0]*f[1]-r[1]*f[0]],c=Math.hypot(...t)||1;for(let n=0;n<3;n++)y.setFloat32(s+n*4,t[n]/c,!0);for(let n=0;n<9;n++)y.setFloat32(s+12+n*4,l[n],!0);if(e.colors){let n=[0,1,2].map(o=>Math.round(e.colors[h*9+o]*31));y.setUint16(s+48,32768|n[0]<<10|n[1]<<5|n[2],!0)}}return u}export{z as a,O as b,U as c,j as d,D as e};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as re}from"./chunk-W5KVLGVR.js";import{h as oe}from"./chunk-IK3WL53C.js";function ae(e={}){let o=e.level??"basic";if(!["basic","detailed"].includes(o))throw new Error("Choose basic or detailed print checks");let l=(n,i,f=1e9)=>{if(!Number.isFinite(n)||n<=0||n>f)throw new Error(`Invalid ${i}`);return n},r=null;if(e.profile!=null){if(e.profile.technology!=="fdm")throw new Error("Only FDM profiles are supported");r={technology:"fdm"};for(let n of["min_wall_mm","line_width_mm"])e.profile[n]!=null&&(r[n]=l(e.profile[n],n));if(e.profile.overhang_degrees!=null&&(r.overhang_degrees=l(e.profile.overhang_degrees,"overhang angle",89.9)),e.profile.build_volume_mm!=null){if(!Array.isArray(e.profile.build_volume_mm)||e.profile.build_volume_mm.length!==3)throw new Error("Build volume requires X, Y and Z");r.build_volume_mm=e.profile.build_volume_mm.map(n=>l(n,"build volume"))}}let c=l(e.max_time_ms??3e4,"check time limit",12e4);return{level:o,profile:r,apply_transforms:e.apply_transforms!==!1,units_confirmed:e.units_confirmed===!0,max_time_ms:c}}var le=e=>64*1024**2+e*2400;var O=(e,o)=>e.map((l,r)=>l-o[r]),R=(e,o)=>e.reduce((l,r,c)=>l+r*o[c],0),C=(e,o)=>[e[1]*o[2]-e[2]*o[1],e[2]*o[0]-e[0]*o[2],e[0]*o[1]-e[1]*o[0]];function D(e,o){return[0,3,6].map(l=>Array.from(e.subarray(o*9+l,o*9+l+3)))}function fe(e,o){let l=e.length/9,r=new Float64Array(l*6);for(let i=0;i<l;i++){i%1024===0&&o();for(let f=0;f<3;f++)r[i*6+f]=Math.min(e[i*9+f],e[i*9+3+f],e[i*9+6+f]),r[i*6+f+3]=Math.max(e[i*9+f],e[i*9+3+f],e[i*9+6+f])}let c=Uint32Array.from({length:l},(i,f)=>f),n=(i,f)=>{o();let w=[1/0,1/0,1/0,-1/0,-1/0,-1/0];for(let u=i;u<f;u++)for(let m=0;m<3;m++)w[m]=Math.min(w[m],r[c[u]*6+m]),w[m+3]=Math.max(w[m+3],r[c[u]*6+m+3]);let _={b:w,start:i,end:f};if(f-i>16){let u=[0,1,2].map(z=>w[z+3]-w[z]),m=u.indexOf(Math.max(...u));c.subarray(i,f).sort((z,$)=>r[z*6+m]+r[z*6+m+3]-(r[$*6+m]+r[$*6+m+3]));let g=i+f>>>1;_.left=n(i,g),_.right=n(g,f)}return _};return{root:n(0,l),ids:c,boxes:r}}var ce=(e,o,l)=>[0,1,2].every(r=>e[r]<=o[r+3]+l&&e[r+3]+l>=o[r]);function ue(e,o,l,r){let c=(n,i,f)=>{if(r(),!!ce(n.b,i.b,o))if(!n.left&&!i.left)for(let w=n.start;w<n.end;w++)for(let _=i.start;_<i.end;_++){let u=e.ids[w],m=e.ids[_];f&&u>=m||ce(e.boxes.subarray(u*6,u*6+6),e.boxes.subarray(m*6,m*6+6),o)&&l(u,m)}else f?(c(n.left,n.left,!0),c(n.left,n.right,!1),c(n.right,n.right,!0)):n.left&&(!i.left||n.end-n.start>=i.end-i.start)?(c(n.left,i,!1),c(n.right,i,!1)):(c(n,i.left,!1),c(n,i.right,!1))};c(e.root,e.root,!0)}var G=(e,o,l)=>(o[0]-e[0])*(l[1]-e[1])-(o[1]-e[1])*(l[0]-e[0]);function me(e,o,l,r){let c=C(O(e[1],e[0]),O(e[2],e[0])),n=C(O(o[1],o[0]),O(o[2],o[0])),i=Math.hypot(...c),f=Math.hypot(...n);if(!i||!f)return 0;let w=o.map(d=>R(O(d,e[0]),c)/i),_=e.map(d=>R(O(d,o[0]),n)/f);if([w,_].some(d=>d.every(b=>b>r)||d.every(b=>b<-r)))return 0;let u=C(c.map(d=>d/i),n.map(d=>d/f));if(Math.hypot(...u)<1e-10){if(Math.max(...w.map(Math.abs))>r)return 0;let d=c.map(Math.abs).indexOf(Math.max(...c.map(Math.abs))),b=A=>A.filter((S,I)=>I!==d);if(l>=2){let A=(I,k)=>I.every((P,p)=>P===k[p]),S=e.filter(I=>o.some(k=>A(I,k)));if(S.length===3)return 1;if(S.length===2){let I=e.find(P=>!S.some(p=>A(P,p))),k=o.find(P=>!S.some(p=>A(P,p)));return G(b(S[0]),b(S[1]),b(I))*G(b(S[0]),b(S[1]),b(k))>0?1:0}}let E=e.map(b),q=o.map(b),B=Math.sign(G(...q));for(let A=0;A<3&&E.length;A++){let S=q[A],I=q[(A+1)%3],k=[];for(let P=0;P<E.length;P++){let p=E[P],j=E[(P+1)%E.length],x=B*G(S,I,p),Q=B*G(S,I,j);if(x>=0&&k.push(p),x>=0!=Q>=0){let K=x/(x-Q);k.push(p.map((Z,W)=>Z+(j[W]-Z)*K))}}E=k}return Math.abs(E.reduce((A,S,I)=>{let k=E[(I+1)%E.length],P=E[0];return A+(S[0]-P[0])*(k[1]-P[1])-(S[1]-P[1])*(k[0]-P[0])},0))/2>r*r*4?1:0}if(l>=2)return 0;let m=u.map(d=>d/Math.hypot(...u)),g=(d,b)=>{let E=[];for(let q=0;q<3;q++){Math.abs(b[q])<=r&&E.push(R(d[q],m));let B=(q+1)%3;if(b[q]*b[B]<0){let V=b[q]/(b[q]-b[B]);E.push(R(d[q].map((A,S)=>A+(d[B][S]-A)*V),m))}}return E.length?[Math.min(...E),Math.max(...E)]:null},z=g(e,_),$=g(o,w);if(!z||!$)return 0;let L=Math.min(z[1],$[1])-Math.max(z[0],$[0]);return L>r?1:l?0:L>=-r?2:0}function ye(e,o,l,r){let c=O(l[1],l[0]),n=O(l[2],l[0]),i=C(o,n),f=R(c,i);if(Math.abs(f)<=Math.hypot(...c)*Math.hypot(...n)*1e-12)return null;let w=O(e,l[0]),_=R(w,i)/f,u=C(w,c),m=R(o,u)/f,g=R(n,u)/f;return _<-1e-10||m<-1e-10||_+m>1+1e-10||g<=r?null:{t:g,edge:Math.min(_,m,1-_-m)<1e-8}}function ve(e,o,l,r){let c=0,n=1/0;for(let i=0;i<3;i++)if(Math.abs(o[i])<1e-15){if(e[i]<l[i]-r||e[i]>l[i+3]+r)return!1}else{let f=(l[i]-r-e[i])/o[i],w=(l[i+3]+r-e[i])/o[i];if(c=Math.max(c,Math.min(f,w)),n=Math.min(n,Math.max(f,w)),c>n)return!1}return!0}function ie(e,o,l,r,c,n,i){let f=[],w=_=>{if(i(),!!ve(l,r,_.b,c))if(_.left)w(_.left),w(_.right);else for(let u=_.start;u<_.end;u++){let m=e.ids[u];if(!n(m))continue;let g=ye(l,r,D(o,m),c);g&&f.push({...g,id:m})}};return w(e.root),f}var we=[["degenerate_faces","Degenerate triangles","error"],["near_degenerate_faces","Very small or thin triangles","warning"],["duplicate_faces","Duplicate triangles","error"],["boundary_edges","Open boundaries","error"],["nonmanifold_edges","Non-manifold edges","error"],["nonmanifold_vertices","Non-manifold vertices","error"],["winding","Triangle winding","error"],["components","Separate connected parts","info"],["intersections","Self-intersections","error"],["shell_orientation","Shell orientation and volume","error"],["units","Physical units","warning"],["build_volume","Build volume fit","warning"],["overhangs","Overhangs","warning"],["bed_contact","Bed contact","warning"],["thin_walls","Thin walls / small features","warning"]];function Se(e,o={},l={},r=()=>{}){let c=ae(o),n=e.positions.length/9;if(!(e.positions instanceof Float32Array)||!Number.isSafeInteger(n)||n<1||e.matrix?.length!==16||Array.from(e.matrix).some(p=>!Number.isFinite(p)))throw new Error("Invalid print-check geometry");if(n>(l.max_triangles??5e6))throw new Error("Print check triangle limit exceeded");re(l,le(n),"print check");let i=performance.now(),f=i+c.max_time_ms,w=0,_="Topology",u=()=>{if(++w%128===0){if(performance.now()>f)throw new Error("PRINT_TIME_LIMIT");r({phase:_,elapsed_ms:performance.now()-i})}},m=we.map(([p,j,x])=>({code:p,label:j,severity:x,status:"not_run",count:0,triangle_ids:[],edge_vertices:[],message:"Not run"})),g=p=>m.find(j=>j.code===p),z=(p,j,x)=>Object.assign(g(p),{status:j,message:x}),$=(p,j)=>{let x=g(p);x.count++,j!=null&&x.triangle_ids.length<128&&x.triangle_ids.push(j)},L=(p,j)=>{let x=g(p);x.status=x.count?"issue":"pass",x.message=j||`${x.count} found`},d=new Float64Array(e.positions.length),b=[1/0,1/0,1/0],E=[-1/0,-1/0,-1/0],q=!1,B,V=[],A=[],S,I=0,k={model_id:e.id,name:e.name,triangles:n,checks:m,settings:c,dimensions_mm:null,volume_mm3:null,volume_valid:!1,elapsed_ms:0,summary:"incomplete"};try{r({phase:_,elapsed_ms:0});for(let t=0;t<e.positions.length;t+=3){t%3072===0&&u();let s=oe(e.matrix,Array.from(e.positions.subarray(t,t+3)));if(s.some(a=>!Number.isFinite(a)))throw new Error("Invalid print-check coordinate");d.set(s,t);for(let a=0;a<3;a++)b[a]=Math.min(b[a],s[a]),E[a]=Math.max(E[a],s[a])}k.dimensions_mm=E.map((t,s)=>t-b[s]);let p=Math.max(...k.dimensions_mm),j=Math.max(...b.map(Math.abs),...E.map(Math.abs));I=Math.max(p*1e-10,j*Number.EPSILON*64,1e-12),k.tolerance_mm=I;for(let t=0;t<d.length;t++)d[t]-=b[t%3];let x=new Uint32Array(n*3),Q=[],K=new Map,Z=new Map,W=new Map,H=[],J=Int32Array.from({length:n},(t,s)=>s),te=t=>{for(;J[t]!==t;)J[t]=J[J[t]],t=J[t];return t},he=(t,s)=>{t=te(t),s=te(s),t!==s&&(J[s]=t)};for(let t=0;t<n;t++){u();for(let h=0;h<3;h++){let M=t*9+h*3,F=Array.from(e.positions.subarray(M,M+3)).join(","),T=K.get(F);T===void 0&&(T=Q.length,K.set(F,T),Q.push(M),H.push([])),x[t*3+h]=T,H[T].push(t)}let s=D(d,t),a=C(O(s[1],s[0]),O(s[2],s[0])),y=Math.hypot(...a);y?y<=I*Math.max(...s.map((h,M)=>Math.hypot(...O(h,s[(M+1)%3]))))&&$("near_degenerate_faces",t):$("degenerate_faces",t);let v=Array.from(x.subarray(t*3,t*3+3)).sort((h,M)=>h-M).join(":");W.has(v)?$("duplicate_faces",t):W.set(v,t);for(let h=0;h<3;h++){let M=x[t*3+h],F=x[t*3+(h+1)%3],T=Math.min(M,F)+":"+Math.max(M,F),N=Z.get(T);N||(N={a:M,b:F,faces:[],direction:0},Z.set(T,N)),N.faces.length&&he(t,N.faces[0]),N.faces.push(t),N.direction+=M<F?1:-1}}let ne=new Set;for(let t of Z.values()){u();let s;if(t.faces.length===1?s="boundary_edges":t.faces.length!==2?(s="nonmanifold_edges",ne.add(t.a),ne.add(t.b)):t.direction!==0&&(s="winding"),s){$(s,t.faces[0]);let a=g(s);a.edge_vertices.length<256*6&&a.edge_vertices.push(...e.positions.subarray(Q[t.a],Q[t.a]+3),...e.positions.subarray(Q[t.b],Q[t.b]+3))}}for(let t=0;t<H.length;t++){if(u(),ne.has(t)){$("nonmanifold_vertices",H[t][0]);continue}let s=new Set(H[t]),a=new Set,y=[H[t][0]];for(;y.length;){u();let v=y.pop();if(!a.has(v)){a.add(v);for(let h=0;h<3;h++){let M=x[v*3+h],F=x[v*3+(h+1)%3];if(M!==t&&F!==t)continue;let T=Z.get(Math.min(M,F)+":"+Math.max(M,F));for(let N of T.faces)a.has(N)||y.push(N)}}}a.size!==s.size&&$("nonmanifold_vertices",H[t][0])}let X=new Map;S=new Int32Array(n);for(let t=0;t<n;t++){u();let s=te(t);X.has(s)||(X.set(s,X.size),A.push([]),V.push(0));let a=X.get(s);S[t]=a,A[a].push(t);let y=D(d,t),v=D(d,A[a][0])[0];V[a]+=R(O(y[0],v),C(O(y[1],v),O(y[2],v)))/6}for(let t of["degenerate_faces","near_degenerate_faces","duplicate_faces","boundary_edges","nonmanifold_edges","nonmanifold_vertices","winding"])L(t);g("components").count=X.size,z("components",X.size>1?"issue":"pass",`${X.size} connected part${X.size===1?"":"s"}. Separate parts can be intentional.`),q=!0,z("units",e.source_units||c.units_confirmed?"pass":"inconclusive",e.source_units?`File units: ${e.source_units}; measurements in mm.`:c.units_confirmed?"Current scale confirmed as millimetres.":"File has no physical units. Confirm the current millimetre scale before using size estimates.");let Y=c.profile;Y?.build_volume_mm&&(g("build_volume").count=k.dimensions_mm.filter((t,s)=>t>Y.build_volume_mm[s]+I).length,L("build_volume",g("build_volume").count?"Exceeds the supplied build volume in the current orientation. Try another orientation or scale in your slicer.":"Fits by dimensions only in the current orientation; placement and printer exclusions are not checked."));let se=m.filter(t=>t.severity==="error"&&t.status!=="not_run").every(t=>t.status==="pass");if(c.level==="detailed"){_="Spatial index",B=fe(d,u),_="Self-intersections";let t=0;if(ue(B,I,(s,a)=>{u();let y=Array.from(x.subarray(s*3,s*3+3)).filter(h=>x.subarray(a*3,a*3+3).includes(h)).length,v=me(D(d,s),D(d,a),y,I);if(v===1){$("intersections",s);let h=g("intersections");h.triangle_ids.length<128&&h.triangle_ids.push(a)}else v===2&&t++},u),L("intersections"),t&&!g("intersections").count&&z("intersections","inconclusive",`${t} near-contact pairs require inspection at the reported numerical tolerance.`),se&&g("intersections").status==="pass"){_="Shell containment";let s=!1;for(let a=0;a<A.length;a++){u();let y=D(d,A[a][0]),v=[0,1,2].map(M=>(y[0][M]+y[1][M]+y[2][M])/3),h=null;for(let M of[[1,.371,.529],[.217,1,.413],[.317,.193,1]]){let F=ie(B,d,v,M,I,N=>S[N]!==a,u);if(F.some(N=>N.edge))continue;let T=new Map;for(let N of F)T.set(S[N.id],(T.get(S[N.id])||0)+1);h=[...T.values()].filter(N=>N%2===1).length;break}if(h===null){s=!0;continue}(Math.abs(V[a])<=I**3||Math.sign(V[a])!==(h%2?-1:1))&&$("shell_orientation",A[a][0])}L("shell_orientation","Outer shells must face outward; cavity shells face inward."),s&&!g("shell_orientation").count&&z("shell_orientation","inconclusive","Shell containment is numerically ambiguous."),g("shell_orientation").status==="pass"&&(k.volume_mm3=V.reduce((a,y)=>a+y,0),k.volume_valid=!0)}else z("shell_orientation","inconclusive","Requires a closed, consistently wound mesh without intersections.")}else z("shell_orientation","not_run","Run Detailed check for intersections, nested shells and validated volume.");if(Y){_="FDM estimates";let t=g("units").status==="pass";if(!t)for(let s of["build_volume","bed_contact","thin_walls"])z(s,"inconclusive","Confirm physical units first.");if(Y.overhang_degrees!=null)if(!se||(c.level==="detailed"?!k.volume_valid:A.length!==1||V[0]<=0))z("overhangs","inconclusive","Fix triangle winding and mesh defects before estimating overhangs.");else{let s=-Math.sin(Y.overhang_degrees*Math.PI/180),a=0;for(let y=0;y<n;y++){u();let v=D(d,y),h=C(O(v[1],v[0]),O(v[2],v[0])),M=Math.hypot(...h);v.every(F=>F[2]<=I*8)||h[2]/M<s&&($("overhangs",y),a+=M/2)}g("overhangs").estimated=!0,g("overhangs").area_mm2=a,L("overhangs",`Potential support regions above ${Y.overhang_degrees}\xB0 from vertical. +Z is up; bridges/supports are not simulated.`)}if(t){let s=0;for(let y=0;y<n;y++){u();let v=D(d,y);v.every(h=>h[2]<=I*8)&&(s+=Math.hypot(...C(O(v[1],v[0]),O(v[2],v[0])))/2)}g("bed_contact").area_mm2=s,g("bed_contact").estimated=!0,g("bed_contact").count=s<=I**2?1:0,L("bed_contact","Contact area after virtually lowering this model onto an XY bed. Adhesion/stability are not predicted; scene placement is unchanged.");let a=Y.min_wall_mm??Y.line_width_mm;if(a!=null&&c.level==="detailed"&&k.volume_valid){_="Sampled wall thickness";let y=0,v=0,h=1/0,M=Math.max(1,Math.ceil(n/2048));for(let F=0;F<n;F+=M){u();let T=D(d,F),N=C(O(T[1],T[0]),O(T[2],T[0])),de=Math.hypot(...N),pe=N.map(U=>-U/de),ge=[0,1,2].map(U=>(T[0][U]+T[1][U]+T[2][U])/3),ee=ie(B,d,ge,pe,I*8,U=>U!==F,u).sort((U,_e)=>U.t-_e.t);if(!ee.length||ee[0].edge){v++;continue}y++,h=Math.min(h,ee[0].t),ee[0].t<a&&$("thin_walls",F)}Object.assign(g("thin_walls"),{estimated:!0,samples:y,unresolved_samples:v,minimum_sampled_mm:Number.isFinite(h)?h:null}),z("thin_walls",g("thin_walls").count?"issue":"inconclusive",`${y} face-centre samples; threshold ${a} mm. Sampling can miss thin features and does not prove a minimum wall thickness.`)}else a!=null&&z("thin_walls","inconclusive","Requires Detailed check with valid closed volume and confirmed units.")}}}catch(p){if(p.message!=="PRINT_TIME_LIMIT")throw p;for(let j of m)j.status==="not_run"&&(j.message=`Time limit reached during ${_}; this check was not completed.`);if(!q)for(let j of m.slice(0,8))j.status="inconclusive",j.message="Topology check exceeded the time limit; counts are partial.";k.limit_reason="time"}let P=m.slice(0,11);return k.summary=m.some(p=>p.status==="issue"&&p.severity!=="info")?"issues_found":P.some(p=>["not_run","inconclusive"].includes(p.status))||m.some(p=>p.status==="inconclusive")?"incomplete":"no_issues_found",k.elapsed_ms=performance.now()-i,k}export{Se as checkMesh};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as v}from"./chunk-Y6VY64LY.js";import"./chunk-ZSV4PTIA.js";import{b as _}from"./chunk-OP2L7L4N.js";import"./chunk-W5KVLGVR.js";import"./chunk-IK3WL53C.js";var I=["flat","smooth","wireframe","edges","mesh","xray","studio","orientation"];function E(e,n){if(!I.includes(e))throw new Error("Invalid display mode");if(typeof n!="boolean")throw new Error("smooth_shading must be a boolean");return{display:e,smooth_shading:e==="smooth"||n}}function S(e){if(e==null)return null;if(!["x","y","z"].includes(e.axis))throw new Error("Section axis must be x, y or z");if(!Number.isFinite(e.position)||e.position<0||e.position>1)throw new Error("Section position must be between 0 and 1");if(e.flip!==void 0&&typeof e.flip!="boolean")throw new Error("Section flip must be a boolean");return{axis:e.axis,position:e.position,flip:e.flip??!1}}var g=["x","y","z","rotationx","rotationy","rotationz","scalex","scaley","scalez"],f=e=>e.startsWith("scale"),d=(e,n,r=!1)=>{if(typeof e!="number"||!Number.isFinite(e)||Math.abs(e)>Number.MAX_SAFE_INTEGER||r&&e<=0)throw new Error(`Invalid animation ${n}`);return e},h=(e,n)=>{if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`Invalid animation ${n}`);return e};function j(e){return Object.fromEntries(g.map(n=>[n,d(e[n]??(f(n)?e.scale??1:0),n,f(n))]))}function y(e){h(e,"definition");let n={},r=new Set;for(let t of["delta","exact"]){if(e[t]==null)continue;let o=h(e[t],t),i={},s=d(o.msec??300,"duration",!0);if(o.loop!==void 0&&typeof o.loop!="boolean")throw new Error("Animation loop must be a boolean");let l=t==="delta"&&!!o.loop;for(let a of g){let c=o[a]??(f(a)?o.scale:void 0);if(c!==void 0){if(r.has(a))throw new Error(`Animation delta/exact conflict: ${a}`);if(d(c,a,t==="exact"&&f(a)),l&&f(a)&&c<0)throw new Error("Looping scale deltas cannot be negative");i[a]=c,r.add(a)}}if(!Object.keys(i).length)throw new Error("Animation track needs a transform");n[t]={...i,msec:s,...l?{loop:!0}:{}}}if(!Object.keys(n).length)throw new Error("Animation needs delta or exact");return n}function x(e){if(h(e,"playback state"),e.version!==1||typeof e.paused!="boolean")throw new Error("Invalid animation playback state version or paused flag");let n=d(e.elapsed_ms,"elapsed time");if(n<0)throw new Error("Invalid animation elapsed time");h(e.origin,"origin");for(let r of g)d(e.origin[r],r,f(r));if(!Array.isArray(e.center)||e.center.length!==3)throw new Error("Invalid animation pivot");return{version:1,elapsed_ms:n,paused:e.paused,origin:j(e.origin),center:e.center.map(r=>d(r,"pivot"))}}function k(e,n,r){let t=y(e),o=r==null?null:x(r),i=o?.origin??j(n),s=Object.entries(t).map(([a,c])=>{let p=Object.entries(c).filter(([u])=>g.includes(u));if(a==="delta")for(let[u,A]of p)d(i[u]+A,`${u} endpoint`,f(u));return{mode:a,values:p,duration:c.msec,loop:!!c.loop,done:!1}}),l={animation:t,origin:i,tracks:s,elapsed_ms:o?.elapsed_ms??0,paused:o?.paused??!1,last_time:null};return O({...l,tracks:s.map(a=>({...a}))},{}),l}function O(e,n){let r=[];for(let o of e.tracks){if(o.done)continue;let i=e.elapsed_ms/o.duration,s=o.loop?i:Math.min(i,1);for(let[l,a]of o.values){let c=e.origin[l],p=o.mode==="delta"?c+a*s:c*(1-s)+a*s;r.push([l,d(p,`${l} result`,f(l))])}}let t=!1;for(let[o,i]of r)t||=f(o)&&n[o]!==i,n[o]=i;for(let o of e.tracks)!o.loop&&e.elapsed_ms>=o.duration&&(o.done=!0);return t&&(n._stats=null),e.tracks.some(o=>!o.done)}var w=["x","y","z"],m=(e,n,r=!1)=>{if(typeof e!="number"||!Number.isFinite(e)||r&&e<=0)throw new Error(`Invalid scene ${n}`);return e},P=e=>String(e||"").match(/\.(vsb|vsj)(?:[?#]|$)/i)?.[1].toLowerCase()||"";function b(e,n){if(!e||!Array.isArray(e.models)||!e.models.length||e.models.length>n.max_archive_entries)throw new Error("Invalid, empty or oversized scene manifest");let r={models:[]};for(let t of["center_models","auto_rotate","grid"])if(e[t]!==void 0){if(typeof e[t]!="boolean")throw new Error(`Invalid scene ${t}`);r[t]=e[t]}if(e.bg_color!==void 0&&(e.bg_color!=="transparent"&&_(e.bg_color),r.bg_color=e.bg_color),e.infinite_grid&&(r.infinite_grid={spacing:m(e.infinite_grid.spacing,"grid spacing",!0),height:m(e.infinite_grid.height,"grid height")}),e.camera_state){let t={};for(let s of["position","target","up"])t[s]=Object.fromEntries(w.map(l=>[l,m(e.camera_state[s]?.[l],"camera")]));let o=w.map(s=>t.position[s]-t.target[s]),i=w.map(s=>t.up[s]);if(!Math.hypot(...o)||!Math.hypot(...i)||!Math.hypot(o[1]*i[2]-o[2]*i[1],o[2]*i[0]-o[0]*i[2],o[0]*i[1]-o[1]*i[0]))throw new Error("Invalid scene camera");r.camera_state=t}for(let t of e.models){if(!t||typeof t.filename!="string"||!t.filename||t.filename.length>8192||/[\x00-\x1f\x7f]/.test(t.filename))throw new Error("Scene model filename is missing or invalid");if(P(t.filename))throw new Error("Nested scene references are not supported");let o={filename:t.filename};typeof t.orig_filename=="string"&&(o.orig_filename=t.orig_filename.slice(0,200));for(let i of w)for(let s of["","rotation","scale"]){let l=s+i;t[l]!==void 0&&(o[l]=m(t[l],l,s==="scale"))}if(t.scale!==void 0&&(o.scale=m(t.scale,"scale",!0)),t.opacity!==void 0&&(o.opacity=m(t.opacity,"opacity"),o.opacity<0||o.opacity>1))throw new Error("Invalid scene opacity");if(t.color!=null&&(t.color!=="transparent"&&_(t.color),o.color=t.color),o.units=t.units??"mm",!["mm","inch"].includes(o.units))throw new Error("Invalid scene units");if(Object.assign(o,E(t.display??"flat",t.smooth_shading??t.display==="smooth")),o.section=S(t.section),t.view_edges!==void 0&&typeof t.view_edges!="boolean")throw new Error("Invalid scene edges");if(o.view_edges=!!t.view_edges,t.animation!=null&&(o.animation=y(t.animation)),t.animation_state!=null){if(!o.animation)throw new Error("Playback state needs an animation");o.animation_state=x(t.animation_state)}o.animation&&k(o.animation,o,o.animation_state),r.models.push(o)}return r}function C(e,n,r){let t=Object.create(null),o=e;if(n==="vsb"){let i=v(e,r);if(o=i.get("json_data.vsj"),!o)throw new Error("VSB manifest json_data.vsj is missing");let s=b(JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(o)),r);for(let l of s.models){let a=i.get(l.filename);if(!a)throw new Error("VSB model file is missing: "+l.filename);t[l.filename]=a.buffer.slice(a.byteOffset,a.byteOffset+a.byteLength)}return{scene:s,files:t}}return{scene:b(JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(o)),r),files:null}}export{C as readSceneDocument};
|
|
@@ -1,46 +0,0 @@
|
|
|
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.
|