three-forcegraph
Version:
Force-directed graph as a ThreeJS 3d object
1,632 lines (1,409 loc) • 274 kB
JavaScript
// Version 1.43.0 three-forcegraph - https://github.com/vasturiano/three-forcegraph
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('three')) :
typeof define === 'function' && define.amd ? define(['three'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ThreeForceGraph = factory(global.THREE));
})(this, (function (three$2) { 'use strict';
function _arrayLikeToArray$2(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
function _arrayWithHoles$2(r) {
if (Array.isArray(r)) return r;
}
function _arrayWithoutHoles$1(r) {
if (Array.isArray(r)) return _arrayLikeToArray$2(r);
}
function _assertClassBrand$1(e, t, n) {
if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n;
throw new TypeError("Private element is not present on this object");
}
function _assertThisInitialized(e) {
if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
return e;
}
function _callSuper(t, o, e) {
return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e));
}
function _checkPrivateRedeclaration$1(e, t) {
if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object");
}
function _classCallCheck$2(a, n) {
if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
}
function _classPrivateFieldGet2$1(s, a) {
return s.get(_assertClassBrand$1(s, a));
}
function _classPrivateFieldInitSpec$1(e, t, a) {
_checkPrivateRedeclaration$1(e, t), t.set(e, a);
}
function _classPrivateFieldSet2$1(s, a, r) {
return s.set(_assertClassBrand$1(s, a), r), r;
}
function _construct(t, e, r) {
if (_isNativeReflectConstruct()) return Reflect.construct.apply(null, arguments);
var o = [null];
o.push.apply(o, e);
var p = new (t.bind.apply(t, o))();
return p;
}
function _defineProperties$1(e, r) {
for (var t = 0; t < r.length; t++) {
var o = r[t];
o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey$1(o.key), o);
}
}
function _createClass$2(e, r, t) {
return r && _defineProperties$1(e.prototype, r), Object.defineProperty(e, "prototype", {
writable: false
}), e;
}
function _defineProperty(e, r, t) {
return (r = _toPropertyKey$1(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: true,
configurable: true,
writable: true
}) : e[r] = t, e;
}
function _get() {
return _get = "undefined" != typeof Reflect && Reflect.get ? Reflect.get.bind() : function (e, t, r) {
var p = _superPropBase(e, t);
if (p) {
var n = Object.getOwnPropertyDescriptor(p, t);
return n.get ? n.get.call(arguments.length < 3 ? e : r) : n.value;
}
}, _get.apply(null, arguments);
}
function _getPrototypeOf(t) {
return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) {
return t.__proto__ || Object.getPrototypeOf(t);
}, _getPrototypeOf(t);
}
function _inherits(t, e) {
if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function");
t.prototype = Object.create(e && e.prototype, {
constructor: {
value: t,
writable: true,
configurable: true
}
}), Object.defineProperty(t, "prototype", {
writable: false
}), e && _setPrototypeOf(t, e);
}
function _isNativeReflectConstruct() {
try {
var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
} catch (t) {}
return (_isNativeReflectConstruct = function () {
return !!t;
})();
}
function _iterableToArray$1(r) {
if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
}
function _iterableToArrayLimit$2(r, l) {
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = true,
o = false;
try {
if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
} catch (r) {
o = true, n = r;
} finally {
try {
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
} finally {
if (o) throw n;
}
}
return a;
}
}
function _nonIterableRest$2() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _nonIterableSpread$1() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function ownKeys(e, r) {
var t = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var o = Object.getOwnPropertySymbols(e);
r && (o = o.filter(function (r) {
return Object.getOwnPropertyDescriptor(e, r).enumerable;
})), t.push.apply(t, o);
}
return t;
}
function _objectSpread2(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2 ? ownKeys(Object(t), true).forEach(function (r) {
_defineProperty(e, r, t[r]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
});
}
return e;
}
function _possibleConstructorReturn(t, e) {
if (e && ("object" == typeof e || "function" == typeof e)) return e;
if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined");
return _assertThisInitialized(t);
}
function _setPrototypeOf(t, e) {
return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) {
return t.__proto__ = e, t;
}, _setPrototypeOf(t, e);
}
function _slicedToArray$2(r, e) {
return _arrayWithHoles$2(r) || _iterableToArrayLimit$2(r, e) || _unsupportedIterableToArray$2(r, e) || _nonIterableRest$2();
}
function _superPropBase(t, o) {
for (; !{}.hasOwnProperty.call(t, o) && null !== (t = _getPrototypeOf(t)););
return t;
}
function _superPropGet(t, o, e, r) {
var p = _get(_getPrototypeOf(t.prototype ), o, e);
return "function" == typeof p ? function (t) {
return p.apply(e, t);
} : p;
}
function _toConsumableArray$1(r) {
return _arrayWithoutHoles$1(r) || _iterableToArray$1(r) || _unsupportedIterableToArray$2(r) || _nonIterableSpread$1();
}
function _toPrimitive$1(t, r) {
if ("object" != typeof t || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r);
if ("object" != typeof i) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function _toPropertyKey$1(t) {
var i = _toPrimitive$1(t, "string");
return "symbol" == typeof i ? i : i + "";
}
function _typeof$1(o) {
"@babel/helpers - typeof";
return _typeof$1 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof$1(o);
}
function _unsupportedIterableToArray$2(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray$2(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray$2(r, a) : void 0;
}
}
function d3ForceCenter(x, y, z) {
var nodes, strength = 1;
if (x == null) x = 0;
if (y == null) y = 0;
if (z == null) z = 0;
function force() {
var i,
n = nodes.length,
node,
sx = 0,
sy = 0,
sz = 0;
for (i = 0; i < n; ++i) {
node = nodes[i], sx += node.x || 0, sy += node.y || 0, sz += node.z || 0;
}
for (sx = (sx / n - x) * strength, sy = (sy / n - y) * strength, sz = (sz / n - z) * strength, i = 0; i < n; ++i) {
node = nodes[i];
if (sx) { node.x -= sx; }
if (sy) { node.y -= sy; }
if (sz) { node.z -= sz; }
}
}
force.initialize = function(_) {
nodes = _;
};
force.x = function(_) {
return arguments.length ? (x = +_, force) : x;
};
force.y = function(_) {
return arguments.length ? (y = +_, force) : y;
};
force.z = function(_) {
return arguments.length ? (z = +_, force) : z;
};
force.strength = function(_) {
return arguments.length ? (strength = +_, force) : strength;
};
return force;
}
function tree_add$2(d) {
const x = +this._x.call(null, d);
return add$2(this.cover(x), x, d);
}
function add$2(tree, x, d) {
if (isNaN(x)) return tree; // ignore invalid points
var parent,
node = tree._root,
leaf = {data: d},
x0 = tree._x0,
x1 = tree._x1,
xm,
xp,
right,
i,
j;
// If the tree is empty, initialize the root as a leaf.
if (!node) return tree._root = leaf, tree;
// Find the existing leaf for the new point, or add it.
while (node.length) {
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
if (parent = node, !(node = node[i = +right])) return parent[i] = leaf, tree;
}
// Is the new point is exactly coincident with the existing point?
xp = +tree._x.call(null, node.data);
if (x === xp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;
// Otherwise, split the leaf node until the old and new point are separated.
do {
parent = parent ? parent[i] = new Array(2) : tree._root = new Array(2);
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
} while ((i = +right) === (j = +(xp >= xm)));
return parent[j] = node, parent[i] = leaf, tree;
}
function addAll$2(data) {
if (!Array.isArray(data)) data = Array.from(data);
const n = data.length;
const xz = new Float64Array(n);
let x0 = Infinity,
x1 = -Infinity;
// Compute the points and their extent.
for (let i = 0, x; i < n; ++i) {
if (isNaN(x = +this._x.call(null, data[i]))) continue;
xz[i] = x;
if (x < x0) x0 = x;
if (x > x1) x1 = x;
}
// If there were no (valid) points, abort.
if (x0 > x1) return this;
// Expand the tree to cover the new points.
this.cover(x0).cover(x1);
// Add the new points.
for (let i = 0; i < n; ++i) {
add$2(this, xz[i], data[i]);
}
return this;
}
function tree_cover$2(x) {
if (isNaN(x = +x)) return this; // ignore invalid points
var x0 = this._x0,
x1 = this._x1;
// If the binarytree has no extent, initialize them.
// Integer extent are necessary so that if we later double the extent,
// the existing half boundaries don’t change due to floating point error!
if (isNaN(x0)) {
x1 = (x0 = Math.floor(x)) + 1;
}
// Otherwise, double repeatedly to cover.
else {
var z = x1 - x0 || 1,
node = this._root,
parent,
i;
while (x0 > x || x >= x1) {
i = +(x < x0);
parent = new Array(2), parent[i] = node, node = parent, z *= 2;
switch (i) {
case 0: x1 = x0 + z; break;
case 1: x0 = x1 - z; break;
}
}
if (this._root && this._root.length) this._root = node;
}
this._x0 = x0;
this._x1 = x1;
return this;
}
function tree_data$2() {
var data = [];
this.visit(function(node) {
if (!node.length) do data.push(node.data); while (node = node.next)
});
return data;
}
function tree_extent$2(_) {
return arguments.length
? this.cover(+_[0][0]).cover(+_[1][0])
: isNaN(this._x0) ? undefined : [[this._x0], [this._x1]];
}
function Half(node, x0, x1) {
this.node = node;
this.x0 = x0;
this.x1 = x1;
}
function tree_find$2(x, radius) {
var data,
x0 = this._x0,
x1,
x2,
x3 = this._x1,
halves = [],
node = this._root,
q,
i;
if (node) halves.push(new Half(node, x0, x3));
if (radius == null) radius = Infinity;
else {
x0 = x - radius;
x3 = x + radius;
}
while (q = halves.pop()) {
// Stop searching if this half can’t contain a closer node.
if (!(node = q.node)
|| (x1 = q.x0) > x3
|| (x2 = q.x1) < x0) continue;
// Bisect the current half.
if (node.length) {
var xm = (x1 + x2) / 2;
halves.push(
new Half(node[1], xm, x2),
new Half(node[0], x1, xm)
);
// Visit the closest half first.
if (i = +(x >= xm)) {
q = halves[halves.length - 1];
halves[halves.length - 1] = halves[halves.length - 1 - i];
halves[halves.length - 1 - i] = q;
}
}
// Visit this point. (Visiting coincident points isn’t necessary!)
else {
var d = Math.abs(x - +this._x.call(null, node.data));
if (d < radius) {
radius = d;
x0 = x - d;
x3 = x + d;
data = node.data;
}
}
}
return data;
}
function tree_remove$2(d) {
if (isNaN(x = +this._x.call(null, d))) return this; // ignore invalid points
var parent,
node = this._root,
retainer,
previous,
next,
x0 = this._x0,
x1 = this._x1,
x,
xm,
right,
i,
j;
// If the tree is empty, initialize the root as a leaf.
if (!node) return this;
// Find the leaf node for the point.
// While descending, also retain the deepest parent with a non-removed sibling.
if (node.length) while (true) {
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
if (!(parent = node, node = node[i = +right])) return this;
if (!node.length) break;
if (parent[(i + 1) & 1]) retainer = parent, j = i;
}
// Find the point to remove.
while (node.data !== d) if (!(previous = node, node = node.next)) return this;
if (next = node.next) delete node.next;
// If there are multiple coincident points, remove just the point.
if (previous) return (next ? previous.next = next : delete previous.next), this;
// If this is the root point, remove it.
if (!parent) return this._root = next, this;
// Remove this leaf.
next ? parent[i] = next : delete parent[i];
// If the parent now contains exactly one leaf, collapse superfluous parents.
if ((node = parent[0] || parent[1])
&& node === (parent[1] || parent[0])
&& !node.length) {
if (retainer) retainer[j] = node;
else this._root = node;
}
return this;
}
function removeAll$2(data) {
for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);
return this;
}
function tree_root$2() {
return this._root;
}
function tree_size$2() {
var size = 0;
this.visit(function(node) {
if (!node.length) do ++size; while (node = node.next)
});
return size;
}
function tree_visit$2(callback) {
var halves = [], q, node = this._root, child, x0, x1;
if (node) halves.push(new Half(node, this._x0, this._x1));
while (q = halves.pop()) {
if (!callback(node = q.node, x0 = q.x0, x1 = q.x1) && node.length) {
var xm = (x0 + x1) / 2;
if (child = node[1]) halves.push(new Half(child, xm, x1));
if (child = node[0]) halves.push(new Half(child, x0, xm));
}
}
return this;
}
function tree_visitAfter$2(callback) {
var halves = [], next = [], q;
if (this._root) halves.push(new Half(this._root, this._x0, this._x1));
while (q = halves.pop()) {
var node = q.node;
if (node.length) {
var child, x0 = q.x0, x1 = q.x1, xm = (x0 + x1) / 2;
if (child = node[0]) halves.push(new Half(child, x0, xm));
if (child = node[1]) halves.push(new Half(child, xm, x1));
}
next.push(q);
}
while (q = next.pop()) {
callback(q.node, q.x0, q.x1);
}
return this;
}
function defaultX$2(d) {
return d[0];
}
function tree_x$2(_) {
return arguments.length ? (this._x = _, this) : this._x;
}
function binarytree(nodes, x) {
var tree = new Binarytree(x == null ? defaultX$2 : x, NaN, NaN);
return nodes == null ? tree : tree.addAll(nodes);
}
function Binarytree(x, x0, x1) {
this._x = x;
this._x0 = x0;
this._x1 = x1;
this._root = undefined;
}
function leaf_copy$2(leaf) {
var copy = {data: leaf.data}, next = copy;
while (leaf = leaf.next) next = next.next = {data: leaf.data};
return copy;
}
var treeProto$2 = binarytree.prototype = Binarytree.prototype;
treeProto$2.copy = function() {
var copy = new Binarytree(this._x, this._x0, this._x1),
node = this._root,
nodes,
child;
if (!node) return copy;
if (!node.length) return copy._root = leaf_copy$2(node), copy;
nodes = [{source: node, target: copy._root = new Array(2)}];
while (node = nodes.pop()) {
for (var i = 0; i < 2; ++i) {
if (child = node.source[i]) {
if (child.length) nodes.push({source: child, target: node.target[i] = new Array(2)});
else node.target[i] = leaf_copy$2(child);
}
}
}
return copy;
};
treeProto$2.add = tree_add$2;
treeProto$2.addAll = addAll$2;
treeProto$2.cover = tree_cover$2;
treeProto$2.data = tree_data$2;
treeProto$2.extent = tree_extent$2;
treeProto$2.find = tree_find$2;
treeProto$2.remove = tree_remove$2;
treeProto$2.removeAll = removeAll$2;
treeProto$2.root = tree_root$2;
treeProto$2.size = tree_size$2;
treeProto$2.visit = tree_visit$2;
treeProto$2.visitAfter = tree_visitAfter$2;
treeProto$2.x = tree_x$2;
function tree_add$1(d) {
const x = +this._x.call(null, d),
y = +this._y.call(null, d);
return add$1(this.cover(x, y), x, y, d);
}
function add$1(tree, x, y, d) {
if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points
var parent,
node = tree._root,
leaf = {data: d},
x0 = tree._x0,
y0 = tree._y0,
x1 = tree._x1,
y1 = tree._y1,
xm,
ym,
xp,
yp,
right,
bottom,
i,
j;
// If the tree is empty, initialize the root as a leaf.
if (!node) return tree._root = leaf, tree;
// Find the existing leaf for the new point, or add it.
while (node.length) {
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree;
}
// Is the new point is exactly coincident with the existing point?
xp = +tree._x.call(null, node.data);
yp = +tree._y.call(null, node.data);
if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;
// Otherwise, split the leaf node until the old and new point are separated.
do {
parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4);
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
} while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm)));
return parent[j] = node, parent[i] = leaf, tree;
}
function addAll$1(data) {
var d, i, n = data.length,
x,
y,
xz = new Array(n),
yz = new Array(n),
x0 = Infinity,
y0 = Infinity,
x1 = -Infinity,
y1 = -Infinity;
// Compute the points and their extent.
for (i = 0; i < n; ++i) {
if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue;
xz[i] = x;
yz[i] = y;
if (x < x0) x0 = x;
if (x > x1) x1 = x;
if (y < y0) y0 = y;
if (y > y1) y1 = y;
}
// If there were no (valid) points, abort.
if (x0 > x1 || y0 > y1) return this;
// Expand the tree to cover the new points.
this.cover(x0, y0).cover(x1, y1);
// Add the new points.
for (i = 0; i < n; ++i) {
add$1(this, xz[i], yz[i], data[i]);
}
return this;
}
function tree_cover$1(x, y) {
if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points
var x0 = this._x0,
y0 = this._y0,
x1 = this._x1,
y1 = this._y1;
// If the quadtree has no extent, initialize them.
// Integer extent are necessary so that if we later double the extent,
// the existing quadrant boundaries don’t change due to floating point error!
if (isNaN(x0)) {
x1 = (x0 = Math.floor(x)) + 1;
y1 = (y0 = Math.floor(y)) + 1;
}
// Otherwise, double repeatedly to cover.
else {
var z = x1 - x0 || 1,
node = this._root,
parent,
i;
while (x0 > x || x >= x1 || y0 > y || y >= y1) {
i = (y < y0) << 1 | (x < x0);
parent = new Array(4), parent[i] = node, node = parent, z *= 2;
switch (i) {
case 0: x1 = x0 + z, y1 = y0 + z; break;
case 1: x0 = x1 - z, y1 = y0 + z; break;
case 2: x1 = x0 + z, y0 = y1 - z; break;
case 3: x0 = x1 - z, y0 = y1 - z; break;
}
}
if (this._root && this._root.length) this._root = node;
}
this._x0 = x0;
this._y0 = y0;
this._x1 = x1;
this._y1 = y1;
return this;
}
function tree_data$1() {
var data = [];
this.visit(function(node) {
if (!node.length) do data.push(node.data); while (node = node.next)
});
return data;
}
function tree_extent$1(_) {
return arguments.length
? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1])
: isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]];
}
function Quad(node, x0, y0, x1, y1) {
this.node = node;
this.x0 = x0;
this.y0 = y0;
this.x1 = x1;
this.y1 = y1;
}
function tree_find$1(x, y, radius) {
var data,
x0 = this._x0,
y0 = this._y0,
x1,
y1,
x2,
y2,
x3 = this._x1,
y3 = this._y1,
quads = [],
node = this._root,
q,
i;
if (node) quads.push(new Quad(node, x0, y0, x3, y3));
if (radius == null) radius = Infinity;
else {
x0 = x - radius, y0 = y - radius;
x3 = x + radius, y3 = y + radius;
radius *= radius;
}
while (q = quads.pop()) {
// Stop searching if this quadrant can’t contain a closer node.
if (!(node = q.node)
|| (x1 = q.x0) > x3
|| (y1 = q.y0) > y3
|| (x2 = q.x1) < x0
|| (y2 = q.y1) < y0) continue;
// Bisect the current quadrant.
if (node.length) {
var xm = (x1 + x2) / 2,
ym = (y1 + y2) / 2;
quads.push(
new Quad(node[3], xm, ym, x2, y2),
new Quad(node[2], x1, ym, xm, y2),
new Quad(node[1], xm, y1, x2, ym),
new Quad(node[0], x1, y1, xm, ym)
);
// Visit the closest quadrant first.
if (i = (y >= ym) << 1 | (x >= xm)) {
q = quads[quads.length - 1];
quads[quads.length - 1] = quads[quads.length - 1 - i];
quads[quads.length - 1 - i] = q;
}
}
// Visit this point. (Visiting coincident points isn’t necessary!)
else {
var dx = x - +this._x.call(null, node.data),
dy = y - +this._y.call(null, node.data),
d2 = dx * dx + dy * dy;
if (d2 < radius) {
var d = Math.sqrt(radius = d2);
x0 = x - d, y0 = y - d;
x3 = x + d, y3 = y + d;
data = node.data;
}
}
}
return data;
}
function tree_remove$1(d) {
if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points
var parent,
node = this._root,
retainer,
previous,
next,
x0 = this._x0,
y0 = this._y0,
x1 = this._x1,
y1 = this._y1,
x,
y,
xm,
ym,
right,
bottom,
i,
j;
// If the tree is empty, initialize the root as a leaf.
if (!node) return this;
// Find the leaf node for the point.
// While descending, also retain the deepest parent with a non-removed sibling.
if (node.length) while (true) {
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
if (!(parent = node, node = node[i = bottom << 1 | right])) return this;
if (!node.length) break;
if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i;
}
// Find the point to remove.
while (node.data !== d) if (!(previous = node, node = node.next)) return this;
if (next = node.next) delete node.next;
// If there are multiple coincident points, remove just the point.
if (previous) return (next ? previous.next = next : delete previous.next), this;
// If this is the root point, remove it.
if (!parent) return this._root = next, this;
// Remove this leaf.
next ? parent[i] = next : delete parent[i];
// If the parent now contains exactly one leaf, collapse superfluous parents.
if ((node = parent[0] || parent[1] || parent[2] || parent[3])
&& node === (parent[3] || parent[2] || parent[1] || parent[0])
&& !node.length) {
if (retainer) retainer[j] = node;
else this._root = node;
}
return this;
}
function removeAll$1(data) {
for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);
return this;
}
function tree_root$1() {
return this._root;
}
function tree_size$1() {
var size = 0;
this.visit(function(node) {
if (!node.length) do ++size; while (node = node.next)
});
return size;
}
function tree_visit$1(callback) {
var quads = [], q, node = this._root, child, x0, y0, x1, y1;
if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1));
while (q = quads.pop()) {
if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) {
var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
}
}
return this;
}
function tree_visitAfter$1(callback) {
var quads = [], next = [], q;
if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1));
while (q = quads.pop()) {
var node = q.node;
if (node.length) {
var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
}
next.push(q);
}
while (q = next.pop()) {
callback(q.node, q.x0, q.y0, q.x1, q.y1);
}
return this;
}
function defaultX$1(d) {
return d[0];
}
function tree_x$1(_) {
return arguments.length ? (this._x = _, this) : this._x;
}
function defaultY$1(d) {
return d[1];
}
function tree_y$1(_) {
return arguments.length ? (this._y = _, this) : this._y;
}
function quadtree(nodes, x, y) {
var tree = new Quadtree(x == null ? defaultX$1 : x, y == null ? defaultY$1 : y, NaN, NaN, NaN, NaN);
return nodes == null ? tree : tree.addAll(nodes);
}
function Quadtree(x, y, x0, y0, x1, y1) {
this._x = x;
this._y = y;
this._x0 = x0;
this._y0 = y0;
this._x1 = x1;
this._y1 = y1;
this._root = undefined;
}
function leaf_copy$1(leaf) {
var copy = {data: leaf.data}, next = copy;
while (leaf = leaf.next) next = next.next = {data: leaf.data};
return copy;
}
var treeProto$1 = quadtree.prototype = Quadtree.prototype;
treeProto$1.copy = function() {
var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1),
node = this._root,
nodes,
child;
if (!node) return copy;
if (!node.length) return copy._root = leaf_copy$1(node), copy;
nodes = [{source: node, target: copy._root = new Array(4)}];
while (node = nodes.pop()) {
for (var i = 0; i < 4; ++i) {
if (child = node.source[i]) {
if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)});
else node.target[i] = leaf_copy$1(child);
}
}
}
return copy;
};
treeProto$1.add = tree_add$1;
treeProto$1.addAll = addAll$1;
treeProto$1.cover = tree_cover$1;
treeProto$1.data = tree_data$1;
treeProto$1.extent = tree_extent$1;
treeProto$1.find = tree_find$1;
treeProto$1.remove = tree_remove$1;
treeProto$1.removeAll = removeAll$1;
treeProto$1.root = tree_root$1;
treeProto$1.size = tree_size$1;
treeProto$1.visit = tree_visit$1;
treeProto$1.visitAfter = tree_visitAfter$1;
treeProto$1.x = tree_x$1;
treeProto$1.y = tree_y$1;
function tree_add(d) {
const x = +this._x.call(null, d),
y = +this._y.call(null, d),
z = +this._z.call(null, d);
return add(this.cover(x, y, z), x, y, z, d);
}
function add(tree, x, y, z, d) {
if (isNaN(x) || isNaN(y) || isNaN(z)) return tree; // ignore invalid points
var parent,
node = tree._root,
leaf = {data: d},
x0 = tree._x0,
y0 = tree._y0,
z0 = tree._z0,
x1 = tree._x1,
y1 = tree._y1,
z1 = tree._z1,
xm,
ym,
zm,
xp,
yp,
zp,
right,
bottom,
deep,
i,
j;
// If the tree is empty, initialize the root as a leaf.
if (!node) return tree._root = leaf, tree;
// Find the existing leaf for the new point, or add it.
while (node.length) {
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
if (deep = z >= (zm = (z0 + z1) / 2)) z0 = zm; else z1 = zm;
if (parent = node, !(node = node[i = deep << 2 | bottom << 1 | right])) return parent[i] = leaf, tree;
}
// Is the new point is exactly coincident with the existing point?
xp = +tree._x.call(null, node.data);
yp = +tree._y.call(null, node.data);
zp = +tree._z.call(null, node.data);
if (x === xp && y === yp && z === zp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;
// Otherwise, split the leaf node until the old and new point are separated.
do {
parent = parent ? parent[i] = new Array(8) : tree._root = new Array(8);
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
if (deep = z >= (zm = (z0 + z1) / 2)) z0 = zm; else z1 = zm;
} while ((i = deep << 2 | bottom << 1 | right) === (j = (zp >= zm) << 2 | (yp >= ym) << 1 | (xp >= xm)));
return parent[j] = node, parent[i] = leaf, tree;
}
function addAll(data) {
if (!Array.isArray(data)) data = Array.from(data);
const n = data.length;
const xz = new Float64Array(n);
const yz = new Float64Array(n);
const zz = new Float64Array(n);
let x0 = Infinity,
y0 = Infinity,
z0 = Infinity,
x1 = -Infinity,
y1 = -Infinity,
z1 = -Infinity;
// Compute the points and their extent.
for (let i = 0, d, x, y, z; i < n; ++i) {
if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d)) || isNaN(z = +this._z.call(null, d))) continue;
xz[i] = x;
yz[i] = y;
zz[i] = z;
if (x < x0) x0 = x;
if (x > x1) x1 = x;
if (y < y0) y0 = y;
if (y > y1) y1 = y;
if (z < z0) z0 = z;
if (z > z1) z1 = z;
}
// If there were no (valid) points, abort.
if (x0 > x1 || y0 > y1 || z0 > z1) return this;
// Expand the tree to cover the new points.
this.cover(x0, y0, z0).cover(x1, y1, z1);
// Add the new points.
for (let i = 0; i < n; ++i) {
add(this, xz[i], yz[i], zz[i], data[i]);
}
return this;
}
function tree_cover(x, y, z) {
if (isNaN(x = +x) || isNaN(y = +y) || isNaN(z = +z)) return this; // ignore invalid points
var x0 = this._x0,
y0 = this._y0,
z0 = this._z0,
x1 = this._x1,
y1 = this._y1,
z1 = this._z1;
// If the octree has no extent, initialize them.
// Integer extent are necessary so that if we later double the extent,
// the existing octant boundaries don’t change due to floating point error!
if (isNaN(x0)) {
x1 = (x0 = Math.floor(x)) + 1;
y1 = (y0 = Math.floor(y)) + 1;
z1 = (z0 = Math.floor(z)) + 1;
}
// Otherwise, double repeatedly to cover.
else {
var t = x1 - x0 || 1,
node = this._root,
parent,
i;
while (x0 > x || x >= x1 || y0 > y || y >= y1 || z0 > z || z >= z1) {
i = (z < z0) << 2 | (y < y0) << 1 | (x < x0);
parent = new Array(8), parent[i] = node, node = parent, t *= 2;
switch (i) {
case 0: x1 = x0 + t, y1 = y0 + t, z1 = z0 + t; break;
case 1: x0 = x1 - t, y1 = y0 + t, z1 = z0 + t; break;
case 2: x1 = x0 + t, y0 = y1 - t, z1 = z0 + t; break;
case 3: x0 = x1 - t, y0 = y1 - t, z1 = z0 + t; break;
case 4: x1 = x0 + t, y1 = y0 + t, z0 = z1 - t; break;
case 5: x0 = x1 - t, y1 = y0 + t, z0 = z1 - t; break;
case 6: x1 = x0 + t, y0 = y1 - t, z0 = z1 - t; break;
case 7: x0 = x1 - t, y0 = y1 - t, z0 = z1 - t; break;
}
}
if (this._root && this._root.length) this._root = node;
}
this._x0 = x0;
this._y0 = y0;
this._z0 = z0;
this._x1 = x1;
this._y1 = y1;
this._z1 = z1;
return this;
}
function tree_data() {
var data = [];
this.visit(function(node) {
if (!node.length) do data.push(node.data); while (node = node.next)
});
return data;
}
function tree_extent(_) {
return arguments.length
? this.cover(+_[0][0], +_[0][1], +_[0][2]).cover(+_[1][0], +_[1][1], +_[1][2])
: isNaN(this._x0) ? undefined : [[this._x0, this._y0, this._z0], [this._x1, this._y1, this._z1]];
}
function Octant(node, x0, y0, z0, x1, y1, z1) {
this.node = node;
this.x0 = x0;
this.y0 = y0;
this.z0 = z0;
this.x1 = x1;
this.y1 = y1;
this.z1 = z1;
}
function tree_find(x, y, z, radius) {
var data,
x0 = this._x0,
y0 = this._y0,
z0 = this._z0,
x1,
y1,
z1,
x2,
y2,
z2,
x3 = this._x1,
y3 = this._y1,
z3 = this._z1,
octs = [],
node = this._root,
q,
i;
if (node) octs.push(new Octant(node, x0, y0, z0, x3, y3, z3));
if (radius == null) radius = Infinity;
else {
x0 = x - radius, y0 = y - radius, z0 = z - radius;
x3 = x + radius, y3 = y + radius, z3 = z + radius;
radius *= radius;
}
while (q = octs.pop()) {
// Stop searching if this octant can’t contain a closer node.
if (!(node = q.node)
|| (x1 = q.x0) > x3
|| (y1 = q.y0) > y3
|| (z1 = q.z0) > z3
|| (x2 = q.x1) < x0
|| (y2 = q.y1) < y0
|| (z2 = q.z1) < z0) continue;
// Bisect the current octant.
if (node.length) {
var xm = (x1 + x2) / 2,
ym = (y1 + y2) / 2,
zm = (z1 + z2) / 2;
octs.push(
new Octant(node[7], xm, ym, zm, x2, y2, z2),
new Octant(node[6], x1, ym, zm, xm, y2, z2),
new Octant(node[5], xm, y1, zm, x2, ym, z2),
new Octant(node[4], x1, y1, zm, xm, ym, z2),
new Octant(node[3], xm, ym, z1, x2, y2, zm),
new Octant(node[2], x1, ym, z1, xm, y2, zm),
new Octant(node[1], xm, y1, z1, x2, ym, zm),
new Octant(node[0], x1, y1, z1, xm, ym, zm)
);
// Visit the closest octant first.
if (i = (z >= zm) << 2 | (y >= ym) << 1 | (x >= xm)) {
q = octs[octs.length - 1];
octs[octs.length - 1] = octs[octs.length - 1 - i];
octs[octs.length - 1 - i] = q;
}
}
// Visit this point. (Visiting coincident points isn’t necessary!)
else {
var dx = x - +this._x.call(null, node.data),
dy = y - +this._y.call(null, node.data),
dz = z - +this._z.call(null, node.data),
d2 = dx * dx + dy * dy + dz * dz;
if (d2 < radius) {
var d = Math.sqrt(radius = d2);
x0 = x - d, y0 = y - d, z0 = z - d;
x3 = x + d, y3 = y + d, z3 = z + d;
data = node.data;
}
}
}
return data;
}
const distance = (x1, y1, z1, x2, y2, z2) => Math.sqrt((x1-x2)**2 + (y1-y2)**2 + (z1-z2)**2);
function findAllWithinRadius(x, y, z, radius) {
const result = [];
const xMin = x - radius;
const yMin = y - radius;
const zMin = z - radius;
const xMax = x + radius;
const yMax = y + radius;
const zMax = z + radius;
this.visit((node, x1, y1, z1, x2, y2, z2) => {
if (!node.length) {
do {
const d = node.data;
if (distance(x, y, z, this._x(d), this._y(d), this._z(d)) <= radius) {
result.push(d);
}
} while (node = node.next);
}
return x1 > xMax || y1 > yMax || z1 > zMax || x2 < xMin || y2 < yMin || z2 < zMin;
});
return result;
}
function tree_remove(d) {
if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d)) || isNaN(z = +this._z.call(null, d))) return this; // ignore invalid points
var parent,
node = this._root,
retainer,
previous,
next,
x0 = this._x0,
y0 = this._y0,
z0 = this._z0,
x1 = this._x1,
y1 = this._y1,
z1 = this._z1,
x,
y,
z,
xm,
ym,
zm,
right,
bottom,
deep,
i,
j;
// If the tree is empty, initialize the root as a leaf.
if (!node) return this;
// Find the leaf node for the point.
// While descending, also retain the deepest parent with a non-removed sibling.
if (node.length) while (true) {
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
if (deep = z >= (zm = (z0 + z1) / 2)) z0 = zm; else z1 = zm;
if (!(parent = node, node = node[i = deep << 2 | bottom << 1 | right])) return this;
if (!node.length) break;
if (parent[(i + 1) & 7] || parent[(i + 2) & 7] || parent[(i + 3) & 7] || parent[(i + 4) & 7] || parent[(i + 5) & 7] || parent[(i + 6) & 7] || parent[(i + 7) & 7]) retainer = parent, j = i;
}
// Find the point to remove.
while (node.data !== d) if (!(previous = node, node = node.next)) return this;
if (next = node.next) delete node.next;
// If there are multiple coincident points, remove just the point.
if (previous) return (next ? previous.next = next : delete previous.next), this;
// If this is the root point, remove it.
if (!parent) return this._root = next, this;
// Remove this leaf.
next ? parent[i] = next : delete parent[i];
// If the parent now contains exactly one leaf, collapse superfluous parents.
if ((node = parent[0] || parent[1] || parent[2] || parent[3] || parent[4] || parent[5] || parent[6] || parent[7])
&& node === (parent[7] || parent[6] || parent[5] || parent[4] || parent[3] || parent[2] || parent[1] || parent[0])
&& !node.length) {
if (retainer) retainer[j] = node;
else this._root = node;
}
return this;
}
function removeAll(data) {
for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);
return this;
}
function tree_root() {
return this._root;
}
function tree_size() {
var size = 0;
this.visit(function(node) {
if (!node.length) do ++size; while (node = node.next)
});
return size;
}
function tree_visit(callback) {
var octs = [], q, node = this._root, child, x0, y0, z0, x1, y1, z1;
if (node) octs.push(new Octant(node, this._x0, this._y0, this._z0, this._x1, this._y1, this._z1));
while (q = octs.pop()) {
if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, z0 = q.z0, x1 = q.x1, y1 = q.y1, z1 = q.z1) && node.length) {
var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2, zm = (z0 + z1) / 2;
if (child = node[7]) octs.push(new Octant(child, xm, ym, zm, x1, y1, z1));
if (child = node[6]) octs.push(new Octant(child, x0, ym, zm, xm, y1, z1));
if (child = node[5]) octs.push(new Octant(child, xm, y0, zm, x1, ym, z1));
if (child = node[4]) octs.push(new Octant(child, x0, y0, zm, xm, ym, z1));
if (child = node[3]) octs.push(new Octant(child, xm, ym, z0, x1, y1, zm));
if (child = node[2]) octs.push(new Octant(child, x0, ym, z0, xm, y1, zm));
if (child = node[1]) octs.push(new Octant(child, xm, y0, z0, x1, ym, zm));
if (child = node[0]) octs.push(new Octant(child, x0, y0, z0, xm, ym, zm));
}
}
return this;
}
function tree_visitAfter(callback) {
var octs = [], next = [], q;
if (this._root) octs.push(new Octant(this._root, this._x0, this._y0, this._z0, this._x1, this._y1, this._z1));
while (q = octs.pop()) {
var node = q.node;
if (node.length) {
var child, x0 = q.x0, y0 = q.y0, z0 = q.z0, x1 = q.x1, y1 = q.y1, z1 = q.z1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2, zm = (z0 + z1) / 2;
if (child = node[0]) octs.push(new Octant(child, x0, y0, z0, xm, ym, zm));
if (child = node[1]) octs.push(new Octant(child, xm, y0, z0, x1, ym, zm));
if (child = node[2]) octs.push(new Octant(child, x0, ym, z0, xm, y1, zm));
if (child = node[3]) octs.push(new Octant(child, xm, ym, z0, x1, y1, zm));
if (child = node[4]) octs.push(new Octant(child, x0, y0, zm, xm, ym, z1));
if (child = node[5]) octs.push(new Octant(child, xm, y0, zm, x1, ym, z1));
if (child = node[6]) octs.push(new Octant(child, x0, ym, zm, xm, y1, z1));
if (child = node[7]) octs.push(new Octant(child, xm, ym, zm, x1, y1, z1));
}
next.push(q);
}
while (q = next.pop()) {
callback(q.node, q.x0, q.y0, q.z0, q.x1, q.y1, q.z1);
}
return this;
}
function defaultX(d) {
return d[0];
}
function tree_x(_) {
return arguments.length ? (this._x = _, this) : this._x;
}
function defaultY(d) {
return d[1];
}
function tree_y(_) {
return arguments.length ? (this._y = _, this) : this._y;
}
function defaultZ(d) {
return d[2];
}
function tree_z(_) {
return arguments.length ? (this._z = _, this) : this._z;
}
function octree(nodes, x, y, z) {
var tree = new Octree(x == null ? defaultX : x, y == null ? defaultY : y, z == null ? defaultZ : z, NaN, NaN, NaN, NaN, NaN, NaN);
return nodes == null ? tree : tree.addAll(nodes);
}
function Octree(x, y, z, x0, y0, z0, x1, y1, z1) {
this._x = x;
this._y = y;
this._z = z;
this._x0 = x0;
this._y0 = y0;
this._z0 = z0;
this._x1 = x1;
this._y1 = y1;
this._z1 = z1;
this._root = undefined;
}
function leaf_copy(leaf) {
var copy = {data: leaf.data}, next = copy;
while (leaf = leaf.next) next = next.next = {data: leaf.data};
return copy;
}
var treeProto = octree.prototype = Octree.prototype;
treeProto.copy = function() {
var copy = new Octree(this._x, this._y, this._z, this._x0, this._y0, this._z0, this._x1, this._y1, this._z1),
node = this._root,
nodes,
child;
if (!node) return copy;
if (!node.length) return copy._root = leaf_copy(node), copy;
nodes = [{source: node, target: copy._root = new Array(8)}];
while (node = nodes.pop()) {
for (var i = 0; i < 8; ++i) {
if (child = node.source[i]) {
if (child.length) nodes.push({source: child, target: node.target[i] = new Array(8)});
else node.target[i] = leaf_copy(child);
}
}
}
return copy;
};
treeProto.add = tree_add;
treeProto.addAll = addAll;
treeProto.cover = tree_cover;
treeProto.data = tree_data;
treeProto.extent = tree_extent;
treeProto.find = tree_find;
treeProto.findAllWithinRadius = findAllWithinRadius;
treeProto.remove = tree_remove;
treeProto.removeAll = removeAll;
treeProto.root = tree_root;
treeProto.size = tree_size;
treeProto.visit = tree_visit;
treeProto.visitAfter = tree_visitAfter;
treeProto.x = tree_x;
treeProto.y = tree_y;
treeProto.z = tree_z;
function constant(x) {
return function() {
return x;
};
}
function jiggle(random) {
return (random() - 0.5) * 1e-6;
}
function index$2(d) {
return d.index;
}
function find(nodeById, nodeId) {
var node = nodeById.get(nodeId);
if (!node) throw new Error("node not found: " + nodeId);
return node;
}
function d3ForceLink(links) {
var id = index$2,
strength = defaultStrength,
strengths,
distance = constant(30),
distances,
nodes,
nDim,
count,
bias,
random,
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 = 0, y = 0, z = 0, l, b; i < n; ++i) {
link = links[i], source = link.source, target = link.target;
x = target.x + target.vx - source.x - source.vx || jiggle(random);
if (nDim > 1) { y = target.y + target.vy - source.y - source.vy || jiggle(random); }
if (nDim > 2) { z = target.z + target.vz - source.z - source.vz || jiggle(random); }
l = Math.sqrt(x * x + y * y + z * z);
l = (l - distances[i]) / l * alpha * strengths[i];
x *= l, y *= l, z *= l;
target.vx -= x * (b = bias[i]);
if (nDim > 1) { target.vy -= y * b; }
if (nDim > 2) { target.vz -= z * b; }
source.vx += x * (b = 1 - b);
if (nDim > 1) { source.vy += y * b; }
if (nDim > 2) { source.vz += z * b; }
}
}
}
function initialize() {
if (!nodes) return;
var i,
n = nodes.length,
m = links.length,
nodeById = new Map(nodes.map((d, i) => [id(d, i, nodes), d])),
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, ...args) {
nodes = _nodes;
random = args.find(arg => typeof arg === 'function') || Math.random;
nDim = args.find(arg => [1, 2, 3].includes(arg)) || 2;
initialize();
};
force.links = function(_) {
return arguments.length ? (links = _, initialize(), force) : links;
};
force.id = function(_)