in public/bubblesets.js [795:888]
function testContainment(contour, bounds, members, nonMembers) {
// precise bounds checking
// copy hull values
var g = [];
var gbounds = null;
function contains(g, p) {
var line = null;
var first = null;
var crossings = 0;
g.forEach(function(cur) {
if(!line) {
line = new Line(cur.x(), cur.y(), cur.x(), cur.y());
first = cur;
return;
}
line.x1(line.x2());
line.y1(line.y2());
line.x2(cur.x());
line.y2(cur.y());
if(line.cuts(p)) {
crossings += 1;
}
});
if(first) {
line.x1(line.x2());
line.y1(line.y2());
line.x2(first.x());
line.y2(first.y());
if(line.cuts(p)) {
crossings += 1;
}
}
return crossings % 2 === 1;
}
// start with global SKIP value, but decrease skip amount if there
// aren't enough points in the surface
var thisSkip = skip;
// prepare viz attribute array
var size = contour.size();
if(thisSkip > 1) {
size = contour.size() / thisSkip;
// if we reduced too much (fewer than three points in reduced surface) reduce skip and try again
while((size < 3) && (thisSkip > 1)) {
thisSkip--;
size = contour.size() / thisSkip;
}
}
var xcorner = bounds.minX();
var ycorner = bounds.minY();
// simulate the surface we will eventually draw, using straight segments (approximate, but fast)
for(var i = 0;i < size - 1;i += 1) {
var px = contour.get(i * thisSkip).x() + xcorner;
var py = contour.get(i * thisSkip).y() + ycorner;
var r = {
x: px,
y: py,
width: 0,
height: 0
};
if(!gbounds) {
gbounds = new Rectangle(r);
} else {
gbounds.add(new Rectangle(r));
}
g.push(new Point(px, py));
}
var containsAll = true;
var containsExtra = false;
if(gbounds) {
members.forEach(function(item) {
var p = new Point(item.centerX(), item.centerY());
// check rough bounds
containsAll = containsAll && gbounds.contains(p);
// check precise bounds if rough passes
containsAll = containsAll && contains(g, p);
});
nonMembers.forEach(function(item) {
var p = new Point(item.centerX(), item.centerY());
// check rough bounds
if(gbounds.contains(p)) {
// check precise bounds if rough passes
if(contains(g, p)) {
containsExtra = true;
}
}
});
}
return [ containsAll, containsExtra ];
}