function occlude({ names = [], padding = 0, remove = true }, marks) {
const collect = [];
let callback = function () {
for (let i = 0; i < collect.length; ++i) {
const node = collect[i];
const bbox = node.getBoundingClientRect();
collect[i] = {
node,
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height
};
}
const filled = [];
for (const d of collect) {
const isOccluded =
names.includes(d.node.nodeName) && filled.some((e) => intersect(d, e, padding));
if (isOccluded) {
if (remove) {
d.node.parentElement.removeChild(d.node);
} else {
d3.select(d.node).classed("occluded", true);
}
} else {
filled.push(d);
}
}
};
marks.forEach((mark, j) => {
const { render } = mark;
mark.render = function () {
const g = render.apply(mark, arguments);
for (const e of g.childNodes) {
collect.push(e);
}
if (callback) {
const cb = callback;
setTimeout(cb, 1);
callback = null;
}
return g;
};
});
return marks;
function intersect(a, b, padding) {
return !(
a.x + a.width < b.x - padding ||
b.x + b.width < a.x - padding ||
a.y + a.height < b.y - padding ||
b.y + b.height < a.y - padding
);
}
}