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.
 
 
 
 
 
 

459 lines
24 KiB

#include "polycube_based_utility.h"
#include <igl/barycentric_coordinates.h>
#include <igl/barycentric_interpolation.h>
#include <igl/in_element.h>
#include <igl/point_mesh_squared_distance.h>
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
#include <set>
#include <vector>
#include <boost/range/adaptors.hpp>
#include "sha-io-foundation/mesh_io.h"
#include "sha-surface-mesh/matmesh.h"
#include "sha-surface-mesh/mesh3.h"
#include "sha-volume-mesh/matmesh.h"
#include "sha-volume-mesh/mesh.h"
namespace da {
namespace sha {
namespace hex {
void LaplacianSmoothForSurfacePatchBoundaryEdges(
const std::vector<VertexIndexEdge> &triangular_boundary_edges_soup,
const std::vector<VertexIndexEdge> &quadrilateral_boundary_edges_soup,
const Eigen::MatrixXd &mat_hexahedral_polycube_coordinates,
const Eigen::MatrixXd &mat_tetrahedral_polycube_coordinates,
const Eigen::MatrixXd &mat_tetrahedral_coordinates, Eigen::MatrixXd &mat_hexahedral_coordinates,
size_t num_iterations) {
log::info("Hex Smoothing Patch Boundary for #{}", num_iterations);
auto triangular_boundary_chains = ConvertEdgesSoupToChains(triangular_boundary_edges_soup);
auto quadrilateral_boundary_chains = ConvertEdgesSoupToChains(quadrilateral_boundary_edges_soup);
if (triangular_boundary_chains.size() != quadrilateral_boundary_chains.size()) {
Terminate(
fmt::format("Size of triangular boundary chains #{} is not equal to quadrilateral #{}",
triangular_boundary_chains.size(), quadrilateral_boundary_chains.size()));
}
constexpr double kEpsilon = 1e-6;
const size_t num_chains = triangular_boundary_chains.size();
for (index_t chain_idx = 0; chain_idx < num_chains; ++chain_idx) {
const auto &triangular_chain = triangular_boundary_chains.at(chain_idx);
bool reverse_flag = false;
auto quadrilateral_chain_it = std::find_if(
quadrilateral_boundary_chains.begin() + chain_idx, quadrilateral_boundary_chains.end(),
[&](const Chain &chain) -> bool {
if ((mat_tetrahedral_polycube_coordinates.row(triangular_chain.front().first) -
mat_hexahedral_polycube_coordinates.row(chain.front().first))
.norm() < kEpsilon &&
(mat_tetrahedral_polycube_coordinates.row(triangular_chain.back().second) -
mat_hexahedral_polycube_coordinates.row(chain.back().second))
.norm() < kEpsilon) {
return true;
}
if ((mat_tetrahedral_polycube_coordinates.row(triangular_chain.front().first) -
mat_hexahedral_polycube_coordinates.row(chain.back().second))
.norm() < kEpsilon &&
(mat_tetrahedral_polycube_coordinates.row(triangular_chain.back().second) -
mat_hexahedral_polycube_coordinates.row(chain.front().first))
.norm() < kEpsilon) {
reverse_flag = true;
return true;
}
return false;
});
if (quadrilateral_chain_it == quadrilateral_boundary_chains.end()) {
Terminate("Can not match quadrilateral chain and triangular chain");
}
if (reverse_flag) {
boost::reverse(*quadrilateral_chain_it);
for (auto &edge : *quadrilateral_chain_it) {
std::swap(edge.first, edge.second);
}
}
std::swap(*quadrilateral_chain_it, *(quadrilateral_boundary_chains.begin() + chain_idx));
}
for (index_t iteration_idx = 0; iteration_idx < num_iterations; ++iteration_idx) {
for (index_t chain_idx = 0; chain_idx < num_chains; ++chain_idx) {
const auto &triangular_chain = triangular_boundary_chains.at(chain_idx);
const auto &quadrilateral_chain = quadrilateral_boundary_chains.at(chain_idx);
if (quadrilateral_chain.size() == 1) continue;
Eigen::MatrixXi mat_triangular_chain(triangular_chain.size(), 2);
for (auto &&[edge_idx, edge] : triangular_chain | boost::adaptors::indexed(0)) {
mat_triangular_chain(edge_idx, 0) = edge.first;
mat_triangular_chain(edge_idx, 1) = edge.second;
}
Eigen::MatrixXd mat_quadrilateral_chain_points(quadrilateral_chain.size() - 1, 3);
for (index_t edge_idx = 0; edge_idx < quadrilateral_chain.size() - 1; ++edge_idx) {
Eigen::Vector3d coordinate =
(mat_hexahedral_coordinates.row(quadrilateral_chain[edge_idx].first) +
mat_hexahedral_coordinates.row(quadrilateral_chain[edge_idx].second) +
mat_hexahedral_coordinates.row(quadrilateral_chain[edge_idx + 1].second)) /
3.0;
mat_quadrilateral_chain_points.row(edge_idx) = coordinate;
}
Eigen::VectorXd chain_point_min_distances;
Eigen::VectorXi chain_point_closet_edges;
Eigen::MatrixXd chain_point_closet_point;
igl::point_mesh_squared_distance(mat_quadrilateral_chain_points, mat_tetrahedral_coordinates,
mat_triangular_chain, chain_point_min_distances,
chain_point_closet_edges, chain_point_closet_point);
for (index_t edge_idx = 0; edge_idx < quadrilateral_chain.size() - 1; ++edge_idx) {
mat_hexahedral_coordinates.row(quadrilateral_chain[edge_idx].second) =
chain_point_closet_point.row(edge_idx);
}
mat_hexahedral_coordinates.row(quadrilateral_chain.front().first) =
mat_tetrahedral_coordinates.row(triangular_chain.front().first);
mat_hexahedral_coordinates.row(quadrilateral_chain.back().second) =
mat_tetrahedral_coordinates.row(triangular_chain.back().second);
}
}
}
void LaplacianSmoothForSurfacePatches(
const SurfaceTopoMesh3 &quadrilateral_topomesh, const Eigen::MatrixXi &mat_surface_triangles,
const std::vector<OrientedPatch> &triangular_oriented_patches,
const std::vector<OrientedPatch> &quadrilateral_oriented_patches,
const std::vector<std::pair<index_t, index_t>> &matching_patch_pairs,
const Eigen::MatrixXd &mat_tetrahedral_coordinates, Eigen::MatrixXd &mat_hexahedral_coordinates,
size_t num_iterations) {
log::info("Hex Smoothing Surface for #{}", num_iterations);
size_t num_patches = matching_patch_pairs.size();
std::vector<Eigen::MatrixXi> triangular_patch_triangles(num_patches);
std::vector<SurfaceTopoMesh3> quadrilateral_patch_topomesh(num_patches);
std::vector<std::set<SurfaceTopoMesh3::Vertex_index>> quadrilateral_patch_vertices(num_patches);
for (index_t pair_idx = 0; pair_idx < num_patches; ++pair_idx) {
index_t quadrilateral_patch_idx = matching_patch_pairs.at(pair_idx).second;
auto &topomesh = quadrilateral_patch_topomesh.at(quadrilateral_patch_idx);
auto &quadrilateral_patch = quadrilateral_oriented_patches.at(quadrilateral_patch_idx);
auto &quadrilateral_vertices = quadrilateral_patch_vertices.at(quadrilateral_patch_idx);
std::set<SurfaceTopoMesh3::Vertex_index> vertices;
for (auto vertex : quadrilateral_topomesh.vertices()) {
topomesh.add_vertex();
}
for (auto face_idx : quadrilateral_patch.face_indices) {
auto vertices_range =
quadrilateral_topomesh.vertices_around_face(quadrilateral_topomesh.halfedge(face_idx));
topomesh.add_face(vertices_range);
for (auto vtx : vertices_range) {
vertices.insert(vtx);
}
}
for (auto vertex : vertices) {
if (!topomesh.is_border(vertex)) {
quadrilateral_vertices.insert(vertex);
}
}
}
for (index_t pair_idx = 0; pair_idx < num_patches; ++pair_idx) {
index_t triangular_patch_idx = matching_patch_pairs.at(pair_idx).first;
auto &mat_patch_triangles = triangular_patch_triangles.at(triangular_patch_idx);
auto &triangular_patch = triangular_oriented_patches.at(triangular_patch_idx);
mat_patch_triangles.resize(triangular_patch.face_indices.size(), 3);
for (auto &&[idx, face_idx] : triangular_patch.face_indices | boost::adaptors::indexed(0)) {
mat_patch_triangles.row(idx) = mat_surface_triangles.row(face_idx.idx());
}
}
for (index_t iteration_idx = 0; iteration_idx < num_iterations; ++iteration_idx) {
for (auto [triangular_patch_idx, quadrilateral_patch_idx] : matching_patch_pairs) {
auto &topomesh = quadrilateral_patch_topomesh.at(quadrilateral_patch_idx);
auto &triangular_patch = triangular_oriented_patches.at(triangular_patch_idx);
auto &quadrilateral_patch = quadrilateral_oriented_patches.at(quadrilateral_patch_idx);
auto &mat_patch_triangles = triangular_patch_triangles.at(triangular_patch_idx);
auto &quadrilateral_vertices = quadrilateral_patch_vertices.at(quadrilateral_patch_idx);
Eigen::MatrixXd mat_coordinates(quadrilateral_vertices.size(), 3);
for (auto &&[idx, vertex] : quadrilateral_vertices | boost::adaptors::indexed(0)) {
Eigen::Vector3d coordinate = mat_hexahedral_coordinates.row(vertex.idx());
size_t num_one_ring_vertices = 0;
for (auto neighbor : topomesh.vertices_around_target(topomesh.halfedge(vertex))) {
coordinate += mat_hexahedral_coordinates.row(neighbor.idx());
num_one_ring_vertices++;
}
coordinate /= (1 + num_one_ring_vertices);
mat_coordinates.row(idx) = coordinate;
}
Eigen::VectorXd patch_point_min_distances;
Eigen::VectorXi patch_point_closet_triangles;
Eigen::MatrixXd patch_point_closet_points;
igl::point_mesh_squared_distance(mat_coordinates, mat_tetrahedral_coordinates,
mat_patch_triangles, patch_point_min_distances,
patch_point_closet_triangles, patch_point_closet_points);
for (auto &&[idx, vertex] : quadrilateral_vertices | boost::adaptors::indexed(0)) {
mat_hexahedral_coordinates.row(vertex.idx()) = patch_point_closet_points.row(idx);
}
} // pair of patches
} // for iteration
}
void LaplacianSmoothForVolume(const HexahedralTopoMesh &hexahedral_topomesh,
Eigen::MatrixXd &mat_coordinates, size_t num_iterations) {
log::info("Hex Smoothing Volume for #{}", num_iterations);
for (index_t iteration_idx = 0; iteration_idx < num_iterations; ++iteration_idx) {
for (auto vertex : hexahedral_topomesh.vertices()) {
if (hexahedral_topomesh.is_boundary(vertex)) continue;
Eigen::Vector3d new_coordinate = mat_coordinates.row(vertex.idx());
size_t num_neighbor = 0;
for (auto neighbor_vtx : hexahedral_topomesh.vertex_vertices(vertex)) {
new_coordinate += mat_coordinates.row(neighbor_vtx.idx());
num_neighbor++;
}
new_coordinate /= (num_neighbor + 1);
mat_coordinates.row(vertex.idx()) = new_coordinate;
}
}
}
} // namespace hex
auto DeformHexadralPolycubeMeshToOriginalDomain(
const TetrahedralMatMesh &tetrahedral_matmesh,
const Eigen::MatrixXd &mat_tetrahedral_polycube_coordinates,
const HexahedralMatMesh &hexahedral_polycube_matmesh) -> Eigen::MatrixXd {
const size_t num_hexahedral_vertices = hexahedral_polycube_matmesh.NumVertices();
const size_t num_tetrahedral_vertices = tetrahedral_matmesh.NumVertices();
Eigen::MatrixXd mat_hexahedral_coordinates = hexahedral_polycube_matmesh.mat_coordinates;
TetrahedralTopoMesh tetrahedral_topomesh = sha::CreateTetrahedralTopoMeshFromMatrix(
num_tetrahedral_vertices, tetrahedral_matmesh.mat_tetrahedrons);
SurfaceTopoMesh3 triangle_topomesh =
sha::CreateSurfaceTopoMesh3FromTetrahedralTopoMesh(tetrahedral_topomesh);
Eigen::MatrixXi mat_surface_triangles =
sha::CreateFaceMatrixFromSurfaceTopoMesh3(triangle_topomesh);
HexahedralTopoMesh hexahedral_topomesh = sha::CreateHexahedralTopoMeshFromMatrix(
num_hexahedral_vertices, hexahedral_polycube_matmesh.mat_hexahedrons);
SurfaceTopoMesh3 quadrilateral_topomesh =
sha::CreateSurfaceTopoMesh3FromHexahedralTopoMesh(hexahedral_topomesh);
Eigen::VectorXd quadrilateral_vtx_min_distances_to_triangles;
Eigen::VectorXi quadrilateral_vtx_cloest_triangle_indices;
Eigen::MatrixXd quadrilateral_vtx_cloest_points_to_triangles;
Eigen::MatrixXd quadrilateral_vtx_barycentric_coord_to_triangles;
igl::point_mesh_squared_distance(
hexahedral_polycube_matmesh.mat_coordinates, mat_tetrahedral_polycube_coordinates,
mat_surface_triangles, quadrilateral_vtx_min_distances_to_triangles,
quadrilateral_vtx_cloest_triangle_indices, quadrilateral_vtx_cloest_points_to_triangles);
igl::barycentric_coordinates(
hexahedral_polycube_matmesh.mat_coordinates,
mat_tetrahedral_polycube_coordinates(
mat_surface_triangles(quadrilateral_vtx_cloest_triangle_indices, 0), Eigen::all),
mat_tetrahedral_polycube_coordinates(
mat_surface_triangles(quadrilateral_vtx_cloest_triangle_indices, 1), Eigen::all),
mat_tetrahedral_polycube_coordinates(
mat_surface_triangles(quadrilateral_vtx_cloest_triangle_indices, 2), Eigen::all),
quadrilateral_vtx_barycentric_coord_to_triangles);
auto ComputeBarycentricCoordinateInTetrahedron =
[&](const Eigen::Vector3d &vertex_a, const Eigen::Vector3d &vertex_b,
const Eigen::Vector3d &vertex_c, const Eigen::Vector3d &vertex_d,
const Eigen::Vector3d &point) -> Eigen::Vector4d {
auto ComputeTripleProduct = [](const Eigen::Vector3d &a, const Eigen::Vector3d &b,
const Eigen::Vector3d &c) { return b.cross(c).dot(a); };
Eigen::Vector3d vec_ap = point - vertex_a;
Eigen::Vector3d vec_bp = point - vertex_b;
Eigen::Vector3d vec_ab = vertex_b - vertex_a;
Eigen::Vector3d vec_ac = vertex_c - vertex_a;
Eigen::Vector3d vec_ad = vertex_d - vertex_a;
Eigen::Vector3d vec_bc = vertex_c - vertex_b;
Eigen::Vector3d vec_bd = vertex_d - vertex_b;
double va6 = ComputeTripleProduct(vec_bp, vec_bd, vec_bc);
double vb6 = ComputeTripleProduct(vec_ap, vec_ac, vec_ad);
double vc6 = ComputeTripleProduct(vec_ap, vec_ad, vec_ab);
double vd6 = ComputeTripleProduct(vec_ap, vec_ab, vec_ac);
double v6 = 1 / ComputeTripleProduct(vec_ab, vec_ac, vec_ad);
return Eigen::Vector4d(va6 * v6, vb6 * v6, vc6 * v6, vd6 * v6);
};
Eigen::VectorXi hexahedral_vtx_cloest_tetrahedron_indices;
igl::AABB<Eigen::MatrixXd, 3> tetrahedral_polycube_tree;
tetrahedral_polycube_tree.init(mat_tetrahedral_polycube_coordinates,
tetrahedral_matmesh.mat_tetrahedrons);
igl::in_element(mat_tetrahedral_polycube_coordinates, tetrahedral_matmesh.mat_tetrahedrons,
hexahedral_polycube_matmesh.mat_coordinates, tetrahedral_polycube_tree,
hexahedral_vtx_cloest_tetrahedron_indices);
for (auto vertex_idx = 0; vertex_idx < num_hexahedral_vertices; ++vertex_idx) {
auto &mat_original_coords = tetrahedral_matmesh.mat_coordinates;
if (hexahedral_topomesh.is_boundary(OpenVolumeMesh::VertexHandle(vertex_idx))) {
index_t triangle_idx = quadrilateral_vtx_cloest_triangle_indices(vertex_idx);
Eigen::Vector3i triangle_indices = mat_surface_triangles.row(triangle_idx);
Eigen::Vector3d barycentric_coord =
quadrilateral_vtx_barycentric_coord_to_triangles.row(vertex_idx);
Eigen::Vector3d restore_coord =
mat_original_coords.row(triangle_indices(0)) * barycentric_coord.x() +
mat_original_coords.row(triangle_indices(1)) * barycentric_coord.y() +
mat_original_coords.row(triangle_indices(2)) * barycentric_coord.z();
mat_hexahedral_coordinates.row(vertex_idx) = restore_coord;
} else {
index_t tetrahedron_idx = hexahedral_vtx_cloest_tetrahedron_indices(vertex_idx);
if (tetrahedron_idx == index_t(-1)) {
Terminate(
fmt::format("Internal vertex #{} of hexahedral polycube can not match any tetrahedron.",
vertex_idx));
}
Eigen::RowVector4i tetrahedron_vertices =
tetrahedral_matmesh.mat_tetrahedrons.row(tetrahedron_idx);
Eigen::Vector4d barycentric_coord = ComputeBarycentricCoordinateInTetrahedron(
mat_tetrahedral_polycube_coordinates.row(tetrahedron_vertices(0)),
mat_tetrahedral_polycube_coordinates.row(tetrahedron_vertices(1)),
mat_tetrahedral_polycube_coordinates.row(tetrahedron_vertices(2)),
mat_tetrahedral_polycube_coordinates.row(tetrahedron_vertices(3)),
hexahedral_polycube_matmesh.mat_coordinates.row(vertex_idx));
mat_hexahedral_coordinates.row(vertex_idx) =
mat_original_coords.row(tetrahedron_vertices(0)) * barycentric_coord(0) +
mat_original_coords.row(tetrahedron_vertices(1)) * barycentric_coord(1) +
mat_original_coords.row(tetrahedron_vertices(2)) * barycentric_coord(2) +
mat_original_coords.row(tetrahedron_vertices(3)) * barycentric_coord(3);
}
}
std::vector<Orientation> triangular_orientations =
MarkMeshFaceOrientations(mat_tetrahedral_polycube_coordinates, mat_surface_triangles);
std::vector<OrientedPatch> triangular_oriented_patches;
std::vector<index_t> map_triangle_to_patch;
std::vector<VertexIndexEdge> triangular_boundary_edges_soup;
std::vector<double> triangular_patch_values;
DivideMeshIntoPatchesByOrientation(triangle_topomesh, mat_tetrahedral_polycube_coordinates,
triangular_orientations, triangular_oriented_patches,
map_triangle_to_patch, triangular_boundary_edges_soup);
log::info("Divided tet polycube into {} patches.", triangular_oriented_patches.size());
Eigen::MatrixXi mat_quadrangles =
sha::CreateFaceMatrixFromSurfaceTopoMesh3(quadrilateral_topomesh, 4);
std::vector<Orientation> quadrilateral_orientations = MarkMeshFaceOrientations(
hexahedral_polycube_matmesh.mat_coordinates, mat_quadrangles.leftCols(3));
std::vector<OrientedPatch> quadrilateral_oriented_patches;
std::vector<index_t> map_quadrangle_to_patch;
std::vector<VertexIndexEdge> quadrilateral_boundary_edges_soup;
std::vector<double> quadrilateral_patch_values;
DivideMeshIntoPatchesByOrientation(quadrilateral_topomesh, mat_hexahedral_coordinates,
quadrilateral_orientations, quadrilateral_oriented_patches,
map_quadrangle_to_patch, quadrilateral_boundary_edges_soup);
log::info("Divided hex polycube into {} patches.", quadrilateral_oriented_patches.size());
for (auto &oriented_patch : triangular_oriented_patches) {
index_t component_idx = GetNondirectionalIndexByOrientation(oriented_patch.orientation);
double component_position = 0;
for (auto face : oriented_patch.face_indices) {
auto [vertices_it, vertices_end] =
triangle_topomesh.vertices_around_face(triangle_topomesh.halfedge(face));
component_position +=
(mat_tetrahedral_polycube_coordinates(vertices_it->idx(), component_idx) +
mat_tetrahedral_polycube_coordinates((++vertices_it)->idx(), component_idx) +
mat_tetrahedral_polycube_coordinates((++vertices_it)->idx(), component_idx)) /
3;
}
component_position /= oriented_patch.face_indices.size();
triangular_patch_values.push_back(component_position);
}
for (auto &oriented_patch : quadrilateral_oriented_patches) {
index_t component_idx = GetNondirectionalIndexByOrientation(oriented_patch.orientation);
double component_position = 0;
for (auto face : oriented_patch.face_indices) {
auto [vertices_it, vertices_end] =
quadrilateral_topomesh.vertices_around_face(quadrilateral_topomesh.halfedge(face));
component_position +=
(hexahedral_polycube_matmesh.mat_coordinates(vertices_it->idx(), component_idx) +
hexahedral_polycube_matmesh.mat_coordinates((++vertices_it)->idx(), component_idx) +
hexahedral_polycube_matmesh.mat_coordinates((++vertices_it)->idx(), component_idx) +
hexahedral_polycube_matmesh.mat_coordinates((++vertices_it)->idx(), component_idx)) /
4;
}
component_position /= oriented_patch.face_indices.size();
quadrilateral_patch_values.push_back(component_position);
}
size_t num_patches = quadrilateral_oriented_patches.size();
if (num_patches != triangular_oriented_patches.size()) {
Terminate("Number of triangular patches is not same as one of quadrilateral patches");
}
// match triangular_oriented_patches and quadrilateral_oriented_patches
std::vector<index_t> triangular_indices, quadrilateral_indices;
std::vector<std::pair<index_t, index_t>> matching_patch_pairs;
boost::copy(boost::counting_range<index_t>(0, num_patches),
std::back_inserter(triangular_indices));
boost::copy(boost::counting_range<index_t>(0, num_patches),
std::back_inserter(quadrilateral_indices));
boost::sort(triangular_indices, [&](index_t patch_0_idx, index_t patch_1_idx) {
const auto &patch_0 = triangular_oriented_patches.at(patch_0_idx);
const auto &patch_1 = triangular_oriented_patches.at(patch_1_idx);
const index_t orientation_0_idx = GetNondirectionalIndexByOrientation(patch_0.orientation);
const index_t orientation_1_idx = GetNondirectionalIndexByOrientation(patch_1.orientation);
if (orientation_0_idx != orientation_1_idx) {
return orientation_0_idx < orientation_1_idx;
} else {
return triangular_patch_values.at(patch_0_idx) < triangular_patch_values.at(patch_1_idx);
}
});
boost::sort(quadrilateral_indices, [&](index_t patch_0_idx, index_t patch_1_idx) {
const auto &patch_0 = quadrilateral_oriented_patches.at(patch_0_idx);
const auto &patch_1 = quadrilateral_oriented_patches.at(patch_1_idx);
const index_t orientation_0_idx = GetNondirectionalIndexByOrientation(patch_0.orientation);
const index_t orientation_1_idx = GetNondirectionalIndexByOrientation(patch_1.orientation);
if (orientation_0_idx != orientation_1_idx) {
return orientation_0_idx < orientation_1_idx;
} else {
return quadrilateral_patch_values.at(patch_0_idx) <
quadrilateral_patch_values.at(patch_1_idx);
}
});
for (index_t patch_idx = 0; patch_idx < num_patches; ++patch_idx) {
constexpr double kEpsilon = 1e-6;
const index_t triangular_patch_idx = triangular_indices.at(patch_idx);
const index_t quadrilateral_patch_idx = quadrilateral_indices.at(patch_idx);
const auto &triangular_patch = triangular_oriented_patches.at(triangular_patch_idx);
const auto &quadrilateral_patch = quadrilateral_oriented_patches.at(quadrilateral_patch_idx);
if (GetNondirectionalIndexByOrientation(triangular_patch.orientation) !=
GetNondirectionalIndexByOrientation(quadrilateral_patch.orientation) ||
std::abs(quadrilateral_patch_values.at(quadrilateral_patch_idx) -
triangular_patch_values.at(triangular_patch_idx)) > kEpsilon) {
Terminate(
fmt::format("Triangular patch #{} and quadrilateral patch #{} don't match.\n"
"Triangular patch: Orientation #{} P: #{}.\n"
"Quadrilateral patch: Orientation #{} P: #{}.\n",
triangular_patch_idx, quadrilateral_patch_idx,
GetNondirectionalIndexByOrientation(triangular_patch.orientation),
triangular_patch_values.at(triangular_patch_idx),
GetNondirectionalIndexByOrientation(quadrilateral_patch.orientation),
quadrilateral_patch_values.at(quadrilateral_patch_idx)));
}
matching_patch_pairs.push_back({triangular_patch_idx, quadrilateral_patch_idx});
}
hex::LaplacianSmoothForSurfacePatchBoundaryEdges(
triangular_boundary_edges_soup, quadrilateral_boundary_edges_soup,
hexahedral_polycube_matmesh.mat_coordinates, mat_tetrahedral_polycube_coordinates,
tetrahedral_matmesh.mat_coordinates, mat_hexahedral_coordinates, 10);
hex::LaplacianSmoothForSurfacePatches(quadrilateral_topomesh, mat_surface_triangles,
triangular_oriented_patches, quadrilateral_oriented_patches,
matching_patch_pairs, tetrahedral_matmesh.mat_coordinates,
mat_hexahedral_coordinates, 10);
hex::LaplacianSmoothForVolume(hexahedral_topomesh, mat_hexahedral_coordinates, 10);
return mat_hexahedral_coordinates;
}
} // namespace sha
} // namespace da