Skip to content

Commit 6fa23b2

Browse files
RobotechnicMDLC01
andauthored
diagraph:0.3.1 (#1562)
Co-authored-by: Malo <[email protected]>
1 parent e294fac commit 6fa23b2

File tree

8 files changed

+1291
-0
lines changed

8 files changed

+1291
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Robotechnic
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
"Software"), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# diagraph
2+
3+
A simple Graphviz binding for Typst using the WebAssembly plugin system.
4+
5+
## Usage
6+
7+
### Basic usage
8+
9+
10+
You can render a Graphviz Dot string to a SVG image using the `render` function:
11+
12+
```typ
13+
#render("digraph { a -> b }")
14+
```
15+
16+
Alternatively, you can use `raw-render` to pass a `raw` instead of a string:
17+
18+
<!--EXAMPLE(raw-render)-->
19+
````typ
20+
#raw-render(
21+
```dot
22+
digraph {
23+
a -> b
24+
}
25+
```
26+
)
27+
````
28+
![raw-render](https://raw.githubusercontent.com/Robotechnic/diagraph/main/images/raw-render1.png)
29+
30+
For more information about the Graphviz Dot language, you can check the [official documentation](https://graphviz.org/documentation/).
31+
32+
### Advanced usage
33+
34+
Check the [manual](https://raw.githubusercontent.com/Robotechnic/diagraph/main/doc/manual.pdf) for more information about the plugin.
35+
36+
37+
## License
38+
39+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
40+
41+
## Changelog
42+
43+
### 0.3.1
44+
45+
- Updated graphviz version to 12.2.1
46+
- Fixed a bug with the font being incorrectly set
47+
- Added adjacency lists to the graph rendering possibilities
48+
49+
### 0.3.0
50+
51+
- Added support for edge labels
52+
- Added a manual generated with Typst
53+
- Updated graphviz version
54+
- Fix an error in math mode detection
55+
56+
### 0.2.5
57+
58+
- If the shape is point, the label isn't displayed
59+
- Now a minimum size is not enforced if the node label is empty
60+
- Added support for font alternatives
61+
62+
### 0.2.4
63+
64+
- Added support for xlabels which are now rendered by Typst
65+
- Added support for cluster labels which are now rendered by Typst
66+
- Fix a margin problem with the clusters
67+
68+
### 0.2.3
69+
70+
- Updated to typst 0.11.0
71+
- Added support for `fontcolor`, `fontsize` and `fontname` nodes attributes
72+
- Diagraph now uses a protocol generator to generate the wasm interface
73+
74+
### 0.2.2
75+
76+
- Fix an alignment issue
77+
- Added a better mathematic formula recognition for node labels
78+
79+
### 0.2.1
80+
81+
- Added support for relative lenghts in the `width` and `height` arguments
82+
- Fix various bugs
83+
84+
### 0.2.0
85+
86+
- Node labels are now handled by Typst
87+
88+
### 0.1.2
89+
90+
- Graphs are now scaled to make the graph text size match the document text size
91+
92+
### 0.1.1
93+
94+
- Remove the `raw-render-rule` show rule because it doesn't allow use of custom font and the `render` / `raw-render` functions are more flexible
95+
- Add the `background` parameter to the `render` and `raw-render` typst functions and default it to `transparent` instead of `white`
96+
- Add center attribute to draw graph in the center of the svg in the `render` c function
97+
98+
### 0.1.0
99+
100+
Initial working version
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
#import "internals.typ": render
2+
3+
#let inches = (
4+
"width",
5+
"height",
6+
"len",
7+
"lheight",
8+
"lwidth",
9+
"margin",
10+
"nodesep",
11+
"page",
12+
"size",
13+
"vertices",
14+
)
15+
16+
#let value-to-str(key, value) = {
17+
if type(value) == length {
18+
if key in inches {
19+
str(value.to-absolute().inches())
20+
} else {
21+
str(value.to-absolute().pt())
22+
}
23+
} else if type(value) == color {
24+
"\"" + value.to-hex() + "\""
25+
} else if value == none {
26+
"none"
27+
} else if value == true {
28+
"true"
29+
} else if value == false {
30+
"false"
31+
} else {
32+
"\"" + str(value) + "\""
33+
}
34+
}
35+
36+
#let dict-to-graph-args(args, sep: ";", parent: "") = {
37+
for (key, value) in args {
38+
if type(value) == dictionary {
39+
if sep == "," {
40+
panic("Invalid argument for " + parent + "[" + key + "=...]")
41+
}
42+
key + "[" + dict-to-graph-args(value, sep: ",", parent: key) + "]"
43+
} else {
44+
key + "=" + if type(value) == array {
45+
"\"(" + value.map(value-to-str.with(key)).join(",") + ")\""
46+
} else {
47+
value-to-str(key, value)
48+
}
49+
} + sep
50+
}
51+
}
52+
53+
#let create-nodes(min, max) = {
54+
range(min, max).map(str).join(";")
55+
}
56+
57+
#let create-attributes(labels) = {
58+
labels
59+
.enumerate()
60+
.map(label => {
61+
if type(label.at(1)) == dictionary {
62+
let _ = label.at(1).remove("label", default: "")
63+
_ = label.at(1).remove("xlabel", default: "")
64+
str(label.at(0)) + "[" + dict-to-graph-args(label.at(1), sep: ",") + "]"
65+
}
66+
})
67+
.join("")
68+
}
69+
70+
#let create-clusters(clusters) = {
71+
clusters
72+
.enumerate()
73+
.map(cluster => {
74+
let id = str(cluster.at(0))
75+
let nodes = cluster.at(1)
76+
"subgraph cluster_" + id + "{"
77+
if type(nodes) == dictionary {
78+
let _ = nodes.remove("label", default: "")
79+
let subnodes = nodes.remove("nodes")
80+
dict-to-graph-args(nodes)
81+
subnodes.map(str).join(";")
82+
} else {
83+
nodes.map(str).join(";")
84+
}
85+
"};"
86+
})
87+
.join("")
88+
}
89+
90+
#let build-edges(adjacency, directed) = {
91+
adjacency
92+
.enumerate()
93+
.map(edges-list => {
94+
edges-list
95+
.at(1)
96+
.enumerate()
97+
.map(edge => {
98+
if edge.at(1) == none {
99+
""
100+
} else {
101+
str(edges-list.at(0))
102+
if directed {
103+
" -> "
104+
} else {
105+
" -- "
106+
}
107+
str(edge.at(0)) + ";"
108+
}
109+
})
110+
.join("")
111+
})
112+
.join("")
113+
}
114+
115+
#let adjacency(..args) = context {
116+
if args.pos().len() != 1 {
117+
panic("adjacency() requires one argument: an adjacency matrix")
118+
}
119+
let adjacency = args.at(0)
120+
let graph-params = args.named()
121+
let vertex-labels = graph-params.remove("vertex-labels", default: ())
122+
let directed = graph-params.remove("directed", default: true)
123+
let clusters = graph-params.remove("clusters", default: ())
124+
let debug = graph-params.remove("debug", default: false)
125+
let clip = graph-params.remove("clip", default: true)
126+
127+
render(
128+
if directed {
129+
"digraph"
130+
} else {
131+
"graph"
132+
} + "{" + dict-to-graph-args(graph-params) + create-nodes(
133+
0,
134+
adjacency.len(),
135+
) + ";" + create-clusters(clusters) + create-attributes(vertex-labels) + build-edges(adjacency, directed) + "}",
136+
debug: debug,
137+
clip: clip,
138+
labels: name => {
139+
let id = int(name)
140+
if id < vertex-labels.len() {
141+
let label = vertex-labels.at(id)
142+
if type(label) == dictionary {
143+
label.at("label", default: "")
144+
} else {
145+
label
146+
}
147+
} else {
148+
""
149+
}
150+
},
151+
xlabels: name => {
152+
let id = int(name)
153+
if id < vertex-labels.len() {
154+
let label = vertex-labels.at(id)
155+
if type(label) == dictionary {
156+
label.at("xlabel", default: none)
157+
} else {
158+
none
159+
}
160+
} else {
161+
none
162+
}
163+
},
164+
edges: (name, edges) => {
165+
let id = int(name)
166+
let result = (:)
167+
for edge in edges {
168+
let edge-id = int(edge)
169+
let label = adjacency.at(id).at(edge-id)
170+
if label != none {
171+
result.insert(edge, [#label])
172+
}
173+
}
174+
result
175+
},
176+
clusters: (name) => {
177+
let id = int(name.split("_").at(1))
178+
if id < clusters.len() {
179+
let cluster = clusters.at(id)
180+
if type(cluster) == dictionary {
181+
cluster.at("label", default: none)
182+
} else {
183+
none
184+
}
185+
} else {
186+
none
187+
}
188+
}
189+
)
190+
}
191+
1.02 MB
Binary file not shown.

0 commit comments

Comments
 (0)