function testContainment()

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 ];
  }