Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose radial scale point label positions #8588

Merged
merged 1 commit into from Mar 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/docs/getting-started/v3-migration.md
Expand Up @@ -449,6 +449,7 @@ The private APIs listed below were renamed:
* `DatasetController.resyncElements` was renamed to `DatasetController._resyncElements`
* `LayoutItem.isFullWidth` was renamed to `LayoutItem.isFullSize`
* `RadialLinearScale.setReductions` was renamed to `RadialLinearScale._setReductions`
* `RadialLinearScale.pointLabels` was renamed to `RadialLinearScale._pointLabels`
* `Scale.handleMargins` was renamed to `Scale._handleMargins`

### Changed
Expand Down
83 changes: 66 additions & 17 deletions src/scales/scale.radialLinear.js
Expand Up @@ -90,16 +90,16 @@ function fitWithPointLabels(scale) {
const furthestAngles = {};
let i, textSize, pointPosition;

scale._pointLabelSizes = [];
const labelSizes = [];

const valueCount = scale.chart.data.labels.length;
for (i = 0; i < valueCount; i++) {
pointPosition = scale.getPointPosition(i, scale.drawingArea + 5);
const opts = scale.options.pointLabels.setContext(scale.getContext(i));
const plFont = toFont(opts.font);
scale.ctx.font = plFont.string;
textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale.pointLabels[i]);
scale._pointLabelSizes[i] = textSize;
textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale._pointLabels[i]);
labelSizes[i] = textSize;

// Add quarter circle to make degree 0 mean top of circle
const angleRadians = scale.getIndexAngle(i);
Expand Down Expand Up @@ -129,6 +129,51 @@ function fitWithPointLabels(scale) {
}

scale._setReductions(scale.drawingArea, furthestLimits, furthestAngles);

scale._pointLabelItems = [];

// Now that text size is determined, compute the full positions
const opts = scale.options;
const tickBackdropHeight = getTickBackdropHeight(opts);
const outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);

for (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 + 5);

const angle = toDegrees(scale.getIndexAngle(i));
const size = labelSizes[i];
adjustPointPositionForLabelHeight(angle, size, pointLabelPosition);

const textAlign = getTextAlignForAngle(angle);
let left;

if (textAlign === 'left') {
left = pointLabelPosition.x;
} else if (textAlign === 'center') {
left = pointLabelPosition.x - (size.w / 2);
} else {
left = pointLabelPosition.x - size.w;
}

const right = left + size.w;

scale._pointLabelItems[i] = {
// Text position
x: pointLabelPosition.x,
y: pointLabelPosition.y,

// Text rendering data
textAlign,

// Bounding box
left,
top: pointLabelPosition.y,
right,
bottom: pointLabelPosition.y + size.h,
};
}
}

function getTextAlignForAngle(angle) {
Expand All @@ -153,31 +198,24 @@ function drawPointLabels(scale) {
const ctx = scale.ctx;
const opts = scale.options;
const pointLabelOpts = opts.pointLabels;
const tickBackdropHeight = getTickBackdropHeight(opts);
const outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);

ctx.save();

ctx.textBaseline = 'middle';

for (let i = scale.chart.data.labels.length - 1; i >= 0; i--) {
// Extra pixels out for some label spacing
const extra = (i === 0 ? tickBackdropHeight / 2 : 0);
const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5);

const optsAtIndex = pointLabelOpts.setContext(scale.getContext(i));
const plFont = toFont(optsAtIndex.font);
const angle = toDegrees(scale.getIndexAngle(i));
adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
const {x, y, textAlign} = scale._pointLabelItems[i];
renderText(
ctx,
scale.pointLabels[i],
pointLabelPosition.x,
pointLabelPosition.y + (plFont.lineHeight / 2),
scale._pointLabels[i],
x,
y + (plFont.lineHeight / 2),
plFont,
{
color: optsAtIndex.color,
textAlign: getTextAlignForAngle(angle),
textAlign: textAlign,
}
);
}
Expand Down Expand Up @@ -238,7 +276,8 @@ export default class RadialLinearScale extends LinearScaleBase {
/** @type {number} */
this.drawingArea = undefined;
/** @type {string[]} */
this.pointLabels = [];
this._pointLabels = [];
this._pointLabelItems = [];
}

setDimensions() {
Expand Down Expand Up @@ -278,7 +317,7 @@ export default class RadialLinearScale extends LinearScaleBase {
LinearScaleBase.prototype.generateTickLabels.call(me, ticks);

// Point labels
me.pointLabels = me.chart.data.labels.map((value, index) => {
me._pointLabels = me.chart.data.labels.map((value, index) => {
const label = callCallback(me.options.pointLabels.callback, [value, index], me);
return label || label === 0 ? label : '';
});
Expand Down Expand Up @@ -380,6 +419,16 @@ export default class RadialLinearScale extends LinearScaleBase {
return this.getPointPositionForValue(index || 0, this.getBaseValue());
}

getPointLabelPosition(index) {
const {left, top, right, bottom} = this._pointLabelItems[index];
return {
left,
top,
right,
bottom,
};
}

/**
* @protected
*/
Expand Down
6 changes: 3 additions & 3 deletions test/specs/scale.radialLinear.tests.js
Expand Up @@ -324,7 +324,7 @@ describe('Test the radial linear scale', function() {
});

expect(getLabels(chart.scales.r)).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8']);
expect(chart.scales.r.pointLabels).toEqual(['label1', 'label2', 'label3', 'label4', 'label5']);
expect(chart.scales.r._pointLabels).toEqual(['label1', 'label2', 'label3', 'label4', 'label5']);
});

it('Should build point labels using the user supplied callback', function() {
Expand All @@ -349,7 +349,7 @@ describe('Test the radial linear scale', function() {
}
});

expect(chart.scales.r.pointLabels).toEqual(['0', '1', '2', '3', '4']);
expect(chart.scales.r._pointLabels).toEqual(['0', '1', '2', '3', '4']);
});

it('Should build point labels from falsy values', function() {
Expand All @@ -363,7 +363,7 @@ describe('Test the radial linear scale', function() {
}
});

expect(chart.scales.r.pointLabels).toEqual([0, '', '', '', '', '']);
expect(chart.scales.r._pointLabels).toEqual([0, '', '', '', '', '']);
});

it('should correctly set the center point', function() {
Expand Down
1 change: 1 addition & 0 deletions types/index.esm.d.ts
Expand Up @@ -3089,6 +3089,7 @@ export interface RadialLinearScale<O extends RadialLinearScaleOptions = RadialLi
getValueForDistanceFromCenter(distance: number): number;
getPointPosition(index: number, distanceFromCenter: number): { x: number; y: number; angle: number };
getPointPositionForValue(index: number, value: number): { x: number; y: number; angle: number };
getPointLabelPosition(index: number): ChartArea;
getBasePosition(index: number): { x: number; y: number; angle: number };
}
export const RadialLinearScale: ChartComponent & {
Expand Down