77 using namespace simplex;
78 using namespace operations;
79 using namespace operations::tri_mesh;
80 using namespace operations::tet_mesh;
81 using namespace operations::composite;
82 using namespace function;
83 using namespace invariants;
87 const std::shared_ptr<Mesh>& mesh,
88 const std::string& out_dir,
89 const std::string& name,
90 const std::string& vname,
92 const bool intermediate_output)
94 if (intermediate_output) {
97 const std::filesystem::path
data_dir =
"";
99 data_dir / (name +
"_" + std::to_string(index)),
106 mesh->serialize(writer);
109 const std::filesystem::path
data_dir =
"";
111 data_dir / (name +
"_" + std::to_string(index)),
118 mesh->serialize(writer);
121 const std::filesystem::path
data_dir =
"";
123 data_dir / (name +
"_" + std::to_string(index)),
130 mesh->serialize(writer);
145 const double stop_energy,
146 const double current_max_energy,
147 const double initial_target_edge_length,
148 const double min_target_edge_length)
152 std::cout <<
"in here" << std::endl;
158 auto sizing_field_scalar_accessor = m.
create_accessor<
double>(sizing_field_scalar_handle);
159 auto target_edge_length_accessor = m.
create_accessor<
double>(target_edge_length_handle);
162 const double stop_filter_energy = stop_energy * 0.8;
163 double filter_energy = std::max(current_max_energy / 100, stop_filter_energy);
164 filter_energy = std::min(filter_energy, 100.0);
166 const double recover_scalar = 1.5;
167 const double refine_scalar = 0.5;
168 const double min_refine_scalar = min_target_edge_length / initial_target_edge_length;
171 int64_t v_cnt = vertices_all.size();
173 std::vector<Vector3d> centroids;
174 std::queue<Tuple> v_queue;
180 if (energy_accessor.const_scalar_attribute(t) < filter_energy) {
186 for (
int i = 0; i < 4; ++i) {
187 c += coordinate_accessor.const_vector_attribute(
vertices[i]).cast<
double>();
190 centroids.emplace_back(c / 4.0);
194 "filter energy: {}, low quality tets num: {}",
198 const double R = initial_target_edge_length * 1.8;
200 for (
const auto& v : vertices_all) {
201 visited_accessor.scalar_attribute(v) = char(0);
205 auto get_nearest_dist = [&](
const Tuple& v) ->
double {
207 double min_dist = std::numeric_limits<double>::max();
208 const Vector3d v_pos = coordinate_accessor.const_vector_attribute(v).cast<
double>();
209 for (
const auto& c_pos : centroids) {
210 double dist = (c_pos - v_pos).norm();
211 min_dist = std::min(min_dist, dist);
216 while (!v_queue.empty()) {
217 auto v = v_queue.front();
220 if (visited_accessor.scalar_attribute(v) ==
char(1))
continue;
221 visited_accessor.scalar_attribute(v) = char(1);
223 double dist = std::max(0., get_nearest_dist(v));
226 visited_accessor.scalar_attribute(v) = char(0);
230 double scale_multiplier = std::min(
232 dist / R * (1 - refine_scalar) + refine_scalar);
234 auto new_scale = sizing_field_scalar_accessor.scalar_attribute(v) * scale_multiplier;
236 sizing_field_scalar_accessor.scalar_attribute(v) = 1;
237 }
else if (new_scale < min_refine_scalar) {
238 sizing_field_scalar_accessor.scalar_attribute(v) = min_refine_scalar;
240 sizing_field_scalar_accessor.scalar_attribute(v) = new_scale;
246 if (visited_accessor.scalar_attribute(v_one_ring) ==
char(1))
continue;
247 v_queue.push(v_one_ring.tuple());
252 for (
const auto& v : vertices_all) {
253 if (visited_accessor.scalar_attribute(v) ==
char(1))
continue;
254 auto new_scale = sizing_field_scalar_accessor.scalar_attribute(v) * 1.5;
256 sizing_field_scalar_accessor.scalar_attribute(v) = 1;
257 }
else if (new_scale < min_refine_scalar) {
258 sizing_field_scalar_accessor.scalar_attribute(v) = min_refine_scalar;
260 sizing_field_scalar_accessor.scalar_attribute(v) = new_scale;
266 target_edge_length_accessor.scalar_attribute(e) =
267 initial_target_edge_length *
268 (sizing_field_scalar_accessor.scalar_attribute(e) +
269 sizing_field_scalar_accessor.scalar_attribute(
281 const double stop_energy,
282 const double current_max_energy,
283 const double initial_target_edge_length)
291 auto energy_filter_accessor = m.
create_accessor<
char>(energy_filter_handle);
294 const double stop_filter_energy = stop_energy * 0.8;
295 double filter_energy = std::max(current_max_energy / 100, stop_filter_energy);
296 filter_energy = std::min(filter_energy, 100.0);
300 for (
const auto& v : vertices_all) {
301 visited_accessor.scalar_attribute(v) = char(0);
302 energy_filter_accessor.scalar_attribute(v) = char(0);
307 if (energy_accessor.const_scalar_attribute(t) < filter_energy) {
313 energy_filter_accessor.scalar_attribute(v) = char(1);
316 energy_filter_accessor.scalar_attribute(vv) = char(1);
328 const double stop_energy,
329 const double current_max_energy,
330 const double initial_target_edge_length)
337 auto energy_filter_accessor = m.
create_accessor<
char>(energy_filter_handle);
340 const double stop_filter_energy = stop_energy * 0.8;
341 double filter_energy = std::max(current_max_energy / 100, stop_filter_energy);
342 filter_energy = std::min(filter_energy, 100.0);
345 std::vector<Vector3d> centroids;
346 std::queue<Tuple> v_queue;
350 if (energy_accessor.const_scalar_attribute(t) < filter_energy) {
356 for (
int i = 0; i < 4; ++i) {
357 c += coordinate_accessor.const_vector_attribute(
vertices[i]).cast<
double>();
360 centroids.emplace_back(c / 4.0);
363 const double R = initial_target_edge_length * 1.8;
365 for (
const auto& v : vertices_all) {
366 visited_accessor.scalar_attribute(v) = char(0);
367 energy_filter_accessor.scalar_attribute(v) = char(0);
371 auto get_nearest_dist = [&](
const Tuple& v) ->
double {
373 double min_dist = std::numeric_limits<double>::max();
374 const Vector3d v_pos = coordinate_accessor.const_vector_attribute(v).cast<
double>();
375 for (
const auto& c_pos : centroids) {
376 double dist = (c_pos - v_pos).norm();
377 min_dist = std::min(min_dist, dist);
382 while (!v_queue.empty()) {
383 auto v = v_queue.front();
386 if (visited_accessor.scalar_attribute(v) ==
char(1))
continue;
387 visited_accessor.scalar_attribute(v) = char(1);
389 double dist = std::max(0., get_nearest_dist(v));
392 visited_accessor.scalar_attribute(v) = char(0);
396 energy_filter_accessor.scalar_attribute(v) = char(1);
401 if (visited_accessor.scalar_attribute(v_one_ring) ==
char(1))
continue;
402 v_queue.push(v_one_ring.tuple());
414 const std::filesystem::path& file = options.
input;
417 if (!mesh->is_connectivity_valid()) {
418 throw std::runtime_error(
"input mesh for wildmeshing connectivity invalid");
428 auto pt_double_attribute =
432 wmtk::utils::cast_attribute<wmtk::Rational>(
439 auto pt_attribute = mesh->get_attribute_handle<
Rational>(
442 wmtk::utils::cast_attribute<wmtk::Rational>(pt_double_attribute, pt_attribute);
444 mesh->delete_attribute(pt_double_attribute);
448 wmtk::logger().trace(
"Getting rational point accessor");
449 auto pt_accessor = mesh->create_accessor(pt_attribute.as<
Rational>());
451 wmtk::logger().trace(
"Computing bounding box diagonal");
454 Eigen::VectorXd bmin(mesh->top_cell_dimension());
455 bmin.setConstant(std::numeric_limits<double>::max());
456 Eigen::VectorXd bmax(mesh->top_cell_dimension());
457 bmax.setConstant(std::numeric_limits<double>::lowest());
461 const auto p = pt_accessor.vector_attribute(v).cast<
double>();
462 for (int64_t d = 0; d < bmax.size(); ++d) {
463 bmin[d] = std::min(bmin[d], p[d]);
464 bmax[d] = std::max(bmax[d], p[d]);
469 const double bbdiag = (bmax - bmin).norm();
471 wmtk::logger().info(
"bbox max {}, bbox min {}, diag {}", bmax, bmin, bbdiag);
481 wmtk::logger().info(
"target edge length: {}", target_edge_length);
485 auto amips_attribute =
486 mesh->register_attribute<
double>(
"wildmeshing_amips", mesh->top_simplex_type(), 1);
487 auto amips_accessor = mesh->create_accessor(amips_attribute.as<
double>());
489 auto compute_amips = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorXd {
490 assert(P.rows() == 2 || P.rows() == 3);
491 assert(P.cols() == P.rows() + 1);
494 assert(P.rows() == 2);
495 std::array<double, 6> pts;
496 for (
size_t i = 0; i < 3; ++i) {
497 for (
size_t j = 0; j < 2; ++j) {
498 pts[2 * i + j] = P(j, i).to_double();
502 return Eigen::VectorXd::Constant(1, a);
505 assert(P.rows() == 3);
506 std::array<double, 12> pts;
507 for (
size_t i = 0; i < 4; ++i) {
508 for (
size_t j = 0; j < 3; ++j) {
509 pts[3 * i + j] = P(j, i).to_double();
513 return Eigen::VectorXd::Constant(1, a);
517 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<double, Rational>>(
521 amips_update->run_on_all();
523 double max_amips = std::numeric_limits<double>::lowest();
524 double min_amips = std::numeric_limits<double>::max();
526 for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
528 double e = amips_accessor.scalar_attribute(t);
529 max_amips = std::max(max_amips, e);
530 min_amips = std::min(min_amips, e);
533 logger().info(
"Initial Max AMIPS Energy: {}, Min AMIPS Energy: {}", max_amips, min_amips);
539 auto sizing_field_scalar_attribute = mesh->register_attribute<
double>(
540 "sizing_field_scalar",
549 auto target_edge_length_attribute = mesh->register_attribute<
double>(
550 "wildmeshing_target_edge_length",
557 const double min_edge_length = [&]() ->
double {
563 for (
const auto& e : options.
envelopes) {
564 r = std::max(r, e.thickness);
637 auto edge_length_attribute =
639 auto edge_length_accessor = mesh->create_accessor(edge_length_attribute.as<
double>());
641 auto compute_edge_length = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorXd {
642 assert(P.cols() == 2);
643 assert(P.rows() == 2 || P.rows() == 3);
644 return Eigen::VectorXd::Constant(1, sqrt((P.col(0) - P.col(1)).squaredNorm().to_double()));
646 auto edge_length_update =
647 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<double, Rational>>(
648 edge_length_attribute,
650 compute_edge_length);
651 edge_length_update->run_on_all();
656 pass_through_attributes.push_back(edge_length_attribute);
657 pass_through_attributes.push_back(amips_attribute);
666 return -edge_length_accessor.scalar_attribute(s.tuple());
670 return edge_length_accessor.scalar_attribute(s.tuple());
680 auto envelope_invariant = std::make_shared<InvariantCollection>(*mesh);
681 std::vector<std::shared_ptr<AttributeTransferStrategyBase>> update_child_position,
682 update_parent_position;
683 std::vector<std::shared_ptr<Mesh>> envelopes;
684 std::vector<MeshConstrainPair> mesh_constraint_pairs;
686 std::vector<std::shared_ptr<Mesh>> multimesh_meshes;
693 for (
const auto& v : options.
envelopes) {
694 auto envelope = cache.
read_mesh(v.geometry.mesh);
695 if (envelope ==
nullptr) {
696 wmtk::logger().info(
"TetWild: no {} mesh for this mesh", v.geometry.mesh);
699 wmtk::logger().info(
"TetWild: registered {} mesh", v.geometry.mesh);
701 envelopes.emplace_back(envelope);
704 "TetWild: getting constrained position {} from {}",
705 v.constrained_position,
708 multimesh_meshes.push_back(constrained.front().mesh().shared_from_this());
709 assert(constrained.size() == 1);
710 pass_through_attributes.emplace_back(constrained.front());
713 "TetWild: Constructing envelope position handle {} == input = {}",
715 v.geometry.mesh ==
"input");
717 const bool has_double_pos =
719 const bool has_rational_pos =
721 assert(has_double_pos || has_rational_pos);
722 assert(!(v.geometry.mesh ==
"input") || has_double_pos);
724 auto envelope_position_handle =
727 : envelope->get_attribute_handle<
Rational>(
733 mesh_constraint_pairs.emplace_back(envelope_position_handle, constrained.front());
735 envelope_invariant->add(std::make_shared<EnvelopeInvariant>(
736 envelope_position_handle,
737 v.thickness * bbdiag,
738 constrained.front()));
746 constrained.front()));
757 auto inversion_invariant =
758 std::make_shared<SimplexInversionInvariant<Rational>>(*mesh, pt_attribute.as<
Rational>());
760 std::shared_ptr<function::PerSimplexFunction>
amips =
761 std::make_shared<AMIPS>(*mesh, pt_attribute);
764 auto function_invariant =
765 std::make_shared<MaxFunctionInvariant>(mesh->top_simplex_type(),
amips);
767 auto link_condition = std::make_shared<MultiMeshLinkConditionInvariant>(*mesh);
769 auto todo_larger = std::make_shared<TodoLargerInvariant>(
771 edge_length_attribute.as<
double>(),
772 target_edge_length_attribute.as<
double>(),
775 auto todo_smaller = std::make_shared<TodoSmallerInvariant>(
777 edge_length_attribute.as<
double>(),
778 target_edge_length_attribute.as<
double>(),
782 auto interior_edge = std::make_shared<InteriorEdgeInvariant>(*mesh);
785 for (
const auto& em : multimesh_meshes) {
786 interior_edge->add_boundary(*em);
787 interior_face->add_boundary(*em);
790 auto valence_3 = std::make_shared<EdgeValenceInvariant>(*mesh, 3);
791 auto valence_4 = std::make_shared<EdgeValenceInvariant>(*mesh, 4);
792 auto swap44_energy_before =
793 std::make_shared<Swap44EnergyBeforeInvariant>(*mesh, pt_attribute.as<
Rational>(), 0);
794 auto swap44_2_energy_before =
795 std::make_shared<Swap44EnergyBeforeInvariant>(*mesh, pt_attribute.as<
Rational>(), 1);
796 auto swap32_energy_before =
797 std::make_shared<Swap32EnergyBeforeInvariant>(*mesh, pt_attribute.as<
Rational>());
798 auto swap23_energy_before =
799 std::make_shared<Swap23EnergyBeforeInvariant>(*mesh, pt_attribute.as<
Rational>());
801 auto invariant_separate_substructures =
802 std::make_shared<invariants::SeparateSubstructuresInvariant>(*mesh);
807 auto visited_edge_flag =
810 auto update_flag_func = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorX<char> {
811 assert(P.cols() == 2);
812 assert(P.rows() == 2 || P.rows() == 3);
813 return Eigen::VectorX<char>::Constant(1,
char(1));
816 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<char, Rational>>(
824 auto visited_vertex_flag =
826 pass_through_attributes.push_back(visited_vertex_flag);
831 auto energy_filter_attribute =
834 auto energy_filter_accessor = mesh->create_accessor<
char>(energy_filter_attribute);
836 auto update_energy_filter_func = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorX<char> {
839 return Eigen::VectorX<char>::Constant(1,
char(1));
841 auto energy_filter_update =
842 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<char, Rational>>(
843 energy_filter_attribute,
845 update_energy_filter_func);
847 pass_through_attributes.push_back(energy_filter_attribute);
860 std::vector<std::shared_ptr<Operation>> ops;
861 std::vector<std::string> ops_name;
866 auto rounding_pt_attribute = mesh->get_attribute_handle_typed<
Rational>(
869 auto rounding = std::make_shared<Rounding>(*mesh, rounding_pt_attribute);
870 rounding->add_invariant(
871 std::make_shared<RoundedInvariant>(*mesh, pt_attribute.as<
Rational>(),
true));
872 rounding->add_invariant(inversion_invariant);
878 auto split = std::make_shared<EdgeSplit>(*mesh);
879 split->set_priority(long_edges_first);
881 split->add_invariant(todo_larger);
882 split->add_invariant(inversion_invariant);
884 split->set_new_attribute_strategy(pt_attribute);
885 split->set_new_attribute_strategy(sizing_field_scalar_attribute);
886 split->set_new_attribute_strategy(
890 split->set_new_attribute_strategy(
891 target_edge_length_attribute,
895 for (
const auto& attr : pass_through_attributes) {
896 split->set_new_attribute_strategy(
902 split->add_transfer_strategy(amips_update);
903 split->add_transfer_strategy(edge_length_update);
904 split->add_transfer_strategy(tag_update);
905 split->add_transfer_strategy(energy_filter_update);
909 auto split_then_round = std::make_shared<AndOperationSequence>(*mesh);
910 split_then_round->add_operation(split);
911 split_then_round->add_operation(rounding);
913 for (
auto& s : update_child_position) {
914 split_then_round->add_transfer_strategy(s);
918 auto split_unrounded = std::make_shared<EdgeSplit>(*mesh);
919 split_unrounded->set_priority(long_edges_first);
921 split_unrounded->add_invariant(todo_larger);
923 auto split_unrounded_transfer_strategy =
924 std::make_shared<SplitNewAttributeStrategy<Rational>>(pt_attribute);
925 split_unrounded_transfer_strategy->set_strategy(
926 [](
const Eigen::VectorX<Rational>& a,
const std::bitset<2>&) {
927 return std::array<Eigen::VectorX<Rational>, 2>{{a, a}};
929 split_unrounded_transfer_strategy->set_rib_strategy(
930 [](
const Eigen::VectorX<Rational>& p0_d,
931 const Eigen::VectorX<Rational>& p1_d,
932 const std::bitset<2>& bs) -> Eigen::VectorX<Rational> {
933 Eigen::VectorX<Rational> p0(p0_d.size());
934 Eigen::VectorX<Rational> p1(p1_d.size());
935 for (
int i = 0; i < p0_d.size(); ++i) {
939 if (bs[0] == bs[1]) {
940 return (p0 + p1) /
Rational(2,
false);
949 split_unrounded->set_new_attribute_strategy(pt_attribute, split_unrounded_transfer_strategy);
950 split_unrounded->set_new_attribute_strategy(sizing_field_scalar_attribute);
951 split_unrounded->set_new_attribute_strategy(
955 for (
const auto& attr : pass_through_attributes) {
956 split_unrounded->set_new_attribute_strategy(
961 split_unrounded->set_new_attribute_strategy(
962 target_edge_length_attribute,
966 split_unrounded->add_transfer_strategy(amips_update);
967 split_unrounded->add_transfer_strategy(edge_length_update);
968 split_unrounded->add_transfer_strategy(tag_update);
969 split_unrounded->add_transfer_strategy(energy_filter_update);
973 auto split_sequence = std::make_shared<OrOperationSequence>(*mesh);
974 split_sequence->add_operation(split_then_round);
975 split_sequence->add_operation(split_unrounded);
976 split_sequence->add_invariant(
977 std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<
char>()));
979 split_sequence->set_priority(long_edges_first);
981 ops.emplace_back(split_sequence);
982 ops_name.emplace_back(
"SPLIT");
984 ops.emplace_back(rounding);
985 ops_name.emplace_back(
"rounding");
991 auto clps_strat1 = std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
994 clps_strat1->set_strategy(CollapseBasicStrategy::CopyOther);
996 auto clps_strat2 = std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
999 clps_strat2->set_strategy(CollapseBasicStrategy::CopyTuple);
1005 auto setup_collapse = [&](std::shared_ptr<EdgeCollapse>& collapse) {
1006 collapse->add_invariant(invariant_separate_substructures);
1008 collapse->add_invariant(inversion_invariant);
1010 collapse->add_invariant(envelope_invariant);
1012 collapse->set_new_attribute_strategy(
1016 collapse->add_transfer_strategy(tag_update);
1017 collapse->add_transfer_strategy(energy_filter_update);
1018 for (
const auto& attr : pass_through_attributes) {
1019 collapse->set_new_attribute_strategy(
1023 collapse->set_new_attribute_strategy(
1024 target_edge_length_attribute,
1030 collapse->add_transfer_strategy(amips_update);
1031 collapse->add_transfer_strategy(edge_length_update);
1034 for (
auto& s : update_child_position) {
1035 collapse->add_transfer_strategy(s);
1039 auto collapse1 = std::make_shared<EdgeCollapse>(*mesh);
1040 collapse1->add_invariant(std::make_shared<CollapseEnergyBeforeInvariant>(
1043 amips_attribute.as<
double>(),
1046 collapse1->set_new_attribute_strategy(pt_attribute, clps_strat1);
1047 collapse1->set_new_attribute_strategy(sizing_field_scalar_attribute, clps_strat1);
1048 setup_collapse(collapse1);
1050 auto collapse2 = std::make_shared<EdgeCollapse>(*mesh);
1051 collapse2->add_invariant(std::make_shared<CollapseEnergyBeforeInvariant>(
1054 amips_attribute.as<
double>(),
1057 collapse2->set_new_attribute_strategy(pt_attribute, clps_strat2);
1058 collapse2->set_new_attribute_strategy(sizing_field_scalar_attribute, clps_strat2);
1059 setup_collapse(collapse2);
1061 auto collapse = std::make_shared<OrOperationSequence>(*mesh);
1062 collapse->add_operation(collapse1);
1063 collapse->add_operation(collapse2);
1064 collapse->add_invariant(todo_smaller);
1066 auto collapse_then_round = std::make_shared<AndOperationSequence>(*mesh);
1067 collapse_then_round->add_operation(collapse);
1068 collapse_then_round->add_operation(rounding);
1070 collapse_then_round->set_priority(short_edges_first);
1071 collapse_then_round->add_invariant(
1072 std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<
char>()));
1075 for (
auto& s : update_child_position) {
1076 collapse_then_round->add_transfer_strategy(s);
1079 ops.emplace_back(collapse_then_round);
1080 ops_name.emplace_back(
"COLLAPSE");
1082 ops.emplace_back(rounding);
1083 ops_name.emplace_back(
"rounding");
1092 std::shared_ptr<Invariant> simplex_invariant,
1093 bool is_edge =
true) {
1107 collapse.add_invariant(invariant_separate_substructures);
1111 collapse.set_new_attribute_strategy(pt_attribute, CollapseBasicStrategy::CopyOther);
1112 split.set_new_attribute_strategy(pt_attribute);
1114 split.set_new_attribute_strategy(
1115 target_edge_length_attribute,
1118 collapse.set_new_attribute_strategy(
1119 target_edge_length_attribute,
1129 for (
const auto& attr : pass_through_attributes) {
1130 split.set_new_attribute_strategy(
1134 collapse.set_new_attribute_strategy(
1142 auto swap = std::make_shared<TriEdgeSwap>(*mesh);
1143 setup_swap(*swap, swap->collapse(), swap->split(), interior_edge);
1145 ops.push_back(swap);
1146 ops_name.push_back(
"swap");
1148 ops.emplace_back(rounding);
1149 ops_name.emplace_back(
"rounding");
1152 auto swap56 = std::make_shared<MinOperationSequence>(*mesh);
1153 for (
int i = 0; i < 5; ++i) {
1154 auto swap = std::make_shared<TetEdgeSwap>(*mesh, i);
1155 swap->collapse().add_invariant(invariant_separate_substructures);
1157 swap->collapse().set_new_attribute_strategy(
1159 CollapseBasicStrategy::CopyOther);
1160 swap->collapse().set_new_attribute_strategy(
1161 sizing_field_scalar_attribute,
1162 CollapseBasicStrategy::CopyOther);
1163 swap->split().set_new_attribute_strategy(pt_attribute);
1164 swap->split().set_new_attribute_strategy(sizing_field_scalar_attribute);
1167 swap->split().set_new_attribute_strategy(
1171 swap->collapse().set_new_attribute_strategy(
1175 swap->split().set_new_attribute_strategy(
1176 target_edge_length_attribute,
1179 swap->collapse().set_new_attribute_strategy(
1180 target_edge_length_attribute,
1183 swap->add_invariant(std::make_shared<Swap56EnergyBeforeInvariant>(
1188 swap->add_transfer_strategy(amips_update);
1191 swap->collapse().add_invariant(inversion_invariant);
1195 for (
const auto& attr : pass_through_attributes) {
1196 swap->split().set_new_attribute_strategy(
1200 swap->collapse().set_new_attribute_strategy(
1205 swap56->add_operation(swap);
1208 auto swap56_energy_check = [&](int64_t idx,
const simplex::Simplex& t) ->
double {
1214 auto accessor = mesh->create_const_accessor(pt_attribute.as<
Rational>());
1216 const Tuple e0 = t.tuple();
1217 const Tuple e1 = mesh->switch_tuple(e0,
PV);
1219 std::array<Tuple, 5> v;
1220 auto iter_tuple = e0;
1221 for (int64_t i = 0; i < 5; ++i) {
1222 v[i] = mesh->switch_tuples(iter_tuple, {
PE,
PV});
1223 iter_tuple = mesh->switch_tuples(iter_tuple, {
PF,
PT});
1225 if (iter_tuple != e0)
return 0;
1226 assert(iter_tuple == e0);
1230 std::array<Eigen::Vector3<Rational>, 7> positions = {
1231 {accessor.const_vector_attribute(v[(idx + 0) % 5]),
1232 accessor.const_vector_attribute(v[(idx + 1) % 5]),
1233 accessor.const_vector_attribute(v[(idx + 2) % 5]),
1234 accessor.const_vector_attribute(v[(idx + 3) % 5]),
1235 accessor.const_vector_attribute(v[(idx + 4) % 5]),
1236 accessor.const_vector_attribute(e0),
1237 accessor.const_vector_attribute(e1)}};
1239 std::array<Eigen::Vector3d, 7> positions_double = {
1240 {positions[0].cast<
double>(),
1241 positions[1].cast<double>(),
1242 positions[2].cast<
double>(),
1243 positions[3].cast<double>(),
1244 positions[4].cast<
double>(),
1245 positions[5].cast<double>(),
1246 positions[6].cast<
double>()}};
1248 std::array<std::array<int, 4>, 6> new_tets = {
1256 double new_energy_max = std::numeric_limits<double>::lowest();
1258 for (
int i = 0; i < 6; ++i) {
1260 positions[new_tets[i][0]],
1261 positions[new_tets[i][1]],
1262 positions[new_tets[i][2]],
1263 positions[new_tets[i][3]]) > 0) {
1265 positions_double[new_tets[i][0]][0],
1266 positions_double[new_tets[i][0]][1],
1267 positions_double[new_tets[i][0]][2],
1268 positions_double[new_tets[i][1]][0],
1269 positions_double[new_tets[i][1]][1],
1270 positions_double[new_tets[i][1]][2],
1271 positions_double[new_tets[i][2]][0],
1272 positions_double[new_tets[i][2]][1],
1273 positions_double[new_tets[i][2]][2],
1274 positions_double[new_tets[i][3]][0],
1275 positions_double[new_tets[i][3]][1],
1276 positions_double[new_tets[i][3]][2],
1280 if (energy > new_energy_max) new_energy_max = energy;
1283 positions_double[new_tets[i][1]][0],
1284 positions_double[new_tets[i][1]][1],
1285 positions_double[new_tets[i][1]][2],
1286 positions_double[new_tets[i][0]][0],
1287 positions_double[new_tets[i][0]][1],
1288 positions_double[new_tets[i][0]][2],
1289 positions_double[new_tets[i][2]][0],
1290 positions_double[new_tets[i][2]][1],
1291 positions_double[new_tets[i][2]][2],
1292 positions_double[new_tets[i][3]][0],
1293 positions_double[new_tets[i][3]][1],
1294 positions_double[new_tets[i][3]][2],
1298 if (energy > new_energy_max) new_energy_max = energy;
1302 return new_energy_max;
1305 swap56->set_value_function(swap56_energy_check);
1306 swap56->add_invariant(std::make_shared<EdgeValenceInvariant>(*mesh, 5));
1310 auto swap44 = std::make_shared<MinOperationSequence>(*mesh);
1311 for (
int i = 0; i < 2; ++i) {
1312 auto swap = std::make_shared<TetEdgeSwap>(*mesh, i);
1313 swap->collapse().add_invariant(invariant_separate_substructures);
1315 swap->collapse().set_new_attribute_strategy(
1317 CollapseBasicStrategy::CopyOther);
1318 swap->collapse().set_new_attribute_strategy(
1319 sizing_field_scalar_attribute,
1320 CollapseBasicStrategy::CopyOther);
1321 swap->split().set_new_attribute_strategy(pt_attribute);
1322 swap->split().set_new_attribute_strategy(sizing_field_scalar_attribute);
1323 swap->split().set_new_attribute_strategy(
1327 swap->collapse().set_new_attribute_strategy(
1334 swap->split().set_new_attribute_strategy(
1335 target_edge_length_attribute,
1338 swap->collapse().set_new_attribute_strategy(
1339 target_edge_length_attribute,
1342 swap->add_transfer_strategy(amips_update);
1344 swap->add_invariant(std::make_shared<Swap44EnergyBeforeInvariant>(
1350 swap->collapse().add_invariant(inversion_invariant);
1354 for (
const auto& attr : pass_through_attributes) {
1355 swap->split().set_new_attribute_strategy(
1359 swap->collapse().set_new_attribute_strategy(
1364 swap44->add_operation(swap);
1367 auto swap44_energy_check = [&](int64_t idx,
const simplex::Simplex& t) ->
double {
1373 auto accessor = mesh->create_const_accessor(pt_attribute.as<
Rational>());
1377 const Tuple e0 = t.tuple();
1378 const Tuple e1 = mesh->switch_tuple(e0,
PV);
1380 std::array<Tuple, 4> v;
1381 auto iter_tuple = e0;
1382 for (int64_t i = 0; i < 4; ++i) {
1383 v[i] = mesh->switch_tuples(iter_tuple, {
PE,
PV});
1384 iter_tuple = mesh->switch_tuples(iter_tuple, {
PF,
PT});
1387 if (iter_tuple != e0)
return 0;
1388 assert(iter_tuple == e0);
1390 std::array<Eigen::Vector3<Rational>, 6> positions = {
1391 {accessor.const_vector_attribute(v[(idx + 0) % 4]),
1392 accessor.const_vector_attribute(v[(idx + 1) % 4]),
1393 accessor.const_vector_attribute(v[(idx + 2) % 4]),
1394 accessor.const_vector_attribute(v[(idx + 3) % 4]),
1395 accessor.const_vector_attribute(e0),
1396 accessor.const_vector_attribute(e1)}};
1397 std::array<Eigen::Vector3d, 6> positions_double = {
1398 {positions[0].cast<
double>(),
1399 positions[1].cast<double>(),
1400 positions[2].cast<
double>(),
1401 positions[3].cast<double>(),
1402 positions[4].cast<
double>(),
1403 positions[5].cast<double>()}};
1405 std::array<std::array<int, 4>, 4> new_tets = {
1406 {{{0, 1, 2, 4}}, {{0, 2, 3, 4}}, {{0, 1, 2, 5}}, {{0, 2, 3, 5}}}};
1408 double new_energy_max = std::numeric_limits<double>::lowest();
1410 for (
int i = 0; i < 4; ++i) {
1412 positions[new_tets[i][0]],
1413 positions[new_tets[i][1]],
1414 positions[new_tets[i][2]],
1415 positions[new_tets[i][3]]) > 0) {
1417 positions_double[new_tets[i][0]][0],
1418 positions_double[new_tets[i][0]][1],
1419 positions_double[new_tets[i][0]][2],
1420 positions_double[new_tets[i][1]][0],
1421 positions_double[new_tets[i][1]][1],
1422 positions_double[new_tets[i][1]][2],
1423 positions_double[new_tets[i][2]][0],
1424 positions_double[new_tets[i][2]][1],
1425 positions_double[new_tets[i][2]][2],
1426 positions_double[new_tets[i][3]][0],
1427 positions_double[new_tets[i][3]][1],
1428 positions_double[new_tets[i][3]][2],
1431 if (energy > new_energy_max) new_energy_max = energy;
1434 positions_double[new_tets[i][1]][0],
1435 positions_double[new_tets[i][1]][1],
1436 positions_double[new_tets[i][1]][2],
1437 positions_double[new_tets[i][0]][0],
1438 positions_double[new_tets[i][0]][1],
1439 positions_double[new_tets[i][0]][2],
1440 positions_double[new_tets[i][2]][0],
1441 positions_double[new_tets[i][2]][1],
1442 positions_double[new_tets[i][2]][2],
1443 positions_double[new_tets[i][3]][0],
1444 positions_double[new_tets[i][3]][1],
1445 positions_double[new_tets[i][3]][2],
1448 if (energy > new_energy_max) new_energy_max = energy;
1452 return new_energy_max;
1455 swap44->set_value_function(swap44_energy_check);
1456 swap44->add_invariant(std::make_shared<EdgeValenceInvariant>(*mesh, 4));
1459 auto swap32 = std::make_shared<TetEdgeSwap>(*mesh, 0);
1460 swap32->add_invariant(std::make_shared<EdgeValenceInvariant>(*mesh, 3));
1461 swap32->add_invariant(
1462 std::make_shared<Swap32EnergyBeforeInvariant>(*mesh, pt_attribute.as<
Rational>()));
1464 swap32->collapse().add_invariant(invariant_separate_substructures);
1466 swap32->collapse().set_new_attribute_strategy(
1468 CollapseBasicStrategy::CopyOther);
1469 swap32->collapse().set_new_attribute_strategy(
1470 sizing_field_scalar_attribute,
1471 CollapseBasicStrategy::CopyOther);
1473 swap32->split().set_new_attribute_strategy(pt_attribute);
1474 swap32->split().set_new_attribute_strategy(sizing_field_scalar_attribute);
1475 swap32->split().set_new_attribute_strategy(
1479 swap32->collapse().set_new_attribute_strategy(
1486 swap32->split().set_new_attribute_strategy(
1487 target_edge_length_attribute,
1490 swap32->collapse().set_new_attribute_strategy(
1491 target_edge_length_attribute,
1494 swap32->add_transfer_strategy(amips_update);
1497 swap32->collapse().add_invariant(inversion_invariant);
1500 for (
const auto& attr : pass_through_attributes) {
1501 swap32->split().set_new_attribute_strategy(
1505 swap32->collapse().set_new_attribute_strategy(
1512 auto swap_all = std::make_shared<OrOperationSequence>(*mesh);
1513 swap_all->add_operation(swap32);
1514 swap_all->add_operation(swap44);
1515 swap_all->add_operation(swap56);
1516 swap_all->add_transfer_strategy(tag_update);
1517 swap_all->add_transfer_strategy(energy_filter_update);
1519 auto swap_then_round = std::make_shared<AndOperationSequence>(*mesh);
1520 swap_then_round->add_operation(swap_all);
1521 swap_then_round->add_operation(rounding);
1522 swap_then_round->add_invariant(
1523 std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<
char>()));
1524 swap_then_round->add_invariant(std::make_shared<InteriorEdgeInvariant>(*mesh));
1525 swap_then_round->add_invariant(std::make_shared<NoChildMeshAttachingInvariant>(*mesh));
1527 swap_then_round->set_priority(long_edges_first);
1531 for (
auto& s : update_child_position) {
1532 swap_then_round->add_transfer_strategy(s);
1535 ops.push_back(swap_then_round);
1536 ops_name.push_back(
"EDGE SWAP");
1537 ops.emplace_back(rounding);
1538 ops_name.emplace_back(
"rounding");
1588 auto smoothing = std::make_shared<AMIPSOptimizationSmoothing>(*mesh, pt_attribute);
1589 smoothing->add_invariant(
1590 std::make_shared<RoundedInvariant>(*mesh, pt_attribute.as<
Rational>()));
1591 smoothing->add_invariant(inversion_invariant);
1592 for (
auto& s : update_child_position) {
1593 smoothing->add_transfer_strategy(s);
1606 auto proj_smoothing = std::make_shared<ProjectOperation>(smoothing, mesh_constraint_pairs);
1607 proj_smoothing->use_random_priority() =
true;
1609 proj_smoothing->add_invariant(envelope_invariant);
1610 proj_smoothing->add_invariant(inversion_invariant);
1611 proj_smoothing->add_invariant(
1612 std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<
char>()));
1614 proj_smoothing->add_transfer_strategy(amips_update);
1615 proj_smoothing->add_transfer_strategy(edge_length_update);
1616 for (
auto& s : update_parent_position) {
1617 proj_smoothing->add_transfer_strategy(s);
1620 for (
auto& s : update_child_position) {
1621 proj_smoothing->add_transfer_strategy(s);
1625 for (
int i = 0; i < 1; ++i) {
1626 ops.push_back(proj_smoothing);
1627 ops_name.push_back(
"SMOOTHING");
1630 ops.emplace_back(rounding);
1631 ops_name.emplace_back(
"rounding");
1641 auto surface_mesh = mesh->get_child_meshes().front();
1646 options.
output +
"_intermediate_surface",
1658 int64_t success = 10;
1666 logger().info(
"----------------------- Preprocess Collapse -----------------------");
1667 for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1668 const auto vertices = mesh->orient_vertices(t);
1669 std::vector<Vector3r> pos;
1670 for (
int i = 0; i <
vertices.size(); ++i) {
1671 pos.push_back(pt_accessor.const_vector_attribute(
vertices[i]));
1677 logger().info(
"Executing collapse ...");
1684 "Executed {}, {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, "
1686 "preprocessing collapse",
1697 int64_t unrounded = 0;
1699 const auto p = pt_accessor.vector_attribute(v);
1700 for (int64_t d = 0; d < 3; ++d) {
1701 if (!p[d].is_rounded()) {
1708 logger().info(
"Mesh has {} unrounded vertices", unrounded);
1711 double max_energy = std::numeric_limits<double>::lowest();
1712 double min_energy = std::numeric_limits<double>::max();
1713 double avg_energy = 0;
1714 for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1716 double e = amips_accessor.scalar_attribute(t);
1717 max_energy = std::max(max_energy, e);
1718 min_energy = std::min(min_energy, e);
1722 avg_energy = avg_energy / mesh->get_all(mesh->top_simplex_type()).size();
1725 "Max AMIPS Energy: {}, Min AMIPS Energy: {}, Avg AMIPS Energy: {}",
1739 double old_max_energy = max_energy;
1740 double old_avg_energy = avg_energy;
1742 bool is_double =
false;
1743 for (int64_t i = 0; i < options.
passes; ++i) {
1744 logger().info(
"--------------------------- Pass {} ---------------------------", i);
1748 for (
auto& op : ops) {
1749 for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1750 const auto vertices = mesh->orient_vertices(t);
1751 std::vector<Vector3r> pos;
1752 for (
int i = 0; i <
vertices.size(); ++i) {
1753 pos.push_back(pt_accessor.const_vector_attribute(
vertices[i]));
1759 logger().info(
"Executing {} ...", ops_name[jj]);
1771 pass_stats += stats;
1773 "Executed {}, {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, "
1786 int64_t unrounded = 0;
1788 const auto p = pt_accessor.vector_attribute(v);
1789 for (int64_t d = 0; d < 3; ++d) {
1790 if (!p[d].is_rounded()) {
1797 logger().info(
"Mesh has {} unrounded vertices", unrounded);
1802 max_energy = std::numeric_limits<double>::lowest();
1803 min_energy = std::numeric_limits<double>::max();
1804 for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1806 double e = amips_accessor.scalar_attribute(t);
1807 max_energy = std::max(max_energy, e);
1808 min_energy = std::min(min_energy, e);
1812 avg_energy = avg_energy / mesh->get_all(mesh->top_simplex_type()).size();
1815 "Max AMIPS Energy: {}, Min AMIPS Energy: {}, Avg AMIPS Energy: {}",
1825 "Executed {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, executing: {}",
1846 options.
output +
"_intermediate_surface",
1851 assert(mesh->is_connectivity_valid());
1854 max_energy = std::numeric_limits<double>::lowest();
1855 min_energy = std::numeric_limits<double>::max();
1857 for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1859 double e = amips_accessor.scalar_attribute(t);
1860 max_energy = std::max(max_energy, e);
1861 min_energy = std::min(min_energy, e);
1865 avg_energy = avg_energy / mesh->get_all(mesh->top_simplex_type()).size();
1867 int64_t unrounded = 0;
1869 bool rational =
false;
1871 const auto p = pt_accessor.vector_attribute(v);
1872 for (int64_t d = 0; d < bmax.size(); ++d) {
1873 if (!p[d].is_rounded()) {
1881 is_double = !rational;
1884 logger().info(
"Mesh has {} unrounded vertices", unrounded);
1886 "Max AMIPS Energy: {}, Min AMIPS Energy: {}, Avg AMIPS Energy: {}",
1893 if (i > 0 && old_max_energy - max_energy < 5e-1 &&
1894 (old_avg_energy - avg_energy) / avg_energy < 0.1) {
1900 edge_length_attribute.as<
double>(),
1901 sizing_field_scalar_attribute.as<
double>(),
1902 amips_attribute.as<
double>(),
1903 target_edge_length_attribute.as<
double>(),
1904 visited_vertex_flag.as<
char>(),
1910 wmtk::logger().info(
"adjusting sizing field finished");
1938 energy_filter_accessor.scalar_attribute(v) = char(1);
1946 amips_attribute.as<
double>(),
1947 energy_filter_attribute.as<
char>(),
1948 visited_vertex_flag.as<
char>(),
1951 target_edge_length);
1956 if (energy_filter_accessor.scalar_attribute(e) ==
char(1) ||
1957 energy_filter_accessor.scalar_attribute(
1963 "{} edges are going to be executed out of {}",
1968 old_max_energy = max_energy;
1969 old_avg_energy = avg_energy;
1972 if (max_energy <= target_max_amips && is_double)
break;
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.
Handle that represents attributes for some mesh.
void write_mesh(const Mesh &m, const std::string &name, const std::map< std::string, std::vector< int64_t >> &multimesh_names={})
Write a mesh to cache.
std::shared_ptr< Mesh > read_mesh(const std::string &name) const
Load a mesh from cache.
void add_invariant(std::shared_ptr< Invariant > invariant)
void set_priority(const std::function< double(const simplex::Simplex &)> &func)
virtual PrimitiveType primitive_type() const =0
void add_transfer_strategy(const std::shared_ptr< const operations::AttributeTransferStrategyBase > &other)
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 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)
std::vector< attribute::MeshAttributeHandle > get_attributes(const Mesh &m, const nlohmann::json &attribute_names)
void adjust_sizing_field(Mesh &m, const TypedAttributeHandle< Rational > &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)
std::vector< std::pair< std::shared_ptr< Mesh >, std::string > > wildmeshing(const WildMeshingOptions &option)
void set_operation_energy_filter(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)
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
bool replace_double_coordinate
std::vector< nlohmann::json > pass_through
double target_edge_length
std::vector< WildmeshingOptionsEnvelope > envelopes
size_t scheduler_update_frequency
WildmeshingOptionsAttributes attributes
const std::filesystem::path data_dir