diff options
Diffstat (limited to 'packages/svgbob/src/buffer/cell_buffer/endorse.rs')
-rw-r--r-- | packages/svgbob/src/buffer/cell_buffer/endorse.rs | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/packages/svgbob/src/buffer/cell_buffer/endorse.rs b/packages/svgbob/src/buffer/cell_buffer/endorse.rs new file mode 100644 index 0000000..5d6e42f --- /dev/null +++ b/packages/svgbob/src/buffer/cell_buffer/endorse.rs @@ -0,0 +1,222 @@ +use crate::{ + fragment::{Bounds, Rect}, + Fragment, +}; + +/// if a group of fragment can be endorse as rect, return the bounds point for the +/// rectangle +pub fn endorse_rect(fragments: &Vec<Fragment>) -> Option<Rect> { + if is_rect(fragments) { + let is_any_broken = + fragments.iter().any(|fragment| fragment.is_broken()); + let all_points = fragments.iter().fold(vec![], |mut acc, frag| { + let (p1, p2) = frag.bounds(); + acc.push(p1); + acc.push(p2); + acc + }); + let min = all_points.iter().min(); + let max = all_points.iter().max(); + if let (Some(min), Some(max)) = (min, max) { + Some(Rect::new(*min, *max, false, is_any_broken)) + } else { + None + } + } else { + None + } +} + +/// group of fragments can be check if they form: +/// - rectangle +fn is_rect(fragments: &Vec<Fragment>) -> bool { + if fragments.len() == 4 { + let parallels = parallel_aabb_group(fragments); + if parallels.len() == 2 { + let (a1, a2) = parallels[0]; + let (b1, b2) = parallels[1]; + let line_a1 = fragments[a1].as_line().expect("expecting a line"); + let line_b1 = fragments[b1].as_line().expect("expecting a line"); + let line_a2 = fragments[a2].as_line().expect("expecting a line"); + let line_b2 = fragments[b2].as_line().expect("expecting a line"); + line_a1.is_touching_aabb_perpendicular(line_b1) + && line_a2.is_touching_aabb_perpendicular(line_b2) + } else { + false + } + } else { + false + } +} + +/// qualifications: +/// - 8 fragments +/// - 2 parallell pair +/// - 4 aabb right angle arc (top_left, top_right, bottom_left, bottom_right) +/// - each of the right angle touches 2 lines that are aabb_perpendicular +pub fn endorse_rounded_rect(fragments: &Vec<Fragment>) -> Option<Rect> { + if let (true, arc_radius) = is_rounded_rect(fragments) { + let is_any_broken = + fragments.iter().any(|fragment| fragment.is_broken()); + let all_points = fragments.iter().fold(vec![], |mut acc, frag| { + let (p1, p2) = frag.bounds(); + acc.push(p1); + acc.push(p2); + acc + }); + let min = all_points.iter().min(); + let max = all_points.iter().max(); + if let (Some(min), Some(max)) = (min, max) { + //TODO: compute the radius from + Some(Rect::rounded_new( + *min, + *max, + false, + arc_radius.expect("expecting arc radius"), + is_any_broken, + )) + } else { + None + } + } else { + None + } +} + +fn is_rounded_rect(fragments: &Vec<Fragment>) -> (bool, Option<f32>) { + if fragments.len() == 8 { + let parallels = parallel_aabb_group(fragments); + let right_arcs = right_angle_arcs(fragments); + //TODO: throroughly check the arc composition to be top_left, top_right, bottom_left, + //bottom_right + if parallels.len() == 2 && right_arcs.len() == 4 { + let first_right_arc_index = right_arcs[0]; + let arc_fragment = &fragments[first_right_arc_index]; + let arc_radius = + arc_fragment.as_arc().expect("expecting an arc").radius; + let (a1, a2) = parallels[0]; + let (b1, b2) = parallels[1]; + let line_a1 = fragments[a1].as_line().expect("expecting a line"); + let line_b1 = fragments[b1].as_line().expect("expecting a line"); + let line_a2 = fragments[a2].as_line().expect("expecting a line"); + let line_b2 = fragments[b2].as_line().expect("expecting a line"); + let passed = line_a1.is_aabb_perpendicular(line_b1) + && line_a2.is_aabb_perpendicular(line_b2); + (passed, Some(arc_radius)) + } else { + (false, None) + } + } else { + (false, None) + } +} + +/// return the index of the fragments that are right angle arc +fn right_angle_arcs(fragments: &Vec<Fragment>) -> Vec<usize> { + fragments + .iter() + .enumerate() + .filter_map(|(index, frag)| { + if let Some(arc) = frag.as_arc() { + if arc.is_aabb_right_angle_arc() { + Some(index) + } else { + None + } + } else { + None + } + }) + .collect() +} + +/// return the indexes of the fragments that are aabb parallel +fn parallel_aabb_group(fragments: &Vec<Fragment>) -> Vec<(usize, usize)> { + let mut parallels = vec![]; + for (index1, frag1) in fragments.iter().enumerate() { + for (index2, frag2) in fragments.iter().enumerate() { + if index1 != index2 + && !parallels.iter().any(|(pair1, pair2)| { + index1 == *pair1 + || index1 == *pair2 + || index2 == *pair1 + || index2 == *pair2 + }) + && frag1.is_aabb_parallel(&frag2) + { + parallels.push((index1, index2)); + } + } + } + parallels +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{buffer::CellGrid, fragment::line}; + + #[test] + fn test_parallel_grouping() { + let a = CellGrid::a(); + let e = CellGrid::e(); + let u = CellGrid::u(); + let y = CellGrid::y(); + + let line_ae = line(a, e); + let line_uy = line(u, y); + let group = parallel_aabb_group(&vec![line_ae, line_uy]); + println!("group: {:#?}", group); + assert_eq!(group, vec![(0, 1)]) + } + + #[test] + fn test_parallel_grouping_with4() { + let a = CellGrid::a(); + let e = CellGrid::e(); + let u = CellGrid::u(); + let y = CellGrid::y(); + + let line_ae = line(a, e); + let line_uy = line(u, y); + let line_au = line(a, u); + let line_ey = line(e, y); + let group = parallel_aabb_group(&vec![ + line_ae.clone(), + line_au.clone(), + line_uy.clone(), + line_ey.clone(), + ]); + println!("group: {:#?}", group); + assert_eq!(group, vec![(0, 2), (1, 3)]); + + let rect = endorse_rect(&vec![ + line_ae.clone(), + line_au.clone(), + line_uy.clone(), + line_ey.clone(), + ]); + assert!(rect.is_some()); + assert_eq!(rect, Some(Rect::new(a, y, false, false))); + assert!(is_rect(&vec![line_ae, line_au, line_uy, line_ey])); + } + + #[test] + fn parallel_and_perpendicular_but_not_touching_should_not_be_rect() { + let a = CellGrid::a(); + let e = CellGrid::e(); + let u = CellGrid::u(); + let y = CellGrid::y(); + let g = CellGrid::g(); + let q = CellGrid::q(); + let i = CellGrid::i(); + let s = CellGrid::s(); + + let line_ae = line(a, e); + let line_uy = line(u, y); + let line_gq = line(g, q); + let line_is = line(i, s); + + assert!(!is_rect(&vec![line_ae, line_uy, line_gq, line_is])); + } +} |