gravity-dev / static /script.js
broadfield-dev's picture
Update static/script.js
49003da verified
// static/script.js
let scene, camera, renderer, controls;
let spheres = [];
let fluidParticles = [];
let simulationRunning = false;
const PARTICLE_COUNT = 5000;
const SPACE_SIZE = 40;
const FLUID_SPEED = 0.1;
let FLUID_FRICTION = 0.9;
let FLUID_DEFLECTION = 0.1;
const GRAVITY_CONSTANT = 0.1;
// Scaling factors
const MASS_SCALE = 1e-26; // Scale down masses for simulation
const DISTANCE_SCALE = 1e-7; // Scale down distances (km to simulation units)
const VELOCITY_SCALE = 1e-3; // Scale down velocities (km/s to simulation units)
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, 20, 40);
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 Sun
const sunGeometry = new THREE.SphereGeometry(1, 32, 32);
const sunMaterial = new THREE.MeshBasicMaterial({ color: 0xFFFF00 });
const sun = new THREE.Mesh(sunGeometry, sunMaterial);
sun.position.set(0, 0, 0);
sun.userData = { mass: 1.989e30 * MASS_SCALE, velocity: new THREE.Vector3(0, 0, 0) };
scene.add(sun);
spheres.push(sun);
// Add Earth
const earthGeometry = new THREE.SphereGeometry(0.3, 32, 32);
const earthMaterial = new THREE.MeshBasicMaterial({ color: 0x0000FF });
const earth = new THREE.Mesh(earthGeometry, earthMaterial);
earth.position.set(149.6e6 * DISTANCE_SCALE, 0, 0);
earth.userData = {
mass: 5.972e24 * MASS_SCALE,
velocity: new THREE.Vector3(0, 0, 29.8 * VELOCITY_SCALE),
centripetalScale: 1
};
scene.add(earth);
spheres.push(earth);
// Add Mars
const marsGeometry = new THREE.SphereGeometry(0.25, 32, 32);
const marsMaterial = new THREE.MeshBasicMaterial({ color: 0xFF4500 });
const mars = new THREE.Mesh(marsGeometry, marsMaterial);
mars.position.set(227.9e6 * DISTANCE_SCALE, 0, 0);
mars.userData = {
mass: 6.417e23 * MASS_SCALE,
velocity: new THREE.Vector3(0, 0, 24.1 * VELOCITY_SCALE),
centripetalScale: 1
};
scene.add(mars);
spheres.push(mars);
// 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', startSimulation);
document.getElementById('stop-btn').addEventListener('click', stopSimulation);
document.getElementById('reset-btn').addEventListener('click', resetSimulation);
document.getElementById('save-btn').addEventListener('click', saveSettings);
document.getElementById('load-btn').addEventListener('click', loadSettings);
// Update parameters when sliders change
['sun', 'earth', 'mars'].forEach(body => {
document.getElementById(`${body}-mass`).addEventListener('input', updateParams);
document.getElementById(`${body}-x`).addEventListener('input', updateParams);
document.getElementById(`${body}-y`).addEventListener('input', updateParams);
document.getElementById(`${body}-z`).addEventListener('input', updateParams);
if (body !== 'sun') {
document.getElementById(`${body}-orbital-velocity`).addEventListener('input', updateParams);
document.getElementById(`${body}-centripetal`).addEventListener('input', updateParams);
}
});
document.getElementById('fluid-friction').addEventListener('input', updateParams);
document.getElementById('fluid-deflection').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);
});
// Initial update to display scaled values
updateParams();
}
function updateParams() {
// Update Sun
const sunMass = parseFloat(document.getElementById('sun-mass').value);
spheres[0].userData.mass = sunMass * MASS_SCALE;
document.getElementById('sun-mass-scaled').textContent = (sunMass * MASS_SCALE).toExponential(2);
spheres[0].position.set(
parseFloat(document.getElementById('sun-x').value) * DISTANCE_SCALE,
parseFloat(document.getElementById('sun-y').value) * DISTANCE_SCALE,
parseFloat(document.getElementById('sun-z').value) * DISTANCE_SCALE
);
// Update Earth
const earthMass = parseFloat(document.getElementById('earth-mass').value);
spheres[1].userData.mass = earthMass * MASS_SCALE;
document.getElementById('earth-mass-scaled').textContent = (earthMass * MASS_SCALE).toExponential(2);
spheres[1].position.set(
parseFloat(document.getElementById('earth-x').value) * DISTANCE_SCALE,
parseFloat(document.getElementById('earth-y').value) * DISTANCE_SCALE,
parseFloat(document.getElementById('earth-z').value) * DISTANCE_SCALE
);
const earthVelocity = parseFloat(document.getElementById('earth-orbital-velocity').value);
spheres[1].userData.velocity.set(0, 0, earthVelocity * VELOCITY_SCALE);
document.getElementById('earth-velocity-scaled').textContent = (earthVelocity * VELOCITY_SCALE).toFixed(4);
spheres[1].userData.centripetalScale = parseFloat(document.getElementById('earth-centripetal').value);
// Update Mars
const marsMass = parseFloat(document.getElementById('mars-mass').value);
spheres[2].userData.mass = marsMass * MASS_SCALE;
document.getElementById('mars-mass-scaled').textContent = (marsMass * MASS_SCALE).toExponential(2);
spheres[2].position.set(
parseFloat(document.getElementById('mars-x').value) * DISTANCE_SCALE,
parseFloat(document.getElementById('mars-y').value) * DISTANCE_SCALE,
parseFloat(document.getElementById('mars-z').value) * DISTANCE_SCALE
);
const marsVelocity = parseFloat(document.getElementById('mars-orbital-velocity').value);
spheres[2].userData.velocity.set(0, 0, marsVelocity * VELOCITY_SCALE);
document.getElementById('mars-velocity-scaled').textContent = (marsVelocity * VELOCITY_SCALE).toFixed(4);
spheres[2].userData.centripetalScale = parseFloat(document.getElementById('mars-centripetal').value);
// Update fluid interaction parameters
FLUID_FRICTION = parseFloat(document.getElementById('fluid-friction').value);
FLUID_DEFLECTION = parseFloat(document.getElementById('fluid-deflection').value);
}
function startSimulation() {
// Update parameters to ensure the simulation uses the latest values
updateParams();
simulationRunning = true;
document.getElementById('status-message').textContent = 'Simulation started';
document.getElementById('status-message').style.color = '#4CAF50';
}
function stopSimulation() {
simulationRunning = false;
document.getElementById('status-message').textContent = 'Simulation stopped';
document.getElementById('status-message').style.color = '#F44336';
}
function resetSimulation() {
// Stop the simulation
simulationRunning = false;
// Reset fluid particles
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
);
});
// Reset sphere positions and velocities using current control values
updateParams();
document.getElementById('status-message').textContent = 'Simulation reset';
document.getElementById('status-message').style.color = '#2196F3';
}
function saveSettings() {
const settings = {
sun: {
mass: parseFloat(document.getElementById('sun-mass').value),
position: [
parseFloat(document.getElementById('sun-x').value),
parseFloat(document.getElementById('sun-y').value),
parseFloat(document.getElementById('sun-z').value)
],
orbital_velocity: 0
},
earth: {
mass: parseFloat(document.getElementById('earth-mass').value),
position: [
parseFloat(document.getElementById('earth-x').value),
parseFloat(document.getElementById('earth-y').value),
parseFloat(document.getElementById('earth-z').value)
],
orbital_velocity: parseFloat(document.getElementById('earth-orbital-velocity').value)
},
mars: {
mass: parseFloat(document.getElementById('mars-mass').value),
position: [
parseFloat(document.getElementById('mars-x').value),
parseFloat(document.getElementById('mars-y').value),
parseFloat(document.getElementById('mars-z').value)
],
orbital_velocity: parseFloat(document.getElementById('mars-orbital-velocity').value)
},
fluid_speed: FLUID_SPEED,
fluid_friction: FLUID_FRICTION,
fluid_deflection: FLUID_DEFLECTION
};
fetch('/api/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(settings)
})
.then(response => response.json())
.then(data => {
document.getElementById('status-message').textContent = data.message || data.status;
document.getElementById('status-message').style.color = data.status === 'success' ? '#4CAF50' : '#FF0000';
})
.catch(error => {
document.getElementById('status-message').textContent = 'Error saving settings';
document.getElementById('status-message').style.color = '#FF0000';
});
}
function loadSettings() {
fetch('/api/load')
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
const params = data.params;
document.getElementById('sun-mass').value = params.sun.mass;
document.getElementById('sun-x').value = params.sun.position[0];
document.getElementById('sun-y').value = params.sun.position[1];
document.getElementById('sun-z').value = params.sun.position[2];
document.getElementById('earth-mass').value = params.earth.mass;
document.getElementById('earth-x').value = params.earth.position[0];
document.getElementById('earth-y').value = params.earth.position[1];
document.getElementById('earth-z').value = params.earth.position[2];
document.getElementById('earth-orbital-velocity').value = params.earth.orbital_velocity;
document.getElementById('mars-mass').value = params.mars.mass;
document.getElementById('mars-x').value = params.mars.position[0];
document.getElementById('mars-y').value = params.mars.position[1];
document.getElementById('mars-z').value = params.mars.position[2];
document.getElementById('mars-orbital-velocity').value = params.mars.orbital_velocity;
document.getElementById('fluid-friction').value = params.fluid_friction;
document.getElementById('fluid-deflection').value = params.fluid_deflection;
updateParams();
document.getElementById('status-message').textContent = 'Settings loaded successfully';
document.getElementById('status-message').style.color = '#4CAF50';
} else {
document.getElementById('status-message').textContent = data.message;
document.getElementById('status-message').style.color = '#FF0000';
}
})
.catch(error => {
document.getElementById('status-message').textContent = 'Error loading settings';
document.getElementById('status-message').style.color = '#FF0000';
});
}
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;
if (distance < sphereRadius) {
// Apply friction
velocity.multiplyScalar(FLUID_FRICTION);
// Apply gravitational deflection
let direction = sphere.position.clone().sub(position).normalize();
let forceMagnitude = (FLUID_DEFLECTION * 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 and orbital dynamics)
spheres.forEach((sphere, i) => {
if (i === 0) return; // Sun is stationary
let acceleration = new THREE.Vector3();
spheres.forEach((otherSphere, j) => {
if (i !== j) {
let distance = sphere.position.distanceTo(otherSphere.position);
if (distance > 0.1) {
let direction = otherSphere.position.clone().sub(sphere.position).normalize();
let force = (GRAVITY_CONSTANT * otherSphere.userData.mass) / (distance * distance);
acceleration.add(direction.multiplyScalar(force * sphere.userData.centripetalScale));
}
}
});
// Update velocity and position
sphere.userData.velocity.add(acceleration);
sphere.position.add(sphere.userData.velocity);
});
}
controls.update();
renderer.render(scene, camera);
}