|
@@ -91,16 +91,11 @@ impl ShadowLine {
|
|
|
}
|
|
|
|
|
|
fn add(&mut self, other: Shadow) {
|
|
|
- let index = {
|
|
|
- let mut index = 0;
|
|
|
- while index < self.shadows.len() {
|
|
|
- if self.shadows[index].start >= other.start {
|
|
|
- break;
|
|
|
- }
|
|
|
- index += 1;
|
|
|
- }
|
|
|
- index
|
|
|
- };
|
|
|
+ let index = self
|
|
|
+ .shadows
|
|
|
+ .iter()
|
|
|
+ .position(|sh| sh.start >= other.start)
|
|
|
+ .unwrap_or(self.shadows.len());
|
|
|
|
|
|
// find whether there's an overlapping previous and next
|
|
|
// shadow segment
|
|
@@ -114,27 +109,42 @@ impl ShadowLine {
|
|
|
.get(index - 1)
|
|
|
.filter(|sh| sh.end > other.start)
|
|
|
};
|
|
|
- let next = if index < self.shadows.len() {
|
|
|
- None
|
|
|
- } else {
|
|
|
- self.shadows.get(index).filter(|sh| sh.start < other.end)
|
|
|
- };
|
|
|
+ let next = self.shadows.get(index).filter(|sh| sh.start < other.end);
|
|
|
|
|
|
match (previous, next) {
|
|
|
- // two overlapping segments: join them together
|
|
|
+ // 1 2 3 4 5 6 7 1 2 3 4 5 6 7
|
|
|
+ // [prev] [next] => [prev.......]
|
|
|
+ // [other]
|
|
|
+ //
|
|
|
+ // two overlapping segments: join them together,
|
|
|
+ // specifically extending the previous one and deleting
|
|
|
+ // the second
|
|
|
(Some(_), Some(n)) => {
|
|
|
self.shadows[index - 1].end = n.end;
|
|
|
self.shadows.remove(index);
|
|
|
}
|
|
|
- // just one overlapping segment: extend the segment in the
|
|
|
- // appropriate direction
|
|
|
+ // 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
|
|
|
+ // [prev] [next] => [prev] [next...]
|
|
|
+ // [other]
|
|
|
+ //
|
|
|
+ // just overlapping a later segment: pull the later
|
|
|
+ // segment's start point earlier
|
|
|
(None, Some(_)) => {
|
|
|
self.shadows[index].start = other.start;
|
|
|
}
|
|
|
+ // 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
|
|
|
+ // [prev] [next] => [prev...] [next]
|
|
|
+ // [other]
|
|
|
+ //
|
|
|
+ // just overlapping an earlier segment: pull the earlier
|
|
|
+ // segment's end point later
|
|
|
(Some(_), None) => {
|
|
|
self.shadows[index - 1].end = other.end;
|
|
|
}
|
|
|
- // no overlapping segments: add this one
|
|
|
+ // 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
|
|
|
+ // [p] [n] => [p] [other] [n]
|
|
|
+ // [other]
|
|
|
+ // no overlapping segments: just add this one in between
|
|
|
(None, None) => {
|
|
|
self.shadows.insert(index, other);
|
|
|
}
|
|
@@ -145,15 +155,19 @@ impl ShadowLine {
|
|
|
pub struct Viewshed<T> {
|
|
|
pub vis: Board<bool>,
|
|
|
blocking: Box<fn(&T) -> bool>,
|
|
|
+ pub range: Option<usize>,
|
|
|
}
|
|
|
|
|
|
impl<T> Viewshed<T> {
|
|
|
pub fn create(original: &Board<T>, blocking: fn(&T) -> bool) -> Viewshed<T> {
|
|
|
- let vis = Board::new_from(original.width(), original.height(), |_, _| {
|
|
|
- false
|
|
|
- });
|
|
|
+ let vis = Board::new_from(original.width(), original.height(), |_, _| false);
|
|
|
let blocking = Box::new(blocking);
|
|
|
- Viewshed { vis, blocking }
|
|
|
+ let range = None;
|
|
|
+ Viewshed {
|
|
|
+ vis,
|
|
|
+ blocking,
|
|
|
+ range,
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
pub fn visibility(&self, coord: impl Into<Coord>) -> bool {
|
|
@@ -200,9 +214,16 @@ impl<T> Viewshed<T> {
|
|
|
let visible = !line.in_shadow(&projection);
|
|
|
self.vis[pos] = visible;
|
|
|
|
|
|
- if visible && (self.blocking)(&board[pos]) {
|
|
|
- line.add(projection);
|
|
|
- full_shadow = line.is_full_shadow();
|
|
|
+ if visible {
|
|
|
+ let out_of_range = if let Some(r) = self.range {
|
|
|
+ ((row.pow(2) + col.pow(2)) as f32).sqrt() >= r as f32
|
|
|
+ } else {
|
|
|
+ false
|
|
|
+ };
|
|
|
+ if out_of_range || (self.blocking)(&board[pos]) {
|
|
|
+ line.add(projection);
|
|
|
+ full_shadow = line.is_full_shadow();
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -230,9 +251,13 @@ mod test {
|
|
|
const V: bool = true;
|
|
|
const U: bool = false;
|
|
|
|
|
|
- fn assert_same_vis(exp: &Board<bool>, actual: &Board<bool>) {
|
|
|
+ fn assert_same_vis(p: Coord, exp: &Board<bool>, actual: &Board<bool>) {
|
|
|
if exp != actual {
|
|
|
- panic!("Expected:\n{}\n========\nActual:\n{}\n", to_vis(exp), to_vis(actual));
|
|
|
+ panic!(
|
|
|
+ "Expected:\n{}\n========\nActual:\n{}\n",
|
|
|
+ to_vis(p, exp),
|
|
|
+ to_vis(p, actual)
|
|
|
+ );
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -289,14 +314,36 @@ mod test {
|
|
|
assert_eq!(line.shadows[1], super::Shadow::new(0.9, 1.0));
|
|
|
}
|
|
|
|
|
|
- fn to_vis(vis: &super::Board<bool>) -> String {
|
|
|
+ #[test]
|
|
|
+ fn add_shadow_line_several() {
|
|
|
+ let mut line = super::ShadowLine {
|
|
|
+ shadows: Vec::new(),
|
|
|
+ };
|
|
|
+
|
|
|
+ assert_eq!(line.shadows.len(), 0);
|
|
|
+
|
|
|
+ line.add(super::Shadow::new(0.5, 0.8));
|
|
|
+ assert_eq!(line.shadows.len(), 1);
|
|
|
+
|
|
|
+ line.add(super::Shadow::new(0.0, 0.667));
|
|
|
+ assert_eq!(line.shadows.len(), 1);
|
|
|
+
|
|
|
+ line.add(super::Shadow::new(0.6, 1.0));
|
|
|
+ assert_eq!(line.shadows.len(), 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ fn to_vis(p: Coord, vis: &super::Board<bool>) -> String {
|
|
|
let mut buf = String::new();
|
|
|
for y in 0..vis.height() {
|
|
|
for x in 0..vis.width() {
|
|
|
- buf.push(match vis[(x, y)] {
|
|
|
- true => '#',
|
|
|
- false => '.',
|
|
|
- })
|
|
|
+ if p.x == x && p.y == y {
|
|
|
+ buf.push('@')
|
|
|
+ } else {
|
|
|
+ buf.push(match vis[(x, y)] {
|
|
|
+ true => '#',
|
|
|
+ false => '.',
|
|
|
+ })
|
|
|
+ }
|
|
|
}
|
|
|
buf.push('\n')
|
|
|
}
|
|
@@ -316,7 +363,8 @@ mod test {
|
|
|
]
|
|
|
];
|
|
|
let mut v = Viewshed::create(&b, |n| *n == 1);
|
|
|
- v.calculate_from(&b, Coord::new(2, 2));
|
|
|
+ let p = Coord::new(2, 2);
|
|
|
+ v.calculate_from(&b, p);
|
|
|
let exp: Board<bool> = board_from_vec![
|
|
|
5,5;
|
|
|
[
|
|
@@ -327,7 +375,7 @@ mod test {
|
|
|
U, U, U, U, U,
|
|
|
]
|
|
|
];
|
|
|
- assert_same_vis(&exp, &v.vis);
|
|
|
+ assert_same_vis(p, &exp, &v.vis);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
@@ -345,7 +393,8 @@ mod test {
|
|
|
]
|
|
|
];
|
|
|
let mut v = Viewshed::create(&b, |n| *n == 1);
|
|
|
- v.calculate_from(&b, Coord::new(3, 3));
|
|
|
+ let p = Coord::new(3, 3);
|
|
|
+ v.calculate_from(&b, p);
|
|
|
let exp: Board<bool> = board_from_vec![
|
|
|
7,7;
|
|
|
[
|
|
@@ -358,7 +407,7 @@ mod test {
|
|
|
U, U, U, U, U, U, U,
|
|
|
]
|
|
|
];
|
|
|
- assert_same_vis(&exp, &v.vis);
|
|
|
+ assert_same_vis(p, &exp, &v.vis);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
@@ -376,7 +425,8 @@ mod test {
|
|
|
]
|
|
|
];
|
|
|
let mut v = Viewshed::create(&b, |n| *n == 1);
|
|
|
- v.calculate_from(&b, Coord::new(3, 3));
|
|
|
+ let p = Coord::new(3, 3);
|
|
|
+ v.calculate_from(&b, p);
|
|
|
let exp: Board<bool> = board_from_vec![
|
|
|
13,7;
|
|
|
[
|
|
@@ -389,7 +439,7 @@ mod test {
|
|
|
U, U, U, U, U, U, U, U, U, U, U, U, U,
|
|
|
]
|
|
|
];
|
|
|
- assert_same_vis(&exp, &v.vis);
|
|
|
+ assert_same_vis(p, &exp, &v.vis);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
@@ -406,7 +456,8 @@ mod test {
|
|
|
];
|
|
|
|
|
|
let mut v = Viewshed::create(&b, |n| *n == 1);
|
|
|
- v.calculate_from(&b, Coord::new(2, 2));
|
|
|
+ let p = Coord::new(2, 2);
|
|
|
+ v.calculate_from(&b, p);
|
|
|
let exp: Board<bool> = board_from_vec![
|
|
|
7,5;
|
|
|
[
|
|
@@ -417,9 +468,10 @@ mod test {
|
|
|
U, U, U, U, U, U, U,
|
|
|
]
|
|
|
];
|
|
|
- assert_same_vis(&exp, &v.vis);
|
|
|
+ assert_same_vis(p, &exp, &v.vis);
|
|
|
|
|
|
- v.calculate_from(&b, Coord::new(4, 2));
|
|
|
+ let p = Coord::new(4, 2);
|
|
|
+ v.calculate_from(&b, p);
|
|
|
let exp: Board<bool> = board_from_vec![
|
|
|
7,5;
|
|
|
[
|
|
@@ -430,7 +482,7 @@ mod test {
|
|
|
U, U, U, U, U, U, U,
|
|
|
]
|
|
|
];
|
|
|
- assert_same_vis(&exp, &v.vis);
|
|
|
+ assert_same_vis(p, &exp, &v.vis);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
@@ -449,7 +501,8 @@ mod test {
|
|
|
];
|
|
|
|
|
|
let mut v = Viewshed::create(&b, |n| *n == 1);
|
|
|
- v.calculate_from(&b, Coord::new(2, 2));
|
|
|
+ let p = Coord::new(2, 2);
|
|
|
+ v.calculate_from(&b, p);
|
|
|
let exp: Board<bool> = board_from_vec![
|
|
|
9,7;
|
|
|
[
|
|
@@ -462,6 +515,6 @@ mod test {
|
|
|
U, U, U, U, U, U, U, U, U,
|
|
|
]
|
|
|
];
|
|
|
- assert_same_vis(&exp, &v.vis);
|
|
|
+ assert_same_vis(p, &exp, &v.vis);
|
|
|
}
|
|
|
}
|