Carlos Aguni

Highly motivated self-taught IT analyst. Always learning and ready to explore new skills. An eternal apprentice.


ColaJs Try hierarchical grouping

28 Sep 2021 »


<!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>