-
-
Save enjalot/3779865 to your computer and use it in GitHub Desktop.
An inlet to Tributary
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
{"description":"An inlet to Tributary","endpoint":"","display":"svg","public":true,"require":[],"fileconfigs":{"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"_.md":{"default":true,"vim":false,"emacs":false,"fontSize":12},"inlet.svg":{"default":true,"vim":false,"emacs":false,"fontSize":12},"config.json":{"default":true,"vim":false,"emacs":false,"fontSize":12}},"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":581,"height":538,"hide":false},"hidepanel":false} |
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
var thank = "YOU"; | |
//appearance | |
var rx = 7.5; | |
var ry = 5; | |
//number of boids | |
var n = 10; | |
var neighbor_radius = 10; | |
var mouse_radius = 200; | |
var desired_separation = 237; | |
var max_force = .2; | |
var max_speed = 3; | |
var separation_weight = 2; | |
//how much the boids want to line up | |
var alignment_weight = 6; | |
//how much the boids want to stay close together | |
var cohesion_weight = 6; | |
//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 thanks = [ | |
"Mike Bostock", | |
"Gabriel Florit", | |
"EJ Fox", | |
"Evan Bollig", | |
"Kai Chang", | |
"Michael Porath", | |
"Paul van Slembrouck", | |
"Nils Schlomann", | |
"Erik Hazzard", | |
"YOU" | |
]; | |
tributary.init = function(g) { | |
g.append("rect") | |
.attr({ | |
width: "100%", | |
height: "100%", | |
fill: "#000" | |
}) | |
//setup the nodes | |
tributary.nodes = d3.range(thanks.length).map(function(d,i) { | |
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) | |
b.thanks = thanks[i]; | |
return b; | |
}); | |
vertices = tributary.nodes.map(function(boid) { | |
return boid(tributary.nodes); | |
}); | |
//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") | |
var name = gs.append("text") | |
.text(function(d) { return d.thanks }) | |
.attr({ | |
fill: "#fff", | |
"fill-opacity": 0.5, | |
"font-size": 8 | |
}) | |
//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]; | |
}) | |
.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); | |
*/ | |
gs.select("text") | |
.attr("transform", function(d,i) { | |
var angle = 90-Math.atan2(d.velocity()[0], d.velocity()[1]) * 180/Math.PI; | |
var x = d.position()[0]; | |
var y = d.position()[1]; | |
return "translate(" + [x,y] + ")rotate(" + angle + ")translate(" + [13, 5] + ")"; | |
}) | |
gs.filter(function(d) { return d.thanks === thank }) | |
.select("text") | |
.attr({ | |
"font-size": 30, | |
"fill": "#8CBD90", | |
"fill-opacity": 1 | |
}) | |
}; | |
// 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; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment