e2e rendering 0.1 kinda works
This commit is contained in:
parent
89e03c8cb6
commit
32c9bc5b40
21 changed files with 356 additions and 172 deletions
|
|
@ -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 |
32
src/lib.rs
32
src/lib.rs
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
src/main.rs
11
src/main.rs
|
|
@ -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(())
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
svg {
|
svg {
|
||||||
background-color: white;
|
background-color: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node {
|
.node {
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
|
|
|
||||||
|
|
@ -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>()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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())
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()))
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue