paiagram/
lines.rs

1use crate::graph::Interval;
2use crate::units::distance::Distance;
3use crate::{graph::Station, units::time::TimetableTime};
4use bevy::ecs::entity::{EntityMapper, MapEntities};
5use bevy::prelude::*;
6use moonshine_core::kind::{Instance, SpawnInstance};
7use moonshine_core::save::prelude::*;
8
9/// Displayed line type:
10/// A list of (station entity, size of the interval on canvas in mm)
11/// The first entry is the starting station, where the canvas distance is simply omitted.
12/// Each entry afterwards represents the interval from the previous station to this station.
13pub type DisplayedLineType = Vec<(Instance<Station>, f32)>;
14
15pub type RulerLineType = Vec<(Instance<Station>, TimetableTime)>;
16
17#[derive(Reflect, Debug, Default)]
18pub enum ScaleMode {
19    Linear,
20    #[default]
21    Logarithmic,
22    Uniform,
23}
24
25/// An imaginary (railway) line on the canvas, consisting of multiple segments.
26#[derive(Reflect, Component, Debug)]
27#[component(map_entities)]
28#[reflect(Component, MapEntities)]
29#[require(Name, Save)]
30pub struct DisplayedLine {
31    pub stations: Vec<(Instance<Station>, f32)>,
32    pub scale_mode: ScaleMode,
33}
34
35impl MapEntities for DisplayedLine {
36    fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
37        for (station, _) in &mut self.stations {
38            station.map_entities(entity_mapper);
39        }
40    }
41}
42
43pub enum DisplayedLineError {
44    InvalidIndex,
45    SameStationAsNeighbor,
46    AdjacentIntervals((Entity, Entity)),
47}
48
49impl std::fmt::Debug for DisplayedLineError {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        match self {
52            DisplayedLineError::InvalidIndex => write!(f, "Invalid index for inserting station"),
53            DisplayedLineError::SameStationAsNeighbor => {
54                write!(f, "Cannot insert the same station as a neighbor")
55            }
56            DisplayedLineError::AdjacentIntervals((e1, e2)) => {
57                write!(
58                    f,
59                    "Cannot insert station that would create adjacent identical intervals: ({:?}, {:?})",
60                    e1, e2
61                )
62            }
63        }
64    }
65}
66
67impl DisplayedLine {
68    pub fn new(stations: DisplayedLineType) -> Self {
69        Self {
70            stations,
71            scale_mode: ScaleMode::default(),
72        }
73    }
74    pub fn _new(stations: impl Iterator<Item = Entity>) -> Self {
75        todo!("implement this stuff")
76    }
77    pub fn stations(&self) -> &DisplayedLineType {
78        &self.stations
79    }
80    pub unsafe fn stations_mut(&mut self) -> &mut DisplayedLineType {
81        &mut self.stations
82    }
83    pub fn insert(
84        &mut self,
85        index: usize,
86        (station, height): (Instance<Station>, f32),
87    ) -> Result<(), DisplayedLineError> {
88        // Two same intervals cannot be neighbours
89        // an interval is defined by (prev_entity, this_entity)
90        if index > self.stations.len() {
91            return Err(DisplayedLineError::InvalidIndex);
92        };
93        let prev_prev = if index >= 2 {
94            Some(self.stations[index - 2].0)
95        } else {
96            None
97        };
98        let prev = if index >= 1 {
99            Some(self.stations[index - 1].0)
100        } else {
101            None
102        };
103        let next = self.stations.get(index).map(|(e, _)| *e);
104        let next_next = self.stations.get(index + 1).map(|(e, _)| *e);
105        if let Some(prev_prev) = prev_prev
106            && prev_prev == station
107        {
108            return Err(DisplayedLineError::AdjacentIntervals((
109                prev_prev.entity(),
110                prev.unwrap().entity(),
111            )));
112        };
113        if let Some(next_next) = next_next
114            && next_next == station
115        {
116            return Err(DisplayedLineError::AdjacentIntervals((
117                next.unwrap().entity(),
118                next_next.entity(),
119            )));
120        };
121        if let Some(prev) = prev
122            && prev == station
123        {
124            return Err(DisplayedLineError::SameStationAsNeighbor);
125        };
126        if let Some(next) = next
127            && next == station
128        {
129            return Err(DisplayedLineError::SameStationAsNeighbor);
130        };
131        self.stations.insert(index, (station, height));
132        Ok(())
133    }
134    pub fn push(&mut self, station: (Instance<Station>, f32)) -> Result<(), DisplayedLineError> {
135        self.insert(self.stations.len(), station)
136    }
137}
138
139pub fn adjust_intervals_length(
140    In(entity): In<Entity>,
141    graph: Res<crate::graph::Graph>,
142    intervals: Query<&Interval>,
143    mut displayed_lines: Query<&mut DisplayedLine>,
144    names: Query<&Name>,
145) {
146    let mut displayed_line = match displayed_lines.get_mut(entity) {
147        Ok(l) => l,
148        Err(e) => {
149            error!("Could not get displayed line: {:?}", e);
150            return;
151        }
152    };
153    let mut stations_iter = displayed_line.stations.iter_mut();
154    let Some((prev, _)) = stations_iter.next() else {
155        warn!(
156            "Displayed line {} is empty, skipping...",
157            names.get(entity).map_or("<unknown>", Name::as_str)
158        );
159        return;
160    };
161    let mut prev = *prev;
162    for (curr, height) in stations_iter {
163        let (count, acc) = graph
164            .edges_connecting(prev, *curr)
165            .filter_map(|r| intervals.get(r.weight.entity()).ok())
166            .fold((0i32, Distance(0)), |(count, len), i| {
167                (count + 1, len + i.length)
168            });
169        if count == 0 {
170            warn!(
171                "There are no intervals connecting between {} and {}, skipping",
172                names.get(prev.entity()).map_or("<unknown>", Name::as_str),
173                names.get(curr.entity()).map_or("<unknown>", Name::as_str)
174            );
175            continue;
176        }
177        let average_length = acc / count;
178        *height = average_length.0 as f32;
179        prev = *curr;
180    }
181}
182
183pub fn create_intervals_from_displayed_line(
184    In(line_entity): In<Entity>,
185    displayed_lines: Query<&DisplayedLine>,
186    mut graph: ResMut<crate::graph::Graph>,
187    mut commands: Commands,
188    stations: Query<&Station>,
189) {
190    let line = match displayed_lines.get(line_entity) {
191        Ok(l) => l,
192        Err(e) => {
193            error!("Could not find displayed line: {:?}", e);
194            return;
195        }
196    };
197    // TODO: switch to array_windows in the future
198    for w in line.stations.windows(2) {
199        let [(prev, _), (curr, _)] = w else {
200            unreachable!()
201        };
202        let length = {
203            let Ok(p) = stations.get(prev.entity()) else {
204                return;
205            };
206            let Ok(c) = stations.get(curr.entity()) else {
207                return;
208            };
209            Distance(p.0.distance(c.0) as i32)
210        };
211        if !graph.contains_edge(*prev, *curr) {
212            let i1 = commands
213                .spawn_instance(Interval {
214                    length,
215                    speed_limit: None,
216                })
217                .instance();
218            graph.add_edge(*prev, *curr, i1);
219        }
220        if !graph.contains_edge(*curr, *prev) {
221            let i2 = commands
222                .spawn_instance(Interval {
223                    length,
224                    speed_limit: None,
225                })
226                .instance();
227            graph.add_edge(*curr, *prev, i2);
228        }
229    }
230}
231
232#[derive(Component, Debug)]
233#[require(Name)]
234pub struct RulerLine(pub RulerLineType);
235
236pub struct LinesPlugin;
237
238impl Plugin for LinesPlugin {
239    fn build(&self, app: &mut App) {}
240}