let selectedFile = null; let currentTrajectories = null; // DOM elements const uploadBox = document.getElementById('uploadBox'); const videoInput = document.getElementById('videoInput'); const uploadBtn = document.getElementById('uploadBtn'); const loadingSection = document.getElementById('loadingSection'); const resultsSection = document.getElementById('resultsSection'); const playBtn = document.getElementById('playBtn'); const resetBtn = document.getElementById('resetBtn'); const showTrails = document.getElementById('showTrails'); // Upload box click uploadBox.addEventListener('click', () => { videoInput.click(); }); // File selection videoInput.addEventListener('change', (e) => { const file = e.target.files[0]; if (file) { selectedFile = file; uploadBox.querySelector('h3').textContent = `Selected: ${file.name}`; uploadBox.querySelector('p').textContent = `Size: ${(file.size / 1024 / 1024).toFixed(2)} MB`; uploadBtn.style.display = 'block'; } }); // Drag and drop uploadBox.addEventListener('dragover', (e) => { e.preventDefault(); uploadBox.classList.add('dragover'); }); uploadBox.addEventListener('dragleave', () => { uploadBox.classList.remove('dragover'); }); uploadBox.addEventListener('drop', (e) => { e.preventDefault(); uploadBox.classList.remove('dragover'); const file = e.dataTransfer.files[0]; if (file && file.type.startsWith('video/')) { selectedFile = file; uploadBox.querySelector('h3').textContent = `Selected: ${file.name}`; uploadBox.querySelector('p').textContent = `Size: ${(file.size / 1024 / 1024).toFixed(2)} MB`; uploadBtn.style.display = 'block'; } else { alert('Please drop a valid video file'); } }); // Upload and process uploadBtn.addEventListener('click', async () => { if (!selectedFile) return; const formData = new FormData(); formData.append('file', selectedFile); // Show loading document.querySelector('.upload-section').style.display = 'none'; loadingSection.style.display = 'block'; try { const response = await fetch('/api/upload', { method: 'POST', body: formData }); if (!response.ok) { throw new Error('Upload failed'); } const data = await response.json(); currentTrajectories = data.trajectories; // Display results displayResults(data.trajectories); loadingSection.style.display = 'none'; resultsSection.style.display = 'block'; } catch (error) { console.error('Error:', error); alert('Error processing video: ' + error.message); loadingSection.style.display = 'none'; document.querySelector('.upload-section').style.display = 'block'; } }); // Display results function displayResults(data) { const metadata = data.metadata; const trajectories = data.trajectories; // Update stats document.getElementById('statObjects').textContent = metadata.num_objects; document.getElementById('statFrames').textContent = metadata.frame_count; document.getElementById('statFPS').textContent = metadata.fps.toFixed(1); document.getElementById('statDuration').textContent = (metadata.frame_count / metadata.fps).toFixed(1) + 's'; // Initialize 3D visualization if (window.visualizer) { window.visualizer.destroy(); } window.visualizer = new TrajectoryVisualizer('canvas3d'); window.visualizer.loadTrajectories(trajectories); } // Control buttons playBtn.addEventListener('click', () => { if (window.visualizer) { if (window.visualizer.isPlaying) { window.visualizer.pause(); playBtn.textContent = '▶ Play'; } else { window.visualizer.play(); playBtn.textContent = '⏸ Pause'; } } }); resetBtn.addEventListener('click', () => { if (window.visualizer) { window.visualizer.reset(); playBtn.textContent = '▶ Play'; } }); showTrails.addEventListener('change', (e) => { if (window.visualizer) { window.visualizer.setShowTrails(e.target.checked); } });