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

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +390 -129
index.html CHANGED
@@ -38,10 +38,21 @@
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">
@@ -105,6 +116,24 @@
105
  </div>
106
  </div>
107
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  <!-- Material -->
109
  <div class="mb-6 slider-container">
110
  <div class="flex justify-between items-center mb-2">
@@ -153,6 +182,10 @@
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>
@@ -167,14 +200,18 @@
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>
@@ -197,7 +234,7 @@
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>
@@ -225,6 +262,14 @@
225
  <span class="text-gray-600">Material Thickness:</span>
226
  <span class="font-medium">0.75"</span>
227
  </div>
 
 
 
 
 
 
 
 
228
  </div>
229
  </div>
230
 
@@ -272,9 +317,11 @@
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');
@@ -283,6 +330,14 @@
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;
@@ -298,7 +353,7 @@
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);
@@ -343,13 +398,12 @@
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
@@ -364,151 +418,316 @@
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;
@@ -517,11 +736,12 @@
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
@@ -534,35 +754,45 @@
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
@@ -598,9 +828,35 @@
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;
@@ -636,12 +892,17 @@
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', () => {
@@ -649,7 +910,7 @@
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
 
38
  background-color: #f5f3f0;
39
  border-radius: 8px;
40
  overflow: hidden;
41
+ position: relative;
42
  }
43
  #priceSection {
44
  background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
45
  }
46
+ .door-handle {
47
+ position: relative;
48
+ z-index: 5;
49
+ }
50
+ .shelf {
51
+ box-shadow: 0 -2px 5px rgba(0,0,0,0.1);
52
+ }
53
+ #loadingOverlay {
54
+ transition: opacity 0.3s ease;
55
+ }
56
  </style>
57
  </head>
58
  <body class="bg-gray-50">
 
116
  </div>
117
  </div>
118
 
119
+ <!-- Shelves -->
120
+ <div class="mb-6 slider-container">
121
+ <div class="flex justify-between items-center mb-2">
122
+ <label class="text-gray-700 font-medium">Number of Shelves</label>
123
+ <span id="shelvesValue" class="text-gray-500">1</span>
124
+ </div>
125
+ <input id="shelvesSlider" type="range" min="0" max="5" value="1" step="1" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
126
+ </div>
127
+
128
+ <!-- Drawers (only for base cabinets) -->
129
+ <div class="mb-6 slider-container" id="drawersContainer" style="display: none;">
130
+ <div class="flex justify-between items-center mb-2">
131
+ <label class="text-gray-700 font-medium">Number of Drawers</label>
132
+ <span id="drawersValue" class="text-gray-500">0</span>
133
+ </div>
134
+ <input id="drawersSlider" type="range" min="0" max="3" value="0" step="1" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
135
+ </div>
136
+
137
  <!-- Material -->
138
  <div class="mb-6 slider-container">
139
  <div class="flex justify-between items-center mb-2">
 
182
  <input type="checkbox" id="lighting" class="form-checkbox h-5 w-5 text-indigo-600 rounded">
183
  <span class="text-gray-700">LED Lighting</span>
184
  </label>
185
+ <label class="flex items-center space-x-3">
186
+ <input type="checkbox" id="glassDoors" class="form-checkbox h-5 w-5 text-indigo-600 rounded">
187
+ <span class="text-gray-700">Glass Doors</span>
188
+ </label>
189
  </div>
190
  </div>
191
  </div>
 
200
  <span class="text-gray-600">Material Upgrade</span>
201
  <span id="materialPrice" class="font-medium">$50.00</span>
202
  </div>
203
+ <div class="flex justify-between items-center mb-2">
204
  <span class="text-gray-600">Features</span>
205
  <span id="featuresPrice" class="font-medium">$75.00</span>
206
  </div>
207
+ <div class="flex justify-between items-center mb-4">
208
+ <span class="text-gray-600">Options</span>
209
+ <span id="optionsPrice" class="font-medium">$25.00</span>
210
+ </div>
211
  <div class="border-t border-gray-300 pt-4">
212
  <div class="flex justify-between items-center">
213
  <span class="text-xl font-bold text-gray-800">Total</span>
214
+ <span id="totalPrice" class="text-2xl font-bold text-indigo-600">$450.00</span>
215
  </div>
216
  </div>
217
  </div>
 
234
  </div>
235
  </div>
236
  <div id="modelViewer" class="w-full h-96 rounded-b-xl">
237
+ <div id="loadingOverlay" class="absolute inset-0 bg-gray-100 bg-opacity-70 flex flex-col items-center justify-center opacity-0 pointer-events-none">
238
  <div class="w-16 h-16 relative mb-4">
239
  <svg class="w-full h-full" viewBox="0 0 100 100">
240
  <circle class="text-gray-400" cx="50" cy="50" r="40" stroke-width="8" stroke="currentColor" fill="none"></circle>
 
262
  <span class="text-gray-600">Material Thickness:</span>
263
  <span class="font-medium">0.75"</span>
264
  </div>
265
+ <div class="flex justify-between">
266
+ <span class="text-gray-600">Shelves:</span>
267
+ <span id="shelvesDisplay" class="font-medium">1</span>
268
+ </div>
269
+ <div class="flex justify-between" id="drawersDisplayContainer" style="display: none;">
270
+ <span class="text-gray-600">Drawers:</span>
271
+ <span id="drawersDisplay" class="font-medium">0</span>
272
+ </div>
273
  </div>
274
  </div>
275
 
 
317
  const softClose = document.getElementById('softClose');
318
  const pullOutShelves = document.getElementById('pullOutShelves');
319
  const lighting = document.getElementById('lighting');
320
+ const glassDoors = document.getElementById('glassDoors');
321
  const basePrice = document.getElementById('basePrice');
322
  const materialPrice = document.getElementById('materialPrice');
323
  const featuresPrice = document.getElementById('featuresPrice');
324
+ const optionsPrice = document.getElementById('optionsPrice');
325
  const totalPrice = document.getElementById('totalPrice');
326
  const dimensionsDisplay = document.getElementById('dimensionsDisplay');
327
  const capacityDisplay = document.getElementById('capacityDisplay');
 
330
  const downloadUSDZ = document.getElementById('downloadUSDZ');
331
  const downloadGLB = document.getElementById('downloadGLB');
332
  const progressRing = document.querySelector('.progress-ring');
333
+ const shelvesSlider = document.getElementById('shelvesSlider');
334
+ const shelvesValue = document.getElementById('shelvesValue');
335
+ const shelvesDisplay = document.getElementById('shelvesDisplay');
336
+ const drawersSlider = document.getElementById('drawersSlider');
337
+ const drawersValue = document.getElementById('drawersValue');
338
+ const drawersContainer = document.getElementById('drawersContainer');
339
+ const drawersDisplay = document.getElementById('drawersDisplay');
340
+ const drawersDisplayContainer = document.getElementById('drawersDisplayContainer');
341
 
342
  // Three.js variables
343
  let scene, camera, renderer, controls, cabinetGroup;
 
353
  camera.position.set(30, 30, 30);
354
 
355
  // Create renderer
356
+ renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
357
  renderer.setSize(modelViewer.clientWidth, modelViewer.clientHeight);
358
  renderer.shadowMap.enabled = true;
359
  modelViewer.appendChild(renderer.domElement);
 
398
 
399
  function animate() {
400
  requestAnimationFrame(animate);
401
+ if (controls) controls.update();
402
+ if (renderer) renderer.render(scene, camera);
403
  }
404
 
405
+ // Create cabinet geometry with detailed components
406
  function updateCabinetModel() {
 
407
  showLoading();
408
 
409
  // Clear previous cabinet
 
418
  const type = cabinetType.value;
419
  const isTwoDoors = twoDoorsBtn.classList.contains('bg-indigo-100');
420
  const colorValue = color.value;
421
+ const shelvesCount = parseInt(shelvesSlider.value);
422
+ const drawersCount = cabinetType.value === 'base' ? parseInt(drawersSlider.value) : 0;
423
+ const hasGlassDoors = glassDoors.checked;
424
+
425
+ // Set main material
426
+ let mainMaterial = createMaterial(colorValue, false);
427
+ let interiorMaterial = new THREE.MeshStandardMaterial({
428
+ color: 0xF5F5F5,
429
+ roughness: 0.7,
430
+ metalness: 0.0
431
+ });
432
+ let shelfMaterial = new THREE.MeshStandardMaterial({
433
+ color: 0xEAEAEA,
434
+ roughness: 0.6,
435
+ metalness: 0.0
436
+ });
437
+
438
+ // Cabinet dimensions
439
+ const thickness = 0.75;
440
+ const doorThickness = 1.0;
441
+ const handleSize = 0.2;
442
+ const shelfThickness = 0.5;
443
+
444
+ // Bottom panel
445
+ createPanel(width, thickness, depth,
446
+ [0, thickness/2, 0],
447
+ mainMaterial);
448
+
449
+ // Top panel
450
+ createPanel(width, thickness, depth,
451
+ [0, height - thickness/2, 0],
452
+ mainMaterial);
453
+
454
+ // Left side
455
+ createPanel(thickness, height - 2*thickness, depth,
456
+ [-width/2 + thickness/2, height/2, 0],
457
+ mainMaterial);
458
+
459
+ // Right side
460
+ createPanel(thickness, height - 2*thickness, depth,
461
+ [width/2 - thickness/2, height/2, 0],
462
+ mainMaterial);
463
+
464
+ // Back panel
465
+ createPanel(width - 2*thickness, height - 2*thickness, thickness,
466
+ [0, height/2, -depth/2 + thickness/2],
467
+ mainMaterial);
468
+
469
+ // Middle divider if two doors
470
+ if (isTwoDoors) {
471
+ createPanel(thickness, height - 2*thickness - (drawersCount > 0 ? (height * 0.33) : 0), depth,
472
+ [0, height/2 - (drawersCount > 0 ? (height * 0.33)/2 : 0), 0],
473
+ mainMaterial);
474
+ }
475
+
476
+ // Create doors
477
+ const doorHeight = height - thickness - (drawersCount > 0 ? (height * 0.33) : 0);
478
+ const doorYPosition = height/2 - (drawersCount > 0 ? (height * 0.33)/2 : 0);
479
+
480
+ if (isTwoDoors) {
481
+ const doorWidth = (width - thickness) / 2;
482
+
483
+ // Left door
484
+ createDoor(doorWidth, doorHeight, doorThickness,
485
+ [-(width - doorWidth)/2 + doorThickness/2, doorYPosition, depth/2 - doorThickness/2],
486
+ mainMaterial, hasGlassDoors, true);
487
+
488
+ // Right door
489
+ createDoor(doorWidth, doorHeight, doorThickness,
490
+ [(width - doorWidth)/2 - doorThickness/2, doorYPosition, depth/2 - doorThickness/2],
491
+ mainMaterial, hasGlassDoors, false);
492
+ } else {
493
+ // Single door
494
+ const doorWidth = width;
495
+ createDoor(doorWidth, doorHeight, doorThickness,
496
+ [0, doorYPosition, depth/2 - doorThickness/2],
497
+ mainMaterial, hasGlassDoors, false);
498
+ }
499
+
500
+ // Add shelves
501
+ const availableHeight = height - 2*thickness - (drawersCount > 0 ? (height * 0.33) : 0);
502
+ const shelfSpacing = availableHeight / (shelvesCount + 1);
503
+
504
+ for (let i = 0; i < shelvesCount; i++) {
505
+ const shelfY = thickness + (i + 1) * shelfSpacing - (drawersCount > 0 ? (height * 0.33)/2 : 0);
506
+ createShelf(width - 2*thickness - 0.5, shelfThickness, depth - thickness - 0.5,
507
+ [0, shelfY, -0.25],
508
+ shelfMaterial);
509
+ }
510
+
511
+ // Add drawers for base cabinets
512
+ if (type === 'base' && drawersCount > 0) {
513
+ const drawerHeight = (height * 0.33) / drawersCount;
514
+ const drawerFrontMaterial = createMaterial(colorValue, true); // Slightly different for drawer fronts
515
+
516
+ for (let i = 0; i < drawersCount; i++) {
517
+ const drawerY = thickness + i * drawerHeight + drawerHeight/2;
518
+ createDrawer(width, drawerHeight, depth * 0.7,
519
+ [0, drawerY, depth/4 - depth/8],
520
+ drawerFrontMaterial, mainMaterial);
521
+ }
522
+ }
523
+
524
+ // Adjust camera target based on cabinet size
525
+ controls.target.set(0, height/2, 0);
526
+ controls.update();
527
+
528
+ // Update capacity calculation
529
+ const interiorWidth = width - 2*thickness;
530
+ const interiorHeight = height - 2*thickness - (drawersCount > 0 ? (height * 0.33) : 0);
531
+ const interiorDepth = depth - thickness;
532
+ const capacity = (interiorWidth * interiorHeight * interiorDepth) / 1728; // convert to cubic feet
533
+
534
+ // Hide loading after a delay
535
+ setTimeout(hideLoading, 800);
536
+
537
+ // Update displays
538
+ capacityDisplay.textContent = `${capacity.toFixed(1)} cu.ft`;
539
+ dimensionsDisplay.textContent = `${width}" W × ${height}" H × ${depth}" D`;
540
+ shelvesDisplay.textContent = shelvesCount;
541
+ }
542
+
543
+ function createMaterial(colorValue, isDrawerFront = false) {
544
+ let materialOptions = {};
545
 
 
 
546
  if (colorValue === 'natural') {
547
+ materialOptions = {
548
  color: 0xC19A6B,
549
+ roughness: isDrawerFront ? 0.3 : 0.4,
550
  metalness: 0.1
551
+ };
552
  } else if (colorValue === 'white') {
553
+ materialOptions = {
554
  color: 0xF5F5F5,
555
  roughness: 0.1,
556
  metalness: 0.05
557
+ };
558
  } else if (colorValue === 'black') {
559
+ materialOptions = {
560
  color: 0x222222,
561
  roughness: 0.3,
562
  metalness: 0.2
563
+ };
564
  } else if (colorValue === 'gray') {
565
+ materialOptions = {
566
  color: 0x808080,
567
  roughness: 0.3,
568
  metalness: 0.15
569
+ };
570
  } else if (colorValue === 'navy') {
571
+ materialOptions = {
572
  color: 0x2C3E50,
573
  roughness: 0.3,
574
  metalness: 0.1
575
+ };
576
  } else { // green
577
+ materialOptions = {
578
  color: 0x3B5E2E,
579
  roughness: 0.3,
580
  metalness: 0.1
581
+ };
582
  }
583
 
584
+ return new THREE.MeshStandardMaterial(materialOptions);
585
+ }
586
+
587
+ function createPanel(width, height, depth, position, material) {
588
+ const geometry = new THREE.BoxGeometry(width, height, depth);
589
+ const panel = new THREE.Mesh(geometry, material);
590
+ panel.castShadow = true;
591
+ panel.receiveShadow = true;
592
+ panel.position.set(position[0], position[1], position[2]);
593
+ cabinetGroup.add(panel);
594
+ return panel;
595
+ }
596
+
597
+ function createDoor(width, height, thickness, position, material, hasGlass, isLeftDoor) {
598
+ const doorGroup = new THREE.Group();
599
+ doorGroup.position.set(position[0], position[1], position[2]);
600
+
601
+ // Door frame
602
+ const frameGeometry = new THREE.BoxGeometry(width, height, thickness);
603
+ const frame = new THREE.Mesh(frameGeometry, material);
604
+ frame.castShadow = true;
605
+ frame.receiveShadow = true;
606
+ doorGroup.add(frame);
607
+
608
+ // Glass panel if selected
609
+ if (hasGlass) {
610
+ const glassWidth = width * 0.8;
611
+ const glassHeight = height * 0.7;
612
+ const glassGeometry = new THREE.BoxGeometry(glassWidth, glassHeight, thickness * 0.1);
613
+ const glassMaterial = new THREE.MeshPhysicalMaterial({
614
+ color: 0x88ccff,
615
+ transmission: 0.8,
616
+ roughness: 0.1,
617
+ metalness: 0.0,
618
+ transparent: true,
619
+ opacity: 0.7,
620
+ ior: 1.5,
621
+ thickness: 0.1
622
+ });
623
+ const glass = new THREE.Mesh(glassGeometry, glassMaterial);
624
+ glass.position.z = thickness * 0.45;
625
+ doorGroup.add(glass);
626
+ }
627
 
628
+ // Handle
629
+ const handleGeometry = new THREE.CylinderGeometry(0.15, 0.15, 3, 16);
630
+ const handleMaterial = new THREE.MeshStandardMaterial({ color: 0x333333 });
631
+ const handle = new THREE.Mesh(handleGeometry, handleMaterial);
632
+ handle.rotation.z = Math.PI/2;
633
+ handle.position.set(isLeftDoor ? -width/2 + 2 : width/2 - 2, 0, thickness);
634
+ doorGroup.add(handle);
635
 
636
+ cabinetGroup.add(doorGroup);
637
+ return doorGroup;
638
+ }
639
+
640
+ function createShelf(width, thickness, depth, position, material) {
641
+ const shelfGroup = new THREE.Group();
642
+ shelfGroup.position.set(position[0], position[1], position[2]);
643
+
644
+ // Main shelf
645
+ const shelfGeometry = new THREE.BoxGeometry(width, thickness, depth);
646
+ const shelf = new THREE.Mesh(shelfGeometry, material);
647
+ shelf.castShadow = true;
648
+ shelf.receiveShadow = true;
649
+ shelfGroup.add(shelf);
650
+
651
+ // Shelf supports
652
+ const supportGeometry = new THREE.BoxGeometry(0.5, 1, 0.5);
653
+ const supportMaterial = new THREE.MeshStandardMaterial({ color: 0xCCCCCC });
654
+
655
+ // Left support
656
+ const leftSupport = new THREE.Mesh(supportGeometry, supportMaterial);
657
+ leftSupport.position.set(-width/2 + 0.25, -thickness/2 - 0.5, -depth/2 + 0.25);
658
+ shelfGroup.add(leftSupport);
659
+
660
+ // Right support
661
+ const rightSupport = new THREE.Mesh(supportGeometry, supportMaterial);
662
+ rightSupport.position.set(width/2 - 0.25, -thickness/2 - 0.5, -depth/2 + 0.25);
663
+ shelfGroup.add(rightSupport);
664
+
665
+ if (width > 24) {
666
+ // Middle support for wider shelves
667
+ const midSupport = new THREE.Mesh(supportGeometry, supportMaterial);
668
+ midSupport.position.set(0, -thickness/2 - 0.5, -depth/2 + 0.25);
669
+ shelfGroup.add(midSupport);
670
+ }
671
 
672
+ cabinetGroup.add(shelfGroup);
673
+ return shelfGroup;
674
+ }
675
+
676
+ function createDrawer(width, height, depth, position, frontMaterial, sideMaterial) {
677
+ const drawerGroup = new THREE.Group();
678
+ drawerGroup.position.set(position[0], position[1], position[2]);
679
 
680
+ // Drawer box
681
+ const boxHeight = height * 0.9;
682
+ const boxYOffset = -height * 0.05;
 
 
 
 
683
 
684
+ // Front
685
+ const frontGeometry = new THREE.BoxGeometry(width * 0.9, height * 0.95, depth * 0.1);
686
+ const front = new THREE.Mesh(frontGeometry, frontMaterial);
687
+ front.position.set(0, boxYOffset, depth * 0.35);
688
+ drawerGroup.add(front);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
689
 
690
+ // Bottom
691
+ const bottomGeometry = new THREE.BoxGeometry(width * 0.85, 0.2, depth * 0.8);
692
+ const bottom = new THREE.Mesh(bottomGeometry, sideMaterial);
693
+ bottom.position.set(0, boxYOffset - boxHeight/2 + 0.1, 0);
694
+ drawerGroup.add(bottom);
 
 
 
 
 
 
 
 
 
 
 
695
 
696
+ // Sides
697
+ const sideWidth = width * 0.85;
698
+ const sideGeometry = new THREE.BoxGeometry(0.2, boxHeight, depth * 0.8);
699
 
700
+ // Left side
701
+ const leftSide = new THREE.Mesh(sideGeometry, sideMaterial);
702
+ leftSide.position.set(-sideWidth/2, boxYOffset, 0);
703
+ drawerGroup.add(leftSide);
704
 
705
+ // Right side
706
+ const rightSide = new THREE.Mesh(sideGeometry, sideMaterial);
707
+ rightSide.position.set(sideWidth/2, boxYOffset, 0);
708
+ drawerGroup.add(rightSide);
 
709
 
710
+ // Back
711
+ const backGeometry = new THREE.BoxGeometry(sideWidth, boxHeight, 0.2);
712
+ const back = new THREE.Mesh(backGeometry, sideMaterial);
713
+ back.position.set(0, boxYOffset, depth * -0.3);
714
+ drawerGroup.add(back);
715
+
716
+ // Handle (simplified as a bar)
717
+ const handleGeometry = new THREE.CylinderGeometry(0.1, 0.1, 4, 16);
718
+ const handleMaterial = new THREE.MeshStandardMaterial({ color: 0x333333 });
719
+ const handle = new THREE.Mesh(handleGeometry, handleMaterial);
720
+ handle.rotation.z = Math.PI/2;
721
+ handle.position.set(0, boxYOffset, depth * 0.35 + 0.1);
722
+ drawerGroup.add(handle);
723
+
724
+ cabinetGroup.add(drawerGroup);
725
+ return drawerGroup;
726
  }
