// static/script.js let scene, camera, renderer, controls; let spheres = []; let fluidParticles = []; let simulationRunning = false; const PARTICLE_COUNT = 5000; const SPACE_SIZE = 20; const FLUID_SPEED = 0.1; const FRICTION_FACTOR = 0.9; const GRAVITY_CONSTANT = 0.1; init(); animate(); function init() { // Scene setup scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 10, 20); renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth - 300, window.innerHeight); document.getElementById('scene-container').appendChild(renderer.domElement); controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.05; // Add spheres const brownGeometry = new THREE.SphereGeometry(0.5, 32, 32); const brownMaterial = new THREE.MeshBasicMaterial({ color: 0x8B4513 }); const brownSphere = new THREE.Mesh(brownGeometry, brownMaterial); brownSphere.position.set(0, 0, 0); brownSphere.userData = { mass: 92 }; scene.add(brownSphere); spheres.push(brownSphere); const greenGeometry = new THREE.SphereGeometry(0.4, 32, 32); const greenMaterial = new THREE.MeshBasicMaterial({ color: 0x00FF00 }); const greenSphere = new THREE.Mesh(greenGeometry, greenMaterial); greenSphere.position.set(5, 0, 5); greenSphere.userData = { mass: 29 }; scene.add(greenSphere); spheres.push(greenSphere); const redGeometry = new THREE.SphereGeometry(0.3, 32, 32); const redMaterial = new THREE.MeshBasicMaterial({ color: 0xFF0000 }); const redSphere = new THREE.Mesh(redGeometry, redMaterial); redSphere.position.set(-5, 0, -5); redSphere.userData = { mass: 10 }; scene.add(redSphere); spheres.push(redSphere); // Add fluid particles const particleGeometry = new THREE.SphereGeometry(0.05, 8, 8); const particleMaterial = new THREE.MeshBasicMaterial({ color: 0x00BFFF, transparent: true, opacity: 0.5 }); for (let i = 0; i < PARTICLE_COUNT; i++) { const particle = new THREE.Mesh(particleGeometry, particleMaterial); particle.position.set( (Math.random() - 0.5) * SPACE_SIZE, (Math.random() - 0.5) * SPACE_SIZE, (Math.random() - 0.5) * SPACE_SIZE ); particle.userData = { velocity: new THREE.Vector3( (Math.random() - 0.5) * FLUID_SPEED, (Math.random() - 0.5) * FLUID_SPEED, (Math.random() - 0.5) * FLUID_SPEED ) }; scene.add(particle); fluidParticles.push(particle); } // Add grid helper for reference const gridHelper = new THREE.GridHelper(SPACE_SIZE, 20); gridHelper.position.y = -SPACE_SIZE / 2; scene.add(gridHelper); // Event listeners for controls document.getElementById('start-btn').addEventListener('click', () => { simulationRunning = true; updateParams(); }); document.getElementById('reset-btn').addEventListener('click', () => { simulationRunning = false; resetSimulation(); }); // Update sphere positions and masses from sliders ['brown', 'green', 'red'].forEach(color => { document.getElementById(`${color}-mass`).addEventListener('input', updateParams); document.getElementById(`${color}-x`).addEventListener('input', updateParams); document.getElementById(`${color}-y`).addEventListener('input', updateParams); document.getElementById(`${color}-z`).addEventListener('input', updateParams); }); // Handle window resize window.addEventListener('resize', () => { camera.aspect = (window.innerWidth - 300) / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth - 300, window.innerHeight); }); } function updateParams() { spheres[0].userData.mass = parseFloat(document.getElementById('brown-mass').value); spheres[0].position.set( parseFloat(document.getElementById('brown-x').value), parseFloat(document.getElementById('brown-y').value), parseFloat(document.getElementById('brown-z').value) ); spheres[1].userData.mass = parseFloat(document.getElementById('green-mass').value); spheres[1].position.set( parseFloat(document.getElementById('green-x').value), parseFloat(document.getElementById('green-y').value), parseFloat(document.getElementById('green-z').value) ); spheres[2].userData.mass = parseFloat(document.getElementById('red-mass').value); spheres[2].position.set( parseFloat(document.getElementById('red-x').value), parseFloat(document.getElementById('red-y').value), parseFloat(document.getElementById('red-z').value) ); } function resetSimulation() { fluidParticles.forEach(particle => { particle.position.set( (Math.random() - 0.5) * SPACE_SIZE, (Math.random() - 0.5) * SPACE_SIZE, (Math.random() - 0.5) * SPACE_SIZE ); particle.userData.velocity.set( (Math.random() - 0.5) * FLUID_SPEED, (Math.random() - 0.5) * FLUID_SPEED, (Math.random() - 0.5) * FLUID_SPEED ); }); } function animate() { requestAnimationFrame(animate); if (simulationRunning) { // Update fluid particles fluidParticles.forEach(particle => { let position = particle.position; let velocity = particle.userData.velocity; // Check for interactions with spheres spheres.forEach(sphere => { let distance = position.distanceTo(sphere.position); let sphereRadius = sphere.geometry.parameters.radius + 0.5; // Interaction radius if (distance < sphereRadius) { // Apply friction velocity.multiplyScalar(FRICTION_FACTOR); // Apply gravitational deflection let direction = sphere.position.clone().sub(position).normalize(); let forceMagnitude = (GRAVITY_CONSTANT * sphere.userData.mass) / (distance * distance); let force = direction.multiplyScalar(forceMagnitude); velocity.add(force); } }); // Update position position.add(velocity); // Boundary conditions (wrap around) if (Math.abs(position.x) > SPACE_SIZE / 2) position.x = -Math.sign(position.x) * SPACE_SIZE / 2; if (Math.abs(position.y) > SPACE_SIZE / 2) position.y = -Math.sign(position.y) * SPACE_SIZE / 2; if (Math.abs(position.z) > SPACE_SIZE / 2) position.z = -Math.sign(position.z) * SPACE_SIZE / 2; }); // Update sphere positions (gravitational interaction between spheres) spheres.forEach((sphere, i) => { let acceleration = new THREE.Vector3(); spheres.forEach((otherSphere, j) => { if (i !== j) { let distance = sphere.position.distanceTo(otherSphere.position); if (distance > 0.1) { // Avoid division by zero let direction = otherSphere.position.clone().sub(sphere.position).normalize(); let force = (GRAVITY_CONSTANT * otherSphere.userData.mass) / (distance * distance); acceleration.add(direction.multiplyScalar(force)); } } }); sphere.position.add(acceleration); }); } controls.update(); renderer.render(scene, camera); }