Add script to generate concept maps using Graphviz

This commit is contained in:
Ricardo Guiraldelli 2024-04-03 21:39:48 +03:00
parent 495185139b
commit 1b9d4a4a93
2 changed files with 209 additions and 0 deletions

177
concept-map.sh Normal file
View File

@ -0,0 +1,177 @@
SIMPLE_DOT=$(cat <<'EOF'
BEGIN {
edge = "->"
properties_separator = ";"
open_blocks = "{"
close_blocks = "}"
hyperedge_property = "_he"
uuid_command = "uuidgen"
blocks = 0
processed_line = 0
# Print the graph header.
#print "digraph {"
}
# Firstly, we process blocks.
NF == 1 && $1 == open_blocks {
# open block
blocks++
}
NF == 1 && $1 == "}" {
# close block
blocks--
}
# Secondly, we process "special rules".
## Nodes.
NF > 1 && $2 != edge {
printf ($1 "\t")
label = get_label($0)
get_properties($0, properties)
properties_str = generate_properties(label, properties)
if (length(properties_str)) {
print properties_str
}
processed_line = 1
}
## Edges.
NF > 3 && $2 == edge {
printf ($1 FS $2 FS $3)
label = get_label($0)
get_properties($0, properties)
properties_str = generate_properties(label, properties)
if (length(properties_str)) {
printf "\t"
print properties_str
}
processed_line = 1
}
# Thirdly, we do general treatment for every line.
{
if (!processed_line) {
print $0
}
processed_line = 0
}
END {
# Print the closing curly brace.
print "}"
}
function get_label(_line, _field, _out, _split, _len) {
_field = 2
if ($2 == edge) {
_field = 4
}
# Special case: there isn't label, but there are properties.
_len = split($(_field), _split, properties_separator)
if ((_len > 1) && !_split[1]) {
# There is a properties_separator in the field, and no label.
return
}
for (_field; _field <= NF; _field++) {
_len = split($(_field), _split, properties_separator)
if (_len > 1) {
_out = !(_out) ? _split[1] : (_out FS _split[1])
return _out
} else {
_out = !(_out) ? $(_field) : (_out FS $(_field))
}
}
return _out
}
function get_properties(_line, _split, _field, _len, _uuid) {
delete _split
_len = split(_line, _split, properties_separator)
if (!_len) {
return
}
for (_field = 2; _field <= _len; _field++) {
if (_split[_field] == hyperedge_property) {
uuid_command | getline _uuid
_split[_field] = hyperedge_property "=\"" _uuid "\""
close(uuid_command)
}
_split[_field - 1] = _split[_field]
}
delete _split[_len]
return
}
function generate_properties(_label, _properties, _out, _len, _type_label) {
_type_label = "label"
for (p in _properties) {
if (match(hyperedge_property, _properties[p])) {
_type_label = "xlabel"
}
}
if (_label) {
_out = (_type_label "=\"" _label "\"")
}
_len = length(_properties)
for (i = 1; i <= _len; i++) {
_out = !(_out) ? _properties[1] : (_out "," _properties[i])
}
return (!(_out) ? "" : ("[" _out "]"))
}
EOF
)
HYPERGRAPH_PROCESSOR=$(cat <<'EOF'
// An attempt to reproduce what was reported in https://gitlab.com/graphviz/graphviz/-/issues/1911#note_473279838
E [ _he ] {
// Creates the node representing the hyper-edge, if it already doesn't exist.
node_t hyperedge = isNode($G, $._he);
if (!hyperedge) {
hyperedge = node($G, $._he);
hyperedge.shape = "point";
hyperedge.fillcolor = "red";
if ($.label) {
/*
hyperedge.label = $.label;
hyperedge.shape = "none";
*/
hyperedge.xlabel = $.label;
}
}
// Creates the edges to and from hyperedge, if it already doesn't exist.
if (!isEdge_sg($G, $.tail, hyperedge, "")) {
edge_t new_out = edge_sg($G, $.tail, hyperedge, "");
new_out.arrowhead = "none";
}
if(!isEdge_sg($G, hyperedge, $.head, "")) {
edge_t new_in = edge_sg($G, hyperedge, $.head, "");
}
// Removes the current edge (with attribute "_he") because edges to "hyperedge" are added.
delete($G, $);
}
EOF
)
# Process the input files and produce a SVG.
cat <(echo "digraph {") ~/src/scripts/concept-style.dot <(awk -f <(echo "${SIMPLE_DOT}") $*) |
gvpr -c -f <(echo "${HYPERGRAPH_PROCESSOR}") |
dot -Tsvg
## DEAD-CODE
## awk -f ~/tmp/graphviz/concept.awk $* |
## gvpr -c -f ~/tmp/graphviz/hypergraph.gvpr |
## xsel -ib

32
concept-style.dot Normal file
View File

@ -0,0 +1,32 @@
// Style taken, and modified, from https://mostlymaths.net/2023/07/concept-maps-helper.html/
layout=dot
margin=1
bgcolor="#ffffff00"
rankdir=TB
fontname="Sans-Serif"
nodesep=0.5
overlap=scale
compound=true
node [
fontname = "Sans-Serif"
style="rounded,filled"
labelloc=c
margin="0.3,0.15"
splines=true
shape=rect
fontsize=15
];
edge [
minlen=3
penwidth=2
color="#33333388"
fontname="Sans-Serif"
fontsize=14
arrowhead=normal // Latest papers about cmaps have recovered heads
];
graph [
//margin=8
style="rounded,dotted"
];
fontsize=24
labelloc="t"