D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
var
/
www
/
html
/
crsys
/
Filename :
catalogo3D_lateral v19 Oficial.html
back
Copy
<!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="utf-8" /> <title>Gerador de Imagens 3D</title> <style> html,body { height:100%; margin:0; background:#222; color:#ddd; font-family:system-ui, Segoe UI, Roboto } header { padding:10px 14px; border-bottom:1px solid #444; } .wrap { display:grid; grid-template-columns:335px 1fr; height:calc(100vh - 48px); } aside { border-right:1px solid #444; padding:12px; overflow:auto; } main { position:relative; } #viewport { position:absolute; inset:0; } fieldset { border:1px solid #444; border-radius:10px; padding:10px; margin:0 0 12px; } legend { padding:0 6px; color:#a6c1ff; font-size:12px; } :root{--w-input: 85px; --gap: 8px; --w-bloco: calc(var(--w-input)*3 + var(--gap)*2);} /* 3 campos lado a lado, largura fixa (não encosta na borda direita) */ .row{display: grid;grid-template-columns: var(--w-input) var(--w-input) var(--w-input); gap: var(--gap); width: var(--w-bloco); justify-content: start;} .row2{display: grid;grid-template-columns: 1fr 1fr; gap: var(--gap); width: 100%; justify-content: start;} .row2 button{ width: 100%;} input,button{ background:#ffffff; color:#0c0c0c; border:1px solid #555; border-radius:0px; padding:8px 8px } input[type="number"]{ width:100%; min-width: 0; } button{ cursor:pointer; font-weight:600 } label{ display:flex; gap:8px; align-items:center } .hint{ font-size:12px; opacity:.8; margin-top:6px } /* Ocultar os controles de Ângulo, mantendo o HTML/JS intactos */ fieldset div:has(> #latDR_ang), fieldset div:has(> #latESQ_ang), fieldset div:has(> #top_ang) { display: none !important; } </style> </head> <body> <header>Gerador de Imagens 3D</header> <div class="wrap"> <aside> <!-- Tampo base --> <fieldset> <legend>Dimensões do tampo base (cm)</legend> <div class="row"> <input id="dimX" type="number" step="0.1" value="40" title="X (largura)"> <input id="dimY" type="number" step="0.1" value="2" title="Y (espessura)"> <input id="dimZ" type="number" step="0.1" value="20" title="Z (profundidade)"> </div> <div class="row2" style="margin-top:8px"> <button id="aplicarDim">Aplicar</button> <button id="resetCamera">Recentrar câmera</button> </div> <!--<div class="hint">Eixos: Direita = X+, Esquerda = X−, Frente = Z−, Trás = Z+, Cima = Y+.</div> --> </fieldset> <!-- Lateral direita --> <fieldset> <legend>Lateral direita <!-- Frente --> (cm)</legend> <label><input type="checkbox" id="latDR_on"> Incluir direita</label> <div class="row" style="margin-top:8px;"> <input id="latDR_x" type="number" step="0.1" value="1.5" title="Espessura X"> <input id="latDR_y" type="number" step="0.1" value="30" title="Altura Y"> <input id="latDR_z" type="number" step="0.1" value="20" title="Profundidade Z"> </div> <div style="display:flex;align-items:center;gap:8px;margin-top:8px;"> <label>Ângulo (0–180°)</label> <input id="latDR_ang" type="number" min="0" max="180" step="1" value="90" style="width:100px"> </div> <button id="latDR_apply" style="margin-top:8px">Aplicar direita</button> <!--<div class="hint">Pivô: (+X/2, −Y/2, −Z/2). Offset: (+sx/2, +sy/2, +sz/2). 90° = vertical.</div> --> </fieldset> <!-- Lateral esquerda --> <fieldset> <legend>Lateral esquerda <!--— Frente --> (cm)</legend> <label><input type="checkbox" id="latESQ_on"> Incluir esquerda</label> <div class="row" style="margin-top:8px;"> <input id="latESQ_x" type="number" step="0.1" value="1.5" title="Espessura X"> <input id="latESQ_y" type="number" step="0.1" value="30" title="Altura Y"> <input id="latESQ_z" type="number" step="0.1" value="20" title="Profundidade Z"> </div> <div style="display:flex;align-items:center;gap:8px;margin-top:8px;"> <label>Ângulo (0–180°)</label> <input id="latESQ_ang" type="number" min="0" max="180" step="1" value="90" style="width:100px"> </div> <button id="latESQ_apply" style="margin-top:8px">Aplicar esquerda</button> <!--<div class="hint">Pivô: (−X/2, −Y/2, −Z/2). Offset: (−sx/2, +sy/2, +sz/2). 90° = vertical.</div> --> </fieldset> <!-- Tampo superior --> <fieldset> <legend>Tampo superior (cm)</legend> <label><input type="checkbox" id="top_on"> Incluir tampo superior</label> <div class="row" style="margin-top:8px;"> <input id="top_x" type="number" step="0.1" value="40" title="Largura X (ignorada; usamos X do tampo)"> <input id="top_y" type="number" step="0.1" value="1.5" title="Espessura Y"> <input id="top_z" type="number" step="0.1" value="20" title="Profundidade Z"> </div> <div style="display:flex;align-items:center;gap:8px;margin-top:8px;"> <label>Abertura (0–120°)</label> <input id="top_ang" type="number" min="0" max="120" step="1" value="0" style="width:100px"> </div> <button id="top_apply" style="margin-top:8px">Aplicar tampo</button> <!--<div class="hint"> Largura do tampo superior = X do tampo base (sem folga).<br> Centro em Y = (altura das laterais) − (espessura do tampo superior)/2. Abre para trás. </div> --> </fieldset> </aside> <main><div id="viewport"></div></main> </div> <script src="https://unpkg.com/three@0.160.0/build/three.min.js"></script> <script> /* ====== CENA ====== */ const renderer = new THREE.WebGLRenderer({ antialias:true, preserveDrawingBuffer:true }); document.getElementById('viewport').appendChild(renderer.domElement); const scene = new THREE.Scene(); scene.background = new THREE.Color(0x222222); const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 5000); scene.add(new THREE.HemisphereLight(0xffffff, 0x222233, 0.9)); const dir = new THREE.DirectionalLight(0xffffff, 0.85); dir.position.set(30,50,30); scene.add(dir); const root = new THREE.Group(); scene.add(root); let baseMesh, rightPivot, rightMesh, leftPivot, leftMesh, topPivot, topMesh; /* ====== BASE ====== */ function makeBase(x=40,y=2,z=20){ if (baseMesh) root.remove(baseMesh); const g = new THREE.BoxGeometry(x,y,z); const m = new THREE.MeshStandardMaterial({ color:0xffffff, roughness:0.85 }); baseMesh = new THREE.Mesh(g,m); root.add(baseMesh); render(); } makeBase(); /* ====== CÂMERA / ORBIT ====== */ const target = new THREE.Vector3(0,0,0); let radius=120, theta=THREE.MathUtils.degToRad(45), phi=THREE.MathUtils.degToRad(35); function updateCamera(){ const x = target.x + radius*Math.sin(phi)*Math.cos(theta); const y = target.y + radius*Math.cos(phi); const z = target.z + radius*Math.sin(phi)*Math.sin(theta); camera.position.set(x,y,z); camera.lookAt(target); } /* === ENQUADRAMENTO AUTOMÁTICO (FOV horizontal) === */ function fitCameraToScene(margin = 2.0) { // garante que as matrizes estejam atualizadas root.updateWorldMatrix(true, true); const box = new THREE.Box3().setFromObject(root); if (box.isEmpty()) { updateCamera(); render(); return; } const size = box.getSize(new THREE.Vector3()); // x=largura, y=altura, z=profundidade const center = box.getCenter(new THREE.Vector3()); // centro do modelo // mira no centro do modelo target.copy(center); // FOV vertical e horizontal (rad) const vFov = camera.fov * Math.PI / 180; const hFov = 2 * Math.atan(Math.tan(vFov / 2) * camera.aspect); // distâncias necessárias para caber altura, largura e profundidade const distH = (size.y * 0.5) / Math.tan(vFov / 2); const distW = (size.x * 0.5) / Math.tan(hFov / 2); const distZ = (size.z * 0.5) / Math.tan(vFov / 2); radius = Math.max(distH, distW, distZ) * margin; updateCamera(); render(); } // shim: mantém suas chamadas antigas function focusCamera(/* {x,y,z} */){ fitCameraToScene(1.8); } updateCamera(); /* ====== CONTROLE DO MOUSE ====== */ let dragging=false, lx=0, ly=0; addEventListener('pointerdown',e=>{dragging=true; lx=e.clientX; ly=e.clientY;}); addEventListener('pointermove',e=>{ if(!dragging) return; const dx=e.clientX-lx, dy=e.clientY-ly; lx=e.clientX; ly=e.clientY; theta -= dx*0.005; phi -= dy*0.005; phi = Math.max(THREE.MathUtils.degToRad(5), Math.min(THREE.MathUtils.degToRad(175), phi)); updateCamera(); render(); }); addEventListener('pointerup',()=>dragging=false); addEventListener('wheel',e=>{ radius *= (e.deltaY<0?0.9:1.1); updateCamera(); render(); },{passive:true}); /* ====== RESIZE ====== */ function onResize(){ const w = document.getElementById('viewport').clientWidth || (innerWidth-460); const h = document.getElementById('viewport').clientHeight || (innerHeight-48); renderer.setSize(w,h,false); camera.aspect = w/h; camera.updateProjectionMatrix(); fitCameraToScene(1.8); } addEventListener('resize', onResize); onResize(); function render(){ renderer.render(scene,camera); } /* ====== UI: APLICAR / RECENTRAR ====== */ aplicarDim.onclick = ()=>{ const x=+dimX.value||40, y=+dimY.value||2, z=+dimZ.value||20; makeBase(x,y,z); if (latDR_on.checked) applyRight(); if (latESQ_on.checked) applyLeft(); if (top_on.checked) applyTop(); fitCameraToScene(1.8); }; resetCamera.onclick = ()=>fitCameraToScene(1.8); /* ====== LATERAL DIREITA ====== */ latDR_apply.onclick = applyRight; latDR_on.onchange = applyRight; latDR_ang.oninput = applyRight; function applyRight(){ const bx=+dimX.value||40, by=+dimY.value||2, bz=+dimZ.value||20; const sx=Math.max(0.1,+latDR_x.value||1.5); const sy=Math.max(0.1,+latDR_y.value||30); const sz=Math.max(0.1,+latDR_z.value||bz); const ang=Math.max(0,Math.min(180,+latDR_ang.value||90)); if(!latDR_on.checked){ if(rightPivot){ root.remove(rightPivot); rightPivot=null; rightMesh=null; render(); fitCameraToScene(1.8); } return; } if(!rightPivot){ rightPivot=new THREE.Object3D(); root.add(rightPivot); } rightPivot.position.set(+bx/2, -by/2, -bz/2); const m=new THREE.MeshStandardMaterial({ color:0xffffff, roughness:0.85 }); if(!rightMesh){ rightMesh=new THREE.Mesh(new THREE.BoxGeometry(sx,sy,sz),m); rightPivot.add(rightMesh); } else { rightMesh.material=m; rightMesh.geometry.dispose(); rightMesh.geometry=new THREE.BoxGeometry(sx,sy,sz); } rightMesh.position.set(+sx/2, +sy/2, +sz/2); rightPivot.rotation.set(0,0,THREE.MathUtils.degToRad(90-ang)); render(); fitCameraToScene(1.8); } /* ====== LATERAL ESQUERDA ====== */ latESQ_apply.onclick = applyLeft; latESQ_on.onchange = applyLeft; latESQ_ang.oninput = applyLeft; function applyLeft(){ const bx=+dimX.value||40, by=+dimY.value||2, bz=+dimZ.value||20; const sx=Math.max(0.1,+latESQ_x.value||1.5); const sy=Math.max(0.1,+latESQ_y.value||30); const sz=Math.max(0.1,+latESQ_z.value||bz); const ang=Math.max(0,Math.min(180,+latESQ_ang.value||90)); if(!latESQ_on.checked){ if(leftPivot){ root.remove(leftPivot); leftPivot=null; leftMesh=null; render(); fitCameraToScene(1.8); } return; } if(!leftPivot){ leftPivot=new THREE.Object3D(); root.add(leftPivot); } leftPivot.position.set(-bx/2, -by/2, -bz/2); const m=new THREE.MeshStandardMaterial({ color:0xffffff, roughness:0.85 }); if(!leftMesh){ leftMesh=new THREE.Mesh(new THREE.BoxGeometry(sx,sy,sz),m); leftPivot.add(leftMesh); } else { leftMesh.material=m; leftMesh.geometry.dispose(); leftMesh.geometry=new THREE.BoxGeometry(sx,sy,sz); } leftMesh.position.set(-sx/2, +sy/2, +sz/2); leftPivot.rotation.set(0,0,THREE.MathUtils.degToRad(-(90-ang))); render(); fitCameraToScene(1.8); } /* ====== TAMPO SUPERIOR ====== */ top_apply.onclick = applyTop; top_on.onchange = applyTop; top_ang.oninput = applyTop; function applyTop(){ const bx=+dimX.value||40, by=+dimY.value||2, bz=+dimZ.value||20; let tX = Math.max(0.1, +top_x.value || bx); const tY = Math.max(0.1, +top_y.value || 1.5); const tZ = Math.max(0.1, +top_z.value || bz); const ang= Math.max(0, Math.min(120, +top_ang.value || 0)); const R_on = latDR_on.checked, L_on = latESQ_on.checked; const R_sy = R_on ? (+latDR_y.value || 0) : 0; const L_sy = L_on ? (+latESQ_y.value || 0) : 0; if(!top_on.checked){ if(topPivot){ root.remove(topPivot); topPivot=null; topMesh=null; render(); fitCameraToScene(1.8); } return; } if(!topPivot){ topPivot=new THREE.Object3D(); root.add(topPivot); } // altura do topo do vão: usa a menor das duas se ambas existirem let alturaLaterais = 0; if (R_on && L_on) alturaLaterais = Math.min(R_sy, L_sy); else if (R_on) alturaLaterais = R_sy; else if (L_on) alturaLaterais = L_sy; const yTopoVao = -by/2 + alturaLaterais; topPivot.position.set(0, yTopoVao, +bz/2); // largura sem folga (laterais crescem para fora): igual a X do tampo tX = bx; const m = new THREE.MeshStandardMaterial({ color:0xffffff, roughness:0.85 }); if(!topMesh){ topMesh=new THREE.Mesh(new THREE.BoxGeometry(tX,tY,tZ), m); topPivot.add(topMesh); } else { topMesh.material=m; topMesh.geometry.dispose(); topMesh.geometry=new THREE.BoxGeometry(tX,tY,tZ); } // encaixe: centro em X=0, Y= -tY/2 (entra no vão), Z= -tZ/2 (encosta atrás) topMesh.position.set(0, -tY/2, -tZ/2); // abertura para trás topPivot.rotation.set(THREE.MathUtils.degToRad(-ang), 0, 0); render(); fitCameraToScene(1.8); } /* ====== INICIAL ====== */ fitCameraToScene(1.8); // começa já enquadrado </script> </body> </html>