├── .clang-format ├── .github └── workflows │ ├── apt-ci.yaml │ └── conda-forge-ci.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── AddInstallRPATHSupport.cmake ├── AddUninstallTarget.cmake ├── FetchuWebSockets.cmake ├── FinduSockets.cmake ├── FinduWebSockets.cmake ├── InstallBasicPackageFiles.cmake └── MeshcatCppDeps.cmake ├── examples ├── CMakeLists.txt ├── meshcat_example.cpp └── misc │ ├── Dragonite.stl │ └── LICENSE_DRAGONITE_STL ├── include └── MeshcatCpp │ ├── Material.h │ ├── MatrixView.h │ ├── Meshcat.h │ ├── Property.h │ ├── Shape.h │ └── impl │ ├── FindResource.h │ ├── MsgpackTypes.h │ ├── TreeNode.h │ └── UUIDGenerator.h ├── misc ├── LICENSE_MESHCAT ├── favicon.ico ├── index.html ├── main.min.js └── main.min.js.THIRD_PARTY_LICENSES.json └── src ├── Material.cpp ├── Meshcat.cpp ├── MsgpackTypes.cpp ├── Shape.cpp └── UUIDGenerator.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: WebKit 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Left 9 | AlignOperands: true 10 | AlignTrailingComments: false 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: None 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: false 21 | BinPackArguments: false 22 | BinPackParameters: false 23 | BraceWrapping: 24 | AfterClass: true 25 | AfterControlStatement: true 26 | AfterEnum: true 27 | AfterFunction: true 28 | AfterNamespace: true 29 | AfterObjCDeclaration: false 30 | AfterStruct: true 31 | AfterUnion: true 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | SplitEmptyFunction: true 36 | SplitEmptyRecord: true 37 | SplitEmptyNamespace: true 38 | BreakBeforeBinaryOperators: All 39 | BreakBeforeBraces: Custom 40 | BreakBeforeInheritanceComma: false 41 | BreakBeforeTernaryOperators: true 42 | BreakConstructorInitializersBeforeComma: true 43 | BreakConstructorInitializers: BeforeComma 44 | BreakAfterJavaFieldAnnotations: false 45 | BreakStringLiterals: true 46 | ColumnLimit: 100 47 | CommentPragmas: '^ IWYU pragma:' 48 | CompactNamespaces: false 49 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 50 | ConstructorInitializerIndentWidth: 4 51 | ContinuationIndentWidth: 4 52 | Cpp11BracedListStyle: true 53 | DerivePointerAlignment: false 54 | DisableFormat: false 55 | ExperimentalAutoDetectBinPacking: false 56 | FixNamespaceComments: true 57 | ForEachMacros: 58 | - foreach 59 | - Q_FOREACH 60 | - BOOST_FOREACH 61 | IncludeCategories: 62 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 63 | Priority: 2 64 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 65 | Priority: 3 66 | - Regex: '.*' 67 | Priority: 1 68 | IncludeIsMainRegex: '(Test)?$' 69 | IndentCaseLabels: false 70 | IndentWidth: 4 71 | IndentWrappedFunctionNames: false 72 | JavaScriptQuotes: Leave 73 | JavaScriptWrapImports: true 74 | KeepEmptyLinesAtTheStartOfBlocks: true 75 | MacroBlockBegin: '' 76 | MacroBlockEnd: '' 77 | MaxEmptyLinesToKeep: 1 78 | NamespaceIndentation: None 79 | ObjCBlockIndentWidth: 4 80 | ObjCSpaceAfterProperty: true 81 | ObjCSpaceBeforeProtocolList: true 82 | PenaltyBreakAssignment: 10 83 | PenaltyBreakBeforeFirstCallParameter: 1000 84 | PenaltyBreakComment: 10 85 | PenaltyBreakString: 10 86 | PenaltyExcessCharacter: 100 87 | PenaltyReturnTypeOnItsOwnLine: 5 88 | PointerAlignment: Left 89 | ReflowComments: true 90 | SortIncludes: true 91 | SortUsingDeclarations: true 92 | SpaceAfterCStyleCast: false 93 | SpaceAfterTemplateKeyword: true 94 | SpaceBeforeAssignmentOperators: true 95 | SpaceBeforeParens: ControlStatements 96 | SpaceInEmptyParentheses: false 97 | SpacesBeforeTrailingComments: 1 98 | SpacesInAngles: false 99 | SpacesInContainerLiterals: true 100 | SpacesInCStyleCastParentheses: false 101 | SpacesInParentheses: false 102 | SpacesInSquareBrackets: false 103 | Standard: Cpp11 104 | TabWidth: 4 105 | UseTab: Never 106 | -------------------------------------------------------------------------------- /.github/workflows/apt-ci.yaml: -------------------------------------------------------------------------------- 1 | name: C++ CI Workflow with apt dependencies 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | # * is a special character in YAML so you have to quote this string 8 | # Execute a "nightly" build at 2 AM UTC 9 | - cron: '0 2 * * *' 10 | 11 | jobs: 12 | build: 13 | name: '[${{ matrix.docker_image }}@${{ matrix.build_type }}@apt]' 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | build_type: [Release] 18 | os: 19 | - ubuntu-latest 20 | docker_image: 21 | - "ubuntu:20.04" 22 | - "ubuntu:22.04" 23 | - "debian:sid" 24 | container: 25 | image: ${{ matrix.docker_image }} 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | 30 | 31 | - name: Dependencies 32 | run: | 33 | # See https://stackoverflow.com/questions/44331836/apt-get-install-tzdata-noninteractive, 34 | # only required by Ubuntu 20.04 35 | export DEBIAN_FRONTEND=noninteractive 36 | apt-get update 37 | apt-get -y upgrade 38 | apt-get -y install cmake pkg-config build-essential ninja-build git libssl-dev libuv1-dev libz-dev libboost-dev 39 | 40 | - name: Configure 41 | run: | 42 | mkdir -p build 43 | cd build 44 | cmake -GNinja -DBUILD_TESTING:BOOL=ON -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} .. 45 | 46 | - name: Build 47 | shell: bash -l {0} 48 | run: | 49 | cd build 50 | cmake --build . --config ${{ matrix.build_type }} 51 | 52 | - name: Test 53 | shell: bash -l {0} 54 | run: | 55 | cd build 56 | ctest --output-on-failure -C ${{ matrix.build_type }} 57 | 58 | -------------------------------------------------------------------------------- /.github/workflows/conda-forge-ci.yml: -------------------------------------------------------------------------------- 1 | name: C++ CI Workflow with conda-forge dependencies 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | # * is a special character in YAML so you have to quote this string 8 | # Execute a "nightly" build at 2 AM UTC 9 | - cron: '0 2 * * *' 10 | 11 | jobs: 12 | build: 13 | name: '[${{ matrix.os }}@${{ matrix.build_type }}@conda]' 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | build_type: [Release] 18 | os: [ubuntu-latest, windows-2019, macOS-latest] 19 | fail-fast: false 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | 24 | - uses: conda-incubator/setup-miniconda@v2 25 | with: 26 | miniforge-variant: Mambaforge 27 | miniforge-version: latest 28 | 29 | - name: Dependencies 30 | shell: bash -l {0} 31 | run: | 32 | mamba install cmake pkg-config make ninja compilers boost-cpp libuwebsockets zlib openssl libuv msgpack-cxx 33 | 34 | # Workaround for https://github.com/conda-forge/boost-cpp-feedstock/issues/134 35 | - name: Dependencies 36 | if: contains(matrix.os, 'macos') 37 | shell: bash -l {0} 38 | run: | 39 | mamba install "libcxx<16" 40 | 41 | 42 | - name: Configure [Linux&macOS] 43 | if: contains(matrix.os, 'macos') || contains(matrix.os, 'ubuntu') 44 | shell: bash -l {0} 45 | run: | 46 | mkdir -p build 47 | cd build 48 | cmake -GNinja -DBUILD_TESTING:BOOL=ON -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} .. 49 | 50 | - name: Configure [Windows] 51 | if: contains(matrix.os, 'windows') 52 | shell: bash -l {0} 53 | run: | 54 | mkdir -p build 55 | cd build 56 | cmake -G"Visual Studio 16 2019" -DBUILD_TESTING:BOOL=ON -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} .. 57 | 58 | - name: Build 59 | shell: bash -l {0} 60 | run: | 61 | cd build 62 | cmake --build . --config ${{ matrix.build_type }} 63 | 64 | - name: Test 65 | shell: bash -l {0} 66 | run: | 67 | cd build 68 | ctest --output-on-failure -C ${{ matrix.build_type }} 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build folders 2 | build* 3 | 4 | # emacs 5 | *~ 6 | \#*\# 7 | 8 | # Qtcreator 9 | *.user* 10 | *.autosave 11 | 12 | # IDEs 13 | .idea* 14 | 15 | # ====== 16 | # Python (https://github.com/github/gitignore/blob/master/Python.gitignore) 17 | # ====== 18 | 19 | # Byte-compiled / optimized / DLL files 20 | __pycache__/ 21 | *.py[cod] 22 | *$py.class 23 | 24 | # C extensions 25 | *.so 26 | 27 | # Distribution / packaging 28 | .Python 29 | build/ 30 | develop-eggs/ 31 | dist/ 32 | downloads/ 33 | eggs/ 34 | .eggs/ 35 | lib/ 36 | lib64/ 37 | parts/ 38 | sdist/ 39 | var/ 40 | wheels/ 41 | share/python-wheels/ 42 | *.egg-info/ 43 | .installed.cfg 44 | *.egg 45 | MANIFEST 46 | 47 | # PyInstaller 48 | # Usually these files are written by a python script from a template 49 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 50 | *.manifest 51 | *.spec 52 | 53 | # Installer logs 54 | pip-log.txt 55 | pip-delete-this-directory.txt 56 | 57 | # Unit test / coverage reports 58 | htmlcov/ 59 | .tox/ 60 | .nox/ 61 | .coverage 62 | .coverage.* 63 | .cache 64 | nosetests.xml 65 | coverage.xml 66 | *.cover 67 | *.py,cover 68 | .hypothesis/ 69 | .pytest_cache/ 70 | cover/ 71 | 72 | # Translations 73 | *.mo 74 | *.pot 75 | 76 | # Django stuff: 77 | *.log 78 | local_settings.py 79 | db.sqlite3 80 | db.sqlite3-journal 81 | 82 | # Flask stuff: 83 | instance/ 84 | .webassets-cache 85 | 86 | # Scrapy stuff: 87 | .scrapy 88 | 89 | # Sphinx documentation 90 | docs/_build/ 91 | 92 | # PyBuilder 93 | .pybuilder/ 94 | target/ 95 | 96 | # Jupyter Notebook 97 | .ipynb_checkpoints 98 | 99 | # IPython 100 | profile_default/ 101 | ipython_config.py 102 | 103 | # pyenv 104 | # For a library or package, you might want to ignore these files since the code is 105 | # intended to run in multiple environments; otherwise, check them in: 106 | # .python-version 107 | 108 | # pipenv 109 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 110 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 111 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 112 | # install all needed dependencies. 113 | #Pipfile.lock 114 | 115 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 116 | __pypackages__/ 117 | 118 | # Celery stuff 119 | celerybeat-schedule 120 | celerybeat.pid 121 | 122 | # SageMath parsed files 123 | *.sage.py 124 | 125 | # Environments 126 | .env 127 | .venv 128 | env/ 129 | venv/ 130 | ENV/ 131 | env.bak/ 132 | venv.bak/ 133 | 134 | # Spyder project settings 135 | .spyderproject 136 | .spyproject 137 | 138 | # Rope project settings 139 | .ropeproject 140 | 141 | # mkdocs documentation 142 | /site 143 | 144 | # mypy 145 | .mypy_cache/ 146 | .dmypy.json 147 | dmypy.json 148 | 149 | # Pyre type checker 150 | .pyre/ 151 | 152 | # pytype static type analyzer 153 | .pytype/ 154 | 155 | # Cython debug symbols 156 | cython_debug/ 157 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ami-iit/meshcat-cpp/a84be7add7f344d61e615bee7f26e6a7d5444f2a/.gitmodules -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Authors: Giulio Romualdi 2 | 3 | # Set cmake mimimun version 4 | cmake_minimum_required(VERSION 3.12) 5 | 6 | project(MeshcatCpp 7 | VERSION 0.1.0) 8 | 9 | # Avoid warnings with CMake 3.26 10 | if(POLICY CMP0135) 11 | cmake_policy(SET CMP0135 NEW) 12 | endif() 13 | 14 | # ouptut paths 15 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}") 16 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}") 17 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}") 18 | 19 | # Build shared libs 20 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) 21 | 22 | if(MSVC) 23 | set(CMAKE_DEBUG_POSTFIX "d") 24 | endif() 25 | 26 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 27 | 28 | option(BUILD_SHARED_LIBS "Build libraries as shared as opposed to static" ON) 29 | 30 | # Disable C and C++ compiler extensions. 31 | # C/CXX_EXTENSIONS are ON by default to allow the compilers to use extended 32 | # variants of the C/CXX language. 33 | # However, this could expose cross-platform bugs in user code or in the headers 34 | # of third-party dependencies and thus it is strongly suggested to turn 35 | # extensions off. 36 | set(CMAKE_C_EXTENSIONS OFF) 37 | set(CMAKE_CXX_EXTENSIONS OFF) 38 | 39 | # add GNU dirs 40 | include(GNUInstallDirs) 41 | 42 | include(CMakePackageConfigHelpers) 43 | 44 | option(ENABLE_RPATH "Enable RPATH for this library" ON) 45 | mark_as_advanced(ENABLE_RPATH) 46 | include(AddInstallRPATHSupport) 47 | add_install_rpath_support(BIN_DIRS "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}" 48 | LIB_DIRS "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" 49 | DEPENDS ENABLE_RPATH 50 | USE_LINK_PATH) 51 | 52 | # Encourage user to specify a build type (e.g. Release, Debug, etc.), otherwise set it to Release. 53 | if(NOT CMAKE_CONFIGURATION_TYPES) 54 | if(NOT CMAKE_BUILD_TYPE) 55 | message(STATUS "Setting build type to 'Release' as none was specified.") 56 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY VALUE "Release") 57 | endif() 58 | endif() 59 | 60 | 61 | 62 | find_package(Threads REQUIRED) 63 | find_package(ZLIB REQUIRED) 64 | include(MeshcatCppDeps) 65 | 66 | 67 | set(${PROJECT_NAME}_SRC 68 | src/Meshcat.cpp 69 | src/Material.cpp 70 | src/MsgpackTypes.cpp 71 | src/UUIDGenerator.cpp 72 | src/Shape.cpp) 73 | 74 | set(${PROJECT_NAME}_HDR 75 | include/MeshcatCpp/Meshcat.h 76 | include/MeshcatCpp/Material.h 77 | include/MeshcatCpp/MatrixView.h 78 | include/MeshcatCpp/Shape.h) 79 | 80 | cmrc_add_resource_library(${PROJECT_NAME}_resources 81 | ALIAS ${PROJECT_NAME}::resources 82 | NAMESPACE ${PROJECT_NAME} 83 | misc/index.html 84 | misc/main.min.js 85 | misc/favicon.ico) 86 | 87 | set_property(TARGET ${PROJECT_NAME}_resources PROPERTY POSITION_INDEPENDENT_CODE ON) 88 | 89 | 90 | add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SRC} ${${PROJECT_NAME}_HDR}) 91 | add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 92 | 93 | target_include_directories(${PROJECT_NAME} PRIVATE 94 | "$" 95 | "$") 96 | 97 | target_include_directories(${PROJECT_NAME} PUBLIC 98 | "$" 99 | "$") 100 | 101 | target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 102 | 103 | if(MSVC) 104 | target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20) 105 | else() 106 | target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) 107 | endif() 108 | 109 | target_link_libraries(${PROJECT_NAME} PRIVATE 110 | stduuid 111 | Threads::Threads 112 | ZLIB::ZLIB 113 | msgpack-cxx 114 | uWebSockets::uWebSockets 115 | ${PROJECT_NAME}::resources) 116 | 117 | 118 | set_target_properties(${PROJECT_NAME} PROPERTIES 119 | VERSION ${PROJECT_VERSION} 120 | PUBLIC_HEADER "${${PROJECT_NAME}_HDR}" 121 | ) 122 | 123 | install(TARGETS ${PROJECT_NAME} 124 | EXPORT ${PROJECT_NAME} 125 | COMPONENT runtime 126 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib 127 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT lib 128 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin 129 | PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}") 130 | 131 | include(InstallBasicPackageFiles) 132 | install_basic_package_files(${PROJECT_NAME} 133 | NAMESPACE MeshcatCpp:: 134 | VERSION ${${PROJECT_NAME}_VERSION} 135 | COMPATIBILITY SameMajorVersion 136 | VARS_PREFIX ${PROJECT_NAME} 137 | NO_CHECK_REQUIRED_COMPONENTS_MACRO) 138 | 139 | include(AddUninstallTarget) 140 | 141 | # Build examples 142 | option(MESHCAT_CPP_BUILT_EXAMPLES "Build the examples" OFF) 143 | if(${MESHCAT_CPP_BUILT_EXAMPLES}) 144 | add_subdirectory(examples) 145 | endif() 146 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, Giulio Romualdi 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 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

meshcat-cpp

3 |

4 |

5 | C++ Standard 6 | Size 7 |

