Skip to content

Instantly share code, notes, and snippets.

@enjalot
Created September 7, 2013 06:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save enjalot/6473246 to your computer and use it in GitHub Desktop.
Save enjalot/6473246 to your computer and use it in GitHub Desktop.
blood in the water
{"description":"blood in the water","endpoint":"","display":"svg","public":true,"require":[],"tab":"edit","display_percent":0.7,"play":true,"loop":false,"restart":true,"autoinit":false,"pause":true,"loop_type":"period","bv":false,"nclones":15,"clone_opacity":0.4,"duration":3000,"ease":"linear","dt":0.01,"editor_editor":{"coffee":false,"vim":false,"emacs":false,"width":600,"height":300,"hide":false},"fileconfigs":{"_.md":{"default":true,"vim":false,"emacs":false,"fontSize":12},"config.json":{"default":true,"vim":false,"emacs":false,"fontSize":12},"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"inlet.svg":{"default":true,"vim":false,"emacs":false,"fontSize":12}},"fullscreen":false,"thumbnail":"http://i.imgur.com/KrF9Hc2.gif"}
//appearance
var rx = 7.5;
var ry = 5;
//number of boids
var n = 60;
var max_domain = 5000;
var filler = d3.scale.linear()
//.domain([-max_speed, max_speed])
.domain([0, tributary.sw/2])
.interpolate(d3.interpolateLab)
.range(["#FF4646", "#00CCFF"]);
var neighbor_radius = 50;
var mouse_radius = 200;
var desired_separation = 68;
var max_force = -.2;
var max_speed = 2;
var separation_weight = 20;
//how much the boids want to line up
var alignment_weight = 1;
//how much the boids want to stay close together
var cohesion_weight = 11;
//mostly changes how much the mouse affects the boids
var gravity_multiplier = 100;
//some defaults
tributary.trace = true; //turns on console trace for errors
var w = tributary.sw; //get screen width and height
var h = tributary.sh;
//initialize the "global" variable that keeps track of mouse
var mouse = [null, null];
var last_mouse = [0,0];
tributary.init = function(g) {
//setup the nodes
tributary.nodes = d3.range(n).map(function() {
var x = Math.random() * w, y = Math.random() * h;
var b = boid()
.position([Math.random() * w, Math.random() * h])
.velocity([Math.random() * 2 - 1, Math.random() * 2 - 1])
.gravityCenter(mouse)
return b;
});
var vertices = tributary.nodes.map(function(boid) {
return boid(tributary.nodes);
});
//render the voronoi cells
g.selectAll("path")
.data(d3.geom.voronoi(vertices))
.enter().append("path")
.attr("d", function(d) { return "M" + d.join("L") + "Z"; });
/*
//render the boids as ellipses
var gs = g.selectAll("g.node")
.data(tributary.nodes)
.enter().append("g").classed("node", true);
var head = gs.append("svg:ellipse")
*/
//mouse interaction stuff
function nullGravity() {
mouse[0] = mouse[1] = null;
}
d3.select("svg").on("mousemove", function() {
var m = d3.mouse(this);
mouse[0] = m[0];
mouse[1] = m[1];
last_mouse[0] = m[0];
last_mouse[1] = m[1];
})
.on("mouseout", nullGravity);
};
//update the simulation while the play button is running.
tributary.run = function(g,t) {
var len = tributary.nodes.length;
for (var i = -1; ++i < len;) {
var b = tributary.nodes[i];
b.separationWeight(separation_weight)
.alignmentWeight(alignment_weight)
.cohesionWeight(cohesion_weight)
.neighborRadius(neighbor_radius)
.desiredSeparation(desired_separation)
.mouseRadius(mouse_radius)
.gravityMultiplier(gravity_multiplier)
.maxForce(max_force)
.maxSpeed(max_speed)
//perform update with "neighbors"
b(tributary.nodes);
}
var gs = g.selectAll("g.node")
var head = gs.select("ellipse")
head.attr("transform", function(d,i) {
var angle = 90-Math.atan2(d.velocity()[0], d.velocity()[1]) * 180/Math.PI;
return "translate(" + d.position() + ")rotate(" + angle + ")";
})
.attr("rx", rx)
.attr("ry", ry);
var vertices = tributary.nodes.map(function(boid) {
return boid(tributary.nodes);
});
g.selectAll("path")
.data(d3.geom.voronoi(vertices))
.attr("d", function(d) { return "M" + d.join("L") + "Z"; })
.style("fill", function(d,i) {
var node = tributary.nodes[i];
//var area = d3.geom.polygon(d).area();
//var v = node.velocity();
var p = node.position();
var m = last_mouse;
var dist = Math.sqrt((m[0] - p[0])*(m[0] - p[0]) + (m[1] - p[1])*(m[1] - p[1]));
//console.log(dist);
//var mag = Math.sqrt(v[0]*v[0] + v[1]*v[1]);
//var mag = Math.sqrt(p[0]*p[0] + p[1]*p[1]);
//console.log(mag);
return filler(dist);
//return fill(d3.min([area,max_domain]));
})
.style("stroke", function(d,i) {
var node = tributary.nodes[i];
//var area = d3.geom.polygon(d).area();
//var v = node.velocity();
var p = node.position();
var m = last_mouse;
var dist = Math.sqrt((m[0] - p[0])*(m[0] - p[0]) + (m[1] - p[1])*(m[1] - p[1]));
//console.log(dist);
//var mag = Math.sqrt(v[0]*v[0] + v[1]*v[1]);
//var mag = Math.sqrt(p[0]*p[0] + p[1]*p[1]);
//console.log(mag);
return filler(dist);
//return fill(d3.min([area,max_domain]));
});
};
// Boid flocking based on http://harry.me/2011/02/17/neat-algorithms---flocking
var boid = (function() {
function boid() {
var position = [0, 0],
velocity = [0, 0],
gravityCenter = null,
gravityMultiplier = 1,
neighborRadius = 50,
mouseRadius = 50,
maxForce = .1,
maxSpeed = 1,
separationWeight = 2,
alignmentWeight = 1,
cohesionWeight = 1,
desiredSeparation = 10;
function boid(neighbors) {
var accel = flock(neighbors);
d3_ai_boidWrap(position);
velocity[0] += accel[0];
velocity[1] += accel[1];
if (gravityCenter) {
//var g = d3_ai_boidGravity(gravityCenter, position, neighborRadius);
var g = d3_ai_boidGravity(gravityCenter, position, mouseRadius)
velocity[0] += g[0] * gravityMultiplier;
velocity[1] += g[1] * gravityMultiplier;;
}
d3_ai_boidLimit(velocity, maxSpeed);
position[0] += velocity[0];
position[1] += velocity[1];
return position;
}
function flock(neighbors) {
var separation = [0, 0],
alignment = [0, 0],
cohesion = [0, 0],
separationCount = 0,
alignmentCount = 0,
cohesionCount = 0,
i = -1,
l = neighbors.length;
while (++i < l) {
var n = neighbors[i];
if (n === this) continue;
var npos = n.position(),
d = d3_ai_boidDistance(position, npos);
if (d > 0) {
if (d < desiredSeparation) {
var tmp = d3_ai_boidNormalize(d3_ai_boidSubtract(position.slice(), npos));
separation[0] += tmp[0] / d;
separation[1] += tmp[1] / d;
separationCount++;
}
if (d < neighborRadius) {
var nvel = n.velocity();
alignment[0] += nvel[0];
alignment[1] += nvel[1];
alignmentCount++;
cohesion[0] += npos[0];
cohesion[1] += npos[1];
cohesionCount++;
}
}
}
if (separationCount > 0) {
separation[0] /= separationCount;
separation[1] /= separationCount;
}
if (alignmentCount > 0) {
alignment[0] /= alignmentCount;
alignment[1] /= alignmentCount;
}
d3_ai_boidLimit(alignment, maxForce);
if (cohesionCount > 0) {
cohesion[0] /= cohesionCount;
cohesion[1] /= cohesionCount;
} else {
cohesion = position.slice();
}
cohesion = steerTo(cohesion);
return [
separation[0] * separationWeight +
alignment[0] * alignmentWeight +
cohesion[0] * cohesionWeight,
separation[1] * separationWeight +
alignment[1] * alignmentWeight +
cohesion[1] * cohesionWeight
];
}
function steerTo(target) {
var desired = d3_ai_boidSubtract(target, position),
d = d3_ai_boidMagnitude(desired);
if (d > 0) {
d3_ai_boidNormalize(desired);
// Two options for desired vector magnitude (1 -- based on distance, 2 -- maxspeed)
var mul = maxSpeed * (d < 100 ? d / 100 : 1);
desired[0] *= mul;
desired[1] *= mul;
// Steering = Desired minus Velocity
var steer = d3_ai_boidSubtract(desired, velocity);
d3_ai_boidLimit(steer, maxForce) // Limit to maximum steering force
} else {
steer = [0, 0];
}
return steer;
}
boid.position = function(x) {
if (!arguments.length) return position;
position = x;
return boid;
}
boid.velocity = function(x) {
if (!arguments.length) return velocity;
velocity = x;
return boid;
}
boid.gravityCenter = function(x) {
if (!arguments.length) return gravityCenter;
gravityCenter = x;
return boid;
}
boid.gravityMultiplier = function(x) {
if (!arguments.length) return gravityMultiplier;
gravityMultiplier = x;
return boid;
}
boid.neighborRadius = function(x) {
if (!arguments.length) return neighborRadius;
neighborRadius = x;
return boid;
}
boid.mouseRadius = function(x) {
if (!arguments.length) return mouseRadius;
mouseRadius = x;
return boid;
}
boid.maxForce = function(x) {
if (!arguments.length) return maxForce;
maxForce = x;
return boid;
}
boid.maxSpeed = function(x) {
if (!arguments.length) return maxSpeed;
maxSpeed = x;
return boid;
}
boid.separationWeight = function(x) {
if (!arguments.length) return separationWeight;
separationWeight = x;
return boid;
}
boid.alignmentWeight = function(x) {
if (!arguments.length) return alignmentWeight;
alignmentWeight = x;
return boid;
}
boid.cohesionWeight = function(x) {
if (!arguments.length) return cohesionWeight;
cohesionWeight = x;
return boid;
}
boid.desiredSeparation = function(x) {
if (!arguments.length) return desiredSeparation;
desiredSeparation = x;
return boid;
}
return boid;
}
function d3_ai_boidNormalize(a) {
var m = d3_ai_boidMagnitude(a);
if (m > 0) {
a[0] /= m;
a[1] /= m;
}
return a;
}
function d3_ai_boidWrap(position) {
if (position[0] > w) position[0] = 0;
else if (position[0] < 0) position[0] = w;
if (position[1] > h) position[1] = 0;
else if (position[1] < 0) position[1] = h;
}
function d3_ai_boidGravity(center, position, neighborRadius) {
if (center[0] != null) {
var m = d3_ai_boidSubtract(center.slice(), position),
d = d3_ai_boidMagnitude(m) - 10;
if (d > 0 && d < neighborRadius * 5) {
d3_ai_boidNormalize(m);
m[0] /= d;
m[1] /= d;
return m;
}
}
return [0, 0];
}
function d3_ai_boidDistance(a, b) {
var dx = a[0] - b[0],
dy = a[1] - b[1];
if (dx > w / 2) dx = w - dx;
if (dy > h / 2) dy = h - dy;
return Math.sqrt(dx * dx + dy * dy);
}
function d3_ai_boidSubtract(a, b) {
a[0] -= b[0];
a[1] -= b[1];
return a;
}
function d3_ai_boidMagnitude(v) {
return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
}
function d3_ai_boidLimit(a, max) {
if (d3_ai_boidMagnitude(a) > max) {
d3_ai_boidNormalize(a);
a[0] *= max;
a[1] *= max;
}
return a;
}
return boid;
})();
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment