├── debian ├── compat ├── docs ├── source │ └── format ├── mesh-sampling.install ├── libmesh-sampling-dev.install ├── changelog ├── rules ├── control └── copyright ├── .gitignore ├── .gitmodules ├── sample └── sampling_example.png ├── .clang-format-fix.sh ├── CMakeModules ├── FindCLI11.cmake └── Findmesh_sampling_3rd_party_assimp.cmake ├── tests ├── test_mesh_sampling │ ├── src │ │ ├── CMakeLists.txt │ │ └── main.cpp │ └── CMakeLists.txt ├── CMakeLists.txt └── testWeightedRandomSampling.cpp ├── utils ├── CMakeLists.txt └── mesh_sampling.cpp ├── .github └── workflows │ ├── scripts │ └── test-usage.sh │ ├── package.yml │ └── build.yml ├── .clang-format-check.sh ├── include └── mesh_sampling │ ├── qhull_io.h │ ├── assimp_scene.h │ ├── mesh_sampling.h │ └── weighted_random_sampling.h ├── .pre-commit-config.yaml ├── src ├── CMakeLists.txt ├── weighted_random_sampling.cpp └── mesh_sampling.cpp ├── LICENSE ├── CMakeLists.txt ├── .clang-format ├── README.md ├── COPYING └── ext └── effolkronium └── random.hpp /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | compile_commands.json 2 | *.pyc 3 | build/ 4 | .cache/ 5 | .vscode/ 6 | -------------------------------------------------------------------------------- /debian/mesh-sampling.install: -------------------------------------------------------------------------------- 1 | usr/lib/libmesh_sampling.so 2 | usr/bin/mesh_sampling 3 | -------------------------------------------------------------------------------- /debian/libmesh-sampling-dev.install: -------------------------------------------------------------------------------- 1 | usr/include/* 2 | usr/lib/*/pkgconfig/* 3 | usr/lib/cmake/* 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "cmake"] 2 | path = cmake 3 | url = https://github.com/jrl-umi3218/jrl-cmakemodules.git 4 | -------------------------------------------------------------------------------- /sample/sampling_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrl-umi3218/mesh_sampling/HEAD/sample/sampling_example.png -------------------------------------------------------------------------------- /.clang-format-fix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source .clang-format-common.sh 4 | 5 | for f in ${src_files}; do 6 | $clang_format -style=file -i $f 7 | done 8 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | mesh-sampling (1.0.0-1ubuntu1) unstable; urgency=low 2 | 3 | * Initial release 4 | 5 | -- Arnaud Tanguy Sun, 01 Mar 2020 12:46:48 +0900 6 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | 4 | export DH_VERBOSE=1 5 | 6 | %: 7 | dh $@ 8 | 9 | override_dh_auto_configure: 10 | dh_auto_configure -- -DFETCHCONTENT_FULLY_DISCONNECTED=OFF 11 | -------------------------------------------------------------------------------- /CMakeModules/FindCLI11.cmake: -------------------------------------------------------------------------------- 1 | # FetchCLI11.cmake 2 | include(FetchContent) 3 | 4 | FetchContent_Declare( 5 | cli11_proj 6 | QUIET 7 | GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git 8 | GIT_TAG v2.5.0) 9 | 10 | FetchContent_MakeAvailable(cli11_proj) 11 | -------------------------------------------------------------------------------- /tests/test_mesh_sampling/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(test_mesh_sampling main.cpp) 2 | target_link_libraries(test_mesh_sampling PUBLIC mesh_sampling::mesh_sampling) 3 | target_include_directories( 4 | test_mesh_sampling PUBLIC $ 5 | $) 6 | -------------------------------------------------------------------------------- /utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(mesh_sampling_bin mesh_sampling.cpp) 2 | target_link_libraries(mesh_sampling_bin PUBLIC mesh_sampling CLI11::CLI11) 3 | set_target_properties(mesh_sampling_bin PROPERTIES OUTPUT_NAME mesh_sampling) 4 | 5 | install( 6 | TARGETS mesh_sampling_bin 7 | RUNTIME DESTINATION bin 8 | LIBRARY DESTINATION lib 9 | ARCHIVE DESTINATION lib) 10 | -------------------------------------------------------------------------------- /.github/workflows/scripts/test-usage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | readonly CMAKE_BUILD_TYPE=$1 4 | readonly this_dir=`cd $(dirname $0); pwd` 5 | readonly root_dir=`cd $this_dir/../../../; pwd` 6 | readonly project_dir=$root_dir/tests/test_mesh_sampling 7 | 8 | mkdir -p $project_dir/build 9 | 10 | cd $project_dir/build 11 | cmake ../ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} || exit 1 12 | cmake --build . --config ${CMAKE_BUILD_TYPE} || exit 1 13 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(GTest REQUIRED) 2 | include(GoogleTest) 3 | 4 | macro(mesh_sampling_test NAME) 5 | add_executable(${NAME} ${NAME}.cpp) 6 | target_link_libraries(${NAME} PUBLIC GTest::gtest ${ARGN}) 7 | target_compile_definitions( 8 | ${NAME} PRIVATE -DPROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}") 9 | gtest_discover_tests(${NAME}) 10 | endmacro() 11 | 12 | mesh_sampling_test(testWeightedRandomSampling mesh_sampling) 13 | -------------------------------------------------------------------------------- /.clang-format-check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source .clang-format-common.sh 4 | 5 | out=0 6 | tmpfile=$(mktemp /tmp/clang-format-check.XXXXXX) 7 | for f in ${src_files}; do 8 | $clang_format -style=file $f > $tmpfile 9 | if ! [[ -z `diff $tmpfile $f` ]]; then 10 | echo "Wrong formatting in $f" 11 | out=1 12 | fi 13 | done 14 | rm -f $tmpfile 15 | if [[ $out -eq 1 ]]; then 16 | echo "You can run ./.clang-format-fix.sh to fix the issues locally, then commit/push again" 17 | fi 18 | exit $out 19 | -------------------------------------------------------------------------------- /tests/test_mesh_sampling/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) 2 | 3 | set(PROJECT_NAME test_mesh_sampling) 4 | set(PROJECT_DESCRIPTION "Sample pointcloud from meshes") 5 | set(PROJECT_URL https://github.com/jrl-umi3218/mesh_sampling) 6 | set(PROJECT_VERSION 1.0.0) 7 | set(PROJECT_USE_CMAKE_EXPORT FALSE) 8 | set(PROJECT_DEBUG_POSTFIX "_d") 9 | set(CXX_DISABLE_WERROR 1) 10 | set(CMAKE_CXX_STANDARD 17) 11 | 12 | project( 13 | ${PROJECT_NAME} 14 | LANGUAGES CXX 15 | VERSION ${PROJECT_VERSION}) 16 | find_package(mesh_sampling REQUIRED) 17 | 18 | include_directories(include) 19 | add_subdirectory(src) 20 | -------------------------------------------------------------------------------- /.github/workflows/package.yml: -------------------------------------------------------------------------------- 1 | name: Package mesh_sampling 2 | on: 3 | repository_dispatch: 4 | types: 5 | - package-master 6 | - package-release 7 | push: 8 | branches: 9 | - "**" 10 | tags: 11 | - v* 12 | pull_request: 13 | branches: 14 | - "**" 15 | jobs: 16 | package: 17 | uses: jrl-umi3218/github-actions/.github/workflows/package-project.yml@master 18 | with: 19 | latest-cmake: true 20 | update-stable-and-head: true 21 | matrix: | 22 | { 23 | "dist": ["jammy", "noble"], 24 | "arch": ["amd64"] 25 | } 26 | secrets: 27 | CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }} 28 | GH_TOKEN: ${{ secrets.GH_PAGES_TOKEN }} 29 | -------------------------------------------------------------------------------- /include/mesh_sampling/qhull_io.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2020 CNRS-UM LIRMM, CNRS-AIST JRL 3 | */ 4 | 5 | #pragma once 6 | #include 7 | #include 8 | #include 9 | namespace mesh_sampling 10 | { 11 | 12 | using CloudT = std::vector; 13 | namespace io 14 | { 15 | 16 | bool saveQhullFile(const std::string & path, const CloudT & cloud) 17 | { 18 | std::ofstream file; 19 | file.open(path, std::ios::out); 20 | if(!file.is_open()) 21 | { 22 | return false; 23 | } 24 | 25 | file << "3" << std::endl; 26 | file << std::to_string(cloud.size()) << std::endl; 27 | for(const auto & point : cloud) 28 | { 29 | file << point.x() << " " << point.y() << " " << point.z() << std::endl; 30 | } 31 | 32 | return true; 33 | } 34 | 35 | } // namespace io 36 | } // namespace mesh_sampling 37 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | ci: 2 | autoupdate_schedule: quarterly 3 | exclude: '^ext/' 4 | repos: 5 | - repo: meta 6 | hooks: 7 | - id: check-useless-excludes 8 | - id: check-hooks-apply 9 | - repo: https://github.com/pre-commit/mirrors-clang-format 10 | rev: v19.1.7 11 | hooks: 12 | - id: clang-format 13 | types_or: [c++, c] 14 | - repo: https://github.com/pre-commit/pre-commit-hooks 15 | rev: v5.0.0 16 | hooks: 17 | - id: check-added-large-files 18 | - id: check-executables-have-shebangs 19 | - id: check-merge-conflict 20 | - id: destroyed-symlinks 21 | - id: detect-private-key 22 | - id: end-of-file-fixer 23 | - id: fix-byte-order-marker 24 | - id: mixed-line-ending 25 | - id: trailing-whitespace 26 | 27 | - repo: https://github.com/cheshirekow/cmake-format-precommit 28 | rev: v0.6.13 29 | hooks: 30 | - id: cmake-format 31 | -------------------------------------------------------------------------------- /CMakeModules/Findmesh_sampling_3rd_party_assimp.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL 3 | # 4 | 5 | # Try to find the ASSIMP library 6 | # 7 | # If the library is found, then you can use the mesh_sampling::assimp target 8 | # 9 | 10 | if(NOT TARGET mesh_sampling::assimp) 11 | message(STATUS "Checking for module 'ASSIMP'") 12 | find_package(assimp QUIET) 13 | if(NOT ${assimp_FOUND}) 14 | message(FATAL_ERROR "Could not find the ASSIMP library") 15 | endif() 16 | 17 | message( 18 | STATUS 19 | "Found assimp library: ${assimp_VERSION_MAJOR}.${assimp_VERSION_MINOR}.${assimp_VERSION_PATCH}" 20 | ) 21 | add_library(mesh_sampling::assimp INTERFACE IMPORTED) 22 | set_target_properties( 23 | mesh_sampling::assimp 24 | PROPERTIES # INTERFACE_INCLUDE_DIRECTORIES ${ASSIMP_INCLUDE_DIRS} 25 | INTERFACE_LINK_LIBRARIES "${ASSIMP_LIBRARIES}") 26 | endif() 27 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(mesh_sampling SHARED mesh_sampling.cpp weighted_random_sampling.cpp) 2 | 3 | target_link_libraries( 4 | mesh_sampling PUBLIC mesh_sampling::assimp Qhull::qhullcpp Qhull::qhull_r 5 | Eigen3::Eigen) 6 | 7 | if(TARGET Eigen3::Eigen) 8 | target_link_libraries(mesh_sampling PUBLIC Eigen3::Eigen) 9 | else() # legacy for older eigen installations, this is most likely no longer 10 | # needed 11 | target_include_directories(mesh_sampling SYSTEM ${Eigen_INCLUDE_DIR}) 12 | endif() 13 | target_include_directories( 14 | mesh_sampling PUBLIC $ 15 | $) 16 | 17 | target_include_directories(mesh_sampling 18 | PRIVATE $) 19 | 20 | install( 21 | TARGETS mesh_sampling 22 | EXPORT "${TARGETS_EXPORT_NAME}" 23 | RUNTIME DESTINATION bin 24 | LIBRARY DESTINATION lib 25 | ARCHIVE DESTINATION lib) 26 | 27 | install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION include) 28 | -------------------------------------------------------------------------------- /src/weighted_random_sampling.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2020 CNRS-UM LIRMM, CNRS-AIST JRL 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | using Random = effolkronium::random_static; 9 | 10 | namespace mesh_sampling 11 | { 12 | 13 | double triangle_area(const Eigen::Vector3f & v1, const Eigen::Vector3f & v2, const Eigen::Vector3f & v3) 14 | { 15 | return 0.5 * (v2 - v1).cross(v3 - v1).norm(); 16 | } 17 | 18 | /** 19 | * @brief Use barycentric coordinates to compute random point coordinates in a 20 | * triangle 21 | * 22 | * @param v1,v2,v3 : triangle vertices 23 | * 24 | * @return a random point in the triangle (v1,v2,v3) 25 | */ 26 | Eigen::Vector3f random_point_in_triangle(const Eigen::Vector3f & v1, 27 | const Eigen::Vector3f & v2, 28 | const Eigen::Vector3f & v3) 29 | { 30 | float u = Random::get(0.0f, 1.0f); 31 | float v = Random::get(0.0f, 1.0f); 32 | if(u + v > 1) 33 | { 34 | u = 1 - u; 35 | v = 1 - v; 36 | } 37 | return (v1 * u) + (v2 * v) + ((1 - (u + v)) * v3); 38 | } 39 | 40 | } // namespace mesh_sampling 41 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: mesh-sampling 2 | Priority: optional 3 | Maintainer: Arnaud TANGUY 4 | Standards-Version: 3.9.5 5 | Section: libdevel 6 | Homepage: http://github.com/arntanguy/mesh_sampling 7 | Vcs-Git: git://github.com/arntanguy/mesh_sampling 8 | Vcs-Browser: http://github.com/arntanguy/mesh_sampling 9 | Build-Depends: debhelper (>= 9), 10 | pkg-config, 11 | cmake, 12 | libassimp-dev, 13 | doxygen, 14 | libeigen3-dev (>= 3.2), 15 | libgtest-dev, 16 | libqhull-dev 17 | 18 | Package: libmesh-sampling-dev 19 | Section: libdevel 20 | Architecture: any 21 | Depends: pkg-config, 22 | mesh-sampling (= ${binary:Version}), 23 | libeigen3-dev (>= 3.2), 24 | libassimp-dev, 25 | libgtest-dev, 26 | libqhull-dev, 27 | ${misc:Depends} 28 | Description: mesh_sampling Automatic sampling of 3D pointcloud from CAD models 29 | mesh_sampling implements samplers that convert from 3D CAD models to PCL pointcloud (PCD) 30 | 31 | Package: mesh-sampling 32 | Section: libs 33 | Architecture: any 34 | Depends: ${shlibs:Depends}, 35 | ${misc:Depends} 36 | Description: mesh_sampling Automatic sampling of 3D pointcloud from CAD models 37 | mesh_sampling implements samplers that convert from 3D CAD models to PCL pointcloud (PCD) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2012-2019, CNRS-UM LIRMM, CNRS-AIST JRL 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) 2 | 3 | set(PROJECT_NAME mesh_sampling) 4 | set(PROJECT_DESCRIPTION "Sample pointcloud from meshes") 5 | set(PROJECT_URL https://github.com/jrl-umi3218/mesh_sampling) 6 | set(PROJECT_VERSION 1.0.0) 7 | set(PROJECT_USE_CMAKE_EXPORT TRUE) 8 | set(PROJECT_DEBUG_POSTFIX "_d") 9 | set(CXX_DISABLE_WERROR 1) 10 | set(CMAKE_CXX_STANDARD 17) 11 | 12 | include(cmake/base.cmake) 13 | 14 | project( 15 | ${PROJECT_NAME} 16 | LANGUAGES CXX 17 | VERSION ${PROJECT_VERSION}) 18 | 19 | list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules) 20 | 21 | # Note: Since we are using custom cmake scripts to look at 22 | # mesh_sampling_3rd_party_assimp and create clean modern cmake targets, using 23 | # add_project_dependency leads to the mesh_samplingConfig script to try and find 24 | # these dependencies before the path to the required 25 | # mesh_sampling_3rd_party_*.cmake have been included. 26 | find_package(mesh_sampling_3rd_party_assimp REQUIRED) 27 | find_package(CLI11 REQUIRED) 28 | add_project_dependency(Eigen3 REQUIRED NO_MODULE) 29 | add_project_dependency(Qhull REQUIRED) 30 | 31 | install( 32 | FILES CMakeModules/Findmesh_sampling_3rd_party_assimp.cmake 33 | DESTINATION lib/cmake/mesh_sampling 34 | RENAME mesh_sampling_3rd_party_assimpConfig.cmake) 35 | 36 | # Export custom package dependencies to the mesh_samplingConfig script such that 37 | # they are found when importing the package with find_package(mesh_sampling) 38 | set(PACKAGE_EXTRA_MACROS 39 | "include(\"\$\{CMAKE_CURRENT_LIST_DIR\}/mesh_sampling_3rd_party_assimpConfig.cmake\")" 40 | ) 41 | 42 | add_subdirectory(src) 43 | add_subdirectory(utils) 44 | 45 | if(${BUILD_TESTING}) 46 | add_subdirectory(tests) 47 | endif() 48 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI of mesh_sampling 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | pull_request: 8 | branches: 9 | - '**' 10 | 11 | jobs: 12 | clang-format: 13 | runs-on: ubuntu-22.04 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Install clang-format 17 | run: | 18 | sudo apt-get -qq update 19 | sudo apt-get -qq install clang-format-14 20 | - name: Run clang-format-check 21 | run: | 22 | ./.clang-format-check.sh 23 | 24 | build: 25 | needs: clang-format 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | os: [ubuntu-22.04, ubuntu-24.04] 30 | build-type: [Debug, RelWithDebInfo] 31 | compiler: [gcc, clang] 32 | 33 | runs-on: ${{ matrix.os }} 34 | steps: 35 | - uses: actions/checkout@v4 36 | with: 37 | submodules: recursive 38 | - name: Install dependencies 39 | uses: jrl-umi3218/github-actions/install-dependencies@master 40 | with: 41 | compiler: ${{ matrix.compiler }} 42 | build-type: ${{ matrix.build-type }} 43 | ubuntu: | 44 | apt: libeigen3-dev libqhull-dev libproj-dev libassimp-dev libgtest-dev doxygen doxygen-latex 45 | - name: Build and test 46 | uses: jrl-umi3218/github-actions/build-cmake-project@master 47 | with: 48 | compiler: ${{ matrix.compiler }} 49 | build-type: ${{ matrix.build-type }} 50 | # Checks that finding the library and building against it works 51 | - name: Check usage 52 | shell: bash 53 | run: | 54 | set -x 55 | export LD_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH}" 56 | export CMAKE_PREFIX_PATH="/usr/lib/x86_64-linux-gnu/cmake:${CMAKE_PREFIX_PATH}" 57 | 58 | ./.github/workflows/scripts/test-usage.sh ${{ matrix.build-type }} 59 | -------------------------------------------------------------------------------- /tests/testWeightedRandomSampling.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifndef PROJECT_SOURCE_DIR 13 | # error "PROJECT_SOURCE_DIR must be defined to compile this file" 14 | #endif 15 | 16 | using namespace mesh_sampling; 17 | 18 | // This is a very simple test that merely loads a sample CAD model, 19 | // and samples N point from it. 20 | // It only tests that the object can be loaded, and that the number of points 21 | // after sampling is as expected 22 | TEST(TestWeightedRandomSampling, Test1) 23 | { 24 | const std::string model_path = PROJECT_SOURCE_DIR "/sample/suzanne.obj"; 25 | size_t N = 1000; 26 | 27 | ASSERT_THROW(ASSIMPScene("wrong path.dae"), std::runtime_error); 28 | MeshSampling sampler(model_path); 29 | 30 | std::cout << "Sampling " << N << " points from " << model_path << std::endl; 31 | WeightedRandomSampling sampling_xyz(sampler.mesh(model_path)->scene()); 32 | auto cloud_xyz = sampling_xyz.weighted_random_sampling(N); 33 | // pcl::io::savePCDFileASCII("/tmp/example_xyz.pcd", *cloud_xyz); 34 | ASSERT_TRUE(cloud_xyz->size() == N); 35 | 36 | WeightedRandomSampling sampling_rgb(sampler.mesh(model_path)->scene()); 37 | auto cloud_rgb = sampling_rgb.weighted_random_sampling(N); 38 | ASSERT_TRUE(cloud_rgb->size() == N); 39 | 40 | WeightedRandomSampling sampling_normal(sampler.mesh(model_path)->scene()); 41 | auto cloud_normal = sampling_normal.weighted_random_sampling(N); 42 | ASSERT_TRUE(cloud_normal->size() == N); 43 | 44 | WeightedRandomSampling sampling_rgb_normal(sampler.mesh(model_path)->scene()); 45 | auto cloud_rgb_normal = sampling_rgb_normal.weighted_random_sampling(N); 46 | ASSERT_TRUE(cloud_rgb_normal->size() == N); 47 | } 48 | 49 | int main(int argc, char ** argv) 50 | { 51 | testing::InitGoogleTest(&argc, argv); 52 | return RUN_ALL_TESTS(); 53 | } 54 | -------------------------------------------------------------------------------- /tests/test_mesh_sampling/src/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 CNRS-UM LIRMM 2 | // Copyright 2017 Arnaud TANGUY 3 | // 4 | // This file is part of mesh_sampling. 5 | // 6 | // mesh_sampling is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU Lesser General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // mesh_sampling is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU Lesser General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU Lesser General Public License 17 | // along with mesh_sampling. If not, see . 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | using namespace mesh_sampling; 25 | 26 | void help() 27 | { 28 | std::cout << "Usage: ./example path_to_model number_of_points" << std::endl; 29 | exit(1); 30 | } 31 | 32 | int main(int argc, char ** argv) 33 | { 34 | std::string model_path = ""; 35 | int N = 100000; 36 | if(argc > 1) 37 | { 38 | if(std::string(argv[1]) == "-h" || std::string(argv[1]) == "--help") 39 | { 40 | help(); 41 | } 42 | else 43 | { 44 | model_path = argv[1]; 45 | } 46 | if(argc == 3) 47 | { 48 | N = atoi(argv[2]); 49 | } 50 | } 51 | else 52 | { 53 | help(); 54 | } 55 | 56 | MeshSampling sampler(model_path); 57 | 58 | std::cout << "Sampling " << N << " points from " << model_path << std::endl; 59 | WeightedRandomSampling sampling_xyz(sampler.mesh(model_path)->scene()); 60 | auto cloud_xyz = sampling_xyz.weighted_random_sampling(N); 61 | 62 | WeightedRandomSampling sampling_rgb(sampler.mesh(model_path)->scene()); 63 | auto cloud_rgb = sampling_rgb.weighted_random_sampling(N); 64 | 65 | WeightedRandomSampling sampling_normal(sampler.mesh(model_path)->scene()); 66 | auto cloud_normal = sampling_normal.weighted_random_sampling(N); 67 | 68 | WeightedRandomSampling sampling_rgb_normal(sampler.mesh(model_path)->scene()); 69 | auto cloud_rgb_normal = sampling_rgb_normal.weighted_random_sampling(N); 70 | } 71 | -------------------------------------------------------------------------------- /include/mesh_sampling/assimp_scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | namespace fs = std::filesystem; 10 | 11 | namespace mesh_sampling 12 | { 13 | 14 | struct ASSIMPScene 15 | { 16 | ASSIMPScene(const std::string & model_path) : modelPath_(model_path) 17 | { 18 | loadScene(); 19 | } 20 | 21 | ASSIMPScene(const std::string & model_path, float scale) : modelPath_(model_path) 22 | { 23 | importer.SetPropertyFloat(AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, scale); 24 | loadScene(); 25 | } 26 | 27 | /** 28 | * @brief Do not store the returned pointer, only use it 29 | * 30 | * @return 31 | */ 32 | const aiScene * scene() const 33 | { 34 | return scene_; 35 | } 36 | 37 | /** 38 | * @brief Export the scene to a file in a format supported by ASSIMP 39 | * 40 | * @param path Export path 41 | */ 42 | void exportScene(const std::string & path, const bool binary = true) 43 | { 44 | fs::path out_path(path); 45 | auto ext = out_path.extension().string(); 46 | std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return std::tolower(c); }); 47 | if(ext.empty()) 48 | { 49 | throw std::runtime_error("Could't export scene " + modelPath_ + " to " + path + ": invalid extension"); 50 | } 51 | ext.erase(0, 1); // remove leading "." 52 | if(binary) ext += "b"; // ASSIMP suffixes binary export formats with "b" 53 | Assimp::Exporter exporter; 54 | aiReturn ret = exporter.Export(scene_, ext, path); 55 | if(ret != AI_SUCCESS) 56 | { 57 | throw std::runtime_error("ASSIMP failed to export scene " + modelPath_ + " to " + path + ": " 58 | + exporter.GetErrorString()); 59 | } 60 | } 61 | 62 | protected: 63 | void loadScene() 64 | { 65 | scene_ = 66 | importer.ReadFile(modelPath_, aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_GenNormals 67 | | aiProcess_FixInfacingNormals | aiProcess_GlobalScale); 68 | 69 | // If the import failed, report it 70 | if(!scene_) 71 | { 72 | throw std::runtime_error(importer.GetErrorString()); 73 | } 74 | } 75 | 76 | protected: 77 | // The importer will automatically delete the scene 78 | Assimp::Importer importer; 79 | const aiScene * scene_; 80 | std::string modelPath_; 81 | }; 82 | 83 | } // namespace mesh_sampling 84 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: mc_rtc 3 | Source: https://github.com/arntanguy/mesh_sampling 4 | 5 | Files: * 6 | Copyright: 2015-2020 CNRS-UM LIRMM 7 | 2015-2020 CNRS-AIST JRL 8 | License: BSD-2-Clause 9 | BSD 2-Clause License 10 | . 11 | Copyright (c) 2012-2020, CNRS-UM LIRMM, CNRS-AIST JRL 12 | All rights reserved. 13 | . 14 | Redistribution and use in source and binary forms, with or without 15 | modification, are permitted provided that the following conditions are met: 16 | . 17 | 1. Redistributions of source code must retain the above copyright notice, this 18 | list of conditions and the following disclaimer. 19 | . 20 | 2. Redistributions in binary form must reproduce the above copyright notice, 21 | this list of conditions and the following disclaimer in the documentation 22 | and/or other materials provided with the distribution. 23 | . 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | 35 | 36 | Files: debian/* 37 | Copyright: 2016-2020 Pierre Gergondet 38 | License: GPL-2+ 39 | This package is free software; you can redistribute it and/or modify 40 | it under the terms of the GNU General Public License as published by 41 | the Free Software Foundation; either version 2 of the License, or 42 | (at your option) any later version. 43 | . 44 | This package is distributed in the hope that it will be useful, 45 | but WITHOUT ANY WARRANTY; without even the implied warranty of 46 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 47 | GNU General Public License for more details. 48 | . 49 | You should have received a copy of the GNU General Public License 50 | along with this program. If not, see 51 | . 52 | On Debian systems, the complete text of the GNU General 53 | Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". 54 | -------------------------------------------------------------------------------- /utils/mesh_sampling.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2025 CNRS-UM LIRMM, CNRS-AIST JRL 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | namespace fs = std::filesystem; 9 | 10 | using namespace mesh_sampling; 11 | 12 | int main(int argc, char ** argv) 13 | { 14 | CLI::App app("mesh_sampling"); 15 | argv = app.ensure_utf8(argv); 16 | 17 | fs::path in; 18 | app.add_option("--in", in, "Input mesh (supported by ASSIMP)")->required()->check(CLI::ExistingPath); 19 | 20 | fs::path out; 21 | app.add_option("--out", out, "Output file (ply, pcd, qc, stl)"); 22 | 23 | fs::path convex; 24 | app.add_option("--convex", convex, "Output convex directory")->check(CLI::ExistingPath); 25 | 26 | unsigned int N; 27 | app.add_option("--samples", N, "Number of points to sample")->default_val(10000)->check(CLI::PositiveNumber); 28 | 29 | float scale; 30 | app.add_option("--scale", scale, "Scale factor applied to the mesh")->default_val(1.0)->check(CLI::Range(0, 1)); 31 | 32 | std::string cloud_type = "xyz_rgb_normal"; 33 | app.add_option("--type, -t", cloud_type, "Type of cloud to generate (xyz, xyz_rgb, xyz_rgb_normal)"); 34 | 35 | bool binary_format; 36 | app.add_option("--binary, -b", binary_format, "Outputs in binary format (default: false)")->default_val(false); 37 | 38 | bool convert; 39 | app.add_option("--convert", convert, "Convert from one mesh type to another (supported by ASSIMP)") 40 | ->default_val(false); 41 | 42 | CLI11_PARSE(app, argc, argv); 43 | 44 | MeshSampling mesh_sampler(in); 45 | 46 | if(!mesh_sampler.check_supported(cloud_type)) 47 | { 48 | std::cerr << "Type not suppported : "; 49 | std::copy(mesh_sampling::supported_cloud_type.begin(), mesh_sampling::supported_cloud_type.end(), 50 | std::ostream_iterator(std::cerr, ", ")); 51 | std::cerr << std::endl; 52 | } 53 | 54 | if(convert) 55 | { 56 | mesh_sampler.convertTo(out, binary_format); 57 | } 58 | else 59 | { 60 | if(cloud_type == "xyz") 61 | { 62 | auto mesh = mesh_sampler.create_clouds(N, out, ".qc", binary_format); 63 | if(!convex.empty()) mesh_sampler.create_convexes(mesh, convex); 64 | } 65 | else if(cloud_type == "xyz_rgb") 66 | { 67 | auto mesh = mesh_sampler.create_clouds(N, out, ".qc", binary_format); 68 | if(!convex.empty()) mesh_sampler.create_convexes(mesh, convex); 69 | } 70 | else if(cloud_type == "xyz_normal") 71 | { 72 | auto mesh = mesh_sampler.create_clouds(N, out, ".qc", binary_format); 73 | if(!convex.empty()) mesh_sampler.create_convexes(mesh, convex); 74 | } 75 | else if(cloud_type == "xyz_rgb_normal") 76 | { 77 | auto mesh = mesh_sampler.create_clouds(N, out, ".qc", binary_format); 78 | if(!convex.empty()) mesh_sampler.create_convexes(mesh, convex); 79 | } 80 | } 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -2 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: false 6 | AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlinesLeft: true 8 | AlignOperands: true 9 | AlignTrailingComments: false 10 | AllowAllParametersOfDeclarationOnNextLine: false 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: Empty 14 | AllowShortIfStatementsOnASingleLine: true 15 | AllowShortLoopsOnASingleLine: true 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: false 19 | AlwaysBreakTemplateDeclarations: true 20 | BinPackArguments: true 21 | BinPackParameters: false 22 | BreakBeforeBinaryOperators: NonAssignment 23 | BreakBeforeBraces: Allman 24 | BreakBeforeInheritanceComma: false 25 | BreakBeforeTernaryOperators: true 26 | BreakConstructorInitializersBeforeComma: false 27 | BreakConstructorInitializers: BeforeColon 28 | BreakAfterJavaFieldAnnotations: false 29 | BreakStringLiterals: true 30 | ColumnLimit: 120 31 | CommentPragmas: '^ IWYU pragma:' 32 | CompactNamespaces: false 33 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 34 | ConstructorInitializerIndentWidth: 0 35 | ContinuationIndentWidth: 4 36 | Cpp11BracedListStyle: true 37 | DerivePointerAlignment: false 38 | DisableFormat: false 39 | ExperimentalAutoDetectBinPacking: false 40 | FixNamespaceComments: true 41 | ForEachMacros: 42 | - foreach 43 | - Q_FOREACH 44 | - BOOST_FOREACH 45 | IncludeBlocks: Regroup 46 | IncludeCategories: 47 | - Regex: '^( 4 | #include 5 | #include 6 | #include 7 | 8 | class aiScene; 9 | 10 | namespace fs = std::filesystem; 11 | 12 | namespace mesh_sampling 13 | { 14 | struct ASSIMPScene; 15 | 16 | using CloudT = std::vector; 17 | 18 | const auto supported_cloud_type = std::vector{"xyz", "xyz_rgb", "xyz_normal", "xyz_rgb_normal"}; 19 | const auto supported_extensions = std::vector{".ply", ".qc", ".stl", ".obj", ".dae"}; 20 | 21 | class MeshSampling 22 | { 23 | public: 24 | MeshSampling() = default; 25 | MeshSampling(const fs::path & in_path, float scale = 1); 26 | ~MeshSampling() = default; 27 | 28 | /** 29 | * @brief Load mesh from full path or directory 30 | * 31 | * @param in_path path or directory 32 | * @param scale scaling factor (default: 1) 33 | */ 34 | void load(const fs::path & in_path, float scale = 1); 35 | 36 | /** 37 | * @brief Create a cloud 38 | * 39 | * @tparam PointT 40 | * @param N number of sampling points (default: 2000) 41 | * @return pcl::PointCloud 42 | */ 43 | CloudT cloud(const unsigned N = 2000); 44 | 45 | /** 46 | * @brief Create and save a pointcloud 47 | * 48 | * @tparam PointT 49 | * @param scene ASSIMPScene 50 | * @param N number sampling points 51 | * @param out_path if empty or directory, file is not saved (default: {}) 52 | * @param binary_mode is binary (default: false) 53 | * @return pcl::PointCloud 54 | */ 55 | CloudT create_cloud(const aiScene * scene, unsigned N, const fs::path & out_path = {}, bool binary_mode = false); 56 | 57 | /** 58 | * @brief Create and save pointclouds, to be used when importing meshes from a directory. If out_path is empty clouds 59 | * are not saved 60 | * 61 | * @tparam PointT 62 | * @param N number of sampling points 63 | * @param out_path directory to save the clouds (default: {}) 64 | * @param extension saving extension (default: ".qc") 65 | * @param binary_mode is binary mnode (default: false) 66 | */ 67 | std::map create_clouds(unsigned N, 68 | const fs::path & out_path = {}, 69 | const std::string & extension = ".qc", 70 | bool binary_mode = false); 71 | 72 | /** 73 | * @brief Create a convex object using qhull library and save it to a file 74 | * 75 | * @tparam PointT 76 | * @param cloud PCL point cloud 77 | * @param out_path output directory 78 | */ 79 | std::string create_convex(const CloudT & cloud, const fs::path & out_path = {}); 80 | 81 | std::map create_convexes(const std::map & clouds, 82 | const fs::path & out_path = {}, 83 | bool stop_on_fail = true); 84 | 85 | /** 86 | * @brief Export all meshes to a file in a format support by ASSIMP 87 | * 88 | * @param out_path Export path 89 | * @param binary is binary (defaukt:false) 90 | */ 91 | void convertTo(const fs::path & out_path, bool binary = false); 92 | 93 | /** 94 | * @brief Check supported type 95 | * 96 | * @param type type to check 97 | * @param supported list of supported types (default : "xyz", "xyz_rgb", "xyz_normal", "xyz_rgb_normal") 98 | * @return bool 99 | */ 100 | bool check_supported(const std::string & type, const std::vector & supported = supported_cloud_type); 101 | 102 | /** Get mesh at index 103 | * 104 | * \param path mesh path 105 | * @return ASSIMPScene& 106 | */ 107 | ASSIMPScene * mesh(const std::string & path); 108 | 109 | private: 110 | class Impl; 111 | std::map> meshes_; 112 | }; 113 | 114 | }; // namespace mesh_sampling 115 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mesh_sampling 2 | 3 | [![License](https://img.shields.io/badge/License-BSD%202--Clause-green.svg)](https://opensource.org/licenses/BSD-2-Clause) 4 | [![CI of mesh_sampling](https://github.com/jrl-umi3218/mesh_sampling/workflows/CI%20of%20mesh_sampling/badge.svg)](https://github.com/jrl-umi3218/mesh_sampling/actions?query=workflow%3A%22CI+of+mesh_sampling%22) 5 | [![Package mesh_sampling](https://github.com/jrl-umi3218/mesh_sampling/workflows/Package%20mesh_sampling/badge.svg)](https://github.com/jrl-umi3218/mesh_sampling/actions?query=workflow%3A%22Package%20mesh_sampling%22) 6 | 7 | C++ Implementation of pointcloud generation from mesh sampling methods. 8 | 9 | ![Sampling example](https://raw.githubusercontent.com/jrl-umi3218/mesh_sampling/master/sample/sampling_example.png) 10 | 11 | So far, the following samplers have been implemented: 12 | 13 | - Weighted random sampling: generates a given number of points uniformely distributed according to triangle areas. 14 | See [this blog post](https://medium.com/@daviddelaiglesiacastro/3f-point-cloud-generation-from-3f-triangular-mesh-bbb602ecf238) for details on the method. 15 | 16 | It is provided as-is, and could probably be optimized should the need arise. Feel free to submit merge requests. 17 | 18 | ## Installation 19 | 20 | ### From Ubuntu packages (20.04, 22.04, 24.04) 21 | 22 | ```sh 23 | # Setup the mirror 24 | curl -1sLf 'https://dl.cloudsmith.io/public/mc-rtc/stable/setup.deb.sh' | sudo -E bash 25 | # Install packages 26 | sudo apt install libmesh-sampling-dev 27 | ``` 28 | 29 | ### From source 30 | 31 | Requirements: 32 | - cmake >3.11 33 | - Eigen3 34 | - libqhull-dev 35 | 36 | If you do not already have a recent cmake installation (>3.11), you will need to install it. On Ubuntu bionic, this can be done by adding the official [Kitware PPA](https://apt.kitware.com/), and updating cmake 37 | 38 | For bionic: 39 | 40 | ```sh 41 | wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | sudo apt-key add - 42 | sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main' 43 | sudo apt-get update 44 | ``` 45 | 46 | For xenial: 47 | 48 | ```sh 49 | wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | sudo apt-key add - 50 | sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ xenial-rc main' 51 | sudo apt-get update 52 | ``` 53 | 54 | Then install cmake 55 | ```sh 56 | sudo apt install cmake 57 | ``` 58 | 59 | 60 | You can now build and install this package 61 | 62 | ``` 63 | git clone --recursive https://github.com/jrl-umi3218/mesh_sampling.git 64 | cd mesh_sampling 65 | mkdir build && cd build 66 | cmake .. 67 | make 68 | sudo make install 69 | ``` 70 | 71 | ## Usage 72 | 73 | ### Command-line tool 74 | 75 | A simple binary executable `mesh_sampling` is provided. It'll convert any model supported by ASSIMP into its corresponding pointcloud with a given number of points. The command line is of the general form: 76 | 77 | * From file to file 78 | 79 | ``` 80 | mesh_sampling /path/to/model. --out /path/to/cloud/cloud. --type xyz_rgb_normal --samples 10000 --binary 81 | ``` 82 | 83 | * From folder to folder 84 | 85 | Here the default cloud format is `.qc`. For all files in `/path/to/models` a file `/path/to/cloud/filename.qc` will be generated. 86 | 87 | ``` 88 | mesh_sampling /path/to/models --out /path/to/cloud --type xyz_rgb_normal --samples 10000 --binary 89 | ``` 90 | 91 | Where: 92 | - `supported_mesh_format` is one of the mesh format supported by `ASSIMP` (commonly DAE, STL, OBJ) 93 | - `supported_cloud_format` is a PCL formal (`pcd` or `ply`), or qhull's format (`qc`) 94 | 95 | See `mesh_sampling --help` for more options. 96 | 97 | Example: 98 | 99 | ```bash 100 | mesh_sampling --in /path/to/model.dae --out /tmp/cloud.pcd --type xyz_rgb_normal --samples 10000 --binary 101 | pcl_viewer /tmp/cloud.pcd -normals_scale 5 -normals 1 102 | ``` 103 | 104 | ### Generating convex files using qhull 105 | 106 | To generate convex files, you need to add `--convex` option to the command line. The convex file will be generated in the specified folder as `filename-ch.txt`. 107 | 108 | 109 | * From file 110 | ```bash 111 | mesh_sampling --in /path/to/model. --out /tmp/test.qc --convex /path --type xyz --samples 10000 112 | ``` 113 | 114 | * From folder (check all files with respect to the supported extensions ".ply", ".pcd", ".qc", ".stl") 115 | ```bash 116 | mesh_sampling --in /path/to/models --out /tmp --convex /tmp --type xyz --samples 10000 117 | ``` 118 | 119 | > Cloud saving is optional, you can remove `--out` option to avoid it. 120 | -------------------------------------------------------------------------------- /src/mesh_sampling.cpp: -------------------------------------------------------------------------------- 1 | #include "mesh_sampling/mesh_sampling.h" 2 | 3 | // Deactivate clang to ensure include order required by libqhullcpp 4 | // clang-format off 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | //clang-format on 18 | 19 | using namespace orgQhull; 20 | 21 | namespace mesh_sampling 22 | { 23 | 24 | MeshSampling::MeshSampling(const fs::path & in_path, float scale) 25 | { 26 | load(in_path, scale); 27 | } 28 | 29 | std::string MeshSampling::create_convex(const CloudT & cloud, const fs::path & out_path) 30 | { 31 | if(cloud.empty()) 32 | { 33 | throw std::invalid_argument("create_convex: input cloud is empty."); 34 | } 35 | 36 | char * buffer = nullptr; 37 | size_t size = 0; 38 | FILE * out_stream = open_memstream(&buffer, &size); 39 | 40 | if(out_stream == nullptr) 41 | { 42 | throw std::runtime_error("create_convex: failed to open memory stream."); 43 | } 44 | 45 | // Create Qhull object 46 | Qhull qhull; 47 | 48 | std::ofstream ofs; 49 | if(!out_path.empty()){ 50 | ofs.open(out_path.c_str()); 51 | 52 | if(!ofs.is_open()) 53 | { 54 | throw std::invalid_argument("create_convex: could not open file :" + out_path.string()); 55 | } 56 | } 57 | 58 | qhull.qh()->fout = out_stream; 59 | 60 | // Convert PCL cloud to a flat array for Qhull input 61 | std::vector qhull_input; 62 | qhull_input.reserve(cloud.size() * 3); 63 | for(const auto & pt : cloud) 64 | { 65 | qhull_input.push_back(pt.x()); 66 | qhull_input.push_back(pt.y()); 67 | qhull_input.push_back(pt.z()); 68 | } 69 | 70 | try 71 | { 72 | qhull.runQhull("pcl_input", 3, cloud.size(), qhull_input.data(), "Qt"); // 3D, triangulate option 73 | qhull.outputQhull("o f"); 74 | } 75 | catch(const std::exception & e) 76 | { 77 | fclose(out_stream); 78 | free(buffer); 79 | throw std::runtime_error(std::string("Qhull run failed: ") + e.what()); 80 | } 81 | 82 | fclose(out_stream); 83 | std::string output(buffer, size); 84 | free(buffer); 85 | 86 | if (!out_path.empty()) { 87 | ofs << output; 88 | std::cout << "Convex file saved to " << out_path << std::endl; 89 | } 90 | 91 | return output; 92 | } 93 | 94 | std::map MeshSampling::create_convexes(const std::map & clouds, 95 | const fs::path & out_path, 96 | bool stop_on_fail) 97 | { 98 | if(!out_path.empty() && !fs::is_directory(out_path)) 99 | { 100 | throw std::invalid_argument("create_convexes: out_path has to be a directory {" + out_path.string() + "}"); 101 | } 102 | 103 | std::map output; 104 | 105 | for(const auto & cloud : clouds) 106 | { 107 | try 108 | { 109 | if(out_path.empty()) 110 | output[cloud.first] = create_convex(cloud.second, {}); 111 | else 112 | output[cloud.first] = create_convex(cloud.second, out_path / (fs::path(cloud.first).filename().stem().string() + "-ch.txt")); 113 | } 114 | catch(const std::exception & e) 115 | { 116 | std::cerr << e.what() << std::endl; 117 | 118 | if(stop_on_fail) return {}; 119 | } 120 | } 121 | 122 | return output; 123 | } 124 | 125 | std::map MeshSampling::create_clouds(unsigned N, 126 | const fs::path & out_path, 127 | const std::string & extension, 128 | bool binary_mode) 129 | { 130 | std::map clouds; 131 | if(!out_path.empty()) 132 | { 133 | if(!fs::is_directory(out_path) && meshes_.size() > 1) 134 | { 135 | throw std::runtime_error("[Error] create_clouds, out_path is not a directory"); 136 | } 137 | 138 | if(extension.empty()) 139 | { 140 | throw std::runtime_error("[Error] create_clouds, extension is not set"); 141 | } 142 | 143 | for(const auto & mesh : meshes_) 144 | { 145 | auto path = fs::is_directory(out_path) ? out_path / (fs::path(mesh.first).filename().stem().string() + extension) 146 | : out_path; 147 | auto cloud = create_cloud(mesh.second->scene(), N, path, binary_mode); 148 | clouds.insert({mesh.first, cloud}); 149 | } 150 | } 151 | else 152 | { 153 | for(const auto & mesh : meshes_) 154 | { 155 | auto cloud = create_cloud(mesh.second->scene(), N, {}, binary_mode); 156 | clouds.insert({mesh.first, cloud}); 157 | } 158 | } 159 | 160 | return clouds; 161 | } 162 | 163 | CloudT MeshSampling::cloud(const unsigned N) 164 | { 165 | WeightedRandomSampling sampler(meshes_.begin()->second->scene()); 166 | auto cloud = sampler.weighted_random_sampling(N); 167 | return *cloud; 168 | } 169 | 170 | CloudT MeshSampling::create_cloud(const aiScene * scene, 171 | const unsigned N, 172 | const fs::path & out_path, 173 | bool binary_mode) 174 | { 175 | WeightedRandomSampling sampler(scene); 176 | auto cloud = sampler.weighted_random_sampling(N); 177 | 178 | if(!out_path.empty() && !fs::is_directory(out_path)) 179 | { 180 | auto extension = out_path.extension().string(); 181 | bool success = false; 182 | 183 | if(!success) 184 | { 185 | std::cerr << "Saving to " << out_path << " failed." << std::endl; 186 | return {}; 187 | } 188 | else 189 | { 190 | std::cout << "Saving cloud to " << out_path << " success" << std::endl; 191 | } 192 | } 193 | 194 | return *cloud; 195 | } 196 | 197 | void MeshSampling::load(const fs::path & in_path, float scale) 198 | { 199 | try 200 | { 201 | if(fs::is_directory(in_path)) 202 | { 203 | for(const auto & dir_entry : std::filesystem::directory_iterator{in_path}){ 204 | if(check_supported(dir_entry.path().extension(), supported_extensions)){ 205 | meshes_.insert( 206 | std::make_pair(dir_entry.path(), std::make_shared(dir_entry.path(), scale))); 207 | } 208 | } 209 | } 210 | else 211 | { 212 | meshes_.insert(std::make_pair(in_path, std::make_shared(in_path, scale))); 213 | } 214 | } 215 | catch(std::runtime_error & e) 216 | { 217 | std::cerr << e.what() << std::endl; 218 | return; 219 | } 220 | } 221 | 222 | void MeshSampling::convertTo(const fs::path & out_path, bool binary) 223 | { 224 | for(auto & mesh : meshes_) mesh.second->exportScene(out_path, binary); 225 | } 226 | 227 | bool MeshSampling::check_supported(const std::string & type, const std::vector & supported) 228 | { 229 | return std::any_of(supported.begin(), supported.end(), 230 | [&](const std::string & ext) { return std::equal(ext.begin(), ext.end(), type.begin(), type.end(), 231 | [](char a, char b) { return std::tolower(a) == std::tolower(b); }); }); 232 | } 233 | 234 | ASSIMPScene * MeshSampling::mesh(const std::string & path) 235 | { 236 | if(meshes_.count(path) > 0) return meshes_.begin()->second.get(); 237 | 238 | return nullptr; 239 | } 240 | 241 | } // namespace mesh_sampling 242 | -------------------------------------------------------------------------------- /include/mesh_sampling/weighted_random_sampling.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2020 CNRS-UM LIRMM, CNRS-AIST JRL 3 | */ 4 | 5 | #pragma once 6 | #include 7 | #include 8 | #include // Output data structure 9 | #include 10 | #include 11 | #include 12 | 13 | namespace mesh_sampling 14 | { 15 | 16 | double triangle_area(const Eigen::Vector3f & v1, const Eigen::Vector3f & v2, const Eigen::Vector3f & v3); 17 | Eigen::Vector3f random_point_in_triangle(const Eigen::Vector3f & v1, 18 | const Eigen::Vector3f & v2, 19 | const Eigen::Vector3f & v3); 20 | 21 | /** 22 | * @brief Generates indices based on probabilities 23 | * 24 | * @param probabilities The probabilities associated with each entry in samples. 25 | * @param n number of generated samples 26 | * 27 | * @return return n sampled indices from the probability distribution. Each 28 | * index will be between [0, probabilities.size()[ 29 | */ 30 | template 31 | std::vector weighted_random_choice_indices(const std::vector & probabilities, const size_t n) 32 | { 33 | std::default_random_engine generator; 34 | std::discrete_distribution distribution(probabilities.begin(), probabilities.end()); 35 | 36 | std::vector indices(n); 37 | std::generate(indices.begin(), indices.end(), [&generator, &distribution]() { return distribution(generator); }); 38 | return indices; 39 | } 40 | 41 | /** 42 | * @brief Implementation of python's numpy.random.choice 43 | * Generates a random sample from a given 1-D array 44 | * Since range lazy iterators are not available in C++11, using this function 45 | * might necessitate generating a large samples array, whereby python would be 46 | * able to take advantage of lazy generation. 47 | * 48 | * @param samples a random sample is generated from its elements 49 | * @param probabilities The probabilities associated with each entry in samples. 50 | * @param n number of samples 51 | * 52 | * @return The generated random samples 53 | */ 54 | template 55 | std::vector weighted_random_choice(const std::vector & samples, 56 | const std::vector & probabilities, 57 | const size_t n) 58 | { 59 | const auto & indices = weighted_random_choice_indices(probabilities, n); 60 | std::vector vec(n); 61 | std::transform(indices.begin(), indices.end(), vec.begin(), [&samples](int index) { return samples[index]; }); 62 | return vec; 63 | } 64 | 65 | /** 66 | * @brief Create pointcloud from MESH using weighted random sampling 67 | * 68 | * See the pyntcloud python library for more details: 69 | * https://github.com/daavoo/pyntcloud 70 | * https://github.com/daavoo/pyntcloud/blob/master/pyntcloud/samplers/s_mesh.py 71 | * https://medium.com/@daviddelaiglesiacastro/3f-point-cloud-generation-from-3f-triangular-mesh-bbb602ecf238 72 | */ 73 | 74 | class WeightedRandomSampling 75 | { 76 | using CloudT = std::vector; 77 | 78 | private: 79 | const aiScene * scene; 80 | 81 | std::vector> v1_xyz, v2_xyz, v3_xyz; 82 | std::vector> v1_rgb, v2_rgb, v3_rgb; 83 | std::vector> v1_normal, v2_normal, v3_normal; 84 | std::vector areas; 85 | double total_area = 0; 86 | 87 | void process_triangle(const aiMesh * mesh, const aiFace & face) 88 | { 89 | // If face is a triangle 90 | if(face.mNumIndices == 3) 91 | { 92 | auto vertices = mesh->mVertices; 93 | const auto & fv1 = vertices[face.mIndices[0]]; 94 | const auto & fv2 = vertices[face.mIndices[1]]; 95 | const auto & fv3 = vertices[face.mIndices[2]]; 96 | 97 | Eigen::Vector3f v1, v2, v3; 98 | v1 << fv1.x, fv1.y, fv1.z; 99 | v2 << fv2.x, fv2.y, fv2.z; 100 | v3 << fv3.x, fv3.y, fv3.z; 101 | v1_xyz.push_back(v1); 102 | v2_xyz.push_back(v2); 103 | v3_xyz.push_back(v3); 104 | 105 | if(mesh->HasVertexColors(0)) 106 | { 107 | auto color = mesh->mColors[0]; 108 | const auto & cv1 = color[face.mIndices[0]]; 109 | const auto & cv2 = color[face.mIndices[1]]; 110 | const auto & cv3 = color[face.mIndices[2]]; 111 | v1_rgb.emplace_back(Eigen::Vector3f{cv1.r, cv1.g, cv1.b}); 112 | v2_rgb.emplace_back(Eigen::Vector3f{cv2.r, cv2.g, cv2.b}); 113 | v3_rgb.emplace_back(Eigen::Vector3f{cv3.r, cv3.g, cv3.b}); 114 | } 115 | else 116 | { 117 | v1_rgb.emplace_back(255 * Eigen::Vector3f::Ones()); 118 | v2_rgb.emplace_back(255 * Eigen::Vector3f::Ones()); 119 | v3_rgb.emplace_back(255 * Eigen::Vector3f::Ones()); 120 | } 121 | 122 | if(mesh->HasNormals()) 123 | { 124 | auto normals = mesh->mNormals; 125 | const auto & n1 = normals[face.mIndices[0]]; 126 | const auto & n2 = normals[face.mIndices[1]]; 127 | const auto & n3 = normals[face.mIndices[2]]; 128 | v1_normal.emplace_back(Eigen::Vector3f{n1.x, n1.y, n1.z}); 129 | v2_normal.emplace_back(Eigen::Vector3f{n2.x, n2.y, n2.z}); 130 | v3_normal.emplace_back(Eigen::Vector3f{n3.x, n3.y, n3.z}); 131 | } 132 | else 133 | { 134 | // Should always have normals if loaded with ASSIMPScene loader 135 | v1_normal.emplace_back(Eigen::Vector3f{0., 0., 0.}); 136 | v2_normal.emplace_back(Eigen::Vector3f{0., 0., 0.}); 137 | v3_normal.emplace_back(Eigen::Vector3f{0., 0., 0.}); 138 | } 139 | 140 | const auto area = triangle_area(v1, v2, v3); 141 | areas.push_back(area); 142 | total_area += area; 143 | } 144 | } 145 | 146 | void process_mesh(const aiMesh * mesh) 147 | { 148 | if(!mesh->HasFaces()) return; 149 | 150 | // Count total number of triangles 151 | size_t total_triangles = 0; 152 | for(unsigned int i = 0; i < mesh->mNumFaces; ++i) 153 | { 154 | const auto face = mesh->mFaces[i]; 155 | // If face is a triangle 156 | if(face.mNumIndices == 3) 157 | { 158 | ++total_triangles; 159 | } 160 | } 161 | 162 | // Reserve memory for all triangles 163 | v1_xyz.reserve(total_triangles); 164 | v2_xyz.reserve(total_triangles); 165 | v3_xyz.reserve(total_triangles); 166 | areas.reserve(total_triangles); 167 | 168 | // Process each triangle 169 | for(unsigned int i = 0; i < mesh->mNumFaces; ++i) 170 | { 171 | const auto face = mesh->mFaces[i]; 172 | process_triangle(mesh, face); 173 | } 174 | } 175 | 176 | void process_scene(const aiScene * scene) 177 | { 178 | if(!scene->HasMeshes()) return; 179 | 180 | for(unsigned int i = 0; i < scene->mNumMeshes; ++i) 181 | { 182 | process_mesh(scene->mMeshes[i]); 183 | } 184 | } 185 | 186 | public: 187 | WeightedRandomSampling(const aiScene * scene) : scene(scene) 188 | { 189 | process_scene(scene); 190 | } 191 | 192 | /** 193 | * @brief Compute a weighted random sampling of the scene. 194 | * 195 | * @param N Number of samples 196 | * If N = 0, then the the number of triangles in the scene will be used to 197 | * generate samples. 198 | * 199 | * @return Sampled pointcloud from scene 200 | */ 201 | std::unique_ptr weighted_random_sampling(const size_t N_samples = 0) 202 | { 203 | std::vector probabilities; 204 | probabilities.reserve(areas.size()); 205 | double total = 0; 206 | for(const auto & area : areas) 207 | { 208 | total += area / total_area; 209 | probabilities.push_back(area / total_area); 210 | } 211 | 212 | size_t N = N_samples; 213 | if(N_samples == 0) N = areas.size(); 214 | 215 | const auto random_idx = weighted_random_choice_indices(probabilities, N); 216 | std::unique_ptr cloud(new CloudT()); 217 | cloud->reserve(static_cast(N)); 218 | for(const auto idx : random_idx) 219 | { 220 | cloud->push_back(random_point_in_triangle(v1_xyz[idx], v2_xyz[idx], v3_xyz[idx])); 221 | } 222 | 223 | return cloud; 224 | } 225 | }; 226 | } // namespace mesh_sampling 227 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /ext/effolkronium/random.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ______ ___ _ _______ ________ __ 3 | | ___ \/ _ \ | \ | | _ \ _ | \/ | Random for modern C++ 4 | | |_/ / /_\ \| \| | | | | | | | . . | 5 | | /| _ || . ` | | | | | | | |\/| | version 1.4.0 6 | | |\ \| | | || |\ | |/ /\ \_/ / | | | 7 | \_| \_\_| |_/\_| \_/___/ \___/\_| |_/ https://github.com/effolkronium/random 8 | 9 | Licensed under the MIT License . 10 | Copyright (c) 2017-2021 effolkronium 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy 13 | of this software and associated documentation files( the "Software" ), to deal 14 | in the Software without restriction, including without limitation the rights 15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | copies of the Software, and to permit persons to whom the Software is 17 | furnished to do so, subject to the following conditions : 18 | 19 | The above copyright notice and this permission notice shall be included in all 20 | copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | SOFTWARE. 29 | */ 30 | 31 | #ifndef EFFOLKRONIUM_RANDOM_HPP 32 | #define EFFOLKRONIUM_RANDOM_HPP 33 | 34 | #include 35 | #include // timed seed 36 | #include 37 | #include 38 | #include 39 | #include // std::forward, std::declval 40 | #include // std::shuffle, std::next, std::distance 41 | #include // std::begin, std::end, std::iterator_traits 42 | #include // std::numeric_limits 43 | #include 44 | #include 45 | 46 | namespace effolkronium { 47 | 48 | namespace details { 49 | /// Key type for getting common type numbers or objects 50 | struct common{ }; 51 | 52 | /// True if type T is applicable by a std::uniform_int_distribution 53 | template 54 | struct is_uniform_int { 55 | static constexpr bool value = 56 | std::is_same::value 57 | || std::is_same::value 58 | || std::is_same::value 59 | || std::is_same::value 60 | || std::is_same::value 61 | || std::is_same::value 62 | || std::is_same::value 63 | || std::is_same::value; 64 | }; 65 | 66 | /// True if type T is applicable by a std::uniform_real_distribution 67 | template 68 | struct is_uniform_real { 69 | static constexpr bool value = 70 | std::is_same::value 71 | || std::is_same::value 72 | || std::is_same::value; 73 | }; 74 | 75 | /// True if type T is plain byte 76 | template 77 | struct is_byte { 78 | static constexpr bool value = 79 | std::is_same::value 80 | || std::is_same::value; 81 | }; 82 | 83 | /// True if type T is plain number type 84 | template 85 | struct is_supported_number { 86 | static constexpr bool value = 87 | is_byte ::value 88 | || is_uniform_real::value 89 | || is_uniform_int ::value; 90 | }; 91 | 92 | /// True if type T is character type 93 | template 94 | struct is_supported_character { 95 | static constexpr bool value = 96 | std::is_same::value 97 | || std::is_same::value 98 | || std::is_same::value 99 | || std::is_same::value; 100 | }; 101 | 102 | /// True if type T is iterator 103 | template 104 | struct is_iterator { 105 | private: 106 | static char test( ... ); 107 | 108 | template ::difference_type, 110 | typename = typename std::iterator_traits::pointer, 111 | typename = typename std::iterator_traits::reference, 112 | typename = typename std::iterator_traits::value_type, 113 | typename = typename std::iterator_traits::iterator_category 114 | > static long test( U&& ); 115 | public: 116 | static constexpr bool value = std::is_same< 117 | decltype( test( std::declval( ) ) ), long>::value; 118 | }; 119 | 120 | template 121 | class has_reserve 122 | { 123 | private: 124 | template 125 | static char test(...); 126 | 127 | template 128 | static long test(decltype(&C::reserve)); 129 | public: 130 | static constexpr bool value = std::is_same< 131 | decltype(test(0)), long>::value; 132 | }; 133 | 134 | template 135 | class has_insert 136 | { 137 | private: 138 | template 139 | static char test(...); 140 | 141 | template 142 | static long test(decltype(&C::insert)); 143 | public: 144 | static constexpr bool value = std::is_same< 145 | decltype(test(0)), long>::value; 146 | }; 147 | 148 | } // namespace details 149 | 150 | /// Default seeder for 'random' classes 151 | struct seeder_default { 152 | /// return seed sequence 153 | std::seed_seq& operator() ( ) { 154 | // MinGW issue, std::random_device returns constant value 155 | // Use std::seed_seq with additional seed from C++ chrono 156 | return seed_seq; 157 | } 158 | private: 159 | std::seed_seq seed_seq{ { 160 | static_cast( std::random_device{ }( ) ), 161 | static_cast( std::chrono::steady_clock::now( ) 162 | .time_since_epoch( ).count( ) ), 163 | } }; 164 | }; 165 | 166 | /** 167 | * \brief Base template class for random 168 | * with static API and static internal member storage 169 | * \note it is NOT thread safe but more efficient then 170 | * basic_random_thread_local 171 | * \param Engine A random engine with interface like in the std::mt19937 172 | * \param Seeder A seeder type which return seed for internal engine 173 | * through operator() 174 | */ 175 | template< 176 | typename Engine, 177 | typename Seeder = seeder_default, 178 | template class IntegerDist = std::uniform_int_distribution, 179 | template class RealDist = std::uniform_real_distribution, 180 | typename BoolDist = std::bernoulli_distribution 181 | > 182 | class basic_random_static { 183 | public: 184 | basic_random_static( ) = delete; 185 | 186 | /// Type of used random number engine 187 | using engine_type = Engine; 188 | 189 | /// Type of used random number seeder 190 | using seeder_type = Seeder; 191 | 192 | /// Type of used integer distribution 193 | template 194 | using integer_dist_t = IntegerDist; 195 | 196 | /// Type of used real distribution 197 | template 198 | using real_dist_t = RealDist; 199 | 200 | /// Type of used bool distribution 201 | using bool_dist_t = BoolDist; 202 | 203 | /// Key type for getting common type numbers or objects 204 | using common = details::common; 205 | 206 | /** 207 | * \return The minimum value 208 | * potentially generated by the random-number engine 209 | */ 210 | static constexpr typename Engine::result_type min( ) { 211 | return Engine::min( ); 212 | } 213 | 214 | /** 215 | * \return The maximum value 216 | * potentially generated by the random-number engine 217 | */ 218 | static constexpr typename Engine::result_type max( ) { 219 | return Engine::max( ); 220 | } 221 | 222 | /// Advances the internal state by z times 223 | static void discard( const unsigned long long z ) { 224 | engine_instance( ).discard( z ); 225 | } 226 | 227 | /// Reseed by Seeder 228 | static void reseed( ) { 229 | Seeder seeder; 230 | seed( seeder( ) ); 231 | } 232 | 233 | /** 234 | * \brief Reinitializes the internal state 235 | * of the random-number engine using new seed value 236 | * \param value The seed value to use 237 | * in the initialization of the internal state 238 | */ 239 | static void seed( const typename Engine::result_type value = 240 | Engine::default_seed ) { 241 | engine_instance( ).seed( value ); 242 | } 243 | 244 | /** 245 | * \brief Reinitializes the internal state 246 | * of the random-number engine using new seed value 247 | * \param seq The seed sequence 248 | * to use in the initialization of the internal state 249 | */ 250 | template 251 | static void seed( Sseq& seq ) { 252 | engine_instance( ).seed( seq ); 253 | } 254 | 255 | /// return random number from engine in [min(), max()] range 256 | static typename Engine::result_type get( ) { 257 | return engine_instance( )( ); 258 | } 259 | 260 | /** 261 | * \brief Compares internal pseudo-random number engine 262 | * with 'other' pseudo-random number engine. 263 | * Two engines are equal, if their internal states 264 | * are equivalent, that is, if they would generate 265 | * equivalent values for any number of calls of operator() 266 | * \param other The engine, with which the internal engine will be compared 267 | * \return true, if other and internal engine are equal 268 | */ 269 | static bool is_equal( const Engine& other ) { 270 | return engine_instance( ) == other; 271 | } 272 | 273 | /** 274 | * \brief Serializes the internal state of the 275 | * internal pseudo-random number engine as a sequence 276 | * of decimal numbers separated by one or more spaces, 277 | * and inserts it to the stream ost. The fill character 278 | * and the formatting flags of the stream are 279 | * ignored and unaffected. 280 | * \param ost The output stream to insert the data to 281 | */ 282 | template 283 | static void serialize( std::basic_ostream& ost ) { 284 | ost << engine_instance( ); 285 | } 286 | 287 | /** 288 | * \brief Restores the internal state of the 289 | * internal pseudo-random number engine from 290 | * the serialized representation, which 291 | * was created by an earlier call to 'serialize' 292 | * using a stream with the same imbued locale and 293 | * the same CharT and Traits. 294 | * If the input cannot be deserialized, 295 | * internal engine is left unchanged and failbit is raised on ist 296 | * \param ost The input stream to extract the data from 297 | */ 298 | template 299 | static void deserialize( std::basic_istream& ist ) { 300 | ist >> engine_instance( ); 301 | } 302 | 303 | /** 304 | * \brief Generate a random integer number in a [from; to] range 305 | * by std::uniform_int_distribution 306 | * \param from The first limit number of a random range 307 | * \param to The second limit number of a random range 308 | * \return A random integer number in a [from; to] range 309 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 310 | * \note Prevent implicit type conversion 311 | */ 312 | template 313 | static typename std::enable_if::value 314 | , T>::type get( T from = std::numeric_limits::min( ), 315 | T to = std::numeric_limits::max( ) ) { 316 | if( from < to ) // Allow range from higher to lower 317 | return IntegerDist{ from, to }( engine_instance( ) ); 318 | return IntegerDist{ to, from }( engine_instance( ) ); 319 | } 320 | 321 | /** 322 | * \brief Generate a random real number in a [from; to] range 323 | * by std::uniform_real_distribution 324 | * \param from The first limit number of a random range 325 | * \param to The second limit number of a random range 326 | * \return A random real number in a [from; to] range 327 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 328 | * \note Prevent implicit type conversion 329 | */ 330 | template 331 | static typename std::enable_if::value 332 | , T>::type get( T from = std::numeric_limits::min( ), 333 | T to = std::numeric_limits::max( ) ) { 334 | if( from < to ) // Allow range from higher to lower 335 | return RealDist{ from, to }( engine_instance( ) ); 336 | return RealDist{ to, from }( engine_instance( ) ); 337 | } 338 | 339 | /** 340 | * \brief Generate a random byte number in a [from; to] range 341 | * \param from The first limit number of a random range 342 | * \param to The second limit number of a random range 343 | * \return A random byte number in a [from; to] range 344 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 345 | * \note Prevent implicit type conversion 346 | */ 347 | template 348 | static typename std::enable_if::value 349 | , T>::type get( T from = std::numeric_limits::min( ), 350 | T to = std::numeric_limits::max( ) ) { 351 | // Choose between short and unsigned short for byte conversion 352 | using short_t = typename std::conditional::value, 353 | short, unsigned short>::type; 354 | 355 | return static_cast( get( from, to ) ); 356 | } 357 | 358 | /** 359 | * \brief Generate a random common_type number in a [from; to] range 360 | * \param Key The Key type for this version of 'get' method 361 | * Type should be '(THIS_TYPE)::common' struct 362 | * \param from The first limit number of a random range 363 | * \param to The second limit number of a random range 364 | * \return A random common_type number in a [from; to] range 365 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 366 | * \note Allow implicit type conversion 367 | * \note Prevent implicit type conversion from singed to unsigned types 368 | * Why? std::common_type chooses unsigned value, 369 | * then Signed value will be converted to Unsigned value 370 | * which gives us a wrong range for random values. 371 | * https://stackoverflow.com/a/5416498/5734836 372 | */ 373 | template< 374 | typename Key, 375 | typename A, 376 | typename B, 377 | typename C = typename std::common_type::type 378 | > 379 | static typename std::enable_if< 380 | std::is_same::value 381 | && details::is_supported_number::value 382 | && details::is_supported_number::value 383 | // Prevent implicit type conversion from singed to unsigned types 384 | && std::is_signed::value != std::is_unsigned::value 385 | , C>::type get( A from = std::numeric_limits::min( ), 386 | B to = std::numeric_limits::max( ) ) { 387 | return get( static_cast( from ), static_cast( to ) ); 388 | } 389 | 390 | /** 391 | * \brief Generate a random character in a [from; to] range 392 | * by std::uniform_int_distribution 393 | * \param from The first limit number of a random range 394 | * \param to The second limit number of a random range 395 | * \return A random character in a [from; to] range 396 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 397 | * \note Prevent implicit type conversion 398 | */ 399 | template 400 | static typename std::enable_if::value 401 | , T>::type get(T from = std::numeric_limits::min(), 402 | T to = std::numeric_limits::max()) { 403 | if (from < to) // Allow range from higher to lower 404 | return static_cast(IntegerDist{ static_cast(from), static_cast(to) }(engine_instance())); 405 | return static_cast(IntegerDist{ static_cast(to), static_cast(from) }(engine_instance())); 406 | } 407 | 408 | /** 409 | * \brief Generate a bool value with specific probability 410 | * by std::bernoulli_distribution 411 | * \param probability The probability of generating true in [0; 1] range 412 | * 0 means always false, 1 means always true 413 | * \return 'true' with 'probability' probability ('false' otherwise) 414 | */ 415 | template 416 | static typename std::enable_if::value 417 | , bool>::type get( const double probability = 0.5 ) { 418 | assert( 0 <= probability && 1 >= probability ); // out of [0; 1] range 419 | return BoolDist{ probability }( engine_instance( ) ); 420 | } 421 | 422 | /** 423 | * \brief Return random value from initilizer_list 424 | * \param init_list initilizer_list with values 425 | * \return Random value from initilizer_list 426 | * \note Should be 1 or more elements in initilizer_list 427 | * \note Warning! Elements in initilizer_list can't be moved: 428 | * https://stackoverflow.com/a/8193157/5734836 429 | */ 430 | template 431 | static T get( std::initializer_list init_list ) { 432 | assert( 0u != init_list.size( ) ); 433 | return *get( init_list.begin( ), init_list.end( ) ); 434 | } 435 | 436 | /** 437 | * \brief Return random iterator from iterator range 438 | * \param first, last - the range of elements 439 | * \return Random iterator from [first, last) range 440 | * \note If first == last, return last 441 | */ 442 | template 443 | static typename std::enable_if::value 444 | , InputIt>::type get( InputIt first, InputIt last ) { 445 | const auto size = std::distance( first, last ); 446 | if( 0 == size ) return last; 447 | using diff_t = typename std::iterator_traits::difference_type; 448 | return std::next( first, get( 0, size - 1 ) ); 449 | } 450 | 451 | /** 452 | * \brief Return random iterator from Container 453 | * \param container The container with elements 454 | * \return Random iterator from container 455 | * \note If container is empty return std::end( container ) iterator 456 | */ 457 | template 458 | static auto get( Container& container ) -> 459 | typename std::enable_if::value 461 | , decltype(std::begin(container)) 462 | >::type { 463 | return get( std::begin( container ), std::end( container ) ); 464 | } 465 | 466 | /** 467 | * \brief Return container filled with random values 468 | * \param from The first limit number of a random range 469 | * \param to The second limit number of a random range 470 | * \param size The number of elements in resulting container 471 | * \return Container filled with random values 472 | * \note Container "reserve" method will be called before generation 473 | */ 474 | template class Container, typename A> 475 | static typename std::enable_if< 476 | details::has_reserve>::value 477 | , Container>::type get(A from, A to, std::size_t size) { 478 | Container container; 479 | 480 | container.reserve(size); 481 | for (std::size_t i = 0; i < size; ++i) 482 | container.insert(std::end(container), get(from, to)); 483 | 484 | return container; 485 | } 486 | 487 | /** 488 | * \brief Return container filled with random common_type values 489 | * \param Key The Key type for this version of 'get' method 490 | * Type should be '(THIS_TYPE)::common' struct 491 | * \param from The first limit number of a random range 492 | * \param to The second limit number of a random range 493 | * \param size The number of elements in resulting container 494 | * \return Container filled with random values 495 | * \note Container "reserve" method will be called before generation 496 | */ 497 | template< 498 | template class Container, 499 | typename Key, 500 | typename A, 501 | typename B, 502 | typename C = typename std::common_type::type> 503 | static typename std::enable_if< 504 | std::is_same::value 505 | && details::has_reserve>::value 506 | , Container>::type get(A start, B end, std::size_t size) { 507 | Container container; 508 | 509 | container.reserve(size); 510 | for (std::size_t i = 0; i < size; ++i) 511 | container.insert(std::end(container), get(start, end)); 512 | 513 | return container; 514 | } 515 | 516 | /** 517 | * \brief Return container filled with random values 518 | * \param from The first limit number of a random range 519 | * \param to The second limit number of a random range 520 | * \param size The number of elements in resulting container 521 | * \return Container filled with random values 522 | */ 523 | template class Container, typename A> 524 | static typename std::enable_if< 525 | !details::has_reserve>::value 526 | , Container>::type get(A start, A end, std::size_t size) { 527 | Container container; 528 | 529 | for (std::size_t i = 0; i < size; ++i) 530 | container.insert(std::end(container), get(start, end)); 531 | 532 | return container; 533 | } 534 | 535 | /** 536 | * \brief Return container filled with random common_type values 537 | * \param Key The Key type for this version of 'get' method 538 | * Type should be '(THIS_TYPE)::common' struct 539 | * \param from The first limit number of a random range 540 | * \param to The second limit number of a random range 541 | * \param size The number of elements in resulting container 542 | * \return Container filled with random values 543 | */ 544 | template< 545 | template class Container, 546 | typename Key, 547 | typename A, 548 | typename B, 549 | typename C = typename std::common_type::type> 550 | static typename std::enable_if< 551 | std::is_same::value 552 | && !details::has_reserve>::value 553 | , Container>::type get(A start, B end, std::size_t size) { 554 | Container container; 555 | 556 | for (std::size_t i = 0; i < size; ++i) 557 | container.insert(std::end(container), get(start, end)); 558 | 559 | return container; 560 | } 561 | 562 | /** 563 | * \brief Return array-like container filled with random values 564 | * \param from The first limit number of a random range 565 | * \param to The second limit number of a random range 566 | * \param N The number of elements in resulting container 567 | * \return Container filled with random values 568 | */ 569 | template class Container, std::size_t N, typename A> 570 | static typename std::enable_if< 571 | !details::has_insert>::value 572 | , Container>::type get(A start, A end) { 573 | Container container = {{ 0 }}; 574 | 575 | for (std::size_t i = 0; i < N; ++i) 576 | container[i] = get(start, end); 577 | 578 | return container; 579 | } 580 | 581 | /** 582 | * \brief Return array-like container filled with random common_type values 583 | * \param Key The Key type for this version of 'get' method 584 | * Type should be '(THIS_TYPE)::common' struct 585 | * \param from The first limit number of a random range 586 | * \param to The second limit number of a random range 587 | * \param size The number of elements in resulting container 588 | * \return Container filled with random values 589 | */ 590 | template< 591 | template class Container, 592 | std::size_t N, 593 | typename Key, 594 | typename A, 595 | typename B, 596 | typename C = typename std::common_type::type> 597 | static typename std::enable_if< 598 | std::is_same::value 599 | && !details::has_insert>::value 600 | , Container>::type get(A start, B end) { 601 | Container container = {{ 0 }}; 602 | 603 | for (std::size_t i = 0; i < N; ++i) 604 | container[i] = get(start, end); 605 | 606 | return container; 607 | } 608 | 609 | /** 610 | * \brief Return random pointer from built-in array 611 | * \param array The built-in array with elements 612 | * \return Pointer to random element in array 613 | */ 614 | template 615 | static T* get( T( &array )[ N ] ) { 616 | return std::addressof( array[ get( 0, N - 1 ) ] ); 617 | } 618 | 619 | /** 620 | * \brief Return value from custom Dist distribution 621 | * seeded by internal random engine 622 | * \param Dist The type of custom distribution with next concept: 623 | * http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution 624 | * \param args The arguments which will be forwarded to Dist constructor 625 | * \return Value from custom distribution 626 | */ 627 | template 628 | static typename Dist::result_type get( Args&&... args ) { 629 | return Dist{ std::forward( args )... }( engine_instance( ) ); 630 | } 631 | 632 | /** 633 | * \brief Return value from custom 'dist' distribution 634 | * seeded by internal random engine 635 | * \param dist The custom distribution with next concept: 636 | * http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution 637 | * \param args The arguments which will be forwarded to Dist constructor 638 | * \return Value from custom 'dist' distribution 639 | */ 640 | template 641 | static typename Dist::result_type get( Dist& dist ) { 642 | return dist( engine_instance( ) ); 643 | } 644 | 645 | /** 646 | * \brief Reorders the elements in the given range [first, last) 647 | * such that each possible permutation of those elements 648 | * has equal probability of appearance. 649 | * \param first, last - the range of elements to shuffle randomly 650 | */ 651 | template 652 | static void shuffle( RandomIt first, RandomIt last ) { 653 | std::shuffle( first, last, engine_instance( ) ); 654 | } 655 | 656 | /** 657 | * \brief Reorders the elements in the given container 658 | * such that each possible permutation of those elements 659 | * has equal probability of appearance. 660 | * \param container - the container with elements to shuffle randomly 661 | */ 662 | template 663 | static void shuffle( Container& container ) { 664 | shuffle( std::begin( container ), std::end( container ) ); 665 | } 666 | 667 | /// return internal engine by copy 668 | static Engine get_engine( ) { 669 | return engine_instance( ); 670 | } 671 | 672 | /// return internal engine by ref 673 | static Engine& engine() { 674 | return engine_instance(); 675 | } 676 | protected: 677 | /// get reference to the static engine instance 678 | static Engine& engine_instance( ) { 679 | static Engine engine{ Seeder{ }( ) }; 680 | return engine; 681 | } 682 | }; 683 | 684 | /** 685 | * \brief Base template class for random 686 | * with thread_local API and thread_local internal member storage 687 | * \note it IS thread safe but less efficient then 688 | * basic_random_static 689 | * \param Engine A random engine with interface like in the std::mt19937 690 | * \param Seeder A seeder type which return seed for internal engine 691 | * through operator() 692 | */ 693 | template< 694 | typename Engine, 695 | typename Seeder = seeder_default, 696 | template class IntegerDist = std::uniform_int_distribution, 697 | template class RealDist = std::uniform_real_distribution, 698 | typename BoolDist = std::bernoulli_distribution 699 | > 700 | class basic_random_thread_local { 701 | public: 702 | basic_random_thread_local( ) = delete; 703 | 704 | /// Type of used random number engine 705 | using engine_type = Engine; 706 | 707 | /// Type of used random number seeder 708 | using seeder_type = Seeder; 709 | 710 | /// Type of used integer distribution 711 | template 712 | using integer_dist_t = IntegerDist; 713 | 714 | /// Type of used real distribution 715 | template 716 | using real_dist_t = RealDist; 717 | 718 | /// Type of used bool distribution 719 | using bool_dist_t = BoolDist; 720 | 721 | /// Key type for getting common type numbers or objects 722 | using common = details::common; 723 | 724 | /** 725 | * \return The minimum value 726 | * potentially generated by the random-number engine 727 | */ 728 | static constexpr typename Engine::result_type min( ) { 729 | return Engine::min( ); 730 | } 731 | 732 | /** 733 | * \return The maximum value 734 | * potentially generated by the random-number engine 735 | */ 736 | static constexpr typename Engine::result_type max( ) { 737 | return Engine::max( ); 738 | } 739 | 740 | /// Advances the internal state by z times 741 | static void discard( const unsigned long long z ) { 742 | engine_instance( ).discard( z ); 743 | } 744 | 745 | /// Reseed by Seeder 746 | static void reseed( ) { 747 | Seeder seeder; 748 | seed( seeder( ) ); 749 | } 750 | 751 | /** 752 | * \brief Reinitializes the internal state 753 | * of the random-number engine using new seed value 754 | * \param value The seed value to use 755 | * in the initialization of the internal state 756 | */ 757 | static void seed( const typename Engine::result_type value = 758 | Engine::default_seed ) { 759 | engine_instance( ).seed( value ); 760 | } 761 | 762 | /** 763 | * \brief Reinitializes the internal state 764 | * of the random-number engine using new seed value 765 | * \param seq The seed sequence 766 | * to use in the initialization of the internal state 767 | */ 768 | template 769 | static void seed( Sseq& seq ) { 770 | engine_instance( ).seed( seq ); 771 | } 772 | 773 | /// return random number from engine in [min(), max()] range 774 | static typename Engine::result_type get( ) { 775 | return engine_instance( )( ); 776 | } 777 | 778 | /** 779 | * \brief Compares internal pseudo-random number engine 780 | * with 'other' pseudo-random number engine. 781 | * Two engines are equal, if their internal states 782 | * are equivalent, that is, if they would generate 783 | * equivalent values for any number of calls of operator() 784 | * \param other The engine, with which the internal engine will be compared 785 | * \return true, if other and internal engine are equal 786 | */ 787 | static bool is_equal( const Engine& other ) { 788 | return engine_instance( ) == other; 789 | } 790 | 791 | /** 792 | * \brief Serializes the internal state of the 793 | * internal pseudo-random number engine as a sequence 794 | * of decimal numbers separated by one or more spaces, 795 | * and inserts it to the stream ost. The fill character 796 | * and the formatting flags of the stream are 797 | * ignored and unaffected. 798 | * \param ost The output stream to insert the data to 799 | */ 800 | template 801 | static void serialize( std::basic_ostream& ost ) { 802 | ost << engine_instance( ); 803 | } 804 | 805 | /** 806 | * \brief Restores the internal state of the 807 | * internal pseudo-random number engine from 808 | * the serialized representation, which 809 | * was created by an earlier call to 'serialize' 810 | * using a stream with the same imbued locale and 811 | * the same CharT and Traits. 812 | * If the input cannot be deserialized, 813 | * internal engine is left unchanged and failbit is raised on ist 814 | * \param ost The input stream to extract the data from 815 | */ 816 | template 817 | static void deserialize( std::basic_istream& ist ) { 818 | ist >> engine_instance( ); 819 | } 820 | 821 | /** 822 | * \brief Generate a random integer number in a [from; to] range 823 | * by std::uniform_int_distribution 824 | * \param from The first limit number of a random range 825 | * \param to The second limit number of a random range 826 | * \return A random integer number in a [from; to] range 827 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 828 | * \note Prevent implicit type conversion 829 | */ 830 | template 831 | static typename std::enable_if::value 832 | , T>::type get( T from = std::numeric_limits::min( ), 833 | T to = std::numeric_limits::max( ) ) { 834 | if( from < to ) // Allow range from higher to lower 835 | return IntegerDist{ from, to }( engine_instance( ) ); 836 | return IntegerDist{ to, from }( engine_instance( ) ); 837 | } 838 | 839 | /** 840 | * \brief Generate a random real number in a [from; to] range 841 | * by std::uniform_real_distribution 842 | * \param from The first limit number of a random range 843 | * \param to The second limit number of a random range 844 | * \return A random real number in a [from; to] range 845 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 846 | * \note Prevent implicit type conversion 847 | */ 848 | template 849 | static typename std::enable_if::value 850 | , T>::type get( T from = std::numeric_limits::min( ), 851 | T to = std::numeric_limits::max( ) ) { 852 | if( from < to ) // Allow range from higher to lower 853 | return RealDist{ from, to }( engine_instance( ) ); 854 | return RealDist{ to, from }( engine_instance( ) ); 855 | } 856 | 857 | /** 858 | * \brief Generate a random byte number in a [from; to] range 859 | * \param from The first limit number of a random range 860 | * \param to The second limit number of a random range 861 | * \return A random byte number in a [from; to] range 862 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 863 | * \note Prevent implicit type conversion 864 | */ 865 | template 866 | static typename std::enable_if::value 867 | , T>::type get( T from = std::numeric_limits::min( ), 868 | T to = std::numeric_limits::max( ) ) { 869 | // Choose between short and unsigned short for byte conversion 870 | using short_t = typename std::conditional::value, 871 | short, unsigned short>::type; 872 | 873 | return static_cast( get( from, to ) ); 874 | } 875 | 876 | /** 877 | * \brief Generate a random common_type number in a [from; to] range 878 | * \param Key The Key type for this version of 'get' method 879 | * Type should be '(THIS_TYPE)::common' struct 880 | * \param from The first limit number of a random range 881 | * \param to The second limit number of a random range 882 | * \return A random common_type number in a [from; to] range 883 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 884 | * \note Allow implicit type conversion 885 | * \note Prevent implicit type conversion from singed to unsigned types 886 | * Why? std::common_type chooses unsigned value, 887 | * then Signed value will be converted to Unsigned value 888 | * which gives us a wrong range for random values. 889 | * https://stackoverflow.com/a/5416498/5734836 890 | */ 891 | template< 892 | typename Key, 893 | typename A, 894 | typename B, 895 | typename C = typename std::common_type::type 896 | > 897 | static typename std::enable_if< 898 | std::is_same::value 899 | && details::is_supported_number::value 900 | && details::is_supported_number::value 901 | // Prevent implicit type conversion from singed to unsigned types 902 | && std::is_signed::value != std::is_unsigned::value 903 | , C>::type get( A from = std::numeric_limits::min( ), 904 | B to = std::numeric_limits::max( ) ) { 905 | return get( static_cast( from ), static_cast( to ) ); 906 | } 907 | 908 | /** 909 | * \brief Generate a random character in a [from; to] range 910 | * by std::uniform_int_distribution 911 | * \param from The first limit number of a random range 912 | * \param to The second limit number of a random range 913 | * \return A random character in a [from; to] range 914 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 915 | * \note Prevent implicit type conversion 916 | */ 917 | template 918 | static typename std::enable_if::value 919 | , T>::type get(T from = std::numeric_limits::min(), 920 | T to = std::numeric_limits::max()) { 921 | if (from < to) // Allow range from higher to lower 922 | return static_cast(IntegerDist{ static_cast(from), static_cast(to) }(engine_instance())); 923 | return static_cast(IntegerDist{ static_cast(to), static_cast(from) }(engine_instance())); 924 | } 925 | 926 | /** 927 | * \brief Generate a bool value with specific probability 928 | * by std::bernoulli_distribution 929 | * \param probability The probability of generating true in [0; 1] range 930 | * 0 means always false, 1 means always true 931 | * \return 'true' with 'probability' probability ('false' otherwise) 932 | */ 933 | template 934 | static typename std::enable_if::value 935 | , bool>::type get( const double probability = 0.5 ) { 936 | assert( 0 <= probability && 1 >= probability ); // out of [0; 1] range 937 | return BoolDist{ probability }( engine_instance( ) ); 938 | } 939 | 940 | /** 941 | * \brief Return random value from initilizer_list 942 | * \param init_list initilizer_list with values 943 | * \return Random value from initilizer_list 944 | * \note Should be 1 or more elements in initilizer_list 945 | * \note Warning! Elements in initilizer_list can't be moved: 946 | * https://stackoverflow.com/a/8193157/5734836 947 | */ 948 | template 949 | static T get( std::initializer_list init_list ) { 950 | assert( 0u != init_list.size( ) ); 951 | return *get( init_list.begin( ), init_list.end( ) ); 952 | } 953 | 954 | /** 955 | * \brief Return random iterator from iterator range 956 | * \param first, last - the range of elements 957 | * \return Random iterator from [first, last) range 958 | * \note If first == last, return last 959 | */ 960 | template 961 | static typename std::enable_if::value 962 | , InputIt>::type get( InputIt first, InputIt last ) { 963 | const auto size = std::distance( first, last ); 964 | if( 0 == size ) return last; 965 | using diff_t = typename std::iterator_traits::difference_type; 966 | return std::next( first, get( 0, size - 1 ) ); 967 | } 968 | 969 | /** 970 | * \brief Return random iterator from Container 971 | * \param container The container with elements 972 | * \return Random iterator from container 973 | * \note If container is empty return std::end( container ) iterator 974 | */ 975 | template 976 | static auto get( Container& container ) -> 977 | typename std::enable_if::value 979 | , decltype(std::begin(container)) 980 | >::type { 981 | return get( std::begin( container ), std::end( container ) ); 982 | } 983 | 984 | /** 985 | * \brief Return container filled with random values 986 | * \param from The first limit number of a random range 987 | * \param to The second limit number of a random range 988 | * \param size The number of elements in resulting container 989 | * \return Container filled with random values 990 | * \note Container "reserve" method will be called before generation 991 | */ 992 | template class Container, typename A> 993 | static typename std::enable_if< 994 | details::has_reserve>::value 995 | , Container>::type get(A from, A to, std::size_t size) { 996 | Container container; 997 | 998 | container.reserve(size); 999 | for (std::size_t i = 0; i < size; ++i) 1000 | container.insert(std::end(container), get(from, to)); 1001 | 1002 | return container; 1003 | } 1004 | 1005 | /** 1006 | * \brief Return container filled with random common_type values 1007 | * \param Key The Key type for this version of 'get' method 1008 | * Type should be '(THIS_TYPE)::common' struct 1009 | * \param from The first limit number of a random range 1010 | * \param to The second limit number of a random range 1011 | * \param size The number of elements in resulting container 1012 | * \return Container filled with random values 1013 | * \note Container "reserve" method will be called before generation 1014 | */ 1015 | template< 1016 | template class Container, 1017 | typename Key, 1018 | typename A, 1019 | typename B, 1020 | typename C = typename std::common_type::type> 1021 | static typename std::enable_if< 1022 | std::is_same::value 1023 | && details::has_reserve>::value 1024 | , Container>::type get(A start, B end, std::size_t size) { 1025 | Container container; 1026 | 1027 | container.reserve(size); 1028 | for (std::size_t i = 0; i < size; ++i) 1029 | container.insert(std::end(container), get(start, end)); 1030 | 1031 | return container; 1032 | } 1033 | 1034 | /** 1035 | * \brief Return container filled with random values 1036 | * \param from The first limit number of a random range 1037 | * \param to The second limit number of a random range 1038 | * \param size The number of elements in resulting container 1039 | * \return Container filled with random values 1040 | */ 1041 | template class Container, typename A> 1042 | static typename std::enable_if< 1043 | !details::has_reserve>::value 1044 | , Container>::type get(A start, A end, std::size_t size) { 1045 | Container container; 1046 | 1047 | for (std::size_t i = 0; i < size; ++i) 1048 | container.insert(std::end(container), get(start, end)); 1049 | 1050 | return container; 1051 | } 1052 | 1053 | /** 1054 | * \brief Return container filled with random common_type values 1055 | * \param Key The Key type for this version of 'get' method 1056 | * Type should be '(THIS_TYPE)::common' struct 1057 | * \param from The first limit number of a random range 1058 | * \param to The second limit number of a random range 1059 | * \param size The number of elements in resulting container 1060 | * \return Container filled with random values 1061 | */ 1062 | template< 1063 | template class Container, 1064 | typename Key, 1065 | typename A, 1066 | typename B, 1067 | typename C = typename std::common_type::type> 1068 | static typename std::enable_if< 1069 | std::is_same::value 1070 | && !details::has_reserve>::value 1071 | , Container>::type get(A start, B end, std::size_t size) { 1072 | Container container; 1073 | 1074 | for (std::size_t i = 0; i < size; ++i) 1075 | container.insert(std::end(container), get(start, end)); 1076 | 1077 | return container; 1078 | } 1079 | 1080 | /** 1081 | * \brief Return array-like container filled with random values 1082 | * \param from The first limit number of a random range 1083 | * \param to The second limit number of a random range 1084 | * \param N The number of elements in resulting container 1085 | * \return Container filled with random values 1086 | */ 1087 | template class Container, std::size_t N, typename A> 1088 | static typename std::enable_if< 1089 | !details::has_insert>::value 1090 | , Container>::type get(A start, A end) { 1091 | Container container = {{ 0 }}; 1092 | 1093 | for (std::size_t i = 0; i < N; ++i) 1094 | container[i] = get(start, end); 1095 | 1096 | return container; 1097 | } 1098 | 1099 | /** 1100 | * \brief Return array-like container filled with random common_type values 1101 | * \param Key The Key type for this version of 'get' method 1102 | * Type should be '(THIS_TYPE)::common' struct 1103 | * \param from The first limit number of a random range 1104 | * \param to The second limit number of a random range 1105 | * \param size The number of elements in resulting container 1106 | * \return Container filled with random values 1107 | */ 1108 | template< 1109 | template class Container, 1110 | std::size_t N, 1111 | typename Key, 1112 | typename A, 1113 | typename B, 1114 | typename C = typename std::common_type::type> 1115 | static typename std::enable_if< 1116 | std::is_same::value 1117 | && !details::has_insert>::value 1118 | , Container>::type get(A start, B end) { 1119 | Container container = {{ 0 }}; 1120 | 1121 | for (std::size_t i = 0; i < N; ++i) 1122 | container[i] = get(start, end); 1123 | 1124 | return container; 1125 | } 1126 | 1127 | /** 1128 | * \brief Return random pointer from built-in array 1129 | * \param array The built-in array with elements 1130 | * \return Pointer to random element in array 1131 | */ 1132 | template 1133 | static T* get( T( &array )[ N ] ) { 1134 | return std::addressof( array[ get( 0, N - 1 ) ] ); 1135 | } 1136 | 1137 | /** 1138 | * \brief Return value from custom Dist distribution 1139 | * seeded by internal random engine 1140 | * \param Dist The type of custom distribution with next concept: 1141 | * http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution 1142 | * \param args The arguments which will be forwarded to Dist constructor 1143 | * \return Value from custom distribution 1144 | */ 1145 | template 1146 | static typename Dist::result_type get( Args&&... args ) { 1147 | return Dist{ std::forward( args )... }( engine_instance( ) ); 1148 | } 1149 | 1150 | /** 1151 | * \brief Return value from custom 'dist' distribution 1152 | * seeded by internal random engine 1153 | * \param dist The custom distribution with next concept: 1154 | * http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution 1155 | * \param args The arguments which will be forwarded to Dist constructor 1156 | * \return Value from custom 'dist' distribution 1157 | */ 1158 | template 1159 | static typename Dist::result_type get( Dist& dist ) { 1160 | return dist( engine_instance( ) ); 1161 | } 1162 | 1163 | /** 1164 | * \brief Reorders the elements in the given range [first, last) 1165 | * such that each possible permutation of those elements 1166 | * has equal probability of appearance. 1167 | * \param first, last - the range of elements to shuffle randomly 1168 | */ 1169 | template 1170 | static void shuffle( RandomIt first, RandomIt last ) { 1171 | std::shuffle( first, last, engine_instance( ) ); 1172 | } 1173 | 1174 | /** 1175 | * \brief Reorders the elements in the given container 1176 | * such that each possible permutation of those elements 1177 | * has equal probability of appearance. 1178 | * \param container - the container with elements to shuffle randomly 1179 | */ 1180 | template 1181 | static void shuffle( Container& container ) { 1182 | shuffle( std::begin( container ), std::end( container ) ); 1183 | } 1184 | 1185 | /// return internal engine by copy 1186 | static Engine get_engine( ) { 1187 | return engine_instance( ); 1188 | } 1189 | 1190 | /// return internal engine by ref 1191 | static Engine& engine() { 1192 | return engine_instance(); 1193 | } 1194 | protected: 1195 | /// get reference to the thread local engine instance 1196 | static Engine& engine_instance( ) { 1197 | thread_local Engine engine{ Seeder{ }( ) }; 1198 | return engine; 1199 | } 1200 | }; 1201 | 1202 | /** 1203 | * \brief Base template class for random 1204 | * with local API and local internal member storage 1205 | * \note it IS thread safe but less efficient then 1206 | * basic_random_static 1207 | * \param Engine A random engine with interface like in the std::mt19937 1208 | * \param Seeder A seeder type which return seed for internal engine 1209 | * through operator() 1210 | */ 1211 | template< 1212 | typename Engine, 1213 | typename Seeder = seeder_default, 1214 | template class IntegerDist = std::uniform_int_distribution, 1215 | template class RealDist = std::uniform_real_distribution, 1216 | typename BoolDist = std::bernoulli_distribution 1217 | > 1218 | class basic_random_local { 1219 | public: 1220 | /// Type of used random number engine 1221 | using engine_type = Engine; 1222 | 1223 | /// Type of used random number seeder 1224 | using seeder_type = Seeder; 1225 | 1226 | /// Type of used integer distribution 1227 | template 1228 | using integer_dist_t = IntegerDist; 1229 | 1230 | /// Type of used real distribution 1231 | template 1232 | using real_dist_t = RealDist; 1233 | 1234 | /// Type of used bool distribution 1235 | using bool_dist_t = BoolDist; 1236 | 1237 | /// Key type for getting common type numbers or objects 1238 | using common = details::common; 1239 | 1240 | /** 1241 | * \return The minimum value 1242 | * potentially generated by the random-number engine 1243 | */ 1244 | static constexpr typename Engine::result_type min( ) { 1245 | return Engine::min( ); 1246 | } 1247 | 1248 | /** 1249 | * \return The maximum value 1250 | * potentially generated by the random-number engine 1251 | */ 1252 | static constexpr typename Engine::result_type max( ) { 1253 | return Engine::max( ); 1254 | } 1255 | 1256 | /// Advances the internal state by z times 1257 | void discard( const unsigned long long z ) { 1258 | m_engine.discard( z ); 1259 | } 1260 | 1261 | /// Reseed by Seeder 1262 | void reseed( ) { 1263 | Seeder seeder; 1264 | seed( seeder( ) ); 1265 | } 1266 | 1267 | /** 1268 | * \brief Reinitializes the internal state 1269 | * of the random-number engine using new seed value 1270 | * \param value The seed value to use 1271 | * in the initialization of the internal state 1272 | */ 1273 | void seed( const typename Engine::result_type value = 1274 | Engine::default_seed ) { 1275 | m_engine.seed( value ); 1276 | } 1277 | 1278 | /** 1279 | * \brief Reinitializes the internal state 1280 | * of the random-number engine using new seed value 1281 | * \param seq The seed sequence 1282 | * to use in the initialization of the internal state 1283 | */ 1284 | template 1285 | void seed( Sseq& seq ) { 1286 | m_engine.seed( seq ); 1287 | } 1288 | 1289 | /// return random number from engine in [min(), max()] range 1290 | typename Engine::result_type get( ) { 1291 | return m_engine( ); 1292 | } 1293 | 1294 | /** 1295 | * \brief Compares internal pseudo-random number engine 1296 | * with 'other' pseudo-random number engine. 1297 | * Two engines are equal, if their internal states 1298 | * are equivalent, that is, if they would generate 1299 | * equivalent values for any number of calls of operator() 1300 | * \param other The engine, with which the internal engine will be compared 1301 | * \return true, if other and internal engine are equal 1302 | */ 1303 | bool is_equal( const Engine& other ) { 1304 | return m_engine == other; 1305 | } 1306 | 1307 | /** 1308 | * \brief Serializes the internal state of the 1309 | * internal pseudo-random number engine as a sequence 1310 | * of decimal numbers separated by one or more spaces, 1311 | * and inserts it to the stream ost. The fill character 1312 | * and the formatting flags of the stream are 1313 | * ignored and unaffected. 1314 | * \param ost The output stream to insert the data to 1315 | */ 1316 | template 1317 | void serialize( std::basic_ostream& ost ) { 1318 | ost << m_engine; 1319 | } 1320 | 1321 | /** 1322 | * \brief Restores the internal state of the 1323 | * internal pseudo-random number engine from 1324 | * the serialized representation, which 1325 | * was created by an earlier call to 'serialize' 1326 | * using a stream with the same imbued locale and 1327 | * the same CharT and Traits. 1328 | * If the input cannot be deserialized, 1329 | * internal engine is left unchanged and failbit is raised on ist 1330 | * \param ost The input stream to extract the data from 1331 | */ 1332 | template 1333 | void deserialize( std::basic_istream& ist ) { 1334 | ist >> m_engine; 1335 | } 1336 | 1337 | /** 1338 | * \brief Generate a random integer number in a [from; to] range 1339 | * by std::uniform_int_distribution 1340 | * \param from The first limit number of a random range 1341 | * \param to The second limit number of a random range 1342 | * \return A random integer number in a [from; to] range 1343 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 1344 | * \note Prevent implicit type conversion 1345 | */ 1346 | template 1347 | typename std::enable_if::value 1348 | , T>::type get( T from = std::numeric_limits::min( ), 1349 | T to = std::numeric_limits::max( ) ) { 1350 | if( from < to ) // Allow range from higher to lower 1351 | return IntegerDist{ from, to }( m_engine ); 1352 | return IntegerDist{ to, from }( m_engine ); 1353 | } 1354 | 1355 | /** 1356 | * \brief Generate a random real number in a [from; to] range 1357 | * by std::uniform_real_distribution 1358 | * \param from The first limit number of a random range 1359 | * \param to The second limit number of a random range 1360 | * \return A random real number in a [from; to] range 1361 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 1362 | * \note Prevent implicit type conversion 1363 | */ 1364 | template 1365 | typename std::enable_if::value 1366 | , T>::type get( T from = std::numeric_limits::min( ), 1367 | T to = std::numeric_limits::max( ) ) { 1368 | if( from < to ) // Allow range from higher to lower 1369 | return RealDist{ from, to }( m_engine ); 1370 | return RealDist{ to, from }( m_engine ); 1371 | } 1372 | 1373 | /** 1374 | * \brief Generate a random byte number in a [from; to] range 1375 | * \param from The first limit number of a random range 1376 | * \param to The second limit number of a random range 1377 | * \return A random byte number in a [from; to] range 1378 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 1379 | * \note Prevent implicit type conversion 1380 | */ 1381 | template 1382 | typename std::enable_if::value 1383 | , T>::type get( T from = std::numeric_limits::min( ), 1384 | T to = std::numeric_limits::max( ) ) { 1385 | // Choose between short and unsigned short for byte conversion 1386 | using short_t = typename std::conditional::value, 1387 | short, unsigned short>::type; 1388 | 1389 | return static_cast( get( from, to ) ); 1390 | } 1391 | 1392 | /** 1393 | * \brief Generate a random common_type number in a [from; to] range 1394 | * \param Key The Key type for this version of 'get' method 1395 | * Type should be '(THIS_TYPE)::common' struct 1396 | * \param from The first limit number of a random range 1397 | * \param to The second limit number of a random range 1398 | * \return A random common_type number in a [from; to] range 1399 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 1400 | * \note Allow implicit type conversion 1401 | * \note Prevent implicit type conversion from singed to unsigned types 1402 | * Why? std::common_type chooses unsigned value, 1403 | * then Signed value will be converted to Unsigned value 1404 | * which gives us a wrong range for random values. 1405 | * https://stackoverflow.com/a/5416498/5734836 1406 | */ 1407 | template< 1408 | typename Key, 1409 | typename A, 1410 | typename B, 1411 | typename C = typename std::common_type::type 1412 | > 1413 | typename std::enable_if< 1414 | std::is_same::value 1415 | && details::is_supported_number::value 1416 | && details::is_supported_number::value 1417 | // Prevent implicit type conversion from singed to unsigned types 1418 | && std::is_signed::value != std::is_unsigned::value 1419 | , C>::type get( A from = std::numeric_limits::min( ), 1420 | B to = std::numeric_limits::max( ) ) { 1421 | return get( static_cast( from ), static_cast( to ) ); 1422 | } 1423 | 1424 | /** 1425 | * \brief Generate a random character in a [from; to] range 1426 | * by std::uniform_int_distribution 1427 | * \param from The first limit number of a random range 1428 | * \param to The second limit number of a random range 1429 | * \return A random character in a [from; to] range 1430 | * \note Allow both: 'from' <= 'to' and 'from' >= 'to' 1431 | * \note Prevent implicit type conversion 1432 | */ 1433 | template 1434 | typename std::enable_if::value 1435 | , T>::type get(T from = std::numeric_limits::min(), 1436 | T to = std::numeric_limits::max()) { 1437 | if (from < to) // Allow range from higher to lower 1438 | return static_cast(IntegerDist{ static_cast(from), static_cast(to) }(m_engine)); 1439 | return static_cast(IntegerDist{ static_cast(to), static_cast(from) }(m_engine)); 1440 | } 1441 | 1442 | /** 1443 | * \brief Generate a bool value with specific probability 1444 | * by std::bernoulli_distribution 1445 | * \param probability The probability of generating true in [0; 1] range 1446 | * 0 means always false, 1 means always true 1447 | * \return 'true' with 'probability' probability ('false' otherwise) 1448 | */ 1449 | template 1450 | typename std::enable_if::value 1451 | , bool>::type get( const double probability = 0.5 ) { 1452 | assert( 0 <= probability && 1 >= probability ); // out of [0; 1] range 1453 | return BoolDist{ probability }( m_engine ); 1454 | } 1455 | 1456 | /** 1457 | * \brief Return random value from initilizer_list 1458 | * \param init_list initilizer_list with values 1459 | * \return Random value from initilizer_list 1460 | * \note Should be 1 or more elements in initilizer_list 1461 | * \note Warning! Elements in initilizer_list can't be moved: 1462 | * https://stackoverflow.com/a/8193157/5734836 1463 | */ 1464 | template 1465 | T get( std::initializer_list init_list ) { 1466 | assert( 0u != init_list.size( ) ); 1467 | return *get( init_list.begin( ), init_list.end( ) ); 1468 | } 1469 | 1470 | /** 1471 | * \brief Return random iterator from iterator range 1472 | * \param first, last - the range of elements 1473 | * \return Random iterator from [first, last) range 1474 | * \note If first == last, return last 1475 | */ 1476 | template 1477 | typename std::enable_if::value 1478 | , InputIt>::type get( InputIt first, InputIt last ) { 1479 | const auto size = std::distance( first, last ); 1480 | if( 0 == size ) return last; 1481 | using diff_t = typename std::iterator_traits::difference_type; 1482 | return std::next( first, get( 0, size - 1 ) ); 1483 | } 1484 | 1485 | /** 1486 | * \brief Return random iterator from Container 1487 | * \param container The container with elements 1488 | * \return Random iterator from container 1489 | * \note If container is empty return std::end( container ) iterator 1490 | */ 1491 | template 1492 | auto get( Container& container ) -> 1493 | typename std::enable_if::value 1495 | , decltype(std::begin(container)) 1496 | >::type { 1497 | return get( std::begin( container ), std::end( container ) ); 1498 | } 1499 | 1500 | /** 1501 | * \brief Return container filled with random values 1502 | * \param from The first limit number of a random range 1503 | * \param to The second limit number of a random range 1504 | * \param size The number of elements in resulting container 1505 | * \return Container filled with random values 1506 | * \note Container "reserve" method will be called before generation 1507 | */ 1508 | template class Container, typename A> 1509 | typename std::enable_if< 1510 | details::has_reserve>::value 1511 | , Container>::type get(A from, A to, std::size_t size) { 1512 | Container container; 1513 | 1514 | container.reserve(size); 1515 | for (std::size_t i = 0; i < size; ++i) 1516 | container.insert(std::end(container), get(from, to)); 1517 | 1518 | return container; 1519 | } 1520 | 1521 | /** 1522 | * \brief Return container filled with random common_type values 1523 | * \param Key The Key type for this version of 'get' method 1524 | * Type should be '(THIS_TYPE)::common' struct 1525 | * \param from The first limit number of a random range 1526 | * \param to The second limit number of a random range 1527 | * \param size The number of elements in resulting container 1528 | * \return Container filled with random values 1529 | * \note Container "reserve" method will be called before generation 1530 | */ 1531 | template< 1532 | template class Container, 1533 | typename Key, 1534 | typename A, 1535 | typename B, 1536 | typename C = typename std::common_type::type> 1537 | typename std::enable_if< 1538 | std::is_same::value 1539 | && details::has_reserve>::value 1540 | , Container>::type get(A start, B end, std::size_t size) { 1541 | Container container; 1542 | 1543 | container.reserve(size); 1544 | for (std::size_t i = 0; i < size; ++i) 1545 | container.insert(std::end(container), get(start, end)); 1546 | 1547 | return container; 1548 | } 1549 | 1550 | /** 1551 | * \brief Return container filled with random values 1552 | * \param from The first limit number of a random range 1553 | * \param to The second limit number of a random range 1554 | * \param size The number of elements in resulting container 1555 | * \return Container filled with random values 1556 | */ 1557 | template class Container, typename A> 1558 | typename std::enable_if< 1559 | !details::has_reserve>::value 1560 | , Container>::type get(A start, A end, std::size_t size) { 1561 | Container container; 1562 | 1563 | for (std::size_t i = 0; i < size; ++i) 1564 | container.insert(std::end(container), get(start, end)); 1565 | 1566 | return container; 1567 | } 1568 | 1569 | /** 1570 | * \brief Return container filled with random common_type values 1571 | * \param Key The Key type for this version of 'get' method 1572 | * Type should be '(THIS_TYPE)::common' struct 1573 | * \param from The first limit number of a random range 1574 | * \param to The second limit number of a random range 1575 | * \param size The number of elements in resulting container 1576 | * \return Container filled with random values 1577 | */ 1578 | template< 1579 | template class Container, 1580 | typename Key, 1581 | typename A, 1582 | typename B, 1583 | typename C = typename std::common_type::type> 1584 | typename std::enable_if< 1585 | std::is_same::value 1586 | && !details::has_reserve>::value 1587 | , Container>::type get(A start, B end, std::size_t size) { 1588 | Container container; 1589 | 1590 | for (std::size_t i = 0; i < size; ++i) 1591 | container.insert(std::end(container), get(start, end)); 1592 | 1593 | return container; 1594 | } 1595 | 1596 | /** 1597 | * \brief Return array-like container filled with random values 1598 | * \param from The first limit number of a random range 1599 | * \param to The second limit number of a random range 1600 | * \param N The number of elements in resulting container 1601 | * \return Container filled with random values 1602 | */ 1603 | template class Container, std::size_t N, typename A> 1604 | typename std::enable_if< 1605 | !details::has_insert>::value 1606 | , Container>::type get(A start, A end) { 1607 | Container container = {{ 0 }}; 1608 | 1609 | for (std::size_t i = 0; i < N; ++i) 1610 | container[i] = get(start, end); 1611 | 1612 | return container; 1613 | } 1614 | 1615 | /** 1616 | * \brief Return array-like container filled with random common_type values 1617 | * \param Key The Key type for this version of 'get' method 1618 | * Type should be '(THIS_TYPE)::common' struct 1619 | * \param from The first limit number of a random range 1620 | * \param to The second limit number of a random range 1621 | * \param size The number of elements in resulting container 1622 | * \return Container filled with random values 1623 | */ 1624 | template< 1625 | template class Container, 1626 | std::size_t N, 1627 | typename Key, 1628 | typename A, 1629 | typename B, 1630 | typename C = typename std::common_type::type> 1631 | typename std::enable_if< 1632 | std::is_same::value 1633 | && !details::has_insert>::value 1634 | , Container>::type get(A start, B end) { 1635 | Container container = {{ 0 }}; 1636 | 1637 | for (std::size_t i = 0; i < N; ++i) 1638 | container[i] = get(start, end); 1639 | 1640 | return container; 1641 | } 1642 | 1643 | /** 1644 | * \brief Return random pointer from built-in array 1645 | * \param array The built-in array with elements 1646 | * \return Pointer to random element in array 1647 | */ 1648 | template 1649 | T* get( T( &array )[ N ] ) { 1650 | return std::addressof( array[ get( 0, N - 1 ) ] ); 1651 | } 1652 | 1653 | /** 1654 | * \brief Return value from custom Dist distribution 1655 | * seeded by internal random engine 1656 | * \param Dist The type of custom distribution with next concept: 1657 | * http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution 1658 | * \param args The arguments which will be forwarded to Dist constructor 1659 | * \return Value from custom distribution 1660 | */ 1661 | template 1662 | typename Dist::result_type get( Args&&... args ) { 1663 | return Dist{ std::forward( args )... }( m_engine ); 1664 | } 1665 | 1666 | /** 1667 | * \brief Return value from custom 'dist' distribution 1668 | * seeded by internal random engine 1669 | * \param dist The custom distribution with next concept: 1670 | * http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution 1671 | * \param args The arguments which will be forwarded to Dist constructor 1672 | * \return Value from custom 'dist' distribution 1673 | */ 1674 | template 1675 | typename Dist::result_type get( Dist& dist ) { 1676 | return dist( m_engine ); 1677 | } 1678 | 1679 | /** 1680 | * \brief Reorders the elements in the given range [first, last) 1681 | * such that each possible permutation of those elements 1682 | * has equal probability of appearance. 1683 | * \param first, last - the range of elements to shuffle randomly 1684 | */ 1685 | template 1686 | void shuffle( RandomIt first, RandomIt last ) { 1687 | std::shuffle( first, last, m_engine ); 1688 | } 1689 | 1690 | /** 1691 | * \brief Reorders the elements in the given container 1692 | * such that each possible permutation of those elements 1693 | * has equal probability of appearance. 1694 | * \param container - the container with elements to shuffle randomly 1695 | */ 1696 | template 1697 | void shuffle( Container& container ) { 1698 | shuffle( std::begin( container ), std::end( container ) ); 1699 | } 1700 | 1701 | /// return internal engine by copy 1702 | Engine get_engine( ) const { 1703 | return m_engine; 1704 | } 1705 | 1706 | /// return internal engine by ref 1707 | Engine& engine() { 1708 | return m_engine; 1709 | } 1710 | protected: 1711 | /// return engine seeded by Seeder 1712 | static Engine make_seeded_engine( ) { 1713 | // Make seeder instance for seed return by reference like std::seed_seq 1714 | return Engine{ Seeder{ }( ) }; 1715 | } 1716 | protected: 1717 | /// The random number engine 1718 | Engine m_engine{ make_seeded_engine( ) }; 1719 | }; 1720 | 1721 | /** 1722 | * \brief The basic static random alias based on a std::mt19937 1723 | * \note It uses static methods API and data with static storage 1724 | * \note Not thread safe but more prefomance 1725 | */ 1726 | using random_static = basic_random_static; 1727 | 1728 | /** 1729 | * \brief The basic static random alias based on a std::mt19937 1730 | * \note It uses static methods API and data with thread_local storage 1731 | * \note Thread safe but less perfomance 1732 | */ 1733 | using random_thread_local = basic_random_thread_local; 1734 | 1735 | /** 1736 | * \brief The basic static random alias based on a std::mt19937 1737 | * \note It uses non static methods API and data with auto storage 1738 | * \note Not thread safe. Should construct on the stack at local scope 1739 | */ 1740 | using random_local = basic_random_local; 1741 | 1742 | } // namespace effolkronium 1743 | 1744 | #endif // #ifndef EFFOLKRONIUM_RANDOM_HPP 1745 | --------------------------------------------------------------------------------