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 {
|
||||
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 |
32
src/lib.rs
32
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<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,
|
||||
}
|
||||
|
|
|
|||
11
src/main.rs
11
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<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(())
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
svg {
|
||||
background-color: white;
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
.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 {
|
||||
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)]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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>()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()))
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue