├── .gitignore
├── .gitmodules
├── CMakeLists.txt
├── README.md
├── app
├── edit_labeling.cpp
├── evolabel.cpp
├── figure_generator.cpp
├── flagging_viewer.cpp
├── igl_core.cpp
├── init_from_folder.cpp
├── jacob_test.cpp
├── measurement.cpp
├── polycube_withHexEx.cpp
├── supplemental_generator.cpp
├── synthesize_data.cpp
├── test_scaled_jacobian.cpp
└── viz_tet.cpp
├── data
├── grey_checkerboard.png
└── moai.obj
├── images
└── lowres_evocube_teaser.png
├── include
├── HexExWrapper.h
├── MyEmbreeRenderer.h
├── archive.h
├── chart.h
├── disjointset.h
├── distortion.h
├── evaluator.h
├── evocube.h
├── flagging_utils.h
├── graphcut_labeling.h
├── labeling_individual.h
├── labeling_ops.h
├── latex.h
├── logging.h
├── mesh_io.h
├── minSJ.txt
├── quick_label_ev.h
├── scaled_jacobian.h
└── tet_boundary.h
├── lib
├── OpenNL_psm
│ ├── CMakeLists.txt
│ ├── OpenNL_example.c
│ ├── OpenNL_psm.c
│ ├── OpenNL_psm.h
│ └── README.txt
└── gco
│ ├── CMakeLists.txt
│ ├── GCO_README.TXT
│ └── include
│ ├── GCoptimization.cpp
│ ├── GCoptimization.h
│ ├── LinkedBlockList.cpp
│ ├── LinkedBlockList.h
│ ├── block.h
│ ├── energy.h
│ ├── example.cpp
│ ├── graph.cpp
│ ├── graph.h
│ └── maxflow.cpp
├── scripts
├── obj_to_stl.py
├── plot_SJ_table.ipynb
├── step_to_tet.py
└── stl_to_tet.py
├── src
├── HexExWrapper.cpp
├── MyEmbreeRenderer.cpp
├── chart.cpp
├── distortion.cpp
├── flagging_utils.cpp
├── graphcut_labeling.cpp
├── labeling_ops.cpp
├── latex.cpp
├── logging.cpp
├── quick_label_ev.cpp
└── tet_boundary.cpp
└── supplemental
└── cover_page.tex
/.gitignore:
--------------------------------------------------------------------------------
1 | build/*
2 | .vscode/*
3 | data/*
4 | .ipynb_checkpoints
5 | # in supplemental/, include only the cover page
6 | # https://riptutorial.com/git/example/911/exceptions-in-a--gitignore-file
7 | !supplemental/
8 | supplemental/*
9 | !supplemental/cover_page.tex
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lib/libigl"]
2 | path = lib/libigl
3 | url = https://github.com/libigl/libigl.git
4 | [submodule "lib/json"]
5 | path = lib/json
6 | url = https://github.com/nlohmann/json.git
7 | [submodule "lib/libHexEx"]
8 | path = lib/libHexEx
9 | url = https://gitlab.vci.rwth-aachen.de:9000/HexEx/libHexEx.git
10 | [submodule "lib/OpenVolumeMesh"]
11 | path = lib/OpenVolumeMesh
12 | url = https://gitlab.vci.rwth-aachen.de:9000/OpenVolumeMesh/OpenVolumeMesh.git
13 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | cmake_minimum_required(VERSION 3.1)
3 | project(evocube)
4 | set(CMAKE_CXX_STANDARD 17)
5 | file(GLOB SRCFILES src/*.cpp)
6 |
7 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
8 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/libHexEx/cmake)
9 | set(OPENVOLUMEMESH_INCLUDE_DIR
10 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/OpenVolumeMesh/src)
11 |
12 | add_definitions(-DIGL_VIEWER_VIEWER_QUIET)
13 | set(LIBIGL_USE_STATIC_LIBRARY ON)
14 | add_subdirectory(lib/libigl)
15 | add_subdirectory(lib/gco)
16 | add_subdirectory(lib/OpenNL_psm)
17 | add_subdirectory(lib/OpenVolumeMesh)
18 | add_subdirectory(lib/libHexEx)
19 |
20 | find_package(OpenMP REQUIRED)
21 |
22 | include_directories(include)
23 | include_directories(lib/json/include)
24 | include_directories(lib/gco/include)
25 | include_directories(lib/OpenNL_psm)
26 | include_directories(lib/OpenVolumeMesh)
27 | include_directories(lib/libHexEx/src)
28 |
29 | SET(IGL_VIEWER_LIBS
30 | igl::core
31 | igl::opengl_glfw
32 | igl::opengl_glfw_imgui
33 | )
34 |
35 | SET(EVOCUBE_LIBS
36 | gco
37 | OpenMP::OpenMP_CXX
38 | OpenNL_psm
39 | OpenVolumeMesh
40 | HexEx
41 | igl::embree
42 | )
43 |
44 | add_executable(viz_tet app/viz_tet.cpp src/flagging_utils.cpp src/tet_boundary.cpp src/logging.cpp)
45 | target_link_libraries(viz_tet ${IGL_VIEWER_LIBS} igl::tetgen igl::png)
46 |
47 | add_executable(jacob_test app/jacob_test.cpp ${SRCFILES})
48 | target_link_libraries(jacob_test ${IGL_VIEWER_LIBS} ${EVOCUBE_LIBS})
49 |
50 | #add_executable(flagging_viewer app/flagging_viewer.cpp ${SRCFILES})
51 | #target_link_libraries(flagging_viewer ${IGL_VIEWER_LIBS} OpenMP::OpenMP_CXX)
52 |
53 | add_executable(edit_labeling app/edit_labeling.cpp ${SRCFILES})
54 | target_link_libraries(edit_labeling ${EVOCUBE_LIBS} ${IGL_VIEWER_LIBS})
55 |
56 | add_executable(evolabel app/evolabel.cpp ${SRCFILES})
57 | target_link_libraries(evolabel ${IGL_VIEWER_LIBS} ${EVOCUBE_LIBS} igl::tetgen)
58 |
59 | add_executable(init_from_folder app/init_from_folder.cpp ${SRCFILES})
60 | target_link_libraries(init_from_folder igl::core igl::tetgen ${EVOCUBE_LIBS})
61 |
62 | add_executable(polycube_withHexEx app/polycube_withHexEx.cpp ${SRCFILES})
63 | target_link_libraries(polycube_withHexEx igl::core igl::tetgen ${EVOCUBE_LIBS})
64 |
65 | add_executable(test_scaled_jacobian app/test_scaled_jacobian.cpp ${SRCFILES})
66 | target_link_libraries(test_scaled_jacobian igl::core igl::tetgen ${EVOCUBE_LIBS})
67 |
68 | add_executable(measurement app/measurement.cpp ${SRCFILES})
69 | target_link_libraries(measurement igl::core igl::tetgen ${EVOCUBE_LIBS})
70 |
71 | add_executable(figure_generator ${SRCFILES} app/figure_generator.cpp)
72 | include_directories(include)
73 | target_link_libraries(figure_generator igl::core igl::tetgen igl::opengl_glfw igl::png ${EVOCUBE_LIBS})
74 |
75 | add_executable(supplemental_generator ${SRCFILES} app/supplemental_generator.cpp)
76 | include_directories(include)
77 | target_link_libraries(supplemental_generator igl::core igl::tetgen ${EVOCUBE_LIBS})
78 |
79 | add_executable(synthesize_data ${SRCFILES} app/synthesize_data.cpp)
80 | include_directories(include)
81 | target_link_libraries(synthesize_data igl::core igl::tetgen ${EVOCUBE_LIBS})
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Evocube: a Genetic Labeling Framework for Polycube-Maps
4 |
5 | [Corentin Dumery](https://corentindumery.github.io),
6 | [François Protais](https://www.linkedin.com/in/françois-protais-78b9ab13b/),
7 | [Sébastien Mestrallet](https://sebastienmestrallet.fr/),
8 | [Christophe Bourcier](https://www.researchgate.net/profile/Christophe-Bourcier),
9 | [Franck Ledoux](https://www.linkedin.com/in/franck-ledoux-32b99646/)
10 |
11 |
12 |
13 | Watch the video presentation on the [project page](https://corentindumery.github.io/projects/evocube.html).
14 |
15 | 
16 |
17 | ## Abstract
18 | Polycube-maps are used as base-complexes in various fields of computational geometry, including the generation of regular all-hexahedral meshes free of internal singularities. However, the strict alignment constraints behind polycube-based methods
19 | make their computation challenging for CAD models used in numerical simulation via Finite Element Method (FEM). We
20 | propose a novel approach based on an evolutionary algorithm to robustly compute polycube-maps in this context.
21 |
22 | We address the labeling problem, which aims to precompute polycube alignment by assigning one of the base axes to each
23 | boundary face on the input. Previous research has described ways to initialize and improve a labeling via greedy local fixes.
24 | However, such algorithms lack robustness and often converge to inaccurate solutions for complex geometries. Our proposed
25 | framework alleviates this issue by embedding labeling operations in an evolutionary heuristic, defining fitness, crossover, and
26 | mutations in the context of labeling optimization. We evaluate our method on a thousand smooth and CAD meshes, showing
27 | Evocube converges to valid labelings on a wide range of shapes. The limitations of our method are also discussed thoroughly.
28 |
29 |
30 | ## BUILD
31 |
32 | ### Download
33 | ```
34 | git clone https://github.com/LIHPC-Computational-Geometry/evocube.git
35 | git submodule update --init --recursive
36 | ```
37 |
38 | ### Compile
39 |
40 | ```
41 | mkdir build
42 | cd build
43 | cmake ..
44 | make -j4 evolabel
45 | ```
46 |
47 |
48 | ### Run
49 | To compute a labeling on a triangle mesh and visualize it, simply run:
50 | ```
51 | ./evolabel path/to/boundary.obj
52 | ```
53 |
54 | Paste for a quick test: `./evolabel ../data/moai.obj`
55 |
56 | To go further, advanced tools used in our paper are showcased in `init_from_folder.cpp`.
57 |
58 | ### Test data
59 |
60 | * [Mambo](https://gitlab.com/franck.ledoux/mambo)
61 | * [OM Smooth & CAD](https://cims.nyu.edu/gcl/papers/2019-OctreeMeshing.zip)
62 | * [ABC](https://deep-geometry.github.io/abc-dataset/)
63 |
64 | ### Useful repositories
65 |
66 | * [Advanced polycube hex-meshing pipeline (our labelings can be used as input)](https://github.com/fprotais/robustPolycube)
67 | * [Simpler hex-meshing pipeline with libHexEx](https://github.com/fprotais/polycube_withHexEx)
68 | * [Tet mesh preprocessing](https://github.com/fprotais/preprocess_polycube)
69 |
70 | ## Citation
71 | ```
72 | @article{dumery:evocube,
73 | title = {{Evocube: a Genetic Labeling Framework for Polycube-Maps}},
74 | author = {Dumery, Corentin and Protais, Fran{\c c}ois and Mestrallet, S{\'e}bastien and Bourcier, Christophe and Ledoux, Franck},
75 | url = {https://doi.org/10.1111/cgf.14649},
76 | journal = {{Computer Graphics Forum}},
77 | publisher = {{Wiley}},
78 | year = {2022},
79 | month = Aug,
80 | doi = {10.1111/cgf.14649},
81 | volume = {41},
82 | number = {6},
83 | pages = {467--479},
84 | }
85 | ```
86 | ## License
87 | [GPL3](LICENSE) license
88 | ([FAQ](https://www.gnu.org/licenses/gpl-faq.html))
89 |
90 |
110 |
111 |
--------------------------------------------------------------------------------
/app/edit_labeling.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "flagging_utils.h"
11 | #include "labeling_individual.h"
12 | #include "evocube.h"
13 | #include "quick_label_ev.h"
14 | #include "evaluator.h"
15 |
16 | int main(int argc, char *argv[]){
17 |
18 | Eigen::MatrixXd V;
19 | Eigen::MatrixXi F;
20 | std::string input_path = argv[1];
21 | igl::readOBJ(input_path, V, F);
22 | double l_avg = igl::avg_edge_length(V, F);
23 |
24 | Eigen::VectorXi labeling = Eigen::VectorXi::Constant(F.rows(), 1);
25 | Eigen::MatrixXd colors;
26 |
27 | std::shared_ptr evo = std::make_shared(Evocube(V, F));
28 | std::shared_ptr qle = std::make_shared(QuickLabelEv(V, F));
29 | const Evaluator evaluator(evo, qle);
30 | std::shared_ptr indiv = std::make_shared(LabelingIndividual(evo, qle, labeling));
31 |
32 | double labeling_score = -1.0;
33 | double fast_poly_score, invalid_score;
34 | int n_fail_invert;
35 | auto updateLabeling = [&](){
36 | indiv = std::make_shared(LabelingIndividual(evo, qle, labeling));
37 | indiv->updateChartsAndTPs(true);
38 | labeling_score = evaluator.evaluate(*indiv, fast_poly_score, invalid_score, n_fail_invert);
39 | };
40 |
41 |
42 | // --- VISUALIZATION ---
43 |
44 | int insert_label = 0;
45 | float insert_size = 1.0;
46 |
47 | igl::opengl::glfw::Viewer viewer;
48 | viewer.data().set_mesh(V, F);
49 |
50 | igl::opengl::glfw::imgui::ImGuiMenu menu;
51 | menu.callback_draw_viewer_window = []() {};
52 | viewer.plugins.push_back(&menu);
53 |
54 | auto updateViz = [&](){
55 | viewer.data().clear_edges();
56 | viewer.data().clear_points();
57 | colors = colorsFromFlagging(labeling);
58 | viewer.data().set_colors(colors);
59 |
60 | };
61 |
62 | //helper function for menu
63 | auto make_checkbox = [&](const char *label, unsigned int &option) {
64 | return ImGui::Checkbox(
65 | label,
66 | [&]() { return viewer.core().is_set(option); },
67 | [&](bool value) { return viewer.core().set(option, value); });
68 | };
69 |
70 | menu.callback_draw_custom_window = [&]() {
71 | ImGui::SetNextWindowPos(ImVec2(10, 10), ImGuiCond_FirstUseEver);
72 | ImGui::SetNextWindowSize(ImVec2(350, -1), ImGuiCond_FirstUseEver);
73 | if (ImGui::Begin("IGL")) {
74 | if (ImGui::Button("Quick open labeling")){
75 | std::string file = input_path.substr(0, input_path.find_last_of("/\\") + 1) + "labeling.txt";
76 | std::cout << "Attempting to open: " << file << std::endl;
77 | labeling = openFlagging(file, F.rows());
78 | updateLabeling();
79 | updateViz();
80 | }
81 |
82 | if (ImGui::Button("Open labeling")){
83 | std::string file = igl::file_dialog_open();
84 | labeling = openFlagging(file, F.rows());
85 | updateLabeling();
86 | updateViz();
87 | }
88 |
89 | if (ImGui::Button("Save labeling")){
90 | std::string file = igl::file_dialog_save();
91 | saveFlagging(file, labeling);
92 | }
93 |
94 | if (ImGui::Button("Measure labeling")){
95 | std::cout << "Measuring" << std::endl;
96 | }
97 |
98 | ImGui::Text("Total labeling score: %f", labeling_score);
99 | ImGui::Text("\tFastPoly score: %f", fast_poly_score);
100 | ImGui::Text("\tInvalid score: %f", invalid_score);
101 | ImGui::Text("\t# inverted tris: %i", n_fail_invert);
102 |
103 | ImGui::SliderInt("Insert label", &insert_label, 0, 5);
104 | ImGui::SliderFloat("Insert size", &insert_size, 0.0*l_avg, 10.0*l_avg);
105 | ImGui::End();
106 | }
107 | };
108 |
109 | viewer.callback_key_down = [&](igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier){
110 | if (key == ' '){
111 | int fid;
112 | Eigen::Vector3f bc;
113 | // Cast a ray in the view direction starting from the mouse position
114 | double x = viewer.current_mouse_x;
115 | double y = viewer.core().viewport(3) - viewer.current_mouse_y;
116 | if(igl::unproject_onto_mesh(Eigen::Vector2f(x,y), viewer.core().view,
117 | viewer.core().proj, viewer.core().viewport, V, F, fid, bc)){
118 | //labeling(fid) = insert_label;
119 | Eigen::VectorXi old_labeling = labeling;
120 | growFromTri(old_labeling, labeling, evo->TT_, evo->dists_, insert_label, fid, insert_size);
121 |
122 | updateLabeling();
123 | updateViz();
124 | return true;
125 | }
126 | }
127 | return false;
128 | };
129 |
130 | updateViz();
131 |
132 | viewer.core().lighting_factor = 0.0;
133 | viewer.core().set_rotation_type(igl::opengl::ViewerCore::ROTATION_TYPE_TRACKBALL);
134 | //viewer.core().background_color = Eigen::Vector4f(202.0/255.0, 190.0/255.0, 232.0/255.0, 1.0);
135 | viewer.core().background_color = Eigen::Vector4f(255.0/255.0, 255.0/255.0, 255.0/255.0, 1.0);
136 | viewer.launch();
137 | }
--------------------------------------------------------------------------------
/app/flagging_viewer.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | /*
9 | #include
10 | #include
11 | #include
12 | #include
13 | */
14 |
15 | #include "flagging_utils.h"
16 |
17 | int main(int argc, char *argv[]){
18 | igl::opengl::glfw::Viewer viewer;
19 | viewer.append_mesh();
20 | int orig_id = viewer.data_list[0].id;
21 | int hud_id = viewer.data_list[1].id;
22 | Eigen::MatrixXd V;
23 | Eigen::MatrixXi F;
24 |
25 | /*std::string input_name = "bunny"; //"cow";
26 | std::string input_off = "../data/"+input_name+".off";
27 | igl::readOFF(input_off, V, F);*/
28 |
29 | std::string input_obj = "../data/S1/boundary.obj";
30 | std::string input_labeling = "../data/S1/labeling.txt";
31 |
32 | igl::readOBJ(input_obj, V, F);
33 |
34 | viewer.data(orig_id).set_mesh(V, F);
35 |
36 |
37 | Eigen::VectorXi flagging = openFlagging(input_labeling, F.rows());
38 | Eigen::MatrixXd flagging_colors = colorsFromFlagging(flagging);
39 | viewer.data(orig_id).set_colors(flagging_colors);
40 |
41 | igl::opengl::glfw::imgui::ImGuiMenu menu;
42 | menu.callback_draw_viewer_window = []() {};
43 | viewer.plugins.push_back(&menu);
44 |
45 | menu.callback_draw_custom_window = [&]() {
46 | bool show = true;
47 | ImGui::SetNextWindowPos(ImVec2(0.f * menu.menu_scaling(), 0),
48 | ImGuiCond_FirstUseEver);
49 | ImGui::SetNextWindowSize(ImVec2(350, 400));
50 | if (ImGui::Begin("Test flagging")){
51 |
52 | ImGui::SliderFloat("Lighting", &viewer.core().lighting_factor, 0.0f, 5.0f, "%.3f");
53 |
54 | ImGui::End();
55 | }
56 | };
57 |
58 | viewer.data(orig_id).line_width = 8;
59 | viewer.data(hud_id).line_width = 1;
60 | viewer.data(orig_id).point_size = 4;
61 |
62 |
63 | viewer.data(orig_id).show_lines = false;
64 | viewer.core().background_color.setZero();
65 | viewer.core().lighting_factor=0.5;
66 | viewer.launch();
67 | }
--------------------------------------------------------------------------------
/app/igl_core.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | int main(int argc, char *argv[]){
7 |
8 | Eigen::MatrixXd V;
9 | Eigen::MatrixXi F;
10 |
11 | igl::readOBJ("../data/cross/cross_flat.obj", V, F);
12 |
13 |
14 | // --- VISUALIZATION ---
15 |
16 | igl::opengl::glfw::Viewer viewer;
17 | viewer.data().set_mesh(V, F);
18 |
19 | igl::opengl::glfw::imgui::ImGuiMenu menu;
20 | menu.callback_draw_viewer_window = []() {};
21 | viewer.plugins.push_back(&menu);
22 |
23 | auto updateViz = [&](){
24 | viewer.data().clear_edges();
25 | viewer.data().clear_points();
26 |
27 | };
28 |
29 | //helper function for menu
30 | auto make_checkbox = [&](const char *label, unsigned int &option) {
31 | return ImGui::Checkbox(
32 | label,
33 | [&]() { return viewer.core().is_set(option); },
34 | [&](bool value) { return viewer.core().set(option, value); });
35 | };
36 |
37 | menu.callback_draw_custom_window = [&]() {
38 | ImGui::SetNextWindowPos(ImVec2(10, 10), ImGuiCond_FirstUseEver);
39 | ImGui::SetNextWindowSize(ImVec2(350, -1), ImGuiCond_FirstUseEver);
40 | if (ImGui::Begin("IGL")) {
41 |
42 |
43 | ImGui::End();
44 | }
45 |
46 | };
47 |
48 | updateViz();
49 |
50 | viewer.core().set_rotation_type(igl::opengl::ViewerCore::ROTATION_TYPE_TRACKBALL);
51 | viewer.core().background_color = Eigen::Vector4f(202.0/255.0, 190.0/255.0, 232.0/255.0, 1.0);
52 | viewer.launch();
53 | }
--------------------------------------------------------------------------------
/app/jacob_test.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "distortion.h"
11 |
12 | int main(int argc, char *argv[]){
13 | using std::chrono::steady_clock;
14 | using std::chrono::duration_cast;
15 | using std::chrono::microseconds;
16 |
17 | Eigen::MatrixXd V, V_def;
18 | Eigen::MatrixXi F;
19 |
20 | igl::readOBJ("../data/DATASET2/medium_mambo/M1/boundary.obj", V, F);
21 | igl::readOBJ("../data/DATASET2/medium_mambo/M1/fast_polycube_surf.obj", V_def, F);
22 |
23 | Eigen::MatrixXd N;
24 | igl::per_face_normals(V, F, N);
25 | Eigen::MatrixXd N_def;
26 | igl::per_face_normals(V_def, F, N_def);
27 | Eigen::VectorXd A;
28 | igl::doublearea(V, F, A);
29 |
30 | auto time_disto_start = steady_clock::now();
31 | Eigen::VectorXd disto;
32 | computeDisto(V, V_def, F, N, N_def, disto);
33 | auto time_disto_end = steady_clock::now();
34 | std::cout << "Total disto time (μs): "<< duration_cast(time_disto_end - time_disto_start).count() << std::endl;
35 |
36 | std::cout << "disto.mean(): " << disto.mean() << std::endl;
37 | double final_disto = integrateDistortion(A, disto);
38 | std::cout << "final_disto: " << final_disto << std::endl;
39 |
40 | // --- VISUALIZATION ---
41 |
42 | igl::opengl::glfw::Viewer viewer;
43 | viewer.data().set_mesh(V_def, F);
44 |
45 | Eigen::MatrixXd colors = Eigen::MatrixXd::Constant(disto.rows(), 3, 1.0);
46 | colors.col(1) = 1.0 - disto.array();
47 | colors.col(2) = 1.0 - disto.array();
48 | viewer.data().set_colors(colors);
49 |
50 | igl::opengl::glfw::imgui::ImGuiMenu menu;
51 | menu.callback_draw_viewer_window = []() {};
52 | viewer.plugins.push_back(&menu);
53 | menu.callback_draw_custom_window = [&](){};
54 |
55 | viewer.data().show_lines = true;
56 | viewer.core().lighting_factor = 0.0;
57 | viewer.core().set_rotation_type(igl::opengl::ViewerCore::ROTATION_TYPE_TRACKBALL);
58 | viewer.core().background_color = Eigen::Vector4f(202.0/255.0, 190.0/255.0, 232.0/255.0, 1.0);
59 | viewer.launch();
60 | }
--------------------------------------------------------------------------------
/app/measurement.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include