Sunday, October 15, 2017

Forced Directed graph multiple edges using d3 canvas

Leave a Comment

I have created forced directed graph which has multiple edges , but after rending it shows only , other one overlaps on one another . I want to create something like https://bl.ocks.org/mattkohl/146d301c0fc20d89d85880df537de7b0#index.html

My Code : JSBIN

<!DOCTYPE html> <html> <head>         <title>Sample Graph Rendring Using Canvas</title>         <script src="https://rawgit.com/gka/randomgraph.js/master/randomgraph.js"></script>         <script src="https://d3js.org/d3.v4.min.js"></script> </head> <body>     <script>         var graph = {}//randomgraph.WattsStrogatz.beta(15, 4, 0.06);      graph.nodes = [{"label":"x"} , {"label":"y"}];     graph.edges = [{source:0,target:1},{source:0,target:1},                    {source:1,target:0}]          var canvas = null         var width = window.innerWidth,             height = window.innerHeight;         canvas = d3.select("body").append("canvas").attr("width",width).attr("height",height);          var context = canvas.node().getContext("2d");           force = d3.forceSimulation()                 .force("link", d3.forceLink().id(function(d) {                       return d.index;                 })).force("charge", d3.forceManyBody())                 .force("center", d3.forceCenter(width / 2, height / 2));          force.nodes(graph.nodes);         force.force("link").links(graph.edges).distance(200);          var detachedContainer = document.createElement("custom");             dataContainer = d3.select(detachedContainer);          link = dataContainer.selectAll(".link").data(graph.edges)               .enter().append("line").attr("class", "link")               .style("stroke-width", 2)          node = dataContainer.selectAll(".node").data(graph.nodes)               .enter().append("g");            var circles = node.append("circle")               .classed("circle-class", true)               .attr("class", function (d){ return "node node_" + d.index;})               .attr("r", 5)               .attr("fill", 'red')               .attr("strokeStyle", 'black');          d3.timer(function(){             context.clearRect(0, 0, width, height);              // draw links             link.each(function(d) {               context.strokeStyle = "#ccc";               /***** Elliptical arcs *****/               context.stroke(new Path2D(linkArc(d)));               /***** Elliptical arcs *****/             });              context.lineWidth = 2;             node.each(function(d) {                context.beginPath();               context.moveTo(d.x, d.y);               var r = d3.select(this).select("circle").node().getAttribute('r');                   d.x = Math.max(30, Math.min(width - 30, d.x));               d.y = Math.max(30, Math.min(height - 30, d.y));                        context.closePath();               context.arc(d.x, d.y, r, 0, 2 * Math.PI);                context.fillStyle = d3.select(this).select("circle").node().getAttribute('fill');               context.strokeStyle = d3.select(this).select("circle").node().getAttribute('strokeStyle');               context.stroke();               context.fill();                context.beginPath();               context.arc(d.x + 15, d.y-20, 5, 0, 2 * Math.PI);               context.fillStyle = "orange";               context.strokeStyle = "orange";               var data = d3.select(this).data();               context.stroke();               context.fill();               context.font = "10px Arial";               context.fillStyle = "black";               context.strokeStyle = "black";               context.fillText(parseInt(data[0].index),d.x + 10, d.y-15);             });          });          circles.transition().duration(5000).attr('r', 20).attr('fill', 'orange');          canvas.node().addEventListener('click',function( event ){            console.log(event)             // Its COMING ANY TIME INSIDE ON CLICK OF CANVAS         });          /***** Elliptical arcs *****/         function linkArc(d) {           var dx = d.target.x - d.source.x,               dy = d.target.y - d.source.y,               dr = Math.sqrt(dx * dx + dy * dy);           return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;         }         /***** Elliptical arcs *****/     </script> </body> </html>   

1 Answers

Answers 1

You can make the curve like this:

  //make a curve object    var curves = {};    function getCurveFactor(d){     var factor = 0.3;//you may change this as per your choice.     //make a key     var key = d.source.index + "---"+ d.target.index;     //see if the key is present in the object     if (curves[key]){       //if key is present you need a bigger curve      //so that it does not overlap       curves[key] = curves[key] + factor;     } else {       //no key present keep the curve simple       curves[key] = 1;     }     return curves[key];   } 

This function makes the arc:

    function arcPath(d) {                 var x1 = d.target.x,                     y1 = d.target.y,                     x2 = d.source.x,                     y2 = d.source.y,                     dx = x2 - x1,                     dy = y2 - y1,                     dr = Math.sqrt(dx * dx + dy * dy),                     drx = dr,                     dry = dr;                  //store the curve factor inside the data.                 if(!d.curve){                   d.curve = getCurveFactor(d);                 }                     return "M" + x1 + "," + y1 + "A" + (drx/d.curve) + ", " + (dry) + " " + 0 + ", " + 0 + ", " + 0 + " " + x2 + "," + y2;             } 

working code here

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment