Luis-Filipe commited on
Commit
bc2f2bd
·
verified ·
1 Parent(s): 111e471

Upload 2 files

Browse files
Files changed (2) hide show
  1. index.html +524 -19
  2. three.js +0 -0
index.html CHANGED
@@ -1,19 +1,524 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="pt-BR">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>3D Builder Básico - Three.js</title>
7
+ <!-- Carregamento do Tailwind CSS para estilo -->
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <!-- Carregamento do Three.js -->
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
11
+ <!-- Carregamento dos controles de órbita (necessário para navegação na cena) -->
12
+ <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
13
+ <!-- Carregamento dos controles de transformação (essencial para mover/rotacionar/escalar objetos) -->
14
+ <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/TransformControls.js"></script>
15
+ <style>
16
+ /* Estilos customizados para garantir que o canvas ocupe o espaço correto */
17
+ body { margin: 0; overflow: hidden; font-family: 'Inter', sans-serif; }
18
+ #canvas-container { flex-grow: 1; position: relative; }
19
+ canvas { display: block; }
20
+ /* Estilo para ícones de Lucide (usados aqui via SVG inline para simplicidade) */
21
+ .icon { width: 24px; height: 24px; }
22
+ </style>
23
+ </head>
24
+ <body class="bg-gray-50 flex h-screen text-gray-800">
25
+
26
+ <!-- Painel de Ferramentas (Toolbar Superior) -->
27
+ <div id="toolbar" class="absolute top-0 left-0 right-0 bg-white shadow-lg p-3 z-10 flex space-x-4 border-b border-gray-200">
28
+ <button onclick="addShape('box')" class="toolbar-btn group">
29
+ <svg class="icon group-hover:text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10m0-10l8-4m-8 4l-8-4m4-4h4a2 2 0 012 2v4a2 2 0 01-2 2h-4a2 2 0 01-2-2V7a2 2 0 012-2z"></path></svg>
30
+ <span class="tooltip">Adicionar Cubo</span>
31
+ </button>
32
+ <button onclick="addShape('sphere')" class="toolbar-btn group">
33
+ <svg class="icon group-hover:text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zM12 2v20"></path></svg>
34
+ <span class="tooltip">Adicionar Esfera</span>
35
+ </button>
36
+
37
+ <div class="h-full border-l border-gray-300 mx-4"></div>
38
+
39
+ <!-- Ferramentas de Transformação -->
40
+ <button onclick="setTransformMode('translate')" class="toolbar-btn group" id="btn-translate">
41
+ <svg class="icon group-hover:text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path></svg>
42
+ <span class="tooltip">Mover</span>
43
+ </button>
44
+ <button onclick="setTransformMode('rotate')" class="toolbar-btn group" id="btn-rotate">
45
+ <svg class="icon group-hover:text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h5M20 20v-5h-5M4 13a8 8 0 1016 0A8 8 0 004 13z"></path></svg>
46
+ <span class="tooltip">Rotacionar</span>
47
+ </button>
48
+ <button onclick="setTransformMode('scale')" class="toolbar-btn group" id="btn-scale">
49
+ <svg class="icon group-hover:text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-4m0 0l4-4m-4 4l-4-4m8 4V4M6 6v4m0 0l-4 4m4-4l4 4"></path></svg>
50
+ <span class="tooltip">Escalar</span>
51
+ </button>
52
+
53
+ <div class="h-full border-l border-gray-300 mx-4"></div>
54
+
55
+ <!-- Operações Avançadas (Placeholders) -->
56
+ <button onclick="handleBooleanOperation()" class="toolbar-btn group text-red-500 hover:text-red-700">
57
+ <svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0a2 2 0 002 2h6a2 2 0 002-2m-6 2l-2 2"></path></svg>
58
+ <span class="tooltip">Booleana (Subtrair)</span>
59
+ </button>
60
+ <button onclick="handleMeshRepair()" class="toolbar-btn group text-yellow-500 hover:text-yellow-700">
61
+ <svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 10l-4-4m0 0l-4 4m4-4v10m10 4l-4-4m0 0l-4 4m4-4v4"></path></svg>
62
+ <span class="tooltip">Reparo de Malha (WASM)</span>
63
+ </button>
64
+
65
+ <div class="h-full border-l border-gray-300 mx-4"></div>
66
+
67
+ <!-- Importar/Exportar -->
68
+ <label for="file-input" class="toolbar-btn group cursor-pointer text-green-500 hover:text-green-700">
69
+ <svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path></svg>
70
+ <span class="tooltip">Importar (STL/OBJ/GLTF)</span>
71
+ <input type="file" id="file-input" class="hidden" onchange="handleFileImport(event)" accept=".obj, .gltf, .glb, .stl">
72
+ </label>
73
+ <button onclick="exportScene()" class="toolbar-btn group text-indigo-500 hover:text-indigo-700">
74
+ <svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2004/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m0 0l-4 4m4-4l4 4m-4-4v4m0 0h4m-4 0v-4"></path></svg>
75
+ <span class="tooltip">Exportar (JSON)</span>
76
+ </button>
77
+ </div>
78
+
79
+ <!-- Container Principal: Canvas 3D e Painel de Propriedades -->
80
+ <div class="flex flex-1 pt-16">
81
+ <!-- Área do Canvas 3D -->
82
+ <div id="canvas-container" class="bg-gray-100 flex-1 relative">
83
+ <div id="loading-message" class="absolute inset-0 flex items-center justify-center bg-gray-100/90 z-20 hidden">
84
+ <p class="text-xl font-semibold text-blue-600">Carregando Modelo...</p>
85
+ </div>
86
+ </div>
87
+
88
+ <!-- Painel de Propriedades (Barra Lateral Direita) -->
89
+ <div id="properties-panel" class="w-80 bg-white p-4 shadow-xl overflow-y-auto border-l border-gray-200">
90
+ <h2 class="text-xl font-bold mb-4 text-blue-700">Propriedades do Objeto</h2>
91
+ <div id="selection-info" class="text-gray-500 italic">
92
+ Nenhum objeto selecionado. Clique em um modelo para editar.
93
+ </div>
94
+
95
+ <!-- Campos de Edição (Inicialmente Ocultos) -->
96
+ <div id="edit-fields" class="mt-6 space-y-3 hidden">
97
+ <div>
98
+ <label class="block text-sm font-medium text-gray-700">Nome:</label>
99
+ <input type="text" id="prop-name" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2 border" onchange="updateObjectProperty('name', this.value)">
100
+ </div>
101
+ <div>
102
+ <label class="block text-sm font-medium text-gray-700">Posição (X):</label>
103
+ <input type="number" id="prop-px" step="0.1" class="prop-input" onchange="updateObjectProperty('position.x', parseFloat(this.value))">
104
+ </div>
105
+ <div>
106
+ <label class="block text-sm font-medium text-gray-700">Posição (Y):</label>
107
+ <input type="number" id="prop-py" step="0.1" class="prop-input" onchange="updateObjectProperty('position.y', parseFloat(this.value))">
108
+ </div>
109
+ <div>
110
+ <label class="block text-sm font-medium text-gray-700">Posição (Z):</label>
111
+ <input type="number" id="prop-pz" step="0.1" class="prop-input" onchange="updateObjectProperty('position.z', parseFloat(this.value))">
112
+ </div>
113
+ <!-- ... Adicionar campos para Rotação e Escala se necessário ... -->
114
+ <button onclick="deleteSelectedObject()" class="w-full bg-red-500 hover:bg-red-600 text-white font-semibold py-2 px-4 rounded-lg shadow-md transition duration-150 mt-4">
115
+ Deletar Objeto
116
+ </button>
117
+ </div>
118
+ </div>
119
+ </div>
120
+
121
+ <!-- Estilos de botão e tooltip -->
122
+ <style>
123
+ .toolbar-btn {
124
+ position: relative;
125
+ padding: 8px;
126
+ border-radius: 8px;
127
+ background-color: #f3f4f6;
128
+ transition: all 0.15s;
129
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
130
+ }
131
+ .toolbar-btn:hover {
132
+ background-color: #e5e7eb;
133
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
134
+ }
135
+ .tooltip {
136
+ position: absolute;
137
+ top: 120%;
138
+ left: 50%;
139
+ transform: translateX(-50%);
140
+ background-color: #374151;
141
+ color: white;
142
+ padding: 4px 8px;
143
+ border-radius: 4px;
144
+ font-size: 0.75rem;
145
+ white-space: nowrap;
146
+ opacity: 0;
147
+ pointer-events: none;
148
+ transition: opacity 0.2s;
149
+ }
150
+ .toolbar-btn:hover .tooltip {
151
+ opacity: 1;
152
+ }
153
+ .prop-input {
154
+ margin-top: 4px;
155
+ display: block;
156
+ width: 100%;
157
+ border-radius: 6px;
158
+ border: 1px solid #d1d5db;
159
+ padding: 8px;
160
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05);
161
+ transition: border-color 0.15s, box-shadow 0.15s;
162
+ }
163
+ .prop-input:focus {
164
+ border-color: #3b82f6;
165
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5);
166
+ outline: none;
167
+ }
168
+ .active-mode {
169
+ background-color: #bfdbfe; /* blue-200 */
170
+ }
171
+ </style>
172
+
173
+ <script>
174
+ let scene, camera, renderer, controls, transformControls;
175
+ let selectedObject = null;
176
+ let raycaster, mouse;
177
+
178
+ const container = document.getElementById('canvas-container');
179
+ const propertiesPanel = document.getElementById('properties-panel');
180
+ const editFields = document.getElementById('edit-fields');
181
+ const selectionInfo = document.getElementById('selection-info');
182
+ // CORREÇÃO: Adicionando a definição da toolbar
183
+ const toolbar = document.getElementById('toolbar');
184
+
185
+ // --- 1. Inicialização da Cena Three.js ---
186
+ function init() {
187
+ // Cena
188
+ scene = new THREE.Scene();
189
+ scene.background = new THREE.Color(0xf0f0f0);
190
+ scene.add(new THREE.GridHelper(100, 100, 0xcccccc, 0x888888)); // Plano de fundo com grade
191
+
192
+ // Câmera
193
+ camera = new THREE.PerspectiveCamera(70, container.clientWidth / container.clientHeight, 0.1, 1000);
194
+ camera.position.set(20, 15, 20);
195
+ camera.lookAt(0, 0, 0);
196
+
197
+ // Renderer
198
+ renderer = new THREE.WebGLRenderer({ antialias: true });
199
+ renderer.setSize(container.clientWidth, container.clientHeight);
200
+ renderer.setPixelRatio(window.devicePixelRatio);
201
+ container.appendChild(renderer.domElement);
202
+
203
+ // Iluminação
204
+ const ambientLight = new THREE.AmbientLight(0x606060);
205
+ scene.add(ambientLight);
206
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
207
+ directionalLight.position.set(10, 20, 10);
208
+ scene.add(directionalLight);
209
+
210
+ // Controles de Órbita (para navegação)
211
+ controls = new THREE.OrbitControls(camera, renderer.domElement);
212
+ controls.enableDamping = true; // para um movimento suave
213
+
214
+ // Controles de Transformação (para manipulação de objetos)
215
+ transformControls = new THREE.TransformControls(camera, renderer.domElement);
216
+ scene.add(transformControls);
217
+
218
+ // Desabilita os controles de órbita quando os controles de transformação estão ativos
219
+ transformControls.addEventListener('dragging-changed', function (event) {
220
+ controls.enabled = !event.value;
221
+ });
222
+
223
+ // Evento para atualizar o painel de propriedades ao transformar
224
+ transformControls.addEventListener('objectChange', updatePropertiesPanel);
225
+
226
+ // Eventos de Janela
227
+ window.addEventListener('resize', onWindowResize);
228
+ document.addEventListener('click', onDocumentClick, false);
229
+
230
+ // Raycaster para seleção de objetos
231
+ raycaster = new THREE.Raycaster();
232
+ mouse = new THREE.Vector2();
233
+
234
+ // Adiciona um objeto inicial para começar
235
+ addShape('box');
236
+
237
+ // Define o modo de transformação inicial aqui, após a inicialização do transformControls.
238
+ setTransformMode('translate');
239
+ }
240
+
241
+ // --- 2. Loop de Renderização ---
242
+ function animate() {
243
+ requestAnimationFrame(animate);
244
+ controls.update(); // Atualiza os controles de órbita
245
+ renderer.render(scene, camera);
246
+ }
247
+
248
+ function onWindowResize() {
249
+ camera.aspect = container.clientWidth / container.clientHeight;
250
+ camera.updateProjectionMatrix();
251
+ renderer.setSize(container.clientWidth, container.clientHeight);
252
+ }
253
+
254
+ // --- 3. Criação de Formas ---
255
+ let objectCount = 0;
256
+ function addShape(type) {
257
+ objectCount++;
258
+ let geometry, material;
259
+ const color = Math.random() * 0xffffff;
260
+
261
+ material = new THREE.MeshStandardMaterial({
262
+ color: color,
263
+ roughness: 0.5,
264
+ metalness: 0.5
265
+ });
266
+
267
+ if (type === 'box') {
268
+ geometry = new THREE.BoxGeometry(5, 5, 5);
269
+ } else if (type === 'sphere') {
270
+ geometry = new THREE.SphereGeometry(3, 32, 32);
271
+ }
272
+
273
+ const mesh = new THREE.Mesh(geometry, material);
274
+ mesh.position.y = 2.5; // Coloca o objeto no chão (acima da grade)
275
+ mesh.name = `${type.charAt(0).toUpperCase() + type.slice(1)}_${objectCount}`;
276
+ mesh.userData.id = objectCount; // ID único para identificação
277
+
278
+ scene.add(mesh);
279
+ selectObject(mesh); // Seleciona o objeto recém-criado
280
+ }
281
+
282
+ // --- 4. Seleção e Edição de Objeto ---
283
+ function selectObject(mesh) {
284
+ if (selectedObject) {
285
+ // Remove a borda de seleção do objeto anterior (usando userData para material original)
286
+ selectedObject.material.emissive.set(0x000000);
287
+ }
288
+
289
+ if (mesh && mesh.isMesh && mesh.parent !== transformControls) {
290
+ selectedObject = mesh;
291
+ // Adiciona uma borda emissiva para indicar seleção
292
+ selectedObject.material.emissive.set(0x0080ff); // Cor azul de destaque
293
+
294
+ // Anexa os controles de transformação
295
+ transformControls.attach(selectedObject);
296
+ setTransformMode(transformControls.mode); // Garante que o modo atual seja aplicado
297
+
298
+ // Atualiza o painel
299
+ updatePropertiesPanel();
300
+ } else {
301
+ selectedObject = null;
302
+ transformControls.detach();
303
+ updatePropertiesPanel(); // Limpa o painel
304
+ }
305
+ }
306
+
307
+ function onDocumentClick(event) {
308
+ // Verifica se o clique ocorreu dentro do canvas e fora do painel de propriedades/toolbar
309
+ // Agora a variável 'toolbar' está definida.
310
+ if (!container.contains(event.target) || propertiesPanel.contains(event.target) || toolbar.contains(event.target)) return;
311
+
312
+ // Transforma as coordenadas do mouse de tela para as coordenadas normalizadas (-1 a +1)
313
+ mouse.x = (event.clientX / container.clientWidth) * 2 - 1;
314
+ mouse.y = - (event.clientY / container.clientHeight) * 2 + 1;
315
+
316
+ raycaster.setFromCamera(mouse, camera);
317
+
318
+ // Filtra apenas os objetos Mesh na cena que não são o GridHelper
319
+ const intersects = raycaster.intersectObjects(scene.children.filter(obj => obj.isMesh), true);
320
+
321
+ if (intersects.length > 0) {
322
+ // Objeto clicado
323
+ selectObject(intersects[0].object);
324
+ } else {
325
+ // Clicado fora de qualquer objeto
326
+ selectObject(null);
327
+ }
328
+ }
329
+
330
+ function updatePropertiesPanel() {
331
+ if (selectedObject) {
332
+ selectionInfo.textContent = `Objeto Selecionado: ${selectedObject.name}`;
333
+ editFields.classList.remove('hidden');
334
+
335
+ // Atualiza campos de input
336
+ document.getElementById('prop-name').value = selectedObject.name;
337
+ document.getElementById('prop-px').value = selectedObject.position.x.toFixed(3);
338
+ document.getElementById('prop-py').value = selectedObject.position.y.toFixed(3);
339
+ document.getElementById('prop-pz').value = selectedObject.position.z.toFixed(3);
340
+ // ... mais propriedades
341
+ } else {
342
+ selectionInfo.textContent = 'Nenhum objeto selecionado. Clique em um modelo para editar.';
343
+ editFields.classList.add('hidden');
344
+ }
345
+ }
346
+
347
+ function updateObjectProperty(property, value) {
348
+ if (!selectedObject) return;
349
+
350
+ if (property === 'name') {
351
+ selectedObject.name = value;
352
+ } else if (property.startsWith('position')) {
353
+ const axis = property.split('.')[1]; // 'x', 'y', ou 'z'
354
+ selectedObject.position[axis] = value;
355
+ // Força uma atualização para refletir a mudança no controle de transformação
356
+ transformControls.updateMatrixWorld();
357
+ }
358
+ // Chama a função de novo para manter a sincronização visual
359
+ updatePropertiesPanel();
360
+ }
361
+
362
+ function deleteSelectedObject() {
363
+ if (!selectedObject) return;
364
+
365
+ // 1. Remove os controles
366
+ transformControls.detach();
367
+
368
+ // 2. Remove da cena
369
+ scene.remove(selectedObject);
370
+
371
+ // 3. Limpa a memória
372
+ if (selectedObject.geometry) selectedObject.geometry.dispose();
373
+ if (selectedObject.material) selectedObject.material.dispose();
374
+
375
+ // 4. Limpa a seleção
376
+ selectedObject = null;
377
+ updatePropertiesPanel();
378
+
379
+ showMessage('Objeto deletado com sucesso!', 'bg-green-500');
380
+ }
381
+
382
+ // --- 5. Controles de Transformação ---
383
+ function setTransformMode(mode) {
384
+ transformControls.setMode(mode);
385
+
386
+ // Atualiza o estado visual dos botões
387
+ document.querySelectorAll('.toolbar-btn').forEach(btn => btn.classList.remove('active-mode'));
388
+ document.getElementById(`btn-${mode}`).classList.add('active-mode');
389
+
390
+ showMessage(`Modo de Transformação: ${mode.toUpperCase()}`);
391
+ }
392
+
393
+ // --- 6. Funções de Placeholder para Funcionalidades Avançadas ---
394
+
395
+ function handleBooleanOperation() {
396
+ if (!selectedObject) {
397
+ showMessage('Selecione um objeto para iniciar uma Operação Booleana.', 'bg-yellow-500');
398
+ return;
399
+ }
400
+ // REALIZAÇÃO DE BOOLEANA:
401
+ // Esta é a parte que exigiria uma biblioteca CSG (Constructive Solid Geometry)
402
+ // como three-bvh-csg ou three-csg-ts, que são complexas de carregar em um
403
+ // único arquivo HTML sem um sistema de módulos.
404
+
405
+ // Logica de Placeholder:
406
+ console.log(`Iniciando Booleana em: ${selectedObject.name}`);
407
+ showMessage('Operação Booleana (Placeholder): A lógica CSG avançada requer bibliotecas externas.', 'bg-red-500');
408
+
409
+ // Exemplo de como a lógica CSG (Subtração) funcionaria:
410
+ /*
411
+ const subMesh = getSecondMeshForOperation();
412
+ if (subMesh) {
413
+ const result = CSG.subtract(selectedObject, subMesh);
414
+ scene.remove(selectedObject);
415
+ scene.remove(subMesh);
416
+ scene.add(result);
417
+ selectObject(result);
418
+ result.name = "ResultadoBooleana_" + objectCount++;
419
+ }
420
+ */
421
+ }
422
+
423
+ function handleMeshRepair() {
424
+ if (!selectedObject) {
425
+ showMessage('Selecione um objeto para iniciar o Reparo de Malha.', 'bg-yellow-500');
426
+ return;
427
+ }
428
+ // REPARO DE MALHA (WASM):
429
+ // Esta funcionalidade envolveria o carregamento e a execução de um módulo
430
+ // WebAssembly (WASM) compilado a partir de código C++ (por exemplo, usando
431
+ // a biblioteca Manifold ou libigl) para corrigir malhas não-manifold.
432
+
433
+ // Logica de Placeholder:
434
+ console.log(`Iniciando Reparo de Malha WASM em: ${selectedObject.name}`);
435
+ showMessage('Reparo de Malha (Placeholder): Simulação de reparo via WebAssembly concluída.', 'bg-green-500');
436
+ }
437
+
438
+ // --- 7. Importar/Exportar (Simplificado) ---
439
+
440
+ function handleFileImport(event) {
441
+ const file = event.target.files[0];
442
+ if (!file) return;
443
+
444
+ showMessage(`Importando arquivo: ${file.name}. Formatos STL/OBJ/GLTF seriam carregados aqui.`, 'bg-blue-500');
445
+
446
+ // Logica de Placeholder para carregamento real:
447
+ // Um carregador como THREE.STLLoader ou THREE.GLTFLoader seria instanciado
448
+ // e usado para analisar o arquivo e adicionar a malha resultante à cena.
449
+ /*
450
+ const reader = new FileReader();
451
+ reader.onload = (e) => {
452
+ // Exemplo: new THREE.STLLoader().parse(e.target.result);
453
+ };
454
+ reader.readAsArrayBuffer(file);
455
+ */
456
+ }
457
+
458
+ function exportScene() {
459
+ const objectsToExport = [];
460
+
461
+ // Itera sobre todos os objetos Mesh na cena (excluindo TransformControls e GridHelper)
462
+ scene.traverse(obj => {
463
+ if (obj.isMesh) {
464
+ objectsToExport.push({
465
+ name: obj.name,
466
+ uuid: obj.uuid,
467
+ position: obj.position.toArray(),
468
+ rotation: obj.rotation.toArray(),
469
+ scale: obj.scale.toArray(),
470
+ // A geometria real e material seriam muito grandes para JSON simples,
471
+ // então exportamos apenas os metadados de transformação.
472
+ type: obj.geometry.type || 'unknown'
473
+ });
474
+ }
475
+ });
476
+
477
+ const data = {
478
+ metadata: {
479
+ generator: "3D Builder Básico Three.js",
480
+ date: new Date().toISOString(),
481
+ objectCount: objectsToExport.length
482
+ },
483
+ objects: objectsToExport
484
+ };
485
+
486
+ const json = JSON.stringify(data, null, 2);
487
+ const blob = new Blob([json], { type: 'application/json' });
488
+ const url = URL.createObjectURL(blob);
489
+
490
+ // Cria um link e simula o download
491
+ const a = document.createElement('a');
492
+ a.href = url;
493
+ a.download = 'cena_3d_exportada.json';
494
+ document.body.appendChild(a);
495
+ a.click();
496
+ document.body.removeChild(a);
497
+ URL.revokeObjectURL(url);
498
+
499
+ showMessage('Cena exportada como JSON (Metadados do Modelo).', 'bg-indigo-500');
500
+ }
501
+
502
+ // --- 8. Mensagens de Feedback (Substitui alert()) ---
503
+ function showMessage(message, bgColorClass = 'bg-blue-500') {
504
+ const messageBox = document.createElement('div');
505
+ messageBox.textContent = message;
506
+ messageBox.className = `fixed bottom-4 right-4 ${bgColorClass} text-white p-3 rounded-lg shadow-xl z-50 transition-opacity duration-300`;
507
+ document.body.appendChild(messageBox);
508
+
509
+ setTimeout(() => {
510
+ messageBox.style.opacity = '0';
511
+ messageBox.addEventListener('transitionend', () => messageBox.remove());
512
+ }, 3000);
513
+ }
514
+
515
+ // Inicia a aplicação Three.js
516
+ window.onload = function() {
517
+ init();
518
+ animate();
519
+ };
520
+
521
+ </script>
522
+
523
+ </body>
524
+ </html>
three.js ADDED
The diff for this file is too large to render. See raw diff