std::function<Vector3D(const Vector3D&)>
CubicSemiLagrangian3::getVectorSamplerFunc(
    const FaceCenteredGrid3& source) const {
    auto uSourceSampler = CubicArraySampler3<double, double>(
        source.uConstAccessor(),
        source.gridSpacing(),
        source.uOrigin());
    auto vSourceSampler = CubicArraySampler3<double, double>(
        source.vConstAccessor(),
        source.gridSpacing(),
        source.vOrigin());
    auto wSourceSampler = CubicArraySampler3<double, double>(
        source.wConstAccessor(),
        source.gridSpacing(),
        source.wOrigin());
    return
        [uSourceSampler, vSourceSampler, wSourceSampler](const Vector3D& x) {
            return Vector3D(
                uSourceSampler(x), vSourceSampler(x), wSourceSampler(x));
        };
}
void GridForwardEulerDiffusionSolver3::solve(
    const FaceCenteredGrid3& source,
    double diffusionCoefficient,
    double timeIntervalInSeconds,
    FaceCenteredGrid3* dest,
    const ScalarField3& boundarySdf,
    const ScalarField3& fluidSdf) {
    auto uSrc = source.uConstAccessor();
    auto vSrc = source.vConstAccessor();
    auto wSrc = source.wConstAccessor();
    auto u = dest->uAccessor();
    auto v = dest->vAccessor();
    auto w = dest->wAccessor();
    auto uPos = source.uPosition();
    auto vPos = source.vPosition();
    auto wPos = source.wPosition();
    Vector3D h = source.gridSpacing();

    buildMarkers(source.uSize(), uPos, boundarySdf, fluidSdf);

    source.parallelForEachUIndex(
        [&](size_t i, size_t j, size_t k) {
            if (!isInsideSdf(boundarySdf.sample(uPos(i, j, k)))) {
                u(i, j, k)
                    = uSrc(i, j, k)
                    + diffusionCoefficient
                    * timeIntervalInSeconds
                    * laplacian3(uSrc, h, i, j, k);
            }
        });

    buildMarkers(source.vSize(), vPos, boundarySdf, fluidSdf);

    source.parallelForEachVIndex(
        [&](size_t i, size_t j, size_t k) {
            if (!isInsideSdf(boundarySdf.sample(vPos(i, j, k)))) {
                v(i, j, k)
                    = vSrc(i, j, k)
                    + diffusionCoefficient
                    * timeIntervalInSeconds
                    * laplacian3(vSrc, h, i, j, k);
            }
        });

    buildMarkers(source.wSize(), wPos, boundarySdf, fluidSdf);

    source.parallelForEachUIndex(
        [&](size_t i, size_t j, size_t k) {
            if (!isInsideSdf(boundarySdf.sample(wPos(i, j, k)))) {
                w(i, j, k)
                    = wSrc(i, j, k)
                    + diffusionCoefficient
                    * timeIntervalInSeconds
                    * laplacian3(wSrc, h, i, j, k);
            }
        });
}