From ab47a069be2488f835fb8612316c5d3c75dcc9c7 Mon Sep 17 00:00:00 2001 From: t-mangoe Date: Sat, 4 Dec 2021 08:55:39 +0900 Subject: [PATCH 1/6] center point labels to slices in polar chart --- src/controllers/controller.polarArea.js | 3 +- src/scales/scale.radialLinear.js | 36 ++++++++++++---- test/specs/scale.radialLinear.tests.js | 57 ++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 10 deletions(-) diff --git a/src/controllers/controller.polarArea.js b/src/controllers/controller.polarArea.js index 84a9568a4c1..1dd07b94458 100644 --- a/src/controllers/controller.polarArea.js +++ b/src/controllers/controller.polarArea.js @@ -205,7 +205,8 @@ PolarAreaController.overrides = { circular: true }, pointLabels: { - display: false + display: false, + polarChartLabel: true }, startAngle: 0 } diff --git a/src/scales/scale.radialLinear.js b/src/scales/scale.radialLinear.js index cbe37238e66..4534471d432 100644 --- a/src/scales/scale.radialLinear.js +++ b/src/scales/scale.radialLinear.js @@ -1,10 +1,10 @@ import defaults from '../core/core.defaults'; import {_longestText, renderText} from '../helpers/helpers.canvas'; -import {HALF_PI, isNumber, TAU, toDegrees, toRadians, _normalizeAngle} from '../helpers/helpers.math'; +import {HALF_PI, isNumber, TAU, toDegrees, toRadians, _normalizeAngle, PI} from '../helpers/helpers.math'; import LinearScaleBase from './scale.linearbase'; import Ticks from '../core/core.ticks'; import {valueOrDefault, isArray, isFinite, callback as callCallback, isNullOrUndef} from '../helpers/helpers.core'; -import {createContext, toFont, toPadding} from '../helpers/helpers.options'; +import {toFont, toPadding} from '../helpers/helpers.options'; function getTickBackdropHeight(opts) { const tickOpts = opts.ticks; @@ -133,12 +133,19 @@ function buildPointLabelItems(scale, labelSizes, padding) { const opts = scale.options; const tickBackdropHeight = getTickBackdropHeight(opts); const outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); + const pointLableOpts = opts.pointLabels; + // const flag = pointLableOpts.polarChartLabel; + const additionalAngle = pointLableOpts.polarChartLabel ? PI / valueCount : 0; + // const additionalAngle = 0; for (let i = 0; i < valueCount; i++) { // Extra pixels out for some label spacing const extra = (i === 0 ? tickBackdropHeight / 2 : 0); - const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i]); - const angle = toDegrees(scale.getIndexAngle(i)); + // const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i]); + const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i], {additionalAngle}); + // const angle = toDegrees(scale.getIndexAngle(i)); + // const angle = toDegrees(scale.getIndexAngle(i, {additionalAngle})); + const angle = toDegrees(pointLabelPosition.angle + HALF_PI); const size = labelSizes[i]; const y = yForAngle(pointLabelPosition.y, size.h, angle); const textAlign = getTextAlignForAngle(angle); @@ -265,7 +272,7 @@ function numberOrZero(param) { } function createPointLabelContext(parent, index, label) { - return createContext(parent, { + return Object.assign(Object.create(parent), { label, index, type: 'pointLabel' @@ -368,8 +375,12 @@ export default class RadialLinearScale extends LinearScaleBase { } getIndexAngle(index) { + // getIndexAngle(index, {additionalAngle = 0} = {}) { const angleMultiplier = TAU / this.getLabels().length; const startAngle = this.options.startAngle || 0; + // const pointLableOpts = this.options.pointLabels; + // const additionalAngle = pointLableOpts.polarChartLabel ? PI / this.getLabels().length : 0; + // return _normalizeAngle(index * angleMultiplier + toRadians(startAngle) + additionalAngle); return _normalizeAngle(index * angleMultiplier + toRadians(startAngle)); } @@ -404,8 +415,14 @@ export default class RadialLinearScale extends LinearScaleBase { } } - getPointPosition(index, distanceFromCenter) { - const angle = this.getIndexAngle(index) - HALF_PI; + // getPointPosition(index, distanceFromCenter) { + getPointPosition(index, distanceFromCenter, {additionalAngle = 0} = {}) { + // distanceFromCenter /= 2; + // const angle = this.getIndexAngle(index) - HALF_PI; + // const angleMultiplierHalf = PI / this.getLabels().length; + // const pointLableOpts = this.options.pointLabels; + // const additionalAngle = pointLableOpts.polarChartLabel ? PI / this.getLabels().length : 0; + const angle = this.getIndexAngle(index) - HALF_PI + additionalAngle; return { x: Math.cos(angle) * distanceFromCenter + this.xCenter, y: Math.sin(angle) * distanceFromCenter + this.yCenter, @@ -618,7 +635,10 @@ RadialLinearScale.defaults = { }, // Number - Additionl padding between scale and pointLabel - padding: 5 + padding: 5, + + // Boolean - if true, center point labels to slices in polar chart + polarChartLabel: false } }; diff --git a/test/specs/scale.radialLinear.tests.js b/test/specs/scale.radialLinear.tests.js index b2e545b58ae..eedd8fb9d4f 100644 --- a/test/specs/scale.radialLinear.tests.js +++ b/test/specs/scale.radialLinear.tests.js @@ -48,7 +48,8 @@ describe('Test the radial linear scale', function() { size: 10 }, callback: defaultConfig.pointLabels.callback, - padding: 5 + padding: 5, + polarChartLabel: false } }); @@ -590,4 +591,58 @@ describe('Test the radial linear scale', function() { }); }); }); + + it('should correctly get the point positions in polar area graph', function() { + var chart = window.acquireChart({ + type: 'polarArea', + data: { + datasets: [{ + data: [10, 5, 0, 25, 78] + }], + labels: ['label1', 'label2', 'label3', 'label4', 'label5'] + }, + options: { + scales: { + r: { + pointLabels: { + display: true, + padding: 5 + }, + ticks: { + display: false, + } + } + } + } + }); + + const PI = Math.PI; + const lavelNum = 5; + const padding = 5; + const pointLabelItems = chart.scales.r._pointLabelItems; + const additionalAngle = PI / lavelNum; + const opts = chart.scales.r.options; + const outerDistance = chart.scales.r.getDistanceFromCenterForValue(opts.ticks.reverse ? chart.scales.r.min : chart.scales.r.max); + const tickBackdropHeight = 0; + const yForAngle = function(y, h, angle) { + if (angle === 90 || angle === 270) { + y -= (h / 2); + } else if (angle > 270 || angle < 90) { + y -= h; + } + return y; + }; + const toDegrees = function(radians) { + return radians * (180 / PI); + }; + + for (var i = 0; i < 5; i++) { + const extra = (i === 0 ? tickBackdropHeight / 2 : 0); + const pointLabelItem = pointLabelItems[i]; + const pointPosition = chart.scales.r.getPointPosition(i, outerDistance + extra + padding, {additionalAngle}); + expect(pointLabelItem.x).toBe(pointPosition.x); + expect(pointLabelItem.y).toBe(yForAngle(pointPosition.y, 12, toDegrees(pointPosition.angle + PI / 2))); + } + + }); }); From 8bf87b888920dbf68cd14254820149bd67d01445 Mon Sep 17 00:00:00 2001 From: t-mangoe Date: Sat, 4 Dec 2021 09:08:14 +0900 Subject: [PATCH 2/6] remove unnecessary comments --- src/scales/scale.radialLinear.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/scales/scale.radialLinear.js b/src/scales/scale.radialLinear.js index 4534471d432..da394ea382a 100644 --- a/src/scales/scale.radialLinear.js +++ b/src/scales/scale.radialLinear.js @@ -134,17 +134,12 @@ function buildPointLabelItems(scale, labelSizes, padding) { const tickBackdropHeight = getTickBackdropHeight(opts); const outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); const pointLableOpts = opts.pointLabels; - // const flag = pointLableOpts.polarChartLabel; const additionalAngle = pointLableOpts.polarChartLabel ? PI / valueCount : 0; - // const additionalAngle = 0; for (let i = 0; i < valueCount; i++) { // Extra pixels out for some label spacing const extra = (i === 0 ? tickBackdropHeight / 2 : 0); - // const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i]); const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i], {additionalAngle}); - // const angle = toDegrees(scale.getIndexAngle(i)); - // const angle = toDegrees(scale.getIndexAngle(i, {additionalAngle})); const angle = toDegrees(pointLabelPosition.angle + HALF_PI); const size = labelSizes[i]; const y = yForAngle(pointLabelPosition.y, size.h, angle); @@ -375,12 +370,8 @@ export default class RadialLinearScale extends LinearScaleBase { } getIndexAngle(index) { - // getIndexAngle(index, {additionalAngle = 0} = {}) { const angleMultiplier = TAU / this.getLabels().length; const startAngle = this.options.startAngle || 0; - // const pointLableOpts = this.options.pointLabels; - // const additionalAngle = pointLableOpts.polarChartLabel ? PI / this.getLabels().length : 0; - // return _normalizeAngle(index * angleMultiplier + toRadians(startAngle) + additionalAngle); return _normalizeAngle(index * angleMultiplier + toRadians(startAngle)); } @@ -415,13 +406,7 @@ export default class RadialLinearScale extends LinearScaleBase { } } - // getPointPosition(index, distanceFromCenter) { getPointPosition(index, distanceFromCenter, {additionalAngle = 0} = {}) { - // distanceFromCenter /= 2; - // const angle = this.getIndexAngle(index) - HALF_PI; - // const angleMultiplierHalf = PI / this.getLabels().length; - // const pointLableOpts = this.options.pointLabels; - // const additionalAngle = pointLableOpts.polarChartLabel ? PI / this.getLabels().length : 0; const angle = this.getIndexAngle(index) - HALF_PI + additionalAngle; return { x: Math.cos(angle) * distanceFromCenter + this.xCenter, From 87261cd006be56d4a994b0272bf926f4e5a4e1a2 Mon Sep 17 00:00:00 2001 From: t-mangoe Date: Sat, 4 Dec 2021 09:18:04 +0900 Subject: [PATCH 3/6] put the code together in one line --- src/scales/scale.radialLinear.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/scales/scale.radialLinear.js b/src/scales/scale.radialLinear.js index da394ea382a..fe555fbe87c 100644 --- a/src/scales/scale.radialLinear.js +++ b/src/scales/scale.radialLinear.js @@ -133,8 +133,7 @@ function buildPointLabelItems(scale, labelSizes, padding) { const opts = scale.options; const tickBackdropHeight = getTickBackdropHeight(opts); const outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); - const pointLableOpts = opts.pointLabels; - const additionalAngle = pointLableOpts.polarChartLabel ? PI / valueCount : 0; + const additionalAngle = opts.pointLabels.polarChartLabel ? PI / valueCount : 0; for (let i = 0; i < valueCount; i++) { // Extra pixels out for some label spacing From 0888c35e91672504e3eaf7aa1bcdc14385dacc86 Mon Sep 17 00:00:00 2001 From: t-mangoe Date: Sat, 11 Dec 2021 09:29:21 +0900 Subject: [PATCH 4/6] fix the code according to the code review --- src/controllers/controller.polarArea.js | 3 +-- src/scales/scale.radialLinear.js | 8 ++++---- test/specs/scale.radialLinear.tests.js | 11 ++++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/controllers/controller.polarArea.js b/src/controllers/controller.polarArea.js index 1dd07b94458..84a9568a4c1 100644 --- a/src/controllers/controller.polarArea.js +++ b/src/controllers/controller.polarArea.js @@ -205,8 +205,7 @@ PolarAreaController.overrides = { circular: true }, pointLabels: { - display: false, - polarChartLabel: true + display: false }, startAngle: 0 } diff --git a/src/scales/scale.radialLinear.js b/src/scales/scale.radialLinear.js index fe555fbe87c..599f33a5dc5 100644 --- a/src/scales/scale.radialLinear.js +++ b/src/scales/scale.radialLinear.js @@ -133,12 +133,12 @@ function buildPointLabelItems(scale, labelSizes, padding) { const opts = scale.options; const tickBackdropHeight = getTickBackdropHeight(opts); const outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); - const additionalAngle = opts.pointLabels.polarChartLabel ? PI / valueCount : 0; + const additionalAngle = opts.pointLabels.centerPointLabels ? PI / valueCount : 0; for (let i = 0; i < valueCount; i++) { // Extra pixels out for some label spacing const extra = (i === 0 ? tickBackdropHeight / 2 : 0); - const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i], {additionalAngle}); + const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i], additionalAngle); const angle = toDegrees(pointLabelPosition.angle + HALF_PI); const size = labelSizes[i]; const y = yForAngle(pointLabelPosition.y, size.h, angle); @@ -405,7 +405,7 @@ export default class RadialLinearScale extends LinearScaleBase { } } - getPointPosition(index, distanceFromCenter, {additionalAngle = 0} = {}) { + getPointPosition(index, distanceFromCenter, additionalAngle = 0) { const angle = this.getIndexAngle(index) - HALF_PI + additionalAngle; return { x: Math.cos(angle) * distanceFromCenter + this.xCenter, @@ -622,7 +622,7 @@ RadialLinearScale.defaults = { padding: 5, // Boolean - if true, center point labels to slices in polar chart - polarChartLabel: false + centerPointLabels: false } }; diff --git a/test/specs/scale.radialLinear.tests.js b/test/specs/scale.radialLinear.tests.js index eedd8fb9d4f..a7f6d08c49a 100644 --- a/test/specs/scale.radialLinear.tests.js +++ b/test/specs/scale.radialLinear.tests.js @@ -49,7 +49,7 @@ describe('Test the radial linear scale', function() { }, callback: defaultConfig.pointLabels.callback, padding: 5, - polarChartLabel: false + centerPointLabels: false } }); @@ -592,7 +592,7 @@ describe('Test the radial linear scale', function() { }); }); - it('should correctly get the point positions in polar area graph', function() { + it('should correctly get the point positions in center', function() { var chart = window.acquireChart({ type: 'polarArea', data: { @@ -606,10 +606,11 @@ describe('Test the radial linear scale', function() { r: { pointLabels: { display: true, - padding: 5 + padding: 5, + centerPointLabels: true }, ticks: { - display: false, + display: false } } } @@ -639,7 +640,7 @@ describe('Test the radial linear scale', function() { for (var i = 0; i < 5; i++) { const extra = (i === 0 ? tickBackdropHeight / 2 : 0); const pointLabelItem = pointLabelItems[i]; - const pointPosition = chart.scales.r.getPointPosition(i, outerDistance + extra + padding, {additionalAngle}); + const pointPosition = chart.scales.r.getPointPosition(i, outerDistance + extra + padding, additionalAngle); expect(pointLabelItem.x).toBe(pointPosition.x); expect(pointLabelItem.y).toBe(yForAngle(pointPosition.y, 12, toDegrees(pointPosition.angle + PI / 2))); } From 79e871875e5b59549ed058f2cb59c41c1a427268 Mon Sep 17 00:00:00 2001 From: t-mangoe Date: Sat, 11 Dec 2021 10:07:22 +0900 Subject: [PATCH 5/6] Undo changes related to the createContext function --- src/scales/scale.radialLinear.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scales/scale.radialLinear.js b/src/scales/scale.radialLinear.js index 599f33a5dc5..5eeb13988ac 100644 --- a/src/scales/scale.radialLinear.js +++ b/src/scales/scale.radialLinear.js @@ -4,7 +4,7 @@ import {HALF_PI, isNumber, TAU, toDegrees, toRadians, _normalizeAngle, PI} from import LinearScaleBase from './scale.linearbase'; import Ticks from '../core/core.ticks'; import {valueOrDefault, isArray, isFinite, callback as callCallback, isNullOrUndef} from '../helpers/helpers.core'; -import {toFont, toPadding} from '../helpers/helpers.options'; +import {createContext, toFont, toPadding} from '../helpers/helpers.options'; function getTickBackdropHeight(opts) { const tickOpts = opts.ticks; @@ -266,7 +266,7 @@ function numberOrZero(param) { } function createPointLabelContext(parent, index, label) { - return Object.assign(Object.create(parent), { + return createContext(parent, { label, index, type: 'pointLabel' From 2192826bc9f135fc3237b45abe25ba81fe4543b1 Mon Sep 17 00:00:00 2001 From: t-mangoe Date: Sat, 18 Dec 2021 09:53:05 +0900 Subject: [PATCH 6/6] add documentation and types. --- docs/axes/radial/linear.md | 1 + types/index.esm.d.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/docs/axes/radial/linear.md b/docs/axes/radial/linear.md index a7cd8435eab..839a635b583 100644 --- a/docs/axes/radial/linear.md +++ b/docs/axes/radial/linear.md @@ -125,6 +125,7 @@ Namespace: `options.scales[scaleId].pointLabels` | `color` | [`Color`](../../general/colors.md) | Yes | `Chart.defaults.color` | Color of label. | `font` | `Font` | Yes | `Chart.defaults.font` | See [Fonts](../../general/fonts.md) | `padding` | `number` | Yes | 5 | Padding between chart and point labels. +| `centerPointLabels` | `boolean` | | `false` | if true, point labels are centered. The scriptable context is described in [Options](../../general/options.md#scale) section. diff --git a/types/index.esm.d.ts b/types/index.esm.d.ts index 7d7721a7d4d..a750dbbe88e 100644 --- a/types/index.esm.d.ts +++ b/types/index.esm.d.ts @@ -3342,6 +3342,12 @@ export type RadialLinearScaleOptions = CoreScaleOptions & { * Callback function to transform data labels to point labels. The default implementation simply returns the current string. */ callback: (label: string, index: number) => string | string[] | number | number[]; + + /** + * if true, point labels are centered. + * @default false + */ + centerPointLabels: boolean; }; /**