├── .clang-format ├── .github └── workflows │ ├── linux.yml │ ├── macOS.yml │ └── windows.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── EigenChecker.cmake ├── FetchContent │ └── CMakeLists.cmake.in ├── FetchContentLocal.cmake ├── FindEigen3.cmake ├── FindSuiteSparse.cmake └── UpdateCacheVariable.cmake ├── deps ├── CMakeLists.txt ├── downloads │ ├── .gitignore │ └── README.md ├── nanoflann │ └── include │ │ └── nanoflann.hpp └── nanort │ └── include │ └── nanort │ └── nanort.h ├── docs ├── README.md ├── docs │ ├── .gitignore │ ├── CNAME │ ├── build │ │ ├── building.md │ │ ├── dependencies.md │ │ ├── tests.md │ │ └── versions.md │ ├── extras.css │ ├── index.md │ ├── media │ │ ├── barycentric_vector.svg │ │ ├── bean_scalar.jpg │ │ ├── bean_vector.jpg │ │ ├── bff.png │ │ ├── common_subdivision.svg │ │ ├── convex_embed.gif │ │ ├── delta_complex_examples.svg │ │ ├── direction_field_basic.jpg │ │ ├── direction_field_boundary.jpg │ │ ├── direction_field_curvature.jpg │ │ ├── embed_convex.jpg │ │ ├── flip_bezier.jpg │ │ ├── flip_cathead_refine.jpg │ │ ├── flip_geodesic.jpg │ │ ├── geodesic-centroidal-voronoi.png │ │ ├── geometry_inheritance.ai │ │ ├── geometry_inheritance.svg │ │ ├── halfedge_boundary_diagram.ai │ │ ├── halfedge_boundary_diagram.svg │ │ ├── halfedge_index_diagram.pdf │ │ ├── halfedge_index_diagram.svg │ │ ├── halfedge_orientation.png │ │ ├── halfedge_pointers.png │ │ ├── int_tri_teaser.jpg │ │ ├── integer_coordinates.pdf │ │ ├── integer_coordinates.png │ │ ├── intersect.png │ │ ├── karcher_mean_simple.jpg │ │ ├── n_direction_fields.png │ │ ├── octopus_logmap.jpg │ │ ├── point_heat_solvers.jpg │ │ ├── point_heat_solvers_updated.png │ │ ├── poisson_disk_sample.png │ │ ├── polygon_heat_solvers.png │ │ ├── self_intersect.png │ │ ├── signed_heat_method.png │ │ ├── signposts.svg │ │ ├── spot-triangulation-original.png │ │ ├── spot-triangulation-remeshed.png │ │ ├── stripes_isolines.png │ │ ├── tutorials │ │ │ ├── dir_field.jpeg │ │ │ ├── subdiv_after.jpeg │ │ │ └── subdiv_before.jpeg │ │ ├── vertex_scalar_curvatures.jpg │ │ ├── vertex_tangent_coordinates.ai │ │ └── vertex_tangent_coordinates.svg │ ├── numerical │ │ ├── linear_algebra_utilities.md │ │ ├── linear_solvers.md │ │ └── matrix_types.md │ ├── pointcloud │ │ ├── algorithms │ │ │ └── heat_solver.md │ │ ├── basics.md │ │ ├── geometry.md │ │ └── utilities │ │ │ ├── io.md │ │ │ └── sampling.md │ ├── surface │ │ ├── algorithms │ │ │ ├── direction_fields.md │ │ │ ├── embedding.md │ │ │ ├── flip_geodesics.md │ │ │ ├── geodesic_distance.md │ │ │ ├── geodesic_paths.md │ │ │ ├── geodesic_voronoi_tessellations.md │ │ │ ├── intersection.md │ │ │ ├── parameterization.md │ │ │ ├── polygon_heat_solver.md │ │ │ ├── remeshing.md │ │ │ ├── robust_geometry.md │ │ │ ├── signed_heat_method.md │ │ │ ├── stripes.md │ │ │ ├── surface_centers.md │ │ │ ├── surface_sampling.md │ │ │ └── vector_heat_method.md │ │ ├── geometry │ │ │ ├── geometry.md │ │ │ └── quantities.md │ │ ├── intrinsic_triangulations │ │ │ ├── basics.md │ │ │ ├── common_subdivision.md │ │ │ ├── function_transfer.md │ │ │ ├── integer_coordinates.md │ │ │ └── signposts.md │ │ ├── surface_mesh │ │ │ ├── basics.md │ │ │ ├── boundaries.md │ │ │ ├── containers.md │ │ │ ├── delta_complex.md │ │ │ ├── elements.md │ │ │ ├── indexing.md │ │ │ ├── internals.md │ │ │ ├── mutation.md │ │ │ └── navigation.md │ │ └── utilities │ │ │ ├── barycentric_vector.md │ │ │ ├── io.md │ │ │ ├── simple_polygon_mesh.md │ │ │ └── surface_point.md │ ├── tutorials │ │ ├── basic_mutation.md │ │ ├── direction_fields.md │ │ └── load_mesh.md │ └── utilities │ │ ├── eigenmap.md │ │ ├── miscellaneous.md │ │ ├── vector2.md │ │ └── vector3.md └── mkdocs.yml ├── include └── geometrycentral │ ├── numerical │ ├── linear_algebra_types.h │ ├── linear_algebra_utilities.h │ ├── linear_algebra_utilities.ipp │ ├── linear_solvers.h │ └── suitesparse_utilities.h │ ├── pointcloud │ ├── local_triangulation.h │ ├── neighborhoods.h │ ├── point_cloud.h │ ├── point_cloud.ipp │ ├── point_cloud_element_types.h │ ├── point_cloud_element_types.ipp │ ├── point_cloud_heat_solver.h │ ├── point_cloud_io.h │ ├── point_cloud_logic_templates.ipp │ ├── point_position_frame_geometry.h │ ├── point_position_geometry.h │ ├── point_position_normal_geometry.h │ └── sample_cloud.h │ ├── surface │ ├── barycentric_coordinate_helpers.h │ ├── barycentric_coordinate_helpers.ipp │ ├── barycentric_vector.h │ ├── barycentric_vector.ipp │ ├── base_geometry_interface.h │ ├── boundary_first_flattening.h │ ├── common_subdivision.h │ ├── common_subdivision.ipp │ ├── detect_symmetry.h │ ├── direction_fields.h │ ├── edge_length_geometry.h │ ├── edge_length_geometry.ipp │ ├── embed_convex.h │ ├── embedded_geometry_interface.h │ ├── exact_geodesic_helpers.h │ ├── exact_geodesic_helpers.ipp │ ├── exact_geodesics.h │ ├── exact_polyhedral_geodesics.h │ ├── extrinsic_geometry_interface.h │ ├── fast_marching_method.h │ ├── flip_geodesics.h │ ├── geodesic_centroidal_voronoi_tessellation.h │ ├── geometry.h │ ├── halfedge_element_types.h │ ├── halfedge_element_types.ipp │ ├── halfedge_factories.h │ ├── halfedge_logic_templates.ipp │ ├── halfedge_mesh.h │ ├── heat_method_distance.h │ ├── integer_coordinates_intrinsic_triangulation.h │ ├── integer_coordinates_intrinsic_triangulation.ipp │ ├── intersection.h │ ├── intrinsic_geometry_interface.h │ ├── intrinsic_mollification.h │ ├── intrinsic_triangulation.h │ ├── intrinsic_triangulation.ipp │ ├── manifold_surface_mesh.h │ ├── manifold_surface_mesh.ipp │ ├── mesh_graph_algorithms.h │ ├── mesh_ray_tracer.h │ ├── meshio.h │ ├── mutation_manager.h │ ├── mutation_manager.ipp │ ├── normal_coordinates.h │ ├── parameterize.h │ ├── poisson_disk_sampler.h │ ├── polygon_mesh_heat_solver.h │ ├── polygon_mesh_helpers.h │ ├── polygon_soup_mesh.h │ ├── quadric_error_simplification.h │ ├── remeshing.h │ ├── rich_surface_mesh_data.h │ ├── rich_surface_mesh_data.ipp │ ├── signed_heat_method.h │ ├── signpost_intrinsic_triangulation.h │ ├── signpost_intrinsic_triangulation.ipp │ ├── simple_idt.h │ ├── simple_polygon_mesh.h │ ├── stripe_patterns.h │ ├── subdivide.h │ ├── surface_centers.h │ ├── surface_mesh.h │ ├── surface_mesh.ipp │ ├── surface_mesh_factories.h │ ├── surface_mesh_factories.ipp │ ├── surface_point.h │ ├── surface_point.ipp │ ├── surgery.h │ ├── trace_geodesic.h │ ├── transfer_functions.h │ ├── tufted_laplacian.h │ ├── uniformize.h │ ├── vector_heat_method.h │ ├── vertex_position_geometry.h │ └── vertex_position_geometry.ipp │ └── utilities │ ├── combining_hash_functions.h │ ├── curve.h │ ├── curve.ipp │ ├── dependent_quantity.h │ ├── dependent_quantity.ipp │ ├── disjoint_sets.h │ ├── eigen_interop_helpers.h │ ├── element.h │ ├── element.ipp │ ├── element_iterators.h │ ├── element_iterators.ipp │ ├── elementary_geometry.h │ ├── elementary_geometry.ipp │ ├── knn.h │ ├── mesh_data.h │ ├── mesh_data.ipp │ ├── quaternion.h │ ├── timing.h │ ├── utilities.h │ ├── vector2.h │ ├── vector2.ipp │ ├── vector3.h │ └── vector3.ipp ├── src ├── CMakeLists.txt ├── numerical │ ├── eigenproblem_solvers.cpp │ ├── linear_algebra_utilities.cpp │ ├── linear_solvers.cpp │ ├── positive_definite_solvers.cpp │ ├── qr_solvers.cpp │ ├── square_solvers.cpp │ └── suitesparse_utilities.cpp ├── pointcloud │ ├── local_triangulation.cpp │ ├── neighborhoods.cpp │ ├── point_cloud.cpp │ ├── point_cloud_heat_solver.cpp │ ├── point_cloud_io.cpp │ ├── point_position_frame_geometry.cpp │ ├── point_position_geometry.cpp │ ├── point_position_normal_geometry.cpp │ └── sample_cloud.cpp ├── surface │ ├── barycentric_vector.cpp │ ├── base_geometry_interface.cpp │ ├── boundary_first_flattening.cpp │ ├── common_subdivision.cpp │ ├── detect_symmetry.cpp │ ├── direction_fields.cpp │ ├── edge_length_geometry.cpp │ ├── embed_convex.cpp │ ├── embedded_geometry_interface.cpp │ ├── exact_geodesic_helpers.cpp │ ├── exact_geodesics.cpp │ ├── exact_polyhedral_geodesics.cpp │ ├── extrinsic_geometry_interface.cpp │ ├── fast_marching_method.cpp │ ├── flip_geodesics.cpp │ ├── geodesic_centroidal_voronoi_tessellation.cpp │ ├── halfedge_factories.cpp │ ├── heat_method_distance.cpp │ ├── integer_coordinates_intrinsic_triangulation.cpp │ ├── intersection.cpp │ ├── intrinsic_geometry_interface.cpp │ ├── intrinsic_mollification.cpp │ ├── intrinsic_triangulation.cpp │ ├── manifold_surface_mesh.cpp │ ├── mesh_graph_algorithms.cpp │ ├── mesh_ray_tracer.cpp │ ├── meshio.cpp │ ├── mutation_manager.cpp │ ├── normal_coordinates.cpp │ ├── parameterize.cpp │ ├── poisson_disk_sampler.cpp │ ├── polygon_mesh_heat_solver.cpp │ ├── polygon_mesh_helpers.cpp │ ├── quadric_error_simplification.cpp │ ├── remeshing.cpp │ ├── rich_surface_mesh_data.cpp │ ├── signed_heat_method.cpp │ ├── signpost_intrinsic_triangulation.cpp │ ├── simple_idt.cpp │ ├── simple_polygon_mesh.cpp │ ├── stripe_patterns.cpp │ ├── subdivide.cpp │ ├── surface_centers.cpp │ ├── surface_mesh.cpp │ ├── surface_mesh_factories.cpp │ ├── surface_point.cpp │ ├── surgery.cpp │ ├── trace_geodesic.cpp │ ├── transfer_functions.cpp │ ├── tufted_laplacian.cpp │ ├── uniformize.cpp │ ├── vector_heat_method.cpp │ └── vertex_position_geometry.cpp └── utilities │ ├── disjoint_sets.cpp │ ├── elementary_geometry.cpp │ ├── knn.cpp │ ├── quaternion.cpp │ ├── tri_tri_intersect.cpp │ ├── unit_vector3.cpp │ └── utilities.cpp └── test ├── .gitignore ├── CMakeLists.txt ├── assets ├── bob_small.ply ├── cat_head.obj ├── dodecahedron_poly.obj ├── fan3.obj ├── fox.ply ├── hourglass_ico.obj ├── lego.ply ├── moebius.obj ├── platonic_shelf.obj ├── sphere_small.ply ├── spot.ply ├── stl_box_ascii.stl ├── stl_box_binary.stl ├── tet.obj └── triple_vierbein.obj ├── include ├── linear_algebra_test_helpers.h └── load_test_meshes.h └── src ├── eigen_interop_helpers_test.cpp ├── halfedge_geometry_test.cpp ├── halfedge_mesh_test.cpp ├── halfedge_mutation_test.cpp ├── intrinsic_triangulation_test.cpp ├── linear_algebra_test.cpp ├── load_test_meshes.cpp ├── main_test.cpp ├── point_cloud_test.cpp ├── poisson_disk_sampler_test.cpp ├── polygon_operators_test.cpp ├── stl_reader_test.cpp └── surface_misc_test.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AlignAfterOpenBracket: Align 3 | AlignOperands: 'true' 4 | AllowShortBlocksOnASingleLine: 'false' 5 | AllowShortIfStatementsOnASingleLine: 'true' 6 | AllowShortLoopsOnASingleLine: 'true' 7 | AlwaysBreakTemplateDeclarations: 'true' 8 | BinPackParameters: 'true' 9 | BreakBeforeBraces: Attach 10 | ColumnLimit: '120' 11 | IndentWidth: '2' 12 | KeepEmptyLinesAtTheStartOfBlocks: 'true' 13 | MaxEmptyLinesToKeep: '2' 14 | PointerAlignment: Left 15 | ReflowComments: 'true' 16 | SpacesInAngles: 'false' 17 | SpacesInParentheses: 'false' 18 | SpacesInSquareBrackets: 'false' 19 | Standard: Cpp11 20 | UseTab: Never 21 | 22 | ... 23 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: linux 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | os: [ubuntu-latest] 14 | runs-on: ${{ matrix.os }} 15 | if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')" 16 | steps: 17 | - uses: actions/checkout@v1 18 | with: 19 | submodules: true 20 | 21 | - name: configure 22 | run: cd test && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. 23 | 24 | - name: build 25 | run: cd test/build && make 26 | 27 | - name: run test 28 | run: cd test/build && ./bin/geometry-central-test 29 | -------------------------------------------------------------------------------- /.github/workflows/macOS.yml: -------------------------------------------------------------------------------- 1 | name: macOS 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: macos-latest 12 | if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')" 13 | steps: 14 | - uses: actions/checkout@v1 15 | with: 16 | submodules: true 17 | 18 | - name: configure 19 | run: cd test && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. 20 | 21 | - name: build 22 | run: cd test/build && make 23 | 24 | - name: run test 25 | run: cd test/build && ./bin/geometry-central-test 26 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: windows 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')" 13 | steps: 14 | - uses: actions/checkout@v1 15 | with: 16 | submodules: true 17 | 18 | - name: configure 19 | run: cd test && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. 20 | 21 | - name: build 22 | run: cd test/build && cmake --build "." 23 | 24 | - name: run test 25 | run: cd test/build && ./bin/Debug/geometry-central-test.exe 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build directories 2 | build/ 3 | build_debug/ 4 | 5 | # Editor and OS things 6 | .DS_Store 7 | .vscode 8 | *.swp 9 | tags 10 | compile_commands.json 11 | .cache 12 | 13 | # Prerequisites 14 | *.d 15 | 16 | # Compiled Object files 17 | *.slo 18 | *.lo 19 | *.o 20 | *.obj 21 | 22 | # Precompiled Headers 23 | *.gch 24 | *.pch 25 | 26 | # Compiled Dynamic libraries 27 | *.so 28 | *.dylib 29 | *.dll 30 | 31 | # Fortran module files 32 | *.mod 33 | *.smod 34 | 35 | # Compiled Static libraries 36 | *.lai 37 | *.la 38 | *.a 39 | *.lib 40 | 41 | # Executables 42 | *.exe 43 | *.out 44 | *.app 45 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/happly"] 2 | path = deps/happly 3 | url = https://github.com/nmwsharp/happly.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14.0) 2 | 3 | project(geometry-central) 4 | 5 | ### Policy settings 6 | cmake_policy(SET CMP0054 NEW) # don't implicitly dereference inside if() 7 | 8 | 9 | ### Process settings 10 | option(BUILD_SHARED_LIBS "Build the shared library" FALSE) 11 | if(BUILD_SHARED_LIBS) 12 | message("-- Building SHARED libraries") 13 | else() 14 | message("-- Building STATIC libraries") 15 | endif() 16 | 17 | 18 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # look for stuff in the /cmake directory 19 | include(UpdateCacheVariable) 20 | 21 | # Work with non-standard homebrew installations 22 | # (from ceres build system) 23 | if (CMAKE_SYSTEM_NAME MATCHES "Darwin") 24 | find_program(HOMEBREW_EXECUTABLE brew) 25 | mark_as_advanced(FORCE HOMEBREW_EXECUTABLE) 26 | if (HOMEBREW_EXECUTABLE) 27 | # Detected a Homebrew install, query for its install prefix. 28 | execute_process(COMMAND ${HOMEBREW_EXECUTABLE} --prefix 29 | OUTPUT_VARIABLE HOMEBREW_INSTALL_PREFIX 30 | OUTPUT_STRIP_TRAILING_WHITESPACE) 31 | message(STATUS "Detected Homebrew with install prefix: " 32 | "${HOMEBREW_INSTALL_PREFIX}, adding to CMake search paths.") 33 | list(APPEND CMAKE_PREFIX_PATH "${HOMEBREW_INSTALL_PREFIX}") 34 | endif() 35 | endif() 36 | 37 | 38 | ### Handle windows-specific fixes 39 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 40 | add_definitions (-DNOMINMAX) # don't use weird windows built-in min/max 41 | add_definitions (-D_USE_MATH_DEFINES) # match unix behavior of constants in cmath 42 | endif() 43 | 44 | ### Do anything needed for dependencies and bring their stuff in to scope 45 | add_subdirectory(deps) 46 | 47 | # copy variables set by deps upward 48 | SET(GC_HAVE_SUITESPARSE ${GC_HAVE_SUITESPARSE} PARENT_SCOPE) 49 | 50 | ### Recurse to the source code 51 | add_subdirectory(src) 52 | 53 | # install 54 | install( 55 | TARGETS geometry-central 56 | ARCHIVE DESTINATION lib 57 | RUNTIME DESTINATION bin 58 | LIBRARY DESTINATION lib) 59 | 60 | install( 61 | DIRECTORY ${CMAKE_SOURCE_DIR}/include/ 62 | DESTINATION include 63 | FILES_MATCHING 64 | PATTERN "*.h" 65 | PATTERN "*.ipp") 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2019 Nicholas Sharp and the geometry-central contributors 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 | # Documentation is hosted at [geometry-central.net](http://geometry-central.net) 2 | --- 3 | 4 | # Welcome to Geometry Central 5 | 6 | [![actions status linux](https://github.com/nmwsharp/geometry-central/workflows/linux/badge.svg)](https://github.com/nmwsharp/geometry-central/actions) 7 | [![actions status macOS](https://github.com/nmwsharp/geometry-central/workflows/macOS/badge.svg)](https://github.com/nmwsharp/geometry-central/actions) 8 | [![actions status windows](https://github.com/nmwsharp/geometry-central/workflows/windows/badge.svg)](https://github.com/nmwsharp/geometry-central/actions) 9 | 10 | Geometry-central is a modern C++ library of data structures and algorithms for geometry processing, with a particular focus on surface meshes. 11 | 12 | Features include: 13 | 14 | - A polished **surface mesh** class, with efficient support for mesh modification, and a system of containers for associating data with mesh elements. 15 | - Implementations of canonical **geometric quantities** on surfaces, ranging from normals and curvatures to tangent vector bases to operators from discrete differential geometry. 16 | - A suite of **powerful algorithms**, including computing distances on surface, generating direction fields, and manipulating intrinsic Delaunay triangulations. 17 | - A coherent set of sparse **linear algebra tools**, based on Eigen and augmented to automatically utilize better solvers if available on your system. 18 | 19 | 20 | **Sample:** 21 | 22 | ```cpp 23 | // Load a mesh 24 | std::unique_ptr mesh; 25 | std::unique_ptr geometry; 26 | std::tie(mesh, geometry) = readSurfaceMesh("spot.obj"); 27 | 28 | // Compute vertex areas 29 | VertexData vertexAreas(*mesh); 30 | 31 | geometry->requireFaceAreas(); 32 | for(Vertex v : mesh->vertices()) { 33 | double A = 0.; 34 | for(Face f : v.adjacentFaces()) { 35 | A += geometry->faceAreas[f] / v.degree(); 36 | } 37 | vertexAreas[v] = A; 38 | } 39 | ``` 40 | 41 | Check out the docs, tutorials, and build instructions at [geometry-central.net](http://geometry-central.net). Use the [sample project](https://github.com/nmwsharp/gc-polyscope-project-template/) to get started with a build system and a gui. 42 | 43 | 44 | **Related alternatives:** 45 | [CGAL](https://www.cgal.org/), 46 | [libIGL](https://github.com/libigl/libigl), 47 | [OpenMesh](http://www.openmesh.org/), 48 | [Polygon Mesh Processing Library](https://www.pmp-library.org/), 49 | [CinoLib](https://github.com/mlivesu/cinolib) 50 | 51 | --- 52 | 53 | **Credits** 54 | 55 | Geometry-central is developed by [Nicholas Sharp](http://nmwsharp.com), with many contributions from 56 | [Keenan Crane](http://keenan.is/here), 57 | [Yousuf Soliman](http://www.its.caltech.edu/~ysoliman/), 58 | [Mark Gillespie](http://markjgillespie.com/), 59 | [Rohan Sawhney](http://rohansawhney.io/), and many others. 60 | 61 | 62 | 63 | If geometry-central contributes to an academic publication, cite it as: 64 | ```bib 65 | @article{geometrycentral, 66 | title={GeometryCentral: A modern C++ library of data structures and algorithms for geometry processing}, 67 | author={Nicholas Sharp and Keenan Crane and others}, 68 | howpublished="\url{https://geometry-central.net/}", 69 | year={2019} 70 | } 71 | ``` 72 | 73 | Development of this software was funded in part by NSF Award 1717320, an NSF graduate research fellowship, and gifts from Adobe Research and Autodesk, Inc. 74 | -------------------------------------------------------------------------------- /cmake/EigenChecker.cmake: -------------------------------------------------------------------------------- 1 | 2 | 3 | function(eigen3checker GC_EIGEN_LOCATION EIGEN3_FIND_VERSION) 4 | # Function looks for Eigen3 at GC_EIGEN_LOCATION 5 | # Eigen version much be >= EIGEN3_FIND_VERSION 6 | # 7 | # If Eigen is found it sets the following variables on PARENT_SCOPE 8 | # 9 | # EIGEN3_FOUND - system has eigen lib with correct version 10 | # EIGEN3_INCLUDE_DIR - the eigen include directory 11 | # EIGEN3_VERSION - eigen version 12 | # 13 | 14 | set(EIGEN3_FOUND false PARENT_SCOPE) 15 | 16 | ## Search for the signature_of_eigen3_matrix_library 17 | 18 | # Search first for just the requested path 19 | find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library 20 | PATHS ${GC_EIGEN_LOCATION} 21 | NO_DEFAULT_PATH 22 | NO_CMAKE_ENVIRONMENT_PATH 23 | NO_CMAKE_PATH 24 | NO_SYSTEM_ENVIRONMENT_PATH 25 | NO_CMAKE_SYSTEM_PATH 26 | NO_CMAKE_FIND_ROOT_PATH 27 | ) 28 | 29 | # Now search more broadly (will do nothing if the search above succeeded) 30 | find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library 31 | PATH_SUFFIXES 32 | eigen3 33 | eigen 34 | ) 35 | 36 | if(EXISTS "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h") 37 | 38 | # Parse version from Macros.h 39 | file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) 40 | 41 | string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}") 42 | set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}") 43 | string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}") 44 | set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}") 45 | string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}") 46 | set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}") 47 | 48 | set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION}) 49 | 50 | # message(STATUS "Eigen Version: ${EIGEN3_VERSION}") 51 | if(${EIGEN3_VERSION} VERSION_LESS ${EIGEN3_FIND_VERSION}) 52 | # Complain about the inadequate version 53 | message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, " "but at least version ${EIGEN3_FIND_VERSION} is required") 54 | else(${EIGEN3_VERSION} VERSION_LESS ${EIGEN3_FIND_VERSION}) 55 | # Suitable version of Eigen3 found. Set varibles 56 | set(EIGEN3_VERSION ${EIGEN3_VERSION} PARENT_SCOPE) 57 | set(EIGEN3_FOUND true PARENT_SCOPE) 58 | set(EIGEN3_INCLUDE_DIR ${EIGEN3_INCLUDE_DIR} PARENT_SCOPE) 59 | endif(${EIGEN3_VERSION} VERSION_LESS ${EIGEN3_FIND_VERSION}) 60 | else() 61 | # Purge EIGEN3_INCLUDE_DIR value since it's invalid 62 | # Required so find_path will actually search instead of defaulting 63 | set(EIGEN3_INCLUDE_DIR "EIGEN3_INCLUDE_DIR-NOTFOUND" PARENT_SCOPE) 64 | endif() 65 | endfunction() 66 | 67 | 68 | -------------------------------------------------------------------------------- /cmake/FetchContent/CMakeLists.cmake.in: -------------------------------------------------------------------------------- 1 | # Distributed under the OSI-approved BSD 3-Clause License. See accompanying 2 | # file Copyright.txt or https://cmake.org/licensing for details. 3 | 4 | cmake_minimum_required(VERSION ${CMAKE_VERSION}) 5 | 6 | # We name the project and the target for the ExternalProject_Add() call 7 | # to something that will highlight to the user what we are working on if 8 | # something goes wrong and an error message is produced. 9 | 10 | project(${contentName}-populate NONE) 11 | 12 | include(ExternalProject) 13 | ExternalProject_Add(${contentName}-populate 14 | ${ARG_EXTRA} 15 | SOURCE_DIR "${ARG_SOURCE_DIR}" 16 | BINARY_DIR "${ARG_BINARY_DIR}" 17 | CONFIGURE_COMMAND "" 18 | BUILD_COMMAND "" 19 | INSTALL_COMMAND "" 20 | TEST_COMMAND "" 21 | USES_TERMINAL_DOWNLOAD YES 22 | USES_TERMINAL_UPDATE YES 23 | ) -------------------------------------------------------------------------------- /cmake/UpdateCacheVariable.cmake: -------------------------------------------------------------------------------- 1 | # Ceres Solver - A fast non-linear least squares minimizer 2 | # Copyright 2015 Google Inc. All rights reserved. 3 | # http://ceres-solver.org/ 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # * Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # * Neither the name of Google Inc. nor the names of its contributors may be 14 | # used to endorse or promote products derived from this software without 15 | # specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | # POSSIBILITY OF SUCH DAMAGE. 28 | # 29 | # Author: alexs.mac@gmail.com (Alex Stewart) 30 | 31 | # By default, there is no easy way in CMake to set the value of a cache 32 | # variable without reinitialising it, which involves resetting its 33 | # associated help string. This is particularly annoying for CMake options 34 | # where they need to programmatically updated. 35 | # 36 | # This function automates this process by getting the current help string 37 | # for the cache variable to update, then reinitialising it with the new 38 | # value, but with the original help string. 39 | function(UPDATE_CACHE_VARIABLE VAR_NAME VALUE) 40 | get_property(HELP_STRING CACHE ${VAR_NAME} PROPERTY HELPSTRING) 41 | get_property(VAR_TYPE CACHE ${VAR_NAME} PROPERTY TYPE) 42 | set(${VAR_NAME} ${VALUE} CACHE ${VAR_TYPE} "${HELP_STRING}" FORCE) 43 | endfunction() 44 | -------------------------------------------------------------------------------- /deps/downloads/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !README.md 4 | -------------------------------------------------------------------------------- /deps/downloads/README.md: -------------------------------------------------------------------------------- 1 | This directory is used for dependencies automatically downloaded by CMake during the configuration process. 2 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Documentation for geometry-central 2 | 3 | 4 | Managed using mkdocs. 5 | 6 | To install mkdocs and theme: 7 | ``` 8 | pip install mkdocs mkdocs-material pygments 9 | ``` 10 | 11 | To preview locally: 12 | ``` 13 | python3 -m mkdocs serve 14 | ``` 15 | 16 | To build and deploy (TODO): 17 | ``` 18 | mkdocs build 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/docs/.gitignore: -------------------------------------------------------------------------------- 1 | !build 2 | -------------------------------------------------------------------------------- /docs/docs/CNAME: -------------------------------------------------------------------------------- 1 | geometry-central.net 2 | -------------------------------------------------------------------------------- /docs/docs/build/building.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | geometry-central uses CMake for to configure the build system. The basic workflow for downloading and compiling geometry-central via a terminal is: 4 | 5 | ```sh 6 | git clone --recurse-submodules https://github.com/nmwsharp/geometry-central.git 7 | cd geometry-central 8 | mkdir build && cd build 9 | cmake -DCMAKE_BUILD_TYPE=Release .. 10 | make -j4 11 | ``` 12 | 13 | However, since geometry-central is just a library, this does not build any executables, it merely compiles the library. 14 | 15 | You can add geometry-central to an existing project's `CMakeLists.txt` like 16 | 17 | ```cmake 18 | add_subdirectory("path/to/geometry-central") # wherever you put it 19 | target_link_libraries(your-project-target geometry-central) 20 | ``` 21 | 22 | #### Example 23 | 24 | For a simple example project using geometry-central (with [Polyscope](https://polyscope.run) for visualization), see [gc-polyscope-project-template](https://github.com/nmwsharp/gc-polyscope-project-template). This is a good starting point for new projects using geometry-central. 25 | 26 | ### On Windows 27 | 28 | When using Visual Studio on Windows, CMake can be used (either via the terminal or gui) to generate Visual Studio project and solution files. The project has been verified to compile out of the box with Visual Studio 2017 & 2019 (older versions not tested). 29 | 30 | ## Compile flags & options 31 | 32 | The library includes a few optional safety checks which are performed at runtime, even in release mode. Such checks are generally very cheap yet quite useful. Nonetheless, adding the `NGC_SAFETY_CHECKS` define will disable all optional safety checks, for a very small increase in performance. 33 | -------------------------------------------------------------------------------- /docs/docs/build/dependencies.md: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | 3 | geometry-central manages its dependenices via a mix of git submodules, configure-time downloading, and system libraries. However, the build system is explicitly set up such that cloning and building should immediately work on any vaguely reasonable machine, without chasing down dependencies. 4 | 5 | Remember, clone with: 6 | ```sh 7 | git clone --recurse-submodules https://github.com/nmwsharp/geometry-central.git 8 | ``` 9 | 10 | to ensure you resolve git submodules. If you cloned without submodules, you can get them afterwards with: 11 | 12 | ```sh 13 | git submodule update --init --recursive 14 | ``` 15 | 16 | ## Eigen 17 | 18 | [Eigen](https://eigen.tuxfamily.org) is used for linear algebra within geometry-central. Eigen presents a bit of a special challenge as a dependency because many programmers already have Eigen in the project or system, and intermingling multiple copies of Eigen can be problematic. 19 | 20 | As such, the build system uses the following strategies in order to resolve Eigen: 21 | 22 | 1. The target `Eigen3::Eigen` is already defined somewhere. Use the predefined target over any hints from the user 23 | 2. Using Eigen in any directory passed via the `GC_EIGEN_LOCATION` CMake cache variable (empty by default, see note below) 24 | 3. Using Eigen from your system libraries, as resolved via `find_package(Eigen3 3.3)` 25 | 4. Downloading a copy of Eigen in to the `deps/downloads/` directory 26 | 27 | For instance, if your project already has a copy of Eigen in its source tree, you can use it with (2) by setting `GC_EIGEN_LOCATION`. If not, many programmers have installed Eigen, which will be found in (3). Finally, as a last resort the build system will download a copy of Eigen as in (4). 28 | 29 | Set `GC_ALWAYS_DOWNLOAD_EIGEN=TRUE` to cause CMake to always prefer option (4) above, and prefer downloading a new copy of Eigen over using a system copy. 30 | 31 | geometry-central is known to work with version 3.3.8 of Eigen; other versions have not been tested (but recent versions probably work). 32 | 33 | ??? note "setting `GC_EIGEN_LOCATION`" 34 | 35 | The joys of CMake: if you are trying to set `GC_EIGEN_LOCATION` from some higher-level CMake script, you need to set it as a cache variable, which are different from 'normal' variables in CMake. As an example: 36 | 37 | ```cmake 38 | set(GC_EIGEN_LOCATION "full/path/to/eigen" CACHE PATH "my path") 39 | add_subdirectory(geometry-central) 40 | ``` 41 | 42 | ## Suitesparse 43 | 44 | [Suitesparse](http://faculty.cse.tamu.edu/davis/suitesparse.html) is an optional dependency which improves the performance and robustness of geometry-central's sparse linear solver routines. If Suitesparse is detected at configure time, linear solves will automatically use Suitesparse under the hood, and otherwise they will default to Eigen's solvers. The output of the CMake script will indicate whether or not Suitesparse was found. 45 | 46 | At any time, setting the `SUITESPARSE` CMake variable to false will stop the build system from using Suitesparse, even if it is availble. 47 | 48 | Installing suitesparse is up to the user. If using homebrew on OSX, `brew install suitesparse` should be sufficient. On Ubuntu, try `apt-get install libsuitesparse-dev`. Suitesparse is notoriously difficult to install on Windows---if you find a good method, let us know! 49 | -------------------------------------------------------------------------------- /docs/docs/build/tests.md: -------------------------------------------------------------------------------- 1 | ## Building and running tests 2 | 3 | Compile and run the tests with: 4 | ```sh 5 | cd test 6 | mkdir build && cd build 7 | cmake -DCMAKE_BUILD_TYPE=Debug .. 8 | make -j12 && ./bin/geometry-central-test 9 | ``` 10 | 11 | ## Tests organization 12 | 13 | All tests are stored in the `test/` subdirectory, which is not touched by the usual build system. 14 | 15 | ### googletest 16 | 17 | We use [googletest](https://github.com/google/googletest) as a testing framework. Most users do not need the tests, so rather than packing it as a git submodule which would unavoidably be cloned, the build system downloads a binary of the googletest when the tests are built---a consequence is that you must have a network connection to build tests for the first time (TODO: enable using a system install of googletest). 18 | 19 | ### Assets 20 | 21 | The `tests/assets/` directory contains a handful of input files for various tests. The absolute paths to these files are baked in to the test executable by the build system, so moving this directory after compiling tests may cause problems. The disk footprint of assets should be kept as small as possible since they are stored in the library repository. 22 | -------------------------------------------------------------------------------- /docs/docs/build/versions.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Changes in main branch since last version 4 | 5 | - Change the semantics of `MeshData<>::size()` to match what `size()` usually means 6 | 7 | ### July 3, 2020 8 | 9 | This version: 10 | 11 | - Generalize the main halfedge mesh type to support nonmanifold meshes in routines where they make sense. The old `HalfedgeMesh` is now `ManifoldSurfaceMesh`, which is a subclass of the new more general `SurfaceMesh`, offering many of the same operations. The header `halfedge_mesh.h` typedef's `HalfedgeMesh` as `ManifoldSurfaceMesh` so existing code will mostly still work. 12 | - Renamed `PolygonSoupMesh` to `SimplePolygonMesh`, and simplified some methods of this class. For now, the old type `PolygonSoupMesh` is typedef'd to `SimplePolygonMesh`, and the header `polygon_soup_mesh.h` includes `simple_polygon_mesh.h` so existing code should work. Please use `SimplePolygonMesh` in any new code. 13 | - Renamed `PlyHalfedgeMeshData` to `RichSurfaceMeshData`, and changed its workings to apply to more general meshes. 14 | - Changed underlying storage of `MeshData<>` containers from `std::vector<>` to `Eigen::VectorX_`. 15 | - Moved `halfedge_containers.h` to `utilities/mesh_data.h`, along with reorganizing various mesh element headers (you shouldn't need to include any of these headers in user code anyway, just including `surface_mesh.h` is sufficient) 16 | -------------------------------------------------------------------------------- /docs/docs/extras.css: -------------------------------------------------------------------------------- 1 | p { 2 | text-align: justify; 3 | } 4 | 5 | p, a, ul, li { 6 | font-weight: lighter; 7 | } 8 | 9 | 10 | 11 | .md-typeset .admonition.func, .md-typeset details.func { 12 | border-left: .2rem solid #d12; 13 | } 14 | .md-typeset .admonition.func>.admonition-title, .md-typeset details.func>.admonition-title, .md-typeset details.func>summary { 15 | border-bottom: .1rem solid rgba(245, 245, 245, 1); 16 | background-color: rgba(245, 245, 245, 1); 17 | } 18 | .md-typeset .admonition.func>.admonition-title:before, .md-typeset details.func>.admonition-title:before, .md-typeset details.func>summary:before { 19 | color: #d12; 20 | background-color: #d12; 21 | content: "code"} 22 | 23 | -------------------------------------------------------------------------------- /docs/docs/media/bean_scalar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/bean_scalar.jpg -------------------------------------------------------------------------------- /docs/docs/media/bean_vector.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/bean_vector.jpg -------------------------------------------------------------------------------- /docs/docs/media/bff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/bff.png -------------------------------------------------------------------------------- /docs/docs/media/convex_embed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/convex_embed.gif -------------------------------------------------------------------------------- /docs/docs/media/direction_field_basic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/direction_field_basic.jpg -------------------------------------------------------------------------------- /docs/docs/media/direction_field_boundary.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/direction_field_boundary.jpg -------------------------------------------------------------------------------- /docs/docs/media/direction_field_curvature.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/direction_field_curvature.jpg -------------------------------------------------------------------------------- /docs/docs/media/embed_convex.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/embed_convex.jpg -------------------------------------------------------------------------------- /docs/docs/media/flip_bezier.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/flip_bezier.jpg -------------------------------------------------------------------------------- /docs/docs/media/flip_cathead_refine.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/flip_cathead_refine.jpg -------------------------------------------------------------------------------- /docs/docs/media/flip_geodesic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/flip_geodesic.jpg -------------------------------------------------------------------------------- /docs/docs/media/geodesic-centroidal-voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/geodesic-centroidal-voronoi.png -------------------------------------------------------------------------------- /docs/docs/media/geometry_inheritance.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/geometry_inheritance.ai -------------------------------------------------------------------------------- /docs/docs/media/halfedge_boundary_diagram.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/halfedge_boundary_diagram.ai -------------------------------------------------------------------------------- /docs/docs/media/halfedge_index_diagram.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/halfedge_index_diagram.pdf -------------------------------------------------------------------------------- /docs/docs/media/halfedge_orientation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/halfedge_orientation.png -------------------------------------------------------------------------------- /docs/docs/media/halfedge_pointers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/halfedge_pointers.png -------------------------------------------------------------------------------- /docs/docs/media/int_tri_teaser.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/int_tri_teaser.jpg -------------------------------------------------------------------------------- /docs/docs/media/integer_coordinates.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/integer_coordinates.pdf -------------------------------------------------------------------------------- /docs/docs/media/integer_coordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/integer_coordinates.png -------------------------------------------------------------------------------- /docs/docs/media/intersect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/intersect.png -------------------------------------------------------------------------------- /docs/docs/media/karcher_mean_simple.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/karcher_mean_simple.jpg -------------------------------------------------------------------------------- /docs/docs/media/n_direction_fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/n_direction_fields.png -------------------------------------------------------------------------------- /docs/docs/media/octopus_logmap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/octopus_logmap.jpg -------------------------------------------------------------------------------- /docs/docs/media/point_heat_solvers.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/point_heat_solvers.jpg -------------------------------------------------------------------------------- /docs/docs/media/point_heat_solvers_updated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/point_heat_solvers_updated.png -------------------------------------------------------------------------------- /docs/docs/media/poisson_disk_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/poisson_disk_sample.png -------------------------------------------------------------------------------- /docs/docs/media/polygon_heat_solvers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/polygon_heat_solvers.png -------------------------------------------------------------------------------- /docs/docs/media/self_intersect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/self_intersect.png -------------------------------------------------------------------------------- /docs/docs/media/signed_heat_method.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/signed_heat_method.png -------------------------------------------------------------------------------- /docs/docs/media/spot-triangulation-original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/spot-triangulation-original.png -------------------------------------------------------------------------------- /docs/docs/media/spot-triangulation-remeshed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/spot-triangulation-remeshed.png -------------------------------------------------------------------------------- /docs/docs/media/stripes_isolines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/stripes_isolines.png -------------------------------------------------------------------------------- /docs/docs/media/tutorials/dir_field.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/tutorials/dir_field.jpeg -------------------------------------------------------------------------------- /docs/docs/media/tutorials/subdiv_after.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/tutorials/subdiv_after.jpeg -------------------------------------------------------------------------------- /docs/docs/media/tutorials/subdiv_before.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/tutorials/subdiv_before.jpeg -------------------------------------------------------------------------------- /docs/docs/media/vertex_scalar_curvatures.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/vertex_scalar_curvatures.jpg -------------------------------------------------------------------------------- /docs/docs/media/vertex_tangent_coordinates.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/docs/docs/media/vertex_tangent_coordinates.ai -------------------------------------------------------------------------------- /docs/docs/numerical/matrix_types.md: -------------------------------------------------------------------------------- 1 | 2 | ## Eigen 3 | 4 | Generally, geometry central uses [Eigen](http://eigen.tuxfamily.org) for all matrix types. Though we build additional solvers and utilities on top of Eigen. See [the Eigen section of dependencies](../../build/dependencies/#eigen) for instructions about getting Eigen and integrating with existing build systems. 5 | 6 | Note that the [Vector2](../../utilities/vector2/) and [Vector3](../../utilities/vector3) low-dimensional scalar types are entirely separate from these high-dimensional linear algebra types; `Vector2` and `Vector3` do not use Eigen, and they cannot participate in arithmetic expressions with Eigen types. 7 | 8 | Two typedefs are used extensively throughout geometry central to make the default Eigen types slightly less verbose. Both are defined in `linear_algebra_utilities.h`. 9 | 10 | `#!cpp #include "geometrycentral/numerical/linear_algebra_utilities.h"` 11 | 12 | ??? func "`#!cpp Vector`" 13 | 14 | A templated vector typedef, to Eigen's vector type. 15 | ```cpp 16 | template 17 | using Vector = Eigen::Matrix; 18 | ``` 19 | 20 | Use like `Vector` or `Vector`. 21 | 22 | 23 | ??? func "`#!cpp SparseMatrix`" 24 | 25 | A templated sparse matrix typedef, to Eigen's sparse matrix type. 26 | ```cpp 27 | template 28 | using SparseMatrix = Eigen::SparseMatrix; 29 | ``` 30 | 31 | Use like `SparseMatrix` or `SparseMatrix`. 32 | 33 | 34 | 35 | #### Gotchas 36 | 37 | Be wary, Eigen's alignment rules make it efficient, but also impose requirements which can lead to hard-to-debug memory errors. A few particularly common pitfalls are: 38 | 39 | - Avoid intermingling different versions of Eigen in the same program. Suppose some part of you codebase uses one version of Eigen, and a dependency uses a different version. Linking a function which returns an Eigen vector between these versions can lead to segfaults, because different alignment policies were used. 40 | 41 | - Similar to the previous, linking Eigen programs compiled with different preprocessor directives and optimization flags can yield binary incompatibility. Be sure that all parts of your codebase using Eigen receive the same build options. 42 | 43 | - Fixed-sized Eigen types (like `Eigen::Vector4d`, but **not** our `Vector` or `SparseMatrix`) *may not* be passed by value to functions. The same applies transitively to classes which have Eigen types as members. See this (opinionated) [note](https://eigen.tuxfamily.org/dox-devel/group__TopicPassingByValue.html). 44 | -------------------------------------------------------------------------------- /docs/docs/pointcloud/basics.md: -------------------------------------------------------------------------------- 1 | The `PointCloud` class is the main data structure for representing point clouds in geometry-central. It mimics the design of `SurfaceMesh` for the sake of consistency, though it is generally much simpler. The `PointCloud` class is simply a logical object referring to an abstract collection of points. Geometric data (aka point positions) are stored separately in the `PointPositionGeometry` class (or related subclasses). 2 | 3 | ```#include "geometrycentral/pointcloud/point_cloud.h"``` 4 | 5 | !!! warning "Point clouds are in beta" 6 | 7 | The current point cloud API in geometry-central is preliminary, and may change in future versions. 8 | 9 | 10 | **Example:** A tour of basic point cloud functionality. 11 | ```c++ 12 | #include "geometrycentral/pointcloud/point_cloud.h" 13 | #include "geometrycentral/pointcloud/point_position_geometry.h" 14 | 15 | size_t nPts = 5000; 16 | 17 | // create a new cloud 18 | PointCloud cloud(nPts); 19 | 20 | // access properties of the cloud 21 | std::cout << "cloud has " << cloud.nPoints() << " points.\n"; 22 | // Prints "cloud has 5000 points" 23 | 24 | // iterate through the points in the cloud 25 | for(Point p : cloud.points()) { 26 | std::cout << "Hi, I'm point " << p << "\n"; 27 | // Prints "Hi, I'm point p_245" (etc) 28 | } 29 | 30 | // store data on a point cloud 31 | PointData values(cloud); 32 | for(Point p : cloud.points()) { 33 | values[p] = 7; 34 | } 35 | 36 | // associate 3D positions with the point cloud 37 | // compute geometric data using the geomety object 38 | PointData positions = /* some positions */; 39 | PointPositionGeometry geom(*cloud, positions); 40 | 41 | // for example, compute normals 42 | geom.requireNormals(); 43 | for(Point p : cloud.points()) { 44 | std::cout << "normal for point " << p << " is " << geom.normals[p] << "\n"; 45 | } 46 | ``` 47 | 48 | 49 | ??? func "`#!cpp PointCloud::PointCloud(size_t nPts)`" 50 | Constructs a new point cloud with the desired number of points. 51 | 52 | See the IO routines for higher-level constructors to simultaneously construct a point cloud and its geometry, or read from file, etc. 53 | 54 | 55 | ??? func "`#!cpp /*iterator type*/ PointCloud::points()`" 56 | 57 | Iterate through the points in a point cloud, like 58 | 59 | ```c++ 60 | for(Point p : cloud.points()) { 61 | // do science 62 | } 63 | ``` 64 | 65 | ??? func "`#!cpp Point PointCloud::point(size_t iP)`" 66 | 67 | Return a handle to the i'th point. 68 | 69 | ??? func "`#!cpp size_t PointCloud::nPoints() const`" 70 | 71 | Returns the number of points. 72 | 73 | ??? func "`#!cpp std::unique_ptr PointCloud::copy() const`" 74 | 75 | Returns the number of points. 76 | 77 | !!! note "Mutating point clouds" 78 | 79 | TODO The `PointCloud` class contains the groundwork for efficiently adding/removing points from the cloud while updating containers (etc) similar to `SurfaceMesh`. However, this functionality is not fleshed out, tested, or documented yet. 80 | -------------------------------------------------------------------------------- /docs/docs/pointcloud/utilities/io.md: -------------------------------------------------------------------------------- 1 | # Input & Output 2 | 3 | Some basic input/output routines for point clouds. 4 | 5 | ## Input 6 | 7 | **Example:** Read in a point cloud. 8 | 9 | ```cpp 10 | #include "geometrycentral/pointcloud/point_cloud.h" 11 | #include "geometrycentral/pointcloud/point_position_geometry.h" 12 | #include "geometrycentral/pointcloud/point_cloud_io.h" 13 | 14 | using namespace geometrycentral; 15 | using namespace geometrycentral::pointcloud; 16 | 17 | std::unique_ptr cloud; 18 | std::unique_ptr geom; 19 | std::tie(cloud, geom) = readPointCloud("my_cloud.ply"); 20 | 21 | // do science! 22 | ``` 23 | 24 | ??? func "`#!cpp std::tuple, std::unique_ptr> readPointCloud(std::string filename, std::string type = "")`" 25 | 26 | Read a point cloud from file, constructing both the cloud and geometry objects. 27 | 28 | Currently accepted file types: `ply`, `obj`. Using the default empty type string will attempt to infer from the filename. 29 | 30 | ??? func "`#!cpp std::tuple, std::unique_ptr> readPointCloud(std::istream& in, std::string type)`" 31 | 32 | Like above, but reads directly from an `istream`. The type must be specified explicitly. 33 | 34 | 35 | ## Output 36 | 37 | **Example:** Write out a point cloud. 38 | 39 | ```cpp 40 | #include "geometrycentral/pointcloud/point_cloud.h" 41 | #include "geometrycentral/pointcloud/point_position_geometry.h" 42 | #include "geometrycentral/pointcloud/point_cloud_io.h" 43 | 44 | using namespace geometrycentral; 45 | using namespace geometrycentral::pointcloud; 46 | 47 | // your cloud & point positions, populated somehow 48 | std::unique_ptr cloud; 49 | std::unique_ptr geom; 50 | 51 | writePointCloud(*cloud, *geom, "cloud_out.ply"); 52 | ``` 53 | 54 | ??? func "`#!cpp void writePointCloud(PointCloud& cloud, PointPositionGeometry& geometry, std::string filename, std::string type = "")`" 55 | 56 | Write a point cloud to file. 57 | 58 | Currently accepted file types: `ply`, `obj`. Using the default empty type string will attempt to infer from the filename. 59 | 60 | ??? func "`#!cpp void writePointCloud(PointCloud& cloud, PointPositionGeometry& geometry, std::ostream& out, std::string type)`" 61 | 62 | Like above, but writes directly to an `ostream`. The type must be specified explicitly. 63 | -------------------------------------------------------------------------------- /docs/docs/pointcloud/utilities/sampling.md: -------------------------------------------------------------------------------- 1 | Sample random point clouds from triangle meshes. 2 | 3 | **Example:** Read a triangle mesh from file and uniformly sample points 4 | ```cpp 5 | #include "geometrycentral/pointcloud/point_cloud.h" 6 | #include "geometrycentral/pointcloud/point_position_geometry.h" 7 | #include "geometrycentral/pointcloud/point_cloud_io.h" 8 | #include "geometrycentral/pointcloud/point_cloud.h" 9 | #include "geometrycentral/surface/meshio.h" 10 | 11 | using namespace geometrycentral; 12 | using namespace geometrycentral::surface; 13 | using namespace geometrycentral::pointcloud; 14 | 15 | // Load a mesh 16 | std::unique_ptr mesh; 17 | std::unique_ptr meshGeom; 18 | std::tie(mesh, meshGeom) = loadMesh(args::get(inputFilename)); 19 | 20 | // Sample a point cloud from the mesh 21 | std::unique_ptr cloud; 22 | PointData pointPos; 23 | PointData cloudSources; 24 | size_t nPts = 5000; 25 | std::tie(cloud, pointPos, cloudSources) = uniformlySamplePointsOnSurface(*mesh, *meshGeom, nPts); 26 | 27 | // As an example, use the source points to get face normals 28 | PointData normals(*cloud); 29 | meshGeom->requireFaceNormals(); 30 | for (Point p : cloud->points()) { 31 | normals[p] = meshGeom->faceNormals[cloudSources[p].face]; 32 | } 33 | 34 | // Construct a geometry object from the positions for subsequent calculations 35 | PointPositionGeometry cloudGeom(*cloud, pointPos); 36 | ``` 37 | 38 | ??? func "`#!cpp std::tuple, PointData, PointData> uniformlySamplePointsOnSurface(surface::SurfaceMesh& mesh, surface::EmbeddedGeometryInterface& geom, size_t nPts)`" 39 | 40 | Sample `nPts` points from a triangle mesh. Points are sampled uniformly at random from the underlying surface, independent of tesselation. 41 | 42 | The return tuple holds: 43 | 44 | - The new `PointCloud` 45 | - A 3D position for each point 46 | - A `SurfacePoint` on the original mesh corresponding to each point 47 | 48 | 49 | Note that in addition to the cloud and 3D positions, this routine returns a `SurfacePoint` associated with each point sample. The [surface point](/surface/utilities/surface_point/) is a handy class representing a location on the underlying mesh, and makes it easy to do things like interpolate data defined on the source mesh, or grab face normals, etc. 50 | 51 | -------------------------------------------------------------------------------- /docs/docs/surface/algorithms/geodesic_voronoi_tessellations.md: -------------------------------------------------------------------------------- 1 | A Voronoi tessellation partitions a domain $M$ based on a set of sites $s_i \in M$. To each site $s_i$, we associate a region (or "Voronoi cell") consisting of the points closer to $s_i$ than to any other site. Such a tessellation is called _centroidal_ if each site is at the center of its corresponding cell. This routine computes centroidal Voronoi tessellations on surface meshes. 2 | 3 | !!! warning "Output is not unique!" 4 | 5 | By default, this procedure picks some random points and optimizes them to compute a centroidal Voronoi tessellation. Since the initialization is random, the results will generally be different each time the procedure is run. 6 | 7 | `#include "geometrycentral/surface/geodesic_centroidal_voronoi_tessellation.h"` 8 | 9 | ![karcher means](/media/geodesic-centroidal-voronoi.png){: style="max-width: 25em; display: block; margin-left: auto; margin-right: auto;"} 10 | 11 | 12 | ??? func "`#!cpp VoronoiResult computeGeodesicCentroidalVoronoiTessellation(ManifoldSurfaceMesh& mesh, IntrinsicGeometryInterface& geom, VoronoiOptions options = defaultVoronoiOptions)`" 13 | 14 | Compute a geodesic centroidal Voronoi tessellation on the input mesh. Options are passed as a [VoronoiOptions](#options) object, and the output is returned as a [VoronoiResult](#result) object. 15 | 16 | ## Helper Types 17 | ### Options 18 | Options are passed in to `computeGeodesicCentroidalVoronoiTessellation` via a `VoronoiOptions` object. 19 | 20 | | Field | Default value |Meaning| 21 | |---|---|---| 22 | | `#!cpp size_t nSites;`| `10` | the number of sites to place | 23 | | `#!cpp std::vector initialSites;`| `{}` | desired locations for sites. If blank, initial locations are chosen randomly | 24 | | `#!cpp size_t iterations;`| `50` | number of iterations to run for | 25 | | `#!cpp bool useDelaunay;`| `true` | solve on an [intrinsic Delaunay triangulation](/surface/intrinsic_triangulations/basics) of the input | 26 | | `#!cpp double tCoef;`| `1` | diffusion time for the [vector heat method](/surface/algorithms/vector_heat_method) | 27 | | `#!cpp size_t nSubIterations;`| `1` | number of iterations to use when computing [surface centers](/surface/algorithms/surface_centers) during optimization | 28 | 29 | 30 | ### Result 31 | The result is returned as a `VoronoiResult`, which has 3 fields: 32 | 33 | | Field | Meaning| 34 | |---|---| 35 | | `#!cpp std::vector siteLocations;`| the sites at the centers of the Voronoi cells | 36 | | `#!cpp std::vector> siteDistributions;`| indicator functions which each Voronoi cell | 37 | | `#!cpp bool hasDistributions;`| is `siteDistributions` populated? | 38 | 39 | ## Citation 40 | 41 | This algorithms is described in [The Vector Heat Method](http://www.cs.cmu.edu/~kmcrane/Projects/VectorHeatMethod/paper.pdf), the appropriate citation is: 42 | 43 | ```bib 44 | @article{sharp2019vector, 45 | title={The Vector Heat Method}, 46 | author={Sharp, Nicholas and Soliman, Yousuf and Crane, Keenan}, 47 | journal={ACM Transactions on Graphics (TOG)}, 48 | volume={38}, 49 | number={3}, 50 | pages={24}, 51 | year={2019}, 52 | publisher={ACM} 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /docs/docs/surface/algorithms/robust_geometry.md: -------------------------------------------------------------------------------- 1 | Many data structures and algorithms in geometry-central help to "robustify" algorithms, and help them automatically work better on meshes with poor geometry and/or connectivity. Whenever possible, this done in a way which is transparent to the algorithm designer and end user. 2 | 3 | 4 | ### Intrinsic Mollifiction 5 | 6 | `#include "geometrycentral/surface/intrinsic_mollification.h"` 7 | 8 | Poor quality meshes might have triangles whose shape is so close to being degenerate that even basic floating point arithmetic breaks down. Such features are difficult to resolve with by perturbing vertex positions, because a motion that makes one triangle better might make another triangle worse. Fortunately, when working with intrinsic geometry, there is a simple strategy which is always guaranteed to work. 9 | 10 | To ensure that all triangles are nondegenerate, we want to have 11 | 12 | $$ 13 | l_{ij} + l_{jk} > l_{ki} + \delta 14 | $$ 15 | 16 | for all triples of triangle edge lengths, according to some user-specified numerical tolerance $\delta$, which specified how far from degenerate the triangles should be. This is easily achieved by simply adding some small value $\epsilon$ to all edge lengths, chosen to be the smallest $\epsilon$ which will make the above inequality hold. 17 | 18 | This strategy ensures all triangles are non-degenerate, yet yields a negligible change to the mesh's geometry. For a mesh which already has no degenerate triangles, it will have no effect. 19 | 20 | Example: 21 | 22 | ```cpp 23 | #include "geometrycentral/surface/intrinsic_mollification.h" 24 | using namespace geometrycentral; 25 | using namespace surface; 26 | 27 | // your mesh and intrinsic geometry 28 | std::unique_ptr mesh; 29 | std::unique_ptr geometry; 30 | 31 | // mollify the edge lengths 32 | mollifyIntrinsic(*mesh, geometry->edgeLengths); 33 | 34 | // ensure that any existing quantities are updated for the new edge lengths 35 | geometry->refreshQuantities(); 36 | 37 | // continue running algorithms, etc 38 | geometry->requireVertexGaussianCurvatures(); 39 | for(Vertex v : mesh->vertices()) { 40 | std::cout << "Gaussian curvature of " << v << " is " 41 | << geometry->vertexGaussianCurvatures[v] << std::endl; 42 | } 43 | ``` 44 | 45 | 46 | 47 | ??? func "`#!cpp void mollifyIntrinsic(SurfaceMesh& mesh, EdgeData& edgeLengths, double relativeFactor = 1e-6)`" 48 | 49 | Mollify the edge lengths in `edgeLengths`, to ensure that all triangles are nondegenerate by a factor $\delta$, where $\delta$ is computed as `relativeFactor` times the mean edge length in the input. 50 | 51 | 52 | ??? func "`#!cpp void mollifyIntrinsicAbsolute(SurfaceMesh& mesh, EdgeData& edgeLengths, double absoluteFactor)`" 53 | 54 | Similar to above, but mollifies with an absolute factor given by `absoluteFactor`, rather than a relative factor. 55 | 56 | 57 | This strategy is described in [A Laplacian for Nonmanifold Triangle Meshes](http://www.cs.cmu.edu/~kmcrane/Projects/NonmanifoldLaplace/NonmanifoldLaplace.pdf). The appropriate citation is: 58 | 59 | ```bib 60 | @article{Sharp:2020:LNT, 61 | author={Nicholas Sharp and Keenan Crane}, 62 | title={{A Laplacian for Nonmanifold Triangle Meshes}}, 63 | journal={Computer Graphics Forum (SGP)}, 64 | volume={39}, 65 | number={5}, 66 | year={2020} 67 | } 68 | ``` 69 | -------------------------------------------------------------------------------- /docs/docs/surface/algorithms/surface_centers.md: -------------------------------------------------------------------------------- 1 | These routines compute geometric centers of point sets and distrubtions on surfaces. 2 | 3 | A "center" $c$ is a point on the surface which is a local minimum of the energy: 4 | $$ 5 | c = \underset{m}{\textrm{argmin}}\sum_{y \in \mathcal{Y}} d(m,y)^p 6 | $$ 7 | where $\mathcal{Y}$ is a set of sites to take the average of and $d(\cdot, \cdot)$ denotes the geodesic distance. 8 | 9 | Centers on surfaces with $p=2$ ("means") are known as _Karcher Means_ or _Frechet Means_. Centers on surfaces with $p=1$ ("medians") are known as _geometric medians_. 10 | 11 | !!! warning "Centers are not unique!" 12 | 13 | In general, there will not be a single unqiue "center" of a point set or distribution on a surface. For nearby sites there may be a single center, but in general this may not be the case. 14 | 15 | The routines in this section use random initialization to report _a center_. As such, the results may be different under repeated runs of the procedure. 16 | 17 | `#include "geometrycentral/surface/surface_centers.h"` 18 | 19 | ![karcher means](/media/karcher_mean_simple.jpg) 20 | 21 | ## Centers of points 22 | 23 | ??? func "`#!cpp SurfacePoint findCenter(IntrinsicGeometryInterface& geom, const std::vector& vertexPts, int p = 2)`" 24 | 25 | Find a center of a collection of points at vertices. 26 | 27 | `p` must be either $1$ or $2$. 28 | 29 | ??? func "`#!cpp SurfacePoint findCenter(IntrinsicGeometryInterface& geom, VectorHeatMethodSolver& solver, const std::vector& vertexPts, int p = 2)`" 30 | 31 | Like the above method, but uses an existing solver object, which saves precomputation. 32 | 33 | 34 | ## Centers of distributions 35 | 36 | ??? func "`#!cpp SurfacePoint findCenter(IntrinsicGeometryInterface& geom, const VertexData& distribution, int p = 2)`" 37 | 38 | Find a center of a distribution at vertices. 39 | 40 | Note that the input `distribution` is treated as integrated values at vertices. If your distribution is "per-unit-area", you should multiply times vertex area before passing it in. 41 | 42 | `p` must be either $1$ or $2$. 43 | 44 | 45 | ??? func "`#!cpp SurfacePoint findCenter(IntrinsicGeometryInterface& geom, VectorHeatMethodSolver& solver, const VertexData& distribution, int p = 2)`" 46 | 47 | Like the above method, but uses an existing solver object, which saves precomputation. 48 | 49 | 50 | ## Citation 51 | 52 | These algorithms are described in [The Vector Heat Method](http://www.cs.cmu.edu/~kmcrane/Projects/VectorHeatMethod/paper.pdf), the appropriate citation is: 53 | 54 | ```bib 55 | @article{sharp2019vector, 56 | title={The Vector Heat Method}, 57 | author={Sharp, Nicholas and Soliman, Yousuf and Crane, Keenan}, 58 | journal={ACM Transactions on Graphics (TOG)}, 59 | volume={38}, 60 | number={3}, 61 | pages={24}, 62 | year={2019}, 63 | publisher={ACM} 64 | } 65 | ``` 66 | -------------------------------------------------------------------------------- /docs/docs/surface/intrinsic_triangulations/integer_coordinates.md: -------------------------------------------------------------------------------- 1 | # Integer Coordinates Intrinsic Triangulation 2 | 3 | ![integer coordinates example](/media/integer_coordinates.png){: style="max-width: 20em; display: block; margin-left: auto; margin-right: auto;"} 4 | 5 | The integer coordinates data structure encodes an intrinsic triangulation using _normal coordinates_ and _roundabouts_, which are integer values stored on edges. Since it avoids floating point data, this data structure is more robust than signposts, but operations such as vertex insertion can take longer. 6 | For more details, see [Integer Coordinates for Intrinsic Geometry Processing](https://arxiv.org/pdf/2106.00220.pdf). 7 | 8 | The `IntegerCoordinatesIntrinsicTriangulation` is one concrete implementation (subclass) of [intrinsic triangulations](../basics). **Most of its functionality is documented there.** 9 | 10 | ### Tradeoffs 11 | 12 | The signpost intrinsic triangulation is one of the two main intrinsic triangulation representations currently available in geometry-central, the other being [signposts](../signposts). For many tasks, either reprentation will be highly effective, but there are some tradeoffs. 13 | 14 | **Pros:** 15 | 16 | - **Robustness.** Integer coordinates are guaranteed to always maintain a topologically valid representation of the common subdivision, through any sequence of operations. 17 | 18 | **Cons:** 19 | 20 | - **Performance.** Integer coordinates may be moderately more expensive in terms of runtime than signposts (although both are often on the order of milliseconds). Some operations, such as tracing out a single edge of the intrinsic triangulation, are only implemented by first extracting the entire common subdivision, which has some overhead. 21 | 22 | !!! warning "Not yet implemented" 23 | 24 | The methods `equivalentPointOnIntrinsic()` and `splitEdge()` from `IntrinsicTriangulation` are not yet implemented for the integer coordinates representation. For `splitEdge()`, an alternate version is provided which returns a vertex instead. 25 | 26 | 27 | ### Constructors 28 | 29 | ??? func "`#!cpp IntegerCoordinatesIntrinsicTriangulation::IntegerCoordinatesIntrinsicTriangulation(ManifoldSurfaceMesh& mesh, IntrinsicGeometryInterface& inputGeom, double mollifyEPS=1e-5)`" 30 | 31 | Initialize an intrinsic triangulation sitting on top of `mesh`. Recall that `IntrinsicGeometryInterface` can be almost any geometry object, including a `VertexPositionGeometry`. 32 | 33 | Initially, the intrinsic triangulation will be identical to the input mesh; it can be modified with the routines below. 34 | 35 | The `mollifyEPS` parameter performs initial [mollification](/surface/algorithms/robust_geometry/#intrinsic-mollification) on the intrinsic triangulation, which greatly improves floating-point robustness while generally, while having a negligible impact on accuracy. Set `mollifyEPS=0` to disable. 36 | 37 | ## Citation 38 | 39 | ```bib 40 | @article{gillespie2021integer, 41 | title={Integer Coordinates for Intrinsic Geometry Processing}, 42 | author={Gillespie, Mark and Sharp, Nicholas and Crane, Keenan}, 43 | journal={arXiv preprint arXiv:2106.00220}, 44 | year={2021} 45 | } 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/docs/surface/intrinsic_triangulations/signposts.md: -------------------------------------------------------------------------------- 1 | # Signpost Intrinsic Triangulation 2 | 3 | ![signpost example](/media/signposts.svg){: style="max-width: 25em; display: block; margin-left: auto; margin-right: auto;"} 4 | 5 | The signpost data structure encodes an intrinsic triangulation by storing "signposts" at mesh vertices. Explicitly, for each intrinsic edge it stores the edge's length and direction at the two incident vertices. This information fully specifies how the intrinsic triangulation sits above the input mesh. For more details, see [Navigating Intrinsic Triangulations](http://www.cs.cmu.edu/~kmcrane/Projects/NavigatingIntrinsicTriangulations/paper.pdf). 6 | 7 | The `SignpostIntrinsicTriangulation` is one concrete implementation (subclass) of [intrinsic triangulations](../basics). **Most of its functionality is documented there.** Here, we document additional routines which are specific to signposts. 8 | 9 | ### Tradeoffs 10 | 11 | The signpost intrinsic triangulation is one of the two main intrinsic triangulation representations currently available in geometry-central, the other being [integer coordinates](../integer_coordinates). For many tasks, either reprentation will be highly effective, but there are some tradeoffs. 12 | 13 | **Pros:** 14 | 15 | - **Performance.** Signposts are moderately faster in terms of runtime than integer coordinates (although both are often on the order of milliseconds). 16 | 17 | - **Tangent vector data.** Signposts naturally offer tangent space coordinate systems which are consistent with the input mesh, making it easy to work with tangent-valued data at vertices of the intrinsic triangulation. 18 | 19 | **Cons:** 20 | 21 | - **Robustness.** The representation relies heavily on tracing intrinsic edge paths across the input surface, which can be error-prone in floating-point arithmetic. In particular, reconstructing the common subdivision may fail if the input contains degenerate triangles. 22 | 23 | 24 | ### Constructors 25 | 26 | ??? func "`#!cpp SignpostIntrinsicTriangulation::SignpostIntrinsicTriangulation(ManifoldSurfaceMesh& mesh, IntrinsicGeometryInterface& inputGeom)`" 27 | 28 | Initialize an intrinsic triangulation sitting on top of `mesh`. Recall that `IntrinsicGeometryInterface` can be almost any geometry object, including a `VertexPositionGeometry`. 29 | 30 | Initially, the intrinsic triangulation will be identical to the input mesh; it can be modified with the routines below. 31 | 32 | ### Methods 33 | 34 | ??? func "`#!cpp std::vector SignpostIntrinsicTriangulation::traceIntrinsicHalfedgeAlongInput(Halfedge intrinsicHe, bool trimEnd)`" 35 | 36 | This function is generally the same as `traceIntrinsicHalfedgeAlongInput()`. 37 | 38 | When edges paths from signposts, the path often does not _exactly_ hit the destination vertex, but rather ends somewhere very close in the adjacent 1-ring. If `trimEnd=true`, a simple heuristic is used to clean up the path so it exactly hits the target vertex; with `trimEnd=false` the result of tracing is directly reported. 39 | 40 | By default `traceIntrinsicHalfedgeAlongInput(he)` is equivalent to `traceIntrinsicHalfedgeAlongInput(he, true)`. 41 | 42 | 43 | ## Citation 44 | 45 | ```bib 46 | @article{sharp2019navigating, 47 | title={Navigating intrinsic triangulations}, 48 | author={Sharp, Nicholas and Soliman, Yousuf and Crane, Keenan}, 49 | journal={ACM Transactions on Graphics (TOG)}, 50 | volume={38}, 51 | number={4}, 52 | pages={55}, 53 | year={2019}, 54 | publisher={ACM} 55 | } 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/docs/surface/utilities/surface_point.md: -------------------------------------------------------------------------------- 1 | A `SurfacePoint` is a generic location on a surface, which might be at a vertex, along an edge, or inside a face. Surface points are used throughout geometry-central for methods that input or output arbitrary locations on surfaces. 2 | 3 | `#include "geometrycentral/surface/surface_point.h"` 4 | 5 | The field `SurfacePoint::type` is an enum: 6 | ```cpp 7 | enum class SurfacePointType { Vertex = 0, Edge, Face }; 8 | ``` 9 | 10 | which indicates what kind of point this is. 11 | 12 | - if the surface point is a **vertex**, the field `SurfacePoint::vertex` indicates which vertex. Otherwise it is the null default vertex. 13 | 14 | - if the surface point is **along an edge**, the field `SurfacePoint::edge` indicates which edge. Otherwise it is the null default edge. The field `SurfacePoint::tEdge` indicates the location along that edge, in the range `[0,1]`, with `0` at `edge.halfedge().vertex()`. 15 | 16 | - if the surface point is **inside a face**, the field `SurfacePoint::face` indicates which face. Otherwise it is the null default face. The field `SurfacePoint::faceCoords` indicates the location inside that face, as barycentric coordinates (numbered according to the iteration order of vertices about the face, as usual). 17 | 18 | Surface points have a few useful utility methods: 19 | 20 | ??? func "`#!cpp T SurfacePoint::interpolate(const VertexData& data)`" 21 | 22 | Given data of tempalte type `T` defined at vertices, linearly interpolates to a value at this location. 23 | 24 | 25 | ??? func "`#!cpp SurfacePoint SurfacePoint::inSomeFace()`" 26 | 27 | All surface points (vertex, edge, face) have an equivalent point in one or many adjacent faces. For instance, a vertex could be equivalently a point in any of the incident faces, with a single `1` barycentric coordinate, or a point on an edge could be a point in either of the two adjacent faces. 28 | 29 | This function returns one of the equivalent surface points in a face (chosen arbitrarily). If this point is a face point, the output is a copy of this point. 30 | 31 | ??? func "`#!cpp Vertex SurfacePoint::nearestVertex()`" 32 | 33 | Returns the nearest vertex which is adjacent to this point. 34 | 35 | For surface points which are vertices, it will return the same vertex. For surface points which are along edges, it will return one of the two incident vertices. For surface points which are inside faces, it will return one of the three incident vertices. 36 | 37 | In addition, surface points can be used to construct [barycentric vectors](../barycentric_vector). -------------------------------------------------------------------------------- /docs/docs/tutorials/direction_fields.md: -------------------------------------------------------------------------------- 1 | This tutorial shows a higher-level example in geometry-central, where we use a built-in routine to visualize a direction field. 2 | 3 | [View full, runnable source code in the tutorial repository.](https://github.com/nmwsharp/geometry-central-tutorials) 4 | 5 | 6 | ### Basic setup 7 | 8 | To begin we include headers, bring in namespaces, See the first tutorial for more info on these steps. 9 | 10 | ```cpp 11 | #include "geometrycentral/surface/manifold_surface_mesh.h" 12 | #include "geometrycentral/surface/meshio.h" 13 | #include "geometrycentral/surface/surface_mesh.h" 14 | #include "geometrycentral/surface/direction_fields.h" 15 | 16 | #include "polyscope/polyscope.h" 17 | #include "polyscope/surface_mesh.h" 18 | 19 | using namespace geometrycentral; 20 | using namespace geometrycentral::surface; 21 | ``` 22 | 23 | Again, load an input mesh from file, and visualize it using Polyscope. We'll require that the mesh be manifold so we have obvious vertex tangent spaces. 24 | 25 | ```cpp 26 | std::unique_ptr mesh; 27 | std::unique_ptr geometry; 28 | std::tie(mesh, geometry) = readManifoldSurfaceMesh(args::get(inputFilename)); 29 | 30 | polyscope::init(); 31 | auto* psMesh = 32 | polyscope::registerSurfaceMesh("input mesh", 33 | geometry->vertexPositions, 34 | mesh->getFaceVertexList()); 35 | ``` 36 | 37 | Now, computing the direction field itself amoutns to a single function call 38 | ```cpp 39 | VertexData field = computeSmoothestVertexDirectionField(*geometry); 40 | ``` 41 | 42 | 43 | Before we visualize this direction field, we need to do a little more setup. The tangent vectors are defined in an abritrary, intrinsic tangent space at each vertex. We need to tell Polyscope how these tangent spaces sit in 3D space. 44 | 45 | ```cpp 46 | geometry->requireVertexTangentBasis(); 47 | VertexData vBasisX(*mesh); 48 | for (Vertex v : mesh->vertices()) { 49 | vBasisX[v] = geometry->vertexTangentBasis[v][0]; 50 | } 51 | psMesh->setVertexTangentBasisX(vBasisX); 52 | ``` 53 | 54 | Now we can add the vector field to Polyscope and inspect it! 55 | ```cpp 56 | psMesh->addVertexIntrinsicVectorQuantity("vectors", field); 57 | polyscope::show(); 58 | ``` 59 | 60 | 61 | Result: 62 | ![direction field](/media/tutorials/dir_field.jpeg) 63 | -------------------------------------------------------------------------------- /docs/docs/utilities/eigenmap.md: -------------------------------------------------------------------------------- 1 | Helper functions to interface with Eigen data in interesting ways. 2 | 3 | `#!cpp #include "geometrycentral/utilities/eigen_interop_helpers.h"` 4 | 5 | ## Expanding a homogenous POD type into an `Eigen::Matrix` 6 | 7 | ??? func "`#!cpp Eigen::Map, Alignment> EigenMap(MeshData &data)`" 8 | 9 | Given `MeshData` storing a POD type `O` like `Vector3` which can be meaningfully decomposed into a set of `k` other types `T`, this function maps the memory of the vector of `O` as a `Matrix`. E.g., `MeshData` (N x 1) -> `Map>` (N x 3). The template `Options` allows you to specify is the underlying data should be bound in RowMajor or ColMajor order. 10 | 11 | ```cpp 12 | VertexData pos; 13 | // auto resolves to Eigen::Map, Alignment> 14 | auto mapped_position_matrix = EigenMap(pos); 15 | ``` 16 | 17 | Note that it is unnecessary to specify `Eigen::RowMajor` in this example since it is the default template option. Furthermore template options `E` and `O` corresponding to the Mesh Element and Data Type need not be explicitly specified since the compiler can infer it for us. 18 | 19 | Most important, modifications to `mapped_position_matrix` will be reflected in `pos` since they share the same memory! 20 | 21 | ??? func "`#!cpp Eigen::Map, Alignment> EigenMap(const MeshData &data)`" 22 | 23 | Const version of the above. 24 | 25 | ## Flattening a homogeneous POD type into an `Eigen::Vector` 26 | 27 | ??? func "`#!cpp Eigen::Map, Alignment> FlattenedEigenMap(MeshData &data)`" 28 | 29 | Given `MeshData` storing a POD type `O` like `Vector3` which can be meaningfully decomposed into a set of `k` other types `T`, this function maps the memory of the vector of `O` as a vector of `T` (`Matrix`). E.g., `MeshData` (N x 1) -> `Map>` (3N x 1). The template `Options` allows you to specify is the underlying data should be bound in RowMajor or ColMajor order. 30 | 31 | ```cpp 32 | VertexData pos; 33 | // auto resolves to Eigen::Map, Alignment> 34 | auto mapped_flat_position_matrix = FlattenedEigenMap(pos); 35 | ``` 36 | 37 | Note that the return defaults to `ColMajor` order. The template options `E` and `O` corresponding to the Mesh Element and Data Type need not be explicitly specified since the compiler can infer it for us. 38 | 39 | Most important, modifications to `mapped_flat_position_matrix` will be reflected in `pos` since they share the same memory! 40 | 41 | ??? func "`#!cpp Eigen::Map, Alignment> FlattenedEigenMap(const MeshData &data)`" 42 | 43 | Const version of the above. 44 | -------------------------------------------------------------------------------- /docs/docs/utilities/miscellaneous.md: -------------------------------------------------------------------------------- 1 | Miscellaneous utility functions. 2 | 3 | `#!cpp #include "geometrycentral/utilities/utilities.h"` 4 | 5 | ## Constants 6 | 7 | - `size_t INVALID_IND` Used to represent invalid indices. Defined as `std::numeric_limits::max()` 8 | - `double PI` Defined to be `3`. Just kidding. 9 | 10 | ## Angles and arithmetic 11 | 12 | 13 | ??? func "`#!cpp T clamp(T val, T low, T high)`" 14 | 15 | Returns `val` clamped to lie bewteen `low` and `high` (using comparison operators). 16 | 17 | 18 | ??? func "`#!cpp double regularizeAngle(double theta)`" 19 | 20 | Shifts an angle to lie in the range [$0$, $2 \pi$]. 21 | 22 | 23 | ## Random numbers 24 | 25 | All random values are drawn from a generator seeded at program initialization. The generator is seeded via `std::random_device`, so results will _not_ be consistent between repeated runs of the program. 26 | 27 | ??? func "`#!cpp double unitRand()`" 28 | 29 | Returns a uniformly-distributed value on $[0,1]$. 30 | 31 | ??? func "`#!cpp double randomReal()`" 32 | 33 | Returns a uniformly-distributed value on $[0,1]$. 34 | 35 | ??? func "`#!cpp double randomNormal(double mean=0.0, double stddev = 1.0)`" 36 | 37 | Returns a normally-distributed value from the specified mean and variance. 38 | 39 | ??? func "`#!cpp int randomInt(int lower, int upper)`" 40 | 41 | Returns a uniformly-distributed integer on the INCLUSIVE range `[lower, upper]` 42 | 43 | ??? func "`#!cpp size_t randomIndex(size_t size)`" 44 | 45 | Returns a uniformly-distributed integer on the range `[0, size)`. 46 | 47 | 48 | 49 | ## Indices and lists 50 | 51 | ??? func "`#!cpp std::vector applyPermutation(const std::vector& sourceData, const std::vector& permOldToNew)`" 52 | 53 | Apply a permutation to reorder a vector, such that `output[i] = sourceData[permOldToNew[i]]`. 54 | 55 | The permutation should be an injection to `[0,sourceData.size())`. The `sourceData`, `permOldToNew`, and the output should all have same size. 56 | 57 | 58 | ## Printing and strings 59 | 60 | ??? func "`#!cpp std::string to_string(std::vector const& v)`" 61 | 62 | Print the elements of vector to a string using the `<<` operator for each element. 63 | 64 | ??? func "`#!cpp std::string str_printf(const std::string& format, Args... args)`" 65 | 66 | Print directly to a string, where `format` and `args` obey `printf` semantics. 67 | 68 | 69 | ## Memory management 70 | 71 | ??? func "`#!cpp void safeDelete(T*& x)`" 72 | 73 | Call `delete` on a pointer. If the pointer is `nullptr`, does nothing. If it is non-null, sets to `nullptr` after deleting. 74 | 75 | ??? func "`#!cpp void safeDeleteArray(T*& x)`" 76 | 77 | Like `safeDelete()`, but for arrays. 78 | 79 | 80 | ## Type names 81 | 82 | Useful for debugging templated code. Uses `typeid()` from ``. 83 | 84 | ??? func "`#!cpp std::string typeNameString(T& x)`" 85 | 86 | Returns the name of a type as a string. 87 | 88 | ??? func "`#!cpp std::string typeNameString(T* x)`" 89 | 90 | Like `typeNameString(T& x)`, but for pointers. 91 | -------------------------------------------------------------------------------- /include/geometrycentral/numerical/linear_algebra_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace geometrycentral { 7 | 8 | // Convenience typedefs 9 | 10 | // Nicer name for dynamic matrix 11 | template 12 | using Vector = Eigen::Matrix; 13 | 14 | // Nicer name for sparse matrix 15 | template 16 | using SparseMatrix = Eigen::SparseMatrix; 17 | 18 | // Nicer name for dense matrix 19 | template 20 | using DenseMatrix = Eigen::Matrix; 21 | 22 | 23 | /// Type alias for aligned std::vectors 24 | // template 25 | // using AlignedVector_T = std::vector>; 26 | 27 | /** 28 | * @brief Typename for an Eigen map to an raw buffer 29 | * 30 | * @tparam T Typename of the contained data 31 | * @tparam k Number of columns 32 | * @tparam Options Storage order \b Eigen::RowMajor or \b Eigen::ColMajor 33 | * @tparam Alignment Alignment of the underlying data 34 | */ 35 | template 36 | using EigenVectorMap_T = Eigen::Map, Alignment>; 37 | 38 | /** 39 | * @brief Typename for an Eigen map to a const raw buffer 40 | * 41 | * @tparam T Typename of the contained data 42 | * @tparam k Number of columns 43 | * @tparam Options Storage order \b Eigen::RowMajor or \b Eigen::ColMajor 44 | * @tparam Alignment Alignment of the underlying data 45 | */ 46 | template 47 | using ConstEigenVectorMap_T = Eigen::Map, Alignment>; 48 | 49 | } // namespace geometrycentral 50 | -------------------------------------------------------------------------------- /include/geometrycentral/numerical/suitesparse_utilities.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/numerical/linear_algebra_utilities.h" 4 | 5 | #include "Eigen/Sparse" 6 | 7 | // Suitesparse includes, as needed 8 | #ifdef GC_HAVE_SUITESPARSE 9 | #include "geometrycentral/numerical/suitesparse_utilities.h" 10 | #include 11 | #include 12 | #else 13 | #error "USING SUITESPARSE FEATURES, BUT DON'T HAVE SUITESPARSE" 14 | #endif 15 | 16 | 17 | namespace geometrycentral { 18 | 19 | 20 | enum class SType { UNSYMMETRIC = 0, SYMMETRIC }; 21 | 22 | // Suitesparse context class 23 | class CholmodContext { 24 | public: 25 | // constructor 26 | CholmodContext(); 27 | 28 | // destructor 29 | ~CholmodContext(); 30 | 31 | // set mode for Cholesky factorization 32 | void setSimplicial(); 33 | void setSupernodal(); 34 | 35 | // set LL vs LDL mode 36 | void setLL(); 37 | void setLDL(); 38 | 39 | // allows CholmodContext to be treated as a cholmod_common* 40 | operator cholmod_common*(); 41 | 42 | cholmod_common context; 43 | }; 44 | 45 | 46 | // Type helper. This type is 'double' if T == 'float', and T otherwise 47 | template 48 | struct SOLVER_ENTRYTYPE { 49 | typedef typename std::conditional::value, double, T>::type type; 50 | }; 51 | 52 | // === Conversion functions 53 | // Caller is responsible for deallocating 54 | 55 | // Convert a sparse matrix. Always returns a "full" matrix (stype=0, ie not triangular) 56 | template 57 | cholmod_sparse* toCholmod(Eigen::SparseMatrix& A, CholmodContext& context, 58 | SType stype = SType::UNSYMMETRIC); 59 | 60 | // Convert a vector 61 | template 62 | cholmod_dense* toCholmod(const Eigen::Matrix& v, CholmodContext& context); 63 | 64 | // Convert a vector 65 | template 66 | void toEigen(cholmod_dense* cVec, CholmodContext& context, Eigen::Matrix& xOut); 67 | 68 | } // namespace geometrycentral 69 | -------------------------------------------------------------------------------- /include/geometrycentral/pointcloud/local_triangulation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/pointcloud/point_cloud.h" 4 | #include "geometrycentral/pointcloud/point_position_geometry.h" 5 | 6 | namespace geometrycentral { 7 | namespace pointcloud { 8 | 9 | // In the local neighborhood of each triangle, construct the triangles in the local planar Delaunay triangulation which 10 | // touch the center point. 11 | // The optional perturbation heuristic can help to ensure that some triangles are generated in utterly degenerate cases, 12 | // such as many exactly coincident points. 13 | PointData>> buildLocalTriangulations(PointCloud& cloud, PointPositionGeometry& geom, 14 | bool withDegeneracyHeuristic = true); 15 | 16 | // Convert a local neighbor indexed list to global indices 17 | PointData>> 18 | handleToInds(PointCloud& cloud, const PointData>>& handleResult); 19 | 20 | std::vector> handleToFlatInds(PointCloud& cloud, 21 | const PointData>>& handleResult); 22 | 23 | 24 | } // namespace pointcloud 25 | } // namespace geometrycentral 26 | 27 | -------------------------------------------------------------------------------- /include/geometrycentral/pointcloud/neighborhoods.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/pointcloud/point_cloud.h" 4 | #include "geometrycentral/utilities/vector3.h" 5 | 6 | namespace geometrycentral { 7 | namespace pointcloud { 8 | 9 | // Represents a set of neighborhoods for a point cloud. Immutable after construction. 10 | // 11 | // TODO upgrade this to be much cleverer and use a flat representation w/ custom iterators and containers. 12 | // 13 | class Neighborhoods { 14 | public: 15 | // == Constructors 16 | Neighborhoods(PointCloud& cloud, const PointData& positions, unsigned int nNeighbors); 17 | // TODO constructor for fixed-ball neighborhood 18 | 19 | // == Members 20 | PointCloud& cloud; 21 | 22 | PointData> neighbors; 23 | 24 | // == Functions 25 | 26 | protected: 27 | // TODO 28 | // PointData listIndEnd; 29 | // std::vector flatNeighborList; 30 | }; 31 | 32 | 33 | } // namespace pointcloud 34 | } // namespace geometrycentral 35 | -------------------------------------------------------------------------------- /include/geometrycentral/pointcloud/point_cloud.ipp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/numerical/linear_algebra_utilities.h" 4 | 5 | namespace geometrycentral { 6 | namespace pointcloud { 7 | 8 | 9 | // Methods for getting number of mesh elements 10 | inline size_t PointCloud::nPoints() const { return nPointsCount; } 11 | 12 | // Capacities 13 | inline size_t PointCloud::nPointsCapacity() const { return nPointsCapacityCount; } 14 | 15 | // Detect dead elements 16 | inline bool PointCloud::pointIsDead(size_t iP) const { return !pointValid[iP]; } 17 | 18 | // Methods for iterating over mesh elements w/ range-based for loops =========== 19 | inline PointSet PointCloud::points() { return PointSet(this, 0, nPointsFillCount); } 20 | 21 | // Methods for accessing elements by index ===================================== 22 | // Note that these are only valid when the mesh is compressed. 23 | inline Point PointCloud::point(size_t index) { return Point(this, index); } 24 | 25 | // Misc utility methods ===================================== 26 | inline bool PointCloud::isCompressed() const { return isCompressedFlag; } 27 | 28 | 29 | } // namespace pointcloud 30 | } // namespace geometrycentral 31 | -------------------------------------------------------------------------------- /include/geometrycentral/pointcloud/point_cloud_element_types.ipp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Implementations for halfedge_mesh_types.ipp 4 | 5 | // Make the element types hashable (this _should_ be doable for just the parent class, but I couldn't sort out how) 6 | namespace std { 7 | template <> 8 | struct hash { 9 | std::size_t operator()(const geometrycentral::pointcloud::Point& e) const { return std::hash{}(e.getIndex()); } 10 | }; 11 | } // namespace std 12 | 13 | namespace geometrycentral { 14 | namespace pointcloud { 15 | 16 | // ========================================================== 17 | // ================ Point ================== 18 | // ========================================================== 19 | 20 | // Constructors 21 | // (see note in header, these should be inherited but aren't due to compiler issues) 22 | inline Point::Point() {} 23 | inline Point::Point(PointCloud* mesh_, size_t ind_) : Element(mesh_, ind_) {} 24 | // inline Point::Point(const DynamicElement& e) : Element(e.getMesh(), e.getIndex()) {} 25 | 26 | // Navigators 27 | //inline Halfedge Point::halfedge() const { return Halfedge(mesh, mesh->vHalfedge(ind)); } 28 | 29 | // Properties 30 | //inline bool Point::isBoundary() const { return !halfedge().twin().isInterior(); } 31 | 32 | // Navigation iterators 33 | //inline NavigationSetBase Point::incomingHalfedges() const { 34 | //return NavigationSetBase(halfedge().prevOrbitFace()); 35 | //} 36 | 37 | // == Range iterators 38 | inline bool PointRangeF::elementOkay(const PointCloud& mesh, size_t ind) { return !mesh.pointIsDead(ind); } 39 | 40 | // == Navigation iterators 41 | 42 | } // namespace pointcloud 43 | } // namespace geometrycentral 44 | -------------------------------------------------------------------------------- /include/geometrycentral/pointcloud/point_cloud_heat_solver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/numerical/linear_solvers.h" 4 | #include "geometrycentral/pointcloud/point_cloud.h" 5 | #include "geometrycentral/pointcloud/point_position_geometry.h" 6 | #include "geometrycentral/surface/edge_length_geometry.h" 7 | #include "geometrycentral/surface/heat_method_distance.h" 8 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 9 | #include "geometrycentral/surface/surface_mesh.h" 10 | #include "geometrycentral/utilities/vector2.h" 11 | #include "geometrycentral/utilities/vector3.h" 12 | 13 | namespace geometrycentral { 14 | 15 | #ifndef SHM_H 16 | #define SHM_H 17 | // Note: Duplicated in surface/signed_heat_method 18 | enum class LevelSetConstraint { None = 0, ZeroSet, Multiple }; 19 | 20 | struct SignedHeatOptions { 21 | bool preserveSourceNormals = false; 22 | LevelSetConstraint levelSetConstraint = LevelSetConstraint::ZeroSet; 23 | double softLevelSetWeight = -1.; 24 | }; 25 | 26 | #endif 27 | 28 | namespace pointcloud { 29 | 30 | class PointCloudHeatSolver { 31 | 32 | public: 33 | // === Constructor 34 | PointCloudHeatSolver(PointCloud& cloud, PointPositionGeometry& geom, double tCoef = 1.0); 35 | 36 | // === Methods 37 | 38 | // Solve for distance from a single vertex (or collection of points) 39 | PointData computeDistance(const Point& sourcePoint); 40 | PointData computeDistance(const std::vector& sourcePoints); 41 | 42 | // === Scalar Extension 43 | PointData extendScalars(const std::vector>& sources); 44 | 45 | // Compute parallel transport along shortest geodesics from sources at points 46 | PointData transportTangentVector(const Point& sourcePoint, const Vector2& sourceVector); 47 | PointData transportTangentVectors(const std::vector>& sources); 48 | 49 | // Compute the logarithmic map from a source point 50 | PointData computeLogMap(const Point& sourcePoint); 51 | 52 | // Solve for signed distance from a curve comprising a sequence of vertices. 53 | PointData computeSignedDistance(const std::vector>& curves, 54 | const PointData& cloudNormals, 55 | const SignedHeatOptions& options = SignedHeatOptions()); 56 | 57 | // === Options and parameters 58 | 59 | const double tCoef; // the time parameter used for heat flow, measured as time = tCoef * mean_edge_length^2 60 | // default: 1.0 61 | 62 | private: 63 | // === Members 64 | 65 | // Input cloud and geometry 66 | PointCloud& cloud; 67 | PointPositionGeometry& geom; 68 | 69 | // Parameters 70 | double shortTime; // the actual time used for heat flow computed from tCoef 71 | 72 | // Populate solvers lazily as needed 73 | void ensureHaveHeatDistanceWorker(); 74 | void ensureHaveVectorHeatSolver(); 75 | 76 | // Lazy strategy: bootstrap off the mesh version of the solver for distance solves on the tufted mesh 77 | std::unique_ptr heatDistanceWorker; 78 | 79 | // Solvers 80 | std::unique_ptr> vectorHeatSolver; 81 | }; 82 | 83 | } // namespace pointcloud 84 | } // namespace geometrycentral 85 | -------------------------------------------------------------------------------- /include/geometrycentral/pointcloud/point_cloud_io.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/pointcloud/point_cloud.h" 4 | #include "geometrycentral/pointcloud/point_position_geometry.h" 5 | 6 | namespace geometrycentral { 7 | namespace pointcloud { 8 | 9 | // === Readers === 10 | 11 | // Read from a file by name. Type can be optionally inferred from filename. 12 | std::tuple, std::unique_ptr> readPointCloud(std::string filename, 13 | std::string type = ""); 14 | 15 | // Same as above, but from an istream. Must specify type. 16 | std::tuple, std::unique_ptr> readPointCloud(std::istream& in, 17 | std::string type); 18 | 19 | 20 | // === Writers === 21 | 22 | // Write to file by name. Type can be optionally inferred from filename. 23 | void writePointCloud(PointCloud& cloud, PointPositionGeometry& geometry, std::string filename, std::string type = ""); 24 | 25 | // Same as above, to to an ostream. Must specify type. 26 | void writePointCloud(PointCloud& cloud, PointPositionGeometry& geometry, std::ostream& out, std::string type); 27 | 28 | } // namespace pointcloud 29 | } // namespace geometrycentral 30 | -------------------------------------------------------------------------------- /include/geometrycentral/pointcloud/point_cloud_logic_templates.ipp: -------------------------------------------------------------------------------- 1 | 2 | // === Helpers which will allow us abstract over types 3 | // The corresponding template declarations are given in halfedge_element_types.h 4 | 5 | namespace geometrycentral { 6 | 7 | 8 | template <> 9 | inline size_t nElements(pointcloud::PointCloud* mesh) { 10 | return mesh->nPoints(); 11 | } 12 | 13 | template <> 14 | inline size_t elementCapacity(pointcloud::PointCloud* mesh) { 15 | return mesh->nPointsCapacity(); 16 | } 17 | 18 | template <> 19 | inline size_t dataIndexOfElement(pointcloud::PointCloud* mesh, pointcloud::Point e) { 20 | return e.getIndex(); 21 | } 22 | 23 | template <> 24 | inline pointcloud::PointSet iterateElements(pointcloud::PointCloud* mesh) { 25 | return mesh->points(); 26 | } 27 | 28 | template <> 29 | inline std::list>& getExpandCallbackList(pointcloud::PointCloud* mesh) { 30 | return mesh->pointExpandCallbackList; 31 | } 32 | 33 | template <> 34 | inline std::list&)>>& 35 | getPermuteCallbackList(pointcloud::PointCloud* mesh) { 36 | return mesh->pointPermuteCallbackList; 37 | } 38 | 39 | template <> 40 | inline std::string typeShortName() { 41 | return "p"; 42 | } 43 | 44 | } // namespace geometrycentral 45 | -------------------------------------------------------------------------------- /include/geometrycentral/pointcloud/point_position_frame_geometry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/pointcloud/point_position_geometry.h" 4 | 5 | namespace geometrycentral { 6 | namespace pointcloud { 7 | 8 | // Extends the point position geometry by also specifying known normals & tangent frames 9 | 10 | class PointPositionFrameGeometry : public PointPositionGeometry { 11 | 12 | public: 13 | // Frame input should be {basisX, basisY, normal}, forming an orthonormal right-handed coordinate system. 14 | PointPositionFrameGeometry(PointCloud& cloud, const PointData& positions, 15 | const PointData>& frames); 16 | 17 | // Normals & tangents are stored in the `normals` and `tangentBasis` fields already present in the parent class 18 | 19 | protected: 20 | // Override the normal/tangent computation to do nothing; we just use the existing normals. 21 | void computeNormals() override; 22 | void computeTangentBasis() override; 23 | }; 24 | 25 | } // namespace pointcloud 26 | } // namespace geometrycentral 27 | -------------------------------------------------------------------------------- /include/geometrycentral/pointcloud/point_position_normal_geometry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/pointcloud/point_position_geometry.h" 4 | 5 | namespace geometrycentral { 6 | namespace pointcloud { 7 | 8 | // Extends the point position geometry by also specifying known normals 9 | 10 | class PointPositionNormalGeometry : public PointPositionGeometry { 11 | 12 | public: 13 | PointPositionNormalGeometry(PointCloud& cloud, const PointData& positions, const PointData& normals); 14 | 15 | // Normals are stored in the `normals` field already present in the parent class 16 | 17 | 18 | protected: 19 | // Override the normal computation to do nothing; we just use the existing normals. 20 | virtual void computeNormals() override; 21 | }; 22 | 23 | } // namespace pointcloud 24 | } // namespace geometrycentral 25 | -------------------------------------------------------------------------------- /include/geometrycentral/pointcloud/sample_cloud.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/pointcloud/point_cloud.h" 4 | #include "geometrycentral/pointcloud/point_position_geometry.h" 5 | #include "geometrycentral/surface/embedded_geometry_interface.h" 6 | #include "geometrycentral/surface/surface_point.h" 7 | 8 | #include 9 | 10 | namespace geometrycentral { 11 | namespace pointcloud { 12 | 13 | std::tuple, PointData, PointData> 14 | uniformlySamplePointsOnSurface(surface::SurfaceMesh& mesh, surface::EmbeddedGeometryInterface& geom, size_t nPts); 15 | 16 | 17 | } 18 | } // namespace geometrycentral 19 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/barycentric_coordinate_helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/surface_mesh.h" 4 | 5 | #include "geometrycentral/utilities/vector2.h" 6 | #include "geometrycentral/utilities/vector3.h" 7 | 8 | // Various utility arithmetic for working with barycentric coordinates 9 | // 10 | // All barycentric coordinates are assumed to be normalized (sum to 1) unless otherwise stated. 11 | 12 | // Useful references: 13 | // - "Barycentric Coordinates in Olympiad Geometry" 14 | // https://s3.amazonaws.com/aops-cdn.artofproblemsolving.com/resources/articles/bary.pdf 15 | // - https://math.stackexchange.com/a/2385307 (handy barycentric answer to "Determine if a line segment passes 16 | // “through” a triangle...") 17 | 18 | namespace geometrycentral { 19 | namespace surface { 20 | 21 | // Length of a vector 22 | double displacementLength2(Vector3 displacement, Vector3 triangleLengths); 23 | double displacementLength(Vector3 displacement, Vector3 triangleLengths); 24 | 25 | // Convert between cartesian and barycentric coordinates 26 | Vector3 cartesianVectorToBarycentric(const std::array& vertCoords, Vector2 faceVec); 27 | Vector2 barycentricDisplacementToCartesian(const std::array& vertCoords, Vector3 baryVec); 28 | 29 | // Normalize to sum to 1 (does nothing else) 30 | Vector3 normalizeBarycentric(Vector3 baryCoords); // Allows not-normalized input 31 | 32 | // Shift to sum to 0 (does nothing else) 33 | // (note that displacements between normalized barycentric coords should sum to 0) 34 | Vector3 normalizeBarycentricDisplacement(Vector3 baryVec); 35 | 36 | // Ensure all coordinates are in [0,1] and sum to 1 37 | Vector3 projectInsideTriangle(Vector3 baryCoords); // Allows not-normalized input 38 | 39 | // Is the point inside the triangle? 40 | bool isInsideTriangle(Vector3 baryCoords); 41 | 42 | // The index of the halfedge in a triangular face, 43 | int halfedgeIndexInTriangle(Halfedge he); 44 | 45 | // Given barycentric coordinates defined by treating refHe.vertex() as the `x` coordinate, permute to the canonical 46 | // coordinate ordering for the face, which has face.halfedge().vertex() as the `x` coordinate. 47 | Vector3 permuteBarycentricToCanonical(Vector3 baryCoords, Halfedge refHe); 48 | 49 | // Inverse of above 50 | Vector3 permuteBarycentricFromCanonical(Vector3 baryCoords, Halfedge refHe); 51 | 52 | 53 | } // namespace surface 54 | } // namespace geometrycentral 55 | 56 | #include "geometrycentral/surface/barycentric_coordinate_helpers.ipp" 57 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/boundary_first_flattening.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/numerical/linear_algebra_utilities.h" 4 | #include "geometrycentral/numerical/linear_solvers.h" 5 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 6 | #include "geometrycentral/surface/manifold_surface_mesh.h" 7 | 8 | namespace geometrycentral { 9 | namespace surface { 10 | 11 | VertexData parameterizeBFF(ManifoldSurfaceMesh& mesh, IntrinsicGeometryInterface& geo); 12 | VertexData parameterizeBFFfromScaleFactors(ManifoldSurfaceMesh& mesh, IntrinsicGeometryInterface& geo, 13 | const VertexData& boundaryScaleFactors); 14 | VertexData parameterizeBFFfromExteriorAngles(ManifoldSurfaceMesh& mesh, IntrinsicGeometryInterface& geo, 15 | const VertexData& exteriorAngles); 16 | 17 | class BFF { 18 | public: 19 | BFF(ManifoldSurfaceMesh& mesh_, IntrinsicGeometryInterface& geo_); 20 | 21 | VertexData flatten(); 22 | VertexData flattenFromScaleFactors(const VertexData& uBdy); 23 | VertexData flattenFromExteriorAngles(const VertexData& kBdy); 24 | VertexData flattenFromBoth(const Vector& uBdy, const Vector& kBdy); 25 | 26 | Vector dirichletToNeumann(const Vector& uBdy); 27 | Vector neumannToDirichlet(const Vector& kBdy); 28 | 29 | std::array, 2> computeBoundaryPositions(const Vector& uBdy, const Vector& kBdy); 30 | 31 | protected: 32 | ManifoldSurfaceMesh& mesh; 33 | IntrinsicGeometryInterface& geo; 34 | VertexData vIdx; 35 | VertexData iIdx, bIdx; 36 | 37 | 38 | SparseMatrix L, Lii, Lib, Lbb; 39 | std::unique_ptr> Liisolver; 40 | std::unique_ptr> Lsolver; 41 | Vector Omegai, Omegab; 42 | 43 | Vector isInterior; 44 | size_t nInterior, nBoundary, nVertices; 45 | 46 | BlockDecompositionResult Ldecomp; 47 | 48 | void ensureHaveLSolver(); 49 | }; 50 | 51 | } // namespace surface 52 | } // namespace geometrycentral 53 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/common_subdivision.ipp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/common_subdivision.h" 4 | 5 | namespace geometrycentral { 6 | namespace surface { 7 | 8 | template 9 | VertexData CommonSubdivision::interpolateAcrossA(const VertexData& dataA) const { 10 | checkMeshConstructed(); 11 | VertexData interp(*mesh); 12 | for (Vertex v : mesh->vertices()) { 13 | interp[v] = sourcePoints[v]->posA.interpolate(dataA); 14 | } 15 | return interp; 16 | } 17 | 18 | template 19 | VertexData CommonSubdivision::interpolateAcrossB(const VertexData& dataB) const { 20 | checkMeshConstructed(); 21 | VertexData interp(*mesh); 22 | for (Vertex v : mesh->vertices()) { 23 | interp[v] = sourcePoints[v]->posB.interpolate(dataB); 24 | } 25 | return interp; 26 | } 27 | 28 | template 29 | FaceData CommonSubdivision::copyFromA(const FaceData& dataA) const { 30 | checkMeshConstructed(); 31 | FaceData out(*mesh); 32 | for (Face f : mesh->faces()) { 33 | out[f] = dataA[sourceFaceA[f]]; 34 | } 35 | return out; 36 | } 37 | 38 | template 39 | FaceData CommonSubdivision::copyFromB(const FaceData& dataB) const { 40 | checkMeshConstructed(); 41 | FaceData out(*mesh); 42 | for (Face f : mesh->faces()) { 43 | out[f] = dataB[sourceFaceB[f]]; 44 | } 45 | return out; 46 | } 47 | 48 | 49 | } // namespace surface 50 | } // namespace geometrycentral 51 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/detect_symmetry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/geometry.h" 4 | 5 | #include 6 | 7 | namespace geometrycentral { 8 | namespace surface { 9 | 10 | struct SymmetryResult { 11 | bool symmetryFound = false; // was a symmetry found? is this data valid? 12 | std::vector canonicalVertices; // a representative entry from each 13 | // set of symmetry pairs 14 | VertexData> symmetrySet; // for each unique vertex, 15 | // all others vertices that 16 | // are symmetry pairs 17 | }; 18 | 19 | // Look for a symmetry about a mirror plane 20 | SymmetryResult detectSymmetryMirror(Geometry* geom, Vector3 planeNormal, Vector3 planePoint); 21 | 22 | // Look for a rotational symmetry 23 | SymmetryResult detectSymmetryRotation(Geometry* geom, Vector3 rotAxis, Vector3 rotPoint, int nSym); 24 | 25 | // Automatically search for the typical mirror and rotation symmetries about the 26 | // shape center 27 | // Returns any symmetry which is found. 28 | SymmetryResult detectSymmetryAuto(Geometry* geom); 29 | SymmetryResult detectSymmetryAutoRotation(Geometry* geom); 30 | SymmetryResult detectSymmetryAutoMirror(Geometry* geom); // Look for a symmetry about a mirror plane 31 | 32 | // Look for symmetry which is mirrored over the y and z planes 33 | SymmetryResult detectSymmetryDoubleMirror(Geometry* geom); 34 | 35 | } // namespace surface 36 | } // namespace geometrycentral 37 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/direction_fields.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 4 | #include "geometrycentral/surface/extrinsic_geometry_interface.h" 5 | #include "geometrycentral/surface/embedded_geometry_interface.h" 6 | 7 | 8 | namespace geometrycentral { 9 | namespace surface { 10 | 11 | // Compute useful geometric quantities relating to optimal (Levi-Civita) transport 12 | 13 | // === Compute smoothest direction fields 14 | 15 | // Smoothest unit-norm direction field 16 | VertexData computeSmoothestVertexDirectionField(IntrinsicGeometryInterface& geometry, int nSym = 1); 17 | 18 | // Like above, but with Dirichlet boundary conditions to align to hte boundary 19 | VertexData computeSmoothestBoundaryAlignedVertexDirectionField(IntrinsicGeometryInterface& geometry, int nSym = 1); 20 | 21 | FaceData computeSmoothestFaceDirectionField(IntrinsicGeometryInterface& geometry, int nSym = 1); 22 | 23 | FaceData computeSmoothestBoundaryAlignedFaceDirectionField(IntrinsicGeometryInterface& geometry, int nSym = 1); 24 | 25 | // Same as above, but aligned to curvatures 26 | VertexData computeCurvatureAlignedVertexDirectionField(ExtrinsicGeometryInterface& geometry, int nSym = 2); 27 | 28 | FaceData computeCurvatureAlignedFaceDirectionField(EmbeddedGeometryInterface& geometry, int nSym = 2); 29 | 30 | 31 | // Find singularities in direction fields 32 | FaceData computeFaceIndex(IntrinsicGeometryInterface& geometry, const VertexData& directionField, 33 | int nSym = 1); 34 | VertexData computeVertexIndex(IntrinsicGeometryInterface& geometry, const FaceData& directionField, 35 | int nSym = 1); 36 | 37 | } // namespace surface 38 | } // namespace geometrycentral 39 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/edge_length_geometry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 4 | #include "geometrycentral/surface/surface_mesh.h" 5 | 6 | #include 7 | 8 | namespace geometrycentral { 9 | namespace surface { 10 | 11 | 12 | class EdgeLengthGeometry : public IntrinsicGeometryInterface { 13 | 14 | public: 15 | EdgeLengthGeometry(SurfaceMesh& mesh_); 16 | EdgeLengthGeometry(SurfaceMesh& mesh_, const EdgeData& inputEdgeLengths); 17 | virtual ~EdgeLengthGeometry() {} 18 | 19 | // Construct a new geometry which is exactly the same as this one, on the same mesh. 20 | // This is a deep copy, no quantites are shared, etc. Require counts/computed quantities are not copied. 21 | std::unique_ptr copy(); 22 | 23 | // Construct a new geometry which is exactly the same as this one, on another mesh. 24 | // This is a deep copy, no quantites are shared, etc. Require counts/computed quantities are not copied. 25 | // The meshes must be in correspondence (have the same connectivity). 26 | std::unique_ptr reinterpretTo(SurfaceMesh& targetMesh); 27 | 28 | // The actual input data which defines the geometry 29 | // In a previous version of the library, this was a distinct field which got copied in to `edgeLengths`. However, now 30 | // they are simply aliases for the same buffer. 31 | EdgeData& inputEdgeLengths; 32 | 33 | // == Immediates 34 | double faceArea(Face f) const; 35 | double vertexDualArea(Vertex v) const; 36 | double cornerAngle(Corner c) const; 37 | double halfedgeCotanWeight(Halfedge he) const; 38 | double edgeCotanWeight(Edge e) const; 39 | double vertexGaussianCurvature(Vertex v) const; 40 | double faceCircumradius(Face f) const; 41 | 42 | 43 | protected: 44 | // Override the compute edge lengths method from intrinsic geometry. 45 | virtual void computeEdgeLengths() override; 46 | 47 | 48 | private: 49 | }; 50 | 51 | } // namespace surface 52 | } // namespace geometrycentral 53 | 54 | #include "geometrycentral/surface/edge_length_geometry.ipp" 55 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/exact_geodesic_helpers.ipp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2008 Danil Kirsanov, MIT License 2 | // (Modified to work in geometry-central. Original code can be found here: https://code.google.com/p/geodesic/) 3 | 4 | namespace geometrycentral { 5 | namespace surface { 6 | 7 | template 8 | void MemoryAllocator::clear() { 9 | reset(m_block_size, m_max_number_of_blocks); 10 | } 11 | 12 | template 13 | void MemoryAllocator::reset(unsigned block_size, unsigned max_number_of_blocks) { 14 | m_block_size = block_size; 15 | m_max_number_of_blocks = max_number_of_blocks; 16 | 17 | assert(m_block_size > 0); 18 | assert(m_max_number_of_blocks > 0); 19 | 20 | m_current_position = 0; 21 | 22 | m_storage.reserve(max_number_of_blocks); 23 | m_storage.resize(1); 24 | m_storage[0].resize(block_size); 25 | 26 | m_deleted.clear(); 27 | m_deleted.reserve(2 * block_size); 28 | }; 29 | 30 | template 31 | typename MemoryAllocator::pointer MemoryAllocator::allocate() { 32 | pointer result; 33 | if (m_deleted.empty()) { 34 | if (m_current_position + 1 >= m_block_size) { 35 | m_storage.push_back(std::vector()); 36 | m_storage.back().resize(m_block_size); 37 | m_current_position = 0; 38 | } 39 | result = &m_storage.back()[m_current_position]; 40 | ++m_current_position; 41 | } else { 42 | result = m_deleted.back(); 43 | m_deleted.pop_back(); 44 | } 45 | 46 | return result; 47 | }; 48 | 49 | template 50 | void MemoryAllocator::deallocate(pointer p) { 51 | if (m_deleted.size() < m_deleted.capacity()) { 52 | m_deleted.push_back(p); 53 | } 54 | }; 55 | 56 | } // namespace surface 57 | } // namespace geometrycentral 58 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/extrinsic_geometry_interface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 4 | #include "geometrycentral/surface/surface_mesh.h" 5 | #include "geometrycentral/utilities/vector2.h" 6 | 7 | #include 8 | 9 | namespace geometrycentral { 10 | namespace surface { 11 | 12 | 13 | class ExtrinsicGeometryInterface : public IntrinsicGeometryInterface { 14 | 15 | protected: 16 | // Constructor is protected, because this class is an interface which is not meant to be instantiated directly. 17 | // Instantiate it via some realization which encapsulates input data, like VertexPositionGeometry. 18 | ExtrinsicGeometryInterface(SurfaceMesh& mesh_); 19 | 20 | public: 21 | virtual ~ExtrinsicGeometryInterface() {} 22 | 23 | // Edge dihedral angle 24 | EdgeData edgeDihedralAngles; 25 | void requireEdgeDihedralAngles(); 26 | void unrequireEdgeDihedralAngles(); 27 | 28 | // Vertex mean curvature 29 | VertexData vertexMeanCurvatures; 30 | void requireVertexMeanCurvatures(); 31 | void unrequireVertexMeanCurvatures(); 32 | 33 | // Vertex min principal curvature 34 | VertexData vertexMinPrincipalCurvatures; 35 | void requireVertexMinPrincipalCurvatures(); 36 | void unrequireVertexMinPrincipalCurvatures(); 37 | 38 | // Vertex max principal curvature 39 | VertexData vertexMaxPrincipalCurvatures; 40 | void requireVertexMaxPrincipalCurvatures(); 41 | void unrequireVertexMaxPrincipalCurvatures(); 42 | 43 | // Vertex principal curvature direction 44 | VertexData vertexPrincipalCurvatureDirections; 45 | void requireVertexPrincipalCurvatureDirections(); 46 | void unrequireVertexPrincipalCurvatureDirections(); 47 | 48 | // Face principal curvature direction 49 | FaceData facePrincipalCurvatureDirections; 50 | void requireFacePrincipalCurvatureDirections(); 51 | void unrequireFacePrincipalCurvatureDirections(); 52 | 53 | protected: 54 | // Edge dihedral angle 55 | DependentQuantityD> edgeDihedralAnglesQ; 56 | virtual void computeEdgeDihedralAngles() = 0; 57 | 58 | // Vertex mean curvature 59 | DependentQuantityD> vertexMeanCurvaturesQ; 60 | virtual void computeVertexMeanCurvatures(); 61 | 62 | // Vertex min principal curvature 63 | DependentQuantityD> vertexMinPrincipalCurvaturesQ; 64 | virtual void computeVertexMinPrincipalCurvatures(); 65 | 66 | // Vertex max principal curvature 67 | DependentQuantityD> vertexMaxPrincipalCurvaturesQ; 68 | virtual void computeVertexMaxPrincipalCurvatures(); 69 | 70 | virtual void computePrincipalCurvatures( int whichCurvature, VertexData& kappa ); 71 | 72 | // Vertex principal curvature direction 73 | DependentQuantityD> vertexPrincipalCurvatureDirectionsQ; 74 | virtual void computeVertexPrincipalCurvatureDirections(); 75 | 76 | // Face principal curvature direction 77 | DependentQuantityD> facePrincipalCurvatureDirectionsQ; 78 | virtual void computeFacePrincipalCurvatureDirections(); 79 | }; 80 | 81 | } // namespace surface 82 | } // namespace geometrycentral 83 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/fast_marching_method.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 4 | #include "geometrycentral/utilities/utilities.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | namespace geometrycentral { 12 | namespace surface { 13 | 14 | VertexData FMMDistance(IntrinsicGeometryInterface& geometry, 15 | const std::vector>& initialDistances); 16 | 17 | 18 | } // namespace surface 19 | } // namespace geometrycentral 20 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/geodesic_centroidal_voronoi_tessellation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/signpost_intrinsic_triangulation.h" 4 | #include "geometrycentral/surface/trace_geodesic.h" 5 | #include "geometrycentral/surface/vector_heat_method.h" 6 | 7 | namespace geometrycentral { 8 | namespace surface { 9 | 10 | struct VoronoiResult { 11 | std::vector siteLocations; // sites at the centers of the Voronoi cells 12 | std::vector> siteDistributions; // soft indicator functions for each Voronoi cell 13 | bool hasDistributions = false; // is siteDistributions populated? 14 | }; 15 | 16 | struct VoronoiOptions { 17 | size_t nSites = 10; // number of sites to place 18 | std::vector initialSites; // desired locations for sites. If blank, locations are chosen randomly 19 | size_t iterations = 50; // number of iterations to run for 20 | double stepSize = 1; // step size for steps towards cell centers 21 | bool useDelaunay = true; // solve on an intrinsic Delaunay triangulation of the input 22 | bool computeDistributions = false; // return the indicator functions for each cell (`result.siteDistributions`) 23 | double tCoef = 1; // diffusion time for vector heat method 24 | size_t nSubIterations = 1; // number of iterations to use when computing Karcher means 25 | }; 26 | extern const VoronoiOptions defaultVoronoiOptions; 27 | 28 | VoronoiResult computeGeodesicCentroidalVoronoiTessellation(ManifoldSurfaceMesh& mesh, IntrinsicGeometryInterface& geom, 29 | VoronoiOptions options = defaultVoronoiOptions); 30 | 31 | } // namespace surface 32 | } // namespace geometrycentral 33 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/geometry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/base_geometry_interface.h" 4 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 5 | #include "geometrycentral/surface/extrinsic_geometry_interface.h" 6 | #include "geometrycentral/surface/embedded_geometry_interface.h" 7 | 8 | #include "geometrycentral/surface/edge_length_geometry.h" 9 | #include "geometrycentral/surface/vertex_position_geometry.h" 10 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/halfedge_factories.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/surface_mesh_factories.h" 4 | 5 | #include 6 | #include 7 | 8 | // NOTE these are DEPRECATED! Prefer the versions in surface_mesh_factories.h 9 | 10 | // These methods construct the connectivity and geometry of a mesh simultaneously. 11 | 12 | namespace geometrycentral { 13 | namespace surface { 14 | 15 | // Assumes manifoldness, errors our if not 16 | std::tuple, std::unique_ptr> 17 | makeHalfedgeAndGeometry(const std::vector>& polygons, const std::vector vertexPositions); 18 | 19 | 20 | // Like above, but with known twin connectivity 21 | std::tuple, std::unique_ptr> 22 | makeHalfedgeAndGeometry(const std::vector>& polygons, 23 | const std::vector>>& twins, 24 | const std::vector vertexPositions); 25 | 26 | 27 | // Same a above, but constructs a potentially-nonmanifold surface mesh 28 | std::tuple, std::unique_ptr> 29 | makeGeneralHalfedgeAndGeometry(const std::vector>& polygons, 30 | const std::vector vertexPositions); 31 | 32 | 33 | // Like above, but with known twin connectivity 34 | std::tuple, std::unique_ptr> 35 | makeGeneralHalfedgeAndGeometry(const std::vector>& polygons, 36 | const std::vector>>& twins, 37 | const std::vector vertexPositions); 38 | 39 | } // namespace surface 40 | } // namespace geometrycentral 41 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/halfedge_mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/manifold_surface_mesh.h" 4 | 5 | // TODO guard this with a compile flag 6 | // #pragma error 7 | 8 | namespace geometrycentral { 9 | namespace surface { 10 | using HalfedgeMesh = ManifoldSurfaceMesh; 11 | } 12 | } // namespace geometrycentral 13 | 14 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/heat_method_distance.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/numerical/linear_solvers.h" 4 | #include "geometrycentral/surface/edge_length_geometry.h" 5 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 6 | #include "geometrycentral/surface/surface_mesh.h" 7 | #include "geometrycentral/surface/surface_point.h" 8 | #include "geometrycentral/utilities/vector2.h" 9 | #include "geometrycentral/utilities/vector3.h" 10 | 11 | namespace geometrycentral { 12 | 13 | 14 | namespace pointcloud { 15 | class PointCloudHeatSolver; // forward declare to friend below 16 | } 17 | 18 | namespace surface { 19 | 20 | // One-off function to compute distance from a vertex 21 | VertexData heatMethodDistance(IntrinsicGeometryInterface& geom, Vertex v); 22 | 23 | 24 | // Stateful class. Allows efficient repeated solves 25 | class HeatMethodDistanceSolver { 26 | 27 | public: 28 | // === Constructor 29 | HeatMethodDistanceSolver(IntrinsicGeometryInterface& geom, double tCoef = 1.0, bool useRobustLaplacian = false); 30 | 31 | // === Methods 32 | 33 | // Solve for distance from a single vertex 34 | VertexData computeDistance(const Vertex& sourceVert); 35 | 36 | // Solve for distance from a collection of vertices 37 | VertexData computeDistance(const std::vector& sourceVerts); 38 | 39 | // Solve for distance from a single surface point 40 | VertexData computeDistance(const SurfacePoint& sourcePoint); 41 | 42 | // Solve for distance from a collection of surface points 43 | VertexData computeDistance(const std::vector& sourcePoints); 44 | 45 | // Solve for distance from a custom right hand side 46 | // (returns WITHOUT performing constant shift to 0) 47 | Vector computeDistanceRHS(const Vector& rhs); 48 | 49 | // === Options and parameters 50 | 51 | const double tCoef; // the time parameter used for heat flow, measured as time = tCoef * mean_edge_length^2 52 | // default: 1.0 53 | const bool useRobustLaplacian; 54 | 55 | private: 56 | // === Members 57 | 58 | // Input mesh and geometry 59 | SurfaceMesh& mesh; 60 | IntrinsicGeometryInterface& geom; 61 | 62 | // Tufted cover mesh & geometry (these will only be popualted if useRobustLaplacian = true) 63 | std::unique_ptr tuftedMesh; 64 | std::unique_ptr tuftedIntrinsicGeom; 65 | 66 | // Parameters 67 | double shortTime; // the actual time used for heat flow computed from tCoef 68 | 69 | // Solvers 70 | std::unique_ptr> heatSolver; 71 | std::unique_ptr> poissonSolver; 72 | 73 | // Helpers 74 | 75 | // Return either the input mesh/geometry, or the tufted mesh/geometry, based on whether useRobustLaplacian=true 76 | SurfaceMesh& getMesh(); 77 | IntrinsicGeometryInterface& getGeom(); 78 | 79 | // The point cloud version bootstraps off of this code 80 | friend class pointcloud::PointCloudHeatSolver; 81 | }; 82 | 83 | 84 | } // namespace surface 85 | } // namespace geometrycentral 86 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/integer_coordinates_intrinsic_triangulation.ipp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/include/geometrycentral/surface/integer_coordinates_intrinsic_triangulation.ipp -------------------------------------------------------------------------------- /include/geometrycentral/surface/intersection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/embedded_geometry_interface.h" 4 | #include "geometrycentral/utilities/utilities.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | namespace geometrycentral { 12 | namespace surface { 13 | 14 | struct SurfaceIntersectionResult { 15 | std::vector points; 16 | std::vector> edges; 17 | bool hasIntersections; 18 | }; 19 | 20 | SurfaceIntersectionResult selfIntersections(EmbeddedGeometryInterface& geometry); 21 | SurfaceIntersectionResult intersections(EmbeddedGeometryInterface& geometry1, EmbeddedGeometryInterface& geometry2, 22 | bool selfCheck = false); 23 | 24 | } // namespace surface 25 | } // namespace geometrycentral 26 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/intrinsic_mollification.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 4 | #include "geometrycentral/surface/surface_mesh.h" 5 | 6 | namespace geometrycentral { 7 | namespace surface { 8 | 9 | // A simple but effective method to resolve geometrically degenerate faces in a mesh, by adding a tiny epsilon to 10 | // intrinsic edge lengths. 11 | 12 | // Offset via a factor relative to the mean edge length in the mesh 13 | void mollifyIntrinsic(SurfaceMesh& mesh, EdgeData& edgeLengths, double relativeFactor = 1e-6); 14 | 15 | // Offset via an absolute factor 16 | void mollifyIntrinsicAbsolute(SurfaceMesh& mesh, EdgeData& edgeLengths, double absoluteFactor); 17 | 18 | } // namespace surface 19 | } // namespace geometrycentral 20 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/intrinsic_triangulation.ipp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/intrinsic_triangulation.h" 4 | 5 | namespace geometrycentral { 6 | namespace surface { 7 | 8 | inline bool IntrinsicTriangulation::isFixed(Edge e) const { 9 | if (e.isBoundary()) return true; 10 | if (markedEdges.size() > 0 && markedEdges[e]) return true; 11 | return false; 12 | } 13 | 14 | inline bool IntrinsicTriangulation::isOnFixedEdge(Vertex v) const { 15 | for (Edge e : v.adjacentEdges()) { 16 | if (isFixed(e)) return true; 17 | } 18 | return false; 19 | } 20 | 21 | 22 | template 23 | VertexData IntrinsicTriangulation::sampleFromInput(const VertexData& dataOnInput) { 24 | VertexData output(mesh); 25 | for (Vertex v : mesh.vertices()) { 26 | output[v] = vertexLocations[v].interpolate(dataOnInput); 27 | } 28 | return output; 29 | } 30 | 31 | template 32 | VertexData IntrinsicTriangulation::restrictToInput(const VertexData& dataOnIntrinsic) { 33 | VertexData output(inputMesh); 34 | for (Vertex v : mesh.vertices()) { 35 | if (vertexLocations[v].type == SurfacePointType::Vertex) { 36 | output[vertexLocations[v].vertex] = dataOnIntrinsic[v]; 37 | } 38 | } 39 | return output; 40 | } 41 | 42 | inline std::array IntrinsicTriangulation::layoutDiamond(Halfedge iHe) { 43 | 44 | // Conventions: 45 | // - iHe points from vertex 2 to vertex 0, other vertices are numbered ccw 46 | // - iHe is incident on face A, other is face B 47 | // - halfedges within face are numbered CCW as A0, A1, A2 (etc), 48 | // starting with iHe and twin(iHe) 49 | // - When we lay out the triangle, p3 is at the origin and 50 | // edge 3-0 is along the X-axis 51 | // - flips is always ccw, so iHe points from vertex 3 --> 1 after 52 | 53 | // Gather index values 54 | Halfedge iHeA0 = iHe; 55 | Halfedge iHeA1 = iHeA0.next(); 56 | Halfedge iHeA2 = iHeA1.next(); 57 | Halfedge iHeB0 = iHe.twin(); 58 | Halfedge iHeB1 = iHeB0.next(); 59 | Halfedge iHeB2 = iHeB1.next(); 60 | 61 | // Gather length values 62 | double l01 = edgeLengths[iHeA1.edge()]; 63 | double l12 = edgeLengths[iHeA2.edge()]; 64 | double l23 = edgeLengths[iHeB1.edge()]; 65 | double l30 = edgeLengths[iHeB2.edge()]; 66 | double l02 = edgeLengths[iHeA0.edge()]; 67 | 68 | // Lay out the vertices of the diamond 69 | Vector2 p3{0., 0.}; 70 | Vector2 p0{l30, 0.}; 71 | Vector2 p2 = layoutTriangleVertex(p3, p0, l02, l23); // involves more arithmetic than strictly necessary 72 | Vector2 p1 = layoutTriangleVertex(p2, p0, l01, l12); 73 | 74 | return {p0, p1, p2, p3}; 75 | } 76 | 77 | inline double IntrinsicTriangulation::shortestEdge(Face f) const { 78 | Halfedge he = f.halfedge(); 79 | double lA = edgeLengths[he.edge()]; 80 | he = he.next(); 81 | double lB = edgeLengths[he.edge()]; 82 | he = he.next(); 83 | double lC = edgeLengths[he.edge()]; 84 | return std::fmin(std::fmin(lA, lB), lC); 85 | } 86 | 87 | inline std::array IntrinsicTriangulation::vertexCoordinatesInTriangle(Face face) { 88 | return {Vector2{0., 0.}, halfedgeVectorsInFace[face.halfedge()], 89 | -halfedgeVectorsInFace[face.halfedge().next().next()]}; 90 | } 91 | 92 | } // namespace surface 93 | } // namespace geometrycentral 94 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/manifold_surface_mesh.ipp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/numerical/linear_algebra_utilities.h" 4 | 5 | namespace geometrycentral { 6 | namespace surface { 7 | 8 | template 9 | ManifoldSurfaceMesh::ManifoldSurfaceMesh(const Eigen::MatrixBase& faces) 10 | : ManifoldSurfaceMesh(unpackMatrixToStdVector(faces.template cast())) {} 11 | 12 | 13 | } // namespace surface 14 | } // namespace geometrycentral 15 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/mesh_graph_algorithms.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 4 | #include "geometrycentral/surface/manifold_surface_mesh.h" 5 | 6 | #include 7 | 8 | namespace geometrycentral { 9 | namespace surface { 10 | 11 | // Returns the shortest path between two vertices via Dijkstra's algorithm 12 | // Uses sparse data structures, so complexity is output-sensitive (but still O(n logn) worst time of course) 13 | // Returns an empty vector if the target is unreachable 14 | std::vector shortestEdgePath(IntrinsicGeometryInterface& geom, Vertex startVert, Vertex endVert); 15 | 16 | // Return the Dijstra distance to all vertices within the ball radius 17 | std::unordered_map vertexDijkstraDistanceWithinRadius(IntrinsicGeometryInterface& geom, Vertex startVert, double ballRad); 18 | 19 | // Find a subset of edges which connects all vertices 20 | // Return value holds 'true' for an edge if it is in the tree 21 | EdgeData minimalSpanningTree(IntrinsicGeometryInterface& geom); 22 | 23 | // Returns a set of edges which connect all vertices 24 | // Note: Uses an MST+pruning approach to find short trees in O(N logN), but not guaranteed to be minimal; that's an 25 | // NP-hard Steiner tree problem 26 | EdgeData spanningTreeBetweenVertices(IntrinsicGeometryInterface& geom, const std::vector& requiredVertices); 27 | 28 | 29 | 30 | } // namespace surface 31 | } // namespace geometrycentral 32 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/mesh_ray_tracer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/geometry.h" 4 | 5 | 6 | #include "nanort/nanort.h" 7 | 8 | namespace geometrycentral { 9 | namespace surface { 10 | 11 | struct RayHitResult { 12 | bool hit; 13 | double tHit; 14 | Face face; 15 | Vector3 baryCoords; 16 | }; 17 | 18 | class MeshRayTracer { 19 | public: 20 | // Creates a new tracer and builds the acceleration structure 21 | MeshRayTracer(Geometry* geometry); 22 | 23 | // Build the BVH for the current geometry. Called automatically after 24 | // construction, re-call if mesh changes. 25 | void buildBVH(); 26 | 27 | // Trace a ray. Note: geometry should be identical to when BVH was constructed 28 | RayHitResult trace(Vector3 start, Vector3 dir); 29 | 30 | private: 31 | HalfedgeMesh* mesh; 32 | Geometry* geometry; 33 | 34 | // Data for the BVH 35 | std::vector rawPositions; 36 | std::vector rawFaces; 37 | nanort::BVHAccel, nanort::TriangleSAHPred, 38 | nanort::TriangleIntersector> 39 | accel; 40 | 41 | double tFar; 42 | }; 43 | 44 | } // namespace surface 45 | }; // namespace geometrycentral 46 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/parameterize.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 5 | #include "geometrycentral/surface/manifold_surface_mesh.h" 6 | 7 | 8 | namespace geometrycentral { 9 | namespace surface { 10 | 11 | // === High level interface 12 | 13 | // Paramerize a disk-like surface (without introducing any cuts or cones) 14 | VertexData parameterizeDisk(ManifoldSurfaceMesh& mesh, IntrinsicGeometryInterface& geometry); 15 | 16 | // Paramerize a surface with the specified cut (which must render the surface a disk) 17 | // TODO not implemented 18 | CornerData parameterize(SurfaceMesh& mesh, IntrinsicGeometryInterface& geometry, const EdgeData& cut); 19 | 20 | }} 21 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/poisson_disk_sampler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/surface_point.h" 4 | #include "geometrycentral/surface/trace_geodesic.h" 5 | #include "geometrycentral/surface/vertex_position_geometry.h" 6 | 7 | namespace geometrycentral { 8 | namespace surface { 9 | 10 | struct PoissonDiskOptions { 11 | double minDist = 1.0; // minimum distance r between samples 12 | int kCandidates = 30; // number of candidate points chosen from the (r,2r)-annulus around each sample 13 | std::vector pointsToAvoid; 14 | double minDistAvoidance = 1.0; // radius of avoidance 15 | bool use3DAvoidance = true; 16 | }; 17 | 18 | class PoissonDiskSampler { 19 | 20 | public: 21 | // ===== Constructor 22 | 23 | // Currently this takes in a ManifoldSurfaceMesh, because the random sampling uses the traceGeodesic() function, 24 | // which relies on there being a well-defined tangent space at every vertex. However, perhaps for each non-manifold 25 | // vertex, we could choose a random outgoing halfedge. 26 | PoissonDiskSampler(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geometry); 27 | 28 | // ===== The main function: Sample the surface using Poisson Disk Sampling. 29 | std::vector sample(const PoissonDiskOptions& options = PoissonDiskOptions()); 30 | 31 | private: 32 | // ===== Members 33 | ManifoldSurfaceMesh& mesh; 34 | VertexPositionGeometry& geometry; 35 | 36 | int kCandidates; // number of candidate points chosen from the (r,2r)-annulus around each sample 37 | std::vector pointsToAvoid; 38 | 39 | double rMinDist; // the minimum distance between samples 40 | double sideLength; // side length of each bucket 41 | 42 | std::vector faceFromEachComponent; // holds one face for each connected component in the mesh 43 | std::vector activeList; // holds candidate points 44 | std::vector samples; // holds finalized samples 45 | 46 | // Spatial lookup structure 47 | typedef std::array SpatialKey; 48 | std::map spatialBuckets; 49 | Vector3 mapCenter; 50 | 51 | // ===== Utility and auxiliary functions 52 | std::tuple boundingBox(); 53 | void storeFaceFromEachComponent(); 54 | SurfacePoint generateCandidate(const SurfacePoint& xi) const; 55 | void addNewSample(const SurfacePoint& sample); 56 | SpatialKey positionKey(const Vector3& position) const; 57 | void addPointToSpatialLookup(const Vector3& newPos); 58 | void addPointToSpatialLookupWithRadius(const SurfacePoint& newPoint, double radius = 0, bool use3DAvoidance = true); 59 | bool isCandidateValid(const SurfacePoint& candidate) const; 60 | void sampleOnConnectedComponent(const Face& f); 61 | void clearData(); 62 | }; 63 | 64 | } // namespace surface 65 | } // namespace geometrycentral -------------------------------------------------------------------------------- /include/geometrycentral/surface/polygon_mesh_heat_solver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/numerical/linear_solvers.h" 4 | #include "geometrycentral/surface/embedded_geometry_interface.h" 5 | #include "geometrycentral/surface/signed_heat_method.h" 6 | #include "geometrycentral/surface/surface_mesh.h" 7 | 8 | namespace geometrycentral { 9 | namespace surface { 10 | 11 | class PolygonMeshHeatSolver { 12 | 13 | public: 14 | // === Constructor 15 | PolygonMeshHeatSolver(EmbeddedGeometryInterface& geom, double tCoef = 1.0); 16 | 17 | // === Methods 18 | 19 | // Solve for distance from a single vertex (or collection of points) 20 | VertexData computeDistance(const Vertex& sourceVert); 21 | VertexData computeDistance(const std::vector& sourceVerts); 22 | 23 | // Scalar Extension 24 | VertexData extendScalars(const std::vector>& sources); 25 | 26 | // Compute parallel transport along shortest geodesics from sources at points 27 | VertexData transportTangentVector(const Vertex& sourceVert, const Vector2& sourceVector); 28 | VertexData transportTangentVectors(const std::vector>& sources); 29 | 30 | // Solve for signed distance from a curve comprising a sequence of vertices. 31 | VertexData computeSignedDistance(const std::vector>& curves, 32 | const LevelSetConstraint& levelSetConstraint = LevelSetConstraint::ZeroSet); 33 | 34 | // === Options and parameters 35 | 36 | const double tCoef; // the time parameter used for heat flow, measured as time = tCoef * maxDiagonalLength 37 | // default: 1.0 38 | 39 | private: 40 | // === Members 41 | 42 | // Input mesh and geometry 43 | SurfaceMesh& mesh; 44 | EmbeddedGeometryInterface& geom; 45 | 46 | // Parameters 47 | double shortTime; 48 | 49 | // Solvers 50 | void ensureHaveScalarHeatSolver(); 51 | void ensureHaveVectorHeatSolver(); 52 | void ensureHavePoissonSolver(); 53 | std::unique_ptr>> vectorHeatSolver; 54 | std::unique_ptr> scalarHeatSolver, poissonSolver; 55 | SparseMatrix massMat, laplaceMat; 56 | 57 | // Helpers 58 | void buildSignedCurveSource(const std::vector& curve, Vector>& X0) const; 59 | double computeAverageValue(const std::vector>& curves, const Vector& u); 60 | }; 61 | 62 | } // namespace surface 63 | } // namespace geometrycentral -------------------------------------------------------------------------------- /include/geometrycentral/surface/polygon_mesh_helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/embedded_geometry_interface.h" 4 | 5 | namespace geometrycentral { 6 | namespace surface { 7 | 8 | // Helper functions for Bunge et al. "Polygon Laplacian Made Simple" (2020). 9 | // Copyright (C) 2020 Astrid Bunge, Philipp Herholz, Misha Kazhdan, Mario Botsch, MIT license 10 | // (Modified to work in geometry-central. Original code can be found here: 11 | // https://github.com/mbotsch/polygon-laplacian) 12 | 13 | Eigen::VectorXd simplePolygonVirtualVertex(const Eigen::MatrixXd& poly); 14 | 15 | // Helper functions for de Goes et al. "Discrete Differential Operators on Polygonal Meshes" (2020). 16 | // Use of this source code is governed by a LGPL-3.0 license. 17 | // (Modified to work in geometry-central. Original code can be found here: 18 | // https://github.com/DGtal-team/DGtal/blob/master/src/DGtal/dec/PolygonalCalculus.h) 19 | 20 | Eigen::MatrixXd polygonAveragingMatrix(const Face& f); 21 | 22 | Eigen::MatrixXd polygonDerivativeMatrix(const Face& f); 23 | 24 | // helpers to the helper functions: generic linear algebra stuff, though probably wouldn't find much use elsewhere 25 | // so keeping them here -- also they use Eigen::Vectors here for matrix-multiply compatibility. 26 | Eigen::Matrix3d bracket(const Eigen::Vector3d& n); 27 | 28 | Eigen::Vector3d project(const Eigen::Vector3d& u, const Eigen::Vector3d& n); 29 | 30 | Eigen::MatrixXd kroneckerWithI2(const Eigen::MatrixXd& M); 31 | 32 | } // namespace surface 33 | } // namespace geometrycentral -------------------------------------------------------------------------------- /include/geometrycentral/surface/polygon_soup_mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/simple_polygon_mesh.h" 4 | 5 | namespace geometrycentral { 6 | namespace surface { 7 | 8 | using PolygonSoupMesh = SimplePolygonMesh; 9 | 10 | } // namespace surface 11 | } // namespace geometrycentral 12 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/quadric_error_simplification.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/manifold_surface_mesh.h" 4 | #include "geometrycentral/surface/mutation_manager.h" 5 | #include "geometrycentral/surface/vertex_position_geometry.h" 6 | 7 | #include "geometrycentral/utilities/elementary_geometry.h" 8 | 9 | #include 10 | 11 | namespace geometrycentral { 12 | namespace surface { 13 | class Quadric { 14 | public: 15 | Quadric(); 16 | Quadric(const Eigen::Matrix3d& A_, const Eigen::Vector3d& b_, double c_); 17 | Quadric(const Quadric& Q1, const Quadric& Q2); 18 | double cost(const Eigen::Vector3d& v); 19 | Eigen::Vector3d optimalPoint(); 20 | 21 | Quadric operator+=(const Quadric& Q); 22 | 23 | protected: 24 | Eigen::Matrix3d A; 25 | Eigen::Vector3d b; 26 | double c; 27 | 28 | friend Quadric operator+(const Quadric& Q1, const Quadric& Q2); 29 | }; 30 | 31 | Quadric operator+(const Quadric& Q1, const Quadric& Q2); 32 | 33 | void quadricErrorSimplify(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo, double tol = 0.05); 34 | void quadricErrorSimplify(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo, double tol, MutationManager& mm); 35 | 36 | } // namespace surface 37 | } // namespace geometrycentral 38 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/signpost_intrinsic_triangulation.ipp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/signpost_intrinsic_triangulation.h" 4 | 5 | namespace geometrycentral { 6 | namespace surface { 7 | 8 | inline double SignpostIntrinsicTriangulation::standardizeAngle(Vertex vert, double angle) const { 9 | if (vert.isBoundary()) { 10 | // can't wrap around at vertices 11 | return angle; 12 | } 13 | return std::fmod(angle, vertexAngleSums[vert]); 14 | } 15 | 16 | inline Vector2 SignpostIntrinsicTriangulation::halfedgeVector(Halfedge he) const { 17 | double edgeAngle = signpostAngle[he]; 18 | double scaleFac = 1.0 / vertexAngleScaling(he.vertex()); 19 | Vector2 traceVec = Vector2::fromAngle(edgeAngle * scaleFac) * edgeLengths[he.edge()]; 20 | return traceVec; 21 | } 22 | 23 | inline Vector2 SignpostIntrinsicTriangulation::rescaledVertexVector(Vertex v, double angle, double len) const { 24 | double scaleFac = 1.0 / vertexAngleScaling(v); 25 | Vector2 traceVec = Vector2::fromAngle(angle * scaleFac) * len; 26 | return traceVec; 27 | } 28 | 29 | inline double SignpostIntrinsicTriangulation::vertexAngleScaling(Vertex v) const { 30 | return vertexAngleSums[v] / (v.isBoundary() ? M_PI : 2. * M_PI); 31 | } 32 | 33 | 34 | } // namespace surface 35 | } // namespace geometrycentral 36 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/simple_idt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include "geometrycentral/surface/manifold_surface_mesh.h" 5 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 6 | 7 | 8 | namespace geometrycentral { 9 | namespace surface { 10 | 11 | // A simplified interface for flip-based intrinsic Delaunay triangulations, which only supports a length-based 12 | // representation. See signpost_intrinsic_triangulation.h for much more advanced functionality 13 | 14 | // Modifies both the underlying mesh and edge lengths to make the intrinsic Delaunay 15 | enum class FlipType { Euclidean = 0, Hyperbolic }; 16 | size_t flipToDelaunay(SurfaceMesh& mesh, EdgeData& edgeLengths, FlipType flipType = FlipType::Euclidean, 17 | double delaunayEPS = 1e-6); 18 | 19 | } // namespace surface 20 | } // namespace geometrycentral 21 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/stripe_patterns.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/embedded_geometry_interface.h" 4 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 5 | #include 6 | 7 | namespace geometrycentral { 8 | namespace surface { 9 | 10 | // Implementation of "Stripe Patterns on Surfaces" [Knoppel et al. 2015] 11 | // Based on Keenan Crane's original implementation available here: 12 | // https://www.cs.cmu.edu/~kmcrane/Projects/StripePatterns/code.zip 13 | 14 | // Takes as input a geometry along with vertex-based frequencies and a line field (2-RoSy) and outputs a 2\pi-periodic 15 | // function defined on triangle corners such that the 0 (mod 2\pi) 16 | // Isolines of this function are stripes perpendicular to the direction field spaced according to the target frequencies 17 | std::tuple, FaceData, FaceData> 18 | computeStripePattern(IntrinsicGeometryInterface& geometry, const VertexData& frequencies, 19 | const VertexData& directionField); 20 | 21 | // Extracts the zero (mod 2pi) level set of the function values defined on the corners 22 | // Returns a list of vertices and edges suitable for rendering (e.g. with Polyscope) 23 | // (requires access to explicit vertex positions) 24 | std::tuple, std::vector>> 25 | extractPolylinesFromStripePattern(EmbeddedGeometryInterface& geometry, const CornerData& values, 26 | const FaceData& stripeIndices, const FaceData& fieldIndices, 27 | const VertexData& directionField, bool connectOnSingularities); 28 | 29 | // Runs both of the above functions (per-corner data computation and polyline extraction) 30 | // can optionally connect isolines separated by a singularity using a directionField alignment heuristic 31 | std::tuple, std::vector>> 32 | computeStripePatternPolylines(EmbeddedGeometryInterface& geometry, const VertexData& frequencies, 33 | const VertexData& directionField, bool connectIsolinesOnSingularities = true); 34 | 35 | } // namespace surface 36 | } // namespace geometrycentral 37 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/subdivide.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/manifold_surface_mesh.h" 4 | #include "geometrycentral/surface/mutation_manager.h" 5 | #include "geometrycentral/surface/vertex_position_geometry.h" 6 | 7 | namespace geometrycentral { 8 | namespace surface { 9 | 10 | // Subdivide to form quad mesh 11 | // Note: these do not take MutationManager arguments since MutationManager only supports triangular meshes at the moment 12 | void linearSubdivide(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo); 13 | void catmullClarkSubdivide(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo); 14 | 15 | void loopSubdivide(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo); 16 | void loopSubdivide(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo, MutationManager& mm); 17 | 18 | } // namespace surface 19 | } // namespace geometrycentral 20 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/surface_centers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 4 | #include "geometrycentral/surface/vector_heat_method.h" 5 | #include "geometrycentral/surface/manifold_surface_mesh.h" 6 | 7 | namespace geometrycentral { 8 | namespace surface { 9 | 10 | // Find a center of a collection of points at vertices 11 | SurfacePoint findCenter(ManifoldSurfaceMesh& mesh, IntrinsicGeometryInterface& geom, const std::vector& vertexPts, int p = 2); 12 | SurfacePoint findCenter(ManifoldSurfaceMesh& mesh, IntrinsicGeometryInterface& geom, VectorHeatMethodSolver& solver, 13 | const std::vector& vertexPts, int p = 2); 14 | 15 | // Find a center of distribution 16 | SurfacePoint findCenter(ManifoldSurfaceMesh& mesh, IntrinsicGeometryInterface& geom, const VertexData& distribution, int p = 2); 17 | SurfacePoint findCenter(ManifoldSurfaceMesh& mesh, IntrinsicGeometryInterface& geom, VectorHeatMethodSolver& solver, 18 | const VertexData& distribution, int p = 2); 19 | 20 | } // namespace surface 21 | } // namespace geometrycentral 22 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/surface_mesh_factories.ipp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | namespace geometrycentral { 5 | namespace surface { 6 | 7 | template 8 | std::tuple, std::unique_ptr> 9 | makeManifoldSurfaceMeshAndGeometry(const Eigen::MatrixBase& vMat, const Eigen::MatrixBase& fMat) { 10 | 11 | std::unique_ptr mesh(new ManifoldSurfaceMesh(fMat)); 12 | std::unique_ptr geometry(new VertexPositionGeometry(*mesh, vMat)); 13 | 14 | return std::make_tuple(std::move(mesh), std::move(geometry)); 15 | } 16 | 17 | template 18 | std::tuple, std::unique_ptr> 19 | makeSurfaceMeshAndGeometry(const Eigen::MatrixBase& vMat, const Eigen::MatrixBase& fMat) { 20 | 21 | std::unique_ptr mesh(new SurfaceMesh(fMat)); 22 | std::unique_ptr geometry(new VertexPositionGeometry(*mesh, vMat)); 23 | 24 | return std::make_tuple(std::move(mesh), std::move(geometry)); 25 | } 26 | 27 | } // namespace surface 28 | } // namespace geometrycentral 29 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/surgery.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/manifold_surface_mesh.h" 4 | #include "geometrycentral/surface/mesh_graph_algorithms.h" 5 | #include "geometrycentral/utilities/disjoint_sets.h" 6 | 7 | namespace geometrycentral { 8 | namespace surface { 9 | 10 | 11 | // Returns a brand new mesh cut along the edges, and a map from new halfedges --> old halfedges which can be used to 12 | // evaluate correspondence. All halfedges on the resulting mesh will have come from some halfedge on the original mesh, 13 | // with the exception of new exterior halfedges along the cut, which will be set to Halfedge(). 14 | std::tuple, HalfedgeData> cutAlongEdges(ManifoldSurfaceMesh& mesh, 15 | const EdgeData& cut); 16 | 17 | } // namespace surface 18 | } // namespace geometrycentral 19 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/trace_geodesic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 4 | #include "geometrycentral/surface/manifold_surface_mesh.h" 5 | #include "geometrycentral/surface/surface_point.h" 6 | 7 | 8 | namespace geometrycentral { 9 | namespace surface { 10 | 11 | 12 | struct TraceGeodesicResult { 13 | SurfacePoint endPoint; // the point the trace ended at 14 | std::vector pathPoints; // all points along the path, including start and end 15 | Vector2 endingDir; // the incoming direction to the final point, in its tangent space 16 | bool hitBoundary = false; // did the trace stop early because we hit a boundary? 17 | bool hasPath = false; // is pathPoints populated? 18 | double length; // length of traced path (generally equals norm of traceVec, 19 | // unless tracing stopped early) 20 | }; 21 | 22 | struct TraceOptions { 23 | bool includePath = false; 24 | bool errorOnProblem = false; 25 | EdgeData* barrierEdges = nullptr; // if set, traces will stop when they hit barrier edges 26 | size_t maxIters = INVALID_IND; 27 | }; 28 | extern const TraceOptions defaultTraceOptions; 29 | 30 | // These trace routines will always yield a path that looks like: 31 | // - the start point 32 | // - 0 or more edge crossings 33 | // - the end point in a face (unless allowEndOnEdge set) 34 | // the only exception is tracing on a surface with boundary, which may yield an end point on a edge if the trace hit the 35 | // boundary 36 | 37 | // Trace from a surface point, and a vector in the canonical tangent space of that point (represented as a vector in 38 | // that tangent space) 39 | TraceGeodesicResult traceGeodesic(IntrinsicGeometryInterface& geom, SurfacePoint startP, Vector2 traceVec, 40 | const TraceOptions& traceOptions = defaultTraceOptions); 41 | 42 | 43 | // Trace from a point in barycentric coordinates inside some face, where the trace vector is a barycentric displacement 44 | // (which must sum to 0) 45 | TraceGeodesicResult traceGeodesic(IntrinsicGeometryInterface& geom, Face startFace, Vector3 startBary, 46 | Vector3 traceBaryVec, const TraceOptions& traceOptions = defaultTraceOptions); 47 | 48 | 49 | // For a trace which was expected to end very near targetVertex, try to clean up the end of the path to end directly at 50 | // targetVertex 51 | // TODO currently DOES NOT fix up traceResult.endingDir, so that field is invalid after calling 52 | // Return value indicates success. If true, the resulting ends in the 1-ring of targetVertex as expected. 53 | bool trimTraceResult(TraceGeodesicResult& traceResult, Vertex targetVertex); 54 | 55 | } // namespace surface 56 | } // namespace geometrycentral 57 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/transfer_functions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/numerical/linear_algebra_types.h" 4 | #include "geometrycentral/numerical/linear_solvers.h" 5 | #include "geometrycentral/surface/intrinsic_triangulation.h" 6 | #include "geometrycentral/surface/manifold_surface_mesh.h" 7 | #include "geometrycentral/surface/meshio.h" 8 | #include "geometrycentral/surface/simple_polygon_mesh.h" 9 | #include "geometrycentral/surface/surface_point.h" 10 | #include "geometrycentral/utilities/elementary_geometry.h" 11 | 12 | #include 13 | 14 | namespace geometrycentral { 15 | namespace surface { 16 | 17 | enum class TransferMethod { Pointwise = 0, L2 }; 18 | 19 | class AttributeTransfer { 20 | 21 | public: 22 | // Constructor 23 | AttributeTransfer(CommonSubdivision& cs, VertexPositionGeometry& geomA); 24 | AttributeTransfer(IntrinsicTriangulation& intTri); 25 | 26 | // Members 27 | CommonSubdivision& cs; 28 | 29 | SparseMatrix P_A; // maps scalars at vertices to CS 30 | SparseMatrix P_B; // maps scalars at vertices to CS 31 | SparseMatrix M_CS_Galerkin; // galerkin mass matrix on common subdivision 32 | 33 | std::unique_ptr> AtoB_L2_Solver; 34 | std::unique_ptr> BtoA_L2_Solver; 35 | 36 | 37 | // Methods 38 | 39 | // High-level 40 | VertexData transferAtoB(const VertexData& valuesOnA, TransferMethod method); 41 | VertexData transferBtoA(const VertexData& valuesOnB, TransferMethod method); 42 | 43 | // Low-level 44 | VertexData transferAtoB_Pointwise(const VertexData& valuesOnA); 45 | VertexData transferAtoB_L2(const VertexData& valuesOnA); 46 | 47 | VertexData transferBtoA_Pointwise(const VertexData& valuesOnB); 48 | VertexData transferBtoA_L2(const VertexData& valuesOnB); 49 | 50 | // Prepare data 51 | std::pair, SparseMatrix> constructAtoBMatrices() const; 52 | std::pair, SparseMatrix> constructBtoAMatrices() const; 53 | }; 54 | 55 | 56 | // One-off functions 57 | VertexData transferAtoB(CommonSubdivision& cs, VertexPositionGeometry& geomA, 58 | const VertexData& valuesOnA, TransferMethod method); 59 | VertexData transferAtoB(IntrinsicTriangulation& intTri, const VertexData& valuesOnA, 60 | TransferMethod method); 61 | 62 | VertexData transferBtoA(CommonSubdivision& cs, VertexPositionGeometry& geomA, 63 | const VertexData& valuesOnB, TransferMethod method); 64 | VertexData transferBtoA(IntrinsicTriangulation& intTri, const VertexData& valuesOnB, 65 | TransferMethod method); 66 | 67 | 68 | } // namespace surface 69 | } // namespace geometrycentral 70 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/tufted_laplacian.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/numerical/linear_algebra_utilities.h" 4 | #include "geometrycentral/surface/embedded_geometry_interface.h" 5 | #include "geometrycentral/surface/surface_mesh.h" 6 | 7 | namespace geometrycentral { 8 | namespace surface { 9 | 10 | 11 | std::tuple, SparseMatrix> 12 | buildTuftedLaplacian(SurfaceMesh& mesh, EmbeddedGeometryInterface& geom, double relativeMollificationFactor = 0.); 13 | 14 | // Modifies the input mesh and edge lengths to be the tufted cover! 15 | void buildIntrinsicTuftedCover(SurfaceMesh& mesh, EdgeData& edgeLengths, 16 | EmbeddedGeometryInterface* posGeom = nullptr); 17 | 18 | 19 | } // namespace surface 20 | } // namespace geometrycentral 21 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/uniformize.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 5 | #include "geometrycentral/surface/manifold_surface_mesh.h" 6 | 7 | 8 | namespace geometrycentral { 9 | namespace surface { 10 | 11 | // Uniformize a topological disk to the flat plane w/ "natural" (u = 0 Dirichlet) boundary conditions 12 | EdgeData uniformizeDisk(ManifoldSurfaceMesh& mesh, IntrinsicGeometryInterface& geometry, 13 | bool withEdgeFlips = true); 14 | 15 | 16 | } // namespace surface 17 | } // namespace geometrycentral 18 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/vector_heat_method.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/numerical/linear_solvers.h" 4 | #include "geometrycentral/surface/heat_method_distance.h" 5 | #include "geometrycentral/surface/intrinsic_geometry_interface.h" 6 | #include "geometrycentral/surface/surface_mesh.h" 7 | #include "geometrycentral/surface/surface_point.h" 8 | #include "geometrycentral/surface/trace_geodesic.h" 9 | #include "geometrycentral/utilities/vector2.h" 10 | #include "geometrycentral/utilities/vector3.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace geometrycentral { 18 | namespace surface { 19 | 20 | // Stateful class. Allows efficient repeated solves 21 | 22 | class VectorHeatMethodSolver { 23 | 24 | public: 25 | // === Constructor 26 | VectorHeatMethodSolver(IntrinsicGeometryInterface& geom, double tCoef = 1.0); 27 | 28 | 29 | // === Scalar Extension 30 | VertexData extendScalar(const std::vector>& sources); 31 | VertexData extendScalar(const std::vector>& sources); 32 | 33 | 34 | // === Tangent Vector Extension 35 | VertexData transportTangentVector(Vertex sourceVert, Vector2 sourceVec); 36 | VertexData transportTangentVectors(const std::vector>& sources); 37 | VertexData transportTangentVectors(const std::vector>& sources); 38 | 39 | 40 | // === The Logarithmic map 41 | VertexData computeLogMap(const Vertex& sourceVert, double vertexDistanceShift = 0.); 42 | VertexData computeLogMap(const SurfacePoint& sourceP); 43 | 44 | // === Options and parameters 45 | const double tCoef; // the time parameter used for heat flow, measured as time = tCoef * mean_edge_length^2 46 | // default: 1.0 47 | 48 | // === Low-level queries 49 | VertexData scalarDiffuse(const VertexData& rhs); // call scalarHeatSolver on rhs 50 | VertexData> 51 | vectorDiffuse(const VertexData>& rhs); // call vectorHeatSolver on rhs 52 | VertexData poissonSolve(const VertexData& rhs); // call poissonSolver on rhs 53 | 54 | private: 55 | // === Members 56 | 57 | // Basics 58 | // TODO FIXME this should probably become a manfiold surface mesh, at least for now 59 | SurfaceMesh& mesh; 60 | IntrinsicGeometryInterface& geom; 61 | 62 | // Parameters 63 | double shortTime; // the actual time used for heat flow computed from tCoef 64 | 65 | // Solvers 66 | std::unique_ptr> scalarHeatSolver; 67 | std::unique_ptr>> vectorHeatSolver; 68 | std::unique_ptr> poissonSolver; 69 | SparseMatrix massMat; 70 | 71 | // Helpers 72 | void ensureHaveScalarHeatSolver(); 73 | void ensureHaveVectorHeatSolver(); 74 | void ensureHavePoissonSolver(); 75 | 76 | void addVertexOutwardBall(Vertex v, Vector>& distGradRHS); 77 | }; 78 | 79 | 80 | } // namespace surface 81 | } // namespace geometrycentral 82 | -------------------------------------------------------------------------------- /include/geometrycentral/surface/vertex_position_geometry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/embedded_geometry_interface.h" 4 | #include "geometrycentral/surface/surface_mesh.h" 5 | 6 | #include 7 | 8 | namespace geometrycentral { 9 | namespace surface { 10 | 11 | 12 | class VertexPositionGeometry : public EmbeddedGeometryInterface { 13 | 14 | public: 15 | // Construct empty -- all positions initially set to the origin 16 | VertexPositionGeometry(SurfaceMesh& mesh_); 17 | 18 | // Construct from positions 19 | VertexPositionGeometry(SurfaceMesh& mesh_, const VertexData& inputVertexPositions); 20 | 21 | // Construct from positions (stored in an Eigen matrix) 22 | template 23 | VertexPositionGeometry(SurfaceMesh& mesh_, const Eigen::MatrixBase& vertexPositions); 24 | 25 | // Boring destructor 26 | virtual ~VertexPositionGeometry() {} 27 | 28 | // Construct a new geometry which is exactly the same as this one, on the same mesh. 29 | // This is a deep copy, no quantites are shared, etc. Require counts/computed quantities are not copied. 30 | std::unique_ptr copy(); 31 | 32 | // Construct a new geometry which is exactly the same as this one, on another mesh. 33 | // This is a deep copy, no quantites are shared, etc. Require counts/computed quantities are not copied. 34 | // The meshes must be in correspondence (have the same connectivity). 35 | std::unique_ptr reinterpretTo(SurfaceMesh& targetMesh); 36 | 37 | 38 | // == Members 39 | 40 | // The actual input data which defines the geometry 41 | // In a previous version of the library, this was a distinct field which got copied in to `vertexPositions`. However, 42 | // now they are simply aliases for the same buffer. 43 | VertexData& inputVertexPositions; 44 | 45 | // == Immediates 46 | double edgeLength(Edge e) const; 47 | double faceArea(Face f) const; 48 | double vertexDualArea(Vertex v) const; 49 | double cornerAngle(Corner c) const; 50 | double halfedgeCotanWeight(Halfedge he) const; 51 | double edgeCotanWeight(Edge e) const; 52 | Vector3 faceNormal(Face f) const; 53 | Vector3 halfedgeVector(Halfedge he) const; 54 | double edgeDihedralAngle(Edge e) const; 55 | double vertexMeanCurvature(Vertex v) const; 56 | double vertexGaussianCurvature(Vertex v) const; 57 | double vertexMinPrincipalCurvature(Vertex v) const; 58 | double vertexMaxPrincipalCurvature(Vertex v) const; 59 | Vector3 vertexDualMeanCurvatureNormal(Vertex v) const; 60 | 61 | protected: 62 | // Override the compute vertex positions method for embedded geometry 63 | virtual void computeVertexPositions() override; 64 | 65 | double vertexPrincipalCurvature(int whichCurvature, Vertex v) const; 66 | 67 | private: 68 | }; 69 | 70 | } // namespace surface 71 | } // namespace geometrycentral 72 | 73 | #include "geometrycentral/surface/vertex_position_geometry.ipp" 74 | -------------------------------------------------------------------------------- /include/geometrycentral/utilities/combining_hash_functions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // Some useful hash functions that should really be in the standard library 7 | // WARNING: It is technically illegal to specialize things in std, so this might 8 | // break in the future. However, this works for now and is immensely practical. 9 | // Avoid including in public-facing headers. 10 | 11 | // See https://stackoverflow.com/questions/7110301/generic-hash-for-tuples-in-unordered-map-unordered-set 12 | // and elsewhere 13 | 14 | namespace std { 15 | 16 | // Combinie hash values in a not-completely-evil way 17 | // (I think this is strategy boost uses) 18 | namespace { 19 | template 20 | inline void hash_combine(std::size_t& seed, T const& v) { 21 | seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); 22 | } 23 | 24 | // Recursive template code derived from Matthieu M. 25 | template ::value - 1> 26 | struct HashValueImpl { 27 | static void apply(size_t& seed, Tuple const& tuple) { 28 | HashValueImpl::apply(seed, tuple); 29 | hash_combine(seed, std::get(tuple)); 30 | } 31 | }; 32 | 33 | template 34 | struct HashValueImpl { 35 | static void apply(size_t& seed, Tuple const& tuple) { hash_combine(seed, std::get<0>(tuple)); } 36 | }; 37 | } // namespace 38 | 39 | // Hash for tuples 40 | template 41 | struct hash> { 42 | size_t operator()(std::tuple const& tt) const { 43 | size_t seed = 0; 44 | HashValueImpl>::apply(seed, tt); 45 | return seed; 46 | } 47 | }; 48 | 49 | 50 | // Hash for pairs 51 | template 52 | struct hash> { 53 | std::size_t operator()(const std::pair& x) const { 54 | size_t hVal = std::hash()(x.first); 55 | hash_combine(hVal, x.second); 56 | return hVal; 57 | } 58 | }; 59 | }; // namespace std 60 | -------------------------------------------------------------------------------- /include/geometrycentral/utilities/dependent_quantity.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Would be great not to include Eigen here, but seems necessary to specialize clearBuffer() in the .ipp. Suggestions 4 | // for an easy workaround are welcome. 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | namespace geometrycentral { 14 | 15 | class DependentQuantity { 16 | 17 | public: 18 | DependentQuantity(std::function evaluateFunc_, std::vector& listToJoin) 19 | : evaluateFunc(evaluateFunc_) { 20 | listToJoin.push_back(this); 21 | } 22 | 23 | virtual ~DependentQuantity(){}; 24 | 25 | std::function evaluateFunc; 26 | bool computed = false; 27 | int requireCount = 0; 28 | bool clearable = true; // if false, clearing does nothing 29 | 30 | // Compute the quantity, if we don't have it already 31 | void ensureHave(); 32 | 33 | // Compute the quantity if we need it and don't have it already 34 | void ensureHaveIfRequired(); 35 | 36 | // Note that something will reqiure this quantity (increments a count of such requirements), 37 | // and ensure that we have this quantity 38 | void require(); 39 | 40 | // Decrement the count of requirements of this quantity 41 | void unrequire(); 42 | 43 | // Clear out the underlying quantity to reduce memory usage 44 | virtual void clearIfNotRequired() = 0; 45 | }; 46 | 47 | // Wrapper class which manages a dependency graph of quantities. Templated on the underlying type of the data. 48 | template 49 | class DependentQuantityD : public DependentQuantity { 50 | 51 | public: 52 | DependentQuantityD(){}; 53 | virtual ~DependentQuantityD(){}; 54 | 55 | DependentQuantityD(D* dataBuffer_, std::function evaluateFunc_, std::vector& listToJoin) 56 | : DependentQuantity(evaluateFunc_, listToJoin), dataBuffer(dataBuffer_) {} 57 | 58 | D* dataBuffer = nullptr; 59 | 60 | // Clear out the underlying quantity to reduce memory usage 61 | virtual void clearIfNotRequired() override; 62 | }; 63 | 64 | } // namespace geometrycentral 65 | 66 | #include "geometrycentral/utilities/dependent_quantity.ipp" 67 | -------------------------------------------------------------------------------- /include/geometrycentral/utilities/dependent_quantity.ipp: -------------------------------------------------------------------------------- 1 | namespace geometrycentral { 2 | 3 | inline void DependentQuantity::ensureHaveIfRequired() { 4 | if (requireCount > 0) { 5 | ensureHave(); 6 | } 7 | } 8 | 9 | inline void DependentQuantity::ensureHave() { 10 | 11 | // If the quantity is already populated, early out 12 | if (computed) { 13 | return; 14 | } 15 | 16 | // Compute this quantity 17 | evaluateFunc(); 18 | 19 | computed = true; 20 | }; 21 | 22 | inline void DependentQuantity::require() { 23 | requireCount++; 24 | ensureHave(); 25 | } 26 | 27 | inline void DependentQuantity::unrequire() { 28 | requireCount--; 29 | 30 | if (requireCount < 0) { 31 | throw std::logic_error("Quantity was unrequire()'d more than than it was require()'d"); 32 | requireCount = 0; 33 | } 34 | } 35 | 36 | // Helper functions to clear data 37 | // Note: if/when we start using more types in these quantities, we might need to generalize this mechanism. But for the 38 | // current set of uses (scalars, MeshData<>, Eigen types), this works just fine. 39 | namespace { 40 | 41 | // General method: call a clear function 42 | template 43 | void clearBuffer(T* buffer) { 44 | buffer->clear(); 45 | } 46 | 47 | // Scalars 48 | void clearBuffer(double* buffer) {} 49 | void clearBuffer(size_t* buffer) {} 50 | void clearBuffer(int* buffer) {} 51 | 52 | 53 | // Eigen sparse matrices 54 | template 55 | void clearBuffer(Eigen::SparseMatrix* buffer) { 56 | *buffer = Eigen::SparseMatrix(); 57 | } 58 | 59 | // any unique_ptr<> type 60 | template 61 | void clearBuffer(std::unique_ptr

* buffer) { 62 | buffer->reset(); 63 | } 64 | 65 | // Array of any otherwise clearable type 66 | template 67 | void clearBuffer(std::array* buffer) { 68 | for (size_t i = 0; i < N; i++) { 69 | // Recurse to an approriate version of this template 70 | A* elem = (*buffer)[i]; 71 | clearBuffer(elem); 72 | } 73 | } 74 | 75 | // Pair of clearable types 76 | template 77 | void clearBuffer(std::pair* buffer) { 78 | A elemA = buffer->first; 79 | clearBuffer(elemA); 80 | B elemB = buffer->second; 81 | clearBuffer(elemB); 82 | } 83 | 84 | } // namespace 85 | 86 | template 87 | void DependentQuantityD::clearIfNotRequired() { 88 | if (clearable && requireCount <= 0 && dataBuffer != nullptr && computed) { 89 | clearBuffer(dataBuffer); 90 | computed = false; 91 | } 92 | } 93 | 94 | } // namespace geometrycentral 95 | -------------------------------------------------------------------------------- /include/geometrycentral/utilities/disjoint_sets.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace geometrycentral { 7 | 8 | class DisjointSets { 9 | public: 10 | // Constructor 11 | DisjointSets(size_t n_); 12 | 13 | // Find parent of element x 14 | size_t find(size_t x); 15 | 16 | // Union by rank 17 | void merge(size_t x, size_t y); 18 | 19 | private: 20 | // Member variables 21 | size_t n; 22 | std::vector parent; 23 | std::vector rank; 24 | }; 25 | 26 | // Slight generalization of a disjoint set, which can track "marked" sets. 27 | class MarkedDisjointSets { 28 | public: 29 | // Constructor 30 | MarkedDisjointSets(size_t n_); 31 | 32 | // Find parent of element x 33 | size_t find(size_t x); 34 | 35 | // Union by rank 36 | // If either set in the union is marked, the result is marked 37 | void merge(size_t x, size_t y); 38 | 39 | // Mark/unmark a set 40 | void mark(size_t x); 41 | void unmark(size_t x); 42 | 43 | // Check if a set is marked 44 | bool isMarked(size_t x); 45 | 46 | private: 47 | // Member variables 48 | size_t n; 49 | std::vector parent; 50 | std::vector rank; 51 | std::vector marked; 52 | }; 53 | 54 | } // namespace geometrycentral 55 | -------------------------------------------------------------------------------- /include/geometrycentral/utilities/knn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/utilities/vector3.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace geometrycentral { 10 | 11 | 12 | class NearestNeighborFinder { 13 | public: 14 | NearestNeighborFinder(const std::vector& points); 15 | ~NearestNeighborFinder(); 16 | 17 | // Return the indices of points in the input set 18 | std::vector kNearest(Vector3 query, size_t k); 19 | 20 | // Source index refers to a point in the input set, which will not appear in the output 21 | std::vector kNearestNeighbors(size_t sourceInd, size_t k); 22 | 23 | // Return all neighbors within ball. `rad` should be the actual distance, not squared distance; we square internally 24 | // to pass to nanoflann 25 | std::vector radiusSearch(Vector3 query, double rad); 26 | 27 | private: 28 | // "PImpl" idiom 29 | class KNNImpl; 30 | std::unique_ptr impl; 31 | }; 32 | 33 | 34 | } // namespace geometrycentral 35 | -------------------------------------------------------------------------------- /include/geometrycentral/utilities/timing.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define NOW (std::chrono::steady_clock::now()) 8 | #define START_TIMING(name) auto generated_timer_777_##name = NOW; 9 | 10 | // pretty-prints to stdout 11 | #define FINISH_TIMING_PRINT(name) \ 12 | auto generated_timer_777_elapsed_##name = \ 13 | std::chrono::duration_cast(NOW - generated_timer_777_##name); \ 14 | std::cout << "--- TIMER RESULT: section " << #name << " took " \ 15 | << pretty_time(generated_timer_777_elapsed_##name.count()) << std::endl; 16 | 17 | // reports as integer wth microseconds 18 | #define FINISH_TIMING(name) \ 19 | (std::chrono::duration_cast(NOW - generated_timer_777_##name).count()) 20 | // reports as double with seconds 21 | #define FINISH_TIMING_SEC(name) \ 22 | (((double)std::chrono::duration_cast(NOW - generated_timer_777_##name).count())/(1e6)) 23 | 24 | inline std::string pretty_time(long long microsec) { 25 | // Useful constants 26 | long long MILLIS = 1000; 27 | long long SECOND = 1000 * MILLIS; 28 | long long MINUTE = 60 * SECOND; 29 | long long HOUR = 60 * MINUTE; 30 | 31 | char buffer[256]; 32 | 33 | // Greater than 1 hour 34 | if (microsec > HOUR) { 35 | long long hours = microsec / HOUR; 36 | microsec -= hours * HOUR; 37 | long long minutes = microsec / MINUTE; 38 | 39 | sprintf(buffer, "%lld hr, %lld min", hours, minutes); 40 | 41 | return std::string(buffer); 42 | } 43 | 44 | // Greater than 1 minute 45 | else if (microsec > MINUTE) { 46 | long long minutes = microsec / MINUTE; 47 | microsec -= minutes * MINUTE; 48 | long long seconds = minutes / SECOND; 49 | 50 | sprintf(buffer, "%lld min, %lld sec", minutes, seconds); 51 | 52 | return std::string(buffer); 53 | } 54 | 55 | // Greater than 1 second 56 | else if (microsec > SECOND) { 57 | double seconds = microsec / (double)SECOND; 58 | 59 | sprintf(buffer, "%.2f sec", seconds); 60 | 61 | return std::string(buffer); 62 | } 63 | 64 | // Greater than 1 millisecond 65 | else if (microsec > MILLIS) { 66 | double millis = microsec / (double)MILLIS; 67 | 68 | sprintf(buffer, "%.2f ms", millis); 69 | 70 | return std::string(buffer); 71 | } 72 | 73 | // Times less than 1 millisecond 74 | else { 75 | double micros = microsec; 76 | 77 | sprintf(buffer, "%.2f µs", micros); // note the nifty unicode \mu. I 78 | // apologize when this breaks something 79 | // later. 80 | 81 | return std::string(buffer); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /include/geometrycentral/utilities/vector3.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/utilities/utilities.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace geometrycentral { 12 | 13 | // Note: this class avoids any constructors so that it is a POD type 14 | struct Vector3 { 15 | // Components 16 | double x, y, z; 17 | 18 | 19 | static Vector3 zero() { return Vector3{0., 0., 0.}; } 20 | static Vector3 constant(double c) { return Vector3{c, c, c}; } 21 | static Vector3 infinity() { 22 | const double inf = ::std::numeric_limits::infinity(); 23 | return Vector3{inf, inf, inf}; 24 | } 25 | static Vector3 undefined() { 26 | const double nan = ::std::numeric_limits::quiet_NaN(); 27 | return Vector3{nan, nan, nan}; 28 | } 29 | 30 | // Access-by-index 31 | double& operator[](int index) { return (&x)[index]; } 32 | double operator[](int index) const { return (&x)[index]; }; 33 | 34 | // Overloaded operators 35 | Vector3 operator+(const Vector3& v) const; 36 | Vector3 operator-(const Vector3& v) const; 37 | Vector3 operator*(double s) const; 38 | Vector3 operator/(double s) const; 39 | Vector3& operator+=(const Vector3& v); 40 | Vector3& operator-=(const Vector3& v); 41 | Vector3& operator*=(const double& s); 42 | Vector3& operator/=(const double& s); 43 | bool operator==(const Vector3& v) const; 44 | bool operator!=(const Vector3& v) const; 45 | const Vector3 operator-() const; 46 | 47 | // Other functions 48 | Vector3 rotateAround(Vector3 axis, double theta) const; 49 | Vector3 removeComponent(const Vector3& unitDir) const; // removes component in direction D 50 | std::array buildTangentBasis() const; // build a basis orthogonal to D (need not be unit already) 51 | Vector3 normalize() const; 52 | Vector3 normalizeCutoff(double mag = 0.) const; 53 | Vector3 unit() const; 54 | 55 | double norm() const; 56 | double norm2() const; 57 | 58 | bool isFinite() const; 59 | bool isDefined() const; 60 | }; 61 | 62 | // Scalar multiplication 63 | template 64 | Vector3 operator*(const T s, const Vector3& v); 65 | 66 | // Printing 67 | ::std::ostream& operator<<(::std::ostream& output, const Vector3& v); 68 | ::std::istream& operator>>(::std::istream& intput, Vector3& v); 69 | 70 | double norm(const Vector3& v); 71 | double norm2(const Vector3& v); 72 | 73 | Vector3 normalize(const Vector3& v); 74 | Vector3 normalizeCutoff(const Vector3& v, double mag = 0.); 75 | Vector3 unit(const Vector3& v); 76 | 77 | Vector3 cross(const Vector3& u, const Vector3& v); 78 | double angle(const Vector3& u, const Vector3& v); 79 | double angleInPlane(const Vector3& u, const Vector3& v, const Vector3& normal); 80 | double dot(const Vector3& u, const Vector3& v); 81 | double sum(const Vector3& u); 82 | bool isfinite(const Vector3& u); // break camel case rule to match std 83 | bool isDefined(const Vector3& u); 84 | Vector3 clamp(const Vector3& val, const Vector3& low, const Vector3& high); 85 | Vector3 componentwiseMin(const Vector3& u, const Vector3& v); 86 | Vector3 componentwiseMax(const Vector3& u, const Vector3& v); 87 | 88 | 89 | } // namespace geometrycentral 90 | 91 | namespace std { 92 | template <> 93 | struct hash { 94 | std::size_t operator()(const geometrycentral::Vector3& v) const; 95 | }; 96 | 97 | // overload for std string 98 | std::string to_string(geometrycentral::Vector3 value); 99 | 100 | } // namespace std 101 | 102 | #include "vector3.ipp" 103 | -------------------------------------------------------------------------------- /src/numerical/linear_algebra_utilities.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/numerical/linear_algebra_utilities.h" 2 | 3 | namespace geometrycentral { 4 | 5 | SparseMatrix complexToReal(const SparseMatrix>& m) { 6 | 7 | size_t nRow = m.rows(); 8 | size_t nCol = m.cols(); 9 | 10 | SparseMatrix realM(2 * nRow, 2 * nCol); 11 | std::vector> triplets; 12 | 13 | for (int k = 0; k < m.outerSize(); ++k) { 14 | for (typename SparseMatrix>::InnerIterator it(m, k); it; ++it) { 15 | 16 | 17 | std::complex val = it.value(); 18 | size_t iRow = it.row(); 19 | size_t iCol = it.col(); 20 | 21 | triplets.emplace_back(2 * iRow + 0, 2 * iCol + 0, val.real()); 22 | triplets.emplace_back(2 * iRow + 0, 2 * iCol + 1, -val.imag()); 23 | triplets.emplace_back(2 * iRow + 1, 2 * iCol + 0, val.imag()); 24 | triplets.emplace_back(2 * iRow + 1, 2 * iCol + 1, val.real()); 25 | } 26 | } 27 | 28 | realM.setFromTriplets(triplets.begin(), triplets.end()); 29 | realM.makeCompressed(); 30 | 31 | return realM; 32 | } 33 | 34 | Vector complexToReal(const Vector>& vec) { 35 | 36 | size_t N = vec.rows(); 37 | 38 | Vector realVec(2 * N); 39 | 40 | for (size_t i = 0; i < N; i++) { 41 | realVec(2 * i) = vec(i).real(); 42 | realVec(2 * i + 1) = vec(i).imag(); 43 | } 44 | 45 | return realVec; 46 | } 47 | 48 | 49 | Vector> realToComplex(const Vector& v) { 50 | 51 | size_t N = v.rows() / 2; 52 | 53 | Vector> cVec(N); 54 | 55 | for (size_t i = 0; i < N; i++) { 56 | cVec(i) = std::complex{v(2 * i), v(2 * i + 1)}; 57 | } 58 | 59 | return cVec; 60 | } 61 | 62 | } // namespace geometrycentral 63 | -------------------------------------------------------------------------------- /src/numerical/linear_solvers.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/numerical/linear_solvers.h" 2 | 3 | #include "geometrycentral/numerical/linear_algebra_utilities.h" 4 | #include "geometrycentral/utilities/vector2.h" 5 | 6 | 7 | namespace geometrycentral { 8 | 9 | template class LinearSolver; 10 | template class LinearSolver; 11 | template class LinearSolver>; 12 | 13 | 14 | template 15 | double residual(const SparseMatrix& matrix, const Vector& lhs, const Vector& rhs) { 16 | Vector residVec = matrix * lhs - rhs; 17 | double resid = std::abs((residVec.conjugate().transpose() * residVec)(0)); 18 | return std::sqrt(resid); 19 | } 20 | 21 | 22 | template double residual(const SparseMatrix& matrix, const Vector& lhs, const Vector& rhs); 23 | template double residual(const SparseMatrix& matrix, const Vector& lhs, const Vector& rhs); 24 | template double residual(const SparseMatrix>& matrix, const Vector>& lhs, 25 | const Vector>& rhs); 26 | 27 | } // namespace geometrycentral 28 | -------------------------------------------------------------------------------- /src/pointcloud/neighborhoods.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/pointcloud/neighborhoods.h" 2 | #include "geometrycentral/pointcloud/point_cloud.h" 3 | 4 | #include "geometrycentral/utilities/knn.h" 5 | 6 | namespace geometrycentral { 7 | namespace pointcloud { 8 | 9 | Neighborhoods::Neighborhoods(PointCloud& cloud_, const PointData& positions, unsigned int nNeighbors) 10 | : cloud(cloud_), neighbors(cloud) 11 | 12 | { 13 | GC_SAFETY_ASSERT(cloud.isCompressed(), "cloud must be compressed"); 14 | 15 | std::vector pointVec; 16 | pointVec.reserve(cloud.nPoints()); 17 | for (Point p : cloud.points()) { 18 | pointVec.push_back(positions[p]); 19 | } 20 | 21 | // Find neighbors 22 | NearestNeighborFinder knn(pointVec); 23 | for (Point p : cloud.points()) { 24 | neighbors[p].resize(nNeighbors); 25 | std::vector neighInd = knn.kNearestNeighbors(p.getIndex(), nNeighbors); 26 | for(size_t j = 0; j < neighInd.size(); j++) { 27 | neighbors[p][j] = cloud.point(neighInd[j]); 28 | } 29 | } 30 | } 31 | 32 | } // namespace pointcloud 33 | } // namespace geometrycentral 34 | -------------------------------------------------------------------------------- /src/pointcloud/point_position_frame_geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/pointcloud/point_position_frame_geometry.h" 2 | 3 | namespace geometrycentral { 4 | namespace pointcloud { 5 | 6 | 7 | PointPositionFrameGeometry::PointPositionFrameGeometry(PointCloud& cloud, const PointData& positions_, 8 | const PointData>& frames) 9 | : PointPositionGeometry(cloud, positions_) { 10 | 11 | normals = PointData(cloud); 12 | tangentBasis = PointData>(cloud); 13 | 14 | for (Point p : cloud.points()) { 15 | tangentBasis[p][0] = frames[p][0]; 16 | tangentBasis[p][1] = frames[p][1]; 17 | normals[p] = frames[p][2]; 18 | } 19 | 20 | normalsQ.clearable = false; 21 | tangentBasisQ.clearable = false; 22 | } 23 | 24 | void PointPositionFrameGeometry::computeNormals() { 25 | // do nothing; already populated 26 | } 27 | void PointPositionFrameGeometry::computeTangentBasis() { 28 | // do nothing; already populated 29 | } 30 | 31 | } // namespace pointcloud 32 | } // namespace geometrycentral 33 | -------------------------------------------------------------------------------- /src/pointcloud/point_position_normal_geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/pointcloud/point_position_normal_geometry.h" 2 | 3 | namespace geometrycentral { 4 | namespace pointcloud { 5 | 6 | 7 | PointPositionNormalGeometry::PointPositionNormalGeometry(PointCloud& cloud, const PointData& positions_, 8 | const PointData& normals_) 9 | : PointPositionGeometry(cloud, positions_) { 10 | normals = normals_; 11 | normalsQ.clearable = false; 12 | } 13 | 14 | void PointPositionNormalGeometry::computeNormals() { 15 | // do nothing; already populated 16 | } 17 | 18 | } // namespace pointcloud 19 | } // namespace geometrycentral 20 | -------------------------------------------------------------------------------- /src/pointcloud/sample_cloud.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/pointcloud/sample_cloud.h" 2 | 3 | #include 4 | 5 | 6 | namespace geometrycentral { 7 | 8 | using surface::Face; 9 | using surface::SurfacePoint; 10 | 11 | namespace pointcloud { 12 | 13 | std::tuple, PointData, PointData> 14 | uniformlySamplePointsOnSurface(surface::SurfaceMesh& mesh, surface::EmbeddedGeometryInterface& geom, size_t nPts) { 15 | 16 | 17 | geom.requireVertexPositions(); 18 | geom.requireFaceAreas(); 19 | 20 | // Random number generator 21 | // if we ever want to seed, do it here 22 | std::random_device rd; 23 | std::mt19937 gen(rd()); 24 | 25 | // Create a sampler to pick points from faces 26 | // (uniformly in the geometric sense) 27 | std::vector areas; 28 | std::vector faces; 29 | areas.reserve(mesh.nFaces()); 30 | faces.reserve(mesh.nFaces()); 31 | for (Face f : mesh.faces()) { 32 | GC_SAFETY_ASSERT(f.isTriangle(), "can only sample point cloud from triangular mesh"); 33 | areas.push_back(geom.faceAreas[f]); 34 | faces.push_back(f); 35 | } 36 | std::discrete_distribution faceDist(areas.begin(), areas.end()); 37 | 38 | // Create a real-valued sampler, which we will use for barycentric coordinates within faces 39 | std::uniform_real_distribution realDist(0., 1.); 40 | 41 | // Store results here 42 | std::unique_ptr cloud(new PointCloud(nPts)); 43 | PointData pos(*cloud); 44 | PointData cloudSources(*cloud); 45 | 46 | // Sample 47 | for (size_t iSample = 0; iSample < nPts; iSample++) { 48 | Point p = cloud->point(iSample); 49 | 50 | // Pick a face 51 | size_t iF = faceDist(gen); 52 | Face f = faces[iF]; 53 | 54 | // Pick barycentric coordinates within the face 55 | double r1 = realDist(gen); 56 | double r2 = realDist(gen); 57 | SurfacePoint surfP(f, Vector3{1. - std::sqrt(r1), std::sqrt(r1) * (1. - r2), std::sqrt(r1) * r2}); 58 | 59 | // Interpolate to get the position of the sampled point 60 | Vector3 newPos = surfP.interpolate(geom.vertexPositions); 61 | 62 | pos[p] = newPos; 63 | cloudSources[p] = surfP; 64 | } 65 | 66 | 67 | geom.unrequireVertexPositions(); 68 | geom.unrequireFaceAreas(); 69 | 70 | return std::make_tuple(std::move(cloud), pos, cloudSources); 71 | } 72 | 73 | 74 | } // namespace pointcloud 75 | } // namespace geometrycentral 76 | -------------------------------------------------------------------------------- /src/surface/barycentric_vector.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/surface/barycentric_vector.h" 2 | 3 | 4 | namespace geometrycentral { 5 | namespace surface { 6 | 7 | BarycentricVector faceVectorRotate90(const BarycentricVector& w, IntrinsicGeometryInterface& geom) { 8 | // Found via (D(fj - fi) x (fk - fi))/(2A) x (Dw), where D is the matrix taking barycentric coords to 3D local coords, 9 | // where (fj - fi) x (fk - fi)/(2A) is the unit normal vector to the 3D-embedded local triangle. Then we apply D^{-1} 10 | // to map back to barycentric coords. 11 | double ui = w.faceCoords[0]; 12 | double uj = w.faceCoords[1]; 13 | double uk = w.faceCoords[2]; 14 | geom.requireEdgeLengths(); 15 | double l_ij = geom.edgeLengths[w.face.halfedge().edge()]; 16 | double l_jk = geom.edgeLengths[w.face.halfedge().next().edge()]; 17 | double l_ki = geom.edgeLengths[w.face.halfedge().next().next().edge()]; 18 | geom.unrequireEdgeLengths(); 19 | // coefficients of matrix D taking barycentric coords to 3D local coords, squared and multiplied by 2 20 | double ai = l_ij * l_ij - l_jk * l_jk + l_ki * l_ki; 21 | double aj = l_jk * l_jk - l_ki * l_ki + l_ij * l_ij; 22 | double ak = l_ki * l_ki - l_ij * l_ij + l_jk * l_jk; 23 | double s = 0.5 * (l_ij + l_jk + l_ki); 24 | double A = std::sqrt(s * (s - l_ij) * (s - l_jk) * (s - l_ki)); 25 | Vector3 newCoords = {ak * uk - aj * uj, ai * ui - ak * uk, aj * uj - ai * ui}; 26 | newCoords /= (4. * A); 27 | return BarycentricVector(w.face, newCoords); 28 | } 29 | 30 | BarycentricVector faceVectorRotate(const BarycentricVector& w, IntrinsicGeometryInterface& geom, double angle) { 31 | // Found by applying the 3D axis-angle rotation matrix in 3D local coordinates (then transforming back to barycentric 32 | // coordinates), which conveniently avoids (almost all) square roots. 33 | double ui = w.faceCoords[0]; 34 | double uj = w.faceCoords[1]; 35 | double uk = w.faceCoords[2]; 36 | geom.requireEdgeLengths(); 37 | double l_ij = geom.edgeLengths[w.face.halfedge().edge()]; 38 | double l_jk = geom.edgeLengths[w.face.halfedge().next().edge()]; 39 | double l_ki = geom.edgeLengths[w.face.halfedge().next().next().edge()]; 40 | geom.unrequireEdgeLengths(); 41 | double lij2 = l_ij * l_ij; 42 | double ljk2 = l_jk * l_jk; 43 | double lki2 = l_ki * l_ki; 44 | double A2 = -(l_ij - l_jk - l_ki) * (l_ij + l_jk - l_ki) * (l_ij - l_jk + l_ki) * (l_ij + l_jk + l_ki) / 16.; 45 | double bA = 2. * std::sqrt(A2); // 2A 46 | double qA2 = 4. * A2; // 4A^2 47 | double ai2 = 0.5 * (lij2 - ljk2 + lki2); 48 | double aj2 = 0.5 * (ljk2 - lki2 + lij2); 49 | double ak2 = 0.5 * (lki2 - lij2 + ljk2); 50 | double cosTheta = std::cos(angle); 51 | double sinTheta = std::sin(angle); 52 | double oneMinusCos = 1. - cosTheta; 53 | double aj2ak2 = aj2 * ak2; 54 | double ai2ak2 = ai2 * ak2; 55 | double ai2aj2 = ai2 * aj2; 56 | double xi = (qA2 * cosTheta + oneMinusCos * aj2ak2) * ui; 57 | double xj = (oneMinusCos * aj2ak2 - bA * sinTheta * aj2) * uj; 58 | double xk = (oneMinusCos * aj2ak2 + bA * sinTheta * ak2) * uk; 59 | double yi = (oneMinusCos * ai2ak2 + bA * sinTheta * ai2) * ui; 60 | double yj = (qA2 * cosTheta + oneMinusCos * ai2ak2) * uj; 61 | double yk = (oneMinusCos * ai2ak2 - bA * sinTheta * ak2) * uk; 62 | double zi = (oneMinusCos * ai2aj2 - bA * sinTheta * ai2) * ui; 63 | double zj = (oneMinusCos * ai2aj2 + bA * sinTheta * aj2) * uj; 64 | double zk = (qA2 * cosTheta + oneMinusCos * ai2aj2) * uk; 65 | Vector3 newCoords = {xi + xj + xk, yi + yj + yk, zi + zj + zk}; 66 | newCoords /= qA2; 67 | return BarycentricVector(w.face, newCoords); 68 | } 69 | 70 | } // namespace surface 71 | } // namespace geometrycentral 72 | -------------------------------------------------------------------------------- /src/surface/edge_length_geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/surface/edge_length_geometry.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace geometrycentral { 7 | namespace surface { 8 | 9 | EdgeLengthGeometry::EdgeLengthGeometry(SurfaceMesh& mesh_) 10 | : IntrinsicGeometryInterface(mesh_), inputEdgeLengths(edgeLengths) { 11 | 12 | edgeLengths = EdgeData(mesh_, 0.); 13 | 14 | // The input edge lengths share storage with edgeLengths, increment the required counter and make sure they 15 | // never get cleared 16 | requireEdgeLengths(); 17 | edgeLengthsQ.clearable = false; 18 | } 19 | 20 | EdgeLengthGeometry::EdgeLengthGeometry(SurfaceMesh& mesh_, const EdgeData& inputEdgeLengths_) 21 | : IntrinsicGeometryInterface(mesh_), inputEdgeLengths(edgeLengths) { 22 | 23 | edgeLengths = inputEdgeLengths_; 24 | 25 | // The input edge lengths share storage with edgeLengths, increment the required counter and make sure they 26 | // never get cleared 27 | requireEdgeLengths(); 28 | edgeLengthsQ.clearable = false; 29 | } 30 | 31 | std::unique_ptr EdgeLengthGeometry::copy() { return reinterpretTo(mesh); } 32 | 33 | std::unique_ptr EdgeLengthGeometry::reinterpretTo(SurfaceMesh& targetMesh) { 34 | std::unique_ptr newGeom(new EdgeLengthGeometry(targetMesh)); 35 | newGeom->inputEdgeLengths = inputEdgeLengths.reinterpretTo(targetMesh); 36 | return newGeom; 37 | } 38 | 39 | 40 | void EdgeLengthGeometry::computeEdgeLengths() { 41 | // The input edge lengthss share storage with edgeLengths, so this is a no-op 42 | } 43 | 44 | } // namespace surface 45 | } // namespace geometrycentral 46 | -------------------------------------------------------------------------------- /src/surface/halfedge_factories.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/surface/halfedge_factories.h" 2 | 3 | // NOTE these are DEPRECATED, and exist only for compatability. Prefer the versions from surface_mesh_factories.h 4 | 5 | namespace geometrycentral { 6 | namespace surface { 7 | 8 | 9 | // (these just forward to the newly-named versions) 10 | 11 | std::tuple, std::unique_ptr> 12 | makeHalfedgeAndGeometry(const std::vector>& polygons, const std::vector vertexPositions) { 13 | return makeHalfedgeAndGeometry(polygons, {}, vertexPositions); 14 | } 15 | 16 | std::tuple, std::unique_ptr> 17 | makeHalfedgeAndGeometry(const std::vector>& polygons, 18 | const std::vector>>& twins, 19 | const std::vector vertexPositions) { 20 | auto lvals = makeManifoldSurfaceMeshAndGeometry(polygons, twins, vertexPositions, {}); 21 | 22 | return std::tuple, 23 | std::unique_ptr>(std::move(std::get<0>(lvals)), // mesh 24 | std::move(std::get<1>(lvals))); // geometry 25 | } 26 | 27 | 28 | std::tuple, std::unique_ptr> 29 | makeGeneralHalfedgeAndGeometry(const std::vector>& polygons, 30 | const std::vector vertexPositions) { 31 | return makeGeneralHalfedgeAndGeometry(polygons, {}, vertexPositions); 32 | } 33 | 34 | 35 | std::tuple, std::unique_ptr> 36 | makeGeneralHalfedgeAndGeometry(const std::vector>& polygons, 37 | const std::vector>>& twins, 38 | const std::vector vertexPositions) { 39 | 40 | auto lvals = makeSurfaceMeshAndGeometry(polygons, twins, vertexPositions, {}); 41 | 42 | return std::tuple, 43 | std::unique_ptr>(std::move(std::get<0>(lvals)), // mesh 44 | std::move(std::get<1>(lvals))); // geometry 45 | } 46 | 47 | 48 | } // namespace surface 49 | } // namespace geometrycentral 50 | -------------------------------------------------------------------------------- /src/surface/intersection.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/surface/intersection.h" 2 | #include "geometrycentral/utilities/elementary_geometry.h" 3 | 4 | #include 5 | #include 6 | 7 | namespace geometrycentral { 8 | namespace surface { 9 | 10 | SurfaceIntersectionResult intersections(EmbeddedGeometryInterface& geometry1, EmbeddedGeometryInterface& geometry2, 11 | bool selfCheck) { 12 | SurfaceMesh& mesh1 = geometry1.mesh; 13 | SurfaceMesh& mesh2 = geometry2.mesh; 14 | SurfaceIntersectionResult intersections; 15 | intersections.hasIntersections = false; 16 | size_t n = 0; // number of intersection points 17 | 18 | // vertices and vertex locations for the current triangle pair 19 | Vertex u[3]; 20 | Vertex v[3]; 21 | Vector3 p[3]; 22 | Vector3 q[3]; 23 | 24 | int N = 0; 25 | 26 | // iterate over all face pairs f,g 27 | for (Face f : mesh1.faces()) { 28 | 29 | if (f.degree() != 3) { 30 | throw std::logic_error("only triangle meshes are supported"); 31 | } 32 | 33 | // get vertices ui of f 34 | int i = 0; 35 | for (Vertex ui : f.adjacentVertices()) { 36 | u[i] = ui; 37 | i++; 38 | } 39 | 40 | for (Face g : mesh2.faces()) { 41 | 42 | // for self-intersections, check each pair only once 43 | // (otherwise we create redundant output segments) 44 | if (selfCheck && g.getIndex() >= f.getIndex()) continue; 45 | 46 | // get vertices vj of g 47 | int j = 0; 48 | for (Vertex vj : g.adjacentVertices()) { 49 | v[j] = vj; 50 | j++; 51 | } 52 | 53 | // skip triangles that share vertices 54 | if (!(u[0] == v[0] || u[0] == v[1] || u[0] == v[2] || u[1] == v[0] || u[1] == v[1] || u[1] == v[2] || 55 | u[2] == v[0] || u[2] == v[1] || u[2] == v[2])) { 56 | 57 | // get vertex locations 58 | for (int k = 0; k < 3; k++) { 59 | p[k] = geometry1.vertexPositions[u[k]]; 60 | q[k] = geometry2.vertexPositions[v[k]]; 61 | } 62 | 63 | 64 | // check for and compute intersection 65 | TriTriIntersectionResult3D r; 66 | r = triTriIntersection(p[0], p[1], p[2], q[0], q[1], q[2]); 67 | 68 | // add to list of all intersections 69 | if (r.intersect) { 70 | intersections.hasIntersections = true; 71 | intersections.points.push_back(r.xA); 72 | intersections.points.push_back(r.xB); 73 | intersections.edges.push_back({n, n + 1}); 74 | n += 2; 75 | } 76 | } 77 | } 78 | } 79 | 80 | return intersections; 81 | } 82 | 83 | SurfaceIntersectionResult selfIntersections(EmbeddedGeometryInterface& geometry) { 84 | return intersections(geometry, geometry, true); 85 | } 86 | 87 | } // namespace surface 88 | } // namespace geometrycentral 89 | -------------------------------------------------------------------------------- /src/surface/intrinsic_mollification.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/surface/intrinsic_mollification.h" 2 | 3 | 4 | namespace geometrycentral { 5 | namespace surface { 6 | 7 | void mollifyIntrinsic(SurfaceMesh& mesh, EdgeData& edgeLengths, double relativeFactor) { 8 | // Mean edge length 9 | double edgeSum = 0.; 10 | for (Edge e : mesh.edges()) { 11 | edgeSum += edgeLengths[e]; 12 | } 13 | double meanEdge = edgeSum / mesh.nEdges(); 14 | 15 | double mollifyDelta = meanEdge * relativeFactor; 16 | 17 | mollifyIntrinsicAbsolute(mesh, edgeLengths, mollifyDelta); 18 | } 19 | 20 | void mollifyIntrinsicAbsolute(SurfaceMesh& mesh, EdgeData& edgeLengths, double absoluteFactor) { 21 | 22 | // Compute the mollify epsilon 23 | double mollifyEps = 0.; 24 | for (Halfedge he : mesh.interiorHalfedges()) { 25 | 26 | double lA = edgeLengths[he.edge()]; 27 | double lB = edgeLengths[he.next().edge()]; 28 | double lC = edgeLengths[he.next().next().edge()]; 29 | 30 | double thisEPS = lC - lA - lB + absoluteFactor; 31 | mollifyEps = std::fmax(mollifyEps, thisEPS); 32 | } 33 | 34 | // Apply the offset 35 | for (Edge e : mesh.edges()) { 36 | edgeLengths[e] += mollifyEps; 37 | } 38 | } 39 | 40 | } // namespace surface 41 | } // namespace geomtrycentral 42 | -------------------------------------------------------------------------------- /src/surface/vertex_position_geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/surface/vertex_position_geometry.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace geometrycentral { 7 | namespace surface { 8 | 9 | VertexPositionGeometry::VertexPositionGeometry(SurfaceMesh& mesh_) 10 | : EmbeddedGeometryInterface(mesh_), inputVertexPositions(vertexPositions) { 11 | 12 | vertexPositions = VertexData(mesh_, Vector3{0., 0., 0.}); 13 | 14 | // The input vertex positions share storage with vertexPositions, incremented the required counter and make sure they 15 | // never get cleared 16 | requireVertexPositions(); 17 | vertexPositionsQ.clearable = false; 18 | } 19 | 20 | VertexPositionGeometry::VertexPositionGeometry(SurfaceMesh& mesh_, const VertexData& inputVertexPositions_) 21 | : EmbeddedGeometryInterface(mesh_), inputVertexPositions(vertexPositions) { 22 | 23 | vertexPositions = inputVertexPositions_; 24 | 25 | // The input vertex positions share storage with vertexPositions, incremented the required counter and make sure they 26 | // never get cleared 27 | requireVertexPositions(); 28 | vertexPositionsQ.clearable = false; 29 | } 30 | 31 | 32 | std::unique_ptr VertexPositionGeometry::copy() { return reinterpretTo(mesh); } 33 | 34 | std::unique_ptr VertexPositionGeometry::reinterpretTo(SurfaceMesh& targetMesh) { 35 | std::unique_ptr newGeom(new VertexPositionGeometry(targetMesh)); 36 | newGeom->inputVertexPositions = inputVertexPositions.reinterpretTo(targetMesh); 37 | return newGeom; 38 | } 39 | 40 | void VertexPositionGeometry::computeVertexPositions() { 41 | // The input vertex positions share storage with vertexPositions, so this is a no-op 42 | } 43 | 44 | 45 | } // namespace surface 46 | } // namespace geometrycentral 47 | -------------------------------------------------------------------------------- /src/utilities/disjoint_sets.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/utilities/disjoint_sets.h" 2 | 3 | using std::vector; 4 | 5 | namespace geometrycentral { 6 | 7 | // Constructor 8 | DisjointSets::DisjointSets(size_t n_) : n(n_), parent(n + 1), rank(n + 1) { 9 | // Initialize all elements to be in different sets and to have rank 0 10 | for (size_t i = 0; i <= n; i++) { 11 | rank[i] = 0; 12 | parent[i] = i; 13 | } 14 | } 15 | 16 | // Find parent of element x 17 | size_t DisjointSets::find(size_t x) { 18 | if (x != parent[x]) parent[x] = find(parent[x]); 19 | return parent[x]; 20 | } 21 | 22 | // Union by rank 23 | void DisjointSets::merge(size_t x, size_t y) { 24 | x = find(x); 25 | y = find(y); 26 | 27 | // Smaller tree becomes a subtree of the larger tree 28 | if (rank[x] > rank[y]) 29 | parent[y] = x; 30 | else 31 | parent[x] = y; 32 | 33 | if (rank[x] == rank[y]) rank[y]++; 34 | } 35 | 36 | // Constructor 37 | MarkedDisjointSets::MarkedDisjointSets(size_t n_) : n(n_), parent(n + 1), rank(n + 1), marked(n + 1) { 38 | // Initialize all elements to be in different sets and to have rank 0 39 | for (size_t i = 0; i <= n; i++) { 40 | rank[i] = 0; 41 | parent[i] = i; 42 | marked[i] = false; 43 | } 44 | } 45 | 46 | void MarkedDisjointSets::mark(size_t x) { 47 | size_t p = find(x); 48 | marked[p] = true; 49 | } 50 | 51 | void MarkedDisjointSets::unmark(size_t x) { 52 | size_t p = find(x); 53 | marked[p] = false; 54 | } 55 | 56 | bool MarkedDisjointSets::isMarked(size_t x) { 57 | size_t p = find(x); 58 | return marked[p]; 59 | } 60 | 61 | // Find parent of element x 62 | size_t MarkedDisjointSets::find(size_t x) { 63 | if (x != parent[x]) parent[x] = find(parent[x]); 64 | return parent[x]; 65 | } 66 | 67 | // Union by rank 68 | void MarkedDisjointSets::merge(size_t x, size_t y) { 69 | x = find(x); 70 | y = find(y); 71 | 72 | // Smaller tree becomes a subtree of the larger tree 73 | if (rank[x] > rank[y]) 74 | parent[y] = x; 75 | else 76 | parent[x] = y; 77 | 78 | if (rank[x] == rank[y]) rank[y]++; 79 | 80 | // If either was marked, both are marked 81 | if (marked[x] || marked[y]) { 82 | marked[x] = true; 83 | marked[y] = true; 84 | } 85 | } 86 | 87 | } // namespace geometrycentral 88 | -------------------------------------------------------------------------------- /src/utilities/elementary_geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/utilities/elementary_geometry.h" 2 | 3 | 4 | #include "Eigen/Dense" 5 | 6 | namespace geometrycentral { 7 | 8 | bool inCircleTest(Vector2 pA, Vector2 pB, Vector2 pC, Vector2 pTest) { 9 | 10 | Eigen::Matrix4d A; 11 | // clang-format off 12 | A << pA.x, pA.y, norm2(pA), 1., 13 | pB.x, pB.y, norm2(pB), 1., 14 | pC.x, pC.y, norm2(pC), 1., 15 | pTest.x, pTest.y, norm2(pTest), 1.; 16 | // clang-format on 17 | return A.determinant() > 0.; 18 | } 19 | 20 | } // namespace geometrycentral 21 | -------------------------------------------------------------------------------- /src/utilities/unit_vector3.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/utilities/unit_vector3.h" 2 | 3 | namespace geometrycentral { 4 | 5 | UnitVector3 interpolate(UnitVector3& u0, UnitVector3& u1, double t) { 6 | double theta = angle(u0, u1); 7 | Vector3 w = cross(u0, u1); 8 | 9 | Vector3 e1 = u0; 10 | Vector3 e2 = cross(w, e1); 11 | 12 | Vector3 r = cos(t * theta) * e1 + sin(t * theta) * e2; 13 | 14 | return UnitVector3(r); 15 | } 16 | 17 | } // namespace geometrycentral 18 | -------------------------------------------------------------------------------- /src/utilities/utilities.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace geometrycentral { 4 | 5 | std::random_device util_random_device; 6 | std::mt19937 util_mersenne_twister(util_random_device()); 7 | // std::mt19937 util_mersenne_twister(0); // uncomment for determinism 8 | 9 | } // namespace geometrycentral 10 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | !assets/* 2 | -------------------------------------------------------------------------------- /test/assets/bob_small.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/test/assets/bob_small.ply -------------------------------------------------------------------------------- /test/assets/dodecahedron_poly.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.80 (sub 69) OBJ File: '' 2 | # www.blender.org 3 | o dodecahedron 4 | v 0.577350 -0.577350 0.577350 5 | v 0.934172 -0.356822 -0.000000 6 | v 0.934172 0.356822 0.000000 7 | v 0.356822 -0.000000 0.934172 8 | v 0.577350 0.577350 0.577350 9 | v 0.356822 0.000000 -0.934172 10 | v 0.577350 0.577350 -0.577350 11 | v 0.577350 -0.577350 -0.577350 12 | v -0.577350 -0.577350 -0.577350 13 | v -0.934172 -0.356822 -0.000000 14 | v -0.934172 0.356822 0.000000 15 | v -0.356822 0.000000 -0.934172 16 | v -0.577350 0.577350 -0.577350 17 | v -0.356822 -0.000000 0.934172 18 | v -0.577350 0.577350 0.577350 19 | v -0.577350 -0.577350 0.577350 20 | v 0.000000 0.934172 -0.356822 21 | v 0.000000 0.934172 0.356822 22 | v 0.000000 -0.934172 -0.356822 23 | v 0.000000 -0.934172 0.356822 24 | s off 25 | f 4 1 2 3 5 26 | f 8 6 7 3 2 27 | f 12 9 10 11 13 28 | f 16 14 15 11 10 29 | f 18 17 13 11 15 30 | f 17 18 5 3 7 31 | f 20 19 8 2 1 32 | f 19 20 16 10 9 33 | f 19 9 12 6 8 34 | f 6 12 13 17 7 35 | f 14 4 5 18 15 36 | f 4 14 16 20 1 37 | -------------------------------------------------------------------------------- /test/assets/fan3.obj: -------------------------------------------------------------------------------- 1 | v 0.000000 0.000000 -3.651100 2 | v -0.000000 -2.000000 1.500000 3 | v 3.000000 0.000000 0.000000 4 | v -0.000000 2.000000 1.500000 5 | v -3.000000 0.000000 -0.000000 6 | f 1 3 5 7 | f 4 3 5 8 | f 3 5 2 9 | -------------------------------------------------------------------------------- /test/assets/fox.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/test/assets/fox.ply -------------------------------------------------------------------------------- /test/assets/hourglass_ico.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.81 (sub 16) OBJ File: '' 2 | # www.blender.org 3 | o Icosphere 4 | v 0.723600 0.552785 0.525720 5 | v -0.276385 0.552785 0.850640 6 | v -0.894425 0.552785 0.000000 7 | v -0.276385 0.552785 -0.850640 8 | v 0.723600 0.552785 -0.525720 9 | v 0.276385 1.447215 0.850640 10 | v -0.723600 1.447215 0.525720 11 | v -0.723600 1.447215 -0.525720 12 | v 0.276385 1.447215 -0.850640 13 | v 0.894425 1.447215 0.000000 14 | v 0.000000 2.000000 0.000000 15 | v 0.000000 -2.000000 0.000000 16 | v 0.723600 -1.447215 0.525720 17 | v -0.276385 -1.447215 0.850640 18 | v -0.894425 -1.447215 0.000000 19 | v -0.276385 -1.447215 -0.850640 20 | v 0.723600 -1.447215 -0.525720 21 | v 0.276385 -0.552785 0.850640 22 | v -0.723600 -0.552785 0.525720 23 | v -0.723600 -0.552785 -0.525720 24 | v 0.276385 -0.552785 -0.850640 25 | v 0.894425 -0.552785 0.000000 26 | v 0.000000 0.000000 0.000000 27 | s off 28 | f 23 1 2 29 | f 1 23 5 30 | f 23 2 3 31 | f 23 3 4 32 | f 23 4 5 33 | f 1 5 10 34 | f 2 1 6 35 | f 3 2 7 36 | f 4 3 8 37 | f 5 4 9 38 | f 1 10 6 39 | f 2 6 7 40 | f 3 7 8 41 | f 4 8 9 42 | f 5 9 10 43 | f 6 10 11 44 | f 7 6 11 45 | f 8 7 11 46 | f 9 8 11 47 | f 10 9 11 48 | f 12 13 14 49 | f 13 12 17 50 | f 12 14 15 51 | f 12 15 16 52 | f 12 16 17 53 | f 13 17 22 54 | f 14 13 18 55 | f 15 14 19 56 | f 16 15 20 57 | f 17 16 21 58 | f 13 22 18 59 | f 14 18 19 60 | f 15 19 20 61 | f 16 20 21 62 | f 17 21 22 63 | f 18 22 23 64 | f 19 18 23 65 | f 20 19 23 66 | f 21 20 23 67 | f 22 21 23 68 | -------------------------------------------------------------------------------- /test/assets/lego.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/test/assets/lego.ply -------------------------------------------------------------------------------- /test/assets/platonic_shelf.obj: -------------------------------------------------------------------------------- 1 | v -3.562626 -0.627972 0.613591 2 | v -3.562626 0.600884 0.613591 3 | v -3.562626 -0.627972 -0.615265 4 | v -3.562626 0.600884 -0.615265 5 | v -2.333769 -0.627972 0.613591 6 | v -2.333769 0.600884 0.613591 7 | v -2.333769 -0.627972 -0.615265 8 | v -2.333769 0.600884 -0.615265 9 | v 3.860231 -0.577350 0.577350 10 | v 4.217053 -0.356822 -0.000000 11 | v 4.217053 0.356822 0.000000 12 | v 3.639703 -0.000000 0.934172 13 | v 3.860231 0.577350 0.577350 14 | v 3.639703 0.000000 -0.934172 15 | v 3.860231 0.577350 -0.577350 16 | v 3.860231 -0.577350 -0.577350 17 | v 2.705531 -0.577350 -0.577350 18 | v 2.348709 -0.356822 -0.000000 19 | v 2.348709 0.356822 0.000000 20 | v 2.926059 0.000000 -0.934172 21 | v 2.705531 0.577350 -0.577350 22 | v 2.926059 -0.000000 0.934172 23 | v 2.705531 0.577350 0.577350 24 | v 2.705531 -0.577350 0.577350 25 | v 3.282881 0.934172 -0.356822 26 | v 3.282881 0.934172 0.356822 27 | v 3.282881 -0.934172 -0.356822 28 | v 3.282881 -0.934172 0.356822 29 | v -6.052902 0.723151 -0.048380 30 | v -5.110093 -0.610182 -0.048380 31 | v -6.524307 -0.610182 -0.864877 32 | v -6.524307 -0.610182 0.768116 33 | v -0.000000 1.158087 0.000000 34 | v -0.000000 0.000000 -1.158087 35 | v -1.158087 0.000000 0.000000 36 | v -0.000000 0.000000 1.158086 37 | v 1.158086 0.000000 0.000000 38 | v -0.000000 -1.158087 0.000000 39 | v 6.068807 -1.014241 0.056527 40 | v 6.792407 -0.461456 0.582247 41 | v 5.792422 -0.461456 0.907167 42 | v 5.174382 -0.461456 0.056527 43 | v 5.792422 -0.461456 -0.794113 44 | v 6.792407 -0.461456 -0.469193 45 | v 6.345192 0.432974 0.907167 46 | v 5.345207 0.432974 0.582247 47 | v 5.345207 0.432974 -0.469193 48 | v 6.345192 0.432974 -0.794113 49 | v 6.963232 0.432974 0.056527 50 | v 6.068807 0.985759 0.056527 51 | f 1 2 4 3 52 | f 3 4 8 7 53 | f 7 8 6 5 54 | f 5 6 2 1 55 | f 3 7 5 1 56 | f 8 4 2 6 57 | f 12 9 10 11 13 58 | f 16 14 15 11 10 59 | f 20 17 18 19 21 60 | f 24 22 23 19 18 61 | f 26 25 21 19 23 62 | f 25 26 13 11 15 63 | f 28 27 16 10 9 64 | f 27 28 24 18 17 65 | f 27 17 20 14 16 66 | f 14 20 21 25 15 67 | f 22 12 13 26 23 68 | f 12 22 24 28 9 69 | f 33 34 35 70 | f 33 35 36 71 | f 33 36 37 72 | f 33 37 34 73 | f 34 38 35 74 | f 35 38 36 75 | f 36 38 37 76 | f 37 38 34 77 | f 39 40 41 78 | f 40 39 44 79 | f 39 41 42 80 | f 39 42 43 81 | f 39 43 44 82 | f 40 44 49 83 | f 41 40 45 84 | f 42 41 46 85 | f 43 42 47 86 | f 44 43 48 87 | f 40 49 45 88 | f 41 45 46 89 | f 42 46 47 90 | f 43 47 48 91 | f 44 48 49 92 | f 45 49 50 93 | f 46 45 50 94 | f 47 46 50 95 | f 48 47 50 96 | f 49 48 50 97 | f 29 30 31 98 | f 29 31 32 99 | f 29 32 30 100 | f 30 32 31 101 | -------------------------------------------------------------------------------- /test/assets/sphere_small.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/test/assets/sphere_small.ply -------------------------------------------------------------------------------- /test/assets/spot.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/test/assets/spot.ply -------------------------------------------------------------------------------- /test/assets/stl_box_ascii.stl: -------------------------------------------------------------------------------- 1 | solid "Design1" 2 | facet normal 1 0 0 3 | outer loop 4 | vertex 10 10 0 5 | vertex 10 10 10 6 | vertex 10 0 0 7 | endloop 8 | endfacet 9 | facet normal 1 0 0 10 | outer loop 11 | vertex 10 0 0 12 | vertex 10 10 10 13 | vertex 10 0 10 14 | endloop 15 | endfacet 16 | facet normal 0 0 1 17 | outer loop 18 | vertex 10 10 10 19 | vertex 0 10 10 20 | vertex 10 0 10 21 | endloop 22 | endfacet 23 | facet normal 0 0 1 24 | outer loop 25 | vertex 10 0 10 26 | vertex 0 10 10 27 | vertex 0 0 10 28 | endloop 29 | endfacet 30 | facet normal -1 0 0 31 | outer loop 32 | vertex 0 10 10 33 | vertex 0 10 0 34 | vertex 0 0 10 35 | endloop 36 | endfacet 37 | facet normal -1 0 0 38 | outer loop 39 | vertex 0 0 10 40 | vertex 0 10 0 41 | vertex 0 0 0 42 | endloop 43 | endfacet 44 | facet normal 0 0 -1 45 | outer loop 46 | vertex 0 10 0 47 | vertex 10 10 0 48 | vertex 0 0 0 49 | endloop 50 | endfacet 51 | facet normal 0 0 -1 52 | outer loop 53 | vertex 0 0 0 54 | vertex 10 10 0 55 | vertex 10 0 0 56 | endloop 57 | endfacet 58 | facet normal 0 1 0 59 | outer loop 60 | vertex 10 10 10 61 | vertex 10 10 0 62 | vertex 0 10 10 63 | endloop 64 | endfacet 65 | facet normal 0 1 0 66 | outer loop 67 | vertex 0 10 10 68 | vertex 10 10 0 69 | vertex 0 10 0 70 | endloop 71 | endfacet 72 | facet normal 0 -1 0 73 | outer loop 74 | vertex 10 0 0 75 | vertex 10 0 10 76 | vertex 0 0 0 77 | endloop 78 | endfacet 79 | facet normal 0 -1 0 80 | outer loop 81 | vertex 0 0 0 82 | vertex 10 0 10 83 | vertex 0 0 10 84 | endloop 85 | endfacet 86 | endsolid 87 | -------------------------------------------------------------------------------- /test/assets/stl_box_binary.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/geometry-central/73da1d863b42efbe89a98e4b007507eded1c8052/test/assets/stl_box_binary.stl -------------------------------------------------------------------------------- /test/assets/tet.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.76 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | mtllib tet.mtl 4 | o Solid 5 | v -6.052902 -1.910479 -0.448446 6 | v -5.110093 -3.243812 -0.448446 7 | v -6.524307 -3.243812 -1.264943 8 | v -6.524307 -3.243812 0.368050 9 | vn 0.471400 0.333300 -0.816500 10 | vn -0.942800 0.333300 0.000000 11 | vn 0.471400 0.333300 0.816500 12 | vn 0.000000 -1.000000 0.000000 13 | usemtl None 14 | s off 15 | f 1//1 2//1 3//1 16 | f 1//2 3//2 4//2 17 | f 1//3 4//3 2//3 18 | f 2//4 4//4 3//4 19 | -------------------------------------------------------------------------------- /test/include/linear_algebra_test_helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/numerical/linear_algebra_utilities.h" 4 | 5 | #include "gtest/gtest.h" 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/include/load_test_meshes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometrycentral/surface/manifold_surface_mesh.h" 4 | #include "geometrycentral/surface/vertex_position_geometry.h" 5 | 6 | #include "gtest/gtest.h" 7 | 8 | // A mesh used for testing 9 | struct MeshAsset { 10 | MeshAsset(){}; 11 | MeshAsset(std::string localPath, bool loadManifold); 12 | 13 | std::string name = "Unnamed_Mesh_Asset"; 14 | std::string sourcePath = "unknown"; 15 | std::unique_ptr mesh; 16 | geometrycentral::surface::ManifoldSurfaceMesh* manifoldMesh = nullptr; 17 | std::unique_ptr geometry; 18 | bool hasBoundary = false; 19 | bool isTriangular = true; 20 | bool isSubclassManifoldSurfaceMesh = true; 21 | bool isPolygonalComplex = true; 22 | 23 | MeshAsset copy() const; 24 | void printThyName() const; 25 | }; 26 | 27 | // Loads test meshes from disk 28 | class MeshAssetSuite : public ::testing::Test { 29 | public: 30 | MeshAsset getAsset(std::string name, bool loadManifold); 31 | 32 | // Get various groups for meshes 33 | // if includeNoGeom=true, will also include meshes which have connectivity but not geometry 34 | std::vector allMeshes(bool includeNoGeom = false); 35 | std::vector closedMeshes(bool includeNoGeom = false); // have no boundary 36 | std::vector manifoldSurfaceMeshes(bool includeNoGeom = false); 37 | std::vector boundaryMeshes(bool includeNoGeom = false); // have boundary 38 | std::vector triangularMeshes(bool includeNoGeom = false, 39 | bool includeNonmanifold = false); // faces have three sides 40 | std::vector polygonalComplexMeshes(bool includeNoGeom = false); // not a general delta complex 41 | 42 | protected: 43 | static void SetUpTestSuite(); 44 | // static void TearDownTestSuite(); 45 | // virtual void SetUp(); 46 | // virtual void TearDown(); 47 | 48 | private: 49 | static std::vector allMeshAssets; 50 | }; 51 | -------------------------------------------------------------------------------- /test/src/main_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "gtest/gtest.h" 5 | 6 | using std::cout; 7 | using std::endl; 8 | 9 | TEST(BasicTest, HelloWorldTest) { 10 | int two = 2; 11 | int four = two + two; 12 | EXPECT_EQ(four, 4); 13 | } 14 | 15 | int main(int argc, char** argv) { 16 | ::testing::InitGoogleTest(&argc, argv); 17 | return RUN_ALL_TESTS(); 18 | } 19 | -------------------------------------------------------------------------------- /test/src/poisson_disk_sampler_test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | using namespace geometrycentral; 10 | using namespace geometrycentral::surface; 11 | 12 | 13 | size_t sampleSquareDisk(double width, double sampling_distance) { 14 | double const PTS[4][3] = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0}}; 15 | unsigned const TRIS[2][3] = {{0, 1, 2}, {0, 2, 3}}; 16 | 17 | SimplePolygonMesh simpleMesh; 18 | for (const auto& t : TRIS) simpleMesh.polygons.push_back({t[0], t[1], t[2]}); 19 | 20 | for (const auto& p : PTS) simpleMesh.vertexCoordinates.push_back({width * p[0], width * p[1], width * p[2]}); 21 | 22 | std::unique_ptr mesh; 23 | std::unique_ptr geometry; 24 | std::tie(mesh, geometry) = makeManifoldSurfaceMeshAndGeometry(simpleMesh.polygons, simpleMesh.vertexCoordinates); 25 | 26 | PoissonDiskOptions options; 27 | options.minDist = sampling_distance; 28 | 29 | // make tests reproducible 30 | geometrycentral::util_mersenne_twister.seed(101); 31 | 32 | PoissonDiskSampler sampler(*mesh, *geometry); 33 | auto samples = sampler.sample(options); 34 | return samples.size(); 35 | } 36 | 37 | 38 | class PoissonDiskSamplerSuite : public ::testing::Test {}; 39 | 40 | TEST_F(PoissonDiskSamplerSuite, PoissonDiskSamplerConstructor) { 41 | // PoissonDiskSampler doesn't allow to set a random seed. 42 | // To prevent flaky test failures we 'average' over 10 iterations. 43 | size_t n1 = 0, n2 = 0; 44 | for (size_t iter = 0; iter < 10; iter++) { 45 | n1 += sampleSquareDisk(1.0, 0.1); 46 | n2 += sampleSquareDisk(100.0, 100.0 * 0.1); 47 | } 48 | 49 | EXPECT_GT(n1, 600); 50 | EXPECT_LT(n1, 720); 51 | EXPECT_NEAR(n1, n2, 100); 52 | } 53 | -------------------------------------------------------------------------------- /test/src/stl_reader_test.cpp: -------------------------------------------------------------------------------- 1 | // A few simple tests for reading STL ascii and binary 2 | // files 3 | #include 4 | 5 | #include "geometrycentral/surface/meshio.h" 6 | #include "gtest/gtest.h" 7 | 8 | 9 | using namespace geometrycentral; 10 | using namespace geometrycentral::surface; 11 | 12 | 13 | TEST(StlReaderTests, LoadAsciiStlFile) { 14 | std::string fullPath = std::string(GC_TEST_ASSETS_ABS_PATH) + "/" + "stl_box_ascii.stl"; 15 | 16 | std::unique_ptr mesh; 17 | geometrycentral::surface::ManifoldSurfaceMesh* manifoldMesh = nullptr; 18 | std::unique_ptr geometry; 19 | std::tie(mesh, geometry) = readSurfaceMesh(fullPath); 20 | ASSERT_EQ(mesh->nVertices(), 8); 21 | ASSERT_EQ(mesh->nFaces(), 12); 22 | } 23 | 24 | TEST(StlReaderTests, LoadBinaryStlFile) { 25 | std::string fullPath = std::string(GC_TEST_ASSETS_ABS_PATH) + "/" + "stl_box_binary.stl"; 26 | 27 | std::unique_ptr mesh; 28 | geometrycentral::surface::ManifoldSurfaceMesh* manifoldMesh = nullptr; 29 | std::unique_ptr geometry; 30 | std::tie(mesh, geometry) = readSurfaceMesh(fullPath); 31 | ASSERT_EQ(mesh->nVertices(), 8); 32 | ASSERT_EQ(mesh->nFaces(), 12); 33 | } 34 | 35 | -------------------------------------------------------------------------------- /test/src/surface_misc_test.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/surface/simple_polygon_mesh.h" 2 | 3 | #include "load_test_meshes.h" 4 | 5 | #include "gtest/gtest.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | using namespace geometrycentral; 13 | using namespace geometrycentral::surface; 14 | using std::cout; 15 | using std::endl; 16 | 17 | class SimplePolygonSuite : public MeshAssetSuite {}; 18 | 19 | // ============================================================ 20 | // =============== SimplePolygonMesh tests 21 | // ============================================================ 22 | 23 | TEST_F(SimplePolygonSuite, BasicTest) { 24 | for (MeshAsset& a : allMeshes()) { 25 | a.printThyName(); 26 | 27 | SimplePolygonMesh simpleMesh(a.sourcePath); 28 | 29 | ASSERT_GT(simpleMesh.nFaces(), 0); 30 | ASSERT_GT(simpleMesh.nVertices(), 0); 31 | } 32 | } 33 | 34 | --------------------------------------------------------------------------------