Skip to content

Commit

Permalink
allow labels to extend out of a chart's boundary with padding: 'inf'.
Browse files Browse the repository at this point in the history
  • Loading branch information
chanwutk committed Jun 27, 2021
1 parent 2d14624 commit 2a7695c
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 13 deletions.
2 changes: 1 addition & 1 deletion packages/vega-label/src/Label.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const Anchors = [
* 'bottom', 'top-right', 'right', 'bottom-right', 'middle'.
* @param {Array<number>} [params.offset] - Label offsets (in pixels) from the base mark bounding box.
* This parameter is parallel to the list of anchor points.
* @param {number} [params.padding=0] - The amount (in pixels) that a label may exceed the layout size.
* @param {number | 'inf'} [params.padding=0] - The amount (in pixels) that a label may exceed the layout size.
* @param {string} [params.lineAnchor='end'] - For group line mark labels only, indicates the anchor
* position for labels. One of 'start' or 'end'.
* @param {string} [params.markIndex=0] - For group mark labels only, an index indicating
Expand Down
4 changes: 2 additions & 2 deletions packages/vega-label/src/LabelLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ export default function(texts, size, compare, offset, anchor,

// generate label placement function
const place = isGroupArea
? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex)
: placeMarkLabel($, bitmaps, anchors, offsets);
? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex, padding)
: placeMarkLabel($, bitmaps, anchors, offsets, padding);

// place all labels
data.forEach(d => d.opacity = +place(d));
Expand Down
29 changes: 27 additions & 2 deletions packages/vega-label/src/util/placeAreaLabel/common.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
export function outOfBounds(x, y, textWidth, textHeight, width, height) {
function outOfBounds(x, y, textWidth, textHeight, width, height) {
let r = textWidth / 2;
return x - r < 0
|| x + r > width
|| y - (r = textHeight / 2) < 0
|| y + r > height;
}

export function collision($, x, y, textHeight, textWidth, h, bm0, bm1) {
function _outOfBounds() {
return false;
}

function collision($, x, y, textHeight, textWidth, h, bm0, bm1) {
const w = (textWidth * h) / (textHeight * 2),
x1 = $(x - w),
x2 = $(x + w),
Expand All @@ -16,4 +20,25 @@ export function collision($, x, y, textHeight, textWidth, h, bm0, bm1) {
return bm0.outOfBounds(x1, y1, x2, y2)
|| bm0.getRange(x1, y1, x2, y2)
|| (bm1 && bm1.getRange(x1, y1, x2, y2));
}

function _collision($, x, y, textHeight, textWidth, h, bm0, bm1) {
const w = (textWidth * h) / (textHeight * 2);
let x1 = $(x - w),
x2 = $(x + w),
y1 = $(y - (h = h/2)),
y2 = $(y + h);

x1 = x1 > 0 ? x1 : 0;
y1 = y1 > 0 ? y1 : 0;
x2 = x2 < $.width ? x2 : $.width - 1;
y2 = y2 < $.height ? y2 : $.height - 1;

return bm0.getRange(x1, y1, x2, y2) || (bm1 && bm1.getRange(x1, y1, x2, y2));
}

export function getTests(padding) {
return padding === 'inf'
? [_collision, _outOfBounds]
: [collision, outOfBounds];
}
5 changes: 3 additions & 2 deletions packages/vega-label/src/util/placeAreaLabel/placeFloodFill.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {textMetrics} from 'vega-scenegraph';
import {collision, outOfBounds} from './common';
import {getTests} from './common';

// pixel direction offsets for flood fill search
const X_DIR = [-1, -1, 1, 1];
const Y_DIR = [-1, 1, -1, 1];

export default function($, bitmaps, avoidBaseMark, markIndex) {
export default function($, bitmaps, avoidBaseMark, markIndex, padding) {
const width = $.width,
height = $.height,
[collision, outOfBounds] = getTests(padding),
bm0 = bitmaps[0], // where labels have been placed
bm1 = bitmaps[1], // area outlines
bm2 = $.bitmap(); // flood-fill visitations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {textMetrics} from 'vega-scenegraph';
import {collision, outOfBounds} from './common';
import {getTests} from './common';

export default function($, bitmaps, avoidBaseMark, markIndex) {
export default function($, bitmaps, avoidBaseMark, markIndex, padding) {
const width = $.width,
height = $.height,
[collision, outOfBounds] = getTests(padding),
bm0 = bitmaps[0], // where labels have been placed
bm1 = bitmaps[1]; // area outlines

Expand Down
18 changes: 15 additions & 3 deletions packages/vega-label/src/util/placeMarkLabel.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@ import {textMetrics} from 'vega-scenegraph';
const Aligns = ['right', 'center', 'left'],
Baselines = ['bottom', 'middle', 'top'];

export default function($, bitmaps, anchors, offsets) {
export default function($, bitmaps, anchors, offsets, padding) {
const width = $.width,
height = $.height,
bm0 = bitmaps[0],
bm1 = bitmaps[1],
n = offsets.length;
n = offsets.length,
infPadding = padding === 'inf';

return function(d) {
const boundary = d.boundary,
textHeight = d.datum.fontSize;

// can not be placed if the mark is not visible in the graph bound
if (boundary[2] < 0 || boundary[5] < 0 || boundary[0] > width || boundary[3] > height) {
if (!infPadding && (boundary[2] < 0 || boundary[5] < 0 || boundary[0] > width || boundary[3] > height)) {
return false;
}

Expand All @@ -42,6 +43,12 @@ export default function($, bitmaps, anchors, offsets) {
_y1 = $(y1);
_y2 = $(y2);

if (infPadding) {
_x1 = _x1 < 0 ? 0 : _x1;
_y1 = _y1 < 0 ? 0 : _y1;
_y2 = _y2 >= $.height ? ($.height - 1) : _y2;
}

if (!textWidth) {
// to avoid finding width of text label,
if (!test(_x1, _x1, _y1, _y2, bm0, bm1, x1, x1, y1, y2, boundary, isInside)) {
Expand All @@ -60,6 +67,11 @@ export default function($, bitmaps, anchors, offsets) {
_x1 = $(x1);
_x2 = $(x2);

if (infPadding) {
_x1 = _x1 < 0 ? 0 : _x1;
_x2 = _x2 >= $.width ? ($.width - 1) : _x2;
}

if (test(_x1, _x2, _y1, _y2, bm0, bm1, x1, x2, y1, y2, boundary, isInside)) {
// place label if the position is placeable
d.x = !dx ? xc : dx * insideFactor < 0 ? x2 : x1;
Expand Down
2 changes: 1 addition & 1 deletion packages/vega-typings/types/spec/transform.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ export interface LabelTransform {
sort?: Compare;
offset?: number[] | number | SignalRef;
anchor?: LabelAnchor[] | LabelAnchor | SignalRef;
padding?: number | SignalRef;
padding?: number | 'inf' | SignalRef;
markIndex?: number;
lineAnchor?: LineLabelAnchor | SignalRef;
avoidBaseMark?: boolean | SignalRef;
Expand Down

0 comments on commit 2a7695c

Please sign in to comment.