├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── console ├── README.md ├── gc-console ├── gc-polyscope-cling.h ├── process.cpp ├── view1.jpg └── view2.jpg └── src └── main.cpp /.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 | 11 | # Prerequisites 12 | *.d 13 | 14 | # Compiled Object files 15 | *.slo 16 | *.lo 17 | *.o 18 | *.obj 19 | 20 | # Precompiled Headers 21 | *.gch 22 | *.pch 23 | 24 | # Compiled Dynamic libraries 25 | *.so 26 | *.dylib 27 | *.dll 28 | 29 | # Fortran module files 30 | *.mod 31 | *.smod 32 | 33 | # Compiled Static libraries 34 | *.lai 35 | *.la 36 | *.a 37 | *.lib 38 | 39 | # Executables 40 | *.exe 41 | *.out 42 | *.app 43 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/geometry-central"] 2 | path = deps/geometry-central 3 | url = https://github.com/nmwsharp/geometry-central.git 4 | [submodule "deps/polyscope"] 5 | path = deps/polyscope 6 | url = https://github.com/nmwsharp/polyscope.git 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10.0) 2 | 3 | project(gc_polyscope_example) 4 | 5 | ### Configure output locations 6 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 7 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 8 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 9 | 10 | # Print the build type 11 | if(NOT CMAKE_BUILD_TYPE) 12 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release" FORCE) 13 | endif() 14 | message(STATUS "cmake build type: ${CMAKE_BUILD_TYPE}") 15 | 16 | ### Configure the compiler 17 | # This is a basic, decent setup that should do something sane on most compilers 18 | 19 | if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 20 | 21 | # using Clang (linux or apple) or GCC 22 | message("Using clang/gcc compiler flags") 23 | SET(BASE_CXX_FLAGS "-std=c++11 -Wall -Wextra") 24 | SET(DISABLED_WARNINGS " -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-deprecated-declarations -Wno-missing-braces -Wno-unused-private-field") 25 | SET(TRACE_INCLUDES " -H -Wno-error=unused-command-line-argument") 26 | 27 | if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") 28 | message("Setting clang-specific options") 29 | SET(BASE_CXX_FLAGS "${BASE_CXX_FLAGS} -ferror-limit=3 -fcolor-diagnostics") 30 | SET(CMAKE_CXX_FLAGS_DEBUG "-g3 -fsanitize=address -fno-limit-debug-info") 31 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 32 | message("Setting gcc-specific options") 33 | SET(BASE_CXX_FLAGS "${BASE_CXX_FLAGS} -fmax-errors=5") 34 | SET(CMAKE_CXX_FLAGS_DEBUG "-g3") 35 | SET(DISABLED_WARNINGS "${DISABLED_WARNINGS} -Wno-maybe-uninitialized -Wno-format-zero-length -Wno-unused-but-set-parameter -Wno-unused-but-set-variable") 36 | endif() 37 | 38 | SET(CMAKE_CXX_FLAGS "${BASE_CXX_FLAGS} ${DISABLED_WARNINGS}") 39 | SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -march=native -DNDEBUG") 40 | 41 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 42 | # using Visual Studio C++ 43 | message("Using Visual Studio compiler flags") 44 | set(BASE_CXX_FLAGS "${BASE_CXX_FLAGS} /W4") 45 | set(BASE_CXX_FLAGS "${BASE_CXX_FLAGS} /MP") # parallel build 46 | SET(DISABLED_WARNINGS "${DISABLED_WARNINGS} /wd\"4267\"") # ignore conversion to smaller type (fires more aggressively than the gcc version, which is annoying) 47 | SET(DISABLED_WARNINGS "${DISABLED_WARNINGS} /wd\"4244\"") # ignore conversion to smaller type (fires more aggressively than the gcc version, which is annoying) 48 | SET(DISABLED_WARNINGS "${DISABLED_WARNINGS} /wd\"4305\"") # ignore truncation on initialization 49 | SET(CMAKE_CXX_FLAGS "${BASE_CXX_FLAGS} ${DISABLED_WARNINGS}") 50 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") 51 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") 52 | 53 | add_definitions(/D "_CRT_SECURE_NO_WARNINGS") 54 | add_definitions(-DNOMINMAX) 55 | add_definitions(-D_USE_MATH_DEFINES) 56 | else() 57 | # unrecognized 58 | message( FATAL_ERROR "Unrecognized compiler [${CMAKE_CXX_COMPILER_ID}]" ) 59 | endif() 60 | 61 | 62 | # == Deps 63 | add_subdirectory(deps/geometry-central) 64 | add_subdirectory(deps/polyscope) 65 | 66 | # == Build our project stuff 67 | 68 | set(SRCS 69 | src/main.cpp 70 | # add any other source files here 71 | ) 72 | 73 | 74 | # To change the name of your executable, change "gc_project" in the lines below to whatever you want 75 | add_executable(gc_project "${SRCS}") 76 | target_include_directories(gc_project PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include/") 77 | # add the args.hxx project which we use for command line args 78 | target_include_directories(gc_project PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/deps/polyscope/deps/args") 79 | target_link_libraries(gc_project geometry-central polyscope) 80 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Nick Sharp 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 | # gc-polyscope-project-template 2 | A template project to get started with [geometry-central](http://geometry-central.net/) and [Polyscope](http://polyscope.run/). 3 | 4 | 5 | ### Get the code 6 | Clone the project 7 | ``` 8 | git clone --recursive https://github.com/nmwsharp/gc-polyscope-project-template.git 9 | ``` 10 | 11 | ### Build the code 12 | 13 | **Unix-like machines**: configure (with cmake) and compile 14 | ``` 15 | cd gc-polyscope-project-template 16 | mkdir build 17 | cd build 18 | cmake .. 19 | make -j6 20 | ``` 21 | 22 | **Windows / Visual Studio** 23 | 24 | Install CMake, and use either the CMake GUI or the command line interface (as on unix) to generate a Visual Studio solution. Build the solution with Visual Studio. 25 | 26 | ### Run the code 27 | ``` 28 | ./bin/gc_project /path/to/a/mesh 29 | ``` 30 | 31 | ### Edit the code 32 | 33 | Modify the main file `src/main.cpp` to start implementing your own algorithms. `CMakeLists.txt` contains a few comments for adding additional files. 34 | 35 | ## Interactive interpreter 36 | 37 | The `console` subdirectory contains instructions and an example for running geometry-central and Polyscope in an interactive C++ interpreter with `cling`. 38 | -------------------------------------------------------------------------------- /console/README.md: -------------------------------------------------------------------------------- 1 | # GC-Polyscope Console 2 | 3 | GeometryCentral and Polyscope can be used via an interactive session on the command line, on any platform that supports the _cling_ C++ interpreter. 4 | 5 | ## Installation 6 | 7 | 1. Run `git clone --recursive https://github.com/nmwsharp/gc-polyscope-project-template.git`, but **don't** follow the standard build instructions. 8 | 2. If not already installed, you will need to install cling and Eigen. On MacOS/homebrew, these packages can be installed via `brew install cling` and `brew install eigen`. 9 | 3. Edit the file `console/gc-polyscope-cling.h`, and set the path to `eigen3` so that it points to your local install. 10 | 4. In CMakeLists.txt add the line `option(BUILD_SHARED_LIBS "Build the shared library" TRUE)` right below the line `project(gc_polyscope_example)`. 11 | 5. Now you can build as usual: from the root directory, run `mkdir build; cd build; cmake ..`. 12 | 13 | ## Usage 14 | 15 | To start the console, just run `./gc-console` from the root directory. From here you can type standard C++ code, and invoke commands from GeometryCentral/Polyscope. 16 | 17 | You can also invoke an external "script", i.e., an ordinary C++ file containing GeometryCentral/Polyscope commands, by typing `.x script.cpp`. This script must contain a method called `script()`, which will be run when you invoke the script. 18 | 19 | Typing `.q` will end your session, and code from existing files can be loaded using the `.L` command. Type `.help` for other commands. 20 | 21 | Note that some common methods have been provided for convenience: 22 | 23 | - `load_mesh( char* filename )` will load the specified mesh into a surface mesh called `mesh` and corresponding vertex position geometry called `geometry`. 24 | - `init_view()` will initialize a Polyscope viewer, where `mesh` is registered as `psMesh`. 25 | - `view()` will pop up the Polyscope viewer. This viewer can be used interactively as usual; closing the window will return you to the console session. You can run `view()` at any moment in a session. 26 | 27 | Additional commands added to the file `gc-polyscope-cling.h` will be made available at startup. 28 | 29 | For further information, see: 30 | 31 | - [Cling homepage](https://github.com/root-project/cling) 32 | - [GeometryCentral documentation](http://geometry-central.net/) 33 | - [Polyscope documentation](https://polyscope.run/) 34 | 35 | ## Tutorial 36 | 37 | Here's a simple example, showing how to load a mesh, compute the area of each face, and display these areas in Polyscope. First, we start the console from the `/console/` directory, which brings up a prompt: 38 | 39 | ``` 40 | $ 41 | ``` 42 | 43 | We next load the mesh `spot.obj` (which should already be sitting inside the `console/` directory): 44 | 45 | ``` 46 | $ load_mesh( "spot.obj" ); 47 | Loading spot.obj into 'mesh' and 'geometry'... 48 | ``` 49 | 50 | The mesh is now loaded into a [surface mesh object](http://geometry-central.net/surface/surface_mesh/basics/) called `mesh`, and a [geometry object](http://geometry-central.net/surface/geometry/geometry/) called `geometry`. At this point, we can run any command we like from GeometryCentral on the mesh. For instance, to see the number of vertices we can just write 51 | 52 | ``` 53 | $ mesh->nVertices() 54 | (unsigned long) 2930 55 | ``` 56 | 57 | We can also write a few lines of C++ code. For example, let's say that we want to compute the _Euler characteristic_, equal to the number of vertices minus edges plus faces (which should be equal to 2 for any sphere-like mesh). We can write 58 | 59 | ``` 60 | $ int nV = mesh->nVertices(); 61 | $ int nE = mesh->nEdges(); 62 | $ int nF = mesh->nFaces(); 63 | $ nV - nE + nF 64 | (int) 2 65 | ``` 66 | 67 | which yields the expected value of 2. 68 | 69 | Next, let's say we want to visualize the mesh. We first initialize the viewer, which only needs to happen once for our session. To do this, just type 70 | 71 | ``` 72 | $ init_view(); 73 | Initializing Polyscope... 74 | [polyscope] Backend: openGL3_glfw -- Loaded openGL version: 4.1 ATI-3.10.18 75 | Registering mesh as 'psMesh'... 76 | ``` 77 | 78 | Nothing will display (yet), but notice that the object `mesh` we've been working with is now attached to the Polyscope mesh `psMesh`, which we'll use to set any attributes we want to visualize. Before we do that, let's just take a look at the mesh—which we can do at any time by typing `view()`: 79 | 80 | ``` 81 | $ view(); 82 | ``` 83 | 84 | Doing so will bring up a Polyscope window like the one shown below. (**Note:** _this window may initially be hidden behind other windows!_) 85 | 86 | ![Mesh viewed in Polyscope](view1.jpg) 87 | 88 | While this window is open, our console session is paused; closing the window lets us continue typing commands on the console. 89 | 90 | Let's now compute some data to display. As a simple example, we'll just compute the area of each face, stored in a [face data array](http://geometry-central.net/surface/surface_mesh/containers/): 91 | 92 | ``` 93 | $ FaceData data( *mesh ); 94 | $ for( Face f : mesh->faces() ) { 95 | $ ? data[f] = geometry->faceArea(f); 96 | $ ? } 97 | ``` 98 | 99 | We can now attach this data to the Polyscope mesh `psMesh` via 100 | 101 | ``` 102 | [cling]$ psMesh->addFaceScalarQuantity( "area", data ); 103 | ``` 104 | 105 | The first argument just provides a label for this data in the viewer. To display the data, we can again type 106 | 107 | ``` 108 | $ view(); 109 | ``` 110 | 111 | which brings up a view like the one below. (**Note:** You must click the check box "Enabled" to see the data!) 112 | 113 | ![Mesh viewed in Polyscope, with face areas](view2.jpg) 114 | 115 | Finally, suppose we want to capture everything we've done as a script, so we can easily run it again rather than re-typing it from the command line. We can dump everything we've typed into a file `process.cpp` that looks like this: 116 | 117 | ``` 118 | void process() 119 | { 120 | load_mesh( "spot.obj" ); 121 | init_view(); 122 | 123 | FaceData data( *mesh ); 124 | for( Face f : mesh->faces() ) { 125 | data[f] = geometry->faceArea(f); 126 | } 127 | psMesh->addFaceScalarQuantity( "area", data ); 128 | 129 | view(); 130 | } 131 | ``` 132 | 133 | **Important**: notice that the _name of the function is the same as the file_. This file can contain other subroutines, but `process()` is the one that will be invoked when we execute it. To run this script, we now just have to start up the console and execute it: 134 | 135 | ``` 136 | ./gc-console 137 | $ .x process.cpp 138 | ``` 139 | 140 | which should bring up the same final view we had before. 141 | 142 | From here, the sky's the limit! Take a look at the documentation links above for more information. -------------------------------------------------------------------------------- /console/gc-console: -------------------------------------------------------------------------------- 1 | cling -std=c++11 --nologo -l gc-polyscope-cling.h 2 | 3 | -------------------------------------------------------------------------------- /console/gc-polyscope-cling.h: -------------------------------------------------------------------------------- 1 | #pragma cling add_include_path("../deps/geometry-central/include/") 2 | #pragma cling add_include_path("../deps/polyscope/include/") 3 | #pragma cling add_include_path("../deps/polyscope/deps/glm/") 4 | #pragma cling add_include_path("../deps/polyscope/deps/imgui/imgui/") 5 | #pragma cling add_include_path("/usr/local/include/eigen3/") 6 | #pragma cling add_library_path("../build/lib/") 7 | #pragma cling load( "libgeometry-central.dylib" ) 8 | #pragma cling load( "libpolyscope.dylib" ) 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "polyscope/polyscope.h" 15 | #include "polyscope/surface_mesh.h" 16 | polyscope::SurfaceMesh *psMesh; 17 | 18 | using namespace geometrycentral; 19 | using namespace geometrycentral::surface; 20 | 21 | std::unique_ptr mesh; 22 | std::unique_ptr geometry; 23 | 24 | void load_mesh( std::string filename ) { 25 | std::cout << "Loading " << filename << " into 'mesh' and 'geometry'..." << std::endl; 26 | std::tie(mesh, geometry) = readManifoldSurfaceMesh( filename.c_str() ); 27 | } 28 | 29 | void init_view() { 30 | // Initialize Polyscope 31 | std::cout << "Initializing Polyscope..." << std::endl; 32 | polyscope::init(); 33 | std::cout << "Registering mesh as 'psMesh'..." << std::endl; 34 | // Register the mesh with Polyscope 35 | psMesh = polyscope::registerSurfaceMesh( 36 | "mesh", 37 | geometry->inputVertexPositions, mesh->getFaceVertexList(), 38 | polyscopePermutations(*mesh)); 39 | } 40 | 41 | void view() { 42 | // Update the vertex positions 43 | psMesh->updateVertexPositions( geometry->inputVertexPositions ); 44 | 45 | // Give control to the Polyscope GUI 46 | polyscope::show(); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /console/process.cpp: -------------------------------------------------------------------------------- 1 | void process() 2 | { 3 | load_mesh( "spot.obj" ); 4 | init_view(); 5 | 6 | FaceData data( *mesh ); 7 | for( Face f : mesh->faces() ) { 8 | data[f] = geometry->faceArea(f); 9 | } 10 | psMesh->addFaceScalarQuantity( "area", data ); 11 | 12 | view(); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /console/view1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/gc-polyscope-project-template/d6a2bd80063624fbffa72ae9ce730030498a79fb/console/view1.jpg -------------------------------------------------------------------------------- /console/view2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmwsharp/gc-polyscope-project-template/d6a2bd80063624fbffa72ae9ce730030498a79fb/console/view2.jpg -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "geometrycentral/surface/manifold_surface_mesh.h" 2 | #include "geometrycentral/surface/meshio.h" 3 | #include "geometrycentral/surface/vertex_position_geometry.h" 4 | 5 | #include "geometrycentral/surface/direction_fields.h" 6 | 7 | #include "polyscope/polyscope.h" 8 | #include "polyscope/surface_mesh.h" 9 | 10 | #include "args/args.hxx" 11 | #include "imgui.h" 12 | 13 | using namespace geometrycentral; 14 | using namespace geometrycentral::surface; 15 | 16 | // == Geometry-central data 17 | std::unique_ptr mesh; 18 | std::unique_ptr geometry; 19 | 20 | // Polyscope visualization handle, to quickly add data to the surface 21 | polyscope::SurfaceMesh *psMesh; 22 | 23 | // Some algorithm parameters 24 | float param1 = 42.0; 25 | 26 | // Example computation function -- this one computes and registers a scalar 27 | // quantity 28 | void doWork() { 29 | polyscope::warning("Computing Gaussian curvature.\nalso, parameter value = " + 30 | std::to_string(param1)); 31 | 32 | geometry->requireVertexGaussianCurvatures(); 33 | psMesh->addVertexScalarQuantity("curvature", 34 | geometry->vertexGaussianCurvatures, 35 | polyscope::DataType::SYMMETRIC); 36 | } 37 | 38 | // A user-defined callback, for creating control panels (etc) 39 | // Use ImGUI commands to build whatever you want here, see 40 | // https://github.com/ocornut/imgui/blob/master/imgui.h 41 | void myCallback() { 42 | 43 | if (ImGui::Button("do work")) { 44 | doWork(); 45 | } 46 | 47 | ImGui::SliderFloat("param", ¶m1, 0., 100.); 48 | } 49 | 50 | int main(int argc, char **argv) { 51 | 52 | // Configure the argument parser 53 | args::ArgumentParser parser("geometry-central & Polyscope example project"); 54 | args::Positional inputFilename(parser, "mesh", "A mesh file."); 55 | 56 | // Parse args 57 | try { 58 | parser.ParseCLI(argc, argv); 59 | } catch (args::Help &h) { 60 | std::cout << parser; 61 | return 0; 62 | } catch (args::ParseError &e) { 63 | std::cerr << e.what() << std::endl; 64 | std::cerr << parser; 65 | return 1; 66 | } 67 | 68 | // Make sure a mesh name was given 69 | if (!inputFilename) { 70 | std::cerr << "Please specify a mesh file as argument" << std::endl; 71 | return EXIT_FAILURE; 72 | } 73 | 74 | // Initialize polyscope 75 | polyscope::init(); 76 | 77 | // Set the callback function 78 | polyscope::state::userCallback = myCallback; 79 | 80 | // Load mesh 81 | std::tie(mesh, geometry) = readManifoldSurfaceMesh(args::get(inputFilename)); 82 | 83 | // Register the mesh with polyscope 84 | psMesh = polyscope::registerSurfaceMesh( 85 | polyscope::guessNiceNameFromPath(args::get(inputFilename)), 86 | geometry->inputVertexPositions, mesh->getFaceVertexList(), 87 | polyscopePermutations(*mesh)); 88 | 89 | // Set vertex tangent spaces 90 | geometry->requireVertexTangentBasis(); 91 | VertexData vBasisX(*mesh); 92 | VertexData vBasisY(*mesh); 93 | for (Vertex v : mesh->vertices()) { 94 | vBasisX[v] = geometry->vertexTangentBasis[v][0]; 95 | vBasisY[v] = geometry->vertexTangentBasis[v][1]; 96 | } 97 | 98 | auto vField = 99 | geometrycentral::surface::computeSmoothestVertexDirectionField(*geometry); 100 | psMesh->addVertexTangentVectorQuantity("VF", vField, vBasisX, vBasisY); 101 | 102 | // Give control to the polyscope gui 103 | polyscope::show(); 104 | 105 | return EXIT_SUCCESS; 106 | } 107 | --------------------------------------------------------------------------------