// remove tets with corners where phi=0 but are outside the volume
static void
remove_exterior_tets(TetMesh& mesh,
                     const std::vector<float> &vphi,
                     const SDF& sdf)
{
    for (size_t t=0; t<mesh.tSize(); ) {
        int i, j, k, l; assign(mesh.T(t), i, j, k, l);
        assert(vphi[i]<=0 && vphi[j]<=0 && vphi[k]<=0 && vphi[l]<=0);
        if (vphi[i]==0 && vphi[j]==0 && vphi[k]==0 && vphi[l]==0
            && sdf((mesh.V(i)+mesh.V(j)+mesh.V(k)+mesh.V(l))/4)>0) {
            mesh.T(t) = mesh.tets().back();
            mesh.tets().pop_back();
        } else {
            ++t;
        }
    }
}
// assuming vphi[i] and vphi[j] have different signs, find or create a new
// vertex on the edge between them where phi interpolates to zero.
static int
cut_edge(int i, int j,
         TetMesh& mesh,
         std::vector<float> &vphi,
         std::map<Vec2i,int> &cut_map)
{
    assert((vphi[i]<0 && vphi[j]>0) || (vphi[i]>0 && vphi[j]<0));
    Vec2i edge(min(i,j), max(i,j));
    std::map<Vec2i,int>::iterator p = cut_map.find(edge);
    if (p == cut_map.end()) { // this edge hasn't been cut yet?
        int v = (int)mesh.vSize();
        float alpha = vphi[i]/(vphi[i]-vphi[j]);
        mesh.verts().push_back((1-alpha)*mesh.V(i) + alpha*mesh.V(j));
        vphi.push_back(0);
        cut_map[edge] = v;
        return v;
    } else { // already done this edge
        return p->second;
    }
}
// Fix some edge crossings by warping vertices if it's admissible
static void
warp_vertices(const float threshold,
              TetMesh& mesh,
              std::vector<float>& vphi)
{
    assert(threshold>=0 && threshold<=0.5);
    std::vector<float> warp(mesh.vSize(), FLT_MAX);
    std::vector<int> warp_nbr(mesh.vSize(), -1);
    std::vector<Vec3f> d(mesh.vSize(), Vec3f(0,0,0));
    // it's wasteful to iterate through tets just to look at edges; oh well
    for (size_t t=0; t<mesh.tSize(); ++t) {
        for (int u=0; u<3; ++u) {
            int i = mesh.T(t)[u];
            for (int v=u+1; v<4; ++v) {
                int j = mesh.T(t)[v];
                if ((vphi[i]<0 && vphi[j]>0) || (vphi[i]>0 && vphi[j]<0)) {
                    float alpha = vphi[i]/(vphi[i]-vphi[j]);
                    if (alpha < threshold) { // warp i?
                        float d2 = alpha*dist2(mesh.V(i), mesh.V(j));
                        if (d2 < warp[i]) {
                            warp[i] = d2;
                            warp_nbr[i] = j;
                            d[i] = alpha*(mesh.V(j)-mesh.V(i));
                        }
                    } else if (alpha > 1-threshold) { // warp j?
                        float d2 = (1-alpha)*dist2(mesh.V(i), mesh.V(j));
                        if (d2 < warp[j]) {
                            warp[j] = d2;
                            warp_nbr[j] = i;
                            d[j] = (1-alpha)*(mesh.V(i)-mesh.V(j));
                        }
                    }
                }
            }
        }
    }
    // do the warps (also wasteful to loop over all vertices; oh well)
    for (size_t i=0; i<mesh.vSize(); ++i) if(warp_nbr[i]>=0) {
        mesh.V(i) += d[i];
        vphi[i] = 0; 
    }
}