TEST(MovingLeastSquares, isInvertibleByExchangingLandmarks) {
    const double rbegin = 0;
    const double rend = 5;
    const double repsmarks = 2.0;  // Spacing for landmarks.
    const double repstest = 0.1;   // Spacing for test positions.
    const double shift = 1;        // Fixed shift.
    const double sigma = 0.2;      // For random shifts.
    const double tolerance = 0.03;

    const Landmarks marks = randomGrid(rbegin, rend, repsmarks, shift, sigma);

    MovingLeastSquares forward;
    forward.setLandmarks(marks.src, marks.dst);

    MovingLeastSquares backward;
    backward.setLandmarks(marks.dst, marks.src);

    // Test that forward transform does something and forward-backward
    // transform gets back to original position.
    for (double x = rbegin; x < rend; x += repstest) {
        for (double y = rbegin; y < rend; y += repstest) {
            const McVec2d pos(x, y);
            const McVec2d tpos = forward.interpolate(pos);
            const McVec2d ttpos = backward.interpolate(tpos);
            const McVec2d tdiff = tpos - pos;
            const McVec2d ttdiff = ttpos - pos;

            EXPECT_THAT(tdiff.length(), Gt(tolerance));
            EXPECT_THAT(ttdiff.length(), Lt(tolerance));

#if 0  // Useful for debugging.
            printf("tdiff = %f, ttdiff = %f\n", tdiff.length(),
                   ttdiff.length());
#endif
        }
    }
}
TEST(MovingLeastSquares, shouldInterpolateSmoothly) {
    const double rbegin = 0;
    const double rend = 5;
    const double repsmarks = 2.0;  // Spacing for landmarks.
    const double shift = 1;        // Fixed shift.
    const double sigma = 0.2;      // For random shifts.
    const double tolerance = 0.01;

    const Landmarks marks = randomGrid(rbegin, rend, repsmarks, shift, sigma);

    MovingLeastSquares mls;
    mls.setLandmarks(marks.src, marks.dst);

    // Exactly interpolate landmarks.
    for (int i = 0; i < marks.src.size(); i++) {
        const McVec2d pos = McVec2d(marks.src[i][0], marks.src[i][1]);
        const McVec2d tpos = mls.interpolate(pos);
        const McVec2d tdiff = tpos - McVec2d(marks.dst[i][0], marks.dst[i][1]);
        EXPECT_FLOAT_EQ(0, tdiff.length());
    }

    const int seed = 38273;
    McRandom rand(seed);
    // Interpolate points close to landmarks.
    for (int i = 0; i < marks.src.size(); i++) {
        const double d = sigma * (rand.nextNumber() - 0.5);
        const McVec2d delta(d, d);
        const McVec2d pos = McVec2d(marks.src[i][0], marks.src[i][1]) + delta;
        const McVec2d tpos = mls.interpolate(pos);
        const McVec2d tdiff =
            tpos - delta - McVec2d(marks.dst[i][0], marks.dst[i][1]);
        EXPECT_THAT(tdiff.length(), Lt(tolerance));
#if 0  // Useful for debugging.
        printf("tdiff = %f.\n", tdiff.length());
#endif
    }
}
void HxMovingLeastSquaresWarp::compute() {
    if (!portAction.wasHit())
        return;

    HxUniformScalarField3* fromImage =
        (HxUniformScalarField3*)portFromImage.getSource();

    HxLandmarkSet* pointSet = hxconnection_cast<HxLandmarkSet>(portData);

    if (!fromImage)
        return;

    /* It is ok to warp without landmarks, if method is rigid and
       input has a transformation */
    if (!pointSet) {
        return;
    } else if (pointSet->getNumSets() < 2) {
        theMsg->printf(
            "Error: LandmarkWarp data has to contain at least 2 sets.");
        return;
    }

    HxUniformScalarField3* outputImage;

    HxUniformVectorField3* outputVectorField = 0;

    outputImage = createOutputDataSet();
    outputVectorField = createOutputVectorDataSet();

    float* outputImageData = (float*)outputImage->lattice().dataPtr();

    McDArray<McVec2d> landmarks1, landmarks2;

    prepareLandmarks(landmarks1, landmarks2);

    MovingLeastSquares mls;
    mls.setAlpha(portAlpha.getValue());
    mls.setLandmarks(landmarks2, landmarks1);

    const McDim3l& dims = outputImage->lattice().getDims();

    McVec3f voxelSizeInOutputImage = outputImage->getVoxelSize();
    const McBox3f& bboxOfOutputImage = outputImage->getBoundingBox();
    const McBox3f& bboxOfFromImage = fromImage->getBoundingBox();
#ifdef _OPENMP
#pragma omp parallel for
#endif
    for (int i = 0; i < dims[0]; i++) {
        HxLocation3* locationInFromImage = fromImage->createLocation();
        std::cout << "\n" << i << " of " << dims[0];
        for (int j = 0; j < dims[1]; j++) {
            McVec2d currentPositionInOutput = McVec2d(
                bboxOfOutputImage[0] + (float)(i)*voxelSizeInOutputImage.x,
                bboxOfOutputImage[2] + (float)(j)*voxelSizeInOutputImage.y);
            McVec2d warpedCoordInFromImage =
                mls.interpolate(currentPositionInOutput);
            McVec3f displacement = McVec3f(
                warpedCoordInFromImage.x - currentPositionInOutput.x,
                warpedCoordInFromImage.y - currentPositionInOutput.y, 0);

            displacement = displacement * -1;
            locationInFromImage->move(McVec3f(warpedCoordInFromImage.x,
                                              warpedCoordInFromImage.y,
                                              bboxOfFromImage[4]));
            float resultValue[1];
            fromImage->eval(*locationInFromImage, resultValue);
            unsigned long pos = latticePos(i, j, 0, dims);
            outputImageData[pos] = resultValue[0];
            outputVectorField->lattice().set(i, j, 0, displacement.getValue());
        }
        delete locationInFromImage;
    }

    outputImage->touch();
    outputImage->fire();
}
void HxCPDSpatialGraphWarp::warpPoint(const McVec3f& source, McVec3f& target,
                                      MovingLeastSquares& mlsInterpolator) {
    McVec2d curPoint = McVec2d(source.x, source.y);
    McVec2d warpedPoint = mlsInterpolator.interpolate(curPoint);
    target = McVec3f(warpedPoint.x, warpedPoint.y, source.z);
}