Built with blockbuilder.org
forked from sxywu's block: DS Aug, Code 1
forked from sxywu's block: DS Aug, Code 1
forked from sxywu's block: DS Aug, Code 3
forked from sxywu's block: DS Aug, Code 4
license: mit |
Built with blockbuilder.org
forked from sxywu's block: DS Aug, Code 1
forked from sxywu's block: DS Aug, Code 1
forked from sxywu's block: DS Aug, Code 3
forked from sxywu's block: DS Aug, Code 4
[ | |
{ | |
"event": "Women's synchronized 3m springboard", | |
"country": "China", | |
"athletes": ["M.X. Wu", "T.M. Shi"], | |
"total": 345.60, | |
"gender": "F", | |
"breakdown": [ | |
[2, 55.8, [9, 9.5, 9, 8.5, 9, 9], [9.5, 9.5, 9.5, 9.5, 9.5]], | |
[2, 52.2, [9, 9, 9, 8.5, 8.5, 9], [8.5, 8.5, 9, 8.5, 9]], | |
[3, 76.5, [8.5, 8.5, 8, 8.5, 7.5, 8], [9, 8.5, 9, 8.5, 8.5]], | |
[3, 80.1, [9, 9, 9, 8.5, 9, 8.5], [8.5, 9, 9, 9, 9]], | |
[3, 81, [9.5, 8.5, 9, 8.5, 9, 9], [9, 9, 9, 9, 9]] | |
] | |
}, | |
{ | |
"event": "Women's synchronized 3m springboard", | |
"country": "Italy", | |
"athletes": ["T. Cagnotto", "F. Dallapé"], | |
"total": 313.83, | |
"gender": "F", | |
"breakdown": [ | |
[2, 51.6, [8.5, 8.5, 8, 8.5, 8.5, 8.5], [9, 8.5, 9, 5.5, 8.5]], | |
[2, 50.4, [8, 8, 8.5, 8.5, 8.5, 9], [8.5, 8.5, 8.5, 8, 8.5]], | |
[3, 71.1, [7.5, 7.5, 8, 8, 8, 8], [8, 7.5, 8, 8.5, 8]], | |
[3.1, 66.03, [8, 7.5, 7.5, 6.5, 7, 7], [7, 7, 7, 7, 7.5]], | |
[3, 74.7, [8, 8, 8, 7.5, 8, 8], [8.5, 8.5, 8.5, 8.5, 8.5]] | |
] | |
}, | |
{ | |
"event": "Women's synchronized 3m springboard", | |
"country": "Australia", | |
"athletes": ["M. Keeney", "A. Smith"], | |
"total": 299.19, | |
"gender": "F", | |
"breakdown": [ | |
[2, 48, [8, 8, 8, 7.5, 7.5, 8], [8, 8.5, 8, 8.5, 8]], | |
[2, 42, [7, 7.5, 7.5, 7, 6, 6.5], [8, 7, 7, 7, 7]], | |
[3, 70.2, [8.5, 8, 8, 7, 7, 7], [8, 8.5, 8, 8, 8]], | |
[3.1, 67.89, [7.5, 8, 7.5, 6.5, 6.5, 5.5], [7.5, 7.5, 7.5, 8, 7.5]], | |
[3, 71.1, [7.5, 8, 7.5, 7.5, 7.5, 7.5], [8, 7.5, 8, 8.5, 8.5]] | |
] | |
}, | |
{ | |
"event": "Men's synchronized 10m platform", | |
"country": "China", | |
"athletes": ["A. Chen", "Y. Lin"], | |
"total": 496.98, | |
"gender": "M", | |
"breakdown": [ | |
[2, 57, [9.5, 9, 9.5, 9.5, 9, 9.5], [9.5, 9.5, 9.5, 9.5, 9.5]], | |
[2, 57, [9.5, 9.5, 9.5, 9.5, 9.5, 9.5], [9.5, 10, 9.5, 9.5, 9.5]], | |
[3.4, 92.82, [8.5, 8.5, 9, 9.5, 10, 9], [9.5, 9, 9, 9, 9.5]], | |
[3.6, 85.32, [7, 7, 6.5, 8, 8.5, 8.5], [8, 8, 8, 8, 8.5]], | |
[3.7, 106.56, [9.5, 9.5, 9, 9.5, 10, 9.5], [9.5, 9.5, 9.5, 10, 10]], | |
[3.6, 98.28, [9, 9, 9, 9, 9, 9], [8.5, 9.5, 9, 9, 9.5]] | |
] | |
}, | |
{ | |
"event": "Men's synchronized 10m platform", | |
"country": "United States", | |
"athletes": ["S. Johnson", "D. Boudia"], | |
"total": 457.11, | |
"gender": "M", | |
"breakdown": [ | |
[2, 54, [9, 9, 9, 9, 9.5, 9], [9, 9, 9, 9, 9.5]], | |
[2, 53.4, [9, 8.5, 9, 8.5, 9, 9], [9, 9, 8.5, 8.5, 9]], | |
[3.2, 83.52, [8.5, 8.5, 8.5, 8.5, 9, 8.5], 8.5, 8.5, 9, 9, 9], | |
[3.4, 85.68, [7.5, 7, 7.5, 9, 9.5, 9], [8, 8.5, 8.5, 8.5, 8.5]], | |
[3.7, 85.47, [7, 7, 7.5, 8, 7.5, 7.5], [8.5, 8, 8, 8, 7.5]], | |
[3.6, 95.04, [8, 9, 8.5, 8.5, 8.5, 8.5], [9, 9, 9, 9, 9]] | |
] | |
}, | |
{ | |
"event": "Men's synchronized 10m platform", | |
"country": "Great Britain", | |
"athletes": ["T. Daley", "D. Goodfellow"], | |
"total": 444.45, | |
"gender": "M", | |
"breakdown": [ | |
[2, 51.6, [8.5, 9.0, 9.5, 8.5, 8.5, 8.5], [9, 8.5, 8.5, 8.5, 8.5]], | |
[2, 49.8, [8.5, 8.5, 8.5, 8.5, 8, 8.5], [8, 8, 8, 8.5, 8.5]], | |
[3.2, 79.68, [9, 8.5, 8.5, 7.5, 7.5, 8], [9, 8, 8.5, 8.5, 8.5]], | |
[3.4, 81.6, [8.5, 9, 8.5, 7.5, 7.5, 7.5], [8, 8.5, 8, 7.5, 8]], | |
[3.7, 92.13, [8, 8, 8, 8, 8.5, 8], [9, 8.5, 8, 8.5, 8.5]], | |
[3.6, 89.64, [8.5, 8.5, 8.5, 8, 7.5, 7.5], [8.5, 8.5, 8, 8.5, 8.5]] | |
] | |
}, | |
{ | |
"event": "Women's synchronized 10m platform", | |
"country": "China", | |
"athletes": ["R. L. Chen", "H. X. Liu"], | |
"total": 354, | |
"gender": "W", | |
"breakdown": [ | |
[2, 55.8, [9, 9, 9, 9, 9, 9], [9.5, 9.5, 9.5, 9.5, 9.5]], | |
[2, 55.8, [9, 8.5, 9, 9.5, 9, 9], [9.5, 9.5, 9.5, 9.5, 9.5]], | |
[3, 79.2, [8.5, 8.5, 9, 8.5, 8.5, 8.5], [9, 8.5, 9, 9, 9]], | |
[3.2, 75.84, [8, 7.5, 7.5, 7, 8, 7.5], [9, 8, 8, 8.5, 8]], | |
[3.2, 87.36, [9.5, 8.5, 8.5, 8.5, 8.5, 8.5], [9.5, 9.5, 9.5, 9.5, 9]] | |
] | |
}, | |
{ | |
"event": "Women's synchronized 10m platform", | |
"country": "Malaysia", | |
"athletes": ["P. Rinong", "J.H. Cheong"], | |
"total": 344.34, | |
"gender": "W", | |
"breakdown": [ | |
[2, 52.8, [8.5, 8.5, 9, 8.5, 9.5, 8.5], [9, 9, 9, 9.5, 9]], | |
[2, 52.8, [8.5, 8.5, 8.5, 9, 8.5, 8.5], [8.5, 9, 9, 9, 9]], | |
[3, 76.5, [6.5, 8, 8, 9, 8.5, 8.5], [8.5, 8, 8.5, 9, 9]], | |
[3.2, 79.68, [8, 7.5, 8.5, 8, 8.5, 7.5], [8.5, 8.5, 8.5, 9, 8.5]], | |
[3.2, 82.56, [8.5, 8.5, 8.5, 8.5, 8, 8.5], [8.5, 8.5, 9, 8.5, 9]] | |
] | |
}, | |
{ | |
"event": "Women's synchronized 10m platform", | |
"country": "Canada", | |
"athletes": ["M. Benfeito", "R. Filion"], | |
"total": 336.18, | |
"gender": "W", | |
"breakdown": [ | |
[2, 52.8, [9, 8.5, 8.5, 8.5, 9.5, 8.5], [9, 9, 9, 9, 9]], | |
[2, 52.8, [9, 8.5, 8, 8.5, 8.5, 8.5], [9, 9, 9, 9, 9]], | |
[3, 74.7, [8, 8, 8, 8, 8, 8], [9, 8.5, 8.5, 8.5, 8.5]], | |
[3.3, 75.24, [7.5, 7, 7, 7, 7, 7.5], [8, 7.5, 8, 8, 8]], | |
[3.2, 80.64, [8.5, 8, 8, 8.5, 8.5, 8], [8.5, 8.5, 8.5, 8.5, 9]] | |
] | |
} | |
] |
<!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'></canvas> | |
<script> | |
var canvas = document.getElementById('canvas'); | |
var ctx = canvas.getContext('2d'); | |
ctx.globalCompositeOperation = 'overlay'; | |
// properties | |
var padding = 25; | |
var width = canvas.width = 800; | |
var height = canvas.height = 1200; | |
var maxRadius = 30; | |
var TWO_PI = 2 * Math.PI; | |
var colors = { | |
'China': [[255,0,0], [255,255,0]], | |
'United States': [[187, 19, 62], [0, 44, 119]], | |
'Great Britain': [[0, 39, 118], [198, 12, 48]], | |
'Italy': [[0, 146, 70], [206, 43, 55]], | |
'Australia': [[0, 0, 139], [255, 0, 0]], | |
'Malaysia': [[1, 0, 102], [255, 204, 0]], | |
'Canada': [ [255, 255, 255], [255, 0, 0]], | |
'F': [250,176,189], | |
'M': [187,209,222] | |
}; | |
// scale | |
var radiusScale = d3.scaleLinear().range([5, maxRadius]); | |
var xScale = d3.scaleLinear() | |
.range([padding, width - padding]); | |
var yScale = d3.scaleLinear().range([0, 1]); | |
function processData(data) { | |
_.each(data, function(d) { | |
// create an artifical first score | |
d.breakdown.unshift([1.5, 1, [5, 5, 5, 5, 5, 5]]); | |
d.processed = _.map(d.breakdown, function(score) { | |
return _.map(score[2], function(num) {return num * score[0]}); | |
}); | |
}); | |
var difficulty = _.chain(data).map('breakdown').flatten().map(0).value(); | |
var maxRadius = _.max(difficulty); | |
var scores = _.chain(data).map('processed').flattenDeep().value(); | |
var minY = _.min(scores); | |
var maxY = _.max(scores); | |
var minX = _.minBy(data, 'total').total - padding; | |
var maxX = _.maxBy(data, 'total').total + padding; | |
radiusScale.domain([1, maxRadius]); | |
xScale.domain([minX, maxX]); | |
yScale.domain([minY, maxY]); | |
} | |
// 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.1)'); | |
gradient.addColorStop(0, 'rgba(' + colors[team.country][1] + ',0.1)'); | |
return { | |
centerX: padding, | |
centerY: xScale(team.total), | |
stroke: gradient, | |
fill: 'rgba(' + colors[team.gender] + ', 0.01)', | |
radii: _.map(team.breakdown, function(scores) { | |
return radiusScale(scores[0]); | |
}), | |
points: _.map(team.processed, function(scores) { | |
return generateCircleData(scores); | |
}), | |
length: _.map(team.breakdown, function(scores) { | |
return Math.round(scores[1]) * 3.5; | |
}), | |
rotations: _.map(team.breakdown, function(scores) { | |
return scores[1] / team.total; | |
}), | |
totalLength: team.total * 3.5, | |
elapsed: 0, | |
data: team | |
} | |
} | |
// 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(scores) { | |
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; | |
_.each(scores, function(score) { | |
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); | |
// vary the y-pos by the score, but subtract it | |
// by what is around the mid-point so that | |
// some are positive and others are negative | |
newY += dx * (yScale(score) * 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 tweenPoints(circle1, circle2) { | |
// interpolate all the points of the circles | |
var interpolators = _.map(circle1, function(point1, i) { | |
return { | |
x: d3.interpolate(point1.x, circle2[i].x), | |
y: d3.interpolate(point1.y, circle2[i].y) | |
}; | |
}); | |
return function(t) { | |
return _.map(interpolators, function(interpolate) { | |
return {x: interpolate.x(t), y: interpolate.y(t)}; | |
}); | |
}; | |
} | |
// given set of points making up a squiggly line | |
// turn it into a squiggly imperfect circle | |
// also calculate the interpolators for them | |
function calculateCircles(flow) { | |
flow.circles = []; | |
flow.interpolators = []; | |
var prevCircle = null; | |
_.each(flow.points, function(points, i) { | |
// calculate circles | |
var point = points.first; | |
var rotation = flow.rotations[i]; | |
var radii = flow.radii[i]; | |
var circle = []; | |
var theta = TWO_PI * (point.x + rotation); | |
var radius = radii * point.y; | |
var x = radius * Math.cos(theta); | |
var y = radius * Math.sin(theta); | |
circle.push({x: x, y: y}); | |
while (point.next) { | |
point = point.next; | |
// given its x and y, calculate its theta and radius | |
var theta = TWO_PI * (point.x + rotation); | |
var radius = radii * point.y; | |
var x = radius * Math.cos(theta); | |
var y = radius * Math.sin(theta); | |
circle.push({x: x, y: y}); | |
} | |
flow.circles.push(circle); | |
// now calculate the interpolators | |
if (prevCircle) { | |
var interpolators = tweenPoints(prevCircle, circle); | |
flow.interpolators.push(interpolators); | |
} | |
prevCircle = circle; | |
}); | |
} | |
function drawCircle(elapsed, flow) { | |
if (elapsed > flow.totalLength) return; | |
var drawCount; | |
elapsed = parseInt(elapsed); | |
elapsed = d3.easeQuad(elapsed / flow.totalLength); | |
elapsed = parseInt(flow.totalLength * elapsed); | |
if (elapsed < flow.elapsed) { | |
// if it's going backwards, clear the canvas | |
// and set everything back to 0 | |
ctx.clearRect(0, 0, width, height); | |
flow.elapsed = 0; | |
} | |
_.times(elapsed - flow.elapsed, function(t) { | |
t += flow.elapsed; | |
drawCount = t; | |
_.some(flow.interpolators, function(interpolator, i) { | |
var length = flow.length[i + 1]; | |
if (t > length) { | |
// if elapsed is more than length of section | |
// subtract length and move to next interpolator | |
t -= length; | |
return false; | |
} | |
// else this is the interpolator to use | |
ctx.strokeStyle = flow.stroke; | |
ctx.fillStyle = flow.fill; | |
ctx.beginPath(); | |
flow.centerX += 0.5; | |
var yOffset = (_.last(flow.radii) / 2) * Math.sin(drawCount/flow.data.total *TWO_PI); | |
ctx.setTransform(1, 0, 0, 1, flow.centerY + yOffset, flow.centerX); | |
var points = interpolator(t / length); | |
_.each(points, function(pos) { | |
ctx.lineTo(pos.x, pos.y); | |
}); | |
ctx.closePath(); | |
ctx.stroke(); | |
// ctx.fill(); | |
return true; | |
}); | |
}); | |
flow.elapsed = elapsed; | |
} | |
// load the data | |
d3.json('data.json', function(data) { | |
processData(data); | |
var flows = _.map(data, function(d) { | |
var flow = generateFlowData(d); | |
calculateCircles(flow); | |
return flow; | |
}) | |
var t = d3.timer(function(elapsed) { | |
_.each(flows, function(flow) { | |
drawCircle(elapsed, flow); | |
}); | |
if (elapsed > 2000) t.stop(); | |
}); | |
}); | |
</script> | |
</body> |