From 32c9bc5b4012d791ee288b8d1b47159239ed2687 Mon Sep 17 00:00:00 2001 From: Shautvast Date: Wed, 22 Jan 2025 17:25:32 +0100 Subject: [PATCH] e2e rendering 0.1 kinda works --- output_multiple_nodes.svg | 6 +- src/lib.rs | 32 +++++- src/main.rs | 11 ++- src/parse/parser.rs | 60 +++++++++++- src/render/mod.rs | 2 +- src/render/rendering_svg_elements.rs | 6 +- src/render/svg_node.css | 2 +- src/render/svglib/circle.rs | 22 ++--- src/render/svglib/div.rs | 11 +-- src/render/svglib/ellipse.rs | 30 +++--- src/render/svglib/foreign_object.rs | 7 +- src/render/svglib/group.rs | 17 ++-- src/render/svglib/image.rs | 36 +++---- src/render/svglib/line.rs | 10 +- src/render/svglib/link.rs | 26 +++-- src/render/svglib/mod.rs | 12 +-- src/render/svglib/path.rs | 23 ++--- src/render/svglib/rect.rs | 11 ++- src/render/svglib/svg.rs | 39 +++----- src/render/svglib/text.rs | 24 +++-- src/render/svgrender2.rs | 141 ++++++++++++++++++++++++++- 21 files changed, 356 insertions(+), 172 deletions(-) diff --git a/output_multiple_nodes.svg b/output_multiple_nodes.svg index ef9e6e0..8ad8a2e 100644 --- a/output_multiple_nodes.svg +++ b/output_multiple_nodes.svg @@ -1,5 +1,5 @@ - \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index c88079b..b3b4889 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ pub mod parse; pub mod render; +use crate::Element::{Edge, Node}; use parse::tokens::TokenType; use std::collections::HashMap; @@ -10,7 +11,7 @@ pub struct Vis { pub styles: Vec, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Element { Node(String, Option, Vec), Edge(String, String, TokenType, Option), @@ -28,6 +29,33 @@ impl Element { ) -> Element { Element::Edge(id, source, token, label) } + + pub fn to_string(&self) -> String { + match self { + Node(id, label, children) => { + let mut s = String::new(); + s.push_str(&format!( + "Node {{{}: {}", + id, + label.as_ref().unwrap_or(&"".to_string()) + )); + for child in children { + s.push_str(&format!(" {}", child.to_string())); + } + s.push_str("}"); + s + } + Edge(id, source, token, label) => { + let mut s = "Edge {{".to_string(); + s.push_str(&format!("{} {} {:?}", id, source, token)); + if let Some(label) = label { + s.push_str(&format!(" {}", label)); + } + s.push_str("}"); + s + } + } + } } #[derive(Debug, Clone)] @@ -39,6 +67,6 @@ pub struct StyleNode { #[derive(Debug, Clone)] pub enum ContainerType { - Node, + NonGroup, // needs thinking about Group, } diff --git a/src/main.rs b/src/main.rs index 560b1d6..cd48301 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,24 @@ use anyhow::anyhow; use std::env::args; use std::fs; +use std::fs::File; +use std::io::Write; +use std::process::exit; +use vis::render::svgrender2::SvgRender; +use vis::render::Renderer; fn main() -> anyhow::Result<()> { let args: Vec = args().collect(); if args.len() != 2 { - return Err(anyhow!("Usage: vis vis-file")); + eprintln!("Usage: vis vis-file"); + exit(-64); } else { let vis_file = read_file(&args[1])?; let vis = vis::parse::parse_vis(vis_file.as_str())?; println!("{:?}", vis); + let svg_bytes = SvgRender {}.render(vis)?; + let mut file = File::create("bank.svg").expect("Unable to create file"); + file.write_all(&svg_bytes).expect("Unable to write data"); } Ok(()) diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 937394c..f595361 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -10,7 +10,7 @@ use std::collections::HashMap; pub fn parse_vis(contents: &str) -> anyhow::Result { let tokens = crate::parse::scanner::scan(contents)?; - println!("{:?}", tokens); + // println!("{:?}", tokens); let mut parser = Parser::new(tokens); Ok(Vis { @@ -93,8 +93,9 @@ impl Parser { fn string(&mut self) -> anyhow::Result { let text = self.peek().clone(); + let text = text.lexeme[1..text.lexeme.len() - 1].to_owned(); self.consume(Str, "Expected quoted string")?; - Ok(text.lexeme.to_owned()) + Ok(text) } fn text(&mut self) -> anyhow::Result { @@ -164,10 +165,10 @@ impl Parser { if self.match_token(Group) { ContainerType::Group } else { - ContainerType::Node + ContainerType::NonGroup } } else { - ContainerType::Node + ContainerType::NonGroup }) } @@ -231,3 +232,54 @@ impl Parser { &self.tokens[self.current] } } + +#[cfg(test)] +mod tests { + use crate::Element; + + #[test] + fn test_parse() { + let contents = r#"structure { + top: "top-node" { + child: "child-node" { + } + } + }"#; + let vis = crate::parse::parser::parse_vis(contents).ok(); + + assert!(vis.is_some(), "Parsed structure should not be None"); + + if let Some(vis) = vis { + // Validate the parsed structure by asserting its elements + assert_eq!( + vis.structure.len(), + 1, + "The structure should contain one top-level element" + ); + + if let Element::Node(id, title, children) = &vis.structure[0] { + assert_eq!(id, "top", "The ID of the first node should be 'top'"); + assert_eq!( + title.as_ref().unwrap(), + &"top-node".to_owned(), + "The title of the first node should be 'top-node'" + ); + assert_eq!(children.len(), 1); + let child = &children[0]; + if let Element::Node(id, title, children) = child { + assert_eq!(id, "child", "The ID of the second node should be 'child'"); + assert_eq!( + title.as_ref().unwrap(), + &"child-node".to_owned(), + "The title of the second node should be 'child-node'" + ); + assert_eq!(children.len(), 0); + } + } else { + panic!("The top-level element should be a Node"); + } + } else { + panic!("Parsed structure was unexpectedly None"); + } + } +} diff --git a/src/render/mod.rs b/src/render/mod.rs index d1cb3f0..1ceabaa 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -4,7 +4,7 @@ pub mod html; mod rendering_svg_elements; mod svg_renderer; pub mod svglib; -mod svgrender2; +pub mod svgrender2; /// trait for turning the object model into a byte representation pub trait Renderer { diff --git a/src/render/rendering_svg_elements.rs b/src/render/rendering_svg_elements.rs index 22f31ce..d28e6b2 100644 --- a/src/render/rendering_svg_elements.rs +++ b/src/render/rendering_svg_elements.rs @@ -1,7 +1,7 @@ use crate::render::svglib::rect::rect; use crate::render::svglib::svg::{svg, Svg}; use crate::render::svglib::text::text; -use crate::{Element, StyleNode, Vis}; +use crate::{Element, Vis}; pub fn render_vis_with_grid_layout( vis: &Vis, @@ -54,7 +54,7 @@ fn layout_box( let child_y = current_y + row as f32 * (grid_cell_size + spacing); match element { - Element::Node(id, label, children) => { + Element::Node(_, label, children) => { let (_, _) = layout_box( svg, children, @@ -128,7 +128,7 @@ mod tests { // Create a mock StyleNode let style_node = StyleNode { id_ref: "node_1".to_string(), - containertype: ContainerType::Node, + containertype: ContainerType::NonGroup, attributes: [("fill".to_string(), "white".to_string())] .iter() .cloned() diff --git a/src/render/svg_node.css b/src/render/svg_node.css index 7325bd8..e8e53c6 100644 --- a/src/render/svg_node.css +++ b/src/render/svg_node.css @@ -1,5 +1,5 @@ svg { - background-color: white; + background-color: gray; } .node { diff --git a/src/render/svglib/circle.rs b/src/render/svglib/circle.rs index ca1e9dd..37a4c91 100644 --- a/src/render/svglib/circle.rs +++ b/src/render/svglib/circle.rs @@ -1,4 +1,4 @@ -use crate::render::svglib::{att, att_str3, Att, Element, ElementType, Shape, Value}; +use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Shape, Value}; pub fn circle() -> Circle { Circle::new() @@ -7,23 +7,23 @@ pub fn circle() -> Circle { pub struct Circle(Vec); impl Circle { - fn new() -> Self { + pub fn new() -> Self { Self(vec![]) } - fn id>(mut self, id: V) -> Self { + pub fn id>(mut self, id: V) -> Self { self.0.push(att("id", id)); self } - fn cx>(mut self, cx: V) -> Self { + pub fn cx>(mut self, cx: V) -> Self { self.0.push(att("cx", cx)); self } - fn cy>(mut self, cy: V) -> Self { + pub fn cy>(mut self, cy: V) -> Self { self.0.push(att("cy", cy)); self } - fn r>(mut self, r: V) -> Self { + pub fn r>(mut self, r: V) -> Self { self.0.push(att("r", r)); self } @@ -51,16 +51,16 @@ impl Element for Circle { ElementType::Circle } - fn atts(&self) -> &[Att] { - &self.0 - } - fn to_string(&self) -> String { format!( r#""#, - self.0.iter().map(att_str3).collect::() + self.0.iter().map(att_to_string).collect::() ) } + + fn atts(&self) -> &[Att] { + &self.0 + } } #[cfg(test)] diff --git a/src/render/svglib/div.rs b/src/render/svglib/div.rs index 02c31fa..f11bbfc 100644 --- a/src/render/svglib/div.rs +++ b/src/render/svglib/div.rs @@ -1,4 +1,4 @@ -use crate::render::svglib::{att, att_str3, Att, Value}; +use crate::render::svglib::{att, att_to_string, Att, Value}; pub fn div() -> Div { Div::new() @@ -17,10 +17,7 @@ impl Div { } } - pub fn id(&mut self, id: V) - where - V: Into, - { + pub fn id>(&mut self, id: V) { self.atts.push(att("id", id)); } @@ -32,7 +29,7 @@ impl Div { self } - pub fn innerHTML>(mut self, html: V) -> Self { + pub fn inner_html>(mut self, html: V) -> Self { self.child = html.into(); self } @@ -40,7 +37,7 @@ impl Div { pub fn to_string(&self) -> String { format!( r#"
{}
"#, - self.atts.iter().map(att_str3).collect::(), + self.atts.iter().map(att_to_string).collect::(), self.child ) } diff --git a/src/render/svglib/ellipse.rs b/src/render/svglib/ellipse.rs index 4271395..94a8a6f 100644 --- a/src/render/svglib/ellipse.rs +++ b/src/render/svglib/ellipse.rs @@ -1,4 +1,4 @@ -use crate::render::svglib::{att, att_str3, Att, Element, ElementType, Shape, Value}; +use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Shape, Value}; pub fn ellipse() -> Ellipse { Ellipse(vec![]) @@ -8,45 +8,39 @@ pub fn ellipse() -> Ellipse { pub struct Ellipse(Vec); impl Ellipse { - fn cx>(mut self, cx: V) -> Self { + pub fn id>(&mut self, id: V) { + self.0.push(att("id", id)); + } + pub fn cx>(mut self, cx: V) -> Self { self.0.push(att("cx", cx)); self } - fn cy>(mut self, cy: V) -> Self { + pub fn cy>(mut self, cy: V) -> Self { self.0.push(att("cy", cy)); self } - fn rx>(mut self, rx: V) -> Self { + pub fn rx>(mut self, rx: V) -> Self { self.0.push(att("rx", rx)); self } - fn ry>(mut self, ry: V) -> Self { + pub fn ry>(mut self, ry: V) -> Self { self.0.push(att("ry", ry)); self } } impl Shape for Ellipse { - fn fill(mut self, value: V) -> Self - where - V: Into, - { + fn fill>(mut self, value: V) -> Self { self.0.push(att("fill", value)); self } - fn stroke(mut self, value: V) -> Self - where - V: Into, - { + fn stroke>(mut self, value: V) -> Self { self.0.push(att("stroke", value)); self } - fn transform(mut self, value: V) -> Self - where - V: Into, - { + fn transform>(mut self, value: V) -> Self { self.0.push(att("transform", value)); self } @@ -64,7 +58,7 @@ impl Element for Ellipse { fn to_string(&self) -> String { format!( r#""#, - self.0.iter().map(att_str3).collect::() + self.0.iter().map(att_to_string).collect::() ) } } diff --git a/src/render/svglib/foreign_object.rs b/src/render/svglib/foreign_object.rs index 922e06b..34e809f 100644 --- a/src/render/svglib/foreign_object.rs +++ b/src/render/svglib/foreign_object.rs @@ -1,5 +1,5 @@ use crate::render::svglib::div::Div; -use crate::render::svglib::{att, att_str3, Att, Element, ElementType, Value}; +use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Value}; pub fn foreign_object() -> ForeignObject { ForeignObject::new() @@ -65,7 +65,10 @@ impl Element for ForeignObject { fn to_string(&self) -> String { format!( r#"{}"#, - self.atts.iter().map(|a| att_str3(a)).collect::(), + self.atts + .iter() + .map(|a| att_to_string(a)) + .collect::(), self.child .as_ref() .map(|c| c.to_string()) diff --git a/src/render/svglib/group.rs b/src/render/svglib/group.rs index 498c084..ad1b16e 100644 --- a/src/render/svglib/group.rs +++ b/src/render/svglib/group.rs @@ -1,4 +1,4 @@ -use crate::render::svglib::{att, att_str3, Att, Element, ElementType, Value}; +use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Value}; pub fn group() -> Group { Group { @@ -17,17 +17,11 @@ impl Group { self.children.push(Box::new(child)); } - pub fn id(&mut self, id: V) - where - V: Into, - { + pub fn id>(&mut self, id: V) { self.atts.push(att("id", id)); } - pub fn transform(&mut self, value: V) - where - V: Into, - { + pub fn transform>(&mut self, value: V) { self.atts.push(att("transform", value)); } } @@ -42,7 +36,10 @@ impl Element for Group { } fn to_string(&self) -> String { - let mut svg = format!("", self.atts.iter().map(att_str3).collect::()); + let mut svg = format!( + "", + self.atts.iter().map(att_to_string).collect::() + ); for e in &self.children { svg.push_str(e.to_string().as_str()); diff --git a/src/render/svglib/image.rs b/src/render/svglib/image.rs index 6f9da1f..9fbf1ad 100644 --- a/src/render/svglib/image.rs +++ b/src/render/svglib/image.rs @@ -1,48 +1,42 @@ -use crate::render::svglib::{att, att_str3, Att, Element, ElementType, Value}; +use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Value}; pub fn image() -> Image { Image::new() } -struct Image { +pub struct Image { atts: Vec, } impl Image { - fn new() -> Self { + pub fn new() -> Self { Self { atts: vec![] } } - fn id(&mut self, id: V) - where - V: Into, - { + pub fn id>(&mut self, id: V) { self.atts.push(att("id", id)); } - fn transform(&mut self, value: V) - where - V: Into, - { + pub fn transform>(&mut self, value: V) { self.atts.push(att("transform", value)); } - fn x>(mut self, x: V) -> Self { + pub fn x>(mut self, x: V) -> Self { self.atts.push(att("x", x)); self } - fn y>(mut self, y: V) -> Self { + pub fn y>(mut self, y: V) -> Self { self.atts.push(att("y", y)); self } - fn width>(mut self, width: V) -> Self { + pub fn width>(mut self, width: V) -> Self { self.atts.push(att("width", width)); self } - fn height>(mut self, height: V) -> Self { + pub fn height>(mut self, height: V) -> Self { self.atts.push(att("height", height)); self } - fn href>(mut self, href: V) -> Self { + pub fn href>(mut self, href: V) -> Self { self.atts.push(att("href", href)); self } @@ -53,16 +47,16 @@ impl Element for Image { ElementType::Image } - fn atts(&self) -> &[Att] { - &self.atts - } - fn to_string(&self) -> String { format!( r#""#, - self.atts.iter().map(att_str3).collect::() + self.atts.iter().map(att_to_string).collect::() ) } + + fn atts(&self) -> &[Att] { + &self.atts + } } #[cfg(test)] diff --git a/src/render/svglib/line.rs b/src/render/svglib/line.rs index 1f5e030..3f0ddf1 100644 --- a/src/render/svglib/line.rs +++ b/src/render/svglib/line.rs @@ -1,4 +1,4 @@ -use crate::render::svglib::{att, att_str3, Att, Element, ElementType, Shape, Value}; +use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Shape, Value}; pub fn line() -> Line { Line(vec![]) @@ -7,11 +7,9 @@ pub fn line() -> Line { pub struct Line(Vec); impl Line { - fn id(&mut self, id: V) - where - V: Into, - { + pub fn id>(mut self, id: V) -> Self { self.0.push(att("id", id)); + self } pub fn x1>(mut self, x: V) -> Self { @@ -48,7 +46,7 @@ impl Element for Line { fn to_string(&self) -> String { format!( r#" "#, - self.0.iter().map(att_str3).collect::() + self.0.iter().map(att_to_string).collect::() ) } } diff --git a/src/render/svglib/link.rs b/src/render/svglib/link.rs index c12840d..58f3522 100644 --- a/src/render/svglib/link.rs +++ b/src/render/svglib/link.rs @@ -1,21 +1,16 @@ -use crate::render::svglib::{att, Att, Element, ElementType, Value}; +use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Value}; pub fn link(href: &str) -> Link { let mut atts = vec![]; atts.push(att("href", href)); - Link { atts } + Link(atts) } -struct Link { - atts: Vec, -} +pub struct Link(Vec); impl Link { - fn id(&mut self, id: V) - where - V: Into, - { - self.atts.push(att("id", id)); + pub fn id>(&mut self, id: V) { + self.0.push(att("id", id)); } } @@ -24,11 +19,14 @@ impl Element for Link { ElementType::Link } - fn atts(&self) -> &[Att] { - &self.atts + fn to_string(&self) -> String { + format!( + r#""#, + self.0.iter().map(att_to_string).collect::() + ) } - fn to_string(&self) -> String { - todo!() + fn atts(&self) -> &[Att] { + &self.0 } } diff --git a/src/render/svglib/mod.rs b/src/render/svglib/mod.rs index ab86762..7f025cf 100644 --- a/src/render/svglib/mod.rs +++ b/src/render/svglib/mod.rs @@ -11,7 +11,7 @@ pub mod rect; pub mod svg; pub mod text; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Value(String); impl From<&str> for Value { @@ -116,10 +116,8 @@ where Att::new(name, value.into()) } -fn att_str(att: &Option) -> String { - att.as_ref() - .map(|a| format!(r#" {}="{}""#, a.name, a.value.to_string())) - .unwrap_or("".to_string()) +fn att_to_string(att: &Att) -> String { + format!(r#" {}="{}""#, att.name, att.value.to_string()) } fn att_str2(att_name: &str, att_val: &Option) -> String { @@ -128,7 +126,3 @@ fn att_str2(att_name: &str, att_val: &Option) -> String { .map(|val| format!(r#" {}="{}""#, att_name, val)) .unwrap_or("".to_string()) } - -fn att_str3(att: &Att) -> String { - format!(r#" {}="{}""#, att.name, att.value.to_string()) -} diff --git a/src/render/svglib/path.rs b/src/render/svglib/path.rs index a2ab851..f5361c0 100644 --- a/src/render/svglib/path.rs +++ b/src/render/svglib/path.rs @@ -14,13 +14,13 @@ impl Element for Path { ElementType::Path } - fn atts(&self) -> &[Att] { - &self.atts - } - fn to_string(&self) -> String { todo!() } + + fn atts(&self) -> &[Att] { + &self.atts + } } impl Path { @@ -98,26 +98,17 @@ impl Path { } impl Shape for Path { - fn fill(mut self, value: V) -> Self - where - V: Into, - { + fn fill>(mut self, value: V) -> Self { self.atts.push(att("fill", value)); self } - fn stroke(mut self, value: V) -> Self - where - V: Into, - { + fn stroke>(mut self, value: V) -> Self { self.atts.push(att("stroke", value)); self } - fn transform(mut self, value: V) -> Self - where - V: Into, - { + fn transform>(mut self, value: V) -> Self { self.atts.push(att("transform", value)); self } diff --git a/src/render/svglib/rect.rs b/src/render/svglib/rect.rs index e9d0949..077214c 100644 --- a/src/render/svglib/rect.rs +++ b/src/render/svglib/rect.rs @@ -1,4 +1,4 @@ -use crate::render::svglib::{att, att_str3, Att, Element, ElementType, Value}; +use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Value}; pub fn rect() -> Rect { Rect::new() @@ -10,8 +10,11 @@ pub struct Rect { impl Rect { pub fn new() -> Self { - let mut atts = vec![]; - Self { atts } + Self { atts: vec![] } + } + pub fn id>(mut self, id: V) -> Self { + self.atts.push(att("id", id)); + self } pub fn rounded() -> Self { Self { atts: vec![] } @@ -71,7 +74,7 @@ impl Element for Rect { fn to_string(&self) -> String { format!( r#""#, - self.atts.iter().map(att_str3).collect::() + self.atts.iter().map(att_to_string).collect::() ) } } diff --git a/src/render/svglib/svg.rs b/src/render/svglib/svg.rs index 39ad8a7..e7744f7 100644 --- a/src/render/svglib/svg.rs +++ b/src/render/svglib/svg.rs @@ -1,4 +1,4 @@ -use crate::render::svglib::{att_str2, Att, Element, Value}; +use crate::render::svglib::{att, att_to_string, Att, Element, Value}; pub fn svg() -> Svg { Svg::new() @@ -7,11 +7,6 @@ pub fn svg() -> Svg { pub struct Svg { style: Option, elements: Vec>, - width: Option, - height: Option, - viewbox: Option, - preserveaspectratio: Option, - transform: Option, atts: Vec, } @@ -19,12 +14,7 @@ impl Svg { pub fn new() -> Self { Self { style: None, - elements: Vec::new(), - width: None, - height: None, - viewbox: None, - preserveaspectratio: None, - transform: None, + elements: vec![], atts: vec![], } } @@ -38,35 +28,32 @@ impl Svg { } pub fn width>(&mut self, width: V) { - self.width = Some(width.into().to_string()); + self.atts.push(att("width", width.into().to_string())); } pub fn height>(&mut self, height: V) { - self.height = Some(height.into().to_string()); + self.atts.push(att("height", height.into().to_string())); } - pub fn viewbox(&mut self, viewbox: &str) { - self.viewbox = Some(viewbox.to_string()); + pub fn viewbox>(&mut self, viewbox: V) { + self.atts.push(att("viewBox", viewbox)); } - pub fn preserveaspectratio(&mut self, preserveaspectratio: &str) { - self.preserveaspectratio = Some(preserveaspectratio.to_string()); + pub fn preserveaspectratio>(&mut self, preserveaspectratio: V) { + self.atts + .push(att("preserveaspectratio", preserveaspectratio)); } - fn transform>(&mut self, value: V) { - self.transform = Some(value.into().to_string()); + pub fn transform>(&mut self, transform: V) { + self.atts.push(att("transform", transform)); } pub fn to_string(&self) -> String { let mut svg = String::new(); svg.push_str( format!( - r#"{}"#, - att_str2("width", &self.width), - att_str2("height", &self.height), - att_str2("viewBox", &self.viewbox), - att_str2("preserveAspectRatio", &self.preserveaspectratio), - att_str2("transform", &self.transform), + r#"{}"#, + self.atts.iter().map(att_to_string).collect::(), self.style .as_ref() .map(|s| format!("", s.to_string())) diff --git a/src/render/svglib/text.rs b/src/render/svglib/text.rs index 6a0efa6..3eb2472 100644 --- a/src/render/svglib/text.rs +++ b/src/render/svglib/text.rs @@ -1,4 +1,4 @@ -use crate::render::svglib::{att, att_str3, Att, Element, ElementType, Value}; +use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Value}; pub fn text() -> Text { Text::new() @@ -11,9 +11,8 @@ pub struct Text { impl Text { pub fn new() -> Self { - let mut atts = vec![]; Self { - atts, + atts: vec![], text: "".to_string(), } } @@ -61,7 +60,7 @@ impl Text { self } - fn transform>(mut self, value: V) -> Self { + pub fn transform>(mut self, value: V) -> Self { self.atts.push(att("transform", value)); self } @@ -77,17 +76,22 @@ impl Element for Text { ElementType::Rect } - fn atts(&self) -> &[Att] { - &self.atts - } - fn to_string(&self) -> String { + let x: Vec<&Att> = self.atts.iter().filter(|a| a.name == "x").collect(); + let x: String = x[0].value.to_string(); format!( r#"{}"#, - self.atts.iter().map(att_str3).collect::(), - self.text, + self.atts.iter().map(att_to_string).collect::(), + self.text + .lines() + .map(|l| format!("{}", x, l)) + .collect::(), ) } + + fn atts(&self) -> &[Att] { + &self.atts + } } #[cfg(test)] diff --git a/src/render/svgrender2.rs b/src/render/svgrender2.rs index a2e460f..957d89b 100644 --- a/src/render/svgrender2.rs +++ b/src/render/svgrender2.rs @@ -1,10 +1,145 @@ +use crate::render::svglib::rect::rect; +use crate::render::svglib::svg::{svg, Svg}; +use crate::render::svglib::text::text; use crate::render::Renderer; -use crate::Vis; +use crate::Element::Node; +use crate::{Element, StyleNode, Vis}; -struct SvgRender; +pub struct SvgRender; impl Renderer for SvgRender { fn render(&self, vis: Vis) -> anyhow::Result> { - Ok(vec![]) + let mut svg = svg(); + + svg.width(100); + svg.height(100); + let style = include_str!("svg_node.css"); + svg.style(style); + let (w, h) = render_elements(&mut svg, &vis.structure, &vis.styles, 0, 0); + svg.viewbox(format!("0 0 {} {}", w, h)); + Ok(svg.to_string().into_bytes()) + } +} + +fn render_elements( + svg: &mut Svg, + elements: &Vec, + _styles: &Vec, + start_x: u32, + start_y: u32, +) -> (u32, u32) { + let padding = 15; + + let mut total_width = 0; + let mut total_height = 0; + let mut x = start_x; + let y = start_y; + + let mut width1 = 0; + let mut height1 = 0; + + println!("width1: {}, height1: {}", width1, height1); + + for element in elements { + println!("{}", element.to_string()); + if let Node(id, label, children) = element { + let (width, height) = if children.len() > 0 { + render_elements(svg, children, _styles, x + padding, y + padding) + } else { + if let Node(_, Some(label), _) = element { + let label_width = label.lines().map(|l| l.len()).max().unwrap_or(0) as u32; + let label_height = label.lines().count() as u32; + height1 = label_height * 15; + width1 = label_width * 5 + 5; + println!("label {}: {}x{}", label, label_width, label_height); + (width1, height1) + } else { + (0, 0) + } + }; + + total_width += width + padding; + total_height = u32::max(total_height, height + padding); + + svg.add( + rect() + .id(id) + .x(x + padding) + .y(y + padding) + .width(width) + .height(height) + .stroke("white") + .fill("none"), + ); + + svg.add( + text() + .x(x + padding + width / 2) + .y(start_y + padding) + .fill("white") + .text(label.as_ref().map(|l| l.as_str()).unwrap_or("")) + .attr("text-anchor", "middle") + .attr("stroke-width", "1") + .class("node"), + ); + x += width + padding; + } + } + + (total_width + padding, total_height + padding) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ContainerType, StyleNode, Vis}; + use std::fs::File; + use std::io::Write; + + #[test] + fn test_render_vis_with_grid_layout() { + // Create a mock StyleNode + let style_node = StyleNode { + id_ref: "node_1".to_string(), + containertype: ContainerType::NonGroup, + attributes: [("fill".to_string(), "white".to_string())] + .iter() + .cloned() + .collect(), + }; + + // Create mock Elements + let element1 = Node( + "node1".to_string(), + Some("Node 1\nlonger".to_string()), + vec![], // No child elements + ); + let element2 = Node( + "node2".to_string(), + Some("Node 2".to_string()), + vec![], // No child elements + ); + + let element3 = Node( + "node3".to_string(), + Some("Node 3\nis longer\nthan you are to me".to_string()), + vec![], // No child elements + ); + let root = Node( + "root".to_string(), + Some("root".to_string()), + vec![element1, element2, element3], + ); + + // Create Vis structure + let vis = Vis { + styles: vec![style_node], + structure: vec![root], + }; + + let svg_output = SvgRender {}.render(vis).unwrap(); + + let mut file = File::create("output_multiple_nodes.svg").expect("Unable to create file"); + file.write_all(&svg_output).expect("Unable to write data"); } }