Skip to content

Commit 7fb2b57

Browse files
committed
Add benchmark for MCUT
1 parent 43f41ba commit 7fb2b57

File tree

7 files changed

+203
-7
lines changed

7 files changed

+203
-7
lines changed

CMakeLists.txt

+9-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ option(KIGUMI_BUILD_CLI "Build the command-line interface" ON)
55
option(KIGUMI_BUILD_TESTS "Build the unit tests" ON)
66

77
if(KIGUMI_BUILD_BENCHES)
8-
list(APPEND VCPKG_MANIFEST_FEATURES "bench-geogram" "bench-manifold")
8+
list(APPEND VCPKG_MANIFEST_FEATURES "bench-geogram" "bench-manifold" "bench-mcut")
99
endif()
1010

1111
project(kigumi CXX)
@@ -76,6 +76,14 @@ if(KIGUMI_BUILD_BENCHES)
7676
set(MANIFOLD_TEST OFF CACHE BOOL "")
7777
FetchContent_MakeAvailable(manifold)
7878

79+
FetchContent_Declare(
80+
mcut
81+
GIT_REPOSITORY https://github.com/cutdigital/mcut.git
82+
GIT_TAG master
83+
GIT_SHALLOW ON
84+
)
85+
FetchContent_MakeAvailable(mcut)
86+
7987
add_subdirectory(benches)
8088
endif()
8189

README.md

+8-6
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ and [Boolean_region_builder.h](include/kigumi/Boolean_region_builder.h).
2121

2222
Here are the timings (in seconds) for computing the Boolean intersection between meshes, excluding I/O time:
2323

24-
| Test case | [coref.][coref] (seq.) | [geogram][geogram] (par.) | kigumi (seq.)¹ | kigumi (par.) | [manif.][manif] (seq.) | manif. (par.)² |
25-
|-------------------|-----------------------:|--------------------------:|---------------:|--------------:|-----------------------:|---------------:|
26-
| **Open** | 4.6 | FAILED | 2.4 | 1.3 | FAILED | FAILED |
27-
| **Open & closed** | FAILED | 70.5 | 1.6 | 0.9 | FAILED | FAILED |
28-
| **Closed** | 57.4 | FAILED | 5.3 | 2.7 | 8.9 | 1.7 |
29-
| **Non-manifold** | FAILED | FAILED | 0.5 | 0.3 | FAILED | FAILED |
24+
| Test case | [coref.][coref] (seq.) | [geogram][geogram] (par.) | kigumi (seq.)¹ | kigumi (par.) | [manif.][manif] (seq.) | manif. (par.)² | [MCUT][mcut] (par.) |
25+
|-------------------|-----------------------:|--------------------------:|---------------:|--------------:|-----------------------:|---------------:|--------------------:|
26+
| **Open** | 4.6 | FAILED | 2.4 | 1.3 | FAILED | FAILED | FAILED |
27+
| **Open & closed** | FAILED | 70.5 | 1.6 | 0.9 | FAILED | FAILED | FAILED |
28+
| **Closed** | 57.4 | FAILED | 5.3 | 2.7 | 8.9 | 1.7 | 24.5 |
29+
| **Non-manifold** | FAILED | FAILED | 0.5 | 0.3 | FAILED | FAILED | FAILED |
3030

3131
¹ Ran with `KIGUMI_NUM_THREADS=1`. ² Configured with `-DMANIFOLD_PAR=TBB`.
3232

@@ -101,3 +101,5 @@ An enhanced version of the algorithm described in [[1]](#1) is used.
101101
[geogram]: https://github.com/BrunoLevy/geogram
102102

103103
[manif]: https://github.com/elalish/manifold
104+
105+
[mcut]: https://github.com/cutdigital/mcut

benches/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ add_subdirectory(corefinement)
22
add_subdirectory(geogram)
33
add_subdirectory(kigumi)
44
add_subdirectory(manifold)
5+
add_subdirectory(mcut)

benches/mcut/CMakeLists.txt

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
set(TARGET kigumi_bench_mcut)
2+
3+
add_executable(${TARGET}
4+
main.cc
5+
)
6+
7+
set_target_properties(${TARGET} PROPERTIES
8+
OUTPUT_NAME mcut
9+
)
10+
11+
if(UNIX)
12+
target_compile_options(${TARGET} PRIVATE -Wall -Wextra -Werror)
13+
elseif(MSVC)
14+
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd4702)
15+
endif()
16+
17+
target_include_directories(${TARGET} PRIVATE
18+
${MCUT_INCLUDE_DIR}
19+
)
20+
21+
target_link_libraries(${TARGET} PRIVATE
22+
kigumi
23+
mcut
24+
)

