1use crate::field::{Field, NodeId, Vec2};
2use crate::viewport::Viewport;
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum Bearing {
6 N,
7 NE,
8 E,
9 SE,
10 S,
11 SW,
12 W,
13 NW,
14}
15
16impl Bearing {
17 pub fn from_delta(d: Vec2) -> Self {
18 let a = d.y.atan2(d.x);
20
21 const PI: f32 = std::f32::consts::PI;
33 const P8: f32 = PI / 8.0;
34
35 if (-P8..=P8).contains(&a) {
36 Bearing::E
37 } else if (P8..=3.0 * P8).contains(&a) {
38 Bearing::SE
39 } else if (3.0 * P8..=5.0 * P8).contains(&a) {
40 Bearing::S
41 } else if (5.0 * P8..=7.0 * P8).contains(&a) {
42 Bearing::SW
43 } else if (-3.0 * P8..=-P8).contains(&a) {
44 Bearing::NE
45 } else if (-5.0 * P8..=-3.0 * P8).contains(&a) {
46 Bearing::N
47 } else if (-7.0 * P8..=-5.0 * P8).contains(&a) {
48 Bearing::NW
49 } else {
50 Bearing::W
51 }
52 }
53}
54
55pub fn bearings_for_visible_nodes(field: &Field, vp: &Viewport) -> Vec<(NodeId, Bearing)> {
58 field
59 .nodes()
60 .keys()
61 .copied()
62 .filter(|&id| field.participates_in_field_view(id))
63 .filter(|&id| field.is_visible(id))
64 .filter_map(|id| {
65 let n = field.node(id)?;
66 let b = bearing_to_point(vp, n.pos)?;
67 Some((id, b))
68 })
69 .collect()
70}
71
72pub fn bearings_for_anchors(field: &Field, vp: &Viewport) -> Vec<(NodeId, Bearing)> {
79 field
80 .nodes()
81 .iter()
82 .filter_map(|(&id, n)| {
83 if !field.participates_in_field_view(id) {
84 return None;
85 }
86 if !field.is_visible(id) {
87 return None;
88 }
89 if !n.anchor {
90 return None;
91 }
92 let b = bearing_to_point(vp, n.pos)?;
93 Some((id, b))
94 })
95 .collect()
96}
97
98pub fn bearing_to_point(vp: &Viewport, point: Vec2) -> Option<Bearing> {
101 let r = vp.rect();
102 if r.contains(point) {
103 return None;
104 }
105
106 let d = Vec2 {
107 x: point.x - vp.center.x,
108 y: point.y - vp.center.y,
109 };
110
111 Some(Bearing::from_delta(d))
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117 use crate::field::Vec2;
118
119 #[test]
120 fn inside_viewport_returns_none() {
121 let vp = Viewport::new(Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 100.0, y: 100.0 });
122 assert_eq!(bearing_to_point(&vp, Vec2 { x: 10.0, y: 10.0 }), None);
123 }
124
125 #[test]
126 fn cardinal_directions() {
127 let vp = Viewport::new(Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 100.0, y: 100.0 });
128
129 assert_eq!(
130 bearing_to_point(&vp, Vec2 { x: 1000.0, y: 0.0 }),
131 Some(Bearing::E)
132 );
133 assert_eq!(
134 bearing_to_point(&vp, Vec2 { x: -1000.0, y: 0.0 }),
135 Some(Bearing::W)
136 );
137 assert_eq!(
138 bearing_to_point(&vp, Vec2 { x: 0.0, y: 1000.0 }),
139 Some(Bearing::S)
140 );
141 assert_eq!(
142 bearing_to_point(&vp, Vec2 { x: 0.0, y: -1000.0 }),
143 Some(Bearing::N)
144 );
145 }
146
147 #[test]
148 fn diagonal_directions() {
149 let vp = Viewport::new(Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 100.0, y: 100.0 });
150
151 assert_eq!(
152 bearing_to_point(
153 &vp,
154 Vec2 {
155 x: 1000.0,
156 y: 1000.0
157 }
158 ),
159 Some(Bearing::SE)
160 );
161 assert_eq!(
162 bearing_to_point(
163 &vp,
164 Vec2 {
165 x: -1000.0,
166 y: 1000.0
167 }
168 ),
169 Some(Bearing::SW)
170 );
171 assert_eq!(
172 bearing_to_point(
173 &vp,
174 Vec2 {
175 x: 1000.0,
176 y: -1000.0
177 }
178 ),
179 Some(Bearing::NE)
180 );
181 assert_eq!(
182 bearing_to_point(
183 &vp,
184 Vec2 {
185 x: -1000.0,
186 y: -1000.0
187 }
188 ),
189 Some(Bearing::NW)
190 );
191 }
192
193 #[test]
194 fn bearings_skip_hidden_nodes() {
195 use crate::field::{Field, Vec2};
196
197 let mut field = Field::new();
198 let a = field.spawn_surface("A", Vec2 { x: 1000.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
199 let b = field.spawn_surface("B", Vec2 { x: -1000.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
200
201 assert!(field.set_hidden(b, true));
202
203 let vp = Viewport::new(Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 100.0, y: 100.0 });
204
205 let bs = bearings_for_visible_nodes(&field, &vp);
206
207 assert_eq!(bs.len(), 1);
208 assert_eq!(bs[0].0, a);
209 assert_eq!(bs[0].1, Bearing::E);
210 }
211
212 #[test]
213 fn bearings_for_anchors_only_includes_anchors() {
214 use crate::field::{Field, Vec2};
215
216 let mut field = Field::new();
217 let a = field.spawn_surface("A", Vec2 { x: 1000.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
218 let b = field.spawn_surface("B", Vec2 { x: -1000.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
219
220 assert!(field.set_anchor(b, true));
221
222 let vp = Viewport::new(Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 100.0, y: 100.0 });
223
224 let bs = bearings_for_anchors(&field, &vp);
225
226 assert_eq!(bs.len(), 1);
227 assert_eq!(bs[0].0, b);
228 assert_eq!(bs[0].1, Bearing::W);
229
230 assert_ne!(a, b);
232 }
233
234 #[test]
235 fn bearings_for_anchors_skips_hidden_anchors() {
236 use crate::field::{Field, Vec2};
237
238 let mut field = Field::new();
239 let a = field.spawn_surface("A", Vec2 { x: 1000.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
240
241 assert!(field.set_anchor(a, true));
242 assert!(field.set_hidden(a, true));
243
244 let vp = Viewport::new(Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 100.0, y: 100.0 });
245
246 let bs = bearings_for_anchors(&field, &vp);
247 assert!(bs.is_empty());
248 }
249}