Skip to content

Instantly share code, notes, and snippets.

@jdherg
Created June 25, 2015 15:16
Show Gist options
  • Save jdherg/6a8632b2f5da6f85a97a to your computer and use it in GitHub Desktop.
Save jdherg/6a8632b2f5da6f85a97a to your computer and use it in GitHub Desktop.
Contrapositive 20150624 Progress
var tau = 2*Math.PI;
var Hands4 = function(center, radius, dance) {
this.center = center;
this.radius = radius;
this.dancers = [];
this.dance = dance;
};
Hands4.prototype.getX = function(count, pos) {
return this.center[0] + this.dance.getX(count, pos);
};
Hands4.prototype.getY = function(count, pos) {
return this.center[1] + this.dance.getY(count, pos);
};
var Dance = function(repeatFlag) {
this.repeat = repeatFlag;
this.moves = [];
this.defaultMove = new Stand(0,75);
this.danceLength = 0;
};
Dance.prototype.getX = function(count, pos) {
var moveInfo = this.currentMove(count);
return moveInfo[0].getX(moveInfo[1], pos);
};
Dance.prototype.getY = function(count, pos) {
var moveInfo = this.currentMove(count);
return moveInfo[0].getY(moveInfo[1], pos);
};
Dance.prototype.currentMove = function(count) {
if(this.repeat) {
count %= this.danceLength;
}
for(var i = 0; i < this.moves.length; i++) {
if(count > this.moves[i].duration) {
count -= this.moves[i].duration;
} else {
return [this.moves[i], count];
}
}
return [this.defaultMove, 0];
};
Dance.prototype.addMove = function(move) {
this.moves.push(move);
this.danceLength += move.duration;
};
var Move = function() {
};
Move.prototype.getX = function(count, pos) {
throw "not implemented";
};
Move.prototype.getY = function(count, pos) {
throw "not implemented";
};
Move.prototype.posTransform = function(pos) {
return pos;
};
var Circle = function(duration, direction, radius) {
this.radius = radius;
this.direction = direction;
this.speed = 1/8;
this.duration = duration;
};
Circle.prototype.__proto__ = Move.prototype;
Circle.prototype.getX = function(count, pos) {
return Math.cos(tau/8 + tau/4 * pos + this.direction * this.speed * count * tau) * this.radius
};
Circle.prototype.getY = function(count, pos) {
return Math.sin(tau/8 + tau/4 * pos + this.direction * this.speed * count * tau) * this.radius
};
Circle.prototype.posTransform = function(pos) {
var posDelta = this.direction * this.duration * this.speed * 4;
pos = (pos + posDelta) % 4;
pos += pos < 0 ? 4 :0;
return pos;
};
var Stand = function(duration, radius) {
this.duration = duration;
this.radius = radius;
};
Stand.prototype.__proto__ = Move.prototype;
Stand.prototype.getX = function(count, pos) {
return Math.cos(tau/8 + tau/4 * pos) * this.radius;
};
Stand.prototype.getY = function(count, pos) {
return Math.sin(tau/8 + tau/4 * pos) * this.radius;
};
var Allemande = function(duration, direction, radius) {
this.duration = duration;
this.direction = direction;
this.radius = radius;
this.speed = 1/4;
}
Allemande.prototype.__proto__ = Move.prototype;
Allemande.prototype.getX = function(count, pos) {
var startingPosition = Math.cos(tau/8 + tau/4 * pos) * this.radius;
if(count === 0) {
return startingPosition;
}
var cx = (this.getX(0,pos) + this.getX(0,pos^1))/2;
var cy = (this.getY(0,pos) + this.getY(0,pos^1))/2;
var polar = cartesianToPolar([cx,cy],[this.getX(0,pos),this.getY(0,pos)]);
var theta_delta = tau * this.speed * count * this.direction;
var theta_end = polar[1] + theta_delta;
return Math.cos(theta_end) * polar[0] + cx;
}
Allemande.prototype.getY = function(count, pos) {
var startingPosition = Math.sin(tau/8 + tau/4 * pos) * this.radius;
if(count === 0) {
return startingPosition;
}
var cx = (this.getX(0,pos) + this.getX(0,pos^1))/2;
var cy = (this.getY(0,pos) + this.getY(0,pos^1))/2;
var polar = cartesianToPolar([cx,cy],[this.getX(0,pos),this.getY(0,pos)]);
var theta_delta = tau * this.speed * count * this.direction;
var theta_end = polar[1] + theta_delta;
return Math.sin(theta_end) * polar[0] + cy;
}
function cartesianToPolar(center, point){
var dx = point[0] - center[0],
dy = point[1] - center[1],
d2 = dx * dx + dy * dy,
dist = Math.sqrt(d2),
sin = dy / dist,
cos = dx / dist,
asin = Math.asin(sin),
acos = Math.acos(cos);
var theta = 0;
if(sin < 0) {
theta = -1 * acos;
} else {
if(cos < 0) {
theta = 2 * Math.PI - acos;
} else {
theta = acos;
}
}
return [dist, theta];
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
border: solid 1px #000;
}
circle {
r: 25;
stroke: #000;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js" charset="utf-8"></script>
<script src="dance_model.js" charset="utf-8"></script>
<body>
<script>
var width = 960,
height = 500,
tau = 2*Math.PI;
var counts_per_second = 4,
taus_per_count = 1/8;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var count_display = d3.select("body").append("p")
.text(0);
var dance = new Dance(true);
dance.addMove(new Allemande(4, 1, 75));
dance.addMove(new Circle(6, 1, 75));
dance.addMove(new Circle(10, -1, 75));
var h4s = [];
h4s.push(new Hands4([240,250], 75, dance));
h4s.push(new Hands4([720,250], 75, dance));
var dancers = [];
for (var i = 0; i < h4s.length*4; i++) {
dancers.push(
{id: i,
pos: i % 4,
theta: 0});
h4s[Math.floor(i/4)].dancers.push(dancers[i]);
dancers[i].h4 = h4s[Math.floor(i/4)];
}
var dots = svg.selectAll("circle")
.data(dancers)
.enter().append("circle")
.attr("fill", function(d) {
switch(d.id % 4) {
case 0:
return "red";
case 1:
return "blue";
case 2:
return "green";
case 3:
return "yellow";
}
});
var previousMove = dance.currentMove(0)[0];
draw(0);
d3.timer(animate);
function draw(count) {
dots.attr("cx", function(d) {
return d.h4.getX(count, d.pos);
})
.attr("cy", function(d) {
return d.h4.getY(count, d.pos);
});
count_display.text(Math.floor(count) % dance.danceLength);
}
function animate(elapsed) {
var count = elapsed/1000 * counts_per_second;
var currentMove = dance.currentMove(count)[0];
if(previousMove !== currentMove) {
dancers.forEach(function(dancer){
dancer.pos = previousMove.posTransform(dancer.pos);
});
previousMove = currentMove;
}
draw(count);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment