Skip to content

Instantly share code, notes, and snippets.

@sxywu
Last active August 27, 2016 00:23
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 sxywu/52829610fa093e10597c44617603a14e to your computer and use it in GitHub Desktop.
Save sxywu/52829610fa093e10597c44617603a14e to your computer and use it in GitHub Desktop.
DS Aug, Code 2
license: mit
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
<style>
</style>
</head>
<body>
<canvas id='canvas' width="1024px" height="576px"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// data from olympics diving
var data = [{
"country": "China",
"athletes": ["A. Chen", "Y. Lin"],
"total": 496.98,
"difficulty": [2, 2, 3.4, 3.4, 3.8, 3.3],
"scores": [56.4, 52.20, 85.68, 88.74, 104.88, 89.10]
}];
// properties
var padding = 25;
var width = 800;
var height = 600;
var colors = {'China': [[255,0,0], [255,255,0]]};
var TWO_PI = 2 * Math.PI;
var maxRadius = 50;
var radiusScale = d3.scaleLinear().range([25, maxRadius]);
function processData(data) {
var difficulty = _.chain(data).map('difficulty').flatten().value();
debugger
var minR = _.min(difficulty);
var maxR = _.max(difficulty);
radiusScale.domain([minR, maxR]);
}
// generate the flow line, given one event for one team
function generateFlowData(team) {
var gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, maxRadius);
gradient.addColorStop(1, 'rgba(' + colors[team.country][0] + ',0.2)');
gradient.addColorStop(0, 'rgba(' + colors[team.country][1] + ',0.2)');
return {
centerX: padding,
centerY: (height - maxRadius) / 2,
color: gradient,
param: 0,
phase: (team.total / 1000) * TWO_PI, // i think phase is used for rotation
globalPhase: (team.total / 1000) * TWO_PI, // globalPhase is for yOffset
radii: _.map(team.difficulty, function(d) {
return radiusScale(d);
}),
points: _.times(team.scores.length, function() {
return generateCircleData();
}),
length: _.map(team.scores, function(score) {
return Math.round(score) * 3;
})
}
}
// generate the data for just one of the circles in the flow line
// majority of this function is taken from
// Dan Gries's tutorial http://rectangleworld.com/blog/archives/462
// in particular the function setLinePoints
function generateCircleData() {
var circle = {
first: {x: 0, y: 1}
};
var last = {x: 1, y: 1};
var minY = maxY = 1;
var point, nextPoint;
var dx, newX, newY;
// connect first point with the last
circle.first.next = last;
_.times(7, function() {
point = circle.first;
while (point.next) {
nextPoint = point.next;
dx = nextPoint.x - point.x;
newX = 0.5 * (point.x + nextPoint.x);
newY = 0.5 * (point.y + nextPoint.y);
newY += dx * (Math.random() * 2 - 1);
var newPoint = {x: newX, y: newY};
//min, max
if (newY < minY) {
minY = newY;
}
else if (newY > maxY) {
maxY = newY;
}
// insert mid-point
newPoint.next = nextPoint;
point.next = newPoint;
point = nextPoint;
}
})
// normalize to values between 0 and 1
if (maxY != minY) {
var normalizeRate = 1/(maxY - minY);
point = circle.first;
while (point != null) {
point.y = normalizeRate*(point.y - minY);
point = point.next;
}
}
return circle;
}
function drawFlowLine(flow) {
console.log(flow)
// for now just draw one circle
var theta, rad;
var point1, point2;
var radii, length;
var x0, y0;
var cosParam, yOffset;
var drawCount = 0;
var xSqueeze = 0.75;
// go through each of the points
for (i = 0; i < flow.points.length - 1; i += 1) {
radii = flow.radii[i];
length = flow.length[i];
flow.param = 0;
ctx.strokeStyle = flow.color;
_.times(length, function() {
point1 = flow.points[i].first;
point2 = flow.points[i + 1].first;
drawCount += 1;
// first set up styling
ctx.beginPath();
flow.phase += 0.0002;
flow.param += 1/length;
cosParam = 0.5 - 0.5 * Math.cos(Math.PI * flow.param);
theta = flow.phase;
rad = (point1.y + cosParam * (point2.y - point1.y)) * radii;
//move center
flow.centerX += 0.5;
yOffset = 40 * Math.sin(flow.globalPhase + drawCount/600*TWO_PI);
ctx.setTransform(xSqueeze, 0, 0, 1, flow.centerY + yOffset, flow.centerX);
x0 = xSqueeze * rad * Math.cos(theta);
y0 = rad * Math.sin(theta);
// draw the first point
ctx.lineTo(x0, y0);
while (point1.next && point2.next) {
point1 = point1.next;
point2 = point2.next;
theta = TWO_PI * (point1.x + cosParam * (point2.x - point1.x)) + flow.phase;
rad = (point1.y + cosParam * (point2.y - point1.y)) * radii;
x0 = xSqueeze * rad * Math.cos(theta);
y0 = rad * Math.sin(theta);
ctx.lineTo(x0, y0);
// console.log(x0, y0)
}
ctx.closePath();
ctx.stroke();
});
}
}
processData(data);
var flow = generateFlowData(data[0]);
drawFlowLine(flow);
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment