Skip to content

Commit 03f7e89

Browse files
authored
Make faces an optional parameter to CollisionMesh (#105)
* Make faces an optional parameter to CollisionMesh
1 parent e243f55 commit 03f7e89

File tree

7 files changed

+105
-27
lines changed

7 files changed

+105
-27
lines changed

python/src/collision_mesh.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ void define_collision_mesh(py::module_& m)
2121
faces: The faces of the collision mesh (#F × 3).
2222
displacement_map: The displacement mapping from displacements on the full mesh to the collision mesh.
2323
)ipc_Qu8mg5v7",
24-
py::arg("rest_positions"), py::arg("edges"), py::arg("faces"),
24+
py::arg("rest_positions"), py::arg("edges") = Eigen::MatrixXi(),
25+
py::arg("faces") = Eigen::MatrixXi(),
2526
py::arg("displacement_map") = Eigen::SparseMatrix<double>())
2627
.def(
2728
py::init<
@@ -39,7 +40,8 @@ void define_collision_mesh(py::module_& m)
3940
displacement_map: The displacement mapping from displacements on the full mesh to the collision mesh.
4041
)ipc_Qu8mg5v7",
4142
py::arg("include_vertex"), py::arg("full_rest_positions"),
42-
py::arg("edges"), py::arg("faces"),
43+
py::arg("edges") = Eigen::MatrixXi(),
44+
py::arg("faces") = Eigen::MatrixXi(),
4345
py::arg("displacement_map") = Eigen::SparseMatrix<double>())
4446
.def_static(
4547
"build_from_full_mesh", &CollisionMesh::build_from_full_mesh,
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import numpy as np
2+
import scipy
3+
4+
import find_ipctk
5+
from ipctk import CollisionMesh
6+
7+
8+
def test_collision_mesh():
9+
V = np.array([[0, 0], [1, 0], [0, 1], [1, 1]], dtype=float)
10+
E = np.array([[0, 1], [1, 3], [3, 2], [2, 0]], dtype=int)
11+
12+
W = scipy.sparse.lil_matrix((4, 3), dtype=float)
13+
W[0, 0] = W[1, 1] = W[2, 2] = W[3, 1] = W[3, 2] = 1
14+
15+
mesh = CollisionMesh(V, E, displacement_map=W)
16+
17+
U = np.array([[0, 0], [1, 1], [0, 0]], dtype=float)
18+
19+
Uc = mesh.map_displacements(U)
20+
expected_Uc = np.array([[0, 0], [1, 1], [0, 0], [1, 1]], dtype=float)
21+
assert (Uc == expected_Uc).all()
22+
23+
Vc = mesh.displace_vertices(U)
24+
expected_Vc = np.array([[0, 0], [2, 1], [0, 1], [2, 2]], dtype=float)
25+
assert (Vc == expected_Vc).all()
26+
27+
g = np.array([1, 1, -1, 1, 1, -1, -1, -1], dtype=float)
28+
gf = mesh.to_full_dof(g)
29+
expected_gf = np.array([1, 1, -2, 0, 0, -2], dtype=float)
30+
assert (gf == expected_gf).all()
31+
32+
H = np.eye(8)
33+
Hf = mesh.to_full_dof(scipy.sparse.csc_matrix(H)).toarray()
34+
expected_Hf = np.array([
35+
[1, 0, 0, 0, 0, 0],
36+
[0, 1, 0, 0, 0, 0],
37+
[0, 0, 2, 0, 1, 0],
38+
[0, 0, 0, 2, 0, 1],
39+
[0, 0, 1, 0, 2, 0],
40+
[0, 0, 0, 1, 0, 2],
41+
], dtype=float)
42+
assert (Hf == expected_Hf).all()
43+
44+
45+
def check_faces_to_edges(E, expected_F2E):
46+
F = np.array([[0, 1, 2]])
47+
assert (CollisionMesh.construct_faces_to_edges(F, E) == expected_F2E).all()
48+
49+
50+
def test_faces_to_edges():
51+
yield check_faces_to_edges, np.array([[0, 1], [1, 2], [2, 0]]), np.array([0, 1, 2])
52+
yield check_faces_to_edges, np.array([[2, 0], [2, 1], [1, 0]]), np.array([2, 1, 0])
53+
yield check_faces_to_edges, np.array([[0, 1], [2, 0], [2, 1]]), np.array([0, 2, 1])
54+
# Shouldnt work
55+
try:
56+
check_faces_to_edges(np.array([[0, 1], [1, 2], [0, 3]]), None)
57+
assert False
58+
except RuntimeError as e:
59+
assert str(e) == "Unable to find edge!"
60+
except:
61+
assert False
62+
63+
64+
def test_codim_points_collision_mesh():
65+
V = np.array([[0, 0], [1, 0], [0, 1], [1, 1]], dtype=float)
66+
mesh = CollisionMesh(V)
67+
expected_codim_vertices = np.array([0, 1, 2, 3], dtype=int)
68+
assert (mesh.codim_vertices == expected_codim_vertices).all()
69+
70+
71+
def test_collision_mesh_no_faces():
72+
# Based on https://github.com/ipc-sim/ipc-toolkit/issues/103
73+
points = np.array([[-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]])
74+
edges = np.array([[0, 1], [1, 2], [2, 0]])
75+
mesh = CollisionMesh(points, edges)
76+
assert mesh.faces.size == 0
77+
78+
79+
def test_collision_mesh_does_not_segfault():
80+
# Based on https://github.com/ipc-sim/ipc-toolkit/issues/102
81+
points = np.array([[-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]])
82+
edges = np.array([[0, 1], [1, 2], [2, 0]])
83+
faces = np.array([[0, 1, 2]])
84+
mesh = CollisionMesh(points, edges, faces)
85+
assert (mesh.rest_positions == points).all()
86+
assert (mesh.edges == edges).all()
87+
assert (mesh.faces == faces).all()

src/ipc/collision_mesh.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ class CollisionMesh {
2121
/// @param displacement_map The displacement mapping from displacements on the full mesh to the collision mesh.
2222
CollisionMesh(
2323
const Eigen::MatrixXd& rest_positions,
24-
const Eigen::MatrixXi& edges,
25-
const Eigen::MatrixXi& faces,
24+
const Eigen::MatrixXi& edges = Eigen::MatrixXi(),
25+
const Eigen::MatrixXi& faces = Eigen::MatrixXi(),
2626
const Eigen::SparseMatrix<double>& displacement_map =
2727
Eigen::SparseMatrix<double>());
2828

@@ -35,8 +35,8 @@ class CollisionMesh {
3535
CollisionMesh(
3636
const std::vector<bool>& include_vertex,
3737
const Eigen::MatrixXd& full_rest_positions,
38-
const Eigen::MatrixXi& edges,
39-
const Eigen::MatrixXi& faces,
38+
const Eigen::MatrixXi& edges = Eigen::MatrixXi(),
39+
const Eigen::MatrixXi& faces = Eigen::MatrixXi(),
4040
const Eigen::SparseMatrix<double>& displacement_map =
4141
Eigen::SparseMatrix<double>());
4242

@@ -48,7 +48,7 @@ class CollisionMesh {
4848
static CollisionMesh build_from_full_mesh(
4949
const Eigen::MatrixXd& full_rest_positions,
5050
const Eigen::MatrixXi& edges,
51-
const Eigen::MatrixXi& faces)
51+
const Eigen::MatrixXi& faces = Eigen::MatrixXi())
5252
{
5353
return CollisionMesh(
5454
construct_is_on_surface(full_rest_positions.rows(), edges),

tests/src/tests/broad_phase/test_broad_phase.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ TEST_CASE("Vertex-Vertex Broad Phase", "[ccd][broad_phase][2D]")
120120
E.row(0) << 1, 0;
121121
E.row(1) << 2, 3;
122122

123-
CollisionMesh mesh(V0, E, /*F=*/Eigen::MatrixXi());
123+
CollisionMesh mesh(V0, E);
124124

125125
const BroadPhaseMethod method = GENERATE_BROAD_PHASE_METHODS();
126126

@@ -144,8 +144,7 @@ TEST_CASE("Broad Phase: 2D Mesh", "[ccd][broad_phase][2D][.]")
144144
REQUIRE(igl::readCSV((tests::DATA_DIR / "mesh-2D/E.csv").string(), E));
145145
E.array() -= 1; // NOTE: Convert from OBJ format to index
146146

147-
CollisionMesh mesh = CollisionMesh::build_from_full_mesh(
148-
V0_full, E, /*F=*/Eigen::MatrixXi());
147+
CollisionMesh mesh = CollisionMesh::build_from_full_mesh(V0_full, E);
149148

150149
const Eigen::MatrixXd V0 = mesh.vertices(V0_full);
151150
const Eigen::MatrixXd V1 = mesh.vertices(V1_full);

tests/src/tests/collisions/test_collisions.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ TEST_CASE("Codim. vertex-vertex collisions", "[collisions][codim]")
2424
1, 1, 1;
2525
vertices.rowwise() -= vertices.colwise().mean();
2626

27-
CollisionMesh mesh(vertices, Eigen::MatrixXi(), Eigen::MatrixXi());
27+
CollisionMesh mesh(vertices);
2828
mesh.init_area_jacobians();
2929

3030
CHECK(mesh.num_vertices() == 8);
@@ -112,7 +112,7 @@ TEST_CASE("Codim. edge-vertex collisions", "[collisions][codim]")
112112
0, 3, //
113113
0, 4;
114114

115-
CollisionMesh mesh(vertices, edges, Eigen::MatrixXi());
115+
CollisionMesh mesh(vertices, edges);
116116
mesh.init_area_jacobians();
117117

118118
CHECK(mesh.num_vertices() == 8);

tests/src/tests/potential/test_barrier_potential.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,7 @@ TEST_CASE(
416416
Eigen::MatrixXd X = data["boundary_nodes_pos"];
417417
Eigen::MatrixXd vertices = data["displaced"];
418418

419-
CollisionMesh mesh = CollisionMesh::build_from_full_mesh(
420-
X, edges, /*faces=*/Eigen::MatrixXi());
419+
CollisionMesh mesh = CollisionMesh::build_from_full_mesh(X, edges);
421420
mesh.init_area_jacobians();
422421
REQUIRE(mesh.are_area_jacobians_initialized());
423422

@@ -537,8 +536,7 @@ TEST_CASE(
537536
Eigen::MatrixXd X = data["boundary_nodes_pos"];
538537
Eigen::MatrixXd vertices = data["displaced"];
539538

540-
CollisionMesh mesh = CollisionMesh::build_from_full_mesh(
541-
X, edges, /*faces=*/Eigen::MatrixXi());
539+
CollisionMesh mesh = CollisionMesh::build_from_full_mesh(X, edges);
542540
mesh.init_area_jacobians();
543541
REQUIRE(mesh.are_area_jacobians_initialized());
544542

tests/src/tests/test_collision_mesh.cpp

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ TEST_CASE("Collision mesh", "[collision_mesh]")
2121
Eigen::SparseMatrix<double> W(4, 3);
2222
W.setFromTriplets(weights.begin(), weights.end());
2323

24-
CollisionMesh mesh(V, E, Eigen::MatrixXi(), W);
24+
CollisionMesh mesh(V, E, /*F=*/Eigen::MatrixXi(), W);
2525

2626
Eigen::MatrixXd U(3, 2);
2727
U << 0, 0, 1, 1, 0, 0;
@@ -43,15 +43,7 @@ TEST_CASE("Collision mesh", "[collision_mesh]")
4343
expected_gf << 1, 1, -2, 0, 0, -2;
4444
CHECK(gf == expected_gf);
4545

46-
Eigen::MatrixXd H(8, 8);
47-
H << 1, 0, 0, 0, 0, 0, 0, 0, //
48-
0, 1, 0, 0, 0, 0, 0, 0, //
49-
0, 0, 1, 0, 0, 0, 0, 0, //
50-
0, 0, 0, 1, 0, 0, 0, 0, //
51-
0, 0, 0, 0, 1, 0, 0, 0, //
52-
0, 0, 0, 0, 0, 1, 0, 0, //
53-
0, 0, 0, 0, 0, 0, 1, 0, //
54-
0, 0, 0, 0, 0, 0, 0, 1; //
46+
Eigen::MatrixXd H = Eigen::MatrixXd::Identity(8, 8);
5547
Eigen::MatrixXd Hf =
5648
mesh.to_full_dof(Eigen::SparseMatrix<double>(H.sparseView()));
5749

@@ -113,7 +105,7 @@ TEST_CASE("Codim points collision mesh", "[collision_mesh]")
113105
Eigen::MatrixXd V(4, 2);
114106
V << 0, 0, 1, 0, 0, 1, 1, 1;
115107

116-
CollisionMesh mesh(V, Eigen::MatrixXi(), Eigen::MatrixXi());
108+
CollisionMesh mesh(V);
117109

118110
Eigen::VectorXi expected_codim_vertices(4);
119111
expected_codim_vertices << 0, 1, 2, 3;

0 commit comments

Comments
 (0)