Full Code of d3/d3-force for AI

main c3e73cf64181 cached
27 files
30.6 KB
10.2k tokens
31 symbols
2 requests
Download .txt
Repository: d3/d3-force
Branch: main
Commit: c3e73cf64181
Files: 27
Total size: 30.6 KB

Directory structure:
gitextract_xmeu_jkh/

├── .eslintrc.json
├── .github/
│   ├── eslint.json
│   └── workflows/
│       └── node.js.yml
├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── rollup.config.js
├── src/
│   ├── center.js
│   ├── collide.js
│   ├── constant.js
│   ├── index.js
│   ├── jiggle.js
│   ├── lcg.js
│   ├── link.js
│   ├── manyBody.js
│   ├── radial.js
│   ├── simulation.js
│   ├── x.js
│   └── y.js
└── test/
    ├── .eslintrc.json
    ├── asserts.js
    ├── center-test.js
    ├── collide-test.js
    ├── find-test.js
    ├── simulation-test.js
    └── x-test.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintrc.json
================================================
{
  "extends": "eslint:recommended",
  "parserOptions": {
    "sourceType": "module",
    "ecmaVersion": 8
  },
  "env": {
    "es6": true
  },
  "rules": {
    "no-cond-assign": 0
  }
}


================================================
FILE: .github/eslint.json
================================================
{
  "problemMatcher": [
    {
      "owner": "eslint-compact",
      "pattern": [
        {
          "regexp": "^(.+):\\sline\\s(\\d+),\\scol\\s(\\d+),\\s(Error|Warning|Info)\\s-\\s(.+)\\s\\((.+)\\)$",
          "file": 1,
          "line": 2,
          "column": 3,
          "severity": 4,
          "message": 5,
          "code": 6
        }
      ]
    }
  ]
}


================================================
FILE: .github/workflows/node.js.yml
================================================
# https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: Node.js CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [14.x]

    steps:
    - uses: actions/checkout@v2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v1
      with:
        node-version: ${{ matrix.node-version }}
    - run: yarn --frozen-lockfile
    - run: |
        echo ::add-matcher::.github/eslint.json
        yarn run eslint src test --format=compact
    - run: yarn test


================================================
FILE: .gitignore
================================================
*.sublime-workspace
.DS_Store
dist/
node_modules
npm-debug.log


================================================
FILE: LICENSE
================================================
Copyright 2010-2021 Mike Bostock

Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.


================================================
FILE: README.md
================================================
# d3-force

<a href="https://d3js.org"><img src="https://github.com/d3/d3/raw/main/docs/public/logo.svg" width="256" height="256"></a>

This 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).

## Resources

