summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJovansonlee Cesar <ivanceras@gmail.com>2018-07-29 16:29:24 +0800
committerJovansonlee Cesar <ivanceras@gmail.com>2018-07-29 16:29:24 +0800
commitd053c331bf2c1dd76c4def1825dbee55e018619c (patch)
tree3d0d298c6b91575c1575d6e133607a2c9b5a2110
parent6310e44a44c14c12238dcd35f3f496b3e20814d8 (diff)
refactored to modularized components to its corresponding file
-rw-r--r--svgbob/src/block.rs32
-rw-r--r--svgbob/src/box_drawing.rs6
-rw-r--r--svgbob/src/element.rs312
-rw-r--r--svgbob/src/focus_char.rs (renamed from svgbob/src/patterns.rs)375
-rw-r--r--svgbob/src/fragments.rs49
-rw-r--r--svgbob/src/grid.rs820
-rw-r--r--svgbob/src/lib.rs1275
-rw-r--r--svgbob/src/loc.rs122
-rw-r--r--svgbob/src/loc_block.rs250
-rw-r--r--svgbob/src/location.rs162
-rw-r--r--svgbob/src/optimizer.rs14
-rw-r--r--svgbob/src/point.rs37
-rw-r--r--svgbob/src/point_block.rs50
-rw-r--r--svgbob/src/properties.rs196
-rw-r--r--svgbob/src/settings.rs99
-rw-r--r--svgbob/src/svg_element.rs28
16 files changed, 1958 insertions, 1869 deletions
diff --git a/svgbob/src/block.rs b/svgbob/src/block.rs
new file mode 100644
index 0000000..11f0d79
--- /dev/null
+++ b/svgbob/src/block.rs
@@ -0,0 +1,32 @@
+
+/// exact location of point
+/// relative to the Character Block
+/// The block is divided in to 5x5 small blocks
+#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Ord, Eq)]
+pub enum Block {
+ A,
+ B,
+ C,
+ D,
+ E,
+ F,
+ G,
+ H,
+ I,
+ J,
+ K,
+ L,
+ M,
+ N,
+ O,
+ P,
+ Q,
+ R,
+ S,
+ T,
+ U,
+ V,
+ W,
+ X,
+ Y,
+}
diff --git a/svgbob/src/box_drawing.rs b/svgbob/src/box_drawing.rs
index 5a8e782..e84fa7f 100644
--- a/svgbob/src/box_drawing.rs
+++ b/svgbob/src/box_drawing.rs
@@ -1,10 +1,10 @@
-use fragments::Block;
-use fragments::Block::{A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y};
+use block::Block;
+use block::Block::{A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y};
use fragments::Fragment;
use fragments::{arc, line};
-use properties::PointBlock;
+use point_block::PointBlock;
use properties::Properties;
pub fn box_drawing(ch: &char) -> (Vec<Block>, Vec<Fragment>) {
diff --git a/svgbob/src/element.rs b/svgbob/src/element.rs
new file mode 100644
index 0000000..5676f2a
--- /dev/null
+++ b/svgbob/src/element.rs
@@ -0,0 +1,312 @@
+use std::cmp::Ordering;
+use svg_element::SvgElement;
+use svg::Node;
+use svg::node::element::{
+ Circle as SvgCircle,
+ Definitions,
+ Line as SvgLine,
+ Marker,
+ Path as SvgPath,
+ Rectangle as SvgRect,
+ Style,
+ Text as SvgText,
+};
+use grid::svg_escape;
+use point::collinear;
+use settings::Settings;
+
+use point::Point;
+use loc::Loc;
+use element::{
+ Stroke::{Solid,Dashed},
+ ArcFlag::{Minor,Major},
+ Feature::{Arrow,ArrowStart,Circle},
+};
+use unicode_width::UnicodeWidthStr;
+
+#[derive(Debug, Clone, PartialEq, PartialOrd )]
+pub enum Element {
+ Circle(Point, f32, String),
+ Line(Point, Point, Stroke, Vec<Feature>),
+ Arc(Point, Point, f32, ArcFlag, bool, Stroke, Vec<Feature>),
+ Text(Loc, String),
+ Path(Point, Point, String, Stroke),
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
+pub enum Stroke {
+ Solid,
+ Dashed,
+}
+
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
+pub enum Feature {
+ ArrowStart, // start arrow
+ Arrow, //end
+ Circle, //start
+}
+
+
+
+#[derive(Debug, Clone, PartialEq, PartialOrd)]
+pub enum ArcFlag {
+ Major,
+ Minor,
+}
+
+impl Ord for Element{
+ fn cmp(&self, other: &Self) -> Ordering{
+ if let Some(order) = self.partial_cmp(&other){
+ return order
+ }
+ Ordering::Less
+ }
+}
+impl Eq for Element{
+}
+
+pub fn line(a: &Point, b: &Point) -> Element {
+ Element::Line(a.clone(), b.clone(), Solid, vec![])
+}
+
+pub fn solid_circle(c: &Point, r: f32) -> Element {
+ Element::Circle(c.clone(), r, "solid".to_string())
+}
+
+pub fn arrow_arc(a: &Point, b: &Point, r: f32) -> Element {
+ Element::Arc(a.clone(), b.clone(), r, Minor, false, Solid, vec![Arrow])
+}
+
+pub fn arrow_sweep_arc(a: &Point, b: &Point, r: f32) -> Element {
+ Element::Arc(a.clone(), b.clone(), r.clone(), Minor, true, Solid, vec![Arrow])
+}
+
+pub fn arc(a: &Point, b: &Point, r: f32) -> Element {
+ Element::Arc(a.clone(), b.clone(), r, Minor, false, Solid, vec![])
+}
+
+pub fn arc_major(a: &Point, b: &Point, r: f32) -> Element {
+ Element::Arc(a.clone(), b.clone(), r, Major, false, Solid, vec![])
+}
+
+pub fn open_circle(c: &Point, r: f32) -> Element {
+ Element::Circle(c.clone(), r.clone(), "open".to_string())
+}
+
+pub fn arrow_line(s: &Point, e: &Point) -> Element {
+ Element::Line(s.clone(), e.clone(), Solid, vec![Arrow])
+}
+
+pub fn start_arrow_line(s: &Point, e: &Point) -> Element {
+ Element::Line(s.clone(), e.clone(), Solid, vec![ArrowStart, Arrow,Circle])
+}
+
+pub fn text(loc: &Loc, txt: &str) -> Element {
+ Element::Text(loc.clone(), svg_escape(txt))
+}
+
+pub fn blank_text(loc: &Loc) -> Element {
+ text(loc, " ".into())
+}
+
+impl Element {
+ // if this element can reduce the other, return the new reduced element
+ // for line it has to be collinear and in can connect start->end->start
+ // for text, the other text should apear on the right side of this text
+ pub fn reduce(&self, other: &Element) -> Option<Element> {
+ match *self {
+ Element::Line(ref s, ref e, ref stroke, ref feature) => {
+ match *other {
+ Element::Line(ref s2, ref e2, ref stroke2, ref feature2) => {
+ // note: dual 3 point check for trully collinear lines
+ if collinear(s, e, s2)
+ && collinear(s, e, e2)
+ && stroke == stroke2{
+
+ // line1 line2
+ // s-----e s2-----e2
+ // s----------------e2
+ if e == s2 {
+ // -----
+ // o----
+ let cond1 = feature.is_empty() || (feature.contains(&Circle) && feature.len() == 1);
+ // ------
+ // ------>
+ let cond2 = feature2.is_empty() || (feature2.contains(&Arrow) && feature2.len() ==1);
+ if cond1 && cond2{
+ return Some(Element::Line(
+ s.clone(),
+ e2.clone(),
+ stroke.clone(),
+ feature2.clone()
+ ));
+ }
+ }
+ // line1 line2
+ // s------e e2-------s2
+ // s-------------------s2
+ else if e == e2{
+ // ------- --------
+ // o------ ---------
+ if (feature.is_empty() || (feature.contains(&Circle) && feature.len() == 1))
+ && feature2.is_empty(){
+ return Some(Element::Line(
+ s.clone(),
+ s2.clone(),
+ stroke.clone(),
+ feature2.clone()
+ ));
+ }
+ }
+ // line1 line2
+ // e------s s2------e2
+ // s------------------e2
+ else if s == s2{
+ // ------- -------
+ // ------- ------->
+ if feature.is_empty()
+ && (feature2.is_empty() || (feature2.contains(&Arrow) && feature2.len() ==1 )){
+ return Some(Element::Line(
+ e.clone(),
+ e2.clone(),
+ stroke.clone(),
+ feature2.clone()
+ ));
+ }
+ }
+ // line1 line2
+ // e------s e2------s2
+ // e---------------------s2
+ //
+ else if s == e2{
+ // ----- -----
+ // ----- ----o
+ // <---- -----
+ // <---- ----o
+ let cond1 = feature.is_empty() || (feature.contains(&Arrow) && feature.len() == 1);
+ let cond2 = feature2.is_empty() || (feature2.contains(&Circle) && feature2.len() == 1);
+ if cond1 && cond2{
+ return Some(Element::Line(
+ s2.clone(),
+ e.clone(),
+ stroke.clone(),
+ feature.clone(),
+ ));
+ }
+ }
+ }
+ return None;
+ }
+ _ => None,
+ }
+ }
+ Element::Text(ref loc, ref text) => {
+ match *other {
+ Element::Text(ref loc2, ref text2) => {
+ // reduce if other is next to it
+ let uwidth = text.width() as i32;
+ if loc.y == loc2.y && loc.x + uwidth == loc2.x {
+ let merged_text = text.clone() + text2;
+ let reduced = Some(Element::Text(loc.clone(), merged_text));
+ reduced
+ } else {
+ None
+ }
+ }
+ _ => None,
+ }
+ }
+ _ => None,
+ }
+ }
+
+ /// convert drawing element to SVG element
+ pub fn to_svg(&self, settings: &Settings) -> SvgElement {
+ match *self {
+ Element::Circle(ref c, r, ref class) => {
+ let svg_circle = SvgCircle::new()
+ .set("class", class.clone())
+ .set("cx", c.x)
+ .set("cy", c.y)
+ .set("r", r);
+
+ SvgElement::Circle(svg_circle)
+ }
+ Element::Line(ref s, ref e, ref stroke, ref features) => {
+ let mut svg_line = SvgLine::new()
+ .set("x1", s.x)
+ .set("y1", s.y)
+ .set("x2", e.x)
+ .set("y2", e.y);
+ for feature in features{
+ match *feature {
+ Arrow => {
+ svg_line.assign("marker-end", "url(#triangle)");
+ }
+ ArrowStart => {
+ svg_line.assign("marker-start", "url(#triangle)");
+ }
+ Circle => {
+ svg_line.assign("marker-start", "url(#circle)");
+ }
+ };
+ }
+ match *stroke {
+ Solid => (),
+ Dashed => {
+ svg_line.assign("stroke-dasharray", (3, 3));
+ svg_line.assign("fill", "none");
+ }
+ };
+
+ SvgElement::Line(svg_line)
+ }
+ Element::Arc(ref s, ref e, radius, ref arc_flag, sweep, _, ref features) => {
+ let sweept = if sweep { "1" } else { "0" };
+ let arc_flag = match *arc_flag {
+ Major => "1",
+ Minor => "0",
+ };
+ let d = format!(
+ "M {} {} A {} {} 0 {} {} {} {}",
+ s.x, s.y, radius, radius, arc_flag, sweept, e.x, e.y
+ );
+ let mut svg_arc = SvgPath::new().set("d", d).set("fill", "none");
+ for feature in features{
+ match *feature {
+ Arrow => {
+ svg_arc.assign("marker-end", "url(#triangle)");
+ }
+ ArrowStart => {
+ svg_arc.assign("marker-start", "url(#triangle)");
+ }
+ Circle => {
+ svg_arc.assign("marker-start", "url(#circle)");
+ }
+ };
+ }
+ SvgElement::Path(svg_arc)
+ }
+ Element::Text(ref loc, ref string) => {
+ let sx = loc.x as f32 * settings.text_width + settings.text_width / 8.0;
+ let sy = loc.y as f32 * settings.text_height + settings.text_height * 3.0 / 4.0;
+ let mut svg_text = SvgText::new().set("x", sx).set("y", sy);
+ let text_node = svg::node::Text::new(string.to_string());
+ svg_text.append(text_node);
+ SvgElement::Text(svg_text)
+ }
+ Element::Path(_, _, ref d, ref stroke) => {
+ let mut path = SvgPath::new().set("d", d.to_owned()).set("fill", "none");
+
+ match *stroke {
+ Solid => (),
+ Dashed => {
+ path.assign("stroke-dasharray", (3, 3));
+ }
+ };
+ SvgElement::Path(path)
+ }
+ }
+ }
+}
diff --git a/svgbob/src/patterns.rs b/svgbob/src/focus_char.rs
index ac7d2cc..cfadb2b 100644
--- a/svgbob/src/patterns.rs
+++ b/svgbob/src/focus_char.rs
@@ -1,252 +1,21 @@
-use properties::Characteristic;
-use properties::Signal;
-use Element;
-use Grid;
-use Loc;
-use Point;
-use Settings;
-
-use fragments::Block;
-use fragments::Block::{A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y};
-use properties::Location;
-use properties::PointBlock;
-
+use block::Block;
use fragments::Fragment;
+use loc::Loc;
+use grid::Grid;
+use properties::{
+ Properties,
+ Signal::{self,Weak,Medium,Strong},
+ Can::{self,ConnectTo,Is,IsStrongAll},
+ Characteristic,
+};
+use point_block::PointBlock;
+use point::Point;
+use loc_block::LocBlock;
+use element::Element;
use fragments::Fragment::Text;
-
-use properties::Can;
-use properties::Properties;
-use properties::Signal::{Medium, Strong, Weak};
-
-use {arc, arrow_line, start_arrow_line, line, open_circle, solid_circle, text};
-
-use properties::Can::{ConnectTo, Is, IsStrongAll};
-
-struct LocBlock {
- loc: Loc,
- settings: Settings,
-}
-
-impl LocBlock {
- fn text_width(&self) -> f32 {
- self.settings.text_width
- }
-
- fn text_height(&self) -> f32 {
- self.settings.text_height
- }
-
- fn loc_x(&self) -> f32 {
- self.loc.x as f32
- }
-
- fn loc_y(&self) -> f32 {
- self.loc.y as f32
- }
-
- /// 1 unit in x dimension is 1/4 of the textwidth
- /// used in calculating the radius, adjustments
- /// in the Fragment PointBlock
- fn unit_x(&self) -> f32 {
- self.text_width() * 1.0 / 4.0
- }
-
- /// 1 unit along y dimension is 1/4 of the textwidth
- #[allow(unused)]
- fn unit_y(&self) -> f32 {
- self.text_height() * 1.0 / 4.0
- }
-
- /// x coordinate on increment of 1/4 of text width
- fn x0(&self) -> f32 {
- self.loc_x() * self.text_width()
- }
-
- fn x1(&self) -> f32 {
- (self.loc_x() + 1.0 / 4.0) * self.text_width()
- }
-
- fn x2(&self) -> f32 {
- (self.loc_x() + 1.0 / 2.0) * self.text_width()
- }
-
- fn x3(&self) -> f32 {
- (self.loc_x() + 3.0 / 4.0) * self.text_width()
- }
-
- fn x4(&self) -> f32 {
- (self.loc_x() + 1.0) * self.text_width()
- }
-
- /// y coordinate on increment of 1/4 of text_height
- fn y0(&self) -> f32 {
- self.loc_y() * self.text_height()
- }
-
- fn y1(&self) -> f32 {
- (self.loc_y() + 1.0 / 4.0) * self.text_height()
- }
-
- fn y2(&self) -> f32 {
- (self.loc_y() + 1.0 / 2.0) * self.text_height()
- }
-
- fn y3(&self) -> f32 {
- (self.loc_y() + 3.0 / 4.0) * self.text_height()
- }
-
- fn y4(&self) -> f32 {
- (self.loc_y() + 1.0) * self.text_height()
- }
-
- /// 1st row a,b,c,d,e
- fn a(&self) -> Point {
- Point::new(self.x0(), self.y0())
- }
-
- fn b(&self) -> Point {
- Point::new(self.x1(), self.y0())
- }
-
- fn c(&self) -> Point {
- Point::new(self.x2(), self.y0())
- }
-
- fn d(&self) -> Point {
- Point::new(self.x3(), self.y0())
- }
-
- fn e(&self) -> Point {
- Point::new(self.x4(), self.y0())
- }
-
- /// 2nd row f,g,h,i,j
- fn f(&self) -> Point {
- Point::new(self.x0(), self.y1())
- }
-
- fn g(&self) -> Point {
- Point::new(self.x1(), self.y1())
- }
-
- fn h(&self) -> Point {
- Point::new(self.x2(), self.y1())
- }
-
- fn i(&self) -> Point {
- Point::new(self.x3(), self.y1())
- }
-
- fn j(&self) -> Point {
- Point::new(self.x4(), self.y1())
- }
-
- /// 3rd row k,l,m,n,o
- fn k(&self) -> Point {
- Point::new(self.x0(), self.y2())
- }
-
- fn l(&self) -> Point {
- Point::new(self.x1(), self.y2())
- }
-
- fn m(&self) -> Point {
- Point::new(self.x2(), self.y2())
- }
-
- fn n(&self) -> Point {
- Point::new(self.x3(), self.y2())
- }
-
- fn o(&self) -> Point {
- Point::new(self.x4(), self.y2())
- }
-
- /// 4th row p,q,r,s,t
- fn p(&self) -> Point {
- Point::new(self.x0(), self.y3())
- }
-
- fn q(&self) -> Point {
- Point::new(self.x1(), self.y3())
- }
-
- fn r(&self) -> Point {
- Point::new(self.x2(), self.y3())
- }
-
- fn s(&self) -> Point {
- Point::new(self.x3(), self.y3())
- }
-
- fn t(&self) -> Point {
- Point::new(self.x4(), self.y3())
- }
-
- /// 5th row u,v,w,x,y
- fn u(&self) -> Point {
- Point::new(self.x0(), self.y4())
- }
-
- fn v(&self) -> Point {
- Point::new(self.x1(), self.y4())
- }
-
- fn w(&self) -> Point {
- Point::new(self.x2(), self.y4())
- }
-
- fn x(&self) -> Point {
- Point::new(self.x3(), self.y4())
- }
-
- fn y(&self) -> Point {
- Point::new(self.x4(), self.y4())
- }
-
- pub fn to_point(&self, pb: &PointBlock) -> Point {
- // move loc to the additional location relative to itself
- let loc = if let Some(ref pbloc) = pb.location {
- self.loc.from_location(&pbloc)
- } else {
- self.loc.clone()
- };
- let lb = LocBlock {
- loc: loc,
- settings: self.settings.clone(),
- };
- let mut p = match pb.block {
- A => lb.a(),
- B => lb.b(),
- C => lb.c(),
- D => lb.d(),
- E => lb.e(),
- F => lb.f(),
- G => lb.g(),
- H => lb.h(),
- I => lb.i(),
- J => lb.j(),
- K => lb.k(),
- L => lb.l(),
- M => lb.m(),
- N => lb.n(),
- O => lb.o(),
- P => lb.p(),
- Q => lb.q(),
- R => lb.r(),
- S => lb.s(),
- T => lb.t(),
- U => lb.u(),
- V => lb.v(),
- W => lb.w(),
- X => lb.x(),
- Y => lb.y(),
- };
- let unit = self.unit_x();
- p.adjust(pb.adjust_x * unit, pb.adjust_y * unit);
- p
- }
-}
+use element::{line,arrow_line,start_arrow_line,arc,open_circle,solid_circle,text};
+use location::Location;
+use settings::Settings;
#[derive(Debug, Clone)]
pub struct FocusChar<'g> {
@@ -583,115 +352,3 @@ impl<'g> FocusChar<'g> {
self.get(&self.loc.bottom_right())
}
}
-
-#[cfg(test)]
-mod test {
- use super::super::Loc;
- use super::super::Settings;
- use super::FocusChar;
- use super::Grid;
- use fragments::Direction::*;
- use properties::Location;
-
- use fragments::Block::{O, U, Y};
-
- #[test]
- fn test_adjascent() {
- let g = Grid::from_str("a统öo͡͡͡", &Settings::compact());
- let fc = FocusChar::new(&Loc::new(1, 0), &g);
- println!("{:?}", fc);
- assert!(fc.left().is('a'));
- assert!(fc.right().right().is('ö'));
- }
-
- #[test]
- fn test100() {
- // ._
- let g = Grid::from_str(".-", &Settings::separate_lines());
- let fc = FocusChar::new(&Loc::new(0, 0), &g);
- let (frags, _consumed) = fc.get_fragments();
- println!("frags: {:?}", frags);
- assert!(fc.is_intensified(&O));
- assert!(fc.can_be_strong_block(&O));
- }
-
- #[test]
- fn test_location() {
- // ._
- let g = Grid::from_str(".-", &Settings::separate_lines());
- let fc = FocusChar::new(&Loc::new(0, 0), &g);
- let (_frags, _consumed) = fc.get_fragments();
- let go_right = fc.from_location(&Location::go(Right));
- let right = fc.right();
- let right2 = fc.in_right(2);
- let mut right2_loop = fc.clone();
- for _ in 0..2 {
- right2_loop = right2_loop.in_right(1);
- }
- println!("in right 2: {:?}", right2.loc);
- println!("in right 2 loop: {:?}", right2_loop.loc);
- assert_eq!(right2.loc, right2_loop.loc);
- assert_eq!(go_right.loc, right.loc);
- }
-
- #[test]
- fn test_loc() {
- let g = Grid::from_str("", &Settings::separate_lines());
- let fc = FocusChar::new(&Loc::new(0, 0), &g);
- let right = fc.right();
- let in_right = fc.in_right(1);
- assert_eq!(Loc::new(1, 0), right.loc);
- assert_eq!(Loc::new(1, 0), in_right.loc);
- }
-
- #[test]
- fn test1() {
- // ._
- let g = Grid::from_str("._", &Settings::separate_lines());
- let fc = FocusChar::new(&Loc::new(0, 0), &g);
- println!("focus char: {:#?}", fc);
- let (frags, _consumed) = fc.get_fragments();
- println!("frags: {:?}", frags);
- assert!(!fc.is_intensified(&U));
- assert!(fc.is_intensified(&Y));
- }
- #[test]
- fn test2() {
- // ._
- let g = Grid::from_str("._", &Settings::separate_lines());
- let fc = FocusChar::new(&Loc::new(1, 0), &g);
- println!("focus char: {:#?}", fc);
- let (frags, _consumed) = fc.get_fragments();
- println!("frags: {:?}", frags);
- assert!(!fc.is_intensified(&Y));
- assert!(!fc.is_intensified(&U));
- assert!(fc.can_be_strong_block(&Y));
- assert!(fc.can_be_strong_block(&U));
- }
-
- #[test]
- fn test_no_character() {
- use properties::Properties;
- use {FocusChar, Grid, Loc, Settings};
-
- let g = Grid::from_str(".l", &Settings::separate_lines());
- let fc = FocusChar::new(&Loc::new(1, 0), &g);
- println!("focus char: {:#?}", fc);
- let character = fc.ch.get_characteristic();
- println!("character: {:#?}", character);
- assert!(character.is_none());
- }
-
- #[test]
- fn test_has_character() {
- use properties::Properties;
- use {FocusChar, Grid, Loc, Settings};
-
- let g = Grid::from_str(".╦", &Settings::separate_lines());
- let fc = FocusChar::new(&Loc::new(1, 0), &g);
- println!("focus char: {:#?}", fc);
- let character = fc.ch.get_characteristic();
- println!("character: {:#?}", character);
- assert!(character.is_some());
- }
-}
diff --git a/svgbob/src/fragments.rs b/svgbob/src/fragments.rs
index c5c71fd..dc485e2 100644
--- a/svgbob/src/fragments.rs
+++ b/svgbob/src/fragments.rs
@@ -1,39 +1,8 @@
use self::Fragment::{Arc, ArrowLine, Line, OpenCircle, SolidCircle, StartArrowLine};
-use properties::PointBlock;
+use point_block::PointBlock;
use std::cmp::Ordering;
-/// exact location of point
-/// relative to the Character Block
-/// The block is divided in to 5x5 small blocks
-#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Ord, Eq)]
-pub enum Block {
- A,
- B,
- C,
- D,
- E,
- F,
- G,
- H,
- I,
- J,
- K,
- L,
- M,
- N,
- O,
- P,
- Q,
- R,
- S,
- T,
- U,
- V,
- W,
- X,
- Y,
-}
/// These are non-final drawing elements
/// Lines most likely fall on the collinear line
@@ -69,19 +38,3 @@ pub fn solid_circle(c: &PointBlock, r: i32) -> Fragment {
SolidCircle(c.clone(), r)
}
-/// 8 directions which a character can connect to
-/// \|/
-/// -+-
-/// /|\
-
-#[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq)]
-pub enum Direction {
- Top,
- Bottom,
- Left,
- Right,
- TopLeft,
- TopRight,
- BottomLeft,
- BottomRight,
-}
diff --git a/svgbob/src/grid.rs b/svgbob/src/grid.rs
new file mode 100644
index 0000000..d8fb129
--- /dev/null
+++ b/svgbob/src/grid.rs
@@ -0,0 +1,820 @@
+use loc::Loc;
+use focus_char::FocusChar;
+use optimizer::Optimizer;
+use svg_element::SvgElement;
+use svg::node::element::SVG;
+use svg::Node;
+use svg::node::element::{
+ Circle as SvgCircle,
+ Definitions,
+ Line as SvgLine,
+ Marker,
+ Path as SvgPath,
+ Rectangle as SvgRect,
+ Style,
+ Text as SvgText,
+};
+use element::Element;
+use pom::TextInput;
+use pom::parser::{sym,none_of};
+use settings::Settings;
+use unicode_width::UnicodeWidthChar;
+
+#[derive(Debug)]
+pub struct Grid {
+ pub settings: Settings,
+ index: Vec<Vec<String>>,
+ text_elm: Vec<(usize, usize, String)>,
+}
+impl Grid {
+ /// instantiate a grid from input ascii text
+ /// Issues:
+ /// 1. 2-width, 5 bytes, single character i.e. 统
+ /// 2. 1-width, 2 bytes, single character i.e. ö
+ /// 3. 1-width, 3 bytes, single character i.e. o͡͡͡
+ pub fn from_str(s: &str, settings: &Settings) -> Grid {
+ let lines: Vec<&str> = s.lines().collect();
+ let mut rows: Vec<Vec<String>> = Vec::with_capacity(lines.len());
+ let mut text_elm: Vec<(usize, usize, String)> = vec![];
+ for (y, line) in lines.iter().enumerate() {
+ let (line, escaped_texts): (String, Vec<(usize, String)>) = exclude_escaped_text(line);
+ let mut row: Vec<String> = Vec::with_capacity(line.chars().count());
+ for (x, escaped) in escaped_texts {
+ text_elm.push((x, y, svg_escape(&escaped)));
+ }
+ for ch in line.chars() {
+ if let Some(1) = ch.width() {
+ row.push(format!("{}", ch));
+ } else if let Some(2) = ch.width() {
+ row.push(format!("{}", ch));
+ // HACK: push a blank to the next cell,
+ //in order to make this character twice as
+ // big and aligns the next succeeding characters on
+ // this row
+ row.push(format!("\0"));
+ }
+ // if zero width char, append it to the previous string
+ else if let Some(0) = ch.width() {
+ let prev: Option<String> = row.pop();
+ match prev {
+ Some(mut prev) => {
+ prev.push(ch);
+ row.push(prev);
+ }
+ None => (),
+ }
+ }
+ }
+ rows.push(row);
+ }
+ Grid {
+ settings: settings.clone(),
+ index: rows,
+ text_elm: text_elm,
+ }
+ }
+
+ /// reassemble the Grid content into a string
+ /// trimming unneeded whitespace to the right for every line
+ pub fn to_string(&self) -> String {
+ let mut buff = String::new();
+ let mut do_ln = false;
+ for row in self.index.iter() {
+ let mut line = String::new();
+ if do_ln {
+ //first line don't do \n
+ buff.push('\n');
+ } else {
+ do_ln = true;
+ }
+ for cell in row {
+ if cell == "\0" {
+; //easy make over the full-width hack of the string
+ } else {
+ line.push_str(cell);
+ }
+ }
+ buff.push_str(&line);
+ }
+ buff
+ }
+
+ pub fn rows(&self) -> usize {
+ self.index.len()
+ }
+
+ /// get the maximum row len
+ pub fn columns(&self) -> usize {
+ self.index.iter().map(|r| r.len()).max().unwrap_or(0)
+ }
+
+ /// get a character at this location
+ /// widths are computed since there are
+ /// characters that spans 2 columns
+ /// and characters that has 0 width
+ ///
+ pub fn get(&self, loc: &Loc) -> Option<&String> {
+ match self.index.get(loc.y as usize) {
+ Some(row) => row.get(loc.x as usize),
+ None => None,
+ }
+ }
+
+ /// put a text into this location
+ /// prepare the grid for this location first
+ pub fn put(&mut self, loc: &Loc, s: &str) {
+ let new_loc = self.accomodate(loc);
+ if let Some(row) = self.index.get_mut(new_loc.y as usize) {
+ if let Some(cell) = row.get_mut(new_