diff --git a/.github/workflows/ci_windows.yml b/.github/workflows/ci_windows.yml index 43ca7126872ff..9e5dc46b21bf7 100644 --- a/.github/workflows/ci_windows.yml +++ b/.github/workflows/ci_windows.yml @@ -21,7 +21,7 @@ jobs: env: BUILD_TYPE: ${{ matrix.build_type }} VCPKG_ROOT: "C:/dartsim/vcpkg" - VCPKG_BUILD_TAG: v0.2.0-70f192e + VCPKG_BUILD_TAG: v6.13-c973b49-1 steps: - name: Checkout uses: actions/checkout@v2 diff --git a/Brewfile b/Brewfile index 6211ab3807e7a..68ff9e0013232 100644 --- a/Brewfile +++ b/Brewfile @@ -3,10 +3,10 @@ brew 'cmake' brew 'pkg-config' brew 'assimp' -brew 'boost' brew 'bullet' brew 'eigen' brew 'fcl' +brew 'fmt' brew 'flann' brew 'ipopt' brew 'libccd' diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ae03efb9e329..5e5ae2da226df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ### [DART 6.13.0 (TBD)](https://github.com/dartsim/dart/milestone/69?closed=1) +* Dependency + + * Added required dependencies: fmt + * Added optional dependencies: spdlog + * Removed required dependencies: Boost + * Build * Dropped supporting FCL < 0.5: [#1647](https://github.com/dartsim/dart/pull/1647) @@ -12,7 +18,7 @@ * Added spdlog support as underlying logging framework: [#1633](https://github.com/dartsim/dart/pull/1633) * Added custom memory allocators: [#1636](https://github.com/dartsim/dart/pull/1636), [#1637](https://github.com/dartsim/dart/pull/1637), [#1639](https://github.com/dartsim/dart/pull/1639), [#1645](https://github.com/dartsim/dart/pull/1645), [#1646](https://github.com/dartsim/dart/pull/1646) * Added Stopwatch class to replace Timer: [#1638](https://github.com/dartsim/dart/pull/1638) - * Removed use of boos::filesystem and boost::optional: [#1648](https://github.com/dartsim/dart/pull/1648) + * Removed Boost dependency: [#1648](https://github.com/dartsim/dart/pull/1648), [#1651](https://github.com/dartsim/dart/pull/1651) * Dynamics diff --git a/CMakeLists.txt b/CMakeLists.txt index a7e2968f967d4..28ca5f2f8b4b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,7 +68,7 @@ string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" set(DART_VERSION "${DART_MAJOR_VERSION}.${DART_MINOR_VERSION}.${DART_PATCH_VERSION}") set(DART_PKG_DESC "Dynamic Animation and Robotics Toolkit.") -set(DART_PKG_EXTERNAL_DEPS "eigen, ccd, fcl, assimp, boost") +set(DART_PKG_EXTERNAL_DEPS "eigen, ccd, fcl, assimp") #=============================================================================== # Build options diff --git a/Dockerfile b/Dockerfile index c751e7c6e9068..aeb5ef9ab10da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,7 @@ RUN apt-get update \ libassimp-dev \ libccd-dev \ libfcl-dev \ - libboost-all-dev \ + libfmt-dev \ libnlopt-cxx-dev \ coinor-libipopt-dev \ libbullet-dev \ @@ -34,6 +34,7 @@ RUN apt-get update \ libxmu-dev \ freeglut3-dev \ libopenscenegraph-dev \ + libspdlog-dev \ && rm -rf /var/lib/apt/lists/* # Install dartpy dependencies diff --git a/cmake/DARTFindBoost.cmake b/cmake/DARTFindBoost.cmake deleted file mode 100644 index 0c1663cf6dbc0..0000000000000 --- a/cmake/DARTFindBoost.cmake +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2011-2022, The DART development contributors -# All rights reserved. -# -# The list of contributors can be found at: -# https://github.com/dartsim/dart/blob/master/LICENSE -# -# This file is provided under the "BSD-style" License - -set(DART_MIN_BOOST_VERSION 1.65.1 CACHE INTERNAL "Boost min version requirement" FORCE) -if(MSVC) - add_definitions(-DBOOST_ALL_NO_LIB) -endif() -add_definitions(-DBOOST_TEST_DYN_LINK) -set(Boost_USE_MULTITHREADED ON) -set(Boost_USE_STATIC_RUNTIME OFF) -set(BOOST_REQUIRED_COMPONENTS system filesystem) -if(DART_VERBOSE) - find_package(Boost ${DART_MIN_BOOST_VERSION} REQUIRED COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) -else() - find_package(Boost ${DART_MIN_BOOST_VERSION} QUIET REQUIRED COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) -endif() - -if(NOT TARGET Boost::system) - add_library(Boost::system INTERFACE IMPORTED) - set_target_properties(Boost::system PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}" - INTERFACE_LINK_LIBRARIES "${Boost_SYSTEM_LIBRARY}" - ) -endif() -if(NOT TARGET Boost::filesystem) - add_library(Boost::filesystem INTERFACE IMPORTED) - set_target_properties(Boost::filesystem PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}" - INTERFACE_LINK_LIBRARIES "${Boost_FILESYSTEM_LIBRARY}" - ) -endif() diff --git a/cmake/DARTFindDependencies.cmake b/cmake/DARTFindDependencies.cmake index b4ef4fce15456..3cdb6ea8df61e 100644 --- a/cmake/DARTFindDependencies.cmake +++ b/cmake/DARTFindDependencies.cmake @@ -9,6 +9,10 @@ if(DART_VERBOSE) message(STATUS "[ Required dependencies for DART core ]") endif() +# fmt +dart_find_package(fmt) +dart_check_required_package(fmt "libfmt") + # Eigen dart_find_package(Eigen3) dart_check_required_package(EIGEN3 "eigen3") @@ -85,9 +89,6 @@ if(ASSIMP_FOUND) unset(CMAKE_REQUIRED_LIBRARIES) endif() -# Boost -dart_find_package(Boost) - # octomap dart_find_package(octomap) if(MSVC) diff --git a/cmake/DARTFindfmt.cmake b/cmake/DARTFindfmt.cmake new file mode 100644 index 0000000000000..2eee80f666e24 --- /dev/null +++ b/cmake/DARTFindfmt.cmake @@ -0,0 +1,9 @@ +# Copyright (c) 2011-2022, The DART development contributors +# All rights reserved. +# +# The list of contributors can be found at: +# https://github.com/dartsim/dart/blob/master/LICENSE +# +# This file is provided under the "BSD-style" License + +find_package(fmt) diff --git a/dart/CMakeLists.txt b/dart/CMakeLists.txt index 8c7ecda232a76..e97af485f9c88 100644 --- a/dart/CMakeLists.txt +++ b/dart/CMakeLists.txt @@ -39,8 +39,8 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${DART_BINARY_DIR}/lib") # Targets - {dependency targets}, source directories, [external dependency libs] #=============================================================================== # dart - common, math, integration, lcpsolver, optimizer, dynamics, collision, -# collision/dart, collision/fcl, constraint, simulation, [eigen3, libccd, -# fcl, assimp, boost] +# collision/dart, collision/fcl, constraint, simulation, [assimp, eigen3, +# fcl, fmt, libccd] # dart-optimizer-ipopt - {dart}, optimizer/ipopt, [ipopt] # dart-optimizer-nlopt - {dart}, optimizer/nlopt, [nlopt] # dart-collision-bullet - {dart}, collision/bullet, [bullet] @@ -131,10 +131,8 @@ target_link_libraries(dart Eigen3::Eigen ccd fcl + fmt::fmt assimp - Boost::boost - Boost::system - Boost::filesystem ) # spdlog settings @@ -207,7 +205,7 @@ endif() add_component_targets(${PROJECT_NAME} dart dart) add_component_dependencies(${PROJECT_NAME} dart external-odelcpsolver) add_component_dependency_packages(${PROJECT_NAME} dart - Eigen3 ccd fcl assimp Boost + assimp Eigen3 ccd fcl fmt ) if(TARGET octomap) add_component_dependency_packages(${PROJECT_NAME} dart octomap) diff --git a/dart/common/String.cpp b/dart/common/String.cpp new file mode 100644 index 0000000000000..a60a42eb198c1 --- /dev/null +++ b/dart/common/String.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2011-2022, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dart/common/String.hpp" + +#include + +namespace dart::common { + +//============================================================================== +std::string toUpper(std::string str) +{ + toUpperInPlace(str); + return str; +} + +//============================================================================== +void toUpperInPlace(std::string& str) +{ + std::transform(str.begin(), str.end(), str.begin(), ::toupper); +} + +//============================================================================== +std::string toLower(std::string str) +{ + toLowerInPlace(str); + return str; +} + +//============================================================================== +void toLowerInPlace(std::string& str) +{ + std::transform(str.begin(), str.end(), str.begin(), ::tolower); +} + +//============================================================================== +std::string trim(const std::string& str, const std::string& whitespaces) +{ + return trimRight(trimLeft(str, whitespaces), whitespaces); +} + +//============================================================================== +std::string trimLeft(const std::string& str, const std::string& whitespaces) +{ + size_t startpos = str.find_first_not_of(whitespaces); + return (startpos == std::string::npos) ? "" : str.substr(startpos); +} + +//============================================================================== +std::string trimRight(const std::string& str, const std::string& whitespaces) +{ + size_t endpos = str.find_last_not_of(whitespaces); + return (endpos == std::string::npos) ? "" : str.substr(0, endpos + 1); +} + +//============================================================================== +std::vector split( + const std::string& str, const std::string& delimiters) +{ + std::vector tokens; + std::size_t start = str.find_first_not_of(delimiters), end = 0; + + while ((end = str.find_first_of(delimiters, start)) != std::string::npos) + { + tokens.push_back(str.substr(start, end - start)); + start = str.find_first_not_of(delimiters, end); + } + + if (start != std::string::npos) + { + tokens.push_back(str.substr(start)); + } + + return tokens; +} + +} // namespace dart::common diff --git a/dart/common/String.hpp b/dart/common/String.hpp new file mode 100644 index 0000000000000..53540698bae0f --- /dev/null +++ b/dart/common/String.hpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2011-2022, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DART_COMMON_STRING_HPP_ +#define DART_COMMON_STRING_HPP_ + +#include +#include + +namespace dart::common { + +/// Converts string to upper cases +std::string toUpper(std::string str); + +/// Converts string to upper cases in place +void toUpperInPlace(std::string& str); + +/// Converts string to lower cases +std::string toLower(std::string str); + +/// Converts string to lower cases in place +void toLowerInPlace(std::string& str); + +/// Trims both sides of string +std::string trim( + const std::string& str, const std::string& whitespaces = " \n\r\t"); + +/// Trims left side of string +std::string trimLeft( + const std::string& str, const std::string& whitespaces = " \n\r\t"); + +/// Trims right side of string +std::string trimRight( + const std::string& str, const std::string& whitespaces = " \n\r\t"); + +/// Splits string given delimiters +std::vector split( + const std::string& str, const std::string& delimiters = " \n\r\t"); + +} // namespace dart::common + +#endif // DART_COMMON_STRING_HPP_ diff --git a/dart/utils/CMakeLists.txt b/dart/utils/CMakeLists.txt index 1f9342dae0f03..be0162f8cefb2 100644 --- a/dart/utils/CMakeLists.txt +++ b/dart/utils/CMakeLists.txt @@ -2,33 +2,11 @@ dart_find_package(tinyxml2) dart_check_optional_package(TINYXML2 "dart-utils" "tinyxml2" "1.0.1") -# Test whether boost algorithm and lexical_cast headers are available. -include(CheckCXXSourceCompiles) -set(CMAKE_REQUIRED_FLAGS "-w") -set(CMAKE_REQUIRED_INCLUDES "${Boost_INCLUDE_DIRS}") -check_cxx_source_compiles( - " - #include - #include - int main() - { - return 0; - } - " - HAS_BOOST_ALGORITHM_LEXICAL_CAST -) -if(HAS_BOOST_ALGORITHM_LEXICAL_CAST) - if(DART_VERBOSE) - message(STATUS "Looking for Boost algorithm and lexical_cast headers - found") - endif() -else() - message(WARNING "Looking for Boost algorithm and lexical_cast headers - NOT found, to use dart-utils, please install missing boost headers") - return() -endif() - # Search all header and source files file(GLOB hdrs "*.hpp") file(GLOB srcs "*.cpp") +file(GLOB detail_hdrs "detail/*.hpp") +file(GLOB detail_srcs "detail/*.cpp") function(dart_add_utils_headers) dart_property_add(DART_UTILS_HEADERS ${ARGN}) @@ -50,7 +28,7 @@ set(target_name ${PROJECT_NAME}-utils) set(component_name utils) # Add target -dart_add_library(${target_name} ${hdrs} ${srcs} ${dart_utils_headers} ${dart_utils_sources}) +dart_add_library(${target_name} ${hdrs} ${srcs} ${detail_hdrs} ${detail_srcs} ${dart_utils_headers} ${dart_utils_sources}) target_link_libraries(${target_name} PUBLIC dart tinyxml2::tinyxml2) # Component @@ -82,8 +60,13 @@ install( DESTINATION include/dart/utils COMPONENT headers ) +install( + FILES ${detail_hdrs} + DESTINATION include/dart/utils/detail + COMPONENT headers +) # Add subdirectories (components) add_subdirectory(urdf) -dart_format_add(${hdrs} ${srcs} ${dart_utils_headers} ${dart_utils_sources}) +dart_format_add(${hdrs} ${srcs} ${detail_hdrs} ${detail_srcs} ${dart_utils_headers} ${dart_utils_sources}) diff --git a/dart/utils/XmlHelpers.cpp b/dart/utils/XmlHelpers.cpp index 14c257d4727ab..42d2805adce1d 100644 --- a/dart/utils/XmlHelpers.cpp +++ b/dart/utils/XmlHelpers.cpp @@ -35,8 +35,7 @@ #include #include -#include -#include +#include #include "dart/common/Console.hpp" #include "dart/common/LocalResourceRetriever.hpp" @@ -48,90 +47,45 @@ namespace utils { //============================================================================== std::string toString(bool v) { - return boost::lexical_cast(v); + return fmt::format("{}", v); } //============================================================================== std::string toString(int v) { - return boost::lexical_cast(v); + return std::to_string(v); } //============================================================================== std::string toString(unsigned int v) { - return boost::lexical_cast(v); + return std::to_string(v); } //============================================================================== std::string toString(float v) { - return boost::lexical_cast(v); + return std::to_string(v); } //============================================================================== std::string toString(double v) { - return boost::lexical_cast(v); + return std::to_string(v); } //============================================================================== std::string toString(char v) { - return boost::lexical_cast(v); -} - -//============================================================================== -std::string toString(const Eigen::Vector2d& v) -{ - return boost::lexical_cast(v.transpose()); -} - -//============================================================================== -std::string toString(const Eigen::Vector3d& v) -{ - return boost::lexical_cast(v.transpose()); -} - -//============================================================================== -std::string toString(const Eigen::Vector3i& v) -{ - return boost::lexical_cast(v.transpose()); -} - -//============================================================================== -std::string toString(const Eigen::Vector6d& v) -{ - return boost::lexical_cast(v.transpose()); -} - -//============================================================================== -std::string toString(const Eigen::VectorXd& v) -{ - return boost::lexical_cast(v.transpose()); -} - -//============================================================================== -std::string toString(const Eigen::Isometry3d& v) -{ - std::ostringstream ostr; - ostr.precision(6); - - Eigen::Vector3d xyz = math::matrixToEulerXYZ(v.linear()); - - ostr << v.translation()(0) << " " << v.translation()(1) << " " - << v.translation()(2) << " "; - ostr << xyz[0] << " " << xyz[1] << " " << xyz[2]; - - return ostr.str(); + return fmt::format("{}", v); } //============================================================================== bool toBool(const std::string& str) { - if (boost::to_upper_copy(str) == "TRUE" || str == "1") + if (common::toUpper(str) == "TRUE" || str == "1") return true; - else if (boost::to_upper_copy(str) == "FALSE" || str == "0") + else if (common::toUpper(str) == "FALSE" || str == "0") return false; else { @@ -144,30 +98,42 @@ bool toBool(const std::string& str) //============================================================================== int toInt(const std::string& str) { - return boost::lexical_cast(str); + return std::stoi(str); } //============================================================================== unsigned int toUInt(const std::string& str) { - return boost::lexical_cast(str); + return static_cast(std::stoul(str)); } //============================================================================== float toFloat(const std::string& str) { - return boost::lexical_cast(str); + return std::stof(str); } //============================================================================== double toDouble(const std::string& str) { - return boost::lexical_cast(str); + return std::stod(str); } + //============================================================================== char toChar(const std::string& str) { - return boost::lexical_cast(str); + if (str.empty()) + { + DART_ERROR(""); + return 0; + } + + if (str.size() != 1) + { + DART_ERROR(""); + } + + return str[0]; } //============================================================================== @@ -175,10 +141,7 @@ Eigen::Vector2d toVector2d(const std::string& str) { Eigen::Vector2d ret; - std::vector pieces; - std::string trimedStr = boost::trim_copy(str); - boost::split( - pieces, trimedStr, boost::is_any_of(" "), boost::token_compress_on); + const std::vector pieces = common::split(common::trim(str)); assert(pieces.size() == 2); for (std::size_t i = 0; i < pieces.size(); ++i) @@ -187,9 +150,9 @@ Eigen::Vector2d toVector2d(const std::string& str) { try { - ret(i) = boost::lexical_cast(pieces[i].c_str()); + ret[i] = toDouble(pieces[i]); } - catch (boost::bad_lexical_cast& e) + catch (std::exception& e) { std::cerr << "value [" << pieces[i] << "] is not a valid double for Eigen::Vector2d[" << i @@ -206,10 +169,7 @@ Eigen::Vector2i toVector2i(const std::string& str) { Eigen::Vector2i ret; - std::vector pieces; - std::string trimedStr = boost::trim_copy(str); - boost::split( - pieces, trimedStr, boost::is_any_of(" "), boost::token_compress_on); + const std::vector pieces = common::split(common::trim(str)); assert(pieces.size() == 2); for (std::size_t i = 0; i < pieces.size(); ++i) @@ -218,9 +178,9 @@ Eigen::Vector2i toVector2i(const std::string& str) { try { - ret(i) = boost::lexical_cast(pieces[i].c_str()); + ret[i] = toDouble(pieces[i]); } - catch (boost::bad_lexical_cast& e) + catch (std::exception& e) { std::cerr << "value [" << pieces[i] << "] is not a valid double for Eigen::Vector2i[" << i @@ -237,10 +197,7 @@ Eigen::Vector3d toVector3d(const std::string& str) { Eigen::Vector3d ret; - std::vector pieces; - std::string trimedStr = boost::trim_copy(str); - boost::split( - pieces, trimedStr, boost::is_any_of(" "), boost::token_compress_on); + const std::vector pieces = common::split(common::trim(str)); assert(pieces.size() == 3); for (std::size_t i = 0; i < pieces.size(); ++i) @@ -249,9 +206,9 @@ Eigen::Vector3d toVector3d(const std::string& str) { try { - ret(i) = boost::lexical_cast(pieces[i].c_str()); + ret[i] = toDouble(pieces[i]); } - catch (boost::bad_lexical_cast& e) + catch (std::exception& e) { std::cerr << "value [" << pieces[i] << "] is not a valid double for Eigen::Vector3d[" << i @@ -268,10 +225,7 @@ Eigen::Vector3i toVector3i(const std::string& str) { Eigen::Vector3i ret; - std::vector pieces; - std::string trimedStr = boost::trim_copy(str); - boost::split( - pieces, trimedStr, boost::is_any_of(" "), boost::token_compress_on); + const std::vector pieces = common::split(common::trim(str)); assert(pieces.size() == 3); for (std::size_t i = 0; i < pieces.size(); ++i) @@ -280,9 +234,9 @@ Eigen::Vector3i toVector3i(const std::string& str) { try { - ret(i) = boost::lexical_cast(pieces[i].c_str()); + ret[i] = toDouble(pieces[i]); } - catch (boost::bad_lexical_cast& e) + catch (std::exception& e) { std::cerr << "value [" << pieces[i] << "] is not a valid int for Eigen::Vector3i[" << i @@ -299,10 +253,7 @@ Eigen::Vector4d toVector4d(const std::string& str) { Eigen::Vector4d ret; - std::vector pieces; - std::string trimedStr = boost::trim_copy(str); - boost::split( - pieces, trimedStr, boost::is_any_of(" "), boost::token_compress_on); + const std::vector pieces = common::split(common::trim(str)); assert(pieces.size() == 4); for (std::size_t i = 0; i < pieces.size(); ++i) @@ -311,9 +262,9 @@ Eigen::Vector4d toVector4d(const std::string& str) { try { - ret(i) = boost::lexical_cast(pieces[i].c_str()); + ret[i] = toDouble(pieces[i]); } - catch (boost::bad_lexical_cast& e) + catch (std::exception& e) { std::cerr << "value [" << pieces[i] << "] is not a valid double for Eigen::Vector4d[" << i @@ -330,10 +281,7 @@ Eigen::Vector6d toVector6d(const std::string& str) { Eigen::Vector6d ret; - std::vector pieces; - std::string trimedStr = boost::trim_copy(str); - boost::split( - pieces, trimedStr, boost::is_any_of(" "), boost::token_compress_on); + const std::vector pieces = common::split(common::trim(str)); assert(pieces.size() == 6); for (std::size_t i = 0; i < pieces.size(); ++i) @@ -342,9 +290,9 @@ Eigen::Vector6d toVector6d(const std::string& str) { try { - ret(i) = boost::lexical_cast(pieces[i].c_str()); + ret[i] = toDouble(pieces[i]); } - catch (boost::bad_lexical_cast& e) + catch (std::exception& e) { std::cerr << "value [" << pieces[i] << "] is not a valid double for Eigen::Vector6d[" << i @@ -359,10 +307,7 @@ Eigen::Vector6d toVector6d(const std::string& str) //============================================================================== Eigen::VectorXd toVectorXd(const std::string& str) { - std::vector pieces; - std::string trimedStr = boost::trim_copy(str); - boost::split( - pieces, trimedStr, boost::is_any_of(" "), boost::token_compress_on); + const std::vector pieces = common::split(common::trim(str)); assert(pieces.size() > 0); Eigen::VectorXd ret(pieces.size()); @@ -373,9 +318,9 @@ Eigen::VectorXd toVectorXd(const std::string& str) { try { - ret(i) = boost::lexical_cast(pieces[i].c_str()); + ret[i] = toDouble(pieces[i]); } - catch (boost::bad_lexical_cast& e) + catch (std::exception& e) { std::cerr << "value [" << pieces[i] << "] is not a valid double for Eigen::VectorXd[" << i @@ -392,10 +337,7 @@ Eigen::Isometry3d toIsometry3d(const std::string& str) { Eigen::Isometry3d T = Eigen::Isometry3d::Identity(); Eigen::Vector6d elements = Eigen::Vector6d::Zero(); - std::vector pieces; - std::string trimedStr = boost::trim_copy(str); - boost::split( - pieces, trimedStr, boost::is_any_of(" "), boost::token_compress_on); + const std::vector pieces = common::split(common::trim(str)); assert(pieces.size() == 6); for (std::size_t i = 0; i < pieces.size(); ++i) @@ -404,9 +346,9 @@ Eigen::Isometry3d toIsometry3d(const std::string& str) { try { - elements(i) = boost::lexical_cast(pieces[i].c_str()); + elements[i] = toDouble(pieces[i]); } - catch (boost::bad_lexical_cast& e) + catch (std::exception& e) { std::cerr << "value [" << pieces[i] << "] is not a valid double for SE3[" << i @@ -425,10 +367,7 @@ Eigen::Isometry3d toIsometry3dWithExtrinsicRotation(const std::string& str) { Eigen::Isometry3d T = Eigen::Isometry3d::Identity(); Eigen::Vector6d elements = Eigen::Vector6d::Zero(); - std::vector pieces; - std::string trimedStr = boost::trim_copy(str); - boost::split( - pieces, trimedStr, boost::is_any_of(" "), boost::token_compress_on); + const std::vector pieces = common::split(common::trim(str)); assert(pieces.size() == 6); for (std::size_t i = 0; i < pieces.size(); ++i) @@ -437,9 +376,9 @@ Eigen::Isometry3d toIsometry3dWithExtrinsicRotation(const std::string& str) { try { - elements(i) = boost::lexical_cast(pieces[i].c_str()); + elements[i] = toDouble(pieces[i]); } - catch (boost::bad_lexical_cast& e) + catch (std::exception& e) { std::cerr << "value [" << pieces[i] << "] is not a valid double for SE3[" << i @@ -477,9 +416,9 @@ bool getValueBool( std::string str = parentElement->FirstChildElement(name.c_str())->GetText(); - if (boost::to_upper_copy(str) == "TRUE" || str == "1") + if (common::toUpper(str) == "TRUE" || str == "1") return true; - else if (boost::to_upper_copy(str) == "FALSE" || str == "0") + else if (common::toUpper(str) == "FALSE" || str == "0") return false; else { diff --git a/dart/utils/XmlHelpers.hpp b/dart/utils/XmlHelpers.hpp index 1b191c948c6ea..1d3684582f7a1 100644 --- a/dart/utils/XmlHelpers.hpp +++ b/dart/utils/XmlHelpers.hpp @@ -36,13 +36,14 @@ #include #include -#include -#include +#include #include #include "dart/common/Console.hpp" #include "dart/common/Deprecated.hpp" +#include "dart/common/Logging.hpp" #include "dart/common/ResourceRetriever.hpp" +#include "dart/math/Geometry.hpp" #include "dart/math/MathTypes.hpp" namespace dart { @@ -54,12 +55,12 @@ std::string toString(unsigned int v); std::string toString(float v); std::string toString(double v); std::string toString(char v); -std::string toString(const Eigen::Vector2d& v); -std::string toString(const Eigen::Vector3d& v); -std::string toString(const Eigen::Vector3i& v); -std::string toString(const Eigen::Vector6d& v); -std::string toString(const Eigen::VectorXd& v); -std::string toString(const Eigen::Isometry3d& v); +template +std::string toString(const Eigen::Matrix& v); +template +std::string toString( + const Eigen::Transform& v, + const std::string& rotationType = "intrinsic"); bool toBool(const std::string& str); int toInt(const std::string& str); @@ -75,46 +76,8 @@ Eigen::Vector4d toVector4d(const std::string& str); Eigen::Vector6d toVector6d(const std::string& str); Eigen::VectorXd toVectorXd(const std::string& str); template -Eigen::Matrix toVectorNd(const std::string& str) -{ - Eigen::Matrix ret = Eigen::Matrix::Zero(); - - std::vector pieces; - std::string trimedStr = boost::trim_copy(str); - boost::split( - pieces, trimedStr, boost::is_any_of(" "), boost::token_compress_on); - std::size_t sizeToRead = std::min(N, pieces.size()); - if (pieces.size() < N) - { - dterr << "Failed to read a vector because the dimension '" << pieces.size() - << "' is less than the expectation '" << N << "'.\n"; - } - else if (pieces.size() > N) - { - dterr << "Failed to read a vector because the dimension '" << pieces.size() - << "' is greater than the expectation '" << N << "'.\n"; - } - - for (std::size_t i = 0; i < sizeToRead; ++i) - { - if (pieces[i] != "") - { - try - { - ret(i) = boost::lexical_cast(pieces[i].c_str()); - } - catch (boost::bad_lexical_cast& e) - { - dterr << "value [" << pieces[i] - << "] is not a valid double for Eigen::Vector" << N << "d[" << i - << "]: " << e.what() << "\n"; - } - } - } - - return ret; -} -// TODO: The definition of _str is not clear for transform (see: #250) +Eigen::Matrix toVectorNd(const std::string& str); +// TODO: The definition of str is not clear for transform (see: #250) Eigen::Isometry3d toIsometry3d(const std::string& str); Eigen::Isometry3d toIsometry3dWithExtrinsicRotation(const std::string& str); @@ -206,11 +169,7 @@ Eigen::VectorXd getAttributeVectorXd( const tinyxml2::XMLElement* element, const std::string& attributeName); template Eigen::Matrix getAttributeVectorNd( - const tinyxml2::XMLElement* element, const std::string& attributeName) -{ - const std::string val = getAttributeString(element, attributeName); - return toVectorNd(val); -} + const tinyxml2::XMLElement* element, const std::string& attributeName); /// TemplatedElementEnumerator is a convenience class to help visiting all the /// child elements of given parent element. This class is templated to cover @@ -225,85 +184,34 @@ class TemplatedElementEnumerator public: /// Constructor that takes parent element and TemplatedElementEnumerator( - ElementPtr parentElement, const std::string& childElementName) - : mParentElement(parentElement), - mChildElementName(childElementName), - mCurrentElement(nullptr) - { - } + ElementPtr parentElement, const std::string& childElementName); /// Destructor - ~TemplatedElementEnumerator() {} + ~TemplatedElementEnumerator(); /// Set the current element to the next sibling element or to the first child /// element of given parent element if it exists; returns success - bool next() - { - if (!mParentElement) - return false; - - if (mCurrentElement) - { - mCurrentElement - = mCurrentElement->NextSiblingElement(mChildElementName.c_str()); - } - else - { - mCurrentElement - = mParentElement->FirstChildElement(mChildElementName.c_str()); - } - - if (!valid()) - mParentElement = nullptr; - - return valid(); - } + bool next(); /// Get the current element - ElementPtr get() const - { - return mCurrentElement; - } + ElementPtr get() const; /// Dereference operator - ElementPtr operator->() const - { - return mCurrentElement; - } + ElementPtr operator->() const; /// Dereference operator - ElementRef operator*() const - { - return *mCurrentElement; - } + ElementRef operator*() const; /// Equality operator - bool operator==(const TemplatedElementEnumerator& rhs) const - { - // If they point at the same node, then the names must match - return (this->mParentElement == rhs.mParentElement) - && (this->mCurrentElement == rhs.mCurrentElement) - && (this->mCurrentElement != nullptr - || (this->mChildElementName == rhs.mChildElementName)); - } + bool operator==(const TemplatedElementEnumerator& rhs) const; /// Assignment operator TemplatedElementEnumerator& operator=( - const TemplatedElementEnumerator& rhs) - { - this->mParentElement = rhs.mParentElement; - this->mChildElementName = rhs.mChildElementName; - this->mCurrentElement = rhs.mCurrentElement; - - return *this; - } + const TemplatedElementEnumerator& rhs); private: /// Returns true if the current element is valid (not a nullptr) - bool valid() const - { - return mCurrentElement != nullptr; - } + bool valid() const; private: /// Parent element @@ -329,4 +237,6 @@ bool copyChildNodes( } // namespace utils } // namespace dart +#include "dart/utils/detail/XmlHelpers-impl.hpp" + #endif // #ifndef DART_UTILS_XMLHELPERS_HPP_ diff --git a/dart/utils/detail/XmlHelpers-impl.hpp b/dart/utils/detail/XmlHelpers-impl.hpp new file mode 100644 index 0000000000000..6fdfc6038c8ce --- /dev/null +++ b/dart/utils/detail/XmlHelpers-impl.hpp @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2011-2022, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DART_UTILS_DETAIL_XMLHELPERS_IMPL_HPP_ +#define DART_UTILS_DETAIL_XMLHELPERS_IMPL_HPP_ + +#include "dart/common/String.hpp" +#include "dart/utils/XmlHelpers.hpp" + +namespace dart::utils { + +//============================================================================== +template +std::string toString(const Eigen::Matrix& v) +{ + std::stringstream ss; + ss << v.transpose(); + return ss.str(); +} + +//============================================================================== +template +std::string toString( + const Eigen::Transform& v, + const std::string& rotationType) +{ + Eigen::Matrix angles; + if (rotationType == "intrinsic") + { + angles = math::matrixToEulerXYZ(v.rotation()); + } + else if (rotationType == "extrinsic") + { + angles = math::matrixToEulerZYX(v.rotation()).reverse(); + } + else + { + DART_ERROR( + "Unsupported rotation type [{}]. Assuming intrinsic.", rotationType); + angles = math::matrixToEulerXYZ(v.rotation()); + } + + std::stringstream ss; + ss.precision(6); + ss << v.translation().transpose() << " "; + ss << angles; + + return ss.str(); +} + +//============================================================================== +template +Eigen::Matrix toVectorNd(const std::string& str) +{ + const std::vector pieces = common::split(common::trim(str)); + const std::size_t sizeToRead = std::min(N, pieces.size()); + if (pieces.size() < N) + { + dterr << "Failed to read a vector because the dimension '" << pieces.size() + << "' is less than the expectation '" << N << "'.\n"; + } + else if (pieces.size() > N) + { + dterr << "Failed to read a vector because the dimension '" << pieces.size() + << "' is greater than the expectation '" << N << "'.\n"; + } + + Eigen::Matrix ret = Eigen::Matrix::Zero(); + + for (std::size_t i = 0; i < sizeToRead; ++i) + { + if (pieces[i] != "") + { + try + { + ret[i] = toDouble(pieces[i]); + } + catch (std::exception& e) + { + dterr << "value [" << pieces[i] + << "] is not a valid double for Eigen::Vector" << N << "d[" << i + << "]: " << e.what() << "\n"; + } + } + } + + return ret; +} + +//============================================================================== +template +Eigen::Matrix getAttributeVectorNd( + const tinyxml2::XMLElement* element, const std::string& attributeName) +{ + const std::string val = getAttributeString(element, attributeName); + return toVectorNd(val); +} + +//============================================================================== +template +TemplatedElementEnumerator::TemplatedElementEnumerator( + ElementPtr parentElement, const std::string& childElementName) + : mParentElement(parentElement), + mChildElementName(childElementName), + mCurrentElement(nullptr) +{ + // Do nothing +} + +//============================================================================== +template +TemplatedElementEnumerator::~TemplatedElementEnumerator() +{ + // Do nothing +} + +//============================================================================== +template +bool TemplatedElementEnumerator::next() +{ + if (!mParentElement) + return false; + + if (mCurrentElement) + { + mCurrentElement + = mCurrentElement->NextSiblingElement(mChildElementName.c_str()); + } + else + { + mCurrentElement + = mParentElement->FirstChildElement(mChildElementName.c_str()); + } + + if (!valid()) + mParentElement = nullptr; + + return valid(); +} + +//============================================================================== +template +typename TemplatedElementEnumerator::ElementPtr +TemplatedElementEnumerator::get() const +{ + return mCurrentElement; +} + +//============================================================================== +template +typename TemplatedElementEnumerator::ElementPtr + TemplatedElementEnumerator::operator->() const +{ + return mCurrentElement; +} + +//============================================================================== +template +typename TemplatedElementEnumerator::ElementRef + TemplatedElementEnumerator::operator*() const +{ + return *mCurrentElement; +} + +//============================================================================== +template +bool TemplatedElementEnumerator::operator==( + const TemplatedElementEnumerator& rhs) const +{ + // If they point at the same node, then the names must match + return (this->mParentElement == rhs.mParentElement) + && (this->mCurrentElement == rhs.mCurrentElement) + && (this->mCurrentElement != nullptr + || (this->mChildElementName == rhs.mChildElementName)); +} + +//============================================================================== +template +TemplatedElementEnumerator& +TemplatedElementEnumerator::operator=( + const TemplatedElementEnumerator& rhs) +{ + this->mParentElement = rhs.mParentElement; + this->mChildElementName = rhs.mChildElementName; + this->mCurrentElement = rhs.mCurrentElement; + + return *this; +} + +//============================================================================== +template +bool TemplatedElementEnumerator::valid() const +{ + return mCurrentElement != nullptr; +} + +} // namespace dart::utils + +#endif // #ifndef DART_UTILS_DETAIL_XMLHELPERS_IMPL_HPP_ diff --git a/dart/utils/mjcf/MjcfParser.cpp b/dart/utils/mjcf/MjcfParser.cpp index 09abf80eb7e25..3a74945315243 100644 --- a/dart/utils/mjcf/MjcfParser.cpp +++ b/dart/utils/mjcf/MjcfParser.cpp @@ -539,7 +539,7 @@ bool createShapeNodes( // Create ShapeNode with the shape created above dynamics::ShapeNode* shapeNode = bodyNode->createShapeNodeWith( - shape, site.getName()); + shape, "site:" + site.getName()); // RGBA dynamics::VisualAspect* visualAspect = shapeNode->getVisualAspect(); diff --git a/dart/utils/mjcf/detail/Body.cpp b/dart/utils/mjcf/detail/Body.cpp index 6d15f66bf1eaa..710f7aceb13af 100644 --- a/dart/utils/mjcf/detail/Body.cpp +++ b/dart/utils/mjcf/detail/Body.cpp @@ -157,7 +157,7 @@ Errors Body::preprocess(const Compiler& compiler) for (Geom& geom : mGeoms) { - const Errors geomErrors = geom.preprocess(compiler); + const Errors geomErrors = geom.preprocess(compiler, true); errors.insert(errors.end(), geomErrors.begin(), geomErrors.end()); } diff --git a/dart/utils/mjcf/detail/Geom.cpp b/dart/utils/mjcf/detail/Geom.cpp index da8991f565624..ba15e85d06733 100644 --- a/dart/utils/mjcf/detail/Geom.cpp +++ b/dart/utils/mjcf/detail/Geom.cpp @@ -110,7 +110,7 @@ static bool canUseFromTo( } //============================================================================== -Errors Geom::preprocess(const Compiler& compiler) +Errors Geom::preprocess(const Compiler& compiler, bool autoName) { Errors errors; @@ -118,6 +118,11 @@ Errors Geom::preprocess(const Compiler& compiler) { mName = *mAttributes.mName; } + else if (autoName) + { + static unsigned int index = 0; + mName = "geom (" + std::to_string(index++) + ")"; + } mType = mAttributes.mType; mConType = mAttributes.mConType; diff --git a/dart/utils/mjcf/detail/Geom.hpp b/dart/utils/mjcf/detail/Geom.hpp index 2464940235160..169428da151af 100644 --- a/dart/utils/mjcf/detail/Geom.hpp +++ b/dart/utils/mjcf/detail/Geom.hpp @@ -117,7 +117,7 @@ class Geom final const GeomAttributes& defaultAttributes); /// Updates attributes and elements that doesn't require any other elements. - Errors preprocess(const Compiler& compiler); + Errors preprocess(const Compiler& compiler, bool autoName = false); /// Updates attributes and elements that require the preprocessed child /// elements of this . diff --git a/dart/utils/urdf/BackwardCompatibility.hpp.in b/dart/utils/urdf/BackwardCompatibility.hpp.in index 815a2fada8604..83b2918af1882 100644 --- a/dart/utils/urdf/BackwardCompatibility.hpp.in +++ b/dart/utils/urdf/BackwardCompatibility.hpp.in @@ -47,25 +47,4 @@ (URDFDOM_HEADERS_MINOR_VERSION > y || (URDFDOM_HEADERS_MINOR_VERSION >= y && \ URDFDOM_HEADERS_PATCH_VERSION >= z)))) -#if URDFDOM_HEADERS_VERSION_AT_LEAST(1,0,0) -#include -#else -#include "boost/shared_ptr.hpp" -#include "boost/weak_ptr.hpp" -#endif - -namespace dart { -namespace utils { - -#if URDFDOM_HEADERS_VERSION_AT_LEAST(1,0,0) -template using urdf_shared_ptr = std::shared_ptr; -template using urdf_weak_ptr = std::weak_ptr; -#else -template using urdf_shared_ptr = boost::shared_ptr; -template using urdf_weak_ptr = boost::weak_ptr; -#endif - -} // namespace utils -} // namespace dart - #endif // DART_UTILS_URDF_BACKWARDCOMPATIBILITY_HPP_ diff --git a/dart/utils/urdf/DartLoader.cpp b/dart/utils/urdf/DartLoader.cpp index 6a04b48641ff5..2a171e18db871 100644 --- a/dart/utils/urdf/DartLoader.cpp +++ b/dart/utils/urdf/DartLoader.cpp @@ -60,8 +60,9 @@ namespace dart { namespace utils { -using ModelInterfacePtr = urdf_shared_ptr; +using ModelInterfacePtr = std::shared_ptr; +//============================================================================== DartLoader::Options::Options( common::ResourceRetrieverPtr resourceRetriever, RootJointType defaultRootJointType, diff --git a/dart/utils/urdf/urdf_world_parser.hpp b/dart/utils/urdf/urdf_world_parser.hpp index c088e271101d5..41acde5ddc09b 100644 --- a/dart/utils/urdf/urdf_world_parser.hpp +++ b/dart/utils/urdf/urdf_world_parser.hpp @@ -62,7 +62,7 @@ class Entity /// Copy over a standard urdfEntity Entity(const urdf::Entity& urdfEntity); - urdf_shared_ptr model; + std::shared_ptr model; urdf::Pose origin; urdf::Twist twist; diff --git a/package.xml b/package.xml index 0fe81252ce067..9562c8582caf6 100644 --- a/package.xml +++ b/package.xml @@ -24,7 +24,6 @@ pkg-config assimp bullet - boost eigen libfcl-dev glut diff --git a/python/dartpy/common/String.cpp b/python/dartpy/common/String.cpp new file mode 100644 index 0000000000000..94b5b21363ce4 --- /dev/null +++ b/python/dartpy/common/String.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2011-2022, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +namespace py = pybind11; + +namespace dart::python { + +void String(py::module& m) +{ + m.def("toUpper", &common::toUpper, py::arg("str")); + m.def("toLower", &common::toLower, py::arg("str")); + m.def( + "trim", + &common::trim, + py::arg("str"), + py::arg("whitespaces") = " \n\r\t"); + m.def( + "trimLeft", + &common::trimLeft, + py::arg("str"), + py::arg("whitespaces") = " \n\r\t"); + m.def( + "trimRight", + &common::trimRight, + py::arg("str"), + py::arg("whitespaces") = " \n\r\t"); + m.def( + "split", + &common::split, + py::arg("str"), + py::arg("delimiters") = " \n\r\t"); +} + +} // namespace dart::python diff --git a/python/dartpy/common/module.cpp b/python/dartpy/common/module.cpp index 606991a64b611..6936fe94af620 100644 --- a/python/dartpy/common/module.cpp +++ b/python/dartpy/common/module.cpp @@ -45,6 +45,7 @@ void Composite(py::module& sm); void Resource(py::module& sm); void ResourceRetriever(py::module& sm); void Stopwatch(py::module& sm); +void String(py::module& sm); void dart_common(py::module& m) { @@ -58,6 +59,7 @@ void dart_common(py::module& m) Resource(sm); ResourceRetriever(sm); Stopwatch(sm); + String(sm); } } // namespace python diff --git a/python/tests/unit/common/test_string.py b/python/tests/unit/common/test_string.py new file mode 100644 index 0000000000000..1bcf2547d0b16 --- /dev/null +++ b/python/tests/unit/common/test_string.py @@ -0,0 +1,42 @@ +# Copyright (c) 2011-2022, The DART development contributors +# All rights reserved. +# +# The list of contributors can be found at: +# https://github.com/dartsim/dart/blob/master/LICENSE +# +# This file is provided under the "BSD-style" License + +import pytest +import dartpy as dart + + +def test_case_conversions(): + assert dart.common.toUpper("to UppEr") == "TO UPPER" + assert dart.common.toLower("to LowEr") == "to lower" + + +def test_trim(): + assert dart.common.trimLeft(" trim ThIs ") == "trim ThIs " + assert dart.common.trimRight(" trim ThIs ") == " trim ThIs" + assert dart.common.trim(" trim ThIs ") == "trim ThIs" + + assert dart.common.trimLeft("\n trim ThIs ", " ") == "\n trim ThIs " + assert dart.common.trimLeft("\n trim ThIs ", "\n") == " trim ThIs " + assert dart.common.trimRight(" trim ThIs \n", " ") == " trim ThIs \n" + assert dart.common.trimRight(" trim ThIs \n", "\n") == " trim ThIs " + assert dart.common.trim("\n trim ThIs \n", " ") == "\n trim ThIs \n" + assert dart.common.trim("\n trim ThIs \n", "\n") == " trim ThIs " + + assert dart.common.trimLeft("\n trim ThIs \n", " \n") == "trim ThIs \n" + assert dart.common.trimRight("\n trim ThIs \n", " \n") == "\n trim ThIs" + assert dart.common.trim("\n trim ThIs \n", " \n") == "trim ThIs" + + +def test_split(): + assert len(dart.common.split(" trim ThIs ")) == 2 + assert dart.common.split(" trim ThIs ")[0] == "trim" + assert dart.common.split(" trim ThIs ")[1] == "ThIs" + + +if __name__ == "__main__": + pytest.main() diff --git a/unittests/unit/common/test_String.cpp b/unittests/unit/common/test_String.cpp new file mode 100644 index 0000000000000..d9ade0b5d79e3 --- /dev/null +++ b/unittests/unit/common/test_String.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011-2022, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +using namespace dart; + +//============================================================================== +TEST(StringTest, CaseConversions) +{ + EXPECT_EQ(common::toUpper("to UppEr"), "TO UPPER"); + EXPECT_EQ(common::toLower("to LowEr"), "to lower"); + + std::string str; + + str = "toUppEr"; + common::toUpperInPlace(str); + EXPECT_EQ(str, "TOUPPER"); + + str = "toLowEr"; + common::toLowerInPlace(str); + EXPECT_EQ(str, "tolower"); +} + +//============================================================================== +TEST(StringTest, Trim) +{ + EXPECT_EQ(common::trimLeft(" trim ThIs "), "trim ThIs "); + EXPECT_EQ(common::trimRight(" trim ThIs "), " trim ThIs"); + EXPECT_EQ(common::trim(" trim ThIs "), "trim ThIs"); + + EXPECT_EQ(common::trimLeft("\n trim ThIs ", " "), "\n trim ThIs "); + EXPECT_EQ(common::trimLeft("\n trim ThIs ", "\n"), " trim ThIs "); + EXPECT_EQ(common::trimRight(" trim ThIs \n", " "), " trim ThIs \n"); + EXPECT_EQ(common::trimRight(" trim ThIs \n", "\n"), " trim ThIs "); + EXPECT_EQ(common::trim("\n trim ThIs \n", " "), "\n trim ThIs \n"); + EXPECT_EQ(common::trim("\n trim ThIs \n", "\n"), " trim ThIs "); + + EXPECT_EQ(common::trimLeft("\n trim ThIs \n", " \n"), "trim ThIs \n"); + EXPECT_EQ(common::trimRight("\n trim ThIs \n", " \n"), "\n trim ThIs"); + EXPECT_EQ(common::trim("\n trim ThIs \n", " \n"), "trim ThIs"); +} + +//============================================================================== +TEST(StringTest, Split) +{ + ASSERT_EQ(common::split(" trim ThIs ").size(), 2); + EXPECT_EQ(common::split(" trim ThIs ")[0], "trim"); + EXPECT_EQ(common::split(" trim ThIs ")[1], "ThIs"); + + ASSERT_EQ(common::split("ThiSisaNApPle ", "a").size(), 2); + ASSERT_EQ(common::split("ThiSisaNApPle ", "a")[0], "ThiSis"); + ASSERT_EQ(common::split("ThiSisaNApPle ", "a")[1], "NApPle "); + + ASSERT_EQ(common::split("ThiSisaNApPle ", "A").size(), 2); + ASSERT_EQ(common::split("ThiSisaNApPle ", "A")[0], "ThiSisaN"); + ASSERT_EQ(common::split("ThiSisaNApPle ", "A")[1], "pPle "); +}