Skip to content

Instantly share code, notes, and snippets.

@enjalot
Created February 1, 2013 04:00
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/4689087 to your computer and use it in GitHub Desktop.
Save enjalot/4689087 to your computer and use it in GitHub Desktop.
blob pointers
{"description":"blob pointers","endpoint":"","display":"svg","public":true,"require":[],"fileconfigs":{"config.json":{"default":true,"vim":false,"emacs":false,"fontSize":12},"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"_.md":{"default":true,"vim":false,"emacs":false,"fontSize":12},"leap.js":{"default":true,"vim":false,"emacs":false,"fontSize":12}},"play":false,"loop":false,"restart":true,"autoinit":true,"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":true,"emacs":false,"width":853,"height":627,"hide":true},"fullscreen":false,"thumbnail":"http://i.imgur.com/SwaSWf5.png"}
//appearance
var rx = 15;
var ry = 15;
var boid_fill = "#05A212";
var boid_fill_opacity = 0.1232;
var boid_path_fill = "#007718";
var boid_path_opacity = 0.1;
//number of boids
var n = 10;
var svg = d3.select("svg");
tributary.updated = [];
tributary.events.off("leap");
tributary.events.on("leap", function(d) {
//console.log(d);
var len = d.pointables.length;
if(len > n) len = n;
tributary.updated = d3.range(n).map(function() {return 0;})
//TODO: do this d3 selection style
for(var i = len; i--;) {
var pointer = d.pointables[i];
var pos = pointer.tipPosition;
//console.log(pos);
//cube.position.set(pos[0], pos[1], pos[2]);
tributary.pointers[i].x = 2*pos[0] + tributary.sw/2;
tributary.pointers[i].y = -pos[1] + tributary.sh/2;
tributary.updated[i] = 1;
}
//not using the t parameter
tributary.run(svg, 0);
})
var filler = d3.scale.linear()
//.domain([-max_speed, max_speed])
.domain([0, tributary.sw/2])
.interpolate(d3.interpolateLab)
.range(["#FF4646", "#00CCFF"]);
var join = {
//r: desired_separation
r: 699
}
//some defaults
var w = tributary.sw; //get screen width and height
var h = tributary.sh;
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;
});
*/
tributary.pointers = d3.range(n).map(function() {
var x = Math.random() * w, y = Math.random() * h;
var p = {x:x, y:y}
return p;
})
tributary.updated = d3.range(n).map(function() {return 0;})
/*
var vertices = tributary.nodes.map(function(boid) {
return boid(tributary.nodes);
});
*/
//render the boids as ellipses
var gs = g.selectAll("g.node")
.data(tributary.pointers)
.enter().append("g").classed("node", true);
//need a path for each potential pair
var paths = g.selectAll("path.lava")
.data(d3.range(n*n))
.enter().append("path")
.attr("class", function(d,i) {
var j = i % n;
var k = parseInt(i/n);
return "lava" + j + "_" + k;
})
.style("fill", boid_path_fill)
.style("fill-opacity", boid_path_opacity);
/*
//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;
var len = tributary.pointers.length;
var b, j, pos, npos;
g.selectAll("path")
//turn off all paths (they will be turned on iff in range)
.style("display", "none")
//restyle all paths (so we can see color changes when scrubbing, this is less efficient)
.style("fill", boid_path_fill)
.style("fill-opacity", boid_path_opacity)
for (var i = -1; ++i < len;) {
//b = tributary.nodes[i];
p = tributary.pointers[i];
//TODO: get boid neighbors with quadtree
//pos = b.position();
pos = p;
for(j=0; j < len; j++) {
if(i === j) { continue; }
//npos = tributary.nodes[j].position();
npos = tributary.pointers[j];
if(dist(pos, npos) < join.r) {//desired_separation) {
//close enough
update(g, i + "_" + j, {x: pos.x, y: pos.y, r: 26},
{x: npos.x, y: npos.y, r: 20});
}
}
}
};
function dist(a,b) {
var dx = a.x - b.x,
dy = a.y - b.y;
//if (dx > w / 2) dx = w - dx;
//if (dy > h / 2) dy = h - dy;
return Math.sqrt(dx * dx + dy * dy);
}
var update = function(g, id, a, b) {
var originDistance = a.r - b.r;
a.area = Math.PI * Math.pow(a.r, 2);
b.area = Math.PI * Math.pow(b.r, 2);
var afterCircleArea = a.area - b.area;
var distance = calculateDistance(a,b);
var distanceDiff = distance - originDistance;
if(distanceDiff < 1) { distanceDiff = 1 }
var dontdraw = false;
if(distance > 2*join.r + a.r + b.r) {
//console.log("MAY DAY")
dontdraw = true;
}
var inds = id.split("_");
if(!tributary.updated[+inds[0]] || !tributary.updated[+inds[1]])
dontdraw = true;
var angle = calculateAngle(a,b);
b.h = 0;
b.k = 0 - a.r + b.r - distanceDiff;
//console.log(b.k, join.r)
var triangleA = a.r + join.r; // Side a
var triangleB = b.r + join.r; // Side b
var triangleC = Math.abs(b.k - 0); // Side c
var triangleP = (triangleA + triangleB + triangleC) / 2; // Triangle half perimeter
var triangleArea = Math.sqrt(triangleP * (triangleP - triangleA) * (triangleP - triangleB) * (triangleP - triangleC)); // Triangle area
if (triangleC >= triangleA)
{
var triangleH = 2 * triangleArea / triangleC; // Triangle height
var triangleD = Math.sqrt(Math.pow(triangleA, 2) - Math.pow(triangleH, 2)); // Big circle bisection of triangleC
}
else
{
var triangleH = 2 * triangleArea / triangleA; // Triangle height
var triangleD = Math.sqrt(Math.pow(triangleC, 2) - Math.pow(triangleH, 2)); // Small circle bisection of triangleA
}
a.tan = triangleH / triangleD;
a.angle = Math.atan(a.tan);
a.sin = Math.sin(a.angle);
a.intersectX = a.sin * a.r;
a.cos = Math.cos(a.angle);
a.intersectY = a.cos * a.r;
join.x = 0 + a.sin * (a.r + join.r);
join.y = 0 - a.cos * (a.r + join.r);
var coord1 = {
x: -a.intersectX,
y: -a.intersectY
};
var coord2 = {
x: a.intersectX,
y: -a.intersectY
}
b.tan = (b.k - join.y) / (b.h - join.x);
b.angle = Math.atan(b.tan);
b.intersectX = join.x - Math.cos(b.angle) * (join.r);
b.intersectY = join.y - Math.sin(b.angle) * (join.r);
var lavaPathD = "M " + coord1.x + " " + coord1.y + " A " + a.r + " " + a.r + " 0 1 0 " + coord2.x + " " + coord2.y;
if (join.x - join.r <= 0 && b.k < join.y)
{
var crossOverY = circleYFromX(join, 0);
lavaPathD += "A " + join.r + " " + join.r + " 0 0 1 0 " + (join.y + crossOverY);
lavaPathD += "m 0 -" + (crossOverY * 2);
}
lavaPathD += "A " + join.r + " " + join.r + " 0 0 1 " + b.intersectX + " " + b.intersectY;
var largeArcFlag = 1;
if (join.y < b.k)
{
largeArcFlag = 0;
}
lavaPathD += "a " + b.r + " " + b.r + " 0 " + largeArcFlag + " 0 " + (b.intersectX * -2) + " 0";
if (join.x - join.r <= 0 && b.k < join.y)
{
lavaPathD += "A " + join.r + " " + join.r + " 0 0 1 0 " + (join.y - crossOverY);
lavaPathD += "m 0 " + (crossOverY * 2);
}
lavaPathD += "A " + join.r + " " + join.r + " 0 0 1 " + coord1.x + " " + coord1.y;
lavaPathD += "A " + join.r + " " + join.r + " 0 0 1 " + coord1.x + " " + coord1.y;
var lavaPath = g.select("path.lava" + id)
if(!dontdraw) {
lavaPath
.attr("transform", function() {
var translate = "translate(" + [a.x, a.y] + ")";
var rotate = "rotate(" + [angle, 0, 0] + ")";
return translate + rotate;
})
.attr("d", lavaPathD)
.style("display", "")
} else {
lavaPath.style("display", "none");
}
}
function calculateDistance(origin, point) {
var xx = point.x - origin.x;
var yy = point.y - origin.y;
return Math.sqrt(xx*xx + yy*yy)
}
function calculateAngle(origin, point)
{
var tan = (point.y - origin.y) / (point.x - origin.x);
var angle = Math.atan(tan) / Math.PI * 180 + 90;
if (point.x < origin.x)
{
angle += 180;
}
return angle;
}
function circleYFromX (circle, x)
{
return Math.sqrt(Math.pow(circle.r, 2) - Math.pow(x - circle.x, 2));
};
if(!tributary.leap) tributary.leap = {};
// Support both the WebSocket and MozWebSocket objects
if ((typeof(WebSocket) == 'undefined') &&
(typeof(MozWebSocket) != 'undefined')) {
WebSocket = MozWebSocket;
}
// Create the socket with event handlers
tributary.leap.init = function() {
if(tributary.ws) {
tributary.ws.close();
}
console.log("Starting");
//Create and open the socket
tributary.ws = new WebSocket("ws://localhost:6437/");
ws = tributary.ws;
// On successful connection
ws.onopen = function(event) {
//document.getElementById("connection").innerHTML = "WebSocket connection open!";
//$("#textoutput").html("Open...")
console.log("Open");
};
// On message received
ws.onmessage = function(event) {
tributary.events.trigger("leap", JSON.parse(event.data));
};
// On socket close
ws.onclose = function(event) {
ws = null;
}
//On socket error
ws.onerror = function(event) {
console.error("Received error");
};
}
tributary.leap.init();
tributary.events.on("restart", function() {
tributary.leap.init();
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment