| import * as vec2 from 'zrender/src/core/vector'; |
| var scaleAndAdd = vec2.scaleAndAdd; // function adjacentNode(n, e) { |
| // return e.n1 === n ? e.n2 : e.n1; |
| // } |
| |
| export function forceLayout(nodes, edges, opts) { |
| var rect = opts.rect; |
| var width = rect.width; |
| var height = rect.height; |
| var center = [rect.x + width / 2, rect.y + height / 2]; // var scale = opts.scale || 1; |
| |
| var gravity = opts.gravity == null ? 0.1 : opts.gravity; // for (var i = 0; i < edges.length; i++) { |
| // var e = edges[i]; |
| // var n1 = e.n1; |
| // var n2 = e.n2; |
| // n1.edges = n1.edges || []; |
| // n2.edges = n2.edges || []; |
| // n1.edges.push(e); |
| // n2.edges.push(e); |
| // } |
| // Init position |
| |
| for (var i = 0; i < nodes.length; i++) { |
| var n = nodes[i]; |
| |
| if (!n.p) { |
| // Use the position from first adjecent node with defined position |
| // Or use a random position |
| // From d3 |
| // if (n.edges) { |
| // var j = -1; |
| // while (++j < n.edges.length) { |
| // var e = n.edges[j]; |
| // var other = adjacentNode(n, e); |
| // if (other.p) { |
| // n.p = vec2.clone(other.p); |
| // break; |
| // } |
| // } |
| // } |
| // if (!n.p) { |
| n.p = vec2.create(width * (Math.random() - 0.5) + center[0], height * (Math.random() - 0.5) + center[1]); // } |
| } |
| |
| n.pp = vec2.clone(n.p); |
| n.edges = null; |
| } // Formula in 'Graph Drawing by Force-directed Placement' |
| // var k = scale * Math.sqrt(width * height / nodes.length); |
| // var k2 = k * k; |
| |
| |
| var friction = 0.6; |
| return { |
| warmUp: function () { |
| friction = 0.5; |
| }, |
| setFixed: function (idx) { |
| nodes[idx].fixed = true; |
| }, |
| setUnfixed: function (idx) { |
| nodes[idx].fixed = false; |
| }, |
| step: function (cb) { |
| var v12 = []; |
| var nLen = nodes.length; |
| |
| for (var i = 0; i < edges.length; i++) { |
| var e = edges[i]; |
| var n1 = e.n1; |
| var n2 = e.n2; |
| vec2.sub(v12, n2.p, n1.p); |
| var d = vec2.len(v12) - e.d; |
| var w = n2.w / (n1.w + n2.w); |
| |
| if (isNaN(w)) { |
| w = 0; |
| } |
| |
| vec2.normalize(v12, v12); |
| !n1.fixed && scaleAndAdd(n1.p, n1.p, v12, w * d * friction); |
| !n2.fixed && scaleAndAdd(n2.p, n2.p, v12, -(1 - w) * d * friction); |
| } // Gravity |
| |
| |
| for (var i = 0; i < nLen; i++) { |
| var n = nodes[i]; |
| |
| if (!n.fixed) { |
| vec2.sub(v12, center, n.p); // var d = vec2.len(v12); |
| // vec2.scale(v12, v12, 1 / d); |
| // var gravityFactor = gravity; |
| |
| scaleAndAdd(n.p, n.p, v12, gravity * friction); |
| } |
| } // Repulsive |
| // PENDING |
| |
| |
| for (var i = 0; i < nLen; i++) { |
| var n1 = nodes[i]; |
| |
| for (var j = i + 1; j < nLen; j++) { |
| var n2 = nodes[j]; |
| vec2.sub(v12, n2.p, n1.p); |
| var d = vec2.len(v12); |
| |
| if (d === 0) { |
| // Random repulse |
| vec2.set(v12, Math.random() - 0.5, Math.random() - 0.5); |
| d = 1; |
| } |
| |
| var repFact = (n1.rep + n2.rep) / d / d; |
| !n1.fixed && scaleAndAdd(n1.pp, n1.pp, v12, repFact); |
| !n2.fixed && scaleAndAdd(n2.pp, n2.pp, v12, -repFact); |
| } |
| } |
| |
| var v = []; |
| |
| for (var i = 0; i < nLen; i++) { |
| var n = nodes[i]; |
| |
| if (!n.fixed) { |
| vec2.sub(v, n.p, n.pp); |
| scaleAndAdd(n.p, n.p, v, friction); |
| vec2.copy(n.pp, n.p); |
| } |
| } |
| |
| friction = friction * 0.992; |
| cb && cb(nodes, edges, friction < 0.01); |
| } |
| }; |
| } |