]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Improve radial scale (#3625)
authorEvert Timberg <evert.timberg+github@gmail.com>
Fri, 25 Nov 2016 12:19:43 +0000 (07:19 -0500)
committerGitHub <noreply@github.com>
Fri, 25 Nov 2016 12:19:43 +0000 (07:19 -0500)
Clean up radial linear scale. It now supports multiple lines for point labels. Fixes #3225

samples/radar/radar.html
src/scales/scale.radialLinear.js
test/controller.polarArea.tests.js
test/scale.radialLinear.tests.js

index 507d5bb47d72317987cacf54af824f5297e64de5..586e2df7aecbe8ddc368bfd86fe5fde28f909662 100644 (file)
@@ -32,7 +32,7 @@
     var config = {
         type: 'radar',
         data: {
-            labels: ["Eating", "Drinking", "Sleeping", "Designing", "Coding", "Cycling", "Running"],
+            labels: [["Eating", "Dinner"], ["Drinking", "Water"], "Sleeping", ["Designing", "Graphics"], "Coding", "Cycling", "Running"],
             datasets: [{
                 label: "My First dataset",
                 backgroundColor: color(window.chartColors.red).alpha(0.2).rgbString(),
index cc7c6557d7466a51496837e8228cf21c2fc8e2cc..b51fa698bd9e85fe9114543dd9f595477f87bd6f 100644 (file)
@@ -47,10 +47,266 @@ module.exports = function(Chart) {
                }
        };
 
+       function getValueCount(scale) {
+               return !scale.options.lineArc ? scale.chart.data.labels.length : 0;
+       }
+
+       function getPointLabelFontOptions(scale) {
+               var pointLabelOptions = scale.options.pointLabels;
+               var fontSize = helpers.getValueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize);
+               var fontStyle = helpers.getValueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle);
+               var fontFamily = helpers.getValueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily);
+               var font = helpers.fontString(fontSize, fontStyle, fontFamily);
+
+               return {
+                       size: fontSize,
+                       style: fontStyle,
+                       family: fontFamily,
+                       font: font
+               };
+       }
+
+       function measureLabelSize(ctx, fontSize, label) {
+               if (helpers.isArray(label)) {
+                       return {
+                               w: helpers.longestText(ctx, ctx.font, label),
+                               h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize)
+                       };
+               }
+
+               return {
+                       w: ctx.measureText(label).width,
+                       h: fontSize
+               };
+       }
+
+       function determineLimits(angle, pos, size, min, max) {
+               if (angle === min || angle === max) {
+                       return {
+                               start: pos - (size / 2),
+                               end: pos + (size / 2)
+                       };
+               } else if (angle < min || angle > max) {
+                       return {
+                               start: pos - size - 5,
+                               end: pos
+                       };
+               }
+
+               return {
+                       start: pos,
+                       end: pos + size + 5
+               };
+       }
+
+       /**
+        * Helper function to fit a radial linear scale with point labels
+        */
+       function fitWithPointLabels(scale) {
+               /*
+                * Right, this is really confusing and there is a lot of maths going on here
+                * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
+                *
+                * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
+                *
+                * Solution:
+                *
+                * We assume the radius of the polygon is half the size of the canvas at first
+                * at each index we check if the text overlaps.
+                *
+                * Where it does, we store that angle and that index.
+                *
+                * After finding the largest index and angle we calculate how much we need to remove
+                * from the shape radius to move the point inwards by that x.
+                *
+                * We average the left and right distances to get the maximum shape radius that can fit in the box
+                * along with labels.
+                *
+                * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
+                * on each side, removing that from the size, halving it and adding the left x protrusion width.
+                *
+                * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
+                * and position it in the most space efficient manner
+                *
+                * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
+                */
+
+               var plFont = getPointLabelFontOptions(scale);
+
+               // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
+               // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
+               var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
+               var furthestLimits = {
+                       l: scale.width,
+                       r: 0,
+                       t: scale.height,
+                       b: 0
+               };
+               var furthestAngles = {};
+               var i;
+               var textSize;
+               var pointPosition;
+
+               scale.ctx.font = plFont.font;
+               scale._pointLabelSizes = [];
+
+               var valueCount = getValueCount(scale);
+               for (i = 0; i < valueCount; i++) {
+                       pointPosition = scale.getPointPosition(i, largestPossibleRadius);
+                       textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || '');
+                       scale._pointLabelSizes[i] = textSize;
+
+                       // Add quarter circle to make degree 0 mean top of circle
+                       var angleRadians = scale.getIndexAngle(i);
+                       var angle = helpers.toDegrees(angleRadians) % 360;
+                       var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
+                       var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
+
+                       if (hLimits.start < furthestLimits.l) {
+                               furthestLimits.l = hLimits.start;
+                               furthestAngles.l = angleRadians;
+                       }
+
+                       if (hLimits.end > furthestLimits.r) {
+                               furthestLimits.r = hLimits.end;
+                               furthestAngles.r = angleRadians;
+                       }
+
+                       if (vLimits.start < furthestLimits.t) {
+                               furthestLimits.t = vLimits.start;
+                               furthestAngles.t = angleRadians;
+                       }
+
+                       if (vLimits.end > furthestLimits.b) {
+                               furthestLimits.b = vLimits.end;
+                               furthestAngles.b = angleRadians;
+                       }
+               }
+
+               scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles);
+       }
+
+       /**
+        * Helper function to fit a radial linear scale with no point labels
+        */
+       function fit(scale) {
+               var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
+               scale.drawingArea = Math.round(largestPossibleRadius);
+               scale.setCenterPoint(0, 0, 0, 0);
+       }
+
+       function getTextAlignForAngle(angle) {
+               if (angle === 0 || angle === 180) {
+                       return 'center';
+               } else if (angle < 180) {
+                       return 'left';
+               }
+
+               return 'right';
+       }
+
+       function fillText(ctx, text, position, fontSize) {
+               if (helpers.isArray(text)) {
+                       var y = position.y;
+                       var spacing = 1.5 * fontSize;
+
+                       for (var i = 0; i < text.length; ++i) {
+                               ctx.fillText(text[i], position.x, y);
+                               y+= spacing;
+                       }
+               } else {
+                       ctx.fillText(text, position.x, position.y);
+               }
+       }
+
+       function adjustPointPositionForLabelHeight(angle, textSize, position) {
+               if (angle === 90 || angle === 270) {
+                       position.y -= (textSize.h / 2);
+               } else if (angle > 270 || angle < 90) {
+                       position.y -= textSize.h;
+               }
+       }
+
+       function drawPointLabels(scale) {
+               var ctx = scale.ctx;
+               var getValueOrDefault = helpers.getValueOrDefault;
+               var opts = scale.options;
+               var angleLineOpts = opts.angleLines;
+               var pointLabelOpts = opts.pointLabels;
+
+               ctx.lineWidth = angleLineOpts.lineWidth;
+               ctx.strokeStyle = angleLineOpts.color;
+
+               var outerDistance = scale.getDistanceFromCenterForValue(opts.reverse ? scale.min : scale.max);
+
+               // Point Label Font
+               var plFont = getPointLabelFontOptions(scale);
+
+               ctx.textBaseline = 'top';
+
+               for (var i = getValueCount(scale) - 1; i >= 0; i--) {
+                       if (angleLineOpts.display) {
+                               var outerPosition = scale.getPointPosition(i, outerDistance);
+                               ctx.beginPath();
+                               ctx.moveTo(scale.xCenter, scale.yCenter);
+                               ctx.lineTo(outerPosition.x, outerPosition.y);
+                               ctx.stroke();
+                               ctx.closePath();
+                       }
+                       // Extra 3px out for some label spacing
+                       var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5);
+
+                       // Keep this in loop since we may support array properties here
+                       var pointLabelFontColor = getValueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor);
+                       ctx.font = plFont.font;
+                       ctx.fillStyle = pointLabelFontColor;
+
+                       var angleRadians = scale.getIndexAngle(i);
+                       var angle = helpers.toDegrees(angleRadians);
+                       ctx.textAlign = getTextAlignForAngle(angle);
+                       adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
+                       fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size);
+               }
+       }
+
+       function drawRadiusLine(scale, gridLineOpts, radius, index) {
+               var ctx = scale.ctx;
+               ctx.strokeStyle = helpers.getValueAtIndexOrDefault(gridLineOpts.color, index - 1);
+               ctx.lineWidth = helpers.getValueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
+
+               if (scale.options.lineArc) {
+                       // Draw circular arcs between the points
+                       ctx.beginPath();
+                       ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
+                       ctx.closePath();
+                       ctx.stroke();
+               } else {
+                       // Draw straight lines connecting each index
+                       var valueCount = getValueCount(scale);
+
+                       if (valueCount === 0) {
+                               return;
+                       }
+
+                       ctx.beginPath();
+                       var pointPosition = scale.getPointPosition(0, radius);
+                       ctx.moveTo(pointPosition.x, pointPosition.y);
+
+                       for (var i = 1; i < valueCount; i++) {
+                               pointPosition = scale.getPointPosition(i, radius);
+                               ctx.lineTo(pointPosition.x, pointPosition.y);
+                       }
+
+                       ctx.closePath();
+                       ctx.stroke();
+               }
+       }
+
+       function numberOrZero(param) {
+               return helpers.isNumber(param) ? param : 0;
+       }
+
        var LinearRadialScale = Chart.LinearScaleBase.extend({
-               getValueCount: function() {
-                       return this.chart.data.labels.length;
-               },
                setDimensions: function() {
                        var me = this;
                        var opts = me.options;
@@ -68,9 +324,8 @@ module.exports = function(Chart) {
                determineDataLimits: function() {
                        var me = this;
                        var chart = me.chart;
-                       me.min = null;
-                       me.max = null;
-
+                       var min = Number.POSITIVE_INFINITY;
+                       var max = Number.NEGATIVE_INFINITY;
 
                        helpers.each(chart.data.datasets, function(dataset, datasetIndex) {
                                if (chart.isDatasetVisible(datasetIndex)) {
@@ -82,21 +337,15 @@ module.exports = function(Chart) {
                                                        return;
                                                }
 
-                                               if (me.min === null) {
-                                                       me.min = value;
-                                               } else if (value < me.min) {
-                                                       me.min = value;
-                                               }
-
-                                               if (me.max === null) {
-                                                       me.max = value;
-                                               } else if (value > me.max) {
-                                                       me.max = value;
-                                               }
+                                               min = Math.min(value, min);
+                                               max = Math.max(value, max);
                                        });
                                }
                        });
 
+                       me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
+                       me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);
+
                        // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
                        me.handleTickRangeOptions();
                },
@@ -116,122 +365,46 @@ module.exports = function(Chart) {
                        return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
                },
                fit: function() {
-                       /*
-                        * Right, this is really confusing and there is a lot of maths going on here
-                        * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
-                        *
-                        * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
-                        *
-                        * Solution:
-                        *
-                        * We assume the radius of the polygon is half the size of the canvas at first
-                        * at each index we check if the text overlaps.
-                        *
-                        * Where it does, we store that angle and that index.
-                        *
-                        * After finding the largest index and angle we calculate how much we need to remove
-                        * from the shape radius to move the point inwards by that x.
-                        *
-                        * We average the left and right distances to get the maximum shape radius that can fit in the box
-                        * along with labels.
-                        *
-                        * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
-                        * on each side, removing that from the size, halving it and adding the left x protrusion width.
-                        *
-                        * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
-                        * and position it in the most space efficient manner
-                        *
-                        * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
-                        */
-
-                       var pointLabels = this.options.pointLabels;
-                       var pointLabelFontSize = helpers.getValueOrDefault(pointLabels.fontSize, globalDefaults.defaultFontSize);
-                       var pointLabeFontStyle = helpers.getValueOrDefault(pointLabels.fontStyle, globalDefaults.defaultFontStyle);
-                       var pointLabeFontFamily = helpers.getValueOrDefault(pointLabels.fontFamily, globalDefaults.defaultFontFamily);
-                       var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily);
-
-                       // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
-                       // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
-                       var largestPossibleRadius = helpers.min([(this.height / 2 - pointLabelFontSize - 5), this.width / 2]),
-                               pointPosition,
-                               i,
-                               textWidth,
-                               halfTextWidth,
-                               furthestRight = this.width,
-                               furthestRightIndex,
-                               furthestRightAngle,
-                               furthestLeft = 0,
-                               furthestLeftIndex,
-                               furthestLeftAngle,
-                               xProtrusionLeft,
-                               xProtrusionRight,
-                               radiusReductionRight,
-                               radiusReductionLeft;
-                       this.ctx.font = pointLabeFont;
-
-                       for (i = 0; i < this.getValueCount(); i++) {
-                               // 5px to space the text slightly out - similar to what we do in the draw function.
-                               pointPosition = this.getPointPosition(i, largestPossibleRadius);
-                               textWidth = this.ctx.measureText(this.pointLabels[i] ? this.pointLabels[i] : '').width + 5;
-
-                               // Add quarter circle to make degree 0 mean top of circle
-                               var angleRadians = this.getIndexAngle(i) + (Math.PI / 2);
-                               var angle = (angleRadians * 360 / (2 * Math.PI)) % 360;
-
-                               if (angle === 0 || angle === 180) {
-                                       // At angle 0 and 180, we're at exactly the top/bottom
-                                       // of the radar chart, so text will be aligned centrally, so we'll half it and compare
-                                       // w/left and right text sizes
-                                       halfTextWidth = textWidth / 2;
-                                       if (pointPosition.x + halfTextWidth > furthestRight) {
-                                               furthestRight = pointPosition.x + halfTextWidth;
-                                               furthestRightIndex = i;
-                                       }
-                                       if (pointPosition.x - halfTextWidth < furthestLeft) {
-                                               furthestLeft = pointPosition.x - halfTextWidth;
-                                               furthestLeftIndex = i;
-                                       }
-                               } else if (angle < 180) {
-                                       // Less than half the values means we'll left align the text
-                                       if (pointPosition.x + textWidth > furthestRight) {
-                                               furthestRight = pointPosition.x + textWidth;
-                                               furthestRightIndex = i;
-                                       }
-                               // More than half the values means we'll right align the text
-                               } else if (pointPosition.x - textWidth < furthestLeft) {
-                                       furthestLeft = pointPosition.x - textWidth;
-                                       furthestLeftIndex = i;
-                               }
+                       if (this.options.lineArc) {
+                               fit(this);
+                       } else {
+                               fitWithPointLabels(this);
                        }
-
-                       xProtrusionLeft = furthestLeft;
-                       xProtrusionRight = Math.ceil(furthestRight - this.width);
-
-                       furthestRightAngle = this.getIndexAngle(furthestRightIndex);
-                       furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
-
-                       radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2);
-                       radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2);
-
-                       // Ensure we actually need to reduce the size of the chart
-                       radiusReductionRight = (helpers.isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
-                       radiusReductionLeft = (helpers.isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
-
-                       this.drawingArea = Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2);
-                       this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
                },
-               setCenterPoint: function(leftMovement, rightMovement) {
+               /**
+                * Set radius reductions and determine new radius and center point
+                * @private
+                */
+               setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {
+                       var me = this;
+                       var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
+                       var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
+                       var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
+                       var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b);
+
+                       radiusReductionLeft = numberOrZero(radiusReductionLeft);
+                       radiusReductionRight = numberOrZero(radiusReductionRight);
+                       radiusReductionTop = numberOrZero(radiusReductionTop);
+                       radiusReductionBottom = numberOrZero(radiusReductionBottom);
+
+                       me.drawingArea = Math.min(
+                               Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
+                               Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
+                       me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
+               },
+               setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
                        var me = this;
                        var maxRight = me.width - rightMovement - me.drawingArea,
-                               maxLeft = leftMovement + me.drawingArea;
+                               maxLeft = leftMovement + me.drawingArea,
+                               maxTop = topMovement + me.drawingArea,
+                               maxBottom = me.height - bottomMovement - me.drawingArea;
 
                        me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left);
-                       // Always vertically in the centre as the text height doesn't change
-                       me.yCenter = Math.round((me.height / 2) + me.top);
+                       me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top);
                },
 
                getIndexAngle: function(index) {
-                       var angleMultiplier = (Math.PI * 2) / this.getValueCount();
+                       var angleMultiplier = (Math.PI * 2) / getValueCount(this);
                        var startAngle = this.chart.options && this.chart.options.startAngle ?
                                this.chart.options.startAngle :
                                0;
@@ -239,7 +412,7 @@ module.exports = function(Chart) {
                        var startAngleRadians = startAngle * Math.PI * 2 / 360;
 
                        // Start from the top instead of right, so remove a quarter of the circle
-                       return index * angleMultiplier - (Math.PI / 2) + startAngleRadians;
+                       return index * angleMultiplier + startAngleRadians;
                },
                getDistanceFromCenterForValue: function(value) {
                        var me = this;
@@ -257,7 +430,7 @@ module.exports = function(Chart) {
                },
                getPointPosition: function(index, distanceFromCenter) {
                        var me = this;
-                       var thisAngle = me.getIndexAngle(index);
+                       var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
                        return {
                                x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter,
                                y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter
@@ -284,8 +457,6 @@ module.exports = function(Chart) {
                        var opts = me.options;
                        var gridLineOpts = opts.gridLines;
                        var tickOpts = opts.ticks;
-                       var angleLineOpts = opts.angleLines;
-                       var pointLabelOpts = opts.pointLabels;
                        var getValueOrDefault = helpers.getValueOrDefault;
 
                        if (opts.display) {
@@ -305,29 +476,7 @@ module.exports = function(Chart) {
 
                                                // Draw circular lines around the scale
                                                if (gridLineOpts.display && index !== 0) {
-                                                       ctx.strokeStyle = helpers.getValueAtIndexOrDefault(gridLineOpts.color, index - 1);
-                                                       ctx.lineWidth = helpers.getValueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
-
-                                                       if (opts.lineArc) {
-                                                               // Draw circular arcs between the points
-                                                               ctx.beginPath();
-                                                               ctx.arc(me.xCenter, me.yCenter, yCenterOffset, 0, Math.PI * 2);
-                                                               ctx.closePath();
-                                                               ctx.stroke();
-                                                       } else {
-                                                               // Draw straight lines connecting each index
-                                                               ctx.beginPath();
-                                                               for (var i = 0; i < me.getValueCount(); i++) {
-                                                                       var pointPosition = me.getPointPosition(i, yCenterOffset);
-                                                                       if (i === 0) {
-                                                                               ctx.moveTo(pointPosition.x, pointPosition.y);
-                                                                       } else {
-                                                                               ctx.lineTo(pointPosition.x, pointPosition.y);
-                                                                       }
-                                                               }
-                                                               ctx.closePath();
-                                                               ctx.stroke();
-                                                       }
+                                                       drawRadiusLine(me, gridLineOpts, yCenterOffset, index);
                                                }
 
                                                if (tickOpts.display) {
@@ -354,59 +503,7 @@ module.exports = function(Chart) {
                                });
 
                                if (!opts.lineArc) {
-                                       ctx.lineWidth = angleLineOpts.lineWidth;
-                                       ctx.strokeStyle = angleLineOpts.color;
-
-                                       var outerDistance = me.getDistanceFromCenterForValue(opts.reverse ? me.min : me.max);
-
-                                       // Point Label Font
-                                       var pointLabelFontSize = getValueOrDefault(pointLabelOpts.fontSize, globalDefaults.defaultFontSize);
-                                       var pointLabeFontStyle = getValueOrDefault(pointLabelOpts.fontStyle, globalDefaults.defaultFontStyle);
-                                       var pointLabeFontFamily = getValueOrDefault(pointLabelOpts.fontFamily, globalDefaults.defaultFontFamily);
-                                       var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily);
-
-                                       for (var i = me.getValueCount() - 1; i >= 0; i--) {
-                                               if (angleLineOpts.display) {
-                                                       var outerPosition = me.getPointPosition(i, outerDistance);
-                                                       ctx.beginPath();
-                                                       ctx.moveTo(me.xCenter, me.yCenter);
-                                                       ctx.lineTo(outerPosition.x, outerPosition.y);
-                                                       ctx.stroke();
-                                                       ctx.closePath();
-                                               }
-                                               // Extra 3px out for some label spacing
-                                               var pointLabelPosition = me.getPointPosition(i, outerDistance + 5);
-
-                                               // Keep this in loop since we may support array properties here
-                                               var pointLabelFontColor = getValueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor);
-                                               ctx.font = pointLabeFont;
-                                               ctx.fillStyle = pointLabelFontColor;
-
-                                               var pointLabels = me.pointLabels;
-
-                                               // Add quarter circle to make degree 0 mean top of circle
-                                               var angleRadians = this.getIndexAngle(i) + (Math.PI / 2);
-                                               var angle = (angleRadians * 360 / (2 * Math.PI)) % 360;
-
-                                               if (angle === 0 || angle === 180) {
-                                                       ctx.textAlign = 'center';
-                                               } else if (angle < 180) {
-                                                       ctx.textAlign = 'left';
-                                               } else {
-                                                       ctx.textAlign = 'right';
-                                               }
-
-                                               // Set the correct text baseline based on outer positioning
-                                               if (angle === 90 || angle === 270) {
-                                                       ctx.textBaseline = 'middle';
-                                               } else if (angle > 270 || angle < 90) {
-                                                       ctx.textBaseline = 'bottom';
-                                               } else {
-                                                       ctx.textBaseline = 'top';
-                                               }
-
-                                               ctx.fillText(pointLabels[i] ? pointLabels[i] : '', pointLabelPosition.x, pointLabelPosition.y);
-                                       }
+                                       drawPointLabels(me);
                                }
                        }
                }
index 3bd95eb231ed925b5346de9bf463a3be4adc037d..de7d4acfaaceaadf88168b387eecd3166320bc8f 100644 (file)
@@ -96,9 +96,9 @@ describe('Polar area controller tests', function() {
                expect(meta.data.length).toBe(4);
 
                [
-                       {o: 156, s: -0.5 * Math.PI, e: 0},
-                       {o: 211, s: 0, e: 0.5 * Math.PI},
-                       {o: 45, s: 0.5 * Math.PI, e: Math.PI},
+                       {o: 168, s: -0.5 * Math.PI, e: 0},
+                       {o: 228, s: 0, e: 0.5 * Math.PI},
+                       {o: 48, s: 0.5 * Math.PI, e: Math.PI},
                        {o: 0, s: Math.PI, e: 1.5 * Math.PI}
                ].forEach(function(expected, i) {
                        expect(meta.data[i]._model.x).toBeCloseToPixel(256);
@@ -140,7 +140,7 @@ describe('Polar area controller tests', function() {
                expect(meta.data[0]._model.x).toBeCloseToPixel(256);
                expect(meta.data[0]._model.y).toBeCloseToPixel(272);
                expect(meta.data[0]._model.innerRadius).toBeCloseToPixel(0);
-               expect(meta.data[0]._model.outerRadius).toBeCloseToPixel(156);
+               expect(meta.data[0]._model.outerRadius).toBeCloseToPixel(168);
                expect(meta.data[0]._model).toEqual(jasmine.objectContaining({
                        startAngle: -0.5 * Math.PI,
                        endAngle: 0,
@@ -178,9 +178,9 @@ describe('Polar area controller tests', function() {
                expect(meta.data.length).toBe(4);
 
                [
-                       {o: 156, s: 0, e: 0.5 * Math.PI},
-                       {o: 211, s: 0.5 * Math.PI, e: Math.PI},
-                       {o: 45, s: Math.PI, e: 1.5 * Math.PI},
+                       {o: 168, s: 0, e: 0.5 * Math.PI},
+                       {o: 228, s: 0.5 * Math.PI, e: Math.PI},
+                       {o: 48, s: Math.PI, e: 1.5 * Math.PI},
                        {o: 0, s: 1.5 * Math.PI, e: 2.0 * Math.PI}
                ].forEach(function(expected, i) {
                        expect(meta.data[i]._model.x).toBeCloseToPixel(256);
index b4004f1bafef8bac982a4cc6058e9e4a8f7f0c6b..69d6c167f92b610ba0a76c2492f8f22be8e6a645 100644 (file)
@@ -342,9 +342,9 @@ describe('Test the radial linear scale', function() {
                        }
                });
 
-               expect(chart.scale.drawingArea).toBe(225);
-               expect(chart.scale.xCenter).toBe(256);
-               expect(chart.scale.yCenter).toBe(272);
+               expect(chart.scale.drawingArea).toBe(233);
+               expect(chart.scale.xCenter).toBe(247);
+               expect(chart.scale.yCenter).toBe(280);
        });
 
        it('should correctly get the label for a given data index', function() {
@@ -390,16 +390,16 @@ describe('Test the radial linear scale', function() {
                });
 
                expect(chart.scale.getDistanceFromCenterForValue(chart.scale.min)).toBe(0);
-               expect(chart.scale.getDistanceFromCenterForValue(chart.scale.max)).toBe(225);
+               expect(chart.scale.getDistanceFromCenterForValue(chart.scale.max)).toBe(233);
                expect(chart.scale.getPointPositionForValue(1, 5)).toEqual({
-                       x: 269,
-                       y: 268,
+                       x: 261,
+                       y: 275,
                });
 
                chart.scale.options.reverse = true;
                chart.update();
 
-               expect(chart.scale.getDistanceFromCenterForValue(chart.scale.min)).toBe(225);
+               expect(chart.scale.getDistanceFromCenterForValue(chart.scale.min)).toBe(233);
                expect(chart.scale.getDistanceFromCenterForValue(chart.scale.max)).toBe(0);
        });
 
@@ -431,14 +431,14 @@ describe('Test the radial linear scale', function() {
                var slice = 72; // (360 / 5)
 
                for (var i = 0; i < 5; i++) {
-                       expect(radToNearestDegree(chart.scale.getIndexAngle(i))).toBe(15 + (slice * i) - 90);
+                       expect(radToNearestDegree(chart.scale.getIndexAngle(i))).toBe(15 + (slice * i));
                }
 
                chart.options.startAngle = 0;
                chart.update();
 
                for (var x = 0; x < 5; x++) {
-                       expect(radToNearestDegree(chart.scale.getIndexAngle(x))).toBe((slice * x) - 90);
+                       expect(radToNearestDegree(chart.scale.getIndexAngle(x))).toBe((slice * x));
                }
        });
 });