Graphviz / dot label overlap with edge - graph

I have a label that barely touch the edge, but there were plenty of space round it, is there anyway to make it not touching the edge in Graphiz? This is minor issue, but I have to redraw in powerpoint if cannot resolve by coding as my collaborator is very unhappy about this.. (Or I may export to JPEG and make changing in paintbrush)...
And we have to do this in black and white only so changing color would not help :(
minimal example: (the whole diagram is much more complex, and I have to put A E B in the same rank)
digraph "md" {
rankdir=TB;
size="8,8";
node [fontname="Helvetica" fontsize=10 shape=box];
edge [fontname="Helvetica" fontsize=10];
center=1;
{rank=min "A"}
{rank=min "B"}
{rank=min "E"}
"A" -> "B" [label="0.55***", dir=both];
"E" -> "B" [label="0.22" labeldistance="2"];
}

For the records - making the example minimal
digraph {
{ rank=same A -> B -> C }
A -> C [label=AC]
}
gives
using xlabel instead of label may help (but seems to have side effects)
digraph {
{ rank=same A -> B -> C }
A -> C [xlabel=AC]
}
gives

Related

How to show arrows symmetrically for self-loops in a dot graph

I am working with a DFA which I have described in the dot file format. One of the nodes in my DFA has two self-loops, which I originally described with the following way
digraph {
rankdir=LR;
a -> a [color=blue]
a -> a [color=green]
}
Which produces this output when I run dot graph.gv -Tpng
For symmetry reasons, I'd like to have the blue and the green arrows on opposite sides of the node. I can modify the headport and tailport of the green arrow to force it to start from the bottom of the node as follows:
digraph {
rankdir=LR;
a -> a [color=blue]
a:sw -> a:se [color=green]
}
Unfortunately, that produces an absolutely wonky output:
What's the correct way of getting the two arrows to be symmetrically opposite each other on the node?
I doubt you have a lot of control over the edge layout. But you can experiment with the headport attribute values. Here are some examples.
Example 1
digraph {
rankdir=LR;
a:n -> a [color=blue]
a:s -> a [color=green headport=center]
}
View in editor
Example 2
digraph {
rankdir=LR;
a:ne -> a [color=blue]
a:sw -> a [color=green headport=center]
}
View in editor
Other layouts
You can also experiment with different layouts. Here are some options for the circo layout.
Example 3
digraph {
rankdir=LR;
layout="circo"
a:n -> a:_ [color=blue headport=n]
a:s -> a:_ [color=green headport=s dir=back]
}
View in editor
Example 4
digraph {
rankdir=LR;
layout="circo"
a:nw -> a:w [color=blue headport=_]
a:se -> a:e [color=green headport=_]
}
View in editor

Given many rectangles, whats the best approach to group by line

TLDR: How to find boxes that are lined up horizontally
Given I have the data from an image like this:
We can visually see that we have two lines:
Tare: 11700 kg 10:40:58 am 16-May
Gross: 21300 kg 12:49:34 pm 9-Aug
The data I have for each blue box shown in the image is:
Top
Left
Width
Height
Coordinates for each corner of the box (X, Y)
My main thought is to start from the top of my "grid" and loop through each value of y, and then group boxes where they share the largest amount of matching "y" values, but it seems very over the top for something that seems simple.
Unsure really where to go from here
Example data set
I was able to get boxes lined up using this bit of code (in JavaScript), it essentially finds the first "most top left" box, and then finds any boxes that "intersect" with a line that starts from the middle of that first box
We don't care what order we get the boxes in, so as long as we start with the most left on any line we are golden.
function getMostTopLeftBox(boxes) {
const sorted = boxes.slice()
.sort(
(a, b) => {
if (a.Left === b.Left) {
return a.Top < b.Top ? -1 : 1;
}
return a.Left < b.Left ? -1 : 1;
}
);
return sorted[0];
}
function getAlignedBoxesFromSet(boxes) {
const mostTopLeftBox = getMostTopLeftBox(boxes);
const line = mostTopLeftBox.Top + (mostTopLeftBox.Height / 2);
return boxes
.filter(({ Top, Height }) => Top < line && (Top + Height) > line)
.sort(({ Left: a }, { Left: b }) => a < b ? -1 : 1)
}
function getAlignedBoxes(boxes) {
let remaining = boxes;
const lines = [];
const next = () => {
const line = getAlignedBoxesFromSet(remaining);
lines.push(line);
remaining = remaining.filter(box => line.indexOf(box) === -1);
if (!remaining.length) {
return;
}
return next();
};
next();
return lines;
}
The above code with the data set provided above gives us this result
However, it doesn't account for slight angles on the boxes, for example this image:
Another example of different boxes, with senstive information removed:
You can see from the above that the values below should be considered to be on the same line:
Product: [type]
Num Of [type]: 0
[value]: [value]
I may make a new question for this, but part of the answer to this is to figure out the actual curve of a line, and not just assume that the median angle of all lines is the actual "curve" of the line, so if I was to start with the most left box, then progress to the second box, now I have two distinct lines that I would want to find the smoothed curve for, which I would then use to find the next box, as I find each box I would want to adjust this curve to find the complete line, I will investigate this one further, if anyone has any hints, please do mention it.
I've managed to solve this, with a variant of the code posted in the question.
Here is a code sandbox of the solution, I will do a full write up of this, but here it is for now: https://codesandbox.io/s/102xnl7on3
Here is an example of grouped boxes based on angled lines calculated from the angle of all horizontal lines, if all the boxes were to be straight, then the result would be the lines being straight as well, so it should work in all scenarios.
Here is also an example where the lines are straight:
You can see the lines from the box before intersecting with the next box, it does this each time until it can find a complete line of boxes (till no more line up), this works out better than using an average angle from the entire data set.
I would like to be able to generate a mathematical curve for the already found boxes and apply that to find the next box, but for now, using the previous box as the anchor works pretty well.

Graphviz Composite Structure Diagram

Is it possible to generate with Graphviz / dot a UML composite structure diagram like the one below?
By using (open) box arrowheads https://graphviz.gitlab.io/doc/info/arrows.html, this gets pretty close:
digraph boxed {
graph [splines=ortho nodesep =.75]
node [shape=box]
edge [dir=both arrowtail=obox arrowhead=obox]
{ rank=same
x1 -> x2 -> x3
}
x1:s -> y1:n
}

graphviz dot to pdf: distance from graph to title

in graphviz dot, generating pdf output.
how do I increase the distance between the title and the graph?
digraph "test" {
graph [ label="need more distance to below" ];
A -> B;
}
advice appreciated.
Not preferable solution but Append "\n" at the end of label. If you need more space add more "\n".

how to optimize layout in graphviz to remove unecessary edges intersections (crossings)?

I am preparing the automaitc documentation of DB relations. The tool is graphviz. Problem I have is that the placement of nodes on the output image is not opptimal and there are many unecessary intersection of edges.
Is there any method to perform optimazation of the graph so the result will have minumum edges intersetction (crossing)?
digraph structs {
node [shape=Mrecord];
overlap="false";
splines="true";
layout=sfdp;
rankdir=LR;
ttype[label="::: ttype :::|<id>id|<table_name>table_name|<type_name>type_name|<synopsis>synopsis"];
tevents[label="::: tevents :::|<id>id|<id_tcases>id_tcases|<id_ttype>id_ttype|<synopsis>synopsis|<expiredate>expiredate|<open>open"];
toperationlog[label="::: toperationlog :::|<id>id|<executiondate>executiondate|<executiontime>executiontime|<query>query|<id_tusers>id_tusers"];
tdocuments[label="::: tdocuments :::|<id>id|<id_tcases>id_tcases|<id_ttype>id_ttype|<path>path|<creationdate>creationdate"];
tcustomers_cases[label="::: tcustomers_cases :::|<id_tcustomers>id_tcustomers|<id_tcases>id_tcases"];
tcases[label="::: tcases :::|<id>id|<creationdate>creationdate|<incomingdate>incomingdate|<clousuredate>clousuredate|<synopsis>synopsis|<notes>notes|<id_ttype>id_ttype|<id_tusers>id_tusers"];
tusers[label="::: tusers :::|<id>id|<username>username|<password>password|<firstname>firstname|<lastname>lastname|<role_id>role_id"];
tcustomers[label="::: tcustomers :::|<id>id|<firstname>firstname|<lastname>lastname|<email>email|<phone>phone|<mobile>mobile|<address>address"];
tevents:id_tcases -> tcases:id [arrowhead="none"];
tevents:id_ttype -> ttype:id [arrowhead="none"];
toperationlog:id_tusers -> tusers:id [arrowhead="none"];
tdocuments:id_tcases -> tcases:id [arrowhead="none"];
tdocuments:id_ttype -> ttype:id [arrowhead="none"];
tcustomers_cases:id_tcustomers -> tcustomers:id [arrowhead="none"];
tcustomers_cases:id_tcases -> tcases:id [arrowhead="none"];
tcases:id_ttype -> ttype:id [arrowhead="none"];
tcases:id_tusers -> tusers:id [arrowhead="none"];
}
Setting remincross to true will cause cross minimization to run a second time, which should improve the look of the graph by reducing the number of edge crosses.

Resources