vega-scenegraph
Version:
Vega scenegraph and renderers.
79 lines (66 loc) • 2.18 kB
JavaScript
import Marks from './marks/index';
import {error} from 'vega-util';
import Bounds from './Bounds';
export function intersect(scene, bounds, filter) {
const hits = [], // intersection results
box = new Bounds().union(bounds), // defensive copy
type = scene.marktype;
return type ? intersectMark(scene, box, filter, hits)
: type === 'group' ? intersectGroup(scene, box, filter, hits)
: error('Intersect scene must be mark node or group item.');
}
function intersectMark(mark, box, filter, hits) {
if (visitMark(mark, box, filter)) {
const items = mark.items,
type = mark.marktype,
n = items.length;
let i = 0;
if (type === 'group') {
for (; i<n; ++i) {
intersectGroup(items[i], box, filter, hits);
}
} else {
for (const test = Marks[type].isect; i<n; ++i) {
const item = items[i];
if (intersectItem(item, box, test)) hits.push(item);
}
}
}
return hits;
}
function visitMark(mark, box, filter) {
// process if bounds intersect and if
// (1) mark is a group mark (so we must recurse), or
// (2) mark is interactive and passes filter
return mark.bounds && box.intersects(mark.bounds) && (
mark.marktype === 'group' ||
mark.interactive !== false && (!filter || filter(mark))
);
}
function intersectGroup(group, box, filter, hits) {
// test intersect against group
// skip groups by default unless filter says otherwise
if ((filter && filter(group.mark)) &&
intersectItem(group, box, Marks.group.isect)) {
hits.push(group);
}
// recursively test children marks
// translate box to group coordinate space
const marks = group.items,
n = marks && marks.length;
if (n) {
const x = group.x || 0,
y = group.y || 0;
box.translate(-x, -y);
for (let i=0; i<n; ++i) {
intersectMark(marks[i], box, filter, hits);
}
box.translate(x, y);
}
return hits;
}
function intersectItem(item, box, test) {
// test bounds enclosure, bounds intersection, then detailed test
const bounds = item.bounds;
return box.encloses(bounds) || (box.intersects(bounds) && test(item, box));
}