]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Doughnut / PolarArea cleanup (#7113)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Mon, 17 Feb 2020 23:45:30 +0000 (01:45 +0200)
committerGitHub <noreply@github.com>
Mon, 17 Feb 2020 23:45:30 +0000 (18:45 -0500)
Doughnut / PolarArea cleanup

docs/getting-started/v3-migration.md
src/controllers/controller.doughnut.js
src/controllers/controller.polarArea.js
test/specs/controller.doughnut.tests.js

index 0aee422ab90cf3cefcbbaee09c08aefae52aa575..9911fc46520227b863004f420c554a31336d198b 100644 (file)
@@ -86,10 +86,16 @@ Animation system was completely rewritten in Chart.js v3. Each property can now
 
 ### Removed
 
+* `Chart.borderWidth`
 * `Chart.chart.chart`
 * `Chart.Controller`
+* `Chart.innerRadius`
+* `Chart.offsetX`
+* `Chart.offsetY`
+* `Chart.outerRadius`
 * `Chart.prototype.generateLegend`
 * `Chart.platform`. It only contained `disableCSSInjection`. CSS is never injected in v3.
+* `Chart.radiusLength`
 * `Chart.types`
 * `Chart.Tooltip` is now provided by the tooltip plugin. The positioners can be accessed from `tooltipPlugin.positioners`
 * `DatasetController.addElementAndReset`
index 88ec56975e01468c5c1a83c9a173450b03ca8a53..1d987adbffaf17ea3ac71f06833cc02f899fb931 100644 (file)
@@ -3,6 +3,10 @@ import defaults from '../core/core.defaults';
 import Arc from '../elements/element.arc';
 import {isArray, valueOrDefault} from '../helpers/helpers.core';
 
+/**
+ * @typedef { import("../core/core.controller").default } Chart
+ */
+
 const PI = Math.PI;
 const DOUBLE_PI = PI * 2;
 const HALF_PI = PI / 2;
@@ -94,6 +98,36 @@ defaults.set('doughnut', {
        }
 });
 
