Built with blockbuilder.org
forked from john-guerra's block: d3.forceBoundary Forces no Border
| license: mit |
Built with blockbuilder.org
forked from john-guerra's block: d3.forceBoundary Forces no Border
| // https://d3js.org/d3-force/ Version 1.1.0. Copyright 2018 Mike Bostock. | |
| (function (global, factory) { | |
| typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-quadtree'), require('d3-collection'), require('d3-dispatch'), require('d3-timer')) : | |
| typeof define === 'function' && define.amd ? define(['exports', 'd3-quadtree', 'd3-collection', 'd3-dispatch', 'd3-timer'], factory) : | |
| (factory((global.d3 = global.d3 || {}),global.d3,global.d3,global.d3,global.d3)); | |
| }(this, (function (exports,d3Quadtree,d3Collection,d3Dispatch,d3Timer) { 'use strict'; | |
| var center = function(x, y) { | |
| var nodes; | |
| if (x == null) x = 0; | |
| if (y == null) y = 0; | |
| function force() { | |
| var i, | |
| n = nodes.length, | |
| node, | |
| sx = 0, | |
| sy = 0; | |
| for (i = 0; i < n; ++i) { | |
| node = nodes[i], sx += node.x, sy += node.y; | |
| } | |
| for (sx = sx / n - x, sy = sy / n - y, i = 0; i < n; ++i) { | |
| node = nodes[i], node.x -= sx, node.y -= sy; | |
| } | |
| } | |
| force.initialize = function(_) { | |
| nodes = _; | |
| }; | |
| force.x = function(_) { | |
| return arguments.length ? (x = +_, force) : x; | |
| }; | |
| force.y = function(_) { | |
| return arguments.length ? (y = +_, force) : y; | |
| }; | |
| return force; | |
| }; | |
| var constant = function(x) { | |
| return function() { | |
| return x; | |
| }; | |
| }; | |
| var jiggle = function() { | |
| return (Math.random() - 0.5) * 1e-6; | |
| }; | |
| function x(d) { | |
| return d.x + d.vx; | |
| } | |
| function y(d) { | |
| return d.y + d.vy; | |
| } | |
| var collide = function(radius) { | |
| var nodes, | |
| radii, | |
| strength = 1, | |
| iterations = 1; | |
| if (typeof radius !== "function") radius = constant(radius == null ? 1 : +radius); | |
| function force() { | |
| var i, n = nodes.length, | |
| tree, | |
| node, | |
| xi, | |
| yi, | |
| ri, | |
| ri2; | |
| for (var k = 0; k < iterations; ++k) { | |
| tree = d3Quadtree.quadtree(nodes, x, y).visitAfter(prepare); | |
| for (i = 0; i < n; ++i) { | |
| node = nodes[i]; | |
| ri = radii[node.index], ri2 = ri * ri; | |
| xi = node.x + node.vx; | |
| yi = node.y + node.vy; | |
| tree.visit(apply); | |
| } | |
| } | |
| function apply(quad, x0, y0, x1, y1) { | |
| var data = quad.data, rj = quad.r, r = ri + rj; | |
| if (data) { | |
| if (data.index > node.index) { | |
| var x = xi - data.x - data.vx, | |
| y = yi - data.y - data.vy, | |
| l = x * x + y * y; | |
| if (l < r * r) { | |
| if (x === 0) x = jiggle(), l += x * x; | |
| if (y === 0) y = jiggle(), l += y * y; | |
| l = (r - (l = Math.sqrt(l))) / l * strength; | |
| node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj)); | |
| node.vy += (y *= l) * r; | |
| data.vx -= x * (r = 1 - r); | |
| data.vy -= y * r; | |
| } | |
| } | |
| return; | |
| } | |
| return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r; | |
| } | |
| } | |
| function prepare(quad) { | |
| if (quad.data) return quad.r = radii[quad.data.index]; | |
| for (var i = quad.r = 0; i < 4; ++i) { | |
| if (quad[i] && quad[i].r > quad.r) { | |
| quad.r = quad[i].r; | |
| } | |
| } | |
| } | |
| function initialize() { | |
| if (!nodes) return; | |
| var i, n = nodes.length, node; | |
| radii = new Array(n); | |
| for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes); | |
| } | |
| force.initialize = function(_) { | |
| nodes = _; | |
| initialize(); | |
| }; | |
| force.iterations = function(_) { | |
| return arguments.length ? (iterations = +_, force) : iterations; | |
| }; | |
| force.strength = function(_) { | |
| return arguments.length ? (strength = +_, force) : strength; | |
| }; | |
| force.radius = function(_) { | |
| return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), initialize(), force) : radius; | |
| }; | |
| return force; | |
| }; | |
| function index(d) { | |
| return d.index; | |
| } | |
| function find(nodeById, nodeId) { | |
| var node = nodeById.get(nodeId); | |
| if (!node) throw new Error("missing: " + nodeId); | |
| return node; | |
| } | |
| var link = function(links) { | |
| var id = index, | |
| strength = defaultStrength, | |
| strengths, | |
| distance = constant(30), | |
| distances, | |
| nodes, | |
| count, | |
| bias, | |
| iterations = 1; | |
| if (links == null) links = []; | |
| function defaultStrength(link) { | |
| return 1 / Math.min(count[link.source.index], count[link.target.index]); | |
| } | |
| function force(alpha) { | |
| for (var k = 0, n = links.length; k < iterations; ++k) { | |
| for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) { | |
| link = links[i], source = link.source, target = link.target; | |
| x = target.x + target.vx - source.x - source.vx || jiggle(); | |
| y = target.y + target.vy - source.y - source.vy || jiggle(); | |
| l = Math.sqrt(x * x + y * y); | |
| l = (l - distances[i]) / l * alpha * strengths[i]; | |
| x *= l, y *= l; | |
| target.vx -= x * (b = bias[i]); | |
| target.vy -= y * b; | |
| source.vx += x * (b = 1 - b); | |
| source.vy += y * b; | |
| } | |
| } | |
| } | |
| function initialize() { | |
| if (!nodes) return; | |
| var i, | |
| n = nodes.length, | |
| m = links.length, | |
| nodeById = d3Collection.map(nodes, id), | |
| link; | |
| for (i = 0, count = new Array(n); i < m; ++i) { | |
| link = links[i], link.index = i; | |
| if (typeof link.source !== "object") link.source = find(nodeById, link.source); | |
| if (typeof link.target !== "object") link.target = find(nodeById, link.target); | |
| count[link.source.index] = (count[link.source.index] || 0) + 1; | |
| count[link.target.index] = (count[link.target.index] || 0) + 1; | |
| } | |
| for (i = 0, bias = new Array(m); i < m; ++i) { | |
| link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]); | |
| } | |
| strengths = new Array(m), initializeStrength(); | |
| distances = new Array(m), initializeDistance(); | |
| } | |
| function initializeStrength() { | |
| if (!nodes) return; | |
| for (var i = 0, n = links.length; i < n; ++i) { | |
| strengths[i] = +strength(links[i], i, links); | |
| } | |
| } | |
| function initializeDistance() { | |
| if (!nodes) return; | |
| for (var i = 0, n = links.length; i < n; ++i) { | |
| distances[i] = +distance(links[i], i, links); | |
| } | |
| } | |
| force.initialize = function(_) { | |
| nodes = _; | |
| initialize(); | |
| }; | |
| force.links = function(_) { | |
| return arguments.length ? (links = _, initialize(), force) : links; | |
| }; | |
| force.id = function(_) { | |
| return arguments.length ? (id = _, force) : id; | |
| }; | |
| force.iterations = function(_) { | |
| return arguments.length ? (iterations = +_, force) : iterations; | |
| }; | |
| force.strength = function(_) { | |
| return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initializeStrength(), force) : strength; | |
| }; | |
| force.distance = function(_) { | |
| return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), initializeDistance(), force) : distance; | |
| }; | |
| return force; | |
| }; | |
| function x$1(d) { | |
| return d.x; | |
| } | |
| function y$1(d) { | |
| return d.y; | |
| } | |
| var initialRadius = 10; | |
| var initialAngle = Math.PI * (3 - Math.sqrt(5)); | |
| var simulation = function(nodes) { | |
| var simulation, | |
| alpha = 1, | |
| alphaMin = 0.001, | |
| alphaDecay = 1 - Math.pow(alphaMin, 1 / 300), | |
| alphaTarget = 0, | |
| velocityDecay = 0.6, | |
| forces = d3Collection.map(), | |
| stepper = d3Timer.timer(step), | |
| event = d3Dispatch.dispatch("tick", "end"); | |
| if (nodes == null) nodes = []; | |
| function step() { | |
| tick(); | |
| event.call("tick", simulation); | |
| if (alpha < alphaMin) { | |
| stepper.stop(); | |
| event.call("end", simulation); | |
| } | |
| } | |
| function tick() { | |
| var i, n = nodes.length, node; | |
| alpha += (alphaTarget - alpha) * alphaDecay; | |
| forces.each(function(force) { | |
| force(alpha); | |
| }); | |
| for (i = 0; i < n; ++i) { | |
| node = nodes[i]; | |
| if (node.fx == null) node.x += node.vx *= velocityDecay; | |
| else node.x = node.fx, node.vx = 0; | |
| if (node.fy == null) node.y += node.vy *= velocityDecay; | |
| else node.y = node.fy, node.vy = 0; | |
| } | |
| } | |
| function initializeNodes() { | |
| for (var i = 0, n = nodes.length, node; i < n; ++i) { | |
| node = nodes[i], node.index = i; | |
| if (isNaN(node.x) || isNaN(node.y)) { | |
| var radius = initialRadius * Math.sqrt(i), angle = i * initialAngle; | |
| node.x = radius * Math.cos(angle); | |
| node.y = radius * Math.sin(angle); | |
| } | |
| if (isNaN(node.vx) || isNaN(node.vy)) { | |
| node.vx = node.vy = 0; | |
| } | |
| } | |
| } | |
| function initializeForce(force) { | |
| if (force.initialize) force.initialize(nodes); | |
| return force; | |
| } | |
| initializeNodes(); | |
| return simulation = { | |
| tick: tick, | |
| restart: function() { | |
| return stepper.restart(step), simulation; | |
| }, | |
| stop: function() { | |
| return stepper.stop(), simulation; | |
| }, | |
| nodes: function(_) { | |
| return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes; | |
| }, | |
| alpha: function(_) { | |
| return arguments.length ? (alpha = +_, simulation) : alpha; | |
| }, | |
| alphaMin: function(_) { | |
| return arguments.length ? (alphaMin = +_, simulation) : alphaMin; | |
| }, | |
| alphaDecay: function(_) { | |
| return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay; | |
| }, | |
| alphaTarget: function(_) { | |
| return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget; | |
| }, | |
| velocityDecay: function(_) { | |
| return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay; | |
| }, | |
| force: function(name, _) { | |
| return arguments.length > 1 ? (_ == null ? forces.remove(name) : forces.set(name, initializeForce(_)), simulation) : forces.get(name); | |
| }, | |
| find: function(x, y, radius) { | |
| var i = 0, | |
| n = nodes.length, | |
| dx, | |
| dy, | |
| d2, | |
| node, | |
| closest; | |
| if (radius == null) radius = Infinity; | |
| else radius *= radius; | |
| for (i = 0; i < n; ++i) { | |
| node = nodes[i]; | |
| dx = x - node.x; | |
| dy = y - node.y; | |
| d2 = dx * dx + dy * dy; | |
| if (d2 < radius) closest = node, radius = d2; | |
| } | |
| return closest; | |
| }, | |
| on: function(name, _) { | |
| return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name); | |
| } | |
| }; | |
| }; | |
| var manyBody = function() { | |
| var nodes, | |
| node, | |
| alpha, | |
| strength = constant(-30), | |
| strengths, | |
| distanceMin2 = 1, | |
| distanceMax2 = Infinity, | |
| theta2 = 0.81; | |
| function force(_) { | |
| var i, n = nodes.length, tree = d3Quadtree.quadtree(nodes, x$1, y$1).visitAfter(accumulate); | |
| for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply); | |
| } | |
| function initialize() { | |
| if (!nodes) return; | |
| var i, n = nodes.length, node; | |
| strengths = new Array(n); | |
| for (i = 0; i < n; ++i) node = nodes[i], strengths[node.index] = +strength(node, i, nodes); | |
| } | |
| function accumulate(quad) { | |
| var strength = 0, q, c, weight = 0, x, y, i; | |
| // For internal nodes, accumulate forces from child quadrants. | |
| if (quad.length) { | |
| for (x = y = i = 0; i < 4; ++i) { | |
| if ((q = quad[i]) && (c = Math.abs(q.value))) { | |
| strength += q.value, weight += c, x += c * q.x, y += c * q.y; | |
| } | |
| } | |
| quad.x = x / weight; | |
| quad.y = y / weight; | |
| } | |
| // For leaf nodes, accumulate forces from coincident quadrants. | |
| else { | |
| q = quad; | |
| q.x = q.data.x; | |
| q.y = q.data.y; | |
| do strength += strengths[q.data.index]; | |
| while (q = q.next); | |
| } | |
| quad.value = strength; | |
| } | |
| function apply(quad, x1, _, x2) { | |
| if (!quad.value) return true; | |
| var x = quad.x - node.x, | |
| y = quad.y - node.y, | |
| w = x2 - x1, | |
| l = x * x + y * y; | |
| // Apply the Barnes-Hut approximation if possible. | |
| // Limit forces for very close nodes; randomize direction if coincident. | |
| if (w * w / theta2 < l) { | |
| if (l < distanceMax2) { | |
| if (x === 0) x = jiggle(), l += x * x; | |
| if (y === 0) y = jiggle(), l += y * y; | |
| if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l); | |
| node.vx += x * quad.value * alpha / l; | |
| node.vy += y * quad.value * alpha / l; | |
| } | |
| return true; | |
| } | |
| // Otherwise, process points directly. | |
| else if (quad.length || l >= distanceMax2) return; | |
| // Limit forces for very close nodes; randomize direction if coincident. | |
| if (quad.data !== node || quad.next) { | |
| if (x === 0) x = jiggle(), l += x * x; | |
| if (y === 0) y = jiggle(), l += y * y; | |
| if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l); | |
| } | |
| do if (quad.data !== node) { | |
| w = strengths[quad.data.index] * alpha / l; | |
| node.vx += x * w; | |
| node.vy += y * w; | |
| } while (quad = quad.next); | |
| } | |
| force.initialize = function(_) { | |
| nodes = _; | |
| initialize(); | |
| }; | |
| force.strength = function(_) { | |
| return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength; | |
| }; | |
| force.distanceMin = function(_) { | |
| return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2); | |
| }; | |
| force.distanceMax = function(_) { | |
| return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2); | |
| }; | |
| force.theta = function(_) { | |
| return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2); | |
| }; | |
| return force; | |
| }; | |
| var radial = function(radius, x, y) { | |
| var nodes, | |
| strength = constant(0.1), | |
| strengths, | |
| radiuses; | |
| if (typeof radius !== "function") radius = constant(+radius); | |
| if (x == null) x = 0; | |
| if (y == null) y = 0; | |
| function force(alpha) { | |
| for (var i = 0, n = nodes.length; i < n; ++i) { | |
| var node = nodes[i], | |
| dx = node.x - x || 1e-6, | |
| dy = node.y - y || 1e-6, | |
| r = Math.sqrt(dx * dx + dy * dy), | |
| k = (radiuses[i] - r) * strengths[i] * alpha / r; | |
| node.vx += dx * k; | |
| node.vy += dy * k; | |
| } | |
| } | |
| function initialize() { | |
| if (!nodes) return; | |
| var i, n = nodes.length; | |
| strengths = new Array(n); | |
| radiuses = new Array(n); | |
| for (i = 0; i < n; ++i) { | |
| radiuses[i] = +radius(nodes[i], i, nodes); | |
| strengths[i] = isNaN(radiuses[i]) ? 0 : +strength(nodes[i], i, nodes); | |
| } | |
| } | |
| force.initialize = function(_) { | |
| nodes = _, initialize(); | |
| }; | |
| force.strength = function(_) { | |
| return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength; | |
| }; | |
| force.radius = function(_) { | |
| return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), initialize(), force) : radius; | |
| }; | |
| force.x = function(_) { | |
| return arguments.length ? (x = +_, force) : x; | |
| }; | |
| force.y = function(_) { | |
| return arguments.length ? (y = +_, force) : y; | |
| }; | |
| return force; | |
| }; | |
| var x$2 = function(x) { | |
| var strength = constant(0.1), | |
| nodes, | |
| strengths, | |
| xz; | |
| if (typeof x !== "function") x = constant(x == null ? 0 : +x); | |
| function force(alpha) { | |
| for (var i = 0, n = nodes.length, node; i < n; ++i) { | |
| node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha; | |
| } | |
| } | |
| function initialize() { | |
| if (!nodes) return; | |
| var i, n = nodes.length; | |
| strengths = new Array(n); | |
| xz = new Array(n); | |
| for (i = 0; i < n; ++i) { | |
| strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes); | |
| } | |
| } | |
| force.initialize = function(_) { | |
| nodes = _; | |
| initialize(); | |
| }; | |
| force.strength = function(_) { | |
| return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength; | |
| }; | |
| force.x = function(_) { | |
| return arguments.length ? (x = typeof _ === "function" ? _ : constant(+_), initialize(), force) : x; | |
| }; | |
| return force; | |
| }; | |
| var y$2 = function(y) { | |
| var strength = constant(0.1), | |
| nodes, | |
| strengths, | |
| yz; | |
| if (typeof y !== "function") y = constant(y == null ? 0 : +y); | |
| function force(alpha) { | |
| for (var i = 0, n = nodes.length, node; i < n; ++i) { | |
| node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha; | |
| } | |
| } | |
| function initialize() { | |
| if (!nodes) return; | |
| var i, n = nodes.length; | |
| strengths = new Array(n); | |
| yz = new Array(n); | |
| for (i = 0; i < n; ++i) { | |
| strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes); | |
| } | |
| } | |
| force.initialize = function(_) { | |
| nodes = _; | |
| initialize(); | |
| }; | |
| force.strength = function(_) { | |
| return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength; | |
| }; | |
| force.y = function(_) { | |
| return arguments.length ? (y = typeof _ === "function" ? _ : constant(+_), initialize(), force) : y; | |
| }; | |
| return force; | |
| }; | |
| var boundary = function(x0, y0, x1, y1) { | |
| var strength = constant(0.1), | |
| hardBoundary = true, | |
| border = constant( Math.min((x1 - x0)/2, (y1 - y0)/2) ), | |
| nodes, | |
| strengthsX, | |
| strengthsY, | |
| x0z, x1z, | |
| y0z, y1z, | |
| borderz, | |
| halfX, halfY; | |
| if (typeof x0 !== "function") x0 = constant(x0 == null ? -100 : +x0); | |
| if (typeof x1 !== "function") x1 = constant(x1 == null ? 100 : +x1); | |
| if (typeof y0 !== "function") y0 = constant(y0 == null ? -100 : +y0); | |
| if (typeof y1 !== "function") y1 = constant(y1 == null ? 100 : +y1); | |
| function getVx(halfX, x, strengthX, border, alpha) { | |
| // var targetX = x > halfX ? (x0 + border) : (x1 - border); | |
| return (halfX - x) * Math.min(2, Math.abs( halfX - x) / halfX) * strengthX * alpha; | |
| } | |
| function force(alpha) { | |
| for (var i = 0, n = nodes.length, node; i < n; ++i) { | |
| node = nodes[i]; | |
| // node.vx += (halfX[i] - node.x) * strengthsX[i] * alpha; | |
| // node.vy += (halfY[i] - node.y) * strengthsY[i] * alpha; | |
| // node.vx += (halfX[i] - node.x) * Math.min(2, Math.abs( (halfX[i]-node.x)/halfX[i]*0.1 )) * alpha; | |
| // node.vy += (halfY[i] - node.y) * Math.min(2, Math.abs( (halfY[i]-node.y)/halfY[i]*0.1 )) * alpha; | |
| if ((node.x < (x0z[i] + borderz[i]) || node.x > (x1z[i] - borderz[i])) || | |
| (node.y < (y0z[i] + borderz[i]) || node.y > (y1z[i] - borderz[i])) ) { | |
| node.vx += getVx(halfX[i], node.x, strengthsX[i], borderz[i], alpha); | |
| node.vy += getVx(halfY[i], node.y, strengthsY[i], borderz[i], alpha); | |
| } else { | |
| node.vx = 0; | |
| node.vy = 0; | |
| } | |
| if (hardBoundary) { | |
| if (node.x >= x1z[i]) node.vx += x1z[i] - node.x; | |
| if (node.x <= x0z[i]) node.vx += x0z[i] - node.x; | |
| if (node.y >= y1z[i]) node.vy += y1z[i] - node.y; | |
| if (node.y <= y0z[i]) node.vy += y0z[i] - node.y; | |
| } | |
| } | |
| } | |
| function initialize() { | |
| if (!nodes) return; | |
| var i, n = nodes.length; | |
| strengthsX = new Array(n); | |
| strengthsY = new Array(n); | |
| x0z = new Array(n); | |
| y0z = new Array(n); | |
| x1z = new Array(n); | |
| y1z = new Array(n); | |
| halfY = new Array(n); | |
| halfX = new Array(n); | |
| borderz = new Array(n); | |
| for (i = 0; i < n; ++i) { | |
| strengthsX[i] = (isNaN(x0z[i] = +x0(nodes[i], i, nodes)) || | |
| isNaN(x1z[i] = +x1(nodes[i], i, nodes))) ? 0 : +strength(nodes[i], i, nodes); | |
| strengthsY[i] = (isNaN(y0z[i] = +y0(nodes[i], i, nodes)) || | |
| isNaN(y1z[i] = +y1(nodes[i], i, nodes))) ? 0 : +strength(nodes[i], i, nodes); | |
| halfX[i] = x0z[i] + (x1z[i] - x0z[i])/2, halfY[i] = y0z[i] + (y1z[i] - y0z[i])/2; | |
| borderz[i] = +border(nodes[i], i, nodes); | |
| } | |
| } | |
| force.initialize = function(_) { | |
| nodes = _; | |
| initialize(); | |
| }; | |
| force.x0 = function(_) { | |
| return arguments.length ? (x0 = typeof _ === "function" ? _ : constant(+_), initialize(), force) : x0; | |
| }; | |
| force.x1 = function(_) { | |
| return arguments.length ? (x1 = typeof _ === "function" ? _ : constant(+_), initialize(), force) : x1; | |
| }; | |
| force.y0 = function(_) { | |
| return arguments.length ? (y0 = typeof _ === "function" ? _ : constant(+_), initialize(), force) : y0; | |
| }; | |
| force.y1 = function(_) { | |
| return arguments.length ? (y1 = typeof _ === "function" ? _ : constant(+_), initialize(), force) : y1; | |
| }; | |
| force.strength = function(_) { | |
| return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength; | |
| }; | |
| force.border = function(_) { | |
| return arguments.length ? (border = typeof _ === "function" ? _ : constant(+_), initialize(), force) : border; | |
| }; | |
| force.hardBoundary = function(_) { | |
| return arguments.length ? (hardBoundary = _, force) : hardBoundary; | |
| }; | |
| return force; | |
| }; | |
| exports.forceCenter = center; | |
| exports.forceCollide = collide; | |
| exports.forceLink = link; | |
| exports.forceManyBody = manyBody; | |
| exports.forceRadial = radial; | |
| exports.forceSimulation = simulation; | |
| exports.forceX = x$2; | |
| exports.forceY = y$2; | |
| exports.forceBoundary = boundary; | |
| Object.defineProperty(exports, '__esModule', { value: true }); | |
| }))); |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Document</title> | |
| </head> | |
| <body> | |
| <canvas width=960 height=420></canvas> | |
| <script src="http://d3js.org/d3.v4.min.js"></script> | |
| <script src="d3-force.js"></script> | |
| <script> | |
| /* global d3 */ | |
| var canvas = d3.select("canvas").node(), | |
| ctx= canvas.getContext("2d"), | |
| width = canvas.width, | |
| height = canvas.height, | |
| r=2, | |
| x0 = 0, | |
| y0 = 0, | |
| x1 = width-r, | |
| y1 = height-r, | |
| strengthBoundary = 0.3, | |
| border = 100; | |
| var boundary = d3.forceBoundary(x0, y0, x1, y1) | |
| .strength(strengthBoundary) | |
| .border(border); | |
| function getVx(halfX, x, strengthX, border, alpha) { | |
| return (halfX - x) * Math.min(2, Math.abs( halfX - x) / halfX) * strengthX * alpha; | |
| } | |
| boundary.visualizeStrength = function(ctx, _alpha) { | |
| var x, y, vx, vy, step=20, | |
| line, | |
| halfX = x0 + (x1 - x0)/2, | |
| halfY = y0 + (y1 - y0)/2, | |
| alpha = _alpha !== undefined ? _alpha : 0.7; | |
| ctx.save(); | |
| ctx.globalAlpha= 0.3; | |
| for (x = x0; x < x1 ; x += step) { | |
| for (y = y0; y <y1 ; y += step) { | |
| if ((x < (x0 + border) || x > (x1 - border)) || | |
| (y < (y0 + border) || y > (y1 - border)) ) { | |
| vx = getVx(halfX, x, strengthBoundary, border, alpha); | |
| vy = getVx(halfY, y, strengthBoundary, border, alpha); | |
| line = new Line(x, y, x+vx, y+vy); | |
| line.drawWithArrowheads(ctx); | |
| } | |
| } | |
| } | |
| ctx.restore(); | |
| } | |
| function Line(x1,y1,x2,y2){ | |
| this.x1=x1; | |
| this.y1=y1; | |
| this.x2=x2; | |
| this.y2=y2; | |
| } | |
| Line.prototype.drawWithArrowheads=function(ctx){ | |
| // arbitrary styling | |
| ctx.strokeStyle="steelblue"; | |
| ctx.fillStyle="steelblue"; | |
| ctx.lineWidth=1; | |
| // draw the line | |
| ctx.beginPath(); | |
| ctx.moveTo(this.x1,this.y1); | |
| ctx.lineTo(this.x2,this.y2); | |
| ctx.stroke(); | |
| // // draw the starting arrowhead | |
| // var startRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1)); | |
| // startRadians+=((this.x2>this.x1)?-90:90)*Math.PI/180; | |
| // this.drawArrowhead(ctx,this.x1,this.y1,startRadians); | |
| // draw the ending arrowhead | |
| var endRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1)); | |
| endRadians+=((this.x2 >= this.x1)?90:-90)*Math.PI/180; | |
| this.drawArrowhead(ctx,this.x2,this.y2,endRadians); | |
| } | |
| Line.prototype.drawArrowhead=function(ctx,x,y,radians){ | |
| ctx.save(); | |
| ctx.beginPath(); | |
| ctx.translate(x,y); | |
| ctx.rotate(radians); | |
| ctx.moveTo(0,0); | |
| ctx.lineTo(2,5); | |
| ctx.lineTo(-2,5); | |
| ctx.closePath(); | |
| ctx.restore(); | |
| ctx.fill(); | |
| } | |
| d3.json("https://gist.githubusercontent.com/john-guerra/ef11d9fcf36e4d6a35be583aa69ac95b/raw/0fbedcebdaa1854cc1b89d86e2d8174520f6ec2d/coauthorshipNetwork2015.json", function (err, graph) { | |
| if (err) throw err; | |
| const simulation = d3.forceSimulation(graph.nodes) | |
| // .force("x", d3.forceX(width/2).strength(0.1)) | |
| // .force("y", d3.forceY(height/2).strength(0.1)) | |
| .force("boundary", boundary) | |
| // .force("collide", d3.forceCollide(r)) | |
| // .force("charge", d3.forceManyBody().strength(-5)) | |
| // .force("link", d3.forceLink(graph.links).distance(10)) | |
| .on("tick", ticked); | |
| // boundary.visualizeStrength(ctx, simulation.alpha()); | |
| function ticked() { | |
| ctx.clearRect(0, 0, width, height); | |
| boundary.visualizeStrength(ctx, simulation.alpha()); | |
| ctx.save(); | |
| ctx.strokeStyle="rgba(100,100,100,0.3)"; | |
| ctx.beginPath(); | |
| for (let l of graph.links) { | |
| ctx.moveTo(l.source.x, l.source.y); | |
| ctx.lineTo(l.target.x, l.target.y); | |
| } | |
| ctx.stroke(); | |
| ctx.fillStyle="steelblue"; | |
| ctx.beginPath(); | |
| for (let n of graph.nodes) { | |
| ctx.moveTo(n.x, n.y); | |
| ctx.arc(n.x+r/2, n.y+r/2, r, 0, 2 * Math.PI); | |
| } | |
| ctx.fill(); | |
| ctx.restore(); | |
| } // ticked | |
| }); | |
| </script> | |
| </body> | |
| </html> |