window.highlightColor = '#bf0bbf' window.makeSliders = function(metrics, sets, c, selectSet, drawRow, onRender){ var width = 180 var height = 30 var color = '#000' var xScale = d3.scaleLinear().range([0, width]).domain([0, 1]) .clamp(1) var sliderSel = c.svg.appendMany('g', metrics) .translate((d, i) => [-c.margin.left -10 , 130*i + 30]) .on('click', function(d){ d.target = xScale.invert(d3.mouse(this)[0]) render() }) .classed('slider', true) .st({cursor: 'pointer'}) var textSel = sliderSel.append('text.slider-label-container') .at({y: -20, fontWeight: 500, textAnchor: 'middle', x: 180/2}) sliderSel.append('rect') .at({width, height, y: -height/2, fill: 'rgba(0,0,0,0)'}) sliderSel.append('path').at({ d: `M 0 -.5 H ${width}`, stroke: color, strokeWidth: 1 }) var leftPathSel = sliderSel.append('path').at({ d: `M 0 -.5 H ${width}`, stroke: color, strokeWidth: 3 }) var drag = d3.drag() .on('drag', function(d){ var x = d3.mouse(this)[0] d.target = xScale.invert(x) render() }) var circleSel = sliderSel.append('circle').call(drag) .at({r: 7, stroke: '#000'}) var exSel = c.svg.append('g').translate([-c.margin.left -10, 400]) .st({fontSize: 13}) var curY = 0 exSel.append('g') .append('text').text('The selected set is...') var selectedSetG = exSel.append('g.selected').translate([-10, curY += 15]) .datum(sets[0]) .call(drawRow) selectedSetG.select('.no-stroke').classed('selected', 1) curY += 25 var exMetrics = exSel.appendMany('g', metrics) .translate(() => curY +=22, 1) .append('text').html(d => '10% small, 10% more than target') curY += 10 var exMeanDiff = exSel.append('text').translate(() => curY +=22, 1) .at({textAnchor: 'end', x: 190}) var exMaxDiff = exSel.append('text').translate(() => curY +=22, 1) .at({textAnchor: 'end', x: 190}) // Make histogram data sliderSel.each(function(metric){ var countKey = metric.key + '_count' sets.forEach(set => { var v = d3.sum(set, d => d[metric.field] == metric.key) set[countKey] = v / set.length }) var byCountKey = d3.nestBy(sets, d => d[countKey]) d3.range(.1, 1, .1).forEach(i => { if (byCountKey.some(d => d.key*100 == Math.round(i*100))) return var rv = [] rv.key = i byCountKey.push(rv) }) byCountKey.forEach(d => { d.metric = metric d.key = +d.key }) var countSel = d3.select(this).append('g.histogram').lower() .translate(30, 1) .appendMany('g', byCountKey) .translate(d => xScale.clamp(0)(d.key - .05), 0) xScale.clamp(1) countSel.append('text') // .text(d => '10') .at({fontSize: 11, opacity: .7, y: -8, textAnchor: 'middle', x: 9.5}) .text(d => d.key*100) countSel.append('path') .at({d: 'M 9.5 -18 V -30', stroke: '#ccc'}) countSel .appendMany('rect.histogram-set', d => d) .at({width: 16, height: 4, x: 1.5, y: (d, i) => i*6}) // .on('mouseover', selectSet) }) var histogramSetSel = sliderSel.selectAll('rect.histogram-set') .st({cursor: 'default'}) var axisSel = sliderSel.selectAll('.histogram text') var pinkSel = sliderSel.append('g') .at({r: 4, fill: highlightColor}) .st({pointerEvents: 'none', opacity:0}) pinkSel.append('path').at({stroke: highlightColor, d: 'M .5 0 V 15'}) pinkSel.append('text').at({y: 30, textAnchor: 'middle'}) pinkSel.append('text.score').at({y: 50, textAnchor: 'middle'}) function render(){ circleSel.at({cx: d => xScale(d.target)}) // circleSel.at({cx: d => xScale(d.target)}) textSel.text(d => (d.str + ' Target: ').replace('s ', ' ') + pctFmt(d.target)) axisSel .classed('selected', false) // .text(function(d){ // var str = Math.round(100*Math.abs(d.key - d.metric.target)) // if (d.some(e => e.selected)){ // d3.select(this).classed('selected', 1) // // str = str + '%' // } // return str // }) leftPathSel.at({d: d => `M 0 -.5 H ${xScale(d.target)}`}) metrics.forEach(d => { d.scoreScale = d3.scaleLinear() .domain([-.1, d.target, 1.1]) .range([0, 1, 0]) }) histogramSetSel.st({fill: d => d === sets.selected ? highlightColor: '#bbb'}) if (onRender) onRender() var shapes = sets.selected var metricVals = metrics.map(m => { return d3.sum(shapes, (d, i) => shapes[i][m.field] == m.key)/shapes.length }) pinkSel.translate((d, i) => xScale(metricVals[i]), 0) pinkSel.select('text').text((d, i) => pctFmt(metricVals[i])) pinkSel.select('.score').text((d, i) => 'Difference: ' + Math.round(shapes.score[i]*100)) selectedSetG.html('') .datum(sets.selected) .call(drawRow) selectedSetG.select('.no-stroke').classed('selected', 1) exMetrics .html((d, i) => { var target = d.target var actual = sets.selected[d.key + '_count'] var diff = sets.selected.score[i] var str = d.str.replace('ls', 'l').replace('ns', 'n').toLowerCase() return ` ${pctFmt(actual)} ${str}, ${pctFmt(diff)} ${actual < target ? 'less' : 'more'} than target ` }) .at({textAnchor: 'end', x: 190}) exMeanDiff .text('Mean Difference: ' + d3.format('.2%')(sets.selected['Utilitarian']/100)) exMaxDiff .text('Max Difference: ' + measures[1].ppFn(sets.selected['score']).replace('%', '.00%')) } return {render} } // window.initColumns('#columns-height', metrics1, measures) // window.initColumns('#columns-height-disagree', metrics2, measures2)