<html> <meta charset="utf-8"> <header>force directed graph</header> <style> #svgHolder { position: relative } .svg2 { position: absolute; top: 0px; left: 0px; } </style> <body> <label for = "n_cnt">Nodes Count:</label> <input type = "number" id = "n_cnt" name = "n_cnt" value = "60" min = "30" max = "1000"></input> <button type = "button" onclick = "update()">Generate</button><br></br> <div id = "svgHolder"> <svg id = "svgOne" width ="1190" height = "590"></svg> <svg id = "svgTwo" class = "svg2" width ="1190" height = "590"></svg> </div> <script> var margin = {top: 10, right: 10, bottom: 10, left: 10}, width = 1200 - margin.left - margin.right, height = 600 - margin.top - margin.bottom; var svgns = "http://www.w3.org/2000/svg";
var colorset1 = ['#FFADAD', '#FFC2A9', '#FFD6A5', '#FDFFB6', '#CAFFBF', '#9BF6FF', '#A0C4FF', '#BDB2FF', '#FFC6FF', '#FFFFFC']; var colorset2 = ['#F94144', '#F3722C', '#F8961E', '#F9C74F', '#90BE6D', '#52838F', '#577590', '#7209B7', '#B5179E', '#EDF2F4'];
let o_nodes = [], nodes = []; var node_cnt = 60, maxNodeX = width - 2, maxNodeY = height - 2; var connect = [], con_fsx = [], con_fsy = [], con_fs = []; var L = 50, K_r = 6250, K_s = 1, delta_t = 0.04; var MAX_DIS_SQR = 50;// ???
gen_d(); draw_old(); cal_force(); update_position();
draw_new()
function update() { document.getElementById('svgOne').innerHTML = ""; document.getElementById('svgTwo').innerHTML = ""; o_nodes.length = 0, nodes.length = 0, node_cnt = document.getElementById("n_cnt").value; connect.length = 0, con_fsx.length = 0, con_fsy.length = 0, con_fs.length = 0; gen_d(); draw_old(); cal_force(); update_position();
draw_new(); }
function update2(_l, _kr, _ks, _dt, _msd) { document.getElementById('svgTwo').innerHTML = ""; if (_l != -1) L = _l; if (_kr != -1) K_r = _kr; if (_ks != -1) K_s = _ks; if (_dt != -1) delta_t = _dt; if (_msd != -1) MAX_DIS_SQR = _msd; con_fs.length = 0; for (var i = 0; i < con_fsx.length; i++) { for (var j = 0; j < con_fsx[i].length; j++) { con_fsx[i][j] = 0; con_fsy[i][j] = 0; } } nodes.length = 0; nodes = deepClone(o_nodes); cal_force(); update_position();
draw_new(); }
function deepClone (obj) { if (typeof obj !== 'object') return obj; if (!obj) // obj null return obj; if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); if (obj instanceof Function) return obj; let newObj; if (obj instanceof Array) { newObj = []; for(let i = 0, len = obj.length; i < len; i++) newObj.push(deepClone(obj[i])); return newObj; } newObj = {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { if (typeof obj[key] !== 'object') newObj[key] = obj[key]; else newObj[key] = deepClone(obj[key]); } } return newObj; }
// generate data.... function gen_d() { var temp_cnt = 0, group = 0; while (temp_cnt < node_cnt) {//max-min+1 +min // generate nodes var x0 = Math.floor(Math.random() * (maxNodeX - 1 + 1) ) + 1; var y0 = Math.floor(Math.random() * (maxNodeY - 1 + 1) ) + 1; var cnt1 = Math.floor(Math.random() * (15 - 2 + 1) ) + 2; if (temp_cnt + cnt1 > node_cnt) cnt1 = node_cnt - temp_cnt; var range_g = 50; var Fr_x = 0, Fr_y = 0, Fs_x = 0, Fs_y = 0; var x = x0, y = y0; o_nodes[group] = new Array; nodes[group] = new Array; o_nodes[group].push({x,y,Fr_x,Fr_y,Fs_x,Fs_y}); nodes[group].push({x,y,Fr_x,Fr_y,Fs_x,Fs_y}); for (var i = 1; i < cnt1; i++) { // generate group nodes var dx = Math.floor(Math.random() * (range_g - 1 + 1) ) + 1; var dy = Math.floor(Math.random() * (range_g - 1 + 1) ) + 1; var x1 = x0 + dx, y1 = y0 + dy; var x2 = x0 - dx, y2 = y0 - dy; var x, y; if (x1 > maxNodeX || x1 < 0) x = x2; else x = x1; if (y1 > maxNodeY || y1 < 0) y = y2; else y = y1; o_nodes[group].push({x,y,Fr_x,Fr_y,Fs_x,Fs_y}); nodes[group].push({x,y,Fr_x,Fr_y,Fs_x,Fs_y}); } // generate connection var con0 = []; for (var i = 0; i < cnt1; i++) { con0[i] = new Array(cnt1); con0[i].fill(0, 0, cnt1); } for (var i = 0; i < cnt1 - 1; i++) { for (var j = i + 1; j < cnt1; j++) { var isconnected = Math.floor(Math.random() * 10); if (isconnected >= 6) { con0[i][j] = 1; con0[j][i] = 1; } } } connect.push(con0); group++; temp_cnt += cnt1; } var con1 = []; for (var i = 0; i < group; i++) { con1[i] = new Array(group); con1[i].fill(0, 0, group); con_fsx[i] = new Array(group); con_fsx[i].fill(0, 0, group); con_fsy[i] = new Array(group); con_fsy[i].fill(0, 0, group); } for (var i = 0; i < group - 1; i++) { var newCon = Math.floor(Math.random() * group - i) + i; con1[newCon][i] = 1; con1[i][newCon] = 1; for (var j = i + 1; j < group; j++) { var isconnected = Math.floor(Math.random() * 10); if (isconnected >= 6) { con1[i][j] = 1; con1[j][i] = 1; } } } connect.push(con1); console.log("connected? : ", connect); console.log("now get data: ", o_nodes); console.log("copy nodes: ", nodes); }
function cal_force() { for (var i1 = 0; i1 < nodes.length; i1++) { for (var i2 = 0; i2 < nodes[i1].length - 1; i2++) { var node1 = nodes[i1][i2]; for(var j1 = i1; j1 < nodes.length; j1++) { for (var j2 = 0; j2 < nodes[j1].length; j2++) { if (j1 == i1 && j2 <= i2) continue; var node2 = nodes[j1][j2]; var dx = node2.x - node1.x; var dy = node2.y - node1.y; if (dx != 0 || dy != 0) { var disSqu = dx*dx + dy*dy; var dis = Math.sqrt(disSqu); var force = K_r / disSqu; var fx = force * dx / dis; var fy = force * dy / dis; node1.Fr_x = node1.Fr_x - fx; node1.Fr_y = node1.Fr_y - fy; node2.Fr_x = node2.Fr_x + fx; node2.Fr_y = node2.Fr_y + fy; if (i1 == j1 && connect[i1][i2][j2] == 1) { var force2 = K_s * (dis - L); var fx2 = force2 * dx / dis; var fy2 = force2 * dy / dis; node1.Fs_x = node1.Fs_x + fx2; node1.Fs_y = node1.Fs_y + fy2; node2.Fs_x = node2.Fs_x - fx2; node2.Fs_y = node2.Fs_y - fy2; } if (i2 == 0 && j2 == 0 && i1 != j1) { if (connect[nodes.length][i1][j1] == 1) { var force3 = K_s * (dis - L); var fx3 = force3 * dx / dis; var fy3 = force3 * dy / dis; con_fsx[i1][j1] += fx3; con_fsy[i1][j1] += fy3; con_fsx[j1][i1] -= fx3; con_fsy[j1][i1] -= fy3; } } nodes[i1][i2] = node1; nodes[j1][j2] = node2; } } } } } for (var i = 0; i < con_fsx.length; i++) { var x = 0, y = 0; for (var j = 0; j < con_fsx.length; j++) { x = x + con_fsx[i][j]; y = y + con_fsy[i][j]; } con_fs.push({x,y}); } } function update_position() { for (var i1 = 0; i1 < nodes.length; i1++) { for (var i2 = 0; i2 < nodes[i1].length; i2++) { var _node = nodes[i1][i2]; var dx = delta_t * (_node.Fr_x + _node.Fs_x + con_fs[i1].x); var dy = delta_t * (_node.Fr_y + _node.Fs_y + con_fs[i1].y); var displacement_Sqr = dx*dx + dy*dy; if (displacement_Sqr > MAX_DIS_SQR) { var disS = Math.sqrt(MAX_DIS_SQR / displacement_Sqr); dx = dx * disS; dy = dy * disS; } _node.x = _node.x + dx; _node.y = _node.y + dy; nodes[i1][i2] = _node; } } console.log("old & new nodes are here: ", o_nodes, nodes); }
function draw_old() { for (var i = 0; i < connect.length; i++) { var temp_con = connect[i]; for (var j = 0; j < temp_con.length - 1; j++) { for (var k = j + 1; k < temp_con.length; k++) { if (temp_con[j][k] == 1) { var x1, x2, y1, y2; var lineStroke = '#ffdab9'; var lineLen = 0.5; if (i == connect.length - 1) { x1 = o_nodes[j][0].x, y1 = o_nodes[j][0].y; x2 = o_nodes[k][0].x, y2 = o_nodes[k][0].y; lineStroke = '#f08080'; lineLen = 1.0; } else{ x1 = o_nodes[i][j].x, y1 = o_nodes[i][j].y; x2 = o_nodes[i][k].x, y2 = o_nodes[i][k].y; }
var line = document.createElementNS(svgns, 'line'); line.setAttributeNS(null, 'x1', x1); line.setAttributeNS(null, 'x2', x2); line.setAttributeNS(null, 'y1', y1); line.setAttributeNS(null, 'y2', y2); line.setAttributeNS(null, 'stroke', lineStroke); line.setAttributeNS(null, 'stroke-width', lineLen); document.getElementById('svgOne').appendChild(line); } } } }
for (var i = 0; i < o_nodes.length; i++) { for (j = 0; j < o_nodes[i].length; j++) { var getx = o_nodes[i][j].x; var gety = o_nodes[i][j].y; var circle = document.createElementNS(svgns, 'circle'); circle.setAttributeNS(null, "cx", getx); circle.setAttributeNS(null, "cy", gety); circle.setAttributeNS(null, "r", 3); circle.setAttributeNS(null, "fill", colorset1[i % colorset1.length]); circle.setAttributeNS(null, "stroke", "none"); document.getElementById('svgOne').appendChild(circle); } } }
function draw_new() { for (var i = 0; i < connect.length; i++) { var temp_con = connect[i]; for (var j = 0; j < temp_con.length - 1; j++) { for (var k = j + 1; k < temp_con.length; k++) { if (temp_con[j][k] == 1) { var x1, x2, y1, y2; var lineStroke = '#ffcad4'; var lineLen = 1.0; if (i == connect.length - 1) { x1 = nodes[j][0].x, y1 = nodes[j][0].y; x2 = nodes[k][0].x, y2 = nodes[k][0].y; lineStroke = '#9e2a2b'; lineLen = 1.5; } else{ x1 = nodes[i][j].x, y1 = nodes[i][j].y; x2 = nodes[i][k].x, y2 = nodes[i][k].y; }
var line = document.createElementNS(svgns, 'line'); line.setAttributeNS(null, 'x1', x1); line.setAttributeNS(null, 'x2', x2); line.setAttributeNS(null, 'y1', y1); line.setAttributeNS(null, 'y2', y2); line.setAttributeNS(null, 'stroke', lineStroke); line.setAttributeNS(null, 'stroke-width', lineLen); document.getElementById('svgTwo').appendChild(line); } } } }
for (var i = 0; i < nodes.length; i++) { for (j = 0; j < nodes[i].length; j++) { var getx = nodes[i][j].x; var gety = nodes[i][j].y; var circle = document.createElementNS(svgns, 'circle'); circle.setAttributeNS(null, "cx", getx); circle.setAttributeNS(null, "cy", gety); circle.setAttributeNS(null, "r", 4.5); circle.setAttributeNS(null, "fill", colorset2[i % colorset2.length]); circle.setAttributeNS(null, "stroke", "none"); document.getElementById('svgTwo').appendChild(circle); } } } </script> <div class = "slidecontainer" id = "slidecon"> <label>L: </label> <input type = "range" min = "10" max="1000" value = "50" class = "slider" id = "s_L" oninput = "document.getElementById('l_L').innerHTML = this.value" onchange = "update2(this.value, -1, -1, -1, -1)" /> <label id = "l_L">50</label> <br> <label>K_s: </label> <input type = "range" min = "100" max="10000" value = "6250" class = "slider" id = "s_S" oninput = "document.getElementById('l_S').innerHTML = this.value" onchange = "update2(-1, this.value, -1, -1, -1)" /> <label id = "l_S">6250</label> <br>
<label>K_r: </label> <input type = "range" min = "0.01" max="5" value = "1" step = "0.01" class = "slider" id = "s_R" oninput = "document.getElementById('l_R').innerHTML = this.value" onchange = "update2(-1, -1, this.value, -1, -1)" /> <label id = "l_R">1</label> <br>
<label>Δt: </label> <input type = "range" min = "0.005" max="5.000" value = "0.04" step = "0.001" class = "slider" id = "s_T" oninput = "document.getElementById('l_T').innerHTML = this.value" onchange = "update2(-1, -1, -1, this.value, -1)" /> <label id = "l_T">0.04</label> <br>
<label>MAX_DIS_SQR: </label> <input type = "range" min = "10" max="200" value = "50" class = "slider" id = "s_M" oninput = "document.getElementById('l_M').innerHTML = this.value" onchange = "update2(-1, -1, -1, -1, this.value)" /> <label id = "l_M">50</label> <br> </div> </body> </html>
|