Svgbob Architecture and Design phases

Svgbob creates an svg drawing based on the input ascii art diagrams. It achieves this by creating a corresponding fragment for each character, and then this little fragments are then merged to form lines and arcs. The lines and arcs are then endorsed into high level shapes such as rect, circles.

Name inspiration:

  • svg for svg document and drawing.
  • bob for Alice and Bob as common characters in most diagrams Bob Ross - a painter who like to draws happy little trees.

Library used

  • nalgebra and ncollide2d for geometric function calculations such as calculating whether lines are intersecting, collinear. Computing the clipping of lines and boxes.
  • pom for parsing the styling directives(Legend) at the bottom of the document
  • sauron for building the svg document object tree.

Iterations, re-architecture rewrites

Phase 1 Exploding if statements. This was in elm fullcode

1 getElement x y model =
2 let
3 char = get x y model
4 in
5 case char of
6 Just char ->
7 if isVertical char
8 && not ( isNeighbor left isAlphaNumeric )
9 && not ( isNeighbor right isAlphaNumeric ) then
10 Just Vertical
11 else if isHorizontal char
12 && not ( isNeighbor left isAlphaNumeric )
13 && not ( isNeighbor right isAlphaNumeric ) then
14 Just Horizontal
15 else if isIntersection char then
16 let
17 isVerticalJunctionLeft =
18 isNeighbor top isVertical
19 && isNeighbor ( bottomOf x y model ) isVertical
20 && isNeighbor ( leftOf x y model ) isHorizontal
21
22 isVerticalJunctionRight =
23 isNeighbor top isVertical
24 && isNeighbor bottom isVertical
25 && isNeighbor right isHorizontal
26
27 isHorizontalJunctionTop =
28 isNeighbor left isHorizontal
29 && isNeighbor right isHorizontal
30 && isNeighbor top isVertical
31
32 isHorizontalJunctionBot =
33 isNeighbor left isHorizontal
34 && isNeighbor right isHorizontal
35 && isNeighbor bottom isVertical
36
37 isTopLeftIntersection =
38 isNeighbor bottom isVertical && isNeighbor right isHorizontal
39
40 isTopRightIntersection =
41 isNeighbor bottom isVertical && isNeighbor left isHorizontal
42
43 isBottomRightIntersection =
44 isNeighbor top isVertical && isNeighbor left isHorizontal
45
46 isBottomLeftIntersection =
47 isNeighbor top isVertical && isNeighbor right isHorizontal
48
49 isCrossIntersection =
50 isNeighbor top isVertical
51 && isNeighbor bottom isVertical
52 && isNeighbor left isHorizontal
53 && isNeighbor right isHorizontal
54
55 ... 200 more lines ...
56

Though elm is fast, but if you throw a lot of conditional branching to it, it will slow it down. At least I don ’ t get to have runtime errors here if it was written in js. Adding an edgecase is just appending a new if else statement at the bottom of the statements.

Pros: Very simple design. Just if statements and return the appropriate shape the character will take form Adding edge case behaviour is just appending an else if to the nearest conditional( if ) behavior.

Caveats: The fragments/drawing elements are named. Naming is hard, we can not name all of them. Consistency is broken.

Phase2: Now in rust. The character behavior is stored in a Vec<(condition, drawing_elements)> This is already close to the current architecture.

Improvements: - Runs a lot faster than elm. Converting the code from elm to rust, accelerate my learning of the usage of functional programming in rust. - Consumed elements, if certain group of elements matches a higher level shapes, those elements are consumed/remove from the grid to avoid generating additional drawing elements when iterated with the rest of the characters in the grid.

