]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Add centerPointLabels option for linear radial scale (#9949)
authort-mangoe <m.takashi.raruba.101.112@gmail.com>
Tue, 21 Dec 2021 18:27:07 +0000 (03:27 +0900)
committerGitHub <noreply@github.com>
Tue, 21 Dec 2021 18:27:07 +0000 (20:27 +0200)
* center point labels to slices in polar chart

* remove unnecessary comments

* put the code together in one line

* fix the code according to the code review

* Undo changes related to the createContext function

* add documentation and types.

docs/axes/radial/linear.md
src/scales/scale.radialLinear.js
test/specs/scale.radialLinear.tests.js
types/index.esm.d.ts

index a7cd8435eabdc6fbf14275c57686840bb0ce1713..839a635b58314afec41230cad0b7355276cc0bc1 100644 (file)
@@ -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.
 
index cbe37238e66374208307e3eaf8b9a4a26a5403ea..5eeb13988ace4b646c1312644aa34fce84d6ecba 100644 (file)
@@ -1,6 +1,6 @@
 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';
@@ -133,12 +133,13 @@ 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.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]);
-    const angle = toDegrees(scale.getIndexAngle(i));
+    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);
     const textAlign = getTextAlignForAngle(angle);
@@ -404,8 +405,8 @@ export default class RadialLinearScale extends LinearScaleBase {
     }
   }
 
-  getPointPosition(index, distanceFromCenter) {
-    const angle = this.getIndexAngle(index) - HALF_PI;
+  getPointPosition(index, distanceFromCenter, additionalAngle = 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 +619,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
+    centerPointLabels: false
   }
 };
 
index b2e545b58ae4408498cebd5c1268d4476f0420f4..a7f6d08c49a0a2bbca7553cabd8f09e911d4ff0e 100644 (file)
@@ -48,7 +48,8 @@ describe('Test the radial linear scale', function() {
           size: 10
         },
         callback: defaultConfig.pointLabels.callback,
-        padding: 5
+        padding: 5,
+        centerPointLabels: false
       }
     });
 
@@ -590,4 +591,59 @@ describe('Test the radial linear scale', function() {
       });
     });
   });
+
+  it('should correctly get the point positions in center', 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,
+              centerPointLabels: true
+            },
+            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)));
+    }
+
+  });
 });
index edf661e7eca0e3a6742ddc247391a325958118e5..7ca3238c6aa954f8b60394cce075256e2c78b248 100644 (file)
@@ -3347,6 +3347,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;
   };
 
   /**