From 21879946165a53faedca0c82dd801e806b34638c Mon Sep 17 00:00:00 2001 From: Thomas Faour Date: Fri, 20 Jun 2025 00:24:03 -0400 Subject: [PATCH] added second binary - starting opengl --- Cargo.toml | 1 + README.md | 33 ++++++--- src/bin/orbiter.rs | 103 ++++++++++++++++++++++++++++ src/{main.rs => bin/simulator.rs} | 17 ++--- src/lib.rs | 3 + src/{simulator.rs => simulation.rs} | 0 6 files changed, 135 insertions(+), 22 deletions(-) create mode 100644 src/bin/orbiter.rs rename src/{main.rs => bin/simulator.rs} (91%) create mode 100644 src/lib.rs rename src/{simulator.rs => simulation.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index 6e2bdc5..a413f9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,4 @@ log = "0.4.27" once_cell = "1.21.3" serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" +bevy = "0.13" diff --git a/README.md b/README.md index c1b36ec..4221d87 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Simulate the motion of celestial bodies under Newtonian gravity, with easy confi ### Prerequisites - [Rust](https://www.rust-lang.org/tools/install) (edition 2021 or later) -- Python (optional, for animation/visualization) +- [Bevy dependencies](https://bevyengine.org/learn/book/getting-started/setup/) (for 3D visualization; see Bevy's docs for Linux requirements) ### Build @@ -29,10 +29,12 @@ Simulate the motion of celestial bodies under Newtonian gravity, with easy confi cargo build --release ``` -### Run +--- + +## Running the Simulator (CLI) ```bash -cargo run --release -- \ +cargo run --release --bin simulator -- \ --config path/to/your_config.json \ --time 30d \ --step-size 10.0 \ @@ -40,7 +42,6 @@ cargo run --release -- \ ``` **Arguments:** - - `--config` (required): Path to your JSON config file with initial body states. - `--time` (required): Total simulation time (e.g. `10s`, `5m`, `2h`, `100d`). - `--step-size`: Simulation step size in seconds (default: `10.0`). @@ -49,10 +50,25 @@ cargo run --release -- \ --- +## Running the 3D Visualizer (`orbiter`) + +```bash +cargo run --release --bin orbiter +``` + +- Opens a 3D window with a camera and a blue sphere (placeholder for future simulation data). +- **Camera controls:** + - **Right mouse drag:** Orbit around the origin + - **Scroll wheel:** Zoom in/out + +Future updates will allow loading and animating simulation output. + +--- + ## Configuration The config file is a JSON file describing the initial state of each body. -Examples provided in config/ +Examples provided in `config/` ```json { @@ -82,12 +98,6 @@ Examples provided in config/ --- -## Animation - -Coming soon. - ---- - ## Extending - Add more bodies or change initial conditions in the config file. @@ -105,6 +115,7 @@ MIT License ## Acknowledgments - [Rust](https://www.rust-lang.org/) +- [Bevy](https://bevyengine.org/) for 3D visualization - [glam](https://crates.io/crates/glam) for fast vector math - [clap](https://crates.io/crates/clap) for CLI parsing - [indicatif](https://crates.io/crates/indicatif) for progress bars diff --git a/src/bin/orbiter.rs b/src/bin/orbiter.rs new file mode 100644 index 0000000..c59294c --- /dev/null +++ b/src/bin/orbiter.rs @@ -0,0 +1,103 @@ +use bevy::prelude::*; +use bevy::input::mouse::{MouseMotion, MouseWheel}; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_systems(Startup, setup) + .add_systems(Update, (camera_orbit_controls,)) + .run(); +} + +#[derive(Component)] +struct CameraController { + pub radius: f32, + pub theta: f32, // azimuthal angle + pub phi: f32, // polar angle + pub last_mouse_pos: Option, +} + +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // Camera with controller + let radius = 30.0; + let theta = 0.0; + let phi = std::f32::consts::FRAC_PI_4; + let cam_pos = spherical_to_cartesian(radius, theta, phi); + commands.spawn(( + Camera3dBundle { + transform: Transform::from_translation(cam_pos).looking_at(Vec3::ZERO, Vec3::Y), + ..default() + }, + CameraController { + radius, + theta, + phi, + last_mouse_pos: None, + }, + )); + // Light + commands.spawn(PointLightBundle { + point_light: PointLight { + intensity: 1500.0, + shadows_enabled: true, + ..default() + }, + transform: Transform::from_xyz(4.0, 8.0, 4.0), + ..default() + }); + // Add a sphere at the origin using the new Sphere primitive + let mesh = meshes.add(Mesh::from(Sphere { radius: 1.0, ..default() })); + let material = materials.add(StandardMaterial { + base_color: Color::BLUE, + ..default() + }); + commands.spawn(PbrBundle { + mesh, + material, + ..default() + }); +} + +fn spherical_to_cartesian(radius: f32, theta: f32, phi: f32) -> Vec3 { + let x = radius * phi.sin() * theta.cos(); + let y = radius * phi.cos(); + let z = radius * phi.sin() * theta.sin(); + Vec3::new(x, y, z) +} + +fn camera_orbit_controls( + mut query: Query<(&mut Transform, &mut CameraController)>, + mut mouse_motion_events: EventReader, + mut mouse_wheel_events: EventReader, + mouse_button_input: Res>, + windows: Query<&Window>, +) { + let window = if let Some(window) = windows.iter().next() { window } else { return; }; + let mut delta = Vec2::ZERO; + for event in mouse_motion_events.read() { + delta += event.delta; + } + let mut scroll = 0.0; + for event in mouse_wheel_events.read() { + scroll += event.y; + } + for (mut transform, mut controller) in query.iter_mut() { + // Orbit (right mouse button) + if mouse_button_input.pressed(MouseButton::Right) { + let sensitivity = 0.01; + controller.theta -= delta.x * sensitivity; + controller.phi = (controller.phi - delta.y * sensitivity).clamp(0.05, std::f32::consts::PI - 0.05); + } + // Zoom + if scroll.abs() > 0.0 { + controller.radius = (controller.radius - scroll).clamp(3.0, 200.0); + } + // Update camera position + let pos = spherical_to_cartesian(controller.radius, controller.theta, controller.phi); + *transform = Transform::from_translation(pos).looking_at(Vec3::ZERO, Vec3::Y); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/bin/simulator.rs similarity index 91% rename from src/main.rs rename to src/bin/simulator.rs index ebf6bd1..2d47561 100644 --- a/src/main.rs +++ b/src/bin/simulator.rs @@ -11,15 +11,10 @@ use log::{info, debug}; use indicatif::{ProgressBar, ProgressStyle}; use humantime; -// Internal modules -mod types; -mod config; -mod simulator; - -// Specific uses from modules -use crate::simulator::Simulation; -use crate::types::{norm_time, real_body}; - +// Internal modules from the library crate +use orbital_simulator::simulation::Simulation; +use orbital_simulator::types::{norm_time, real_body}; +use orbital_simulator as _; // for mod resolution #[derive(Parser, Debug)] #[command( @@ -50,7 +45,7 @@ struct Args { output_file: String, } -fn read_config>(path: P) -> Result> { +fn read_config>(path: P) -> Result> { let file = File::open(path)?; let reader = BufReader::new(file); @@ -119,4 +114,4 @@ fn main() { pb.inc(args.steps_per_save as u64); }) ); -} +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ce63583 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +pub mod types; +pub mod config; +pub mod simulation; \ No newline at end of file diff --git a/src/simulator.rs b/src/simulation.rs similarity index 100% rename from src/simulator.rs rename to src/simulation.rs