glitchlab/geoscatter-3d-raycast-geometry-sampler
0
1// Shared JavaScript for GeoScatter 3D Application2 3// DOM Elements4let canvasContainer;5let loadingOverlay;6let modelSelectBtn;7let modelDropdown;8let resetViewBtn;9let toggleGridBtn;10let rayCountSlider;11let rayValueSpan;12let visualizeRaysCheckbox;13let densityButtons;14let modeRadios;15let startSamplingBtn;16let clearRaysBtn;17let zoomInBtn;18let zoomOutBtn;19let activeRaysSpan;20 21// Sample Distribution Data (mock data)22const sampleData = {23 sphere: [65, 42, 38, 28, 17],24 cylinder: [42, 56, 45, 32, 15],25 organic: [38, 52, 48, 41, 22],26 torus: [28, 46, 52, 47, 31]27};28 29// Current Selected Model30let currentModel = 'sphere';31 32// Initialize when DOM is loaded33document.addEventListener('DOMContentLoaded', function() {34 // Get DOM Elements35 canvasContainer = document.getElementById('canvasContainer');36 loadingOverlay = document.getElementById('loadingOverlay');37 modelSelectBtn = document.getElementById('modelSelectBtn');38 modelDropdown = document.getElementById('modelDropdown');39 resetViewBtn = document.getElementById('resetView');40 toggleGridBtn = document.getElementById('toggleGrid');41 rayCountSlider = document.getElementById('rayCount');42 rayValueSpan = document.getElementById('rayValue');43 visualizeRaysCheckbox = document.getElementById('visualizeRays');44 densityButtons = document.querySelectorAll('.density-btn');45 modeRadios = document.querySelectorAll('input[name="mode"]');46 startSamplingBtn = document.getElementById('startSampling');47 clearRaysBtn = document.getElementById('clearRays');48 zoomInBtn = document.getElementById('zoomIn');49 zoomOutBtn = document.getElementById('zoomOut');50 activeRaysSpan = document.getElementById('activeRays');51 52 // Get coordinate display elements53 const coordX = document.getElementById('coordX');54 const coordY = document.getElementById('coordY');55 const coordZ = document.getElementById('coordZ');56 57 // Model Selection Logic58 const modelOptions = document.querySelectorAll('.model-option');59 modelOptions.forEach(option => {60 option.addEventListener('click', function() {61 const model = this.getAttribute('data-model');62 selectModel(model);63 64 // Close dropdown65 modelDropdown.style.display = 'none';66 67 // Update UI68 this.classList.add('bg-primary', 'text-white');69 this.classList.remove('hover:bg-gray-700');70 modelOptions.forEach(opt => {71 if (opt !== this) {72 opt.classList.remove('bg-primary', 'text-white');73 opt.classList.add('hover:bg-gray-700');74 }75 });76 });77 });78 79 // Toggle model dropdown80 if (modelSelectBtn) {81 modelSelectBtn.addEventListener('click', function() {82 modelDropdown.style.display = modelDropdown.style.display === 'block' ? 'none' : 'block';83 });84 }85 86 // Ray Count Slider87 if (rayCountSlider) {88 rayCountSlider.addEventListener('input', function() {89 const value = this.value;90 if (rayValueSpan) {91 rayValueSpan.textContent = value;92 }93 if (activeRaysSpan) {94 activeRaysSpan.textContent = value;95 }96 97 // Update visualization in scene (simulate)98 if (window.sceneManager) {99 window.sceneManager.updateRayCount(value);100 }101 });102 }103 104 // Density Buttons105 if (densityButtons) {106 densityButtons.forEach(btn => {107 btn.addEventListener('click', function() {108 const density = this.getAttribute('data-density');109 selectDensity(density);110 111 // Update UI112 densityButtons.forEach(b => {113 b.classList.remove('bg-primary', 'text-white');114 b.classList.add('bg-gray-100', 'dark:bg-gray-800', 'hover:bg-gray-200', 'dark:hover:bg-gray-700');115 });116 this.classList.add('bg-primary', 'text-white');117 this.classList.remove('hover:bg-gray-200', 'dark:hover:bg-gray-700');118 });119 });120 }121 122 // Algorithm Mode Selection123 if (modeRadios) {124 modeRadios.forEach(radio => {125 radio.addEventListener('change', function() {126 selectMode(this.value);127 });128 });129 }130 131 // Start Sampling Button132 if (startSamplingBtn) {133 startSamplingBtn.addEventListener('click', function() {134 startSampling();135 });136 }137 138 // Clear Rays Button139 if (clearRaysBtn) {140 clearRaysBtn.addEventListener('click', function() {141 clearRays();142 });143 }144 145 // Toggle Grid Button146 if (toggleGridBtn) {147 toggleGridBtn.addEventListener('click', function() {148 toggleGridVisibility();149 });150 }151 152 // Reset View Button153 if (resetViewBtn) {154 resetViewBtn.addEventListener('click', function() {155 resetCameraView();156 });157 }158 159 // Zoom Controls160 if (zoomInBtn) {161 zoomInBtn.addEventListener('click', function() {162 zoomCamera('in');163 });164 }165 166 if (zoomOutBtn) {167 zoomOutBtn.addEventListener('click', function() {168 zoomCamera('out');169 });170 }171 172 // Visualize Rays Checkbox173 if (visualizeRaysCheckbox) {174 visualizeRaysCheckbox.addEventListener('change', function() {175 toggleRayVisualization(this.checked);176 });177 }178 179 // Update sample chart based on model180 updateSampleChart(currentModel);181 182 // Hide loading overlay after 2 seconds (simulate loading)183 setTimeout(() => {184 if (loadingOverlay) {185 loadingOverlay.style.opacity = '0';186 setTimeout(() => {187 loadingOverlay.style.display = 'none';188 }, 300);189 }190 }, 1500);191 192 // Update coordinates display (simulate 3D interaction)193 if (coordX && coordY && coordZ) {194 simulateCameraMovement(coordX, coordY, coordZ);195 }196 197 // Event Listeners for Ray Visualization198 document.addEventListener('rayCreated', function(e) {199 if (e.detail && e.detail.count) {200 updateRayStats(e.detail.count);201 }202 });203});204 205// Model Selection Function206function selectModel(model) {207 currentModel = model;208 console.log(`Selected model: ${model}`);209 210 // Update UI211 const modelIcon = document.querySelector('#modelSelectBtn i');212 if (modelIcon) {213 modelIcon.setAttribute('data-feather', getModelIcon(model));214 feather.replace();215 }216 217 // Update sample chart218 updateSampleChart(model);219 220 // Update Three.js scene221 if (window.sceneManager) {222 window.sceneManager.changeModel(model);223 }224 225 // Dispatch event226 const event = new CustomEvent('modelChanged', { detail: { model: model } });227 document.dispatchEvent(event);228}229 230// Get icon name based on model231function getModelIcon(model) {232 const icons = {233 sphere: 'circle',234 cylinder: 'square',235 organic: 'hexagon',236 torus: 'triangle'237 };238 return icons[model] || 'circle';239}240 241// Density Selection Function242function selectDensity(density) {243 console.log(`Selected density: ${density}`);244 245 // Update UI246 const densityValueSpan = document.getElementById('densityValue');247 if (densityValueSpan) {248 densityValueSpan.textContent = density.charAt(0).toUpperCase() + density.slice(1);249 }250 251 // Dispatch event252 const event = new CustomEvent('densityChanged', { detail: { density: density } });253 document.dispatchEvent(event);254}255 256// Mode Selection Function257function selectMode(mode) {258 console.log(`Selected mode: ${mode}`);259 260 // Dispatch event261 const event = new CustomEvent('modeChanged', { detail: { mode: mode } });262 document.dispatchEvent(event);263}264 265// Start Sampling Function266function startSampling() {267 console.log('Starting outside-in ray sampling...');268 269 // Show visual feedback270 if (startSamplingBtn) {271 startSamplingBtn.innerHTML = '<i data-feather="loader" class="w-4 h-4 mr-2 animate-spin"></i>Sampling...';272 feather.replace();273 274 setTimeout(() => {275 startSamplingBtn.innerHTML = '<i data-feather="check" class="w-4 h-4 mr-2"></i>Sampling Complete';276 feather.replace();277 }, 2000);278 }279 280 // Dispatch event281 const event = new CustomEvent('samplingStarted');282 document.dispatchEvent(event);283}284 285// Clear Rays Function286function clearRays() {287 console.log('Clearing all ray visualizations...');288 289 // Dispatch event290 const event = new CustomEvent('raysCleared');291 document.dispatchEvent(event);292}293 294// Grid Visibility Toggle295function toggleGridVisibility() {296 console.log('Toggling grid visibility...');297 298 // Dispatch event299 const event = new CustomEvent('gridToggled');300 document.dispatchEvent(event);301}302 303// Camera Reset Function304function resetCameraView() {305 console.log('Resetting camera view...');306 307 // Dispatch event308 const event = new CustomEvent('cameraReset');309 document.dispatchEvent(event);310}311 312// Camera Zoom Function313function zoomCamera(direction) {314 console.log(`Zooming camera ${direction}...`);315 316 // Dispatch event317 const event = new CustomEvent('cameraZoomed', { detail: { direction: direction } });318 document.dispatchEvent(event);319}320 321// Ray Visualization Toggle322function toggleRayVisualization(visible) {323 console.log(`Ray visualization ${visible ? 'enabled' : 'disabled'}`);324 325 // Dispatch event326 const event = new CustomEvent('rayVisualizationToggled', { detail: { visible: visible } });327 document.dispatchEvent(event);328}329 330// Update Sample Chart Function331function updateSampleChart(model) {332 if (window.updateSampleChart) {333 window.updateSampleChart(model);334 }335}336 337// Simulate Camera Movement for Coordinates Display338function simulateCameraMovement(coordX, coordY, coordZ) {339 let x = 0, y = 0, z = 0;340 let increment = 0.01;341 342 function updateCoordinates() {343 x += (Math.random() - 0.5) * increment;344 y += (Math.random() - 0.5) * increment;345 z += (Math.random() - 0.5) * increment;346 347 // Apply some damping348 x *= 0.99;349 y *= 0.99;350 z *= 0.99;351 352 if (coordX) coordX.textContent = x.toFixed(2);353 if (coordY) coordY.textContent = y.toFixed(2);354 if (coordZ) coordZ.textContent = z.toFixed(2);355 356 requestAnimationFrame(updateCoordinates);357 }358 359 updateCoordinates();360}361 362// Update Ray Statistics363function updateRayStats(count) {364 const totalSamples = document.getElementById('totalSamples');365 const rayCollisions = document.getElementById('rayCollisions');366 const shellAccuracy = document.getElementById('shellAccuracy');367 const avgDistance = document.getElementById('avgDistance');368 369 if (totalSamples) {370 totalSamples.textContent = count * 4;371 }372 373 if (rayCollisions) {374 const collisions = Math.floor(count * 3.8);375 rayCollisions.textContent = collisions;376 }377 378 if (shellAccuracy) {379 const accuracy = 90 + Math.floor(Math.random() * 10);380 shellAccuracy.textContent = `${accuracy}.${Math.floor(Math.random() * 10)}%`;381 }382 383 if (avgDistance) {384 const distance = 0.8 + Math.random() * 0.4;385 avgDistance.textContent = `${distance.toFixed(2)}m`;386 }387}388 389// Add event listeners for custom events390document.addEventListener('modelChanged', function(e) {391 console.log(`Model changed to: ${e.detail.model}`);392});393 394document.addEventListener('densityChanged', function(e) {395 console.log(`Density changed to: ${e.detail.density}`);396});397 398document.addEventListener('modeChanged', function(e) {399 console.log(`Mode changed to: ${e.detail.mode}`);400});401 402document.addEventListener('samplingStarted', function() {403 console.log('Sampling process started.');404});405 406document.addEventListener('raysCleared', function() {407 console.log('All rays cleared.');408});409 410document.addEventListener('gridToggled', function() {411 console.log('Grid toggled.');412});413 414document.addEventListener('cameraReset', function() {415 console.log('Camera view reset.');416});417 418document.addEventListener('cameraZoomed', function(e) {419 console.log(`Camera zoomed ${e.detail.direction}`);420});421 422document.addEventListener('rayVisualizationToggled', function(e) {423 console.log(`Ray visualization toggled: ${e.detail.visible}`);424});425 426// Export Functions for Use in Other Scripts427window.updateSampleChart = function(model) {428 console.log(`Sample chart updated for model: ${model}`);429 430 // Update UI elements431 const sampleChartBars = document.querySelectorAll('#sampleChart div div');432 if (sampleChartBars.length >= 5) {433 const data = sampleData[model] || sampleData.sphere;434 for (let i = 0; i < data.length; i++) {435 const height = data[i];436 if (sampleChartBars[i]) {437 sampleChartBars[i].style.height = `${height * 0.2}px`;438 }439 }440 }441};