cms/app-client/app/components/discourses-component.js
changeset 467 762fc0eb4946
parent 466 a8effb60ccb6
child 511 fbf4e1afab01
--- 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 + ' <span class="count">(' + d.count + ')</span>'; })
+            .html(function(d) { return d.data.name + ' <span class="count">(' + d.data.count + ')</span>'; })
             .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'; });