mirror of
https://github.com/its-michaelroy/Deep-Impact.git
synced 2026-06-04 02:20:41 +00:00
added forecast line and an angle slider to control the impact point with earth.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { Stage, Layer, Circle, Text, Line } from 'react-konva';
|
import { Stage, Layer, Circle, Text, Line } from 'react-konva';
|
||||||
import { Slider } from '@mui/material';
|
import { Slider, Button } from '@mui/material';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
const API_KEY = 'LF7i77oqghRiq54HEFJh991WgjHcKsETP9D5ofsg';
|
const API_KEY = 'LF7i77oqghRiq54HEFJh991WgjHcKsETP9D5ofsg';
|
||||||
@@ -23,11 +23,12 @@ function Scenario() {
|
|||||||
size: 1,
|
size: 1,
|
||||||
speed: 0.05,
|
speed: 0.05,
|
||||||
mass: 1,
|
mass: 1,
|
||||||
angle: Math.PI / 4,
|
angle: Math.PI / 9,
|
||||||
x: 100,
|
x: 100,
|
||||||
y: CANVAS_HEIGHT / 2 - 100,
|
y: CANVAS_HEIGHT / 2 - 100,
|
||||||
});
|
});
|
||||||
const [trajectoryPoints, setTrajectoryPoints] = useState([]);
|
const [trajectoryPoints, setTrajectoryPoints] = useState([]);
|
||||||
|
const [forecastPoints, setForecastPoints] = useState([]);
|
||||||
const animationRef = useRef();
|
const animationRef = useRef();
|
||||||
const asteroidsData = useRef([]);
|
const asteroidsData = useRef([]);
|
||||||
const [isSimulating, setIsSimulating] = useState(false);
|
const [isSimulating, setIsSimulating] = useState(false);
|
||||||
@@ -59,16 +60,18 @@ function Scenario() {
|
|||||||
console.log('Asteroid Data:', asteroidData);
|
console.log('Asteroid Data:', asteroidData);
|
||||||
const size = asteroidData.estimated_diameter.kilometers.estimated_diameter_max;
|
const size = asteroidData.estimated_diameter.kilometers.estimated_diameter_max;
|
||||||
const mass = estimateMass(size);
|
const mass = estimateMass(size);
|
||||||
|
const speed = asteroidData.close_approach_data[0].relative_velocity.kilometers_per_second / 1000;
|
||||||
setAsteroid({
|
setAsteroid({
|
||||||
name: asteroidData.name,
|
name: asteroidData.name,
|
||||||
size: size,
|
size: size,
|
||||||
speed: asteroidData.close_approach_data[0].relative_velocity.kilometers_per_second / 1000,
|
speed: speed,
|
||||||
mass: mass,
|
mass: mass,
|
||||||
angle: Math.PI / 8,
|
angle: Math.PI / 9,
|
||||||
x: 100,
|
x: 100,
|
||||||
y: CANVAS_HEIGHT / 2 - 100,
|
y: CANVAS_HEIGHT / 2 - 100,
|
||||||
});
|
});
|
||||||
setTrajectoryPoints([]);
|
setTrajectoryPoints([]);
|
||||||
|
calculateForecastPath(size, speed, Math.PI / 9, mass); // Calculate the forecast path once
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAsteroidSelect = (event) => {
|
const handleAsteroidSelect = (event) => {
|
||||||
@@ -86,6 +89,52 @@ function Scenario() {
|
|||||||
return { vx, vy };
|
return { vx, vy };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const calculateForecastPath = (size, speed, angle, mass) => {
|
||||||
|
let x = 100;
|
||||||
|
let y = CANVAS_HEIGHT / 2 - 100;
|
||||||
|
let vx = speed * Math.cos(angle);
|
||||||
|
let vy = speed * Math.sin(angle);
|
||||||
|
const points = [];
|
||||||
|
let iterationCount = 0;
|
||||||
|
const maxIterations = 2000; // Extended limit to allow for longer forecast
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const dx = EARTH_X - x;
|
||||||
|
const dy = EARTH_Y - y;
|
||||||
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
// Check if the asteroid hits Earth
|
||||||
|
if (distance <= EARTH_RADIUS_KM * EARTH_DISPLAY_SCALE + size * ASTEROID_DISPLAY_SCALE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply gravitational force
|
||||||
|
const force = (G * EARTH_MASS * mass) / (distance * distance);
|
||||||
|
const ax = force * (dx / distance) / mass;
|
||||||
|
const ay = force * (dy / distance) / mass;
|
||||||
|
|
||||||
|
vx += ax * timeStep;
|
||||||
|
vy += ay * timeStep;
|
||||||
|
|
||||||
|
x += vx * timeStep;
|
||||||
|
y += vy * timeStep;
|
||||||
|
|
||||||
|
points.push(x, y);
|
||||||
|
|
||||||
|
iterationCount++;
|
||||||
|
if (iterationCount >= maxIterations) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the loop if the asteroid goes off screen
|
||||||
|
if (y > CANVAS_HEIGHT * SIMULATION_AREA_MULTIPLIER || x > CANVAS_WIDTH * SIMULATION_AREA_MULTIPLIER || y < -CANVAS_HEIGHT * SIMULATION_AREA_MULTIPLIER || x < -CANVAS_WIDTH * SIMULATION_AREA_MULTIPLIER) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setForecastPoints(points);
|
||||||
|
};
|
||||||
|
|
||||||
const simulate = () => {
|
const simulate = () => {
|
||||||
let { x, y, speed, angle, mass } = asteroid;
|
let { x, y, speed, angle, mass } = asteroid;
|
||||||
|
|
||||||
@@ -143,6 +192,11 @@ function Scenario() {
|
|||||||
setTimeStep(newValue);
|
setTimeStep(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAngleChange = (event, newValue) => {
|
||||||
|
setAsteroid(prev => ({ ...prev, angle: newValue }));
|
||||||
|
calculateForecastPath(asteroid.size, asteroid.speed, newValue, asteroid.mass);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
@@ -158,7 +212,15 @@ function Scenario() {
|
|||||||
<option value="Kinetic Impact">Kinetic Impact</option>
|
<option value="Kinetic Impact">Kinetic Impact</option>
|
||||||
<option value="Gravity Tractor">Gravity Tractor</option>
|
<option value="Gravity Tractor">Gravity Tractor</option>
|
||||||
</select>
|
</select>
|
||||||
<button onClick={handleSimulate} disabled={isSimulating}>Simulate</button>
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleSimulate}
|
||||||
|
disabled={isSimulating}
|
||||||
|
style={{ marginTop: '10px' }}
|
||||||
|
>
|
||||||
|
Simulate
|
||||||
|
</Button>
|
||||||
{simulationResult && (
|
{simulationResult && (
|
||||||
<div>
|
<div>
|
||||||
<h2>Simulation Result</h2>
|
<h2>Simulation Result</h2>
|
||||||
@@ -185,6 +247,17 @@ function Scenario() {
|
|||||||
aria-labelledby="time-step-slider"
|
aria-labelledby="time-step-slider"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3>Initial Angle: {(asteroid.angle * 180 / Math.PI).toFixed(2)}°</h3>
|
||||||
|
<Slider
|
||||||
|
value={asteroid.angle}
|
||||||
|
min={0}
|
||||||
|
max={Math.PI / 2}
|
||||||
|
step={Math.PI / 180}
|
||||||
|
onChange={handleAngleChange}
|
||||||
|
aria-labelledby="angle-slider"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Stage width={CANVAS_WIDTH} height={CANVAS_HEIGHT}>
|
<Stage width={CANVAS_WIDTH} height={CANVAS_HEIGHT}>
|
||||||
<Layer>
|
<Layer>
|
||||||
@@ -194,6 +267,9 @@ function Scenario() {
|
|||||||
{trajectoryPoints.length > 0 && (
|
{trajectoryPoints.length > 0 && (
|
||||||
<Line points={trajectoryPoints.flat()} stroke="red" strokeWidth={2} />
|
<Line points={trajectoryPoints.flat()} stroke="red" strokeWidth={2} />
|
||||||
)}
|
)}
|
||||||
|
{forecastPoints.length > 0 && (
|
||||||
|
<Line points={forecastPoints} stroke="gray" strokeWidth={2} dash={[10, 10]} />
|
||||||
|
)}
|
||||||
</Layer>
|
</Layer>
|
||||||
</Stage>
|
</Stage>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user