// Main application logic document.addEventListener('DOMContentLoaded', function() { // Style selection const styleButtons = document.querySelectorAll('.style-btn'); let selectedStyle = 'realistic'; styleButtons.forEach(btn => { btn.addEventListener('click', () => { styleButtons.forEach(b => b.classList.remove('active')); btn.classList.add('active'); selectedStyle = btn.dataset.style; }); }); // Set default active style document.querySelector('[data-style="realistic"]').classList.add('active'); // Generate button functionality const generateBtn = document.getElementById('generate-btn'); const textInput = document.getElementById('text-input'); const progressContainer = document.getElementById('progress-container'); const progressBar = document.getElementById('progress-bar'); const progressPercent = document.getElementById('progress-percent'); const previewContainer = document.getElementById('preview-container'); const canvas = document.getElementById('3d-canvas'); const actionButtons = document.getElementById('action-buttons'); const renderTime = document.getElementById('render-time'); generateBtn.addEventListener('click', async () => { const text = textInput.value.trim(); if (!text) { showNotification('Please enter a description for your 3D design', 'warning'); return; } // Show progress progressContainer.classList.remove('hidden'); generateBtn.disabled = true; generateBtn.classList.add('opacity-50', 'cursor-not-allowed'); // Simulate generation progress let progress = 0; const startTime = Date.now(); const progressInterval = setInterval(() => { progress += Math.random() * 15; if (progress > 90) progress = 90; progressBar.style.width = `${progress}%`; progressPercent.textContent = `${Math.round(progress)}%`; }, 200); // Simulate API call setTimeout(() => { clearInterval(progressInterval); progressBar.style.width = '100%'; progressPercent.textContent = '100%'; // Generate 3D visualization generate3DVisualization(text, selectedStyle); // Calculate render time const endTime = Date.now(); const renderDuration = ((endTime - startTime) / 1000).toFixed(1); renderTime.textContent = `Rendered in ${renderDuration}s`; // Show result setTimeout(() => { progressContainer.classList.add('hidden'); generateBtn.disabled = false; generateBtn.classList.remove('opacity-50', 'cursor-not-allowed'); actionButtons.classList.remove('hidden'); canvas.classList.remove('hidden'); previewContainer.querySelector('.text-center').classList.add('hidden'); // Add to recent creations addToRecentCreations(text); }, 500); }, 3000); }); // Enhanced 3D Visualization function generate3DVisualization(description, style) { const ctx = canvas.getContext('2d'); canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; // Store the visualization data for animation canvas.dataset.description = description; canvas.dataset.style = style; canvas.dataset.rotation = '0'; draw3DModel(ctx, canvas.width, canvas.height, style, 0); } function draw3DModel(ctx, width, height, style, rotation) { // Clear canvas ctx.clearRect(0, 0, width, height); // Create gradient background based on style let gradient; switch(style) { case 'cartoon': gradient = ctx.createLinearGradient(0, 0, width, height); gradient.addColorStop(0, '#fef3c7'); gradient.addColorStop(0.5, '#fed7aa'); gradient.addColorStop(1, '#fbbf24'); break; case 'minimalist': gradient = ctx.createLinearGradient(0, 0, width, height); gradient.addColorStop(0, '#f3f4f6'); gradient.addColorStop(0.5, '#e5e7eb'); gradient.addColorStop(1, '#d1d5db'); break; case 'fantasy': gradient = ctx.createRadialGradient(width/2, height/2, 0, width/2, height/2, width/2); gradient.addColorStop(0, '#ddd6fe'); gradient.addColorStop(0.3, '#f9a8d4'); gradient.addColorStop(0.6, '#c084fc'); gradient.addColorStop(1, '#818cf8'); break; default: // realistic gradient = ctx.createLinearGradient(0, 0, width, height); gradient.addColorStop(0, '#0f172a'); gradient.addColorStop(0.5, '#1e293b'); gradient.addColorStop(1, '#334155'); } ctx.fillStyle = gradient; ctx.fillRect(0, 0, width, height); // Add grid for depth perception ctx.strokeStyle = style === 'minimalist' ? 'rgba(0, 0, 0, 0.1)' : 'rgba(255, 255, 255, 0.05)'; ctx.lineWidth = 1; const gridSize = 30; for (let x = 0; x <= width; x += gridSize) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke(); } for (let y = 0; y <= height; y += gridSize) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); } const centerX = width / 2; const centerY = height / 2; // Main 3D object - More complex and realistic ctx.save(); ctx.translate(centerX, centerY); // Rotate the entire scene ctx.rotate(rotation); // Draw 3D cube with proper perspective const size = 100; const depth = 60; // Back faces (darker) ctx.fillStyle = style === 'cartoon' ? 'rgba(251, 191, 36, 0.4)' : style === 'minimalist' ? 'rgba(156, 163, 175, 0.3)' : style === 'fantasy' ? 'rgba(168, 85, 247, 0.4)' : 'rgba(14, 165, 233, 0.3)'; ctx.strokeStyle = style === 'cartoon' ? 'rgba(251, 191, 36, 0.8)' : style === 'minimalist' ? 'rgba(75, 85, 99, 0.8)' : style === 'fantasy' ? 'rgba(168, 85, 247, 0.8)' : 'rgba(14, 165, 233, 0.8)'; ctx.lineWidth = 2; // Back face ctx.beginPath(); ctx.rect(-size/2 - depth/3, -size/2 - depth/3, size, size); ctx.fill(); ctx.stroke(); // Side faces ctx.fillStyle = style === 'cartoon' ? 'rgba(251, 191, 36, 0.5)' : style === 'minimalist' ? 'rgba(156, 163, 175, 0.4)' : style === 'fantasy' ? 'rgba(236, 72, 153, 0.5)' : 'rgba(217, 70, 239, 0.4)'; // Right face ctx.beginPath(); ctx.moveTo(size/2, -size/2); ctx.lineTo(size/2 + depth/3, -size/2 - depth/3); ctx.lineTo(size/2 + depth/3, size/2 - depth/3); ctx.lineTo(size/2, size/2); ctx.closePath(); ctx.fill(); ctx.stroke(); // Top face ctx.beginPath(); ctx.moveTo(-size/2, -size/2); ctx.lineTo(-size/2 - depth/3, -size/2 - depth/3); ctx.lineTo(size/2 - depth/3, -size/2 - depth/3); ctx.lineTo(size/2, -size/2); ctx.closePath(); ctx.fill(); ctx.stroke(); // Front face (brightest) ctx.fillStyle = style === 'cartoon' ? 'rgba(251, 191, 36, 0.7)' : style === 'minimalist' ? 'rgba(156, 163, 175, 0.6)' : style === 'fantasy' ? 'rgba(236, 72, 153, 0.7)' : 'rgba(14, 165, 233, 0.6)'; ctx.beginPath(); ctx.rect(-size/2, -size/2, size, size); ctx.fill(); ctx.stroke(); // Add decorative elements based on style if (style === 'fantasy') { // Add sparkles ctx.fillStyle = 'rgba(255, 255, 255, 0.8)'; for (let i = 0; i < 5; i++) { const sparkleX = (Math.random() - 0.5) * size * 1.5; const sparkleY = (Math.random() - 0.5) * size * 1.5; const sparkleSize = Math.random() * 3 + 1; ctx.beginPath(); ctx.arc(sparkleX, sparkleY, sparkleSize, 0, Math.PI * 2); ctx.fill(); } } else if (style === 'cartoon') { // Add cartoon eyes ctx.fillStyle = 'white'; ctx.beginPath(); ctx.arc(-20, -10, 12, 0, Math.PI * 2); ctx.arc(20, -10, 12, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = 'black'; ctx.beginPath(); ctx.arc(-20, -10, 6, 0, Math.PI * 2); ctx.arc(20, -10, 6, 0, Math.PI * 2); ctx.fill(); } ctx.restore(); // Add shadow ctx.save(); ctx.translate(centerX, centerY + 100); ctx.scale(1, 0.3); ctx.fillStyle = 'rgba(0, 0, 0, 0.2)'; ctx.beginPath(); ctx.ellipse(0, 0, size/2, size/4, 0, 0, Math.PI * 2); ctx.fill(); ctx.restore(); // Add text overlay ctx.fillStyle = style === 'minimalist' ? 'rgba(0, 0, 0, 0.7)' : 'rgba(255, 255, 255, 0.9)'; ctx.font = 'bold 14px sans-serif'; ctx.textAlign = 'center'; ctx.fillText('3D Model Generated!', centerX, height - 20); } // Add to recent creations function addToRecentCreations(description) { const recentContainer = document.getElementById('recent-creations'); const newItem = document.createElement('div'); newItem.className = 'bg-gray-800/50 rounded-lg overflow-hidden hover:scale-105 transition-transform duration-300 cursor-pointer opacity-0'; const randomSeed = Math.floor(Math.random() * 100); newItem.innerHTML = ` Creation

${description.substring(0, 30)}${description.length > 30 ? '...' : ''}

Just now

`; recentContainer.insertBefore(newItem, recentContainer.firstChild); // Animate in setTimeout(() => { newItem.classList.remove('opacity-0'); newItem.classList.add('opacity-100'); }, 100); // Remove last item if more than 4 if (recentContainer.children.length > 4) { recentContainer.removeChild(recentContainer.lastChild); } } // Notification system function showNotification(message, type = 'info') { const notification = document.createElement('div'); notification.className = `fixed top-20 right-4 px-6 py-3 rounded-lg shadow-lg transform translate-x-full transition-transform duration-300 z-50`; const bgColor = type === 'warning' ? 'bg-yellow-500' : type === 'error' ? 'bg-red-500' : 'bg-primary-500'; notification.classList.add(bgColor); notification.innerHTML = `
${message}
`; document.body.appendChild(notification); feather.replace(); // Animate in setTimeout(() => { notification.classList.remove('translate-x-full'); notification.classList.add('translate-x-0'); }, 100); // Remove after 3 seconds setTimeout(() => { notification.classList.add('translate-x-full'); setTimeout(() => { notification.remove(); }, 300); }, 3000); } // Enhanced Canvas animation loop let animationId; function animateCanvas() { if (!canvas.classList.contains('hidden') && canvas.dataset.style) { const rotation = (Date.now() / 1000) % (Math.PI * 2); const ctx = canvas.getContext('2d'); draw3DModel( ctx, canvas.width, canvas.height, canvas.dataset.style, rotation * 0.5 ); } animationId = requestAnimationFrame(animateCanvas); } // Start animation loop animateCanvas(); // Clean up animation when canvas is hidden const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'attributes' && mutation.attributeName === 'class') { if (canvas.classList.contains('hidden')) { cancelAnimationFrame(animationId); } else if (canvas.dataset.style) { animateCanvas(); } } }); }); observer.observe(canvas, { attributes: true }); }); // Smooth scroll for anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth' }); } }); }); // Intersection Observer for animations const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('animate-fadeIn'); } }); }, observerOptions); document.querySelectorAll('.section-animate').forEach(el => { observer.observe(el); });