console.clear() d3.select('body').selectAppend('div.tooltip.tooltip-hidden') var teamgames = [] d3.loadData('games.csv', (err, [games]) => { games .sort((a, b) => a.timestamp > b.timestamp) .filter(d => d.tournmentName == 'NA 2017 Summer Split' && d.bracketName == 'regular_season') .forEach(game => { teamgames.push(_.extend({team: game.winner, won: 1}, game)) teamgames.push(_.extend({team: game.loser, won: 0}, game)) }) byTeam = d3.nestBy(teamgames, d => d.team) byTeam.forEach(team => { team.byMatch = d3.nestBy(team, d => d.matchName) team.bb = [[0, 0], [1, 1]] team.byWin = d3.nestBy(team.byMatch, d => d[0].won).sort(wonSort) team.byWin.forEach((w1, i) => { w1.parent = team.byMatch w1.percent = w1.length/team.byMatch.length w1.bb = cutBB(team.bb, w1.percent, w1.key == 1 ? 'l' : 'r') w1.byWin = d3.nestBy(w1, d => d[1].won).sort(wonSort) w1.byWin.forEach(w2 => { w2.parent = w1 w2.percent = w2.length/w1.length w2.bb = cutBB(w1.bb, w2.percent, w2.key == 1 ? 'u' : 'd') w2.byWin = d3.nestBy(w2.filter(d => d[2]), d => d[2] ? d[2].won : w2.key).sort(wonSort) w2.byWin.forEach(w3 => { w3.parent = w2 w3.percent = w3.length/w2.length w3.bb = cutBB(w2.bb, w3.percent, w3.key == 1 ? 'l' : 'r') }) // w2.byWin = null }) }) }) var sel = d3.select('.graph').html('') var teamSel = sel.append('div').appendMany(byTeam, 'div') .st({width: 192, display: 'inline-block'}) teamSel.append('div').text(d => d.key).st({textAlign: 'center'}) teamSel.each(drawTreemap) var teamSel = sel.append('div').appendMany(byTeam, 'div') .st({width: 192, display: 'inline-block'}) teamSel.append('div').text(d => d.key).st({textAlign: 'center'}) teamSel.each(drawIce) }) function drawTreemap(team){ var sel = d3.select(this) var s = 180 var svg = sel.append('svg').at({width: s, height: s}) team.byWin.forEach(d => drawByWin(d, 0)) function drawByWin(w, delay){ var fill = w.byWin && w.byWin.length ? w.key == 1 ? '#4caf50' : '#ff9800' : w.key == 1 ? '#4caf50' : '#ff9800' // w.bbstr = JSON.stringify(w.bb) svg.append('path') .at({d: bbToPath(w.bb, s), stroke: '#fff'}) .st({fill}) .datum(w) .call(d3.attachTooltip) .st({opacity: 0}) .transition().delay(delay).duration(delay ? 500 : 0) .st({opacity: 1}) if (w.byWin) w.byWin.forEach(d => drawByWin(d, delay*2 + 3000)) } } function drawIce(team){ var sel = d3.select(this) var s = 180 var svg = sel.append('svg').at({width: s, height: s}) .st({overflow: 'hidden'}) drawByWin(team, 0, -s/3, s) function drawByWin(m, x, y, width){ if (!m) return var fill = m.key == 1 ? '#4caf50' : '#ff9800' svg.append('rect') .at({x, y, width, height: s/3 - 1, fill}) .datum(m) .call(d3.attachTooltip) if (!m.byWin || !m.byWin.length) return var [w, l] = m.byWin var p = w.percent drawByWin(w, x, y + s/3, width*p - 1) drawByWin(l, x + width*p + 1, y + s/3, width*(1 - p) - 1) // setTimeout(() => { // svg.append('path') // .at({d: `M ${x + width} ${y} V ${300}`, stroke: '#000', strokeWidth: 2}) // }, 0) } } function wonSort(a, b){ return a.key > b.key } function cutBB([[x0, y0], [x1, y1]], p, type){ x0 = x0 + .03 x1 = x1 - .03 y0 = y0 + .03 y1 = y1 - .03 var mx = mix(x0, x1, type == 'l' ? p : 1 - p) var my = mix(y0, y1, type == 'u' ? p : 1 - p) if (type == 'l' || type == 'r'){ return type == 'l' ? [[x0, y0], [mx, y1]] : [[mx, y0], [x1, y1]] } else { return type == 'u' ? [[x0, y0], [x1, my]] : [[x0, my], [x1, y1]] } } function bbToPath([[x0, y0], [x1, y1]], s){ return [ 'M', x0*s, y0*s, 'L', x0*s, y1*s, 'L', x1*s, y1*s, 'L', x1*s, y0*s, 'Z' ].join(' ') } function mix(x0, x1, p){ return x0*(1 - p) + x1*p }