Skip to content

Commit

Permalink
chore: simplify isoline
Browse files Browse the repository at this point in the history
  • Loading branch information
tversteeg committed Aug 28, 2023
1 parent a3afdb4 commit 2eedbd0
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 65 deletions.
7 changes: 6 additions & 1 deletion src/gen/bitmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
ops::{Bound, Index, IndexMut, RangeBounds},
};

use bitvec::vec::BitVec;
use bitvec::{slice::BitSlice, vec::BitVec};
use vek::{Extent2, Rect, Vec2};

/// Binary 2D map.
Expand All @@ -23,6 +23,11 @@ impl Bitmap {
Self { size, map }
}

/// Create from a bitvec.
pub fn from_bitvec(map: BitVec, size: Extent2<usize>) -> Self {
Self { size, map }
}

/// Apply a removal mask to a position.
///
/// Returns a delta map of which pixels got updated the same size as the removal map.
Expand Down
81 changes: 18 additions & 63 deletions src/gen/isoline.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use std::collections::VecDeque;

use itertools::Itertools;
use vek::{Aabr, Extent2, Rect, Vec2};
use vek::{Extent2, Vec2};

use crate::physics::collision::shape::Shape;

Expand All @@ -21,8 +18,7 @@ impl Isoline {
#[must_use]
pub fn from_bitmap(bitmap: &Bitmap) -> Self {
// Create the vertices with a marching squares iterator over the bitmap
let vertices = MarchingSquaresIterator::new_find_starting_point(bitmap, [])
.map(Option::unwrap)
let vertices = MarchingSquaresIterator::new_find_starting_point(bitmap)
.map(Vec2::as_)
.collect::<Vec<_>>();

Expand All @@ -38,12 +34,11 @@ impl Isoline {
///
/// Assumes no islands exist on the bitmap.
/// If the whole shape is cleared an extra border of 1 pixel should be added to each side.
pub fn update(&mut self, bitmap: &Bitmap, delta_mask: &Bitmap, mask_position: Vec2<usize>) {
pub fn update(&mut self, bitmap: &Bitmap, _delta_mask: &Bitmap, _mask_position: Vec2<usize>) {
puffin::profile_scope!("Update isoline");

// PERF: don't do a full recalculation
let vertices = MarchingSquaresIterator::new_find_starting_point(bitmap, [])
.map(Option::unwrap)
let vertices = MarchingSquaresIterator::new_find_starting_point(bitmap)
.map(Vec2::as_)
.collect::<Vec<_>>();

Expand Down Expand Up @@ -103,13 +98,11 @@ impl Isoline {

/// Marching square walker over the source image.
#[derive(Debug, Clone)]
struct MarchingSquaresIterator<'a, const STOP_COUNT: usize> {
struct MarchingSquaresIterator<'a> {
/// Current position.
pos: Vec2<usize>,
/// Starting position.
start: Vec2<usize>,
/// Iterator fails when reaching any of these coordinates.
stop_at: [Vec2<usize>; STOP_COUNT],
/// We are done iterating.
done: bool,
/// Previous direction.
Expand All @@ -120,15 +113,11 @@ struct MarchingSquaresIterator<'a, const STOP_COUNT: usize> {
map: &'a Bitmap,
}

impl<'a, const STOP_COUNT: usize> MarchingSquaresIterator<'a, STOP_COUNT> {
impl<'a> MarchingSquaresIterator<'a> {
/// Create a new iterator walking over the source image using the marching square algorithm.
///
/// <div class='warning'>BitMap must be padded by 0 bits around the edges!</div>
pub fn new(
starting_position: Vec2<usize>,
map: &'a Bitmap,
stop_at: [Vec2<usize>; STOP_COUNT],
) -> Self {
pub fn new(starting_position: Vec2<usize>, map: &'a Bitmap) -> Self {
let pos = starting_position;
let start = pos;
// Initial value doesn't matter
Expand All @@ -140,7 +129,6 @@ impl<'a, const STOP_COUNT: usize> MarchingSquaresIterator<'a, STOP_COUNT> {
pos,
start,
prev_dir,
stop_at,
map,
}
}
Expand All @@ -150,12 +138,12 @@ impl<'a, const STOP_COUNT: usize> MarchingSquaresIterator<'a, STOP_COUNT> {
/// The starting point is found as the first bit that's set.
///
/// <div class='warning'>BitMap must be padded by 0 bits around the edges!</div>
pub fn new_find_starting_point(map: &'a Bitmap, stop_at: [Vec2<usize>; STOP_COUNT]) -> Self {
pub fn new_find_starting_point(map: &'a Bitmap) -> Self {
let starting_position = map
.first_one()
.expect("Cannot create marching squares iterator over empty map");

Self::new(starting_position, map, stop_at)
Self::new(starting_position, map)
}

/// Convert a position to a 4bit number looking at it as a 2x2 grid if possible.
Expand All @@ -181,24 +169,10 @@ impl<'a, const STOP_COUNT: usize> MarchingSquaresIterator<'a, STOP_COUNT> {

dir_number == numbers[0] || dir_number == numbers[1] || dir_number == numbers[2]
}

/// Check whether the coordinate is reached.
#[inline(always)]
fn should_stop(&mut self) -> bool {
for stop_at in self.stop_at {
if self.pos == stop_at {
self.done = true;

return true;
}
}

false
}
}

impl<'a, const STOP_COUNT: usize> Iterator for MarchingSquaresIterator<'a, STOP_COUNT> {
type Item = Option<Vec2<usize>>;
impl<'a> Iterator for MarchingSquaresIterator<'a> {
type Item = Vec2<usize>;

fn next(&mut self) -> Option<Self::Item> {
puffin::profile_scope!("Marching squares iterator step");
Expand All @@ -216,9 +190,6 @@ impl<'a, const STOP_COUNT: usize> Iterator for MarchingSquaresIterator<'a, STOP_
loop {
self.pos.y -= 1;
debug_assert!(self.pos.y > 0);
if self.should_stop() {
return Some(None);
}

if !self.is_dir_number([1, 5, 13]) {
break;
Expand All @@ -233,9 +204,6 @@ impl<'a, const STOP_COUNT: usize> Iterator for MarchingSquaresIterator<'a, STOP_
loop {
self.pos.y += 1;
debug_assert!(self.pos.y < self.map.height());
if self.should_stop() {
return Some(None);
}

if !self.is_dir_number([8, 10, 11]) {
break;
Expand All @@ -250,9 +218,6 @@ impl<'a, const STOP_COUNT: usize> Iterator for MarchingSquaresIterator<'a, STOP_
loop {
self.pos.x -= 1;
debug_assert!(self.pos.x > 0);
if self.should_stop() {
return Some(None);
}

if !self.is_dir_number([4, 12, 14]) {
break;
Expand All @@ -267,9 +232,6 @@ impl<'a, const STOP_COUNT: usize> Iterator for MarchingSquaresIterator<'a, STOP_
loop {
self.pos.x += 1;
debug_assert!(self.pos.x < self.map.width());
if self.should_stop() {
return Some(None);
}

if !self.is_dir_number([2, 3, 7]) {
break;
Expand All @@ -285,10 +247,6 @@ impl<'a, const STOP_COUNT: usize> Iterator for MarchingSquaresIterator<'a, STOP_
} else {
self.pos.y -= 1;
}

if self.should_stop() {
return Some(None);
}
}
// Right if previous was down, left if previous was up
6 => {
Expand All @@ -297,10 +255,6 @@ impl<'a, const STOP_COUNT: usize> Iterator for MarchingSquaresIterator<'a, STOP_
} else {
self.pos.x -= 1;
}

if self.should_stop() {
return Some(None);
}
}
_ => panic!("Unknown direction"),
}
Expand All @@ -310,7 +264,7 @@ impl<'a, const STOP_COUNT: usize> Iterator for MarchingSquaresIterator<'a, STOP_
self.done = true;
}

Some(Some(self.pos))
Some(self.pos)
}
}

Expand All @@ -328,12 +282,14 @@ mod tests {
use bitvec::prelude::*;
use vek::{Extent2, Vec2};

use crate::gen::bitmap::Bitmap;

use super::MarchingSquaresIterator;

#[test]
fn test_marching_cubes_iterator() {
#[rustfmt::skip]
let image: &BitSlice = bits![
let image = Bitmap::from_bitvec(bits![
0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0,
Expand All @@ -343,10 +299,9 @@ mod tests {
0, 0, 0, 1, 1, 0, 0,
0, 0, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0,
];
].to_bitvec(), Extent2::new(7, 9));

let outline: Vec<_> =
MarchingSquaresIterator::new(Vec2::new(2, 2), image, Extent2::new(7, 9)).collect();
assert_eq!(outline.len(), 19);
let outline: Vec<_> = MarchingSquaresIterator::new(Vec2::new(2, 2), &image).collect();
assert_eq!(outline.len(), 20);
}
}
2 changes: 1 addition & 1 deletion src/solid_shape.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use bitvec::{slice::BitSlice, vec::BitVec};


use vek::{Extent2, Rect, Vec2};

Expand Down

0 comments on commit 2eedbd0

Please sign in to comment.