72 lines
3.9 KiB
Plaintext
72 lines
3.9 KiB
Plaintext
|
Notes from reading https://www.graphviz.org/Documentation/TSE93.pdf, which
|
||
|
describes an algorithm for drawing an acyclic graph in basically the way which I
|
||
|
want.
|
||
|
|
||
|
This document assumes the primary flow of drawing is downward, and secondary is
|
||
|
right.
|
||
|
|
||
|
For all of this it might be easier to not even consider edge values yet, as
|
||
|
those could be done by converting them into vertices themselves after the
|
||
|
cyclic-edge-reversal and then converting them back later.
|
||
|
|
||
|
Drawing the graph is a four step process:
|
||
|
|
||
|
1) Rank nodes in the Y axis
|
||
|
- Graph must be acyclic.
|
||
|
- This can be accomplished by strategically reversing edges which cause
|
||
|
a cycle, and then reversing them back as a post-processing step.
|
||
|
- Edges can be found by:
|
||
|
- walking out from a particular node depth-first from some arbitrary
|
||
|
node.
|
||
|
- As you do so you assign a rank based on depth to each node you
|
||
|
encounter.
|
||
|
- If any edge is destined for a node which has already been seen you
|
||
|
look at the ranks of the source and destination, and if the source
|
||
|
is _greater_ than the destination you reverse the edge's
|
||
|
direction.
|
||
|
- I think that algorithm only works if there's a source/sink? might have
|
||
|
to be modified, or the walk must traverse both to & from.
|
||
|
- Assign all edges a weight, default 1, but possibly externally assigned to
|
||
|
be greater.
|
||
|
- Take a "feasible" minimum spanning tree (MST) of the graph
|
||
|
- Feasibility is defined as each edge being "tight", meaning, once you
|
||
|
rank each node by their distance from the root and define the length
|
||
|
of an edge as the difference of rank of its head and tail, that each
|
||
|
tree edge will have a length of 1.
|
||
|
- Perform the following on the MST:
|
||
|
- For each edge of the graph assign the cut value
|
||
|
- If you were to remove any edge of an MST it would create two
|
||
|
separate MSTs. The side the edge was pointing from is the tail,
|
||
|
the side it was pointing to is the head.
|
||
|
- Looking at edges _in the original graph_, sum the weights of all
|
||
|
edges directed from the tail to the head (including the one
|
||
|
removed) and subtract from that the sum of the weights of the
|
||
|
edges directed from the head to the tail. This is the cut value.
|
||
|
- "...note that the cut values can be computed using information
|
||
|
local to an edge if the search is ordered from the leaves of the
|
||
|
feasible tree inward. It is trivial to compute the cut value of a
|
||
|
tree edge with one of its endpoints a leaf in the tree, since
|
||
|
either the head or the tail component consists of a single node.
|
||
|
Now, assuming the cut values are known for all the edges incident
|
||
|
on a given node except one, the cut value of the remaining edge is
|
||
|
the sum of the known cut values plus a term dependent only on the
|
||
|
edges incident to the given node."
|
||
|
- Take an edge with a negative cut value and remove it. Find the graph
|
||
|
edge between the remaining head and tail MSTs with the smallest
|
||
|
"slack" (distance in rank between its ends) and add that edge to the
|
||
|
MST to make it connected again.
|
||
|
- Repeat until there are no negative cut values.
|
||
|
- Apparently searching "cyclically" through the negative edges, rather
|
||
|
than iterating from the start each time, is worthwhile.
|
||
|
- Normalize the MST by assigning the root node the rank of 0 (and so on), if
|
||
|
it changed.
|
||
|
- All edges in the MST are of length 1, and the rest can be inferred from
|
||
|
that.
|
||
|
- To reduce crowding, nodes with equal in/out edge weights and which could
|
||
|
be placed on multiple rankings are moved to the ranking with the fewest
|
||
|
nodes.
|
||
|
|
||
|
2) Order nodes in the X axis to reduce edge crossings
|
||
|
3) Compute node coordinates
|
||
|
4) Determine edge splines
|