8 | 9 | Self-contained C++ interface for the [MeshCat visualizer](https://github.com/rdeits/meshcat). 10 | 11 | **meshcat-cpp** took (heavy) inspiration for C++ (drake) MeshCat interface extensively discussed [here](https://github.com/RobotLocomotion/drake/issues/13038). The main purpose of this package is to have an independent (self-contained) C++ library that exposes the MeshCat interface in C++. 12 | 13 | --- 14 | 15 |

16 | 🚧 REPOSITORY UNDER DEVELOPMENT 🚧 17 |
The library implemented in this repository is still experimental and we cannot guarantee stable API 18 |

19 | 20 | --- 21 | 22 | ## 🏗️ Dependencies 23 | 24 | **meshcat-cpp** is a self-contained library. Most dependencies can be cloned at compile time and they are statically linked to the library. The only dependencies you need is a sufficiently recent C++ compiler (full support to C++20), `cmake`, `openssl`, `zlib`, `libuv`, `boost` and `pkg-config`. 25 | 26 | ### Install dependencies on Debian/Ubuntu 27 | 28 | ~~~ 29 | sudo apt install cmake pkg-config build-essential ninja-build git libssl-dev libuv1-dev libz-dev libboost-dev 30 | ~~~ 31 | 32 | ### Install dependencies with conda-forge 33 | 34 | ~~~ 35 | mamba create -n meshcatcppdev boost-cpp libuwebsockets cmake pkg-config compilers zlib openssl libuv msgpack-cxx 36 | ~~~ 37 | 38 | Then, execute all the other commands after activating the environment: 39 | ~~~ 40 | mamba activate meshcatcppdev 41 | ~~~ 42 | 43 | ## ⚒️ Build the library 44 | 45 | You can build the library coping and paste the following snippet into a terminal 46 | ```console 47 | git clone https://github.com/GiulioRomualdi/meshcat-cpp.git 48 | cd meshcat-cpp 49 | mkdir build && cd build 50 | cmake .. 51 | cmake --build . 52 | [sudo] make install 53 | ``` 54 | ## 🏃 Example 55 | 56 | **meshcat-cpp** provides native `CMake` support which allows the library to be easily used in `CMake` projects. Please add in your `CMakeLists.txt` 57 | ```cmake 58 | project(foo) 59 | find_package(MeshcatCpp REQUIRED) 60 | add_executable(${PROJECT_NAME} src/foo.cpp) 61 | target_link_libraries(${PROJECT_NAME} MeshcatCpp::MeshcatCpp) 62 | ``` 63 | 64 | Differently for the [python](https://github.com/rdeits/meshcat-python), 65 | [julia](https://github.com/rdeits/MeshCat.jl), and [C++ 66 | (drake)](https://drake.mit.edu/doxygen_cxx/classdrake_1_1geometry_1_1_meshcat_visualizer.html) 67 | interfaces this interface currently supports only a subset of functionalities (🚧 New 68 | functionalities will be soon implemented). The following example shows you how to display some primary shapes. 69 | ```cpp 70 | #include 71 | #include 72 | #include 73 | 74 | MeshcatCpp::MatrixView array_to_matrix_view(std::array& array) 75 | { 76 | constexpr MeshcatCpp::MatrixView::index_type rows = 4; 77 | constexpr MeshcatCpp::MatrixView::index_type cols = 4; 78 | constexpr auto order = MeshcatCpp::MatrixStorageOrdering::ColumnMajor; 79 | return MeshcatCpp::make_matrix_view(array.data(), rows, cols, order); 80 | } 81 | 82 | int main() 83 | { 84 | MeshcatCpp::Meshcat meshcat; 85 | MeshcatCpp::Material m; 86 | 87 | std::array transform = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; 88 | auto matrix_view = array_to_matrix_view(transform); 89 | 90 | m.set_color(66, 133, 244); 91 | meshcat.set_object("box", MeshcatCpp::Box(0.5, 0.5, 0.5), m); 92 | matrix_view(3, 1) = 1.75; 93 | meshcat.set_transform("box", matrix_view); 94 | 95 | m.set_color(234, 67, 53); 96 | meshcat.set_object("sphere", MeshcatCpp::Sphere(0.5), m); 97 | matrix_view(3, 1) = 0.75; 98 | meshcat.set_transform("sphere", matrix_view); 99 | 100 | m.set_color(251, 188, 5); 101 | meshcat.set_object("ellipsoid", MeshcatCpp::Ellipsoid(0.5, 0.25, 0.75), m); 102 | matrix_view(3, 1) = -0.75; 103 | meshcat.set_transform("ellipsoid", matrix_view); 104 | 105 | m.set_color(52, 168, 83); 106 | meshcat.set_object("cylinder", MeshcatCpp::Cylinder(0.25, 0.5), m); 107 | matrix_view(3, 1) = -1.75; 108 | meshcat.set_transform("cylinder", matrix_view); 109 | 110 | meshcat.join(); 111 | 112 | return 0; 113 | } 114 | ``` 115 | 116 | Once you have run the [example](./examples/meshcat_example.cpp), the `MeshcatCpp::Meshcat` class will print the `URL` at which the MeshCat server runs. Please open the link in your browser and you should be able to see the following screen 117 | 118 | ![meshcat_screen](https://user-images.githubusercontent.com/16744101/211311137-3271f266-1a65-4be8-9bd9-230f32bd2d83.png) 119 | 120 | 121 | ## 🐛 Bug reports and support 122 | 123 | All types of [issues](https://github.com/GiulioRomualdi/meshcat-cpp/issues/new) are welcome. 124 | 125 | ## 📝 License 126 | Materials in this repository are distributed under the following license: 127 | 128 | > All software is licensed under the BSD 3-Clause "New" or "Revised" License. See [LICENSE](https://github.com/GiulioRomualdi/meshcat-cpp/blob/master/LICENSE) file for details. 129 | -------------------------------------------------------------------------------- /cmake/AddInstallRPATHSupport.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # AddInstallRPATHSupport 3 | # ---------------------- 4 | # 5 | # Add support to RPATH during installation to your project:: 6 | # 7 | # add_install_rpath_support([BIN_DIRS dir [dir]] 8 | # [LIB_DIRS dir [dir]] 9 | # [INSTALL_NAME_DIR [dir]] 10 | # [DEPENDS condition [condition]] 11 | # [USE_LINK_PATH]) 12 | # 13 | # Normally (depending on the platform) when you install a shared 14 | # library you can either specify its absolute path as the install name, 15 | # or leave just the library name itself. In the former case the library 16 | # will be correctly linked during run time by all executables and other 17 | # shared libraries, but it must not change its install location. This 18 | # is often the case for libraries installed in the system default 19 | # library directory (e.g. ``/usr/lib``). 20 | # In the latter case, instead, the library can be moved anywhere in the 21 | # file system but at run time the dynamic linker must be able to find 22 | # it. This is often accomplished by setting environmental variables 23 | # (i.e. ``LD_LIBRARY_PATH`` on Linux). 24 | # This procedure is usually not desirable for two main reasons: 25 | # 26 | # - by setting the variable you are changing the default behaviour 27 | # of the dynamic linker thus potentially breaking executables (not as 28 | # destructive as ``LD_PRELOAD``) 29 | # - the variable will be used only by applications spawned by the shell 30 | # and not by other processes. 31 | # 32 | # RPATH is aimed to solve the issues introduced by the second 33 | # installation method. Using run-path dependent libraries you can 34 | # create a directory structure containing executables and dependent 35 | # libraries that users can relocate without breaking it. 36 | # A run-path dependent library is a dependent library whose complete 37 | # install name is not known when the library is created. 38 | # Instead, the library specifies that the dynamic loader must resolve 39 | # the library’s install name when it loads the executable that depends 40 | # on the library. The executable or the other shared library will 41 | # hardcode in the binary itself the additional search directories 42 | # to be passed to the dynamic linker. This works great in conjunction 43 | # with relative paths. 44 | # This command will enable support to RPATH to your project. 45 | # It will enable the following things: 46 | # 47 | # - If the project builds shared libraries it will generate a run-path 48 | # enabled shared library, i.e. its install name will be resolved 49 | # only at run time. 50 | # - In all cases (building executables and/or shared libraries) 51 | # dependent shared libraries with RPATH support will be properly 52 | # 53 | # The command has the following parameters: 54 | # 55 | # Options: 56 | # - ``USE_LINK_PATH``: if passed the command will automatically adds to 57 | # the RPATH the path to all the dependent libraries. 58 | # 59 | # Arguments: 60 | # - ``BIN_DIRS`` list of directories when the targets (executable and 61 | # plugins) will be installed. 62 | # - ``LIB_DIRS`` list of directories to be added to the RPATH. These 63 | # directories will be added "relative" w.r.t. the ``BIN_DIRS`` and 64 | # ``LIB_DIRS``. 65 | # - ``INSTALL_NAME_DIR`` directory where the libraries will be installed. 66 | # This variable will be used only if ``CMAKE_SKIP_RPATH`` or 67 | # ``CMAKE_SKIP_INSTALL_RPATH`` is set to ``TRUE`` as it will set the 68 | # ``INSTALL_NAME_DIR`` on all targets 69 | # - ``DEPENDS`` list of conditions that should be ``TRUE`` to enable 70 | # RPATH, for example ``FOO; NOT BAR``. 71 | # 72 | # Note: see https://gitlab.kitware.com/cmake/cmake/issues/16589 for further 73 | # details. 74 | 75 | #======================================================================= 76 | # Copyright 2014 Istituto Italiano di Tecnologia (IIT) 77 | # @author Francesco Romano 78 | # 79 | # Distributed under the OSI-approved BSD License (the "License"); 80 | # see accompanying file Copyright.txt for details. 81 | # 82 | # This software is distributed WITHOUT ANY WARRANTY; without even the 83 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 84 | # See the License for more information. 85 | #======================================================================= 86 | # (To distribute this file outside of CMake, substitute the full 87 | # License text for the above reference.) 88 | 89 | 90 | include(CMakeParseArguments) 91 | 92 | 93 | function(ADD_INSTALL_RPATH_SUPPORT) 94 | 95 | set(_options USE_LINK_PATH) 96 | set(_oneValueArgs INSTALL_NAME_DIR) 97 | set(_multiValueArgs BIN_DIRS 98 | LIB_DIRS 99 | DEPENDS) 100 | 101 | cmake_parse_arguments(_ARS "${_options}" 102 | "${_oneValueArgs}" 103 | "${_multiValueArgs}" 104 | "${ARGN}") 105 | 106 | # if either RPATH or INSTALL_RPATH is disabled 107 | # and the INSTALL_NAME_DIR variable is set, then hardcode the install name 108 | if(CMAKE_SKIP_RPATH OR CMAKE_SKIP_INSTALL_RPATH) 109 | if(DEFINED _ARS_INSTALL_NAME_DIR) 110 | set(CMAKE_INSTALL_NAME_DIR ${_ARS_INSTALL_NAME_DIR} PARENT_SCOPE) 111 | endif() 112 | endif() 113 | 114 | if (CMAKE_SKIP_RPATH OR (CMAKE_SKIP_INSTALL_RPATH AND CMAKE_SKIP_BUILD_RPATH)) 115 | return() 116 | endif() 117 | 118 | 119 | set(_rpath_available 1) 120 | if(DEFINED _ARS_DEPENDS) 121 | foreach(_dep ${_ARS_DEPENDS}) 122 | string(REGEX REPLACE " +" ";" _dep "${_dep}") 123 | if(NOT (${_dep})) 124 | set(_rpath_available 0) 125 | endif() 126 | endforeach() 127 | endif() 128 | 129 | if(_rpath_available) 130 | 131 | # Enable RPATH on OSX. 132 | set(CMAKE_MACOSX_RPATH TRUE PARENT_SCOPE) 133 | 134 | # Find system implicit lib directories 135 | set(_system_lib_dirs ${CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES}) 136 | if(EXISTS "/etc/debian_version") # is this a debian system ? 137 | if(CMAKE_LIBRARY_ARCHITECTURE) 138 | list(APPEND _system_lib_dirs "/lib/${CMAKE_LIBRARY_ARCHITECTURE}" 139 | "/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}") 140 | endif() 141 | endif() 142 | # This is relative RPATH for libraries built in the same project 143 | foreach(lib_dir ${_ARS_LIB_DIRS}) 144 | list(FIND _system_lib_dirs "${lib_dir}" isSystemDir) 145 | if("${isSystemDir}" STREQUAL "-1") 146 | foreach(bin_dir ${_ARS_LIB_DIRS} ${_ARS_BIN_DIRS}) 147 | file(RELATIVE_PATH _rel_path ${bin_dir} ${lib_dir}) 148 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 149 | list(APPEND CMAKE_INSTALL_RPATH "@loader_path/${_rel_path}") 150 | else() 151 | list(APPEND CMAKE_INSTALL_RPATH "\$ORIGIN/${_rel_path}") 152 | endif() 153 | endforeach() 154 | endif() 155 | endforeach() 156 | if(NOT "${CMAKE_INSTALL_RPATH}" STREQUAL "") 157 | list(REMOVE_DUPLICATES CMAKE_INSTALL_RPATH) 158 | endif() 159 | set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH} PARENT_SCOPE) 160 | 161 | # add the automatically determined parts of the RPATH 162 | # which point to directories outside the build tree to the install RPATH 163 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH ${_ARS_USE_LINK_PATH} PARENT_SCOPE) 164 | 165 | endif() 166 | 167 | endfunction() 168 | -------------------------------------------------------------------------------- /cmake/AddUninstallTarget.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # AddUninstallTarget 3 | # ------------------ 4 | # 5 | # Add the "uninstall" target for your project:: 6 | # 7 | # include(AddUninstallTarget) 8 | # 9 | # 10 | # will create a file cmake_uninstall.cmake in the build directory and add a 11 | # custom target uninstall that will remove the files installed by your package 12 | # (using install_manifest.txt) 13 | 14 | #============================================================================= 15 | # Copyright 2008-2013 Kitware, Inc. 16 | # Copyright 2013 iCub Facility, Istituto Italiano di Tecnologia 17 | # Authors: Daniele E. Domenichelli 18 | # 19 | # Distributed under the OSI-approved BSD License (the "License"); 20 | # see accompanying file Copyright.txt for details. 21 | # 22 | # This software is distributed WITHOUT ANY WARRANTY; without even the 23 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 24 | # See the License for more information. 25 | #============================================================================= 26 | # (To distribute this file outside of CMake, substitute the full 27 | # License text for the above reference.) 28 | 29 | 30 | if(DEFINED __ADD_UNINSTALL_TARGET_INCLUDED) 31 | return() 32 | endif() 33 | set(__ADD_UNINSTALL_TARGET_INCLUDED TRUE) 34 | 35 | 36 | set(_filename ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) 37 | 38 | file(WRITE ${_filename} 39 | "if(NOT EXISTS \"${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt\") 40 | message(WARNING \"Cannot find install manifest: \\\"${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt\\\"\") 41 | return() 42 | endif() 43 | 44 | file(READ \"${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt\" files) 45 | string(STRIP \"\${files}\" files) 46 | string(REGEX REPLACE \"\\n\" \";\" files \"\${files}\") 47 | list(REVERSE files) 48 | foreach(file \${files}) 49 | message(STATUS \"Uninstalling: \$ENV{DESTDIR}\${file}\") 50 | if(EXISTS \"\$ENV{DESTDIR}\${file}\") 51 | execute_process( 52 | COMMAND \${CMAKE_COMMAND} -E remove \"\$ENV{DESTDIR}\${file}\" 53 | OUTPUT_VARIABLE rm_out 54 | RESULT_VARIABLE rm_retval) 55 | if(NOT \"\${rm_retval}\" EQUAL 0) 56 | message(FATAL_ERROR \"Problem when removing \\\"\$ENV{DESTDIR}\${file}\\\"\") 57 | endif() 58 | else() 59 | message(STATUS \"File \\\"\$ENV{DESTDIR}\${file}\\\" does not exist.\") 60 | endif() 61 | endforeach(file) 62 | ") 63 | 64 | if("${CMAKE_GENERATOR}" MATCHES "^(Visual Studio|Xcode)") 65 | set(_uninstall "UNINSTALL") 66 | else() 67 | set(_uninstall "uninstall") 68 | endif() 69 | add_custom_target(${_uninstall} COMMAND "${CMAKE_COMMAND}" -P "${_filename}") 70 | set_property(TARGET ${_uninstall} PROPERTY FOLDER "CMakePredefinedTargets") 71 | -------------------------------------------------------------------------------- /cmake/FetchuWebSockets.cmake: -------------------------------------------------------------------------------- 1 | # Download uSockets and uWebSockets 2 | include(FetchContent) 3 | 4 | FetchContent_Declare(usockets 5 | URL https://github.com/uNetworking/uSockets/archive/refs/tags/v0.8.5.zip) 6 | FetchContent_GetProperties(usockets) 7 | 8 | FetchContent_Declare(uwebsockets 9 | URL https://github.com/uNetworking/uWebSockets/archive/refs/tags/v20.37.0.zip) 10 | FetchContent_GetProperties(uwebsockets) 11 | 12 | if(NOT usockets_POPULATED AND NOT uwebsockets_POPULATED) 13 | FetchContent_Populate(usockets) 14 | FetchContent_Populate(uwebsockets) 15 | 16 | # Build uSockets 17 | find_package(OpenSSL REQUIRED) 18 | find_package(PkgConfig REQUIRED) 19 | pkg_check_modules(UV REQUIRED IMPORTED_TARGET libuv) 20 | 21 | # See https://github.com/uNetworking/uSockets/blob/v0.8.5/Makefile#L66 22 | # and https://github.com/uNetworking/uSockets/blob/v0.8.5/Makefile#L74 23 | file(GLOB US_SRCS ${usockets_SOURCE_DIR}/src/*.c ${usockets_SOURCE_DIR}/src/eventing/*.c 24 | ${usockets_SOURCE_DIR}/src/crypto/*.c ${usockets_SOURCE_DIR}/src/crypto/*.cpp) 25 | 26 | # Hardcode static library as we do not install the library and we do compile as shared 27 | add_library(uSockets STATIC ${US_SRCS}) 28 | add_library(uSockets::uSockets ALIAS uSockets) 29 | # Necessary to link a static library to a shared library 30 | set_property(TARGET uSockets PROPERTY POSITION_INDEPENDENT_CODE ON) 31 | 32 | target_include_directories(uSockets PUBLIC 33 | $ 34 | ) 35 | 36 | target_link_libraries(uSockets PRIVATE OpenSSL::SSL OpenSSL::Crypto PkgConfig::UV) 37 | 38 | # See https://github.com/uNetworking/uSockets/blob/v0.8.5/Makefile#L32 39 | # and https://github.com/uNetworking/uSockets/blob/v0.8.5/Makefile#L15 40 | target_compile_definitions(uSockets PRIVATE -DLIBUS_USE_LIBUV -DLIBUS_USE_OPENSSL) 41 | target_compile_features(uSockets PRIVATE cxx_std_17) 42 | 43 | # Define target for uWebSockets 44 | add_library(uWebSockets INTERFACE) 45 | add_library(uWebSockets::uWebSockets ALIAS uWebSockets) 46 | 47 | target_include_directories(uWebSockets INTERFACE 48 | $ 49 | ) 50 | 51 | target_link_libraries(uWebSockets INTERFACE uSockets::uSockets ZLIB::ZLIB) 52 | target_compile_features(uWebSockets INTERFACE cxx_std_17) 53 | endif() 54 | 55 | -------------------------------------------------------------------------------- /cmake/FinduSockets.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2023 Istituto Italiano di Tecnologia (IIT) 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | #[=======================================================================[.rst: 5 | FinduSockets 6 | ----------- 7 | 8 | The following imported targets are created: 9 | 10 | uSockets::uSockets 11 | 12 | #]=======================================================================] 13 | 14 | include(FindPackageHandleStandardArgs) 15 | 16 | find_path(uSockets_INCLUDE_DIR libusockets.h) 17 | mark_as_advanced(uSockets_INCLUDE_DIR) 18 | find_library(uSockets_LIBRARY uSockets libuSockets) 19 | mark_as_advanced(uSockets_LIBRARY) 20 | 21 | find_package_handle_standard_args(uSockets DEFAULT_MSG uSockets_INCLUDE_DIR uSockets_LIBRARY) 22 | 23 | if(uSockets_FOUND) 24 | if(NOT TARGET uSockets::uSockets) 25 | add_library(uSockets::uSockets UNKNOWN IMPORTED) 26 | set_target_properties(uSockets::uSockets PROPERTIES 27 | INTERFACE_INCLUDE_DIRECTORIES "${uSockets_INCLUDE_DIR}") 28 | set_property(TARGET uSockets::uSockets PROPERTY 29 | IMPORTED_LOCATION "${uSockets_LIBRARY}") 30 | endif() 31 | endif() 32 | -------------------------------------------------------------------------------- /cmake/FinduWebSockets.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2023 Istituto Italiano di Tecnologia (IIT) 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | #[=======================================================================[.rst: 5 | FinduWebSockets 6 | ----------- 7 | 8 | Find the uWebSockets library. 9 | 10 | The following imported targets are created: 11 | 12 | uWebSockets::uWebSockets 13 | 14 | #]=======================================================================] 15 | 16 | include(FindPackageHandleStandardArgs) 17 | include(CMakeFindDependencyMacro) 18 | 19 | find_dependency(uSockets REQUIRED) 20 | find_dependency(ZLIB REQUIRED) 21 | 22 | # We do not use WebSocket.h as it has the same name of a 23 | # Windows system header, found for example in 24 | # C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0/um 25 | find_path(uWebSockets_INCLUDE_DIR WebSocketProtocol.h PATH_SUFFIXES uWebSockets uwebsockets) 26 | 27 | mark_as_advanced(uWebSockets_INCLUDE_DIR) 28 | 29 | find_package_handle_standard_args(uWebSockets DEFAULT_MSG uWebSockets_INCLUDE_DIR) 30 | 31 | if(uWebSockets_FOUND) 32 | if(NOT TARGET uWebSockets::uWebSockets) 33 | add_library(uWebSockets::uWebSockets INTERFACE IMPORTED) 34 | set_target_properties(uWebSockets::uWebSockets PROPERTIES 35 | INTERFACE_INCLUDE_DIRECTORIES "${uWebSockets_INCLUDE_DIR}") 36 | set_target_properties(uWebSockets::uWebSockets PROPERTIES 37 | INTERFACE_LINK_LIBRARIES ZLIB::ZLIB) 38 | set_property(TARGET uWebSockets::uWebSockets APPEND PROPERTY 39 | INTERFACE_LINK_LIBRARIES uSockets::uSockets) 40 | endif() 41 | endif() 42 | -------------------------------------------------------------------------------- /cmake/InstallBasicPackageFiles.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2012-2021 Istituto Italiano di Tecnologia (IIT) 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | #[=======================================================================[.rst: 5 | InstallBasicPackageFiles 6 | ------------------------ 7 | 8 | A helper module to make your package easier to be found by other 9 | projects. 10 | 11 | 12 | .. command:: install_basic_package_files 13 | 14 | Create and install a basic version of cmake config files for your 15 | project:: 16 | 17 | install_basic_package_files( 18 | COMPATIBILITY 19 | [VERSION ] 20 | [ARCH_INDEPENDENT] 21 | [NO_EXPORT | EXPORT ] # (default = "EXPORT ") 22 | [NO_SET_AND_CHECK_MACRO] 23 | [NO_CHECK_REQUIRED_COMPONENTS_MACRO] 24 | [VARS_PREFIX ] # (default = "") 25 | [EXPORT_DESTINATION ] 26 | [INSTALL_DESTINATION ] 27 | [NAMESPACE ] # (default = "::") 28 | [EXTRA_PATH_VARS_SUFFIX path1 [path2 ...]] 29 | [CONFIG_TEMPLATE ] 30 | [UPPERCASE_FILENAMES | LOWERCASE_FILENAMES] 31 | [DEPENDENCIES " [...]" ...] 32 | [PRIVATE_DEPENDENCIES " [...]" ...] 33 | [INCLUDE_FILE | INCLUDE_CONTENT ] 34 | [COMPONENT ] # (default = "") 35 | ) 36 | 37 | Depending on ``UPPERCASE_FILENAMES`` and ``LOWERCASE_FILENAMES``, this 38 | function generates 3 files: 39 | 40 | - ``ConfigVersion.cmake`` or ``-config-version.cmake`` 41 | - ``Config.cmake`` or ``-config.cmake`` 42 | - ``Targets.cmake`` or ``-targets.cmake`` 43 | 44 | If neither ``UPPERCASE_FILENAMES`` nor ``LOWERCASE_FILENAMES`` is 45 | set, a file ``Config.cmake.in`` or 46 | ``-config.cmake.in`` is searched, and the convention 47 | is chosed according to the file found. If no file was found, the 48 | uppercase convention is used. 49 | 50 | The ``DEPENDENCIES`` argument can be used to set a list of dependencies 51 | that will be searched using the :cmake:command:`find_dependency` command 52 | from the :module:`CMakeFindDependencyMacro` module. 53 | Dependencies can be followed by any of the possible 54 | :cmake:command:`find_dependency` argument. 55 | In this case, all the arguments must be specified within double quotes (e.g. 56 | ``" 1.0.0 EXACT"``, or ``" CONFIG"``). 57 | The ``PRIVATE_DEPENDENCIES`` argument is similar to ``DEPENDENCIES``, but 58 | these dependencies are included only when :variable:`BUILD_SHARED_LIBS` is 59 | ``OFF``. 60 | If a libraries is declared ``STATIC``, ``OBJECT`` or ``INTERFACE``, and they 61 | link to some dependency, these should be added using the ``DEPENDENCIES`` 62 | argument, since the ``PRIVATE_DEPENDENCIES`` argument would work only when 63 | :variable:`BUILD_SHARED_LIBS` is disabled. 64 | 65 | When using a custom template file, the ``@PACKAGE_DEPENDENCIES@`` 66 | string is replaced with the code checking for the dependencies 67 | specified by these two argument. 68 | 69 | If the ``ARCH_INDEPENDENT`` option is enabled, the installed package version 70 | will be considered compatible even if it was built for a different 71 | architecture than the requested architecture. 72 | 73 | Each file is generated twice, one for the build directory and one for 74 | the installation directory. The ``INSTALL_DESTINATION`` argument can be 75 | passed to install the files in a location different from the default 76 | one (``${CMAKE_INSTALL_DATADIR}/cmake/${Name}`` if the ``ARCH_INDEPENDENT`` 77 | option is enabled, ``${CMAKE_INSTALL_LIBDIR}/cmake/${Name}`` otherwise). 78 | The ``EXPORT_DESTINATION`` argument can be passed to 79 | generate the files in the build tree in a location different from the default 80 | one (``CMAKE_BINARY_DIR``). If this is a relative path, it is 81 | considered relative to the ``CMAKE_CURRENT_BINARY_DIR`` directory. 82 | 83 | The build directory is exported to the CMake user package registry if the 84 | build option ``CMAKE_EXPORT_PACKAGE_REGISTRY`` is set. 85 | 86 | The ``ConfigVersion.cmake`` file is generated using 87 | :cmake:command:`write_basic_package_version_file`. The ``VERSION``, 88 | ``COMPATIBILITY``, and ``ARCH_INDEPENDENT``arguments are passed to this 89 | function. 90 | 91 | ``VERSION`` shall be in the form ``[.[.[.]]]]``. 92 | If no ``VERSION`` is given, the ``PROJECT_VERSION`` variable is used. 93 | If this hasn’t been set, it errors out. The ``VERSION`` argument is also used 94 | to replace the ``@PACKAGE_VERSION@`` string in the configuration file. 95 | 96 | ``COMPATIBILITY`` shall be any of the options accepted by the 97 | :cmake:command:`write_basic_package_version_file` command 98 | (``AnyNewerVersion``, ``SameMajorVersion``, ``SameMinorVersion`` [CMake 3.11], 99 | or ``ExactVersion``). 100 | These options are explained in :cmake:command:`write_basic_package_version_file` 101 | command documentation. 102 | If your project has more elaborate version matching rules, you will need to 103 | write your own custom ConfigVersion.cmake file instead of using this macro. 104 | 105 | The ``Config.cmake`` file is generated using 106 | :cmake:command:`configure_package_config_file`. The 107 | ``NO_SET_AND_CHECK_MACRO``, ``NO_CHECK_REQUIRED_COMPONENTS_MACRO``, and 108 | arguments are passed to this function. 109 | 110 | By default :command:`install_basic_package_files` also generates the two helper 111 | macros ``set_and_check()`` and ``check_required_components()`` into the 112 | ``Config.cmake`` file. ``set_and_check()`` should be used instead of the 113 | normal :cmake:command:`set()` command for setting directories and file locations. 114 | Additionally to setting the variable it also checks that the referenced file 115 | or directory actually exists and fails with a ``FATAL_ERROR`` otherwise. 116 | This makes sure that the created ``Config.cmake`` file does not contain 117 | wrong references. 118 | When using the ``NO_SET_AND_CHECK_MACRO, this macro is not generated into the 119 | ``Config.cmake`` file. 120 | 121 | By default, :command:`install_basic_package_files` append a call to 122 | ``check_required_components()`` in ``Config.cmake`` file if the 123 | package supports components. This macro checks whether all requested, 124 | non-optional components have been found, and if this is not the case, sets the 125 | ``_FOUND`` variable to ``FALSE``, so that the package is considered to 126 | be not found. It does that by testing the ``__FOUND`` 127 | variables for all requested required components. When using the 128 | ``NO_CHECK_REQUIRED_COMPONENTS_MACRO`` option, this macro is not generated 129 | into the ``Config.cmake`` file. 130 | 131 | Finally, the files in the build and install directory are exactly the same. 132 | 133 | See the documentation of :module:`CMakePackageConfigHelpers` module for 134 | further information and references therein. 135 | 136 | If the ``CONFIG_TEMPLATE`` argument is passed, the specified file 137 | is used as template for generating the configuration file, otherwise 138 | this module expects to find a ``Config.cmake.in`` or 139 | ``-config.cmake.in`` file either in current source directory. 140 | If the file does not exist, a very basic file is created. 141 | 142 | A set of variables are checked and passed to 143 | :cmake:command:`configure_package_config_file` as ``PATH_VARS``. For each of the 144 | ``SUFFIX`` considered, if one of the variables:: 145 | 146 | _(BUILD|INSTALL)_ 147 | (BUILD|INSTALL)__ 148 | 149 | is defined, the ``_`` variable will be defined 150 | before configuring the package. In order to use that variable in the 151 | config file, you have to add a line:: 152 | 153 | set_and_check(_ \"@PACKAGE__@\") 154 | 155 | if the path must exist or just:: 156 | 157 | set(_ \"@PACKAGE__@\") 158 | 159 | if the path could be missing. 160 | 161 | These variable will have different values whether you are using the 162 | package from the build tree or from the install directory. Also these 163 | files will contain only relative paths, meaning that you can move the 164 | whole installation and the CMake files will still work. 165 | 166 | Default ``PATH_VARS`` suffixes are:: 167 | 168 | BINDIR BIN_DIR 169 | SBINDIR SBIN_DIR 170 | LIBEXECDIR LIBEXEC_DIR 171 | SYSCONFDIR SYSCONF_DIR 172 | SHAREDSTATEDIR SHAREDSTATE_DIR 173 | LOCALSTATEDIR LOCALSTATE_DIR 174 | LIBDIR LIB_DIR 175 | INCLUDEDIR INCLUDE_DIR 176 | OLDINCLUDEDIR OLDINCLUDE_DIR 177 | DATAROOTDIR DATAROOT_DIR 178 | DATADIR DATA_DIR 179 | INFODIR INFO_DIR 180 | LOCALEDIR LOCALE_DIR 181 | MANDIR MAN_DIR 182 | DOCDIR DOC_DIR 183 | 184 | more suffixes can be added using the ``EXTRA_PATH_VARS_SUFFIX`` 185 | argument. 186 | 187 | 188 | The ``Targets.cmake`` is generated using :cmake:command:`export(EXPORT)` 189 | in the build tree and :cmake:command:`install(EXPORT)` in the installation 190 | directory. The targets are exported using the value for the ``NAMESPACE`` 191 | argument as namespace. 192 | The export can be passed using the ``EXPORT`` argument. If no export is 193 | used (e.g. for a CMake script library), pass ``NO_EXPORT``. 194 | 195 | If the ``INCLUDE_FILE`` argument is passed, the content of the specified file 196 | (which might contain ``@variables@``) is appended to the generated 197 | ``Config.cmake`` file. 198 | If the ``INCLUDE_CONTENT`` argument is passed, the specified content 199 | (which might contain ``@variables@``) is appended to the generated 200 | ``Config.cmake`` file. 201 | When a ``CONFIG_TEMPLATE`` is passed, or a ``ConfigVersion.cmake.in`` or 202 | a ``-config-version.cmake.in file is available, these 2 arguments are 203 | used to replace the ``@INCLUDED_CONTENT@`` string in this file. 204 | This allows one to inject custom code to this file, useful e.g. to set 205 | additional variables which are loaded by downstream projects. 206 | 207 | Note that content specified with ``INCLUDE_FILE`` or ``INCLUDE_CONTENT`` 208 | cannot reference any of the ``PATH_VARS`` because this content is not 209 | expanded by :cmake:command:`configure_package_config_file`. 210 | 211 | If the ``COMPONENT`` argument is passed, it is forwarded to the 212 | :cmake:command:`install` commands, otherwise ```` is used. 213 | #]=======================================================================] 214 | 215 | 216 | if(COMMAND install_basic_package_files) 217 | return() 218 | endif() 219 | 220 | 221 | include(GNUInstallDirs) 222 | include(CMakePackageConfigHelpers) 223 | include(CMakeParseArguments) 224 | 225 | 226 | function(INSTALL_BASIC_PACKAGE_FILES _Name) 227 | 228 | set(_options ARCH_INDEPENDENT 229 | NO_EXPORT 230 | NO_SET_AND_CHECK_MACRO 231 | NO_CHECK_REQUIRED_COMPONENTS_MACRO 232 | UPPERCASE_FILENAMES 233 | LOWERCASE_FILENAMES 234 | NO_COMPATIBILITY_VARS # Deprecated 235 | ENABLE_COMPATIBILITY_VARS) # Deprecated 236 | set(_oneValueArgs VERSION 237 | COMPATIBILITY 238 | EXPORT 239 | FIRST_TARGET # Deprecated 240 | TARGETS_PROPERTY # Deprecated 241 | VARS_PREFIX 242 | EXPORT_DESTINATION 243 | INSTALL_DESTINATION 244 | DESTINATION # Deprecated 245 | NAMESPACE 246 | CONFIG_TEMPLATE 247 | INCLUDE_FILE 248 | INCLUDE_CONTENT 249 | COMPONENT) 250 | set(_multiValueArgs EXTRA_PATH_VARS_SUFFIX 251 | TARGETS # Deprecated 252 | TARGETS_PROPERTIES # Deprecated 253 | DEPENDENCIES 254 | PRIVATE_DEPENDENCIES) 255 | cmake_parse_arguments(_IBPF "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" "${ARGN}") 256 | 257 | if(NOT DEFINED _IBPF_VARS_PREFIX) 258 | set(_IBPF_VARS_PREFIX ${_Name}) 259 | endif() 260 | 261 | if(NOT DEFINED _IBPF_VERSION) 262 | if(NOT DEFINED PROJECT_VERSION) 263 | message(FATAL_ERROR "VERSION argument is required (PROJECT_VERSION is not defined)") 264 | endif() 265 | set(_IBPF_VERSION ${PROJECT_VERSION}) 266 | endif() 267 | 268 | if(NOT DEFINED _IBPF_COMPATIBILITY) 269 | message(FATAL_ERROR "COMPATIBILITY argument is required") 270 | endif() 271 | 272 | unset(_arch_independent) 273 | if(_IBPF_ARCH_INDEPENDENT) 274 | set(_arch_independent ARCH_INDEPENDENT) 275 | endif() 276 | 277 | if(_IBPF_UPPERCASE_FILENAMES AND _IBPF_LOWERCASE_FILENAMES) 278 | message(FATAL_ERROR "UPPERCASE_FILENAMES and LOWERCASE_FILENAMES arguments cannot be used together") 279 | endif() 280 | 281 | if(DEFINED _IBPF_INCLUDE_FILE AND DEFINED _IBPF_INCLUDE_CONTENT) 282 | message(FATAL_ERROR "INCLUDE_FILE and INCLUDE_CONTENT arguments cannot be used together") 283 | endif() 284 | 285 | # Prepare install and export commands 286 | unset(_targets) 287 | set(_install_cmd EXPORT ${_Name}) 288 | set(_export_cmd EXPORT ${_Name}) 289 | 290 | if(DEFINED _IBPF_EXPORT) 291 | if(_IBPF_NO_EXPORT OR DEFINED _IBPF_TARGETS OR DEFINED _IBPF_TARGETS_PROPERTIES OR DEFINED _IBPF_TARGETS_PROPERTIES) 292 | message(FATAL_ERROR "EXPORT cannot be used with NO_EXPORT, TARGETS, TARGETS_PROPERTY, or TARGETS_PROPERTIES") 293 | endif() 294 | 295 | set(_export_cmd EXPORT ${_IBPF_EXPORT}) 296 | set(_install_cmd EXPORT ${_IBPF_EXPORT}) 297 | 298 | elseif(_IBPF_NO_EXPORT) 299 | if(DEFINED _IBPF_TARGETS OR DEFINED _IBPF_TARGETS_PROPERTIES OR DEFINED _IBPF_TARGETS_PROPERTIES) 300 | message(FATAL_ERROR "NO_EXPORT cannot be used with TARGETS, TARGETS_PROPERTY, or TARGETS_PROPERTIES") 301 | endif() 302 | 303 | elseif(DEFINED _IBPF_TARGETS) 304 | message(DEPRECATION "TARGETS is deprecated. Use EXPORT instead") 305 | 306 | if(DEFINED _IBPF_TARGETS_PROPERTY OR DEFINED _IBPF_TARGETS_PROPERTIES) 307 | message(FATAL_ERROR "TARGETS cannot be used with TARGETS_PROPERTY or TARGETS_PROPERTIES") 308 | endif() 309 | 310 | set(_targets ${_IBPF_TARGETS}) 311 | set(_export_cmd TARGETS ${_IBPF_TARGETS}) 312 | 313 | elseif(DEFINED _IBPF_TARGETS_PROPERTY) 314 | message(DEPRECATION "TARGETS_PROPERTY is deprecated. Use EXPORT instead") 315 | 316 | if(DEFINED _IBPF_TARGETS_PROPERTIES) 317 | message(FATAL_ERROR "TARGETS_PROPERTIES cannot be used with TARGETS_PROPERTIES") 318 | endif() 319 | 320 | get_property(_targets GLOBAL PROPERTY ${_IBPF_TARGETS_PROPERTY}) 321 | set(_export_cmd TARGETS ${_targets}) 322 | 323 | elseif(DEFINED _IBPF_TARGETS_PROPERTIES) 324 | message(DEPRECATION "TARGETS_PROPERTIES is deprecated. Use EXPORT instead") 325 | 326 | set(_targets "") # Defined but empty 327 | foreach(_prop ${_IBPF_TARGETS_PROPERTIES}) 328 | get_property(_prop_val GLOBAL PROPERTY ${_prop}) 329 | list(APPEND _targets ${_prop_val}) 330 | endforeach() 331 | set(_export_cmd TARGETS ${_targets}) 332 | 333 | endif() 334 | 335 | # Path for installed cmake files 336 | if(DEFINED _IBPF_DESTINATION) 337 | message(DEPRECATION "DESTINATION is deprecated. Use INSTALL_DESTINATION instead") 338 | if(NOT DEFINED _IBPF_INSTALL_DESTINATION) 339 | set(_IBPF_INSTALL_DESTINATION ${_IBPF_DESTINATION}) 340 | endif() 341 | endif() 342 | 343 | # If not set by the user, choose an adequate destination 344 | if(NOT DEFINED _IBPF_INSTALL_DESTINATION) 345 | if(_IBPF_ARCH_INDEPENDENT) 346 | set(_IBPF_INSTALL_DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/${_Name}) 347 | else() 348 | set(_IBPF_INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${_Name}) 349 | endif() 350 | endif() 351 | 352 | # FIRST_TARGET is no longer used 353 | if(DEFINED _IBPF_FIRST_TARGET) 354 | message(DEPRECATION "FIRST_TARGET is deprecated.") 355 | endif() 356 | 357 | # NO_COMPATIBILITY_VARS and ENABLE_COMPATIBILITY_VARS cannot be used together 358 | if(_IBPF_NO_COMPATIBILITY_VARS AND _ENABLE_COMPATIBILITY_VARS) 359 | message(FATAL_ERROR "NO_COMPATIBILITY_VARS and ENABLE_COMPATIBILITY_VARS cannot be used together") 360 | endif() 361 | # NO_COMPATIBILITY_VARS is deprecated 362 | if(_IBPF_NO_COMPATIBILITY_VARS) 363 | message(DEPRECATION "NO_COMPATIBILITY_VARS is deprecated.") 364 | endif() 365 | # ENABLE_COMPATIBILITY_VARS is deprecated 366 | if(_IBPF_ENABLE_COMPATIBILITY_VARS) 367 | message(DEPRECATION "ENABLE_COMPATIBILITY_VARS is deprecated.") 368 | endif() 369 | # ENABLE_COMPATIBILITY_VARS does not work with EXPORT 370 | if(NOT DEFINED _targets AND _IBPF_ENABLE_COMPATIBILITY_VARS) 371 | message(FATAL_ERROR "ENABLE_COMPATIBILITY_VARS does not work with EXPORT") 372 | endif() 373 | # ENABLE_COMPATIBILITY_VARS can be enabled for projects still using targets 374 | if(DEFINED _targets AND NOT _IBPF_NO_COMPATIBILITY_VARS AND NOT _IBPF_ENABLE_COMPATIBILITY_VARS) 375 | message(DEPRECATION "Compatibility variables are no longer generated. Use ENABLE_COMPATIBILITY_VARS to re-enable them (deprecated) or define them using either INCLUDE_FILE or INCLUDE_CONTENT (recommended).") 376 | endif() 377 | 378 | if(NOT DEFINED _IBPF_EXPORT_DESTINATION) 379 | set(_IBPF_EXPORT_DESTINATION "${CMAKE_BINARY_DIR}") 380 | elseif(NOT IS_ABSOLUTE "${_IBPF_EXPORT_DESTINATION}") 381 | set(_IBPF_EXPORT_DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/${_IBPF_EXPORT_DESTINATION}") 382 | endif() 383 | 384 | if(NOT DEFINED _IBPF_NAMESPACE) 385 | set(_IBPF_NAMESPACE "${_Name}::") 386 | endif() 387 | 388 | if(NOT DEFINED _IBPF_COMPONENT) 389 | set(_IBPF_COMPONENT "${_Name}") 390 | endif() 391 | 392 | if(_IBPF_NO_SET_AND_CHECK_MACRO) 393 | list(APPEND configure_package_config_file_extra_args NO_SET_AND_CHECK_MACRO) 394 | endif() 395 | 396 | if(_IBPF_NO_CHECK_REQUIRED_COMPONENTS_MACRO) 397 | list(APPEND configure_package_config_file_extra_args NO_CHECK_REQUIRED_COMPONENTS_MACRO) 398 | endif() 399 | 400 | 401 | 402 | # Set input file for config, and ensure that _IBPF_UPPERCASE_FILENAMES 403 | # and _IBPF_LOWERCASE_FILENAMES are set correctly 404 | unset(_config_cmake_in) 405 | set(_generate_file 0) 406 | if(DEFINED _IBPF_CONFIG_TEMPLATE) 407 | if(NOT EXISTS "${_IBPF_CONFIG_TEMPLATE}") 408 | message(FATAL_ERROR "Config template file \"${_IBPF_CONFIG_TEMPLATE}\" not found") 409 | endif() 410 | set(_config_cmake_in "${_IBPF_CONFIG_TEMPLATE}") 411 | if(NOT _IBPF_UPPERCASE_FILENAMES AND NOT _IBPF_LOWERCASE_FILENAMES) 412 | if("${_IBPF_CONFIG_TEMPLATE}" MATCHES "${_Name}Config.cmake.in") 413 | set(_IBPF_UPPERCASE_FILENAMES 1) 414 | elseif("${_IBPF_CONFIG_TEMPLATE}" MATCHES "${_name}-config.cmake.in") 415 | set(_IBPF_LOWERCASE_FILENAMES 1) 416 | else() 417 | set(_IBPF_UPPERCASE_FILENAMES 1) 418 | endif() 419 | endif() 420 | else() 421 | string(TOLOWER "${_Name}" _name) 422 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_Name}Config.cmake.in") 423 | set(_config_cmake_in "${CMAKE_CURRENT_SOURCE_DIR}/${_Name}Config.cmake.in") 424 | if(NOT _IBPF_UPPERCASE_FILENAMES AND NOT _IBPF_LOWERCASE_FILENAMES) 425 | set(_IBPF_UPPERCASE_FILENAMES 1) 426 | endif() 427 | elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_name}-config.cmake.in") 428 | set(_config_cmake_in "${CMAKE_CURRENT_SOURCE_DIR}/${_name}-config.cmake.in") 429 | if(NOT _IBPF_UPPERCASE_FILENAMES AND NOT _IBPF_LOWERCASE_FILENAMES) 430 | set(_IBPF_LOWERCASE_FILENAMES 1) 431 | endif() 432 | else() 433 | set(_generate_file 1) 434 | if(_IBPF_LOWERCASE_FILENAMES) 435 | set(_config_cmake_in "${CMAKE_CURRENT_BINARY_DIR}/${_name}-config.cmake.in") 436 | else() 437 | set(_config_cmake_in "${CMAKE_CURRENT_BINARY_DIR}/${_Name}Config.cmake.in") 438 | set(_IBPF_UPPERCASE_FILENAMES 1) 439 | endif() 440 | endif() 441 | endif() 442 | 443 | # Set input file containing user variables 444 | if(DEFINED _IBPF_INCLUDE_FILE) 445 | if(NOT IS_ABSOLUTE "${_IBPF_INCLUDE_FILE}") 446 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_IBPF_INCLUDE_FILE}") 447 | set(_IBPF_INCLUDE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${_IBPF_INCLUDE_FILE}") 448 | endif() 449 | endif() 450 | if(NOT EXISTS "${_IBPF_INCLUDE_FILE}") 451 | message(FATAL_ERROR "File \"${_IBPF_INCLUDE_FILE}\" not found") 452 | endif() 453 | file(READ ${_IBPF_INCLUDE_FILE} _IBPF_INCLUDE_CONTENT) 454 | endif() 455 | 456 | if(DEFINED _IBPF_INCLUDE_CONTENT) 457 | string(CONFIGURE ${_IBPF_INCLUDE_CONTENT} 458 | _IBPF_INCLUDE_CONTENT 459 | @ONLY) 460 | set(INCLUDED_CONTENT 461 | "#### Expanded from INCLUDE_FILE/INCLUDE_CONTENT by install_basic_package_files() #### 462 | 463 | ${_IBPF_INCLUDE_CONTENT} 464 | 465 | ##################################################################################### 466 | ") 467 | endif() 468 | 469 | # Backwards compatibility 470 | if(NOT _generate_file AND DEFINED _IBPF_INCLUDE_FILE) 471 | file(READ ${_config_cmake_in} _config_cmake_in_content) 472 | if("${_config_cmake_in_content}" MATCHES "@INCLUDED_FILE_CONTENT@") 473 | message(DEPRECATION "The @INCLUDED_FILE_CONTENT@ variable is deprecated in favour of @INCLUDED_CONTENT@") 474 | set(INCLUDED_FILE_CONTENT "${INCLUDED_CONTENT}") 475 | endif() 476 | endif() 477 | 478 | # Select output file names 479 | if(_IBPF_UPPERCASE_FILENAMES) 480 | set(_config_filename ${_Name}Config.cmake) 481 | set(_version_filename ${_Name}ConfigVersion.cmake) 482 | set(_targets_filename ${_Name}Targets.cmake) 483 | elseif(_IBPF_LOWERCASE_FILENAMES) 484 | set(_config_filename ${_name}-config.cmake) 485 | set(_version_filename ${_name}-config-version.cmake) 486 | set(_targets_filename ${_name}-targets.cmake) 487 | endif() 488 | 489 | 490 | # If the template config file does not exist, write a basic one 491 | if(_generate_file) 492 | # Generate the compatibility code 493 | unset(_compatibility_vars) 494 | if(_IBPF_ENABLE_COMPATIBILITY_VARS) 495 | unset(_get_include_dir_code) 496 | unset(_set_include_dir_code) 497 | unset(_target_list) 498 | foreach(_target ${_targets}) 499 | list(APPEND _target_list ${_IBPF_NAMESPACE}${_target}) 500 | endforeach() 501 | if(DEFINED ${_IBPF_VARS_PREFIX}_BUILD_INCLUDEDIR OR 502 | DEFINED BUILD_${_IBPF_VARS_PREFIX}_INCLUDEDIR OR 503 | DEFINED ${_IBPF_VARS_PREFIX}_INSTALL_INCLUDEDIR OR 504 | DEFINED INSTALL_${_IBPF_VARS_PREFIX}_INCLUDEDIR) 505 | list(APPEND _include_dir_list "\"\@PACKAGE_${_IBPF_VARS_PREFIX}_INCLUDEDIR\@\"") 506 | elseif(DEFINED ${_IBPF_VARS_PREFIX}_BUILD_INCLUDE_DIR OR 507 | DEFINED BUILD_${_IBPF_VARS_PREFIX}_INCLUDE_DIR OR 508 | DEFINED ${_IBPF_VARS_PREFIX}_INSTALL_INCLUDE_DIR OR 509 | DEFINED INSTALL_${_IBPF_VARS_PREFIX}_INCLUDE_DIR) 510 | list(APPEND _include_dir_list "\"\@PACKAGE_${_IBPF_VARS_PREFIX}_INCLUDE_DIR\@\"") 511 | else() 512 | unset(_include_dir_list) 513 | foreach(_target ${_targets}) 514 | list(APPEND _include_dir_list "\$") 515 | endforeach() 516 | string(REPLACE ";" " " _include_dir_list "${_include_dir_list}") 517 | string(REPLACE ";" " " _target_list "${_target_list}") 518 | set(_set_include_dir "") 519 | endif() 520 | set(_compatibility_vars 521 | "# Compatibility\nset(${_Name}_LIBRARIES ${_target_list}) 522 | set(${_Name}_INCLUDE_DIRS ${_include_dir_list}) 523 | if(NOT \"\${${_Name}_INCLUDE_DIRS}\" STREQUAL \"\") 524 | list(REMOVE_DUPLICATES ${_Name}_INCLUDE_DIRS) 525 | endif() 526 | ") 527 | endif() 528 | 529 | if(_IBPF_NO_EXPORT) 530 | set(_include_targets_cmd "") 531 | else() 532 | set(_include_targets_cmd "include(\"\${CMAKE_CURRENT_LIST_DIR}/${_targets_filename}\")") 533 | endif() 534 | 535 | # Write the file 536 | file(WRITE "${_config_cmake_in}" 537 | "set(${_IBPF_VARS_PREFIX}_VERSION \@PACKAGE_VERSION\@) 538 | 539 | \@PACKAGE_INIT\@ 540 | 541 | \@PACKAGE_DEPENDENCIES\@ 542 | 543 | ${_include_targets_cmd} 544 | 545 | ${_compatibility_vars} 546 | 547 | \@INCLUDED_CONTENT\@ 548 | ") 549 | endif() 550 | 551 | # Make relative paths absolute (needed later on) and append the 552 | # defined variables to _(build|install)_path_vars_suffix 553 | foreach(p BINDIR BIN_DIR 554 | SBINDIR SBIN_DIR 555 | LIBEXECDIR LIBEXEC_DIR 556 | SYSCONFDIR SYSCONF_DIR 557 | SHAREDSTATEDIR SHAREDSTATE_DIR 558 | LOCALSTATEDIR LOCALSTATE_DIR 559 | LIBDIR LIB_DIR 560 | INCLUDEDIR INCLUDE_DIR 561 | OLDINCLUDEDIR OLDINCLUDE_DIR 562 | DATAROOTDIR DATAROOT_DIR 563 | DATADIR DATA_DIR 564 | INFODIR INFO_DIR 565 | LOCALEDIR LOCALE_DIR 566 | MANDIR MAN_DIR 567 | DOCDIR DOC_DIR 568 | ${_IBPF_EXTRA_PATH_VARS_SUFFIX}) 569 | if(DEFINED ${_IBPF_VARS_PREFIX}_BUILD_${p}) 570 | list(APPEND _build_path_vars_suffix ${p}) 571 | list(APPEND _build_path_vars "${_IBPF_VARS_PREFIX}_${p}") 572 | endif() 573 | if(DEFINED BUILD_${_IBPF_VARS_PREFIX}_${p}) 574 | list(APPEND _build_path_vars_suffix ${p}) 575 | list(APPEND _build_path_vars "${_IBPF_VARS_PREFIX}_${p}") 576 | endif() 577 | if(DEFINED ${_IBPF_VARS_PREFIX}_INSTALL_${p}) 578 | list(APPEND _install_path_vars_suffix ${p}) 579 | list(APPEND _install_path_vars "${_IBPF_VARS_PREFIX}_${p}") 580 | endif() 581 | if(DEFINED INSTALL_${_IBPF_VARS_PREFIX}_${p}) 582 | list(APPEND _install_path_vars_suffix ${p}) 583 | list(APPEND _install_path_vars "${_IBPF_VARS_PREFIX}_${p}") 584 | endif() 585 | endforeach() 586 | 587 | 588 | # ConfigVersion.cmake file (same for build tree and intall) 589 | write_basic_package_version_file("${_IBPF_EXPORT_DESTINATION}/${_version_filename}" 590 | VERSION ${_IBPF_VERSION} 591 | COMPATIBILITY ${_IBPF_COMPATIBILITY} 592 | ${_arch_independent}) 593 | install(FILES "${_IBPF_EXPORT_DESTINATION}/${_version_filename}" 594 | DESTINATION ${_IBPF_INSTALL_DESTINATION} 595 | COMPONENT ${_IBPF_COMPONENT}) 596 | 597 | 598 | # Prepare PACKAGE_DEPENDENCIES variable 599 | set(_need_private_deps 0) 600 | if(NOT BUILD_SHARED_LIBS) 601 | set(_need_private_deps 1) 602 | endif() 603 | 604 | unset(PACKAGE_DEPENDENCIES) 605 | if(DEFINED _IBPF_DEPENDENCIES OR (DEFINED _IBPF_PRIVATE_DEPENDENCIES AND _need_private_deps)) 606 | set(PACKAGE_DEPENDENCIES "#### Expanded from @PACKAGE_DEPENDENCIES@ by install_basic_package_files() ####\n\ninclude(CMakeFindDependencyMacro)\n") 607 | 608 | foreach(_dep ${_IBPF_DEPENDENCIES}) 609 | string(APPEND PACKAGE_DEPENDENCIES "find_dependency(${_dep})\n") 610 | endforeach() 611 | 612 | if(_need_private_deps) 613 | foreach(_dep ${_IBPF_PRIVATE_DEPENDENCIES}) 614 | string(APPEND PACKAGE_DEPENDENCIES "find_dependency(${_dep})\n") 615 | endforeach() 616 | endif() 617 | 618 | string(APPEND PACKAGE_DEPENDENCIES "\n###############################################################################\n") 619 | endif() 620 | 621 | # Prepare PACKAGE_VERSION variable 622 | set(PACKAGE_VERSION ${_IBPF_VERSION}) 623 | 624 | # Config.cmake (build tree) 625 | foreach(p ${_build_path_vars_suffix}) 626 | if(DEFINED ${_IBPF_VARS_PREFIX}_BUILD_${p}) 627 | set(${_IBPF_VARS_PREFIX}_${p} "${${_IBPF_VARS_PREFIX}_BUILD_${p}}") 628 | elseif(DEFINED BUILD_${_IBPF_VARS_PREFIX}_${p}) 629 | set(${_IBPF_VARS_PREFIX}_${p} "${BUILD_${_IBPF_VARS_PREFIX}_${p}}") 630 | endif() 631 | endforeach() 632 | configure_package_config_file("${_config_cmake_in}" 633 | "${_IBPF_EXPORT_DESTINATION}/${_config_filename}" 634 | INSTALL_DESTINATION ${_IBPF_EXPORT_DESTINATION} 635 | PATH_VARS ${_build_path_vars} 636 | ${configure_package_config_file_extra_args} 637 | INSTALL_PREFIX ${CMAKE_BINARY_DIR}) 638 | 639 | # Config.cmake (installed) 640 | foreach(p ${_install_path_vars_suffix}) 641 | if(DEFINED ${_IBPF_VARS_PREFIX}_INSTALL_${p}) 642 | set(${_IBPF_VARS_PREFIX}_${p} "${${_IBPF_VARS_PREFIX}_INSTALL_${p}}") 643 | elseif(DEFINED INSTALL_${_IBPF_VARS_PREFIX}_${p}) 644 | set(${_IBPF_VARS_PREFIX}_${p} "${INSTALL_${_IBPF_VARS_PREFIX}_${p}}") 645 | endif() 646 | endforeach() 647 | configure_package_config_file("${_config_cmake_in}" 648 | "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${_config_filename}.install" 649 | INSTALL_DESTINATION ${_IBPF_INSTALL_DESTINATION} 650 | PATH_VARS ${_install_path_vars} 651 | ${configure_package_config_file_extra_args}) 652 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${_config_filename}.install" 653 | DESTINATION ${_IBPF_INSTALL_DESTINATION} 654 | RENAME ${_config_filename} 655 | COMPONENT ${_IBPF_COMPONENT}) 656 | 657 | 658 | # Targets.cmake (build tree) 659 | if(NOT _IBPF_NO_EXPORT) 660 | export(${_export_cmd} 661 | NAMESPACE ${_IBPF_NAMESPACE} 662 | FILE "${_IBPF_EXPORT_DESTINATION}/${_targets_filename}") 663 | endif() 664 | 665 | # Export build directory if CMAKE_EXPORT_PACKAGE_REGISTRY is set. 666 | # CMake >= 3.15 already checks for CMAKE_EXPORT_PACKAGE_REGISTRY in `export(PACKAGE)` (cf. 667 | # cf. https://cmake.org/cmake/help/latest/policy/CMP0090.html), and we effectively back-port 668 | # this behavior to earlier versions. 669 | # Note that even never CMake versions may apply old policy behaviors if the consuming project 670 | # requires a lower version of CMake (e.g. `cmake_minimum_required(VERSION 3.14)`), so the 671 | # check for `CMAKE_EXPORT_PACKAGE_REGISTRY` is necessary for CMake >= 3.15 as well. 672 | if(CMAKE_EXPORT_PACKAGE_REGISTRY) 673 | export(PACKAGE ${_Name}) 674 | endif() 675 | 676 | # Targets.cmake (installed) 677 | if(NOT _IBPF_NO_EXPORT) 678 | install(${_install_cmd} 679 | NAMESPACE ${_IBPF_NAMESPACE} 680 | DESTINATION ${_IBPF_INSTALL_DESTINATION} 681 | FILE "${_targets_filename}" 682 | COMPONENT ${_IBPF_COMPONENT}) 683 | endif() 684 | endfunction() 685 | -------------------------------------------------------------------------------- /cmake/MeshcatCppDeps.cmake: -------------------------------------------------------------------------------- 1 | # Some dependencies are available in conda-forge but not in Debian/Ubuntu 2 | # If we are in a conda environment, let's assume that the dependency are available 3 | # and use them by default. Advanced user can always cahnge the MESHCAT_CPP_USE_SYSTEM_* 4 | # variables to match the desired behaviour 5 | if(DEFINED ENV{CONDA_PREFIX}) 6 | set(MESHCAT_CPP_USE_SYSTEM_UWEBSOCKETS_DEFAULT ON) 7 | set(MESHCAT_CPP_USE_SYSTEM_MSGPACKCXX_DEFAULT ON) 8 | else() 9 | set(MESHCAT_CPP_USE_SYSTEM_UWEBSOCKETS_DEFAULT OFF) 10 | set(MESHCAT_CPP_USE_SYSTEM_MSGPACKCXX_DEFAULT OFF) 11 | endif() 12 | 13 | option(MESHCAT_CPP_USE_SYSTEM_STDUUID "Use system stduuid" OFF) 14 | mark_as_advanced(MESHCAT_CPP_USE_SYSTEM_STDUUID) 15 | if(MESHCAT_CPP_USE_SYSTEM_STDUUID) 16 | find_package(stduuid REQUIRED) 17 | else() 18 | include(FetchContent) 19 | FetchContent_Declare( 20 | stduuid 21 | GIT_REPOSITORY https://github.com/mariusbancila/stduuid.git 22 | GIT_TAG v1.2.3 23 | ) 24 | FetchContent_GetProperties(stduuid) 25 | if(NOT stduuid_POPULATED) 26 | FetchContent_Populate(stduuid) 27 | #set(UUID_USING_CXX20_SPAN ON CACHE BOOL "" FORCE) 28 | add_subdirectory(${stduuid_SOURCE_DIR} ${stduuid_BINARY_DIR} EXCLUDE_FROM_ALL) 29 | endif() 30 | endif() 31 | 32 | 33 | option(MESHCAT_CPP_USE_SYSTEM_MSGPACKCXX "Use system msgpack-cxx" ${MESHCAT_CPP_USE_SYSTEM_MSGPACKCXX_DEFAULT}) 34 | mark_as_advanced(MESHCAT_CPP_USE_SYSTEM_MSGPACKCXX) 35 | if(MESHCAT_CPP_USE_SYSTEM_MSGPACKCXX) 36 | find_package(msgpack-cxx REQUIRED) 37 | else() 38 | include(FetchContent) 39 | FetchContent_Declare( 40 | msgpack-cxx 41 | URL https://github.com/msgpack/msgpack-c/archive/refs/tags/cpp-6.0.0.zip 42 | ) 43 | FetchContent_GetProperties(msgpack-cxx) 44 | if(NOT msgpack-cxx_POPULATED) 45 | FetchContent_Populate(msgpack-cxx) 46 | add_subdirectory(${msgpack-cxx_SOURCE_DIR} ${msgpack-cxx_BINARY_DIR} EXCLUDE_FROM_ALL) 47 | endif() 48 | endif() 49 | 50 | option(MESHCAT_CPP_USE_SYSTEM_UWEBSOCKETS "Use system uwebsockets" ${MESHCAT_CPP_USE_SYSTEM_UWEBSOCKETS_DEFAULT}) 51 | if(NOT MESHCAT_CPP_USE_SYSTEM_UWEBSOCKETS AND NOT BUILD_SHARED_LIBS) 52 | message(FATAL_ERROR "MESHCAT_CPP_USE_SYSTEM_UWEBSOCKETS and BUILD_SHARED_LIBS can't be OFF at the same time") 53 | endif() 54 | mark_as_advanced(MESHCAT_CPP_USE_SYSTEM_UWEBSOCKETS) 55 | if(MESHCAT_CPP_USE_SYSTEM_UWEBSOCKETS) 56 | find_package(uWebSockets REQUIRED) 57 | else() 58 | include(FetchuWebSockets) 59 | endif() 60 | 61 | # Handle relocatability via cmrc 62 | option(MESHCAT_CPP_USE_SYSTEM_CMRC "Use system cmrc" OFF) 63 | mark_as_advanced(MESHCAT_CPP_USE_SYSTEM_CMRC) 64 | if(MESHCAT_CPP_USE_SYSTEM_CMRC) 65 | find_package(CMakeRC REQUIRED) 66 | else() 67 | include(FetchContent) 68 | FetchContent_Declare( 69 | cmrc 70 | GIT_REPOSITORY https://github.com/vector-of-bool/cmrc.git 71 | GIT_TAG 2.0.1 72 | ) 73 | FetchContent_GetProperties(cmrc) 74 | if(NOT cmrc_POPULATED) 75 | FetchContent_Populate(cmrc) 76 | add_subdirectory(${cmrc_SOURCE_DIR} ${cmrc_BINARY_DIR} EXCLUDE_FROM_ALL) 77 | endif() 78 | endif() 79 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(example meshcat_example.cpp) 2 | target_link_libraries(example ${PROJECT_NAME}::${PROJECT_NAME}) 3 | -------------------------------------------------------------------------------- /examples/meshcat_example.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file meshcat_example.cpp 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | MeshcatCpp::MatrixView array_to_matrix_view(std::array& array) 15 | { 16 | constexpr MeshcatCpp::MatrixView::index_type rows = 4; 17 | constexpr MeshcatCpp::MatrixView::index_type cols = 4; 18 | constexpr auto order = MeshcatCpp::MatrixStorageOrdering::ColumnMajor; 19 | return MeshcatCpp::make_matrix_view(array.data(), rows, cols, order); 20 | } 21 | 22 | int main() 23 | { 24 | MeshcatCpp::Meshcat meshcat; 25 | MeshcatCpp::Material m = MeshcatCpp::Material::get_default_material(); 26 | 27 | std::array transform = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; 28 | auto matrix_view = array_to_matrix_view(transform); 29 | 30 | m.set_color(66, 133, 244, 0.5); 31 | meshcat.set_object("box", MeshcatCpp::Box(0.5, 0.5, 0.5), m); 32 | matrix_view(1, 3) = 1.75; 33 | 34 | meshcat.set_transform("box", matrix_view); 35 | 36 | m.set_color(234, 67, 53); 37 | meshcat.set_object("sphere", MeshcatCpp::Sphere(0.5), m); 38 | matrix_view(1, 3) = 0.75; 39 | meshcat.set_transform("sphere", matrix_view); 40 | 41 | m.set_color(251, 188, 5, 0.5); 42 | meshcat.set_object("ellipsoid", MeshcatCpp::Ellipsoid(0.5, 0.25, 0.75), m); 43 | matrix_view(1, 3) = -0.75; 44 | meshcat.set_transform("ellipsoid", matrix_view); 45 | 46 | m.set_color(52, 168, 83); 47 | meshcat.set_object("cylinder", MeshcatCpp::Cylinder(0.25, 0.5), m); 48 | matrix_view(1, 3) = -1.75; 49 | meshcat.set_transform("cylinder", matrix_view); 50 | 51 | const auto stl_path = std::filesystem::path(__FILE__).parent_path() / "misc" / "Dragonite.stl"; 52 | auto transparent_default = MeshcatCpp::Material::get_default_material(); 53 | transparent_default.transparent = true; 54 | transparent_default.opacity = 0.5; 55 | meshcat.set_object("obj", MeshcatCpp::Mesh(stl_path.string(), 0.01), transparent_default); 56 | matrix_view(0, 3) = -1; 57 | matrix_view(1, 3) = 0; 58 | matrix_view(0, 0) = 0; 59 | matrix_view(1, 1) = 0; 60 | matrix_view(0, 1) = -1; 61 | matrix_view(1, 0) = 1; 62 | meshcat.set_transform("obj", matrix_view); 63 | 64 | meshcat.join(); 65 | 66 | return EXIT_SUCCESS; 67 | } 68 | -------------------------------------------------------------------------------- /examples/misc/Dragonite.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ami-iit/meshcat-cpp/a84be7add7f344d61e615bee7f26e6a7d5444f2a/examples/misc/Dragonite.stl -------------------------------------------------------------------------------- /examples/misc/LICENSE_DRAGONITE_STL: -------------------------------------------------------------------------------- 1 | Dragonite(Pokemon) by Patrickartis licensed under the [Creative Commons - Attribution - Non-Commercial - Share Alike](https://creativecommons.org/licenses/by-nc-sa/4.0/) license. -------------------------------------------------------------------------------- /include/MeshcatCpp/Material.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Material.h 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #ifndef MESHCAT_CPP_MATERIAL_H 9 | #define MESHCAT_CPP_MATERIAL_H 10 | 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace MeshcatCpp 19 | { 20 | 21 | struct Material 22 | { 23 | public: 24 | enum class Type 25 | { 26 | MeshBasicMaterial, 27 | MeshPhongMaterial, 28 | MeshLambertMaterial, 29 | MeshToonMaterial, 30 | LineBasicMaterial 31 | }; 32 | 33 | int color{(229 << 16) + (229 << 8) + 229}; 34 | std::optional reflectivity; 35 | std::optional side; 36 | std::optional transparent; 37 | std::optional opacity; 38 | std::optional linewidth; 39 | std::optional wireframe; 40 | std::optional wireframeLineWidth; 41 | bool vertexColors{false}; 42 | Type type{Type::MeshPhongMaterial}; 43 | 44 | const std::unordered_map type_map{{Type::MeshBasicMaterial, "MeshBasicMaterial"}, 45 | {Type::MeshPhongMaterial, "MeshPhongMaterial"}, 46 | {Type::MeshLambertMaterial, "MeshLambertMaterial"}, 47 | {Type::MeshToonMaterial, "MeshToonMaterial"}, 48 | {Type::LineBasicMaterial, "LineBasicMaterial"}}; 49 | 50 | 51 | void set_color(uint8_t r, uint8_t g, uint8_t b, double a = 1.0); 52 | 53 | static Material get_default_material(); 54 | }; 55 | 56 | } // namespace MeshcatCpp 57 | 58 | #endif // MESHCAT_CPP_MATERIAL_H 59 | -------------------------------------------------------------------------------- /include/MeshcatCpp/MatrixView.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file MatrixView.h 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #ifndef MESHCAT_CPP_MATRIX_VIEW_H 9 | #define MESHCAT_CPP_MATRIX_VIEW_H 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | namespace MeshcatCpp 17 | { 18 | 19 | namespace MatrixViewInternal 20 | { 21 | /** 22 | * has_IsRowMajor is used to build a type-dependent expression that check if an 23 | * element has IsRowMajor argument. This specific implementation is used when 24 | * the the object has not IsRowMajor. 25 | */ 26 | template struct has_IsRowMajor : std::false_type 27 | { 28 | }; 29 | 30 | /** 31 | * has_IsRowMajor is used to build a type-dependent expression that check if an 32 | * element has IsRowMajor argument. This specific implementation is used when 33 | * the the object has not IsRowMajor, indeed void_t<\endcode> is used to 34 | * detect ill-formed types in SFINAE context. 35 | */ 36 | template 37 | struct has_IsRowMajor> : std::true_type 38 | { 39 | }; 40 | } // namespace MatrixViewInternal 41 | 42 | namespace details 43 | { 44 | template 45 | struct is_allowed_element_type_conversion 46 | : public std::integral_constant::value> 47 | { 48 | }; 49 | 50 | } // namespace details 51 | 52 | /** 53 | * Enum describing the possible matrix storage ordering 54 | */ 55 | enum class MatrixStorageOrdering 56 | { 57 | RowMajor, /*!< Row Major ordering, i.e. matrix is serialized row by row */ 58 | ColumnMajor /*!< Column Major ordering, i.e. matrix is serialized row by column */ 59 | }; 60 | 61 | /** 62 | * MatrixView implements a view interface of Matrices. Both RowMajor and ColumnMajor matrices are 63 | * supported. 64 | * @note The user should define the storage ordering when the MatrixView is created (the default 65 | order is RowMajor). However if the MatrixView is generated: 66 | * - from an object having a public member called IsRowMajor, the correct storage 67 | order is chosen. 68 | * - from another MatrixView, the correct storage order is chosen. 69 | * @note the original implementation can be found in 70 | https://github.com/robotology/idyntree/blob/a7fdef001d38e47aabcdc541607a255101212dd9/src/core/include/iDynTree/Core/MatrixView.h 71 | */ 72 | template class MatrixView 73 | { 74 | public: 75 | using element_type = ElementType; 76 | using value_type = std::remove_cv_t; 77 | using index_type = std::ptrdiff_t; 78 | using pointer = element_type*; 79 | using reference = element_type&; 80 | 81 | private: 82 | pointer m_storage; 83 | index_type m_rows; 84 | index_type m_cols; 85 | 86 | MatrixStorageOrdering m_storageOrder; 87 | 88 | index_type m_innerStride; 89 | index_type m_outerStride; 90 | 91 | index_type rawIndex(index_type row, index_type col) const 92 | { 93 | if (m_storageOrder == MatrixStorageOrdering::RowMajor) 94 | { 95 | return (col + this->m_outerStride * row); 96 | } else 97 | { 98 | return (this->m_outerStride * col + row); 99 | } 100 | } 101 | 102 | public: 103 | MatrixView() 104 | : MatrixView(nullptr, 0, 0, MatrixStorageOrdering::RowMajor) 105 | { 106 | } 107 | MatrixView(const MatrixView& other) 108 | : MatrixView(other.m_storage, other.m_rows, other.m_cols, other.m_storageOrder) 109 | { 110 | } 111 | 112 | template ::value_type, 115 | value_type>::value>> 116 | constexpr MatrixView(const MatrixView& other) 117 | : MatrixView(other.data(), other.rows(), other.cols(), other.storageOrder()) 118 | { 119 | } 120 | 121 | template ::value 123 | && std::is_convertible().data()), 124 | pointer>::value 125 | && MatrixViewInternal::has_IsRowMajor::value 126 | && !std::is_same::value, 127 | int> = 0> 128 | MatrixView(const Container& matrix) 129 | : MatrixView(matrix.data(), 130 | matrix.rows(), 131 | matrix.cols(), 132 | Container::IsRowMajor ? MatrixStorageOrdering::RowMajor 133 | : MatrixStorageOrdering::ColumnMajor) 134 | { 135 | } 136 | 137 | template ::value 139 | && std::is_convertible().data()), 140 | pointer>::value 141 | && !MatrixViewInternal::has_IsRowMajor::value 142 | && !std::is_same::value, 143 | int> = 0> 144 | MatrixView(const Container& matrix, 145 | const MatrixStorageOrdering& order = MatrixStorageOrdering::RowMajor) 146 | : MatrixView(matrix.data(), matrix.rows(), matrix.cols(), order) 147 | { 148 | } 149 | 150 | template ().data()), pointer>::value 153 | && MatrixViewInternal::has_IsRowMajor::value 154 | && !std::is_same::value, 155 | int> = 0> 156 | MatrixView(Container& matrix) 157 | : MatrixView(matrix.data(), 158 | matrix.rows(), 159 | matrix.cols(), 160 | Container::IsRowMajor ? MatrixStorageOrdering::RowMajor 161 | : MatrixStorageOrdering::ColumnMajor) 162 | { 163 | } 164 | 165 | template ().data()), pointer>::value 168 | && !MatrixViewInternal::has_IsRowMajor::value 169 | && !std::is_same::value 170 | && !std::is_same().data())>>>::value, 173 | int> = 0> 174 | MatrixView(Container& matrix, 175 | const MatrixStorageOrdering& order = MatrixStorageOrdering::RowMajor) 176 | : MatrixView(matrix.data(), matrix.rows(), matrix.cols(), order) 177 | { 178 | } 179 | 180 | MatrixView(pointer in_data, 181 | index_type in_rows, 182 | index_type in_cols, 183 | const MatrixStorageOrdering& order = MatrixStorageOrdering::RowMajor) 184 | : m_storage(in_data) 185 | , m_rows(in_rows) 186 | , m_cols(in_cols) 187 | , m_storageOrder(order) 188 | , m_innerStride(1) 189 | , m_outerStride(order == MatrixStorageOrdering::RowMajor ? in_cols : in_rows) 190 | { 191 | } 192 | 193 | const MatrixStorageOrdering& storageOrder() const noexcept 194 | { 195 | return m_storageOrder; 196 | } 197 | 198 | pointer data() const noexcept 199 | { 200 | return m_storage; 201 | } 202 | 203 | template ::value_type, value_type>>> 206 | MatrixView& operator=(MatrixView other) 207 | { 208 | assert(other.rows() == this->rows()); 209 | assert(other.cols() == this->cols()); 210 | 211 | if (other.storageOrder() == this->storageOrder()) 212 | { 213 | std::memcpy(this->m_storage, 214 | other.data(), 215 | this->rows() * this->cols() * sizeof(element_type)); 216 | 217 | return *this; 218 | } 219 | 220 | for (index_type i = 0; i < this->rows(); i++) 221 | { 222 | for (index_type j = 0; j < this->cols(); j++) 223 | { 224 | this->operator()(i, j) = other(i, j); 225 | } 226 | } 227 | 228 | return *this; 229 | } 230 | 231 | /** 232 | * @name Matrix interface methods. 233 | * Methods exposing a matrix-like interface to MatrixView. 234 | * 235 | */ 236 | ///@{ 237 | reference operator()(index_type row, const index_type col) const 238 | { 239 | assert(row < this->rows()); 240 | assert(col < this->cols()); 241 | return this->m_storage[rawIndex(row, col)]; 242 | } 243 | 244 | index_type rows() const noexcept 245 | { 246 | return this->m_rows; 247 | } 248 | 249 | index_type cols() const noexcept 250 | { 251 | return this->m_cols; 252 | } 253 | ///@} 254 | 255 | MatrixView 256 | block(index_type startingRow, index_type startingColumn, index_type rows, index_type cols) const 257 | { 258 | assert(rows <= this->rows()); 259 | assert(cols <= this->cols()); 260 | 261 | const index_type offset = rawIndex(startingRow, startingColumn); 262 | assert(offset < this->rows() * this->cols()); 263 | 264 | MatrixView block; 265 | block = *this; 266 | block.m_rows = rows; 267 | block.m_cols = cols; 268 | block.m_storage = this->m_storage + offset; 269 | 270 | return block; 271 | } 272 | }; 273 | 274 | template 275 | constexpr MatrixView 276 | make_matrix_view(ElementType* ptr, 277 | typename MatrixView::index_type rows, 278 | typename MatrixView::index_type cols, 279 | MatrixStorageOrdering order = MatrixStorageOrdering::RowMajor) 280 | { 281 | return MatrixView(ptr, rows, cols, order); 282 | } 283 | 284 | template ::value 287 | || std::is_same, Container>::value, 288 | int> = 0> 289 | constexpr MatrixView make_matrix_view(Container& cont) 290 | { 291 | return MatrixView(cont); 292 | } 293 | 294 | template ::value 296 | || std::is_same, 297 | Container>::value, 298 | int> = 0> 299 | constexpr MatrixView make_matrix_view(const Container& cont) 300 | { 301 | return MatrixView(cont); 302 | } 303 | 304 | template ::value 307 | && !std::is_same, Container>::value, 308 | int> = 0> 309 | constexpr MatrixView 310 | make_matrix_view(Container& cont, 311 | typename MatrixView::MatrixStorageOrdering order 312 | = MatrixView::MatrixStorageOrdering::RowMajor) 313 | { 314 | return MatrixView(cont, order); 315 | } 316 | 317 | template ::value 320 | && !std::is_same, Container>::value, 321 | int> = 0> 322 | constexpr MatrixView make_matrix_view( 323 | const Container& cont, 324 | typename MatrixView::MatrixStorageOrdering order 325 | = MatrixView::MatrixStorageOrdering::RowMajor) 326 | { 327 | return MatrixView(cont, order); 328 | } 329 | 330 | } // namespace MeshcatCpp 331 | 332 | #endif /* IDYNTREE_MATRIX_MATRIX_VIEW_H */ 333 | -------------------------------------------------------------------------------- /include/MeshcatCpp/Meshcat.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Meshcat.h 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | // Part of the content of this file has been taken from drake. Drake lincense follows 9 | // 10 | // All components of Drake are licensed under the BSD 3-Clause License 11 | // shown below. Where noted in the source code, some portions may 12 | // be subject to other permissive, non-viral licenses. 13 | // 14 | // Copyright 2012-2022 Robot Locomotion Group @ CSAIL 15 | // All rights reserved. 16 | // 17 | // Redistribution and use in source and binary forms, with or without 18 | // modification, are permitted provided that the following conditions are 19 | // met: 20 | // 21 | // Redistributions of source code must retain the above copyright notice, 22 | // this list of conditions and the following disclaimer. Redistributions 23 | // in binary form must reproduce the above copyright notice, this list of 24 | // conditions and the following disclaimer in the documentation and/or 25 | // other materials provided with the distribution. Neither the name of 26 | // the Massachusetts Institute of Technology nor the names of its 27 | // contributors may be used to endorse or promote products derived from 28 | // this software without specific prior written permission. 29 | // 30 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 31 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 32 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 33 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 34 | // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 35 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 36 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 37 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 38 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 39 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 40 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41 | 42 | #ifndef MESHCAT_CPP_MESHCAT_H 43 | #define MESHCAT_CPP_MESHCAT_H 44 | 45 | #include 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | namespace MeshcatCpp 52 | { 53 | /** 54 | * The Meshcat class provides an interface to [meshcat](https://github.com/rdeits/meshcat). 55 | * This class's instances start a thread that runs a http/websocket server. Users may view the 56 | * Meshcat scene by navigating their browser to the hosted URL. 57 | * Users can exploit this class to load primary shapes (e.g. spheres, cylinders ellipsoids and 58 | * boxes). For example, 59 | * @verbatim 60 | * Meshcat viz; 61 | * viz.set_object("box", MeshcatCpp::Box(0.1, 0.1, 0.1)); 62 | * @endverbatim 63 | * will start an interface between meshcat and load a box. 64 | * @note the Design of this class took inspiration form drake Meshcat C++ implementation. 65 | * Please refer to https://github.com/RobotLocomotion/drake/issues/13038 if you are interested in 66 | * the original project. 67 | */ 68 | class Meshcat 69 | { 70 | public: 71 | Meshcat& operator=(const Meshcat&) = delete; 72 | Meshcat(const Meshcat&) = delete; 73 | 74 | /** 75 | * Constructs the Meshcat instance. It will listen on the first available port starting at 7001 76 | * (up to 7099). 77 | */ 78 | Meshcat(); 79 | 80 | ~Meshcat(); 81 | 82 | /** 83 | * Utility function to make the meshcat interface run forever (until the user stop the 84 | * application) 85 | */ 86 | void join(); 87 | 88 | void set_property(std::string_view path, const std::string& property, bool value); 89 | 90 | void set_object(std::string_view path, 91 | const Sphere& sphere, 92 | const Material& material = Material::get_default_material()); 93 | 94 | void set_object(std::string_view path, 95 | const Cylinder& cylinder, 96 | const Material& material = Material::get_default_material()); 97 | 98 | void set_object(std::string_view path, 99 | const Box& box, 100 | const Material& material = Material::get_default_material()); 101 | 102 | void set_object(std::string_view path, 103 | const Ellipsoid& ellipsoid, 104 | const Material& material = Material::get_default_material()); 105 | 106 | void set_object(std::string_view path, 107 | const Mesh& mesh, 108 | const Material& material = Material::get_default_material()); 109 | 110 | void set_transform(std::string_view path, const MatrixView& matrix); 111 | 112 | private: 113 | class Impl; 114 | std::unique_ptr pimpl_; 115 | }; 116 | 117 | } // namespace MeshcatCpp 118 | 119 | #endif // MESHCAT_CPP_MESHCAT_H 120 | -------------------------------------------------------------------------------- /include/MeshcatCpp/Property.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Property.h 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #ifndef MESHCAT_CPP_PROPERTY_H 9 | #define MESHCAT_CPP_PROPERTY_H 10 | 11 | #include 12 | 13 | namespace MeshcatCpp 14 | { 15 | 16 | template struct Property 17 | { 18 | std::string path; 19 | std::string property; 20 | T value; 21 | }; 22 | 23 | } // namespace MeshcatCpp 24 | 25 | #endif // MESHCAT_CPP_MATERIAL_H 26 | -------------------------------------------------------------------------------- /include/MeshcatCpp/Shape.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Shape.h 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #ifndef MESHCAT_CPP_SHAPE_H 9 | #define MESHCAT_CPP_SHAPE_H 10 | 11 | #include 12 | 13 | #define MESHCAT_CPP_ADD_SHAPE_ATTRIBUTE(type, attribute) \ 14 | private: \ 15 | type attribute##_; \ 16 | \ 17 | public: \ 18 | const type& attribute() const \ 19 | { \ 20 | return this->attribute##_; \ 21 | } 22 | 23 | namespace MeshcatCpp 24 | { 25 | 26 | struct Shape 27 | { 28 | const std::string type; 29 | }; 30 | 31 | class Sphere : public Shape 32 | { 33 | public: 34 | Sphere(double radius); 35 | MESHCAT_CPP_ADD_SHAPE_ATTRIBUTE(double, radius); 36 | }; 37 | 38 | class Ellipsoid : public Shape 39 | { 40 | public: 41 | Ellipsoid(double a, double b, double c); 42 | 43 | MESHCAT_CPP_ADD_SHAPE_ATTRIBUTE(double, a); 44 | MESHCAT_CPP_ADD_SHAPE_ATTRIBUTE(double, b); 45 | MESHCAT_CPP_ADD_SHAPE_ATTRIBUTE(double, c); 46 | }; 47 | 48 | class Cylinder : public Shape 49 | { 50 | public: 51 | Cylinder(double radius, double height); 52 | 53 | MESHCAT_CPP_ADD_SHAPE_ATTRIBUTE(double, radius); 54 | MESHCAT_CPP_ADD_SHAPE_ATTRIBUTE(double, height); 55 | }; 56 | 57 | class Box : public Shape 58 | { 59 | public: 60 | Box(double width, double depth, double height); 61 | 62 | MESHCAT_CPP_ADD_SHAPE_ATTRIBUTE(double, width); 63 | MESHCAT_CPP_ADD_SHAPE_ATTRIBUTE(double, depth); 64 | MESHCAT_CPP_ADD_SHAPE_ATTRIBUTE(double, height); 65 | }; 66 | 67 | class Mesh : public Shape 68 | { 69 | public: 70 | Mesh(std::string_view file_path, double scale = 1); 71 | 72 | MESHCAT_CPP_ADD_SHAPE_ATTRIBUTE(std::string, file_path); 73 | MESHCAT_CPP_ADD_SHAPE_ATTRIBUTE(double, scale); 74 | }; 75 | 76 | 77 | } // namespace MeshcatCpp 78 | 79 | #undef MESHCAT_CPP_ADD_SHAPE_ATTRIBUTE 80 | 81 | #endif // MESHCAT_CPP_SHAPE_H 82 | -------------------------------------------------------------------------------- /include/MeshcatCpp/impl/FindResource.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file FindResource.h 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #ifndef MESHCAT_CPP_FIND_ASSET_H 9 | #define MESHCAT_CPP_FIND_ASSET_H 10 | 11 | #include 12 | #include 13 | 14 | namespace MeshcatCpp::details 15 | { 16 | 17 | std::filesystem::path get_resource_path(const std::string& resource); 18 | 19 | } // namespace MeshcatCpp::details 20 | 21 | #endif // MESHCAT_CPP_FIND_ASSET_H 22 | -------------------------------------------------------------------------------- /include/MeshcatCpp/impl/MsgpackTypes.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file MsgpackTypes.h 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #ifndef MESHCAT_CPP_MSGPACK_TYPES_H 9 | #define MESHCAT_CPP_MSGPACK_TYPES_H 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include 18 | 19 | #define PACK_MAP_VAR(packer, var) \ 20 | packer.pack(#var); \ 21 | packer.pack(var); 22 | 23 | #define PACK_MAP_VAR_WITH_NAME(packer, name, var) \ 24 | packer.pack(#name); \ 25 | packer.pack(var); 26 | 27 | #define PACK_MAP_VAR_FROM_INNER_CLASS(packer, inner_class, var) \ 28 | PACK_MAP_VAR_WITH_NAME(packer, var, inner_class.var); 29 | 30 | #define PACK_MAP_OPTIONAL_VAR_WITH_NAME(packer, name, var) \ 31 | if (var) \ 32 | { \ 33 | PACK_MAP_VAR_WITH_NAME(packer, name, var.value()); \ 34 | } 35 | 36 | #define PACK_MAP_OPTIONAL_VAR_FROM_INNER_CLASS(packer, inner_class, var) \ 37 | PACK_MAP_OPTIONAL_VAR_WITH_NAME(packer, var, inner_class.var); 38 | 39 | #define SHAPE_TRAMPOLINE(shape) \ 40 | struct shape##Trampoline; \ 41 | template <> struct traits<::MeshcatCpp::shape> \ 42 | { \ 43 | using trampoline = shape##Trampoline; \ 44 | }; 45 | 46 | namespace MeshcatCpp::details 47 | { 48 | 49 | template struct traits; 50 | 51 | /// @note the following is from the Eigen library 52 | /// here we say once and for all that traits == traits 53 | /// 54 | /// When constness must affect traits, it has to be constness on 55 | /// template parameters on which T itself depends. 56 | /// For example, traits > != traits >, but 57 | /// traits > == traits > 58 | template struct traits : traits 59 | { 60 | }; 61 | 62 | struct MaterialTrampoline 63 | { 64 | const std::string uuid; 65 | const ::MeshcatCpp::Material material; 66 | 67 | MaterialTrampoline(const ::MeshcatCpp::Material& material); 68 | 69 | template void msgpack_pack(Packer& o) const 70 | { 71 | int n = 4; 72 | if (material.linewidth) 73 | ++n; 74 | if (material.opacity) 75 | ++n; 76 | if (material.reflectivity) 77 | ++n; 78 | if (material.side) 79 | ++n; 80 | if (material.transparent) 81 | ++n; 82 | if (material.wireframe) 83 | ++n; 84 | if (material.wireframeLineWidth) 85 | ++n; 86 | 87 | o.pack_map(n); 88 | PACK_MAP_VAR(o, uuid); 89 | 90 | PACK_MAP_VAR_WITH_NAME(o, type, material.type_map.at(material.type)); 91 | PACK_MAP_VAR_WITH_NAME(o, color, material.color); 92 | PACK_MAP_VAR_WITH_NAME(o, vertexColors, material.vertexColors); 93 | 94 | PACK_MAP_OPTIONAL_VAR_FROM_INNER_CLASS(o, material, linewidth); 95 | PACK_MAP_OPTIONAL_VAR_FROM_INNER_CLASS(o, material, opacity); 96 | PACK_MAP_OPTIONAL_VAR_FROM_INNER_CLASS(o, material, reflectivity); 97 | PACK_MAP_OPTIONAL_VAR_FROM_INNER_CLASS(o, material, side); 98 | PACK_MAP_OPTIONAL_VAR_FROM_INNER_CLASS(o, material, transparent); 99 | PACK_MAP_OPTIONAL_VAR_FROM_INNER_CLASS(o, material, wireframe); 100 | PACK_MAP_OPTIONAL_VAR_FROM_INNER_CLASS(o, material, wireframeLineWidth); 101 | } 102 | 103 | // This method must be defined, but the implementation is not needed. 104 | void msgpack_unpack(msgpack::object const&); 105 | }; 106 | 107 | struct ObjectMetaData 108 | { 109 | const std::string type{"Object"}; 110 | const double version{4.5}; 111 | 112 | template void msgpack_pack(Packer& o) const 113 | { 114 | constexpr int n = 2; 115 | o.pack_map(n); 116 | PACK_MAP_VAR(o, type); 117 | PACK_MAP_VAR(o, version); 118 | } 119 | 120 | // This method must be defined, but the implementation is not needed. 121 | void msgpack_unpack(msgpack::object const&) 122 | { 123 | throw std::runtime_error("unpack is not implemented for ObjectMetaData."); 124 | } 125 | }; 126 | 127 | struct GeometryData 128 | { 129 | virtual ~GeometryData() = default; 130 | 131 | GeometryData(); 132 | 133 | const std::string uuid; 134 | 135 | // NOLINTNEXTLINE(runtime/references) cpplint disapproves of msgpack choices. 136 | virtual void msgpack_pack(msgpack::packer& o) const = 0; 137 | 138 | // This method must be defined, but the implementation is not needed in the 139 | // current workflows. 140 | void msgpack_unpack(msgpack::object const&); 141 | }; 142 | 143 | SHAPE_TRAMPOLINE(Sphere); 144 | struct SphereTrampoline : public GeometryData 145 | { 146 | const ::MeshcatCpp::Sphere sphere; 147 | double widthSegments{20}; 148 | double heightSegments{20}; 149 | 150 | SphereTrampoline(const ::MeshcatCpp::Sphere& sphere); 151 | 152 | void msgpack_pack(msgpack::packer& o) const override; 153 | }; 154 | 155 | SHAPE_TRAMPOLINE(Ellipsoid); 156 | struct EllipsoidTrampoline : public GeometryData 157 | { 158 | const ::MeshcatCpp::Ellipsoid ellipsoid; 159 | double widthSegments{20}; 160 | double heightSegments{20}; 161 | 162 | EllipsoidTrampoline(const ::MeshcatCpp::Ellipsoid& ellipsoid); 163 | 164 | void msgpack_pack(msgpack::packer& o) const override; 165 | }; 166 | 167 | SHAPE_TRAMPOLINE(Box); 168 | struct BoxTrampoline : public GeometryData 169 | { 170 | const ::MeshcatCpp::Box box; 171 | double widthSegments{20}; 172 | double heightSegments{20}; 173 | 174 | BoxTrampoline(const ::MeshcatCpp::Box& sphere); 175 | 176 | void msgpack_pack(msgpack::packer& o) const override; 177 | }; 178 | 179 | SHAPE_TRAMPOLINE(Cylinder); 180 | struct CylinderTrampoline : public GeometryData 181 | { 182 | const ::MeshcatCpp::Cylinder cylinder; 183 | double radialSegments{50}; 184 | double heightSegments{20}; 185 | 186 | CylinderTrampoline(const ::MeshcatCpp::Cylinder& cylinder); 187 | 188 | void msgpack_pack(msgpack::packer& o) const override; 189 | }; 190 | 191 | SHAPE_TRAMPOLINE(Mesh); 192 | struct MeshTrampoline : public GeometryData 193 | { 194 | const ::MeshcatCpp::Mesh mesh; 195 | 196 | std::string format; 197 | std::vector data; 198 | 199 | MeshTrampoline(const ::MeshcatCpp::Mesh& mesh); 200 | 201 | void msgpack_pack(msgpack::packer& o) const override; 202 | }; 203 | 204 | struct MeshData 205 | { 206 | std::string uuid; 207 | std::string type{"Mesh"}; 208 | std::string geometry; 209 | std::string material; 210 | std::array matrix_vec = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; 211 | 212 | // TODO it cannot be copiable 213 | MeshData(); 214 | 215 | MeshcatCpp::MatrixView matrix(); 216 | 217 | template void update_matrix_from_shape(const T& shape) 218 | { 219 | if constexpr (std::is_same_v) 220 | { 221 | auto view = this->matrix(); 222 | view(0, 0) = shape.a(); 223 | view(1, 1) = shape.b(); 224 | view(2, 2) = shape.c(); 225 | } 226 | 227 | if constexpr (std::is_same_v) 228 | { 229 | auto view = this->matrix(); 230 | view(0, 0) = view(1, 1) = view(2, 2) = shape.scale(); 231 | } 232 | } 233 | 234 | MSGPACK_DEFINE_MAP(uuid, type, geometry, material, MSGPACK_NVP("matrix", matrix_vec)); 235 | }; 236 | 237 | struct LumpedObjectData 238 | { 239 | ObjectMetaData metadata; 240 | std::unique_ptr geometry; 241 | std::unique_ptr material; 242 | MeshData object; 243 | 244 | template void msgpack_pack(Packer& o) const 245 | { 246 | int size = 2; 247 | if (geometry) 248 | { 249 | size++; 250 | } 251 | if (material) 252 | { 253 | size++; 254 | } 255 | 256 | o.pack_map(size); 257 | PACK_MAP_VAR(o, metadata); 258 | 259 | if (geometry) 260 | { 261 | o.pack("geometries"); 262 | o.pack_array(1); 263 | o.pack(*geometry); 264 | } 265 | if (material) 266 | { 267 | o.pack("materials"); 268 | o.pack_array(1); 269 | o.pack(*material); 270 | } 271 | 272 | o.pack("object"); 273 | o.pack(object); 274 | } 275 | 276 | // This method must be defined, but the implementation is not needed 277 | void msgpack_unpack(msgpack::object const&) 278 | { 279 | throw std::runtime_error("unpack is not implemented for LumpedObjectData."); 280 | } 281 | }; 282 | 283 | struct SetObjectData 284 | { 285 | std::string type{"set_object"}; 286 | std::string path; 287 | LumpedObjectData object; 288 | MSGPACK_DEFINE_MAP(type, path, object); 289 | }; 290 | 291 | } // namespace MeshcatCpp::details 292 | 293 | #endif // MESHCAT_CPP_MSGPACK_TYPES_H 294 | -------------------------------------------------------------------------------- /include/MeshcatCpp/impl/TreeNode.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file TreeNode.h 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #ifndef MESHCAT_CPP_TREE_NODE_H 9 | #define MESHCAT_CPP_TREE_NODE_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace MeshcatCpp::details 19 | { 20 | 21 | /** 22 | * TreeNode represents the Node in Tree struct. 23 | */ 24 | template class TreeNode : public std::enable_shared_from_this> 25 | { 26 | public: 27 | /** 28 | * Check if a child with a given name exist 29 | * @param[in] name The name of the child 30 | * @return True if the child exist false otherwise. 31 | */ 32 | [[nodiscard]] bool child_exists(const std::string& name) const 33 | { 34 | return children_.find(name) != children_.end(); 35 | } 36 | 37 | /** 38 | * Returns a reference to the value that is mapped to a key equivalent to key, performing an 39 | * insertion if such key does not already exist. 40 | * @param[in] path the relative path of the node. 41 | * @return Ponter to the mapped value of the new element if no child with key key existed. 42 | * Otherwise a pointer to the mapped value of the existing element whose path is equivalent to 43 | * path. 44 | * @note operator[] is non-const because it inserts a new node in the tree if it doesn't exist. 45 | * If this behavior is undesirable or if the container is const, at() may be used. 46 | */ 47 | std::shared_ptr> operator[](std::string_view path) 48 | { 49 | const auto separator_size = TreeNode::string_separator.size(); 50 | while (!path.empty() && path.find_last_of(TreeNode::string_separator) == path.size() - 1) 51 | { 52 | path.remove_prefix(separator_size); 53 | } 54 | if (path.empty()) 55 | { 56 | return this->shared_from_this(); 57 | } 58 | 59 | const auto loc = path.find_first_of(TreeNode::string_separator); 60 | 61 | const std::string name(path.substr(0, loc)); 62 | 63 | auto child = children_.find(name); 64 | 65 | // Create the child if it doesn't exist. 66 | if (child == children_.end()) 67 | { 68 | child = children_.emplace(name, std::make_shared>()).first; 69 | } 70 | if (loc == std::string_view::npos) 71 | { 72 | return child->second; 73 | } 74 | 75 | return child->second->operator[](path.substr(loc + separator_size)); 76 | } 77 | 78 | /** 79 | * Returns a reference to the mapped value of the element with key equivalent to key. If no such 80 | * element exists, an invalid weak_ptr is provided. 81 | * @param[in] path the relative path of the node. 82 | * @return Pointer to the mapped value of the requested element. 83 | */ 84 | std::weak_ptr> at(std::string_view path) const 85 | { 86 | const auto separator_size = TreeNode::string_separator.size(); 87 | while (!path.empty() && path.find_last_of(TreeNode::string_separator) == path.size() - 1) 88 | { 89 | path.remove_prefix(separator_size); 90 | } 91 | if (path.empty()) 92 | { 93 | return this->weak_from_this(); 94 | } 95 | 96 | const auto loc = path.find_first_of(TreeNode::string_separator); 97 | 98 | const std::string name(path.substr(0, loc)); 99 | 100 | auto child = children_.find(name); 101 | 102 | // Create the child if it doesn't exist. 103 | if (child == children_.end()) 104 | { 105 | return nullptr; 106 | } 107 | if (loc == std::string_view::npos) 108 | { 109 | return child->second; 110 | } else 111 | { 112 | return child->second->at(path.substr(loc + separator_size)); 113 | } 114 | } 115 | 116 | const std::unordered_map>& children() const 117 | { 118 | return this->children_; 119 | } 120 | 121 | /** 122 | * Get the value stored in the node. 123 | * @return The value stored in the node. 124 | */ 125 | const T& value() const 126 | { 127 | return node_; 128 | } 129 | 130 | T& value() 131 | { 132 | return node_; 133 | } 134 | 135 | /** 136 | * Return a standard text representation of the content of the node. 137 | * @return a string containing the standard text representation of the content of the object. 138 | */ 139 | [[nodiscard]] std::string 140 | to_string(const std::string& name = ".", const unsigned int depth = 0) const 141 | { 142 | std::ostringstream oss; 143 | for (unsigned int i = 0; i < depth; i++) 144 | { 145 | if (i != depth - 1) 146 | { 147 | oss << " "; 148 | } else 149 | { 150 | oss << "|-- "; 151 | } 152 | } 153 | oss << name << std::endl; 154 | for (const auto& [key, child] : children_) 155 | { 156 | oss << child->to_string(key, depth + 1); 157 | } 158 | 159 | return oss.str(); 160 | } 161 | 162 | static std::string string_separator; /**< The string separator the default value is / */ 163 | 164 | private: 165 | T node_; 166 | std::unordered_map> children_; 167 | }; 168 | 169 | template std::string TreeNode::string_separator = "/"; 170 | 171 | } // namespace MeshcatCpp::details 172 | 173 | #endif // MESHCAT_CPP_TREE_NODE_H 174 | -------------------------------------------------------------------------------- /include/MeshcatCpp/impl/UUIDGenerator.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file UUIDGenerator.h 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #ifndef MESHCAT_CPP_UUID_GENERATOR_H 9 | #define MESHCAT_CPP_UUID_GENERATOR_H 10 | 11 | #include 12 | #include 13 | 14 | namespace MeshcatCpp::details 15 | { 16 | 17 | class UUIDGenerator 18 | { 19 | public: 20 | std::string operator()(); 21 | static UUIDGenerator& generator(); 22 | 23 | private: 24 | UUIDGenerator() = default; 25 | ~UUIDGenerator() = default; 26 | UUIDGenerator(const UUIDGenerator&) = delete; 27 | UUIDGenerator& operator=(const UUIDGenerator&) = delete; 28 | 29 | 30 | inline static std::mt19937 generator_{}; 31 | }; 32 | 33 | } // namespace MeshcatCpp 34 | 35 | #endif // MESHCAT_CPP_UUID_GENERATOR_H 36 | -------------------------------------------------------------------------------- /misc/LICENSE_MESHCAT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Robin Deits 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 | -------------------------------------------------------------------------------- /misc/favicon.ico: -------------------------------------------------------------------------------- 1 | h(    2 |         3 |       4 |        5 |       -------------------------------------------------------------------------------- /misc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 20 | 21 | 22 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /misc/main.min.js.THIRD_PARTY_LICENSES.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "ccapture.js", 4 | "version": "1.0.8", 5 | "author": "Jaume Sanchez (https://www.clicktorelease.com)", 6 | "repository": "https://github.com/spite/ccapture.js", 7 | "source": "https://registry.npmjs.org/ccapture.js/-/ccapture.js-1.0.8.tgz", 8 | "license": "MIT", 9 | "licenseText": "The MIT License\n\nCopyright (c) 2012 Jaume Sanchez Elias\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE." 10 | }, 11 | { 12 | "name": "dat.gui", 13 | "version": "0.7.7", 14 | "author": "Data Arts Team, Google", 15 | "repository": "https://github.com/dataarts/dat.gui", 16 | "source": "https://registry.npmjs.org/dat.gui/-/dat.gui-0.7.7.tgz", 17 | "license": "Apache-2.0", 18 | "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2014, Google Inc.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" 19 | }, 20 | { 21 | "name": "event-lite", 22 | "version": "0.1.2", 23 | "author": "@kawanet", 24 | "repository": "https://github.com/kawanet/event-lite", 25 | "source": "https://registry.npmjs.org/event-lite/-/event-lite-0.1.2.tgz", 26 | "license": "MIT", 27 | "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Yusuke Kawasaki\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" 28 | }, 29 | { 30 | "name": "ieee754", 31 | "version": "1.2.1", 32 | "author": "Feross Aboukhadijeh (https://feross.org)", 33 | "repository": "https://github.com/feross/ieee754", 34 | "source": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 35 | "license": "BSD-3-Clause", 36 | "licenseText": "Copyright 2008 Fair Oaks Labs, Inc.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" 37 | }, 38 | { 39 | "name": "int64-buffer", 40 | "version": "0.1.10", 41 | "author": "@kawanet", 42 | "repository": "https://github.com/kawanet/int64-buffer", 43 | "source": "https://registry.npmjs.org/int64-buffer/-/int64-buffer-0.1.10.tgz", 44 | "license": "MIT", 45 | "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015-2016 Yusuke Kawasaki\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" 46 | }, 47 | { 48 | "name": "isarray", 49 | "version": "1.0.0", 50 | "author": "Julian Gruber (http://juliangruber.com)", 51 | "repository": "https://github.com/juliangruber/isarray", 52 | "source": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 53 | "license": "MIT", 54 | "licenseText": null 55 | }, 56 | { 57 | "name": "msgpack-lite", 58 | "version": "0.1.26", 59 | "author": "@kawanet", 60 | "repository": "https://github.com/kawanet/msgpack-lite", 61 | "source": "https://registry.npmjs.org/msgpack-lite/-/msgpack-lite-0.1.26.tgz", 62 | "license": "MIT", 63 | "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Yusuke Kawasaki\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" 64 | }, 65 | { 66 | "name": "three", 67 | "version": "0.132.2", 68 | "author": "mrdoob", 69 | "repository": "https://github.com/mrdoob/three.js", 70 | "source": "https://registry.npmjs.org/three/-/three-0.132.2.tgz", 71 | "license": "MIT", 72 | "licenseText": "The MIT License\n\nCopyright © 2010-2021 three.js authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" 73 | }, 74 | { 75 | "name": "wwobjloader2", 76 | "version": "4.0.0", 77 | "author": "kaisalmen", 78 | "repository": "https://github.com/kaisalmen/WWOBJLoader", 79 | "source": "https://registry.npmjs.org/wwobjloader2/-/wwobjloader2-4.0.0.tgz", 80 | "license": "MIT", 81 | "licenseText": "MIT License\n\nCopyright (c) 2016-2021 Kai Salmen\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." 82 | } 83 | ] -------------------------------------------------------------------------------- /src/Material.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Material.cpp 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #include 9 | 10 | using namespace MeshcatCpp; 11 | 12 | void Material::set_color(uint8_t r, uint8_t g, uint8_t b, double a) 13 | { 14 | this->color = static_cast(r << 16) + static_cast(g << 8) + static_cast(b); 15 | this->opacity = a; 16 | if (this->opacity < 1.0) 17 | { 18 | this->transparent = true; 19 | } 20 | } 21 | 22 | Material Material::get_default_material() 23 | { 24 | Material tmp{.reflectivity = 0.5, 25 | .side = 2, 26 | .transparent = false, 27 | .opacity = 1.0, 28 | .linewidth = 1.0, 29 | .wireframe = false, 30 | .wireframeLineWidth = 1.0, 31 | .vertexColors = false, 32 | .type = Material::Type::MeshPhongMaterial}; 33 | 34 | return tmp; 35 | } 36 | -------------------------------------------------------------------------------- /src/Meshcat.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Meshcat.cpp 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | // uWebSockets 34 | #include 35 | 36 | // msgpack 37 | #include 38 | #include 39 | 40 | // cmrc 41 | #include 42 | CMRC_DECLARE(MeshcatCpp); 43 | 44 | namespace MeshcatCpp::details 45 | { 46 | 47 | struct TransformData 48 | { 49 | std::string type{"set_transform"}; 50 | std::string path; 51 | std::array matrix; 52 | MSGPACK_DEFINE_MAP(type, path, matrix); 53 | 54 | MeshcatCpp::MatrixView transform(); 55 | MeshcatCpp::MatrixView transform() const; 56 | }; 57 | 58 | MeshcatCpp::MatrixView TransformData::transform() 59 | { 60 | constexpr MatrixView::index_type rows = 4; 61 | constexpr MatrixView::index_type cols = 4; 62 | constexpr auto order = MeshcatCpp::MatrixStorageOrdering::ColumnMajor; 63 | return make_matrix_view(this->matrix.data(), rows, cols, order); 64 | } 65 | 66 | MeshcatCpp::MatrixView TransformData::transform() const 67 | { 68 | constexpr MatrixView::index_type rows = 4; 69 | constexpr MatrixView::index_type cols = 4; 70 | constexpr auto order = MeshcatCpp::MatrixStorageOrdering::ColumnMajor; 71 | return make_matrix_view(this->matrix.data(), rows, cols, order); 72 | } 73 | 74 | template struct PropertyTrampoline : public ::MeshcatCpp::Property 75 | { 76 | // TOFO make it const 77 | std::string type{"set_property"}; 78 | 79 | MSGPACK_DEFINE_MAP(type, 80 | MSGPACK_NVP("path", ::MeshcatCpp::Property::path), 81 | MSGPACK_NVP("property", ::MeshcatCpp::Property::property), 82 | MSGPACK_NVP("value", ::MeshcatCpp::Property::value)); 83 | }; 84 | 85 | } // namespace MeshcatCpp::details 86 | 87 | namespace MeshcatCpp 88 | { 89 | 90 | constexpr static bool use_ssl = false; 91 | constexpr static bool is_server = true; 92 | 93 | using PerSocketData = std::monostate; 94 | 95 | using WebSocket = uWS::WebSocket; 96 | 97 | struct Node 98 | { 99 | // The msgpack'd set_object command. 100 | std::optional object; 101 | // The msgpack'd set_transform command. 102 | std::optional transform; 103 | // The msgpack'd set_property command(s). 104 | std::map properties; 105 | 106 | void send(WebSocket* ws) const 107 | { 108 | if (this->object) 109 | { 110 | ws->send(this->object.value()); 111 | } 112 | if (this->transform) 113 | { 114 | ws->send(this->transform.value()); 115 | } 116 | for (const auto& [property, msg] : this->properties) 117 | { 118 | ws->send(msg); 119 | } 120 | } 121 | }; 122 | 123 | class Meshcat::Impl 124 | { 125 | public: 126 | Impl& operator=(const Impl&) = delete; 127 | Impl(const Impl&) = delete; 128 | 129 | Impl() 130 | { 131 | if (!this->load_file("misc/index.html", this->index_html_)) 132 | { 133 | throw std::runtime_error("Unable to load index.html"); 134 | } 135 | 136 | if (!this->load_file("misc/favicon.ico", this->favicon_)) 137 | { 138 | throw std::runtime_error("Unable to load index.html"); 139 | } 140 | 141 | if (!this->load_file("misc/main.min.js", this->main_min_js_)) 142 | { 143 | throw std::runtime_error("Unable to load main.min.js"); 144 | } 145 | 146 | this->root_ = std::make_shared>(); 147 | this->app_future_ = app_promise_.get_future(); 148 | } 149 | 150 | ~Impl() 151 | { 152 | loop_->defer([this]() { us_listen_socket_close(0, this->listen_socket_); }); 153 | this->websocket_thread_.join(); 154 | } 155 | 156 | void websocket_main() 157 | { 158 | int port = 7001; 159 | const int kMaxPort = 7099; 160 | 161 | uWS::App::WebSocketBehavior behavior; 162 | 163 | // Set maxBackpressure = 0 so that uWS does *not* drop any messages due to 164 | // back pressure. 165 | behavior.maxBackpressure = 0; 166 | behavior.open = [this](WebSocket* ws) { 167 | ws->subscribe("all"); 168 | // Update this new connection with previously published data. 169 | this->send_tree(ws, this->root_); 170 | }; 171 | 172 | uWS::App app = uWS::App() 173 | .get("/*", 174 | [this](uWS::HttpResponse* res, uWS::HttpRequest* req) { 175 | if (req->getUrl() == "/main.min.js") 176 | { 177 | res->end(this->main_min_js_); 178 | } else if (req->getUrl() == "/favicon.ico") 179 | { 180 | res->end(this->favicon_); 181 | } else 182 | { 183 | res->end(this->index_html_); 184 | } 185 | }) 186 | .ws("/*", std::move(behavior)); 187 | 188 | us_listen_socket_t* listen_socket = nullptr; 189 | do 190 | { 191 | app.listen(port, 192 | LIBUS_LISTEN_EXCLUSIVE_PORT, 193 | [port, &listen_socket](us_listen_socket_t* socket) { 194 | if (socket) 195 | { 196 | std::cout << "Meshcat listening for connections at http://127.0.0.1:" 197 | << port << std::endl; 198 | listen_socket = socket; 199 | } 200 | }); 201 | } while (listen_socket == nullptr && port++ <= kMaxPort); 202 | 203 | this->set_app_promise(&app, uWS::Loop::get(), port, listen_socket); 204 | 205 | app.run(); 206 | 207 | // run() should not terminate. 208 | throw std::runtime_error("Meshcat websocket thread failed"); 209 | } 210 | 211 | void get_app_future() 212 | { 213 | std::tie(this->app_, this->loop_, this->port_, this->listen_socket_) 214 | = this->app_future_.get(); 215 | } 216 | 217 | template 218 | void set_property(const Property& property) 219 | { 220 | details::PropertyTrampoline data{property}; 221 | this->loop_->defer([this, data = std::move(data)]() { 222 | std::stringstream message_stream; 223 | msgpack::pack(message_stream, data); 224 | const std::string msg = message_stream.str(); 225 | this->app_->publish("all", msg, uWS::OpCode::BINARY, false); 226 | (*this->root_)[data.path]->value().properties[data.property] = std::move(msg); 227 | }); 228 | } 229 | 230 | template 231 | void set_property(std::string_view path, const std::string& property, const T& value) 232 | { 233 | details::PropertyTrampoline data{ 234 | {.path = this->absolute_path(path), .property = property, .value = value}}; 235 | 236 | this->loop_->defer([this, data = std::move(data)]() { 237 | std::stringstream message_stream; 238 | msgpack::pack(message_stream, data); 239 | const std::string msg = message_stream.str(); 240 | this->app_->publish("all", msg, uWS::OpCode::BINARY, false); 241 | (*this->root_)[data.path]->value().properties[data.property] = std::move(msg); 242 | }); 243 | } 244 | 245 | 246 | template 247 | void set_object(std::string_view path, const T& shape, const Material& material) 248 | { 249 | static_assert(std::is_base_of_v<::MeshcatCpp::Shape, T>, "Invalid shape type"); 250 | 251 | details::SetObjectData data{.path = this->absolute_path(path)}; 252 | data.object.material = std::make_unique(material); 253 | data.object.geometry = std::make_unique::trampoline>(shape); 254 | data.object.object.type = "Mesh"; 255 | data.object.object.material = data.object.material->uuid; 256 | data.object.object.geometry = data.object.geometry->uuid; 257 | data.object.object.update_matrix_from_shape(shape); 258 | 259 | this->loop_->defer([this, data = std::move(data)]() { 260 | std::stringstream message_stream; 261 | msgpack::pack(message_stream, data); 262 | std::string msg = message_stream.str(); 263 | this->app_->publish("all", msg, uWS::OpCode::BINARY, false); 264 | (*this->root_)[data.path]->value().object = std::move(msg); 265 | }); 266 | } 267 | 268 | void set_transform(std::string_view path, const MatrixView& matrix) 269 | { 270 | details::TransformData data{.path = this->absolute_path(path)}; 271 | 272 | auto matrix_view = data.transform(); 273 | matrix_view = matrix; 274 | 275 | this->loop_->defer([this, data = std::move(data)]() { 276 | std::stringstream message_stream; 277 | msgpack::pack(message_stream, data); 278 | std::string msg = message_stream.str(); 279 | this->app_->publish("all", msg, uWS::OpCode::BINARY, false); 280 | (*this->root_)[data.path]->value().transform = std::move(msg); 281 | }); 282 | } 283 | 284 | std::thread websocket_thread_{}; 285 | 286 | private: 287 | static bool load_file(const std::string& filename, std::string& content) 288 | { 289 | auto fs = ::cmrc::MeshcatCpp::get_filesystem(); 290 | if (!fs.exists(filename)) 291 | { 292 | std::cerr << "Unable to find " << filename << " in the embedded filesystem" 293 | << std::endl; 294 | return false; 295 | } 296 | try 297 | { 298 | const auto file = fs.open(filename); 299 | content = std::string(file.begin(), file.end()); 300 | } catch (const std::exception& e) 301 | { 302 | std::cerr << "Unable to open " << filename << " in the embedded filesystem" 303 | << std::endl; 304 | std::cerr << "The following exception has been throw " << e.what() << std::endl; 305 | 306 | return false; 307 | } 308 | 309 | return true; 310 | } 311 | 312 | std::string absolute_path(std::string_view path) const 313 | { 314 | assert(this->root_ != nullptr); 315 | 316 | while (!path.empty() && path.find_last_of(this->root_->string_separator) == path.size() - 1) 317 | { 318 | path.remove_prefix(this->root_->string_separator.size()); 319 | } 320 | 321 | if (path.empty()) 322 | { 323 | return this->prefix_; 324 | } 325 | 326 | if (path.find_first_of(this->root_->string_separator) == 0) 327 | { 328 | return std::string(path); 329 | } 330 | 331 | return this->prefix_ + this->root_->string_separator + std::string(path); 332 | } 333 | 334 | void set_app_promise(uWS::App* app, uWS::Loop* loop, int port, us_listen_socket_t* socket) 335 | { 336 | this->app_promise_.set_value(std::make_tuple(app, loop, port, socket)); 337 | } 338 | 339 | void send_tree(WebSocket* ws, std::shared_ptr> node) 340 | { 341 | if (node == nullptr) 342 | { 343 | return; 344 | } 345 | 346 | node->value().send(ws); 347 | for (const auto& [name, child] : node->children()) 348 | { 349 | assert(child != nullptr); 350 | send_tree(ws, child); 351 | } 352 | } 353 | 354 | std::string index_html_; 355 | std::string main_min_js_; 356 | std::string favicon_; 357 | 358 | std::promise> app_promise_{}; 359 | std::future> app_future_{}; 360 | 361 | // As explained in 362 | // https://github.com/uNetworking/uWebSockets/blob/d94bf2cd43bed5e0de396a8412f156e15c141e98/misc/READMORE.md#threading 363 | // Only loop_->defer() should be called from outside the websocket_thread. 364 | uWS::Loop* loop_{nullptr}; 365 | 366 | // The remaining variables should only be accessed from the websocket_thread. 367 | uWS::App* app_{nullptr}; 368 | us_listen_socket_t* listen_socket_{nullptr}; 369 | int port_{-1}; 370 | 371 | std::shared_ptr> root_; 372 | std::string prefix_{"meshcat"}; 373 | }; 374 | 375 | Meshcat::Meshcat() 376 | { 377 | // A std::promise is made in the WebSocketPublisher. 378 | this->pimpl_ = std::make_unique(); 379 | this->pimpl_->websocket_thread_ 380 | = std::thread(&Meshcat::Impl::websocket_main, this->pimpl_.get()); 381 | 382 | // The std::promise is full-filled in WebsocketMain; we wait here to obtain 383 | // that value. 384 | this->pimpl_->get_app_future(); 385 | } 386 | 387 | Meshcat::~Meshcat() = default; 388 | 389 | void Meshcat::join() 390 | { 391 | this->pimpl_->websocket_thread_.join(); 392 | } 393 | 394 | void Meshcat::set_property(std::string_view path, const std::string& property, bool value) 395 | { 396 | this->pimpl_->set_property(path, property, value); 397 | } 398 | 399 | void Meshcat::set_object(std::string_view path, const Sphere& sphere, const Material& material) 400 | { 401 | this->pimpl_->set_object(path, sphere, material); 402 | } 403 | 404 | void Meshcat::set_transform(std::string_view path, const MatrixView& matrix) 405 | { 406 | this->pimpl_->set_transform(path, matrix); 407 | } 408 | 409 | void Meshcat::set_object(std::string_view path, const Cylinder& cylinder, const Material& material) 410 | { 411 | this->pimpl_->set_object(path, cylinder, material); 412 | } 413 | 414 | void Meshcat::set_object(std::string_view path, 415 | const Ellipsoid& ellipsoid, 416 | const Material& material) 417 | { 418 | this->pimpl_->set_object(path, ellipsoid, material); 419 | } 420 | 421 | void Meshcat::set_object(std::string_view path, const Mesh& mesh, const Material& material) 422 | { 423 | this->pimpl_->set_object(path, mesh, material); 424 | } 425 | 426 | void Meshcat::set_object(std::string_view path, const Box& box, const Material& material) 427 | { 428 | this->pimpl_->set_object(path, box, material); 429 | } 430 | 431 | } // namespace MeshcatCpp 432 | -------------------------------------------------------------------------------- /src/MsgpackTypes.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file MsgpackTypes.cpp 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | using namespace MeshcatCpp::details; 16 | 17 | MaterialTrampoline::MaterialTrampoline(const ::MeshcatCpp::Material& material) 18 | : uuid(MeshcatCpp::details::UUIDGenerator::generator()()) 19 | , material(material) 20 | { 21 | } 22 | 23 | // This method must be defined, but the implementation is not needed. 24 | void MaterialTrampoline::msgpack_unpack(msgpack::object const&) 25 | { 26 | throw std::runtime_error("unpack is not implemented for MaterialTrampoline."); 27 | } 28 | 29 | GeometryData::GeometryData() 30 | : uuid(MeshcatCpp::details::UUIDGenerator::generator()()) 31 | { 32 | } 33 | 34 | void GeometryData::msgpack_unpack(msgpack::object const&) 35 | { 36 | throw std::runtime_error("unpack is not implemented for BufferGeometryData."); 37 | } 38 | 39 | SphereTrampoline::SphereTrampoline(const ::MeshcatCpp::Sphere& sphere) 40 | : GeometryData() 41 | , sphere(sphere) 42 | { 43 | } 44 | 45 | void SphereTrampoline::msgpack_pack(msgpack::packer& o) const 46 | { 47 | o.pack_map(5); 48 | PACK_MAP_VAR_FROM_INNER_CLASS(o, sphere, type); 49 | PACK_MAP_VAR(o, uuid); 50 | PACK_MAP_VAR_WITH_NAME(o, radius, sphere.radius()); 51 | PACK_MAP_VAR(o, widthSegments); 52 | PACK_MAP_VAR(o, heightSegments); 53 | } 54 | 55 | CylinderTrampoline::CylinderTrampoline(const ::MeshcatCpp::Cylinder& cylinder) 56 | : GeometryData() 57 | , cylinder(cylinder) 58 | { 59 | } 60 | 61 | void CylinderTrampoline::msgpack_pack(msgpack::packer& o) const 62 | { 63 | o.pack_map(6); 64 | PACK_MAP_VAR_FROM_INNER_CLASS(o, cylinder, type); 65 | PACK_MAP_VAR(o, uuid); 66 | PACK_MAP_VAR_WITH_NAME(o, radiusTop, cylinder.radius()); 67 | PACK_MAP_VAR_WITH_NAME(o, radiusBottom, cylinder.radius()); 68 | PACK_MAP_VAR_WITH_NAME(o, height, cylinder.height()); 69 | PACK_MAP_VAR(o, radialSegments); 70 | } 71 | 72 | BoxTrampoline::BoxTrampoline(const ::MeshcatCpp::Box& box) 73 | : GeometryData() 74 | , box(box) 75 | { 76 | } 77 | 78 | void BoxTrampoline::msgpack_pack(msgpack::packer& o) const 79 | { 80 | o.pack_map(5); 81 | PACK_MAP_VAR_FROM_INNER_CLASS(o, box, type); 82 | PACK_MAP_VAR(o, uuid); 83 | PACK_MAP_VAR_WITH_NAME(o, width, box.width()); 84 | PACK_MAP_VAR_WITH_NAME(o, height, box.depth()); 85 | PACK_MAP_VAR_WITH_NAME(o, depth, box.height()); 86 | } 87 | 88 | EllipsoidTrampoline::EllipsoidTrampoline(const ::MeshcatCpp::Ellipsoid& ellipsoid) 89 | : GeometryData() 90 | , ellipsoid(ellipsoid) 91 | { 92 | } 93 | 94 | void EllipsoidTrampoline::msgpack_pack(msgpack::packer& o) const 95 | { 96 | constexpr int radius = 1; 97 | o.pack_map(5); 98 | PACK_MAP_VAR_FROM_INNER_CLASS(o, ellipsoid, type); 99 | PACK_MAP_VAR(o, uuid); 100 | PACK_MAP_VAR_WITH_NAME(o, radius, radius); 101 | PACK_MAP_VAR(o, widthSegments); 102 | PACK_MAP_VAR(o, heightSegments); 103 | } 104 | 105 | MeshTrampoline::MeshTrampoline(const ::MeshcatCpp::Mesh& mesh) 106 | : GeometryData() 107 | , mesh(mesh) 108 | { 109 | const auto& path = mesh.file_path(); 110 | size_t pos = path.find_last_of('.'); 111 | if (pos == std::string::npos) 112 | { 113 | return; 114 | } 115 | this->format = path.substr(pos + 1); 116 | 117 | std::ifstream input(path, std::ios::binary | std::ios::ate); 118 | if (!input.is_open()) 119 | { 120 | return; 121 | } 122 | 123 | const int size = input.tellg(); 124 | 125 | input.seekg(0, std::ios::beg); 126 | this->data.resize(size); 127 | input.read(this->data.data(), size); 128 | 129 | input.close(); 130 | } 131 | 132 | void MeshTrampoline::msgpack_pack(msgpack::packer& o) const 133 | { 134 | o.pack_map(4); 135 | PACK_MAP_VAR_FROM_INNER_CLASS(o, mesh, type); 136 | PACK_MAP_VAR(o, uuid); 137 | PACK_MAP_VAR(o, format); 138 | PACK_MAP_VAR(o, data); 139 | } 140 | 141 | MeshData::MeshData() 142 | : uuid(MeshcatCpp::details::UUIDGenerator::generator()()) 143 | { 144 | } 145 | 146 | ::MeshcatCpp::MatrixView MeshData::matrix() 147 | { 148 | constexpr MeshcatCpp::MatrixView::index_type rows = 4; 149 | constexpr MeshcatCpp::MatrixView::index_type cols = 4; 150 | return ::MeshcatCpp::make_matrix_view(this->matrix_vec.data(), 151 | rows, 152 | cols, 153 | ::MeshcatCpp::MatrixStorageOrdering::ColumnMajor); 154 | } 155 | -------------------------------------------------------------------------------- /src/Shape.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Shape.cpp 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #include 9 | 10 | using namespace MeshcatCpp; 11 | 12 | Sphere::Sphere(double radius) 13 | : Shape{.type = "SphereGeometry"} 14 | , radius_(std::move(radius)) 15 | { 16 | } 17 | 18 | Cylinder::Cylinder(double radius, double height) 19 | : Shape{.type = "CylinderGeometry"} 20 | , radius_(std::move(radius)) 21 | , height_(std::move(height)) 22 | { 23 | } 24 | 25 | Box::Box(double width, double depth, double height) 26 | : Shape{.type = "BoxGeometry"} 27 | , width_(std::move(width)) 28 | , depth_(std::move(depth)) 29 | , height_(std::move(height)) 30 | { 31 | } 32 | 33 | Ellipsoid::Ellipsoid(double a, double b, double c) 34 | : Shape{.type = "SphereGeometry"} 35 | , a_(std::move(a)) 36 | , b_(std::move(b)) 37 | , c_(std::move(c)) 38 | { 39 | } 40 | 41 | Mesh::Mesh(std::string_view file_path, double scale) 42 | : Shape{.type = "_meshfile_geometry"} 43 | , file_path_(file_path) 44 | , scale_(std::move(scale)) 45 | { 46 | } 47 | -------------------------------------------------------------------------------- /src/UUIDGenerator.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file UUIDGenerator.cpp 3 | * @authors Giulio Romualdi 4 | * @copyright This software may be modified and distributed under the terms of the BSD-3-Clause 5 | * license. 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | using namespace MeshcatCpp::details; 14 | 15 | UUIDGenerator& UUIDGenerator::generator() 16 | { 17 | static UUIDGenerator instance; // Guaranteed to be destroyed. 18 | // Instantiated on first use. 19 | return instance; 20 | } 21 | 22 | std::string UUIDGenerator::operator()() 23 | { 24 | uuids::uuid_random_generator uuid_generator{this->generator_}; 25 | return uuids::to_string(uuid_generator()); 26 | } 27 | --------------------------------------------------------------------------------