727
 
728
  function showLoading() {
729
+ loadingOverlay.style.opacity = 1;
730
+ loadingOverlay.style.pointerEvents = 'auto';
731
  let progress = 0;
732
  const interval = setInterval(() => {
733
  progress += 10;
 
736
  if (progress >= 100) {
737
  clearInterval(interval);
738
  }
739
+ }, 30);
740
  }
741
 
742
  function hideLoading() {
743
+ loadingOverlay.style.opacity = 0;
744
+ loadingOverlay.style.pointerEvents = 'none';
745
  }
746
 
747
  // Update price calculation
 
754
  const hasSoftClose = softClose.checked;
755
  const hasPullOutShelves = pullOutShelves.checked;
756
  const hasLighting = lighting.checked;
757
+ const hasGlassDoors = glassDoors.checked;
758
+ const shelvesCount = parseInt(shelvesSlider.value);
759
+ const drawersCount = cabinetType.value === 'base' ? parseInt(drawersSlider.value) : 0;
760
 
761
  // Calculate base price based on size and type
762
  let base = 100;
763
  const sizeFactor = (width * height * depth) / 10000;
764
  base += sizeFactor * 100;
765
 
766
+ if (cabinetType.value === 'base') base *= 1.3;
767
+ else if (cabinetType.value === 'tall') base *= 1.4;
768
+ else if (cabinetType.value === 'island') base *= 1.6;
769
 
