/** this test compares result of MT and ST distance calculations */
  void devTestMTvsSTprecision() throws Exception {

    if (DEBUG) printf("%s.testPrecisionMTvsST()\n", this.getClass().getName());
    double vs = 0.5 * MM;
    double w = 10 * MM; // half width
    // double firstLayerThickness = 0.7;
    // double firstLayerThickness = 1.7;
    double firstLayerThickness = 2.7;
    // double xmin = -w, xmax = w, ymin = -w, ymax = w, zmin = -vs, zmax = 2*vs;
    double xmin = -w, xmax = w, ymin = -w, ymax = w, zmin = -w, zmax = w;
    int densityBitCount = 8;
    int distanceBitCount = 16;

    int subvoxelResolution = (1 << densityBitCount) - 1;

    int voxelSquareSize = 20; // for visualization
    boolean snapToGrid = false;
    int iterationsCount = 0;
    int threadCount = 4;
    boolean writeViz = false;
    boolean compareDistances = false;

    Bounds bounds = new Bounds(xmin, xmax, ymin, ymax, zmin, zmax);
    double pnts[];
    pnts = makeCircleZ(0.5 * vs, 0.5 * vs, 0.5 * vs, 0.9 * w, 128);
    // double pnts[] = makeCircleX(0.5*vs, 0.5*vs, 0.5*vs, 0.9*w, 128);
    // double pnts[]  = makeCircleY(0.5*vs, 0.5*vs, 0.5*vs, 0.9*w, 128);
    // pnts = makeUnion(makeUnion(makeCircleX(0.5*vs, 0.5*vs, 0.5*vs, 0.9*w,
    // 128),makeCircleY(0.5*vs, 0.5*vs, 0.5*vs, 0.9*w, 128)),makeCircleZ(0.5*vs, 0.5*vs, 0.5*vs,
    // 0.9*w, 128));
    // double pnts[] = makeUnion(makeUnion(makeCircleZ(0.5*vs, 0.25*vs, 0.5*w, 0.9*w,
    // 128),makeCircleZ(0.5*vs, 0.5*vs, 0.5*w, 0.9*w, 128)),makeCircleZ(0.5*vs, 0.5*vs, 0.5*vs,
    // 0.9*w, 128));

    ArrayAttributeGridInt indexGrid1 = new ArrayAttributeGridInt(bounds, vs, vs);
    ArrayAttributeGridInt indexGrid2 = new ArrayAttributeGridInt(bounds, vs, vs);

    int pcount = pnts.length / 3;
    double pntx[] = new double[pcount];
    double pnty[] = new double[pcount];
    double pntz[] = new double[pcount];

    ClosestPointIndexer.getPointsInGridUnits(indexGrid1, pnts, pntx, pnty, pntz);
    if (snapToGrid) {
      ClosestPointIndexer.snapToVoxels(pntx);
      ClosestPointIndexer.snapToVoxels(pnty);
      ClosestPointIndexer.snapToVoxels(pntz);
    }

    ClosestPointIndexer.initFirstLayer(indexGrid1, pntx, pnty, pntz, firstLayerThickness);
    ClosestPointIndexer.initFirstLayer(indexGrid2, pntx, pnty, pntz, firstLayerThickness);

    for (int z = 0; z < indexGrid1.getDepth(); z++) {
      renderDiff(
          indexGrid1,
          z,
          pntx,
          pnty,
          pntz,
          voxelSquareSize,
          fmt("/tmp/dist/dist00_%02d.png", z),
          true);
    }

    int usedCount = ClosestPointIndexer.removeUnusedPoints(indexGrid1, pntx, pnty, pntz);
    printf(
        "grid: [%d x %d x %d] threads: %d points: %d usedPoints: %d \n",
        indexGrid1.getWidth(),
        indexGrid1.getHeight(),
        indexGrid1.getDepth(),
        threadCount,
        pcount,
        usedCount);

    // distribute distances to the whole grid
    long t0 = time();
    ClosestPointIndexer.PI3_sorted(pntx, pnty, pntz, indexGrid1);
    printf("ClosestPointIndexer done: %d\n", (time() - t0));
    t0 = time();
    ClosestPointIndexerMT.PI3_MT(pntx, pnty, pntz, indexGrid2, threadCount);
    printf("ClosestPointIndexerMT done: %d\n ", (time() - t0));
    long diff = GridUtil.compareGrids(indexGrid1, indexGrid2);
    printf("index difference count: %d\n", diff);

    printf("maxDiff1: %6.4f\n", getMaxDiff(indexGrid1, pntx, pnty, pntz));
    printf("maxDiff2: %6.4f\n", getMaxDiff(indexGrid1, pntx, pnty, pntz));

    if (writeViz) {
      for (int z = 0; z < indexGrid1.getDepth(); z++) {
        // renderDiff(indexGrid1, z, pntx, pnty, pntz, voxelSquareSize,
        // fmt("/tmp/dist/dist01_%02d.png",z), true);
        renderDiff(
            indexGrid2,
            z,
            pntx,
            pnty,
            pntz,
            voxelSquareSize,
            fmt("/tmp/dist/distDiff2_%02d.png", z),
            true);
      }
    }

    if (compareDistances) {
      AttributeGrid distGrid1 = makeDistanceGrid(bounds, -w, w, distanceBitCount, vs);
      AttributeGrid distGrid2 = makeDistanceGrid(bounds, -w, w, distanceBitCount, vs);
      ClosestPointIndexer.getPointsInWorldUnits(indexGrid1, pntx, pnty, pntz);

      ClosestPointIndexer.makeDistanceGrid(indexGrid1, pntx, pnty, pntz, null, -w, w, distGrid1);
      ClosestPointIndexer.makeDistanceGrid(indexGrid2, pntx, pnty, pntz, null, -w, w, distGrid2);
      long diffDist = GridUtil.compareGrids(distGrid1, distGrid2);
      printf("distance difference count: %d\n", diffDist);
    }
  }
  void makeTestPoint() throws Exception {

    if (DEBUG) printf("%s.makeTestPoint()\n", this.getClass().getName());
    double vs = 0.05 * MM;
    double w = 10 * MM; // half width
    double firstLayerThickness = 2.5; // 1.5;//3.5;//2.4;//1.8;//0.8
    double xmin = -w, xmax = w, ymin = -w, ymax = w, zmin = -w, zmax = w; // 10*vs;
    int subvoxelResolution = 100;
    double INF = 1.e5; // point to ignore
    int voxelSquareSize = 25; // for visualization
    boolean snapToGrid = false;
    int iterationsCount = 0;
    int threadCount = 1;

    ArrayAttributeGridInt indexGrid =
        new ArrayAttributeGridInt(new Bounds(xmin, xmax, ymin, ymax, zmin, zmax), vs, vs);
    double pnts[] =
        new double[] {
          0,
          0,
          0,
          // 1*vs, 0*vs, 0.5*vs,  0.2*vs, -2.3*vs,  0.5*vs,
          // 1*vs, 0*vs, 0.5*vs,  0.2*vs, -2.4*vs,  0.5*vs,
          // 1*vs, 0*vs, 0.5*vs,  0.5*vs, -2.4*vs,  0.5*vs, / bad
          // 7*vs, 0*vs, 0.5*vs,  6.5*vs, -2.4*vs,  0.5*vs, //6.5*vs, 2.4*vs,  0.5*vs,

          7 * vs,
          0 * vs,
          0.5 * vs,
          6.5 * vs,
          2.4 * vs,
          0.5 * vs,
          6.5 * vs,
          -2.4 * vs,
          0.5 * vs,
          -7 * vs,
          0 * vs,
          0.5 * vs,
          -6.5 * vs,
          2.4 * vs,
          0.5 * vs,
          -6.5 * vs,
          -2.4 * vs,
          0.5 * vs,
          0,
          7 * vs,
          0.5 * vs,
          2.4 * vs,
          6.5 * vs,
          0.5 * vs,
          -2.4 * vs,
          6.5 * vs,
          0.5 * vs,
          0,
          -7 * vs,
          0.5 * vs,
          2.4 * vs,
          -6.5 * vs,
          0.5 * vs,
          -2.4 * vs,
          -6.5 * vs,
          0.5 * vs,
        };
    // pnts = makeCircle(0.2*vs, 0.6*vs, 0, 7*vs, 32);
    // pnts = makeCircleZ(0.2*vs, 0.2*vs, 0.2*vs, 7*vs, 128);
    // pnts = makeCircleZ(0.5*vs, 0.5*vs, 0.5*vs, 7*vs, 64);
    // pnts = makeCircleZ(0.5*vs, 0.5*vs, 0.5*vs, 0.8*w, 64);
    // pnts = makeCircleZ(0.5*vs, 0.5*vs, 0.5*vs, 0.7*w, 80);
    // pnts = makeCircleZ(0.5*vs, 0.5*vs, 0.5*vs, 0.7*w, 256);
    // pnts = makeCircleX(0.5*vs, 0.5*vs, 0.5*vs, 7*vs, 32);
    // pnts = makeCircleY(0.5*vs, 0.5*vs, 0.5*vs, 7*vs, 32);
    // pnts = makeUnion(makeUnion(makeCircleX(0.5*vs, 0.5*vs, 0.5*vs, 7*vs, 32),makeCircleY(0.5*vs,
    // 0.5*vs, 0.5*vs, 7*vs, 32)),makeCircleZ(0.5*vs, 0.5*vs, 0.5*vs, 7*vs, 32));
    pnts =
        makeUnion(
            makeUnion(
                makeCircleX(0.5 * vs, 0.5 * vs, 0.5 * vs, 0.9 * w, 128),
                makeCircleY(0.5 * vs, 0.5 * vs, 0.5 * vs, 0.9 * w, 128)),
            makeCircleZ(0.5 * vs, 0.5 * vs, 0.5 * vs, 0.9 * w, 128));
    // pnts = makeUnion(makeUnion(makeCircleX(0.3*vs, 0.3*vs, 0.3*vs, 7*vs, 32),makeCircleY(0.3*vs,
    // 0.3*vs, 0.3*vs, 7*vs, 32)),makeCircleZ(0.3*vs, 0.3*vs, 0.3*vs, 7*vs, 32));
    // pnts = makeUnion(makeUnion(makeCircleX(0.3*vs, 0.3*vs, 0.3*vs, 7*vs, 64),makeCircleY(0.3*vs,
    // 0.3*vs, 0.3*vs, 7*vs, 64)),makeCircleZ(0.3*vs, 0.3*vs, 0.3*vs, 7*vs, 64));
    // pnts = makeUnion(makeUnion(makeCircleX(0.5*vs, 0.5*vs, 0.5*vs, 7*vs, 64),makeCircleY(0.5*vs,
    // 0.5*vs, 0.5*vs, 7*vs, 64)),makeCircleZ(0.5*vs, 0.5*vs, 0.5*vs, 7*vs, 64));
    // pnts = makeUnion(makeUnion(makeCircleX(0.5*vs, 0.5*vs, 0.5*vs, 7*vs, 128),makeCircleY(0.5*vs,
    // 0.5*vs, 0.5*vs, 7*vs, 128)),makeCircleZ(0.5*vs, 0.5*vs, 0.5*vs, 7*vs, 128));
    // pnts = makeUnion(makeCircle(0.5*vs, 0.5*vs, 0.5*vs, 7*vs, 32),makeCircle(2.5*vs, 0.5*vs,
    // 3.5*vs, 7*vs, 32));
    // pnts = makeUnion(makeCircle(-9.5*vs, 0.5*vs, 0, 7*vs, 32),makeCircle(9.5*vs, 0.5*vs, 0, 7*vs,
    // 32));
    // pnts = makeUnion(makeUnion(makeCircle(-10.5*vs, 0.5*vs, 0.5*vs, 7*vs, 32),makeCircle(11.5*vs,
    // 0.5*vs, 0.5*vs, 7*vs, 32)),
    //                 makeUnion(makeCircle(0.5*vs, -10.5*vs, 0.5*vs, 7*vs, 32),makeCircle(0.5*vs,
    // 11.5*vs, 0.5*vs, 7*vxs, 32)));
    // pnts = makeRandomPoints();
    int pcount = pnts.length / 3;
    // printf("pcount: %d\n", (pcount-1));
    double pntx[] = new double[pcount];
    double pnty[] = new double[pcount];
    double pntz[] = new double[pcount];

    ClosestPointIndexer.getPointsInGridUnits(indexGrid, pnts, pntx, pnty, pntz);
    if (snapToGrid) {
      ClosestPointIndexer.snapToVoxels(pntx);
      ClosestPointIndexer.snapToVoxels(pnty);
      ClosestPointIndexer.snapToVoxels(pntz);
    }

    ClosestPointIndexer.initFirstLayer(indexGrid, pntx, pnty, pntz, firstLayerThickness);
    int usedCount = ClosestPointIndexer.removeUnusedPoints(indexGrid, pntx, pnty, pntz);
    printf(
        "grid: [%d x %d x %d] threads: %d points: %d\n",
        indexGrid.getWidth(), indexGrid.getHeight(), indexGrid.getDepth(), threadCount, pcount);

    int nz = indexGrid.getDepth();

    // if(true) printIndices(indexGrid);
    // for(int z = 0; z < nz; z++)renderDiff(indexGrid, z, pntx, pnty, pntz, voxelSquareSize,
    // fmt("/tmp/dist/distDiff1_%02d.png",z), true);

    // distribute distances to the whole grid
    long t0 = time();
    ClosestPointIndexerMT.PI3_MT(pntx, pnty, pntz, indexGrid, threadCount);
    // ClosestPointIndexer.PI3(pntx, pnty, pntz, indexGrid);
    printf("ClosestPointIndexer time: %d\n ", (time() - t0));
    // printIndices(indexGrid);
    // printDiff(indexGrid, pntx, pnty, pntz, true);
    // printDiff(indexGrid, pntx, pnty, pntz, false);
    // printf("maxDiff: %10.3e\n", getMaxDiff(indexGrid, pntx, pnty, pntz));
    for (int z = 0; z < nz; z++) {
      // renderDiff(indexGrid, z, pntx, pnty, pntz, voxelSquareSize,
      // fmt("/tmp/dist/distDiff2_%02d.png",z), true);
    }
  }