use crate::field::{Field, NodeId, Vec2};
use crate::viewport::Viewport;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Bearing {
N,
NE,
E,
SE,
S,
SW,
W,
NW,
}
impl Bearing {
pub fn from_delta(d: Vec2) -> Self {
let a = d.y.atan2(d.x);
const PI: f32 = std::f32::consts::PI;
const P8: f32 = PI / 8.0;
if (-P8..=P8).contains(&a) {
Bearing::E
} else if (P8..=3.0 * P8).contains(&a) {
Bearing::SE
} else if (3.0 * P8..=5.0 * P8).contains(&a) {
Bearing::S
} else if (5.0 * P8..=7.0 * P8).contains(&a) {
Bearing::SW
} else if (-3.0 * P8..=-P8).contains(&a) {
Bearing::NE
} else if (-5.0 * P8..=-3.0 * P8).contains(&a) {
Bearing::N
} else if (-7.0 * P8..=-5.0 * P8).contains(&a) {
Bearing::NW
} else {
Bearing::W
}
}
}
pub fn bearings_for_visible_nodes(field: &Field, vp: &Viewport) -> Vec<(NodeId, Bearing)> {
field
.nodes()
.keys()
.copied()
.filter(|&id| field.participates_in_field_view(id))
.filter(|&id| field.is_visible(id))
.filter_map(|id| {
let n = field.node(id)?;
let b = bearing_to_point(vp, n.pos)?;
Some((id, b))
})
.collect()
}
pub fn bearings_for_anchors(field: &Field, vp: &Viewport) -> Vec<(NodeId, Bearing)> {
field
.nodes()
.iter()
.filter_map(|(&id, n)| {
if !field.participates_in_field_view(id) {
return None;
}
if !field.is_visible(id) {
return None;
}
if !n.anchor {
return None;
}
let b = bearing_to_point(vp, n.pos)?;
Some((id, b))
})
.collect()
}
pub fn bearing_to_point(vp: &Viewport, point: Vec2) -> Option<Bearing> {
let r = vp.rect();
if r.contains(point) {
return None;
}
let d = Vec2 {
x: point.x - vp.center.x,
y: point.y - vp.center.y,
};
Some(Bearing::from_delta(d))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::field::Vec2;
#[test]
fn inside_viewport_returns_none() {
let vp = Viewport::new(Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 100.0, y: 100.0 });
assert_eq!(bearing_to_point(&vp, Vec2 { x: 10.0, y: 10.0 }), None);
}
#[test]
fn cardinal_directions() {
let vp = Viewport::new(Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 100.0, y: 100.0 });
assert_eq!(
bearing_to_point(&vp, Vec2 { x: 1000.0, y: 0.0 }),
Some(Bearing::E)
);
assert_eq!(
bearing_to_point(&vp, Vec2 { x: -1000.0, y: 0.0 }),
Some(Bearing::W)
);
assert_eq!(
bearing_to_point(&vp, Vec2 { x: 0.0, y: 1000.0 }),
Some(Bearing::S)
);
assert_eq!(
bearing_to_point(&vp, Vec2 { x: 0.0, y: -1000.0 }),
Some(Bearing::N)
);
}
#[test]
fn diagonal_directions() {
let vp = Viewport::new(Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 100.0, y: 100.0 });
assert_eq!(
bearing_to_point(
&vp,
Vec2 {
x: 1000.0,
y: 1000.0
}
),
Some(Bearing::SE)
);
assert_eq!(
bearing_to_point(
&vp,
Vec2 {
x: -1000.0,
y: 1000.0
}
),
Some(Bearing::SW)
);
assert_eq!(
bearing_to_point(
&vp,
Vec2 {
x: 1000.0,
y: -1000.0
}
),
Some(Bearing::NE)
);
assert_eq!(
bearing_to_point(
&vp,
Vec2 {
x: -1000.0,
y: -1000.0
}
),
Some(Bearing::NW)
);
}
#[test]
fn bearings_skip_hidden_nodes() {
use crate::field::{Field, Vec2};
let mut field = Field::new();
let a = field.spawn_surface("A", Vec2 { x: 1000.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
let b = field.spawn_surface("B", Vec2 { x: -1000.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
assert!(field.set_hidden(b, true));
let vp = Viewport::new(Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 100.0, y: 100.0 });
let bs = bearings_for_visible_nodes(&field, &vp);
assert_eq!(bs.len(), 1);
assert_eq!(bs[0].0, a);
assert_eq!(bs[0].1, Bearing::E);
}
#[test]
fn bearings_for_anchors_only_includes_anchors() {
use crate::field::{Field, Vec2};
let mut field = Field::new();
let a = field.spawn_surface("A", Vec2 { x: 1000.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
let b = field.spawn_surface("B", Vec2 { x: -1000.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
assert!(field.set_anchor(b, true));
let vp = Viewport::new(Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 100.0, y: 100.0 });
let bs = bearings_for_anchors(&field, &vp);
assert_eq!(bs.len(), 1);
assert_eq!(bs[0].0, b);
assert_eq!(bs[0].1, Bearing::W);
assert_ne!(a, b);
}
#[test]
fn bearings_for_anchors_skips_hidden_anchors() {
use crate::field::{Field, Vec2};
let mut field = Field::new();
let a = field.spawn_surface("A", Vec2 { x: 1000.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
assert!(field.set_anchor(a, true));
assert!(field.set_hidden(a, true));
let vp = Viewport::new(Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 100.0, y: 100.0 });
let bs = bearings_for_anchors(&field, &vp);
assert!(bs.is_empty());
}
}