770
  // Material premium
771
  let matPremium = 0;
772
+ if (mat === 'cherry' || mat === 'walnut') matPremium = 120;
773
+ else if (mat === 'maple') matPremium = 80;
774
+ else if (mat === 'oak') matPremium = 40;
775
+ else if (mat === 'painted') matPremium = 60;
776
+ else if (mat === 'laminate') matPremium = 0;
777
 
778
  // Features
779
  let featuresPremium = 0;
780
+ if (hasSoftClose) featuresPremium += 40;
781
+ if (hasPullOutShelves) featuresPremium += 80;
782
+ if (hasLighting) featuresPremium += 60;
783
+ if (hasGlassDoors) featuresPremium += 90;
784
+
785
+ // Options (shelves, drawers)
786
+ let optionsPremium = shelvesCount * 20;
787
+ optionsPremium += drawersCount * 45;
788
+ if (isTwoDoors) optionsPremium += 35;
789
 
790
  // Update displays
791
  basePrice.textContent = `$${base.toFixed(2)}`;
792
  materialPrice.textContent = `$${matPremium.toFixed(2)}`;
793
  featuresPrice.textContent = `$${featuresPremium.toFixed(2)}`;
794
+ optionsPrice.textContent = `$${optionsPremium.toFixed(2)}`;
795
+ totalPrice.textContent = `$${(base + matPremium + featuresPremium + optionsPremium).toFixed(2)}`;
796
  }
