diff --git a/.clang-tidy b/.clang-tidy index a9ae81e6..68304b47 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,27 +1,22 @@ --- -Checks: 'clang-diagnostic-*,clang-analyzer-*' -WarningsAsErrors: '' -HeaderFilterRegex: '/(lib|tests)/' +Checks: > + clang-diagnostic-*, + clang-analyzer-*, + bugprone-*, + cert-*, + cppcoreguidelines-*, + google-*, + llvm-*, + modernize-*, + performance-*, + readability-* +WarningsAsErrors: 'bugprone-*,performance-*' +HeaderFilterRegex: '/(apps|lib|tests)/' AnalyzeTemporaryDtors: false -FormatStyle: none -User: dduberg +FormatStyle: file CheckOptions: - llvm-else-after-return.WarnOnConditionVariables: 'false' - modernize-loop-convert.MinConfidence: reasonable - modernize-replace-auto-ptr.IncludeStyle: llvm - cert-str34-c.DiagnoseSignedUnsignedCharComparisons: 'false' - google-readability-namespace-comments.ShortNamespaceLines: '10' - cert-err33-c.CheckedFunctions: '::aligned_alloc;::asctime_s;::at_quick_exit;::atexit;::bsearch;::bsearch_s;::btowc;::c16rtomb;::c32rtomb;::calloc;::clock;::cnd_broadcast;::cnd_init;::cnd_signal;::cnd_timedwait;::cnd_wait;::ctime_s;::fclose;::fflush;::fgetc;::fgetpos;::fgets;::fgetwc;::fopen;::fopen_s;::fprintf;::fprintf_s;::fputc;::fputs;::fputwc;::fputws;::fread;::freopen;::freopen_s;::fscanf;::fscanf_s;::fseek;::fsetpos;::ftell;::fwprintf;::fwprintf_s;::fwrite;::fwscanf;::fwscanf_s;::getc;::getchar;::getenv;::getenv_s;::gets_s;::getwc;::getwchar;::gmtime;::gmtime_s;::localtime;::localtime_s;::malloc;::mbrtoc16;::mbrtoc32;::mbsrtowcs;::mbsrtowcs_s;::mbstowcs;::mbstowcs_s;::memchr;::mktime;::mtx_init;::mtx_lock;::mtx_timedlock;::mtx_trylock;::mtx_unlock;::printf_s;::putc;::putwc;::raise;::realloc;::remove;::rename;::scanf;::scanf_s;::setlocale;::setvbuf;::signal;::snprintf;::snprintf_s;::sprintf;::sprintf_s;::sscanf;::sscanf_s;::strchr;::strerror_s;::strftime;::strpbrk;::strrchr;::strstr;::strtod;::strtof;::strtoimax;::strtok;::strtok_s;::strtol;::strtold;::strtoll;::strtoul;::strtoull;::strtoumax;::strxfrm;::swprintf;::swprintf_s;::swscanf;::swscanf_s;::thrd_create;::thrd_detach;::thrd_join;::thrd_sleep;::time;::timespec_get;::tmpfile;::tmpfile_s;::tmpnam;::tmpnam_s;::tss_create;::tss_get;::tss_set;::ungetc;::ungetwc;::vfprintf;::vfprintf_s;::vfscanf;::vfscanf_s;::vfwprintf;::vfwprintf_s;::vfwscanf;::vfwscanf_s;::vprintf_s;::vscanf;::vscanf_s;::vsnprintf;::vsnprintf_s;::vsprintf;::vsprintf_s;::vsscanf;::vsscanf_s;::vswprintf;::vswprintf_s;::vswscanf;::vswscanf_s;::vwprintf_s;::vwscanf;::vwscanf_s;::wcrtomb;::wcschr;::wcsftime;::wcspbrk;::wcsrchr;::wcsrtombs;::wcsrtombs_s;::wcsstr;::wcstod;::wcstof;::wcstoimax;::wcstok;::wcstok_s;::wcstol;::wcstold;::wcstoll;::wcstombs;::wcstombs_s;::wcstoul;::wcstoull;::wcstoumax;::wcsxfrm;::wctob;::wctrans;::wctype;::wmemchr;::wprintf_s;::wscanf;::wscanf_s;' - cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField: 'false' - cert-dcl16-c.NewSuffixes: 'L;LL;LU;LLU' - google-readability-braces-around-statements.ShortStatementLines: '1' cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: 'true' + google-readability-braces-around-statements.ShortStatementLines: '1' + google-readability-function-size.StatementThreshold: '100' google-readability-namespace-comments.SpacesBeforeComments: '2' - modernize-loop-convert.MaxCopySize: '16' - modernize-pass-by-value.IncludeStyle: llvm - modernize-use-nullptr.NullMacros: 'NULL' - llvm-qualified-auto.AddConstToQualified: 'false' - modernize-loop-convert.NamingStyle: CamelCase - llvm-else-after-return.WarnOnUnfixable: 'false' - google-readability-function-size.StatementThreshold: '800' ... diff --git a/.clangd b/.clangd index 44c86466..b4db9067 100644 --- a/.clangd +++ b/.clangd @@ -1,3 +1,5 @@ +CompileFlags: + Add: [-std=c++23] InlayHints: Enabled: Yes Designators: No diff --git a/.github/labeler.yml b/.github/labeler.yml index 85a60b78..bd2fb7a9 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -17,6 +17,14 @@ dependency: - changed-files: - any-glob-to-any-file: 'external/**' +application: +- changed-files: + - any-glob-to-any-file: 'apps/**' + +example: +- changed-files: + - any-glob-to-any-file: 'examples/**' + # Add 'lib-cloud' label to any changes within the cloud library lib-cloud: - changed-files: diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 13baf881..659b9f6d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -31,10 +31,30 @@ Please describe the tests that you ran to verify your changes. Provide instructi - [ ] Test B **Test Configuration**: -* Firmware version: -* Hardware: -* Toolchain: -* SDK: + # Checklist @@ -46,3 +66,4 @@ Please describe the tests that you ran to verify your changes. Provide instructi - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules +- [ ] I have added newly created `.cpp` and `.hpp` files to the `target_sources` in the `CMakeLists.txt` files diff --git a/.github/workflows/cmake-install.yml b/.github/workflows/cmake-install.yml index 09e1f837..c05bf410 100644 --- a/.github/workflows/cmake-install.yml +++ b/.github/workflows/cmake-install.yml @@ -11,18 +11,13 @@ on: jobs: test-install: runs-on: ubuntu-latest - steps: - - name: Install TBB - run: | - sudo apt-get update -yqq - sudo apt-get install -yqq libtbb-dev - + steps: - name: Checkout UFO Library - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Build and Install UFO run: | - cmake -B build -DCMAKE_BUILD_TYPE=Release + cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_C_COMPILER=gcc-14 -DUFO_DEV_MODE=OFF cmake --build build cmake --install build --prefix $GITHUB_WORKSPACE/dist @@ -37,7 +32,7 @@ jobs: find_package(UFO REQUIRED) add_executable(test_consumer main.cpp) - target_link_libraries(test_consumer PRIVATE UFO::Map) + target_link_libraries(test_consumer PRIVATE UFO::UFO) set_target_properties(test_consumer PROPERTIES VERSION ${PROJECT_VERSION} @@ -48,18 +43,17 @@ jobs: EOF cat < consumer/main.cpp - #include - #include + // #include + // #include int main() { - ufo::Map3D map(0.5, 3); - ufo::Vec3f coord(0, 0, 0); - map.occupancySet(coord, 0.95f); + // ufo::Map3D map(0.5, 3); + // ufo::Vec3f coord(0, 0, 0); + // map.occupancySet(coord, 0.95f); return 0; } EOF - name: Build and Link Consumer run: | - cmake -S consumer -B consumer/build \ - -DCMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/dist + cmake -S consumer -B consumer/build -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/dist cmake --build consumer/build --config Release \ No newline at end of file diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 5ff7c9ad..a3b1e98d 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -1,5 +1,5 @@ # This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. -# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +# See: https://github.com/actions/starter-gitgithubworkflows/blob/main/ci/cmake-single-platform.yml name: CMake on: @@ -21,11 +21,11 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] build_type: [Debug, Release] - c_compiler: [gcc, clang, cl] + c_compiler: [gcc-14, clang, cl] include: - os: ubuntu-latest - c_compiler: gcc - cpp_compiler: g++ + c_compiler: gcc-14 + cpp_compiler: g++-14 - os: ubuntu-latest c_compiler: clang cpp_compiler: clang++ @@ -39,16 +39,41 @@ jobs: - os: ubuntu-latest c_compiler: cl - os: macos-latest - c_compiler: gcc + c_compiler: gcc-14 - os: macos-latest c_compiler: cl - os: windows-latest - c_compiler: gcc + c_compiler: gcc-14 - os: windows-latest c_compiler: clang steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 + + - name: Install dependencies (Ubuntu) + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y doxygen + + - name: Install LLVM 21 (macOS) + if: matrix.os == 'macos-latest' + run: | + brew install llvm@21 + + # Force the shell to find LLVM 21 binaries first + echo "/opt/homebrew/opt/llvm@21/bin" >> $GITHUB_PATH + + # Point to the macOS SDK + echo "SDKROOT=$(xcrun --show-sdk-path)" >> $GITHUB_ENV + + # Tell the compiler and linker to use the LLVM 21 library/headers + # instead of the outdated Apple Clang system headers + echo "LDFLAGS=-L/opt/homebrew/opt/llvm@21/lib -Wl,-rpath,/opt/homebrew/opt/llvm@21/lib" >> $GITHUB_ENV + echo "CPPFLAGS=-I/opt/homebrew/opt/llvm@21/include" >> $GITHUB_ENV + + # These help CMake find the correct standard library for C++23 Ranges + echo "CXXFLAGS=-stdlib=libc++ -fexperimental-library" >> $GITHUB_ENV - name: Set reusable strings # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. @@ -60,18 +85,25 @@ jobs: - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + # On macOS, we explicitly pass the Homebrew paths to CMake run: > cmake -B ${{ steps.strings.outputs.build-output-dir }} - -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} - -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_CXX_COMPILER=${{ matrix.os == 'macos-latest' && '/opt/homebrew/opt/llvm@21/bin/clang++' || matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.os == 'macos-latest' && '/opt/homebrew/opt/llvm@21/bin/clang' || matrix.c_compiler }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -S ${{ github.workspace }} -DUFO_BUILD_TESTS=ON + -DUFO_DEV_MODE=${{ matrix.build_type == 'Release' && 'OFF' || 'ON' }} + -DUFO_BUILD_DOCS=${{ matrix.os == 'ubuntu-latest' && matrix.cpp_compiler == 'g++-14' && 'ON' || 'OFF' }} - name: Build # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} + - name: Build Documentation + if: matrix.os == 'ubuntu-latest' && matrix.cpp_compiler == 'g++-14' + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --target Docs + - name: Test working-directory: ${{ steps.strings.outputs.build-output-dir }} # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). diff --git a/.github/workflows/cpp-linter.yml b/.github/workflows/cpp-linter.yml index dfec4e8a..768952ed 100644 --- a/.github/workflows/cpp-linter.yml +++ b/.github/workflows/cpp-linter.yml @@ -16,7 +16,7 @@ jobs: contents: read pull-requests: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set reusable strings # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. @@ -46,11 +46,10 @@ jobs: style: 'file' # Use .clang-format config file tidy-checks: '' # Use .clang-tidy config file ignore: 'external' - # only 'update' a single comment in a pull request thread. lines-changed-only: true - tidy-review: true - format-review: true - thread-comments: ${{ github.event_name == 'pull_request' && 'update' }} + tidy-review: false + format-review: false + thread-comments: ${{ github.event_name == 'pull_request' && 'update' || 'false' }} database: ${{ steps.strings.outputs.build-output-dir }} - name: Fail fast?! if: steps.linter.outputs.checks-failed > 0 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d637cddd..cb3d6e1d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,7 +4,7 @@ on: # Trigger the workflow every time you push to the `main` branch # Using a different branch name? Replace `main` with your branch’s name push: - branches: [ main ] + branches: [ "**" ] # Allows you to run this workflow manually from the Actions tab on GitHub. workflow_dispatch: @@ -25,14 +25,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout your repository using git - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install Doxygen and Graphviz run: sudo apt update && sudo apt install -y doxygen graphviz - name: Generate Doxygen API documentation run: | - cmake -B build -DUFO_BUILD_DOCS=ON + cmake -B build -DUFO_BUILD_DOCS=ON -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_C_COMPILER=gcc-14 -DUFO_DEV_MODE=OFF cmake --build build --target Docs cp -r build/docs/html docs/public/cpp_api diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml index 701e4a93..8c48d612 100644 --- a/.github/workflows/greetings.yml +++ b/.github/workflows/greetings.yml @@ -9,7 +9,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/first-interaction@v1 + - uses: actions/first-interaction@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} issue-message: "Hi and thank you for supporting UFO! :)" diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 89845250..4621e1fa 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -21,9 +21,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v6 with: python-version: '3.x' - name: Install dependencies diff --git a/CMakeLists.txt b/CMakeLists.txt index 7aea23c1..d025049a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,7 @@ -cmake_minimum_required(VERSION 3.16...3.24) +cmake_minimum_required(VERSION 3.23...3.31) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) project(UFO VERSION 1.0.0 @@ -6,30 +9,39 @@ project(UFO LANGUAGES CXX C ) -option(UFO_BUILD_DOCS "Generate documentation" OFF) -option(UFO_BUILD_TESTS "Build all unit tests" OFF) -option(UFO_BUILD_EXAMPLES "Build example applications" OFF) -option(UFO_COVERAGE "Test Coverage" OFF) -option(UFO_DEV_MODE "Set up development helper settings" ON) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_SCAN_FOR_MODULES OFF) + -option(USE_SYSTEM_CATCH2 "Use system-installed Catch2 instead of fetching/building it" OFF) -option(USE_SYSTEM_CLI11 "Use system-installed CLI11 instead of fetching/building it" OFF) -option(USE_SYSTEM_IMGUI "Use system-installed ImGUI instead of fetching/building it" OFF) -option(USE_SYSTEM_JPEG "Use system-installed libjpeg-turbo instead of fetching/building it" OFF) -option(USE_SYSTEM_SPNG "Use system-installed libspng instead of fetching/building it" OFF) +option(UFO_BUILD_DOCS "Generate documentation" OFF) +option(UFO_BUILD_TESTS "Build all unit tests" OFF) +option(UFO_BUILD_EXAMPLES "Build example applications" OFF) +option(UFO_COVERAGE "Test Coverage" OFF) +option(UFO_DEV_MODE "Set up development helper settings" ON) +# option(UFO_ENABLE_NATIVE_OPTIMIZATION "Optimize for the host CPU" ON) +option(UFO_ENABLE_LTO "Enable Link Time Optimization" ON) +option(UFO_ENABLE_BMI2 "Enable BMI2 instructions" ON) + +if (UFO_DEV_MODE) + # Speed up development builds + set(UFO_ENABLE_LTO OFF) +endif() + +option(UFO_USE_SYSTEM_CATCH2 "Use system-installed Catch2 instead of fetching/building it" OFF) +option(UFO_USE_SYSTEM_CLI11 "Use system-installed CLI11 instead of fetching/building it" OFF) +option(UFO_USE_SYSTEM_IMGUI "Use system-installed ImGUI instead of fetching/building it" OFF) +option(UFO_USE_SYSTEM_JPEG "Use system-installed libjpeg-turbo instead of fetching/building it" OFF) +option(UFO_USE_SYSTEM_SPNG "Use system-installed libspng instead of fetching/building it" OFF) include(external/find_dependencies.cmake) +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) if(UFO_BUILD_TESTS) - # NOTE: Important that this is first - enable_testing() - include(CTest) - include(Catch) - add_subdirectory(tests) + enable_testing() + include(CTest) + include(Catch) + add_subdirectory(tests) endif() add_subdirectory(lib/cloud) @@ -38,94 +50,110 @@ add_subdirectory(lib/container) add_subdirectory(lib/core) add_subdirectory(lib/execution) add_subdirectory(lib/geometry) -add_subdirectory(lib/map) -add_subdirectory(lib/math) +# add_subdirectory(lib/io) +# add_subdirectory(lib/map) add_subdirectory(lib/morton) -add_subdirectory(lib/plan) -add_subdirectory(lib/time) +add_subdirectory(lib/numeric) +# add_subdirectory(lib/plan) +add_subdirectory(lib/sensor) +# add_subdirectory(lib/time) add_subdirectory(lib/utility) add_subdirectory(lib/vision) -# add_subdirectory(lib/viz) - - -add_library(ufo INTERFACE) -add_library(UFO::UFO ALIAS ufo) - -target_link_libraries(ufo INTERFACE - UFO::Cloud - UFO::Compute - UFO::Container - UFO::Core - UFO::Execution - UFO::Geometry - UFO::Map - UFO::Math - UFO::Morton - UFO::Plan - UFO::Time - UFO::Utility - UFO::Vision - # UFO::Viz +# # add_subdirectory(lib/viz) + +add_library(UFO_Warnings INTERFACE) +add_library(UFO::Warnings ALIAS UFO_Warnings) +if (MSVC) + target_compile_options(UFO_Warnings INTERFACE /W4 /Zc:__cplusplus) +else() + target_compile_options(UFO_Warnings INTERFACE -Wall -Wextra -Wpedantic) +endif() + +add_library(UFO INTERFACE) +add_library(UFO::UFO ALIAS UFO) + +target_link_libraries(UFO INTERFACE + UFO::Cloud + UFO::Compute + UFO::Container + UFO::Core + UFO::Execution + UFO::Geometry + # UFO::IO + # UFO::Map + UFO::Morton + UFO::Numeric + # UFO::Plan + UFO::Sensor + # UFO::Time + UFO::Utility + UFO::Vision + # # UFO::Viz + $ ) +add_subdirectory(apps) + if(UFO_BUILD_DOCS) - find_package(Doxygen REQUIRED OPTIONAL_COMPONENTS dot) - - if(DOXYGEN_DOT_FOUND) - set(DOXYGEN_HAVE_DOT YES) - else() - set(DOXYGEN_HAVE_DOT NO) - endif() - - set(DOXYGEN_OUTPUT_DIR "${PROJECT_BINARY_DIR}/docs") - - configure_file( - "${PROJECT_SOURCE_DIR}/Doxyfile.in" - "${PROJECT_BINARY_DIR}/Doxyfile" - @ONLY - ) - - add_custom_target(Docs - COMMAND "${DOXYGEN_EXECUTABLE}" "${PROJECT_BINARY_DIR}/Doxyfile" - WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" - COMMENT "Generating API documentation with Doxygen" - VERBATIM - ) + if(DOXYGEN_DOT_FOUND) + set(DOXYGEN_HAVE_DOT YES) + else() + set(DOXYGEN_HAVE_DOT NO) + endif() + + set(DOXYGEN_OUTPUT_DIR "${PROJECT_BINARY_DIR}/docs") + set(DOXYGEN_AWESOME_DIR "${doxygen_awesome_css_SOURCE_DIR}") + + configure_file( + "${PROJECT_SOURCE_DIR}/Doxyfile.in" + "${PROJECT_BINARY_DIR}/Doxyfile" + @ONLY + ) + + add_custom_target(Docs + COMMAND "${DOXYGEN_EXECUTABLE}" "${PROJECT_BINARY_DIR}/Doxyfile" + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + COMMENT "Generating API documentation with Doxygen" + VERBATIM + ) endif() if(UFO_BUILD_EXAMPLES) - add_subdirectory(examples) + add_subdirectory(examples) endif() -# Installation & Export Logic -include(CMakePackageConfigHelpers) -include(GNUInstallDirs) - # Generate the version file write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/ufo-config-version.cmake" - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMajorVersion + "${CMAKE_CURRENT_BINARY_DIR}/ufo-config-version.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion ) # Generate the master Config file configure_package_config_file( - "${CMAKE_SOURCE_DIR}/cmake/ufo-config.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/ufo-config.cmake" - INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/UFO" + "${CMAKE_SOURCE_DIR}/cmake/ufo-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/ufo-config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/UFO" ) # Export all targets under the UFO:: namespace -install(EXPORT UFO-targets - FILE ufo-targets.cmake - NAMESPACE UFO:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/UFO" +install( + TARGETS UFO + EXPORT UFOTargets + DESTINATION "${CMAKE_INSTALL_LIBDIR}" +) + +install( + EXPORT UFOTargets + FILE ufo-targets.cmake + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/UFO" + NAMESPACE UFO:: ) # Install the config files install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}/ufo-config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/ufo-config-version.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/UFO" + FILES + "${CMAKE_CURRENT_BINARY_DIR}/ufo-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/ufo-config-version.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/UFO" ) \ No newline at end of file diff --git a/Doxyfile.in b/Doxyfile.in index c424a720..c4cab970 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -8,6 +8,7 @@ PROJECT_NAME = "@PROJECT_NAME@" PROJECT_NUMBER = @PROJECT_VERSION@ PROJECT_BRIEF = "An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown" OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIR@" +OPTIMIZE_OUTPUT_FOR_C = NO #--------------------------------------------------------------------------- # Input @@ -15,17 +16,25 @@ OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIR@" INPUT = "@PROJECT_SOURCE_DIR@/README.md" \ "@PROJECT_SOURCE_DIR@/lib" -FILE_PATTERNS = *.hpp +FILE_PATTERNS = *.hpp \ + *.cpp RECURSIVE = YES +EXCLUDE_PATTERNS = */tests/* */test/* */detail/* USE_MDFILE_AS_MAINPAGE = "@PROJECT_SOURCE_DIR@/README.md" +QUIET = YES #--------------------------------------------------------------------------- # Extraction #--------------------------------------------------------------------------- -EXTRACT_ALL = YES +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_PROTECTED = NO EXTRACT_STATIC = YES BUILTIN_STL_SUPPORT = YES +SEPARATE_MEMBER_PAGES = NO +GENERATE_GROUPS = YES +GROUP_NESTED_COMPOUNDS = YES #--------------------------------------------------------------------------- # Source browsing @@ -44,9 +53,17 @@ GENERATE_HTML = YES GENERATE_TREEVIEW = YES DISABLE_INDEX = NO FULL_SIDEBAR = NO -HTML_COLORSTYLE = AUTO_DARK +HTML_COLORSTYLE = LIGHT # Required for Doxygen Awesome SORT_BRIEF_DOCS = YES SORT_MEMBERS_CTORS_1ST = YES +HTML_EXTRA_STYLESHEET = "@DOXYGEN_AWESOME_DIR@/doxygen-awesome.css" +HTML_EXTRA_FILES = "@DOXYGEN_AWESOME_DIR@/doxygen-awesome-darkmode-toggle.js" \ + "@DOXYGEN_AWESOME_DIR@/doxygen-awesome-fragment-copy-button.js" \ + "@DOXYGEN_AWESOME_DIR@/doxygen-awesome-paragraph-link.js" \ + "@DOXYGEN_AWESOME_DIR@/doxygen-awesome-interactive-toc.js" \ + "@DOXYGEN_AWESOME_DIR@/doxygen-awesome-tabs.js" \ + "../../../docs/public/favicon.svg" +HTML_HEAD_EXTRA = #--------------------------------------------------------------------------- # LaTeX output @@ -60,10 +77,24 @@ GENERATE_LATEX = NO HAVE_DOT = @DOXYGEN_HAVE_DOT@ DOT_IMAGE_FORMAT = svg -DOT_TRANSPARENT = YES -CLASS_DIAGRAMS = YES CLASS_GRAPH = YES COLLABORATION_GRAPH = YES TEMPLATE_RELATIONS = YES INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES +HIDE_UNDOC_RELATIONS = YES + +#--------------------------------------------------------------------------- +# General enhancements +#--------------------------------------------------------------------------- +MARKDOWN_SUPPORT = YES +BRIEF_MEMBER_DESC = YES +SEARCHENGINE = YES +UML_LOOK = YES +GENERATE_TODOLIST = YES +GENERATE_FILELIST = YES +GENERATE_CLASSLIST = YES +SHOW_AUTHORS = YES +CALL_GRAPH = YES +CALLER_GRAPH = YES +PROJECT_LOGO = docs/public/favicon.svg # Uncomment and set if you have a logo \ No newline at end of file diff --git a/README.md b/README.md index 1af99acf..dbc5b2b3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,70 @@ -# ufo -UFO +# UFO (Unknown Free Occupied) + +An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown. + +[![License](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](https://raw.githubusercontent.com/UnknownFreeOccupied/ufo/0b998a3bbadd0a59eb25c0be5b9af0e56a5755a2/LICENSE) +[![Standard](https://img.shields.io/badge/C%2B%2B-23-blue.svg)](https://en.cppreference.com/w/cpp/23) + +UFO is a high-performance, modular 3D mapping framework designed for modern robotics and computer vision. It provides a robust probabilistic representation of space that explicitly handles free, occupied, and unknown regions, making it ideal for path planning and exploration in complex environments. + +## Key Features + +- **Modern C++23**: Leverages the latest language features for maximum performance and readability. +- **Probabilistic Mapping**: Accurately represents uncertainty in 3D environments. +- **High Performance**: Optimized with SIMD (SSE2, AVX, etc.) and Link Time Optimization (LTO). +- **Modular Architecture**: Easy to extend or use individual components like `Core`, `Math`, and `Utility`. +- **Cross-Platform**: Supports Linux, macOS, and Windows. + +## Quick Start + +### Prerequisites + +- **CMake** (v3.23 or newer) +- **C++23 Compatible Compiler** (GCC 14+, Clang 18+, MSVC 19.40+) +- Dependencies (automatically fetched if not found): + - [Catch2](https://github.com/catchorg/Catch2) (Testing) + - [CLI11](https://github.com/CLIUtils/CLI11) (CLI Parser) + - [Doxygen](https://www.doxygen.nl/) (Documentation) + +### Build Instructions + +```bash +# Clone the repository +git clone https://github.com/UnknownFreeOccupied/ufo.git +cd ufo + +# Configure and build +cmake -B build -DUFO_DEV_MODE=ON +cmake --build build -j$(nproc) +``` + +### Running the App + +The main `ufo` utility provides system diagnostics and diagnostic information about the build environment: + +```bash +./build/apps/ufo --version +``` + +## Project Structure + +- [`lib/core`](lib/core): Fundamental types and core mapping logic (Confidence, Semantic, etc.). +- [`lib/math`](lib/math): Optimized linear algebra and geometric primitives. +- [`lib/utility`](lib/utility): Threading, string manipulation, and system helpers. +- [`apps/`](apps): Command-line tools and applications. +- [`tests/`](tests): Global test suite (Catch2). + +## Documentation + +API documentation can be generated using Doxygen: + +```bash +cmake -B build -DUFO_BUILD_DOCS=ON +cmake --build build --target Docs +``` + +The output will be available in `build/docs/html/index.html`. + +## License + +UFO is released under the [BSD 3-Clause License](https://raw.githubusercontent.com/UnknownFreeOccupied/ufo/0b998a3bbadd0a59eb25c0be5b9af0e56a5755a2/LICENSE). diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt new file mode 100644 index 00000000..b8c81223 --- /dev/null +++ b/apps/CMakeLists.txt @@ -0,0 +1,47 @@ +add_executable(App) + +target_sources(App + PRIVATE + src/system_info.cpp + src/app.cpp + PUBLIC + FILE_SET HEADERS + BASE_DIRS + include + FILES + include/ufo/apps/system_info.hpp +) + +set_target_properties(App + PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + CXX_STANDARD 23 + CXX_EXTENSIONS OFF + CXX_SCAN_FOR_MODULES OFF + OUTPUT_NAME "ufo" +) + +target_link_libraries(App PRIVATE CLI11::CLI11 UFO::UFO $) + +if(WIN32) + target_link_libraries(App PRIVATE advapi32) +endif() + +if(UFO_ENABLE_LTO) + include(CheckIPOSupported) + check_ipo_supported(RESULT lto_supported OUTPUT lto_error) + + if(lto_supported) + set_target_properties(App PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) + message(STATUS "LTO: Enabled (Cross-file optimizations active)") + else() + message(WARNING "LTO: Not supported by this compiler/linker: ${lto_error}") + endif() +endif() + +install( + TARGETS App + EXPORT UFOTargets + FILE_SET HEADERS +) \ No newline at end of file diff --git a/lib/morton/include/ufo/morton/detail/morton.hpp b/apps/include/ufo/apps/system_info.hpp similarity index 75% rename from lib/morton/include/ufo/morton/detail/morton.hpp rename to apps/include/ufo/apps/system_info.hpp index e67aeae8..8dc5e35c 100644 --- a/lib/morton/include/ufo/morton/detail/morton.hpp +++ b/apps/include/ufo/apps/system_info.hpp @@ -1,18 +1,14 @@ -/*! - * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the - * Unknown - * - * @author Daniel Duberg (dduberg@kth.se) - * @see https://github.com/UnknownFreeOccupied/ufomap +/** + * @author Daniel Duberg (danielduberg@gmail.com) + * @see https://github.com/UnknownFreeOccupied/ufo * @version 1.0 - * @date 2022-05-13 + * @date 2026-02-22 * - * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of - * Technology + * @copyright Copyright (c) 2020-2026, Daniel Duberg * * BSD 3-Clause License * - * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology + * Copyright (c) 2020-2026, Daniel Duberg * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,16 +38,15 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UFO_DETAIL_MORTON_HPP -#define UFO_DETAIL_MORTON_HPP +#ifndef UFO_APPS_SYSTEM_INFO_HPP +#define UFO_APPS_SYSTEM_INFO_HPP // STL -#include +#include namespace ufo { -template -struct Morton; +[[nodiscard]] std::string systemInfo(); } // namespace ufo -#endif // UFO_DETAIL_MORTON_HPP \ No newline at end of file +#endif // UFO_APPS_SYSTEM_INFO_HPP \ No newline at end of file diff --git a/apps/src/app.cpp b/apps/src/app.cpp new file mode 100644 index 00000000..f0c89320 --- /dev/null +++ b/apps/src/app.cpp @@ -0,0 +1,79 @@ +/** + * @author Daniel Duberg (danielduberg@gmail.com) + * @see https://github.com/UnknownFreeOccupied/ufo + * @version 1.0 + * @date 2026-02-22 + * + * @copyright Copyright (c) 2020-2026, Daniel Duberg + * + * BSD 3-Clause License + * + * Copyright (c) 2020-2026, Daniel Duberg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. 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. + * + * THIS 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. + */ + +// UFO +#include + +// STL +#include +#include +#include +#include + +// CLI11 +#include + +int main(int argc, char* argv[]) +{ + std::string const app_description = + "ufo is a command-line tool for UFO.\nWebsite: " + "https://github.com/UnknownFreeOccupied/ufo"; + std::string const app_version = + std::format("UFO v{:d}.{:d}.{:d}\n\n{}", 1, 0, 0, ufo::systemInfo()); + constexpr std::string_view const app_footer_format = + "Call `ufo {} -h` for more detailed usage"; + + CLI::App app(app_description); + + app.set_version_flag("-v,--version", app_version)->group("INFO"); + + app.footer(std::format(app_footer_format, " ")); + + app.set_help_all_flag("--help-all", "Expand all help"); + + app.get_formatter()->label("SUBCOMMAND", "COMMAND"); + app.get_formatter()->label("SUBCOMMANDS", "COMMANDS"); + + app.require_subcommand(0); + + CLI11_PARSE(app, argc, argv); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/apps/src/system_info.cpp b/apps/src/system_info.cpp new file mode 100644 index 00000000..07231310 --- /dev/null +++ b/apps/src/system_info.cpp @@ -0,0 +1,638 @@ +/** + * @author Daniel Duberg (danielduberg@gmail.com) + * @see https://github.com/UnknownFreeOccupied/ufo + * @version 1.0 + * @date 2026-02-22 + * + * @copyright Copyright (c) 2020-2026, Daniel Duberg + * + * BSD 3-Clause License + * + * Copyright (c) 2020-2026, Daniel Duberg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. 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. + * + * THIS 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. + */ + +// UFO +#include +#include + +// STL +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#include + +#include +#pragma comment(lib, "advapi32.lib") +#elif defined(__APPLE__) +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +namespace +{ + +#if defined(_WIN32) +[[nodiscard]] OSVERSIONINFOEXW rtlOsVersion() +{ + using Fn = LONG(WINAPI*)(OSVERSIONINFOEXW*); + auto fn = reinterpret_cast( + GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion")); + OSVERSIONINFOEXW vi = {}; + vi.dwOSVersionInfoSize = sizeof(vi); + if (fn) fn(&vi); + return vi; +} +#endif + +#if defined(__linux__) +[[nodiscard]] std::string linuxName() +{ + std::ifstream file("/etc/os-release"); + std::string line; + if (file.is_open()) { + while (std::getline(file, line)) { + constexpr std::string_view key = "PRETTY_NAME="; + if (line.starts_with(key)) { + std::string name = line.substr(key.size()); + std::erase(name, '\"'); + return name; + } + } + } + return "Linux"; +} +#endif + +[[nodiscard]] std::string os() +{ +#if defined(_WIN32) + auto vi = rtlOsVersion(); + if (vi.dwMajorVersion != 0) { + if (vi.dwMajorVersion == 10 && vi.dwBuildNumber >= 22000) return "Windows 11"; + if (vi.dwMajorVersion == 10) return "Windows 10"; + return std::format("Windows {}.{}", vi.dwMajorVersion, vi.dwMinorVersion); + } + return "Windows"; +#elif defined(__APPLE__) + char ver[256] = {}; + size_t sz = sizeof(ver); + if (sysctlbyname("kern.osproductversion", ver, &sz, nullptr, 0) == 0) { + std::string version(ver); + int major = std::stoi(version); + + auto name = [major]() -> std::string_view { + switch (major) { + case 11: return "Big Sur"; + case 12: return "Monterey"; + case 13: return "Ventura"; + case 14: return "Sonoma"; + case 15: return "Sequoia"; + default: return {}; + } + }(); + if (!name.empty()) return std::format("macOS {} ({})", version, name); + return "macOS " + version; + } + return "macOS"; +#elif defined(__linux__) + return linuxName(); +#else + return "Unknown"; +#endif +} + +[[nodiscard]] std::string cpuModel() +{ +#if defined(_WIN32) + HKEY hKey; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, + "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, + &hKey) == ERROR_SUCCESS) { + char buf[256] = {}; + DWORD sz = sizeof(buf); + if (RegQueryValueExA(hKey, "ProcessorNameString", nullptr, nullptr, (LPBYTE)buf, + &sz) == ERROR_SUCCESS) { + RegCloseKey(hKey); + return std::string(buf); + } + RegCloseKey(hKey); + } + return "Unknown"; +#elif defined(__APPLE__) + char buf[256] = {}; + size_t sz = sizeof(buf); + sysctlbyname("machdep.cpu.brand_string", buf, &sz, nullptr, 0); + return std::string(buf); +#else + std::ifstream file("/proc/cpuinfo"); + std::string line; + while (std::getline(file, line)) { + if (line.starts_with("model name")) { + auto pos = line.find(':'); + if (pos != std::string::npos) { + auto name = line.substr(pos + 2); + name.erase(name.find_last_not_of(" \t\r\n") + 1); + return name; + } + } + } + return "Unknown"; +#endif +} + +[[nodiscard]] unsigned physicalCores() +{ +#if defined(_WIN32) + DWORD len = 0; + GetLogicalProcessorInformation(nullptr, &len); + std::vector buf( + len / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)); + GetLogicalProcessorInformation(buf.data(), &len); + unsigned count = 0; + for (auto& info : buf) { + if (info.Relationship == RelationProcessorCore) ++count; + } + return count; +#elif defined(__APPLE__) + int count = 0; + size_t sz = sizeof(count); + sysctlbyname("hw.physicalcpu", &count, &sz, nullptr, 0); + return static_cast(count); +#else + std::ifstream file("/proc/cpuinfo"); + std::string line; + std::set> cores; + int physical_id = -1, core_id = -1; + while (std::getline(file, line)) { + if (line.starts_with("physical id")) { + auto pos = line.find(':'); + if (pos != std::string::npos) physical_id = std::stoi(line.substr(pos + 1)); + } else if (line.starts_with("core id")) { + auto pos = line.find(':'); + if (pos != std::string::npos) core_id = std::stoi(line.substr(pos + 1)); + } else if (line.empty() && physical_id >= 0 && core_id >= 0) { + cores.insert({physical_id, core_id}); + physical_id = -1; + core_id = -1; + } + } + // Handle last entry when file does not end with a blank line + if (physical_id >= 0 && core_id >= 0) cores.insert({physical_id, core_id}); + + return cores.empty() ? std::thread::hardware_concurrency() + : static_cast(cores.size()); +#endif +} + +[[nodiscard]] auto cpuCores() { return std::thread::hardware_concurrency(); } + +[[nodiscard]] std::string kernelVersion() +{ +#if defined(_WIN32) + auto vi = rtlOsVersion(); + if (vi.dwMajorVersion != 0) { + return std::format("{}.{}.{}", vi.dwMajorVersion, vi.dwMinorVersion, + vi.dwBuildNumber); + } + return "Unknown"; +#else + struct utsname buf; + uname(&buf); + return std::string(buf.release); +#endif +} + +[[nodiscard]] std::string cpuArch() +{ +#if defined(_WIN32) + SYSTEM_INFO si; + GetNativeSystemInfo(&si); + switch (si.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_AMD64: return "x86_64"; + case PROCESSOR_ARCHITECTURE_ARM: return "ARM"; + case PROCESSOR_ARCHITECTURE_ARM64: return "ARM64"; + case PROCESSOR_ARCHITECTURE_INTEL: return "x86"; + default: return "Unknown"; + } +#else + struct utsname buf; + uname(&buf); + return std::string(buf.machine); +#endif +} + +[[nodiscard]] auto physicalRam() +{ + std::size_t bytes = 0; +#if defined(_WIN32) + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + bytes = static_cast(status.ullTotalPhys); +#elif defined(__APPLE__) + int64_t mem = 0; + size_t len = sizeof(mem); + sysctlbyname("hw.memsize", &mem, &len, nullptr, 0); + bytes = static_cast(mem); +#else + struct sysinfo si; + sysinfo(&si); + bytes = static_cast(si.totalram) * si.mem_unit; +#endif + return static_cast(bytes) / (1024 * 1024 * 1024); +} + +[[nodiscard]] consteval std::size_t addressModel() { return sizeof(void*) * 8; } + +[[nodiscard]] consteval std::string_view endianness() +{ + return (std::endian::native == std::endian::little) ? "Little Endian" : "Big Endian"; +} + +[[nodiscard]] std::string compiler() +{ +#if defined(__clang__) + return std::format("Clang {}.{}.{}", __clang_major__, __clang_minor__, + __clang_patchlevel__); +#elif defined(__GNUC__) + return std::format("GCC {}.{}.{}", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); +#elif defined(_MSC_VER) + return std::format("MSVC {}", _MSC_FULL_VER); +#else + return "Unknown Compiler"; +#endif +} + +[[nodiscard]] double availableRam() +{ + std::size_t bytes = 0; +#if defined(_WIN32) + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + bytes = static_cast(status.ullAvailPhys); +#elif defined(__APPLE__) + vm_size_t page_size = 0; + mach_port_t host = mach_host_self(); + vm_statistics64_data_t vmstat = {}; + mach_msg_type_number_t count = HOST_VM_INFO64_COUNT; + host_page_size(host, &page_size); + if (host_statistics64(host, HOST_VM_INFO64, (host_info64_t)&vmstat, &count) == + KERN_SUCCESS) { + // free + inactive: inactive pages are reclaimable and count as available + bytes = (static_cast(vmstat.free_count) + + static_cast(vmstat.inactive_count)) * + page_size; + } +#else + // MemAvailable accounts for free + reclaimable cache — same as `free -h` + std::ifstream meminfo("/proc/meminfo"); + std::string line; + while (std::getline(meminfo, line)) { + if (line.starts_with("MemAvailable:")) { + auto pos = line.find(':'); + // Value is in kB + bytes = std::stoull(line.substr(pos + 1)) * 1024; + break; + } + } +#endif + return static_cast(bytes) / (1024 * 1024 * 1024); +} + +[[nodiscard]] consteval std::string_view buildType() +{ +#ifdef NDEBUG + return "Release"; +#else + return "Debug"; +#endif +} + +[[nodiscard]] std::string simdExtensions() +{ + std::string ext; +#if defined(__AVX512F__) + ext += "AVX-512F "; +#endif +#if defined(__AVX512BW__) + ext += "AVX-512BW "; +#endif +#if defined(__AVX512DQ__) + ext += "AVX-512DQ "; +#endif +#if defined(__AVX2__) + ext += "AVX2 "; +#endif +#if defined(__FMA__) + ext += "FMA "; +#endif +#if defined(__AVX__) + ext += "AVX "; +#endif +#if defined(__SSE4_2__) + ext += "SSE4.2 "; +#endif +#if defined(__SSE4_1__) + ext += "SSE4.1 "; +#endif +#if defined(__SSSE3__) + ext += "SSSE3 "; +#endif +#if defined(__SSE3__) + ext += "SSE3 "; +#endif +#if defined(__SSE2__) + ext += "SSE2 "; +#endif +#if defined(__ARM_FEATURE_SVE2) + ext += "SVE2 "; +#endif +#if defined(__ARM_FEATURE_SVE) + ext += "SVE "; +#endif +#if defined(__ARM_NEON) + ext += "NEON "; +#endif + if (ext.empty()) return "None"; + ext.pop_back(); // remove trailing space + return ext; +} + +[[nodiscard]] consteval std::string_view cppVersion() +{ +#if __cplusplus > 202302L + return "C++26 or later"; +#elif __cplusplus == 202302L + return "C++23"; +#elif __cplusplus > 202002L + return "C++23 (Experimental/Draft)"; +#elif __cplusplus == 202002L + return "C++20"; +#elif __cplusplus > 201703L + return "C++20 (Experimental/Draft)"; +#elif __cplusplus == 201703L + return "C++17"; +#elif __cplusplus > 201402L + return "C++17 (Experimental/Draft)"; +#elif __cplusplus == 201402L + return "C++14"; +#elif __cplusplus > 201103L + return "C++14 (Experimental/Draft)"; +#elif __cplusplus == 201103L + return "C++11"; +#else + return "Pre-C++11 or unknown"; +#endif +} + +size_t getTerminalWidth() +{ +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { + return static_cast(csbi.srWindow.Right - csbi.srWindow.Left + 1); + } +#else + struct winsize w; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) { + return static_cast(w.ws_col); + } +#endif + return 80; // Fallback +} + +std::string wrapAndIndent(std::string const& text, size_t indentWidth, size_t maxWidth) +{ + if (text.empty()) return ""; + std::string result; + size_t availableWidth = (maxWidth > indentWidth) ? maxWidth - indentWidth : 20; + std::string indent(indentWidth, ' '); + size_t start = 0; + + while (start < text.length()) { + if (start > 0) result += "\n" + indent; + + if (text.length() - start <= availableWidth) { + result += text.substr(start); + break; + } + + size_t end = start + availableWidth; + // Search ONLY for spaces to keep version numbers (dots) together + size_t break_pos = text.find_last_of(' ', end); + + if (break_pos != std::string::npos && break_pos > start) { + result += text.substr(start, break_pos - start); + start = break_pos + 1; // Skip the space + } else { + // If a single word is longer than the line, force break it + result += text.substr(start, availableWidth); + start += availableWidth; + } + } + return result; +} + +} // namespace + +std::string ufo::systemInfo() +{ + size_t const MAX_WIDTH = std::clamp(getTerminalWidth(), 60, 100); + size_t const LABEL_WIDTH = 19; + + // ANSI Palette + std::string const RESET = "\033[0m"; + std::string const BOLD = "\033[1m"; + std::string const CYAN = "\033[36m"; + std::string const BLUE = "\033[34m"; + std::string const PURPLE = "\033[35m"; + std::string const YELLOW = "\033[33m"; + std::string const RED = "\033[31m"; + std::string const GREEN = "\033[32m"; + + // 1. Dynamic Borders + std::string title_text = " SYSTEM INFO "; + size_t side_len = + (MAX_WIDTH > title_text.length()) ? (MAX_WIDTH - title_text.length()) / 2 : 0; + std::string header = + std::format("{}{:=<{}}{}{:=<{}}{}", BOLD + BLUE, "", side_len, title_text, "", + MAX_WIDTH - side_len - title_text.length(), RESET); + + // 2. RAM Status Check (assuming physicalRam() returns GB) + // You might need to parse availableRam() if it's a string, or use a numeric version + // For this example, I'm assuming a numeric 'avail_gb' exists + double total_gb = physicalRam(); + double avail_gb = availableRam(); + double ratio = avail_gb / total_gb; + std::string ram_status_color = (ratio < 0.15) ? RED : (ratio < 0.35) ? YELLOW : GREEN; + + // 3. GPU Section + auto const gpus = compute::gpusInfo(); + std::string gpu_str; + for (auto const& gpu : gpus) { + std::string type = + (gpu.type == "Unknown" || gpu.type.empty()) ? "" : std::format("({}) ", gpu.type); + // Note: Formatting width adjusted to 28 to account for 9 hidden ANSI chars in label + std::string label = std::format("{}GPU [{}]:{}", PURPLE, gpu.backend, RESET); + std::string content = + std::format("{} {}[Driver: {}]", gpu.name, type, gpu.description); + gpu_str += + std::format("{:<28}{}\n", label, wrapAndIndent(content, LABEL_WIDTH, MAX_WIDTH)); + } + + // 4. Build Status + std::string bType(buildType()); + std::string bColor = (bType == "Debug") ? RED : GREEN; + + return std::format( + "{}\n" + "{:<28}{}\n" // OS + "{:<28}{}\n" // CPU + "{:<28}{}\n" // SIMD + "{:<28}{:.1f} GB Total ({}{:.1f} GB{})\n\n" // RAM with status color + "{}\n" + "{:<28}{}\n" // Env + "{:<28}{}\n" // Compiler + "{:<28}{}{} [{} {}]{}\n" // Build + "{}{}{}", + header, YELLOW + "OS:" + RESET, + wrapAndIndent(std::format("{} (Kernel: {})", os(), kernelVersion()), LABEL_WIDTH, + MAX_WIDTH), + YELLOW + "CPU:" + RESET, + cpuModel() + " (" + std::to_string(physicalCores()) + "C/" + + std::to_string(cpuCores()) + "T) @ " + cpuArch(), + YELLOW + "SIMD Extensions:" + RESET, + wrapAndIndent(simdExtensions(), LABEL_WIDTH, MAX_WIDTH), YELLOW + "RAM:" + RESET, + total_gb, ram_status_color, avail_gb, RESET, gpu_str, CYAN + "Environment:" + RESET, + std::format("{}-bit | {}", addressModel(), endianness()), + CYAN + "Compiler:" + RESET, + std::format("{} | {} ({})", compiler(), cppVersion(), __cplusplus), + CYAN + "Build:" + RESET, bColor, bType, __DATE__, __TIME__, RESET, BOLD + BLUE, + std::string(MAX_WIDTH, '='), RESET); +} + +// std::string ufo::systemInfo() +// { +// auto const gpus = compute::gpusInfo(); + +// std::string gpu_str; +// for (std::size_t i = 0; i < gpus.size(); ++i) { +// auto const& gpu = gpus[i]; + +// std::string type_display = +// (gpu.type == "Unknown") ? "" : std::format("({}) ", gpu.type); + +// std::string label = std::format("GPU [{}]", gpu.backend); + +// gpu_str += std::format("{:<19}{} {}[Driver: {}]\n", label + ":", gpu.name, +// type_display, gpu.description); +// } + +// return std::format( +// "==================== SYSTEM INFO ====================\n" +// "OS: {} (Kernel: {})\n" +// "CPU: {} ({}C/{}T) @ {}\n" +// "SIMD Extensions: {}\n" +// "RAM: {:.1f} GB Total ({} Available)\n" +// "\n" +// "{}" +// "\n" +// "Environment: {}-bit | {}\n" +// "Compiler: {} | {} ({})\n" +// "Build: {} [{} {}]\n" +// "=====================================================", +// os(), kernelVersion(), cpuModel(), physicalCores(), cpuCores(), cpuArch(), +// simdExtensions(), physicalRam(), availableRam(), gpu_str, addressModel(), +// endianness(), compiler(), cppVersion(), __cplusplus, buildType(), __DATE__, +// __TIME__); +// } + +// std::string ufo::systemInfo() +// { +// auto const gpus = compute::gpusInfo(); + +// std::string gpu_str; +// for (std::size_t i = 0; i < gpus.size(); ++i) { +// auto const& gpu = gpus[i]; +// if (i > 0) { +// gpu_str += std::format("\n{:<19}", std::format("GPU {}:", i)); +// } + +// gpu_str += std::format("{} [{}]", gpu.name, gpu.backend); +// gpu_str += std::format("\n{:<23}Vendor: {} ({})", " ", gpu.vendor, gpu.type); + +// if (!gpu.architecture.empty()) { +// gpu_str += std::format("\n{:<23}Arch: {}", " ", gpu.architecture); +// } + +// if (!gpu.description.empty()) { +// gpu_str += std::format("\n{:<23}Driver: {}", " ", gpu.description); +// } +// } + +// return std::format( +// "==================== SYSTEM INFO ====================\n" +// "OS: {}\n" +// "Kernel: {}\n" +// "CPU: {}\n" +// "CPU Architecture: {}\n" +// "SIMD Extensions: {}\n" +// "Physical Cores: {}\n" +// "Logical Cores: {}\n" +// "Physical RAM: {:.1f} GB\n" +// "Available RAM: {}\n" +// "GPU: {}\n" +// "Address Model: {}-bit\n" +// "Endianness: {}\n" +// "Compiler: {}\n" +// "C++ Standard: {} ({})\n" +// "Build Type: {}\n" +// "Build Time: {} {}\n" +// "=====================================================", +// os(), kernelVersion(), cpuModel(), cpuArch(), simdExtensions(), physicalCores(), +// cpuCores(), physicalRam(), availableRam(), gpu_str, addressModel(), endianness(), +// compiler(), cppVersion(), __cplusplus, buildType(), __DATE__, __TIME__); +// } diff --git a/cmake/ufo-config.cmake.in b/cmake/ufo-config.cmake.in index 756ae037..46d7b121 100644 --- a/cmake/ufo-config.cmake.in +++ b/cmake/ufo-config.cmake.in @@ -1,6 +1,24 @@ @PACKAGE_INIT@ -include("${CMAKE_CURRENT_LIST_DIR}/wgpu_native-targets.cmake") +include(CMakeFindDependencyMacro) + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + find_dependency(Threads) +endif() + +if(@TBB_FOUND@) + find_dependency(TBB) +endif() + +if(@OpenMP_CXX_FOUND@) + find_dependency(OpenMP) +endif() + +# WGPU Native targets (only if the Compute component was installed) +if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/wgpu_native-targets.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/wgpu_native-targets.cmake") +endif() + include("${CMAKE_CURRENT_LIST_DIR}/ufo-targets.cmake") foreach(_comp ${UFO_FIND_COMPONENTS}) @@ -11,12 +29,5 @@ foreach(_comp ${UFO_FIND_COMPONENTS}) endif() endforeach() -include(CMakeFindDependencyMacro) -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - find_dependency(Threads) -endif() -find_dependency(TBB) -find_dependency(OpenMP) - #checks if any REQUIRED components were marked FOUND=FALSE and sets UFO_FOUND check_required_components(UFO) \ No newline at end of file diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 290d66bb..eb86f366 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -1,21 +1,35 @@ // @ts-check import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; +import mermaid from 'astro-mermaid'; +import { starlightKatex } from 'starlight-katex'; // https://astro.build/config export default defineConfig({ site: 'https://unknownfreeoccupied.github.io', - base: '/ufo', + base: '/ufo', integrations: [ + mermaid(), // MUST come before Starlight starlight({ title: { en: 'UFO', sv: 'UFO', }, + plugins: [starlightKatex()], + head: [ + { + tag: 'link', // Force the CSS to load to prevent double-rendering + attrs: { + rel: 'stylesheet', + href: 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css', + }, + }, + ], favicon: '/favicon.svg', + customCss: ['./src/styles/custom.css'], social: [{ icon: 'github', label: 'GitHub', href: 'https://github.com/UnknownFreeOccupied/ufo' }], sidebar: [ - { + { label: 'Start Here', translations: { 'sv-SE': 'Börja här', @@ -34,6 +48,7 @@ export default defineConfig({ items: [ // Each item here is one entry in the navigation menu. 'concepts/core_concepts', + 'concepts/predicates', ], }, { @@ -70,6 +85,8 @@ export default defineConfig({ }, items: [ // Each item here is one entry in the navigation menu. + 'guides/create_map_type', + 'guides/create_predicate', 'guides/host_website', ], }, @@ -85,19 +102,19 @@ export default defineConfig({ }, ], // Set English as the default language for this site. - defaultLocale: 'en', - locales: { - // English docs in `src/content/docs/en/` - en: { - label: 'English', + defaultLocale: 'en', + locales: { + // English docs in `src/content/docs/en/` + en: { + label: 'English', lang: 'en', - }, - // Swedish docs in `src/content/docs/sv/` - sv: { - label: 'Svenska', + }, + // Swedish docs in `src/content/docs/sv/` + sv: { + label: 'Svenska', lang: 'sv-SE', - }, - }, + }, + }, }), ], }); diff --git a/docs/bun.lock b/docs/bun.lock index ba311917..eadd8029 100644 --- a/docs/bun.lock +++ b/docs/bun.lock @@ -6,12 +6,18 @@ "name": "docs", "dependencies": { "@astrojs/starlight": "^0.37.6", + "@mermaid-js/layout-elk": "^0.2.0", "astro": "^5.6.1", + "astro-mermaid": "^1.3.1", + "mermaid": "^11.12.3", "sharp": "^0.34.2", + "starlight-katex": "^0.0.4", }, }, }, "packages": { + "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="], + "@astrojs/compiler": ["@astrojs/compiler@2.13.1", "", {}, "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg=="], "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.5", "", {}, "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA=="], @@ -38,8 +44,20 @@ "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="], + "@capsizecss/unpack": ["@capsizecss/unpack@4.0.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA=="], + "@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@11.1.2", "", { "dependencies": { "@chevrotain/gast": "11.1.2", "@chevrotain/types": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-XTsjvDVB5nDZBQB8o0o/0ozNelQtn2KrUVteIHSlPd2VAV2utEb6JzyCJaJ8tGxACR4RiBNWy5uYUHX2eji88Q=="], + + "@chevrotain/gast": ["@chevrotain/gast@11.1.2", "", { "dependencies": { "@chevrotain/types": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-Z9zfXR5jNZb1Hlsd/p+4XWeUFugrHirq36bKzPWDSIacV+GPSVXdk+ahVWZTwjhNwofAWg/sZg58fyucKSQx5g=="], + + "@chevrotain/regexp-to-ast": ["@chevrotain/regexp-to-ast@11.1.2", "", {}, "sha512-nMU3Uj8naWer7xpZTYJdxbAs6RIv/dxYzkYU8GSwgUtcAAlzjcPfX1w+RKRcYG8POlzMeayOQ/znfwxEGo5ulw=="], + + "@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="], + + "@chevrotain/utils": ["@chevrotain/utils@11.1.2", "", {}, "sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA=="], + "@ctrl/tinycolor": ["@ctrl/tinycolor@4.2.0", "", {}, "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A=="], "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], @@ -104,6 +122,10 @@ "@expressive-code/plugin-text-markers": ["@expressive-code/plugin-text-markers@0.41.6", "", { "dependencies": { "@expressive-code/core": "^0.41.6" } }, "sha512-PBFa1wGyYzRExMDzBmAWC6/kdfG1oLn4pLpBeTfIRrALPjcGA/59HP3e7q9J0Smk4pC7U+lWkA2LHR8FYV8U7Q=="], + "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], + + "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="], + "@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="], "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], @@ -158,6 +180,10 @@ "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], + "@mermaid-js/layout-elk": ["@mermaid-js/layout-elk@0.2.0", "", { "dependencies": { "d3": "^7.9.0", "elkjs": "^0.9.3" }, "peerDependencies": { "mermaid": "^11.0.2" } }, "sha512-vjjYGnCCjYlIA/rR7M//eFi0rHM6dsMyN1JQKfckpt30DTC/esrw36hcrvA2FNPHaqh3Q/SyBWzddyaky8EtUQ=="], + + "@mermaid-js/parser": ["@mermaid-js/parser@1.0.0", "", { "dependencies": { "langium": "^4.0.0" } }, "sha512-vvK0Hi/VWndxoh03Mmz6wa1KDriSPjS2XMZL/1l19HFwygiObEEoEwSDxOqyLzzAI6J2PU3261JjTMTO7x+BPw=="], + "@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="], "@pagefind/darwin-arm64": ["@pagefind/darwin-arm64@1.4.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ=="], @@ -240,16 +266,82 @@ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + "@types/d3": ["@types/d3@7.4.3", "", { "dependencies": { "@types/d3-array": "*", "@types/d3-axis": "*", "@types/d3-brush": "*", "@types/d3-chord": "*", "@types/d3-color": "*", "@types/d3-contour": "*", "@types/d3-delaunay": "*", "@types/d3-dispatch": "*", "@types/d3-drag": "*", "@types/d3-dsv": "*", "@types/d3-ease": "*", "@types/d3-fetch": "*", "@types/d3-force": "*", "@types/d3-format": "*", "@types/d3-geo": "*", "@types/d3-hierarchy": "*", "@types/d3-interpolate": "*", "@types/d3-path": "*", "@types/d3-polygon": "*", "@types/d3-quadtree": "*", "@types/d3-random": "*", "@types/d3-scale": "*", "@types/d3-scale-chromatic": "*", "@types/d3-selection": "*", "@types/d3-shape": "*", "@types/d3-time": "*", "@types/d3-time-format": "*", "@types/d3-timer": "*", "@types/d3-transition": "*", "@types/d3-zoom": "*" } }, "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww=="], + + "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], + + "@types/d3-axis": ["@types/d3-axis@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw=="], + + "@types/d3-brush": ["@types/d3-brush@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A=="], + + "@types/d3-chord": ["@types/d3-chord@3.0.6", "", {}, "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg=="], + + "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], + + "@types/d3-contour": ["@types/d3-contour@3.0.6", "", { "dependencies": { "@types/d3-array": "*", "@types/geojson": "*" } }, "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg=="], + + "@types/d3-delaunay": ["@types/d3-delaunay@6.0.4", "", {}, "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="], + + "@types/d3-dispatch": ["@types/d3-dispatch@3.0.7", "", {}, "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA=="], + + "@types/d3-drag": ["@types/d3-drag@3.0.7", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ=="], + + "@types/d3-dsv": ["@types/d3-dsv@3.0.7", "", {}, "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g=="], + + "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="], + + "@types/d3-fetch": ["@types/d3-fetch@3.0.7", "", { "dependencies": { "@types/d3-dsv": "*" } }, "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA=="], + + "@types/d3-force": ["@types/d3-force@3.0.10", "", {}, "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw=="], + + "@types/d3-format": ["@types/d3-format@3.0.4", "", {}, "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="], + + "@types/d3-geo": ["@types/d3-geo@3.1.0", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ=="], + + "@types/d3-hierarchy": ["@types/d3-hierarchy@3.1.7", "", {}, "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg=="], + + "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="], + + "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="], + + "@types/d3-polygon": ["@types/d3-polygon@3.0.2", "", {}, "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA=="], + + "@types/d3-quadtree": ["@types/d3-quadtree@3.0.6", "", {}, "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="], + + "@types/d3-random": ["@types/d3-random@3.0.3", "", {}, "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ=="], + + "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], + + "@types/d3-scale-chromatic": ["@types/d3-scale-chromatic@3.1.0", "", {}, "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ=="], + + "@types/d3-selection": ["@types/d3-selection@3.0.11", "", {}, "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w=="], + + "@types/d3-shape": ["@types/d3-shape@3.1.8", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="], + + "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], + + "@types/d3-time-format": ["@types/d3-time-format@4.0.3", "", {}, "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="], + + "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], + + "@types/d3-transition": ["@types/d3-transition@3.0.9", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg=="], + + "@types/d3-zoom": ["@types/d3-zoom@3.0.8", "", { "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw=="], + "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], + "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="], + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], "@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="], + "@types/katex": ["@types/katex@0.16.8", "", {}, "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg=="], + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], @@ -262,6 +354,8 @@ "@types/sax": ["@types/sax@1.2.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A=="], + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], @@ -286,12 +380,18 @@ "array-iterate": ["array-iterate@2.0.1", "", {}, "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg=="], + "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="], + "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], "astro": ["astro@5.17.2", "", { "dependencies": { "@astrojs/compiler": "^2.13.0", "@astrojs/internal-helpers": "0.7.5", "@astrojs/markdown-remark": "6.3.10", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^4.0.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "acorn": "^8.15.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.3.1", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.1.1", "cssesc": "^3.0.0", "debug": "^4.4.3", "deterministic-object-hash": "^2.0.2", "devalue": "^5.6.2", "diff": "^8.0.3", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.27.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.4.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.1", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.1", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.3", "shiki": "^3.21.0", "smol-toml": "^1.6.0", "svgo": "^4.0.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.3", "unist-util-visit": "^5.0.0", "unstorage": "^1.17.4", "vfile": "^6.0.3", "vite": "^6.4.1", "vitefu": "^1.1.1", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.3", "zod": "^3.25.76", "zod-to-json-schema": "^3.25.1", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "astro.js" } }, "sha512-7jnMqGo53hOQNwo1N/wqeOvUp8wwW/p+DeerSjSkHNx8L/1mhy6P7rVo7EhdmF8DpKqw0tl/B5Fx1WcIzg1ysA=="], "astro-expressive-code": ["astro-expressive-code@0.41.6", "", { "dependencies": { "rehype-expressive-code": "^0.41.6" }, "peerDependencies": { "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta" } }, "sha512-l47tb1uhmVIebHUkw+HEPtU/av0G4O8Q34g2cbkPvC7/e9ZhANcjUUciKt9Hp6gSVDdIuXBBLwJQn2LkeGMOAw=="], + "astro-integration-kit": ["astro-integration-kit@0.16.1", "", { "dependencies": { "pathe": "^1.1.2", "recast": "^0.23.7" }, "peerDependencies": { "astro": "^4.12.0" } }, "sha512-N/iam0PAFrRT9azYZqscP1HowQhC77Dwlp912P0/72k+kwUVgO3m73F26XXukHYoZBsrHgrUrfsWBxuCH3kEUg=="], + + "astro-mermaid": ["astro-mermaid@1.3.1", "", { "dependencies": { "import-meta-resolve": "^4.2.0", "mdast-util-to-string": "^4.0.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@mermaid-js/layout-elk": "^0.2.0", "astro": "^4.0.0 || ^5.0.0", "mermaid": "^10.0.0 || ^11.0.0" }, "optionalPeers": ["@mermaid-js/layout-elk"] }, "sha512-1+FjwayMSZLtFd+ofdu1+v8a902nN5wmPmjY2qb8tLiO96YlL65LbskiuUcyH6q9h0CdZCrkc5FimlaHZsMJsg=="], + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], @@ -320,6 +420,10 @@ "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], + "chevrotain": ["chevrotain@11.1.2", "", { "dependencies": { "@chevrotain/cst-dts-gen": "11.1.2", "@chevrotain/gast": "11.1.2", "@chevrotain/regexp-to-ast": "11.1.2", "@chevrotain/types": "11.1.2", "@chevrotain/utils": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-opLQzEVriiH1uUQ4Kctsd49bRoFDXGGSC4GUqj7pGyxM3RehRhvTlZJc1FL/Flew2p5uwxa1tUDWKzI4wNM8pg=="], + + "chevrotain-allstar": ["chevrotain-allstar@0.3.1", "", { "dependencies": { "lodash-es": "^4.17.21" }, "peerDependencies": { "chevrotain": "^11.0.0" } }, "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw=="], + "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="], @@ -336,10 +440,14 @@ "common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="], + "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], "cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="], + "cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="], + "crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], @@ -354,12 +462,88 @@ "csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="], + "cytoscape": ["cytoscape@3.33.1", "", {}, "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ=="], + + "cytoscape-cose-bilkent": ["cytoscape-cose-bilkent@4.1.0", "", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="], + + "cytoscape-fcose": ["cytoscape-fcose@2.2.0", "", { "dependencies": { "cose-base": "^2.2.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ=="], + + "d3": ["d3@7.9.0", "", { "dependencies": { "d3-array": "3", "d3-axis": "3", "d3-brush": "3", "d3-chord": "3", "d3-color": "3", "d3-contour": "4", "d3-delaunay": "6", "d3-dispatch": "3", "d3-drag": "3", "d3-dsv": "3", "d3-ease": "3", "d3-fetch": "3", "d3-force": "3", "d3-format": "3", "d3-geo": "3", "d3-hierarchy": "3", "d3-interpolate": "3", "d3-path": "3", "d3-polygon": "3", "d3-quadtree": "3", "d3-random": "3", "d3-scale": "4", "d3-scale-chromatic": "3", "d3-selection": "3", "d3-shape": "3", "d3-time": "3", "d3-time-format": "4", "d3-timer": "3", "d3-transition": "3", "d3-zoom": "3" } }, "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA=="], + + "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], + + "d3-axis": ["d3-axis@3.0.0", "", {}, "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="], + + "d3-brush": ["d3-brush@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "3", "d3-transition": "3" } }, "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ=="], + + "d3-chord": ["d3-chord@3.0.1", "", { "dependencies": { "d3-path": "1 - 3" } }, "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g=="], + + "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], + + "d3-contour": ["d3-contour@4.0.2", "", { "dependencies": { "d3-array": "^3.2.0" } }, "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA=="], + + "d3-delaunay": ["d3-delaunay@6.0.4", "", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="], + + "d3-dispatch": ["d3-dispatch@3.0.1", "", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="], + + "d3-drag": ["d3-drag@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" } }, "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg=="], + + "d3-dsv": ["d3-dsv@3.0.1", "", { "dependencies": { "commander": "7", "iconv-lite": "0.6", "rw": "1" }, "bin": { "csv2json": "bin/dsv2json.js", "csv2tsv": "bin/dsv2dsv.js", "dsv2dsv": "bin/dsv2dsv.js", "dsv2json": "bin/dsv2json.js", "json2csv": "bin/json2dsv.js", "json2dsv": "bin/json2dsv.js", "json2tsv": "bin/json2dsv.js", "tsv2csv": "bin/dsv2dsv.js", "tsv2json": "bin/dsv2json.js" } }, "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q=="], + + "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], + + "d3-fetch": ["d3-fetch@3.0.1", "", { "dependencies": { "d3-dsv": "1 - 3" } }, "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw=="], + + "d3-force": ["d3-force@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-quadtree": "1 - 3", "d3-timer": "1 - 3" } }, "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg=="], + + "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="], + + "d3-geo": ["d3-geo@3.1.1", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="], + + "d3-hierarchy": ["d3-hierarchy@3.1.2", "", {}, "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="], + + "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], + + "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], + + "d3-polygon": ["d3-polygon@3.0.1", "", {}, "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="], + + "d3-quadtree": ["d3-quadtree@3.0.1", "", {}, "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="], + + "d3-random": ["d3-random@3.0.1", "", {}, "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="], + + "d3-sankey": ["d3-sankey@0.12.3", "", { "dependencies": { "d3-array": "1 - 2", "d3-shape": "^1.2.0" } }, "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ=="], + + "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], + + "d3-scale-chromatic": ["d3-scale-chromatic@3.1.0", "", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="], + + "d3-selection": ["d3-selection@3.0.0", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="], + + "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], + + "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], + + "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], + + "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], + + "d3-transition": ["d3-transition@3.0.1", "", { "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", "d3-ease": "1 - 3", "d3-interpolate": "1 - 3", "d3-timer": "1 - 3" }, "peerDependencies": { "d3-selection": "2 - 3" } }, "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w=="], + + "d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="], + + "dagre-d3-es": ["dagre-d3-es@7.0.13", "", { "dependencies": { "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q=="], + + "dayjs": ["dayjs@1.11.19", "", {}, "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], + "delaunator": ["delaunator@5.0.1", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw=="], + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], @@ -384,10 +568,14 @@ "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], + "dompurify": ["dompurify@3.3.1", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q=="], + "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], "dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="], + "elkjs": ["elkjs@0.9.3", "", {}, "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ=="], + "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], @@ -402,6 +590,8 @@ "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + "estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="], "estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" } }, "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ=="], @@ -438,12 +628,20 @@ "h3": ["h3@1.15.5", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", "defu": "^6.1.4", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg=="], + "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], + + "hast-util-classnames": ["hast-util-classnames@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-tI3JjoGDEBVorMAWK4jNRsfLMYmih1BUOG3VV36pH36njs1IEl7xkNrVTD2mD2yYHmQCa5R/fj61a8IAF4bRaQ=="], + "hast-util-embedded": ["hast-util-embedded@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-is-element": "^3.0.0" } }, "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA=="], "hast-util-format": ["hast-util-format@1.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-embedded": "^3.0.0", "hast-util-minify-whitespace": "^1.0.0", "hast-util-phrasing": "^3.0.0", "hast-util-whitespace": "^3.0.0", "html-whitespace-sensitive-tag-names": "^3.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA=="], + "hast-util-from-dom": ["hast-util-from-dom@5.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hastscript": "^9.0.0", "web-namespaces": "^2.0.0" } }, "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q=="], + "hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="], + "hast-util-from-html-isomorphic": ["hast-util-from-html-isomorphic@2.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-dom": "^5.0.0", "hast-util-from-html": "^2.0.0", "unist-util-remove-position": "^5.0.0" } }, "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw=="], + "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="], "hast-util-has-property": ["hast-util-has-property@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA=="], @@ -488,10 +686,14 @@ "i18next": ["i18next@23.16.8", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg=="], + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], + "internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="], + "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="], "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], @@ -514,10 +716,20 @@ "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "katex": ["katex@0.16.33", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-q3N5u+1sY9Bu7T4nlXoiRBXWfwSefNGoKeOwekV+gw0cAXQlz2Ww6BLcmBxVDeXBMUDQv6fK5bcNaJLxob3ZQA=="], + + "khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="], + "kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], "klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="], + "langium": ["langium@4.2.1", "", { "dependencies": { "chevrotain": "~11.1.1", "chevrotain-allstar": "~0.3.1", "vscode-languageserver": "~9.0.1", "vscode-languageserver-textdocument": "~1.0.11", "vscode-uri": "~3.1.0" } }, "sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ=="], + + "layout-base": ["layout-base@1.0.2", "", {}, "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="], + + "lodash-es": ["lodash-es@4.17.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="], + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], "lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="], @@ -530,6 +742,8 @@ "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + "marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="], + "mdast-util-definitions": ["mdast-util-definitions@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ=="], "mdast-util-directive": ["mdast-util-directive@3.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q=="], @@ -550,6 +764,8 @@ "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + "mdast-util-math": ["mdast-util-math@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "longest-streak": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.1.0", "unist-util-remove-position": "^5.0.0" } }, "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w=="], + "mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="], "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], @@ -568,6 +784,8 @@ "mdn-data": ["mdn-data@2.12.2", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="], + "mermaid": ["mermaid@11.12.3", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.1", "@mermaid-js/parser": "^1.0.0", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.13", "dayjs": "^1.11.18", "dompurify": "^3.2.5", "katex": "^0.16.22", "khroma": "^2.1.0", "lodash-es": "^4.17.23", "marked": "^16.2.1", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0" } }, "sha512-wN5ZSgJQIC+CHJut9xaKWsknLxaFBwCPwPkGTSUYrTiHORWvpT8RxGk849HPnpUAQ+/9BPRqYb80jTpearrHzQ=="], + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], @@ -588,6 +806,8 @@ "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + "micromark-extension-math": ["micromark-extension-math@3.1.0", "", { "dependencies": { "@types/katex": "^0.16.0", "devlop": "^1.0.0", "katex": "^0.16.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg=="], + "micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="], "micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="], @@ -640,6 +860,8 @@ "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="], + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -682,12 +904,22 @@ "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="], + + "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], + "piccolore": ["piccolore@0.1.3", "", {}, "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], + + "points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="], + + "points-on-path": ["points-on-path@0.2.1", "", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="], + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], "postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="], @@ -704,6 +936,8 @@ "readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], + "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], + "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="], "recma-jsx": ["recma-jsx@1.0.1", "", { "dependencies": { "acorn-jsx": "^5.0.0", "estree-util-to-js": "^2.0.0", "recma-parse": "^1.0.0", "recma-stringify": "^1.0.0", "unified": "^11.0.0" }, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w=="], @@ -720,10 +954,14 @@ "rehype": ["rehype@13.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "rehype-parse": "^9.0.0", "rehype-stringify": "^10.0.0", "unified": "^11.0.0" } }, "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A=="], + "rehype-class-names": ["rehype-class-names@2.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-classnames": "^3.0.0", "hast-util-select": "^6.0.0", "unified": "^11.0.4" } }, "sha512-jldCIiAEvXKdq8hqr5f5PzNdIDkvHC6zfKhwta9oRoMu7bn0W7qLES/JrrjBvr9rKz3nJ8x4vY1EWI+dhjHVZQ=="], + "rehype-expressive-code": ["rehype-expressive-code@0.41.6", "", { "dependencies": { "expressive-code": "^0.41.6" } }, "sha512-aBMX8kxPtjmDSFUdZlAWJkMvsQ4ZMASfee90JWIAV8tweltXLzkWC3q++43ToTelI8ac5iC0B3/S/Cl4Ql1y2g=="], "rehype-format": ["rehype-format@5.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-format": "^1.0.0" } }, "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ=="], + "rehype-katex": ["rehype-katex@7.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/katex": "^0.16.0", "hast-util-from-html-isomorphic": "^2.0.0", "hast-util-to-text": "^4.0.0", "katex": "^0.16.0", "unist-util-visit-parents": "^6.0.0", "vfile": "^6.0.0" } }, "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA=="], + "rehype-parse": ["rehype-parse@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" } }, "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag=="], "rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="], @@ -736,6 +974,8 @@ "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + "remark-math": ["remark-math@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-math": "^3.0.0", "micromark-extension-math": "^3.0.0", "unified": "^11.0.0" } }, "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA=="], + "remark-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="], "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], @@ -754,8 +994,16 @@ "retext-stringify": ["retext-stringify@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unified": "^11.0.0" } }, "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA=="], + "robust-predicates": ["robust-predicates@3.0.2", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="], + "rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="], + "roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="], + + "rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "sax": ["sax@1.4.4", "", {}, "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw=="], "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], @@ -776,6 +1024,8 @@ "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + "starlight-katex": ["starlight-katex@0.0.4", "", { "dependencies": { "astro-integration-kit": "^0.16.1", "import-meta-resolve": "^4.1.0", "katex": "^0.16.11", "rehype-class-names": "^2.0.0", "rehype-katex": "^7.0.1", "remark-math": "^6.0.0" }, "peerDependencies": { "@astrojs/starlight": ">=0.16.0", "astro": ">=4.0.0" } }, "sha512-bb2oR/K4+wK61tZvOTSo9/zH7FI/+guwGLGgIuJSld8vKmcxeAG1kmde8CfH0IKohXlSwUYE3YCR/r6qd0jIEA=="], + "stream-replace-string": ["stream-replace-string@2.0.0", "", {}, "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w=="], "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], @@ -788,10 +1038,14 @@ "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], + "stylis": ["stylis@4.3.6", "", {}, "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ=="], + "svgo": ["svgo@4.0.0", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.4.1" }, "bin": "./bin/svgo.js" }, "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw=="], "tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="], + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], @@ -800,6 +1054,8 @@ "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + "ts-dedent": ["ts-dedent@2.2.0", "", {}, "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="], + "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -842,6 +1098,8 @@ "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], "vfile-location": ["vfile-location@5.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg=="], @@ -852,6 +1110,18 @@ "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], + "vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="], + + "vscode-languageserver": ["vscode-languageserver@9.0.1", "", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="], + + "vscode-languageserver-protocol": ["vscode-languageserver-protocol@3.17.5", "", { "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" } }, "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg=="], + + "vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.12", "", {}, "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="], + + "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], + + "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], + "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], "which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="], @@ -886,10 +1156,26 @@ "csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="], + "cytoscape-fcose/cose-base": ["cose-base@2.2.0", "", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="], + + "d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], + + "d3-sankey/d3-array": ["d3-array@2.12.1", "", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="], + + "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], + "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], + + "mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -898,6 +1184,10 @@ "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], + "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="], + + "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], diff --git a/docs/package.json b/docs/package.json index cc3b958f..dd8e2335 100644 --- a/docs/package.json +++ b/docs/package.json @@ -11,7 +11,11 @@ }, "dependencies": { "@astrojs/starlight": "^0.37.6", + "@mermaid-js/layout-elk": "^0.2.0", "astro": "^5.6.1", - "sharp": "^0.34.2" + "astro-mermaid": "^1.3.1", + "mermaid": "^11.12.3", + "sharp": "^0.34.2", + "starlight-katex": "^0.0.4" } } \ No newline at end of file diff --git a/docs/src/content/docs/en/concepts/predicates.mdx b/docs/src/content/docs/en/concepts/predicates.mdx new file mode 100644 index 00000000..1de5f911 --- /dev/null +++ b/docs/src/content/docs/en/concepts/predicates.mdx @@ -0,0 +1,357 @@ +--- +title: Predicates +description: Tree predicates. +--- + +Predicates are functions that return a boolean value. They are used to filter nodes, simplifying code, and making it more readable. The predicates allows the developer to extract the desired information, in an efficient manner, without having deep knowledge of the underlying tree structure. They reduce the risk of bugs creeping in, as correct tree traversal code can be tricky to write. They can be combined to create complex structural queries. They are also efficient, even beating manual traversal code written by the author. + +> **Mental Model:** Always think of predicates from the *current evaluated node's* perspective (e.g., *"Is this node a descendant of X?"*, *"Does this node have children?"*). + +--- + +## Tree Concepts + +### Structure + +The following predicates evaluate the *structure* of a node. + +| Predicate | Arguments | Description | +| --------- | ------------ | ----------------------------------------------------------- | +| `Inner` | | Is an inner node (i.e., can have children, `height > 0`). | +| `Leaf` | | Is a leaf node (i.e., cannot have children, `height == 0`). | +| `Height` | `min`, `max` | Height is in `[min..max]`. | +| `Offset` | `min`, `max` | Offset is in `[min..max]`. | + +Below you can find a binary tree diagram to illustrate when the different predicates return true. + +```mermaid +--- +config: + layout: dagre + look: classic +--- +flowchart TB + %% Vertical column for labels + L3["Height 3 (Inner)"] ~~~ L2["Height 2 (Inner)"] + L2 ~~~ L1["Height 1 (Inner)"] + L1 ~~~ L0["Height 0 (Leaf)"] + + %% Tree Structure + A{{"`Root +Offset: 0`"}} --> B{ +Offset: 0} + A --> C{Offset: 1} + B --> D{Offset: 0} + B --> E[Offset: 1] + D --> F((Offset: 0)) + D --> G((Offset: 1)) + C --> H{Offset: 0} + C --> I[Offset: 1] + H --> J((Offset: 0)) + H --> K((Offset: 1)) + + %% Strip background/borders from labels + classDef textLabel fill:none,stroke:none,font-weight:bold,font-size:16px; + class L3,L2,L1,L0 textLabel; +``` + +### State + +The following predicates evaluate the *current state* of a node. + +| Predicate | Description | +| ------------- | ----------------------- | +| `HasChildren` | Has children. | +| `Childless` | Is childless. | +| `Exists` | Exists. | +| `Modified` | Is in a modified state. | + +### Relationships + +These predicates evaluate a node based on its position relative to a target node (`T`). + +| Predicate | Arguments | Description | +| -------------- | --------- | ------------------------------------------------- | +| `AncestorOf` | `query` | Is an ancestor of the given `query` node. | +| `SiblingOf` | `query` | Shares the same parent as the given `query` node. | +| `DescendantOf` | `query` | Is a descendant of the given `query` node. | + +--- + +## Spatial & Coordinates + +### Spatial + +These predicates evaluate the physical intersection between a node and a given geometry. + +
+ +| Predicate | Diagram | Arguments | Description | +| --- | :---: | :---: | --- | +| `Contains` | GN | `geometry` | `node` contains the `geometry`. | +| `Disjoint` | NG | `geometry` | `node` is disjoint from the `geometry`. | +| `Intersects` | NG | `geometry` | `node` intersects with the `geometry`. | +| `Inside` | NG | `geometry` | `node` is inside the `geometry`. | + +
+ +### Coordinates + +| Predicate | Arguments | Description | +| --------- | ------------ | -------------------------------------------------------- | +| `X` | `min`, `max` | Intersects along the x-axis with the range `[min, max]`. | +| `Y` | `min`, `max` | Intersects along the y-axis with the range `[min, max]`. | +| `Z` | `min`, `max` | Intersects along the z-axis with the range `[min, max]`. | +| `W` | `min`, `max` | Intersects along the w-axis with the range `[min, max]`. | + +--- + +## Combining Predicates + +### Logical +The following predicates are used to combine, negate, or compare other predicates. + +| Predicate | Arguments | Description | +| --- | --- | --- | +| `And` | $P_1, \dots, P_n$ | True if all predicates are true ($P_1 \land \dots \land P_n$). | +| `Or` | $P_1, \dots, P_n$ | True if at least one predicate is true ($P_1 \lor \dots \lor P_n$). | +| `IfThen` | $P, Q$ | True if $P$ is false or $Q$ is true ($P \rightarrow Q$). | +| `Iff` | $P, Q$ | True if both arguments are either true or false ($P \leftrightarrow Q$). | +| `Xor` | $P, Q$ | True if exactly one argument is true ($P \oplus Q$). | + +
+ +| P | Q | And
$P \land Q$ | Or
$P \lor Q$ | IfThen
$P \rightarrow Q$ | Iff
$P \leftrightarrow Q$ | Xor
$P \oplus Q$ | +| :---: | :---: | :---: | :---: | :---: | :---: | :---: | +| **T** | **T** | **T** | **T** | **T** | **T** | F | +| **T** | F | F | **T** | F | F | **T** | +| F | **T** | F | **T** | **T** | F | **T** | +| F | F | F | F | **T** | **T** | F | + +
+ +#### C++ Operator Overloads +In C++, you can use standard operators to make complex queries much more readable: + +| C++ Operator | Predicate | Equivalent Logic | +| :---: | --- | --- | +| `&&` | `And` | | +| `\|\|` | `Or` | | +| `!` | Negate | | +| `>>` | `IfThen` | | +| `^` | `Xor` | | +| *(None)* | `Iff` | `!(P ^ Q)` | + +**Example: Combining Predicates** +Using the operators, this verbose functional code: +```cpp +auto pred = Or(And(a, Xor(b, c), d), e, Iff(f, IfThen(g, h))) + +``` + +Can be written cleanly as: + +```cpp +auto pred = (a && (b ^ c) && d) || e || !(f ^ (g >> h)); + +``` + +**Example: Negating Predicates** +For predicates that check for ranges, it can be more efficient to negate the predicate instead of splitting it into two predicates. Below, the first predicate will be two separate checks per node, while the second predicate will be a single check per node. + +```cpp +// Instead of this: +auto pred = 3 > depth || depth > 5; + +// You can write this: +auto pred = !(3 < depth < 5); +``` + +> **⚠️ Note on C++ Range Syntax** +> In standard C++, `3 < depth < 5` evaluates incorrectly as `(3 < depth) < 5`. However, our library overrides these operators to build a safe expression chain, meaning you can write mathematical ranges exactly as they appear above! + +```cpp +auto pred = 3 > depth > 5; + +// Is equivalent to: +auto pred = (3 > depth) > 5; + +// Which results in: +auto pred = depth > 5; // Since there is only one predicate at play here +``` + +While: + +```cpp +auto pred = 3 < depth < 5; + +// Is equivalent to: +auto pred = 3 < depth && depth < 5; +``` + +### Boolean +These are the simplest predicates, used primarily for testing or forcing specific evaluation states. + +| Predicate | Arguments | Description | +| --- | --- | --- | +| `True` | | Always true. | +| `False` | | Never true. | +| `Bool` | `B` | True if the provided boolean `B` is true. | + +--- + +## Customizable Filters + +| Predicate | Arguments | Description | +| --- | --- | --- | +| `Satisfies` | `RetFun`, `IntFun` | Satisfies `RetFun` and all ancestors satisfy `IntFun`. | + +--- + +## Map-specific Predicates + +### Color + +These predicates are only available if the map has color, i.e., `ColorMap` is used. + +> **Note:** Color properties are evaluated using the [**Oklch** color space](https://bottosson.github.io/posts/oklab/). + +| Predicate | Arguments | Description | +| ------------- | ------------ | ---------------------------------------------------- | +| `HasColor` | | Has color. | +| `Lightness` | `min`, `max` | Lightness is in `[min, max]`. Valid range: `[0, 1]`. | +| `Chroma` | `min`, `max` | Chroma is in `[min, max]`. Valid range: `[0, 0.4]`. | +| `Hue` | `min`, `max` | Hue is in `[min, max]`. Valid range: `[0, 360]`. | +| `Alpha` | `min`, `max` | Alpha is in `[min, max]`. Valid range: `[0, 1]`. | +| `ColorWeight` | `min`, `max` | Color weight is in `[min, max]`. | + +### Confidence + +These predicates are only available if the map has confidence, i.e., `ConfidenceMap` or `SemanticMap` is used. + +| Predicate | Arguments | Description | +| ------------ | ------------ | ------------------------------ | +| `Confidence` | `min`, `max` | Confidence is in `[min, max]`. | + + +### Cost + +These predicates are only available if the map has cost, i.e., `CostMap` is used. + +| Predicate | Arguments | Description | +| --------- | ------------ | ------------------------ | +| `Cost` | `min`, `max` | Cost is in `[min, max]`. | + +### Count + +These predicates are only avilable if the map has count, i.e., `CountMap` is used. + +| Predicate | Arguments | Description | +| --------- | ------------ | ------------------------- | +| `Count` | `min`, `max` | Count is in `[min, max]`. | + +### Distance + +These predicates are only avilable if the map has distance, i.e., `DistanceMap` is used. + +| Predicate | Arguments | Description | +| ---------- | ------------ | ---------------------------- | +| `Distance` | `min`, `max` | Distance is in `[min, max]`. | + +### Dynamics + +These predicates are only avilable if the map has dynamics, i.e., `DynamicsMap` is used. + +| Predicate | Arguments | Description | +| ------------ | --------- | ------------------------------- | +| `Dynamic` | | Is dynamic. | +| `Static` | | Is static. | +| `HasDynamic` | | Itself or any child is dynamic. | +| `HasStatic` | | Itself or any child is static. | + +### Intensity + +These predicates are only avilable if the map has intensity, i.e., `IntensityMap` is used. + +| Predicate | Arguments | Description | +| ----------- | ------------ | ----------------------------- | +| `Intensity` | `min`, `max` | Intensity is in `[min, max]`. | + +### Label + +These predicates are only avilable if the map has labels, i.e., `LabelMap` or `SemanticMap` is used. + +| Predicate | Arguments | Description | +| --------- | --------- | ---------------------- | +| `Label` | `label` | Has the given `label`. | + +### Occupancy + +These predicates are only avilable if the map has occupancy, i.e., `OccupancyMap` is used. + +| Predicate | Arguments | Description | +| ----------------- | ------------ | ---------------------------------------------------- | +| `Free` | | Is free. | +| `Occupied` | | Is occupied. | +| `Unknown` | | Is unknown. | +| `Occupancy` | `min`, `max` | Occupancy is in `[min, max]`. Valid range: `[0, 1]`. | +| `OccupancyStates` | `states` | Occupancy is one of the given `states`. | +| `HasFree` | | Itself or any child is free. | +| `HasOccupied` | | Itself or any child is occupied. | +| `HasUnknown` | | Itself or any child is unknown. | + +### Reflection + +These predicates are only avilable if the map has reflection, i.e., `ReflectionMap` is used. + +| Predicate | Arguments | Description | +| ---------------- | ------------ | --------------------------------------------------------- | +| `Hits` | `min`, `max` | Hits is in `[min, max]`. | +| `Misses` | `min`, `max` | Misses is in `[min, max]`. | +| `Reflectiveness` | `min`, `max` | Reflectiveness is in `[min, max]`. Valid range: `[0, 1]`. | + +### Semantic + +These predicates are only avilable if the map has semantic, i.e., `SemanticMap` is used. + +| Predicate | Arguments | Description | +| ---------- | --------------------- | -------------------------------------------- | +| `Semantic` | `label`, `min`, `max` | Has `label` with confidence in `[min, max]`. | + +### Surfel + +These predicates are only avilable if the map has surfels, i.e., `SurfelMap` is used. + +| Predicate | Arguments | Description | +| ---------------- | ------------ | --------------------------------- | +| `SurfelRadius` | `min`, `max` | Surfel radius is in `[min, max]`. | +| `IsPlanar` | | Surfel is planar. | + +### Time + +These predicates are only avilable if the map has time, i.e., `TimeMap` is used. + +| Predicate | Arguments | Description | +| --------- | ------------ | ----------------------------- | +| `Time` | `min`, `max` | Timestamp is in `[min, max]`. | + + +## Examples + +```cpp +ufo::Map map; + +// Fill in map with data + +namespace up = ufo::pred; + +auto pred = up::childless && up::occupied && up::dynamic && up::Semantic("car", 0.7, 1.0); + +for (auto node : pred) { + // Node is a node that satisfies the predicate, so it is: + // - Childless + // - Occupied + // - Dynamic + // - Has label "car" with confidence in [0.7, 1.0] +} +``` diff --git a/docs/src/content/docs/en/guides/create_map_type.md b/docs/src/content/docs/en/guides/create_map_type.md new file mode 100644 index 00000000..b6aabf04 --- /dev/null +++ b/docs/src/content/docs/en/guides/create_map_type.md @@ -0,0 +1,6 @@ +--- +title: Create Your Own Map Type +description: A guide to creating your own map types. +--- + +# Create Your Own Map Type diff --git a/docs/src/content/docs/en/guides/create_predicate.md b/docs/src/content/docs/en/guides/create_predicate.md new file mode 100644 index 00000000..b55f770b --- /dev/null +++ b/docs/src/content/docs/en/guides/create_predicate.md @@ -0,0 +1,73 @@ +--- +title: Create Your Own Predicate +description: A guide to creating your own predicates. +--- + +# Create Your Own Predicate + +Need to specialize the following class with the mentioned functions: + +```cpp +template +struct MyPredicate { + // TODO: Add member variables here + float example; + int example2; +}; + +template +[[nodiscard]] constexpr MyPredicate operator!(MyPredicate const& p) noexcept +{ + return MyPredicate{p.example}; +} + +template +struct Filter> { + using Pred = MyPredicate; + + template + static constexpr void init(Pred& p, Tree const& t) noexcept + { + // TODO: Initialize member variables here. + // This functions runs once when the predicate is created. So you can do some heavy calculation here. + p.example2 = t.heavyCalc(p.example); + } + + template + [[nodiscard]] static constexpr bool returnableValue(Pred const& p, + Value const& v) noexcept + { + // TODO: Check if the value is returnable. + } + + template + [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, + typename Tree::Node const& n) noexcept + { + // TODO: Check if the node is returnable. + } + + template + [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, + typename Tree::Node const& n) noexcept + { + // TODO: Check if the node is traversable. + } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const& r) noexcept + { + // TODO: Check if the node is returnable by the ray. + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const& r) noexcept + { + // TODO: Check if the node is traversable by the ray. + } +}; +``` diff --git a/docs/src/content/docs/en/index.mdx b/docs/src/content/docs/en/index.mdx index 83f56d92..a04dfc5c 100644 --- a/docs/src/content/docs/en/index.mdx +++ b/docs/src/content/docs/en/index.mdx @@ -1,9 +1,9 @@ --- -title: Welcome to UFO -description: Get started building your robotics projects with UFO. +title: UFO [u´fo] +shortTitle: UFO template: splash # Remove or comment out this line to display the site sidebar on this page. hero: - tagline: Congrats on setting up a new UFO project! + tagline: Welcome to UFO! image: file: ../../../assets/ufo.png actions: diff --git a/docs/src/content/docs/en/start_here/getting_started.md b/docs/src/content/docs/en/start_here/getting_started.md index 7b81111a..850bf6d4 100644 --- a/docs/src/content/docs/en/start_here/getting_started.md +++ b/docs/src/content/docs/en/start_here/getting_started.md @@ -1,4 +1,81 @@ --- title: Getting Started description: Get familiar with UFO. ---- \ No newline at end of file +--- + +UFO (Unknown Free Occupied) is a high-performance 3D mapping framework that explicitly represents free, occupied, and unknown space. This guide will help you get started with the core concepts and basic usage. + +## Basic Concepts + +- **Spatial Containers**: UFO provides various tree-based structures like `OctreeMap`, `QuadtreeMap`, and `HextreeMap` to represent space at different resolutions and dimensions. +- **Probabilistic Representation**: Each cell in a map can store occupancy information, confidence, semantics, and more. +- **Efficient Queries**: The framework is optimized for fast spatial queries, such as nearest neighbor searches and intersections. + +## Basic Usage + +Here is a simple example of how to use an `OctreeMap` to store and query 3D points. + +### Code Example + +```cpp +#include +#include + +int main() { + // 1. Initialize an OctreeMap with 'int' as the data type + ufo::OctreeMap map; + + // 2. Insert data at specific 3D coordinates (x, y, z) + map.insert({{0.0, 0.0, 0.0}, 42}); + map.insert({{1.0, 2.0, 3.0}, 100}); + + // 3. Perform a nearest neighbor search + ufo::Vec3f query_point(0.1, 0.1, 0.1); + auto nearest = map.nearest(query_point); + + if (!nearest.empty()) { + auto [pos, value] = *nearest.begin(); + std::cout << "Nearest point at " << pos << " has value: " << value << std::endl; + } + + // 4. Iterate over all elements in the map + std::cout << "All elements in map:" << std::endl; + for (auto [pos, value] : map) { + std::cout << pos << ": " << value << std::endl; + } + + return 0; +} +``` + +## Building a Simple App + +To use UFO in your own project, you can find it via CMake. + +### CMakeLists.txt + +```cmake +cmake_minimum_required(VERSION 3.23) +project(MyUfoApp) + +# Set C++23 standard +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Find UFO package +find_package(UFO REQUIRED) + +# Add your executable +add_executable(my_app main.cpp) + +# Link against UFO +target_link_libraries(my_app PRIVATE UFO::UFO) +``` + +### Build Instructions + +```bash +cmake -B build +cmake --build build +./build/my_app +``` \ No newline at end of file diff --git a/docs/src/content/docs/en/start_here/installation.mdx b/docs/src/content/docs/en/start_here/installation.mdx index 50f068d3..3c52a6d2 100644 --- a/docs/src/content/docs/en/start_here/installation.mdx +++ b/docs/src/content/docs/en/start_here/installation.mdx @@ -5,12 +5,36 @@ description: Install UFO. import { Tabs, TabItem } from '@astrojs/starlight/components'; +## System Requirements + +UFO requires a modern C++ environment: + +- **CMake**: version 3.23 or newer. +- **C++ Standard**: C++23. + +### Supported Compilers + + + + - **GCC**: 14 or newer. + - **Clang**: 18 or newer. + - **Note**: Ensure [`oneTBB`](https://uxlfoundation.github.io/oneTBB/) or [`OpenMP`](https://www.openmp.org/) is installed for parallel processing. + + + - **LLVM/Clang**: 21 or newer (installed via Homebrew). + - **Note**: Apple Clang currently lacks full C++23 support. Use `brew install llvm@21`. + + + - **MSVC**: Visual Studio 2022 (version 17.10+) or newer. + + + ## Prerequisite - You need to have [`Git`](https://git-scm.com/), [`CMake`](https://cmake.org/), and a `C++` compiler installed. To use parallel processing, you may need to install [`oneTBB`](https://uxlfoundation.github.io/oneTBB/) or [`OpenMP`](https://www.openmp.org/) as well. - You need to have [`Git`](https://git-scm.com/), [`CMake`](https://cmake.org/), and a `C++` compiler installed. - You need to have [`Git`](https://git-scm.com/), [`CMake`](https://cmake.org/), and a `C++` compiler installed. + You need to have [`Git`](https://git-scm.com/) and [`CMake`](https://cmake.org/) installed along with a supported C++ compiler. + You need to have [`Git`](https://git-scm.com/) and [`CMake`](https://cmake.org/) installed along with a supported C++ compiler. + You need to have [`Git`](https://git-scm.com/) and [`CMake`](https://cmake.org/) installed along with a supported C++ compiler. ## Install diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index 0734a7cc..c78a148b 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -1,9 +1,10 @@ --- -title: Welcome to UFO +title: UFO [u´fo] +shortTitle: UFO description: Get started building your robotics projects with UFO. template: splash # Remove or comment out this line to display the site sidebar on this page. hero: - tagline: Congrats on setting up a new UFO project! + tagline: Welcome to UFO! image: file: ../../assets/ufo.png actions: diff --git a/docs/src/content/docs/sv/index.mdx b/docs/src/content/docs/sv/index.mdx index c47a1c4a..b38c6f13 100644 --- a/docs/src/content/docs/sv/index.mdx +++ b/docs/src/content/docs/sv/index.mdx @@ -1,9 +1,9 @@ --- -title: Välkommen till UFO -description: Kom igång med att bygga dina robotprojekt med UFO. +title: UFO [u´fo] +shortTitle: UFO template: splash # Remove or comment out this line to display the site sidebar on this page. hero: - tagline: Grattis till att ha startat ett nytt UFO-projekt! + tagline: Välkommen till UFO! image: file: ../../../assets/ufo.png actions: diff --git a/docs/src/styles/custom.css b/docs/src/styles/custom.css new file mode 100644 index 00000000..a369ee8b --- /dev/null +++ b/docs/src/styles/custom.css @@ -0,0 +1,21 @@ +/* Targets the 2nd column (Q) of the header and standard rows */ +.truth-table th:nth-child(2), +.truth-table td:nth-child(2) { + border-right: 2px solid var(--sl-color-hairline); + /* var(--sl-color-hairline) uses Starlight's built-in theme colors so it looks native in both dark and light modes! */ +} + +.katex-html svg { + height: inherit; +} + +/* Force Mermaid containers and their SVGs to be transparent */ +.mermaid, +.mermaid svg { + background-color: transparent !important; +} + +/* Vertically center the contents of table data cells */ +.middle-align-table td { + vertical-align: middle; +} \ No newline at end of file diff --git a/external/cli11/cli11.cmake b/external/cli11/cli11.cmake index 6283011d..2f790a22 100644 --- a/external/cli11/cli11.cmake +++ b/external/cli11/cli11.cmake @@ -4,6 +4,6 @@ FetchContent_Declare( cli11_proj GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git GIT_TAG bfffd37e1f804ca4fae1caae106935791696b6a9 # v2.6.1 - GIT_PROGRESS TRUE + GIT_PROGRESS FALSE ) FetchContent_MakeAvailable(cli11_proj) \ No newline at end of file diff --git a/external/doxygen/doxygen-awesome.cmake b/external/doxygen/doxygen-awesome.cmake new file mode 100644 index 00000000..0843b91b --- /dev/null +++ b/external/doxygen/doxygen-awesome.cmake @@ -0,0 +1,9 @@ +include(FetchContent) + +FetchContent_Declare( + doxygen_awesome_css + GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css.git + GIT_TAG v2.4.1 +) + +FetchContent_MakeAvailable(doxygen_awesome_css) diff --git a/external/find_dependencies.cmake b/external/find_dependencies.cmake index a687fd2a..0020dd8e 100644 --- a/external/find_dependencies.cmake +++ b/external/find_dependencies.cmake @@ -1,61 +1,55 @@ # Catch2 if(UFO_BUILD_TESTS) - if(USE_SYSTEM_CATCH2) + if(UFO_USE_SYSTEM_CATCH2) find_package(Catch2 CONFIG) if(TARGET Catch2::Catch2WithMain) message(STATUS "Using installed third-party library Catch2 ${Catch2_VERSION}") else() message(STATUS "Unable to find installed third-party library Catch2") - set(USE_SYSTEM_CATCH2 OFF) + set(UFO_USE_SYSTEM_CATCH2 OFF) endif() endif() - if(NOT USE_SYSTEM_CATCH2) + if(NOT UFO_USE_SYSTEM_CATCH2) message(STATUS "Fetching and building Catch2 from source") include("${CMAKE_CURRENT_LIST_DIR}/catch2/catch2.cmake") endif() endif() # CLI11 -if(USE_SYSTEM_CLI11) +if(UFO_USE_SYSTEM_CLI11) find_package(CLI11 CONFIG) if(TARGET CLI11::CLI11) message(STATUS "Using installed third-party library CLI11 ${CLI11_VERSION}") else() message(STATUS "Unable to find installed third-party library CLI11") - set(USE_SYSTEM_CLI11 OFF) + set(UFO_USE_SYSTEM_CLI11 OFF) endif() endif() -if(NOT USE_SYSTEM_CLI11) +if(NOT UFO_USE_SYSTEM_CLI11) message(STATUS "Fetching and building CLI11 from source") include("${CMAKE_CURRENT_LIST_DIR}/cli11/cli11.cmake") endif() # Doxygen if(UFO_BUILD_DOCS) - find_package(Doxygen REQUIRED dot) + find_package(Doxygen REQUIRED OPTIONAL_COMPONENTS dot) + include("${CMAKE_CURRENT_LIST_DIR}/doxygen/doxygen-awesome.cmake") endif() # ImGUI -# libjpeg-turbo +# # libjpeg-turbo +# message(STATUS "Fetching and building libjpeg-turbo from source") +# include("${CMAKE_CURRENT_LIST_DIR}/libjpeg_turbo/libjpeg_turbo.cmake") -# libspng -# if(USE_SYSTEM_SPNG) -# find_package(PkgConfig REQUIRED) -# pkg_check_modules(SPNG REQUIRED libspng) -# if(TARGET spng) -# message(STATUS "Using installed third-party library SPNG ${CLI11_VERSION}") -# else() -# message(STATUS "Unable to find installed third-party library SPNG") -# set(USE_SYSTEM_SPNG OFF) -# endif() -# endif() -# if(NOT USE_SYSTEM_SPNG) -# message(STATUS "Fetching and building CLI11 from source") -# include("${CMAKE_CURRENT_LIST_DIR}/libspng/libspng.cmake") -# endif() +# # libspng +# message(STATUS "Fetching and building libspng from source") +# include("${CMAKE_CURRENT_LIST_DIR}/libspng/libspng.cmake") -# Rply +# # Rply +# message(STATUS "Fetching and building rply from source") +# # include("${CMAKE_CURRENT_LIST_DIR}/rply/rply.cmake") +# add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/rply") # WGPU Native include("${CMAKE_CURRENT_LIST_DIR}/wgpu_native/wgpu_native.cmake") \ No newline at end of file diff --git a/external/imgui/CMakeLists.txt b/external/imgui/CMakeLists.txt index 63935e8a..9e2689b3 100755 --- a/external/imgui/CMakeLists.txt +++ b/external/imgui/CMakeLists.txt @@ -4,7 +4,7 @@ FetchContent_Declare( imgui GIT_REPOSITORY https://github.com/ocornut/imgui.git GIT_TAG 6d910d5487d11ca567b61c7824b0c78c569d62f0 # v1.92.5 - GIT_PROGRESS TRUE + GIT_PROGRESS FALSE ) FetchContent_MakeAvailable(imgui) diff --git a/external/libjpeg-turbo/LICENSE b/external/libjpeg_turbo/LICENSE similarity index 100% rename from external/libjpeg-turbo/LICENSE rename to external/libjpeg_turbo/LICENSE diff --git a/external/libjpeg-turbo/CMakeLists.txt b/external/libjpeg_turbo/libjpeg_turbo.cmake similarity index 100% rename from external/libjpeg-turbo/CMakeLists.txt rename to external/libjpeg_turbo/libjpeg_turbo.cmake diff --git a/external/libspng/libspng.cmake b/external/libspng/libspng.cmake index 6909356d..22aea330 100644 --- a/external/libspng/libspng.cmake +++ b/external/libspng/libspng.cmake @@ -4,7 +4,7 @@ FetchContent_Declare( libspng GIT_REPOSITORY https://github.com/randy408/libspng.git GIT_TAG fb768002d4288590083a476af628e51c3f1d47cd # v0.7.4 - GIT_PROGRESS TRUE + GIT_PROGRESS FALSE ) FetchContent_MakeAvailable(libspng) \ No newline at end of file diff --git a/external/rply/CMakeLists.txt b/external/rply/CMakeLists.txt index 524f5462..07c9027f 100644 --- a/external/rply/CMakeLists.txt +++ b/external/rply/CMakeLists.txt @@ -1,6 +1,17 @@ project(rply) -add_library(rply STATIC rply/rply.c rply/rply.h) +add_library(rply STATIC) + +target_sources(rply + PRIVATE + rply/rply.c + PUBLIC + FILE_SET HEADERS + BASE_DIRS + rply + FILES + rply/rply.h +) set_target_properties(rply PROPERTIES POSITION_INDEPENDENT_CODE ON diff --git a/lib/cloud/CMakeLists.txt b/lib/cloud/CMakeLists.txt index b1aa2c7e..47e2737a 100644 --- a/lib/cloud/CMakeLists.txt +++ b/lib/cloud/CMakeLists.txt @@ -6,6 +6,16 @@ include(GNUInstallDirs) add_library(Cloud INTERFACE) add_library(UFO::Cloud ALIAS Cloud) +target_sources(Cloud + INTERFACE + FILE_SET HEADERS + BASE_DIRS + include + FILES + include/ufo/cloud/cloud.hpp + include/ufo/cloud/point_cloud.hpp +) + target_link_libraries(Cloud INTERFACE UFO::Container @@ -17,19 +27,9 @@ if(UFO_BUILD_TESTS AND UFO_CLOUD_BUILD_TESTS) add_subdirectory(tests) endif() -target_include_directories(Cloud - INTERFACE - $ - $ -) - -install(TARGETS Cloud - EXPORT UFO-targets - COMPONENT Cloud - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) - -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ +install( + TARGETS Cloud + EXPORT UFOTargets + FILE_SET HEADERS COMPONENT Cloud - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) \ No newline at end of file diff --git a/lib/cloud/README.md b/lib/cloud/README.md deleted file mode 100644 index 5d07237b..00000000 --- a/lib/cloud/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# UFOCloud -The UFO cloud library diff --git a/lib/cloud/include/happly/happly.h b/lib/cloud/include/happly/happly.h deleted file mode 100644 index 31ab8162..00000000 --- a/lib/cloud/include/happly/happly.h +++ /dev/null @@ -1,2130 +0,0 @@ -#pragma once - -/* A header-only implementation of the .ply file format. - * https://github.com/nmwsharp/happly - * By Nicholas Sharp - nsharp@cs.cmu.edu - * - * Version 2, July 20, 2019 - */ - -/* -MIT License - -Copyright (c) 2018 Nick Sharp - -Permission 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: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE 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. -*/ - -// clang-format off -/* - - === Changelog === - - Significant changes to the file recorded here. - - - Version 5 (Aug 22, 2020) Minor: skip blank lines before properties in ASCII files - - Version 4 (Sep 11, 2019) Change internal list format to be flat. Other small perf fixes and cleanup. - - Version 3 (Aug 1, 2019) Add support for big endian and obj_info - - Version 2 (July 20, 2019) Catch exceptions by const reference. - - Version 1 (undated) Initial version. Unnamed changes before version numbering. - -*/ -// clang-format on - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// General namespace wrapping all Happly things. -namespace happly -{ - -// Enum specifying binary or ASCII filetypes. Binary can be little-endian -// (default) or big endian. -enum class DataFormat { ASCII, Binary, BinaryBigEndian }; - -// Type name strings -// clang-format off -template std::string typeName() { return "unknown"; } -template<> inline std::string typeName() { return "char"; } -template<> inline std::string typeName() { return "uchar"; } -template<> inline std::string typeName() { return "short"; } -template<> inline std::string typeName() { return "ushort"; } -template<> inline std::string typeName() { return "int"; } -template<> inline std::string typeName() { return "uint"; } -template<> inline std::string typeName() { return "float"; } -template<> inline std::string typeName() { return "double"; } - -// Template hackery that makes getProperty() and friends pretty while automatically picking up smaller types -namespace { - -// A pointer for the equivalent/smaller equivalent of a type (eg. when a double is requested a float works too, etc) -// long int is intentionally absent to avoid platform confusion -template struct TypeChain { bool hasChildType = false; typedef T type; }; -template <> struct TypeChain { bool hasChildType = true; typedef int32_t type; }; -template <> struct TypeChain { bool hasChildType = true; typedef int16_t type; }; -template <> struct TypeChain { bool hasChildType = true; typedef int8_t type; }; -template <> struct TypeChain { bool hasChildType = true; typedef uint32_t type; }; -template <> struct TypeChain { bool hasChildType = true; typedef uint16_t type; }; -template <> struct TypeChain { bool hasChildType = true; typedef uint8_t type; }; -template <> struct TypeChain { bool hasChildType = true; typedef float type; }; - -template struct CanonicalName { typedef T type; }; -template <> struct CanonicalName { typedef int8_t type; }; -template <> struct CanonicalName { typedef uint8_t type; }; -template <> struct CanonicalName { typedef std::conditional::type, int>::value, uint32_t, uint64_t>::type type; }; - -// Used to change behavior of >> for 8bit ints, which does not do what we want. -template struct SerializeType { typedef T type; }; -template <> struct SerializeType { typedef int32_t type; }; -template <> struct SerializeType< int8_t> { typedef int32_t type; }; - -// Give address only if types are same (used below when conditionally copying data) -// last int/char arg is to resolve ambiguous overloads, just always pass 0 and the int version will be preferred -template -S* addressIfSame(T&, char) { - throw std::runtime_error("tried to take address for types that are not same"); - return nullptr;} -template -S* addressIfSame(S& t, int) {return &t;} - -// clang-format on -} // namespace - -/** - * @brief A generic property, which is associated with some element. Can be plain Property - * or a ListProperty, of some type. Generally, the user should not need to interact with - * these directly, but they are exposed in case someone wants to get clever. - */ -class Property -{ - public: - /** - * @brief Create a new Property with the given name. - * - * @param name_ - */ - Property(std::string const& name_) : name(name_) {}; - virtual ~Property() {}; - - std::string name; - - /** - * @brief Reserve memory. - * - * @param capacity Expected number of elements. - */ - virtual void reserve(size_t capacity) = 0; - - /** - * @brief (ASCII reading) Parse out the next value of this property from a list of - * tokens. - * - * @param tokens The list of property tokens for the element. - * @param currEntry Index in to tokens, updated after this property is read. - */ - virtual void parseNext(std::vector const& tokens, size_t& currEntry) = 0; - - /** - * @brief (binary reading) Copy the next value of this property from a stream of bits. - * - * @param stream Stream to read from. - */ - virtual void readNext(std::istream& stream) = 0; - - /** - * @brief (binary reading) Copy the next value of this property from a stream of bits. - * - * @param stream Stream to read from. - */ - virtual void readNextBigEndian(std::istream& stream) = 0; - - /** - * @brief (reading) Write a header entry for this property. - * - * @param outStream Stream to write to. - */ - virtual void writeHeader(std::ostream& outStream) = 0; - - /** - * @brief (ASCII writing) write this property for some element to a stream in plaintext - * - * @param outStream Stream to write to. - * @param iElement index of the element to write. - */ - virtual void writeDataASCII(std::ostream& outStream, size_t iElement) = 0; - - /** - * @brief (binary writing) copy the bits of this property for some element to a stream - * - * @param outStream Stream to write to. - * @param iElement index of the element to write. - */ - virtual void writeDataBinary(std::ostream& outStream, size_t iElement) = 0; - - /** - * @brief (binary writing) copy the bits of this property for some element to a stream - * - * @param outStream Stream to write to. - * @param iElement index of the element to write. - */ - virtual void writeDataBinaryBigEndian(std::ostream& outStream, size_t iElement) = 0; - - /** - * @brief Number of element entries for this property - * - * @return - */ - virtual size_t size() = 0; - - /** - * @brief A string naming the type of the property - * - * @return - */ - virtual std::string propertyTypeName() = 0; -}; - -namespace -{ - -/** - * Check if the platform is little endian. - * (not foolproof, but will work on most platforms) - * - * @return true if little endian - */ -bool isLittleEndian() -{ - int32_t oneVal = 0x1; - char* numPtr = (char*)&oneVal; - return (numPtr[0] == 1); -} - -/** - * Swap endianness. - * - * @param value Value to swap. - * - * @return Swapped value. - */ -template -T swapEndian(T val) -{ - char* bytes = reinterpret_cast(&val); - for (unsigned int i = 0; i < sizeof(val) / 2; i++) { - std::swap(bytes[sizeof(val) - 1 - i], bytes[i]); - } - return val; -} - -// The following specializations for single-byte types are used to avoid compiler -// warnings. -template <> -int8_t swapEndian(int8_t val) -{ - return val; -} -template <> -uint8_t swapEndian(uint8_t val) -{ - return val; -} - -// Unpack flattened list from the convention used in TypedListProperty -template -std::vector> unflattenList(std::vector const& flatList, - std::vector const flatListStarts) -{ - size_t outerCount = flatListStarts.size() - 1; - - // Put the output here - std::vector> outLists(outerCount); - - if (outerCount == 0) { - return outLists; // quick out for empty - } - - // Copy each sublist - for (size_t iOuter = 0; iOuter < outerCount; iOuter++) { - size_t iFlatStart = flatListStarts[iOuter]; - size_t iFlatEnd = flatListStarts[iOuter + 1]; - outLists[iOuter].insert(outLists[iOuter].begin(), flatList.begin() + iFlatStart, - flatList.begin() + iFlatEnd); - } - - return outLists; -} - -}; // namespace - -/** - * @brief A property which takes a single value (not a list). - */ -template -class TypedProperty : public Property -{ - public: - /** - * @brief Create a new Property with the given name. - * - * @param name_ - */ - TypedProperty(std::string const& name_) : Property(name_) - { - if (typeName() == "unknown") { - // TODO should really be a compile-time error - throw std::runtime_error( - "Attempted property type does not match any type defined by the .ply format."); - } - }; - - /** - * @brief Create a new property and initialize with data. - * - * @param name_ - * @param data_ - */ - TypedProperty(std::string const& name_, std::vector const& data_) - : Property(name_), data(data_) - { - if (typeName() == "unknown") { - throw std::runtime_error( - "Attempted property type does not match any type defined by the .ply format."); - } - }; - - virtual ~TypedProperty() override {}; - - /** - * @brief Reserve memory. - * - * @param capacity Expected number of elements. - */ - virtual void reserve(size_t capacity) override { data.reserve(capacity); } - - /** - * @brief (ASCII reading) Parse out the next value of this property from a list of - * tokens. - * - * @param tokens The list of property tokens for the element. - * @param currEntry Index in to tokens, updated after this property is read. - */ - virtual void parseNext(std::vector const& tokens, - size_t& currEntry) override - { - data.emplace_back(); - std::istringstream iss(tokens[currEntry]); - typename SerializeType::type tmp; // usually the same type as T - iss >> tmp; - data.back() = tmp; - currEntry++; - }; - - /** - * @brief (binary reading) Copy the next value of this property from a stream of bits. - * - * @param stream Stream to read from. - */ - virtual void readNext(std::istream& stream) override - { - data.emplace_back(); - stream.read((char*)&data.back(), sizeof(T)); - } - - /** - * @brief (binary reading) Copy the next value of this property from a stream of bits. - * - * @param stream Stream to read from. - */ - virtual void readNextBigEndian(std::istream& stream) override - { - data.emplace_back(); - stream.read((char*)&data.back(), sizeof(T)); - data.back() = swapEndian(data.back()); - } - - /** - * @brief (reading) Write a header entry for this property. - * - * @param outStream Stream to write to. - */ - virtual void writeHeader(std::ostream& outStream) override - { - outStream << "property " << typeName() << " " << name << "\n"; - } - - /** - * @brief (ASCII writing) write this property for some element to a stream in plaintext - * - * @param outStream Stream to write to. - * @param iElement index of the element to write. - */ - virtual void writeDataASCII(std::ostream& outStream, size_t iElement) override - { - outStream.precision(std::numeric_limits::max_digits10); - outStream << static_cast::type>( - data[iElement]); // case is usually a no-op - } - - /** - * @brief (binary writing) copy the bits of this property for some element to a stream - * - * @param outStream Stream to write to. - * @param iElement index of the element to write. - */ - virtual void writeDataBinary(std::ostream& outStream, size_t iElement) override - { - outStream.write((char*)&data[iElement], sizeof(T)); - } - - /** - * @brief (binary writing) copy the bits of this property for some element to a stream - * - * @param outStream Stream to write to. - * @param iElement index of the element to write. - */ - virtual void writeDataBinaryBigEndian(std::ostream& outStream, size_t iElement) override - { - auto value = swapEndian(data[iElement]); - outStream.write((char*)&value, sizeof(T)); - } - - /** - * @brief Number of element entries for this property - * - * @return - */ - virtual size_t size() override { return data.size(); } - - /** - * @brief A string naming the type of the property - * - * @return - */ - virtual std::string propertyTypeName() override { return typeName(); } - - /** - * @brief The actual data contained in the property - */ - std::vector data; -}; - -/** - * @brief A property which is a list of value (eg, 3 doubles). Note that lists are always - * variable length per-element. - */ -template -class TypedListProperty : public Property -{ - public: - /** - * @brief Create a new Property with the given name. - * - * @param name_ - */ - TypedListProperty(std::string const& name_, int listCountBytes_) - : Property(name_), listCountBytes(listCountBytes_) - { - if (typeName() == "unknown") { - throw std::runtime_error( - "Attempted property type does not match any type defined by the .ply format."); - } - - flattenedIndexStart.push_back(0); - }; - - /** - * @brief Create a new property and initialize with data - * - * @param name_ - * @param data_ - */ - TypedListProperty(std::string const& name_, std::vector> const& data_) - : Property(name_) - { - if (typeName() == "unknown") { - throw std::runtime_error( - "Attempted property type does not match any type defined by the .ply format."); - } - - // Populate list with data - flattenedIndexStart.push_back(0); - for (std::vector const& vec : data_) { - for (T const& val : vec) { - flattenedData.emplace_back(val); - } - flattenedIndexStart.push_back(flattenedData.size()); - } - }; - - virtual ~TypedListProperty() override {}; - - /** - * @brief Reserve memory. - * - * @param capacity Expected number of elements. - */ - virtual void reserve(size_t capacity) override - { - flattenedData.reserve(3 * capacity); // optimize for triangle meshes - flattenedIndexStart.reserve(capacity + 1); - } - - /** - * @brief (ASCII reading) Parse out the next value of this property from a list of - * tokens. - * - * @param tokens The list of property tokens for the element. - * @param currEntry Index in to tokens, updated after this property is read. - */ - virtual void parseNext(std::vector const& tokens, - size_t& currEntry) override - { - std::istringstream iss(tokens[currEntry]); - size_t count; - iss >> count; - currEntry++; - - size_t currSize = flattenedData.size(); - size_t afterSize = currSize + count; - flattenedData.resize(afterSize); - for (size_t iFlat = currSize; iFlat < afterSize; iFlat++) { - std::istringstream iss(tokens[currEntry]); - typename SerializeType::type tmp; // usually the same type as T - iss >> tmp; - flattenedData[iFlat] = tmp; - currEntry++; - } - flattenedIndexStart.emplace_back(afterSize); - } - - /** - * @brief (binary reading) Copy the next value of this property from a stream of bits. - * - * @param stream Stream to read from. - */ - virtual void readNext(std::istream& stream) override - { - // Read the size of the list - size_t count = 0; - stream.read(((char*)&count), listCountBytes); - - // Read list elements - size_t currSize = flattenedData.size(); - size_t afterSize = currSize + count; - flattenedData.resize(afterSize); - if (count > 0) { - stream.read((char*)&flattenedData[currSize], count * sizeof(T)); - } - flattenedIndexStart.emplace_back(afterSize); - } - - /** - * @brief (binary reading) Copy the next value of this property from a stream of bits. - * - * @param stream Stream to read from. - */ - virtual void readNextBigEndian(std::istream& stream) override - { - // Read the size of the list - size_t count = 0; - stream.read(((char*)&count), listCountBytes); - if (listCountBytes == 8) { - count = (size_t)swapEndian((uint64_t)count); - } else if (listCountBytes == 4) { - count = (size_t)swapEndian((uint32_t)count); - } else if (listCountBytes == 2) { - count = (size_t)swapEndian((uint16_t)count); - } - - // Read list elements - size_t currSize = flattenedData.size(); - size_t afterSize = currSize + count; - flattenedData.resize(afterSize); - if (count > 0) { - stream.read((char*)&flattenedData[currSize], count * sizeof(T)); - } - flattenedIndexStart.emplace_back(afterSize); - - // Swap endian order of list elements - for (size_t iFlat = currSize; iFlat < afterSize; iFlat++) { - flattenedData[iFlat] = swapEndian(flattenedData[iFlat]); - } - } - - /** - * @brief (reading) Write a header entry for this property. Note that we already use - * "uchar" for the list count type. - * - * @param outStream Stream to write to. - */ - virtual void writeHeader(std::ostream& outStream) override - { - // NOTE: We ALWAYS use uchar as the list count output type - outStream << "property list uchar " << typeName() << " " << name << "\n"; - } - - /** - * @brief (ASCII writing) write this property for some element to a stream in plaintext - * - * @param outStream Stream to write to. - * @param iElement index of the element to write. - */ - virtual void writeDataASCII(std::ostream& outStream, size_t iElement) override - { - size_t dataStart = flattenedIndexStart[iElement]; - size_t dataEnd = flattenedIndexStart[iElement + 1]; - - // Get the number of list elements as a uchar, and ensure the value fits - size_t dataCount = dataEnd - dataStart; - if (dataCount > std::numeric_limits::max()) { - throw std::runtime_error( - "List property has an element with more entries than fit in a uchar. See note " - "in README."); - } - - outStream << dataCount; - outStream.precision(std::numeric_limits::max_digits10); - for (size_t iFlat = dataStart; iFlat < dataEnd; iFlat++) { - outStream << " " - << static_cast::type>( - flattenedData[iFlat]); // cast is usually a no-op - } - } - - /** - * @brief (binary writing) copy the bits of this property for some element to a stream - * - * @param outStream Stream to write to. - * @param iElement index of the element to write. - */ - virtual void writeDataBinary(std::ostream& outStream, size_t iElement) override - { - size_t dataStart = flattenedIndexStart[iElement]; - size_t dataEnd = flattenedIndexStart[iElement + 1]; - - // Get the number of list elements as a uchar, and ensure the value fits - size_t dataCount = dataEnd - dataStart; - if (dataCount > std::numeric_limits::max()) { - throw std::runtime_error( - "List property has an element with more entries than fit in a uchar. See note " - "in README."); - } - uint8_t count = static_cast(dataCount); - - outStream.write((char*)&count, sizeof(uint8_t)); - outStream.write((char*)&flattenedData[dataStart], count * sizeof(T)); - } - - /** - * @brief (binary writing) copy the bits of this property for some element to a stream - * - * @param outStream Stream to write to. - * @param iElement index of the element to write. - */ - virtual void writeDataBinaryBigEndian(std::ostream& outStream, size_t iElement) override - { - size_t dataStart = flattenedIndexStart[iElement]; - size_t dataEnd = flattenedIndexStart[iElement + 1]; - - // Get the number of list elements as a uchar, and ensure the value fits - size_t dataCount = dataEnd - dataStart; - if (dataCount > std::numeric_limits::max()) { - throw std::runtime_error( - "List property has an element with more entries than fit in a uchar. See note " - "in README."); - } - uint8_t count = static_cast(dataCount); - - outStream.write((char*)&count, sizeof(uint8_t)); - for (size_t iFlat = dataStart; iFlat < dataEnd; iFlat++) { - T value = swapEndian(flattenedData[iFlat]); - outStream.write((char*)&value, sizeof(T)); - } - } - - /** - * @brief Number of element entries for this property - * - * @return - */ - virtual size_t size() override { return flattenedIndexStart.size() - 1; } - - /** - * @brief A string naming the type of the property - * - * @return - */ - virtual std::string propertyTypeName() override { return typeName(); } - - /** - * @brief The (flattened) data for the property, as formed by concatenating all of the - * individual element lists together. - */ - std::vector flattenedData; - - /** - * @brief Indices in to flattenedData. The i'th element gives the index in to - * flattenedData where the element's data begins. A final entry is included which is the - * length of flattenedData. Size is N_elem + 1. - */ - std::vector flattenedIndexStart; - - /** - * @brief The number of bytes used to store the count for lists of data. - */ - int listCountBytes = -1; -}; - -/** - * @brief Helper function to construct a new property of the appropriate type. - * - * @param name The name of the property to construct. - * @param typeStr A string naming the type according to the format. - * @param isList Is this a plain property, or a list property? - * @param listCountTypeStr If a list property, the type of the count varible. - * - * @return A new Property with the proper type. - */ -inline std::unique_ptr createPropertyWithType( - std::string const& name, std::string const& typeStr, bool isList, - std::string const& listCountTypeStr) -{ - // == Figure out how many bytes the list count field has, if this is a list type - // Note: some files seem to use signed types here, we read the width but always parse as - // if unsigned - int listCountBytes = -1; - if (isList) { - if (listCountTypeStr == "uchar" || listCountTypeStr == "uint8" || - listCountTypeStr == "char" || listCountTypeStr == "int8") { - listCountBytes = 1; - } else if (listCountTypeStr == "ushort" || listCountTypeStr == "uint16" || - listCountTypeStr == "short" || listCountTypeStr == "int16") { - listCountBytes = 2; - } else if (listCountTypeStr == "uint" || listCountTypeStr == "uint32" || - listCountTypeStr == "int" || listCountTypeStr == "int32") { - listCountBytes = 4; - } else { - throw std::runtime_error("Unrecognized list count type: " + listCountTypeStr); - } - } - - // = Unsigned int - - // 8 bit unsigned - if (typeStr == "uchar" || typeStr == "uint8") { - if (isList) { - return std::unique_ptr( - new TypedListProperty(name, listCountBytes)); - } else { - return std::unique_ptr(new TypedProperty(name)); - } - } - - // 16 bit unsigned - else if (typeStr == "ushort" || typeStr == "uint16") { - if (isList) { - return std::unique_ptr( - new TypedListProperty(name, listCountBytes)); - } else { - return std::unique_ptr(new TypedProperty(name)); - } - } - - // 32 bit unsigned - else if (typeStr == "uint" || typeStr == "uint32") { - if (isList) { - return std::unique_ptr( - new TypedListProperty(name, listCountBytes)); - } else { - return std::unique_ptr(new TypedProperty(name)); - } - } - - // = Signed int - - // 8 bit signed - if (typeStr == "char" || typeStr == "int8") { - if (isList) { - return std::unique_ptr( - new TypedListProperty(name, listCountBytes)); - } else { - return std::unique_ptr(new TypedProperty(name)); - } - } - - // 16 bit signed - else if (typeStr == "short" || typeStr == "int16") { - if (isList) { - return std::unique_ptr( - new TypedListProperty(name, listCountBytes)); - } else { - return std::unique_ptr(new TypedProperty(name)); - } - } - - // 32 bit signed - else if (typeStr == "int" || typeStr == "int32") { - if (isList) { - return std::unique_ptr( - new TypedListProperty(name, listCountBytes)); - } else { - return std::unique_ptr(new TypedProperty(name)); - } - } - - // = Float - - // 32 bit float - else if (typeStr == "float" || typeStr == "float32") { - if (isList) { - return std::unique_ptr( - new TypedListProperty(name, listCountBytes)); - } else { - return std::unique_ptr(new TypedProperty(name)); - } - } - - // 64 bit float - else if (typeStr == "double" || typeStr == "float64") { - if (isList) { - return std::unique_ptr( - new TypedListProperty(name, listCountBytes)); - } else { - return std::unique_ptr(new TypedProperty(name)); - } - } - - else { - throw std::runtime_error("Data type: " + typeStr + - " cannot be mapped to .ply format"); - } -} - -/** - * @brief An element (more properly an element type) in the .ply object. Tracks the name - * of the elemnt type (eg, "vertices"), the number of elements of that type (eg, 1244), - * and any properties associated with that element (eg, "position", "color"). - */ -class Element -{ - public: - /** - * @brief Create a new element type. - * - * @param name_ Name of the element type (eg, "vertices") - * @param count_ Number of instances of this element. - */ - Element(std::string const& name_, size_t count_) : name(name_), count(count_) {} - - std::string name; - size_t count; - std::vector> properties; - - /** - * @brief Check if a property exists. - * - * @param target The name of the property to get. - * - * @return Whether the target property exists. - */ - bool hasProperty(std::string const& target) - { - for (std::unique_ptr& prop : properties) { - if (prop->name == target) { - return true; - } - } - return false; - } - - /** - * @brief Check if a property exists with the requested type. - * - * @tparam T The type of the property - * @param target The name of the property to get. - * - * @return Whether the target property exists. - */ - template - bool hasPropertyType(std::string const& target) - { - for (std::unique_ptr& prop : properties) { - if (prop->name == target) { - TypedProperty* castedProp = dynamic_cast*>(prop.get()); - if (castedProp) { - return true; - } - return false; - } - } - return false; - } - - /** - * @brief A list of the names of all properties - * - * @return Property names - */ - std::vector getPropertyNames() - { - std::vector names; - for (std::unique_ptr& p : properties) { - names.push_back(p->name); - } - return names; - } - - /** - * @brief Low-level method to get a pointer to a property. Users probably don't need to - * call this. - * - * @param target The name of the property to get. - * - * @return A (unique_ptr) pointer to the property. - */ - std::unique_ptr& getPropertyPtr(std::string const& target) - { - for (std::unique_ptr& prop : properties) { - if (prop->name == target) { - return prop; - } - } - throw std::runtime_error("PLY parser: element " + name + " does not have property " + - target); - } - - /** - * @brief Add a new (plain, not list) property for this element type. - * - * @tparam T The type of the property - * @param propertyName The name of the property - * @param data The data for the property. Must have the same length as the number of - * elements. - */ - template - void addProperty(std::string const& propertyName, std::vector const& data) - { - if (data.size() != count) { - throw std::runtime_error("PLY write: new property " + propertyName + - " has size which does not match element"); - } - - // If there is already some property with this name, remove it - for (size_t i = 0; i < properties.size(); i++) { - if (properties[i]->name == propertyName) { - properties.erase(properties.begin() + i); - i--; - } - } - - // Copy to canonical type. Often a no-op, but takes care of standardizing widths - // across platforms. - std::vector::type> canonicalVec(data.begin(), data.end()); - - properties.push_back(std::unique_ptr( - new TypedProperty::type>(propertyName, canonicalVec))); - } - - /** - * @brief Add a new list property for this element type. - * - * @tparam T The type of the property (eg, "double" for a list of doubles) - * @param propertyName The name of the property - * @param data The data for the property. Outer vector must have the same length as the - * number of elements. - */ - template - void addListProperty(std::string const& propertyName, - std::vector> const& data) - { - if (data.size() != count) { - throw std::runtime_error("PLY write: new property " + propertyName + - " has size which does not match element"); - } - - // If there is already some property with this name, remove it - for (size_t i = 0; i < properties.size(); i++) { - if (properties[i]->name == propertyName) { - properties.erase(properties.begin() + i); - i--; - } - } - - // Copy to canonical type. Often a no-op, but takes care of standardizing widths - // across platforms. - std::vector::type>> canonicalListVec; - for (std::vector const& subList : data) { - canonicalListVec.emplace_back(subList.begin(), subList.end()); - } - - properties.push_back( - std::unique_ptr(new TypedListProperty::type>( - propertyName, canonicalListVec))); - } - - /** - * @brief Get a vector of a data from a property for this element. Automatically - * promotes to larger types. Throws if requested data is unavailable. - * - * @tparam T The type of data requested - * @param propertyName The name of the property to get. - * - * @return The data. - */ - template - std::vector getProperty(std::string const& propertyName) - { - // Find the property - std::unique_ptr& prop = getPropertyPtr(propertyName); - - // Get a copy of the data with auto-promoting type magic - return getDataFromPropertyRecursive(prop.get()); - } - - /** - * @brief Get a vector of a data from a property for this element. Unlike getProperty(), - * only returns if the ply record contains a type that matches T exactly. Throws if * - * requested data is unavailable. - * - * @tparam T The type of data requested - * @param propertyName The name of the property to get. - * - * @return The data. - */ - template - std::vector getPropertyType(std::string const& propertyName) - { - // Find the property - std::unique_ptr& prop = getPropertyPtr(propertyName); - TypedProperty* castedProp = dynamic_cast*>(prop); - if (castedProp) { - return castedProp->data; - } - - // No match, failure - throw std::runtime_error("PLY parser: property " + prop->name + - " is not of type type " + typeName() + ". Has type " + - prop->propertyTypeName()); - } - - /** - * @brief Get a vector of lists of data from a property for this element. Automatically - * promotes to larger types. Throws if requested data is unavailable. - * - * @tparam T The type of data requested - * @param propertyName The name of the property to get. - * - * @return The data. - */ - template - std::vector> getListProperty(std::string const& propertyName) - { - // Find the property - std::unique_ptr& prop = getPropertyPtr(propertyName); - - // Get a copy of the data with auto-promoting type magic - return getDataFromListPropertyRecursive(prop.get()); - } - - /** - * @brief Get a vector of a data from a property for this element. Unlike getProperty(), - * only returns if the ply record contains a type that matches T exactly. Throws if * - * requested data is unavailable. - * - * @tparam T The type of data requested - * @param propertyName The name of the property to get. - * - * @return The data. - */ - template - std::vector> getListPropertyType(std::string const& propertyName) - { - // Find the property - std::unique_ptr& prop = getPropertyPtr(propertyName); - TypedListProperty* castedProp = dynamic_cast*>(prop); - if (castedProp) { - return unflattenList(castedProp->flattenedData, castedProp->flattenedIndexStart); - } - - // No match, failure - throw std::runtime_error("PLY parser: list property " + prop->name + - " is not of type " + typeName() + ". Has type " + - prop->propertyTypeName()); - } - - /** - * @brief Get a vector of lists of data from a property for this element. Automatically - * promotes to larger types. Unlike getListProperty(), this method will additionally - * convert between types of different sign (eg, requesting and int32 would get data from - * a uint32); doing so naively converts between signed and unsigned types. This is - * typically useful for data representing indices, which might be stored as signed or - * unsigned numbers. - * - * @tparam T The type of data requested - * @param propertyName The name of the property to get. - * - * @return The data. - */ - template - std::vector> getListPropertyAnySign(std::string const& propertyName) - { - // Find the property - std::unique_ptr& prop = getPropertyPtr(propertyName); - - // Get a copy of the data with auto-promoting type magic - try { - // First, try the usual approach, looking for a version of the property with the - // same signed-ness and possibly smaller size - return getDataFromListPropertyRecursive(prop.get()); - } catch (std::runtime_error const& orig_e) { - // If the usual approach fails, look for a version with opposite signed-ness - try { - // This type has the oppopsite signeness as the input type - typedef typename CanonicalName::type Tcan; - typedef typename std::conditional< - std::is_signed::value, typename std::make_unsigned::type, - typename std::make_signed::type>::type OppsignType; - - return getDataFromListPropertyRecursive(prop.get()); - - } catch (std::runtime_error const&) { - throw orig_e; - } - - throw orig_e; - } - } - - /** - * @brief Performs sanity checks on the element, throwing if any fail. - */ - void validate() - { - // Make sure no properties have duplicate names, and no names have whitespace - for (size_t iP = 0; iP < properties.size(); iP++) { - for (char c : properties[iP]->name) { - if (std::isspace(c)) { - throw std::runtime_error("Ply validate: illegal whitespace in name " + - properties[iP]->name); - } - } - for (size_t jP = iP + 1; jP < properties.size(); jP++) { - if (properties[iP]->name == properties[jP]->name) { - throw std::runtime_error("Ply validate: multiple properties with name " + - properties[iP]->name); - } - } - } - - // Make sure all properties have right length - for (size_t iP = 0; iP < properties.size(); iP++) { - if (properties[iP]->size() != count) { - throw std::runtime_error("Ply validate: property has wrong size. " + - properties[iP]->name + " does not match element size."); - } - } - } - - /** - * @brief Writes out this element's information to the file header. - * - * @param outStream The stream to use. - */ - void writeHeader(std::ostream& outStream) - { - outStream << "element " << name << " " << count << "\n"; - - for (std::unique_ptr& p : properties) { - p->writeHeader(outStream); - } - } - - /** - * @brief (ASCII writing) Writes out all of the data for every element of this element - * type to the stream, including all contained properties. - * - * @param outStream The stream to write to. - */ - void writeDataASCII(std::ostream& outStream) - { - // Question: what is the proper output for an element with no properties? Here, we - // write a blank line, so there is one line per element no matter what. - for (size_t iE = 0; iE < count; iE++) { - for (size_t iP = 0; iP < properties.size(); iP++) { - properties[iP]->writeDataASCII(outStream, iE); - if (iP < properties.size() - 1) { - outStream << " "; - } - } - outStream << "\n"; - } - } - - /** - * @brief (binary writing) Writes out all of the data for every element of this element - * type to the stream, including all contained properties. - * - * @param outStream The stream to write to. - */ - void writeDataBinary(std::ostream& outStream) - { - for (size_t iE = 0; iE < count; iE++) { - for (size_t iP = 0; iP < properties.size(); iP++) { - properties[iP]->writeDataBinary(outStream, iE); - } - } - } - - /** - * @brief (binary writing) Writes out all of the data for every element of this element - * type to the stream, including all contained properties. - * - * @param outStream The stream to write to. - */ - void writeDataBinaryBigEndian(std::ostream& outStream) - { - for (size_t iE = 0; iE < count; iE++) { - for (size_t iP = 0; iP < properties.size(); iP++) { - properties[iP]->writeDataBinaryBigEndian(outStream, iE); - } - } - } - - /** - * @brief Helper function which does the hard work to implement type promotion for data - * getters. Throws if type conversion fails. - * - * @tparam D The desired output type - * @tparam T The current attempt for the actual type of the property - * @param prop The property to get (does not delete nor share pointer) - * - * @return The data, with the requested type - */ - template - std::vector getDataFromPropertyRecursive(Property* prop) - { - typedef typename CanonicalName::type Tcan; - - { // Try to return data of type D from a property of type T - TypedProperty* castedProp = dynamic_cast*>(prop); - if (castedProp) { - // Succeeded, return a buffer of the data (copy while converting type) - std::vector castedVec; - castedVec.reserve(castedProp->data.size()); - for (Tcan& v : castedProp->data) { - castedVec.push_back(static_cast(v)); - } - return castedVec; - } - } - - TypeChain chainType; - if (chainType.hasChildType) { - return getDataFromPropertyRecursive::type>(prop); - } else { - // No smaller type to try, failure - throw std::runtime_error("PLY parser: property " + prop->name + - " cannot be coerced to requested type " + typeName() + - ". Has type " + prop->propertyTypeName()); - } - } - - /** - * @brief Helper function which does the hard work to implement type promotion for list - * data getters. Throws if type conversion fails. - * - * @tparam D The desired output type - * @tparam T The current attempt for the actual type of the property - * @param prop The property to get (does not delete nor share pointer) - * - * @return The data, with the requested type - */ - template - std::vector> getDataFromListPropertyRecursive(Property* prop) - { - typedef typename CanonicalName::type Tcan; - - TypedListProperty* castedProp = dynamic_cast*>(prop); - if (castedProp) { - // Succeeded, return a buffer of the data (copy while converting type) - - // Convert to flat buffer of new type - std::vector* castedFlatVec = nullptr; - std::vector - castedFlatVecCopy; // we _might_ make a copy here, depending on is_same below - - if (std::is_same, std::vector>::value) { - // just use the array we already have - castedFlatVec = addressIfSame>(castedProp->flattenedData, - 0 /* dummy arg to disambiguate */); - } else { - // make a copy - castedFlatVecCopy.reserve(castedProp->flattenedData.size()); - for (Tcan& v : castedProp->flattenedData) { - castedFlatVecCopy.push_back(static_cast(v)); - } - castedFlatVec = &castedFlatVecCopy; - } - - // Unflatten and return - return unflattenList(*castedFlatVec, castedProp->flattenedIndexStart); - } - - TypeChain chainType; - if (chainType.hasChildType) { - return getDataFromListPropertyRecursive::type>(prop); - } else { - // No smaller type to try, failure - throw std::runtime_error("PLY parser: list property " + prop->name + - " cannot be coerced to requested type list " + - typeName() + ". Has type list " + - prop->propertyTypeName()); - } - } -}; - -// Some string helpers -namespace -{ - -inline std::string trimSpaces(std::string const& input) -{ - size_t start = 0; - while (start < input.size() && input[start] == ' ') start++; - size_t end = input.size(); - while (end > start && - (input[end - 1] == ' ' || input[end - 1] == '\n' || input[end - 1] == '\r')) - end--; - return input.substr(start, end - start); -} - -inline std::vector tokenSplit(std::string const& input) -{ - std::vector result; - size_t curr = 0; - size_t found = 0; - while ((found = input.find_first_of(' ', curr)) != std::string::npos) { - std::string token = input.substr(curr, found - curr); - token = trimSpaces(token); - if (token.size() > 0) { - result.push_back(token); - } - curr = found + 1; - } - std::string token = input.substr(curr); - token = trimSpaces(token); - if (token.size() > 0) { - result.push_back(token); - } - - return result; -} - -inline bool startsWith(std::string const& input, std::string const& query) -{ - return input.compare(0, query.length(), query) == 0; -} -}; // namespace - -/** - * @brief Primary class; represents a set of data in the .ply format. - */ -class PLYData -{ - public: - /** - * @brief Create an empty PLYData object. - */ - PLYData() {}; - - /** - * @brief Initialize a PLYData by reading from a file. Throws if any failures occur. - * - * @param filename The file to read from. - * @param verbose If true, print useful info about the file to stdout - */ - PLYData(std::string const& filename, bool verbose = false) - { - using std::cout; - using std::endl; - using std::string; - using std::vector; - - if (verbose) cout << "PLY parser: Reading ply file: " << filename << endl; - - // Open a file in binary always, in case it turns out to have binary data. - std::ifstream inStream(filename, std::ios::binary); - if (inStream.fail()) { - throw std::runtime_error("PLY parser: Could not open file " + filename); - } - - parsePLY(inStream, verbose); - - if (verbose) { - cout << " - Finished parsing file." << endl; - } - } - - /** - * @brief Initialize a PLYData by reading from a stringstream. Throws if any failures - * occur. - * - * @param inStream The stringstream to read from. - * @param verbose If true, print useful info about the file to stdout - */ - PLYData(std::istream& inStream, bool verbose = false) - { - using std::cout; - using std::endl; - - if (verbose) cout << "PLY parser: Reading ply file from stream" << endl; - - parsePLY(inStream, verbose); - - if (verbose) { - cout << " - Finished parsing stream." << endl; - } - } - - /** - * @brief Perform sanity checks on the file, throwing if any fail. - */ - void validate() - { - for (size_t iE = 0; iE < elements.size(); iE++) { - for (char c : elements[iE].name) { - if (std::isspace(c)) { - throw std::runtime_error("Ply validate: illegal whitespace in element name " + - elements[iE].name); - } - } - for (size_t jE = iE + 1; jE < elements.size(); jE++) { - if (elements[iE].name == elements[jE].name) { - throw std::runtime_error("Ply validate: duplcate element name " + - elements[iE].name); - } - } - } - - // Do a quick validation sanity check - for (Element& e : elements) { - e.validate(); - } - } - - /** - * @brief Write this data to a .ply file. - * - * @param filename The file to write to. - * @param format The format to use (binary or ascii?) - */ - void write(std::string const& filename, DataFormat format = DataFormat::ASCII) - { - outputDataFormat = format; - - validate(); - - // Open stream for writing - std::ofstream outStream(filename, std::ios::out | std::ios::binary); - if (!outStream.good()) { - throw std::runtime_error("Ply writer: Could not open output file " + filename + - " for writing"); - } - - writePLY(outStream); - } - - /** - * @brief Write this data to an output stream - * - * @param outStream The output stream to write to. - * @param format The format to use (binary or ascii?) - */ - void write(std::ostream& outStream, DataFormat format = DataFormat::ASCII) - { - outputDataFormat = format; - - validate(); - - writePLY(outStream); - } - - /** - * @brief Get an element type by name ("vertices") - * - * @param target The name of the element type to get - * - * @return A reference to the element type. - */ - Element& getElement(std::string const& target) - { - for (Element& e : elements) { - if (e.name == target) return e; - } - throw std::runtime_error("PLY parser: no element with name: " + target); - } - - /** - * @brief Check if an element type exists - * - * @param target The name to check for. - * - * @return True if exists. - */ - bool hasElement(std::string const& target) - { - for (Element& e : elements) { - if (e.name == target) return true; - } - return false; - } - - /** - * @brief A list of the names of all elements - * - * @return Element names - */ - std::vector getElementNames() - { - std::vector names; - for (Element& e : elements) { - names.push_back(e.name); - } - return names; - } - - /** - * @brief Add a new element type to the object - * - * @param name The name of the new element type ("vertices"). - * @param count The number of elements of this type. - */ - void addElement(std::string const& name, size_t count) - { - elements.emplace_back(name, count); - } - - // === Common-case helpers - - /** - * @brief Common-case helper get mesh vertex positions - * - * @param vertexElementName The element name to use (default: "vertex") - * - * @return A vector of vertex positions. - */ - std::vector> getVertexPositions( - std::string const& vertexElementName = "vertex") - { - std::vector xPos = getElement(vertexElementName).getProperty("x"); - std::vector yPos = getElement(vertexElementName).getProperty("y"); - std::vector zPos = getElement(vertexElementName).getProperty("z"); - - std::vector> result(xPos.size()); - for (size_t i = 0; i < result.size(); i++) { - result[i][0] = xPos[i]; - result[i][1] = yPos[i]; - result[i][2] = zPos[i]; - } - - return result; - } - - /** - * @brief Common-case helper get mesh vertex colors - * - * @param vertexElementName The element name to use (default: "vertex") - * - * @return A vector of vertex colors (unsigned chars [0,255]). - */ - std::vector> getVertexColors( - std::string const& vertexElementName = "vertex") - { - std::vector r = - getElement(vertexElementName).getProperty("red"); - std::vector g = - getElement(vertexElementName).getProperty("green"); - std::vector b = - getElement(vertexElementName).getProperty("blue"); - - std::vector> result(r.size()); - for (size_t i = 0; i < result.size(); i++) { - result[i][0] = r[i]; - result[i][1] = g[i]; - result[i][2] = b[i]; - } - - return result; - } - - /** - * @brief Common-case helper to get face indices for a mesh. If not template type is - * given, size_t is used. Naively converts to requested signedness, which may lead to - * unexpected values if an unsigned type is used and file contains negative values. - * - * @return The indices into the vertex elements for each face. Usually 0-based, though - * there are no formal rules. - */ - template - std::vector> getFaceIndices() - { - for (std::string const& f : std::vector{"face"}) { - for (std::string const& p : - std::vector{"vertex_indices", "vertex_index"}) { - try { - return getElement(f).getListPropertyAnySign(p); - } catch (std::runtime_error const&) { - // that's fine - } - } - } - throw std::runtime_error( - "PLY parser: could not find face vertex indices attribute under any common " - "name."); - } - - /** - * @brief Common-case helper set mesh vertex positons. Creates vertex element, if - * necessary. - * - * @param vertexPositions A vector of vertex positions - */ - void addVertexPositions(std::vector>& vertexPositions) - { - std::string vertexName = "vertex"; - size_t N = vertexPositions.size(); - - // Create the element - if (!hasElement(vertexName)) { - addElement(vertexName, N); - } - - // De-interleave - std::vector xPos(N); - std::vector yPos(N); - std::vector zPos(N); - for (size_t i = 0; i < vertexPositions.size(); i++) { - xPos[i] = vertexPositions[i][0]; - yPos[i] = vertexPositions[i][1]; - zPos[i] = vertexPositions[i][2]; - } - - // Store - getElement(vertexName).addProperty("x", xPos); - getElement(vertexName).addProperty("y", yPos); - getElement(vertexName).addProperty("z", zPos); - } - - /** - * @brief Common-case helper set mesh vertex colors. Creates a vertex element, if - * necessary. - * - * @param colors A vector of vertex colors (unsigned chars [0,255]). - */ - void addVertexColors(std::vector>& colors) - { - std::string vertexName = "vertex"; - size_t N = colors.size(); - - // Create the element - if (!hasElement(vertexName)) { - addElement(vertexName, N); - } - - // De-interleave - std::vector r(N); - std::vector g(N); - std::vector b(N); - for (size_t i = 0; i < colors.size(); i++) { - r[i] = colors[i][0]; - g[i] = colors[i][1]; - b[i] = colors[i][2]; - } - - // Store - getElement(vertexName).addProperty("red", r); - getElement(vertexName).addProperty("green", g); - getElement(vertexName).addProperty("blue", b); - } - - /** - * @brief Common-case helper set mesh vertex colors. Creates a vertex element, if - * necessary. - * - * @param colors A vector of vertex colors as floating point [0,1] values. Internally - * converted to [0,255] chars. - */ - void addVertexColors(std::vector>& colors) - { - std::string vertexName = "vertex"; - size_t N = colors.size(); - - // Create the element - if (!hasElement(vertexName)) { - addElement(vertexName, N); - } - - auto toChar = [](double v) { - if (v < 0.0) v = 0.0; - if (v > 1.0) v = 1.0; - return static_cast(v * 255.); - }; - - // De-interleave - std::vector r(N); - std::vector g(N); - std::vector b(N); - for (size_t i = 0; i < colors.size(); i++) { - r[i] = toChar(colors[i][0]); - g[i] = toChar(colors[i][1]); - b[i] = toChar(colors[i][2]); - } - - // Store - getElement(vertexName).addProperty("red", r); - getElement(vertexName).addProperty("green", g); - getElement(vertexName).addProperty("blue", b); - } - - /** - * @brief Common-case helper to set face indices. Creates a face element if needed. The - * input type will be casted to a 32 bit integer of the same signedness. - * - * @param indices The indices into the vertex list around each face. - */ - template - void addFaceIndices(std::vector>& indices) - { - std::string faceName = "face"; - size_t N = indices.size(); - - // Create the element - if (!hasElement(faceName)) { - addElement(faceName, N); - } - - // Cast to 32 bit - typedef typename std::conditional::value, int32_t, uint32_t>::type - IndType; - std::vector> intInds; - for (std::vector& l : indices) { - std::vector thisInds; - for (T& val : l) { - IndType valConverted = static_cast(val); - if (valConverted != val) { - throw std::runtime_error("Index value " + std::to_string(val) + - " could not be converted to a .ply integer without " - "loss of data. Note that .ply " - "only supports 32-bit ints."); - } - thisInds.push_back(valConverted); - } - intInds.push_back(thisInds); - } - - // Store - getElement(faceName).addListProperty("vertex_indices", intInds); - } - - /** - * @brief Comments for the file. When writing, each entry will be written as a - * sequential comment line. - */ - std::vector comments; - - /** - * @brief obj_info comments for the file. When writing, each entry will be written as a - * sequential comment line. - */ - std::vector objInfoComments; - - private: - std::vector elements; - int const majorVersion = 1; // I'll buy you a drink if these ever get bumped - int const minorVersion = 0; - - DataFormat inputDataFormat = DataFormat::ASCII; // set when reading from a file - DataFormat outputDataFormat = DataFormat::ASCII; // option for writing files - - // === Reading === - - /** - * @brief Parse a PLY file from an input stream - * - * @param inStream - * @param verbose - */ - void parsePLY(std::istream& inStream, bool verbose) - { - // == Process the header - parseHeader(inStream, verbose); - - // === Parse data from a binary file - if (inputDataFormat == DataFormat::Binary) { - parseBinary(inStream, verbose); - } - // === Parse data from an binary file - else if (inputDataFormat == DataFormat::BinaryBigEndian) { - parseBinaryBigEndian(inStream, verbose); - } - // === Parse data from an ASCII file - else if (inputDataFormat == DataFormat::ASCII) { - parseASCII(inStream, verbose); - } - } - - /** - * @brief Read the header for a file - * - * @param inStream - * @param verbose - */ - void parseHeader(std::istream& inStream, bool verbose) - { - using std::cout; - using std::endl; - using std::string; - using std::vector; - - // First two lines are predetermined - { // First line is magic constant - string plyLine; - std::getline(inStream, plyLine); - if (trimSpaces(plyLine) != "ply") { - throw std::runtime_error( - "PLY parser: File does not appear to be ply file. First line should be " - "'ply'"); - } - } - - { // second line is version - string styleLine; - std::getline(inStream, styleLine); - vector tokens = tokenSplit(styleLine); - if (tokens.size() != 3) throw std::runtime_error("PLY parser: bad format line"); - std::string formatStr = tokens[0]; - std::string typeStr = tokens[1]; - std::string versionStr = tokens[2]; - - // "format" - if (formatStr != "format") throw std::runtime_error("PLY parser: bad format line"); - - // ascii/binary - if (typeStr == "ascii") { - inputDataFormat = DataFormat::ASCII; - if (verbose) cout << " - Type: ascii" << endl; - } else if (typeStr == "binary_little_endian") { - inputDataFormat = DataFormat::Binary; - if (verbose) cout << " - Type: binary" << endl; - } else if (typeStr == "binary_big_endian") { - inputDataFormat = DataFormat::BinaryBigEndian; - if (verbose) cout << " - Type: binary big endian" << endl; - } else { - throw std::runtime_error("PLY parser: bad format line"); - } - - // version - if (versionStr != "1.0") { - throw std::runtime_error( - "PLY parser: encountered file with version != 1.0. Don't know how to parse " - "that"); - } - if (verbose) cout << " - Version: " << versionStr << endl; - } - - // Consume header line by line - while (inStream.good()) { - string line; - std::getline(inStream, line); - - // Parse a comment - if (startsWith(line, "comment")) { - string comment = line.substr(8); - if (verbose) cout << " - Comment: " << comment << endl; - comments.push_back(comment); - continue; - } - - // Parse an obj_info comment - if (startsWith(line, "obj_info")) { - string infoComment = line.substr(9); - if (verbose) cout << " - obj_info: " << infoComment << endl; - objInfoComments.push_back(infoComment); - continue; - } - - // Parse an element - else if (startsWith(line, "element")) { - vector tokens = tokenSplit(line); - if (tokens.size() != 3) - throw std::runtime_error("PLY parser: Invalid element line"); - string name = tokens[1]; - size_t count; - std::istringstream iss(tokens[2]); - iss >> count; - elements.emplace_back(name, count); - if (verbose) - cout << " - Found element: " << name << " (count = " << count << ")" << endl; - continue; - } - - // Parse a property list - else if (startsWith(line, "property list")) { - vector tokens = tokenSplit(line); - if (tokens.size() != 5) - throw std::runtime_error("PLY parser: Invalid property list line"); - if (elements.size() == 0) - throw std::runtime_error( - "PLY parser: Found property list without previous element"); - string countType = tokens[2]; - string type = tokens[3]; - string name = tokens[4]; - elements.back().properties.push_back( - createPropertyWithType(name, type, true, countType)); - if (verbose) - cout << " - Found list property: " << name << " (count type = " << countType - << ", data type = " << type << ")" << endl; - continue; - } - - // Parse a property - else if (startsWith(line, "property")) { - vector tokens = tokenSplit(line); - if (tokens.size() != 3) - throw std::runtime_error("PLY parser: Invalid property line"); - if (elements.size() == 0) - throw std::runtime_error("PLY parser: Found property without previous element"); - string type = tokens[1]; - string name = tokens[2]; - elements.back().properties.push_back( - createPropertyWithType(name, type, false, "")); - if (verbose) - cout << " - Found property: " << name << " (type = " << type << ")" << endl; - continue; - } - - // Parse end of header - else if (startsWith(line, "end_header")) { - break; - } - - // Error! - else { - throw std::runtime_error("Unrecognized header line: " + line); - } - } - } - - /** - * @brief Read the actual data for a file, in ASCII - * - * @param inStream - * @param verbose - */ - void parseASCII(std::istream& inStream, bool verbose) - { - using std::string; - using std::vector; - - // Read all elements - for (Element& elem : elements) { - if (verbose) { - std::cout << " - Processing element: " << elem.name << std::endl; - } - - for (size_t iP = 0; iP < elem.properties.size(); iP++) { - elem.properties[iP]->reserve(elem.count); - } - for (size_t iEntry = 0; iEntry < elem.count; iEntry++) { - string line; - std::getline(inStream, line); - - // Some .ply files seem to include empty lines before the start of property data - // (though this is not specified in the format description). We attempt to recover - // and parse such files by skipping any empty lines. - if (!elem.properties.empty()) { // if the element has no properties, the line - // _should_ be blank, presumably - while (line.empty()) { // skip lines until we hit something nonempty - std::getline(inStream, line); - } - } - - vector tokens = tokenSplit(line); - size_t iTok = 0; - for (size_t iP = 0; iP < elem.properties.size(); iP++) { - elem.properties[iP]->parseNext(tokens, iTok); - } - } - } - } - - /** - * @brief Read the actual data for a file, in binary. - * - * @param inStream - * @param verbose - */ - void parseBinary(std::istream& inStream, bool verbose) - { - if (!isLittleEndian()) { - throw std::runtime_error("binary reading assumes little endian system"); - } - - using std::string; - using std::vector; - - // Read all elements - for (Element& elem : elements) { - if (verbose) { - std::cout << " - Processing element: " << elem.name << std::endl; - } - - for (size_t iP = 0; iP < elem.properties.size(); iP++) { - elem.properties[iP]->reserve(elem.count); - } - for (size_t iEntry = 0; iEntry < elem.count; iEntry++) { - for (size_t iP = 0; iP < elem.properties.size(); iP++) { - elem.properties[iP]->readNext(inStream); - } - } - } - } - - /** - * @brief Read the actual data for a file, in binary. - * - * @param inStream - * @param verbose - */ - void parseBinaryBigEndian(std::istream& inStream, bool verbose) - { - if (!isLittleEndian()) { - throw std::runtime_error("binary reading assumes little endian system"); - } - - using std::string; - using std::vector; - - // Read all elements - for (Element& elem : elements) { - if (verbose) { - std::cout << " - Processing element: " << elem.name << std::endl; - } - - for (size_t iP = 0; iP < elem.properties.size(); iP++) { - elem.properties[iP]->reserve(elem.count); - } - for (size_t iEntry = 0; iEntry < elem.count; iEntry++) { - for (size_t iP = 0; iP < elem.properties.size(); iP++) { - elem.properties[iP]->readNextBigEndian(inStream); - } - } - } - } - - // === Writing === - - /** - * @brief write a PLY file to an output stream - * - * @param outStream - */ - void writePLY(std::ostream& outStream) - { - writeHeader(outStream); - - // Write all elements - for (Element& e : elements) { - if (outputDataFormat == DataFormat::Binary) { - if (!isLittleEndian()) { - throw std::runtime_error("binary writing assumes little endian system"); - } - e.writeDataBinary(outStream); - } else if (outputDataFormat == DataFormat::BinaryBigEndian) { - if (!isLittleEndian()) { - throw std::runtime_error("binary writing assumes little endian system"); - } - e.writeDataBinaryBigEndian(outStream); - } else if (outputDataFormat == DataFormat::ASCII) { - e.writeDataASCII(outStream); - } - } - } - - /** - * @brief Write out a header for a file - * - * @param outStream - */ - void writeHeader(std::ostream& outStream) - { - // Magic line - outStream << "ply\n"; - - // Type line - outStream << "format "; - if (outputDataFormat == DataFormat::Binary) { - outStream << "binary_little_endian "; - } else if (outputDataFormat == DataFormat::BinaryBigEndian) { - outStream << "binary_big_endian "; - } else if (outputDataFormat == DataFormat::ASCII) { - outStream << "ascii "; - } - - // Version number - outStream << majorVersion << "." << minorVersion << "\n"; - - // Write comments - bool hasHapplyComment = false; - std::string happlyComment = - "Written with hapPLY (https://github.com/nmwsharp/happly)"; - for (std::string const& comment : comments) { - if (comment == happlyComment) hasHapplyComment = true; - outStream << "comment " << comment << "\n"; - } - if (!hasHapplyComment) { - outStream << "comment " << happlyComment << "\n"; - } - - // Write obj_info comments - for (std::string const& comment : objInfoComments) { - outStream << "obj_info " << comment << "\n"; - } - - // Write elements (and their properties) - for (Element& e : elements) { - e.writeHeader(outStream); - } - - // End header - outStream << "end_header\n"; - } -}; - -} // namespace happly diff --git a/lib/cloud/include/ufo/cloud/cloud.hpp b/lib/cloud/include/ufo/cloud/cloud.hpp index 9fa8e544..c088a273 100644 --- a/lib/cloud/include/ufo/cloud/cloud.hpp +++ b/lib/cloud/include/ufo/cloud/cloud.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/cloud/include/ufo/cloud/ply.hpp b/lib/cloud/include/ufo/cloud/ply.hpp deleted file mode 100644 index d99c2ece..00000000 --- a/lib/cloud/include/ufo/cloud/ply.hpp +++ /dev/null @@ -1,158 +0,0 @@ -/*! - * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown - * - * @author Daniel Duberg (dduberg@kth.se) - * @see https://github.com/UnknownFreeOccupied/ufomap - * @version 1.0 - * @date 2022-05-13 - * - * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * - * BSD 3-Clause License - * - * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. 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. - * - * 3. 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. - * - * THIS 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. - */ - -#ifndef UFO_CLOUD_PLY_HPP -#define UFO_CLOUD_PLY_HPP - -// UFO -#include -#include -#include - -// Happly -#include - -// STL -#include -#include -#include -#include - -namespace ufo -{ -namespace detail -{ -template -void readCloudPLY(happly::PLYData& ply, Cloud& cloud) -{ - cloud.clear(); - - std::size_t max_size{}; - - auto vertices = ply.getVertexPositions(); - - // std::vector> indices; - // if (ply.hasElement("face")) { - // auto& face = ply.getElement("face"); - // if (face.hasProperty("vertex_indices") || face.hasProperty("vertex_index")) { - // // FIXME: Maybe check type as well? - // indices = ply.getFaceIndices(); - // } - // } - - if constexpr (contains_type_v) { - auto& points = get(cloud); - - points.reserve(vertices.size()); - - for (auto const& v : vertices) { - points.emplace_back(v[0], v[1], v[2]); - } - - max_size = std::max(max_size, points.size()); - } - - if constexpr (contains_type_v) { - auto& points = get(cloud); - - points.reserve(vertices.size()); - - for (auto const& v : vertices) { - points.emplace_back(v[0], v[1], v[2]); - } - - max_size = std::max(max_size, points.size()); - } - - if constexpr (contains_type_v) { - auto& vertex = ply.getElement("vertex"); - if (vertex.hasProperty("red") && vertex.hasProperty("green") && - vertex.hasProperty("blue")) { - auto v_colors = ply.getVertexColors(); - - auto& colors = get(cloud); - - colors.reserve(v_colors.size()); - - for (auto const& c : v_colors) { - colors.emplace_back(c[0], c[1], c[2]); - } - - max_size = std::max(max_size, colors.size()); - } - } - - (get(cloud).resize(max_size), ...); -} -} // namespace detail - -template -void readCloudPLY(std::filesystem::path const& file, Cloud& cloud) -{ - happly::PLYData ply(file); - detail::readCloudPLY(ply, cloud); -} - -template -void readCloudPLY(std::istream& in, Cloud& cloud) -{ - happly::PLYData ply(in); - detail::readCloudPLY(ply, cloud); -} - -template -void writeCloudPLY(std::filesystem::path const& file, Cloud const& cloud) -{ - std::ofstream ofs; - ofs.exceptions(std::ofstream::failbit | std::ofstream::badbit); - ofs.imbue(std::locale()); - ofs.open(file, std::ios::out | std::ios::binary); - - writeCloudPLY(ofs, cloud); -} - -template -void writeCloudPLY(std::ostream& out, Cloud const& cloud) -{ -} -} // namespace ufo - -#endif // UFO_CLOUD_PLY_HPP \ No newline at end of file diff --git a/lib/cloud/include/ufo/cloud/point_cloud.hpp b/lib/cloud/include/ufo/cloud/point_cloud.hpp index 438101a2..e0df00d1 100644 --- a/lib/cloud/include/ufo/cloud/point_cloud.hpp +++ b/lib/cloud/include/ufo/cloud/point_cloud.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -43,38 +43,91 @@ #define UFO_CLOUD_POINT_CLOUD_HPP // UFO -// TODO: Add dependency on ufomath #include -#include -#include -// TODO: Add dependency on ufoutility #include +#include +#include // STL +#include #include #include -#include namespace ufo { +/** + * @brief A structure-of-arrays point cloud whose first channel is `Dim`-dimensional + * positions of scalar type `T`. + * + * `PointCloud` is an alias for `Cloud, Rest...>`, which stores per-point + * data in separate contiguous arrays (one per channel) for cache-friendly access. The + * position channel is always channel 0; any additional per-point attributes (colors, + * normals, intensities, ...) are appended as `Rest...`. + * + * @tparam Dim Spatial dimensionality (typically 2, 3, or 4). + * @tparam T Scalar type for position coordinates (e.g., `float`, `double`). + * @tparam Rest Additional per-point attribute types stored in separate SoA channels. + */ template using PointCloud = Cloud, Rest...>; +//! @brief 2D single-precision point cloud (positions only). +using PointCloud2f = PointCloud<2, float>; +//! @brief 3D single-precision point cloud (positions only). +using PointCloud3f = PointCloud<3, float>; +//! @brief 4D single-precision point cloud (positions only). +using PointCloud4f = PointCloud<4, float>; +//! @brief 2D double-precision point cloud (positions only). +using PointCloud2d = PointCloud<2, double>; +//! @brief 3D double-precision point cloud (positions only). +using PointCloud3d = PointCloud<3, double>; +//! @brief 4D double-precision point cloud (positions only). +using PointCloud4d = PointCloud<4, double>; + // // Transform // +/** + * @brief Applies a rigid transform to every point position and returns the result. + * + * A copy of `pc` is made and transformed; the original is left unchanged. + * Runs sequentially (equivalent to calling the overload with `execution::seq`). + * + * @tparam Dim Spatial dimensionality. + * @tparam T Scalar type. + * @tparam Rest Additional per-point attribute types (passed through unchanged). + * @param t The rigid transform (rotation + translation) to apply. + * @param pc The source point cloud (taken by value to allow move-from). + * @return A new point cloud with transformed positions and identical attributes. + */ template [[nodiscard]] PointCloud transform(Transform const& t, PointCloud pc) { - // TODO: Implement - return transform(execution::seq, t, pc); + transformInPlace(t, pc); + return pc; } -template < - class ExecutionPolicy, std::size_t Dim, class T, class... Rest, - std::enable_if_t, bool> = true> +/** + * @brief Applies a rigid transform to every point position and returns the result, + * using the given execution policy. + * + * A copy of `pc` is made, then `transformInPlace` is called on it. The original + * cloud is left unchanged. + * + * @tparam ExecutionPolicy An execution policy type (e.g., `execution::seq_t`, + * `execution::par_t`). + * @tparam Dim Spatial dimensionality. + * @tparam T Scalar type. + * @tparam Rest Additional per-point attribute types. + * @param policy Execution policy controlling parallelism. + * @param t The rigid transform to apply. + * @param pc The source point cloud (taken by value to allow move-from). + * @return A new point cloud with transformed positions. + */ +template + requires execution::is_execution_policy_v [[nodiscard]] PointCloud transform(ExecutionPolicy&& policy, Transform const& t, PointCloud pc) @@ -83,26 +136,77 @@ template < return pc; } +/** + * @brief Applies a rigid transform to every point position in-place. + * + * Only the position channel (channel 0) is modified; all other attribute channels + * are left unchanged. Runs sequentially. + * + * @tparam Dim Spatial dimensionality. + * @tparam T Scalar type. + * @tparam Rest Additional per-point attribute types. + * @param t The rigid transform to apply. + * @param pc The point cloud to transform in-place. + */ template void transformInPlace(Transform const& t, PointCloud& pc) { - // TODO: Implement - transformInPlace(execution::seq, t, pc); + auto v = view<0>(pc); + transform(v.begin(), v.end(), v.begin(), t); } -template < - class ExecutionPolicy, std::size_t Dim, class T, class... Rest, - std::enable_if_t, bool> = true> +/** + * @brief Applies a rigid transform to every point position in-place, using the given + * execution policy. + * + * Only the position channel (channel 0) is modified; all other attribute channels + * are left unchanged. + * + * @tparam ExecutionPolicy An execution policy type. + * @tparam Dim Spatial dimensionality. + * @tparam T Scalar type. + * @tparam Rest Additional per-point attribute types. + * @param policy Execution policy controlling parallelism. + * @param t The rigid transform to apply. + * @param pc The point cloud to transform in-place. + */ +template + requires execution::is_execution_policy_v void transformInPlace(ExecutionPolicy&& policy, Transform const& t, PointCloud& pc) { - transformInPlace(std::forward(policy), t, pc.template view<0>()); + auto v = view<0>(pc); + transform(std::forward(policy), v.begin(), v.end(), v.begin(), t); } // // Filter // +/** + * @brief Returns a copy of `pc` with all points outside the given distance range + * removed. + * + * Points are kept if and only if: + * @code + * min_distance <= distance(origin, point) <= max_distance + * @endcode + * Distance comparisons are performed on squared distances to avoid `sqrt`. If + * `filter_nan` is `true`, any point with a NaN coordinate is also removed. + * + * @tparam Dim Spatial dimensionality. + * @tparam T Scalar type. + * @tparam Rest Additional per-point attribute types (filtered together with positions). + * @param pc The source point cloud (taken by value to allow move-from). + * @param origin The reference point from which distances are measured. + * @param min_distance Minimum allowed distance (inclusive). Use `T(0)` for no lower + * bound. + * @param max_distance Maximum allowed distance (inclusive). Use + * `std::numeric_limits::max()` for no upper bound. + * @param filter_nan If `true`, points with any NaN coordinate are removed. + * Defaults to `true`. + * @return A new point cloud containing only points within the distance range. + */ template [[nodiscard]] PointCloud filterDistance(PointCloud pc, Vec const& origin, @@ -114,6 +218,34 @@ template return pc; } +/** + * @brief Removes in-place all points from `pc` that fall outside the given distance + * range. + * + * Points are kept if and only if: + * @code + * min_distance <= distance(origin, point) <= max_distance + * @endcode + * Comparisons are done on squared distances to avoid `sqrt`. If `filter_nan` is + * `true`, any point with a NaN coordinate is also erased. + * + * If the parameters describe a no-op filter (min_distance <= 0, + * max_distance >= `std::numeric_limits::max()`, and filter_nan is `false`), the + * function returns immediately without touching the cloud. + * + * All per-point attribute channels (positions and `Rest...`) are erased together, + * preserving the SoA layout invariant. + * + * @tparam Dim Spatial dimensionality. + * @tparam T Scalar type. + * @tparam Rest Additional per-point attribute types. + * @param pc The point cloud to filter in-place. + * @param origin The reference point from which distances are measured. + * @param min_distance Minimum allowed distance (inclusive). + * @param max_distance Maximum allowed distance (inclusive). + * @param filter_nan If `true`, points with any NaN coordinate are removed. + * Defaults to `true`. + */ template void filterDistanceInPlace(PointCloud& pc, Vec const& origin, T const& min_distance, T const& max_distance, @@ -127,22 +259,13 @@ void filterDistanceInPlace(PointCloud& pc, Vec const& o auto const min_sq = min_distance * min_distance; auto const max_sq = max_distance * max_distance; - if (filter_nan) { - auto it = std::remove_if(pc.begin(), pc.end(), - [&origin, min_sq, max_sq](Vec const& x) { - auto dist_sq = distanceSquared(origin, x); - return min_sq > dist_sq || max_sq < dist_sq || isnan(x); - }); - pc.erase(it, pc.end()); - } else { - auto it = std::remove_if(pc.begin(), pc.end(), - [&origin, min_sq, max_sq](Vec const& x) { - auto dist_sq = distanceSquared(origin, x); - return min_sq > dist_sq || max_sq < dist_sq; - }); - pc.erase(it, pc.end()); - } + auto to_erase = std::ranges::remove_if( + pc, [&origin, min_sq, max_sq, filter_nan](Vec const& x) { + auto dist_sq = distanceSquared(origin, x); + return min_sq > dist_sq || max_sq < dist_sq || (filter_nan && isnan(x)); + }); + pc.erase(to_erase.begin(), to_erase.end()); } } // namespace ufo -#endif // UFO_CLOUD_POINT_CLOUD_HPP \ No newline at end of file +#endif // UFO_CLOUD_POINT_CLOUD_HPP diff --git a/lib/cloud/include/ufo/cloud/ufo.hpp b/lib/cloud/include/ufo/cloud/ufo.hpp deleted file mode 100644 index 4c38e72d..00000000 --- a/lib/cloud/include/ufo/cloud/ufo.hpp +++ /dev/null @@ -1,347 +0,0 @@ -/*! - * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown - * - * @author Daniel Duberg (dduberg@kth.se) - * @see https://github.com/UnknownFreeOccupied/ufomap - * @version 1.0 - * @date 2022-05-13 - * - * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * - * BSD 3-Clause License - * - * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. 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. - * - * 3. 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. - * - * THIS 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. - */ - -#ifndef UFO_CLOUD_UFO_HPP -#define UFO_CLOUD_UFO_HPP - -// UFO -#include -#include -#include -#include -#include - -// STL -#include -#include -#include -#include - -namespace ufo -{ -namespace detail -{ -// template < -// class ExecutionPolicy, class... T, -// std::enable_if_t, bool> = -// true> -// void readCloudUFO(ExecutionPolicy&& policy, happly::UFOData& ply, Cloud& cloud) -// { -// cloud.clear(); - -// std::size_t max_size{}; - -// auto vertices = ply.getVertexPositions(); - -// // std::vector> indices; -// // if (ply.hasElement("face")) { -// // auto& face = ply.getElement("face"); -// // if (face.hasProperty("vertex_indices") || face.hasProperty("vertex_index")) { -// // // FIXME: Maybe check type as well? -// // indices = ply.getFaceIndices(); -// // } -// // } - -// if constexpr (contains_type_v) { -// auto& points = get(cloud); - -// if constexpr (std::is_same_v>) { -// points.reserve(vertices.size()); - -// for (auto const& v : vertices) { -// points.emplace_back(v[0], v[1], v[2]); -// } -// } else { -// points.resize(vertices.size()); - -// std::transform(policy, vertices.begin(), vertices.end(), points.begin(), -// [](auto const& v) { return Vec3f(v[0], v[1], v[2]); }); -// } - -// max_size = std::max(max_size, points.size()); -// } - -// if constexpr (contains_type_v) { -// auto& points = get(cloud); - -// if constexpr (std::is_same_v>) { -// points.reserve(vertices.size()); - -// for (auto const& v : vertices) { -// points.emplace_back(v[0], v[1], v[2]); -// } -// } else { -// points.resize(vertices.size()); - -// std::transform(policy, vertices.begin(), vertices.end(), points.begin(), -// [](auto const& v) { return Vec3d(v[0], v[1], v[2]); }); -// } - -// max_size = std::max(max_size, points.size()); -// } - -// if constexpr (contains_type_v) { -// auto& vertex = ply.getElement("vertex"); -// if (vertex.hasProperty("red") && vertex.hasProperty("green") && -// vertex.hasProperty("blue")) { -// auto v_colors = ply.getVertexColors(); - -// auto& colors = get(cloud); - -// if constexpr (std::is_same_v>) { -// colors.reserve(v_colors.size()); - -// for (auto const& c : v_colors) { -// colors.emplace_back(c[0], c[1], c[2]); -// } -// } else { -// colors.resize(v_colors.size()); - -// std::transform(policy, v_colors.begin(), v_colors.end(), colors.begin(), -// [](auto const& c) { return Color(c[0], c[1], c[2]); }); -// } - -// max_size = std::max(max_size, colors.size()); -// } -// } - -// (get(cloud).resize(max_size), ...); -// } - -template -void writeCloudUFO(std::ostream& out, Cloud const& cloud) -{ - auto data = cloud.template view(); - - out << data.size() << ' ' << sizeof(T) << ' '; - - if constexpr (std::is_same_v) { - out << "vec3f"; - } else if constexpr (std::is_same_v) { - out << "color"; - } else { - out << "unknown"; - } - out << '\n'; - - out.write(reinterpret_cast(data.data()), data.size() * sizeof(T)); -} -} // namespace detail - -template -void readCloudUFO(std::filesystem::path const& file, Cloud& cloud) -{ - std::ifstream ifs; - ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); - ifs.imbue(std::locale()); - ifs.open(file, std::ios::in | std::ios::binary); - - readCloudUFO(ifs, cloud); - - ifs.close(); -} - -template -void readCloudUFO(std::istream& in, Cloud& cloud) -{ - std::string line; - std::getline(in, line); - if ("# UFO cloud file" != line) { - return; - } - - std::size_t num_elements; - std::size_t element_size; - std::string type; - - std::size_t max_num_elements{}; - - while (std::getline(in, line) && in.good()) { - std::stringstream ss(line); - ss >> num_elements >> element_size >> type; - - // TODO: Add checks that cloud contains the data fields - - if ("vec3f" == type) { - max_num_elements = std::max(max_num_elements, num_elements); - - auto data = cloud.template view(); - data.resize(num_elements); - in.read(reinterpret_cast(data.data()), num_elements * element_size); - } else if ("color" == type) { - max_num_elements = std::max(max_num_elements, num_elements); - - auto data = cloud.template view(); - data.resize(num_elements); - in.read(reinterpret_cast(data.data()), num_elements * element_size); - - // TODO: Remove - // for (auto& c : get(cloud)) { - // std::swap(c.alpha, c.red); - // std::swap(c.green, c.blue); - // } - } else { - in.seekg(num_elements * element_size, std::ios::cur); - } - } - - cloud.reisze(max_num_elements); -} - -template -void readCloudUFO(std::filesystem::path const& file, Cloud& cloud, - Transform& pose) -{ - std::ifstream ifs; - ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); - ifs.imbue(std::locale()); - ifs.open(file, std::ios::in | std::ios::binary); - - readCloudUFO(ifs, cloud, pose); - - ifs.close(); -} - -template -void readCloudUFO(std::istream& in, Cloud& cloud, Transform& pose) -{ - std::string line; - std::getline(in, line); - if ("# UFO cloud file" != line) { - return; - } - - std::size_t num_elements; - std::size_t element_size; - std::string type; - - std::size_t max_num_elements{}; - - while (std::getline(in, line) && in.good()) { - std::stringstream ss(line); - ss >> num_elements >> element_size >> type; - - // TODO: Add checks that cloud contains the data fields - - if ("pose" == type) { - in.read(reinterpret_cast(&pose), num_elements * element_size); - } else if ("vec3f" == type) { - max_num_elements = std::max(max_num_elements, num_elements); - - auto data = cloud.template view(); - data.resize(num_elements); - in.read(reinterpret_cast(data.data()), num_elements * element_size); - } else if ("color" == type) { - max_num_elements = std::max(max_num_elements, num_elements); - - auto data = cloud.template view(); - data.resize(num_elements); - in.read(reinterpret_cast(data.data()), num_elements * element_size); - - // TODO: Remove - // for (auto& c : get(cloud)) { - // std::swap(c.alpha, c.red); - // std::swap(c.green, c.blue); - // } - } else { - in.seekg(num_elements * element_size, std::ios::cur); - } - } - - cloud.resize(max_num_elements); -} - -template -void writeCloudUFO(std::filesystem::path const& file, Cloud const& cloud) -{ - std::ofstream ofs; - ofs.exceptions(std::ofstream::failbit | std::ofstream::badbit); - ofs.imbue(std::locale()); - ofs.open(file, std::ios::out | std::ios::binary); - - writeCloudUFO(ofs, cloud); - - ofs.close(); -} - -template -void writeCloudUFO(std::ostream& out, Cloud const& cloud) -{ - out << "# UFO cloud file\n"; - - (detail::writeCloudUFO(out, cloud), ...); - - out << "# end"; -} - -template -void writeCloudUFO(std::filesystem::path const& file, Cloud const& cloud, - Transform const& pose) -{ - std::ofstream ofs; - ofs.exceptions(std::ofstream::failbit | std::ofstream::badbit); - ofs.imbue(std::locale()); - ofs.open(file, std::ios::out | std::ios::binary); - - writeCloudUFO(ofs, cloud, pose); - - ofs.close(); -} - -template -void writeCloudUFO(std::ostream& out, Cloud const& cloud, - Transform const& pose) -{ - out << "# UFO cloud file\n"; - - out << "1 " << sizeof(pose) << " pose\n"; - out.write(reinterpret_cast(&pose), sizeof(pose)); - - (detail::writeCloudUFO(out, cloud), ...); - - out << "# end"; -} -} // namespace ufo - -#endif // UFO_CLOUD_UFO_HPP \ No newline at end of file diff --git a/lib/cloud/tests/CMakeLists.txt b/lib/cloud/tests/CMakeLists.txt index 990b814e..8442fe48 100644 --- a/lib/cloud/tests/CMakeLists.txt +++ b/lib/cloud/tests/CMakeLists.txt @@ -9,7 +9,7 @@ set_target_properties(CloudTests PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} - CXX_STANDARD 17 + CXX_STANDARD 23 CXX_EXTENSIONS OFF ) diff --git a/lib/compute/CMakeLists.txt b/lib/compute/CMakeLists.txt index 27b5255e..601ef7a7 100644 --- a/lib/compute/CMakeLists.txt +++ b/lib/compute/CMakeLists.txt @@ -1,34 +1,29 @@ -option(UFO_COMPUTE_BUILD_TESTS "Unit testing" ON) -option(UFO_COMPUTE_BUILD_COVERAGE "Test Coverage" ON) - -include(GNUInstallDirs) - -set(SRC_FILES - src/compute.cpp -) +add_library(Compute STATIC) +add_library(UFO::Compute ALIAS Compute) -set(INC_FILES - include/ufo/compute/compute.hpp +target_sources(Compute + PRIVATE + src/compute.cpp + PUBLIC + FILE_SET HEADERS + BASE_DIRS + include + FILES + include/ufo/compute/compute.hpp ) -add_library(Compute STATIC ${SRC_FILES} ${INC_FILES}) -add_library(UFO::Compute ALIAS Compute) - set_target_properties(Compute PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} - CXX_STANDARD 17 + CXX_STANDARD 23 CXX_EXTENSIONS OFF + CXX_SCAN_FOR_MODULES OFF OUTPUT_NAME "UFOCompute" POSITION_INDEPENDENT_CODE ON ) -if (MSVC) - target_compile_options(Compute PRIVATE /W4) -else() - target_compile_options(Compute PRIVATE -Wall -Wextra -Wpedantic) -endif() +target_link_libraries(Compute PRIVATE $) target_link_libraries(Compute PUBLIC @@ -38,23 +33,16 @@ target_link_libraries(Compute target_include_directories(Compute PUBLIC - $ $ - $ ) -if(UFO_BUILD_TESTS AND UFO_COMPUTE_BUILD_TESTS) +if(UFO_BUILD_TESTS) add_subdirectory(tests) endif() - -# We add this target to the shared 'UFO-targets' export set -install(TARGETS Compute - EXPORT UFO-targets - COMPONENT Compute -) - -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ +install( + TARGETS Compute + EXPORT UFOTargets + FILE_SET HEADERS COMPONENT Compute - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) \ No newline at end of file diff --git a/lib/compute/README.md b/lib/compute/README.md deleted file mode 100644 index 7d8e5e27..00000000 --- a/lib/compute/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# ufocompute -The UFO compute library diff --git a/lib/compute/include/ufo/compute/compute.hpp b/lib/compute/include/ufo/compute/compute.hpp index 6efb04b4..24b42a87 100755 --- a/lib/compute/include/ufo/compute/compute.hpp +++ b/lib/compute/include/ufo/compute/compute.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -46,6 +46,8 @@ // STL #include +#include +#include // WebGPU #include @@ -726,6 +728,15 @@ namespace ufo::compute { +struct GPUInfo { + std::string name; + std::string architecture; + std::string vendor; + std::string type; + std::string backend; + std::string description; +}; + [[nodiscard]] WGPUInstance createInstance( WGPUInstanceDescriptor const* descriptor = nullptr); @@ -789,6 +800,10 @@ void release(WGPUCommandEncoder command_encoder); void release(WGPUCommandBuffer command_buffer); void release(WGPUSampler sampler); + +[[nodiscard]] GPUInfo gpuInfo(WGPUAdapter adapter = nullptr); + +[[nodiscard]] std::vector gpusInfo(); } // namespace ufo::compute #endif // UFO_COMPUTE_COMPUTE_HPP \ No newline at end of file diff --git a/lib/compute/src/compute.cpp b/lib/compute/src/compute.cpp index 643b4cf1..fd1905a6 100755 --- a/lib/compute/src/compute.cpp +++ b/lib/compute/src/compute.cpp @@ -1,11 +1,55 @@ +/** + * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown + * + * @author Daniel Duberg (dduberg@kth.se) + * @see https://github.com/UnknownFreeOccupied/ufomap + * @version 1.0 + * @date 2022-05-13 + * + * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology + * + * BSD 3-Clause License + * + * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. 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. + * + * THIS 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. + */ + // UFO #include // STL #include #include +#include #include #include +#include +#include namespace ufo::compute { @@ -257,4 +301,103 @@ void release(WGPUCommandBuffer command_buffer) } void release(WGPUSampler sampler) { wgpuSamplerRelease(sampler); } + +GPUInfo gpuInfo(WGPUAdapter adapter) +{ + bool const adapter_was_passed = (nullptr != adapter); + + WGPUInstance instance = nullptr; + if (!adapter_was_passed) { + instance = createInstance(); + if (nullptr == instance) { + return GPUInfo{}; + } + + adapter = createAdapter(instance); + if (nullptr == adapter) { + release(instance); + return GPUInfo{}; + } + } + + WGPUAdapterInfo info = {}; + wgpuAdapterGetInfo(adapter, &info); + + GPUInfo gpu_info = {}; + gpu_info.name = std::string_view(info.device.data, info.device.length); + gpu_info.architecture = + std::string_view(info.architecture.data, info.architecture.length); + gpu_info.description = std::string_view(info.description.data, info.description.length); + + gpu_info.vendor = [id = info.vendorID, v = info.vendor.data] { + switch (id) { + case 0x10DE: return "NVIDIA"; + case 0x1002: return "AMD"; + case 0x8086: return "Intel"; + case 0x13B5: return "ARM"; + case 0x5143: return "Qualcomm"; + case 0x106B: return "Apple"; + default: return v; + } + }(); + + gpu_info.type = [t = info.adapterType] { + switch (t) { + case WGPUAdapterType_DiscreteGPU: return "Discrete GPU"; + case WGPUAdapterType_IntegratedGPU: return "Integrated GPU"; + case WGPUAdapterType_CPU: return "CPU"; + case WGPUAdapterType_Unknown: return "Unknown"; + default: return "Other"; + } + }(); + + gpu_info.backend = [b = info.backendType] { + switch (b) { + case WGPUBackendType_WebGPU: return "WebGPU"; + case WGPUBackendType_D3D11: return "D3D11"; + case WGPUBackendType_D3D12: return "D3D12"; + case WGPUBackendType_Metal: return "Metal"; + case WGPUBackendType_Vulkan: return "Vulkan"; + case WGPUBackendType_OpenGL: return "OpenGL"; + case WGPUBackendType_OpenGLES: return "OpenGLES"; + default: return "Unknown"; + } + }(); + + wgpuAdapterInfoFreeMembers(info); + if (!adapter_was_passed) { + release(adapter); + release(instance); + } + + return gpu_info; +} + +std::vector gpusInfo() +{ + WGPUInstance instance = createInstance(); + if (nullptr == instance) { + return {}; + } + + size_t count = wgpuInstanceEnumerateAdapters(instance, nullptr, nullptr); + if (0 == count) { + release(instance); + return {}; + } + + std::vector adapters(count); + wgpuInstanceEnumerateAdapters(instance, nullptr, adapters.data()); + + std::vector res; + res.reserve(count); + for (auto adapter : adapters) { + res.push_back(gpuInfo(adapter)); + release(adapter); + } + + release(instance); + + return res; +} } // namespace ufo::compute \ No newline at end of file diff --git a/lib/compute/tests/CMakeLists.txt b/lib/compute/tests/CMakeLists.txt index f6ea765d..20069ad0 100644 --- a/lib/compute/tests/CMakeLists.txt +++ b/lib/compute/tests/CMakeLists.txt @@ -9,16 +9,11 @@ set_target_properties(ComputeTests PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} - CXX_STANDARD 17 + CXX_STANDARD 23 CXX_EXTENSIONS OFF + CXX_SCAN_FOR_MODULES OFF ) -if (MSVC) - target_compile_options(ComputeTests PRIVATE /W4) -else() - target_compile_options(ComputeTests PRIVATE -Wall -Wextra -Wpedantic) -endif() - -target_link_libraries(ComputeTests PRIVATE UFO::Compute Catch2::Catch2WithMain) +target_link_libraries(ComputeTests PRIVATE Catch2::Catch2WithMain UFO::Compute UFO::Warnings) catch_discover_tests(ComputeTests) \ No newline at end of file diff --git a/lib/container/CMakeLists.txt b/lib/container/CMakeLists.txt index a46ca73b..f2d7e980 100644 --- a/lib/container/CMakeLists.txt +++ b/lib/container/CMakeLists.txt @@ -1,11 +1,93 @@ -option(UFO_CONTAINER_BUILD_TESTS "Unit testing" ON) -option(UFO_CONTAINER_BUILD_COVERAGE "Test Coverage" ON) - -include(GNUInstallDirs) - add_library(Container INTERFACE) add_library(UFO::Container ALIAS Container) +target_sources(Container + INTERFACE + FILE_SET HEADERS + BASE_DIRS + include + FILES + include/ufo/container/structure_of_arrays/element.hpp + include/ufo/container/structure_of_arrays/value.hpp + include/ufo/container/structure_of_arrays/view.hpp + + include/ufo/container/tree/map/block.hpp + include/ufo/container/tree/map/iterator.hpp + include/ufo/container/tree/map/nearest_iterator.hpp + include/ufo/container/tree/map/query_iterator.hpp + include/ufo/container/tree/map/query_nearest_iterator.hpp + + include/ufo/container/tree/predicate/and.hpp + include/ufo/container/tree/predicate/bool.hpp + include/ufo/container/tree/predicate/child_of.hpp + include/ufo/container/tree/predicate/coord.hpp + include/ufo/container/tree/predicate/depth_interval.hpp + include/ufo/container/tree/predicate/depth.hpp + include/ufo/container/tree/predicate/exists.hpp + include/ufo/container/tree/predicate/false.hpp + include/ufo/container/tree/predicate/filter.hpp + include/ufo/container/tree/predicate/if_and_only_if.hpp + include/ufo/container/tree/predicate/inner.hpp + include/ufo/container/tree/predicate/leaf_or_depth.hpp + include/ufo/container/tree/predicate/leaf.hpp + include/ufo/container/tree/predicate/length_interval.hpp + include/ufo/container/tree/predicate/length.hpp + include/ufo/container/tree/predicate/modified.hpp + include/ufo/container/tree/predicate/offset.hpp + include/ufo/container/tree/predicate/or.hpp + include/ufo/container/tree/predicate/parent.hpp + include/ufo/container/tree/predicate/predicate_compare.hpp + include/ufo/container/tree/predicate/predicate.hpp + include/ufo/container/tree/predicate/pure_leaf.hpp + include/ufo/container/tree/predicate/satisfies_inner.hpp + include/ufo/container/tree/predicate/satisfies.hpp + include/ufo/container/tree/predicate/spatial.hpp + include/ufo/container/tree/predicate/then.hpp + include/ufo/container/tree/predicate/true.hpp + + include/ufo/container/tree/set/block.hpp + include/ufo/container/tree/set/iterator.hpp + include/ufo/container/tree/set/nearest_iterator.hpp + include/ufo/container/tree/set/query_iterator.hpp + include/ufo/container/tree/set/query_nearest_iterator.hpp + + include/ufo/container/tree/block.hpp + include/ufo/container/tree/code.hpp + include/ufo/container/tree/container_bucket_iterator.hpp + include/ufo/container/tree/container_iterator.hpp + include/ufo/container/tree/container.hpp + include/ufo/container/tree/coord.hpp + include/ufo/container/tree/data.hpp + include/ufo/container/tree/distance_node.hpp + include/ufo/container/tree/index.hpp + include/ufo/container/tree/iterator.hpp + include/ufo/container/tree/key.hpp + include/ufo/container/tree/nearest_iterator.hpp + include/ufo/container/tree/node.hpp + include/ufo/container/tree/predicate.hpp + include/ufo/container/tree/query_iterator.hpp + include/ufo/container/tree/query_nearest_iterator.hpp + include/ufo/container/tree/render.hpp + include/ufo/container/tree/trace_result.hpp + include/ufo/container/tree/tree.hpp + + include/ufo/container/binary_tree_map.hpp + include/ufo/container/binary_tree_set.hpp + include/ufo/container/hextree_map.hpp + include/ufo/container/hextree_set.hpp + include/ufo/container/octree_map.hpp + include/ufo/container/octree_set.hpp + include/ufo/container/quadtree_map.hpp + include/ufo/container/quadtree_set.hpp + include/ufo/container/range_map.hpp + include/ufo/container/range_set.hpp + include/ufo/container/range.hpp + include/ufo/container/small_vector.hpp + include/ufo/container/structure_of_arrays.hpp + include/ufo/container/tree_map.hpp + include/ufo/container/tree_set.hpp +) + target_link_libraries(Container INTERFACE UFO::Compute @@ -16,27 +98,14 @@ target_link_libraries(Container UFO::Vision ) -target_include_directories(Container - INTERFACE - $ - $ -) - if(UFO_BUILD_TESTS AND UFO_CONTAINER_BUILD_TESTS) add_subdirectory(tests) endif() -install(TARGETS Container - EXPORT UFO-targets - COMPONENT Container - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) - -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ +install( + TARGETS Container + EXPORT UFOTargets + FILE_SET HEADERS COMPONENT Container - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) \ No newline at end of file diff --git a/lib/container/README.md b/lib/container/README.md deleted file mode 100644 index 69fffc75..00000000 --- a/lib/container/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# UFOContainer -The UFO container library \ No newline at end of file diff --git a/lib/container/include/ufo/container/binary_tree_map.hpp b/lib/container/include/ufo/container/binary_tree_map.hpp old mode 100644 new mode 100755 index 90332b8d..76bf92d6 --- a/lib/container/include/ufo/container/binary_tree_map.hpp +++ b/lib/container/include/ufo/container/binary_tree_map.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/binary_tree_set.hpp b/lib/container/include/ufo/container/binary_tree_set.hpp old mode 100644 new mode 100755 index 40127c17..df7f1e15 --- a/lib/container/include/ufo/container/binary_tree_set.hpp +++ b/lib/container/include/ufo/container/binary_tree_set.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/hextree_map.hpp b/lib/container/include/ufo/container/hextree_map.hpp old mode 100644 new mode 100755 index a2b776cc..7ca0783b --- a/lib/container/include/ufo/container/hextree_map.hpp +++ b/lib/container/include/ufo/container/hextree_map.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/hextree_set.hpp b/lib/container/include/ufo/container/hextree_set.hpp old mode 100644 new mode 100755 index 6bb710e4..ddc9d24e --- a/lib/container/include/ufo/container/hextree_set.hpp +++ b/lib/container/include/ufo/container/hextree_set.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/octree_map.hpp b/lib/container/include/ufo/container/octree_map.hpp old mode 100644 new mode 100755 index eaffe4b6..5ed4248a --- a/lib/container/include/ufo/container/octree_map.hpp +++ b/lib/container/include/ufo/container/octree_map.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/octree_set.hpp b/lib/container/include/ufo/container/octree_set.hpp old mode 100644 new mode 100755 index a1048e46..9fa09418 --- a/lib/container/include/ufo/container/octree_set.hpp +++ b/lib/container/include/ufo/container/octree_set.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/quadtree_map.hpp b/lib/container/include/ufo/container/quadtree_map.hpp old mode 100644 new mode 100755 index 9d09a12e..1ffa239c --- a/lib/container/include/ufo/container/quadtree_map.hpp +++ b/lib/container/include/ufo/container/quadtree_map.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/quadtree_set.hpp b/lib/container/include/ufo/container/quadtree_set.hpp old mode 100644 new mode 100755 index 2a94669d..71cd31cd --- a/lib/container/include/ufo/container/quadtree_set.hpp +++ b/lib/container/include/ufo/container/quadtree_set.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/range.hpp b/lib/container/include/ufo/container/range.hpp deleted file mode 100644 index 5f6a1ed6..00000000 --- a/lib/container/include/ufo/container/range.hpp +++ /dev/null @@ -1,340 +0,0 @@ -/*! - * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown - * - * @author Daniel Duberg (dduberg@kth.se) - * @see https://github.com/UnknownFreeOccupied/ufomap - * @version 1.0 - * @date 2022-05-13 - * - * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * - * BSD 3-Clause License - * - * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. 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. - * - * 3. 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. - * - * THIS 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. - */ - -#ifndef UFO_CONTAINER_RANGE_HPP -#define UFO_CONTAINER_RANGE_HPP - -// STL -#include -#include -#include -#include -#include -#include -#include - -/* - * Range, RangeSet, and RangeMap implementation - * - * Functions present in the respective STL implementation adhere to their - * interface (std::set, std::map). - * - * A Range takes an arithmetic type T and defines an inclusive interval [T1, T2] - * - * A RangeSet is a container of unique ranges. If a Range is inserted into the - * container and is adjacent or overlapping with a Range already present the two - * are joined: e.g., [4,10] and [11, 40] becomes [4, 40]. - * - * A RangeMap is a container where each Range (key) has an associated value. - * Adjacent or overlapping ranges with the same value are joined. - * Ranges within the container are not allowed to overlap, e.g., if - * the range and value [0,10]:4 is present and [5,15]:6 is inserted - * the resulting container will contain: [0,10]:4, [11,15]:6 - * There is support for overwriting, see insert_or_assign - */ - -namespace ufo -{ -template , bool> = true> -class Range -{ - public: - struct Comparator { - using range_t = std::pair; - using is_transparent = std::true_type; - [[nodiscard]] constexpr bool operator()(Range lhs, range_t rhs) const noexcept - { - return lhs.upper() < rhs.first; - } - [[nodiscard]] constexpr bool operator()(range_t lhs, Range rhs) const noexcept - { - return lhs.second < rhs.upper(); - } - }; - - public: - // Tags - using value_type = T; - - Range() = default; - - Range(T value) : lower_(value), upper_(value) {} - - Range(T lower, T upper) : lower_(lower), upper_(upper) { assert(lower <= upper); } - - template , bool> = true> - Range(V value) : Range(value, value) - { - } - - template , bool> = true> - Range(V lower, V upper) - { - setRange(lower, upper); - } - - Range(Range const& other) = default; - - template - Range(Range other) : Range(other.lower(), other.upper()) - { - } - - Range(Range&& other) = default; - - Range& operator=(Range const& rhs) = default; - - template - Range& operator=(Range rhs) - { - setRange(rhs.lower(), rhs.upper()); - return *this; - } - - Range& operator=(Range&& rhs) = default; - - constexpr void setValue(T value) - { - lower_ = value; - upper_ = value; - } - - constexpr void setRange(T lower, T upper) - { - assert(lower <= upper); - - lower_ = lower; - upper_ = upper; - } - - template , bool> = true> - constexpr void setValue(V value) - { - setRange(value, value); - } - - template , bool> = true> - constexpr void setRange(V lower, V upper) - { - assert(lower <= upper); - - // FIXME: Check that this is correct - constexpr T t_min = std::numeric_limits::lowest(); - constexpr T t_max = std::numeric_limits::max(); - - // Check for error - if constexpr (std::is_floating_point_v || std::is_floating_point_v || - std::is_signed_v == std::is_signed_v) { - if (upper < t_min || t_max < lower) { - // FIXME: Fix better message - throw std::range_error("Outside representable range."); - } - } else if constexpr (std::is_unsigned_v) { - if (lower < 0 || t_max < lower) { - // FIXME: Fix better message - throw std::range_error("Outside representable range."); - } - } else if (t_max < lower) { - // FIXME: Fix better message - throw std::range_error("Outside representable range."); - } - - if constexpr (std::is_floating_point_v) { - lower_ = t_min <= lower ? lower : t_min; - upper_ = t_max >= upper ? upper : t_max; - } else if constexpr (std::is_floating_point_v) { - auto v_min = std::floor(lower); - auto v_max = std::ceil(upper); - lower_ = t_min <= v_min ? v_min : t_min; - upper_ = t_max >= v_max ? v_max : t_max; - } else if constexpr (std::is_signed_v == std::is_signed_v) { - lower_ = t_min <= lower ? lower : t_min; - upper_ = t_max >= upper ? upper : t_max; - } else if constexpr (std::is_unsigned_v) { - lower_ = 0 > lower ? 0 : (t_max >= lower ? lower : t_max); - upper_ = 0 > upper ? 0 : (t_max >= upper ? upper : t_max); - } else { - lower_ = t_max >= lower ? lower : t_max; - upper_ = t_max >= upper ? upper : t_max; - } - } - - [[nodiscard]] constexpr T lower() const noexcept { return lower_; } - - [[nodiscard]] constexpr T upper() const noexcept { return upper_; } - - [[nodiscard]] constexpr bool contains(Range range) const noexcept - { - return lower_ <= range.lower_ && range.upper_ <= upper_; - } - - template - [[nodiscard]] constexpr bool contains(Range range) const noexcept - { - return contains(range.lower(), range.upper()); - } - - template , bool> = true> - [[nodiscard]] constexpr bool contains(V value) const noexcept - { - return contains(value, value); - } - - template , bool> = true> - [[nodiscard]] constexpr bool contains(V lower, V upper) const noexcept - { - assert(lower <= upper); - - // FIXME: Is correct? - if constexpr (std::is_floating_point_v || std::is_floating_point_v || - (std::is_signed_v == std::is_signed_v)) { - return lower_ <= lower && upper <= upper_; - } else { - if (lower < 0 || upper_ < 0 || upper > upper_) { - return false; - } - return lower_ < 0 || lower_ <= lower; - } - } - - void swap(Range other) - { - std::swap(lower_, other.lower_); - std::swap(upper_, other.upper_); - } - - std::ostream& writeData(std::ostream& out_stream) const - { - out_stream.write(reinterpret_cast(&lower_), sizeof(T)); - return out_stream.write(reinterpret_cast(&upper_), sizeof(T)); - } - - std::istream& readData(std::istream& in_stream) - { - in_stream.read(reinterpret_cast(&lower_), sizeof(T)); - return in_stream.read(reinterpret_cast(&upper_), sizeof(T)); - } - - // - // Friends - // - - template - friend bool operator==(Range lhs, Range rhs); - template - friend bool operator!=(Range lhs, Range rhs); - template - friend bool operator<(Range lhs, Range rhs); - template - friend bool operator<=(Range lhs, Range rhs); - template - friend bool operator>(Range lhs, Range rhs); - template - friend bool operator>=(Range lhs, Range rhs); - - template - friend void swap(Range& lhs, Range& rhs) noexcept(noexcept(lhs.swap(rhs))); - - template - friend std::ostream& operator<<(std::ostream& os, Range range); - - private: - T lower_; - T upper_; -}; - -template -bool operator==(Range lhs, Range rhs) -{ - return lhs.lower_ == rhs.lower_ && lhs.upper_ == rhs.upper_; -} - -template -bool operator!=(Range lhs, Range rhs) -{ - return lhs.lower_ != rhs.lower_ || lhs.upper_ != rhs.upper_; -} - -template -bool operator<(Range lhs, Range rhs) -{ - return lhs.upper_ < rhs.lower_; -} - -template -bool operator<=(Range lhs, Range rhs) -{ - return lhs.upper_ <= rhs.lower_; -} - -template -bool operator>(Range lhs, Range rhs) -{ - return lhs.lower_ > rhs.upper_; -} - -template -bool operator>=(Range lhs, Range rhs) -{ - return lhs.lower_ >= rhs.upper_; -} - -template -void swap(Range& lhs, Range& rhs) noexcept(noexcept(lhs.swap(rhs))) -{ - lhs.swap(rhs); -} - -template -std::ostream& operator<<(std::ostream& os, Range range) -{ - if (range.lower() == range.upper()) { - os << '[' << +range.lower() << ']'; - } else if constexpr (std::is_floating_point_v) { - os << '[' << +range.lower() << ',' << +range.upper() << ']'; - } else { - os << '[' << +range.lower() << ".." << +range.upper() << ']'; - } - return os; -} -} // namespace ufo - -#endif // UFO_CONTAINER_RANGE_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/range_map.hpp b/lib/container/include/ufo/container/range_map.hpp old mode 100644 new mode 100755 index c677884d..00f1a650 --- a/lib/container/include/ufo/container/range_map.hpp +++ b/lib/container/include/ufo/container/range_map.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -43,7 +43,7 @@ #define UFO_CONTAINER_RANGE_MAP_HPP // UFO -#include +#include namespace ufo { diff --git a/lib/container/include/ufo/container/range_set.hpp b/lib/container/include/ufo/container/range_set.hpp old mode 100644 new mode 100755 index 736fb92d..aa3ed4dd --- a/lib/container/include/ufo/container/range_set.hpp +++ b/lib/container/include/ufo/container/range_set.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -43,7 +43,7 @@ #define UFO_CONTAINER_RANGE_SET_HPP // UFO -#include +#include namespace ufo { @@ -813,7 +813,7 @@ std::ostream& operator<<(std::ostream& os, RangeSet const& range_set) return os; } -/*! +/** * @brief Returns true if b is a subsequence of a. * * @tparam Key1 @@ -843,7 +843,7 @@ template return true; } -/*! +/** * @brief Creates a new RangeSet containing the ranges in a which are not find * in b. * diff --git a/lib/container/include/ufo/container/small_vector.hpp b/lib/container/include/ufo/container/small_vector.hpp index 61523973..0b7faeaf 100644 --- a/lib/container/include/ufo/container/small_vector.hpp +++ b/lib/container/include/ufo/container/small_vector.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/structure_of_arrays.hpp b/lib/container/include/ufo/container/structure_of_arrays.hpp old mode 100644 new mode 100755 index 121b8c63..515ffabf --- a/lib/container/include/ufo/container/structure_of_arrays.hpp +++ b/lib/container/include/ufo/container/structure_of_arrays.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -43,30 +43,171 @@ #define UFO_CONTAINER_STRUCTURE_OF_ARRAYS_HPP // UFO -#include -#include -#include -#include #include // STL #include -#include #include -#include +#include +#include +#include +#include +#include #include -#include +#include #include namespace ufo { template -class SoA + requires(0 < sizeof...(Ts)) +class SoA; + +namespace detail { - static_assert(0 < sizeof...(Ts)); +struct access { + template + static constexpr auto const& data(SoA const& soa) noexcept + { + return soa.data_; + } + + template + static constexpr auto& data(SoA& soa) noexcept + { + return soa.data_; + } + + template + static constexpr auto data(SoA&& soa) noexcept + { + return std::move(soa.data_); + } +}; + +template +struct is_soa : std::false_type { +}; + +template +struct is_soa> : std::true_type { +}; + +template +struct is_vector : std::false_type { +}; + +template +struct is_vector> : std::true_type { +}; + +template +struct is_span : std::false_type { +}; + +template +struct is_span> : std::true_type { +}; + +template +constexpr inline bool is_soa_v = is_soa::value; + +template +constexpr inline bool is_vector_v = is_vector::value; + +template +constexpr inline bool is_span_v = is_span::value; + +template +struct is_mergable : std::disjunction, is_vector, is_span> { +}; + +template +constexpr inline bool is_mergable_v = is_mergable::value; + +template + requires is_mergable_v +struct extract_types { + using type = std::tuple>; +}; + +template +struct extract_types> { + using type = std::tuple; +}; + +template +struct extract_types> { + using type = std::tuple; +}; + +template +using extract_types_t = typename extract_types>::type; + +template +using merged_types_t = decltype(std::tuple_cat(std::declval>()...)); - friend SoAElement; +template +struct tuple_to_soa; +template +struct tuple_to_soa> { + using type = SoA; +}; + +template +using merged_soa_t = typename tuple_to_soa>::type; + +template +constexpr auto const& wrap_data(SoA const& soa) noexcept +{ + return access::data(soa); +} + +template +constexpr auto& wrap_data(SoA& soa) noexcept +{ + return access::data(soa); +} + +template +constexpr auto wrap_data(SoA&& soa) noexcept +{ + return access::data(std::move(soa)); +} + +template +constexpr auto wrap_data(std::vector const& vec) +{ + return std::make_tuple(vec); +} + +template +constexpr auto wrap_data(std::vector& vec) +{ + return std::make_tuple(vec); +} + +template +constexpr auto wrap_data(std::vector&& vec) +{ + return std::make_tuple(std::move(vec)); +} + +template + requires is_mergable_v> && (!is_soa_v>) && + (!is_vector_v>) +constexpr auto wrap_data(R&& r) +{ + using T = std::ranges::range_value_t; + return std::make_tuple(std::vector(std::ranges::begin(r), std::ranges::end(r))); +} +} // namespace detail + +template + requires(0 < sizeof...(Ts)) +class SoA +{ public: /************************************************************************************** | | @@ -74,46 +215,79 @@ class SoA | | **************************************************************************************/ - using data_type = std::tuple...>; - using value_type = SoAElement; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using reference = value_type&; - using const_reference = value_type const&; - using pointer = value_type*; - using const_pointer = value_type const*; - using iterator = pointer; - using const_iterator = const_pointer; + using data_type = std::tuple...>; + + template + using zip_view_type = std::ranges::zip_view...>; + + using value_type = std::tuple; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = std::tuple; + using const_reference = std::tuple; + using iterator = std::ranges::iterator_t...>>; + using const_iterator = std::ranges::iterator_t const...>>; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; + template + requires(0 < sizeof...(Us)) + friend class SoA; + + friend struct detail::access; + + private: + using indices = std::index_sequence_for; + + public: /************************************************************************************** | | | Constructors | | | **************************************************************************************/ - SoA() = default; + constexpr SoA() noexcept = default; - explicit SoA(size_type count) { resize(count); } + constexpr explicit SoA(size_type count) { resize(count); } - SoA(size_type count, value_type const& value) { resize(count, value); } + constexpr SoA(size_type count, value_type const& value) { resize(count, value); } - SoA(size_type count, SoAValue const& value) { resize(count, value); } + template + constexpr SoA(InputIt first, InputIt last) + { + assign(first, last); + } - template - SoA(InputIt first, InputIt last) + template + constexpr SoA(std::from_range_t, R&& range) { - insert(begin(), first, last); + assign_range(std::forward(range)); } - SoA(SoA const& other) : data_(other.data_) { create_elements(); } + constexpr SoA(std::vector... vecs) : data_(std::move(vecs)...) {} - SoA(SoA&& other) noexcept = default; + constexpr SoA(SoA const& other) = default; - SoA(std::initializer_list init) : SoA(init.begin(), init.end()) {} + constexpr SoA(SoA&& other) noexcept = default; - SoA(std::initializer_list> init) : SoA(init.begin(), init.end()) {} + constexpr SoA(std::initializer_list init) : SoA(init.begin(), init.end()) {} + + template + requires(0 < sizeof...(Args)) && + (requires { detail::wrap_data(std::declval()); } && ...) + constexpr SoA(Args&&... args) + : data_(std::tuple_cat(detail::wrap_data(std::forward(args))...)) + { + if constexpr (sizeof...(Args) > 1) { + size_type const s = size(); + auto const check = [s](auto const& vec) { + if (vec.size() != s) { + throw std::invalid_argument("SoA: merged components must have the same size"); + } + }; + std::apply([&check](auto const&... vecs) { (check(vecs), ...); }, data_); + } + } /************************************************************************************** | | @@ -121,7 +295,7 @@ class SoA | | **************************************************************************************/ - ~SoA() = default; + constexpr ~SoA() = default; /************************************************************************************** | | @@ -129,25 +303,13 @@ class SoA | | **************************************************************************************/ - SoA& operator=(SoA const& rhs) - { - data_ = rhs.data_; - create_elements(); - } - - SoA& operator=(SoA&& rhs) = default; + constexpr SoA& operator=(SoA const& rhs) = default; - SoA& operator=(std::initializer_list ilist) - { - clear(); - insert(begin(), ilist.begin(), ilist.end()); - return *this; - } + constexpr SoA& operator=(SoA&& rhs) noexcept = default; - SoA& operator=(std::initializer_list> ilist) + constexpr SoA& operator=(std::initializer_list ilist) { - clear(); - insert(begin(), ilist.begin(), ilist.end()); + assign(ilist); return *this; } @@ -157,46 +319,28 @@ class SoA | | **************************************************************************************/ - // void assign(size_type count, value_type const& value) - // { - // // TODO: Implement - // clear(); - // reserve(count); - // for (std::size_t i{}; count > i; ++i) { - // push_back(value); - // } - // } - - // void assign(size_type count, SoAValue const& value) - // { - // // TODO: Implement - // clear(); - // reserve(count); - // for (std::size_t i{}; count > i; ++i) { - // push_back(value); - // } - // } - - // template - // void assign(InputIt first, InputIt last) - // { - // // TODO: Implement - // clear(); - // reserve(std::distance(first, last)); - // for (; first != last; ++first) { - // push_back(*first); - // } - // } - - // void assign(std::initializer_list ilist) - // { - // assign(std::begin(ilist), std::end(ilist)); - // } - - // void assign(std::initializer_list> ilist) - // { - // assign(std::begin(ilist), std::end(ilist)); - // } + constexpr void assign(size_type count, value_type const& value) + { + assign_impl(count, value, indices{}); + } + + template S> + constexpr void assign(I first, S last) + { + assign_range(std::ranges::subrange(first, last)); + } + + constexpr void assign(std::initializer_list ilist) + { + assign(ilist.begin(), ilist.end()); + } + + template + requires(std::same_as, value_type>) + constexpr void assign_range(R&& range) + { + assign_range_impl(std::forward(range), std::make_index_sequence{}); + } /************************************************************************************** | | @@ -205,27 +349,28 @@ class SoA **************************************************************************************/ template - [[nodiscard]] SoAView view() + [[nodiscard]] constexpr std::span view() { - return SoAView(std::get>(data_)); + return std::span{std::get>(data_)}; } template - [[nodiscard]] SoAView view() const + [[nodiscard]] constexpr std::span view() const { - return SoAView(std::get>(data_)); + return std::span{std::get>(data_)}; } template - [[nodiscard]] SoAView> view() + [[nodiscard]] constexpr std::span> view() { - return SoAView>(std::get(data_)); + return std::span{std::get(data_)}; } template - [[nodiscard]] SoAView const> view() const + [[nodiscard]] constexpr std::span const> view() + const { - return SoAView const>(std::get(data_)); + return std::span{std::get(data_)}; } /************************************************************************************** @@ -234,39 +379,39 @@ class SoA | | **************************************************************************************/ - [[nodiscard]] reference at(size_type pos) + [[nodiscard]] constexpr reference operator[](size_type pos) { - if (size() <= pos) { - // FIXME: Improve message - std::out_of_range("Out of range"); - } - return operator[](pos); + return operator[](pos, indices{}); } - [[nodiscard]] const_reference at(size_type pos) const + [[nodiscard]] constexpr const_reference operator[](size_type pos) const { - if (size() <= pos) { - // FIXME: Improve message - std::out_of_range("Out of range"); - } - return operator[](pos); + return operator[](pos, indices{}); } - [[nodiscard]] reference operator[](size_type pos) { return elements_[pos]; } + [[nodiscard]] constexpr reference at(size_type pos) + { + check_index(pos); + return operator[](pos); + } - [[nodiscard]] const_reference operator[](size_type pos) const { return elements_[pos]; } + [[nodiscard]] constexpr const_reference at(size_type pos) const + { + check_index(pos); + return operator[](pos); + } - [[nodiscard]] reference front() { return operator[](0u); } + [[nodiscard]] constexpr reference front() { return operator[](0u); } - [[nodiscard]] const_reference front() const { return operator[](0u); } + [[nodiscard]] constexpr const_reference front() const { return operator[](0u); } - [[nodiscard]] reference back() { return operator[](size() - 1); } + [[nodiscard]] constexpr reference back() { return operator[](size() - 1); } - [[nodiscard]] const_reference back() const { return operator[](size() - 1); } + [[nodiscard]] constexpr const_reference back() const { return operator[](size() - 1); } - [[nodiscard]] data_type* data() { return &data_; } + [[nodiscard]] constexpr data_type* data() noexcept { return &data_; } - [[nodiscard]] data_type const* data() const { return &data_; } + [[nodiscard]] constexpr data_type const* data() const noexcept { return &data_; } /************************************************************************************** | | @@ -274,31 +419,47 @@ class SoA | | **************************************************************************************/ - [[nodiscard]] iterator begin() { return elements_.get(); } - - [[nodiscard]] const_iterator begin() const { return elements_.get(); } + [[nodiscard]] constexpr iterator begin() noexcept { return get_view().begin(); } - [[nodiscard]] const_iterator cbegin() const { return begin(); } + [[nodiscard]] constexpr const_iterator begin() const noexcept + { + return get_view().begin(); + } - [[nodiscard]] iterator end() { return begin() + size(); } + [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); } - [[nodiscard]] const_iterator end() const { return begin() + size(); } + [[nodiscard]] constexpr iterator end() noexcept { return get_view().end(); } - [[nodiscard]] const_iterator cend() const { end(); } + [[nodiscard]] constexpr const_iterator end() const noexcept { return get_view().end(); } - // TODO: Implement below + [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); } - // [[nodiscard]] reverse_iterator rbegin() { return elements_.rbegin(); } + [[nodiscard]] constexpr reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } - // [[nodiscard]] const_reverse_iterator rbegin() const { return elements_.rbegin(); } + [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept + { + return const_reverse_iterator(end()); + } - // [[nodiscard]] const_reverse_iterator crbegin() const { return rbegin(); } + [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept + { + return rbegin(); + } - // [[nodiscard]] reverse_iterator rend() { return elements_.rend(); } + [[nodiscard]] constexpr reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } - // [[nodiscard]] const_reverse_iterator rend() const { return elements_.rend(); } + [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept + { + return const_reverse_iterator(begin()); + } - // [[nodiscard]] const_reverse_iterator crend() const { rend(); } + [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); } /************************************************************************************** | | @@ -306,266 +467,509 @@ class SoA | | **************************************************************************************/ - [[nodiscard]] bool empty() const noexcept { return std::get<0>(data_).empty(); } - - [[nodiscard]] size_type size() const noexcept { return std::get<0>(data_).size(); } - - [[nodiscard]] size_type max_size() const noexcept + [[nodiscard]] constexpr bool empty() const noexcept { - return std::get<0>(data_).max_size(); + return std::get<0>(data_).empty(); } - void reserve(size_type new_cap) + [[nodiscard]] constexpr size_type size() const noexcept { - (std::get>(data_).reserve(new_cap), ...); - create_elements(); + return std::get<0>(data_).size(); } - [[nodiscard]] size_type capacity() const noexcept + [[nodiscard]] constexpr size_type max_size() const noexcept { - std::array cap{std::get>(data_).capacity()...}; - return *std::min_element(cap.begin(), cap.end()); + return max_size_impl(indices{}); } - void shrink_to_fit() + constexpr void reserve(size_type new_cap) { reserve(new_cap, indices{}); } + + [[nodiscard]] constexpr size_type capacity() const noexcept { - (std::get>(data_).shrink_to_fit(), ...); - create_elements(); + return capacity_impl(indices{}); } + constexpr void shrink_to_fit() { shrink_to_fit_impl(indices{}); } + /************************************************************************************** | | | Modifiers | | | **************************************************************************************/ - void clear() - { - (std::get>(data_).clear(), ...); - create_elements(); - } - - // iterator insert(const_iterator pos, value_type const& value) - // { - // // TODO: Implement - // } - - // iterator insert(const_iterator pos, SoAValue const& value) - // { - // // TODO: Implement - // } - - // iterator insert(const_iterator pos, value_type&& value) - // { - // // TODO: Implement - // } - - // iterator insert(const_iterator pos, SoAValue&& value) - // { - // // TODO: Implement - // } - - // iterator insert(const_iterator pos, size_type count, value_type const& value) - // { - // // TODO: Implement - // } - - // iterator insert(const_iterator pos, size_type count, SoAValue const& value) - // { - // // TODO: Implement - // } - - // template - // iterator insert(const_iterator pos, InputIt first, InputIt last) - // { - // // TODO: Implement - // } - - // iterator insert(const_iterator pos, std::initializer_list ilist) - // { - // // TODO: Implement - // } - - // iterator insert(const_iterator pos, std::initializer_list> ilist) - // { - // // TODO: Implement - // } - - // iterator emplace(const_iterator pos, Ts const&... ts) - // { - // // TODO: Implement - // } - - // iterator emplace(const_iterator pos, Ts&&... ts) - // { - // // TODO: Implement - // } - - iterator erase(const_iterator pos) - { - auto const idx = std::distance(cbegin(), pos); - (std::get>(data_).erase(std::get>(data_).begin() + - idx), - ...); - create_elements(); - return begin() + idx; + constexpr void clear() noexcept { clear_impl(indices{}); } + + constexpr iterator insert(iterator pos, value_type const& value) + { + return insert_impl(pos, value, indices{}); } - iterator erase(const_iterator first, const_iterator last) + constexpr iterator insert(iterator pos, value_type&& value) { - auto const f_idx = std::distance(cbegin(), first); - auto const l_idx = std::distance(cbegin(), last); - (std::get>(data_).erase( - std::get>(data_).begin() + f_idx, - std::get>(data_).begin() + l_idx), - ...); - create_elements(); - return begin() + f_idx; + return insert_impl(pos, std::move(value), indices{}); } - size_type erase(value_type const& value) + constexpr iterator insert(iterator pos, size_type count, value_type const& value) { - // TODO: Implement - auto it = std::remove(begin(), end(), value); - auto r = end() - it; - erase(it, end()); - return r; + return insert_impl(pos, count, value, indices{}); } - size_type erase(SoAValue const& value) + template S> + constexpr iterator insert(iterator pos, I first, S last) { - // TODO: Implement - auto it = std::remove(begin(), end(), value); - auto r = end() - it; - erase(it, end()); - return r; + return insert_impl(pos, first, last, indices{}); } - template , bool> = true> - size_type erase(T const& value) + constexpr iterator insert(iterator pos, std::initializer_list ilist) { - // TODO: Implement - return erase_if( - [&value](auto&& element) { return value == element.template get(); }); + return insert(pos, ilist.begin(), ilist.end()); } - template - size_type erase_if(Pred pred) + template + constexpr iterator insert_range(iterator pos, R&& rg) { - // TODO: Implement - auto it = std::remove_if(begin(), end(), pred); - auto r = end() - it; - erase(it, end()); - return r; + return insert_range_impl(pos, std::forward(rg), indices{}); } - void push_back(value_type const& value) + template + constexpr iterator emplace(iterator pos, Us&&... us) { - std::apply( - [&value](auto&&... data) { - (data.push_back(value.template get()), ...); - }, - data_); - create_elements(); + static_assert(sizeof...(Us) == sizeof...(Ts), "SoA: invalid number of arguments"); + std::size_t const idx = std::distance(begin(), pos); + emplace_impl(pos, std::forward(us)...); + return begin() + idx; } - void push_back(SoAValue const& value) + constexpr iterator erase(iterator pos) { return erase_impl(pos, indices{}); } + + constexpr iterator erase(iterator first, iterator last) { - std::apply( - [&value](auto&&... data) { - (data.push_back(value.template get()), ...); - }, - data_); - create_elements(); + return erase_impl(first, last, indices{}); } - void push_back(value_type&& value) + constexpr void push_back(value_type const& value) { push_back_impl(value, indices{}); } + + constexpr void push_back(value_type&& value) { - // FIXME: Is it correct? - std::apply( - [&value](auto&&... data) { - (data.push_back(std::move(value.template get())), - ...); - }, - data_); - create_elements(); + push_back_impl(std::move(value), indices{}); } - void push_back(SoAValue&& value) + template + constexpr void push_back(Us&&... us) { - // FIXME: Is it correct? - std::apply( - [&value](auto&&... data) { - (data.push_back(std::move(value.template get())), - ...); - }, - data_); - create_elements(); + emplace_back(std::forward(us)...); } - reference emplace_back(Ts const&... ts) + // TODO: Check so each Us is of type Ts + template + constexpr reference emplace_back(Us&&... us) { - std::apply([&ts...](auto&&... data) { (data.push_back(ts), ...); }, data_); - create_elements(); + static_assert(sizeof...(Us) == sizeof...(Ts), "SoA: invalid number of arguments"); + emplace_back_impl(std::forward(us)...); return back(); } - reference emplace_back(Ts&&... ts) + template + constexpr void append_range(R&& rg) { - // FIXME: Is it correct? - std::apply([&ts...](auto&&... data) { (data.push_back(std::move(ts)), ...); }, data_); - create_elements(); - return back(); + append_range_impl(std::forward(rg), indices{}); + } + + constexpr void pop_back() { pop_back_impl(indices{}); } + + constexpr void resize(size_type count) { resize_impl(count, indices{}); } + + constexpr void resize(size_type count, value_type const& value) + { + resize_impl(count, value, indices{}); } - void pop_back() + constexpr void swap(SoA& other) noexcept { swap_impl(other, indices{}); } + + friend constexpr void swap(SoA& lhs, SoA& rhs) noexcept { lhs.swap(rhs); } + + template + [[nodiscard]] constexpr std::tuple_element_t& get(size_type pos) { - (std::get>(data_).pop_back(), ...); - create_elements(); + return std::get(data_)[pos]; } - void resize(size_type count) + template + [[nodiscard]] constexpr std::tuple_element_t const& get( + size_type pos) const { - (std::get>(data_).resize(count), ...); - create_elements(); + return std::get(data_)[pos]; } - void resize(size_type count, value_type const& value) + template + [[nodiscard]] constexpr T& get(size_type pos) { - (std::get>(data_).resize(count, value.template get()), ...); - create_elements(); + static_assert(contains_type_v, "SoA: type not found"); + static_assert(is_unique_v, "SoA: type is not unique, use get(pos) instead"); + return std::get>(data_)[pos]; } - void swap(SoA& other) + template + [[nodiscard]] constexpr T const& get(size_type pos) const { - (std::get>(data_).swap(std::get>(other.data_)), ...); - create_elements(); - other.create_elements(); + static_assert(contains_type_v, "SoA: type not found"); + static_assert(is_unique_v, "SoA: type is not unique, use get(pos) instead"); + return std::get>(data_)[pos]; } private: - void create_elements() - { - auto const cap = capacity(); - if (0 == cap) { - elements_ = nullptr; - elements_cap_ = 0; - } else if (elements_cap_ != cap) { - elements_ = std::unique_ptr(new value_type[cap]); - elements_cap_ = cap; - for (std::size_t i{}; cap > i; ++i) { - elements_[i].reference_ = this; - elements_[i].is_reference_ = true; - } + constexpr void check_index(size_type pos) const + { + if (size() <= pos) { + throw std::out_of_range("SoA::at: index " + std::to_string(pos) + + " is out of range for size " + std::to_string(size())); } } - private: - std::unique_ptr elements_; - std::size_t elements_cap_ = {}; - data_type data_; + template + constexpr void assign_impl(size_type count, value_type const& value, + std::index_sequence) + { + (std::get(data_).assign(count, std::get(value)), ...); + } + + template + constexpr void assign_range_impl(R&& range, std::index_sequence) + { + (std::get(data_).assign(std::views::elements(range).begin(), + std::views::elements(range).end()), + ...); + } + + template + [[nodiscard]] constexpr reference operator[](size_type pos, std::index_sequence) + { + return std::tie(std::get(data_)[pos]...); + } + + template + [[nodiscard]] constexpr const_reference operator[](size_type pos, + std::index_sequence) const + { + return std::tie(std::get(data_)[pos]...); + } + + template + [[nodiscard]] constexpr size_type max_size_impl( + std::index_sequence) const noexcept + { + return std::min({std::get(data_).max_size()...}); + } + + template + constexpr void reserve_impl(size_type new_cap, std::index_sequence) + { + (std::get(data_).reserve(new_cap), ...); + } + + template + [[nodiscard]] constexpr size_type capacity_impl( + std::index_sequence) const noexcept + { + return std::min({std::get(data_).capacity()...}); + } + + template + constexpr void shrink_to_fit_impl(std::index_sequence) + { + (std::get(data_).shrink_to_fit(), ...); + } + + template + constexpr void clear_impl(std::index_sequence) noexcept + { + (std::get(data_).clear(), ...); + } + + template + constexpr iterator insert_impl(iterator pos, value_type const& value, + std::index_sequence) + { + std::size_t const idx = std::distance(begin(), pos); + (std::get(data_).insert(std::get(data_).begin() + idx, std::get(value)), + ...); + return begin() + idx; + } + + template + constexpr iterator insert_impl(iterator pos, value_type&& value, + std::index_sequence) + { + std::size_t const idx = std::distance(begin(), pos); + (std::get(data_).insert(std::get(data_).begin() + idx, + std::move(std::get(value))), + ...); + return begin() + idx; + } + + template + constexpr iterator insert_impl(iterator pos, size_type count, value_type const& value, + std::index_sequence) + { + std::size_t const idx = std::distance(begin(), pos); + (std::get(data_).insert(std::get(data_).begin() + idx, count, + std::get(value)), + ...); + return begin() + idx; + } + + template S, std::size_t... Is> + constexpr iterator insert_impl(iterator pos, I first, S last, + std::index_sequence) + { + std::size_t const idx = std::distance(begin(), pos); + auto rg = std::ranges::subrange(first, last); + (std::get(data_).insert(std::get(data_).begin() + idx, + std::views::elements(rg).begin(), + std::views::elements(rg).end()), + ...); + return begin() + idx; + } + + template + constexpr iterator insert_range_impl(iterator pos, R&& rg, std::index_sequence) + { + std::size_t const idx = std::distance(begin(), pos); + (std::get(data_).insert(std::get(data_).begin() + idx, + std::views::elements(rg).begin(), + std::views::elements(rg).end()), + ...); + return begin() + idx; + } + + template + constexpr void emplace_impl(iterator pos, Us&&... us) + { + emplace_impl(pos, indices{}, std::forward(us)...); + } + + template + constexpr void emplace_impl(iterator pos, std::index_sequence, Us&&... us) + { + std::size_t const idx = std::distance(begin(), pos); + (std::get(data_).emplace(std::get(data_).begin() + idx, std::forward(us)), + ...); + } + + template + constexpr iterator erase_impl(iterator pos, std::index_sequence) + { + auto const idx = std::distance(begin(), pos); + (std::get(data_).erase(std::get(data_).begin() + idx), ...); + return begin() + idx; + } + + template + constexpr iterator erase_impl(iterator first, iterator last, std::index_sequence) + { + auto const f_idx = std::distance(begin(), first); + auto const l_idx = std::distance(begin(), last); + (std::get(data_).erase(std::get(data_).begin() + f_idx, + std::get(data_).begin() + l_idx), + ...); + return begin() + f_idx; + } + + template + constexpr void push_back_impl(value_type const& value, std::index_sequence) + { + (std::get(data_).push_back(std::get(value)), ...); + } + + template + constexpr void push_back_impl(value_type&& value, std::index_sequence) + { + (std::get(data_).push_back(std::move(std::get(value))), ...); + } + + template + constexpr void emplace_back_impl(Us&&... us) + { + emplace_back_impl(indices{}, std::forward(us)...); + } + + template + constexpr void emplace_back_impl(std::index_sequence, Us&&... us) + { + (std::get(data_).emplace_back(std::forward(us)), ...); + } + + template + constexpr void append_range_impl(R&& range, std::index_sequence) + { + (std::get(data_).append_range(std::views::elements(range)), ...); + } + + template + constexpr void pop_back_impl(std::index_sequence) + { + (std::get(data_).pop_back(), ...); + } + + template + constexpr void resize_impl(size_type count, std::index_sequence) + { + (std::get(data_).resize(count), ...); + } + + template + constexpr void resize_impl(size_type count, value_type const& value, + std::index_sequence) + { + (std::get(data_).resize(count, std::get(value)), ...); + } + + template + constexpr void swap_impl(SoA& other, std::index_sequence) noexcept + { + (std::get(data_).swap(std::get(other.data_)), ...); + } + + [[nodiscard]] constexpr auto get_view() noexcept { return get_view(indices{}); } + + [[nodiscard]] constexpr auto get_view() const noexcept { return get_view(indices{}); } + + template + [[nodiscard]] constexpr auto get_view(std::index_sequence) noexcept + { + return std::views::zip(std::ranges::ref_view(std::get(data_))...); + } + + template + [[nodiscard]] constexpr auto get_view(std::index_sequence) const noexcept + { + return std::views::zip(std::ranges::ref_view(std::get(data_))...); + } + + data_type data_; }; +template +SoA(std::initializer_list>) -> SoA; + +template +SoA(std::size_t, std::tuple) -> SoA; + +template +SoA(std::vector...) -> SoA; + +// 2-argument merging +template +SoA(SoA, SoA) -> SoA; + +template +SoA(SoA, std::vector) -> SoA; + +template +SoA(std::vector, SoA) -> SoA; + +template +SoA(std::vector, std::vector) -> SoA; + +// 3-argument merging +template +SoA(SoA, SoA, SoA) -> SoA; + +template +SoA(SoA, SoA, std::vector) -> SoA; + +template +SoA(SoA, std::vector, SoA) -> SoA; + +template +SoA(std::vector, SoA, SoA) -> SoA; + +template +SoA(SoA, std::vector, std::vector) -> SoA; + +template +SoA(std::vector, SoA, std::vector) -> SoA; + +template +SoA(std::vector, std::vector, SoA) -> SoA; + +template +SoA(std::vector, std::vector, std::vector) + -> SoA; + +// +// Non-member functions +// + +template +[[nodiscard]] constexpr auto begin(SoA& c) +{ + return c.begin(); +} + +template +[[nodiscard]] constexpr auto begin(SoA const& c) +{ + return c.begin(); +} + +template +[[nodiscard]] constexpr auto end(SoA& c) +{ + return c.end(); +} + +template +[[nodiscard]] constexpr auto end(SoA const& c) +{ + return c.end(); +} + +template +[[nodiscard]] constexpr auto size(SoA const& c) +{ + return c.size(); +} + +template +constexpr typename SoA::size_type erase(SoA& c, U const& value) +{ + auto it = std::remove(c.begin(), c.end(), value); + auto r = std::distance(it, c.end()); + c.erase(it, c.end()); + return r; +} + +template +constexpr typename SoA::size_type erase_if(SoA& c, Pred pred) +{ + auto it = std::remove_if(c.begin(), c.end(), pred); + auto r = std::distance(it, c.end()); + c.erase(it, c.end()); + return r; +} + +template +[[nodiscard]] constexpr bool operator==(SoA const& lhs, SoA const& rhs) +{ + return lhs.size() == rhs.size() && std::ranges::equal(lhs, rhs); +} + +template +[[nodiscard]] constexpr auto operator<=>(SoA const& lhs, SoA const& rhs) +{ + return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), + rhs.end()); +} + +template + requires(0 < sizeof...(Args)) && (detail::is_soa_v> || ...) && + (requires { detail::wrap_data(std::declval()); } && ...) +[[nodiscard]] constexpr auto merge(Args&&... args) +{ + return detail::merged_soa_t(std::forward(args)...); +} + // // Type traits // @@ -573,6 +977,30 @@ class SoA template struct contains_type> : contains_type { }; + +template +[[nodiscard]] constexpr auto view(SoA& soa) +{ + return soa.template view(); +} + +template +[[nodiscard]] constexpr auto view(SoA const& soa) +{ + return soa.template view(); +} + +template +[[nodiscard]] constexpr auto view(SoA& soa) +{ + return soa.template view(); +} + +template +[[nodiscard]] constexpr auto view(SoA const& soa) +{ + return soa.template view(); +} } // namespace ufo #endif // UFO_CONTAINER_STRUCTURE_OF_ARRAYS_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/structure_of_arrays/element.hpp b/lib/container/include/ufo/container/structure_of_arrays/element.hpp deleted file mode 100644 index 49a38fe8..00000000 --- a/lib/container/include/ufo/container/structure_of_arrays/element.hpp +++ /dev/null @@ -1,402 +0,0 @@ -/*! - * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown - * - * @author Daniel Duberg (dduberg@kth.se) - * @see https://github.com/UnknownFreeOccupied/ufomap - * @version 1.0 - * @date 2022-05-13 - * - * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * - * BSD 3-Clause License - * - * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. 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. - * - * 3. 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. - * - * THIS 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. - */ - -#ifndef UFO_CONTAINER_STRUCTURE_OF_ARRAYS_ELEMENT_HPP -#define UFO_CONTAINER_STRUCTURE_OF_ARRAYS_ELEMENT_HPP - -// UFO -#include -#include - -// STL -#include -#include -#include -#include -#include -#include - -namespace ufo -{ -// Forward declaration -template -class SoA; - -template -class SoAElement -{ - friend SoA; - - public: - SoAElement() = default; - - SoAElement(SoAElement const& other) - { - if (other.is_reference_) { - auto other_pos = other.position(); - value_ = new SoAValue(other.reference_->template view()[other_pos]...); - } else if (nullptr != other.value_) { - value_ = new SoAValue(*other.value_); - } - } - - SoAElement(SoAElement&& other) - { - if (other.is_reference_) { - auto other_pos = other.position(); - value_ = new SoAValue( - std::move(other.reference_->template view()[other_pos])...); - } else { - value_ = other.value_; - other.value_ = nullptr; - } - } - - SoAElement(Ts const&... ts) : value_(new SoAValue(ts...)), is_reference_(false) - { - } - - SoAElement(SoAValue const& value) : value_(new SoAValue(value)) {} - - ~SoAElement() - { - if (!is_reference_ && nullptr != value_) { - delete value_; - } - } - - SoAElement& operator=(SoAElement const& rhs) - { - if (is_reference_ && rhs.is_reference_) { - auto pos = position(); - auto rhs_pos = rhs.position(); - ((reference_->template view()[pos] = - rhs.reference_->template view()[rhs_pos]), - ...); - } else if (is_reference_) { - auto pos = position(); - if (nullptr == rhs.value_) { - ((reference_->template view()[pos] = {}), ...); - } else { - ((reference_->template view()[pos] = rhs.value_->template get()), ...); - } - } else if (rhs.is_reference_) { - auto rhs_pos = rhs.position(); - if (nullptr == value_) { - value_ = new SoAValue(rhs.reference_->template view()[rhs_pos]...); - } else { - ((value_->template get() = rhs.reference_->template view()[rhs_pos]), - ...); - } - } else if (nullptr == value_) { - if (nullptr != rhs.value_) { - value_ = new SoAValue(rhs.value_->template get()...); - } - } else if (nullptr == rhs.value_) { - *value_ = SoAValue(); - } else { - *value_ = *rhs.value_; - } - - return *this; - } - - SoAElement& operator=(SoAElement&& rhs) - { - if (is_reference_ && rhs.is_reference_) { - auto pos = position(); - auto rhs_pos = rhs.position(); - ((reference_->template view()[pos] = - std::move(rhs.reference_->template view()[rhs_pos])), - ...); - } else if (is_reference_) { - auto pos = position(); - if (nullptr == rhs.value_) { - ((reference_->template view()[pos] = {}), ...); - } else { - ((reference_->template view()[pos] = - std::move(rhs.value_->template get())), - ...); - } - } else if (rhs.is_reference_) { - auto rhs_pos = rhs.position(); - if (nullptr == value_) { - value_ = new SoAValue(std::move(rhs.reference_->template view()[rhs_pos])...); - } else { - ((value_->template get() = - std::move(rhs.reference_->template view()[rhs_pos])), - ...); - } - } else if (nullptr == value_) { - value_ = rhs.value_; - rhs.value_ = nullptr; - } else if (nullptr == rhs.value_) { - *value_ = SoAValue(); - } else { - delete value_; - value_ = rhs.value_; - rhs.value_ = nullptr; - } - - return *this; - } - - SoAElement& operator=(SoAValue const& rhs) - { - if (is_reference_) { - auto pos = position(); - ((reference_->template view()[pos] = rhs.template get()), ...); - } else if (nullptr == value_) { - value_ = new SoAValue(rhs); - } else { - *value_ = rhs; - } - - return *this; - } - - SoAElement& operator=(SoAValue&& rhs) - { - if (is_reference_) { - auto pos = position(); - ((reference_->template view()[pos] = std::move(rhs.template get())), ...); - } else if (nullptr == value_) { - value_ = new SoAValue(std::move(rhs)); - } else { - *value_ = std::move(rhs); - } - - return *this; - } - - template , bool> = true> - SoAElement& operator=(T const& rhs) - { - get() = rhs; - return *this; - } - - operator SoAValue() const - { - if (is_reference_) { - auto pos = position(); - return SoAValue(reference_->template view()[pos]...); - } else if (nullptr == value_) { - return SoAValue(); - } else { - return *value_; - } - } - - template , bool> = true> - operator T&() - { - return get(); - } - - template , bool> = true> - operator T const&() const - { - return get(); - } - - template , bool> = true> - [[nodiscard]] T& get() - { - if (is_reference_) { - auto pos = position(); - return reference_->template view()[pos]; - } else { - if (nullptr == value_) { - value_ = new SoAValue(); - } - return value_->template get(); - } - } - - template , bool> = true> - [[nodiscard]] T const& get() const - { - if (is_reference_) { - auto pos = position(); - return reference_->template view()[pos]; - } else { - if (nullptr == value_) { - value_ = new SoAValue(); - } - return value_->template get(); - } - } - - template - [[nodiscard]] auto&& get() & - { - return get_helper(*this); - } - - template - [[nodiscard]] auto&& get() && - { - return get_helper(*this); - } - - template - [[nodiscard]] auto&& get() const& - { - return get_helper(*this); - } - - template - [[nodiscard]] auto&& get() const&& - { - return get_helper(*this); - } - - private: - SoAElement(SoA* soa) : reference_(soa), is_reference_(true) {} - - template - [[nodiscard]] auto&& get_helper(T&& t) - { - if (is_reference_) { - auto pos = position(); - return std::forward(t).reference_->template view()[pos]; - } else { - if (nullptr == value_) { - value_ = new SoAValue(); - } - return std::forward(t).value_->template get(); - } - } - - template - [[nodiscard]] auto&& get_helper(T&& t) const - { - if (is_reference_) { - auto pos = position(); - return std::forward(t).reference_->template view()[pos]; - } else { - if (nullptr == value_) { - value_ = new SoAValue(); - } - return std::forward(t).value_->template get(); - } - } - - [[nodiscard]] std::size_t position() const - { - assert(is_reference_ && nullptr != reference_); - return static_cast(std::distance(reference_->cbegin(), this)); - } - - private: - union { - SoA* reference_ = nullptr; - mutable SoAValue* value_; - }; - bool is_reference_ = false; -}; - -template -bool operator==(SoAElement const& lhs, SoAElement const& rhs) -{ - return ((lhs.template get() == rhs.template get()) && ...); -} - -template -bool operator==(SoAElement const& lhs, SoAValue const& rhs) -{ - return ((lhs.template get() == rhs.template get()) && ...); -} - -template -bool operator==(SoAValue const& lhs, SoAElement const& rhs) -{ - return ((lhs.template get() == rhs.template get()) && ...); -} - -template -bool operator!=(SoAElement const& lhs, SoAElement const& rhs) -{ - return !(lhs == rhs); -} - -template -bool operator!=(SoAElement const& lhs, SoAValue const& rhs) -{ - return !(lhs == rhs); -} - -template -bool operator!=(SoAValue const& lhs, SoAElement const& rhs) -{ - return !(lhs == rhs); -} - -template -std::ostream& operator<<(std::ostream& os, SoAElement const& element) -{ - os << element.template get(); - ((os << ", " << element.template get()), ...); - return os; -} - -// -// Type traits -// - -template -struct contains_type> : contains_type { -}; -} // namespace ufo - -namespace std -{ -template -struct tuple_size> : integral_constant { -}; - -template -struct tuple_element> : tuple_element> { -}; -} // namespace std - -#endif // UFO_CONTAINER_STRUCTURE_OF_ARRAYS_ELEMENT_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/structure_of_arrays/value.hpp b/lib/container/include/ufo/container/structure_of_arrays/value.hpp deleted file mode 100644 index e4a43246..00000000 --- a/lib/container/include/ufo/container/structure_of_arrays/value.hpp +++ /dev/null @@ -1,159 +0,0 @@ -/*! - * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown - * - * @author Daniel Duberg (dduberg@kth.se) - * @see https://github.com/UnknownFreeOccupied/ufomap - * @version 1.0 - * @date 2022-05-13 - * - * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * - * BSD 3-Clause License - * - * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. 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. - * - * 3. 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. - * - * THIS 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. - */ - -#ifndef UFO_CONTAINER_STRUCTURE_OF_ARRAYS_VALUE_HPP -#define UFO_CONTAINER_STRUCTURE_OF_ARRAYS_VALUE_HPP - -// UFO -#include - -// STL -#include -#include -#include -#include -#include - -namespace ufo -{ -template -class SoAValue -{ - public: - SoAValue() = default; - - SoAValue(Ts const&... ts) : data_(ts...) {} - - template , bool> = true> - operator T&() - { - return get(); - } - - template , bool> = true> - operator T const&() const - { - return get(); - } - - template , bool> = true> - SoAValue& operator=(T const& rhs) - { - get() = rhs; - return *this; - } - - template , bool> = true> - [[nodiscard]] T& get() - { - return std::get(data_); - } - - template , bool> = true> - [[nodiscard]] T const& get() const - { - return std::get(data_); - } - - template - auto&& get() & - { - return get_helper(*this); - } - - template - auto&& get() && - { - return get_helper(*this); - } - - template - auto&& get() const& - { - return get_helper(*this); - } - - template - auto&& get() const&& - { - return get_helper(*this); - } - - private: - template - auto&& get_helper(T&& t) - { - return std::get(std::forward(t).data_); - } - - private: - std::tuple data_; -}; - -template -std::ostream& operator<<(std::ostream& os, SoAValue const& value) -{ - os << value.template get(); - ((os << ", " << value.template get()), ...); - return os; -} - -// -// Type traits -// - -template -struct contains_type> : contains_type { -}; -} // namespace ufo - -namespace std -{ -template -struct tuple_size> : integral_constant { -}; - -template -struct tuple_element> : tuple_element> { -}; -} // namespace std - -#endif // UFO_CONTAINER_STRUCTURE_OF_ARRAYS_VALUE_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/structure_of_arrays/view.hpp b/lib/container/include/ufo/container/structure_of_arrays/view.hpp deleted file mode 100644 index 27462e3b..00000000 --- a/lib/container/include/ufo/container/structure_of_arrays/view.hpp +++ /dev/null @@ -1,160 +0,0 @@ -/*! - * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown - * - * @author Daniel Duberg (dduberg@kth.se) - * @see https://github.com/UnknownFreeOccupied/ufomap - * @version 1.0 - * @date 2022-05-13 - * - * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * - * BSD 3-Clause License - * - * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. 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. - * - * 3. 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. - * - * THIS 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. - */ - -#ifndef UFO_CONTAINER_STRUCTURE_OF_ARRAYS_VIEW_HPP -#define UFO_CONTAINER_STRUCTURE_OF_ARRAYS_VIEW_HPP - -// STL -#include -#include -#include -#include - -namespace ufo -{ -// TODO: Make it so you can convert from non-const to const - -template -class SoAView -{ - template - friend class SoA; - - public: - /************************************************************************************** - | | - | Tags | - | | - **************************************************************************************/ - - using element_type = T; - using value_type = std::remove_cv_t; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using pointer = T*; - using const_pointer = T const*; - using reference = T&; - using const_reference = T const&; - using iterator = std::conditional_t, - typename std::vector::const_iterator, - typename std::vector::iterator>; - using const_iterator = typename std::vector::const_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - - /************************************************************************************** - | | - | Constructors | - | | - **************************************************************************************/ - - SoAView() = default; - - /************************************************************************************** - | | - | Iterators | - | | - **************************************************************************************/ - - iterator begin() const noexcept { return data_->begin(); } - - const_iterator cbegin() const noexcept { return data_->cbegin(); } - - iterator end() const noexcept { return data_->end(); } - - const_iterator cend() const noexcept { return data_->cend(); } - - reverse_iterator rbegin() const noexcept { return data_->rbegin(); } - - const_reverse_iterator crbegin() const noexcept { return data_->crbegin(); } - - reverse_iterator rend() const noexcept { return data_->rend(); } - - const_reverse_iterator crend() const noexcept { return data_->crend(); } - - /************************************************************************************** - | | - | Element access | - | | - **************************************************************************************/ - - [[nodiscard]] reference front() const { return data_->front(); } - - [[nodiscard]] reference back() const { return data_->back(); } - - [[nodiscard]] reference at(size_type pos) const { return data_->at(pos); } - - [[nodiscard]] reference operator[](size_type idx) const { return (*data_)[idx]; } - - [[nodiscard]] pointer data() const noexcept { return data_->data(); } - - /************************************************************************************** - | | - | Observers | - | | - **************************************************************************************/ - - [[nodiscard]] size_type size() const noexcept { return data_->size(); } - - [[nodiscard]] bool empty() const noexcept { return data_->empty(); } - - private: - /************************************************************************************** - | | - | Constructors | - | | - **************************************************************************************/ - - SoAView(std::conditional_t, std::vector const&, - std::vector&> - data) - : data_(&data) - { - } - - private: - std::conditional_t, std::vector const*, - std::vector*> - data_ = nullptr; -}; -} // namespace ufo - -#endif // UFO_CONTAINER_STRUCTURE_OF_ARRAYS_VIEW_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/block.hpp b/lib/container/include/ufo/container/tree/block.hpp old mode 100644 new mode 100755 index 91073800..e4a38bcb --- a/lib/container/include/ufo/container/tree/block.hpp +++ b/lib/container/include/ufo/container/tree/block.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -43,304 +43,20 @@ #define UFO_CONTAINER_TREE_BLOCK_HPP // UFO -#include -#include -#include -#include -#include -#include -#include +#include +#include // STL -#include -#include -#include #include -#include namespace ufo { -template struct TreeBlock { - static constexpr bool const HasCenter = WithCenter; + template + using LeafBlock = TreeLeafBlock; - using Code = TreeCode; - using Point = Vec; - using length_t = double; - using Length = Vec; - - std::array, BF> children; - - constexpr TreeBlock() - { - for (std::size_t i{}; BF > i; ++i) { - children[i].store(TreeIndex::NULL_POS, std::memory_order_relaxed); - } - } - - constexpr TreeBlock(TreeBlock const& other) - : parent_block_(other.parent_block_) - , modified_(other.modified_.load()) - , code_(other.code_) - { - for (std::size_t i{}; BF > i; ++i) { - children[i].store(other.children[i].load(std::memory_order_relaxed), - std::memory_order_relaxed); - } - } - - // For createRoot - - constexpr TreeBlock(TreeIndex::pos_t parent_block, Code code, Point /* center */, - Length /* half_length */) - : parent_block_(parent_block), modified_(std::uint16_t(0)), code_(code) - { - for (std::size_t i{}; BF > i; ++i) { - children[i].store(TreeIndex::NULL_POS, std::memory_order_relaxed); - } - } - - constexpr TreeBlock& operator=(TreeBlock const& rhs) - { - for (std::size_t i{}; BF > i; ++i) { - children[i].store(rhs.children[i].load(std::memory_order_relaxed), - std::memory_order_relaxed); - } - parent_block_ = rhs.parent_block_; - modified_ = rhs.modified_.load(); - code_ = rhs.code_; - return *this; - } - - // For initChildren - - constexpr void fill(TreeIndex::pos_t parent_block, TreeBlock const& parent, - std::size_t offset, Length /* half_length */) - { - parent_block_ = parent_block; - modifiedFill(parent.modified(offset)); - code_ = parent.code(offset).firstborn(); - } - - [[nodiscard]] constexpr TreeIndex::pos_t parentBlock() const { return parent_block_; } - - [[nodiscard]] constexpr TreeIndex parent() const - { - return TreeIndex(parent_block_, code_.offset(code_.depth() + 1)); - } - - [[nodiscard]] constexpr Code code(std::size_t idx) const - { - return code_.firstbornSibling(idx); - } - - [[nodiscard]] constexpr Code parentCode() const { return code_.parent(); } - - /*! - * @return The depth of the block. - */ - [[nodiscard]] constexpr auto depth() const noexcept(noexcept(code_.depth())) - { - return code_.depth(); - } - - [[nodiscard]] constexpr bool valid() const { return code_.valid(); } - - friend constexpr bool operator==(TreeBlock const& lhs, TreeBlock const& rhs) - { - return lhs.children == rhs.children && lhs.code_ == rhs.code_; - } - - friend constexpr bool operator!=(TreeBlock const& lhs, TreeBlock const& rhs) - { - return !(lhs == rhs); - } - - void reset() - { - for (std::size_t i{}; BF > i; ++i) { - children[i].store(TreeIndex::NULL_POS, std::memory_order_relaxed); - } - - parent_block_ = TreeIndex::NULL_POS; - modified_ = {}; - code_ = Code::invalid(); - } - - /************************************************************************************** - | | - | Modified | - | | - **************************************************************************************/ - - [[nodiscard]] std::uint16_t modified() const { return modified_.load(); } - - [[nodiscard]] bool modified(std::size_t pos) const - { - assert(BF > pos); - return (modified_.load() >> pos) & std::uint16_t(1); - } - - [[nodiscard]] bool modifiedAny() const { return std::uint16_t(0) != modified_; } - - [[nodiscard]] bool modifiedAll() const - { - return (~(static_cast(-1) << BF)) != modified_; - } - - [[nodiscard]] bool modifiedNone() const { return std::uint16_t(0) == modified_; } - - std::uint16_t modifiedExchange(std::uint16_t value) - { - return modified_.exchange(value); - } - - void modifiedFill(bool value) - { - value ? modifiedSet() : modifiedReset(); - // modified_ = (-static_cast(value)) >> (16 - BF); - } - - void modifiedSet() - { - constexpr std::uint16_t const ALL_SET = ~(static_cast(-1) << BF); - // if (ALL_SET != modified_) { - modified_ = ALL_SET; - // } - } - - void modifiedSet(std::size_t pos) - { - assert(BF > pos); - if (!modified(pos)) { - modified_ |= std::uint16_t(1) << pos; - // modified_.fetch_or(std::uint16_t(1) << pos, std::memory_order_release); - } - } - - void modifiedSet(std::size_t pos, bool value) - { - assert(BF > pos); - if (modified(pos) != value) { - modified_ ^= (-static_cast(value) ^ modified_.load()) & - (std::uint16_t(1) << pos); - } - } - - void modifiedReset() - { - // if (std::uint16_t(0) != modified_) { - modified_ = std::uint16_t(0); - // } - } - - void modifiedReset(std::size_t pos) - { - assert(BF > pos); - if (modified(pos)) { - modified_ &= ~(std::uint16_t(1) << pos); - } - } - - void modifiedUpdate(std::uint16_t update) { modified_ |= update; } - - /************************************************************************************** - | | - | Lock | - | | - **************************************************************************************/ - - [[nodiscard]] Spinlock& chicken() noexcept { return chicken_; } - - void lock() noexcept(noexcept(chicken_.lock())) { chicken_.lock(); } - - [[nodiscard]] bool try_lock() noexcept(noexcept(chicken_.try_lock())) - { - return chicken_.try_lock(); - } - - void unlock() noexcept(noexcept(chicken_.unlock())) { chicken_.unlock(); } - - private: - // Position of the parent block - TreeIndex::pos_t parent_block_ = TreeIndex::NULL_POS; - - // Bit set saying if the node corresponding to the bit has been modified - std::atomic_uint16_t modified_{}; - - // You want chicken? - Spinlock chicken_; - - // Code to the first node of the block - Code code_ = Code::invalid(); -}; - -template -struct TreeBlock : TreeBlock { - using Base = TreeBlock; - - static constexpr bool const HasCenter = true; - - using Code = TreeCode; - using Point = Vec; - using length_t = double; - using Length = Vec; - - constexpr TreeBlock() = default; - - constexpr TreeBlock(TreeIndex::pos_t parent_block, Code code, Point center, - Length half_length) - : Base(parent_block, code, center, half_length) - { - } - - constexpr void fill(TreeIndex::pos_t parent_block, TreeBlock const& parent, - std::size_t offset, Length half_length) - { - Base::fill(parent_block, static_cast(parent), offset, half_length); - - for (std::size_t i{}; Dim > i; ++i) { - center_[i] = (offset & (std::size_t(1) << i)) ? parent.center_[i] + half_length[i] - : parent.center_[i] - half_length[i]; - } - } - - [[nodiscard]] constexpr TreeCoord center(std::size_t offset, - Length half_length) const - { - Point ret; - - for (std::size_t i{}; Dim > i; ++i) { - ret[i] = (offset & (std::size_t(1) << i)) ? center_[i] + half_length[i] - : center_[i] - half_length[i]; - } - - return TreeCoord(ret, this->depth()); - } - - [[nodiscard]] constexpr float centerAxis(std::size_t offset, Length half_length, - std::size_t axis) const - { - assert(Dim > axis); - - return (offset & (std::size_t(1) << axis)) ? center_[axis] + half_length[axis] - : center_[axis] - half_length[axis]; - } - - friend constexpr bool operator==(TreeBlock const& lhs, TreeBlock const& rhs) - { - return lhs.center_ == rhs.center_ && - static_cast const&>(lhs) == - static_cast const&>(rhs); - } - - friend constexpr bool operator!=(TreeBlock const& lhs, TreeBlock const& rhs) - { - return !(lhs == rhs); - } - - private: - Point center_; + template + using InnerBlock = TreeInnerBlock; }; } // namespace ufo diff --git a/lib/container/include/ufo/container/tree/code.hpp b/lib/container/include/ufo/container/tree/code.hpp old mode 100644 new mode 100755 index 7907085a..477d5e15 --- a/lib/container/include/ufo/container/tree/code.hpp +++ b/lib/container/include/ufo/container/tree/code.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -44,8 +44,8 @@ // UFO #include -#include #include +#include // STL #include @@ -65,18 +65,18 @@ class TreeCode friend class std::hash; public: - using code_t = std::uint32_t; - using key_t = typename TreeKey::key_t; - using depth_t = std::uint32_t; - using size_type = std::size_t; + using code_type = std::uint32_t; + using key_type = typename TreeKey::key_type; + using depth_type = std::uint32_t; + using size_type = std::size_t; private: - static constexpr code_t const DEPTHS_PER_IDX = Morton::LEVELS_32; - static constexpr code_t const OFFSET_MASK = ~((~code_t(0)) << Dim); + static constexpr code_type const DEPTHS_PER_IDX = Morton::LEVELS_32; + static constexpr code_type const OFFSET_MASK = ~((~code_type(0)) << Dim); static constexpr std::array const ACTIVE{ - true, std::numeric_limits::digits > DEPTHS_PER_IDX, - std::numeric_limits::digits > 2 * DEPTHS_PER_IDX}; + true, std::numeric_limits::digits > DEPTHS_PER_IDX, + std::numeric_limits::digits > 2 * DEPTHS_PER_IDX}; public: /************************************************************************************** @@ -88,13 +88,13 @@ class TreeCode constexpr TreeCode() noexcept = default; constexpr TreeCode(TreeCode const&) noexcept = default; - constexpr TreeCode(std::array const& code, depth_t const& depth) + constexpr TreeCode(std::array const& code, depth_type const& depth) : code_(code), depth_(depth) { assert(valid()); } - constexpr explicit TreeCode(std::array const& code) : TreeCode(code, 0) {} + constexpr explicit TreeCode(std::array const& code) : TreeCode(code, 0) {} constexpr explicit TreeCode(TreeKey const& key) : depth_(key.depth()) { @@ -156,11 +156,11 @@ class TreeCode | | **************************************************************************************/ - [[nodiscard]] constexpr key_t operator[](size_type pos) const noexcept + [[nodiscard]] constexpr key_type operator[](size_type pos) const noexcept { assert(size() > pos); - key_t k = Morton::decode32(code_[0], pos); + key_type k = Morton::decode32(code_[0], pos); if constexpr (ACTIVE[1]) { k |= Morton::decode32(code_[1], pos) << DEPTHS_PER_IDX; @@ -206,11 +206,11 @@ class TreeCode return ipow(std::size_t(2), static_cast(Dim)); } - // [[nodiscard]] constexpr code_t code() const noexcept { return code_; } + // [[nodiscard]] constexpr code_type code() const noexcept { return code_; } - [[nodiscard]] static constexpr depth_t maxDepth() noexcept + [[nodiscard]] static constexpr depth_type maxDepth() noexcept { - depth_t max_depth = DEPTHS_PER_IDX; + depth_type max_depth = DEPTHS_PER_IDX; if constexpr (ACTIVE[1]) { max_depth += DEPTHS_PER_IDX; } @@ -220,11 +220,11 @@ class TreeCode return max_depth; } - [[nodiscard]] constexpr depth_t depth() const noexcept { return depth_; } + [[nodiscard]] constexpr depth_type depth() const noexcept { return depth_; } - [[nodiscard]] static constexpr bool equalAtDepth(TreeCode const& lhs, - TreeCode const& rhs, - depth_t depth) noexcept + [[nodiscard]] static constexpr bool sameAncestorAtDepth(TreeCode const& lhs, + TreeCode const& rhs, + depth_type depth) noexcept { assert(maxDepth() >= depth); assert(lhs.valid() && rhs.valid()); @@ -232,9 +232,9 @@ class TreeCode auto i = depth / DEPTHS_PER_IDX; auto d = depth % DEPTHS_PER_IDX; - std::array m{0 >= i ? ~code_t(0) : code_t(0), - 1 >= i ? ~code_t(0) : code_t(0), - 2 >= i ? ~code_t(0) : code_t(0)}; + std::array m{0 >= i ? ~code_type(0) : code_type(0), + 1 >= i ? ~code_type(0) : code_type(0), + 2 >= i ? ~code_type(0) : code_type(0)}; m[i] <<= Dim * d; @@ -243,27 +243,42 @@ class TreeCode (lhs.code_[2] & m[2]) == (rhs.code_[2] & m[2]); } - [[nodiscard]] static constexpr depth_t depthWhereEqual(TreeCode const& lhs, - TreeCode const& rhs) noexcept + [[nodiscard]] constexpr bool isAncestorOf(TreeCode const& other) const noexcept + { + return depth() >= other.depth() && sameAncestorAtDepth(*this, other, depth()); + } + + [[nodiscard]] constexpr bool isDescendantOf(TreeCode const& other) const noexcept + { + return other.isAncestorOf(*this); + } + + [[nodiscard]] constexpr bool isSiblingOf(TreeCode const& other) const noexcept + { + return depth() == other.depth() && sameAncestorAtDepth(*this, other, depth() + 1); + } + + [[nodiscard]] static constexpr depth_type lowestCommonAncestor( + TreeCode const& lhs, TreeCode const& rhs) noexcept { assert(lhs.valid() && rhs.valid()); auto depth = lhs.code_[2] != rhs.code_[2] ? 2 * DEPTHS_PER_IDX - : static_cast(lhs.code_[1] != rhs.code_[1]) * DEPTHS_PER_IDX; + : static_cast(lhs.code_[1] != rhs.code_[1]) * DEPTHS_PER_IDX; depth = std::max(depth, std::max(lhs.depth(), rhs.depth())); - auto i = depth / DEPTHS_PER_IDX; - auto d = depth % DEPTHS_PER_IDX; - code_t c = (lhs.code_[i] ^ rhs.code_[i]) >> (Dim * d); + auto i = depth / DEPTHS_PER_IDX; + auto d = depth % DEPTHS_PER_IDX; + code_type c = (lhs.code_[i] ^ rhs.code_[i]) >> (Dim * d); for (; c; c >>= Dim, ++depth) { } return depth; } - [[nodiscard]] constexpr TreeCode toDepth(depth_t depth) const + [[nodiscard]] constexpr TreeCode toDepth(depth_type depth) const { assert(maxDepth() >= depth); @@ -272,16 +287,16 @@ class TreeCode return ret; } - constexpr void setDepth(depth_t depth) + constexpr void setDepth(depth_type depth) { assert(maxDepth() >= depth); auto i = depth / DEPTHS_PER_IDX; auto d = depth % DEPTHS_PER_IDX; - std::array m{0 >= i ? ~code_t(0) : code_t(0), - 1 >= i ? ~code_t(0) : code_t(0), - 2 >= i ? ~code_t(0) : code_t(0)}; + std::array m{0 >= i ? ~code_type(0) : code_type(0), + 1 >= i ? ~code_type(0) : code_type(0), + 2 >= i ? ~code_type(0) : code_type(0)}; m[i] <<= Dim * d; @@ -291,31 +306,45 @@ class TreeCode depth_ = depth; } - /*! + /** * @brief Get the offset at the current depth (same as `c.offset(c.depth())`). * * @return The offset at the current depth. */ - [[nodiscard]] constexpr code_t offset() const { return offset(depth_); } + [[nodiscard]] constexpr code_type offset() const { return offset(depth_); } - /*! + /** * @brief Get the offset at a specific depth for this code. * * @param depth The depth the index is requested for. * @return The offset at the specified depth. */ - [[nodiscard]] constexpr code_t offset(depth_t depth) const + [[nodiscard]] constexpr code_type offset(depth_type depth) const { - assert(maxDepth() >= depth); + assert(maxDepth() >= depth && depth_ <= depth); auto i = depth / DEPTHS_PER_IDX; auto d = depth % DEPTHS_PER_IDX; return (code_[i] >> (Dim * d)) & OFFSET_MASK; } + constexpr void setOffset(code_type offset) { setOffset(depth_, offset); } + + constexpr void setOffset(depth_type depth, code_type offset) + { + assert(maxDepth() >= depth && depth_ <= depth && branchingFactor() > offset); + + auto i = depth / DEPTHS_PER_IDX; + auto d = depth % DEPTHS_PER_IDX; + + // NOTE: Two shifts to prevent shifting by more bits than code_type contains + code_[i] &= ((~code_type(0)) << Dim) << (Dim * d); + code_[i] |= static_cast(offset) << (Dim * d); + } + [[nodiscard]] constexpr TreeCode parent() const { return toDepth(depth_ + 1); } - /*! + /** * @brief Get the code of a specific child of this code * * @param idx The index of the child @@ -329,7 +358,7 @@ class TreeCode return firstborn().firstbornSibling(idx); } - /*! + /** * @brief Get the code of the firstborn child of this code (same as child(0)). * * @return TreeCode The firstborn child code @@ -340,7 +369,7 @@ class TreeCode return TreeCode(code_, depth_ - 1); } - /*! + /** * @brief Get the code of a specific sibling of this code * * @param idx The index of the sibling @@ -348,21 +377,36 @@ class TreeCode */ [[nodiscard]] constexpr TreeCode sibling(std::size_t idx) const { - assert(branchingFactor() > idx); - TreeCode ret = *this; + ret.setOffset(depth_, idx); + return ret; + } + + [[nodiscard]] constexpr TreeCode nextSibling() const + { + assert(branchingFactor() > offset() + 1); auto i = depth_ / DEPTHS_PER_IDX; auto d = depth_ % DEPTHS_PER_IDX; - // NOTE: Two shifts to prevent shifting by more bits than code_t contains - ret.code_[i] &= ((~code_t(0)) << Dim) << (Dim * d); - ret.code_[i] |= static_cast(idx) << (Dim * d); + TreeCode ret = *this; + ret.code_[i] += code_type(1) << (Dim * d); + return ret; + } + + [[nodiscard]] constexpr TreeCode prevSibling() const + { + assert(0 < offset()); + + auto i = depth_ / DEPTHS_PER_IDX; + auto d = depth_ % DEPTHS_PER_IDX; + TreeCode ret = *this; + ret.code_[i] -= code_type(1) << (Dim * d); return ret; } - /*! + /** * @brief Get the code of a specific sibling of this firstborn code * * @note Should only be called on firstborn codes (i.e., `offset(depth())` should be @@ -380,11 +424,11 @@ class TreeCode auto d = depth_ % DEPTHS_PER_IDX; TreeCode ret = *this; - ret.code_[i] |= static_cast(idx) << (Dim * d); + ret.code_[i] |= static_cast(idx) << (Dim * d); return ret; } - // void append(code_t code, depth_t num_depths) + // void append(code_type code, depth_type num_depths) // { // // TODO: Fix asserts // assert(depth() >= num_depths); @@ -392,7 +436,7 @@ class TreeCode // // TODO: Implement // } - // code_t remove(depth_t num_depths) + // code_type remove(depth_type num_depths) // { // // TODO: Fix asserts // assert(maxDepth() - depth() >= num_depths); @@ -401,7 +445,7 @@ class TreeCode // // TODO: Implement // } - // [[nodiscard]] code_t lowestOffsets(depth_t num_depths) const + // [[nodiscard]] code_type lowestOffsets(depth_type num_depths) const // { // // // TODO: Fix asserts // // assert(maxDepth() - depth() >= num_depths); @@ -413,10 +457,11 @@ class TreeCode // assert(DEPTHS_PER_IDX >= num_depths); // return code_[0] & - // (~code_t(0) >> (std::numeric_limits::digits - Dim * num_depths)); + // (~code_type(0) >> (std::numeric_limits::digits - Dim * + // num_depths)); // } - // void lowestOffsets(depth_t num_depths, code_t lowest_offsets) + // void lowestOffsets(depth_type num_depths, code_type lowest_offsets) // { // // // TODO: Fix asserts // // assert(maxDepth() - depth() >= num_depths); @@ -431,12 +476,13 @@ class TreeCode // code_[0] |= lowest_offsets; // // return code_[0] & - // // (~code_t(0) >> (std::numeric_limits::digits - Dim * num_depths)); + // // (~code_type(0) >> (std::numeric_limits::digits - Dim * + // num_depths)); // } - [[nodiscard]] code_t lowestOffsets() const { return code_[0]; } + [[nodiscard]] code_type lowestOffsets() const { return code_[0]; } - void lowestOffsets(code_t lowest_offsets, depth_t depth = 0) + void lowestOffsets(code_type lowest_offsets, depth_type depth = 0) { code_[0] = lowest_offsets; depth_ = depth; @@ -491,8 +537,8 @@ class TreeCode } private: - std::array code_{}; - depth_t depth_ = maxDepth() + 1; + std::array code_{}; + depth_type depth_ = maxDepth() + 1; }; using BinaryCode = TreeCode<1>; @@ -510,7 +556,7 @@ struct hash> { { // Code size static constexpr std::size_t const CS = - std::numeric_limits::code_t>::digits; + std::numeric_limits::code_type>::digits; // Hash size static constexpr std::size_t const HS = std::numeric_limits::digits; diff --git a/lib/container/include/ufo/container/tree/container.hpp b/lib/container/include/ufo/container/tree/container.hpp old mode 100644 new mode 100755 index 2640b837..7adb990d --- a/lib/container/include/ufo/container/tree/container.hpp +++ b/lib/container/include/ufo/container/tree/container.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -48,11 +48,11 @@ #include #include #include -#include #include // STL #include +#include #include #include #include @@ -62,6 +62,8 @@ namespace ufo { +// TODO: Add array of std::atomic_flag to value_type + template class TreeContainer { @@ -79,7 +81,7 @@ class TreeContainer using Data = std::array; template - struct alignas(8) S { + struct alignas(32) S { Data data; alignas(8) bool modified = false; }; @@ -87,9 +89,9 @@ class TreeContainer template using bucket_type = S; using size_type = std::size_t; - using pos_t = TreeIndex::pos_t; + using pos_type = TreeIndex::pos_type; - using value_type = std::tuple, S...>; + using value_type = std::tuple, S...>; using Bucket = std::atomic; template @@ -247,27 +249,27 @@ class TreeContainer } template - [[nodiscard]] IteratorWrapper> iter() + [[nodiscard]] auto iter() { - return IteratorWrapper>(begin(), end()); + return std::ranges::subrange(begin(), end()); } template - [[nodiscard]] IteratorWrapper> iter() const + [[nodiscard]] auto iter() const { - return IteratorWrapper>(begin(), end()); + return std::ranges::subrange(begin(), end()); } template - [[nodiscard]] IteratorWrapper> riter() + [[nodiscard]] auto riter() { - return IteratorWrapper>(rbegin(), rend()); + return std::ranges::subrange(rbegin(), rend()); } template - [[nodiscard]] IteratorWrapper> riter() const + [[nodiscard]] auto riter() const { - return IteratorWrapper>(rbegin(), rend()); + return std::ranges::subrange(rbegin(), rend()); } template @@ -343,29 +345,27 @@ class TreeContainer } template - [[nodiscard]] IteratorWrapper> iterBucket() + [[nodiscard]] auto iterBucket() { - return IteratorWrapper>(beginBucket(), endBucket()); + return std::ranges::subrange(beginBucket(), endBucket()); } template - [[nodiscard]] IteratorWrapper> iterBucket() const + [[nodiscard]] auto iterBucket() const { - return IteratorWrapper>(beginBucket(), endBucket()); + return std::ranges::subrange(beginBucket(), endBucket()); } template - [[nodiscard]] IteratorWrapper> riterBucket() + [[nodiscard]] auto riterBucket() { - return IteratorWrapper>(rbeginBucket(), - rendBucket()); + return std::ranges::subrange(rbeginBucket(), rendBucket()); } template - [[nodiscard]] IteratorWrapper> riterBucket() const + [[nodiscard]] auto riterBucket() const { - return IteratorWrapper>(rbeginBucket(), - rendBucket()); + return std::ranges::subrange(rbeginBucket(), rendBucket()); } template @@ -383,13 +383,13 @@ class TreeContainer template [[nodiscard]] auto& bucket(std::size_t idx) { - return std::get(bucket(idx)); + return std::get(bucket(idx)); } template [[nodiscard]] auto const& bucket(std::size_t idx) const { - return std::get(bucket(idx)); + return std::get(bucket(idx)); } template @@ -445,61 +445,61 @@ class TreeContainer } template - [[nodiscard]] T& get(pos_t pos) + [[nodiscard]] T& get(pos_type pos) { return bucketData(pos)[blockPos(pos)]; } template - [[nodiscard]] T const& get(pos_t pos) const + [[nodiscard]] T const& get(pos_type pos) const { return bucketData(pos)[blockPos(pos)]; } template - [[nodiscard]] auto& get(pos_t pos) + [[nodiscard]] auto& get(pos_type pos) { return bucketData(pos)[blockPos(pos)]; } template - [[nodiscard]] auto const& get(pos_t pos) const + [[nodiscard]] auto const& get(pos_type pos) const { return bucketData(pos)[blockPos(pos)]; } - [[nodiscard]] constexpr pos_t numBuckets() const noexcept + [[nodiscard]] constexpr pos_type numBuckets() const noexcept { return empty() ? 0 : bucketPos(capacity() - 1) + 1; } - [[nodiscard]] constexpr pos_t numBlocksPerBucket() const noexcept + [[nodiscard]] constexpr pos_type numBlocksPerBucket() const noexcept { return NUM_BLOCKS_PER_BUCKET; } - [[nodiscard]] constexpr pos_t bucketPos(pos_t pos) const noexcept + [[nodiscard]] constexpr pos_type bucketPos(pos_type pos) const noexcept { return pos / NUM_BLOCKS_PER_BUCKET; } - [[nodiscard]] constexpr pos_t blockPos(pos_t pos) const noexcept + [[nodiscard]] constexpr pos_type blockPos(pos_type pos) const noexcept { return pos % NUM_BLOCKS_PER_BUCKET; } - [[nodiscard]] pos_t create() + [[nodiscard]] pos_type create() { - if (pos_t idx = num_inactive_.load(std::memory_order_relaxed); 0u < idx) { + if (pos_type idx = num_inactive_.load(std::memory_order_relaxed); 0u < idx) { --idx; num_inactive_.store(idx, std::memory_order_relaxed); auto& b = *buckets_[bucketPos(idx)].load(std::memory_order_relaxed); return std::get<0>(b)[blockPos(idx)]; } - pos_t idx = cap_.fetch_add(pos_t(1u), std::memory_order_relaxed); + pos_type idx = cap_.fetch_add(pos_type(1u), std::memory_order_relaxed); - pos_t bucket = bucketPos(idx); + pos_type bucket = bucketPos(idx); value_type* b = buckets_[bucket]; if (nullptr == b) { @@ -511,11 +511,11 @@ class TreeContainer return idx; } - [[nodiscard]] pos_t createThreadSafe() + [[nodiscard]] pos_type createThreadSafe() { if (0u < num_inactive_.load(std::memory_order_relaxed)) { std::scoped_lock lock(mutex_); - if (pos_t idx = num_inactive_.load(std::memory_order_acquire); 0u < idx) { + if (pos_type idx = num_inactive_.load(std::memory_order_acquire); 0u < idx) { --idx; num_inactive_.store(idx, std::memory_order_release); auto& b = *buckets_[bucketPos(idx)].load(std::memory_order_relaxed); @@ -523,10 +523,10 @@ class TreeContainer } } - pos_t idx = cap_.fetch_add(pos_t(1u), std::memory_order_acq_rel); + pos_type idx = cap_.fetch_add(pos_type(1u), std::memory_order_acq_rel); - pos_t bucket = bucketPos(idx); - pos_t block = blockPos(idx); + pos_type bucket = bucketPos(idx); + pos_type block = blockPos(idx); value_type* v = buckets_[bucket]; if (0 == block) { @@ -546,10 +546,10 @@ class TreeContainer return idx; } - void eraseBlock(pos_t block) + void eraseBlock(pos_type block) { - pos_t idx = num_inactive_.fetch_add(pos_t(1), std::memory_order_acq_rel); - auto& b = *buckets_[bucketPos(idx)].load(std::memory_order_relaxed); + pos_type idx = num_inactive_.fetch_add(pos_type(1), std::memory_order_acq_rel); + auto& b = *buckets_[bucketPos(idx)].load(std::memory_order_relaxed); std::get<0>(b)[blockPos(idx)] = block; } @@ -561,18 +561,19 @@ class TreeContainer break; } - bucket(i) = value_type(); + // TODO: Why does the line below not work? + // *(buckets_[i]) = {}; } cap_ = 0; num_inactive_ = 0; } - void reserve(pos_t cap) + void reserve(pos_type cap) { // FIXME: Can be improved - pos_t first = bucketPos(capacity()); - pos_t last = bucketPos(cap); + pos_type first = bucketPos(capacity()); + pos_type last = bucketPos(cap); for (; last >= first; ++first) { if (nullptr == buckets_[first]) { break; @@ -601,20 +602,20 @@ class TreeContainer [[nodiscard]] bool empty() const { return 0 == size(); } - [[nodiscard]] pos_t size() const { return cap_ - num_inactive_; } + [[nodiscard]] pos_type size() const { return cap_ - num_inactive_; } - [[nodiscard]] pos_t capacity() const { return cap_; } + [[nodiscard]] pos_type capacity() const { return cap_; } template - [[nodiscard]] constexpr size_type serializedBucketSize() const + [[nodiscard]] static constexpr size_type serializedBucketSize() { - // return sizeof(Data); - return NUM_BLOCKS_PER_BUCKET * sizeof(T); + return sizeof(S::data); } + template [[nodiscard]] constexpr size_type serializedSize() const { - return numBuckets() * serializedBucketSize(); + return numBuckets() * serializedBucketSize(); } void swap(TreeContainer& other) @@ -622,9 +623,9 @@ class TreeContainer using std::swap; swap(buckets_, other.buckets_); - pos_t tmp = cap_; - cap_ = other.cap_.load(); - other.cap_ = tmp; + pos_type tmp = cap_; + cap_ = other.cap_.load(); + other.cap_ = tmp; tmp = num_inactive_; num_inactive_ = other.num_inactive_.load(); @@ -645,8 +646,8 @@ class TreeContainer private: std::unique_ptr buckets_ = std::make_unique(NUM_BUCKETS); - std::atomic cap_{}; - std::atomic num_inactive_{}; + std::atomic cap_{}; + std::atomic num_inactive_{}; std::mutex mutex_; }; diff --git a/lib/container/include/ufo/container/tree/container_bucket_iterator.hpp b/lib/container/include/ufo/container/tree/container_bucket_iterator.hpp old mode 100644 new mode 100755 index b3fb74cd..5ffa9665 --- a/lib/container/include/ufo/container/tree/container_bucket_iterator.hpp +++ b/lib/container/include/ufo/container/tree/container_bucket_iterator.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/tree/container_iterator.hpp b/lib/container/include/ufo/container/tree/container_iterator.hpp old mode 100644 new mode 100755 index aa8e41e6..32a4ec12 --- a/lib/container/include/ufo/container/tree/container_iterator.hpp +++ b/lib/container/include/ufo/container/tree/container_iterator.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/tree/coord.hpp b/lib/container/include/ufo/container/tree/coord.hpp old mode 100644 new mode 100755 index f13039e4..2ae83a46 --- a/lib/container/include/ufo/container/tree/coord.hpp +++ b/lib/container/include/ufo/container/tree/coord.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -43,7 +43,7 @@ #define UFO_CONTAINER_TREE_COORD_HPP // UFO -#include +#include #include // STL @@ -95,21 +95,16 @@ struct TreeCoord : public Vec { { } - private: - // NOTE: This is a helper for MSVC - - template - static constexpr bool is_valid_coord_v = - (D == sizeof...(Args) && (std::is_scalar_v && ...)); - - public: - template , bool> = true> + template && ...), + bool> = true> constexpr TreeCoord(Args&&... args) noexcept : Point(std::forward(args)...) { } - template , bool> = true> + template && ...), + bool> = true> constexpr TreeCoord(Args&&... args) noexcept : TreeCoord(std::integral_constant{}, std::forward(args)...) { diff --git a/lib/container/include/ufo/container/tree/data.hpp b/lib/container/include/ufo/container/tree/data.hpp old mode 100644 new mode 100755 index 880279e0..cdf2225e --- a/lib/container/include/ufo/container/tree/data.hpp +++ b/lib/container/include/ufo/container/tree/data.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -46,121 +46,120 @@ #include #include #include +#include #include // STL +#include #include #include -#include namespace ufo { -template +template class TreeData { - public: - using Data = TreeContainer; - - using Index = TreeIndex; - using pos_t = Index::pos_t; - using offset_t = Index::offset_t; + friend Derived; public: - [[nodiscard]] Data& data() { return data_; } - - [[nodiscard]] Data const& data() const { return data_; } + static constexpr TreeIndex::offset_type const BF = ipow(std::size_t(2), Dim); - /*! - * @brief Checks if a block is valid. - * - * @param block the block to check - * @return `true` if the block is valid, `false` otherwise. - */ - [[nodiscard]] bool valid(pos_t block) const { return data_.capacity() > block; } + using LeafData = TreeContainer...>; + using InnerData = TreeContainer...>; - void swap(TreeData& other) - { - using std::swap; - swap(data_, other.data_); - } + using Index = TreeIndex; + using pos_type = Index::pos_type; - protected: - [[nodiscard]] std::size_t size() const { return data_.size(); } + static constexpr std::size_t const NumBuffers = sizeof...(Ts); - void reserve(std::size_t cap) { data_.reserve(cap); } + public: + ~TreeData() { gpuRelease(); } - void clear() { data_.clear(); } + [[nodiscard]] LeafData& leafData() { return leaf_data_; } - [[nodiscard]] pos_t create() { return data_.create(); } + [[nodiscard]] LeafData const& leafData() const { return leaf_data_; } - [[nodiscard]] pos_t createThreadSafe() { return data_.createThreadSafe(); } + [[nodiscard]] InnerData& innerData() { return inner_data_; } - void eraseBlock(pos_t block) { data_.eraseBlock(block); } + [[nodiscard]] InnerData const& innerData() const { return inner_data_; } - template - [[nodiscard]] T& data(pos_t block) + /** + * @brief Checks if a block exists. + * + * @param block the block to check + * @return `true` if the block exists, `false` otherwise. + */ + [[nodiscard]] bool exists(pos_type block) const { - return data_.template get(block); + return leaf(block) ? leafExists(block) : innerExists(block); } - template - [[nodiscard]] T const& data(pos_t block) const + [[nodiscard]] bool leafExists(pos_type block) const { - return data_.template get(block); + assert(leaf(block)); + return leaf_data_.capacity() > removeLeafType(block); } - template - [[nodiscard]] T const& dataConst(pos_t block) const + [[nodiscard]] bool innerExists(pos_type block) const { - return data(block); + assert(inner(block)); + return inner_data_.capacity() > removeInnerType(block); } - template - [[nodiscard]] T& data(TreeIndex node) + bool gpuInit(WGPUPowerPreference power_preference = WGPUPowerPreference_HighPerformance, + WGPUBackendType backend_type = WGPUBackendType_Undefined) { - return data(node.pos); - } + if (nullptr != device_) { + return false; + } - template - [[nodiscard]] T const& data(TreeIndex node) const - { - return data(node.pos); - } + instance_ = compute::createInstance(); + adapter_ = compute::createAdapter(instance_, nullptr, power_preference, backend_type); + auto required_limits = requiredLimits(adapter_); + device_ = compute::createDevice(adapter_, &required_limits); + queue_ = compute::queue(device_); - template - [[nodiscard]] T const& dataConst(TreeIndex node) const - { - return data(node); + return true; } - protected: - Data data_; -}; + bool gpuInit(WGPULimits const& required_limits, + WGPUSurface compatible_surface = nullptr, + WGPUPowerPreference power_preference = WGPUPowerPreference_HighPerformance, + WGPUBackendType backend_type = WGPUBackendType_Undefined) + { + if (nullptr != device_) { + return false; + } -#if defined(UFO_WEBGPU) + instance_ = compute::createInstance(); + adapter_ = compute::createAdapter(instance_, compatible_surface, power_preference, + backend_type); + device_ = compute::createDevice(adapter_, &required_limits); + queue_ = compute::queue(device_); -template -class TreeData - : public TreeData -{ - private: - using Base = TreeData; + return true; + } - static constexpr std::size_t const NumBuffers = 1 + sizeof...(Blocks); + bool gpuInit(WGPUAdapter adapter) + { + assert(nullptr != adapter); - public: - ~TreeData() { gpuRelease(); } + return gpuInit(adapter, requiredLimits(adapter)); + } - bool gpuInit(WGPUPowerPreference power_preference = WGPUPowerPreference_HighPerformance, - WGPUBackendType backend_type = WGPUBackendType_Undefined) + bool gpuInit(WGPUAdapter adapter, WGPULimits const& required_limits) { if (nullptr != device_) { return false; } - instance_ = compute::createInstance(); - adapter_ = compute::createAdapter(instance_, nullptr, power_preference, backend_type); - device_ = compute::createDevice(adapter_, requiredLimits(adapter_)); + assert(nullptr != adapter); + + // Increase reference count + wgpuAdapterAddRef(adapter); + + adapter_ = adapter; + device_ = compute::createDevice(adapter, &required_limits); queue_ = compute::queue(device_); return true; @@ -185,11 +184,22 @@ class TreeData void gpuRelease() { - for (WGPUBuffer& buf : buffers_) { - if (nullptr != buf) { - wgpuBufferRelease(buf); - buf = nullptr; + for (auto& buffers : leaf_buffers_) { + for (WGPUBuffer& buf : buffers) { + if (nullptr != buf) { + wgpuBufferRelease(buf); + } + } + buffers.clear(); + } + + for (auto& buffers : inner_buffers_) { + for (WGPUBuffer& buf : buffers) { + if (nullptr != buf) { + wgpuBufferRelease(buf); + } } + buffers.clear(); } if (nullptr != queue_) { @@ -213,128 +223,405 @@ class TreeData } } - [[nodiscard]] WGPUInstance gpuInstance() const { return instance_; } - - [[nodiscard]] WGPUAdapter gpuAdapter() const { return adapter_; } - [[nodiscard]] WGPUDevice gpuDevice() const { return device_; } [[nodiscard]] WGPUQueue gpuQueue() const { return queue_; } - [[nodiscard]] std::array const& gpuBuffers() const + template + [[nodiscard]] std::size_t gpuNumBuffers() const { - return buffers_; + return gpuNumLeafBuffers() + gpuNumInnerBuffers(); } template - [[nodiscard]] WGPUBuffer gpuBuffer() const + [[nodiscard]] std::size_t gpuNumLeafBuffers() const { - return buffers_[index_v]; + return leaf_buffers_[index_v].size(); } template - [[nodiscard]] std::size_t gpuBufferSize() const + [[nodiscard]] std::size_t gpuNumInnerBuffers() const { - double size_factor = sizeof(T) / static_cast(sizeof(Block)); - std::size_t content_size = static_cast(size_factor * tree_buffer_size_); - return compute::bufferPaddedSize(content_size); + return inner_buffers_[index_v].size(); } - // template - // void gpuUpdateBuffers(Predicate const& pred) - // { - // // TODO: Implement - // derived().onGpuUpdateBuffers(pred); - // } + template + [[nodiscard]] WGPUBuffer gpuLeafBuffer(std::size_t index) const + { + return leaf_buffers_[index_v][index]; + } - void gpuUpdateBuffers() + template + [[nodiscard]] WGPUBuffer gpuInnerBuffer(std::size_t index) const { - gpuUpdateBuffer(); - (gpuUpdateBuffer(), ...); + return inner_buffers_[index_v][index]; } template - void gpuUpdateBuffer() + [[nodiscard]] std::size_t gpuLeafBufferSize(std::size_t index) const { - WGPUBuffer& buffer = buffers_[index_v]; + return wgpuBufferGetSize(gpuLeafBuffer(index)); + } - std::size_t size = this->data_.template serializedBucketSize(); + template + [[nodiscard]] std::size_t gpuInnerBufferSize(std::size_t index) const + { + return wgpuBufferGetSize(gpuInnerBuffer(index)); + } - if (nullptr == buffer) { - double size_factor = sizeof(T) / static_cast(sizeof(Block)); + void gpuRead() + { + gpuReadLeaf(); + gpuReadInner(); + } - std::size_t content_size = - static_cast(size_factor * tree_buffer_size_); + void gpuReadLeaf() { (gpuReadLeaf(), ...); } - buffer = - compute::createBuffer(device_, "", content_size, - WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst, true); + void gpuReadInner() { (gpuReadInner(), ...); } - assert(nullptr != buffer); + template + void gpuRead() + { + gpuReadLeaf(); + gpuReadInner(); + } - void* buf = wgpuBufferGetMappedRange(buffer, 0, content_size); + template + void gpuReadLeaf() + { + // TODO: Implement + } - for (auto& bucket : this->data_.template iterBucket()) { - std::memcpy(buf, bucket.data.data(), size); - buf = static_cast(buf) + size; - bucket.modified = false; + template + void gpuReadInner() + { + // TODO: Implement + } + + bool gpuWrite() + { + bool a = gpuWriteLeaf(); + bool b = gpuWriteInner(); + return a || b; + } + + bool gpuWriteLeaf() { return (gpuWriteLeaf() | ...); } + + bool gpuWriteInner() { return (gpuWriteInner() | ...); } + + template + bool gpuWrite() + { + bool a = gpuWriteLeaf(); + bool b = gpuWriteInner(); + return a || b; + } + + template + bool gpuWriteLeaf() + { + // TODO: Make gpuWriteLeaf and gpuWriteInner with a single implementation. + + assert(nullptr != device_); + assert(nullptr != queue_); + + using Block = typename T::template LeafBlock; + + std::size_t const size = leaf_data_.template serializedSize(); + + auto& buffers = leaf_buffers_[index_v]; + + if (0 == size) { + for (auto& buf : buffers) { + wgpuBufferRelease(buf); } + bool empty = buffers.empty(); + buffers.clear(); + return !empty; + } + + constexpr std::size_t const bucket_size = + LeafData::template serializedBucketSize(); + + std::size_t const buckets_per_buffer = max_buffer_size_ / bucket_size; + std::size_t const buffer_size = bucket_size * buckets_per_buffer; + + std::size_t const num_buffers = 1 + (size - 1) / buffer_size; + + buffers.reserve(num_buffers); + + bool updated = false; + + auto it = leaf_data_.template beginBucket(); + auto last = leaf_data_.template endBucket(); - wgpuBufferUnmap(buffer); - } else { - std::size_t offset = 0; - for (auto it = this->data_.template beginBucket(), - last = this->data_.template endBucket(); - last != it; ++it, offset += size) { - if (it->modified) { - wgpuQueueWriteBuffer(queue_, buffer, offset, it->data.data(), size); - it->modified = false; + for (std::size_t i{}; num_buffers > i; ++i) { + if (buffers.size() <= i) { + updated = true; + + auto& buffer = buffers.emplace_back(compute::createBuffer( + device_, "", buffer_size, WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst, + true)); + + assert(nullptr != buffer); + + void* buf = wgpuBufferGetMappedRange(buffer, 0, buffer_size); + + for (std::size_t i{}; buckets_per_buffer > i && it != last; ++i, ++it) { + auto& [data, modified] = *it; + + std::memcpy(buf, data.data(), bucket_size); + buf = static_cast(static_cast(buf) + bucket_size); + modified = false; + } + + wgpuBufferUnmap(buffer); + } else { + WGPUBuffer& buffer = buffers[i]; + + std::size_t offset = 0; + for (std::size_t i{}; buckets_per_buffer > i && it != last; ++i, ++it) { + auto& [data, modified] = *it; + + if (modified) { + wgpuQueueWriteBuffer(queue_, buffer, offset, data.data(), bucket_size); + modified = false; + } + offset += bucket_size; } } } + + // FIXME: Probably do not want to release them but instead reuse later when needed, + // like how std::vector works. Need a function similar to `shrink_to_fit`. Also, need + // to keep track of which ones are "active" and not. + for (std::size_t i = num_buffers; buffers.size() > i; ++i) { + updated = true; + wgpuBufferRelease(buffers[i]); + } + buffers.resize(num_buffers); + + return updated; } - // - // Tree - // + template + bool gpuWriteInner() + { + // TODO: Make gpuWriteLeaf and gpuWriteInner with a single implementation. + + assert(nullptr != device_); + assert(nullptr != queue_); + + using Block = typename T::template InnerBlock; + + std::size_t const size = inner_data_.template serializedSize(); + + auto& buffers = inner_buffers_[index_v]; + + if (0 == size) { + for (auto& buf : buffers) { + wgpuBufferRelease(buf); + } + bool empty = buffers.empty(); + buffers.clear(); + return !empty; + } + + constexpr std::size_t const bucket_size = + InnerData::template serializedBucketSize(); + + std::size_t const buckets_per_buffer = max_buffer_size_ / bucket_size; + std::size_t const buffer_size = bucket_size * buckets_per_buffer; + + std::size_t const num_buffers = 1 + (size - 1) / buffer_size; + + buffers.reserve(num_buffers); + + bool updated = false; + + auto it = inner_data_.template beginBucket(); + auto last = inner_data_.template endBucket(); - [[nodiscard]] WGPUBuffer gpuTreeBuffer() const { return gpuBuffer(); } + for (std::size_t i{}; num_buffers > i; ++i) { + if (buffers.size() <= i) { + updated = true; - [[nodiscard]] std::size_t gpuTreeBufferSize() const { return gpuBufferSize(); } + auto& buffer = buffers.emplace_back(compute::createBuffer( + device_, "", buffer_size, WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst, + true)); - void swap(TreeData& other) + assert(nullptr != buffer); + + void* buf = wgpuBufferGetMappedRange(buffer, 0, buffer_size); + + for (std::size_t i{}; buckets_per_buffer > i && it != last; ++i, ++it) { + auto& [data, modified] = *it; + + std::memcpy(buf, data.data(), bucket_size); + buf = static_cast(static_cast(buf) + bucket_size); + modified = false; + } + + wgpuBufferUnmap(buffer); + } else { + WGPUBuffer& buffer = buffers[i]; + + std::size_t offset = 0; + for (std::size_t i{}; buckets_per_buffer > i && it != last; ++i, ++it) { + auto& [data, modified] = *it; + + if (modified) { + wgpuQueueWriteBuffer(queue_, buffer, offset, data.data(), bucket_size); + modified = false; + } + offset += bucket_size; + } + } + } + + // FIXME: Probably do not want to release them but instead reuse later when needed, + // like how std::vector works. Need a function similar to `shrink_to_fit`. Also, need + // to keep track of which ones are "active" and not. + for (std::size_t i = num_buffers; buffers.size() > i; ++i) { + updated = true; + wgpuBufferRelease(buffers[i]); + } + buffers.resize(num_buffers); + + return updated; + } + + protected: + [[nodiscard]] static constexpr bool leaf(pos_type block) noexcept { - using std::swap; - swap(static_cast(*this), static_cast(other)); - swap(instance_, other.instance_); - swap(adapter_, other.adapter_); - swap(device_, other.device_); - swap(queue_, other.queue_); - swap(buffers_, other.buffers_); + return Index::TYPE_BIT != (Index::TYPE_BIT & block); + } + + [[nodiscard]] static constexpr bool inner(pos_type block) noexcept + { + return !leaf(block); + } + + [[nodiscard]] static constexpr pos_type addLeafType(pos_type block) noexcept + { + return block; + } + + [[nodiscard]] static constexpr pos_type removeLeafType(pos_type block) noexcept + { + return block; + } + + [[nodiscard]] static constexpr pos_type addInnerType(pos_type block) noexcept + { + return Index::TYPE_BIT | block; + } + + [[nodiscard]] static constexpr pos_type removeInnerType(pos_type block) noexcept + { + return ~Index::TYPE_BIT & block; + } + + [[nodiscard]] std::size_t size() const { return leafSize() + innerSize(); } + + [[nodiscard]] std::size_t leafSize() const { return leaf_data_.size(); } + + [[nodiscard]] std::size_t innerSize() const { return inner_data_.size(); } + + void reserve(std::size_t cap) + { + leafReserve((cap + 1) / 2); + innerReserve(cap / 2); + } + + void leafReserve(std::size_t cap) { leaf_data_.reserve(cap); } + + void innerReserve(std::size_t cap) { inner_data_.reserve(cap); } + + void clear() + { + leafClear(); + innerClear(); + } + + void leafClear() { leaf_data_.clear(); } + + void innerClear() { inner_data_.clear(); } + + [[nodiscard]] pos_type create(bool leaf) { return leaf ? leafCreate() : innerCreate(); } + + [[nodiscard]] pos_type leafCreate() { return addLeafType(leaf_data_.create()); } + + [[nodiscard]] pos_type innerCreate() { return addInnerType(inner_data_.create()); } + + [[nodiscard]] pos_type createThreadSafe(bool leaf) + { + return leaf ? leafCreateThreadSafe() : innerCreateThreadSafe(); + } + + [[nodiscard]] pos_type leafCreateThreadSafe() + { + return addLeafType(leaf_data_.createThreadSafe()); + } + + [[nodiscard]] pos_type innerCreateThreadSafe() + { + return addInnerType(inner_data_.createThreadSafe()); + } + + void erase(pos_type block) { leaf(block) ? leafErase(block) : innerErase(block); } + + void leafErase(pos_type block) { leaf_data_.eraseBlock(removeLeafType(block)); } + + void innerErase(pos_type block) { inner_data_.eraseBlock(removeInnerType(block)); } + + template + [[nodiscard]] T& leafBlock(pos_type block) + { + assert(leafExists(block)); + return leaf_data_.template get(removeLeafType(block)); + } + + template + [[nodiscard]] T const& leafBlock(pos_type block) const + { + return leaf_data_.template get(removeLeafType(block)); + } + + template + [[nodiscard]] T& innerBlock(pos_type block) + { + assert(innerExists(block)); + return inner_data_.template get(removeInnerType(block)); + } + + template + [[nodiscard]] T const& innerBlock(pos_type block) const + { + return inner_data_.template get(removeInnerType(block)); } private: - WGPULimits requiredLimits(WGPUAdapter adapter) + [[nodiscard]] WGPULimits requiredLimits(WGPUAdapter adapter) { - WGPULimits supported = WGPU_LIMITS_INIT; - supported.nextInChain = nullptr; - wgpuAdapterGetLimits(adapter, &supported); + WGPULimits required = WGPU_LIMITS_INIT; + WGPULimits supported = WGPU_LIMITS_INIT; - WGPULimits required = WGPU_LIMITS_INIT; + wgpuAdapterGetLimits(adapter, &supported); // These two limits are different because they are "minimum" limits, // they are the only ones we may forward from the adapter's supported limits. required.minUniformBufferOffsetAlignment = supported.minUniformBufferOffsetAlignment; required.minStorageBufferOffsetAlignment = supported.minStorageBufferOffsetAlignment; - tree_buffer_size_ = - std::min(tree_buffer_size_, static_cast(supported.maxBufferSize)); - tree_buffer_size_ = - std::min(tree_buffer_size_, + max_buffer_size_ = + std::min(max_buffer_size_, static_cast(supported.maxBufferSize)); + max_buffer_size_ = + std::min(max_buffer_size_, static_cast(supported.maxStorageBufferBindingSize)); - required.maxBufferSize = tree_buffer_size_; - required.maxStorageBufferBindingSize = tree_buffer_size_; + required.maxBufferSize = max_buffer_size_; + required.maxStorageBufferBindingSize = max_buffer_size_; required.maxComputeWorkgroupStorageSize = 16352; required.maxComputeInvocationsPerWorkgroup = 256; @@ -344,28 +631,24 @@ class TreeData required.maxComputeWorkgroupsPerDimension = 65535; required.maxUniformBuffersPerShaderStage = 12; - required.maxUniformBufferBindingSize = 16 << 10; // (16 KiB) + required.maxUniformBufferBindingSize = 65536; return required; } protected: - WGPUInstance instance_ = nullptr; - WGPUAdapter adapter_ = nullptr; - WGPUDevice device_ = nullptr; - WGPUQueue queue_ = nullptr; - std::array buffers_{}; - - std::size_t tree_buffer_size_ = 2'147'483'648; -}; + LeafData leaf_data_; + InnerData inner_data_; -template -void swap(TreeData& lhs, TreeData& rhs) -{ - lhs.swap(rhs); -} + WGPUInstance instance_ = nullptr; + WGPUAdapter adapter_ = nullptr; + WGPUDevice device_ = nullptr; + WGPUQueue queue_ = nullptr; + std::array, NumBuffers> leaf_buffers_{}; + std::array, NumBuffers> inner_buffers_{}; -#endif + std::size_t max_buffer_size_ = 1'073'741'824 / 2; +}; } // namespace ufo #endif // UFO_CONTAINER_TREE_DATA_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/distance_node.hpp b/lib/container/include/ufo/container/tree/distance_node.hpp deleted file mode 100644 index 77680c6c..00000000 --- a/lib/container/include/ufo/container/tree/distance_node.hpp +++ /dev/null @@ -1,152 +0,0 @@ -/*! - * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown - * - * @author Daniel Duberg (dduberg@kth.se) - * @see https://github.com/UnknownFreeOccupied/ufomap - * @version 1.0 - * @date 2022-05-13 - * - * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * - * BSD 3-Clause License - * - * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. 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. - * - * 3. 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. - * - * THIS 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. - */ - -#ifndef UFO_CONTAINER_TREE_DISTANCE_NODE_HPP -#define UFO_CONTAINER_TREE_DISTANCE_NODE_HPP - -// UFO -#include - -// STL -#include -#include - -namespace ufo -{ -template -struct TreeDistanceNode : public TreeNode { - float distance{}; - - constexpr TreeDistanceNode() = default; - - constexpr TreeDistanceNode(TreeNode const& node, float distance = 0.0f) - : TreeNode(node), distance(distance) - { - } - - constexpr TreeDistanceNode(TreeCode const& code, TreeIndex const& index, - float distance = 0.0f) noexcept - : TreeNode(code, index), distance(distance) - { - } -}; - -// -// Deduction guide -// - -template -TreeDistanceNode(TreeNode) -> TreeDistanceNode; - -template -TreeDistanceNode(TreeNode, float) -> TreeDistanceNode; - -template -TreeDistanceNode(TreeCode, TreeIndex) -> TreeDistanceNode; - -template -TreeDistanceNode(TreeCode, TreeIndex, float) -> TreeDistanceNode; - -template -constexpr bool operator==(TreeDistanceNode const& lhs, - TreeDistanceNode const& rhs) -{ - return lhs.distance == rhs.distance && - static_cast const&>(lhs) == static_cast const&>(rhs); -} - -template -constexpr bool operator!=(TreeDistanceNode const& lhs, - TreeDistanceNode const& rhs) -{ - return !(lhs == rhs); -} - -template -constexpr bool operator<(TreeDistanceNode const& lhs, - TreeDistanceNode const& rhs) -{ - return lhs.distance < rhs.distance; -} - -template -constexpr bool operator<=(TreeDistanceNode const& lhs, - TreeDistanceNode const& rhs) -{ - return lhs.distance <= rhs.distance; -} - -template -constexpr bool operator>(TreeDistanceNode const& lhs, - TreeDistanceNode const& rhs) -{ - return lhs.distance > rhs.distance; -} - -template -constexpr bool operator>=(TreeDistanceNode const& lhs, - TreeDistanceNode const& rhs) -{ - return lhs.distance >= rhs.distance; -} - -template -std::ostream& operator<<(std::ostream& os, TreeDistanceNode const& node) -{ - return os << "Node: (" << static_cast const&>(node) - << "), Distance: " << node.distance; -} - -using BinaryDistanceNode = TreeDistanceNode<1>; -using QuadDistanceNode = TreeDistanceNode<2>; -using OctDistanceNode = TreeDistanceNode<3>; -using HexDistanceNode = TreeDistanceNode<4>; -} // namespace ufo - -template -struct std::hash> { - std::size_t operator()(ufo::TreeDistanceNode const& node) const - { - return hash(static_cast const&>(node)); - } -}; - -#endif // UFO_CONTAINER_TREE_DISTANCE_NODE_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/index.hpp b/lib/container/include/ufo/container/tree/index.hpp old mode 100644 new mode 100755 index 0b3d3a12..5919f676 --- a/lib/container/include/ufo/container/tree/index.hpp +++ b/lib/container/include/ufo/container/tree/index.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -60,18 +60,25 @@ struct TreeIndex { // 32 bits Y low levels // Dim bits for offset - using pos_t = std::uint32_t; - using offset_t = std::uint32_t; + using pos_type = std::uint32_t; + using offset_type = std::uint32_t; - static constexpr pos_t const NULL_POS = std::numeric_limits::max(); - static constexpr pos_t const PROCESSING_POS = NULL_POS - 1; + static constexpr pos_type const TYPE_BIT = + pos_type(1) << (std::numeric_limits::digits - 1); + static constexpr pos_type const NULL_POS = std::numeric_limits::max(); + static constexpr pos_type const PROCESSING_POS = NULL_POS - 1; + static constexpr pos_type const INVALID_POS = NULL_POS - 2; + static constexpr pos_type const MAX_VALID_POS = INVALID_POS - 1; - pos_t pos{NULL_POS}; - offset_t offset{0}; + pos_type pos{NULL_POS}; + offset_type offset{0}; constexpr TreeIndex() noexcept = default; - constexpr TreeIndex(pos_t pos, offset_t offset) noexcept : pos(pos), offset(offset) {} + constexpr TreeIndex(pos_type pos, offset_type offset) noexcept + : pos(pos), offset(offset) + { + } friend void swap(TreeIndex& lhs, TreeIndex& rhs) noexcept { @@ -86,12 +93,12 @@ struct TreeIndex { constexpr bool operator!=(TreeIndex rhs) const { return !(operator==(rhs)); } - [[nodiscard]] constexpr TreeIndex sibling(offset_t offset) const + [[nodiscard]] constexpr TreeIndex sibling(offset_type offset) const { return {pos, offset}; } - [[nodiscard]] constexpr bool valid() const { return PROCESSING_POS > pos; } + [[nodiscard]] constexpr bool valid() const { return MAX_VALID_POS >= pos; } }; inline std::ostream& operator<<(std::ostream& out, TreeIndex index) diff --git a/lib/map/include/ufo/map/semantic/semantic.hpp b/lib/container/include/ufo/container/tree/inner_block.hpp old mode 100644 new mode 100755 similarity index 59% rename from lib/map/include/ufo/map/semantic/semantic.hpp rename to lib/container/include/ufo/container/tree/inner_block.hpp index b1bc9717..6f725dba --- a/lib/map/include/ufo/map/semantic/semantic.hpp +++ b/lib/container/include/ufo/container/tree/inner_block.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -39,61 +39,50 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UFO_MAP_SEMANTIC_HPP -#define UFO_MAP_SEMANTIC_HPP +#ifndef UFO_CONTAINER_TREE_INNER_BLOCK_HPP +#define UFO_CONTAINER_TREE_INNER_BLOCK_HPP // UFO -#include -#include -#include -#include +// #include +#include // STL -#include +#include +#include +#include +#include namespace ufo { -using SemanticRange = Range; -using SemanticRangeSet = RangeSet; -using SemanticRangeMap = RangeMap; +template +struct TreeInnerBlock { + using modified_type = std::uint16_t; -struct Semantic { - label_t label = 0; - value_t value = 0; + static_assert(sizeof(std::atomic) == sizeof(modified_type)); + static_assert(sizeof(std::atomic) == sizeof(TreeIndex::pos_type)); - constexpr Semantic() noexcept = default; + alignas(std::atomic_ref::required_alignment) + std::array children; - constexpr Semantic(label_t label, value_t value = 0) noexcept - : label(label), value(value) - { - } -}; - -constexpr bool operator==(Semantic lhs, Semantic rhs) -{ - return lhs.label == rhs.label && lhs.value == rhs.value; -} + // Code to the first node of the block + // std::array::code_type, 3> code; -constexpr bool operator!=(Semantic lhs, Semantic rhs) { return !(lhs == rhs); } + TreeIndex::pos_type parent_block; + std::uint8_t parent_offset; -constexpr bool operator<(Semantic lhs, Semantic rhs) -{ - return lhs.label < rhs.label || (lhs.label == rhs.label && lhs.value < rhs.value); -} + std::uint8_t depth; -constexpr bool operator<=(Semantic lhs, Semantic rhs) { return !(rhs < lhs); } + // Bit set saying if the node corresponding to the bit has been modified + alignas(std::atomic_ref::required_alignment) modified_type modified; +}; -constexpr bool operator>(Semantic lhs, Semantic rhs) { return rhs < lhs; } +static_assert(24 == sizeof(TreeInnerBlock<2, 4>)); +static_assert(40 == sizeof(TreeInnerBlock<3, 8>)); +static_assert(72 == sizeof(TreeInnerBlock<4, 16>)); -constexpr bool operator>=(Semantic lhs, Semantic rhs) { return !(lhs < rhs); } +static_assert(std::is_standard_layout_v>); +static_assert(std::is_standard_layout_v>); +static_assert(std::is_standard_layout_v>); } // namespace ufo -namespace std -{ -inline std::ostream& operator<<(std::ostream& out, ufo::Semantic s) -{ - return out << s.label << ": " << s.value; -} -} // namespace std - -#endif // UFO_MAP_SEMANTIC_HPP \ No newline at end of file +#endif // UFO_CONTAINER_TREE_INNER_BLOCK_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/iterator.hpp b/lib/container/include/ufo/container/tree/iterator.hpp old mode 100644 new mode 100755 index 765a9ded..f09bedbb --- a/lib/container/include/ufo/container/tree/iterator.hpp +++ b/lib/container/include/ufo/container/tree/iterator.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -65,8 +65,9 @@ class TreeIterator private: static constexpr std::size_t const BF = Tree::branchingFactor(); - using Node = typename Tree::Node; - using offset_t = typename Tree::offset_t; + using Node = typename Tree::Node; + using offset_type = typename Tree::offset_type; + using depth_type = typename Tree::depth_type; public: // @@ -81,36 +82,22 @@ class TreeIterator constexpr TreeIterator() = default; - TreeIterator(Tree* t, Node const& node, bool only_leaves, bool only_exists) - : t_(t) - , root_(node) + TreeIterator(Tree const* tree, Node node, bool only_leaves, bool only_exists) + : tree_(tree) , cur_(node) - , only_leaves_(only_leaves) - , only_exists_(only_exists) + , start_(tree->depth(node)) + , nextNode(nextNodeFun(only_leaves, only_exists)) { - if (only_exists_ && !t_->exists(root_)) { - root_ = {}; - cur_ = {}; - return; + if (only_exists && !tree_->exists(cur_)) { + cur_ = {}; + } else if (only_leaves && tree_->isParent(node.index)) { + cur_ = nextNode(*tree_, cur_, start_); } - - if (returnable(cur_)) { - return; - } - - nextNode(); } TreeIterator& operator++() { - if (!only_exists_) { - auto min_depth = t_->depth(cur_.code); - auto depth = t_->depth(cur_.index); - while (min_depth < depth && t_->isParent(cur_.index)) { - cur_.index = t_->child(cur_.index, cur_.code.offset(--depth)); - } - } - nextNode(); + cur_ = nextNode(*tree_, cur_, start_); return *this; } @@ -127,7 +114,7 @@ class TreeIterator friend bool operator==(TreeIterator const& lhs, TreeIterator const& rhs) { - return lhs.cur_ == rhs.cur_; + return lhs.cur_.code == rhs.cur_.code; } friend bool operator!=(TreeIterator const& lhs, TreeIterator const& rhs) @@ -136,104 +123,85 @@ class TreeIterator } private: - [[nodiscard]] bool returnable(Node const& node) const - { - return !only_leaves_ || t_->isLeaf(node.index); - } - - [[nodiscard]] bool traversable(Node const& node) const - { - return t_->isParent(node.index) || (!only_exists_ && !t_->isPureLeaf(node.code)); - } - - [[nodiscard]] bool exists(Node const& node) const - { - return only_exists_ || t_->code(node.index) == node.code; - } - - [[nodiscard]] Node sibling(Node const& node, offset_t sibling_index) const + [[nodiscard]] static auto nextNodeFun(bool only_leaves, bool only_exists) { - return Node(t_->sibling(node.code, sibling_index), - exists(node) ? t_->sibling(node.index, sibling_index) : node.index); + if (only_leaves && only_exists) { + return &TreeIterator::next; + } else if (only_leaves && !only_exists) { + return &TreeIterator::next; + } else if (!only_leaves && only_exists) { + return &TreeIterator::next; + } else { + return &TreeIterator::next; + } } - [[nodiscard]] Node child(Node const& node, offset_t child_index) const + template + [[nodiscard]] static Node next(Tree const& tree, Node node, depth_type start) { - return Node( - t_->child(node.code, child_index), - t_->isParent(node.index) ? t_->child(node.index, child_index) : node.index); - } + if constexpr (!OnlyLeaves) { + // Traverse down the branch + + if constexpr (OnlyExists) { + if (tree.isParent(node.index)) { + node.code = node.code.firstborn(); + node.index = tree.child(node.index, 0); + return node; + } + } else { + if (0 < node.code.depth()) { + node.code = node.code.firstborn(); + if (tree.isParent(node.index)) { + node.index = tree.child(node.index, 0); + } + return node; + } + } + } - [[nodiscard]] Node parent(Node const& node) const - { - return Node(t_->parent(node.code), - exists(node) ? t_->parent(node.index) : node.index); - } + // Find next branch - [[nodiscard]] offset_t offset(Node const& node) const { return node.code.offset(); } + while (BF - 1 <= node.code.offset()) { + node.code = node.code.parent(); + } - void nextNode() - { - if (traversable(cur_)) { - cur_ = child(cur_, 0); - if (nextNodeDownwards()) { - return; - } + if (start <= node.code.depth()) { + return Node{}; } - while (root_ != cur_) { - auto branch = offset(cur_); - if (BF - 1 == branch) { - cur_ = parent(cur_); - continue; - } + node.code = node.code.nextSibling(); + node.index = tree.ancestor(node.index, node.code.depth()); - cur_ = sibling(cur_, branch + 1); + if constexpr (OnlyExists) { + ++node.index.offset; + } else { + node.index.offset += node.code.depth() == tree.depth(node.index) ? 1 : 0; + } - if (returnable(cur_)) { - return; - } + if constexpr (OnlyLeaves) { + // Adjust index - if (traversable(cur_)) { - cur_ = child(cur_, 0); - if (nextNodeDownwards()) { - return; - } + while (tree.isParent(node.index)) { + node.index = tree.child(node.index, 0); } - } - // We have visited all nodes - root_ = {}; - cur_ = {}; - } - - /*! - * @brief - * - * @return true if a new node was found, false otherwise. - */ - bool nextNodeDownwards() - { - while (true) { - if (returnable(cur_)) { - return true; - } else if (traversable(cur_)) { - cur_ = child(cur_, 0); + if constexpr (OnlyExists) { + node.code.setDepth(tree.depth(node.index)); } else { - break; + node.code.setDepth(0); } } - return false; + + return node; } private: - Tree* t_ = nullptr; + Tree const* tree_; - Node root_{}; - Node cur_{}; + Node cur_{}; + depth_type start_; - bool only_leaves_{}; - bool only_exists_{}; + decltype(&TreeIterator::next) nextNode; }; } // namespace ufo diff --git a/lib/container/include/ufo/container/tree/key.hpp b/lib/container/include/ufo/container/tree/key.hpp old mode 100644 new mode 100755 index 9be06a39..21ebe748 --- a/lib/container/include/ufo/container/tree/key.hpp +++ b/lib/container/include/ufo/container/tree/key.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -43,8 +43,8 @@ #define UFO_CONTAINER_TREE_KEY_HPP // UFO -#include #include +#include // STL #include @@ -60,10 +60,10 @@ template class TreeKey : public Vec { public: - using key_t = std::uint32_t; - using Key = Vec; - using depth_t = key_t; - using size_type = std::size_t; + using key_type = std::uint32_t; + using Key = Vec; + using depth_type = key_type; + using size_type = std::size_t; public: /************************************************************************************** @@ -75,7 +75,7 @@ class TreeKey : public Vec constexpr TreeKey() noexcept = default; constexpr TreeKey(TreeKey const&) noexcept = default; - constexpr TreeKey(Key key, depth_t depth) : Key(key), depth_(depth) {} + constexpr TreeKey(Key key, depth_type depth) : Key(key), depth_(depth) {} constexpr explicit TreeKey(Key key) : TreeKey(key, 0) {} @@ -93,14 +93,14 @@ class TreeKey : public Vec | | **************************************************************************************/ - [[nodiscard]] static constexpr depth_t maxDepth() noexcept + [[nodiscard]] static constexpr depth_type maxDepth() noexcept { // All the time you have to leave the space. // Shifting with `maxDepth()` causes problems when - // `std::numeric_limits::digits <= maxDepth()` because you are trying to + // `std::numeric_limits::digits <= maxDepth()` because you are trying to // shift more bits than are allowed and has a well-defined behaviour in C++. // Therefore, we have this check; otherwise, would cause "shift count overflow". - return std::numeric_limits::digits - 1; + return std::numeric_limits::digits - 1; } /************************************************************************************** @@ -111,9 +111,9 @@ class TreeKey : public Vec [[nodiscard]] constexpr bool valid() const noexcept { return maxDepth() >= depth_; } - [[nodiscard]] constexpr depth_t depth() const noexcept { return depth_; } + [[nodiscard]] constexpr depth_type depth() const noexcept { return depth_; } - [[nodiscard]] constexpr TreeKey toDepth(depth_t depth) const + [[nodiscard]] constexpr TreeKey toDepth(depth_type depth) const { assert(maxDepth() >= depth); @@ -122,12 +122,12 @@ class TreeKey : public Vec return ret; } - /*! + /** * @brief Change the depth of the key. * * @note This will change the x, y, z components of the key. */ - constexpr void setDepth(depth_t depth) noexcept + constexpr void setDepth(depth_type depth) noexcept { assert(maxDepth() >= depth); @@ -141,13 +141,15 @@ class TreeKey : public Vec // TODO: Add methods from Code - [[nodiscard]] constexpr key_t offset(depth_t depth) const noexcept + [[nodiscard]] constexpr key_type offset() const noexcept { return offset(depth_); } + + [[nodiscard]] constexpr key_type offset(depth_type depth) const noexcept { assert(maxDepth() >= depth); assert(depth_ <= depth); - auto v = (*this >> (depth - depth_)) & key_t(1); - key_t ret = v[0]; + auto v = (*this >> (depth - depth_)) & key_type(1); + key_type ret = v[0]; for (std::size_t i = 1; Dim > i; ++i) { ret |= v[i] << i; } @@ -172,7 +174,7 @@ class TreeKey : public Vec } private: - depth_t depth_{}; + depth_type depth_{}; }; using BinaryKey = TreeKey<1>; diff --git a/lib/map/include/ufo/map/view_direction/block.hpp b/lib/container/include/ufo/container/tree/leaf_block.hpp old mode 100644 new mode 100755 similarity index 69% rename from lib/map/include/ufo/map/view_direction/block.hpp rename to lib/container/include/ufo/container/tree/leaf_block.hpp index bc1f4739..340b97e9 --- a/lib/map/include/ufo/map/view_direction/block.hpp +++ b/lib/container/include/ufo/container/tree/leaf_block.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -38,44 +38,41 @@ * 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. */ -#ifndef UFO_MAP_VIEW_DIRECTION_BLOCK_HPP -#define UFO_MAP_VIEW_DIRECTION_BLOCK_HPP + +#ifndef UFO_CONTAINER_TREE_LEAF_BLOCK_HPP +#define UFO_CONTAINER_TREE_LEAF_BLOCK_HPP // UFO -#include -#include +#include // STL -#include -#include +#include #include -#include +#include #include namespace ufo { template -struct ViewDirectionBlock { - using Direction = Vec; - - constexpr ViewDirectionBlock() = default; - constexpr ViewDirectionBlock(ViewDirectionBlock const&) = default; - constexpr ViewDirectionBlock& operator=(ViewDirectionBlock const&) = default; +struct TreeLeafBlock { + using modified_type = std::uint16_t; - constexpr Direction& operator[](std::size_t pos) - { - assert(BF > pos); - return view_direction[pos]; - } + static_assert(sizeof(std::atomic) == sizeof(modified_type)); - constexpr Direction const& operator[](std::size_t pos) const - { - assert(BF > pos); - return view_direction[pos]; - } + TreeIndex::pos_type parent_block; + std::uint8_t parent_offset; - std::array view_direction{}; + // Bit saying if the node corresponding to the bit has been modified + alignas(std::atomic_ref::required_alignment) modified_type modified; }; + +static_assert(8 == sizeof(TreeLeafBlock<2, 4>)); +static_assert(8 == sizeof(TreeLeafBlock<3, 8>)); +static_assert(8 == sizeof(TreeLeafBlock<4, 16>)); + +static_assert(std::is_standard_layout_v>); +static_assert(std::is_standard_layout_v>); +static_assert(std::is_standard_layout_v>); } // namespace ufo -#endif // UFO_MAP_VIEW_DIRECTION_BLOCK_HPP \ No newline at end of file +#endif // UFO_CONTAINER_TREE_LEAF_BLOCK_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/map/block.hpp b/lib/container/include/ufo/container/tree/map/block.hpp old mode 100644 new mode 100755 index 6ee4a143..43ac56d9 --- a/lib/container/include/ufo/container/tree/map/block.hpp +++ b/lib/container/include/ufo/container/tree/map/block.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -43,54 +43,21 @@ #define UFO_CONTAINER_TREE_MAP_BLOCK_HPP // UFO -#include -#include -#include -#include +#include +#include // STL -#include #include -#include -#include -#include namespace ufo { -template -struct TreeMapBlock : public TreeBlock { - using Base = TreeBlock; +template +struct TreeMapBlock { + template + using LeafBlock = TreeMapLeafBlock; - using Code = TreeCode; - using length_t = typename Base::length_t; - using Length = typename Base::Length; - using Point = typename Base::Point; - using Bounds = AABB; - using scalar_type = typename Point::value_type; - using value_type = std::pair; - using container_type = std::list; - - static constexpr auto const MIN = - Point(std::numeric_limits::lowest()); - static constexpr auto const MAX = - Point(std::numeric_limits::max()); - - std::array bounds = createArray(Bounds(MAX, MIN)); - std::array values; - - constexpr TreeMapBlock() = default; - - constexpr TreeMapBlock(TreeIndex::pos_t parent_block, Code code, Point center, - Length half_length) - : Base(parent_block, code, center, half_length) - { - } - - constexpr TreeMapBlock(TreeIndex::pos_t parent_block, TreeMapBlock const& parent, - std::size_t offset, Length half_length) - : Base(parent_block, parent, offset, half_length) - { - } + template + using InnerBlock = TreeMapInnerBlock; }; } // namespace ufo diff --git a/lib/container/include/ufo/container/tree/map/inner_block.hpp b/lib/container/include/ufo/container/tree/map/inner_block.hpp new file mode 100755 index 00000000..5ea32cd8 --- /dev/null +++ b/lib/container/include/ufo/container/tree/map/inner_block.hpp @@ -0,0 +1,68 @@ +/** + * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown + * + * @author Daniel Duberg (dduberg@kth.se) + * @see https://github.com/UnknownFreeOccupied/ufomap + * @version 1.0 + * @date 2022-05-13 + * + * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology + * + * BSD 3-Clause License + * + * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. 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. + * + * THIS 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. + */ + +#ifndef UFO_CONTAINER_TREE_MAP_INNER_BLOCK_HPP +#define UFO_CONTAINER_TREE_MAP_INNER_BLOCK_HPP + +// UFO +#include +#include + +// STL +#include +#include +#include +#include + +namespace ufo +{ +template +struct TreeMapInnerBlock { + using value_type = std::pair const, T>; + using container_type = std::list; + using bounds_type = AABB; + + std::array values; + std::array bounds; +}; +} // namespace ufo + +#endif // UFO_CONTAINER_TREE_MAP_INNER_BLOCK_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/map/iterator.hpp b/lib/container/include/ufo/container/tree/map/iterator.hpp old mode 100644 new mode 100755 index fc05d6bf..be11de17 --- a/lib/container/include/ufo/container/tree/map/iterator.hpp +++ b/lib/container/include/ufo/container/tree/map/iterator.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -161,7 +161,7 @@ class TreeMapIterator last_ = {}; } - /*! + /** * @brief * * @return true if a new node was found, false otherwise. diff --git a/lib/vision/include/ufo/vision/color_map.hpp b/lib/container/include/ufo/container/tree/map/leaf_block.hpp old mode 100644 new mode 100755 similarity index 77% rename from lib/vision/include/ufo/vision/color_map.hpp rename to lib/container/include/ufo/container/tree/map/leaf_block.hpp index 3c4b8ca6..a0b57879 --- a/lib/vision/include/ufo/vision/color_map.hpp +++ b/lib/container/include/ufo/container/tree/map/leaf_block.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -39,24 +39,30 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UFO_VISION_COLOR_MAP_HPP -#define UFO_VISION_COLOR_MAP_HPP +#ifndef UFO_CONTAINER_TREE_MAP_LEAF_BLOCK_HPP +#define UFO_CONTAINER_TREE_MAP_LEAF_BLOCK_HPP // UFO -#include -#include -#include -#include +#include +#include + +// STL +#include +#include +#include +#include namespace ufo { -template -[[nodiscard]] constexpr Color colorMap(std::array const& color_map, T value, - T min_value, T max_value) -{ - T v = (value - min_value) / (max_value - min_value); - return color_map[static_cast(v * (N - 1))]; -} +template +struct TreeMapLeafBlock { + using value_type = std::pair const, T>; + using container_type = std::list; + using bounds_type = AABB; + + std::array values; + std::array bounds; +}; } // namespace ufo -#endif // UFO_VISION_COLOR_MAP_HPP \ No newline at end of file +#endif // UFO_CONTAINER_TREE_MAP_LEAF_BLOCK_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/map/nearest_iterator.hpp b/lib/container/include/ufo/container/tree/map/nearest_iterator.hpp old mode 100644 new mode 100755 index 838f7ab5..6b020af9 --- a/lib/container/include/ufo/container/tree/map/nearest_iterator.hpp +++ b/lib/container/include/ufo/container/tree/map/nearest_iterator.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/tree/map/query_iterator.hpp b/lib/container/include/ufo/container/tree/map/query_iterator.hpp old mode 100644 new mode 100755 index 7ed3f55d..c7b1bc69 --- a/lib/container/include/ufo/container/tree/map/query_iterator.hpp +++ b/lib/container/include/ufo/container/tree/map/query_iterator.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -164,7 +164,7 @@ class TreeMapQueryIterator return tm_->isParent(node) && Filter::traversable(pred_, *tm_, tm_->node(node)); } - /*! + /** * @brief * * @return true if a new value was found, false otherwise. @@ -175,7 +175,7 @@ class TreeMapQueryIterator return it_ != last_; } - /*! + /** * @brief * * @return true if a new node was found, false otherwise. @@ -201,7 +201,7 @@ class TreeMapQueryIterator return false; } - /*! + /** * @brief * * @return true if a new node was found, false otherwise. diff --git a/lib/container/include/ufo/container/tree/map/query_nearest_iterator.hpp b/lib/container/include/ufo/container/tree/map/query_nearest_iterator.hpp old mode 100644 new mode 100755 index bec7f6fa..52d8416b --- a/lib/container/include/ufo/container/tree/map/query_nearest_iterator.hpp +++ b/lib/container/include/ufo/container/tree/map/query_nearest_iterator.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/tree/nearest_iterator.hpp b/lib/container/include/ufo/container/tree/nearest_iterator.hpp old mode 100644 new mode 100755 index 798750cb..bdf5f3e8 --- a/lib/container/include/ufo/container/tree/nearest_iterator.hpp +++ b/lib/container/include/ufo/container/tree/nearest_iterator.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/tree/node.hpp b/lib/container/include/ufo/container/tree/node.hpp old mode 100644 new mode 100755 index e79a1505..645fc9c6 --- a/lib/container/include/ufo/container/tree/node.hpp +++ b/lib/container/include/ufo/container/tree/node.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) diff --git a/lib/container/include/ufo/container/tree/predicate.hpp b/lib/container/include/ufo/container/tree/predicate.hpp old mode 100644 new mode 100755 index c2889071..b7661ecf --- a/lib/container/include/ufo/container/tree/predicate.hpp +++ b/lib/container/include/ufo/container/tree/predicate.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -45,38 +45,83 @@ // UFO #include #include +#include #include #include #include -#include +#include #include -#include #include +#include #include +#include #include +#include #include -#include #include -#include #include #include #include #include #include -#include +#include #include #include -#include #include -#include -#include +#include namespace ufo::pred { -constexpr PureLeaf operator!(Inner) { return {}; } -constexpr Inner operator!(PureLeaf) { return {}; } -constexpr Parent operator!(Leaf) { return {}; } -constexpr Leaf operator!(Parent) { return {}; } +template +[[nodiscard]] constexpr auto operator!(And const& p) noexcept +{ + return std::apply([](auto&&... preds) { return Or(!preds...); }, p.preds); +} + +template +[[nodiscard]] constexpr auto operator!(Or const& p) noexcept +{ + return std::apply([](auto&&... preds) { return And(!preds...); }, p.preds); +} + +template +[[nodiscard]] constexpr auto operator!(IfThen const& p) noexcept +{ + return And(p.pre, !p.post); +} + +template +[[nodiscard]] constexpr Xor operator!( + Iff const& p) noexcept +{ + return Xor(p.left, p.right); +} + +template +[[nodiscard]] constexpr Iff operator!( + Xor const& p) noexcept +{ + return Iff(p.left, p.right); +} + +template +[[nodiscard]] constexpr Disjoint operator!( + Intersects const& p) noexcept +{ + return Disjoint(p.geometry); +} + +template +[[nodiscard]] constexpr Intersects operator!( + Disjoint const& p) noexcept +{ + return Intersects(p.geometry); +} + +[[nodiscard]] constexpr PureLeaf operator!(Inner) noexcept { return {}; } +[[nodiscard]] constexpr Inner operator!(PureLeaf) noexcept { return {}; } +[[nodiscard]] constexpr Parent operator!(Leaf) noexcept { return {}; } +[[nodiscard]] constexpr Leaf operator!(Parent) noexcept { return {}; } } // namespace ufo::pred #endif // UFO_CONTAINER_TREE_PREDICATE_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/predicate/and.hpp b/lib/container/include/ufo/container/tree/predicate/and.hpp old mode 100644 new mode 100755 index ab06ac62..801c87c7 --- a/lib/container/include/ufo/container/tree/predicate/and.hpp +++ b/lib/container/include/ufo/container/tree/predicate/and.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -53,142 +53,101 @@ namespace ufo::pred { -template +template struct And { - And(Preds&&... preds) : preds(std::forward(preds)...) {} - And(Preds const&... preds) : preds(preds...) {} + And(Preds&&... preds) noexcept : preds(std::forward(preds)...) {} std::tuple preds; }; -template > && - !is_specialization_of_v>, - bool> = true> -constexpr And, remove_cvref_t> operator&&( - PredLeft&& p1, PredRight&& p2) -{ - return {std::forward(p1), std::forward(p2)}; -} - -template -constexpr And operator&&(And&& p1, - And&& p2) -{ - return std::apply( - [](auto&&... xs) { - return And(std::forward(xs)...); - }, - std::tuple_cat(std::move(p1.preds), std::move(p2.preds))); -} - -template -constexpr And operator&&(And const& p1, - And const& p2) -{ - return std::apply( - [](auto&&... xs) { - return And(std::forward(xs)...); - }, - std::tuple_cat(p1.preds, p2.preds)); -} - -template >, - bool> = true> -constexpr And> operator&&(And&& p1, - PredRight&& p2) -{ - return std::apply( - [](auto&&... xs) { - return And>( - std::forward(xs)...); - }, - std::tuple_cat(std::move(p1.preds), std::make_tuple(std::forward(p2)))); -} - -template >, - bool> = true> -constexpr And> operator&&( - And const& p1, PredRight&& p2) +namespace detail { - return std::apply( - [](auto&&... xs) { - return And>( - std::forward(xs)...); - }, - std::tuple_cat(p1.preds, std::make_tuple(std::forward(p2)))); -} - -template < - class PredLeft, class... PredsRight, - std::enable_if_t>, bool> = true> -constexpr And, PredsRight...> operator&&(PredLeft&& p1, - And&& p2) +template +[[nodiscard]] constexpr auto getAndTuple(T&& t) noexcept { - return std::apply( - [](auto&&... xs) { - return And, PredsRight...>( - std::forward(xs)...); - }, - std::tuple_cat(std::make_tuple(std::forward(p1)), std::move(p2.preds))); + if constexpr (is_specialization_of_v>) { + return std::forward(t).preds; + } else { + return std::forward_as_tuple(std::forward(t)); + } } +} // namespace detail -template < - class PredLeft, class... PredsRight, - std::enable_if_t>, bool> = true> -constexpr And, PredsRight...> operator&&( - PredLeft&& p1, And const& p2) +template +[[nodiscard]] constexpr auto operator&&(PredLeft&& p1, PredRight&& p2) noexcept { return std::apply( - [](auto&&... xs) { - return And, PredsRight...>( - std::forward(xs)...); + [](auto&&... preds) { + return And...>( + std::forward(preds)...); }, - std::tuple_cat(std::make_tuple(std::forward(p1)), p2.preds)); + std::tuple_cat(detail::getAndTuple(std::forward(p1)), + detail::getAndTuple(std::forward(p2)))); } -template -struct Filter> : public FilterBase> { +template +struct Filter> { using Pred = And; template - static constexpr void init(Pred& p, Tree const& t) + static constexpr void init(Pred& p, Tree const& t) noexcept { - std::apply( - [&t](auto&... p) { ((Filter>::init(p, t)), ...); }, - p.preds); + std::apply([&t](auto&&... preds) { (Filter::init(preds, t), ...); }, p.preds); } template - [[nodiscard]] static constexpr bool returnable(Pred const& p, Value const& v) + [[nodiscard]] static constexpr bool returnableValue(Pred const& p, + Value const& v) noexcept { return std::apply( - [&v](auto const&... p) { - return ((Filter>::returnable(p, v)) && ...); + [&v](auto&&... preds) { + return (Filter::returnableValue(preds, v) && ...); }, p.preds); } template [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { return std::apply( - [&t, &n](auto const&... p) { - return ((Filter>::returnable(p, t, n)) && ...); + [&t, &n](auto&&... preds) { + return (Filter::returnable(preds, t, n) && ...); }, p.preds); } template [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept + { + return std::apply( + [&t, &n](auto&&... preds) { + return (Filter::traversable(preds, t, n) && ...); + }, + p.preds); + } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const& r) noexcept + { + return std::apply( + [&t, &n, &r](auto&&... preds) { + return (Filter::returnableRay(preds, t, n, r) && ...); + }, + p.preds); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const& r) noexcept { return std::apply( - [&t, &n](auto const&... p) { - return ((Filter>::traversable(p, t, n)) && ...); + [&t, &n, &r](auto&&... preds) { + return (Filter::traversableRay(preds, t, n, r) && ...); }, p.preds); } @@ -196,11 +155,11 @@ struct Filter> : public FilterBase> { namespace detail { -template +template struct contains_pred> : std::disjunction...> { }; -template +template struct contains_always_pred> : std::disjunction...> { }; diff --git a/lib/container/include/ufo/container/tree/predicate/bool.hpp b/lib/container/include/ufo/container/tree/predicate/bool.hpp old mode 100644 new mode 100755 index c528dd33..a9a505a9 --- a/lib/container/include/ufo/container/tree/predicate/bool.hpp +++ b/lib/container/include/ufo/container/tree/predicate/bool.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -46,40 +46,62 @@ #include #include -// STL -#include -#include -#include - namespace ufo::pred { +struct Bool { + bool value{}; + + constexpr Bool() noexcept = default; + + constexpr explicit Bool(bool value) noexcept : value(value) {} +}; + +[[nodiscard]] constexpr Bool operator!(Bool const& p) noexcept { return Bool{!p.value}; } + template <> -struct Filter : public FilterBase { - using Pred = bool; +struct Filter { + using Pred = Bool; template - static constexpr void init(Pred&, Tree const&) + static constexpr void init(Pred&, Tree const&) noexcept { } template - [[nodiscard]] static constexpr bool returnable(Pred const& p, Value const&) + [[nodiscard]] static constexpr bool returnableValue(Pred const& p, + Value const&) noexcept { - return p; + return p.value; } template [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const&, - typename Tree::Node const&) + typename Tree::Node const&) noexcept { - return p; + return p.value; } template [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const&, - typename Tree::Node const&) + typename Tree::Node const&) noexcept + { + return p.value; + } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const&, + typename Tree::Node const&, + typename Tree::Ray const&) noexcept + { + return p.value; + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const&, + typename Tree::Node const&, + typename Tree::Ray const&) noexcept { - return p; + return p.value; } }; } // namespace ufo::pred diff --git a/lib/container/include/ufo/container/tree/predicate/false.hpp b/lib/container/include/ufo/container/tree/predicate/boolean.hpp similarity index 65% rename from lib/container/include/ufo/container/tree/predicate/false.hpp rename to lib/container/include/ufo/container/tree/predicate/boolean.hpp index a6176831..4ce78ddf 100644 --- a/lib/container/include/ufo/container/tree/predicate/false.hpp +++ b/lib/container/include/ufo/container/tree/predicate/boolean.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -39,52 +39,72 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UFO_CONTAINER_TREE_PREDICATE_FALSE_HPP -#define UFO_CONTAINER_TREE_PREDICATE_FALSE_HPP +#ifndef UFO_CONTAINER_TREE_PREDICATE_BOOLEAN_HPP +#define UFO_CONTAINER_TREE_PREDICATE_BOOLEAN_HPP // UFO #include -#include - -// STL -#include -#include -#include namespace ufo::pred { -struct False { +template +struct Boolean { }; -template <> -struct Filter : public FilterBase { - using Pred = False; +template +[[nodiscard]] constexpr Boolean operator!(Boolean const&) noexcept +{ + return Boolean{}; +} + +using True = Boolean; +using False = Boolean; + +template +struct Filter> { + using Pred = Boolean; template - static constexpr void init(Pred&, Tree const&) + static constexpr void init(Pred&, Tree const&) noexcept { } template - [[nodiscard]] static constexpr bool returnable(Pred const&, Value const&) + [[nodiscard]] static constexpr bool returnableValue(Pred const&, Value const&) noexcept { - return false; + return Negated; } template [[nodiscard]] static constexpr bool returnable(Pred const&, Tree const&, - typename Tree::Node const&) + typename Tree::Node const&) noexcept { - return false; + return Negated; } template [[nodiscard]] static constexpr bool traversable(Pred const&, Tree const&, - typename Tree::Node const&) + typename Tree::Node const&) noexcept + { + return Negated; + } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const&, Tree const&, + typename Tree::Node const&, + typename Tree::Ray const&) noexcept + { + return Negated; + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const&, Tree const&, + typename Tree::Node const&, + typename Tree::Ray const&) noexcept { - return false; + return Negated; } }; } // namespace ufo::pred -#endif // UFO_CONTAINER_TREE_PREDICATE_FALSE_HPP \ No newline at end of file +#endif // UFO_CONTAINER_TREE_PREDICATE_BOOLEAN_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/predicate/child_of.hpp b/lib/container/include/ufo/container/tree/predicate/child_of.hpp old mode 100644 new mode 100755 index 64bec58c..09d3316f --- a/lib/container/include/ufo/container/tree/predicate/child_of.hpp +++ b/lib/container/include/ufo/container/tree/predicate/child_of.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -52,7 +52,7 @@ namespace ufo::pred { -template +template struct ChildOf { TreeCode code; @@ -63,29 +63,67 @@ struct ChildOf { template ChildOf(TreeCode) -> ChildOf; -template -struct Filter> : public FilterBase> { - using Pred = ChildOf; +template +[[nodiscard]] constexpr ChildOf operator!( + ChildOf const& p) noexcept +{ + return ChildOf{p.code}; +} + +template +struct Filter> { + using Pred = ChildOf; template - static constexpr void init(Pred&, Tree const&) + static constexpr void init(Pred&, Tree const&) noexcept { } + template + [[nodiscard]] static constexpr bool returnableValue(Pred const&, Value const&) noexcept + { + return true; + } + template [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { - return p.code.depth() > t.depth(n) && - TreeCode::equalAtDepth(p.code, t.code(n), p.code.depth()); + if constexpr (Negated) { + return !(p.code.depth() > t.depth(n) && + TreeCode::equalAtDepth(p.code, t.code(n), p.code.depth())); + } else { + return p.code.depth() > t.depth(n) && + TreeCode::equalAtDepth(p.code, t.code(n), p.code.depth()); + } } template [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept + { + if constexpr (Negated) { + return returnable(p, t, n); + } else { + return TreeCode::equalAtDepth(p.code, t.code(n), + std::max(p.code.depth(), t.depth(n))); + } + } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept { - return TreeCode::equalAtDepth(p.code, t.code(n), - std::max(p.code.depth(), t.depth(n))); + return traversable(p, t, n); } }; } // namespace ufo::pred diff --git a/lib/container/include/ufo/container/tree/predicate/satisfies_inner.hpp b/lib/container/include/ufo/container/tree/predicate/contains.hpp similarity index 51% rename from lib/container/include/ufo/container/tree/predicate/satisfies_inner.hpp rename to lib/container/include/ufo/container/tree/predicate/contains.hpp index d7da0800..dfbebd56 100644 --- a/lib/container/include/ufo/container/tree/predicate/satisfies_inner.hpp +++ b/lib/container/include/ufo/container/tree/predicate/contains.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -39,61 +39,104 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UFO_CONTAINER_TREE_PREDICATE_SATISFIES_INNER_HPP -#define UFO_CONTAINER_TREE_PREDICATE_SATISFIES_INNER_HPP +#ifndef UFO_CONTAINER_TREE_PREDICATE_CONTAINS_HPP +#define UFO_CONTAINER_TREE_PREDICATE_CONTAINS_HPP // UFO #include +#include +#include + +// STL +#include namespace ufo::pred { -template -struct SatisfiesInner { - SatisfiesInner(Fun fun) : fun(fun) {} - - Fun fun; +template +struct Contains { + Geometry geometry; }; -template -constexpr SatisfiesInner operator!(SatisfiesInner const& p) +template +[[nodiscard]] constexpr Contains operator!( + Contains const& p) noexcept { - return SatisfiesInner(p.fun); + return Contains{p.geometry}; } -template -struct Filter> - : public FilterBase> { - using Pred = SatisfiesInner; +template +struct Filter> { + using Pred = Contains; template - static constexpr void init(Pred&, Tree const&) + static constexpr void init(Pred&, Tree const&) noexcept { } template - [[nodiscard]] static constexpr bool returnable(Pred const&, Value const&) + [[nodiscard]] static constexpr bool returnableValue(Pred const& p, + Value const& v) noexcept { - return true; + if constexpr (Negated) { + if constexpr (is_pair_v>) { + return !contains(v.first, p.geometry); + } else { + return !contains(v, p.geometry); + } + } else { + if constexpr (is_pair_v>) { + return contains(v.first, p.geometry); + } else { + return contains(v, p.geometry); + } + } } template - [[nodiscard]] static constexpr bool returnable(Pred const&, Tree const&, - typename Tree::Node const&) + [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, + typename Tree::Node const& n) noexcept { - return true; + if constexpr (Negated) { + return !contains(t.bounds(n), p.geometry); + } else { + return contains(t.bounds(n), p.geometry); + } } template [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { if constexpr (Negated) { - return !p.fun(n); + return true; } else { - return p.fun(n); + return contains(t.bounds(n), p.geometry); } } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return traversable(p, t, n); + } +}; + +namespace detail +{ +template +struct is_spatial_pred> : std::true_type { }; +} // namespace detail } // namespace ufo::pred -#endif // UFO_CONTAINER_TREE_PREDICATE_SATISFIES_INNER_HPP \ No newline at end of file +#endif // UFO_CONTAINER_TREE_PREDICATE_CONTAINS_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/predicate/coord.hpp b/lib/container/include/ufo/container/tree/predicate/coord.hpp old mode 100644 new mode 100755 index 0bc212d8..9b646db8 --- a/lib/container/include/ufo/container/tree/predicate/coord.hpp +++ b/lib/container/include/ufo/container/tree/predicate/coord.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -44,105 +44,100 @@ // UFO #include -#include +#include namespace ufo::pred { -template +namespace detail +{ +template struct Coord { - float coord; - - constexpr Coord(float coord = 0.0f) noexcept : coord(coord) {} + using value_type = float; }; +} // namespace detail -template -struct X : Coord<0, PC> { - constexpr X(float x = 0.0f) : Coord<0, PC>(x) {} -}; +template +using Coord = PredicateInterval, Negated>; -template -struct Y : Coord<1, PC> { - constexpr Y(float y = 0.0f) : Coord<1, PC>(y) {} -}; +template +using X = Coord<0, Negated>; -template -struct Z : Coord<2, PC> { - constexpr Z(float z = 0.0f) : Coord<2, PC>(z) {} -}; +template +using Y = Coord<1, Negated>; -template -struct W : Coord<3, PC> { - constexpr W(float w = 0.0f) : Coord<3, PC>(w) {} -}; +template +using Z = Coord<2, Negated>; -template -struct Filter> : public FilterBase> { - using Pred = Coord; +template +using W = Coord<3, Negated>; + +static constexpr inline X const x; +static constexpr inline Y const y; +static constexpr inline Z const z; +static constexpr inline W const w; + +template +struct Filter> { + using Pred = Coord; template - static constexpr void init(Pred&, Tree const&) + static constexpr void init(Pred&, Tree const&) noexcept + { + } + + template + [[nodiscard]] static constexpr bool returnableValue(Pred const&, Value const&) noexcept { + return true; } template [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { auto c = t.centerAxis(n, Axis); auto hl = t.halfLength(n)[Axis]; - if constexpr (PredicateCompare::EQUAL == PC) { - return c - hl <= p.coord && c + hl >= p.coord; - } else if constexpr (PredicateCompare::NOT_EQUAL == PC) { - return c - hl > p.coord || c + hl < p.coord; - } else if constexpr (PredicateCompare::LESS_EQUAL == PC) { - return c - hl <= p.coord; - } else if constexpr (PredicateCompare::GREATER_EQUAL == PC) { - return c + hl >= p.coord; - } else if constexpr (PredicateCompare::LESS == PC) { - return c + hl < p.coord; - } else if constexpr (PredicateCompare::GREATER == PC) { - return c - hl > p.coord; + if constexpr (Negated) { + // Check if the interval is outside the node + return p.min > c + hl || p.max < c - hl; + } else { + // Check if the node overlaps with the interval + return p.min <= c + hl && p.max >= c - hl; } } template [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { auto c = t.centerAxis(n, Axis); auto hl = t.halfLength(n)[Axis]; - if constexpr (PredicateCompare::EQUAL == PC) { - return c - hl <= p.coord && c + hl >= p.coord; - } else if constexpr (PredicateCompare::NOT_EQUAL == PC) { - return true; - } else if constexpr (PredicateCompare::LESS_EQUAL == PC) { - return c - hl <= p.coord; - } else if constexpr (PredicateCompare::GREATER_EQUAL == PC) { - return c + hl >= p.coord; - } else if constexpr (PredicateCompare::LESS == PC) { - return c - hl + t.length(0)[Axis] < p.coord; - } else if constexpr (PredicateCompare::GREATER == PC) { - return c + hl - t.length(0)[Axis] > p.coord; + if constexpr (Negated) { + // Check if the whole node is contained in the negated interval + return !(p.min <= c - hl && p.max >= c + hl); + } else { + // Check if the node overlaps with the interval + return p.min <= c + hl && p.max >= c - hl; } } -}; -template -struct Filter> : Filter> { -}; - -template -struct Filter> : Filter> { -}; - -template -struct Filter> : Filter> { -}; + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } -template -struct Filter> : Filter> { + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return traversable(p, t, n); + } }; } // namespace ufo::pred diff --git a/lib/container/include/ufo/container/tree/predicate/depth.hpp b/lib/container/include/ufo/container/tree/predicate/depth.hpp old mode 100644 new mode 100755 index e14cbe1f..0de33e2a --- a/lib/container/include/ufo/container/tree/predicate/depth.hpp +++ b/lib/container/include/ufo/container/tree/predicate/depth.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -44,78 +44,79 @@ // UFO #include -#include +#include namespace ufo::pred { -template +namespace detail +{ struct Depth { - // It is int because length predicate requires it - int depth; - - constexpr Depth(int depth = 0) noexcept : depth(depth) {} + using value_type = int; }; +} // namespace detail -using DepthE = Depth; -using DepthNE = Depth; -using DepthLE = Depth; -using DepthGE = Depth; -using DepthL = Depth; -using DepthG = Depth; +template +using Depth = PredicateInterval; -using DepthMin = DepthGE; -using DepthMax = DepthLE; +static constexpr inline Depth const depth; -template -struct Filter> : public FilterBase> { - using Pred = Depth; +template +struct Filter> { + using Pred = Depth; template - static constexpr void init(Pred&, Tree const&) + static constexpr void init(Pred&, Tree const&) noexcept { } + template + [[nodiscard]] static constexpr bool returnableValue(Pred const&, Value const&) noexcept + { + return true; + } + template [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { // Cast to int to prevent int to be promoted to unsigned - int n_depth = static_cast(t.depth(n)); - if constexpr (PredicateCompare::EQUAL == PC) { - return n_depth == p.depth; - } else if constexpr (PredicateCompare::NOT_EQUAL == PC) { - return n_depth != p.depth; - } else if constexpr (PredicateCompare::LESS_EQUAL == PC) { - return n_depth <= p.depth; - } else if constexpr (PredicateCompare::GREATER_EQUAL == PC) { - return n_depth >= p.depth; - } else if constexpr (PredicateCompare::LESS == PC) { - return n_depth < p.depth; - } else if constexpr (PredicateCompare::GREATER == PC) { - return n_depth > p.depth; + int depth = static_cast(t.depth(n)); + bool ret = p.min <= depth && depth <= p.max; + if constexpr (Negated) { + return !ret; + } else { + return ret; } } template [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { // Cast to int to prevent int to be promoted to unsigned - int n_depth = static_cast(t.depth(n)); - if constexpr (PredicateCompare::EQUAL == PC) { - return n_depth > p.depth; - } else if constexpr (PredicateCompare::NOT_EQUAL == PC) { - return true; - } else if constexpr (PredicateCompare::LESS_EQUAL == PC) { - return true; - } else if constexpr (PredicateCompare::GREATER_EQUAL == PC) { - return n_depth > p.depth; - } else if constexpr (PredicateCompare::LESS == PC) { - return true; - } else if constexpr (PredicateCompare::GREATER == PC) { - return n_depth > (p.depth + 1); + int depth = static_cast(t.depth(n)); + if constexpr (Negated) { + return 0 < p.min || p.max + 1 < depth; + } else { + return p.min < depth; } } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return traversable(p, t, n); + } }; } // namespace ufo::pred diff --git a/lib/container/include/ufo/container/tree/predicate/depth_interval.hpp b/lib/container/include/ufo/container/tree/predicate/disjoint.hpp similarity index 58% rename from lib/container/include/ufo/container/tree/predicate/depth_interval.hpp rename to lib/container/include/ufo/container/tree/predicate/disjoint.hpp index 0985ff75..7960d530 100644 --- a/lib/container/include/ufo/container/tree/predicate/depth_interval.hpp +++ b/lib/container/include/ufo/container/tree/predicate/disjoint.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -39,69 +39,82 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UFO_CONTAINER_TREE_PREDICATE_DEPTH_INTERVAL_HPP -#define UFO_CONTAINER_TREE_PREDICATE_DEPTH_INTERVAL_HPP +#ifndef UFO_CONTAINER_TREE_PREDICATE_DISJOINT_HPP +#define UFO_CONTAINER_TREE_PREDICATE_DISJOINT_HPP // UFO -#include #include +#include +#include +#include + +// STL +#include namespace ufo::pred { -/*! - * @brief - * - * @note The interval is inclusive (i.e., [min .. max]). - * - */ -template -struct DepthInterval { - DepthMin min; - DepthMax max; - - constexpr DepthInterval(int min, int max) noexcept : min(min), max(max) {} +template +struct Disjoint { + Geometry geometry; }; -template -constexpr DepthInterval operator!(DepthInterval const& p) -{ - return DepthInterval(p.min, p.max); -} - -template -struct Filter> : public FilterBase> { - using Pred = DepthInterval; +template +struct Filter> { + using Pred = Disjoint; template - static constexpr void init(Pred&, Tree const&) + static constexpr void init(Pred&, Tree const&) noexcept { } - template - [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + template + [[nodiscard]] static constexpr bool returnableValue(Pred const& p, + Value const& v) noexcept { - if constexpr (Negated) { - return !(Filter::returnable(p.min, t, n) && - Filter::returnable(p.max, t, n)); + if constexpr (is_pair_v>) { + return disjoint(v.first, p.geometry); } else { - return Filter::returnable(p.min, t, n) && - Filter::returnable(p.max, t, n); + return disjoint(v, p.geometry); } } + template + [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, + typename Tree::Node const& n) noexcept + { + return disjoint(t.bounds(n), p.geometry); + } + template [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { - if constexpr (Negated) { - return 0 < p.min.depth || p.max.depth + 1 < t.depth(n); - } else { - return Filter::traversable(p.min, t, n) && - Filter::traversable(p.max, t, n); - } + return !inside(t.bounds(n), p.geometry); } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return traversable(p, t, n); + } +}; + +namespace detail +{ +template +struct is_spatial_pred> : std::true_type { }; +} // namespace detail } // namespace ufo::pred -#endif // UFO_CONTAINER_TREE_PREDICATE_DEPTH_INTERVAL_HPP \ No newline at end of file +#endif // UFO_CONTAINER_TREE_PREDICATE_DISJOINT_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/predicate/exists.hpp b/lib/container/include/ufo/container/tree/predicate/exists.hpp old mode 100644 new mode 100755 index 44a2ca9d..36b05a7e --- a/lib/container/include/ufo/container/tree/predicate/exists.hpp +++ b/lib/container/include/ufo/container/tree/predicate/exists.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -53,23 +53,31 @@ struct Exists { }; template -constexpr Exists operator!(Exists const&) +[[nodiscard]] constexpr Exists operator!(Exists const&) noexcept { return Exists{}; } +static constexpr inline Exists const exists; + template -struct Filter> : public FilterBase> { +struct Filter> { using Pred = Exists; template - static constexpr void init(Pred&, Tree const&) + static constexpr void init(Pred&, Tree const&) noexcept + { + } + + template + [[nodiscard]] static constexpr bool returnableValue(Pred const&, Value const&) noexcept { + return true; } template [[nodiscard]] static constexpr bool returnable(Pred const&, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { if constexpr (Negated) { return !t.exists(n); @@ -80,7 +88,7 @@ struct Filter> : public FilterBase> { template [[nodiscard]] static constexpr bool traversable(Pred const&, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { if constexpr (Negated) { return true; @@ -88,6 +96,22 @@ struct Filter> : public FilterBase> { return t.isParent(n.index); } } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return traversable(p, t, n); + } }; } // namespace ufo::pred diff --git a/lib/container/include/ufo/container/tree/predicate/filter.hpp b/lib/container/include/ufo/container/tree/predicate/filter.hpp old mode 100644 new mode 100755 index bcd7e5b0..4ead78d9 --- a/lib/container/include/ufo/container/tree/predicate/filter.hpp +++ b/lib/container/include/ufo/container/tree/predicate/filter.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOTree: An Efficient Probabilistic 3D Treeping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -42,11 +42,6 @@ #ifndef UFO_CONTAINER_TREE_PREDICATE_FILTER_HPP #define UFO_CONTAINER_TREE_PREDICATE_FILTER_HPP -// UFO -#include -#include -#include - // STL #include @@ -54,110 +49,7 @@ namespace ufo::pred { template struct Filter { - static_assert(dependent_false_v, "Predicate not implemented correctly."); -}; - -enum class FilterType : std::size_t { - None = 0u, - Init = 1u << 0u, - Value = 1u << 1u, - Node = 1u << 2u, - Ray = 1u << 3u, -}; - -constexpr FilterType operator|(FilterType lhs, FilterType rhs) -{ - return static_cast(to_underlying(lhs) | to_underlying(rhs)); -} - -constexpr FilterType operator&(FilterType lhs, FilterType rhs) -{ - return static_cast(to_underlying(lhs) & to_underlying(rhs)); -} - -constexpr FilterType operator^(FilterType lhs, FilterType rhs) -{ - return static_cast(to_underlying(lhs) ^ to_underlying(rhs)); -} - -namespace detail -{ -template -struct FilterInit { -}; - -template -struct FilterInit { - template - static constexpr void init(Predicate&, Tree const&) - { - } -}; - -template -struct FilterValue { -}; - -template -struct FilterValue { - template - [[nodiscard]] static constexpr bool returnableValue(Predicate const&, Value const&) - { - return true; - } -}; - -template -struct FilterNode { -}; - -template -struct FilterNode { - template - [[nodiscard]] static constexpr bool returnable(Predicate const&, Tree const&, - typename Tree::Node const&) - { - return true; - } - - template - [[nodiscard]] static constexpr bool traversable(Predicate const&, Tree const&, - typename Tree::Node const&) - { - return true; - } -}; - -template -struct FilterRay { -}; - -template -struct FilterRay { - template - [[nodiscard]] static constexpr bool returnableRay(Predicate const& p, Tree const& t, - typename Tree::Node const& n, - typename Tree::Ray const&) - { - return Filter::returnable(p, t, n); - } - - template - [[nodiscard]] static constexpr bool traversableRay(Predicate const& p, Tree const& t, - typename Tree::Node const& n, - typename Tree::Ray const&) - { - return Filter::traversable(p, t, n); - } -}; -} // namespace detail - -template -struct FilterBase - : detail::FilterInit - , detail::FilterValue - , detail::FilterNode - , detail::FilterRay { + static_assert(false, "Predicate not implemented correctly."); }; // @@ -184,46 +76,19 @@ struct contains_always_pred : std::true_type { } // namespace detail template -using contains_pred = detail::contains_pred; - -template -constexpr inline bool contains_pred_v = contains_pred::value; - -template -using contains_always_pred = detail::contains_always_pred; +constexpr inline bool contains_pred_v = detail::contains_pred::value; template -constexpr inline bool contains_always_pred_v = contains_always_pred::value; +constexpr inline bool contains_always_pred_v = + detail::contains_always_pred::value; // -// Is predicate +// Predicate concept // -template -struct is_pred : std::false_type { -}; - -namespace detail -{ -template -struct is_pred { - using type = decltype(test(std::declval())); - - private: - template - static constexpr std::true_type test(FilterBase const*); - - static constexpr std::false_type test(...); -}; -} // namespace detail - -template -struct is_pred>>> - : std::true_type { -}; +template +concept Filterable = requires { typename Filter; }; -template -constexpr inline bool is_pred_v = is_pred::value; } // namespace ufo::pred #endif // UFO_CONTAINER_TREE_PREDICATE_FILTER_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/predicate/length_interval.hpp b/lib/container/include/ufo/container/tree/predicate/height.hpp similarity index 60% rename from lib/container/include/ufo/container/tree/predicate/length_interval.hpp rename to lib/container/include/ufo/container/tree/predicate/height.hpp index 15f41eee..c7755c43 100644 --- a/lib/container/include/ufo/container/tree/predicate/length_interval.hpp +++ b/lib/container/include/ufo/container/tree/predicate/height.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -39,71 +39,85 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UFO_CONTAINER_TREE_PREDICATE_LENGTH_INTERVAL_HPP -#define UFO_CONTAINER_TREE_PREDICATE_LENGTH_INTERVAL_HPP +#ifndef UFO_CONTAINER_TREE_PREDICATE_HEIGHT_HPP +#define UFO_CONTAINER_TREE_PREDICATE_HEIGHT_HPP // UFO #include -#include +#include namespace ufo::pred { -/*! - * @brief - * - * @note The interval is inclusive (i.e., [min .. max]). - * - */ -template -struct LengthInterval { - LengthMin min; - LengthMax max; - - constexpr LengthInterval(double min, double max) noexcept : min(min), max(max) {} +namespace detail +{ +struct Height { + using value_type = int; }; +} // namespace detail -template -constexpr LengthInterval operator!(LengthInterval const& p) -{ - return LengthInterval(p.min, p.max); -} +template +using Height = PredicateInterval; + +static constexpr inline Height const height; template -struct Filter> : public FilterBase> { - using Pred = LengthInterval; +struct Filter> { + using Pred = Height; template - static constexpr void init(Pred& p, Tree const& t) + static constexpr void init(Pred&, Tree const&) noexcept { - Filter::init(p.min, t); - Filter::init(p.max, t); + } + + template + [[nodiscard]] static constexpr bool returnableValue(Pred const&, Value const&) noexcept + { + return true; } template [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { + // Cast to int to prevent int to be promoted to unsigned + int height = static_cast(t.height(n)); + bool ret = p.min <= height && height <= p.max; if constexpr (Negated) { - return !(Filter::returnable(p.min, t, n) && - Filter::returnable(p.max, t, n)); + return !ret; } else { - return Filter::returnable(p.min, t, n) && - Filter::returnable(p.max, t, n); + return ret; } } template [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { + // Cast to int to prevent int to be promoted to unsigned + int height = static_cast(t.height(n)); if constexpr (Negated) { - return 0 < p.min.depth_ || p.max.depth_ + 1 < t.depth(n); + return 0 < p.min || p.max + 1 < height; } else { - return Filter::traversable(p.min, t, n) && - Filter::traversable(p.max, t, n); + return p.min < height; } } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return traversable(p, t, n); + } }; } // namespace ufo::pred -#endif // UFO_CONTAINER_TREE_PREDICATE_LENGTH_INTERVAL_HPP \ No newline at end of file +#endif // UFO_CONTAINER_TREE_PREDICATE_HEIGHT_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/predicate/if_and_only_if.hpp b/lib/container/include/ufo/container/tree/predicate/if_and_only_if.hpp old mode 100644 new mode 100755 index 25f8942d..8739b70c --- a/lib/container/include/ufo/container/tree/predicate/if_and_only_if.hpp +++ b/lib/container/include/ufo/container/tree/predicate/if_and_only_if.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -47,63 +47,80 @@ #include // STL -#include #include -#include namespace ufo::pred { -template +template struct Iff { - Iff(PredLeft const& left, PredRight const& right) : left(left), right(right) {} + Iff(PredLeft const& left, PredRight const& right) noexcept : left(left), right(right) {} PredLeft left; PredRight right; }; -template -struct Filter> : public FilterBase> { +template +struct Filter> { using Pred = Iff; template - static constexpr void init(Pred& p, Tree const& t) + static constexpr void init(Pred& p, Tree const& t) noexcept { Filter::init(p.left, t); Filter::init(p.right, t); } template - [[nodiscard]] static constexpr bool returnable(Pred const& p, Value const& v) + [[nodiscard]] static constexpr bool returnableValue(Pred const& p, + Value const& v) noexcept { - return Filter::returnable(p.left, v) == - Filter::returnable(p.right, v); + return Filter::returnableValue(p.left, v) == + Filter::returnableValue(p.right, v); } template [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { return Filter::returnable(p.left, t, n) == Filter::returnable(p.right, t, n); } template - [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + [[nodiscard]] static constexpr bool traversable(Pred const&, Tree const&, + typename Tree::Node const&) noexcept { - return Filter::traversable(p.left, t, n) == - Filter::traversable(p.right, t, n); + // NOTE: This is not the same as the returnable check! + return true; + } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const& r) noexcept + { + return Filter::returnableRay(p.left, t, n, r) == + Filter::returnableRay(p.right, t, n, r); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const&, Tree const&, + typename Tree::Node const&, + typename Tree::Ray const&) noexcept + { + // NOTE: This is not the same as the returnable check! + return true; } }; namespace detail { -template +template struct contains_pred> : std::disjunction, contains_pred> { }; -template +template struct contains_always_pred> : std::false_type { }; } // namespace detail diff --git a/lib/container/include/ufo/container/tree/predicate/if_then.hpp b/lib/container/include/ufo/container/tree/predicate/if_then.hpp new file mode 100644 index 00000000..d85a3fdf --- /dev/null +++ b/lib/container/include/ufo/container/tree/predicate/if_then.hpp @@ -0,0 +1,139 @@ +/** + * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown + * + * @author Daniel Duberg (dduberg@kth.se) + * @see https://github.com/UnknownFreeOccupied/ufomap + * @version 1.0 + * @date 2022-05-13 + * + * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology + * + * BSD 3-Clause License + * + * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. 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. + * + * THIS 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. + */ + +#ifndef UFO_CONTAINER_TREE_PREDICATE_IF_THEN_HPP +#define UFO_CONTAINER_TREE_PREDICATE_IF_THEN_HPP + +// UFO +#include +#include + +// STL +#include + +namespace ufo::pred +{ +template +struct IfThen { + IfThen(PredPre const& pre, PredPost const& post) noexcept : pre(pre), post(post) {} + + PredPre pre; + PredPost post; +}; + +template +[[nodiscard]] constexpr IfThen, + std::remove_cvref_t> +operator>>(PredPre&& pre, PredPost&& post) noexcept +{ + return IfThen(std::forward(pre), std::forward(post)); +} + +template +struct Filter> { + using Pred = IfThen; + + template + static constexpr void init(Pred& p, Tree const& t) noexcept + { + Filter::init(p.pre, t); + Filter::init(p.post, t); + } + + template + [[nodiscard]] static constexpr bool returnableValue(Pred const& p, + Value const& v) noexcept + { + return !Filter::returnableValue(p.pre, v) || + Filter::returnableValue(p.post, v); + } + + template + [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, + typename Tree::Node const& n) noexcept + { + return !Filter::returnable(p.pre, t, n) || + Filter::returnable(p.post, t, n); + } + + template + [[nodiscard]] static constexpr bool traversable(Pred const&, Tree const&, + typename Tree::Node const&) noexcept + { + // NOTE: This is not the same as the returnable check! If the pre-predicate + // is false, we can still traverse the subtree corresponding to the post-predicate. + return true; + } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const& r) noexcept + { + return !Filter::returnableRay(p.pre, t, n, r) || + Filter::returnableRay(p.post, t, n, r); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const&, Tree const&, + typename Tree::Node const&, + typename Tree::Ray const&) noexcept + { + // NOTE: This is not the same as the returnable check! If the pre-predicate + // is false, we can still traverse the subtree corresponding to the post-predicate. + return true; + } +}; + +namespace detail +{ +template +struct contains_pred> + : std::disjunction, contains_pred> { +}; + +template +struct contains_always_pred> : std::false_type { +}; +} // namespace detail +} // namespace ufo::pred + +#endif // UFO_CONTAINER_TREE_PREDICATE_IF_THEN_HPP diff --git a/lib/container/include/ufo/container/tree/predicate/inner.hpp b/lib/container/include/ufo/container/tree/predicate/inner.hpp index 058ab161..ab849cfc 100644 --- a/lib/container/include/ufo/container/tree/predicate/inner.hpp +++ b/lib/container/include/ufo/container/tree/predicate/inner.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -50,28 +50,52 @@ namespace ufo::pred struct Inner { }; +static constexpr inline Inner const inner; + template <> -struct Filter : public FilterBase { +struct Filter { using Pred = Inner; template - static constexpr void init(Pred&, Tree const&) + static constexpr void init(Pred&, Tree const&) noexcept + { + } + + template + [[nodiscard]] static constexpr bool returnableValue(Pred const&, Value const&) noexcept { + return true; } template [[nodiscard]] static constexpr bool returnable(Pred const&, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { return 0 < t.depth(n); } template [[nodiscard]] static constexpr bool traversable(Pred const&, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { return 1 < t.depth(n); } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return traversable(p, t, n); + } }; } // namespace ufo::pred diff --git a/lib/container/include/ufo/container/tree/predicate/inside.hpp b/lib/container/include/ufo/container/tree/predicate/inside.hpp new file mode 100644 index 00000000..fe19e9cb --- /dev/null +++ b/lib/container/include/ufo/container/tree/predicate/inside.hpp @@ -0,0 +1,144 @@ +/** + * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown + * + * @author Daniel Duberg (dduberg@kth.se) + * @see https://github.com/UnknownFreeOccupied/ufomap + * @version 1.0 + * @date 2022-05-13 + * + * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology + * + * BSD 3-Clause License + * + * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. 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. + * + * THIS 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. + */ + +#ifndef UFO_CONTAINER_TREE_PREDICATE_INSIDE_HPP +#define UFO_CONTAINER_TREE_PREDICATE_INSIDE_HPP + +// UFO +#include +#include +#include +#include + +// STL +#include + +namespace ufo::pred +{ +template +struct Inside { + Geometry geometry; +}; + +template +[[nodiscard]] constexpr Inside operator!( + Inside const& p) noexcept +{ + return Inside{p.geometry}; +} + +template +struct Filter> { + using Pred = Inside; + + template + static constexpr void init(Pred&, Tree const&) noexcept + { + } + + template + [[nodiscard]] static constexpr bool returnableValue(Pred const& p, + Value const& v) noexcept + { + if constexpr (Negated) { + if constexpr (is_pair_v>) { + return !inside(v.first, p.geometry); + } else { + return !inside(v, p.geometry); + } + } else { + if constexpr (is_pair_v>) { + return inside(v.first, p.geometry); + } else { + return inside(v, p.geometry); + } + } + } + + template + [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, + typename Tree::Node const& n) noexcept + { + if constexpr (Negated) { + return !inside(t.bounds(n), p.geometry); + } else { + return inside(t.bounds(n), p.geometry); + } + } + + template + [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, + typename Tree::Node const& n) noexcept + { + if constexpr (Negated) { + return !inside(t.bounds(n), p.geometry); + } else { + return intersects(t.bounds(n), p.geometry); + } + } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return traversable(p, t, n); + } +}; + +namespace detail +{ +template +struct is_spatial_pred> : std::true_type { +}; +} // namespace detail + +} // namespace ufo::pred + +#endif // UFO_CONTAINER_TREE_PREDICATE_INSIDE_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/predicate/then.hpp b/lib/container/include/ufo/container/tree/predicate/intersects.hpp similarity index 61% rename from lib/container/include/ufo/container/tree/predicate/then.hpp rename to lib/container/include/ufo/container/tree/predicate/intersects.hpp index d52daace..16c36290 100644 --- a/lib/container/include/ufo/container/tree/predicate/then.hpp +++ b/lib/container/include/ufo/container/tree/predicate/intersects.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -39,74 +39,82 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UFO_CONTAINER_TREE_PREDICATE_THEN_HPP -#define UFO_CONTAINER_TREE_PREDICATE_THEN_HPP +#ifndef UFO_CONTAINER_TREE_PREDICATE_INTERSECTS_HPP +#define UFO_CONTAINER_TREE_PREDICATE_INTERSECTS_HPP // UFO #include -#include +#include +#include // STL -#include #include -#include namespace ufo::pred { -template -struct Then { - Then(PredPre const& pre, PredPost const& post) : pre(pre), post(post) {} - - PredPre pre; - PredPost post; +template +struct Intersects { + Geometry geometry; }; -template -struct Filter> : public FilterBase> { - using Pred = Then; +template +struct Filter> { + using Pred = Intersects; template - static constexpr void init(Pred& p, Tree const& t) + static constexpr void init(Pred&, Tree const&) noexcept { - Filter::init(p.pre, t); - Filter::init(p.post, t); } template - [[nodiscard]] static constexpr bool returnable(Pred const& p, Value const& v) + [[nodiscard]] static constexpr bool returnableValue(Pred const& p, + Value const& v) noexcept { - return !Filter::returnable(p.left, v) || - Filter::returnable(p.right, v); + if constexpr (is_pair_v>) { + return intersects(v.first, p.geometry); + } else { + return intersects(v, p.geometry); + } } template [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { - return !Filter::returnable(p.left, t, n) || - Filter::returnable(p.right, t, n); + return intersects(t.bounds(n), p.geometry); } template [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept + { + return intersects(t.bounds(n), p.geometry); + } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept { - return !Filter::traversable(p.left, t, n) || - Filter::traversable(p.right, t, n); + return traversable(p, t, n); } }; namespace detail { -template -struct contains_pred> - : std::disjunction, contains_pred> { -}; - -template -struct contains_always_pred> : std::false_type { +template +struct is_spatial_pred> : std::true_type { }; } // namespace detail + } // namespace ufo::pred -#endif // UFO_CONTAINER_TREE_PREDICATE_THEN_HPP \ No newline at end of file +#endif // UFO_CONTAINER_TREE_PREDICATE_INTERSECTS_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/predicate/leaf.hpp b/lib/container/include/ufo/container/tree/predicate/leaf.hpp index 682f56a9..406e05bb 100644 --- a/lib/container/include/ufo/container/tree/predicate/leaf.hpp +++ b/lib/container/include/ufo/container/tree/predicate/leaf.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -50,28 +50,52 @@ namespace ufo::pred struct Leaf { }; +static constexpr inline Leaf const leaf; + template <> -struct Filter : public FilterBase { +struct Filter { using Pred = Leaf; template - static constexpr void init(Pred&, Tree const&) + static constexpr void init(Pred&, Tree const&) noexcept { } + template + [[nodiscard]] static constexpr bool returnableValue(Pred const&, Value const&) noexcept + { + return true; + } + template [[nodiscard]] static constexpr bool returnable(Pred const&, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { - return t.isLeaf(n.index); + return !t.isParent(n); } template - [[nodiscard]] static constexpr bool traversable(Pred const&, Tree const&, - typename Tree::Node const&) + [[nodiscard]] static constexpr bool traversable(Pred const&, Tree const& t, + typename Tree::Node const& n) noexcept { return true; } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return traversable(p, t, n); + } }; } // namespace ufo::pred diff --git a/lib/container/include/ufo/container/tree/predicate/leaf_or_depth.hpp b/lib/container/include/ufo/container/tree/predicate/leaf_or_depth.hpp deleted file mode 100644 index 589125fa..00000000 --- a/lib/container/include/ufo/container/tree/predicate/leaf_or_depth.hpp +++ /dev/null @@ -1,108 +0,0 @@ -/*! - * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown - * - * @author Daniel Duberg (dduberg@kth.se) - * @see https://github.com/UnknownFreeOccupied/ufomap - * @version 1.0 - * @date 2022-05-13 - * - * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * - * BSD 3-Clause License - * - * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. 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. - * - * 3. 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. - * - * THIS 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. - */ - -#ifndef UFO_CONTAINER_TREE_PREDICATE_LEAF_OR_DEPTH_HPP -#define UFO_CONTAINER_TREE_PREDICATE_LEAF_OR_DEPTH_HPP - -// UFO -#include -#include - -namespace ufo::pred -{ -template -struct LeafOrDepth { - // It is int because length predicate requires it - int depth; - - constexpr LeafOrDepth(int depth = 0) : depth(depth) {} -}; - -using LeafOrDepthE = LeafOrDepth; -using LeafOrDepthNE = LeafOrDepth; -using LeafOrDepthLE = LeafOrDepth; -using LeafOrDepthGE = LeafOrDepth; -using LeafOrDepthL = LeafOrDepth; -using LeafOrDepthG = LeafOrDepth; - -using LeafOrDepthMin = LeafOrDepthGE; -using LeafOrDepthMax = LeafOrDepthLE; - -template -struct Filter> : public FilterBase> { - using Pred = LeafOrDepth; - - template - static constexpr void init(Pred&, Tree const&) - { - } - - template - [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, - typename Tree::Node const& n) - { - // Cast to int to prevent int to be promoted to unsigned - int n_depth = static_cast(t.depth(n)); - if constexpr (PredicateCompare::EQUAL == PC) { - return t.isLeaf(n) || n_depth == p.depth; - } else if constexpr (PredicateCompare::NOT_EQUAL == PC) { - return t.isLeaf(n) || n_depth != p.depth; - } else if constexpr (PredicateCompare::LESS_EQUAL == PC) { - return t.isLeaf(n) || n_depth <= p.depth; - } else if constexpr (PredicateCompare::GREATER_EQUAL == PC) { - return t.isLeaf(n) || n_depth >= p.depth; - } else if constexpr (PredicateCompare::LESS == PC) { - return t.isLeaf(n) || n_depth < p.depth; - } else if constexpr (PredicateCompare::GREATER == PC) { - return t.isLeaf(n) || n_depth > p.depth; - } - } - - template - [[nodiscard]] static constexpr bool traversable(Pred const&, Tree const&, - typename Tree::Node const&) - { - return true; - } -}; -} // namespace ufo::pred - -#endif // UFO_CONTAINER_TREE_PREDICATE_LEAF_OR_DEPTH_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/predicate/length.hpp b/lib/container/include/ufo/container/tree/predicate/length.hpp old mode 100644 new mode 100755 index 3b543f07..37bcf6a2 --- a/lib/container/include/ufo/container/tree/predicate/length.hpp +++ b/lib/container/include/ufo/container/tree/predicate/length.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -45,100 +45,106 @@ // UFO #include #include -#include +#include + +// TODO: Implement namespace ufo::pred { -template +namespace detail +{ struct Length { - double length; - - constexpr Length(double length = 0.0) noexcept : length(length) {} + using value_type = double; - private: - Depth depth_; - - template - friend class Filter; + int min_depth{}; + int max_depth{}; }; +} // namespace detail -using LengthE = Length; -using LengthNE = Length; -using LengthLE = Length; -using LengthGE = Length; -using LengthL = Length; -using LengthG = Length; +template +using Length = PredicateInterval; -using LengthMin = LengthGE; -using LengthMax = LengthLE; +static constexpr inline Length const length; -template -struct Filter> : public FilterBase> { - using Pred = Length; +template +struct Filter> { + using Pred = Length; template - static constexpr void init(Pred& p, Tree const& t) + static constexpr void init(Pred& p, Tree const& t) noexcept { - int max = t.maxNumDepthLevels(); - - if constexpr (PredicateCompare::EQUAL == PC || PredicateCompare::NOT_EQUAL == PC) { - for (int d{}; max > d; ++d) { - if (p.length == t.length(d)) { - p.depth_ = d; - return; - } + int min_d = -1; + int max_d = -1; + int num = t.maxNumDepthLevels(); + for (int d{}; d < num; ++d) { + auto l = t.length(d); + if (p.min >= l && p.max >= l) { + min_d = -1 == min_d ? d : min_d; + max_d = d; } + } - p.depth_ = max; - } else if constexpr (PredicateCompare::LESS_EQUAL == PC) { - p.depth_ = -1; + if (min_d > 0 && max_d > 0) { + p.min_depth = min_d; + p.max_depth = max_d; + return; + } - for (int d{}; max > d; ++d) { - if (p.length >= t.length(d)) { - p.depth_ = d; - } - } - } else if constexpr (PredicateCompare::LESS == PC) { - p.depth_ = -1; + if constexpr (Negated) { + p.min_depth = std::numeric_limits::lowest(); + p.max_depth = std::numeric_limits::max(); + } else { + p.min_depth = std::numeric_limits::max(); + p.max_depth = std::numeric_limits::lowest(); + } + } - for (int d{}; max > d; ++d) { - if (p.length > t.length(d)) { - p.depth_ = d; - } - } - } else if constexpr (PredicateCompare::GREATER_EQUAL == PC) { - for (int d{}; max > d; ++d) { - if (p.length <= t.length(d)) { - p.depth_ = d; - return; - } - } + template + [[nodiscard]] static constexpr bool returnableValue(Pred const&, Value const&) noexcept + { + return true; + } - p.depth_ = max; - } else if constexpr (PredicateCompare::GREATER == PC) { - for (int d{}; max > d; ++d) { - if (p.length < t.length(d)) { - p.depth_ = d; - return; - } - } + template + [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, + typename Tree::Node const& n) noexcept + { + // Cast to int to prevent int to be promoted to unsigned + int depth = static_cast(t.depth(n)); + if constexpr (Negated) { + return p.min_depth > depth || p.max_depth < depth; + } else { + return p.min_depth <= depth && p.max_depth >= depth; + } + } - p.depth_ = max; + template + [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, + typename Tree::Node const& n) noexcept + { + // Cast to int to prevent int to be promoted to unsigned + int depth = static_cast(t.depth(n)); + if constexpr (Negated) { + return 0 < p.min_depth || p.max_depth + 1 < depth; + } else { + return p.min_depth < depth; } } template - [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept { - return Filter>::returnable(p, t, n); + return returnable(p, t, n); } template - [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept { - return Filter>::traversable(p, t, n); + return traversable(p, t, n); } }; } // namespace ufo::pred diff --git a/lib/container/include/ufo/container/tree/predicate/modified.hpp b/lib/container/include/ufo/container/tree/predicate/modified.hpp old mode 100644 new mode 100755 index 4f33ef90..54ac638f --- a/lib/container/include/ufo/container/tree/predicate/modified.hpp +++ b/lib/container/include/ufo/container/tree/predicate/modified.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -52,23 +52,31 @@ struct Modified { }; template -constexpr Modified operator!(Modified) +[[nodiscard]] constexpr Modified operator!(Modified const&) noexcept { return Modified{}; } +static constexpr inline Modified const modified; + template -struct Filter> : public FilterBase> { +struct Filter> { using Pred = Modified; template - static constexpr void init(Pred&, Tree const&) + static constexpr void init(Pred&, Tree const&) noexcept + { + } + + template + [[nodiscard]] static constexpr bool returnableValue(Pred const&, Value const&) noexcept { + return true; } template [[nodiscard]] static constexpr bool returnable(Pred const&, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { if constexpr (Negated) { return !t.modified(n.index); @@ -79,7 +87,7 @@ struct Filter> : public FilterBase> { template [[nodiscard]] static constexpr bool traversable(Pred const&, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { if constexpr (Negated) { return true; @@ -87,6 +95,22 @@ struct Filter> : public FilterBase> { return t.modified(n.index); } } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return traversable(p, t, n); + } }; } // namespace ufo::pred diff --git a/lib/container/include/ufo/container/tree/predicate/offset.hpp b/lib/container/include/ufo/container/tree/predicate/offset.hpp old mode 100644 new mode 100755 index 490ed995..2c99e799 --- a/lib/container/include/ufo/container/tree/predicate/offset.hpp +++ b/lib/container/include/ufo/container/tree/predicate/offset.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -60,18 +60,26 @@ template return Offset(p.offset); } +static constexpr inline Offset const offset; + template -struct Filter> : public FilterBase> { +struct Filter> { using Pred = Offset; template - static constexpr void init(Pred&, Tree const&) + static constexpr void init(Pred&, Tree const&) noexcept { } + template + [[nodiscard]] static constexpr bool returnableValue(Pred const&, Value const&) noexcept + { + return true; + } + template - [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const&, + typename Tree::Node const& n) noexcept { if constexpr (Negated) { return p.offset != n.code.offset(); @@ -81,11 +89,27 @@ struct Filter> : public FilterBase> { } template - [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + [[nodiscard]] static constexpr bool traversable(Pred const&, Tree const&, + typename Tree::Node const&) noexcept { return true; } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return traversable(p, t, n); + } }; } // namespace ufo::pred diff --git a/lib/container/include/ufo/container/tree/predicate/or.hpp b/lib/container/include/ufo/container/tree/predicate/or.hpp old mode 100644 new mode 100755 index 1a2531a0..947f01b1 --- a/lib/container/include/ufo/container/tree/predicate/or.hpp +++ b/lib/container/include/ufo/container/tree/predicate/or.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -53,145 +53,101 @@ namespace ufo::pred { -template +template struct Or { - Or(Preds&&... preds) : preds(std::forward(preds)...) {} - Or(Preds const&... preds) : preds(preds...) {} + Or(Preds&&... preds) noexcept : preds(std::forward(preds)...) {} std::tuple preds; }; -template > && - !is_specialization_of_v>, - bool> = true> -constexpr Or, remove_cvref_t> operator||( - PredLeft&& p1, PredRight&& p2) -{ - return {std::forward(p1), std::forward(p2)}; -} - -template -constexpr Or operator||(Or&& p1, - Or&& p2) -{ - return std::apply( - [](auto&&... xs) { - return Or(std::forward(xs)...); - }, - std::tuple_cat(std::move(p1.preds), std::move(p2.preds))); -} - -template -constexpr Or operator||(Or const& p1, - Or const& p2) -{ - return std::apply( - [](auto&&... xs) { - return Or(std::forward(xs)...); - }, - std::tuple_cat(p1.preds, p2.preds)); -} - -template < - class... PredsLeft, class PredRight, - std::enable_if_t>, bool> = true> -constexpr Or> operator||(Or&& p1, - PredRight&& p2) -{ - return std::apply( - [](auto&&... xs) { - return Or>( - std::forward(xs)...); - }, - std::tuple_cat(std::move(p1.preds), std::make_tuple(std::forward(p2)))); -} - -template < - class... PredsLeft, class PredRight, - std::enable_if_t>, bool> = true> -constexpr Or> operator||( - Or const& p1, PredRight&& p2) +namespace detail { - return std::apply( - [](auto&&... xs) { - return Or>( - std::forward(xs)...); - }, - std::tuple_cat(p1.preds, std::make_tuple(std::forward(p2)))); -} - -template < - class PredLeft, class... PredsRight, - std::enable_if_t>, bool> = true> -constexpr Or, PredsRight...> operator||(PredLeft&& p1, - Or&& p2) +template +[[nodiscard]] constexpr auto getOrTuple(T&& t) noexcept { - return { - std::tuple_cat(std::make_tuple(std::forward(p1)), std::move(p2.preds))}; - - return std::apply( - [](auto&&... xs) { - return Or, PredsRight...>( - std::forward(xs)...); - }, - std::tuple_cat(std::make_tuple(std::forward(p1)), std::move(p2.preds))); + if constexpr (is_specialization_of_v>) { + return std::forward(t).preds; + } else { + return std::forward_as_tuple(std::forward(t)); + } } +} // namespace detail -template < - class PredLeft, class... PredsRight, - std::enable_if_t>, bool> = true> -constexpr Or, PredsRight...> operator||( - PredLeft&& p1, Or const& p2) +template +[[nodiscard]] constexpr auto operator||(PredLeft&& p1, PredRight&& p2) noexcept { return std::apply( - [](auto&&... xs) { - return Or, PredsRight...>( - std::forward(xs)...); + [](auto&&... preds) { + return Or...>( + std::forward(preds)...); }, - std::tuple_cat(std::make_tuple(std::forward(p1)), p2.preds)); + std::tuple_cat(detail::getOrTuple(std::forward(p1)), + detail::getOrTuple(std::forward(p2)))); } -template -struct Filter> : public FilterBase> { +template +struct Filter> { using Pred = Or; template - static constexpr void init(Pred& p, Tree const& t) + static constexpr void init(Pred& p, Tree const& t) noexcept { - std::apply( - [&t](auto&... p) { ((Filter>::init(p, t)), ...); }, - p.preds); + std::apply([&t](auto&&... preds) { (Filter::init(preds, t), ...); }, p.preds); } template - [[nodiscard]] static constexpr bool returnable(Pred const& p, Value const& v) + [[nodiscard]] static constexpr bool returnableValue(Pred const& p, + Value const& v) noexcept { return std::apply( - [&v](auto const&... p) { - return ((Filter>::returnable(p, v)) || ...); + [&v](auto&&... preds) { + return (Filter::returnableValue(preds, v) || ...); }, p.preds); } template [[nodiscard]] static constexpr bool returnable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { return std::apply( - [&t, &n](auto const&... p) { - return ((Filter>::returnable(p, t, n)) || ...); + [&t, &n](auto&&... preds) { + return (Filter::returnable(preds, t, n) || ...); }, p.preds); } template [[nodiscard]] static constexpr bool traversable(Pred const& p, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept + { + return std::apply( + [&t, &n](auto&&... preds) { + return (Filter::traversable(preds, t, n) || ...); + }, + p.preds); + } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const& r) noexcept { return std::apply( - [&t, &n](auto const&... p) { - return ((Filter>::traversable(p, t, n)) || ...); + [&t, &n, &r](auto&&... preds) { + return (Filter::returnableRay(preds, t, n, r) || ...); + }, + p.preds); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const& r) noexcept + { + return std::apply( + [&t, &n, &r](auto&&... preds) { + return (Filter::traversableRay(preds, t, n, r) || ...); }, p.preds); } @@ -199,15 +155,16 @@ struct Filter> : public FilterBase> { namespace detail { -template +template struct contains_pred> : std::disjunction...> { }; -template +template struct contains_always_pred> : std::conjunction...> { }; } // namespace detail + } // namespace ufo::pred #endif // UFO_CONTAINER_TREE_PREDICATE_OR_HPP \ No newline at end of file diff --git a/lib/container/include/ufo/container/tree/predicate/parent.hpp b/lib/container/include/ufo/container/tree/predicate/parent.hpp old mode 100644 new mode 100755 index 05ad55c8..a227cca9 --- a/lib/container/include/ufo/container/tree/predicate/parent.hpp +++ b/lib/container/include/ufo/container/tree/predicate/parent.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -50,28 +50,52 @@ namespace ufo::pred struct Parent { }; +static constexpr inline Parent const parent; + template <> -struct Filter : public FilterBase { +struct Filter { using Pred = Parent; template - static constexpr void init(Pred&, Tree const&) + static constexpr void init(Pred&, Tree const&) noexcept + { + } + + template + [[nodiscard]] static constexpr bool returnableValue(Pred const&, Value const&) noexcept { + return true; } template [[nodiscard]] static constexpr bool returnable(Pred const&, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { return t.isParent(n); } template [[nodiscard]] static constexpr bool traversable(Pred const&, Tree const& t, - typename Tree::Node const& n) + typename Tree::Node const& n) noexcept { return t.isParent(n); } + + template + [[nodiscard]] static constexpr bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return returnable(p, t, n); + } + + template + [[nodiscard]] static constexpr bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const&) noexcept + { + return traversable(p, t, n); + } }; } // namespace ufo::pred diff --git a/lib/container/include/ufo/container/tree/predicate/predicate.hpp b/lib/container/include/ufo/container/tree/predicate/predicate.hpp old mode 100644 new mode 100755 index 04966c0f..2ab67b1c --- a/lib/container/include/ufo/container/tree/predicate/predicate.hpp +++ b/lib/container/include/ufo/container/tree/predicate/predicate.hpp @@ -1,4 +1,4 @@ -/*! +/** * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown * * @author Daniel Duberg (dduberg@kth.se) @@ -53,17 +53,6 @@ namespace ufo::pred { namespace detail { -template -struct pred_has_value_type : std::false_type { -}; - -template -struct pred_has_value_type> : std::true_type { -}; - -template -constexpr inline bool pred_has_value_type_v = pred_has_value_type::value; - template class Dynamic { @@ -78,18 +67,24 @@ class Dynamic [[nodiscard]] virtual bool traversable(Tree const&, typename Tree::Node const&) const = 0; + [[nodiscard]] virtual bool returnableRay(Tree const&, typename Tree::Node const&, + typename Tree::Ray const&) const = 0; + + [[nodiscard]] virtual bool traversableRay(Tree const&, typename Tree::Node const&, + typename Tree::Ray const&) const = 0; + [[nodiscard]] virtual Dynamic* clone() const = 0; }; template -class Dynamic>> +class Dynamic> { public: virtual ~Dynamic() {} virtual void init(Tree const&) = 0; - [[nodiscard]] virtual bool returnable(typename Tree::value_type const& v) const = 0; + [[nodiscard]] virtual bool returnableValue(typename Tree::value_type const&) const = 0; [[nodiscard]] virtual bool returnable(Tree const&, typename Tree::Node const&) const = 0; @@ -97,6 +92,12 @@ class Dynamic>> [[nodiscard]] virtual bool traversable(Tree const&, typename Tree::Node const&) const = 0; + [[nodiscard]] virtual bool returnableRay(Tree const&, typename Tree::Node const&, + typename Tree::Ray const&) const = 0; + + [[nodiscard]] virtual bool traversableRay(Tree const&, typename Tree::Node const&, + typename Tree::Ray const&) const = 0; + [[nodiscard]] virtual Dynamic* clone() const = 0; }; @@ -106,9 +107,7 @@ class DynamicPredicate , public Predicate { public: - DynamicPredicate(Predicate const& pred) : Predicate(pred) {} - - DynamicPredicate(Predicate&& pred) : Predicate(std::move(pred)) {} + using Predicate::Predicate; DynamicPredicate(Tree const&, Predicate const& pred) : Predicate(pred) {} @@ -133,6 +132,20 @@ class DynamicPredicate return Filter::traversable(static_cast(*this), t, n); } + [[nodiscard]] bool returnableRay(Tree const& t, typename Tree::Node const& n, + typename Tree::Ray const& r) const override + { + return Filter::returnableRay(static_cast(*this), t, n, + r); + } + + [[nodiscard]] bool traversableRay(Tree const& t, typename Tree::Node const& n, + typename Tree::Ray const& r) const override + { + return Filter::traversableRay(static_cast(*this), t, n, + r); + } + protected: [[nodiscard]] DynamicPredicate* clone() const override { @@ -141,14 +154,12 @@ class DynamicPredicate }; template -class DynamicPredicate>> +class DynamicPredicate> : public Dynamic , public Predicate { public: - DynamicPredicate(Predicate const& pred) : Predicate(pred) {} - - DynamicPredicate(Predicate&& pred) : Predicate(std::move(pred)) {} + using Predicate::Predicate; DynamicPredicate(Tree const&, Predicate const& pred) : Predicate(pred) {} @@ -161,9 +172,9 @@ class DynamicPredicate::init(static_cast(*this), t); } - [[nodiscard]] bool returnable(typename Tree::value_type const& v) const override + [[nodiscard]] bool returnableValue(typename Tree::value_type const& v) const override { - return Filter::returnable(static_cast(*this), v); + return Filter::returnableValue(static_cast(*this), v); } [[nodiscard]] bool returnable(Tree const& t, @@ -178,6 +189,20 @@ class DynamicPredicate::traversable(static_cast(*this), t, n); } + [[nodiscard]] bool returnableRay(Tree const& t, typename Tree::Node const& n, + typename Tree::Ray const& r) const override + { + return Filter::returnableRay(static_cast(*this), t, n, + r); + } + + [[nodiscard]] bool traversableRay(Tree const& t, typename Tree::Node const& n, + typename Tree::Ray const& r) const override + { + return Filter::traversableRay(static_cast(*this), t, n, + r); + } + protected: [[nodiscard]] DynamicPredicate* clone() const override { @@ -200,9 +225,11 @@ class Predicate } template + requires Filterable> Predicate(Pred&& pred) - : predicate_(std::make_unique>( - std::forward(pred))) + : predicate_( + std::make_unique>>( + std::forward(pred))) { } @@ -229,10 +256,12 @@ class Predicate } template + requires Filterable> Predicate& operator=(Pred&& pred) { predicate_ = - std::make_unique>(std::forward(pred)); + std::make_unique>>( + std::forward(pred)); return *this; } @@ -240,7 +269,7 @@ class Predicate { if (rhs.hasPredicate()) { if (hasPredicate()) { - *this = *this && rhs; + *this = (*this && rhs); } else { predicate_.reset(rhs.predicate_->clone()); } @@ -252,7 +281,7 @@ class Predicate { if (rhs.hasPredicate()) { if (hasPredicate()) { - *this = *this || rhs; + *this = (*this || rhs); } else { predicate_.reset(rhs.predicate_->clone()); } @@ -269,11 +298,10 @@ class Predicate } } - template , bool> = true> - [[nodiscard]] bool returnable(typename Tree2::value_type const& v) const + [[nodiscard]] bool returnableValue(typename Tree::value_type const& v) const + requires requires { typename Tree::value_type; } { - return !hasPredicate() || predicate_->returnable(v); + return !hasPredicate() || predicate_->returnableValue(v); } [[nodiscard]] bool returnable(Tree const& t, typename Tree::Node const& n) const @@ -286,6 +314,18 @@ class Predicate return !hasPredicate() || predicate_->traversable(t, n); } + [[nodiscard]] bool returnableRay(Tree const& t, typename Tree::Node const& n, + typename Tree::Ray const& r) const + { + return !hasPredicate() || predicate_->returnableRay(t, n, r); + } + + [[nodiscard]] bool traversableRay(Tree const& t, typename Tree::Node const& n, + typename Tree::Ray const& r) const + { + return !hasPredicate() || predicate_->traversableRay(t, n, r); + } + private: std::unique_ptr> predicate_; }; @@ -300,11 +340,11 @@ struct Filter> { static void init(Pred& p, Tree const& t) { p.init(t); } - template , bool> = true> - [[nodiscard]] static bool returnable(Pred const& p, typename Tree2::value_type const& v) + [[nodiscard]] static bool returnableValue(Pred const& p, + typename Tree::value_type const& v) + requires requires { typename Tree::value_type; } { - return p.returnable(v); + return p.returnableValue(v); } [[nodiscard]] static bool returnable(Pred const& p, Tree const& t, @@ -318,6 +358,20 @@ struct Filter> { { return p.traversable(t, n); } + + [[nodiscard]] static bool returnableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const& r) + { + return p.returnableRay(t, n, r); + } + + [[nodiscard]] static bool traversableRay(Pred const& p, Tree const& t, + typename Tree::Node const& n, + typename Tree::Ray const& r) + { + return p.traversableRay(t, n, r); + } }; } // namespace ufo::pred diff --git a/lib/container/include/ufo/container/tree/predicate/predicate_compare.hpp b/lib/container/include/ufo/container/tree/predicate/predicate_compare.hpp deleted file mode 100644 index e97c2edc..00000000 --- a/lib/container/include/ufo/container/tree/predicate/predicate_compare.hpp +++ /dev/null @@ -1,179 +0,0 @@ -/*! - * UFOMap: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown - * - * @author Daniel Duberg (dduberg@kth.se) - * @see https://github.com/UnknownFreeOccupied/ufomap - * @version 1.0 - * @date 2022-05-13 - * - * @copyright Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * - * BSD 3-Clause License - * - * Copyright (c) 2022, Daniel Duberg, KTH Royal Institute of Technology - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. 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. - * - * 3. 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. - * - * THIS 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. - */ - -#ifndef UFO_CONTAINER_TREE_PREDICATE_PREDICATE_COMPARE_HPP -#define UFO_CONTAINER_TREE_PREDICATE_PREDICATE_COMPARE_HPP - -// STL -#include - -namespace ufo::pred -{ -enum class PredicateCompare { - EQUAL, - NOT_EQUAL, - LESS_EQUAL, - GREATER_EQUAL, - LESS, - GREATER -}; - -template -[[nodiscard]] constexpr std::string_view enumToString() noexcept -{ - using namespace std::literals; - if constexpr (PredicateCompare::EQUAL == PC) { - return "="sv; - } else if constexpr (PredicateCompare::NOT_EQUAL == PC) { - return "~="sv; - } else if constexpr (PredicateCompare::LESS_EQUAL == PC) { - return "<="sv; - } else if constexpr (PredicateCompare::GREATER_EQUAL == PC) { - return ">="sv; - } else if constexpr (PredicateCompare::LESS == PC) { - return "<"sv; - } else if constexpr (PredicateCompare::GREATER == PC) { - return ">"sv; - } else { - // Error - } -} - -[[nodiscard]] constexpr std::string_view enumToString(PredicateCompare pc) noexcept -{ - switch (pc) { - case PredicateCompare::EQUAL: return enumToString(); - case PredicateCompare::NOT_EQUAL: return enumToString(); - case PredicateCompare::LESS_EQUAL: - return enumToString(); - case PredicateCompare::GREATER_EQUAL: - return enumToString(); - case PredicateCompare::LESS: return enumToString(); - case PredicateCompare::GREATER: return enumToString(); - } -} - -[[nodiscard]] constexpr PredicateCompare switchSide(PredicateCompare PC) noexcept -{ - switch (PC) { - case PredicateCompare::EQUAL: return PredicateCompare::EQUAL; - case PredicateCompare::NOT_EQUAL: return PredicateCompare::NOT_EQUAL; - case PredicateCompare::LESS_EQUAL: return PredicateCompare::GREATER_EQUAL; - case PredicateCompare::GREATER_EQUAL: return PredicateCompare::LESS_EQUAL; - case PredicateCompare::LESS: return PredicateCompare::GREATER; - case PredicateCompare::GREATER: return PredicateCompare::LESS; - } -} - -template