Socket
Socket
Sign inDemoInstall

vega-label

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vega-label - npm Package Compare versions

Comparing version 1.2.0 to 1.2.1

rollup.config.mjs

480

build/vega-label.js

@@ -7,6 +7,7 @@ (function (global, factory) {

// bit mask for getting first 2 bytes of alpha value
const ALPHA_MASK = 0xff000000;
function baseBitmaps($, data) {
const bitmap = $.bitmap(); // when there is no base mark but data points are to be avoided
const bitmap = $.bitmap();
// when there is no base mark but data points are to be avoided
(data || []).forEach(d => bitmap.set($(d.boundary[0]), $(d.boundary[3])));

@@ -18,24 +19,24 @@ return [bitmap, undefined];

const width = $.width,
height = $.height,
border = labelInside || isGroupArea,
context = vegaCanvas.canvas(width, height).getContext('2d'),
baseMarkContext = vegaCanvas.canvas(width, height).getContext('2d'),
strokeContext = border && vegaCanvas.canvas(width, height).getContext('2d'); // render all marks to be avoided into canvas
height = $.height,
border = labelInside || isGroupArea,
context = vegaCanvas.canvas(width, height).getContext('2d'),
baseMarkContext = vegaCanvas.canvas(width, height).getContext('2d'),
strokeContext = border && vegaCanvas.canvas(width, height).getContext('2d');
// render all marks to be avoided into canvas
avoidMarks.forEach(items => draw(context, items, false));
draw(baseMarkContext, baseMark, false);
if (border) {
draw(strokeContext, baseMark, true);
} // get canvas buffer, create bitmaps
}
// get canvas buffer, create bitmaps
const buffer = getBuffer(context, width, height),
baseMarkBuffer = getBuffer(baseMarkContext, width, height),
strokeBuffer = border && getBuffer(strokeContext, width, height),
layer1 = $.bitmap(),
layer2 = border && $.bitmap(); // populate bitmap layers
baseMarkBuffer = getBuffer(baseMarkContext, width, height),
strokeBuffer = border && getBuffer(strokeContext, width, height),
layer1 = $.bitmap(),
layer2 = border && $.bitmap();
// populate bitmap layers
let x, y, u, v, index, alpha, strokeAlpha, baseMarkAlpha;
for (y = 0; y < height; ++y) {

@@ -47,3 +48,2 @@ for (x = 0; x < width; ++x) {

strokeAlpha = border && strokeBuffer[index] & ALPHA_MASK;
if (alpha || strokeAlpha || baseMarkAlpha) {

@@ -53,3 +53,2 @@ u = $(x);

if (!isGroupArea && (alpha || baseMarkAlpha)) layer1.set(u, v); // update interior bitmap
if (border && (alpha || strokeAlpha)) layer2.set(u, v); // update border bitmap

@@ -62,11 +61,8 @@ }

}
function getBuffer(context, width, height) {
return new Uint32Array(context.getImageData(0, 0, width, height).data.buffer);
}
function draw(context, items, interior) {
if (!items.length) return;
const type = items[0].mark.marktype;
if (type === 'group') {

@@ -82,2 +78,3 @@ items.forEach(group => {

}
/**

@@ -88,9 +85,7 @@ * Prepare item before drawing into canvas (setting stroke and opacity)

*/
function prepare(source) {
const item = vegaDataflow.rederive(source, {});
if (item.stroke && item.strokeOpacity !== 0 || item.fill && item.fillOpacity !== 0) {
return { ...item,
return {
...item,
strokeOpacity: 1,

@@ -101,3 +96,2 @@ stroke: '#000',

}
return item;

@@ -107,14 +101,13 @@ }

const DIV = 5,
// bit shift from x, y index to bit vector array index
MOD = 31,
// bit mask for index lookup within a bit vector
SIZE = 32,
// individual bit vector size
RIGHT0 = new Uint32Array(SIZE + 1),
// left-anchored bit vectors, full -> 0
RIGHT1 = new Uint32Array(SIZE + 1); // right-anchored bit vectors, 0 -> full
// bit shift from x, y index to bit vector array index
MOD = 31,
// bit mask for index lookup within a bit vector
SIZE = 32,
// individual bit vector size
RIGHT0 = new Uint32Array(SIZE + 1),
// left-anchored bit vectors, full -> 0
RIGHT1 = new Uint32Array(SIZE + 1); // right-anchored bit vectors, 0 -> full
RIGHT1[0] = 0;
RIGHT0[0] = ~RIGHT1[0];
for (let i = 1; i <= SIZE; ++i) {

@@ -124,14 +117,10 @@ RIGHT1[i] = RIGHT1[i - 1] << 1 | 1;

}
function Bitmap (w, h) {
const array = new Uint32Array(~~((w * h + SIZE) / SIZE));
function _set(index, mask) {
array[index] |= mask;
}
function _clear(index, mask) {
array[index] &= mask;
}
return {

@@ -145,3 +134,2 @@ array: array,

const index = y * w + x;
_set(index >>> DIV, 1 << (index & MOD));

@@ -151,3 +139,2 @@ },

const index = y * w + x;
_clear(index >>> DIV, ~(1 << (index & MOD)));

@@ -157,7 +144,6 @@ },

let r = y2,
start,
end,
indexStart,
indexEnd;
start,
end,
indexStart,
indexEnd;
for (; r >= y; --r) {

@@ -168,3 +154,2 @@ start = r * w + x;

indexEnd = end >>> DIV;
if (indexStart === indexEnd) {

@@ -177,3 +162,2 @@ if (array[indexStart] & RIGHT0[start & MOD] & RIGHT1[(end & MOD) + 1]) {

if (array[indexEnd] & RIGHT1[(end & MOD) + 1]) return true;
for (let i = indexStart + 1; i < indexEnd; ++i) {

@@ -184,3 +168,2 @@ if (array[i]) return true;

}
return false;

@@ -190,3 +173,2 @@ },

let start, end, indexStart, indexEnd, i;
for (; y <= y2; ++y) {

@@ -197,3 +179,2 @@ start = y * w + x;

indexEnd = end >>> DIV;
if (indexStart === indexEnd) {

@@ -203,5 +184,3 @@ _set(indexStart, RIGHT0[start & MOD] & RIGHT1[(end & MOD) + 1]);

_set(indexStart, RIGHT0[start & MOD]);
_set(indexEnd, RIGHT1[(end & MOD) + 1]);
for (i = indexStart + 1; i < indexEnd; ++i) _set(i, 0xffffffff);

@@ -213,3 +192,2 @@ }

let start, end, indexStart, indexEnd, i;
for (; y <= y2; ++y) {

@@ -220,3 +198,2 @@ start = y * w + x;

indexEnd = end >>> DIV;
if (indexStart === indexEnd) {

@@ -226,5 +203,3 @@ _clear(indexStart, RIGHT1[start & MOD] | RIGHT0[(end & MOD) + 1]);

_clear(indexStart, RIGHT1[start & MOD]);
_clear(indexEnd, RIGHT0[(end & MOD) + 1]);
for (i = indexStart + 1; i < indexEnd; ++i) _clear(i, 0);

@@ -240,10 +215,7 @@ }

const ratio = Math.max(1, Math.sqrt(width * height / 1e6)),
w = ~~((width + 2 * padding + ratio) / ratio),
h = ~~((height + 2 * padding + ratio) / ratio),
scale = _ => ~~((_ + padding) / ratio);
w = ~~((width + 2 * padding + ratio) / ratio),
h = ~~((height + 2 * padding + ratio) / ratio),
scale = _ => ~~((_ + padding) / ratio);
scale.invert = _ => _ * ratio - padding;
scale.bitmap = () => Bitmap(w, h);
scale.ratio = ratio;

@@ -258,22 +230,24 @@ scale.padding = padding;

const width = $.width,
height = $.height; // try to place a label within an input area mark
height = $.height;
// try to place a label within an input area mark
return function (d) {
const items = d.datum.datum.items[markIndex].items,
// area points
n = items.length,
// number of points
textHeight = d.datum.fontSize,
// label width
textWidth = vegaScenegraph.textMetrics.width(d.datum, d.datum.text); // label height
// area points
n = items.length,
// number of points
textHeight = d.datum.fontSize,
// label width
textWidth = vegaScenegraph.textMetrics.width(d.datum, d.datum.text); // label height
let maxAreaWidth = 0,
x1,
x2,
y1,
y2,
x,
y,
areaWidth; // for each area sample point
x1,
x2,
y1,
y2,
x,
y,
areaWidth;
// for each area sample point
for (let i = 0; i < n; ++i) {

@@ -287,3 +261,2 @@ x1 = items[i].x;

areaWidth = Math.abs(x2 - x1 + y2 - y1);
if (areaWidth >= maxAreaWidth) {

@@ -295,3 +268,2 @@ maxAreaWidth = areaWidth;

}
x = textWidth / 2;

@@ -304,3 +276,2 @@ y = textHeight / 2;

d.align = 'center';
if (x1 < 0 && x2 <= width) {

@@ -311,5 +282,3 @@ d.align = 'left';

}
d.baseline = 'middle';
if (y1 < 0 && y2 <= height) {

@@ -320,3 +289,2 @@ d.baseline = 'top';

}
return true;

@@ -332,6 +300,6 @@ };

const w = textWidth * h / (textHeight * 2),
x1 = $(x - w),
x2 = $(x + w),
y1 = $(y - (h = h / 2)),
y2 = $(y + h);
x1 = $(x - w),
x2 = $(x + w),
y1 = $(y - (h = h / 2)),
y2 = $(y + h);
return bm0.outOfBounds(x1, y1, x2, y2) || bm0.getRange(x1, y1, x2, y2) || bm1 && bm1.getRange(x1, y1, x2, y2);

@@ -342,14 +310,13 @@ }

const width = $.width,
height = $.height,
bm0 = bitmaps[0],
// where labels have been placed
bm1 = bitmaps[1]; // area outlines
height = $.height,
bm0 = bitmaps[0],
// where labels have been placed
bm1 = bitmaps[1]; // area outlines
function tryLabel(_x, _y, maxSize, textWidth, textHeight) {
const x = $.invert(_x),
y = $.invert(_y);
y = $.invert(_y);
let lo = maxSize,
hi = height,
mid;
hi = height,
mid;
if (!outOfBounds(x, y, textWidth, textHeight, width, height) && !collision($, x, y, textHeight, textWidth, lo, bm0, bm1) && !collision($, x, y, textHeight, textWidth, textHeight, bm0, null)) {

@@ -360,3 +327,2 @@ // if the label fits at the current sample point,

mid = (lo + hi) / 2;
if (collision($, x, y, textHeight, textWidth, mid, bm0, bm1)) {

@@ -367,5 +333,4 @@ hi = mid;

}
} // place label if current lower bound exceeds prior max font size
}
// place label if current lower bound exceeds prior max font size
if (lo > maxSize) {

@@ -375,37 +340,37 @@ return [x, y, lo, true];

}
} // try to place a label within an input area mark
}
// try to place a label within an input area mark
return function (d) {
const items = d.datum.datum.items[markIndex].items,
// area points
n = items.length,
// number of points
textHeight = d.datum.fontSize,
// label width
textWidth = vegaScenegraph.textMetrics.width(d.datum, d.datum.text); // label height
// area points
n = items.length,
// number of points
textHeight = d.datum.fontSize,
// label width
textWidth = vegaScenegraph.textMetrics.width(d.datum, d.datum.text); // label height
let maxSize = avoidBaseMark ? textHeight : 0,
labelPlaced = false,
labelPlaced2 = false,
maxAreaWidth = 0,
x1,
x2,
y1,
y2,
x,
y,
_x,
_y,
_x1,
_xMid,
_x2,
_y1,
_yMid,
_y2,
areaWidth,
result,
swapTmp; // for each area sample point
labelPlaced = false,
labelPlaced2 = false,
maxAreaWidth = 0,
x1,
x2,
y1,
y2,
x,
y,
_x,
_y,
_x1,
_xMid,
_x2,
_y1,
_yMid,
_y2,
areaWidth,
result,
swapTmp;
// for each area sample point
for (let i = 0; i < n; ++i) {

@@ -416,3 +381,2 @@ x1 = items[i].x;

y2 = items[i].y2 === undefined ? y1 : items[i].y2;
if (x1 > x2) {

@@ -423,3 +387,2 @@ swapTmp = x1;

}
if (y1 > y2) {

@@ -430,3 +393,2 @@ swapTmp = y1;

}
_x1 = $(x1);

@@ -437,8 +399,8 @@ _x2 = $(x2);

_y2 = $(y2);
_yMid = ~~((_y1 + _y2) / 2); // search along the line from mid point between the 2 border to lower border
_yMid = ~~((_y1 + _y2) / 2);
// search along the line from mid point between the 2 border to lower border
for (_x = _xMid; _x >= _x1; --_x) {
for (_y = _yMid; _y >= _y1; --_y) {
result = tryLabel(_x, _y, maxSize, textWidth, textHeight);
if (result) {

@@ -448,9 +410,8 @@ [d.x, d.y, maxSize, labelPlaced] = result;

}
} // search along the line from mid point between the 2 border to upper border
}
// search along the line from mid point between the 2 border to upper border
for (_x = _xMid; _x <= _x2; ++_x) {
for (_y = _yMid; _y <= _y2; ++_y) {
result = tryLabel(_x, _y, maxSize, textWidth, textHeight);
if (result) {

@@ -460,6 +421,6 @@ [d.x, d.y, maxSize, labelPlaced] = result;

}
} // place label at slice center if not placed through other means
}
// place label at slice center if not placed through other means
// and if we're not avoiding overlap with other areas
if (!labelPlaced && !avoidBaseMark) {

@@ -469,4 +430,5 @@ // one span is zero, hence we can add

x = (x1 + x2) / 2;
y = (y1 + y2) / 2; // place label if it fits and improves the max area width
y = (y1 + y2) / 2;
// place label if it fits and improves the max area width
if (areaWidth >= maxAreaWidth && !outOfBounds(x, y, textWidth, textHeight, width, height) && !collision($, x, y, textHeight, textWidth, textHeight, bm0, null)) {

@@ -479,5 +441,5 @@ maxAreaWidth = areaWidth;

}
} // record current label placement information, update label bitmap
}
// record current label placement information, update label bitmap
if (labelPlaced || labelPlaced2) {

@@ -496,2 +458,3 @@ x = textWidth / 2;

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

@@ -501,39 +464,39 @@ const Y_DIR = [-1, 1, -1, 1];

const width = $.width,
height = $.height,
bm0 = bitmaps[0],
// where labels have been placed
bm1 = bitmaps[1],
// area outlines
bm2 = $.bitmap(); // flood-fill visitations
height = $.height,
bm0 = bitmaps[0],
// where labels have been placed
bm1 = bitmaps[1],
// area outlines
bm2 = $.bitmap(); // flood-fill visitations
// try to place a label within an input area mark
return function (d) {
const items = d.datum.datum.items[markIndex].items,
// area points
n = items.length,
// number of points
textHeight = d.datum.fontSize,
// label width
textWidth = vegaScenegraph.textMetrics.width(d.datum, d.datum.text),
// label height
stack = []; // flood fill stack
// area points
n = items.length,
// number of points
textHeight = d.datum.fontSize,
// label width
textWidth = vegaScenegraph.textMetrics.width(d.datum, d.datum.text),
// label height
stack = []; // flood fill stack
let maxSize = avoidBaseMark ? textHeight : 0,
labelPlaced = false,
labelPlaced2 = false,
maxAreaWidth = 0,
x1,
x2,
y1,
y2,
x,
y,
_x,
_y,
lo,
hi,
mid,
areaWidth; // for each area sample point
labelPlaced = false,
labelPlaced2 = false,
maxAreaWidth = 0,
x1,
x2,
y1,
y2,
x,
y,
_x,
_y,
lo,
hi,
mid,
areaWidth;
// for each area sample point
for (let i = 0; i < n; ++i) {

@@ -543,14 +506,17 @@ x1 = items[i].x;

x2 = items[i].x2 === undefined ? x1 : items[i].x2;
y2 = items[i].y2 === undefined ? y1 : items[i].y2; // add scaled center point to stack
y2 = items[i].y2 === undefined ? y1 : items[i].y2;
stack.push([$((x1 + x2) / 2), $((y1 + y2) / 2)]); // perform flood fill, visit points
// add scaled center point to stack
stack.push([$((x1 + x2) / 2), $((y1 + y2) / 2)]);
// perform flood fill, visit points
while (stack.length) {
[_x, _y] = stack.pop(); // exit if point already marked
[_x, _y] = stack.pop();
if (bm0.get(_x, _y) || bm1.get(_x, _y) || bm2.get(_x, _y)) continue; // mark point in flood fill bitmap
// exit if point already marked
if (bm0.get(_x, _y) || bm1.get(_x, _y) || bm2.get(_x, _y)) continue;
// mark point in flood fill bitmap
// add search points for all (in bound) directions
bm2.set(_x, _y);
for (let j = 0; j < 4; ++j) {

@@ -560,5 +526,5 @@ x = _x + X_DIR[j];

if (!bm2.outOfBounds(x, y, x, y)) stack.push([x, y]);
} // unscale point back to x, y space
}
// unscale point back to x, y space
x = $.invert(_x);

@@ -574,3 +540,2 @@ y = $.invert(_y);

mid = (lo + hi) / 2;
if (collision($, x, y, textHeight, textWidth, mid, bm0, bm1)) {

@@ -581,5 +546,4 @@ hi = mid;

}
} // place label if current lower bound exceeds prior max font size
}
// place label if current lower bound exceeds prior max font size
if (lo > maxSize) {

@@ -592,6 +556,6 @@ d.x = x;

}
} // place label at slice center if not placed through other means
}
// place label at slice center if not placed through other means
// and if we're not avoiding overlap with other areas
if (!labelPlaced && !avoidBaseMark) {

@@ -601,4 +565,5 @@ // one span is zero, hence we can add

x = (x1 + x2) / 2;
y = (y1 + y2) / 2; // place label if it fits and improves the max area width
y = (y1 + y2) / 2;
// place label if it fits and improves the max area width
if (areaWidth >= maxAreaWidth && !outOfBounds(x, y, textWidth, textHeight, width, height) && !collision($, x, y, textHeight, textWidth, textHeight, bm0, null)) {

@@ -611,5 +576,5 @@ maxAreaWidth = areaWidth;

}
} // record current label placement information, update label bitmap
}
// record current label placement information, update label bitmap
if (labelPlaced || labelPlaced2) {

@@ -629,35 +594,35 @@ x = textWidth / 2;

const Aligns = ['right', 'center', 'left'],
Baselines = ['bottom', 'middle', 'top'];
Baselines = ['bottom', 'middle', 'top'];
function placeMarkLabel ($, bitmaps, anchors, offsets) {
const width = $.width,
height = $.height,
bm0 = bitmaps[0],
bm1 = bitmaps[1],
n = offsets.length;
height = $.height,
bm0 = bitmaps[0],
bm1 = bitmaps[1],
n = offsets.length;
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
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) {
return false;
}
let textWidth = d.textWidth ?? 0,
dx,
dy,
isInside,
sizeFactor,
insideFactor,
x1,
x2,
y1,
y2,
xc,
yc,
_x1,
_x2,
_y1,
_y2; // for each anchor and offset
dx,
dy,
isInside,
sizeFactor,
insideFactor,
x1,
x2,
y1,
y2,
xc,
yc,
_x1,
_x2,
_y1,
_y2;
// for each anchor and offset
for (let i = 0; i < n; ++i) {

@@ -676,3 +641,2 @@ dx = (anchors[i] & 0x3) - 1;

_y2 = $(y2);
if (!textWidth) {

@@ -688,3 +652,2 @@ // to avoid finding width of text label,

}
xc = x1 + insideFactor * textWidth * dx / 2;

@@ -695,3 +658,2 @@ x1 = xc - textWidth / 2;

_x2 = $(x2);
if (test(_x1, _x2, _y1, _y2, bm0, bm1, x1, x2, y1, y2, boundary, isInside)) {

@@ -707,7 +669,7 @@ // place label if the position is placeable

}
return false;
};
} // Test if a label with the given dimensions can be added without overlap
}
// Test if a label with the given dimensions can be added without overlap
function test(_x1, _x2, _y1, _y2, bm0, bm1, x1, x2, y1, y2, boundary, isInside) {

@@ -717,9 +679,11 @@ return !(bm0.outOfBounds(_x1, _y1, _x2, _y2) || (isInside && bm1 || bm0).getRange(_x1, _y1, _x2, _y2));

// 8-bit representation of anchors
const TOP = 0x0,
MIDDLE = 0x4,
BOTTOM = 0x8,
LEFT = 0x0,
CENTER = 0x1,
RIGHT = 0x2; // Mapping from text anchor to number representation
MIDDLE = 0x4,
BOTTOM = 0x8,
LEFT = 0x0,
CENTER = 0x1,
RIGHT = 0x2;
// Mapping from text anchor to number representation
const anchorCode = {

@@ -745,13 +709,14 @@ 'top-left': TOP + LEFT,

const positions = Math.max(offset.length, anchor.length),
offsets = getOffsets(offset, positions),
anchors = getAnchors(anchor, positions),
marktype = markType(texts[0].datum),
grouptype = marktype === 'group' && texts[0].datum.items[markIndex].marktype,
isGroupArea = grouptype === 'area',
boundary = markBoundary(marktype, grouptype, lineAnchor, markIndex),
infPadding = padding === null || padding === Infinity,
isNaiveGroupArea = isGroupArea && method === 'naive';
offsets = getOffsets(offset, positions),
anchors = getAnchors(anchor, positions),
marktype = markType(texts[0].datum),
grouptype = marktype === 'group' && texts[0].datum.items[markIndex].marktype,
isGroupArea = grouptype === 'area',
boundary = markBoundary(marktype, grouptype, lineAnchor, markIndex),
infPadding = padding === null || padding === Infinity,
isNaiveGroupArea = isGroupArea && method === 'naive';
let maxTextWidth = -1,
maxTextHeight = -1; // prepare text mark data for placing
maxTextHeight = -1;
// prepare text mark data for placing
const data = texts.map(d => {

@@ -775,3 +740,2 @@ const textWidth = infPadding ? vegaScenegraph.textMetrics.width(d, d.text) : undefined;

let bitmaps;
if (!isNaiveGroupArea) {

@@ -781,7 +745,6 @@ // sort labels in priority order, if comparator is provided

data.sort((a, b) => compare(a.datum, b.datum));
} // flag indicating if label can be placed inside its base mark
}
// flag indicating if label can be placed inside its base mark
let labelInside = false;
for (let i = 0; i < anchors.length && !labelInside; ++i) {

@@ -791,43 +754,37 @@ // label inside if anchor is at center

labelInside = anchors[i] === 0x5 || offsets[i] < 0;
} // extract data information from base mark when base mark is to be avoided
}
// extract data information from base mark when base mark is to be avoided
// base mark is implicitly avoided if it is a group area
const baseMark = (marktype && avoidBaseMark || isGroupArea) && texts.map(d => d.datum);
const baseMark = (marktype && avoidBaseMark || isGroupArea) && texts.map(d => d.datum); // generate bitmaps for layout calculation
// generate bitmaps for layout calculation
bitmaps = avoidMarks.length || baseMark ? markBitmaps($, baseMark || [], avoidMarks, labelInside, isGroupArea) : baseBitmaps($, avoidBaseMark && data);
} // generate label placement function
}
// generate label placement function
const place = isGroupArea ? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex) : placeMarkLabel($, bitmaps, anchors, offsets);
const place = isGroupArea ? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex) : placeMarkLabel($, bitmaps, anchors, offsets); // place all labels
// place all labels
data.forEach(d => d.opacity = +place(d));
return data;
}
function getOffsets(_, count) {
const offsets = new Float64Array(count),
n = _.length;
n = _.length;
for (let i = 0; i < n; ++i) offsets[i] = _[i] || 0;
for (let i = n; i < count; ++i) offsets[i] = offsets[n - 1];
return offsets;
}
function getAnchors(_, count) {
const anchors = new Int8Array(count),
n = _.length;
n = _.length;
for (let i = 0; i < n; ++i) anchors[i] |= anchorCode[_[i]];
for (let i = n; i < count; ++i) anchors[i] = anchors[n - 1];
return anchors;
}
function markType(item) {
return item && item.mark && item.mark.marktype;
}
/**

@@ -840,7 +797,4 @@ * Factory function for function for getting base mark boundary, depending

*/
function markBoundary(marktype, grouptype, lineAnchor, markIndex) {
const xy = d => [d.x, d.x, d.x, d.y, d.y, d.y];
if (!marktype) {

@@ -868,2 +822,3 @@ return xy; // no reactive geometry

const Anchors = ['top-left', 'left', 'bottom-left', 'top', 'bottom', 'top-right', 'right', 'bottom-right'];
/**

@@ -896,3 +851,2 @@ * Compute text label layout to annotate marks.

*/
function Label(params) {

@@ -965,13 +919,10 @@ vegaDataflow.Transform.call(this, null, params);

}
const mod = _.modified();
if (!(mod || pulse.changed(pulse.ADD_REM) || modp('sort'))) return;
if (!_.size || _.size.length !== 2) {
vegaUtil.error('Size parameter should be specified as a [width, height] array.');
}
const as = _.as || Output;
const as = _.as || Output; // run label layout
// run label layout
labelLayout(pulse.materialize(pulse.SOURCE).source || [], _.size, _.sort, vegaUtil.array(_.offset == null ? 1 : _.offset), vegaUtil.array(_.anchor || Anchors), _.avoidMarks || [], _.avoidBaseMark !== false, _.lineAnchor || 'end', _.markIndex || 0, _.padding === undefined ? 0 : _.padding, _.method || 'naive').forEach(l => {

@@ -988,3 +939,2 @@ // write layout results to data stream

}
});

@@ -994,4 +944,2 @@

Object.defineProperty(exports, '__esModule', { value: true });
}));

@@ -1,2 +0,2 @@

!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("vega-scenegraph"),require("vega-canvas"),require("vega-dataflow"),require("vega-util")):"function"==typeof define&&define.amd?define(["exports","vega-scenegraph","vega-canvas","vega-dataflow","vega-util"],e):e(((t="undefined"!=typeof globalThis?globalThis:t||self).vega=t.vega||{},t.vega.transforms={}),t.vega,t.vega,t.vega,t.vega)}(this,(function(t,e,n,a,r){"use strict";const i=4278190080;function o(t,e,n){return new Uint32Array(t.getImageData(0,0,e,n).data.buffer)}function u(t,n,a){if(!n.length)return;const r=n[0].mark.marktype;"group"===r?n.forEach((e=>{e.items.forEach((e=>u(t,e.items,a)))})):e.Marks[r].draw(t,{items:a?n.map(s):n})}function s(t){const e=a.rederive(t,{});return e.stroke&&0!==e.strokeOpacity||e.fill&&0!==e.fillOpacity?{...e,strokeOpacity:1,stroke:"#000",fillOpacity:0}:e}const l=31,f=new Uint32Array(33),d=new Uint32Array(33);d[0]=0,f[0]=~d[0];for(let t=1;t<=32;++t)d[t]=d[t-1]<<1|1,f[t]=~d[t];function c(t,e,n){const a=Math.max(1,Math.sqrt(t*e/1e6)),r=~~((t+2*n+a)/a),i=~~((e+2*n+a)/a),o=t=>~~((t+n)/a);return o.invert=t=>t*a-n,o.bitmap=()=>function(t,e){const n=new Uint32Array(~~((t*e+32)/32));function a(t,e){n[t]|=e}function r(t,e){n[t]&=e}return{array:n,get:(e,a)=>{const r=a*t+e;return n[r>>>5]&1<<(r&l)},set:(e,n)=>{const r=n*t+e;a(r>>>5,1<<(r&l))},clear:(e,n)=>{const a=n*t+e;r(a>>>5,~(1<<(a&l)))},getRange:(e,a,r,i)=>{let o,u,s,c,m=i;for(;m>=a;--m)if(o=m*t+e,u=m*t+r,s=o>>>5,c=u>>>5,s===c){if(n[s]&f[o&l]&d[1+(u&l)])return!0}else{if(n[s]&f[o&l])return!0;if(n[c]&d[1+(u&l)])return!0;for(let t=s+1;t<c;++t)if(n[t])return!0}return!1},setRange:(e,n,r,i)=>{let o,u,s,c,m;for(;n<=i;++n)if(o=n*t+e,u=n*t+r,s=o>>>5,c=u>>>5,s===c)a(s,f[o&l]&d[1+(u&l)]);else for(a(s,f[o&l]),a(c,d[1+(u&l)]),m=s+1;m<c;++m)a(m,4294967295)},clearRange:(e,n,a,i)=>{let o,u,s,c,m;for(;n<=i;++n)if(o=n*t+e,u=n*t+a,s=o>>>5,c=u>>>5,s===c)r(s,d[o&l]|f[1+(u&l)]);else for(r(s,d[o&l]),r(c,f[1+(u&l)]),m=s+1;m<c;++m)r(m,0)},outOfBounds:(n,a,r,i)=>n<0||a<0||i>=e||r>=t}}(r,i),o.ratio=a,o.padding=n,o.width=t,o.height=e,o}function m(t,e,n,a,r,i){let o=n/2;return t-o<0||t+o>r||e-(o=a/2)<0||e+o>i}function g(t,e,n,a,r,i,o,u){const s=r*i/(2*a),l=t(e-s),f=t(e+s),d=t(n-(i/=2)),c=t(n+i);return o.outOfBounds(l,d,f,c)||o.getRange(l,d,f,c)||u&&u.getRange(l,d,f,c)}const h=[-1,-1,1,1],y=[-1,1,-1,1];const p=["right","center","left"],x=["bottom","middle","top"];function v(t,e,n,a,r,i,o,u,s,l,f,d){return!(r.outOfBounds(t,n,e,a)||(d&&i||r).getRange(t,n,e,a))}const b={"top-left":0,top:1,"top-right":2,left:4,middle:5,right:6,"bottom-left":8,bottom:9,"bottom-right":10},M={naive:function(t,n,a,r){const i=t.width,o=t.height;return function(t){const n=t.datum.datum.items[r].items,a=n.length,u=t.datum.fontSize,s=e.textMetrics.width(t.datum,t.datum.text);let l,f,d,c,m,g,h,y=0;for(let e=0;e<a;++e)l=n[e].x,d=n[e].y,f=void 0===n[e].x2?l:n[e].x2,c=void 0===n[e].y2?d:n[e].y2,m=(l+f)/2,g=(d+c)/2,h=Math.abs(f-l+c-d),h>=y&&(y=h,t.x=m,t.y=g);return m=s/2,g=u/2,l=t.x-m,f=t.x+m,d=t.y-g,c=t.y+g,t.align="center",l<0&&f<=i?t.align="left":0<=l&&i<f&&(t.align="right"),t.baseline="middle",d<0&&c<=o?t.baseline="top":0<=d&&o<c&&(t.baseline="bottom"),!0}},"reduced-search":function(t,n,a,r){const i=t.width,o=t.height,u=n[0],s=n[1];function l(e,n,a,r,l){const f=t.invert(e),d=t.invert(n);let c,h=a,y=o;if(!m(f,d,r,l,i,o)&&!g(t,f,d,l,r,h,u,s)&&!g(t,f,d,l,r,l,u,null)){for(;y-h>=1;)c=(h+y)/2,g(t,f,d,l,r,c,u,s)?y=c:h=c;if(h>a)return[f,d,h,!0]}}return function(n){const s=n.datum.datum.items[r].items,f=s.length,d=n.datum.fontSize,c=e.textMetrics.width(n.datum,n.datum.text);let h,y,p,x,v,b,M,w,k,R,z,O,A,E,S,q,B,T=a?d:0,U=!1,C=!1,D=0;for(let e=0;e<f;++e){for(h=s[e].x,p=s[e].y,y=void 0===s[e].x2?h:s[e].x2,x=void 0===s[e].y2?p:s[e].y2,h>y&&(B=h,h=y,y=B),p>x&&(B=p,p=x,x=B),k=t(h),z=t(y),R=~~((k+z)/2),O=t(p),E=t(x),A=~~((O+E)/2),M=R;M>=k;--M)for(w=A;w>=O;--w)q=l(M,w,T,c,d),q&&([n.x,n.y,T,U]=q);for(M=R;M<=z;++M)for(w=A;w<=E;++w)q=l(M,w,T,c,d),q&&([n.x,n.y,T,U]=q);U||a||(S=Math.abs(y-h+x-p),v=(h+y)/2,b=(p+x)/2,S>=D&&!m(v,b,c,d,i,o)&&!g(t,v,b,d,c,d,u,null)&&(D=S,n.x=v,n.y=b,C=!0))}return!(!U&&!C)&&(v=c/2,b=d/2,u.setRange(t(n.x-v),t(n.y-b),t(n.x+v),t(n.y+b)),n.align="center",n.baseline="middle",!0)}},floodfill:function(t,n,a,r){const i=t.width,o=t.height,u=n[0],s=n[1],l=t.bitmap();return function(n){const f=n.datum.datum.items[r].items,d=f.length,c=n.datum.fontSize,p=e.textMetrics.width(n.datum,n.datum.text),x=[];let v,b,M,w,k,R,z,O,A,E,S,q,B=a?c:0,T=!1,U=!1,C=0;for(let e=0;e<d;++e){for(v=f[e].x,M=f[e].y,b=void 0===f[e].x2?v:f[e].x2,w=void 0===f[e].y2?M:f[e].y2,x.push([t((v+b)/2),t((M+w)/2)]);x.length;)if([z,O]=x.pop(),!(u.get(z,O)||s.get(z,O)||l.get(z,O))){l.set(z,O);for(let t=0;t<4;++t)k=z+h[t],R=O+y[t],l.outOfBounds(k,R,k,R)||x.push([k,R]);if(k=t.invert(z),R=t.invert(O),A=B,E=o,!m(k,R,p,c,i,o)&&!g(t,k,R,c,p,A,u,s)&&!g(t,k,R,c,p,c,u,null)){for(;E-A>=1;)S=(A+E)/2,g(t,k,R,c,p,S,u,s)?E=S:A=S;A>B&&(n.x=k,n.y=R,B=A,T=!0)}}T||a||(q=Math.abs(b-v+w-M),k=(v+b)/2,R=(M+w)/2,q>=C&&!m(k,R,p,c,i,o)&&!g(t,k,R,c,p,c,u,null)&&(C=q,n.x=k,n.y=R,U=!0))}return!(!T&&!U)&&(k=p/2,R=c/2,u.setRange(t(n.x-k),t(n.y-R),t(n.x+k),t(n.y+R)),n.align="center",n.baseline="middle",!0)}}};function w(t,a,r,s,l,f,d,m,g,h,y){if(!t.length)return t;const w=Math.max(s.length,l.length),k=function(t,e){const n=new Float64Array(e),a=t.length;for(let e=0;e<a;++e)n[e]=t[e]||0;for(let t=a;t<e;++t)n[t]=n[a-1];return n}(s,w),R=function(t,e){const n=new Int8Array(e),a=t.length;for(let e=0;e<a;++e)n[e]|=b[t[e]];for(let t=a;t<e;++t)n[t]=n[a-1];return n}(l,w),z=(B=t[0].datum)&&B.mark&&B.mark.marktype,O="group"===z&&t[0].datum.items[g].marktype,A="area"===O,E=function(t,e,n,a){const r=t=>[t.x,t.x,t.x,t.y,t.y,t.y];return t?"line"===t||"area"===t?t=>r(t.datum):"line"===e?t=>{const e=t.datum.items[a].items;return r(e.length?e["start"===n?0:e.length-1]:{x:NaN,y:NaN})}:t=>{const e=t.datum.bounds;return[e.x1,(e.x1+e.x2)/2,e.x2,e.y1,(e.y1+e.y2)/2,e.y2]}:r}(z,O,m,g),S=null===h||h===1/0,q=A&&"naive"===y;var B;let T=-1,U=-1;const C=t.map((t=>{const n=S?e.textMetrics.width(t,t.text):void 0;return T=Math.max(T,n),U=Math.max(U,t.fontSize),{datum:t,opacity:0,x:void 0,y:void 0,align:void 0,baseline:void 0,boundary:E(t),textWidth:n}}));h=null===h||h===1/0?Math.max(T,U)+Math.max(...s):h;const D=c(a[0],a[1],h);let I;if(!q){r&&C.sort(((t,e)=>r(t.datum,e.datum)));let e=!1;for(let t=0;t<R.length&&!e;++t)e=5===R[t]||k[t]<0;const a=(z&&d||A)&&t.map((t=>t.datum));I=f.length||a?function(t,e,a,r,s){const l=t.width,f=t.height,d=r||s,c=n.canvas(l,f).getContext("2d"),m=n.canvas(l,f).getContext("2d"),g=d&&n.canvas(l,f).getContext("2d");a.forEach((t=>u(c,t,!1))),u(m,e,!1),d&&u(g,e,!0);const h=o(c,l,f),y=o(m,l,f),p=d&&o(g,l,f),x=t.bitmap(),v=d&&t.bitmap();let b,M,w,k,R,z,O,A;for(M=0;M<f;++M)for(b=0;b<l;++b)R=M*l+b,z=h[R]&i,A=y[R]&i,O=d&&p[R]&i,(z||O||A)&&(w=t(b),k=t(M),s||!z&&!A||x.set(w,k),d&&(z||O)&&v.set(w,k));return[x,v]}(D,a||[],f,e,A):function(t,e){const n=t.bitmap();return(e||[]).forEach((e=>n.set(t(e.boundary[0]),t(e.boundary[3])))),[n,void 0]}(D,d&&C)}const N=A?M[y](D,I,d,g):function(t,n,a,r){const i=t.width,o=t.height,u=n[0],s=n[1],l=r.length;return function(n){var f;const d=n.boundary,c=n.datum.fontSize;if(d[2]<0||d[5]<0||d[0]>i||d[3]>o)return!1;let m,g,h,y,b,M,w,k,R,z,O,A,E,S,q,B=null!==(f=n.textWidth)&&void 0!==f?f:0;for(let i=0;i<l;++i){if(m=(3&a[i])-1,g=(a[i]>>>2&3)-1,h=0===m&&0===g||r[i]<0,y=m&&g?Math.SQRT1_2:1,b=r[i]<0?-1:1,M=d[1+m]+r[i]*m*y,O=d[4+g]+b*c*g/2+r[i]*g*y,k=O-c/2,R=O+c/2,A=t(M),S=t(k),q=t(R),!B){if(!v(A,A,S,q,u,s,0,0,0,0,0,h))continue;B=e.textMetrics.width(n.datum,n.datum.text)}if(z=M+b*B*m/2,M=z-B/2,w=z+B/2,A=t(M),E=t(w),v(A,E,S,q,u,s,0,0,0,0,0,h))return n.x=m?m*b<0?w:M:z,n.y=g?g*b<0?R:k:O,n.align=p[m*b+1],n.baseline=x[g*b+1],u.setRange(A,S,E,q),!0}return!1}}(D,I,R,k);return C.forEach((t=>t.opacity=+N(t))),C}const k=["x","y","opacity","align","baseline"],R=["top-left","left","bottom-left","top","bottom","top-right","right","bottom-right"];function z(t){a.Transform.call(this,null,t)}z.Definition={type:"Label",metadata:{modifies:!0},params:[{name:"size",type:"number",array:!0,length:2,required:!0},{name:"sort",type:"compare"},{name:"anchor",type:"string",array:!0,default:R},{name:"offset",type:"number",array:!0,default:[1]},{name:"padding",type:"number",default:0,null:!0},{name:"lineAnchor",type:"string",values:["start","end"],default:"end"},{name:"markIndex",type:"number",default:0},{name:"avoidBaseMark",type:"boolean",default:!0},{name:"avoidMarks",type:"data",array:!0},{name:"method",type:"string",default:"naive"},{name:"as",type:"string",array:!0,length:k.length,default:k}]},r.inherits(z,a.Transform,{transform(t,e){const n=t.modified();if(!(n||e.changed(e.ADD_REM)||function(n){const a=t[n];return r.isFunction(a)&&e.modified(a.fields)}("sort")))return;t.size&&2===t.size.length||r.error("Size parameter should be specified as a [width, height] array.");const a=t.as||k;return w(e.materialize(e.SOURCE).source||[],t.size,t.sort,r.array(null==t.offset?1:t.offset),r.array(t.anchor||R),t.avoidMarks||[],!1!==t.avoidBaseMark,t.lineAnchor||"end",t.markIndex||0,void 0===t.padding?0:t.padding,t.method||"naive").forEach((t=>{const e=t.datum;e[a[0]]=t.x,e[a[1]]=t.y,e[a[2]]=t.opacity,e[a[3]]=t.align,e[a[4]]=t.baseline})),e.reflow(n).modifies(a)}}),t.label=z,Object.defineProperty(t,"__esModule",{value:!0})}));
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("vega-scenegraph"),require("vega-canvas"),require("vega-dataflow"),require("vega-util")):"function"==typeof define&&define.amd?define(["exports","vega-scenegraph","vega-canvas","vega-dataflow","vega-util"],e):e(((t="undefined"!=typeof globalThis?globalThis:t||self).vega=t.vega||{},t.vega.transforms={}),t.vega,t.vega,t.vega,t.vega)}(this,(function(t,e,n,a,r){"use strict";const i=4278190080;function o(t,e,n){return new Uint32Array(t.getImageData(0,0,e,n).data.buffer)}function s(t,n,a){if(!n.length)return;const r=n[0].mark.marktype;"group"===r?n.forEach((e=>{e.items.forEach((e=>s(t,e.items,a)))})):e.Marks[r].draw(t,{items:a?n.map(u):n})}function u(t){const e=a.rederive(t,{});return e.stroke&&0!==e.strokeOpacity||e.fill&&0!==e.fillOpacity?{...e,strokeOpacity:1,stroke:"#000",fillOpacity:0}:e}const f=5,l=31,d=32,c=new Uint32Array(d+1),m=new Uint32Array(d+1);m[0]=0,c[0]=~m[0];for(let t=1;t<=d;++t)m[t]=m[t-1]<<1|1,c[t]=~m[t];function g(t,e,n){const a=Math.max(1,Math.sqrt(t*e/1e6)),r=~~((t+2*n+a)/a),i=~~((e+2*n+a)/a),o=t=>~~((t+n)/a);return o.invert=t=>t*a-n,o.bitmap=()=>function(t,e){const n=new Uint32Array(~~((t*e+d)/d));function a(t,e){n[t]|=e}function r(t,e){n[t]&=e}return{array:n,get:(e,a)=>{const r=a*t+e;return n[r>>>f]&1<<(r&l)},set:(e,n)=>{const r=n*t+e;a(r>>>f,1<<(r&l))},clear:(e,n)=>{const a=n*t+e;r(a>>>f,~(1<<(a&l)))},getRange:(e,a,r,i)=>{let o,s,u,d,g=i;for(;g>=a;--g)if(o=g*t+e,s=g*t+r,u=o>>>f,d=s>>>f,u===d){if(n[u]&c[o&l]&m[1+(s&l)])return!0}else{if(n[u]&c[o&l])return!0;if(n[d]&m[1+(s&l)])return!0;for(let t=u+1;t<d;++t)if(n[t])return!0}return!1},setRange:(e,n,r,i)=>{let o,s,u,d,g;for(;n<=i;++n)if(o=n*t+e,s=n*t+r,u=o>>>f,d=s>>>f,u===d)a(u,c[o&l]&m[1+(s&l)]);else for(a(u,c[o&l]),a(d,m[1+(s&l)]),g=u+1;g<d;++g)a(g,4294967295)},clearRange:(e,n,a,i)=>{let o,s,u,d,g;for(;n<=i;++n)if(o=n*t+e,s=n*t+a,u=o>>>f,d=s>>>f,u===d)r(u,m[o&l]|c[1+(s&l)]);else for(r(u,m[o&l]),r(d,c[1+(s&l)]),g=u+1;g<d;++g)r(g,0)},outOfBounds:(n,a,r,i)=>n<0||a<0||i>=e||r>=t}}(r,i),o.ratio=a,o.padding=n,o.width=t,o.height=e,o}function h(t,e,n,a,r,i){let o=n/2;return t-o<0||t+o>r||e-(o=a/2)<0||e+o>i}function y(t,e,n,a,r,i,o,s){const u=r*i/(2*a),f=t(e-u),l=t(e+u),d=t(n-(i/=2)),c=t(n+i);return o.outOfBounds(f,d,l,c)||o.getRange(f,d,l,c)||s&&s.getRange(f,d,l,c)}const p=[-1,-1,1,1],x=[-1,1,-1,1];const v=["right","center","left"],b=["bottom","middle","top"];function w(t,e,n,a,r,i,o,s,u,f,l,d){return!(r.outOfBounds(t,n,e,a)||(d&&i||r).getRange(t,n,e,a))}const M={"top-left":0,top:1,"top-right":2,left:4,middle:5,right:6,"bottom-left":8,bottom:9,"bottom-right":10},k={naive:function(t,n,a,r){const i=t.width,o=t.height;return function(t){const n=t.datum.datum.items[r].items,a=n.length,s=t.datum.fontSize,u=e.textMetrics.width(t.datum,t.datum.text);let f,l,d,c,m,g,h,y=0;for(let e=0;e<a;++e)f=n[e].x,d=n[e].y,l=void 0===n[e].x2?f:n[e].x2,c=void 0===n[e].y2?d:n[e].y2,m=(f+l)/2,g=(d+c)/2,h=Math.abs(l-f+c-d),h>=y&&(y=h,t.x=m,t.y=g);return m=u/2,g=s/2,f=t.x-m,l=t.x+m,d=t.y-g,c=t.y+g,t.align="center",f<0&&l<=i?t.align="left":0<=f&&i<l&&(t.align="right"),t.baseline="middle",d<0&&c<=o?t.baseline="top":0<=d&&o<c&&(t.baseline="bottom"),!0}},"reduced-search":function(t,n,a,r){const i=t.width,o=t.height,s=n[0],u=n[1];function f(e,n,a,r,f){const l=t.invert(e),d=t.invert(n);let c,m=a,g=o;if(!h(l,d,r,f,i,o)&&!y(t,l,d,f,r,m,s,u)&&!y(t,l,d,f,r,f,s,null)){for(;g-m>=1;)c=(m+g)/2,y(t,l,d,f,r,c,s,u)?g=c:m=c;if(m>a)return[l,d,m,!0]}}return function(n){const u=n.datum.datum.items[r].items,l=u.length,d=n.datum.fontSize,c=e.textMetrics.width(n.datum,n.datum.text);let m,g,p,x,v,b,w,M,k,R,z,A,O,E,S,q,B,T=a?d:0,U=!1,C=!1,D=0;for(let e=0;e<l;++e){for(m=u[e].x,p=u[e].y,g=void 0===u[e].x2?m:u[e].x2,x=void 0===u[e].y2?p:u[e].y2,m>g&&(B=m,m=g,g=B),p>x&&(B=p,p=x,x=B),k=t(m),z=t(g),R=~~((k+z)/2),A=t(p),E=t(x),O=~~((A+E)/2),w=R;w>=k;--w)for(M=O;M>=A;--M)q=f(w,M,T,c,d),q&&([n.x,n.y,T,U]=q);for(w=R;w<=z;++w)for(M=O;M<=E;++M)q=f(w,M,T,c,d),q&&([n.x,n.y,T,U]=q);U||a||(S=Math.abs(g-m+x-p),v=(m+g)/2,b=(p+x)/2,S>=D&&!h(v,b,c,d,i,o)&&!y(t,v,b,d,c,d,s,null)&&(D=S,n.x=v,n.y=b,C=!0))}return!(!U&&!C)&&(v=c/2,b=d/2,s.setRange(t(n.x-v),t(n.y-b),t(n.x+v),t(n.y+b)),n.align="center",n.baseline="middle",!0)}},floodfill:function(t,n,a,r){const i=t.width,o=t.height,s=n[0],u=n[1],f=t.bitmap();return function(n){const l=n.datum.datum.items[r].items,d=l.length,c=n.datum.fontSize,m=e.textMetrics.width(n.datum,n.datum.text),g=[];let v,b,w,M,k,R,z,A,O,E,S,q,B=a?c:0,T=!1,U=!1,C=0;for(let e=0;e<d;++e){for(v=l[e].x,w=l[e].y,b=void 0===l[e].x2?v:l[e].x2,M=void 0===l[e].y2?w:l[e].y2,g.push([t((v+b)/2),t((w+M)/2)]);g.length;)if([z,A]=g.pop(),!(s.get(z,A)||u.get(z,A)||f.get(z,A))){f.set(z,A);for(let t=0;t<4;++t)k=z+p[t],R=A+x[t],f.outOfBounds(k,R,k,R)||g.push([k,R]);if(k=t.invert(z),R=t.invert(A),O=B,E=o,!h(k,R,m,c,i,o)&&!y(t,k,R,c,m,O,s,u)&&!y(t,k,R,c,m,c,s,null)){for(;E-O>=1;)S=(O+E)/2,y(t,k,R,c,m,S,s,u)?E=S:O=S;O>B&&(n.x=k,n.y=R,B=O,T=!0)}}T||a||(q=Math.abs(b-v+M-w),k=(v+b)/2,R=(w+M)/2,q>=C&&!h(k,R,m,c,i,o)&&!y(t,k,R,c,m,c,s,null)&&(C=q,n.x=k,n.y=R,U=!0))}return!(!T&&!U)&&(k=m/2,R=c/2,s.setRange(t(n.x-k),t(n.y-R),t(n.x+k),t(n.y+R)),n.align="center",n.baseline="middle",!0)}}};function R(t,a,r,u,f,l,d,c,m,h,y){if(!t.length)return t;const p=Math.max(u.length,f.length),x=function(t,e){const n=new Float64Array(e),a=t.length;for(let e=0;e<a;++e)n[e]=t[e]||0;for(let t=a;t<e;++t)n[t]=n[a-1];return n}(u,p),R=function(t,e){const n=new Int8Array(e),a=t.length;for(let e=0;e<a;++e)n[e]|=M[t[e]];for(let t=a;t<e;++t)n[t]=n[a-1];return n}(f,p),z=(B=t[0].datum)&&B.mark&&B.mark.marktype,A="group"===z&&t[0].datum.items[m].marktype,O="area"===A,E=function(t,e,n,a){const r=t=>[t.x,t.x,t.x,t.y,t.y,t.y];return t?"line"===t||"area"===t?t=>r(t.datum):"line"===e?t=>{const e=t.datum.items[a].items;return r(e.length?e["start"===n?0:e.length-1]:{x:NaN,y:NaN})}:t=>{const e=t.datum.bounds;return[e.x1,(e.x1+e.x2)/2,e.x2,e.y1,(e.y1+e.y2)/2,e.y2]}:r}(z,A,c,m),S=null===h||h===1/0,q=O&&"naive"===y;var B;let T=-1,U=-1;const C=t.map((t=>{const n=S?e.textMetrics.width(t,t.text):void 0;return T=Math.max(T,n),U=Math.max(U,t.fontSize),{datum:t,opacity:0,x:void 0,y:void 0,align:void 0,baseline:void 0,boundary:E(t),textWidth:n}}));h=null===h||h===1/0?Math.max(T,U)+Math.max(...u):h;const D=g(a[0],a[1],h);let I;if(!q){r&&C.sort(((t,e)=>r(t.datum,e.datum)));let e=!1;for(let t=0;t<R.length&&!e;++t)e=5===R[t]||x[t]<0;const a=(z&&d||O)&&t.map((t=>t.datum));I=l.length||a?function(t,e,a,r,u){const f=t.width,l=t.height,d=r||u,c=n.canvas(f,l).getContext("2d"),m=n.canvas(f,l).getContext("2d"),g=d&&n.canvas(f,l).getContext("2d");a.forEach((t=>s(c,t,!1))),s(m,e,!1),d&&s(g,e,!0);const h=o(c,f,l),y=o(m,f,l),p=d&&o(g,f,l),x=t.bitmap(),v=d&&t.bitmap();let b,w,M,k,R,z,A,O;for(w=0;w<l;++w)for(b=0;b<f;++b)R=w*f+b,z=h[R]&i,O=y[R]&i,A=d&&p[R]&i,(z||A||O)&&(M=t(b),k=t(w),u||!z&&!O||x.set(M,k),d&&(z||A)&&v.set(M,k));return[x,v]}(D,a||[],l,e,O):function(t,e){const n=t.bitmap();return(e||[]).forEach((e=>n.set(t(e.boundary[0]),t(e.boundary[3])))),[n,void 0]}(D,d&&C)}const N=O?k[y](D,I,d,m):function(t,n,a,r){const i=t.width,o=t.height,s=n[0],u=n[1],f=r.length;return function(n){const l=n.boundary,d=n.datum.fontSize;if(l[2]<0||l[5]<0||l[0]>i||l[3]>o)return!1;let c,m,g,h,y,p,x,M,k,R,z,A,O,E,S,q=n.textWidth??0;for(let i=0;i<f;++i){if(c=(3&a[i])-1,m=(a[i]>>>2&3)-1,g=0===c&&0===m||r[i]<0,h=c&&m?Math.SQRT1_2:1,y=r[i]<0?-1:1,p=l[1+c]+r[i]*c*h,z=l[4+m]+y*d*m/2+r[i]*m*h,M=z-d/2,k=z+d/2,A=t(p),E=t(M),S=t(k),!q){if(!w(A,A,E,S,s,u,0,0,0,0,0,g))continue;q=e.textMetrics.width(n.datum,n.datum.text)}if(R=p+y*q*c/2,p=R-q/2,x=R+q/2,A=t(p),O=t(x),w(A,O,E,S,s,u,0,0,0,0,0,g))return n.x=c?c*y<0?x:p:R,n.y=m?m*y<0?k:M:z,n.align=v[c*y+1],n.baseline=b[m*y+1],s.setRange(A,E,O,S),!0}return!1}}(D,I,R,x);return C.forEach((t=>t.opacity=+N(t))),C}const z=["x","y","opacity","align","baseline"],A=["top-left","left","bottom-left","top","bottom","top-right","right","bottom-right"];function O(t){a.Transform.call(this,null,t)}O.Definition={type:"Label",metadata:{modifies:!0},params:[{name:"size",type:"number",array:!0,length:2,required:!0},{name:"sort",type:"compare"},{name:"anchor",type:"string",array:!0,default:A},{name:"offset",type:"number",array:!0,default:[1]},{name:"padding",type:"number",default:0,null:!0},{name:"lineAnchor",type:"string",values:["start","end"],default:"end"},{name:"markIndex",type:"number",default:0},{name:"avoidBaseMark",type:"boolean",default:!0},{name:"avoidMarks",type:"data",array:!0},{name:"method",type:"string",default:"naive"},{name:"as",type:"string",array:!0,length:z.length,default:z}]},r.inherits(O,a.Transform,{transform(t,e){const n=t.modified();if(!(n||e.changed(e.ADD_REM)||function(n){const a=t[n];return r.isFunction(a)&&e.modified(a.fields)}("sort")))return;t.size&&2===t.size.length||r.error("Size parameter should be specified as a [width, height] array.");const a=t.as||z;return R(e.materialize(e.SOURCE).source||[],t.size,t.sort,r.array(null==t.offset?1:t.offset),r.array(t.anchor||A),t.avoidMarks||[],!1!==t.avoidBaseMark,t.lineAnchor||"end",t.markIndex||0,void 0===t.padding?0:t.padding,t.method||"naive").forEach((t=>{const e=t.datum;e[a[0]]=t.x,e[a[1]]=t.y,e[a[2]]=t.opacity,e[a[3]]=t.align,e[a[4]]=t.baseline})),e.reflow(n).modifies(a)}}),t.label=O}));
//# sourceMappingURL=vega-label.min.js.map
import { Marks, textMetrics } from 'vega-scenegraph';
import { canvas } from 'vega-canvas';
import { rederive, Transform } from 'vega-dataflow';
import { inherits, isFunction, error, array } from 'vega-util';
import { inherits, error, array, isFunction } from 'vega-util';
// bit mask for getting first 2 bytes of alpha value
const ALPHA_MASK = 0xff000000;
function baseBitmaps($, data) {
const bitmap = $.bitmap(); // when there is no base mark but data points are to be avoided
const bitmap = $.bitmap();
// when there is no base mark but data points are to be avoided
(data || []).forEach(d => bitmap.set($(d.boundary[0]), $(d.boundary[3])));

@@ -16,24 +17,24 @@ return [bitmap, undefined];

const width = $.width,
height = $.height,
border = labelInside || isGroupArea,
context = canvas(width, height).getContext('2d'),
baseMarkContext = canvas(width, height).getContext('2d'),
strokeContext = border && canvas(width, height).getContext('2d'); // render all marks to be avoided into canvas
height = $.height,
border = labelInside || isGroupArea,
context = canvas(width, height).getContext('2d'),
baseMarkContext = canvas(width, height).getContext('2d'),
strokeContext = border && canvas(width, height).getContext('2d');
// render all marks to be avoided into canvas
avoidMarks.forEach(items => draw(context, items, false));
draw(baseMarkContext, baseMark, false);
if (border) {
draw(strokeContext, baseMark, true);
} // get canvas buffer, create bitmaps
}
// get canvas buffer, create bitmaps
const buffer = getBuffer(context, width, height),
baseMarkBuffer = getBuffer(baseMarkContext, width, height),
strokeBuffer = border && getBuffer(strokeContext, width, height),
layer1 = $.bitmap(),
layer2 = border && $.bitmap(); // populate bitmap layers
baseMarkBuffer = getBuffer(baseMarkContext, width, height),
strokeBuffer = border && getBuffer(strokeContext, width, height),
layer1 = $.bitmap(),
layer2 = border && $.bitmap();
// populate bitmap layers
let x, y, u, v, index, alpha, strokeAlpha, baseMarkAlpha;
for (y = 0; y < height; ++y) {

@@ -45,3 +46,2 @@ for (x = 0; x < width; ++x) {

strokeAlpha = border && strokeBuffer[index] & ALPHA_MASK;
if (alpha || strokeAlpha || baseMarkAlpha) {

@@ -51,3 +51,2 @@ u = $(x);

if (!isGroupArea && (alpha || baseMarkAlpha)) layer1.set(u, v); // update interior bitmap
if (border && (alpha || strokeAlpha)) layer2.set(u, v); // update border bitmap

@@ -60,11 +59,8 @@ }

}
function getBuffer(context, width, height) {
return new Uint32Array(context.getImageData(0, 0, width, height).data.buffer);
}
function draw(context, items, interior) {
if (!items.length) return;
const type = items[0].mark.marktype;
if (type === 'group') {

@@ -80,2 +76,3 @@ items.forEach(group => {

}
/**

@@ -86,9 +83,7 @@ * Prepare item before drawing into canvas (setting stroke and opacity)

*/
function prepare(source) {
const item = rederive(source, {});
if (item.stroke && item.strokeOpacity !== 0 || item.fill && item.fillOpacity !== 0) {
return { ...item,
return {
...item,
strokeOpacity: 1,

@@ -99,3 +94,2 @@ stroke: '#000',

}
return item;

@@ -105,14 +99,13 @@ }

const DIV = 5,
// bit shift from x, y index to bit vector array index
MOD = 31,
// bit mask for index lookup within a bit vector
SIZE = 32,
// individual bit vector size
RIGHT0 = new Uint32Array(SIZE + 1),
// left-anchored bit vectors, full -> 0
RIGHT1 = new Uint32Array(SIZE + 1); // right-anchored bit vectors, 0 -> full
// bit shift from x, y index to bit vector array index
MOD = 31,
// bit mask for index lookup within a bit vector
SIZE = 32,
// individual bit vector size
RIGHT0 = new Uint32Array(SIZE + 1),
// left-anchored bit vectors, full -> 0
RIGHT1 = new Uint32Array(SIZE + 1); // right-anchored bit vectors, 0 -> full
RIGHT1[0] = 0;
RIGHT0[0] = ~RIGHT1[0];
for (let i = 1; i <= SIZE; ++i) {

@@ -122,14 +115,10 @@ RIGHT1[i] = RIGHT1[i - 1] << 1 | 1;

}
function Bitmap (w, h) {
const array = new Uint32Array(~~((w * h + SIZE) / SIZE));
function _set(index, mask) {
array[index] |= mask;
}
function _clear(index, mask) {
array[index] &= mask;
}
return {

@@ -143,3 +132,2 @@ array: array,

const index = y * w + x;
_set(index >>> DIV, 1 << (index & MOD));

@@ -149,3 +137,2 @@ },

const index = y * w + x;
_clear(index >>> DIV, ~(1 << (index & MOD)));

@@ -155,7 +142,6 @@ },

let r = y2,
start,
end,
indexStart,
indexEnd;
start,
end,
indexStart,
indexEnd;
for (; r >= y; --r) {

@@ -166,3 +152,2 @@ start = r * w + x;

indexEnd = end >>> DIV;
if (indexStart === indexEnd) {

@@ -175,3 +160,2 @@ if (array[indexStart] & RIGHT0[start & MOD] & RIGHT1[(end & MOD) + 1]) {

if (array[indexEnd] & RIGHT1[(end & MOD) + 1]) return true;
for (let i = indexStart + 1; i < indexEnd; ++i) {

@@ -182,3 +166,2 @@ if (array[i]) return true;

}
return false;

@@ -188,3 +171,2 @@ },

let start, end, indexStart, indexEnd, i;
for (; y <= y2; ++y) {

@@ -195,3 +177,2 @@ start = y * w + x;

indexEnd = end >>> DIV;
if (indexStart === indexEnd) {

@@ -201,5 +182,3 @@ _set(indexStart, RIGHT0[start & MOD] & RIGHT1[(end & MOD) + 1]);

_set(indexStart, RIGHT0[start & MOD]);
_set(indexEnd, RIGHT1[(end & MOD) + 1]);
for (i = indexStart + 1; i < indexEnd; ++i) _set(i, 0xffffffff);

@@ -211,3 +190,2 @@ }

let start, end, indexStart, indexEnd, i;
for (; y <= y2; ++y) {

@@ -218,3 +196,2 @@ start = y * w + x;

indexEnd = end >>> DIV;
if (indexStart === indexEnd) {

@@ -224,5 +201,3 @@ _clear(indexStart, RIGHT1[start & MOD] | RIGHT0[(end & MOD) + 1]);

_clear(indexStart, RIGHT1[start & MOD]);
_clear(indexEnd, RIGHT0[(end & MOD) + 1]);
for (i = indexStart + 1; i < indexEnd; ++i) _clear(i, 0);

@@ -238,10 +213,7 @@ }

const ratio = Math.max(1, Math.sqrt(width * height / 1e6)),
w = ~~((width + 2 * padding + ratio) / ratio),
h = ~~((height + 2 * padding + ratio) / ratio),
scale = _ => ~~((_ + padding) / ratio);
w = ~~((width + 2 * padding + ratio) / ratio),
h = ~~((height + 2 * padding + ratio) / ratio),
scale = _ => ~~((_ + padding) / ratio);
scale.invert = _ => _ * ratio - padding;
scale.bitmap = () => Bitmap(w, h);
scale.ratio = ratio;

@@ -256,22 +228,24 @@ scale.padding = padding;

const width = $.width,
height = $.height; // try to place a label within an input area mark
height = $.height;
// try to place a label within an input area mark
return function (d) {
const items = d.datum.datum.items[markIndex].items,
// area points
n = items.length,
// number of points
textHeight = d.datum.fontSize,
// label width
textWidth = textMetrics.width(d.datum, d.datum.text); // label height
// area points
n = items.length,
// number of points
textHeight = d.datum.fontSize,
// label width
textWidth = textMetrics.width(d.datum, d.datum.text); // label height
let maxAreaWidth = 0,
x1,
x2,
y1,
y2,
x,
y,
areaWidth; // for each area sample point
x1,
x2,
y1,
y2,
x,
y,
areaWidth;
// for each area sample point
for (let i = 0; i < n; ++i) {

@@ -285,3 +259,2 @@ x1 = items[i].x;

areaWidth = Math.abs(x2 - x1 + y2 - y1);
if (areaWidth >= maxAreaWidth) {

@@ -293,3 +266,2 @@ maxAreaWidth = areaWidth;

}
x = textWidth / 2;

@@ -302,3 +274,2 @@ y = textHeight / 2;

d.align = 'center';
if (x1 < 0 && x2 <= width) {

@@ -309,5 +280,3 @@ d.align = 'left';

}
d.baseline = 'middle';
if (y1 < 0 && y2 <= height) {

@@ -318,3 +287,2 @@ d.baseline = 'top';

}
return true;

@@ -330,6 +298,6 @@ };

const w = textWidth * h / (textHeight * 2),
x1 = $(x - w),
x2 = $(x + w),
y1 = $(y - (h = h / 2)),
y2 = $(y + h);
x1 = $(x - w),
x2 = $(x + w),
y1 = $(y - (h = h / 2)),
y2 = $(y + h);
return bm0.outOfBounds(x1, y1, x2, y2) || bm0.getRange(x1, y1, x2, y2) || bm1 && bm1.getRange(x1, y1, x2, y2);

@@ -340,14 +308,13 @@ }

const width = $.width,
height = $.height,
bm0 = bitmaps[0],
// where labels have been placed
bm1 = bitmaps[1]; // area outlines
height = $.height,
bm0 = bitmaps[0],
// where labels have been placed
bm1 = bitmaps[1]; // area outlines
function tryLabel(_x, _y, maxSize, textWidth, textHeight) {
const x = $.invert(_x),
y = $.invert(_y);
y = $.invert(_y);
let lo = maxSize,
hi = height,
mid;
hi = height,
mid;
if (!outOfBounds(x, y, textWidth, textHeight, width, height) && !collision($, x, y, textHeight, textWidth, lo, bm0, bm1) && !collision($, x, y, textHeight, textWidth, textHeight, bm0, null)) {

@@ -358,3 +325,2 @@ // if the label fits at the current sample point,

mid = (lo + hi) / 2;
if (collision($, x, y, textHeight, textWidth, mid, bm0, bm1)) {

@@ -365,5 +331,4 @@ hi = mid;

}
} // place label if current lower bound exceeds prior max font size
}
// place label if current lower bound exceeds prior max font size
if (lo > maxSize) {

@@ -373,37 +338,37 @@ return [x, y, lo, true];

}
} // try to place a label within an input area mark
}
// try to place a label within an input area mark
return function (d) {
const items = d.datum.datum.items[markIndex].items,
// area points
n = items.length,
// number of points
textHeight = d.datum.fontSize,
// label width
textWidth = textMetrics.width(d.datum, d.datum.text); // label height
// area points
n = items.length,
// number of points
textHeight = d.datum.fontSize,
// label width
textWidth = textMetrics.width(d.datum, d.datum.text); // label height
let maxSize = avoidBaseMark ? textHeight : 0,
labelPlaced = false,
labelPlaced2 = false,
maxAreaWidth = 0,
x1,
x2,
y1,
y2,
x,
y,
_x,
_y,
_x1,
_xMid,
_x2,
_y1,
_yMid,
_y2,
areaWidth,
result,
swapTmp; // for each area sample point
labelPlaced = false,
labelPlaced2 = false,
maxAreaWidth = 0,
x1,
x2,
y1,
y2,
x,
y,
_x,
_y,
_x1,
_xMid,
_x2,
_y1,
_yMid,
_y2,
areaWidth,
result,
swapTmp;
// for each area sample point
for (let i = 0; i < n; ++i) {

@@ -414,3 +379,2 @@ x1 = items[i].x;

y2 = items[i].y2 === undefined ? y1 : items[i].y2;
if (x1 > x2) {

@@ -421,3 +385,2 @@ swapTmp = x1;

}
if (y1 > y2) {

@@ -428,3 +391,2 @@ swapTmp = y1;

}
_x1 = $(x1);

@@ -435,8 +397,8 @@ _x2 = $(x2);

_y2 = $(y2);
_yMid = ~~((_y1 + _y2) / 2); // search along the line from mid point between the 2 border to lower border
_yMid = ~~((_y1 + _y2) / 2);
// search along the line from mid point between the 2 border to lower border
for (_x = _xMid; _x >= _x1; --_x) {
for (_y = _yMid; _y >= _y1; --_y) {
result = tryLabel(_x, _y, maxSize, textWidth, textHeight);
if (result) {

@@ -446,9 +408,8 @@ [d.x, d.y, maxSize, labelPlaced] = result;

}
} // search along the line from mid point between the 2 border to upper border
}
// search along the line from mid point between the 2 border to upper border
for (_x = _xMid; _x <= _x2; ++_x) {
for (_y = _yMid; _y <= _y2; ++_y) {
result = tryLabel(_x, _y, maxSize, textWidth, textHeight);
if (result) {

@@ -458,6 +419,6 @@ [d.x, d.y, maxSize, labelPlaced] = result;

}
} // place label at slice center if not placed through other means
}
// place label at slice center if not placed through other means
// and if we're not avoiding overlap with other areas
if (!labelPlaced && !avoidBaseMark) {

@@ -467,4 +428,5 @@ // one span is zero, hence we can add

x = (x1 + x2) / 2;
y = (y1 + y2) / 2; // place label if it fits and improves the max area width
y = (y1 + y2) / 2;
// place label if it fits and improves the max area width
if (areaWidth >= maxAreaWidth && !outOfBounds(x, y, textWidth, textHeight, width, height) && !collision($, x, y, textHeight, textWidth, textHeight, bm0, null)) {

@@ -477,5 +439,5 @@ maxAreaWidth = areaWidth;

}
} // record current label placement information, update label bitmap
}
// record current label placement information, update label bitmap
if (labelPlaced || labelPlaced2) {

@@ -494,2 +456,3 @@ x = textWidth / 2;

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

@@ -499,39 +462,39 @@ const Y_DIR = [-1, 1, -1, 1];

const width = $.width,
height = $.height,
bm0 = bitmaps[0],
// where labels have been placed
bm1 = bitmaps[1],
// area outlines
bm2 = $.bitmap(); // flood-fill visitations
height = $.height,
bm0 = bitmaps[0],
// where labels have been placed
bm1 = bitmaps[1],
// area outlines
bm2 = $.bitmap(); // flood-fill visitations
// try to place a label within an input area mark
return function (d) {
const items = d.datum.datum.items[markIndex].items,
// area points
n = items.length,
// number of points
textHeight = d.datum.fontSize,
// label width
textWidth = textMetrics.width(d.datum, d.datum.text),
// label height
stack = []; // flood fill stack
// area points
n = items.length,
// number of points
textHeight = d.datum.fontSize,
// label width
textWidth = textMetrics.width(d.datum, d.datum.text),
// label height
stack = []; // flood fill stack
let maxSize = avoidBaseMark ? textHeight : 0,
labelPlaced = false,
labelPlaced2 = false,
maxAreaWidth = 0,
x1,
x2,
y1,
y2,
x,
y,
_x,
_y,
lo,
hi,
mid,
areaWidth; // for each area sample point
labelPlaced = false,
labelPlaced2 = false,
maxAreaWidth = 0,
x1,
x2,
y1,
y2,
x,
y,
_x,
_y,
lo,
hi,
mid,
areaWidth;
// for each area sample point
for (let i = 0; i < n; ++i) {

@@ -541,14 +504,17 @@ x1 = items[i].x;

x2 = items[i].x2 === undefined ? x1 : items[i].x2;
y2 = items[i].y2 === undefined ? y1 : items[i].y2; // add scaled center point to stack
y2 = items[i].y2 === undefined ? y1 : items[i].y2;
stack.push([$((x1 + x2) / 2), $((y1 + y2) / 2)]); // perform flood fill, visit points
// add scaled center point to stack
stack.push([$((x1 + x2) / 2), $((y1 + y2) / 2)]);
// perform flood fill, visit points
while (stack.length) {
[_x, _y] = stack.pop(); // exit if point already marked
[_x, _y] = stack.pop();
if (bm0.get(_x, _y) || bm1.get(_x, _y) || bm2.get(_x, _y)) continue; // mark point in flood fill bitmap
// exit if point already marked
if (bm0.get(_x, _y) || bm1.get(_x, _y) || bm2.get(_x, _y)) continue;
// mark point in flood fill bitmap
// add search points for all (in bound) directions
bm2.set(_x, _y);
for (let j = 0; j < 4; ++j) {

@@ -558,5 +524,5 @@ x = _x + X_DIR[j];

if (!bm2.outOfBounds(x, y, x, y)) stack.push([x, y]);
} // unscale point back to x, y space
}
// unscale point back to x, y space
x = $.invert(_x);

@@ -572,3 +538,2 @@ y = $.invert(_y);

mid = (lo + hi) / 2;
if (collision($, x, y, textHeight, textWidth, mid, bm0, bm1)) {

@@ -579,5 +544,4 @@ hi = mid;

}
} // place label if current lower bound exceeds prior max font size
}
// place label if current lower bound exceeds prior max font size
if (lo > maxSize) {

@@ -590,6 +554,6 @@ d.x = x;

}
} // place label at slice center if not placed through other means
}
// place label at slice center if not placed through other means
// and if we're not avoiding overlap with other areas
if (!labelPlaced && !avoidBaseMark) {

@@ -599,4 +563,5 @@ // one span is zero, hence we can add

x = (x1 + x2) / 2;
y = (y1 + y2) / 2; // place label if it fits and improves the max area width
y = (y1 + y2) / 2;
// place label if it fits and improves the max area width
if (areaWidth >= maxAreaWidth && !outOfBounds(x, y, textWidth, textHeight, width, height) && !collision($, x, y, textHeight, textWidth, textHeight, bm0, null)) {

@@ -609,5 +574,5 @@ maxAreaWidth = areaWidth;

}
} // record current label placement information, update label bitmap
}
// record current label placement information, update label bitmap
if (labelPlaced || labelPlaced2) {

@@ -627,37 +592,35 @@ x = textWidth / 2;

const Aligns = ['right', 'center', 'left'],
Baselines = ['bottom', 'middle', 'top'];
Baselines = ['bottom', 'middle', 'top'];
function placeMarkLabel ($, bitmaps, anchors, offsets) {
const width = $.width,
height = $.height,
bm0 = bitmaps[0],
bm1 = bitmaps[1],
n = offsets.length;
height = $.height,
bm0 = bitmaps[0],
bm1 = bitmaps[1],
n = offsets.length;
return function (d) {
var _d$textWidth;
const boundary = d.boundary,
textHeight = d.datum.fontSize; // can not be placed if the mark is not visible in the graph bound
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) {
return false;
}
let textWidth = d.textWidth ?? 0,
dx,
dy,
isInside,
sizeFactor,
insideFactor,
x1,
x2,
y1,
y2,
xc,
yc,
_x1,
_x2,
_y1,
_y2;
let textWidth = (_d$textWidth = d.textWidth) !== null && _d$textWidth !== void 0 ? _d$textWidth : 0,
dx,
dy,
isInside,
sizeFactor,
insideFactor,
x1,
x2,
y1,
y2,
xc,
yc,
_x1,
_x2,
_y1,
_y2; // for each anchor and offset
// for each anchor and offset
for (let i = 0; i < n; ++i) {

@@ -676,3 +639,2 @@ dx = (anchors[i] & 0x3) - 1;

_y2 = $(y2);
if (!textWidth) {

@@ -688,3 +650,2 @@ // to avoid finding width of text label,

}
xc = x1 + insideFactor * textWidth * dx / 2;

@@ -695,3 +656,2 @@ x1 = xc - textWidth / 2;

_x2 = $(x2);
if (test(_x1, _x2, _y1, _y2, bm0, bm1, x1, x2, y1, y2, boundary, isInside)) {

@@ -707,7 +667,7 @@ // place label if the position is placeable

}
return false;
};
} // Test if a label with the given dimensions can be added without overlap
}
// Test if a label with the given dimensions can be added without overlap
function test(_x1, _x2, _y1, _y2, bm0, bm1, x1, x2, y1, y2, boundary, isInside) {

@@ -717,9 +677,11 @@ return !(bm0.outOfBounds(_x1, _y1, _x2, _y2) || (isInside && bm1 || bm0).getRange(_x1, _y1, _x2, _y2));

// 8-bit representation of anchors
const TOP = 0x0,
MIDDLE = 0x4,
BOTTOM = 0x8,
LEFT = 0x0,
CENTER = 0x1,
RIGHT = 0x2; // Mapping from text anchor to number representation
MIDDLE = 0x4,
BOTTOM = 0x8,
LEFT = 0x0,
CENTER = 0x1,
RIGHT = 0x2;
// Mapping from text anchor to number representation
const anchorCode = {

@@ -745,13 +707,14 @@ 'top-left': TOP + LEFT,

const positions = Math.max(offset.length, anchor.length),
offsets = getOffsets(offset, positions),
anchors = getAnchors(anchor, positions),
marktype = markType(texts[0].datum),
grouptype = marktype === 'group' && texts[0].datum.items[markIndex].marktype,
isGroupArea = grouptype === 'area',
boundary = markBoundary(marktype, grouptype, lineAnchor, markIndex),
infPadding = padding === null || padding === Infinity,
isNaiveGroupArea = isGroupArea && method === 'naive';
offsets = getOffsets(offset, positions),
anchors = getAnchors(anchor, positions),
marktype = markType(texts[0].datum),
grouptype = marktype === 'group' && texts[0].datum.items[markIndex].marktype,
isGroupArea = grouptype === 'area',
boundary = markBoundary(marktype, grouptype, lineAnchor, markIndex),
infPadding = padding === null || padding === Infinity,
isNaiveGroupArea = isGroupArea && method === 'naive';
let maxTextWidth = -1,
maxTextHeight = -1; // prepare text mark data for placing
maxTextHeight = -1;
// prepare text mark data for placing
const data = texts.map(d => {

@@ -775,3 +738,2 @@ const textWidth = infPadding ? textMetrics.width(d, d.text) : undefined;

let bitmaps;
if (!isNaiveGroupArea) {

@@ -781,7 +743,6 @@ // sort labels in priority order, if comparator is provided

data.sort((a, b) => compare(a.datum, b.datum));
} // flag indicating if label can be placed inside its base mark
}
// flag indicating if label can be placed inside its base mark
let labelInside = false;
for (let i = 0; i < anchors.length && !labelInside; ++i) {

@@ -791,43 +752,37 @@ // label inside if anchor is at center

labelInside = anchors[i] === 0x5 || offsets[i] < 0;
} // extract data information from base mark when base mark is to be avoided
}
// extract data information from base mark when base mark is to be avoided
// base mark is implicitly avoided if it is a group area
const baseMark = (marktype && avoidBaseMark || isGroupArea) && texts.map(d => d.datum);
const baseMark = (marktype && avoidBaseMark || isGroupArea) && texts.map(d => d.datum); // generate bitmaps for layout calculation
// generate bitmaps for layout calculation
bitmaps = avoidMarks.length || baseMark ? markBitmaps($, baseMark || [], avoidMarks, labelInside, isGroupArea) : baseBitmaps($, avoidBaseMark && data);
} // generate label placement function
}
// generate label placement function
const place = isGroupArea ? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex) : placeMarkLabel($, bitmaps, anchors, offsets);
const place = isGroupArea ? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex) : placeMarkLabel($, bitmaps, anchors, offsets); // place all labels
// place all labels
data.forEach(d => d.opacity = +place(d));
return data;
}
function getOffsets(_, count) {
const offsets = new Float64Array(count),
n = _.length;
n = _.length;
for (let i = 0; i < n; ++i) offsets[i] = _[i] || 0;
for (let i = n; i < count; ++i) offsets[i] = offsets[n - 1];
return offsets;
}
function getAnchors(_, count) {
const anchors = new Int8Array(count),
n = _.length;
n = _.length;
for (let i = 0; i < n; ++i) anchors[i] |= anchorCode[_[i]];
for (let i = n; i < count; ++i) anchors[i] = anchors[n - 1];
return anchors;
}
function markType(item) {
return item && item.mark && item.mark.marktype;
}
/**

@@ -840,7 +795,4 @@ * Factory function for function for getting base mark boundary, depending

*/
function markBoundary(marktype, grouptype, lineAnchor, markIndex) {
const xy = d => [d.x, d.x, d.x, d.y, d.y, d.y];
if (!marktype) {

@@ -868,2 +820,3 @@ return xy; // no reactive geometry

const Anchors = ['top-left', 'left', 'bottom-left', 'top', 'bottom', 'top-right', 'right', 'bottom-right'];
/**

@@ -896,3 +849,2 @@ * Compute text label layout to annotate marks.

*/
function Label(params) {

@@ -965,13 +917,10 @@ Transform.call(this, null, params);

}
const mod = _.modified();
if (!(mod || pulse.changed(pulse.ADD_REM) || modp('sort'))) return;
if (!_.size || _.size.length !== 2) {
error('Size parameter should be specified as a [width, height] array.');
}
const as = _.as || Output;
const as = _.as || Output; // run label layout
// run label layout
labelLayout(pulse.materialize(pulse.SOURCE).source || [], _.size, _.sort, array(_.offset == null ? 1 : _.offset), array(_.anchor || Anchors), _.avoidMarks || [], _.avoidBaseMark !== false, _.lineAnchor || 'end', _.markIndex || 0, _.padding === undefined ? 0 : _.padding, _.method || 'naive').forEach(l => {

@@ -988,5 +937,4 @@ // write layout results to data stream

}
});
export { Label as label };
{
"name": "vega-label",
"version": "1.2.0",
"version": "1.2.1",
"description": "Label layout transform for Vega dataflows.",

@@ -21,3 +21,3 @@ "keywords": [

"prebuild": "rimraf build",
"build": "rollup -c --config-transform",
"build": "rollup -c rollup.config.mjs --config-transform",
"pretest": "yarn build --config-test",

@@ -36,3 +36,3 @@ "test": "tape 'test/**/*-test.js'",

},
"gitHead": "9a3faca4395cade9ecdfde90af98f1c53e9916b2"
"gitHead": "fb1092f6b931d450f9c210b67ae4752bd3dd461b"
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc