├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── docs ├── Report.pdf ├── Slides.pdf └── SoftwareDemo.mp4 ├── experiment ├── BoundaryFree │ ├── bunny_disk.obj │ ├── cube.mark │ └── cube.obj ├── ConeParameterization │ ├── david.obj │ ├── david1.mark │ ├── david2.mark │ ├── max.mark │ └── max.obj ├── EuclideanOrbifold │ ├── david.obj │ ├── david_orbifold1.mark │ ├── david_orbifold2.mark │ └── david_orbifold3.mark ├── HyperbolicOrbifold │ ├── bunny_sphere.mark │ └── bunny_sphere.obj ├── Polygon │ ├── bunny_disk.mark │ ├── bunny_disk.obj │ ├── half_sphere.mark │ └── half_sphere.obj ├── Texture │ ├── checker_1k.bmp │ └── circles_1.bmp └── bin │ └── Viewer.exe ├── images ├── bunny_free_emb.png ├── bunny_free_texture.png ├── bunny_hyper.png ├── bunny_hyper_cover.png ├── bunny_hyper_emb.png ├── bunny_hyper_texture.png ├── bunny_polygon.png ├── bunny_polygon_emb.png ├── bunny_polygon_texture.png ├── cone.png ├── david_euc.png ├── david_euc_cover.png ├── david_euc_emb.png ├── david_euc_texture.png ├── david_global.png ├── david_global_emb.png ├── david_global_emb_hilbert.png ├── david_global_texture.png ├── euc_orbifold.png ├── four_euc_orbifolds.png ├── gui.png ├── halfedge.png ├── halfshpere_polygon.png ├── halfshpere_polygon_texture.png ├── halfsphere_polygon_emb.png ├── hyperbolic.png ├── locally.png ├── loop.png ├── max_global.png ├── max_global_emb.png ├── max_global_texture.png ├── parameterizations.png ├── q-fold.png ├── q-folds.png ├── spherical.png └── torus.png └── src ├── BoundaryFirstFlattening ├── BFF.cpp ├── BFF.h ├── BFFInitializer.cpp └── BFFInitializer.h ├── CMakeLists.txt ├── Mesh ├── MeshDefinition.cpp └── MeshDefinition.h ├── OrbifoldEmbedding ├── EuclideanOrbifoldSolver.cpp ├── EuclideanOrbifoldSolver.h ├── HyperbolicOrbifoldSolver.cpp ├── HyperbolicOrbifoldSolver.h ├── OrbifoldInitializer.cpp └── OrbifoldInitializer.h ├── Utilities ├── Circle.cpp ├── Circle.h ├── Dijkstra.cpp ├── Dijkstra.h ├── EuclideanCoveringSpace.cpp ├── EuclideanCoveringSpace.h ├── EuclideanGeometry2D.cpp ├── EuclideanGeometry2D.h ├── HyperbolicCoveringSpace.cpp ├── HyperbolicCoveringSpace.h ├── HyperbolicGeometry.cpp ├── HyperbolicGeometry.h ├── LineCylinder.cpp ├── LineCylinder.h ├── MeshFormConverter.cpp ├── MeshFormConverter.h ├── MeshMarker.cpp ├── MeshMarker.h ├── MeshMerger.cpp ├── MeshMerger.h ├── MeshSlicer.cpp ├── MeshSlicer.h ├── PointSphere.cpp ├── PointSphere.h ├── StringParser.cpp └── StringParser.h └── Viewer ├── GUIViewer.cpp ├── GUIViewer.h └── main.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | UseTab: Never 4 | AlwaysBreakTemplateDeclarations: true 5 | ColumnLimit: 160 6 | Language: Cpp 7 | Standard: Cpp11 8 | BinPackParameters: false 9 | BreakBeforeBraces: Allman 10 | ### you may add more rules to fit your own taste 11 | #PointerBindsToType: true 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | install 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(Parameterization) 4 | 5 | set(CMAKE_CXX_STANDARD 14) 6 | 7 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 8 | set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install CACHE PATH "cmake install prefix" FORCE) 9 | endif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 10 | 11 | if(MSVC) 12 | set(CMAKE_DEBUG_POSTFIX "d") 13 | else() 14 | set(CMAKE_DEBUG_POSTFIX "") 15 | endif() 16 | 17 | # if there are some customized FindXXX modules 18 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake" CACHE STRING "Modules for CMake" FORCE) 19 | 20 | ###################### user-selected option #################### 21 | option(WITH_OPENMP "Enable OpenMP support?" ON) 22 | if(WITH_OPENMP) 23 | find_package(OpenMP REQUIRED) 24 | if(OPENMP_FOUND) 25 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") 26 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 27 | endif() 28 | add_definitions(-DWITH_OPENMP) 29 | endif() 30 | 31 | ####################### eigen ################### 32 | 33 | include(FetchContent) 34 | FetchContent_Declare( 35 | eigen 36 | URL https://gitlab.com/libeigen/eigen/-/archive/3.3.7/eigen-3.3.7.tar.bz2 37 | URL_MD5 b9e98a200d2455f06db9c661c5610496 38 | ) 39 | FetchContent_GetProperties(eigen) 40 | FetchContent_Populate(eigen) 41 | include_directories(${eigen_SOURCE_DIR}) 42 | 43 | 44 | ####################### lbfgs ################### 45 | 46 | FetchContent_Declare( 47 | lbfgs 48 | GIT_REPOSITORY https://github.com/yixuan/LBFGSpp.git 49 | GIT_TAG 7fea82aab31607fc51c2ffd751a21c031b9a1061 50 | ) 51 | FetchContent_GetProperties(lbfgs) 52 | FetchContent_Populate(lbfgs) 53 | include_directories(${lbfgs_SOURCE_DIR}/include) 54 | 55 | ############### libigl ####################### 56 | 57 | option(LIBIGL_WITH_OPENGL "Use OpenGL" ON) 58 | option(LIBIGL_WITH_GLFW "Use GLFW" ON) 59 | option(LIBIGL_WITH_IMGUI "Use ImGui" ON) 60 | option(LIBIGL_WITH_PNG "Use PNG" ON) 61 | 62 | include(FetchContent) 63 | FetchContent_Declare( 64 | libigl 65 | GIT_REPOSITORY https://github.com/libigl/libigl.git 66 | GIT_TAG 237ffa20b1e2fd92e0be7862e9246c3774e4fc14 67 | ) 68 | FetchContent_MakeAvailable(libigl) 69 | 70 | 71 | ############## OpenMesh ################# 72 | 73 | FetchContent_Declare( 74 | openmesh 75 | GIT_REPOSITORY https://gitlab.vci.rwth-aachen.de:9000/OpenMesh/OpenMesh.git 76 | GIT_TAG d50cad4640d6d3657c4d0188fbf27ff38e4bfdca 77 | ) 78 | FetchContent_MakeAvailable(openmesh) 79 | include_directories(${openmesh_SOURCE_DIR}/src) 80 | 81 | 82 | # ####################### precompiled dependencies #################### 83 | 84 | # find_package(OpenMesh REQUIRED) 85 | # include_directories(${OpenMesh_INCLUDE_DIRS}) 86 | ##################### import files ################## 87 | add_definitions(-D_USE_MATH_DEFINES) 88 | add_subdirectory(src) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Xuan Li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Project for CSE528 On Surface Parameterization](https://github.com/xuan-li/GraphicsProject) 2 | 3 | ![screenshot](https://github.com/xuan-li/GraphicsProject/blob/master/images/parameterizations.png) 4 | 5 | ## Project Overview 6 | 7 | In this project, I explore two kinds of parameterization algorithms. The first kind is from the view of Tutte embedding called Orbifold Tutte embedding (OTE). The other kind is from the view of differential geometry called boundary first flattening (BFF). 8 | 9 | [Full report on this project.](https://github.com/xuan-li/GraphicsProject/blob/master/docs/Report.pdf) 10 | 11 | [Software Demo](https://github.com/xuan-li/GraphicsProject/blob/master/docs/SoftwareDemo.mp4) 12 | 13 | 14 | ## References 15 | 16 | - Aigerman, N. and Lipman, Y. (2015). Orbifold tutte embeddings. ACM Trans. Graph., 34(6):190:1–190:12. 17 | 18 | - Aigerman, N. and Lipman, Y. (2016). Hyperbolic orbifold tutte embeddings. ACM Trans. Graph., 35(6):217:1–217:14. 19 | 20 | - Sawhney, R. and Crane, K. (2017). Boundary first flattening. https://arxiv.org/abs/1704.06873. 21 | 22 | 37 | 38 | ## Compile and Run (Ubuntu) 39 | ``` 40 | sudo apt-get install freeglut3-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev 41 | mkidir build 42 | cmake -DCMAKE_BUILD_TYPE=Release .. 43 | make -j12 44 | src/Viewer 45 | ``` 46 | 47 | 48 | ## How To Use 49 | 50 | ### Folder structure 51 | 52 | Released .exe file is in experiment/bin/ 53 | 54 | Test data are included in experiment/. 55 | 56 | There are seven folders in experiment/: 57 | 58 | - bin: released exe file 59 | 60 | - Texture: some textures to show texture mappings. 61 | 62 | - Euclidean Orbifold / Hyperbolic Orbifold: test data for OTE 63 | 64 | - BoundaryFree / Polygon / ConeParameterization: test data for BFF 65 | 66 | 67 | ### Viewer Options 68 | 69 | Show Option: 70 | 71 | - Original: show loaded model. 72 | 73 | - Sliced: show model after cut, which is a disk. 74 | 75 | - Embedding: show results of algorithms 76 | 77 | - Covering Space: show tiled plane for orbifolds. (Only enabled in orbifold embeddings). 78 | 79 | Show Slices and Cones: whether to show added cones and slices. 80 | 81 | 82 | 83 | ### Vertex Selection and Cutting System 84 | 85 | Both algorithm needs to cut mesh and set angle sum for some vertices. 86 | 87 | The loaded mesh is shown in "Original" mode. In this mode, you can select vertices while pressing down 'S'. Input cone angle sum in the unit of . Select one vertex to add a cone and select two vertices to add a slice. 88 | 89 | Current marker can be saved and loaded. There's a .mark file associated with each model file. 90 | 91 | 92 | ### Orbifold Tutte Embedding 93 | 94 | Only sphere-type Euclidean orbifolds with three cones supported: 95 | , 96 | , 97 | 98 | 99 | 100 | Only sphere-type hyperbolic orbifolds with cone angles all supported. The number of cones for this kind should be larger than 4. 101 | 102 | Needed .mark file is included for test. 103 | 104 | Use Covering Space flag to see tiled plane: part of Euclidean plane and part of Poincare disk. 105 | 106 | 107 | ### Boundary First Flattening 108 | 109 | Three settings: free boundary (Harmonic/Hilbert BFF with free B), polygonal boundary (Harmonic/Hilbert BFF with K), cone parameterization (Harmonic/Hilbert BFF with Cones). 110 | 111 | Needed .mark file is included for test. 112 | 113 | 114 | ### Texture Mapping 115 | 116 | After computation, load a texture. Choose "Show texture" flag. See it in "Sliced" mode. 117 | 118 | ## Build From Source 119 | 120 | All codes are included in Code/. 121 | 122 | Open .sln file in VS2017 and build. Only x64 mode supported. Needed libraries' path are set in relative mode, you needn't modify them. 123 | 124 | ## License 125 | 126 | MIT 127 | -------------------------------------------------------------------------------- /docs/Report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/docs/Report.pdf -------------------------------------------------------------------------------- /docs/Slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/docs/Slides.pdf -------------------------------------------------------------------------------- /docs/SoftwareDemo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/docs/SoftwareDemo.mp4 -------------------------------------------------------------------------------- /experiment/BoundaryFree/cube.mark: -------------------------------------------------------------------------------- 1 | e 8 2 | e 9 3 | e 13 4 | e 73 5 | e 111 6 | e 118 7 | e 165 8 | e 180 9 | e 240 10 | e 300 11 | e 310 12 | e 324 13 | e 327 14 | e 353 15 | e 362 16 | e 417 17 | e 493 18 | e 506 19 | e 522 20 | e 618 21 | e 707 22 | e 713 23 | e 722 24 | e 804 25 | e 848 26 | e 1014 27 | e 1068 28 | e 1092 29 | e 1132 30 | e 1155 31 | -------------------------------------------------------------------------------- /experiment/ConeParameterization/david1.mark: -------------------------------------------------------------------------------- 1 | v 14611 1.57079627 2 | v 25291 3.14159254 3 | v 25414 1.57079627 4 | e 376 5 | e 1634 6 | e 1783 7 | e 3022 8 | e 3047 9 | e 4207 10 | e 4867 11 | e 8189 12 | e 9639 13 | e 10226 14 | e 10887 15 | e 11394 16 | e 11437 17 | e 11635 18 | e 11807 19 | e 13065 20 | e 13245 21 | e 13361 22 | e 14614 23 | e 14803 24 | e 15676 25 | e 15966 26 | e 16084 27 | e 16153 28 | e 16751 29 | e 16763 30 | e 17245 31 | e 18242 32 | e 18435 33 | e 18628 34 | e 20016 35 | e 20659 36 | e 20696 37 | e 20900 38 | e 21356 39 | e 22564 40 | e 22750 41 | e 22814 42 | e 22883 43 | e 23856 44 | e 24119 45 | e 24161 46 | e 24997 47 | e 25768 48 | e 26882 49 | e 27027 50 | e 28091 51 | e 28096 52 | e 30437 53 | e 30698 54 | e 31046 55 | e 32507 56 | e 32774 57 | e 32778 58 | e 32972 59 | e 33803 60 | e 35571 61 | e 35679 62 | e 37517 63 | e 38413 64 | e 40037 65 | e 40847 66 | e 40925 67 | e 42352 68 | e 42871 69 | e 43090 70 | e 43453 71 | e 44612 72 | e 46187 73 | e 46953 74 | e 47738 75 | e 49971 76 | e 49983 77 | e 50605 78 | e 51792 79 | e 51812 80 | e 52083 81 | e 52106 82 | e 55430 83 | e 56413 84 | e 56459 85 | e 57181 86 | e 58835 87 | e 59172 88 | e 59973 89 | e 62198 90 | e 62930 91 | e 63510 92 | e 63870 93 | e 65540 94 | e 66303 95 | e 67137 96 | e 67140 97 | e 67239 98 | e 67612 99 | e 68447 100 | -------------------------------------------------------------------------------- /experiment/ConeParameterization/david2.mark: -------------------------------------------------------------------------------- 1 | v 14611 3.14159254 2 | v 17336 3.14159254 3 | v 18396 3.14159254 4 | v 25414 3.14159254 5 | e 376 6 | e 1634 7 | e 1783 8 | e 3022 9 | e 3047 10 | e 4207 11 | e 4867 12 | e 8189 13 | e 9639 14 | e 10226 15 | e 10887 16 | e 11394 17 | e 11437 18 | e 11635 19 | e 11807 20 | e 13065 21 | e 13245 22 | e 13361 23 | e 14614 24 | e 14803 25 | e 15676 26 | e 15966 27 | e 16084 28 | e 16153 29 | e 16751 30 | e 16763 31 | e 17245 32 | e 18242 33 | e 18435 34 | e 18628 35 | e 20016 36 | e 20659 37 | e 20696 38 | e 20900 39 | e 21356 40 | e 22564 41 | e 22750 42 | e 22814 43 | e 22883 44 | e 23856 45 | e 24119 46 | e 24161 47 | e 24997 48 | e 25768 49 | e 26882 50 | e 27027 51 | e 28091 52 | e 28096 53 | e 30437 54 | e 30698 55 | e 31046 56 | e 32507 57 | e 32774 58 | e 32778 59 | e 32972 60 | e 33803 61 | e 35571 62 | e 35679 63 | e 37517 64 | e 38413 65 | e 40037 66 | e 40847 67 | e 40925 68 | e 42352 69 | e 42871 70 | e 43090 71 | e 43453 72 | e 44612 73 | e 46187 74 | e 46953 75 | e 47738 76 | e 49971 77 | e 49983 78 | e 50605 79 | e 51792 80 | e 51812 81 | e 52083 82 | e 52106 83 | e 55430 84 | e 56413 85 | e 56459 86 | e 57181 87 | e 58835 88 | e 59172 89 | e 59973 90 | e 62198 91 | e 62930 92 | e 63510 93 | e 63870 94 | e 65540 95 | e 66303 96 | e 67137 97 | e 67140 98 | e 67239 99 | e 67612 100 | e 68447 101 | -------------------------------------------------------------------------------- /experiment/ConeParameterization/max.mark: -------------------------------------------------------------------------------- 1 | v 3374 4.71238881 2 | v 3693 4.71238881 3 | v 6274 4.71238881 4 | v 6308 4.71238881 5 | e 74 6 | e 102 7 | e 433 8 | e 520 9 | e 779 10 | e 935 11 | e 1273 12 | e 1425 13 | e 1514 14 | e 1907 15 | e 2138 16 | e 2227 17 | e 2275 18 | e 2326 19 | e 2637 20 | e 2969 21 | e 2977 22 | e 3423 23 | e 3468 24 | e 3481 25 | e 3851 26 | e 4486 27 | e 4560 28 | e 4582 29 | e 4662 30 | e 4884 31 | e 4957 32 | e 5470 33 | e 5737 34 | e 5824 35 | e 5995 36 | e 6169 37 | e 7066 38 | e 7230 39 | e 7532 40 | e 7801 41 | e 8022 42 | e 8372 43 | e 8420 44 | e 8776 45 | e 8859 46 | e 9443 47 | e 9582 48 | e 9865 49 | e 9868 50 | e 10332 51 | e 10701 52 | e 11051 53 | e 11267 54 | e 11565 55 | e 11811 56 | e 12455 57 | e 12580 58 | e 12627 59 | e 12774 60 | e 12996 61 | e 13163 62 | e 13313 63 | e 13852 64 | e 13880 65 | e 14151 66 | e 14310 67 | e 14427 68 | e 14549 69 | e 14984 70 | e 15002 71 | e 15121 72 | e 15392 73 | e 15829 74 | e 16066 75 | e 16335 76 | e 16416 77 | e 16465 78 | e 16688 79 | e 16953 80 | e 16985 81 | e 17274 82 | e 17586 83 | e 17670 84 | e 17688 85 | e 17985 86 | e 18490 87 | e 18526 88 | e 18545 89 | e 19002 90 | e 19026 91 | e 19254 92 | e 19259 93 | e 19461 94 | e 19768 95 | e 19911 96 | e 19929 97 | e 20053 98 | e 20120 99 | e 20634 100 | e 20970 101 | e 21067 102 | e 21118 103 | e 21646 104 | e 21670 105 | e 21719 106 | e 21960 107 | e 22369 108 | e 22666 109 | e 22733 110 | e 22783 111 | e 23053 112 | e 23289 113 | e 23309 114 | e 23325 115 | e 23538 116 | e 23912 117 | e 24092 118 | e 24104 119 | e 24275 120 | e 24427 121 | e 24592 122 | e 24606 123 | e 24778 124 | e 25004 125 | e 25068 126 | e 25091 127 | e 25646 128 | e 25655 129 | e 25726 130 | e 26344 131 | -------------------------------------------------------------------------------- /experiment/EuclideanOrbifold/david_orbifold1.mark: -------------------------------------------------------------------------------- 1 | v 14611 1.57079627 2 | v 25291 3.14159254 3 | v 25414 1.57079627 4 | e 376 5 | e 1634 6 | e 1783 7 | e 3022 8 | e 3047 9 | e 4207 10 | e 4867 11 | e 8189 12 | e 9639 13 | e 10226 14 | e 10887 15 | e 11394 16 | e 11437 17 | e 11635 18 | e 11807 19 | e 13065 20 | e 13245 21 | e 13361 22 | e 14614 23 | e 14803 24 | e 15676 25 | e 15966 26 | e 16084 27 | e 16153 28 | e 16751 29 | e 16763 30 | e 17245 31 | e 18242 32 | e 18435 33 | e 18628 34 | e 20016 35 | e 20659 36 | e 20696 37 | e 20900 38 | e 21356 39 | e 22564 40 | e 22750 41 | e 22814 42 | e 22883 43 | e 23856 44 | e 24119 45 | e 24161 46 | e 24997 47 | e 25768 48 | e 26882 49 | e 27027 50 | e 28091 51 | e 28096 52 | e 30437 53 | e 30698 54 | e 31046 55 | e 32507 56 | e 32774 57 | e 32778 58 | e 32972 59 | e 33803 60 | e 35571 61 | e 35679 62 | e 37517 63 | e 38413 64 | e 40037 65 | e 40847 66 | e 40925 67 | e 42352 68 | e 42871 69 | e 43090 70 | e 43453 71 | e 44612 72 | e 46187 73 | e 46953 74 | e 47738 75 | e 49971 76 | e 49983 77 | e 50605 78 | e 51792 79 | e 51812 80 | e 52083 81 | e 52106 82 | e 55430 83 | e 56413 84 | e 56459 85 | e 57181 86 | e 58835 87 | e 59172 88 | e 59973 89 | e 62198 90 | e 62930 91 | e 63510 92 | e 63870 93 | e 65540 94 | e 66303 95 | e 67137 96 | e 67140 97 | e 67239 98 | e 67612 99 | e 68447 100 | -------------------------------------------------------------------------------- /experiment/EuclideanOrbifold/david_orbifold2.mark: -------------------------------------------------------------------------------- 1 | v 14611 2.09439510 2 | v 25291 2.09439510 3 | v 25414 2.09439510 4 | e 376 5 | e 1634 6 | e 1783 7 | e 3022 8 | e 3047 9 | e 4207 10 | e 4867 11 | e 8189 12 | e 9639 13 | e 10226 14 | e 10887 15 | e 11394 16 | e 11437 17 | e 11635 18 | e 11807 19 | e 13065 20 | e 13245 21 | e 13361 22 | e 14614 23 | e 14803 24 | e 15676 25 | e 15966 26 | e 16084 27 | e 16153 28 | e 16751 29 | e 16763 30 | e 17245 31 | e 18242 32 | e 18435 33 | e 18628 34 | e 20016 35 | e 20659 36 | e 20696 37 | e 20900 38 | e 21356 39 | e 22564 40 | e 22750 41 | e 22814 42 | e 22883 43 | e 23856 44 | e 24119 45 | e 24161 46 | e 24997 47 | e 25768 48 | e 26882 49 | e 27027 50 | e 28091 51 | e 28096 52 | e 30437 53 | e 30698 54 | e 31046 55 | e 32507 56 | e 32774 57 | e 32778 58 | e 32972 59 | e 33803 60 | e 35571 61 | e 35679 62 | e 37517 63 | e 38413 64 | e 40037 65 | e 40847 66 | e 40925 67 | e 42352 68 | e 42871 69 | e 43090 70 | e 43453 71 | e 44612 72 | e 46187 73 | e 46953 74 | e 47738 75 | e 49971 76 | e 49983 77 | e 50605 78 | e 51792 79 | e 51812 80 | e 52083 81 | e 52106 82 | e 55430 83 | e 56413 84 | e 56459 85 | e 57181 86 | e 58835 87 | e 59172 88 | e 59973 89 | e 62198 90 | e 62930 91 | e 63510 92 | e 63870 93 | e 65540 94 | e 66303 95 | e 67137 96 | e 67140 97 | e 67239 98 | e 67612 99 | e 68447 100 | -------------------------------------------------------------------------------- /experiment/EuclideanOrbifold/david_orbifold3.mark: -------------------------------------------------------------------------------- 1 | v 14611 1.04719755 2 | v 25291 2.09439510 3 | v 25414 3.14159254 4 | e 376 5 | e 1634 6 | e 1783 7 | e 3022 8 | e 3047 9 | e 4207 10 | e 4867 11 | e 8189 12 | e 9639 13 | e 10226 14 | e 10887 15 | e 11394 16 | e 11437 17 | e 11635 18 | e 11807 19 | e 13065 20 | e 13245 21 | e 13361 22 | e 14614 23 | e 14803 24 | e 15676 25 | e 15966 26 | e 16084 27 | e 16153 28 | e 16751 29 | e 16763 30 | e 17245 31 | e 18242 32 | e 18435 33 | e 18628 34 | e 20016 35 | e 20659 36 | e 20696 37 | e 20900 38 | e 21356 39 | e 22564 40 | e 22750 41 | e 22814 42 | e 22883 43 | e 23856 44 | e 24119 45 | e 24161 46 | e 24997 47 | e 25768 48 | e 26882 49 | e 27027 50 | e 28091 51 | e 28096 52 | e 30437 53 | e 30698 54 | e 31046 55 | e 32507 56 | e 32774 57 | e 32778 58 | e 32972 59 | e 33803 60 | e 35571 61 | e 35679 62 | e 37517 63 | e 38413 64 | e 40037 65 | e 40847 66 | e 40925 67 | e 42352 68 | e 42871 69 | e 43090 70 | e 43453 71 | e 44612 72 | e 46187 73 | e 46953 74 | e 47738 75 | e 49971 76 | e 49983 77 | e 50605 78 | e 51792 79 | e 51812 80 | e 52083 81 | e 52106 82 | e 55430 83 | e 56413 84 | e 56459 85 | e 57181 86 | e 58835 87 | e 59172 88 | e 59973 89 | e 62198 90 | e 62930 91 | e 63510 92 | e 63870 93 | e 65540 94 | e 66303 95 | e 67137 96 | e 67140 97 | e 67239 98 | e 67612 99 | e 68447 100 | -------------------------------------------------------------------------------- /experiment/HyperbolicOrbifold/bunny_sphere.mark: -------------------------------------------------------------------------------- 1 | v 22 3.14159254 2 | v 3141 3.14159254 3 | v 5956 3.14159254 4 | v 6211 3.14159254 5 | v 6266 3.14159254 6 | v 7107 3.14159254 7 | v 8038 3.14159254 8 | e 1109 9 | e 1340 10 | e 1352 11 | e 1504 12 | e 1753 13 | e 1823 14 | e 1932 15 | e 2040 16 | e 2158 17 | e 2214 18 | e 2233 19 | e 2469 20 | e 2866 21 | e 2966 22 | e 3105 23 | e 3478 24 | e 3559 25 | e 3627 26 | e 3667 27 | e 4336 28 | e 4355 29 | e 4502 30 | e 4570 31 | e 4814 32 | e 5437 33 | e 5999 34 | e 6014 35 | e 6035 36 | e 6050 37 | e 6273 38 | e 6330 39 | e 6779 40 | e 7025 41 | e 7104 42 | e 7243 43 | e 7413 44 | e 7433 45 | e 7499 46 | e 7662 47 | e 7833 48 | e 7852 49 | e 8591 50 | e 8612 51 | e 8761 52 | e 9086 53 | e 9980 54 | e 10591 55 | e 10598 56 | e 10938 57 | e 11018 58 | e 11348 59 | e 11684 60 | e 11923 61 | e 12346 62 | e 13332 63 | e 13502 64 | e 13778 65 | e 14101 66 | e 14964 67 | e 15279 68 | e 15304 69 | e 16035 70 | e 16178 71 | e 17581 72 | e 17592 73 | e 17610 74 | e 17846 75 | e 17998 76 | e 18001 77 | e 18054 78 | e 18425 79 | e 18479 80 | e 18664 81 | e 18728 82 | e 19193 83 | e 19304 84 | e 19529 85 | e 19812 86 | e 19829 87 | e 20080 88 | e 20313 89 | e 20867 90 | e 20870 91 | e 21626 92 | e 21753 93 | e 21789 94 | e 21809 95 | e 22053 96 | e 23035 97 | e 23060 98 | e 23385 99 | e 23482 100 | e 23529 101 | e 23557 102 | e 23655 103 | e 23735 104 | e 23812 105 | e 23846 106 | e 24237 107 | e 24438 108 | e 24870 109 | e 25434 110 | e 25549 111 | e 25812 112 | e 25873 113 | e 25901 114 | e 25984 115 | e 26011 116 | e 26151 117 | e 26564 118 | e 26658 119 | e 26736 120 | e 26740 121 | e 26831 122 | e 26999 123 | e 27591 124 | e 27746 125 | e 27907 126 | e 28123 127 | e 28249 128 | e 28408 129 | e 28743 130 | e 28892 131 | e 28907 132 | e 28951 133 | e 28961 134 | e 29253 135 | e 29392 136 | e 30456 137 | e 30943 138 | e 31578 139 | e 31751 140 | e 31841 141 | e 31973 142 | e 32596 143 | e 32944 144 | e 33266 145 | e 33268 146 | e 33316 147 | e 33408 148 | e 33491 149 | -------------------------------------------------------------------------------- /experiment/Polygon/bunny_disk.mark: -------------------------------------------------------------------------------- 1 | v 2210 1.57079627 2 | v 2255 1.57079627 3 | v 2275 1.57079627 4 | v 2415 1.57079627 5 | -------------------------------------------------------------------------------- /experiment/Polygon/half_sphere.mark: -------------------------------------------------------------------------------- 1 | v 199 1.57079627 2 | v 352 1.57079627 3 | v 400 1.57079627 4 | v 757 1.57079627 5 | v 1613 1.57079627 6 | v 1835 4.71238881 7 | -------------------------------------------------------------------------------- /experiment/Texture/checker_1k.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/experiment/Texture/checker_1k.bmp -------------------------------------------------------------------------------- /experiment/Texture/circles_1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/experiment/Texture/circles_1.bmp -------------------------------------------------------------------------------- /experiment/bin/Viewer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/experiment/bin/Viewer.exe -------------------------------------------------------------------------------- /images/bunny_free_emb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/bunny_free_emb.png -------------------------------------------------------------------------------- /images/bunny_free_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/bunny_free_texture.png -------------------------------------------------------------------------------- /images/bunny_hyper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/bunny_hyper.png -------------------------------------------------------------------------------- /images/bunny_hyper_cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/bunny_hyper_cover.png -------------------------------------------------------------------------------- /images/bunny_hyper_emb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/bunny_hyper_emb.png -------------------------------------------------------------------------------- /images/bunny_hyper_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/bunny_hyper_texture.png -------------------------------------------------------------------------------- /images/bunny_polygon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/bunny_polygon.png -------------------------------------------------------------------------------- /images/bunny_polygon_emb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/bunny_polygon_emb.png -------------------------------------------------------------------------------- /images/bunny_polygon_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/bunny_polygon_texture.png -------------------------------------------------------------------------------- /images/cone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/cone.png -------------------------------------------------------------------------------- /images/david_euc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/david_euc.png -------------------------------------------------------------------------------- /images/david_euc_cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/david_euc_cover.png -------------------------------------------------------------------------------- /images/david_euc_emb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/david_euc_emb.png -------------------------------------------------------------------------------- /images/david_euc_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/david_euc_texture.png -------------------------------------------------------------------------------- /images/david_global.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/david_global.png -------------------------------------------------------------------------------- /images/david_global_emb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/david_global_emb.png -------------------------------------------------------------------------------- /images/david_global_emb_hilbert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/david_global_emb_hilbert.png -------------------------------------------------------------------------------- /images/david_global_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/david_global_texture.png -------------------------------------------------------------------------------- /images/euc_orbifold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/euc_orbifold.png -------------------------------------------------------------------------------- /images/four_euc_orbifolds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/four_euc_orbifolds.png -------------------------------------------------------------------------------- /images/gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/gui.png -------------------------------------------------------------------------------- /images/halfedge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/halfedge.png -------------------------------------------------------------------------------- /images/halfshpere_polygon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/halfshpere_polygon.png -------------------------------------------------------------------------------- /images/halfshpere_polygon_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/halfshpere_polygon_texture.png -------------------------------------------------------------------------------- /images/halfsphere_polygon_emb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/halfsphere_polygon_emb.png -------------------------------------------------------------------------------- /images/hyperbolic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/hyperbolic.png -------------------------------------------------------------------------------- /images/locally.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/locally.png -------------------------------------------------------------------------------- /images/loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/loop.png -------------------------------------------------------------------------------- /images/max_global.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/max_global.png -------------------------------------------------------------------------------- /images/max_global_emb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/max_global_emb.png -------------------------------------------------------------------------------- /images/max_global_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/max_global_texture.png -------------------------------------------------------------------------------- /images/parameterizations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/parameterizations.png -------------------------------------------------------------------------------- /images/q-fold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/q-fold.png -------------------------------------------------------------------------------- /images/q-folds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/q-folds.png -------------------------------------------------------------------------------- /images/spherical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/spherical.png -------------------------------------------------------------------------------- /images/torus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuan-li/Surface-Parameterization-Algorithms/c92997447447791bd1db2900717ce91d12a2203a/images/torus.png -------------------------------------------------------------------------------- /src/BoundaryFirstFlattening/BFF.cpp: -------------------------------------------------------------------------------- 1 | #include "BFF.h" 2 | 3 | BFFSolver::BFFSolver(SurfaceMesh & mesh, OpenMesh::VPropHandleT cone_flag, OpenMesh::VPropHandleT cone_angle, OpenMesh::EPropHandleT slice_flag) 4 | :mesh_(mesh), cone_flag_(cone_flag), cone_angle_(cone_angle), slice_flag_(slice_flag) 5 | { 6 | 7 | } 8 | 9 | 10 | SurfaceMesh BFFSolver::Compute(int mode) 11 | { 12 | /* 13 | mode 0: BFF known k with hilbert extension 14 | mode 1: BFF known k with harmonic extension 15 | mode 2: BFF free boundary with hilbert extension 16 | mode 3: BFF free boundary with harmonic extension 17 | mode 4: BFF cone parameterization with hilbert extension 18 | mode 5: BFF cone parameterization with harmonic extension 19 | */ 20 | 21 | Init(); 22 | 23 | ComputeVertexCurvatures(sliced_mesh_); 24 | 25 | if (mode == 2 || mode == 3) 26 | FreeBoundary(); 27 | else if (mode == 0 || mode == 1) 28 | BoundaryTargetKKnown(); 29 | else if (mode == 4 || mode == 5) 30 | GlobalParameterization(); 31 | 32 | IntegrateBoundaryCurve(); 33 | 34 | if(mode == 0 || mode == 2 || mode == 4) 35 | ExtendToInteriorHilbert(); 36 | else 37 | ExtendToInteriorHarmonic(); 38 | 39 | NormalizeUV(); 40 | 41 | return sliced_mesh_; 42 | } 43 | 44 | void BFFSolver::Init() 45 | { 46 | BFFInitializer initializer(mesh_); 47 | initializer.Initiate(sliced_mesh_, cone_flag_, cone_angle_, slice_flag_); 48 | cone_vts_ = initializer.GetConeVertices(); 49 | split_to_ = initializer.split_to(); 50 | } 51 | 52 | double BFFSolver::CosineLaw(double a, double b, double c) 53 | { 54 | double cs = (a * a + b * b - c * c) / (2 * a * b); 55 | if (-1 > cs) 56 | return PI; 57 | else if (cs > 1) 58 | return 0; 59 | else 60 | return acos(cs); 61 | } 62 | 63 | void BFFSolver::ComputeCornerAngles(SurfaceMesh &mesh, Eigen::VectorXd L) 64 | { 65 | using namespace OpenMesh; 66 | bool with_length = false; 67 | if (L.size() == mesh.n_edges()) 68 | with_length = true; 69 | for (auto fiter = mesh.faces_begin(); fiter != mesh.faces_end(); ++fiter) { 70 | FaceHandle f = *fiter; 71 | std::vector he; 72 | 73 | for (auto fhiter = mesh.fh_iter(f); fhiter.is_valid(); ++fhiter) { 74 | he.push_back(*fhiter); 75 | } 76 | double l[3]; 77 | if (with_length) { 78 | l[0] = L(mesh.edge_handle(he[0]).idx()); 79 | l[1] = L(mesh.edge_handle(he[1]).idx()); 80 | l[2] = L(mesh.edge_handle(he[2]).idx()); 81 | } 82 | else { 83 | l[0] = mesh.calc_edge_length(mesh.edge_handle(he[0])); 84 | l[1] = mesh.calc_edge_length(mesh.edge_handle(he[1])); 85 | l[2] = mesh.calc_edge_length(mesh.edge_handle(he[2])); 86 | } 87 | for (int i = 0; i < 3; ++i) { 88 | double cs = CosineLaw(l[i], l[(i + 1) % 3], l[(i + 2) % 3]); 89 | mesh.data(he[i]).set_angle(cs); 90 | } 91 | } 92 | } 93 | 94 | void BFFSolver::ComputeVertexCurvatures(SurfaceMesh &mesh, Eigen::VectorXd L) 95 | { 96 | using namespace OpenMesh; 97 | ComputeCornerAngles(mesh, L); 98 | double sum = 0.0; 99 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 100 | VertexHandle v = *viter; 101 | double angle_sum = 0.0; 102 | for (auto vihiter = mesh.vih_iter(v); vihiter.is_valid(); ++vihiter) { 103 | HalfedgeHandle h = *vihiter; 104 | if (mesh.is_boundary(h)) continue; 105 | angle_sum += mesh.data(h).angle(); 106 | } 107 | if(!mesh.is_boundary(v)) 108 | mesh.data(v).set_curvature(2 * PI - angle_sum); 109 | else 110 | mesh.data(v).set_curvature(PI - angle_sum); 111 | sum += mesh.data(v).curvature(); 112 | } 113 | std::cout << "Total Curvature: " << sum/ PI << " pi."<< std::endl; 114 | } 115 | 116 | void BFFSolver::ComputeLaplacian(SurfaceMesh & mesh, bool mode) 117 | { 118 | using namespace OpenMesh; 119 | using namespace Eigen; 120 | 121 | ReindexVertices(mesh); 122 | ComputeHalfedgeWeights(mesh); 123 | 124 | Delta_.resize(mesh.n_vertices(), mesh.n_vertices()); 125 | Delta_.setZero(); 126 | std::vector> A_coefficients; 127 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 128 | VertexHandle v = *viter; 129 | if (mode && (viter == mesh.vertices_begin())){ 130 | for (auto viter1 = mesh.vertices_begin(); viter1 != mesh.vertices_end(); ++viter1) { 131 | VertexHandle v1 = *viter1; 132 | A_coefficients.push_back(Eigen::Triplet(mesh.data(v).reindex(), mesh.data(v1).reindex(), 1.)); 133 | } 134 | continue; 135 | } 136 | double s_w = 0; 137 | for (SurfaceMesh::VertexVertexIter vviter = mesh.vv_iter(v); vviter.is_valid(); ++vviter) { 138 | VertexHandle neighbor = *vviter; 139 | HalfedgeHandle h = mesh.find_halfedge(v, neighbor); 140 | double n_w = mesh.data(h).weight(); 141 | 142 | s_w += n_w; 143 | A_coefficients.push_back(Eigen::Triplet(mesh.data(v).reindex(), mesh.data(neighbor).reindex(), -n_w)); 144 | } 145 | A_coefficients.push_back(Eigen::Triplet(mesh.data(v).reindex(), mesh.data(v).reindex(), s_w)); 146 | } 147 | Delta_.setFromTriplets(A_coefficients.begin(), A_coefficients.end()); 148 | } 149 | 150 | 151 | void BFFSolver::ComputeHalfedgeWeights(SurfaceMesh &mesh) 152 | { 153 | using namespace OpenMesh; 154 | ComputeCornerAngles(mesh); 155 | for (auto eiter = mesh.edges_begin(); eiter != mesh.edges_end(); ++eiter) { 156 | EdgeHandle e = *eiter; 157 | HalfedgeHandle h0 = mesh.halfedge_handle(e, 0); 158 | HalfedgeHandle h1 = mesh.halfedge_handle(e, 1); 159 | HalfedgeHandle h0_next; 160 | if (!mesh.is_boundary(h0)) 161 | h0_next = mesh.next_halfedge_handle(h0); 162 | HalfedgeHandle h1_next; 163 | if (!mesh.is_boundary(h1)) 164 | h1_next = mesh.next_halfedge_handle(h1); 165 | 166 | double weight = 0.; 167 | 168 | if (h0_next.is_valid()) 169 | weight += 1. / tan(mesh.data(h0_next).angle()); 170 | if (h1_next.is_valid()) 171 | weight += 1. / tan(mesh.data(h1_next).angle()); 172 | weight *= 0.5; 173 | //if (weight < 0) 174 | //weight = 0.01; 175 | mesh.data(h0).set_weight(weight); 176 | mesh.data(h1).set_weight(weight); 177 | } 178 | } 179 | 180 | void BFFSolver::ReindexVertices(SurfaceMesh & mesh) 181 | { 182 | using namespace OpenMesh; 183 | int n_interior = 0; 184 | int n_boundary = 0; 185 | 186 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 187 | VertexHandle v = *viter; 188 | if (!mesh.is_boundary(v)) { 189 | mesh.data(v).set_reindex(n_interior); 190 | ++n_interior; 191 | } 192 | } 193 | 194 | mesh.RequestBoundary(); 195 | if (mesh.GetBoundaries().size() == 0) return; 196 | auto boundary = mesh.GetBoundaries().front(); 197 | std::list boundary_list(boundary.begin(), boundary.end()); 198 | for (auto it = boundary_list.begin(); it != boundary_list.end(); ++it) { 199 | if (!mesh.data(mesh.from_vertex_handle(*it)).equivalent_vertex().is_valid()) { 200 | boundary_list.insert(boundary_list.end(), boundary_list.begin(), it); 201 | boundary_list.erase(boundary_list.begin(), it); 202 | break; 203 | } 204 | } 205 | 206 | for (auto it = boundary_list.begin(); it != boundary_list.end(); ++it) { 207 | mesh.data(mesh.from_vertex_handle(*it)).set_reindex(n_interior+n_boundary); 208 | ++n_boundary; 209 | } 210 | 211 | 212 | 213 | n_boundary_ = n_boundary; 214 | n_interior_ = n_interior; 215 | } 216 | 217 | void BFFSolver::BoundaryTargetKKnown() 218 | { 219 | using namespace Eigen; 220 | using namespace OpenMesh; 221 | SurfaceMesh &mesh = sliced_mesh_; 222 | VectorXd target_k(mesh.n_vertices()); 223 | target_k.setZero(); 224 | 225 | ReindexVertices(mesh); 226 | 227 | std::cout << "Singularities' curvature:" << std::endl; 228 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 229 | VertexHandle v = *viter; 230 | if (mesh.data(v).is_singularity()) { 231 | target_k(mesh.data(v).reindex()) = mesh.data(v).target_curvature(); 232 | std::cout << mesh.data(v).target_curvature() / PI << "pi" << std::endl; 233 | } 234 | } 235 | VectorXd k_B = target_k.segment(n_interior_, n_boundary_); 236 | VectorXd u = BoundaryTargetKToU(k_B); 237 | 238 | mesh.RequestBoundary(); 239 | auto boundary = mesh.GetBoundaries().front(); 240 | for (auto it = boundary.begin(); it != boundary.end(); ++it) { 241 | VertexHandle v = mesh.to_vertex_handle(*it); 242 | mesh.data(v).set_u(u(mesh.data(v).reindex() - n_interior_)); 243 | } 244 | 245 | } 246 | 247 | 248 | 249 | void BFFSolver::FreeBoundary() 250 | { 251 | using namespace Eigen; 252 | using namespace OpenMesh; 253 | SurfaceMesh &mesh = sliced_mesh_; 254 | 255 | VectorXd u(mesh.n_vertices()); 256 | ReindexVertices(mesh); 257 | 258 | u.setZero(); 259 | 260 | Eigen::VectorXd u_B = u.segment(n_interior_, n_boundary_); 261 | 262 | Eigen::VectorXd target_k = BoundaryUToTargetK(u_B); 263 | 264 | auto boundary = sliced_mesh_.GetBoundaries().front(); 265 | for (auto it = boundary.begin(); it != boundary.end(); ++it) { 266 | VertexHandle v = sliced_mesh_.to_vertex_handle(*it); 267 | mesh.data(v).set_target_curvature(target_k(mesh.data(v).reindex() - n_interior_)); 268 | mesh.data(v).set_u(0); 269 | } 270 | 271 | std::cout << "Singularities' curvature:" << std::endl; 272 | for (auto it = cone_vts_.begin(); it != cone_vts_.end(); ++it) { 273 | VertexHandle v = *it; 274 | std::cout << mesh.data(v).target_curvature() / PI << "pi" << std::endl; 275 | } 276 | 277 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 278 | VertexHandle v = *viter; 279 | mesh.data(v).reindex(); 280 | } 281 | 282 | 283 | } 284 | 285 | void BFFSolver::GlobalParameterization() 286 | { 287 | using namespace Eigen; 288 | SurfaceMesh &mesh = mesh_; 289 | using namespace OpenMesh; 290 | // Using Cherrier Formula 291 | // Construct Sparse system; 292 | ComputeLaplacian(mesh, true); 293 | ComputeVertexCurvatures(mesh); 294 | Eigen::VectorXd b(mesh.n_vertices()); 295 | 296 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 297 | VertexHandle v = *viter; 298 | if (!mesh.property(cone_flag_, v)) { 299 | b(mesh.data(v).reindex()) = mesh.data(v).curvature(); 300 | } 301 | else { 302 | if (mesh.is_boundary(v)) 303 | b(mesh.data(v).reindex()) = mesh.data(v).curvature() - (PI - mesh.property(cone_angle_, v)); 304 | else 305 | b(mesh.data(v).reindex()) = mesh.data(v).curvature() - (2 * PI - mesh.property(cone_angle_, v)); 306 | } 307 | } 308 | 309 | b(mesh.data(*mesh.vertices_begin()).reindex()) = 0; 310 | 311 | SparseLU, COLAMDOrdering> solver; 312 | solver.compute(Delta_); 313 | if (solver.info() != Eigen::Success){ 314 | std::cerr << "Waring: Eigen decomposition failed" << std::endl; 315 | } 316 | Eigen::VectorXd u = solver.solve(b); 317 | 318 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 319 | VertexHandle v = *viter; 320 | mesh.data(v).set_u(u(mesh.data(v).reindex())); 321 | } 322 | 323 | ReindexVertices(sliced_mesh_); 324 | u.resize(sliced_mesh_.n_vertices()); 325 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 326 | VertexHandle v = *viter; 327 | auto verts = mesh.property(split_to_, v); 328 | for (auto it = verts.begin(); it != verts.end(); ++it) { 329 | sliced_mesh_.data(*it).set_u(mesh.data(v).u()); 330 | u(sliced_mesh_.data(*it).reindex()) = mesh.data(v).u(); 331 | } 332 | } 333 | 334 | 335 | // Convert u to k 336 | VectorXd uB = u.segment(n_interior_, n_boundary_); 337 | Eigen::VectorXd target_k = BoundaryUToTargetK(uB); 338 | auto boundary = sliced_mesh_.GetBoundaries().front(); 339 | for (auto it = boundary.begin(); it != boundary.end(); ++it) { 340 | VertexHandle v = sliced_mesh_.to_vertex_handle(*it); 341 | sliced_mesh_.data(v).set_target_curvature(target_k(sliced_mesh_.data(v).reindex() - n_interior_)); 342 | } 343 | 344 | std::cout << "Singularities' curvature:" << std::endl; 345 | for (auto it = cone_vts_.begin(); it != cone_vts_.end(); ++it) { 346 | VertexHandle v = *it; 347 | std::cout << sliced_mesh_.data(v).target_curvature() / PI << "pi" << std::endl; 348 | } 349 | 350 | } 351 | 352 | 353 | Eigen::VectorXd BFFSolver::BoundaryUToTargetK(Eigen::VectorXd & u) 354 | { 355 | using namespace Eigen; 356 | using namespace OpenMesh; 357 | 358 | SurfaceMesh &mesh = sliced_mesh_; 359 | 360 | ComputeLaplacian(mesh); 361 | VectorXd omega(mesh.n_vertices()); 362 | VectorXd k(n_boundary_); 363 | 364 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 365 | VertexHandle v = *viter; 366 | omega(mesh.data(v).reindex()) = mesh.data(v).curvature(); 367 | } 368 | 369 | k = omega.segment(n_interior_, n_boundary_); 370 | SparseLU> solver; 371 | solver.compute(Delta_.block(0, 0, n_interior_, n_interior_)); 372 | if (solver.info() != Eigen::Success) 373 | { 374 | std::cerr << "Waring: Eigen decomposition failed" << std::endl; 375 | } 376 | 377 | Eigen::SparseMatrix A_IB = Delta_.block(0, n_interior_, n_interior_, n_boundary_); 378 | Eigen::SparseMatrix A_BB = Delta_.block(n_interior_, n_interior_, n_boundary_, n_boundary_); 379 | Eigen::SparseMatrix A_BI = Delta_.block(n_interior_, 0, n_boundary_, n_interior_); 380 | VectorXd omega_I = omega.segment(0, n_interior_); 381 | Eigen::VectorXd to_inverse = omega_I - A_IB * u; 382 | Eigen::VectorXd inverse = solver.solve(to_inverse); 383 | assert((Delta_.block(0, 0, n_interior_, n_interior_) * inverse - to_inverse).norm() < 1e-7); 384 | Eigen::VectorXd h = A_BI * inverse + A_BB * u; 385 | 386 | Eigen::VectorXd target_k = k - h; 387 | 388 | std::cout << "Sum of target total curvature:" << target_k.sum() / PI << " pi" << std::endl; 389 | 390 | return target_k; 391 | 392 | } 393 | 394 | Eigen::VectorXd BFFSolver::BoundaryTargetKToU(Eigen::VectorXd & target_k) 395 | { 396 | using namespace Eigen; 397 | using namespace OpenMesh; 398 | 399 | SurfaceMesh &mesh = sliced_mesh_; 400 | ComputeLaplacian(mesh, true); 401 | 402 | VectorXd omega(mesh.n_vertices()); 403 | VectorXd h(mesh.n_vertices()); 404 | 405 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 406 | VertexHandle v = *viter; 407 | if (!mesh.is_boundary(v)) { 408 | omega(mesh.data(v).reindex()) = mesh.data(v).curvature(); 409 | h(mesh.data(v).reindex()) = 0.; 410 | } 411 | else { 412 | omega(mesh.data(v).reindex()) = 0; 413 | h(mesh.data(v).reindex()) = (mesh.data(v).curvature() - target_k(mesh.data(v).reindex() - n_interior_)); 414 | } 415 | 416 | } 417 | 418 | omega(mesh.data(*mesh.vertices_begin()).reindex()) = 0; 419 | 420 | 421 | SparseLU> solver; 422 | solver.compute(Delta_); 423 | if (solver.info() != Eigen::Success) 424 | { 425 | std::cerr << "Waring: Eigen decomposition failed" << std::endl; 426 | } 427 | 428 | Eigen::VectorXd u = solver.solve(omega + h); 429 | 430 | return u.segment(n_interior_, n_boundary_); 431 | 432 | 433 | } 434 | 435 | void BFFSolver::IntegrateBoundaryCurve() 436 | { 437 | using namespace OpenMesh; 438 | using namespace Eigen; 439 | SurfaceMesh &mesh = sliced_mesh_; 440 | 441 | VPropHandleT cumulative_angle; 442 | VPropHandleT tangent; 443 | mesh.add_property(cumulative_angle); 444 | mesh.add_property(tangent); 445 | 446 | Eigen::MatrixXd T(2, n_boundary_); // Tangent vector 447 | 448 | T.setZero(); 449 | 450 | Eigen::VectorXd L_star(n_boundary_); // edge length after conformal map 451 | Eigen::VectorXd L(n_boundary_); // mesh euclidean length. 452 | auto boundary = mesh.GetBoundaries().front(); 453 | mesh.property(cumulative_angle, mesh.from_vertex_handle(boundary.front())) = mesh.data(mesh.from_vertex_handle(boundary.front())).target_curvature(); 454 | int i = 0; 455 | for (auto it = boundary.begin(); it != boundary.end(); ++it, ++i) { 456 | VertexHandle v0 = mesh.from_vertex_handle(*it); 457 | VertexHandle v1 = mesh.to_vertex_handle(*it); 458 | EdgeHandle e = mesh.edge_handle(*it); 459 | double u0 = mesh.data(v0).u(); 460 | double u1 = mesh.data(v1).u(); 461 | double l = mesh.calc_edge_length(e); 462 | double l_star = exp(0.5 * (u0 + u1)) * l; 463 | double angle = mesh.property(cumulative_angle, v0); 464 | mesh.property(cumulative_angle, v1) = angle + mesh.data(v1).target_curvature(); 465 | mesh.property(tangent, v0) = Vec2d(cos(angle), sin(angle)); 466 | 467 | T(0, i) = cos(angle); 468 | T(1, i) = sin(angle); 469 | L_star(i) = l_star; 470 | L(i) = l; 471 | } 472 | 473 | // Reindex boundary halfedges 474 | HPropHandleT reindex; 475 | mesh.add_property(reindex); 476 | 477 | int index = 0; 478 | for (auto it = boundary.begin(); it != boundary.end(); ++it, ++index) { 479 | HalfedgeHandle h = *it; 480 | mesh.property(reindex, h) = index; 481 | } 482 | 483 | // Equivalent edges produced by cut should be of the same length. 484 | std::vector oppo_relation(boundary.size()); 485 | 486 | for (auto it = boundary.begin(); it != boundary.end(); ++it) { 487 | HalfedgeHandle h = *it; 488 | HalfedgeHandle oppo_inner = mesh.data(mesh.opposite_halfedge_handle(h)).original_opposition(); 489 | if (oppo_inner.is_valid()) { 490 | HalfedgeHandle oppo = mesh.opposite_halfedge_handle(oppo_inner); 491 | oppo_relation[mesh.property(reindex, h)] = mesh.property(reindex, oppo); 492 | oppo_relation[mesh.property(reindex, oppo)] = mesh.property(reindex, h); 493 | } 494 | else { 495 | oppo_relation[mesh.property(reindex, h)] = -1; 496 | } 497 | } 498 | 499 | std::vector valid_halfedges; 500 | for (int i = 0; i < oppo_relation.size(); ++i) { 501 | if (i > oppo_relation[i] && oppo_relation[i] != -1) continue; 502 | valid_halfedges.push_back(boundary[i]); 503 | } 504 | 505 | 506 | int n_valid_h = valid_halfedges.size(); 507 | 508 | Eigen::MatrixXd N(n_valid_h, n_valid_h); 509 | N.setZero(); 510 | for (int i = 1; i < n_valid_h; ++i) { 511 | HalfedgeHandle h = valid_halfedges[i]; 512 | int index = mesh.property(reindex, h); 513 | int oppo_index = oppo_relation[index]; 514 | N(i, i) = L_star(index); 515 | if (oppo_index >= 0) { 516 | N(i, i) *= 2; 517 | } 518 | } 519 | 520 | Eigen::MatrixXd newT(T.rows(), n_valid_h); 521 | Eigen::VectorXd L_star_valid(n_valid_h); 522 | 523 | for (int i = 0; i < n_valid_h; ++i) { 524 | HalfedgeHandle h = valid_halfedges[i]; 525 | int index = mesh.property(reindex, h); 526 | int oppo_index = oppo_relation[index]; 527 | newT.col(i) = T.col(index); 528 | if (oppo_index >= 0) 529 | newT.col(i) += T.col(oppo_index); 530 | L_star_valid(i) = L_star(index); 531 | } 532 | 533 | // Use quadratic programming to get optimal solutions. 534 | Eigen::SparseMatrix Q = (0.5 * N * N).sparseView(); 535 | Eigen::VectorXd B = - N.diagonal(); 536 | 537 | Eigen::SparseMatrix A_eq(2, Q.rows()); 538 | A_eq = newT.sparseView(); 539 | Eigen::VectorXd B_eq(2); B_eq.setZero(); 540 | 541 | Eigen::SparseMatrix A_ieq = (-Eigen::MatrixXd::Identity(Q.rows(), Q.cols())).sparseView(); 542 | Eigen::VectorXd B_ieq = -Eigen::VectorXd::Constant(A_ieq.rows(), -1e-3); 543 | 544 | Eigen::VectorXd lx = Eigen::VectorXd::Constant(Q.cols(), -1000); 545 | Eigen::VectorXd ux = Eigen::VectorXd::Constant(Q.cols(), 1000); 546 | 547 | Eigen::VectorXd L_normalized_valid(L_star_valid.size()); 548 | L_normalized_valid = L_star_valid; 549 | igl::active_set_params as; 550 | igl::active_set(Q, B, Eigen::VectorXi(), Eigen::VectorXd(), A_eq, B_eq, A_ieq, B_ieq, lx, ux, as, L_normalized_valid); 551 | Eigen::VectorXd L_normalized(L_star.size()); 552 | 553 | 554 | for (int i = 0; i < n_valid_h; ++i) { 555 | HalfedgeHandle h = valid_halfedges[i]; 556 | int index = mesh.property(reindex, h); 557 | int oppo_index = oppo_relation[index]; 558 | L_normalized(index) = L_normalized_valid[i]; 559 | if (oppo_index >= 0) 560 | L_normalized(oppo_index) = L_normalized_valid(i); 561 | } 562 | 563 | i = 0; 564 | mesh.set_texcoord2D(mesh.from_vertex_handle(boundary.front()), Vec2d(0, 0)); 565 | for (auto it = boundary.begin(); it != boundary.end(); ++it, ++i) { 566 | VertexHandle v0 = mesh.from_vertex_handle(*it); 567 | VertexHandle v1 = mesh.to_vertex_handle(*it); 568 | Vec2d coord = mesh.texcoord2D(v0) + mesh.property(tangent, v0) * L_normalized(i); 569 | mesh.set_texcoord2D(v1, coord); 570 | } 571 | 572 | } 573 | 574 | void BFFSolver::ExtendToInteriorHilbert() 575 | { 576 | using namespace Eigen; 577 | using namespace OpenMesh; 578 | SurfaceMesh &mesh = sliced_mesh_; 579 | ComputeHarmonicMatrix(); 580 | 581 | Eigen::VectorXd a_boundary(mesh.n_vertices()); 582 | a_boundary.setZero(); 583 | 584 | auto boundary = mesh.GetBoundaries().front(); 585 | for (int i = 0; i < boundary.size(); ++i) { 586 | VertexHandle v = mesh.to_vertex_handle(boundary[i]); 587 | Vec2d coord = mesh.texcoord2D(v); 588 | a_boundary(v.idx()) = coord[0]; 589 | } 590 | 591 | SparseLU> solver; 592 | solver.compute(Delta_); 593 | if (solver.info() != Eigen::Success) 594 | { 595 | std::cerr << "Waring: Eigen decomposition failed" << std::endl; 596 | } 597 | Eigen::VectorXd a = solver.solve(a_boundary); 598 | 599 | ComputeLaplacian(mesh); 600 | Eigen::VectorXd h(mesh.n_vertices()); 601 | h.setZero(); 602 | for (int i = 0; i < boundary.size(); ++i) { 603 | VertexHandle v = mesh.to_vertex_handle(boundary[i]); 604 | VertexHandle v_prev = mesh.from_vertex_handle(boundary[i]); 605 | VertexHandle v_next = mesh.to_vertex_handle(boundary[(i + 1) % boundary.size()]); 606 | h(mesh.data(v).reindex()) = -0.5 * (a(v_next.idx()) - a(v_prev.idx())); 607 | } 608 | 609 | solver.compute(Delta_); 610 | if (solver.info() != Eigen::Success) 611 | { 612 | std::cerr << "Waring: Eigen decomposition failed" << std::endl; 613 | } 614 | Eigen::VectorXd b = solver.solve(h); 615 | 616 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 617 | VertexHandle v = *viter; 618 | mesh.set_texcoord2D(v, Vec2d(a(v.idx()), b(mesh.data(v).reindex()))); 619 | } 620 | } 621 | 622 | void BFFSolver::ExtendToInteriorHarmonic() 623 | { 624 | using namespace Eigen; 625 | using namespace OpenMesh; 626 | SurfaceMesh &mesh = sliced_mesh_; 627 | ComputeHarmonicMatrix(); 628 | 629 | Eigen::MatrixXd uv_boundary(mesh.n_vertices(), 2); 630 | uv_boundary.setZero(); 631 | 632 | auto boundary = mesh.GetBoundaries().front(); 633 | for (int i = 0; i < boundary.size(); ++i) { 634 | VertexHandle v = mesh.to_vertex_handle(boundary[i]); 635 | Vec2d coord = mesh.texcoord2D(v); 636 | uv_boundary(v.idx(), 0) = coord[0]; 637 | uv_boundary(v.idx(), 1) = coord[1]; 638 | } 639 | 640 | SparseLU> solver; 641 | solver.compute(Delta_); 642 | if (solver.info() != Eigen::Success) 643 | { 644 | std::cerr << "Waring: Eigen decomposition failed" << std::endl; 645 | } 646 | Eigen::MatrixXd uv = solver.solve(uv_boundary); 647 | 648 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 649 | VertexHandle v = *viter; 650 | mesh.set_texcoord2D(v, Vec2d(uv(v.idx(), 0), uv(v.idx(), 1))); 651 | } 652 | } 653 | 654 | void BFFSolver::ComputeHarmonicMatrix() 655 | { 656 | using namespace OpenMesh; 657 | using namespace Eigen; 658 | SurfaceMesh &mesh = sliced_mesh_; 659 | 660 | Delta_.resize(mesh.n_vertices(), mesh.n_vertices()); 661 | Delta_.setZero(); 662 | std::vector> A_coefficients; 663 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 664 | VertexHandle v = *viter; 665 | if (mesh.is_boundary(v)) { 666 | A_coefficients.push_back(Eigen::Triplet(v.idx(), v.idx(), 1.)); 667 | continue; 668 | } 669 | double s_w = 0; 670 | for (SurfaceMesh::VertexVertexIter vviter = mesh.vv_iter(v); vviter.is_valid(); ++vviter) { 671 | VertexHandle neighbor = *vviter; 672 | HalfedgeHandle h = mesh.find_halfedge(v, neighbor); 673 | double n_w = mesh.data(h).weight(); 674 | s_w += n_w; 675 | A_coefficients.push_back(Eigen::Triplet(v.idx(), neighbor.idx(), -n_w)); 676 | } 677 | A_coefficients.push_back(Eigen::Triplet(v.idx(), v.idx(), s_w)); 678 | } 679 | Delta_.setFromTriplets(A_coefficients.begin(), A_coefficients.end()); 680 | } 681 | 682 | void BFFSolver::NormalizeUV() 683 | { 684 | using namespace OpenMesh; 685 | SurfaceMesh &mesh = sliced_mesh_; 686 | Vec2d s(0, 0); 687 | for (SurfaceMesh::VertexIter viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 688 | VertexHandle v = *viter; 689 | s += mesh.texcoord2D(v); 690 | } 691 | s /= mesh.n_vertices(); 692 | for (SurfaceMesh::VertexIter viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 693 | VertexHandle v = *viter; 694 | mesh.set_texcoord2D(v, mesh.texcoord2D(v) -s); 695 | } 696 | 697 | double scale = 0; 698 | for (SurfaceMesh::VertexIter viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 699 | VertexHandle v = *viter; 700 | scale = mesh.texcoord2D(v).norm() > scale ? mesh.texcoord2D(v).norm() : scale; 701 | } 702 | for (SurfaceMesh::VertexIter viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 703 | VertexHandle v = *viter; 704 | mesh.set_texcoord2D(v, mesh.texcoord2D(v) / scale); 705 | } 706 | } 707 | 708 | 709 | -------------------------------------------------------------------------------- /src/BoundaryFirstFlattening/BFF.h: -------------------------------------------------------------------------------- 1 | #ifndef BOUNDARY_FIRST_FLATTENING 2 | #define BOUNDARY_FIRST_FLATTENING 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "BFFInitializer.h" 10 | 11 | #include 12 | 13 | #ifndef PI 14 | #define PI 3.141592653 15 | #endif 16 | 17 | // This class is the implementation of paper Boundary First Flattening. 18 | // 19 | // BFF algorithm parameterize the surface according to boundary data. 20 | // We need one of two kinds of boundary as input: conformal factors or target geodesic curvature. 21 | // These two kinds of data can be converted to each other. 22 | // Known boundary data is determined by user and processed in the initializer for BFF. 23 | class BFFSolver { 24 | public: 25 | BFFSolver(SurfaceMesh &mesh, OpenMesh::VPropHandleT cone_flag, OpenMesh::VPropHandleT cone_angle, OpenMesh::EPropHandleT slice_flag); 26 | SurfaceMesh Compute(int mode = 0); 27 | std::vector ConeVertices() { return cone_vts_; } 28 | 29 | protected: 30 | SurfaceMesh &mesh_; 31 | SurfaceMesh sliced_mesh_; 32 | 33 | std::vector cone_vts_; 34 | 35 | OpenMesh::VPropHandleT cone_flag_; 36 | OpenMesh::VPropHandleT cone_angle_; 37 | OpenMesh::EPropHandleT slice_flag_; 38 | 39 | OpenMesh::VPropHandleT> split_to_; 40 | 41 | Eigen::SparseMatrix Delta_; 42 | 43 | int n_boundary_; 44 | int n_interior_; 45 | int n_cones_; 46 | 47 | protected: 48 | 49 | // Cut the mesh into disk, and set all kinds of data and flags. 50 | void Init(); 51 | 52 | double CosineLaw(double a, double b, double c); 53 | 54 | // Compute mesh data 55 | void ComputeCornerAngles(SurfaceMesh &mesh, Eigen::VectorXd l = Eigen::VectorXd()); 56 | void ComputeHalfedgeWeights(SurfaceMesh &mesh); 57 | void ComputeVertexCurvatures(SurfaceMesh &mesh, Eigen::VectorXd l = Eigen::VectorXd()); 58 | 59 | // Compute cotangent Laplacian operator. 60 | void ComputeLaplacian(SurfaceMesh &mesh, bool mode = false); 61 | 62 | // Seperate inner vertices and boundary vertices. 63 | void ReindexVertices(SurfaceMesh &mesh); 64 | 65 | // Convert boundary target geodesic curvature into conformal factors. 66 | void BoundaryTargetKKnown(); 67 | 68 | // To minimize area distorsion, we preserve the length of boundary, i.e. u_B = 0. 69 | // And then we convert conformal factors u_B to target curvature. 70 | void FreeBoundary(); 71 | 72 | // To compute a global parameterization with fixed cone angles. 73 | void GlobalParameterization(); 74 | 75 | 76 | // The operator that convert boundary conformal factors to target curvatures. 77 | Eigen::VectorXd BoundaryUToTargetK(Eigen::VectorXd &u); 78 | 79 | // The operator that convert boundary target curvatures to conformal factors. 80 | Eigen::VectorXd BoundaryTargetKToU(Eigen::VectorXd &k); 81 | 82 | // Integrate boundary data into a closed loop. 83 | void IntegrateBoundaryCurve(); 84 | 85 | // Given boundary's embedding, we use harmonic map to get one component. 86 | // And minimize conformal energy use hilbert transform over the other component. 87 | void ExtendToInteriorHilbert(); 88 | 89 | // Use harmonic map on both components. 90 | void ExtendToInteriorHarmonic(); 91 | 92 | void ComputeHarmonicMatrix(); 93 | 94 | // Normalize uvs s.t. they all fall in unit circle. 95 | void NormalizeUV(); 96 | }; 97 | 98 | #endif // !BOUNDARY_FIRST_FLATTENING 99 | -------------------------------------------------------------------------------- /src/BoundaryFirstFlattening/BFFInitializer.cpp: -------------------------------------------------------------------------------- 1 | #include "BFFInitializer.h" 2 | 3 | BFFInitializer::BFFInitializer(SurfaceMesh & mesh) 4 | : mesh_(mesh) 5 | { 6 | 7 | } 8 | 9 | void BFFInitializer::Initiate(SurfaceMesh & sliced_mesh, OpenMesh::VPropHandleT cone_flag, OpenMesh::VPropHandleT cone_angle, OpenMesh::EPropHandleT slice_flag) 10 | { 11 | cone_flag_ = cone_flag; 12 | cone_angle_ = cone_angle; 13 | slice_flag_ = slice_flag; 14 | CutMesh(sliced_mesh); 15 | } 16 | 17 | void BFFInitializer::CutMesh(SurfaceMesh & sliced_mesh) 18 | { 19 | using namespace OpenMesh; 20 | SurfaceMesh &mesh = mesh_; 21 | 22 | MeshSlicer slicer(mesh); 23 | slicer.ResetFlags(); 24 | for (auto eiter = mesh.edges_begin(); eiter != mesh.edges_end(); ++eiter) { 25 | EdgeHandle e = *eiter; 26 | if (mesh.property(slice_flag_, e)) { 27 | slicer.AddOnCutEdge(e); 28 | } 29 | } 30 | 31 | slicer.ConstructWedge(); 32 | slicer.SliceAccordingToWedge(sliced_mesh); 33 | 34 | for (auto eiter = mesh.edges_begin(); eiter != mesh.edges_end(); ++eiter) { 35 | EdgeHandle e = *eiter; 36 | if (mesh.is_boundary(e)) continue; 37 | HalfedgeHandle h0 = mesh.halfedge_handle(e, 0); 38 | HalfedgeHandle h1 = mesh.halfedge_handle(e, 1); 39 | HalfedgeHandle h0_to = slicer.ConvertTo(h0); 40 | HalfedgeHandle h1_to = slicer.ConvertTo(h1); 41 | sliced_mesh.data(h0_to).set_original_opposition(h1_to); 42 | sliced_mesh.data(h1_to).set_original_opposition(h0_to); 43 | } 44 | 45 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 46 | VertexHandle v = *viter; 47 | 48 | auto verts = slicer.SplitTo(v); 49 | 50 | for (auto it = verts.begin(); it != verts.end(); ++it) { 51 | VertexHandle sv = *it; 52 | sliced_mesh.data(sv).set_target_curvature(0); 53 | if (mesh.property(cone_flag_, v)) { 54 | double angle = mesh_.property(cone_angle_, v) / verts.size(); 55 | sliced_mesh.data(sv).set_singularity(true); 56 | if (sliced_mesh.is_boundary(sv)) 57 | sliced_mesh.data(sv).set_target_curvature(PI - angle); 58 | else 59 | sliced_mesh.data(sv).set_target_curvature(2 * PI - angle); 60 | } 61 | } 62 | 63 | } 64 | 65 | 66 | split_to_ = slicer.split_to(); 67 | 68 | sliced_mesh.RequestBoundary(); 69 | auto boundary = sliced_mesh.GetBoundaries().front(); 70 | for (auto it = boundary.begin(); it != boundary.end(); ++it) { 71 | HalfedgeHandle h = *it; 72 | if (sliced_mesh.data(sliced_mesh.from_vertex_handle(h)).is_singularity()) { 73 | cone_vertices_.push_back(sliced_mesh.from_vertex_handle(h)); 74 | } 75 | } 76 | 77 | } 78 | 79 | -------------------------------------------------------------------------------- /src/BoundaryFirstFlattening/BFFInitializer.h: -------------------------------------------------------------------------------- 1 | #ifndef BFF_INITIALIZER_H_ 2 | #define BFF_INITIALIZER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifndef PI 11 | #define PI 3.141592653 12 | #endif 13 | 14 | // This class is a warper of class MeshSlicer to serve for BFF algorithm. 15 | // The first step is to cut the mesh to disk. 16 | // And then it will set singularity flags and target curvatures according to user's input. 17 | // The constructor receive three properties: 18 | // cone_flag: whether a vertex is a cone point , 19 | // cone_angle: the target sum angles around cones, 20 | // slice_flag: whether a edge is on slice. 21 | class BFFInitializer { 22 | public: 23 | BFFInitializer(SurfaceMesh &mesh); 24 | void Initiate(SurfaceMesh &sliced_mesh, OpenMesh::VPropHandleT cone_flag, OpenMesh::VPropHandleT cone_angle, OpenMesh::EPropHandleT slice_flag); 25 | std::vector GetConeVertices() { return cone_vertices_; } 26 | OpenMesh::VPropHandleT> split_to() { return split_to_; } 27 | protected: 28 | SurfaceMesh &mesh_; 29 | std::vector cone_vertices_; 30 | OpenMesh::VPropHandleT cone_flag_; 31 | OpenMesh::VPropHandleT cone_angle_; 32 | OpenMesh::EPropHandleT slice_flag_; 33 | 34 | //this property stores the each vertex is splitted to what vertices. 35 | OpenMesh::VPropHandleT> split_to_; 36 | 37 | protected: 38 | // Cut the mesh into a disk. 39 | void CutMesh(SurfaceMesh &sliced_mesh); 40 | 41 | }; 42 | 43 | 44 | #endif -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(Mesh Utilities BoundaryFirstFlattening OrbifoldEmbedding Viewer) 2 | 3 | file(GLOB MESH_HEADERS Mesh/*.h) 4 | file(GLOB MESH_SOURCES Mesh/*.cpp) 5 | add_library(Mesh ${MESH_HEADERS} ${MESH_SOURCES}) 6 | target_link_libraries(Mesh ${OpenMesh_LIBRARIES}) 7 | 8 | file(GLOB UTILITIES_HEADERS Utilities/*.h) 9 | file(GLOB UTILITIES_SOURCES Utilities/*.cpp) 10 | add_library(Utilities ${UTILITIES_HEADERS} ${UTILITIES_SOURCES}) 11 | target_link_libraries(Utilities Mesh) 12 | 13 | file(GLOB BFF_HEADERS BoundaryFirstFlattening/*.h) 14 | file(GLOB BFF_SOURCES BoundaryFirstFlattening/*.cpp) 15 | add_library(BoundaryFirstFlattening ${BFF_HEADERS} ${BFF_SOURCES}) 16 | target_link_libraries(BoundaryFirstFlattening Mesh igl::core) 17 | 18 | file(GLOB OE_HEADERS OrbifoldEmbedding/*.h) 19 | file(GLOB OE_SOURCES OrbifoldEmbedding/*.cpp) 20 | add_library(OrbifoldEmbedding ${OE_HEADERS} ${OE_SOURCES}) 21 | target_link_libraries(OrbifoldEmbedding Mesh Utilities) 22 | 23 | 24 | file(GLOB VIEWER_HEADERS Viewer/*.h) 25 | file(GLOB VIEWER_SOURCES Viewer/*.cpp) 26 | add_executable(Viewer ${VIEWER_HEADERS} ${VIEWER_SOURCES}) 27 | target_link_libraries(Viewer Mesh Utilities OrbifoldEmbedding BoundaryFirstFlattening igl::png igl::core igl::opengl igl::glfw igl::imgui OpenMeshCore OpenMeshTools) -------------------------------------------------------------------------------- /src/Mesh/MeshDefinition.cpp: -------------------------------------------------------------------------------- 1 | #include "MeshDefinition.h" 2 | 3 | SurfaceMesh::SurfaceMesh() 4 | { 5 | 6 | } 7 | 8 | void SurfaceMesh::RequestBoundary() 9 | { 10 | OpenMesh::HPropHandleT touched; 11 | this->add_property(touched); 12 | boundaries.clear(); 13 | bool has_boundary = false; 14 | for (HalfedgeIter hiter = halfedges_begin(); hiter != halfedges_end(); ++hiter) { 15 | HalfedgeHandle h = *hiter; 16 | property(touched, h) = false; 17 | if (is_boundary(h)) { 18 | has_boundary = true; 19 | } 20 | } 21 | if (!has_boundary) return; 22 | bool stop; 23 | do { 24 | stop = true; 25 | HalfedgeHandle hs, hc; 26 | for (HalfedgeIter hiter = halfedges_begin(); hiter != halfedges_end(); ++hiter) { 27 | HalfedgeHandle h = *hiter; 28 | if (is_boundary(h) && !property(touched, h)) { 29 | stop = false; 30 | hs = h; 31 | hc = next_halfedge_handle(h); 32 | break; 33 | } 34 | } 35 | if (hs.is_valid()) { 36 | std::vector boundary; 37 | boundary.push_back(hs); 38 | property(touched, hs) = true; 39 | while (hc != hs) { 40 | boundary.push_back(hc); 41 | property(touched, hc) = true; 42 | hc = next_halfedge_handle(hc); 43 | } 44 | boundaries.push_back(boundary); 45 | } 46 | } while (!stop); 47 | } 48 | 49 | std::vector> SurfaceMesh::GetBoundaries() 50 | { 51 | return boundaries; 52 | } 53 | 54 | void NormalizeMesh(SurfaceMesh & mesh) 55 | { 56 | using namespace OpenMesh; 57 | Vec3d s(0, 0, 0); 58 | for (SurfaceMesh::VertexIter viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 59 | VertexHandle v = *viter; 60 | s += mesh.point(v); 61 | } 62 | s /= mesh.n_vertices(); 63 | for (SurfaceMesh::VertexIter viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 64 | VertexHandle v = *viter; 65 | mesh.set_point(v, mesh.point(v) - s); 66 | } 67 | 68 | double scale = 0; 69 | for (SurfaceMesh::VertexIter viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 70 | VertexHandle v = *viter; 71 | scale = mesh.point(v).norm() > scale ? mesh.point(v).norm() : scale; 72 | } 73 | for (SurfaceMesh::VertexIter viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 74 | VertexHandle v = *viter; 75 | mesh.set_point(v, mesh.point(v) / scale); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Mesh/MeshDefinition.h: -------------------------------------------------------------------------------- 1 | #ifndef MESHDEFINITION_H 2 | #define MESHDEFINITION_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | struct SurfaceMeshTraits : public OpenMesh::DefaultTraits 10 | { 11 | typedef OpenMesh::Vec3d Point; 12 | typedef OpenMesh::Vec3d Normal; 13 | typedef OpenMesh::Vec2d TexCoord2D; 14 | 15 | VertexAttributes(OpenMesh::Attributes::Status | OpenMesh::Attributes::Normal| OpenMesh::Attributes::TexCoord2D); 16 | FaceAttributes(OpenMesh::Attributes::Status| OpenMesh::Attributes::Normal); 17 | EdgeAttributes(OpenMesh::Attributes::Status); 18 | HalfedgeAttributes(OpenMesh::Attributes::Status); 19 | 20 | VertexTraits 21 | { 22 | public: 23 | VertexT() :is_singularity_(false),order_(1) {} 24 | typename Refs::VertexHandle equivalent_vertex() { return equivalent_vertex_; } 25 | void set_equivalent_vertex(typename Refs::VertexHandle v) { equivalent_vertex_ = v; } 26 | bool is_singularity() { return is_singularity_; } 27 | int order() { return order_; } 28 | void set_order(int order) { order_ = order; } 29 | void set_singularity(bool s) { is_singularity_ = s; } 30 | OpenMesh::Vec2d gradient() { return gradient_; } 31 | void set_gradient(OpenMesh::Vec2d t) { gradient_ = t; } 32 | double curvature() { return curvature_; } 33 | double target_curvature() { return target_curvature_; } 34 | void set_curvature(double k) { curvature_ = k; } 35 | void set_target_curvature(double target_k) { target_curvature_ = target_k; } 36 | int reindex() { return reindex_; } 37 | void set_reindex(int i) { reindex_ = i; } 38 | double u() { return u_; } 39 | void set_u(double u_value) {u_ = u_value; } 40 | void set_angle_sum(double angle_sum) { angle_sum_ = angle_sum; } 41 | double angle_sum() { return angle_sum_; } 42 | void set_dual_length(double l) { dual_length_ = l; } 43 | double dual_length() { return dual_length_; } 44 | 45 | private: 46 | typename Refs::VertexHandle equivalent_vertex_; 47 | bool is_singularity_; 48 | int order_; 49 | OpenMesh::Vec2d gradient_; 50 | double curvature_; 51 | double target_curvature_; 52 | double u_; 53 | double angle_sum_; 54 | int reindex_; 55 | double dual_length_; 56 | 57 | }; 58 | 59 | HalfedgeTraits 60 | { 61 | private: 62 | typename Refs::HalfedgeHandle original_opposition_; 63 | double weight_; 64 | double angle_; 65 | public: 66 | HalfedgeT() : weight_(-1.) {} 67 | double angle() { return angle_; } 68 | void set_angle(double a) { angle_ = a; } 69 | double weight() { return weight_; } 70 | void set_weight(double w) { weight_ = w; } 71 | void set_original_opposition(typename Refs::HalfedgeHandle h) { original_opposition_ = h; } 72 | typename Refs::HalfedgeHandle original_opposition() { return original_opposition_; } 73 | }; 74 | 75 | EdgeTraits 76 | { 77 | private: 78 | double length_; 79 | public: 80 | EdgeT():length_(0.0) {} 81 | double length() { return length_; } 82 | void set_length(double l) { length_ = l; } 83 | 84 | }; 85 | 86 | FaceTraits 87 | { 88 | private: 89 | 90 | public: 91 | FaceT() {}; 92 | }; 93 | }; 94 | 95 | typedef OpenMesh::PolyMesh_ArrayKernelT BaseSurfaceMesh; 96 | 97 | class SurfaceMesh : public BaseSurfaceMesh 98 | { 99 | public: 100 | SurfaceMesh(); 101 | void RequestBoundary(); 102 | std::vector> GetBoundaries(); 103 | protected: 104 | std::vector> boundaries; 105 | }; 106 | 107 | 108 | void NormalizeMesh(SurfaceMesh &mesh); 109 | 110 | #endif -------------------------------------------------------------------------------- /src/OrbifoldEmbedding/EuclideanOrbifoldSolver.cpp: -------------------------------------------------------------------------------- 1 | #include "EuclideanOrbifoldSolver.h" 2 | #include 3 | 4 | EuclideanOrbifoldSolver::EuclideanOrbifoldSolver(SurfaceMesh & mesh, OpenMesh::VPropHandleT cone_flag, OpenMesh::VPropHandleT cone_angle, OpenMesh::EPropHandleT slice_flag) 5 | :mesh_(mesh), cone_flag_(cone_flag), cone_angle_(cone_angle), slice_flag_(slice_flag) 6 | { 7 | 8 | } 9 | 10 | SurfaceMesh EuclideanOrbifoldSolver::Compute() 11 | { 12 | if (mesh_.n_vertices() > 10) { 13 | InitOrbifold(); 14 | ComputeHalfedgeWeights(); 15 | ConstructSparseSystem(); 16 | SolveLinearSystem(); 17 | } 18 | return sliced_mesh_; 19 | } 20 | 21 | 22 | void EuclideanOrbifoldSolver::InitOrbifold() 23 | { 24 | using namespace OpenMesh; 25 | SurfaceMesh &mesh = sliced_mesh_; 26 | OrbifoldInitializer initializer(mesh_); 27 | initializer.Initiate(mesh,cone_flag_, cone_angle_, slice_flag_); 28 | initializer.ComputeEuclideanTransformations(sliced_mesh_, vtx_transit_); 29 | cone_vts_ = initializer.GetConeVertices(); 30 | segments_vts_ = initializer.GetSegments(); 31 | 32 | std::cout << "Cone coordinates:\n"; 33 | for (int i = 0; i < cone_vts_.size(); ++i) { 34 | Vec2d uv = mesh.texcoord2D(cone_vts_[i]); 35 | std::cout << uv[0] << "\t" << uv[1] << std::endl; 36 | } 37 | 38 | } 39 | 40 | 41 | double EuclideanOrbifoldSolver::CosineLaw(double a, double b, double c) 42 | { 43 | double cs = (a * a + b * b - c * c) / (2 * a * b); 44 | assert(-1 <= cs && cs <= 1); 45 | return acos(cs); 46 | } 47 | 48 | void EuclideanOrbifoldSolver::ComputeCornerAngles() 49 | { 50 | using namespace OpenMesh; 51 | SurfaceMesh &mesh = sliced_mesh_; 52 | for (auto fiter = mesh.faces_begin(); fiter != mesh.faces_end(); ++fiter) { 53 | FaceHandle f = *fiter; 54 | std::vector he; 55 | 56 | for (auto fhiter = mesh.fh_iter(f); fhiter.is_valid(); ++fhiter) { 57 | he.push_back(*fhiter); 58 | } 59 | double l[3]; 60 | l[0] = mesh.calc_edge_length(mesh.edge_handle(he[0])); 61 | l[1] = mesh.calc_edge_length(mesh.edge_handle(he[1])); 62 | l[2] = mesh.calc_edge_length(mesh.edge_handle(he[2])); 63 | for (int i = 0; i < 3; ++i) { 64 | double cs = CosineLaw(l[i], l[(i + 1) % 3], l[(i + 2) % 3]); 65 | mesh.data(he[i]).set_angle(cs); 66 | } 67 | } 68 | } 69 | 70 | void EuclideanOrbifoldSolver::ComputeHalfedgeWeights() 71 | { 72 | using namespace OpenMesh; 73 | SurfaceMesh &mesh = sliced_mesh_; 74 | 75 | ComputeCornerAngles(); 76 | for (auto eiter = mesh.edges_begin(); eiter != mesh.edges_end(); ++eiter) { 77 | EdgeHandle e = *eiter; 78 | HalfedgeHandle h0 = mesh.halfedge_handle(e, 0); 79 | HalfedgeHandle h1 = mesh.halfedge_handle(e, 1); 80 | HalfedgeHandle h0_next; 81 | if(!mesh.is_boundary(h0)) 82 | h0_next = mesh.next_halfedge_handle(h0); 83 | HalfedgeHandle h1_next; 84 | if(!mesh.is_boundary(h1)) 85 | h1_next = mesh.next_halfedge_handle(h1); 86 | 87 | double weight = 0.; 88 | 89 | if (h0_next.is_valid()) 90 | weight += 1. / tan(mesh.data(h0_next).angle()); 91 | if (h1_next.is_valid()) 92 | weight += 1. / tan(mesh.data(h1_next).angle()); 93 | weight *= 0.5; 94 | if (weight < 0) 95 | weight = 0.01; 96 | mesh.data(h0).set_weight(weight); 97 | mesh.data(h1).set_weight(weight); 98 | } 99 | 100 | } 101 | 102 | void EuclideanOrbifoldSolver::ConstructSparseSystem() 103 | { 104 | using namespace OpenMesh; 105 | using namespace Eigen; 106 | SurfaceMesh &mesh = sliced_mesh_; 107 | 108 | A_.resize(2 * mesh.n_vertices(), 2 * mesh.n_vertices()); 109 | A_.setZero(); 110 | b_.resize(2 * mesh.n_vertices()); 111 | std::vector > A_coefficients; 112 | 113 | // interior vertex satisfies normal harmonic condition 114 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 115 | VertexHandle v = *viter; 116 | if (mesh.data(v).is_singularity()) { 117 | auto uv = mesh.texcoord2D(v); 118 | b_(2 * v.idx()) = uv[0]; 119 | b_(2 * v.idx() + 1) = uv[1]; 120 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx(), 2 * v.idx(), 1.)); 121 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx() + 1, 2 * v.idx() + 1, 1.)); 122 | } 123 | else if (!mesh.is_boundary(v)) { 124 | b_(2 * v.idx()) = 0; 125 | b_(2 * v.idx() + 1) = 0; 126 | double s_w = 0; 127 | for (SurfaceMesh::VertexOHalfedgeIter vohiter = mesh.voh_iter(v); vohiter.is_valid(); ++vohiter) { 128 | HalfedgeHandle h = *vohiter; 129 | VertexHandle neighbor = mesh.to_vertex_handle(h); 130 | double n_w = mesh.data(h).weight(); 131 | s_w += n_w; 132 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx(), 2 * neighbor.idx(), -n_w)); 133 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx() + 1, 2 * neighbor.idx() + 1, -n_w)); 134 | } 135 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx(), 2 * v.idx(), s_w)); 136 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx() + 1, 2 * v.idx() + 1, s_w)); 137 | } 138 | } 139 | 140 | 141 | // handle boundary vts; 142 | for (int i = 0; i < segments_vts_.size() / 2; ++i) { 143 | for (auto it = segments_vts_[i].begin(); it != segments_vts_[i].end(); ++it) { 144 | VertexHandle v = *it; 145 | if (mesh.data(v).is_singularity()) continue; 146 | b_(2 * v.idx()) = 0; 147 | b_(2 * v.idx() + 1) = 0; 148 | double s_w = 0.; 149 | for (auto vviter = mesh.vv_iter(v); vviter.is_valid(); ++vviter) { 150 | VertexHandle neighbor = *vviter; 151 | HalfedgeHandle h = mesh.find_halfedge(v, neighbor); 152 | double n_w = mesh.data(h).weight(); 153 | s_w += n_w; 154 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx(), 2 * neighbor.idx(), -n_w)); 155 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx() + 1, 2 * neighbor.idx() + 1, -n_w)); 156 | } 157 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx(), 2 * v.idx(), s_w)); 158 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx() + 1, 2 * v.idx() + 1, s_w)); 159 | 160 | 161 | auto equiv = mesh.data(v).equivalent_vertex(); 162 | Matrix2d coeff_equiv; 163 | coeff_equiv.setZero(); 164 | Matrix3d T = mesh.property(vtx_transit_, equiv); // from equiv to v 165 | Matrix2d rotation_matrix = T.block(0,0,2,2); 166 | //std::cout << rotation_matrix << std::endl; 167 | for (auto vviter = mesh.vv_iter(equiv); vviter.is_valid(); ++vviter) { 168 | VertexHandle neighbor = *vviter; 169 | HalfedgeHandle h = mesh.find_halfedge(equiv, neighbor); 170 | double n_w = mesh.data(h).weight(); 171 | auto coeff_equiv_neighbor = n_w * rotation_matrix; 172 | coeff_equiv += coeff_equiv_neighbor; 173 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx(), 2 * neighbor.idx(), -coeff_equiv_neighbor(0,0))); 174 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx(), 2 * neighbor.idx() + 1, -coeff_equiv_neighbor(0,1))); 175 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx() + 1, 2 * neighbor.idx(), -coeff_equiv_neighbor(1,0))); 176 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx() + 1, 2 * neighbor.idx() + 1, -coeff_equiv_neighbor(1,1))); 177 | } 178 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx(), 2 * equiv.idx(), coeff_equiv(0, 0))); 179 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx(), 2 * equiv.idx() + 1, coeff_equiv(0, 1))); 180 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx() + 1, 2 * equiv.idx(), coeff_equiv(1, 0))); 181 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx() + 1, 2 * equiv.idx() + 1, coeff_equiv(1, 1))); 182 | 183 | 184 | b_.segment(2 * equiv.idx(), 2) = - T.block(0,2, 2, 1); 185 | 186 | A_coefficients.push_back(Eigen::Triplet(2 * equiv.idx(), 2 * equiv.idx(), rotation_matrix(0,0))); 187 | A_coefficients.push_back(Eigen::Triplet(2 * equiv.idx(), 2 * equiv.idx() + 1, rotation_matrix(0, 1))); 188 | A_coefficients.push_back(Eigen::Triplet(2 * equiv.idx() + 1, 2 * equiv.idx(), rotation_matrix(1, 0))); 189 | A_coefficients.push_back(Eigen::Triplet(2 * equiv.idx() + 1, 2 * equiv.idx()+1, rotation_matrix(1, 1))); 190 | 191 | A_coefficients.push_back(Eigen::Triplet(2 * equiv.idx(), 2 * v.idx(), -1.)); 192 | A_coefficients.push_back(Eigen::Triplet(2 * equiv.idx() + 1, 2 * v.idx() + 1, - 1.)); 193 | 194 | } 195 | } 196 | 197 | A_.setFromTriplets(A_coefficients.begin(), A_coefficients.end()); 198 | } 199 | 200 | void EuclideanOrbifoldSolver::SolveLinearSystem() 201 | { 202 | using namespace OpenMesh; 203 | using namespace Eigen; 204 | SurfaceMesh &mesh = sliced_mesh_; 205 | 206 | //Eigen::SparseQR, COLAMDOrdering> solver; 207 | Eigen::SparseLU> solver; 208 | solver.compute(A_); 209 | if (solver.info() != Eigen::Success) 210 | { 211 | std::cerr << "Waring: Eigen decomposition failed" << std::endl; 212 | } 213 | Eigen::VectorXd x = solver.solve(b_); 214 | std::cout << "Error:" << (A_ * x - b_).norm() << std::endl; 215 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 216 | VertexHandle v = *viter; 217 | Vec2d uv(x(2 * v.idx()), x(2 * v.idx() + 1)); 218 | mesh.set_texcoord2D(v, uv); 219 | } 220 | /*Eigen::MatrixXd B = A_.toDense(); 221 | int n_boundary = mesh.GetBoundaries().front().size(); 222 | Eigen::MatrixXd C(n_boundary * 2, mesh.n_vertices() * 2); 223 | int i = 0; 224 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 225 | VertexHandle v = *viter; 226 | if (mesh.is_boundary(v)) { 227 | C.block(2 * i, 0, 2, mesh.n_vertices() * 2) = B.block(2 * v.idx(), 0, 2, mesh.n_vertices() * 2); 228 | ++i; 229 | } 230 | } 231 | std::cout << C.transpose() << std::endl;*/ 232 | } 233 | -------------------------------------------------------------------------------- /src/OrbifoldEmbedding/EuclideanOrbifoldSolver.h: -------------------------------------------------------------------------------- 1 | #ifndef EUCLIDEAN_ORBIFOLD_H_ 2 | #define EUCLIDEAN_ORBIFOLD_H_ 3 | 4 | #include 5 | #include "OrbifoldInitializer.h" 6 | #include 7 | #include 8 | #include 9 | 10 | #ifndef PI 11 | #define PI 3.141592653 12 | #endif 13 | 14 | 15 | // This class is the implementation of paper: Orbifold Tutte Embeddings. 16 | // The system is harmonic system plus rotation constraints. 17 | // So the problem is linear. 18 | class EuclideanOrbifoldSolver 19 | { 20 | public: 21 | EuclideanOrbifoldSolver(SurfaceMesh &mesh, OpenMesh::VPropHandleT cone_flag, OpenMesh::VPropHandleT cone_angle, OpenMesh::EPropHandleT slice_flag); 22 | SurfaceMesh Compute(); 23 | std::vector ConeVertices() { return cone_vts_; } 24 | protected: 25 | SurfaceMesh &mesh_; 26 | SurfaceMesh sliced_mesh_; 27 | 28 | OpenMesh::VPropHandleT cone_flag_; 29 | OpenMesh::VPropHandleT cone_angle_; 30 | OpenMesh::EPropHandleT slice_flag_; 31 | 32 | std::vector cone_vts_; 33 | std::vector> segments_vts_; 34 | OpenMesh::VPropHandleT vtx_transit_; 35 | OpenMesh::VPropHandleT vtx_rotation_center_; 36 | Eigen::SparseMatrix A_; 37 | Eigen::VectorXd b_; 38 | Eigen::VectorXd X_; 39 | 40 | 41 | protected: 42 | 43 | void InitOrbifold(); 44 | 45 | 46 | double CosineLaw(double a, double b, double c); 47 | void ComputeCornerAngles(); 48 | void ComputeHalfedgeWeights(); 49 | 50 | void ConstructSparseSystem(); 51 | void SolveLinearSystem(); 52 | 53 | 54 | 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/OrbifoldEmbedding/HyperbolicOrbifoldSolver.cpp: -------------------------------------------------------------------------------- 1 | #include "HyperbolicOrbifoldSolver.h" 2 | 3 | 4 | 5 | HyperbolicOrbifoldSolver::HyperbolicOrbifoldSolver(SurfaceMesh &mesh, OpenMesh::VPropHandleT cone_flag, OpenMesh::EPropHandleT slice_flag) 6 | : mesh_(mesh), cone_flag_(cone_flag), slice_flag_(slice_flag) 7 | { 8 | 9 | } 10 | 11 | SurfaceMesh HyperbolicOrbifoldSolver::Compute() 12 | { 13 | using namespace Eigen; 14 | using namespace LBFGSpp; 15 | 16 | if (mesh_.n_vertices() > 10) { 17 | InitOrbifold(); 18 | ComputeCornerAngles(); 19 | ComputeHalfedgeWeights(); 20 | InitMap(); 21 | Normalize(); 22 | this->ComputeGradient(); 23 | std::function fun= 24 | [this](const VectorXd& x, VectorXd& grad) ->double 25 | { 26 | this->SetCoords(x); 27 | this->Normalize(); 28 | this->ComputeGradient(); 29 | grad = this->GetGradientVector(); 30 | return this->ComputeEnergy(); 31 | }; 32 | 33 | LBFGSParam param; 34 | param.epsilon = max_error; 35 | param.max_iterations = 2000; 36 | LBFGSSolver solver(param); 37 | VectorXd x = GetCoordsVector(); 38 | 39 | double fx; 40 | int niter = solver.minimize(fun, x, fx); 41 | 42 | std::cout << niter << " iterations" << std::endl; 43 | std::cout << "f(x) = " << fx << std::endl; 44 | 45 | } 46 | return sliced_mesh_; 47 | } 48 | 49 | void HyperbolicOrbifoldSolver::InitOrbifold() 50 | { 51 | using namespace OpenMesh; 52 | if (!cone_angle_.is_valid()) mesh_.add_property(cone_angle_); 53 | for (auto viter = mesh_.vertices_begin(); viter != mesh_.vertices_end(); ++viter) { 54 | VertexHandle v = *viter; 55 | if (mesh_.property(cone_flag_, v)) { 56 | mesh_.property(cone_angle_, v) = PI; 57 | } 58 | } 59 | OrbifoldInitializer initializer(mesh_); 60 | initializer.Initiate(sliced_mesh_, cone_flag_, cone_angle_, slice_flag_); 61 | initializer.ComputeHyperbolicTransformations(sliced_mesh_, vtx_transit_); 62 | cone_vts_ = initializer.GetConeVertices(); 63 | segments_vts_ = initializer.GetSegments(); 64 | 65 | std::cout << "Cone coordinates:\n"; 66 | for (int i = 0; i < cone_vts_.size(); ++i) { 67 | Vec2d uv = sliced_mesh_.texcoord2D(cone_vts_[i]); 68 | std::cout << uv[0] << "\t" << uv[1] << std::endl; 69 | } 70 | } 71 | 72 | void HyperbolicOrbifoldSolver::InitiateBoundaryData() 73 | { 74 | using namespace OpenMesh; 75 | SurfaceMesh &mesh = sliced_mesh_; 76 | for (int i = 0; i < segments_vts_.size(); ++i) { 77 | auto seg = segments_vts_[i]; 78 | Vec2d segment_start = mesh.texcoord2D(seg.front()); 79 | Vec2d segment_end = mesh.texcoord2D(seg.back()); 80 | Vec2d segment_vector = segment_end - segment_start; 81 | double segment_length = (segment_vector).norm(); 82 | double interval_length = segment_length / (segments_vts_[i].size() + 1); 83 | int j = 1; 84 | for (auto it = seg.begin(); it != seg.end(); ++it) { 85 | VertexHandle v = *it; 86 | if (mesh.data(v).is_singularity()) continue; 87 | mesh.set_texcoord2D(*it, segment_start + segment_vector * j * interval_length / segment_length); 88 | ++j; 89 | } 90 | } 91 | 92 | } 93 | 94 | double HyperbolicOrbifoldSolver::CosineLaw(double a, double b, double c) 95 | { 96 | //double cs = (cosh(a) * cosh(b) - cosh(c)) / (sinh(a) * sinh(b)); 97 | double cs = (a * a + b * b - c * c) / (2 * a * b); 98 | assert(-1 <= cs && cs <= 1); 99 | return acos(cs); 100 | 101 | } 102 | 103 | double HyperbolicOrbifoldSolver::AngleCosineLaw(double a, double b, double c) 104 | { 105 | double l = (cos(c) + cos(a)*cos(b)) / (sin(a) * sin(b)); 106 | return acosh(l); 107 | } 108 | 109 | void HyperbolicOrbifoldSolver::ComputeCornerAngles() 110 | { 111 | using namespace OpenMesh; 112 | SurfaceMesh &mesh = sliced_mesh_; 113 | for (auto fiter = mesh.faces_begin(); fiter != mesh.faces_end(); ++fiter) { 114 | FaceHandle f = *fiter; 115 | std::vector he; 116 | 117 | for (auto fhiter = mesh.fh_iter(f); fhiter.is_valid(); ++fhiter) { 118 | he.push_back(*fhiter); 119 | } 120 | double l[3]; 121 | l[0] = mesh.calc_edge_length(mesh.edge_handle(he[0])); 122 | l[1] = mesh.calc_edge_length(mesh.edge_handle(he[1])); 123 | l[2] = mesh.calc_edge_length(mesh.edge_handle(he[2])); 124 | for (int i = 0; i < 3; ++i) { 125 | double cs = CosineLaw(l[i], l[(i + 1) % 3], l[(i + 2) % 3]); 126 | mesh.data(he[i]).set_angle(cs); 127 | } 128 | } 129 | } 130 | 131 | void HyperbolicOrbifoldSolver::ComputeHalfedgeWeights() 132 | { 133 | using namespace OpenMesh; 134 | SurfaceMesh &mesh = sliced_mesh_; 135 | 136 | ComputeCornerAngles(); 137 | for (auto eiter = mesh.edges_begin(); eiter != mesh.edges_end(); ++eiter) { 138 | EdgeHandle e = *eiter; 139 | HalfedgeHandle h0 = mesh.halfedge_handle(e, 0); 140 | HalfedgeHandle h1 = mesh.halfedge_handle(e, 1); 141 | HalfedgeHandle h0_next; 142 | if (!mesh.is_boundary(h0)) 143 | h0_next = mesh.next_halfedge_handle(h0); 144 | HalfedgeHandle h1_next; 145 | if (!mesh.is_boundary(h1)) 146 | h1_next = mesh.next_halfedge_handle(h1); 147 | 148 | double weight = 0.; 149 | 150 | if (h0_next.is_valid()) 151 | weight += 1. / tan(mesh.data(h0_next).angle()); 152 | if (h1_next.is_valid()) 153 | weight += 1. / tan(mesh.data(h1_next).angle()); 154 | weight *= 0.5; 155 | if (weight < 0) 156 | weight = 0.01; 157 | mesh.data(h0).set_weight(weight); 158 | mesh.data(h1).set_weight(weight); 159 | } 160 | 161 | } 162 | 163 | void HyperbolicOrbifoldSolver::ComputeEdgeLength() 164 | { 165 | using namespace OpenMesh; 166 | SurfaceMesh &mesh = sliced_mesh_; 167 | 168 | for (auto eiter = mesh.edges_begin(); eiter != mesh.edges_end(); ++eiter) { 169 | EdgeHandle e = *eiter; 170 | HalfedgeHandle h = mesh.halfedge_handle(e, 0); 171 | VertexHandle v0 = mesh.from_vertex_handle(h); 172 | VertexHandle v1 = mesh.to_vertex_handle(h); 173 | Vec2d v0_uv = mesh.texcoord2D(v0); 174 | Vec2d v1_uv = mesh.texcoord2D(v1); 175 | Complex v0_complex(v0_uv[0], v0_uv[1]); 176 | Complex v1_complex(v1_uv[0], v1_uv[1]); 177 | mesh.data(e).set_length(HyperbolicDistance(v0_complex, v1_complex)); 178 | } 179 | } 180 | 181 | double HyperbolicOrbifoldSolver::ComputeGradient() 182 | { 183 | using namespace OpenMesh; 184 | SurfaceMesh &mesh = sliced_mesh_; 185 | double max_gradient_norm = 0.0; 186 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 187 | VertexHandle v = *viter; 188 | Vec2d gradient = ComputeGradient(v); 189 | mesh.data(v).set_gradient(gradient); 190 | if (gradient.norm() > max_gradient_norm) { 191 | max_gradient_norm = gradient.norm(); 192 | } 193 | } 194 | return max_gradient_norm; 195 | } 196 | 197 | OpenMesh::Vec2d HyperbolicOrbifoldSolver::ComputeGradientOfDistance2(Complex src_complex, Complex dst_complex) 198 | { 199 | using namespace OpenMesh; 200 | SurfaceMesh &mesh = sliced_mesh_; 201 | 202 | Vec2d src_uv(src_complex.real(), src_complex.imag()); 203 | Vec2d dst_uv(dst_complex.real(), dst_complex.imag()); 204 | Vec2d diff = src_uv - dst_uv; 205 | double distance = HyperbolicDistance(src_complex, dst_complex); 206 | 207 | double f = 1 + 2 * pow(diff.norm(), 2) / ((1 - pow(src_uv.norm(), 2))*(1 - pow(dst_uv.norm(), 2))); 208 | double darccosh = 1 / sqrt(f * f - 1); 209 | double constant_df = 4. / ((1 - pow(dst_uv.norm(), 2))*(1 - pow(src_uv.norm(), 2))); 210 | Vec2d vector_df = diff + pow(diff.norm(), 2) * src_uv / (1 - pow(src_uv.norm(), 2)); 211 | return distance * darccosh * constant_df * vector_df; 212 | } 213 | 214 | OpenMesh::Vec2d HyperbolicOrbifoldSolver::ComputeGradient(OpenMesh::VertexHandle v) 215 | { 216 | using namespace OpenMesh; 217 | SurfaceMesh &mesh = sliced_mesh_; 218 | 219 | if (mesh.data(v).is_singularity()) { 220 | return Vec2d(0, 0); 221 | } 222 | Vec2d v_uv = mesh.texcoord2D(v); 223 | Complex v_complex(v_uv[0], v_uv[1]); 224 | 225 | Vec2d gradient(0, 0); 226 | 227 | for (SurfaceMesh::VertexOHalfedgeIter vohiter = mesh.voh_iter(v); vohiter.is_valid(); ++vohiter) { 228 | HalfedgeHandle h = *vohiter; 229 | VertexHandle neighbor = mesh.to_vertex_handle(h); 230 | auto neighbor_uv = mesh.texcoord2D(neighbor); 231 | Complex neighbor_complex(neighbor_uv[0], neighbor_uv[1]); 232 | double n_w = mesh.data(h).weight(); 233 | assert(n_w > 0); 234 | gradient += n_w * ComputeGradientOfDistance2(v_complex, neighbor_complex); 235 | } 236 | 237 | if (!mesh.is_boundary(v)) return gradient; 238 | 239 | VertexHandle equiv = mesh.data(v).equivalent_vertex(); 240 | 241 | for (SurfaceMesh::VertexOHalfedgeIter vohiter = mesh.voh_iter(equiv); vohiter.is_valid(); ++vohiter) { 242 | HalfedgeHandle h = *vohiter; 243 | VertexHandle neighbor = mesh.to_vertex_handle(h); 244 | auto neighbor_uv = mesh.texcoord2D(neighbor); 245 | Complex neighbor_complex(neighbor_uv[0], neighbor_uv[1]); 246 | std::function transformation = mesh.property(vtx_transit_, equiv); 247 | neighbor_complex = transformation(neighbor_complex); 248 | double n_w = mesh.data(h).weight(); 249 | gradient += n_w * ComputeGradientOfDistance2(v_complex, neighbor_complex); 250 | } 251 | double metric_factor = pow(1 - pow(v_uv.norm(), 2), 2) / 4.; 252 | return gradient * metric_factor; 253 | } 254 | 255 | double HyperbolicOrbifoldSolver::ComputeEnergy() 256 | { 257 | using namespace OpenMesh; 258 | SurfaceMesh &mesh = sliced_mesh_; 259 | double energy = 0; 260 | for (auto hiter = mesh.halfedges_begin(); hiter != mesh.halfedges_end(); ++hiter) { 261 | HalfedgeHandle h = *hiter; 262 | VertexHandle v = mesh.from_vertex_handle(h); 263 | VertexHandle tv = mesh.to_vertex_handle(h); 264 | Vec2d v_uv = mesh.texcoord2D(v); 265 | Vec2d tv_uv = mesh.texcoord2D(tv); 266 | Complex v_complex(v_uv[0], v_uv[1]); 267 | Complex tv_complex(tv_uv[0], tv_uv[1]); 268 | double weight = mesh.data(h).weight(); 269 | energy += weight * pow(HyperbolicDistance(v_complex, tv_complex), 2); 270 | } 271 | return energy * 0.5; 272 | } 273 | 274 | Eigen::VectorXd HyperbolicOrbifoldSolver::GetCoordsVector() 275 | { 276 | using namespace OpenMesh; 277 | SurfaceMesh &mesh = sliced_mesh_; 278 | Eigen::VectorXd uv_vector(mesh.n_vertices() * 2); 279 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 280 | VertexHandle v = *viter; 281 | Vec2d uv = mesh.texcoord2D(v); 282 | uv_vector(v.idx() * 2) = uv[0]; 283 | uv_vector(v.idx() * 2 + 1) = uv[1]; 284 | } 285 | return uv_vector; 286 | } 287 | 288 | Eigen::VectorXd HyperbolicOrbifoldSolver::GetGradientVector() 289 | { 290 | using namespace OpenMesh; 291 | SurfaceMesh &mesh = sliced_mesh_; 292 | Eigen::VectorXd gradient_vector(mesh.n_vertices() * 2); 293 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 294 | VertexHandle v = *viter; 295 | Vec2d grad = mesh.data(v).gradient(); 296 | gradient_vector(v.idx() * 2) = grad[0]; 297 | gradient_vector(v.idx() * 2 + 1) = grad[1]; 298 | } 299 | return gradient_vector; 300 | } 301 | 302 | void HyperbolicOrbifoldSolver::SetCoords(const Eigen::VectorXd & uv_vector) 303 | { 304 | using namespace OpenMesh; 305 | SurfaceMesh &mesh = sliced_mesh_; 306 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 307 | VertexHandle v = *viter; 308 | Vec2d uv(uv_vector(v.idx() * 2), uv_vector(v.idx() * 2 + 1)); 309 | mesh.set_texcoord2D(v, uv); 310 | } 311 | } 312 | 313 | double HyperbolicOrbifoldSolver::OptimizationLoop(double step_length, double error) 314 | { 315 | using namespace OpenMesh; 316 | SurfaceMesh &mesh = sliced_mesh_; 317 | //double energy_prev = 1e10; 318 | //double energy = ComputeEnergy(); 319 | int epoch = 0; 320 | double gradient_length = 1e10; 321 | while (gradient_length > error) { 322 | gradient_length = ComputeGradient(); 323 | if (epoch % 20) { 324 | std::cout << "Max Gradient Length:" << gradient_length << "\tEnergy:" << ComputeEnergy() << std::endl; 325 | } 326 | 327 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 328 | VertexHandle v = *viter; 329 | Vec2d uv = mesh.texcoord2D(v); 330 | Vec2d gradient = mesh.data(v).gradient(); 331 | 332 | Complex uv_complex(uv[0], uv[1]); 333 | uv -= step_length * gradient; 334 | assert(uv.norm() < 1); 335 | mesh.set_texcoord2D(v, uv); 336 | } 337 | 338 | Normalize(); 339 | //energy_prev = energy; 340 | //energy = ComputeEnergy(); 341 | epoch++; 342 | } 343 | return gradient_length; 344 | } 345 | 346 | void HyperbolicOrbifoldSolver::Normalize() 347 | { 348 | using namespace OpenMesh; 349 | SurfaceMesh &mesh = sliced_mesh_; 350 | for (auto it = segments_vts_.begin(); it != segments_vts_.end(); ++it) { 351 | for (auto viter = (*it).begin(); viter != (*it).end(); ++viter) { 352 | VertexHandle v = *viter; 353 | if (mesh.data(v).is_singularity()) continue; 354 | VertexHandle equiv = mesh.data(v).equivalent_vertex(); 355 | Vec2d equiv_uv = mesh.texcoord2D(equiv); 356 | Complex equiv_complex(equiv_uv[0], equiv_uv[1]); 357 | Complex v_complex = mesh.property(vtx_transit_, equiv)(equiv_complex); 358 | Vec2d v_uv(v_complex.real(), v_complex.imag()); 359 | mesh.set_texcoord2D(v, v_uv); 360 | } 361 | } 362 | } 363 | 364 | // Compute a harmonic map as a initial map 365 | void HyperbolicOrbifoldSolver::InitMap() 366 | { 367 | using namespace OpenMesh; 368 | SurfaceMesh &mesh = sliced_mesh_; 369 | using namespace Eigen; 370 | 371 | InitiateBoundaryData(); 372 | 373 | SparseMatrix A(mesh.n_vertices() * 2, mesh.n_vertices() * 2); 374 | A.setZero(); 375 | VectorXd b(mesh.n_vertices() * 2); 376 | 377 | std::vector> A_coefficients; 378 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 379 | VertexHandle v = *viter; 380 | int idx = v.idx(); 381 | if (mesh.is_boundary(v)) { 382 | A_coefficients.push_back(Triplet(2 * idx, 2 * idx, 1.)); 383 | A_coefficients.push_back(Triplet(2 * idx + 1, 2 * idx + 1, 1.)); 384 | auto uv = mesh.texcoord2D(v); 385 | b(2 * idx) = uv[0]; 386 | b(2 * idx + 1) = uv[1]; 387 | } 388 | else { 389 | b(2 * idx) = 0; 390 | b(2 * idx + 1) = 0; 391 | double s_w = 0; 392 | for (SurfaceMesh::VertexOHalfedgeIter vohiter = mesh.voh_iter(v); vohiter.is_valid(); ++vohiter) { 393 | HalfedgeHandle h = *vohiter; 394 | VertexHandle neighbor = mesh.to_vertex_handle(h); 395 | double n_w = mesh.data(h).weight(); 396 | s_w += n_w; 397 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx(), 2 * neighbor.idx(), -n_w)); 398 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx() + 1, 2 * neighbor.idx() + 1, -n_w)); 399 | } 400 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx(), 2 * v.idx(), s_w)); 401 | A_coefficients.push_back(Eigen::Triplet(2 * v.idx() + 1, 2 * v.idx() + 1, s_w)); 402 | } 403 | } 404 | A.setFromTriplets(A_coefficients.begin(), A_coefficients.end()); 405 | 406 | Eigen::SparseLU> solver; 407 | solver.compute(A); 408 | if (solver.info() != Eigen::Success) 409 | { 410 | std::cerr << "Waring: Eigen decomposition failed" << std::endl; 411 | } 412 | Eigen::VectorXd x = solver.solve(b); 413 | //std::cout << "Error:" << (A * x - b).norm() << std::endl; 414 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 415 | VertexHandle v = *viter; 416 | if (mesh.is_boundary(v)) continue; 417 | Vec2d uv(x(2 * v.idx()), x(2 * v.idx() + 1)); 418 | mesh.set_texcoord2D(v, uv); 419 | } 420 | } 421 | 422 | 423 | -------------------------------------------------------------------------------- /src/OrbifoldEmbedding/HyperbolicOrbifoldSolver.h: -------------------------------------------------------------------------------- 1 | #ifndef HYPERBOLIC_ORBIFOLD_SOLVER_H_ 2 | #define HYPERBOLIC_ORBIFOLD_SOLVER_H_ 3 | 4 | #include 5 | #include "OrbifoldInitializer.h" 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | 13 | #ifndef PI 14 | #define PI 3.141592653 15 | #endif 16 | 17 | 18 | // This is the implementation of paper Hyperbolic Orbifold Embeddings. 19 | // The problem is non linear. 20 | class HyperbolicOrbifoldSolver 21 | { 22 | public: 23 | HyperbolicOrbifoldSolver(SurfaceMesh &mesh, OpenMesh::VPropHandleT cone_flag, OpenMesh::EPropHandleT slice_flag); 24 | SurfaceMesh Compute(); 25 | std::vector ConeVertices() { return cone_vts_; } 26 | 27 | protected: 28 | SurfaceMesh &mesh_; 29 | SurfaceMesh sliced_mesh_; 30 | OpenMesh::VPropHandleT cone_flag_; 31 | OpenMesh::VPropHandleT cone_angle_; 32 | OpenMesh::EPropHandleT slice_flag_; 33 | std::vector cone_vts_; 34 | std::vector> segments_vts_; 35 | OpenMesh::VPropHandleT> vtx_transit_; 36 | 37 | int n_cones_; 38 | 39 | double max_error = 1e-4; 40 | 41 | protected: 42 | 43 | void InitOrbifold(); 44 | 45 | double CosineLaw(double a, double b, double c); 46 | double AngleCosineLaw(double a, double b, double c); 47 | void ComputeCornerAngles(); 48 | void ComputeHalfedgeWeights(); 49 | 50 | void InitiateBoundaryData(); 51 | void InitMap(); 52 | 53 | void ComputeEdgeLength(); 54 | double ComputeGradient(); 55 | OpenMesh::Vec2d ComputeGradientOfDistance2(Complex src, Complex dst); 56 | OpenMesh::Vec2d ComputeGradient(OpenMesh::VertexHandle v); 57 | 58 | double ComputeEnergy(); 59 | Eigen::VectorXd GetCoordsVector(); 60 | Eigen::VectorXd GetGradientVector(); 61 | void SetCoords(const Eigen::VectorXd &uv_vector); 62 | double OptimizationLoop(double step_length, double error); 63 | 64 | // Normalize boundary such that it satisfies orbifold requirement. 65 | void Normalize(); 66 | }; 67 | 68 | #endif // !HYPERBOLIC_ORBIFOLD_SOLVER_H_ 69 | -------------------------------------------------------------------------------- /src/OrbifoldEmbedding/OrbifoldInitializer.cpp: -------------------------------------------------------------------------------- 1 | #include "OrbifoldInitializer.h" 2 | #include 3 | 4 | OrbifoldInitializer::OrbifoldInitializer(SurfaceMesh & mesh):mesh_(mesh) 5 | { 6 | 7 | } 8 | 9 | void OrbifoldInitializer::Initiate(SurfaceMesh &sliced_mesh, OpenMesh::VPropHandleT cone_flag, OpenMesh::VPropHandleT cone_angle, OpenMesh::EPropHandleT slice_flag) 10 | { 11 | using namespace OpenMesh; 12 | SurfaceMesh &mesh = mesh_; 13 | cone_flag_ = cone_flag; 14 | cone_angle_ = cone_angle; 15 | slice_flag_ = slice_flag; 16 | CutMesh(sliced_mesh); 17 | CutBoundaryToSegments(sliced_mesh); 18 | 19 | } 20 | 21 | void OrbifoldInitializer::ComputeEuclideanTransformations(SurfaceMesh & sliced_mesh, OpenMesh::VPropHandleT& vtx_transit) 22 | { 23 | if (!vtx_transit.is_valid()) 24 | sliced_mesh.add_property(vtx_transit); 25 | using namespace OpenMesh; 26 | InitiateEConeCoords(sliced_mesh); 27 | 28 | for (auto it = segments_vts_.begin(); it != segments_vts_.end(); ++it) { 29 | auto seg = *it; 30 | VertexHandle vs0 = seg.front(); 31 | VertexHandle vs1 = seg.back(); 32 | VertexHandle vt0 = sliced_mesh.data(vs0).equivalent_vertex(); 33 | VertexHandle vt1 = sliced_mesh.data(vs1).equivalent_vertex(); 34 | if (!vt0.is_valid()) 35 | vt0 = vs0; 36 | if (!vt1.is_valid()) 37 | vt1 = vs1; 38 | Complex s0(sliced_mesh.texcoord2D(vs0)[0], sliced_mesh.texcoord2D(vs0)[1]); 39 | Complex s1(sliced_mesh.texcoord2D(vs1)[0], sliced_mesh.texcoord2D(vs1)[1]); 40 | Complex t0(sliced_mesh.texcoord2D(vt0)[0], sliced_mesh.texcoord2D(vt0)[1]); 41 | Complex t1(sliced_mesh.texcoord2D(vt1)[0], sliced_mesh.texcoord2D(vt1)[1]); 42 | Eigen::MatrixXd T = ComputeHomogeousRigidTransformation(s0, s1, t0, t1); 43 | std::cout << T << std::endl << std::endl; 44 | for (auto viter = seg.begin(); viter != seg.end(); ++viter) { 45 | VertexHandle v = *viter; 46 | if (sliced_mesh.data(v).is_singularity()) continue; 47 | sliced_mesh.property(vtx_transit, v) = T; 48 | } 49 | } 50 | 51 | } 52 | 53 | void OrbifoldInitializer::ComputeHyperbolicTransformations(SurfaceMesh & sliced_mesh, OpenMesh::VPropHandleT>& vtx_transit) 54 | { 55 | using namespace OpenMesh; 56 | SurfaceMesh &mesh = sliced_mesh; 57 | if (!vtx_transit.is_valid()) 58 | sliced_mesh.add_property(vtx_transit); 59 | 60 | InitiateHConeCoords(sliced_mesh); 61 | 62 | for (auto it = segments_vts_.begin(); it != segments_vts_.end(); ++it) { 63 | auto seg = *it; 64 | VertexHandle vs0 = seg.front(); 65 | VertexHandle vs1 = seg.back(); 66 | VertexHandle vt0 = sliced_mesh.data(vs0).equivalent_vertex(); 67 | VertexHandle vt1 = sliced_mesh.data(vs1).equivalent_vertex(); 68 | if (!vt0.is_valid()) 69 | vt0 = vs0; 70 | if (!vt1.is_valid()) 71 | vt1 = vs1; 72 | Complex s0(sliced_mesh.texcoord2D(vs0)[0], sliced_mesh.texcoord2D(vs0)[1]); 73 | Complex s1(sliced_mesh.texcoord2D(vs1)[0], sliced_mesh.texcoord2D(vs1)[1]); 74 | Complex t0(sliced_mesh.texcoord2D(vt0)[0], sliced_mesh.texcoord2D(vt0)[1]); 75 | Complex t1(sliced_mesh.texcoord2D(vt1)[0], sliced_mesh.texcoord2D(vt1)[1]); 76 | 77 | 78 | auto transformation = ComputeMobiusTransformation(s0, s1, t0, t1); 79 | assert(abs(transformation(s1) - t1) < 1e-6); 80 | 81 | for (auto vit = seg.begin(); vit != seg.end(); ++vit) { 82 | if(!mesh.data(*vit).is_singularity()) 83 | mesh.property(vtx_transit, *vit) = transformation; 84 | } 85 | } 86 | } 87 | 88 | void OrbifoldInitializer::InitiateEConeCoords(SurfaceMesh & sliced_mesh) 89 | { 90 | if (abs(sliced_mesh.data(cone_vertices_.front()).angle_sum() - 0.5 * PI) < 1e-5 && 91 | cone_vertices_.size() == 4) { 92 | InitiateEConeCoordsType1(sliced_mesh); 93 | } 94 | 95 | if (abs(sliced_mesh.data(cone_vertices_.front()).angle_sum() - 2 * PI / 3.) < 1e-5 && 96 | cone_vertices_.size() == 4) { 97 | InitiateEConeCoordsType2(sliced_mesh); 98 | } 99 | 100 | if (abs(sliced_mesh.data(cone_vertices_.front()).angle_sum() - PI) < 1e-5 && 101 | cone_vertices_.size() == 4) { 102 | InitiateEConeCoordsType3(sliced_mesh, true); 103 | } 104 | 105 | if (abs(sliced_mesh.data(cone_vertices_.front()).angle_sum() - PI/3.) < 1e-5 && 106 | cone_vertices_.size() == 4) { 107 | InitiateEConeCoordsType3(sliced_mesh, false); 108 | } 109 | 110 | } 111 | 112 | void OrbifoldInitializer::InitiateHConeCoords(SurfaceMesh & sliced_mesh) 113 | { 114 | using namespace OpenMesh; 115 | SurfaceMesh &mesh = sliced_mesh; 116 | int n_cones = (cone_vertices_.size()+2) / 2; 117 | 118 | double radius = HyperbolicCosineLaw(2 * PI / n_cones, PI / 4., PI / 4.); 119 | double ratio = (exp(radius) - 1) / (exp(radius) + 1); 120 | 121 | Vec2d p1(cos(PI / 2 + (n_cones / 2) * 2 * PI / n_cones)*ratio, sin(PI / 2 + (n_cones / 2) * 2 * PI / n_cones)*ratio); 122 | Vec2d pk(cos(PI / 2 + (1 + n_cones / 2) * 2 * PI / n_cones)*ratio, sin(PI / 2 + (1 + n_cones / 2) * 2 * PI / n_cones)*ratio); 123 | double edge_length = HyperbolicCosineLaw(PI / 4, PI / 4, 2 * PI / n_cones); 124 | double target_radis = (exp(edge_length / 2) - 1) / (exp(edge_length / 2) + 1); 125 | Complex p1_source(p1[0], p1[1]); 126 | Complex pk_source(pk[0], pk[1]); 127 | Complex p1_target(-target_radis, 0); 128 | Complex pk_target(target_radis, 0); 129 | 130 | auto transformation = ComputeMobiusTransformation(p1_source, pk_source, p1_target, pk_target); 131 | for (int i = 0; i < n_cones; ++i) { 132 | Vec2d uv = Vec2d(cos(PI / 2 + (-i + n_cones / 2) * 2 * PI / n_cones)*ratio, sin(PI / 2 + (-i + n_cones / 2) * 2 * PI / n_cones)*ratio); 133 | Complex uv_complex = transformation(Complex(uv[0], uv[1])); 134 | uv = Vec2d(uv_complex.real(), uv_complex.imag()); 135 | mesh.set_texcoord2D(cone_vertices_[i], uv); 136 | uv_complex = std::conj(uv_complex); 137 | uv = Vec2d(uv_complex.real(), uv_complex.imag()); 138 | if (i != 0 && i != n_cones - 1) 139 | mesh.set_texcoord2D(mesh.data(cone_vertices_[i]).equivalent_vertex(), uv); 140 | } 141 | std::cout << "Cone coordinates:\n"; 142 | for (int i = 0; i < cone_vertices_.size(); ++i) { 143 | Vec2d uv = mesh.texcoord2D(cone_vertices_[i]); 144 | std::cout << uv[0] << "\t" << uv[1] << std::endl; 145 | } 146 | } 147 | 148 | double OrbifoldInitializer::HyperbolicCosineLaw(double a, double b, double c) 149 | { 150 | double l = (cos(c) + cos(a)*cos(b)) / (sin(a) * sin(b)); 151 | return acosh(l); 152 | } 153 | 154 | 155 | void OrbifoldInitializer::CutMesh(SurfaceMesh & sliced_mesh) 156 | { 157 | using namespace OpenMesh; 158 | SurfaceMesh &mesh = mesh_; 159 | 160 | MeshSlicer slicer(mesh); 161 | slicer.ResetFlags(); 162 | for (auto eiter = mesh.edges_begin(); eiter != mesh.edges_end(); ++eiter) { 163 | EdgeHandle e = *eiter; 164 | if (mesh.property(slice_flag_, e)) { 165 | slicer.AddOnCutEdge(e); 166 | } 167 | } 168 | 169 | slicer.ConstructWedge(); 170 | slicer.SliceAccordingToWedge(sliced_mesh); 171 | 172 | 173 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 174 | VertexHandle v = *viter; 175 | auto verts = slicer.SplitTo(v); 176 | if (verts.size() == 2) { 177 | sliced_mesh.data(verts[0]).set_equivalent_vertex(verts[1]); 178 | sliced_mesh.data(verts[1]).set_equivalent_vertex(verts[0]); 179 | } 180 | if (mesh.property(cone_flag_, v)) { 181 | for (auto it = verts.begin(); it != verts.end(); ++it) { 182 | sliced_mesh.data(*it).set_singularity(true); 183 | sliced_mesh.data(*it).set_angle_sum(mesh.property(cone_angle_, v) / verts.size()); 184 | } 185 | } 186 | } 187 | 188 | } 189 | 190 | void OrbifoldInitializer::CutBoundaryToSegments(SurfaceMesh &sliced_mesh) 191 | { 192 | using namespace OpenMesh; 193 | SurfaceMesh &mesh = sliced_mesh; 194 | 195 | mesh.RequestBoundary(); 196 | auto boundary = mesh.GetBoundaries().front(); 197 | 198 | std::list boundary_list(boundary.begin(), boundary.end()); 199 | for (auto it = boundary_list.begin(); it != boundary_list.end(); ++it) { 200 | HalfedgeHandle h = *it; 201 | if (mesh.data(mesh.from_vertex_handle(h)).is_singularity() && !mesh.data(mesh.from_vertex_handle(h)).equivalent_vertex().is_valid()) { 202 | boundary_list.insert(boundary_list.end(), boundary_list.begin(), it); 203 | boundary_list.erase(boundary_list.begin(), it); 204 | break; 205 | } 206 | } 207 | 208 | segments_vts_.clear(); 209 | std::vector segment; 210 | for (auto it = boundary_list.begin(); it != boundary_list.end(); ++it) { 211 | HalfedgeHandle h = *it; 212 | segment.push_back(mesh.from_vertex_handle(h)); 213 | if (mesh.data(mesh.from_vertex_handle(h)).is_singularity()) { 214 | cone_vertices_.push_back(mesh.from_vertex_handle(h)); 215 | } 216 | if (mesh.data(mesh.to_vertex_handle(h)).is_singularity()) { 217 | segment.push_back(mesh.to_vertex_handle(h)); 218 | segments_vts_.push_back(segment); 219 | segment.clear(); 220 | } 221 | } 222 | } 223 | 224 | void OrbifoldInitializer::InitiateEConeCoordsType1(SurfaceMesh & sliced_mesh) 225 | { 226 | using namespace OpenMesh; 227 | OpenMesh::Vec2d v0(0, 0); 228 | OpenMesh::Vec2d v1(0, 1); 229 | OpenMesh::Vec2d v2(1, 1); 230 | OpenMesh::Vec2d v3(1, 0); 231 | sliced_mesh.set_texcoord2D(cone_vertices_[0], v0); 232 | sliced_mesh.set_texcoord2D(cone_vertices_[1], v1); 233 | sliced_mesh.set_texcoord2D(cone_vertices_[2], v2); 234 | sliced_mesh.set_texcoord2D(cone_vertices_[3], v3); 235 | } 236 | 237 | void OrbifoldInitializer::InitiateEConeCoordsType2(SurfaceMesh & sliced_mesh) 238 | { 239 | using namespace OpenMesh; 240 | OpenMesh::Vec2d v0(0, 0); 241 | OpenMesh::Vec2d v1(-sqrt(3)/2., 0.5); 242 | OpenMesh::Vec2d v2(0, 1); 243 | OpenMesh::Vec2d v3(sqrt(3)/2., 0.5); 244 | sliced_mesh.set_texcoord2D(cone_vertices_[0], v0); 245 | sliced_mesh.set_texcoord2D(cone_vertices_[1], v1); 246 | sliced_mesh.set_texcoord2D(cone_vertices_[2], v2); 247 | sliced_mesh.set_texcoord2D(cone_vertices_[3], v3); 248 | } 249 | 250 | void OrbifoldInitializer::InitiateEConeCoordsType3(SurfaceMesh & sliced_mesh, bool up) 251 | { 252 | using namespace OpenMesh; 253 | OpenMesh::Vec2d v0(0, 0); 254 | OpenMesh::Vec2d v1(-0.5, 0); 255 | OpenMesh::Vec2d v2(0, sqrt(3)/2.); 256 | OpenMesh::Vec2d v3(0.5, 0); 257 | if (!up) { 258 | OpenMesh::Vec2d v0(0, sqrt(3) / 2.); 259 | OpenMesh::Vec2d v1(0.5, 0); 260 | OpenMesh::Vec2d v2(0, 0); 261 | OpenMesh::Vec2d v3(-0.5, 0); 262 | } 263 | sliced_mesh.set_texcoord2D(cone_vertices_[0], v0); 264 | sliced_mesh.set_texcoord2D(cone_vertices_[1], v1); 265 | sliced_mesh.set_texcoord2D(cone_vertices_[2], v2); 266 | sliced_mesh.set_texcoord2D(cone_vertices_[3], v3); 267 | } 268 | 269 | 270 | -------------------------------------------------------------------------------- /src/OrbifoldEmbedding/OrbifoldInitializer.h: -------------------------------------------------------------------------------- 1 | #ifndef ORBIFOLD_INITIALIZER_H 2 | #define ORBIFOLD_INITIALIZER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifndef PI 12 | #define PI 3.141592654 13 | #endif // !PI 14 | 15 | 16 | typedef std::complex Complex; 17 | 18 | 19 | // This class is a warper of MeshSlicer to serve for Orbifold algorithms. 20 | // The first step is to cut the mesh to disk. 21 | // And then it will set singularity flags and target curvatures according to user's input. 22 | // The process receives three properties: 23 | // cone_flag: whether a vertex is a cone point , 24 | // cone_angle: the target sum angles around cones, 25 | // slice_flag: whether a edge is on slice. 26 | class OrbifoldInitializer { 27 | 28 | public: 29 | OrbifoldInitializer(SurfaceMesh &mesh); 30 | void Initiate(SurfaceMesh &sliced_mesh, OpenMesh::VPropHandleT cone_flag, OpenMesh::VPropHandleT cone_angle, OpenMesh::EPropHandleT slice_flag); 31 | 32 | // Compute Isometries needed for orbifold requirements. 33 | // Euclidean isometries are stored using homogenous matrx. 34 | // Hyperbolic isometries are stored using functions. 35 | void ComputeEuclideanTransformations(SurfaceMesh &sliced_mesh, OpenMesh::VPropHandleT &vtx_transit); 36 | void ComputeHyperbolicTransformations(SurfaceMesh &sliced_mesh, OpenMesh::VPropHandleT> &vtx_transit); 37 | 38 | std::vector GetConeVertices() { return cone_vertices_; } 39 | 40 | std::vector> GetSegments() { return segments_vts_; } 41 | 42 | protected: 43 | SurfaceMesh &mesh_; 44 | std::vector cone_vertices_; 45 | std::vector> segments_vts_; 46 | 47 | OpenMesh::VPropHandleT cone_flag_; 48 | OpenMesh::VPropHandleT cone_angle_; 49 | OpenMesh::EPropHandleT slice_flag_; 50 | 51 | protected: 52 | // Cut the mesh into a disk. 53 | void CutMesh(SurfaceMesh &sliced_mesh); 54 | 55 | // Cut the boundary into segments according to cones. 56 | void CutBoundaryToSegments(SurfaceMesh &sliced_mesh); 57 | 58 | // Initiate the coordinates of cones. 59 | void InitiateEConeCoords(SurfaceMesh &sliced_mesh); 60 | void InitiateEConeCoordsType1(SurfaceMesh &sliced_mesh); 61 | void InitiateEConeCoordsType2(SurfaceMesh &sliced_mesh); 62 | void InitiateEConeCoordsType3(SurfaceMesh &sliced_mesh, bool up ); 63 | void InitiateHConeCoords(SurfaceMesh &sliced_mesh); 64 | 65 | double HyperbolicCosineLaw(double a, double b, double c); 66 | 67 | }; 68 | #endif // !ORBIFOLD_MESH_SLICER_H 69 | 70 | -------------------------------------------------------------------------------- /src/Utilities/Circle.cpp: -------------------------------------------------------------------------------- 1 | #include "Circle.h" 2 | 3 | Circle::Circle(std::complex center, double radius):center_(center), radius_(radius) 4 | { 5 | 6 | } 7 | 8 | std::complex Circle::Tangent(std::complex point) 9 | { 10 | std::complex diff = point - center_; 11 | diff /= abs(diff); 12 | return std::complex(-diff.imag(), diff.real()); 13 | } 14 | 15 | double Circle::PolarAngle(std::complex point) 16 | { 17 | std::complex diff = point - center_; 18 | diff /= abs(diff); 19 | double t = diff.imag() / diff.real(); 20 | double angle = atan(t); 21 | if (diff.imag() < 0) { 22 | angle += 3.141592653; 23 | } 24 | return angle; 25 | } 26 | 27 | Circle CircleFromPoints(std::vector> points) 28 | { 29 | assert(points.size() >= 3); 30 | std::complex p1 = points[0]; 31 | std::complex p2 = points[1]; 32 | std::complex p3 = points[2]; 33 | 34 | double offset = p2.real() * p2.real() + p2.imag() * p2.imag(); 35 | double bc = (p1.real() * p1.real() + p1.imag() * p1.imag() - offset) / 2.0; 36 | double cd = (offset - p3.real() * p3.real() - p3.imag() * p3.imag()) / 2.0; 37 | double det = (p1.real() - p2.real()) * (p2.imag() - p3.imag()) - (p2.real() - p3.real())* (p1.imag() - p2.imag()); 38 | 39 | //assert(abs(det) > 1e-7); 40 | 41 | double idet = 1 / det; 42 | 43 | double centerx = (bc * (p2.imag() - p3.imag()) - cd * (p1.imag() - p2.imag())) * idet; 44 | double centery = (cd * (p1.real() - p2.real()) - bc * (p2.real() - p3.real())) * idet; 45 | std::complex center(centerx, centery); 46 | double radius = abs(p2 - center); 47 | 48 | return Circle(center, radius); 49 | } 50 | 51 | std::complex ReflectByCircle(std::complex point, Circle circle) 52 | { 53 | double dist_original = abs(point - circle.center()); 54 | double dist_target = circle.radius() * circle.radius() / dist_original; 55 | return circle.center() + (point - circle.center()) * dist_target / dist_original; 56 | } 57 | -------------------------------------------------------------------------------- /src/Utilities/Circle.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCLE_2D_H_ 2 | #define CIRCLE_2D_H_ 3 | #endif // !CIRCLE_2D_H_ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class Circle { 12 | public: 13 | Circle(std::complex center, double radius); 14 | std::complex center() { return center_; } 15 | double radius() { return radius_; } 16 | void set_center(std::complex center) { center_ = center; } 17 | void set_radius(double radius) { radius_ = radius; } 18 | std::complex Tangent(std::complex point); 19 | double PolarAngle(std::complex point); 20 | protected: 21 | std::complex center_; 22 | double radius_; 23 | }; 24 | 25 | Circle CircleFromPoints(std::vector> points); 26 | 27 | std::complex ReflectByCircle(std::complex point, Circle circle); -------------------------------------------------------------------------------- /src/Utilities/Dijkstra.cpp: -------------------------------------------------------------------------------- 1 | #include "Dijkstra.h" 2 | 3 | 4 | 5 | void DijkstraShortestDist(SurfaceMesh & mesh, OpenMesh::VertexHandle src, OpenMesh::VPropHandleT& dist, OpenMesh::VPropHandleT& parent) 6 | { 7 | // check whether dist property is registered; 8 | if (!(dist.is_valid())) { 9 | mesh.add_property(dist); 10 | } 11 | 12 | if (!(parent.is_valid())) { 13 | mesh.add_property(parent); 14 | } 15 | 16 | 17 | OpenMesh::VPropHandleT touched; 18 | mesh.add_property(touched); 19 | 20 | 21 | printf("Calculate distence from vertex %d\r\n", src.idx()); 22 | std::list S; 23 | std::list U; 24 | 25 | // initiate dist of all vertices as INF; 26 | for (SurfaceMesh::VertexIter viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 27 | OpenMesh::VertexHandle v = *viter; 28 | mesh.property(dist, v) = INF; 29 | mesh.property(touched, v) = false; 30 | } 31 | 32 | // set dist of root as 0 33 | mesh.property(dist, src) = 0; 34 | S.push_back(src); 35 | mesh.property(touched, src) = true; 36 | 37 | // update the neighbors of the root 38 | for (SurfaceMesh::VertexVertexIter vviter = mesh.vv_iter(src); vviter.is_valid(); ++vviter) { 39 | OpenMesh::VertexHandle v = *vviter; 40 | mesh.property(dist, v) = mesh.calc_edge_length(mesh.edge_handle(mesh.find_halfedge(src, v))); 41 | mesh.property(parent, v) = src; 42 | U.push_back(v); 43 | } 44 | 45 | while (U.size() > 0) 46 | { 47 | OpenMesh::VertexHandle v_to_update; 48 | double shortest = 2 * INF; 49 | std::list::iterator iv; 50 | for (std::list::iterator it = U.begin(); it != U.end(); ++it) 51 | { 52 | OpenMesh::VertexHandle v = *it; 53 | if (mesh.property(dist, v) < shortest) { 54 | shortest = mesh.property(dist, v); 55 | v_to_update = v; 56 | iv = it; 57 | } 58 | } 59 | S.push_back(v_to_update); 60 | U.erase(iv); 61 | mesh.property(touched, v_to_update) = true; 62 | for (SurfaceMesh::VertexVertexIter vviter = mesh.vv_iter(v_to_update); vviter.is_valid(); ++vviter) { 63 | OpenMesh::VertexHandle v = *vviter; 64 | if (!mesh.property(touched, v)) { 65 | if (mesh.property(dist, v_to_update) + mesh.calc_edge_length(mesh.edge_handle(mesh.find_halfedge(v_to_update, v))) < mesh.property(dist, v)) { 66 | mesh.property(parent, v) = v_to_update; 67 | mesh.property(dist, v) = mesh.property(dist, v_to_update) + mesh.calc_edge_length(mesh.edge_handle(mesh.find_halfedge(v_to_update, v))); 68 | U.push_back(v); 69 | } 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Utilities/Dijkstra.h: -------------------------------------------------------------------------------- 1 | #ifndef DIJKSTRA 2 | #define DIJKSTRA 3 | 4 | #include 5 | #include 6 | 7 | #ifndef INF 8 | #define INF 0x3f3f3f3f 9 | #endif 10 | 11 | // This function is modified from my previous research codes. 12 | void DijkstraShortestDist( 13 | SurfaceMesh &mesh, 14 | OpenMesh::VertexHandle src, // root node 15 | OpenMesh::VPropHandleT &dist, // property to store distance from the root 16 | OpenMesh::VPropHandleT &parent //property to store parent 17 | ); 18 | 19 | 20 | #endif -------------------------------------------------------------------------------- /src/Utilities/EuclideanCoveringSpace.cpp: -------------------------------------------------------------------------------- 1 | #include "EuclideanCoveringSpace.h" 2 | 3 | EuclideanCoveringSpaceComputer::EuclideanCoveringSpaceComputer(SurfaceMesh & mesh, std::vector cones) 4 | :mesh_(mesh), cone_vts_(cones) 5 | { 6 | 7 | } 8 | 9 | void EuclideanCoveringSpaceComputer::Compute() 10 | { 11 | Init(); 12 | 13 | while (Update()) { 14 | while (!min_heap_.top()->valid) { 15 | auto it = min_heap_.top(); 16 | min_heap_.pop(); 17 | boundary_segs_.erase(it); 18 | } 19 | //std::cout << "Min Dist:" << (min_heap_.top())->dist() << std::endl; 20 | } 21 | } 22 | 23 | void EuclideanCoveringSpaceComputer::GenerateMeshMatrix(Eigen::MatrixXd & V, Eigen::MatrixXd & NV, Eigen::MatrixXi & F, Eigen::MatrixXd & NF) 24 | { 25 | OpenMeshToMatrix(mesh_, V, NV, F, NF); 26 | 27 | int n_copies = orbits_[0].size(); 28 | 29 | Eigen::MatrixXd new_V(V.rows()* n_copies, 3); 30 | Eigen::MatrixXi new_F(F.rows() * n_copies, 3); 31 | Eigen::MatrixXd new_NV(NV.rows() *n_copies, 3); 32 | Eigen::MatrixXd new_NF(NF.rows() * n_copies, 3); 33 | new_V.setZero(); 34 | for (int i = 0; i < n_copies; ++i) { 35 | new_NV.block(i * V.rows(), 0, V.rows(), 3) = NV; 36 | new_NF.block(i * F.rows(), 0, F.rows(), 3) = NF; 37 | for(auto viter = mesh_.vertices_begin(); viter != mesh_.vertices_end(); ++viter) 38 | { 39 | OpenMesh::VertexHandle v = *viter; 40 | for (int k = 0; k < 2; ++k) 41 | new_V(i * V.rows() + v.idx(), k) = orbits_[v.idx()][i][k]; 42 | } 43 | new_F.block(i * F.rows(), 0, F.rows(), 3) = F + Eigen::MatrixXi::Constant(F.rows(), 3, i * V.rows()); 44 | } 45 | V = new_V; 46 | F = new_F; 47 | NV = new_NV; 48 | NF = new_NF; 49 | 50 | } 51 | 52 | void EuclideanCoveringSpaceComputer::Init() 53 | { 54 | if (!next_cone_vtx.is_valid()) 55 | mesh_.add_property(next_cone_vtx); 56 | boundary_segs_.clear(); 57 | for (int i = 0; i < cone_vts_.size(); ++i) { 58 | Segment seg; 59 | seg.start = cone_vts_[i]; 60 | seg.end = cone_vts_[(i + 1) % cone_vts_.size()]; 61 | mesh_.property(next_cone_vtx, seg.start) = seg.end; 62 | seg.start_coord = mesh_.texcoord2D(seg.start); 63 | seg.end_coord = mesh_.texcoord2D(seg.end); 64 | seg.valid = true; 65 | boundary_segs_.push_back(seg); 66 | } 67 | for (auto it = boundary_segs_.begin(); it != boundary_segs_.end(); ++it) { 68 | min_heap_.push(it); 69 | } 70 | orbits_.clear(); 71 | orbits_.resize(mesh_.n_vertices()); 72 | 73 | auto identity = [&mesh_=mesh_](OpenMesh::VertexHandle v)->OpenMesh::Vec2d {return mesh_.texcoord2D(v); }; 74 | ExtendOrbits(identity); 75 | } 76 | 77 | void EuclideanCoveringSpaceComputer::ExtendOrbits(std::function transformation) 78 | { 79 | using namespace OpenMesh; 80 | for (auto viter = mesh_.vertices_begin(); viter != mesh_.vertices_end(); ++viter) { 81 | VertexHandle v = *viter; 82 | orbits_[v.idx()].push_back(transformation(v)); 83 | } 84 | } 85 | 86 | void EuclideanCoveringSpaceComputer::StitchCommonSegment(std::list::iterator it1, std::list::iterator it2) 87 | { 88 | if ((it1->middle() - it2->middle()).norm() < 1e-5) { 89 | it1->valid = false; 90 | it2->valid = false; 91 | } 92 | } 93 | 94 | bool EuclideanCoveringSpaceComputer::Update() 95 | { 96 | using namespace OpenMesh; 97 | 98 | // pop it and find its neighbors in the segment list; 99 | auto it = min_heap_.top(); 100 | min_heap_.pop(); 101 | it->valid = false; 102 | auto prev_it = FindLastValid(it); 103 | auto next_it = FindNextValid(it); 104 | 105 | 106 | if (it->dist() > max_dist_) return false; 107 | 108 | 109 | VertexHandle start = it->start; 110 | VertexHandle end = it->end; 111 | 112 | VertexHandle start_equiv; 113 | VertexHandle end_equiv; 114 | 115 | if (mesh_.data(start).equivalent_vertex().is_valid()) 116 | start_equiv = mesh_.data(start).equivalent_vertex(); 117 | else 118 | start_equiv = start; 119 | 120 | if (mesh_.data(end).equivalent_vertex().is_valid()) 121 | end_equiv = mesh_.data(end).equivalent_vertex(); 122 | else 123 | end_equiv = end; 124 | 125 | Vec2d start_equiv_coord = mesh_.texcoord2D(start_equiv); 126 | Vec2d end_equiv_coord = mesh_.texcoord2D(end_equiv); 127 | auto transfer_to_complex = [](Vec2d p)->Complex {return Complex(p[0], p[1]); }; 128 | 129 | auto transformation_complex = ComputeRigidTransformation( 130 | transfer_to_complex(start_equiv_coord), 131 | transfer_to_complex(end_equiv_coord), 132 | transfer_to_complex(it->start_coord), 133 | transfer_to_complex(it->end_coord) 134 | ); 135 | 136 | auto transformation_vertex = [=, &mesh_=mesh_](VertexHandle v)->Vec2d { 137 | Vec2d p = mesh_.texcoord2D(v); 138 | auto result_complex = transformation_complex(transfer_to_complex(p)); 139 | return Vec2d(result_complex.real(), result_complex.imag()); 140 | }; 141 | 142 | 143 | 144 | VertexHandle viter; 145 | VertexHandle viter_bound; 146 | if (mesh_.property(next_cone_vtx, start_equiv) == end_equiv) { 147 | viter = end_equiv; 148 | viter_bound = start_equiv; 149 | } 150 | else { 151 | viter = start_equiv; 152 | viter_bound = end_equiv; 153 | } 154 | 155 | while (viter != end_equiv) 156 | { 157 | Segment seg; 158 | seg.start = viter; 159 | viter = mesh_.property(next_cone_vtx, viter); 160 | seg.end = viter; 161 | 162 | seg.start_coord = transformation_vertex(seg.start); 163 | seg.end_coord = transformation_vertex(seg.end); 164 | seg.valid = true; 165 | boundary_segs_.insert(it, seg); 166 | min_heap_.push(std::prev(it)); 167 | 168 | } 169 | 170 | boundary_segs_.erase(it); 171 | StitchCommonSegment(FindLastValid(next_it), next_it); 172 | StitchCommonSegment(FindNextValid(prev_it), prev_it); 173 | 174 | 175 | ExtendOrbits(transformation_vertex); 176 | 177 | return true; 178 | } 179 | 180 | std::list::iterator EuclideanCoveringSpaceComputer::FindLastValid(std::list::iterator it) 181 | { 182 | while (true) { 183 | if (it == boundary_segs_.begin()) 184 | it = std::prev(boundary_segs_.end()); 185 | else 186 | it = std::prev(it); 187 | if (it->valid) 188 | break; 189 | } 190 | return it; 191 | } 192 | 193 | std::list::iterator EuclideanCoveringSpaceComputer::FindNextValid(std::list::iterator it) 194 | { 195 | while (true) { 196 | if (std::next(it) == boundary_segs_.end()) 197 | it = boundary_segs_.begin(); 198 | else 199 | it = std::next(it); 200 | if (it->valid) 201 | break; 202 | } 203 | return it; 204 | } 205 | -------------------------------------------------------------------------------- /src/Utilities/EuclideanCoveringSpace.h: -------------------------------------------------------------------------------- 1 | #ifndef EUCLIDEAN_COVERING_SPACE_H_ 2 | #define EUCLIDEAN_COVERING_SPACE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "EuclideanGeometry2D.h" 8 | #include 9 | 10 | #include "MeshFormConverter.h" 11 | 12 | struct Segment { 13 | OpenMesh::Vec2d start_coord; 14 | OpenMesh::Vec2d end_coord; 15 | OpenMesh::VertexHandle start; 16 | OpenMesh::VertexHandle end; 17 | bool valid; 18 | 19 | double dist() { 20 | return ((start_coord + end_coord) / 2).norm(); 21 | } 22 | 23 | OpenMesh::Vec2d middle() { 24 | return (start_coord + end_coord) / 2; 25 | } 26 | 27 | } ; 28 | 29 | 30 | 31 | struct comparator { 32 | bool operator()(std::list::iterator it1, std::list::iterator it2) { 33 | return it1->dist() > it2->dist(); 34 | } 35 | }; 36 | 37 | 38 | 39 | class EuclideanCoveringSpaceComputer { 40 | public: 41 | EuclideanCoveringSpaceComputer(SurfaceMesh &mesh, std::vector cones); 42 | void Compute(); 43 | void GenerateMeshMatrix(Eigen::MatrixXd &V, Eigen::MatrixXd &NV, Eigen::MatrixXi &F, Eigen::MatrixXd &NF); 44 | protected: 45 | SurfaceMesh &mesh_; 46 | std::vector cone_vts_; 47 | std::list boundary_segs_; 48 | std::vector> orbits_; 49 | std::priority_queue::iterator, std::vector::iterator>, comparator> min_heap_; 50 | OpenMesh::VPropHandleT next_cone_vtx; 51 | 52 | double max_dist_ = 5; 53 | 54 | protected: 55 | void Init(); 56 | void ExtendOrbits(std::function); 57 | void StitchCommonSegment(std::list::iterator it1, std::list::iterator it2); 58 | bool Update(); 59 | std::list::iterator FindLastValid(std::list::iterator it); 60 | std::list::iterator FindNextValid(std::list::iterator it); 61 | }; 62 | 63 | #endif // !EUCLIDEAN_COVERING_SPACE_H_ 64 | -------------------------------------------------------------------------------- /src/Utilities/EuclideanGeometry2D.cpp: -------------------------------------------------------------------------------- 1 | #include "EuclideanGeometry2D.h" 2 | 3 | std::function ComputeRigidTransformation(Complex const s0, Complex const s1, Complex const t0, Complex const t1) 4 | { 5 | Complex source_direction = s1 - s0; 6 | Complex target_direction = t1 - t0; 7 | Complex rotate_factor = target_direction / source_direction; 8 | auto result = [=](Complex p0)->Complex {return (p0 - s0)*rotate_factor + t0; }; 9 | assert(std::abs(result(s1) - t1) < 1e-7); 10 | return [=](Complex p0)->Complex {return (p0 - s0)*rotate_factor + t0; }; 11 | } 12 | 13 | Eigen::Matrix3d ComputeHomogeousRigidTransformation(Complex const s0, Complex const s1, Complex const t0, Complex const t1) 14 | { 15 | Complex rotation = (t1 - t0) / (s1 - s0); 16 | Eigen::Matrix3d R = Eigen::Matrix3d::Identity(); 17 | Eigen::Matrix3d Ts = Eigen::Matrix3d::Identity(); 18 | Eigen::Matrix3d Tt = Eigen::Matrix3d::Identity(); 19 | R(0, 0) = rotation.real(); 20 | R(0, 1) = -rotation.imag(); 21 | R(1, 0) = rotation.imag(); 22 | R(1, 1) = rotation.real(); 23 | Ts(0, 2) = -s0.real(); 24 | Ts(1, 2) = -s0.imag(); 25 | Ts(2, 2) = 1; 26 | Tt(0, 2) = t0.real(); 27 | Tt(1, 2) = t0.imag(); 28 | Tt(2, 2) = 1; 29 | Eigen::Matrix3d T = Tt * R * Ts; 30 | 31 | // test 32 | Eigen::Vector3d s(s1.real(), s1.imag(), 1); 33 | Eigen::Vector3d t(t1.real(), t1.imag(), 1); 34 | assert((T*s - t).norm() < 1e-5); 35 | return T; 36 | } 37 | -------------------------------------------------------------------------------- /src/Utilities/EuclideanGeometry2D.h: -------------------------------------------------------------------------------- 1 | #ifndef EULIDEAN_GEOMETRY_2D_H_ 2 | #define EULIDEAN_GEOMETRY_2D_H_ 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | typedef std::complex Complex; 11 | 12 | std::function ComputeRigidTransformation(Complex const s0, Complex const s1, Complex const t0, Complex const t1); 13 | 14 | Eigen::Matrix3d ComputeHomogeousRigidTransformation(Complex const s0, Complex const s1, Complex const t0, Complex const t1); 15 | 16 | 17 | #endif -------------------------------------------------------------------------------- /src/Utilities/HyperbolicCoveringSpace.cpp: -------------------------------------------------------------------------------- 1 | #include "HyperbolicCoveringSpace.h" 2 | 3 | HyperbolicCoveringSpaceComputer::HyperbolicCoveringSpaceComputer(SurfaceMesh & mesh, std::vector cones) 4 | :mesh_(mesh), cone_vts_(cones) 5 | { 6 | for (auto it = cone_vts_.begin(); it != cone_vts_.end(); ++it) { 7 | OpenMesh::VertexHandle v = *it; 8 | OpenMesh::VertexHandle ev = mesh_.data(v).equivalent_vertex(); 9 | } 10 | } 11 | 12 | void HyperbolicCoveringSpaceComputer::Compute() 13 | { 14 | Init(); 15 | 16 | while (Update()) { 17 | while (!min_heap_.top()->valid) { 18 | auto it = min_heap_.top(); 19 | min_heap_.pop(); 20 | boundary_segs_.erase(it); 21 | } 22 | std::cout << "Min Dist:" << (min_heap_.top())->dist() << std::endl; 23 | } 24 | } 25 | 26 | void HyperbolicCoveringSpaceComputer::GenerateMeshMatrix(Eigen::MatrixXd & V, Eigen::MatrixXd & NV, Eigen::MatrixXi & F, Eigen::MatrixXd & NF) 27 | { 28 | OpenMeshToMatrix(mesh_, V, NV, F, NF); 29 | 30 | int n_copies = orbits_[0].size(); 31 | 32 | Eigen::MatrixXd new_V(V.rows()* n_copies, 3); 33 | Eigen::MatrixXi new_F(F.rows() * n_copies, 3); 34 | Eigen::MatrixXd new_NV(NV.rows() *n_copies, 3); 35 | Eigen::MatrixXd new_NF(NF.rows() * n_copies, 3); 36 | new_V.setZero(); 37 | for (int i = 0; i < n_copies; ++i) { 38 | new_NV.block(i * V.rows(), 0, V.rows(), 3) = NV; 39 | new_NF.block(i * F.rows(), 0, F.rows(), 3) = NF; 40 | for (auto viter = mesh_.vertices_begin(); viter != mesh_.vertices_end(); ++viter) 41 | { 42 | OpenMesh::VertexHandle v = *viter; 43 | for (int k = 0; k < 2; ++k) 44 | new_V(i * V.rows() + v.idx(), k) = orbits_[v.idx()][i][k]; 45 | } 46 | new_F.block(i * F.rows(), 0, F.rows(), 3) = F + Eigen::MatrixXi::Constant(F.rows(), 3, i * V.rows()); 47 | } 48 | V = new_V; 49 | F = new_F; 50 | NV = new_NV; 51 | NF = new_NF; 52 | 53 | } 54 | 55 | void HyperbolicCoveringSpaceComputer::Init() 56 | { 57 | if (!next_cone_vtx.is_valid()) 58 | mesh_.add_property(next_cone_vtx); 59 | boundary_segs_.clear(); 60 | for (int i = 0; i < cone_vts_.size(); ++i) { 61 | HyperbolicSegment seg; 62 | seg.start = cone_vts_[i]; 63 | seg.end = cone_vts_[(i + 1) % cone_vts_.size()]; 64 | mesh_.property(next_cone_vtx, seg.start) = seg.end; 65 | seg.start_coord = mesh_.texcoord2D(seg.start); 66 | seg.end_coord = mesh_.texcoord2D(seg.end); 67 | seg.valid = true; 68 | boundary_segs_.push_back(seg); 69 | } 70 | for (auto it = boundary_segs_.begin(); it != boundary_segs_.end(); ++it) { 71 | min_heap_.push(it); 72 | } 73 | orbits_.clear(); 74 | orbits_.resize(mesh_.n_vertices()); 75 | 76 | auto identity = [&mesh_ = mesh_](OpenMesh::VertexHandle v)->OpenMesh::Vec2d {return mesh_.texcoord2D(v); }; 77 | ExtendOrbits(identity); 78 | } 79 | 80 | void HyperbolicCoveringSpaceComputer::ExtendOrbits(std::function transformation) 81 | { 82 | using namespace OpenMesh; 83 | for (auto viter = mesh_.vertices_begin(); viter != mesh_.vertices_end(); ++viter) { 84 | VertexHandle v = *viter; 85 | orbits_[v.idx()].push_back(transformation(v)); 86 | } 87 | } 88 | 89 | void HyperbolicCoveringSpaceComputer::StitchCommonSegment(std::list::iterator it1, std::list::iterator it2) 90 | { 91 | if ((*it1 - *it2) < 1e-5) { 92 | it1->valid = false; 93 | it2->valid = false; 94 | } 95 | } 96 | 97 | bool HyperbolicCoveringSpaceComputer::Update() 98 | { 99 | using namespace OpenMesh; 100 | 101 | // pop it and find its neighbors in the HyperbolicSegment list; 102 | auto it = min_heap_.top(); 103 | min_heap_.pop(); 104 | it->valid = false; 105 | auto prev_it = FindLastValid(it); 106 | auto next_it = FindNextValid(it); 107 | 108 | 109 | if (it->dist() > max_dist_) return false; 110 | 111 | 112 | VertexHandle start = it->start; 113 | VertexHandle end = it->end; 114 | 115 | VertexHandle start_equiv; 116 | VertexHandle end_equiv; 117 | 118 | if (mesh_.data(start).equivalent_vertex().is_valid()) 119 | start_equiv = mesh_.data(start).equivalent_vertex(); 120 | else 121 | start_equiv = start; 122 | 123 | if (mesh_.data(end).equivalent_vertex().is_valid()) 124 | end_equiv = mesh_.data(end).equivalent_vertex(); 125 | else 126 | end_equiv = end; 127 | 128 | Vec2d start_equiv_coord = mesh_.texcoord2D(start_equiv); 129 | Vec2d end_equiv_coord = mesh_.texcoord2D(end_equiv); 130 | auto transfer_to_complex = [](Vec2d p)->Complex {return Complex(p[0], p[1]); }; 131 | 132 | auto transformation_complex = ComputeMobiusTransformation( 133 | transfer_to_complex(start_equiv_coord), 134 | transfer_to_complex(end_equiv_coord), 135 | transfer_to_complex(it->start_coord), 136 | transfer_to_complex(it->end_coord) 137 | ); 138 | 139 | auto transformation_vertex = [=, &mesh_ = mesh_](VertexHandle v)->Vec2d { 140 | Vec2d p = mesh_.texcoord2D(v); 141 | auto result_complex = transformation_complex(transfer_to_complex(p)); 142 | return Vec2d(result_complex.real(), result_complex.imag()); 143 | }; 144 | 145 | 146 | 147 | VertexHandle viter; 148 | VertexHandle viter_bound; 149 | if (mesh_.property(next_cone_vtx, start_equiv) == end_equiv) { 150 | viter = end_equiv; 151 | viter_bound = start_equiv; 152 | } 153 | else { 154 | viter = start_equiv; 155 | viter_bound = end_equiv; 156 | } 157 | 158 | while (viter != end_equiv) 159 | { 160 | HyperbolicSegment seg; 161 | seg.start = viter; 162 | viter = mesh_.property(next_cone_vtx, viter); 163 | seg.end = viter; 164 | 165 | seg.start_coord = transformation_vertex(seg.start); 166 | seg.end_coord = transformation_vertex(seg.end); 167 | seg.valid = true; 168 | boundary_segs_.insert(it, seg); 169 | min_heap_.push(std::prev(it)); 170 | 171 | } 172 | 173 | boundary_segs_.erase(it); 174 | StitchCommonSegment(FindLastValid(next_it), next_it); 175 | StitchCommonSegment(FindNextValid(prev_it), prev_it); 176 | 177 | 178 | ExtendOrbits(transformation_vertex); 179 | 180 | return true; 181 | } 182 | 183 | std::list::iterator HyperbolicCoveringSpaceComputer::FindLastValid(std::list::iterator it) 184 | { 185 | while (true) { 186 | if (it == boundary_segs_.begin()) 187 | it = std::prev(boundary_segs_.end()); 188 | else 189 | it = std::prev(it); 190 | if (it->valid) 191 | break; 192 | } 193 | return it; 194 | } 195 | 196 | std::list::iterator HyperbolicCoveringSpaceComputer::FindNextValid(std::list::iterator it) 197 | { 198 | while (true) { 199 | if (std::next(it) == boundary_segs_.end()) 200 | it = boundary_segs_.begin(); 201 | else 202 | it = std::next(it); 203 | if (it->valid) 204 | break; 205 | } 206 | return it; 207 | } 208 | -------------------------------------------------------------------------------- /src/Utilities/HyperbolicCoveringSpace.h: -------------------------------------------------------------------------------- 1 | #ifndef HYPERBOLIC_COVERING_SPACE_H_ 2 | #define HYPERBOLIC_COVERING_SPACE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "HyperbolicGeometry.h" 8 | #include 9 | 10 | #include "MeshFormConverter.h" 11 | 12 | struct HyperbolicSegment { 13 | OpenMesh::Vec2d start_coord; 14 | OpenMesh::Vec2d end_coord; 15 | OpenMesh::VertexHandle start; 16 | OpenMesh::VertexHandle end; 17 | bool valid; 18 | 19 | double dist() { 20 | auto middle = (start_coord + end_coord) / 2; 21 | return HyperbolicDistance(Complex(0,0), Complex(middle[0], middle[1])); 22 | } 23 | 24 | OpenMesh::Vec2d middle() { 25 | return (start_coord + end_coord) / 2; 26 | } 27 | 28 | double operator - (HyperbolicSegment &s) 29 | { 30 | auto middle1 = (start_coord + end_coord) / 2; 31 | auto middle2 = (s.start_coord + s.end_coord) / 2; 32 | return HyperbolicDistance(Complex(middle1[0], middle1[1]), Complex(middle2[0], middle2[1])); 33 | } 34 | 35 | }; 36 | 37 | 38 | 39 | struct hyperboliccomparator { 40 | bool operator()(std::list::iterator it1, std::list::iterator it2) { 41 | return it1->dist() > it2->dist(); 42 | } 43 | }; 44 | 45 | 46 | 47 | class HyperbolicCoveringSpaceComputer { 48 | public: 49 | HyperbolicCoveringSpaceComputer(SurfaceMesh &mesh, std::vector cones); 50 | void Compute(); 51 | void GenerateMeshMatrix(Eigen::MatrixXd &V, Eigen::MatrixXd &NV, Eigen::MatrixXi &F, Eigen::MatrixXd &NF); 52 | protected: 53 | SurfaceMesh &mesh_; 54 | std::vector cone_vts_; 55 | std::list boundary_segs_; 56 | std::vector> orbits_; 57 | std::priority_queue::iterator, std::vector::iterator>, hyperboliccomparator> min_heap_; 58 | OpenMesh::VPropHandleT next_cone_vtx; 59 | 60 | double max_dist_ = 3.5; 61 | 62 | protected: 63 | void Init(); 64 | void ExtendOrbits(std::function); 65 | void StitchCommonSegment(std::list::iterator it1, std::list::iterator it2); 66 | bool Update(); 67 | std::list::iterator FindLastValid(std::list::iterator it); 68 | std::list::iterator FindNextValid(std::list::iterator it); 69 | }; 70 | 71 | #endif // !EUCLIDEAN_COVERING_SPACE_H_ 72 | -------------------------------------------------------------------------------- /src/Utilities/HyperbolicGeometry.cpp: -------------------------------------------------------------------------------- 1 | #include "HyperbolicGeometry.h" 2 | #include 3 | 4 | std::function ComputeMobiusTransformation(Complex const s0, Complex const s1, Complex const t0, Complex const t1) 5 | { 6 | auto s0_to_zero = [=](Complex const c)->Complex { 7 | return (c - s0) / (Complex(1,0) - std::conj(s0)*c); 8 | }; 9 | 10 | auto t0_to_zero = [=](Complex const c)->Complex { 11 | return (c - t0) / (Complex(1, 0) - std::conj(t0)*c); 12 | }; 13 | 14 | auto zero_to_t0 = [=](Complex const c)->Complex { 15 | return (c + t0) / (Complex(1, 0) + std::conj(t0)*c); 16 | }; 17 | 18 | Complex new_s1 = s0_to_zero(s1); 19 | Complex new_t1 = t0_to_zero(t1); 20 | 21 | Complex rotation_coeff = new_t1 / new_s1; 22 | 23 | auto result = [=](Complex const c)->Complex { 24 | Complex resualt = (c - s0) / (Complex(1, 0) - std::conj(s0)*c); 25 | resualt *= rotation_coeff; 26 | resualt = (resualt + t0) / (Complex(1, 0) + std::conj(t0)*resualt); 27 | return resualt; 28 | }; 29 | auto test = result(s1); 30 | assert(std::abs(result(s1) - t1) < 1e-6); 31 | return result; 32 | } 33 | 34 | double HyperbolicDistance(Complex p0, Complex p1) 35 | { 36 | double dist = acosh( 37 | 1 + 2 * abs(p0 - p1) * abs(p0 - p1) / 38 | ((1. - abs(p0) * abs(p0)) * (1 - abs(p1) * abs(p1))) 39 | ); 40 | return dist; 41 | } 42 | 43 | Complex InverseExponentialMap(Complex p0, Complex p1) 44 | { 45 | Circle unit(Complex(0, 0), 1.0); 46 | Complex reflection = ReflectByCircle(p0, unit); 47 | Circle geodesic_circle = CircleFromPoints(std::vector({ p0, p1, reflection })); 48 | double tangent_length = pow(1 - abs(p0) * abs(p0),2) / 4; 49 | double angle0 = geodesic_circle.PolarAngle(p0); 50 | double angle1 = geodesic_circle.PolarAngle(p1); 51 | Complex tangent = geodesic_circle.Tangent(p0); 52 | if (angle1 < angle0) 53 | tangent = -tangent; 54 | double dist = HyperbolicDistance(p0, p1); 55 | double ratio = pow(1 - pow(abs(p0), 2), 2) / 4.0; 56 | return tangent * dist * ratio; 57 | } 58 | -------------------------------------------------------------------------------- /src/Utilities/HyperbolicGeometry.h: -------------------------------------------------------------------------------- 1 | #ifndef HYPERBOLIC_GEOMETRY_H_ 2 | #define HYPERBOLIC_GEOMETRY_H_ 3 | #include 4 | #include 5 | #include 6 | #include "Circle.h" 7 | 8 | typedef std::complex Complex; 9 | 10 | /* 11 | These codes are used to handle geometry on Poincare disk 12 | */ 13 | 14 | 15 | 16 | std::function ComputeMobiusTransformation(Complex const s0, Complex const s1, Complex const t0, Complex const t1); 17 | 18 | double HyperbolicDistance(Complex p0, Complex p1); 19 | 20 | Complex InverseExponentialMap(Complex p0, Complex p1); 21 | 22 | #endif // !HYPERBOLIC_GEOEMETRY_H_ 23 | -------------------------------------------------------------------------------- /src/Utilities/LineCylinder.cpp: -------------------------------------------------------------------------------- 1 | #include "LineCylinder.h" 2 | #include 3 | 4 | bool LineCylinders(const Eigen::MatrixXd & P1, const Eigen::MatrixXd & P2, const double & radius, const Eigen::MatrixXd & C, const int res, const bool colorPerVertex, Eigen::MatrixXd & V, Eigen::MatrixXi & T, Eigen::MatrixXd & TC) 5 | { 6 | using namespace Eigen; 7 | V.resize(2 * res*P1.rows(), 3); 8 | T.resize(2 * res*P1.rows(), 3); 9 | RowVector3d ZAxis; ZAxis << 0.0, 0.0, 1.0; 10 | RowVector3d YAxis; YAxis << 0.0, 1.0, 0.0; 11 | 12 | int NewColorSize = (colorPerVertex ? V.rows() : T.rows()); 13 | TC.resize(NewColorSize, 3); 14 | 15 | MatrixXd PlanePattern(res, 2); 16 | for (int i = 0; i < res; i++) { 17 | std::complex CurrRoot = exp(2 * M_PI*std::complex(0, 1)*(double)i / (double)res); 18 | PlanePattern.row(i) << CurrRoot.real(), CurrRoot.imag(); 19 | } 20 | 21 | 22 | for (int i = 0; i < P1.rows(); i++) { 23 | RowVector3d NormAxis = (P2.row(i) - P1.row(i)).normalized(); 24 | RowVector3d PlaneAxis1 = NormAxis.cross(ZAxis); 25 | if (PlaneAxis1.norm() < 10e-2) 26 | PlaneAxis1 = NormAxis.cross(YAxis).normalized(); 27 | else 28 | PlaneAxis1 = PlaneAxis1.normalized(); 29 | RowVector3d PlaneAxis2 = NormAxis.cross(PlaneAxis1).normalized(); 30 | for (int j = 0; j < res; j++) { 31 | int v1 = 2 * res*i + 2 * j; 32 | int v2 = 2 * res*i + 2 * j + 1; 33 | int v3 = 2 * res*i + 2 * ((j + 1) % res); 34 | int v4 = 2 * res*i + 2 * ((j + 1) % res) + 1; 35 | V.row(v1) << P1.row(i) + (PlaneAxis1*PlanePattern(j, 0) + PlaneAxis2*PlanePattern(j, 1))*radius; 36 | V.row(v2) << P2.row(i) + (PlaneAxis1*PlanePattern(j, 0) + PlaneAxis2*PlanePattern(j, 1))*radius; 37 | 38 | if (colorPerVertex) { 39 | TC.row(v1) << C.row(i); 40 | TC.row(v2) << C.row(i); 41 | } 42 | 43 | 44 | T.row(2 * res*i + 2 * j) << v3, v2, v1; 45 | T.row(2 * res*i + 2 * j + 1) << v4, v2, v3; 46 | 47 | if (!colorPerVertex) { 48 | TC.row(2 * res*i + 2 * j) << C.row(i); 49 | TC.row(2 * res*i + 2 * j + 1) << C.row(i); 50 | } 51 | } 52 | } 53 | return true; 54 | } 55 | -------------------------------------------------------------------------------- /src/Utilities/LineCylinder.h: -------------------------------------------------------------------------------- 1 | #ifndef LINE_CYLINDER 2 | #define LINE_CYLINDER 3 | 4 | #include 5 | 6 | // This function is copied from libhedra library: 7 | // https://github.com/avaxman/libhedra/blob/master/include/hedra/line_cylinders.h 8 | bool LineCylinders(const Eigen::MatrixXd& P1, 9 | const Eigen::MatrixXd& P2, 10 | const double& radius, 11 | const Eigen::MatrixXd& C, 12 | const int res, 13 | const bool colorPerVertex, 14 | Eigen::MatrixXd& V, 15 | Eigen::MatrixXi& T, 16 | Eigen::MatrixXd& TC); 17 | 18 | #endif // !LINE_CYLINDER 19 | -------------------------------------------------------------------------------- /src/Utilities/MeshFormConverter.cpp: -------------------------------------------------------------------------------- 1 | #include "MeshFormConverter.h" 2 | 3 | void OpenMeshToMatrix(SurfaceMesh & mesh, Eigen::MatrixXd & V, Eigen::MatrixXi & F) 4 | { 5 | int nv = mesh.n_vertices(); 6 | if (nv == 0) 7 | return; 8 | int nf = mesh.n_faces(); 9 | 10 | V.setZero(); 11 | V.resize(nv, 3); 12 | 13 | /*load vertex data*/ 14 | for (SurfaceMesh::VertexIter viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 15 | SurfaceMesh::VertexHandle v = *viter; 16 | 17 | int idx = v.idx(); 18 | SurfaceMesh::Point point = mesh.point(v); 19 | 20 | for (int i = 0; i < 3; i++) { 21 | V(idx, i) = point[i]; 22 | } 23 | } 24 | 25 | F.resize(nf, 3); 26 | F.setConstant(-1); 27 | for (SurfaceMesh::FaceIter fiter = mesh.faces_begin(); fiter != mesh.faces_end(); ++fiter) { 28 | SurfaceMesh::FaceHandle f = *fiter; 29 | int i = 0; 30 | for (SurfaceMesh::FaceVertexIter fviter = mesh.fv_iter(f); fviter.is_valid(); ++fviter) { 31 | SurfaceMesh::VertexHandle v = *fviter; 32 | F(f.idx(), i) = v.idx(); 33 | i++; 34 | } 35 | } 36 | } 37 | 38 | void OpenMeshToMatrix(SurfaceMesh & mesh, Eigen::MatrixXd & V, Eigen::MatrixXd & V_normal, Eigen::MatrixXi & F, Eigen::MatrixXd & F_normal) 39 | { 40 | int nv = mesh.n_vertices(); 41 | if (nv == 0) 42 | return; 43 | int nf = mesh.n_faces(); 44 | mesh.update_normals(); 45 | 46 | V.setZero(); 47 | V.resize(nv, 3); 48 | V_normal.resize(nv, 3); 49 | F_normal.resize(nf, 3); 50 | F.resize(nf, 3); 51 | F.setConstant(-1); 52 | 53 | /*load vertex data*/ 54 | for (SurfaceMesh::VertexIter viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 55 | SurfaceMesh::VertexHandle v = *viter; 56 | 57 | int idx = v.idx(); 58 | SurfaceMesh::Point point = mesh.point(v); 59 | SurfaceMesh::Normal normal = mesh.normal(v); 60 | 61 | for (int i = 0; i < 3; i++) { 62 | V(idx, i) = point[i]; 63 | V_normal(idx, i) = normal[i]; 64 | } 65 | } 66 | 67 | 68 | for (SurfaceMesh::FaceIter fiter = mesh.faces_begin(); fiter != mesh.faces_end(); ++fiter) { 69 | SurfaceMesh::FaceHandle f = *fiter; 70 | SurfaceMesh::Normal normal = mesh.normal(f); 71 | int i = 0; 72 | for (SurfaceMesh::FaceVertexIter fviter = mesh.fv_iter(f); fviter.is_valid(); ++fviter) { 73 | SurfaceMesh::VertexHandle v = *fviter; 74 | F(f.idx(), i) = v.idx(); 75 | if(i < 3) 76 | F_normal(f.idx(), i) = normal[i]; 77 | i++; 78 | } 79 | } 80 | } 81 | 82 | 83 | 84 | void OpenMeshCoordToMatrix(SurfaceMesh & mesh, Eigen::MatrixXd &UV) 85 | { 86 | int nv = mesh.n_vertices(); 87 | int nf = mesh.n_faces(); 88 | 89 | UV.setZero(); 90 | UV.resize(nv, 2); 91 | /*load vertex data*/ 92 | for (SurfaceMesh::VertexIter viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 93 | SurfaceMesh::VertexHandle v = *viter; 94 | int idx = v.idx(); 95 | SurfaceMesh::TexCoord2D point = mesh.texcoord2D(v); 96 | 97 | for (int i = 0; i < 2; i++) { 98 | UV(idx, i) = point[i]; 99 | } 100 | } 101 | } 102 | 103 | void MatrixToOpenMesh(Eigen::MatrixXd & V, Eigen::MatrixXi & F, SurfaceMesh & mesh) 104 | { 105 | 106 | } 107 | 108 | void HalfedgesToMatrix(SurfaceMesh & mesh, std::vector halfedges, Eigen::MatrixXd & P1, Eigen::MatrixXd & P2) 109 | { 110 | int n_halfedges = halfedges.size(); 111 | P1.resize(n_halfedges, 3); 112 | P2.resize(n_halfedges, 3); 113 | for (int i = 0; i < n_halfedges; i++) { 114 | OpenMesh::HalfedgeHandle h = halfedges[i]; 115 | OpenMesh::VertexHandle v1 = mesh.from_vertex_handle(h); 116 | OpenMesh::VertexHandle v2 = mesh.to_vertex_handle(h); 117 | OpenMesh::Vec3d p1 = mesh.point(v1); 118 | OpenMesh::Vec3d p2 = mesh.point(v2); 119 | P1.row(i) = Eigen::RowVector3d(p1[0], p1[1], p1[2]); 120 | P2.row(i) = Eigen::RowVector3d(p2[0], p2[1], p2[2]); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Utilities/MeshFormConverter.h: -------------------------------------------------------------------------------- 1 | #ifndef MESH_FORM_CONVERTER 2 | #define MESH_FORM_CONVERTER 3 | 4 | #include 5 | #include 6 | 7 | void OpenMeshToMatrix(SurfaceMesh &mesh, Eigen::MatrixXd &V, Eigen::MatrixXi &F); 8 | void OpenMeshToMatrix(SurfaceMesh &mesh, Eigen::MatrixXd &V, Eigen::MatrixXd &V_normal, Eigen::MatrixXi &F, Eigen::MatrixXd &F_normal); 9 | 10 | void OpenMeshCoordToMatrix(SurfaceMesh &mesh, Eigen::MatrixXd &UV); 11 | 12 | void MatrixToOpenMesh(Eigen::MatrixXd &V, Eigen::MatrixXi &F, SurfaceMesh &mesh); 13 | 14 | void HalfedgesToMatrix(SurfaceMesh &mesh, std::vector halfedges, Eigen::MatrixXd &P1, Eigen::MatrixXd &P2); 15 | 16 | #endif // !MESH_FORM_CONVERTER 17 | -------------------------------------------------------------------------------- /src/Utilities/MeshMarker.cpp: -------------------------------------------------------------------------------- 1 | #include "MeshMarker.h" 2 | #include 3 | 4 | void MeshMarker::ResetMarker() 5 | { 6 | SurfaceMesh &mesh = *p_mesh_; 7 | using namespace OpenMesh; 8 | if (!singularity_.is_valid()) 9 | mesh.add_property(singularity_); 10 | if (!slice_.is_valid()) 11 | mesh.add_property(slice_); 12 | if (!cone_angle_.is_valid()) 13 | mesh.add_property(cone_angle_); 14 | 15 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 16 | OpenMesh::VertexHandle v = *viter; 17 | mesh.property(singularity_, v) = false; 18 | mesh.property(cone_angle_, v) = 2 *PI; 19 | } 20 | for (auto eiter = mesh.edges_begin(); eiter != mesh.edges_end(); ++eiter) { 21 | OpenMesh::EdgeHandle e = *eiter; 22 | mesh.property(slice_, e) = false; 23 | } 24 | n_edges_ = 0; 25 | n_vertices_ = 0; 26 | } 27 | 28 | // Compute the shortest path between two vertex and set slice flag. 29 | void MeshMarker::ComputeAndSetSlice(OpenMesh::VertexHandle v0, OpenMesh::VertexHandle v1) 30 | { 31 | SurfaceMesh &mesh = *p_mesh_; 32 | using namespace OpenMesh; 33 | VPropHandleT dist; 34 | VPropHandleT parent; 35 | 36 | DijkstraShortestDist(mesh, v0, dist, parent); 37 | std::vector slice_vertices; 38 | 39 | VertexHandle cptr = v1; 40 | slice_vertices.push_back(v1); 41 | while (mesh.property(parent, cptr).is_valid()) { 42 | cptr = mesh.property(parent, cptr); 43 | slice_vertices.push_back(cptr); 44 | } 45 | 46 | std::reverse(slice_vertices.begin(), slice_vertices.end()); 47 | 48 | for (int i = 0; i < slice_vertices.size() - 1; ++i) { 49 | EdgeHandle e = mesh.edge_handle(mesh.find_halfedge(slice_vertices[i], slice_vertices[i + 1])); 50 | if (!mesh.property(slice_, e)) { 51 | mesh.property(slice_, e) = true; 52 | ++n_edges_; 53 | } 54 | } 55 | 56 | } 57 | 58 | // The cone angle is cone_angle * PI; 59 | void MeshMarker::SetSingularity(OpenMesh::VertexHandle v, double cone_angle) 60 | { 61 | SurfaceMesh &mesh = *p_mesh_; 62 | using namespace OpenMesh; 63 | if (!mesh.property(singularity_, v)) { 64 | mesh.property(singularity_, v) = true; 65 | mesh.property(cone_angle_, v) = cone_angle * PI; 66 | ++n_vertices_; 67 | } 68 | else { 69 | mesh.property(cone_angle_, v) = cone_angle * PI; 70 | } 71 | } 72 | 73 | void MeshMarker::GenerateMatrix(Eigen::MatrixXd & P, Eigen::MatrixXd & EP1, Eigen::MatrixXd & EP2) 74 | { 75 | SurfaceMesh &mesh = *p_mesh_; 76 | using namespace OpenMesh; 77 | 78 | P.resize(n_vertices_, 3); 79 | EP1.resize(n_edges_, 3); 80 | EP2.resize(n_edges_, 3); 81 | 82 | int index = 0; 83 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 84 | OpenMesh::VertexHandle v = *viter; 85 | if (mesh.property(singularity_, v)) { 86 | auto p = mesh.point(v); 87 | P.row(index) = Eigen::Vector3d(p[0], p[1], p[2]); 88 | ++index; 89 | } 90 | } 91 | index = 0; 92 | for (auto eiter = mesh.edges_begin(); eiter != mesh.edges_end(); ++eiter) { 93 | OpenMesh::EdgeHandle e = *eiter; 94 | if (mesh.property(slice_, e)) { 95 | HalfedgeHandle h = mesh.halfedge_handle(e, 0); 96 | VertexHandle v1 = mesh.from_vertex_handle(h); 97 | VertexHandle v2 = mesh.to_vertex_handle(h); 98 | auto p1 = mesh.point(v1); 99 | auto p2 = mesh.point(v2); 100 | EP1.row(index) = Eigen::Vector3d(p1[0], p1[1], p1[2]); 101 | EP2.row(index) = Eigen::Vector3d(p2[0], p2[1], p2[2]); 102 | ++index; 103 | } 104 | } 105 | } 106 | 107 | void MeshMarker::LoadFromFile(std::string filename) 108 | { 109 | using namespace OpenMesh; 110 | SurfaceMesh &mesh = *p_mesh_; 111 | ResetMarker(); 112 | std::ifstream f(filename); 113 | std::string line; 114 | if (f.is_open()) 115 | { 116 | while (!f.eof()) 117 | { 118 | std::getline(f, line); 119 | std::vector parser; 120 | Split(" ", line, parser); 121 | if (parser.size() == 0) continue; 122 | if (parser[0] == "v") { 123 | VertexHandle v = mesh.vertex_handle(stoi(parser[1])); 124 | mesh.property(singularity_, v) = true; 125 | mesh.property(cone_angle_, v) = atof(parser[2].c_str()); 126 | ++n_vertices_; 127 | } 128 | else if (parser[0] == "e") { 129 | EdgeHandle e = mesh.edge_handle(stoi(parser[1])); 130 | mesh.property(slice_, e) = true; 131 | ++n_edges_; 132 | } 133 | } 134 | f.close(); 135 | } 136 | } 137 | 138 | void MeshMarker::SaveToFile(std::string filename) 139 | { 140 | using namespace OpenMesh; 141 | SurfaceMesh &mesh = *p_mesh_; 142 | 143 | std::ofstream f(filename); 144 | 145 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 146 | VertexHandle v = *viter; 147 | if (mesh.property(singularity_, v)) { 148 | f << "v " << v.idx() << " " << std::setprecision(9) << mesh.property(cone_angle_, v) << "\n"; 149 | } 150 | } 151 | for (auto eiter = mesh.edges_begin(); eiter != mesh.edges_end(); ++eiter) { 152 | EdgeHandle e = *eiter; 153 | if (mesh.property(slice_, e)) { 154 | f << "e " << e.idx() << "\n"; 155 | } 156 | } 157 | 158 | f.close(); 159 | 160 | } 161 | -------------------------------------------------------------------------------- /src/Utilities/MeshMarker.h: -------------------------------------------------------------------------------- 1 | #ifndef MESH_MARKER_H_ 2 | #define MESH_MARKER_H_ 3 | 4 | #include 5 | #include "Dijkstra.h" 6 | #include 7 | #include "StringParser.h" 8 | 9 | #ifndef PI 10 | #define PI 3.14159254 11 | #endif // !PI 12 | 13 | // This class is to store user-defined cones, cone angles and slices. 14 | class MeshMarker { 15 | public: 16 | void SetObject(SurfaceMesh &mesh) { p_mesh_ = &mesh; }; 17 | void ResetMarker(); 18 | void ComputeAndSetSlice(OpenMesh::VertexHandle v0, OpenMesh::VertexHandle v1); 19 | void SetSingularity(OpenMesh::VertexHandle v, double cone_angle); 20 | OpenMesh::VPropHandleT GetSingularityFlag() { return singularity_; } 21 | OpenMesh::EPropHandleT GetSliceFlag() { return slice_; } 22 | OpenMesh::VPropHandleT GetConeAngleFlag() { return cone_angle_; } 23 | void GenerateMatrix(Eigen::MatrixXd &P, Eigen::MatrixXd &EP1, Eigen::MatrixXd &EP2); 24 | void LoadFromFile(std::string filename); 25 | void SaveToFile(std::string filename); 26 | protected: 27 | SurfaceMesh *p_mesh_; 28 | OpenMesh::VPropHandleT singularity_; 29 | OpenMesh::VPropHandleT cone_angle_; 30 | OpenMesh::EPropHandleT slice_; 31 | int n_edges_; 32 | int n_vertices_; 33 | }; 34 | 35 | #endif // !MESH_MARKER_H_ 36 | -------------------------------------------------------------------------------- /src/Utilities/MeshMerger.cpp: -------------------------------------------------------------------------------- 1 | #include "MeshMerger.h" 2 | 3 | void MergeMeshMatrix(Eigen::MatrixXd V1, Eigen::MatrixXi T1, Eigen::MatrixXd TC1, 4 | Eigen::MatrixXd V2, Eigen::MatrixXi T2, Eigen::MatrixXd TC2, 5 | Eigen::MatrixXd & OV, Eigen::MatrixXi & OT, Eigen::MatrixXd &OTC) 6 | { 7 | using namespace Eigen; 8 | if (V1.rows() == 0) { 9 | OV = V2; 10 | OT = T2; 11 | OTC = TC2; 12 | } 13 | else if (V2.rows() == 0) { 14 | OV = V1; 15 | OT = T1; 16 | OTC = TC1; 17 | } 18 | 19 | MatrixXd bigV(V1.rows() + V2.rows(), 3); 20 | MatrixXi bigT(T1.rows() + T2.rows(), 3); 21 | MatrixXd bigTC(TC1.rows() + TC2.rows(), 3); 22 | bigV.block(0, 0, V1.rows(), 3) = V1; 23 | bigT.block(0, 0, T1.rows(), 3) = T1; 24 | bigTC.block(0, 0, TC1.rows(), 3) = TC1; 25 | bigV.block(V1.rows(), 0, V2.rows(), 3) = V2; 26 | bigT.block(T1.rows(), 0, T2.rows(), 3) = T2 + Eigen::MatrixXi::Constant(T2.rows(), T2.cols(), V1.rows()); 27 | bigTC.block(TC1.rows(), 0, TC2.rows(), 3) = TC2; 28 | OV = bigV; 29 | OT = bigT; 30 | OTC = bigTC; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/Utilities/MeshMerger.h: -------------------------------------------------------------------------------- 1 | #ifndef MESH_MERGER_H 2 | #define MESH_MERGER_H 3 | 4 | #include 5 | 6 | // This function is used to merge two meshes represented by matrix 7 | void MergeMeshMatrix(Eigen::MatrixXd V1, Eigen::MatrixXi T1, Eigen::MatrixXd TC1, 8 | Eigen::MatrixXd V2, Eigen::MatrixXi T2, Eigen::MatrixXd TC2, 9 | Eigen::MatrixXd & OV, Eigen::MatrixXi & OT, Eigen::MatrixXd &OTC); 10 | 11 | 12 | #endif // !MESH_MATRIX_MERGER_H 13 | -------------------------------------------------------------------------------- /src/Utilities/MeshSlicer.cpp: -------------------------------------------------------------------------------- 1 | #include "MeshSlicer.h" 2 | #include 3 | #include 4 | 5 | MeshSlicer::MeshSlicer( 6 | SurfaceMesh &mesh 7 | ): mesh_(mesh) 8 | { 9 | 10 | mesh_.add_property(wedge_); 11 | mesh_.add_property(on_cut_); 12 | mesh_.add_property(split_to_); 13 | mesh_.add_property(convert_to_); 14 | 15 | } 16 | 17 | void MeshSlicer::ResetFlags() 18 | { 19 | for (auto eiter = mesh_.edges_begin(); eiter != mesh_.edges_end(); ++eiter) { 20 | mesh_.property(on_cut_, *eiter) = false; 21 | } 22 | } 23 | 24 | void MeshSlicer::SliceMeshToDisk(SurfaceMesh &slice_mesh) 25 | { 26 | int euler = mesh_.n_vertices() - mesh_.n_edges() + mesh_.n_faces(); 27 | if (euler == 2) 28 | FindAndMarkCutGraphSphere(); 29 | else 30 | FindAndMarkCutGraphNonSphere(); 31 | ConstructWedge(); 32 | SliceAccordingToWedge(slice_mesh); 33 | } 34 | 35 | std::vector MeshSlicer::SplitTo(OpenMesh::VertexHandle v) 36 | { 37 | return mesh_.property(split_to_,v); 38 | } 39 | 40 | OpenMesh::HalfedgeHandle MeshSlicer::ConvertTo(OpenMesh::HalfedgeHandle h) 41 | { 42 | return mesh_.property(convert_to_, h); 43 | } 44 | 45 | std::vector MeshSlicer::GetLongestPath() 46 | { 47 | return longest_path_; 48 | } 49 | 50 | void MeshSlicer::FindAndMarkCutGraphSphere() 51 | { 52 | using namespace OpenMesh; 53 | base_point_ = *(mesh_.vertices_begin()); 54 | OpenMesh::VPropHandleT dist; 55 | OpenMesh::VPropHandleT parent; 56 | DijkstraShortestDist(mesh_, base_point_, dist, parent); 57 | 58 | // Find the biggest dist 59 | double max_dist = 0; 60 | VertexHandle end; 61 | for (auto viter = mesh_.vertices_begin(); viter != mesh_.vertices_end(); ++viter) { 62 | VertexHandle v = *viter; 63 | if (mesh_.property(dist, v) > max_dist) { 64 | max_dist = mesh_.property(dist, v); 65 | end = v; 66 | } 67 | } 68 | 69 | std::vector longest_path_vertices; 70 | 71 | VertexHandle cptr = end; 72 | longest_path_vertices.push_back(end); 73 | while (mesh_.property(parent, cptr).is_valid()) { 74 | cptr = mesh_.property(parent, cptr); 75 | longest_path_vertices.push_back(cptr); 76 | } 77 | 78 | std::reverse(longest_path_vertices.begin(), longest_path_vertices.end()); 79 | 80 | longest_path_ = longest_path_vertices; 81 | for (auto eiter = mesh_.edges_begin(); eiter != mesh_.edges_end(); ++eiter) { 82 | OpenMesh::EdgeHandle e = *eiter; 83 | mesh_.property(on_cut_, e) = false; 84 | } 85 | for (int i = 0; i < longest_path_vertices.size() - 1; ++i) { 86 | EdgeHandle e = mesh_.edge_handle(mesh_.find_halfedge(longest_path_vertices[i], longest_path_vertices[i+1])); 87 | mesh_.property(on_cut_, e) = true; 88 | } 89 | } 90 | 91 | void MeshSlicer::FindAndMarkCutGraphNonSphere() 92 | { 93 | 94 | } 95 | 96 | void MeshSlicer::ConstructWedge() 97 | { 98 | using namespace OpenMesh; 99 | for (auto hiter = mesh_.halfedges_begin(); hiter != mesh_.halfedges_end(); ++hiter) { 100 | OpenMesh::HalfedgeHandle h = *hiter; 101 | mesh_.property(wedge_, h) = 0; 102 | } 103 | for (SurfaceMesh::VertexIter viter = mesh_.vertices_begin(); viter != mesh_.vertices_end(); ++viter) { 104 | VertexHandle v = *viter; 105 | std::list halfedges_around; 106 | for (SurfaceMesh::VertexIHalfedgeCWIter vihiter = mesh_.vih_cwiter(v); vihiter.is_valid(); ++vihiter) { 107 | HalfedgeHandle h = *vihiter; 108 | if (mesh_.is_boundary(h)) continue; 109 | halfedges_around.push_back(h); 110 | } 111 | for (std::list::iterator it = halfedges_around.begin(); it != halfedges_around.end(); ++it) { 112 | HalfedgeHandle h = *it; 113 | if (mesh_.is_boundary(v)) { 114 | if (mesh_.is_boundary(mesh_.opposite_halfedge_handle(h))) { 115 | halfedges_around.insert(halfedges_around.end(), halfedges_around.begin(), it); 116 | halfedges_around.erase(halfedges_around.begin(), it); 117 | break; 118 | } 119 | } 120 | else { 121 | if (mesh_.property(on_cut_,mesh_.edge_handle(h))) { 122 | halfedges_around.insert(halfedges_around.end(), halfedges_around.begin(), it); 123 | halfedges_around.erase(halfedges_around.begin(), it); 124 | break; 125 | } 126 | } 127 | } 128 | 129 | /*construct wedge around vertex v*/ 130 | int w = 0; 131 | std::list::iterator it = halfedges_around.begin(); 132 | mesh_.property(wedge_, *it) = 0; 133 | ++it; 134 | for (; it != halfedges_around.end(); ++it) { 135 | HalfedgeHandle h = *it; 136 | if (mesh_.property(on_cut_, mesh_.edge_handle(h)) 137 | || mesh_.is_boundary(mesh_.edge_handle(h))) { 138 | w++; 139 | } 140 | mesh_.property(wedge_, h) = w; 141 | } 142 | } 143 | } 144 | 145 | void MeshSlicer::SliceAccordingToWedge(SurfaceMesh &new_mesh) 146 | { 147 | using namespace OpenMesh; 148 | HPropHandleT new_end; // store the new end of an halfedge after being cutted. 149 | mesh_.add_property(new_end); 150 | 151 | HPropHandleT halfedge_split_to; 152 | mesh_.add_property(halfedge_split_to); 153 | 154 | int max_vid = mesh_.n_vertices() - 1; 155 | 156 | for (SurfaceMesh::VertexIter viter = mesh_.vertices_begin(); viter != mesh_.vertices_end(); ++viter) { 157 | VertexHandle v = *viter; 158 | // triverse around in-halfedges, add new vertices if needed and bind halfedges to their new ends. 159 | std::map whether_new_vert_exists; 160 | for (SurfaceMesh::VertexIHalfedgeIter vihiter = mesh_.vih_iter(v); vihiter.is_valid(); ++vihiter) { 161 | HalfedgeHandle h = *vihiter; 162 | int wedge = mesh_.property(wedge_, h); 163 | if (whether_new_vert_exists[wedge].is_valid()) 164 | mesh_.property(new_end, h) = whether_new_vert_exists[wedge]; 165 | else { 166 | VertexHandle new_vertex = new_mesh.add_vertex(mesh_.point(v)); 167 | mesh_.property(split_to_, v).push_back(new_vertex); 168 | mesh_.property(new_end, h) = new_vertex; 169 | whether_new_vert_exists[wedge] = new_vertex; 170 | } 171 | } 172 | } 173 | 174 | /*construct face*/ 175 | for (SurfaceMesh::FaceIter fiter = mesh_.faces_begin(); fiter != mesh_.faces_end(); ++fiter) { 176 | FaceHandle f = *fiter; 177 | std::vector verts; 178 | std::vector old_verts; 179 | for (SurfaceMesh::FaceHalfedgeIter fhiter = mesh_.fh_iter(f); fhiter.is_valid(); ++fhiter) { 180 | HalfedgeHandle h = *fhiter; 181 | VertexHandle v = mesh_.to_vertex_handle(h); 182 | old_verts.push_back(v); 183 | VertexHandle nv = mesh_.property(new_end, h); 184 | verts.push_back(nv); 185 | } 186 | FaceHandle new_f = new_mesh.add_face(verts); 187 | for (int i = 0; i < 3; ++i) { 188 | HalfedgeHandle he = new_mesh.find_halfedge(verts[i], verts[(i + 1) % 3]); 189 | HalfedgeHandle ohe = mesh_.find_halfedge(old_verts[i], old_verts[(i + 1) % 3]); 190 | mesh_.property(convert_to_, ohe) = he; 191 | EdgeHandle e = new_mesh.edge_handle(he); 192 | EdgeHandle oe = mesh_.edge_handle(ohe); 193 | } 194 | } 195 | 196 | new_mesh.RequestBoundary(); 197 | 198 | 199 | //for (auto eiter = mesh_.edges_begin(); eiter != mesh_.edges_end(); ++eiter) { 200 | // EdgeHandle e = *eiter; 201 | // HalfedgeHandle h0 = mesh_.halfedge_handle(e, 0); 202 | // HalfedgeHandle h1 = mesh_.halfedge_handle(e, 1); 203 | // HalfedgeHandle new_h0 = mesh_.property(halfedge_split_to, h0); 204 | // HalfedgeHandle new_h1 = mesh_.property(halfedge_split_to, h1); 205 | // if (mesh_.is_boundary(e)) { 206 | // assert(new_h0.is_valid()); 207 | // assert(new_h1.is_valid()); 208 | // } 209 | // //new_mesh.data(new_h0).set_original_opposition(new_h1); 210 | // //new_mesh.data(new_h1).set_original_opposition(new_h0); 211 | //} 212 | } -------------------------------------------------------------------------------- /src/Utilities/MeshSlicer.h: -------------------------------------------------------------------------------- 1 | #ifndef MESH_SLICER 2 | #define MESH_SLICER 3 | 4 | #include "MeshDefinition.h" 5 | #include "Dijkstra.h" 6 | 7 | // This class is modified from my previous research codes. 8 | // The algorithm is in Gu's book Computational Conformal Geometry. 9 | class MeshSlicer { 10 | public: 11 | MeshSlicer(SurfaceMesh &mesh); 12 | void ResetFlags(); 13 | void SliceMeshToDisk(SurfaceMesh &sliced_mesh); 14 | std::vector SplitTo(OpenMesh::VertexHandle v); 15 | OpenMesh::HalfedgeHandle ConvertTo(OpenMesh::HalfedgeHandle h); 16 | std::vector GetLongestPath(); 17 | void FindAndMarkCutGraphSphere(); 18 | void FindAndMarkCutGraphNonSphere(); 19 | void ConstructWedge(); 20 | void SliceAccordingToWedge(SurfaceMesh &sliced_mesh); 21 | void AddOnCutEdge(OpenMesh::EdgeHandle e) { mesh_.property(on_cut_, e) = true; } 22 | 23 | OpenMesh::VPropHandleT> split_to() { return split_to_; } 24 | protected: 25 | 26 | SurfaceMesh &mesh_; 27 | OpenMesh::VertexHandle base_point_; 28 | OpenMesh::HPropHandleT wedge_; 29 | OpenMesh::EPropHandleT on_cut_; 30 | OpenMesh::VPropHandleT> split_to_; 31 | // one halfedge on the original mesh will appear once and only once on sliced mesh; 32 | OpenMesh::HPropHandleT convert_to_; 33 | std::vector longest_path_; 34 | OpenMesh::HPropHandleT original_reflection_; 35 | 36 | }; 37 | 38 | 39 | #endif // !MESH_SLICER 40 | -------------------------------------------------------------------------------- /src/Utilities/PointSphere.cpp: -------------------------------------------------------------------------------- 1 | #include "PointSphere.h" 2 | 3 | bool PointSpheres(const Eigen::MatrixXd & points, const double & radius, const Eigen::MatrixXd & colors, const int res, const bool colorPerVertex, Eigen::MatrixXd & V, Eigen::MatrixXi & T, Eigen::MatrixXd & TC) 4 | { 5 | using namespace Eigen; 6 | V.resize(res*res*points.rows(), 3); 7 | T.resize(2 * (res - 1)*res*points.rows(), 3); 8 | TC.resize((colorPerVertex ? V.rows() : T.rows()), 3); 9 | 10 | for (int i = 0; i < points.rows(); i++) { 11 | RowVector3d center = points.row(i); 12 | 13 | //creating vertices 14 | for (int j = 0; j < res; j++) { 15 | double z = center(2) + radius*cos(M_PI*(double)j / (double(res - 1))); 16 | for (int k = 0; k < res; k++) { 17 | double x = center(0) + radius*sin(M_PI*(double)j / (double(res - 1)))*cos(2 * M_PI*(double)k / (double(res - 1))); 18 | double y = center(1) + radius*sin(M_PI*(double)j / (double(res - 1)))*sin(2 * M_PI*(double)k / (double(res - 1))); 19 | V.row((res*res)*i + j*res + k) << x, y, z; 20 | if (colorPerVertex) 21 | TC.row((res*res)*i + j*res + k) << colors.row(i); 22 | } 23 | } 24 | 25 | 26 | //creating faces 27 | for (int j = 0; j < res - 1; j++) { 28 | for (int k = 0; k < res; k++) { 29 | int v1 = (res*res)*i + j*res + k; 30 | int v2 = (res*res)*i + (j + 1)*res + k; 31 | int v3 = (res*res)*i + (j + 1)*res + (k + 1) % res; 32 | int v4 = (res*res)*i + j*res + (k + 1) % res; 33 | T.row(2 * (((res - 1)*res)*i + res*j + k)) << v1, v2, v3; 34 | T.row(2 * (((res - 1)*res)*i + res*j + k) + 1) << v4, v1, v3; 35 | if (!colorPerVertex) { 36 | TC.row(2 * (((res - 1)*res)*i + res*j + k)) << colors.row(i); 37 | TC.row(2 * (((res - 1)*res)*i + res*j + k) + 1) << colors.row(i); 38 | } 39 | } 40 | } 41 | } 42 | 43 | return true; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/Utilities/PointSphere.h: -------------------------------------------------------------------------------- 1 | #ifndef POINTSPHERE_H 2 | #define POINTSPHERE_H 3 | 4 | #include 5 | 6 | // This function is copied from libhedra library: 7 | // https://github.com/avaxman/libhedra/blob/master/include/hedra/point_spheres.h 8 | bool PointSpheres(const Eigen::MatrixXd& points, 9 | const double& radius, 10 | const Eigen::MatrixXd& colors, 11 | const int res, 12 | const bool colorPerVertex, 13 | Eigen::MatrixXd& V, 14 | Eigen::MatrixXi& T, 15 | Eigen::MatrixXd& TC); 16 | 17 | 18 | #endif // !POINTSPHERE 19 | -------------------------------------------------------------------------------- /src/Utilities/StringParser.cpp: -------------------------------------------------------------------------------- 1 | #include "StringParser.h" 2 | 3 | void Split(std::string delimiter, std::string s, std::vector &result) 4 | { 5 | result.clear(); 6 | size_t pos = 0; 7 | std::string token; 8 | while ((pos = s.find(delimiter)) != std::string::npos) { 9 | token = s.substr(0, pos); 10 | if (token.length() > 0) 11 | result.push_back(token); 12 | s.erase(0, pos + delimiter.length()); 13 | } 14 | if (s.length() > 0) 15 | result.push_back(s); 16 | } 17 | -------------------------------------------------------------------------------- /src/Utilities/StringParser.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_PARSER_H 2 | #define STRING_PARSER_H 3 | 4 | #include 5 | #include 6 | 7 | void Split(std::string delimiter, std::string s, std::vector &result); 8 | 9 | #endif // !PARSER_J 10 | -------------------------------------------------------------------------------- /src/Viewer/GUIViewer.cpp: -------------------------------------------------------------------------------- 1 | #include "GUIViewer.h" 2 | 3 | OTEViewer::OTEViewer() 4 | { 5 | } 6 | 7 | void OTEViewer::Init() 8 | { 9 | InitMenu(); 10 | InitKeyboard(); 11 | InitMouse(); 12 | } 13 | 14 | 15 | void OTEViewer::InitMenu() 16 | { 17 | static igl::opengl::glfw::imgui::ImGuiMenu menu; 18 | plugins.push_back(&menu); 19 | 20 | menu.callback_draw_viewer_menu = [&]() { 21 | if (ImGui::CollapsingHeader("MeshIO", ImGuiTreeNodeFlags_DefaultOpen)) 22 | { 23 | if (ImGui::Button("Load Mesh", ImVec2(-1, 0))) 24 | { 25 | LoadMesh(); 26 | } 27 | 28 | if (ImGui::Button("Load Texture", ImVec2(-1, 0))) 29 | { 30 | LoadTexture(); 31 | } 32 | 33 | if (ImGui::Button("Save Mesh", ImVec2(-1, 0))) 34 | { 35 | SaveMesh(); 36 | } 37 | } 38 | 39 | if (ImGui::CollapsingHeader("Cutting System", ImGuiTreeNodeFlags_DefaultOpen)) 40 | { 41 | if (ImGui::Button("Load Marker", ImVec2(-1, 0))) 42 | { 43 | LoadMarker(); 44 | } 45 | if (ImGui::Button("Save Marker", ImVec2(-1, 0))) 46 | { 47 | SaveMarker(); 48 | } 49 | ImGui::InputDouble("double", &cone_angle_, 0, 0, "%.4f"); 50 | if (ImGui::Button("Add Cone", ImVec2(-1, 0))) 51 | { 52 | SetSingularity(cone_angle_); 53 | UpdateMeshViewer(); 54 | } 55 | if (ImGui::Button("Add Slice", ImVec2(-1, 0))) 56 | { 57 | SetSlice(); 58 | UpdateMeshViewer(); 59 | } 60 | if (ImGui::Button("Reset Marker", ImVec2(-1, 0))) 61 | { 62 | marker_.ResetMarker(); 63 | selected_verts_.clear(); 64 | UpdateMeshViewer(); 65 | } 66 | } 67 | 68 | if (ImGui::CollapsingHeader("Core Functions", ImGuiTreeNodeFlags_DefaultOpen)) 69 | { 70 | if (ImGui::Button("Euclidean Orbifold", ImVec2(-1, 0))) 71 | { 72 | EuclideanOrbifoldSolver solver(this->mesh_, marker_.GetSingularityFlag(), marker_.GetConeAngleFlag(), marker_.GetSliceFlag()); 73 | this->sliced_mesh_ = solver.Compute(); 74 | this->cone_vts_ = solver.ConeVertices(); 75 | hyperbolic_ = false; 76 | euclidean_ = true; 77 | } 78 | 79 | if (ImGui::Button("Hyperbolic Orbifold", ImVec2(-1, 0))) 80 | { 81 | HyperbolicOrbifoldSolver solver(this->mesh_, marker_.GetSingularityFlag(), marker_.GetSliceFlag()); 82 | this->sliced_mesh_ = solver.Compute(); 83 | this->cone_vts_ = solver.ConeVertices(); 84 | euclidean_ = false; 85 | hyperbolic_ = true; 86 | } 87 | if (ImGui::Button("Hilbert BFF With K", ImVec2(-1, 0))) 88 | { 89 | BFFSolver solver(this->mesh_, marker_.GetSingularityFlag(), marker_.GetConeAngleFlag(), marker_.GetSliceFlag()); 90 | this->sliced_mesh_ = solver.Compute(0); 91 | this->cone_vts_ = solver.ConeVertices(); 92 | euclidean_ = false; 93 | hyperbolic_ = false; 94 | } 95 | if (ImGui::Button("Harmonic BFF With K", ImVec2(-1, 0))) 96 | { 97 | BFFSolver solver(this->mesh_, marker_.GetSingularityFlag(), marker_.GetConeAngleFlag(), marker_.GetSliceFlag()); 98 | this->sliced_mesh_ = solver.Compute(1); 99 | this->cone_vts_ = solver.ConeVertices(); 100 | euclidean_ = false; 101 | hyperbolic_ = false; 102 | } 103 | if (ImGui::Button("Hilbert BFF With Free B", ImVec2(-1, 0))) 104 | { 105 | BFFSolver solver(this->mesh_, marker_.GetSingularityFlag(), marker_.GetConeAngleFlag(), marker_.GetSliceFlag()); 106 | this->sliced_mesh_ = solver.Compute(2); 107 | this->cone_vts_ = solver.ConeVertices(); 108 | euclidean_ = false; 109 | hyperbolic_ = false; 110 | } 111 | if (ImGui::Button("Harmonic BFF With Free B", ImVec2(-1, 0))){ 112 | BFFSolver solver(this->mesh_, marker_.GetSingularityFlag(), marker_.GetConeAngleFlag(), marker_.GetSliceFlag()); 113 | this->sliced_mesh_ = solver.Compute(3); 114 | this->cone_vts_ = solver.ConeVertices(); 115 | euclidean_ = false; 116 | hyperbolic_ = false; 117 | } 118 | if (ImGui::Button("Harmonic BFF With Cones", ImVec2(-1, 0))) { 119 | BFFSolver solver(this->mesh_, marker_.GetSingularityFlag(), marker_.GetConeAngleFlag(), marker_.GetSliceFlag()); 120 | this->sliced_mesh_ = solver.Compute(5); 121 | this->cone_vts_ = solver.ConeVertices(); 122 | euclidean_ = false; 123 | hyperbolic_ = false; 124 | } 125 | if (ImGui::Button("Hilbert BFF With Cones", ImVec2(-1, 0))) { 126 | BFFSolver solver(this->mesh_, marker_.GetSingularityFlag(), marker_.GetConeAngleFlag(), marker_.GetSliceFlag()); 127 | this->sliced_mesh_ = solver.Compute(4); 128 | this->cone_vts_ = solver.ConeVertices(); 129 | euclidean_ = false; 130 | hyperbolic_ = false; 131 | } 132 | } 133 | 134 | if (ImGui::CollapsingHeader("Viewer Options", ImGuiTreeNodeFlags_DefaultOpen)) 135 | { 136 | if (ImGui::Combo("Show Option", (int *)(&show_option_), "Original\0Sliced\0Embedding\0Embedding\0Covering Space\0\0")) 137 | { 138 | UpdateMeshViewer(); 139 | } 140 | if (ImGui::Checkbox("Show Boundaries", &show_boundaries_)) 141 | { 142 | UpdateMeshViewer(); 143 | } 144 | 145 | if (ImGui::Checkbox("Show Slices and Cones", &show_slice_)) 146 | { 147 | UpdateMeshViewer(); 148 | } 149 | 150 | if (ImGui::Checkbox("Show Vertex Labels", &show_vertex_labels_)) 151 | { 152 | UpdateMeshViewer(); 153 | } 154 | } 155 | }; 156 | } 157 | 158 | void OTEViewer::InitKeyboard() 159 | { 160 | callback_key_down = [this](igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier) 161 | { 162 | if (key == 'S') { 163 | selection_mode_ = true; 164 | } 165 | return false; 166 | }; 167 | callback_key_up = [this](igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier) 168 | { 169 | if (key == 'S') { 170 | selection_mode_ = false; 171 | } 172 | return false; 173 | }; 174 | } 175 | 176 | void OTEViewer::InitMouse() 177 | { 178 | callback_mouse_down = [this](igl::opengl::glfw::Viewer& viewer, int button, int modifier)->bool 179 | { 180 | if ( selection_mode_ && show_option_ == ORIGINAL && button == GLFW_MOUSE_BUTTON_1) { 181 | Eigen::Vector4f viewport = viewer.core().viewport; 182 | Eigen::Vector2f center(viewport(2) / 2., viewport(3)/2.); 183 | double x = viewer.down_mouse_x; 184 | double y = viewer.down_mouse_y; 185 | x = (viewer.down_mouse_x - center(0)) / (viewport(2) / 2.); 186 | y = -(viewer.down_mouse_y - center(1)) / (viewport(3) / 2.); 187 | this->FindIntersection(x, y); 188 | this->UpdateMeshViewer(); 189 | } 190 | 191 | return false; 192 | }; 193 | } 194 | 195 | void OTEViewer::LoadMesh() 196 | { 197 | std::string fname = igl::file_dialog_open(); 198 | if (fname.length() == 0) 199 | return; 200 | OpenMesh::IO::Options opt; 201 | opt += OpenMesh::IO::Options::VertexTexCoord; 202 | OpenMesh::IO::read_mesh(mesh_, fname, opt); 203 | NormalizeMesh(mesh_); 204 | UpdateMeshData(mesh_); 205 | UpdateTextureCoordData(mesh_); 206 | marker_.SetObject(mesh_); 207 | marker_.ResetMarker(); 208 | } 209 | 210 | void OTEViewer::LoadTexture() 211 | { 212 | std::string fname = igl::file_dialog_open(); 213 | if (fname.length() == 0) 214 | return; 215 | igl::png::texture_from_png(fname, R_, G_, B_, A_); 216 | data().set_texture(R_, G_, B_, A_); 217 | UpdateMeshViewer(); 218 | } 219 | 220 | void OTEViewer::SaveMesh() 221 | { 222 | std::string fname = igl::file_dialog_save(); 223 | if (fname.length() == 0) 224 | return; 225 | OpenMesh::IO::Options opt; 226 | opt += OpenMesh::IO::Options::VertexTexCoord; 227 | 228 | if (show_option_ == ORIGINAL) { 229 | OpenMesh::IO::write_mesh(mesh_, fname, opt); 230 | } 231 | 232 | else if (show_option_ == SLICED) { 233 | OpenMesh::IO::write_mesh(sliced_mesh_, fname, opt); 234 | } 235 | 236 | 237 | 238 | } 239 | 240 | void OTEViewer::UpdateMeshData(SurfaceMesh &mesh) 241 | { 242 | OpenMeshToMatrix(mesh, V_,V_normal_, F_, F_normal_); 243 | TC_.resize(F_.rows(), 3); 244 | TC_.col(0) = Eigen::VectorXd::Constant(TC_.rows(), 1.0); 245 | TC_.col(1) = Eigen::VectorXd::Constant(TC_.rows(), 1.0); 246 | TC_.col(2) = Eigen::VectorXd::Constant(TC_.rows(), 1.0); 247 | if (V_.rows() == 0) return; 248 | data().clear(); 249 | data().set_mesh(V_, F_); 250 | data().set_colors(TC_); 251 | data().set_normals(V_normal_); 252 | data().set_normals(F_normal_); 253 | } 254 | 255 | 256 | void OTEViewer::UpdateTextureCoordData(SurfaceMesh &mesh) 257 | { 258 | OpenMeshCoordToMatrix(mesh, UV_); 259 | data().set_uv(UV_); 260 | if (R_.rows() > 0) 261 | data().set_texture(R_, G_, B_); 262 | } 263 | 264 | 265 | void OTEViewer::ShowUV() 266 | { 267 | if (UV_.rows() == 0) return; 268 | UV_Z0_.resize(UV_.rows(), 3); 269 | UV_Z0_.setZero(); 270 | UV_Z0_.block(0, 0, UV_.rows(), UV_.cols()) = UV_; 271 | data().clear(); 272 | data().set_mesh(UV_Z0_, F_); 273 | data().set_uv(UV_); 274 | data().set_colors(TC_); 275 | data().set_texture(R_, G_, B_); 276 | 277 | Eigen::MatrixXd V_normal = V_normal_; 278 | V_normal.col(2) = (V_normal.col(2)).cwiseAbs(); 279 | Eigen::MatrixXd F_normal = F_normal_; 280 | F_normal.col(2) = F_normal.col(2).cwiseAbs(); 281 | data().set_normals(V_normal); 282 | data().set_normals(F_normal); 283 | } 284 | 285 | void OTEViewer::ShowCoveringSpace() 286 | { 287 | if (euclidean_) { 288 | EuclideanCoveringSpaceComputer covering_computer(sliced_mesh_, cone_vts_); 289 | covering_computer.Compute(); 290 | covering_computer.GenerateMeshMatrix(V_, V_normal_, F_, F_normal_); 291 | data().clear(); 292 | data().set_mesh(V_, F_); 293 | V_normal_.col(2) = (V_normal_.col(2)).cwiseAbs(); 294 | F_normal_.col(2) = (F_normal_.col(2)).cwiseAbs(); 295 | data().set_normals(V_normal_); 296 | data().set_normals(F_normal_); 297 | data().set_uv(V_.block(0, 0, V_.rows(),2)); 298 | } 299 | 300 | if (hyperbolic_) { 301 | HyperbolicCoveringSpaceComputer covering_computer(sliced_mesh_, cone_vts_); 302 | covering_computer.Compute(); 303 | covering_computer.GenerateMeshMatrix(V_, V_normal_, F_, F_normal_); 304 | data().clear(); 305 | data().set_mesh(V_, F_); 306 | V_normal_.col(2) = (V_normal_.col(2)).cwiseAbs(); 307 | F_normal_.col(2) = (F_normal_.col(2)).cwiseAbs(); 308 | data().set_normals(V_normal_); 309 | data().set_normals(F_normal_); 310 | data().set_uv(V_.block(0, 0, V_.rows(), 2)); 311 | } 312 | 313 | } 314 | 315 | void OTEViewer::ShowHalfedges(SurfaceMesh &mesh, std::vector h_vector) 316 | { 317 | // This algorithm to generate cylinder is from the library libhedra 318 | using namespace OpenMesh; 319 | 320 | if (h_vector.size() == 0) return; 321 | 322 | Eigen::MatrixXd lineV, lineTC; 323 | Eigen::MatrixXi lineT; 324 | Eigen::MatrixXd P1, P2; 325 | Eigen::MatrixXd lineColors; 326 | double radius = 0.005; 327 | 328 | HalfedgesToMatrix(mesh, h_vector, P1, P2); 329 | lineColors.resize(P1.rows(), 3); 330 | lineColors.setZero(); 331 | lineColors.col(0) = Eigen::VectorXd::Constant(P1.rows(), 1); 332 | LineCylinders( 333 | P1, 334 | P2, 335 | radius, lineColors, 336 | 10, 337 | false, 338 | lineV, 339 | lineT, 340 | lineTC); 341 | 342 | MergeMeshMatrix(V_, F_, TC_, lineV, lineT, lineTC, V_, F_, TC_); 343 | 344 | 345 | data().clear(); 346 | data().set_mesh(V_, F_); 347 | data().set_colors(TC_); 348 | } 349 | 350 | void OTEViewer::ShowBoundaries(SurfaceMesh &mesh) 351 | { 352 | mesh.RequestBoundary(); 353 | auto boundaries = mesh.GetBoundaries(); 354 | ShowHalfedges(mesh,boundaries.front()); 355 | 356 | } 357 | 358 | void OTEViewer::ShowSliceAndCones() 359 | { 360 | 361 | // For edges; 362 | Eigen::MatrixXd lineV, lineTC; 363 | Eigen::MatrixXi lineT; 364 | Eigen::MatrixXd P1, P2; 365 | Eigen::MatrixXd lineColors; 366 | 367 | // For vertices; 368 | Eigen::MatrixXd P; 369 | Eigen::MatrixXi T; 370 | Eigen::MatrixXd V, TC; 371 | Eigen::MatrixXd vertexColors; 372 | 373 | 374 | marker_.GenerateMatrix(P, P1, P2); 375 | 376 | if (P1.rows() > 0) { 377 | 378 | lineColors.resize(P1.rows(), 3); 379 | lineColors.setZero(); 380 | lineColors.col(0) = Eigen::VectorXd::Constant(P1.rows(), 1); 381 | 382 | 383 | LineCylinders( 384 | P1, 385 | P2, 386 | 0.005, lineColors, 387 | 10, 388 | false, 389 | lineV, 390 | lineT, 391 | lineTC); 392 | MergeMeshMatrix(V_, F_, TC_, lineV, lineT, lineTC, V_, F_, TC_); 393 | } 394 | 395 | if (P.rows() > 0) { 396 | 397 | vertexColors.resize(P.rows(), 3); 398 | vertexColors.setZero(); 399 | vertexColors.col(1) = Eigen::VectorXd::Constant(P.rows(), 1); 400 | 401 | 402 | PointSpheres(P, 403 | 0.03, 404 | vertexColors, 405 | 10, 406 | false, 407 | V, 408 | T, 409 | TC); 410 | MergeMeshMatrix(V_, F_, TC_, V, T, TC, V_, F_, TC_); 411 | } 412 | //show 413 | data().clear(); 414 | data().set_mesh(V_, F_); 415 | data().set_colors(TC_); 416 | } 417 | 418 | void OTEViewer::ShowVertexLabels() 419 | { 420 | 421 | using namespace OpenMesh; 422 | if (mesh_.n_edges() == 0) return; 423 | for (SurfaceMesh::VertexIter viter = mesh_.vertices_begin(); viter != mesh_.vertices_end(); ++viter) { 424 | VertexHandle v = *viter; 425 | Vec3d p = mesh_.point(v); 426 | data().add_label(Eigen::Vector3d(p[0], p[1], p[2]), std::to_string(v.idx())); 427 | } 428 | 429 | } 430 | 431 | 432 | 433 | void OTEViewer::UpdateMeshViewer() 434 | { 435 | if (show_option_ == ORIGINAL) { 436 | UpdateMeshData(mesh_); 437 | UpdateTextureCoordData(mesh_); 438 | if(show_slice_) 439 | ShowSliceAndCones(); 440 | if (show_vertex_labels_) 441 | ShowVertexLabels(); 442 | ShowSelction(); 443 | } 444 | else if (show_option_ == SLICED) { 445 | UpdateMeshData(sliced_mesh_); 446 | UpdateTextureCoordData(sliced_mesh_); 447 | if (show_boundaries_) { 448 | ShowBoundaries(sliced_mesh_); 449 | } 450 | } 451 | else if (show_option_ == EMBEDDING) { 452 | UpdateMeshData(sliced_mesh_); 453 | UpdateTextureCoordData(sliced_mesh_); 454 | ShowUV(); 455 | } 456 | 457 | else if (show_option_ == COVERING_SPACE) { 458 | ShowCoveringSpace(); 459 | } 460 | 461 | } 462 | 463 | void OTEViewer::SetSlice() 464 | { 465 | using namespace OpenMesh; 466 | VertexHandle v1 = selected_verts_.front(); 467 | VertexHandle v2 = selected_verts_.back(); 468 | marker_.ComputeAndSetSlice(v1, v2); 469 | selected_verts_.clear(); 470 | } 471 | 472 | void OTEViewer::SetSingularity(double cone_angle) 473 | { 474 | using namespace OpenMesh; 475 | SurfaceMesh &mesh = mesh_; 476 | VertexHandle v = selected_verts_.front(); 477 | marker_.SetSingularity(v, cone_angle); 478 | selected_verts_.clear(); 479 | } 480 | 481 | void OTEViewer::LoadMarker() 482 | { 483 | std::string fname = igl::file_dialog_open(); 484 | marker_.LoadFromFile(fname); 485 | } 486 | 487 | void OTEViewer::SaveMarker() 488 | { 489 | std::string fname = igl::file_dialog_save(); 490 | marker_.SaveToFile(fname); 491 | } 492 | 493 | void OTEViewer::FindIntersection(double x, double y) 494 | { 495 | using namespace OpenMesh; 496 | using namespace Eigen; 497 | SurfaceMesh &mesh = mesh_; 498 | 499 | Eigen::Matrix4f view = core().view; 500 | Eigen::Matrix4f proj = core().proj; 501 | 502 | Eigen::Matrix4f world = view; 503 | 504 | double min_dist = 10000; 505 | VertexHandle nearest_vertex; 506 | for (auto viter = mesh.vertices_begin(); viter != mesh.vertices_end(); ++viter) { 507 | VertexHandle v = *viter; 508 | Vec3d p = mesh.point(v); 509 | Vec3d n = mesh.normal(v); 510 | Eigen::Vector4f p_h(p[0], p[1], p[2], 1.); 511 | Eigen::Vector4f n_h(n[0], n[1], n[2], 0.); 512 | 513 | Eigen::Vector4f current_n = world * n_h; 514 | Eigen::Vector4f current_p = world * p_h; 515 | Eigen::Vector4f eye_direction(-current_p(0), -current_p(1), 5 - current_p(2), 0); 516 | 517 | if ((current_n.transpose() * eye_direction)(0) < 0) continue; 518 | 519 | Eigen::Vector4f p_proj = proj * current_p; 520 | p_proj /= p_proj(3); 521 | 522 | Vec2f mouse(x, y); 523 | Vec2f pp(p_proj(0), p_proj(1)); 524 | 525 | double dist = (mouse - pp).norm(); 526 | 527 | if (dist < min_dist) { 528 | min_dist = dist; 529 | nearest_vertex = v; 530 | } 531 | 532 | } 533 | bool exist = false; 534 | for (auto it = selected_verts_.begin(); it != selected_verts_.end(); ++it) { 535 | if (nearest_vertex == *it) { 536 | selected_verts_.erase(it); 537 | exist = true; 538 | break; 539 | } 540 | } 541 | if(!exist) 542 | selected_verts_.push_back(nearest_vertex); 543 | } 544 | 545 | void OTEViewer::ShowSelction() 546 | { 547 | using namespace OpenMesh; 548 | // For edges; 549 | Eigen::MatrixXd bigV, bigTC; 550 | Eigen::MatrixXi bigT; 551 | 552 | // For vertices; 553 | Eigen::MatrixXd P; 554 | Eigen::MatrixXi T; 555 | Eigen::MatrixXd V, TC; 556 | Eigen::MatrixXd vertexColors; 557 | 558 | 559 | bigV = V_; 560 | bigT = F_; 561 | bigTC = TC_; 562 | 563 | P.resize(selected_verts_.size(), 3); 564 | 565 | if (P.rows() > 0) { 566 | int i = 0; 567 | for (auto it = selected_verts_.begin(); it != selected_verts_.end(); ++it, ++i) { 568 | VertexHandle v = *it; 569 | Vec3d p = mesh_.point(v); 570 | P.row(i) = Eigen::Vector3d(p[0], p[1], p[2]); 571 | } 572 | 573 | vertexColors.resize(P.rows(), 3); 574 | vertexColors.setZero(); 575 | vertexColors.col(1) = Eigen::VectorXd::Constant(P.rows(), 1); 576 | 577 | 578 | PointSpheres(P, 579 | 0.03, 580 | vertexColors, 581 | 10, 582 | false, 583 | V, 584 | T, 585 | TC); 586 | MergeMeshMatrix(bigV, bigT, bigTC, V, T, TC, bigV, bigT, bigTC); 587 | data().clear(); 588 | data().set_mesh(bigV, bigT); 589 | data().set_colors(bigTC); 590 | } 591 | 592 | 593 | } 594 | -------------------------------------------------------------------------------- /src/Viewer/GUIViewer.h: -------------------------------------------------------------------------------- 1 | #ifndef OTE_VIEWER 2 | #define OTE_VIEWER 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | 30 | enum ShowOption { ORIGINAL, SLICED, EMBEDDING, COVERING_SPACE }; 31 | 32 | class OTEViewer: public igl::opengl::glfw::Viewer 33 | { 34 | public: 35 | OTEViewer(); 36 | void Init(); 37 | private: 38 | SurfaceMesh mesh_; 39 | SurfaceMesh sliced_mesh_; 40 | MeshMarker marker_; 41 | 42 | std::vector cone_vts_; 43 | 44 | Eigen::MatrixXd V_; 45 | Eigen::MatrixXd V_normal_; 46 | Eigen::MatrixXd UV_Z0_; // z equal to zero 47 | Eigen::MatrixXi F_; 48 | Eigen::MatrixXd F_normal_; 49 | Eigen::MatrixXd TC_; 50 | Eigen::MatrixXd UV_; 51 | 52 | 53 | 54 | // texture RGB channels; 55 | Eigen::Matrix R_; 56 | Eigen::Matrix G_; 57 | Eigen::Matrix B_; 58 | Eigen::Matrix A_; 59 | 60 | // Flags 61 | ShowOption show_option_ = ORIGINAL; 62 | bool show_boundaries_ = false; 63 | bool show_slice_ = false; 64 | bool show_vertex_labels_ = false; 65 | bool euclidean_ = false; 66 | bool hyperbolic_ = false; 67 | 68 | double cone_angle_ = 0.; 69 | 70 | bool selection_mode_ = false; 71 | 72 | 73 | std::list selected_verts_; 74 | 75 | 76 | protected: 77 | 78 | // Init functions 79 | void InitMenu(); 80 | void InitKeyboard(); 81 | void InitMouse(); 82 | 83 | // IO functions 84 | void LoadMesh(); 85 | void LoadTexture(); 86 | void SaveMesh(); 87 | 88 | void UpdateMeshData(SurfaceMesh &mesh); 89 | void UpdateTextureCoordData(SurfaceMesh &mesh); 90 | void ShowUV(); 91 | void ShowCoveringSpace(); 92 | 93 | void ShowHalfedges(SurfaceMesh &mesh, std::vector h_vector); 94 | void ShowBoundaries(SurfaceMesh &mesh); 95 | void ShowSliceAndCones(); 96 | void ShowVertexLabels(); 97 | void UpdateMeshViewer(); 98 | 99 | 100 | // Setting slices and singularities 101 | void SetSlice(); 102 | void SetSingularity(double cone_angle); 103 | 104 | void LoadMarker(); 105 | void SaveMarker(); 106 | 107 | // These two functions are used to achieve vertex selection. 108 | void FindIntersection(double x, double y); 109 | void ShowSelction(); 110 | 111 | }; 112 | 113 | #endif -------------------------------------------------------------------------------- /src/Viewer/main.cpp: -------------------------------------------------------------------------------- 1 | #include "GUIViewer.h" 2 | 3 | int main(int argc, char ** argv) 4 | { 5 | OTEViewer viewer; 6 | viewer.Init(); 7 | viewer.launch(); 8 | } --------------------------------------------------------------------------------