797
 
798
  // Event listeners
 
828
  updatePrice();
829
  });
830
 
831
+ shelvesSlider.addEventListener('input', () => {
832
+ shelvesValue.textContent = shelvesSlider.value;
833
+ shelvesDisplay.textContent = shelvesSlider.value;
834
+ updateCabinetModel();
835
+ updatePrice();
836
+ });
837
+
838
+ drawersSlider.addEventListener('input', () => {
839
+ drawersValue.textContent = drawersSlider.value;
840
+ drawersDisplay.textContent = drawersSlider.value;
841
+ updateCabinetModel();
842
+ updatePrice();
843
+ });
844
+
845
  cabinetType.addEventListener('change', () => {
846
  cabinetTypeDisplay.textContent = cabinetType.options[cabinetType.selectedIndex].text;
847
 
848
+ // Show/hide drawers control based on cabinet type
849
+ if (cabinetType.value === 'base') {
850
+ drawersContainer.style.display = 'block';
851
+ drawersDisplayContainer.style.display = 'flex';
852
+ } else {
853
+ drawersContainer.style.display = 'none';
854
+ drawersDisplayContainer.style.display = 'none';
855
+ drawersSlider.value = 0;
856
+ drawersValue.textContent = '0';
857
+ drawersDisplay.textContent = '0';
858
+ }
859
+
860
  // Adjust default dimensions based on cabinet type
861
  if (cabinetType.value === 'wall') {
862
  heightSlider.value = 36;
 
892
 
893
  color.addEventListener('change', () => {
894
  colorDisplay.textContent = color.options[color.selectedIndex].text;
895
+ updatePrice();
896
  updateCabinetModel();
897
  });
898
 
899
  softClose.addEventListener('change', updatePrice);
900
  pullOutShelves.addEventListener('change', updatePrice);
901
  lighting.addEventListener('change', updatePrice);
902
+ glassDoors.addEventListener('change', () => {
903
+ updatePrice();
904
+ updateCabinetModel();
905
+ });
906
 
907
  // Simulate download buttons (in a real app, these would generate actual files)
908
  downloadUSDZ.addEventListener('click', () => {
 
910
  });
911
 
912
  downloadGLB.addEventListener('click', () => {
913
+ alert('In a real implementation, this would generate and download a GLB/GLTF file of your cabinet design.');
914
  });
915
 
916
  // Initialize