calculate_INTERNAL method

SegmentList calculate_INTERNAL(
  1. bool primaryPolyInverted,
  2. bool secondaryPolyInverted
)

Implementation

SegmentList calculate_INTERNAL(
    bool primaryPolyInverted, bool secondaryPolyInverted) {
  //
  // main event loop
  //
  var segments = new SegmentList();

  status_root = new StatusLinkedList();

  while (!event_root.isEmpty) {
    var ev = event_root.head;

    //if (buildLog != null) buildLog.vert(ev.pt.x);

    if (ev.isStart) {
      // if (buildLog != null) {
      //   buildLog.segmentNew(ev.seg, ev.primary);
      // }

      var surrounding = statusFindSurrounding(ev);
      var above = surrounding.before != null ? surrounding.before : null;
      var below = surrounding.after != null ? surrounding.after : null;

      // if( buildLog != null )
      // {
      // buildLog.tempStatus(
      // ev.seg,
      // above != null ? above.seg : (object)false,
      // below != null ? below.seg : (object)false
      // );
      // }

      var eve = checkBothIntersections(ev, above, below);
      if (eve != null) {
        // ev and eve are equal
        // we'll keep eve and throw away ev

        // merge ev.seg's fill information into eve.seg

        if (selfIntersection) {
          var toggle = false; // are we a toggling edge?
          if (ev.seg.myFill.below == null)
            toggle = true;
          else
            toggle = ev.seg.myFill.above != ev.seg.myFill.below;

          // merge two segments that belong to the same polygon
          // think of this as sandwiching two segments together, where `eve.seg` is
          // the bottom -- this will cause the above fill flag to toggle
          if (toggle) {
            eve.seg.myFill.above = !eve.seg.myFill.above;
          }
        } else {
          // merge two segments that belong to different polygons
          // each segment has distinct knowledge, so no special logic is needed
          // note that this can only happen once per segment in this phase, because we
          // are guaranteed that all self-intersections are gone
          eve.seg.otherFill = ev.seg.myFill;
        }

        // if (buildLog != null) {
        //   buildLog.segmentUpdate(eve.seg);
        // }

        ev.other.remove();
        ev.remove();
      }

      if (event_root.head != ev) {
        // something was inserted before us in the event queue, so loop back around and
        // process it before continuing
        // if (buildLog != null) {
        //   buildLog.rewind(ev.seg);
        // }

        continue;
      }

      //
      // calculate fill flags
      //
      if (selfIntersection) {
        bool toggle = false; // are we a toggling edge?

        // if we are a new segment...
        if (ev.seg.myFill.below == null)
          // then we toggle
          toggle = true;
        else
          // we are a segment that has previous knowledge from a division
          toggle =
              ev.seg.myFill.above != ev.seg.myFill.below; // calculate toggle

        // next, calculate whether we are filled below us
        if (below == null) {
          // if nothing is below us...
          // we are filled below us if the polygon is inverted
          ev.seg.myFill.below = primaryPolyInverted;
        } else {
          // otherwise, we know the answer -- it's the same if whatever is below
          // us is filled above it
          ev.seg.myFill.below = below.seg.myFill.above;
        }

        // since now we know if we're filled below us, we can calculate whether
        // we're filled above us by applying toggle to whatever is below us
        if (toggle)
          ev.seg.myFill.above = ev.seg.myFill.below != null
              ? !ev.seg.myFill.below
              : ev.seg.myFill.above;
        else
          ev.seg.myFill.above = ev.seg.myFill.below != null
              ? ev.seg.myFill.below
              : ev.seg.myFill.above;
      } else {
        // now we fill in any missing transition information, since we are all-knowing
        // at this point

        if (ev.seg.otherFill == null) {
          // if we don't have other information, then we need to figure out if we're
          // inside the other polygon
          var inside = false;
          if (below == null) {
            // if nothing is below us, then we're inside if the other polygon is
            // inverted
            inside = ev.primary ? secondaryPolyInverted : primaryPolyInverted;
          } else {
            // otherwise, something is below us
            // so copy the below segment's other polygon's above
            if (ev.primary == below.primary)
              inside = below.seg.otherFill.above;
            else
              inside = below.seg.myFill.above;
          }

          ev.seg.otherFill = new SegmentFill(above: inside, below: inside);
        }
      }

      // if( buildLog != null )
      // {
      // buildLog.status(
      // ev.seg,
      // above != null ? above.seg : (object)false,
      // below != null ? below.seg : (object)false
      // );
      // }

      // insert the status and remember it for later removal
      ev.other.status = status_root.insert(surrounding, ev);
    } else {
      var st = ev.status;

      if (st == null) {
        throw new Exception(
            "PolyBool: Zero-length segment detected; your epsilon is probably too small or too large");
      }

      // removing the status will create two new adjacent edges, so we'll need to check
      // for those
      if (status_root.exists(st.prev) && status_root.exists(st.next))
        checkIntersection(st.prev.ev, st.next.ev);

      // if (buildLog != null) buildLog.statusRemove(st.ev.seg);

      // remove the status
      st.remove();

      // if we've reached this point, we've calculated everything there is to know, so
      // save the segment for reporting
      if (!ev.primary) {
        // make sure `seg.myFill` actually points to the primary polygon though
        var s = ev.seg.myFill;
        ev.seg.myFill = ev.seg.otherFill;
        ev.seg.otherFill = s;
      }

      segments.add(ev.seg);
    }

    // remove the event and continue
    event_root.head.remove();
  }

  // if (buildLog != null) {
  //   buildLog.done();
  // }

  return segments;
}