Skip to main content

paiagram_core/
station.rs

1use std::borrow::Cow;
2
3use crate::{
4    graph::{Graph, Node, NodeCoor},
5    trip::class::DisplayedStroke,
6};
7use bevy::{ecs::query::QueryData, prelude::*};
8use moonshine_core::prelude::{MapEntities, ReflectMapEntities};
9
10mod fetch_name;
11pub use fetch_name::StationNamePending;
12
13pub struct StationPlugin;
14impl Plugin for StationPlugin {
15    fn build(&self, app: &mut App) {
16        app.add_plugins(fetch_name::FetchNamePlugin)
17            .add_observer(add_new_station);
18    }
19}
20
21/// Entries that visit this station.
22/// This does not contain all entries that visit this station, since a station
23/// may contain extra platforms, and each platform would also record a set of
24/// entries that visit the platform, not the station.
25/// See [`PlatformEntries`] for further details.
26pub type StationEntries = PlatformEntries;
27/// A type alias to make things clearer.
28pub type Platforms = Children;
29
30/// Marker component for stations that are not managed by the current diagram.
31/// Note that being "external" doesn't mean that it can be excluded from
32/// [`crate::graph::Graph`].
33#[derive(Reflect, Component)]
34#[reflect(Component)]
35#[require(Name, Station)]
36pub struct IsExternalStation;
37
38/// Marker component for depots.
39#[derive(Reflect, Component)]
40#[reflect(Component)]
41#[require(Name, Station)]
42pub struct IsDepot;
43
44/// A station in the network. A station would also host a default platform.
45#[derive(Reflect, Component, Default)]
46#[reflect(Component)]
47#[require(Name, Platform, PlatformEntries, Platforms, DisplayedStroke)]
48pub struct Station;
49
50/// Spawn with [`with_children()``]
51#[derive(Bundle)]
52pub struct StationBundle {
53    station: Station,
54    name: Name,
55    node: Node,
56}
57
58impl StationBundle {
59    pub fn new(name: Cow<'static, str>, node: Node) -> Self {
60        Self {
61            station: Station,
62            name: Name::new(name),
63            node,
64        }
65    }
66}
67
68/// Entries that passes this platform.
69#[derive(Default, Reflect, Component, MapEntities)]
70#[reflect(Component, MapEntities)]
71#[relationship_target(relationship = crate::entry::EntryStop)]
72pub struct PlatformEntries(#[entities] Vec<Entity>);
73
74#[derive(Default, Reflect, Component)]
75#[reflect(Component)]
76#[require(Name, PlatformEntries, crate::graph::Node)]
77pub struct Platform;
78
79#[derive(QueryData)]
80pub struct StationQuery {
81    entity: Entity,
82    station: &'static Station,
83    platforms: &'static Platforms,
84    entries: &'static StationEntries,
85    pub name: &'static Name,
86    is_external_station: Option<&'static IsExternalStation>,
87    pub position: &'static crate::graph::Node,
88    stroke: &'static DisplayedStroke,
89}
90
91impl<'w, 's> StationQueryItem<'w, 's> {
92    /// Whether if the station is an external station
93    pub fn is_external_station(&self) -> bool {
94        self.is_external_station.is_some()
95    }
96    /// Whether if the station is movable by the user
97    pub fn is_movable_by_user(&self) -> bool {
98        !self.is_external_station() && self.platforms.is_empty()
99    }
100    /// Returns all entries passing the station.
101    /// The order is not guaranteed.
102    pub fn passing_entries<'a>(
103        &self,
104        platform_entries: &Query<'a, 'a, &PlatformEntries>,
105    ) -> impl Iterator<Item = Entity> {
106        let platform_entries = platform_entries
107            .iter_many(self.platforms)
108            .flat_map(|p| p.iter());
109        self.entries.iter().chain(platform_entries)
110    }
111    /// Returns all entries passing the station.
112    pub fn passing_entries_by_platform<'a>(
113        &self,
114        platform_entries: &'a Query<'a, 'a, &PlatformEntries>,
115    ) -> impl Iterator<Item = (Entity, impl Iterator<Item = Entity> + 'a)> + 'a
116    where
117        'w: 'a,
118    {
119        let platform_entries = self.platforms.iter().filter_map(|pe| {
120            let Ok(entries) = platform_entries.get(pe) else {
121                return None;
122            };
123            Some((pe, entries.iter()))
124        });
125        std::iter::once((self.entity, self.entries.iter())).chain(platform_entries)
126    }
127}
128
129#[derive(QueryData)]
130pub struct ParentStationOrStation {
131    entity: Entity,
132    station: AnyOf<(
133        &'static Station,
134        (&'static ChildOf, &'static PlatformEntries),
135    )>,
136}
137
138impl<'w, 'q> ParentStationOrStationItem<'w, 'q> {
139    pub fn parent(&self) -> Entity {
140        match self.station {
141            (Some(_), _) => self.entity,
142            (None, Some((parent, _))) => parent.parent(),
143            (None, None) => unreachable!(),
144        }
145    }
146}
147
148#[derive(QueryData)]
149pub struct PlatformQuery {
150    entity: Entity,
151    pub name: &'static Name,
152    station: AnyOf<(&'static Station, &'static ChildOf)>,
153    pub entries: &'static PlatformEntries,
154}
155
156impl<'w, 'q> PlatformQueryItem<'w, 'q> {
157    pub fn station<'a>(&self, station_q: &'a Query<StationQuery>) -> StationQueryItem<'a, 'a> {
158        match self.station {
159            (Some(_), _) => station_q.get(self.entity).unwrap(),
160            (None, Some(parent)) => station_q.get(parent.parent()).unwrap(),
161            (None, None) => unreachable!(),
162        }
163    }
164}
165
166#[derive(Event)]
167pub struct CreateNewStation {
168    pub name: Option<String>,
169    pub coor: NodeCoor,
170}
171
172fn add_new_station(msg: On<CreateNewStation>, mut commands: Commands, mut graph: ResMut<Graph>) {
173    let entity = if let Some(s) = msg.name.as_ref() {
174        commands
175            .spawn(StationBundle::new(
176                s.clone().into(),
177                Node { coor: msg.coor },
178            ))
179            .id()
180    } else {
181        commands
182            .spawn((
183                StationBundle::new("Name Pending".into(), Node { coor: msg.coor }),
184                StationNamePending::new(msg.coor),
185            ))
186            .id()
187    };
188    graph.add_node(entity);
189}