#Foci layout Example of d3.layout.foci plugin.
Last active
February 23, 2016 14:50
-
-
Save christophe-g/23616ee1a7125032d557 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
! function() { | |
function t(t) { | |
return function(e, i) { | |
e = d3.hsl(e), i = d3.hsl(i); | |
var r = (e.h + 120) * a, | |
h = (i.h + 120) * a - r, | |
s = e.s, | |
l = i.s - s, | |
o = e.l, | |
u = i.l - o; | |
return isNaN(l) && (l = 0, s = isNaN(s) ? i.s : s), isNaN(h) && (h = 0, r = isNaN(r) ? i.h : r), | |
function(a) { | |
var e = r + h * a, | |
i = Math.pow(o + u * a, t), | |
c = (s + l * a) * i * (1 - i); | |
return "#" + n(i + c * (-.14861 * Math.cos(e) + 1.78277 * Math.sin(e))) + n(i + c * (-.29227 * Math.cos(e) - .90649 * Math.sin(e))) + n(i + c * 1.97294 * Math.cos(e)) | |
} | |
} | |
} | |
function n(t) { | |
var n = (t = 0 >= t ? 0 : t >= 1 ? 255 : 0 | 255 * t).toString(16); | |
return 16 > t ? "0" + n : n | |
} | |
var a = Math.PI / 180; | |
d3.scale.cubehelix = function() { | |
return d3.scale.linear().range([d3.hsl(300, .5, 0), d3.hsl(-240, .5, 1)]).interpolate(d3.interpolateCubehelix) | |
}, d3.interpolateCubehelix = t(1), d3.interpolateCubehelix.gamma = t | |
}(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function (global, factory) { | |
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | |
typeof define === 'function' && define.amd ? define('d3-foci', ['exports'], factory) : | |
factory((global.d3_foci = {})); | |
}(this, function (exports) { 'use strict'; | |
/** | |
* getSet creates a getter/setter function for a re-usable D3.js component. | |
* | |
* @method getSet | |
* @param {string} option - the name of the object in the string you want agetter/setter for. | |
* @param {function} component - the D3 component this getter/setter relates to. | |
* | |
* @return {mixed} The value of the option or the component. | |
*/ | |
function getSet(option, component) { | |
return function(_) { | |
if (! arguments.length) { | |
return this[option]; | |
} | |
this[option] = _; | |
return component; | |
}; | |
} | |
function applier(component, options) { | |
for (var key in options) { | |
if(component[key] && (typeof component[key] == "function")) { | |
component[key](options[key]); | |
} | |
} | |
return component; | |
} | |
function binder(component, options) { | |
for (var key in options) { | |
if(!component[key]) { | |
component[key] = getSet(key, component).bind(options); | |
} | |
} | |
} | |
// apply a d3.fore layout with foci on venn area center to set foci | |
// d3.layout.venn.packCircles looks prettier. | |
function force(layout, data, links) { | |
var force = layout.packer() | |
if (!force) { | |
force = d3.layout.force(); | |
binder(force, { | |
padding: 3, | |
maxRadius: 8, | |
collider: true, | |
ticker: null, | |
ender : null, | |
starter : null | |
}); | |
} | |
var packingConfig = layout.packingConfig(), | |
size = layout.size(), | |
sets = layout.sets(), | |
padding = force.padding(), // separation between nodes | |
maxRadius = force.maxRadius(), | |
collider = force.collider; | |
// foci = d3.map({}, function(d) { | |
// return d.__key__ | |
// }); | |
// layout.sets().forEach(function(set) { | |
// foci.set(set.__key__, set.center); | |
// }) | |
applier(force, packingConfig) | |
.nodes(data) | |
.links(links || []) | |
.gravity(0) | |
.charge(0) | |
.size(size) | |
.on('start.__packer__', init) | |
.on('tick.__packer__', tick) | |
var ender ; | |
if(ender = force.ender()) { | |
force.on('end.__packer__', ender) | |
} | |
function init(e) { | |
// if(layout.__ended__) { | |
data.forEach(function(d) { | |
var center = sets.get(d.__setKey__); | |
center = center.center || center; | |
d.x = d.x ? d.x * 1 : center.x; | |
d.y = d.y ? d.y * 1 : center.y; | |
}) | |
var starter ; | |
if(starter = force.starter()) { | |
starter(layout) | |
} | |
} | |
function tick(e) { | |
var ticker; | |
data | |
.forEach(gravity(.2 * e.alpha)) | |
if (collider) { | |
data | |
.forEach(collide(.5)) | |
} | |
if (ticker = force.ticker()) { | |
ticker(layout) | |
} | |
} | |
// Move nodes toward cluster focus. | |
function gravity(alpha) { | |
return function(d) { | |
var center = sets.get(d.__setKey__); | |
center = center.center || center; | |
d.y += (center.y - d.y) * alpha; | |
d.x += (center.x - d.x) * alpha; | |
}; | |
} | |
// Resolve collisions between nodes. | |
function collide(alpha) { | |
var quadtree = d3.geom.quadtree(data); | |
return function(d) { | |
var r = d.r + maxRadius + padding, | |
nx1 = d.x - r, | |
nx2 = d.x + r, | |
ny1 = d.y - r, | |
ny2 = d.y + r; | |
quadtree.visit(function(quad, x1, y1, x2, y2) { | |
if (quad.point && (quad.point !== d)) { | |
var x = d.x - quad.point.x, | |
y = d.y - quad.point.y, | |
l = Math.sqrt(x * x + y * y), | |
r = d.r + quad.point.r + (d.__setKey__ !== quad.point.__setKey__) * padding; | |
if (l < r) { | |
l = (l - r) / l * alpha; | |
d.x -= x *= l; | |
d.y -= y *= l; | |
quad.point.x += x; | |
quad.point.y += y; | |
} | |
} | |
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; | |
}); | |
}; | |
} | |
return force; | |
} | |
function foci() { | |
// d3.layout.foci = function() { | |
var opts = { | |
sets: null, | |
links: [], | |
chargeFactor : -70, | |
iterationLength: 120, | |
setsAccessor: setsAccessorFn, | |
setsSize: setsSize, | |
packingStragegy: force, | |
packingConfig: { | |
value: valueFn, | |
}, | |
fociConfig: { | |
linkStrength: 0.01, | |
gravity: 0.1, | |
}, | |
size: [1, 1] | |
}; | |
var event = d3.dispatch("start", "tick", "end"); | |
// fociEvent = d3.dispatch("tick"); | |
var packer, | |
centers , | |
nodeLinks, | |
nodes; | |
// var circles, | |
// nodes, | |
// centres; | |
var fociForce = d3.layout.force().charge(function(d){ | |
return opts.chargeFactor * d.size | |
}), | |
// var fociForce = d3.layout.force(), | |
fociLinks; | |
// Build simple getter and setter Functions | |
binder(foci, opts); | |
//The layout function | |
function foci(data) { | |
if (!arguments.length) return nodes; | |
nodes = compute(data); | |
return foci; | |
} | |
function runForce() { | |
event.start({type: "start"}); | |
var n = foci.iterationLength(), | |
sets = foci.sets(); | |
packer.stop(); | |
//run the simulation n times | |
fociForce.start(); | |
for (var i = n * n; i > 0; --i) fociForce.tick(); | |
fociForce.stop(); | |
sets.forEach(function(k, set) { | |
centers.push(set.center = { | |
x: set.x, | |
y: set.y | |
}) | |
}); | |
packer.start(); | |
event.end({type: "end"}); | |
} | |
function compute(data) { | |
var sets, | |
setsValues, | |
// layout = foci.layoutFunction(), | |
packingStragegy = foci.packingStragegy(); | |
// foci.__ended__ = false; | |
centers = []; | |
nodeLinks = []; | |
fociLinks = []; | |
sets = extractSets(data); | |
setsValues = sets.values(); | |
applier(fociForce, opts.fociConfig); | |
fociForce | |
.size(foci.size()) | |
.nodes(setsValues) | |
.links(fociLinks) | |
packer = packingStragegy(foci, data, foci.links()); | |
// Use a timeout to allow the rest of the page to load first. | |
setTimeout(function() { | |
// Run the layout a fixed number of times. | |
// The ideal number of times scales with graph complexity. | |
// Of course, don't run too long—you'll hang the page! | |
runForce(); | |
}, 10); | |
// solution = layout(setsValues); | |
console.info("data: ", data) | |
console.info("sets: ", sets) | |
console.info("links: ", fociLinks) | |
return data | |
} | |
// loop over data and build the set so that they comply with https://github.com/benfred/foci.js | |
/* | |
from data = [ | |
{"set":["A"],"name":"node_0"}, | |
{"set":["B"],"name":"node_1"}, | |
{"set":["B","A"],"name":"node_2"} | |
{"set":["B","A"],"name":"node_3"} | |
] | |
to sets = [ | |
{sets: ['A'], size: 1, nodes : ['node_0'], __key__: 'A'}, | |
{sets: ['B'], size: 1, nodes : ['node_1'],__key__: 'A'}, | |
{sets: ['A','B'], size: 2, nodes ['node_2', 'node_3'], __key__: 'A,B'} | |
]; | |
links : [ | |
{source: 'A', target: 'A,B', weight : 1} | |
{source: 'B', target: 'A,B', weight : 1} | |
] | |
*/ | |
function extractSets(data) { | |
var oldSets = foci.sets() , | |
sets = d3.map({}, function(d) { | |
return d.__key__ | |
}), | |
individualSets = d3.map(), | |
accessor = foci.setsAccessor(), | |
size = foci.setsSize(), | |
set, | |
s, | |
key, | |
i, | |
n = data.length; | |
//reset fociLink s | |
for (i = -1; ++i < n;) { | |
set = accessor(data[i]); | |
if (set.length) { | |
key = set.sort().join(','); //so taht we have the same key as in https://github.com/benfred/foci.js | |
set.forEach(function(val) { | |
if (s = individualSets.get(val)) { | |
s.size += 1; | |
// s.nodes.push([data[i]]); | |
} else { | |
individualSets.set(val, { | |
__key__: val, | |
size: 1, | |
sets: [val], | |
nodes: [] | |
// nodes: [data[i]] | |
}) | |
} | |
}); | |
data[i].__setKey__ = key; | |
if (s = sets.get(key)) { | |
s.size++; | |
s.nodes.push(data[i]); | |
} else { | |
sets.set(key, { | |
__key__: key, | |
sets: set, | |
size: 1, | |
nodes: [data[i]] | |
}); | |
} | |
} | |
} | |
individualSets.forEach(function(k, v) { | |
if (!sets.get(k)) { | |
sets.set(k, v); | |
} | |
}); | |
// reset the size for each set. | |
sets.forEach(function(k, v) { | |
v.size = size(v.size); | |
if ((n = v.sets.length) && n != 1) { | |
for (i = -1; ++i < n;) { | |
fociLinks.push({ | |
source: sets.get(v.sets[i]), | |
target: sets.get(k) | |
}) | |
} | |
} | |
if(oldSets && (set = oldSets.get(k))){ | |
v.center = set.center; | |
v.x = set.x; | |
v.y = set.y; | |
} | |
v.nodes.forEach(function(n){ | |
i = v.sets.length; | |
v.sets.forEach(function(v){ | |
nodeLinks.push({source: sets.get(v), target: n, size: i}); | |
}) | |
}) | |
}) | |
// sets = sets.values(); | |
foci.sets(sets); | |
return sets; | |
} | |
function setsSize(size) { | |
return size; | |
} | |
// data accessors | |
function setsAccessorFn(d) { | |
return d.set || []; | |
} | |
function valueFn(d) { | |
return d.value; | |
} | |
foci.packingConfig = function(_) { | |
var config = opts.packingConfig; | |
if (!arguments.length) { | |
return config; | |
} | |
for (var k in _) { | |
config[k] = _[k] | |
} | |
if (packer) { | |
applier(packer, _) | |
} | |
return foci; | |
}; | |
foci.fociConfig = function(_) { | |
var config = opts.fociConfig; | |
if (!arguments.length) { | |
return config; | |
} | |
for (var k in _) { | |
config[k] = _[k] | |
} | |
if (packer) { | |
applier(fociForce, _) | |
} | |
return foci; | |
}; | |
foci.force = function() { | |
return fociForce; | |
} | |
foci.packer = function() { | |
return packer; | |
} | |
foci.fociLinks = function() { | |
return fociLinks; | |
} | |
foci.centers = function() { | |
return centers; | |
} | |
foci.nodeLinks = function() { | |
return nodeLinks; | |
} | |
foci.start = function() { | |
runForce(); | |
// fociForce.start() | |
} | |
foci.nodes = foci; | |
// return foci; | |
// d3.rebind(fociForce, endFoci, 'on'); | |
return d3.rebind(foci, event, "on"); | |
}; | |
var version = "0.0.1"; | |
exports.version = version; | |
exports.foci = foci; | |
}));(function(target, export_name, name) { | |
if (target) { | |
target[name] = this[export_name] && this[export_name][name]; | |
for (var k in this[export_name]) { | |
if (k != name) { | |
target[name][k] = this[export_name][k]; | |
} | |
} | |
delete this[export_name]; | |
} | |
}(this.d3 && this.d3.layout, 'd3_foci', 'foci')); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<title>Venn Diagram - pack layout</title> | |
<script src="//d3js.org/d3.v3.min.js"></script> | |
<!-- <script src="https://raw.githubusercontent.com/christophe-g/vennLayout/master/vennLayout.js"></script> --> | |
<!-- <script type="text/javascript" src="../d3.js"></script> --> | |
<script type="text/javascript" src="cubehelix.js"></script> | |
<script type="text/javascript" src="d3-foci.js"></script> | |
<style> | |
#venn { | |
margin-left: 250px; | |
margin-top: -200px; | |
} | |
</style> | |
</head> | |
<body> | |
<form> | |
<div id="inputs"> | |
<p> | |
<label for="dataLength">Number of Nodes</label> | |
<input type="number" min="10" step="10" max="600" name="dataLength" id="dataLength" value="" /> | |
</p> | |
<p> | |
<label for="setLength">Number of Circles</label> | |
<input min="2" max="8" type="number" name="setLength" id="setLength" value="" /> | |
</p> | |
<p> | |
<label for="circleOpacity">opacity for Circle</label> | |
<input min="0.1" max="1" step ="0.1" type="range" name="circleOpacity" id="circleOpacity" value="" /> | |
</p> | |
</div> | |
<div id="force"> | |
<p> | |
<label for="linkDistance">link distance</label> | |
<input type="number" name="linkDistance" id="linkDistance" value="" /> | |
</p> | |
<p> | |
<label for="linkStrength">link strength</label> | |
<input type="number" max="1" min="0" step="0.1" name="linkStrength" id="linkStrength" value="" /> | |
</p> | |
<!-- <p> | |
<label for="charge">charge</label> | |
<input type="number" name="charge" id="charge" value="" /> | |
</p> --> | |
<p> | |
<label for="gravity">gravity</label> | |
<input type="number" max="1" min="0" step="0.1" name="gravity" id="gravity" value="" /> | |
</p> | |
<p> | |
<label for="theta">theta</label> | |
<input type="number" name="theta" id="theta" value="" /> | |
</p> | |
</div> | |
</form> | |
<svg id="venn"></svg> | |
<script type="text/javascript" src="script.js"></script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function test() { | |
var width = 600, | |
height = 600, | |
colors = d3.scale.cubehelix() | |
.domain([0, .5, 1]) | |
.range([ | |
d3.hsl(-100, 0.75, 0.35), | |
d3.hsl(80, 1.50, 0.80), | |
d3.hsl(260, 0.75, 0.35) | |
]); | |
var setChar = 'ABCDEFGHIJKLMN', | |
charFn = i => setChar[i], | |
setLength = 4, | |
sets = d3.range(setLength).map(function(d, i) { | |
return setChar[i] | |
}) | |
var opts = { | |
dataLength: 120, | |
setLength: setLength, | |
duration: 800, | |
circleOpacity: 0.4, | |
innerOpacity: 0.2 | |
}; | |
var forceOpts = { | |
linkDistance: 10, | |
linkStrength: 2, | |
// charge: -30, | |
gravity: 0.1, | |
theta: 1 | |
} | |
function resetColorScale() { | |
var l = layout.sets().values().length; | |
colors = colors.domain([0, l / 2, l]) | |
} | |
// Build simple getter and setter Functions | |
for (var key in opts) { | |
test[key] = getSet(key, test).bind(opts); | |
} | |
for (var key in forceOpts) { | |
test[key] = getSet(key, test).bind(forceOpts); | |
} | |
function getSet(option, component) { | |
return function(_) { | |
if (!arguments.length) { | |
return this[option]; | |
} | |
this[option] = _; | |
return component; | |
}; | |
} | |
function refreshInput() { | |
var sel = d3.select(this), | |
name = sel.attr("name"), | |
value = sel.property("value") | |
test[name](value); | |
if (name == 'dataLength' || name == 'setLength') { | |
if (name == 'setLength') { | |
globalData = [] // we reshuffle everything | |
} | |
return refresh(generateData()) | |
} | |
refresh(); | |
} | |
function refreshForce() { | |
var sel = d3.select(this), | |
name = sel.attr("name"), | |
value = sel.property("value") | |
test[name](value); | |
if (layout) { | |
var force = layout.force(); | |
if (force[name]) { | |
force[name](value); | |
layout.start(); | |
// force.start().on('end.force', function() { | |
// refresh() | |
// }) | |
// refresh(); | |
} | |
} | |
} | |
function binder(obj) { | |
return function() { | |
var sel = d3.select(this), | |
name = sel.attr("name"); | |
if (obj[name]) { | |
sel.property("value", obj[name]()) | |
} | |
} | |
} | |
//set input value accorging to options and handle change of input | |
d3.selectAll('#inputs input') | |
.each(binder(test)) | |
.on('input', refreshInput) | |
d3.selectAll('#force input') | |
.each(binder(test)) | |
.on('input', refreshForce) | |
var layout = d3.layout.foci() | |
.size([width, height]) | |
var svg = d3.select('svg') | |
.attr('width', width) | |
.attr('height', height), | |
isFirstLayout = true; | |
var linkNodeContainer = svg.append("g") | |
.attr("class", "link-node-container") | |
var svgDefs = svg.append("defs") | |
var globalData = [], | |
generator = 0; | |
function generateData() { | |
var dataLength = test.dataLength(), | |
setLength = test.setLength(), | |
diff = dataLength - globalData.length; | |
if (diff > 0) { | |
globalData = globalData.concat(d3.range(diff).map((d, i) => { | |
var l = Math.floor((Math.random() * setLength / 2) + 1), | |
set = [], | |
c, | |
i; | |
for (i = -1; ++i < l;) { | |
c = charFn(Math.floor((Math.random() * setLength))); | |
if (set.indexOf(c) == -1) { | |
set.push(c) | |
} | |
} | |
return { | |
set: set, | |
r: 8, | |
name: 'node_' + generator++ | |
} | |
})) | |
} else { | |
globalData.splice(0, -diff); | |
} | |
return globalData; | |
} | |
function refresh(data) { | |
if (data) { | |
// we recalculate the layout for new data only | |
layout.nodes(data) | |
resetColorScale(); | |
} | |
var setData = layout.sets().values(); | |
var vennArea = svg.selectAll("g.venn-area") | |
.data(setData, function(d) { | |
return d.__key__; | |
}); | |
var vennEnter = vennArea.enter() | |
.append('g') | |
.attr("class", function(d) { | |
return "venn-area venn-" + | |
(d.sets.length == 1 ? "circle" : "intersection"); | |
}) | |
.attr('fill', function(d, i) { | |
return colors(i) | |
}) | |
vennEnter.append('circle') | |
.attr('class', 'venn-area-path') | |
.attr('r', 25) | |
// .call(layout.force().drag) | |
vennEnter.append('circle') | |
.attr('class', 'inner') | |
.attr('fill', 'grey'); | |
vennEnter.append("text") | |
.attr("class", "label") | |
.attr("text-anchor", "middle") | |
.attr("dy", ".35em") | |
var link = linkNodeContainer.selectAll('.link') | |
.data(layout.fociLinks()); | |
link.enter().append('line') | |
.attr('class', 'link') | |
// .attr('stroke', "#AAA") | |
.attr('stroke', function(d) { | |
return "url(#" + d.source.__key__ + '__' + d.target.__key__ + ")" | |
}) | |
.attr('stroke-dasharray', "10,10") | |
.attr("stroke-width", 5); | |
link.exit().remove() | |
var linkNode = linkNodeContainer.selectAll('.link-node') | |
.data(layout.nodeLinks()); | |
linkNode.enter().append('line') | |
.attr('class', 'link-node') | |
.attr('stroke', "#bbb") | |
.attr("stroke-width", 1); | |
linkNode.exit().remove() | |
var vennAreaLabel = vennArea.selectAll("text.label").data(function(d) { | |
return [d]; | |
}).text(function(d) { | |
return d.__key__; | |
}) | |
var vennAreaCircle = vennArea.selectAll('circle.venn-area-path').data(function(d) { | |
return [d]; | |
}).text(function(d) { | |
return d.__key__; | |
}) | |
.attr('opacity', test.circleOpacity()) | |
layout.on('end', function() { | |
vennAreaLabel | |
.attr("x", function(d) { | |
// return d.x; | |
return d.center.x | |
}) | |
.attr("y", function(d) { | |
// return d.y | |
return d.center.y | |
}); | |
vennAreaCircle.attr("cx", function(d) { | |
// return d.x; | |
return d.center.x | |
}) | |
.attr("cy", function(d) { | |
// return d.y; | |
return d.center.y | |
}); | |
link | |
.attr('x1', function(d) { | |
return d.source.x; | |
}) | |
.attr('y1', function(d) { | |
return d.source.y; | |
}) | |
.attr('x2', function(d) { | |
return d.target.x; | |
}) | |
.attr('y2', function(d) { | |
return d.target.y; | |
}); | |
svgDefs.selectAll('.grad').remove() | |
var grad = svgDefs.selectAll('.grad') | |
.data(layout.fociLinks()); | |
var gradEnter = grad.enter() | |
.append('linearGradient') | |
.attr({ | |
id: function(d) { | |
return d.source.__key__ + '__' + d.target.__key__ | |
}, | |
x1: '0%', | |
x1: '0%', | |
x2: '100%', | |
y2: '0%', | |
}) | |
gradEnter.append('stop') | |
.attr({ | |
offset: '0%', | |
'stop-color': function(d) { | |
return colors(setData.indexOf(d.source.x < d.target.x ? d.source : d.target)) | |
} | |
}) | |
gradEnter.append('stop') | |
.attr({ | |
offset: '100%', | |
'stop-color': function(d) { | |
return colors(setData.indexOf(d.source.x <= d.target.x ? d.target : d.source)) | |
// return colors(setData.indexOf(d.target)) | |
} | |
}) | |
}) | |
vennArea.exit().transition() | |
.duration(test.duration()) | |
// .attrTween('d', function(d) { | |
// return d.d | |
// }) | |
.remove() | |
// need this so that nodes always on top | |
var circleContainer = svg.selectAll("g.venn-circle-container") | |
.data(layout.sets().values(), function(d) { | |
return d.__key__; | |
}); | |
circleContainer.enter() | |
.append('g') | |
.attr("class", "venn-circle-container") | |
.attr('fill', function(d, i) { | |
return colors(i) | |
}); | |
circleContainer.exit().remove(); | |
// need this so that nodes always on top | |
var circleContainer = svg.selectAll("g.venn-circle-container") | |
.data(layout.sets().values(), function(d) { | |
return d.__key__; | |
}); | |
circleContainer.enter() | |
.append('g') | |
.attr("class", "venn-circle-container") | |
.attr('fill', function(d, i) { | |
return colors(i) | |
}); | |
circleContainer.exit().remove(); | |
var points = circleContainer.selectAll("circle.node") | |
.data(function(d) { | |
return d.nodes | |
}, function(d) { | |
return d.name | |
}) | |
var pointsEnter = points.enter() | |
.append('circle') | |
.attr('r', 0) | |
.attr('class', 'node') | |
.call(layout.packer().drag) | |
points.transition() | |
.duration(isFirstLayout ? 0 : test.duration()) | |
.attr('r', function(d) { | |
return d.r | |
}) | |
points.exit().transition() | |
.attr('r', 0) | |
.remove() | |
isFirstLayout = false; | |
// var foci = layout.force() | |
// foci.nodes() | |
//set the force ticker | |
layout.packingConfig({ | |
ticker: function() { | |
points.attr("cx", function(d) { | |
return d.x | |
}) | |
.attr("cy", function(d) { | |
return d.y | |
}) | |
linkNode | |
.attr('x1', function(d) { | |
return d.source.x; | |
}) | |
.attr('y1', function(d) { | |
return d.source.y; | |
}) | |
.attr('x2', function(d) { | |
return d.target.x; | |
}) | |
.attr('y2', function(d) { | |
return d.target.y; | |
}); | |
} | |
}) | |
//start the force layout | |
// var packer = layout.packer(); | |
// packer.on('tick', function() { | |
// points.attr("cx", function(d) { | |
// return d.x | |
// }) | |
// .attr("cy", function(d) { | |
// return d.y | |
// }) | |
// }).start() | |
// layout.start() | |
return test | |
} | |
return refresh(generateData()) | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment