constructor(props){ super(props) this.state = { labels: [ { type: 'chartTitle', text : 'Chord Interval Counts', textClass : 'chartTitle', gWrapperClass : 'chartTitleG', transformation: '', fontSize: '1.5em' } ], margins : { top: 20, right: 20, bottom: 100, left: 20 }, curShowing: 0 } this.theData = this.removeLessimportantData(this.props.data[this.state.curShowing]); this.seqClr = d3.scaleSequential().domain(this.makeSeqDom(this.theData)).interpolator(d3.interpolateRainbow); this.radiusScale = d3.scaleSqrt(); this.colorScale = d3.scaleOrdinal(d3.schemeDark2); this.simulation; this.d3Circles; this.myTickFn = this.myTickFn.bind(this) this.makeSeqDom = this.makeSeqDom.bind(this) this.toggle = this.toggle.bind(this) this.drawChart = this.drawChart.bind(this) this.calcXPos = this.calcXPos.bind(this) this.calcYPos = this.calcYPos.bind(this) this.bubbleDataJoin; }
const computeColorGen = days => { let min = Number.MAX_VALUE let max = Number.MIN_VALUE forEach(days, day => { const [_min, _max] = extent(day.hours, value => value && value.value) if (_min < min) { min = _min } if (_max > max) { max = _max } }) return scaleSequential(interpolateViridis).domain([max, min]) }
constructor(props: *) { super(props) this.colorScale = d3.scaleSequential(d3.interpolateWarm) .domain([0, 5]) this.radiusScale = d3.scaleBand() .domain(arcMilestones) .range([.15 * width, .45 * width]) .paddingInner(0.1) this.arcFn = d3.arc() .innerRadius(milestone => this.radiusScale(milestone)) .outerRadius(milestone => this.radiusScale(milestone) + this.radiusScale.bandwidth()) .startAngle(- Math.PI / trackIds.length) .endAngle(Math.PI / trackIds.length) .padAngle(Math.PI / 200) .padRadius(.45 * width) .cornerRadius(2) }
function render(data, symbols, symbolLabel, label, stream) { const symbolVolume = {}; symbols.forEach((ticker) => { symbolVolume[ticker] = sum(pluck(data, ticker)); }); const symbolVolumeSorted = sortBy(values(symbolVolume), identify).reverse(); var stack = d3.stack() .keys(symbols.sort()) .order(d3.stackOrderNone); if (stream) { stack.offset(d3.stackOffsetWiggle); } var series = stack(data); let width = Math.max(Math.max(document.documentElement.clientWidth, window.innerWidth || 0) - getScrollbarWidth(), 1200); let height = Math.max(Math.max(document.documentElement.clientHeight, window.innerHeight || 0), 700); let margin = { top: 60, left: 0, right: 0, bottom: 0 }; var x = d3.scaleLinear() .domain(d3.extent(data, function(d){ return d.date; })) .range([0, width]); function stackMax(layer) { return d3.max(layer, function(d) { return d[1]; }); } function stackMin(layer) { return d3.min(layer, function(d) { return d[0]; }); } var y = d3.scaleLinear() .domain([d3.min(series, stackMin), d3.max(series, stackMax)]) .range([height - margin.top - margin.bottom, 0]); var color = d3.scaleSequential(d3.interpolateRainbow) .domain([0, symbols.length]); var area = d3.area() .x(function(d) { return x(d.data.date); }) .y0(function(d) { return y(d[0]); }) .y1(function(d) { return y(d[1]); }) .curve(d3.curveMonotoneX); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); var xAxis = d3.axisTop() .tickSize(-height) .tickValues(data.map(d => d.date)) .tickFormat((t, i) => moment(t).format('MMM-YY')) .scale(x); svg.append('g') .attr('class', 'x axis') .attr('transform', `translate(${margin.left}, 15)`) .call(xAxis); svg.append('text') .attr('transform', 'translate(10, 40)') .attr('class', 'main-label') .text(label); var paths = svg.append('g').selectAll("path") .data(series) .enter(); paths.append("path") .attr('transform', `translate(${margin.left}, ${margin.top})`) .attr("d", area) .style("fill", function(d, i) { return color(symbolVolumeSorted.indexOf(symbolVolume[d.key])); }) .append('title') .text((d) => symbolLabel[d.key]); function canPlace(point, polygon, ticker, fontHeight, fontWidth) { const length = symbolLabel[ticker].length * fontWidth; const textBox = [ [point[0] - (length/2), point[1] - (fontHeight/2)], [point[0] + (length/2), point[1] - (fontHeight/2)], [point[0] + (length/2), point[1] + (fontHeight/2)], [point[0] - (length/2), point[1] + (fontHeight/2)], ]; if (any(textBox, (p) => !d3.polygonContains(polygon, p))) { return false; } return true; } const fontSize = []; function centroid(d, i) { var polygon = []; var heights = []; let fontHeight = 12; let fontWidth = 5; d.forEach((datum) => { heights.push(y(datum[0]) - y(datum[1])); polygon.push([x(datum.data.date), y(datum[0])]); }); clone(d).reverse().forEach((datum) => { polygon.push([x(datum.data.date), y(datum[1])]); }); const center = d3.polygonCentroid(polygon); if (canPlace(center, polygon, d.key, fontHeight, fontWidth)) { fontSize[i] = fontHeight; return center; } for (let [fontHeight, fontWeight] of [[12, 5], [10, 4], [8, 3], [6, 2.5]]) { for (let datum of shuffle(d)) { let y1 = y(datum[1]); let y0 = y(datum[0]); let point = [x(datum.data.date), y0 - ((y0 - y1) / 2)]; if (canPlace(point, polygon, d.key, fontHeight, fontWidth)) { fontSize[i] = fontHeight; return point; } } } return [-1000, -1000]; } paths.append("text") .attr('transform', (d, i) => { const c = centroid(d, i); return `translate(${margin.left + c[0]}, ${margin.top + c[1]})`; }) .attr("dy", "0.32em") .style('fill', 'white') .style('font-size', (d, i) => fontSize[i] + 'px') .style('text-anchor', 'middle') .style('font-family', 'sans-serif') .text((d) => symbolLabel[d.key]); }
function renderTree(newRoot) { var t = d3.transition() .duration(500) .ease(d3.easeLinear); let w = newRoot.x1 - newRoot.x0, h = newRoot.y1 - newRoot.y0; backEnter.merge(backG) .select('rect') .on('click', () => { if (currentRoot.parent) { renderTree(currentRoot.parent); } }); const backLabel = newRoot.id.replace(/\./g, ' → ') + (newRoot.parent ? ' ↑ ' : ''); backEnter.merge(backG).select('text').text(backLabel); const scaleX = width/w; const scaleY = height/h; const translateX = -newRoot.x0 * scaleX; const translateY = -newRoot.y0 * scaleY + backHeight; rootG.merge(rootGEnter).transition(t).attr('transform', `translate(${translateX},${translateY}),scale(${scaleX},${scaleY})`); labelRoot.merge(labelRootEnter).transition(t).attr('transform', `translate(${translateX},${translateY})`); currentRoot = newRoot; const nodes = _.chain(root.descendants()) .filter(d => d.parent === currentRoot || grandParent(d) === currentRoot) .sortBy(d => -d.depth) .value(); const sorted = _.chain(nodes) .filter(d => d.parent === currentRoot) .map(d => d.id) .sortBy('value') .value(); var colorScale = d3.scaleSequential(d3.interpolateRainbow) .domain([0, sorted.length]); const color = (t) => colorScale(sorted.indexOf(t)); const rect = rootG.merge(rootGEnter) .selectAll(".node") .data(nodes, d => d.id); const updateRects = (selection) => { selection .style('opacity', d => grandParent(d) === currentRoot || (d.parent === currentRoot && !d.children) ? 1 : 0) .style('stroke-width', 1/(scaleX + scaleY)) .attr("x", d => d.x0) .attr("y", d => d.y0) .attr("width", d => d.x1 - d.x0) .attr("height", d => d.y1 - d.y0); }; rect.exit().transition(t).remove(); rect.enter() .append("rect") .attr("class", "node") .attr("title", function(d) { return d.id + "\n" + format(d.value); }) .call(updateRects) .merge(rect) .style('cursor', d => (d.parent !== currentRoot) ? 'pointer' : null) .on('click', (d) => { if (d.parent !== currentRoot) { renderTree(immediateChild(d)); } }) .attr("fill", function(d) { if(root === d || (d.parent === currentRoot && d.children)) { return 'transparent'; } else { return color(d.parent !== currentRoot ? d.parent.id : d.id); } }) .attr('pointer-events', d => grandParent(d) === currentRoot || (d.parent === currentRoot && !d.children) ? null : 'none') .transition(t) .call(updateRects); const label = labelRoot.merge(labelRootEnter) .selectAll('.node-label-wrapper') .data(nodes, d => d.id); label.exit().remove(); const updateLabels = (selection, scaleX, scaleY) => { selection .attr("x", d => scaleX * d.x0) .attr("y", d => scaleY * d.y0) .attr("width", d => (scaleX * (d.x1 - d.x0)) + 'px') .attr("height", d => (scaleY * (d.y1 - d.y0)) + 'px') .style('opacity', d => d.parent === currentRoot ? 1 : 0); }; const labelEnter = label.enter() .append('foreignObject') .attr("pointer-events", "none") .attr('class', 'node-label-wrapper') .call(updateLabels, lastScaleX, lastScaleY); const labelDiv = labelEnter.append('xhtml:div'); labelDiv.append('div').attr('class', 'node-label'); labelDiv.append('div').attr('class', 'node-value'); labelEnter.merge(label) .select("div.node-label") .text(d => _.last(d.id.split('.'))); labelEnter.merge(label) .select("div.node-value") .text(d => format(d.value)); labelEnter .merge(label) .style('pointer-events', d => d.parent === currentRoot ? null : 'none') .transition(t) .call(updateLabels, scaleX, scaleY); lastScaleX = scaleX; lastScaleY = scaleY; }
.range([0, width]); function stackMax(layer) { return d3.max(layer, function(d) { return d[1]; }); } function stackMin(layer) { return d3.min(layer, function(d) { return d[0]; }); } var y = d3.scaleLinear() .domain([d3.min(series, stackMin), d3.max(series, stackMax)]) .range([height - margin.top - margin.bottom, 0]); var color = d3.scaleSequential(d3.interpolateRainbow) .domain([0, tickers.length]); var area = d3.area() .x(function(d) { return x(d.data.date); }) .y0(function(d) { return y(d[0]); }) .y1(function(d) { return y(d[1]); }) .curve(d3.curveMonotoneX); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); var xAxis = d3.axisTop() .tickSize(-height)