You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

508 lines
25 KiB

#include <topology_ray_shooting.hpp>
#include <patch_connectivity.hpp>
ISNP_API void topo_ray_shooting(const tetrahedron_mesh_t &tet_mesh,
const stl_vector_mp<std::optional<arrangement_t>> &cut_results,
const stl_vector_mp<iso_vertex_t> &iso_verts,
const stl_vector_mp<polygon_face_t> &iso_faces,
const stl_vector_mp<stl_vector_mp<uint32_t>> &patches,
const stl_vector_mp<uint32_t> &patch_of_face,
const stl_vector_mp<stl_vector_mp<uint32_t>> &shells,
const stl_vector_mp<uint32_t> &shell_of_half_patch,
const stl_vector_mp<stl_vector_mp<uint32_t>> &components,
const stl_vector_mp<uint32_t> &component_of_patch,
stl_vector_mp<std::pair<uint32_t, uint32_t>> &shell_links)
{
// map: tet vert index --> index of next vert (with smaller (x,y,z))
stl_vector_mp<uint32_t> next_vert{};
build_next_vert(tet_mesh, next_vert);
// find extremal edge for each component
// extremal edge of component i is stored at position [2*i], [2*i+1]
stl_vector_mp<uint32_t> extremal_edge_of_component{};
// store an iso-vert index on edge (v, v_next), Mesh_None means there is no such iso-vert
stl_vector_mp<uint32_t> iso_vert_on_v_v_next{};
// map: (tet_id, tet_face_id) --> iso_face_id
flat_hash_map_mp<face_header_t, uint32_t> iso_face_id_of_tet_face{};
// map: (tet_id, tet_vert_id) --> (iso_vert_id, component_id)
flat_hash_map_mp<simplified_vertex_header_t, component_header_t> iso_vId_compId_of_tet_vert{};
find_extremal_edges(tet_mesh.vertices,
iso_verts,
iso_faces,
patches,
components,
component_of_patch,
next_vert,
extremal_edge_of_component,
iso_vert_on_v_v_next,
iso_face_id_of_tet_face,
iso_vId_compId_of_tet_vert);
// topological ray shooting
{
shell_links.reserve(components.size());
stl_vector_mp<uint32_t> sorted_vert_indices_on_edge{};
sorted_vert_indices_on_edge.reserve(3);
for (uint32_t i = 0; i < components.size(); ++i) {
// extremal edge: v1 -> v2
const auto extreme_v1 = extremal_edge_of_component[2 * i];
const auto extreme_v2 = extremal_edge_of_component[2 * i + 1];
const auto iso_vId = iso_vert_on_v_v_next[extreme_v1];
const auto tetId = iso_verts[iso_vId].header.volume_index;
const auto &tet_cut_result = cut_results[tetId].value();
// get local index of v1 and v2 in the tet
uint32_t local_v1, local_v2;
for (uint32_t j = 0; j < 4; ++j) {
if (tet_mesh.indices[tetId][j] == extreme_v1) {
local_v1 = j;
} else if (tet_mesh.indices[tetId][j] == extreme_v2) {
local_v2 = j;
}
}
// get an ordered list of vertices on edge v1 -> v2
sorted_vert_indices_on_edge.clear();
compute_edge_intersection_order(tet_cut_result, local_v1, local_v2, sorted_vert_indices_on_edge);
// find the vertex v_start on v1->v2
// 1. on current component
// 2. nearest to v2
uint32_t j_start;
for (uint32_t j = 0; j + 1 < sorted_vert_indices_on_edge.size(); ++j) {
const auto &iso_vId_compId =
iso_vId_compId_of_tet_vert[simplified_vertex_header_t{tetId, sorted_vert_indices_on_edge[j]}];
if (iso_vId_compId.component_index == i) { j_start = j; }
}
if (j_start + 2 < sorted_vert_indices_on_edge.size()) {
// there is a vert from another component between v_start -> v2
face_with_orient_t face_orient1, face_orient2;
compute_passing_face_pair(tet_cut_result,
sorted_vert_indices_on_edge[j_start],
sorted_vert_indices_on_edge[j_start + 1],
face_orient1,
face_orient2);
const auto iso_fId1 = iso_face_id_of_tet_face[face_header_t{tetId, face_orient1.face_id}];
const auto iso_fId2 = iso_face_id_of_tet_face[face_header_t{tetId, face_orient2.face_id}];
const auto shell1 = (face_orient1.orient == 1) ? shell_of_half_patch[2 * patch_of_face[iso_fId1]]
: shell_of_half_patch[2 * patch_of_face[iso_fId1] + 1];
const auto shell2 = (face_orient2.orient == 1) ? shell_of_half_patch[2 * patch_of_face[iso_fId2]]
: shell_of_half_patch[2 * patch_of_face[iso_fId2] + 1];
// link shell1 with shell2
shell_links.emplace_back(shell1, shell2);
} else {
// there is no vert between v_start -> v2
face_with_orient_t face_orient;
compute_passing_face(tet_cut_result,
sorted_vert_indices_on_edge[j_start],
sorted_vert_indices_on_edge.back(),
face_orient);
auto iso_fId = iso_face_id_of_tet_face[face_header_t{tetId, face_orient.face_id}];
const auto shell_start = (face_orient.orient == 1) ? shell_of_half_patch[2 * patch_of_face[iso_fId]]
: shell_of_half_patch[2 * patch_of_face[iso_fId] + 1];
// follow the ray till another iso-vertex or the sink
auto v_curr = extreme_v2;
while (next_vert[v_curr] != invalid_index && iso_vert_on_v_v_next[v_curr] == invalid_index) {
v_curr = next_vert[v_curr];
}
if (iso_vert_on_v_v_next[v_curr] != invalid_index) {
// reached iso-vert at end of the ray
const auto iso_vId_end = iso_vert_on_v_v_next[v_curr];
const auto end_tetId = iso_verts[iso_vId_end].header.volume_index;
const auto &end_tet_cut_result = cut_results[end_tetId].value();
auto v_next = next_vert[v_curr];
// find local vertex indices in the end tetrahedron
for (uint32_t j = 0; j < 4; ++j) {
if (tet_mesh.indices[end_tetId][j] == v_curr) {
local_v1 = j;
} else if (tet_mesh.indices[end_tetId][j] == v_next) {
local_v2 = j;
}
}
// get an ordered list of vertices on edge v_curr -> v_next
sorted_vert_indices_on_edge.clear();
compute_edge_intersection_order(end_tet_cut_result, local_v1, local_v2, sorted_vert_indices_on_edge);
// find the end shell
compute_passing_face(end_tet_cut_result,
sorted_vert_indices_on_edge[1],
sorted_vert_indices_on_edge.front(),
face_orient);
iso_fId = iso_face_id_of_tet_face[face_header_t{end_tetId, face_orient.face_id}];
const auto shell_end = (face_orient.orient == 1) ? shell_of_half_patch[2 * patch_of_face[iso_fId]]
: shell_of_half_patch[2 * patch_of_face[iso_fId] + 1];
// link start shell with end shell
shell_links.emplace_back(shell_start, shell_end);
} else {
// next_vert[v_curr] is Mesh_None, v_curr is the sink vertex
// link shell_start with the sink
shell_links.emplace_back(shell_start, invalid_index);
}
}
}
}
}
ISNP_API void build_next_vert(const tetrahedron_mesh_t &tet_mesh, stl_vector_mp<uint32_t> &next_vert)
{
next_vert.resize(tet_mesh.vertices.size(), invalid_index);
for (const auto &tet : tet_mesh.indices) {
// find the smallest vertex of tet
uint32_t min_id = 0;
for (uint32_t i = 1; i < 4; ++i) {
if (point_xyz_less(tet_mesh.vertices[tet[i]], tet_mesh.vertices[tet[min_id]])) { min_id = i; }
}
uint32_t min_vId = tet[min_id];
//
for (uint32_t i = 0; i < 4; ++i) {
if (i != min_id) { next_vert[tet[i]] = min_vId; }
}
}
}
ISNP_API void find_extremal_edges(const stl_vector_mp<raw_point_t> &pts,
const stl_vector_mp<iso_vertex_t> &iso_verts,
const stl_vector_mp<polygon_face_t> &iso_faces,
const stl_vector_mp<stl_vector_mp<uint32_t>> &patches,
const stl_vector_mp<stl_vector_mp<uint32_t>> &components,
const stl_vector_mp<uint32_t> &component_of_patch,
const stl_vector_mp<uint32_t> &next_vert,
stl_vector_mp<uint32_t> &extremal_edge_of_component,
stl_vector_mp<uint32_t> &iso_vert_on_v_v_next,
flat_hash_map_mp<face_header_t, uint32_t> &iso_face_id_of_tet_face,
flat_hash_map_mp<simplified_vertex_header_t, component_header_t> &iso_vId_compId_of_tet_vert)
{
extremal_edge_of_component.resize(2 * components.size(), invalid_index);
iso_vert_on_v_v_next.resize(pts.size(), invalid_index);
iso_face_id_of_tet_face.reserve(iso_faces.size());
iso_vId_compId_of_tet_vert.reserve(iso_faces.size() / 2);
//
stl_vector_mp<bool> is_iso_vert_visited(iso_verts.size(), false);
for (uint32_t i = 0; i < patches.size(); ++i) {
uint32_t component_id = component_of_patch[i];
auto &u1 = extremal_edge_of_component[2 * component_id];
auto &u2 = extremal_edge_of_component[2 * component_id + 1];
for (auto fId : patches[i]) {
for (const auto &tet_face : iso_faces[fId].headers) { iso_face_id_of_tet_face.try_emplace(tet_face, fId); }
for (const auto vId : iso_faces[fId].vertex_indices) {
if (!is_iso_vert_visited[vId]) {
is_iso_vert_visited[vId] = true;
const auto &vert = iso_verts[vId];
if (vert.header.minimal_simplex_flag == 2) { // edge iso-vertex
const auto v1 = vert.simplex_vertex_indices[0];
const auto v2 = vert.simplex_vertex_indices[1];
if (next_vert[v1] == v2) { // on tree edge v1 -> v2
// update extremal edge
if (u1 == invalid_index) {
u1 = v1;
u2 = v2;
} else {
if (v2 == u2) {
if (point_xyz_less(pts[v1], pts[u1])) { u1 = v1; }
} else if (point_xyz_less(pts[v2], pts[u2])) {
u1 = v1;
u2 = v2;
}
}
// record an iso-vert on edge v1 -> v2
iso_vert_on_v_v_next[v1] = vId;
// fill map
iso_vId_compId_of_tet_vert.try_emplace(
simplified_vertex_header_t{vert.header.volume_index, vert.header.local_vertex_index},
component_header_t{vId, component_id});
} else if (next_vert[v2] == v1) { // on tree edge v2 -> v1
// update extremal edge
if (u1 == invalid_index) {
u1 = v2;
u2 = v1;
} else {
if (v1 == u2) {
if (point_xyz_less(pts[v2], pts[u1])) { u1 = v2; }
} else if (point_xyz_less(pts[v1], pts[u2])) {
u1 = v2;
u2 = v1;
}
}
// record an iso-vert on v2 -> v1
iso_vert_on_v_v_next[v2] = vId;
// fill map
iso_vId_compId_of_tet_vert.try_emplace(
simplified_vertex_header_t{vert.header.volume_index, vert.header.local_vertex_index},
component_header_t{vId, component_id});
}
}
}
}
}
}
}
ISNP_API void compute_edge_intersection_order(const arrangement_t &tet_cut_result,
uint32_t v,
uint32_t u,
stl_vector_mp<uint32_t> &vert_indices)
{
const auto &vertices = tet_cut_result.vertices;
const auto &faces = tet_cut_result.faces;
std::array<bool, 4> edge_flag{true, true, true, true};
edge_flag[v] = false;
edge_flag[u] = false;
// find vertices on edge v->u, and index of v and u in tet_cut_result.vertices
uint32_t v_id, u_id;
stl_vector_mp<bool> is_on_edge_vu(vertices.size(), false);
uint32_t num_vert_in_edge_vu = 0;
uint32_t flag_count;
uint32_t other_plane;
for (uint32_t i = 0; i < vertices.size(); ++i) {
flag_count = 0;
other_plane = invalid_index;
const auto &vert = vertices[i];
for (int j = 0; j < 3; ++j) {
if (vert[j] < 4) { // 0,1,2,3 are tet boundary planes
if (edge_flag[vert[j]]) {
++flag_count;
} else {
other_plane = vert[j];
}
}
}
if (flag_count == 2) {
is_on_edge_vu[i] = true;
if (other_plane == u) { // current vertex is v
v_id = i;
} else if (other_plane == v) { // current vertex is u
u_id = i;
} else { // current vertex is in interior of edge v->u
++num_vert_in_edge_vu;
}
}
}
if (num_vert_in_edge_vu == 0) { // no intersection points in edge v->u
vert_indices.emplace_back(v_id);
vert_indices.emplace_back(u_id);
return;
}
// find all faces on triangle v->u->w, w is a third tet vertex
uint32_t pId; // plane index of v->u->w, pick pId != v and pId != u
for (uint32_t i = 0; i < 4; ++i) {
if (edge_flag[i]) {
pId = i;
break;
}
}
// std::vector<bool> is_on_tri_vuw(faces.size(), false);
stl_vector_mp<uint32_t> faces_on_tri_vuw{};
for (uint32_t i = 0; i < faces.size(); ++i) {
const auto &face = faces[i];
if (face.negative_cell == invalid_index // face is on tet boundary
&& face.supporting_plane == pId)
faces_on_tri_vuw.emplace_back(i);
}
// build edge-face connectivity on triangle v->u->w
// map: edge (v1, v2) --> incident faces (f1, f2), f2 is None if (v1,v2) is on triangle boundary
flat_hash_map_mp<std::pair<uint32_t, uint32_t>, std::pair<uint32_t, uint32_t>> faces_of_edge{};
for (const auto fId : faces_on_tri_vuw) {
const auto &face = faces[fId];
const auto num_vert = face.vertices.size();
for (uint32_t i = 0; i < num_vert; ++i) {
const auto i_next = (i + 1) % num_vert;
const auto vi = face.vertices[i];
const auto vi_next = face.vertices[i_next];
// add fId to edge (vi, vi_next)
auto iter_inserted = faces_of_edge.try_emplace(std::make_pair(vi, vi_next), std::make_pair(fId, invalid_index));
if (!iter_inserted.second) { // inserted before
iter_inserted.first->second.second = fId;
}
// add fId to edge (vi_next, vi)
iter_inserted = faces_of_edge.try_emplace(std::make_pair(vi_next, vi), std::make_pair(fId, invalid_index));
if (!iter_inserted.second) { // inserted before
iter_inserted.first->second.second = fId;
}
}
}
// find the face on triangle v->u->w:
// 1. has vertex v
// 2. has an edge on v->u
uint32_t f_start;
for (const auto fId : faces_on_tri_vuw) {
const auto &face = faces[fId];
bool find_v = false;
uint32_t count = 0;
for (auto vi : face.vertices) {
if (vi == v_id) { find_v = true; }
if (is_on_edge_vu[vi]) { ++count; }
}
if (find_v && count == 2) {
f_start = fId;
break;
}
}
// trace edge v->u
vert_indices.reserve(num_vert_in_edge_vu + 2);
vert_indices.emplace_back(v_id);
stl_vector_mp<bool> visited_face(faces.size(), false);
uint32_t v_curr = v_id;
uint32_t f_curr = f_start;
std::pair<uint32_t, uint32_t> edge_prev;
std::pair<uint32_t, uint32_t> edge_next;
std::pair<uint32_t, uint32_t> edge_on_vu;
while (v_curr != u_id) {
// clear edge_on_vu
edge_on_vu.first = invalid_index;
edge_on_vu.second = invalid_index;
//
const auto &face = faces[f_curr];
const auto num_vert = face.vertices.size();
// visit all edges of face, find edge_prev, edge_next and edge_on_vu
for (uint32_t i = 0; i < num_vert; ++i) {
const auto i_next = (i + 1) % num_vert;
const auto vi = face.vertices[i];
const auto vi_next = face.vertices[i_next];
if (is_on_edge_vu[vi] && !is_on_edge_vu[vi_next]) {
auto &two_faces = faces_of_edge[std::make_pair(vi, vi_next)];
auto other_face = (two_faces.first == f_curr) ? two_faces.second : two_faces.first;
if (vi == v_id || (other_face != invalid_index && visited_face[other_face])) {
edge_prev.first = vi;
edge_prev.second = vi_next;
} else {
edge_next.first = vi;
edge_next.second = vi_next;
}
} else if (is_on_edge_vu[vi_next] && !is_on_edge_vu[vi]) {
auto &two_faces = faces_of_edge[std::make_pair(vi, vi_next)];
auto other_face = (two_faces.first == f_curr) ? two_faces.second : two_faces.first;
if (vi_next == v_id || (other_face != invalid_index && visited_face[other_face])) {
edge_prev.first = vi;
edge_prev.second = vi_next;
} else {
edge_next.first = vi;
edge_next.second = vi_next;
}
} else if (is_on_edge_vu[vi] && is_on_edge_vu[vi_next]) {
edge_on_vu.first = vi;
edge_on_vu.second = vi_next;
}
}
//
if (edge_on_vu.first == invalid_index) {
// no edge of the face is on v->u
// keep v_curr, update f_curr
visited_face[f_curr] = true;
auto &two_faces = faces_of_edge[edge_next];
f_curr = (two_faces.first == f_curr) ? two_faces.second : two_faces.first;
} else {
// there is an edge of the face on v->u
// update v_curr to be the next vert on edge v->u
v_curr = (edge_on_vu.first == v_curr) ? edge_on_vu.second : edge_on_vu.first;
vert_indices.emplace_back(v_curr);
// update f_curr
visited_face[f_curr] = true;
auto &two_faces = faces_of_edge[edge_next];
f_curr = (two_faces.first == f_curr) ? two_faces.second : two_faces.first;
}
}
}
ISNP_API void compute_passing_face_pair(const arrangement_t &tet_cut_result,
uint32_t v1,
uint32_t v2,
face_with_orient_t &face_orient1,
face_with_orient_t &face_orient2)
{
// find a face incident to edge v1 -> v2
const auto &faces = tet_cut_result.faces;
uint32_t incident_face_id;
bool found_incident_face = false;
for (uint32_t i = 0; i < faces.size(); ++i) {
const auto &face = faces[i];
uint32_t num_vert = static_cast<uint32_t>(face.vertices.size());
for (uint32_t j = 0; j < num_vert; ++j) {
uint32_t vId1 = face.vertices[j];
uint32_t vId2 = face.vertices[(j + 1) % num_vert];
if ((vId1 == v1 && vId2 == v2) || (vId1 == v2 && vId2 == v1)) {
incident_face_id = i;
found_incident_face = true;
break;
}
}
if (found_incident_face) { break; }
}
// assert: found_incident_face == true
const auto cell_id = faces[incident_face_id].positive_cell;
const auto &cell = tet_cut_result.cells[cell_id];
// find the two faces
// 1. bounding the cell
// 2. passing vertex v1 or v2
for (auto fId : cell.faces) {
const auto &face = faces[fId];
bool found_v1 = false;
bool found_v2 = false;
for (auto vId : face.vertices) {
if (vId == v1) {
found_v1 = true;
} else if (vId == v2) {
found_v2 = true;
}
}
if (found_v1 && !found_v2) {
// the current face passes v1 but not v2
face_orient1.face_id = fId;
face_orient1.orient = (face.positive_cell == cell_id) ? 1 : -1;
} else if (!found_v1 && found_v2) {
// the current face passes v2 but not v1
face_orient2.face_id = fId;
face_orient2.orient = (face.positive_cell == cell_id) ? 1 : -1;
}
}
}
ISNP_API void compute_passing_face(const arrangement_t &tet_cut_result, uint32_t v, uint32_t u, face_with_orient_t &face_orient)
{
// find a face incident to edge v -> u
const auto &faces = tet_cut_result.faces;
uint32_t incident_face_id;
bool found_incident_face = false;
for (uint32_t i = 0; i < faces.size(); ++i) {
const auto &face = faces[i];
uint32_t num_vert = static_cast<uint32_t>(face.vertices.size());
for (uint32_t j = 0; j < num_vert; ++j) {
uint32_t vId1 = face.vertices[j];
uint32_t vId2 = face.vertices[(j + 1) % num_vert];
if ((vId1 == v && vId2 == u) || (vId1 == u && vId2 == v)) {
incident_face_id = i;
found_incident_face = true;
break;
}
}
if (found_incident_face) { break; }
}
// assert: found_incident_face == true
const auto cell_id = faces[incident_face_id].positive_cell;
const auto &cell = tet_cut_result.cells[cell_id];
// find the face
// 1. bounding the cell
// 2. passing vertex v
for (auto fId : cell.faces) {
const auto &face = faces[fId];
bool found_v = false;
bool found_u = false;
for (auto vId : face.vertices) {
if (vId == v) {
found_v = true;
} else if (vId == u) {
found_u = true;
}
}
if (found_v && !found_u) {
// the current face passes v but not u
face_orient.face_id = fId;
face_orient.orient = (face.positive_cell == cell_id) ? 1 : -1;
return;
}
}
}