JET_BEGIN_TEST_F(PhysicsAnimation, SimpleMassSpringAnimation)
{
    Array1<double> x;
    Array1<double> y;

    SimpleMassSpringAnimation anim;
    anim.makeChain(10);
    anim.wind = std::make_shared<ConstantVectorField3>(Vector3D(30.0, 0.0, 0.0));
    anim.constraints.push_back(SimpleMassSpringAnimation::Constraint{0, Vector3D(), Vector3D()});
    anim.exportStates(x, y);

    char filename[256];
    snprintf(filename, sizeof(filename), "data.#line2,0000,x.npy");
    saveData(x.constAccessor(), filename);
    snprintf(filename, sizeof(filename), "data.#line2,0000,y.npy");
    saveData(y.constAccessor(), filename);

    Frame frame(1, 1.0 / 60.0);
    for ( ; frame.index < 360; frame.advance())
    {
        anim.update(frame);
        anim.exportStates(x, y);

        snprintf(filename, sizeof(filename), "data.#line2,%04d,x.npy", frame.index);
        saveData(x.constAccessor(), filename);
        snprintf(filename, sizeof(filename), "data.#line2,%04d,y.npy", frame.index);
        saveData(y.constAccessor(), filename);
    }
}
JET_BEGIN_TEST_F(ZhuBridsonPointsToImplicit3, ConvertTwo) {
    Array1<Vector3D> points;

    std::mt19937 rng{0};
    std::uniform_real_distribution<> dist(0.2, 0.8);
    for (size_t i = 0; i < 2; ++i) {
        points.append({dist(rng), dist(rng), dist(rng)});
    }

    VertexCenteredScalarGrid3 grid(128, 128, 128, 1.0 / 128, 1.0 / 128,
                                   1.0 / 128);

    ZhuBridsonPointsToImplicit3 converter(0.6, 0.25);
    converter.convert(points.constAccessor(), &grid);

    TriangleMesh3 triMesh;
    marchingCubes(grid.constDataAccessor(), grid.gridSpacing(),
                  grid.dataOrigin(), &triMesh, 0, kDirectionAll);

    saveTriangleMeshData(triMesh,
                         "zhu_bridson_points_to_implicit3_convert_two.obj");
}
TEST(SphSystemData3, Particles) {
    SphSystemData3 data;

    data.setTargetSpacing(1.0);
    data.setRelativeKernelRadius(1.0);

    data.addParticle(Vector3D(0, 0, 0));
    data.addParticle(Vector3D(1, 0, 0));

    data.buildNeighborSearcher();
    data.updateDensities();

    // See if we get symmetric density profile
    auto den = data.densities();
    EXPECT_LT(0.0, den[0]);
    EXPECT_EQ(den[0], den[1]);

    Array1<double> values = {1.0, 1.0};
    double midVal = data.interpolate(
        Vector3D(0.5, 0, 0), values.constAccessor());
    EXPECT_LT(0.0, midVal);
    EXPECT_GT(1.0, midVal);
}