benches/mcut/main.cc

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
2+
#include <CGAL/number_utils.h>
3+
#include <kigumi/Mesh_indices.h>
4+
#include <kigumi/Triangle_soup.h>
5+
#include <kigumi/Triangle_soup_io.h>
6+
#include <mcut/mcut.h>
7+
8+
#include <algorithm>
9+
#include <chrono>
10+
#include <exception>
11+
#include <iostream>
12+
#include <stdexcept>
13+
#include <string>
14+
#include <vector>
15+
16+
using K = CGAL::Exact_predicates_exact_constructions_kernel;
17+
using Triangle_soup = kigumi::Triangle_soup<K>;
18+
using kigumi::read_triangle_soup;
19+
using kigumi::Vertex_index;
20+
using kigumi::write_triangle_soup;
21+
22+
namespace {
23+
24+
struct Mesh {
25+
std::vector<double> vertices;
26+
std::vector<McUint32> vertex_indices;
27+
std::vector<McUint32> face_sizes;
28+
McUint32 num_vertices;
29+
McUint32 num_faces;
30+
};
31+
32+
bool read_mesh(const std::string& filename, Mesh& mesh) {
33+
Triangle_soup soup;
34+
if (!read_triangle_soup(filename, soup)) {
35+
return false;
36+
}
37+
38+
for (auto vi : soup.vertices()) {
39+
const auto& p = soup.point(vi);
40+
mesh.vertices.push_back(CGAL::to_double(p.x()));
41+
mesh.vertices.push_back(CGAL::to_double(p.y()));
42+
mesh.vertices.push_back(CGAL::to_double(p.z()));
43+
}
44+
for (auto fi : soup.faces()) {
45+
const auto& f = soup.face(fi);
46+
mesh.vertex_indices.push_back(f[0].idx());
47+
mesh.vertex_indices.push_back(f[1].idx());
48+
mesh.vertex_indices.push_back(f[2].idx());
49+
mesh.face_sizes.push_back(3);
50+
}
51+
mesh.num_vertices = soup.num_vertices();
52+
mesh.num_faces = soup.num_faces();
53+
54+
return true;
55+
}
56+
57+
bool write_mesh(const std::string& filename, const Mesh& mesh) {
58+
Triangle_soup soup;
59+
for (McUint32 i = 0; i < mesh.num_vertices; ++i) {
60+
soup.add_vertex(
61+
{mesh.vertices.at(3 * i), mesh.vertices.at(3 * i + 1), mesh.vertices.at(3 * i + 2)});
62+
}
63+
for (McUint32 i = 0; i < mesh.num_faces; ++i) {
64+
soup.add_face({Vertex_index(mesh.vertex_indices.at(3 * i)),
65+
Vertex_index(mesh.vertex_indices.at(3 * i + 1)),
66+
Vertex_index(mesh.vertex_indices.at(3 * i + 2))});
67+
}
68+
69+
return write_triangle_soup(filename, soup);
70+
}
71+
72+
void check(McResult result) {
73+
if (result != MC_NO_ERROR) {
74+
throw std::runtime_error("mcut failed");
75+
}
76+
}
77+
78+
} // namespace
79+
80+
int main(int argc, char* argv[]) {
81+
try {
82+
std::vector<std::string> args(argv + 1, argv + argc);
83+
84+
Mesh first;
85+
Mesh second;
86+
87+
read_mesh(args.at(0), first);
88+
read_mesh(args.at(1), second);
89+
90+
McContext context = MC_NULL_HANDLE;
91+
check(mcCreateContext(&context, MC_NULL_HANDLE));
92+
93+
McFlags dispatch_flags =
94+
MC_DISPATCH_VERTEX_ARRAY_DOUBLE | MC_DISPATCH_ENFORCE_GENERAL_POSITION |
95+
MC_DISPATCH_FILTER_FRAGMENT_SEALING_INSIDE | MC_DISPATCH_FILTER_FRAGMENT_LOCATION_BELOW;
96+
97+
auto start = std::chrono::high_resolution_clock::now();
98+
check(mcDispatch(context, dispatch_flags, first.vertices.data(), first.vertex_indices.data(),
99+
first.face_sizes.data(), first.num_vertices, first.num_faces,
100+
second.vertices.data(), second.vertex_indices.data(), second.face_sizes.data(),
101+
second.num_vertices, second.num_faces));
102+
auto end = std::chrono::high_resolution_clock::now();
103+
std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start) << std::endl;
104+
105+
McUint32 num_ccs = 0;
106+
McConnectedComponentType cc_types = static_cast<McConnectedComponentType>(
107+
MC_CONNECTED_COMPONENT_TYPE_FRAGMENT | MC_CONNECTED_COMPONENT_TYPE_PATCH);
108+
check(mcGetConnectedComponents(context, cc_types, 0, nullptr, &num_ccs));
109+
std::vector<McConnectedComponent> ccs(num_ccs, MC_NULL_HANDLE);
110+
check(mcGetConnectedComponents(context, cc_types, num_ccs, ccs.data(), nullptr));
111+
112+
Mesh result;
113+
114+
for (McUint32 i = 0; i < num_ccs; ++i) {
115+
McConnectedComponent cc = ccs.at(i);
116+
McSize num_bytes = 0;
117+
118+
check(mcGetConnectedComponentData(context, cc, MC_CONNECTED_COMPONENT_DATA_VERTEX_DOUBLE, 0,
119+
nullptr, &num_bytes));
120+
McUint32 num_vertices = num_bytes / (3 * sizeof(double));
121+
result.vertices.resize(3 * (result.num_vertices + num_vertices));
122+
check(mcGetConnectedComponentData(context, cc, MC_CONNECTED_COMPONENT_DATA_VERTEX_DOUBLE,
123+
num_bytes, result.vertices.data() + 3 * result.num_vertices,
124+
nullptr));
125+
126+
check(mcGetConnectedComponentData(context, cc, MC_CONNECTED_COMPONENT_DATA_FACE_TRIANGULATION,
127+
0, nullptr, &num_bytes));
128+
McUint32 num_faces = num_bytes / (3 * sizeof(McUint32));
129+
result.vertex_indices.resize(3 * (result.num_faces + num_faces));
130+
check(mcGetConnectedComponentData(
131+
context, cc, MC_CONNECTED_COMPONENT_DATA_FACE_TRIANGULATION, num_bytes,
132+
result.vertex_indices.data() + 3 * result.num_faces, nullptr));
133+
std::transform(result.vertex_indices.begin() + 3 * result.num_faces,
134+
result.vertex_indices.end(),
135+
result.vertex_indices.begin() + 3 * result.num_faces,
136+
[offset = result.num_vertices](McUint32 idx) { return offset + idx; });
137+
138+
result.num_vertices += num_vertices;
139+
result.num_faces += num_faces;
140+
}
141+
142+
result.face_sizes.resize(result.num_faces, 3);
143+
144+
mcReleaseConnectedComponents(context, 0, nullptr);
145+
mcReleaseContext(context);
146+
147+
write_mesh(args.at(2), result);
148+
149+
return 0;
150+
} catch (const std::exception& e) {
151+
std::cerr << "error: " << e.what() << std::endl;
152+
return 1;
153+
} catch (...) {
154+
std::cerr << "unknown error" << std::endl;
155+
return 1;
156+
}
157+
}

tools/bench_methods.txt

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ corefinement
22
geogram
33
kigumi
44
manifold
5+
mcut

vcpkg.json

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
},
2929
"tbb"
3030
]
31+
},
32+
"bench-mcut": {
33+
"description": "Build the benchmark for MCUT"
3134
}
3235
}
3336
}

0 commit comments

Comments
 (0)