#include #include #include ISNP_API void compute_patch_edges(const stl_vector_mp& patch_faces, stl_vector_mp>& edges_of_face, stl_vector_mp& patch_edges) { uint32_t max_num_edge = 0; for (const auto& iso_face : patch_faces) { max_num_edge += static_cast(iso_face.vertex_indices.size()); } patch_edges.reserve(max_num_edge / 2); edges_of_face.reserve(patch_faces.size()); uint32_t num_iso_edge{}; // map: (v1, v2) -> iso-edge index flat_hash_map_mp, uint32_t> edge_id{}; for (uint32_t i = 0; i < patch_faces.size(); i++) { auto& face = patch_faces[i]; const uint32_t num_edge = static_cast(face.vertex_indices.size()); // emplace at back of edges_of_face a vector of size num_edge auto& face_edges = edges_of_face.emplace_back(num_edge); for (uint32_t j = 0; j < num_edge; j++) { auto v1 = face.vertex_indices[j]; auto v2 = (j + 1 == num_edge) ? face.vertex_indices[0] : face.vertex_indices[j + 1]; // swap if v1 > v2 if (v1 > v2) std::swap(v1, v2); // num_iso_edge = static_cast(patch_edges.size()); auto iter_inserted = edge_id.try_emplace(std::make_pair(v1, v2), num_iso_edge); if (iter_inserted.second) { // new iso-edge auto& edge = patch_edges.emplace_back(); edge.v1 = v1; edge.v2 = v2; edge.headers.emplace_back(i, j); face_edges[j] = num_iso_edge; } else { // existing iso-edge uint32_t eId = iter_inserted.first->second; patch_edges[eId].headers.emplace_back(i, j); face_edges[j] = eId; } } } } ISNP_API void compute_patches(const stl_vector_mp>& edges_of_face, const stl_vector_mp& patch_edges, const stl_vector_mp& patch_faces, stl_vector_mp>& patches, stl_vector_mp& patch_of_face_mapping) { stl_vector_mp visited_face(edges_of_face.size(), false); for (uint32_t i = 0; i < edges_of_face.size(); i++) { if (!visited_face[i]) { // new patch auto& patch = patches.emplace_back(); const auto patch_id = static_cast(patches.size() - 1); std::queue Q{}; Q.emplace(i); patch.emplace_back(i); visited_face[i] = true; patch_of_face_mapping[i] = patch_id; while (!Q.empty()) { const auto fId = Q.front(); Q.pop(); for (const auto eId : edges_of_face[fId]) { if (patch_edges[eId].headers.size() == 2) { // manifold edge const auto other_fId = (patch_edges[eId].headers[0].face_index == fId) ? patch_edges[eId].headers[1].face_index : patch_edges[eId].headers[0].face_index; if (!visited_face[other_fId]) { Q.emplace(other_fId); patch.emplace_back(other_fId); patch_of_face_mapping[other_fId] = patch_id; visited_face[other_fId] = true; } } } } } } } ISNP_API void compute_chains(const stl_vector_mp& patch_edges, const stl_vector_mp>& non_manifold_edges_of_vert, stl_vector_mp>& chains) { stl_vector_mp visited_edge(patch_edges.size(), false); for (uint32_t i = 0; i < patch_edges.size(); i++) { if (!visited_edge[i] && patch_edges[i].headers.size() > 2) { // unvisited non-manifold iso-edge (not a boundary edge) // new chain auto& chain = chains.emplace_back(); std::queue Q{}; Q.emplace(i); chain.emplace_back(i); visited_edge[i] = true; while (!Q.empty()) { const auto eId = Q.front(); Q.pop(); // v1 auto v = patch_edges[eId].v1; if (non_manifold_edges_of_vert[v].size() == 2) { const auto other_eId = (non_manifold_edges_of_vert[v][0] == eId) ? non_manifold_edges_of_vert[v][1] : non_manifold_edges_of_vert[v][0]; if (!visited_edge[other_eId]) { Q.emplace(other_eId); chain.emplace_back(other_eId); visited_edge[other_eId] = true; } } // v2 v = patch_edges[eId].v2; if (non_manifold_edges_of_vert[v].size() == 2) { const auto other_eId = (non_manifold_edges_of_vert[v][0] == eId) ? non_manifold_edges_of_vert[v][1] : non_manifold_edges_of_vert[v][0]; if (!visited_edge[other_eId]) { Q.emplace(other_eId); chain.emplace_back(other_eId); visited_edge[other_eId] = true; } } } } } } ISNP_API void compute_shells_and_components(const stl_vector_mp>& half_patch_adj_list, stl_vector_mp>& shells, stl_vector_mp& shell_of_half_patch, stl_vector_mp>& components, stl_vector_mp& component_of_patch) { const auto num_patch = half_patch_adj_list.size() / 2; // find connected component of half-patch adjacency graph // each component is a shell stl_vector_mp visited_flags(2 * num_patch, false); shells.clear(); shell_of_half_patch.resize(2 * num_patch); for (uint32_t i = 0; i < 2 * num_patch; i++) { if (!visited_flags[i]) { // create new component const uint32_t shell_Id = static_cast(shells.size()); auto& shell = shells.emplace_back(); std::queue Q{}; Q.emplace(i); shell.emplace_back(i); shell_of_half_patch[i] = shell_Id; visited_flags[i] = true; while (!Q.empty()) { auto half_patch = Q.front(); Q.pop(); for (auto hp : half_patch_adj_list[half_patch]) { if (!visited_flags[hp]) { shell.emplace_back(hp); shell_of_half_patch[hp] = shell_Id; Q.emplace(hp); visited_flags[hp] = true; } } } } } // find connected component of patch-adjacency graph // each component is an iso-surface component visited_flags.clear(); visited_flags.resize(num_patch, false); components.clear(); component_of_patch.resize(num_patch); for (uint32_t i = 0; i < num_patch; i++) { if (!visited_flags[i]) { // create new component const uint32_t component_Id = static_cast(components.size()); auto& component = components.emplace_back(); std::queue Q{}; Q.emplace(i); component.emplace_back(i); component_of_patch[i] = component_Id; visited_flags[i] = true; while (!Q.empty()) { auto patch = Q.front(); Q.pop(); // 1-side of patch for (auto hp : half_patch_adj_list[2 * patch]) { if (!visited_flags[hp / 2]) { const auto p = hp / 2; component.emplace_back(p); component_of_patch[p] = component_Id; Q.emplace(p); visited_flags[p] = true; } } // -1-side of patch for (auto hp : half_patch_adj_list[2 * patch + 1]) { if (!visited_flags[hp / 2]) { const auto p = hp / 2; component.emplace_back(p); component_of_patch[p] = component_Id; Q.emplace(p); visited_flags[p] = true; } } } } } // get shells as list of patch indices // for (auto& shell : shells) { // for (auto& pId : shell) { // pId /= 2; // get patch index of half-patch // } // } } ISNP_API void compute_arrangement_cells(uint32_t num_shell, const stl_vector_mp>& shell_links, stl_vector_mp>& arrangement_cells) { // build shell adjacency list uint32_t sink_shell = num_shell; flat_hash_map_mp> adjacent_shells{}; for (const auto& link : shell_links) { if (link.first == invalid_index) { adjacent_shells[sink_shell].emplace_back(link.second); adjacent_shells[link.second].emplace_back(sink_shell); } else if (link.second == invalid_index) { adjacent_shells[sink_shell].emplace_back(link.first); adjacent_shells[link.first].emplace_back(sink_shell); } else { adjacent_shells[link.first].emplace_back(link.second); adjacent_shells[link.second].emplace_back(link.first); } } // find connected components of shell adjacency graph // each component is an arrangement cells stl_vector_mp visited_shell(num_shell + 1, false); // arrangement_cells.clear(); for (uint32_t i = 0; i < num_shell + 1; ++i) { if (!visited_shell[i]) { // create new component auto& arr_cell = arrangement_cells.emplace_back(); std::queue Q{}; Q.emplace(i); arr_cell.emplace_back(i); visited_shell[i] = true; while (!Q.empty()) { const auto shell_id = Q.front(); Q.pop(); for (const auto s : adjacent_shells[shell_id]) { if (!visited_shell[s]) { arr_cell.emplace_back(s); Q.emplace(s); visited_shell[s] = true; } } } } } // remove sink shell from arrangement cells stl_vector_mp sink_free_shell_list{}; for (auto& arr_cell : arrangement_cells) { sink_free_shell_list.clear(); for (const auto s : arr_cell) { if (s < num_shell) { sink_free_shell_list.emplace_back(s); } } // arr_cell = sink_free_shell_list; std::swap(arr_cell, sink_free_shell_list); } }