build-layer-graph.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. import _ from 'lodash'
  2. import { Graph } from 'graphlibrary'
  3. /*
  4. * Constructs a graph that can be used to sort a layer of nodes. The graph will
  5. * contain all base and subgraph nodes from the request layer in their original
  6. * hierarchy and any edges that are incident on these nodes and are of the type
  7. * requested by the "relationship" parameter.
  8. *
  9. * Nodes from the requested rank that do not have parents are assigned a root
  10. * node in the output graph, which is set in the root graph attribute. This
  11. * makes it easy to walk the hierarchy of movable nodes during ordering.
  12. *
  13. * Pre-conditions:
  14. *
  15. * 1. Input graph is a DAG
  16. * 2. Base nodes in the input graph have a rank attribute
  17. * 3. Subgraph nodes in the input graph has minRank and maxRank attributes
  18. * 4. Edges have an assigned weight
  19. *
  20. * Post-conditions:
  21. *
  22. * 1. Output graph has all nodes in the movable rank with preserved
  23. * hierarchy.
  24. * 2. Root nodes in the movable layer are made children of the node
  25. * indicated by the root attribute of the graph.
  26. * 3. Non-movable nodes incident on movable nodes, selected by the
  27. * relationship parameter, are included in the graph (without hierarchy).
  28. * 4. Edges incident on movable nodes, selected by the relationship
  29. * parameter, are added to the output graph.
  30. * 5. The weights for copied edges are aggregated as need, since the output
  31. * graph is not a multi-graph.
  32. */
  33. function buildLayerGraph (g, rank, relationship) {
  34. const root = createRootNode(g)
  35. const result = new Graph({ compound: true }).setGraph({ root: root })
  36. .setDefaultNodeLabel(function (v) { return g.node(v) })
  37. _.forEach(g.nodes(), function (v) {
  38. const node = g.node(v)
  39. const parent = g.parent(v)
  40. if (node.rank === rank || (node.minRank <= rank && rank <= node.maxRank)) {
  41. result.setNode(v)
  42. result.setParent(v, parent || root)
  43. // This assumes we have only short edges!
  44. _.forEach(g[relationship](v), function (e) {
  45. const u = e.v === v ? e.w : e.v
  46. const edge = result.edge(u, v)
  47. const weight = !_.isUndefined(edge) ? edge.weight : 0
  48. result.setEdge(u, v, { weight: g.edge(e).weight + weight })
  49. })
  50. if (_.has(node, 'minRank')) {
  51. result.setNode(v, {
  52. borderLeft: node.borderLeft[rank],
  53. borderRight: node.borderRight[rank]
  54. })
  55. }
  56. }
  57. })
  58. return result
  59. }
  60. function createRootNode (g) {
  61. let v
  62. while (g.hasNode((v = _.uniqueId('_root'))));
  63. return v
  64. }
  65. export default buildLayerGraph