Fix cmake with msvc generator.
* Remove unused libnest2d files. Make it use the global build script targets. * Modify FindTBB to address multi-config builds and take care of __TBB_NO_IMPLICIT_LINKAGE * Move FindNLopt to project common cmake module dir * Rename libnest.hpp to nester.hpp and libnest.h to libnest.hpp * Clean up common test suite build scripts
This commit is contained in:
parent
62b7892fa1
commit
fbc174ad06
@ -291,14 +291,14 @@ if(SLIC3R_STATIC)
|
|||||||
endif()
|
endif()
|
||||||
set(TBB_DEBUG 1)
|
set(TBB_DEBUG 1)
|
||||||
find_package(TBB REQUIRED)
|
find_package(TBB REQUIRED)
|
||||||
include_directories(${TBB_INCLUDE_DIRS})
|
# include_directories(${TBB_INCLUDE_DIRS})
|
||||||
add_definitions(${TBB_DEFINITIONS})
|
# add_definitions(${TBB_DEFINITIONS})
|
||||||
if(MSVC)
|
# if(MSVC)
|
||||||
# Suppress implicit linking of the TBB libraries by the Visual Studio compiler.
|
# # Suppress implicit linking of the TBB libraries by the Visual Studio compiler.
|
||||||
add_definitions(-D__TBB_NO_IMPLICIT_LINKAGE)
|
# add_definitions(-D__TBB_NO_IMPLICIT_LINKAGE)
|
||||||
endif()
|
# endif()
|
||||||
# The Intel TBB library will use the std::exception_ptr feature of C++11.
|
# The Intel TBB library will use the std::exception_ptr feature of C++11.
|
||||||
add_definitions(-DTBB_USE_CAPTURED_EXCEPTION=0)
|
# add_definitions(-DTBB_USE_CAPTURED_EXCEPTION=0)
|
||||||
|
|
||||||
find_package(CURL REQUIRED)
|
find_package(CURL REQUIRED)
|
||||||
include_directories(${CURL_INCLUDE_DIRS})
|
include_directories(${CURL_INCLUDE_DIRS})
|
||||||
@ -375,6 +375,8 @@ add_custom_target(pot
|
|||||||
COMMENT "Generate pot file from strings in the source tree"
|
COMMENT "Generate pot file from strings in the source tree"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
find_package(NLopt 1.4 REQUIRED)
|
||||||
|
|
||||||
# libslic3r, PrusaSlicer GUI and the PrusaSlicer executable.
|
# libslic3r, PrusaSlicer GUI and the PrusaSlicer executable.
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT PrusaSlicer_app_console)
|
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT PrusaSlicer_app_console)
|
||||||
|
@ -21,8 +21,7 @@
|
|||||||
set(NLopt_FOUND FALSE)
|
set(NLopt_FOUND FALSE)
|
||||||
set(NLopt_ERROR_REASON "")
|
set(NLopt_ERROR_REASON "")
|
||||||
set(NLopt_DEFINITIONS "")
|
set(NLopt_DEFINITIONS "")
|
||||||
set(NLopt_LIBS)
|
unset(NLopt_LIBS CACHE)
|
||||||
|
|
||||||
|
|
||||||
set(NLopt_DIR $ENV{NLOPT})
|
set(NLopt_DIR $ENV{NLOPT})
|
||||||
if(NOT NLopt_DIR)
|
if(NOT NLopt_DIR)
|
||||||
@ -48,7 +47,6 @@ if(NOT NLopt_DIR)
|
|||||||
set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Cannot find NLopt header file '${_NLopt_HEADER_FILE_NAME}'.")
|
set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Cannot find NLopt header file '${_NLopt_HEADER_FILE_NAME}'.")
|
||||||
endif()
|
endif()
|
||||||
unset(_NLopt_HEADER_FILE_NAME)
|
unset(_NLopt_HEADER_FILE_NAME)
|
||||||
unset(_NLopt_HEADER_FILE)
|
|
||||||
|
|
||||||
if(NOT NLopt_FOUND)
|
if(NOT NLopt_FOUND)
|
||||||
set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} NLopt not found in system directories (and environment variable NLOPT is not set).")
|
set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} NLopt not found in system directories (and environment variable NLOPT is not set).")
|
||||||
@ -56,7 +54,7 @@ if(NOT NLopt_DIR)
|
|||||||
get_filename_component(NLopt_INCLUDE_DIR ${_NLopt_HEADER_FILE} DIRECTORY )
|
get_filename_component(NLopt_INCLUDE_DIR ${_NLopt_HEADER_FILE} DIRECTORY )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
unset(_NLopt_HEADER_FILE CACHE)
|
||||||
|
|
||||||
else()
|
else()
|
||||||
|
|
||||||
@ -95,7 +93,7 @@ else()
|
|||||||
set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Cannot find NLopt header file '${_NLopt_HEADER_FILE_NAME}' in '${NLopt_INCLUDE_DIR}'.")
|
set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Cannot find NLopt header file '${_NLopt_HEADER_FILE_NAME}' in '${NLopt_INCLUDE_DIR}'.")
|
||||||
endif()
|
endif()
|
||||||
unset(_NLopt_HEADER_FILE_NAME)
|
unset(_NLopt_HEADER_FILE_NAME)
|
||||||
unset(_NLopt_HEADER_FILE)
|
unset(_NLopt_HEADER_FILE CACHE)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -114,10 +112,10 @@ if(NLopt_FOUND)
|
|||||||
message(STATUS "Found NLopt in '${NLopt_DIR}'.")
|
message(STATUS "Found NLopt in '${NLopt_DIR}'.")
|
||||||
message(STATUS "Using NLopt include directory '${NLopt_INCLUDE_DIR}'.")
|
message(STATUS "Using NLopt include directory '${NLopt_INCLUDE_DIR}'.")
|
||||||
message(STATUS "Using NLopt library '${NLopt_LIBS}'.")
|
message(STATUS "Using NLopt library '${NLopt_LIBS}'.")
|
||||||
add_library(Nlopt::Nlopt INTERFACE IMPORTED)
|
add_library(NLopt::nlopt INTERFACE IMPORTED)
|
||||||
set_target_properties(Nlopt::Nlopt PROPERTIES INTERFACE_LINK_LIBRARIES ${NLopt_LIBS})
|
set_target_properties(NLopt::nlopt PROPERTIES INTERFACE_LINK_LIBRARIES ${NLopt_LIBS})
|
||||||
set_target_properties(Nlopt::Nlopt PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${NLopt_INCLUDE_DIR})
|
set_target_properties(NLopt::nlopt PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${NLopt_INCLUDE_DIR})
|
||||||
set_target_properties(Nlopt::Nlopt PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${NLopt_DEFINITIONS}")
|
set_target_properties(NLopt::nlopt PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${NLopt_DEFINITIONS}")
|
||||||
# target_link_libraries(Nlopt::Nlopt INTERFACE ${NLopt_LIBS})
|
# target_link_libraries(Nlopt::Nlopt INTERFACE ${NLopt_LIBS})
|
||||||
# target_include_directories(Nlopt::Nlopt INTERFACE ${NLopt_INCLUDE_DIR})
|
# target_include_directories(Nlopt::Nlopt INTERFACE ${NLopt_INCLUDE_DIR})
|
||||||
# target_compile_definitions(Nlopt::Nlopt INTERFACE ${NLopt_DEFINITIONS})
|
# target_compile_definitions(Nlopt::Nlopt INTERFACE ${NLopt_DEFINITIONS})
|
@ -250,26 +250,23 @@ if(NOT TBB_FOUND)
|
|||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
unset(TBB_STATIC_SUFFIX)
|
|
||||||
|
|
||||||
##################################
|
##################################
|
||||||
# Set compile flags and libraries
|
# Set compile flags and libraries
|
||||||
##################################
|
##################################
|
||||||
|
|
||||||
set(TBB_DEFINITIONS_RELEASE "")
|
set(TBB_DEFINITIONS_RELEASE "")
|
||||||
set(TBB_DEFINITIONS_DEBUG "-DTBB_USE_DEBUG=1")
|
set(TBB_DEFINITIONS_DEBUG "TBB_USE_DEBUG=1")
|
||||||
|
|
||||||
if(TBB_LIBRARIES_${TBB_BUILD_TYPE})
|
if(TBB_LIBRARIES_${TBB_BUILD_TYPE})
|
||||||
set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${TBB_BUILD_TYPE}}")
|
|
||||||
set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}")
|
set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}")
|
||||||
elseif(TBB_LIBRARIES_RELEASE)
|
|
||||||
set(TBB_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}")
|
|
||||||
set(TBB_LIBRARIES "${TBB_LIBRARIES_RELEASE}")
|
|
||||||
elseif(TBB_LIBRARIES_DEBUG)
|
|
||||||
set(TBB_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}")
|
|
||||||
set(TBB_LIBRARIES "${TBB_LIBRARIES_DEBUG}")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (MSVC AND TBB_STATIC)
|
||||||
|
set(TBB_DEFINITIONS __TBB_NO_IMPLICIT_LINKAGE)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
unset (TBB_STATIC_SUFFIX)
|
||||||
|
|
||||||
find_package_handle_standard_args(TBB
|
find_package_handle_standard_args(TBB
|
||||||
REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES
|
REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES
|
||||||
HANDLE_COMPONENTS
|
HANDLE_COMPONENTS
|
||||||
@ -280,25 +277,18 @@ if(NOT TBB_FOUND)
|
|||||||
##################################
|
##################################
|
||||||
|
|
||||||
if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND)
|
if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND)
|
||||||
add_library(tbb UNKNOWN IMPORTED)
|
add_library(TBB::tbb UNKNOWN IMPORTED)
|
||||||
set_target_properties(tbb PROPERTIES
|
set_target_properties(TBB::tbb PROPERTIES
|
||||||
INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS}
|
INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS}
|
||||||
IMPORTED_LOCATION ${TBB_LIBRARIES})
|
IMPORTED_LOCATION ${TBB_LIBRARIES})
|
||||||
if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG)
|
if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG)
|
||||||
set_target_properties(tbb PROPERTIES
|
set_target_properties(TBB::tbb PROPERTIES
|
||||||
INTERFACE_COMPILE_DEFINITIONS "$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:TBB_USE_DEBUG=1>"
|
INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS};$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:${TBB_DEFINITIONS_DEBUG}>;$<$<CONFIG:Release>:${TBB_DEFINITIONS_RELEASE}>"
|
||||||
IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG}
|
IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG}
|
||||||
IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_RELEASE}
|
IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_RELEASE}
|
||||||
IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE}
|
IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE}
|
||||||
IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE}
|
IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE}
|
||||||
)
|
)
|
||||||
elseif(TBB_LIBRARIES_RELEASE)
|
|
||||||
set_target_properties(tbb PROPERTIES IMPORTED_LOCATION ${TBB_LIBRARIES_RELEASE})
|
|
||||||
else()
|
|
||||||
set_target_properties(tbb PROPERTIES
|
|
||||||
INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}"
|
|
||||||
IMPORTED_LOCATION ${TBB_LIBRARIES_DEBUG}
|
|
||||||
)
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ add_subdirectory(semver)
|
|||||||
add_subdirectory(libigl)
|
add_subdirectory(libigl)
|
||||||
|
|
||||||
# Adding libnest2d project for bin packing...
|
# Adding libnest2d project for bin packing...
|
||||||
set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d")
|
|
||||||
add_subdirectory(libnest2d)
|
add_subdirectory(libnest2d)
|
||||||
|
|
||||||
add_subdirectory(libslic3r)
|
add_subdirectory(libslic3r)
|
||||||
|
@ -1,106 +1,31 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
|
||||||
|
|
||||||
project(Libnest2D)
|
|
||||||
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
# Update if necessary
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long ")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED)
|
|
||||||
|
|
||||||
# Add our own cmake module path.
|
|
||||||
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/)
|
|
||||||
|
|
||||||
option(LIBNEST2D_HEADER_ONLY "If enabled static library will not be built." ON)
|
|
||||||
|
|
||||||
set(GEOMETRY_BACKENDS clipper boost eigen)
|
|
||||||
set(LIBNEST2D_GEOMETRIES clipper CACHE STRING "Geometry backend")
|
|
||||||
set_property(CACHE LIBNEST2D_GEOMETRIES PROPERTY STRINGS ${GEOMETRY_BACKENDS})
|
|
||||||
list(FIND GEOMETRY_BACKENDS ${LIBNEST2D_GEOMETRIES} GEOMETRY_TYPE)
|
|
||||||
if(${GEOMETRY_TYPE} EQUAL -1)
|
|
||||||
message(FATAL_ERROR "Option ${LIBNEST2D_GEOMETRIES} not supported, valid entries are ${GEOMETRY_BACKENDS}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(OPTIMIZERS nlopt optimlib)
|
|
||||||
set(LIBNEST2D_OPTIMIZER nlopt CACHE STRING "Optimization backend")
|
|
||||||
set_property(CACHE LIBNEST2D_OPTIMIZER PROPERTY STRINGS ${OPTIMIZERS})
|
|
||||||
list(FIND OPTIMIZERS ${LIBNEST2D_OPTIMIZER} OPTIMIZER_TYPE)
|
|
||||||
if(${OPTIMIZER_TYPE} EQUAL -1)
|
|
||||||
message(FATAL_ERROR "Option ${LIBNEST2D_OPTIMIZER} not supported, valid entries are ${OPTIMIZERS}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(libnest2d INTERFACE)
|
|
||||||
|
|
||||||
set(SRC_DIR ${PROJECT_SOURCE_DIR}/include)
|
|
||||||
|
|
||||||
set(LIBNEST2D_SRCFILES
|
set(LIBNEST2D_SRCFILES
|
||||||
${SRC_DIR}/libnest2d/libnest2d.hpp # Templates only
|
include/libnest2d/libnest2d.hpp
|
||||||
${SRC_DIR}/libnest2d/geometry_traits.hpp
|
include/libnest2d/nester.hpp
|
||||||
${SRC_DIR}/libnest2d/geometry_traits_nfp.hpp
|
include/libnest2d/geometry_traits.hpp
|
||||||
${SRC_DIR}/libnest2d/common.hpp
|
include/libnest2d/geometry_traits_nfp.hpp
|
||||||
${SRC_DIR}/libnest2d/optimizer.hpp
|
include/libnest2d/common.hpp
|
||||||
${SRC_DIR}/libnest2d/utils/metaloop.hpp
|
include/libnest2d/optimizer.hpp
|
||||||
${SRC_DIR}/libnest2d/utils/rotfinder.hpp
|
include/libnest2d/utils/metaloop.hpp
|
||||||
${SRC_DIR}/libnest2d/utils/rotcalipers.hpp
|
include/libnest2d/utils/rotfinder.hpp
|
||||||
${SRC_DIR}/libnest2d/utils/bigint.hpp
|
include/libnest2d/utils/rotcalipers.hpp
|
||||||
${SRC_DIR}/libnest2d/utils/rational.hpp
|
include/libnest2d/placers/placer_boilerplate.hpp
|
||||||
${SRC_DIR}/libnest2d/placers/placer_boilerplate.hpp
|
include/libnest2d/placers/bottomleftplacer.hpp
|
||||||
${SRC_DIR}/libnest2d/placers/bottomleftplacer.hpp
|
include/libnest2d/placers/nfpplacer.hpp
|
||||||
${SRC_DIR}/libnest2d/placers/nfpplacer.hpp
|
include/libnest2d/selections/selection_boilerplate.hpp
|
||||||
${SRC_DIR}/libnest2d/selections/selection_boilerplate.hpp
|
#include/libnest2d/selections/filler.hpp
|
||||||
${SRC_DIR}/libnest2d/selections/filler.hpp
|
include/libnest2d/selections/firstfit.hpp
|
||||||
${SRC_DIR}/libnest2d/selections/firstfit.hpp
|
#include/libnest2d/selections/djd_heuristic.hpp
|
||||||
${SRC_DIR}/libnest2d/selections/djd_heuristic.hpp
|
include/libnest2d/backends/clipper/geometries.hpp
|
||||||
|
include/libnest2d/backends/clipper/clipper_polygon.hpp
|
||||||
|
include/libnest2d/optimizers/nlopt/nlopt_boilerplate.hpp
|
||||||
|
include/libnest2d/optimizers/nlopt/simplex.hpp
|
||||||
|
include/libnest2d/optimizers/nlopt/subplex.hpp
|
||||||
|
include/libnest2d/optimizers/nlopt/genetic.hpp
|
||||||
|
src/libnest2d.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(TBB_STATIC ON)
|
add_library(libnest2d ${LIBNEST2D_SRCFILES})
|
||||||
find_package(TBB QUIET)
|
|
||||||
if(TBB_FOUND)
|
|
||||||
message(STATUS "Parallelization with Intel TBB")
|
|
||||||
target_include_directories(libnest2d INTERFACE ${TBB_INCLUDE_DIRS})
|
|
||||||
target_compile_definitions(libnest2d INTERFACE ${TBB_DEFINITIONS} -DUSE_TBB)
|
|
||||||
if(MSVC)
|
|
||||||
# Suppress implicit linking of the TBB libraries by the Visual Studio compiler.
|
|
||||||
target_compile_definitions(libnest2d INTERFACE -D__TBB_NO_IMPLICIT_LINKAGE)
|
|
||||||
endif()
|
|
||||||
# The Intel TBB library will use the std::exception_ptr feature of C++11.
|
|
||||||
target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=0)
|
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||||
target_link_libraries(libnest2d INTERFACE
|
target_link_libraries(libnest2d PUBLIC clipper NLopt::nlopt TBB::tbb Boost::boost)
|
||||||
tbb # VS debug mode needs linking this way:
|
target_compile_definitions(libnest2d PUBLIC LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_clipper)
|
||||||
# ${TBB_LIBRARIES}
|
|
||||||
${CMAKE_DL_LIBS}
|
|
||||||
Threads::Threads
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
find_package(OpenMP QUIET)
|
|
||||||
|
|
||||||
if(OpenMP_CXX_FOUND)
|
|
||||||
message(STATUS "Parallelization with OpenMP")
|
|
||||||
target_include_directories(libnest2d INTERFACE OpenMP::OpenMP_CXX)
|
|
||||||
target_link_libraries(libnest2d INTERFACE OpenMP::OpenMP_CXX)
|
|
||||||
else()
|
|
||||||
message("Parallelization with C++11 threads")
|
|
||||||
find_package(Threads REQUIRED)
|
|
||||||
target_link_libraries(libnest2d INTERFACE Threads::Threads)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_subdirectory(${SRC_DIR}/libnest2d/backends/${LIBNEST2D_GEOMETRIES})
|
|
||||||
target_link_libraries(libnest2d INTERFACE ${LIBNEST2D_GEOMETRIES}Backend)
|
|
||||||
|
|
||||||
add_subdirectory(${SRC_DIR}/libnest2d/optimizers/${LIBNEST2D_OPTIMIZER})
|
|
||||||
target_link_libraries(libnest2d INTERFACE ${LIBNEST2D_OPTIMIZER}Optimizer)
|
|
||||||
|
|
||||||
# target_sources(libnest2d INTERFACE ${LIBNEST2D_SRCFILES})
|
|
||||||
target_include_directories(libnest2d INTERFACE ${SRC_DIR})
|
|
||||||
|
|
||||||
if(NOT LIBNEST2D_HEADER_ONLY)
|
|
||||||
set(LIBNAME libnest2d_${LIBNEST2D_GEOMETRIES}_${LIBNEST2D_OPTIMIZER})
|
|
||||||
add_library(${LIBNAME} ${PROJECT_SOURCE_DIR}/src/libnest2d.cpp)
|
|
||||||
target_link_libraries(${LIBNAME} PUBLIC libnest2d)
|
|
||||||
target_compile_definitions(${LIBNAME} PUBLIC LIBNEST2D_STATIC)
|
|
||||||
endif()
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
include(DownloadProject)
|
|
||||||
|
|
||||||
if (CMAKE_VERSION VERSION_LESS 3.2)
|
|
||||||
set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
|
|
||||||
else()
|
|
||||||
set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(URL_NLOPT "https://github.com/stevengj/nlopt.git"
|
|
||||||
CACHE STRING "Location of the nlopt git repository")
|
|
||||||
|
|
||||||
# set(NLopt_DIR ${CMAKE_BINARY_DIR}/nlopt)
|
|
||||||
include(DownloadProject)
|
|
||||||
download_project( PROJ nlopt
|
|
||||||
GIT_REPOSITORY ${URL_NLOPT}
|
|
||||||
GIT_TAG v2.5.0
|
|
||||||
# CMAKE_CACHE_ARGS -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${NLopt_DIR}
|
|
||||||
${UPDATE_DISCONNECTED_IF_AVAILABLE}
|
|
||||||
)
|
|
||||||
|
|
||||||
set(SHARED_LIBS_STATE BUILD_SHARED_LIBS)
|
|
||||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NLOPT_PYTHON OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NLOPT_OCTAVE OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NLOPT_MATLAB OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NLOPT_GUILE OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NLOPT_SWIG OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NLOPT_LINK_PYTHON OFF CACHE BOOL "" FORCE)
|
|
||||||
|
|
||||||
add_subdirectory(${nlopt_SOURCE_DIR} ${nlopt_BINARY_DIR})
|
|
||||||
|
|
||||||
set(NLopt_LIBS nlopt)
|
|
||||||
set(NLopt_INCLUDE_DIR ${nlopt_BINARY_DIR}
|
|
||||||
${nlopt_BINARY_DIR}/src/api)
|
|
||||||
set(SHARED_LIBS_STATE ${SHARED_STATE})
|
|
@ -1,17 +0,0 @@
|
|||||||
# Distributed under the OSI-approved MIT License. See accompanying
|
|
||||||
# file LICENSE or https://github.com/Crascit/DownloadProject for details.
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 2.8.2)
|
|
||||||
|
|
||||||
project(${DL_ARGS_PROJ}-download NONE)
|
|
||||||
|
|
||||||
include(ExternalProject)
|
|
||||||
ExternalProject_Add(${DL_ARGS_PROJ}-download
|
|
||||||
${DL_ARGS_UNPARSED_ARGUMENTS}
|
|
||||||
SOURCE_DIR "${DL_ARGS_SOURCE_DIR}"
|
|
||||||
BINARY_DIR "${DL_ARGS_BINARY_DIR}"
|
|
||||||
CONFIGURE_COMMAND ""
|
|
||||||
BUILD_COMMAND ""
|
|
||||||
INSTALL_COMMAND ""
|
|
||||||
TEST_COMMAND ""
|
|
||||||
)
|
|
@ -1,182 +0,0 @@
|
|||||||
# Distributed under the OSI-approved MIT License. See accompanying
|
|
||||||
# file LICENSE or https://github.com/Crascit/DownloadProject for details.
|
|
||||||
#
|
|
||||||
# MODULE: DownloadProject
|
|
||||||
#
|
|
||||||
# PROVIDES:
|
|
||||||
# download_project( PROJ projectName
|
|
||||||
# [PREFIX prefixDir]
|
|
||||||
# [DOWNLOAD_DIR downloadDir]
|
|
||||||
# [SOURCE_DIR srcDir]
|
|
||||||
# [BINARY_DIR binDir]
|
|
||||||
# [QUIET]
|
|
||||||
# ...
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# Provides the ability to download and unpack a tarball, zip file, git repository,
|
|
||||||
# etc. at configure time (i.e. when the cmake command is run). How the downloaded
|
|
||||||
# and unpacked contents are used is up to the caller, but the motivating case is
|
|
||||||
# to download source code which can then be included directly in the build with
|
|
||||||
# add_subdirectory() after the call to download_project(). Source and build
|
|
||||||
# directories are set up with this in mind.
|
|
||||||
#
|
|
||||||
# The PROJ argument is required. The projectName value will be used to construct
|
|
||||||
# the following variables upon exit (obviously replace projectName with its actual
|
|
||||||
# value):
|
|
||||||
#
|
|
||||||
# projectName_SOURCE_DIR
|
|
||||||
# projectName_BINARY_DIR
|
|
||||||
#
|
|
||||||
# The SOURCE_DIR and BINARY_DIR arguments are optional and would not typically
|
|
||||||
# need to be provided. They can be specified if you want the downloaded source
|
|
||||||
# and build directories to be located in a specific place. The contents of
|
|
||||||
# projectName_SOURCE_DIR and projectName_BINARY_DIR will be populated with the
|
|
||||||
# locations used whether you provide SOURCE_DIR/BINARY_DIR or not.
|
|
||||||
#
|
|
||||||
# The DOWNLOAD_DIR argument does not normally need to be set. It controls the
|
|
||||||
# location of the temporary CMake build used to perform the download.
|
|
||||||
#
|
|
||||||
# The PREFIX argument can be provided to change the base location of the default
|
|
||||||
# values of DOWNLOAD_DIR, SOURCE_DIR and BINARY_DIR. If all of those three arguments
|
|
||||||
# are provided, then PREFIX will have no effect. The default value for PREFIX is
|
|
||||||
# CMAKE_BINARY_DIR.
|
|
||||||
#
|
|
||||||
# The QUIET option can be given if you do not want to show the output associated
|
|
||||||
# with downloading the specified project.
|
|
||||||
#
|
|
||||||
# In addition to the above, any other options are passed through unmodified to
|
|
||||||
# ExternalProject_Add() to perform the actual download, patch and update steps.
|
|
||||||
# The following ExternalProject_Add() options are explicitly prohibited (they
|
|
||||||
# are reserved for use by the download_project() command):
|
|
||||||
#
|
|
||||||
# CONFIGURE_COMMAND
|
|
||||||
# BUILD_COMMAND
|
|
||||||
# INSTALL_COMMAND
|
|
||||||
# TEST_COMMAND
|
|
||||||
#
|
|
||||||
# Only those ExternalProject_Add() arguments which relate to downloading, patching
|
|
||||||
# and updating of the project sources are intended to be used. Also note that at
|
|
||||||
# least one set of download-related arguments are required.
|
|
||||||
#
|
|
||||||
# If using CMake 3.2 or later, the UPDATE_DISCONNECTED option can be used to
|
|
||||||
# prevent a check at the remote end for changes every time CMake is run
|
|
||||||
# after the first successful download. See the documentation of the ExternalProject
|
|
||||||
# module for more information. It is likely you will want to use this option if it
|
|
||||||
# is available to you. Note, however, that the ExternalProject implementation contains
|
|
||||||
# bugs which result in incorrect handling of the UPDATE_DISCONNECTED option when
|
|
||||||
# using the URL download method or when specifying a SOURCE_DIR with no download
|
|
||||||
# method. Fixes for these have been created, the last of which is scheduled for
|
|
||||||
# inclusion in CMake 3.8.0. Details can be found here:
|
|
||||||
#
|
|
||||||
# https://gitlab.kitware.com/cmake/cmake/commit/bdca68388bd57f8302d3c1d83d691034b7ffa70c
|
|
||||||
# https://gitlab.kitware.com/cmake/cmake/issues/16428
|
|
||||||
#
|
|
||||||
# If you experience build errors related to the update step, consider avoiding
|
|
||||||
# the use of UPDATE_DISCONNECTED.
|
|
||||||
#
|
|
||||||
# EXAMPLE USAGE:
|
|
||||||
#
|
|
||||||
# include(DownloadProject)
|
|
||||||
# download_project(PROJ googletest
|
|
||||||
# GIT_REPOSITORY https://github.com/google/googletest.git
|
|
||||||
# GIT_TAG master
|
|
||||||
# UPDATE_DISCONNECTED 1
|
|
||||||
# QUIET
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
|
|
||||||
#
|
|
||||||
#========================================================================================
|
|
||||||
|
|
||||||
|
|
||||||
set(_DownloadProjectDir "${CMAKE_CURRENT_LIST_DIR}")
|
|
||||||
|
|
||||||
include(CMakeParseArguments)
|
|
||||||
|
|
||||||
function(download_project)
|
|
||||||
|
|
||||||
set(options QUIET)
|
|
||||||
set(oneValueArgs
|
|
||||||
PROJ
|
|
||||||
PREFIX
|
|
||||||
DOWNLOAD_DIR
|
|
||||||
SOURCE_DIR
|
|
||||||
BINARY_DIR
|
|
||||||
# Prevent the following from being passed through
|
|
||||||
CONFIGURE_COMMAND
|
|
||||||
BUILD_COMMAND
|
|
||||||
INSTALL_COMMAND
|
|
||||||
TEST_COMMAND
|
|
||||||
)
|
|
||||||
set(multiValueArgs "")
|
|
||||||
|
|
||||||
cmake_parse_arguments(DL_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
||||||
|
|
||||||
# Hide output if requested
|
|
||||||
if (DL_ARGS_QUIET)
|
|
||||||
set(OUTPUT_QUIET "OUTPUT_QUIET")
|
|
||||||
else()
|
|
||||||
unset(OUTPUT_QUIET)
|
|
||||||
message(STATUS "Downloading/updating ${DL_ARGS_PROJ}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Set up where we will put our temporary CMakeLists.txt file and also
|
|
||||||
# the base point below which the default source and binary dirs will be.
|
|
||||||
# The prefix must always be an absolute path.
|
|
||||||
if (NOT DL_ARGS_PREFIX)
|
|
||||||
set(DL_ARGS_PREFIX "${CMAKE_BINARY_DIR}")
|
|
||||||
else()
|
|
||||||
get_filename_component(DL_ARGS_PREFIX "${DL_ARGS_PREFIX}" ABSOLUTE
|
|
||||||
BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
|
||||||
endif()
|
|
||||||
if (NOT DL_ARGS_DOWNLOAD_DIR)
|
|
||||||
set(DL_ARGS_DOWNLOAD_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-download")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Ensure the caller can know where to find the source and build directories
|
|
||||||
if (NOT DL_ARGS_SOURCE_DIR)
|
|
||||||
set(DL_ARGS_SOURCE_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-src")
|
|
||||||
endif()
|
|
||||||
if (NOT DL_ARGS_BINARY_DIR)
|
|
||||||
set(DL_ARGS_BINARY_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-build")
|
|
||||||
endif()
|
|
||||||
set(${DL_ARGS_PROJ}_SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" PARENT_SCOPE)
|
|
||||||
set(${DL_ARGS_PROJ}_BINARY_DIR "${DL_ARGS_BINARY_DIR}" PARENT_SCOPE)
|
|
||||||
|
|
||||||
# The way that CLion manages multiple configurations, it causes a copy of
|
|
||||||
# the CMakeCache.txt to be copied across due to it not expecting there to
|
|
||||||
# be a project within a project. This causes the hard-coded paths in the
|
|
||||||
# cache to be copied and builds to fail. To mitigate this, we simply
|
|
||||||
# remove the cache if it exists before we configure the new project. It
|
|
||||||
# is safe to do so because it will be re-generated. Since this is only
|
|
||||||
# executed at the configure step, it should not cause additional builds or
|
|
||||||
# downloads.
|
|
||||||
file(REMOVE "${DL_ARGS_DOWNLOAD_DIR}/CMakeCache.txt")
|
|
||||||
|
|
||||||
# Create and build a separate CMake project to carry out the download.
|
|
||||||
# If we've already previously done these steps, they will not cause
|
|
||||||
# anything to be updated, so extra rebuilds of the project won't occur.
|
|
||||||
# Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project
|
|
||||||
# has this set to something not findable on the PATH.
|
|
||||||
configure_file("${_DownloadProjectDir}/DownloadProject.CMakeLists.cmake.in"
|
|
||||||
"${DL_ARGS_DOWNLOAD_DIR}/CMakeLists.txt")
|
|
||||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}"
|
|
||||||
-D "CMAKE_MAKE_PROGRAM:FILE=${CMAKE_MAKE_PROGRAM}"
|
|
||||||
.
|
|
||||||
RESULT_VARIABLE result
|
|
||||||
${OUTPUT_QUIET}
|
|
||||||
WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}"
|
|
||||||
)
|
|
||||||
if(result)
|
|
||||||
message(FATAL_ERROR "CMake step for ${DL_ARGS_PROJ} failed: ${result}")
|
|
||||||
endif()
|
|
||||||
execute_process(COMMAND ${CMAKE_COMMAND} --build .
|
|
||||||
RESULT_VARIABLE result
|
|
||||||
${OUTPUT_QUIET}
|
|
||||||
WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}"
|
|
||||||
)
|
|
||||||
if(result)
|
|
||||||
message(FATAL_ERROR "Build step for ${DL_ARGS_PROJ} failed: ${result}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
endfunction()
|
|
@ -1,58 +0,0 @@
|
|||||||
# Find Clipper library (http://www.angusj.com/delphi/clipper.php).
|
|
||||||
# The following variables are set
|
|
||||||
#
|
|
||||||
# CLIPPER_FOUND
|
|
||||||
# CLIPPER_INCLUDE_DIRS
|
|
||||||
# CLIPPER_LIBRARIES
|
|
||||||
#
|
|
||||||
# It searches the environment variable $CLIPPER_PATH automatically.
|
|
||||||
|
|
||||||
FIND_PATH(CLIPPER_INCLUDE_DIRS clipper.hpp
|
|
||||||
$ENV{CLIPPER_PATH}
|
|
||||||
$ENV{CLIPPER_PATH}/cpp/
|
|
||||||
$ENV{CLIPPER_PATH}/include/
|
|
||||||
$ENV{CLIPPER_PATH}/include/polyclipping/
|
|
||||||
${PROJECT_SOURCE_DIR}/python/pymesh/third_party/include/
|
|
||||||
${PROJECT_SOURCE_DIR}/python/pymesh/third_party/include/polyclipping/
|
|
||||||
${CMAKE_PREFIX_PATH}/include/polyclipping
|
|
||||||
${CMAKE_PREFIX_PATH}/include/
|
|
||||||
/opt/local/include/
|
|
||||||
/opt/local/include/polyclipping/
|
|
||||||
/usr/local/include/
|
|
||||||
/usr/local/include/polyclipping/
|
|
||||||
/usr/include
|
|
||||||
/usr/include/polyclipping/)
|
|
||||||
|
|
||||||
FIND_LIBRARY(CLIPPER_LIBRARIES polyclipping
|
|
||||||
$ENV{CLIPPER_PATH}
|
|
||||||
$ENV{CLIPPER_PATH}/cpp/
|
|
||||||
$ENV{CLIPPER_PATH}/cpp/build/
|
|
||||||
$ENV{CLIPPER_PATH}/lib/
|
|
||||||
$ENV{CLIPPER_PATH}/lib/polyclipping/
|
|
||||||
${PROJECT_SOURCE_DIR}/python/pymesh/third_party/lib/
|
|
||||||
${PROJECT_SOURCE_DIR}/python/pymesh/third_party/lib/polyclipping/
|
|
||||||
${CMAKE_PREFIX_PATH}/lib/
|
|
||||||
${CMAKE_PREFIX_PATH}/lib/polyclipping/
|
|
||||||
/opt/local/lib/
|
|
||||||
/opt/local/lib/polyclipping/
|
|
||||||
/usr/local/lib/
|
|
||||||
/usr/local/lib/polyclipping/
|
|
||||||
/usr/lib/polyclipping)
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Clipper
|
|
||||||
"Clipper library cannot be found. Consider set CLIPPER_PATH environment variable"
|
|
||||||
CLIPPER_INCLUDE_DIRS
|
|
||||||
CLIPPER_LIBRARIES)
|
|
||||||
|
|
||||||
MARK_AS_ADVANCED(
|
|
||||||
CLIPPER_INCLUDE_DIRS
|
|
||||||
CLIPPER_LIBRARIES)
|
|
||||||
|
|
||||||
if(CLIPPER_FOUND)
|
|
||||||
add_library(Clipper::Clipper INTERFACE IMPORTED)
|
|
||||||
set_target_properties(Clipper::Clipper PROPERTIES INTERFACE_LINK_LIBRARIES ${CLIPPER_LIBRARIES})
|
|
||||||
set_target_properties(Clipper::Clipper PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CLIPPER_INCLUDE_DIRS})
|
|
||||||
#target_link_libraries(Clipper::Clipper INTERFACE ${CLIPPER_LIBRARIES})
|
|
||||||
#target_include_directories(Clipper::Clipper INTERFACE ${CLIPPER_INCLUDE_DIRS})
|
|
||||||
endif()
|
|
@ -1,134 +0,0 @@
|
|||||||
#ifndef LIBNEST2D_H
|
|
||||||
#define LIBNEST2D_H
|
|
||||||
|
|
||||||
// The type of backend should be set conditionally by the cmake configuriation
|
|
||||||
// for now we set it statically to clipper backend
|
|
||||||
#ifdef LIBNEST2D_BACKEND_CLIPPER
|
|
||||||
#include <libnest2d/backends/clipper/geometries.hpp>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef LIBNEST2D_OPTIMIZER_NLOPT
|
|
||||||
// We include the stock optimizers for local and global optimization
|
|
||||||
#include <libnest2d/optimizers/nlopt/subplex.hpp> // Local subplex for NfpPlacer
|
|
||||||
#include <libnest2d/optimizers/nlopt/genetic.hpp> // Genetic for min. bounding box
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libnest2d/libnest2d.hpp>
|
|
||||||
#include <libnest2d/placers/bottomleftplacer.hpp>
|
|
||||||
#include <libnest2d/placers/nfpplacer.hpp>
|
|
||||||
#include <libnest2d/selections/firstfit.hpp>
|
|
||||||
#include <libnest2d/selections/filler.hpp>
|
|
||||||
#include <libnest2d/selections/djd_heuristic.hpp>
|
|
||||||
|
|
||||||
namespace libnest2d {
|
|
||||||
|
|
||||||
using Point = PointImpl;
|
|
||||||
using Coord = TCoord<PointImpl>;
|
|
||||||
using Box = _Box<PointImpl>;
|
|
||||||
using Segment = _Segment<PointImpl>;
|
|
||||||
using Circle = _Circle<PointImpl>;
|
|
||||||
|
|
||||||
using Item = _Item<PolygonImpl>;
|
|
||||||
using Rectangle = _Rectangle<PolygonImpl>;
|
|
||||||
using PackGroup = _PackGroup<PolygonImpl>;
|
|
||||||
|
|
||||||
using FillerSelection = selections::_FillerSelection<PolygonImpl>;
|
|
||||||
using FirstFitSelection = selections::_FirstFitSelection<PolygonImpl>;
|
|
||||||
using DJDHeuristic = selections::_DJDHeuristic<PolygonImpl>;
|
|
||||||
|
|
||||||
template<class Bin> // Generic placer for arbitrary bin types
|
|
||||||
using _NfpPlacer = placers::_NofitPolyPlacer<PolygonImpl, Bin>;
|
|
||||||
|
|
||||||
// NfpPlacer is with Box bin
|
|
||||||
using NfpPlacer = _NfpPlacer<Box>;
|
|
||||||
|
|
||||||
// This supports only box shaped bins
|
|
||||||
using BottomLeftPlacer = placers::_BottomLeftPlacer<PolygonImpl>;
|
|
||||||
|
|
||||||
#ifdef LIBNEST2D_STATIC
|
|
||||||
|
|
||||||
extern template class Nester<NfpPlacer, FirstFitSelection>;
|
|
||||||
extern template class Nester<BottomLeftPlacer, FirstFitSelection>;
|
|
||||||
extern template std::size_t Nester<NfpPlacer, FirstFitSelection>::execute(
|
|
||||||
std::vector<Item>::iterator, std::vector<Item>::iterator);
|
|
||||||
extern template std::size_t Nester<BottomLeftPlacer, FirstFitSelection>::execute(
|
|
||||||
std::vector<Item>::iterator, std::vector<Item>::iterator);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template<class Placer = NfpPlacer, class Selector = FirstFitSelection>
|
|
||||||
struct NestConfig {
|
|
||||||
typename Placer::Config placer_config;
|
|
||||||
typename Selector::Config selector_config;
|
|
||||||
using Placement = typename Placer::Config;
|
|
||||||
using Selection = typename Selector::Config;
|
|
||||||
|
|
||||||
NestConfig() = default;
|
|
||||||
NestConfig(const typename Placer::Config &cfg) : placer_config{cfg} {}
|
|
||||||
NestConfig(const typename Selector::Config &cfg) : selector_config{cfg} {}
|
|
||||||
NestConfig(const typename Placer::Config & pcfg,
|
|
||||||
const typename Selector::Config &scfg)
|
|
||||||
: placer_config{pcfg}, selector_config{scfg} {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NestControl {
|
|
||||||
ProgressFunction progressfn;
|
|
||||||
StopCondition stopcond = []{ return false; };
|
|
||||||
|
|
||||||
NestControl() = default;
|
|
||||||
NestControl(ProgressFunction pr) : progressfn{std::move(pr)} {}
|
|
||||||
NestControl(StopCondition sc) : stopcond{std::move(sc)} {}
|
|
||||||
NestControl(ProgressFunction pr, StopCondition sc)
|
|
||||||
: progressfn{std::move(pr)}, stopcond{std::move(sc)}
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Placer = NfpPlacer,
|
|
||||||
class Selector = FirstFitSelection,
|
|
||||||
class Iterator = std::vector<Item>::iterator>
|
|
||||||
std::size_t nest(Iterator from, Iterator to,
|
|
||||||
const typename Placer::BinType & bin,
|
|
||||||
Coord dist = 0,
|
|
||||||
const NestConfig<Placer, Selector> &cfg = {},
|
|
||||||
NestControl ctl = {})
|
|
||||||
{
|
|
||||||
_Nester<Placer, Selector> nester{bin, dist, cfg.placer_config, cfg.selector_config};
|
|
||||||
if(ctl.progressfn) nester.progressIndicator(ctl.progressfn);
|
|
||||||
if(ctl.stopcond) nester.stopCondition(ctl.stopcond);
|
|
||||||
return nester.execute(from, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef LIBNEST2D_STATIC
|
|
||||||
|
|
||||||
extern template class Nester<NfpPlacer, FirstFitSelection>;
|
|
||||||
extern template class Nester<BottomLeftPlacer, FirstFitSelection>;
|
|
||||||
extern template std::size_t nest(std::vector<Item>::iterator from,
|
|
||||||
std::vector<Item>::iterator from to,
|
|
||||||
const Box & bin,
|
|
||||||
Coord dist,
|
|
||||||
const NestConfig<NfpPlacer, FirstFitSelection> &cfg,
|
|
||||||
NestControl ctl);
|
|
||||||
extern template std::size_t nest(std::vector<Item>::iterator from,
|
|
||||||
std::vector<Item>::iterator from to,
|
|
||||||
const Box & bin,
|
|
||||||
Coord dist,
|
|
||||||
const NestConfig<BottomLeftPlacer, FirstFitSelection> &cfg,
|
|
||||||
NestControl ctl);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template<class Placer = NfpPlacer,
|
|
||||||
class Selector = FirstFitSelection,
|
|
||||||
class Container = std::vector<Item>>
|
|
||||||
std::size_t nest(Container&& cont,
|
|
||||||
const typename Placer::BinType & bin,
|
|
||||||
Coord dist = 0,
|
|
||||||
const NestConfig<Placer, Selector> &cfg = {},
|
|
||||||
NestControl ctl = {})
|
|
||||||
{
|
|
||||||
return nest<Placer, Selector>(cont.begin(), cont.end(), bin, dist, cfg, ctl);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // LIBNEST2D_H
|
|
@ -1,73 +0,0 @@
|
|||||||
if(NOT TARGET clipper) # If there is a clipper target in the parent project we are good to go.
|
|
||||||
|
|
||||||
find_package(Clipper 6.1)
|
|
||||||
|
|
||||||
if(NOT CLIPPER_FOUND)
|
|
||||||
find_package(Subversion QUIET)
|
|
||||||
if(Subversion_FOUND)
|
|
||||||
|
|
||||||
set(URL_CLIPPER "https://svn.code.sf.net/p/polyclipping/code/trunk/cpp"
|
|
||||||
CACHE STRING "Clipper source code repository location.")
|
|
||||||
|
|
||||||
message(STATUS "Clipper not found so it will be downloaded.")
|
|
||||||
# Silently download and build the library in the build dir
|
|
||||||
|
|
||||||
if (CMAKE_VERSION VERSION_LESS 3.2)
|
|
||||||
set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
|
|
||||||
else()
|
|
||||||
set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(DownloadProject)
|
|
||||||
download_project( PROJ clipper_library
|
|
||||||
SVN_REPOSITORY ${URL_CLIPPER}
|
|
||||||
SVN_REVISION -r540
|
|
||||||
#SOURCE_SUBDIR cpp
|
|
||||||
INSTALL_COMMAND ""
|
|
||||||
CONFIGURE_COMMAND "" # Not working, I will just add the source files
|
|
||||||
${UPDATE_DISCONNECTED_IF_AVAILABLE}
|
|
||||||
)
|
|
||||||
|
|
||||||
# This is not working and I dont have time to fix it
|
|
||||||
# add_subdirectory(${clipper_library_SOURCE_DIR}/cpp
|
|
||||||
# ${clipper_library_BINARY_DIR}
|
|
||||||
# )
|
|
||||||
|
|
||||||
add_library(clipperBackend STATIC
|
|
||||||
${clipper_library_SOURCE_DIR}/clipper.cpp
|
|
||||||
${clipper_library_SOURCE_DIR}/clipper.hpp)
|
|
||||||
|
|
||||||
target_include_directories(clipperBackend INTERFACE ${clipper_library_SOURCE_DIR})
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Can't find clipper library and no SVN client found to download.
|
|
||||||
You can download the clipper sources and define a clipper target in your project, that will work for libnest2d.")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
add_library(clipperBackend INTERFACE)
|
|
||||||
target_link_libraries(clipperBackend INTERFACE Clipper::Clipper)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
# set(CLIPPER_INCLUDE_DIRS "" PARENT_SCOPE)
|
|
||||||
# set(CLIPPER_LIBRARIES clipper PARENT_SCOPE)
|
|
||||||
add_library(clipperBackend INTERFACE)
|
|
||||||
target_link_libraries(clipperBackend INTERFACE clipper)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Clipper backend is not enough on its own, it still needs some functions
|
|
||||||
# from Boost geometry
|
|
||||||
if(NOT Boost_FOUND)
|
|
||||||
find_package(Boost 1.58 REQUIRED)
|
|
||||||
# TODO automatic download of boost geometry headers
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(clipperBackend INTERFACE Boost::boost )
|
|
||||||
#target_sources(ClipperBackend INTERFACE
|
|
||||||
# ${CMAKE_CURRENT_SOURCE_DIR}/geometries.hpp
|
|
||||||
# ${CMAKE_CURRENT_SOURCE_DIR}/clipper_polygon.hpp
|
|
||||||
# ${SRC_DIR}/libnest2d/utils/boost_alg.hpp )
|
|
||||||
|
|
||||||
target_compile_definitions(clipperBackend INTERFACE LIBNEST2D_BACKEND_CLIPPER)
|
|
||||||
|
|
||||||
# And finally plug the clipperBackend into libnest2d
|
|
||||||
# target_link_libraries(libnest2d INTERFACE clipperBackend)
|
|
||||||
|
|
@ -299,9 +299,456 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
|
|||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
|
NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
|
||||||
const RawShape& cother)
|
const RawShape& cother)
|
||||||
{
|
{
|
||||||
return {};
|
|
||||||
|
// Algorithms are from the original algorithm proposed in paper:
|
||||||
|
// https://eprints.soton.ac.uk/36850/1/CORMSIS-05-05.pdf
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
// Algorithm 1: Obtaining the minkowski sum
|
||||||
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// I guess this is not a full minkowski sum of the two input polygons by
|
||||||
|
// definition. This yields a subset that is compatible with the next 2
|
||||||
|
// algorithms.
|
||||||
|
|
||||||
|
using Result = NfpResult<RawShape>;
|
||||||
|
using Vertex = TPoint<RawShape>;
|
||||||
|
using Coord = TCoord<Vertex>;
|
||||||
|
using Edge = _Segment<Vertex>;
|
||||||
|
namespace sl = shapelike;
|
||||||
|
using std::signbit;
|
||||||
|
using std::sort;
|
||||||
|
using std::vector;
|
||||||
|
using std::ref;
|
||||||
|
using std::reference_wrapper;
|
||||||
|
|
||||||
|
// TODO The original algorithms expects the stationary polygon in
|
||||||
|
// counter clockwise and the orbiter in clockwise order.
|
||||||
|
// So for preventing any further complication, I will make the input
|
||||||
|
// the way it should be, than make my way around the orientations.
|
||||||
|
|
||||||
|
// Reverse the stationary contour to counter clockwise
|
||||||
|
auto stcont = sl::contour(cstationary);
|
||||||
|
{
|
||||||
|
std::reverse(sl::begin(stcont), sl::end(stcont));
|
||||||
|
stcont.pop_back();
|
||||||
|
auto it = std::min_element(sl::begin(stcont), sl::end(stcont),
|
||||||
|
[](const Vertex& v1, const Vertex& v2) {
|
||||||
|
return getY(v1) < getY(v2);
|
||||||
|
});
|
||||||
|
std::rotate(sl::begin(stcont), it, sl::end(stcont));
|
||||||
|
sl::addVertex(stcont, sl::front(stcont));
|
||||||
|
}
|
||||||
|
RawShape stationary;
|
||||||
|
sl::contour(stationary) = stcont;
|
||||||
|
|
||||||
|
// Reverse the orbiter contour to counter clockwise
|
||||||
|
auto orbcont = sl::contour(cother);
|
||||||
|
{
|
||||||
|
std::reverse(orbcont.begin(), orbcont.end());
|
||||||
|
|
||||||
|
// Step 1: Make the orbiter reverse oriented
|
||||||
|
|
||||||
|
orbcont.pop_back();
|
||||||
|
auto it = std::min_element(orbcont.begin(), orbcont.end(),
|
||||||
|
[](const Vertex& v1, const Vertex& v2) {
|
||||||
|
return getY(v1) < getY(v2);
|
||||||
|
});
|
||||||
|
|
||||||
|
std::rotate(orbcont.begin(), it, orbcont.end());
|
||||||
|
orbcont.emplace_back(orbcont.front());
|
||||||
|
|
||||||
|
for(auto &v : orbcont) v = -v;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the orbiter (contour only), we will have to work on it
|
||||||
|
RawShape orbiter;
|
||||||
|
sl::contour(orbiter) = orbcont;
|
||||||
|
|
||||||
|
// An edge with additional data for marking it
|
||||||
|
struct MarkedEdge {
|
||||||
|
Edge e; Radians turn_angle = 0; bool is_turning_point = false;
|
||||||
|
MarkedEdge() = default;
|
||||||
|
MarkedEdge(const Edge& ed, Radians ta, bool tp):
|
||||||
|
e(ed), turn_angle(ta), is_turning_point(tp) {}
|
||||||
|
|
||||||
|
// debug
|
||||||
|
std::string label;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Container for marked edges
|
||||||
|
using EdgeList = vector<MarkedEdge>;
|
||||||
|
|
||||||
|
EdgeList A, B;
|
||||||
|
|
||||||
|
// This is how an edge list is created from the polygons
|
||||||
|
auto fillEdgeList = [](EdgeList& L, const RawShape& ppoly, int dir) {
|
||||||
|
auto& poly = sl::contour(ppoly);
|
||||||
|
|
||||||
|
L.reserve(sl::contourVertexCount(poly));
|
||||||
|
|
||||||
|
if(dir > 0) {
|
||||||
|
auto it = poly.begin();
|
||||||
|
auto nextit = std::next(it);
|
||||||
|
|
||||||
|
double turn_angle = 0;
|
||||||
|
bool is_turn_point = false;
|
||||||
|
|
||||||
|
while(nextit != poly.end()) {
|
||||||
|
L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point);
|
||||||
|
it++; nextit++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto it = sl::rbegin(poly);
|
||||||
|
auto nextit = std::next(it);
|
||||||
|
|
||||||
|
double turn_angle = 0;
|
||||||
|
bool is_turn_point = false;
|
||||||
|
|
||||||
|
while(nextit != sl::rend(poly)) {
|
||||||
|
L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point);
|
||||||
|
it++; nextit++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getTurnAngle = [](const Edge& e1, const Edge& e2) {
|
||||||
|
auto phi = e1.angleToXaxis();
|
||||||
|
auto phi_prev = e2.angleToXaxis();
|
||||||
|
auto turn_angle = phi-phi_prev;
|
||||||
|
if(turn_angle > Pi) turn_angle -= TwoPi;
|
||||||
|
if(turn_angle < -Pi) turn_angle += TwoPi;
|
||||||
|
return turn_angle;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto eit = L.begin();
|
||||||
|
auto enext = std::next(eit);
|
||||||
|
|
||||||
|
eit->turn_angle = getTurnAngle(L.front().e, L.back().e);
|
||||||
|
|
||||||
|
while(enext != L.end()) {
|
||||||
|
enext->turn_angle = getTurnAngle( enext->e, eit->e);
|
||||||
|
eit->is_turning_point =
|
||||||
|
signbit(enext->turn_angle) != signbit(eit->turn_angle);
|
||||||
|
++eit; ++enext;
|
||||||
|
}
|
||||||
|
|
||||||
|
L.back().is_turning_point = signbit(L.back().turn_angle) !=
|
||||||
|
signbit(L.front().turn_angle);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 2: Fill the edgelists
|
||||||
|
fillEdgeList(A, stationary, 1);
|
||||||
|
fillEdgeList(B, orbiter, 1);
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
for(MarkedEdge& me : A) {
|
||||||
|
std::cout << "a" << i << ":\n\t"
|
||||||
|
<< getX(me.e.first()) << " " << getY(me.e.first()) << "\n\t"
|
||||||
|
<< getX(me.e.second()) << " " << getY(me.e.second()) << "\n\t"
|
||||||
|
<< "Turning point: " << (me.is_turning_point ? "yes" : "no")
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
me.label = "a"; me.label += std::to_string(i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 1;
|
||||||
|
for(MarkedEdge& me : B) {
|
||||||
|
std::cout << "b" << i << ":\n\t"
|
||||||
|
<< getX(me.e.first()) << " " << getY(me.e.first()) << "\n\t"
|
||||||
|
<< getX(me.e.second()) << " " << getY(me.e.second()) << "\n\t"
|
||||||
|
<< "Turning point: " << (me.is_turning_point ? "yes" : "no")
|
||||||
|
<< std::endl;
|
||||||
|
me.label = "b"; me.label += std::to_string(i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A reference to a marked edge that also knows its container
|
||||||
|
struct MarkedEdgeRef {
|
||||||
|
reference_wrapper<MarkedEdge> eref;
|
||||||
|
reference_wrapper<vector<MarkedEdgeRef>> container;
|
||||||
|
Coord dir = 1; // Direction modifier
|
||||||
|
|
||||||
|
inline Radians angleX() const { return eref.get().e.angleToXaxis(); }
|
||||||
|
inline const Edge& edge() const { return eref.get().e; }
|
||||||
|
inline Edge& edge() { return eref.get().e; }
|
||||||
|
inline bool isTurningPoint() const {
|
||||||
|
return eref.get().is_turning_point;
|
||||||
|
}
|
||||||
|
inline bool isFrom(const vector<MarkedEdgeRef>& cont ) {
|
||||||
|
return &(container.get()) == &cont;
|
||||||
|
}
|
||||||
|
inline bool eq(const MarkedEdgeRef& mr) {
|
||||||
|
return &(eref.get()) == &(mr.eref.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
|
||||||
|
reference_wrapper<vector<MarkedEdgeRef>> ec):
|
||||||
|
eref(er), container(ec), dir(1) {}
|
||||||
|
|
||||||
|
MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
|
||||||
|
reference_wrapper<vector<MarkedEdgeRef>> ec,
|
||||||
|
Coord d):
|
||||||
|
eref(er), container(ec), dir(d) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
using EdgeRefList = vector<MarkedEdgeRef>;
|
||||||
|
|
||||||
|
// Comparing two marked edges
|
||||||
|
auto sortfn = [](const MarkedEdgeRef& e1, const MarkedEdgeRef& e2) {
|
||||||
|
return e1.angleX() < e2.angleX();
|
||||||
|
};
|
||||||
|
|
||||||
|
EdgeRefList Aref, Bref; // We create containers for the references
|
||||||
|
Aref.reserve(A.size()); Bref.reserve(B.size());
|
||||||
|
|
||||||
|
// Fill reference container for the stationary polygon
|
||||||
|
std::for_each(A.begin(), A.end(), [&Aref](MarkedEdge& me) {
|
||||||
|
Aref.emplace_back( ref(me), ref(Aref) );
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fill reference container for the orbiting polygon
|
||||||
|
std::for_each(B.begin(), B.end(), [&Bref](MarkedEdge& me) {
|
||||||
|
Bref.emplace_back( ref(me), ref(Bref) );
|
||||||
|
});
|
||||||
|
|
||||||
|
auto mink = [sortfn] // the Mink(Q, R, direction) sub-procedure
|
||||||
|
(const EdgeRefList& Q, const EdgeRefList& R, bool positive)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Step 1 "merge sort_list(Q) and sort_list(R) to form merge_list(Q,R)"
|
||||||
|
// Sort the containers of edge references and merge them.
|
||||||
|
// Q could be sorted only once and be reused here but we would still
|
||||||
|
// need to merge it with sorted(R).
|
||||||
|
|
||||||
|
EdgeRefList merged;
|
||||||
|
EdgeRefList S, seq;
|
||||||
|
merged.reserve(Q.size() + R.size());
|
||||||
|
|
||||||
|
merged.insert(merged.end(), R.begin(), R.end());
|
||||||
|
std::stable_sort(merged.begin(), merged.end(), sortfn);
|
||||||
|
merged.insert(merged.end(), Q.begin(), Q.end());
|
||||||
|
std::stable_sort(merged.begin(), merged.end(), sortfn);
|
||||||
|
|
||||||
|
// Step 2 "set i = 1, k = 1, direction = 1, s1 = q1"
|
||||||
|
// we don't use i, instead, q is an iterator into Q. k would be an index
|
||||||
|
// into the merged sequence but we use "it" as an iterator for that
|
||||||
|
|
||||||
|
// here we obtain references for the containers for later comparisons
|
||||||
|
const auto& Rcont = R.begin()->container.get();
|
||||||
|
const auto& Qcont = Q.begin()->container.get();
|
||||||
|
|
||||||
|
// Set the initial direction
|
||||||
|
Coord dir = 1;
|
||||||
|
|
||||||
|
// roughly i = 1 (so q = Q.begin()) and s1 = q1 so S[0] = q;
|
||||||
|
if(positive) {
|
||||||
|
auto q = Q.begin();
|
||||||
|
S.emplace_back(*q);
|
||||||
|
|
||||||
|
// Roughly step 3
|
||||||
|
|
||||||
|
std::cout << "merged size: " << merged.size() << std::endl;
|
||||||
|
auto mit = merged.begin();
|
||||||
|
for(bool finish = false; !finish && q != Q.end();) {
|
||||||
|
++q; // "Set i = i + 1"
|
||||||
|
|
||||||
|
while(!finish && mit != merged.end()) {
|
||||||
|
if(mit->isFrom(Rcont)) {
|
||||||
|
auto s = *mit;
|
||||||
|
s.dir = dir;
|
||||||
|
S.emplace_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mit->eq(*q)) {
|
||||||
|
S.emplace_back(*q);
|
||||||
|
if(mit->isTurningPoint()) dir = -dir;
|
||||||
|
if(q == Q.begin()) finish = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mit += dir;
|
||||||
|
// __nfp::advance(mit, merged, dir > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto q = Q.rbegin();
|
||||||
|
S.emplace_back(*q);
|
||||||
|
|
||||||
|
// Roughly step 3
|
||||||
|
|
||||||
|
std::cout << "merged size: " << merged.size() << std::endl;
|
||||||
|
auto mit = merged.begin();
|
||||||
|
for(bool finish = false; !finish && q != Q.rend();) {
|
||||||
|
++q; // "Set i = i + 1"
|
||||||
|
|
||||||
|
while(!finish && mit != merged.end()) {
|
||||||
|
if(mit->isFrom(Rcont)) {
|
||||||
|
auto s = *mit;
|
||||||
|
s.dir = dir;
|
||||||
|
S.emplace_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mit->eq(*q)) {
|
||||||
|
S.emplace_back(*q);
|
||||||
|
S.back().dir = -1;
|
||||||
|
if(mit->isTurningPoint()) dir = -dir;
|
||||||
|
if(q == Q.rbegin()) finish = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mit += dir;
|
||||||
|
// __nfp::advance(mit, merged, dir > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Step 4:
|
||||||
|
|
||||||
|
// "Let starting edge r1 be in position si in sequence"
|
||||||
|
// whaaat? I guess this means the following:
|
||||||
|
auto it = S.begin();
|
||||||
|
while(!it->eq(*R.begin())) ++it;
|
||||||
|
|
||||||
|
// "Set j = 1, next = 2, direction = 1, seq1 = si"
|
||||||
|
// we don't use j, seq is expanded dynamically.
|
||||||
|
dir = 1;
|
||||||
|
auto next = std::next(R.begin()); seq.emplace_back(*it);
|
||||||
|
|
||||||
|
// Step 5:
|
||||||
|
// "If all si edges have been allocated to seqj" should mean that
|
||||||
|
// we loop until seq has equal size with S
|
||||||
|
auto send = it; //it == S.begin() ? it : std::prev(it);
|
||||||
|
while(it != S.end()) {
|
||||||
|
++it; if(it == S.end()) it = S.begin();
|
||||||
|
if(it == send) break;
|
||||||
|
|
||||||
|
if(it->isFrom(Qcont)) {
|
||||||
|
seq.emplace_back(*it); // "If si is from Q, j = j + 1, seqj = si"
|
||||||
|
|
||||||
|
// "If si is a turning point in Q,
|
||||||
|
// direction = - direction, next = next + direction"
|
||||||
|
if(it->isTurningPoint()) {
|
||||||
|
dir = -dir;
|
||||||
|
next += dir;
|
||||||
|
// __nfp::advance(next, R, dir > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(it->eq(*next) /*&& dir == next->dir*/) { // "If si = direction.rnext"
|
||||||
|
// "j = j + 1, seqj = si, next = next + direction"
|
||||||
|
seq.emplace_back(*it);
|
||||||
|
next += dir;
|
||||||
|
// __nfp::advance(next, R, dir > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return seq;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<EdgeRefList> seqlist;
|
||||||
|
seqlist.reserve(Bref.size());
|
||||||
|
|
||||||
|
EdgeRefList Bslope = Bref; // copy Bref, we will make a slope diagram
|
||||||
|
|
||||||
|
// make the slope diagram of B
|
||||||
|
std::sort(Bslope.begin(), Bslope.end(), sortfn);
|
||||||
|
|
||||||
|
auto slopeit = Bslope.begin(); // search for the first turning point
|
||||||
|
while(!slopeit->isTurningPoint() && slopeit != Bslope.end()) slopeit++;
|
||||||
|
|
||||||
|
if(slopeit == Bslope.end()) {
|
||||||
|
// no turning point means convex polygon.
|
||||||
|
seqlist.emplace_back(mink(Aref, Bref, true));
|
||||||
|
} else {
|
||||||
|
int dir = 1;
|
||||||
|
|
||||||
|
auto firstturn = Bref.begin();
|
||||||
|
while(!firstturn->eq(*slopeit)) ++firstturn;
|
||||||
|
|
||||||
|
assert(firstturn != Bref.end());
|
||||||
|
|
||||||
|
EdgeRefList bgroup; bgroup.reserve(Bref.size());
|
||||||
|
bgroup.emplace_back(*slopeit);
|
||||||
|
|
||||||
|
auto b_it = std::next(firstturn);
|
||||||
|
while(b_it != firstturn) {
|
||||||
|
if(b_it == Bref.end()) b_it = Bref.begin();
|
||||||
|
|
||||||
|
while(!slopeit->eq(*b_it)) {
|
||||||
|
__nfp::advance(slopeit, Bslope, dir > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!slopeit->isTurningPoint()) {
|
||||||
|
bgroup.emplace_back(*slopeit);
|
||||||
|
} else {
|
||||||
|
if(!bgroup.empty()) {
|
||||||
|
if(dir > 0) bgroup.emplace_back(*slopeit);
|
||||||
|
for(auto& me : bgroup) {
|
||||||
|
std::cout << me.eref.get().label << ", ";
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
seqlist.emplace_back(mink(Aref, bgroup, dir == 1 ? true : false));
|
||||||
|
bgroup.clear();
|
||||||
|
if(dir < 0) bgroup.emplace_back(*slopeit);
|
||||||
|
} else {
|
||||||
|
bgroup.emplace_back(*slopeit);
|
||||||
|
}
|
||||||
|
|
||||||
|
dir *= -1;
|
||||||
|
}
|
||||||
|
++b_it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// while(it != Bref.end()) // This is step 3 and step 4 in one loop
|
||||||
|
// if(it->isTurningPoint()) {
|
||||||
|
// R = {R.last, it++};
|
||||||
|
// auto seq = mink(Q, R, orientation);
|
||||||
|
|
||||||
|
// // TODO step 6 (should be 5 shouldn't it?): linking edges from A
|
||||||
|
// // I don't get this step
|
||||||
|
|
||||||
|
// seqlist.insert(seqlist.end(), seq.begin(), seq.end());
|
||||||
|
// orientation = !orientation;
|
||||||
|
// } else ++it;
|
||||||
|
|
||||||
|
// if(seqlist.empty()) seqlist = mink(Q, {Bref.begin(), Bref.end()}, true);
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
// Algorithm 2: breaking Minkowski sums into track line trips
|
||||||
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
// Algorithm 3: finding the boundary of the NFP from track line trips
|
||||||
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
for(auto& seq : seqlist) {
|
||||||
|
std::cout << "seqlist size: " << seq.size() << std::endl;
|
||||||
|
for(auto& s : seq) {
|
||||||
|
std::cout << (s.dir > 0 ? "" : "-") << s.eref.get().label << ", ";
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& seq = seqlist.front();
|
||||||
|
RawShape rsh;
|
||||||
|
Vertex top_nfp;
|
||||||
|
std::vector<Edge> edgelist; edgelist.reserve(seq.size());
|
||||||
|
for(auto& s : seq) {
|
||||||
|
edgelist.emplace_back(s.eref.get().e);
|
||||||
|
}
|
||||||
|
|
||||||
|
__nfp::buildPolygon(edgelist, rsh, top_nfp);
|
||||||
|
|
||||||
|
return Result(rsh, top_nfp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specializable NFP implementation class. Specialize it if you have a faster
|
// Specializable NFP implementation class. Specialize it if you have a faster
|
||||||
|
@ -1,869 +1,134 @@
|
|||||||
#ifndef LIBNEST2D_HPP
|
#ifndef LIBNEST2D_HPP
|
||||||
#define LIBNEST2D_HPP
|
#define LIBNEST2D_HPP
|
||||||
|
|
||||||
#include <memory>
|
// The type of backend should be set conditionally by the cmake configuriation
|
||||||
#include <vector>
|
// for now we set it statically to clipper backend
|
||||||
#include <map>
|
#ifdef LIBNEST2D_GEOMETRIES_clipper
|
||||||
#include <array>
|
#include <libnest2d/backends/clipper/geometries.hpp>
|
||||||
#include <algorithm>
|
#endif
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include <libnest2d/geometry_traits.hpp>
|
#ifdef LIBNEST2D_OPTIMIZER_nlopt
|
||||||
|
// We include the stock optimizers for local and global optimization
|
||||||
|
#include <libnest2d/optimizers/nlopt/subplex.hpp> // Local subplex for NfpPlacer
|
||||||
|
#include <libnest2d/optimizers/nlopt/genetic.hpp> // Genetic for min. bounding box
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <libnest2d/nester.hpp>
|
||||||
|
#include <libnest2d/placers/bottomleftplacer.hpp>
|
||||||
|
#include <libnest2d/placers/nfpplacer.hpp>
|
||||||
|
#include <libnest2d/selections/firstfit.hpp>
|
||||||
|
#include <libnest2d/selections/filler.hpp>
|
||||||
|
#include <libnest2d/selections/djd_heuristic.hpp>
|
||||||
|
|
||||||
namespace libnest2d {
|
namespace libnest2d {
|
||||||
|
|
||||||
static const constexpr int BIN_ID_UNSET = -1;
|
using Point = PointImpl;
|
||||||
|
using Coord = TCoord<PointImpl>;
|
||||||
/**
|
using Box = _Box<PointImpl>;
|
||||||
* \brief An item to be placed on a bin.
|
using Segment = _Segment<PointImpl>;
|
||||||
*
|
using Circle = _Circle<PointImpl>;
|
||||||
* It holds a copy of the original shape object but supports move construction
|
|
||||||
* from the shape objects if its an rvalue reference. This way we can construct
|
using Item = _Item<PolygonImpl>;
|
||||||
* the items without the cost of copying a potentially large amount of input.
|
using Rectangle = _Rectangle<PolygonImpl>;
|
||||||
*
|
using PackGroup = _PackGroup<PolygonImpl>;
|
||||||
* The results of some calculations are cached for maintaining fast run times.
|
|
||||||
* For this reason, memory demands are much higher but this should pay off.
|
using FillerSelection = selections::_FillerSelection<PolygonImpl>;
|
||||||
*/
|
using FirstFitSelection = selections::_FirstFitSelection<PolygonImpl>;
|
||||||
template<class RawShape>
|
using DJDHeuristic = selections::_DJDHeuristic<PolygonImpl>;
|
||||||
class _Item {
|
|
||||||
using Coord = TCoord<TPoint<RawShape>>;
|
template<class Bin> // Generic placer for arbitrary bin types
|
||||||
using Vertex = TPoint<RawShape>;
|
using _NfpPlacer = placers::_NofitPolyPlacer<PolygonImpl, Bin>;
|
||||||
using Box = _Box<Vertex>;
|
|
||||||
|
// NfpPlacer is with Box bin
|
||||||
using VertexConstIterator = typename TContour<RawShape>::const_iterator;
|
using NfpPlacer = _NfpPlacer<Box>;
|
||||||
|
|
||||||
// The original shape that gets encapsulated.
|
// This supports only box shaped bins
|
||||||
RawShape sh_;
|
using BottomLeftPlacer = placers::_BottomLeftPlacer<PolygonImpl>;
|
||||||
|
|
||||||
// Transformation data
|
#ifdef LIBNEST2D_STATIC
|
||||||
Vertex translation_{0, 0};
|
|
||||||
Radians rotation_{0.0};
|
extern template class _Nester<NfpPlacer, FirstFitSelection>;
|
||||||
Coord inflation_{0};
|
extern template class _Nester<BottomLeftPlacer, FirstFitSelection>;
|
||||||
|
extern template std::size_t _Nester<NfpPlacer, FirstFitSelection>::execute(
|
||||||
// Info about whether the transformations will have to take place
|
std::vector<Item>::iterator, std::vector<Item>::iterator);
|
||||||
// This is needed because if floating point is used, it is hard to say
|
extern template std::size_t _Nester<BottomLeftPlacer, FirstFitSelection>::execute(
|
||||||
// that a zero angle is not a rotation because of testing for equality.
|
std::vector<Item>::iterator, std::vector<Item>::iterator);
|
||||||
bool has_rotation_ = false, has_translation_ = false, has_inflation_ = false;
|
|
||||||
|
#endif
|
||||||
// For caching the calculations as they can get pretty expensive.
|
|
||||||
mutable RawShape tr_cache_;
|
template<class Placer = NfpPlacer, class Selector = FirstFitSelection>
|
||||||
mutable bool tr_cache_valid_ = false;
|
struct NestConfig {
|
||||||
mutable double area_cache_ = 0;
|
typename Placer::Config placer_config;
|
||||||
mutable bool area_cache_valid_ = false;
|
typename Selector::Config selector_config;
|
||||||
mutable RawShape inflate_cache_;
|
using Placement = typename Placer::Config;
|
||||||
mutable bool inflate_cache_valid_ = false;
|
using Selection = typename Selector::Config;
|
||||||
|
|
||||||
enum class Convexity: char {
|
NestConfig() = default;
|
||||||
UNCHECKED,
|
NestConfig(const typename Placer::Config &cfg) : placer_config{cfg} {}
|
||||||
C_TRUE,
|
NestConfig(const typename Selector::Config &cfg) : selector_config{cfg} {}
|
||||||
C_FALSE
|
NestConfig(const typename Placer::Config & pcfg,
|
||||||
};
|
const typename Selector::Config &scfg)
|
||||||
|
: placer_config{pcfg}, selector_config{scfg} {}
|
||||||
mutable Convexity convexity_ = Convexity::UNCHECKED;
|
|
||||||
mutable VertexConstIterator rmt_; // rightmost top vertex
|
|
||||||
mutable VertexConstIterator lmb_; // leftmost bottom vertex
|
|
||||||
mutable bool rmt_valid_ = false, lmb_valid_ = false;
|
|
||||||
mutable struct BBCache {
|
|
||||||
Box bb; bool valid;
|
|
||||||
BBCache(): valid(false) {}
|
|
||||||
} bb_cache_;
|
|
||||||
|
|
||||||
int binid_{BIN_ID_UNSET}, priority_{0};
|
|
||||||
bool fixed_{false};
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// The type of the shape which was handed over as the template argument.
|
|
||||||
using ShapeType = RawShape;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Iterator type for the outer vertices.
|
|
||||||
*
|
|
||||||
* Only const iterators can be used. The _Item type is not intended to
|
|
||||||
* modify the carried shapes from the outside. The main purpose of this type
|
|
||||||
* is to cache the calculation results from the various operators it
|
|
||||||
* supports. Giving out a non const iterator would make it impossible to
|
|
||||||
* perform correct cache invalidation.
|
|
||||||
*/
|
|
||||||
using Iterator = VertexConstIterator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the orientation of the polygon.
|
|
||||||
*
|
|
||||||
* The orientation have to be specified as a specialization of the
|
|
||||||
* OrientationType struct which has a Value constant.
|
|
||||||
*
|
|
||||||
* @return The orientation type identifier for the _Item type.
|
|
||||||
*/
|
|
||||||
static BP2D_CONSTEXPR Orientation orientation() {
|
|
||||||
return OrientationType<RawShape>::Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Constructing an _Item form an existing raw shape. The shape will
|
|
||||||
* be copied into the _Item object.
|
|
||||||
* @param sh The original shape object.
|
|
||||||
*/
|
|
||||||
explicit inline _Item(const RawShape& sh): sh_(sh) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construction of an item by moving the content of the raw shape,
|
|
||||||
* assuming that it supports move semantics.
|
|
||||||
* @param sh The original shape object.
|
|
||||||
*/
|
|
||||||
explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create an item from an initializer list.
|
|
||||||
* @param il The initializer list of vertices.
|
|
||||||
*/
|
|
||||||
inline _Item(const std::initializer_list< Vertex >& il):
|
|
||||||
sh_(sl::create<RawShape>(il)) {}
|
|
||||||
|
|
||||||
inline _Item(const TContour<RawShape>& contour,
|
|
||||||
const THolesContainer<RawShape>& holes = {}):
|
|
||||||
sh_(sl::create<RawShape>(contour, holes)) {}
|
|
||||||
|
|
||||||
inline _Item(TContour<RawShape>&& contour,
|
|
||||||
THolesContainer<RawShape>&& holes):
|
|
||||||
sh_(sl::create<RawShape>(std::move(contour), std::move(holes))) {}
|
|
||||||
|
|
||||||
inline bool isFixed() const noexcept { return fixed_; }
|
|
||||||
inline void markAsFixedInBin(int binid)
|
|
||||||
{
|
|
||||||
fixed_ = binid >= 0;
|
|
||||||
binid_ = binid;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void binId(int idx) { binid_ = idx; }
|
|
||||||
inline int binId() const noexcept { return binid_; }
|
|
||||||
|
|
||||||
inline void priority(int p) { priority_ = p; }
|
|
||||||
inline int priority() const noexcept { return priority_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Convert the polygon to string representation. The format depends
|
|
||||||
* on the implementation of the polygon.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
inline std::string toString() const
|
|
||||||
{
|
|
||||||
return sl::toString(sh_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator tho the first contour vertex in the polygon.
|
|
||||||
inline Iterator begin() const
|
|
||||||
{
|
|
||||||
return sl::cbegin(sh_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias to begin()
|
|
||||||
inline Iterator cbegin() const
|
|
||||||
{
|
|
||||||
return sl::cbegin(sh_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator to the last contour vertex.
|
|
||||||
inline Iterator end() const
|
|
||||||
{
|
|
||||||
return sl::cend(sh_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias to end()
|
|
||||||
inline Iterator cend() const
|
|
||||||
{
|
|
||||||
return sl::cend(sh_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get a copy of an outer vertex within the carried shape.
|
|
||||||
*
|
|
||||||
* Note that the vertex considered here is taken from the original shape
|
|
||||||
* that this item is constructed from. This means that no transformation is
|
|
||||||
* applied to the shape in this call.
|
|
||||||
*
|
|
||||||
* @param idx The index of the requested vertex.
|
|
||||||
* @return A copy of the requested vertex.
|
|
||||||
*/
|
|
||||||
inline Vertex vertex(unsigned long idx) const
|
|
||||||
{
|
|
||||||
return sl::vertex(sh_, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Modify a vertex.
|
|
||||||
*
|
|
||||||
* Note that this method will invalidate every cached calculation result
|
|
||||||
* including polygon offset and transformations.
|
|
||||||
*
|
|
||||||
* @param idx The index of the requested vertex.
|
|
||||||
* @param v The new vertex data.
|
|
||||||
*/
|
|
||||||
inline void setVertex(unsigned long idx, const Vertex& v )
|
|
||||||
{
|
|
||||||
invalidateCache();
|
|
||||||
sl::vertex(sh_, idx) = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate the shape area.
|
|
||||||
*
|
|
||||||
* The method returns absolute value and does not reflect polygon
|
|
||||||
* orientation. The result is cached, subsequent calls will have very little
|
|
||||||
* cost.
|
|
||||||
* @return The shape area in floating point double precision.
|
|
||||||
*/
|
|
||||||
inline double area() const {
|
|
||||||
double ret ;
|
|
||||||
if(area_cache_valid_) ret = area_cache_;
|
|
||||||
else {
|
|
||||||
ret = sl::area(infaltedShape());
|
|
||||||
area_cache_ = ret;
|
|
||||||
area_cache_valid_ = true;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool isContourConvex() const {
|
|
||||||
bool ret = false;
|
|
||||||
|
|
||||||
switch(convexity_) {
|
|
||||||
case Convexity::UNCHECKED:
|
|
||||||
ret = sl::isConvex(sl::contour(transformedShape()));
|
|
||||||
convexity_ = ret? Convexity::C_TRUE : Convexity::C_FALSE;
|
|
||||||
break;
|
|
||||||
case Convexity::C_TRUE: ret = true; break;
|
|
||||||
case Convexity::C_FALSE:;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool isHoleConvex(unsigned /*holeidx*/) const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool areHolesConvex() const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The number of the outer ring vertices.
|
|
||||||
inline size_t vertexCount() const {
|
|
||||||
return sl::contourVertexCount(sh_);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t holeCount() const {
|
|
||||||
return sl::holeCount(sh_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief isPointInside
|
|
||||||
* @param p
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
inline bool isInside(const Vertex& p) const
|
|
||||||
{
|
|
||||||
return sl::isInside(p, transformedShape());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool isInside(const _Item& sh) const
|
|
||||||
{
|
|
||||||
return sl::isInside(transformedShape(), sh.transformedShape());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool isInside(const RawShape& sh) const
|
|
||||||
{
|
|
||||||
return sl::isInside(transformedShape(), sh);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool isInside(const _Box<TPoint<RawShape>>& box) const;
|
|
||||||
inline bool isInside(const _Circle<TPoint<RawShape>>& box) const;
|
|
||||||
|
|
||||||
inline void translate(const Vertex& d) BP2D_NOEXCEPT
|
|
||||||
{
|
|
||||||
translation(translation() + d);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void rotate(const Radians& rads) BP2D_NOEXCEPT
|
|
||||||
{
|
|
||||||
rotation(rotation() + rads);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void inflation(Coord distance) BP2D_NOEXCEPT
|
|
||||||
{
|
|
||||||
inflation_ = distance;
|
|
||||||
has_inflation_ = true;
|
|
||||||
invalidateCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Coord inflation() const BP2D_NOEXCEPT {
|
|
||||||
return inflation_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void inflate(Coord distance) BP2D_NOEXCEPT
|
|
||||||
{
|
|
||||||
inflation(inflation() + distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Radians rotation() const BP2D_NOEXCEPT
|
|
||||||
{
|
|
||||||
return rotation_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline TPoint<RawShape> translation() const BP2D_NOEXCEPT
|
|
||||||
{
|
|
||||||
return translation_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void rotation(Radians rot) BP2D_NOEXCEPT
|
|
||||||
{
|
|
||||||
if(rotation_ != rot) {
|
|
||||||
rotation_ = rot; has_rotation_ = true; tr_cache_valid_ = false;
|
|
||||||
rmt_valid_ = false; lmb_valid_ = false;
|
|
||||||
bb_cache_.valid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void translation(const TPoint<RawShape>& tr) BP2D_NOEXCEPT
|
|
||||||
{
|
|
||||||
if(translation_ != tr) {
|
|
||||||
translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
|
|
||||||
//bb_cache_.valid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const RawShape& transformedShape() const
|
|
||||||
{
|
|
||||||
if(tr_cache_valid_) return tr_cache_;
|
|
||||||
|
|
||||||
RawShape cpy = infaltedShape();
|
|
||||||
if(has_rotation_) sl::rotate(cpy, rotation_);
|
|
||||||
if(has_translation_) sl::translate(cpy, translation_);
|
|
||||||
tr_cache_ = cpy; tr_cache_valid_ = true;
|
|
||||||
rmt_valid_ = false; lmb_valid_ = false;
|
|
||||||
|
|
||||||
return tr_cache_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline operator RawShape() const
|
|
||||||
{
|
|
||||||
return transformedShape();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const RawShape& rawShape() const BP2D_NOEXCEPT
|
|
||||||
{
|
|
||||||
return sh_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void resetTransformation() BP2D_NOEXCEPT
|
|
||||||
{
|
|
||||||
has_translation_ = false; has_rotation_ = false; has_inflation_ = false;
|
|
||||||
invalidateCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Box boundingBox() const {
|
|
||||||
if(!bb_cache_.valid) {
|
|
||||||
if(!has_rotation_)
|
|
||||||
bb_cache_.bb = sl::boundingBox(infaltedShape());
|
|
||||||
else {
|
|
||||||
// TODO make sure this works
|
|
||||||
auto rotsh = infaltedShape();
|
|
||||||
sl::rotate(rotsh, rotation_);
|
|
||||||
bb_cache_.bb = sl::boundingBox(rotsh);
|
|
||||||
}
|
|
||||||
bb_cache_.valid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto &bb = bb_cache_.bb; auto &tr = translation_;
|
|
||||||
return {bb.minCorner() + tr, bb.maxCorner() + tr };
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Vertex referenceVertex() const {
|
|
||||||
return rightmostTopVertex();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Vertex rightmostTopVertex() const {
|
|
||||||
if(!rmt_valid_ || !tr_cache_valid_) { // find max x and max y vertex
|
|
||||||
auto& tsh = transformedShape();
|
|
||||||
rmt_ = std::max_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
|
|
||||||
rmt_valid_ = true;
|
|
||||||
}
|
|
||||||
return *rmt_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Vertex leftmostBottomVertex() const {
|
|
||||||
if(!lmb_valid_ || !tr_cache_valid_) { // find min x and min y vertex
|
|
||||||
auto& tsh = transformedShape();
|
|
||||||
lmb_ = std::min_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
|
|
||||||
lmb_valid_ = true;
|
|
||||||
}
|
|
||||||
return *lmb_;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Static methods:
|
|
||||||
|
|
||||||
inline static bool intersects(const _Item& sh1, const _Item& sh2)
|
|
||||||
{
|
|
||||||
return sl::intersects(sh1.transformedShape(),
|
|
||||||
sh2.transformedShape());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static bool touches(const _Item& sh1, const _Item& sh2)
|
|
||||||
{
|
|
||||||
return sl::touches(sh1.transformedShape(),
|
|
||||||
sh2.transformedShape());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
inline const RawShape& infaltedShape() const {
|
|
||||||
if(has_inflation_ ) {
|
|
||||||
if(inflate_cache_valid_) return inflate_cache_;
|
|
||||||
|
|
||||||
inflate_cache_ = sh_;
|
|
||||||
sl::offset(inflate_cache_, inflation_);
|
|
||||||
inflate_cache_valid_ = true;
|
|
||||||
return inflate_cache_;
|
|
||||||
}
|
|
||||||
return sh_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void invalidateCache() const BP2D_NOEXCEPT
|
|
||||||
{
|
|
||||||
tr_cache_valid_ = false;
|
|
||||||
lmb_valid_ = false; rmt_valid_ = false;
|
|
||||||
area_cache_valid_ = false;
|
|
||||||
inflate_cache_valid_ = false;
|
|
||||||
bb_cache_.valid = false;
|
|
||||||
convexity_ = Convexity::UNCHECKED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool vsort(const Vertex& v1, const Vertex& v2)
|
|
||||||
{
|
|
||||||
TCompute<Vertex> x1 = getX(v1), x2 = getX(v2);
|
|
||||||
TCompute<Vertex> y1 = getY(v1), y2 = getY(v2);
|
|
||||||
return y1 == y2 ? x1 < x2 : y1 < y2;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
struct NestControl {
|
||||||
* \brief Subclass of _Item for regular rectangle items.
|
ProgressFunction progressfn;
|
||||||
*/
|
StopCondition stopcond = []{ return false; };
|
||||||
template<class RawShape>
|
|
||||||
class _Rectangle: public _Item<RawShape> {
|
|
||||||
using _Item<RawShape>::vertex;
|
|
||||||
using TO = Orientation;
|
|
||||||
public:
|
|
||||||
|
|
||||||
using Unit = TCoord<TPoint<RawShape>>;
|
NestControl() = default;
|
||||||
|
NestControl(ProgressFunction pr) : progressfn{std::move(pr)} {}
|
||||||
template<TO o = OrientationType<RawShape>::Value>
|
NestControl(StopCondition sc) : stopcond{std::move(sc)} {}
|
||||||
inline _Rectangle(Unit width, Unit height,
|
NestControl(ProgressFunction pr, StopCondition sc)
|
||||||
// disable this ctor if o != CLOCKWISE
|
: progressfn{std::move(pr)}, stopcond{std::move(sc)}
|
||||||
enable_if_t< o == TO::CLOCKWISE, int> = 0 ):
|
{}
|
||||||
_Item<RawShape>( sl::create<RawShape>( {
|
|
||||||
{0, 0},
|
|
||||||
{0, height},
|
|
||||||
{width, height},
|
|
||||||
{width, 0},
|
|
||||||
{0, 0}
|
|
||||||
} ))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template<TO o = OrientationType<RawShape>::Value>
|
|
||||||
inline _Rectangle(Unit width, Unit height,
|
|
||||||
// disable this ctor if o != COUNTER_CLOCKWISE
|
|
||||||
enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ):
|
|
||||||
_Item<RawShape>( sl::create<RawShape>( {
|
|
||||||
{0, 0},
|
|
||||||
{width, 0},
|
|
||||||
{width, height},
|
|
||||||
{0, height},
|
|
||||||
{0, 0}
|
|
||||||
} ))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Unit width() const BP2D_NOEXCEPT {
|
|
||||||
return getX(vertex(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Unit height() const BP2D_NOEXCEPT {
|
|
||||||
return getY(vertex(2));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class RawShape>
|
template<class Placer = NfpPlacer,
|
||||||
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
|
class Selector = FirstFitSelection,
|
||||||
return sl::isInside(boundingBox(), box);
|
class Iterator = std::vector<Item>::iterator>
|
||||||
|
std::size_t nest(Iterator from, Iterator to,
|
||||||
|
const typename Placer::BinType & bin,
|
||||||
|
Coord dist = 0,
|
||||||
|
const NestConfig<Placer, Selector> &cfg = {},
|
||||||
|
NestControl ctl = {})
|
||||||
|
{
|
||||||
|
_Nester<Placer, Selector> nester{bin, dist, cfg.placer_config, cfg.selector_config};
|
||||||
|
if(ctl.progressfn) nester.progressIndicator(ctl.progressfn);
|
||||||
|
if(ctl.stopcond) nester.stopCondition(ctl.stopcond);
|
||||||
|
return nester.execute(from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape> inline bool
|
#ifdef LIBNEST2D_STATIC
|
||||||
_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
|
|
||||||
return sl::isInside(transformedShape(), circ);
|
extern template class _Nester<NfpPlacer, FirstFitSelection>;
|
||||||
|
extern template class _Nester<BottomLeftPlacer, FirstFitSelection>;
|
||||||
|
extern template std::size_t nest(std::vector<Item>::iterator from,
|
||||||
|
std::vector<Item>::iterator to,
|
||||||
|
const Box & bin,
|
||||||
|
Coord dist,
|
||||||
|
const NestConfig<NfpPlacer, FirstFitSelection> &cfg,
|
||||||
|
NestControl ctl);
|
||||||
|
extern template std::size_t nest(std::vector<Item>::iterator from,
|
||||||
|
std::vector<Item>::iterator to,
|
||||||
|
const Box & bin,
|
||||||
|
Coord dist,
|
||||||
|
const NestConfig<BottomLeftPlacer, FirstFitSelection> &cfg,
|
||||||
|
NestControl ctl);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<class Placer = NfpPlacer,
|
||||||
|
class Selector = FirstFitSelection,
|
||||||
|
class Container = std::vector<Item>>
|
||||||
|
std::size_t nest(Container&& cont,
|
||||||
|
const typename Placer::BinType & bin,
|
||||||
|
Coord dist = 0,
|
||||||
|
const NestConfig<Placer, Selector> &cfg = {},
|
||||||
|
NestControl ctl = {})
|
||||||
|
{
|
||||||
|
return nest<Placer, Selector>(cont.begin(), cont.end(), bin, dist, cfg, ctl);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape> using _ItemRef = std::reference_wrapper<_Item<RawShape>>;
|
|
||||||
template<class RawShape> using _ItemGroup = std::vector<_ItemRef<RawShape>>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A list of packed item vectors. Each vector represents a bin.
|
|
||||||
*/
|
|
||||||
template<class RawShape>
|
|
||||||
using _PackGroup = std::vector<std::vector<_ItemRef<RawShape>>>;
|
|
||||||
|
|
||||||
template<class Iterator>
|
|
||||||
struct ConstItemRange {
|
|
||||||
Iterator from;
|
|
||||||
Iterator to;
|
|
||||||
bool valid = false;
|
|
||||||
|
|
||||||
ConstItemRange() = default;
|
|
||||||
ConstItemRange(Iterator f, Iterator t): from(f), to(t), valid(true) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Container>
|
|
||||||
inline ConstItemRange<typename Container::const_iterator>
|
|
||||||
rem(typename Container::const_iterator it, const Container& cont) {
|
|
||||||
return {std::next(it), cont.end()};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A wrapper interface (trait) class for any placement strategy provider.
|
|
||||||
*
|
|
||||||
* If a client wants to use its own placement algorithm, all it has to do is to
|
|
||||||
* specialize this class template and define all the ten methods it has. It can
|
|
||||||
* use the strategies::PlacerBoilerplace class for creating a new placement
|
|
||||||
* strategy where only the constructor and the trypack method has to be provided
|
|
||||||
* and it will work out of the box.
|
|
||||||
*/
|
|
||||||
template<class PlacementStrategy>
|
|
||||||
class PlacementStrategyLike {
|
|
||||||
PlacementStrategy impl_;
|
|
||||||
public:
|
|
||||||
|
|
||||||
using RawShape = typename PlacementStrategy::ShapeType;
|
|
||||||
|
|
||||||
/// The item type that the placer works with.
|
|
||||||
using Item = _Item<RawShape>;
|
|
||||||
|
|
||||||
/// The placer's config type. Should be a simple struct but can be anything.
|
|
||||||
using Config = typename PlacementStrategy::Config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief The type of the bin that the placer works with.
|
|
||||||
*
|
|
||||||
* Can be a box or an arbitrary shape or just a width or height without a
|
|
||||||
* second dimension if an infinite bin is considered.
|
|
||||||
*/
|
|
||||||
using BinType = typename PlacementStrategy::BinType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Pack result that can be used to accept or discard it. See trypack
|
|
||||||
* method.
|
|
||||||
*/
|
|
||||||
using PackResult = typename PlacementStrategy::PackResult;
|
|
||||||
|
|
||||||
using ItemGroup = _ItemGroup<RawShape>;
|
|
||||||
using DefaultIterator = typename ItemGroup::const_iterator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Constructor taking the bin and an optional configuration.
|
|
||||||
* @param bin The bin object whose type is defined by the placement strategy.
|
|
||||||
* @param config The configuration for the particular placer.
|
|
||||||
*/
|
|
||||||
explicit PlacementStrategyLike(const BinType& bin,
|
|
||||||
const Config& config = Config()):
|
|
||||||
impl_(bin)
|
|
||||||
{
|
|
||||||
configure(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Provide a different configuration for the placer.
|
|
||||||
*
|
|
||||||
* Note that it depends on the particular placer implementation how it
|
|
||||||
* reacts to config changes in the middle of a calculation.
|
|
||||||
*
|
|
||||||
* @param config The configuration object defined by the placement strategy.
|
|
||||||
*/
|
|
||||||
inline void configure(const Config& config) { impl_.configure(config); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try to pack an item with a result object that contains the packing
|
|
||||||
* information for later accepting it.
|
|
||||||
*
|
|
||||||
* \param item_store A container of items that are intended to be packed
|
|
||||||
* later. Can be used by the placer to switch tactics. When it's knows that
|
|
||||||
* many items will come a greedy strategy may not be the best.
|
|
||||||
* \param from The iterator to the item from which the packing should start,
|
|
||||||
* including the pointed item
|
|
||||||
* \param count How many items should be packed. If the value is 1, than
|
|
||||||
* just the item pointed to by "from" argument should be packed.
|
|
||||||
*/
|
|
||||||
template<class Iter = DefaultIterator>
|
|
||||||
inline PackResult trypack(
|
|
||||||
Item& item,
|
|
||||||
const ConstItemRange<Iter>& remaining = ConstItemRange<Iter>())
|
|
||||||
{
|
|
||||||
return impl_.trypack(item, remaining);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A method to accept a previously tried item (or items).
|
|
||||||
*
|
|
||||||
* If the pack result is a failure the method should ignore it.
|
|
||||||
* @param r The result of a previous trypack call.
|
|
||||||
*/
|
|
||||||
inline void accept(PackResult& r) { impl_.accept(r); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief pack Try to pack and immediately accept it on success.
|
|
||||||
*
|
|
||||||
* A default implementation would be to call
|
|
||||||
* { auto&& r = trypack(...); accept(r); return r; } but we should let the
|
|
||||||
* implementor of the placement strategy to harvest any optimizations from
|
|
||||||
* the absence of an intermediate step. The above version can still be used
|
|
||||||
* in the implementation.
|
|
||||||
*
|
|
||||||
* @param item The item to pack.
|
|
||||||
* @return Returns true if the item was packed or false if it could not be
|
|
||||||
* packed.
|
|
||||||
*/
|
|
||||||
template<class Range = ConstItemRange<DefaultIterator>>
|
|
||||||
inline bool pack(
|
|
||||||
Item& item,
|
|
||||||
const Range& remaining = Range())
|
|
||||||
{
|
|
||||||
return impl_.pack(item, remaining);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method makes possible to "preload" some items into the placer. It
|
|
||||||
* will not move these items but will consider them as already packed.
|
|
||||||
*/
|
|
||||||
inline void preload(const ItemGroup& packeditems)
|
|
||||||
{
|
|
||||||
impl_.preload(packeditems);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unpack the last element (remove it from the list of packed items).
|
|
||||||
inline void unpackLast() { impl_.unpackLast(); }
|
|
||||||
|
|
||||||
/// Get the bin object.
|
|
||||||
inline const BinType& bin() const { return impl_.bin(); }
|
|
||||||
|
|
||||||
/// Set a new bin object.
|
|
||||||
inline void bin(const BinType& bin) { impl_.bin(bin); }
|
|
||||||
|
|
||||||
/// Get the packed items.
|
|
||||||
inline ItemGroup getItems() { return impl_.getItems(); }
|
|
||||||
|
|
||||||
/// Clear the packed items so a new session can be started.
|
|
||||||
inline void clearItems() { impl_.clearItems(); }
|
|
||||||
|
|
||||||
inline double filledArea() const { return impl_.filledArea(); }
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// The progress function will be called with the number of placed items
|
|
||||||
using ProgressFunction = std::function<void(unsigned)>;
|
|
||||||
using StopCondition = std::function<bool(void)>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper interface (trait) class for any selections strategy provider.
|
|
||||||
*/
|
|
||||||
template<class SelectionStrategy>
|
|
||||||
class SelectionStrategyLike {
|
|
||||||
SelectionStrategy impl_;
|
|
||||||
public:
|
|
||||||
using RawShape = typename SelectionStrategy::ShapeType;
|
|
||||||
using Item = _Item<RawShape>;
|
|
||||||
using PackGroup = _PackGroup<RawShape>;
|
|
||||||
using Config = typename SelectionStrategy::Config;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Provide a different configuration for the selection strategy.
|
|
||||||
*
|
|
||||||
* Note that it depends on the particular placer implementation how it
|
|
||||||
* reacts to config changes in the middle of a calculation.
|
|
||||||
*
|
|
||||||
* @param config The configuration object defined by the selection strategy.
|
|
||||||
*/
|
|
||||||
inline void configure(const Config& config) {
|
|
||||||
impl_.configure(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A function callback which should be called whenever an item or
|
|
||||||
* a group of items where successfully packed.
|
|
||||||
* @param fn A function callback object taking one unsigned integer as the
|
|
||||||
* number of the remaining items to pack.
|
|
||||||
*/
|
|
||||||
void progressIndicator(ProgressFunction fn) { impl_.progressIndicator(fn); }
|
|
||||||
|
|
||||||
void stopCondition(StopCondition cond) { impl_.stopCondition(cond); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A method to start the calculation on the input sequence.
|
|
||||||
*
|
|
||||||
* \tparam TPlacer The only mandatory template parameter is the type of
|
|
||||||
* placer compatible with the PlacementStrategyLike interface.
|
|
||||||
*
|
|
||||||
* \param first, last The first and last iterator if the input sequence. It
|
|
||||||
* can be only an iterator of a type convertible to Item.
|
|
||||||
* \param bin. The shape of the bin. It has to be supported by the placement
|
|
||||||
* strategy.
|
|
||||||
* \param An optional config object for the placer.
|
|
||||||
*/
|
|
||||||
template<class TPlacer, class TIterator,
|
|
||||||
class TBin = typename PlacementStrategyLike<TPlacer>::BinType,
|
|
||||||
class PConfig = typename PlacementStrategyLike<TPlacer>::Config>
|
|
||||||
inline void packItems(
|
|
||||||
TIterator first,
|
|
||||||
TIterator last,
|
|
||||||
TBin&& bin,
|
|
||||||
PConfig&& config = PConfig() )
|
|
||||||
{
|
|
||||||
impl_.template packItems<TPlacer>(first, last,
|
|
||||||
std::forward<TBin>(bin),
|
|
||||||
std::forward<PConfig>(config));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the items for a particular bin.
|
|
||||||
* @param binIndex The index of the requested bin.
|
|
||||||
* @return Returns a list of all items packed into the requested bin.
|
|
||||||
*/
|
|
||||||
inline const PackGroup& getResult() const {
|
|
||||||
return impl_.getResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() { impl_.clear(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The _Nester is the front-end class for the libnest2d library. It takes the
|
|
||||||
* input items and changes their transformations to be inside the provided bin.
|
|
||||||
*/
|
|
||||||
template<class PlacementStrategy, class SelectionStrategy >
|
|
||||||
class _Nester {
|
|
||||||
using TSel = SelectionStrategyLike<SelectionStrategy>;
|
|
||||||
TSel selector_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using Item = typename PlacementStrategy::Item;
|
|
||||||
using ShapeType = typename Item::ShapeType;
|
|
||||||
using ItemRef = std::reference_wrapper<Item>;
|
|
||||||
using TPlacer = PlacementStrategyLike<PlacementStrategy>;
|
|
||||||
using BinType = typename TPlacer::BinType;
|
|
||||||
using PlacementConfig = typename TPlacer::Config;
|
|
||||||
using SelectionConfig = typename TSel::Config;
|
|
||||||
using Coord = TCoord<TPoint<typename Item::ShapeType>>;
|
|
||||||
using PackGroup = _PackGroup<typename Item::ShapeType>;
|
|
||||||
using ResultType = PackGroup;
|
|
||||||
|
|
||||||
private:
|
|
||||||
BinType bin_;
|
|
||||||
PlacementConfig pconfig_;
|
|
||||||
Coord min_obj_distance_;
|
|
||||||
|
|
||||||
using SItem = typename SelectionStrategy::Item;
|
|
||||||
using TPItem = remove_cvref_t<Item>;
|
|
||||||
using TSItem = remove_cvref_t<SItem>;
|
|
||||||
|
|
||||||
StopCondition stopfn_;
|
|
||||||
|
|
||||||
template<class It> using TVal = remove_ref_t<typename It::value_type>;
|
|
||||||
|
|
||||||
template<class It, class Out>
|
|
||||||
using ItemIteratorOnly =
|
|
||||||
enable_if_t<std::is_convertible<TVal<It>&, TPItem&>::value, Out>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Constructor taking the bin as the only mandatory parameter.
|
|
||||||
*
|
|
||||||
* \param bin The bin shape that will be used by the placers. The type
|
|
||||||
* of the bin should be one that is supported by the placer type.
|
|
||||||
*/
|
|
||||||
template<class TBinType = BinType,
|
|
||||||
class PConf = PlacementConfig,
|
|
||||||
class SConf = SelectionConfig>
|
|
||||||
_Nester(TBinType&& bin, Coord min_obj_distance = 0,
|
|
||||||
const PConf& pconfig = PConf(), const SConf& sconfig = SConf()):
|
|
||||||
bin_(std::forward<TBinType>(bin)),
|
|
||||||
pconfig_(pconfig),
|
|
||||||
min_obj_distance_(min_obj_distance)
|
|
||||||
{
|
|
||||||
static_assert( std::is_same<TPItem, TSItem>::value,
|
|
||||||
"Incompatible placement and selection strategy!");
|
|
||||||
|
|
||||||
selector_.configure(sconfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
void configure(const PlacementConfig& pconf) { pconfig_ = pconf; }
|
|
||||||
void configure(const SelectionConfig& sconf) { selector_.configure(sconf); }
|
|
||||||
void configure(const PlacementConfig& pconf, const SelectionConfig& sconf)
|
|
||||||
{
|
|
||||||
pconfig_ = pconf;
|
|
||||||
selector_.configure(sconf);
|
|
||||||
}
|
|
||||||
void configure(const SelectionConfig& sconf, const PlacementConfig& pconf)
|
|
||||||
{
|
|
||||||
pconfig_ = pconf;
|
|
||||||
selector_.configure(sconf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Arrange an input sequence of _Item-s.
|
|
||||||
*
|
|
||||||
* To get the result, call the translation(), rotation() and binId()
|
|
||||||
* methods of each item. If only the transformed polygon is needed, call
|
|
||||||
* transformedShape() to get the properly transformed shapes.
|
|
||||||
*
|
|
||||||
* The number of groups in the pack group is the number of bins opened by
|
|
||||||
* the selection algorithm.
|
|
||||||
*/
|
|
||||||
template<class It>
|
|
||||||
inline ItemIteratorOnly<It, size_t> execute(It from, It to)
|
|
||||||
{
|
|
||||||
auto infl = static_cast<Coord>(std::ceil(min_obj_distance_/2.0));
|
|
||||||
if(infl > 0) std::for_each(from, to, [this, infl](Item& item) {
|
|
||||||
item.inflate(infl);
|
|
||||||
});
|
|
||||||
|
|
||||||
selector_.template packItems<PlacementStrategy>(
|
|
||||||
from, to, bin_, pconfig_);
|
|
||||||
|
|
||||||
if(min_obj_distance_ > 0) std::for_each(from, to, [infl](Item& item) {
|
|
||||||
item.inflate(-infl);
|
|
||||||
});
|
|
||||||
|
|
||||||
return selector_.getResult().size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set a progress indicator function object for the selector.
|
|
||||||
inline _Nester& progressIndicator(ProgressFunction func)
|
|
||||||
{
|
|
||||||
selector_.progressIndicator(func); return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set a predicate to tell when to abort nesting.
|
|
||||||
inline _Nester& stopCondition(StopCondition fn)
|
|
||||||
{
|
|
||||||
stopfn_ = fn; selector_.stopCondition(fn); return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const PackGroup& lastResult() const
|
|
||||||
{
|
|
||||||
return selector_.getResult();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // LIBNEST2D_HPP
|
#endif // LIBNEST2D_HPP
|
||||||
|
869
src/libnest2d/include/libnest2d/nester.hpp
Normal file
869
src/libnest2d/include/libnest2d/nester.hpp
Normal file
@ -0,0 +1,869 @@
|
|||||||
|
#ifndef NESTER_HPP
|
||||||
|
#define NESTER_HPP
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <array>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <libnest2d/geometry_traits.hpp>
|
||||||
|
|
||||||
|
namespace libnest2d {
|
||||||
|
|
||||||
|
static const constexpr int BIN_ID_UNSET = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief An item to be placed on a bin.
|
||||||
|
*
|
||||||
|
* It holds a copy of the original shape object but supports move construction
|
||||||
|
* from the shape objects if its an rvalue reference. This way we can construct
|
||||||
|
* the items without the cost of copying a potentially large amount of input.
|
||||||
|
*
|
||||||
|
* The results of some calculations are cached for maintaining fast run times.
|
||||||
|
* For this reason, memory demands are much higher but this should pay off.
|
||||||
|
*/
|
||||||
|
template<class RawShape>
|
||||||
|
class _Item {
|
||||||
|
using Coord = TCoord<TPoint<RawShape>>;
|
||||||
|
using Vertex = TPoint<RawShape>;
|
||||||
|
using Box = _Box<Vertex>;
|
||||||
|
|
||||||
|
using VertexConstIterator = typename TContour<RawShape>::const_iterator;
|
||||||
|
|
||||||
|
// The original shape that gets encapsulated.
|
||||||
|
RawShape sh_;
|
||||||
|
|
||||||
|
// Transformation data
|
||||||
|
Vertex translation_{0, 0};
|
||||||
|
Radians rotation_{0.0};
|
||||||
|
Coord inflation_{0};
|
||||||
|
|
||||||
|
// Info about whether the transformations will have to take place
|
||||||
|
// This is needed because if floating point is used, it is hard to say
|
||||||
|
// that a zero angle is not a rotation because of testing for equality.
|
||||||
|
bool has_rotation_ = false, has_translation_ = false, has_inflation_ = false;
|
||||||
|
|
||||||
|
// For caching the calculations as they can get pretty expensive.
|
||||||
|
mutable RawShape tr_cache_;
|
||||||
|
mutable bool tr_cache_valid_ = false;
|
||||||
|
mutable double area_cache_ = 0;
|
||||||
|
mutable bool area_cache_valid_ = false;
|
||||||
|
mutable RawShape inflate_cache_;
|
||||||
|
mutable bool inflate_cache_valid_ = false;
|
||||||
|
|
||||||
|
enum class Convexity: char {
|
||||||
|
UNCHECKED,
|
||||||
|
C_TRUE,
|
||||||
|
C_FALSE
|
||||||
|
};
|
||||||
|
|
||||||
|
mutable Convexity convexity_ = Convexity::UNCHECKED;
|
||||||
|
mutable VertexConstIterator rmt_; // rightmost top vertex
|
||||||
|
mutable VertexConstIterator lmb_; // leftmost bottom vertex
|
||||||
|
mutable bool rmt_valid_ = false, lmb_valid_ = false;
|
||||||
|
mutable struct BBCache {
|
||||||
|
Box bb; bool valid;
|
||||||
|
BBCache(): valid(false) {}
|
||||||
|
} bb_cache_;
|
||||||
|
|
||||||
|
int binid_{BIN_ID_UNSET}, priority_{0};
|
||||||
|
bool fixed_{false};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// The type of the shape which was handed over as the template argument.
|
||||||
|
using ShapeType = RawShape;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Iterator type for the outer vertices.
|
||||||
|
*
|
||||||
|
* Only const iterators can be used. The _Item type is not intended to
|
||||||
|
* modify the carried shapes from the outside. The main purpose of this type
|
||||||
|
* is to cache the calculation results from the various operators it
|
||||||
|
* supports. Giving out a non const iterator would make it impossible to
|
||||||
|
* perform correct cache invalidation.
|
||||||
|
*/
|
||||||
|
using Iterator = VertexConstIterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the orientation of the polygon.
|
||||||
|
*
|
||||||
|
* The orientation have to be specified as a specialization of the
|
||||||
|
* OrientationType struct which has a Value constant.
|
||||||
|
*
|
||||||
|
* @return The orientation type identifier for the _Item type.
|
||||||
|
*/
|
||||||
|
static BP2D_CONSTEXPR Orientation orientation() {
|
||||||
|
return OrientationType<RawShape>::Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructing an _Item form an existing raw shape. The shape will
|
||||||
|
* be copied into the _Item object.
|
||||||
|
* @param sh The original shape object.
|
||||||
|
*/
|
||||||
|
explicit inline _Item(const RawShape& sh): sh_(sh) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construction of an item by moving the content of the raw shape,
|
||||||
|
* assuming that it supports move semantics.
|
||||||
|
* @param sh The original shape object.
|
||||||
|
*/
|
||||||
|
explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create an item from an initializer list.
|
||||||
|
* @param il The initializer list of vertices.
|
||||||
|
*/
|
||||||
|
inline _Item(const std::initializer_list< Vertex >& il):
|
||||||
|
sh_(sl::create<RawShape>(il)) {}
|
||||||
|
|
||||||
|
inline _Item(const TContour<RawShape>& contour,
|
||||||
|
const THolesContainer<RawShape>& holes = {}):
|
||||||
|
sh_(sl::create<RawShape>(contour, holes)) {}
|
||||||
|
|
||||||
|
inline _Item(TContour<RawShape>&& contour,
|
||||||
|
THolesContainer<RawShape>&& holes):
|
||||||
|
sh_(sl::create<RawShape>(std::move(contour), std::move(holes))) {}
|
||||||
|
|
||||||
|
inline bool isFixed() const noexcept { return fixed_; }
|
||||||
|
inline void markAsFixedInBin(int binid)
|
||||||
|
{
|
||||||
|
fixed_ = binid >= 0;
|
||||||
|
binid_ = binid;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void binId(int idx) { binid_ = idx; }
|
||||||
|
inline int binId() const noexcept { return binid_; }
|
||||||
|
|
||||||
|
inline void priority(int p) { priority_ = p; }
|
||||||
|
inline int priority() const noexcept { return priority_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert the polygon to string representation. The format depends
|
||||||
|
* on the implementation of the polygon.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
inline std::string toString() const
|
||||||
|
{
|
||||||
|
return sl::toString(sh_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator tho the first contour vertex in the polygon.
|
||||||
|
inline Iterator begin() const
|
||||||
|
{
|
||||||
|
return sl::cbegin(sh_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Alias to begin()
|
||||||
|
inline Iterator cbegin() const
|
||||||
|
{
|
||||||
|
return sl::cbegin(sh_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator to the last contour vertex.
|
||||||
|
inline Iterator end() const
|
||||||
|
{
|
||||||
|
return sl::cend(sh_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Alias to end()
|
||||||
|
inline Iterator cend() const
|
||||||
|
{
|
||||||
|
return sl::cend(sh_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a copy of an outer vertex within the carried shape.
|
||||||
|
*
|
||||||
|
* Note that the vertex considered here is taken from the original shape
|
||||||
|
* that this item is constructed from. This means that no transformation is
|
||||||
|
* applied to the shape in this call.
|
||||||
|
*
|
||||||
|
* @param idx The index of the requested vertex.
|
||||||
|
* @return A copy of the requested vertex.
|
||||||
|
*/
|
||||||
|
inline Vertex vertex(unsigned long idx) const
|
||||||
|
{
|
||||||
|
return sl::vertex(sh_, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modify a vertex.
|
||||||
|
*
|
||||||
|
* Note that this method will invalidate every cached calculation result
|
||||||
|
* including polygon offset and transformations.
|
||||||
|
*
|
||||||
|
* @param idx The index of the requested vertex.
|
||||||
|
* @param v The new vertex data.
|
||||||
|
*/
|
||||||
|
inline void setVertex(unsigned long idx, const Vertex& v )
|
||||||
|
{
|
||||||
|
invalidateCache();
|
||||||
|
sl::vertex(sh_, idx) = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate the shape area.
|
||||||
|
*
|
||||||
|
* The method returns absolute value and does not reflect polygon
|
||||||
|
* orientation. The result is cached, subsequent calls will have very little
|
||||||
|
* cost.
|
||||||
|
* @return The shape area in floating point double precision.
|
||||||
|
*/
|
||||||
|
inline double area() const {
|
||||||
|
double ret ;
|
||||||
|
if(area_cache_valid_) ret = area_cache_;
|
||||||
|
else {
|
||||||
|
ret = sl::area(infaltedShape());
|
||||||
|
area_cache_ = ret;
|
||||||
|
area_cache_valid_ = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isContourConvex() const {
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
switch(convexity_) {
|
||||||
|
case Convexity::UNCHECKED:
|
||||||
|
ret = sl::isConvex(sl::contour(transformedShape()));
|
||||||
|
convexity_ = ret? Convexity::C_TRUE : Convexity::C_FALSE;
|
||||||
|
break;
|
||||||
|
case Convexity::C_TRUE: ret = true; break;
|
||||||
|
case Convexity::C_FALSE:;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isHoleConvex(unsigned /*holeidx*/) const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool areHolesConvex() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The number of the outer ring vertices.
|
||||||
|
inline size_t vertexCount() const {
|
||||||
|
return sl::contourVertexCount(sh_);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t holeCount() const {
|
||||||
|
return sl::holeCount(sh_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief isPointInside
|
||||||
|
* @param p
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
inline bool isInside(const Vertex& p) const
|
||||||
|
{
|
||||||
|
return sl::isInside(p, transformedShape());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isInside(const _Item& sh) const
|
||||||
|
{
|
||||||
|
return sl::isInside(transformedShape(), sh.transformedShape());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isInside(const RawShape& sh) const
|
||||||
|
{
|
||||||
|
return sl::isInside(transformedShape(), sh);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isInside(const _Box<TPoint<RawShape>>& box) const;
|
||||||
|
inline bool isInside(const _Circle<TPoint<RawShape>>& box) const;
|
||||||
|
|
||||||
|
inline void translate(const Vertex& d) BP2D_NOEXCEPT
|
||||||
|
{
|
||||||
|
translation(translation() + d);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void rotate(const Radians& rads) BP2D_NOEXCEPT
|
||||||
|
{
|
||||||
|
rotation(rotation() + rads);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void inflation(Coord distance) BP2D_NOEXCEPT
|
||||||
|
{
|
||||||
|
inflation_ = distance;
|
||||||
|
has_inflation_ = true;
|
||||||
|
invalidateCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Coord inflation() const BP2D_NOEXCEPT {
|
||||||
|
return inflation_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void inflate(Coord distance) BP2D_NOEXCEPT
|
||||||
|
{
|
||||||
|
inflation(inflation() + distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Radians rotation() const BP2D_NOEXCEPT
|
||||||
|
{
|
||||||
|
return rotation_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline TPoint<RawShape> translation() const BP2D_NOEXCEPT
|
||||||
|
{
|
||||||
|
return translation_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void rotation(Radians rot) BP2D_NOEXCEPT
|
||||||
|
{
|
||||||
|
if(rotation_ != rot) {
|
||||||
|
rotation_ = rot; has_rotation_ = true; tr_cache_valid_ = false;
|
||||||
|
rmt_valid_ = false; lmb_valid_ = false;
|
||||||
|
bb_cache_.valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void translation(const TPoint<RawShape>& tr) BP2D_NOEXCEPT
|
||||||
|
{
|
||||||
|
if(translation_ != tr) {
|
||||||
|
translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
|
||||||
|
//bb_cache_.valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const RawShape& transformedShape() const
|
||||||
|
{
|
||||||
|
if(tr_cache_valid_) return tr_cache_;
|
||||||
|
|
||||||
|
RawShape cpy = infaltedShape();
|
||||||
|
if(has_rotation_) sl::rotate(cpy, rotation_);
|
||||||
|
if(has_translation_) sl::translate(cpy, translation_);
|
||||||
|
tr_cache_ = cpy; tr_cache_valid_ = true;
|
||||||
|
rmt_valid_ = false; lmb_valid_ = false;
|
||||||
|
|
||||||
|
return tr_cache_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline operator RawShape() const
|
||||||
|
{
|
||||||
|
return transformedShape();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const RawShape& rawShape() const BP2D_NOEXCEPT
|
||||||
|
{
|
||||||
|
return sh_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void resetTransformation() BP2D_NOEXCEPT
|
||||||
|
{
|
||||||
|
has_translation_ = false; has_rotation_ = false; has_inflation_ = false;
|
||||||
|
invalidateCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Box boundingBox() const {
|
||||||
|
if(!bb_cache_.valid) {
|
||||||
|
if(!has_rotation_)
|
||||||
|
bb_cache_.bb = sl::boundingBox(infaltedShape());
|
||||||
|
else {
|
||||||
|
// TODO make sure this works
|
||||||
|
auto rotsh = infaltedShape();
|
||||||
|
sl::rotate(rotsh, rotation_);
|
||||||
|
bb_cache_.bb = sl::boundingBox(rotsh);
|
||||||
|
}
|
||||||
|
bb_cache_.valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &bb = bb_cache_.bb; auto &tr = translation_;
|
||||||
|
return {bb.minCorner() + tr, bb.maxCorner() + tr };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vertex referenceVertex() const {
|
||||||
|
return rightmostTopVertex();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vertex rightmostTopVertex() const {
|
||||||
|
if(!rmt_valid_ || !tr_cache_valid_) { // find max x and max y vertex
|
||||||
|
auto& tsh = transformedShape();
|
||||||
|
rmt_ = std::max_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
|
||||||
|
rmt_valid_ = true;
|
||||||
|
}
|
||||||
|
return *rmt_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vertex leftmostBottomVertex() const {
|
||||||
|
if(!lmb_valid_ || !tr_cache_valid_) { // find min x and min y vertex
|
||||||
|
auto& tsh = transformedShape();
|
||||||
|
lmb_ = std::min_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
|
||||||
|
lmb_valid_ = true;
|
||||||
|
}
|
||||||
|
return *lmb_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Static methods:
|
||||||
|
|
||||||
|
inline static bool intersects(const _Item& sh1, const _Item& sh2)
|
||||||
|
{
|
||||||
|
return sl::intersects(sh1.transformedShape(),
|
||||||
|
sh2.transformedShape());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static bool touches(const _Item& sh1, const _Item& sh2)
|
||||||
|
{
|
||||||
|
return sl::touches(sh1.transformedShape(),
|
||||||
|
sh2.transformedShape());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
inline const RawShape& infaltedShape() const {
|
||||||
|
if(has_inflation_ ) {
|
||||||
|
if(inflate_cache_valid_) return inflate_cache_;
|
||||||
|
|
||||||
|
inflate_cache_ = sh_;
|
||||||
|
sl::offset(inflate_cache_, inflation_);
|
||||||
|
inflate_cache_valid_ = true;
|
||||||
|
return inflate_cache_;
|
||||||
|
}
|
||||||
|
return sh_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void invalidateCache() const BP2D_NOEXCEPT
|
||||||
|
{
|
||||||
|
tr_cache_valid_ = false;
|
||||||
|
lmb_valid_ = false; rmt_valid_ = false;
|
||||||
|
area_cache_valid_ = false;
|
||||||
|
inflate_cache_valid_ = false;
|
||||||
|
bb_cache_.valid = false;
|
||||||
|
convexity_ = Convexity::UNCHECKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool vsort(const Vertex& v1, const Vertex& v2)
|
||||||
|
{
|
||||||
|
TCompute<Vertex> x1 = getX(v1), x2 = getX(v2);
|
||||||
|
TCompute<Vertex> y1 = getY(v1), y2 = getY(v2);
|
||||||
|
return y1 == y2 ? x1 < x2 : y1 < y2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Subclass of _Item for regular rectangle items.
|
||||||
|
*/
|
||||||
|
template<class RawShape>
|
||||||
|
class _Rectangle: public _Item<RawShape> {
|
||||||
|
using _Item<RawShape>::vertex;
|
||||||
|
using TO = Orientation;
|
||||||
|
public:
|
||||||
|
|
||||||
|
using Unit = TCoord<TPoint<RawShape>>;
|
||||||
|
|
||||||
|
template<TO o = OrientationType<RawShape>::Value>
|
||||||
|
inline _Rectangle(Unit width, Unit height,
|
||||||
|
// disable this ctor if o != CLOCKWISE
|
||||||
|
enable_if_t< o == TO::CLOCKWISE, int> = 0 ):
|
||||||
|
_Item<RawShape>( sl::create<RawShape>( {
|
||||||
|
{0, 0},
|
||||||
|
{0, height},
|
||||||
|
{width, height},
|
||||||
|
{width, 0},
|
||||||
|
{0, 0}
|
||||||
|
} ))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<TO o = OrientationType<RawShape>::Value>
|
||||||
|
inline _Rectangle(Unit width, Unit height,
|
||||||
|
// disable this ctor if o != COUNTER_CLOCKWISE
|
||||||
|
enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ):
|
||||||
|
_Item<RawShape>( sl::create<RawShape>( {
|
||||||
|
{0, 0},
|
||||||
|
{width, 0},
|
||||||
|
{width, height},
|
||||||
|
{0, height},
|
||||||
|
{0, 0}
|
||||||
|
} ))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Unit width() const BP2D_NOEXCEPT {
|
||||||
|
return getX(vertex(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Unit height() const BP2D_NOEXCEPT {
|
||||||
|
return getY(vertex(2));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class RawShape>
|
||||||
|
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
|
||||||
|
return sl::isInside(boundingBox(), box);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class RawShape> inline bool
|
||||||
|
_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
|
||||||
|
return sl::isInside(transformedShape(), circ);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class RawShape> using _ItemRef = std::reference_wrapper<_Item<RawShape>>;
|
||||||
|
template<class RawShape> using _ItemGroup = std::vector<_ItemRef<RawShape>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A list of packed item vectors. Each vector represents a bin.
|
||||||
|
*/
|
||||||
|
template<class RawShape>
|
||||||
|
using _PackGroup = std::vector<std::vector<_ItemRef<RawShape>>>;
|
||||||
|
|
||||||
|
template<class Iterator>
|
||||||
|
struct ConstItemRange {
|
||||||
|
Iterator from;
|
||||||
|
Iterator to;
|
||||||
|
bool valid = false;
|
||||||
|
|
||||||
|
ConstItemRange() = default;
|
||||||
|
ConstItemRange(Iterator f, Iterator t): from(f), to(t), valid(true) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Container>
|
||||||
|
inline ConstItemRange<typename Container::const_iterator>
|
||||||
|
rem(typename Container::const_iterator it, const Container& cont) {
|
||||||
|
return {std::next(it), cont.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A wrapper interface (trait) class for any placement strategy provider.
|
||||||
|
*
|
||||||
|
* If a client wants to use its own placement algorithm, all it has to do is to
|
||||||
|
* specialize this class template and define all the ten methods it has. It can
|
||||||
|
* use the strategies::PlacerBoilerplace class for creating a new placement
|
||||||
|
* strategy where only the constructor and the trypack method has to be provided
|
||||||
|
* and it will work out of the box.
|
||||||
|
*/
|
||||||
|
template<class PlacementStrategy>
|
||||||
|
class PlacementStrategyLike {
|
||||||
|
PlacementStrategy impl_;
|
||||||
|
public:
|
||||||
|
|
||||||
|
using RawShape = typename PlacementStrategy::ShapeType;
|
||||||
|
|
||||||
|
/// The item type that the placer works with.
|
||||||
|
using Item = _Item<RawShape>;
|
||||||
|
|
||||||
|
/// The placer's config type. Should be a simple struct but can be anything.
|
||||||
|
using Config = typename PlacementStrategy::Config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief The type of the bin that the placer works with.
|
||||||
|
*
|
||||||
|
* Can be a box or an arbitrary shape or just a width or height without a
|
||||||
|
* second dimension if an infinite bin is considered.
|
||||||
|
*/
|
||||||
|
using BinType = typename PlacementStrategy::BinType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Pack result that can be used to accept or discard it. See trypack
|
||||||
|
* method.
|
||||||
|
*/
|
||||||
|
using PackResult = typename PlacementStrategy::PackResult;
|
||||||
|
|
||||||
|
using ItemGroup = _ItemGroup<RawShape>;
|
||||||
|
using DefaultIterator = typename ItemGroup::const_iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructor taking the bin and an optional configuration.
|
||||||
|
* @param bin The bin object whose type is defined by the placement strategy.
|
||||||
|
* @param config The configuration for the particular placer.
|
||||||
|
*/
|
||||||
|
explicit PlacementStrategyLike(const BinType& bin,
|
||||||
|
const Config& config = Config()):
|
||||||
|
impl_(bin)
|
||||||
|
{
|
||||||
|
configure(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Provide a different configuration for the placer.
|
||||||
|
*
|
||||||
|
* Note that it depends on the particular placer implementation how it
|
||||||
|
* reacts to config changes in the middle of a calculation.
|
||||||
|
*
|
||||||
|
* @param config The configuration object defined by the placement strategy.
|
||||||
|
*/
|
||||||
|
inline void configure(const Config& config) { impl_.configure(config); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to pack an item with a result object that contains the packing
|
||||||
|
* information for later accepting it.
|
||||||
|
*
|
||||||
|
* \param item_store A container of items that are intended to be packed
|
||||||
|
* later. Can be used by the placer to switch tactics. When it's knows that
|
||||||
|
* many items will come a greedy strategy may not be the best.
|
||||||
|
* \param from The iterator to the item from which the packing should start,
|
||||||
|
* including the pointed item
|
||||||
|
* \param count How many items should be packed. If the value is 1, than
|
||||||
|
* just the item pointed to by "from" argument should be packed.
|
||||||
|
*/
|
||||||
|
template<class Iter = DefaultIterator>
|
||||||
|
inline PackResult trypack(
|
||||||
|
Item& item,
|
||||||
|
const ConstItemRange<Iter>& remaining = ConstItemRange<Iter>())
|
||||||
|
{
|
||||||
|
return impl_.trypack(item, remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A method to accept a previously tried item (or items).
|
||||||
|
*
|
||||||
|
* If the pack result is a failure the method should ignore it.
|
||||||
|
* @param r The result of a previous trypack call.
|
||||||
|
*/
|
||||||
|
inline void accept(PackResult& r) { impl_.accept(r); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief pack Try to pack and immediately accept it on success.
|
||||||
|
*
|
||||||
|
* A default implementation would be to call
|
||||||
|
* { auto&& r = trypack(...); accept(r); return r; } but we should let the
|
||||||
|
* implementor of the placement strategy to harvest any optimizations from
|
||||||
|
* the absence of an intermediate step. The above version can still be used
|
||||||
|
* in the implementation.
|
||||||
|
*
|
||||||
|
* @param item The item to pack.
|
||||||
|
* @return Returns true if the item was packed or false if it could not be
|
||||||
|
* packed.
|
||||||
|
*/
|
||||||
|
template<class Range = ConstItemRange<DefaultIterator>>
|
||||||
|
inline bool pack(
|
||||||
|
Item& item,
|
||||||
|
const Range& remaining = Range())
|
||||||
|
{
|
||||||
|
return impl_.pack(item, remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method makes possible to "preload" some items into the placer. It
|
||||||
|
* will not move these items but will consider them as already packed.
|
||||||
|
*/
|
||||||
|
inline void preload(const ItemGroup& packeditems)
|
||||||
|
{
|
||||||
|
impl_.preload(packeditems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unpack the last element (remove it from the list of packed items).
|
||||||
|
inline void unpackLast() { impl_.unpackLast(); }
|
||||||
|
|
||||||
|
/// Get the bin object.
|
||||||
|
inline const BinType& bin() const { return impl_.bin(); }
|
||||||
|
|
||||||
|
/// Set a new bin object.
|
||||||
|
inline void bin(const BinType& bin) { impl_.bin(bin); }
|
||||||
|
|
||||||
|
/// Get the packed items.
|
||||||
|
inline ItemGroup getItems() { return impl_.getItems(); }
|
||||||
|
|
||||||
|
/// Clear the packed items so a new session can be started.
|
||||||
|
inline void clearItems() { impl_.clearItems(); }
|
||||||
|
|
||||||
|
inline double filledArea() const { return impl_.filledArea(); }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// The progress function will be called with the number of placed items
|
||||||
|
using ProgressFunction = std::function<void(unsigned)>;
|
||||||
|
using StopCondition = std::function<bool(void)>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper interface (trait) class for any selections strategy provider.
|
||||||
|
*/
|
||||||
|
template<class SelectionStrategy>
|
||||||
|
class SelectionStrategyLike {
|
||||||
|
SelectionStrategy impl_;
|
||||||
|
public:
|
||||||
|
using RawShape = typename SelectionStrategy::ShapeType;
|
||||||
|
using Item = _Item<RawShape>;
|
||||||
|
using PackGroup = _PackGroup<RawShape>;
|
||||||
|
using Config = typename SelectionStrategy::Config;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Provide a different configuration for the selection strategy.
|
||||||
|
*
|
||||||
|
* Note that it depends on the particular placer implementation how it
|
||||||
|
* reacts to config changes in the middle of a calculation.
|
||||||
|
*
|
||||||
|
* @param config The configuration object defined by the selection strategy.
|
||||||
|
*/
|
||||||
|
inline void configure(const Config& config) {
|
||||||
|
impl_.configure(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A function callback which should be called whenever an item or
|
||||||
|
* a group of items where successfully packed.
|
||||||
|
* @param fn A function callback object taking one unsigned integer as the
|
||||||
|
* number of the remaining items to pack.
|
||||||
|
*/
|
||||||
|
void progressIndicator(ProgressFunction fn) { impl_.progressIndicator(fn); }
|
||||||
|
|
||||||
|
void stopCondition(StopCondition cond) { impl_.stopCondition(cond); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A method to start the calculation on the input sequence.
|
||||||
|
*
|
||||||
|
* \tparam TPlacer The only mandatory template parameter is the type of
|
||||||
|
* placer compatible with the PlacementStrategyLike interface.
|
||||||
|
*
|
||||||
|
* \param first, last The first and last iterator if the input sequence. It
|
||||||
|
* can be only an iterator of a type convertible to Item.
|
||||||
|
* \param bin. The shape of the bin. It has to be supported by the placement
|
||||||
|
* strategy.
|
||||||
|
* \param An optional config object for the placer.
|
||||||
|
*/
|
||||||
|
template<class TPlacer, class TIterator,
|
||||||
|
class TBin = typename PlacementStrategyLike<TPlacer>::BinType,
|
||||||
|
class PConfig = typename PlacementStrategyLike<TPlacer>::Config>
|
||||||
|
inline void packItems(
|
||||||
|
TIterator first,
|
||||||
|
TIterator last,
|
||||||
|
TBin&& bin,
|
||||||
|
PConfig&& config = PConfig() )
|
||||||
|
{
|
||||||
|
impl_.template packItems<TPlacer>(first, last,
|
||||||
|
std::forward<TBin>(bin),
|
||||||
|
std::forward<PConfig>(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the items for a particular bin.
|
||||||
|
* @param binIndex The index of the requested bin.
|
||||||
|
* @return Returns a list of all items packed into the requested bin.
|
||||||
|
*/
|
||||||
|
inline const PackGroup& getResult() const {
|
||||||
|
return impl_.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() { impl_.clear(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The _Nester is the front-end class for the libnest2d library. It takes the
|
||||||
|
* input items and changes their transformations to be inside the provided bin.
|
||||||
|
*/
|
||||||
|
template<class PlacementStrategy, class SelectionStrategy >
|
||||||
|
class _Nester {
|
||||||
|
using TSel = SelectionStrategyLike<SelectionStrategy>;
|
||||||
|
TSel selector_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Item = typename PlacementStrategy::Item;
|
||||||
|
using ShapeType = typename Item::ShapeType;
|
||||||
|
using ItemRef = std::reference_wrapper<Item>;
|
||||||
|
using TPlacer = PlacementStrategyLike<PlacementStrategy>;
|
||||||
|
using BinType = typename TPlacer::BinType;
|
||||||
|
using PlacementConfig = typename TPlacer::Config;
|
||||||
|
using SelectionConfig = typename TSel::Config;
|
||||||
|
using Coord = TCoord<TPoint<typename Item::ShapeType>>;
|
||||||
|
using PackGroup = _PackGroup<typename Item::ShapeType>;
|
||||||
|
using ResultType = PackGroup;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BinType bin_;
|
||||||
|
PlacementConfig pconfig_;
|
||||||
|
Coord min_obj_distance_;
|
||||||
|
|
||||||
|
using SItem = typename SelectionStrategy::Item;
|
||||||
|
using TPItem = remove_cvref_t<Item>;
|
||||||
|
using TSItem = remove_cvref_t<SItem>;
|
||||||
|
|
||||||
|
StopCondition stopfn_;
|
||||||
|
|
||||||
|
template<class It> using TVal = remove_ref_t<typename It::value_type>;
|
||||||
|
|
||||||
|
template<class It, class Out>
|
||||||
|
using ItemIteratorOnly =
|
||||||
|
enable_if_t<std::is_convertible<TVal<It>&, TPItem&>::value, Out>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Constructor taking the bin as the only mandatory parameter.
|
||||||
|
*
|
||||||
|
* \param bin The bin shape that will be used by the placers. The type
|
||||||
|
* of the bin should be one that is supported by the placer type.
|
||||||
|
*/
|
||||||
|
template<class TBinType = BinType,
|
||||||
|
class PConf = PlacementConfig,
|
||||||
|
class SConf = SelectionConfig>
|
||||||
|
_Nester(TBinType&& bin, Coord min_obj_distance = 0,
|
||||||
|
const PConf& pconfig = PConf(), const SConf& sconfig = SConf()):
|
||||||
|
bin_(std::forward<TBinType>(bin)),
|
||||||
|
pconfig_(pconfig),
|
||||||
|
min_obj_distance_(min_obj_distance)
|
||||||
|
{
|
||||||
|
static_assert( std::is_same<TPItem, TSItem>::value,
|
||||||
|
"Incompatible placement and selection strategy!");
|
||||||
|
|
||||||
|
selector_.configure(sconfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
void configure(const PlacementConfig& pconf) { pconfig_ = pconf; }
|
||||||
|
void configure(const SelectionConfig& sconf) { selector_.configure(sconf); }
|
||||||
|
void configure(const PlacementConfig& pconf, const SelectionConfig& sconf)
|
||||||
|
{
|
||||||
|
pconfig_ = pconf;
|
||||||
|
selector_.configure(sconf);
|
||||||
|
}
|
||||||
|
void configure(const SelectionConfig& sconf, const PlacementConfig& pconf)
|
||||||
|
{
|
||||||
|
pconfig_ = pconf;
|
||||||
|
selector_.configure(sconf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Arrange an input sequence of _Item-s.
|
||||||
|
*
|
||||||
|
* To get the result, call the translation(), rotation() and binId()
|
||||||
|
* methods of each item. If only the transformed polygon is needed, call
|
||||||
|
* transformedShape() to get the properly transformed shapes.
|
||||||
|
*
|
||||||
|
* The number of groups in the pack group is the number of bins opened by
|
||||||
|
* the selection algorithm.
|
||||||
|
*/
|
||||||
|
template<class It>
|
||||||
|
inline ItemIteratorOnly<It, size_t> execute(It from, It to)
|
||||||
|
{
|
||||||
|
auto infl = static_cast<Coord>(std::ceil(min_obj_distance_/2.0));
|
||||||
|
if(infl > 0) std::for_each(from, to, [this, infl](Item& item) {
|
||||||
|
item.inflate(infl);
|
||||||
|
});
|
||||||
|
|
||||||
|
selector_.template packItems<PlacementStrategy>(
|
||||||
|
from, to, bin_, pconfig_);
|
||||||
|
|
||||||
|
if(min_obj_distance_ > 0) std::for_each(from, to, [infl](Item& item) {
|
||||||
|
item.inflate(-infl);
|
||||||
|
});
|
||||||
|
|
||||||
|
return selector_.getResult().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a progress indicator function object for the selector.
|
||||||
|
inline _Nester& progressIndicator(ProgressFunction func)
|
||||||
|
{
|
||||||
|
selector_.progressIndicator(func); return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a predicate to tell when to abort nesting.
|
||||||
|
inline _Nester& stopCondition(StopCondition fn)
|
||||||
|
{
|
||||||
|
stopfn_ = fn; selector_.stopCondition(fn); return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const PackGroup& lastResult() const
|
||||||
|
{
|
||||||
|
return selector_.getResult();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // NESTER_HPP
|
@ -1,61 +0,0 @@
|
|||||||
find_package(NLopt 1.4)
|
|
||||||
|
|
||||||
if(NOT NLopt_FOUND)
|
|
||||||
message(STATUS "NLopt not found so downloading "
|
|
||||||
"and automatic build is performed...")
|
|
||||||
|
|
||||||
include(DownloadProject)
|
|
||||||
|
|
||||||
if (CMAKE_VERSION VERSION_LESS 3.2)
|
|
||||||
set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
|
|
||||||
else()
|
|
||||||
set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(URL_NLOPT "https://github.com/stevengj/nlopt.git"
|
|
||||||
CACHE STRING "Location of the nlopt git repository")
|
|
||||||
|
|
||||||
# set(NLopt_DIR ${CMAKE_BINARY_DIR}/nlopt)
|
|
||||||
include(DownloadProject)
|
|
||||||
download_project( PROJ nlopt
|
|
||||||
GIT_REPOSITORY ${URL_NLOPT}
|
|
||||||
GIT_TAG v2.5.0
|
|
||||||
# CMAKE_CACHE_ARGS -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${NLopt_DIR}
|
|
||||||
${UPDATE_DISCONNECTED_IF_AVAILABLE}
|
|
||||||
)
|
|
||||||
|
|
||||||
set(SHARED_LIBS_STATE BUILD_SHARED_LIBS)
|
|
||||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NLOPT_PYTHON OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NLOPT_OCTAVE OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NLOPT_MATLAB OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NLOPT_GUILE OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NLOPT_SWIG OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NLOPT_LINK_PYTHON OFF CACHE BOOL "" FORCE)
|
|
||||||
|
|
||||||
add_subdirectory(${nlopt_SOURCE_DIR} ${nlopt_BINARY_DIR})
|
|
||||||
|
|
||||||
set(NLopt_LIBS nlopt)
|
|
||||||
set(NLopt_INCLUDE_DIR ${nlopt_BINARY_DIR} ${nlopt_BINARY_DIR}/src/api)
|
|
||||||
set(SHARED_LIBS_STATE ${SHARED_STATE})
|
|
||||||
|
|
||||||
add_library(nloptOptimizer INTERFACE)
|
|
||||||
target_link_libraries(nloptOptimizer INTERFACE nlopt)
|
|
||||||
target_include_directories(nloptOptimizer INTERFACE ${NLopt_INCLUDE_DIR})
|
|
||||||
|
|
||||||
else()
|
|
||||||
add_library(nloptOptimizer INTERFACE)
|
|
||||||
target_link_libraries(nloptOptimizer INTERFACE Nlopt::Nlopt)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
#target_sources( nloptOptimizer INTERFACE
|
|
||||||
#${CMAKE_CURRENT_SOURCE_DIR}/simplex.hpp
|
|
||||||
#${CMAKE_CURRENT_SOURCE_DIR}/subplex.hpp
|
|
||||||
#${CMAKE_CURRENT_SOURCE_DIR}/genetic.hpp
|
|
||||||
#${CMAKE_CURRENT_SOURCE_DIR}/nlopt_boilerplate.hpp
|
|
||||||
#)
|
|
||||||
|
|
||||||
target_compile_definitions(nloptOptimizer INTERFACE LIBNEST2D_OPTIMIZER_NLOPT)
|
|
||||||
|
|
||||||
# And finally plug the nloptOptimizer into libnest2d
|
|
||||||
#target_link_libraries(libnest2d INTERFACE nloptOptimizer)
|
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef PLACER_BOILERPLATE_HPP
|
#ifndef PLACER_BOILERPLATE_HPP
|
||||||
#define PLACER_BOILERPLATE_HPP
|
#define PLACER_BOILERPLATE_HPP
|
||||||
|
|
||||||
#include <libnest2d/libnest2d.hpp>
|
#include <libnest2d/nester.hpp>
|
||||||
|
|
||||||
namespace libnest2d { namespace placers {
|
namespace libnest2d { namespace placers {
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#define SELECTION_BOILERPLATE_HPP
|
#define SELECTION_BOILERPLATE_HPP
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <libnest2d/libnest2d.hpp>
|
#include <libnest2d/nester.hpp>
|
||||||
|
|
||||||
namespace libnest2d { namespace selections {
|
namespace libnest2d { namespace selections {
|
||||||
|
|
||||||
|
@ -1,19 +1,24 @@
|
|||||||
#include <libnest2d.h>
|
#include <libnest2d/libnest2d.hpp>
|
||||||
|
|
||||||
namespace libnest2d {
|
namespace libnest2d {
|
||||||
|
|
||||||
template class Nester<NfpPlacer, FirstFitSelection>;
|
template class _Nester<NfpPlacer, FirstFitSelection>;
|
||||||
template class Nester<BottomLeftPlacer, FirstFitSelection>;
|
template class _Nester<BottomLeftPlacer, FirstFitSelection>;
|
||||||
|
|
||||||
|
template std::size_t _Nester<NfpPlacer, FirstFitSelection>::execute(
|
||||||
|
std::vector<Item>::iterator, std::vector<Item>::iterator);
|
||||||
|
template std::size_t _Nester<BottomLeftPlacer, FirstFitSelection>::execute(
|
||||||
|
std::vector<Item>::iterator, std::vector<Item>::iterator);
|
||||||
|
|
||||||
template std::size_t nest(std::vector<Item>::iterator from,
|
template std::size_t nest(std::vector<Item>::iterator from,
|
||||||
std::vector<Item>::iterator from to,
|
std::vector<Item>::iterator to,
|
||||||
const Box & bin,
|
const Box & bin,
|
||||||
Coord dist,
|
Coord dist,
|
||||||
const NestConfig<NfpPlacer, FirstFitSelection> &cfg,
|
const NestConfig<NfpPlacer, FirstFitSelection> &cfg,
|
||||||
NestControl ctl);
|
NestControl ctl);
|
||||||
|
|
||||||
template std::size_t nest(std::vector<Item>::iterator from,
|
template std::size_t nest(std::vector<Item>::iterator from,
|
||||||
std::vector<Item>::iterator from to,
|
std::vector<Item>::iterator to,
|
||||||
const Box & bin,
|
const Box & bin,
|
||||||
Coord dist,
|
Coord dist,
|
||||||
const NestConfig<BottomLeftPlacer, FirstFitSelection> &cfg,
|
const NestConfig<BottomLeftPlacer, FirstFitSelection> &cfg,
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
|
|
||||||
# Try to find existing GTest installation
|
|
||||||
find_package(GTest 1.7)
|
|
||||||
|
|
||||||
if(NOT GTEST_FOUND)
|
|
||||||
set(URL_GTEST "https://github.com/google/googletest.git"
|
|
||||||
CACHE STRING "Google test source code repository location.")
|
|
||||||
|
|
||||||
message(STATUS "GTest not found so downloading from ${URL_GTEST}")
|
|
||||||
# Go and download google test framework, integrate it with the build
|
|
||||||
set(GTEST_LIBS_TO_LINK gtest gtest_main)
|
|
||||||
|
|
||||||
if (CMAKE_VERSION VERSION_LESS 3.2)
|
|
||||||
set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
|
|
||||||
else()
|
|
||||||
set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(DownloadProject)
|
|
||||||
download_project(PROJ googletest
|
|
||||||
GIT_REPOSITORY ${URL_GTEST}
|
|
||||||
GIT_TAG release-1.7.0
|
|
||||||
${UPDATE_DISCONNECTED_IF_AVAILABLE}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Prevent GoogleTest from overriding our compiler/linker options
|
|
||||||
# when building with Visual Studio
|
|
||||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
||||||
|
|
||||||
add_subdirectory(${googletest_SOURCE_DIR}
|
|
||||||
${googletest_BINARY_DIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
set(GTEST_INCLUDE_DIRS ${googletest_SOURCE_DIR}/include)
|
|
||||||
|
|
||||||
else()
|
|
||||||
find_package(Threads REQUIRED)
|
|
||||||
set(GTEST_LIBS_TO_LINK ${GTEST_BOTH_LIBRARIES} Threads::Threads)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_executable(tests_clipper_nlopt
|
|
||||||
test.cpp
|
|
||||||
../tools/svgtools.hpp
|
|
||||||
# ../tools/libnfpglue.hpp
|
|
||||||
# ../tools/libnfpglue.cpp
|
|
||||||
printer_parts.h
|
|
||||||
printer_parts.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(tests_clipper_nlopt libnest2d ${GTEST_LIBS_TO_LINK} )
|
|
||||||
|
|
||||||
target_include_directories(tests_clipper_nlopt PRIVATE BEFORE ${GTEST_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
if(NOT LIBNEST2D_HEADER_ONLY)
|
|
||||||
target_link_libraries(tests_clipper_nlopt ${LIBNAME})
|
|
||||||
else()
|
|
||||||
target_link_libraries(tests_clipper_nlopt libnest2d)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_test(libnest2d_tests tests_clipper_nlopt)
|
|
File diff suppressed because it is too large
Load Diff
@ -1,14 +0,0 @@
|
|||||||
#ifndef PRINTER_PARTS_H
|
|
||||||
#define PRINTER_PARTS_H
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
|
|
||||||
|
|
||||||
using TestData = std::vector<ClipperLib::Path>;
|
|
||||||
using TestDataEx = std::vector<ClipperLib::Polygon>;
|
|
||||||
|
|
||||||
extern const TestData PRINTER_PART_POLYGONS;
|
|
||||||
extern const TestData STEGOSAUR_POLYGONS;
|
|
||||||
extern const TestDataEx PRINTER_PART_POLYGONS_EX;
|
|
||||||
|
|
||||||
#endif // PRINTER_PARTS_H
|
|
File diff suppressed because it is too large
Load Diff
@ -204,7 +204,7 @@ if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
|||||||
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
|
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
target_compile_definitions(libslic3r PUBLIC -DUSE_TBB)
|
target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0)
|
||||||
target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNEST2D_INCLUDES} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
|
target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNEST2D_INCLUDES} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
target_link_libraries(libslic3r
|
target_link_libraries(libslic3r
|
||||||
libnest2d
|
libnest2d
|
||||||
@ -221,7 +221,7 @@ target_link_libraries(libslic3r
|
|||||||
poly2tri
|
poly2tri
|
||||||
qhull
|
qhull
|
||||||
semver
|
semver
|
||||||
tbb
|
TBB::tbb
|
||||||
${CMAKE_DL_LIBS}
|
${CMAKE_DL_LIBS}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "libslic3r/Utils.hpp"
|
// #include "libslic3r/Utils.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
|
@ -10,23 +10,14 @@ target_include_directories(Catch2 INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
|||||||
add_library(Catch2::Catch2 ALIAS Catch2)
|
add_library(Catch2::Catch2 ALIAS Catch2)
|
||||||
include(Catch)
|
include(Catch)
|
||||||
|
|
||||||
add_library(test_catch2_common INTERFACE)
|
|
||||||
target_compile_definitions(test_catch2_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)" CATCH_CONFIG_FAST_COMPILE)
|
|
||||||
target_link_libraries(test_catch2_common INTERFACE Catch2::Catch2)
|
|
||||||
|
|
||||||
add_library(test_common INTERFACE)
|
add_library(test_common INTERFACE)
|
||||||
|
target_compile_definitions(test_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)" CATCH_CONFIG_FAST_COMPILE)
|
||||||
|
target_link_libraries(test_common INTERFACE Catch2::Catch2)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
target_link_libraries(test_common INTERFACE "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)
|
target_link_libraries(test_common INTERFACE "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(test_common INTERFACE test_catch2_common)
|
|
||||||
|
|
||||||
# DEPRECATED:
|
|
||||||
#find_package(GTest REQUIRED)
|
|
||||||
#add_library(test_gtest_common INTERFACE)
|
|
||||||
#target_compile_definitions(test_gtest_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)")
|
|
||||||
#target_link_libraries(test_gtest_common INTERFACE GTest::GTest GTest::Main)
|
|
||||||
|
|
||||||
add_subdirectory(libnest2d)
|
add_subdirectory(libnest2d)
|
||||||
add_subdirectory(timeutils)
|
add_subdirectory(timeutils)
|
||||||
add_subdirectory(sla_print)
|
add_subdirectory(sla_print)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
||||||
add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp)
|
add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp)
|
||||||
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES})
|
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r
|
||||||
|
#${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
||||||
add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp printer_parts.cpp printer_parts.hpp)
|
add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp printer_parts.cpp printer_parts.hpp)
|
||||||
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES})
|
target_link_libraries(${_TEST_NAME}_tests test_common libnest2d )
|
||||||
|
|
||||||
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
||||||
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes")
|
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes")
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include <libnest2d.h>
|
#include <libnest2d/libnest2d.hpp>
|
||||||
#include "printer_parts.hpp"
|
#include "printer_parts.hpp"
|
||||||
//#include <libnest2d/geometry_traits_nfp.hpp>
|
//#include <libnest2d/geometry_traits_nfp.hpp>
|
||||||
#include "../tools/svgtools.hpp"
|
#include "../tools/svgtools.hpp"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
||||||
add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp)
|
add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp)
|
||||||
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES})
|
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
|
||||||
|
|
||||||
#catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
#catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
||||||
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes")
|
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes")
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
||||||
add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp)
|
add_executable(${_TEST_NAME}_tests
|
||||||
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES})
|
${_TEST_NAME}_tests_main.cpp
|
||||||
|
${PROJECT_SOURCE_DIR}/src/libslic3r/Time.cpp
|
||||||
|
${PROJECT_SOURCE_DIR}/src/libslic3r/Time.hpp
|
||||||
|
)
|
||||||
|
target_link_libraries(${_TEST_NAME}_tests test_common)
|
||||||
|
|
||||||
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
||||||
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes")
|
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes")
|
||||||
|
Loading…
Reference in New Issue
Block a user