437 const std::vector<basis::ElementBases> &bases,
438 const std::vector<mesh::LocalBoundary> &total_local_boundary,
439 Eigen::MatrixXd &node_positions,
440 Eigen::MatrixXi &boundary_edges,
441 Eigen::MatrixXi &boundary_triangles,
442 std::vector<Eigen::Triplet<double>> &displacement_map_entries,
443 const int sampling_order)
457 logger().warn(
"max_order collision-proxy sampling requires a conforming volume mesh without polytopes; falling back to the standard boundary extraction");
459 node_positions, boundary_edges, boundary_triangles, displacement_map_entries);
463 displacement_map_entries.clear();
464 const Mesh3D &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
467 if (sampling_order > 0)
475 int o = b.bases.front().order();
477 o = std::max(o, prism_q_order(b));
487 std::map<std::array<int, 4>,
int> vertex_id;
488 std::vector<Eigen::Vector3d> vertices;
489 std::vector<std::tuple<int, int, int>> proxy_tris;
493 const int el = lb.element_id();
495 Eigen::MatrixXd ref_nodes;
496 if (b.bases.empty() || !element_ref_nodes(mesh, el, b, ref_nodes))
504 for (
int i = 0; i < ref_nodes.rows(); ++i)
505 if (std::abs(ref_nodes(i, 2) - 1.0) < 1e-12)
511 for (
int j = 0; j < lb.size(); ++j)
513 const int eid = lb.global_primitive_id(j);
515 assert(nfv == 3 || nfv == 4);
524 assert(nav.
face == eid);
525 std::vector<int> gv(nfv);
528 for (
int k = 0; k < nfv; ++k)
539 Eigen::MatrixXd ref_vertices;
540 std::vector<int> local_to_global;
545 local_to_global.assign(vertices.begin(), vertices.end());
551 local_to_global.assign(vertices.begin(), vertices.end());
557 local_to_global.assign(vertices.begin(), vertices.end());
564 local_to_global.assign(vertices.begin(), vertices.end());
567 std::vector<Eigen::RowVector3d> c(nfv);
568 for (
int k = 0; k < nfv; ++k)
570 const auto it = std::find(local_to_global.begin(), local_to_global.end(), gv[k]);
571 assert(it != local_to_global.end());
572 c[k] = ref_vertices.row(std::distance(local_to_global.begin(), it));
576 std::vector<std::array<int, 2>> coords;
577 std::vector<std::tuple<int, int, int>> local_tris;
580 pts.resize((M + 1) * (M + 2) / 2, 3);
581 coords.resize(pts.rows());
582 std::vector<int> off(M + 2, 0);
583 for (
int r = 0; r <= M; ++r)
584 off[r + 1] = off[r] + (M + 1 - r);
585 for (
int r = 0; r <= M; ++r)
586 for (
int i = 0; i <= M - r; ++i)
588 pts.row(off[r] + i) = c[0] + (double(i) / M) * (c[1] - c[0]) + (double(r) / M) * (c[2] - c[0]);
589 coords[off[r] + i] = {{i, r}};
591 for (
int r = 0; r < M; ++r)
593 for (
int i = 0; i < M - r; ++i)
595 local_tris.emplace_back(off[r] + i, off[r] + i + 1, off[r + 1] + i);
597 local_tris.emplace_back(off[r] + i + 1, off[r + 1] + i + 1, off[r + 1] + i);
603 pts.resize((M + 1) * (M + 1), 3);
604 coords.resize(pts.rows());
605 const auto gid = [M](
const int i,
const int r) {
return r * (M + 1) + i; };
606 for (
int r = 0; r <= M; ++r)
608 for (
int i = 0; i <= M; ++i)
610 const double u = double(i) / M, v = double(r) / M;
611 pts.row(gid(i, r)) = (1 - u) * (1 - v) * c[0] + u * (1 - v) * c[1] + u * v * c[2] + (1 - u) * v * c[3];
612 coords[gid(i, r)] = {{i, r}};
615 for (
int r = 0; r < M; ++r)
617 for (
int i = 0; i < M; ++i)
619 local_tris.emplace_back(gid(i, r), gid(i + 1, r), gid(i, r + 1));
620 local_tris.emplace_back(gid(i + 1, r + 1), gid(i, r + 1), gid(i + 1, r));
625 std::vector<polyfem::assembler::AssemblyValues>
vals;
626 b.evaluate_bases(pts,
vals);
628 const auto edge_key = [M](
const int va,
const int vb,
const int step) {
629 return va < vb ? std::array<int, 4>{{1, va, vb, step}}
630 : std::array<int, 4>{{1, vb, va, M - step}};
633 std::vector<int> ids(pts.rows());
634 for (
int s = 0; s < pts.rows(); ++s)
636 const int i = coords[s][0], r = coords[s][1];
637 std::array<int, 4> key;
640 if (r == 0 && i == 0)
641 key = {{0, gv[0], 0, 0}};
642 else if (r == 0 && i == M)
643 key = {{0, gv[1], 0, 0}};
645 key = {{0, gv[2], 0, 0}};
647 key = edge_key(gv[0], gv[1], i);
649 key = edge_key(gv[0], gv[2], r);
651 key = edge_key(gv[1], gv[2], r);
653 key = {{2, eid, i, r}};
657 if (i == 0 && r == 0)
658 key = {{0, gv[0], 0, 0}};
659 else if (i == M && r == 0)
660 key = {{0, gv[1], 0, 0}};
661 else if (i == M && r == M)
662 key = {{0, gv[2], 0, 0}};
663 else if (i == 0 && r == M)
664 key = {{0, gv[3], 0, 0}};
666 key = edge_key(gv[0], gv[1], i);
668 key = edge_key(gv[1], gv[2], r);
670 key = edge_key(gv[3], gv[2], i);
672 key = edge_key(gv[0], gv[3], r);
674 key = {{2, eid, i, r}};
677 const auto it = vertex_id.find(key);
678 if (it != vertex_id.end())
684 Eigen::Vector3d pos = Eigen::Vector3d::Zero();
686 if (apex_node >= 0 && std::abs(pts(s, 2) - 1.0) < 1e-12)
688 for (
const auto &g : b.bases[apex_node].global())
690 pos += g.val * g.node.transpose();
695 for (
size_t i2 = 0; i2 <
vals.size(); ++i2)
697 const double Ni =
vals[i2].val(s);
698 if (std::abs(Ni) < 1e-12)
700 for (
const auto &g : b.bases[i2].global())
702 pos += Ni * g.val * g.node.transpose();
703 weights[g.index] += Ni * g.val;
706 assert(pos.allFinite());
708 const int vid = int(vertices.size());
709 vertex_id[key] = vid;
710 vertices.push_back(pos);
712 if (std::abs(kv.second) > 1e-10)
713 displacement_map_entries.emplace_back(vid, kv.first, kv.second);
717 for (
const auto &t : local_tris)
718 proxy_tris.emplace_back(ids[std::get<0>(t)], ids[std::get<1>(t)], ids[std::get<2>(t)]);
722 node_positions.resize(vertices.size(), 3);
723 for (
int i = 0; i < int(vertices.size()); ++i)
724 node_positions.row(i) = vertices[i];
726 boundary_triangles.resize(proxy_tris.size(), 3);
727 for (
int i = 0; i < int(proxy_tris.size()); ++i)
728 boundary_triangles.row(i) << std::get<0>(proxy_tris[i]), std::get<2>(proxy_tris[i]), std::get<1>(proxy_tris[i]);
730 if (boundary_triangles.rows() > 0)
731 igl::edges(boundary_triangles, boundary_edges);
733 if (
const char *dump = getenv(
"POLYFEM_DUMP_COLLISION_PROXY"))
734 igl::write_triangle_mesh(dump, node_positions, boundary_triangles);
740 const std::vector<basis::ElementBases> &bases,
741 const std::vector<mesh::LocalBoundary> &total_local_boundary,
742 Eigen::MatrixXd &node_positions,
743 Eigen::MatrixXi &boundary_edges,
744 Eigen::MatrixXi &boundary_triangles,
745 std::vector<Eigen::Triplet<double>> &displacement_map_entries)
749 displacement_map_entries.clear();
755 logger().warn(
"Skipping as the mesh has polygons");
761 std::vector<Eigen::Vector3d> node_positions_vec;
762 node_positions_vec.reserve(n_bases + (is_simplicial ? 0 : mesh.
n_faces()));
766 const Mesh3D &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
768 std::vector<std::tuple<int, int, int>> tris;
770 std::vector<bool> visited_node(n_bases,
false);
772 std::stringstream print_warning;
786 bool has_prism_or_pyramid =
false;
791 has_prism_or_pyramid =
true;
798 const auto edge_dofs = build_edge_dofs(mesh, bases);
799 constexpr long S = PROXY_PARAM_SCALE;
801 const auto emit_dof = [&](
const int gindex,
const Eigen::Vector3d &pos) {
802 if (gindex >=
int(node_positions_vec.size()))
803 node_positions_vec.resize(gindex + 1, Eigen::Vector3d::Zero());
804 node_positions_vec[gindex] = pos;
805 if (!visited_node[gindex])
806 displacement_map_entries.emplace_back(gindex, gindex, 1);
807 visited_node[gindex] =
true;
810 Eigen::MatrixXd ref_nodes;
813 const int el = lb.element_id();
815 if (b.bases.empty() || !element_ref_nodes(mesh, el, b, ref_nodes))
818 for (
int j = 0; j < lb.size(); ++j)
820 const int eid = lb.global_primitive_id(j);
821 const Eigen::VectorXi nodes = b.local_nodes_for_primitive(eid, mesh3d);
823 assert(nfv == 3 || nfv == 4);
824 assert(nodes.size() >= nfv);
827 std::vector<std::array<long, 2>> pts;
828 std::vector<int> dof;
831 std::array<std::array<long, 2>, 4> cp;
833 cp = {{{{0, 0}}, {{S, 0}}, {{0, S}}, {{0, 0}}}};
835 cp = {{{{0, 0}}, {{S, 0}}, {{S, S}}, {{0, S}}}};
836 std::array<int, 4> cd{{-1, -1, -1, -1}};
838 for (
int k = 0; k < nfv; ++k)
840 const auto &glob = b.bases[nodes(k)].global();
841 assert(glob.size() == 1);
842 if (glob.size() != 1)
847 cd[k] = glob.front().index;
848 pts.push_back(cp[k]);
849 dof.push_back(cd[k]);
850 emit_dof(cd[k], glob.front().node.transpose());
856 for (
int k = 0; k < nfv; ++k)
858 const int va = cd[k], vb = cd[(k + 1) % nfv];
859 const auto it = edge_dofs.find({std::min(va, vb), std::max(va, vb)});
860 if (it == edge_dofs.end())
862 for (
const auto &ed : it->second)
864 const long s = va < vb ? std::get<0>(ed) : S - std::get<0>(ed);
865 const auto &A = cp[k];
866 const auto &B = cp[(k + 1) % nfv];
867 pts.push_back({{(A[0] * (S - s) + B[0] * s) / S, (A[1] * (S - s) + B[1] * s) / S}});
868 dof.push_back(std::get<1>(ed));
869 emit_dof(std::get<1>(ed), std::get<2>(ed));
875 const Eigen::RowVector3d c0 = ref_nodes.row(nodes(0));
876 const Eigen::RowVector3d A3 = ref_nodes.row(nodes(1)) - c0;
877 const Eigen::RowVector3d B3 = ref_nodes.row(nodes(nfv - 1)) - c0;
878 const double aa = A3.squaredNorm(), bb = B3.squaredNorm(), ab = A3.dot(B3);
879 const double det = aa * bb - ab * ab;
880 for (
long n = nfv; n < nodes.size(); ++n)
882 const auto &glob = b.bases[nodes(n)].global();
883 if (glob.size() != 1)
885 const Eigen::RowVector3d d3 = ref_nodes.row(nodes(n)) - c0;
886 const double du = d3.dot(A3), dv = d3.dot(B3);
887 const double u = (du * bb - dv * ab) / det;
888 const double v = (dv * aa - du * ab) / det;
889 const long lu = std::lround(u * S), lv = std::lround(v * S);
890 assert(std::abs(u * S - lu) < 1e-6 && std::abs(v * S - lv) < 1e-6);
892 const bool on_edge = nfv == 3
893 ? (lu == 0 || lv == 0 || lu + lv == S)
894 : (lu == 0 || lu == S || lv == 0 || lv == S);
897 pts.push_back({{lu, lv}});
898 dof.push_back(glob.front().index);
899 emit_dof(glob.front().index, glob.front().node.transpose());
902 std::vector<std::array<int, 3>> local_tris;
903 if (!triangulate_lattice(pts, nfv, local_tris))
904 triangulate_convex_pointset(pts, local_tris);
905 for (
const auto &t : local_tris)
906 tris.emplace_back(dof[t[0]], dof[t[1]], dof[t[2]]);
912 node_positions_vec.resize(
913 std::max(node_positions_vec.size(),
size_t(n_bases)), Eigen::Vector3d::Zero());
915 node_positions.resize(node_positions_vec.size(), 3);
916 for (
int i = 0; i < int(node_positions_vec.size()); ++i)
917 node_positions.row(i) = node_positions_vec[i];
919 boundary_triangles.resize(tris.size(), 3);
920 for (
int i = 0; i < int(tris.size()); ++i)
921 boundary_triangles.row(i) << std::get<0>(tris[i]), std::get<2>(tris[i]), std::get<1>(tris[i]);
923 if (boundary_triangles.rows() > 0)
924 igl::edges(boundary_triangles, boundary_edges);
926 if (
const char *dump = getenv(
"POLYFEM_DUMP_COLLISION_PROXY"))
927 igl::write_triangle_mesh(dump, node_positions, boundary_triangles);
936 for (
int j = 0; j < lb.size(); ++j)
938 const int eid = lb.global_primitive_id(j);
939 const int lid = lb[j];
940 const Eigen::VectorXi nodes = b.local_nodes_for_primitive(eid, mesh3d);
942 if (mesh.
is_cube(lb.element_id()))
944 assert(!is_simplicial);
946 std::vector<int> loc_nodes;
949 for (
long n = 0; n < nodes.size(); ++n)
951 auto &bs = b.bases[nodes(n)];
952 const auto &glob = bs.global();
953 if (glob.size() != 1)
956 int gindex = glob.front().index;
957 node_positions_vec.resize(std::max(
int(node_positions_vec.size()), gindex + 1));
958 node_positions_vec[gindex] = glob.front().node;
959 bary += glob.front().node;
960 loc_nodes.push_back(gindex);
963 if (loc_nodes.size() != 4)
965 logger().trace(
"skipping element {} since it is not Q1", eid);
971 const int new_node = n_bases + eid;
972 node_positions_vec.resize(std::max(
int(node_positions_vec.size()), new_node + 1));
973 node_positions_vec[new_node] = bary;
974 tris.emplace_back(loc_nodes[1], loc_nodes[0], new_node);
975 tris.emplace_back(loc_nodes[2], loc_nodes[1], new_node);
976 tris.emplace_back(loc_nodes[3], loc_nodes[2], new_node);
977 tris.emplace_back(loc_nodes[0], loc_nodes[3], new_node);
979 for (
int q = 0; q < 4; ++q)
981 if (!visited_node[loc_nodes[q]])
982 displacement_map_entries.emplace_back(loc_nodes[q], loc_nodes[q], 1);
984 visited_node[loc_nodes[q]] =
true;
985 displacement_map_entries.emplace_back(new_node, loc_nodes[q], 0.25);
990 else if (mesh.
is_prism(lb.element_id()))
992 assert(!is_simplicial);
994 std::vector<int> loc_nodes;
995 std::vector<int> loc_local_nodes;
997 for (
long n = 0; n < nodes.size(); ++n)
999 auto &bs = b.bases[nodes(n)];
1000 const auto &glob = bs.global();
1001 if (glob.size() != 1)
1004 int gindex = glob.front().index;
1005 node_positions_vec.resize(std::max(
int(node_positions_vec.size()), gindex + 1));
1006 node_positions_vec[gindex] = glob.front().node;
1007 loc_nodes.push_back(gindex);
1008 loc_local_nodes.push_back(nodes(n));
1011 auto update_mapping = [&displacement_map_entries, &visited_node](
const std::vector<int> &loc_nodes) {
1012 for (
int k = 0; k < loc_nodes.size(); ++k)
1014 if (!visited_node[loc_nodes[k]])
1015 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
1017 visited_node[loc_nodes[k]] =
true;
1024 if (loc_nodes.size() == 3)
1026 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
1028 update_mapping(loc_nodes);
1030 else if (loc_nodes.size() == 6)
1032 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[5]);
1033 tris.emplace_back(loc_nodes[3], loc_nodes[1], loc_nodes[4]);
1034 tris.emplace_back(loc_nodes[4], loc_nodes[2], loc_nodes[5]);
1035 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[5]);
1037 update_mapping(loc_nodes);
1039 else if (loc_nodes.size() == 10)
1041 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[8]);
1042 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[9]);
1043 tris.emplace_back(loc_nodes[4], loc_nodes[1], loc_nodes[5]);
1044 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[9]);
1045 tris.emplace_back(loc_nodes[6], loc_nodes[2], loc_nodes[7]);
1046 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[9]);
1047 tris.emplace_back(loc_nodes[8], loc_nodes[3], loc_nodes[9]);
1048 tris.emplace_back(loc_nodes[9], loc_nodes[4], loc_nodes[5]);
1049 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[9]);
1050 update_mapping(loc_nodes);
1054 logger().trace(
"skipping element {} since it is not linear, it has {} nodes", eid, loc_nodes.size());
1059 if (loc_nodes.size() < 4 || loc_local_nodes.size() < 4)
1061 logger().trace(
"skipping prism quad face {} since it has only {} complete nodes", eid, loc_nodes.size());
1065 const int p = b.bases.empty() ? -1 : b.bases.front().order();
1066 const int n_tri_nodes = (p + 1) * (p + 2) / 2;
1067 const int q = n_tri_nodes > 0 && b.bases.size() % n_tri_nodes == 0 ? int(b.bases.size()) / n_tri_nodes - 1 : -1;
1069 if (p < 1 || p > 3 || q < 1 || q > 3 || (p == 3 && q == 3))
1071 logger().trace(
"skipping prism quad face {} with unsupported p={}, q={}", eid, p, q);
1075 auto is_vertical_prism_edge = [](
const int a,
const int b) {
1076 return (a >= 0 && a < 3 && b == a + 3) || (b >= 0 && b < 3 && a == b + 3);
1079 std::vector<int> edge_orders(4);
1080 for (
int k = 0; k < 4; ++k)
1081 edge_orders[k] = is_vertical_prism_edge(loc_local_nodes[k], loc_local_nodes[(k + 1) % 4]) ? q : p;
1083 const int u_order = edge_orders[0];
1084 const int v_order = edge_orders[1];
1085 const int expected_nodes = (u_order + 1) * (v_order + 1);
1086 if (loc_nodes.size() != expected_nodes || edge_orders[0] != edge_orders[2] || edge_orders[1] != edge_orders[3])
1088 logger().trace(
"skipping prism quad face {} with p={}, q={} and {} nodes", eid, p, q, loc_nodes.size());
1092 std::vector<int> grid(expected_nodes, -1);
1093 auto grid_index = [u_order](
const int i,
const int j) {
1094 return j * (u_order + 1) + i;
1097 grid[grid_index(0, 0)] = loc_nodes[0];
1098 grid[grid_index(u_order, 0)] = loc_nodes[1];
1099 grid[grid_index(u_order, v_order)] = loc_nodes[2];
1100 grid[grid_index(0, v_order)] = loc_nodes[3];
1103 for (
int i = 1; i < u_order; ++i)
1104 grid[grid_index(i, 0)] = loc_nodes[node_index++];
1105 for (
int j = 1; j < v_order; ++j)
1106 grid[grid_index(u_order, j)] = loc_nodes[node_index++];
1107 for (
int i = u_order - 1; i > 0; --i)
1108 grid[grid_index(i, v_order)] = loc_nodes[node_index++];
1109 for (
int j = v_order - 1; j > 0; --j)
1110 grid[grid_index(0, j)] = loc_nodes[node_index++];
1112 for (
int j = 1; j < v_order; ++j)
1113 for (
int i = 1; i < u_order; ++i)
1114 grid[grid_index(i, j)] = loc_nodes[node_index++];
1116 assert(node_index == loc_nodes.size());
1117 assert(std::all_of(grid.begin(), grid.end(), [](
const int n) { return n >= 0; }));
1119 for (
int j = 0; j < v_order; ++j)
1121 for (
int i = 0; i < u_order; ++i)
1123 tris.emplace_back(grid[grid_index(i, j)], grid[grid_index(i + 1, j)], grid[grid_index(i, j + 1)]);
1124 tris.emplace_back(grid[grid_index(i + 1, j + 1)], grid[grid_index(i, j + 1)], grid[grid_index(i + 1, j)]);
1128 update_mapping(loc_nodes);
1135 assert(!is_simplicial);
1137 std::vector<int> loc_nodes;
1138 std::vector<int> loc_local_nodes;
1140 for (
long n = 0; n < nodes.size(); ++n)
1142 auto &bs = b.bases[nodes(n)];
1143 const auto &glob = bs.global();
1144 if (glob.size() != 1)
1147 int gindex = glob.front().index;
1148 node_positions_vec.resize(std::max(
int(node_positions_vec.size()), gindex + 1));
1149 node_positions_vec[gindex] = glob.front().node;
1150 loc_nodes.push_back(gindex);
1151 loc_local_nodes.push_back(nodes(n));
1154 auto update_mapping = [&displacement_map_entries, &visited_node](
const std::vector<int> &loc_nodes) {
1155 for (
int k = 0; k < loc_nodes.size(); ++k)
1157 if (!visited_node[loc_nodes[k]])
1158 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
1160 visited_node[loc_nodes[k]] =
true;
1164 const int p = b.bases.empty() ? -1 : b.bases.front().order();
1167 logger().trace(
"skipping pyramid face {} with unsupported p={}", eid, p);
1173 const int expected_nodes = (p + 1) * (p + 1);
1174 if (loc_nodes.size() != expected_nodes || loc_local_nodes.size() != expected_nodes)
1176 logger().trace(
"skipping pyramid quad face {} with p={} and {} nodes", eid, p, loc_nodes.size());
1180 Eigen::MatrixXd pyramid_nodes;
1183 const Eigen::RowVector3d origin = pyramid_nodes.row(loc_local_nodes[0]);
1184 const Eigen::RowVector3d u_axis = pyramid_nodes.row(loc_local_nodes[1]) - origin;
1185 const Eigen::RowVector3d v_axis = pyramid_nodes.row(loc_local_nodes[3]) - origin;
1187 std::vector<int> grid(expected_nodes, -1);
1188 auto grid_index = [p](
const int i,
const int j) {
1189 return j * (p + 1) + i;
1192 bool valid_grid =
true;
1193 for (
int n = 0; n < loc_nodes.size(); ++n)
1195 const Eigen::RowVector3d rel = pyramid_nodes.row(loc_local_nodes[n]) - origin;
1196 const int i = int(std::lround(p * rel.dot(u_axis) / u_axis.squaredNorm()));
1197 const int j = int(std::lround(p * rel.dot(v_axis) / v_axis.squaredNorm()));
1198 if (i < 0 || i > p || j < 0 || j > p)
1200 logger().trace(
"skipping pyramid quad face {} with invalid local grid coordinate ({}, {})", eid, i, j);
1204 if (grid[grid_index(i, j)] >= 0)
1206 logger().trace(
"skipping pyramid quad face {} with duplicate local grid coordinate ({}, {})", eid, i, j);
1210 grid[grid_index(i, j)] = loc_nodes[n];
1213 if (!valid_grid || !std::all_of(grid.begin(), grid.end(), [](
const int n) { return n >= 0; }))
1216 for (
int j = 0; j < p; ++j)
1218 for (
int i = 0; i < p; ++i)
1220 tris.emplace_back(grid[grid_index(i, j)], grid[grid_index(i + 1, j)], grid[grid_index(i, j + 1)]);
1221 tris.emplace_back(grid[grid_index(i + 1, j + 1)], grid[grid_index(i, j + 1)], grid[grid_index(i + 1, j)]);
1225 update_mapping(loc_nodes);
1227 else if (loc_nodes.size() == 3)
1229 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
1230 update_mapping(loc_nodes);
1232 else if (loc_nodes.size() == 6)
1234 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[5]);
1235 tris.emplace_back(loc_nodes[3], loc_nodes[1], loc_nodes[4]);
1236 tris.emplace_back(loc_nodes[4], loc_nodes[2], loc_nodes[5]);
1237 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[5]);
1238 update_mapping(loc_nodes);
1240 else if (loc_nodes.size() == 10)
1242 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[8]);
1243 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[9]);
1244 tris.emplace_back(loc_nodes[4], loc_nodes[1], loc_nodes[5]);
1245 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[9]);
1246 tris.emplace_back(loc_nodes[6], loc_nodes[2], loc_nodes[7]);
1247 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[9]);
1248 tris.emplace_back(loc_nodes[8], loc_nodes[3], loc_nodes[9]);
1249 tris.emplace_back(loc_nodes[9], loc_nodes[4], loc_nodes[5]);
1250 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[9]);
1251 update_mapping(loc_nodes);
1255 logger().trace(
"skipping pyramid tri face {} with p={} and {} nodes", eid, p, loc_nodes.size());
1264 logger().trace(
"skipping element {} since it is not a simplex or hex", eid);
1270 std::vector<int> loc_nodes;
1272 bool is_follower =
false;
1275 for (
long n = 0; n < nodes.size(); ++n)
1277 auto &bs = b.bases[nodes(n)];
1278 const auto &glob = bs.global();
1279 if (glob.size() != 1)
1290 for (
long n = 0; n < nodes.size(); ++n)
1293 const std::vector<basis::Local2Global> &glob = bs.
global();
1294 if (glob.size() != 1)
1297 int gindex = glob.front().index;
1298 node_positions_vec.resize(std::max(
int(node_positions_vec.size()), gindex + 1));
1299 node_positions_vec[gindex] = glob.front().node;
1300 loc_nodes.push_back(gindex);
1303 if (loc_nodes.size() == 3)
1305 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
1307 else if (loc_nodes.size() == 6)
1309 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[5]);
1310 tris.emplace_back(loc_nodes[3], loc_nodes[1], loc_nodes[4]);
1311 tris.emplace_back(loc_nodes[4], loc_nodes[2], loc_nodes[5]);
1312 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[5]);
1314 else if (loc_nodes.size() == 10)
1316 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[8]);
1317 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[9]);
1318 tris.emplace_back(loc_nodes[4], loc_nodes[1], loc_nodes[5]);
1319 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[9]);
1320 tris.emplace_back(loc_nodes[6], loc_nodes[2], loc_nodes[7]);
1321 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[9]);
1322 tris.emplace_back(loc_nodes[8], loc_nodes[3], loc_nodes[9]);
1323 tris.emplace_back(loc_nodes[9], loc_nodes[4], loc_nodes[5]);
1324 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[9]);
1326 else if (loc_nodes.size() == 15)
1328 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[11]);
1329 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[12]);
1330 tris.emplace_back(loc_nodes[3], loc_nodes[12], loc_nodes[11]);
1331 tris.emplace_back(loc_nodes[12], loc_nodes[10], loc_nodes[11]);
1332 tris.emplace_back(loc_nodes[4], loc_nodes[5], loc_nodes[13]);
1333 tris.emplace_back(loc_nodes[4], loc_nodes[13], loc_nodes[12]);
1334 tris.emplace_back(loc_nodes[12], loc_nodes[13], loc_nodes[14]);
1335 tris.emplace_back(loc_nodes[12], loc_nodes[14], loc_nodes[10]);
1336 tris.emplace_back(loc_nodes[14], loc_nodes[9], loc_nodes[10]);
1337 tris.emplace_back(loc_nodes[5], loc_nodes[1], loc_nodes[6]);
1338 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[13]);
1339 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[13]);
1340 tris.emplace_back(loc_nodes[13], loc_nodes[7], loc_nodes[14]);
1341 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[14]);
1342 tris.emplace_back(loc_nodes[14], loc_nodes[8], loc_nodes[9]);
1343 tris.emplace_back(loc_nodes[8], loc_nodes[2], loc_nodes[9]);
1347 print_warning << loc_nodes.size() <<
" ";
1353 for (
int k = 0; k < loc_nodes.size(); ++k)
1355 if (!visited_node[loc_nodes[k]])
1356 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
1358 visited_node[loc_nodes[k]] =
true;
1364 if (print_warning.str().size() > 0)
1365 logger().warn(
"Skipping faces as theys have {} nodes, boundary export supported up to p4", print_warning.str());
1369 node_positions_vec.resize(
1370 std::max(node_positions_vec.size(),
size_t(n_bases + (is_simplicial ? 0 : mesh.
n_faces()))),
1371 Eigen::Vector3d::Zero());
1373 node_positions.resize(node_positions_vec.size(), 3);
1374 for (
int i = 0; i < node_positions_vec.size(); ++i)
1375 node_positions.row(i) = node_positions_vec[i];
1377 boundary_triangles.resize(tris.size(), 3);
1378 for (
int i = 0; i < tris.size(); ++i)
1380 boundary_triangles.row(i) << std::get<0>(tris[i]), std::get<2>(tris[i]), std::get<1>(tris[i]);
1383 if (boundary_triangles.rows() > 0)
1385 igl::edges(boundary_triangles, boundary_edges);
1388 if (
const char *dump = getenv(
"POLYFEM_DUMP_COLLISION_PROXY"))
1389 igl::write_triangle_mesh(dump, node_positions, boundary_triangles);
1393 node_positions.resize(n_bases, 2);
1394 node_positions.setZero();
1395 const Mesh2D &mesh2d =
dynamic_cast<const Mesh2D &
>(mesh);
1397 std::vector<std::pair<int, int>> edges;
1403 for (
int j = 0; j < lb.size(); ++j)
1405 const int eid = lb.global_primitive_id(j);
1406 const int lid = lb[j];
1407 const Eigen::VectorXi nodes = b.local_nodes_for_primitive(eid, mesh2d);
1411 for (
long n = 0; n < nodes.size(); ++n)
1414 const std::vector<basis::Local2Global> &glob = bs.
global();
1415 if (glob.size() != 1)
1418 int gindex = glob.front().index;
1419 node_positions.row(gindex) = glob.front().node.head<2>();
1422 edges.emplace_back(prev_node, gindex);
1429 boundary_triangles.resize(0, 0);
1430 boundary_edges.resize(edges.size(), 2);
1431 for (
int i = 0; i < edges.size(); ++i)
1433 boundary_edges.row(i) << edges[i].first, edges[i].second;
1440 const std::vector<basis::ElementBases> &gbases,
1441 const std::vector<mesh::LocalBoundary> &total_local_boundary,
1442 Eigen::MatrixXd &boundary_vis_vertices,
1443 Eigen::MatrixXd &boundary_vis_local_vertices,
1444 Eigen::MatrixXi &boundary_vis_elements,
1445 Eigen::MatrixXi &boundary_vis_elements_ids,
1446 Eigen::MatrixXi &boundary_vis_primitive_ids,
1447 Eigen::MatrixXd &boundary_vis_normals)
const
1451 std::vector<Eigen::MatrixXd> lv, vertices, allnormals;
1452 std::vector<int> el_ids, global_primitive_ids;
1453 Eigen::MatrixXd uv, local_pts, tmp_n, normals;
1459 std::vector<std::pair<int, int>> edges;
1460 std::vector<std::tuple<int, int, int>> tris;
1462 for (
auto it = total_local_boundary.begin(); it != total_local_boundary.end(); ++it)
1464 const auto &lb = *it;
1465 const auto &gbs = gbases[lb.element_id()];
1467 for (
int k = 0; k < lb.size(); ++k)
1471 case BoundaryType::TRI_LINE:
1475 case BoundaryType::QUAD_LINE:
1479 case BoundaryType::QUAD:
1483 case BoundaryType::TRI:
1487 case BoundaryType::PRISM:
1491 case BoundaryType::PYRAMID:
1495 case BoundaryType::POLYGON:
1499 case BoundaryType::POLYHEDRON:
1502 case BoundaryType::INVALID:
1509 vertices.emplace_back();
1510 lv.emplace_back(local_pts);
1511 el_ids.push_back(lb.element_id());
1512 global_primitive_ids.push_back(lb.global_primitive_id(k));
1513 gbs.eval_geom_mapping(local_pts, vertices.back());
1514 vals.compute(lb.element_id(), mesh.
is_volume(), local_pts, gbs, gbs);
1515 const int tris_start = tris.size();
1519 const bool prism_quad = lb.type() == BoundaryType::PRISM && lb[k] >= 2;
1520 const bool prism_tri = lb.type() == BoundaryType::PRISM && lb[k] < 2;
1522 const bool pyramid_quad = lb.type() == BoundaryType::PYRAMID && lb[k] == 0;
1523 const bool pyramid_tri = lb.type() == BoundaryType::PYRAMID && lb[k] > 0;
1525 if (lb.type() == BoundaryType::QUAD || prism_quad || pyramid_quad)
1527 const auto map = [n_samples, size](
int i,
int j) {
return j * n_samples + i + size; };
1529 for (
int j = 0; j < n_samples - 1; ++j)
1531 for (
int i = 0; i < n_samples - 1; ++i)
1533 tris.emplace_back(map(i, j), map(i + 1, j), map(i, j + 1));
1534 tris.emplace_back(map(i + 1, j + 1), map(i, j + 1), map(i + 1, j));
1538 else if (lb.type() == BoundaryType::TRI || prism_tri || pyramid_tri)
1541 std::vector<int> mapp(n_samples * n_samples, -1);
1542 for (
int j = 0; j < n_samples; ++j)
1544 for (
int i = 0; i < n_samples - j; ++i)
1546 mapp[j * n_samples + i] = index;
1550 const auto map = [mapp, n_samples](
int i,
int j) {
1551 if (j * n_samples + i >= mapp.size())
1553 return mapp[j * n_samples + i];
1556 for (
int j = 0; j < n_samples - 1; ++j)
1558 for (
int i = 0; i < n_samples - j; ++i)
1560 if (map(i, j) >= 0 && map(i + 1, j) >= 0 && map(i, j + 1) >= 0)
1561 tris.emplace_back(map(i, j) + size, map(i + 1, j) + size, map(i, j + 1) + size);
1563 if (map(i + 1, j + 1) >= 0 && map(i, j + 1) >= 0 && map(i + 1, j) >= 0)
1564 tris.emplace_back(map(i + 1, j + 1) + size, map(i, j + 1) + size, map(i + 1, j) + size);
1575 for (
int i = 0; i < vertices.back().rows() - 1; ++i)
1576 edges.emplace_back(i + size, i + size + 1);
1579 normals.resize(
vals.jac_it.size(), tmp_n.cols());
1581 for (
int n = 0; n <
vals.jac_it.size(); ++n)
1583 normals.row(n) = tmp_n *
vals.jac_it[n];
1584 normals.row(n).normalize();
1587 allnormals.push_back(normals);
1590 for (
int n = 0; n <
vals.jac_it.size(); ++n)
1592 tmp_n += normals.row(n);
1597 Eigen::Vector3d e1 = vertices.back().row(std::get<1>(tris.back()) - size) - vertices.back().row(std::get<0>(tris.back()) - size);
1598 Eigen::Vector3d e2 = vertices.back().row(std::get<2>(tris.back()) - size) - vertices.back().row(std::get<0>(tris.back()) - size);
1600 Eigen::Vector3d n = e1.cross(e2);
1601 Eigen::Vector3d nn = tmp_n.transpose();
1605 for (
int i = tris_start; i < tris.size(); ++i)
1607 tris[i] = std::tuple<int, int, int>(std::get<0>(tris[i]), std::get<2>(tris[i]), std::get<1>(tris[i]));
1612 size += vertices.back().rows();
1616 boundary_vis_vertices.resize(size, vertices.front().cols());
1617 boundary_vis_local_vertices.resize(size, vertices.front().cols());
1618 boundary_vis_elements_ids.resize(size, 1);
1619 boundary_vis_primitive_ids.resize(size, 1);
1620 boundary_vis_normals.resize(size, vertices.front().cols());
1623 boundary_vis_elements.resize(tris.size(), 3);
1625 boundary_vis_elements.resize(edges.size(), 2);
1629 for (
const auto &v : vertices)
1631 boundary_vis_vertices.block(index, 0, v.rows(), v.cols()) = v;
1632 boundary_vis_local_vertices.block(index, 0, v.rows(), v.cols()) = lv[ii];
1633 boundary_vis_elements_ids.block(index, 0, v.rows(), 1).setConstant(el_ids[ii]);
1634 boundary_vis_primitive_ids.block(index, 0, v.rows(), 1).setConstant(global_primitive_ids[ii++]);
1639 for (
const auto &n : allnormals)
1641 boundary_vis_normals.block(index, 0, n.rows(), n.cols()) = n;
1648 for (
const auto &t : tris)
1650 boundary_vis_elements.row(index) << std::get<0>(t), std::get<1>(t), std::get<2>(t);
1656 for (
const auto &e : edges)
1658 boundary_vis_elements.row(index) << e.first, e.second;
1666 const Eigen::VectorXi &disc_orders,
1667 const std::vector<basis::ElementBases> &gbases,
1668 const std::map<int, Eigen::MatrixXd> &polys,
1669 const std::map<
int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> &polys_3d,
1670 const bool boundary_only,
1671 Eigen::MatrixXd &points,
1672 Eigen::MatrixXi &tets,
1673 Eigen::MatrixXi &el_id,
1674 Eigen::MatrixXd &discr,
1675 Eigen::MatrixXd &local_points)
const
1679 const auto ¤t_bases = gbases;
1680 int tet_total_size = 0;
1681 int pts_total_size = 0;
1683 Eigen::MatrixXd vis_pts_poly;
1684 Eigen::MatrixXi vis_faces_poly, vis_edges_poly;
1686 for (
size_t i = 0; i < current_bases.size(); ++i)
1688 const auto &bs = current_bases[i];
1696 pts_total_size += sampler.simplex_points().rows();
1700 tet_total_size += sampler.cube_volume().rows();
1701 pts_total_size += sampler.cube_points().rows();
1705 tet_total_size += sampler.prism_volume().rows();
1706 pts_total_size += sampler.prism_points().rows();
1710 tet_total_size += sampler.pyramid_volume().rows();
1711 pts_total_size += sampler.pyramid_points().rows();
1717 sampler.sample_polyhedron(polys_3d.at(i).first, polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
1719 tet_total_size += vis_faces_poly.rows();
1720 pts_total_size += vis_pts_poly.rows();
1724 sampler.sample_polygon(polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
1726 tet_total_size += vis_faces_poly.rows();
1727 pts_total_size += vis_pts_poly.rows();
1732 points.resize(pts_total_size, mesh.
dimension());
1733 local_points.resize(pts_total_size, mesh.
dimension());
1734 local_points.setZero();
1735 tets.resize(tet_total_size, mesh.
is_volume() ? 4 : 3);
1737 el_id.resize(pts_total_size, 1);
1738 discr.resize(pts_total_size, 1);
1740 Eigen::MatrixXd mapped, tmp;
1741 int tet_index = 0, pts_index = 0;
1743 for (
size_t i = 0; i < current_bases.size(); ++i)
1745 const auto &bs = current_bases[i];
1752 bs.eval_geom_mapping(sampler.simplex_points(), mapped);
1754 tets.block(tet_index, 0, sampler.simplex_volume().rows(), tets.cols()) = sampler.simplex_volume().array() + pts_index;
1755 tet_index += sampler.simplex_volume().rows();
1757 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
1758 local_points.block(pts_index, 0, sampler.simplex_points().rows(), sampler.simplex_points().cols()) = sampler.simplex_points();
1759 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
1760 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
1761 pts_index += mapped.rows();
1765 bs.eval_geom_mapping(sampler.cube_points(), mapped);
1767 tets.block(tet_index, 0, sampler.cube_volume().rows(), tets.cols()) = sampler.cube_volume().array() + pts_index;
1768 tet_index += sampler.cube_volume().rows();
1770 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
1771 local_points.block(pts_index, 0, sampler.cube_points().rows(), sampler.cube_points().cols()) = sampler.cube_points();
1772 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
1773 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
1774 pts_index += mapped.rows();
1778 bs.eval_geom_mapping(sampler.prism_points(), mapped);
1780 tets.block(tet_index, 0, sampler.prism_volume().rows(), tets.cols()) = sampler.prism_volume().array() + pts_index;
1781 tet_index += sampler.prism_volume().rows();
1783 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
1784 local_points.block(pts_index, 0, sampler.prism_points().rows(), sampler.prism_points().cols()) = sampler.prism_points();
1785 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
1786 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
1787 pts_index += mapped.rows();
1791 bs.eval_geom_mapping(sampler.pyramid_points(), mapped);
1793 tets.block(tet_index, 0, sampler.pyramid_volume().rows(), tets.cols()) = sampler.pyramid_volume().array() + pts_index;
1794 tet_index += sampler.pyramid_volume().rows();
1796 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
1797 local_points.block(pts_index, 0, sampler.pyramid_points().rows(), sampler.pyramid_points().cols()) = sampler.pyramid_points();
1798 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
1799 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
1800 pts_index += mapped.rows();
1806 sampler.sample_polyhedron(polys_3d.at(i).first, polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
1807 bs.eval_geom_mapping(vis_pts_poly, mapped);
1809 tets.block(tet_index, 0, vis_faces_poly.rows(), tets.cols()) = vis_faces_poly.array() + pts_index;
1810 tet_index += vis_faces_poly.rows();
1812 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
1813 local_points.block(pts_index, 0, vis_pts_poly.rows(), vis_pts_poly.cols()) = vis_pts_poly;
1814 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(-1);
1815 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
1816 pts_index += mapped.rows();
1820 sampler.sample_polygon(polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
1821 bs.eval_geom_mapping(vis_pts_poly, mapped);
1823 tets.block(tet_index, 0, vis_faces_poly.rows(), tets.cols()) = vis_faces_poly.array() + pts_index;
1824 tet_index += vis_faces_poly.rows();
1826 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
1827 local_points.block(pts_index, 0, vis_pts_poly.rows(), vis_pts_poly.cols()) = vis_pts_poly;
1828 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(-1);
1829 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
1830 pts_index += mapped.rows();
1835 assert(pts_index == points.rows());
1836 assert(tet_index == tets.rows());