/*
    this is very unefficient and stack intensive recursive algorithm
  */
  void floodFill(int x, int y, int z, int level) {

    if (level > MAXLEVEL) return;
    level++;
    if (x <= 0 || y <= 0 || z <= 0 || x >= nx1 || y >= ny1 || z >= nz1) {
      // we don't test boundary poins
      return;
    }

    if (mask.get(x, y, z) != 0) {
      // voxel already was visited
      return;
    }

    if (!compareState(grid, x, y, z, state)) // different material
    return;

    // voxel of our material - add it and check 6 heighbors
    if (m_component != null) m_component.add(x, y, z);
    updateBounds(x, y, z);
    m_volume++;

    mask.set(x, y, z, 1);

    floodFill(x + 1, y, z, level);
    floodFill(x - 1, y, z, level);
    floodFill(x, y + 1, z, level);
    floodFill(x, y - 1, z, level);
    floodFill(x, y, z + 1, level);
    floodFill(x, y, z - 1, level);
  }
  /** similar to recursive fill, but uses no system stack */
  void floodFillQue(int start[]) {

    // int start_state = grid.getState(start[0], start[1], start[2] );

    LinkedList<int[]> que = new LinkedList<int[]>();

    que.add(start);
    mask.set(start[0], start[1], start[2], 1);

    while (!que.isEmpty()) {
      int vc[] = que.remove();

      int i = vc[0];
      int j = vc[1];
      int k = vc[2];

      if (compareState(grid, i, j, k, state)) {

        if (m_component != null) m_component.add(i, j, k);
        updateBounds(i, j, k);
        m_volume++;
        // test adjacent voxels

        for (int n1 = -1; n1 < 2; n1++) {
          for (int n2 = -1; n2 < 2; n2++) {
            for (int n3 = -1; n3 < 2; n3++) {
              if (n1 == 0 && n2 == 0 && n3 == 0) continue;

              int ni = i + n1;
              int nj = j + n2;
              int nk = k + n3;

              if (mask.get(ni, nj, nk) == 0) {

                if (!compareState(grid, ni, nj, nk, state)) continue;

                que.offer(new int[] {ni, nj, nk});
                mask.set(ni, nj, nk, 1);
                // printf("que: %d (%d,%d,%d)\n",que.size(),ni,nj,nk);
              }
            }
          }
        }
      }
    }
  }
  /** fills region using analog of scan line algorithm */
  boolean fillScanLineStack(int start[]) {

    StackInt3 stack = new StackInt3(100);

    int pnt[] = new int[3]; // point to pop from stack

    boolean spanxLeft, spanxRight, spanzLeft, spanzRight;
    int x = start[0], y = start[1], z = start[2];

    stack.push(x, y, z);

    while (stack.pop(pnt)) {

      x = pnt[0];
      y = pnt[1];
      z = pnt[2];

      if (mask.get(x, y, z) != 0) {
        // this line was already visited
        continue;
      }
      int y1 = y;

      // go down in y as far as possible
      while (y1 >= 0 && compareState(grid, x, y1, z, state)) {
        y1--;
      }
      y1++; // increment back
      // printf("scanline [%2d,%2d,%2d]->", x,y1,z);

      spanxLeft = spanxRight = spanzLeft = spanzRight = false;

      while ((y1 <= ny1) && compareState(grid, x, y1, z, state)) {
        // fill one scan line
        // mark voxel visited
        mask.set(x, y1, z, 1);
        if (m_component != null) m_component.add(x, y1, z);
        updateBounds(x, y1, z);
        m_volume++;

        // check x-direction
        if (!spanxLeft
            && (x > 0)
            && (mask.get(x - 1, y1, z) == 0)
            && compareState(grid, x - 1, y1, z, state)) {
          // start of potential new span
          if (!stack.push(x - 1, y1, z)) return false;
          spanxLeft = true;

        } else if (spanxLeft
            && (x > 0)
            && ((mask.get(x - 1, y1, z) != 0) || !compareState(grid, x - 1, y1, z, state))) {
          // end of potential new span
          spanxLeft = false;
        }

        if (!spanxRight
            && x < nx1
            && (mask.get(x + 1, y1, z) == 0)
            && compareState(grid, x + 1, y1, z, state)) {

          // start of potential new span
          if (!stack.push(x + 1, y1, z)) return false; // stack overflow
          spanxRight = true;

        } else if (spanxRight
            && x < nx1
            && ((mask.get(x + 1, y1, z) != 0) || !compareState(grid, x + 1, y1, z, state))) {
          // end of potential new span

          spanxRight = false;
        }

        // check z direction
        if (!spanzLeft
            && (z > 0)
            && (mask.get(x, y1, z - 1) == 0)
            && compareState(grid, x, y1, z - 1, state)) {
          // start of potential new span
          if (!stack.push(x, y1, z - 1)) return false;
          spanzLeft = true;

        } else if (spanzLeft
            && (z > 0)
            && ((mask.get(x, y1, z - 1) != 0) || !compareState(grid, x, y1, z - 1, state))) {
          // end of potential new span

          spanzLeft = false;
        }

        if (!spanzRight
            && z < nz1
            && (mask.get(x, y1, z + 1) == 0)
            && compareState(grid, x, y1, z + 1, state)) {
          // start of potential new span

          if (!stack.push(x, y1, z + 1)) return false; // stack overflow
          spanzRight = true;

        } else if (spanzRight
            && z < nz1
            && ((mask.get(x, y1, z + 1) != 0) || !compareState(grid, x, y1, z + 1, state))) {

          // end of potential new span
          spanzRight = false;
        }
        y1++;
      }
      // printf("[%2d,%2d,%2d]\n ", x,y1,z);

    }

    // stack.printStat();

    return true;
  } // fillScanLine
  /** fills region using analog of scan line algorithm */
  boolean fillScanLineQueue(int start[]) {

    // LinkedList<int[]> que = new LinkedList<int[]>();
    // ArrayDeque<int[]> que = new ArrayDeque<int[]>();
    QueueInt que = new QueueInt(100000);

    boolean spanxLeft, spanxRight, spanzLeft, spanzRight;
    int x = start[0], y = start[1], z = start[2];
    int pnt[] = new int[3];

    que.offer(x, y, z);
    while (!que.isEmpty()) {

      // int pnt[] = que.remove();
      que.remove(pnt);
      x = pnt[0];
      y = pnt[1];
      z = pnt[2];

      if (mask.get(x, y, z) != 0) {
        // this line was already visited
        continue;
      }
      // printf("new scanline: [%d,%d,%d]\n", x, y, z);
      // dumpScanLine(x,y,z);

      int y1 = y;
      // go down in y as far as possible
      while (y1 >= 0 && compareState(grid, x, y1, z, state)) {
        y1--;
      }
      y1++; // increment back
      // printf("scanline [%2d,%2d,%2d]->", x,y1,z);

      spanxLeft = spanxRight = spanzLeft = spanzRight = false;

      while ((y1 <= ny1) && compareState(grid, x, y1, z, state)) {
        // fill one scan line
        // mark voxel visited
        mask.set(x, y1, z, 1);
        if (m_component != null) m_component.add(x, y1, z);
        updateBounds(x, y1, z);
        m_volume++;

        // check x-direction
        if (!spanxLeft
            && (x > 0)
            && (mask.get(x - 1, y1, z) == 0)
            && compareState(grid, x - 1, y1, z, state)) {
          // start of potential new span
          // que.offer(new int[]{x - 1, y1, z});
          que.offer(x - 1, y1, z);

          spanxLeft = true;

        } else if (spanxLeft
            && (x > 0)
            && ((mask.get(x - 1, y1, z) != 0) || !compareState(grid, x - 1, y1, z, state))) {
          // end of potential new span

          spanxLeft = false;
        }

        if (!spanxRight
            && x < nx1
            && (mask.get(x + 1, y1, z) == 0)
            && compareState(grid, x + 1, y1, z, state)) {
          // start of potential new span

          // que.offer(new int[]{x + 1, y1, z});
          que.offer(x + 1, y1, z);
          spanxRight = true;

        } else if (spanxRight
            && x < nx1
            && ((mask.get(x + 1, y1, z) != 0) || !compareState(grid, x + 1, y1, z, state))) {
          // end of potential new span

          spanxRight = false;
        }

        // check z direction
        if (!spanzLeft
            && (z > 0)
            && (mask.get(x, y1, z - 1) == 0)
            && compareState(grid, x, y1, z - 1, state)) {
          // start of potential new span
          // que.offer(new int[]{x, y1, z-1});
          que.offer(x, y1, z - 1);
          spanzLeft = true;

        } else if (spanzLeft
            && (z > 0)
            && ((mask.get(x, y1, z - 1) != 0) || !compareState(grid, x, y1, z - 1, state))) {
          // end of potential new span

          spanzLeft = false;
        }

        if (!spanzRight
            && z < nz1
            && (mask.get(x, y1, z + 1) == 0)
            && compareState(grid, x, y1, z + 1, state)) {
          // start of potential new span
          // que.offer(new int[]{x, y1, z+1});
          que.offer(x, y1, z + 1);
          spanzRight = true;

        } else if (spanzRight
            && z < nz1
            && ((mask.get(x, y1, z + 1) != 0) || !compareState(grid, x, y1, z + 1, state))) {
          // end of potential new span

          spanzRight = false;
        }
        y1++;
      }
      // printf("[%2d,%2d,%2d]\n ", x,y1,z);

    }

    // que.printStat();
    return true;
  } // fillScanLineQue