summaryrefslogtreecommitdiffstats
path: root/svgbob/src/map/circle_map.rs
diff options
context:
space:
mode:
Diffstat (limited to 'svgbob/src/map/circle_map.rs')
-rw-r--r--svgbob/src/map/circle_map.rs475
1 files changed, 475 insertions, 0 deletions
diff --git a/svgbob/src/map/circle_map.rs b/svgbob/src/map/circle_map.rs
new file mode 100644
index 0000000..8b4a8c6
--- /dev/null
+++ b/svgbob/src/map/circle_map.rs
@@ -0,0 +1,475 @@
+use crate::{
+ buffer::{CellBuffer, Contacts, Span},
+ fragment,
+ fragment::Circle,
+ Cell, Point,
+};
+use lazy_static::lazy_static;
+use std::{collections::BTreeMap, iter::FromIterator};
+
+// 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
+ ///
+ pub static ref CIRCLE_MAP: Vec<(&'static str, Cell, Point, f32)> =
+ vec![
+ // CIRCLE_1
+ //center 0,0,o, radius = 0.5
+ (r#"
+ ()
+ "#, Cell::new(0,0), Cell::new(0,0).o(), 0.5),
+
+ // CIRCLE_2
+ //center = 1,1,m radius = 1.0
+ (r#"
+ (_)
+ "#, Cell::new(1,0), Cell::new(1,0).m(), 1.0),
+
+ // CIRCLE_3
+ //center = 1,1,o radius = 1.5,
+ (r#"
+ __
+ (__)
+ "#, Cell::new(1,1), Cell::new(1,1).o(), 1.5),
+
+ // CIRCLE_4
+ //center: 2,1,m radius: 2.0
+ (r#"
+ ,-.
+ ( )
+ `-'
+ "#, Cell::new(2,1), Cell::new(2,1).m(), 2.0),
+
+ // CIRCLE_5
+ //center: 2,1,o radius: 2.5
+ (r#"
+ .--.
+ ( )
+ `--'
+ "#, Cell::new(2,1), Cell::new(2,1).o(), 2.5),
+
+ // CIRCLE_6
+ //center: 3,2,m radius: 3.0
+ (r#"
+ _
+ .' '.
+ ( )
+ `._.'
+ "#, Cell::new(3,2), Cell::new(3,2).m(), 3.0),
+
+ // CIRCLE_7
+ //center: 3,2,o radius: 3.5
+ (r#"
+ __
+ ,' '.
+ ( )
+ `.__.'
+ "#, Cell::new(3,2), Cell::new(3,2).o(), 3.5),
+
+ // CIRCLE_8
+ //center: 4,2,m radius:4.0
+ (r#"
+ ___
+ ,' '.
+ ( )
+ `. .'
+ `-'
+ "#, Cell::new(4,2), Cell::new(4,2).m(), 4.0),
+
+ // 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
+ (r#"
+ ___
+ ,' `.
+ / \
+ \ /
+ `.___.'
+ "#, Cell::new(4,2), Cell::new(4,2).w(), 4.5),
+
+ // CIRCLE_10
+ //center: 4,3,y radius: 5.0
+ (r#"
+ ____
+ ,' `.
+ / \
+ \ /
+ `.____.'
+ "#, Cell::new(4,3), Cell::new(4,3).y(), 5.0),
+
+ // CIRCLE_11
+ //center:5,3,o radius: 5.5
+ (r#"
+ ____
+ .' `.
+ / \
+ ( )
+ \ /
+ `.____.'
+ "#, Cell::new(5,3), Cell::new(5,3).o(), 5.5),
+
+ // CIRCLE_12
+ //center:6,3,m radius: 6.0
+ (r#"
+ _____
+ ,' `.
+ / \
+ ( )
+ \ /
+ `._____.'
+ "#, Cell::new(6,3), Cell::new(6,3).m(), 6.0),
+
+ // CIRCLE_13
+ //center: 6,3,y radius: 6.5
+ (r#"
+ ______
+ ,' `.
+ / \
+ | |
+ | |
+ \ /
+ `.______.'
+ "#, Cell::new(6,3), Cell::new(6,3).y(), 6.5),
+
+ // CIRCLE_14
+ //center: 7,3,w radius: 7.0
+ (r#"
+ _______
+ ,' `.
+ / \
+ | |
+ | |
+ \ /
+ `._______.'
+ "#, Cell::new(7,3), Cell::new(7,3).w(), 7.0),
+
+
+ // CIRCLE_15
+ //center: 7,4,o radius: 7.5
+ (r#"
+ ________
+ ,' `.
+ / \
+ | |
+ | |
+ | |
+ \ /
+ `.________.'
+ "#, Cell::new(7,4), Cell::new(7,4).o(), 7.5),
+
+ // CIRCLE_16
+ //center: 8,4,m radius: 8.0
+ (r#"
+ __-----__
+ ,' `.
+ / \
+ | |
+ | |
+ | |
+ \ /
+ `. .'
+ `-------'
+ "#, Cell::new(8,4), Cell::new(8,4).m(), 8.0),
+
+ // CIRCLE_17
+ //center: 8,4,o radius: 8.5
+ (r#"
+ .--------.
+ ,' `.
+ / \
+ | |
+ | |
+ | |
+ \ /
+ `. .'
+ `--------'
+ "#, Cell::new(8,4), Cell::new(8,4).o(), 8.5),
+
+ // CIRCLE_18
+ //center:9,5,m radius: 9.0
+ (r#"
+ _.-'''''-._
+ ,' `.
+ / \
+ . .
+ | |
+ | |
+ | |
+ \ /
+ `._ _.'
+ '-.....-'
+ "#, Cell::new(9,5), Cell::new(9,5).m(), 9.0),
+
+ // CIRCLE_19
+ // center: 9,5,o radius: 9.5
+ (r#"
+ _.-''''''-._
+ ,' `.
+ / \
+ . .
+ | |
+ | |
+ | |
+ \ /
+ `._ _.'
+ '-......-'
+ "#, Cell::new(9,5), Cell::new(9,5).o(), 9.5),
+
+
+ // CIRCLE_20
+ // center: 10,5,m radius: 10
+ (r#"
+ _.-'''''''-._
+ ,' `.
+ / \
+ . .
+ | |
+ | |
+ | |
+ \ /
+ `._ _.'
+ '-.......-'
+ "#, Cell::new(10,5), Cell::new(10,5).m(), 10.0),
+ ];
+
+
+ /// The fragments for each of the circle
+ /// Calculate the span and get the group fragments
+ pub static ref FRAGMENTS_CIRCLE: Vec<(Vec<Contacts>,Circle)> = Vec::from_iter(
+ CIRCLE_MAP.iter().map(|(art, center_cell, center, radius)|{
+ (circle_art_to_group(art), Circle::new(*center, *radius, false))
+ })
+ );
+
+ /// 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_cell, center, radius)|{
+ 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)
+ })
+ );
+
+ /// Build a fragment for the characters in the range
+ /// extract the characters
+ ///
+ /// top_left arc: top_left to center cell
+ ///
+ /// top_right arc: top_right to center_cell
+ ///
+ /// bottom_left arc: bottom_left to center_cell
+ ///
+ /// bottom_right arc: bottom_right to center_cell
+ pub static ref FRAGMENTS_ARC: Vec<(Vec<Contacts>,fragment::Arc)> =Vec::from_iter(
+ CIRCLE_MAP.iter().skip(3).flat_map(|(art, center_cell, center, radius)|{
+ let cb = CellBuffer::from(*art);
+ let mut spans = cb.group_adjacents();
+ assert_eq!(spans.len(), 1);
+ let span = spans.remove(0).localize();
+ let (tl_bounds, br_bounds) = span.bounds().expect("There should be bounds");
+
+ let center_cell_br_bound = center_cell.bottom_right_most();
+ // inlude the center cell for x when this is true, when deriving a span for arc1, arc3, arc4
+ let eq_include_center_x = center.x <= center_cell_br_bound.x;
+ // include the center cell for y when this is true, when deriving a span for arc1, arc3, arc4
+ let eq_include_center_y = center.y <= center_cell_br_bound.y;
+
+ let strict_include_center_x = center.x < center_cell_br_bound.x;
+ let strict_include_center_y = center.y < center_cell_br_bound.y;
+
+ //
+ // ARC 1
+ // __
+ // | '.
+ // | \
+ // |_____|
+ //
+ let arc1_top_right = Cell::new(br_bounds.x, tl_bounds.y);
+ let arc1_center_cell = Cell::new(
+ if strict_include_center_x { center_cell.x } else { center_cell.x + 1},
+ if eq_include_center_y { center_cell.y } else { center_cell.y + 1});
+
+ let arc1_top_left = Cell::new(arc1_center_cell.x, tl_bounds.y);
+ let arc1_center = arc1_top_left.localize_point(*center);
+
+
+ let arc1_span = span.extract(arc1_center_cell, arc1_top_right);
+ let arc1 = fragment::Arc::new(Point::new(arc1_center.x + *radius, arc1_center.y), Point::new(arc1_center.x, arc1_center.y - *radius) , *radius);
+
+ //
+ // ARC 2
+ // __
+ // .' |
+ // / |
+ // |_____|
+ //
+
+ let arc2_top_left = Cell::new(tl_bounds.x, tl_bounds.y);
+ let arc2_span = span.extract(arc2_top_left, *center_cell);
+ let arc2 = fragment::Arc::new(Point::new(center.x, center.y - radius), Point::new(center.x - radius , center.y), *radius);
+
+
+ //
+ // ARC 3
+ // ______.
+ // | |
+ // \ |
+ // `.__|
+ //
+ //
+ let arc3_center_cell = Cell::new(
+ if eq_include_center_x { center_cell.x } else { center_cell.x + 1},
+ if strict_include_center_y { center_cell.y } else { center_cell.y + 1}
+ );
+ let arc3_top_left = Cell::new(tl_bounds.x, arc3_center_cell.y);
+ let arc3_bottom_right = Cell::new(arc3_center_cell.x, br_bounds.y);
+ let arc3_center = arc3_top_left.localize_point(*center);
+ let arc3_span = span.extract(arc3_top_left, arc3_bottom_right);
+ let arc3 = fragment::Arc::new(Point::new(arc3_center.x - radius, arc3_center.y), Point::new(arc3_center.x, arc3_center.y + radius), *radius);
+
+
+ //
+ // ARC 4
+ //
+ // .______
+ // | |
+ // | /
+ // |__.'
+ //
+ let arc4_center_cell = Cell::new(
+ if strict_include_center_x { center_cell.x } else { center_cell.x + 1},
+ if strict_include_center_y { center_cell.y } else { center_cell.y + 1}
+ );
+ let arc4_top_left = Cell::new(arc4_center_cell.x, arc4_center_cell.y);
+ let arc4_bottom_right = Cell::new(br_bounds.x, br_bounds.y);
+ let arc4_center = arc4_top_left.localize_point(*center);
+ let arc4_span = span.extract(arc4_top_left, arc4_bottom_right);
+ let arc4 = fragment::Arc::new(Point::new(arc4_center.x, arc4_center.y + radius), Point::new(arc4_center.x + radius, arc4_center.y), *radius);
+
+ vec![
+ (arc1_span.get_contacts(), arc1),
+ (arc2_span.get_contacts(), arc2),
+ (arc3_span.get_contacts(), arc3),
+ (arc4_span.get_contacts(), arc4),
+ ]
+ })
+ );
+}
+
+fn circle_art_to_group(art: &str) -> 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);
+ span1.get_contacts()
+}
+
+/// [X](Done) TODO: search only the subset of contacts that matches the circle.
+/// if it is a subset then the circle is matched and the non-matching ones are returned
+pub fn endorse_circle(search: &Vec<Contacts>) -> Option<(&Circle, Vec<usize>)> {
+ FRAGMENTS_CIRCLE.iter().rev().find_map(|(contacts, circle)| {
+ let (matched, unmatched) = is_subset_of(contacts, search);
+ if matched { Some((circle, unmatched)) } 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(subset: &Vec<Contacts>, big_set: &Vec<Contacts>) -> (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)
+}
+
+/// 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> {
+ FRAGMENTS_ARC
+ .iter()
+ .rev()
+ .find_map(|(contacts, arc)| if contacts == search { Some(arc) } else { None })
+}
+
+
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_circle1() {
+ let art = r#"
+ _.-'''''''-._
+ ,' `.
+ / \
+ . .
+ | |
+ | |
+ | |
+ \ /
+ `._ _.'
+ '-.......-'
+ "#;
+ 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 groups = span1.get_contacts();
+ for (i, group) in groups.iter().enumerate() {
+ println!("group{}\n{}", i, group);
+ }
+ assert_eq!(11, groups.len());
+ }
+
+ #[test]
+ fn test_arc9_top_right() {
+ let art = r#"
+ __
+ `.
+ \
+ "#;
+ 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 groups = span1.get_contacts();
+ for (i, group) in groups.iter().enumerate() {
+ println!("group{}\n{}", i, group);
+ }
+ assert_eq!(2, groups.len());
+ let arc = endorse_arc(&groups);
+ assert!(arc.is_some());
+ }
+}