scad-gltf 0.1.1 → 0.1.2
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/LICENSE +339 -0
- package/README.md +1 -2
- package/bin/scad-godot.js +18 -2
- package/editor/dist/assets/{index-t9MYrExo.css → index-B1KAb97r.css} +1 -1
- package/editor/dist/assets/{index-CXqDtloH.js → index-Cy6q5gqL.js} +64 -19
- package/editor/dist/icon.png +0 -0
- package/editor/dist/index.html +3 -2
- package/editor/dist/sw.js +1 -1
- package/godot/addons/scad_importer/LICENSE +21 -0
- package/godot/addons/scad_importer/plugin.cfg +1 -0
- package/godot/addons/scad_importer/scad_importer.gd +3 -0
- package/godot/addons/scad_importer/scad_plugin.gd +3 -0
- package/godot/examples/LICENSE +21 -0
- package/godot/examples/README.md +4 -0
- package/godot/examples/horde_survivor_3d.js +2694 -0
- package/godot/examples/naval_battle_3d.js +1691 -0
- package/package.json +1 -1
|
@@ -4081,19 +4081,34 @@ armature(animations=anim_data) {
|
|
|
4081
4081
|
|
|
4082
4082
|
// Deep Volumetric Sapphire
|
|
4083
4083
|
// Showcasing: $asa=0.0 (Preserves faceted "cut" look of the crystal)
|
|
4084
|
-
color([1.0, 1.0, 1.0, 1.0], transmission=1.0, roughness=0.0, thickness=8.0, ior=1.76, attenuationColor=[0.1, 0.3, 0.9], attenuationDistance=
|
|
4084
|
+
color([1.0, 1.0, 1.0, 1.0], transmission=1.0, roughness=0.0, thickness=8.0, ior=1.76, attenuationColor=[0.1, 0.3, 0.9], attenuationDistance=6.0, $asa=30) {
|
|
4085
|
+
|
|
4086
|
+
// Use difference() to carve out an inner cavity so the shell is hollow,
|
|
4087
|
+
// allowing physically correct index-of-refraction depth sorting.
|
|
4088
|
+
difference() {
|
|
4089
|
+
// Octagonal faceted crystal shape
|
|
4090
|
+
union() {
|
|
4091
|
+
cylinder(h=5, r1=4, r2=0, $fn=8);
|
|
4092
|
+
translate([0,0,-5]) cylinder(h=5, r1=0, r2=4, $fn=8);
|
|
4093
|
+
}
|
|
4094
|
+
|
|
4095
|
+
// Hollow cavity
|
|
4096
|
+
sphere(r=0.9, $fn=32);
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
4085
4099
|
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4100
|
+
// 4. Glowing Core (Child of Gem Shell)
|
|
4101
|
+
// Placed inside its own bone to prevent auto-unioning with the transparent gem
|
|
4102
|
+
bone(name="Core", t=[0,0,0]) {
|
|
4103
|
+
color([0.0, 0.0, 0.0, 1.0], emissive=[0.5, 0.8, 1.0], emissiveIntensity=20.0, $asa=45) {
|
|
4104
|
+
sphere(r=0.6, $fn=32);
|
|
4090
4105
|
}
|
|
4091
4106
|
}
|
|
4092
4107
|
}
|
|
4093
4108
|
}
|
|
4094
4109
|
}
|
|
4095
4110
|
}
|
|
4096
|
-
`,To=document.getElementById(`prompt-desc`),Eo=document.getElementById(`copy-prompt-btn`),Do=document.getElementById(`model-name-input`),z=document.getElementById(`editor`),Oo=document.getElementById(`render-btn`),ko=document.getElementById(`load-scad-btn`),Ao=document.getElementById(`download-scad-btn`),jo=document.getElementById(`export-gltf-btn`),Mo=document.getElementById(`capture-image-btn`),No=document.getElementById(`share-btn`),Po=document.getElementById(`auto-render-cb`),Fo=document.getElementById(`minify-share-cb`),B=document.getElementById(`viewer`),Io=document.getElementById(`right-panel`),Lo=document.getElementById(`backend-url`),Ro=document.getElementById(`backend-connect-btn`),zo=document.getElementById(`backend-ui`),Bo=document.getElementById(`backend-select`),Vo=document.getElementById(`backend-single-save-btn`),Ho=document.getElementById(`backend-delete-btn`),Uo=document.getElementById(`anim-controls-section`),Wo=document.getElementById(`anim-play-btn`),Go=document.getElementById(`anim-select`),Ko=document.getElementById(`anim-slider`),qo=document.getElementById(`prompt-ui-container`),Jo=document.getElementById(`show-grid-cb`),Yo=document.getElementById(`wireframe-cb`),Xo=document.getElementById(`fullscreen-btn`),Zo
|
|
4111
|
+
`,To=document.getElementById(`prompt-desc`),Eo=document.getElementById(`copy-prompt-btn`),Do=document.getElementById(`model-name-input`),z=document.getElementById(`editor`),Oo=document.getElementById(`render-btn`),ko=document.getElementById(`load-scad-btn`),Ao=document.getElementById(`download-scad-btn`),jo=document.getElementById(`export-gltf-btn`),Mo=document.getElementById(`capture-image-btn`),No=document.getElementById(`share-btn`),Po=document.getElementById(`auto-render-cb`),Fo=document.getElementById(`minify-share-cb`),B=document.getElementById(`viewer`),Io=document.getElementById(`right-panel`),Lo=document.getElementById(`backend-url`),Ro=document.getElementById(`backend-connect-btn`),zo=document.getElementById(`backend-ui`),Bo=document.getElementById(`backend-select`),Vo=document.getElementById(`backend-single-save-btn`),Ho=document.getElementById(`backend-delete-btn`),Uo=document.getElementById(`anim-controls-section`),Wo=document.getElementById(`anim-play-btn`),Go=document.getElementById(`anim-select`),Ko=document.getElementById(`anim-slider`),qo=document.getElementById(`prompt-ui-container`),Jo=document.getElementById(`show-grid-cb`),Yo=document.getElementById(`wireframe-cb`),Xo=document.getElementById(`fullscreen-btn`),Zo=document.getElementById(`screenshot-btn`),Qo=``,V=null,$o=null,es=[],ts=!1,ns=null,H=null,rs=!1,is=!1,as=null,os=[],ss=!1,cs=!1,ls={isNew:!0,options:{},content:``},U=null,us=!0,ds=!1;function fs(){return cs?z.value:z.value||`// Global detail setting
|
|
4097
4112
|
$fn = 64;
|
|
4098
4113
|
|
|
4099
4114
|
// Animation Data Dictionary
|
|
@@ -4192,20 +4207,35 @@ armature(animations=anim_data) {
|
|
|
4192
4207
|
|
|
4193
4208
|
// Deep Volumetric Sapphire
|
|
4194
4209
|
// Showcasing: $asa=0.0 (Preserves faceted "cut" look of the crystal)
|
|
4195
|
-
color([1.0, 1.0, 1.0, 1.0], transmission=1.0, roughness=0.0, thickness=8.0, ior=1.76, attenuationColor=[0.1, 0.3, 0.9], attenuationDistance=
|
|
4210
|
+
color([1.0, 1.0, 1.0, 1.0], transmission=1.0, roughness=0.0, thickness=8.0, ior=1.76, attenuationColor=[0.1, 0.3, 0.9], attenuationDistance=6.0, $asa=30) {
|
|
4211
|
+
|
|
4212
|
+
// Use difference() to carve out an inner cavity so the shell is hollow,
|
|
4213
|
+
// allowing physically correct index-of-refraction depth sorting.
|
|
4214
|
+
difference() {
|
|
4215
|
+
// Octagonal faceted crystal shape
|
|
4216
|
+
union() {
|
|
4217
|
+
cylinder(h=5, r1=4, r2=0, $fn=8);
|
|
4218
|
+
translate([0,0,-5]) cylinder(h=5, r1=0, r2=4, $fn=8);
|
|
4219
|
+
}
|
|
4220
|
+
|
|
4221
|
+
// Hollow cavity
|
|
4222
|
+
sphere(r=0.9, $fn=32);
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4196
4225
|
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4226
|
+
// 4. Glowing Core (Child of Gem Shell)
|
|
4227
|
+
// Placed inside its own bone to prevent auto-unioning with the transparent gem
|
|
4228
|
+
bone(name="Core", t=[0,0,0]) {
|
|
4229
|
+
color([0.0, 0.0, 0.0, 1.0], emissive=[0.5, 0.8, 1.0], emissiveIntensity=20.0, $asa=45) {
|
|
4230
|
+
sphere(r=0.6, $fn=32);
|
|
4201
4231
|
}
|
|
4202
4232
|
}
|
|
4203
4233
|
}
|
|
4204
4234
|
}
|
|
4205
4235
|
}
|
|
4206
4236
|
}
|
|
4207
|
-
`}function
|
|
4208
|
-
`;)r++;else if(e[r]===`/`&&e[r+1]===`*`){for(r+=2;r<e.length&&(e[r]!==`*`||e[r+1]!==`/`);)r++;r+=2}else/\s/.test(e[r])?(t.length>0&&!/\s/.test(t[t.length-1])&&(t+=` `),r++):(t+=e[r],r++);e=t.trim()}if(!e||!
|
|
4237
|
+
`}function ps(){return Do.value.trim()||`model`}Do.addEventListener(`input`,Os),qo&&(qo.innerHTML=We,Ue(qo,`scad_editor_prompt_settings`)),Eo.onclick=async()=>{let e=To.value.trim()||`an object`,t=He(qo),n=Ve(e,t);try{await navigator.clipboard.writeText(n);let e=Eo.innerText;Eo.innerText=`✅ Copied!`,setTimeout(()=>{Eo.innerText=e},2e3)}catch(e){alert(`Failed to copy clipboard: `+e)}};async function ms(e){let t={};if(!cs)return t;let n=new Set;async function r(e){let i=/(?:include|use)\s*([<"])([^>"]+)([>"])/g,a;for(;(a=i.exec(e))!==null;){let e=a[2],i=e.split(/[/\\]/).pop();if(!n.has(e)){n.add(e);try{let n=await fetch(`${ws}/api/scads/${encodeURIComponent(i)}`);if(n.ok){let i=await n.json();t[e]=i.content,await r(i.content)}}catch{console.warn(`Failed to load dependency: ${e}`)}}}}return await r(e),t}async function hs(e){if(typeof e!=`string`)return;if(ts){ns=e;return}if(e.trim()===``){zs();return}let t=e.match(/\/\*\s*Model Name:\s*(.*?)\s*\*\//i);if(t&&t[1]){let e=t[1].trim().replace(/\s+/g,`_`).replace(/[^a-zA-Z0-9_-]/g,``);e&&Do.value!==e&&(Do.value=e,Os())}ts=!0;try{let t=await ms(e);$o=await xe(e,{wasmUrl:me,additionalFiles:t}),await Vs($o)}catch(e){console.error(e)}finally{if(ts=!1,ns!==null){let e=ns;ns=null,hs(e)}}}Oo.onclick=()=>hs(fs());var gs;z.addEventListener(`input`,()=>{Os(),clearTimeout(gs),Po.checked&&(gs=setTimeout(()=>{hs(fs())},800))}),Po.addEventListener(`change`,()=>{Po.checked&&hs(fs())}),ko.onclick=()=>{let e=document.createElement(`input`);e.type=`file`,e.accept=`.scad`,e.onchange=async e=>{if(e.target.files&&e.target.files.length>0)try{let t=await e.target.files[0].text();z.value=t,Os(),hs(t)}catch(e){alert(`Failed to read file: `+e.message)}},e.click()};function _s(e,t){let n=URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(n)}Ao.onclick=()=>{let e=fs();_s(new Blob([e],{type:`text/plain`}),`${ps()}.scad`)},jo.onclick=()=>{$o&&_s(new Blob([$o],{type:`application/octet-stream`}),`${ps()}.glb`)};function vs(){Mo.innerText=is?`🔴 Rec...`:U&&us?`🎥 Video`:`📷 Image`}Mo.onclick=()=>{if(is){ss=!0,as&&as.state!==`inactive`&&as.stop();return}if(U&&us){if(typeof MediaRecorder<`u`){let e=q!==void 0&&J&&J.checked;U.time=0,H&&H.update(0),G.aspect=1,G.updateProjectionMatrix(),K.setSize(512,512,!1),As.setSize(512,512),e&&q.updateCamera();let t=K.domElement,n=document.createElement(`canvas`);n.width=512,n.height=512;let r=n.getContext(`2d`),i=n.captureStream(30),a=25e5,o=`mp4`,s={mimeType:`video/mp4;codecs=avc1`,videoBitsPerSecond:a};MediaRecorder.isTypeSupported(s.mimeType)||(s={mimeType:`video/mp4`,videoBitsPerSecond:a},MediaRecorder.isTypeSupported(s.mimeType)||(o=`webm`,s={mimeType:`video/webm;codecs=vp9`,videoBitsPerSecond:a},MediaRecorder.isTypeSupported(s.mimeType)||(s={mimeType:`video/webm;codecs=vp8`,videoBitsPerSecond:a},MediaRecorder.isTypeSupported(s.mimeType)||(s={mimeType:`video/webm`,videoBitsPerSecond:a}))));let c=null;ss=!1,is=!0,vs();let l=U.getClip().duration,u=Math.round(l*30),d=0,f=[],p=()=>{if(ss||d>=u){if(f.length===0){is=!1,G.aspect=B.clientWidth/B.clientHeight,G.updateProjectionMatrix(),K.setSize(B.clientWidth,B.clientHeight),As.setSize(B.clientWidth,B.clientHeight),e&&q!==void 0&&q.updateCamera(),vs();return}ss=!1,as=new MediaRecorder(i,s),os=[],as.ondataavailable=e=>{e.data.size>0&&os.push(e.data)},as.onstop=()=>{c&&clearTimeout(c),_s(new Blob(os,{type:s.mimeType.split(`;`)[0]}),`${ps()}.${o}`),is=!1,G.aspect=B.clientWidth/B.clientHeight,G.updateProjectionMatrix(),K.setSize(B.clientWidth,B.clientHeight),As.setSize(B.clientWidth,B.clientHeight),e&&q!==void 0&&q.updateCamera(),vs()},r.drawImage(f[0],0,0,512,512),as.start();let t=0,n=performance.now(),a=()=>{if(ss){as.stop();return}if(t<f.length){r.drawImage(f[t],0,0,512,512),t++;let e=n+1e3/30*t,i=Math.max(0,e-performance.now());c=setTimeout(a,i)}else c=setTimeout(()=>as.stop(),50)};c=setTimeout(a,0);return}if(U.time=d/30,H&&H.update(0),e){Z!==void 0&&(Z.visible=!1),Y!==void 0&&(Y.visible=!1),Q!==void 0&&(Q.visible=!1),$!==void 0&&($.visible=!1),q.setScene(W,G);let e=()=>{if(ss){p();return}let n=0;for(;q.samples<100&&n<5;)q.renderSample(),n++;if(q.samples>=100){let e=document.createElement(`canvas`);e.width=512,e.height=512,e.getContext(`2d`).drawImage(t,0,0,512,512),f.push(e),d++,setTimeout(p,0)}else requestAnimationFrame(e)};e()}else{let e=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!0),Y!==void 0&&(Y.visible=!0),Q!==void 0&&(Q.visible=e),$!==void 0&&($.visible=e),As.render();let n=document.createElement(`canvas`);n.width=512,n.height=512,n.getContext(`2d`).drawImage(t,0,0,512,512),f.push(n),d++,setTimeout(p,0)}};p()}else alert(`Video recording is not supported in this browser.`)}else rs=!0};function ys(e){let t=e.length%4;return t===2?e+`==`:t===3?e+`=`:e}async function bs(e){try{if(typeof CompressionStream<`u`){let t=new Blob([e]).stream().pipeThrough(new CompressionStream(`deflate-raw`)),n=await new Response(t).arrayBuffer(),r=new Uint8Array(n),i=``;for(let e=0;e<r.length;e++)i+=String.fromCharCode(r[e]);return`c`+btoa(i).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}}catch(e){console.warn(`CompressionStream failed, falling back`,e)}return`u`+btoa(unescape(encodeURIComponent(e))).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}async function xs(e){if(!e)return null;let t=e.charAt(0),n=e.substring(1);if(n=ys(n.replace(/-/g,`+`).replace(/_/g,`/`)),t===`c`)try{let e=atob(n),t=new Uint8Array(e.length);for(let n=0;n<e.length;n++)t[n]=e.charCodeAt(n);let r=new Blob([t]).stream().pipeThrough(new DecompressionStream(`deflate-raw`)),i=await new Response(r).arrayBuffer();return new TextDecoder().decode(i)}catch(e){console.warn(`DecompressionStream failed`,e)}else if(t===`u`)try{return decodeURIComponent(escape(atob(n)))}catch(e){console.warn(`Unescape failed`,e)}else try{return decodeURIComponent(escape(atob(ys(e.replace(/-/g,`+`).replace(/_/g,`/`)))))}catch{return decodeURIComponent(e)}return null}No.onclick=async()=>{let e=z.value.trim(),t=e,n=new URL(window.location.href),r=``;if(Fo&&Fo.checked){let t=``,n=!1,r=0;for(;r<e.length;)if(n)t+=e[r],e[r]===`"`&&e[r-1]!==`\\`&&(n=!1),r++;else if(e[r]===`"`)n=!0,t+=e[r],r++;else if(e[r]===`/`&&e[r+1]===`/`)for(r+=2;r<e.length&&e[r]!==`
|
|
4238
|
+
`;)r++;else if(e[r]===`/`&&e[r+1]===`*`){for(r+=2;r<e.length&&(e[r]!==`*`||e[r+1]!==`/`);)r++;r+=2}else/\s/.test(e[r])?(t.length>0&&!/\s/.test(t[t.length-1])&&(t+=` `),r++):(t+=e[r],r++);e=t.trim()}if(!e||!cs&&t===`// Global detail setting
|
|
4209
4239
|
$fn = 64;
|
|
4210
4240
|
|
|
4211
4241
|
// Animation Data Dictionary
|
|
@@ -4304,15 +4334,30 @@ armature(animations=anim_data) {
|
|
|
4304
4334
|
|
|
4305
4335
|
// Deep Volumetric Sapphire
|
|
4306
4336
|
// Showcasing: $asa=0.0 (Preserves faceted "cut" look of the crystal)
|
|
4307
|
-
color([1.0, 1.0, 1.0, 1.0], transmission=1.0, roughness=0.0, thickness=8.0, ior=1.76, attenuationColor=[0.1, 0.3, 0.9], attenuationDistance=
|
|
4337
|
+
color([1.0, 1.0, 1.0, 1.0], transmission=1.0, roughness=0.0, thickness=8.0, ior=1.76, attenuationColor=[0.1, 0.3, 0.9], attenuationDistance=6.0, $asa=30) {
|
|
4338
|
+
|
|
4339
|
+
// Use difference() to carve out an inner cavity so the shell is hollow,
|
|
4340
|
+
// allowing physically correct index-of-refraction depth sorting.
|
|
4341
|
+
difference() {
|
|
4342
|
+
// Octagonal faceted crystal shape
|
|
4343
|
+
union() {
|
|
4344
|
+
cylinder(h=5, r1=4, r2=0, $fn=8);
|
|
4345
|
+
translate([0,0,-5]) cylinder(h=5, r1=0, r2=4, $fn=8);
|
|
4346
|
+
}
|
|
4347
|
+
|
|
4348
|
+
// Hollow cavity
|
|
4349
|
+
sphere(r=0.9, $fn=32);
|
|
4350
|
+
}
|
|
4351
|
+
}
|
|
4308
4352
|
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4353
|
+
// 4. Glowing Core (Child of Gem Shell)
|
|
4354
|
+
// Placed inside its own bone to prevent auto-unioning with the transparent gem
|
|
4355
|
+
bone(name="Core", t=[0,0,0]) {
|
|
4356
|
+
color([0.0, 0.0, 0.0, 1.0], emissive=[0.5, 0.8, 1.0], emissiveIntensity=20.0, $asa=45) {
|
|
4357
|
+
sphere(r=0.6, $fn=32);
|
|
4313
4358
|
}
|
|
4314
4359
|
}
|
|
4315
4360
|
}
|
|
4316
4361
|
}
|
|
4317
4362
|
}
|
|
4318
|
-
}`)r=n.origin+n.pathname+n.search;else try{n.hash=await ys(e),r=n.toString()}catch{r=n.origin+n.pathname+n.search}window.history.replaceState(null,``,r);try{if(navigator.share){let e=Do.value.trim(),t=e?e.replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()):`Scadify`;await navigator.share({title:t,url:r});let n=No.innerText;No.innerText=`✅ Shared!`,setTimeout(()=>No.innerText=n,2e3)}else{await navigator.clipboard.writeText(r);let e=No.innerText;No.innerText=`✅ Copied Link!`,setTimeout(()=>No.innerText=e,2e3)}}catch(e){if(e.name!==`AbortError`)try{await navigator.clipboard.writeText(r),No.innerText=`✅ Copied Link!`,setTimeout(()=>No.innerText=`🔗 Share`,2e3)}catch{alert(`Failed to share or copy link.`)}}};var xs=document.getElementById(`drag-overlay`);window.addEventListener(`dragenter`,e=>{e.preventDefault(),xs.classList.add(`active`)}),window.addEventListener(`dragover`,e=>{e.preventDefault(),xs.classList.add(`active`)}),xs.addEventListener(`dragleave`,e=>{e.preventDefault(),xs.classList.remove(`active`)}),window.addEventListener(`drop`,async e=>{if(e.preventDefault(),xs.classList.remove(`active`),e.dataTransfer.files&&e.dataTransfer.files.length>0){let t=e.dataTransfer.files[0],n=t.name.toLowerCase();if(n.endsWith(`.hdr`)){let e=URL.createObjectURL(t);new Co().load(e,e=>{e.mapping=303,W.environment=e,Ps=e,J&&J.checked?(W.background=e,W.backgroundBlurriness=.5,W.backgroundIntensity=.3):(W.background=new ze(2236962),W.backgroundBlurriness=0,W.backgroundIntensity=1),q!==void 0&&(q.setScene(W,G),q.updateCamera())},void 0,e=>{console.warn(`Error loading HDR:`,e),alert(`Failed to load HDR file.`)})}else if(n.endsWith(`.scad`)||!t.type||t.type.includes(`text`))try{let e=await t.text();z.value=e,Ds(),ms(e)}catch(e){alert(`Failed to read file: `+e.message)}else alert(`Please drop a valid .scad or .hdr file.`)}});var Ss=null,Cs=``;async function ws(e){let t=await fetch(`${e}/api/scads`);if(!t.ok)throw Error(`Failed to fetch files`);return(await t.json()).files}function Ts(){Bo.innerHTML=`<option value="">-- Create New Model --</option>`,Array.isArray(Ss)&&Ss.forEach((e,t)=>{let n=document.createElement(`option`);n.value=t,n.innerText=e,Bo.appendChild(n)}),Bo.value=Zo,Ho&&(Ho.style.display=Zo===``?`none`:`flex`)}function Es(){let e=Do.value.trim();return e&&=e.replace(/\.scad$/i,``).split(/[/\\]/).pop()+`.scad`,{filename:e}}function Ds(){if(Ss===null)return;let e=Bo.value===``,{filename:t}=Es(),n=z.value,r=!1;e?(t&&t!==`.scad`||n.trim()!==``)&&(r=!0):n!==cs.content&&(r=!0),r?(Vo.style.display=`flex`,Vo.innerText=`Save`):Vo.style.display=`none`}async function Os(e,t=!1){if(!e)return!1;try{return Ro.innerText=`Connecting...`,Ss=await ws(e),Cs=e,ss=!0,Ro.innerText=`Connected`,zo.classList.add(`active`),z.placeholder=`Enter OpenSCAD code here...`,Zo=``,Ts(),cs={isNew:!0,content:``},Ds(),!0}catch(e){return t?console.warn(`Auto-connect failed:`,e.message):alert(`Connection failed: `+e.message),Ro.innerText=`Connect`,zo.classList.remove(`active`),ss=!1,z.placeholder=wo,!1}}Ro.onclick=async()=>{await Os(Lo.value.trim(),!1)&&ms(ds())},Bo.addEventListener(`change`,async()=>{if(Vo.style.display!==`none`&&!confirm(`You have unsaved changes. Are you sure you want to discard them and load another model?`)){Bo.value=Zo;return}let e=Bo.value;if(Zo=e,Ho&&(Ho.style.display=e===``?`none`:`flex`),e===``)Do.value=``,z.value=``,ms(ds()),cs={isNew:!0,content:``},Ds();else{let t=Ss[e];Do.value=t.replace(/\.scad$/i,``);try{let e=await fetch(`${Cs}/api/scads/${encodeURIComponent(t)}`);if(!e.ok){let t=await e.json();throw Error(t.error||`Failed to load model`)}let n=await e.json();z.value=n.content,ms(ds()),cs={isNew:!1,content:n.content},Ds()}catch(e){alert(`Error loading model: `+e.message)}}}),Vo.onclick=async()=>{let{filename:e}=Es();if(!e||e===`.scad`){let e=Vo.innerText;Vo.innerText=`⚠️ Name Required!`,Vo.style.borderColor=`#ff4444`,Do.focus(),setTimeout(()=>{Vo.innerText=e,Vo.style.borderColor=``},2e3);return}if(Bo.value===``&&Ss&&Ss.includes(e)&&!confirm(`A model named "${e}" already exists. Are you sure you want to overwrite it?`))return;let t={filename:e,content:z.value};try{Vo.innerText=`Saving...`;let n=await fetch(`${Cs}/api/scads`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok){let e=await n.json();throw Error(e.error||`Save failed`)}Ss=await ws(Cs);let r=Ss.findIndex(t=>t===e);Zo=r>=0?r.toString():``,Ts(),cs={isNew:!1,content:z.value},Ds()}catch(e){Ds(),alert(`Error: `+e.message)}},Ho.onclick=async()=>{let e=Bo.value;if(e===``)return;let t=Ss[e];if(confirm(`Are you sure you want to delete "${t}"?`))try{Ho.innerText=`Deleting...`;let e=await fetch(`${Cs}/api/scads/${encodeURIComponent(t)}`,{method:`DELETE`});if(!e.ok){let t=`Delete failed`;try{let n=await e.json();n.error&&(t=n.error)}catch{}throw Error(t)}Ss=await ws(Cs),Zo=``,Ts(),Do.value=``,z.value=``,ms(ds()),cs={isNew:!0,content:``},Ds(),Ho.innerText=`Delete`}catch(e){alert(`Error: `+e.message),Ho.innerText=`Delete`}};var W=new le;W.background=new ze(2236962);var G=new c(60,B.clientWidth/B.clientHeight,.1,2e3);G.position.set(50,50,-50);var K=new Fe({antialias:!0,preserveDrawingBuffer:!0});K.setSize(B.clientWidth,B.clientHeight),K.setPixelRatio(Math.min(window.devicePixelRatio,2)),K.toneMapping=4,K.toneMappingExposure=1,K.shadowMap.enabled=!0,K.shadowMap.type=2,B.appendChild(K.domElement);var ks=new he(K),As=new ke(W,G);ks.addPass(As);var js=new Se(new E(B.clientWidth,B.clientHeight),.1,.1,2);ks.addPass(js);var Ms=new Ne;ks.addPass(Ms);var q=new So(K);q.bounces=10,q.transmissiveBounces=10,q.multipleImportanceSampling=!0,q.filterGlossyFactor=.2;var Ns=new je(G,K.domElement);Ns.addEventListener(`change`,()=>{q!==void 0&&q.updateCamera()});var Ps=null;new Co().load(`./aristea_wreck_puresky_2k.hdr`,e=>{e.mapping=303,W.environment=e,Ps=e;let t=J&&J.checked,n=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!t),Y!==void 0&&(Y.visible=!t),Q!==void 0&&(Q.visible=!t&&n),$!==void 0&&($.visible=!t&&n),t?(W.background=e,W.backgroundBlurriness=.5,W.backgroundIntensity=.3):(W.background=new ze(2236962),W.backgroundBlurriness=0,W.backgroundIntensity=1),q!==void 0&&(q.setScene(W,G),q.updateCamera())},void 0,e=>console.warn(`Error loading HDR:`,e));var J=document.getElementById(`path-tracing-cb`);J&&J.addEventListener(`change`,()=>{let e=J.checked,t=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!e),Y!==void 0&&(Y.visible=!e),Q!==void 0&&(Q.visible=!e&&t),$!==void 0&&($.visible=!e&&t),e?Ps&&(W.background=Ps,W.backgroundBlurriness=.5,W.backgroundIntensity=.3):(W.background=new ze(2236962),W.backgroundBlurriness=0,W.backgroundIntensity=1),e&&q!==void 0&&q.setScene(W,G)}),Ns.enableDamping=!0,Ns.dampingFactor=.1,Ns.maxDistance=2e3,W.environment=new ye(K).fromScene(new ne,.04).texture,W.environmentIntensity=.8;var Y=new h;W.add(Y);var X=new Le(16777215,1.5);X.castShadow=!0,X.shadow.mapSize.width=2048,X.shadow.mapSize.height=2048,X.shadow.bias=-5e-4,Y.add(X),Y.add(X.target);var Fs=new t(16777215,4473924,.6);Fs.position.set(0,20,0),Y.add(Fs);var Z=new v(new C(2e3,2e3),new n({color:2236962,roughness:.8,metalness:.1}));Z.rotation.x=-Math.PI/2,Z.receiveShadow=!0,W.add(Z);var Q=new i(2e3,100,5592405,3355443);Q.material.transparent=!0,Q.material.opacity=.5,W.add(Q);var $=new ue(100);W.add($),Yo&&Yo.addEventListener(`change`,()=>{let e=Yo.checked;V&&V.traverse(t=>{t.isMesh&&t.material&&(Array.isArray(t.material)?t.material.forEach(t=>{t.wireframe=e}):t.material.wireframe=e)})}),Xo&&Io&&(Xo.addEventListener(`click`,()=>{document.fullscreenElement?document.exitFullscreen():Io.requestFullscreen().catch(e=>{console.warn(`Error attempting to enable fullscreen: ${e.message}`)})}),document.addEventListener(`fullscreenchange`,()=>{setTimeout(()=>window.dispatchEvent(new Event(`resize`)),100)}));function Is(){U&&(ls=!1,U.paused=!0,Wo.innerText=`▶ Play`,_s())}function Ls(e){if(!H||!$o[e])return;U&&U.stop();let t=$o[e];U=H.clipAction(t),U.play(),U.time=0,H.update(0),ls=!0,U.paused=!1,Wo.innerText=`⏸ Pause`,Ko.value=0,_s()}Go.addEventListener(`change`,e=>{Ls(parseInt(e.target.value))}),Wo.addEventListener(`click`,()=>{U&&(ls=!ls,U.paused=!ls,Wo.innerText=ls?`⏸ Pause`:`▶ Play`,_s())}),Ko.addEventListener(`mousedown`,()=>{us=!0,Is()}),Ko.addEventListener(`mouseup`,()=>{us=!1}),Ko.addEventListener(`touchstart`,()=>{us=!0,Is()},{passive:!0}),Ko.addEventListener(`touchend`,()=>{us=!1},{passive:!0}),Ko.addEventListener(`input`,e=>{if(Is(),U){let t=U.getClip().duration;U.time=parseFloat(e.target.value)*t,H&&(H.update(0),J&&J.checked&&q!==void 0&&(Z!==void 0&&(Z.visible=!1),Y!==void 0&&(Y.visible=!1),Q!==void 0&&(Q.visible=!1),$!==void 0&&($.visible=!1),q.setScene(W,G)))}});function Rs(){rs&&is&&is.state!==`inactive`&&(is.stop(),rs=!1),V&&=(H&&=(H.stopAllAction(),H.uncacheRoot(H.getRoot()),null),U=null,W.remove(V),V.traverse(e=>{e.isMesh&&(e.geometry.dispose(),Array.isArray(e.material)?e.material.forEach(e=>e.dispose()):e.material&&e.material.dispose())}),null),_s()}function zs(e,t){if(!e)return new Ee;e.updateMatrixWorld(!0);let n=new Ee().setFromObject(e);if(!t||t.length===0)return n;let r=new Map;e.traverse(e=>{r.set(e,{position:e.position.clone(),quaternion:e.quaternion.clone(),scale:e.scale.clone()})});let i=new se(e);for(let r of t){let t=i.clipAction(r);t.play();let a=new Set([0,r.duration]);if(r.tracks){for(let e of r.tracks)if(e.times)for(let t=0;t<e.times.length;t++)a.add(e.times[t])}if(r.duration>0)for(let e=0;e<=20;e++)a.add(e/20*r.duration);let o=Array.from(a).sort((e,t)=>e-t);if(o.length>60){let e=[],t=(o.length-1)/59;for(let n=0;n<60;n++)e.push(o[Math.round(n*t)]);o=e}for(let r of o)t.time=r,i.update(0),e.updateMatrixWorld(!0),n.expandByObject(e);t.stop()}return i.stopAllAction(),i.uncacheRoot(e),r.forEach((e,t)=>{t.position.copy(e.position),t.quaternion.copy(e.quaternion),t.scale.copy(e.scale)}),e.updateMatrixWorld(!0),n}function Bs(e){return new Promise((t,n)=>{let r=null;V&&(H&&U&&(U.time=0,H.update(0),J&&J.checked&&q!==void 0&&(Z!==void 0&&(Z.visible=!1),Y!==void 0&&(Y.visible=!1),Q!==void 0&&(Q.visible=!1),$!==void 0&&($.visible=!1),q.setScene(W,G))),r=zs(V,$o)),Rs();let i=e;e instanceof Uint8Array&&(i=e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength)),new we().parse(i,``,e=>{V=e.scene,$o=e.animations||[];let n=Yo?Yo.checked:!1;$o.length?(H=new se(V),Uo.style.display=`flex`,Go.innerHTML=``,$o.forEach((e,t)=>{let n=document.createElement(`option`);n.value=t,n.innerText=e.name||`Animation ${t+1}`,Go.appendChild(n)}),Ls(0)):(Uo.style.display=`none`,U=null,_s()),V.traverse(e=>{e.isMesh&&(e.castShadow=!0,e.receiveShadow=!0,e.frustumCulled=!1,e.material&&(Array.isArray(e.material)?e.material.forEach(e=>{e.wireframe=n}):e.material.wireframe=n))}),W.add(V);let i=!0;if(r&&!r.isEmpty()){let e=zs(V,$o);if(!e.isEmpty()){let t=.001;r.min.distanceTo(e.min)<t&&r.max.distanceTo(e.max)<t&&(i=!1)}}if(i)typeof Vs==`function`&&Vs();else{let e=J&&J.checked,t=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!e),Y!==void 0&&(Y.visible=!e),Q!==void 0&&(Q.visible=!e&&t),$!==void 0&&($.visible=!e&&t),q!==void 0&&q.setScene(W,G)}t()},n)})}function Vs(){if(!V)return;let e=zs(V,$o);if(e.isEmpty())return;let t=e.getCenter(new D),n=e.getSize(new D),r=Math.max(n.x,n.y,n.z)||10;Z.position.y=e.min.y-.01,Q.position.y=Z.position.y+.001,$.position.y=Z.position.y+.002;let i=G.fov*(Math.PI/180),o=r/(2*Math.tan(i/2));G.aspect<1&&(o/=G.aspect),o*=1.5,G.near=Math.max(.01,r*.01),G.far=Math.max(2e3,o*10),G.updateProjectionMatrix(),Ns.maxDistance=G.far,G.position.set(t.x+o*.8,t.y+o*.8,t.z-o*.8),G.lookAt(t),Ns.target.copy(t),Ns.update(),X.position.set(t.x+r,t.y+r*1.5,t.z-r),X.target.position.copy(t),X.target.updateMatrixWorld();let s=r*1.5;X.shadow.camera.left=-s,X.shadow.camera.right=s,X.shadow.camera.top=s,X.shadow.camera.bottom=-s,X.shadow.camera.near=.1,X.shadow.camera.far=r*5,X.shadow.camera.updateProjectionMatrix(),W.fog=new a(2236962,o*1.5,o*5);let c=J&&J.checked,l=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!c),Y!==void 0&&(Y.visible=!c),Q!==void 0&&(Q.visible=!c&&l),$!==void 0&&($.visible=!c&&l),q!==void 0&&q.setScene(W,G)}var Hs=performance.now();function Us(){requestAnimationFrame(Us);let e=performance.now(),t=(e-Hs)/1e3;if(Hs=e,H&&(!(J&&J.checked)&&!rs&&H.update(t),U&&ls&&!us)){let e=U.getClip().duration;if(e>0){let t=U.time%e;t<0&&(t+=e),Ko.value=t/e}}Ns.update();let n=J&&J.checked,r=!Jo||Jo.checked;n?(Z!==void 0&&(Z.visible=!1),Y!==void 0&&(Y.visible=!1),Q!==void 0&&(Q.visible=!1),$!==void 0&&($.visible=!1),q!==void 0&&!rs&&q.renderSample()):(Z!==void 0&&(Z.visible=!0),Y!==void 0&&(Y.visible=!0),Q!==void 0&&(Q.visible=r),$!==void 0&&($.visible=r),rs||ks.render()),ns&&(ns=!1,K.domElement.toBlob(e=>{e&&gs(e,`${fs()}.png`)},`image/png`))}Us(),window.addEventListener(`resize`,()=>{if(!B||rs)return;let e=B.clientWidth,t=B.clientHeight;G.aspect=e/t,G.updateProjectionMatrix(),K.setSize(e,t),ks.setSize(e,t)}),setTimeout(()=>window.dispatchEvent(new Event(`resize`)),100);var Ws=[`localhost`,`127.0.0.1`,``].includes(window.location.hostname);z.placeholder=Ws?`Enter OpenSCAD code here...`:wo,(async function(){if(Ws&&await Os(Lo.value.trim(),!0),window.location.hash&&window.location.hash.length>1)try{let e=await bs(window.location.hash.substring(1));e&&(z.value=e)}catch(e){console.error(`Failed to decode hash`,e)}Ds(),setTimeout(()=>ms(ds()),500)})();
|
|
4363
|
+
}`)r=n.origin+n.pathname+n.search;else try{n.hash=await bs(e),r=n.toString()}catch{r=n.origin+n.pathname+n.search}window.history.replaceState(null,``,r);try{if(navigator.share){let e=Do.value.trim(),t=e?e.replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()):`Scadify`;await navigator.share({title:t,url:r});let n=No.innerText;No.innerText=`✅ Shared!`,setTimeout(()=>No.innerText=n,2e3)}else{await navigator.clipboard.writeText(r);let e=No.innerText;No.innerText=`✅ Copied Link!`,setTimeout(()=>No.innerText=e,2e3)}}catch(e){if(e.name!==`AbortError`)try{await navigator.clipboard.writeText(r),No.innerText=`✅ Copied Link!`,setTimeout(()=>No.innerText=`🔗 Share`,2e3)}catch{alert(`Failed to share or copy link.`)}}};var Ss=document.getElementById(`drag-overlay`);window.addEventListener(`dragenter`,e=>{e.preventDefault(),Ss.classList.add(`active`)}),window.addEventListener(`dragover`,e=>{e.preventDefault(),Ss.classList.add(`active`)}),Ss.addEventListener(`dragleave`,e=>{e.preventDefault(),Ss.classList.remove(`active`)}),window.addEventListener(`drop`,async e=>{if(e.preventDefault(),Ss.classList.remove(`active`),e.dataTransfer.files&&e.dataTransfer.files.length>0){let t=e.dataTransfer.files[0],n=t.name.toLowerCase();if(n.endsWith(`.hdr`)){let e=URL.createObjectURL(t);new Co().load(e,e=>{e.mapping=303,W.environment=e,Fs=e,J&&J.checked?(W.background=e,W.backgroundBlurriness=.5,W.backgroundIntensity=.3):(W.background=new ze(2236962),W.backgroundBlurriness=0,W.backgroundIntensity=1),q!==void 0&&(q.setScene(W,G),q.updateCamera())},void 0,e=>{console.warn(`Error loading HDR:`,e),alert(`Failed to load HDR file.`)})}else if(n.endsWith(`.scad`)||!t.type||t.type.includes(`text`))try{let e=await t.text();z.value=e,Os(),hs(e)}catch(e){alert(`Failed to read file: `+e.message)}else alert(`Please drop a valid .scad or .hdr file.`)}});var Cs=null,ws=``;async function Ts(e){let t=await fetch(`${e}/api/scads`);if(!t.ok)throw Error(`Failed to fetch files`);return(await t.json()).files}function Es(){Bo.innerHTML=`<option value="">-- Create New Model --</option>`,Array.isArray(Cs)&&Cs.forEach((e,t)=>{let n=document.createElement(`option`);n.value=t,n.innerText=e,Bo.appendChild(n)}),Bo.value=Qo,Ho&&(Ho.style.display=Qo===``?`none`:`flex`)}function Ds(){let e=Do.value.trim();return e&&=e.replace(/\.scad$/i,``).split(/[/\\]/).pop()+`.scad`,{filename:e}}function Os(){if(Cs===null)return;let e=Bo.value===``,{filename:t}=Ds(),n=z.value,r=!1;e?(t&&t!==`.scad`||n.trim()!==``)&&(r=!0):n!==ls.content&&(r=!0),r?(Vo.style.display=`flex`,Vo.innerText=`Save`):Vo.style.display=`none`}async function ks(e,t=!1){if(!e)return!1;try{return Ro.innerText=`Connecting...`,Cs=await Ts(e),ws=e,cs=!0,Ro.innerText=`Connected`,zo.classList.add(`active`),z.placeholder=`Enter OpenSCAD code here...`,Qo=``,Es(),ls={isNew:!0,content:``},Os(),!0}catch(e){return t?console.warn(`Auto-connect failed:`,e.message):alert(`Connection failed: `+e.message),Ro.innerText=`Connect`,zo.classList.remove(`active`),cs=!1,z.placeholder=wo,!1}}Ro.onclick=async()=>{await ks(Lo.value.trim(),!1)&&hs(fs())},Bo.addEventListener(`change`,async()=>{if(Vo.style.display!==`none`&&!confirm(`You have unsaved changes. Are you sure you want to discard them and load another model?`)){Bo.value=Qo;return}let e=Bo.value;if(Qo=e,Ho&&(Ho.style.display=e===``?`none`:`flex`),e===``)Do.value=``,z.value=``,hs(fs()),ls={isNew:!0,content:``},Os();else{let t=Cs[e];Do.value=t.replace(/\.scad$/i,``);try{let e=await fetch(`${ws}/api/scads/${encodeURIComponent(t)}`);if(!e.ok){let t=await e.json();throw Error(t.error||`Failed to load model`)}let n=await e.json();z.value=n.content,hs(fs()),ls={isNew:!1,content:n.content},Os()}catch(e){alert(`Error loading model: `+e.message)}}}),Vo.onclick=async()=>{let{filename:e}=Ds();if(!e||e===`.scad`){let e=Vo.innerText;Vo.innerText=`⚠️ Name Required!`,Vo.style.borderColor=`#ff4444`,Do.focus(),setTimeout(()=>{Vo.innerText=e,Vo.style.borderColor=``},2e3);return}if(Bo.value===``&&Cs&&Cs.includes(e)&&!confirm(`A model named "${e}" already exists. Are you sure you want to overwrite it?`))return;let t={filename:e,content:z.value};try{Vo.innerText=`Saving...`;let n=await fetch(`${ws}/api/scads`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok){let e=await n.json();throw Error(e.error||`Save failed`)}Cs=await Ts(ws);let r=Cs.findIndex(t=>t===e);Qo=r>=0?r.toString():``,Es(),ls={isNew:!1,content:z.value},Os()}catch(e){Os(),alert(`Error: `+e.message)}},Ho.onclick=async()=>{let e=Bo.value;if(e===``)return;let t=Cs[e];if(confirm(`Are you sure you want to delete "${t}"?`))try{Ho.innerText=`Deleting...`;let e=await fetch(`${ws}/api/scads/${encodeURIComponent(t)}`,{method:`DELETE`});if(!e.ok){let t=`Delete failed`;try{let n=await e.json();n.error&&(t=n.error)}catch{}throw Error(t)}Cs=await Ts(ws),Qo=``,Es(),Do.value=``,z.value=``,hs(fs()),ls={isNew:!0,content:``},Os(),Ho.innerText=`Delete`}catch(e){alert(`Error: `+e.message),Ho.innerText=`Delete`}};var W=new le;W.background=new ze(2236962);var G=new c(60,B.clientWidth/B.clientHeight,.1,2e3);G.position.set(50,50,-50);var K=new Fe({antialias:!0,preserveDrawingBuffer:!0});K.setSize(B.clientWidth,B.clientHeight),K.setPixelRatio(Math.min(window.devicePixelRatio,2)),K.toneMapping=4,K.toneMappingExposure=1,K.shadowMap.enabled=!0,K.shadowMap.type=2,B.appendChild(K.domElement);var As=new he(K),js=new ke(W,G);As.addPass(js);var Ms=new Se(new E(B.clientWidth,B.clientHeight),.1,.1,2);As.addPass(Ms);var Ns=new Ne;As.addPass(Ns);var q=new So(K);q.bounces=10,q.transmissiveBounces=10,q.multipleImportanceSampling=!0,q.filterGlossyFactor=.2;var Ps=new je(G,K.domElement);Ps.addEventListener(`change`,()=>{q!==void 0&&q.updateCamera()});var Fs=null;new Co().load(`./aristea_wreck_puresky_2k.hdr`,e=>{e.mapping=303,W.environment=e,Fs=e;let t=J&&J.checked,n=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!t),Y!==void 0&&(Y.visible=!t),Q!==void 0&&(Q.visible=!t&&n),$!==void 0&&($.visible=!t&&n),t?(W.background=e,W.backgroundBlurriness=.5,W.backgroundIntensity=.3):(W.background=new ze(2236962),W.backgroundBlurriness=0,W.backgroundIntensity=1),q!==void 0&&(q.setScene(W,G),q.updateCamera())},void 0,e=>console.warn(`Error loading HDR:`,e));var J=document.getElementById(`path-tracing-cb`);J&&J.addEventListener(`change`,()=>{let e=J.checked,t=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!e),Y!==void 0&&(Y.visible=!e),Q!==void 0&&(Q.visible=!e&&t),$!==void 0&&($.visible=!e&&t),e?Fs&&(W.background=Fs,W.backgroundBlurriness=.5,W.backgroundIntensity=.3):(W.background=new ze(2236962),W.backgroundBlurriness=0,W.backgroundIntensity=1),e&&q!==void 0&&q.setScene(W,G)}),Ps.enableDamping=!0,Ps.dampingFactor=.1,Ps.maxDistance=2e3,W.environment=new ye(K).fromScene(new ne,.04).texture,W.environmentIntensity=.8;var Y=new h;W.add(Y);var X=new Le(16777215,1.5);X.castShadow=!0,X.shadow.mapSize.width=2048,X.shadow.mapSize.height=2048,X.shadow.bias=-5e-4,Y.add(X),Y.add(X.target);var Is=new t(16777215,4473924,.6);Is.position.set(0,20,0),Y.add(Is);var Z=new v(new C(2e3,2e3),new n({color:2236962,roughness:.8,metalness:.1}));Z.rotation.x=-Math.PI/2,Z.receiveShadow=!0,W.add(Z);var Q=new i(2e3,100,5592405,3355443);Q.material.transparent=!0,Q.material.opacity=.5,W.add(Q);var $=new ue(100);W.add($),Yo&&Yo.addEventListener(`change`,()=>{let e=Yo.checked;V&&V.traverse(t=>{t.isMesh&&t.material&&(Array.isArray(t.material)?t.material.forEach(t=>{t.wireframe=e}):t.material.wireframe=e)})}),Xo&&Io&&(Xo.addEventListener(`click`,()=>{document.fullscreenElement?document.exitFullscreen():Io.requestFullscreen().catch(e=>{console.warn(`Error attempting to enable fullscreen: ${e.message}`)})}),document.addEventListener(`fullscreenchange`,()=>{setTimeout(()=>window.dispatchEvent(new Event(`resize`)),100)})),Zo&&Zo.addEventListener(`click`,()=>{rs=!0;let e=Zo.innerText;Zo.innerText=`✅`,setTimeout(()=>{Zo.innerText=e},1e3)});function Ls(){U&&(us=!1,U.paused=!0,Wo.innerText=`▶ Play`,vs())}function Rs(e){if(!H||!es[e])return;U&&U.stop();let t=es[e];U=H.clipAction(t),U.play(),U.time=0,H.update(0),us=!0,U.paused=!1,Wo.innerText=`⏸ Pause`,Ko.value=0,vs()}Go.addEventListener(`change`,e=>{Rs(parseInt(e.target.value))}),Wo.addEventListener(`click`,()=>{U&&(us=!us,U.paused=!us,Wo.innerText=us?`⏸ Pause`:`▶ Play`,vs())}),Ko.addEventListener(`mousedown`,()=>{ds=!0,Ls()}),Ko.addEventListener(`mouseup`,()=>{ds=!1}),Ko.addEventListener(`touchstart`,()=>{ds=!0,Ls()},{passive:!0}),Ko.addEventListener(`touchend`,()=>{ds=!1},{passive:!0}),Ko.addEventListener(`input`,e=>{if(Ls(),U){let t=U.getClip().duration;U.time=parseFloat(e.target.value)*t,H&&(H.update(0),J&&J.checked&&q!==void 0&&(Z!==void 0&&(Z.visible=!1),Y!==void 0&&(Y.visible=!1),Q!==void 0&&(Q.visible=!1),$!==void 0&&($.visible=!1),q.setScene(W,G)))}});function zs(){is&&as&&as.state!==`inactive`&&(as.stop(),is=!1),V&&=(H&&=(H.stopAllAction(),H.uncacheRoot(H.getRoot()),null),U=null,W.remove(V),V.traverse(e=>{e.isMesh&&(e.geometry.dispose(),Array.isArray(e.material)?e.material.forEach(e=>e.dispose()):e.material&&e.material.dispose())}),null),vs()}function Bs(e,t){if(!e)return new Ee;e.updateMatrixWorld(!0);let n=new Ee().setFromObject(e);if(!t||t.length===0)return n;let r=new Map;e.traverse(e=>{r.set(e,{position:e.position.clone(),quaternion:e.quaternion.clone(),scale:e.scale.clone()})});let i=new se(e);for(let r of t){let t=i.clipAction(r);t.play();let a=new Set([0,r.duration]);if(r.tracks){for(let e of r.tracks)if(e.times)for(let t=0;t<e.times.length;t++)a.add(e.times[t])}if(r.duration>0)for(let e=0;e<=20;e++)a.add(e/20*r.duration);let o=Array.from(a).sort((e,t)=>e-t);if(o.length>60){let e=[],t=(o.length-1)/59;for(let n=0;n<60;n++)e.push(o[Math.round(n*t)]);o=e}for(let r of o)t.time=r,i.update(0),e.updateMatrixWorld(!0),n.expandByObject(e);t.stop()}return i.stopAllAction(),i.uncacheRoot(e),r.forEach((e,t)=>{t.position.copy(e.position),t.quaternion.copy(e.quaternion),t.scale.copy(e.scale)}),e.updateMatrixWorld(!0),n}function Vs(e){return new Promise((t,n)=>{let r=null;V&&(H&&U&&(U.time=0,H.update(0),J&&J.checked&&q!==void 0&&(Z!==void 0&&(Z.visible=!1),Y!==void 0&&(Y.visible=!1),Q!==void 0&&(Q.visible=!1),$!==void 0&&($.visible=!1),q.setScene(W,G))),r=Bs(V,es)),zs();let i=e;e instanceof Uint8Array&&(i=e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength)),new we().parse(i,``,e=>{V=e.scene,es=e.animations||[];let n=Yo?Yo.checked:!1;es.length?(H=new se(V),Uo.style.display=`flex`,Go.innerHTML=``,es.forEach((e,t)=>{let n=document.createElement(`option`);n.value=t,n.innerText=e.name||`Animation ${t+1}`,Go.appendChild(n)}),Rs(0)):(Uo.style.display=`none`,U=null,vs()),V.traverse(e=>{e.isMesh&&(e.castShadow=!0,e.receiveShadow=!0,e.frustumCulled=!1,e.material&&(Array.isArray(e.material)?e.material.forEach(e=>{e.wireframe=n}):e.material.wireframe=n))}),W.add(V);let i=!0;if(r&&!r.isEmpty()){let e=Bs(V,es);if(!e.isEmpty()){let t=.001;r.min.distanceTo(e.min)<t&&r.max.distanceTo(e.max)<t&&(i=!1)}}if(i)typeof Hs==`function`&&Hs();else{let e=J&&J.checked,t=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!e),Y!==void 0&&(Y.visible=!e),Q!==void 0&&(Q.visible=!e&&t),$!==void 0&&($.visible=!e&&t),q!==void 0&&q.setScene(W,G)}t()},n)})}function Hs(){if(!V)return;let e=Bs(V,es);if(e.isEmpty())return;let t=e.getCenter(new D),n=e.getSize(new D),r=Math.max(n.x,n.y,n.z)||10;Z.position.y=e.min.y-.01,Q.position.y=Z.position.y+.001,$.position.y=Z.position.y+.002;let i=G.fov*(Math.PI/180),o=r/(2*Math.tan(i/2));G.aspect<1&&(o/=G.aspect),o*=1.5,G.near=Math.max(.01,r*.01),G.far=Math.max(2e3,o*10),G.updateProjectionMatrix(),Ps.maxDistance=G.far,G.position.set(t.x+o*.8,t.y+o*.8,t.z-o*.8),G.lookAt(t),Ps.target.copy(t),Ps.update(),X.position.set(t.x+r,t.y+r*1.5,t.z-r),X.target.position.copy(t),X.target.updateMatrixWorld();let s=r*1.5;X.shadow.camera.left=-s,X.shadow.camera.right=s,X.shadow.camera.top=s,X.shadow.camera.bottom=-s,X.shadow.camera.near=.1,X.shadow.camera.far=r*5,X.shadow.camera.updateProjectionMatrix(),W.fog=new a(2236962,o*1.5,o*5);let c=J&&J.checked,l=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!c),Y!==void 0&&(Y.visible=!c),Q!==void 0&&(Q.visible=!c&&l),$!==void 0&&($.visible=!c&&l),q!==void 0&&q.setScene(W,G)}var Us=performance.now();function Ws(){requestAnimationFrame(Ws);let e=performance.now(),t=(e-Us)/1e3;if(Us=e,H&&(!(J&&J.checked)&&!is&&H.update(t),U&&us&&!ds)){let e=U.getClip().duration;if(e>0){let t=U.time%e;t<0&&(t+=e),Ko.value=t/e}}Ps.update();let n=J&&J.checked,r=!Jo||Jo.checked;n?(Z!==void 0&&(Z.visible=!1),Y!==void 0&&(Y.visible=!1),Q!==void 0&&(Q.visible=!1),$!==void 0&&($.visible=!1),q!==void 0&&!is&&q.renderSample()):(Z!==void 0&&(Z.visible=!0),Y!==void 0&&(Y.visible=!0),Q!==void 0&&(Q.visible=r),$!==void 0&&($.visible=r),is||As.render()),rs&&(rs=!1,K.domElement.toBlob(e=>{e&&_s(e,`${ps()}.png`)},`image/png`))}Ws(),window.addEventListener(`resize`,()=>{if(!B||is)return;let e=B.clientWidth,t=B.clientHeight;G.aspect=e/t,G.updateProjectionMatrix(),K.setSize(e,t),As.setSize(e,t),q!==void 0&&q.updateCamera()}),setTimeout(()=>window.dispatchEvent(new Event(`resize`)),100);var Gs=[`localhost`,`127.0.0.1`,``].includes(window.location.hostname);z.placeholder=Gs?`Enter OpenSCAD code here...`:wo,(async function(){if(Gs&&await ks(Lo.value.trim(),!0),window.location.hash&&window.location.hash.length>1)try{let e=await xs(window.location.hash.substring(1));e&&(z.value=e)}catch(e){console.error(`Failed to decode hash`,e)}Os(),setTimeout(()=>hs(fs()),500)})();
|
package/editor/dist/icon.png
CHANGED
|
Binary file
|
package/editor/dist/index.html
CHANGED
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
content="https://iliagrigorevdev.github.io/scad-gltf/icon.png"
|
|
45
45
|
/>
|
|
46
46
|
|
|
47
|
-
<script type="module" crossorigin src="./assets/index-
|
|
47
|
+
<script type="module" crossorigin src="./assets/index-Cy6q5gqL.js"></script>
|
|
48
48
|
<link rel="modulepreload" crossorigin href="./assets/OutputPass-Bvl6NigM.js">
|
|
49
49
|
<link rel="modulepreload" crossorigin href="./assets/prompt-ui-mDTjxcQO.js">
|
|
50
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
50
|
+
<link rel="stylesheet" crossorigin href="./assets/index-B1KAb97r.css">
|
|
51
51
|
<link rel="manifest" href="./manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="./registerSW.js"></script></head>
|
|
52
52
|
<body>
|
|
53
53
|
<div id="app-container">
|
|
@@ -174,6 +174,7 @@
|
|
|
174
174
|
><input type="checkbox" id="show-grid-cb" checked /> Grid</label
|
|
175
175
|
>
|
|
176
176
|
<label><input type="checkbox" id="wireframe-cb" /> Wireframe</label>
|
|
177
|
+
<button id="screenshot-btn" title="Capture Image">📷</button>
|
|
177
178
|
<button id="fullscreen-btn" title="Toggle Full Screen">⛶</button>
|
|
178
179
|
</div>
|
|
179
180
|
<div id="viewer"></div>
|
package/editor/dist/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
if(!self.define){let e,s={};const i=(i,r)=>(i=new URL(i+".js",r).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(r,n)=>{const
|
|
1
|
+
if(!self.define){let e,s={};const i=(i,r)=>(i=new URL(i+".js",r).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(r,n)=>{const l=e||("document"in self?document.currentScript.src:"")||location.href;if(s[l])return;let t={};const o=e=>i(e,l),c={module:{uri:l},exports:t,require:o};s[l]=Promise.all(r.map(e=>c[e]||o(e))).then(e=>(n(...e),t))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"registerSW.js",revision:"402b66900e731ca748771b6fc5e7a068"},{url:"preview.html",revision:"c90d9c25847a398e770ccae0a6dad222"},{url:"index.html",revision:"bb603511d628bd2d3f2ded6c824c31b1"},{url:"icon.png",revision:"a22007c1fb99682b77960b0d210e273e"},{url:"content.js",revision:"fe4d9df0d512444e8fd3e28dce98b2ad"},{url:"content.css",revision:"8a04232febb2faba7b10edc60e2974dd"},{url:"content-loader.js",revision:"d94f1755552d49c1fe3a6da123e8a346"},{url:"aristea_wreck_puresky_2k.hdr",revision:"e764c66f871ab0987f3fac422edc841d"},{url:"assets/prompt-ui-mDTjxcQO.js",revision:null},{url:"assets/preview-fQfL_FJ-.css",revision:null},{url:"assets/preview-C1zc24MJ.js",revision:null},{url:"assets/openscad-CdBCY4mx.wasm",revision:null},{url:"assets/index-Cy6q5gqL.js",revision:null},{url:"assets/index-B1KAb97r.css",revision:null},{url:"assets/OutputPass-Bvl6NigM.js",revision:null},{url:"icon.png",revision:"a22007c1fb99682b77960b0d210e273e"},{url:"manifest.webmanifest",revision:"81dc3433323859b43ec568f4f95cd344"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html")))});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ilia Grigorev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ilia Grigorev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/godot/examples/README.md
CHANGED
|
@@ -80,3 +80,7 @@ scad-godot "A simple 3D platformer where you control a rolling ball collecting c
|
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
_(See the main repository documentation for full details on generating your own AI projects)._
|
|
83
|
+
|
|
84
|
+
## 📜 License
|
|
85
|
+
|
|
86
|
+
These examples and their generated Godot project assets are licensed under the [MIT License](LICENSE).
|