+function getRatioAndOffset(rotation, circumference, cutout) {
+       let ratioX = 1;
+       let ratioY = 1;
+       let offsetX = 0;
+       let offsetY = 0;
+       // If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc
+       if (circumference < DOUBLE_PI) {
+               let startAngle = rotation % DOUBLE_PI;
+               startAngle += startAngle >= PI ? -DOUBLE_PI : startAngle < -PI ? DOUBLE_PI : 0;
+               const endAngle = startAngle + circumference;
+               const startX = Math.cos(startAngle);
+               const startY = Math.sin(startAngle);
+               const endX = Math.cos(endAngle);
+               const endY = Math.sin(endAngle);
+               const contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI;
+               const contains90 = (startAngle <= HALF_PI && endAngle >= HALF_PI) || endAngle >= DOUBLE_PI + HALF_PI;
+               const contains180 = startAngle === -PI || endAngle >= PI;
+               const contains270 = (startAngle <= -HALF_PI && endAngle >= -HALF_PI) || endAngle >= PI + HALF_PI;
+               const minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout);
+               const minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout);
+               const maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout);
+               const maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout);
+               ratioX = (maxX - minX) / 2;
+               ratioY = (maxY - minY) / 2;
+               offsetX = -(maxX + minX) / 2;
+               offsetY = -(maxY + minY) / 2;
+       }
+       return {ratioX, ratioY, offsetX, offsetY};
+}
+
 class DoughnutController extends DatasetController {
 
        constructor(chart, datasetIndex) {
@@ -101,6 +135,8 @@ class DoughnutController extends DatasetController {
 
                this.innerRadius = undefined;
                this.outerRadius = undefined;
+               this.offsetX = undefined;
+               this.offsetY = undefined;
        }
 
        linkScales() {}
@@ -131,61 +167,31 @@ class DoughnutController extends DatasetController {
                return ringIndex;
        }
 
+       /**
+        * @param {string} mode
+        */
        update(mode) {
                const me = this;
                const chart = me.chart;
-               const chartArea = chart.chartArea;
-               const opts = chart.options;
-               let ratioX = 1;
-               let ratioY = 1;
-               let offsetX = 0;
-               let offsetY = 0;
+               const {chartArea, options} = chart;
                const meta = me._cachedMeta;
                const arcs = meta.data;
-               const cutout = opts.cutoutPercentage / 100 || 0;
-               const circumference = opts.circumference;
+               const cutout = options.cutoutPercentage / 100 || 0;
                const chartWeight = me._getRingWeight(me.index);
-
-               // If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc
-               if (circumference < DOUBLE_PI) {
-                       let startAngle = opts.rotation % DOUBLE_PI;
-                       startAngle += startAngle >= PI ? -DOUBLE_PI : startAngle < -PI ? DOUBLE_PI : 0;
-                       const endAngle = startAngle + circumference;
-                       const startX = Math.cos(startAngle);
-                       const startY = Math.sin(startAngle);
-                       const endX = Math.cos(endAngle);
-                       const endY = Math.sin(endAngle);
-                       const contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI;
-                       const contains90 = (startAngle <= HALF_PI && endAngle >= HALF_PI) || endAngle >= DOUBLE_PI + HALF_PI;
-                       const contains180 = startAngle === -PI || endAngle >= PI;
-                       const contains270 = (startAngle <= -HALF_PI && endAngle >= -HALF_PI) || endAngle >= PI + HALF_PI;
-                       const minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout);
-                       const minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout);
-                       const maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout);
-                       const maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout);
-                       ratioX = (maxX - minX) / 2;
-                       ratioY = (maxY - minY) / 2;
-                       offsetX = -(maxX + minX) / 2;
-                       offsetY = -(maxY + minY) / 2;
-               }
-
-               for (let i = 0, ilen = arcs.length; i < ilen; ++i) {
-                       arcs[i]._options = me._resolveDataElementOptions(i, mode);
-               }
-
-               chart.borderWidth = me.getMaxBorderWidth();
-               const maxWidth = (chartArea.right - chartArea.left - chart.borderWidth) / ratioX;
-               const maxHeight = (chartArea.bottom - chartArea.top - chart.borderWidth) / ratioY;
-               chart.outerRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
-               chart.innerRadius = Math.max(chart.outerRadius * cutout, 0);
-               chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1);
-               chart.offsetX = offsetX * chart.outerRadius;
-               chart.offsetY = offsetY * chart.outerRadius;
+               const {ratioX, ratioY, offsetX, offsetY} = getRatioAndOffset(options.rotation, options.circumference, cutout);
+               const borderWidth = me.getMaxBorderWidth();
+               const maxWidth = (chartArea.right - chartArea.left - borderWidth) / ratioX;
+               const maxHeight = (chartArea.bottom - chartArea.top - borderWidth) / ratioY;
+               const outerRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
+               const innerRadius = Math.max(outerRadius * cutout, 0);
+               const radiusLength = (outerRadius - innerRadius) / me._getVisibleDatasetWeightTotal();
+               me.offsetX = offsetX * outerRadius;
+               me.offsetY = offsetY * outerRadius;
 
                meta.total = me.calculateTotal();
 
-               me.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index);
-               me.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0);
+               me.outerRadius = outerRadius - radiusLength * me._getRingWeightOffset(me.index);
+               me.innerRadius = Math.max(me.outerRadius - radiusLength * chartWeight, 0);
 
                me.updateElements(arcs, 0, mode);
        }
@@ -209,8 +215,12 @@ class DoughnutController extends DatasetController {
                const animationOpts = opts.animation;
                const centerX = (chartArea.left + chartArea.right) / 2;
                const centerY = (chartArea.top + chartArea.bottom) / 2;
-               const innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
-               const outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
+               const animateScale = reset && animationOpts.animateScale;
+               const innerRadius = animateScale ? 0 : me.innerRadius;
+               const outerRadius = animateScale ? 0 : me.outerRadius;
+               const firstOpts = me._resolveDataElementOptions(start, mode);
+               const sharedOptions = me._getSharedOptions(mode, arcs[start], firstOpts);
+               const includeOptions = me._includeOptions(mode, sharedOptions);
                let startAngle = opts.rotation;
                let i;
 
@@ -222,21 +232,23 @@ class DoughnutController extends DatasetController {
                        const index = start + i;
                        const circumference = me._circumference(index, reset);
                        const arc = arcs[i];
-                       const options = arc._options || {};
                        const properties = {
-                               x: centerX + chart.offsetX,
-                               y: centerY + chart.offsetY,
+                               x: centerX + me.offsetX,
+                               y: centerY + me.offsetY,
                                startAngle,
                                endAngle: startAngle + circumference,
                                circumference,
                                outerRadius,
-                               innerRadius,
-                               options
+                               innerRadius
                        };
+                       if (includeOptions) {
+                               properties.options = me._resolveDataElementOptions(index, mode);
+                       }
                        startAngle += circumference;
 
                        me._updateElement(arc, index, properties, mode);
                }
+               me._updateSharedOptions(sharedOptions, mode);
        }
 
        calculateTotal() {
@@ -252,10 +264,6 @@ class DoughnutController extends DatasetController {
                        }
                }
 
