geo/algorithm/relate/geomgraph/
topology_position.rs

1use super::{CoordPos, Direction};
2
3use std::fmt;
4
5/// A `TopologyPosition` is the labelling of a graph component's topological relationship to a
6/// single Geometry for each of the component's [`Direction`s](Direction).
7///
8/// If the graph component is an _area_ edge, there is a position for each [`Direction`]:
9/// - [`On`](Direction::On): on the edge
10/// - [`Left`](Direction::Left): left-hand side of the edge
11/// - [`Right`](Direction::Right): right-hand side
12///
13/// If the parent component is a _line_ edge or a node (a point), there is a single
14/// topological relationship attribute for the [`On`](Direction::On) position.
15///
16/// See [`CoordPos`] for the possible values.
17#[derive(Copy, Clone, PartialEq)]
18pub(crate) enum TopologyPosition {
19    Area {
20        on: Option<CoordPos>,
21        left: Option<CoordPos>,
22        right: Option<CoordPos>,
23    },
24    LineOrPoint {
25        on: Option<CoordPos>,
26    },
27}
28
29impl fmt::Debug for TopologyPosition {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        fn fmt_position(position: &Option<CoordPos>, f: &mut fmt::Formatter) -> fmt::Result {
32            match position {
33                Some(CoordPos::Inside) => write!(f, "i"),
34                Some(CoordPos::OnBoundary) => write!(f, "b"),
35                Some(CoordPos::Outside) => write!(f, "e"),
36                None => write!(f, "_"),
37            }
38        }
39        match self {
40            Self::LineOrPoint { on } => fmt_position(on, f)?,
41            Self::Area { on, left, right } => {
42                fmt_position(left, f)?;
43                fmt_position(on, f)?;
44                fmt_position(right, f)?;
45            }
46        }
47        Ok(())
48    }
49}
50
51impl TopologyPosition {
52    pub fn area(on: CoordPos, left: CoordPos, right: CoordPos) -> Self {
53        Self::Area {
54            on: Some(on),
55            left: Some(left),
56            right: Some(right),
57        }
58    }
59
60    pub fn empty_area() -> Self {
61        Self::Area {
62            on: None,
63            left: None,
64            right: None,
65        }
66    }
67
68    pub fn line_or_point(on: CoordPos) -> Self {
69        Self::LineOrPoint { on: Some(on) }
70    }
71
72    pub fn empty_line_or_point() -> Self {
73        Self::LineOrPoint { on: None }
74    }
75
76    pub fn get(&self, direction: Direction) -> Option<CoordPos> {
77        match (direction, self) {
78            (Direction::Left, Self::Area { left, .. }) => *left,
79            (Direction::Right, Self::Area { right, .. }) => *right,
80            (Direction::On, Self::LineOrPoint { on }) | (Direction::On, Self::Area { on, .. }) => {
81                *on
82            }
83            (_, Self::LineOrPoint { .. }) => {
84                panic!("LineOrPoint only has a position for `Direction::On`")
85            }
86        }
87    }
88
89    pub fn is_empty(&self) -> bool {
90        matches!(
91            self,
92            Self::LineOrPoint { on: None }
93                | Self::Area {
94                    on: None,
95                    left: None,
96                    right: None,
97                }
98        )
99    }
100
101    pub fn is_any_empty(&self) -> bool {
102        !matches!(
103            self,
104            Self::LineOrPoint { on: Some(_) }
105                | Self::Area {
106                    on: Some(_),
107                    left: Some(_),
108                    right: Some(_),
109                }
110        )
111    }
112
113    pub fn is_area(&self) -> bool {
114        matches!(self, Self::Area { .. })
115    }
116
117    pub fn is_line(&self) -> bool {
118        matches!(self, Self::LineOrPoint { .. })
119    }
120
121    pub fn flip(&mut self) {
122        match self {
123            Self::LineOrPoint { .. } => {}
124            Self::Area { left, right, .. } => {
125                std::mem::swap(left, right);
126            }
127        }
128    }
129
130    pub fn set_all_positions(&mut self, position: CoordPos) {
131        match self {
132            Self::LineOrPoint { on } => {
133                *on = Some(position);
134            }
135            Self::Area { on, left, right } => {
136                *on = Some(position);
137                *left = Some(position);
138                *right = Some(position);
139            }
140        }
141    }
142
143    pub fn set_all_positions_if_empty(&mut self, position: CoordPos) {
144        match self {
145            Self::LineOrPoint { on } => {
146                if on.is_none() {
147                    *on = Some(position);
148                }
149            }
150            Self::Area { on, left, right } => {
151                if on.is_none() {
152                    *on = Some(position);
153                }
154                if left.is_none() {
155                    *left = Some(position);
156                }
157                if right.is_none() {
158                    *right = Some(position);
159                }
160            }
161        }
162    }
163
164    pub fn set_position(&mut self, direction: Direction, position: CoordPos) {
165        match (direction, self) {
166            (Direction::On, Self::LineOrPoint { on }) => *on = Some(position),
167            (_, Self::LineOrPoint { .. }) => {
168                panic!("invalid assignment dimensions for Self::Line")
169            }
170            (Direction::On, Self::Area { on, .. }) => *on = Some(position),
171            (Direction::Left, Self::Area { left, .. }) => *left = Some(position),
172            (Direction::Right, Self::Area { right, .. }) => *right = Some(position),
173        }
174    }
175
176    pub fn set_on_position(&mut self, position: CoordPos) {
177        match self {
178            Self::LineOrPoint { on } | Self::Area { on, .. } => {
179                *on = Some(position);
180            }
181        }
182    }
183
184    pub fn set_locations(&mut self, new_on: CoordPos, new_left: CoordPos, new_right: CoordPos) {
185        match self {
186            Self::LineOrPoint { .. } => {
187                error!("invalid assignment dimensions for {self:?}");
188                debug_assert!(false, "invalid assignment dimensions for {self:?}");
189            }
190            Self::Area { on, left, right } => {
191                *on = Some(new_on);
192                *left = Some(new_left);
193                *right = Some(new_right);
194            }
195        }
196    }
197}