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 {
background-color: white;
<svg width="100" height="100" viewBox="0 0 15 0" xmlns="http://www.w3.org/2000/svg"><style>svg {
background-color: gray;
}
.node {
@ -12,4 +12,4 @@
font-size: 10px;
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 render;
use crate::Element::{Edge, Node};
use parse::tokens::TokenType;
use std::collections::HashMap;
@ -10,7 +11,7 @@ pub struct Vis {
pub styles: Vec<StyleNode>,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum Element {
Node(String, Option<String>, Vec<Element>),
Edge(String, String, TokenType, Option<String>),
@ -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,
}

View file

@ -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<String> = 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(())

View file

@ -10,7 +10,7 @@ use std::collections::HashMap;
pub fn parse_vis(contents: &str) -> anyhow::Result<Vis> {
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<String> {
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<String> {
@ -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");
}
}
}

View file

@ -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 {

View file

@ -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()

View file

@ -1,5 +1,5 @@
svg {
background-color: white;
background-color: gray;
}
.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 {
Circle::new()
@ -7,23 +7,23 @@ pub fn circle() -> Circle {
pub struct Circle(Vec<Att>);
impl Circle {
fn new() -> Self {
pub fn new() -> Self {
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
}
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
}
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
}
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
}
@ -51,16 +51,16 @@ impl Element for Circle {
ElementType::Circle
}
fn atts(&self) -> &[Att] {
&self.0
}
fn to_string(&self) -> String {
format!(
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)]

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 {
Div::new()
@ -17,10 +17,7 @@ impl Div {
}
}
pub fn id<V>(&mut self, id: V)
where
V: Into<Value>,
{
pub fn id<V: Into<Value>>(&mut self, id: V) {
self.atts.push(att("id", id));
}
@ -32,7 +29,7 @@ impl Div {
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
}
@ -40,7 +37,7 @@ impl Div {
pub fn to_string(&self) -> String {
format!(
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
)
}

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 {
Ellipse(vec![])
@ -8,45 +8,39 @@ pub fn ellipse() -> Ellipse {
pub struct Ellipse(Vec<Att>);
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
}
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
}
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
}
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
}
}
impl Shape for Ellipse {
fn fill<V>(mut self, value: V) -> Self
where
V: Into<Value>,
{
fn fill<V: Into<Value>>(mut self, value: V) -> Self {
self.0.push(att("fill", value));
self
}
fn stroke<V>(mut self, value: V) -> Self
where
V: Into<Value>,
{
fn stroke<V: Into<Value>>(mut self, value: V) -> Self {
self.0.push(att("stroke", value));
self
}
fn transform<V>(mut self, value: V) -> Self
where
V: Into<Value>,
{
fn transform<V: Into<Value>>(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#"<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::{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#"<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
.as_ref()
.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 {
Group {
@ -17,17 +17,11 @@ impl Group {
self.children.push(Box::new(child));
}
pub fn id<V>(&mut self, id: V)
where
V: Into<Value>,
{
pub fn id<V: Into<Value>>(&mut self, id: V) {
self.atts.push(att("id", id));
}
pub fn transform<V>(&mut self, value: V)
where
V: Into<Value>,
{
pub fn transform<V: Into<Value>>(&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!("<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 {
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 {
Image::new()
}
struct Image {
pub struct Image {
atts: Vec<Att>,
}
impl Image {
fn new() -> Self {
pub fn new() -> Self {
Self { atts: vec![] }
}
fn id<V>(&mut self, id: V)
where
V: Into<Value>,
{
pub fn id<V: Into<Value>>(&mut self, id: V) {
self.atts.push(att("id", id));
}
fn transform<V>(&mut self, value: V)
where
V: Into<Value>,
{
pub fn transform<V: Into<Value>>(&mut self, value: V) {
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
}
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
}
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
}
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
}
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
}
@ -53,16 +47,16 @@ impl Element for Image {
ElementType::Image
}
fn atts(&self) -> &[Att] {
&self.atts
}
fn to_string(&self) -> String {
format!(
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)]

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

View file

@ -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<Att>) -> 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>) -> String {
@ -128,7 +126,3 @@ fn att_str2(att_name: &str, att_val: &Option<String>) -> 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())
}

View file

@ -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<V>(mut self, value: V) -> Self
where
V: Into<Value>,
{
fn fill<V: Into<Value>>(mut self, value: V) -> Self {
self.atts.push(att("fill", value));
self
}
fn stroke<V>(mut self, value: V) -> Self
where
V: Into<Value>,
{
fn stroke<V: Into<Value>>(mut self, value: V) -> Self {
self.atts.push(att("stroke", value));
self
}
fn transform<V>(mut self, value: V) -> Self
where
V: Into<Value>,
{
fn transform<V: Into<Value>>(mut self, value: V) -> Self {
self.atts.push(att("transform", value));
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 {
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<V: Into<Value>>(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#"<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 {
Svg::new()
@ -7,11 +7,6 @@ pub fn svg() -> Svg {
pub struct Svg {
style: Option<String>,
elements: Vec<Box<dyn Element>>,
width: Option<String>,
height: Option<String>,
viewbox: Option<String>,
preserveaspectratio: Option<String>,
transform: Option<String>,
atts: Vec<Att>,
}
@ -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<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) {
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<V: Into<Value>>(&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<V: Into<Value>>(&mut self, preserveaspectratio: V) {
self.atts
.push(att("preserveaspectratio", preserveaspectratio));
}
fn transform<V: Into<Value>>(&mut self, value: V) {
self.transform = Some(value.into().to_string());
pub fn transform<V: Into<Value>>(&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#"<svg{}{}{}{}{} xmlns="http://www.w3.org/2000/svg">{}"#,
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#"<svg xmlns="http://www.w3.org/2000/svg" {}>{}"#,
self.atts.iter().map(att_to_string).collect::<String>(),
self.style
.as_ref()
.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 {
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<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
}
@ -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#"<text{}>{}</text>"#,
self.atts.iter().map(att_str3).collect::<String>(),
self.text,
self.atts.iter().map(att_to_string).collect::<String>(),
self.text
.lines()
.map(|l| format!("<tspan x=\"{}\" dy=\"10\">{}</tspan>", x, l))
.collect::<String>(),
)
}
fn atts(&self) -> &[Att] {
&self.atts
}
}
#[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::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<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");
}
}