-               /* if (total === 0) {
-                       total = NaN;
-               }*/
-
                return total;
        }
 
@@ -267,7 +275,6 @@ class DoughnutController extends DatasetController {
                return 0;
        }
 
-       // gets the max border or hover width to properly scale pie charts
        getMaxBorderWidth(arcs) {
                const me = this;
                let max = 0;
@@ -321,16 +328,16 @@ class DoughnutController extends DatasetController {
        /**
         * @private
         */
-       _getRingWeight(dataSetIndex) {
-               return Math.max(valueOrDefault(this.chart.data.datasets[dataSetIndex].weight, 1), 0);
+       _getRingWeight(datasetIndex) {
+               return Math.max(valueOrDefault(this.chart.data.datasets[datasetIndex].weight, 1), 0);
        }
 
        /**
-        * Returns the sum of all visibile data set weights.  This value can be 0.
+        * Returns the sum of all visibile data set weights.
         * @private
         */
        _getVisibleDatasetWeightTotal() {
-               return this._getRingWeightOffset(this.chart.data.datasets.length);
+               return this._getRingWeightOffset(this.chart.data.datasets.length) || 1;
        }
 }
 
index d68f1024e8c71c54e2e449b1c977becf5ba62345..10a51a4796c7285ff54321462ede12b6fbe8cd09 100644 (file)
@@ -128,12 +128,12 @@ class PolarAreaController extends DatasetController {
                const opts = chart.options;
                const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
 
-               chart.outerRadius = Math.max(minSize / 2, 0);
-               chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
-               chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
+               const outerRadius = Math.max(minSize / 2, 0);
+               const innerRadius = Math.max(opts.cutoutPercentage ? (outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
+               const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount();
 
-               me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
-               me.innerRadius = me.outerRadius - chart.radiusLength;
+               me.outerRadius = outerRadius - (radiusLength * me.index);
+               me.innerRadius = me.outerRadius - radiusLength;
        }
 
        updateElements(arcs, start, mode) {
index 09044ccedd3982b5ee510cde75030d3c40b0ac94..dbf9f0ce34e1906438b6bc4cfdedeb45ecf28e45 100644 (file)
@@ -46,20 +46,6 @@ describe('Chart.controllers.doughnut', function() {
                expect(meta.data[3] instanceof Chart.elements.Arc).toBe(true);
        });
 
-       it('should set the innerRadius to 0 if the config option is 0', function() {
-               var chart = window.acquireChart({
-                       type: 'pie',
-                       data: {
-                               datasets: [{
-                                       data: [10, 15, 0, 4]
-                               }],
-                               labels: []
-                       }
-               });
-
-               expect(chart.innerRadius).toBe(0);
-       });
-
        it ('should reset and update elements', function() {
                var chart = window.acquireChart({
                        type: 'doughnut',
@@ -302,13 +288,9 @@ describe('Chart.controllers.doughnut', function() {
 
                chart.update();
 
+               var controller = chart.getDatasetMeta(0).controller;
                expect(chart.chartArea.bottom - chart.chartArea.top).toBe(512);
-               expect(chart.borderWidth).toBe(8);
-               expect(chart.outerRadius).toBe(252);
-               expect(chart.innerRadius).toBe(126);
-               expect(chart.radiusLength).toBe(63);
 
-               var controller = chart.getDatasetMeta(0).controller;
                expect(controller.getMaxBorderWidth()).toBe(8);
                expect(controller.outerRadius).toBe(252);
                expect(controller.innerRadius).toBe(189);