aboutsummaryrefslogtreecommitdiffstats
path: root/html/js/git-deps-layout.coffee
diff options
context:
space:
mode:
authorAdam Spiers <git@adamspiers.org>2015-01-11 13:08:35 +0000
committerAdam Spiers <git@adamspiers.org>2015-01-11 13:25:46 +0000
commit9ac7713b5037710f2b1db82a63343e2f20ae775c (patch)
treed48473735eab3fd2b60ff8adee094bff2a5fb57a /html/js/git-deps-layout.coffee
parent1caef36fdeae554d5cc8bef3ca2734755bd70f08 (diff)
downloadgit-deps-9ac7713b5037710f2b1db82a63343e2f20ae775c.tar.gz
switch to CoffeeScript!
Diffstat (limited to 'html/js/git-deps-layout.coffee')
-rw-r--r--html/js/git-deps-layout.coffee116
1 files changed, 116 insertions, 0 deletions
diff --git a/html/js/git-deps-layout.coffee b/html/js/git-deps-layout.coffee
new file mode 100644
index 0000000..8e88dde
--- /dev/null
+++ b/html/js/git-deps-layout.coffee
@@ -0,0 +1,116 @@
+$ = require "jquery"
+dagre = require "dagre"
+
+gdd = require "./git-deps-data.coffee"
+
+# The list of constraints to feed into WebCola.
+constraints = []
+
+# Group nodes by row, as assigned by the y coordinates returned from
+# dagre's layout(). This will map a y coordinate onto all nodes
+# within that row.
+row_groups = {}
+
+# Expose a container for externally accessible objects. We can't
+# directly expose the objects themselves because the references
+# change each time they're constructed. However we don't need this
+# trick for the constraints arrays since we can easily empty that by
+# setting length to 0.
+externs = {}
+
+dagre_layout = ->
+ g = new dagre.graphlib.Graph()
+ externs.graph = g
+
+ # Set an object for the graph label
+ g.setGraph {}
+
+ # Default to assigning a new object as a label for each new edge.
+ g.setDefaultEdgeLabel -> {}
+
+ $.each gdd.nodes, (i, node) ->
+ g.setNode node.sha1,
+ label: node.name
+ width: node.rect_width or 70
+ height: node.rect_height or 30
+
+ $.each gdd.deps, (parent_sha1, children) ->
+ $.each children, (child_sha1, bool) ->
+ g.setEdge parent_sha1, child_sha1
+
+ dagre.layout g
+ return g
+
+dagre_row_groups = ->
+ g = dagre_layout()
+ row_groups = {}
+ externs.row_groups = row_groups
+ g.nodes().forEach (sha1) ->
+ x = g.node(sha1).x
+ y = g.node(sha1).y
+ row_groups[y] = [] unless y of row_groups
+ row_groups[y].push
+ sha1: sha1
+ x: x
+ return row_groups
+
+build_constraints = ->
+ row_groups = dagre_row_groups()
+
+ constraints.length = 0 # FIXME: only rebuild constraints which changed
+
+ # We want alignment constraints between all nodes which dagre
+ # assigned the same y value.
+ for y of row_groups
+ row_nodes = row_groups[y]
+
+ # No point having an alignment group with only one node in.
+ if row_nodes.length > 1
+ constraints.push build_alignment_constraint(row_nodes)
+
+ # We also need separation constraints ensuring that the
+ # top-to-bottom ordering assigned by dagre is preserved. Since
+ # all nodes within a single row are already constrained to the
+ # same y coordinate from above, it should be enough to only
+ # have separation between a single node in adjacent rows.
+ row_y_coords = Object.keys(row_groups).sort()
+
+ i = 0
+ while i < row_y_coords.length - 1
+ upper_y = row_y_coords[i]
+ lower_y = row_y_coords[i + 1]
+ upper_node = row_groups[upper_y][0]
+ lower_node = row_groups[lower_y][0]
+ constraints.push
+ gap: 30
+ axis: "y"
+ left: gdd.node_index[upper_node.sha1]
+ right: gdd.node_index[lower_node.sha1]
+
+ i++
+
+build_alignment_constraint = (row_nodes) ->
+ constraint =
+ axis: "y"
+ type: "alignment"
+ offsets: []
+
+ for i of row_nodes
+ node = row_nodes[i]
+ constraint.offsets.push
+ node: gdd.node_index[node.sha1]
+ offset: 0
+
+ return constraint
+
+node = (sha1) ->
+ externs.graph.node sha1
+
+module.exports =
+ # Variables
+ constraints: constraints
+ g: externs
+
+ # Functions
+ build_constraints: build_constraints
+ node: node