1 #include <catch2/catch_test_macros.hpp>
2 #include <nlohmann/json.hpp>
6 #include <wmtk/components/utils/Paths.hpp>
64 using namespace simplex;
65 using namespace operations;
66 using namespace operations::tri_mesh;
67 using namespace operations::tet_mesh;
68 using namespace operations::composite;
70 using namespace function;
71 using namespace invariants;
75 const std::filesystem::path
data_dir = WMTK_DATA_DIR;
77 TEST_CASE(
"wildmeshing",
"[components][wildmeshing][.]")
81 json input_component_json = {
83 {
"file",
data_dir /
"adaptive_tessellation_test" /
"after_smooth_uv.msh"},
93 "target_edge_length": 0.01,
94 "intermediate_output": true,
95 "attributes": {"position": "vertices"},
104 TEST_CASE("wildmeshing_3d",
"[components][wildmeshing][.]")
108 json input_component_json = {
114 {
"ignore_z",
false}};
117 json attributes = {{
"position",
"vertices"}};
122 {
"target_edge_length", 0.05},
123 {
"intermediate_output",
false},
124 {
"output",
"test_mm"},
125 {
"pass_through", std::vector<int64_t>()},
126 {
"attributes", attributes}};
132 TEST_CASE(
"wildmeshing_3d_multimesh",
"[components][wildmeshing][.]")
136 json input_component_json = {
140 {
"file",
data_dir /
"sphere_coarse_005_.msh"},
141 {
"ignore_z",
false}};
144 json attributes = {{
"position",
"vertices"}};
149 {
"target_edge_length", 0.1},
150 {
"intermediate_output",
true},
151 {
"output",
"test_multimesh"},
152 {
"pass_through", std::vector<int64_t>()},
153 {
"attributes", attributes}};
160 const std::string& path,
161 const std::string& name,
162 std::function<std::shared_ptr<Operation>(
164 std::shared_ptr<Rounding>&,
167 std::vector<attribute::MeshAttributeHandle>&,
174 const double target_max_amips = 10;
175 const double min_edge_length = 0.001;
176 const double target_edge_length = 1.04;
179 std::ifstream file(
data_dir / path);
181 std::vector<Vector4l> tets;
182 std::string x, y, z, token;
184 while (file.good()) {
186 std::getline(file, line);
187 std::istringstream iss(line);
189 if (line[0] ==
'v') {
190 int sx = line.find(
' ', 2);
191 x = line.substr(2, sx - 2);
193 int sy = line.find(
' ', sx + 1);
194 y = line.substr(sx + 1, sy - (sx + 1));
196 int sz = line.find(
' ', sy + 1);
197 z = line.substr(sy + 1, sz - (sy + 1));
200 }
else if (line[0] ==
't') {
202 iss >> f[0] >> f[1] >> f[3] >> f[2];
208 for (int64_t i = 0; i <
vertices.size(); ++i) {
213 for (int64_t i = 0; i < tets.size(); ++i) {
224 v.col(0).cast<
double>().maxCoeff(),
225 v.col(1).cast<
double>().maxCoeff(),
226 v.col(2).cast<
double>().maxCoeff());
228 v.col(0).cast<
double>().minCoeff(),
229 v.col(1).cast<
double>().minCoeff(),
230 v.col(2).cast<
double>().minCoeff());
235 double diag = (bbox_max - bbox_min).norm();
236 std::cout << diag << std::endl;
237 std::cout << 0.05 * diag << std::endl;
240 auto mesh = std::make_shared<TetMesh>();
243 logger().info(
"Mesh has {} vertices {} tests",
vertices.size(), tets.size());
248 auto pt_accessor = mesh->create_accessor(pt_attribute.as<
Rational>());
250 auto amips_attribute =
251 mesh->register_attribute<
double>(
"wildmeshing_amips", mesh->top_simplex_type(), 1);
252 auto amips_accessor = mesh->create_accessor(amips_attribute.as<
double>());
254 auto compute_amips = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorXd {
255 assert(P.rows() == 2 || P.rows() == 3);
256 assert(P.cols() == P.rows() + 1);
259 assert(P.rows() == 2);
260 std::array<double, 6> pts;
261 for (
size_t i = 0; i < 3; ++i) {
262 for (
size_t j = 0; j < 2; ++j) {
263 pts[2 * i + j] = P(j, i).to_double();
267 return Eigen::VectorXd::Constant(1, a);
270 assert(P.rows() == 3);
271 std::array<double, 12> pts;
272 for (
size_t i = 0; i < 4; ++i) {
273 for (
size_t j = 0; j < 3; ++j) {
274 pts[3 * i + j] = P(j, i).to_double();
278 return Eigen::VectorXd::Constant(1, a);
282 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<double, Rational>>(
286 amips_update->run_on_all();
290 auto edge_length_attribute =
292 auto edge_length_accessor = mesh->create_accessor(edge_length_attribute.as<
double>());
294 auto compute_edge_length = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorXd {
295 assert(P.cols() == 2);
296 assert(P.rows() == 2 || P.rows() == 3);
297 return Eigen::VectorXd::Constant(1, sqrt((P.col(0) - P.col(1)).squaredNorm().to_double()));
299 auto edge_length_update =
300 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<double, Rational>>(
301 edge_length_attribute,
303 compute_edge_length);
304 edge_length_update->run_on_all();
306 auto visited_edge_flag =
350 auto update_flag_func = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorX<char> {
351 assert(P.cols() == 2);
352 assert(P.rows() == 2 || P.rows() == 3);
353 return Eigen::VectorX<char>::Constant(1,
char(1));
356 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<char, Rational>>(
362 std::vector<attribute::MeshAttributeHandle> pass_through_attributes;
363 pass_through_attributes.push_back(edge_length_attribute);
364 pass_through_attributes.push_back(amips_attribute);
369 auto inversion_invariant =
370 std::make_shared<SimplexInversionInvariant<Rational>>(*mesh, pt_attribute.as<
Rational>());
374 auto rounding_pt_attribute =
376 auto rounding = std::make_shared<Rounding>(*mesh, rounding_pt_attribute);
377 rounding->add_invariant(
378 std::make_shared<RoundedInvariant>(*mesh, pt_attribute.as<
Rational>(),
true));
379 rounding->add_invariant(inversion_invariant);
385 while (success > 0) {
388 "Rounding, {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, "
389 "executing: {}. Total {}",
390 stats.number_of_performed_operations(),
391 stats.number_of_successful_operations(),
392 stats.number_of_failed_operations(),
393 stats.collecting_time,
395 stats.executing_time,
398 success = stats.number_of_successful_operations();
400 int64_t unrounded = 0;
402 const auto p = pt_accessor.vector_attribute(v);
403 for (int64_t d = 0; d < 3; ++d) {
404 if (!p[d].is_rounded()) {
411 logger().info(
"Mesh has {} unrounded vertices", unrounded);
419 pass_through_attributes,
426 "{}, {} ops (S/F) {}/{}. Passes: {}. Time collecting: {}, sorting: {}, "
427 "executing: {}; total {}. Passes (avg) collecting: {}, sorting: {}, executing: {}",
429 stats.number_of_performed_operations(),
430 stats.number_of_successful_operations(),
431 stats.number_of_failed_operations(),
432 stats.sub_stats.size(),
433 stats.collecting_time,
435 stats.executing_time,
437 stats.avg_sub_collecting_time(),
438 stats.avg_sub_sorting_time(),
439 stats.avg_sub_executing_time());
441 int64_t unrounded = 0;
443 const auto p = pt_accessor.vector_attribute(v);
444 for (int64_t d = 0; d < 3; ++d) {
445 if (!p[d].is_rounded()) {
452 logger().info(
"Mesh has {} unrounded vertices", unrounded);
454 double max_energy = std::numeric_limits<double>::lowest();
455 double min_energy = std::numeric_limits<double>::max();
456 for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
458 double e = amips_accessor.scalar_attribute(t);
459 max_energy = std::max(max_energy, e);
460 min_energy = std::min(min_energy, e);
463 logger().info(
"Max AMIPS Energy: {}, Min AMIPS Energy: {}", max_energy, min_energy);
465 "{} vertices, {} edges, {} tets",
471 "Reference performance of original TetWild: split: 0.08s, collapse: 1.81525s, swap: 0.24s");
475 TEST_CASE(
"tetwild-split",
"[components][wildmeshing][.]")
477 logger().set_level(spdlog::level::debug);
480 "split_initial_state_test.obj",
483 std::shared_ptr<Rounding>& rounding,
486 std::vector<attribute::MeshAttributeHandle>& pass_through_attributes,
493 const double target_edge_length = 0.59019;
494 const double target_max_amips = 10;
495 const double min_edge_length = 0.001;
498 "wildmeshing_target_edge_length",
505 auto compute_target_edge_length =
509 target_edge_length_attribute,
511 const Eigen::MatrixXd& P,
512 const std::vector<Tuple>& neighs) -> Eigen::VectorXd {
513 auto target_edge_length_accessor =
516 assert(P.rows() == 1);
517 assert(!neighs.empty());
518 assert(P.cols() == neighs.size());
519 const double current_target_edge_length =
520 target_edge_length_accessor.const_scalar_attribute(neighs[0]);
521 const double max_amips = P.maxCoeff();
523 double new_target_edge_length = current_target_edge_length;
524 if (max_amips > target_max_amips) {
525 new_target_edge_length *= 0.5;
527 new_target_edge_length *= 1.5;
529 new_target_edge_length =
530 std::min(new_target_edge_length, target_edge_length);
531 new_target_edge_length =
532 std::max(new_target_edge_length, min_edge_length);
534 return Eigen::VectorXd::Constant(1, new_target_edge_length);
537 pass_through_attributes.push_back(target_edge_length_attribute);
539 auto update_flag_func = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorX<char> {
540 assert(P.cols() == 2);
541 assert(P.rows() == 2 || P.rows() == 3);
542 return Eigen::VectorX<char>::Constant(1,
char(1));
545 auto edge_length_attribute =
548 auto edge_length_accessor = mesh.
create_accessor(edge_length_attribute.as<
double>());
552 return -edge_length_accessor.scalar_attribute(s);
555 auto todo_larger = std::make_shared<TodoLargerInvariant>(
557 edge_length_attribute.as<
double>(),
558 target_edge_length_attribute.as<
double>(),
561 auto split = std::make_shared<EdgeSplit>(mesh);
562 split->set_priority(long_edges_first);
564 split->add_invariant(todo_larger);
566 split->set_new_attribute_strategy(pt_attribute);
567 split->set_new_attribute_strategy(
571 for (
const auto& attr : pass_through_attributes) {
572 split->set_new_attribute_strategy(attr);
575 split->add_transfer_strategy(amips_update);
576 split->add_transfer_strategy(edge_length_update);
577 split->add_transfer_strategy(tag_update);
579 auto split_then_round = std::make_shared<AndOperationSequence>(mesh);
580 split_then_round->add_operation(split);
581 split_then_round->add_operation(rounding);
583 return split_then_round;
587 TEST_CASE(
"tetwild-collapse",
"[components][wildmeshing][.]")
591 "collapse_initial_state_104985.obj",
594 std::shared_ptr<Rounding>& rounding,
597 std::vector<attribute::MeshAttributeHandle>& pass_through_attributes,
604 const double target_edge_length = 1.04;
605 const double target_max_amips = 10;
606 const double min_edge_length = 0.001;
609 "wildmeshing_target_edge_length",
615 pass_through_attributes.push_back(target_edge_length_attribute);
617 auto edge_length_attribute =
620 auto edge_length_accessor = mesh.
create_accessor(edge_length_attribute.as<
double>());
624 std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
625 clps_strat->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
627 clps_strat->set_strategy(CollapseBasicStrategy::CopyOther);
630 auto inversion_invariant = std::make_shared<SimplexInversionInvariant<Rational>>(
633 auto link_condition = std::make_shared<MultiMeshLinkConditionInvariant>(mesh);
634 auto invariant_separate_substructures =
635 std::make_shared<invariants::SeparateSubstructuresInvariant>(mesh);
639 return edge_length_accessor.scalar_attribute(s);
642 auto todo_smaller = std::make_shared<TodoSmallerInvariant>(
644 edge_length_attribute.as<
double>(),
645 target_edge_length_attribute.as<
double>(),
648 std::shared_ptr<function::PerSimplexFunction>
amips =
649 std::make_shared<AMIPS>(mesh, pt_attribute);
651 auto function_invariant = std::make_shared<MaxFunctionInvariant>(
659 auto collapse = std::make_shared<EdgeCollapse>(mesh);
663 collapse->add_invariant(todo_smaller);
664 collapse->add_invariant(invariant_separate_substructures);
666 collapse->add_invariant(inversion_invariant);
667 collapse->add_invariant(function_invariant);
670 collapse->set_new_attribute_strategy(pt_attribute, clps_strat);
671 collapse->add_transfer_strategy(tag_update);
672 collapse->set_new_attribute_strategy(
675 for (
const auto& attr : pass_through_attributes) {
676 collapse->set_new_attribute_strategy(attr);
679 collapse->add_transfer_strategy(amips_update);
680 collapse->add_transfer_strategy(edge_length_update);
683 auto collapse_then_round = std::make_shared<AndOperationSequence>(mesh);
684 collapse_then_round->add_operation(collapse);
685 collapse_then_round->add_operation(rounding);
687 return collapse_then_round;
691 TEST_CASE(
"tetwild-collapse-twoway",
"[components][wildmeshing][.]")
693 logger().set_level(spdlog::level::debug);
695 "collapse_initial_state_104985.obj",
698 std::shared_ptr<Rounding>& rounding,
701 std::vector<attribute::MeshAttributeHandle>& pass_through_attributes,
708 const double target_edge_length = 1.04;
709 const double target_max_amips = 10;
710 const double min_edge_length = 0.001;
713 "wildmeshing_target_edge_length",
719 pass_through_attributes.push_back(target_edge_length_attribute);
721 auto edge_length_attribute =
724 auto edge_length_accessor = mesh.
create_accessor(edge_length_attribute.as<
double>());
728 std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
729 clps_strat->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
731 clps_strat->set_strategy(CollapseBasicStrategy::CopyOther);
734 auto inversion_invariant = std::make_shared<SimplexInversionInvariant<Rational>>(
737 auto link_condition = std::make_shared<MultiMeshLinkConditionInvariant>(mesh);
738 auto invariant_separate_substructures =
739 std::make_shared<invariants::SeparateSubstructuresInvariant>(mesh);
743 return edge_length_accessor.scalar_attribute(s);
746 auto todo_smaller = std::make_shared<TodoSmallerInvariant>(
748 edge_length_attribute.as<
double>(),
749 target_edge_length_attribute.as<
double>(),
752 std::shared_ptr<function::PerSimplexFunction>
amips =
753 std::make_shared<AMIPS>(mesh, pt_attribute);
755 auto function_invariant = std::make_shared<MaxFunctionInvariant>(
764 std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
765 clps_strat1->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
767 clps_strat1->set_strategy(CollapseBasicStrategy::CopyOther);
770 std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
771 clps_strat2->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
773 clps_strat2->set_strategy(CollapseBasicStrategy::CopyTuple);
775 auto setup_collapse = [&](std::shared_ptr<EdgeCollapse>& collapse) {
776 collapse->add_invariant(invariant_separate_substructures);
778 collapse->add_invariant(inversion_invariant);
779 collapse->add_invariant(function_invariant);
782 collapse->set_new_attribute_strategy(
786 collapse->add_transfer_strategy(tag_update);
787 for (
const auto& attr : pass_through_attributes) {
788 collapse->set_new_attribute_strategy(attr);
791 collapse->set_priority(short_edges_first);
795 collapse->add_transfer_strategy(amips_update);
796 collapse->add_transfer_strategy(edge_length_update);
800 auto collapse1 = std::make_shared<EdgeCollapse>(mesh);
801 collapse1->set_new_attribute_strategy(pt_attribute, clps_strat1);
802 setup_collapse(collapse1);
804 auto collapse2 = std::make_shared<EdgeCollapse>(mesh);
805 collapse2->set_new_attribute_strategy(pt_attribute, clps_strat2);
806 setup_collapse(collapse2);
808 auto collapse = std::make_shared<OrOperationSequence>(mesh);
809 collapse->add_operation(collapse1);
810 collapse->add_operation(collapse2);
811 collapse->add_invariant(todo_smaller);
813 auto collapse_then_round = std::make_shared<AndOperationSequence>(mesh);
814 collapse_then_round->add_operation(collapse);
815 collapse_then_round->add_operation(rounding);
817 return collapse_then_round;
821 TEST_CASE(
"tetwild-collapse-simulated",
"[components][wildmeshing][.]")
823 logger().set_level(spdlog::level::debug);
825 "collapse_initial_state_104985.obj",
828 std::shared_ptr<Rounding>& rounding,
831 std::vector<attribute::MeshAttributeHandle>& pass_through_attributes,
838 const double target_edge_length = 1.04;
839 const double target_max_amips = 10;
840 const double min_edge_length = 0.001;
843 "wildmeshing_target_edge_length",
849 pass_through_attributes.push_back(target_edge_length_attribute);
851 auto edge_length_attribute =
854 auto edge_length_accessor = mesh.
create_accessor(edge_length_attribute.as<
double>());
858 std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
859 clps_strat->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
861 clps_strat->set_strategy(CollapseBasicStrategy::CopyOther);
863 auto inversion_invariant = std::make_shared<SimplexInversionInvariant<Rational>>(
866 auto link_condition = std::make_shared<MultiMeshLinkConditionInvariant>(mesh);
867 auto invariant_separate_substructures =
868 std::make_shared<invariants::SeparateSubstructuresInvariant>(mesh);
872 return edge_length_accessor.scalar_attribute(s);
875 auto todo_smaller = std::make_shared<TodoSmallerInvariant>(
877 edge_length_attribute.as<
double>(),
878 target_edge_length_attribute.as<
double>(),
881 std::shared_ptr<function::PerSimplexFunction>
amips =
882 std::make_shared<AMIPS>(mesh, pt_attribute);
884 auto function_invariant = std::make_shared<MaxFunctionInvariant>(
893 std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
894 clps_strat1->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
896 clps_strat1->set_strategy(CollapseBasicStrategy::CopyOther);
899 std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
900 clps_strat2->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
902 clps_strat2->set_strategy(CollapseBasicStrategy::CopyTuple);
904 auto setup_collapse = [&](std::shared_ptr<EdgeCollapse>& collapse) {
905 collapse->add_invariant(invariant_separate_substructures);
907 collapse->add_invariant(inversion_invariant);
911 collapse->set_new_attribute_strategy(
915 collapse->add_transfer_strategy(tag_update);
916 for (
const auto& attr : pass_through_attributes) {
917 collapse->set_new_attribute_strategy(attr);
920 collapse->set_priority(short_edges_first);
924 collapse->add_transfer_strategy(amips_update);
925 collapse->add_transfer_strategy(edge_length_update);
929 auto amips_attribute =
932 auto collapse1 = std::make_shared<EdgeCollapse>(mesh);
933 collapse1->add_invariant(std::make_shared<CollapseEnergyBeforeInvariant>(
936 amips_attribute.as<
double>(),
938 collapse1->set_new_attribute_strategy(pt_attribute, clps_strat1);
939 setup_collapse(collapse1);
941 auto collapse2 = std::make_shared<EdgeCollapse>(mesh);
942 collapse2->add_invariant(std::make_shared<CollapseEnergyBeforeInvariant>(
945 amips_attribute.as<
double>(),
947 collapse2->set_new_attribute_strategy(pt_attribute, clps_strat2);
948 setup_collapse(collapse2);
950 auto collapse = std::make_shared<OrOperationSequence>(mesh);
951 collapse->add_operation(collapse1);
952 collapse->add_operation(collapse2);
953 collapse->add_invariant(todo_smaller);
955 auto collapse_then_round = std::make_shared<AndOperationSequence>(mesh);
956 collapse_then_round->add_operation(collapse);
957 collapse_then_round->add_operation(rounding);
959 return collapse_then_round;
964 TEST_CASE(
"tetwild-swap",
"[components][wildmeshing][.]")
966 logger().set_level(spdlog::level::debug);
968 "swap_initial_state.obj",
971 std::shared_ptr<Rounding>& rounding,
974 std::vector<attribute::MeshAttributeHandle>& pass_through_attributes,
981 const double target_edge_length = 0.59019;
982 const double target_max_amips = 10;
983 const double min_edge_length = 0.001;
986 "wildmeshing_target_edge_length",
992 pass_through_attributes.push_back(target_edge_length_attribute);
994 auto edge_length_attribute =
997 auto edge_length_accessor = mesh.
create_accessor(edge_length_attribute.as<
double>());
1001 std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
1002 clps_strat->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
1004 clps_strat->set_strategy(CollapseBasicStrategy::CopyOther);
1007 auto inversion_invariant = std::make_shared<SimplexInversionInvariant<Rational>>(
1010 auto link_condition = std::make_shared<MultiMeshLinkConditionInvariant>(mesh);
1011 auto invariant_separate_substructures =
1012 std::make_shared<invariants::SeparateSubstructuresInvariant>(mesh);
1016 return -edge_length_accessor.scalar_attribute(s);
1019 std::shared_ptr<function::PerSimplexFunction>
amips =
1020 std::make_shared<AMIPS>(mesh, pt_attribute);
1028 auto swap56 = std::make_shared<MinOperationSequence>(mesh);
1029 for (
int i = 0; i < 5; ++i) {
1030 auto swap = std::make_shared<TetEdgeSwap>(mesh, i);
1031 swap->collapse().add_invariant(invariant_separate_substructures);
1033 swap->collapse().set_new_attribute_strategy(
1035 CollapseBasicStrategy::CopyOther);
1036 swap->split().set_new_attribute_strategy(pt_attribute);
1037 swap->split().set_new_attribute_strategy(
1041 swap->collapse().set_new_attribute_strategy(
1045 swap->add_invariant(std::make_shared<Swap56EnergyBeforeInvariant>(
1050 for (
const auto& attr : pass_through_attributes) {
1051 swap->split().set_new_attribute_strategy(attr);
1052 swap->collapse().set_new_attribute_strategy(attr);
1055 swap56->add_operation(swap);
1058 auto swap56_energy_check = [&](int64_t idx,
const simplex::Simplex& t) ->
double {
1064 auto accessor = mesh.create_const_accessor(pt_attribute.
as<
Rational>());
1066 const Tuple e0 = t.tuple();
1067 const Tuple e1 = mesh.switch_tuple(e0,
PV);
1069 std::array<Tuple, 5> v;
1070 auto iter_tuple = e0;
1071 for (int64_t i = 0; i < 5; ++i) {
1072 v[i] = mesh.switch_tuples(iter_tuple, {
PE,
PV});
1073 iter_tuple = mesh.switch_tuples(iter_tuple, {
PF,
PT});
1075 if (iter_tuple != e0)
return 0;
1076 assert(iter_tuple == e0);
1080 std::array<Eigen::Vector3<Rational>, 7> positions = {
1081 {accessor.const_vector_attribute(v[(idx + 0) % 5]),
1082 accessor.const_vector_attribute(v[(idx + 1) % 5]),
1083 accessor.const_vector_attribute(v[(idx + 2) % 5]),
1084 accessor.const_vector_attribute(v[(idx + 3) % 5]),
1085 accessor.const_vector_attribute(v[(idx + 4) % 5]),
1086 accessor.const_vector_attribute(e0),
1087 accessor.const_vector_attribute(e1)}};
1089 std::array<Eigen::Vector3d, 7> positions_double = {
1090 {positions[0].cast<
double>(),
1091 positions[1].cast<double>(),
1092 positions[2].cast<
double>(),
1093 positions[3].cast<double>(),
1094 positions[4].cast<
double>(),
1095 positions[5].cast<double>(),
1096 positions[6].cast<
double>()}};
1098 std::array<std::array<int, 4>, 6> new_tets = {
1106 double new_energy_max = std::numeric_limits<double>::lowest();
1108 for (
int i = 0; i < 6; ++i) {
1110 positions[new_tets[i][0]],
1111 positions[new_tets[i][1]],
1112 positions[new_tets[i][2]],
1113 positions[new_tets[i][3]]) > 0) {
1115 positions_double[new_tets[i][0]][0],
1116 positions_double[new_tets[i][0]][1],
1117 positions_double[new_tets[i][0]][2],
1118 positions_double[new_tets[i][1]][0],
1119 positions_double[new_tets[i][1]][1],
1120 positions_double[new_tets[i][1]][2],
1121 positions_double[new_tets[i][2]][0],
1122 positions_double[new_tets[i][2]][1],
1123 positions_double[new_tets[i][2]][2],
1124 positions_double[new_tets[i][3]][0],
1125 positions_double[new_tets[i][3]][1],
1126 positions_double[new_tets[i][3]][2],
1130 if (energy > new_energy_max) new_energy_max = energy;
1133 positions_double[new_tets[i][1]][0],
1134 positions_double[new_tets[i][1]][1],
1135 positions_double[new_tets[i][1]][2],
1136 positions_double[new_tets[i][0]][0],
1137 positions_double[new_tets[i][0]][1],
1138 positions_double[new_tets[i][0]][2],
1139 positions_double[new_tets[i][2]][0],
1140 positions_double[new_tets[i][2]][1],
1141 positions_double[new_tets[i][2]][2],
1142 positions_double[new_tets[i][3]][0],
1143 positions_double[new_tets[i][3]][1],
1144 positions_double[new_tets[i][3]][2],
1148 if (energy > new_energy_max) new_energy_max = energy;
1152 return new_energy_max;
1155 swap56->set_value_function(swap56_energy_check);
1156 swap56->add_invariant(std::make_shared<EdgeValenceInvariant>(mesh, 5));
1160 auto swap44 = std::make_shared<MinOperationSequence>(mesh);
1161 for (
int i = 0; i < 2; ++i) {
1162 auto swap = std::make_shared<TetEdgeSwap>(mesh, i);
1163 swap->collapse().add_invariant(invariant_separate_substructures);
1165 swap->collapse().set_new_attribute_strategy(
1167 CollapseBasicStrategy::CopyOther);
1168 swap->split().set_new_attribute_strategy(pt_attribute);
1169 swap->split().set_new_attribute_strategy(
1173 swap->collapse().set_new_attribute_strategy(
1177 swap->add_invariant(std::make_shared<Swap44EnergyBeforeInvariant>(
1182 for (
const auto& attr : pass_through_attributes) {
1183 swap->split().set_new_attribute_strategy(attr);
1184 swap->collapse().set_new_attribute_strategy(attr);
1187 swap44->add_operation(swap);
1190 auto swap44_energy_check = [&](int64_t idx,
const simplex::Simplex& t) ->
double {
1196 auto accessor = mesh.create_const_accessor(pt_attribute.
as<
Rational>());
1200 const Tuple e0 = t.tuple();
1201 const Tuple e1 = mesh.switch_tuple(e0,
PV);
1203 std::array<Tuple, 4> v;
1204 auto iter_tuple = e0;
1205 for (int64_t i = 0; i < 4; ++i) {
1206 v[i] = mesh.switch_tuples(iter_tuple, {
PE,
PV});
1207 iter_tuple = mesh.switch_tuples(iter_tuple, {
PF,
PT});
1210 if (iter_tuple != e0)
return 0;
1211 assert(iter_tuple == e0);
1213 std::array<Eigen::Vector3<Rational>, 6> positions = {
1214 {accessor.const_vector_attribute(v[(idx + 0) % 4]),
1215 accessor.const_vector_attribute(v[(idx + 1) % 4]),
1216 accessor.const_vector_attribute(v[(idx + 2) % 4]),
1217 accessor.const_vector_attribute(v[(idx + 3) % 4]),
1218 accessor.const_vector_attribute(e0),
1219 accessor.const_vector_attribute(e1)}};
1220 std::array<Eigen::Vector3d, 6> positions_double = {
1221 {positions[0].cast<
double>(),
1222 positions[1].cast<double>(),
1223 positions[2].cast<
double>(),
1224 positions[3].cast<double>(),
1225 positions[4].cast<
double>(),
1226 positions[5].cast<double>()}};
1228 std::array<std::array<int, 4>, 4> new_tets = {
1229 {{{0, 1, 2, 4}}, {{0, 2, 3, 4}}, {{0, 1, 2, 5}}, {{0, 2, 3, 5}}}};
1231 double new_energy_max = std::numeric_limits<double>::lowest();
1233 for (
int i = 0; i < 4; ++i) {
1235 positions[new_tets[i][0]],
1236 positions[new_tets[i][1]],
1237 positions[new_tets[i][2]],
1238 positions[new_tets[i][3]]) > 0) {
1240 positions_double[new_tets[i][0]][0],
1241 positions_double[new_tets[i][0]][1],
1242 positions_double[new_tets[i][0]][2],
1243 positions_double[new_tets[i][1]][0],
1244 positions_double[new_tets[i][1]][1],
1245 positions_double[new_tets[i][1]][2],
1246 positions_double[new_tets[i][2]][0],
1247 positions_double[new_tets[i][2]][1],
1248 positions_double[new_tets[i][2]][2],
1249 positions_double[new_tets[i][3]][0],
1250 positions_double[new_tets[i][3]][1],
1251 positions_double[new_tets[i][3]][2],
1254 if (energy > new_energy_max) new_energy_max = energy;
1257 positions_double[new_tets[i][1]][0],
1258 positions_double[new_tets[i][1]][1],
1259 positions_double[new_tets[i][1]][2],
1260 positions_double[new_tets[i][0]][0],
1261 positions_double[new_tets[i][0]][1],
1262 positions_double[new_tets[i][0]][2],
1263 positions_double[new_tets[i][2]][0],
1264 positions_double[new_tets[i][2]][1],
1265 positions_double[new_tets[i][2]][2],
1266 positions_double[new_tets[i][3]][0],
1267 positions_double[new_tets[i][3]][1],
1268 positions_double[new_tets[i][3]][2],
1271 if (energy > new_energy_max) new_energy_max = energy;
1275 return new_energy_max;
1278 swap44->set_value_function(swap44_energy_check);
1279 swap44->add_invariant(std::make_shared<EdgeValenceInvariant>(mesh, 4));
1282 auto swap32 = std::make_shared<TetEdgeSwap>(mesh, 0);
1283 swap32->add_invariant(std::make_shared<EdgeValenceInvariant>(mesh, 3));
1284 swap32->add_invariant(
1285 std::make_shared<Swap32EnergyBeforeInvariant>(mesh, pt_attribute.
as<
Rational>()));
1287 swap32->collapse().add_invariant(invariant_separate_substructures);
1289 swap32->collapse().set_new_attribute_strategy(
1291 CollapseBasicStrategy::CopyOther);
1292 swap32->split().set_new_attribute_strategy(pt_attribute);
1293 swap32->split().set_new_attribute_strategy(
1297 swap32->collapse().set_new_attribute_strategy(
1301 for (
const auto& attr : pass_through_attributes) {
1302 swap32->split().set_new_attribute_strategy(attr);
1303 swap32->collapse().set_new_attribute_strategy(attr);
1308 auto swap_all = std::make_shared<OrOperationSequence>(mesh);
1310 swap_all->add_operation(swap32);
1311 swap_all->add_operation(swap44);
1312 swap_all->add_operation(swap56);
1313 swap_all->add_transfer_strategy(tag_update);
1315 auto swap_then_round = std::make_shared<AndOperationSequence>(mesh);
1316 swap_then_round->add_operation(swap_all);
1318 swap_then_round->add_invariant(std::make_shared<InteriorEdgeInvariant>(mesh));
1319 swap_then_round->add_operation(rounding);
1322 return swap_then_round;
attribute::MeshAttributeHandle register_attribute(const std::string &name, PrimitiveType type, int64_t size, bool replace=false, T default_value=T(0))
attribute::MeshAttributeHandle get_attribute_handle(const std::string &name, const PrimitiveType ptype) const
attribute::Accessor< T, Mesh, D > create_accessor(const attribute::MeshAttributeHandle &handle)
PrimitiveType top_simplex_type() const
SchedulerStats run_operation_on_all(operations::Operation &op)
auto as() const -> const held_handle_type< held_type_from_primitive< T >()> &
constexpr wmtk::PrimitiveType PT
constexpr wmtk::PrimitiveType PF
std::vector< std::pair< std::shared_ptr< Mesh >, std::string > > wildmeshing(const WildMeshingOptions &option)
auto amips(const Eigen::MatrixBase< Derived > &B)
double Tet_AMIPS_energy(const std::array< double, 12 > &T)
double Tet_AMIPS_energy(const std::array< double, 12 > &T)
double Tri_AMIPS_energy(const std::array< double, 6 > &T)
attribute::MeshAttributeHandle set_matrix_attribute(const Mat &data, const std::string &name, const PrimitiveType &type, Mesh &mesh)
bool link_condition(const EdgeMesh &mesh, const Tuple &edge)
Check if the edge to collapse satisfying the link condition.
std::vector< Tuple > vertices(const Mesh &m, const Simplex &simplex)
int wmtk_orient3d(const Eigen::Ref< const Eigen::Vector3< Rational >> &p0, const Eigen::Ref< const Eigen::Vector3< Rational >> &p1, const Eigen::Ref< const Eigen::Vector3< Rational >> &p2, const Eigen::Ref< const Eigen::Vector3< Rational >> &p3)
constexpr PrimitiveType PV
RowVectors< int64_t, 4 > RowVectors4l
spdlog::logger & logger()
Retrieves the current logger.
Vector< int64_t, 4 > Vector4l
Vector< double, 3 > Vector3d
RowVectors< Rational, 3 > RowVectors3r
constexpr PrimitiveType PE
void run_tetwild_test(const std::string &path, const std::string &name, std::function< std::shared_ptr< Operation >(Mesh &, std::shared_ptr< Rounding > &, MeshAttributeHandle &, MeshAttributeHandle &, std::vector< attribute::MeshAttributeHandle > &, std::shared_ptr< wmtk::operations::SingleAttributeTransferStrategy< double, Rational >> &, std::shared_ptr< wmtk::operations::SingleAttributeTransferStrategy< double, Rational >> &, std::shared_ptr< wmtk::operations::SingleAttributeTransferStrategy< char, Rational >> &)> create_op)
const std::filesystem::path data_dir
TEST_CASE("wildmeshing", "[components][wildmeshing][.]")