gladiopeace commited on
Commit
5b3145f
·
verified ·
1 Parent(s): 8dc4a5b

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +662 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Cabinet
3
- emoji: 📚
4
- colorFrom: green
5
  colorTo: purple
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: cabinet
3
+ emoji: 🐳
4
+ colorFrom: pink
5
  colorTo: purple
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,662 @@
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>Parametric Kitchen Cabinet Generator</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
11
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/GLTFLoader.js"></script>
12
+ <style>
13
+ .slider-container {
14
+ transition: all 0.3s ease;
15
+ }
16
+ .slider-container:hover {
17
+ transform: translateY(-2px);
18
+ }
19
+ .preview-container {
20
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
21
+ transition: all 0.3s ease;
22
+ }
23
+ .cabinet-part {
24
+ display: none;
25
+ position: absolute;
26
+ background-color: rgba(255, 255, 255, 0.9);
27
+ padding: 15px;
28
+ border-radius: 8px;
29
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
30
+ z-index: 10;
31
+ }
32
+ .progress-ring {
33
+ stroke-dasharray: 314; /* 2 * π * r where r is 50 */
34
+ stroke-dashoffset: 0;
35
+ transition: stroke-dashoffset 0.5s ease;
36
+ }
37
+ #modelViewer {
38
+ background-color: #f5f3f0;
39
+ border-radius: 8px;
40
+ overflow: hidden;
41
+ }
42
+ #priceSection {
43
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
44
+ }
45
+ </style>
46
+ </head>
47
+ <body class="bg-gray-50">
48
+ <div class="container mx-auto px-4 py-8">
49
+ <header class="mb-10 text-center">
50
+ <h1 class="text-4xl font-bold text-gray-800 mb-2">Parametric Kitchen Cabinet Generator</h1>
51
+ <p class="text-gray-600 max-w-2xl mx-auto">Design your perfect kitchen cabinet with real-time 3D preview and instant pricing.</p>
52
+ </header>
53
+
54
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
55
+ <!-- Controls Section -->
56
+ <div class="lg:col-span-1 bg-white rounded-xl shadow-lg p-6">
57
+ <div class="mb-8">
58
+ <h2 class="text-2xl font-semibold text-gray-800 mb-4">Cabinet Configuration</h2>
59
+
60
+ <!-- Cabinet Type -->
61
+ <div class="mb-6 slider-container">
62
+ <div class="flex justify-between items-center mb-2">
63
+ <label class="text-gray-700 font-medium">Cabinet Type</label>
64
+ <span id="cabinetTypeDisplay" class="text-gray-500">Wall</span>
65
+ </div>
66
+ <select id="cabinetType" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 bg-white">
67
+ <option value="wall">Wall Cabinet</option>
68
+ <option value="base">Base Cabinet</option>
69
+ <option value="tall">Tall Cabinet</option>
70
+ <option value="island">Island Cabinet</option>
71
+ </select>
72
+ </div>
73
+
74
+ <!-- Dimensions -->
75
+ <div class="mb-6 slider-container">
76
+ <div class="flex justify-between items-center mb-2">
77
+ <label class="text-gray-700 font-medium">Width (inches)</label>
78
+ <span id="widthValue" class="text-gray-500">24</span>
79
+ </div>
80
+ <input id="widthSlider" type="range" min="12" max="48" value="24" step="3" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
81
+ </div>
82
+
83
+ <div class="mb-6 slider-container">
84
+ <div class="flex justify-between items-center mb-2">
85
+ <label class="text-gray-700 font-medium">Height (inches)</label>
86
+ <span id="heightValue" class="text-gray-500">36</span>
87
+ </div>
88
+ <input id="heightSlider" type="range" min="12" max="96" value="36" step="3" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
89
+ </div>
90
+
91
+ <div class="mb-6 slider-container">
92
+ <div class="flex justify-between items-center mb-2">
93
+ <label class="text-gray-700 font-medium">Depth (inches)</label>
94
+ <span id="depthValue" class="text-gray-500">12</span>
95
+ </div>
96
+ <input id="depthSlider" type="range" min="12" max="24" value="12" step="3" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
97
+ </div>
98
+
99
+ <!-- Doors -->
100
+ <div class="mb-6">
101
+ <label class="block text-gray-700 font-medium mb-2">Number of Doors</label>
102
+ <div class="flex space-x-4">
103
+ <button id="oneDoor" class="door-btn px-4 py-2 border rounded-lg flex-1 bg-indigo-100 text-indigo-700 border-indigo-300">1 Door</button>
104
+ <button id="twoDoors" class="door-btn px-4 py-2 border rounded-lg flex-1 hover:bg-gray-100">2 Doors</button>
105
+ </div>
106
+ </div>
107
+
108
+ <!-- Material -->
109
+ <div class="mb-6 slider-container">
110
+ <div class="flex justify-between items-center mb-2">
111
+ <label class="text-gray-700 font-medium">Material</label>
112
+ <span id="materialDisplay" class="text-gray-500">Oak</span>
113
+ </div>
114
+ <select id="material" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 bg-white">
115
+ <option value="oak">Oak</option>
116
+ <option value="maple">Maple</option>
117
+ <option value="cherry">Cherry</option>
118
+ <option value="walnut">Walnut</option>
119
+ <option value="laminate">Laminate</option>
120
+ <option value="painted">Painted MDF</option>
121
+ </select>
122
+ </div>
123
+
124
+ <!-- Color -->
125
+ <div class="mb-6 slider-container">
126
+ <div class="flex justify-between items-center mb-2">
127
+ <label class="text-gray-700 font-medium">Color</label>
128
+ <span id="colorDisplay" class="text-gray-500">Natural</span>
129
+ </div>
130
+ <select id="color" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 bg-white">
131
+ <option value="natural">Natural</option>
132
+ <option value="white">White</option>
133
+ <option value="black">Black</option>
134
+ <option value="gray">Gray</option>
135
+ <option value="navy">Navy Blue</option>
136
+ <option value="green">Forest Green</option>
137
+ </select>
138
+ </div>
139
+
140
+ <!-- Features -->
141
+ <div class="mb-6">
142
+ <label class="block text-gray-700 font-medium mb-2">Features</label>
143
+ <div class="space-y-2">
144
+ <label class="flex items-center space-x-3">
145
+ <input type="checkbox" id="softClose" class="form-checkbox h-5 w-5 text-indigo-600 rounded">
146
+ <span class="text-gray-700">Soft-Close Hinges</span>
147
+ </label>
148
+ <label class="flex items-center space-x-3">
149
+ <input type="checkbox" id="pullOutShelves" class="form-checkbox h-5 w-5 text-indigo-600 rounded">
150
+ <span class="text-gray-700">Pull-Out Shelves</span>
151
+ </label>
152
+ <label class="flex items-center space-x-3">
153
+ <input type="checkbox" id="lighting" class="form-checkbox h-5 w-5 text-indigo-600 rounded">
154
+ <span class="text-gray-700">LED Lighting</span>
155
+ </label>
156
+ </div>
157
+ </div>
158
+ </div>
159
+
160
+ <div id="priceSection" class="p-5 rounded-xl">
161
+ <h3 class="text-lg font-semibold text-gray-800 mb-3">Estimated Price</h3>
162
+ <div class="flex justify-between items-center mb-2">
163
+ <span class="text-gray-600">Base Price</span>
164
+ <span id="basePrice" class="font-medium">$300.00</span>
165
+ </div>
166
+ <div class="flex justify-between items-center mb-2">
167
+ <span class="text-gray-600">Material Upgrade</span>
168
+ <span id="materialPrice" class="font-medium">$50.00</span>
169
+ </div>
170
+ <div class="flex justify-between items-center mb-4">
171
+ <span class="text-gray-600">Features</span>
172
+ <span id="featuresPrice" class="font-medium">$75.00</span>
173
+ </div>
174
+ <div class="border-t border-gray-300 pt-4">
175
+ <div class="flex justify-between items-center">
176
+ <span class="text-xl font-bold text-gray-800">Total</span>
177
+ <span id="totalPrice" class="text-2xl font-bold text-indigo-600">$425.00</span>
178
+ </div>
179
+ </div>
180
+ </div>
181
+ </div>
182
+
183
+ <!-- Preview Section -->
184
+ <div class="lg:col-span-2 relative">
185
+ <div class="bg-white rounded-xl shadow-lg overflow-hidden">
186
+ <div class="p-4 border-b border-gray-200">
187
+ <div class="flex justify-between items-center">
188
+ <h2 class="text-xl font-semibold text-gray-800">3D Model Preview</h2>
189
+ <div class="flex space-x-2">
190
+ <button id="downloadUSDZ" class="px-3 py-1 bg-indigo-100 text-indigo-700 rounded-lg text-sm flex items-center hover:bg-indigo-200">
191
+ <i class="fas fa-download mr-2"></i> USDZ
192
+ </button>
193
+ <button id="downloadGLB" class="px-3 py-1 bg-indigo-600 text-white rounded-lg text-sm flex items-center hover:bg-indigo-700">
194
+ <i class="fas fa-download mr-2"></i> GLB
195
+ </button>
196
+ </div>
197
+ </div>
198
+ </div>
199
+ <div id="modelViewer" class="w-full h-96 rounded-b-xl">
200
+ <div id="loadingOverlay" class="absolute inset-0 bg-gray-100 bg-opacity-70 flex flex-col items-center justify-center">
201
+ <div class="w-16 h-16 relative mb-4">
202
+ <svg class="w-full h-full" viewBox="0 0 100 100">
203
+ <circle class="text-gray-400" cx="50" cy="50" r="40" stroke-width="8" stroke="currentColor" fill="none"></circle>
204
+ <circle class="progress-ring text-indigo-600" cx="50" cy="50" r="40" stroke-width="8" stroke="currentColor" fill="none" stroke-dashoffset="314"></circle>
205
+ </svg>
206
+ </div>
207
+ <p class="text-gray-600">Generating 3D model...</p>
208
+ </div>
209
+ </div>
210
+ </div>
211
+
212
+ <div class="mt-8 grid grid-cols-1 md:grid-cols-2 gap-6">
213
+ <div class="bg-white p-6 rounded-xl shadow-lg">
214
+ <h3 class="text-lg font-semibold text-gray-800 mb-4">Cabinet Specifications</h3>
215
+ <div class="space-y-3">
216
+ <div class="flex justify-between">
217
+ <span class="text-gray-600">Exterior Dimensions:</span>
218
+ <span id="dimensionsDisplay" class="font-medium">24" W × 36" H × 12" D</span>
219
+ </div>
220
+ <div class="flex justify-between">
221
+ <span class="text-gray-600">Interior Capacity:</span>
222
+ <span id="capacityDisplay" class="font-medium">6.0 cu.ft</span>
223
+ </div>
224
+ <div class="flex justify-between">
225
+ <span class="text-gray-600">Material Thickness:</span>
226
+ <span class="font-medium">0.75"</span>
227
+ </div>
228
+ </div>
229
+ </div>
230
+
231
+ <div class="bg-white p-6 rounded-xl shadow-lg">
232
+ <h3 class="text-lg font-semibold text-gray-800 mb-4">Order Information</h3>
233
+ <div class="space-y-3">
234
+ <div class="flex justify-between">
235
+ <span class="text-gray-600">Production Time:</span>
236
+ <span class="font-medium">2-3 weeks</span>
237
+ </div>
238
+ <div class="flex justify-between">
239
+ <span class="text-gray-600">Shipping:</span>
240
+ <span class="font-medium">Flat rate $49.00</span>
241
+ </div>
242
+ <div class="flex justify-between">
243
+ <span class="text-gray-600">Warranty:</span>
244
+ <span class="font-medium">10 years</span>
245
+ </div>
246
+ </div>
247
+ <button class="w-full mt-6 py-3 bg-indigo-600 text-white rounded-lg font-medium hover:bg-indigo-700 transition-colors">
248
+ Add to Cart
249
+ </button>
250
+ </div>
251
+ </div>
252
+ </div>
253
+ </div>
254
+ </div>
255
+
256
+ <script>
257
+ // DOM Elements
258
+ const widthSlider = document.getElementById('widthSlider');
259
+ const heightSlider = document.getElementById('heightSlider');
260
+ const depthSlider = document.getElementById('depthSlider');
261
+ const widthValue = document.getElementById('widthValue');
262
+ const heightValue = document.getElementById('heightValue');
263
+ const depthValue = document.getElementById('depthValue');
264
+ const oneDoorBtn = document.getElementById('oneDoor');
265
+ const twoDoorsBtn = document.getElementById('twoDoors');
266
+ const cabinetType = document.getElementById('cabinetType');
267
+ const cabinetTypeDisplay = document.getElementById('cabinetTypeDisplay');
268
+ const material = document.getElementById('material');
269
+ const materialDisplay = document.getElementById('materialDisplay');
270
+ const color = document.getElementById('color');
271
+ const colorDisplay = document.getElementById('colorDisplay');
272
+ const softClose = document.getElementById('softClose');
273
+ const pullOutShelves = document.getElementById('pullOutShelves');
274
+ const lighting = document.getElementById('lighting');
275
+ const basePrice = document.getElementById('basePrice');
276
+ const materialPrice = document.getElementById('materialPrice');
277
+ const featuresPrice = document.getElementById('featuresPrice');
278
+ const totalPrice = document.getElementById('totalPrice');
279
+ const dimensionsDisplay = document.getElementById('dimensionsDisplay');
280
+ const capacityDisplay = document.getElementById('capacityDisplay');
281
+ const modelViewer = document.getElementById('modelViewer');
282
+ const loadingOverlay = document.getElementById('loadingOverlay');
283
+ const downloadUSDZ = document.getElementById('downloadUSDZ');
284
+ const downloadGLB = document.getElementById('downloadGLB');
285
+ const progressRing = document.querySelector('.progress-ring');
286
+
287
+ // Three.js variables
288
+ let scene, camera, renderer, controls, cabinetGroup;
289
+
290
+ // Initialize the 3D viewer
291
+ function initThreeJS() {
292
+ // Create scene
293
+ scene = new THREE.Scene();
294
+ scene.background = new THREE.Color(0xf5f3f0);
295
+
296
+ // Create camera
297
+ camera = new THREE.PerspectiveCamera(60, modelViewer.clientWidth / modelViewer.clientHeight, 0.1, 1000);
298
+ camera.position.set(30, 30, 30);
299
+
300
+ // Create renderer
301
+ renderer = new THREE.WebGLRenderer({ antialias: true });
302
+ renderer.setSize(modelViewer.clientWidth, modelViewer.clientHeight);
303
+ renderer.shadowMap.enabled = true;
304
+ modelViewer.appendChild(renderer.domElement);
305
+
306
+ // Add lights
307
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
308
+ scene.add(ambientLight);
309
+
310
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
311
+ directionalLight.position.set(20, 30, 20);
312
+ directionalLight.castShadow = true;
313
+ directionalLight.shadow.mapSize.width = 2048;
314
+ directionalLight.shadow.mapSize.height = 2048;
315
+ scene.add(directionalLight);
316
+
317
+ const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.4);
318
+ directionalLight2.position.set(-20, -30, -20);
319
+ scene.add(directionalLight2);
320
+
321
+ // Add controls
322
+ controls = new THREE.OrbitControls(camera, renderer.domElement);
323
+ controls.enableDamping = true;
324
+ controls.dampingFactor = 0.05;
325
+
326
+ // Create initial cabinet
327
+ cabinetGroup = new THREE.Group();
328
+ scene.add(cabinetGroup);
329
+ updateCabinetModel();
330
+
331
+ // Handle window resize
332
+ window.addEventListener('resize', onWindowResize);
333
+
334
+ // Start animation loop
335
+ animate();
336
+ }
337
+
338
+ function onWindowResize() {
339
+ camera.aspect = modelViewer.clientWidth / modelViewer.clientHeight;
340
+ camera.updateProjectionMatrix();
341
+ renderer.setSize(modelViewer.clientWidth, modelViewer.clientHeight);
342
+ }
343
+
344
+ function animate() {
345
+ requestAnimationFrame(animate);
346
+ controls.update();
347
+ renderer.render(scene, camera);
348
+ }
349
+
350
+ // Create cabinet geometry based on parameters
351
+ function updateCabinetModel() {
352
+ // Simulate loading (in a real app, this would be replaced with actual model generation)
353
+ showLoading();
354
+
355
+ // Clear previous cabinet
356
+ while (cabinetGroup.children.length > 0) {
357
+ cabinetGroup.remove(cabinetGroup.children[0]);
358
+ }
359
+
360
+ // Get current values
361
+ const width = parseFloat(widthSlider.value);
362
+ const height = parseFloat(heightSlider.value);
363
+ const depth = parseFloat(depthSlider.value);
364
+ const type = cabinetType.value;
365
+ const isTwoDoors = twoDoorsBtn.classList.contains('bg-indigo-100');
366
+ const colorValue = color.value;
367
+
368
+ // Set material (simplified for demo)
369
+ let material;
370
+ if (colorValue === 'natural') {
371
+ material = new THREE.MeshStandardMaterial({
372
+ color: 0xC19A6B,
373
+ roughness: 0.4,
374
+ metalness: 0.1
375
+ });
376
+ } else if (colorValue === 'white') {
377
+ material = new THREE.MeshStandardMaterial({
378
+ color: 0xF5F5F5,
379
+ roughness: 0.1,
380
+ metalness: 0.05
381
+ });
382
+ } else if (colorValue === 'black') {
383
+ material = new THREE.MeshStandardMaterial({
384
+ color: 0x222222,
385
+ roughness: 0.3,
386
+ metalness: 0.2
387
+ });
388
+ } else if (colorValue === 'gray') {
389
+ material = new THREE.MeshStandardMaterial({
390
+ color: 0x808080,
391
+ roughness: 0.3,
392
+ metalness: 0.15
393
+ });
394
+ } else if (colorValue === 'navy') {
395
+ material = new THREE.MeshStandardMaterial({
396
+ color: 0x2C3E50,
397
+ roughness: 0.3,
398
+ metalness: 0.1
399
+ });
400
+ } else { // green
401
+ material = new THREE.MeshStandardMaterial({
402
+ color: 0x3B5E2E,
403
+ roughness: 0.3,
404
+ metalness: 0.1
405
+ });
406
+ }
407
+
408
+ // Create cabinet parts
409
+ const thickness = 0.75;
410
+ const doorThickness = 0.75;
411
+
412
+ // Bottom
413
+ const bottomGeometry = new THREE.BoxGeometry(width, thickness, depth);
414
+ const bottom = new THREE.Mesh(bottomGeometry, material);
415
+ bottom.position.y = thickness/2;
416
+ cabinetGroup.add(bottom);
417
+
418
+ // Top
419
+ const topGeometry = new THREE.BoxGeometry(width, thickness, depth);
420
+ const top = new THREE.Mesh(topGeometry, material);
421
+ top.position.y = height - thickness/2;
422
+ cabinetGroup.add(top);
423
+
424
+ // Sides
425
+ const leftSideGeometry = new THREE.BoxGeometry(thickness, height - 2*thickness, depth);
426
+ const leftSide = new THREE.Mesh(leftSideGeometry, material);
427
+ leftSide.position.set(-width/2 + thickness/2, height/2, 0);
428
+ cabinetGroup.add(leftSide);
429
+
430
+ const rightSideGeometry = new THREE.BoxGeometry(thickness, height - 2*thickness, depth);
431
+ const rightSide = new THREE.Mesh(rightSideGeometry, material);
432
+ rightSide.position.set(width/2 - thickness/2, height/2, 0);
433
+ cabinetGroup.add(rightSide);
434
+
435
+ // Back
436
+ const backGeometry = new THREE.BoxGeometry(width - 2*thickness, height - 2*thickness, thickness);
437
+ const back = new THREE.Mesh(backGeometry, material);
438
+ back.position.set(0, height/2, -depth/2 + thickness/2);
439
+ cabinetGroup.add(back);
440
+
441
+ // Create dividers if two doors
442
+ if (isTwoDoors) {
443
+ const dividerGeometry = new THREE.BoxGeometry(thickness, height - 2*thickness, depth);
444
+ const divider = new THREE.Mesh(dividerGeometry, material);
445
+ divider.position.set(0, height/2, 0);
446
+ cabinetGroup.add(divider);
447
+ }
448
+
449
+ // Doors (simplified representation)
450
+ const doorHeight = height - thickness;
451
+ let doorWidth;
452
+ if (isTwoDoors) {
453
+ doorWidth = (width - thickness) / 2;
454
+
455
+ // Left door
456
+ const leftDoorGeometry = new THREE.BoxGeometry(doorWidth, doorHeight, doorThickness);
457
+ const leftDoor = new THREE.Mesh(leftDoorGeometry, material);
458
+ leftDoor.position.set(-(width - doorWidth)/2 + doorThickness/2, height/2, depth/2 - doorThickness/2);
459
+ cabinetGroup.add(leftDoor);
460
+
461
+ // Right door
462
+ const rightDoorGeometry = new THREE.BoxGeometry(doorWidth, doorHeight, doorThickness);
463
+ const rightDoor = new THREE.Mesh(rightDoorGeometry, material);
464
+ rightDoor.position.set((width - doorWidth)/2 - doorThickness/2, height/2, depth/2 - doorThickness/2);
465
+ cabinetGroup.add(rightDoor);
466
+ } else {
467
+ doorWidth = width;
468
+
469
+ // Single door
470
+ const doorGeometry = new THREE.BoxGeometry(doorWidth, doorHeight, doorThickness);
471
+ const door = new THREE.Mesh(doorGeometry, material);
472
+ door.position.set(0, height/2, depth/2 - doorThickness/2);
473
+ cabinetGroup.add(door);
474
+ }
475
+
476
+ // Add drawer if base cabinet
477
+ if (type === 'base') {
478
+ const drawerHeight = height * 0.3;
479
+ const drawerGeometry = new THREE.BoxGeometry(width * 0.9, drawerHeight, depth * 0.5);
480
+ const drawer = new THREE.Mesh(drawerGeometry, material);
481
+ drawer.position.set(0, drawerHeight/2 + thickness, depth/4 - depth/8);
482
+ cabinetGroup.add(drawer);
483
+
484
+ // Drawer handle
485
+ const handleGeometry = new THREE.CylinderGeometry(0.2, 0.2, 2, 16);
486
+ const handleMaterial = new THREE.MeshStandardMaterial({ color: 0x333333 });
487
+ const handle = new THREE.Mesh(handleGeometry, handleMaterial);
488
+ handle.rotation.z = Math.PI/2;
489
+ handle.position.set(0, drawerHeight + thickness - 1, depth * 0.25 + 0.4);
490
+ cabinetGroup.add(handle);
491
+ }
492
+
493
+ // Adjust camera target based on cabinet size
494
+ controls.target.set(0, height/2, 0);
495
+ controls.update();
496
+
497
+ // Hide loading after a delay (in a real app, this would be when the model is ready)
498
+ setTimeout(hideLoading, 800);
499
+
500
+ // Update capacity calculation
501
+ const interiorWidth = width - 2*thickness;
502
+ const interiorHeight = height - 2*thickness;
503
+ const interiorDepth = depth - thickness;
504
+ const capacity = (interiorWidth * interiorHeight * interiorDepth) / 1728; // convert to cubic feet
505
+
506
+ capacityDisplay.textContent = `${capacity.toFixed(1)} cu.ft`;
507
+ dimensionsDisplay.textContent = `${width}" W × ${height}" H × ${depth}" D`;
508
+ }
509
+
510
+ function showLoading() {
511
+ loadingOverlay.style.display = 'flex';
512
+ let progress = 0;
513
+ const interval = setInterval(() => {
514
+ progress += 10;
515
+ const offset = 314 * (1 - progress/100);
516
+ progressRing.style.strokeDashoffset = offset;
517
+ if (progress >= 100) {
518
+ clearInterval(interval);
519
+ }
520
+ }, 50);
521
+ }
522
+
523
+ function hideLoading() {
524
+ loadingOverlay.style.display = 'none';
525
+ }
526
+
527
+ // Update price calculation
528
+ function updatePrice() {
529
+ const width = parseFloat(widthSlider.value);
530
+ const height = parseFloat(heightSlider.value);
531
+ const depth = parseFloat(depthSlider.value);
532
+ const mat = material.value;
533
+ const isTwoDoors = twoDoorsBtn.classList.contains('bg-indigo-100');
534
+ const hasSoftClose = softClose.checked;
535
+ const hasPullOutShelves = pullOutShelves.checked;
536
+ const hasLighting = lighting.checked;
537
+
538
+ // Calculate base price based on size and type
539
+ let base = 100;
540
+ const sizeFactor = (width * height * depth) / 10000;
541
+ base += sizeFactor * 100;
542
+
543
+ if (cabinetType.value === 'base') base *= 1.2;
544
+ else if (cabinetType.value === 'tall') base *= 1.3;
545
+ else if (cabinetType.value === 'island') base *= 1.5;
546
+
547
+ // Material premium
548
+ let matPremium = 0;
549
+ if (mat === 'cherry' || mat === 'walnut') matPremium = 100;
550
+ else if (mat === 'maple') matPremium = 60;
551
+ else if (mat === 'oak') matPremium = 30;
552
+ else if (mat === 'painted') matPremium = 40;
553
+
554
+ // Features
555
+ let featuresPremium = 0;
556
+ if (hasSoftClose) featuresPremium += 30;
557
+ if (hasPullOutShelves) featuresPremium += 60;
558
+ if (hasLighting) featuresPremium += 45;
559
+ if (isTwoDoors) featuresPremium += 25;
560
+
561
+ // Update displays
562
+ basePrice.textContent = `$${base.toFixed(2)}`;
563
+ materialPrice.textContent = `$${matPremium.toFixed(2)}`;
564
+ featuresPrice.textContent = `$${featuresPremium.toFixed(2)}`;
565
+ totalPrice.textContent = `$${(base + matPremium + featuresPremium).toFixed(2)}`;
566
+ }
567
+
568
+ // Event listeners
569
+ widthSlider.addEventListener('input', () => {
570
+ widthValue.textContent = widthSlider.value;
571
+ updateCabinetModel();
572
+ updatePrice();
573
+ });
574
+
575
+ heightSlider.addEventListener('input', () => {
576
+ heightValue.textContent = heightSlider.value;
577
+ updateCabinetModel();
578
+ updatePrice();
579
+ });
580
+
581
+ depthSlider.addEventListener('input', () => {
582
+ depthValue.textContent = depthSlider.value;
583
+ updateCabinetModel();
584
+ updatePrice();
585
+ });
586
+
587
+ oneDoorBtn.addEventListener('click', () => {
588
+ oneDoorBtn.classList.add('bg-indigo-100', 'text-indigo-700', 'border-indigo-300');
589
+ twoDoorsBtn.classList.remove('bg-indigo-100', 'text-indigo-700', 'border-indigo-300');
590
+ updateCabinetModel();
591
+ updatePrice();
592
+ });
593
+
594
+ twoDoorsBtn.addEventListener('click', () => {
595
+ twoDoorsBtn.classList.add('bg-indigo-100', 'text-indigo-700', 'border-indigo-300');
596
+ oneDoorBtn.classList.remove('bg-indigo-100', 'text-indigo-700', 'border-indigo-300');
597
+ updateCabinetModel();
598
+ updatePrice();
599
+ });
600
+
601
+ cabinetType.addEventListener('change', () => {
602
+ cabinetTypeDisplay.textContent = cabinetType.options[cabinetType.selectedIndex].text;
603
+
604
+ // Adjust default dimensions based on cabinet type
605
+ if (cabinetType.value === 'wall') {
606
+ heightSlider.value = 36;
607
+ depthSlider.value = 12;
608
+ heightValue.textContent = '36';
609
+ depthValue.textContent = '12';
610
+ } else if (cabinetType.value === 'base') {
611
+ heightSlider.value = 34.5;
612
+ depthSlider.value = 24;
613
+ heightValue.textContent = '34.5';
614
+ depthValue.textContent = '24';
615
+ } else if (cabinetType.value === 'tall') {
616
+ heightSlider.value = 84;
617
+ depthSlider.value = 24;
618
+ heightValue.textContent = '84';
619
+ depthValue.textContent = '24';
620
+ } else if (cabinetType.value === 'island') {
621
+ heightSlider.value = 36;
622
+ depthSlider.value = 24;
623
+ heightValue.textContent = '36';
624
+ depthValue.textContent = '24';
625
+ }
626
+
627
+ updateCabinetModel();
628
+ updatePrice();
629
+ });
630
+
631
+ material.addEventListener('change', () => {
632
+ materialDisplay.textContent = material.options[material.selectedIndex].text;
633
+ updatePrice();
634
+ updateCabinetModel();
635
+ });
636
+
637
+ color.addEventListener('change', () => {
638
+ colorDisplay.textContent = color.options[color.selectedIndex].text;
639
+ updateCabinetModel();
640
+ });
641
+
642
+ softClose.addEventListener('change', updatePrice);
643
+ pullOutShelves.addEventListener('change', updatePrice);
644
+ lighting.addEventListener('change', updatePrice);
645
+
646
+ // Simulate download buttons (in a real app, these would generate actual files)
647
+ downloadUSDZ.addEventListener('click', () => {
648
+ alert('In a real implementation, this would generate and download a USDZ file of your cabinet design.');
649
+ });
650
+
651
+ downloadGLB.addEventListener('click', () => {
652
+ alert('In a real implementation, this would generate and download a GLB file of your cabinet design.');
653
+ });
654
+
655
+ // Initialize
656
+ window.addEventListener('load', () => {
657
+ initThreeJS();
658
+ updatePrice();
659
+ });
660
+ </script>
661
+ <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=gladiopeace/cabinet" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
662
+ </html>