* [Documentation](https://d3js.org/d3-force)
* [Examples](https://observablehq.com/collection/@d3/d3-force)
* [Releases](https://github.com/d3/d3-force/releases)
* [Getting help](https://d3js.org/community)



================================================
FILE: package.json
================================================
{
  "name": "d3-force",
  "version": "3.0.0",
  "description": "Force-directed graph layout using velocity Verlet integration.",
  "homepage": "https://d3js.org/d3-force/",
  "repository": {
    "type": "git",
    "url": "https://github.com/d3/d3-force.git"
  },
  "keywords": [
    "d3",
    "d3-module",
    "layout",
    "network",
    "graph",
    "force",
    "verlet",
    "infovis"
  ],
  "license": "ISC",
  "author": {
    "name": "Mike Bostock",
    "url": "https://bost.ocks.org/mike"
  },
  "type": "module",
  "files": [
    "src/**/*.js",
    "dist/**/*.js"
  ],
  "module": "src/index.js",
  "main": "src/index.js",
  "jsdelivr": "dist/d3-force.min.js",
  "unpkg": "dist/d3-force.min.js",
  "exports": {
    "umd": "./dist/d3-force.min.js",
    "default": "./src/index.js"
  },
  "sideEffects": false,
  "dependencies": {
    "d3-dispatch": "1 - 3",
    "d3-quadtree": "1 - 3",
    "d3-timer": "1 - 3"
  },
  "devDependencies": {
    "eslint": "7",
    "mocha": "8",
    "rollup": "2",
    "rollup-plugin-terser": "7"
  },
  "scripts": {
    "test": "mocha 'test/**/*-test.js' && eslint src test",
    "prepublishOnly": "rm -rf dist && yarn test && rollup -c",
    "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 -"
  },
  "engines": {
    "node": ">=12"
  }
}


================================================
FILE: rollup.config.js
================================================
import {readFileSync} from "fs";
import {terser} from "rollup-plugin-terser";
import * as meta from "./package.json";

// Extract copyrights from the LICENSE.
const copyright = readFileSync("./LICENSE", "utf-8")
  .split(/\n/g)
  .filter(line => /^Copyright\s+/.test(line))
  .map(line => line.replace(/^Copyright\s+/, ""))
  .join(", ");

const config = {
  input: "src/index.js",
  external: Object.keys(meta.dependencies || {}).filter(key => /^d3-/.test(key)),
  output: {
    file: `dist/${meta.name}.js`,
    name: "d3",
    format: "umd",
    indent: false,
    extend: true,
    banner: `// ${meta.homepage} v${meta.version} Copyright ${copyright}`,
    globals: Object.assign({}, ...Object.keys(meta.dependencies || {}).filter(key => /^d3-/.test(key)).map(key => ({[key]: "d3"})))
  },
  plugins: []
};

export default [
  config,
  {
    ...config,
    output: {
      ...config.output,
      file: `dist/${meta.name}.min.js`
    },
    plugins: [
      ...config.plugins,
      terser({
        output: {
          preamble: config.output.banner
        }
      })
    ]
  }
];


================================================
FILE: src/center.js
================================================
export default function(x, y) {
  var nodes, strength = 1;

  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) * strength, sy = (sy / n - y) * strength, 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;
  };

  force.strength = function(_) {
    return arguments.length ? (strength = +_, force) : strength;
  };

  return force;
}


================================================
FILE: src/collide.js
================================================
import {quadtree} from "d3-quadtree";
import constant from "./constant.js";
import jiggle from "./jiggle.js";

function x(d) {
  return d.x + d.vx;
}

function y(d) {
  return d.y + d.vy;
}

export default function(radius) {
  var nodes,
      radii,
      random,
      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 = quadtree(nodes, x, y).visitAfter(prepare);
      for (i = 0; i < n; ++i) {
        node = nodes[i];
        ri = radii[node.index], ri2 = ri * ri;
        xi = node.x + node.vx;
        yi = node.y + node.vy;
        tree.visit(apply);
      }
    }

    function apply(quad, x0, y0, x1, y1) {
      var data = quad.data, rj = quad.r, r = ri + rj;
      if (data) {
        if (data.index > node.index) {
          var x = xi - data.x - data.vx,
              y = yi - data.y - data.vy,
              l = x * x + y * y;
          if (l < r * r) {
            if (x === 0) x = jiggle(random), l += x * x;
            if (y === 0) y = jiggle(random), l += y * y;
            l = (r - (l = Math.sqrt(l))) / l * strength;
            node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj));
            node.vy += (y *= l) * r;
            data.vx -= x * (r = 1 - r);
            data.vy -= y * r;
          }
        }
        return;
      }
      return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r;
    }
  }

  function prepare(quad) {
    if (quad.data) return quad.r = radii[quad.data.index];
    for (var i = quad.r = 0; i < 4; ++i) {
      if (quad[i] && quad[i].r > quad.r) {
        quad.r = quad[i].r;
      }
    }
  }

  function initialize() {
    if (!nodes) return;
    var i, n = nodes.length, node;
    radii = new Array(n);
    for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes);
  }

  force.initialize = function(_nodes, _random) {
    nodes = _nodes;
    random = _random;
    initialize();
  };

  force.iterations = function(_) {
    return arguments.length ? (iterations = +_, force) : iterations;
  };

  force.strength = function(_) {
    return arguments.length ? (strength = +_, force) : strength;
  };

  force.radius = function(_) {
    return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), initialize(), force) : radius;
  };

  return force;
}


================================================
FILE: src/constant.js
================================================
export default function(x) {
  return function() {
    return x;
  };
}


================================================
FILE: src/index.js
================================================
export {default as forceCenter} from "./center.js";
export {default as forceCollide} from "./collide.js";
export {default as forceLink} from "./link.js";
export {default as forceManyBody} from "./manyBody.js";
export {default as forceRadial} from "./radial.js";
export {default as forceSimulation} from "./simulation.js";
export {default as forceX} from "./x.js";
export {default as forceY} from "./y.js";


================================================
FILE: src/jiggle.js
================================================
export default function(random) {
  return (random() - 0.5) * 1e-6;
}


================================================
FILE: src/lcg.js
================================================
// https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
const a = 1664525;
const c = 1013904223;
const m = 4294967296; // 2^32

export default function() {
  let s = 1;
  return () => (s = (a * s + c) % m) / m;
}


