1use crate::core::{
2 container::path_network::NodeId,
3 geometry::{angle::Angle, line_segment::LineSegment, site::Site},
4 Stage,
5};
6
7use super::rules::TransportRules;
8
9#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
10pub struct TransportNode {
11 pub site: Site,
12 pub stage: Stage,
13}
14
15impl TransportNode {
16 pub fn new(site: Site, stage: Stage) -> Self {
17 Self { site, stage }
18 }
19
20 #[allow(dead_code)]
21 fn set_site(mut self, site: Site) -> Self {
22 self.site = site;
23 self
24 }
25}
26
27impl From<TransportNode> for Site {
28 fn from(node: TransportNode) -> Self {
29 node.site
30 }
31}
32
33impl PartialOrd for TransportNode {
34 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
35 Some(self.cmp(other))
36 }
37}
38
39impl Ord for TransportNode {
40 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
41 self.site.cmp(&other.site)
42 }
43}
44
45#[derive(Debug)]
46pub enum NextTransportNode {
47 New(TransportNode),
48 Existing(NodeId),
49 Intersect(TransportNode, (NodeId, NodeId)),
50}
51
52#[derive(Debug, Clone, PartialEq)]
53pub struct PathCandidate {
54 node_start: TransportNode,
55 node_start_id: NodeId,
56 angle_expected_end: Angle,
57 stage: Stage,
58 rules: TransportRules,
59}
60
61impl Eq for PathCandidate {}
62
63impl PartialOrd for PathCandidate {
64 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
65 Some(self.cmp(other))
66 }
67}
68
69impl Ord for PathCandidate {
70 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
71 self.rules
72 .path_priority
73 .total_cmp(&other.rules.path_priority)
74 }
75}
76
77type RelatedNode<'a> = (&'a TransportNode, NodeId);
78
79impl PathCandidate {
80 pub fn new(
82 node_start: TransportNode,
83 node_start_id: NodeId,
84 angle_expected_end: Angle,
85 stage: Stage,
86 rules: TransportRules,
87 ) -> Self {
88 Self {
89 node_start,
90 node_start_id,
91 angle_expected_end,
92 stage,
93 rules,
94 }
95 }
96
97 pub fn get_node_start_id(&self) -> NodeId {
99 self.node_start_id
100 }
101
102 pub fn get_site_start(&self) -> Site {
104 self.node_start.site
105 }
106
107 pub fn angle_expected_end(&self) -> Angle {
109 self.angle_expected_end
110 }
111
112 pub fn get_rules(&self) -> &TransportRules {
114 &self.rules
115 }
116
117 pub fn get_stage(&self) -> Stage {
119 self.stage
120 }
121
122 fn get_expected_site_to_with_extra_length(&self) -> Site {
125 self.node_start.site.extend(
126 self.angle_expected_end,
127 self.rules.path_normal_length + self.rules.path_extra_length_for_intersection,
128 )
129 }
130
131 pub fn determine_next_node(
133 &self,
134 site_expected_end: Site,
135 stage: Stage,
136 related_nodes: &[RelatedNode],
137 related_paths: &[(RelatedNode, RelatedNode)],
138 ) -> NextTransportNode {
139 let search_start = self.node_start.site;
140
141 {
144 let existing_node = related_nodes
145 .iter()
146 .filter(|(existing_node, _)| {
147 LineSegment::new(search_start, site_expected_end)
148 .get_distance(&existing_node.site)
149 < self.rules.path_extra_length_for_intersection
150 })
151 .filter(|(existing_node, existing_node_id)| {
152 let has_intersection = related_paths.iter().any(|(path_start, path_end)| {
153 if *existing_node_id == path_start.1 || *existing_node_id == path_end.1 {
154 return false;
156 }
157 let path_line = LineSegment::new(path_start.0.site, path_end.0.site);
158 let search_line = LineSegment::new(search_start, existing_node.site);
159 path_line.get_intersection(&search_line).is_some()
160 });
161 !has_intersection
162 })
163 .min_by(|a, b| {
164 let distance_a = a.0.site.distance_2(&search_start);
165 let distance_b = b.0.site.distance_2(&search_start);
166 distance_a.total_cmp(&distance_b)
167 })
168 .map(|(_, node_id)| NextTransportNode::Existing(*node_id));
169
170 if let Some(existing_node) = existing_node {
171 return existing_node;
172 }
173 }
174
175 {
177 let search_end = self.get_expected_site_to_with_extra_length();
178 let search_line = LineSegment::new(search_start, search_end);
179
180 let crossing_path = related_paths
181 .iter()
182 .filter_map(|(path_start, path_end)| {
183 let path_line = LineSegment::new(path_start.0.site, path_end.0.site);
184
185 if let Some(intersect) = path_line.get_intersection(&search_line) {
186 return Some((
187 TransportNode::new(intersect, stage),
188 (path_start.1, path_end.1),
189 ));
190 }
191 None
192 })
193 .min_by(|a, b| {
194 let distance_a = a.0.site.distance_2(&search_start);
195 let distance_b = b.0.site.distance_2(&search_start);
196 distance_a.total_cmp(&distance_b)
197 })
198 .map(|(node, node_ids)| NextTransportNode::Intersect(node, node_ids));
199
200 if let Some(crossing_path) = crossing_path {
201 return crossing_path;
202 }
203 }
204
205 NextTransportNode::New(TransportNode::new(site_expected_end, stage))
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use crate::transport::rules::{BranchRules, PathDirectionRules};
214
215 use super::*;
216
217 macro_rules! assert_eq_f64 {
218 ($a:expr, $b:expr) => {
219 assert!(($a - $b).abs() < 1e-6);
220 };
221 }
222
223 #[test]
224 fn test_next_node() {
225 let nodes = vec![
226 TransportNode::default().set_site(Site::new(3.0, 0.0)),
227 TransportNode::default().set_site(Site::new(1.0, 0.0)),
228 TransportNode::default().set_site(Site::new(0.0, 1.0)),
229 TransportNode::default().set_site(Site::new(0.0, 3.0)),
230 ];
231
232 let nodes_parsed = nodes
233 .iter()
234 .enumerate()
235 .map(|(i, node)| (node, NodeId::new(i)))
236 .collect::<Vec<_>>();
237
238 let paths = vec![(0, 1), (1, 2), (2, 3)];
239
240 let paths_parsed = paths
241 .iter()
242 .map(|(start, end)| (nodes_parsed[*start], nodes_parsed[*end]))
243 .collect::<Vec<_>>();
244
245 let rules = TransportRules {
246 path_priority: 0.0,
247 elevation: 0.0,
248 population_density: 0.0,
249 path_normal_length: 1.0,
250 path_extra_length_for_intersection: 0.25,
251 branch_rules: BranchRules::default(),
252 path_direction_rules: PathDirectionRules::default(),
253 };
254
255 let (node_start, angle_expected_end) = (
256 TransportNode::default().set_site(Site::new(1.0, 1.0)),
257 Angle::new(std::f64::consts::PI * 0.75),
258 );
259 let site_expected_end = node_start
260 .site
261 .extend(angle_expected_end, rules.path_normal_length);
262
263 let static_stage = Stage::default();
264
265 let new = PathCandidate::new(
267 node_start,
268 NodeId::new(10000),
269 angle_expected_end,
270 static_stage,
271 rules.clone(),
272 )
273 .determine_next_node(
274 site_expected_end,
275 static_stage,
276 &nodes_parsed,
277 &paths_parsed,
278 );
279
280 if let NextTransportNode::New(node) = new {
281 assert_eq_f64!(
282 node.site.distance(&Site::new(
283 1.0 + 1.0 / 2.0_f64.sqrt(),
284 1.0 + 1.0 / 2.0_f64.sqrt()
285 )),
286 0.0
287 );
288 } else {
289 panic!("Unexpected node type");
290 }
291
292 let (node_start, angle_expected_end) = (
294 TransportNode::default().set_site(Site::new(1.0, 1.0)),
295 Angle::new(-std::f64::consts::PI * 0.25),
296 );
297 let site_expected_end = node_start
298 .site
299 .extend(angle_expected_end, rules.path_normal_length);
300 let intersect = PathCandidate::new(
301 node_start,
302 NodeId::new(10000),
303 angle_expected_end,
304 static_stage,
305 rules.clone(),
306 )
307 .determine_next_node(
308 site_expected_end,
309 static_stage,
310 &nodes_parsed,
311 &paths_parsed,
312 );
313
314 if let NextTransportNode::Intersect(node, _) = intersect {
315 assert_eq_f64!(node.site.distance(&Site::new(0.5, 0.5)), 0.0);
316 } else {
317 panic!("Unexpected node type");
318 }
319
320 let (node_start, angle_expected_end) = (
322 TransportNode::default().set_site(Site::new(1.0, 1.0)),
323 Angle::new(std::f64::consts::PI * 0.05),
324 );
325 let site_expected_end = node_start
326 .site
327 .extend(angle_expected_end, rules.path_normal_length);
328 let existing = PathCandidate::new(
329 node_start,
330 NodeId::new(10000),
331 angle_expected_end,
332 static_stage,
333 rules.clone(),
334 )
335 .determine_next_node(
336 site_expected_end,
337 static_stage,
338 &nodes_parsed,
339 &paths_parsed,
340 );
341
342 if let NextTransportNode::Existing(node_id) = existing {
343 assert_eq!(node_id, NodeId::new(1));
344 } else {
345 panic!("Unexpected node type");
346 }
347
348 let (node_start, angle_expected_end) = (
350 TransportNode::default().set_site(Site::new(1.0, 0.5)),
351 Angle::new(std::f64::consts::PI * 0.05),
352 );
353 let site_expected_end = node_start
354 .site
355 .extend(angle_expected_end, rules.path_normal_length);
356 let existing = PathCandidate::new(
357 node_start,
358 NodeId::new(10000),
359 angle_expected_end,
360 static_stage,
361 rules.clone(),
362 )
363 .determine_next_node(
364 site_expected_end,
365 static_stage,
366 &nodes_parsed,
367 &paths_parsed,
368 );
369
370 if let NextTransportNode::Existing(node_id) = existing {
371 assert_eq!(node_id, NodeId::new(1));
372 } else {
373 panic!("Unexpected node type");
374 }
375 }
376
377 #[test]
378 fn test_next_node_across_multiple_paths() {
379 let nodes = vec![
380 TransportNode::default().set_site(Site::new(0.0, 0.0)),
381 TransportNode::default().set_site(Site::new(0.3, 0.0)),
382 TransportNode::default().set_site(Site::new(0.7, 0.0)),
383 TransportNode::default().set_site(Site::new(1.0, 0.0)),
384 TransportNode::default().set_site(Site::new(0.0, 10.0)),
385 TransportNode::default().set_site(Site::new(0.3, 10.0)),
386 TransportNode::default().set_site(Site::new(0.7, 10.0)),
387 TransportNode::default().set_site(Site::new(1.0, 10.0)),
388 ];
389
390 let nodes_parsed = nodes
391 .iter()
392 .enumerate()
393 .map(|(i, node)| (node, NodeId::new(i)))
394 .collect::<Vec<_>>();
395
396 let paths = vec![(0, 5), (5, 2), (2, 7), (7, 3), (3, 6), (6, 1), (1, 4)];
397
398 let paths_parsed = paths
399 .iter()
400 .map(|(start, end)| (nodes_parsed[*start], nodes_parsed[*end]))
401 .collect::<Vec<_>>();
402
403 let rules = TransportRules {
404 path_priority: 0.0,
405 elevation: 0.0,
406 population_density: 0.0,
407 path_normal_length: 10000.0,
408 path_extra_length_for_intersection: 0.0,
409 branch_rules: BranchRules::default(),
410 path_direction_rules: PathDirectionRules::default(),
411 };
412
413 let (node_start, angle_expected_end) = (
414 TransportNode::default().set_site(Site::new(-1.0, 1.0)),
415 Angle::new(std::f64::consts::PI * 0.5),
416 );
417 let site_expected_end = node_start
418 .site
419 .extend(angle_expected_end, rules.path_normal_length);
420
421 let static_stage = Stage::default();
422
423 let next = PathCandidate::new(
424 node_start,
425 NodeId::new(10000),
426 angle_expected_end,
427 static_stage,
428 rules.clone(),
429 )
430 .determine_next_node(
431 site_expected_end,
432 static_stage,
433 &nodes_parsed,
434 &paths_parsed,
435 );
436
437 println!("{:?}", next);
438
439 assert!(matches!(next, NextTransportNode::Intersect(_, _)));
440 if let NextTransportNode::Intersect(node, _) = next {
441 assert!(
442 (node.site.x >= 0.0 && node.site.x <= 0.3)
443 && (node.site.y >= 0.0 && node.site.y <= 5.0)
444 );
445 } else {
446 panic!("Unexpected node type");
447 }
448 }
449}