111TEST_CASE(
"smoothing_simple_examples",
"[components][isotropic_remeshing][2D]")
113 using namespace operations;
114 using namespace tri_mesh;
116 SECTION(
"hex_plus_two")
118 DEBUG_TriMesh mesh = wmtk::tests::hex_plus_two_with_position();
121 mesh.get_attribute_handle<
double>(
"vertices", PrimitiveType::Vertex);
124 auto pos = mesh.create_accessor<
double>(pos_attribute);
125 Tuple v4 = mesh.tuple_from_id(PrimitiveType::Vertex, 4);
126 pos.vector_attribute(v4) = Eigen::Vector3d{0.6, 0.9, 0};
131 std::make_shared<invariants::InteriorSimplexInvariant>(mesh, PrimitiveType::Vertex));
136 v4 = mesh.tuple_from_id(PrimitiveType::Vertex, 4);
137 Eigen::Vector3d after_smooth = pos.vector_attribute(v4);
138 CHECK((after_smooth - Eigen::Vector3d{1, 0, 0}).squaredNorm() == 0);
141 SECTION(
"edge_region")
143 DEBUG_TriMesh mesh = wmtk::tests::edge_region_with_position();
146 mesh.get_attribute_handle<
double>(
"vertices", PrimitiveType::Vertex);
149 auto pos = mesh.create_accessor<
double>(pos_attribute);
150 Tuple v4 = mesh.tuple_from_id(PrimitiveType::Vertex, 4);
151 Tuple v5 = mesh.tuple_from_id(PrimitiveType::Vertex, 5);
152 pos.vector_attribute(v4) = Eigen::Vector3d{0.6, 0.9, 0};
153 pos.vector_attribute(v5) = Eigen::Vector3d{1.4, -0.9, 0};
158 std::make_shared<invariants::InteriorSimplexInvariant>(mesh, PrimitiveType::Vertex));
162 for (
size_t i = 0; i < 10; ++i) {
168 v4 = mesh.tuple_from_id(PrimitiveType::Vertex, 4);
169 Eigen::Vector3d p4_after_smooth = pos.vector_attribute(v4);
170 CHECK((p4_after_smooth - Eigen::Vector3d{1, 0, 0}).squaredNorm() < 1e-10);
172 v5 = mesh.tuple_from_id(PrimitiveType::Vertex, 5);
173 Eigen::Vector3d p5_after_smooth = pos.vector_attribute(v5);
174 CHECK((p5_after_smooth - Eigen::Vector3d{2, 0, 0}).squaredNorm() < 1e-10);
178TEST_CASE(
"tangential_smoothing",
"[components][isotropic_remeshing][2D]")
180 using namespace operations;
182 DEBUG_TriMesh mesh = wmtk::tests::hex_plus_two_with_position();
185 mesh.get_attribute_handle<
double>(
"vertices", PrimitiveType::Vertex);
188 auto pos = mesh.create_accessor<
double>(pos_attribute);
189 Tuple v4 = mesh.tuple_from_id(PrimitiveType::Vertex, 4);
191 Eigen::Vector3d p_init;
194 p_init = Eigen::Vector3d{1, 0, 1};
198 p_init = Eigen::Vector3d{0.5, 0.5, 1};
202 p_init = Eigen::Vector3d{0, 0, 7};
205 pos.vector_attribute(v4) = p_init;
210 std::make_shared<invariants::InteriorSimplexInvariant>(mesh, PrimitiveType::Vertex));
215 v4 = mesh.tuple_from_id(PrimitiveType::Vertex, 4);
216 Eigen::Vector3d after_smooth = pos.vector_attribute(v4);
217 Eigen::Vector3d target = Eigen::Vector3d{1, 0, p_init[2]};
218 CHECK((after_smooth - target).squaredNorm() == 0);
262TEST_CASE(
"split_long_edges",
"[components][isotropic_remeshing][split][2D]")
264 using namespace operations;
268 DEBUG_TriMesh mesh = wmtk::tests::edge_region_with_position();
271 mesh.get_attribute_handle<
double>(
"vertices", PrimitiveType::Vertex);
274 op.set_new_attribute_strategy(
276 SplitBasicStrategy::None,
277 SplitRibBasicStrategy::Mean);
280 auto pos = mesh.create_accessor<
double>(pos_attribute);
281 const Tuple v4 = mesh.tuple_from_id(PrimitiveType::Vertex, 4);
282 const Tuple v5 = mesh.tuple_from_id(PrimitiveType::Vertex, 5);
284 pos.vector_attribute(v4) = Eigen::Vector3d{0.6, 0.9, 0};
285 pos.vector_attribute(v5) = Eigen::Vector3d{2.4, -0.9, 0};
289 double min_split_length_squared = -1;
293 min_split_length_squared = 6.4;
294 op.add_invariant(std::make_shared<MinEdgeLengthInvariant>(
296 pos_attribute.
as<
double>(),
297 min_split_length_squared));
301 size_t n_vertices = mesh.get_all(PrimitiveType::Vertex).size();
302 size_t n_iterations = 0;
303 for (; n_iterations < 10; ++n_iterations) {
306 const size_t n_vertices_new = mesh.get_all(PrimitiveType::Vertex).size();
307 if (n_vertices_new == n_vertices) {
310 n_vertices = n_vertices_new;
314 CHECK(n_iterations == 1);
315 REQUIRE(n_vertices == 11);
318 auto pos = mesh.create_accessor<
double>(pos_attribute);
319 const Tuple v10 = mesh.tuple_from_id(PrimitiveType::Vertex, 10);
320 CHECK((pos.vector_attribute(v10) - Eigen::Vector3d{1.5, 0, 0}).squaredNorm() == 0);
324 min_split_length_squared = 3.5;
325 op.add_invariant(std::make_shared<MinEdgeLengthInvariant>(
327 pos_attribute.
as<
double>(),
328 min_split_length_squared));
332 size_t n_vertices = mesh.get_all(PrimitiveType::Vertex).size();
333 size_t n_iterations = 0;
334 for (; n_iterations < 10; ++n_iterations) {
337 const size_t n_vertices_new = mesh.get_all(PrimitiveType::Vertex).size();
338 if (n_vertices_new == n_vertices) {
341 n_vertices = n_vertices_new;
345 CHECK(n_iterations < 5);
346 CHECK(n_vertices == 15);
350 auto pos = mesh.create_accessor<
double>(pos_attribute);
351 for (
const Tuple& e : mesh.get_all(PrimitiveType::Edge)) {
352 const Eigen::Vector3d p0 = pos.vector_attribute(e);
353 const Eigen::Vector3d p1 = pos.vector_attribute(mesh.switch_vertex(e));
354 const double l_squared = (p1 - p0).squaredNorm();
355 CHECK(l_squared < min_split_length_squared);
359TEST_CASE(
"collapse_short_edges",
"[components][isotropic_remeshing][collapse][2D]")
361 using namespace operations;
363 DEBUG_TriMesh mesh = wmtk::tests::edge_region_with_position();
365 auto pos_attribute = mesh.get_attribute_handle<
double>(
"vertices", PrimitiveType::Vertex);
369 op.add_invariant(std::make_shared<MultiMeshLinkConditionInvariant>(mesh));
370 op.set_new_attribute_strategy(pos_attribute, CollapseBasicStrategy::Mean);
375 auto pos = mesh.create_accessor<
double>(pos_attribute);
376 const Tuple v4 = mesh.tuple_from_id(PrimitiveType::Vertex, 4);
377 const Tuple v5 = mesh.tuple_from_id(PrimitiveType::Vertex, 5);
379 pos.vector_attribute(v4) = Eigen::Vector3d{1.4, 0, 0};
380 pos.vector_attribute(v5) = Eigen::Vector3d{1.6, 0, 0};
384 std::make_shared<MaxEdgeLengthInvariant>(mesh, pos_attribute.as<
double>(), 0.1));
388 size_t n_vertices = mesh.get_all(PrimitiveType::Vertex).size();
389 size_t n_iterations = 0;
390 for (; n_iterations < 10; ++n_iterations) {
393 const size_t n_vertices_new = mesh.get_all(PrimitiveType::Vertex).size();
394 if (n_vertices_new == n_vertices) {
397 n_vertices = n_vertices_new;
401 REQUIRE(n_iterations == 1);
402 REQUIRE(n_vertices == 9);
405 const Tuple v5 = mesh.tuple_from_id(PrimitiveType::Vertex, 5);
406#if defined(WMTK_ENABLE_HASH_UPDATE)
407 REQUIRE(mesh.is_valid_with_hash(v5));
409 REQUIRE(mesh.is_valid(v5));
412 auto pos = mesh.create_accessor<
double>(pos_attribute);
413 Eigen::Vector3d p5 = pos.vector_attribute(v5);
414 CHECK((p5 - Eigen::Vector3d{1.5, 0, 0}).squaredNorm() == 0);
416 SECTION(
"towards_boundary_true")
419 auto pos = mesh.create_accessor<
double>(pos_attribute);
420 const Tuple v4 = mesh.tuple_from_id(PrimitiveType::Vertex, 4);
422 pos.vector_attribute(v4) = Eigen::Vector3d{0.6, 0.9, 0};
427 auto tmp = std::make_shared<CollapseNewAttributeStrategy<double>>(pos_attribute);
428 tmp->set_strategy(CollapseBasicStrategy::Mean);
429 tmp->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
430 op.set_new_attribute_strategy(pos_attribute, tmp);
434 std::make_shared<MaxEdgeLengthInvariant>(mesh, pos_attribute.as<
double>(), 0.1));
438 size_t n_vertices = mesh.get_all(PrimitiveType::Vertex).size();
439 size_t n_iterations = 0;
440 for (; n_iterations < 10; ++n_iterations) {
443 const size_t n_vertices_new = mesh.get_all(PrimitiveType::Vertex).size();
444 if (n_vertices_new == n_vertices) {
447 n_vertices = n_vertices_new;
451 REQUIRE(n_iterations == 1);
452 REQUIRE(n_vertices == 9);
454 const Tuple v0 = mesh.tuple_from_id(PrimitiveType::Vertex, 0);
455#if defined(WMTK_ENABLE_HASH_UPDATE)
456 REQUIRE(mesh.is_valid_with_hash(v0));
458 REQUIRE(mesh.is_valid(v0));
461 auto pos = mesh.create_accessor<
double>(pos_attribute);
462 Eigen::Vector3d p0 = pos.vector_attribute(v0);
463 CHECK((p0 - Eigen::Vector3d{0.5, 1, 0}).squaredNorm() == 0);
465 SECTION(
"towards_boundary_false")
468 auto pos = mesh.create_accessor<
double>(pos_attribute);
469 const Tuple v4 = mesh.tuple_from_id(PrimitiveType::Vertex, 4);
471 pos.vector_attribute(v4) = Eigen::Vector3d{0.6, 0.9, 0};
475 std::make_shared<MaxEdgeLengthInvariant>(mesh, pos_attribute.as<
double>(), 0.1));
481 size_t n_vertices = mesh.get_all(PrimitiveType::Vertex).size();
482 size_t n_iterations = 0;
483 for (; n_iterations < 10; ++n_iterations) {
486 const size_t n_vertices_new = mesh.get_all(PrimitiveType::Vertex).size();
487 if (n_vertices_new == n_vertices) {
490 n_vertices = n_vertices_new;
494 REQUIRE(n_iterations == 1);
495 REQUIRE(n_vertices == 9);
497 const Tuple v0 = mesh.tuple_from_id(PrimitiveType::Vertex, 0);
498#if defined(WMTK_ENABLE_HASH_UPDATE)
499 REQUIRE(mesh.is_valid_with_hash(v0));
501 REQUIRE(mesh.is_valid(v0));
504 auto pos = mesh.create_accessor<
double>(pos_attribute);
505 Eigen::Vector3d p0 = pos.vector_attribute(v0);
506 CHECK((p0 - Eigen::Vector3d{0.55, 0.95, 0}).squaredNorm() == 0);
508 SECTION(
"collapse_boundary_true")
511 auto pos = mesh.create_accessor<
double>(pos_attribute);
512 const Tuple v1 = mesh.tuple_from_id(PrimitiveType::Vertex, 1);
514 pos.vector_attribute(v1) = Eigen::Vector3d{0.6, 1, 0};
518 std::make_shared<MaxEdgeLengthInvariant>(mesh, pos_attribute.as<
double>(), 0.1));
524 REQUIRE(mesh.get_all(PrimitiveType::Vertex).size() == 9);
526 const Tuple v0 = mesh.tuple_from_id(PrimitiveType::Vertex, 0);
527#if defined(WMTK_ENABLE_HASH_UPDATE)
528 REQUIRE(mesh.is_valid_with_hash(v0));
530 REQUIRE(mesh.is_valid(v0));
533 auto pos = mesh.create_accessor<
double>(pos_attribute);
534 Eigen::Vector3d p0 = pos.vector_attribute(v0);
535 CHECK((p0 - Eigen::Vector3d{0.55, 1, 0}).squaredNorm() == 0);
537 SECTION(
"collapse_boundary_false")
540 auto pos = mesh.create_accessor<
double>(pos_attribute);
541 const Tuple v1 = mesh.tuple_from_id(PrimitiveType::Vertex, 1);
543 pos.vector_attribute(v1) = Eigen::Vector3d{0.6, 1, 0};
547 std::make_shared<MaxEdgeLengthInvariant>(mesh, pos_attribute.as<
double>(), 0.1));
549 std::make_shared<invariants::InteriorSimplexInvariant>(mesh, PrimitiveType::Edge));
555 REQUIRE(mesh.get_all(PrimitiveType::Vertex).size() == 10);
559TEST_CASE(
"swap_edge_for_valence",
"[components][isotropic_remeshing][swap][2D]")
561 using namespace operations;
563 DEBUG_TriMesh mesh = wmtk::tests::embedded_diamond();
567 std::make_shared<invariants::InteriorSimplexInvariant>(mesh, PrimitiveType::Edge));
570 Tuple swap_edge = mesh.edge_tuple_with_vs_and_t(6, 7, 5);
577 SECTION(
"single_op_fail")
579 op.
add_invariant(std::make_shared<invariants::ValenceImprovementInvariant>(mesh));
582 SECTION(
"swap_success")
587 REQUIRE(!ret.empty());
588 const Tuple& return_tuple = ret[0].tuple();
589 swap_edge = return_tuple;
590 long id0 = mesh.id_vertex(swap_edge);
591 long id1 = mesh.id_vertex(mesh.switch_vertex(swap_edge));
594 const Tuple v3 = mesh.tuple_from_id(PrimitiveType::Vertex, 3);
595 const Tuple v6 = mesh.tuple_from_id(PrimitiveType::Vertex, 6);
596 const Tuple v7 = mesh.tuple_from_id(PrimitiveType::Vertex, 7);
597 const Tuple v10 = mesh.tuple_from_id(PrimitiveType::Vertex, 10);
598 CHECK(vertex_one_ring(mesh, v3).size() == 7);
599 CHECK(vertex_one_ring(mesh, v10).size() == 7);
600 CHECK(vertex_one_ring(mesh, v6).size() == 5);
601 CHECK(vertex_one_ring(mesh, v7).size() == 5);
604 op.
add_invariant(std::make_shared<invariants::ValenceImprovementInvariant>(mesh));
610 REQUIRE(!ret.empty());
611 const Tuple& return_tuple = ret[0].tuple();
612 swap_edge = return_tuple;
614 CHECK(mesh.id(
Simplex::vertex(mesh, mesh.switch_vertex(swap_edge))) == 6);
616 SECTION(
"with_scheduler")
625 const Tuple v3 = mesh.tuple_from_id(PrimitiveType::Vertex, 3);
626 const Tuple v6 = mesh.tuple_from_id(PrimitiveType::Vertex, 6);
627 const Tuple v7 = mesh.tuple_from_id(PrimitiveType::Vertex, 7);
628 const Tuple v10 = mesh.tuple_from_id(PrimitiveType::Vertex, 10);
629 CHECK(vertex_one_ring(mesh, v3).size() == 6);
630 CHECK(vertex_one_ring(mesh, v10).size() == 6);
631 CHECK(vertex_one_ring(mesh, v6).size() == 6);
632 CHECK(vertex_one_ring(mesh, v7).size() == 6);
637 const Tuple e = mesh.edge_tuple_with_vs_and_t(6, 7, 5);
638 op.
add_invariant(std::make_shared<invariants::ValenceImprovementInvariant>(mesh));
820TEST_CASE(
"remeshing_preserve_topology",
"[components][isotropic_remeshing][2D][.]")
825 DEBUG_TriMesh mesh = edge_region_with_position();
828 auto pos_handle = mesh.get_attribute_handle<
double>(
"vertices", PrimitiveType::Vertex);
829 std::vector<attribute::MeshAttributeHandle> pass_through_attributes;
832 auto tag_accessor = mesh.create_accessor<int64_t>(tag_handle);
833 for (
const Tuple& e : mesh.get_all(PrimitiveType::Edge)) {
834 if (mesh.is_boundary_edge(e)) {
835 tag_accessor.scalar_attribute(e) = 1;
837 tag_accessor.scalar_attribute(e) = 0;
840 std::shared_ptr<Mesh> child_ptr =
845 PrimitiveType::Edge);
847 REQUIRE(mesh.get_child_meshes().size() == 1);
848 mesh.multi_mesh_manager().check_map_valid(mesh);
849 const auto& child_mesh = *child_ptr;
850 CHECK(child_mesh.get_all(PrimitiveType::Edge).size() == 8);
851 CHECK(child_mesh.get_all(PrimitiveType::Vertex).size() == 8);
854 wmtk::components::internal::isotropic_remeshing(
856 pass_through_attributes,
864 REQUIRE(mesh.is_connectivity_valid());
865 mesh.multi_mesh_manager().check_map_valid(mesh);
868 size_t n_boundary_edges = 0;
869 for (
const Tuple& e : mesh.get_all(PrimitiveType::Edge)) {
870 if (mesh.is_boundary_edge(e)) {
878 ParaviewWriter writer(
"remeshing_test",
"vertices", mesh,
true,
true,
true,
false);
879 mesh.serialize(writer);
883TEST_CASE(
"remeshing_preserve_topology_realmesh",
"[components][isotropic_remeshing][2D][.]")
886 using namespace operations;
893 json input_component_json = {
895 {
"name",
"input_mesh"},
896 {
"cell_dimension", 2},
897 {
"file", (
data_dir /
"circle.msh").
string()},
898 {
"ignore_z",
false}};
901 auto m = cache.
read_mesh(input_component_json[
"name"]);
902 tests::DEBUG_TriMesh& mesh =
static_cast<tests::DEBUG_TriMesh&
>(*m);
904 auto pos_handle = mesh.get_attribute_handle<
double>(
"vertices", PrimitiveType::Vertex);
905 std::vector<attribute::MeshAttributeHandle> pass_through_attributes;
908 auto tag_accessor = mesh.create_accessor<int64_t>(tag_handle);
909 for (
const Tuple& e : mesh.get_all(PrimitiveType::Edge)) {
910 if (mesh.is_boundary_edge(e)) {
911 tag_accessor.scalar_attribute(e) = 1;
913 tag_accessor.scalar_attribute(e) = 0;
916 std::shared_ptr<Mesh> child_ptr =
921 PrimitiveType::Edge);
923 REQUIRE(mesh.get_child_meshes().size() == 1);
930 for (
int i = 0; i < 25; i++) {
931 wmtk::components::internal::isotropic_remeshing(
933 pass_through_attributes,
940 REQUIRE(mesh.is_connectivity_valid());
941 mesh.multi_mesh_manager().check_map_valid(mesh);
945 auto child_vertex_handle =
947 auto child_vertex_accessor = child_ptr->create_accessor<int64_t>(child_vertex_handle);
949 auto parent_vertex_handle =
950 mesh.get_attribute_handle<
double>(
"vertices", PrimitiveType::Vertex);
951 auto parent_vertex_accessor = mesh.create_accessor<int64_t>(parent_vertex_handle);
953 for (
const auto& v : child_ptr->get_all(PrimitiveType::Vertex)) {
954 auto parent_v = child_ptr->map_to_root_tuple(
Simplex(*child_ptr, PrimitiveType::Vertex, v));
955 child_vertex_accessor.vector_attribute(v) =
956 parent_vertex_accessor.vector_attribute(parent_v);
969 mesh.serialize(writer);
972 cache.
get_cache_path() /
"remeshing_test_circle_child_mesh_final",
979 child_ptr->serialize(writer2);