/d3-manybody-wasm

WebAssembly C++ implementation of D3's many-body, X and Y forces

Primary LanguageC++MIT LicenseMIT

d3-manybody-wasm

This module provides WebAssembly-powered implementations of force algorithms used in d3-force: forceManyBody(), forceX(), and forceY(). By leveraging WebAssembly, these implementations can significantly improve the performance of force-directed graph layouts, especially for large graphs, without compromising on layout quality.

This is done through a C++ implementation, which is then compiled to WebAssembly. This allows for faster execution of the force calculations, particularly on larger datasets where JavaScript performance may become a bottleneck.

Installing

If you use NPM, npm install d3-manybody-wasm. Otherwise, clone latest version.

Usage

Before using any of the force functions, you must ensure that the WebAssembly module is initialized. Use the ensureInitialized() function to wait for the initialization to complete:

import { ensureInitialized, forceManyBody, forceX, forceY } from 'd3-manybody-wasm';

ensureInitialized().then(() => {
  const simulation = d3.forceSimulation(nodes)
    .force("link", d3.forceLink().id(d => d.id))
    .force("charge", forceManyBody())
    .force("x", forceX())
    .force("y", forceY());

  // ... rest of your simulation code
});

Aside from that, The forceManyBody(), forceX(), and forceY() functions are designed as drop-in replacements for their d3-force counterparts, using the same API.

API Reference

Many-Body Force

# d3.forceManyBody()

Creates a new many-body force with the default parameters. This force can be used to simulate gravity (attraction) or electrostatic charge (repulsion).

# manyBody.strength([strength])

If strength is specified, sets the strength accessor to the specified number or function and returns this force. If strength is not specified, returns the current strength accessor.

# manyBody.theta([theta])

If theta is specified, sets the Barnes–Hut approximation criterion to the specified number and returns this force. If theta is not specified, returns the current value.

# manyBody.distanceMin([distance])

If distance is specified, sets the minimum distance between nodes and returns this force. If distance is not specified, returns the current minimum distance.

# manyBody.distanceMax([distance])

If distance is specified, sets the maximum distance between nodes and returns this force. If distance is not specified, returns the current maximum distance.

Position Forces

# d3.forceX([x])

Creates a new position force along the x-axis towards the given position x.

# d3.forceY([y])

Creates a new position force along the y-axis towards the given position y.

Both position forces share the following methods:

# force.strength([strength])

If strength is specified, sets the strength accessor to the specified number or function and returns this force. If strength is not specified, returns the current strength accessor.

# force.x([x])

If x is specified, sets the x-coordinate accessor to the specified number or function and returns this force. If x is not specified, returns the current x-coordinate accessor.

# force.y([y])

If y is specified, sets the y-coordinate accessor to the specified number or function and returns this force. If y is not specified, returns the current y-coordinate accessor.

Performance

The WebAssembly implementation can provide decent performance improvements, especially for larger graphs. However, the exact performance gain may vary depending on the specific use case and hardware. I encourage you to benchmark this implementation against the standard D3 force implementation and see for yourself – in particular, data copying between C++ and JavaScript can be a bottleneck. Setting up shared memory and hacking the package a bit to accommodate it might be a good idea if you're looking to optimize further.

License

This project is licensed under the MIT License - see the LICENSE file for details.