diff --git a/.github/contributing.md b/.github/contributing.md index 483535f..985a0a1 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -39,9 +39,11 @@ Before submitting a pull request: 2. **Follow Code Standards:** Ensure your code adheres to LP's coding standards. -3. **Provide Unit Tests:** Include relevant unit tests for new features or changes. +3. **Run Local Checks:** Before opening a pull request, run `cargo fmt`, `cargo clippy --workspace`, and `cargo test --workspace` so CI time stays focused on regressions instead of formatting or lint fixes. -4. **Document Changes:** Update documentation to reflect any modifications made. +4. **Provide Unit Tests:** Include relevant unit tests for new features or changes. + +5. **Document Changes:** Update documentation to reflect any modifications made. ## Communicating with Developers diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f243f63..dc11d60 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -6,18 +6,22 @@ on: pull_request: branches: [main] +permissions: + contents: read + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: CARGO_TERM_COLOR: always + DEBIAN_FRONTEND: noninteractive jobs: test: name: Test Suite runs-on: ubuntu-latest - timeout-minutes: 25 + timeout-minutes: 30 steps: - name: Free disk space run: sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /usr/local/share/boost @@ -28,13 +32,13 @@ jobs: - name: Cache dependencies uses: Swatinem/rust-cache@v2 with: - shared-key: test-suite + shared-key: workspace - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable - name: Install system dependencies - run: sudo apt-get update && sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev clang lld + run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev clang lld - name: Quick check run: cargo check --workspace @@ -51,7 +55,7 @@ jobs: clippy: name: Clippy runs-on: ubuntu-latest - timeout-minutes: 25 + timeout-minutes: 30 steps: - name: Free disk space run: sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /usr/local/share/boost @@ -62,7 +66,7 @@ jobs: - name: Cache dependencies uses: Swatinem/rust-cache@v2 with: - shared-key: clippy-lint + shared-key: workspace - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable @@ -70,7 +74,7 @@ jobs: components: clippy - name: Install system dependencies - run: sudo apt-get update && sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev clang lld + run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev clang lld - name: Run clippy run: cargo clippy --workspace -- -A clippy::upper-case-acronyms -A clippy::manual-flatten -A clippy::excessive-precision -A clippy::too-many-arguments @@ -83,15 +87,10 @@ jobs: - name: Checkout sources uses: actions/checkout@v4 - - name: Cache dependencies - uses: Swatinem/rust-cache@v2 - with: - shared-key: format-check - - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: components: rustfmt - name: Check formatting - run: cargo fmt --all -- --check \ No newline at end of file + run: cargo fmt --all -- --check diff --git a/Cargo.toml b/Cargo.toml index 5c3899b..d60e5a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ forces = { path = "crates/forces" } information = { path = "crates/information" } matter = { path = "crates/matter" } -bevy = { version = "0.16", features = ["dynamic_linking"] } +bevy = { version = "0.17", features = ["dynamic_linking"] } glam = "0.29.2" @@ -41,4 +41,4 @@ serde = "1.0" serde_json = "1.0" # rand for picking random values -rand = "0.9" \ No newline at end of file +rand = "0.9" diff --git a/crates/energy/Cargo.toml b/crates/energy/Cargo.toml index 30f4947..00a97f2 100644 --- a/crates/energy/Cargo.toml +++ b/crates/energy/Cargo.toml @@ -5,4 +5,4 @@ edition = "2024" description = "Core systems governing how energy manifests, transforms and flows." [dependencies] -bevy = "0.16" \ No newline at end of file +bevy = "0.17" diff --git a/crates/energy/src/conservation.rs b/crates/energy/src/conservation.rs index 5c76e42..2545916 100644 --- a/crates/energy/src/conservation.rs +++ b/crates/energy/src/conservation.rs @@ -14,6 +14,7 @@ pub enum EnergyType { /// Component tracking energy in a system #[derive(Component, Debug, Clone, Copy, Reflect)] +#[reflect(Component)] pub struct EnergyQuantity { /// Energy amount in joules pub value: f32, @@ -70,7 +71,7 @@ pub enum TransactionType { } /// Event for energy transfers between entities -#[derive(Event, Debug)] +#[derive(Message, Debug)] pub struct EnergyTransferEvent { /// Source entity losing energy pub source: Entity, @@ -84,6 +85,7 @@ pub struct EnergyTransferEvent { /// Component for precise energy accounting #[derive(Component, Debug, Reflect)] +#[reflect(Component)] pub struct EnergyAccountingLedger { /// History of all transactions, newest first pub transactions: Vec, @@ -244,6 +246,6 @@ impl Plugin for EnergyConservationPlugin { // Add resources .init_resource::() // Add event channel - .add_event::(); + .add_message::(); } } diff --git a/crates/energy/src/electromagnetism/fields.rs b/crates/energy/src/electromagnetism/fields.rs index 241cfc3..ed9102b 100644 --- a/crates/energy/src/electromagnetism/fields.rs +++ b/crates/energy/src/electromagnetism/fields.rs @@ -6,6 +6,7 @@ pub const MAGNETIC_CONSTANT_DIV_4PI: f32 = 1e-7; /// Represents an electric field component #[derive(Component, Debug, Clone, Copy, Reflect, Default)] +#[reflect(Component)] pub struct ElectricField { /// Magnitude and direction of the electric field pub field: Vec2, @@ -52,6 +53,7 @@ impl ElectricField { /// Represents a magnetic field component #[derive(Component, Debug, Clone, Copy, Reflect, Default)] +#[reflect(Component)] pub struct MagneticField { /// Magnitude and direction of the magnetic field pub field: Vec2, @@ -107,7 +109,7 @@ impl MagneticField { } /// Event for field interactions -#[derive(Event, Debug)] +#[derive(Message, Debug)] pub struct ElectromagneticFieldInteractionEvent { /// Source entity generating the field pub source: Entity, @@ -119,45 +121,45 @@ pub struct ElectromagneticFieldInteractionEvent { /// System for calculating field interactions pub fn calculate_field_interactions( - mut field_interaction_events: EventWriter, + mut field_interaction_events: MessageWriter, electric_fields: Query<(Entity, &ElectricField)>, magnetic_fields: Query<(Entity, &MagneticField)>, ) { // Electric field interactions - for (source_entity, source_field) in electric_fields.iter() { - for (target_entity, target_field) in electric_fields.iter() { - if source_entity == target_entity { - continue; - } - - let interaction_strength = source_field.strength() * target_field.strength(); - - if interaction_strength > f32::EPSILON { - field_interaction_events.write(ElectromagneticFieldInteractionEvent { - source: source_entity, - target: target_entity, - interaction_strength, - }); - } + let mut electric_pairs = electric_fields.iter_combinations(); + while let Some([(entity_a, field_a), (entity_b, field_b)]) = electric_pairs.fetch_next() { + let interaction_strength = field_a.strength() * field_b.strength(); + + if interaction_strength > f32::EPSILON { + field_interaction_events.write(ElectromagneticFieldInteractionEvent { + source: entity_a, + target: entity_b, + interaction_strength, + }); + field_interaction_events.write(ElectromagneticFieldInteractionEvent { + source: entity_b, + target: entity_a, + interaction_strength, + }); } } // Magnetic field interactions (similar logic) - for (source_entity, source_field) in magnetic_fields.iter() { - for (target_entity, target_field) in magnetic_fields.iter() { - if source_entity == target_entity { - continue; - } - - let interaction_strength = source_field.strength() * target_field.strength(); - - if interaction_strength > f32::EPSILON { - field_interaction_events.write(ElectromagneticFieldInteractionEvent { - source: source_entity, - target: target_entity, - interaction_strength, - }); - } + let mut magnetic_pairs = magnetic_fields.iter_combinations(); + while let Some([(entity_a, field_a), (entity_b, field_b)]) = magnetic_pairs.fetch_next() { + let interaction_strength = field_a.strength() * field_b.strength(); + + if interaction_strength > f32::EPSILON { + field_interaction_events.write(ElectromagneticFieldInteractionEvent { + source: entity_a, + target: entity_b, + interaction_strength, + }); + field_interaction_events.write(ElectromagneticFieldInteractionEvent { + source: entity_b, + target: entity_a, + interaction_strength, + }); } } } @@ -172,7 +174,7 @@ impl Plugin for ElectromagneticFieldPlugin { .register_type::() .register_type::() // Add electromagnetic field interaction event - .add_event::() + .add_message::() // Add system for field interactions .add_systems(Update, calculate_field_interactions); } diff --git a/crates/energy/src/electromagnetism/interactions.rs b/crates/energy/src/electromagnetism/interactions.rs index f73a23a..82bd4be 100644 --- a/crates/energy/src/electromagnetism/interactions.rs +++ b/crates/energy/src/electromagnetism/interactions.rs @@ -24,6 +24,11 @@ pub struct ElectromagneticWave { impl ElectromagneticWave { pub fn new(frequency: f32, direction: Vec2, electric_amplitude: f32, phase: f32) -> Self { + assert!( + frequency > 0.0, + "Electromagnetic wave frequency must be strictly positive" + ); + // Calculate wavelength and wave number let wavelength = C / frequency; let wave_number = 2.0 * std::f32::consts::PI / wavelength; diff --git a/crates/energy/src/electromagnetism/mod.rs b/crates/energy/src/electromagnetism/mod.rs index c9f04da..97c307a 100644 --- a/crates/energy/src/electromagnetism/mod.rs +++ b/crates/energy/src/electromagnetism/mod.rs @@ -11,7 +11,7 @@ impl Plugin for ElectromagnetismPlugin { .register_type::() .register_type::() .register_type::() - .add_event::() + .add_message::() .add_systems(Update, fields::calculate_field_interactions); } } diff --git a/crates/energy/src/thermodynamics/entropy.rs b/crates/energy/src/thermodynamics/entropy.rs index 4d7c9eb..0dca535 100644 --- a/crates/energy/src/thermodynamics/entropy.rs +++ b/crates/energy/src/thermodynamics/entropy.rs @@ -2,6 +2,7 @@ use bevy::prelude::*; /// Entropy component for thermodynamic systems #[derive(Component, Debug, Clone, Copy, Reflect)] +#[reflect(Component)] pub struct Entropy { /// Entropy in J/K pub value: f32, @@ -17,6 +18,7 @@ impl Entropy { /// Process reversibility characteristic #[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Reflect)] +#[reflect(Component)] pub enum Reversibility { Reversible, Irreversible, diff --git a/crates/energy/src/thermodynamics/equilibrium.rs b/crates/energy/src/thermodynamics/equilibrium.rs index c469a86..acc4619 100644 --- a/crates/energy/src/thermodynamics/equilibrium.rs +++ b/crates/energy/src/thermodynamics/equilibrium.rs @@ -2,6 +2,7 @@ use bevy::prelude::*; /// Component marking systems in thermal equilibrium #[derive(Component, Debug, Reflect)] +#[reflect(Component)] pub struct ThermalEquilibrium { pub connected_entities: Vec, /// Equilibrium group ID for transitivity tracking @@ -12,6 +13,7 @@ pub struct ThermalEquilibrium { /// and the matter crate is implemented /// This is a placeholder for the actual phase state representation #[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Reflect)] +#[reflect(Component)] pub enum PhaseState { Solid, Liquid, diff --git a/crates/energy/src/thermodynamics/mod.rs b/crates/energy/src/thermodynamics/mod.rs index f6215e9..a7e4f59 100644 --- a/crates/energy/src/thermodynamics/mod.rs +++ b/crates/energy/src/thermodynamics/mod.rs @@ -23,7 +23,7 @@ impl Plugin for ThermodynamicsPlugin { .register_type::() .register_type::() .register_type::() - .add_event::() + .add_message::() .configure_sets( Update, ( diff --git a/crates/energy/src/thermodynamics/thermal.rs b/crates/energy/src/thermodynamics/thermal.rs index 46aa6aa..1cafd29 100644 --- a/crates/energy/src/thermodynamics/thermal.rs +++ b/crates/energy/src/thermodynamics/thermal.rs @@ -5,6 +5,7 @@ pub const STEFAN_BOLTZMANN: f32 = 5.67e-8; // W/(m²·K⁴) /// Temperature component for thermal systems #[derive(Component, Debug, Clone, Copy, Reflect, Default)] +#[reflect(Component)] pub struct Temperature { /// Temperature in Kelvin pub value: f32, @@ -36,6 +37,7 @@ impl Temperature { /// Thermal conductivity property #[derive(Component, Debug, Clone, Copy, Reflect, Default)] +#[reflect(Component)] pub struct ThermalConductivity { /// W/(m·K) pub value: f32, @@ -43,6 +45,7 @@ pub struct ThermalConductivity { /// Thermal diffusivity property #[derive(Component, Debug, Clone, Copy, Reflect, Default)] +#[reflect(Component)] pub struct ThermalDiffusivity { /// m²/s pub value: f32, @@ -63,6 +66,7 @@ impl ThermalDiffusivity { /// Emissivity property for radiation calculations #[derive(Component, Debug, Clone, Copy, Reflect, Default)] +#[reflect(Component)] pub struct Emissivity { /// Dimensionless value between 0.0 and 1.0 /// 0.0 = perfect reflector, 1.0 = perfect emitter (black body) @@ -78,7 +82,7 @@ impl Emissivity { } /// Event for thermal energy transfer between entities -#[derive(Event, Debug)] +#[derive(Message, Debug)] pub struct ThermalTransferEvent { /// Source entity losing thermal energy pub source: Entity, @@ -90,7 +94,7 @@ pub struct ThermalTransferEvent { /// System for calculating heat conduction between entities pub fn calculate_thermal_transfer( - mut thermal_transfer_events: EventWriter, + mut thermal_transfer_events: MessageWriter, query: Query<(Entity, &Temperature, &ThermalConductivity)>, ) { // Use query.iter_combinations() to efficiently compare all entities @@ -157,7 +161,7 @@ impl Plugin for ThermalSystemPlugin { .register_type::() .register_type::() // Add thermal transfer event channel - .add_event::() + .add_message::() // Add system for thermal calculations .add_systems(Update, calculate_thermal_transfer); } diff --git a/crates/energy/src/waves/mod.rs b/crates/energy/src/waves/mod.rs index d535ec8..8b1e76c 100644 --- a/crates/energy/src/waves/mod.rs +++ b/crates/energy/src/waves/mod.rs @@ -16,7 +16,7 @@ impl Plugin for WavesPlugin { .register_type::() .register_type::() .register_type::() - .add_event::() + .add_message::() .add_systems( Update, ( diff --git a/crates/energy/src/waves/oscillation.rs b/crates/energy/src/waves/oscillation.rs index a23414e..89f63bf 100644 --- a/crates/energy/src/waves/oscillation.rs +++ b/crates/energy/src/waves/oscillation.rs @@ -1,7 +1,17 @@ use bevy::prelude::*; +#[inline] +fn normalize_or(vec: Vec2, fallback: Vec2) -> Vec2 { + if vec.length_squared() > f32::EPSILON { + vec.normalize() + } else { + fallback + } +} + /// Wave parameters for configuring wave behavior #[derive(Component, Debug, Clone, Copy, Reflect)] +#[reflect(Component)] pub struct WaveParameters { /// Wave propagation speed (units/second) pub speed: f32, @@ -68,13 +78,13 @@ impl WaveParameters { /// Fluent interface for setting direction pub fn with_direction(mut self, direction: Vec2) -> Self { - self.direction = direction.normalize(); + self.direction = normalize_or(direction, self.direction); self } /// Fluent interface for setting displacement axis pub fn with_displacement_axis(mut self, displacement_axis: Vec2) -> Self { - self.displacement_axis = displacement_axis.normalize(); + self.displacement_axis = normalize_or(displacement_axis, self.displacement_axis); self } @@ -113,7 +123,7 @@ pub fn damping_from_half_life(half_life: f32) -> f32 { } /// Event for wave generation or modification -#[derive(Event, Debug)] +#[derive(Message, Debug)] pub struct WaveGenerationEvent { /// Parameters of the generated wave pub parameters: WaveParameters, @@ -122,7 +132,7 @@ pub struct WaveGenerationEvent { } /// System for wave parameter validation -pub fn validate_wave_parameters(mut wave_generation_events: EventReader) { +pub fn validate_wave_parameters(mut wave_generation_events: MessageReader) { for event in wave_generation_events.read() { // Validate wave parameters assert!( diff --git a/crates/energy/src/waves/propagation.rs b/crates/energy/src/waves/propagation.rs index cd9415d..7dee9ec 100644 --- a/crates/energy/src/waves/propagation.rs +++ b/crates/energy/src/waves/propagation.rs @@ -1,6 +1,15 @@ use super::oscillation::{WaveParameters, angular_frequency, wave_number}; use bevy::prelude::*; +#[inline] +fn normalize_or(vec: Vec2, fallback: Vec2) -> Vec2 { + if vec.length_squared() > f32::EPSILON { + vec.normalize() + } else { + fallback + } +} + // Calculate modified angular frequency with dispersion #[inline] pub fn dispersive_angular_frequency(params: &WaveParameters, k: f32) -> f32 { @@ -19,6 +28,7 @@ pub fn dispersive_angular_frequency(params: &WaveParameters, k: f32) -> f32 { /// Component to store position for wave calculations #[derive(Component, Debug, Clone, Reflect)] +#[reflect(Component)] pub struct WavePosition(pub Vec2); impl WavePosition { @@ -37,6 +47,7 @@ impl WavePosition { /// Wave type marker component #[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Reflect)] +#[reflect(Component)] pub enum WaveType { Traveling, Radial, @@ -45,6 +56,7 @@ pub enum WaveType { // Marker component for wave centers (for radial waves) #[derive(Component, Reflect)] +#[reflect(Component)] pub struct WaveCenterMarker; #[inline] @@ -52,7 +64,8 @@ pub fn solve_wave(params: &WaveParameters, position: Vec2, time: f32) -> f32 { let k = wave_number(params.wavelength); let omega = dispersive_angular_frequency(params, k); - let k_vec = params.direction.normalize() * k; + let direction = normalize_or(params.direction, Vec2::X); + let k_vec = direction * k; let dot_product = k_vec.dot(position); // Calculate the phase argument for the wave function @@ -106,6 +119,8 @@ pub fn update_wave_displacements( let t = time.elapsed_secs(); for (mut transform, params, position, wave_type) in query.iter_mut() { + let base_translation = Vec3::new(position.0.x, position.0.y, transform.translation.z); + let displacement = match wave_type { Some(WaveType::Radial) => { // Find the nearest wave center @@ -128,9 +143,10 @@ pub fn update_wave_displacements( _ => solve_wave(params, position.0, t), }; - let displacement_vec = params.displacement_axis.normalize() * displacement; - transform.translation.x += displacement_vec.x; - transform.translation.y += displacement_vec.y; + let displacement_axis = normalize_or(params.displacement_axis, Vec2::Y); + let displacement_vec = displacement_axis * displacement; + transform.translation = + base_translation + Vec3::new(displacement_vec.x, displacement_vec.y, 0.0); } } @@ -145,13 +161,15 @@ pub fn create_linear_wave( damping: f32, dispersion_factor: f32, ) -> WaveParameters { + let current = WaveParameters::default(); + WaveParameters { amplitude, wavelength, speed, phase, - direction: direction.normalize(), - displacement_axis: displacement_axis.normalize(), + direction: normalize_or(direction, current.direction), + displacement_axis: normalize_or(displacement_axis, current.displacement_axis), damping, dispersion_factor, } diff --git a/crates/energy/src/waves/superposition.rs b/crates/energy/src/waves/superposition.rs index 6757faa..6f9cb4c 100644 --- a/crates/energy/src/waves/superposition.rs +++ b/crates/energy/src/waves/superposition.rs @@ -2,6 +2,15 @@ use super::oscillation::{WaveParameters, angular_frequency, wave_number}; use super::propagation::WavePosition; use bevy::prelude::*; +#[inline] +fn normalize_or(vec: Vec2, fallback: Vec2) -> Vec2 { + if vec.length_squared() > f32::EPSILON { + vec.normalize() + } else { + fallback + } +} + #[inline] pub fn solve_standing_wave( params: &WaveParameters, @@ -12,7 +21,7 @@ pub fn solve_standing_wave( let k = wave_number(params.wavelength); let omega = angular_frequency(params.speed, k); - let direction = params.direction.normalize(); + let direction = normalize_or(params.direction, Vec2::X); let spatial_term = (k * direction.dot(position) + params.phase).sin(); let temporal_term = (omega * time).cos(); @@ -27,6 +36,7 @@ pub fn solve_standing_wave( /// Marker component for standing waves #[derive(Component, Reflect, Default)] +#[reflect(Component)] pub struct StandingWaveMarker; /// System for updating standing waves specifically @@ -37,15 +47,17 @@ pub fn update_standing_waves( let t = time.elapsed_secs(); for (mut transform, params, position) in query.iter_mut() { + let base_translation = Vec3::new(position.0.x, position.0.y, transform.translation.z); let displacement = solve_standing_wave(params, position.0, t, None:: f32>); - let displacement_vec = params.displacement_axis.normalize() * displacement; - transform.translation.x += displacement_vec.x; - transform.translation.y += displacement_vec.y; + let displacement_axis = normalize_or(params.displacement_axis, Vec2::Y); + let displacement_vec = displacement_axis * displacement; + transform.translation = + base_translation + Vec3::new(displacement_vec.x, displacement_vec.y, 0.0); } } /// Event for standing wave modifications -#[derive(Event)] +#[derive(Message)] pub struct StandingWaveModificationEvent { pub entity: Entity, pub new_parameters: WaveParameters, @@ -54,7 +66,7 @@ pub struct StandingWaveModificationEvent { /// System to handle wave parameter modifications pub fn handle_wave_modifications( mut commands: Commands, - mut wave_mod_events: EventReader, + mut wave_mod_events: MessageReader, ) { for event in wave_mod_events.read() { commands.entity(event.entity).insert(event.new_parameters); @@ -72,13 +84,15 @@ pub fn create_standing_wave_parameters( damping: f32, dispersion_factor: f32, ) -> WaveParameters { + let defaults = WaveParameters::default(); + WaveParameters { amplitude, wavelength, speed: frequency * wavelength, phase, - direction: direction.normalize(), - displacement_axis: displacement_axis.normalize(), + direction: normalize_or(direction, defaults.direction), + displacement_axis: normalize_or(displacement_axis, defaults.displacement_axis), damping, dispersion_factor, } @@ -90,7 +104,7 @@ pub struct StandingWavePlugin; impl Plugin for StandingWavePlugin { fn build(&self, app: &mut App) { app.register_type::() - .add_event::() + .add_message::() .add_systems(Update, (update_standing_waves, handle_wave_modifications)); } } diff --git a/crates/energy/src/waves/wave_equation.rs b/crates/energy/src/waves/wave_equation.rs index d5c2a97..9ae3f1a 100644 --- a/crates/energy/src/waves/wave_equation.rs +++ b/crates/energy/src/waves/wave_equation.rs @@ -89,31 +89,39 @@ impl WaveEquation2D { const CHUNK_SIZE: usize = 32; // Optimize memory access patterns by processing in blocks - for j_chunk in 1..((self.ny - 1).div_ceil(CHUNK_SIZE)) { - let j_start = (j_chunk - 1) * CHUNK_SIZE + 1; - let j_end = (j_start + CHUNK_SIZE - 1).min(self.ny - 2); - - for i_chunk in 1..((self.nx - 1).div_ceil(CHUNK_SIZE)) { - let i_start = (i_chunk - 1) * CHUNK_SIZE + 1; - let i_end = (i_start + CHUNK_SIZE - 1).min(self.nx - 2); - - for j in j_start..=j_end { - for i in i_start..=i_end { - // Finite difference formula for 2D wave equation - let laplacian_x = self.get(&self.u_current, i + 1, j) - - 2.0 * self.get(&self.u_current, i, j) - + self.get(&self.u_current, i - 1, j); - - let laplacian_y = self.get(&self.u_current, i, j + 1) - - 2.0 * self.get(&self.u_current, i, j) - + self.get(&self.u_current, i, j - 1); - - let next_value = 2.0 * self.get(&self.u_current, i, j) - - self.get(&self.u_previous, i, j) - + cx * laplacian_x - + cy * laplacian_y; - - self.set(&mut u_next, i, j, next_value); + let inner_height = self.ny.saturating_sub(2); + let inner_width = self.nx.saturating_sub(2); + + if inner_height > 0 && inner_width > 0 { + let j_chunks = (inner_height + CHUNK_SIZE - 1) / CHUNK_SIZE; + let i_chunks = (inner_width + CHUNK_SIZE - 1) / CHUNK_SIZE; + + for j_chunk in 0..j_chunks { + let j_start = 1 + j_chunk * CHUNK_SIZE; + let j_end = (j_start + CHUNK_SIZE - 1).min(self.ny - 2); + + for i_chunk in 0..i_chunks { + let i_start = 1 + i_chunk * CHUNK_SIZE; + let i_end = (i_start + CHUNK_SIZE - 1).min(self.nx - 2); + + for j in j_start..=j_end { + for i in i_start..=i_end { + // Finite difference formula for 2D wave equation + let laplacian_x = self.get(&self.u_current, i + 1, j) + - 2.0 * self.get(&self.u_current, i, j) + + self.get(&self.u_current, i - 1, j); + + let laplacian_y = self.get(&self.u_current, i, j + 1) + - 2.0 * self.get(&self.u_current, i, j) + + self.get(&self.u_current, i, j - 1); + + let next_value = 2.0 * self.get(&self.u_current, i, j) + - self.get(&self.u_previous, i, j) + + cx * laplacian_x + + cy * laplacian_y; + + self.set(&mut u_next, i, j, next_value); + } } } } @@ -138,6 +146,7 @@ impl WaveEquation2D { /// Component wrapper for the wave equation #[derive(Component, Reflect)] +#[reflect(Component)] pub struct WaveEquationComponent { pub solver: WaveEquation2D, } diff --git a/crates/forces/Cargo.toml b/crates/forces/Cargo.toml index 67c46b2..6d569bb 100644 --- a/crates/forces/Cargo.toml +++ b/crates/forces/Cargo.toml @@ -5,4 +5,4 @@ edition = "2024" description = "Fundamental interaction mechanisms that create relationships between entities and drive dynamic behaviors across scales." [dependencies] -bevy = "0.16" \ No newline at end of file +bevy = "0.17" diff --git a/crates/forces/src/core/gravity.rs b/crates/forces/src/core/gravity.rs index 282cb94..378b9ac 100644 --- a/crates/forces/src/core/gravity.rs +++ b/crates/forces/src/core/gravity.rs @@ -33,18 +33,22 @@ impl Default for UniformGravity { /// Component for entities affected by gravity #[derive(Component, Debug, Clone, Copy, Reflect)] +#[reflect(Component)] pub struct GravityAffected; /// Marker component for gravity field measurement points #[derive(Component, Debug, Clone, Copy, Reflect)] +#[reflect(Component)] pub struct GravityFieldMarker; /// Component for objects that generate gravitational attraction #[derive(Component, Debug, Clone, Copy, Reflect)] +#[reflect(Component)] pub struct GravitySource; /// Marker for bodies with significant mass #[derive(Component, Debug, Clone, Copy, Reflect)] +#[reflect(Component)] pub struct MassiveBody; // Barnes-Hut spatial partitioning diff --git a/crates/forces/src/core/newton_laws.rs b/crates/forces/src/core/newton_laws.rs index 4b9b5b7..a7189fd 100644 --- a/crates/forces/src/core/newton_laws.rs +++ b/crates/forces/src/core/newton_laws.rs @@ -37,6 +37,7 @@ impl Distance for Vec2 {} /// Component for mass properties of an entity #[derive(Component, Debug, Clone, Copy, Reflect)] +#[reflect(Component)] pub struct Mass { /// Mass in kilograms pub value: f32, @@ -95,6 +96,7 @@ impl Mass { /// Component representing a force applied to an entity #[derive(Component, Debug, Clone, Reflect)] +#[reflect(Component)] pub struct AppliedForce { /// Force vector in Newtons pub force: Vec3, @@ -143,6 +145,7 @@ impl AppliedForce { /// Component for velocity (both linear and angular) #[derive(Component, Debug, Clone, Copy, Reflect, Default)] +#[reflect(Component)] pub struct Velocity { /// Linear velocity in meters per second pub linvel: Vec3, @@ -159,6 +162,11 @@ pub fn apply_forces(time: Res