Skip to main content

traaains/sim/track/segment/commands/
split.rs

1use bevy::{
2    ecs::{entity::Entity, observer::ObserverSystemExt, system::Command},
3    mesh::Mesh2d,
4    sprite_render::MeshMaterial2d,
5    state::condition::in_state,
6    transform::components::Transform,
7};
8use bezier::Bezier;
9use shapes::lerp::Lerp;
10
11use crate::{
12    AssetType, AssetsManager,
13    editor::EditorState,
14    sim::{
15        signal::SignalPos,
16        track::{
17            node::{TrackNode, on_node_press_window},
18            segment::{
19                TrackSegment,
20                commands::{RemoveSegment, SpawnSegment},
21                signals_manager::SignalsManager,
22            },
23        },
24    },
25};
26
27/// Splits a segment
28pub struct SplitSegment {
29    /// The target segment
30    target: Entity,
31    /// Index of the wanted split point
32    idx: f32,
33}
34
35impl SplitSegment {
36    pub fn new(target: Entity, idx: f32) -> Self {
37        SplitSegment { target, idx }
38    }
39}
40
41impl Command for SplitSegment {
42    type Out = Result<(), &'static str>;
43
44    fn apply(self, world: &mut bevy::ecs::world::World) -> Self::Out {
45        let Some((ts_transform, mut ts_signals)) = world
46            .entity_mut(self.target)
47            .take::<(Transform, SignalsManager)>()
48        else {
49            return Err("Failed to get transform from target entity!");
50        };
51
52        let ts = RemoveSegment::new(self.target)
53            .with_despawn_nodes(false)
54            .with_despawn_block(false)
55            .with_despawn_signals(false)
56            .apply(world)?;
57
58        let split_point_world = ts_transform.translation.truncate() + ts.shape.lerp(self.idx);
59
60        // spawn new track node
61        let (mesh, mat) = {
62            let assets_manager = world.resource::<AssetsManager>();
63            (
64                assets_manager
65                    .meshes
66                    .get(&AssetType::TrackNodeNormal)
67                    .cloned()
68                    .unwrap(),
69                assets_manager
70                    .materials
71                    .get(&AssetType::TrackNodeNormal)
72                    .cloned()
73                    .unwrap(),
74            )
75        };
76
77        let split_node = world
78            .spawn((
79                TrackNode::default(),
80                Transform::from_translation(split_point_world.extend(5.0)),
81                Mesh2d(mesh),
82                MeshMaterial2d(mat),
83            ))
84            .observe(on_node_press_window.run_if(in_state(EditorState::Viewing)))
85            .id();
86
87        // split
88        let (new_from_shape, new_to_shape) = ts.shape.split(self.idx);
89        let new_from_segment =
90            TrackSegment::new(new_from_shape, ts.from_node, split_node, ts.block);
91        let new_to_segment = TrackSegment::new(new_to_shape, split_node, ts.to_node, ts.block);
92
93        // split signals managers
94        let to_signals_map = ts_signals.inner.split_off(&SignalPos(self.idx));
95        let to_signals = SignalsManager {
96            inner: to_signals_map,
97        };
98
99        // spawn new track segment
100        SpawnSegment::new(new_from_segment, ts_signals, ts_transform).apply(world);
101        SpawnSegment::new(
102            new_to_segment,
103            to_signals,
104            Transform::from_translation(split_point_world.extend(0.0)),
105        )
106        .apply(world);
107
108        Ok(())
109    }
110}