diff --git a/Cargo.toml b/Cargo.toml index 68530ea..175c662 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["bevy", "rapier", "png", "collider", "2d"] readme = "README.md" [features] -default = ["xpbd_2d","rapier2d"] +default = ["xpbd_2d", "rapier2d"] xpbd_2d = ["dep:bevy_xpbd_2d"] rapier2d = ["dep:bevy_rapier2d"] @@ -20,6 +20,8 @@ rapier2d = ["dep:bevy_rapier2d"] bevy = "0.13.0" bevy_rapier2d = { version = "0.25.0", optional = true } bevy_xpbd_2d = { version = "0.4.2", optional = true } +edges = { version = "0.3.0", features = ["bevy"] } +thiserror = "1.0.57" [dev-dependencies] bevy_prototype_lyon = "0.11.0" diff --git a/README.md b/README.md index c5e0db3..d277cef 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ but you'll probably only want to just use one of the physics engines supported s ```toml [dependencies.bevy_collider_gen] +# replace "*" with the most recent version of bevy_collider_gen version = "*" features = ["rapier2d"] default-features = false @@ -23,6 +24,7 @@ or this for `bevy_xpbd_2d` ```toml [dependencies.bevy_collider_gen] +# replace "*" with the most recent version of bevy_collider_gen version = "*" features = ["xpbd_2d"] default-features = false @@ -60,19 +62,11 @@ packaged up my approach here in case anyone else could benefit. ## how it works -i was inspired by [a coding train (or, coding in the cabana rather) on an implementation of "marching squares"](). -so this crate takes a "march through all the values" approach to find edges, i.e. pixels with at least 1 empty neighboring pixel, but -instead of drawing a contour in place, it just keeps track of all the actual pixel coordinates. to determine "empty" I bitwise -or all the bytes for each pixel and, in images with transparency, "empty" is a zero value for the pixel. - -after that, we need to put the coordinates in some kind of "drawing order" so whatever we pass all the points to, knows how we want the object constructed. for this, the -crate collects all pixels, in order, that are a distance of 1 from eachother. if there are pixels that have a distance greater than 1 -from any pixel in an existing group, that pixel begins a new group. +😄 head on over to the edges crate to learn more ## caveats - as mentioned here and there in these docs, this implementation requires images to have transparency in order to distinguish object from non-object :) -- there's no reason we couldn't generate colliders / geometry without transparency, it's just not implemented. if you've got a compelling case, raise an issue! or even better, create a pr! - i imagine for generating things at a larger scale, i.e. colliders for sets of sprites bigger than pixel counts in the hundreds, this implementation won't be performant to do at runtime. i'll suggest serializing the colliders you like and deserializing in your app instead of doing all the number crunching on load when you need a performance boost ## examples of colliders generated for assets/sprite/car.png @@ -105,7 +99,8 @@ convex decomposition colliders you could construct them with the edge coordinate ```rust let sprite_image = image_assets.get(sprite_handle.unwrap()).unwrap(); -let edge_coordinate_groups = multi_image_edge_translated(sprite_image); +let edges = Edges::from(sprite_image) +let edge_coordinate_groups = edges.multi_image_edge_translated(); for coords in edge_coordinate_groups { let indices: Vec<[u32; 2]> = (0..coords.len()).map(|i| [i as u32, i as u32]).collect(); let collider = Collider::convex_decomposition(&coords, &indices); @@ -120,7 +115,7 @@ for coords in edge_coordinate_groups { } ``` -![convex decomposition collider on an upside down car sprite]() +![convex decomposition collider on a car sprite]() ## license diff --git a/examples/rapier2d_colliders.rs b/examples/rapier2d_colliders.rs index 3c57a3b..c226ffd 100644 --- a/examples/rapier2d_colliders.rs +++ b/examples/rapier2d_colliders.rs @@ -3,10 +3,12 @@ use bevy::pbr::wireframe::WireframePlugin; use bevy::prelude::*; use bevy::render::settings::{RenderCreation, WgpuFeatures, WgpuSettings}; use bevy::render::RenderPlugin; -use bevy_collider_gen::multi_image_edge_translated; -use bevy_collider_gen::rapier2d::{ - multi_convex_polyline_collider_translated, single_convex_polyline_collider_translated, - single_heightfield_collider_translated, +use bevy_collider_gen::{ + rapier2d::{ + multi_convex_polyline_collider_translated, single_convex_polyline_collider_translated, + single_heightfield_collider_translated, + }, + Edges, }; use bevy_prototype_lyon::prelude::{Fill, GeometryBuilder, ShapePlugin}; use bevy_prototype_lyon::shapes; @@ -137,7 +139,8 @@ pub fn boulders_spawn( } let sprite_image = image_assets.get(sprite_handle.unwrap()).unwrap(); - let coord_group = multi_image_edge_translated(sprite_image); + let edges = Edges::from(sprite_image); + let coord_group = edges.multi_image_edge_translated(); let colliders = multi_convex_polyline_collider_translated(sprite_image); for (coords, collider) in coord_group.iter().zip(colliders.into_iter()) { diff --git a/examples/xpbd_2d_colliders.rs b/examples/xpbd_2d_colliders.rs index 96ebdae..fc0cf4b 100644 --- a/examples/xpbd_2d_colliders.rs +++ b/examples/xpbd_2d_colliders.rs @@ -3,10 +3,12 @@ use bevy::pbr::wireframe::WireframePlugin; use bevy::prelude::*; use bevy::render::settings::{RenderCreation, WgpuFeatures, WgpuSettings}; use bevy::render::RenderPlugin; -use bevy_collider_gen::multi_image_edge_translated; -use bevy_collider_gen::xpbd_2d::{ - multi_convex_polyline_collider_translated, single_convex_polyline_collider_translated, - single_heightfield_collider_translated, +use bevy_collider_gen::{ + xpbd_2d::{ + multi_convex_polyline_collider_translated, single_convex_polyline_collider_translated, + single_heightfield_collider_translated, + }, + Edges, }; use bevy_prototype_lyon::prelude::{Fill, GeometryBuilder, ShapePlugin}; use bevy_prototype_lyon::shapes; @@ -126,7 +128,8 @@ pub fn boulders_spawn( } let sprite_image = image_assets.get(sprite_handle.unwrap()).unwrap(); - let coord_group = multi_image_edge_translated(sprite_image); + let edges = Edges::from(sprite_image); + let coord_group = edges.multi_image_edge_translated(); let colliders = multi_convex_polyline_collider_translated(sprite_image); for (coords, collider) in coord_group.iter().zip(colliders.into_iter()) { diff --git a/src/collider/rapier2d.rs b/src/collider/rapier2d.rs index e906d98..e89404a 100644 --- a/src/collider/rapier2d.rs +++ b/src/collider/rapier2d.rs @@ -1,82 +1,90 @@ -use crate::{ - multi_image_edge_translated, multi_image_edges_raw, single_image_edge_raw, - single_image_edge_translated, -}; use bevy::prelude::{Image, Vec2}; use bevy_rapier2d::prelude::{Collider, Real}; +use edges::Edges; /// Generate a single bevy_rapier2d polyline collider from the image, /// coordinates translated to either side of (0, 0) pub fn single_polyline_collider_translated(image: &Image) -> Collider { - Collider::polyline(single_image_edge_translated(image), None) + let e = Edges::from(image); + Collider::polyline(e.single_image_edge_translated(), None) } /// Generate a single bevy_rapier2d polyline collider from the image, /// coordinates left alone and all in positive x and y pub fn single_polyline_collider_raw(image: &Image) -> Collider { - Collider::polyline(single_image_edge_raw(image), None) + let e = Edges::from(image); + Collider::polyline(e.single_image_edge_raw(), None) } /// Generate a single bevy_rapier2d convex_polyline collider from the image, /// coordinates translated to either side of (0, 0) pub fn single_convex_polyline_collider_translated(image: &Image) -> Option { - Collider::convex_polyline(single_image_edge_translated(image)) + let e = Edges::from(image); + Collider::convex_polyline(e.single_image_edge_translated()) } /// Generate a single bevy_rapier2d convex_polyline collider from the image, /// coordinates left alone and all in positive x and y pub fn single_convex_polyline_collider_raw(image: &Image) -> Option { - Collider::convex_polyline(single_image_edge_raw(image)) + let e = Edges::from(image); + Collider::convex_polyline(e.single_image_edge_raw()) } /// Generate a single bevy_rapier2d convex_hull collider from the image, /// coordinates translated to either side of (0, 0) pub fn single_convex_hull_collider_translated(image: &Image) -> Option { - let points = single_image_edge_translated(image); + let e = Edges::from(image); + let points = e.single_image_edge_translated(); Collider::convex_hull(&points) } /// Generate a single bevy_rapier2d convex_hull collider from the image, /// coordinates left alone and all in positive x and y pub fn single_convex_hull_collider_raw(image: &Image) -> Option { - let points = single_image_edge_translated(image); + let e = Edges::from(image); + let points = e.single_image_edge_translated(); Collider::convex_hull(&points) } /// Generate a single bevy_rapier2d heightfield collider from the image, /// coordinates translated to either side of (0, 0) pub fn single_heightfield_collider_translated(image: &Image) -> Collider { - heightfield_collider_from_points(&single_image_edge_translated(image)) + let e = Edges::from(image); + heightfield_collider_from_points(&e.single_image_edge_translated()) } /// Generate a single bevy_rapier2d heightfield collider from the image, /// coordinates left alone and all in positive x and y pub fn single_heightfield_collider_raw(image: &Image) -> Collider { - heightfield_collider_from_points(&single_image_edge_raw(image)) + let e = Edges::from(image); + heightfield_collider_from_points(&e.single_image_edge_raw()) } /// Generate as many bevy_rapier2d polyline colliders as it can find in the image, /// coordinates translated to either side of (0, 0) pub fn multi_polyline_collider_translated(image: &Image) -> Vec { - multi_image_edge_translated(image) + let e = Edges::from(image); + e.multi_image_edge_translated() .into_iter() - .map(|e| Collider::polyline(e, None)) + .map(|v| Collider::polyline(v, None)) .collect() } /// Generate as many bevy_rapier2d polyline colliders as it can find in the image, /// coordinates left alone and all in positive x and y pub fn multi_polyline_collider_raw(image: &Image) -> Vec { - multi_image_edges_raw(image) + let e = Edges::from(image); + e.multi_image_edges_raw() .into_iter() - .map(|e| Collider::polyline(e, None)) + .map(|v| Collider::polyline(v, None)) .collect() } /// Generate as many bevy_rapier2d convex_polyline colliders as it can find in the image, /// coordinates translated to either side of (0, 0) pub fn multi_convex_polyline_collider_translated(image: &Image) -> Vec> { - multi_image_edge_translated(image) + let e = Edges::from(image); + e.multi_image_edge_translated() .into_iter() .map(Collider::convex_polyline) .collect() @@ -85,7 +93,8 @@ pub fn multi_convex_polyline_collider_translated(image: &Image) -> Vec Vec> { - multi_image_edges_raw(image) + let e = Edges::from(image); + e.multi_image_edges_raw() .into_iter() .map(Collider::convex_polyline) .collect() @@ -94,36 +103,40 @@ pub fn multi_convex_polyline_collider_raw(image: &Image) -> Vec /// Generate as many bevy_rapier2d heightfield colliders as it can find in the image, /// coordinates translated to either side of (0, 0) pub fn multi_heightfield_collider_translated(image: &Image) -> Vec { - multi_image_edge_translated(image) + let e = Edges::from(image); + e.multi_image_edge_translated() .into_iter() - .map(|e| heightfield_collider_from_points(&e)) + .map(|v| heightfield_collider_from_points(&v)) .collect() } /// Generate as many bevy_rapier2d heightfield colliders as it can find in the image, /// coordinates left alone and all in positive x and y pub fn multi_heightfield_collider_raw(image: &Image) -> Vec { - multi_image_edges_raw(image) + let e = Edges::from(image); + e.multi_image_edges_raw() .into_iter() - .map(|e| heightfield_collider_from_points(&e)) + .map(|v| heightfield_collider_from_points(&v)) .collect() } /// Generate as many bevy_rapier2d convex_hull colliders as it can find in the image, /// coordinates translated to either side of (0, 0) pub fn multi_convex_hull_collider_translated(image: &Image) -> Vec> { - multi_image_edge_translated(image) + let e = Edges::from(image); + e.multi_image_edge_translated() .into_iter() - .map(|e| Collider::convex_hull(&e)) + .map(|v| Collider::convex_hull(&v)) .collect() } /// Generate as many bevy_rapier2d convex_hull colliders as it can find in the image, /// coordinates left alone and all in positive x and y pub fn multi_convex_hull_collider_raw(image: &Image) -> Vec> { - multi_image_edges_raw(image) + let e = Edges::from(image); + e.multi_image_edges_raw() .into_iter() - .map(|e| Collider::convex_hull(&e)) + .map(|v| Collider::convex_hull(&v)) .collect() } @@ -151,5 +164,5 @@ fn heights_from_points(points: &[Vec2]) -> Vec { } } - heights.iter().map(|e| e.y).collect::>() + heights.iter().map(|v| v.y).collect::>() } diff --git a/src/collider/xpbd_2d.rs b/src/collider/xpbd_2d.rs index 0bd4514..a744a06 100644 --- a/src/collider/xpbd_2d.rs +++ b/src/collider/xpbd_2d.rs @@ -1,7 +1,3 @@ -use crate::{ - multi_image_edge_translated, multi_image_edges_raw, single_image_edge_raw, - single_image_edge_translated, -}; use bevy::prelude::{Image, Vec2}; use bevy_xpbd_2d::{ math::Vector2, @@ -12,23 +8,28 @@ use bevy_xpbd_2d::{ }, prelude::Collider, }; +use edges::Edges; /// Generate a single polyline collider from the image, /// coordinates translated to either side of (0, 0) pub fn single_polyline_collider_translated(image: &Image) -> Collider { - Collider::polyline(single_image_edge_translated(image), None) + let e = Edges::from(image); + Collider::polyline(e.single_image_edge_translated(), None) } /// Generate a single polyline collider from the image, /// coordinates left alone and all in positive x and y pub fn single_polyline_collider_raw(image: &Image) -> Collider { - Collider::polyline(single_image_edge_raw(image), None) + let e = Edges::from(image); + Collider::polyline(e.single_image_edge_raw(), None) } /// Generate a single convex_polyline collider from the image, /// coordinates translated to either side of (0, 0) pub fn single_convex_polyline_collider_translated(image: &Image) -> Option { - let points = single_image_edge_translated(image) + let e = Edges::from(image); + let points = e + .single_image_edge_translated() .into_iter() .map(Point::from) .collect::>>(); @@ -38,7 +39,9 @@ pub fn single_convex_polyline_collider_translated(image: &Image) -> Option Option { - let points = single_image_edge_raw(image) + let e = Edges::from(image); + let points = e + .single_image_edge_raw() .into_iter() .map(Point::from) .collect::>>(); @@ -48,54 +51,61 @@ pub fn single_convex_polyline_collider_raw(image: &Image) -> Option { /// Generate a single convex_hull collider from the image, /// coordinates translated to either side of (0, 0) pub fn single_convex_hull_collider_translated(image: &Image) -> Option { - let points = single_image_edge_translated(image); + let e = Edges::from(image); + let points = e.single_image_edge_translated(); Collider::convex_hull(points) } /// Generate a single convex_hull collider from the image, /// coordinates left alone and all in positive x and y pub fn single_convex_hull_collider_raw(image: &Image) -> Option { - let points = single_image_edge_translated(image); + let e = Edges::from(image); + let points = e.single_image_edge_translated(); Collider::convex_hull(points) } /// Generate a single heightfield collider from the image, /// coordinates translated to either side of (0, 0) pub fn single_heightfield_collider_translated(image: &Image) -> Collider { - heightfield_collider_from_points(&single_image_edge_translated(image)) + let e = Edges::from(image); + heightfield_collider_from_points(&e.single_image_edge_translated()) } /// Generate a single heightfield collider from the image, /// coordinates left alone and all in positive x and y pub fn single_heightfield_collider_raw(image: &Image) -> Collider { - heightfield_collider_from_points(&single_image_edge_raw(image)) + let e = Edges::from(image); + heightfield_collider_from_points(&e.single_image_edge_raw()) } /// Generate as many polyline colliders as it can find in the image, /// coordinates translated to either side of (0, 0) pub fn multi_polyline_collider_translated(image: &Image) -> Vec { - multi_image_edge_translated(image) + let e = Edges::from(image); + e.multi_image_edge_translated() .into_iter() - .map(|e| Collider::polyline(e, None)) + .map(|v| Collider::polyline(v, None)) .collect() } /// Generate as many polyline colliders as it can find in the image, /// coordinates left alone and all in positive x and y pub fn multi_polyline_collider_raw(image: &Image) -> Vec { - multi_image_edges_raw(image) + let e = Edges::from(image); + e.multi_image_edges_raw() .into_iter() - .map(|e| Collider::polyline(e, None)) + .map(|v| Collider::polyline(v, None)) .collect() } /// Generate as many convex_polyline colliders as it can find in the image, /// coordinates translated to either side of (0, 0) pub fn multi_convex_polyline_collider_translated(image: &Image) -> Vec> { - multi_image_edge_translated(image) + let e = Edges::from(image); + e.multi_image_edge_translated() .into_iter() - .map(|e| { - let points = e.into_iter().map(Point::from).collect::>>(); + .map(|v| { + let points = v.into_iter().map(Point::from).collect::>>(); SharedShape::convex_polyline(points).map(Collider::from) }) .collect() @@ -104,10 +114,11 @@ pub fn multi_convex_polyline_collider_translated(image: &Image) -> Vec Vec> { - multi_image_edges_raw(image) + let e = Edges::from(image); + e.multi_image_edges_raw() .into_iter() - .map(|e| { - let points = e.into_iter().map(Point::from).collect::>>(); + .map(|v| { + let points = v.into_iter().map(Point::from).collect::>>(); SharedShape::convex_polyline(points).map(Collider::from) }) .collect() @@ -116,25 +127,28 @@ pub fn multi_convex_polyline_collider_raw(image: &Image) -> Vec /// Generate as many heightfield colliders as it can find in the image, /// coordinates translated to either side of (0, 0) pub fn multi_heightfield_collider_translated(image: &Image) -> Vec { - multi_image_edge_translated(image) + let e = Edges::from(image); + e.multi_image_edge_translated() .into_iter() - .map(|e| heightfield_collider_from_points(&e)) + .map(|v| heightfield_collider_from_points(&v)) .collect() } /// Generate as many heightfield colliders as it can find in the image, /// coordinates left alone and all in positive x and y pub fn multi_heightfield_collider_raw(image: &Image) -> Vec { - multi_image_edges_raw(image) + let e = Edges::from(image); + e.multi_image_edges_raw() .into_iter() - .map(|e| heightfield_collider_from_points(&e)) + .map(|v| heightfield_collider_from_points(&v)) .collect() } /// Generate as many convex_hull colliders as it can find in the image, /// coordinates translated to either side of (0, 0) pub fn multi_convex_hull_collider_translated(image: &Image) -> Vec> { - multi_image_edge_translated(image) + let e = Edges::from(image); + e.multi_image_edge_translated() .into_iter() .map(Collider::convex_hull) .collect() @@ -143,7 +157,8 @@ pub fn multi_convex_hull_collider_translated(image: &Image) -> Vec Vec> { - multi_image_edges_raw(image) + let e = Edges::from(image); + e.multi_image_edges_raw() .into_iter() .map(Collider::convex_hull) .collect() diff --git a/src/edge.rs b/src/edge.rs deleted file mode 100644 index 1bb36a9..0000000 --- a/src/edge.rs +++ /dev/null @@ -1,175 +0,0 @@ -use bevy::prelude::{Image, Vec2}; - -/// If there's only one sprite / object in the image, this returns just one, with -/// coordinates translated to either side of (0, 0) -pub fn single_image_edge_translated(image: &Image) -> Vec { - image_to_edges(image, true).into_iter().flatten().collect() -} - -/// If there's only one sprite / object in the image, this returns just one, with -/// coordinates left alone and all in positive x and y -pub fn single_image_edge_raw(image: &Image) -> Vec { - image_to_edges(image, false).into_iter().flatten().collect() -} - -/// If there's more than one sprite / object in the image, this returns all it finds, with -/// coordinates translated to either side of (0, 0) -pub fn multi_image_edge_translated(image: &Image) -> Vec> { - image_to_edges(image, true) -} - -/// If there's more than one sprite / object in the image, this returns all it finds, with -/// coordinates left alone and all in positive x and y -pub fn multi_image_edges_raw(image: &Image) -> Vec> { - image_to_edges(image, false) -} - -/// Takes a Bevy Image type and an boolean to indicate whether to translate -/// the points you get back to either side of (0, 0) instead of everything in positive x and y -pub fn image_to_edges(image: &Image, translate: bool) -> Vec> { - let rows = (image.size().y) as usize; - let cols = (image.size().x) as usize; - let data: Vec = image.data.clone(); - let mut byte_combine_step: usize = 1; - if (rows * cols) < data.len() { - byte_combine_step = data.len() / (rows * cols); - } - - let mut processed: Vec = vec![]; - for i in (0..data.len()).step_by(byte_combine_step) { - let mut b: usize = 0; - for j in 0..byte_combine_step { - b |= data[i + j] as usize; // just need to retain any non-zero values - } - processed.push(b); - } - - march_edges(&processed, rows, cols, translate) -} - -/// Marching squares adjacent, walks all the pixels in the provided data and keeps track of -/// any that have at least one transparent / zero value neighbor then, while sorting into drawing -/// order, groups them into sets of connected pixels -/// -/// Accepts a flag indicating whether or not to translate coordinates to either side of (0,0) -/// or leave it all in positive x,y -pub fn march_edges(data: &[usize], rows: usize, cols: usize, translate: bool) -> Vec> { - let mut edge_points: Vec = vec![]; - - for d in 0..data.len() { - let (x, y) = get_xy(d, rows); - let (c, r) = (x as isize, y as isize); - - if get_at(r, c, rows, cols, data) == 0 { - continue; - } - - let neighbors = [ - get_at(r + 1, c, rows, cols, data), - get_at(r - 1, c, rows, cols, data), - get_at(r, c + 1, rows, cols, data), - get_at(r, c - 1, rows, cols, data), - get_at(r + 1, c + 1, rows, cols, data), - get_at(r - 1, c - 1, rows, cols, data), - get_at(r + 1, c - 1, rows, cols, data), - get_at(r - 1, c + 1, rows, cols, data), - ]; - - let n: usize = neighbors.iter().sum(); - let surrounded = neighbors.len(); - if n < surrounded { - edge_points.push(Vec2::new(x, y)); - } - } - - points_to_drawing_order(&edge_points, translate, rows, cols) -} - -/// Takes a collection of coordinates and attempts to sort them according to drawing order -/// -/// Pixel sorted so that the distance to previous and next is 1. When there is no pixel left -/// with distance 1, another group is created and sorted the same way. -fn points_to_drawing_order( - points: &[Vec2], - translate: bool, - rows: usize, - cols: usize, -) -> Vec> { - let mut edge_points: Vec = points.to_vec(); - let mut in_drawing_order: Vec = vec![]; - let mut groups: Vec> = vec![]; - while !edge_points.is_empty() { - if in_drawing_order.is_empty() { - in_drawing_order.push(edge_points.swap_remove(0)); - } - - let prev = *in_drawing_order.last().unwrap(); - - let neighbor = edge_points - .iter() - .enumerate() - .find(|(_, p)| distance(prev, **p) == 1.0); - - if let Some((i, _)) = neighbor { - let next = edge_points.remove(i); - in_drawing_order.push(next); - continue; - } - - if !in_drawing_order.is_empty() { - groups.push(in_drawing_order.clone()); - in_drawing_order.clear() - } - } - - if !in_drawing_order.is_empty() { - groups.push(in_drawing_order.clone()); - } - - if translate { - groups = groups - .into_iter() - .map(|p| translate_vec(p, rows, cols)) - .collect(); - } - - groups -} - -/// conceptual helper, access a 1D vector like it's a 2D vector -fn get_xy(idx: usize, offset: usize) -> (f32, f32) { - let quot = idx / offset; - let rem = idx % offset; - (quot as f32, rem as f32) -} - -/// pythagoras, distance between two points -fn distance(a: Vec2, b: Vec2) -> f32 { - // d=√((x2-x1)²+(y2-y1)²) - ((a.x - b.x).abs().powi(2) + (a.y - b.y).abs().powi(2)).sqrt() -} - -/// get zero or non-zero pixel the value at given coordinate -fn get_at(row: isize, col: isize, rows: usize, cols: usize, data: &[usize]) -> usize { - if row < 0 || col < 0 || row >= rows as isize || col >= cols as isize { - 0 - } else { - let idx = row as usize * cols + col as usize; - data.get(idx) - .map(|i| if *i == 0 { 0 } else { 1 }) - .unwrap_or_else(|| 0) - } -} - -/// translate point in positive x,y to either side of (0,0) -fn xy_translate(p: Vec2, rows: usize, cols: usize) -> Vec2 { - Vec2::new( - p.x - (cols as f32 / 2. - 1.0), - -p.y + (rows as f32 / 2. - 1.0), - ) -} - -/// Translate vector of points in positive x,y to either side of (0,0) -pub fn translate_vec(v: Vec, rows: usize, cols: usize) -> Vec { - v.into_iter().map(|p| xy_translate(p, rows, cols)).collect() -} diff --git a/src/lib.rs b/src/lib.rs index a8ed647..b885a6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ #![doc = include_str!("../README.md")] mod collider; -mod edge; #[cfg(feature = "rapier2d")] pub use collider::rapier2d; @@ -9,10 +8,4 @@ pub use collider::rapier2d; #[cfg(feature = "xpbd_2d")] pub use collider::xpbd_2d; -pub use edge::image_to_edges; -pub use edge::march_edges; -pub use edge::multi_image_edge_translated; -pub use edge::multi_image_edges_raw; -pub use edge::single_image_edge_raw; -pub use edge::single_image_edge_translated; -pub use edge::translate_vec; +pub use ::edges::Edges;