Commits

Andy Clayton committed 6158170

Support not overlapping labels in CategoryAxisRenderer

Comments (0)

Files changed (1)

src/plugins/jqplot.categoryAxisRenderer.js

         // axes when creating a plot:
         // > axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer, rendererOptions:{sortMergedLabels:true}}}
         this.sortMergedLabels = false;
+
+        // prop: avoidOverlap
+        // True to skip drawing labels if necessary to avoid overlapping
+        this.avoidOverlap = false;
     };
     
     $.jqplot.CategoryAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
         var xaxis = (this.name == 'xaxis' || this.name == 'x2axis');
         var primaryAxis = (this.name == 'xaxis' || this.name == 'yaxis');
 
+        // the outer size of an element on the dimension parallel to the axis
         var outerSizeParallel = function(o, includeMargin) {
             return xaxis ? o.outerWidth(includeMargin) : o.outerHeight(includeMargin);
         };
         } 
 
         if (this.show) {
+            var lastTick = null;
             for (i=0; i<ticks.length; i++) {
                 var t = ticks[i];
+                // will need to adjust auto positioning based on which axis this is.
+                var temp = primaryAxis ? 1 : -1;
                 if (t.show && t.showLabel) {
-                    var positionOffset;
+                    var positionOffset = 0;
                     var shim = 0;
+                    // projection of text height onto the axis
+                    var textHeightOnAxis = 0;
                     var labelPosition = 'middle';
                     if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
-                        // will need to adjust auto positioning based on which axis this is.
-                        var temp = primaryAxis ? 1 : -1;
-                        var side = (xaxis ? t._textRenderer.height : t._textRenderer.width); 
-                        shim = -temp * side * Math.sin(t._textRenderer.angle) / 2; 
+                        var textHeightProj = xaxis ?  Math.sin(t._textRenderer.angle)
+                                                   : -Math.cos(t._textRenderer.angle);
+                        textHeightOnAxis = t._textRenderer.height * textHeightProj; 
+                        shim = -temp * textHeightOnAxis / 2;
                         labelPosition = t.labelPosition;
                         if (labelPosition == 'auto') {
                             if (t.angle % 90 == 0) {
                                 break;
                         }
                     } else {
-                        positionOffset = -t.getHeight()/2;
                         switch (labelPosition) {
                             case 'end':
+                                positionOffset = 0;
+                                shim *= -1;
+                                break;
                             case 'start':
-                                // nothing
+                                positionOffset = -t.getHeight();
                                 break;
                             case 'middle':
                             default:
+                                positionOffset = -t.getHeight()/2;
                                 shim = 0;
                                 break;
                         }
                     }
-                    var val = this.u2p(t.value) + positionOffset + shim + 'px';
-                    t._elem.css(edge, val);
+
+                    var middle = this.u2p(t.value);
+                    var val = middle + positionOffset + shim;
+
+                    if (this.avoidOverlap) {
+                        if (lastTick != null && lastTick[0] <= middle && middle <= lastTick[1]) {
+                            continue;
+                        }
+                        //
+                        // proj = the projection of the text height onto the axis
+                        // off = the minimum offset for two text boxes not to overlap
+                        //       (though it is limited by the size of the two adjacent
+                        //        boxes on the axis, whichever is greater)
+                        //            
+                        //            |<---off-->|     
+                        //            |<-proj->| |
+                        //  ----AXIS--|--------|-|----
+                        //    text    /`-.     | / <-- two triangles here. well three.
+                        //   height--/--> `-.  |/      but only two we care about. wee.
+                        //          /        `-.
+                        //         /  TEXT    / 
+                        //        /    BOX   /
+                        //
+
+                        // the triangle formed by projecting the text height onto the axis
+                        // (with text height being the hypotenuse) is similar to the triangle that
+                        // is formed by extending the text bottom or top to the axis (with the segment
+                        // of the axis forming the hypotenuse, labeled 'off' in the diagram).
+                        // this could also be done with something like textHeight/cos(angle)
+                        var offset = Math.abs(t._textRenderer.height * (t._textRenderer.height / textHeightOnAxis));
+                        // maxOffset should really take the adjacent label(s) into account
+                        // and use whichever is larger, but for now assume other labels
+                        // are the same size as us.
+                        var maxOffset = (xaxis ? t.getWidth() : t.getHeight());
+                        var offset = Math.min(offset, maxOffset);
+                        lastTick = [middle-offset, middle+offset];
+                    }
+
+                    t._elem.css(edge, val + 'px');
                     t.pack();
                 }
             }