Use the Force

Simulating the physical world

Vasco Asturiano

github.com/vasturiano       |        bl.ocks.org/vasturiano       |        twitter.com/vastur

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)
  • Intensity cooldown via "alphaDecay"

Lifecycle of a force simulation

F1

\Delta v
Δv\Delta v

F2

\Delta v
Δ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
Δv\Delta v

F2

\Delta v
Δ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("link", d3.forceLink(links))   // distance: 30
    .force("charge", d3.forceManyBody())  // strength: -30
    .force("center", d3.forceCenter(width/2, height/2));

d3-force

  • d3.forceManyBody()
  • d3.forceLink([links])
  • d3.forceX([x])
  • d3.forceY([y])
  • d3.forceCenter([x, y])
  • d3.forceRadial(radius, [x], [y]) *
  • d3.forceCollide([radius])

positioning

charge

linked

collision

Positioning Forces

Charge forces

 

Linked forces

  • d3.forceLink (core)
    • Spring-like (linear) symmetrical link

 

Collision forces

D3 force registry

Compilation of all-things D3-force

Entropy

a

a

a

aaa

a

a

a

Newton's Cradle

a

a

a

aaa

a

a

a

Accretion

a

a

a

aaa

a

a

a

Plasma

a

a

a

aaa

a

a

a

Orbital Trajectory

a

a

a

aaa

a

a

a

Hierarchical Orbits (forceLink)

a

a

a

aaa

a

a

a

Hierarchical Orbits (forceMagnetic)

a

a

a

aaa

a

a

a

Solar System

a

a

a

aaa

a

a

a

Quad Pong

a

a

a

aaa

a

a

a

d3-force-pod - DIY Simulation

d3ForcePod()(document.body)
    .genNodes()
    .addForce(d3.forceBounce()
        .radius(d => d.r)
    )
    .addForce(d3.forceX()
        .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*/)

d3-force-3d

a

a

a

aaa

a

a

a

Internet Topology

a

a

a

aaa

a

a

a

Fabric Data

a

a

a

aaa

a

a

a

Thanks!

and may the force be with you...

Vasco Asturiano

github.com/vasturiano       |        bl.ocks.org/vasturiano       |        twitter.com/vastur

<vastur@gmail.com>

Use the Force - d3.unconf 2017

By Vasco Asturiano