e2e rendering 0.1 kinda works

This commit is contained in:
Shautvast 2025-01-22 17:25:32 +01:00
parent 89e03c8cb6
commit 32c9bc5b40
21 changed files with 356 additions and 172 deletions

View file

@ -1,5 +1,5 @@
<svg width="135" height="75" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><style>svg { <svg width="100" height="100" viewBox="0 0 15 0" xmlns="http://www.w3.org/2000/svg"><style>svg {
background-color: white; background-color: gray;
} }
.node { .node {
@ -12,4 +12,4 @@
font-size: 10px; font-size: 10px;
font-family: sans-serif; font-family: sans-serif;
} }
</style><rect x="5" y="5" width="60" height="60" fill="none" stroke="green" /><text x="35" y="35" text-anchor="middle">Node 1</text><rect x="65" y="5" width="60" height="60" fill="none" stroke="green" /><text x="95" y="35" text-anchor="middle">Node 1</text><rect x="0" y="0" width="135" height="75" fill="none" stroke="red" stroke-width="2" /></svg> </style></svg>

Before

Width:  |  Height:  |  Size: 684 B

After

Width:  |  Height:  |  Size: 346 B

View file

@ -1,6 +1,7 @@
pub mod parse; pub mod parse;
pub mod render; pub mod render;
use crate::Element::{Edge, Node};
use parse::tokens::TokenType; use parse::tokens::TokenType;
use std::collections::HashMap; use std::collections::HashMap;
@ -10,7 +11,7 @@ pub struct Vis {
pub styles: Vec<StyleNode>, pub styles: Vec<StyleNode>,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum Element { pub enum Element {
Node(String, Option<String>, Vec<Element>), Node(String, Option<String>, Vec<Element>),
Edge(String, String, TokenType, Option<String>), Edge(String, String, TokenType, Option<String>),
@ -28,6 +29,33 @@ impl Element {
) -> Element { ) -> Element {
Element::Edge(id, source, token, label) 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)] #[derive(Debug, Clone)]
@ -39,6 +67,6 @@ pub struct StyleNode {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ContainerType { pub enum ContainerType {
Node, NonGroup, // needs thinking about
Group, Group,
} }

View file

@ -1,15 +1,24 @@
use anyhow::anyhow; use anyhow::anyhow;
use std::env::args; use std::env::args;
use std::fs; 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<()> { fn main() -> anyhow::Result<()> {
let args: Vec<String> = args().collect(); let args: Vec<String> = args().collect();
if args.len() != 2 { if args.len() != 2 {
return Err(anyhow!("Usage: vis vis-file")); eprintln!("Usage: vis vis-file");
exit(-64);
} else { } else {
let vis_file = read_file(&args[1])?; let vis_file = read_file(&args[1])?;
let vis = vis::parse::parse_vis(vis_file.as_str())?; let vis = vis::parse::parse_vis(vis_file.as_str())?;
println!("{:?}", vis); 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(()) Ok(())

View file

@ -10,7 +10,7 @@ use std::collections::HashMap;
pub fn parse_vis(contents: &str) -> anyhow::Result<Vis> { pub fn parse_vis(contents: &str) -> anyhow::Result<Vis> {
let tokens = crate::parse::scanner::scan(contents)?; let tokens = crate::parse::scanner::scan(contents)?;
println!("{:?}", tokens); // println!("{:?}", tokens);
let mut parser = Parser::new(tokens); let mut parser = Parser::new(tokens);
Ok(Vis { Ok(Vis {
@ -93,8 +93,9 @@ impl Parser {
fn string(&mut self) -> anyhow::Result<String> { fn string(&mut self) -> anyhow::Result<String> {
let text = self.peek().clone(); let text = self.peek().clone();
let text = text.lexeme[1..text.lexeme.len() - 1].to_owned();
self.consume(Str, "Expected quoted string")?; self.consume(Str, "Expected quoted string")?;
Ok(text.lexeme.to_owned()) Ok(text)
} }
fn text(&mut self) -> anyhow::Result<String> { fn text(&mut self) -> anyhow::Result<String> {
@ -164,10 +165,10 @@ impl Parser {
if self.match_token(Group) { if self.match_token(Group) {
ContainerType::Group ContainerType::Group
} else { } else {
ContainerType::Node ContainerType::NonGroup
} }
} else { } else {
ContainerType::Node ContainerType::NonGroup
}) })
} }
@ -231,3 +232,54 @@ impl Parser {
&self.tokens[self.current] &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");
}
}
}

View file

@ -4,7 +4,7 @@ pub mod html;
mod rendering_svg_elements; mod rendering_svg_elements;
mod svg_renderer; mod svg_renderer;
pub mod svglib; pub mod svglib;
mod svgrender2; pub mod svgrender2;
/// trait for turning the object model into a byte representation /// trait for turning the object model into a byte representation
pub trait Renderer { pub trait Renderer {

View file

@ -1,7 +1,7 @@
use crate::render::svglib::rect::rect; use crate::render::svglib::rect::rect;
use crate::render::svglib::svg::{svg, Svg}; use crate::render::svglib::svg::{svg, Svg};
use crate::render::svglib::text::text; use crate::render::svglib::text::text;
use crate::{Element, StyleNode, Vis}; use crate::{Element, Vis};
pub fn render_vis_with_grid_layout( pub fn render_vis_with_grid_layout(
vis: &Vis, vis: &Vis,
@ -54,7 +54,7 @@ fn layout_box(
let child_y = current_y + row as f32 * (grid_cell_size + spacing); let child_y = current_y + row as f32 * (grid_cell_size + spacing);
match element { match element {
Element::Node(id, label, children) => { Element::Node(_, label, children) => {
let (_, _) = layout_box( let (_, _) = layout_box(
svg, svg,
children, children,
@ -128,7 +128,7 @@ mod tests {
// Create a mock StyleNode // Create a mock StyleNode
let style_node = StyleNode { let style_node = StyleNode {
id_ref: "node_1".to_string(), id_ref: "node_1".to_string(),
containertype: ContainerType::Node, containertype: ContainerType::NonGroup,
attributes: [("fill".to_string(), "white".to_string())] attributes: [("fill".to_string(), "white".to_string())]
.iter() .iter()
.cloned() .cloned()

View file

@ -1,5 +1,5 @@
svg { svg {
background-color: white; background-color: gray;
} }
.node { .node {

View file

@ -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 { pub fn circle() -> Circle {
Circle::new() Circle::new()
@ -7,23 +7,23 @@ pub fn circle() -> Circle {
pub struct Circle(Vec<Att>); pub struct Circle(Vec<Att>);
impl Circle { impl Circle {
fn new() -> Self { pub fn new() -> Self {
Self(vec![]) Self(vec![])
} }
fn id<V: Into<Value>>(mut self, id: V) -> Self { pub fn id<V: Into<Value>>(mut self, id: V) -> Self {
self.0.push(att("id", id)); self.0.push(att("id", id));
self self
} }
fn cx<V: Into<Value>>(mut self, cx: V) -> Self { pub fn cx<V: Into<Value>>(mut self, cx: V) -> Self {
self.0.push(att("cx", cx)); self.0.push(att("cx", cx));
self self
} }
fn cy<V: Into<Value>>(mut self, cy: V) -> Self { pub fn cy<V: Into<Value>>(mut self, cy: V) -> Self {
self.0.push(att("cy", cy)); self.0.push(att("cy", cy));
self self
} }
fn r<V: Into<Value>>(mut self, r: V) -> Self { pub fn r<V: Into<Value>>(mut self, r: V) -> Self {
self.0.push(att("r", r)); self.0.push(att("r", r));
self self
} }
@ -51,16 +51,16 @@ impl Element for Circle {
ElementType::Circle ElementType::Circle
} }
fn atts(&self) -> &[Att] {
&self.0
}
fn to_string(&self) -> String { fn to_string(&self) -> String {
format!( format!(
r#"<circle{} />"#, r#"<circle{} />"#,
self.0.iter().map(att_str3).collect::<String>() self.0.iter().map(att_to_string).collect::<String>()
) )
} }
fn atts(&self) -> &[Att] {
&self.0
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -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 { pub fn div() -> Div {
Div::new() Div::new()
@ -17,10 +17,7 @@ impl Div {
} }
} }
pub fn id<V>(&mut self, id: V) pub fn id<V: Into<Value>>(&mut self, id: V) {
where
V: Into<Value>,
{
self.atts.push(att("id", id)); self.atts.push(att("id", id));
} }
@ -32,7 +29,7 @@ impl Div {
self self
} }
pub fn innerHTML<V: Into<String>>(mut self, html: V) -> Self { pub fn inner_html<V: Into<String>>(mut self, html: V) -> Self {
self.child = html.into(); self.child = html.into();
self self
} }
@ -40,7 +37,7 @@ impl Div {
pub fn to_string(&self) -> String { pub fn to_string(&self) -> String {
format!( format!(
r#"<div xmlns="http://www.w3.org/1999/xhtml"{}>{}</div>"#, r#"<div xmlns="http://www.w3.org/1999/xhtml"{}>{}</div>"#,
self.atts.iter().map(att_str3).collect::<String>(), self.atts.iter().map(att_to_string).collect::<String>(),
self.child self.child
) )
} }

View file

@ -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 { pub fn ellipse() -> Ellipse {
Ellipse(vec![]) Ellipse(vec![])
@ -8,45 +8,39 @@ pub fn ellipse() -> Ellipse {
pub struct Ellipse(Vec<Att>); pub struct Ellipse(Vec<Att>);
impl Ellipse { impl Ellipse {
fn cx<V: Into<Value>>(mut self, cx: V) -> Self { pub fn id<V: Into<Value>>(&mut self, id: V) {
self.0.push(att("id", id));
}
pub fn cx<V: Into<Value>>(mut self, cx: V) -> Self {
self.0.push(att("cx", cx)); self.0.push(att("cx", cx));
self self
} }
fn cy<V: Into<Value>>(mut self, cy: V) -> Self { pub fn cy<V: Into<Value>>(mut self, cy: V) -> Self {
self.0.push(att("cy", cy)); self.0.push(att("cy", cy));
self self
} }
fn rx<V: Into<Value>>(mut self, rx: V) -> Self { pub fn rx<V: Into<Value>>(mut self, rx: V) -> Self {
self.0.push(att("rx", rx)); self.0.push(att("rx", rx));
self self
} }
fn ry<V: Into<Value>>(mut self, ry: V) -> Self { pub fn ry<V: Into<Value>>(mut self, ry: V) -> Self {
self.0.push(att("ry", ry)); self.0.push(att("ry", ry));
self self
} }
} }
impl Shape for Ellipse { impl Shape for Ellipse {
fn fill<V>(mut self, value: V) -> Self fn fill<V: Into<Value>>(mut self, value: V) -> Self {
where
V: Into<Value>,
{
self.0.push(att("fill", value)); self.0.push(att("fill", value));
self self
} }
fn stroke<V>(mut self, value: V) -> Self fn stroke<V: Into<Value>>(mut self, value: V) -> Self {
where
V: Into<Value>,
{
self.0.push(att("stroke", value)); self.0.push(att("stroke", value));
self self
} }
fn transform<V>(mut self, value: V) -> Self fn transform<V: Into<Value>>(mut self, value: V) -> Self {
where
V: Into<Value>,
{
self.0.push(att("transform", value)); self.0.push(att("transform", value));
self self
} }
@ -64,7 +58,7 @@ impl Element for Ellipse {
fn to_string(&self) -> String { fn to_string(&self) -> String {
format!( format!(
r#"<ellipse{} />"#, r#"<ellipse{} />"#,
self.0.iter().map(att_str3).collect::<String>() self.0.iter().map(att_to_string).collect::<String>()
) )
} }
} }

View file

@ -1,5 +1,5 @@
use crate::render::svglib::div::Div; 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 { pub fn foreign_object() -> ForeignObject {
ForeignObject::new() ForeignObject::new()
@ -65,7 +65,10 @@ impl Element for ForeignObject {
fn to_string(&self) -> String { fn to_string(&self) -> String {
format!( format!(
r#"<foreignObject{}>{}</foreignObject>"#, r#"<foreignObject{}>{}</foreignObject>"#,
self.atts.iter().map(|a| att_str3(a)).collect::<String>(), self.atts
.iter()
.map(|a| att_to_string(a))
.collect::<String>(),
self.child self.child
.as_ref() .as_ref()
.map(|c| c.to_string()) .map(|c| c.to_string())

View file

@ -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 { pub fn group() -> Group {
Group { Group {
@ -17,17 +17,11 @@ impl Group {
self.children.push(Box::new(child)); self.children.push(Box::new(child));
} }
pub fn id<V>(&mut self, id: V) pub fn id<V: Into<Value>>(&mut self, id: V) {
where
V: Into<Value>,
{
self.atts.push(att("id", id)); self.atts.push(att("id", id));
} }
pub fn transform<V>(&mut self, value: V) pub fn transform<V: Into<Value>>(&mut self, value: V) {
where
V: Into<Value>,
{
self.atts.push(att("transform", value)); self.atts.push(att("transform", value));
} }
} }
@ -42,7 +36,10 @@ impl Element for Group {
} }
fn to_string(&self) -> String { fn to_string(&self) -> String {
let mut svg = format!("<g{}>", self.atts.iter().map(att_str3).collect::<String>()); let mut svg = format!(
"<g{}>",
self.atts.iter().map(att_to_string).collect::<String>()
);
for e in &self.children { for e in &self.children {
svg.push_str(e.to_string().as_str()); svg.push_str(e.to_string().as_str());

View file

@ -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 { pub fn image() -> Image {
Image::new() Image::new()
} }
struct Image { pub struct Image {
atts: Vec<Att>, atts: Vec<Att>,
} }
impl Image { impl Image {
fn new() -> Self { pub fn new() -> Self {
Self { atts: vec![] } Self { atts: vec![] }
} }
fn id<V>(&mut self, id: V) pub fn id<V: Into<Value>>(&mut self, id: V) {
where
V: Into<Value>,
{
self.atts.push(att("id", id)); self.atts.push(att("id", id));
} }
fn transform<V>(&mut self, value: V) pub fn transform<V: Into<Value>>(&mut self, value: V) {
where
V: Into<Value>,
{
self.atts.push(att("transform", value)); self.atts.push(att("transform", value));
} }
fn x<V: Into<Value>>(mut self, x: V) -> Self { pub fn x<V: Into<Value>>(mut self, x: V) -> Self {
self.atts.push(att("x", x)); self.atts.push(att("x", x));
self self
} }
fn y<V: Into<Value>>(mut self, y: V) -> Self { pub fn y<V: Into<Value>>(mut self, y: V) -> Self {
self.atts.push(att("y", y)); self.atts.push(att("y", y));
self self
} }
fn width<V: Into<Value>>(mut self, width: V) -> Self { pub fn width<V: Into<Value>>(mut self, width: V) -> Self {
self.atts.push(att("width", width)); self.atts.push(att("width", width));
self self
} }
fn height<V: Into<Value>>(mut self, height: V) -> Self { pub fn height<V: Into<Value>>(mut self, height: V) -> Self {
self.atts.push(att("height", height)); self.atts.push(att("height", height));
self self
} }
fn href<V: Into<Value>>(mut self, href: V) -> Self { pub fn href<V: Into<Value>>(mut self, href: V) -> Self {
self.atts.push(att("href", href)); self.atts.push(att("href", href));
self self
} }
@ -53,16 +47,16 @@ impl Element for Image {
ElementType::Image ElementType::Image
} }
fn atts(&self) -> &[Att] {
&self.atts
}
fn to_string(&self) -> String { fn to_string(&self) -> String {
format!( format!(
r#"<image{} />"#, r#"<image{} />"#,
self.atts.iter().map(att_str3).collect::<String>() self.atts.iter().map(att_to_string).collect::<String>()
) )
} }
fn atts(&self) -> &[Att] {
&self.atts
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -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 { pub fn line() -> Line {
Line(vec![]) Line(vec![])
@ -7,11 +7,9 @@ pub fn line() -> Line {
pub struct Line(Vec<Att>); pub struct Line(Vec<Att>);
impl Line { impl Line {
fn id<V>(&mut self, id: V) pub fn id<V: Into<Value>>(mut self, id: V) -> Self {
where
V: Into<Value>,
{
self.0.push(att("id", id)); self.0.push(att("id", id));
self
} }
pub fn x1<V: Into<Value>>(mut self, x: V) -> Self { pub fn x1<V: Into<Value>>(mut self, x: V) -> Self {
@ -48,7 +46,7 @@ impl Element for Line {
fn to_string(&self) -> String { fn to_string(&self) -> String {
format!( format!(
r#" <line{} />"#, r#" <line{} />"#,
self.0.iter().map(att_str3).collect::<String>() self.0.iter().map(att_to_string).collect::<String>()
) )
} }
} }

View file

@ -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 { pub fn link(href: &str) -> Link {
let mut atts = vec![]; let mut atts = vec![];
atts.push(att("href", href)); atts.push(att("href", href));
Link { atts } Link(atts)
} }
struct Link { pub struct Link(Vec<Att>);
atts: Vec<Att>,
}
impl Link { impl Link {
fn id<V>(&mut self, id: V) pub fn id<V: Into<Value>>(&mut self, id: V) {
where self.0.push(att("id", id));
V: Into<Value>,
{
self.atts.push(att("id", id));
} }
} }
@ -24,11 +19,14 @@ impl Element for Link {
ElementType::Link ElementType::Link
} }
fn atts(&self) -> &[Att] { fn to_string(&self) -> String {
&self.atts format!(
r#"<link{} />"#,
self.0.iter().map(att_to_string).collect::<String>()
)
} }
fn to_string(&self) -> String { fn atts(&self) -> &[Att] {
todo!() &self.0
} }
} }

View file

@ -11,7 +11,7 @@ pub mod rect;
pub mod svg; pub mod svg;
pub mod text; pub mod text;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Value(String); pub struct Value(String);
impl From<&str> for Value { impl From<&str> for Value {
@ -116,10 +116,8 @@ where
Att::new(name, value.into()) Att::new(name, value.into())
} }
fn att_str(att: &Option<Att>) -> String { fn att_to_string(att: &Att) -> String {
att.as_ref() format!(r#" {}="{}""#, att.name, att.value.to_string())
.map(|a| format!(r#" {}="{}""#, a.name, a.value.to_string()))
.unwrap_or("".to_string())
} }
fn att_str2(att_name: &str, att_val: &Option<String>) -> String { fn att_str2(att_name: &str, att_val: &Option<String>) -> String {
@ -128,7 +126,3 @@ fn att_str2(att_name: &str, att_val: &Option<String>) -> String {
.map(|val| format!(r#" {}="{}""#, att_name, val)) .map(|val| format!(r#" {}="{}""#, att_name, val))
.unwrap_or("".to_string()) .unwrap_or("".to_string())
} }
fn att_str3(att: &Att) -> String {
format!(r#" {}="{}""#, att.name, att.value.to_string())
}

View file

@ -14,13 +14,13 @@ impl Element for Path {
ElementType::Path ElementType::Path
} }
fn atts(&self) -> &[Att] {
&self.atts
}
fn to_string(&self) -> String { fn to_string(&self) -> String {
todo!() todo!()
} }
fn atts(&self) -> &[Att] {
&self.atts
}
} }
impl Path { impl Path {
@ -98,26 +98,17 @@ impl Path {
} }
impl Shape for Path { impl Shape for Path {
fn fill<V>(mut self, value: V) -> Self fn fill<V: Into<Value>>(mut self, value: V) -> Self {
where
V: Into<Value>,
{
self.atts.push(att("fill", value)); self.atts.push(att("fill", value));
self self
} }
fn stroke<V>(mut self, value: V) -> Self fn stroke<V: Into<Value>>(mut self, value: V) -> Self {
where
V: Into<Value>,
{
self.atts.push(att("stroke", value)); self.atts.push(att("stroke", value));
self self
} }
fn transform<V>(mut self, value: V) -> Self fn transform<V: Into<Value>>(mut self, value: V) -> Self {
where
V: Into<Value>,
{
self.atts.push(att("transform", value)); self.atts.push(att("transform", value));
self self
} }

View file

@ -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 { pub fn rect() -> Rect {
Rect::new() Rect::new()
@ -10,8 +10,11 @@ pub struct Rect {
impl Rect { impl Rect {
pub fn new() -> Self { pub fn new() -> Self {
let mut atts = vec![]; Self { atts: vec![] }
Self { atts } }
pub fn id<V: Into<Value>>(mut self, id: V) -> Self {
self.atts.push(att("id", id));
self
} }
pub fn rounded() -> Self { pub fn rounded() -> Self {
Self { atts: vec![] } Self { atts: vec![] }
@ -71,7 +74,7 @@ impl Element for Rect {
fn to_string(&self) -> String { fn to_string(&self) -> String {
format!( format!(
r#"<rect{} />"#, r#"<rect{} />"#,
self.atts.iter().map(att_str3).collect::<String>() self.atts.iter().map(att_to_string).collect::<String>()
) )
} }
} }

View file

@ -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 { pub fn svg() -> Svg {
Svg::new() Svg::new()
@ -7,11 +7,6 @@ pub fn svg() -> Svg {
pub struct Svg { pub struct Svg {
style: Option<String>, style: Option<String>,
elements: Vec<Box<dyn Element>>, elements: Vec<Box<dyn Element>>,
width: Option<String>,
height: Option<String>,
viewbox: Option<String>,
preserveaspectratio: Option<String>,
transform: Option<String>,
atts: Vec<Att>, atts: Vec<Att>,
} }
@ -19,12 +14,7 @@ impl Svg {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
style: None, style: None,
elements: Vec::new(), elements: vec![],
width: None,
height: None,
viewbox: None,
preserveaspectratio: None,
transform: None,
atts: vec![], atts: vec![],
} }
} }
@ -38,35 +28,32 @@ impl Svg {
} }
pub fn width<V: Into<Value>>(&mut self, width: V) { pub fn width<V: Into<Value>>(&mut self, width: V) {
self.width = Some(width.into().to_string()); self.atts.push(att("width", width.into().to_string()));
} }
pub fn height<V: Into<Value>>(&mut self, height: V) { pub fn height<V: Into<Value>>(&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) { pub fn viewbox<V: Into<Value>>(&mut self, viewbox: V) {
self.viewbox = Some(viewbox.to_string()); self.atts.push(att("viewBox", viewbox));
} }
pub fn preserveaspectratio(&mut self, preserveaspectratio: &str) { pub fn preserveaspectratio<V: Into<Value>>(&mut self, preserveaspectratio: V) {
self.preserveaspectratio = Some(preserveaspectratio.to_string()); self.atts
.push(att("preserveaspectratio", preserveaspectratio));
} }
fn transform<V: Into<Value>>(&mut self, value: V) { pub fn transform<V: Into<Value>>(&mut self, transform: V) {
self.transform = Some(value.into().to_string()); self.atts.push(att("transform", transform));
} }
pub fn to_string(&self) -> String { pub fn to_string(&self) -> String {
let mut svg = String::new(); let mut svg = String::new();
svg.push_str( svg.push_str(
format!( format!(
r#"<svg{}{}{}{}{} xmlns="http://www.w3.org/2000/svg">{}"#, r#"<svg xmlns="http://www.w3.org/2000/svg" {}>{}"#,
att_str2("width", &self.width), self.atts.iter().map(att_to_string).collect::<String>(),
att_str2("height", &self.height),
att_str2("viewBox", &self.viewbox),
att_str2("preserveAspectRatio", &self.preserveaspectratio),
att_str2("transform", &self.transform),
self.style self.style
.as_ref() .as_ref()
.map(|s| format!("<style>{}</style>", s.to_string())) .map(|s| format!("<style>{}</style>", s.to_string()))

View file

@ -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 { pub fn text() -> Text {
Text::new() Text::new()
@ -11,9 +11,8 @@ pub struct Text {
impl Text { impl Text {
pub fn new() -> Self { pub fn new() -> Self {
let mut atts = vec![];
Self { Self {
atts, atts: vec![],
text: "".to_string(), text: "".to_string(),
} }
} }
@ -61,7 +60,7 @@ impl Text {
self self
} }
fn transform<V: Into<Value>>(mut self, value: V) -> Self { pub fn transform<V: Into<Value>>(mut self, value: V) -> Self {
self.atts.push(att("transform", value)); self.atts.push(att("transform", value));
self self
} }
@ -77,17 +76,22 @@ impl Element for Text {
ElementType::Rect ElementType::Rect
} }
fn atts(&self) -> &[Att] {
&self.atts
}
fn to_string(&self) -> String { 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!( format!(
r#"<text{}>{}</text>"#, r#"<text{}>{}</text>"#,
self.atts.iter().map(att_str3).collect::<String>(), self.atts.iter().map(att_to_string).collect::<String>(),
self.text, self.text
.lines()
.map(|l| format!("<tspan x=\"{}\" dy=\"10\">{}</tspan>", x, l))
.collect::<String>(),
) )
} }
fn atts(&self) -> &[Att] {
&self.atts
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -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::render::Renderer;
use crate::Vis; use crate::Element::Node;
use crate::{Element, StyleNode, Vis};
struct SvgRender; pub struct SvgRender;
impl Renderer for SvgRender { impl Renderer for SvgRender {
fn render(&self, vis: Vis) -> anyhow::Result<Vec<u8>> { fn render(&self, vis: Vis) -> anyhow::Result<Vec<u8>> {
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<Element>,
_styles: &Vec<StyleNode>,
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");
} }
} }