Skip to content

Commit

Permalink
Expose radial scale point label positions (#8588)
Browse files Browse the repository at this point in the history
  • Loading branch information
etimberg authored Mar 7, 2021
1 parent 5a48604 commit 275fdaf
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 20 deletions.
1 change: 1 addition & 0 deletions docs/docs/getting-started/v3-migration.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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

0 comments on commit 275fdaf

Please sign in to comment.