Mathieu2025 commited on
Commit
c1771b2
·
verified ·
1 Parent(s): 766eae8

undefined - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +453 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: 2dto3d Converter
3
- emoji: 🌖
4
- colorFrom: purple
5
- colorTo: indigo
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: 2dto3d-converter
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: red
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,453 @@
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="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>2D to 3D STL Generator</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/stl-export.min.js"></script>
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
11
+ <style>
12
+ .dropzone {
13
+ border: 2px dashed #4b5563;
14
+ transition: all 0.3s ease;
15
+ }
16
+ .dropzone.active {
17
+ border-color: #3b82f6;
18
+ background-color: rgba(59, 130, 246, 0.1);
19
+ }
20
+ #previewCanvas {
21
+ background-color: #f3f4f6;
22
+ border-radius: 0.5rem;
23
+ }
24
+ #3dViewer {
25
+ width: 100%;
26
+ height: 400px;
27
+ background-color: #f3f4f6;
28
+ border-radius: 0.5rem;
29
+ }
30
+ .slider-thumb::-webkit-slider-thumb {
31
+ -webkit-appearance: none;
32
+ width: 20px;
33
+ height: 20px;
34
+ border-radius: 50%;
35
+ background: #3b82f6;
36
+ cursor: pointer;
37
+ }
38
+ .slider-thumb::-moz-range-thumb {
39
+ width: 20px;
40
+ height: 20px;
41
+ border-radius: 50%;
42
+ background: #3b82f6;
43
+ cursor: pointer;
44
+ }
45
+ </style>
46
+ </head>
47
+ <body class="bg-gray-100 min-h-screen">
48
+ <div class="container mx-auto px-4 py-8">
49
+ <header class="text-center mb-8">
50
+ <h1 class="text-4xl font-bold text-gray-800 mb-2">2D to 3D STL Generator</h1>
51
+ <p class="text-gray-600">Convert your 2D images into printable 3D STL files</p>
52
+ </header>
53
+
54
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
55
+ <!-- Image Upload Section -->
56
+ <div class="bg-white rounded-xl shadow-md p-6">
57
+ <h2 class="text-2xl font-semibold text-gray-800 mb-4">1. Upload 2D Image</h2>
58
+
59
+ <div id="dropzone" class="dropzone rounded-lg p-8 text-center cursor-pointer mb-6">
60
+ <div class="flex flex-col items-center justify-center">
61
+ <i class="fas fa-cloud-upload-alt text-4xl text-blue-500 mb-3"></i>
62
+ <p class="text-gray-600 mb-2">Drag & drop your image here</p>
63
+ <p class="text-sm text-gray-500 mb-4">or</p>
64
+ <label for="fileInput" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg cursor-pointer transition">
65
+ Select Image
66
+ </label>
67
+ <input type="file" id="fileInput" accept="image/*" class="hidden">
68
+ </div>
69
+ </div>
70
+
71
+ <div id="imagePreviewContainer" class="hidden">
72
+ <h3 class="text-lg font-medium text-gray-700 mb-3">Image Preview</h3>
73
+ <div class="flex justify-center">
74
+ <canvas id="previewCanvas" class="w-full max-w-md h-auto"></canvas>
75
+ </div>
76
+ </div>
77
+ </div>
78
+
79
+ <!-- 3D Settings Section -->
80
+ <div class="bg-white rounded-xl shadow-md p-6">
81
+ <h2 class="text-2xl font-semibold text-gray-800 mb-4">2. 3D Generation Settings</h2>
82
+
83
+ <div class="space-y-4">
84
+ <div>
85
+ <label for="heightRange" class="block text-sm font-medium text-gray-700 mb-1">3D Height (mm)</label>
86
+ <div class="flex items-center">
87
+ <input type="range" id="heightRange" min="1" max="50" value="10" class="w-full slider-thumb">
88
+ <span id="heightValue" class="ml-3 text-gray-700 w-12 text-center">10</span>
89
+ </div>
90
+ </div>
91
+
92
+ <div>
93
+ <label for="smoothnessRange" class="block text-sm font-medium text-gray-700 mb-1">Edge Smoothness</label>
94
+ <div class="flex items-center">
95
+ <input type="range" id="smoothnessRange" min="1" max="10" value="5" class="w-full slider-thumb">
96
+ <span id="smoothnessValue" class="ml-3 text-gray-700 w-12 text-center">5</span>
97
+ </div>
98
+ </div>
99
+
100
+ <div>
101
+ <label for="baseThicknessRange" class="block text-sm font-medium text-gray-700 mb-1">Base Thickness (mm)</label>
102
+ <div class="flex items-center">
103
+ <input type="range" id="baseThicknessRange" min="0" max="10" value="2" class="w-full slider-thumb">
104
+ <span id="baseThicknessValue" class="ml-3 text-gray-700 w-12 text-center">2</span>
105
+ </div>
106
+ </div>
107
+
108
+ <div class="pt-4">
109
+ <button id="generateBtn" class="w-full bg-blue-500 hover:bg-blue-600 text-white py-3 px-4 rounded-lg font-medium transition flex items-center justify-center">
110
+ <i class="fas fa-cube mr-2"></i> Generate 3D Model
111
+ </button>
112
+ </div>
113
+ </div>
114
+ </div>
115
+ </div>
116
+
117
+ <!-- 3D Preview Section -->
118
+ <div id="3dPreviewSection" class="hidden mt-8 bg-white rounded-xl shadow-md p-6">
119
+ <div class="flex justify-between items-center mb-4">
120
+ <h2 class="text-2xl font-semibold text-gray-800">3D Model Preview</h2>
121
+ <button id="downloadBtn" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-lg flex items-center">
122
+ <i class="fas fa-download mr-2"></i> Download STL
123
+ </button>
124
+ </div>
125
+
126
+ <div id="3dViewer"></div>
127
+
128
+ <div class="mt-4 grid grid-cols-1 md:grid-cols-3 gap-4">
129
+ <div class="bg-gray-50 p-4 rounded-lg">
130
+ <h3 class="font-medium text-gray-700 mb-2">Model Info</h3>
131
+ <p class="text-sm text-gray-600">Height: <span id="modelHeight" class="font-medium">10</span> mm</p>
132
+ <p class="text-sm text-gray-600">Base: <span id="modelBase" class="font-medium">2</span> mm</p>
133
+ </div>
134
+ <div class="bg-gray-50 p-4 rounded-lg">
135
+ <h3 class="font-medium text-gray-700 mb-2">Estimated Print Time</h3>
136
+ <p class="text-sm text-gray-600">Approx. <span id="printTime" class="font-medium">2-4</span> hours</p>
137
+ </div>
138
+ <div class="bg-gray-50 p-4 rounded-lg">
139
+ <h3 class="font-medium text-gray-700 mb-2">File Size</h3>
140
+ <p class="text-sm text-gray-600"><span id="fileSize" class="font-medium">1.2</span> MB</p>
141
+ </div>
142
+ </div>
143
+ </div>
144
+
145
+ <!-- How It Works Section -->
146
+ <div class="mt-12 bg-white rounded-xl shadow-md p-6">
147
+ <h2 class="text-2xl font-semibold text-gray-800 mb-4">How It Works</h2>
148
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
149
+ <div class="text-center">
150
+ <div class="bg-blue-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-3">
151
+ <i class="fas fa-upload text-blue-500 text-2xl"></i>
152
+ </div>
153
+ <h3 class="font-medium text-gray-800 mb-2">Upload Image</h3>
154
+ <p class="text-gray-600 text-sm">Upload any 2D image in JPG, PNG, or GIF format.</p>
155
+ </div>
156
+ <div class="text-center">
157
+ <div class="bg-blue-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-3">
158
+ <i class="fas fa-sliders-h text-blue-500 text-2xl"></i>
159
+ </div>
160
+ <h3 class="font-medium text-gray-800 mb-2">Adjust Settings</h3>
161
+ <p class="text-gray-600 text-sm">Customize the height, smoothness, and base thickness.</p>
162
+ </div>
163
+ <div class="text-center">
164
+ <div class="bg-blue-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-3">
165
+ <i class="fas fa-cube text-blue-500 text-2xl"></i>
166
+ </div>
167
+ <h3 class="font-medium text-gray-800 mb-2">Download STL</h3>
168
+ <p class="text-gray-600 text-sm">Generate and download your 3D model in STL format.</p>
169
+ </div>
170
+ </div>
171
+ </div>
172
+ </div>
173
+
174
+ <script>
175
+ document.addEventListener('DOMContentLoaded', function() {
176
+ // DOM Elements
177
+ const dropzone = document.getElementById('dropzone');
178
+ const fileInput = document.getElementById('fileInput');
179
+ const previewCanvas = document.getElementById('previewCanvas');
180
+ const ctx = previewCanvas.getContext('2d');
181
+ const imagePreviewContainer = document.getElementById('imagePreviewContainer');
182
+ const generateBtn = document.getElementById('generateBtn');
183
+ const downloadBtn = document.getElementById('downloadBtn');
184
+ const heightRange = document.getElementById('heightRange');
185
+ const heightValue = document.getElementById('heightValue');
186
+ const smoothnessRange = document.getElementById('smoothnessRange');
187
+ const smoothnessValue = document.getElementById('smoothnessValue');
188
+ const baseThicknessRange = document.getElementById('baseThicknessRange');
189
+ const baseThicknessValue = document.getElementById('baseThicknessValue');
190
+ const modelHeight = document.getElementById('modelHeight');
191
+ const modelBase = document.getElementById('modelBase');
192
+ const printTime = document.getElementById('printTime');
193
+ const fileSize = document.getElementById('fileSize');
194
+ const dPreviewSection = document.getElementById('3dPreviewSection');
195
+ const dViewer = document.getElementById('3dViewer');
196
+
197
+ // Variables
198
+ let uploadedImage = null;
199
+ let generatedMesh = null;
200
+ let scene, camera, renderer, controls;
201
+
202
+ // Initialize Three.js viewer
203
+ function init3DViewer() {
204
+ scene = new THREE.Scene();
205
+ scene.background = new THREE.Color(0xf3f4f6);
206
+
207
+ camera = new THREE.PerspectiveCamera(75, dViewer.clientWidth / dViewer.clientHeight, 0.1, 1000);
208
+ camera.position.z = 5;
209
+
210
+ renderer = new THREE.WebGLRenderer({ antialias: true });
211
+ renderer.setSize(dViewer.clientWidth, dViewer.clientHeight);
212
+ dViewer.appendChild(renderer.domElement);
213
+
214
+ // Add lights
215
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
216
+ scene.add(ambientLight);
217
+
218
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
219
+ directionalLight.position.set(1, 1, 1);
220
+ scene.add(directionalLight);
221
+
222
+ // Add orbit controls
223
+ controls = new THREE.OrbitControls(camera, renderer.domElement);
224
+ controls.enableDamping = true;
225
+ controls.dampingFactor = 0.25;
226
+
227
+ // Handle window resize
228
+ window.addEventListener('resize', onWindowResize);
229
+
230
+ // Start animation loop
231
+ animate();
232
+ }
233
+
234
+ function animate() {
235
+ requestAnimationFrame(animate);
236
+ controls.update();
237
+ renderer.render(scene, camera);
238
+ }
239
+
240
+ function onWindowResize() {
241
+ camera.aspect = dViewer.clientWidth / dViewer.clientHeight;
242
+ camera.updateProjectionMatrix();
243
+ renderer.setSize(dViewer.clientWidth, dViewer.clientHeight);
244
+ }
245
+
246
+ // Initialize the 3D viewer
247
+ init3DViewer();
248
+
249
+ // Update slider values display
250
+ heightRange.addEventListener('input', function() {
251
+ heightValue.textContent = this.value;
252
+ });
253
+
254
+ smoothnessRange.addEventListener('input', function() {
255
+ smoothnessValue.textContent = this.value;
256
+ });
257
+
258
+ baseThicknessRange.addEventListener('input', function() {
259
+ baseThicknessValue.textContent = this.value;
260
+ });
261
+
262
+ // Handle drag and drop
263
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
264
+ dropzone.addEventListener(eventName, preventDefaults, false);
265
+ });
266
+
267
+ function preventDefaults(e) {
268
+ e.preventDefault();
269
+ e.stopPropagation();
270
+ }
271
+
272
+ ['dragenter', 'dragover'].forEach(eventName => {
273
+ dropzone.addEventListener(eventName, highlight, false);
274
+ });
275
+
276
+ ['dragleave', 'drop'].forEach(eventName => {
277
+ dropzone.addEventListener(eventName, unhighlight, false);
278
+ });
279
+
280
+ function highlight() {
281
+ dropzone.classList.add('active');
282
+ }
283
+
284
+ function unhighlight() {
285
+ dropzone.classList.remove('active');
286
+ }
287
+
288
+ dropzone.addEventListener('drop', handleDrop, false);
289
+
290
+ function handleDrop(e) {
291
+ const dt = e.dataTransfer;
292
+ const files = dt.files;
293
+ handleFiles(files);
294
+ }
295
+
296
+ fileInput.addEventListener('change', function() {
297
+ handleFiles(this.files);
298
+ });
299
+
300
+ function handleFiles(files) {
301
+ if (files.length > 0) {
302
+ const file = files[0];
303
+ if (file.type.match('image.*')) {
304
+ const reader = new FileReader();
305
+ reader.onload = function(e) {
306
+ const img = new Image();
307
+ img.onload = function() {
308
+ uploadedImage = img;
309
+ displayImagePreview(img);
310
+ };
311
+ img.src = e.target.result;
312
+ };
313
+ reader.readAsDataURL(file);
314
+ } else {
315
+ alert('Please upload an image file (JPG, PNG, GIF)');
316
+ }
317
+ }
318
+ }
319
+
320
+ function displayImagePreview(img) {
321
+ // Set canvas dimensions
322
+ const maxWidth = 500;
323
+ const maxHeight = 400;
324
+ let width = img.width;
325
+ let height = img.height;
326
+
327
+ if (width > maxWidth) {
328
+ height = (maxWidth / width) * height;
329
+ width = maxWidth;
330
+ }
331
+
332
+ if (height > maxHeight) {
333
+ width = (maxHeight / height) * width;
334
+ height = maxHeight;
335
+ }
336
+
337
+ previewCanvas.width = width;
338
+ previewCanvas.height = height;
339
+
340
+ // Draw image on canvas
341
+ ctx.drawImage(img, 0, 0, width, height);
342
+
343
+ // Show preview container
344
+ imagePreviewContainer.classList.remove('hidden');
345
+ }
346
+
347
+ // Generate 3D model
348
+ generateBtn.addEventListener('click', function() {
349
+ if (!uploadedImage) {
350
+ alert('Please upload an image first');
351
+ return;
352
+ }
353
+
354
+ // Show loading state
355
+ generateBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Generating...';
356
+ generateBtn.disabled = true;
357
+
358
+ // Simulate generation process (in a real app, this would be more complex)
359
+ setTimeout(function() {
360
+ generate3DModel();
361
+
362
+ // Update model info
363
+ modelHeight.textContent = heightRange.value;
364
+ modelBase.textContent = baseThicknessRange.value;
365
+
366
+ // Estimate print time and file size (these would be calculated in a real app)
367
+ const height = parseInt(heightRange.value);
368
+ const base = parseInt(baseThicknessRange.value);
369
+ const estTime = Math.round(height * 0.2 + base * 0.1);
370
+ printTime.textContent = `${estTime}-${estTime + 2}`;
371
+
372
+ const fileSizeEst = (height * width * 0.001).toFixed(1);
373
+ fileSize.textContent = fileSizeEst;
374
+
375
+ // Show 3D preview section
376
+ dPreviewSection.classList.remove('hidden');
377
+
378
+ // Reset button
379
+ generateBtn.innerHTML = '<i class="fas fa-cube mr-2"></i> Generate 3D Model';
380
+ generateBtn.disabled = false;
381
+
382
+ // Scroll to 3D preview
383
+ dPreviewSection.scrollIntoView({ behavior: 'smooth' });
384
+ }, 1500);
385
+ });
386
+
387
+ function generate3DModel() {
388
+ // Clear previous mesh
389
+ if (generatedMesh) {
390
+ scene.remove(generatedMesh);
391
+ }
392
+
393
+ // Create a simple 3D shape from the image (simplified for demo)
394
+ // In a real app, you would use more sophisticated algorithms
395
+ const geometry = new THREE.BoxGeometry(
396
+ 3,
397
+ parseFloat(baseThicknessRange.value) * 0.1,
398
+ parseFloat(heightRange.value) * 0.1
399
+ );
400
+
401
+ // Create texture from image
402
+ const texture = new THREE.Texture(previewCanvas);
403
+ texture.needsUpdate = true;
404
+
405
+ const material = new THREE.MeshPhongMaterial({
406
+ map: texture,
407
+ side: THREE.DoubleSide
408
+ });
409
+
410
+ generatedMesh = new THREE.Mesh(geometry, material);
411
+ scene.add(generatedMesh);
412
+
413
+ // Reset camera position
414
+ camera.position.z = 5;
415
+ controls.reset();
416
+ }
417
+
418
+ // Download STL
419
+ downloadBtn.addEventListener('click', function() {
420
+ if (!generatedMesh) {
421
+ alert('Please generate a 3D model first');
422
+ return;
423
+ }
424
+
425
+ // Show loading state
426
+ downloadBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Preparing Download...';
427
+ downloadBtn.disabled = true;
428
+
429
+ // Simulate STL export (in a real app, you would use a proper STL exporter)
430
+ setTimeout(function() {
431
+ // Create a simplified STL export (demo only)
432
+ const exporter = new THREE.STLExporter();
433
+ const stlString = exporter.parse(generatedMesh);
434
+
435
+ // Create download link
436
+ const blob = new Blob([stlString], { type: 'application/octet-stream' });
437
+ const url = URL.createObjectURL(blob);
438
+ const link = document.createElement('a');
439
+ link.href = url;
440
+ link.download = '3d-model.stl';
441
+ document.body.appendChild(link);
442
+ link.click();
443
+ document.body.removeChild(link);
444
+
445
+ // Reset button
446
+ downloadBtn.innerHTML = '<i class="fas fa-download mr-2"></i> Download STL';
447
+ downloadBtn.disabled = false;
448
+ }, 1000);
449
+ });
450
+ });
451
+ </script>
452
+ <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=Mathieu2025/2dto3d-converter" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
453
+ </html>