// graph elements function addAxis(element, scale, location) { var axis = d3.svg.axis() .scale(scale) .orient(location) .ticks(15, ",.1s") .tickSize(3, 0); element.append("g").attr("class", "axis") .attr("transform", placeAxis(location)) .call(axis); }; function placeAxis(location) { if (location == 'bottom') { return "translate(0," + height + ")"; } else if (location == 'left') { return "translate(0,0)"; } } function applyData(data) { // initialize counter variables var maxValue = 0, maxWeight = 0, minValue = data[0].startvalue, minWeight = data[0].startweight, diff = 0, sizeSum = [0, 0], comboSum = [0, 0]; // use data to define max value, weight, weight diffs, and sum size and combo data.forEach(function(d) { d.weightDiff = +d.endweight - +d.startweight; if (Math.abs(d.weightDiff) > diff) { diff = Math.abs(d.weightDiff) }; maxValue = Math.max(maxValue, +d.startvalue, +d.endvalue); maxWeight = Math.max(maxWeight, +d.startweight, +d.endweight); minValue = Math.min(minValue, +d.startvalue, +d.endvalue); minWeight = Math.min(minWeight, +d.startweight, +d.endweight); // calculate for aggregate sizeSum = [sizeSum[0] + +d.startweight, sizeSum[1] + +d.endweight ]; comboSum = [comboSum[0] + +d.startweight * +d.startvalue, comboSum[1] + +d.endweight * +d.endvalue ]; }) // calculate and append aggregate data // assumes same number of segments for start & end // assumes no missing values var aggregate = { startvalue: comboSum[0] / sizeSum[0], endvalue: comboSum[1] / sizeSum[1], startweight: sizeSum[0] / data.length, endweight: sizeSum[1] / data.length, }; aggregate[mapping.segmentName] = 'aggregate'; data.push(aggregate); // sets x and y scale to determine size of visible boxes if (scaleOptions.weightScale == 'log') { var sizeScale = d3.scale.log().clamp(true) .domain([minWeight * .8, maxWeight * 2]) .range([0, width]); } else { var sizeScale = d3.scale.linear().clamp(true) .domain([minWeight * .8, maxWeight * 1.1]) .range([0, width]); } if (scaleOptions.valueScale == 'log') { var valueScale = d3.scale.log().clamp(true) .domain([minValue * .9, maxValue * 2]) .range([height, 0]); } else { var valueScale = d3.scale.linear().clamp(true) .domain([minValue * .9, maxValue * 1.1]) .range([height, 0]); } // color scale, based on data diffs var colorScale = d3.scale.linear() .domain([-diff, 0, diff]) .range(['orange', 'grey', 'blue']) return { size: sizeScale, value: valueScale, color: colorScale } }; // data to polygons function valuesToPoints(startweight, endweight, startvalue, endvalue, halfWidth) { points = [ [startweight, startvalue] ]; var a = startweight - endweight; var b = startvalue - endvalue; var dist = Math.sqrt(a * a + b * b); var newPoint1 = [halfWidth / dist * b + endweight, -halfWidth / dist * a + endvalue]; var newPoint2 = [-halfWidth / dist * b + endweight, halfWidth / dist * a + endvalue]; points.push(newPoint1, newPoint2); return points.join(" ") }; // function to draw comets function drawComets(element, data) { element.selectAll("polygon") .data(data) .enter() .append("polygon") .attr("points", function(d) { return valuesToPoints(scales.size(+d.startweight), scales.size(+d.endweight), scales.value(+d.startvalue), scales.value(+d.endvalue), 3); }) .attr("fill", function(d) { if (d[mapping.segmentName] == 'aggregate') { // return 'black'; } else { return scales.color(d.weightDiff) }; }) .append("title") .text(function(d) { return d[mapping.filterName] + ', ' + mapping.segmentName + d[mapping.segmentName] + ', value: ' + d.startvalue + ', ' + d.endvalue + ' weights ' + d.startweight + ' ' + d.endweight; }); };