diff options
author | Jovansonlee Cesar <ivanceras@gmail.com> | 2021-09-13 15:45:55 +0800 |
---|---|---|
committer | Jovansonlee Cesar <ivanceras@gmail.com> | 2021-09-13 15:45:55 +0800 |
commit | 277f28b337a2d00a61cf342247060cf5fbf88d50 (patch) | |
tree | bc69da069d18061457356e74ad74e5aad3c700e0 /packages/svgbob/src/map/circle_map.rs | |
parent | 06c4e56d16d3c4d06434bf920a62ee8926a3db9a (diff) |
Reorganize directory structure, update to sauron 0.43.40.6.2
Diffstat (limited to 'packages/svgbob/src/map/circle_map.rs')
-rw-r--r-- | packages/svgbob/src/map/circle_map.rs | 624 |
1 files changed, 624 insertions, 0 deletions
diff --git a/packages/svgbob/src/map/circle_map.rs b/packages/svgbob/src/map/circle_map.rs new file mode 100644 index 0000000..c2c43f5 --- /dev/null +++ b/packages/svgbob/src/map/circle_map.rs @@ -0,0 +1,624 @@ +use crate::{ + buffer::{CellBuffer, Contacts, Span}, + fragment, + fragment::Arc, + fragment::Circle, + Cell, Point, Settings, +}; +use lazy_static::lazy_static; +use std::{collections::BTreeMap, collections::HashMap, iter::FromIterator}; + +#[derive(PartialEq, Debug, Clone, Copy)] +/// edge cases of the circle art +pub enum EdgeCase { + /// circle arc is touching the edge of the first cell + /// ie: if the left most cell is `/` then it is touching the dge + StartEdge, + /// if the left most cell is `(` or `|` then it starts at half the cell + StartHalf, +} + +// These are circle map, when a group is detected to have these set of characters +// arrange together in such this way, then endorse them as a circle +// Each of these character formation will have a certain circle parameters: center, and radius. +// +lazy_static! { + + /// ```ignore + /// 0 1 2 3 4 B C D + /// 0┌─┬─┬─┬─┐ A┌─┬─┬─┬─┐E + /// 1├─┼─┼─┼─┤ │ │ │ │ │ + /// 2├─┼─┼─┼─┤ F├─G─H─I─┤J + /// 3├─┼─┼─┼─┤ │ │ │ │ │ + /// 4├─┼─┼─┼─┤ K├─L─M─N─┤O + /// 5├─┼─┼─┼─┤ │ │ │ │ │ + /// 6├─┼─┼─┼─┤ P├─Q─R─S─┤T + /// 7├─┼─┼─┼─┤ │ │ │ │ │ + /// 8└─┴─┴─┴─┘ U└─┴─┴─┴─┘Y + /// ``` V W X + /// art, edge_case, top_offset to center point + static ref CIRCLE_ART_MAP: Vec<(&'static str, EdgeCase, f32)> = + vec![ + // CIRCLE_1 + //center 1,0,k, radius = 0.5 + // 2 cell width , radius formula: (n-1)/2 = (2-1)/2 = 0.5 + // where n is the number of cells used + // if edge_case starts at edge then n is added by 1. + // vert_mid: half (0.5/1.0) + // cx_lies: edge + // cy_lies: mid + // + // edge_case: start_half = 0.5, start_edge = 0.0 + // if radius + edge_case has 0.5 then mid, 0.0 them edge + // + // + (r#" + () + "#, EdgeCase::StartHalf, 0.5), + + // CIRCLE_2 + //center = 1,1,m radius = 1.0 + // 3 cell width, (n-1)/2 = (3-1)/2 = 1.0 + // vert_mid: half (0.5/1.0) + // cx_lies: mid + // cy_lies: mid + (r#" + (_) + "#, EdgeCase::StartHalf, 0.5), + + // CIRCLE_3 + //center = 1,1,o radius = 1.5, + // 4 cell width, (n-1)/2 = (4-1)/2 = 1.5 + // vert_mid: 3/4 (1.5/2.0) + // cx_lies: edge + // cy_lies: mid + (r#" + __ + (__) + "#, EdgeCase::StartHalf, 1.5), + + // CIRCLE_4 + //center: 2,1,m radius: 2.0 + // 5 cell width, (n-1)/2 = (5-1)/2 = 2.0 + // vert_mid: half (1.5/3.0) + // cx_lies: mid + // cy_lies: mid + (r#" + ,-. + ( ) + `-' + "#, EdgeCase::StartHalf, 1.5), + + // CIRCLE_5 + //center: 2,1,o radius: 2.5 + // 6 cell width, (n-1)/2 = (6-1)/2 = 2.5 + // vert_mid: half (1.5/3.0) + // cx_lies: edge + // cy_lies: mid + (r#" + .--. + ( ) + `--' + "#, EdgeCase::StartHalf, 1.5), + + // CIRCLE_6 + //center: 3,2,m radius: 3.0 + // 7 cell width, (n-1)/2 = (7-1)/2 = 3.0 + // vert_mid: 2.5/4 + // cx_lies: mid + // cy_lies: mid + (r#" + _ + .' '. + ( ) + `._.' + "#, EdgeCase::StartHalf, 2.5), + + // CIRCLE_7 + //center: 3,2,o radius: 3.5 + // 8 cell width, (n-1)/2 = (8-1)/2 = 3.5 + // vert_mid: 2.5/4 + // cx_lies: edge + // cy_lies: mid + (r#" + __ + ,' '. + ( ) + `.__.' + "#, EdgeCase::StartHalf, 2.5), + + // CIRCLE_8 + //center: 4,2,m radius:4.0 + // 9 cell width, (n-1)/2 = (9-1)/2 = 4.0 + // vert_mid: half (2.5/5.0) + // cx_lies: mid + // cy_lies: mid + (r#" + ___ + ,' '. + ( ) + `. .' + `-' + "#, EdgeCase::StartHalf, 2.5), + + // circle 9 and up can be divided into 4 quadrants these quadrants can be arcs and can be used as + // rounded edge with larger radius for rouded rect + // CIRCLE_9 + //center: 4,2,w radius: 4.5 + // start_edge: true + // 9 cell width, (n-0)/2 = (9-0)/2 = 4.5 + // vert_mid: 3.0/5.0 + // cx_lies: mid + // cy_lies: edge + (r#" + ___ + ,' `. + / \ + \ / + `.___.' + "#, EdgeCase::StartEdge, 3.0 ), + + // CIRCLE_10 + //center: 4,2,y radius: 5.0 + //start_edge: true + // 10 cell width, (n-0)/2 = (10-0)/2 = 5.0 + // vert_mid: 3.0/5.0 + // cx_lies: edge + // cy_lies: edge + (r#" + ____ + ,' `. + / \ + \ / + `.____.' + "#, EdgeCase::StartEdge, 3.0), + + // CIRCLE_11 + //center:5,3,o radius: 5.5 + // 12 cell width, (n-1)/2 = (12-1)/2 = 5.5 + // vert_mid: 3.5/6.0 + // cx_lies: edge + // cy_lies: mid + (r#" + ____ + .' `. + / \ + ( ) + \ / + `.____.' + "#, EdgeCase::StartHalf , 3.5), + + // CIRCLE_12 + //center:6,3,m radius: 6.0 + // 13 cell width, (n-1)/2 = (13-1)/2 = 6.0 + // vert_mid: 3.5/6.0 + // cx_lies: mid + // cy_lies: mid + (r#" + _____ + ,' `. + / \ + ( ) + \ / + `._____.' + "#, EdgeCase::StartHalf, 3.5), + + // CIRCLE_13 + // center: 6,3,y radius: 6.5 + // vert_mid: 4.0/7.0 + // cx_lies: edge + // cy_lies: edge + (r#" + ______ + ,' `. + / \ + | | + | | + \ / + `.______.' + "#, EdgeCase::StartHalf, 4.0), + + // CIRCLE_14 + //center: 7,3,w radius: 7.0 + //vert_mid: 4.0/7.0 + // cx_lies: mid + // cy_lies: edge + (r#" + _______ + ,' `. + / \ + | | + | | + \ / + `._______.' + "#, EdgeCase::StartHalf , 4.0), + + + // CIRCLE_15 + //center: 7,4,o radius: 7.5 + //vert_mid: 4.5/8.0 + // cx_lies: edge + // cy_lies: mid + (r#" + ________ + ,' `. + / \ + | | + | | + | | + \ / + `.________.' + "#, EdgeCase::StartHalf, 4.5), + + // CIRCLE_16 + //center: 8,4,m radius: 8.0 + //vert_mid: 4.5/9.0 + // cx_lies: mid + // cy_lies: mid + (r#" + __-----__ + ,' `. + / \ + | | + | | + | | + \ / + `. .' + `-------' + "#, EdgeCase::StartHalf, 4.5), + + // CIRCLE_17 + //center: 8,4,o radius: 8.5 + // vert_mid: 4.5/9.0 + // cx_lies: edge + // cy_lies: mid + (r#" + .--------. + ,' `. + / \ + | | + | | + | | + \ / + `. .' + `--------' + "#, EdgeCase::StartHalf, 4.5), + + // CIRCLE_18 + //center:9,5,m radius: 9.0 + //vert_mid: 5.5/10.0 + // cx_lies: mid + // cy_lies: mid + (r#" + _.-'''''-._ + ,' `. + / \ + . . + | | + | | + | | + \ / + `._ _.' + '-.....-' + "#, EdgeCase::StartHalf, 5.5), + + // CIRCLE_19 + // center: 9,5,o radius: 9.5 + // vert_mid: 5.5/10.0 + // cx_lies: edge + // cy_lies: mid + (r#" + _.-''''''-._ + ,' `. + / \ + . . + | | + | | + | | + \ / + `._ _.' + '-......-' + "#, EdgeCase::StartHalf, 5.5), + + + // CIRCLE_20 + // center: 10,5,m radius: 10 + // vert_mid: 5.5/10.0 + // cx_lies: mid + // cy_lies: mid + (r#" + _.-'''''''-._ + ,' `. + / \ + . . + | | + | | + | | + \ / + `._ _.' + '-.......-' + "#, EdgeCase::StartHalf, 5.5), + + // CIRCLE_21 + // center: 10,5,o radius: 10.5 + // vert_mid: 5.5/11.0 + // cx_lies: edge + // cy_lies: mid + (r#" + _.-''''''''-._ + ,' `. + / \ + . . + | | + | | + | | + | | + \ / + `._ _.' + '-........-' + "#, EdgeCase::StartHalf, 5.5), + + // CIRCLE_22 + // center: 10,5,m radius: 11 + // radius = (n-1)/2 = (23-1)/2 = 11 + // vert_mid: 5.5/11.0 + // cx_lies: mid + // cy_lies: mid + (r#" + _.-'''''''''-._ + ,' `. + / \ + . . + | | + | | + | | + | | + \ / + `._ _.' + '-.........-' + "#, EdgeCase::StartHalf, 5.5), + + ]; + + + static ref CIRCLE_MAP: Vec<(&'static str, Point, f32, EdgeCase, f32)> =Vec::from_iter( + CIRCLE_ART_MAP.iter().enumerate().map(|(ndx, (art, edge_case, offset_center_y))|{ + let cb = CellBuffer::from(*art); + let (lo, hi) = cb.bounds().expect("circle must have bounds"); + + let width = match *edge_case { + EdgeCase::StartEdge => { + (hi.x - lo.x) as f32 + 1.0 + } + EdgeCase::StartHalf => { + (hi.x - lo.x) as f32 + } + }; + + let calc_radius = width / 2.0; + let index = (width - 1.0) as usize; + assert_eq!(ndx, index); + + let edge_inc_x = match edge_case{ + EdgeCase::StartEdge => 0.0, + EdgeCase::StartHalf => 0.5, + }; + + let calc_center_x = calc_radius + edge_inc_x; + let calc_center_y = offset_center_y * 2.0; + let calc_center = Point::new(calc_center_x, calc_center_y); + + (*art, calc_center, calc_radius, *edge_case, *offset_center_y) + }) + ); + + + /// The fragments for each of the circle + /// Calculate the span and get the group fragments + static ref FRAGMENTS_CIRCLE: Vec<(Vec<Contacts>,Circle)> = Vec::from_iter( + CIRCLE_MAP.iter().map(|(art, center, radius, edge_case, offset_center_y)|{ + (circle_art_to_group(art, &Settings::default()), Circle::new(*center, *radius, false)) + }) + ); + + /// map of circle spans and their radius + pub static ref DIAMETER_CIRCLE: HashMap<i32,(Point,Span)> = HashMap::from_iter( + CIRCLE_MAP.iter().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(); + ((*radius * 2.0) as i32, (*center, span)) + }) + ); + + /// There is only 1 span per circle, and localized + pub static ref CIRCLES_SPAN: BTreeMap<Circle, Span> = BTreeMap::from_iter( + CIRCLE_MAP.iter().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(); + (Circle::new(*center, *radius, false), span) + }) + ); + + /// 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(3).map(|(art, center, radius, edge_case, offset_center_y)|{ + let span = circle_art_to_span(art); + 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); + let p3 = Point::new(center.x - radius, center.y); + let p4 = Point::new(center.x, center.y + radius); + + + let center_cell = center.cell(); + + // TODO: use the edge_case from the circle_art map + 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 span1_center = Cell::new(center_cell.x, cy_adjusted); + let span2_center = Cell::new(cx_adjusted, cy_adjusted); + let span3_center = Cell::new(cx_adjusted, center_cell.y); + let span4_center = center_cell; + + let bounds1 = Cell::rearrange_bound(span1_center, top_right); + let bounds2 = Cell::rearrange_bound(top_left, span2_center); + let bounds3 = Cell::rearrange_bound(bottom_left, span3_center); + let bounds4 = Cell::rearrange_bound(span4_center, bottom_right); + + let span1 = span.extract(bounds1.0, bounds1.1).localize(); + let span2 = span.extract(bounds2.0, bounds2.1).localize(); + let span3 = span.extract(bounds3.0, bounds3.1).localize(); + let span4 = span.extract(bounds4.0, bounds4.1).localize(); + + let arc1_start = bounds1.0.localize_point(p1); + let arc1_end = bounds1.0.localize_point(p2); + + let arc2_start = bounds2.0.localize_point(p2); + let arc2_end = bounds2.0.localize_point(p3); + + let arc3_start = bounds3.0.localize_point(p3); + let arc3_end = bounds3.0.localize_point(p4); + + let arc4_start = bounds4.0.localize_point(p4); + let arc4_end = bounds4.0.localize_point(p1); + + 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 arc4 = Arc::new(arc4_start, arc4_end, *radius); + + 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; + + 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 span1 = circle_art_to_span(art); + span1.get_contacts(settings) +} + +fn circle_art_to_span(art: &str) -> Span { + let cell_buffer = CellBuffer::from(art); + let mut spans = cell_buffer.group_adjacents(); + assert_eq!(spans.len(), 1); + spans.remove(0).localize() +} + +pub fn endorse_circle_span(search: &Span) -> Option<(&Circle, Span)> { + CIRCLES_SPAN.iter().rev().find_map(|(circle, span)| { + let search_localized = search.clone().localize(); + let (matched, unmatched) = is_subset_of(span, &search_localized); + if matched { + let unmatched_cell_chars = search + .iter() + .enumerate() + .filter_map(|(i, cell_char)| { + if unmatched.contains(&i) { + Some(cell_char.clone()) + } else { + None + } + }) + .collect::<Vec<_>>(); + Some((circle, Span::from(unmatched_cell_chars))) + } else { + None + } + }) +} + +pub fn endorse_arc_span(search: &Span) -> Option<(&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 { + let unmatched_cell_chars = search + .iter() + .enumerate() + .filter_map(|(i, cell_char)| { + if unmatched.contains(&i) { + Some(cell_char.clone()) + } else { + None + } + }) + .collect::<Vec<_>>(); + Some((arc, Span::from(unmatched_cell_chars))) + } else { + None + } + }) +} + +/// returns true if all the contacts in subset is in big_set +/// This also returns the indices of big_set that are not found in the subset +fn is_subset_of<T: PartialEq>( + subset: &[T], + big_set: &[T], +) -> (bool, Vec<usize>) { + let mut unmatched = vec![]; + let mut matched = 0; + for (_i, set) in subset.iter().enumerate() { + if big_set.contains(set) { + matched += 1; + } + } + for (bi, bset) in big_set.iter().enumerate() { + if !subset.contains(bset) { + unmatched.push(bi); + } + } + (matched == subset.len(), unmatched) +} + +#[cfg(test)] +mod test_circle_map; |