Skip to content

Commit 74b798d

Browse files
committed
Merge branch 'main' into spenbert/offsets
* main: Simplify before embedding if topology preservation is not required. Make shortest edge collapse work for non-manifold meshes. Fix the issue with the empty envelope after simplification.
2 parents 8e62de1 + af84175 commit 74b798d

17 files changed

Lines changed: 261 additions & 72 deletions

File tree

cmake/wmtk_data.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ ExternalProject_Add(
1616
SOURCE_DIR ${WMTK_DATA_ROOT}
1717

1818
GIT_REPOSITORY https://github.com/wildmeshing/data2.git
19-
GIT_TAG 5cf9136e9004c15605227001a67df971af025fd7
19+
GIT_TAG 36bb3968aa1e685d3da2e38b87bc6fa5ab328a13
2020

2121
CONFIGURE_COMMAND ""
2222
BUILD_COMMAND ""

components/image_simulation/wmtk/components/image_simulation/EdgeCollapsing.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,11 @@ void ImageSimulationMesh::simplify()
389389
m_envelope = nullptr;
390390
m_V_envelope.clear();
391391
m_F_envelope.clear();
392-
init_envelope(V, F);
392+
if (V.size() > 0 && F.size() > 0) {
393+
init_envelope(V, F);
394+
} else {
395+
logger().warn("No surface faces left after simplification, skip re-building envelope");
396+
}
393397
}
394398
logger().info("===== Simplification done =====");
395399
}

