| <!DOCTYPE html> |
| <html lang="fr"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Image2STL - Convertir des images en modèles 3D</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/exporters/STLExporter.min.js"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| .dropzone { |
| border: 2px dashed #4b5563; |
| transition: all 0.3s ease; |
| } |
| .dropzone.active { |
| border-color: #3b82f6; |
| background-color: rgba(59, 130, 246, 0.05); |
| } |
| .preview-container { |
| transition: all 0.3s ease; |
| opacity: 0; |
| height: 0; |
| overflow: hidden; |
| } |
| .preview-container.show { |
| opacity: 1; |
| height: auto; |
| } |
| #stlPreview { |
| width: 100%; |
| height: 400px; |
| background-color: #f3f4f6; |
| } |
| .slider-thumb::-webkit-slider-thumb { |
| -webkit-appearance: none; |
| appearance: none; |
| width: 20px; |
| height: 20px; |
| background: #3b82f6; |
| cursor: pointer; |
| border-radius: 50%; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50 min-h-screen"> |
| |
| <nav class="bg-white shadow-sm"> |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> |
| <div class="flex justify-between h-16"> |
| <div class="flex items-center"> |
| <i class="fas fa-cube text-blue-500 text-2xl mr-2"></i> |
| <span class="text-xl font-bold text-gray-800">Image2STL</span> |
| </div> |
| <div class="hidden md:flex items-center space-x-8"> |
| <a href="#" class="text-gray-700 hover:text-blue-500">Accueil</a> |
| <a href="#" class="text-gray-700 hover:text-blue-500">Comment ça marche</a> |
| <a href="#" class="text-gray-700 hover:text-blue-500">Exemples</a> |
| <a href="#" class="text-gray-700 hover:text-blue-500">Contact</a> |
| </div> |
| <button class="md:hidden text-gray-500"> |
| <i class="fas fa-bars text-xl"></i> |
| </button> |
| </div> |
| </div> |
| </nav> |
|
|
| |
| <div class="bg-gradient-to-r from-blue-500 to-indigo-600 text-white py-16"> |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center"> |
| <h1 class="text-4xl md:text-5xl font-bold mb-6">Transformez vos images en modèles 3D</h1> |
| <p class="text-xl mb-8 max-w-3xl mx-auto">Convertissez simplement n'importe quelle image en fichier STL prêt pour l'impression 3D</p> |
| <button id="startConversion" class="bg-white text-blue-600 font-bold py-3 px-8 rounded-full hover:bg-gray-100 transition duration-300 transform hover:scale-105"> |
| Commencer maintenant <i class="fas fa-arrow-right ml-2"></i> |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12"> |
| <div class="bg-white rounded-xl shadow-md overflow-hidden"> |
| <div class="p-6 md:p-8"> |
| <h2 class="text-2xl font-bold text-gray-800 mb-6">Convertisseur Image vers STL</h2> |
| |
| <div class="grid grid-cols-1 lg:grid-cols-2 gap-8"> |
| |
| <div> |
| <div id="dropzone" class="dropzone rounded-lg p-8 text-center cursor-pointer mb-6"> |
| <i class="fas fa-cloud-upload-alt text-4xl text-blue-500 mb-4"></i> |
| <h3 class="text-lg font-semibold mb-2">Glissez-déposez votre image ici</h3> |
| <p class="text-gray-500 mb-4">ou</p> |
| <input type="file" id="fileInput" accept="image/*" multiple class="hidden"> |
| <label for="fileInput" class="inline-block bg-blue-500 text-white py-2 px-6 rounded-full hover:bg-blue-600 cursor-pointer transition"> |
| Sélectionner un fichier |
| </label> |
| <p class="text-gray-500 text-sm mt-4">Formats supportés: JPG, PNG, BMP (Max 10MB par fichier)</p> |
| </div> |
| |
| <div id="imagePreviewContainer" class="preview-container mb-6"> |
| <h4 class="font-medium text-gray-700 mb-2">Aperçu des images</h4> |
| <div id="imagePreviews" class="grid grid-cols-2 gap-4"> |
| |
| </div> |
| </div> |
| </div> |
| |
| |
| <div> |
| <div class="bg-gray-50 p-6 rounded-lg"> |
| <h3 class="text-lg font-semibold mb-4">Paramètres de conversion</h3> |
| |
| <div class="space-y-6"> |
| <div> |
| <label for="thickness" class="block text-sm font-medium text-gray-700 mb-1">Épaisseur du modèle</label> |
| <input type="range" id="thickness" min="1" max="10" value="5" class="slider-thumb w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> |
| <div class="flex justify-between text-xs text-gray-500 mt-1"> |
| <span>Min</span> |
| <span>Max</span> |
| </div> |
| </div> |
| |
| <div> |
| <label for="smoothing" class="block text-sm font-medium text-gray-700 mb-1">Niveau de lissage</label> |
| <input type="range" id="smoothing" min="0" max="100" value="70" class="slider-thumb w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> |
| <div class="flex justify-between text-xs text-gray-500 mt-1"> |
| <span>Détails</span> |
| <span>Lisse</span> |
| </div> |
| </div> |
| |
| <div> |
| <label for="resolution" class="block text-sm font-medium text-gray-700 mb-1">Résolution</label> |
| <select id="resolution" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md"> |
| <option value="low">Basse (rapide)</option> |
| <option value="medium">Moyenne</option> |
| <option value="high" selected>Haute (précise)</option> |
| <option value="ultra">Ultra (AI optimisé)</option> |
| </select> |
| </div> |
| |
| <div class="pt-2"> |
| <button id="convertBtn" class="w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-4 rounded-lg transition flex items-center justify-center"> |
| <i class="fas fa-robot mr-2"></i> Optimiser avec IA |
| </button> |
| </div> |
| </div> |
| </div> |
| |
| <div id="resultContainer" class="preview-container mt-6"> |
| <h4 class="font-medium text-gray-700 mb-2">Résultat STL</h4> |
| <div id="stlPreview" class="border rounded-lg flex items-center justify-center"> |
| <p class="text-gray-500">Votre modèle 3D apparaîtra ici</p> |
| </div> |
| <div class="mt-4 flex space-x-3"> |
| <button id="downloadBtn" class="flex-1 bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 rounded-lg transition flex items-center justify-center"> |
| <i class="fas fa-download mr-2"></i> Télécharger |
| </button> |
| <button id="resetBtn" class="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-700 font-medium py-2 px-4 rounded-lg transition flex items-center justify-center"> |
| <i class="fas fa-redo mr-2"></i> Recommencer |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-gray-100 py-12"> |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> |
| <h2 class="text-3xl font-bold text-center text-gray-800 mb-12">Pourquoi choisir Image2STL ?</h2> |
| |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-8"> |
| <div class="bg-white p-6 rounded-lg shadow-sm text-center"> |
| <div class="text-blue-500 mb-4"> |
| <i class="fas fa-bolt text-4xl"></i> |
| </div> |
| <h3 class="text-xl font-semibold mb-3">Conversion rapide</h3> |
| <p class="text-gray-600">Transformez vos images en fichiers STL en quelques secondes grâce à notre algorithme optimisé.</p> |
| </div> |
| |
| <div class="bg-white p-6 rounded-lg shadow-sm text-center"> |
| <div class="text-blue-500 mb-4"> |
| <i class="fas fa-brain text-4xl"></i> |
| </div> |
| <h3 class="text-xl font-semibold mb-3">IA avancée</h3> |
| <p class="text-gray-600">Notre intelligence artificielle optimise automatiquement votre modèle pour une qualité d'impression exceptionnelle.</p> |
| </div> |
| |
| <div class="bg-white p-6 rounded-lg shadow-sm text-center"> |
| <div class="text-blue-500 mb-4"> |
| <i class="fas fa-print text-4xl"></i> |
| </div> |
| <h3 class="text-xl font-semibold mb-3">Prêt à imprimer</h3> |
| <p class="text-gray-600">Fichiers STL optimisés pour une impression 3D sans problème sur toutes les imprimantes.</p> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <footer class="bg-gray-800 text-white py-8"> |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> |
| <div class="grid grid-cols-1 md:grid-cols-4 gap-8"> |
| <div> |
| <h4 class="text-lg font-semibold mb-4">Image2STL</h4> |
| <p class="text-gray-400">Transformez vos images en modèles 3D imprimables en quelques clics.</p> |
| </div> |
| <div> |
| <h4 class="text-lg font-semibold mb-4">Liens utiles</h4> |
| <ul class="space-y-2"> |
| <li><a href="#" class="text-gray-400 hover:text-white">Comment ça marche</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white">FAQ</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white">Conditions d'utilisation</a></li> |
| </ul> |
| </div> |
| <div> |
| <h4 class="text-lg font-semibold mb-4">Ressources</h4> |
| <ul class="space-y-2"> |
| <li><a href="#" class="text-gray-400 hover:text-white">Documentation</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white">Exemples</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white">API</a></li> |
| </ul> |
| </div> |
| <div> |
| <h4 class="text-lg font-semibold mb-4">Contact</h4> |
| <ul class="space-y-2"> |
| <li class="flex items-center text-gray-400"><i class="fas fa-envelope mr-2"></i> contact@image2stl.com</li> |
| <li class="flex items-center text-gray-400"><i class="fas fa-phone mr-2"></i> +33 1 23 45 67 89</li> |
| </ul> |
| </div> |
| </div> |
| <div class="border-t border-gray-700 mt-8 pt-8 text-center text-gray-400"> |
| <p>© 2023 Image2STL. Tous droits réservés.</p> |
| </div> |
| </div> |
| </footer> |
|
|
| <script> |
| |
| const dropzone = document.getElementById('dropzone'); |
| const fileInput = document.getElementById('fileInput'); |
| const imagePreview = document.getElementById('imagePreview'); |
| const imagePreviewContainer = document.getElementById('imagePreviewContainer'); |
| const convertBtn = document.getElementById('convertBtn'); |
| const resultContainer = document.getElementById('resultContainer'); |
| const downloadBtn = document.getElementById('downloadBtn'); |
| const resetBtn = document.getElementById('resetBtn'); |
| const startConversion = document.getElementById('startConversion'); |
| |
| |
| startConversion.addEventListener('click', () => { |
| document.querySelector('#conversion-tool').scrollIntoView({ behavior: 'smooth' }); |
| }); |
| |
| |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { |
| dropzone.addEventListener(eventName, preventDefaults, false); |
| }); |
| |
| function preventDefaults(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| } |
| |
| ['dragenter', 'dragover'].forEach(eventName => { |
| dropzone.addEventListener(eventName, highlight, false); |
| }); |
| |
| ['dragleave', 'drop'].forEach(eventName => { |
| dropzone.addEventListener(eventName, unhighlight, false); |
| }); |
| |
| function highlight() { |
| dropzone.classList.add('active'); |
| } |
| |
| function unhighlight() { |
| dropzone.classList.remove('active'); |
| } |
| |
| |
| dropzone.addEventListener('drop', handleDrop, false); |
| |
| function handleDrop(e) { |
| const dt = e.dataTransfer; |
| const files = dt.files; |
| handleFiles(files); |
| } |
| |
| |
| fileInput.addEventListener('change', function() { |
| handleFiles(this.files); |
| }); |
| |
| |
| function handleFiles(files) { |
| if (files.length > 0) { |
| const previewsContainer = document.getElementById('imagePreviews'); |
| previewsContainer.innerHTML = ''; |
| |
| let validFiles = 0; |
| |
| Array.from(files).forEach((file, index) => { |
| if (file.type.match('image.*')) { |
| validFiles++; |
| const reader = new FileReader(); |
| reader.onload = function(e) { |
| const previewDiv = document.createElement('div'); |
| previewDiv.className = 'border rounded-lg overflow-hidden relative'; |
| |
| const img = document.createElement('img'); |
| img.src = e.target.result; |
| img.alt = `Aperçu image ${index + 1}`; |
| img.className = 'w-full h-auto'; |
| |
| const badge = document.createElement('div'); |
| badge.className = 'absolute top-2 right-2 bg-blue-500 text-white text-xs px-2 py-1 rounded-full'; |
| badge.textContent = `#${index + 1}`; |
| |
| previewDiv.appendChild(img); |
| previewDiv.appendChild(badge); |
| previewsContainer.appendChild(previewDiv); |
| |
| if (validFiles === files.length) { |
| imagePreviewContainer.classList.add('show'); |
| } |
| } |
| reader.readAsDataURL(file); |
| } else { |
| alert(`Le fichier "${file.name}" n'est pas une image valide (JPG, PNG, BMP).`); |
| } |
| }); |
| |
| if (validFiles > 0) { |
| imagePreviewContainer.classList.add('show'); |
| } |
| } |
| } |
| |
| |
| convertBtn.addEventListener('click', function() { |
| const previews = document.querySelectorAll('#imagePreviews img'); |
| if (previews.length === 0) { |
| alert('Veuillez d\'abord sélectionner au moins une image.'); |
| return; |
| } |
| |
| |
| convertBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Conversion en cours...'; |
| convertBtn.disabled = true; |
| |
| |
| const img = previews[0]; |
| |
| |
| const canvas = document.createElement('canvas'); |
| const ctx = canvas.getContext('2d'); |
| canvas.width = img.width; |
| canvas.height = img.height; |
| ctx.drawImage(img, 0, 0); |
| |
| |
| const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); |
| const data = imgData.data; |
| |
| |
| const geometry = new THREE.BufferGeometry(); |
| const vertices = []; |
| const indices = []; |
| |
| |
| const thickness = parseInt(document.getElementById('thickness').value); |
| const smoothing = parseInt(document.getElementById('smoothing').value) / 100; |
| const resolution = document.getElementById('resolution').value; |
| |
| |
| let segments = 50; |
| if (resolution === 'low') segments = 30; |
| else if (resolution === 'medium') segments = 60; |
| else if (resolution === 'high') segments = 100; |
| else if (resolution === 'ultra') segments = 150; |
| |
| const size = 1; |
| const segmentSize = size / segments; |
| |
| |
| const heightmap = []; |
| for (let y = 0; y <= segments; y++) { |
| heightmap[y] = []; |
| for (let x = 0; x <= segments; x++) { |
| const px = Math.floor(x / segments * img.width); |
| const py = Math.floor(y / segments * img.height); |
| const idx = (py * img.width + px) * 4; |
| const gray = (data[idx] + data[idx+1] + data[idx+2]) / 765; |
| heightmap[y][x] = gray * thickness; |
| } |
| } |
| |
| |
| if (smoothing > 0) { |
| for (let y = 1; y < segments; y++) { |
| for (let x = 1; x < segments; x++) { |
| heightmap[y][x] = heightmap[y][x] * (1 - smoothing) + |
| ((heightmap[y-1][x] + heightmap[y+1][x] + |
| heightmap[y][x-1] + heightmap[y][x+1]) / 4) * smoothing; |
| } |
| } |
| } |
| |
| |
| for (let y = 0; y <= segments; y++) { |
| for (let x = 0; x <= segments; x++) { |
| vertices.push( |
| x * segmentSize - size/2, |
| y * segmentSize - size/2, |
| heightmap[y][x] |
| ); |
| } |
| } |
| |
| |
| for (let y = 0; y < segments; y++) { |
| for (let x = 0; x < segments; x++) { |
| const a = y * (segments + 1) + x; |
| const b = y * (segments + 1) + x + 1; |
| const c = (y + 1) * (segments + 1) + x; |
| const d = (y + 1) * (segments + 1) + x + 1; |
| |
| |
| indices.push(a, b, d); |
| indices.push(a, d, c); |
| } |
| } |
| |
| geometry.setIndex(indices); |
| geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); |
| geometry.computeVertexNormals(); |
| |
| |
| const mesh = new THREE.Mesh(geometry, new THREE.MeshNormalMaterial()); |
| |
| |
| const scene = new THREE.Scene(); |
| scene.add(mesh); |
| |
| |
| const light1 = new THREE.DirectionalLight(0xffffff, 1); |
| light1.position.set(1, 1, 1); |
| scene.add(light1); |
| |
| const light2 = new THREE.DirectionalLight(0xffffff, 0.5); |
| light2.position.set(-1, -1, -1); |
| scene.add(light2); |
| |
| |
| const renderer = new THREE.WebGLRenderer({ antialias: true }); |
| renderer.setSize(document.getElementById('stlPreview').clientWidth, |
| document.getElementById('stlPreview').clientHeight); |
| document.getElementById('stlPreview').innerHTML = ''; |
| document.getElementById('stlPreview').appendChild(renderer.domElement); |
| |
| |
| const camera = new THREE.PerspectiveCamera(45, |
| document.getElementById('stlPreview').clientWidth / |
| document.getElementById('stlPreview').clientHeight, 0.1, 1000); |
| camera.position.z = 2; |
| |
| |
| function animate() { |
| requestAnimationFrame(animate); |
| mesh.rotation.x += 0.005; |
| mesh.rotation.y += 0.007; |
| renderer.render(scene, camera); |
| } |
| animate(); |
| |
| |
| const exporter = new THREE.STLExporter(); |
| const stlString = exporter.parse(mesh); |
| |
| |
| window.stlData = stlString; |
| |
| |
| resultContainer.classList.add('show'); |
| convertBtn.innerHTML = '<i class="fas fa-magic mr-2"></i> Convertir en STL'; |
| convertBtn.disabled = false; |
| |
| |
| resultContainer.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); |
| }); |
| |
| |
| downloadBtn.addEventListener('click', function() { |
| if (!window.stlData) { |
| alert('Aucun modèle STL à télécharger. Veuillez d\'abord convertir une image.'); |
| return; |
| } |
| |
| const blob = new Blob([window.stlData], { type: 'application/octet-stream' }); |
| const url = URL.createObjectURL(blob); |
| |
| const a = document.createElement('a'); |
| a.href = url; |
| a.download = 'modele_3d.stl'; |
| document.body.appendChild(a); |
| a.click(); |
| document.body.removeChild(a); |
| URL.revokeObjectURL(url); |
| }); |
| |
| |
| resetBtn.addEventListener('click', function() { |
| imagePreview.src = ''; |
| imagePreviewContainer.classList.remove('show'); |
| resultContainer.classList.remove('show'); |
| fileInput.value = ''; |
| }); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Invader1/image-stl" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |