From 60cb046930b899926877e62dd5700dfc37ec32b8 Mon Sep 17 00:00:00 2001 From: salam Date: Wed, 30 Oct 2024 18:14:57 +0900 Subject: [PATCH 01/15] reworking edges search algoritm --- Cargo.toml | 2 - src/{bin_image.rs => bin_image/mod.rs} | 45 +++-- src/bin_image/neighbors.rs | 8 + src/lib.rs | 70 +++++-- src/utils.rs | 250 +++++++++++++++++++------ 5 files changed, 284 insertions(+), 91 deletions(-) rename src/{bin_image.rs => bin_image/mod.rs} (71%) create mode 100644 src/bin_image/neighbors.rs diff --git a/Cargo.toml b/Cargo.toml index 7619b8b..80f3444 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,6 @@ repository = "https://github.com/shnewto/edges" license = "MIT OR Apache-2.0" [lints.clippy] -type_complexity = { level = "allow", priority = 1 } -needless_pass_by_value = { level = "allow", priority = 1 } cast_precision_loss = { level = "allow", priority = 1 } pedantic = { level = "warn", priority = 0 } diff --git a/src/bin_image.rs b/src/bin_image/mod.rs similarity index 71% rename from src/bin_image.rs rename to src/bin_image/mod.rs index 896a7e2..3549c6d 100644 --- a/src/bin_image.rs +++ b/src/bin_image/mod.rs @@ -1,4 +1,5 @@ use crate::{UVec2, Vec2}; +pub mod neighbors; pub struct BinImage { data: Vec, @@ -74,19 +75,35 @@ impl BinImage { /// /// # Returns /// - /// An array of 8 boolean values representing the state of the neighboring pixels. - pub fn get_neighbors(&self, p: UVec2) -> [bool; 8] { + /// An byte representing the state of the neighboring pixels. + pub fn get_neighbors(&self, p: UVec2) -> u8 { let (x, y) = (p.x, p.y); - [ - y < u32::MAX && self.get((x, y + 1).into()), // North - y > u32::MIN && self.get((x, y - 1).into()), // South - x < u32::MAX && self.get((x + 1, y).into()), // East - x > u32::MIN && self.get((x - 1, y).into()), // West - x < u32::MAX && y < u32::MAX && self.get((x + 1, y + 1).into()), // Northeast - x > u32::MIN && y > u32::MIN && self.get((x - 1, y - 1).into()), // Southwest - x < u32::MAX && y > u32::MIN && self.get((x + 1, y - 1).into()), // Southeast - x > u32::MIN && y < u32::MAX && self.get((x - 1, y + 1).into()), // Northwest - ] + let mut neighbors = 0; + if y < u32::MAX && self.get(UVec2::new(x, y + 1)) { + neighbors |= neighbors::NORTH; + } + if y > u32::MIN && self.get(UVec2::new(x, y - 1)) { + neighbors |= neighbors::SOUTH; + } + if x < u32::MAX && self.get(UVec2::new(x + 1, y)) { + neighbors |= neighbors::EAST; + } + if x > u32::MIN && self.get(UVec2::new(x - 1, y)) { + neighbors |= neighbors::WEST; + } + if x < u32::MAX && y < u32::MAX && self.get(UVec2::new(x + 1, y + 1)) { + neighbors |= neighbors::NORTHEAST; + } + if x > u32::MIN && y > u32::MIN && self.get(UVec2::new(x - 1, y - 1)) { + neighbors |= neighbors::NORTHWEST; + } + if x < u32::MAX && y > u32::MIN && self.get(UVec2::new(x + 1, y - 1)) { + neighbors |= neighbors::SOUTHEAST; + } + if x > u32::MIN && y < u32::MAX && self.get(UVec2::new(x - 1, y + 1)) { + neighbors |= neighbors::SOUTHWEST; + } + neighbors } /// Translates a point in positive (x, y) coordinates to a coordinate system centered at (0, 0). @@ -100,8 +117,8 @@ impl BinImage { /// A new `Vec2` representing the translated coordinates fn translate_point(&self, p: Vec2) -> Vec2 { Vec2::new( - p.x - (self.width as f32 / 2.0 - 1.0), - (self.height as f32 / 2.0 - 1.0) - p.y, + p.x - ((self.width / 2) as f32 - 1.0), + ((self.height / 2) as f32 - 1.0) - p.y, ) } diff --git a/src/bin_image/neighbors.rs b/src/bin_image/neighbors.rs new file mode 100644 index 0000000..311d9cb --- /dev/null +++ b/src/bin_image/neighbors.rs @@ -0,0 +1,8 @@ +pub const NORTH: u8 = 0b1000_0000; +pub const SOUTH: u8 = 0b0100_0000; +pub const EAST: u8 = 0b0010_0000; +pub const WEST: u8 = 0b0001_0000; +pub const NORTHEAST: u8 = 0b0000_1000; +pub const NORTHWEST: u8 = 0b0000_0100; +pub const SOUTHEAST: u8 = 0b0000_0010; +pub const SOUTHWEST: u8 = 0b0000_0001; diff --git a/src/lib.rs b/src/lib.rs index ec37732..e5e6615 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,12 @@ #![doc = include_str!("../README.md")] +use crate::bin_image::BinImage; #[cfg(feature = "bevy")] pub use bevy_math::prelude::{UVec2, Vec2}; #[cfg(not(feature = "bevy"))] pub use glam::{UVec2, Vec2}; use std::fmt; - -use crate::{bin_image::BinImage, utils::points_to_drawing_order}; +use utils::{is_corner, match_neighbors}; mod bin_image; #[cfg(feature = "bevy")] @@ -62,24 +62,64 @@ impl Edges { // 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 - let edge_points = (0..image.height() * image.width()) - .map(|i| (i / image.height(), i % image.height())) - .map(|(x, y)| UVec2::new(x, y)) - .filter(|p| image.get(*p)) - .filter(|p| (0..8).contains(&image.get_neighbors(*p).iter().filter(|i| **i).count())) + let edge_points: Vec<_> = (0..image.height() * image.width()) + .map(|i| UVec2::new(i / image.height(), i % image.height())) + .filter(|p| image.get(*p) && is_corner(image.get_neighbors(*p))) .collect(); - points_to_drawing_order(edge_points) + let groups: Vec<_> = self + .points_to_drawing_order(&edge_points) .into_iter() - .map(|group| { - let group = group.into_iter().map(|p| p.as_vec2()).collect(); - if translate { - self.translate(group) + .map(|group| group.into_iter().map(|p| p.as_vec2()).collect()) + .collect(); + if translate { + groups + .into_iter() + .map(|group| self.translate(group)) + .collect() + } else { + groups + } + } + + /// 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(&self, points: &[UVec2]) -> Vec> { + if points.is_empty() { + return Vec::new(); + } + + let mut groups: Vec> = Vec::new(); + let mut group: Vec<_> = Vec::new(); + let mut start = points[0]; + let mut current = start; + group.push(current); + + loop { + match_neighbors(self.image.get_neighbors(current), &mut current, &mut group); + + // we've traversed and backtracked and we're back at the start without reaching the end of the points + // so we need to start a collecting the points of a new unconnected object + if current == start { + groups.push(group.clone()); + group.clear(); + + if let Some(new_start) = points + .iter() + .find(|p1| groups.iter().flatten().all(|p2| *p1 != p2)) + { + start = *new_start; + current = start; + group.push(current); } else { - group + break; } - }) - .collect() + } + } + + groups } /// Translates an `Vec` of points in positive (x, y) coordinates to a coordinate system centered at (0, 0). diff --git a/src/utils.rs b/src/utils.rs index 07cd18d..afb3a1b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,69 +1,199 @@ -use crate::{UVec2, Vec2}; -use std::collections::HashMap; +use crate::UVec2; +use std::cmp::Ordering::{Equal, Greater, Less}; +use std::ops::{AddAssign, SubAssign}; -// d=√((x2-x1)²+(y2-y1)²) -pub fn distance(a: Vec2, b: Vec2) -> f32 { - ((a.x - b.x).powi(2) + (a.y - b.y).powi(2)).sqrt() +pub fn is_corner(neighbors: u8) -> bool { + !matches!( + neighbors, + 213 | 234 + | 200 + | 196 + | 194 + | 193 + | 192 + | 185 + | 118 + | 56 + | 52 + | 50 + | 49 + | 48 + | 177 + | 170 + | 184 + | 212 + | 209 + | 114 + | 116 + | 232 + | 226 + | 251 + | 255 + | 0 + ) } -/// 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. -pub fn points_to_drawing_order(points: Vec) -> Vec> { - if points.is_empty() { - return Vec::new(); - } - - let mut groups: Vec> = Vec::new(); - let mut group: Vec = Vec::new(); - let mut drawn_points_with_counts = HashMap::new(); - - let mut start = points[0]; - let mut current = start; - group.push(current); - drawn_points_with_counts.insert(current, 2); - - while drawn_points_with_counts.len() < points.len() { - if let Some(p) = points - .iter() - .filter(|p| (distance(current.as_vec2(), p.as_vec2()) - 1.0).abs() <= f32::EPSILON) - .min_by_key(|n| drawn_points_with_counts.get(n).map_or(0, |c| *c)) - { - current = *p; - group.push(current); - if let Some(c) = drawn_points_with_counts.get_mut(p) { - *c += 1; - } else { - drawn_points_with_counts.insert(current, 2); - } +#[allow(clippy::too_many_lines)] +pub fn match_neighbors(neighbors: u8, current: &mut UVec2, group: &mut Vec) { + if let Some(last) = group.last() { + let last = *last; + if last != *current && is_corner(neighbors) { + group.push(*current); } - - // we've traversed and backtracked and we're back at the start without reaching the end of the points - // so we need to start a collecting the points of a new unconnected object - if current == start { - // remove the connecting coordinate - let _ = group.pop(); - groups.push(group.clone()); - group.clear(); - for val in drawn_points_with_counts.values_mut() { - *val = 1; + println!("l:{last}"); + println!("c:{current}"); + println!("n:{neighbors}"); + // println!("{group:#?}"); + if current.x == 511 { + todo!(); + } + match neighbors { + 189 | 191 | 119 | 126 | 187 | 185 | 118 | 56 | 52 | 50 | 49 | 48 => { + match last.x.cmp(¤t.x) { + Greater | Equal => current.x.sub_assign(1), + Less => current.x.add_assign(1), + } } - - if let Some(new_start) = points - .iter() - .find(|p| !drawn_points_with_counts.contains_key(p)) - { - start = *new_start; - current = start; - group.push(current); - drawn_points_with_counts.insert(current, 2); - } else { - break; + 221 | 215 | 238 | 235 | 213 | 234 | 200 | 196 | 194 | 193 | 192 => { + match last.y.cmp(¤t.y) { + Greater | Equal => current.y.sub_assign(1), + Less => current.y.add_assign(1), + } } + 168 | 160 => match last.x.cmp(¤t.x) { + Greater => current.y.add_assign(1), + Equal => current.x.add_assign(1), + Less => unreachable!(), + }, + 145 | 144 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => current.x.sub_assign(1), + Less => current.y.add_assign(1), + }, + 84 | 80 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => current.x.sub_assign(1), + Less => current.y.sub_assign(1), + }, + 98 | 96 => match last.x.cmp(¤t.x) { + Greater => current.y.sub_assign(1), + Equal => current.x.add_assign(1), + Less => unreachable!(), + }, + 177 => match last.x.cmp(¤t.x) { + Greater => { + group.push(*current); + current.y += 1; + } + Equal => unreachable!(), + Less => current.x.add_assign(1), + }, + 184 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => { + group.push(*current); + current.x -= 1; + } + Less => current.x.add_assign(1), + }, + 212 => match last.y.cmp(¤t.y) { + Greater => { + group.push(*current); + current.x -= 1; + } + Equal => unreachable!(), + Less => current.y.add_assign(1), + }, + 209 => match last.y.cmp(¤t.y) { + Greater => unreachable!(), + Equal => { + group.push(*current); + current.y -= 1; + } + Less => current.y.add_assign(1), + }, + 114 => match last.x.cmp(¤t.x) { + Greater => current.x.sub_assign(1), + Equal => unreachable!(), + Less => { + group.push(*current); + current.y -= 1; + } + }, + 116 => match last.x.cmp(¤t.x) { + Greater => current.x.sub_assign(1), + Equal => { + group.push(*current); + current.x += 1; + } + Less => unreachable!(), + }, + 232 => match last.y.cmp(¤t.y) { + Greater => current.y.sub_assign(1), + Equal => unreachable!(), + Less => { + group.push(*current); + current.x += 1; + } + }, + 226 => match last.y.cmp(¤t.y) { + Greater => current.y.sub_assign(1), + Equal => { + group.push(*current); + current.y -= 1; + } + Less => unreachable!(), + }, + 169..=171 | 253 => match last.x.cmp(¤t.x) { + Greater | Less => unreachable!(), + Equal => { + group.push(*current); + current.x += 1; + } + }, + 153 | 157 | 149 => match last.x.cmp(¤t.x) { + Greater | Equal => unreachable!(), + Less => { + group.push(*current); + current.y += 1; + } + }, + 85..=87 | 254 => match last.x.cmp(¤t.x) { + Greater | Less => unreachable!(), + Equal => { + group.push(*current); + current.x -= 1; + } + }, + 251 => match last.x.cmp(¤t.x) { + Greater | Equal => unreachable!(), + Less => { + group.push(*current); + current.y -= 1; + } + }, + 247 => match last.x.cmp(¤t.x) { + Greater => { + group.push(*current); + current.y += 1; + } + Equal | Less => unreachable!(), + }, + 110 | 102 => match last.x.cmp(¤t.x) { + Greater => { + group.push(*current); + current.y -= 1; + } + Equal | Less => unreachable!(), + }, + 106 => match last.x.cmp(¤t.x) { + Greater | Equal => { + group.push(*current); + current.y -= 1; + } + Less => unreachable!(), + }, + _ => {} } } - groups.push(group); - - groups } From b1dbde757ed71e94dcbdb4dd6520775b61bb1bcc Mon Sep 17 00:00:00 2001 From: salam Date: Wed, 30 Oct 2024 19:50:55 +0900 Subject: [PATCH 02/15] fixed get method of `BinImage` --- src/bin_image/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin_image/mod.rs b/src/bin_image/mod.rs index 3549c6d..8dd736b 100644 --- a/src/bin_image/mod.rs +++ b/src/bin_image/mod.rs @@ -61,7 +61,7 @@ impl BinImage { .copied() { byte >>= index % 8; // index of bit - x <= self.width && byte & 1 > 0 + x < self.width && byte & 1 > 0 } else { false } From 0eca991f382c3a1a5ba3a9c66a62122f70325b3b Mon Sep 17 00:00:00 2001 From: salam Date: Wed, 30 Oct 2024 20:09:38 +0900 Subject: [PATCH 03/15] modified: src/utils.rs --- src/utils.rs | 62 ++++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index afb3a1b..0322d66 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,13 +5,22 @@ use std::ops::{AddAssign, SubAssign}; pub fn is_corner(neighbors: u8) -> bool { !matches!( neighbors, - 213 | 234 + 238 | 235 + | 234 + | 221 + | 215 + | 213 | 200 | 196 | 194 | 193 | 192 + | 191 + | 189 + | 187 | 185 + | 126 + | 119 | 118 | 56 | 52 @@ -44,17 +53,14 @@ pub fn match_neighbors(neighbors: u8, current: &mut UVec2, group: &mut Vec { + 191 | 189 | 187 | 185 | 126 | 119 | 118 | 56 | 52 | 48..=50 => { match last.x.cmp(¤t.x) { Greater | Equal => current.x.sub_assign(1), Less => current.x.add_assign(1), } } - 221 | 215 | 238 | 235 | 213 | 234 | 200 | 196 | 194 | 193 | 192 => { + 238 | 235 | 234 | 221 | 215 | 213 | 200 | 196 | 192..=194 => { match last.y.cmp(¤t.y) { Greater | Equal => current.y.sub_assign(1), Less => current.y.add_assign(1), @@ -80,6 +86,27 @@ pub fn match_neighbors(neighbors: u8, current: &mut UVec2, group: &mut Vec current.x.add_assign(1), Less => unreachable!(), }, + 169..=171 | 253 => match last.x.cmp(¤t.x) { + Greater | Less => unreachable!(), + Equal => { + group.push(*current); + current.x += 1; + } + }, + 85..=87 | 254 => match last.x.cmp(¤t.x) { + Greater | Less => unreachable!(), + Equal => { + group.push(*current); + current.x -= 1; + } + }, + 153 | 157 | 149 => match last.x.cmp(¤t.x) { + Greater | Equal => unreachable!(), + Less => { + group.push(*current); + current.y += 1; + } + }, 177 => match last.x.cmp(¤t.x) { Greater => { group.push(*current); @@ -99,7 +126,7 @@ pub fn match_neighbors(neighbors: u8, current: &mut UVec2, group: &mut Vec match last.y.cmp(¤t.y) { Greater => { group.push(*current); - current.x -= 1; + current.x -= 1; } Equal => unreachable!(), Less => current.y.add_assign(1), @@ -144,27 +171,6 @@ pub fn match_neighbors(neighbors: u8, current: &mut UVec2, group: &mut Vec unreachable!(), }, - 169..=171 | 253 => match last.x.cmp(¤t.x) { - Greater | Less => unreachable!(), - Equal => { - group.push(*current); - current.x += 1; - } - }, - 153 | 157 | 149 => match last.x.cmp(¤t.x) { - Greater | Equal => unreachable!(), - Less => { - group.push(*current); - current.y += 1; - } - }, - 85..=87 | 254 => match last.x.cmp(¤t.x) { - Greater | Less => unreachable!(), - Equal => { - group.push(*current); - current.x -= 1; - } - }, 251 => match last.x.cmp(¤t.x) { Greater | Equal => unreachable!(), Less => { From c4ca604e3cde1a40dffc8d92b2dd378b951335f9 Mon Sep 17 00:00:00 2001 From: salam Date: Thu, 31 Oct 2024 20:58:25 +0900 Subject: [PATCH 04/15] reworking edges search algoritm --- examples/bevy-image.rs | 4 +- src/lib.rs | 58 +++---- src/utils.rs | 354 +++++++++++++++++++++-------------------- 3 files changed, 199 insertions(+), 217 deletions(-) diff --git a/examples/bevy-image.rs b/examples/bevy-image.rs index a24cba2..218a169 100644 --- a/examples/bevy-image.rs +++ b/examples/bevy-image.rs @@ -48,9 +48,9 @@ fn draw_png(image: Image, img_path: &str) { // draw the edges to a png let mut dt = DrawTarget::new(width, height); - let objects_iter = edges.multi_image_edges_raw().into_iter(); + let objects = edges.multi_image_edges_raw(); - for object in objects_iter { + for object in objects { let mut pb = PathBuilder::new(); let mut edges_iter = object.into_iter(); diff --git a/src/lib.rs b/src/lib.rs index e5e6615..84b05e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ pub use bevy_math::prelude::{UVec2, Vec2}; #[cfg(not(feature = "bevy"))] pub use glam::{UVec2, Vec2}; use std::fmt; -use utils::{is_corner, match_neighbors}; +use utils::is_corner; mod bin_image; #[cfg(feature = "bevy")] @@ -62,64 +62,42 @@ impl Edges { // 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 - let edge_points: Vec<_> = (0..image.height() * image.width()) + let corners: Vec<_> = (0..image.height() * image.width()) .map(|i| UVec2::new(i / image.height(), i % image.height())) .filter(|p| image.get(*p) && is_corner(image.get_neighbors(*p))) .collect(); - let groups: Vec<_> = self - .points_to_drawing_order(&edge_points) + let objects: Vec<_> = self + .collect_objects(&corners) .into_iter() - .map(|group| group.into_iter().map(|p| p.as_vec2()).collect()) + .map(|object| object.into_iter().map(|p| p.as_vec2()).collect()) .collect(); if translate { - groups + objects .into_iter() .map(|group| self.translate(group)) .collect() } else { - groups + objects } } - /// 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(&self, points: &[UVec2]) -> Vec> { - if points.is_empty() { + fn collect_objects(&self, corners: &[UVec2]) -> Vec> { + if corners.is_empty() { return Vec::new(); } - let mut groups: Vec> = Vec::new(); - let mut group: Vec<_> = Vec::new(); - let mut start = points[0]; - let mut current = start; - group.push(current); - - loop { - match_neighbors(self.image.get_neighbors(current), &mut current, &mut group); - - // we've traversed and backtracked and we're back at the start without reaching the end of the points - // so we need to start a collecting the points of a new unconnected object - if current == start { - groups.push(group.clone()); - group.clear(); - - if let Some(new_start) = points - .iter() - .find(|p1| groups.iter().flatten().all(|p2| *p1 != p2)) - { - start = *new_start; - current = start; - group.push(current); - } else { - break; - } - } + let mut objects: Vec> = Vec::new(); + + while let Some(start) = corners + .iter() + .find(|point| objects.iter().all(|object| !object.contains(point))) + { + let object = self.collect_object(*start); + objects.push(object); } - groups + objects } /// Translates an `Vec` of points in positive (x, y) coordinates to a coordinate system centered at (0, 0). diff --git a/src/utils.rs b/src/utils.rs index 0322d66..de199c8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,205 +1,209 @@ -use crate::UVec2; +use crate::{Edges, UVec2}; use std::cmp::Ordering::{Equal, Greater, Less}; use std::ops::{AddAssign, SubAssign}; pub fn is_corner(neighbors: u8) -> bool { !matches!( neighbors, - 238 | 235 - | 234 + 255 + | 239 + | 238 + | 232..=235 + | 127 + | 226 + | 223 | 221 | 215 | 213 + | 212 + | 209 + | 207 + | 201 | 200 - | 196 - | 194 - | 193 - | 192 - | 191 + | 196..=198 + | 191..=194 | 189 | 187 | 185 + | 184 + | 177 | 126 | 119 | 118 - | 56 - | 52 - | 50 - | 49 - | 48 - | 177 - | 170 - | 184 - | 212 - | 209 - | 114 | 116 - | 232 - | 226 - | 251 - | 255 + | 114 + | 58 + | 56 + | 52..=54 + | 48..=50 | 0 ) } -#[allow(clippy::too_many_lines)] -pub fn match_neighbors(neighbors: u8, current: &mut UVec2, group: &mut Vec) { - if let Some(last) = group.last() { - let last = *last; - if last != *current && is_corner(neighbors) { - group.push(*current); - } - println!("l:{last}"); - println!("c:{current}"); - println!("n:{neighbors}"); - // println!("{group:#?}"); - match neighbors { - 191 | 189 | 187 | 185 | 126 | 119 | 118 | 56 | 52 | 48..=50 => { - match last.x.cmp(¤t.x) { +impl Edges { + #[allow(clippy::too_many_lines)] + pub(super) fn collect_object(&self, mut current: UVec2) -> Vec { + let mut group: Vec = Vec::new(); + group.push(current); + loop { + let (last, neighbors) = (*group.last().unwrap(), self.image.get_neighbors(current)); + if last != current && is_corner(neighbors) { + group.push(current); + } + match neighbors { + 253 | 169..=171 | 32 => current.x.add_assign(1), + 254 | 85..=87 | 16 => current.x.sub_assign(1), + 251 | 110 | 106 | 102 | 64 => current.y.sub_assign(1), + 247 | 153 | 157 | 149 | 128 => current.y.add_assign(1), + 233 + | 191 + | 189 + | 187 + | 185 + | 127 + | 126 + | 119 + | 118 + | 58 + | 56 + | 52..=54 + | 48..=50 => match last.x.cmp(¤t.x) { Greater | Equal => current.x.sub_assign(1), Less => current.x.add_assign(1), - } - } - 238 | 235 | 234 | 221 | 215 | 213 | 200 | 196 | 192..=194 => { - match last.y.cmp(¤t.y) { + }, + 239 + | 238 + | 235 + | 234 + | 223 + | 221 + | 215 + | 213 + | 207 + | 201 + | 200 + | 196..=198 + | 192..=194 => match last.y.cmp(¤t.y) { Greater | Equal => current.y.sub_assign(1), Less => current.y.add_assign(1), + }, + 168 | 160 => match last.x.cmp(¤t.x) { + Greater => current.y.add_assign(1), + Equal => current.x.add_assign(1), + Less => unreachable!(), + }, + 145 | 144 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => current.x.sub_assign(1), + Less => current.y.add_assign(1), + }, + 84 | 80 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => current.x.sub_assign(1), + Less => current.y.sub_assign(1), + }, + 98 | 96 => match last.x.cmp(¤t.x) { + Greater => current.y.sub_assign(1), + Equal => current.x.add_assign(1), + Less => unreachable!(), + }, + 177 => match last.x.cmp(¤t.x) { + Greater => { + group.push(current); + current.y += 1; + } + Equal => unreachable!(), + Less => current.x.add_assign(1), + }, + 184 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => { + group.push(current); + current.x -= 1; + } + Less => current.x.add_assign(1), + }, + 212 => match last.y.cmp(¤t.y) { + Greater => { + group.push(current); + current.x -= 1; + } + Equal => unreachable!(), + Less => current.y.add_assign(1), + }, + 209 => match last.y.cmp(¤t.y) { + Greater => unreachable!(), + Equal => { + group.push(current); + current.y -= 1; + } + Less => current.y.add_assign(1), + }, + 114 => match last.x.cmp(¤t.x) { + Greater => current.x.sub_assign(1), + Equal => unreachable!(), + Less => { + group.push(current); + current.y -= 1; + } + }, + 116 => match last.x.cmp(¤t.x) { + Greater => current.x.sub_assign(1), + Equal => { + group.push(current); + current.x += 1; + } + Less => unreachable!(), + }, + 232 => match last.y.cmp(¤t.y) { + Greater => current.y.sub_assign(1), + Equal => unreachable!(), + Less => { + group.push(current); + current.x += 1; + } + }, + 226 => match last.y.cmp(¤t.y) { + Greater => current.y.sub_assign(1), + Equal => { + group.push(current); + current.y -= 1; + } + Less => unreachable!(), + }, + 240 => match (last.x.cmp(¤t.x), last.y.cmp(¤t.y)) { + (Less, Equal) => current.y.sub_assign(1), + (Equal, Less) => current.x.add_assign(1), + (Greater, Equal) => current.y.add_assign(1), + (Equal, Greater) => current.x.sub_assign(1), + _ => unreachable!(), + }, + 249 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => current.x.add_assign(1), + Less => current.y.sub_assign(1), + }, + 112 => match last.x.cmp(¤t.x) { + Greater => current.x.sub_assign(1), + Equal => current.x.add_assign(1), + Less => current.y.sub_assign(1), + }, + 246 => match last.x.cmp(¤t.x) { + Greater => current.y.add_assign(1), + Equal => current.x.sub_assign(1), + Less => current.x.add_assign(1), + }, + 0 | 255 => unreachable!(), + _ => { + println!("l:{last}"); + println!("c:{current}"); + println!("n:{neighbors}"); + unreachable!() } } - 168 | 160 => match last.x.cmp(¤t.x) { - Greater => current.y.add_assign(1), - Equal => current.x.add_assign(1), - Less => unreachable!(), - }, - 145 | 144 => match last.x.cmp(¤t.x) { - Greater => unreachable!(), - Equal => current.x.sub_assign(1), - Less => current.y.add_assign(1), - }, - 84 | 80 => match last.x.cmp(¤t.x) { - Greater => unreachable!(), - Equal => current.x.sub_assign(1), - Less => current.y.sub_assign(1), - }, - 98 | 96 => match last.x.cmp(¤t.x) { - Greater => current.y.sub_assign(1), - Equal => current.x.add_assign(1), - Less => unreachable!(), - }, - 169..=171 | 253 => match last.x.cmp(¤t.x) { - Greater | Less => unreachable!(), - Equal => { - group.push(*current); - current.x += 1; - } - }, - 85..=87 | 254 => match last.x.cmp(¤t.x) { - Greater | Less => unreachable!(), - Equal => { - group.push(*current); - current.x -= 1; - } - }, - 153 | 157 | 149 => match last.x.cmp(¤t.x) { - Greater | Equal => unreachable!(), - Less => { - group.push(*current); - current.y += 1; - } - }, - 177 => match last.x.cmp(¤t.x) { - Greater => { - group.push(*current); - current.y += 1; - } - Equal => unreachable!(), - Less => current.x.add_assign(1), - }, - 184 => match last.x.cmp(¤t.x) { - Greater => unreachable!(), - Equal => { - group.push(*current); - current.x -= 1; - } - Less => current.x.add_assign(1), - }, - 212 => match last.y.cmp(¤t.y) { - Greater => { - group.push(*current); - current.x -= 1; - } - Equal => unreachable!(), - Less => current.y.add_assign(1), - }, - 209 => match last.y.cmp(¤t.y) { - Greater => unreachable!(), - Equal => { - group.push(*current); - current.y -= 1; - } - Less => current.y.add_assign(1), - }, - 114 => match last.x.cmp(¤t.x) { - Greater => current.x.sub_assign(1), - Equal => unreachable!(), - Less => { - group.push(*current); - current.y -= 1; - } - }, - 116 => match last.x.cmp(¤t.x) { - Greater => current.x.sub_assign(1), - Equal => { - group.push(*current); - current.x += 1; - } - Less => unreachable!(), - }, - 232 => match last.y.cmp(¤t.y) { - Greater => current.y.sub_assign(1), - Equal => unreachable!(), - Less => { - group.push(*current); - current.x += 1; - } - }, - 226 => match last.y.cmp(¤t.y) { - Greater => current.y.sub_assign(1), - Equal => { - group.push(*current); - current.y -= 1; - } - Less => unreachable!(), - }, - 251 => match last.x.cmp(¤t.x) { - Greater | Equal => unreachable!(), - Less => { - group.push(*current); - current.y -= 1; - } - }, - 247 => match last.x.cmp(¤t.x) { - Greater => { - group.push(*current); - current.y += 1; - } - Equal | Less => unreachable!(), - }, - 110 | 102 => match last.x.cmp(¤t.x) { - Greater => { - group.push(*current); - current.y -= 1; - } - Equal | Less => unreachable!(), - }, - 106 => match last.x.cmp(¤t.x) { - Greater | Equal => { - group.push(*current); - current.y -= 1; - } - Less => unreachable!(), - }, - _ => {} + if current == group[0] { + break group; + } } } } From 2bbb2be083cb85275f32fcd927fd55519cd61c2b Mon Sep 17 00:00:00 2001 From: salam Date: Fri, 1 Nov 2024 18:53:38 +0900 Subject: [PATCH 05/15] added new image for tests --- assets/broken-square.png | Bin 0 -> 296 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/broken-square.png diff --git a/assets/broken-square.png b/assets/broken-square.png new file mode 100644 index 0000000000000000000000000000000000000000..dd2a5c0f4d40d922898e97485b5667cfdb810c77 GIT binary patch literal 296 zcmV+@0oVSCP)Px#;z>k7R9J=OS3wd3Aqaz={{PGLl9SsNz~Bk!f-C&W75Y z0UBH3-2%4wdI3yvF@c~`2O!01=K)!fi%5CvMjlul%aQ$CVzTj#$SYysDnS)Ybnh;3 zxpun%?Um_9bax?|Jn->Kh`tk^Txh2x)J3O~ct;DcNr=q&0W0XGXkF0&oo&Hk>pgBF uVSa}p$-2{<+*`TjhrSE43Eg_U{~n&c=rKA7{t{0B0000 Date: Fri, 1 Nov 2024 18:55:37 +0900 Subject: [PATCH 06/15] fix: inner edges collecting --- src/lib.rs | 28 +++- src/utils.rs | 439 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 297 insertions(+), 170 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 84b05e6..746f721 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ pub use bevy_math::prelude::{UVec2, Vec2}; #[cfg(not(feature = "bevy"))] pub use glam::{UVec2, Vec2}; use std::fmt; -use utils::is_corner; +use utils::{handle_neighbors, in_polygon, is_corner}; mod bin_image; #[cfg(feature = "bevy")] @@ -89,10 +89,11 @@ impl Edges { let mut objects: Vec> = Vec::new(); - while let Some(start) = corners - .iter() - .find(|point| objects.iter().all(|object| !object.contains(point))) - { + while let Some(start) = corners.iter().find(|point| { + objects + .iter() + .all(|object| !object.contains(point) && !in_polygon(**point, object)) + }) { let object = self.collect_object(*start); objects.push(object); } @@ -100,6 +101,23 @@ impl Edges { objects } + fn collect_object(&self, mut current: UVec2) -> Vec { + let mut group: Vec = Vec::new(); + group.push(current); + loop { + let (last, neighbors) = (*group.last().unwrap(), self.image.get_neighbors(current)); + if last != current && is_corner(neighbors) { + group.push(current); + } + if let Some(point) = handle_neighbors(&mut current, last, neighbors) { + group.push(point); + } + if current == group[0] { + break group; + } + } + } + /// Translates an `Vec` of points in positive (x, y) coordinates to a coordinate system centered at (0, 0). /// /// # Arguments diff --git a/src/utils.rs b/src/utils.rs index de199c8..c6f783d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,41 @@ -use crate::{Edges, UVec2}; +use crate::UVec2; use std::cmp::Ordering::{Equal, Greater, Less}; -use std::ops::{AddAssign, SubAssign}; + +pub fn in_polygon(point: UVec2, polygon: &[UVec2]) -> bool { + let mut is_inside = false; + for win in polygon.windows(2) { + let (p1, p2) = (win[0], win[1]); + if (p1.x.min(p2.x) <= point.x && point.x <= p1.x.max(p2.x)) + && (p1.y.min(p2.y) <= point.y && point.y <= p1.y.max(p2.y)) + && (p1.y.max(p2.y) - p1.y.min(p2.y)) * (point.x - p1.x.min(p2.x)) + == (p2.x.max(p2.x) - p1.x.min(p2.x)) * (point.y - p1.y.min(p2.y)) + { + return true; + } + + if p1.y <= point.y && point.y < p2.y || p2.y <= point.y && point.y < p1.y { + let (point_x, offset_x) = point + .x + .checked_sub(p1.x) + .map_or_else(|| (0, p1.x - point.x), |dx| (dx, 0)); + let (point_y, offset_y) = point + .y + .checked_sub(p1.y) + .map_or_else(|| (0, p1.y - point.y), |dy| (dy, 0)); + + let (dx, offset_x) = (p2.x + offset_x) + .checked_sub(p1.x) + .map_or_else(|| (0, p1.x - p2.x - offset_x), |dx| (dx, 0)); + let (dy, offset_y) = (p2.y + offset_y) + .checked_sub(p1.y) + .map_or_else(|| (0, p1.y - p2.y - offset_y), |dy| (dy, 0)); + if (point_x + offset_x) * dy >= dx * (point_y + offset_y) { + is_inside = !is_inside; + } + } + } + is_inside +} pub fn is_corner(neighbors: u8) -> bool { !matches!( @@ -40,170 +75,244 @@ pub fn is_corner(neighbors: u8) -> bool { ) } -impl Edges { - #[allow(clippy::too_many_lines)] - pub(super) fn collect_object(&self, mut current: UVec2) -> Vec { - let mut group: Vec = Vec::new(); - group.push(current); - loop { - let (last, neighbors) = (*group.last().unwrap(), self.image.get_neighbors(current)); - if last != current && is_corner(neighbors) { - group.push(current); - } - match neighbors { - 253 | 169..=171 | 32 => current.x.add_assign(1), - 254 | 85..=87 | 16 => current.x.sub_assign(1), - 251 | 110 | 106 | 102 | 64 => current.y.sub_assign(1), - 247 | 153 | 157 | 149 | 128 => current.y.add_assign(1), - 233 - | 191 - | 189 - | 187 - | 185 - | 127 - | 126 - | 119 - | 118 - | 58 - | 56 - | 52..=54 - | 48..=50 => match last.x.cmp(¤t.x) { - Greater | Equal => current.x.sub_assign(1), - Less => current.x.add_assign(1), - }, - 239 - | 238 - | 235 - | 234 - | 223 - | 221 - | 215 - | 213 - | 207 - | 201 - | 200 - | 196..=198 - | 192..=194 => match last.y.cmp(¤t.y) { - Greater | Equal => current.y.sub_assign(1), - Less => current.y.add_assign(1), - }, - 168 | 160 => match last.x.cmp(¤t.x) { - Greater => current.y.add_assign(1), - Equal => current.x.add_assign(1), - Less => unreachable!(), - }, - 145 | 144 => match last.x.cmp(¤t.x) { - Greater => unreachable!(), - Equal => current.x.sub_assign(1), - Less => current.y.add_assign(1), - }, - 84 | 80 => match last.x.cmp(¤t.x) { - Greater => unreachable!(), - Equal => current.x.sub_assign(1), - Less => current.y.sub_assign(1), - }, - 98 | 96 => match last.x.cmp(¤t.x) { - Greater => current.y.sub_assign(1), - Equal => current.x.add_assign(1), - Less => unreachable!(), - }, - 177 => match last.x.cmp(¤t.x) { - Greater => { - group.push(current); - current.y += 1; - } - Equal => unreachable!(), - Less => current.x.add_assign(1), - }, - 184 => match last.x.cmp(¤t.x) { - Greater => unreachable!(), - Equal => { - group.push(current); - current.x -= 1; - } - Less => current.x.add_assign(1), - }, - 212 => match last.y.cmp(¤t.y) { - Greater => { - group.push(current); - current.x -= 1; - } - Equal => unreachable!(), - Less => current.y.add_assign(1), - }, - 209 => match last.y.cmp(¤t.y) { - Greater => unreachable!(), - Equal => { - group.push(current); - current.y -= 1; - } - Less => current.y.add_assign(1), - }, - 114 => match last.x.cmp(¤t.x) { - Greater => current.x.sub_assign(1), - Equal => unreachable!(), - Less => { - group.push(current); - current.y -= 1; - } - }, - 116 => match last.x.cmp(¤t.x) { - Greater => current.x.sub_assign(1), - Equal => { - group.push(current); - current.x += 1; - } - Less => unreachable!(), - }, - 232 => match last.y.cmp(¤t.y) { - Greater => current.y.sub_assign(1), - Equal => unreachable!(), - Less => { - group.push(current); - current.x += 1; - } - }, - 226 => match last.y.cmp(¤t.y) { - Greater => current.y.sub_assign(1), - Equal => { - group.push(current); - current.y -= 1; - } - Less => unreachable!(), - }, - 240 => match (last.x.cmp(¤t.x), last.y.cmp(¤t.y)) { - (Less, Equal) => current.y.sub_assign(1), - (Equal, Less) => current.x.add_assign(1), - (Greater, Equal) => current.y.add_assign(1), - (Equal, Greater) => current.x.sub_assign(1), - _ => unreachable!(), - }, - 249 => match last.x.cmp(¤t.x) { - Greater => unreachable!(), - Equal => current.x.add_assign(1), - Less => current.y.sub_assign(1), - }, - 112 => match last.x.cmp(¤t.x) { - Greater => current.x.sub_assign(1), - Equal => current.x.add_assign(1), - Less => current.y.sub_assign(1), - }, - 246 => match last.x.cmp(¤t.x) { - Greater => current.y.add_assign(1), - Equal => current.x.sub_assign(1), - Less => current.x.add_assign(1), - }, - 0 | 255 => unreachable!(), - _ => { - println!("l:{last}"); - println!("c:{current}"); - println!("n:{neighbors}"); - unreachable!() - } - } - if current == group[0] { - break group; +#[allow(clippy::too_many_lines)] +pub fn handle_neighbors(current: &mut UVec2, last: UVec2, neighbors: u8) -> Option { + println!("l:{last}"); + println!("c:{current}"); + println!("n:{neighbors}"); + match neighbors { + 253 | 169..=171 | 40 | 38 | 32 => current.x += 1, + 254 | 85..=87 | 16 => current.x -= 1, + 251 | 110 | 106 | 102 | 64 => current.y -= 1, + 247 | 153 | 157 | 149 | 129 | 128 => current.y += 1, + 233 | 191 | 189 | 187 | 185 | 127 | 126 | 119 | 118 | 58 | 56 | 52..=54 | 48..=50 => { + match last.x.cmp(¤t.x) { + Greater | Equal => current.x -= 1, + Less => current.x += 1, + } + } + 239 | 238 | 235 | 234 | 223 | 221 | 215 | 213 | 207 | 201 | 200 | 196..=198 | 192..=194 => { + match last.y.cmp(¤t.y) { + Greater | Equal => current.y -= 1, + Less => current.y += 1, + } + } + 168 | 162 | 160 => match last.x.cmp(¤t.x) { + Greater => current.y += 1, + Equal => current.x += 1, + Less => unreachable!(), + }, + 145 | 144 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => current.x -= 1, + Less => current.y += 1, + }, + 90 | 84 | 80 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => current.x -= 1, + Less => current.y -= 1, + }, + 98 | 96 => match last.x.cmp(¤t.x) { + Greater => current.y -= 1, + Equal => current.x += 1, + Less => unreachable!(), + }, + 177 => match last.x.cmp(¤t.x) { + Greater => { + current.y += 1; + return Some(current.with_y(current.y - 1)); + } + Equal => unreachable!(), + Less => current.x += 1, + }, + 184 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => { + current.x -= 1; + return Some(current.with_x(current.x + 1)); + } + Less => current.x += 1, + }, + 212 => match last.y.cmp(¤t.y) { + Greater => { + current.x -= 1; + return Some(current.with_x(current.x + 1)); + } + Equal => unreachable!(), + Less => current.y += 1, + }, + 209 => match last.y.cmp(¤t.y) { + Greater => unreachable!(), + Equal => { + current.y -= 1; + return Some(current.with_y(current.y + 1)); + } + Less => current.y += 1, + }, + 114 => match last.x.cmp(¤t.x) { + Greater => current.x -= 1, + Equal => unreachable!(), + Less => { + current.y -= 1; + return Some(current.with_y(current.y + 1)); + } + }, + 116 => match last.x.cmp(¤t.x) { + Greater => current.x -= 1, + Equal => { + current.x += 1; + return Some(current.with_x(current.x - 1)); + } + Less => unreachable!(), + }, + 232 => match last.y.cmp(¤t.y) { + Greater => current.y -= 1, + Equal => unreachable!(), + Less => { + current.x += 1; + return Some(current.with_x(current.x - 1)); + } + }, + 226 => match last.y.cmp(¤t.y) { + Greater => current.y -= 1, + Equal => { + current.y -= 1; + return Some(current.with_y(current.y + 1)); + } + Less => unreachable!(), + }, + 240 => match (last.x.cmp(¤t.x), last.y.cmp(¤t.y)) { + (Less, Equal) => current.y -= 1, + (Equal, Less) => current.x += 1, + (Greater, Equal) => current.y += 1, + (Equal, Greater) => current.x -= 1, + _ => unreachable!(), + }, + 249 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => current.x += 1, + Less => current.y -= 1, + }, + 115 | 112 => match last.x.cmp(¤t.x) { + Greater => current.x -= 1, + Equal => current.x += 1, + Less => current.y -= 1, + }, + 246 => match last.x.cmp(¤t.x) { + Greater => current.y += 1, + Equal => current.x -= 1, + Less => current.x += 1, + }, + 186 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => current.x -= 1, + Less => { + current.x += 1; + return Some(current.with_x(current.x - 1)); + } + }, + 231 => match last.x.cmp(¤t.x) { + Greater => current.y += 1, + Equal => current.y -= 1, + Less => unreachable!(), + }, + 8 => { + current.x += 1; + current.y += 1; + } + 12 => match last.x.cmp(¤t.x) { + Greater => { + current.x -= 1; + current.y -= 1; + } + Equal => unreachable!(), + Less => { + current.x += 1; + current.y += 1; + } + }, + 44 => match last.x.cmp(¤t.x) { + Greater => { + current.x -= 1; + current.y -= 1; + } + Equal => unreachable!(), + Less => current.x += 1, + }, + 24 => match last.x.cmp(¤t.x) { + Greater => current.x -= 1, + Equal => unreachable!(), + Less => { + current.x += 1; + current.y += 1; + } + }, + 132 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => { + current.x -= 1; + current.y -= 1; + } + Less => current.y += 1, + }, + 103 => match last.x.cmp(¤t.x) { + Greater => { + current.x -= 1; + current.y += 1; + } + Equal => unreachable!(), + Less => current.y -= 1, + }, + 2 => { + current.x += 1; + current.y -= 1; + } + 67 | 65 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => { + current.x -= 1; + current.y += 1; + } + Less => current.y -= 1, + }, + 3 => match last.x.cmp(¤t.x) { + Greater => { + current.x -= 1; + current.y += 1; + } + Equal => unreachable!(), + Less => { + current.x += 1; + current.y -= 1; + } + }, + 22 | 18 => match last.x.cmp(¤t.x) { + Greater => current.x -= 1, + Equal => unreachable!(), + Less => { + current.x += 1; + current.y -= 1; + } + }, + 7 => match (last.x.cmp(¤t.x), last.y.cmp(¤t.y)) { + (Greater, Less) => { + current.x -= 1; + current.y += 1; + } + (Less, Greater) => { + current.x -= 1; + current.y -= 1; + } + (Less, Less) => { + current.x += 1; + current.y -= 1; } + _ => unreachable!(), + }, + 0 | 255 => unreachable!(), + _ => { + unreachable!() } } + None } From ac0c3ddcde59c036fa55c59e3a9880d77e348ae2 Mon Sep 17 00:00:00 2001 From: salam Date: Fri, 1 Nov 2024 21:18:33 +0900 Subject: [PATCH 07/15] added more handlers --- src/utils.rs | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index c6f783d..d82eabe 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -69,23 +69,19 @@ pub fn is_corner(neighbors: u8) -> bool { | 114 | 58 | 56 - | 52..=54 - | 48..=50 + | 48..=54 | 0 ) } #[allow(clippy::too_many_lines)] pub fn handle_neighbors(current: &mut UVec2, last: UVec2, neighbors: u8) -> Option { - println!("l:{last}"); - println!("c:{current}"); - println!("n:{neighbors}"); match neighbors { 253 | 169..=171 | 40 | 38 | 32 => current.x += 1, 254 | 85..=87 | 16 => current.x -= 1, 251 | 110 | 106 | 102 | 64 => current.y -= 1, 247 | 153 | 157 | 149 | 129 | 128 => current.y += 1, - 233 | 191 | 189 | 187 | 185 | 127 | 126 | 119 | 118 | 58 | 56 | 52..=54 | 48..=50 => { + 233 | 191 | 189 | 187 | 185 | 127 | 126 | 119 | 118 | 58 | 56 | 48..=54 => { match last.x.cmp(¤t.x) { Greater | Equal => current.x -= 1, Less => current.x += 1, @@ -107,7 +103,7 @@ pub fn handle_neighbors(current: &mut UVec2, last: UVec2, neighbors: u8) -> Opti Equal => current.x -= 1, Less => current.y += 1, }, - 90 | 84 | 80 => match last.x.cmp(¤t.x) { + 90 | 84 | 82 | 80 => match last.x.cmp(¤t.x) { Greater => unreachable!(), Equal => current.x -= 1, Less => current.y -= 1, @@ -309,9 +305,30 @@ pub fn handle_neighbors(current: &mut UVec2, last: UVec2, neighbors: u8) -> Opti } _ => unreachable!(), }, + 36 => match last.x.cmp(¤t.x) { + Greater => { + current.x -= 1; + current.y -= 1; + } + Equal => unreachable!(), + Less => current.x += 1, + }, + 74 => match last.x.cmp(¤t.x) { + Greater => current.y -= 1, + Equal => { + current.x += 1; + current.y += 1; + } + Less => unreachable!(), + } + 250 => match last.y.cmp(¤t.y) { + Greater => current.x -= 1, + Equal => current.y -= 1, + Less => current.y += 1, + } 0 | 255 => unreachable!(), _ => { - unreachable!() + todo!("\nadd handle for this case:\nlast point:{last}\ncurrent point:{current}\ncurrent neighbors:{neighbors}") } } None From 27542a311e5d7f4a29adea40f69a7617739b682f Mon Sep 17 00:00:00 2001 From: salam Date: Sun, 10 Nov 2024 09:54:07 +0900 Subject: [PATCH 08/15] add: new image for test --- assets/diagonals.png | Bin 0 -> 1108 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/diagonals.png diff --git a/assets/diagonals.png b/assets/diagonals.png new file mode 100644 index 0000000000000000000000000000000000000000..d6307147e4da6f2704f749896d0012717d06f50e GIT binary patch literal 1108 zcmV-a1grarP)Px(4oO5oRCt{2oJ&^ZFbqWlht04NHpQ4tu@RO-PdXe^_$TX0dUA3neW+AyeQa5l z-E|yBe7%2uTeZ#3s_tesyRtJ*zTQ8-C3d)-;l#)uUB_vSc{`&{%jmVwo@!ru{doKH z`AnxP?^&ZABuJe~8;R^CRU-S$IOX@yL%O#7dJ*|OZ)W6rG*0Usl#VlfO*gReJd#z} zXJb7=o~7AhJ!-7z$tQiL(RIe?YNxO}<2ZB!38k-eA0q0egI_MkL+HrcyG&y17VDy~Vbi3Pee z@jta)JDZFrCZ}PwzoTF}F?1vA=5b55jlUVme$2ZS=!+1y(-c@3kM1a)( zYZ^h7q|q~tpv1s!xaHJWkj@^xovybmR?MzZ6goeaty|A_py!w{U2CQ4D>>yj_Q!WMqk1(ODvKG&3=O_jGcHm z#WHhCXYfd@Q)nw^Y_*2W?v@dx+RS=SSEdamiyXi35{k(Yv@l%f$xNCx#B+VvhHPmo zBeXMwVn-VQokOnbh-qfS#g)QJIB9NJT#3=*dOIWt%A?6e80BvM7=MeIAK=eKQ~?q$ z3)NZi0=>IqR{bEpH*7@H(8{}rGZgQzJ6e08eS13PaaOL((x}9wW+!-msA<2FR<9_> zkiMIS`kGNtAw=>$be#5bid(~;PO&nw^*Ow0xLSFxc}J?0bd9mPp<=p*MBL;+eDBfV zBL^V#G|#da^7h%b5F_3ri-c?ntzg`2T83avAG#OG`jEmXldDjS2C$Y%TwJ_5DT}M< zg&EZr5>~mI+)g3H%PHnXyPZH8XSOnMJ7WTGr`$|%)x8Tf9wnFCDMH{!mdCxQL$Gkn a4*UZ$5oLhfEH2Cd0000 Date: Sun, 10 Nov 2024 09:54:41 +0900 Subject: [PATCH 09/15] add: diagonals.png to example --- examples/bevy-image.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/examples/bevy-image.rs b/examples/bevy-image.rs index 218a169..3e552f8 100644 --- a/examples/bevy-image.rs +++ b/examples/bevy-image.rs @@ -32,8 +32,19 @@ fn main() { ) .unwrap(); + let diagonals = Image::from_buffer( + include_bytes!("../assets/diagonals.png"), + ImageType::Extension("png"), + CompressedImageFormats::default(), + true, + ImageSampler::default(), + RenderAssetUsages::default(), + ) + .unwrap(); + draw_png(boulders, "boulders"); draw_png(more_lines, "more-lines"); + draw_png(diagonals, "diagonals"); } fn draw_png(image: Image, img_path: &str) { From b1028ece2c11bd08cf5e803f502fd585e09bfb93 Mon Sep 17 00:00:00 2001 From: salam Date: Sun, 10 Nov 2024 09:55:03 +0900 Subject: [PATCH 10/15] removed: broken square.png --- assets/broken-square.png | Bin 296 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 assets/broken-square.png diff --git a/assets/broken-square.png b/assets/broken-square.png deleted file mode 100644 index dd2a5c0f4d40d922898e97485b5667cfdb810c77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 296 zcmV+@0oVSCP)Px#;z>k7R9J=OS3wd3Aqaz={{PGLl9SsNz~Bk!f-C&W75Y z0UBH3-2%4wdI3yvF@c~`2O!01=K)!fi%5CvMjlul%aQ$CVzTj#$SYysDnS)Ybnh;3 zxpun%?Um_9bax?|Jn->Kh`tk^Txh2x)J3O~ct;DcNr=q&0W0XGXkF0&oo&Hk>pgBF uVSa}p$-2{<+*`TjhrSE43Eg_U{~n&c=rKA7{t{0B0000 Date: Sun, 10 Nov 2024 09:56:20 +0900 Subject: [PATCH 11/15] neighbors moved to mod-block into bin_image module --- src/bin_image/mod.rs | 50 +++++++++++++++++++++++++++++++++++--- src/bin_image/neighbors.rs | 8 ------ 2 files changed, 47 insertions(+), 11 deletions(-) delete mode 100644 src/bin_image/neighbors.rs diff --git a/src/bin_image/mod.rs b/src/bin_image/mod.rs index 8dd736b..90595d3 100644 --- a/src/bin_image/mod.rs +++ b/src/bin_image/mod.rs @@ -1,5 +1,14 @@ use crate::{UVec2, Vec2}; -pub mod neighbors; +pub mod neighbors { + pub const NORTH: u8 = 0b1000_0000; + pub const SOUTH: u8 = 0b0100_0000; + pub const EAST: u8 = 0b0010_0000; + pub const WEST: u8 = 0b0001_0000; + pub const NORTHEAST: u8 = 0b0000_1000; + pub const NORTHWEST: u8 = 0b0000_0100; + pub const SOUTHEAST: u8 = 0b0000_0010; + pub const SOUTHWEST: u8 = 0b0000_0001; +} pub struct BinImage { data: Vec, @@ -94,18 +103,53 @@ impl BinImage { if x < u32::MAX && y < u32::MAX && self.get(UVec2::new(x + 1, y + 1)) { neighbors |= neighbors::NORTHEAST; } - if x > u32::MIN && y > u32::MIN && self.get(UVec2::new(x - 1, y - 1)) { + if x > u32::MIN && y < u32::MAX && self.get(UVec2::new(x - 1, y + 1)) { neighbors |= neighbors::NORTHWEST; } if x < u32::MAX && y > u32::MIN && self.get(UVec2::new(x + 1, y - 1)) { neighbors |= neighbors::SOUTHEAST; } - if x > u32::MIN && y < u32::MAX && self.get(UVec2::new(x - 1, y + 1)) { + if x > u32::MIN && y > u32::MIN && self.get(UVec2::new(x - 1, y - 1)) { neighbors |= neighbors::SOUTHWEST; } neighbors } + pub fn is_corner(&self, p: UVec2) -> bool { + !matches!( + self.get_neighbors(p), + 255 + | 239 + | 238 + | 234..=236 + | 231 + | 223 + | 221 + | 215 + | 213 + | 212 + | 209 + | 204 + | 201 + | 195..=197 + | 188..=192 + | 186 + | 184 + | 181 + | 180 + | 127 + | 123 + | 119 + | 118 + | 113..=115 + | 58 + | 56 + | 52..=54 + | 48..=50 + | 0 + ) + } + /// Translates a point in positive (x, y) coordinates to a coordinate system centered at (0, 0). /// /// # Arguments diff --git a/src/bin_image/neighbors.rs b/src/bin_image/neighbors.rs deleted file mode 100644 index 311d9cb..0000000 --- a/src/bin_image/neighbors.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub const NORTH: u8 = 0b1000_0000; -pub const SOUTH: u8 = 0b0100_0000; -pub const EAST: u8 = 0b0010_0000; -pub const WEST: u8 = 0b0001_0000; -pub const NORTHEAST: u8 = 0b0000_1000; -pub const NORTHWEST: u8 = 0b0000_0100; -pub const SOUTHEAST: u8 = 0b0000_0010; -pub const SOUTHWEST: u8 = 0b0000_0001; From df77244fc05604334285ce426b7186030a61ee7b Mon Sep 17 00:00:00 2001 From: salam Date: Sun, 10 Nov 2024 22:15:05 +0900 Subject: [PATCH 12/15] add: rayon to dependecies --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 80f3444..088d505 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,12 +19,13 @@ cast_precision_loss = { level = "allow", priority = 1 } pedantic = { level = "warn", priority = 0 } [features] -default = ["glam-latest"] +default = ["bevy"] glam-latest = ["dep:glam"] bevy = ["dep:bevy_math", "dep:bevy_render"] [dependencies] image = "0.25" +rayon = "1.10.0" [dependencies.glam] version = "0.29" From cb7a3b8231940f10e0cf323572ce10fa94a0ba0d Mon Sep 17 00:00:00 2001 From: salam Date: Sun, 10 Nov 2024 22:16:46 +0900 Subject: [PATCH 13/15] bin_image/mod.rs moved to src/ and renamed to bin_image.rs --- src/{bin_image/mod.rs => bin_image.rs} | 50 ++++++-------------------- 1 file changed, 11 insertions(+), 39 deletions(-) rename src/{bin_image/mod.rs => bin_image.rs} (82%) diff --git a/src/bin_image/mod.rs b/src/bin_image.rs similarity index 82% rename from src/bin_image/mod.rs rename to src/bin_image.rs index 90595d3..f2db458 100644 --- a/src/bin_image/mod.rs +++ b/src/bin_image.rs @@ -1,4 +1,5 @@ -use crate::{UVec2, Vec2}; +use crate::{utils::is_corner, UVec2, Vec2}; +use rayon::prelude::*; pub mod neighbors { pub const NORTH: u8 = 0b1000_0000; pub const SOUTH: u8 = 0b0100_0000; @@ -37,10 +38,10 @@ impl BinImage { let compress_step = data.len() / (height * width) as usize; Self { data: data - .chunks(8 * compress_step) + .par_chunks(8 * compress_step) .map(|chunk| { chunk - .chunks(compress_step) + .par_chunks(compress_step) .map(|chunk| chunk.iter().any(|i| *i != 0)) .enumerate() .map(|(index, bit)| u8::from(bit) << index) @@ -62,15 +63,17 @@ impl BinImage { /// /// Returns `true` if the pixel is "on" (1), and `false` if it is "off" (0) or out of bounds. pub fn get(&self, p: UVec2) -> bool { - let (x, y) = (p.x, p.y); - let index = y * self.width + x; + if p.x >= self.width { + return false; + } + let index = p.y * self.width + p.x; if let Some(mut byte) = self .data .get((index / 8) as usize) // index of byte .copied() { byte >>= index % 8; // index of bit - x < self.width && byte & 1 > 0 + byte & 1 > 0 } else { false } @@ -116,38 +119,7 @@ impl BinImage { } pub fn is_corner(&self, p: UVec2) -> bool { - !matches!( - self.get_neighbors(p), - 255 - | 239 - | 238 - | 234..=236 - | 231 - | 223 - | 221 - | 215 - | 213 - | 212 - | 209 - | 204 - | 201 - | 195..=197 - | 188..=192 - | 186 - | 184 - | 181 - | 180 - | 127 - | 123 - | 119 - | 118 - | 113..=115 - | 58 - | 56 - | 52..=54 - | 48..=50 - | 0 - ) + is_corner(self.get_neighbors(p)) } /// Translates a point in positive (x, y) coordinates to a coordinate system centered at (0, 0). @@ -176,7 +148,7 @@ impl BinImage { /// /// A vector of `Vec2` representing the translated coordinates. pub fn translate(&self, v: Vec) -> Vec { - v.into_iter().map(|p| self.translate_point(p)).collect() + v.into_par_iter().map(|p| self.translate_point(p)).collect() } pub const fn height(&self) -> u32 { From 8d38555bfa9252a8fe70c799fc68653780641232 Mon Sep 17 00:00:00 2001 From: salam Date: Sun, 10 Nov 2024 22:19:13 +0900 Subject: [PATCH 14/15] reworking of edge collecting algorithm finished --- src/lib.rs | 80 +++++---- src/utils.rs | 496 ++++++++++++++++++++++++--------------------------- 2 files changed, 287 insertions(+), 289 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 746f721..6abfdca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,10 +3,11 @@ use crate::bin_image::BinImage; #[cfg(feature = "bevy")] pub use bevy_math::prelude::{UVec2, Vec2}; -#[cfg(not(feature = "bevy"))] +#[cfg(all(not(feature = "bevy"), feature = "glam-latest"))] pub use glam::{UVec2, Vec2}; +use rayon::prelude::*; use std::fmt; -use utils::{handle_neighbors, in_polygon, is_corner}; +use utils::{handle_neighbors, in_polygon, Direction}; mod bin_image; #[cfg(feature = "bevy")] @@ -30,14 +31,14 @@ impl Edges { /// coordinates translated to either side of (0, 0) #[must_use] pub fn single_image_edge_translated(&self) -> Vec { - self.image_edges(true).into_iter().flatten().collect() + self.image_edges(true).into_par_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 #[must_use] pub fn single_image_edge_raw(&self) -> Vec { - self.image_edges(false).into_iter().flatten().collect() + self.image_edges(false).into_par_iter().flatten().collect() } /// If there's more than one sprite / object in the image, this returns all it finds, with @@ -63,19 +64,20 @@ impl Edges { // any that have at least one transparent / zero value neighbor then, while sorting into drawing // order, groups them into sets of connected pixels let corners: Vec<_> = (0..image.height() * image.width()) + .into_par_iter() .map(|i| UVec2::new(i / image.height(), i % image.height())) - .filter(|p| image.get(*p) && is_corner(image.get_neighbors(*p))) + .filter(|p| image.get(*p) && image.is_corner(*p)) .collect(); let objects: Vec<_> = self .collect_objects(&corners) - .into_iter() - .map(|object| object.into_iter().map(|p| p.as_vec2()).collect()) + .into_par_iter() + .map(|object| object.into_par_iter().map(|p| p.as_vec2()).collect()) .collect(); if translate { objects - .into_iter() - .map(|group| self.translate(group)) + .into_par_iter() + .map(|object| self.translate(object)) .collect() } else { objects @@ -91,33 +93,49 @@ impl Edges { while let Some(start) = corners.iter().find(|point| { objects - .iter() - .all(|object| !object.contains(point) && !in_polygon(**point, object)) + .par_iter() + .all(|object| !(object.contains(point) || in_polygon(**point, object))) }) { - let object = self.collect_object(*start); + let mut current = *start; + let mut group: Vec = Vec::new(); + group.push(current); + let object = loop { + let (last, neighbors) = (*group.last().unwrap(), self.image.get_neighbors(current)); + if last != current { + group.push(current); + } + match handle_neighbors(current, last, neighbors) { + Direction::North => current.y += 1, + Direction::South => current.y -= 1, + Direction::East => current.x += 1, + Direction::West => current.x -= 1, + Direction::Northeast => { + current.x += 1; + current.y += 1; + } + Direction::Northwest => { + current.x -= 1; + current.y += 1; + } + Direction::Southeast => { + current.x += 1; + current.y -= 1; + } + Direction::Southwest => { + current.x -= 1; + current.y -= 1; + } + } + if current == *start { + break group; + } + }; objects.push(object); } objects } - fn collect_object(&self, mut current: UVec2) -> Vec { - let mut group: Vec = Vec::new(); - group.push(current); - loop { - let (last, neighbors) = (*group.last().unwrap(), self.image.get_neighbors(current)); - if last != current && is_corner(neighbors) { - group.push(current); - } - if let Some(point) = handle_neighbors(&mut current, last, neighbors) { - group.push(point); - } - if current == group[0] { - break group; - } - } - } - /// Translates an `Vec` of points in positive (x, y) coordinates to a coordinate system centered at (0, 0). /// /// # Arguments @@ -163,9 +181,9 @@ impl fmt::Debug for Edges { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "{}", + "Edges {{{}\n}}", format!( - "Edges {{\nraw: {:#?},\ntranslated: {:#?}\n}}", + "\nraw: {:#?},\ntranslated: {:#?}", self.image_edges(false), self.image_edges(true), ) diff --git a/src/utils.rs b/src/utils.rs index d82eabe..c67a4f2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,37 +1,35 @@ use crate::UVec2; use std::cmp::Ordering::{Equal, Greater, Less}; +// Get the bounding box of the polygon +fn bounding_box(polygon: &[UVec2]) -> Option<(UVec2, UVec2)> { + polygon + .iter() + .copied() + .zip(polygon.iter().copied()) + .reduce(|(min, max), (a, b)| (min.min(a), max.max(b))) +} + pub fn in_polygon(point: UVec2, polygon: &[UVec2]) -> bool { - let mut is_inside = false; - for win in polygon.windows(2) { - let (p1, p2) = (win[0], win[1]); - if (p1.x.min(p2.x) <= point.x && point.x <= p1.x.max(p2.x)) - && (p1.y.min(p2.y) <= point.y && point.y <= p1.y.max(p2.y)) - && (p1.y.max(p2.y) - p1.y.min(p2.y)) * (point.x - p1.x.min(p2.x)) - == (p2.x.max(p2.x) - p1.x.min(p2.x)) * (point.y - p1.y.min(p2.y)) - { - return true; + if let Some((min, max)) = bounding_box(polygon) { + // Check if the point is within the bounding box + if point.x < min.x || point.x > max.x || point.y < min.y || point.y > max.y { + return false; // Early exit if outside the bounding box } + } + + let mut is_inside = false; - if p1.y <= point.y && point.y < p2.y || p2.y <= point.y && point.y < p1.y { - let (point_x, offset_x) = point - .x - .checked_sub(p1.x) - .map_or_else(|| (0, p1.x - point.x), |dx| (dx, 0)); - let (point_y, offset_y) = point - .y - .checked_sub(p1.y) - .map_or_else(|| (0, p1.y - point.y), |dy| (dy, 0)); + for i in 0..polygon.len() { + let (p1, p2) = (polygon[i], polygon[(i + 1) % polygon.len()]); + let (min, max) = (p1.min(p2), p1.max(p2)); + let (dy, dx) = (max.y - min.y, max.x - min.x); - let (dx, offset_x) = (p2.x + offset_x) - .checked_sub(p1.x) - .map_or_else(|| (0, p1.x - p2.x - offset_x), |dx| (dx, 0)); - let (dy, offset_y) = (p2.y + offset_y) - .checked_sub(p1.y) - .map_or_else(|| (0, p1.y - p2.y - offset_y), |dy| (dy, 0)); - if (point_x + offset_x) * dy >= dx * (point_y + offset_y) { - is_inside = !is_inside; + if min.y <= point.y && point.y < max.y && point.x <= min.x + dx * (point.y - min.y) / dy { + if min.x <= point.x && point.x < max.x { + return true; } + is_inside = !is_inside; } } is_inside @@ -43,293 +41,275 @@ pub fn is_corner(neighbors: u8) -> bool { 255 | 239 | 238 - | 232..=235 - | 127 - | 226 + | 235 + | 234 | 223 | 221 | 215 | 213 - | 212 - | 209 - | 207 - | 201 - | 200 - | 196..=198 - | 191..=194 - | 189 - | 187 - | 185 - | 184 - | 177 - | 126 + | 188..=207 + | 127 + | 123 | 119 - | 118 - | 116 - | 114 - | 58 - | 56 - | 48..=54 + | 115 + | 48..=63 + | 9 + | 6 | 0 ) } +pub enum Direction { + North, + South, + East, + West, + + Northeast, + Northwest, + Southeast, + Southwest, +} + #[allow(clippy::too_many_lines)] -pub fn handle_neighbors(current: &mut UVec2, last: UVec2, neighbors: u8) -> Option { +pub fn handle_neighbors(current: UVec2, last: UVec2, neighbors: u8) -> Direction { + use Direction::{East, North, Northeast, Northwest, South, Southeast, Southwest, West}; match neighbors { - 253 | 169..=171 | 40 | 38 | 32 => current.x += 1, - 254 | 85..=87 | 16 => current.x -= 1, - 251 | 110 | 106 | 102 | 64 => current.y -= 1, - 247 | 153 | 157 | 149 | 129 | 128 => current.y += 1, - 233 | 191 | 189 | 187 | 185 | 127 | 126 | 119 | 118 | 58 | 56 | 48..=54 => { - match last.x.cmp(¤t.x) { - Greater | Equal => current.x -= 1, - Less => current.x += 1, - } - } - 239 | 238 | 235 | 234 | 223 | 221 | 215 | 213 | 207 | 201 | 200 | 196..=198 | 192..=194 => { - match last.y.cmp(¤t.y) { - Greater | Equal => current.y -= 1, - Less => current.y += 1, - } - } - 168 | 162 | 160 => match last.x.cmp(¤t.x) { - Greater => current.y += 1, - Equal => current.x += 1, + 0 | 255 => unreachable!(), + 188..=191 | 127 | 123 | 119 | 115 | 48..=63 => match last.x.cmp(¤t.x) { + Greater => West, + Equal => unreachable!(), + Less => East, + }, + 239 | 238 | 235 | 234 | 223 | 221 | 215 | 213 | 192..=207 => match last.y.cmp(¤t.y) { + Greater => South, + Equal => unreachable!(), + Less => North, + }, + 6 => match last.x.cmp(¤t.x) { + Greater => Northwest, + Equal => unreachable!(), + Less => Southeast, + }, + 9 => match last.x.cmp(¤t.x) { + Greater => Southwest, + Equal => unreachable!(), + Less => Northeast, + }, + + 140 | 136 | 132 | 128 => North, + 99 | 98 | 64..=67 => South, + 42 | 40 | 34 | 32 => East, + 21 | 20 | 17 | 16 => West, + 8 => Northeast, + 4 => Northwest, + 2 => Southeast, + 1 => Southwest, + 247 | 245 | 174 | 172 | 170 | 168 | 166 | 164 | 162 | 160 => match last.x.cmp(¤t.x) { + Greater => North, + Equal => East, + Less => unreachable!(), + }, + 253 | 104..=107 | 97 | 96 => match last.x.cmp(¤t.x) { + Greater => South, + Equal => East, Less => unreachable!(), }, - 145 | 144 => match last.x.cmp(¤t.x) { + 251 | 157 | 156 | 153 | 152 | 149 | 148 | 145 | 144 => match last.x.cmp(¤t.x) { Greater => unreachable!(), - Equal => current.x -= 1, - Less => current.y += 1, + Equal => West, + Less => North, + }, + 254 | 250 | 80..=87 => match last.x.cmp(¤t.x) { + Greater => unreachable!(), + Equal => West, + Less => South, + }, + 180..=182 => match last.x.cmp(¤t.x) { + Greater => North, + Equal => unreachable!(), + Less => East, }, - 90 | 84 | 82 | 80 => match last.x.cmp(¤t.x) { + 186 | 184 => match last.x.cmp(¤t.x) { Greater => unreachable!(), - Equal => current.x -= 1, - Less => current.y -= 1, + Equal => West, + Less => East, }, - 98 | 96 => match last.x.cmp(¤t.x) { - Greater => current.y -= 1, - Equal => current.x += 1, + 231 | 226 => match last.x.cmp(¤t.x) { + Greater => North, + Equal => South, Less => unreachable!(), }, - 177 => match last.x.cmp(¤t.x) { - Greater => { - current.y += 1; - return Some(current.with_y(current.y - 1)); - } + 236 | 232 => match last.y.cmp(¤t.y) { + Greater => South, Equal => unreachable!(), - Less => current.x += 1, + Less => East, }, - 184 => match last.x.cmp(¤t.x) { - Greater => unreachable!(), - Equal => { - current.x -= 1; - return Some(current.with_x(current.x + 1)); + 249 | 248 | 246 | 244 | 240..=242 => { + match (last.x.cmp(¤t.x), last.y.cmp(¤t.y)) { + (Less, Equal) => South, + (Equal, Less) => East, + (Greater, Equal) => North, + (Equal, Greater) => West, + _ => unreachable!(), } - Less => current.x += 1, + } + + 110 | 103 | 102 => match last.x.cmp(¤t.x) { + Greater => Northwest, + Equal => unreachable!(), + Less => South, }, - 212 => match last.y.cmp(¤t.y) { - Greater => { - current.x -= 1; - return Some(current.with_x(current.x + 1)); - } + 111 | 109 | 108 | 101 | 100 => match last.x.cmp(¤t.x) { + Greater => Northwest, + Equal => East, + Less => South, + }, + 46 | 44 | 38 | 36 => match last.x.cmp(¤t.x) { + Greater => Northwest, + Equal => unreachable!(), + Less => East, + }, + 43 | 41 | 35 | 33 => match last.x.cmp(¤t.x) { + Greater => Southwest, Equal => unreachable!(), - Less => current.y += 1, + Less => East, + }, + 175 | 173 | 171 | 169 | 167 | 165 | 163 | 161 => match last.x.cmp(¤t.x) { + Greater => North, + Equal => Southwest, + Less => East, }, - 209 => match last.y.cmp(¤t.y) { + 142 | 138 | 134 | 130 => match last.x.cmp(¤t.x) { + Greater => North, + Equal => Southeast, + Less => unreachable!(), + }, + 95 | 93 | 91 | 89 => match last.x.cmp(¤t.x) { + Greater => West, + Equal => Northeast, + Less => unreachable!(), + }, + 141 | 137 | 133 | 129 => match last.x.cmp(¤t.x) { Greater => unreachable!(), - Equal => { - current.y -= 1; - return Some(current.with_y(current.y + 1)); - } - Less => current.y += 1, + Equal => Southwest, + Less => North, + }, + 94 | 92 | 90 | 88 => match last.x.cmp(¤t.x) { + Greater => West, + Equal => Northeast, + Less => South, }, - 114 => match last.x.cmp(¤t.x) { - Greater => current.x -= 1, + 23 | 22 | 19 | 18 => match last.x.cmp(¤t.x) { + Greater => West, Equal => unreachable!(), - Less => { - current.y -= 1; - return Some(current.with_y(current.y + 1)); - } + Less => Southeast, }, - 116 => match last.x.cmp(¤t.x) { - Greater => current.x -= 1, - Equal => { - current.x += 1; - return Some(current.with_x(current.x - 1)); - } - Less => unreachable!(), + 159 | 158 | 155 | 154 | 151 | 150 | 147 | 146 => match last.x.cmp(¤t.x) { + Greater => North, + Equal => West, + Less => Southeast, }, - 232 => match last.y.cmp(¤t.y) { - Greater => current.y -= 1, + 29 | 28 | 25 | 24 => match last.x.cmp(¤t.x) { + Greater => West, Equal => unreachable!(), - Less => { - current.x += 1; - return Some(current.with_x(current.x - 1)); - } + Less => Northeast, }, - 226 => match last.y.cmp(¤t.y) { - Greater => current.y -= 1, - Equal => { - current.y -= 1; - return Some(current.with_y(current.y + 1)); - } + 72..=75 => match last.x.cmp(¤t.x) { + Greater => South, + Equal => Northeast, Less => unreachable!(), }, - 240 => match (last.x.cmp(¤t.x), last.y.cmp(¤t.y)) { - (Less, Equal) => current.y -= 1, - (Equal, Less) => current.x += 1, - (Greater, Equal) => current.y += 1, - (Equal, Greater) => current.x -= 1, - _ => unreachable!(), - }, - 249 => match last.x.cmp(¤t.x) { + 68..=71 => match last.x.cmp(¤t.x) { Greater => unreachable!(), - Equal => current.x += 1, - Less => current.y -= 1, + Equal => Northwest, + Less => South, }, - 115 | 112 => match last.x.cmp(¤t.x) { - Greater => current.x -= 1, - Equal => current.x += 1, - Less => current.y -= 1, + + 31 | 30 | 27 | 26 => match last.y.cmp(¤t.y) { + Greater => West, + Equal => Southeast, + Less => Northeast, }, - 246 => match last.x.cmp(¤t.x) { - Greater => current.y += 1, - Equal => current.x -= 1, - Less => current.x += 1, + 76..=79 => match last.x.cmp(¤t.x) { + Greater => Northwest, + Equal => Northeast, + Less => South, }, - 186 => match last.x.cmp(¤t.x) { - Greater => unreachable!(), - Equal => current.x -= 1, - Less => { - current.x += 1; - return Some(current.with_x(current.x - 1)); - } + 47 | 45 | 39 | 37 => match last.y.cmp(¤t.y) { + Greater => Southwest, + Equal => Northwest, + Less => East, }, - 231 => match last.x.cmp(¤t.x) { - Greater => current.y += 1, - Equal => current.y -= 1, - Less => unreachable!(), + 143 | 139 | 135 | 131 => match last.x.cmp(¤t.x) { + Greater => North, + Equal => Southwest, + Less => Southeast, + }, + 10 => match last.y.cmp(¤t.y) { + Greater | Equal => Southeast, + Less => Northeast, }, - 8 => { - current.x += 1; - current.y += 1; - } 12 => match last.x.cmp(¤t.x) { - Greater => { - current.x -= 1; - current.y -= 1; - } + Greater => Northwest, Equal => unreachable!(), - Less => { - current.x += 1; - current.y += 1; - } + Less => Northeast, }, - 44 => match last.x.cmp(¤t.x) { - Greater => { - current.x -= 1; - current.y -= 1; - } + 3 => match last.x.cmp(¤t.x) { + Greater => Southwest, Equal => unreachable!(), - Less => current.x += 1, + Less => Southeast, }, - 24 => match last.x.cmp(¤t.x) { - Greater => current.x -= 1, + 5 => match last.x.cmp(¤t.x) { + Greater => Southwest, Equal => unreachable!(), - Less => { - current.x += 1; - current.y += 1; - } + Less => Northwest, }, - 132 => match last.x.cmp(¤t.x) { - Greater => unreachable!(), - Equal => { - current.x -= 1; - current.y -= 1; - } - Less => current.y += 1, + 15 => match (last.x.cmp(¤t.x), last.y.cmp(¤t.y)) { + (Greater, Less) => Northeast, + (Greater, Greater) => Northwest, + (Less, Less) => Southeast, + (Less, Greater) => Southwest, + _ => unreachable!(), }, - 103 => match last.x.cmp(¤t.x) { - Greater => { - current.x -= 1; - current.y += 1; - } - Equal => unreachable!(), - Less => current.y -= 1, + + 252 | 124..=126 | 120..=122 | 116..=118 | 112..=114 => match last.x.cmp(¤t.x) { + Greater => West, + Equal => East, + Less => South, }, - 2 => { - current.x += 1; - current.y -= 1; - } - 67 | 65 => match last.x.cmp(¤t.x) { - Greater => unreachable!(), - Equal => { - current.x -= 1; - current.y += 1; - } - Less => current.y -= 1, + 243 | 187 | 185 | 183 | 176..=179 => match last.x.cmp(¤t.x) { + Greater => North, + Equal => West, + Less => East, }, - 3 => match last.x.cmp(¤t.x) { - Greater => { - current.x -= 1; - current.y += 1; - } - Equal => unreachable!(), - Less => { - current.x += 1; - current.y -= 1; - } + 222 | 216..=220 | 214 | 208..=212 => match last.y.cmp(¤t.y) { + Greater => West, + Equal => South, + Less => North, }, - 22 | 18 => match last.x.cmp(¤t.x) { - Greater => current.x -= 1, - Equal => unreachable!(), - Less => { - current.x += 1; - current.y -= 1; - } + 237 | 233 | 227..=230 | 225 | 224 => match last.y.cmp(¤t.y) { + Greater => South, + Equal => North, + Less => East, }, 7 => match (last.x.cmp(¤t.x), last.y.cmp(¤t.y)) { - (Greater, Less) => { - current.x -= 1; - current.y += 1; - } - (Less, Greater) => { - current.x -= 1; - current.y -= 1; - } - (Less, Less) => { - current.x += 1; - current.y -= 1; - } + (Greater, Less) => Northwest, + (Less, Less) => Southeast, + (Less, Greater) => Southwest, _ => unreachable!(), }, - 36 => match last.x.cmp(¤t.x) { - Greater => { - current.x -= 1; - current.y -= 1; - } - Equal => unreachable!(), - Less => current.x += 1, + 14 | 11 => match (last.x.cmp(¤t.x), last.y.cmp(¤t.y)) { + (Greater, Less) => Northeast, + (Less, Less) => Southeast, + (Greater, Greater) => Southwest, + _ => unreachable!(), + }, + 13 => match (last.x.cmp(¤t.x), last.y.cmp(¤t.y)) { + (Less, Greater) => Northeast, + (Less, Less) => Southeast, + (Greater, Greater) => Southwest, + _ => unreachable!(), }, - 74 => match last.x.cmp(¤t.x) { - Greater => current.y -= 1, - Equal => { - current.x += 1; - current.y += 1; - } - Less => unreachable!(), - } - 250 => match last.y.cmp(¤t.y) { - Greater => current.x -= 1, - Equal => current.y -= 1, - Less => current.y += 1, - } - 0 | 255 => unreachable!(), - _ => { - todo!("\nadd handle for this case:\nlast point:{last}\ncurrent point:{current}\ncurrent neighbors:{neighbors}") - } } - None } From 075d42b4bc15ef34e6b1e8b4b06afc7bbfd0418b Mon Sep 17 00:00:00 2001 From: salam Date: Sun, 10 Nov 2024 22:21:12 +0900 Subject: [PATCH 15/15] replaced pass by value with pass by reference --- examples/bevy-image.rs | 13 +++++++------ examples/dynamic-image.rs | 6 ++++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/examples/bevy-image.rs b/examples/bevy-image.rs index 3e552f8..1af434b 100644 --- a/examples/bevy-image.rs +++ b/examples/bevy-image.rs @@ -42,19 +42,20 @@ fn main() { ) .unwrap(); - draw_png(boulders, "boulders"); - draw_png(more_lines, "more-lines"); - draw_png(diagonals, "diagonals"); + draw_png(&boulders, "boulders"); + draw_png(&more_lines, "more-lines"); + draw_png(&diagonals, "diagonals"); } -fn draw_png(image: Image, img_path: &str) { +fn draw_png(image: &Image, img_path: &str) { + // get the image's edges + let edges = Edges::from(image); + let scale = 8; let (width, height) = ( i32::try_from(image.width()).expect("Image to wide.") * scale, i32::try_from(image.height()).expect("Image to tall.") * scale, ); - // get the image's edges - let edges = Edges::from(image); // draw the edges to a png let mut dt = DrawTarget::new(width, height); diff --git a/examples/dynamic-image.rs b/examples/dynamic-image.rs index 6bd4db7..3e1e544 100644 --- a/examples/dynamic-image.rs +++ b/examples/dynamic-image.rs @@ -9,8 +9,10 @@ fn main() { } fn draw_png(img_path: &str) { - let image = &image::open(Path::new(&format!("assets/{img_path}"))).unwrap(); - let edges = Edges::from(image); + let image = image::open(Path::new(&format!("assets/{img_path}"))).unwrap(); + // get the image's edges + let edges = Edges::from(&image); + let scale = 8; let (width, height) = ( i32::try_from(image.width()).expect("Image to wide.") * scale,