UNPKG

dc.graph

Version:

Graph visualizations integrated with crossfilter and dc.js

213 lines (192 loc) 6.55 kB
var qs = querystring.parse(); var options = Object.assign({ min: 3, max: 10, algo: 'yoga-layout', log: false }, qs); var vertical = options.rankdir === 'TB'; // intentionally using a dumb identifier scheme: 'top, 'col-a', .. 'a1 .. 'b3' for generality's sake // it would be smarter irl to use key = prefix + addr.join(delimiter) var parentNodes = [ { id: 'top', flexDirection: vertical ? 'column' : 'row', justifyContent: 'space-between', padding: 10 }, { id: 'col-a', flexDirection: vertical ? 'row' : 'column', justifyContent: 'flex-start', alignItems: 'flex-end', flex: 0 }, { id: 'col-b', flexDirection: vertical ? 'row' : 'column', justifyContent: 'flex-start', alignItems: 'flex-start', flex: 0 } ]; var adjectives = ['strong', 'happy', 'libelious', 'rainy', 'effulgent', 'bookish'], nouns = ['steel', 'book', 'cat', 'talk', 'road', 'umbrella', 'nimwit']; function rndsel(a) { return a[Math.floor(Math.random()*a.length)]; } function phrase() { return rndsel(adjectives) + ' ' + rndsel(nouns); } var data = d3.range(Math.round(+options.min + Math.random()*(options.max-options.min))).map(function(i) { return { id: 'a' + i, label: [String.fromCharCode(97+i) + '.', phrase()], flex: 1 }; }).concat(d3.range(Math.round(+options.min + Math.random()*(options.max-options.min))).map(function(i) { return { id: 'b' + i, label: [String.fromCharCode(48+i) + '.', phrase()], flex: 1 }; })); var lbounds = [Math.PI-1, Math.PI+1], rbounds = [-1,1], ubounds = [-Math.PI/2-1, -Math.PI/2+1], dbounds = [Math.PI/2-1, Math.PI/2+1]; var inbounds, outbounds; if(vertical) { inbounds = ubounds; outbounds = dbounds; } else { inbounds = lbounds; outbounds = rbounds; } var ports = data.map(function (n) { return { nodeId: n.id, side: n.id[0] === 'a' ? 'out' : 'in', bounds: n.id[0] === 'a' ? outbounds : inbounds }; }); var node_flat = dc_graph.flat_group.make(parentNodes.concat(data), function (n) { return n.id; }), edge_flat = dc_graph.flat_group.make([], function (e) { return e.id; }), port_flat = dc_graph.flat_group.make(ports, function (p) { return p.nodeId + '/' + p.side; }); var layout = dc_graph.flexbox_layout(null, {algo: options.algo}) .logStuff(qs.log && qs.log !== 'false') .addressToKey(function(ad) { switch(ad.length) { case 0: return 'top'; case 1: return 'col-' + ad[0]; case 2: return ad[0] + ad[1]; default: throw new Error('not expecting more than depth 2'); } }) .keyToAddress(function(key) { if(key==='top') return []; else if(/^col-/.test(key)) return [key.split('col-')[1]]; else if(/^(a|b)/.test(key)) return [key[0], +key.slice(1)]; else throw new Error('couldn\'t parse key: ' + key); }); var matchDiagram = dc_graph.diagram('#graph') .layoutEngine(layout) .width('auto').height('auto') .transitionDuration(250) .layoutUnchanged(true) .mouseZoomable(false) .nodeDimension(node_flat.dimension).nodeGroup(node_flat.group) .edgeDimension(edge_flat.dimension).edgeGroup(edge_flat.group) .portDimension(port_flat.dimension).portGroup(port_flat.group) .nodeShape(function (n) { return layout.keyToAddress()(matchDiagram.nodeKey()(n)).length < 2 ? 'nothing' : 'rounded-rect'; }) .nodeLabelPadding({x: 20, y: 0}) .nodeLabelAlignment(function (n) { return (/^a/.test(n.key) ? 'right' : 'left'); }) .nodeStrokeWidth(0) .nodeTitle(null) .nodeFill('none') .edgesInFront(true) .edgeSourcePortName('out') .edgeTargetPortName('in') .edgeLabel(null) .portNodeKey(function (p) { return p.value.nodeId; }).portName(function (p) { return p.value.side; }).portBounds(function (p) { return p.value.bounds; }) .portElastic(false); matchDiagram.child('validate', dc_graph.validate()); if(options.trouble) matchDiagram.child('troubleshoot', dc_graph.troubleshoot()); matchDiagram.child('place-ports', dc_graph.place_ports()); var circlePorts = dc_graph.symbol_port_style() .portSymbol(null) .displacement(0) .smallRadius(2).mediumRadius(4).largeRadius(6) .outlineFill('white') .outlineStroke('black').outlineStrokeWidth(1); matchDiagram.portStyle('circle-ports', circlePorts) .portStyleName('circle-ports'); var drawGraphs = dc_graph.draw_graphs({ idTag: 'id', sourceTag: 'sourcename', targetTag: 'targetname' }) .usePorts(true) .clickCreatesNodes(false) .edgeCrossfilter(edge_flat.crossfilter); matchDiagram.child('draw-graphs', drawGraphs); var select_edges = dc_graph.select_edges({ edgeStroke: 'lightblue', edgeStrokeWidth: 3 }).multipleSelect(false); matchDiagram.child('select-edges', select_edges); var select_edges_group = dc_graph.select_things_group('select-edges-group', 'select-edges'); var delete_edges = dc_graph.delete_things(select_edges_group, 'delete-edges', 'id') .crossfilterAccessor(function(matchDiagram) { return edge_flat.crossfilter; }) .dimensionAccessor(function(matchDiagram) { return matchDiagram.edgeDimension(); }); matchDiagram.child('delete-edges', delete_edges); var oppositeMatcher = dc_graph.match_opposites(matchDiagram, { edgeStroke: 'orangered' }, { delete_edges: delete_edges }); drawGraphs.conduct(oppositeMatcher); if(qs.selports) { var select_ports = dc_graph.select_ports({ portBackgroundFill: 'lightgreen', outlineStroke: 'orange', outlineStrokeWidth: 2, smallRadius: 5, mediumRadius: 7, largeRadius: 10 }, { portStyle: 'circle-ports' }).multipleSelect(false); matchDiagram.child('select-ports', select_ports); var select_ports_group = dc_graph.select_things_group('select-ports-group', 'select-ports'); select_ports_group.on('set_changed.show-info', function(ports) { if(ports.length>0) { select_edges_group.set_changed([]); } }); } dc.renderAll(); $('#resize').resizable({ resize: function(event, ui) { matchDiagram .redraw(); } });