Use the Force

Simulating the physical world

Vasco Asturiano

September 22-23, 2017 | San Francisco

d3.forceSimulation()

• D3 layout: pure JS
• Manipulate coordinates (x, y) on a data model
• Agnostic on how objects are rendered
• Iterative engine running Verlet velocity integration
• Force "plugins" influence node velocities (vx, vy)

Lifecycle of a force simulation

F1

\Delta v
$\Delta v$

F2

\Delta v
$\Delta v$

...

Fn

Forces

Calc ($$x$$,$$y$$) coords

($$\Delta x = vx$$)

alpha < min

stop

y

alpha decay

n

Init nodes

$$x$$,$$y$$

$$vx$$,$$vy$$

tick

(raf)

(still phyllotaxis)

onTick()

Lifecycle of a force simulation

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

alpha += (alphaTarget - alpha) * alphaDecay;

forces.each(function(force) {
force(alpha);
});

for (i = 0; i < n; ++i) {
node = nodes[i];
if (node.fx == null) node.x += node.vx *= velocityDecay;
else node.x = node.fx, node.vx = 0;
if (node.fy == null) node.y += node.vy *= velocityDecay;
else node.y = node.fy, node.vy = 0;
}
}


F1

\Delta v
$\Delta v$

F2

\Delta v
$\Delta v$

...

Fn

Forces

Calc ($$x$$,$$y$$) coords

($$\Delta x = vx$$)

alpha < min

stop

y

alpha decay

n

Init nodes

$$x$$,$$y$$

$$vx$$,$$vy$$

tick

(raf)

(still phyllotaxis)

onTick()

Inside a force

function myForce(alpha) {
const k = alpha * 1e-4;
nodes.forEach(node => {
node.vx -= node.x * k;
node.vy -= node.y * k;
});
}
function myForce(alpha) {
const k = alpha * 1e-4;
nodes.forEach(node => {
node.vx -= node.x * k;
node.vy -= node.y * k;
});
}

const myNodes = [ /* nodes data */ ];

d3.forceSimulation()
.nodes(myNodes)
.force('toZero', myForce)
.on('tick', () => {
// Draw nodes
const dot = domNode
.selectAll('circle')
.data(myNodes);

dot.merge(
dot.enter
.append('circle')
.attr('r', 5)
)
.attr('cx', d => d.x)
.attr('cy', d => d.y);
});

Graph Drawing

d3.forceSimulation()
.force("charge", d3.forceManyBody())  // strength: -30
.force("center", d3.forceCenter(width/2, height/2));

d3-force

• d3.forceManyBody()
• d3.forceX([x])
• d3.forceY([y])
• d3.forceCenter([x, y])

positioning

charge

collision

D3 force registry

Compilation of all-things D3-force

a

a

a

aaa

a

a

a

a

a

a

aaa

a

a

a

a

a

a

aaa

a

a

a

a

a

a

aaa

a

a

a

a

a

a

aaa

a

a

a

a

a

a

aaa

a

a

a

a

a

a

aaa

a

a

a

Solar System

a

a

a

aaa

a

a

a

a

a

a

aaa

a

a

a

d3-force-pod - DIY Simulation

d3ForcePod()(document.body)
.genNodes()
)
.x(width/2)
.strength(0.002)
);

a

a

aaa

a

a

a

The lost dimension

• Extended version to support 3D (& 1D)
• Forces manipulate 'x,y,z' according to internal logic
• Drop-in replacement
d3.forceSimulation()
.numDimensions(/* 1, 2 or 3*/)

a

a

a

aaa

a

a

a

a

a

a

aaa

a

a

a

a

a

a

aaa

a

a

a

Thanks!

and may the force be with you...