#include #include #include "ia_cut_face.hpp" #include "implicit_arrangement.hpp" #include "robust_assert.hpp" std::array ia_cut_3_face(ia_complex_t& ia_complex, uint32_t cid, uint32_t plane_index, const stl_vector_mp>& subfaces) { auto& edges = ia_complex.edges; auto& faces = ia_complex.faces; auto& cells = ia_complex.cells; const auto& cell = cells[cid]; const auto num_boundary_faces = cell.faces.size(); uint32_t cut_face_id = INVALID_INDEX; stl_vector_mp positive_subfaces{}; stl_vector_mp negative_subfaces{}; stl_vector_mp cut_edges{}; stl_vector_mp cut_edge_orientations{}; positive_subfaces.reserve(num_boundary_faces + 1); negative_subfaces.reserve(num_boundary_faces + 1); cut_edges.reserve(num_boundary_faces); cut_edge_orientations.reserve(num_boundary_faces); auto compute_cut_edge_orientation = [&](uint32_t fid, const std::array& subface) -> bool { ROBUST_ASSERT(subface[2] != INVALID_INDEX); const auto& f = faces[fid]; bool s = cell.signs[f.supporting_plane]; if (subface[0] == INVALID_INDEX || subface[1] == INVALID_INDEX) { // Intersection edge is on the boundary of the face. auto itr = algorithm::find(f.edges.begin(), f.edges.end(), subface[2]); ROBUST_ASSERT(itr != f.edges.end()); size_t curr_i = itr - f.edges.begin(); size_t next_i = (curr_i + 1) % f.edges.size(); const auto& curr_e = edges[f.edges[curr_i]]; const auto& next_e = edges[f.edges[next_i]]; bool edge_is_consistent_with_face = (curr_e.vertices[1] == next_e.vertices[0] || curr_e.vertices[1] == next_e.vertices[1]); bool on_positive_side = subface[0] != INVALID_INDEX; uint8_t key = static_cast(s) + static_cast(edge_is_consistent_with_face) + static_cast(on_positive_side); return key % 2 == 0; } else { // Intersection edge is a cross cut. return !s; } }; for (auto fid : cell.faces) { const auto& subface = subfaces[fid]; if (subface[0] == INVALID_INDEX && subface[1] == INVALID_INDEX) { cut_face_id = fid; } if (subface[0] != INVALID_INDEX) { positive_subfaces.emplace_back(subface[0]); } if (subface[1] != INVALID_INDEX) { negative_subfaces.emplace_back(subface[1]); } if (subface[2] != INVALID_INDEX) { cut_edges.emplace_back(subface[2]); cut_edge_orientations.emplace_back(compute_cut_edge_orientation(fid, subface)); } } if (positive_subfaces.empty() && negative_subfaces.empty()) { // The implicit function is identical over the whole cell. return {INVALID_INDEX, INVALID_INDEX, INVALID_INDEX}; } else if (positive_subfaces.empty()) { cells[cid].signs[plane_index] = false; return {INVALID_INDEX, cid, cut_face_id}; } else if (negative_subfaces.empty()) { cells[cid].signs[plane_index] = true; return {cid, INVALID_INDEX, cut_face_id}; } // Chain cut edges into a loop. { size_t num_cut_edges = cut_edges.size(); ROBUST_ASSERT(num_cut_edges >= 3); flat_hash_map_mp v2e{}; v2e.reserve(num_cut_edges); for (size_t i = 0; i < num_cut_edges; i++) { const auto eid = cut_edges[i]; const auto& e = edges[eid]; if (cut_edge_orientations[i]) { v2e[e.vertices[0]] = i; } else { v2e[e.vertices[1]] = i; } } stl_vector_mp chained_cut_edges{}; chained_cut_edges.reserve(num_cut_edges); chained_cut_edges.emplace_back(0u); while (chained_cut_edges.size() < num_cut_edges) { const uint32_t i = chained_cut_edges.back(); const auto& e = edges[cut_edges[i]]; const auto vid = cut_edge_orientations[i] ? e.vertices[1] : e.vertices[0]; const auto itr = v2e.find(vid); ROBUST_ASSERT(itr != v2e.end()); const auto next_i = itr->second; if (cut_edges[next_i] == cut_edges[chained_cut_edges.front()]) { break; } chained_cut_edges.emplace_back(next_i); } algorithm::transform(chained_cut_edges.begin(), chained_cut_edges.end(), chained_cut_edges.begin(), [&](auto i) { return cut_edges[i]; }); std::swap(cut_edges, chained_cut_edges); } // Cross cut. ROBUST_ASSERT(!cut_edges.empty()); ia_face_t cut_face; cut_face.edges = std::move(cut_edges); cut_face.supporting_plane = plane_index; faces.emplace_back(std::move(cut_face)); cut_face_id = faces.size() - 1; // Generate positive and negative subcell. ia_cell_t positive_cell, negative_cell; positive_cell.faces.reserve(positive_subfaces.size() + 1); negative_cell.faces.reserve(negative_subfaces.size() + 1); positive_subfaces.emplace_back(cut_face_id); positive_cell.faces = std::move(positive_subfaces); positive_cell.signs = cell.signs; positive_cell.signs[plane_index] = true; negative_subfaces.emplace_back(cut_face_id); negative_cell.faces = std::move(negative_subfaces); negative_cell.signs = cell.signs; negative_cell.signs[plane_index] = false; cells.emplace_back(std::move(positive_cell)); cells.emplace_back(std::move(negative_cell)); uint32_t positive_cell_id = static_cast(cells.size() - 2); uint32_t negative_cell_id = static_cast(cells.size() - 1); // Update cell id on each side of involved faces. { // cut face ROBUST_ASSERT(cut_face_id != INVALID_INDEX); auto& cut_f = faces[cut_face_id]; cut_f.positive_cell = positive_cell_id; cut_f.negative_cell = negative_cell_id; auto& positive_c = cells[positive_cell_id]; auto& negative_c = cells[negative_cell_id]; for (auto fid : positive_c.faces) { if (fid == cut_face_id) continue; auto& f = faces[fid]; ROBUST_ASSERT(f.positive_cell == cid || f.negative_cell == cid); if (f.positive_cell == cid) { f.positive_cell = positive_cell_id; } else { f.negative_cell = positive_cell_id; } } for (auto fid : negative_c.faces) { if (fid == cut_face_id) continue; auto& f = faces[fid]; ROBUST_ASSERT(f.positive_cell == cid || f.negative_cell == cid); if (f.positive_cell == cid) { f.positive_cell = negative_cell_id; } else { f.negative_cell = negative_cell_id; } } } return {positive_cell_id, negative_cell_id, cut_face_id}; }