<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Grouped Layout</title>
<style>
@import url(./style.css);
.node {
stroke: #fff;
stroke-width: 1.5px;
cursor: move;
}
.group {
stroke: #fff;
stroke-width: 1.5px;
cursor: move;
opacity: 0.7;
}
.link {
stroke: #7a4e4e;
stroke-width: 3px;
stroke-opacity: 1;
}
.label {
fill: black;
font-family: Verdana;
font-size: 15px;
text-anchor: middle;
cursor: move;
}
</style>
</head>
<body>
<a href="../index.html">cola.js home</a>
<button type="buttom" onclick="start()">start</button>
<button type="buttom" onclick="stop()">stop</button>
<button type="buttom" onclick="group1()">g1</button>
<button type="buttom" onclick="group2()">g2</button>
<h1>Layout with hierarchical grouping</h1>
<script src="./d3v4.js"></script>
<script src="./cola.min.js"></script>
<script>
var width = 860,
height = 700;
var color = d3.scaleOrdinal(d3.schemeCategory20);
var mycola = cola.d3adaptor(d3)
.linkDistance(80)
//.flowLayout('y', 120)
.avoidOverlaps(true)
.handleDisconnected(true)
.size([width, height]);
function handleZoom(){
d3.select('svg g')
.attr('transform', d3.event.transform)
}
// zoom
// https://bl.ocks.org/puzzler10/49f13307e818ea9a909ba5adba5b6ed9
let zoom = d3.zoom()
.on('zoom', handleZoom)
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.style("border", "1px solid black")
//https://stackoverflow.com/questions/7430580/setting-rounded-corners-for-svgimage
var defs = svg.append("defs")
const avatarRadius = 25
defs.append("clipPath")
.attr("id", "avatar-clip")
.append("circle")
.attr("cx", avatarRadius)
.attr("cy", avatarRadius)
.attr("r", avatarRadius)
var container = svg.append('g')
function start(){
cola.start()
}
function stop(){
cola.stop()
}
function group1(){
container.remove()
container = svg.append("g")
if (node)
node.on('mouseup', null)
if (mycola)
mycola.on("tick", null)
const newgraph = JSON.parse(JSON.stringify(ggg))
newgraph.groups = newgraph.groups1
renderGraph(newgraph)
}
var g2 = true
function group2(){
container.remove()
container = svg.append("g")
console.log(mycola)
if (node)
node.on('mouseup', null)
if (mycola)
mycola.on("tick", null)
mycola = cola.d3adaptor(d3)
.linkDistance(80)
//.flowLayout('y', 120)
.avoidOverlaps(true)
.handleDisconnected(true)
.size([width, height]);
const newgraph = JSON.parse(JSON.stringify(ggg))
newgraph.groups = newgraph.groups2
console.log(newgraph)
renderGraph(newgraph)
}
var group, link, pad, node, ggg
function renderGraph(graph){
//graph.nodes.forEach(function (v) {
// v.width = v.height = 95;
//})
//graph.groups.forEach(function (g) { g.padding = 10; });
//graph.groups.forEach(function (g) { g.padding = 20; });
mycola
.nodes(graph.nodes)
.links(graph.links)
.groups(graph.groups)
.start(100,0)
//.start(100, 0, 50, 50);
group = container.selectAll(".group")
.data(graph.groups)
.enter().append("rect")
.attr("rx", 8).attr("ry", 8)
.attr("class", "group")
.style("fill", function (d, i) { return color(i); });
link = container.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link");
pad = 20;
node = container.selectAll(".node")
.data(graph.nodes)
.enter()
.append("g")
.style("border", "1px solid black")
.call(mycola.drag)
.on('mouseup', function (d) {
d.fixed = 0;
mycola.alpha(1); // fire it off again to satify gridify
});
// node.append("rect")
// .attr("width", function (d) { return d.width - 2 * pad; })
// .attr("height", function (d) { return d.height - 2 * pad; })
// .attr("class", "node")
// .style("fill", function (d) { return color(graph.groups.length); })
node.append("circle")
.attr("cx", 25)
.attr("cy", 25)
.attr("r", 30)
.attr("class", "node")
.style("fill", function (d) { return color(1); })
node.append("text")
.attr("class", "label")
.text(function (d) { return d.name; })
//.call(cola.drag);
const randomAvatar = () => {
const id = Math.floor(Math.random()*50)
const gender = Math.random()*100 > 50 ? 'women' : 'men'
return `https://randomuser.me/api/portraits/${gender}/${id}.jpg`
}
node.append("svg:image")
.attr("width", "50px")
.attr("height", "50px")
.attr("xlink:href", (d) => randomAvatar())
.attr("clip-path", "url(#avatar-clip")
// node.append("title")
// .text(function (d) { return d.name; });
mycola.on("tick", function () {
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("transform", (d) => `translate(${d.x - d.width / 2 + pad}, ${d.y - d.height / 2 + pad})`)
// node.attr("x", function (d) { return d.x - d.width / 2 + pad; })
// .attr("y", function (d) { return d.y - d.height / 2 + pad; });
group.attr("x", function (d) { return d.bounds.x; })
.attr("y", function (d) { return d.bounds.y; })
.attr("width", function (d) { return d.bounds.width(); })
.attr("height", function (d) { return d.bounds.height(); });
// label.attr("x", function (d) { return d.x; })
// .attr("y", function (d) {
// var h = this.getBBox().height;
// return d.y + h/4;
// });
});
d3.select('svg').call(zoom)
container.attr('transform', 'translate(142.84548566425633,156.1980992544233) scale(0.5743491774985179)')
}
d3.json("./smallgrouped2.json", function (error, g2) {
ggg = g2
group2()
});
</script>
<p>This graph has a hierarchical grouping defined over the nodes. We generate constraints to keep the bounding boxes of
disjoint groups from overlapping and to keep nested groups fully contained within their parents' bounding boxes.
Try dragging the nodes so that they bump into other nodes and groups.
</p>
</body>
</html>