diff options
author | Jovansonlee Cesar <ivanceras@gmail.com> | 2021-08-27 20:30:57 +0800 |
---|---|---|
committer | Jovansonlee Cesar <ivanceras@gmail.com> | 2021-08-27 20:30:57 +0800 |
commit | 990ff5fed437888ea8cc6ebb26479a2af9598de0 (patch) | |
tree | 7e6c2a66f5d5230f89a9ebc3f5a1a99d4fe54e9a | |
parent | dfb72d701bb5bd0e59e8bee4daa86d6861d3ac46 (diff) |
Fix some tests
-rw-r--r-- | svgbob/src/buffer/cell_buffer/cell.rs | 17 | ||||
-rw-r--r-- | svgbob/src/buffer/cell_buffer/span.rs | 106 | ||||
-rw-r--r-- | svgbob/src/map/circle_map.rs | 250 | ||||
-rw-r--r-- | svgbob/test_data/circles.bob | 221 |
4 files changed, 392 insertions, 202 deletions
diff --git a/svgbob/src/buffer/cell_buffer/cell.rs b/svgbob/src/buffer/cell_buffer/cell.rs index 0a5b82e..e2fa1ca 100644 --- a/svgbob/src/buffer/cell_buffer/cell.rs +++ b/svgbob/src/buffer/cell_buffer/cell.rs @@ -6,6 +6,7 @@ use parry2d::{ query::intersection_test, shape::{Polyline, Segment}, }; +use std::ops::{Add, Sub}; use std::{cmp, cmp::Ordering, fmt}; mod cell_grid; @@ -307,6 +308,22 @@ impl Cell { } } +impl Add for Cell { + type Output = Self; + + fn add(self, other: Self) -> Self::Output { + Self::new(self.x + other.x, self.y + other.y) + } +} + +impl Sub for Cell { + type Output = Self; + + fn sub(self, other: Self) -> Self::Output { + Self::new(self.x - other.x, self.y - other.y) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/svgbob/src/buffer/cell_buffer/span.rs b/svgbob/src/buffer/cell_buffer/span.rs index 199bf1a..d3fc338 100644 --- a/svgbob/src/buffer/cell_buffer/span.rs +++ b/svgbob/src/buffer/cell_buffer/span.rs @@ -26,6 +26,11 @@ impl Deref for Span { } } +pub struct Bounds { + top_left: Cell, + bottom_right: Cell, +} + impl DerefMut for Span { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 @@ -64,6 +69,17 @@ impl Span { self.extend_from_slice(&*other) } + /// paste the other Span at cell location `loc` + pub fn paste_at(&self, loc: Cell, other: &Self) -> Self { + let mut this = self.clone(); + for (cell, ch) in other.deref() { + this.push((*cell + loc, *ch)); + } + this.sort(); + this.dedup(); + this + } + /// returns the top_left most cell which aligns the top most and the left most cell. pub(crate) fn bounds(&self) -> Option<(Cell, Cell)> { if let Some((min_y, max_y)) = @@ -81,6 +97,14 @@ impl Span { } } + pub fn cell_bounds(&self) -> Option<Bounds> { + if let Some((top_left, top_right)) = self.bounds() { + Some(Bounds::new(top_left, top_right)) + } else { + None + } + } + /// shift the cells relative to the top_left most bound pub(crate) fn localize(self) -> Self { if let Some((tl, _br)) = self.bounds() { @@ -234,6 +258,31 @@ impl Span { } } +impl Bounds { + pub fn new(cell1: Cell, cell2: Cell) -> Self { + let (top_left, bottom_right) = Cell::rearrange_bound(cell1, cell2); + Self { + top_left, + bottom_right, + } + } + + pub fn top_left(&self) -> Cell { + self.top_left + } + + pub fn bottom_right(&self) -> Cell { + self.bottom_right + } + + pub fn top_right(&self) -> Cell { + Cell::new(self.bottom_right.x, self.top_left.y) + } + pub fn bottom_left(&self) -> Cell { + Cell::new(self.top_left.x, self.bottom_right.y) + } +} + /// create a property buffer for all the cells of this span impl<'p> Into<PropertyBuffer<'p>> for &Span { fn into(self) -> PropertyBuffer<'p> { @@ -509,7 +558,15 @@ mod tests { let (mut fragments, _groups) = span1.endorse(&Settings::default()); assert_eq!(fragments.len(), 2); + let circle = fragments.remove(0); + assert!(circle.is_circle()); + assert_eq!( + circle, + Fragment::Circle(Circle::new(Point::new(11.0, 5.0), 2.5, false)) + ); + let rect = fragments.remove(0); + dbg!(&rect); assert!(rect.is_rect()); assert_eq!( rect, @@ -520,13 +577,6 @@ mod tests { false )) ); - - let circle = fragments.remove(0); - assert!(circle.is_circle()); - assert_eq!( - circle, - Fragment::Circle(Circle::new(Point::new(11.0, 5.0), 2.5, false)) - ); } #[test] @@ -630,3 +680,45 @@ mod tests { assert_eq!(abs_since.start, Cell::new(28, 6)); } } + +#[cfg(test)] +mod test_arcs { + use super::*; + use crate::{ + buffer::{ + fragment::{Arc, Circle, Rect}, + CellBuffer, + }, + Point, + }; + + #[test] + fn test_endorse_arc() { + let art = r#" + .- + ( + "#; + let buffer = CellBuffer::from(art); + println!("buffer: {}", buffer); + let mut adjacents = buffer.group_adjacents(); + println!("There are {} adjacents", adjacents.len()); + assert_eq!(1, adjacents.len()); + let span = adjacents.remove(0); + let (top_left, _) = span.bounds().unwrap(); + assert_eq!(top_left, Cell::new(8, 1)); + let (mut fragments, _groups) = span.endorse(&Settings::default()); + for (i, frag) in fragments.iter().enumerate() { + println!("frag {}:\n{}", i, frag); + } + assert_eq!(fragments.len(), 1); + let arc = fragments.remove(0); + assert_eq!( + arc, + Fragment::Arc(Arc::new( + Point::new(11.0, 2.5), + Point::new(8.5, 5.0), + 2.5 + )) + ); + } +} diff --git a/svgbob/src/map/circle_map.rs b/svgbob/src/map/circle_map.rs index 5f48446..f58e08d 100644 --- a/svgbob/src/map/circle_map.rs +++ b/svgbob/src/map/circle_map.rs @@ -443,12 +443,25 @@ lazy_static! { }) ); - pub static ref ARC_SPAN: BTreeMap<Arc, Span> = BTreeMap::from_iter( - CIRCLE_MAP.iter().skip(3).flat_map(|(art, center, radius, edge_case, offset_center_y)|{ + /// top_left top top_right + /// p2 + /// arc2 | arc1 + /// | + /// left p3----+----- p1 right + /// | + /// arc3 | arc4 + /// p4 + /// bottom_left bottom bottom_right + /// + /// (diameter, quarter arcs) + pub static ref QUARTER_ARC_SPAN: BTreeMap<i32, [(Arc,Span);4]> = BTreeMap::from_iter( + CIRCLE_MAP.iter().skip(4).map(|(art, center, radius, edge_case, offset_center_y)|{ let span = circle_art_to_span(art); - let (top_left, bottom_right) = span.bounds().expect("must have bounds"); - let top_right = Cell::new(bottom_right.x, top_left.y); - let bottom_left = Cell::new(top_left.x, bottom_right.y); + let bounds = span.cell_bounds().expect("must have bounds"); + let top_left = bounds.top_left(); + let bottom_right = bounds.bottom_right(); + let top_right = bounds.top_right(); + let bottom_left = bounds.bottom_left(); let p1 = Point::new(center.x + radius, center.y); let p2 = Point::new(center.x, center.y - radius); @@ -491,130 +504,43 @@ lazy_static! { let arc1 = Arc::new(arc1_start, arc1_end, *radius); let arc2 = Arc::new(arc2_start, arc2_end, *radius); - let arc3 = Arc::new(arc3_start, arc3_end, *radius); + let arc3 = Arc::new(arc3_start, arc3_end, *radius); let arc4 = Arc::new(arc4_start, arc4_end, *radius); - vec![(arc1, span1), (arc2, span2), (arc3, span3), (arc4, span4)] - + let diameter = (*radius * 2.0) as i32; + (diameter, [(arc1, span1), (arc2, span2), (arc3, span3), (arc4, span4)]) }) ); + pub static ref HALF_ARC_SPAN: BTreeMap<i32,[(Arc,Span);4]> = BTreeMap::from_iter( + QUARTER_ARC_SPAN.iter().map(|(diameter, [(arc1, span1), (arc2, span2), (arc3, span3), (arc4, span4)])|{ + let radius = (diameter / 2) as f32; - /// Simplified version of fragment arcs derived from CIRCLE_MAP - /// Algorithm: - /// 1. Locate the cells corresponding to the 4 quadrant boundary of the circle - /// - /// top_left top top_right - /// p2 - /// arc2 | arc1 - /// | - /// left p3----+----- p1 right - /// | - /// arc3 | arc4 - /// p4 - /// bottom_left bottom bottom_right - /// - /// p1 = center.x + radius, center.y - /// p2 = center.x, center.y - radius - /// p3 = center.x - radius, center.y - /// p4 = center.x + radius, center.y + radius - /// - /// right,top,left,bottom, top_left, top_right,bottom_left,bottom_right are cells dervied from - /// bounds. - /// - /// assert that snapping points to cell corresponds respectively: - /// p1 -> right cell - /// p2 -> top cell - /// p3 -> left cell - /// p4 -> bottom cell - /// - /// 2. Locate the span of each quadrants - /// arc4 span is always center_cell, to bottom_right - /// arc2 span is conditional - /// if center cell lies on mid, the span coverage is inclusive of the center cell. - /// otherwise if the center cell lies on the edge, then the cell coverage is exclusive of - /// the center cell - /// - static ref FRAGMENTS_ARC: Vec<(Vec<Contacts>,fragment::Arc)> =Vec::from_iter( - CIRCLE_MAP.iter().skip(3).flat_map(|(art, center, radius, edge_case, offset_center_y)|{ - let cb = CellBuffer::from(*art); - let mut spans = cb.group_adjacents(); - assert_eq!(spans.len(), 1); - let span = spans.remove(0).localize(); - - let (start_bound, end_bound) = span.bounds().expect("There should be bounds"); - - let center_cell = center.cell(); - - let right = Cell::new(end_bound.x, center_cell.y); - let top = Cell::new(center_cell.x, start_bound.y); - let left = Cell::new(start_bound.x, center_cell.y); - let bottom = Cell::new(center_cell.x, end_bound.y); - - let top_left = Cell::new(left.x, top.y); - let top_right = Cell::new(right.x, top.y); - let bottom_left = Cell::new(left.x, bottom.y); - let bottom_right = Cell::new(right.x, bottom.y); - assert_eq!(top_left, start_bound); - assert_eq!(bottom_right, end_bound); - - - // include cx if the center lies on the horizontal midline - // include cy if the center lies on the veritcal midline - let cx_adjusted = if !center.is_edge_x(){ center_cell.x }else{ center_cell.x - 1 }; - let cy_adjusted = if !center.is_edge_y(){ center_cell.y }else{ center_cell.y - 1 }; - - let arc1_bounds = Cell::rearrange_bound(Cell::new(center_cell.x, cy_adjusted), top_right); - let arc2_bounds = Cell::rearrange_bound(top_left, Cell::new( cx_adjusted, cy_adjusted)); - let arc3_bounds = Cell::rearrange_bound(bottom_left, Cell::new(cx_adjusted, center_cell.y)); - let arc4_bounds = Cell::rearrange_bound(center_cell, bottom_right); - - let arc1_span = span.extract(arc1_bounds.0, arc1_bounds.1); - let arc2_span = span.extract(arc2_bounds.0, arc2_bounds.1); - let arc3_span = span.extract(arc3_bounds.0, arc3_bounds.1); - let arc4_span = span.extract(arc4_bounds.0, arc4_bounds.1); - - let p1 = Point::new(center.x + radius, center.y); - let p2 = Point::new(center.x, center.y - radius); - let p3 = Point::new(center.x - radius, center.y); - let p4 = Point::new(center.x, center.y + radius); - - let arc1_start = arc1_bounds.0.localize_point(p1); - let arc1_end = arc1_bounds.0.localize_point(p2); - - let arc2_start = arc2_bounds.0.localize_point(p2); - let arc2_end = arc2_bounds.0.localize_point(p3); - - let arc3_start = arc3_bounds.0.localize_point(p3); - let arc3_end = arc3_bounds.0.localize_point(p4); - - let arc4_start = arc4_bounds.0.localize_point(p4); - let arc4_end = arc4_bounds.0.localize_point(p1); - - let arc1 = fragment::Arc::new(arc1_start, arc1_end, *radius); - let arc2 = fragment::Arc::new(arc2_start, arc2_end, *radius); - let arc3 = fragment::Arc::new(arc3_start, arc3_end, *radius); - let arc4 = fragment::Arc::new(arc4_start, arc4_end, *radius); - - //TODO: get the settings supplied by the user - let settings = &Settings::default(); - - vec![ - (arc1_span.get_contacts(settings), arc1), - (arc2_span.get_contacts(settings), arc2), - (arc3_span.get_contacts(settings), arc3), - (arc4_span.get_contacts(settings), arc4), - ] - }) + let half12 = Arc::new(arc1.start, arc2.end, radius); + let span12 = span2.paste_at(span2.cell_bounds().unwrap().top_right(), span1); + + let half23 = Arc::new(arc2.start, arc3.end, radius); + let span23 = span2.paste_at(span2.cell_bounds().unwrap().bottom_left(), span3); + + let half34 = Arc::new(arc3.start, arc4.end, radius); + let span34 = span3.paste_at(span3.cell_bounds().unwrap().top_right(), span4); + + let half41 = Arc::new(arc4.start, arc1.end, radius); + let span41 = span1.paste_at(span1.cell_bounds().unwrap().bottom_left(), span4); + + (*diameter, [(half12, span12), (half23, span23), (half34, span34), (half41, span41)]) + }) + ); + + pub static ref ARC_SPAN: BTreeMap<Arc,Span> = BTreeMap::from_iter( + QUARTER_ARC_SPAN.iter() + .flat_map(|(_diameter, arcs)|arcs.clone()) ); } fn circle_art_to_group(art: &str, settings: &Settings) -> Vec<Contacts> { - let cell_buffer = CellBuffer::from(art); - let mut spans: Vec<Span> = cell_buffer.group_adjacents(); - assert_eq!(spans.len(), 1); - let span1 = spans.remove(0); + let span1 = circle_art_to_span(art); span1.get_contacts(settings) } @@ -649,7 +575,7 @@ pub fn endorse_circle_span(search: &Span) -> Option<(&Circle, Span)> { } pub fn endorse_arc_span(search: &Span) -> Option<(&Arc, Span)> { - ARC_SPAN.iter().rev().find_map(|(arc, span)| { + ARC_SPAN.iter().find_map(|(arc, span)| { let search_localized = search.clone().localize(); let (matched, unmatched) = is_subset_of(span, &search_localized); if matched { @@ -692,40 +618,11 @@ fn is_subset_of<T: PartialEq>( (matched == subset.len(), unmatched) } -/// searches from the largest arc first -/// [x](solved) ISSUE: An arc can have multiple matches at different radius. -/// searching from the largest or from the smallest will still result in 4 parts not equally -/// proportional, due to 1 of the arc not matching on the same arc level since the -/// arc parameters is determine only by the contacts, and have no clue about it's radius. -/// Multiple arc contacts can be the same while not having the same arc radius. -pub fn endorse_arc( - search: &Vec<Contacts>, -) -> Option<(&fragment::Arc, Vec<usize>)> { - FRAGMENTS_ARC.iter().rev().find_map(|(contacts, arc)| { - let (matched, unmatched) = is_subset_of(contacts, search); - if matched { - Some((arc, unmatched)) - } else { - None - } - }) -} - #[cfg(test)] mod tests { use super::*; #[test] - fn access_circles() { - let len = DIAMETER_CIRCLE.len(); - println!("len: {}", len); - assert_eq!(len, 22); - let frag_len = FRAGMENTS_ARC.len(); - println!("frag len: {}", frag_len); - assert_eq!(frag_len, 76); - } - - #[test] fn test_circle1() { let art = r#" _.-'''''''-._ @@ -761,12 +658,7 @@ mod tests { let mut spans: Vec<Span> = cell_buffer.group_adjacents(); assert_eq!(spans.len(), 1); let span1 = spans.remove(0); - let groups = span1.get_contacts(&Settings::default()); - for (i, group) in groups.iter().enumerate() { - println!("group{}\n{}", i, group); - } - assert_eq!(2, groups.len()); - let (arc, _) = endorse_arc(&groups).unwrap(); + let (arc, _) = endorse_arc_span(&span1).unwrap(); assert_eq!(arc.radius, 5.0); } @@ -780,11 +672,7 @@ mod tests { let mut spans: Vec<Span> = cell_buffer.group_adjacents(); assert_eq!(spans.len(), 1); let span1 = spans.remove(0); - let groups = span1.get_contacts(&Settings::default()); - for (i, group) in groups.iter().enumerate() { - println!("group{}\n{}", i, group); - } - let (arc, _) = endorse_arc(&groups).unwrap(); + let (arc, _) = endorse_arc_span(&span1).unwrap(); assert_eq!(arc.radius, 2.5); } @@ -798,11 +686,7 @@ mod tests { let mut spans: Vec<Span> = cell_buffer.group_adjacents(); assert_eq!(spans.len(), 1); let span1 = spans.remove(0); - let groups = span1.get_contacts(&Settings::default()); - for (i, group) in groups.iter().enumerate() { - println!("group{}\n{}", i, group); - } - let (arc, _) = endorse_arc(&groups).unwrap(); + let (arc, _) = endorse_arc_span(&span1).unwrap(); assert_eq!(arc.radius, 2.5); } @@ -816,11 +700,7 @@ mod tests { let mut spans: Vec<Span> = cell_buffer.group_adjacents(); assert_eq!(spans.len(), 1); let span1 = spans.remove(0); - let groups = span1.get_contacts(&Settings::default()); - for (i, group) in groups.iter().enumerate() { - println!("group{}\n{}", i, group); - } - let (arc, _) = endorse_arc(&groups).unwrap(); + let (arc, _) = endorse_arc_span(&span1).unwrap(); assert_eq!(arc.radius, 2.5); } @@ -834,11 +714,7 @@ mod tests { let mut spans: Vec<Span> = cell_buffer.group_adjacents(); assert_eq!(spans.len(), 1); let span1 = spans.remove(0); - let groups = span1.get_contacts(&Settings::default()); - for (i, group) in groups.iter().enumerate() { - println!("group{}\n{}", i, group); - } - let (arc, _) = endorse_arc(&groups).unwrap(); + let (arc, _) = endorse_arc_span(&span1).unwrap(); assert_eq!(arc.radius, 2.5); } @@ -856,11 +732,7 @@ mod tests { let mut spans: Vec<Span> = cell_buffer.group_adjacents(); assert_eq!(spans.len(), 1); let span1 = spans.remove(0); - let groups = span1.get_contacts(&Settings::default()); - for (i, group) in groups.iter().enumerate() { - println!("group{}\n{}", i, group); - } - let (arc, _) = endorse_arc(&groups).unwrap(); + let (arc, _) = endorse_arc_span(&span1).unwrap(); assert_eq!(arc.radius, 10.5); //also matched the arc21 radius and since larger it will matched it instead of arc20 } @@ -878,11 +750,7 @@ mod tests { let mut spans: Vec<Span> = cell_buffer.group_adjacents(); assert_eq!(spans.len(), 1); let span1 = spans.remove(0); - let groups = span1.get_contacts(&Settings::default()); - for (i, group) in groups.iter().enumerate() { - println!("group{}\n{}", i, group); - } - let (arc, _) = endorse_arc(&groups).unwrap(); + let (arc, _) = endorse_arc_span(&span1).unwrap(); assert_eq!(arc.radius, 10.5); //also matched the arc21 radius and since larger it will matched it instead of arc20 } @@ -899,12 +767,8 @@ mod tests { let mut spans: Vec<Span> = cell_buffer.group_adjacents(); assert_eq!(spans.len(), 1); let span1 = spans.remove(0); - let groups = span1.get_contacts(&Settings::default()); - for (i, group) in groups.iter().enumerate() { - println!("group{}\n{}", i, group); - } - let (arc, _) = endorse_arc(&groups).unwrap(); - assert_eq!(arc.radius, 10.0); + let (arc, _) = endorse_arc_span(&span1).unwrap(); + assert_eq!(arc.radius, 9.0); } #[test] @@ -920,11 +784,7 @@ mod tests { let mut spans: Vec<Span> = cell_buffer.group_adjacents(); assert_eq!(spans.len(), 1); let span1 = spans.remove(0); - let groups = span1.get_contacts(&Settings::default()); - for (i, group) in groups.iter().enumerate() { - println!("group{}\n{}", i, group); - } - let (arc, _) = endorse_arc(&groups).unwrap(); + let (arc, _) = endorse_arc_span(&span1).unwrap(); assert_eq!(arc.radius, 10.0); } } diff --git a/svgbob/test_data/circles.bob b/svgbob/test_data/circles.bob index a5facc2..abe4166 100644 --- a/svgbob/test_data/circles.bob +++ b/svgbob/test_data/circles.bob @@ -186,6 +186,31 @@ `._ _.' '-.........-' + + + + 1 ( ) + + + 2 ( _) + + _ _ + 3 (_ _) + + + ,- -. + 4 ( ) + `- -' + + + _ _ + .' '. + ( ) + `._ _.' + + + + | | V V @@ -410,3 +435,199 @@ `._ / | \ _.' '-.... ....-' + +/// HALF ARCS + + + 1 ( ) + + + 2 ( _) + + _ _ + 3 (_ _) + + + ,- -. + 4 ( ) + `- -' + + + _ _ + .' '. + ( ) + `._ _.' + + + + + + ,- -. + ( ) + `- -' + + .- -. + ( ) + `- -' + + _ _ + .' '. + ( ) + `._ _.' + + _ _ + ,' '. + ( ) + `._ _.' + + __ __ + ,' '. + ( ) + `. .' + `- -' + + __ __ + ,' `. + / \ + \ / + `.__ __.' + + __ __ + ,' `. + / \ + \ / + `.__ __.' + + __ __ + .' `. + / \ + ( ) + \ / + `.__ __.' + + ___ ___ + ,' `. + / \ + ( ) + \ / + `.___ ___.' + + + ___ ___ + ,' `. + / \ + | | + | | + \ / + `.___ ___.' + + + + ____ ____ + ,' `. + / \ + | | + | | + \ / + `.____ ____.' + + + ____ ____ + ,' `. + / \ + | | + | | + | | + | | + \ / + `.____ ____.' + + + __--- ---__ + ,' `. + / \ + | | + | | + | | + | | + \ / + `. .' + `---- ----' + + + .---- ----. + ,' `. + / \ + | | + | | + | | + | | + \ / + `. .' + `---- ----' + + + _.-''' '''-._ + ,' `. + / \ + . . + | | + | | + | | + | | + \ / + `._ _.' + '-... ...-' + + | | | | + V V V V + + _.-''' '''-._ + ,' `. + / \ + . . + | | + --> | | <-- + --> | | <-- + | | + \ / + `._ _.' + '-... ...-' + + ^ ^ ^ ^ + | | | | + + + | | | | + V V V V + + --> _.-'''' ''''-._ <-- + ,' `. + / \ + . . + | | + --> | | <-- + --> | | <-- + | | + \ / + `._ _.' + --> '-.... ....-' <-- + + ^ ^ ^ ^ + | | | | + + + + _.-'''' ''''-._ + ,' | | `. + / | | \ + . | | . + | | | | + |---------+ +---------| + |---------+ +---------| + | | | | + \ | | / + `._ | | _.' + '-.... ....-' + + |