Skip to content

Commit

Permalink
Merge pull request #16034 from apache/fix-pie-label
Browse files Browse the repository at this point in the history
fix(pie): label layout and text wrapping
  • Loading branch information
pissang committed Dec 21, 2021
2 parents c0df9ec + 967e738 commit 933c06f
Show file tree
Hide file tree
Showing 4 changed files with 356 additions and 26 deletions.
153 changes: 130 additions & 23 deletions src/chart/pie/labelLayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ interface LabelLayout {
edgeDistance: number
bleedMargin: PieSeriesOption['label']['bleedMargin']
rect: BoundingRect
/**
* user-set style.width.
* This is useful because label.style.width might be changed
* by constrainTextWidth.
*/
labelStyleWidth: number
unconstrainedWidth: number
targetTextWidth?: number
}

function adjustSingleSide(
Expand Down Expand Up @@ -82,7 +90,12 @@ function adjustSingleSide(
const rA2 = rA * rA;
// Use ellipse implicit function to calculate x
const dx = Math.sqrt((1 - Math.abs(dy * dy / rB2)) * rA2);
item.label.x = cx + (dx + item.len2) * dir;
const newX = cx + (dx + item.len2) * dir;
const deltaX = newX - item.label.x;
const newTargetWidth = item.targetTextWidth - deltaX * dir;
// text x is changed, so need to recalculate width.
constrainTextWidth(item, newTargetWidth, true);
item.label.x = newX;
}
}

Expand Down Expand Up @@ -160,23 +173,18 @@ function avoidOverlap(
}
}

adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight, viewLeft, viewTop, rightmostX);
adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight, viewLeft, viewTop, leftmostX);

for (let i = 0; i < labelLayoutList.length; i++) {
const layout = labelLayoutList[i];
const label = layout.label;
if (isPositionCenter(layout)) {
continue;
}
if (!isPositionCenter(layout) && layout.linePoints) {
if (layout.labelStyleWidth != null) {
continue;
}

const linePoints = layout.linePoints;
if (linePoints) {
const isAlignToEdge = layout.labelAlignTo === 'edge';
const label = layout.label;
const linePoints = layout.linePoints;

let realTextWidth = layout.rect.width;
let targetTextWidth;
if (isAlignToEdge) {
if (layout.labelAlignTo === 'edge') {
if (label.x < cx) {
targetTextWidth = linePoints[2][0] - layout.labelDistance
- viewLeft - layout.edgeDistance;
Expand All @@ -186,6 +194,14 @@ function avoidOverlap(
- linePoints[2][0] - layout.labelDistance;
}
}
else if (layout.labelAlignTo === 'labelLine') {
if (label.x < cx) {
targetTextWidth = leftmostX - viewLeft - layout.bleedMargin;
}
else {
targetTextWidth = viewLeft + viewWidth - rightmostX - layout.bleedMargin;
}
}
else {
if (label.x < cx) {
targetTextWidth = label.x - viewLeft - layout.bleedMargin;
Expand All @@ -194,16 +210,26 @@ function avoidOverlap(
targetTextWidth = viewLeft + viewWidth - label.x - layout.bleedMargin;
}
}
if (targetTextWidth < layout.rect.width) {
// TODOTODO
// layout.text = textContain.truncateText(layout.text, targetTextWidth, layout.font);
layout.label.style.width = targetTextWidth;
if (layout.labelAlignTo === 'edge') {
realTextWidth = targetTextWidth;
// realTextWidth = textContain.getWidth(layout.text, layout.font);
}
}
layout.targetTextWidth = targetTextWidth;

constrainTextWidth(layout, targetTextWidth);
}
}

adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight, viewLeft, viewTop, rightmostX);
adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight, viewLeft, viewTop, leftmostX);

for (let i = 0; i < labelLayoutList.length; i++) {
const layout = labelLayoutList[i];
if (!isPositionCenter(layout) && layout.linePoints) {
const label = layout.label;
const linePoints = layout.linePoints;
const isAlignToEdge = layout.labelAlignTo === 'edge';
const padding = label.style.padding as number[];
const paddingH = padding ? padding[1] + padding[3] : 0;
// textRect.width already contains paddingH if bgColor is set
const extraPaddingH = label.style.backgroundColor ? 0 : paddingH;
const realTextWidth = layout.rect.width + extraPaddingH;
const dist = linePoints[1][0] - linePoints[2][0];
if (isAlignToEdge) {
if (label.x < cx) {
Expand All @@ -228,6 +254,85 @@ function avoidOverlap(
}
}

/**
* Set max width of each label, and then wrap each label to the max width.
*
* @param layout label layout
* @param availableWidth max width for the label to display
* @param forceRecalculate recaculate the text layout even if the current width
* is smaller than `availableWidth`. This is useful when the text was previously
* wrapped by calling `constrainTextWidth` but now `availableWidth` changed, in
* which case, previous wrapping should be redo.
*/
function constrainTextWidth(
layout: LabelLayout,
availableWidth: number,
forceRecalculate: boolean = false
) {
if (layout.labelStyleWidth != null) {
// User-defined style.width has the highest priority.
return;
}

const label = layout.label;
const style = label.style;
const textRect = layout.rect;
const bgColor = style.backgroundColor;
const padding = style.padding as number[];
const paddingH = padding ? padding[1] + padding[3] : 0;
const overflow = style.overflow;

// textRect.width already contains paddingH if bgColor is set
const oldOuterWidth = textRect.width + (bgColor ? 0 : paddingH);
if (availableWidth < oldOuterWidth || forceRecalculate) {
const oldHeight = textRect.height;
if (overflow && overflow.match('break')) {
// Temporarily set background to be null to calculate
// the bounding box without backgroud.
label.setStyle('backgroundColor', null);
// Set constraining width
label.setStyle('width', availableWidth - paddingH);

// This is the real bounding box of the text without padding
const innerRect = label.getBoundingRect();

label.setStyle('width', Math.ceil(innerRect.width));
label.setStyle('backgroundColor', bgColor);
}
else {
const availableInnerWidth = availableWidth - paddingH;
const newWidth = availableWidth < oldOuterWidth
// Current text is too wide, use `availableWidth` as max width.
? availableInnerWidth
: (
// Current available width is enough, but the text may have
// already been wrapped with a smaller available width.
forceRecalculate
? (availableInnerWidth > layout.unconstrainedWidth
// Current available is larger than text width,
// so don't constrain width (otherwise it may have
// empty space in the background).
? null
// Current available is smaller than text width, so
// use the current available width as constraining
// width.
: availableInnerWidth
)
// Current available width is enough, so no need to
// constrain.
: null
);
label.setStyle('width', newWidth);
}

const newRect = label.getBoundingRect();
textRect.width = newRect.width;
const margin = (label.style.margin || 0) + 2.1;
textRect.height = newRect.height + margin;
textRect.y -= (textRect.height - oldHeight) / 2;
}
}

function isPositionCenter(sectorShape: LabelLayout) {
// Not change x for center label
return sectorShape.position === 'center';
Expand Down Expand Up @@ -411,7 +516,9 @@ export default function pieLabelLayout(
labelAlignTo: labelAlignTo,
edgeDistance: edgeDistance,
bleedMargin: bleedMargin,
rect: textRect
rect: textRect,
unconstrainedWidth: textRect.width,
labelStyleWidth: label.style.width
});
}
else {
Expand Down
18 changes: 18 additions & 0 deletions test/lib/testHelper.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

150 changes: 150 additions & 0 deletions test/pie-label-alignTo-adjust.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 933c06f

Please sign in to comment.