Skip to content

Instantly share code, notes, and snippets.

@razum2um
Last active October 30, 2017 12:49
Show Gist options
  • Save razum2um/8a4021efdcb9c7b8c4ab685763b6a68b to your computer and use it in GitHub Desktop.
Save razum2um/8a4021efdcb9c7b8c4ab685763b6a68b to your computer and use it in GitHub Desktop.
More bounding box collide
// https://d3js.org/d3-force/ Version 1.0.0. Copyright 2016 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';
function center(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;
}
function constant(x) {
return function() {
return x;
};
}
function jiggle() {
return (Math.random() - 0.5) * 1e-6;
}
function x(d) {
return d.x + d.vx;
}
function y(d) {
return d.y + d.vy;
}
function collide(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[i], 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 > i) {
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;
}
}
}
force.initialize = function(_) {
var i, n = (nodes = _).length; radii = new Array(n);
for (i = 0; i < n; ++i) radii[i] = +radius(nodes[i], i, nodes);
};
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(+_), force) : radius;
};
return force;
}
function x$1(d) {
return d.x + d.vx;
}
function y$1(d) {
return d.y + d.vy;
}
function rectCollide(bbox) {
var nodes,
boundingBoxes,
strength = 1,
iterations = 1;
if (typeof bbox !== "function") bbox = constant(bbox == null ? [[0,0][1,1]] : bbox);
function force() {
var i,
n = nodes.length,
tree,
node,
xi,
yi,
bbi,
nx1,
ny1,
nx2,
ny2
for (var k = 0; k < iterations; ++k) {
tree = d3Quadtree.quadtree(nodes, x$1, y$1).visitAfter(prepare);
for (i = 0; i < n; ++i) {
var node = nodes[i];
bbi = boundingBoxes[i],
xi = node.x + node.vx;
yi = node.y + node.vy;
nx1 = xi + bbi[0][0]
ny1 = yi + bbi[0][1]
nx2 = xi + bbi[1][0]
ny2 = yi + bbi[1][1]
tree.visit(apply);
}
}
function apply(quad, x0, y0, x1, y1) {
var data = quad.data,
bWidth = bbLength(bbi, 0),
bHeight = bbLength(bbi, 1);
if (data) {
if (data.index > ~~(i / 4)) {
var bbj = boundingBoxes[data.index],
dnx1 = data.x + data.vx + bbj[0][0],
dny1 = data.y + data.vy + bbj[0][1],
dnx2 = data.x + data.vx + bbj[1][0],
dny2 = data.y + data.vy + bbj[1][1],
dWidth = bbLength(bbj, 0),
dHeight = bbLength(bbj, 1)
if (nx1 <= dnx2 && dnx1 <= nx2 && ny1 <= dny2 && dny1 <= ny2) {
var xSize = d3.extent([dnx1, dnx2, nx1, nx2])
var ySize = d3.extent([dny1, dny2, ny1, ny2])
var xOverlap = (bWidth + dWidth) - (xSize[1] - xSize[0])
var yOverlap = (bHeight + dHeight) - (ySize[1] - ySize[0])
var xBPush = xOverlap * strength * (xOverlap / bWidth)
var yBPush = yOverlap * strength * (yOverlap / bHeight)
var xDPush = xOverlap * strength * (xOverlap / dWidth)
var yDPush = yOverlap * strength * (yOverlap / dHeight)
if (xOverlap / bWidth < xOverlap / dWidth) {
node.vx -= xBPush
data.vx += xDPush
}
else {
node.vx += xBPush
data.vx -= xDPush
}
if ( yOverlap / bHeight < yOverlap / dHeight) {
node.vy -= yBPush
data.vy += yDPush
}
else {
node.vy += yBPush
data.vy -= yDPush
}
}
}
return;
}
return x0 > nx2 || x1 < nx1 || y0 > ny2 || y1 < ny1;
}
}
function prepare(quad) {
if (quad.data) return quad.bb = boundingBoxes[quad.data.index];
for (var i = quad.bb = [[0,0],[0,0]]; i < 4; ++i) {
if (quad[i] && bbArea(quad[i].bb) > bbArea(quad.bb)) {
quad.bb = quad[i].bb;
}
}
}
function bbArea(bbox) {
return (bbox[1][0] - bbox[0][0]) * (bbox[1][1] - bbox[0][1])
}
function bbLength(bbox, heightWidth) {
return (bbox[1][heightWidth] - bbox[0][heightWidth])
}
force.initialize = function(_) {
var i, n = (nodes = _).length; boundingBoxes = new Array(n);
for (i = 0; i < n; ++i) boundingBoxes[i] = bbox(nodes[i], i, nodes);
};
force.iterations = function(_) {
return arguments.length ? (iterations = +_, force) : iterations;
};
force.strength = function(_) {
return arguments.length ? (strength = +_, force) : strength;
};
force.bbox = function(_) {
return arguments.length ? (bbox = typeof _ === "function" ? _ : constant(+_), force) : bbox;
};
return force;
}
function index(d, i) {
return i;
}
function link(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 < n; ++i) {
count[i] = 0;
}
for (i = 0; i < m; ++i) {
link = links[i], link.index = i;
if (typeof link.source !== "object") link.source = nodeById.get(link.source);
if (typeof link.target !== "object") link.target = nodeById.get(link.target);
++count[link.source.index], ++count[link.target.index];
}
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$2(d) {
return d.x;
}
function y$2(d) {
return d.y;
}
var initialRadius = 10;
var initialAngle = Math.PI * (3 - Math.sqrt(5));
function simulation(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);
}
};
}
function manyBody() {
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$2, y$2).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;
strengths = new Array(n);
for (i = 0; i < n; ++i) strengths[i] = +strength(nodes[i], i, nodes);
}
function accumulate(quad) {
var strength = 0, q, c, 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 = q.value)) {
strength += c, x += c * q.x, y += c * q.y;
}
}
quad.x = x / strength;
quad.y = y / strength;
}
// 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;
}
function x$3(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;
}
function y$3(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;
}
exports.forceCenter = center;
exports.forceCollide = collide;
exports.forceRectCollide = rectCollide;
exports.forceLink = link;
exports.forceManyBody = manyBody;
exports.forceSimulation = simulation;
exports.forceX = x$3;
exports.forceY = y$3;
Object.defineProperty(exports, '__esModule', { value: true });
}));
<html>
<head>
<title>d3v4 Simple Word Cloud</title>
<meta charset="utf-8" />
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="d3-force.js"></script>
</head>
<style>
svg {
height: 768px;
width: 768px;
border: 1px solid gray;
}
</style>
<body>
<div id="viz">
<svg class="main">
<defs>
<linearGradient id="green">
<stop offset="0%" stop-color="#92E6CB"/>
<stop offset="100%" stop-color="#61D3A5"/>
</linearGradient>
<linearGradient id="yellow">
<stop offset="0%" stop-color="#FFB140"/>
<stop offset="100%" stop-color="#E19930"/>
</linearGradient>
<linearGradient id="red">
<stop offset="0%" stop-color="#EE6767"/>
<stop offset="100%" stop-color="#D14C4C"/>
</linearGradient>
<linearGradient id="gray">
<stop offset="0%" stop-color="#8D98A2"/>
<stop offset="100%" stop-color="#76818C"/>
</linearGradient>
<marker id="green-end" viewBox="0 -5 10 10" refX="-30" refY="0" markerWidth="6" markerHeight="6" orient="auto">
<path d="M0,-5L10,0L0,5" fill="#8BE3C5"></path>
</marker>
<marker id="yellow-end" viewBox="0 -5 10 10" refX="-30" refY="0" markerWidth="6" markerHeight="6" orient="auto">
<path d="M0,-5L10,0L0,5" fill="#FFB140"></path>
</marker>
<marker id="red-end" viewBox="0 -5 10 10" refX="-30" refY="0" markerWidth="6" markerHeight="6" orient="auto">
<path d="M0,-5L10,0L0,5" fill="#E55B5A"></path>
</marker>
<marker id="green-start" viewBox="0 -5 10 10" refX="-30" refY="0" markerWidth="6" markerHeight="6" orient="auto">
<path d="M0,-5L10,0L0,5" fill="#8BE3C5"></path>
</marker>
<filter id="dropshadow" filterUnits="userSpaceOnUse">
<feGaussianBlur in="SourceAlpha" stdDeviation="20">
</feGaussianBlur>
<feOffset dx="0" dy="20"></feOffset>
<feComponentTransfer>
<feFuncA type="linear" slope="0.3"></feFuncA>
</feComponentTransfer>
<feMerge>
<feMergeNode></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
</defs>
</svg>
</div>
</body>
<footer>
<script>
MARKER_GREEN = '#8BE3C5';
MARKER_YELLOW = '#FFB140'
MARKER_RED = '#E55B5A'
function startMarkerColor(idx) {
switch (idx % 4) {
case 1: return 'url(#green-start)'
case 2: return null
case 3: return null
default: return null
}
}
function endMarkerColor(idx) {
switch (idx % 4) {
case 1: return null
case 2: return 'url(#yellow-end)'
case 3: return 'url(#red-end)'
default: return null
}
}
function lineColor(idx) {
switch (idx % 4) {
case 1: return MARKER_GREEN
case 2: return MARKER_YELLOW
case 3: return MARKER_RED
default: return null
}
}
function nodeColor(node, idx) {
// console.log('node=', node)
switch (idx % 4) {
case 1: return 'url(#green)'
case 2: return 'url(#yellow)'
case 3: return 'url(#red)'
default: return 'url(#gray)'
}
}
const HEIGHT = 768;
const WIDTH = 768;
const STRONG = 390;
const USER_CENTER_X = WIDTH / 2
const USER_CENTER_Y = 10 + STRONG / 2
const GREEN = '#61D3A5';
const GRAY = '#8D98A2';
const FONT = 'Verdana';
const USER_RADIUS = 40;
const svg = d3.select("svg.main")
const colors = d3.scaleOrdinal(d3.schemeCategory20)
const total = 20
const nodes = d3.range(total).map(i => {
let n = { index: i, r: 30, x: WIDTH / 2, y: HEIGHT, name: `N${i}` }
if (i == 0) n.r = USER_RADIUS
return n
});
const links = d3.range(total).map(i => {
let n
if ((i % 4) == 1) {
n = { source: i, target: 0, value: i }
} else {
n = { source: 0, target: i, value: i }
}
return n
});
// scales
const vScale = d3.scaleLinear()
.domain([0, total])
.range([USER_RADIUS, WIDTH / 2])
// svg
const outerG = svg.append('g')
.attr('transform',`translate(${WIDTH/2},${HEIGHT/2})`)
const outerCirle = outerG.append('circle')
.attr('x', WIDTH / 2)
.attr('y', HEIGHT / 2)
.attr('r', WIDTH / 2)
.attr('fill', 'none')
.attr('stroke', '#8D98A2')
.attr('stroke-opacity', '0.2')
const outerText = outerG.append('text')
.text('Weak')
.attr('text-anchor', 'middle')
.attr('y', HEIGHT / 6)
.attr('fill', GRAY)
.attr('style', `font-family: ${FONT}; font-size: 16px;`)
//
const innerG = svg.append('g')
.attr('transform', `translate(${USER_CENTER_X},${USER_CENTER_Y})`)
const innerCircle = innerG.append('circle')
.attr('x', WIDTH / 2)
.attr('y', HEIGHT / 2)
.attr('r', STRONG / 2)
.attr('fill', GREEN)
.attr('fill-opacity', '0.1')
.attr('stroke', GREEN)
.attr('stroke-opacity', '0.8')
const innerText = innerG.append('text')
.text('Strong')
.attr('text-anchor', 'middle')
.attr('y', 110)
.attr('fill', GREEN)
.attr('style', `font-family: ${FONT}; font-size: 16px;`)
//
const centerForce = d3.forceCenter(WIDTH / 2, HEIGHT / 2)
const forceX = d3.forceX(USER_CENTER_X).strength(0.005)
const forceY = d3.forceY(USER_CENTER_Y).strength(0.005)
const collideForce = d3.forceCollide().radius(70).strength(0.2).iterations(100)
const bodyForce = d3.forceManyBody().strength(-0.2)
const linkForce = d3.forceLink().strength(0.3).iterations(100).distance(l => vScale(l.value))
const simulation = d3.forceSimulation()
.velocityDecay(0.9)
.force('charge', bodyForce)
.force('collide', collideForce)
.force('center', centerForce)
.force('links', linkForce)
.force('forceX', forceX)
.force('forceY', forceY)
const nodeCirles = svg.append("g")
.attr("class", "nodes")
.selectAll('circle')
.filter('.friend')
.data(nodes)
.enter().append('circle')
.attr('r', d => d.r)
.attr('class', 'friend')
.attr('fill', (d, i) => nodeColor(d, i))
.attr('style', 'filter: url("#dropshadow")')
const nodeLinks = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(links)
.enter().append("line")
.attr("marker-start", (d, i) => startMarkerColor(i))
.attr("marker-end", (d, i) => endMarkerColor(i))
.attr("stroke-width", d => 2)
// .style("stroke", (d, i) => lineColor(i))
const nodeLabels = svg.selectAll("text")
.filter('.node-label')
.data(nodes)
.enter()
.append("text")
.attr('class', 'node-label')
.text(function (d) { return d.name; })
.attr('fill', '#fff')
.attr('style', `font-family: ${FONT}; font-size: 16px;`)
simulation
.nodes(nodes)
.on('tick', ticked)
simulation.force("links").links(links)
// let mutex = true
function ticked (e) {
nodeLinks
.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 });
nodeCirles
.attr('cx', d => {
// if (d.index == 1 && mutex) {
// console.log(d)
// mutex = false;
// }
return d.x
})
.attr('cy', d => d.y)
nodeLabels
.attr("x", function(d){ return d.x })
.attr("y", function (d) {return d.y + 5.5; })
.style("text-anchor", "middle")
nodes[0].x = USER_CENTER_X
nodes[0].y = USER_CENTER_Y
}
</script>
</footer>
</html>
text frequency
layout 63
function 61
data 47
return 36
attr 29
chart 28
array 24
style 24
layouts 22
values 22
need 21
nodes 21
pie 21
use 21
figure 20
circle 19
we'll 19
zoom 19
append 17
elements 17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment