function selfIntersections(poly, filterFn) {
  var seen = {};
  var l = poly.length;
  var isects = [];
  for (var o=0; o<l; o++) {
    var s0 = poly[o];
    var e0 = poly[(o+1) % l];
    arrayOrObject(s0, oc);
    arrayOrObject(e0, on);
    for (var p=0; p<l; p++) {
      if (o === p) { continue; }

      var s1 = poly[p]
      var e1 = poly[(p+1) % l];
      arrayOrObject(s1, pc);
      arrayOrObject(e1, pn);

      if (cmp(pc, oc) || cmp(pc, on) || cmp(pn, oc) || cmp(pn, on)) {
        continue;
      }

      var r = isect(oc, on, pc, pn);
      // since these are homogeneous vectors, if the last component `w` is 0
      // then we've done something wrong
      var wraw = r[2];
      if (wraw.length === 1 && !wraw[0]) {
        continue;
      }

      var w = float(r[2]);
      r[0] = float(r[0]) / w;
      r[1] = float(r[1]) / w;
      r.pop();

      if (cmp(r, oc) || cmp(r, on) || cmp(r, pc) || cmp(r, pn)) {
        continue;
      }

      var key = r+'';
      var unique = !seen[key];
      if (unique) {
        seen[key] = true;
      }

      var collect = unique;
      if (filterFn) {
        collect = filterFn(r, o, s0, e0, p, s1, e1, unique);
      }

      if (collect) {
        isects.push(r);
      }
    }
  }

  return isects;
}
function isect(a, b, fn) {
  var ret = [];

  var al = a.cells.length;
  var bl = b.cells.length;

  for (var ai = 0; ai<al; ai++) {
    var astart = a.positions[a.cells[ai][0]]
    var aend = a.positions[a.cells[ai][1]]

    for (var bi = 0; bi<bl; bi++) {

      var bstart =  b.positions[b.cells[bi][0]];
      var bend = b.positions[b.cells[bi][1]];

      if (doesIsect(astart, aend, bstart, bend)) {
        var r = segseg(
          astart,
          aend,
          bstart,
          bend
        );

        if (fn) {
          var res = fn(r, ret.length)
          if (res) {
            ret.push(res);
          } else {
            ret.push(r);
          }
        } else {
          ret.push(r);
        }
      }
    }
  }

  return ret;
}