components/image_simulation/wmtk/components/image_simulation/EmbedSurface.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <bitset>
2020
#include <filesystem>
2121
#include <paraviewo/VTUWriter.hpp>
22+
#include <wmtk/io/read_triangle_mesh.hpp>
2223
#include <wmtk/utils/InsertTriangleUtils.hpp>
2324
#include <wmtk/utils/Logger.hpp>
2425
#include <wmtk/utils/ManifoldUtils.hpp>
@@ -856,7 +857,9 @@ EmbedSurface::EmbedSurface(const std::vector<std::string>& img_filenames, const
856857

857858
EmbedSurface::EmbedSurface(
858859
const std::vector<std::string>& img_filenames,
859-
const std::vector<Matrix4d>& img_transform)
860+
const std::vector<Matrix4d>& img_transform,
861+
const double tol_rel,
862+
const double tol_abs)
860863
: m_img_filenames(img_filenames)
861864
{
862865
assert(img_filenames.size() == img_transform.size());
@@ -869,7 +872,7 @@ EmbedSurface::EmbedSurface(
869872
log_and_throw_error("Input file {} does not exist", m_img_filenames[i]);
870873
}
871874
MatrixXi F_single;
872-
igl::read_triangle_mesh(m_img_filenames[i], Vs[i], F_single);
875+
io::read_triangle_mesh(m_img_filenames[i], Vs[i], F_single, tol_rel, tol_abs);
873876

874877
assert(Vs[i].cols() == 3);
875878
assert(F_single.cols() == 3);
@@ -920,13 +923,13 @@ EmbedSurface::EmbedSurface(
920923
F_surf_from_vector(tris);
921924
}
922925

923-
void EmbedSurface::simplify_surface(const double eps)
926+
void EmbedSurface::simplify_surface(const double eps, const int num_threads)
924927
{
925928
// convert to STL vectors
926929
std::vector<Eigen::Vector3d> verts = V_surf_to_vector();
927930
std::vector<std::array<size_t, 3>> tris = F_surf_to_vector();
928931

929-
shortest_edge_collapse::ShortestEdgeCollapse surf_mesh(verts, 0, false);
932+
shortest_edge_collapse::ShortestEdgeCollapse surf_mesh(verts, num_threads, false);
930933

931934
// must be a small envelope to ensure correct tet tags later on
932935
surf_mesh.create_mesh(verts.size(), tris, modified_nonmanifold_v, eps);
@@ -1375,6 +1378,14 @@ std::pair<Vector3d, Vector3d> EmbedSurface::bbox_minmax() const
13751378
return std::make_pair(bbox_min, bbox_max);
13761379
}
13771380

1381+
std::pair<Vector3d, Vector3d> EmbedSurface::bbox_surf_minmax() const
1382+
{
1383+
const Vector3d bbox_max = m_V_surface.colwise().maxCoeff();
1384+
const Vector3d bbox_min = m_V_surface.colwise().minCoeff();
1385+
1386+
return std::make_pair(bbox_min, bbox_max);
1387+
}
1388+
13781389
std::vector<Eigen::Vector3d> EmbedSurface::V_surf_to_vector() const
13791390
{
13801391
std::vector<Eigen::Vector3d> verts;

components/image_simulation/wmtk/components/image_simulation/EmbedSurface.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,16 @@ class EmbedSurface
101101
*/
102102
EmbedSurface(
103103
const std::vector<std::string>& img_filenames,
104-
const std::vector<Matrix4d>& img_transform = {});
104+
const std::vector<Matrix4d>& img_transform = {},
105+
const double tol_rel = -1,
106+
const double tol_abs = -1);
105107

106108
/**
107109
* @brief Simplify the input surface while staying within the eps envelope.
108110
*
109111
* @param eps The absolute envelope thickness.
110112
*/
111-
void simplify_surface(const double eps);
113+
void simplify_surface(const double eps, const int num_threads = 0);
112114

113115
/**
114116
* @brief Merge vertices that are closer than eps.
@@ -151,6 +153,7 @@ class EmbedSurface
151153
void write_emb_vtu(const std::string& filename) const;
152154

153155
std::pair<Vector3d, Vector3d> bbox_minmax() const;
156+
std::pair<Vector3d, Vector3d> bbox_surf_minmax() const;
154157

155158
std::vector<Eigen::Vector3d> V_surf_to_vector() const;
156159
std::vector<std::array<size_t, 3>> F_surf_to_vector() const;

components/image_simulation/wmtk/components/image_simulation/ImageSimulationMesh.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,8 +586,11 @@ void ImageSimulationMesh::init_envelope(const MatrixXd& V, const MatrixXi& F)
586586
if (m_envelope) {
587587
log_and_throw_error("Envelope was already initialized once.");
588588
}
589+
if (V.size() == 0 || F.size() == 0) {
590+
log_and_throw_error("Envelope vertices and faces cannot be empty.");
591+
}
592+
589593
assert(m_V_envelope.empty() && m_F_envelope.empty());
590-
assert(V.size() != 0 && F.size() != 0);
591594
assert(V.cols() == 3); // vertices must be in 3D
592595
assert(F.cols() == 3); // envelope must be triangles
593596

components/image_simulation/wmtk/components/image_simulation/image_simulation.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,9 @@ void run_3D(const nlohmann::json& json_params, const InputData& input_data)
9999
write_unique_vtu();
100100

101101

102-
if (!skip_simplify) {
102+
if (params.preserve_topology && !skip_simplify) {
103103
// collapse for getting the right edge length
104+
logger().info("Simplify after insertion");
104105
mesh.simplify();
105106
}
106107

components/image_simulation/wmtk/components/image_simulation/image_simulation_spec.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ nlohmann::json image_simulation_spec = R"(
147147
{
148148
"pointer": "/skip_simplify",
149149
"type": "bool",
150-
"default": true,
150+
"default": false,
151151
"doc": "If true, input simplification will be skipped."
152152
},
153153
{
@@ -189,7 +189,7 @@ nlohmann::json image_simulation_spec = R"(
189189
{
190190
"pointer": "/eps_simplify_rel",
191191
"type": "float",
192-
"default": 2e-3,
192+
"default": 2e-4,
193193
"doc": "Envelope thickness relative to the bounding box for the initial simplification."
194194
},
195195
{

components/image_simulation/wmtk/components/image_simulation/image_simulation_spec.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@
142142
{
143143
"pointer": "/skip_simplify",
144144
"type": "bool",
145-
"default": true,
145+
"default": false,
146146
"doc": "If true, input simplification will be skipped."
147147
},
148148
{
@@ -184,7 +184,7 @@
184184
{
185185
"pointer": "/eps_simplify_rel",
186186
"type": "float",
187-
"default": 2e-3,
187+
"default": 2e-4,
188188
"doc": "Envelope thickness relative to the bounding box for the initial simplification."
189189
},
190190
{

components/image_simulation/wmtk/components/image_simulation/read_image_msh.cpp

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -475,17 +475,47 @@ InputData read_mesh(
475475
const bool use_sample_envelope = json_params["use_sample_envelope"];
476476
const int NUM_THREADS = json_params["num_threads"];
477477
const int max_its = json_params["max_iterations"];
478-
const bool write_vtu = json_params["write_vtu"];
478+
const bool debug_output = json_params["DEBUG_output"];
479479
const bool preserve_topology = json_params["preserve_topology"];
480+
const bool skip_simplify = json_params["skip_simplify"];
481+
const double epsr_simplify = json_params["eps_simplify_rel"];
482+
double eps_simplify = json_params["eps_simplify"];
480483
const std::vector<std::string> input_names = json_params["input_names"];
481484

485+
double tol_rel = -1;
486+
double tol_abs = -1;
487+
if (!preserve_topology) {
488+
tol_rel = 0.1 * epsr_simplify;
489+
tol_abs = 0.1 * eps_simplify;
490+
}
491+
482492
// convert mesh into tet mesh
483-
EmbedSurface image_mesh(input_paths, input_transforms);
493+
EmbedSurface image_mesh(input_paths, input_transforms, tol_rel, tol_abs);
484494

485-
if (write_vtu) {
495+
if (debug_output) {
486496
image_mesh.write_surf_off(output_filename + "_input.off");
487497
}
488498

499+
if (!preserve_topology && !skip_simplify) {
500+
/**
501+
* Simplify the input surface only if topology preservation is not required, since
502+
* simplification can change the topology.
503+
* If topology preservation is required, simplification is performed after insertion.
504+
*/
505+
auto [bbox_min, bbox_max] = image_mesh.bbox_surf_minmax();
506+
double diag = (bbox_max - bbox_min).norm();
507+
if (epsr_simplify > 0) {
508+
eps_simplify = diag * epsr_simplify;
509+
}
510+
logger().info("Simplify before insertion with eps = {:.4}", eps_simplify);
511+
512+
image_mesh.simplify_surface(eps_simplify, NUM_THREADS);
513+
514+
if (debug_output) {
515+
image_mesh.write_surf_off(output_filename + "_input_simplified.off");
516+
}
517+
}
518+
489519
input_data.V_envelope = image_mesh.V_surface();
490520
input_data.F_envelope = image_mesh.F_surface();
491521

@@ -494,7 +524,7 @@ InputData read_mesh(
494524
: image_mesh.embed_surface(preserve_topology);
495525
image_mesh.consolidate();
496526

497-
if (write_vtu) {
527+
if (debug_output) {
498528
image_mesh.write_emb_surf_off(output_filename + "_input_emb.off");
499529
}
500530

components/shortest_edge_collapse/wmtk/components/shortest_edge_collapse/ShortestEdgeCollapse.cpp

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <Eigen/Core>
88
#include <Eigen/Geometry>
9+
#include <paraviewo/VTUWriter.hpp>
910

1011
namespace wmtk::components::shortest_edge_collapse {
1112

@@ -24,23 +25,16 @@ ShortestEdgeCollapse::ShortestEdgeCollapse(
2425
vertex_attrs[i] = {_m_vertex_positions[i], 0, false};
2526
}
2627

27-
void ShortestEdgeCollapse::set_freeze(const TriMesh::Tuple& v)
28+
void ShortestEdgeCollapse::freeze_boundary()
2829
{
29-
for (const Tuple& e : get_one_ring_edges_for_vertex(v)) {
30+
for (const Tuple& e : get_edges()) {
3031
if (is_boundary_edge(e)) {
31-
vertex_attrs[v.vid(*this)].freeze = true;
32-
continue;
32+
vertex_attrs[e.vid(*this)].freeze = true;
33+
vertex_attrs[e.switch_vertex(*this).vid(*this)].freeze = true;
3334
}
3435
}
3536
}
3637

37-
void ShortestEdgeCollapse::create_mesh_nofreeze(
38-
size_t n_vertices,
39-
const std::vector<std::array<size_t, 3>>& tris)
40-
{
41-
wmtk::TriMesh::init(n_vertices, tris);
42-
}
43-
4438
void ShortestEdgeCollapse::create_mesh(
4539
size_t n_vertices,
4640
const std::vector<std::array<size_t, 3>>& tris,
@@ -65,9 +59,7 @@ void ShortestEdgeCollapse::create_mesh(
6559
for (size_t v : frozen_verts) {
6660
vertex_attrs[v].freeze = true;
6761
}
68-
for (const Tuple& v : get_vertices()) { // the better way is to iterate through edges.
69-
set_freeze(v);
70-
}
62+
freeze_boundary();
7163
}
7264

7365
void ShortestEdgeCollapse::partition_mesh()
@@ -113,6 +105,36 @@ bool ShortestEdgeCollapse::write_triangle_mesh(std::string path)
113105
return igl::write_triangle_mesh(path, V, F);
114106
}
115107

108+
void ShortestEdgeCollapse::write_vtu(const std::string& path)
109+
{
110+
const std::string out_path = path + ".vtu";
111+
logger().info("Write {}", out_path);
112+
113+
MatrixXd V = MatrixXd::Zero(vert_capacity(), 3);
114+
MatrixXi F = MatrixXi::Zero(tri_capacity(), 3);
115+
116+
VectorXd freeze(vertex_attrs.size());
117+
freeze.setZero();
118+
119+
for (Tuple& t : get_vertices()) {
120+
const size_t i = t.vid(*this);
121+
V.row(i) = vertex_attrs[i].pos;
122+
freeze(i) = vertex_attrs[i].freeze ? 1 : 0;
123+
}
124+
125+
for (Tuple& t : get_faces()) {
126+
const size_t i = t.fid(*this);
127+
const auto vs = oriented_tri_vertices(t);
128+
for (int j = 0; j < 3; j++) {
129+
F(i, j) = (int)vs[j].vid(*this);
130+
}
131+
}
132+
133+
paraviewo::VTUWriter writer;
134+
writer.add_field("freeze", freeze);
135+
writer.write_mesh(out_path, V, F, paraviewo::CellType::Triangle);
136+
}
137+
116138
bool ShortestEdgeCollapse::collapse_edge_before(const Tuple& t)
117139
{
118140
if (!TriMesh::collapse_edge_before(t)) return false;

0 commit comments

Comments
 (0)