diff -r a8effb60ccb6 -r 762fc0eb4946 cms/app-client/app/components/discourses-component.js --- a/cms/app-client/app/components/discourses-component.js Sun Dec 04 13:49:44 2016 +0100 +++ b/cms/app-client/app/components/discourses-component.js Fri Dec 09 11:41:15 2016 +0100 @@ -1,13 +1,18 @@ import Ember from 'ember'; -import d3 from 'd3'; +import * as d3 from 'd3-selection'; +import * as d3h from 'd3-hierarchy'; +import * as d3s from 'd3-scale'; import _ from 'lodash/lodash'; +const MINIMUM_CIRCLE_WIDTH = 60.0; + export default Ember.Component.extend({ classNames: ['discourses-component'], constants: Ember.inject.service(), filter: Ember.inject.service(), + utils: Ember.inject.service(), discourseObserver: Ember.observer('filter.discourse', function() { Ember.$('.item').removeClass("selected"); @@ -25,22 +30,36 @@ var discourses = this.get('discourses'); var array = discourses.map(function (d) { return d.get('count'); }); - var oldMin = Math.min(...array), - oldMax = Math.max(...array); - var sum = array.reduce(function(a, b) { return a + b; }); - var average = sum / array.length; - var newMin = Math.floor((average - oldMin)), - newMax = Math.floor((oldMax - average)); var width = self.$().parent().width(); var height = self.$().parent().height() - self.$().siblings().outerHeight(true); - var bubble = d3.layout.pack() - .sort(function comparator(a, b) { return a.value + b.value; }) + //Determine the minimum circle width + var longerStr = _.max( + [].concat(...(discourses.map(function(d) { return d.get('label').split(' ');}))), + function(s) { return s.length; } + ); + var w = this.get('utils').getWidthOfText(longerStr, '11px'); + // we try to take into account the fact that there is at least 2 lines + var minimum_circle_width = Math.max( Math.sqrt((w*w)+Math.pow(11+11/2, 2)) + 10, MINIMUM_CIRCLE_WIDTH); + + // to avoid division by zero. In any case it makes no sense to consider dimensions + // under MINIMUM_CIRCLE_WIDTH + var scaleFactor = minimum_circle_width/Math.max(minimum_circle_width, Math.min(width, height)); + + var min = Math.min(...array), + max = Math.max(...array); + + var scale = d3s.scaleLinear(); + // The range is the range for font sizes + var fontScale = d3s.scaleQuantize().domain([min, max]).range(_.range(10, 14)); + + if((min/max) < scaleFactor) { + scale = scale.domain([min, max]).range([scaleFactor, 1]); + } + + var bubble = d3h.pack() .size([width, height]) - .value(function(d){ - return Math.floor((((d.value - oldMin) * (newMax - newMin)) / (oldMax - oldMin)) + newMin); - }) .padding(10); var element = d3.select('#' + self.get('elementId')) @@ -49,8 +68,10 @@ .style('position','absolute') .style('left', -100000); - var bubbles = bubble.nodes(self.createNodes()); + var root = d3h.hierarchy(self.createNodes()) + .sum(function(d) {return scale(d.value);}); + var bubbles = bubble(root).descendants(); var nodes = element .selectAll() .data(bubbles); @@ -59,20 +80,20 @@ .attr("class", function(d) { return ( d.children ? "category": "item" ) + ( (self.get('filter').get('discourse') !== null && _.contains(self.get('filter').get('discourse'), d.id)) ? " selected" : "" ) ; }); var item = element.selectAll(".item") - .attr("data-id", function(d) { return d.id; }) + .attr("data-id", function(d) { return d.data.id; }) .style("left", function(d) { return ( d.x - d.r) + "px"; }) .style("top", function(d) { return ( d.y - d.r) + "px"; }) .style("width", function(d) { return (d.r * 2) + "px"; }) .style("height", function(d) { return (d.r * 2) + "px"; }) - .style("background-color", function(d) { return d.fill; }) - .style("border-color", function(d) { return d.stroke; }) - .style("font-size", function(d) { return Math.floor((((d.value - oldMin) * (13 - 10)) / (oldMax - oldMin)) + 10) + 'px'; }) + .style("background-color", function(d) { return d.data.fill; }) + .style("border-color", function(d) { return d.data.stroke; }) + .style("font-size", function(d) { return fontScale(d.data.count) + 'px'; }) .on('click', function(d) { - self.get('filter').setFilter('discourse', d.id); + self.get('filter').setFilter('discourse', d.data.id); }); item.append("span") - .html(function(d) { return d.name + ' (' + d.count + ')'; }) + .html(function(d) { return d.data.name + ' (' + d.data.count + ')'; }) .style("margin-left", function() { return ( Ember.$(this).width() > Ember.$(this).parent().width() ? - ( Ember.$(this).width() / 2 ) + ( Ember.$(this).parent().width() / 2 ) : 0 ) + 'px'; }) .style("margin-top", function() { return Ember.$(this).parent().height() / 2 - Ember.$(this).height() / 2 + 'px'; });