street_engine/transport/
node.rs

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    /// Create a new path candidate.
81    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    /// Get node id
98    pub fn get_node_start_id(&self) -> NodeId {
99        self.node_start_id
100    }
101
102    /// Get the start site of the path.
103    pub fn get_site_start(&self) -> Site {
104        self.node_start.site
105    }
106
107    /// Get the end site of the path.
108    pub fn angle_expected_end(&self) -> Angle {
109        self.angle_expected_end
110    }
111
112    /// Get rules of the path.
113    pub fn get_rules(&self) -> &TransportRules {
114        &self.rules
115    }
116
117    /// Get stage of the path.
118    pub fn get_stage(&self) -> Stage {
119        self.stage
120    }
121
122    /// Get the end site of the path with extra length.
123    /// This is temporary used for searching intersections.
124    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    /// Determine the next node type from related(close) nodes and paths.
132    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        // Existing Node
142        // For this situation, path crosses are needed to be checked again because the direction of the path can be changed from original.
143        {
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                            // ignore
155                            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        // Crossing Paths
176        {
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        // New Node
206        // Path crosses are already checked in the previous steps.
207        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        // New node
266        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        // Intersect (Crossing Path)
293        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        // Existing node (close between two nodes)
321        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        // Existing node (close between an existing node and expected path)
349        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}