Example of a force-directed graph with an extra layer for grouping nodes.
Nodes and groups can be dragged indepedently. The type of curve and the scale of the shape can be tuned in order to define the contour of the group shapes
license: gpl-3.0 | |
height: 600 |
<!DOCTYPE html> | |
<meta charset='utf-8'> | |
<style> | |
body { | |
font-family: sans-serif, Arial; | |
font-size: 12px; | |
font-weight: bold; | |
} | |
.links line { | |
stroke: #999; | |
stroke-opacity: 0.6; | |
} | |
.nodes circle { | |
stroke: #fff; | |
stroke-width: 1.5px; | |
} | |
path { | |
fill-opacity: .1; | |
stroke-opacity: 1; | |
} | |
</style> | |
<div id="scaleFactorSettings"> | |
<p>Scale of the groups: <span id='scaleFactorLabel'>1.2</span></p> | |
<input type="range" min="1" max="3" value="1.2" step=".1" | |
oninput="scaleFactor = value; d3.select('#scaleFactorLabel').text(scaleFactor); updateGroups()"> | |
</div> | |
<div id="curveSettings"> | |
<p>Type of curve: <span id='curveLabel'>curveCatmullRomClosed</span></p> | |
</div> | |
<svg width='960' height='600'></svg> | |
<script src='https://d3js.org/d3.v4.min.js'></script> | |
<script> | |
var svg = d3.select('svg'), | |
width = +svg.attr('width'), | |
height = +svg.attr('height'), | |
color = d3.scaleOrdinal(d3.schemeCategory20), | |
valueline = d3.line() | |
.x(function(d) { return d[0]; }) | |
.y(function(d) { return d[1]; }) | |
.curve(d3.curveCatmullRomClosed), | |
paths, | |
groups, | |
groupIds, | |
scaleFactor = 1.2, | |
polygon, | |
centroid, | |
node, | |
link, | |
curveTypes = ['curveBasisClosed', 'curveCardinalClosed', 'curveCatmullRomClosed', 'curveLinearClosed'], | |
simulation = d3.forceSimulation() | |
.force('link', d3.forceLink().id(function(d) { return d.id; })) | |
.force('charge', d3.forceManyBody()) | |
.force('center', d3.forceCenter(width / 2, height / 2)); | |
d3.json('miserables.json', function(error, graph) { | |
if (error) throw error; | |
// create selector for curve types | |
var select = d3.select('#curveSettings') | |
.append('select') | |
.attr('class','select') | |
.on('change', function() { | |
var val = d3.select('select').property('value'); | |
d3.select('#curveLabel').text(val); | |
valueline.curve(d3[val]); | |
updateGroups(); | |
}); | |
var options = select | |
.selectAll('option') | |
.data(curveTypes).enter() | |
.append('option') | |
.text(function (d) { return d; }); | |
// create groups, links and nodes | |
groups = svg.append('g').attr('class', 'groups'); | |
link = svg.append('g') | |
.attr('class', 'links') | |
.selectAll('line') | |
.data(graph.links) | |
.enter().append('line') | |
.attr('stroke-width', function(d) { return Math.sqrt(d.value); }); | |
node = svg.append('g') | |
.attr('class', 'nodes') | |
.selectAll('circle') | |
.data(graph.nodes) | |
.enter().append('circle') | |
.attr('r', 5) | |
.attr('fill', function(d) { return color(d.group); }) | |
.call(d3.drag() | |
.on('start', dragstarted) | |
.on('drag', dragged) | |
.on('end', dragended)); | |
// count members of each group. Groups with less | |
// than 3 member will not be considered (creating | |
// a convex hull need 3 points at least) | |
groupIds = d3.set(graph.nodes.map(function(n) { return +n.group; })) | |
.values() | |
.map( function(groupId) { | |
return { | |
groupId : groupId, | |
count : graph.nodes.filter(function(n) { return +n.group == groupId; }).length | |
}; | |
}) | |
.filter( function(group) { return group.count > 2;}) | |
.map( function(group) { return group.groupId; }); | |
paths = groups.selectAll('.path_placeholder') | |
.data(groupIds, function(d) { return +d; }) | |
.enter() | |
.append('g') | |
.attr('class', 'path_placeholder') | |
.append('path') | |
.attr('stroke', function(d) { return color(d); }) | |
.attr('fill', function(d) { return color(d); }) | |
.attr('opacity', 0); | |
paths | |
.transition() | |
.duration(2000) | |
.attr('opacity', 1); | |
// add interaction to the groups | |
groups.selectAll('.path_placeholder') | |
.call(d3.drag() | |
.on('start', group_dragstarted) | |
.on('drag', group_dragged) | |
.on('end', group_dragended) | |
); | |
node.append('title') | |
.text(function(d) { return d.id; }); | |
simulation | |
.nodes(graph.nodes) | |
.on('tick', ticked) | |
.force('link') | |
.links(graph.links); | |
function ticked() { | |
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; }); | |
node | |
.attr('cx', function(d) { return d.x; }) | |
.attr('cy', function(d) { return d.y; }); | |
updateGroups(); | |
} | |
}); | |
// select nodes of the group, retrieve its positions | |
// and return the convex hull of the specified points | |
// (3 points as minimum, otherwise returns null) | |
var polygonGenerator = function(groupId) { | |
var node_coords = node | |
.filter(function(d) { return d.group == groupId; }) | |
.data() | |
.map(function(d) { return [d.x, d.y]; }); | |
return d3.polygonHull(node_coords); | |
}; | |
function updateGroups() { | |
groupIds.forEach(function(groupId) { | |
var path = paths.filter(function(d) { return d == groupId;}) | |
.attr('transform', 'scale(1) translate(0,0)') | |
.attr('d', function(d) { | |
polygon = polygonGenerator(d); | |
centroid = d3.polygonCentroid(polygon); | |
// to scale the shape properly around its points: | |
// move the 'g' element to the centroid point, translate | |
// all the path around the center of the 'g' and then | |
// we can scale the 'g' element properly | |
return valueline( | |
polygon.map(function(point) { | |
return [ point[0] - centroid[0], point[1] - centroid[1] ]; | |
}) | |
); | |
}); | |
d3.select(path.node().parentNode).attr('transform', 'translate(' + centroid[0] + ',' + (centroid[1]) + ') scale(' + scaleFactor + ')'); | |
}); | |
} | |
// drag nodes | |
function dragstarted(d) { | |
if (!d3.event.active) simulation.alphaTarget(0.3).restart(); | |
d.fx = d.x; | |
d.fy = d.y; | |
} | |
function dragged(d) { | |
d.fx = d3.event.x; | |
d.fy = d3.event.y; | |
} | |
function dragended(d) { | |
if (!d3.event.active) simulation.alphaTarget(0); | |
d.fx = null; | |
d.fy = null; | |
} | |
// drag groups | |
function group_dragstarted(groupId) { | |
if (!d3.event.active) simulation.alphaTarget(0.3).restart(); | |
d3.select(this).select('path').style('stroke-width', 3); | |
} | |
function group_dragged(groupId) { | |
node | |
.filter(function(d) { return d.group == groupId; }) | |
.each(function(d) { | |
d.x += d3.event.dx; | |
d.y += d3.event.dy; | |
}) | |
} | |
function group_dragended(groupId) { | |
if (!d3.event.active) simulation.alphaTarget(0.3).restart(); | |
d3.select(this).select('path').style('stroke-width', 1); | |
} | |
</script> |
{ | |
"nodes": [ | |
{"id": "Myriel", "group": 1}, | |
{"id": "Napoleon", "group": 1}, | |
{"id": "Mlle.Baptistine", "group": 1}, | |
{"id": "Mme.Magloire", "group": 1}, | |
{"id": "CountessdeLo", "group": 1}, | |
{"id": "Geborand", "group": 1}, | |
{"id": "Champtercier", "group": 1}, | |
{"id": "Cravatte", "group": 1}, | |
{"id": "Count", "group": 1}, | |
{"id": "OldMan", "group": 1}, | |
{"id": "Labarre", "group": 2}, | |
{"id": "Valjean", "group": 2}, | |
{"id": "Marguerite", "group": 3}, | |
{"id": "Mme.deR", "group": 2}, | |
{"id": "Isabeau", "group": 2}, | |
{"id": "Gervais", "group": 2}, | |
{"id": "Tholomyes", "group": 3}, | |
{"id": "Listolier", "group": 3}, | |
{"id": "Fameuil", "group": 3}, | |
{"id": "Blacheville", "group": 3}, | |
{"id": "Favourite", "group": 3}, | |
{"id": "Dahlia", "group": 3}, | |
{"id": "Zephine", "group": 3}, | |
{"id": "Fantine", "group": 3}, | |
{"id": "Mme.Thenardier", "group": 4}, | |
{"id": "Thenardier", "group": 4}, | |
{"id": "Cosette", "group": 5}, | |
{"id": "Javert", "group": 4}, | |
{"id": "Fauchelevent", "group": 0}, | |
{"id": "Bamatabois", "group": 2}, | |
{"id": "Perpetue", "group": 3}, | |
{"id": "Simplice", "group": 2}, | |
{"id": "Scaufflaire", "group": 2}, | |
{"id": "Woman1", "group": 2}, | |
{"id": "Judge", "group": 2}, | |
{"id": "Champmathieu", "group": 2}, | |
{"id": "Brevet", "group": 2}, | |
{"id": "Chenildieu", "group": 2}, | |
{"id": "Cochepaille", "group": 2}, | |
{"id": "Pontmercy", "group": 4}, | |
{"id": "Boulatruelle", "group": 6}, | |
{"id": "Eponine", "group": 4}, | |
{"id": "Anzelma", "group": 4}, | |
{"id": "Woman2", "group": 5}, | |
{"id": "MotherInnocent", "group": 0}, | |
{"id": "Gribier", "group": 0}, | |
{"id": "Jondrette", "group": 7}, | |
{"id": "Mme.Burgon", "group": 7}, | |
{"id": "Gavroche", "group": 8}, | |
{"id": "Gillenormand", "group": 5}, | |
{"id": "Magnon", "group": 5}, | |
{"id": "Mlle.Gillenormand", "group": 5}, | |
{"id": "Mme.Pontmercy", "group": 5}, | |
{"id": "Mlle.Vaubois", "group": 5}, | |
{"id": "Lt.Gillenormand", "group": 5}, | |
{"id": "Marius", "group": 8}, | |
{"id": "BaronessT", "group": 5}, | |
{"id": "Mabeuf", "group": 8}, | |
{"id": "Enjolras", "group": 8}, | |
{"id": "Combeferre", "group": 8}, | |
{"id": "Prouvaire", "group": 8}, | |
{"id": "Feuilly", "group": 8}, | |
{"id": "Courfeyrac", "group": 8}, | |
{"id": "Bahorel", "group": 8}, | |
{"id": "Bossuet", "group": 8}, | |
{"id": "Joly", "group": 8}, | |
{"id": "Grantaire", "group": 8}, | |
{"id": "MotherPlutarch", "group": 9}, | |
{"id": "Gueulemer", "group": 4}, | |
{"id": "Babet", "group": 4}, | |
{"id": "Claquesous", "group": 4}, | |
{"id": "Montparnasse", "group": 4}, | |
{"id": "Toussaint", "group": 5}, | |
{"id": "Child1", "group": 10}, | |
{"id": "Child2", "group": 10}, | |
{"id": "Brujon", "group": 4}, | |
{"id": "Mme.Hucheloup", "group": 8} | |
], | |
"links": [ | |
{"source": "Napoleon", "target": "Myriel", "value": 1}, | |
{"source": "Mlle.Baptistine", "target": "Myriel", "value": 8}, | |
{"source": "Mme.Magloire", "target": "Myriel", "value": 10}, | |
{"source": "Mme.Magloire", "target": "Mlle.Baptistine", "value": 6}, | |
{"source": "CountessdeLo", "target": "Myriel", "value": 1}, | |
{"source": "Geborand", "target": "Myriel", "value": 1}, | |
{"source": "Champtercier", "target": "Myriel", "value": 1}, | |
{"source": "Cravatte", "target": "Myriel", "value": 1}, | |
{"source": "Count", "target": "Myriel", "value": 2}, | |
{"source": "OldMan", "target": "Myriel", "value": 1}, | |
{"source": "Valjean", "target": "Labarre", "value": 1}, | |
{"source": "Valjean", "target": "Mme.Magloire", "value": 3}, | |
{"source": "Valjean", "target": "Mlle.Baptistine", "value": 3}, | |
{"source": "Valjean", "target": "Myriel", "value": 5}, | |
{"source": "Marguerite", "target": "Valjean", "value": 1}, | |
{"source": "Mme.deR", "target": "Valjean", "value": 1}, | |
{"source": "Isabeau", "target": "Valjean", "value": 1}, | |
{"source": "Gervais", "target": "Valjean", "value": 1}, | |
{"source": "Listolier", "target": "Tholomyes", "value": 4}, | |
{"source": "Fameuil", "target": "Tholomyes", "value": 4}, | |
{"source": "Fameuil", "target": "Listolier", "value": 4}, | |
{"source": "Blacheville", "target": "Tholomyes", "value": 4}, | |
{"source": "Blacheville", "target": "Listolier", "value": 4}, | |
{"source": "Blacheville", "target": "Fameuil", "value": 4}, | |
{"source": "Favourite", "target": "Tholomyes", "value": 3}, | |
{"source": "Favourite", "target": "Listolier", "value": 3}, | |
{"source": "Favourite", "target": "Fameuil", "value": 3}, | |
{"source": "Favourite", "target": "Blacheville", "value": 4}, | |
{"source": "Dahlia", "target": "Tholomyes", "value": 3}, | |
{"source": "Dahlia", "target": "Listolier", "value": 3}, | |
{"source": "Dahlia", "target": "Fameuil", "value": 3}, | |
{"source": "Dahlia", "target": "Blacheville", "value": 3}, | |
{"source": "Dahlia", "target": "Favourite", "value": 5}, | |
{"source": "Zephine", "target": "Tholomyes", "value": 3}, | |
{"source": "Zephine", "target": "Listolier", "value": 3}, | |
{"source": "Zephine", "target": "Fameuil", "value": 3}, | |
{"source": "Zephine", "target": "Blacheville", "value": 3}, | |
{"source": "Zephine", "target": "Favourite", "value": 4}, | |
{"source": "Zephine", "target": "Dahlia", "value": 4}, | |
{"source": "Fantine", "target": "Tholomyes", "value": 3}, | |
{"source": "Fantine", "target": "Listolier", "value": 3}, | |
{"source": "Fantine", "target": "Fameuil", "value": 3}, | |
{"source": "Fantine", "target": "Blacheville", "value": 3}, | |
{"source": "Fantine", "target": "Favourite", "value": 4}, | |
{"source": "Fantine", "target": "Dahlia", "value": 4}, | |
{"source": "Fantine", "target": "Zephine", "value": 4}, | |
{"source": "Fantine", "target": "Marguerite", "value": 2}, | |
{"source": "Fantine", "target": "Valjean", "value": 9}, | |
{"source": "Mme.Thenardier", "target": "Fantine", "value": 2}, | |
{"source": "Mme.Thenardier", "target": "Valjean", "value": 7}, | |
{"source": "Thenardier", "target": "Mme.Thenardier", "value": 13}, | |
{"source": "Thenardier", "target": "Fantine", "value": 1}, | |
{"source": "Thenardier", "target": "Valjean", "value": 12}, | |
{"source": "Cosette", "target": "Mme.Thenardier", "value": 4}, | |
{"source": "Cosette", "target": "Valjean", "value": 31}, | |
{"source": "Cosette", "target": "Tholomyes", "value": 1}, | |
{"source": "Cosette", "target": "Thenardier", "value": 1}, | |
{"source": "Javert", "target": "Valjean", "value": 17}, | |
{"source": "Javert", "target": "Fantine", "value": 5}, | |
{"source": "Javert", "target": "Thenardier", "value": 5}, | |
{"source": "Javert", "target": "Mme.Thenardier", "value": 1}, | |
{"source": "Javert", "target": "Cosette", "value": 1}, | |
{"source": "Fauchelevent", "target": "Valjean", "value": 8}, | |
{"source": "Fauchelevent", "target": "Javert", "value": 1}, | |
{"source": "Bamatabois", "target": "Fantine", "value": 1}, | |
{"source": "Bamatabois", "target": "Javert", "value": 1}, | |
{"source": "Bamatabois", "target": "Valjean", "value": 2}, | |
{"source": "Perpetue", "target": "Fantine", "value": 1}, | |
{"source": "Simplice", "target": "Perpetue", "value": 2}, | |
{"source": "Simplice", "target": "Valjean", "value": 3}, | |
{"source": "Simplice", "target": "Fantine", "value": 2}, | |
{"source": "Simplice", "target": "Javert", "value": 1}, | |
{"source": "Scaufflaire", "target": "Valjean", "value": 1}, | |
{"source": "Woman1", "target": "Valjean", "value": 2}, | |
{"source": "Woman1", "target": "Javert", "value": 1}, | |
{"source": "Judge", "target": "Valjean", "value": 3}, | |
{"source": "Judge", "target": "Bamatabois", "value": 2}, | |
{"source": "Champmathieu", "target": "Valjean", "value": 3}, | |
{"source": "Champmathieu", "target": "Judge", "value": 3}, | |
{"source": "Champmathieu", "target": "Bamatabois", "value": 2}, | |
{"source": "Brevet", "target": "Judge", "value": 2}, | |
{"source": "Brevet", "target": "Champmathieu", "value": 2}, | |
{"source": "Brevet", "target": "Valjean", "value": 2}, | |
{"source": "Brevet", "target": "Bamatabois", "value": 1}, | |
{"source": "Chenildieu", "target": "Judge", "value": 2}, | |
{"source": "Chenildieu", "target": "Champmathieu", "value": 2}, | |
{"source": "Chenildieu", "target": "Brevet", "value": 2}, | |
{"source": "Chenildieu", "target": "Valjean", "value": 2}, | |
{"source": "Chenildieu", "target": "Bamatabois", "value": 1}, | |
{"source": "Cochepaille", "target": "Judge", "value": 2}, | |
{"source": "Cochepaille", "target": "Champmathieu", "value": 2}, | |
{"source": "Cochepaille", "target": "Brevet", "value": 2}, | |
{"source": "Cochepaille", "target": "Chenildieu", "value": 2}, | |
{"source": "Cochepaille", "target": "Valjean", "value": 2}, | |
{"source": "Cochepaille", "target": "Bamatabois", "value": 1}, | |
{"source": "Pontmercy", "target": "Thenardier", "value": 1}, | |
{"source": "Boulatruelle", "target": "Thenardier", "value": 1}, | |
{"source": "Eponine", "target": "Mme.Thenardier", "value": 2}, | |
{"source": "Eponine", "target": "Thenardier", "value": 3}, | |
{"source": "Anzelma", "target": "Eponine", "value": 2}, | |
{"source": "Anzelma", "target": "Thenardier", "value": 2}, | |
{"source": "Anzelma", "target": "Mme.Thenardier", "value": 1}, | |
{"source": "Woman2", "target": "Valjean", "value": 3}, | |
{"source": "Woman2", "target": "Cosette", "value": 1}, | |
{"source": "Woman2", "target": "Javert", "value": 1}, | |
{"source": "MotherInnocent", "target": "Fauchelevent", "value": 3}, | |
{"source": "MotherInnocent", "target": "Valjean", "value": 1}, | |
{"source": "Gribier", "target": "Fauchelevent", "value": 2}, | |
{"source": "Mme.Burgon", "target": "Jondrette", "value": 1}, | |
{"source": "Gavroche", "target": "Mme.Burgon", "value": 2}, | |
{"source": "Gavroche", "target": "Thenardier", "value": 1}, | |
{"source": "Gavroche", "target": "Javert", "value": 1}, | |
{"source": "Gavroche", "target": "Valjean", "value": 1}, | |
{"source": "Gillenormand", "target": "Cosette", "value": 3}, | |
{"source": "Gillenormand", "target": "Valjean", "value": 2}, | |
{"source": "Magnon", "target": "Gillenormand", "value": 1}, | |
{"source": "Magnon", "target": "Mme.Thenardier", "value": 1}, | |
{"source": "Mlle.Gillenormand", "target": "Gillenormand", "value": 9}, | |
{"source": "Mlle.Gillenormand", "target": "Cosette", "value": 2}, | |
{"source": "Mlle.Gillenormand", "target": "Valjean", "value": 2}, | |
{"source": "Mme.Pontmercy", "target": "Mlle.Gillenormand", "value": 1}, | |
{"source": "Mme.Pontmercy", "target": "Pontmercy", "value": 1}, | |
{"source": "Mlle.Vaubois", "target": "Mlle.Gillenormand", "value": 1}, | |
{"source": "Lt.Gillenormand", "target": "Mlle.Gillenormand", "value": 2}, | |
{"source": "Lt.Gillenormand", "target": "Gillenormand", "value": 1}, | |
{"source": "Lt.Gillenormand", "target": "Cosette", "value": 1}, | |
{"source": "Marius", "target": "Mlle.Gillenormand", "value": 6}, | |
{"source": "Marius", "target": "Gillenormand", "value": 12}, | |
{"source": "Marius", "target": "Pontmercy", "value": 1}, | |
{"source": "Marius", "target": "Lt.Gillenormand", "value": 1}, | |
{"source": "Marius", "target": "Cosette", "value": 21}, | |
{"source": "Marius", "target": "Valjean", "value": 19}, | |
{"source": "Marius", "target": "Tholomyes", "value": 1}, | |
{"source": "Marius", "target": "Thenardier", "value": 2}, | |
{"source": "Marius", "target": "Eponine", "value": 5}, | |
{"source": "Marius", "target": "Gavroche", "value": 4}, | |
{"source": "BaronessT", "target": "Gillenormand", "value": 1}, | |
{"source": "BaronessT", "target": "Marius", "value": 1}, | |
{"source": "Mabeuf", "target": "Marius", "value": 1}, | |
{"source": "Mabeuf", "target": "Eponine", "value": 1}, | |
{"source": "Mabeuf", "target": "Gavroche", "value": 1}, | |
{"source": "Enjolras", "target": "Marius", "value": 7}, | |
{"source": "Enjolras", "target": "Gavroche", "value": 7}, | |
{"source": "Enjolras", "target": "Javert", "value": 6}, | |
{"source": "Enjolras", "target": "Mabeuf", "value": 1}, | |
{"source": "Enjolras", "target": "Valjean", "value": 4}, | |
{"source": "Combeferre", "target": "Enjolras", "value": 15}, | |
{"source": "Combeferre", "target": "Marius", "value": 5}, | |
{"source": "Combeferre", "target": "Gavroche", "value": 6}, | |
{"source": "Combeferre", "target": "Mabeuf", "value": 2}, | |
{"source": "Prouvaire", "target": "Gavroche", "value": 1}, | |
{"source": "Prouvaire", "target": "Enjolras", "value": 4}, | |
{"source": "Prouvaire", "target": "Combeferre", "value": 2}, | |
{"source": "Feuilly", "target": "Gavroche", "value": 2}, | |
{"source": "Feuilly", "target": "Enjolras", "value": 6}, | |
{"source": "Feuilly", "target": "Prouvaire", "value": 2}, | |
{"source": "Feuilly", "target": "Combeferre", "value": 5}, | |
{"source": "Feuilly", "target": "Mabeuf", "value": 1}, | |
{"source": "Feuilly", "target": "Marius", "value": 1}, | |
{"source": "Courfeyrac", "target": "Marius", "value": 9}, | |
{"source": "Courfeyrac", "target": "Enjolras", "value": 17}, | |
{"source": "Courfeyrac", "target": "Combeferre", "value": 13}, | |
{"source": "Courfeyrac", "target": "Gavroche", "value": 7}, | |
{"source": "Courfeyrac", "target": "Mabeuf", "value": 2}, | |
{"source": "Courfeyrac", "target": "Eponine", "value": 1}, | |
{"source": "Courfeyrac", "target": "Feuilly", "value": 6}, | |
{"source": "Courfeyrac", "target": "Prouvaire", "value": 3}, | |
{"source": "Bahorel", "target": "Combeferre", "value": 5}, | |
{"source": "Bahorel", "target": "Gavroche", "value": 5}, | |
{"source": "Bahorel", "target": "Courfeyrac", "value": 6}, | |
{"source": "Bahorel", "target": "Mabeuf", "value": 2}, | |
{"source": "Bahorel", "target": "Enjolras", "value": 4}, | |
{"source": "Bahorel", "target": "Feuilly", "value": 3}, | |
{"source": "Bahorel", "target": "Prouvaire", "value": 2}, | |
{"source": "Bahorel", "target": "Marius", "value": 1}, | |
{"source": "Bossuet", "target": "Marius", "value": 5}, | |
{"source": "Bossuet", "target": "Courfeyrac", "value": 12}, | |
{"source": "Bossuet", "target": "Gavroche", "value": 5}, | |
{"source": "Bossuet", "target": "Bahorel", "value": 4}, | |
{"source": "Bossuet", "target": "Enjolras", "value": 10}, | |
{"source": "Bossuet", "target": "Feuilly", "value": 6}, | |
{"source": "Bossuet", "target": "Prouvaire", "value": 2}, | |
{"source": "Bossuet", "target": "Combeferre", "value": 9}, | |
{"source": "Bossuet", "target": "Mabeuf", "value": 1}, | |
{"source": "Bossuet", "target": "Valjean", "value": 1}, | |
{"source": "Joly", "target": "Bahorel", "value": 5}, | |
{"source": "Joly", "target": "Bossuet", "value": 7}, | |
{"source": "Joly", "target": "Gavroche", "value": 3}, | |
{"source": "Joly", "target": "Courfeyrac", "value": 5}, | |
{"source": "Joly", "target": "Enjolras", "value": 5}, | |
{"source": "Joly", "target": "Feuilly", "value": 5}, | |
{"source": "Joly", "target": "Prouvaire", "value": 2}, | |
{"source": "Joly", "target": "Combeferre", "value": 5}, | |
{"source": "Joly", "target": "Mabeuf", "value": 1}, | |
{"source": "Joly", "target": "Marius", "value": 2}, | |
{"source": "Grantaire", "target": "Bossuet", "value": 3}, | |
{"source": "Grantaire", "target": "Enjolras", "value": 3}, | |
{"source": "Grantaire", "target": "Combeferre", "value": 1}, | |
{"source": "Grantaire", "target": "Courfeyrac", "value": 2}, | |
{"source": "Grantaire", "target": "Joly", "value": 2}, | |
{"source": "Grantaire", "target": "Gavroche", "value": 1}, | |
{"source": "Grantaire", "target": "Bahorel", "value": 1}, | |
{"source": "Grantaire", "target": "Feuilly", "value": 1}, | |
{"source": "Grantaire", "target": "Prouvaire", "value": 1}, | |
{"source": "MotherPlutarch", "target": "Mabeuf", "value": 3}, | |
{"source": "Gueulemer", "target": "Thenardier", "value": 5}, | |
{"source": "Gueulemer", "target": "Valjean", "value": 1}, | |
{"source": "Gueulemer", "target": "Mme.Thenardier", "value": 1}, | |
{"source": "Gueulemer", "target": "Javert", "value": 1}, | |
{"source": "Gueulemer", "target": "Gavroche", "value": 1}, | |
{"source": "Gueulemer", "target": "Eponine", "value": 1}, | |
{"source": "Babet", "target": "Thenardier", "value": 6}, | |
{"source": "Babet", "target": "Gueulemer", "value": 6}, | |
{"source": "Babet", "target": "Valjean", "value": 1}, | |
{"source": "Babet", "target": "Mme.Thenardier", "value": 1}, | |
{"source": "Babet", "target": "Javert", "value": 2}, | |
{"source": "Babet", "target": "Gavroche", "value": 1}, | |
{"source": "Babet", "target": "Eponine", "value": 1}, | |
{"source": "Claquesous", "target": "Thenardier", "value": 4}, | |
{"source": "Claquesous", "target": "Babet", "value": 4}, | |
{"source": "Claquesous", "target": "Gueulemer", "value": 4}, | |
{"source": "Claquesous", "target": "Valjean", "value": 1}, | |
{"source": "Claquesous", "target": "Mme.Thenardier", "value": 1}, | |
{"source": "Claquesous", "target": "Javert", "value": 1}, | |
{"source": "Claquesous", "target": "Eponine", "value": 1}, | |
{"source": "Claquesous", "target": "Enjolras", "value": 1}, | |
{"source": "Montparnasse", "target": "Javert", "value": 1}, | |
{"source": "Montparnasse", "target": "Babet", "value": 2}, | |
{"source": "Montparnasse", "target": "Gueulemer", "value": 2}, | |
{"source": "Montparnasse", "target": "Claquesous", "value": 2}, | |
{"source": "Montparnasse", "target": "Valjean", "value": 1}, | |
{"source": "Montparnasse", "target": "Gavroche", "value": 1}, | |
{"source": "Montparnasse", "target": "Eponine", "value": 1}, | |
{"source": "Montparnasse", "target": "Thenardier", "value": 1}, | |
{"source": "Toussaint", "target": "Cosette", "value": 2}, | |
{"source": "Toussaint", "target": "Javert", "value": 1}, | |
{"source": "Toussaint", "target": "Valjean", "value": 1}, | |
{"source": "Child1", "target": "Gavroche", "value": 2}, | |
{"source": "Child2", "target": "Gavroche", "value": 2}, | |
{"source": "Child2", "target": "Child1", "value": 3}, | |
{"source": "Brujon", "target": "Babet", "value": 3}, | |
{"source": "Brujon", "target": "Gueulemer", "value": 3}, | |
{"source": "Brujon", "target": "Thenardier", "value": 3}, | |
{"source": "Brujon", "target": "Gavroche", "value": 1}, | |
{"source": "Brujon", "target": "Eponine", "value": 1}, | |
{"source": "Brujon", "target": "Claquesous", "value": 1}, | |
{"source": "Brujon", "target": "Montparnasse", "value": 1}, | |
{"source": "Mme.Hucheloup", "target": "Bossuet", "value": 1}, | |
{"source": "Mme.Hucheloup", "target": "Joly", "value": 1}, | |
{"source": "Mme.Hucheloup", "target": "Grantaire", "value": 1}, | |
{"source": "Mme.Hucheloup", "target": "Bahorel", "value": 1}, | |
{"source": "Mme.Hucheloup", "target": "Courfeyrac", "value": 1}, | |
{"source": "Mme.Hucheloup", "target": "Gavroche", "value": 1}, | |
{"source": "Mme.Hucheloup", "target": "Enjolras", "value": 1} | |
] | |
} |
You can modify the forces applied to the network. More specifically, the force 'link'.
Here an example: https://bl.ocks.org/xaviergimenez/c8d24b2d2da3181455aa4607e266c347
I want to increase the size of nodes, for that, I have increased the value or "r" in attr('r', 10), Now to make the graph consistent I want to increase the separation between nodes, how can I?