Skip to main content

khora_lanes/physics_lane/
mod.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Physics Lane.
16//!
17//! After the Phase A refactor, `StandardPhysicsLane` is a thin wrapper
18//! around [`PhysicsProvider::step`]: it does **no** World queries and
19//! **no** structural mutations. The surrounding work is split into three
20//! Substrate-Pass stages:
21//!
22//! 1. [`khora_data::flow::PhysicsFlow::adapt`] — AGDF detach/reattach
23//!    plus the `ECS → provider` sync (`sync_to_world`-equivalent) that
24//!    feeds the simulation its inputs.
25//! 2. **This lane** — `provider.step(dt)`. Pure simulation, no World
26//!    access at all.
27//! 3. [`khora_data::ecs::systems::physics_world_writeback`] — pulls the
28//!    new transforms, kinematic results, and collision events back into
29//!    the World during the `Maintenance` phase.
30
31use khora_core::physics::PhysicsProvider;
32
33/// The standard physics lane for industrial-grade simulation.
34#[derive(Debug, Default)]
35pub struct StandardPhysicsLane;
36
37impl StandardPhysicsLane {
38    /// Creates a new `StandardPhysicsLane`.
39    pub fn new() -> Self {
40        Self
41    }
42}
43
44impl khora_core::lane::Lane for StandardPhysicsLane {
45    fn strategy_name(&self) -> &'static str {
46        "StandardPhysics"
47    }
48
49    fn lane_kind(&self) -> khora_core::lane::LaneKind {
50        khora_core::lane::LaneKind::Physics
51    }
52
53    fn execute(
54        &self,
55        ctx: &mut khora_core::lane::LaneContext,
56    ) -> Result<(), khora_core::lane::LaneError> {
57        use khora_core::lane::{LaneError, OutputDeck, Slot};
58
59        let dt = ctx
60            .get::<khora_core::lane::PhysicsDeltaTime>()
61            .ok_or(LaneError::missing("PhysicsDeltaTime"))?
62            .0;
63        let provider = ctx
64            .get::<Slot<dyn PhysicsProvider>>()
65            .ok_or(LaneError::missing("Slot<dyn PhysicsProvider>"))?
66            .get();
67
68        provider.step(dt);
69
70        // Mark the simulation as having advanced this frame. The
71        // `physics_world_writeback` DataSystem checks this slot and only
72        // pulls fresh transforms from the provider when it's present —
73        // unifying the Lane → Deck → DataSystem pattern across audio and
74        // physics, and avoiding stale writebacks when the agent is paused.
75        if let Some(deck_slot) = ctx.get::<Slot<OutputDeck>>() {
76            *deck_slot
77                .get()
78                .slot::<khora_data::flow::PhysicsStepResult>() =
79                khora_data::flow::PhysicsStepResult { dt };
80        }
81        Ok(())
82    }
83
84    fn as_any(&self) -> &dyn std::any::Any {
85        self
86    }
87
88    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
89        self
90    }
91}