[
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"extends\": \"eslint:recommended\",\n  \"parserOptions\": {\n    \"sourceType\": \"module\",\n    \"ecmaVersion\": 8\n  },\n  \"env\": {\n    \"es6\": true\n  },\n  \"rules\": {\n    \"no-cond-assign\": 0\n  }\n}\n"
  },
  {
    "path": ".github/eslint.json",
    "content": "{\n  \"problemMatcher\": [\n    {\n      \"owner\": \"eslint-compact\",\n      \"pattern\": [\n        {\n          \"regexp\": \"^(.+):\\\\sline\\\\s(\\\\d+),\\\\scol\\\\s(\\\\d+),\\\\s(Error|Warning|Info)\\\\s-\\\\s(.+)\\\\s\\\\((.+)\\\\)$\",\n          \"file\": 1,\n          \"line\": 2,\n          \"column\": 3,\n          \"severity\": 4,\n          \"message\": 5,\n          \"code\": 6\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": ".github/workflows/node.js.yml",
    "content": "# https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions\n\nname: Node.js CI\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: [14.x]\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Use Node.js ${{ matrix.node-version }}\n      uses: actions/setup-node@v1\n      with:\n        node-version: ${{ matrix.node-version }}\n    - run: yarn --frozen-lockfile\n    - run: |\n        echo ::add-matcher::.github/eslint.json\n        yarn run eslint src test --format=compact\n    - run: yarn test\n"
  },
  {
    "path": ".gitignore",
    "content": "*.sublime-workspace\n.DS_Store\ndist/\nnode_modules\nnpm-debug.log\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2010-2021 Mike Bostock\n\nPermission to use, copy, modify, and/or distribute this software for any purpose\nwith or without fee is hereby granted, provided that the above copyright notice\nand this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS\nOF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\nTORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# d3-force\n\n<a href=\"https://d3js.org\"><img src=\"https://github.com/d3/d3/raw/main/docs/public/logo.svg\" width=\"256\" height=\"256\"></a>\n\nThis module implements a [velocity Verlet](https://en.wikipedia.org/wiki/Verlet_integration) numerical integrator for simulating physical forces on particles. Force simulations can be used to visualize [networks](https://observablehq.com/@d3/force-directed-graph/2?intent=fork) and [hierarchies](https://observablehq.com/@d3/force-directed-tree?intent=fork), and to resolve [collisions](./d3-force/collide.md) as in [bubble charts](http://www.nytimes.com/interactive/2012/09/06/us/politics/convention-word-counts.html).\n\n## Resources\n\n* [Documentation](https://d3js.org/d3-force)\n* [Examples](https://observablehq.com/collection/@d3/d3-force)\n* [Releases](https://github.com/d3/d3-force/releases)\n* [Getting help](https://d3js.org/community)\n\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"d3-force\",\n  \"version\": \"3.0.0\",\n  \"description\": \"Force-directed graph layout using velocity Verlet integration.\",\n  \"homepage\": \"https://d3js.org/d3-force/\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/d3/d3-force.git\"\n  },\n  \"keywords\": [\n    \"d3\",\n    \"d3-module\",\n    \"layout\",\n    \"network\",\n    \"graph\",\n    \"force\",\n    \"verlet\",\n    \"infovis\"\n  ],\n  \"license\": \"ISC\",\n  \"author\": {\n    \"name\": \"Mike Bostock\",\n    \"url\": \"https://bost.ocks.org/mike\"\n  },\n  \"type\": \"module\",\n  \"files\": [\n    \"src/**/*.js\",\n    \"dist/**/*.js\"\n  ],\n  \"module\": \"src/index.js\",\n  \"main\": \"src/index.js\",\n  \"jsdelivr\": \"dist/d3-force.min.js\",\n  \"unpkg\": \"dist/d3-force.min.js\",\n  \"exports\": {\n    \"umd\": \"./dist/d3-force.min.js\",\n    \"default\": \"./src/index.js\"\n  },\n  \"sideEffects\": false,\n  \"dependencies\": {\n    \"d3-dispatch\": \"1 - 3\",\n    \"d3-quadtree\": \"1 - 3\",\n    \"d3-timer\": \"1 - 3\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"7\",\n    \"mocha\": \"8\",\n    \"rollup\": \"2\",\n    \"rollup-plugin-terser\": \"7\"\n  },\n  \"scripts\": {\n    \"test\": \"mocha 'test/**/*-test.js' && eslint src test\",\n    \"prepublishOnly\": \"rm -rf dist && yarn test && rollup -c\",\n    \"postpublish\": \"git push && git push --tags && cd ../d3.github.com && git pull && cp ../${npm_package_name}/dist/${npm_package_name}.js ${npm_package_name}.v${npm_package_version%%.*}.js && cp ../${npm_package_name}/dist/${npm_package_name}.min.js ${npm_package_name}.v${npm_package_version%%.*}.min.js && git add ${npm_package_name}.v${npm_package_version%%.*}.js ${npm_package_name}.v${npm_package_version%%.*}.min.js && git commit -m \\\"${npm_package_name} ${npm_package_version}\\\" && git push && cd -\"\n  },\n  \"engines\": {\n    \"node\": \">=12\"\n  }\n}\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import {readFileSync} from \"fs\";\nimport {terser} from \"rollup-plugin-terser\";\nimport * as meta from \"./package.json\";\n\n// Extract copyrights from the LICENSE.\nconst copyright = readFileSync(\"./LICENSE\", \"utf-8\")\n  .split(/\\n/g)\n  .filter(line => /^Copyright\\s+/.test(line))\n  .map(line => line.replace(/^Copyright\\s+/, \"\"))\n  .join(\", \");\n\nconst config = {\n  input: \"src/index.js\",\n  external: Object.keys(meta.dependencies || {}).filter(key => /^d3-/.test(key)),\n  output: {\n    file: `dist/${meta.name}.js`,\n    name: \"d3\",\n    format: \"umd\",\n    indent: false,\n    extend: true,\n    banner: `// ${meta.homepage} v${meta.version} Copyright ${copyright}`,\n    globals: Object.assign({}, ...Object.keys(meta.dependencies || {}).filter(key => /^d3-/.test(key)).map(key => ({[key]: \"d3\"})))\n  },\n  plugins: []\n};\n\nexport default [\n  config,\n  {\n    ...config,\n    output: {\n      ...config.output,\n      file: `dist/${meta.name}.min.js`\n    },\n    plugins: [\n      ...config.plugins,\n      terser({\n        output: {\n          preamble: config.output.banner\n        }\n      })\n    ]\n  }\n];\n"
  },
  {
    "path": "src/center.js",
    "content": "export default function(x, y) {\n  var nodes, strength = 1;\n\n  if (x == null) x = 0;\n  if (y == null) y = 0;\n\n  function force() {\n    var i,\n        n = nodes.length,\n        node,\n        sx = 0,\n        sy = 0;\n\n    for (i = 0; i < n; ++i) {\n      node = nodes[i], sx += node.x, sy += node.y;\n    }\n\n    for (sx = (sx / n - x) * strength, sy = (sy / n - y) * strength, i = 0; i < n; ++i) {\n      node = nodes[i], node.x -= sx, node.y -= sy;\n    }\n  }\n\n  force.initialize = function(_) {\n    nodes = _;\n  };\n\n  force.x = function(_) {\n    return arguments.length ? (x = +_, force) : x;\n  };\n\n  force.y = function(_) {\n    return arguments.length ? (y = +_, force) : y;\n  };\n\n  force.strength = function(_) {\n    return arguments.length ? (strength = +_, force) : strength;\n  };\n\n  return force;\n}\n"
  },
  {
    "path": "src/collide.js",
    "content": "import {quadtree} from \"d3-quadtree\";\nimport constant from \"./constant.js\";\nimport jiggle from \"./jiggle.js\";\n\nfunction x(d) {\n  return d.x + d.vx;\n}\n\nfunction y(d) {\n  return d.y + d.vy;\n}\n\nexport default function(radius) {\n  var nodes,\n      radii,\n      random,\n      strength = 1,\n      iterations = 1;\n\n  if (typeof radius !== \"function\") radius = constant(radius == null ? 1 : +radius);\n\n  function force() {\n    var i, n = nodes.length,\n        tree,\n        node,\n        xi,\n        yi,\n        ri,\n        ri2;\n\n    for (var k = 0; k < iterations; ++k) {\n      tree = quadtree(nodes, x, y).visitAfter(prepare);\n      for (i = 0; i < n; ++i) {\n        node = nodes[i];\n        ri = radii[node.index], ri2 = ri * ri;\n        xi = node.x + node.vx;\n        yi = node.y + node.vy;\n        tree.visit(apply);\n      }\n    }\n\n    function apply(quad, x0, y0, x1, y1) {\n      var data = quad.data, rj = quad.r, r = ri + rj;\n      if (data) {\n        if (data.index > node.index) {\n          var x = xi - data.x - data.vx,\n              y = yi - data.y - data.vy,\n              l = x * x + y * y;\n          if (l < r * r) {\n            if (x === 0) x = jiggle(random), l += x * x;\n            if (y === 0) y = jiggle(random), l += y * y;\n            l = (r - (l = Math.sqrt(l))) / l * strength;\n            node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj));\n            node.vy += (y *= l) * r;\n            data.vx -= x * (r = 1 - r);\n            data.vy -= y * r;\n          }\n        }\n        return;\n      }\n      return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r;\n    }\n  }\n\n  function prepare(quad) {\n    if (quad.data) return quad.r = radii[quad.data.index];\n    for (var i = quad.r = 0; i < 4; ++i) {\n      if (quad[i] && quad[i].r > quad.r) {\n        quad.r = quad[i].r;\n      }\n    }\n  }\n\n  function initialize() {\n    if (!nodes) return;\n    var i, n = nodes.length, node;\n    radii = new Array(n);\n    for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes);\n  }\n\n  force.initialize = function(_nodes, _random) {\n    nodes = _nodes;\n    random = _random;\n    initialize();\n  };\n\n  force.iterations = function(_) {\n    return arguments.length ? (iterations = +_, force) : iterations;\n  };\n\n  force.strength = function(_) {\n    return arguments.length ? (strength = +_, force) : strength;\n  };\n\n  force.radius = function(_) {\n    return arguments.length ? (radius = typeof _ === \"function\" ? _ : constant(+_), initialize(), force) : radius;\n  };\n\n  return force;\n}\n"
  },
  {
    "path": "src/constant.js",
    "content": "export default function(x) {\n  return function() {\n    return x;\n  };\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "export {default as forceCenter} from \"./center.js\";\nexport {default as forceCollide} from \"./collide.js\";\nexport {default as forceLink} from \"./link.js\";\nexport {default as forceManyBody} from \"./manyBody.js\";\nexport {default as forceRadial} from \"./radial.js\";\nexport {default as forceSimulation} from \"./simulation.js\";\nexport {default as forceX} from \"./x.js\";\nexport {default as forceY} from \"./y.js\";\n"
  },
  {
    "path": "src/jiggle.js",
    "content": "export default function(random) {\n  return (random() - 0.5) * 1e-6;\n}\n"
  },
  {
    "path": "src/lcg.js",
    "content": "// https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use\nconst a = 1664525;\nconst c = 1013904223;\nconst m = 4294967296; // 2^32\n\nexport default function() {\n  let s = 1;\n  return () => (s = (a * s + c) % m) / m;\n}\n"
  },
  {
    "path": "src/link.js",
    "content": "import constant from \"./constant.js\";\nimport jiggle from \"./jiggle.js\";\n\nfunction index(d) {\n  return d.index;\n}\n\nfunction find(nodeById, nodeId) {\n  var node = nodeById.get(nodeId);\n  if (!node) throw new Error(\"node not found: \" + nodeId);\n  return node;\n}\n\nexport default function(links) {\n  var id = index,\n      strength = defaultStrength,\n      strengths,\n      distance = constant(30),\n      distances,\n      nodes,\n      count,\n      bias,\n      random,\n      iterations = 1;\n\n  if (links == null) links = [];\n\n  function defaultStrength(link) {\n    return 1 / Math.min(count[link.source.index], count[link.target.index]);\n  }\n\n  function force(alpha) {\n    for (var k = 0, n = links.length; k < iterations; ++k) {\n      for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) {\n        link = links[i], source = link.source, target = link.target;\n        x = target.x + target.vx - source.x - source.vx || jiggle(random);\n        y = target.y + target.vy - source.y - source.vy || jiggle(random);\n        l = Math.sqrt(x * x + y * y);\n        l = (l - distances[i]) / l * alpha * strengths[i];\n        x *= l, y *= l;\n        target.vx -= x * (b = bias[i]);\n        target.vy -= y * b;\n        source.vx += x * (b = 1 - b);\n        source.vy += y * b;\n      }\n    }\n  }\n\n  function initialize() {\n    if (!nodes) return;\n\n    var i,\n        n = nodes.length,\n        m = links.length,\n        nodeById = new Map(nodes.map((d, i) => [id(d, i, nodes), d])),\n        link;\n\n    for (i = 0, count = new Array(n); i < m; ++i) {\n      link = links[i], link.index = i;\n      if (typeof link.source !== \"object\") link.source = find(nodeById, link.source);\n      if (typeof link.target !== \"object\") link.target = find(nodeById, link.target);\n      count[link.source.index] = (count[link.source.index] || 0) + 1;\n      count[link.target.index] = (count[link.target.index] || 0) + 1;\n    }\n\n    for (i = 0, bias = new Array(m); i < m; ++i) {\n      link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]);\n    }\n\n    strengths = new Array(m), initializeStrength();\n    distances = new Array(m), initializeDistance();\n  }\n\n  function initializeStrength() {\n    if (!nodes) return;\n\n    for (var i = 0, n = links.length; i < n; ++i) {\n      strengths[i] = +strength(links[i], i, links);\n    }\n  }\n\n  function initializeDistance() {\n    if (!nodes) return;\n\n    for (var i = 0, n = links.length; i < n; ++i) {\n      distances[i] = +distance(links[i], i, links);\n    }\n  }\n\n  force.initialize = function(_nodes, _random) {\n    nodes = _nodes;\n    random = _random;\n    initialize();\n  };\n\n  force.links = function(_) {\n    return arguments.length ? (links = _, initialize(), force) : links;\n  };\n\n  force.id = function(_) {\n    return arguments.length ? (id = _, force) : id;\n  };\n\n  force.iterations = function(_) {\n    return arguments.length ? (iterations = +_, force) : iterations;\n  };\n\n  force.strength = function(_) {\n    return arguments.length ? (strength = typeof _ === \"function\" ? _ : constant(+_), initializeStrength(), force) : strength;\n  };\n\n  force.distance = function(_) {\n    return arguments.length ? (distance = typeof _ === \"function\" ? _ : constant(+_), initializeDistance(), force) : distance;\n  };\n\n  return force;\n}\n"
  },
  {
    "path": "src/manyBody.js",
    "content": "import {quadtree} from \"d3-quadtree\";\nimport constant from \"./constant.js\";\nimport jiggle from \"./jiggle.js\";\nimport {x, y} from \"./simulation.js\";\n\nexport default function() {\n  var nodes,\n      node,\n      random,\n      alpha,\n      strength = constant(-30),\n      strengths,\n      distanceMin2 = 1,\n      distanceMax2 = Infinity,\n      theta2 = 0.81;\n\n  function force(_) {\n    var i, n = nodes.length, tree = quadtree(nodes, x, y).visitAfter(accumulate);\n    for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply);\n  }\n\n  function initialize() {\n    if (!nodes) return;\n    var i, n = nodes.length, node;\n    strengths = new Array(n);\n    for (i = 0; i < n; ++i) node = nodes[i], strengths[node.index] = +strength(node, i, nodes);\n  }\n\n  function accumulate(quad) {\n    var strength = 0, q, c, weight = 0, x, y, i;\n\n    // For internal nodes, accumulate forces from child quadrants.\n    if (quad.length) {\n      for (x = y = i = 0; i < 4; ++i) {\n        if ((q = quad[i]) && (c = Math.abs(q.value))) {\n          strength += q.value, weight += c, x += c * q.x, y += c * q.y;\n        }\n      }\n      quad.x = x / weight;\n      quad.y = y / weight;\n    }\n\n    // For leaf nodes, accumulate forces from coincident quadrants.\n    else {\n      q = quad;\n      q.x = q.data.x;\n      q.y = q.data.y;\n      do strength += strengths[q.data.index];\n      while (q = q.next);\n    }\n\n    quad.value = strength;\n  }\n\n  function apply(quad, x1, _, x2) {\n    if (!quad.value) return true;\n\n    var x = quad.x - node.x,\n        y = quad.y - node.y,\n        w = x2 - x1,\n        l = x * x + y * y;\n\n    // Apply the Barnes-Hut approximation if possible.\n    // Limit forces for very close nodes; randomize direction if coincident.\n    if (w * w / theta2 < l) {\n      if (l < distanceMax2) {\n        if (x === 0) x = jiggle(random), l += x * x;\n        if (y === 0) y = jiggle(random), l += y * y;\n        if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);\n        node.vx += x * quad.value * alpha / l;\n        node.vy += y * quad.value * alpha / l;\n      }\n      return true;\n    }\n\n    // Otherwise, process points directly.\n    else if (quad.length || l >= distanceMax2) return;\n\n    // Limit forces for very close nodes; randomize direction if coincident.\n    if (quad.data !== node || quad.next) {\n      if (x === 0) x = jiggle(random), l += x * x;\n      if (y === 0) y = jiggle(random), l += y * y;\n      if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);\n    }\n\n    do if (quad.data !== node) {\n      w = strengths[quad.data.index] * alpha / l;\n      node.vx += x * w;\n      node.vy += y * w;\n    } while (quad = quad.next);\n  }\n\n  force.initialize = function(_nodes, _random) {\n    nodes = _nodes;\n    random = _random;\n    initialize();\n  };\n\n  force.strength = function(_) {\n    return arguments.length ? (strength = typeof _ === \"function\" ? _ : constant(+_), initialize(), force) : strength;\n  };\n\n  force.distanceMin = function(_) {\n    return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2);\n  };\n\n  force.distanceMax = function(_) {\n    return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2);\n  };\n\n  force.theta = function(_) {\n    return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2);\n  };\n\n  return force;\n}\n"
  },
  {
    "path": "src/radial.js",
    "content": "import constant from \"./constant.js\";\n\nexport default function(radius, x, y) {\n  var nodes,\n      strength = constant(0.1),\n      strengths,\n      radiuses;\n\n  if (typeof radius !== \"function\") radius = constant(+radius);\n  if (x == null) x = 0;\n  if (y == null) y = 0;\n\n  function force(alpha) {\n    for (var i = 0, n = nodes.length; i < n; ++i) {\n      var node = nodes[i],\n          dx = node.x - x || 1e-6,\n          dy = node.y - y || 1e-6,\n          r = Math.sqrt(dx * dx + dy * dy),\n          k = (radiuses[i] - r) * strengths[i] * alpha / r;\n      node.vx += dx * k;\n      node.vy += dy * k;\n    }\n  }\n\n  function initialize() {\n    if (!nodes) return;\n    var i, n = nodes.length;\n    strengths = new Array(n);\n    radiuses = new Array(n);\n    for (i = 0; i < n; ++i) {\n      radiuses[i] = +radius(nodes[i], i, nodes);\n      strengths[i] = isNaN(radiuses[i]) ? 0 : +strength(nodes[i], i, nodes);\n    }\n  }\n\n  force.initialize = function(_) {\n    nodes = _, initialize();\n  };\n\n  force.strength = function(_) {\n    return arguments.length ? (strength = typeof _ === \"function\" ? _ : constant(+_), initialize(), force) : strength;\n  };\n\n  force.radius = function(_) {\n    return arguments.length ? (radius = typeof _ === \"function\" ? _ : constant(+_), initialize(), force) : radius;\n  };\n\n  force.x = function(_) {\n    return arguments.length ? (x = +_, force) : x;\n  };\n\n  force.y = function(_) {\n    return arguments.length ? (y = +_, force) : y;\n  };\n\n  return force;\n}\n"
  },
  {
    "path": "src/simulation.js",
    "content": "import {dispatch} from \"d3-dispatch\";\nimport {timer} from \"d3-timer\";\nimport lcg from \"./lcg.js\";\n\nexport function x(d) {\n  return d.x;\n}\n\nexport function y(d) {\n  return d.y;\n}\n\nvar initialRadius = 10,\n    initialAngle = Math.PI * (3 - Math.sqrt(5));\n\nexport default function(nodes) {\n  var simulation,\n      alpha = 1,\n      alphaMin = 0.001,\n      alphaDecay = 1 - Math.pow(alphaMin, 1 / 300),\n      alphaTarget = 0,\n      velocityDecay = 0.6,\n      forces = new Map(),\n      stepper = timer(step),\n      event = dispatch(\"tick\", \"end\"),\n      random = lcg();\n\n  if (nodes == null) nodes = [];\n\n  function step() {\n    tick();\n    event.call(\"tick\", simulation);\n    if (alpha < alphaMin) {\n      stepper.stop();\n      event.call(\"end\", simulation);\n    }\n  }\n\n  function tick(iterations) {\n    var i, n = nodes.length, node;\n\n    if (iterations === undefined) iterations = 1;\n\n    for (var k = 0; k < iterations; ++k) {\n      alpha += (alphaTarget - alpha) * alphaDecay;\n\n      forces.forEach(function(force) {\n        force(alpha);\n      });\n\n      for (i = 0; i < n; ++i) {\n        node = nodes[i];\n        if (node.fx == null) node.x += node.vx *= velocityDecay;\n        else node.x = node.fx, node.vx = 0;\n        if (node.fy == null) node.y += node.vy *= velocityDecay;\n        else node.y = node.fy, node.vy = 0;\n      }\n    }\n\n    return simulation;\n  }\n\n  function initializeNodes() {\n    for (var i = 0, n = nodes.length, node; i < n; ++i) {\n      node = nodes[i], node.index = i;\n      if (node.fx != null) node.x = node.fx;\n      if (node.fy != null) node.y = node.fy;\n      if (isNaN(node.x) || isNaN(node.y)) {\n        var radius = initialRadius * Math.sqrt(0.5 + i), angle = i * initialAngle;\n        node.x = radius * Math.cos(angle);\n        node.y = radius * Math.sin(angle);\n      }\n      if (isNaN(node.vx) || isNaN(node.vy)) {\n        node.vx = node.vy = 0;\n      }\n    }\n  }\n\n  function initializeForce(force) {\n    if (force.initialize) force.initialize(nodes, random);\n    return force;\n  }\n\n  initializeNodes();\n\n  return simulation = {\n    tick: tick,\n\n    restart: function() {\n      return stepper.restart(step), simulation;\n    },\n\n    stop: function() {\n      return stepper.stop(), simulation;\n    },\n\n    nodes: function(_) {\n      return arguments.length ? (nodes = _, initializeNodes(), forces.forEach(initializeForce), simulation) : nodes;\n    },\n\n    alpha: function(_) {\n      return arguments.length ? (alpha = +_, simulation) : alpha;\n    },\n\n    alphaMin: function(_) {\n      return arguments.length ? (alphaMin = +_, simulation) : alphaMin;\n    },\n\n    alphaDecay: function(_) {\n      return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay;\n    },\n\n    alphaTarget: function(_) {\n      return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget;\n    },\n\n    velocityDecay: function(_) {\n      return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay;\n    },\n\n    randomSource: function(_) {\n      return arguments.length ? (random = _, forces.forEach(initializeForce), simulation) : random;\n    },\n\n    force: function(name, _) {\n      return arguments.length > 1 ? ((_ == null ? forces.delete(name) : forces.set(name, initializeForce(_))), simulation) : forces.get(name);\n    },\n\n    find: function(x, y, radius) {\n      var i = 0,\n          n = nodes.length,\n          dx,\n          dy,\n          d2,\n          node,\n          closest;\n\n      if (radius == null) radius = Infinity;\n      else radius *= radius;\n\n      for (i = 0; i < n; ++i) {\n        node = nodes[i];\n        dx = x - node.x;\n        dy = y - node.y;\n        d2 = dx * dx + dy * dy;\n        if (d2 < radius) closest = node, radius = d2;\n      }\n\n      return closest;\n    },\n\n    on: function(name, _) {\n      return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name);\n    }\n  };\n}\n"
  },
  {
    "path": "src/x.js",
    "content": "import constant from \"./constant.js\";\n\nexport default function(x) {\n  var strength = constant(0.1),\n      nodes,\n      strengths,\n      xz;\n\n  if (typeof x !== \"function\") x = constant(x == null ? 0 : +x);\n\n  function force(alpha) {\n    for (var i = 0, n = nodes.length, node; i < n; ++i) {\n      node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha;\n    }\n  }\n\n  function initialize() {\n    if (!nodes) return;\n    var i, n = nodes.length;\n    strengths = new Array(n);\n    xz = new Array(n);\n    for (i = 0; i < n; ++i) {\n      strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);\n    }\n  }\n\n  force.initialize = function(_) {\n    nodes = _;\n    initialize();\n  };\n\n  force.strength = function(_) {\n    return arguments.length ? (strength = typeof _ === \"function\" ? _ : constant(+_), initialize(), force) : strength;\n  };\n\n  force.x = function(_) {\n    return arguments.length ? (x = typeof _ === \"function\" ? _ : constant(+_), initialize(), force) : x;\n  };\n\n  return force;\n}\n"
  },
  {
    "path": "src/y.js",
    "content": "import constant from \"./constant.js\";\n\nexport default function(y) {\n  var strength = constant(0.1),\n      nodes,\n      strengths,\n      yz;\n\n  if (typeof y !== \"function\") y = constant(y == null ? 0 : +y);\n\n  function force(alpha) {\n    for (var i = 0, n = nodes.length, node; i < n; ++i) {\n      node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha;\n    }\n  }\n\n  function initialize() {\n    if (!nodes) return;\n    var i, n = nodes.length;\n    strengths = new Array(n);\n    yz = new Array(n);\n    for (i = 0; i < n; ++i) {\n      strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);\n    }\n  }\n\n  force.initialize = function(_) {\n    nodes = _;\n    initialize();\n  };\n\n  force.strength = function(_) {\n    return arguments.length ? (strength = typeof _ === \"function\" ? _ : constant(+_), initialize(), force) : strength;\n  };\n\n  force.y = function(_) {\n    return arguments.length ? (y = typeof _ === \"function\" ? _ : constant(+_), initialize(), force) : y;\n  };\n\n  return force;\n}\n"
  },
  {
    "path": "test/.eslintrc.json",
    "content": "{\n  \"extends\": \"eslint:recommended\",\n  \"parserOptions\": {\n    \"sourceType\": \"module\",\n    \"ecmaVersion\": 8\n  },\n  \"env\": {\n    \"es6\": true,\n    \"mocha\": true\n  }\n}\n"
  },
  {
    "path": "test/asserts.js",
    "content": "import assert from \"assert\";\n\nexport function assertNodeEqual(actual, expected, delta = 1e-6) {\n  assert(nodeEqual(actual, expected, delta), `${actual} and ${expected} should be similar`);\n}\n\nfunction nodeEqual(actual, expected, delta) {\n  return actual.index == expected.index\n      && Math.abs(actual.x - expected.x) < delta\n      && Math.abs(actual.vx - expected.vx) < delta\n      && Math.abs(actual.y - expected.y) < delta\n      && Math.abs(actual.vy - expected.vy) < delta\n      && !(Math.abs(actual.fx - expected.fx) > delta)\n      && !(Math.abs(actual.fy - expected.fy) > delta);\n}\n"
  },
  {
    "path": "test/center-test.js",
    "content": "import {forceCenter, forceSimulation} from \"../src/index.js\";\nimport {assertNodeEqual} from \"./asserts.js\";\n\nit(\"forceCenter repositions nodes\", () => {\n  const center = forceCenter(0, 0);\n  const f = forceSimulation().force(\"center\", center).stop();\n  const a = {x: 100, y: 0}, b = {x: 200, y: 0}, c = {x: 300, y: 0};\n  f.nodes([a, b, c]);\n  f.tick();\n  assertNodeEqual(a, {index: 0, x: -100, y: 0, vy: 0, vx: 0});\n  assertNodeEqual(b, {index: 1, x: 0, y: 0, vy: 0, vx: 0});\n  assertNodeEqual(c, {index: 2, x: 100, y: 0, vy: 0, vx: 0});\n});\n\nit(\"forceCenter respects fixed positions\", () => {\n  const center = forceCenter();\n  const f = forceSimulation().force(\"center\", center).stop();\n  const a = {fx: 0, fy: 0}, b = {}, c = {};\n  f.nodes([a, b, c]);\n  f.tick();\n  assertNodeEqual(a, {fx: 0, fy: 0, index: 0, x: 0, y: 0, vy: 0, vx: 0});\n});\n"
  },
  {
    "path": "test/collide-test.js",
    "content": "import assert from \"assert\";\nimport {forceCollide, forceSimulation} from \"../src/index.js\";\nimport {assertNodeEqual} from \"./asserts.js\";\n\nit(\"forceCollide collides nodes\", () => {\n  const collide = forceCollide(1);\n  const f = forceSimulation().force(\"collide\", collide).stop();\n  const a = {}, b = {}, c = {};\n  f.nodes([a, b, c]);\n  f.tick(10);\n  assertNodeEqual(a, {index: 0, x: 7.0710678118654755, y: 0, vy: 0, vx: 0});\n  assertNodeEqual(b, {index: 1, x: -9.03088751750192, y: 8.27303273571596, vy: 0, vx: 0});\n  assertNodeEqual(c, {index: 2, x: 1.3823220809823638, y: -15.750847141167634, vy: 0, vx: 0});\n  collide.radius(100);\n  f.tick(10);\n  assertNodeEqual(a, {index: 0, x: 174.08616723117228, y: 66.51743051995625, vy: 0.26976816231064354, vx: 0.677346615710878});\n  assertNodeEqual(b, {index: 1, x: -139.73606544743998, y: 95.69860503079263, vy: 0.3545632444404687, vx: -0.5300880593105067});\n  assertNodeEqual(c, {index: 2, x: -34.9275994083864, y: -169.69384995620052, vy: -0.6243314067511122, vx: -0.1472585564003713});\n});\n\n\nit(\"forceCollide respects fixed positions\", () => {\n  const collide = forceCollide(1);\n  const f = forceSimulation().force(\"collide\", collide).stop();\n  const a = {fx: 0, fy: 0}, b = {}, c = {};\n  f.nodes([a, b, c]);\n  f.tick(10);\n  assertNodeEqual(a, {fx: 0, fy: 0, index: 0, x: 0, y: 0, vy: 0, vx: 0});\n  collide.radius(100);\n  f.tick(10);\n  assertNodeEqual(a, {fx: 0, fy: 0, index: 0, x: 0, y: 0, vy: 0, vx: 0});\n});\n\nit(\"forceCollide jiggles equal positions\", () => {\n  const collide = forceCollide(1);\n  const f = forceSimulation().force(\"collide\", collide).stop();\n  const a = {x: 0, y: 0}, b = {x: 0, y: 0};\n  f.nodes([a, b]);\n  f.tick();\n  assert(a.x !== b.x);\n  assert(a.y !== b.y);\n  assert.strictEqual(a.vx, -b.vx);\n  assert.strictEqual(a.vy, -b.vy);\n});\n\nit(\"forceCollide jiggles in a reproducible way\", () => {\n  const nodes = Array.from({length:10}, () => ({x: 0, y: 0}));\n  forceSimulation().nodes(nodes).force(\"collide\", forceCollide()).stop().tick(50);\n  assertNodeEqual(nodes[0], {x: -5.371433857229194, y: -2.6644608278592576, index: 0, vy: 0, vx: 0});\n});\n"
  },
  {
    "path": "test/find-test.js",
    "content": "import assert from \"assert\";\nimport {forceSimulation} from \"../src/index.js\";\n\nit(\"simulation.find finds a node\", () => {\n  const f = forceSimulation().stop();\n  const a = {x: 5, y: 0}, b = {x: 10, y: 16}, c = {x: -10, y: -4};\n  f.nodes([a, b, c]);\n  assert.strictEqual(f.find(0, 0), a);\n  assert.strictEqual(f.find(0, 20), b);\n});\n\nit(\"simulation.find(x, y, radius) finds a node within radius\", () => {\n  const f = forceSimulation().stop();\n  const a = {x: 5, y: 0}, b = {x: 10, y: 16}, c = {x: -10, y: -4};\n  f.nodes([a, b, c]);\n  assert.strictEqual(f.find(0, 0), a);\n  assert.strictEqual(f.find(0, 0, 1), undefined);\n  assert.strictEqual(f.find(0, 20), b);\n});\n"
  },
  {
    "path": "test/simulation-test.js",
    "content": "import assert from \"assert\";\nimport {forceSimulation} from \"../src/index.js\";\nimport {assertNodeEqual} from \"./asserts.js\";\n\nit(\"forceSimulation() returns a simulation\", () => {\n  const f = forceSimulation().stop();\n  assert.deepStrictEqual(Object.keys(f).sort(), [ 'alpha', 'alphaDecay', 'alphaMin', 'alphaTarget', 'find', 'force', 'nodes', 'on', 'randomSource', 'restart', 'stop', 'tick', 'velocityDecay' ]);\n});\n\nit(\"simulation.nodes(nodes) initializes a simulation with indices & phyllotaxis positions, 0 speed\", () => {\n  const f = forceSimulation().stop();\n  const a = {}, b = {}, c = {};\n  f.nodes([a, b, c]);\n  assertNodeEqual(a, {index: 0, x: 7.0710678118654755, y: 0, vy: 0, vx: 0});\n  assertNodeEqual(b, {index: 1, x: -9.03088751750192, y: 8.27303273571596, vy: 0, vx: 0});\n  assertNodeEqual(c, {index: 2, x: 1.3823220809823638, y: -15.750847141167634, vy: 0, vx: 0});\n});\n\n"
  },
  {
    "path": "test/x-test.js",
    "content": "import assert from \"assert\";\nimport {forceSimulation, forceX, forceY} from \"../src/index.js\";\nimport {assertNodeEqual} from \"./asserts.js\";\n\nit(\"forceX centers nodes\", () => {\n  const x = forceX(200);\n  const f = forceSimulation().force(\"x\", x).stop();\n  const a = { x: 100, y: 0 }, b = { x: 200, y: 0 }, c = { x: 300, y: 0 };\n  f.nodes([a, b, c]);\n  f.tick(30);\n  assert(a.x > 190);\n  assert(a.vx > 0);\n  assert.strictEqual(b.x, 200);\n  assert.strictEqual(b.vx, 0);\n  assert(c.x < 210);\n  assert(c.vx < 0);\n});\n\nit(\"forceY centers nodes\", () => {\n  const y = forceY(200);\n  const f = forceSimulation().force(\"y\", y).stop();\n  const a = { y: 100, x: 0 }, b = { y: 200, x: 0 }, c = { y: 300, x: 0 };\n  f.nodes([a, b, c]);\n  f.tick(30);\n  assert(a.y > 190);\n  assert(a.vy > 0);\n  assert.strictEqual(b.y, 200);\n  assert.strictEqual(b.vy, 0);\n  assert(c.y < 210);\n  assert(c.vy < 0);\n});\n\nit(\"forceX respects fixed positions\", () => {\n  const x = forceX(200);\n  const f = forceSimulation().force(\"x\", x).stop();\n  const a = { fx: 0, fy:0 }, b = {}, c = {};\n  f.nodes([a, b, c]);\n  f.tick();\n  assertNodeEqual(a, { fx: 0, fy: 0, index: 0, x: 0, y: 0, vy: 0, vx: 0 });\n});\n\nit(\"forceY respects fixed positions\", () => {\n  const y = forceX(200);\n  const f = forceSimulation().force(\"y\", y).stop();\n  const a = { fx: 0, fy:0 }, b = {}, c = {};\n  f.nodes([a, b, c]);\n  f.tick();\n  assertNodeEqual(a, { fx: 0, fy: 0, index: 0, x: 0, y: 0, vy: 0, vx: 0 });\n});\n\nit(\"forceX.x() accessor\", () => {\n  const x = forceX().x(d => d.x0);\n  const f = forceSimulation().force(\"x\", x).stop();\n  const a = { x: 100, y: 0, x0: 300 }, b = { x: 200, y: 0, x0: 200 }, c = { x: 300, y: 0, x0: 100 };\n  f.nodes([a, b, c]);\n  f.tick(30);\n  assert(a.x > 290);\n  assert(a.vx > 0);\n  assert.strictEqual(b.x, 200);\n  assert.strictEqual(b.vx, 0);\n  assert(c.x < 110);\n  assert(c.vx < 0);\n});\n\nit(\"forceY.y() accessor\", () => {\n  const y = forceY().y(d => d.y0);\n  const f = forceSimulation().force(\"y\", y).stop();\n  const a = { y: 100, x: 0, y0: 300 }, b = { y: 200, x: 0, y0: 200 }, c = { y: 300, x: 0, y0: 100 };\n  f.nodes([a, b, c]);\n  f.tick(30);\n  assert(a.y > 290);\n  assert(a.vy > 0);\n  assert.strictEqual(b.y, 200);\n  assert.strictEqual(b.vy, 0);\n  assert(c.y < 110);\n  assert(c.vy < 0);\n});\n"
  }
]