nadavrot / layout Goto Github PK
View Code? Open in Web Editor NEWLayout is a rust library and a tool that renders Graphviz dot files.
License: MIT License
Layout is a rust library and a tool that renders Graphviz dot files.
License: MIT License
I’d love to be able to integrate graphs from this crate into my UI through egui :)
Hello! Ive been using your library to create interactive graphs via wasm. Its been a great experience so far!
Ive bodged your svg writer a bit to support foreign objects however its a horrible hack.
It would be really useful if there was an element kind like Box(impl ForeignObejct) where foreign object trait dictates how it would be rendered.
Another thing that would be really nice is to generate ids for vertices and edges. Currently i do that via requiring a queue of edges in the svg writer constructor , dequeue it at each draw_arrow/line call to generate edge ids and
creating an empty stack, pushing vertex names into the stack and generating an id string based on the vertex counts replacing it at the finalize call from the stack. Although it works mechanism is really flimsy. it would be really nice if RenderBackend trait supported that natively.
Here is what ive done so far. You can click on edges to remove them and click two vertices in row to bind them .Dont judge the code ive been messing with it for couple of days purely explotarily
This input
digraph G {
node [width=0.13,height=0.13,fontsize=10,shape=point];
edge [fontsize=18,penwidth=1.3,arrowsize=1.3,fontname=Arial];
rankdir=LR;
labeljust=l;
margin=0;
10 -> 2 [minlen=1,color=black,label="0 (len=31,supp=56)"];
11 -> 10 [minlen=2,color=red,label="1 (len=402,supp=496)"];
10 -> 2 [minlen=1,color=black,label="2 (len=31,supp=20)"];
0 -> 8 [minlen=2,color=red,label="3 (len=154,supp=113)"];
5 -> 0 [minlen=1,color=black,label="4 (len=20,supp=27)"];
5 -> 0 [minlen=1,color=black,label="5 (len=20,supp=12)"];
2 -> 5 [minlen=1,color=black,label="6 (len=19,supp=51)"];
}
yields this error
Boxes must not intersect
0: ◼ layout-rs-0.1.1/src/topo/placer/verifier.rs:22
1: layout::topo::placer::verifier
◼ at layout-rs-0.1.1/src/topo/placer/verifier.rs:22:13
layout::topo::placer::verifier
◼ at layout-rs-0.1.1/src/topo/placer/verifier.rs:5:5
2: layout::topo::placer::place::Placer
◼ at layout-rs-0.1.1/src/topo/placer/place.rs:60:9
3: layout::topo::layout::VisualGraph
◼ at layout-rs-0.1.1/src/topo/layout.rs:146:9
Hey, thank you for writing the crate. I am currently working with an svg generation library that makes use of html like labels (https://graphviz.org/doc/info/shapes.html#html) which this crate does not support just yet.
Would you be interested in accepting a PR for support?
I found your library and it looks it has a great support for rendering orthogonal edge connections and nodes from dot files however I couldn’t find in Rust ecosystem any library thigh might support erd diagrams rendering from ie. dot format. From my research I found only GraphViz is able to render html tables embedded in node labels or edges with from and to arrow with cardianity. Have you considered to extend your library in such direction in the future?
The example dot file:
graph {
graph [
label=<<FONT POINT-SIZE="20">nfldb Entity-Relationship diagram (condensed)</FONT>>,
labeljust=l,
labelloc=t,
rankdir=LR,
splines=spline,
];
node [
label="\N",
shape=plaintext,
];
edge [
color=gray50,
minlen=2,
style=dashed,
];
"player" [
label=<
<FONT FACE="Helvetica">
<TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#d0e0d0">
<TR><TD><B><FONT POINT-SIZE="16">player</FONT></B></TD></TR>
<TR><TD ALIGN="LEFT"><U>player_id</U> [varchar, not null]</TD></TR>
<TR><TD ALIGN="LEFT">full_name [varchar, null]</TD></TR>
<TR><TD ALIGN="LEFT">team [varchar, not null]</TD></TR>
<TR><TD ALIGN="LEFT">position [player_pos, not null]</TD></TR>
<TR><TD ALIGN="LEFT">status [player_status, not null]</TD></TR>
</TABLE>
</FONT>
>];
"team" [
label=<
<FONT FACE="Helvetica">
<TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#d0e0d0">
<TR><TD><B><FONT POINT-SIZE="16">team</FONT></B></TD></TR>
<TR><TD ALIGN="LEFT"><U>team_id</U> [varchar, not null]</TD></TR>
<TR><TD ALIGN="LEFT">city [varchar, not null]</TD></TR>
<TR><TD ALIGN="LEFT">name [varchar, not null]</TD></TR>
</TABLE>
</FONT>
>];
"game" [
label=<
<FONT FACE="Helvetica">
<TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#ececfc">
<TR><TD><B><FONT POINT-SIZE="16">game</FONT></B></TD></TR>
<TR><TD ALIGN="LEFT"><U>gsis_id</U> [gameid, not null]</TD></TR>
<TR><TD ALIGN="LEFT">start_time [utctime, not null]</TD></TR>
<TR><TD ALIGN="LEFT">week [usmallint, not null]</TD></TR>
<TR><TD ALIGN="LEFT">season_year [usmallint, not null]</TD></TR>
<TR><TD ALIGN="LEFT">season_type [season_phase, not null]</TD></TR>
<TR><TD ALIGN="LEFT">finished [boolean, not null]</TD></TR>
<TR><TD ALIGN="LEFT">home_team [varchar, not null]</TD></TR>
<TR><TD ALIGN="LEFT">home_score [usmallint, not null]</TD></TR>
<TR><TD ALIGN="LEFT">away_team [varchar, not null]</TD></TR>
<TR><TD ALIGN="LEFT">away_score [usmallint, not null]</TD></TR>
</TABLE>
</FONT>
>];
"drive" [
label=<
<FONT FACE="Helvetica">
<TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#ececfc">
<TR><TD><B><FONT POINT-SIZE="16">drive</FONT></B></TD></TR>
<TR><TD ALIGN="LEFT"><I><U>gsis_id</U></I> [gameid, not null]</TD></TR>
<TR><TD ALIGN="LEFT"><U>drive_id</U> [usmallint, not null]</TD></TR>
<TR><TD ALIGN="LEFT">start_field [field_pos, null]</TD></TR>
<TR><TD ALIGN="LEFT">start_time [game_time, not null]</TD></TR>
<TR><TD ALIGN="LEFT">end_field [field_pos, null]</TD></TR>
<TR><TD ALIGN="LEFT">end_time [game_time, not null]</TD></TR>
<TR><TD ALIGN="LEFT">pos_team [varchar, not null]</TD></TR>
<TR><TD ALIGN="LEFT">pos_time [pos_period, null]</TD></TR>
</TABLE>
</FONT>
>];
"play" [
label=<
<FONT FACE="Helvetica">
<TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#ececfc">
<TR><TD><B><FONT POINT-SIZE="16">play</FONT></B></TD></TR>
<TR><TD ALIGN="LEFT"><I><U>gsis_id</U></I> [gameid, not null]</TD></TR>
<TR><TD ALIGN="LEFT"><I><U>drive_id</U></I> [usmallint, not null]</TD></TR>
<TR><TD ALIGN="LEFT"><U>play_id</U> [usmallint, not null]</TD></TR>
<TR><TD ALIGN="LEFT">time [game_time, not null]</TD></TR>
<TR><TD ALIGN="LEFT">pos_team [varchar, not null]</TD></TR>
<TR><TD ALIGN="LEFT">yardline [field_pos, null]</TD></TR>
<TR><TD ALIGN="LEFT">down [smallint, null]</TD></TR>
<TR><TD ALIGN="LEFT">yards_to_go [smallint, null]</TD></TR>
</TABLE>
</FONT>
>];
"play_player" [
label=<
<FONT FACE="Helvetica">
<TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#ececfc">
<TR><TD><B><FONT POINT-SIZE="16">play_player</FONT></B></TD></TR>
<TR><TD ALIGN="LEFT"><I><U>gsis_id</U></I> [gameid, not null]</TD></TR>
<TR><TD ALIGN="LEFT"><I><U>drive_id</U></I> [usmallint, not null]</TD></TR>
<TR><TD ALIGN="LEFT"><I><U>play_id</U></I> [usmallint, not null]</TD></TR>
<TR><TD ALIGN="LEFT"><I><U>player_id</U></I> [varchar, not null]</TD></TR>
<TR><TD ALIGN="LEFT">team [varchar, not null]</TD></TR>
</TABLE>
</FONT>
>];
"meta" [
label=<
<FONT FACE="Helvetica">
<TABLE BORDER="0" CELLBORDER="1" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#fcecec">
<TR><TD><B><FONT POINT-SIZE="16">meta</FONT></B></TD></TR>
<TR><TD ALIGN="LEFT">version [smallint, null]</TD></TR>
<TR><TD ALIGN="LEFT">season_type [season_phase, null]</TD></TR>
<TR><TD ALIGN="LEFT">season_year [usmallint, null]</TD></TR>
<TR><TD ALIGN="LEFT">week [usmallint, null]</TD></TR>
</TABLE>
</FONT>
>];
"player" -- "team" [ headlabel="1", taillabel="0..N" ];
"game" -- "team" [ headlabel="1", taillabel="0..N" ];
"game" -- "team" [ headlabel="1", taillabel="0..N" ];
"drive" -- "team" [ headlabel="1", taillabel="0..N" ];
"play" -- "team" [ headlabel="1", taillabel="0..N" ];
"play_player" -- "team" [ headlabel="1", taillabel="0..N" ];
"game" -- "drive" [ headlabel="0..N", taillabel="1" ];
"game" -- "play" [ headlabel="0..N", taillabel="1" ];
"game" -- "play_player" [ headlabel="0..N", taillabel="1" ];
"drive" -- "play" [ headlabel="0..N", taillabel="1" ];
"drive" -- "play_player" [ headlabel="0..N", taillabel="1" ];
"play" -- "play_player" [ headlabel="0..N", taillabel="1" ];
"player" -- "play_player" [ headlabel="0..N", taillabel="1" ];
}
Would it be some better method to draw tables than html tables? I generated this dot syntax by https://github.com/BurntSushi/erd which parses some custom markup syntax and produces dot files but as in all similar projects it uses GraphViz to render final digram. I would be nice to have everything in Rust to use it in eg wasm. I also think erd or class diagrams might need some different way of rendering in square grid not in top down hierarchy, I think similarly as in your example here https://github.com/nadavrot/layout/blob/master/inputs/5.dot but more compact also arrows should point the whole table node or given record from FK if it would match relation key also it shouldn't cross the node but anchored on the outside edge.
Hello!
Thank you for this crate! I needed a quick way to draw graph in the browser and update it in real-time (I am using Yew
as the frontend Framework), and it works great!
It would be nice to have a way to support something similar to the ColorList
feature of graphivz
:
https://graphviz.org/docs/attr-types/colorList/
In my specific use case I would like to represent the State of Charge of a battery as the fill color gradient. A bit like so as showed in the previous link:
If there is already another existing way to do so please let me know!
Thank you for your time
I have hundreds (overlap with Graphviz source & many more). Let me know if you want some.
Could you provide a musl binary for linux?
This could be of interest to you: https://github.com/rust-build/rust-build.action
Hello,
Fairly simple problem: when I generate an SVG image with a directed edge that is some non-black color (e.g., red blue etc.), the arrow head that is generated seems to always be black. I did not see an option to change this via API.
I manually modified the output files and added fill="context-stroke"
to each marker and that resolved it, while also allowing for multiple edge colors in the output graph. I hypothesize that it's as simple as modifying the static at this line to resolve this:
layout/layout/src/backends/svg.rs
Line 19 in 573248c
Happy to submit a PR myself if it is that simple, but I wasn't sure if there are some non-obvious issues with those changes.
The source dot file is both times:
digraph {
node [colorscheme=rdylgn10]
0 [ label="GoblinTower"]
1 [ label="Goldmine"]
2 [ label="💀",fillcolor=black,shape=doublecircle,style=filled,fontname="NotoColorEmoji"]
3 [ label="✨",fillcolor=black,shape=doublecircle,style=filled,fontname="NotoColorEmoji"]
1 -> 0 [ label="Plinko"]
}
Can be checked out in paritytech/orchestra#43 when run with cargo test --all-features
The expectation would be to obtain the identical rendering as retrieved.
Is there anything else you'd need or how I can help?
Hi,
Wanted to compare the perf. of your lib with Graphviz dot on some examples where dot is really slow at making the layout.
The DOT file I tried may have hit either a bug in the lib, or there is some algorithm that is slow.
The input file has around 700 nodes and at least that many edges - it's the CFG of a function being optimized by MSVC.
With Graphviz, dot took 1m 52s to complete. layout after >10 min is still not done. Built it on a Mac and took with the Process monitor several samples, all seem to be similar, with this deep call tree showing up each time. Build is done in release mode.
2330 layout::topo::layout::VisualGraph::do_it::h4815c5196694548a (in run) + 181 [0x104ef6fd5] 2330 layout::topo::layout::VisualGraph::to_valid_dag::hd5e8ba77059eb154 (in run) + 1129 [0x104ef7a19] 2330 layout::adt::dag::DAG::verify::h0fcd6cdb9de61321 (in run) + 406 [0x104eec266] 2330 layout::adt::dag::DAG::is_reachable_inner::h765e6031c3bd508a (in run) + 152 [0x104eec488] 2330 layout::adt::dag::DAG::is_reachable_inner::h765e6031c3bd508a (in run) + 152 [0x104eec488] 2330 layout::adt::dag::DAG::is_reachable_inner::h765e6031c3bd508a (in run) + 152 [0x104eec488] and lots more calls to is_reachable_inner
Sharing the input DOT file here:
https://gist.github.com/gratianlup/f51dd6efc7ebc314d12a317dd0b01c60
And the sampling summary with the call graph here:
https://gist.github.com/gratianlup/8e7d8fd3c9883108ba9e55a87f0464eb
Thanks,
Gratian
Given more than one edge between two nodes exists, all sibling edges should be spaced appropriately to minimize intersections and overlapping between siblings.
In this example, the dashed and normal edges between Node 1 and Node 2 follow along the same path between nodes.
use layout::adt::dag::NodeHandle;
use layout::core::base::Orientation;
use layout::core::style::LineStyleKind;
use layout::core::style::StyleAttr;
use layout::std_shapes::shapes::Element;
use layout::std_shapes::shapes::LineEndKind;
use layout::std_shapes::shapes::ShapeKind;
let mut svg_writer: layout::backends::svg::SVGWriter = layout::backends::svg::SVGWriter::new();
let mut graph: layout::topo::layout::VisualGraph = layout::topo::layout::VisualGraph::new(layout::core::base::Orientation::LeftToRight);
let node_1: Element = Element::create(
ShapeKind::new_circle(&"Node 1"),
StyleAttr::simple(),
Orientation::LeftToRight,
layout::core::geometry::Point::new(100., 100.));
let node_2: Element = Element::create(
ShapeKind::new_circle(&"Node 2"),
StyleAttr::simple(),
Orientation::LeftToRight,
layout::core::geometry::Point::new(100., 100.));
let handle_1: NodeHandle = graph.add_node(node_1);
let handle_2: NodeHandle = graph.add_node(node_2);
// Dashed edge.
graph.add_edge(layout::std_shapes::shapes::Arrow::new(
LineEndKind::Arrow,
LineEndKind::None,
LineStyleKind::Dashed,
&"",
&StyleAttr::simple(),
&None,
&None),
handle_2,
handle_1);
// Normal edge
graph.add_edge(layout::std_shapes::shapes::Arrow::new(
LineEndKind::Arrow,
LineEndKind::None,
LineStyleKind::Normal,
&"",
&StyleAttr::simple(),
&None,
&None),
handle_1,
handle_2);
graph.do_it(false, false, false, &mut svg_writer);
std::fs::write(path, &svg_writer.finalize());
Hi,
I've discovered the project from HN, as you probably have guessed. I would be excited to add this as a dependency for one of my personal projects. The way I'm used to is by configuring the dependency in Cargo.toml
, which pulls crates from crates.io (I think this is best practice). Are there any plans to publish this crate as well?
Hi -- this crate is really cool!!
I tested an example
digraph G {
node [width=0.13,height=0.13,fontsize=10,shape=point];
edge [fontsize=18,penwidth=1.3,arrowsize=1.3,fontname=Arial];
rankdir=LR;
labeljust=l;
margin=0;
15 -> 10 [minlen=2,color=red,label="0 (len=173,supp=807)"];
5 -> 15 [minlen=2,color=red,label="1 (len=430,supp=1567)"];
10 -> 7 [minlen=1,color=black,label="2 (len=26,supp=541)"];
7 -> 10 [minlen=1,color=black,label="3 (len=30,supp=951)"];
0 -> 7 [minlen=2,color=red,label="4 (len=618,supp=3220)"];
14 -> 13 [minlen=2,color=red,label="5 (len=632,supp=269)"];
11 -> 15 [minlen=1,color=black,label="6 (len=43,supp=40)"];
}
and got this output
as compared to what running dot yields
It would be awesome to get closer to the dot output.
Thank you very much!!
So it seems rust can easily be compiled to WebAssembly, at least that's what I read. Is there anything stopping me from throwing this in a compiler and having it generate an svg in the browser by throwing my dot source into the module? I don't really understand how to build or integrate WASM yet, this is like my first try of understanding that.
Hi!
As a user, I would like to request support for compass points in the src_port
/tailport
and dst_port
/headport
properties of arrows in graphviz. This would allow me to specify which side of a node an arrow should originate from or end at, without having to manually define ports.
Doc for the portPos
type.
Example:
digraph G {
B -> A:sw;
}
With a box shape:
digraph G {
{
a [shape=box]
b [shape=box]
}
a:e -> b:nw;
}
What license is the code being published under?
I suspect a related question will be: How much of it was developed via inspection of the original graphviz code? (That may make it subject to the license for that code, which is presented here; that document says it uses "The Common Public License Version 1.0" that was produced from the Eclipse project.)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.