================================================
FILE: src/link.js
================================================
import constant from "./constant.js";
import jiggle from "./jiggle.js";

function index(d) {
  return d.index;
}

function find(nodeById, nodeId) {
  var node = nodeById.get(nodeId);
  if (!node) throw new Error("node not found: " + nodeId);
  return node;
}

export default function(links) {
  var id = index,
      strength = defaultStrength,
      strengths,
      distance = constant(30),
      distances,
      nodes,
      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, 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(random);
        y = target.y + target.vy - source.y - source.vy || jiggle(random);
        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 = 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, _random) {
    nodes = _nodes;
    random = _random;
    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;
}


================================================
FILE: src/manyBody.js
================================================
import {quadtree} from "d3-quadtree";
import constant from "./constant.js";
import jiggle from "./jiggle.js";
import {x, y} from "./simulation.js";

export default function() {
  var nodes,
      node,
      random,
      alpha,
      strength = constant(-30),
      strengths,
      distanceMin2 = 1,
      distanceMax2 = Infinity,
      theta2 = 0.81;

  function force(_) {
    var i, n = nodes.length, tree = quadtree(nodes, x, y).visitAfter(accumulate);
    for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply);
  }

  function initialize() {
    if (!nodes) return;
    var i, n = nodes.length, node;
    strengths = new Array(n);
    for (i = 0; i < n; ++i) node = nodes[i], strengths[node.index] = +strength(node, i, nodes);
  }

  function accumulate(quad) {
    var strength = 0, q, c, weight = 0, x, y, i;

    // For internal nodes, accumulate forces from child quadrants.
    if (quad.length) {
      for (x = y = i = 0; i < 4; ++i) {
        if ((q = quad[i]) && (c = Math.abs(q.value))) {
          strength += q.value, weight += c, x += c * q.x, y += c * q.y;
        }
      }
      quad.x = x / weight;
      quad.y = y / weight;
    }

    // For leaf nodes, accumulate forces from coincident quadrants.
    else {
      q = quad;
      q.x = q.data.x;
      q.y = q.data.y;
      do strength += strengths[q.data.index];
      while (q = q.next);
    }

    quad.value = strength;
  }

  function apply(quad, x1, _, x2) {
    if (!quad.value) return true;

    var x = quad.x - node.x,
        y = quad.y - node.y,
        w = x2 - x1,
        l = x * x + y * y;

    // Apply the Barnes-Hut approximation if possible.
    // Limit forces for very close nodes; randomize direction if coincident.
    if (w * w / theta2 < l) {
      if (l < distanceMax2) {
        if (x === 0) x = jiggle(random), l += x * x;
        if (y === 0) y = jiggle(random), 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(random), l += x * x;
      if (y === 0) y = jiggle(random), 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, _random) {
    nodes = _nodes;
    random = _random;
    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;
}


================================================
FILE: src/radial.js
================================================
import constant from "./constant.js";

export default function(radius, x, y) {
  var nodes,
      strength = constant(0.1),
      strengths,
      radiuses;

  if (typeof radius !== "function") radius = constant(+radius);
  if (x == null) x = 0;
  if (y == null) y = 0;

  function force(alpha) {
    for (var i = 0, n = nodes.length; i < n; ++i) {
      var node = nodes[i],
          dx = node.x - x || 1e-6,
          dy = node.y - y || 1e-6,
          r = Math.sqrt(dx * dx + dy * dy),
          k = (radiuses[i] - r) * strengths[i] * alpha / r;
      node.vx += dx * k;
      node.vy += dy * k;
    }
  }

  function initialize() {
    if (!nodes) return;
    var i, n = nodes.length;
    strengths = new Array(n);
    radiuses = new Array(n);
    for (i = 0; i < n; ++i) {
      radiuses[i] = +radius(nodes[i], i, nodes);
      strengths[i] = isNaN(radiuses[i]) ? 0 : +strength(nodes[i], i, nodes);
    }
  }

  force.initialize = function(_) {
    nodes = _, initialize();
  };

  force.strength = function(_) {
    return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength;
  };

  force.radius = function(_) {
    return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), initialize(), force) : radius;
  };

  force.x = function(_) {
    return arguments.length ? (x = +_, force) : x;
  };

  force.y = function(_) {
    return arguments.length ? (y = +_, force) : y;
  };

  return force;
}


================================================
FILE: src/simulation.js
================================================
import {dispatch} from "d3-dispatch";
import {timer} from "d3-timer";
import lcg from "./lcg.js";

