chain method

List<List<Coordinate>> chain(
  1. SegmentList segments
)

Implementation

List<List<JTS.Coordinate>> chain(SegmentList segments) {
  this.chains = [];
  this.regions = [];

  for (var seg in segments) {
    var pt1 = seg.start;
    var pt2 = seg.end;

    if (Epsilon().pointsSame(pt1, pt2)) {
      print(
          "PolyBool: Warning: Zero-length segment detected; your epsilon is probably too small or too large");
      continue;
    }

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

    first_match = Match(index: 0, matches_head: false, matches_pt1: false);

    second_match =
        new Match(index: 0, matches_head: false, matches_pt1: false);

    next_match = first_match;

    for (var i = 0; i < chains.length; i++) {
      var chain = chains[i];
      var head = chain[0];
      var head2 = chain[1];

      var tail = chain[chain.length - 1];
      var tail2 = chain[chain.length - 2];

      if (Epsilon().pointsSame(head, pt1)) {
        if (setMatch(i, true, true)) break;
      } else if (Epsilon().pointsSame(head, pt2)) {
        if (setMatch(i, true, false)) break;
      } else if (Epsilon().pointsSame(tail, pt1)) {
        if (setMatch(i, false, true)) break;
      } else if (Epsilon().pointsSame(tail, pt2)) {
        if (setMatch(i, false, false)) break;
      }
    }

    if (next_match == first_match) {
      // we didn't match anything, so create a new chain
      List<JTS.Coordinate> pointList = [];
      pointList.add(pt1);
      pointList.add(pt2);
      chains.add(pointList);

      // if (buildLog != null) {
      //   buildLog.chainNew(pt1, pt2);
      // }

      continue;
    }

    if (next_match == second_match) {
      // we matched a single chain

      // if( buildLog != null )
      // buildLog.chainMatch( first_match.index );

      // add the other point to the apporpriate end, and check to see if we've closed the
      // chain into a loop

      var index = first_match.index;
      var pt = first_match.matches_pt1
          ? pt2
          : pt1; // if we matched pt1, then we add pt2, etc
      var addToHead = first_match
          .matches_head; // if we matched at head, then add to the head

      var chain = chains[index];
      var grow = addToHead ? chain[0] : chain[chain.length - 1];
      var grow2 = addToHead ? chain[1] : chain[chain.length - 2];
      var oppo = addToHead ? chain[chain.length - 1] : chain[0];
      var oppo2 = addToHead ? chain[chain.length - 2] : chain[1];

      if (Epsilon().pointsCollinear(grow2, grow, pt)) {
        // grow isn't needed because it's directly between grow2 and pt:
        // grow2 ---grow---> pt
        if (addToHead) {
          // if (buildLog != null)
          //   buildLog.chainRemoveHead(first_match.index, pt);

          chain.removeAt(0);
        } else {
          // if (buildLog != null)
          //   buildLog.chainRemoveTail(first_match.index, pt);

          chain.removeAt(chain.length - 1);
        }
        grow = grow2; // old grow is gone... new grow is what grow2 was
      }

      if (Epsilon().pointsSame(oppo, pt)) {
        // we're closing the loop, so remove chain from chains
        chains.removeAt(index);

        if (Epsilon().pointsCollinear(oppo2, oppo, grow)) {
          // oppo isn't needed because it's directly between oppo2 and grow:
          // oppo2 ---oppo--->grow
          if (addToHead) {
            // if (buildLog != null)
            //   buildLog.chainRemoveTail(first_match.index, grow);

            chain.removeAt(chain.length - 1);
          } else {
            // if (buildLog != null)
            //   buildLog.chainRemoveHead(first_match.index, grow);

            chain.removeAt(0);
          }
        }

        // if (buildLog != null) buildLog.chainClose(first_match.index);

        // we have a closed chain!
        regions.add(chain);
        continue;
      }

      // not closing a loop, so just add it to the apporpriate side
      if (addToHead) {
        //   if (buildLog != null) buildLog.chainAddHead(first_match.index, pt);

        chain.insert(0, pt);
      } else {
        // if (buildLog != null) buildLog.chainAddTail(first_match.index, pt);

        chain.add(pt);
      }

      continue;
    }

    // otherwise, we matched two chains, so we need to combine those chains together

    var F = first_match.index;
    var S = second_match.index;

    // if (buildLog != null) {
    //   buildLog.chainConnect(F, S);
    // }

    var reverseF = chains[F].length <
        chains[S].length; // reverse the shorter chain, if needed
    if (first_match.matches_head) {
      if (second_match.matches_head) {
        if (reverseF) {
          // <<<< F <<<< --- >>>> S >>>>
          reverseChain(F);
          // >>>> F >>>> --- >>>> S >>>>
          appendChain(F, S);
        } else {
          // <<<< F <<<< --- >>>> S >>>>
          reverseChain(S);
          // <<<< F <<<< --- <<<< S <<<<   logically same as:
          // >>>> S >>>> --- >>>> F >>>>
          appendChain(S, F);
        }
      } else {
        // <<<< F <<<< --- <<<< S <<<<   logically same as:
        // >>>> S >>>> --- >>>> F >>>>
        appendChain(S, F);
      }
    } else {
      if (second_match.matches_head) {
        // >>>> F >>>> --- >>>> S >>>>
        appendChain(F, S);
      } else {
        if (reverseF) {
          // >>>> F >>>> --- <<<< S <<<<
          reverseChain(F);
          // <<<< F <<<< --- <<<< S <<<<   logically same as:
          // >>>> S >>>> --- >>>> F >>>>
          appendChain(S, F);
        } else {
          // >>>> F >>>> --- <<<< S <<<<
          reverseChain(S);
          // >>>> F >>>> --- >>>> S >>>>
          appendChain(F, S);
        }
      }
    }
  }

  return regions;
}