Эх сурвалжийг харах

HUE-279. Various improvements to HueChart
Add handling for current offset parent in scrolled containter in pv.Mark.prototype.mouse
Add event bar and vector calculations to manually determine and fire wedgeOver/Out events.
Add getNormalizedForField function to HueChart.Data
Re-working event handling in HueChart.Box
Response to Nutron's review comments

Marcus McLaughlin 15 жил өмнө
parent
commit
bcd913075f

+ 20 - 9
desktop/core/static/js/Source/CCS/HueChart.Box.js

@@ -80,7 +80,7 @@ HueChart.Box = new Class({
                         //Add position indicator if enabled
                         if (this.options.positionIndicator) this.setPositionIndicator(vis);
                         //If there's an event, add the event bar to capture these events.
-                        if (this.$events.pointMouseOut && this.$events.pointMouseOver) this.addEventBar(vis);
+                        if (this.$events.pointMouseOut && this.$events.pointMouseOver || this.$events.pointClick) this.addEventBar(vis);
                 }.bind(this));
         },
         
@@ -192,28 +192,39 @@ HueChart.Box = new Class({
         addEventBar: function(vis) {
                 //Create functions which handle the graph specific aspects of the event and call the event arguments.
                 var outVisFn = function() {
-                        this.selected_index = -1;
                         this.fireEvent('pointMouseOut');
                         return vis;
                 }.bind(this);
-                var moveVisFn = function() {
+                var getDataIndexFromMouse = function() {
                         //Convert the mouse's xValue into its corresponding data value on the xScale. 
                         var mx = this.xScale.invert(vis.mouse().x);
                         //Search the data for the index of the element at this data value.
-                        i = pv.search(this.data.getObjects().map(function(d){ return d[this.options.xField]; }.bind(this)), Math.round(mx));
+                        var i = pv.search(this.data.getObjects().map(function(d){ return d[this.options.xField]; }.bind(this)), Math.round(mx));
                         //Adjust for ProtoVis search
                         i = i < 0 ? (-i - 2) : i;
-                        //Set selected index and run moveFn if the item exists
-                        if(i >= 0 && i < this.data.getLength()) {
-                                this.selected_index = i;
-                                this.fireEvent('pointMouseOver', this.data.getObjects()[this.selected_index]);
+                        return (i >= 0 && i < this.data.getLength() ? i : null);
+                }.bind(this);
+                var moveVisFn = function() {
+                        var dataIndex = getDataIndexFromMouse();
+                        //Fire pointMouseOver if the item exists
+                        if(dataIndex) {
+                                this.fireEvent('pointMouseOver', [this.data.getObjects()[dataIndex], dataIndex]);
+                        }
+                        return vis;
+                }.bind(this);
+                var clickFn = function() {
+                        var dataIndex = getDataIndexFromMouse();
+                        //Fire pointClick if the item exists
+                        if(dataIndex != null) {
+                                this.fireEvent('pointClick', [ this.data.getObjects()[dataIndex], dataIndex ]);
                         }
                         return vis;
                 }.bind(this);
                 vis.add(pv.Bar)
                         .fillStyle("rgba(0,0,0,.001)")
                         .event("mouseout", outVisFn)
-                        .event("mousemove", moveVisFn);
+                        .event("mousemove", moveVisFn)
+                        .event("click", clickFn);
         }
         
 });

+ 95 - 8
desktop/core/static/js/Source/CCS/HueChart.Circle.js

@@ -24,7 +24,8 @@ HueChart.Circle = new Class({
         Extends: HueChart,
 
         options: {
-                radius: null// the radius of the chart, (will default to width/2)
+                selectedColor: '#fff'
+                //radius: null the radius of the chart, (will default to width/2)
                 //graphField: '' // the field in the data object that should be graphed,
                 /*
                 onWedgeOver: function to be executed when the mouse is moved over a wedge of the chart, 
@@ -43,34 +44,120 @@ HueChart.Circle = new Class({
                 this.radius = this.options.radius || this.width/2;
                 this.addEvent('setupChart', function(vis) {
                         this.addGraph(vis);
+                        this.addEventBar(vis);
                 });
         },
 
         addGraph: function(vis) {
-                //Hm...should this happen here or somewhere else ?
                 var valueSum = this.data.getSeriesSum(this.options.graphField);
                 //Put selected index, color array, and hue chart in scope
                 var get_selected_index = this.getSelectedIndex.bind(this);
                 var colorArray = this.options.colorArray;
                 var hueChart = this;
                 vis.add(pv.Wedge)
+                        //Data is the array of contained within the HueChart.Data object.
                         .data(this.data.getObjects())
+                        //Bottom of the wedge space is the radius
                         .bottom(this.radius)
+                        //Left of the wedge space is the radius
                         .left(this.radius)
+                        //The outer radius is the radius
                         .outerRadius(this.radius)
+                        //The angle is a normalized value summing to 2PI based on the values in the graphField. 
                         .angle(function(d) { 
                                 return (d[this.options.graphField]/valueSum) * 2 * Math.PI; 
                         }.bind(this))
+                        //Fill with white if selected, otherwise use corresponding value in colorArray
                         .fillStyle(function() {
-                                return this.index == get_selected_index() ? '#fff' : colorArray[this.index % colorArray.length];  
-                        }).event("mouseover", function() {
-                                hueChart.fireEvent('wedgeOver', this.index);
-                        }).event("mouseout", function() {
-                                hueChart.fireEvent('wedgeOut');
+                                return this.index == get_selected_index() ? hueChart.options.selectedColor : colorArray[this.index % colorArray.length];
                         }).event("click", function(d) {
-                                hueChart.fireEvent('wedgeClick', d);
+                                hueChart.fireEvent('wedgeClick', [d, this.index]);
                         });                
         },
+        
+        //Adds an event processing bar on top of the panel, which is used to appropriately fire wedgeOut/Over events
+        addEventBar: function(vis) {
+                //Base vector is vector pointing straight up.
+                var baseVector = {x: 0, y: this.radius};
+                //Shortcut to data array
+                var dataArray = this.data.getObjects();
+                //Calculate sum of graph values
+                var valueSum = this.data.getSeriesSum(this.options.graphField);
+                //Shortcut to graphField
+                var graphField = this.options.graphField;
+                //Add an invisible bar to catch events
+                vis.add(pv.Bar)
+                        //Left of bar at 0
+                        .left(0)
+                        //Bottom of bar at 0
+                        .bottom(0)
+                        //Width is width of panel
+                        .width(this.width)
+                        //Height is height of panel
+                        .height(this.height)
+                        //Fillstyle is white with a very very small (negligible, even) opacity
+                        .fillStyle("rgba(0,0,0,.001)")
+                        .event("mousemove", function() {
+                                //baseVector.y is equal to radius
+                                var curPoint = vis.mouse();
+                                //Conversion from mouse points to vectors where mouse point (radius, radius) is equal to vector (0, 0) (origin)
+                                //xPoint -> xVector: xVector = xPoint - radius;
+                                var curVector = {};
+                                curVector.x = curPoint.x - baseVector.y; 
+                                //yPoint -> yVector: yVector = (yPoint - radius) * -1;
+                                curVector.y = (curPoint.y - baseVector.y) * -1;
+                                //Length of vector - sqrt(x^2 + y^2)
+                                var vectorLength = Math.sqrt(Math.pow(curVector.x, 2) + Math.pow(curVector.y, 2));
+                                //Angle of a vector 2 relative to vector 1 = atan2(v2.y, v2.x) - atan2(v1.y, v1.x)
+                                //In radians
+                                //Quadrants 1 to 3 - negative numbers ascending clockwise from vertical.  
+                                //Quadrant 4 - positive number ascending counter-clockwise from vertical. 
+                                var angle = Math.atan2(curVector.y, curVector.x) - Math.atan2(baseVector.y, baseVector.x);
+                                //Convert angle to positive number ascending clockwise from vertical.
+                                if (angle < 0) {
+                                         //In quadrant 1 to 3 - convert to a positive number.
+                                         angle = -angle;
+                                } else {
+                                         //In quadrant 4 - subtract angle from Math.PI/2 to find positive amount from 3pi/2, add 3pi/2.  
+                                         angle = (Math.PI/2 - angle) + (3 * Math.PI/2);
+                                }
+                                var nextAngle = 0;
+                                //Create array of angle sums.
+                                var angleSums = [];
+                                for (var i = 0; i < dataArray.length; i++) {
+                                        //Calculate angle -- previousAngle (angleSums[i-1] plus the latest angle. 
+                                        newAngle = (angleSums[i -1] || 0) + ((dataArray[i][graphField]/valueSum) * 2 * Math.PI); 
+                                        angleSums.push(newAngle);
+                                }
+
+                                //Fire wedgeOver event if the current angle is greater than the sum of all prior angles but less than that sum plus the next angle.
+                                //If the vector is shorter than the radius (meaning it's within the circle), search for a wedge, otherwise, fire wedge out
+                                if (vectorLength < this.radius) {
+                                        for (i = 0; i < dataArray.length; i++) {
+                                                lastAngle = angleSums[i -1] || 0;
+                                                if (angleSums[i] > angle && angle > lastAngle) {
+                                                        //Don't do anything if this wedge is already selected.
+                                                        if (i != this.getSelectedIndex()) {
+                                                                //Fire wedgeOut for the previously selected wedge
+                                                                if (this.getSelectedIndex() != -1) {
+                                                                        this.fireEvent('wedgeOut', [ dataArray[this.getSelectedIndex()], this.getSelectedIndex()]);
+                                                                }
+                                                                //Fire wedgeOver for the newly selected wedge
+                                                                this.fireEvent('wedgeOver', [ dataArray[i], i]);
+                                                        }
+                                                } 
+                                        }
+                                } else if(this.getSelectedIndex() != -1) {
+                                        this.fireEvent('wedgeOut', [ dataArray[this.getSelectedIndex()], this.getSelectedIndex()]);
+                                }
+                        }.bind(this))
+                        //Fire wedgeOut when we mouse out of the panel, since clearly we can no longer be over a wedge within the panel;.
+                        .event("mouseout", function() {
+                                if (this.getSelectedIndex() != -1) {
+                                        this.fireEvent('wedgeOut', [ dataArray[this.getSelectedIndex() ], this.getSelectedIndex()]);
+                                }
+                        }.bind(this));
+        },
 
         highlightWedge: function(sliceIndex) {
                 this.setSelectedIndex(sliceIndex);

+ 8 - 0
desktop/core/static/js/Source/CCS/HueChart.js

@@ -236,6 +236,14 @@ HueChart.Data = new Class({
                 return this.dataObjects;
         },
 
+        //Uses the protovis pv.normalize function to return an array containing values which sum to 1
+        //Essentially returns an array containing the percentage that each dataObject's field value
+        //contributes to the sum of the dataObjects' array field values.
+
+        getNormalizedForField: function(field) {
+                pv.normalize(this.dataObjects, function(d) { return d[field]; });        
+        },
+
         getLength: function() {
                 return this.dataObjects.length;
         },

+ 6 - 3
ext/thirdparty/js/protovis/protovis-d3.2.js

@@ -7914,7 +7914,7 @@ pv.Mark.prototype.mouse = function() {
   var x = pv.event.pageX || 0,
       y = pv.event.pageY || 0,
       n = this.root.canvas();
-
+  
   // Calculate pageX/Y if missing and clientX/Y available
   /*
         CLOUDERA CHANGE:
@@ -7926,9 +7926,12 @@ pv.Mark.prototype.mouse = function() {
     y = pv.event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
   }
 
+ 
   do {
-    x -= n.offsetLeft;
-    y -= n.offsetTop;
+    /*  CLOUDERA CHANGE */
+    //Accomodate for the possibility that the current offset parent (n) is in a scrolled object.
+        x -= (n.offsetLeft - n.scrollLeft);
+        y -= (n.offsetTop - n.scrollTop);
   } while (n = n.offsetParent);
 
   /* Compute the inverse transform of all enclosing panels. */