1 // get the paths in the location x,y
2 // if non path, then see if it can return a text path
3 fn get_elements ( & self , x : isize , y : isize , settings : & Settings ) -> Option < Vec < Element > > {
4 ...
5 // common path lines
6 let vertical = Element :: solid_line ( center_top , center_bottom ) ;
7 let horizontal = Element :: solid_line ( mid_left , mid_right ) ;
8 let slant_left = Element :: solid_line ( high_left , low_right ) ;
9 let slant_right = Element :: solid_line ( low_left , high_right ) ;
10 let low_horizontal = Element :: solid_line ( low_left , low_right ) ;
11
12
13 let match_list : Vec < ( bool , Vec < Element > ) > =
14 vec! [
15 /*
16 .-
17 |
18 */
19 ( self . is_char ( this , is_round )
20 && self . is_char ( right , is_horizontal )
21 && self . is_char ( bottom , is_vertical ) ,
22 vec! [ cxdy_cxey . clone ( ) , arc_excy_cxdy . clone ( ) ]
23 ) ,
24 /*
25 -.
26 |
27 */
28 ( self . is_char ( this , is_round )
29 && self . is_char ( left , is_horizontal )
30 && self . is_char ( bottom , is_vertical ) ,
31 vec! [ cxdy_cxey . clone ( ) , arc_cxdy_axcy . clone ( ) ]
32 ) ,
33 /*
34 |
35 '-
36 */
37 ( self . is_char ( this , is_round )
38 && self . is_char ( right , is_horizontal )
39 && self . is_char ( top , is_vertical ) ,
40 vec! [ cxay_cxby . clone ( ) , arc_cxby_excy . clone ( ) ]
41 ) ,
42 /*
43 |
44 -'
45 */
46 ( self . is_char ( this , is_round )
47 && self . is_char ( left , is_horizontal )
48 && self . is_char ( top , is_vertical ) ,
49 vec! [ cxay_cxby . clone ( ) , arc_axcy_cxby . clone ( ) ]
50 ) ,
51 /*
52 .-
53 /
54 */
55 ( self . is_char ( this , is_round )
56 && self . is_char ( right , is_horizontal )
57 && self . is_char ( bottom_left , is_slant_right ) ,
58 vec! [ axey_bxdy . clone ( ) , arc_excy_bxdy . clone ( ) ]
59 ) ,
60 /*
61 -.
62 \
63 */
64 ( self . is_char ( this , is_round )
65 && self . is_char ( left , is_horizontal )
66 && self . is_char ( bottom_right , is_slant_left ) ,
67 vec! [ exey_dxdy . clone ( ) , arc_dxdy_axcy . clone ( ) ]
68 ) ,
69 /*
70 -.
71 /
72 */
73 ( self . is_char ( this , is_round )
74 && self . is_char ( left , is_horizontal )
75 && self . is_char ( bottom_left , is_slant_right ) ,
76 vec! [ axey_bxdy . clone ( ) , arc_bxdy_axcy . clone ( ) ]
77 ) ,
78 /*
79 .-
80 \
81 */
82 ( self . is_char ( this , is_round )
83 && self . is_char ( right , is_horizontal )
84 && self . is_char ( bottom_right , is_slant_left ) ,
85 vec! [ exey_dxdy . clone ( ) , arc_excy_dxdy . clone ( ) ]
86 ) ,
87 /*
88 \
89 '-
90 */
91 ( self . is_char ( this , is_round )
92 && self . is_char ( right , is_horizontal )
93 && self . is_char ( top_left , is_slant_left ) ,
94 vec! [ axay_bxby . clone ( ) , arc_bxby_excy . clone ( ) ]
95 ) ,
96 /*
97 /
98 '-
99 */
100 ( self . is_char ( this , is_round )
101 && self . is_char ( right , is_horizontal )
102 && self . is_char ( top_right , is_slant_right ) ,
103 vec! [ dxby_exay . clone ( ) , arc_dxby_excy . clone ( ) ]
104 ) ,
105 /*
106 \
107 -'
108 */
109 ( self . is_char ( this , is_round )
110 && self . is_char ( left , is_horizontal )
111 && self . is_char ( top_left , is_slant_left ) ,
112 vec! [ axay_bxby . clone ( ) , arc_axcy_bxby . clone ( ) ]
113 ) ,
114 /*
115 /
116 -'
117 */
118 ( self . is_char ( this , is_round )
119 && self . is_char ( left , is_horizontal )
120 && self . is_char ( top_right , is_slant_right ) ,
121 vec! [ dxby_exay . clone ( ) , arc_axcy_dxby . clone ( ) ]
122 ) ,
123 ]
1
2 // Circle 12
3 // _
4 // .' '.
5 // ( + )
6 // `._.'
7 if self . in_left ( 3 ) . is ( ' ( ' )
8 && self . in_right ( 3 ) . is ( ' ) ' )
9 && self . in_top ( 2 ) . is ( ' _ ' )
10 && self . bottom ( ) . is ( ' _ ' )
11 && self . top ( ) . in_left ( 2 ) . any ( " ,. " )
12 && self . top_left ( ) . is ( ' \' ' )
13 && self . top_right ( ) . any ( " `' " )
14 && self . top ( ) . in_right ( 2 ) . is ( ' . ' )
15 && self . bottom ( ) . in_left ( 2 ) . any ( " `' " )
16 && self . bottom_left ( ) . is ( ' . ' )
17 && self . bottom_right ( ) . any ( " ., " )
18 && self . bottom ( ) . in_right ( 2 ) . is ( ' \' ' )
19 {
20 elm . push ( open_circle ( m , 12 ) ) ;
21 consumed . extend ( vec! [
22 left3 ( ) ,
23 right3 ( ) ,
24 top2 ( ) ,
25 bottom ( ) ,
26 top_left2 ( ) ,
27 top_left ( ) ,
28 top_right ( ) ,
29 top_right2 ( ) ,
30 bottom_left2 ( ) ,
31 bottom_left ( ) ,
32 bottom_right ( ) ,
33 bottom_right2 ( ) ,
34 ] ) ;
35 }

Caveats: - Merging of small fragments requires checking against all the other fragments of the entire grid. Runtime complexity is at least O(n^2) - Endorsing to shapes requires a lot of if statement comparisons and every cell is checked even for cell that has only a few elements that couldn ’ t form into a certain shapes is tested. - Processing high level stage and low level fragment stage is one execution. - Drawing elements are still named.

Phase 3: Attempts to add a signal strength to characters depending on their neighboring character whether they should connect or not. This makes the dynamic behavior flexible but the control flow is not very intuitive.

  • Strong + Strong should connect
  • Medium + Medium connects
  • Medium + Weak may connect
  • Weak + Weak should not connect.
1
2 /// get the characteristic of a character
3 /// it's behavior and the intended behavior
4 ///
5 /// ┌─┬─┬─┬─┬─┐
6 /// │a│b│c│d│e│
7 /// ├─┼─┼─┼─┼─┤
8 /// │f│g│h│i│j│
9 /// ├─┼─┼─┼─┼─┤
10 /// │k│l│m│n│o│
11 /// ├─┼─┼─┼─┼─┤
12 /// │p│q│r│s│t│
13 /// ├─┼─┼─┼─┼─┤
14 /// │u│v│w│x│y│
15 /// └─┴─┴─┴─┴─┘
16 ///
17 fn get_characteristic ( & self ) -> Option < Characteristic > {
18 /// ////////////////////////
19 //
20 // ., dot or period and comma
21 //
22 /// ////////////////////////
23 if self . any ( " ., " ) {
24 Some ( Characteristic {
25 is_static : false ,
26 intensify : vec! [
27 // -. +.
28 (
29 K ,
30 Condition {
31 loc : left ( ) ,
32 can : ConnectTo ( O , Medium ) ,
33 } ,
34 ) ,
35 // .- .+
36 (
37 O ,
38 Condition {
39 loc : right ( ) ,
40 can : ConnectTo ( K , Medium ) ,
41 } ,
42 ) ,
43 // _.
44 (
45 U ,
46 Condition {
47 loc : left ( ) ,
48 can : ConnectTo ( Y , Strong ) ,
49 } ,
50 ) ,
51 // ._
52 (
53 Y ,
54 Condition {
55 loc : right ( ) ,
56 can : ConnectTo ( U , Strong ) ,
57 } ,
58 ) ,
59 // .
60 // /
61 (
62 U ,
63 Condition {
64 loc : bottom_left ( ) ,
65 can : ConnectTo ( E , Strong ) ,
66 } ,
67 ) ,
68 // / only for / else _
69 // . . will connect
70 (
71 E ,
72 Condition {
73 loc : top_right ( ) ,
74 can : IsStrongAll ( vec! [ E , U ] ) ,
75 } ,
76 ) ,
77 // .
78 // \
79 (
80 Y ,
81 Condition {
82 loc : bottom_right ( ) ,
83 can : ConnectTo ( A , Strong ) ,
84 } ,
85 ) ,
86 ...
87 ] ,
88 intended_behavior : vec! [
89 // .-
90 // /
91 ( vec! [ O , U ] , vec! [ arc ( o , q , 4 ) , line ( q , u ) ] ) ,
92 // .-
93 // \
94 ( vec! [ O , Y ] , vec! [ arc ( o , s , 4 ) , line ( s , y ) ] ) ,
95 // -.
96 // \
97 ( vec! [ K , Y ] , vec! [ arc ( s , k , 4 ) , line ( s , y ) ] ) ,
98 // -.
99 // /
100 ( vec! [ K , U ] , vec! [ line ( u , q ) , arc ( q , k , 2 ) ] ) ,
101 // /
102 // .
103 // /
104 ( vec! [ U , E ] , vec! [ line ( u , e ) ] ) ,
105 // \
106 // .
107 // \
108 ...
109 ] ,
110 properties : vec! [
111 ( O , Weak , vec! [ arc ( o , r , 2 ) ] ) ,
112 ( K , Weak , vec! [ arc ( r , k , 2 ) ] ) ,
113 ( W , Medium , vec! [ line ( r , w ) ] ) ,
114 ( U , Weak , vec! [ line ( q , u ) ] ) ,
115 ( Y , Weak , vec! [ line ( s , y ) ] ) ,
116 ( A , Weak , vec! [ line ( m , a ) ] ) ,
117 ( E , Weak , vec! [ line ( m , e ) ] ) ,
118 ( F , Weak , vec! [ line ( m , f ) ] ) ,
119 ( J , Weak , vec! [ line ( m , j ) ] ) ,
120 ] ,
121 } )
122 }

Pros: - Characters are assigned with certain properties. This allows similar characters such as dash(-) and line drawing (-) to have the same behavior without explicitly coding for each of those variations.

Phase 4.

Improvements: - Uses of Buffers - StringBuffer, input strings are slices into rows and columns - CellBuffer, which cells contains which character. - FragmentBuffer, which cell contains what fragments(drawing elements) - PropertyBuffer, what is the property of each cell based on the the character it contains.

PropertyBuffer is calculated only once for each character, so the succeeding lookup should not waste execution time to recompute.

How the fragments are conceived based on a character?

Neighbor character: There are 8 neighbors of a character and each character on the input is checked agains this 8 neighbor for appropriate drawing element

TopLeft Left BottomLeft Top char Bottom TopRight Right BottomRight

Character Grid: a 5x5 grid which covers the most significant points for a character to be converted into drawing elements.

Character grid: / is the line connecting E to U. Dash is connecting K to O, etc. bob

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

1 These fragments are processed such as merging collinear lines that are touching their endpoints.
2
3 ```bob
4
5 +--------------+ +------------+ +----------------+ +-----------------+
6 | StringBuffer |------> | CellBuffer |-------->| FragmentBuffer |--------->| Svg drawing |
7 +--------------+ +------------+ +----------------+ +-----------------+
8 \ ^
9 \ +-------+ /
10 `-->| Spans | /
11 +-------+ /
12 \ /
13 \ +---------------+ .----------------. /
14 `-->|Contact groups |---/ endorse shapes /--'
15 +---------------+ '----------------'
  • Optimizations. - Usage of span and contact groups. Span group together that are neighbors. Contact groups group together fragments that are touching together. Cells don ’ t need to be checked against other cells when they are far from each other. Merging of fragments such as lines into longer lines needs to interact only elements that are within its group.

  • Endorsing group of fragments into higher level shapes. - rect, rounded rect, circles, arcs are higher level shapes that are from small fragment components: arc,lines,

  • Tagging shapes. Text inside of a shape with the pattern “ “ , ” ” will become a tag of the enclosing shape. At the DOM level, the shape is an svg dom element such as: rect,circle,path and the tag is the element class which you can use css to apply a style to the element. The legend part at the bottom of the document is parsed and converted into css which is then appended to the svg document.

1
2 ///
3 /// 0 1 2 3 4 B C D
4 /// 0┌─┬─┬─┬─┐ A┌─┬─┬─┬─┐E
5 /// 1├─┼─┼─┼─┤ │ │ │ │ │
6 /// 2├─┼─┼─┼─┤ F├─G─H─I─┤J
7 /// 3├─┼─┼─┼─┤ │ │ │ │ │
8 /// 4├─┼─┼─┼─┤ K├─L─M─N─┤O
9 /// 5├─┼─┼─┼─┤ │ │ │ │ │
10 /// 6├─┼─┼─┼─┤ P├─Q─R─S─┤T
11 /// 7├─┼─┼─┼─┤ │ │ │ │ │
12 /// 8└─┴─┴─┴─┘ U└─┴─┴─┴─┘Y
13 /// V W X
14 pub static ref ASCII_PROPERTIES : BTreeMap < char , Property > = {
15
16 ...
17
18 vec! [
19
20 /// ///////////////////
21 // dot period .
22 /// ///////////////////
23 (
24 ' . ' ,
25 vec! [
26 ( Medium , vec! [ line ( m , w ) ] ) , // connects down
27 ( Weak , vec! [ line ( m , k ) ] ) , // connects left
28 ( Weak , vec! [ line ( m , o ) ] ) , // connects right
29 ] ,
30 Arc :: new (
31 move | top_left , top , top_right , left , right , bottom_left , bottom , bottom_right | {
32 vec! [
33 // .
34 // |
35 ( bottom . line_strongly_overlap ( c , h ) , vec! [ line ( r , w ) ] ) ,
36 // .
37 // / \
38 ( bottom_left . line_strongly_overlap ( e , i ) && bottom_right . line_strongly_overlap ( a , g ) , vec! [ line ( m , u ) , line ( m , y ) ] ) ,
39 // .-
40 // |
41 ( right . line_overlap ( k , l ) && bottom . line_overlap ( c , h ) , vec! [ arc ( o , r , unit2 ) , line ( r , w ) ] ) ,
42 // .-
43 // |
44 ( right . line_overlap ( k , l ) && bottom_left . line_overlap ( c , h ) , vec! [ arc ( m , cell . bottom_left ( ) . c ( ) , unit4 ) , line ( m , o ) ] ) ,
45 // -.
46 // |
47 ( left . line_overlap ( n , o ) && bottom . line_overlap ( c , h ) , vec! [ arc ( r , k , unit2 ) , line ( r , w ) ] ) ,
48 // -.
49 // |
50 // exemption that bottom right is not a backquote
51 ( ! bottom_right . is ( ' ` ' ) && left . line_overlap ( n , o ) && bottom_right . line_overlap ( c , h ) , vec! [ arc ( cell . bottom_right ( ) . c ( ) , m , unit4 ) , line ( k , m ) ] ) ,
52 // .-
53 // /
54 ( right . line_overlap ( k , l ) && bottom_left . line_overlap ( e , i ) , vec! [ arc ( o , q , unit4 ) , line ( q , u ) ] ) ,
55 // .-
56 // \
57 ( right . line_overlap ( k , l ) && bottom_right . line_overlap ( a , g ) , vec! [ arc ( o , s , between1_2 ) , line ( s , y ) ] ) ,
58 // -.
59 // \
60 ( left . line_overlap ( n , o ) && bottom_right . line_overlap ( a , g ) , vec! [ arc ( s , k , unit4 ) , line ( s , y ) ] ) ,
61 // -.
62 // /
63 ( left . line_overlap ( n , o ) && bottom_left . line_overlap ( e , i ) , vec! [ arc ( q , k , between1_2 ) , line ( u , q ) ] ) ,
64
65 ...
66 ] }
67 )
68 ) ,

