Skip to content

Instantly share code, notes, and snippets.

@steveroush
Created June 5, 2023 02:21
Show Gist options
  • Save steveroush/b45d4c2b79019128d64e7292da955436 to your computer and use it in GitHub Desktop.
Save steveroush/b45d4c2b79019128d64e7292da955436 to your computer and use it in GitHub Desktop.
A GVPR program to improve Graphviz edge labels
/***********************************************
1. save this file as improveEdgeLabels.gvpr
2. what it does: it allows you to re-position Graphviz edge labels
3. command line example (works with any Graphviz engine):
dot myfile.gv |gvpr -cf improveEdgeLabels.gvpr |neato -n2 -Tpng >myfile.png
4. how to use:
improveEdgeLabels adds 3 new edge attributes to Graphviz:
- labelOverlay, labelAdjust, label2Node
- they can be used separately or in combination
- if labelOverlay=true
overlay label/xlabel on-top-of edge - centered
- if labelOverlay=n% (n is integer)
overlay label/xlabel on-top-of edge - n% of the way from tail-to-head
- if labelAdjust (X,Y)
adjust edge.lp by X,Y (either/both can be negative)
- if label2Node = true
delete the edge label
replace the edge label with a node, where pos=edge.lp
set node attributes
#### future:
- set/change various attributes of the new node
*************************************************/
BEGIN{
int i, cnt, LN=0;
string tStr, tok[int];
graph_t Root;
// do NOT call with direct output from sprintf - there is a bug - string will be empty
void doError(string eString) {
printf(2, "Error:: %s\n", eString);
}
////////////////////////////////////////////////////////////////////
int istrue(string checkme, int rc) {
checkme = tolower(checkme);
if (checkme == "@(1|yes|true)") // ksh syntax
rc=1;
else
rc=0;
return(rc);
}
/////////////////////////////////////////////////////////////////
string overlayPos(string Pos, string cmd) {
string p1, p2, pnew;
float x1, y1, x2, y2, dx, dy, percent;
p1="";
if (cmd=="(+([0-9])\%)") {
percent=.01 * (float)sub(cmd,"%");
} else {
percent=.5;
}
cnt=tokens(Pos, tok);
for (i=0; i<cnt; i++) {
if (tok[i]=="[es]*")
continue;
if (p1=="") {
p1=tok[i];
}
if (i==cnt-1)
p2=tok[i];
}
//print("// cmd: ", cmd, " p1: ", p1," p2: ",p2);
//print("// PERCENT: ", percent );
x1=(float)xOf(p1);
x2=(float)xOf(p2);
y1=(float)yOf(p1);
y2=(float)yOf(p2);
dx=x2-x1;
dy=y2-y1;
return (string)(x1 + percent*dx) + ", " + (string)(y1 + percent*dy);
}
///////////////////////////////////////////////////////////////////
string adjustPos(string Pos, string adj) {
string newPt;
newPt=(string)((float)xOf(Pos) + (float)xOf(adj)) + ", " + (string)((float)yOf(Pos) + (float)yOf(adj));
return newPt;
}
}
BEG_G{
Root=$G;
$G.notranslate="true"; // fighting pos changes for created nodes
if (!(hasAttr(Root, "splines") && Root.splines!=""))
Root.splines="true";
}
N{
$.REALLY_USE_THIS_pos=$.pos;
}
E{
if (hasAttr($, "labelOverlay")) {
if (istrue($.labelOverlay) || $.labelOverlay=="(+([0-9])\%)") {
if (hasAttr($, "lp") && $.lp!="") {
$.preOverlaylp=$.lp;
$.lp=overlayPos($.pos, $.labelOverlay);
}
} else {
doError("For edge " + $.name + ", invalid value (" + $.labelOverlay + ") for labelOverlay attribute");
}
}
if (hasAttr($, "labelAdjust") && ($.labelAdjust!="")) {
if (hasAttr($, "lp") && $.lp!="") {
$.preAdjustlp=$.lp;
$.lp=adjustPos($.lp, $.labelAdjust);
}
}
if (hasAttr($, "label2node") && istrue($.label2node)) {
node_t aNode;
string lbl, rep, s1, s2;
if (hasAttr($, "lp") && $.lp!="") {
$.preL2Nlp=$.lp;
$.preL2Nlabel=$.label;
//// create node
tStr="__labelNODE__" + (string)++LN;
aNode=node($G, tStr);
lbl=$.label;
rep=$.tail.name;
/********** problem w/ gsub, so roll our own ***********/
print ("// escape stuff >", lbl);
while (i=index(lbl,"\\T"), i!=-1) {
//print ("// x ", lbl, " ",i);
if (i>0)
s1=substr(lbl,0,i-1);
else
s1="";
if (i<(length(lbl)-2))
s2=substr(lbl,i+1);
else
s2="";
lbl=s1 + rep + s2;
}
print("// lbl:", lbl);
while (i=index(lbl,"\H"), i!=-1) {
//print ("// y ", lbl, " ",i);
lbl=substr(lbl,0,i-1) + rep + substr(lbl,i+1);
}
//print("// lbl:", lbl);
while (i=index(lbl,"\E"), i!=-1) {
//print ("// z ", lbl, " ",i);
lbl=substr(lbl,0,i-1) + rep + substr(lbl,i+1);
}
//print("// lbl:", lbl);
aNode.label=lbl;
aNode.shape="plaintext";
aNode.style="filled";
aNode.width=".05";
aNode.height=".05";
aNode.margin="0.01,0.01";
aNode.fillcolor="white";
aNode.pos=$.lp;
// clean-up edge attributes
$.label="";
$.lp="";
$._comment="edge label replaced by " + tStr;
}
}
}
/******************* test graph **************************
graph straight {
splines=false
edge [label="\T" labelOverlay=true color=purple]
node [shape=rect style=filled fillcolor=lightblue]
a1 -- b1
c1 -- d1 [labelOverlay="15%" label=high]
e1 -- f1 [label2node=true labelOverlay="75%" label=low]
g1:sw -- h1:nw
g1:s -- h1:n [labelOverlay="20%"]
g1:se -- h1:ne
j1 -- k1 [ label2node=true]
node[shape=square style=filled fillcolor=lightgreen]
{rank=sink
edge [fontcolor=red]
Aaa -- B3 [labelOverlay="33%"]
C98 -- D3 [labelOverlay="80%" label2node=true]
Eee -- F6 [label="adjust me" labelAdjust="45,-25"]
G12 -- H3 [label2node=true]
node [shape=plain fontcolor=blue style=""]
Lxx -- M123
Noo -- Ooo [label2node=true]
node [shape=oval fontcolor=blue style=""]
P -- Q
}
node[shape=circle style=filled fillcolor=pink]
edge [label2node=true label="aeiou" labelOverlay="78%"]
10 -- {11 12}
10 -- 13 [labelOverlay="38%"]
10 -- 14 [labelOverlay="62%"]
edge [label="three"]
node [shape=Mrecord]
13 -- 21
edge [labelOverlay="55%" label="T\nh\nr\ne\ne\n \n33"] 13 -- 22
edge [labelOverlay="60%"] 13 -- 23
}
*************************************************/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment