Skip to content

Commit

Permalink
radialLinear: fix positioning & scaling (#10021)
Browse files Browse the repository at this point in the history
* radialLinear: fix positioning & scaling

* bloody pixels

* better radar fixtures
  • Loading branch information
kurkle authored Dec 23, 2021
1 parent d24fe14 commit 1da9fdd
Show file tree
Hide file tree
Showing 55 changed files with 828 additions and 140 deletions.
130 changes: 54 additions & 76 deletions src/scales/scale.radialLinear.js
Original file line number Diff line number Diff line change
@@ -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, PI} from '../helpers/helpers.math';
import {HALF_PI, 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';
Expand Down Expand Up @@ -76,70 +76,77 @@ function fitWithPointLabels(scale) {

// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
const furthestLimits = {
l: 0,
r: scale.width,
t: 0,
b: scale.height - scale.paddingTop
const orig = {
l: scale.left + scale._padding.left,
r: scale.right - scale._padding.right,
t: scale.top + scale._padding.top,
b: scale.bottom - scale._padding.bottom
};
const furthestAngles = {};
const limits = Object.assign({}, orig);
const labelSizes = [];
const padding = [];

const valueCount = scale._pointLabels.length;
const pointLabelOpts = scale.options.pointLabels;
const additionalAngle = pointLabelOpts.centerPointLabels ? PI / valueCount : 0;

for (let i = 0; i < valueCount; i++) {
const opts = scale.options.pointLabels.setContext(scale.getPointLabelContext(i));
const opts = pointLabelOpts.setContext(scale.getPointLabelContext(i));
padding[i] = opts.padding;
const pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i]);
const pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i], additionalAngle);
const plFont = toFont(opts.font);
const textSize = measureLabelSize(scale.ctx, plFont, scale._pointLabels[i]);
labelSizes[i] = textSize;

const angleRadians = scale.getIndexAngle(i);
const angle = toDegrees(angleRadians);
const angleRadians = _normalizeAngle(scale.getIndexAngle(i) + additionalAngle);
const angle = Math.round(toDegrees(angleRadians));
const hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
const vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);

if (hLimits.start < furthestLimits.l) {
furthestLimits.l = hLimits.start;
furthestAngles.l = angleRadians;
}

if (hLimits.end > furthestLimits.r) {
furthestLimits.r = hLimits.end;
furthestAngles.r = angleRadians;
}

if (vLimits.start < furthestLimits.t) {
furthestLimits.t = vLimits.start;
furthestAngles.t = angleRadians;
}

if (vLimits.end > furthestLimits.b) {
furthestLimits.b = vLimits.end;
furthestAngles.b = angleRadians;
}
updateLimits(limits, orig, angleRadians, hLimits, vLimits);
}

scale._setReductions(scale.drawingArea, furthestLimits, furthestAngles);
scale.setCenterPoint(
orig.l - limits.l,
limits.r - orig.r,
orig.t - limits.t,
limits.b - orig.b
);

// Now that text size is determined, compute the full positions
scale._pointLabelItems = buildPointLabelItems(scale, labelSizes, padding);
}

function updateLimits(limits, orig, angle, hLimits, vLimits) {
const sin = Math.abs(Math.sin(angle));
const cos = Math.abs(Math.cos(angle));
let x = 0;
let y = 0;
if (hLimits.start < orig.l) {
x = (orig.l - hLimits.start) / sin;
limits.l = Math.min(limits.l, orig.l - x);
} else if (hLimits.end > orig.r) {
x = (hLimits.end - orig.r) / sin;
limits.r = Math.max(limits.r, orig.r + x);
}
if (vLimits.start < orig.t) {
y = (orig.t - vLimits.start) / cos;
limits.t = Math.min(limits.t, orig.t - y);
} else if (vLimits.end > orig.b) {
y = (vLimits.end - orig.b) / cos;
limits.b = Math.max(limits.b, orig.b + y);
}
}

function buildPointLabelItems(scale, labelSizes, padding) {
const items = [];
const valueCount = scale._pointLabels.length;
const opts = scale.options;
const tickBackdropHeight = getTickBackdropHeight(opts);
const outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
const extra = getTickBackdropHeight(opts) / 2;
const outerDistance = scale.drawingArea;
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 angle = toDegrees(pointLabelPosition.angle + HALF_PI);
const angle = Math.round(toDegrees(_normalizeAngle(pointLabelPosition.angle + HALF_PI)));
const size = labelSizes[i];
const y = yForAngle(pointLabelPosition.y, size.h, angle);
const textAlign = getTextAlignForAngle(angle);
Expand Down Expand Up @@ -261,10 +268,6 @@ function drawRadiusLine(scale, gridLineOpts, radius, labelCount) {
ctx.restore();
}

function numberOrZero(param) {
return isNumber(param) ? param : 0;
}

function createPointLabelContext(parent, index, label) {
return createContext(parent, {
label,
Expand All @@ -291,12 +294,12 @@ export default class RadialLinearScale extends LinearScaleBase {

setDimensions() {
// Set the unconstrained dimension before label rotation
this.width = this.maxWidth;
this.height = this.maxHeight;
this.paddingTop = getTickBackdropHeight(this.options) / 2;
this.xCenter = Math.floor(this.width / 2);
this.yCenter = Math.floor((this.height - this.paddingTop) / 2);
this.drawingArea = Math.min(this.height - this.paddingTop, this.width) / 2;
const padding = this._padding = toPadding(getTickBackdropHeight(this.options) / 2);
const w = this.width = this.maxWidth - padding.width;
const h = this.height = this.maxHeight - padding.height;
this.xCenter = Math.floor(this.left + w / 2 + padding.left);
this.yCenter = Math.floor(this.top + h / 2 + padding.top);
this.drawingArea = Math.floor(Math.min(w, h) / 2);
}

determineDataLimits() {
Expand Down Expand Up @@ -339,35 +342,10 @@ export default class RadialLinearScale extends LinearScaleBase {
}
}

/**
* Set radius reductions and determine new radius and center point
* @private
*/
_setReductions(largestPossibleRadius, furthestLimits, furthestAngles) {
let radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
let radiusReductionRight = Math.max(furthestLimits.r - this.width, 0) / Math.sin(furthestAngles.r);
let radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
let radiusReductionBottom = -Math.max(furthestLimits.b - (this.height - this.paddingTop), 0) / Math.cos(furthestAngles.b);

radiusReductionLeft = numberOrZero(radiusReductionLeft);
radiusReductionRight = numberOrZero(radiusReductionRight);
radiusReductionTop = numberOrZero(radiusReductionTop);
radiusReductionBottom = numberOrZero(radiusReductionBottom);

this.drawingArea = Math.max(largestPossibleRadius / 2, Math.min(
Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)));
this.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
}

setCenterPoint(leftMovement, rightMovement, topMovement, bottomMovement) {
const maxRight = this.width - rightMovement - this.drawingArea;
const maxLeft = leftMovement + this.drawingArea;
const maxTop = topMovement + this.drawingArea;
const maxBottom = (this.height - this.paddingTop) - bottomMovement - this.drawingArea;

this.xCenter = Math.floor(((maxLeft + maxRight) / 2) + this.left);
this.yCenter = Math.floor(((maxTop + maxBottom) / 2) + this.top + this.paddingTop);
this.xCenter += Math.floor((leftMovement - rightMovement) / 2);
this.yCenter += Math.floor((topMovement - bottomMovement) / 2);
this.drawingArea -= Math.min(this.drawingArea / 2, Math.max(leftMovement, rightMovement, topMovement, bottomMovement));
}

getIndexAngle(index) {
Expand Down
26 changes: 26 additions & 0 deletions test/fixtures/controller.polarArea/pointLabels/centered-180.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module.exports = {
config: {
type: 'polarArea',
data: {
datasets: [{
data: [1, 2, 3, 4],
backgroundColor: ['#f003', '#0f03', '#00f3', '#0003']
}],
labels: [['label 1', 'line 2'], ['label 2', 'line 2'], ['label 3', 'line 2'], ['label 4', 'line 2']]
},
options: {
scales: {
r: {
startAngle: 180,
pointLabels: {
display: true,
centerPointLabels: true
}
}
}
}
},
options: {
spriteText: true
}
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions test/fixtures/controller.polarArea/pointLabels/centered-45.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module.exports = {
config: {
type: 'polarArea',
data: {
datasets: [{
data: [1, 2, 3, 4],
backgroundColor: ['#f003', '#0f03', '#00f3', '#0003']
}],
labels: [['label 1', 'line 2'], ['label 2', 'line 2'], ['label 3', 'line 2'], ['label 4', 'line 2']]
},
options: {
scales: {
r: {
startAngle: 45,
pointLabels: {
display: true,
centerPointLabels: true
}
}
}
}
},
options: {
spriteText: true
}
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions test/fixtures/controller.polarArea/pointLabels/centered.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module.exports = {
config: {
type: 'polarArea',
data: {
datasets: [{
data: [1, 2, 3, 4],
backgroundColor: ['#f003', '#0f03', '#00f3', '#0003']
}],
labels: [['label 1', 'line 2'], ['label 2', 'line 2'], ['label 3', 'line 2'], ['label 4', 'line 2']]
},
options: {
scales: {
r: {
pointLabels: {
display: true,
centerPointLabels: true
}
}
}
}
},
options: {
spriteText: true
}
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions test/fixtures/controller.polarArea/pointLabels/default-180.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module.exports = {
config: {
type: 'polarArea',
data: {
datasets: [{
data: [1, 2, 3, 4],
backgroundColor: ['#f003', '#0f03', '#00f3', '#0003']
}],
labels: [['label 1', 'line 2'], ['label 2', 'line 2'], ['label 3', 'line 2'], ['label 4', 'line 2']]
},
options: {
scales: {
r: {
startAngle: 180,
pointLabels: {
display: true,
}
}
}
}
},
options: {
spriteText: true
}
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions test/fixtures/controller.polarArea/pointLabels/default-45.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module.exports = {
config: {
type: 'polarArea',
data: {
datasets: [{
data: [1, 2, 3, 4],
backgroundColor: ['#f003', '#0f03', '#00f3', '#0003']
}],
labels: [['label 1', 'line 2'], ['label 2', 'line 2'], ['label 3', 'line 2'], ['label 4', 'line 2']]
},
options: {
scales: {
r: {
startAngle: 45,
pointLabels: {
display: true
}
}
}
}
},
options: {
spriteText: true
}
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions test/fixtures/controller.polarArea/pointLabels/default.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module.exports = {
config: {
type: 'polarArea',
data: {
datasets: [{
data: [1, 2, 3, 4],
backgroundColor: ['#f003', '#0f03', '#00f3', '#0003']
}],
labels: [['label 1', 'line 2'], ['label 2', 'line 2'], ['label 3', 'line 2'], ['label 4', 'line 2']]
},
options: {
scales: {
r: {
pointLabels: {
display: true,
}
}
}
}
},
options: {
spriteText: true
}
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module.exports = {
config: {
type: 'polarArea',
data: {
datasets: [{
data: [1, 2, 3, 4],
backgroundColor: ['#f003', '#0f03', '#00f3', '#0003']
}],
labels: [['label 1', 'line 2'], ['label 2', 'line 2'], ['label 3', 'line 2'], ['label 4', 'line 2']]
},
options: {
plugins: {
title: {
display: true,
position: 'bottom',
text: 'Chart Title'
},
legend: false
},
scales: {
r: {
startAngle: 45,
pointLabels: {
display: true,
centerPointLabels: true
}
}
}
}
},
options: {
spriteText: true
}
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 1da9fdd

Please sign in to comment.