I'm currently having issues with designing UML-like diagrams on graphiz. The reason for the problem is that they are not exactly UML diagrams. The main difference is that I make use of indentations to add an hierarchy to an object's properties. Implementing these idiosyncrasies is a little difficult for me. What I'm trying to achieve is this:
I normally use a node shape called record to design these diagrams. The problem arises when I have to link two of these UML-like diagrams just like relationships in UML i.e. aggregation, association, composition, etc.
When I have the diagrams, I can't make the relationship with the arrows because the arrows only go from a random part of one node to another random part of the other node.
The way I have the UML-like diagrams is good, but the relationship arrow causes it not to be what I want as I want the arrows to go from a specific point of one node to another specific point of another node.
The DOT code I used to create this graph is like this:
digraph G {
fontname = "Bitstream Vera Sans"
fontsize = 8
node [
fontname = "Bitstream Vera Sans"
fontsize = 8
shape = "record"
]
edge [
fontname = "Bitstream Vera Sans"
fontsize = 8
]
Person [
label = "{Person \l\l \ age : int\l \ livesIn : City \l \ \ \ sinceYear : int}"
] // \l -new line, \ -indentation
City [
label = "{City \l \ \ name : string}"
]
Person -> City
}
I tried getting around this problem by using horizontal line divisions within the nodes even though I didn't want the lines. The horizontal line divisions make it possible for me to make this specific relationship possible by using ports, but they create a new problem of their own. The problem they create is that they get rid of the indentations I want and had in the previous graph. The way I tried to get around the arrow problems works, but new problems are created - the indentation disappears and the horizontal line divisions can't be made invisible
.
The code I used to create this graph is:
digraph G {
fontname = "Bitstream Vera Sans"
fontsize = 8
node [
fontname = "Bitstream Vera Sans"
fontsize = 8
shape = "record"
penwidth = 0.5
]
edge [
fontname = "Bitstream Vera Sans"
fontsize = 8
]
Person [
label = "{<g0> Person | <g1> age : int | <g2> livesIn : City | <g3> sinceYear : int}"
] // \l -new line, \ -indentation
City [
label = "{<f0> City | <f1> name : string}"
]
Person:<g2> -> City:<f1> [arrowhead = "empty", headlabel = "*"]
}
These indentations are a big part of the relationship, so I'm wondering if anyone knows what I can do to have these indentations back in the diagrams as well what I can do to make the horizontal line divisions invisible?
I'll appreciate if someone has a better way/idea that's also totally different from what I have done in diagrams 2 & 3, that will help me achieve diagram 1.
Your original attempt wasn't bad. I would say using ports is definitely the way to go.
If you place the node in a cluster you can use the cluster's border and hide the border of the record node, getting rid of those divider lines.
As you noted, using a backslash \ no longer works to escape a space. The workaround is to either use \ instead, this will escape the whitespace. As an alternative you could also replace each space with an &nnbsp;. Either one will achieve the required effect.
I made some minor changes to make things more readable, like put Graph properties in a graph block instead of in the root of the graph and rename the port-names to something more sensible. I also removed any ports not in use.
The final result I came up with was this:
...and this is the DOT code I used:
digraph G {
graph [
compound = true // To clip the head at the cluster border
penwidth = 2 // Make the cluster's borders a bit thicker
rankdir = "LR" // Make the arrow and nodes go from Left to Right
ranksep = 1 // Add a bit more space inbetween nodes
]
node [
color = none // Hide the node's border
fontname = "Bitstream Vera Sans"
height = 0 // Make the node as small as possible (it will grow if it needs more space)
margin = 0 // Remove unneeded whitespace
shape = "record" // So we can use ports
]
edge [
arrowhead = "open"
labelangle = -5 // Place the asteriks closer to the line
labeldistance = 2.5 // Place the asteriks further away from the arrow head
penwidth = 2 // Make the line a bit thicker
]
/* #NOTE: escaping spaces in the label using '\' doesn't work so use ' ' or '\' instead. */
subgraph cluster_Person {
Person [
label = "\N\l | \ \ \ age : int\l | <livesIn> \ \ \ livesIn : City\l | \ \ \ \ \ \ sinceYear : int\l"
]
}
subgraph cluster_City {
City [
label = "<city> \N\l | \ \ \ name : string\l"
]
}
Person:livesIn -> City:city [headlabel = "*", lhead = "cluster_City"] // lhead allows us to point to the cluster's border instead of the node, as long as we add `compound = true` to the graph
}
Related
I am using fruchterman_reingold_force_directed_layout algorithm on my graph
to get a cluster free layout. Below is code for my vertices and edge
using RectTopology = boost::rectangle_topology<>;
using point = RectTopology::point_type;
class MyVertex{
public:
MyVertex(){ myObject = NULL; }
Mybject* myObject;
point position;
std::string name;
};
class MyEdge{
public:
MyEdge(){ myLine = NULL; }
MyLine* myLine;
double weight;
};
//Boost graph defination
using graphT = boost::adjacency_list<boost::listS, boost::vecS, boost::undirectedS, MyVertex, MyEdge>;
using vertexT = boost::graph_traits<graphT>::vertex_descriptor; //Define Vertex
using vertexIt = boost::graph_traits<graphT>::vertex_iterator; //Vertex Iterator
using edgeT = boost::graph_traits<graphT>::edge_descriptor; //Define Edge
using edgeIt = boost::graph_traits<graphT>::edge_iterator; //Edge Iterator
forcedDirLay(){
boost::minstd_rand gen;
RectTopology rect_top(gen, 0, 0, 1, 1);
boost::random_graph_layout(myGraph, boost::get(&SLDVertex::position, myGraph), rect_top);
boost::fruchterman_reingold_force_directed_layout(myGraph, boost::get(&SLDVertex::position, myGraph), rect_top);
}
Imagine now I have a graph and perform my layout algorithm which all works fine
and I have position info for every vertices.
How can I visualize each vertex where they end up after layout algorithm
finished ? Is there a way to get position info to Dot file and I can visualize
Dot file ?
I have a function to convert my graph to dot file but don't know how
to get position info to dot
file. Thanks in advance.
GraphToDotFile(){
std::ofstream dot(".\\graph.dot");
boost::write_graphviz(dot, myGraph,
boost::make_label_writer(boost::get(&MyVertex::name, myGraph)));
dot.close();
}
You can use dynamic properties to add graphviz attributes, see the example:
https://www.boost.org/doc/libs/1_74_0/libs/graph/example/graphviz.cpp
The attributes that specify the position in graphviz are here:
Position of node, or spline control points.
For nodes, the position indicates the center of the node. On output,
the coordinates are in points.
In neato and fdp, pos can be used to set the initial position of a
node. By default, the coordinates are assumed to be in inches.
However, the -s command line flag can be used to specify different
units. As the output coordinates are in points, feeding the output of
a graph laid out by a Graphviz program into neato or fdp will almost
always require the -s flag.
When the -n command line flag is used with neato, it is assumed the
positions have been set by one of the layout programs, and are
therefore in points. Thus, neato -n can accept input correctly without
requiring a -s flag and, in fact, ignores any such flag.
Valid for: Edges, Nodes.
The neato -n trick works like a champ.
Adding a bit to the above,
yourProg produces graph.dot; then
neato -n -Tpng graph.dot >graph.png
You may have to fiddle with yourProg to make sure that graph.dot has all the required attributes, units are correct, etc. You can produce a tiny example by hand, then
dot -Tdot tiny.gv >tiny.dot
and compare the two .dot files
Hi my arrow like following:
https://i.imgur.com/EJNtfc3.png
Arrow on the red marked is glutinous.
How do I divide it?
add code
https://www.codepile.net/pile/XKvOwL3A
In your situation fastest fix would be to add nodesep = 0.15 graph attribute (right after digraph { statement). This attribute adjusts the minimal distance between the nodes in one rank. This results in:
Also you may play with headport and tailport attributes, as I suggested in the comment. Shortcut for them is to add a colon after node when you are defining edge.
If you replace TR_Client_Data -> idle with TR_Client_Data -> idle:e you will get this result:
Edges are crossed, but they are apart.
Also I've noticed that you define node attributes in a wrong way: keyword node defines global attributes for all nodes in the graph (or subgraph). If you want to specify attributes for a single node, place them after node definition.
e.g.
WRONG:
node [
shape = point,
fontsize = 12
] start_point;
CORRECT:
start_point [
shape = point,
fontsize = 12
];
I am wondering if I can define an alternative font for the whole graph.
...
digraph script_concept {
graph [layout="dot",fontname="helvetica"];
...
According to this 1 older post the fontname atribute can be defined only separately:
Nodes and edges don't inherit the font of the graph, you need to specify
them separately
Is there any other way, how to define the font globally?
No, there is no other way.
As in the forum post you linked, you have to define the default values separately (like the other attributes) at the beginning of your graphviz file:
digraph g {
graph [fontname = "helvetica"];
node [fontname = "helvetica"];
edge [fontname = "helvetica"];
...
}
Not sure if this is a recent update, but you can change these at the command-line level using the -G, -E and -N attribute flags. That is, the following works for me:
$ dot -Tpng -Nfontname=Roboto -Nfontsize=10 \
-Efontname=Roboto -Efontsize=10 \
tree.dot > tree.png
However, there's one easy trick, if you are exporting svgs:
sed 's/Times,serif/Helvetica/g' thegraph.svg > thegraph_helvetica.svg
combine this with Make and all the horror will be hidden :)
here's an example Makefile:
all: helvetica
svg:
cat thegraph.dot | dot -Tsvg > thegraph.svg
helvetica: svg
sed 's/Times,serif/Helvetica/g' thegraph.svg > thegraph_helvetica.svg
So far I tried this line but dot keeps pushing it aside making room for my nodes (pushes it to the right):
_diagram_info [shape="plaintext", label="My Diagram\l", fontsize=13]
Is there a way to center the label by pos, using dot?
That's how I'd add a title for a graph:
digraph {
// nodes, edges, subgraphs
...
// title
labelloc="t";
label="My Diagram";
}
This will add a centered title to the top of the graph.
The same syntax can also be used for subgraphs.
I am trying to have a node (or a subgraph, enclosing a node - whichever is possible/easier) rotated, like shown in this image:
(Note that it doesn't matter to me if the "B" label is rotated - only that the 'verti-*' texts in the record [or rather, the whole record node] are rotated as shown)
However, the closest I can to that, is the following dot code:
digraph graphname {
node [fontname=Monospace, fontsize=14];
subgraph clusterMasterBox {
node [shape=record];
l1 [label = "{ horiz-1 \r| \
horiz-2 \r| \
horiz-3 \r| \
horiz-4 \r} \
"];
subgraph clusterSubRotateBox {
rotate=90;
node [shape=record,rotate=90];
l2 [label = "{ verti-1 \r| \
verti-2 \r| \
verti-3 \r| \
verti-4 \r} \
"];
label="B";
}
label="A"
}
}
The only reason I have the subgraph clusterSubRotateBox there (and the only reason why it is nested inside the clusterMasterBox), is because I hoped I could assign rotation to it, but apparently I cannot - as the above code generates this image:
So my question is - is there a way to rotate a record node; if not on its own, then maybe as a part of subgraph (or a different kind of 'object')?
Thanks in advance for any suggestions,
Cheers!
If you want to rotate a single record based node then rankdir will work. I tried it for my graph,
digraph plugnoid {
rankdir=LR;
node[shape=Mrecord];
plugnoid [label="swarm| {<load0> onLoad|<plugin0> Plugin|<quit0> onQuit}|{<run0>run|<rehash0>rehash}"];}
The rankdir can have values LR,RL and TB(default). When I changed the rankdir to TB the output changed,
You may want to try them on your graph to get desired results. I experienced that when I used subgraph and set different rankdir the result was not as good. Please see http://www.graphviz.org/doc/info/shapes.html#record for more details.
For a single node, there is the orientation attribute. I just used
node[shape=hexagon, orientation=30]
To make a hexagon with a point at the top rather than a flat top.
Unfortunately doesn't seem to work on 'record' types :-(
there should be a "rotation" attribute on the graph object (see http://www.graphviz.org/doc/info/attrs.html#drotation) but it didn't do anything in my test. and, it would only apply to the whole graph (not cluster/subgraph) according to the docs.
i guess you'd first render the subgraph to postscript and then include it in the final graph as a custom shape for a single placeholder node. if you can't get "rotation" to do its thing, surely postscript has a simple way to apply a transform (rotation in this case), probably as simple as prefixing the generated code with some coordinates definition. sorry for the handwaving but i don't have time to try it now.
Graphviz does not support partial rotation - only entire graphs or polygonial nodes (https://www.graphviz.org/docs/attrs/rotate/ and https://graphviz.org/docs/attrs/orientation/).
To accomplish your goal, create a separate graph for each node or cluster you want rotated, and use dot (or desired engine) to create images (png, svg, etc.) Then include each image into an node using the image attribute (https://graphviz.org/docs/attrs/image/).
Note that Graphviz only supports rotate=90, while you are asking for -90. Use some other image manipulation program to rotate the result by 180 (good grief)
of the complete graphLike so:
rotatePart1a.gv
digraph graphname {
node [fontname=Monospace, fontsize=14];
subgraph clusterMasterBox {
label="A"
{rank=same // keep on same rank
l2 [shape=plain label="" image="rotatePart1b.png"]
node [shape=record];
l1 [label = "{ horiz-1 \r| \
horiz-2 \r| \
horiz-3 \r| \
horiz-4 \r} \
"];
}
}
}
rotatePart1b.gv
digraph graphname {
rotate=90;
node [fontname=Monospace, fontsize=14];
subgraph clusterSubRotateBox {
node [shape=record] // rotation at node-level is silently ignored
l2 [label = "{ verti-1 \r| \
verti-2 \r| \
verti-3 \r| \
verti-4 \r} \
"];
label="B";
}
}
Giving: