/**
 * Clip a line to a rectangle using the Cohen–Sutherland clipping algorithm.
 * [line] is a list of 4 ints <x1, y1, x2, y2>.
 * [rect] is a list of 4 ints <x1, y1, x2, y2>.
 * Results are stored in [line].
 * If [line] falls completely outside of [rect], false is returned, otherwise
 * true is returned.
 */
bool clipLine(List<int> line, List<int> rect) {
  int x0 = line[0];
  int y0 = line[1];
  int x1 = line[2];
  int y1 = line[3];
  int xmin = rect[0];
  int ymin = rect[1];
  int xmax = rect[2];
  int ymax = rect[3];

  const int INSIDE = 0; // 0000
  const int LEFT = 1;   // 0001
  const int RIGHT = 2;  // 0010
  const int BOTTOM = 4; // 0100
  const int TOP = 8;    // 1000

  // Compute the bit code for a point (x, y) using the clip rectangle
  // bounded diagonally by (xmin, ymin), and (xmax, ymax)
  int _computeOutCode(int x, int y) {
    int code = INSIDE; // initialised as being inside of clip window
    if (x < xmin) { // to the left of clip window
      code |= LEFT;
    } else if (x > xmax) { // to the right of clip window
      code |= RIGHT;
    }

    if (y < ymin) { // below the clip window
      code |= BOTTOM;
    } else if (y > ymax) { // above the clip window
      code |= TOP;
    }

    return code;
  }

  // compute outcodes for P0, P1, and whatever point lies outside the clip
  // rectangle
  int outcode0 = _computeOutCode(x0, y0);
  int outcode1 = _computeOutCode(x1, y1);
  bool accept = false;

  while (true) {
    if ((outcode0 | outcode1) == 0) {
      // Bitwise OR is 0. Trivially accept and get out of loop
      accept = true;
      break;
    } else if ((outcode0 & outcode1) != 0) {
      // Bitwise AND is not 0. Trivially reject and get out of loop
      break;
    } else {
      // failed both tests, so calculate the line segment to clip
      // from an outside point to an intersection with clip edge

      // At least one endpoint is outside the clip rectangle; pick it.
      int outcodeOut = outcode0 != 0 ? outcode0 : outcode1;

      int x, y;
      // Now find the intersection point;
      // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
      if ((outcodeOut & TOP) != 0) {
        // point is above the clip rectangle
        x = x0 + (x1 - x0) * (ymax - y0) ~/ (y1 - y0);
        y = ymax;
      } else if ((outcodeOut & BOTTOM) != 0) {
        // point is below the clip rectangle
        x = x0 + (x1 - x0) * (ymin - y0) ~/ (y1 - y0);
        y = ymin;
      } else if ((outcodeOut & RIGHT) != 0) {
        // point is to the right of clip rectangle
        y = y0 + (y1 - y0) * (xmax - x0) ~/ (x1 - x0);
        x = xmax;
      } else if ((outcodeOut & LEFT) != 0) {
        // point is to the left of clip rectangle
        y = y0 + (y1 - y0) * (xmin - x0) ~/ (x1 - x0);
        x = xmin;
      }

      // Now we move outside point to intersection point to clip
      // and get ready for next pass.
      if (outcodeOut == outcode0) {
        x0 = x;
        y0 = y;
        outcode0 = _computeOutCode(x0, y0);
      } else {
        x1 = x;
        y1 = y;
        outcode1 = _computeOutCode(x1, y1);
      }
    }
  }

  line[0] = x0;
  line[1] = y0;
  line[2] = x1;
  line[3] = y1;

  return accept;
}
