notes for gim on graph drawing algo, and some TODOs
This commit is contained in:
parent
1dc53518af
commit
c16fc00bf7
16
gg/gg.go
16
gg/gg.go
@ -8,6 +8,22 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TODO it's a bit unfortunate that it's possible to create disjointed graphs
|
||||
// within the same graph instance. That's not really something that would be
|
||||
// possible with any other type of datastructure. I think all that would be
|
||||
// needed to get rid of this is to remove the Null instance and instead do a
|
||||
// New(value) function. This would allow a graph to be just a single value with
|
||||
// no edges, but I _think_ that's fine?
|
||||
//
|
||||
// Actually, that's kinda bogus, it really messes with how I conceptualized
|
||||
// ginger being used. Which is maybe fine. I should do some research on the
|
||||
// typical way that graphs and other structures are defined.
|
||||
//
|
||||
// It seems that disjoint unions, as they're called, are an accepted thing... if
|
||||
// I need it for gim a Disjoin method might be the best bet, which would walk
|
||||
// through the Graph and compute each disjointed Graph and return them
|
||||
// individually.
|
||||
|
||||
// Value wraps a go value in a way such that it will be uniquely identified
|
||||
// within any Graph and between Graphs. Use NewValue to create a Value instance.
|
||||
// You can create an instance manually as long as ID is globally unique.
|
||||
|
71
gim/NOTES
Normal file
71
gim/NOTES
Normal file
@ -0,0 +1,71 @@
|
||||
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
|
@ -16,10 +16,8 @@ import (
|
||||
// - Changing the "flow" direction
|
||||
// - Absolute positioning of some/all vertices
|
||||
|
||||
// TODO
|
||||
// - edge values
|
||||
// - be able to draw circular graphs
|
||||
// - audit all steps, make sure everything is deterministic
|
||||
// TODO be able to draw circular graphs
|
||||
// TODO audit all steps, make sure everything is deterministic
|
||||
|
||||
const (
|
||||
framerate = 10
|
||||
|
Loading…
Reference in New Issue
Block a user