Skip to content

Commit d446085

Browse files
committed
Add tests & docs for ccMesh
1 parent 57914ad commit d446085

6 files changed

Lines changed: 265 additions & 5 deletions

File tree

docs/examples.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ Creating a ccPointCloud from Python
99
:language: Python
1010

1111

12+
Using ccMesh
13+
-----------------------------------
14+
15+
.. literalinclude:: ../script_examples/creating_a_mesh.py
16+
:language: Python
17+
1218
Loading a file
1319
--------------
1420

script_examples/creating_a_mesh.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""
2+
An example of how to create a mesh or query a mesh
3+
"""
4+
import cccorelib
5+
import pycc
6+
import numpy as np
7+
8+
# Vertices of the cube
9+
VERTICES = np.array([
10+
[-0.5, -0.5, -0.5],
11+
[-0.5, -0.5, 0.5],
12+
[-0.5, 0.5, -0.5],
13+
[-0.5, 0.5, 0.5],
14+
[0.5, -0.5, -0.5],
15+
[0.5, -0.5, 0.5],
16+
[0.5, 0.5, -0.5],
17+
[0.5, 0.5, 0.5]
18+
])
19+
20+
# Indices of the cube
21+
INDICES = np.array([
22+
[0, 1, 3],
23+
[0, 3, 2],
24+
[0, 2, 4],
25+
[2, 6, 4],
26+
[0, 4, 1],
27+
[1, 4, 5],
28+
[2, 3, 6],
29+
[3, 7, 6],
30+
[4, 6, 5],
31+
[5, 6, 7],
32+
[1, 5, 7],
33+
[1, 7, 3]
34+
])
35+
36+
37+
def main():
38+
# Creating mesh
39+
vertices = pycc.ccPointCloud(VERTICES[:, 0], VERTICES[:, 1], VERTICES[:, 2])
40+
mesh = pycc.ccMesh(vertices)
41+
for (i1, i2, i3) in INDICES:
42+
mesh.addTriangle(i1, i2, i3)
43+
44+
assert vertices.size() == len(VERTICES)
45+
assert mesh.size() == len(INDICES)
46+
assert vertices == mesh.getAssociatedCloud()
47+
48+
# Query triangles
49+
x, y, z = cccorelib.CCVector3(), cccorelib.CCVector3(), cccorelib.CCVector3()
50+
for i in range(mesh.size()):
51+
vert_indices = mesh.getTriangleVertIndexes(i)
52+
assert vert_indices.i1 == INDICES[i, 0]
53+
assert vert_indices.i2 == INDICES[i, 1]
54+
assert vert_indices.i3 == INDICES[i, 2]
55+
56+
for j in range(3):
57+
assert vert_indices[j] == INDICES[i, j]
58+
59+
vertices.getPoint(vert_indices.i1, x)
60+
vertices.getPoint(vert_indices.i2, y)
61+
vertices.getPoint(vert_indices.i3, z)
62+
63+
# or
64+
mesh.getTriangleVertices(i, x, y, z)
65+
66+
CC = pycc.GetInstance()
67+
CC.addToDB(mesh)
68+
CC.updateUI()
69+
70+
if __name__ == '__main__':
71+
main()

wrapper/cccorelib/src/GenericMesh.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,21 @@ using namespace pybind11::literals;
2525

2626
void define_GenericMesh(py::module &cccorelib)
2727
{
28-
py::class_<CCCoreLib::GenericMesh>(cccorelib, "GenericMesh")
29-
.def("size", &CCCoreLib::GenericMesh::size)
28+
py::class_<CCCoreLib::GenericMesh>(cccorelib, "GenericMesh", R"pbdoc(
29+
A generic mesh interface for data communication between library and client applications
30+
)pbdoc")
31+
.def("size", &CCCoreLib::GenericMesh::size, R"pbdoc(
32+
Returns the number of triangles
33+
)pbdoc")
3034
.def("forEach", &CCCoreLib::GenericMesh::forEach, "action"_a)
31-
.def("getBoundingBox", &CCCoreLib::GenericMesh::getBoundingBox, "bbMin"_a, "bbMax"_a)
35+
.def("getBoundingBox", &CCCoreLib::GenericMesh::getBoundingBox, "bbMin"_a, "bbMax"_a, R"pbdoc(
36+
Returns the mesh bounding-box
37+
38+
Parameters
39+
----------
40+
bbMin: out parameter, lower bounding-box limits (Xmin,Ymin,Zmin)
41+
bbMax: out parameter, higher bounding-box limits (Xmax,Ymax,Zmax)
42+
)pbdoc")
3243
.def("placeIteratorAtBeginning", &CCCoreLib::GenericMesh::placeIteratorAtBeginning)
3344
.def("_getNextTriangle",
3445
&CCCoreLib::GenericMesh::_getNextTriangle,

wrapper/pycc/src/qcc_db/ccMesh.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,43 @@
2222
#include <ccGenericMesh.h>
2323
#include <ccGenericPointCloud.h>
2424
#include <ccMesh.h>
25+
#include <ccPointCloud.h>
2526

2627
namespace py = pybind11;
2728
using namespace pybind11::literals;
2829

2930
void define_ccMesh(py::module &m)
3031
{
3132
py::class_<ccMesh, ccGenericMesh>(m, "ccMesh")
32-
.def("setAssociatedCloud", &ccMesh::setAssociatedCloud, "cloud"_a);
33+
.def(
34+
py::init<ccGenericPointCloud *, unsigned>(),
35+
"vertices"_a,
36+
"uniquedID"_a = []() { return ccUniqueIDGenerator::InvalidUniqueID; }(),
37+
// Keep the cloud (2) alive while the mesh (1) is alive py::keep_alive<1,
38+
// 2>())
39+
40+
// one could have expected <0, 1> as the mesh is the return value in C++
41+
// but in python __init__ there is a self/this as arg 1
42+
py::keep_alive<1, 2>())
43+
.def("setAssociatedCloud",
44+
&ccMesh::setAssociatedCloud,
45+
"cloud"_a,
46+
py::keep_alive<1, 2>() /* Keep the cloud (2) alive while the mesh (1) is alive */)
47+
.def("addTriangle",
48+
&ccMesh::addTriangle,
49+
"i1"_a,
50+
"i2"_a,
51+
"i3"_a,
52+
R"pbdoc(
53+
Adds a triangle to the mesh
54+
Bounding-box validity is broken after a call to this method.
55+
However, for the sake of performance, no call to notifyGeometryUpdate
56+
is made automatically. Make sure to do so when all modifications are done!
57+
58+
Parameters
59+
----------
60+
i1 first vertex index (relatively to the vertex cloud)
61+
i2 second vertex index (relatively to the vertex cloud)
62+
i3 third vertex index (relatively to the vertex cloud)
63+
)pbdoc");
3364
}

wrapper/pycc/src/qcc_db/ccObject.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,10 @@ void define_ccObject(py::module &m)
292292
"trans"_a = nullptr)
293293
.def("getGLTransformationHistory", &ccHObject::getGLTransformationHistory)
294294
.def("setGLTransformationHistory", &ccHObject::setGLTransformationHistory, "mat"_a)
295-
.def("resetGLTransformationHistory", &ccHObject::resetGLTransformationHistory);
295+
.def("resetGLTransformationHistory", &ccHObject::resetGLTransformationHistory)
296+
.def("notifyGeometryUpdate", &ccHObject::notifyGeometryUpdate, R"pbdoc(
297+
Notifies all dependent entities that the geometry of this entity has changed
298+
)pbdoc");
296299

297300
py::class_<ccShiftedObject, ccHObject>(m, "ccShiftedObject")
298301
.def("setGlobalShift",

wrapper/pycc/tests/test_cc_mesh.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import cccorelib
2+
import numpy as np
3+
import pycc
4+
import pytest
5+
6+
# Vertices of the cube
7+
VERTICES = np.array([
8+
[-0.5, -0.5, -0.5],
9+
[-0.5, -0.5, 0.5],
10+
[-0.5, 0.5, -0.5],
11+
[-0.5, 0.5, 0.5],
12+
[0.5, -0.5, -0.5],
13+
[0.5, -0.5, 0.5],
14+
[0.5, 0.5, -0.5],
15+
[0.5, 0.5, 0.5]
16+
])
17+
18+
# Indices of the cube
19+
INDICES = np.array([
20+
[0, 1, 3],
21+
[0, 3, 2],
22+
[0, 2, 4],
23+
[2, 6, 4],
24+
[0, 4, 1],
25+
[1, 4, 5],
26+
[2, 3, 6],
27+
[3, 7, 6],
28+
[4, 6, 5],
29+
[5, 6, 7],
30+
[1, 5, 7],
31+
[1, 7, 3]
32+
])
33+
34+
def assert_ccvector_are_equal(a: cccorelib.CCVector3, b: cccorelib.CCVector3):
35+
assert a.x == b.x
36+
assert a.y == b.y
37+
assert a.z == b.z
38+
39+
def expected_points_of_triangle(triangle_index):
40+
x = cccorelib.CCVector3(
41+
VERTICES[INDICES[triangle_index, 0], 0],
42+
VERTICES[INDICES[triangle_index, 0], 1],
43+
VERTICES[INDICES[triangle_index, 0], 2]
44+
)
45+
y = cccorelib.CCVector3(
46+
VERTICES[INDICES[triangle_index, 1], 0],
47+
VERTICES[INDICES[triangle_index, 1], 1],
48+
VERTICES[INDICES[triangle_index, 1], 2]
49+
)
50+
z = cccorelib.CCVector3(
51+
VERTICES[INDICES[triangle_index, 2], 0],
52+
VERTICES[INDICES[triangle_index, 2], 1],
53+
VERTICES[INDICES[triangle_index, 2], 2]
54+
)
55+
return x, y, z
56+
57+
58+
def build_cube_mesh():
59+
vertices = pycc.ccPointCloud(VERTICES[:, 0], VERTICES[:, 1], VERTICES[:, 2])
60+
mesh = pycc.ccMesh(vertices)
61+
for (i1, i2, i3) in INDICES:
62+
mesh.addTriangle(i1, i2, i3)
63+
64+
return mesh, vertices
65+
66+
67+
def test_building_mesh():
68+
mesh, vertices = build_cube_mesh()
69+
assert vertices.size() == len(VERTICES)
70+
assert mesh.size() == len(INDICES)
71+
assert vertices == mesh.getAssociatedCloud()
72+
73+
74+
def test_mesh_lifetime_1():
75+
mesh, vertices = build_cube_mesh()
76+
assert vertices == mesh.getAssociatedCloud()
77+
del mesh
78+
# If mesh is deleted, vertices should still be alive
79+
assert vertices.size() == len(VERTICES)
80+
81+
82+
def test_mesh_lifetime_2():
83+
mesh, vertices = build_cube_mesh()
84+
assert vertices == mesh.getAssociatedCloud()
85+
del vertices
86+
# If vertices is deleted, then actually the mesh still have access to it
87+
# i.e. the mesh keeps it alive.
88+
# In CPP API, there is a HOjbect dependency between the two, and so when
89+
# the vertices are deleted, the mesh is notified of it and empties himself, so it is actually safe
90+
# to delete the vertices, but as lifetime is not manual in Python, it's better to keep it alive
91+
assert mesh.size() == len(INDICES)
92+
assert mesh.getAssociatedCloud().size() == len(VERTICES)
93+
94+
95+
def test_getTriangleVertIndexes():
96+
x, y, z = cccorelib.CCVector3(), cccorelib.CCVector3(), cccorelib.CCVector3()
97+
mesh, vertices = build_cube_mesh()
98+
99+
for i in range(mesh.size()):
100+
vert_indices = mesh.getTriangleVertIndexes(i)
101+
assert vert_indices.i1 == INDICES[i, 0]
102+
assert vert_indices.i2 == INDICES[i, 1]
103+
assert vert_indices.i3 == INDICES[i, 2]
104+
105+
for j in range(3):
106+
assert vert_indices[j] == INDICES[i, j]
107+
108+
vertices.getPoint(vert_indices.i1, x)
109+
vertices.getPoint(vert_indices.i2, y)
110+
vertices.getPoint(vert_indices.i3, z)
111+
ex, ey, ez = expected_points_of_triangle(i)
112+
assert_ccvector_are_equal(x, ex)
113+
assert_ccvector_are_equal(y, ey)
114+
assert_ccvector_are_equal(z, ez)
115+
116+
with pytest.raises(IndexError):
117+
mesh.getTriangleVertIndexes(mesh.size())
118+
119+
with pytest.raises(IndexError):
120+
mesh.getTriangleVertIndexes(mesh.size() + 1)
121+
122+
123+
def test_getTriangleVertices():
124+
x, y, z = cccorelib.CCVector3(), cccorelib.CCVector3(), cccorelib.CCVector3()
125+
mesh, _ = build_cube_mesh()
126+
127+
for i in range(mesh.size()):
128+
mesh.getTriangleVertices(i, x, y, z)
129+
ex, ey, ez = expected_points_of_triangle(i)
130+
assert_ccvector_are_equal(x, ex)
131+
assert_ccvector_are_equal(y, ey)
132+
assert_ccvector_are_equal(z, ez)
133+
134+
with pytest.raises(IndexError):
135+
mesh.getTriangleVertices(mesh.size(), x, y, z)
136+
137+
with pytest.raises(IndexError):
138+
mesh.getTriangleVertices(mesh.size() + 1, x, y, z)

0 commit comments

Comments
 (0)