80 using namespace simplex;
81 using namespace operations;
82 using namespace operations::tri_mesh;
83 using namespace operations::tet_mesh;
84 using namespace operations::composite;
85 using namespace function;
86 using namespace invariants;
88 std::vector<std::pair<std::shared_ptr<Mesh>, std::string>>
wildmeshing3d(
93 if (!mesh->is_connectivity_valid()) {
94 throw std::runtime_error(
"input mesh for wildmeshing connectivity invalid");
104 auto pt_double_attribute =
108 wmtk::utils::cast_attribute<wmtk::Rational>(
115 auto pt_attribute = mesh->get_attribute_handle<
Rational>(
118 wmtk::utils::cast_attribute<wmtk::Rational>(pt_double_attribute, pt_attribute);
120 mesh->delete_attribute(pt_double_attribute);
124 wmtk::logger().trace(
"Getting rational point accessor");
125 auto pt_accessor = mesh->create_accessor(pt_attribute.as<
Rational>());
127 wmtk::logger().trace(
"Computing bounding box diagonal");
130 Eigen::VectorXd bmin(mesh->top_cell_dimension());
131 bmin.setConstant(std::numeric_limits<double>::max());
132 Eigen::VectorXd bmax(mesh->top_cell_dimension());
133 bmax.setConstant(std::numeric_limits<double>::lowest());
137 const auto p = pt_accessor.vector_attribute(v).cast<
double>();
138 for (int64_t d = 0; d < bmax.size(); ++d) {
139 bmin[d] = std::min(bmin[d], p[d]);
140 bmax[d] = std::max(bmax[d], p[d]);
144 const double bbdiag = (bmax - bmin).norm();
146 wmtk::logger().info(
"bbox max {}, bbox min {}, diag {}", bmax, bmin, bbdiag);
156 wmtk::logger().info(
"target edge length: {}", target_edge_length);
160 auto amips_attribute =
161 mesh->register_attribute<
double>(
"wildmeshing_amips", mesh->top_simplex_type(), 1);
162 auto amips_accessor = mesh->create_accessor(amips_attribute.as<
double>());
164 auto compute_amips = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorXd {
165 assert(P.rows() == 2 || P.rows() == 3);
166 assert(P.cols() == P.rows() + 1);
169 assert(P.rows() == 2);
170 std::array<double, 6> pts;
171 for (
size_t i = 0; i < 3; ++i) {
172 for (
size_t j = 0; j < 2; ++j) {
173 pts[2 * i + j] = P(j, i).to_double();
177 return Eigen::VectorXd::Constant(1, a);
180 assert(P.rows() == 3);
181 std::array<double, 12> pts;
182 for (
size_t i = 0; i < 4; ++i) {
183 for (
size_t j = 0; j < 3; ++j) {
184 pts[3 * i + j] = P(j, i).to_double();
188 return Eigen::VectorXd::Constant(1, a);
192 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<double, Rational>>(
196 amips_update->run_on_all();
198 double max_amips = std::numeric_limits<double>::lowest();
199 double min_amips = std::numeric_limits<double>::max();
201 for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
203 double e = amips_accessor.scalar_attribute(t);
204 max_amips = std::max(max_amips, e);
205 min_amips = std::min(min_amips, e);
208 logger().info(
"Initial Max AMIPS Energy: {}, Min AMIPS Energy: {}", max_amips, min_amips);
214 auto sizing_field_scalar_attribute = mesh->register_attribute<
double>(
215 "sizing_field_scalar",
224 auto target_edge_length_attribute = mesh->register_attribute<
double>(
225 "wildmeshing_target_edge_length",
232 const double min_edge_length = [&]() ->
double {
238 for (
const auto& e : options.
envelopes) {
239 r = std::max(r, e.thickness);
250 auto edge_length_attribute =
252 auto edge_length_accessor = mesh->create_accessor(edge_length_attribute.as<
double>());
254 auto compute_edge_length = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorXd {
255 assert(P.cols() == 2);
256 assert(P.rows() == 2 || P.rows() == 3);
257 return Eigen::VectorXd::Constant(1, sqrt((P.col(0) - P.col(1)).squaredNorm().to_double()));
259 auto edge_length_update =
260 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<double, Rational>>(
261 edge_length_attribute,
263 compute_edge_length);
264 edge_length_update->run_on_all();
269 pass_through_attributes.push_back(edge_length_attribute);
270 pass_through_attributes.push_back(amips_attribute);
279 return -edge_length_accessor.scalar_attribute(s.tuple());
283 return edge_length_accessor.scalar_attribute(s.tuple());
293 auto envelope_invariant = std::make_shared<InvariantCollection>(*mesh);
294 std::vector<std::shared_ptr<AttributeTransferStrategyBase>> update_child_position,
295 update_parent_position;
296 std::vector<std::shared_ptr<Mesh>> envelopes;
297 std::vector<MeshConstrainPair> mesh_constraint_pairs;
299 std::vector<std::pair<std::shared_ptr<Mesh>, std::string>> multimesh_meshes;
306 for (
const auto& e : options.
envelopes) {
307 auto constrained_mesh = e.envelope_constrained_mesh;
308 auto geometry_mesh = e.envelope_geometry_mesh;
311 "wildmeshing3d: registered {} mesh as envelope constraints",
314 const bool geometry_has_double_pos =
316 const bool geometry_has_rational_pos =
318 assert(geometry_has_double_pos || geometry_has_rational_pos);
320 auto geometry_pt_handle = geometry_has_double_pos
321 ? geometry_mesh->get_attribute_handle<
double>(
322 e.geometry_position_name,
324 : geometry_mesh->get_attribute_handle<
Rational>(
325 e.geometry_position_name,
328 auto constrained_pt_handle = constrained_mesh->get_attribute_handle<
Rational>(
329 e.constrained_position_name,
332 multimesh_meshes.push_back(std::make_pair(constrained_mesh, e.envelope_name));
333 pass_through_attributes.emplace_back(constrained_pt_handle);
335 mesh_constraint_pairs.emplace_back(geometry_pt_handle, constrained_pt_handle);
337 envelope_invariant->add(std::make_shared<EnvelopeInvariant>(
339 e.thickness * bbdiag,
340 constrained_pt_handle));
343 constrained_pt_handle,
348 constrained_pt_handle));
359 auto inversion_invariant =
360 std::make_shared<SimplexInversionInvariant<Rational>>(*mesh, pt_attribute.as<
Rational>());
362 std::shared_ptr<function::PerSimplexFunction>
amips =
363 std::make_shared<AMIPS>(*mesh, pt_attribute);
366 auto function_invariant =
367 std::make_shared<MaxFunctionInvariant>(mesh->top_simplex_type(),
amips);
369 auto link_condition = std::make_shared<MultiMeshLinkConditionInvariant>(*mesh);
371 auto todo_larger = std::make_shared<TodoLargerInvariant>(
373 edge_length_attribute.as<
double>(),
374 target_edge_length_attribute.as<
double>(),
377 auto todo_smaller = std::make_shared<TodoSmallerInvariant>(
379 edge_length_attribute.as<
double>(),
380 target_edge_length_attribute.as<
double>(),
384 auto interior_edge = std::make_shared<InteriorEdgeInvariant>(*mesh);
387 for (
const auto& em : multimesh_meshes) {
388 interior_edge->add_boundary(*(em.first));
389 interior_face->add_boundary(*(em.first));
392 auto valence_3 = std::make_shared<EdgeValenceInvariant>(*mesh, 3);
393 auto valence_4 = std::make_shared<EdgeValenceInvariant>(*mesh, 4);
394 auto swap44_energy_before =
395 std::make_shared<Swap44EnergyBeforeInvariant>(*mesh, pt_attribute.as<
Rational>(), 0);
396 auto swap44_2_energy_before =
397 std::make_shared<Swap44EnergyBeforeInvariant>(*mesh, pt_attribute.as<
Rational>(), 1);
398 auto swap32_energy_before =
399 std::make_shared<Swap32EnergyBeforeInvariant>(*mesh, pt_attribute.as<
Rational>());
400 auto swap23_energy_before =
401 std::make_shared<Swap23EnergyBeforeInvariant>(*mesh, pt_attribute.as<
Rational>());
403 auto invariant_separate_substructures =
404 std::make_shared<invariants::SeparateSubstructuresInvariant>(*mesh);
409 auto visited_edge_flag =
412 auto update_flag_func = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorX<char> {
413 assert(P.cols() == 2);
414 assert(P.rows() == 2 || P.rows() == 3);
415 return Eigen::VectorX<char>::Constant(1,
char(1));
418 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<char, Rational>>(
426 auto visited_vertex_flag =
428 pass_through_attributes.push_back(visited_vertex_flag);
433 auto energy_filter_attribute =
436 auto energy_filter_accessor = mesh->create_accessor<
char>(energy_filter_attribute);
438 auto update_energy_filter_func = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorX<char> {
441 return Eigen::VectorX<char>::Constant(1,
char(1));
443 auto energy_filter_update =
444 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<char, Rational>>(
445 energy_filter_attribute,
447 update_energy_filter_func);
449 pass_through_attributes.push_back(energy_filter_attribute);
454 std::vector<std::shared_ptr<Operation>> ops;
455 std::vector<std::string> ops_name;
460 auto rounding_pt_attribute = mesh->get_attribute_handle_typed<
Rational>(
463 auto rounding = std::make_shared<Rounding>(*mesh, rounding_pt_attribute);
464 rounding->add_invariant(
465 std::make_shared<RoundedInvariant>(*mesh, pt_attribute.as<
Rational>(),
true));
466 rounding->add_invariant(inversion_invariant);
472 auto split = std::make_shared<EdgeSplit>(*mesh);
473 split->set_priority(long_edges_first);
475 split->add_invariant(todo_larger);
476 split->add_invariant(inversion_invariant);
478 split->set_new_attribute_strategy(pt_attribute);
479 split->set_new_attribute_strategy(sizing_field_scalar_attribute);
480 split->set_new_attribute_strategy(
484 split->set_new_attribute_strategy(
485 target_edge_length_attribute,
489 for (
const auto& attr : pass_through_attributes) {
490 split->set_new_attribute_strategy(
496 split->add_transfer_strategy(amips_update);
497 split->add_transfer_strategy(edge_length_update);
498 split->add_transfer_strategy(tag_update);
499 split->add_transfer_strategy(energy_filter_update);
503 auto split_then_round = std::make_shared<AndOperationSequence>(*mesh);
504 split_then_round->add_operation(split);
505 split_then_round->add_operation(rounding);
507 for (
auto& s : update_child_position) {
508 split_then_round->add_transfer_strategy(s);
512 auto split_unrounded = std::make_shared<EdgeSplit>(*mesh);
513 split_unrounded->set_priority(long_edges_first);
515 split_unrounded->add_invariant(todo_larger);
517 auto split_unrounded_transfer_strategy =
518 std::make_shared<SplitNewAttributeStrategy<Rational>>(pt_attribute);
519 split_unrounded_transfer_strategy->set_strategy(
520 [](
const Eigen::VectorX<Rational>& a,
const std::bitset<2>&) {
521 return std::array<Eigen::VectorX<Rational>, 2>{{a, a}};
523 split_unrounded_transfer_strategy->set_rib_strategy(
524 [](
const Eigen::VectorX<Rational>& p0_d,
525 const Eigen::VectorX<Rational>& p1_d,
526 const std::bitset<2>& bs) -> Eigen::VectorX<Rational> {
527 Eigen::VectorX<Rational> p0(p0_d.size());
528 Eigen::VectorX<Rational> p1(p1_d.size());
529 for (
int i = 0; i < p0_d.size(); ++i) {
533 if (bs[0] == bs[1]) {
534 return (p0 + p1) /
Rational(2,
false);
543 split_unrounded->set_new_attribute_strategy(pt_attribute, split_unrounded_transfer_strategy);
544 split_unrounded->set_new_attribute_strategy(sizing_field_scalar_attribute);
545 split_unrounded->set_new_attribute_strategy(
549 for (
const auto& attr : pass_through_attributes) {
550 split_unrounded->set_new_attribute_strategy(
555 split_unrounded->set_new_attribute_strategy(
556 target_edge_length_attribute,
560 split_unrounded->add_transfer_strategy(amips_update);
561 split_unrounded->add_transfer_strategy(edge_length_update);
562 split_unrounded->add_transfer_strategy(tag_update);
563 split_unrounded->add_transfer_strategy(energy_filter_update);
567 auto split_sequence = std::make_shared<OrOperationSequence>(*mesh);
568 split_sequence->add_operation(split_then_round);
569 split_sequence->add_operation(split_unrounded);
570 split_sequence->add_invariant(
571 std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<
char>()));
573 split_sequence->set_priority(long_edges_first);
576 ops.emplace_back(split_sequence);
577 ops_name.emplace_back(
"SPLIT");
579 ops.emplace_back(rounding);
580 ops_name.emplace_back(
"rounding");
587 auto clps_strat1 = std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
590 clps_strat1->set_strategy(CollapseBasicStrategy::CopyOther);
592 auto clps_strat2 = std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
595 clps_strat2->set_strategy(CollapseBasicStrategy::CopyTuple);
601 auto setup_collapse = [&](std::shared_ptr<EdgeCollapse>& collapse) {
602 collapse->add_invariant(invariant_separate_substructures);
603 collapse->add_invariant(std::make_shared<MultiMeshMapValidInvariant>(*mesh));
605 collapse->add_invariant(inversion_invariant);
607 collapse->add_invariant(envelope_invariant);
609 collapse->set_new_attribute_strategy(
613 collapse->add_transfer_strategy(tag_update);
614 collapse->add_transfer_strategy(energy_filter_update);
615 for (
const auto& attr : pass_through_attributes) {
616 collapse->set_new_attribute_strategy(
620 collapse->set_new_attribute_strategy(
621 target_edge_length_attribute,
627 collapse->add_transfer_strategy(amips_update);
628 collapse->add_transfer_strategy(edge_length_update);
631 for (
auto& s : update_child_position) {
632 collapse->add_transfer_strategy(s);
636 auto collapse1 = std::make_shared<EdgeCollapse>(*mesh);
637 collapse1->add_invariant(std::make_shared<CollapseEnergyBeforeInvariant>(
640 amips_attribute.as<
double>(),
643 collapse1->set_new_attribute_strategy(pt_attribute, clps_strat1);
644 collapse1->set_new_attribute_strategy(
645 sizing_field_scalar_attribute,
646 CollapseBasicStrategy::CopyOther);
647 setup_collapse(collapse1);
649 auto collapse2 = std::make_shared<EdgeCollapse>(*mesh);
650 collapse2->add_invariant(std::make_shared<CollapseEnergyBeforeInvariant>(
653 amips_attribute.as<
double>(),
656 collapse2->set_new_attribute_strategy(pt_attribute, clps_strat2);
657 collapse2->set_new_attribute_strategy(
658 sizing_field_scalar_attribute,
659 CollapseBasicStrategy::CopyTuple);
660 setup_collapse(collapse2);
662 auto collapse = std::make_shared<OrOperationSequence>(*mesh);
663 collapse->add_operation(collapse1);
664 collapse->add_operation(collapse2);
665 collapse->add_invariant(todo_smaller);
667 auto collapse_then_round = std::make_shared<AndOperationSequence>(*mesh);
668 collapse_then_round->add_operation(collapse);
669 collapse_then_round->add_operation(rounding);
671 collapse_then_round->set_priority(short_edges_first);
672 collapse_then_round->add_invariant(
673 std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<
char>()));
676 for (
auto& s : update_child_position) {
677 collapse_then_round->add_transfer_strategy(s);
681 ops.emplace_back(collapse_then_round);
682 ops_name.emplace_back(
"COLLAPSE");
684 ops.emplace_back(rounding);
685 ops_name.emplace_back(
"rounding");
692 auto swap56 = std::make_shared<MinOperationSequence>(*mesh);
693 for (
int i = 0; i < 5; ++i) {
694 auto swap = std::make_shared<TetEdgeSwap>(*mesh, i);
695 swap->collapse().add_invariant(invariant_separate_substructures);
697 swap->collapse().set_new_attribute_strategy(pt_attribute, CollapseBasicStrategy::CopyOther);
698 swap->collapse().set_new_attribute_strategy(
699 sizing_field_scalar_attribute,
700 CollapseBasicStrategy::CopyOther);
701 swap->split().set_new_attribute_strategy(pt_attribute);
702 swap->split().set_new_attribute_strategy(sizing_field_scalar_attribute);
705 swap->split().set_new_attribute_strategy(
709 swap->collapse().set_new_attribute_strategy(
713 swap->split().set_new_attribute_strategy(
714 target_edge_length_attribute,
717 swap->collapse().set_new_attribute_strategy(
718 target_edge_length_attribute,
722 std::make_shared<Swap56EnergyBeforeInvariant>(*mesh, pt_attribute.as<
Rational>(), i));
724 swap->add_transfer_strategy(amips_update);
727 swap->collapse().add_invariant(inversion_invariant);
731 for (
const auto& attr : pass_through_attributes) {
732 swap->split().set_new_attribute_strategy(
736 swap->collapse().set_new_attribute_strategy(
741 swap56->add_operation(swap);
744 auto swap56_energy_check = [&](int64_t idx,
const simplex::Simplex& t) ->
double {
750 auto accessor = mesh->create_const_accessor(pt_attribute.as<
Rational>());
752 const Tuple e0 = t.tuple();
753 const Tuple e1 = mesh->switch_tuple(e0,
PV);
755 std::array<Tuple, 5> v;
756 auto iter_tuple = e0;
757 for (int64_t i = 0; i < 5; ++i) {
758 v[i] = mesh->switch_tuples(iter_tuple, {
PE,
PV});
759 iter_tuple = mesh->switch_tuples(iter_tuple, {
PF,
PT});
761 if (iter_tuple != e0)
return 0;
762 assert(iter_tuple == e0);
766 std::array<Eigen::Vector3<Rational>, 7> positions = {
767 {accessor.const_vector_attribute(v[(idx + 0) % 5]),
768 accessor.const_vector_attribute(v[(idx + 1) % 5]),
769 accessor.const_vector_attribute(v[(idx + 2) % 5]),
770 accessor.const_vector_attribute(v[(idx + 3) % 5]),
771 accessor.const_vector_attribute(v[(idx + 4) % 5]),
772 accessor.const_vector_attribute(e0),
773 accessor.const_vector_attribute(e1)}};
775 std::array<Eigen::Vector3d, 7> positions_double = {
776 {positions[0].cast<
double>(),
777 positions[1].cast<double>(),
778 positions[2].cast<
double>(),
779 positions[3].cast<double>(),
780 positions[4].cast<
double>(),
781 positions[5].cast<double>(),
782 positions[6].cast<
double>()}};
784 std::array<std::array<int, 4>, 6> new_tets = {
792 double new_energy_max = std::numeric_limits<double>::lowest();
794 for (
int i = 0; i < 6; ++i) {
796 positions[new_tets[i][0]],
797 positions[new_tets[i][1]],
798 positions[new_tets[i][2]],
799 positions[new_tets[i][3]]) > 0) {
801 positions_double[new_tets[i][0]][0],
802 positions_double[new_tets[i][0]][1],
803 positions_double[new_tets[i][0]][2],
804 positions_double[new_tets[i][1]][0],
805 positions_double[new_tets[i][1]][1],
806 positions_double[new_tets[i][1]][2],
807 positions_double[new_tets[i][2]][0],
808 positions_double[new_tets[i][2]][1],
809 positions_double[new_tets[i][2]][2],
810 positions_double[new_tets[i][3]][0],
811 positions_double[new_tets[i][3]][1],
812 positions_double[new_tets[i][3]][2],
816 if (energy > new_energy_max) new_energy_max = energy;
819 positions_double[new_tets[i][1]][0],
820 positions_double[new_tets[i][1]][1],
821 positions_double[new_tets[i][1]][2],
822 positions_double[new_tets[i][0]][0],
823 positions_double[new_tets[i][0]][1],
824 positions_double[new_tets[i][0]][2],
825 positions_double[new_tets[i][2]][0],
826 positions_double[new_tets[i][2]][1],
827 positions_double[new_tets[i][2]][2],
828 positions_double[new_tets[i][3]][0],
829 positions_double[new_tets[i][3]][1],
830 positions_double[new_tets[i][3]][2],
834 if (energy > new_energy_max) new_energy_max = energy;
838 return new_energy_max;
841 swap56->set_value_function(swap56_energy_check);
842 swap56->add_invariant(std::make_shared<EdgeValenceInvariant>(*mesh, 5));
846 auto swap44 = std::make_shared<MinOperationSequence>(*mesh);
847 for (
int i = 0; i < 2; ++i) {
848 auto swap = std::make_shared<TetEdgeSwap>(*mesh, i);
849 swap->collapse().add_invariant(invariant_separate_substructures);
851 swap->collapse().set_new_attribute_strategy(pt_attribute, CollapseBasicStrategy::CopyOther);
852 swap->collapse().set_new_attribute_strategy(
853 sizing_field_scalar_attribute,
854 CollapseBasicStrategy::CopyOther);
855 swap->split().set_new_attribute_strategy(pt_attribute);
856 swap->split().set_new_attribute_strategy(sizing_field_scalar_attribute);
857 swap->split().set_new_attribute_strategy(
861 swap->collapse().set_new_attribute_strategy(
868 swap->split().set_new_attribute_strategy(
869 target_edge_length_attribute,
872 swap->collapse().set_new_attribute_strategy(
873 target_edge_length_attribute,
876 swap->add_transfer_strategy(amips_update);
879 std::make_shared<Swap44EnergyBeforeInvariant>(*mesh, pt_attribute.as<
Rational>(), i));
882 swap->collapse().add_invariant(inversion_invariant);
886 for (
const auto& attr : pass_through_attributes) {
887 swap->split().set_new_attribute_strategy(
891 swap->collapse().set_new_attribute_strategy(
896 swap44->add_operation(swap);
899 auto swap44_energy_check = [&](int64_t idx,
const simplex::Simplex& t) ->
double {
905 auto accessor = mesh->create_const_accessor(pt_attribute.as<
Rational>());
909 const Tuple e0 = t.tuple();
910 const Tuple e1 = mesh->switch_tuple(e0,
PV);
912 std::array<Tuple, 4> v;
913 auto iter_tuple = e0;
914 for (int64_t i = 0; i < 4; ++i) {
915 v[i] = mesh->switch_tuples(iter_tuple, {
PE,
PV});
916 iter_tuple = mesh->switch_tuples(iter_tuple, {
PF,
PT});
919 if (iter_tuple != e0)
return 0;
920 assert(iter_tuple == e0);
922 std::array<Eigen::Vector3<Rational>, 6> positions = {
923 {accessor.const_vector_attribute(v[(idx + 0) % 4]),
924 accessor.const_vector_attribute(v[(idx + 1) % 4]),
925 accessor.const_vector_attribute(v[(idx + 2) % 4]),
926 accessor.const_vector_attribute(v[(idx + 3) % 4]),
927 accessor.const_vector_attribute(e0),
928 accessor.const_vector_attribute(e1)}};
929 std::array<Eigen::Vector3d, 6> positions_double = {
930 {positions[0].cast<
double>(),
931 positions[1].cast<double>(),
932 positions[2].cast<
double>(),
933 positions[3].cast<double>(),
934 positions[4].cast<
double>(),
935 positions[5].cast<double>()}};
937 std::array<std::array<int, 4>, 4> new_tets = {
938 {{{0, 1, 2, 4}}, {{0, 2, 3, 4}}, {{0, 1, 2, 5}}, {{0, 2, 3, 5}}}};
940 double new_energy_max = std::numeric_limits<double>::lowest();
942 for (
int i = 0; i < 4; ++i) {
944 positions[new_tets[i][0]],
945 positions[new_tets[i][1]],
946 positions[new_tets[i][2]],
947 positions[new_tets[i][3]]) > 0) {
949 positions_double[new_tets[i][0]][0],
950 positions_double[new_tets[i][0]][1],
951 positions_double[new_tets[i][0]][2],
952 positions_double[new_tets[i][1]][0],
953 positions_double[new_tets[i][1]][1],
954 positions_double[new_tets[i][1]][2],
955 positions_double[new_tets[i][2]][0],
956 positions_double[new_tets[i][2]][1],
957 positions_double[new_tets[i][2]][2],
958 positions_double[new_tets[i][3]][0],
959 positions_double[new_tets[i][3]][1],
960 positions_double[new_tets[i][3]][2],
963 if (energy > new_energy_max) new_energy_max = energy;
966 positions_double[new_tets[i][1]][0],
967 positions_double[new_tets[i][1]][1],
968 positions_double[new_tets[i][1]][2],
969 positions_double[new_tets[i][0]][0],
970 positions_double[new_tets[i][0]][1],
971 positions_double[new_tets[i][0]][2],
972 positions_double[new_tets[i][2]][0],
973 positions_double[new_tets[i][2]][1],
974 positions_double[new_tets[i][2]][2],
975 positions_double[new_tets[i][3]][0],
976 positions_double[new_tets[i][3]][1],
977 positions_double[new_tets[i][3]][2],
980 if (energy > new_energy_max) new_energy_max = energy;
984 return new_energy_max;
987 swap44->set_value_function(swap44_energy_check);
988 swap44->add_invariant(std::make_shared<EdgeValenceInvariant>(*mesh, 4));
991 auto swap32 = std::make_shared<TetEdgeSwap>(*mesh, 0);
992 swap32->add_invariant(std::make_shared<EdgeValenceInvariant>(*mesh, 3));
993 swap32->add_invariant(
994 std::make_shared<Swap32EnergyBeforeInvariant>(*mesh, pt_attribute.as<
Rational>()));
996 swap32->collapse().add_invariant(invariant_separate_substructures);
998 swap32->collapse().set_new_attribute_strategy(pt_attribute, CollapseBasicStrategy::CopyOther);
999 swap32->collapse().set_new_attribute_strategy(
1000 sizing_field_scalar_attribute,
1001 CollapseBasicStrategy::CopyOther);
1003 swap32->split().set_new_attribute_strategy(pt_attribute);
1004 swap32->split().set_new_attribute_strategy(sizing_field_scalar_attribute);
1005 swap32->split().set_new_attribute_strategy(
1009 swap32->collapse().set_new_attribute_strategy(
1016 swap32->split().set_new_attribute_strategy(
1017 target_edge_length_attribute,
1020 swap32->collapse().set_new_attribute_strategy(
1021 target_edge_length_attribute,
1024 swap32->add_transfer_strategy(amips_update);
1027 swap32->collapse().add_invariant(inversion_invariant);
1030 for (
const auto& attr : pass_through_attributes) {
1031 swap32->split().set_new_attribute_strategy(
1035 swap32->collapse().set_new_attribute_strategy(
1042 auto swap_all = std::make_shared<OrOperationSequence>(*mesh);
1043 swap_all->add_operation(swap32);
1044 swap_all->add_operation(swap44);
1045 swap_all->add_operation(swap56);
1046 swap_all->add_transfer_strategy(tag_update);
1047 swap_all->add_transfer_strategy(energy_filter_update);
1049 auto swap_then_round = std::make_shared<AndOperationSequence>(*mesh);
1050 swap_then_round->add_operation(swap_all);
1051 swap_then_round->add_operation(rounding);
1052 swap_then_round->add_invariant(
1053 std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<
char>()));
1054 swap_then_round->add_invariant(std::make_shared<InteriorEdgeInvariant>(*mesh));
1055 swap_then_round->add_invariant(std::make_shared<NoChildMeshAttachingInvariant>(*mesh));
1057 swap_then_round->set_priority(long_edges_first);
1061 for (
auto& s : update_child_position) {
1062 swap_then_round->add_transfer_strategy(s);
1066 ops.push_back(swap_then_round);
1067 ops_name.push_back(
"EDGE SWAP");
1068 ops.emplace_back(rounding);
1069 ops_name.emplace_back(
"rounding");
1119 auto smoothing = std::make_shared<AMIPSOptimizationSmoothing>(*mesh, pt_attribute);
1120 smoothing->add_invariant(
1121 std::make_shared<RoundedInvariant>(*mesh, pt_attribute.as<
Rational>()));
1122 smoothing->add_invariant(inversion_invariant);
1123 for (
auto& s : update_child_position) {
1124 smoothing->add_transfer_strategy(s);
1137 auto proj_smoothing = std::make_shared<ProjectOperation>(smoothing, mesh_constraint_pairs);
1138 proj_smoothing->use_random_priority() =
true;
1140 proj_smoothing->add_invariant(envelope_invariant);
1141 proj_smoothing->add_invariant(inversion_invariant);
1142 proj_smoothing->add_invariant(
1143 std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<
char>()));
1145 proj_smoothing->add_transfer_strategy(amips_update);
1146 proj_smoothing->add_transfer_strategy(edge_length_update);
1147 for (
auto& s : update_parent_position) {
1148 proj_smoothing->add_transfer_strategy(s);
1151 for (
auto& s : update_child_position) {
1152 proj_smoothing->add_transfer_strategy(s);
1157 for (
int i = 0; i < 1; ++i) {
1159 ops.push_back(proj_smoothing);
1160 ops_name.push_back(
"SMOOTHING");
1163 ops.emplace_back(rounding);
1164 ops_name.emplace_back(
"rounding");
1182 int64_t success = 10;
1190 logger().info(
"----------------------- Preprocess Collapse -----------------------");
1193 for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1194 const auto vertices = mesh->orient_vertices(t);
1195 std::vector<Vector3r> pos;
1196 for (
int i = 0; i <
vertices.size(); ++i) {
1197 pos.push_back(pt_accessor.const_vector_attribute(
vertices[i]));
1203 logger().info(
"Executing collapse ...");
1210 "Executed {}, {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, "
1212 "preprocessing collapse",
1223 int64_t unrounded = 0;
1225 const auto p = pt_accessor.vector_attribute(v);
1226 for (int64_t d = 0; d < 3; ++d) {
1227 if (!p[d].is_rounded()) {
1234 logger().info(
"Mesh has {} unrounded vertices", unrounded);
1237 double max_energy = std::numeric_limits<double>::lowest();
1238 double min_energy = std::numeric_limits<double>::max();
1239 double avg_energy = 0;
1240 for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1242 double e = amips_accessor.scalar_attribute(t);
1243 max_energy = std::max(max_energy, e);
1244 min_energy = std::min(min_energy, e);
1248 avg_energy = avg_energy / mesh->get_all(mesh->top_simplex_type()).size();
1251 "Max AMIPS Energy: {}, Min AMIPS Energy: {}, Avg AMIPS Energy: {}",
1265 double old_max_energy = max_energy;
1266 double old_avg_energy = avg_energy;
1268 bool is_double =
false;
1269 for (int64_t i = 0; i < options.
max_passes; ++i) {
1270 logger().info(
"--------------------------- Pass {} ---------------------------", i);
1274 for (
auto& op : ops) {
1276 for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1277 const auto vertices = mesh->orient_vertices(t);
1278 std::vector<Vector3r> pos;
1279 for (
int i = 0; i <
vertices.size(); ++i) {
1280 pos.push_back(pt_accessor.const_vector_attribute(
vertices[i]));
1286 logger().info(
"Executing {} ...", ops_name[jj]);
1298 pass_stats += stats;
1300 "Executed {}, {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, "
1313 int64_t unrounded = 0;
1315 const auto p = pt_accessor.vector_attribute(v);
1316 for (int64_t d = 0; d < 3; ++d) {
1317 if (!p[d].is_rounded()) {
1324 logger().info(
"Mesh has {} unrounded vertices", unrounded);
1329 max_energy = std::numeric_limits<double>::lowest();
1330 min_energy = std::numeric_limits<double>::max();
1331 for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1333 double e = amips_accessor.scalar_attribute(t);
1334 max_energy = std::max(max_energy, e);
1335 min_energy = std::min(min_energy, e);
1339 avg_energy = avg_energy / mesh->get_all(mesh->top_simplex_type()).size();
1342 "Max AMIPS Energy: {}, Min AMIPS Energy: {}, Avg AMIPS Energy: {}",
1352 "Executed {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, executing: {}",
1370 assert(mesh->is_connectivity_valid());
1373 max_energy = std::numeric_limits<double>::lowest();
1374 min_energy = std::numeric_limits<double>::max();
1376 for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1378 double e = amips_accessor.scalar_attribute(t);
1379 max_energy = std::max(max_energy, e);
1380 min_energy = std::min(min_energy, e);
1384 avg_energy = avg_energy / mesh->get_all(mesh->top_simplex_type()).size();
1386 int64_t unrounded = 0;
1388 bool rational =
false;
1390 const auto p = pt_accessor.vector_attribute(v);
1391 for (int64_t d = 0; d < bmax.size(); ++d) {
1392 if (!p[d].is_rounded()) {
1400 is_double = !rational;
1403 logger().info(
"Mesh has {} unrounded vertices", unrounded);
1405 "Max AMIPS Energy: {}, Min AMIPS Energy: {}, Avg AMIPS Energy: {}",
1412 if (i > 0 && old_max_energy - max_energy < 5e-1 &&
1413 (old_avg_energy - avg_energy) / avg_energy < 0.1) {
1419 edge_length_attribute.as<
double>(),
1420 sizing_field_scalar_attribute.as<
double>(),
1421 amips_attribute.as<
double>(),
1422 target_edge_length_attribute.as<
double>(),
1423 visited_vertex_flag.as<
char>(),
1429 wmtk::logger().info(
"adjusting sizing field finished");
1457 energy_filter_accessor.scalar_attribute(v) = char(1);
1465 amips_attribute.as<
double>(),
1466 energy_filter_attribute.as<
char>(),
1467 visited_vertex_flag.as<
char>(),
1470 target_edge_length);
1475 if (energy_filter_accessor.scalar_attribute(e) ==
char(1) ||
1476 energy_filter_accessor.scalar_attribute(
1482 "{} edges are going to be executed out of {}",
1487 old_max_energy = max_energy;
1488 old_avg_energy = avg_energy;
1491 if (max_energy <= target_max_amips && is_double)
break;
1494 std::vector<std::pair<std::shared_ptr<Mesh>, std::string>> all_meshes;
1495 all_meshes.push_back(std::make_pair(mesh,
"main"));
1497 for (
const auto& p : multimesh_meshes) {
1498 all_meshes.push_back(p);
1515 const double stop_energy,
1516 const double current_max_energy,
1517 const double initial_target_edge_length,
1518 const double min_target_edge_length)
1522 std::cout <<
"in here" << std::endl;
1528 auto sizing_field_scalar_accessor = m.
create_accessor<
double>(sizing_field_scalar_handle);
1529 auto target_edge_length_accessor = m.
create_accessor<
double>(target_edge_length_handle);
1532 const double stop_filter_energy = stop_energy * 0.8;
1533 double filter_energy = std::max(current_max_energy / 100, stop_filter_energy);
1534 filter_energy = std::min(filter_energy, 100.0);
1536 const double recover_scalar = 1.5;
1537 const double refine_scalar = 0.5;
1538 const double min_refine_scalar = min_target_edge_length / initial_target_edge_length;
1541 int64_t v_cnt = vertices_all.size();
1543 std::vector<Vector3d> centroids;
1544 std::queue<Tuple> v_queue;
1550 if (energy_accessor.const_scalar_attribute(t) < filter_energy) {
1556 for (
int i = 0; i < 4; ++i) {
1557 c += coordinate_accessor.const_vector_attribute(
vertices[i]).cast<
double>();
1560 centroids.emplace_back(c / 4.0);
1564 "filter energy: {}, low quality tets num: {}",
1568 const double R = initial_target_edge_length * 1.8;
1570 for (
const auto& v : vertices_all) {
1571 visited_accessor.scalar_attribute(v) = char(0);
1575 auto get_nearest_dist = [&](
const Tuple& v) ->
double {
1576 Tuple nearest_tuple;
1577 double min_dist = std::numeric_limits<double>::max();
1578 const Vector3d v_pos = coordinate_accessor.const_vector_attribute(v).cast<
double>();
1579 for (
const auto& c_pos : centroids) {
1580 double dist = (c_pos - v_pos).norm();
1581 min_dist = std::min(min_dist, dist);
1586 while (!v_queue.empty()) {
1587 auto v = v_queue.front();
1590 if (visited_accessor.scalar_attribute(v) ==
char(1))
continue;
1591 visited_accessor.scalar_attribute(v) = char(1);
1593 double dist = std::max(0., get_nearest_dist(v));
1596 visited_accessor.scalar_attribute(v) = char(0);
1600 double scale_multiplier = std::min(
1602 dist / R * (1 - refine_scalar) + refine_scalar);
1604 auto new_scale = sizing_field_scalar_accessor.scalar_attribute(v) * scale_multiplier;
1605 if (new_scale > 1) {
1606 sizing_field_scalar_accessor.scalar_attribute(v) = 1;
1607 }
else if (new_scale < min_refine_scalar) {
1608 sizing_field_scalar_accessor.scalar_attribute(v) = min_refine_scalar;
1610 sizing_field_scalar_accessor.scalar_attribute(v) = new_scale;
1616 if (visited_accessor.scalar_attribute(v_one_ring) ==
char(1))
continue;
1617 v_queue.push(v_one_ring.tuple());
1622 for (
const auto& v : vertices_all) {
1623 if (visited_accessor.scalar_attribute(v) ==
char(1))
continue;
1624 auto new_scale = sizing_field_scalar_accessor.scalar_attribute(v) * 1.5;
1625 if (new_scale > 1) {
1626 sizing_field_scalar_accessor.scalar_attribute(v) = 1;
1627 }
else if (new_scale < min_refine_scalar) {
1628 sizing_field_scalar_accessor.scalar_attribute(v) = min_refine_scalar;
1630 sizing_field_scalar_accessor.scalar_attribute(v) = new_scale;
1636 target_edge_length_accessor.scalar_attribute(e) =
1637 initial_target_edge_length *
1638 (sizing_field_scalar_accessor.scalar_attribute(e) +
1639 sizing_field_scalar_accessor.scalar_attribute(
1651 const double stop_energy,
1652 const double current_max_energy,
1653 const double initial_target_edge_length)
1661 auto energy_filter_accessor = m.
create_accessor<
char>(energy_filter_handle);
1664 const double stop_filter_energy = stop_energy * 0.8;
1665 double filter_energy = std::max(current_max_energy / 100, stop_filter_energy);
1666 filter_energy = std::min(filter_energy, 100.0);
1670 for (
const auto& v : vertices_all) {
1671 visited_accessor.scalar_attribute(v) = char(0);
1672 energy_filter_accessor.scalar_attribute(v) = char(0);
1677 if (energy_accessor.const_scalar_attribute(t) < filter_energy) {
1683 energy_filter_accessor.scalar_attribute(v) = char(1);
1686 energy_filter_accessor.scalar_attribute(vv) = char(1);
1698 const double stop_energy,
1699 const double current_max_energy,
1700 const double initial_target_edge_length)
1707 auto energy_filter_accessor = m.
create_accessor<
char>(energy_filter_handle);
1710 const double stop_filter_energy = stop_energy * 0.8;
1711 double filter_energy = std::max(current_max_energy / 100, stop_filter_energy);
1712 filter_energy = std::min(filter_energy, 100.0);
1715 std::vector<Vector3d> centroids;
1716 std::queue<Tuple> v_queue;
1720 if (energy_accessor.const_scalar_attribute(t) < filter_energy) {
1726 for (
int i = 0; i < 4; ++i) {
1727 c += coordinate_accessor.const_vector_attribute(
vertices[i]).cast<
double>();
1730 centroids.emplace_back(c / 4.0);
1733 const double R = initial_target_edge_length * 1.8;
1735 for (
const auto& v : vertices_all) {
1736 visited_accessor.scalar_attribute(v) = char(0);
1737 energy_filter_accessor.scalar_attribute(v) = char(0);
1741 auto get_nearest_dist = [&](
const Tuple& v) ->
double {
1742 Tuple nearest_tuple;
1743 double min_dist = std::numeric_limits<double>::max();
1744 const Vector3d v_pos = coordinate_accessor.const_vector_attribute(v).cast<
double>();
1745 for (
const auto& c_pos : centroids) {
1746 double dist = (c_pos - v_pos).norm();
1747 min_dist = std::min(min_dist, dist);
1752 while (!v_queue.empty()) {
1753 auto v = v_queue.front();
1756 if (visited_accessor.scalar_attribute(v) ==
char(1))
continue;
1757 visited_accessor.scalar_attribute(v) = char(1);
1759 double dist = std::max(0., get_nearest_dist(v));
1762 visited_accessor.scalar_attribute(v) = char(0);
1766 energy_filter_accessor.scalar_attribute(v) = char(1);
1771 if (visited_accessor.scalar_attribute(v_one_ring) ==
char(1))
continue;
1772 v_queue.push(v_one_ring.tuple());
virtual std::vector< Tuple > orient_vertices(const Tuple &t) const =0
const attribute::Accessor< T, Mesh, D > create_const_accessor(const attribute::MeshAttributeHandle &handle) const
std::vector< Tuple > get_all(PrimitiveType type) const
Generate a vector of Tuples from global vertex/edge/triangle/tetrahedron index.
virtual Tuple switch_tuple(const Tuple &tuple, PrimitiveType type) const =0
switch the orientation of the Tuple of the given dimension
attribute::Accessor< T, Mesh, D > create_accessor(const attribute::MeshAttributeHandle &handle)
PrimitiveType top_simplex_type() const
SchedulerStats run_operation_on_all(operations::Operation &op)
void set_update_frequency(std::optional< size_t > &&freq={})
int64_t number_of_failed_operations() const
Returns the number of failed operations performed by the scheduler.
int64_t number_of_performed_operations() const
Returns the number of performed operations performed by the scheduler.
int64_t number_of_successful_operations() const
Returns the number of successful operations performed by the scheduler.
std::pair< attribute::MeshAttributeHandle, attribute::MeshAttributeHandle > MeshConstrainPair
static Simplex vertex(const Mesh &m, const Tuple &t)
constexpr wmtk::PrimitiveType PT
constexpr wmtk::PrimitiveType PF
void set_operation_energy_filter(Mesh &m, const TypedAttributeHandle< double > &coordinate_handle, const TypedAttributeHandle< double > &energy_handle, const TypedAttributeHandle< char > &energy_filter_handle, const TypedAttributeHandle< char > &visited_handle, const double stop_energy, const double current_max_energy, const double initial_target_edge_length)
std::vector< std::pair< std::shared_ptr< Mesh >, std::string > > wildmeshing3d(const WildMeshingOptions &options)
void adjust_sizing_field(Mesh &m, const TypedAttributeHandle< double > &coordinate_handle, const TypedAttributeHandle< double > &edge_length_handle, const TypedAttributeHandle< double > &sizing_field_scalar_handle, const TypedAttributeHandle< double > &energy_handle, const TypedAttributeHandle< double > &target_edge_length_handle, const TypedAttributeHandle< char > &visited_handle, const double stop_energy, const double current_max_energy, const double initial_target_edge_length, const double min_target_edge_length)
void set_operation_energy_filter_after_sizing_field(Mesh &m, const TypedAttributeHandle< Rational > &coordinate_handle, const TypedAttributeHandle< double > &energy_handle, const TypedAttributeHandle< char > &energy_filter_handle, const TypedAttributeHandle< char > &visited_handle, const double stop_energy, const double current_max_energy, const double initial_target_edge_length)
void write(const Mesh &mesh, const std::string &out_dir, const std::string &name, const std::string &vname, const int64_t index, const bool intermediate_output)
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)
void consolidate(Mesh &mesh)
std::shared_ptr< AttributeTransferStrategyBase > make_cast_attribute_transfer_strategy(const wmtk::attribute::MeshAttributeHandle &source, const wmtk::attribute::MeshAttributeHandle &target)
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)
SimplexCollection link(const Mesh &mesh, const simplex::Simplex &simplex, const bool sort_and_clean)
SimplexCollection k_ring(const Mesh &mesh, const Simplex &simplex, int64_t k)
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
spdlog::logger & logger()
Retrieves the current logger.
Vector< double, 3 > Vector3d
constexpr PrimitiveType PE
std::vector< EnvelopeOptions > envelopes
bool replace_double_coordinate
std::string intermediate_output_path
double target_edge_length
std::string intermediate_output_name
std::vector< attribute::MeshAttributeHandle > pass_through
std::shared_ptr< Mesh > input_mesh
std::string input_mesh_position
size_t scheduler_update_frequency