Spaces:
Runtime error
Runtime error
// 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); | |
} |