traaains/sim/track/node/
mod.rs1use bevy::{
4 asset::Handle,
5 ecs::{
6 component::Component,
7 entity::Entity,
8 event::EntityEvent,
9 observer::{ObserverSystemExt, On},
10 system::{Commands, ResMut},
11 },
12 mesh::{Mesh, Mesh2d},
13 picking::events::PointerPress,
14 sprite_render::{ColorMaterial, MeshMaterial2d},
15 state::{condition::in_state, state::NextState},
16 transform::components::Transform,
17};
18use smallvec::SmallVec;
19
20use crate::{editor::EditorState, ui::UiState, utils::alloc_map::AllocMap};
21
22mod commands;
23mod connection;
24mod manager;
25
26pub use commands::RemoveConnectionFor;
27pub use connection::{Connection, ConnectionLocation};
28
29const MAX_CONNECTIONS: usize = 32;
30const STATIC_CONNECTIONS: usize = 3;
31
32#[derive(Clone, Default)]
44pub struct NodeConnections {
45 inner: SmallVec<[Option<Connection>; STATIC_CONNECTIONS]>,
46 alloc_map: AllocMap<MAX_CONNECTIONS>,
47 count: usize,
48
49 active: Option<(usize, usize)>,
52}
53
54impl NodeConnections {
55 fn find_first_two(&self) -> Option<[usize; 2]> {
56 let mut conns = self
58 .inner
59 .iter()
60 .enumerate()
61 .filter_map(|(i, c)| if c.is_some() { Some(i) } else { None });
62
63 Some([conns.next()?, conns.next()?])
64 }
65
66 pub fn add(&mut self, c: Connection) -> Result<usize, Connection> {
69 match self.alloc_map.allocate() {
70 Some(index) => {
71 if index == self.inner.len() {
74 self.inner.push(Some(c));
75 } else {
76 self.inner[index] = Some(c);
77 }
78
79 self.count += 1;
80
81 if self.count == 2 {
82 let [c0, c1] = self.find_first_two().unwrap();
83 self.activate(c0, c1);
84 }
85
86 Ok(index)
87 }
88 None => Err(c),
89 }
90 }
91
92 pub fn remove(&mut self, index: usize) -> bool {
95 if !self.alloc_map.is_allocated(index) {
96 return false;
97 }
98
99 self.inner[index] = None;
100 self.alloc_map.deallocate(index);
101 self.count -= 1;
102
103 if let Some(ac) = self.active
104 && (ac.0 == index || ac.1 == index)
105 {
106 self.deactivate();
107 }
108
109 true
110 }
111
112 pub fn remove_for(&mut self, segment: Entity) -> bool {
115 for (i, c) in self.inner.iter().enumerate() {
116 if let Some(conn) = c
117 && conn.segment == segment
118 {
119 return self.remove(i);
120 }
121 }
122
123 false
124 }
125
126 pub fn has_free(&self) -> bool {
128 self.alloc_map.has_free()
129 }
130
131 pub fn iter(&self) -> impl Iterator<Item = &Connection> {
133 self.inner.iter().filter_map(|c| c.as_ref())
134 }
135
136 pub fn len(&self) -> usize {
138 self.count
139 }
140
141 pub fn is_empty(&self) -> bool {
143 self.len() == 0
144 }
145
146 pub fn activate(&mut self, mut c0: usize, mut c1: usize) {
148 assert!(c0 != c1);
149
150 if c0 > c1 {
151 (c0, c1) = (c1, c0);
152 }
153
154 self.active = Some((c0, c1));
155 }
156
157 pub fn deactivate(&mut self) {
159 self.active = None;
160 }
161
162 pub fn active(&self) -> Option<(&Connection, &Connection)> {
164 Some((
165 self.inner[self.active?.0].as_ref().unwrap(),
166 self.inner[self.active?.1].as_ref().unwrap(),
167 ))
168 }
169
170 fn advance(c0: &mut usize, c1: &mut usize, len: usize) {
171 if *c1 + 1 < len {
172 *c1 += 1;
173 } else {
174 if *c0 + 2 < len {
175 *c0 += 1;
176 } else {
177 *c0 = 0;
178 }
179 *c1 = *c0 + 1;
180 }
181 }
182
183 pub fn activate_next(&mut self) {
185 match self.active {
186 Some((sc0, sc1)) => {
187 let len = self.inner.len();
188 let (mut c0, mut c1) = (sc0, sc1);
189 NodeConnections::advance(&mut c0, &mut c1, len);
190
191 loop {
192 if self.inner[c1].is_some() {
193 self.activate(c0, c1);
194 return;
195 }
196
197 NodeConnections::advance(&mut c0, &mut c1, len);
198
199 if (c0, c1) == (sc0, sc1) {
200 return;
201 }
202 }
203 }
204 None => {
205 if let Some([c0, c1]) = self.find_first_two() {
206 self.activate(c0, c1);
207 }
208 }
209 }
210 }
211
212 pub fn get_active_for(&self, segment: Entity) -> Option<&Connection> {
215 if self.inner[self.active?.0].as_ref().unwrap().segment == segment {
216 return self.inner[self.active?.1].as_ref();
217 }
218
219 if self.inner[self.active?.1].as_ref().unwrap().segment == segment {
220 return self.inner[self.active?.0].as_ref();
221 }
222
223 None
224 }
225}
226
227#[derive(Clone, Default, Component)]
229pub struct TrackNode {
230 pub connections: NodeConnections,
231}
232
233impl TrackNode {
234 pub fn spawn(
235 cmd: &mut Commands,
236 transform: Transform,
237 mesh: Handle<Mesh>,
238 mat: Handle<ColorMaterial>,
239 ) -> Entity {
240 cmd.spawn((
241 TrackNode::default(),
242 transform,
243 Mesh2d(mesh),
244 MeshMaterial2d(mat),
245 ))
246 .observe(on_node_press_window.run_if(in_state(EditorState::Viewing)))
247 .id()
248 }
249}
250
251pub fn on_node_press_window(e: On<PointerPress>, mut next_state: ResMut<NextState<UiState>>) {
253 next_state.set(UiState::NodeWindow {
254 target: e.event_target(),
255 });
256}