export function x(d) {
  return d.x;
}

export function y(d) {
  return d.y;
}

var initialRadius = 10,
    initialAngle = Math.PI * (3 - Math.sqrt(5));

export default function(nodes) {
  var simulation,
      alpha = 1,
      alphaMin = 0.001,
      alphaDecay = 1 - Math.pow(alphaMin, 1 / 300),
      alphaTarget = 0,
      velocityDecay = 0.6,
      forces = new Map(),
      stepper = timer(step),
      event = dispatch("tick", "end"),
      random = lcg();

  if (nodes == null) nodes = [];

  function step() {
    tick();
    event.call("tick", simulation);
    if (alpha < alphaMin) {
      stepper.stop();
      event.call("end", simulation);
    }
  }

  function tick(iterations) {
    var i, n = nodes.length, node;

    if (iterations === undefined) iterations = 1;

    for (var k = 0; k < iterations; ++k) {
      alpha += (alphaTarget - alpha) * alphaDecay;

      forces.forEach(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;
      }
    }

    return simulation;
  }

  function initializeNodes() {
    for (var i = 0, n = nodes.length, node; i < n; ++i) {
      node = nodes[i], node.index = i;
      if (node.fx != null) node.x = node.fx;
      if (node.fy != null) node.y = node.fy;
      if (isNaN(node.x) || isNaN(node.y)) {
        var radius = initialRadius * Math.sqrt(0.5 + 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, random);
    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.forEach(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;
    },

    randomSource: function(_) {
      return arguments.length ? (random = _, forces.forEach(initializeForce), simulation) : random;
    },

    force: function(name, _) {
      return arguments.length > 1 ? ((_ == null ? forces.delete(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);
    }
  };
}


================================================
FILE: src/x.js
================================================
import constant from "./constant.js";

export default function(x) {
  var strength = constant(0.1),
      nodes,
      strengths,
      xz;

  if (typeof x !== "function") x = constant(x == null ? 0 : +x);

  function force(alpha) {
    for (var i = 0, n = nodes.length, node; i < n; ++i) {
      node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha;
    }
  }

  function initialize() {
    if (!nodes) return;
    var i, n = nodes.length;
    strengths = new Array(n);
    xz = new Array(n);
    for (i = 0; i < n; ++i) {
      strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
    }
  }

  force.initialize = function(_) {
    nodes = _;
    initialize();
  };

  force.strength = function(_) {
    return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength;
  };

  force.x = function(_) {
    return arguments.length ? (x = typeof _ === "function" ? _ : constant(+_), initialize(), force) : x;
  };

  return force;
}


================================================
FILE: src/y.js
================================================
import constant from "./constant.js";

export default function(y) {
  var strength = constant(0.1),
      nodes,
      strengths,
      yz;

  if (typeof y !== "function") y = constant(y == null ? 0 : +y);

  function force(alpha) {
    for (var i = 0, n = nodes.length, node; i < n; ++i) {
      node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha;
    }
  }

  function initialize() {
    if (!nodes) return;
    var i, n = nodes.length;
    strengths = new Array(n);
    yz = new Array(n);
    for (i = 0; i < n; ++i) {
      strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
    }
  }

  force.initialize = function(_) {
    nodes = _;
    initialize();
  };

  force.strength = function(_) {
    return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength;
  };

  force.y = function(_) {
    return arguments.length ? (y = typeof _ === "function" ? _ : constant(+_), initialize(), force) : y;
  };

  return force;
}


================================================
FILE: test/.eslintrc.json
================================================
{
  "extends": "eslint:recommended",
  "parserOptions": {
    "sourceType": "module",
    "ecmaVersion": 8
  },
  "env": {
    "es6": true,
    "mocha": true
  }
}


================================================
FILE: test/asserts.js
================================================
import assert from "assert";

export function assertNodeEqual(actual, expected, delta = 1e-6) {
  assert(nodeEqual(actual, expected, delta), `${actual} and ${expected} should be similar`);
}

function nodeEqual(actual, expected, delta) {
  return actual.index == expected.index
      && Math.abs(actual.x - expected.x) < delta
      && Math.abs(actual.vx - expected.vx) < delta
      && Math.abs(actual.y - expected.y) < delta
      && Math.abs(actual.vy - expected.vy) < delta
      && !(Math.abs(actual.fx - expected.fx) > delta)
      && !(Math.abs(actual.fy - expected.fy) > delta);
}


================================================
FILE: test/center-test.js
================================================
import {forceCenter, forceSimulation} from "../src/index.js";
import {assertNodeEqual} from "./asserts.js";

it("forceCenter repositions nodes", () => {
  const center = forceCenter(0, 0);
  const f = forceSimulation().force("center", center).stop();
  const a = {x: 100, y: 0}, b = {x: 200, y: 0}, c = {x: 300, y: 0};
  f.nodes([a, b, c]);
  f.tick();
  assertNodeEqual(a, {index: 0, x: -100, y: 0, vy: 0, vx: 0});
  assertNodeEqual(b, {index: 1, x: 0, y: 0, vy: 0, vx: 0});
  assertNodeEqual(c, {index: 2, x: 100, y: 0, vy: 0, vx: 0});
});

it("forceCenter respects fixed positions", () => {
  const center = forceCenter();
  const f = forceSimulation().force("center", center).stop();
  const a = {fx: 0, fy: 0}, b = {}, c = {};
  f.nodes([a, b, c]);
  f.tick();
  assertNodeEqual(a, {fx: 0, fy: 0, index: 0, x: 0, y: 0, vy: 0, vx: 0});
});


================================================
FILE: test/collide-test.js
================================================
import assert from "assert";
import {forceCollide, forceSimulation} from "../src/index.js";
import {assertNodeEqual} from "./asserts.js";

it("forceCollide collides nodes", () => {
  const collide = forceCollide(1);
  const f = forceSimulation().force("collide", collide).stop();
  const a = {}, b = {}, c = {};
  f.nodes([a, b, c]);
  f.tick(10);
  assertNodeEqual(a, {index: 0, x: 7.0710678118654755, y: 0, vy: 0, vx: 0});
  assertNodeEqual(b, {index: 1, x: -9.03088751750192, y: 8.27303273571596, vy: 0, vx: 0});
  assertNodeEqual(c, {index: 2, x: 1.3823220809823638, y: -15.750847141167634, vy: 0, vx: 0});
  collide.radius(100);
  f.tick(10);
  assertNodeEqual(a, {index: 0, x: 174.08616723117228, y: 66.51743051995625, vy: 0.26976816231064354, vx: 0.677346615710878});
  assertNodeEqual(b, {index: 1, x: -139.73606544743998, y: 95.69860503079263, vy: 0.3545632444404687, vx: -0.5300880593105067});
  assertNodeEqual(c, {index: 2, x: -34.9275994083864, y: -169.69384995620052, vy: -0.6243314067511122, vx: -0.1472585564003713});
});


it("forceCollide respects fixed positions", () => {
  const collide = forceCollide(1);
  const f = forceSimulation().force("collide", collide).stop();
  const a = {fx: 0, fy: 0}, b = {}, c = {};
  f.nodes([a, b, c]);
  f.tick(10);
  assertNodeEqual(a, {fx: 0, fy: 0, index: 0, x: 0, y: 0, vy: 0, vx: 0});
  collide.radius(100);
  f.tick(10);
  assertNodeEqual(a, {fx: 0, fy: 0, index: 0, x: 0, y: 0, vy: 0, vx: 0});
});

it("forceCollide jiggles equal positions", () => {
  const collide = forceCollide(1);
  const f = forceSimulation().force("collide", collide).stop();
  const a = {x: 0, y: 0}, b = {x: 0, y: 0};
  f.nodes([a, b]);
  f.tick();
  assert(a.x !== b.x);
  assert(a.y !== b.y);
  assert.strictEqual(a.vx, -b.vx);
  assert.strictEqual(a.vy, -b.vy);
});

it("forceCollide jiggles in a reproducible way", () => {
  const nodes = Array.from({length:10}, () => ({x: 0, y: 0}));
  forceSimulation().nodes(nodes).force("collide", forceCollide()).stop().tick(50);
  assertNodeEqual(nodes[0], {x: -5.371433857229194, y: -2.6644608278592576, index: 0, vy: 0, vx: 0});
});


================================================
FILE: test/find-test.js
================================================
import assert from "assert";
import {forceSimulation} from "../src/index.js";

it("simulation.find finds a node", () => {
  const f = forceSimulation().stop();
  const a = {x: 5, y: 0}, b = {x: 10, y: 16}, c = {x: -10, y: -4};
  f.nodes([a, b, c]);
  assert.strictEqual(f.find(0, 0), a);
  assert.strictEqual(f.find(0, 20), b);
});

it("simulation.find(x, y, radius) finds a node within radius", () => {
  const f = forceSimulation().stop();
  const a = {x: 5, y: 0}, b = {x: 10, y: 16}, c = {x: -10, y: -4};
  f.nodes([a, b, c]);
  assert.strictEqual(f.find(0, 0), a);
  assert.strictEqual(f.find(0, 0, 1), undefined);
  assert.strictEqual(f.find(0, 20), b);
});


================================================
FILE: test/simulation-test.js
================================================
import assert from "assert";
import {forceSimulation} from "../src/index.js";
import {assertNodeEqual} from "./asserts.js";

it("forceSimulation() returns a simulation", () => {
  const f = forceSimulation().stop();
  assert.deepStrictEqual(Object.keys(f).sort(), [ 'alpha', 'alphaDecay', 'alphaMin', 'alphaTarget', 'find', 'force', 'nodes', 'on', 'randomSource', 'restart', 'stop', 'tick', 'velocityDecay' ]);
});

it("simulation.nodes(nodes) initializes a simulation with indices & phyllotaxis positions, 0 speed", () => {
  const f = forceSimulation().stop();
  const a = {}, b = {}, c = {};
  f.nodes([a, b, c]);
  assertNodeEqual(a, {index: 0, x: 7.0710678118654755, y: 0, vy: 0, vx: 0});
  assertNodeEqual(b, {index: 1, x: -9.03088751750192, y: 8.27303273571596, vy: 0, vx: 0});
  assertNodeEqual(c, {index: 2, x: 1.3823220809823638, y: -15.750847141167634, vy: 0, vx: 0});
});



================================================
FILE: test/x-test.js
================================================
import assert from "assert";
import {forceSimulation, forceX, forceY} from "../src/index.js";
import {assertNodeEqual} from "./asserts.js";

it("forceX centers nodes", () => {
  const x = forceX(200);
  const f = forceSimulation().force("x", x).stop();
  const a = { x: 100, y: 0 }, b = { x: 200, y: 0 }, c = { x: 300, y: 0 };
  f.nodes([a, b, c]);
  f.tick(30);
  assert(a.x > 190);
  assert(a.vx > 0);
  assert.strictEqual(b.x, 200);
  assert.strictEqual(b.vx, 0);
  assert(c.x < 210);
  assert(c.vx < 0);
});

it("forceY centers nodes", () => {
  const y = forceY(200);
  const f = forceSimulation().force("y", y).stop();
  const a = { y: 100, x: 0 }, b = { y: 200, x: 0 }, c = { y: 300, x: 0 };
  f.nodes([a, b, c]);
  f.tick(30);
  assert(a.y > 190);
  assert(a.vy > 0);
  assert.strictEqual(b.y, 200);
  assert.strictEqual(b.vy, 0);
  assert(c.y < 210);
  assert(c.vy < 0);
});

it("forceX respects fixed positions", () => {
  const x = forceX(200);
  const f = forceSimulation().force("x", x).stop();
  const a = { fx: 0, fy:0 }, b = {}, c = {};
  f.nodes([a, b, c]);
  f.tick();
  assertNodeEqual(a, { fx: 0, fy: 0, index: 0, x: 0, y: 0, vy: 0, vx: 0 });
});

it("forceY respects fixed positions", () => {
  const y = forceX(200);
  const f = forceSimulation().force("y", y).stop();
  const a = { fx: 0, fy:0 }, b = {}, c = {};
  f.nodes([a, b, c]);
  f.tick();
  assertNodeEqual(a, { fx: 0, fy: 0, index: 0, x: 0, y: 0, vy: 0, vx: 0 });
});

it("forceX.x() accessor", () => {
  const x = forceX().x(d => d.x0);
  const f = forceSimulation().force("x", x).stop();
  const a = { x: 100, y: 0, x0: 300 }, b = { x: 200, y: 0, x0: 200 }, c = { x: 300, y: 0, x0: 100 };
  f.nodes([a, b, c]);
  f.tick(30);
  assert(a.x > 290);
  assert(a.vx > 0);
  assert.strictEqual(b.x, 200);
  assert.strictEqual(b.vx, 0);
  assert(c.x < 110);
  assert(c.vx < 0);
});

it("forceY.y() accessor", () => {
  const y = forceY().y(d => d.y0);
  const f = forceSimulation().force("y", y).stop();
  const a = { y: 100, x: 0, y0: 300 }, b = { y: 200, x: 0, y0: 200 }, c = { y: 300, x: 0, y0: 100 };
  f.nodes([a, b, c]);
  f.tick(30);
  assert(a.y > 290);
  assert(a.vy > 0);
  assert.strictEqual(b.y, 200);
  assert.strictEqual(b.vy, 0);
  assert(c.y < 110);
  assert(c.vy < 0);
});
Download .txt
gitextract_xmeu_jkh/

├── .eslintrc.json
├── .github/
│   ├── eslint.json
│   └── workflows/
│       └── node.js.yml
├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── rollup.config.js
├── src/
│   ├── center.js
│   ├── collide.js
│   ├── constant.js
│   ├── index.js
│   ├── jiggle.js
│   ├── lcg.js
│   ├── link.js
│   ├── manyBody.js
│   ├── radial.js
│   ├── simulation.js
│   ├── x.js
│   └── y.js
└── test/
    ├── .eslintrc.json
    ├── asserts.js
    ├── center-test.js
    ├── collide-test.js
    ├── find-test.js
    ├── simulation-test.js
    └── x-test.js
Download .txt
SYMBOL INDEX (31 symbols across 9 files)

FILE: src/center.js
  function force (line 7) | function force() {

FILE: src/collide.js
  function x (line 5) | function x(d) {
  function y (line 9) | function y(d) {
  function force (line 22) | function force() {
  function prepare (line 65) | function prepare(quad) {
  function initialize (line 74) | function initialize() {

FILE: src/link.js
  function index (line 4) | function index(d) {
  function find (line 8) | function find(nodeById, nodeId) {
  function defaultStrength (line 28) | function defaultStrength(link) {
  function force (line 32) | function force(alpha) {
  function initialize (line 49) | function initialize() {
  function initializeStrength (line 74) | function initializeStrength() {
  function initializeDistance (line 82) | function initializeDistance() {

FILE: src/manyBody.js
  function force (line 17) | function force(_) {
  function initialize (line 22) | function initialize() {
  function accumulate (line 29) | function accumulate(quad) {
  function apply (line 55) | function apply(quad, x1, _, x2) {

FILE: src/radial.js
  function force (line 13) | function force(alpha) {
  function initialize (line 25) | function initialize() {

FILE: src/simulation.js
  function x (line 5) | function x(d) {
  function y (line 9) | function y(d) {
  function step (line 30) | function step() {
  function tick (line 39) | function tick(iterations) {
  function initializeNodes (line 63) | function initializeNodes() {
  function initializeForce (line 79) | function initializeForce(force) {

FILE: src/x.js
  function force (line 11) | function force(alpha) {
  function initialize (line 17) | function initialize() {

FILE: src/y.js
  function force (line 11) | function force(alpha) {
  function initialize (line 17) | function initialize() {

FILE: test/asserts.js
  function assertNodeEqual (line 3) | function assertNodeEqual(actual, expected, delta = 1e-6) {
  function nodeEqual (line 7) | function nodeEqual(actual, expected, delta) {
Condensed preview — 27 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (34K chars).
[
  {
    "path": ".eslintrc.json",
    "chars": 187,
    "preview": "{\n  \"extends\": \"eslint:recommended\",\n  \"parserOptions\": {\n    \"sourceType\": \"module\",\n    \"ecmaVersion\": 8\n  },\n  \"env\":"
  },
  {
    "path": ".github/eslint.json",
    "chars": 367,
    "preview": "{\n  \"problemMatcher\": [\n    {\n      \"owner\": \"eslint-compact\",\n      \"pattern\": [\n        {\n          \"regexp\": \"^(.+):\\"
  },
  {
    "path": ".github/workflows/node.js.yml",
    "chars": 648,
    "preview": "# https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions\n\nname: Node.js CI\n\non:\n"
  },
  {
    "path": ".gitignore",
    "chars": 63,
    "preview": "*.sublime-workspace\n.DS_Store\ndist/\nnode_modules\nnpm-debug.log\n"
  },
  {
    "path": "LICENSE",
    "chars": 731,
    "preview": "Copyright 2010-2021 Mike Bostock\n\nPermission to use, copy, modify, and/or distribute this software for any purpose\nwith "
  },
  {
    "path": "README.md",
    "chars": 879,
    "preview": "# d3-force\n\n<a href=\"https://d3js.org\"><img src=\"https://github.com/d3/d3/raw/main/docs/public/logo.svg\" width=\"256\" hei"
  },
  {
    "path": "package.json",
    "chars": 1730,
    "preview": "{\n  \"name\": \"d3-force\",\n  \"version\": \"3.0.0\",\n  \"description\": \"Force-directed graph layout using velocity Verlet integr"
  },
  {
    "path": "rollup.config.js",
    "chars": 1088,
    "preview": "import {readFileSync} from \"fs\";\nimport {terser} from \"rollup-plugin-terser\";\nimport * as meta from \"./package.json\";\n\n/"
  },
  {
    "path": "src/center.js",
    "chars": 798,
    "preview": "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 "
  },
  {
    "path": "src/collide.js",
    "chars": 2520,
    "preview": "import {quadtree} from \"d3-quadtree\";\nimport constant from \"./constant.js\";\nimport jiggle from \"./jiggle.js\";\n\nfunction "
  },
  {
    "path": "src/constant.js",
    "chars": 72,
    "preview": "export default function(x) {\n  return function() {\n    return x;\n  };\n}\n"
  },
  {
    "path": "src/index.js",
    "chars": 406,
    "preview": "export {default as forceCenter} from \"./center.js\";\nexport {default as forceCollide} from \"./collide.js\";\nexport {defaul"
  },
  {
    "path": "src/jiggle.js",
    "chars": 70,
    "preview": "export default function(random) {\n  return (random() - 0.5) * 1e-6;\n}\n"
  },
  {
    "path": "src/lcg.js",
    "chars": 245,
    "preview": "// https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use\nconst a = 1664525;\nconst c = 101"
  },
  {
    "path": "src/link.js",
    "chars": 3294,
    "preview": "import constant from \"./constant.js\";\nimport jiggle from \"./jiggle.js\";\n\nfunction index(d) {\n  return d.index;\n}\n\nfuncti"
  },
  {
    "path": "src/manyBody.js",
    "chars": 3306,
    "preview": "import {quadtree} from \"d3-quadtree\";\nimport constant from \"./constant.js\";\nimport jiggle from \"./jiggle.js\";\nimport {x,"
  },
  {
    "path": "src/radial.js",
    "chars": 1480,
    "preview": "import constant from \"./constant.js\";\n\nexport default function(radius, x, y) {\n  var nodes,\n      strength = constant(0."
  },
  {
    "path": "src/simulation.js",
    "chars": 3869,
    "preview": "import {dispatch} from \"d3-dispatch\";\nimport {timer} from \"d3-timer\";\nimport lcg from \"./lcg.js\";\n\nexport function x(d) "
  },
  {
    "path": "src/x.js",
    "chars": 1033,
    "preview": "import constant from \"./constant.js\";\n\nexport default function(x) {\n  var strength = constant(0.1),\n      nodes,\n      s"
  },
  {
    "path": "src/y.js",
    "chars": 1033,
    "preview": "import constant from \"./constant.js\";\n\nexport default function(y) {\n  var strength = constant(0.1),\n      nodes,\n      s"
  },
  {
    "path": "test/.eslintrc.json",
    "chars": 164,
    "preview": "{\n  \"extends\": \"eslint:recommended\",\n  \"parserOptions\": {\n    \"sourceType\": \"module\",\n    \"ecmaVersion\": 8\n  },\n  \"env\":"
  },
  {
    "path": "test/asserts.js",
    "chars": 589,
    "preview": "import assert from \"assert\";\n\nexport function assertNodeEqual(actual, expected, delta = 1e-6) {\n  assert(nodeEqual(actua"
  },
  {
    "path": "test/center-test.js",
    "chars": 844,
    "preview": "import {forceCenter, forceSimulation} from \"../src/index.js\";\nimport {assertNodeEqual} from \"./asserts.js\";\n\nit(\"forceCe"
  },
  {
    "path": "test/collide-test.js",
    "chars": 2116,
    "preview": "import assert from \"assert\";\nimport {forceCollide, forceSimulation} from \"../src/index.js\";\nimport {assertNodeEqual} fro"
  },
  {
    "path": "test/find-test.js",
    "chars": 664,
    "preview": "import assert from \"assert\";\nimport {forceSimulation} from \"../src/index.js\";\n\nit(\"simulation.find finds a node\", () => "
  },
  {
    "path": "test/simulation-test.js",
    "chars": 885,
    "preview": "import assert from \"assert\";\nimport {forceSimulation} from \"../src/index.js\";\nimport {assertNodeEqual} from \"./asserts.j"
  },
  {
    "path": "test/x-test.js",
    "chars": 2266,
    "preview": "import assert from \"assert\";\nimport {forceSimulation, forceX, forceY} from \"../src/index.js\";\nimport {assertNodeEqual} f"
  }
]

About this extraction

This page contains the full source code of the d3/d3-force GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 27 files (30.6 KB), approximately 10.2k tokens, and a symbol index with 31 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!