Skip to content

Instantly share code, notes, and snippets.

@monfera
Last active May 18, 2017 08:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save monfera/ffa2a4fdacbb7c7b1c1ad34337fb8086 to your computer and use it in GitHub Desktop.
Save monfera/ffa2a4fdacbb7c7b1c1ad34337fb8086 to your computer and use it in GitHub Desktop.
Collision test with D3 4.0
license: gpl-3.0

Following up on a chat with Toph Tucker, this example shows that the collide force of d3.forceSimulation takes into account the particle "masses" (proxy: square radii of the colliding particles). It conserves momentum but the collision isn't elastic. It appears to be a perfectly inelastic collision.

Toph implemented elastic collisions with his force and the D3 force.

It doesn't matter much for the visual output of the Brownian motion example from which it's extracted, but for a pool / snooker simulator, just roll your own forces, similar to how the Brownian example makes particles bounce back from walls.

The main benefit of the built-in collide force is that it uses the quadtree for efficiency, but a cue game simulator would need to handle few "particles" only.

Built with blockbuilder.org

forked from monfera's block: Brownian motion with D3 4.0 velocity Verlet physics

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body style="margin: 0; overflow: hidden">
<canvas id="realtime" width="1920" height="1000"
style="position: absolute; left: 0; top: 0; width: 960px; height: 500px" >
</canvas>
<script>
var width = 1920
var height = 1000
var gasMoleculeCount = 1
var dustCount = 4
var gasMoleculeRadius = 12
var dustRadius = 36
var realtimeContext = document.querySelector('#realtime')
.getContext('2d', {alpha: true})
realtimeContext.transform(1, 0, 0, 1, width / 2, height / 2)
var radius = function(node) { return node.radius }
var gasMolecules = d3.range(gasMoleculeCount).map(function() {
return {
radius: gasMoleculeRadius,
x: -width / 3 + dustRadius - gasMoleculeRadius, // collide at same time
y: height / 6,
vx: 2,
vy: 0
}
})
var dustParticles = d3.range(dustCount).map(function(d, i) {
return {
radius: dustRadius,
x: width / 3 * [-1, 0, 0, -1][i],
y: height / 6 * [-1, 0, 1, 0][i],
vx: [2, 0, 0, 2][i],
vy: 0
}})
var nodes = dustParticles.concat(gasMolecules)
d3.forceSimulation(nodes)
.alphaDecay(0)
.velocityDecay(0)
.force("collide", d3.forceCollide()
.radius(radius).strength(1).iterations(10))
.on("tick", render)
realtimeContext.lineWidth = 1
realtimeContext.fillStyle = "red"
function render() {
var i
var r
var particle
realtimeContext.clearRect(-width / 2, -height / 2, width, height)
realtimeContext.beginPath()
realtimeContext.moveTo(-dustRadius, -height / 3)
realtimeContext.lineTo(-dustRadius, height / 3)
realtimeContext.stroke()
realtimeContext.beginPath()
for(i = 0; i < gasMoleculeCount; i++) {
particle = gasMolecules[i]
r = particle.radius
realtimeContext.moveTo(particle.x + r, particle.y)
realtimeContext.arc(particle.x, particle.y, r, 0, 2 * Math.PI)
}
realtimeContext.stroke()
realtimeContext.beginPath()
for(i = 0; i < dustCount; i++) {
particle = dustParticles[i]
r = particle.radius
realtimeContext.moveTo(particle.x + r, particle.y)
realtimeContext.arc(particle.x, particle.y, r, 0, 2 * Math.PI)
}
realtimeContext.fill()
realtimeContext.stroke()
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment