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
forked from sxywu's block: DS Aug, Code 5
| 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
forked from sxywu's block: DS Aug, Code 5
| [ | |
| { | |
| "event": "Women's synchronized 3m springboard", | |
| "country": "Italy", | |
| "athletes": ["T. Cagnotto", "F. Dallapé"], | |
| "total": 313.83, | |
| "gender": "F", | |
| "breakdown": [ | |
| [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": "China", | |
| "athletes": ["M.X. Wu", "T.M. Shi"], | |
| "total": 345.60, | |
| "gender": "F", | |
| "breakdown": [ | |
| [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": "Australia", | |
| "athletes": ["M. Keeney", "A. Smith"], | |
| "total": 299.19, | |
| "gender": "F", | |
| "breakdown": [ | |
| [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": "Women's synchronized 10m platform", | |
| "country": "Malaysia", | |
| "athletes": ["P. Rinong", "J.H. Cheong"], | |
| "total": 344.34, | |
| "gender": "W", | |
| "breakdown": [ | |
| [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": "China", | |
| "athletes": ["R. L. Chen", "H. X. Liu"], | |
| "total": 354, | |
| "gender": "W", | |
| "breakdown": [ | |
| [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": "Canada", | |
| "athletes": ["M. Benfeito", "R. Filion"], | |
| "total": 336.18, | |
| "gender": "W", | |
| "breakdown": [ | |
| [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]] | |
| ] | |
| }, | |
| { | |
| "event": "Men's synchronized 10m platform", | |
| "country": "United States", | |
| "athletes": ["S. Johnson", "D. Boudia"], | |
| "total": 457.11, | |
| "gender": "M", | |
| "breakdown": [ | |
| [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": "China", | |
| "athletes": ["A. Chen", "Y. Lin"], | |
| "total": 496.98, | |
| "gender": "M", | |
| "breakdown": [ | |
| [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": "Great Britain", | |
| "athletes": ["T. Daley", "D. Goodfellow"], | |
| "total": 444.45, | |
| "gender": "M", | |
| "breakdown": [ | |
| [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": "Men's synchronized 3m springboard", | |
| "country": "United States", | |
| "athletes": ["M. Hixon", "S. Dorman"], | |
| "total": 450.21, | |
| "gender": "M", | |
| "breakdown": [ | |
| [3.4, 85.68, [8, 8, 8, 9, 8.5, 8.5], [8.5, 8.5, 8.5, 8.5, 8.5]], | |
| [3.5, 87.15, [8.5, 8, 8, 7, 8, 8.5], [8.5, 8.5, 8.5, 8.5, 8]], | |
| [3.4, 78.54, [7, 7.5, 7.5, 7.5, 8, 6], [7.5, 8.5, 7.5, 8, 8]], | |
| [3.8, 98.04, [8.5, 8, 8, 8.5, 8.5, 9], [9, 9, 8.5, 8.5, 9]] | |
| ] | |
| }, | |
| { | |
| "event": "Men's synchronized 3m springboard", | |
| "country": "Great Britain", | |
| "athletes": ["J. Laugher", "C. Mears"], | |
| "total": 454.32, | |
| "gender": "M", | |
| "breakdown": [ | |
| [3.4, 84.66, [7.5, 8, 7.5, 8.5, 8.5, 8.5], [8.5, 8, 8.5, 9, 8.5]], | |
| [3.4, 85.68, [8, 8, 8.5, 8, 8.5, 9], [8.5, 8.5, 8.5, 9, 8]], | |
| [3.9, 86.58, [7, 8, 7.5, 7.5, 8, 8.5], [7.5, 7, 7, 7.5, 7]], | |
| [3.8, 91.2, [7.5, 7.5, 7.5, 8.5, 8.5, 8.5], [8, 8, 8, 8.5, 8]] | |
| ] | |
| }, | |
| { | |
| "event": "Men's synchronized 3m springboard", | |
| "country": "China", | |
| "athletes": ["K. Qin", "Y. Cao"], | |
| "total": 443.7, | |
| "gender": "M", | |
| "breakdown": [ | |
| [3.4, 79.56, [8.5, 8, 8, 8, 7.5, 7.5], [8, 7.5, 8, 8, 7.5]], | |
| [3.4, 82.62, [9, 8.5, 8.5, 8, 6.5, 7], [8.5, 8, 8.5, 8.5, 8]], | |
| [3.5, 90.3, [9, 8.5, 8.5, 9.5, 9, 9], [8.5, 8.5, 8.5, 9, 8.5]], | |
| [3.8, 83.22, [7, 7.5, 7, 7, 7, 7.5], [7.5, 8, 7.5, 7, 7.5]] | |
| ] | |
| } | |
| ] |
| <!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> | |
| body { | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <canvas id='canvas'></canvas> | |
| <svg id='svg'></svg> | |
| <script> | |
| var canvas = document.getElementById('canvas'); | |
| var ctx = canvas.getContext('2d'); | |
| ctx.globalCompositeOperation = 'overlay'; | |
| // properties | |
| var padding = 50; | |
| var width = canvas.width = 800; | |
| var height = canvas.height = 1200; | |
| var maxRadius = 45; | |
| 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([maxRadius / 3 * 2, maxRadius]); | |
| var yScale = d3.scaleLinear().range([0, 1]); | |
| var xWidth = 0; | |
| function processData(data) { | |
| xWidth = (width - 2 * padding) / data.length; | |
| _.each(data, function(d) { | |
| // create an artifical first score | |
| d.breakdown.unshift(d.breakdown[0]); | |
| d.processed = _.map(d.breakdown, function(score) { | |
| return _.map(score[2], function(num) {return num * score[0]}); | |
| }); | |
| d.totalDifficulty = _.reduce(d.breakdown, function(sum, score) { | |
| return sum + score[0]; | |
| }, 0) * d.total; | |
| }); | |
| 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, 'totalDifficulty').totalDifficulty - padding; | |
| // var maxX = _.maxBy(data, 'totalDifficulty').totalDifficulty + padding; | |
| radiusScale.domain([1, maxRadius]); | |
| yScale.domain([minY, maxY]); | |
| } | |
| // generate the flow line, given one event for one team | |
| function generateFlowData(team, i) { | |
| 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: xWidth * i + padding, | |
| centerY: padding, | |
| 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.floor(scores[1]) * 4; | |
| }), | |
| rotations: _.map(team.breakdown, function(scores) { | |
| return scores[1] / team.total; | |
| }), | |
| dataPoints: [], | |
| totalLength: Math.floor(team.total) * 4, | |
| 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 (flow.elapsed === flow.totalLength) { | |
| // if the last set of drawings wasthe totalLength | |
| // we're finished drawing this flow line | |
| return true; | |
| } | |
| var drawCount; | |
| elapsed = Math.min(parseInt(elapsed) / flow.totalLength, 1); | |
| elapsed = d3.easeQuad(elapsed); | |
| elapsed = parseInt(flow.totalLength * elapsed); | |
| _.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.beginPath(); | |
| flow.centerY += 0.5; | |
| var xOffset = (_.last(flow.radii) / 3) * Math.sin(drawCount/flow.data.total * TWO_PI); | |
| ctx.setTransform(1, 0, 0, 1, flow.centerX + xOffset, flow.centerY); | |
| var points = interpolator(t / length); | |
| _.each(points, function(pos) { | |
| ctx.lineTo(pos.x, pos.y); | |
| }); | |
| ctx.closePath(); | |
| ctx.stroke(); | |
| // ctx.fill(); | |
| if (t === length) { | |
| var x1 = flow.centerX + xOffset; | |
| var maxX = _.maxBy(flow.circles[i + 1], 'x').x; | |
| flow.dataPoints.push({ | |
| x1: x1, | |
| x2: x1 + maxX, | |
| y: flow.centerY, | |
| score: flow.data.breakdown[i + 1] | |
| }); | |
| } | |
| return true; | |
| }); | |
| }); | |
| flow.elapsed = elapsed; | |
| return false; | |
| } | |
| // load the data | |
| d3.json('data.json', function(data) { | |
| processData(data); | |
| var flows = _.map(data, function(d, i) { | |
| var flow = generateFlowData(d, i); | |
| calculateCircles(flow); | |
| return flow; | |
| }) | |
| var t = d3.timer(function(elapsed) { | |
| var allFinished = true; | |
| _.each(flows, function(flow) { | |
| allFinished &= drawCircle(elapsed, flow); | |
| }); | |
| if (allFinished) t.stop(); | |
| }); | |
| }); | |
| </script> | |
| </body> |