Created
October 1, 2016 19:46
-
-
Save KristinHenry/74da47847eac56dae0dc2771c988dcd1 to your computer and use it in GitHub Desktop.
particles
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<meta charset="utf-8"> | |
<head> | |
<title>Testing interactive svg + canvas plot</title> | |
<style> | |
body { margin: 0; } | |
svg { | |
position: absolute; | |
top: 0; | |
left: 0; | |
} | |
rect { fill: transparent; } | |
</style> | |
<script src='http://d3js.org/d3.v3.min.js'></script> | |
</head> | |
<body style="background-color: #246980"> | |
<script type='text/javascript'> | |
var filler = '#eee'; | |
var prp1 = '#7b3294'; | |
var prp2 = '#c2a5cf'; | |
var grn1 = '#a6dba0'; | |
var grn2 = '#008837'; | |
var colors = [filler, prp1, prp2, grn1, grn2]; | |
function randomRange(min, max){ | |
return Math.floor((Math.random() * (max - min + 1)) + min); | |
} | |
function getDir(){ | |
if(randomRange(0,1) == 1){ return 1;} | |
return -1; | |
} | |
var map = {}; | |
map.width = 1050; | |
map.height = 1500; | |
var xmin = 20; | |
var xmax = map.width-20; | |
var ymin = 20; | |
var ymax = map.height-20; | |
var s = 1; | |
var maxNodesCan = map.width/2; //200; | |
var maxNodesSVG = maxNodesCan/10; //20; | |
map.canvas = | |
d3.select('body') | |
.append('canvas') | |
.attr('width', map.width) | |
.attr('height', map.height) | |
.node().getContext('2d'); | |
map.svg = | |
d3.select('body') | |
.append('svg') | |
.attr('width', map.width) | |
.attr('height', map.height) | |
.append('g'); | |
map.svg.append('rect') | |
.attr('class', 'overlay') | |
.attr('width', map.width) | |
.attr('height', map.height); | |
/* Initialize particles */ | |
// background molecules | |
var rmin = 10; | |
map.canvas.nodes = | |
d3.range(maxNodesCan).map(function(d, i) { | |
return { | |
x: randomRange(xmin,xmax), | |
y: randomRange(ymin,ymax), | |
r: randomRange(3,20) + rmin, | |
t: 0, | |
dx: getDir(), | |
dy: getDir() | |
}; | |
}); | |
// interactive molecules | |
map.svg.nodes = | |
d3.range(maxNodesSVG).map(function(d, i) { | |
var t = randomRange(1,colors.length-1); | |
return { | |
x: randomRange(xmin,xmax), | |
y: randomRange(ymin,ymax), | |
r: t*5 + 2 + rmin, | |
t: t, | |
dx: getDir(), | |
dy: getDir() | |
}; | |
}); | |
map.nodes = map.svg.nodes.concat( map.canvas.nodes ); | |
var root = map.nodes[0]; | |
root.r = 0; | |
root.fixed = true; | |
map.canvas.draw = | |
function() { | |
map.canvas.clearRect(0, 0, map.width, map.height); | |
map.canvas.beginPath(); | |
var i = -1, cx, cy; | |
while (++i < map.canvas.nodes.length) { | |
d = map.canvas.nodes[i]; | |
map.canvas.fillStyle = colors[d.t]; | |
cx = d.x; | |
cy = d.y; | |
map.canvas.moveTo(cx, cy); | |
map.canvas.arc(cx, cy, d.r, 0, 2 * Math.PI); | |
} | |
map.canvas.fill(); | |
}; | |
map.svg.draw = | |
function() { | |
circle = map.svg.selectAll('circle') | |
.data(map.svg.nodes).enter() | |
.append('circle') | |
.attr('r', function(d) { return d.r; }) | |
.attr('fill', function(d){return colors[d.t];}) | |
.attr('transform', map.svg.transform); | |
}; | |
// initial draw of molecules | |
map.canvas.draw(); | |
map.svg.draw(); | |
map.redraw = function() { | |
map.canvas.draw(); | |
circle.attr('transform', map.svg.transform); | |
}; | |
map.svg.transform = | |
function(d) { | |
return 'translate(' + d.x + ',' + d.y + ')'; | |
}; | |
map.redraw(); | |
map.updatePositions = function(){ | |
var d; | |
var q = d3.geom.quadtree(map.nodes) | |
for(var i=0; i<map.canvas.nodes.length; i++){ | |
d = map.canvas.nodes[i]; | |
q.visit(collide(d)); | |
testBounds(d); | |
} | |
for(var i=0; i<map.svg.nodes.length; i++){ | |
d = map.svg.nodes[i]; | |
q.visit(collide(d)); | |
testBounds(d); | |
} | |
} | |
var timer_ret_val = false; | |
// Keeps a record of the elapsed time since the timer began. | |
var timer_elapsed = 0; | |
// Kick off the timer, and the action begins: | |
d3.timer(tickFxn); | |
function tickFxn(_elapsed) { | |
timer_elapsed = _elapsed; | |
map.updatePositions(); | |
map.redraw(); | |
var q = d3.geom.quadtree(map.nodes); | |
return timer_ret_val; | |
} | |
//--------------------------------------------------- | |
function testBounds(mol){ | |
if(mol.x <= (xmin+mol.r) ){ | |
mol.dx = s; | |
} else if(mol.x >= (xmax-mol.r) ){ | |
mol.dx = -s; | |
} | |
if(mol.y < (ymin+2*mol.r)){ | |
mol.dy = s; | |
} else if(mol.y >= (ymax-2*mol.r)){ | |
mol.dy = -s; | |
} | |
mol.x += mol.dx; | |
mol.y += mol.dy; | |
} | |
function collide(node) { | |
var b = .8; | |
var r = node.r + 1, | |
nx1 = node.x - r, | |
nx2 = node.x + r, | |
ny1 = node.y - r, | |
ny2 = node.y + r; | |
//then check nearby particles in quadtree | |
return function(quad, x1, y1, x2, y2) { | |
if (quad.point && (quad.point !== node)) { | |
var x = node.x - quad.point.x, | |
y = node.y - quad.point.y, | |
l = Math.sqrt(x * x + y * y), | |
r = node.r + quad.point.r + 3; | |
if (l < r) { | |
l = (l - r) / l * b; | |
node.x -= x *= l; | |
node.y -= y *= l; | |
quad.point.x += x; | |
quad.point.y += y; | |
} | |
} | |
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; | |
}; | |
} | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment