Skip to content
91 changes: 79 additions & 12 deletions src/types/line.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export default class LineAnnotation extends Element {
y2,
width: x2 - x,
height: y2 - y,
_chartArea: chart.chartArea,
_horizontal
};
}
Expand Down Expand Up @@ -151,13 +152,12 @@ function drawLabel(ctx, line) {
ctx.textAlign = 'center';

const {width, height} = measureLabel(ctx, label);
const pos = calculateLabelPosition(line, width, height);
const rotation = label.rotation === 'auto' ? calculateAutoRotation(line) : toRadians(label.rotation);

line.labelRect = {x: pos.x, y: pos.y, width, height};
loadCornersOfRotatedLabelRect(line.labelRect, rotation);
line.labelRect = calculateLabelPosition(line, width, height, rotation);
adjustLabelPosition(line, rotation);

ctx.translate(pos.x, pos.y);
ctx.translate(line.labelRect.x, line.labelRect.y);
ctx.rotate(rotation);

ctx.fillStyle = label.backgroundColor;
Expand Down Expand Up @@ -203,7 +203,7 @@ function measureLabel(ctx, label) {
};
}

function calculateLabelPosition(line, width, height) {
function calculateLabelPosition(line, width, height, angle) {
const label = line.options.label;
const {xPadding, xAdjust, yPadding, yAdjust, position} = label;
const p1 = {x: line.x, y: line.y};
Expand Down Expand Up @@ -232,7 +232,68 @@ function calculateLabelPosition(line, width, height) {
x = pt.x + xAdjust;
y = pt.y + yAdjust;
}
return {x, y};

const cos = Math.cos(angle);
const sin = Math.sin(angle);
const rotatedHeight = Math.abs(width * sin) + Math.abs(height * cos);
const rotatedWidth = Math.abs(width * cos) + Math.abs(height * sin);

return {
x,
y,
width,
height,
rotatedWidth,
rotatedHeight
};
}

function adjustLabelPosition(line, angle) {
const {options, labelRect, _chartArea} = line;
const {rotatedHeight, rotatedWidth, height, width} = labelRect;
const label = options.label;
const {xPadding, xAdjust, yPadding, yAdjust, position} = label;
const p1 = {x: line.x, y: line.y};
const p2 = {x: line.x2, y: line.y2};
const xCoordinateSizes = {size: rotatedWidth, min: _chartArea.left, max: _chartArea.right, padding: xPadding, adjust: xAdjust};
const yCoordinateSizes = {size: rotatedHeight, min: _chartArea.top, max: _chartArea.bottom, padding: yPadding, adjust: yAdjust};
const xApexFactor = (width / 2 * Math.cos(angle)) - (height / 2 * Math.sin(angle));
const yApexFactor = (width / 2 * Math.sin(angle)) + (height / 2 * Math.cos(angle));
let x, y, pt;

switch (validPosition(position, line._horizontal)) {
case 'top':
y = line.y + (rotatedHeight / 2) + yAdjust + yPadding;
break;
case 'bottom':
y = line.y2 - (rotatedHeight / 2) + yAdjust - yPadding;
break;
case 'left':
x = line.x + (rotatedWidth / 2) + xAdjust + xPadding;
break;
case 'right':
x = line.x2 - (rotatedWidth / 2) + xAdjust - xPadding;
break;
default:
pt = pointInLine(p1, p2, 0.5);
x = adjustLabelCoordinate(pt.x + xAdjust, xCoordinateSizes);
y = adjustLabelCoordinate(pt.y + yAdjust, yCoordinateSizes);
}

if (!y) {
x = adjustLabelCoordinate(x, xCoordinateSizes);
y = adjustLabelCoordinate(interpolateY(x, p1, p2) + yAdjust, yCoordinateSizes);
} else if (!x) {
y = adjustLabelCoordinate(y, yCoordinateSizes);
x = adjustLabelCoordinate(interpolateX(y, p1, p2) + xAdjust, xCoordinateSizes);
}

labelRect.x = x;
labelRect.y = y;
labelRect.a = {x: x - xApexFactor, y: y - yApexFactor};
labelRect.b = {x: x + xApexFactor, y: y + yApexFactor};
labelRect.c = {x: x - xApexFactor, y: y - yApexFactor};
labelRect.d = {x: x + xApexFactor, y: y + yApexFactor};
}

function validPosition(position, horizontal) {
Expand All @@ -241,11 +302,17 @@ function validPosition(position, horizontal) {
? 'center' : position;
}

function loadCornersOfRotatedLabelRect(labelRect, angle) {
const {x, y, width, height} = labelRect;
function adjustLabelCoordinate(coordinate, labelSizes) {
const {size, min, max, padding, adjust} = labelSizes;
let value = coordinate;
const halfSize = size / 2;

if (min >= (coordinate + padding + adjust - halfSize)) {
value = min + padding - adjust + halfSize;
}
if (max <= (coordinate - padding + adjust + halfSize)) {
value = max - padding + adjust - halfSize;
}

labelRect.a = {x: x - ((width / 2) * Math.cos(angle)) - ((height / 2) * Math.sin(angle)), y: y - ((width / 2) * Math.sin(angle)) + ((height / 2) * Math.cos(angle))};
labelRect.b = {x: x + ((width / 2) * Math.cos(angle)) - ((height / 2) * Math.sin(angle)), y: y + ((width / 2) * Math.sin(angle)) + ((height / 2) * Math.cos(angle))};
labelRect.c = {x: x - ((width / 2) * Math.cos(angle)) + ((height / 2) * Math.sin(angle)), y: y - ((width / 2) * Math.sin(angle)) - ((height / 2) * Math.cos(angle))};
labelRect.d = {x: x + ((width / 2) * Math.cos(angle)) + ((height / 2) * Math.sin(angle)), y: y + ((width / 2) * Math.sin(angle)) - ((height / 2) * Math.cos(angle))};
return value;
}