Endorse to higher level shapes

1
2 /// First phase of endorsing to shapes, in this case, rects and rounded_rects
3 ///
4 /// This function is calling on endorse methods that is applicable
5 /// to fragments that are touching, to be promoted to a shape.
6 /// These includes: rect, roundedrect,
7 fn endorse_rects ( groups : Vec < Contacts > ) -> ( Vec < Fragment > , Vec < Contacts > ) {
8 let mut fragments = vec! [ ] ;
9 let mut un_endorsed_rect : Vec < Contacts > = vec! [ ] ;
10 for group in groups {
11 if let Some ( fragment ) = is_rect ( group ) {
12 fragments . push ( fragment ) ;
13 } else {
14 un_endorsed_rect . push ( group ) ;
15 }
16 }
17 ( fragments , un_endorsed_rect )
18 }
19
20 ...
21
22 /// group of fragments can be check if they form:
23 /// - rectangle
24 fn is_rect ( fragments : & Vec < Fragment > ) -> bool {
25 if fragments . len ( ) == 4 {
26 let parallels = parallel_aabb_group ( fragments ) ;
27 if parallels . len ( ) == 2 {
28 let ( a1 , a2 ) = parallels [ 0 ] ;
29 let ( b1 , b2 ) = parallels [ 1 ] ;
30 let line_a1 = fragments [ a1 ] . as_line ( ) ;
31 let line_b1 = fragments [ b1 ] . as_line ( ) ;
32 let line_a2 = fragments [ a2 ] . as_line ( ) ;
33 let line_b2 = fragments [ b2 ] . as_line ( ) ;
34 line_a1 . is_touching_aabb_perpendicular ( line_b1 )
35 && line_a2 . is_touching_aabb_perpendicular ( line_b2 )
36 } else {
37 false
38 }
39 } else {
40 false
41 }
42 }
43
44 ...
45
46 /// [X](Done) TODO: search only the subset of contacts that matches the circle.
47 /// if it is a subset then the circle is matched and the non-matching ones are returned
48 pub fn endorse_circle ( search : & Vec < Contacts > ) -> Option < ( & Circle, Vec < usize > ) > {
49 FRAGMENTS_CIRCLE . iter ( ) . rev ( ) . find_map ( | ( contacts , circle ) | {
50 let ( matched , unmatched ) = is_subset_of ( contacts , search ) ;
51 if matched { Some ( ( circle , unmatched ) ) } else { None }
52 } )
53 }
54
55 ...
56
57 /// This function is calling on endorse algorithmn on fragments that
58 /// are neighbors, but not necessarily touching to be promoted to a shape.
59 /// These includes: circle, arc, and line with arrow heads.
60 fn endorse_circles_and_arcs ( groups : Vec < Contacts > ) -> ( Vec < Fragment > , Vec < Contacts > ) {
61 let mut fragments = vec! [ ] ;
62 let mut un_endorsed_circles : Vec < Contacts > = vec! [ ] ;
63 if let Some ( ( circle , unmatched ) ) = circle_map :: endorse_circle ( & groups ) {
64 fragments . push ( circle . clone ( ) . into ( ) ) ;
65 for um in unmatched {
66 un_endorsed_circles . push ( groups [ um ] . clone ( ) ) ;
67 }
68 } else if let Some ( arc ) = circle_map :: endorse_arc ( & groups ) {
69 fragments . push ( arc . clone ( ) . into ( ) ) ;
70 } else {
71 un_endorsed_circles . extend ( groups )
72 }
73 ( fragments , un_endorsed_circles )
74 }
1
2 // ascii art, Center Cell, Center Point, radius
3 pub static ref CIRCLE_MAP : Vec < ( & 'static str , Cell, Point, f32 ) > =
4 vec! [
5 // CIRCLE_1
6 // center 0,0,o, radius = 0.5
7 ( r #"
8 ()
9 "# , Cell :: new ( 0 , 0 ) , Cell :: new ( 0 , 0 ) . o ( ) , 0. 5 ) ,
10
11 ...
12
13 // CIRCLE_4
14 // center: 2,1,m radius: 2.0
15 ( r #"
16 ,-.
17 ( )
18 `-'
19 "# , Cell :: new ( 2 , 1 ) , Cell :: new ( 2 , 1 ) . m ( ) , 2. 0 ) ,
20
21
22 // CIRCLE_12
23 // center:6,3,m radius: 6.0
24 ( r #"
25 _____
26 ,' `.
27 / \
28 ( )
29 \ /
30 `._____.'
31 "# , Cell :: new ( 6 , 3 ) , Cell :: new ( 6 , 3 ) . m ( ) , 6. 0 ) ,
32
33 // CIRCLE_17
34 // center: 8,4,o radius: 8.5
35 ( r #"
36 .--------.
37 ,' `.
38 / \
39 | |
40 | |
41 | |
42 \ /
43 `. .'
44 `--------'
45 "# , Cell :: new ( 8 , 4 ) , Cell :: new ( 8 , 4 ) . o ( ) , 8. 5 ) ,
46
47
48 ...
49
50 // CIRCLE_20
51 // center: 10,5,m radius: 10
52 ( r #"
53 _.-'''''''-._
54 ,' `.
55 / \
56 . .
57 | |
58 | |
59 | |
60 \ /
61 `._ _.'
62 '-.......-'
63 "# , Cell :: new ( 10 , 5 ) , Cell :: new ( 10 , 5 ) . m ( ) , 10. 0 ) ,
64 ] ;
65

Flexibility:

  • Adding behaviours and edge-cases is still simple
  • Due to the grouping of spans and contacts, it is now more efficient to check whether a combination of fragments can be endorsed into a high level shapes.
  • Behavior can be coded according to the properties of their neighboring characters, and/or can also specify that a neighbor should match a specific character. (ie: neighboring character top should be a caret ^ , then this is the behavior)

Modular:

  • Adding more shapes it can endorse to, such as in the circle map is merely putting the ascii art to right next to the existing ones, as oppused to the multiple if-statements in Phase 2
  • Adding endorse code to certain shapes is merely describing the filter rules on the combination of the fragments

Extensiblity:

  • Since the new architecture is now implemented through the use of Buffers. It opens to a lot of possible improvements.
  • Shapes are now properly endorsed, which can be styled with css standard. Which means, users can add crazy css-animation to the shapes.
  • Making the cell buffer as a canvas. Meaning you can draw lines and shapes on it, while the system will try to match the closest character appropriate to the input shape. A possibility of generating an ascii drawing from svg diagrams. The reverse of the functionality of svgbob.

Adaption of svgbob