static void check_neighbour(const std::vector<Vec3ui> &tri, const std::vector<Vec3f> &x, Array3f &phi, Array3i &closest_tri, const Vec3f &gx, int i0, int j0, int k0, int i1, int j1, int k1) { if(closest_tri(i1,j1,k1)>=0){ unsigned int p, q, r; assign(tri[closest_tri(i1,j1,k1)], p, q, r); float d=point_triangle_distance(gx, x[p], x[q], x[r]); if(d<phi(i0,j0,k0)){ phi(i0,j0,k0)=d; closest_tri(i0,j0,k0)=closest_tri(i1,j1,k1); } } }
void meshsurf3::buildSurface( std::vector<vec3d> &vertices, std::vector<vec3d> &normals, std::vector<vec3i> &faces, const levelset3 *fluid, const levelset3 *solid, bool enclose, FLOAT64 dpx, uint iteration, bool doFit ) { // Save a reference to the mesher const mesher3 &g = *this->g; tick(); dump("Computing levelset for %d nodes...", g.nodes.size()); values.clear(); values.resize(g.nodes.size()); if( values.empty() ) { email::print("Mesher Uninitialized !\n"); email::send(); exit(0); } // Compute levelset for each node PARALLEL_FOR for( uint n=0; n<g.nodes.size(); n++ ) { FLOAT64 phi = fluid->evalLevelset(g.nodes[n]); if( enclose ) phi = fmax(phi,-dpx-solid->evalLevelset(g.nodes[n])); values[n] = phi; } dump("Done. Took %s.\n",stock("meshsurf_levelset_sample")); // Fill holes fillHoles(values,solid,g.nodes,g.elements,g.node2node,fluid->dx); // Calculate intersections on edges tick(); dump("Computing cut edges for each tet..."); std::vector<vec3d> cutpoints(g.edges.size()); std::vector<int> cutIndices(g.edges.size()); uint index = 0; for( int n=0; n<g.edges.size(); n++ ) { FLOAT64 levelsets[2]; for( uint m=0; m<2; m++ ) levelsets[m] = values[g.edges[n][m]]; if( copysign(1.0,levelsets[0]) * copysign(1.0,levelsets[1]) <= 0 ) { FLOAT64 det = levelsets[0]-levelsets[1]; if( fabs(det) < 1e-16 ) det = copysign(1e-16,det); FLOAT64 a = fmin(0.99,fmax(0.01,levelsets[0]/det)); vec3d p = (1.0-a)*g.nodes[g.edges[n][0]]+a*g.nodes[g.edges[n][1]]; cutpoints[n] = p; cutIndices[n] = index++; } else { cutIndices[n] = -1; } } // Copy them to packed array vertices.clear(); vertices.resize(index); index = 0; for( uint n=0; n<cutIndices.size(); n++ ) { if( cutIndices[n] >= 0 ) vertices[index++] = cutpoints[n]; } dump("Done. Took %s.\n",stock()); // Stitch faces (marching tet) tick(); dump("Stitching edges..."); std::vector<std::vector<uint> > node_faces; node_faces.resize(g.nodes.size()); faces.clear(); for( uint n=0; n<g.elements.size(); n++ ) { std::vector<uint> nv; for( uint m=0; m<g.element_edges[n].size(); m++ ) { uint idx = g.element_edges[n][m]; if( cutIndices[idx] >= 0 ) { nv.push_back(cutIndices[idx]); } } if( nv.size() == 4 ) { // Triangulate the veritces int idx0 = -1; int idx1 = -1; for( uint m=0; m<g.element_edges[n].size(); m++ ) { if( cutIndices[g.element_edges[n][m]] >= 0 ) { idx0 = g.element_edges[n][m]; break; } } // If found one intersection edge if( idx0 >= 0 ) { for( uint m=0; m<g.element_edges[n].size(); m++ ) { uint opID = g.element_edges[n][m]; if( cutIndices[opID] >= 0 && isOpEdge(idx0,opID)) { idx1 = opID; break; } } // If found opposite intersection edge if( idx1 >= 0 ) { vec3i v; v[0] = cutIndices[idx0]; v[1] = cutIndices[idx1]; for( uint m=0; m<g.element_edges[n].size(); m++ ) { uint idx2 = g.element_edges[n][m]; if( cutIndices[idx2] >= 0 && idx2 != idx0 && idx2 != idx1 ) { v[2] = cutIndices[idx2]; faces.push_back(v); for( uint m=0; m<g.elements[n].size(); m++ ) { node_faces[g.elements[n][m]].push_back(faces.size()-1); } } } } else { dump( "Opposite edge was not found !\n"); exit(0); } } else { dump( "Ground edge was not found !\n"); exit(0); } } else if( nv.size() == 3 ) { faces.push_back(vec3i(nv[0],nv[1],nv[2])); for( uint m=0; m<g.elements[n].size(); m++ ) { node_faces[g.elements[n][m]].push_back(faces.size()-1); } } else if( nv.size() == 0 ) { // Empty surface. Do nothing... } else { dump( "\nBad stitch encounrtered: Element - edge count = %d.\n", g.element_edges[n].size() ); dump( "Unknown set found N(%e,%e,%e,%e) = %d.\n", values[g.elements[n][0]], values[g.elements[n][1]], values[g.elements[n][2]], values[g.elements[n][3]], nv.size()); for( uint k=0; k<g.element_edges[n].size(); k++ ) { uint vidx[2] = { g.edges[g.element_edges[n][k]][0], g.edges[g.element_edges[n][k]][1] }; dump( "Edge info[%d] = edge(%d,%d) = (%e,%e) = (%f,%f)\n", k, vidx[0], vidx[1], values[vidx[0]], values[vidx[1]], copysign(1.0,values[vidx[0]]), copysign(1.0,values[vidx[1]]) ); } } } dump("Done. Took %s.\n",stock("meshsurf_stitch_mesh")); // Find closest position tick(); dump("Finding closest surface position at surface tets..."); surfacePos.clear(); surfacePos.resize(g.nodes.size()); for( uint n=0; n<g.nodes.size(); n++ ) { FLOAT64 dist = 1e8; vec3d p = g.nodes[n]; for( uint m=0; m<node_faces[n].size(); m++ ) { vec3d p0 = vertices[faces[node_faces[n][m]][0]]; vec3d p1 = vertices[faces[node_faces[n][m]][1]]; vec3d p2 = vertices[faces[node_faces[n][m]][2]]; vec3d out = g.nodes[n]; vec3d src = out; FLOAT64 d = fmax(1e-8,point_triangle_distance(src,p0,p1,p2,out)); if( d < dist ) { p = out; dist = d; } } if( dist < 1.0 ) { values[n] = (values[n]>=0 ? 1.0 : -1.0) * dist; surfacePos[n].push_back(g.nodes[n]); surfacePos[n].push_back(p); } else { values[n] = (values[n]>=0 ? 1.0 : -1.0) * 1e8; } } dump("Done. Took %s.\n",stock("meshsurf_find_close_pos1")); if( extrapolate_dist ) { // Fast march tick(); std::vector<fastmarch3<FLOAT64>::node3 *> fnodes(g.nodes.size()); for( uint n=0; n<g.nodes.size(); n++ ) fnodes[n] = new fastmarch3<FLOAT64>::node3; for( uint n=0; n<g.nodes.size(); n++ ) { vec3d p = g.nodes[n]; bool fixed = fabs(values[n]) < 1.0; fnodes[n]->p = p; fnodes[n]->fixed = fixed; fnodes[n]->levelset = values[n]; fnodes[n]->value = 0.0; fnodes[n]->p2p.resize(g.node2node[n].size()); for( uint m=0; m<g.node2node[n].size(); m++ ) { fnodes[n]->p2p[m] = fnodes[g.node2node[n][m]]; } } fastmarch3<FLOAT64>::fastMarch(fnodes,extrapolate_dist,-extrapolate_dist,1); // Pick up values for( uint n=0; n<g.nodes.size(); n++ ) { values[n] = fnodes[n]->levelset; delete fnodes[n]; } stock("meshsurf_fastmarch"); } // Compute normal tick(); dump("Computing normals..."); computeNormals(vertices,normals,faces,cutIndices); dump("Done. Took %s.\n",stock("meshsurf_normal")); // Flip facet rotation if necessary flipFacet(vertices,normals,faces,0.1*fluid->dx); #if 1 // Fit surface if( doFit ) { tick(); dump("Fitting %d surface vertices...", vertices.size() ); for( uint k=0; k<1; k++ ) { smoothMesh(vertices,normals,faces,solid,dpx,1); PARALLEL_FOR for( uint n=0; n<vertices.size(); n++ ) { vec3d out = vertices[n]; if( solid->evalLevelset(out) > dpx && fluid->getClosestSurfacePos(out) ) { vertices[n] = out; } } } dump("Done. Took %s.\n",stock("meshsurf_fit")); } #endif // Smooth mesh #if 1 tick(); dump("Smoothing faces..."); smoothMesh(vertices,normals,faces,solid,dpx,iteration); dump("Done. Took %s.\n",stock("meshsurf_smooth")); #endif // Find closest position again tick(); dump("Finding closest surface position at surface tets again..."); surfacePos.clear(); surfacePos.resize(g.nodes.size()); for( uint n=0; n<g.nodes.size(); n++ ) { FLOAT64 dist = 1e8; vec3d p = g.nodes[n]; for( uint m=0; m<node_faces[n].size(); m++ ) { vec3d p0 = vertices[faces[node_faces[n][m]][0]]; vec3d p1 = vertices[faces[node_faces[n][m]][1]]; vec3d p2 = vertices[faces[node_faces[n][m]][2]]; vec3d out = g.nodes[n]; vec3d src = out; FLOAT64 d = fmax(1e-8,point_triangle_distance(src,p0,p1,p2,out)); if( d < dist ) { p = out; dist = d; } } if( dist < 1.0 ) { values[n] = (values[n]>=0 ? 1.0 : -1.0) * dist; } } dump("Done. Took %s.\n",stock("meshsurf_find_close_pos2")); // Flip again flipFacet(vertices,normals,faces,0.1*fluid->dx); }
Array3i make_level_set3(const std::vector<Vec3ui> &tri, const std::vector<Vec3f> &x, const Vec3f &origin, float dx, float dy, float dz, int ni, int nj, int nk, Array3f &phi, const int exact_band) { phi.resize(ni, nj, nk); phi.assign(ni*dx+nj*dy+nk*dz); // upper bound on distance Array3i closest_tri(ni, nj, nk, -1); Array3i intersection_count(ni, nj, nk, 0); // intersection_count(i,j,k) is # of tri intersections in (i-1,i]x{j}x{k} // we begin by initializing distances near the mesh, and figuring out intersection counts Vec3f ijkmin, ijkmax; for(unsigned int t=0; t<tri.size(); ++t){ unsigned int p, q, r; assign(tri[t], p, q, r); // coordinates in grid to high precision double fip=((double)x[p][0]-origin[0])/dx, fjp=((double)x[p][1]-origin[1])/dy, fkp=((double)x[p][2]-origin[2])/dz; double fiq=((double)x[q][0]-origin[0])/dx, fjq=((double)x[q][1]-origin[1])/dy, fkq=((double)x[q][2]-origin[2])/dz; double fir=((double)x[r][0]-origin[0])/dx, fjr=((double)x[r][1]-origin[1])/dy, fkr=((double)x[r][2]-origin[2])/dz; // do distances nearby int i0=clamp(int(min(fip,fiq,fir))-exact_band, 0, ni-1), i1=clamp(int(max(fip,fiq,fir))+exact_band+1, 0, ni-1); int j0=clamp(int(min(fjp,fjq,fjr))-exact_band, 0, nj-1), j1=clamp(int(max(fjp,fjq,fjr))+exact_band+1, 0, nj-1); int k0=clamp(int(min(fkp,fkq,fkr))-exact_band, 0, nk-1), k1=clamp(int(max(fkp,fkq,fkr))+exact_band+1, 0, nk-1); for(int k=k0; k<=k1; ++k) for(int j=j0; j<=j1; ++j) for(int i=i0; i<=i1; ++i){ Vec3f gx(i*dx+origin[0], j*dy+origin[1], k*dz+origin[2]); float d=point_triangle_distance(gx, x[p], x[q], x[r]); if(d<phi(i,j,k)){ phi(i,j,k)=d; closest_tri(i,j,k)=t; } } // and do intersection counts j0=clamp((int)std::ceil(min(fjp,fjq,fjr)), 0, nj-1); j1=clamp((int)std::floor(max(fjp,fjq,fjr)), 0, nj-1); k0=clamp((int)std::ceil(min(fkp,fkq,fkr)), 0, nk-1); k1=clamp((int)std::floor(max(fkp,fkq,fkr)), 0, nk-1); for(int k=k0; k<=k1; ++k) for(int j=j0; j<=j1; ++j){ double a, b, c; if(point_in_triangle_2d(j, k, fjp, fkp, fjq, fkq, fjr, fkr, a, b, c)){ double fi=a*fip+b*fiq+c*fir; // intersection i coordinate int i_interval=int(std::ceil(fi)); // intersection is in (i_interval-1,i_interval] if(i_interval<0) ++intersection_count(0, j, k); // we enlarge the first interval to include everything to the -x direction else if(i_interval<ni) ++intersection_count(i_interval,j,k); // we ignore intersections that are beyond the +x side of the grid } } } // and now we fill in the rest of the distances with fast sweeping for(unsigned int pass=0; pass<2; ++pass){ sweep(tri, x, phi, closest_tri, origin, dx, dy, dz, +1, +1, +1); sweep(tri, x, phi, closest_tri, origin, dx, dy, dz, -1, -1, -1); sweep(tri, x, phi, closest_tri, origin, dx, dy, dz, +1, +1, -1); sweep(tri, x, phi, closest_tri, origin, dx, dy, dz, -1, -1, +1); sweep(tri, x, phi, closest_tri, origin, dx, dy, dz, +1, -1, +1); sweep(tri, x, phi, closest_tri, origin, dx, dy, dz, -1, +1, -1); sweep(tri, x, phi, closest_tri, origin, dx, dy, dz, +1, -1, -1); sweep(tri, x, phi, closest_tri, origin, dx, dy, dz, -1, +1, +1); } // then figure out signs (inside/outside) from intersection counts for(int k=0; k<nk; ++k) for(int j=0; j<nj; ++j){ int total_count=0; for(int i=0; i<ni; ++i){ total_count+=intersection_count(i,j,k); if(total_count%2==1){ // if parity of intersections so far is odd, phi(i,j,k)=-phi(i,j,k); // we are inside the mesh } } } return closest_tri; }